@docubook/flame 2.0.0-alpha.0 → 2.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/.docu/components/Pagination.tsx +9 -1
  2. package/.docu/components/Toc.tsx +3 -3
  3. package/.docu/lib/build.deno.js +3 -3
  4. package/.docu/lib/{build.impl-WR24HDUT.js → build.impl-VXB4KL4D.js} +3 -3
  5. package/.docu/lib/build.node.js +3 -3
  6. package/.docu/lib/{chunk-X7KPVCDN.js → chunk-7PRQ3RQB.js} +1 -1
  7. package/.docu/lib/{chunk-VPV7KP7K.js → chunk-A6FIEG3H.js} +7 -3
  8. package/.docu/lib/{chunk-AJV2GEDF.js → chunk-JRERMREW.js} +1 -1
  9. package/.docu/lib/{chunk-KEWRVASF.js → chunk-LZDEWK25.js} +1 -1
  10. package/.docu/lib/{chunk-2EYRWRDP.js → chunk-MQWWCO6O.js} +6 -3
  11. package/.docu/lib/{chunk-QPF4DPZQ.js → chunk-TRT6WQZG.js} +54 -8
  12. package/.docu/lib/clean.js +1 -1
  13. package/.docu/lib/deploy.deno.js +1 -1
  14. package/.docu/lib/deploy.node.js +1 -1
  15. package/.docu/lib/preview.deno.js +2 -2
  16. package/.docu/lib/preview.node.js +2 -2
  17. package/.docu/lib/server.deno.js +3 -3
  18. package/.docu/lib/server.node.js +3 -3
  19. package/.docu/node/build.impl.ts +3 -0
  20. package/.docu/node/build.ts +3 -0
  21. package/.docu/node/html.shared.ts +7 -2
  22. package/.docu/node/html.ts +4 -2
  23. package/.docu/node/route.ts +57 -1
  24. package/.docu/node/server-routes.ts +4 -0
  25. package/.docu/styles/globals.css +9 -0
  26. package/package.json +5 -5
  27. package/template/README.md +1 -1
  28. package/template/docs/getting-started/configuration.mdx +16 -4
  29. package/template/docs/getting-started/overview.mdx +34 -42
  30. package/template/docs/guide/components.mdx +46 -228
  31. package/template/docs/guide/routing.mdx +8 -6
  32. package/template/docs/index.mdx +9 -8
  33. package/template/docu.json +4 -4
@@ -29,7 +29,15 @@ export default function Pagination({
29
29
  return (
30
30
  <PaginationDocs
31
31
  prev={prev ? { href: docsHtmlHref(`/docs${prev.href}`), title: prev.title } : undefined}
32
- next={next ? { href: docsHtmlHref(`/docs${next.href}`), title: next.title } : undefined}
32
+ next={
33
+ next
34
+ ? {
35
+ href: docsHtmlHref(`/docs${next.href}`),
36
+ title: next.title,
37
+ description: next.description,
38
+ }
39
+ : undefined
40
+ }
33
41
  className={className}
34
42
  prevIcon={prevIcon}
35
43
  nextIcon={nextIcon}
@@ -25,12 +25,12 @@ export default function Toc({ tocs }: TocProps) {
25
25
  // CSS scroll-margin-top (prose-headings:scroll-mt-4 desktop /
26
26
  // scroll-mt-16 mobile) so a change in one place updates both, and the
27
27
  // heading lands exactly where the browser would put a native anchor jump.
28
- function getAnchorOffset(): number {
28
+ const getAnchorOffset = useCallback((): number => {
29
29
  if (typeof window === "undefined") return 100;
30
30
  const first = tocs[0] ? document.getElementById(tocs[0].href.slice(1)) : null;
31
31
  const margin = first ? parseFloat(getComputedStyle(first).scrollMarginTop) : 0;
32
32
  return margin || 100;
33
- }
33
+ }, [tocs]);
34
34
 
35
35
  useEffect(() => {
36
36
  // Desktop-only TOC — on mobile this island is display:none but still
@@ -88,7 +88,7 @@ export default function Toc({ tocs }: TocProps) {
88
88
  scrollTarget.removeEventListener("scroll", listener);
89
89
  if (throttleTimer) clearTimeout(throttleTimer);
90
90
  };
91
- }, [tocs]);
91
+ }, [tocs, getAnchorOffset]);
92
92
 
93
93
  const handleLinkClick = useCallback((id: string) => {
94
94
  clickedIdRef.current = id;
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  runBuildCli
3
- } from "./chunk-2EYRWRDP.js";
4
- import "./chunk-QPF4DPZQ.js";
3
+ } from "./chunk-MQWWCO6O.js";
4
+ import "./chunk-TRT6WQZG.js";
5
5
  import "./chunk-EOK6KATZ.js";
6
- import "./chunk-AJV2GEDF.js";
6
+ import "./chunk-JRERMREW.js";
7
7
  import "./chunk-4IQXHHPF.js";
8
8
 
9
9
  // .docu/node/build.deno.ts
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  runBuild,
3
3
  runBuildCli
4
- } from "./chunk-2EYRWRDP.js";
5
- import "./chunk-QPF4DPZQ.js";
4
+ } from "./chunk-MQWWCO6O.js";
5
+ import "./chunk-TRT6WQZG.js";
6
6
  import "./chunk-EOK6KATZ.js";
7
- import "./chunk-AJV2GEDF.js";
7
+ import "./chunk-JRERMREW.js";
8
8
  import "./chunk-4IQXHHPF.js";
9
9
  export {
10
10
  runBuild,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  runBuildCli
3
- } from "./chunk-2EYRWRDP.js";
4
- import "./chunk-QPF4DPZQ.js";
3
+ } from "./chunk-MQWWCO6O.js";
4
+ import "./chunk-TRT6WQZG.js";
5
5
  import "./chunk-EOK6KATZ.js";
6
- import "./chunk-AJV2GEDF.js";
6
+ import "./chunk-JRERMREW.js";
7
7
  import "./chunk-4IQXHHPF.js";
