import type { Database } from "bun:sqlite"; import { readFileSync } from "node:fs"; import { join } from "node:path"; import type { Hono } from "hono"; import { describeFile } from "../describe/describe.ts"; import { setting } from "../server/db.ts"; import { type Html, html } from "./html.ts"; import { cacheFor, notFound, type PageContext, page } from "./layout.ts"; import { renderParagraph, renderSiteMarkdown } from "./markdown.ts"; const SITE_DIR = join(import.meta.dir, "../../docs/site"); const { version } = (await Bun.file(`${import.meta.dir}/../../package.json`).json()) as { version: string; }; /** * ยง21.10: `/docs` is a Markdown file in Kepr's repo, rendered once at startup. `/support` is the * operator's, edited in the console (June, 2026-09-29): the shipped file is only its first words, * and the page can be switched off, which takes it out of the nav too. */ export function registerDocsPages(app: Hono, ctx: PageContext): void { const docs = readFileSync(join(SITE_DIR, "docs.md"), "utf8"); const docsBody = sitePage(docs, true); app.get("/docs", (c) => { cacheFor(c, false); return c.html( page(ctx, { title: "docs", path: "/docs", ...docsBody, body: html`
Running Kepr v${version}
${docsBody.body}`, }), ); }); app.get("/support", (c) => { if (setting(ctx.kepr, "support_off")) return notFound(c, ctx); cacheFor(c, false); const body = sitePage(supportText(ctx.kepr)); return c.html(page(ctx, { title: "support", path: "/support", ...body })); }); } /** The support page's Markdown: the operator's, or the shipped words until they write their own. */ export function supportText(db: Database): string { return setting(db, "support_page") ?? readFileSync(join(SITE_DIR, "support.md"), "utf8"); } /** A site page; with `contents`, a sidebar links each of its sections. */ function sitePage(source: string, contents = false): { description: string; body: Html } { const intro = describeFile("page.md", source); const doc = renderSiteMarkdown(source); const article = html`