@docubook/flame 1.7.2 → 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 (67) hide show
  1. package/.docu/components/Context.tsx +1 -1
  2. package/.docu/components/Pagination.tsx +9 -1
  3. package/.docu/components/Search.tsx +6 -6
  4. package/.docu/components/Sidebar.tsx +37 -8
  5. package/.docu/components/Theme.tsx +2 -2
  6. package/.docu/components/Toc.tsx +52 -6
  7. package/.docu/components/Typography.tsx +1 -1
  8. package/.docu/components/home/Hero.tsx +4 -4
  9. package/.docu/components/registry.ts +1 -1
  10. package/.docu/lib/build.deno.js +3 -3
  11. package/.docu/lib/{build.impl-TSIF3F7O.js → build.impl-VXB4KL4D.js} +3 -3
  12. package/.docu/lib/build.node.js +3 -3
  13. package/.docu/lib/{chunk-SPHVBXRR.js → chunk-7PRQ3RQB.js} +6 -6
  14. package/.docu/lib/{chunk-DITXUPUV.js → chunk-A6FIEG3H.js} +61 -10
  15. package/.docu/lib/{chunk-PJIJEPNR.js → chunk-JRERMREW.js} +5 -4
  16. package/.docu/lib/{chunk-KMDGSD57.js → chunk-LZDEWK25.js} +3 -3
  17. package/.docu/lib/{chunk-HRO7ONJQ.js → chunk-MQWWCO6O.js} +70 -27
  18. package/.docu/lib/{chunk-B6LGUADD.js → chunk-TRT6WQZG.js} +346 -183
  19. package/.docu/lib/chunk-UISOJ4RW.js +114 -0
  20. package/.docu/lib/clean.js +1 -1
  21. package/.docu/lib/deploy.deno.js +1 -1
  22. package/.docu/lib/deploy.node.js +1 -1
  23. package/.docu/lib/preview.deno.js +5 -3
  24. package/.docu/lib/preview.node.js +5 -3
  25. package/.docu/lib/server.deno.js +6 -4
  26. package/.docu/lib/server.node.js +6 -4
  27. package/.docu/node/build.impl.ts +75 -26
  28. package/.docu/node/build.ts +70 -17
  29. package/.docu/node/client.ts +64 -27
  30. package/.docu/node/deploy.shared.ts +5 -5
  31. package/.docu/node/deploy.ts +1 -1
  32. package/.docu/node/html.shared.ts +7 -2
  33. package/.docu/node/html.ts +4 -2
  34. package/.docu/node/hydrate.node.ts +69 -4
  35. package/.docu/node/hydrate.ts +50 -1
  36. package/.docu/node/mdx-manifest.d.ts +5 -0
  37. package/.docu/node/mdx.ts +103 -17
  38. package/.docu/node/preview.deno.ts +1 -1
  39. package/.docu/node/preview.impl.ts +3 -3
  40. package/.docu/node/preview.node.ts +1 -1
  41. package/.docu/node/preview.ts +2 -2
  42. package/.docu/node/route.ts +57 -1
  43. package/.docu/node/runtime/bun.ts +28 -0
  44. package/.docu/node/runtime/deno.ts +34 -0
  45. package/.docu/node/runtime/index.ts +4 -0
  46. package/.docu/node/runtime/node.ts +107 -0
  47. package/.docu/node/runtime/types.ts +19 -0
  48. package/.docu/node/search-indexer.ts +3 -2
  49. package/.docu/node/seo.ts +2 -2
  50. package/.docu/node/server-routes.ts +32 -13
  51. package/.docu/node/server.deno.ts +1 -1
  52. package/.docu/node/server.impl.ts +49 -3
  53. package/.docu/node/server.node.ts +1 -1
  54. package/.docu/node/server.ts +45 -1
  55. package/.docu/pages/docs/[[...slug]].tsx +13 -4
  56. package/.docu/pages/index.tsx +1 -1
  57. package/.docu/styles/globals.css +38 -20
  58. package/README.md +7 -48
  59. package/bin/cli.js +25 -7
  60. package/package.json +7 -6
  61. package/template/README.md +7 -48
  62. package/template/docs/getting-started/configuration.mdx +16 -4
  63. package/template/docs/getting-started/overview.mdx +34 -42
  64. package/template/docs/guide/components.mdx +46 -228
  65. package/template/docs/guide/routing.mdx +8 -6
  66. package/template/docs/index.mdx +9 -8
  67. package/template/docu.json +4 -4
@@ -801,13 +801,12 @@ function collectAllLucideIcons() {
801
801
  join2(FRAMEWORK_ROOT, "..", "mdx-content", "dist"),
802
802
  join2(FRAMEWORK_ROOT, "..", "ui-react", "dist"),
803
803
  join2(FRAMEWORK_ROOT, "..", "core", "dist"),
804
- join2(FRAMEWORK_ROOT, "..", "runt", "dist"),
805
804
  join2(FRAMEWORK_ROOT, "..", "themes-colors", "dist")
806
805
  ];