8
8
 
9
9
  // .docu/node/build.node.ts
@@ -137,7 +137,7 @@ async function runBuild() {
137
137
  process.env.FLAME_BUILD_SILENT = "1";
138
138
  process.env.LOG_LEVEL = "error";
139
139
  }
140
- const { runBuildCli } = await import("./build.impl-WR24HDUT.js");
140
+ const { runBuildCli } = await import("./build.impl-VXB4KL4D.js");
141
141
  await runBuildCli();
142
142
  }
143
143
  async function writeDockerFiles() {
@@ -15,7 +15,7 @@ import {
15
15
  htmlShell,
16
16
  initSentry,
17
17
  loadPlugins
18
- } from "./chunk-QPF4DPZQ.js";
18
+ } from "./chunk-TRT6WQZG.js";
19
19
  import {
20
20
  SECURITY_HEADERS,
21
21
  generateNonce,
@@ -28,7 +28,7 @@ import {
28
28
  } from "./chunk-EOK6KATZ.js";
29
29
  import {
30
30
  logger
31
- } from "./chunk-AJV2GEDF.js";
31
+ } from "./chunk-JRERMREW.js";
32
32
  import {
33
33
  DIST_DIR,
34
34
  DOCS_DIR,
@@ -60,7 +60,11 @@ function createHtmlResponse(title, description, body, status, state, depth = 0)
60
60
  nonce,
61
61
  extraScripts: hmrScript(nonce),
62
62
  themeCss: state.inlineThemeCss,
63
- depth
63
+ depth,
64
+ // 404 pages can be requested at arbitrary depths (e.g. a noLink section
65
+ // path typed in the address bar) — relative asset paths would resolve
66
+ // against the wrong directory and break CSS/JS.
67
+ absoluteAssets: status === 404
64
68
  });
65
69
  return htmlResponse(html, nonce, status, true);
66
70
  }
