From 86fb349ba852c9becce0dee9e35c848474c9b781 Mon Sep 17 00:00:00 2001 From: Akanimoh12 Date: Fri, 28 Aug 2026 01:09:37 +0100 Subject: [PATCH] feat(docs): add theme, anchors, search, and steps --- apps/docs/scripts/build.ts | 28 ++++++++++++++++++++-------- apps/docs/scripts/content.ts | 16 ++++++++++++++-- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/apps/docs/scripts/build.ts b/apps/docs/scripts/build.ts index f8d510e4..4ddc19e4 100644 --- a/apps/docs/scripts/build.ts +++ b/apps/docs/scripts/build.ts @@ -3,6 +3,7 @@ import { join } from "node:path" import { $ } from "bun" import { appRoot, loadPages } from "./content" +import { appRoot, loadPages, slugifyHeading } from "./content" await $`bun run ${join(appRoot, "scripts/check-content.ts")}` await $`bun run ${join(appRoot, "scripts/check-links.ts")}` @@ -37,15 +38,22 @@ function render(body: string) { const blocks = body.split(/\n\n+/) return blocks .map((block) => { - const heading = block.match(/^## (.+?)(?: \{#([a-z0-9-]+)\})?$/) + const heading = block.match(/^(#{2,6}) (.+?)(?: \{#([a-z0-9-]+)\})?$/) if (heading) { - const id = - heading[2] ?? - heading[1] - .toLowerCase() - .replace(/[^a-z0-9]+/g, "-") - .replace(/(^-|-$)/g, "") - return `

${escape(heading[1])}

` + const level = heading[1].length + const title = heading[2] + const id = heading[3] ?? slugifyHeading(title) + return `#${escape(title)}` + } + if (block.startsWith("") && block.endsWith("")) { + const items = block + .slice("".length, -"".length) + .trim() + .split("\n") + .filter((line) => /^\d+\. /.test(line)) + .map((line) => `
  • ${renderInline(line.replace(/^\d+\. /, ""))}
  • `) + .join("") + return `
      ${items}
    ` } if (block.startsWith("> ")) return `` @@ -60,6 +68,10 @@ function render(body: string) { } await rm(outputRoot, { recursive: true, force: true }) +const themeBootstrap = `` +const pageScript = `` +await rm(outputRoot, { recursive: true, force: true }) + const html = `${themeBootstrap}${escape(page.frontmatter.title)} ยท SO4 docs
    SO4 docs

    ${escape(page.frontmatter.title)}

    ${render(page.body)}
    ${pageScript}` for (const page of pages) { const directory = join(outputRoot, page.route.slice(1)) await mkdir(directory, { recursive: true }) diff --git a/apps/docs/scripts/content.ts b/apps/docs/scripts/content.ts index c8a13577..cb4f0e5d 100644 --- a/apps/docs/scripts/content.ts +++ b/apps/docs/scripts/content.ts @@ -80,9 +80,21 @@ export async function loadPages(): Promise> { export function headingEntries(body: string) { return [ ...body.matchAll( - /^## (.+?) \{#([a-z0-9-]+)\}\n\n([\s\S]*?)(?=\n\n## |$)/gm, + /^(#{2,6}) (.+?)(?: \{#([a-z0-9-]+)\})?$/gm, ), - ].map(([, title, id, answer]) => ({ title, id, answer: answer.trim() })) + ].map(([, , title, explicitId]) => ({ + title, + id: explicitId ?? slugifyHeading(title), + })) +} + +export function slugifyHeading(title: string): string { + return title + .toLocaleLowerCase() + .normalize("NFKD") + .replace(/[\u0300-\u036f]/g, "") + .replace(/[^a-z0-9]+/g, "-") + .replace(/(^-|-$)/g, "") } export function internalLinks(body: string) {