@docubook/flame 2.0.0-alpha.2 → 2.0.0-beta.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.
@@ -8,9 +8,10 @@ interface ScrollToProps {
8
8
  className?: string;
9
9
  showIcon?: boolean;
10
10
  offset?: number;
11
+ onScrollToTop?: () => void;
11
12
  }
12
13
 
13
- export function ScrollTo({ className, showIcon = true }: ScrollToProps) {
14
+ export function ScrollTo({ className, showIcon = true, onScrollToTop }: ScrollToProps) {
14
15
  const [isVisible, setIsVisible] = useState(false);
15
16
 
16
17
  const checkScroll = useCallback(() => {
@@ -41,15 +42,20 @@ export function ScrollTo({ className, showIcon = true }: ScrollToProps) {
41
42
  };
42
43
  }, [checkScroll]);
43
44
 
44
- const scrollToTop = useCallback((e: React.MouseEvent) => {
45
- e.preventDefault();
46
- const container = document.getElementById("scroll-container");
47
- if (container) {
48
- container.scrollTo({ top: 0, behavior: "smooth" });
49
- } else {
50
- window.scrollTo({ top: 0, behavior: "smooth" });
51
- }
52
- }, []);
45
+ const scrollToTop = useCallback(
46
+ (e: React.MouseEvent) => {
47
+ e.preventDefault();
48
+ onScrollToTop?.();
49
+ history.replaceState(null, "", "#top");
50
+ const container = document.getElementById("scroll-container");
51
+ if (container) {
52
+ container.scrollTo({ top: 0, behavior: "smooth" });
53
+ } else {
54
+ window.scrollTo({ top: 0, behavior: "smooth" });
55
+ }
56
+ },
57
+ [onScrollToTop]
58
+ );
53
59
 
54
60
  return (
55
61
  <div
@@ -61,7 +67,7 @@ export function ScrollTo({ className, showIcon = true }: ScrollToProps) {
61
67
  )}
62
68
  >
63
69
  <a
64
- href="#"
70
+ href="#top"
65
71
  onClick={scrollToTop}
66
72
  className={cn(
67
73
  "inline-flex items-center text-sm",
@@ -62,9 +62,9 @@ export default function Toc({ tocs }: TocProps) {
62
62
  }
63
63
  }
64
64
 
65
- if (currentId && currentId !== activeIdRef.current) {
65
+ if (currentId !== activeIdRef.current) {
66
66
  setActiveId(currentId);
67
- history.replaceState(null, "", `#${currentId}`);
67
+ history.replaceState(null, "", currentId ? `#${currentId}` : "#top");
68
68
  }
69
69
  };
70
70
 
@@ -100,22 +100,43 @@ export default function Toc({ tocs }: TocProps) {
100
100
  }, 1000);
101
101
  }, []);
102
102
 
103
+ const handleScrollToTop = useCallback(() => {
104
+ clickedIdRef.current = "__top__";
105
+ setActiveId(null);
106
+ history.replaceState(null, "", "#top");
107
+ if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
108
+ clickTimerRef.current = setTimeout(() => {
109
+ clickedIdRef.current = null;
110
+ }, 1000);
111
+ }, []);
112
+
103
113
  useEffect(() => {
104
114
  return () => {
105
115
  if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
106
116
  };
107
117
  }, []);
108
118
 
119
+ const activeItemRef = useRef<HTMLDivElement | null>(null);
120
+
121
+ useEffect(() => {
122
+ if (activeId && activeItemRef.current && !clickedIdRef.current) {
123
+ activeItemRef.current.scrollIntoView({
124
+ block: "nearest",
125
+ behavior: "smooth",
126
+ });
127
+ }
128
+ }, [activeId]);
129
+
109
130
  if (!tocs.length) return null;
110
131
 