807
806
  for (const d of depDirs) scanDirLucideIcons(resolve2(d), icons);
808
807
  return [...icons];
809
808
  }
810
- async function buildClientBundle() {
809
+ async function buildClientBundle(mdxSources = {}) {
811
810
  await mkdir(ASSETS_DIR, { recursive: true });
812
811
  const twKey = tailwindCacheKey();
813
812
  await cleanOldBundles(/* @__PURE__ */ new Set([`client-${twKey}.css`]));
@@ -854,7 +853,7 @@ async function buildClientBundle() {
854
853
  }
855
854
  if (args.importer) {
856
855
  const normalized = normalizeImporterPath(args.importer);
857
- if (normalized.includes("/mdx-content/dist/")) {
856
+ if (normalized.includes("/markdown/dist/")) {
858
857
  return { path: getLucideRealEntry(), namespace: "file" };
859
858
  }
860
859
  }
@@ -895,6 +894,61 @@ async function buildClientBundle() {
895
894
  };
896
895
  });
897
896
  }
897
+ },
898
+ {
899
+ // Serves per-page compiled MDX (program format) as real modules so
900
+ // the client hydrates the content island without `new Function`.
901
+ // client.ts imports `{ mdxModules } from "./mdx-manifest"`.
902
+ name: "mdx-hydrate",
903
+ setup(build2) {
904
+ const require2 = createRequire(import.meta.url);
905
+ const mdxModuleDeps = {
906
+ "react/jsx-runtime": require2.resolve("react/jsx-runtime"),
907
+ "react/jsx-dev-runtime": require2.resolve("react/jsx-dev-runtime"),
908
+ "@mdx-js/react": require2.resolve("@mdx-js/react")
909
+ };
910
+ build2.onResolve(
911
+ { filter: /^(react\/jsx-runtime|react\/jsx-dev-runtime|@mdx-js\/react)$/ },
912
+ (args) => {
913
+ if (args.namespace !== "mdx-module") return;
914
+ return { path: mdxModuleDeps[args.path], namespace: "file" };
915
+ }
916
+ );
917
+ build2.onResolve({ filter: /^mdx-module:/ }, (args) => ({
918
+ path: args.path,
919
+ namespace: "mdx-module"
920
+ }));
921
+ build2.onLoad({ filter: /.*/, namespace: "mdx-module" }, (args) => {
922
+ const slug = args.path.slice("mdx-module:".length);
923
+ const contents = mdxSources[slug];
924
+ if (contents == null) {
925
+ return {
926
+ errors: [{ text: `unknown mdx module: ${slug}` }],
927
+ contents: "",
928
+ loader: "js"
929
+ };
930
+ }
931
+ return { contents, loader: "js" };
932
+ });
933
+ build2.onResolve({ filter: /mdx-manifest$/ }, (args) => ({
934
+ path: args.path,
935
+ namespace: "mdx-manifest"
936
+ }));
937
+ build2.onLoad({ filter: /.*/, namespace: "mdx-manifest" }, () => {
938
+ const slugs = Object.keys(mdxSources).sort();
939
+ const imports = slugs.map((slug, i) => {
940
+ const key = slug.replace(/["\\]/g, "");
941
+ return `import * as _mdx${i} from "mdx-module:${key}";`;
942
+ }).join("\n");
943
+ const map = slugs.map((slug, i) => `${JSON.stringify(slug)}: _mdx${i}`).join(", ");
944
+ return {
945
+ contents: `${imports}
946
+ export const mdxModules = { ${map} };
947
+ `,
948
+ loader: "js"
949
+ };
950
+ });
951
+ }
898
952
  }
899
953
  ]
900
954
  });
@@ -915,10 +969,196 @@ async function buildClientBundle() {
915
969
  return { js: jsFile, css: cssFile };
916
970
  }
917
971
 