@@ -5,7 +5,7 @@ import {
5
5
  // package.json
6
6
  var package_default = {
7
7
  name: "@docubook/flame",
8
- version: "2.0.0-alpha.0",
8
+ version: "2.0.0-alpha.1",
9
9
  description: "A blazing-fast React + MDX framework powered by Bun, built for modern documentation experiences.",
10
10
  type: "module",
11
11
  bin: {
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-EOK6KATZ.js";
8
8
  import {
9
9
  logger
10
- } from "./chunk-AJV2GEDF.js";
10
+ } from "./chunk-JRERMREW.js";
11
11
  import {
12
12
  DIST_DIR
13
13
  } from "./chunk-4IQXHHPF.js";
@@ -15,7 +15,7 @@ import {
15
15
  htmlShell,
16
16
  initSentry,
17
17
  loadPlugins
18
- } from "./chunk-QPF4DPZQ.js";
18
+ } from "./chunk-TRT6WQZG.js";
19
19
  import {
20
20
  cspHeader,
21
21
  generateNonce,
@@ -23,7 +23,7 @@ import {
23
23
  } from "./chunk-EOK6KATZ.js";
24
24
  import {
25
25
  logger
26
- } from "./chunk-AJV2GEDF.js";
26
+ } from "./chunk-JRERMREW.js";
27
27
  import {
28
28
  ASSETS_DIR,
29
29
  CACHE_FILE,
@@ -398,7 +398,10 @@ async function runBuild() {
398
398
  css: assetManifest.css,
399
399
  js: assetManifest.js,
400
400
  nonce: notFoundNonce,
401
- themeCss: inlineThemeCss
401
+ themeCss: inlineThemeCss,
402
+ // Served as the static-host fallback at ANY requested path — relative
403
+ // depth can never be right there, so use root-absolute asset URLs.
404
+ absoluteAssets: true
402
405
  });
403
406
  await writeFile(join(DIST_DIR, "404.html"), notFoundHtml);
404
407
  logger.spinner.stop(
@@ -1333,6 +1333,9 @@ function DocsBreadcrumb({ paths }) {
1333
1333
  }
1334
1334
 
1335
1335
  // .docu/node/route.ts
1336
+ import { readFileSync as readFileSync2 } from "node:fs";
1337
+ import { join as join4 } from "node:path";
1338
+ import { extractFrontmatter } from "@docubook/core";
1336
1339
  var docuConfig2 = loadDocuConfig();
1337
1340
  var routes = resolveRoutes(docuConfig2.routes);
1338
1341
  function flattenRoutes() {
@@ -1361,8 +1364,42 @@ function getRouteMap() {
1361
1364
  routes.forEach((route) => traverse(route));
1362
1365
  return map;
1363
1366
  }
1367
+ var descriptionCache = /* @__PURE__ */ new Map();
1368
+ function readDescription(href) {
1369
+ const cached = descriptionCache.get(href);
1370
+ if (cached !== void 0) return cached;
1371
+ let description = "";
1372
+ const rel = href.replace(/^\/|$/g, "");
1373
+ for (const ext of [".mdx", ".md"]) {
1374
+ for (const file of [join4(DOCS_DIR, `${rel}${ext}`), join4(DOCS_DIR, `${rel}/index${ext}`)]) {
1375
+ try {
1376
+ const fm = extractFrontmatter(readFileSync2(file, "utf-8"));
1377
+ description = typeof fm.description === "string" ? fm.description : "";
1378
+ if (description) break;
1379
+ } catch {
1380
+ }
1381
+ }
1382
+ if (description) break;
1383
+ }
1384
+ descriptionCache.set(href, description);
1385
+ return description;
1386
+ }
1364
1387
  function getPreviousNext(pathname) {
1365
1388
  const normalizedPath = pathname.replace(/^\/|$/g, "");
1389
+ if (normalizedPath === "docs" || normalizedPath === "") {
1390
+ const paths2 = flattenRoutes();
1391
+ const routeMap2 = getRouteMap();
1392
+ const first = paths2[0];
1393
+ if (!first) return { prev: null, next: null };
1394
+ return {
1395
+ prev: null,
1396
+ next: {
1397
+ href: first,
1398
+ title: routeMap2.get(first) || "",
1399
+ description: readDescription(first)
1400
+ }
1401
+ };
1402
+ }
1366
1403
  const paths = flattenRoutes();
1367
1404
  const index = paths.findIndex((href) => href === `/${normalizedPath}` || href === normalizedPath);
1368
1405
  if (index === -1) {
@@ -1373,7 +1410,11 @@ function getPreviousNext(pathname) {
1373
1410
  const nextHref = index < paths.length - 1 ? paths[index + 1] : null;
1374
1411
  return {
1375
1412
  prev: prevHref ? { href: prevHref, title: routeMap.get(prevHref) || "" } : null,
1376
- next: nextHref ? { href: nextHref, title: routeMap.get(nextHref) || "" } : null
1413
+ next: nextHref ? {
1414
+ href: nextHref,
1415
+ title: routeMap.get(nextHref) || "",
1416
+ description: readDescription(nextHref)
1417
+ } : null
1377
1418
  };
1378
1419
  }
1379
1420
 
@@ -1395,7 +1436,11 @@ function Pagination({
1395
1436
  PaginationDocs,
1396
1437
  {
1397
1438
  prev: prev ? { href: docsHtmlHref(`/docs${prev.href}`), title: prev.title } : void 0,
1398
- next: next ? { href: docsHtmlHref(`/docs${next.href}`), title: next.title } : void 0,
1439
+ next: next ? {
1440
+ href: docsHtmlHref(`/docs${next.href}`),
1441
+ title: next.title,
1442
+ description: next.description
1443
+ } : void 0,
1399
1444
  className,
1400
1445
  prevIcon,
1401
1446
  nextIcon,
@@ -1671,12 +1716,12 @@ function Toc({ tocs }) {
1671
1716
  useEffect2(() => {
1672
1717
  activeIdRef.current = activeId;
1673
1718
  }, [activeId]);
1674
- function getAnchorOffset() {
1719
+ const getAnchorOffset = useCallback2(() => {
1675
1720
  if (typeof window === "undefined") return 100;
1676
1721
  const first = tocs[0] ? document.getElementById(tocs[0].href.slice(1)) : null;
1677
1722
  const margin = first ? parseFloat(getComputedStyle(first).scrollMarginTop) : 0;
1678
1723
  return margin || 100;
1679
- }
1724
+ }, [tocs]);
1680
1725
  useEffect2(() => {
1681
1726
  if (typeof window === "undefined" || window.innerWidth < 1024) return;
1682
1727
  if (!tocs.length) return;
@@ -1717,7 +1762,7 @@ function Toc({ tocs }) {
1717
1762
  scrollTarget.removeEventListener("scroll", listener);
1718
1763
  if (throttleTimer) clearTimeout(throttleTimer);
1719
1764
  };
1720
- }, [tocs]);
1765
+ }, [tocs, getAnchorOffset]);
1721
1766
  const handleLinkClick = useCallback2((id) => {
1722
1767
  clickedIdRef.current = id;
1723
1768
  setActiveId(id);
@@ -2475,7 +2520,8 @@ function htmlShell(opts) {
2475
2520
  themeCss,
2476
2521
  depth = 0,
2477
2522
  headExtra,
2478
- bodyExtra
2523
+ bodyExtra,
2524
+ absoluteAssets = false
2479
2525
  } = opts;
2480
2526
  const nonceAttr = nonce ? ` nonce="${escapeHtml(nonce)}"` : "";
2481
2527
  const themeStyle = themeCss ? `
@@ -2485,8 +2531,8 @@ function htmlShell(opts) {
2485
2531
  const bodyInjection = bodyExtra?.length ? `
2486
2532
  ${bodyExtra.join("\n ")}` : "";
2487
2533
  const depthPrefix = depth === 0 ? "" : "../".repeat(depth);
2488
- const assetPrefix = depthPrefix + "assets/";
2489
- const resolvePath = (path) => path.startsWith("/") ? depthPrefix + path.slice(1) : path;
2534
+ const assetPrefix = absoluteAssets ? "/assets/" : depthPrefix + "assets/";
2535
+ const resolvePath = (path) => absoluteAssets ? path : path.startsWith("/") ? depthPrefix + path.slice(1) : path;
2490
2536
  let seoTags = "";
2491
2537
  if (opts.seo) {
2492
2538
  const s = opts.seo;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-AJV2GEDF.js";
3
+ } from "./chunk-JRERMREW.js";
4
4
  import "./chunk-4IQXHHPF.js";
5
5
 
6
6
  // .docu/node/clean.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runDeploy
3
- } from "./chunk-X7KPVCDN.js";
3
+ } from "./chunk-7PRQ3RQB.js";
4
4
  import "./chunk-4IQXHHPF.js";
5
5
 
6
6
  // .docu/node/deploy.deno.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runDeploy
3
- } from "./chunk-X7KPVCDN.js";
3
+ } from "./chunk-7PRQ3RQB.js";
4
4
  import "./chunk-4IQXHHPF.js";
5
5
 
6
6
  // .docu/node/deploy.node.ts
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  runPreview
3
- } from "./chunk-KEWRVASF.js";
3
+ } from "./chunk-LZDEWK25.js";
4
4
  import {
5
5
  denoAdapter
6
6
  } from "./chunk-UISOJ4RW.js";
7
7
  import "./chunk-EOK6KATZ.js";
8
- import "./chunk-AJV2GEDF.js";
8
+ import "./chunk-JRERMREW.js";
9
9
  import "./chunk-4IQXHHPF.js";
10
10
 
11
11
  // .docu/node/preview.deno.ts
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  runPreview
3
- } from "./chunk-KEWRVASF.js";
3
+ } from "./chunk-LZDEWK25.js";
4
4
  import {
5
5
  nodeAdapter
6
6
  } from "./chunk-UISOJ4RW.js";
7
7
  import "./chunk-EOK6KATZ.js";
8
- import "./chunk-AJV2GEDF.js";
8
+ import "./chunk-JRERMREW.js";
9
9
  import "./chunk-4IQXHHPF.js";
10
10
 
11
11
  // .docu/node/preview.node.ts
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  runServer
3
- } from "./chunk-VPV7KP7K.js";
4
- import "./chunk-QPF4DPZQ.js";
3
+ } from "./chunk-A6FIEG3H.js";
4
+ import "./chunk-TRT6WQZG.js";
5
5
  import {
6
6
  denoAdapter
7
7
  } from "./chunk-UISOJ4RW.js";
8
8
  import "./chunk-EOK6KATZ.js";
9
- import "./chunk-AJV2GEDF.js";
9
+ import "./chunk-JRERMREW.js";
10
10
  import "./chunk-4IQXHHPF.js";
11
11
 
12
12
  // .docu/node/server.deno.ts
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  runServer
3
- } from "./chunk-VPV7KP7K.js";
4
- import "./chunk-QPF4DPZQ.js";
3
+ } from "./chunk-A6FIEG3H.js";
4
+ import "./chunk-TRT6WQZG.js";
5
5
  import {
6
6
  nodeAdapter
7
7
  } from "./chunk-UISOJ4RW.js";
8
8
  import "./chunk-EOK6KATZ.js";
9
- import "./chunk-AJV2GEDF.js";
9
+ import "./chunk-JRERMREW.js";
10
10
  import "./chunk-4IQXHHPF.js";
11
11
 
12
12
  // .docu/node/server.node.ts
@@ -436,6 +436,9 @@ export async function runBuild(): Promise<void> {
436
436
  js: assetManifest.js,
437
437
  nonce: notFoundNonce,
438
438
  themeCss: inlineThemeCss,
439
+ // Served as the static-host fallback at ANY requested path — relative
440
+ // depth can never be right there, so use root-absolute asset URLs.
441
+ absoluteAssets: true,
439
442
  });
440
443
  await writeFile(join(DIST_DIR, "404.html"), notFoundHtml);
441
444
 
@@ -418,6 +418,9 @@ async function build() {
418
418
  js: assetManifest.js,
419
419
  nonce: generateNonce(),
420
420
  themeCss: inlineThemeCss,
421
+ // Served as the static-host fallback at ANY requested path — relative
422
+ // depth can never be right there, so use root-absolute asset URLs.
423
+ absoluteAssets: true,
421
424
  });
422
425
  await writeFile(join(DIST_DIR, "404.html"), notFoundHtml);
423
426
 
@@ -34,6 +34,9 @@ export interface HtmlShellOptions {
34
34
  bodyExtra?: string[];
35
35
  /** SEO meta tags derived from config + frontmatter */
36
36
  seo?: SeoMeta;
37
+ /** Root-absolute asset URLs (`/assets/...`). Required for pages served at
38
+ * arbitrary paths (404 fallback) — relative depth is wrong there. */
39
+ absoluteAssets?: boolean;
37
40
  }
38
41
 
39
42
  export function htmlShell(opts: HtmlShellOptions): string {
@@ -51,14 +54,16 @@ export function htmlShell(opts: HtmlShellOptions): string {
51
54
  depth = 0,
52
55
  headExtra,
53
56
  bodyExtra,
57
+ absoluteAssets = false,
54
58
  } = opts;
55
59
  const nonceAttr = nonce ? ` nonce="${escapeHtml(nonce)}"` : "";
56
60
  const themeStyle = themeCss ? `\n <style${nonceAttr}>${escapeHtml(themeCss)}</style>` : "";
57
61
  const headInjection = headExtra?.length ? `\n ${headExtra.join("\n ")}` : "";
58
62
  const bodyInjection = bodyExtra?.length ? `\n ${bodyExtra.join("\n ")}` : "";
59
63
  const depthPrefix = depth === 0 ? "" : "../".repeat(depth);
60
- const assetPrefix = depthPrefix + "assets/";
61
- const resolvePath = (path: string) => (path.startsWith("/") ? depthPrefix + path.slice(1) : path);
64
+ const assetPrefix = absoluteAssets ? "/assets/" : depthPrefix + "assets/";
65
+ const resolvePath = (path: string) =>
66
+ absoluteAssets ? path : path.startsWith("/") ? depthPrefix + path.slice(1) : path;
62
67
 
63
68
  // Build SEO meta tags (OG, Twitter, canonical)
64
69
  let seoTags = "";
@@ -16,14 +16,16 @@ export function htmlShell(opts: HtmlShellOptions): string {
16
16
  depth = 0,
17
17
  headExtra,
18
18
  bodyExtra,
19
+ absoluteAssets = false,
19
20
  } = opts;
20
21
  const nonceAttr = nonce ? ` nonce="${Bun.escapeHTML(nonce)}"` : "";
21
22
  const themeStyle = themeCss ? `\n <style${nonceAttr}>${Bun.escapeHTML(themeCss)}</style>` : "";
22
23
  const headInjection = headExtra?.length ? `\n ${headExtra.join("\n ")}` : "";
23
24
  const bodyInjection = bodyExtra?.length ? `\n ${bodyExtra.join("\n ")}` : "";
24
25
  const depthPrefix = depth === 0 ? "" : "../".repeat(depth);
25
- const assetPrefix = depthPrefix + "assets/";
26
- const resolvePath = (path: string) => (path.startsWith("/") ? depthPrefix + path.slice(1) : path);
26
+ const assetPrefix = absoluteAssets ? "/assets/" : depthPrefix + "assets/";
27
+ const resolvePath = (path: string) =>
28
+ absoluteAssets ? path : path.startsWith("/") ? depthPrefix + path.slice(1) : path;
27
29
 
28
30
  // Build SEO meta tags (OG, Twitter, canonical)
29
31
  let seoTags = "";
@@ -1,6 +1,11 @@
1
1
  import { loadDocuConfig } from "./paths";
2
2
  import type { DocuRoute } from "./types";
3
3
  import { resolveRoutes } from "./fs-scanner";
4
+ import { DOCS_DIR } from "./paths";
5
+ import { readFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { extractFrontmatter } from "@docubook/core";
8
+ import type { Frontmatter } from "./mdx";
4
9
 
5
10
  const docuConfig = loadDocuConfig();
6
11
  export const routes: DocuRoute[] = resolveRoutes(docuConfig.routes);
@@ -41,8 +46,53 @@ export function getRouteMap(): Map<string, string> {
41
46
  return map;
42
47
  }
43
48
 
49
+ /** Build-time cache of href → frontmatter description (docs content is static). */
50
+ const descriptionCache = new Map<string, string>();
51
+
52
+ function readDescription(href: string): string {
53
+ const cached = descriptionCache.get(href);
54
+ if (cached !== undefined) return cached;
55
+
56
+ let description = "";
57
+ const rel = href.replace(/^\/|$/g, "");
58
+ for (const ext of [".mdx", ".md"]) {
59
+ for (const file of [join(DOCS_DIR, `${rel}${ext}`), join(DOCS_DIR, `${rel}/index${ext}`)]) {
60
+ try {
61
+ const fm = extractFrontmatter<Frontmatter>(readFileSync(file, "utf-8"));
62
+ description = typeof fm.description === "string" ? fm.description : "";
63
+ if (description) break;
64
+ } catch {
65
+ // not this file — try the next candidate
66
+ }
67
+ }
68
+ if (description) break;
69
+ }
70
+ descriptionCache.set(href, description);
71
+ return description;
72
+ }
73
+
44
74
  export function getPreviousNext(pathname: string) {
45
75
  const normalizedPath = pathname.replace(/^\/|$/g, "");
76
+
77
+ // Docs index (/docs — DocsPage renders with pathname "" from slug []):
78
+ // next-only navigation into the first docs page — never read the route
79
+ // backward from the index, so prev stays null even if a page sits before
80
+ // it in the route list.
81
+ if (normalizedPath === "docs" || normalizedPath === "") {
82
+ const paths = flattenRoutes();
83
+ const routeMap = getRouteMap();
84
+ const first = paths[0];
85
+ if (!first) return { prev: null, next: null };
86
+ return {
87
+ prev: null,
88
+ next: {
89
+ href: first,
90
+ title: routeMap.get(first) || "",
91
+ description: readDescription(first),
92
+ },
93
+ };
94
+ }
95
+
46
96
  const paths = flattenRoutes();
47
97
 
48
98
  const index = paths.findIndex((href) => href === `/${normalizedPath}` || href === normalizedPath);
@@ -57,7 +107,13 @@ export function getPreviousNext(pathname: string) {
57
107
 
58
108
  return {
59
109
  prev: prevHref ? { href: prevHref, title: routeMap.get(prevHref) || "" } : null,
60
- next: nextHref ? { href: nextHref, title: routeMap.get(nextHref) || "" } : null,
110
+ next: nextHref
111
+ ? {
112
+ href: nextHref,
113
+ title: routeMap.get(nextHref) || "",
114
+ description: readDescription(nextHref),
115
+ }
116
+ : null,
61
117
  };
62
118
  }
63
119
 
@@ -44,6 +44,10 @@ function createHtmlResponse(
44
44
  extraScripts: hmrScript(nonce),
45
45
  themeCss: state.inlineThemeCss,
46
46
  depth,
47
+ // 404 pages can be requested at arbitrary depths (e.g. a noLink section
48
+ // path typed in the address bar) — relative asset paths would resolve
49
+ // against the wrong directory and break CSS/JS.
50
+ absoluteAssets: status === 404,
47
51
  });
48
52
  /** Dev-only: serves compiledSource → MDXRemote eval path (no CSP in production). */
49
53
  return htmlResponse(html, nonce, status, true);
@@ -9,6 +9,15 @@
9
9
  @plugin "@tailwindcss/typography";
10
10
  @source "../../.docu/components";
11
11
  @source "../../.docu/pages";
12
+ @source "../../../ui-react/src";
13
+
14
+ /* daisyUI breadcrumbs underline every li child on hover (including plain
15
+ spans) — crumbs are not links, so strip the underline (and pointer cursor)
16
+ for non-anchor items; real anchors keep daisyUI's hover feedback. */
17
+ .breadcrumbs > ul > li > :not(a):hover {
18
+ text-decoration-line: none;
19
+ cursor: default;
20
+ }
12
21
  @source inline(
13
22
  "breadcrumbs collapse collapse-open collapse-close collapse-arrow collapse-plus collapse-title collapse-content"
14
23
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docubook/flame",
3
- "version": "2.0.0-alpha.0",
3
+ "version": "2.0.0-alpha.1",
4
4
  "description": "A blazing-fast React + MDX framework powered by Bun, built for modern documentation experiences.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -56,10 +56,10 @@
56
56
  "react-dom": "^19.2.7",
57
57
  "unified": "^11.0.0",
58
58
  "zod": "^4.4.3",
59
- "@docubook/core": "^2.0.0-alpha.0",
60
- "@docubook/themes-colors": "^2.0.0-alpha.0",
61
- "@docubook/ui-react": "^2.0.0-alpha.0",
62
- "@docubook/markdown": "^2.0.0-alpha.0"
59
+ "@docubook/core": "^2.0.0-alpha.1",
60
+ "@docubook/markdown": "^2.0.0-alpha.1",
61
+ "@docubook/themes-colors": "^2.0.0-alpha.1",
62
+ "@docubook/ui-react": "^2.0.0-alpha.1"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "@sentry/bun": "^10.0.0"
@@ -42,7 +42,7 @@ deno task dev
42
42
 
43
43
  ## Documentation
44
44
 
45
- For the full documentation, please visit **[packages/flame/docs](https://github.com/DocuBook/docubook/tree/main/packages/flame/docs)** or read the official site [docubook.pro](https://docubook.pro)
45
+ For the full documentation, visit **[www.docubook.pro](https://www.docubook.pro/)**
46
46
 
47
47
  ---
48
48
 
@@ -3,16 +3,28 @@ title: Configuration
3
3
  description: docu.json reference — meta, navbar, themes, routes, and plugins.
4
4
  ---
5
5
 
6
- `docu.json` in your project root controls the site. Key sections:
6
+ `docu.json` in the project root controls the whole site. It is validated against `docu.schema.json`.
7
+
8
+ ## First edits
9
+
10
+ | What | Where |
11
+ |---|---|
12
+ | Site name & tagline | `meta.title`, `meta.description` |
13
+ | Brand in the header | `navbar.logoText` |
14
+ | Landing copy | `home.hero` — headline, description, actions |
15
+ | Colour preset | `themes.colors` — `default`, `freshlime`, `coffee` |
16
+ | Footer links | `footer.social` |
17
+
18
+ ## Reference
7
19
 
8
20
  | Field | Description |
9
21
  |---|---|
10
22
  | `meta` | Site title, description, favicon, base URL |
11
23
  | `navbar` | Logo, navigation menu links |
12
- | `home.hero` | Landing page hero with tagline, headline, actions |
13
- | `home.features` | Feature cards with icon, title, description |
24
+ | `home.hero` | Landing page hero — tagline, headline, actions |
25
+ | `home.features` | Feature cards — icon, title, description |
14
26
  | `routes` | Sidebar navigation (auto-detected from `docs/` when empty) |
15
27
  | `themes` | Colour preset or custom palette |
16
28
  | `plugins` | Build/dev pipeline extensions |
17
29
 
18
- See this site's `docu.json` for a complete example, or refer to `docu.schema.json` in the package root.
30
+ This project's `docu.json` is a complete working example — edit it and the dev server applies the changes live.
@@ -1,50 +1,42 @@
1
1
  ---
2
2
  title: Quick Start
3
- description: Get your first documentation site running on Bun, Node.js, or Deno.
3
+ description: The Flame workflow — dev server, first edit, authoring, and build.
4
4
  ---
5
5
 
6
- ## Prerequisites
7
-
8
- Choose **one** runtime:
9
-
10
- - **Bun** >= 1.1: `curl -fsSL https://bun.sh/install | bash`
11
- - **Node.js** >= 20.11: [nodejs.org](https://nodejs.org)
12
- - **Deno** >= 2.x: `curl -fsSL https://deno.land/install.sh | sh`
13
-
14
- ## Create a project
15
-
16
- <Tabs>
17
- <Tab title="Bun">
18
- ```bash
19
- mkdir my-docs && cd my-docs
20
- bun add @docubook/flame
21
- bunx flame init
22
- bun run dev
23
- ```
24
- </Tab>
25
- <Tab title="Node.js">
26
- ```bash
27
- mkdir my-docs && cd my-docs
28
- npm install @docubook/flame
29
- npx flame init
30
- npm run dev
31
- ```
32
- </Tab>
33
- <Tab title="Deno">
34
- ```bash
35
- mkdir my-docs && cd my-docs
36
- deno run -A npm:@docubook/flame init
37
- deno task dev
38
- ```
39
- </Tab>
40
- </Tabs>
41
-
42
- Open http://localhost:3000 — changes to `docs/` or `docu.json` hot-reload.
43
-
44
- ## Build for production
6
+ Your Flame project is ready — this is the workflow to make it yours.
7
+
8
+ ## 1. Run the dev server
9
+
10
+ ```bash
11
+ bun run dev # or: npm run dev / deno task dev
12
+ ```
13
+
14
+ Open http://localhost:3000 — edits to `docs/` and `docu.json` hot-reload instantly.
15
+
16
+ ## 2. Make your first edit
17
+
18
+ Open `docs/getting-started/overview.mdx` (this file):
19
+
20
+ 1. Change `title` and `description` in the frontmatter — the browser updates live
21
+ 2. Add a heading below and save
22
+ 3. New pages appear in the sidebar and search automatically
23
+
24
+ ## 3. Author with directives
25
+
26
+ Content is plain markdown — components are directives, no JSX:
27
+
28
+ ```md
29
+ :::tip
30
+ A callout — no JSX needed.
31
+ :::
32
+ ```
33
+
34
+ See [Components](/docs/guide/components) for the full directive set.
35
+
36
+ ## 4. Build for production
45
37
 
46
38
  ```bash
47
- npm run build # or: bun run build / deno task build
39
+ bun run build # or: npm run build / deno task build
48
40
  ```
49
41
 
50
- Static output goes to `.docu/dist/` — deploy to any CDN or static host.
42
+ Flat static HTML lands in `.docu/dist/` — deploy anywhere ([Deployment](/docs/guide/deployment)).
@@ -1,256 +1,74 @@
1
1
  ---
2
2
  title: Components
3
- description: Built-in MDX components available in DocuBook Flame.
4
- date: 2026-05-25
3
+ description: The directive syntax for built-in components — callouts, tabs, cards, code blocks, and more.
5
4
  ---
6
5
 
7
- DocuBook Flame includes pre-built MDX components you can use directly in your docs.
6
+ Components are **directives**, not JSX — content stays plain markdown. A few to get you started; the full reference lives at [docubook.pro/docs/components](https://www.docubook.pro/docs/components/accordion).
8
7
 
9
- > For full documentation and live examples, visit [docubook.pro/docs/components](https://www.docubook.pro/docs/components/accordion).
8
+ ## Callout
10
9
 
11
- ## Note
12
-
13
- Display messages with different severity levels.
14
-
15
- ```jsx:filename.mdx
16
- <Note type="tip" title="Tip">
17
- This is a tip to suggests a helpful.
18
- </Note>
19
- <Note type="info" title="Info">
20
- This is a general note to convey information to the user.
21
- </Note>
22
- <Note type="danger" title="Danger">
23
- This is a danger alert to notify the user of a critical issue.
24
- </Note>
25
- <Note type="warning">
26
- This note uses default title and default icon from its type.
27
- </Note>
28
- <Note type="success" title="Success">
29
- This is a success message to inform the user of successful actions.
30
- </Note>
31
- ```
32
-
33
- ## Tabs
34
-
35
- Organize content into switchable sections.
36
-
37
- ````jsx:filename.mdx
38
- <Tabs className="pt-5 pb-1">
39
- <Tab title="Java">
40
- ```java:HelloWorld.java
41
- // HelloWorld.java
42
- public class HelloWorld {
43
- public static void main(String[] args) {
44
- System.out.println("Hello, World!");
45
- }
46
- }
47
- ```
48
- </Tab>
49
- <Tab title="TypeScript">
50
- ```typescript:helloWorld.ts
51
- // helloWorld.ts
52
- function helloWorld(): void {
53
- console.log("Hello, World!");
54
- }
55
- helloWorld();
56
- ```
57
- </Tab>
58
- </Tabs>
59
- ````
60
-
61
- ## Card
62
-
63
- Display content in a compact card layout.
64
-
65
- ```jsx:filename.mdx
66
- <Card title="Card with Link and icon" icon="Link" href="/docs/components/card-group">
67
- This is how you use a card with an icon and a link. Clicking on this card
68
- brings you to the Card Group page.
69
- </Card>
10
+ ```md
11
+ :::tip{title="Optional title"}
12
+ Callout content.
13
+ :::
70
14
  ```
71
15
 
72
- ## Card Group
16
+ Variants: `tip`, `info`, `warning`, `danger`, `success`.
73
17
 
74
- Group multiple cards in a grid.
18
+ ## Code block with title
75
19
 
76
- ```jsx:filename.mdx
77
- <Cards cols={2}>
78
- <Card title="Heading 1" icon="Heading1">
79
- This is an example of card content with columns.
80
- </Card>
81
- <Card title="Heading 2" icon="Heading2">
82
- This is an example of card content with columns.
83
- </Card>
84
- <Card title="Grid Card" icon="Grid" horizontal>
85
- This is a horizontal card layout.
86
- </Card>
87
- <Card title="Horizontal Card" icon="Layout" horizontal>
88
- This is a horizontal card layout.
89
- </Card>
90
- </Cards>
20
+ ````md
21
+ ```javascript:app.js showLineNumbers {2-3}
22
+ const docs = "markdown";
23
+ console.log(docs);
91
24
  ```
92
-
93
- ## Accordion
94
-
95
- ````markdown:filename.mdx
96
- <Accordions>
97
- <Accordion title="Basic Usage">
98
- This accordion includes a [Lucide Icon](https://lucide.dev/icons/) because the `icon` prop is provided.
99
- </Accordion>
100
-
101
- <Accordion title="With icon props" icon="MousePointerClick">
102
- This accordion includes a Lucide icon because the `icon` prop is provided.
103
- </Accordion>
104
-
105
- <Accordion title="With Code Block">
106
- You can put other components inside Accordions.
107
- ```jsx:helloword.jsx
108
- class HelloWorld {
109
- public static void main(String[] args) {
110
- System.out.println("Hello, World!");
111
- }
112
- }
113
- ```
114
- </Accordion>
115
- </Accordions>
116
25
  ````
117
26
 
118
- ## Steps / Stepper
119
-
120
- Display step-by-step instructions.
121
-
122
- ````jsx:filename.mdx
123
- <Steps>
124
- <Step title="Step 1: Clone the DocuBook Repository">
125
- ```bash
126
- git clone https://github.com/DocuBook/docubook.git
127
- ```
128
- </Step>
129
- <Step title="Step 2: Access the Project Directory">
130
- ```bash
131
- cd docubook
132
- ```
133
- </Step>
134
- <Step title="Step 3: Install Required Dependencies">
135
- ```bash
136
- pnpm install
137
- ```
138
- </Step>
139
- </Steps>
140
- ````
141
-
142
- ## Button
143
-
144
- Styled buttons with icons.
145
-
146
- ```jsx
147
- <Button
148
- text="Learn More"
149
- href="https://example.com"
150
- icon="MoveUpRight"
151
- size="md"
152
- variant="secondary"
153
- target="_blank"
154
- />
155
- ```
156
-
157
- ## File Tree
158
-
159
- Display hierarchical file structures.
160
-
161
- ```jsx
162
- <Files>
163
- <Folder name="src">
164
- <File name="App.tsx" />
165
- <File name="index.tsx" />
166
- <Folder name="components">
167
- <File name="Button.tsx" />
168
- </Folder>
169
- </Folder>
170
- <File name="package.json" />
171
- </Files>
172
- ```
173
-
174
- ## Code Block
175
-
176
- Syntax-highlighted code with title and line highlighting.
27
+ The `:filename` suffix adds a title bar; `showLineNumbers` and `{2-3}` enable line numbers and highlighting.
177
28
 
178
- ````jsx
179
- ```ts:server.ts {3-5}
180
- import { serve } from "bun";
29
+ ## Tabs
181
30
 
182
- serve({
183
- port: 3000,
184
- fetch: () => new Response("Hello!"),
185
- });
186
- ```
31
+ ````md
32
+ ::::tabs
33
+ :::tab{title="Bun"}
34
+ bun run dev
35
+ :::
36
+ :::tab{title="Node"}
37
+ npm run dev
38
+ :::
39
+ ::::
187
40
  ````
188
41
 
189
- ## Image
190
-
191
- Responsive images with optional caption.
192
-
193
- ```markdown:filename.mdx
194
- ![Alt text for the image](https://via.placeholder.com/150)
42
+ ## Card group
43
+
44
+ ```md
45
+ ::::cards{cols="2"}
46
+ :::card{title="First" icon="Heading1"}
47
+ Card content.
48
+ :::
49
+ :::card{title="Second" icon="Heading2"}
50
+ Card content.
51
+ :::
52
+ ::::
195
53
  ```
196
54
 
197
- ## Link
198
-
199
- Styled links with external indicator.
55
+ ## Mermaid
200
56
 
201
- ```markdown:filename.mdx
202
- [Visit OpenAI](https://www.openai.com)
57
+ ```mermaid
58
+ flowchart LR
59
+ A --> B
203
60
  ```
204
61
 
205
- ## Keyboard
206
-
207
- Display keyboard shortcuts.
62
+ ## Tooltip
208
63
 
209
- ```tsx
210
- {/* Windows style (default) */}
211
- <Kbd show="ctrl" /> + <Kbd show="v" />
212
-
213
- {/* Mac style */}
214
- <Kbd show="cmd" type="mac" /> + <Kbd show="v" type="mac" />
64
+ ```md
65
+ Inline :tooltip[hover me]{tip="Extra details"}.
215
66
  ```
216
67
 
217
- Renders as:
218
- - Windows: <Kbd show="ctrl" type="window" /> + <Kbd show="v" type="window" />
219
- - Mac: <Kbd show="cmd" type="mac" /> + <Kbd show="v" type="mac" />
220
-
221
- ## Tooltips
222
-
223
- Hover tooltips for inline content.
68
+ ## Youtube
224
69
 
225
- ```markdown:filename.mdx
226
- This a tooltip <Tooltip text="Hover me show on top" tip="npx @docubook/cli@latest" side="top" /> and <Tooltip text="Hover me show on bottom" tip="npx @docubook/cli@latest" side="bottom" /> thanks you.
70
+ ```md
71
+ ::youtube{videoId="OPM2t54T-Vo"}
227
72
  ```
228
73
 
229
- ## YouTube
230
-
231
- Embed YouTube videos.
232
-
233
- ```markdown:filename.mdx
234
- <Youtube videoId="OPM2t54T-Vo" />
235
- ```
236
-
237
- > Usage
238
- > for example the youtube URL show this https://www.youtube.com/watch?v=OPM2t54T-Vo
239
- > the ID is `OPM2t54T-Vo`
240
-
241
- ## Release Note
242
-
243
- Display changelog entries.
244
-
245
- ```markdown:filename.mdx
246
- <Release version="1.10.1" date="2025-05-24" title="Accessibility Improvements and Bug Fixes">
247
- <Changes type="added">
248
- - New feature to improve accessibility
249
- - Keyboard navigation support for dialog components
250
- </Changes>
251
- <Changes type="fixed">
252
- - Bug fix for mobile menu
253
- - Fixed loading issues on documentation pages
254
- </Changes>
255
- </Release>
256
- ```
74
+ > All directives render server-side to static HTML — no runtime JavaScript unless the component needs it.
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  title: Routing
3
- description: How Flame maps MDX files to static HTML pages.
3
+ description: How Flame maps markdown files to static pages.
4
4
  ---
5
5
 
6
- Flame uses **filesystem-based routing** — every `.mdx` file in `docs/` compiles to a matching `.html` path:
6
+ Flame uses **filesystem-based routing** — every markdown file in `docs/` compiles to a matching `.html` path:
7
7
 
8
8
  ```
9
9
  docs/
@@ -16,10 +16,12 @@ docs/
16
16
  └── routing.mdx → /docs/guide/routing.html
17
17
  ```
18
18
 
19
- ## Auto-routing
19
+ ## Add a page
20
20
 
21
- When `routes: []` in `docu.json`, Flame automatically scans `docs/` at build time and generates the sidebar from the directory structure.
21
+ 1. Create `docs/guide/faq.mdx`
22
+ 2. Add frontmatter: `title` and `description`
23
+ 3. Write markdown — the page appears in the sidebar and search automatically
22
24
 
23
- ## Manual routes
25
+ ## Sidebar order
24
26
 
25
- Define `routes` in `docu.json` for a custom sidebar. Add `context` groups with icons for multi-section documentation. See this project's `docu.json` for examples.
27
+ When `routes: []`, Flame scans `docs/` and builds the sidebar from the directory structure. Define `routes` in `docu.json` for a custom order and grouped sections — see this project's `docu.json` for a working example.
@@ -1,15 +1,16 @@
1
1
  ---
2
- title: DocuBook Flame 🔥
3
- description: An open-source alternative to Mintlify or GitBook — MDX-powered documentation, static HTML output.
2
+ title: My Docs
3
+ description: Documentation powered by DocuBook Flame.
4
4
  ---
5
5
 
6
- **@docubook/flame** is a static site generator for documentation. Write in MDX, compile to flat static HTML — no server required. Runs on **Bun**, **Node.js**, and **Deno**.
6
+ Welcome to **My Docs** — a static documentation site built with [DocuBook Flame](https://www.docubook.pro/).
7
+
8
+ Flame compiles the markdown in `docs/` into flat static HTML. No server, no database, no runtime — deploy anywhere.
7
9
 
8
10
  ```mermaid
9
- flowchart LR
10
- A[Your .mdx content] --> B[flame build]
11
- B --> C[Static HTML + assets]
12
- C --> D[Vercel / Netlify / GitHub Pages / S3]
11
+ flowchart TD
12
+ A["docs/*.md"] --> B["flame build"]
13
+ B --> C["Static HTML + assets"]
14
+ C --> D["Vercel · Netlify · GitHub Pages · S3"]
13
15
  ```
14
16
 
15
- Check the sidebar for guides on getting started, configuration, components, routing, and deployment.
@@ -7,6 +7,9 @@
7
7
  "favicon": "/docs/assets/images/favicon.ico",
8
8
  "ogImage": "/docs/assets/images/og.png"
9
9
  },
10
+ "sidebar": {
11
+ "context": "separator"
12
+ },
10
13
  "themes": {
11
14
  "colors": "default"
12
15
  },
@@ -71,13 +74,10 @@
71
74
  "social": []
72
75
  },
73
76
  "repo": {
74
- "url": "",
77
+ "url": "https://github.com/DocuBook/docubook",
75
78
  "path": "blob/main/{filePath}",
76
79
  "edit": false
77
80
  },
78
- "sidebar": {
79
- "context": "dropdown"
80
- },
81
81
  "routes": [
82
82
  {
83
83
  "title": "Getting Started",