111
132
  return (
112
- <div className="flex w-full flex-col gap-2">
113
- <div className="flex items-center gap-2">
133
+ <div className="flex h-full min-h-0 w-full flex-col gap-2">
134
+ <div className="flex shrink-0 items-center gap-2">
114
135
  <ListIcon className="h-4 w-4" />
115
136
  <h3 className="text-sm font-medium">On this page</h3>
116
137
  </div>
117
138
 
118
- <div className="relative">
139
+ <div className="relative min-h-0 flex-1 overflow-y-auto overscroll-contain pr-1">
119
140
  <div className="relative text-sm">
120
141
  <div className="bg-base-300 absolute top-0 left-0 h-full w-px" />
121
142
 
@@ -128,6 +149,7 @@ export default function Toc({ tocs }: TocProps) {
128
149
  return (
129
150
  <div
130
151
  key={href}
152
+ ref={isActive ? activeItemRef : undefined}
131
153
  className={cn(
132
154
  "relative flex items-center transition-all duration-200",
133
155
  isActive && "bg-primary/5"
@@ -206,9 +228,12 @@ export default function Toc({ tocs }: TocProps) {
206
228
  })}
207
229
  </div>
208
230
  </div>
209
- </div>
210
231
 
211
- <ScrollTo className="mt-2" />
232
+ <div className="mt-2 grid grid-cols-[28px_minmax(0,1fr)] items-start px-4 text-sm">
233
+ <div aria-hidden="true" />
234
+ <ScrollTo className="mt-0" onScrollToTop={handleScrollToTop} />
235
+ </div>
236
+ </div>
212
237
  </div>
213
238
  );
214
239
  }
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  runBuildCli
3
- } from "./chunk-3LRUTZZD.js";
4
- import "./chunk-42JQLAKP.js";
3
+ } from "./chunk-FERG25YW.js";
4
+ import "./chunk-URBATJEC.js";
5
5
  import "./chunk-EOK6KATZ.js";
6
- import "./chunk-IN2QAGDZ.js";
6
+ import "./chunk-6EOUQCRM.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-3LRUTZZD.js";
5
- import "./chunk-42JQLAKP.js";
4
+ } from "./chunk-FERG25YW.js";
5
+ import "./chunk-URBATJEC.js";
6
6
  import "./chunk-EOK6KATZ.js";
7
- import "./chunk-IN2QAGDZ.js";
7
+ import "./chunk-6EOUQCRM.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-3LRUTZZD.js";
4
- import "./chunk-42JQLAKP.js";
3
+ } from "./chunk-FERG25YW.js";
4
+ import "./chunk-URBATJEC.js";
5
5
  import "./chunk-EOK6KATZ.js";
6
- import "./chunk-IN2QAGDZ.js";
6
+ import "./chunk-6EOUQCRM.js";
7
7
  import "./chunk-4IQXHHPF.js";
8
8
 
9
9
  // .docu/node/build.node.ts
@@ -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.2",
8
+ version: "2.0.0-beta.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: {
@@ -239,9 +239,11 @@ ${c.bold}${c.cyan} \u{1F525} DocuBook Flame${c.reset} ${c.dim}v${package_defaul
239
239
  if (guard("info", "index_start")) return;
240
240
  this.spinner.start("Generating search index...");
241
241
  },