972
+ // .docu/node/git.ts
973
+ import { execFile as execFile2 } from "node:child_process";
974
+ import { stat } from "node:fs/promises";
975
+ function runGit(args) {
976
+ return new Promise((resolve4, reject) => {
977
+ execFile2("git", args, { maxBuffer: 16 * 1024 * 1024 }, (err, stdout) => {
978
+ if (err) reject(err);
979
+ else resolve4(stdout);
980
+ });
981
+ });
982
+ }
983
+ function sanitizePath(filePath) {
984
+ const cleanPath = filePath.replace(/^\//, "");
985
+ if (!cleanPath || !/^[a-zA-Z0-9\-_/.\s]+$/.test(cleanPath) || /(^|\/)\.\.($|\/)/.test(cleanPath))
986
+ return null;
987
+ return cleanPath;
988
+ }
989
+ async function getGitLastModified(filePath) {
990
+ const cleanPath = sanitizePath(filePath);
991
+ if (!cleanPath) return null;
992
+ try {
993
+ const text = await runGit(["log", "-1", "--format=%cI", "--", cleanPath]);
994
+ const date = text.trim();
995
+ return date || null;
996
+ } catch {
997
+ return null;
998
+ }
999
+ }
1000
+ async function getFilesystemMtime(filePath) {
1001
+ try {
1002
+ const stats = await stat(filePath);
1003
+ return stats.mtime.toISOString();
1004
+ } catch {
1005
+ return null;
1006
+ }
1007
+ }
1008
+ async function getGitLastModifiedBatch(filePaths) {
1009
+ const result = /* @__PURE__ */ new Map();
1010
+ if (filePaths.length === 0) return result;
1011
+ const safePaths = [];
1012
+ for (const fp of filePaths) {
1013
+ const cleanPath = sanitizePath(fp);
1014
+ if (!cleanPath) {
1015
+ console.warn(`[git] getGitLastModifiedBatch: skipping invalid path "${fp}"`);
1016
+ continue;
1017
+ }
1018
+ safePaths.push(cleanPath);
1019
+ }
1020
+ if (safePaths.length === 0) return result;
1021
+ try {
1022
+ const text = await runGit([
1023
+ "log",
1024
+ "--format=%cI",
1025
+ "--name-only",
1026
+ "--diff-filter=ACMR",
1027
+ ...safePaths
1028
+ ]);
1029
+ let currentDate = "";
1030
+ for (const line of text.split("\n")) {
1031
+ const trimmed = line.trim();
1032
+ if (!trimmed) continue;
1033
+ if (/^\d{4}-\d{2}-\d{2}T/.test(trimmed)) {
1034
+ currentDate = trimmed;
1035
+ } else if (currentDate && !result.has(trimmed)) {
1036
+ result.set(trimmed, currentDate);
1037
+ }
1038
+ }
1039
+ } catch (err) {
1040
+ console.error("Failed to get git last modified batch for", filePaths, err);
1041
+ }
1042
+ return result;
1043
+ }
1044
+
1045
+ // .docu/node/mdx.ts
1046
+ import React from "react";
1047
+ import { z } from "zod";
1048
+ import {
1049
+ serialize,
1050
+ extractTocsFromRawMdx,
1051
+ extractFrontmatterWithContent,
1052
+ createDefaultRehypePlugins,
1053
+ createDefaultRemarkPlugins,
1054
+ MDXRemote
1055
+ } from "@docubook/core";
1056
+ import { createMdxComponents } from "@docubook/markdown";
1057
+ function appendHtml(value) {
1058
+ if (typeof value !== "string") return null;
1059
+ if (/^https?:\/\//.test(value)) return null;
1060
+ if (!value.startsWith("/docs/")) return null;
1061
+ if (value.includes("#")) return null;
1062
+ if (value.endsWith(".html")) return null;
1063
+ return `${value}.html`;
1064
+ }
1065
+ function rehypeDocsHtmlLinks() {
1066
+ return (tree) => {
1067
+ function walk(node) {
1068
+ if (node.type === "element" && node.tagName === "a") {
1069
+ const fixed = appendHtml(node.properties?.href);
1070
+ if (fixed) node.properties.href = fixed;
1071
+ }
1072
+ if (node.children) {
1073
+ for (const child of node.children) walk(child);
1074
+ }
1075
+ }
1076
+ walk(tree);
1077
+ return tree;
1078
+ };
1079
+ }
1080
+ function remarkMdxJsxDocsHtmlLinks() {
1081
+ return (tree) => {
1082
+ function walk(node) {
1083
+ if ((node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") && node.attributes) {
1084
+ for (const attr of node.attributes) {
1085
+ if (attr.type === "mdxJsxAttribute" && attr.name === "href") {
1086
+ const fixed = appendHtml(attr.value);
1087
+ if (fixed) attr.value = fixed;
1088
+ }
1089
+ }
1090
+ }
1091
+ if (node.children) {
1092
+ for (const child of node.children) walk(child);
1093
+ }
1094
+ }
1095
+ walk(tree);
1096
+ return tree;
1097
+ };
1098
+ }
1099
+ var frontmatterSchema = z.object({
1100
+ title: z.coerce.string().optional(),
1101
+ description: z.coerce.string().optional(),
1102
+ image: z.coerce.string().optional(),
1103
+ date: z.coerce.string().optional()
1104
+ });
1105
+ function frontmatterField(frontmatter, key) {
1106
+ return typeof frontmatter[key] === "string" ? frontmatter[key] : "";
1107
+ }
1108
+ async function serializeWithDocPlugins(rawMdx, opts = {}) {
1109
+ const { strippedContent } = extractFrontmatterWithContent(
1110
+ rawMdx,
1111
+ opts.frontmatterSchema
1112
+ );
1113
+ const defaultRemark = createDefaultRemarkPlugins();
1114
+ const defaultRehype = createDefaultRehypePlugins();
1115
+ const finalRemark = [...defaultRemark, remarkMdxJsxDocsHtmlLinks, ...opts.remarkPlugins ?? []];
1116
+ const finalRehype = [...defaultRehype, rehypeDocsHtmlLinks, ...opts.rehypePlugins ?? []];
1117
+ return serialize(strippedContent, {
1118
+ outputFormat: opts.outputFormat,
1119
+ format: "md",
1120
+ mdxOptions: {
1121
+ rehypePlugins: finalRehype,
1122
+ remarkPlugins: finalRemark
1123
+ }
1124
+ });
1125
+ }
1126
+ async function compileMdx(rawMdx, filePath, gitDates, remarkPlugins, rehypePlugins, frontmatterSchema2) {
1127
+ const tocs = extractTocsFromRawMdx(rawMdx);
1128
+ const { frontmatter } = extractFrontmatterWithContent(rawMdx, frontmatterSchema2);
1129
+ const serialized = await serializeWithDocPlugins(rawMdx, {
1130
+ remarkPlugins,
1131
+ rehypePlugins,
1132
+ frontmatterSchema: frontmatterSchema2
1133
+ });
1134
+ const components = createMdxComponents();
1135
+ const content = React.createElement(MDXRemote, {
1136
+ compiledSource: serialized.compiledSource,
1137
+ scope: {},
1138
+ frontmatter: {},
1139
+ components
1140
+ });
1141
+ const date = frontmatter.date || gitDates?.get(filePath) || await getGitLastModified(filePath) || await getFilesystemMtime(filePath) || void 0;
1142
+ return {
1143
+ content,
1144
+ compiledSource: serialized.compiledSource,
1145
+ frontmatter: { ...frontmatter, date },
1146
+ tocs
1147
+ };
1148
+ }
1149
+ async function compileMdxModule(rawMdx, remarkPlugins, rehypePlugins) {
1150
+ const serialized = await serializeWithDocPlugins(rawMdx, {
1151
+ outputFormat: "program",
1152
+ remarkPlugins,
1153
+ rehypePlugins
1154
+ });
1155
+ return serialized.compiledSource;
1156
+ }
1157
+
918
1158
  // .docu/node/search-indexer.ts
919
1159
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
920
1160
  import { resolve as resolve3, join as join3 } from "node:path";
921
- import { extractFrontmatterWithContent } from "@docubook/core";
1161
+ import { extractFrontmatterWithContent as extractFrontmatterWithContent2 } from "@docubook/core";
922
1162
  var docuConfig = loadDocuConfig();
923
1163
  function getSectionTitle(filePath) {
924
1164
  const parts = filePath.split("/");
@@ -943,11 +1183,11 @@ function stripJsx(content) {
943
1183
  return result;
944
1184
  }
945
1185
  function extractRecords(filePath, raw) {
946
- const { frontmatter, strippedContent: content } = extractFrontmatterWithContent(raw);
1186
+ const { frontmatter, strippedContent: content } = extractFrontmatterWithContent2(raw);
947
1187
  const records = [];
948
1188
  const url = docsHtmlHref(`/docs/${filePath}`);
949
1189
  const lvl0 = getSectionTitle(filePath);
950
- const lvl1 = frontmatter.title || null;
1190
+ const lvl1 = frontmatterField(frontmatter, "title") || null;
951
1191
  const hierarchy = {
952
1192
  lvl0,
953
1193
  lvl1,
@@ -961,7 +1201,7 @@ function extractRecords(filePath, raw) {
961
1201
  records.push({
962
1202
  url,
963
1203
  hierarchy: { ...hierarchy },
964
- content: frontmatter.description || null,
1204
+ content: frontmatterField(frontmatter, "description") || null,
965
1205
  type: "lvl1"
966
1206
  });
967
1207
  }
@@ -1071,161 +1311,6 @@ function captureException(err, context) {
1071
1311
  sentry.captureException(err, context ? { extra: context } : void 0);
1072
1312
  }
1073
1313
 
1074
- // .docu/node/git.ts
1075
- import { execFile as execFile2 } from "node:child_process";
1076
- import { stat } from "node:fs/promises";
1077
- function runGit(args) {
1078
- return new Promise((resolve4, reject) => {
1079
- execFile2("git", args, { maxBuffer: 16 * 1024 * 1024 }, (err, stdout) => {
1080
- if (err) reject(err);
1081
- else resolve4(stdout);
1082
- });
1083
- });
1084
- }
1085
- function sanitizePath(filePath) {
1086
- const cleanPath = filePath.replace(/^\//, "");
1087
- if (!cleanPath || !/^[a-zA-Z0-9\-_/.\s]+$/.test(cleanPath) || /(^|\/)\.\.($|\/)/.test(cleanPath))
1088
- return null;
1089
- return cleanPath;
1090
- }
1091
- async function getGitLastModified(filePath) {
1092
- const cleanPath = sanitizePath(filePath);
1093
- if (!cleanPath) return null;
1094
- try {
1095
- const text = await runGit(["log", "-1", "--format=%cI", "--", cleanPath]);
1096
- const date = text.trim();
1097
- return date || null;
1098
- } catch {
1099
- return null;
1100
- }
1101
- }
1102
- async function getFilesystemMtime(filePath) {
1103
- try {
1104
- const stats = await stat(filePath);
1105
- return stats.mtime.toISOString();
1106
- } catch {
1107
- return null;
1108
- }
1109
- }
1110
- async function getGitLastModifiedBatch(filePaths) {
1111
- const result = /* @__PURE__ */ new Map();
1112
- if (filePaths.length === 0) return result;
1113
- const safePaths = [];
1114
- for (const fp of filePaths) {
1115
- const cleanPath = sanitizePath(fp);
1116
- if (!cleanPath) {
1117
- console.warn(`[git] getGitLastModifiedBatch: skipping invalid path "${fp}"`);
1118
- continue;
1119
- }
1120
- safePaths.push(cleanPath);
1121
- }
1122
- if (safePaths.length === 0) return result;
1123
- try {
1124
- const text = await runGit([
1125
- "log",
1126
- "--format=%cI",
1127
- "--name-only",
1128
- "--diff-filter=ACMR",
1129
- ...safePaths
1130
- ]);
1131
- let currentDate = "";
1132
- for (const line of text.split("\n")) {
1133
- const trimmed = line.trim();
1134
- if (!trimmed) continue;
1135
- if (/^\d{4}-\d{2}-\d{2}T/.test(trimmed)) {
1136
- currentDate = trimmed;
1137
- } else if (currentDate && !result.has(trimmed)) {
1138
- result.set(trimmed, currentDate);
1139
- }
1140
- }
1141
- } catch (err) {
1142
- console.error("Failed to get git last modified batch for", filePaths, err);
1143
- }
1144
- return result;
1145
- }
1146
-
1147
- // .docu/node/mdx.ts
1148
- import React from "react";
1149
- import {
1150
- serialize,
1151
- extractTocsFromRawMdx,
1152
- extractFrontmatterWithContent as extractFrontmatterWithContent2,
1153
- createDefaultRehypePlugins,
1154
- createDefaultRemarkPlugins,
1155
- MDXRemote
1156
- } from "@docubook/core";
1157
- import { createMdxComponents } from "@docubook/mdx-content";
1158
- function appendHtml(value) {
1159
- if (typeof value !== "string") return null;
1160
- if (/^https?:\/\//.test(value)) return null;
1161
- if (!value.startsWith("/docs/")) return null;
1162
- if (value.includes("#")) return null;
1163
- if (value.endsWith(".html")) return null;
1164
- return `${value}.html`;
1165
- }
1166
- function rehypeDocsHtmlLinks() {
1167
- return (tree) => {
1168
- function walk(node) {
1169
- if (node.type === "element" && node.tagName === "a") {
1170
- const fixed = appendHtml(node.properties?.href);
1171
- if (fixed) node.properties.href = fixed;
1172
- }
1173
- if (node.children) {
1174
- for (const child of node.children) walk(child);
1175
- }
1176
- }
1177
- walk(tree);
1178
- return tree;
1179
- };
1180
- }
1181
- function remarkMdxJsxDocsHtmlLinks() {
1182
- return (tree) => {
1183
- function walk(node) {
1184
- if ((node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") && node.attributes) {
1185
- for (const attr of node.attributes) {
1186
- if (attr.type === "mdxJsxAttribute" && attr.name === "href") {
1187
- const fixed = appendHtml(attr.value);
1188
- if (fixed) attr.value = fixed;
1189
- }
1190
- }
1191
- }
1192
- if (node.children) {
1193
- for (const child of node.children) walk(child);
1194
- }
1195
- }
1196
- walk(tree);
1197
- return tree;
1198
- };
1199
- }
1200
- async function compileMdx(rawMdx, filePath, gitDates, remarkPlugins, rehypePlugins) {
1201
- const tocs = extractTocsFromRawMdx(rawMdx);
1202
- const { frontmatter, strippedContent } = extractFrontmatterWithContent2(rawMdx);
1203
- const defaultRemark = createDefaultRemarkPlugins();
1204
- const defaultRehype = createDefaultRehypePlugins();
1205
- const finalRemark = [...defaultRemark, remarkMdxJsxDocsHtmlLinks, ...remarkPlugins ?? []];
1206
- const finalRehype = [...defaultRehype, rehypeDocsHtmlLinks, ...rehypePlugins ?? []];
1207
- const serialized = await serialize(strippedContent, {
1208
- mdxOptions: {
1209
- rehypePlugins: finalRehype,
1210
- remarkPlugins: finalRemark
1211
- }
1212
- });
1213
- const components = createMdxComponents();
1214
- const content = React.createElement(MDXRemote, {
1215
- compiledSource: serialized.compiledSource,
1216
- scope: {},
1217
- frontmatter: {},
1218
- components
1219
- });
1220
- const date = frontmatter.date || gitDates?.get(filePath) || await getGitLastModified(filePath) || await getFilesystemMtime(filePath) || void 0;
1221
- return {
1222
- content,
1223
- compiledSource: serialized.compiledSource,
1224
- frontmatter: { ...frontmatter, date },
1225
- tocs
1226
- };
1227
- }
1228
-
1229
1314
  // .docu/pages/docs/[[...slug]].tsx
1230
1315
  import { ChevronLeft, ChevronRight } from "lucide-react";
1231
1316
 
@@ -1248,6 +1333,9 @@ function DocsBreadcrumb({ paths }) {
1248
1333
  }
1249
1334
 
1250
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";
1251
1339
  var docuConfig2 = loadDocuConfig();
1252
1340
  var routes = resolveRoutes(docuConfig2.routes);
1253
1341
  function flattenRoutes() {
@@ -1276,8 +1364,42 @@ function getRouteMap() {
1276
1364
  routes.forEach((route) => traverse(route));
1277
1365
  return map;
1278
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
+ }
1279
1387
  function getPreviousNext(pathname) {
1280
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
+ }
1281
1403
  const paths = flattenRoutes();
1282
1404
  const index = paths.findIndex((href) => href === `/${normalizedPath}` || href === normalizedPath);
1283
1405
  if (index === -1) {
@@ -1288,7 +1410,11 @@ function getPreviousNext(pathname) {
1288
1410
  const nextHref = index < paths.length - 1 ? paths[index + 1] : null;
1289
1411
  return {
1290
1412
  prev: prevHref ? { href: prevHref, title: routeMap.get(prevHref) || "" } : null,
1291
- 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
1292
1418
  };
1293
1419
  }
1294
1420
 
@@ -1310,7 +1436,11 @@ function Pagination({
1310
1436
  PaginationDocs,
1311
1437
  {
1312
1438
  prev: prev ? { href: docsHtmlHref(`/docs${prev.href}`), title: prev.title } : void 0,
1313
- 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,
1314
1444
  className,
1315
1445
  prevIcon,
1316
1446
  nextIcon,
@@ -1322,7 +1452,7 @@ function Pagination({
1322
1452
  // .docu/components/Typography.tsx
1323
1453
  import { jsx as jsx3 } from "react/jsx-runtime";
1324
1454
  function Typography({ children }) {
1325
- return /* @__PURE__ */ jsx3("div", { className: "prose prose-zinc dark:prose-invert prose-code:font-code dark:prose-code:bg-stone-900/25 prose-code:bg-stone-50 prose-pre:bg-background max-lg:prose-headings:scroll-mt-54 prose-headings:scroll-mt-4 prose-code:text-sm prose-code:leading-6 dark:prose-code:text-white prose-code:text-stone-800 prose-code:p-1 prose-code:rounded-md prose-code:border prose-img:rounded-md prose-img:border prose-code:before:content-none prose-code:after:content-none prose-code:px-1.5 prose-code:overflow-x-auto prose-img:my-3 prose-h2:my-4 prose-h2:mt-8 max-w-full! w-full pt-2", children });
1455
+ return /* @__PURE__ */ jsx3("div", { className: "prose prose-zinc dark:prose-invert prose-code:font-code dark:prose-code:bg-stone-900/25 prose-code:bg-stone-50 prose-pre:bg-background max-lg:prose-headings:scroll-mt-16 prose-headings:scroll-mt-4 prose-code:text-sm prose-code:leading-6 dark:prose-code:text-white prose-code:text-stone-800 prose-code:p-1 prose-code:rounded-md prose-code:border prose-img:rounded-md prose-img:border prose-code:before:content-none prose-code:after:content-none prose-code:px-1.5 prose-code:overflow-x-auto prose-img:my-3 prose-h2:my-4 prose-h2:mt-8 w-full max-w-full! pt-2", children });
1326
1456
  }
1327
1457
 
1328
1458
  // .docu/components/EditWith.tsx
@@ -1586,12 +1716,19 @@ function Toc({ tocs }) {
1586
1716
  useEffect2(() => {
1587
1717
  activeIdRef.current = activeId;
1588
1718
  }, [activeId]);
1719
+ const getAnchorOffset = useCallback2(() => {
1720
+ if (typeof window === "undefined") return 100;
1721
+ const first = tocs[0] ? document.getElementById(tocs[0].href.slice(1)) : null;
1722
+ const margin = first ? parseFloat(getComputedStyle(first).scrollMarginTop) : 0;
1723
+ return margin || 100;
1724
+ }, [tocs]);
1589
1725
  useEffect2(() => {
1590
- if (typeof document === "undefined" || !tocs.length) return;
1726
+ if (typeof window === "undefined" || window.innerWidth < 1024) return;
1727
+ if (!tocs.length) return;
1591
1728
  const isDesktop = window.innerWidth >= 1024;
1592
1729
  const container = isDesktop ? document.getElementById("scroll-container") : null;
1593
1730
  const scrollTarget = container || window;
1594
- const offset = isDesktop ? 80 : 100;
1731
+ const offset = getAnchorOffset();
1595
1732
  const handleScroll = () => {
1596
1733
  if (clickedIdRef.current) return;
1597
1734
  let currentId = null;
@@ -1625,7 +1762,7 @@ function Toc({ tocs }) {
1625
1762
  scrollTarget.removeEventListener("scroll", listener);
1626
1763
  if (throttleTimer) clearTimeout(throttleTimer);
1627
1764
  };
1628
- }, [tocs]);
1765
+ }, [tocs, getAnchorOffset]);
1629
1766
  const handleLinkClick = useCallback2((id) => {
1630
1767
  clickedIdRef.current = id;
1631
1768
  setActiveId(id);
@@ -1647,7 +1784,7 @@ function Toc({ tocs }) {
1647
1784
  /* @__PURE__ */ jsx8("h3", { className: "text-sm font-medium", children: "On this page" })
1648
1785
  ] }),
1649
1786
  /* @__PURE__ */ jsx8("div", { className: "relative", children: /* @__PURE__ */ jsxs5("div", { className: "relative text-sm", children: [
1650
- /* @__PURE__ */ jsx8("div", { className: "bg-base-300 absolute left-0 top-0 h-full w-px" }),
1787
+ /* @__PURE__ */ jsx8("div", { className: "bg-base-300 absolute top-0 left-0 h-full w-px" }),
1651
1788
  /* @__PURE__ */ jsx8("div", { className: "flex flex-col", children: tocs.map(({ href, level, text }) => {
1652
1789
  const id = href.slice(1);
1653
1790
  const isActive = activeId === id;
@@ -1697,14 +1834,29 @@ function Toc({ tocs }) {
1697
1834
  e.preventDefault();
1698
1835
  handleLinkClick(id);
1699
1836
  const el = document.getElementById(id);
1700
- if (el) el.scrollIntoView({ behavior: "smooth" });
1837
+ if (el) {
1838
+ const offset = getAnchorOffset();
1839
+ const scroller = window.innerWidth >= 1024 ? document.getElementById("scroll-container") : null;
1840
+ const elTop = el.getBoundingClientRect().top;
1841
+ if (scroller) {
1842
+ scroller.scrollTo({
1843
+ top: elTop - scroller.getBoundingClientRect().top + scroller.scrollTop - offset,
1844
+ behavior: "smooth"
1845
+ });
1846
+ } else {
1847
+ window.scrollTo({
1848
+ top: elTop + window.scrollY - offset,
1849
+ behavior: "smooth"
1850
+ });
1851
+ }
1852
+ }
1701
1853
  },
1702
1854
  className: cn(
1703
1855
  "flex flex-1 items-center py-2 transition-all duration-200",
1704
1856
  isActive ? "text-primary font-medium" : "text-base-content/60 hover:text-base-content"
1705
1857
  ),
1706
1858
  style: { paddingLeft: `${levelPadding + 6}px` },
1707
- children: /* @__PURE__ */ jsx8("span", { className: "line-clamp-2 break-words text-sm", children: text })
1859
+ children: /* @__PURE__ */ jsx8("span", { className: "line-clamp-2 text-sm break-words", children: text })
1708
1860
  }
1709
1861
  )
1710
1862
  ]
@@ -1728,6 +1880,7 @@ function DocsPage({
1728
1880
  tocs,
1729
1881
  filePath,
1730
1882
  repoUrl,
1883
+ mdxSlug,
1731
1884
  compiledSource
1732
1885
  }) {
1733
1886
  const pathname = slug.join("/");
@@ -1736,7 +1889,7 @@ function DocsPage({
1736
1889
  "div",
1737
1890
  {
1738
1891
  id: "scroll-container",
1739
- className: "bg-base-100 border-base-300 max-lg:scroll-p-54 relative flex w-full flex-col items-start rounded-b-3xl border shadow-md lg:h-full lg:flex-row lg:overflow-y-auto lg:rounded-xl",
1892
+ className: "bg-base-100 border-base-300 relative flex w-full flex-col items-start rounded-b-3xl border shadow-md max-lg:scroll-p-54 lg:h-full lg:flex-row lg:overflow-y-auto lg:rounded-xl",
1740
1893
  children: [
1741
1894
  /* @__PURE__ */ jsx9(
1742
1895
  "div",
@@ -1758,7 +1911,14 @@ function DocsPage({
1758
1911
  /* @__PURE__ */ jsxs6(Typography, { children: [
1759
1912
  /* @__PURE__ */ jsx9("h1", { className: "-mt-0.5 text-3xl", children: title }),
1760
1913
  description && /* @__PURE__ */ jsx9("p", { className: "text-muted-foreground -mt-4 text-[16.5px]", children: description }),
1761
- /* @__PURE__ */ jsx9("div", { id: "mdx-content-island", children: content }),
1914
+ /* @__PURE__ */ jsx9(
1915
+ "div",
1916
+ {
1917
+ id: "mdx-content-island",
1918
+ "data-mdx-slug": mdxSlug,
1919
+ dangerouslySetInnerHTML: { __html: content }
1920
+ }
1921
+ ),
1762
1922
  compiledSource && /* @__PURE__ */ jsx9(
1763
1923
  "script",
1764
1924
  {
@@ -1842,8 +2002,8 @@ function renderActionButtonIcon(iconName, className) {
1842
2002
  }
1843
2003
  function ActionButton({ action }) {
1844
2004
  const themeClasses = {
1845
- primary: "bg-primary text-primary-content hover:bg-primary/90",
1846
- secondary: "bg-secondary text-secondary-content hover:bg-secondary/90",
2005
+ primary: "bg-primary text-primary-foreground hover:bg-primary/90",
2006
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/90",
1847
2007
  ghost: "bg-transparent text-muted-foreground border border-base-300 hover:bg-base-200"
1848
2008
  };
1849
2009
  const isExternal = isExternalLink(action.link);
@@ -1868,8 +2028,8 @@ function Hero({ hero, className }) {
1868
2028
  const { tagline, headline, description, actions } = hero;
1869
2029
  return /* @__PURE__ */ jsx12("div", { className: cn("mx-auto max-w-4xl px-6 py-32 sm:py-44", className), children: /* @__PURE__ */ jsxs8("div", { className: "text-center", children: [
1870
2030
  tagline && /* @__PURE__ */ jsx12("p", { className: "text-primary mb-4 text-lg font-semibold", children: tagline }),
1871
- /* @__PURE__ */ jsx12("h1", { className: "text-balance text-5xl font-semibold tracking-tight sm:text-7xl", children: headline }),
1872
- description && /* @__PURE__ */ jsx12("p", { className: "text-muted-foreground mt-8 text-pretty text-lg sm:text-xl", children: description }),
2031
+ /* @__PURE__ */ jsx12("h1", { className: "text-5xl font-semibold tracking-tight text-balance sm:text-7xl", children: headline }),
2032
+ description && /* @__PURE__ */ jsx12("p", { className: "text-muted-foreground mt-8 text-lg text-pretty sm:text-xl", children: description }),
1873
2033
  actions && actions.length > 0 && /* @__PURE__ */ jsx12("div", { className: "mt-10 flex flex-wrap items-center justify-center gap-4", children: actions.map((action, index) => /* @__PURE__ */ jsx12(ActionButton, { action }, index)) })
1874
2034
  ] }) });
1875
2035
  }
@@ -1956,7 +2116,7 @@ function IndexPage() {
1956
2116
  description: meta.description
1957
2117
  };
1958
2118
  return /* @__PURE__ */ jsxs10("div", { className: "bg-base-100 relative isolate min-h-screen overflow-hidden", children: [
1959
- /* @__PURE__ */ jsx14("div", { className: "absolute right-4 top-4 z-10", id: "theme-island" }),
2119
+ /* @__PURE__ */ jsx14("div", { className: "absolute top-4 right-4 z-10", id: "theme-island" }),
1960
2120
  /* @__PURE__ */ jsx14(
1961
2121
  "div",
1962
2122
  {
@@ -2360,7 +2520,8 @@ function htmlShell(opts) {
2360
2520
  themeCss,
2361
2521
  depth = 0,
2362
2522
  headExtra,
2363
- bodyExtra
2523
+ bodyExtra,
2524
+ absoluteAssets = false
2364
2525
  } = opts;
2365
2526
  const nonceAttr = nonce ? ` nonce="${escapeHtml(nonce)}"` : "";
2366
2527
  const themeStyle = themeCss ? `
@@ -2370,8 +2531,8 @@ function htmlShell(opts) {
2370
2531
  const bodyInjection = bodyExtra?.length ? `
2371
2532
  ${bodyExtra.join("\n ")}` : "";
2372
2533
  const depthPrefix = depth === 0 ? "" : "../".repeat(depth);
2373
- const assetPrefix = depthPrefix + "assets/";
2374
- 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;
2375
2536
  let seoTags = "";
2376
2537
  if (opts.seo) {
2377
2538
  const s = opts.seo;
@@ -2452,11 +2613,13 @@ export {
2452
2613
  BuildPluginBuilder,
2453
2614
  computeInlineThemeCss,
2454
2615
  buildClientBundle,
2616
+ getGitLastModifiedBatch,
2617
+ frontmatterField,
2618
+ compileMdx,
2619
+ compileMdxModule,
2455
2620
  generateSearchIndex,
2456
2621
  initSentry,
2457
2622
  captureException,
2458
- getGitLastModifiedBatch,
2459
- compileMdx,
2460
2623
  DocsPage,
2461
2624
  NotFoundPage,
2462
2625
  IndexPage,