docs.ts

2.4 KB · raw

1import type { Database } from "bun:sqlite";2import { readFileSync } from "node:fs";3import { join } from "node:path";4import type { Hono } from "hono";5import { describeFile } from "../describe/describe.ts";6import { setting } from "../server/db.ts";7import { type Html, html } from "./html.ts";8import { cacheFor, notFound, type PageContext, page } from "./layout.ts";9import { renderParagraph, renderSiteMarkdown } from "./markdown.ts";1011const SITE_DIR = join(import.meta.dir, "../../docs/site");12const { version } = (await Bun.file(`${import.meta.dir}/../../package.json`).json()) as {13	version: string;14};1516/**17 * §21.10: `/docs` is a Markdown file in Kepr's repo, rendered once at startup. `/support` is the18 * operator's, edited in the console (June, 2026-09-29): the shipped file is only its first words,19 * and the page can be switched off, which takes it out of the nav too.20 */21export function registerDocsPages(app: Hono, ctx: PageContext): void {22	const docs = readFileSync(join(SITE_DIR, "docs.md"), "utf8");23	const docsBody = sitePage(docs, true);24	app.get("/docs", (c) => {25		cacheFor(c, false);26		return c.html(27			page(ctx, {28				title: "docs",29				path: "/docs",30				...docsBody,31				body: html`<p class="mono muted">Running Kepr v${version}</p>${docsBody.body}`,32			}),33		);34	});35	app.get("/support", (c) => {36		if (setting(ctx.kepr, "support_off")) return notFound(c, ctx);37		cacheFor(c, false);38		const body = sitePage(supportText(ctx.kepr));39		return c.html(page(ctx, { title: "support", path: "/support", ...body }));40	});41}4243/** The support page's Markdown: the operator's, or the shipped words until they write their own. */44export function supportText(db: Database): string {45	return setting(db, "support_page") ?? readFileSync(join(SITE_DIR, "support.md"), "utf8");46}4748/** A site page; with `contents`, a sidebar links each of its sections. */49function sitePage(source: string, contents = false): { description: string; body: Html } {50	const intro = describeFile("page.md", source);51	const doc = renderSiteMarkdown(source);52	const article = html`<article class="site-doc markdown">${doc.html}</article>`;53	const links = doc.sections.map(({ id, title }) => html`<a href="#${id}">${title}</a>`);54	return {55		description: intro ? renderParagraph(intro, { repo: "", dir: "", at: null }).text : "",56		body: contents57			? html`<div class="site-doc-layout">58<nav class="site-doc-nav mono" aria-label="sections">${links}</nav>59${article}60</div>`61			: article,62	};63}