242
- indexDone(records, ms) {
243
- if (guard("info", "index_done", { records, duration_ms: ms })) return;
244
- this.spinner.stop(`Search index generated ${c.dim}(${records} records, ${ms}ms)${c.reset}`);
242
+ indexDone(records, ms, skipped = false) {
243
+ if (guard("info", "index_done", { records, duration_ms: ms, skipped })) return;
244
+ this.spinner.stop(
245
+ skipped ? `Search index cached ${c.dim}(${ms}ms)${c.reset}` : `Search index generated ${c.dim}(${records} records, ${ms}ms)${c.reset}`
246
+ );
245
247
  },
246
248
  routes() {
247
249
  if (guard("debug", "routes")) return;
@@ -15,7 +15,7 @@ import {
15
15
  htmlShell,
16
16
  initSentry,
17
17
  loadPlugins
18
- } from "./chunk-42JQLAKP.js";
18
+ } from "./chunk-URBATJEC.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-IN2QAGDZ.js";
31
+ } from "./chunk-6EOUQCRM.js";
32
32
  import {
33
33
  DIST_DIR,
34
34
  DOCS_DIR,
@@ -12,10 +12,14 @@ import {
12
12
  frontmatterField,
13
13
  generateSearchIndex,
14
14
  getGitLastModifiedBatch,
15
+ getPageContent,
16
+ getPageFrontmatter,
17
+ getPageStripped,
15
18
  htmlShell,
16
19
  initSentry,
17
- loadPlugins
18
- } from "./chunk-42JQLAKP.js";
20
+ loadPlugins,
21
+ registerPageContent
22
+ } from "./chunk-URBATJEC.js";
19
23
  import {
20
24
  cspHeader,
21
25
  generateNonce,
@@ -23,7 +27,7 @@ import {
23
27
  } from "./chunk-EOK6KATZ.js";
24
28
  import {
25
29
  logger
26
- } from "./chunk-IN2QAGDZ.js";
30
+ } from "./chunk-6EOUQCRM.js";
27
31
  import {
28
32
  ASSETS_DIR,
29
33
  CACHE_FILE,
@@ -109,7 +113,18 @@ async function renderDocsPage(docuConfig, slug, rawMdx, filePath, gitDates, buil
109
113
  try {
110
114
  const remarkPlugins = builder?.collectRemarkPlugins();
111
115
  const rehypePlugins = builder?.collectRehypePlugins();
112
- result = await compileMdx(content, filePath, gitDates, remarkPlugins, rehypePlugins);
116
+ const preFm = getPageFrontmatter(`/${slug}`);
117
+ const preStripped = getPageStripped(`/${slug}`);
118
+ const pre = preFm !== void 0 && preStripped !== void 0 ? { frontmatter: preFm, strippedContent: preStripped } : void 0;
119
+ result = await compileMdx(
120
+ content,
121
+ filePath,
122
+ gitDates,
123
+ remarkPlugins,
124
+ rehypePlugins,
125
+ void 0,
126
+ pre
127
+ );
113
128
  } catch (err) {
114
129
  const msg = err instanceof Error ? err.message : "Unknown MDX error";
115
130
  throw new Error(`MDX Error in: docs/${slug}.mdx
@@ -222,6 +237,7 @@ async function runBuild() {
222
237
  } catch {
223
238
  return;
224
239
  }
240
+ registerPageContent(`/${file.path}`, raw);
225
241
  let content = raw;
226
242
  if (builder) {
227
243
  const relPath = file.absPath.replace(PROJECT_ROOT + "/", "");
@@ -230,7 +246,12 @@ async function runBuild() {
230
246
  }
231
247
  const remarkPlugins = builder?.collectRemarkPlugins();
232
248
  const rehypePlugins = builder?.collectRehypePlugins();
233
- mdxSources[file.path] = await compileMdxModule(content, remarkPlugins, rehypePlugins);
249
+ mdxSources[file.path] = await compileMdxModule(
250
+ content,
251
+ remarkPlugins,
252
+ rehypePlugins,
253
+ `/${file.path}`
254
+ );
234
255
  });
235
256
  await Promise.all(prePassTasks);
236
257
  const indexMdxPath = join(DOCS_DIR, "index.mdx");
@@ -285,12 +306,14 @@ async function runBuild() {
285
306
  continue;
286
307
  }
287
308
  }
288
- let rawMdx;
289
- try {
290
- rawMdx = await readFile(file.absPath, "utf-8");
291
- } catch (err) {
292
- if (err.code !== "ENOENT") throw err;
293
- continue;
309
+ let rawMdx = getPageContent(`/${file.path}`);
310
+ if (rawMdx === void 0) {
311
+ try {
312
+ rawMdx = await readFile(file.absPath, "utf-8");
313
+ } catch (err) {
314
+ if (err.code !== "ENOENT") throw err;
315
+ continue;
316
+ }
294
317
  }
295
318
  if (rebuildDecision === "hash_check") {
296
319
  const contentHash = hashContent(rawMdx);
@@ -418,8 +441,9 @@ async function runBuild() {
418
441
  }
419
442
  logger.indexStart();
420
443
  t = performance.now();
421
- const indexCount = await generateSearchIndex();
422
- logger.indexDone(indexCount, Math.round(performance.now() - t));
444
+ const indexSkipped = built === 0 && existsSync(join(ASSETS_DIR, "search-index.json"));
445
+ const indexCount = indexSkipped ? 0 : await generateSearchIndex();
446
+ logger.indexDone(indexCount, Math.round(performance.now() - t), indexSkipped);
423
447
  logger.routes();
424
448
  await writeCache(cache);
425
449
  if (errors.length > 0) {
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-EOK6KATZ.js";
8
8
  import {
9
9
  logger
10
- } from "./chunk-IN2QAGDZ.js";
10
+ } from "./chunk-6EOUQCRM.js";
11
11
  import {
12
12
  DIST_DIR
13
13
  } from "./chunk-4IQXHHPF.js";
@@ -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-AZBZN66Q.js");
140
+ const { runBuildCli } = await import("./build.impl-3ZOQORN3.js");
141
141
  await runBuildCli();
142
142
  }
143
143
  async function writeDockerFiles() {