@docubook/flame 1.4.4 → 1.5.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 (53) hide show
  1. package/.docu/lib/build.deno.js +11 -0
  2. package/.docu/lib/build.impl-7KJ4ZTAJ.js +12 -0
  3. package/.docu/lib/build.node.js +10 -0
  4. package/.docu/lib/chunk-7ZEUL6PR.js +383 -0
  5. package/.docu/lib/chunk-AI7QAMMZ.js +2410 -0
  6. package/.docu/lib/chunk-E4OIJWCU.js +368 -0
  7. package/.docu/lib/chunk-IR5TVJOV.js +79 -0
  8. package/.docu/lib/chunk-J5NMYSBJ.js +59 -0
  9. package/.docu/lib/chunk-PTRZ2S2C.js +298 -0
  10. package/.docu/lib/chunk-RE4NGTMT.js +185 -0
  11. package/.docu/lib/chunk-TE52TIEW.js +92 -0
  12. package/.docu/lib/clean.js +32 -0
  13. package/.docu/lib/deploy.deno.js +13 -0
  14. package/.docu/lib/deploy.node.js +10 -0
  15. package/.docu/lib/preview.deno.js +10 -0
  16. package/.docu/lib/preview.node.js +10 -0
  17. package/.docu/lib/server.deno.js +11 -0
  18. package/.docu/lib/server.node.js +11 -0
  19. package/.docu/node/build.deno.ts +7 -0
  20. package/.docu/node/build.impl.ts +416 -0
  21. package/.docu/node/build.node.ts +3 -0
  22. package/.docu/node/deploy.deno.ts +11 -0
  23. package/.docu/node/deploy.node.ts +6 -0
  24. package/.docu/node/deploy.shared.ts +85 -0
  25. package/.docu/node/deploy.ts +11 -0
  26. package/.docu/node/escapeHtml.ts +18 -0
  27. package/.docu/node/git.ts +79 -0
  28. package/.docu/node/html.shared.ts +110 -0
  29. package/.docu/node/hydrate.node.ts +276 -0
  30. package/.docu/node/hydrate.ts +17 -31
  31. package/.docu/node/mdx.ts +1 -1
  32. package/.docu/node/paths.ts +24 -0
  33. package/.docu/node/plugin-builder.ts +6 -2
  34. package/.docu/node/plugin.ts +11 -2
  35. package/.docu/node/preview.deno.ts +4 -0
  36. package/.docu/node/preview.impl.ts +96 -0
  37. package/.docu/node/preview.node.ts +4 -0
  38. package/.docu/node/security.ts +5 -0
  39. package/.docu/node/server-routes.ts +4 -4
  40. package/.docu/node/server.deno.ts +4 -0
  41. package/.docu/node/server.impl.ts +184 -0
  42. package/.docu/node/server.node.ts +4 -0
  43. package/.docu/styles/globals.css +20 -5
  44. package/README.md +57 -506
  45. package/bin/cli.js +99 -14
  46. package/bin/compile-lib.mjs +67 -0
  47. package/package.json +9 -7
  48. package/template/docs/getting-started/configuration.mdx +18 -0
  49. package/template/docs/getting-started/overview.mdx +50 -0
  50. package/template/docs/guide/deployment.mdx +27 -0
  51. package/template/docs/guide/routing.mdx +25 -0
  52. package/template/docs/index.mdx +8 -205
  53. package/template/docu.json +32 -1
@@ -0,0 +1,2410 @@
1
+ import {
2
+ cn,
3
+ docsHtmlHref,
4
+ formatDate2,
5
+ isExternalUrl,
6
+ normalizeImporterPath,
7
+ scanMdxFiles
8
+ } from "./chunk-RE4NGTMT.js";
9
+ import {
10
+ ASSETS_DIR,
11
+ DOCS_DIR,
12
+ FRAMEWORK_ROOT,
13
+ LIB_DIR,
14
+ PROJECT_ROOT,
15
+ STYLES_DIR,
16
+ cleanOldBundles,
17
+ loadDocuConfig
18
+ } from "./chunk-J5NMYSBJ.js";
19
+
20
+ // .docu/node/plugin-loader.ts
21
+ import { resolve } from "node:path";
22
+ var NPM_PACKAGE_RE = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
23
+ function resolveSpecifier(specifier) {
24
+ let resolved;
25
+ if (specifier.startsWith(".")) {
26
+ resolved = resolve(PROJECT_ROOT, specifier);
27
+ } else if (specifier.startsWith("/")) {
28
+ resolved = specifier;
29
+ } else {
30
+ if (!NPM_PACKAGE_RE.test(specifier)) {
31
+ throw new Error(
32
+ `[plugin-loader] Invalid plugin specifier "${specifier}": must be a valid npm package name, relative path, or absolute path`
33
+ );
34
+ }
35
+ return specifier;
36
+ }
37
+ const root = PROJECT_ROOT.endsWith("/") ? PROJECT_ROOT : PROJECT_ROOT + "/";
38
+ if (!resolved.startsWith(root)) {
39
+ throw new Error(
40
+ `[plugin-loader] Path traversal blocked: "${specifier}" resolves outside project root`
41
+ );
42
+ }
43
+ return resolved;
44
+ }
45
+ async function loadPlugins(entries = []) {
46
+ const plugins = [];
47
+ for (const entry of entries) {
48
+ const [specifier, options] = Array.isArray(entry) ? entry : [entry, void 0];
49
+ const resolved = resolveSpecifier(specifier);
50
+ let mod;
51
+ try {
52
+ mod = await import(resolved);
53
+ } catch (err) {
54
+ const message = err instanceof Error ? err.message : String(err);
55
+ throw new Error(`[plugin-loader] Failed to import plugin "${specifier}": ${message}`, {
56
+ cause: err
57
+ });
58
+ }
59
+ const exported = mod.default;
60
+ let plugin;
61
+ if (typeof exported === "function") {
62
+ try {
63
+ plugin = exported(options);
64
+ } catch (err) {
65
+ const message = err instanceof Error ? err.message : String(err);
66
+ throw new Error(
67
+ `[plugin-loader] Plugin factory "${specifier}" threw during initialization: ${message}`,
68
+ { cause: err }
69
+ );
70
+ }
71
+ } else if (exported && typeof exported === "object") {
72
+ plugin = exported;
73
+ } else {
74
+ throw new Error(
75
+ `[plugin-loader] Plugin "${specifier}" must export a default function or object. Got: ${typeof exported}`
76
+ );
77
+ }
78
+ if (!plugin.name || typeof plugin.name !== "string") {
79
+ throw new Error(
80
+ `[plugin-loader] Plugin "${specifier}" must have a valid 'name' property (string). Got: ${typeof plugin.name}`
81
+ );
82
+ }
83
+ if (typeof plugin.setup !== "function") {
84
+ throw new Error(
85
+ `[plugin-loader] Plugin "${specifier}" (name: "${plugin.name}") must have a 'setup(build)' function.`
86
+ );
87
+ }
88
+ plugins.push(plugin);
89
+ }
90
+ return plugins;
91
+ }
92
+
93
+ // .docu/node/plugin-builder.ts
94
+ var BuildPluginBuilder = class {
95
+ config;
96
+ _handleRequest = [];
97
+ _injectBody = [];
98
+ _injectHead = [];
99
+ _onEnd = [];
100
+ _onLoad = [];
101
+ _onStart = [];
102
+ _rehypePlugins = [];
103
+ _remarkPlugins = [];
104
+ _transformFrontmatter = [];
105
+ _transformHtml = [];
106
+ constructor(config3) {
107
+ this.config = config3;
108
+ }
109
+ /**
110
+ * Collect and deduplicate all `<body>` injection snippets from registered plugins.
111
+ * Each callback is executed in registration order; plugin errors are wrapped
112
+ * with a descriptive message.
113
+ *
114
+ * @param context - Current page context passed to each injectBody callback.
115
+ * @returns Deduplicated array of HTML strings to inject before `</body>`.
116
+ * @throws Error if any injectBody callback throws — wraps original error as cause.
117
+ */
118
+ collectBody(context) {
119
+ const items = [];
120
+ for (const cb of this._injectBody) {
121
+ try {
122
+ const result = cb(context);
123
+ if (result) {
124
+ this.collectItems(items, result, "injectBody");
125
+ }
126
+ } catch (err) {
127
+ throw new Error(
128
+ `[plugin] injectBody callback failed: ${err instanceof Error ? err.message : String(err)}`,
129
+ { cause: err }
130
+ );
131
+ }
132
+ }
133
+ return [...new Set(items)];
134
+ }
135
+ /**
136
+ * Collect and deduplicate all `<head>` injection snippets from registered plugins.
137
+ * Each callback is executed in registration order; plugin errors are wrapped
138
+ * with a descriptive message.
139
+ *
140
+ * @param context - Current page context passed to each injectHead callback.
141
+ * @returns Deduplicated array of HTML strings to inject before `</head>`.
142
+ * @throws Error if any injectHead callback throws — wraps original error as cause.
143
+ */
144
+ collectHead(context) {
145
+ const items = [];
146
+ for (const cb of this._injectHead) {
147
+ try {
148
+ const result = cb(context);
149
+ if (result) {
150
+ this.collectItems(items, result, "injectHead");
151
+ }
152
+ } catch (err) {
153
+ throw new Error(
154
+ `[plugin] injectHead callback failed: ${err instanceof Error ? err.message : String(err)}`,
155
+ { cause: err }
156
+ );
157
+ }
158
+ }
159
+ return [...new Set(items)];
160
+ }
161
+ /**
162
+ * Collect all rehype plugin arrays from registered rehypePlugins callbacks.
163
+ * Results from all plugins are flattened into a single array.
164
+ *
165
+ * @returns Flattened array of rehype plugin instances applied after default set.
166
+ * @throws Error if any rehypePlugins callback throws — wraps original error as cause.
167
+ */
168
+ collectRehypePlugins() {
169
+ const plugins = [];
170
+ for (const cb of this._rehypePlugins) {
171
+ try {
172
+ plugins.push(...cb());
173
+ } catch (err) {
174
+ throw new Error(
175
+ `[plugin] rehypePlugins callback failed: ${err instanceof Error ? err.message : String(err)}`,
176
+ { cause: err }
177
+ );
178
+ }
179
+ }
180
+ return plugins;
181
+ }
182
+ /**
183
+ * Collect all remark plugin arrays from registered remarkPlugins callbacks.
184
+ * Results from all plugins are flattened into a single array.
185
+ *
186
+ * @returns Flattened array of remark plugin instances applied after default set.
187
+ * @throws Error if any remarkPlugins callback throws — wraps original error as cause.
188
+ */
189
+ collectRemarkPlugins() {
190
+ const plugins = [];
191
+ for (const cb of this._remarkPlugins) {
192
+ try {
193
+ plugins.push(...cb());
194
+ } catch (err) {
195
+ throw new Error(
196
+ `[plugin] remarkPlugins callback failed: ${err instanceof Error ? err.message : String(err)}`,
197
+ { cause: err }
198
+ );
199
+ }
200
+ }
201
+ return plugins;
202
+ }
203
+ /**
204
+ * Register a callback to intercept incoming requests during development.
205
+ * The **first** callback to return a `Response` short-circuits all subsequent handlers.
206
+ * Errors inside callbacks are caught and logged — execution continues to next handler.
207
+ *
208
+ * @param callback - Receives the Request and dev server context. Return Response or void.
209
+ *
210
+ * @example
211
+ * build.handleRequest((req, ctx) => {
212
+ * if (new URL(req.url).pathname === "/api/status") {
213
+ * return new Response(JSON.stringify({ ok: true }), {
214
+ * headers: { "Content-Type": "application/json" },
215
+ * });
216
+ * }
217
+ * });
218
+ */
219
+ handleRequest(callback) {
220
+ this._handleRequest.push(callback);
221
+ }
222
+ /**
223
+ * Register a callback that returns HTML strings to inject before `</body>`.
224
+ * Results from all plugins are merged, deduplicated, and served via `collectBody()`.
225
+ *
226
+ * @param callback - Returns a single HTML string or an array. Called once per page.
227
+ *
228
+ * @example
229
+ * build.injectBody(() => `<div id="chat-widget"></div>`);
230
+ */
231
+ injectBody(callback) {
232
+ this._injectBody.push(callback);
233
+ }
234
+ /**
235
+ * Register a callback that returns HTML strings to inject inside `<head>`.
236
+ * Results from all plugins are merged, deduplicated, and served via `collectHead()`.
237
+ *
238
+ * @param callback - Returns a single HTML string or an array. Called once per page.
239
+ *
240
+ * @example
241
+ * build.injectHead(() => `<script async src="https://cdn.example.com/analytics.js"></script>`);
242
+ */
243
+ injectHead(callback) {
244
+ this._injectHead.push(callback);
245
+ }
246
+ /**
247
+ * Register a callback to run once after all pages are built.
248
+ * Receives the resolved config and aggregated page metadata.
249
+ * Errors thrown by the callback propagate to the caller via `runOnEnd()`.
250
+ *
251
+ * @param callback - Receives config and page metadata array. May return a Promise.
252
+ *
253
+ * @example
254
+ * build.onEnd(async (config, pages) => {
255
+ * const xml = generateSitemap(pages, config.meta.baseURL);
256
+ * const out = ".docu/dist/sitemap.xml";
257
+ * // Bun.write on Bun for speed, writeFile on Node/Deno
258
+ * await (typeof Bun !== "undefined"
259
+ * ? Bun.write(out, xml)
260
+ * : writeFile(out, xml));
261
+ * });
262
+ */
263
+ onEnd(callback) {
264
+ this._onEnd.push(callback);
265
+ }
266
+ /**
267
+ * Register a callback to transform raw file content before MDX compilation.
268
+ * Filtered by regex against the file's relative path — only the **first** matching
269
+ * handler's result is used.
270
+ * Errors thrown by the callback propagate to the caller via `runOnLoad()`.
271
+ *
272
+ * @param args.filter - RegExp matched against the file's relative path.
273
+ * @param args.namespace - Optional namespace prefix (reserved for future use).
274
+ * @param callback - Receives file path and raw content. Return new contents or void.
275
+ *
276
+ * @example
277
+ * build.onLoad({ filter: /\.md$/ }, ({ path, content }) => {
278
+ * return { contents: `<!-- preprocessed -->\n${content}`, loader: "mdx" };
279
+ * });
280
+ */
281
+ onLoad(args, callback) {
282
+ this._onLoad.push({ ...args, fn: callback });
283
+ }
284
+ /**
285
+ * Register a callback to run once before the build starts.
286
+ * Receives the resolved DocuConfig for validation or resource initialization.
287
+ * Errors thrown by the callback propagate to the caller via `runOnStart()`.
288
+ *
289
+ * @param callback - Receives the resolved config. May return a Promise.
290
+ *
291
+ * @example
292
+ * build.onStart((config) => {
293
+ * if (!config.meta.baseURL) throw new Error("baseURL required");
294
+ * });
295
+ */
296
+ onStart(callback) {
297
+ this._onStart.push(callback);
298
+ }
299
+ /**
300
+ * Register additional rehype (HTML) plugins for the MDX compilation pipeline.
301
+ * Results from all plugins are merged and applied **after** the default set.
302
+ *
303
+ * @param callback - Returns an array of rehype plugins.
304
+ *
305
+ * @example
306
+ * build.rehypePlugins(() => [require("rehype-autolink-headings")]);
307
+ */
308
+ rehypePlugins(callback) {
309
+ this._rehypePlugins.push(callback);
310
+ }
311
+ /**
312
+ * Register additional remark (Markdown) plugins for the MDX compilation pipeline.
313
+ * Results from all plugins are merged and applied **after** the default set.
314
+ *
315
+ * @param callback - Returns an array of remark plugins.
316
+ *
317
+ * @example
318
+ * build.remarkPlugins(() => [require("remark-custom-heading-id")]);
319
+ */
320
+ remarkPlugins(callback) {
321
+ this._remarkPlugins.push(callback);
322
+ }
323
+ /**
324
+ * Execute all registered handleRequest callbacks sequentially.
325
+ * Stops and returns the **first** `Response` returned by any callback.
326
+ * Errors inside individual callbacks are caught and logged — execution
327
+ * continues to the next callback without throwing.
328
+ *
329
+ * @param req - The incoming HTTP Request.
330
+ * @param context - Dev server context (port, hostname).
331
+ * @returns A Response if a callback intercepted the request, or null if none did.
332
+ */
333
+ async runHandleRequest(req, context) {
334
+ for (let i = 0; i < this._handleRequest.length; i++) {
335
+ try {
336
+ const result = await this._handleRequest[i](req, context);
337
+ if (result instanceof Response) {
338
+ return result;
339
+ }
340
+ } catch (err) {
341
+ console.error(
342
+ `[plugin] handleRequest callback #${i + 1} error: ${err instanceof Error ? err.message : String(err)}`
343
+ );
344
+ }
345
+ }
346
+ return null;
347
+ }
348
+ /**
349
+ * Execute all registered onEnd callbacks sequentially with the resolved
350
+ * config and aggregated page metadata.
351
+ * Errors inside individual callbacks are caught and logged — execution
352
+ * continues to the next callback without throwing.
353
+ *
354
+ * @param pages - Array of metadata for every built page.
355
+ */
356
+ async runOnEnd(pages) {
357
+ for (let i = 0; i < this._onEnd.length; i++) {
358
+ try {
359
+ await this._onEnd[i](this.config, pages);
360
+ } catch (err) {
361
+ console.error(
362
+ `[plugin] onEnd callback #${i + 1} error: ${err instanceof Error ? err.message : String(err)}`
363
+ );
364
+ }
365
+ }
366
+ }
367
+ /**
368
+ * Execute registered onLoad handlers in registration order against a file.
369
+ * Only the **first** handler whose `filter` regex matches the path and returns
370
+ * a result is applied. If a matching handler throws, the error is logged and
371
+ * subsequent handlers are tried.
372
+ *
373
+ * @param path - Relative path of the file being loaded.
374
+ * @param content - Raw file content.
375
+ * @returns Transformed content if a matching handler returned it, or null.
376
+ */
377
+ async runOnLoad(path, content) {
378
+ for (const handler of this._onLoad) {
379
+ if (handler.filter.test(path)) {
380
+ try {
381
+ const result = await handler.fn({ path, content });
382
+ if (result) return result;
383
+ } catch (err) {
384
+ console.error(
385
+ `[plugin] onLoad handler for filter ${handler.filter} error: ${err instanceof Error ? err.message : String(err)}`
386
+ );
387
+ }
388
+ }
389
+ }
390
+ return null;
391
+ }
392
+ /**
393
+ * Execute all registered onStart callbacks sequentially.
394
+ * Each callback receives the resolved DocuConfig.
395
+ * Errors inside individual callbacks are caught and logged — execution
396
+ * continues to the next callback without throwing.
397
+ */
398
+ async runOnStart() {
399
+ for (let i = 0; i < this._onStart.length; i++) {
400
+ try {
401
+ await this._onStart[i](this.config);
402
+ } catch (err) {
403
+ console.error(
404
+ `[plugin] onStart callback #${i + 1} error: ${err instanceof Error ? err.message : String(err)}`
405
+ );
406
+ }
407
+ }
408
+ }
409
+ /**
410
+ * Execute the transformFrontmatter chain in waterfall pattern.
411
+ * Each callback receives the **previous** callback's return value (or the
412
+ * original frontmatter for the first). Callbacks that return `undefined` or
413
+ * `null` pass the current value through unchanged.
414
+ * Callbacks that return a non-object (string, number, array) are skipped
415
+ * with a console warning — only plain objects are accepted.
416
+ * Errors inside individual callbacks are caught and logged — the current
417
+ * frontmatter passes through unchanged for that step.
418
+ *
419
+ * @param frontmatter - Initial frontmatter object parsed from MDX.
420
+ * @param context - Page context with slug, filePath, and raw content.
421
+ * @returns The final transformed frontmatter object.
422
+ */
423
+ async runTransformFrontmatterChain(frontmatter, context) {
424
+ let result = frontmatter;
425
+ for (let i = 0; i < this._transformFrontmatter.length; i++) {
426
+ try {
427
+ const next = await this._transformFrontmatter[i](result, context);
428
+ if (next !== void 0 && next !== null) {
429
+ if (typeof next === "object" && !Array.isArray(next)) {
430
+ result = next;
431
+ } else {
432
+ console.warn(
433
+ `[plugin] transformFrontmatter callback #${i + 1} returned invalid type (expected a plain object), skipping`
434
+ );
435
+ }
436
+ }
437
+ } catch (err) {
438
+ console.error(
439
+ `[plugin] transformFrontmatter callback #${i + 1} error: ${err instanceof Error ? err.message : String(err)}`
440
+ );
441
+ }
442
+ }
443
+ return result;
444
+ }
445
+ /**
446
+ * Execute the transformHtml chain in pipeline pattern.
447
+ * Each callback receives the **previous** callback's return value (or the
448
+ * original HTML for the first). Every callback **must** return a string.
449
+ * Errors inside individual callbacks are caught and logged — the current
450
+ * HTML passes through unchanged for that step.
451
+ *
452
+ * @param html - The initial HTML string.
453
+ * @param context - Full page context (slug, filePath, frontmatter, content, config).
454
+ * @returns The final transformed HTML string.
455
+ */
456
+ async runTransformHtmlChain(html, context) {
457
+ let result = html;
458
+ for (let i = 0; i < this._transformHtml.length; i++) {
459
+ try {
460
+ result = await this._transformHtml[i](result, context);
461
+ } catch (err) {
462
+ console.error(
463
+ `[plugin] transformHtml callback #${i + 1} error: ${err instanceof Error ? err.message : String(err)}`
464
+ );
465
+ }
466
+ }
467
+ return result;
468
+ }
469
+ /**
470
+ * Register a callback to mutate frontmatter before MDX compilation.
471
+ * Callbacks are chained in a waterfall: the return value of one is passed
472
+ * as input to the next. Return `undefined` to pass through unchanged.
473
+ *
474
+ * **Note:** Only plain objects are accepted as return values. Returning
475
+ * a string, number, or array will be silently skipped with a warning.
476
+ * Plugin authors should validate their return values before returning.
477
+ *
478
+ * @param callback - Receives frontmatter object and page context.
479
+ *
480
+ * @example
481
+ * build.transformFrontmatter((fm, ctx) => {
482
+ * const wordCount = ctx.content!.split(/\s+/).length;
483
+ * return { ...fm, readingTime: `${Math.ceil(wordCount / 200)} min read` };
484
+ * });
485
+ */
486
+ transformFrontmatter(callback) {
487
+ this._transformFrontmatter.push(callback);
488
+ }
489
+ /**
490
+ * Register a callback to transform the final HTML string per page.
491
+ * This is the **last** hook before the HTML is written to disk.
492
+ * Callbacks are chained in a pipeline: each receives the previous callback's output.
493
+ *
494
+ * @param callback - Receives HTML string and full page context. Must return HTML.
495
+ *
496
+ * @example
497
+ * build.transformHtml((html, ctx) => {
498
+ * return html.replace(/https?:\/\/old-domain\.com\//g, "/");
499
+ * });
500
+ */
501
+ transformHtml(callback) {
502
+ this._transformHtml.push(callback);
503
+ }
504
+ /**
505
+ * Collect items from a callback result, filtering only valid strings.
506
+ * Non-string items and unexpected types are logged as warnings.
507
+ */
508
+ collectItems(items, result, hookName) {
509
+ if (Array.isArray(result)) {
510
+ for (const item of result) {
511
+ if (typeof item === "string") {
512
+ items.push(item);
513
+ } else {
514
+ console.warn(
515
+ `[plugin] ${hookName} callback returned non-string item (got ${typeof item}), skipping`
516
+ );
517
+ }
518
+ }
519
+ } else if (typeof result === "string") {
520
+ items.push(result);
521
+ } else {
522
+ console.warn(
523
+ `[plugin] ${hookName} callback returned unexpected type (got ${typeof result}), expected string or string[], skipping`
524
+ );
525
+ }
526
+ }
527
+ };
528
+
529
+ // .docu/node/hydrate.ts
530
+ import { resolveTheme, generateThemeCss, presetRegistry } from "@docubook/themes-colors";
531
+
532
+ // .docu/node/fs-scanner.ts
533
+ import { readdirSync, statSync } from "node:fs";
534
+ import { join, relative, extname, sep } from "node:path";
535
+ function toTitleCase(str) {
536
+ return str.replace(/-/g, " ").replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
537
+ }
538
+ function isDocFile(filename) {
539
+ const ext = extname(filename).toLowerCase();
540
+ return [".mdx", ".md"].includes(ext);
541
+ }
542
+ function normalizePath(path) {
543
+ return path.split(sep).join("/");
544
+ }
545
+ function scanDir(dirPath, docsRoot) {
546
+ const nodes = [];
547
+ let entries;
548
+ try {
549
+ entries = readdirSync(dirPath).sort();
550
+ } catch (err) {
551
+ if (err.code !== "ENOENT") throw err;
552
+ return nodes;
553
+ }
554
+ for (const entry of entries) {
555
+ if (entry.startsWith(".") || entry === "assets") continue;
556
+ const absPath = join(dirPath, entry);
557
+ try {
558
+ const stat = statSync(absPath);
559
+ if (stat.isDirectory()) {
560
+ const children = scanDir(absPath, docsRoot);
561
+ if (children.length > 0) {
562
+ nodes.push({
563
+ name: entry,
564
+ relPath: normalizePath(relative(docsRoot, absPath)),
565
+ absPath,
566
+ isDirectory: true,
567
+ children
568
+ });
569
+ }
570
+ } else if (stat.isFile() && isDocFile(entry)) {
571
+ nodes.push({
572
+ name: entry,
573
+ relPath: normalizePath(relative(docsRoot, absPath).replace(/\.(mdx|md)$/, "")),
574
+ absPath,
575
+ isDirectory: false
576
+ });
577
+ }
578
+ } catch (err) {
579
+ if (err.code !== "ENOENT") throw err;
580
+ continue;
581
+ }
582
+ }
583
+ return nodes;
584
+ }
585
+ function fileNodesToRoutes(nodes, parentHref = "") {
586
+ const routes3 = [];
587
+ for (const node of nodes) {
588
+ if (!node.isDirectory) {
589
+ const baseName = node.name.replace(/\.(mdx|md)$/, "");
590
+ const isIndexFile = /^(index|readme)$/i.test(baseName);
591
+ if (isIndexFile) continue;
592
+ const segment = node.relPath.split("/").pop();
593
+ const href = `/${segment}`;
594
+ routes3.push({
595
+ title: toTitleCase(baseName),
596
+ href
597
+ });
598
+ } else {
599
+ const dirTitle = toTitleCase(node.name);
600
+ const segment = node.relPath.split("/").pop();
601
+ const dirHref = `/${segment}`;
602
+ const children = fileNodesToRoutes(node.children || [], dirHref);
603
+ if (children.length === 0) continue;
604
+ const hasIndexFile = (node.children || []).some(
605
+ (c) => !c.isDirectory && /^(index|readme)\.(mdx|md)$/i.test(c.name)
606
+ );
607
+ if (hasIndexFile) {
608
+ routes3.push({
609
+ title: dirTitle,
610
+ href: dirHref,
611
+ ...parentHref === "" && {
612
+ context: { title: dirTitle, icon: "CircleHelp", description: dirTitle }
613
+ },
614
+ items: children
615
+ });
616
+ } else {
617
+ routes3.push({
618
+ title: dirTitle,
619
+ href: dirHref,
620
+ noLink: true,
621
+ ...parentHref === "" && {
622
+ context: { title: dirTitle, icon: "CircleHelp", description: dirTitle }
623
+ },
624
+ items: children
625
+ });
626
+ }
627
+ }
628
+ }
629
+ return routes3;
630
+ }
631
+ function scanDocsFolder(docsPath = "./docs") {
632
+ const absDocsPath = join(process.cwd(), docsPath);
633
+ const nodes = scanDir(absDocsPath, absDocsPath);
634
+ return fileNodesToRoutes(nodes);
635
+ }
636
+ function resolveRoutes(docuJsonRoutes) {
637
+ if (docuJsonRoutes && docuJsonRoutes.length > 0) {
638
+ return docuJsonRoutes;
639
+ }
640
+ return scanDocsFolder();
641
+ }
642
+
643
+ // .docu/node/hydrate.ts
644
+ var themeRegistry = presetRegistry;
645
+ function getThemeConfig() {
646
+ if (process.env.FLAME_THEME) {
647
+ return process.env.FLAME_THEME;
648
+ }
649
+ const config3 = loadDocuConfig();
650
+ return config3.themes?.colors;
651
+ }
652
+ function buildThemeCss(baseCss, themeConfig) {
653
+ try {
654
+ const resolved = resolveTheme(themeConfig, themeRegistry);
655
+ return baseCss + "\n" + generateThemeCss(resolved);
656
+ } catch (err) {
657
+ console.warn(
658
+ `[flame] Failed to resolve theme CSS: ${err instanceof Error ? err.message : String(err)}`
659
+ );
660
+ return baseCss;
661
+ }
662
+ }
663
+ function computeInlineThemeCss() {
664
+ try {
665
+ const themeColors = getThemeConfig();
666
+ if (themeColors) {
667
+ const resolved = resolveTheme(themeColors, themeRegistry);
668
+ return generateThemeCss(resolved);
669
+ }
670
+ } catch (err) {
671
+ console.warn(
672
+ `[flame] Failed to compute inline theme CSS: ${err instanceof Error ? err.message : String(err)}`
673
+ );
674
+ }
675
+ return void 0;
676
+ }
677
+
678
+ // .docu/node/hydrate.node.ts
679
+ import { execFile } from "node:child_process";
680
+ import { builtinModules, createRequire } from "node:module";
681
+ import { basename, dirname, join as join2, resolve as resolve2 } from "node:path";
682
+ import { existsSync, readFileSync, readdirSync as readdirSync2 } from "node:fs";
683
+ import { promisify } from "node:util";
684
+ import { mkdir, readFile, unlink, writeFile } from "node:fs/promises";
685
+ import { createHash } from "node:crypto";
686
+ function extractConfigIcons(config3) {
687
+ const icons = [];
688
+ const pushIf = (s) => {
689
+ if (s) icons.push(s);
690
+ };
691
+ config3.home?.hero?.actions?.forEach((a) => pushIf(a.icon));
692
+ config3.home?.features?.forEach((f) => pushIf(f.icon));
693
+ (function walk(routes3) {
694
+ for (const r of routes3) {
695
+ pushIf(r.context?.icon);
696
+ if (r.items) walk(r.items);
697
+ }
698
+ })(config3.routes ?? []);
699
+ return [...new Set(icons.filter((n) => /^[A-Z]/.test(n)))];
700
+ }
701
+ var execFileAsync = promisify(execFile);
702
+ function resolveTailwindBin() {
703
+ const require2 = createRequire(import.meta.url);
704
+ const pkgPath = require2.resolve("@tailwindcss/cli/package.json");
705
+ const pkg = require2(pkgPath);
706
+ const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin.tailwindcss;
707
+ return join2(dirname(pkgPath), binRel);
708
+ }
709
+ async function runTailwind(outputCss) {
710
+ const bin = resolveTailwindBin();
711
+ const twArgs = ["-i", join2(STYLES_DIR, "globals.css"), "-o", outputCss, "--minify"];
712
+ const isDeno = "Deno" in globalThis;
713
+ const args = isDeno ? ["run", "-A", bin, ...twArgs] : [bin, ...twArgs];
714
+ try {
715
+ await execFileAsync(process.execPath, args, { maxBuffer: 16 * 1024 * 1024 });
716
+ } catch (err) {
717
+ const stderr = err.stderr ?? String(err);
718
+ throw new Error(`Tailwind CSS build failed:
719
+ ${stderr}`, { cause: err });
720
+ }
721
+ }
722
+ var NODE_BUILTINS_RE = new RegExp(
723
+ `^(node:.*|${builtinModules.map((m) => m.replace(/\//g, "\\/")).join("|")})$`
724
+ );
725
+ var lucideRealEntry;
726
+ function getLucideRealEntry() {
727
+ if (!lucideRealEntry) {
728
+ lucideRealEntry = createRequire(import.meta.url).resolve("lucide-react");
729
+ }
730
+ return lucideRealEntry;
731
+ }
732
+ var LUCIDE_IMPORT_RE = /import\s*\{([^}]+)\}\s*from\s*["']lucide-react["']/g;
733
+ var LUCIDE_ICON_RE = /^[A-Z]/;
734
+ function scanDirLucideIcons(dir, set) {
735
+ if (!existsSync(dir)) return;
736
+ try {
737
+ const entries = readdirSync2(dir, { withFileTypes: true });
738
+ for (const e of entries) {
739
+ const full = join2(dir, e.name);
740
+ if (e.isDirectory()) {
741
+ if (e.name !== "node_modules") scanDirLucideIcons(full, set);
742
+ } else if (/\.(js|ts|tsx)$/.test(e.name)) {
743
+ const content = readFileSync(full, "utf-8");
744
+ for (const m of content.matchAll(LUCIDE_IMPORT_RE)) {
745
+ for (const s of m[1].split(",")) {
746
+ const name = s.trim().split(/\s+as\s+/)[0].trim();
747
+ if (LUCIDE_ICON_RE.test(name)) set.add(name);
748
+ }
749
+ }
750
+ }
751
+ }
752
+ } catch {
753
+ }
754
+ }
755
+ function collectAllLucideIcons() {
756
+ const icons = /* @__PURE__ */ new Set();
757
+ scanDirLucideIcons(join2(FRAMEWORK_ROOT, ".docu/components"), icons);
758
+ scanDirLucideIcons(join2(FRAMEWORK_ROOT, ".docu/pages"), icons);
759
+ const depDirs = [
760
+ join2(FRAMEWORK_ROOT, "..", "mdx-content", "dist"),
761
+ join2(FRAMEWORK_ROOT, "..", "ui-react", "dist"),
762
+ join2(FRAMEWORK_ROOT, "..", "core", "dist"),
763
+ join2(FRAMEWORK_ROOT, "..", "runt", "dist"),
764
+ join2(FRAMEWORK_ROOT, "..", "themes-colors", "dist")
765
+ ];
766
+ for (const d of depDirs) scanDirLucideIcons(resolve2(d), icons);
767
+ return [...icons];
768
+ }
769
+ async function buildClientBundle() {
770
+ await mkdir(ASSETS_DIR, { recursive: true });
771
+ await cleanOldBundles();
772
+ const nodeEnv = process.env.NODE_ENV || "development";
773
+ const esbuild = await import("esbuild");
774
+ const { build } = esbuild;
775
+ const entryPath = join2(LIB_DIR, "client.ts");
776
+ const workingDir = process.cwd();
777
+ let result;
778
+ try {
779
+ result = await build({
780
+ entryPoints: [entryPath],
781
+ bundle: true,
782
+ outdir: ASSETS_DIR,
783
+ entryNames: "client-[hash]",
784
+ chunkNames: "chunks/[name]-[hash]",
785
+ platform: "browser",
786
+ format: "esm",
787
+ splitting: true,
788
+ minify: nodeEnv === "production",
789
+ define: { "process.env.NODE_ENV": JSON.stringify(nodeEnv) },
790
+ jsx: "automatic",
791
+ jsxDev: nodeEnv !== "production",
792
+ metafile: true,
793
+ logLevel: "silent",
794
+ plugins: [
795
+ {
796
+ name: "node-builtin-stub",
797
+ setup(build2) {
798
+ build2.onResolve({ filter: NODE_BUILTINS_RE }, (args) => ({
799
+ path: args.path,
800
+ namespace: "node-stub"
801
+ }));
802
+ build2.onLoad({ filter: /.*/, namespace: "node-stub" }, () => ({
803
+ contents: "module.exports = {};",
804
+ loader: "js"
805
+ }));
806
+ }
807
+ },
808
+ {
809
+ name: "lucide-optimize",
810
+ setup(build2) {
811
+ build2.onResolve({ filter: /^lucide-react$/ }, (args) => {
812
+ if (args.namespace === "lucide-virt") {
813
+ return { path: getLucideRealEntry(), namespace: "file" };
814
+ }
815
+ if (args.importer) {
816
+ const normalized = normalizeImporterPath(args.importer);
817
+ if (normalized.endsWith("/.docu/components/Lucide.tsx") || normalized.includes("/mdx-content/dist/")) {
818
+ return { path: getLucideRealEntry(), namespace: "file" };
819
+ }
820
+ }
821
+ return { path: args.path, namespace: "lucide-virt" };
822
+ });
823
+ build2.onLoad({ filter: /.*/, namespace: "lucide-virt" }, () => {
824
+ const scanned = collectAllLucideIcons();
825
+ const configured = extractConfigIcons(loadDocuConfig());
826
+ const allIcons = [.../* @__PURE__ */ new Set([...scanned, ...configured])];
827
+ return {
828
+ contents: `export { ${allIcons.join(", ")} } from "lucide-react";`,
829
+ loader: "js"
830
+ };
831
+ });
832
+ }
833
+ },
834
+ {
835
+ name: "docu-config",
836
+ setup(build2) {
837
+ build2.onResolve({ filter: /docu\.json$/ }, (args) => ({
838
+ path: args.path,
839
+ namespace: "docu-config"
840
+ }));
841
+ build2.onLoad({ filter: /.*/, namespace: "docu-config" }, () => {
842
+ const config3 = loadDocuConfig();
843
+ const resolved = {
844
+ ...config3,
845
+ routes: resolveRoutes(config3.routes)
846
+ };
847
+ return { contents: JSON.stringify(resolved), loader: "json" };
848
+ });
849
+ }
850
+ }
851
+ ]
852
+ });
853
+ } finally {
854
+ await esbuild.stop();
855
+ }
856
+ const { outputs } = result.metafile;
857
+ const jsOutput = Object.keys(outputs).find((p) => {
858
+ const o = outputs[p];
859
+ return o.entryPoint && resolve2(workingDir, o.entryPoint) === entryPath;
860
+ });
861
+ if (!jsOutput) {
862
+ throw new Error("Client bundle produced no output files");
863
+ }
864
+ const jsFile = basename(jsOutput);
865
+ const tmpCss = join2(ASSETS_DIR, "_tmp.css");
866
+ await runTailwind(tmpCss);
867
+ let cssContent = await readFile(tmpCss, "utf-8");
868
+ try {
869
+ const themeColors = getThemeConfig();
870
+ if (themeColors) {
871
+ cssContent = buildThemeCss(cssContent, themeColors);
872
+ }
873
+ } catch (err) {
874
+ console.warn(
875
+ `[flame] Failed to resolve theme config, falling back to globals.css only: ${err instanceof Error ? err.message : String(err)}`
876
+ );
877
+ }
878
+ const cssHash = createHash("md5").update(cssContent).digest("hex").slice(0, 8);
879
+ const cssFile = `client-${cssHash}.css`;
880
+ await writeFile(join2(ASSETS_DIR, cssFile), cssContent);
881
+ await unlink(tmpCss);
882
+ await writeFile(join2(ASSETS_DIR, "manifest.json"), JSON.stringify({ js: jsFile, css: cssFile }));
883
+ return { js: jsFile, css: cssFile };
884
+ }
885
+
886
+ // .docu/node/search-indexer.ts
887
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
888
+ import { resolve as resolve3, join as join3 } from "node:path";
889
+ import { extractFrontmatterWithContent } from "@docubook/core";
890
+ var docuConfig = loadDocuConfig();
891
+ function getSectionTitle(filePath) {
892
+ const parts = filePath.split("/");
893
+ if (parts.length > 1) {
894
+ const section = docuConfig.routes?.find(
895
+ (r) => r.href === `/${parts[0]}` || r.href === parts[0]
896
+ );
897
+ if (section) return section.title;
898
+ }
899
+ return docuConfig.meta?.title || "Docs";
900
+ }
901
+ function slugify(text) {
902
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
903
+ }
904
+ function stripJsx(content) {
905
+ let result = content;
906
+ let prev = "";
907
+ while (result !== prev) {
908
+ prev = result;
909
+ result = result.replace(/<[A-Z][\w.]*[\s\S]*?\/>/g, "").replace(/<[A-Z][\w.]*[\s\S]*?>([\s\S]*?)<\/[A-Z][\w.]*>/g, "$1");
910
+ }
911
+ return result;
912
+ }
913
+ function extractRecords(filePath, raw) {
914
+ const { frontmatter, strippedContent: content } = extractFrontmatterWithContent(raw);
915
+ const records = [];
916
+ const url = docsHtmlHref(`/docs/${filePath}`);
917
+ const lvl0 = getSectionTitle(filePath);
918
+ const lvl1 = frontmatter.title || null;
919
+ const hierarchy = {
920
+ lvl0,
921
+ lvl1,
922
+ lvl2: null,
923
+ lvl3: null,
924
+ lvl4: null,
925
+ lvl5: null,
926
+ lvl6: null
927
+ };
928
+ if (lvl1) {
929
+ records.push({
930
+ url,
931
+ hierarchy: { ...hierarchy },
932
+ content: frontmatter.description || null,
933
+ type: "lvl1"
934
+ });
935
+ }
936
+ const plainContent = stripJsx(content);
937
+ const lines = plainContent.split("\n");
938
+ let currentParagraph = [];
939
+ let inCodeBlock = false;
940
+ const flushParagraph = () => {
941
+ if (currentParagraph.length > 0) {
942
+ const text = currentParagraph.join(" ").trim();
943
+ if (text) {
944
+ records.push({
945
+ url: buildAnchorUrl(),
946
+ hierarchy: { ...hierarchy },
947
+ content: text,
948
+ type: "content"
949
+ });
950
+ }
951
+ currentParagraph = [];
952
+ }
953
+ };
954
+ const buildAnchorUrl = () => {
955
+ for (let i = 6; i >= 2; i--) {
956
+ const key = `lvl${i}`;
957
+ if (hierarchy[key]) return `${url}#${slugify(hierarchy[key])}`;
958
+ }
959
+ return url;
960
+ };
961
+ for (const line of lines) {
962
+ const trimmed = line.trim();
963
+ if (trimmed.startsWith("```")) {
964
+ inCodeBlock = !inCodeBlock;
965
+ continue;
966
+ }
967
+ if (inCodeBlock) continue;
968
+ if (/^import\s+[\w{*]/.test(trimmed) || /^export\s+[\w{*]/.test(trimmed)) continue;
969
+ const headingMatch = trimmed.match(/^(#{1,6})\s+(.+)$/);
970
+ if (headingMatch) {
971
+ flushParagraph();
972
+ const level = headingMatch[1].length;
973
+ const title = headingMatch[2].replace(/[*`[\]]/g, "").trim();
974
+ for (let i = level; i <= 6; i++) {
975
+ hierarchy[`lvl${i}`] = null;
976
+ }
977
+ hierarchy[`lvl${level}`] = title;
978
+ if (level === 1 && !hierarchy.lvl1) {
979
+ hierarchy.lvl1 = title;
980
+ }
981
+ if (level >= 2) {
982
+ records.push({
983
+ url: `${url}#${slugify(title)}`,
984
+ hierarchy: { ...hierarchy },
985
+ content: null,
986
+ type: `lvl${level}`
987
+ });
988
+ }
989
+ continue;
990
+ }
991
+ if (trimmed === "" || trimmed === "---") {
992
+ flushParagraph();
993
+ continue;
994
+ }
995
+ if (/^\|.+\|/.test(trimmed)) continue;
996
+ const cleaned = trimmed.replace(/^[-*+]\s+/, "").replace(/^\d+\.\s+/, "").replace(/^>\s+/, "").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").trim();
997
+ if (cleaned) currentParagraph.push(cleaned);
998
+ }
999
+ flushParagraph();
1000
+ return records;
1001
+ }
1002
+ async function generateSearchIndex(docsDir, outputDir) {
1003
+ const docs = resolve3(docsDir || DOCS_DIR);
1004
+ const dist = resolve3(outputDir || ASSETS_DIR);
1005
+ await mkdir2(dist, { recursive: true });
1006
+ const mdxFiles = await scanMdxFiles(docs);
1007
+ const results = await Promise.all(
1008
+ mdxFiles.map(async (file) => {
1009
+ const raw = await readFile2(file.absPath, "utf-8");
1010
+ return extractRecords(file.path, raw);
1011
+ })
1012
+ );
1013
+ const allRecords = results.flat();
1014
+ await writeFile2(join3(dist, "search-index.json"), JSON.stringify(allRecords));
1015
+ return allRecords.length;
1016
+ }
1017
+
1018
+ // .docu/node/sentry.ts
1019
+ var sentry = null;
1020
+ var initialized = false;
1021
+ async function initSentry() {
1022
+ const dsn = process.env.SENTRY_DSN;
1023
+ if (!dsn) return;
1024
+ try {
1025
+ sentry = await import("@sentry/bun");
1026
+ sentry.init({
1027
+ dsn,
1028
+ environment: process.env.NODE_ENV || "development",
1029
+ release: process.env.SENTRY_RELEASE || void 0
1030
+ });
1031
+ initialized = true;
1032
+ } catch {
1033
+ sentry = null;
1034
+ }
1035
+ }
1036
+ function captureException(err, context) {
1037
+ if (!initialized || !sentry) return;
1038
+ sentry.captureException(err, context ? { extra: context } : void 0);
1039
+ }
1040
+
1041
+ // .docu/node/git.ts
1042
+ import { execFile as execFile2 } from "node:child_process";
1043
+ function runGit(args) {
1044
+ return new Promise((resolve4, reject) => {
1045
+ execFile2("git", args, { maxBuffer: 16 * 1024 * 1024 }, (err, stdout) => {
1046
+ if (err) reject(err);
1047
+ else resolve4(stdout);
1048
+ });
1049
+ });
1050
+ }
1051
+ function sanitizePath(filePath) {
1052
+ const cleanPath = filePath.replace(/^\//, "");
1053
+ if (!cleanPath || !/^[a-zA-Z0-9\-_/.\s]+$/.test(cleanPath) || /(^|\/)\.\.($|\/)/.test(cleanPath))
1054
+ return null;
1055
+ return cleanPath;
1056
+ }
1057
+ async function getGitLastModified(filePath) {
1058
+ const cleanPath = sanitizePath(filePath);
1059
+ if (!cleanPath) return null;
1060
+ try {
1061
+ const text = await runGit(["log", "-1", "--format=%cI", "--", cleanPath]);
1062
+ const date = text.trim();
1063
+ return date || null;
1064
+ } catch {
1065
+ return null;
1066
+ }
1067
+ }
1068
+ async function getGitLastModifiedBatch(filePaths) {
1069
+ const result = /* @__PURE__ */ new Map();
1070
+ if (filePaths.length === 0) return result;
1071
+ const safePaths = [];
1072
+ for (const fp of filePaths) {
1073
+ const cleanPath = sanitizePath(fp);
1074
+ if (!cleanPath) {
1075
+ console.warn(`[git] getGitLastModifiedBatch: skipping invalid path "${fp}"`);
1076
+ continue;
1077
+ }
1078
+ safePaths.push(cleanPath);
1079
+ }
1080
+ if (safePaths.length === 0) return result;
1081
+ try {
1082
+ const text = await runGit([
1083
+ "log",
1084
+ "--format=%cI",
1085
+ "--name-only",
1086
+ "--diff-filter=ACMR",
1087
+ ...safePaths
1088
+ ]);
1089
+ let currentDate = "";
1090
+ for (const line of text.split("\n")) {
1091
+ const trimmed = line.trim();
1092
+ if (!trimmed) continue;
1093
+ if (/^\d{4}-\d{2}-\d{2}T/.test(trimmed)) {
1094
+ currentDate = trimmed;
1095
+ } else if (currentDate && !result.has(trimmed)) {
1096
+ result.set(trimmed, currentDate);
1097
+ }
1098
+ }
1099
+ } catch (err) {
1100
+ console.error("Failed to get git last modified batch for", filePaths, err);
1101
+ }
1102
+ return result;
1103
+ }
1104
+
1105
+ // .docu/node/mdx.ts
1106
+ import React from "react";
1107
+ import {
1108
+ serialize,
1109
+ extractTocsFromRawMdx,
1110
+ extractFrontmatterWithContent as extractFrontmatterWithContent2,
1111
+ createDefaultRehypePlugins,
1112
+ createDefaultRemarkPlugins,
1113
+ MDXRemote
1114
+ } from "@docubook/core";
1115
+ import { createMdxComponents } from "@docubook/mdx-content";
1116
+ function appendHtml(value) {
1117
+ if (typeof value !== "string") return null;
1118
+ if (/^https?:\/\//.test(value)) return null;
1119
+ if (!value.startsWith("/docs/")) return null;
1120
+ if (value.includes("#")) return null;
1121
+ if (value.endsWith(".html")) return null;
1122
+ return `${value}.html`;
1123
+ }
1124
+ function rehypeDocsHtmlLinks() {
1125
+ return (tree) => {
1126
+ function walk(node) {
1127
+ if (node.type === "element" && node.tagName === "a") {
1128
+ const fixed = appendHtml(node.properties?.href);
1129
+ if (fixed) node.properties.href = fixed;
1130
+ }
1131
+ if (node.children) {
1132
+ for (const child of node.children) walk(child);
1133
+ }
1134
+ }
1135
+ walk(tree);
1136
+ return tree;
1137
+ };
1138
+ }
1139
+ function remarkMdxJsxDocsHtmlLinks() {
1140
+ return (tree) => {
1141
+ function walk(node) {
1142
+ if ((node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") && node.attributes) {
1143
+ for (const attr of node.attributes) {
1144
+ if (attr.type === "mdxJsxAttribute" && attr.name === "href") {
1145
+ const fixed = appendHtml(attr.value);
1146
+ if (fixed) attr.value = fixed;
1147
+ }
1148
+ }
1149
+ }
1150
+ if (node.children) {
1151
+ for (const child of node.children) walk(child);
1152
+ }
1153
+ }
1154
+ walk(tree);
1155
+ return tree;
1156
+ };
1157
+ }
1158
+ async function compileMdx(rawMdx, filePath, gitDates, remarkPlugins, rehypePlugins) {
1159
+ const tocs = extractTocsFromRawMdx(rawMdx);
1160
+ const { frontmatter, strippedContent } = extractFrontmatterWithContent2(rawMdx);
1161
+ const defaultRemark = createDefaultRemarkPlugins();
1162
+ const defaultRehype = createDefaultRehypePlugins();
1163
+ const finalRemark = [...defaultRemark, remarkMdxJsxDocsHtmlLinks, ...remarkPlugins ?? []];
1164
+ const finalRehype = [...defaultRehype, rehypeDocsHtmlLinks, ...rehypePlugins ?? []];
1165
+ const serialized = await serialize(strippedContent, {
1166
+ mdxOptions: {
1167
+ rehypePlugins: finalRehype,
1168
+ remarkPlugins: finalRemark
1169
+ }
1170
+ });
1171
+ const components = createMdxComponents();
1172
+ const content = React.createElement(MDXRemote, {
1173
+ compiledSource: serialized.compiledSource,
1174
+ scope: {},
1175
+ frontmatter: {},
1176
+ components
1177
+ });
1178
+ const date = frontmatter.date || gitDates?.get(filePath) || await getGitLastModified(filePath) || void 0;
1179
+ return {
1180
+ content,
1181
+ compiledSource: serialized.compiledSource,
1182
+ frontmatter: { ...frontmatter, date },
1183
+ tocs
1184
+ };
1185
+ }
1186
+
1187
+ // .docu/pages/docs/[[...slug]].tsx
1188
+ import { ChevronLeft, ChevronRight } from "lucide-react";
1189
+
1190
+ // .docu/components/Breadcrumb.tsx
1191
+ import {
1192
+ Breadcrumb,
1193
+ BreadcrumbItem,
1194
+ BreadcrumbList,
1195
+ BreadcrumbPage
1196
+ } from "@docubook/ui-react/breadcrumbs";
1197
+ import { jsx, jsxs } from "react/jsx-runtime";
1198
+ function toTitleCase2(input) {
1199
+ return input.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
1200
+ }
1201
+ function DocsBreadcrumb({ paths }) {
1202
+ return /* @__PURE__ */ jsx(Breadcrumb, { className: "py-4", children: /* @__PURE__ */ jsxs(BreadcrumbList, { children: [
1203
+ /* @__PURE__ */ jsx(BreadcrumbItem, { children: /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Docs" }) }),
1204
+ paths.map((path, index) => /* @__PURE__ */ jsx(BreadcrumbItem, { children: index < paths.length - 1 ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: toTitleCase2(path) }) : /* @__PURE__ */ jsx(BreadcrumbPage, { className: "text-base-content", children: toTitleCase2(path) }) }, `${path}-${index}`))
1205
+ ] }) });
1206
+ }
1207
+
1208
+ // .docu/node/route.ts
1209
+ var docuConfig2 = loadDocuConfig();
1210
+ var routes = resolveRoutes(docuConfig2.routes);
1211
+ function flattenRoutes() {
1212
+ const paths = [];
1213
+ function traverse(route, section = "") {
1214
+ const fullPath = route.href.startsWith(section) ? route.href : `${section}${route.href}`.replace(/\/+/g, "/");
1215
+ if (route.href && !route.noLink) {
1216
+ paths.push(fullPath);
1217
+ }
1218
+ if (route.items) {
1219
+ route.items.forEach((item) => traverse(item, fullPath));
1220
+ }
1221
+ }
1222
+ routes.forEach((route) => traverse(route));
1223
+ return paths;
1224
+ }
1225
+ function getRouteMap() {
1226
+ const map = /* @__PURE__ */ new Map();
1227
+ function traverse(route, section = "") {
1228
+ const fullPath = route.href.startsWith(section) ? route.href : `${section}${route.href}`.replace(/\/+/g, "/");
1229
+ map.set(fullPath, route.title);
1230
+ if (route.items) {
1231
+ route.items.forEach((item) => traverse(item, fullPath));
1232
+ }
1233
+ }
1234
+ routes.forEach((route) => traverse(route));
1235
+ return map;
1236
+ }
1237
+ function getPreviousNext(pathname) {
1238
+ const normalizedPath = pathname.replace(/^\/|$/g, "");
1239
+ const paths = flattenRoutes();
1240
+ const index = paths.findIndex((href) => href === `/${normalizedPath}` || href === normalizedPath);
1241
+ if (index === -1) {
1242
+ return { prev: null, next: null };
1243
+ }
1244
+ const routeMap = getRouteMap();
1245
+ const prevHref = index > 0 ? paths[index - 1] : null;
1246
+ const nextHref = index < paths.length - 1 ? paths[index + 1] : null;
1247
+ return {
1248
+ prev: prevHref ? { href: prevHref, title: routeMap.get(prevHref) || "" } : null,
1249
+ next: nextHref ? { href: nextHref, title: routeMap.get(nextHref) || "" } : null
1250
+ };
1251
+ }
1252
+
1253
+ // .docu/components/Pagination.tsx
1254
+ import { PaginationDocs } from "@docubook/ui-react/pagination";
1255
+ import { jsx as jsx2 } from "react/jsx-runtime";
1256
+ function Pagination({
1257
+ pathname,
1258
+ className,
1259
+ prevIcon,
1260
+ nextIcon,
1261
+ linkClassName
1262
+ }) {
1263
+ const { prev, next } = getPreviousNext(pathname);
1264
+ if (!prev && !next) {
1265
+ return null;
1266
+ }
1267
+ return /* @__PURE__ */ jsx2(
1268
+ PaginationDocs,
1269
+ {
1270
+ prev: prev ? { href: docsHtmlHref(`/docs${prev.href}`), title: prev.title } : void 0,
1271
+ next: next ? { href: docsHtmlHref(`/docs${next.href}`), title: next.title } : void 0,
1272
+ className,
1273
+ prevIcon,
1274
+ nextIcon,
1275
+ linkClassName
1276
+ }
1277
+ );
1278
+ }
1279
+
1280
+ // .docu/components/Typography.tsx
1281
+ import { jsx as jsx3 } from "react/jsx-runtime";
1282
+ function Typography({ children }) {
1283
+ 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 w-[85vw] max-w-[500px]! min-w-full! pt-2 sm:mx-auto sm:w-full", children });
1284
+ }
1285
+
1286
+ // .docu/components/EditWith.tsx
1287
+ import { SquarePen } from "lucide-react";
1288
+
1289
+ // .docu/node/helpers.ts
1290
+ var docuConfig3 = loadDocuConfig();
1291
+ function getEditLink(url, filePath) {
1292
+ const configPath = docuConfig3?.repo?.path || detectPlatformPath(url);
1293
+ const encodedPath = filePath.replace(/^\//, "").split("/").map(encodeURIComponent).join("/");
1294
+ return `${url}/${configPath}`.replace("{filePath}", encodedPath);
1295
+ }
1296
+ function detectPlatformPath(url) {
1297
+ try {
1298
+ const host = new URL(url).hostname;
1299
+ if (host === "github.com") return "blob/main/{filePath}";
1300
+ if (host === "gitlab.com") return "-/blob/main/{filePath}";
1301
+ if (host === "bitbucket.org") return "src/main/{filePath}";
1302
+ if (host === "gitea.com") return "src/branch/main/{filePath}";
1303
+ if (host === "codeberg.org") return "src/branch/main/{filePath}";
1304
+ return "src/branch/main/{filePath}";
1305
+ } catch {
1306
+ return "blob/main/{filePath}";
1307
+ }
1308
+ }
1309
+ function isEditEnabled() {
1310
+ return docuConfig3?.repo?.edit ?? false;
1311
+ }
1312
+ function getRepoUrl() {
1313
+ return docuConfig3?.repo?.url || "";
1314
+ }
1315
+ function getSocialLinks() {
1316
+ return docuConfig3?.footer?.social || [];
1317
+ }
1318
+
1319
+ // .docu/components/EditWith.tsx
1320
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
1321
+ function EditWith({
1322
+ filePath,
1323
+ text = "Edit this page",
1324
+ className = ""
1325
+ }) {
1326
+ if (!isEditEnabled()) return null;
1327
+ const repoUrl = getRepoUrl();
1328
+ if (!repoUrl) return null;
1329
+ const editUrl = getEditLink(repoUrl, filePath);
1330
+ return /* @__PURE__ */ jsx4("div", { className: `text-right text-sm ${className}`, children: /* @__PURE__ */ jsxs2(
1331
+ "a",
1332
+ {
1333
+ href: editUrl,
1334
+ target: "_blank",
1335
+ rel: "noopener noreferrer",
1336
+ "aria-label": "Edit this page",
1337
+ className: "flex items-center gap-1 no-underline",
1338
+ children: [
1339
+ /* @__PURE__ */ jsx4(SquarePen, { className: "h-4 w-4" }),
1340
+ text
1341
+ ]
1342
+ }
1343
+ ) });
1344
+ }
1345
+
1346
+ // .docu/components/Social.tsx
1347
+ import { jsx as jsx5 } from "react/jsx-runtime";
1348
+ function createIcon(pathData) {
1349
+ return function Icon({ className = "", size = 20 }) {
1350
+ return /* @__PURE__ */ jsx5(
1351
+ "svg",
1352
+ {
1353
+ className,
1354
+ width: size,
1355
+ height: size,
1356
+ viewBox: "0 0 24 24",
1357
+ xmlns: "http://www.w3.org/2000/svg",
1358
+ fill: "currentColor",
1359
+ children: /* @__PURE__ */ jsx5("path", { d: pathData })
1360
+ }
1361
+ );
1362
+ };
1363
+ }
1364
+ var GithubIcon = createIcon(
1365
+ "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
1366
+ );
1367
+ var BitbucketIcon = createIcon(
1368
+ "M.778 1.213a.768.768 0 00-.768.892l3.263 19.81c.084.5.515.868 1.022.873H19.95a.772.772 0 00.77-.646l3.27-20.03a.768.768 0 00-.768-.891zM14.52 15.53H9.522L8.17 8.466h7.561z"
1369
+ );
1370
+ var GitlabIcon = createIcon(
1371
+ "m23.6004 9.5927-.0337-.0862L20.3.9814a.851.851 0 0 0-.3362-.405.8748.8748 0 0 0-.9997.0539.8748.8748 0 0 0-.29.4399l-2.2055 6.748H7.5375l-2.2057-6.748a.8573.8573 0 0 0-.29-.4412.8748.8748 0 0 0-.9997-.0537.8585.8585 0 0 0-.3362.4049L.4332 9.5015l-.0325.0862a6.0657 6.0657 0 0 0 2.0119 7.0105l.0113.0087.03.0213 4.976 3.7264 2.462 1.8633 1.4995 1.1321a1.0085 1.0085 0 0 0 1.2197 0l1.4995-1.1321 2.4619-1.8633 5.006-3.7489.0125-.01a6.0682 6.0682 0 0 0 2.0094-7.003z"
1372
+ );
1373
+ var NpmIcon = createIcon(
1374
+ "M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.04 19.17H5.113z"
1375
+ );
1376
+ var YoutubeIcon = createIcon(
1377
+ "M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"
1378
+ );
1379
+ var TwitterIcon = createIcon(
1380
+ "M14.234 10.162 22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299-.929-1.329L3.076 1.56h3.182l5.965 8.532.929 1.329 7.754 11.09h-3.182z"
1381
+ );
1382
+ var InstagramIcon = createIcon(
1383
+ "M7.0301.084c-1.2768.0602-2.1487.264-2.911.5634-.7888.3075-1.4575.72-2.1228 1.3877-.6652.6677-1.075 1.3368-1.3802 2.127-.2954.7638-.4956 1.6365-.552 2.914-.0564 1.2775-.0689 1.6882-.0626 4.947.0062 3.2586.0206 3.6671.0825 4.9473.061 1.2765.264 2.1482.5635 2.9107.308.7889.72 1.4573 1.388 2.1228.6679.6655 1.3365 1.0743 2.1285 1.38.7632.295 1.6361.4961 2.9134.552 1.2773.056 1.6884.069 4.9462.0627 3.2578-.0062 3.668-.0207 4.9478-.0814 1.28-.0607 2.147-.2652 2.9098-.5633.7889-.3086 1.4578-.72 2.1228-1.3881.665-.6682 1.0745-1.3378 1.3795-2.1284.2957-.7632.4966-1.636.552-2.9124.056-1.2809.0692-1.6898.063-4.948-.0063-3.2583-.021-3.6668-.0817-4.9465-.0607-1.2797-.264-2.1487-.5633-2.9117-.3084-.7889-.72-1.4568-1.3876-2.1228C21.2982 1.33 20.628.9208 19.8378.6165 19.074.321 18.2017.1197 16.9244.0645 15.6471.0093 15.236-.005 11.977.0014 8.718.0076 8.31.0215 7.0301.0839m.1402 21.6932c-1.17-.0509-1.8053-.2453-2.2287-.408-.5606-.216-.96-.4771-1.3819-.895-.422-.4178-.6811-.8186-.9-1.378-.1644-.4234-.3624-1.058-.4171-2.228-.0595-1.2645-.072-1.6442-.079-4.848-.007-3.2037.0053-3.583.0607-4.848.05-1.169.2456-1.805.408-2.2282.216-.5613.4762-.96.895-1.3816.4188-.4217.8184-.6814 1.3783-.9003.423-.1651 1.0575-.3614 2.227-.4171 1.2655-.06 1.6447-.072 4.848-.079 3.2033-.007 3.5835.005 4.8495.0608 1.169.0508 1.8053.2445 2.228.408.5608.216.96.4754 1.3816.895.4217.4194.6816.8176.9005 1.3787.1653.4217.3617 1.056.4169 2.2263.0602 1.2655.0739 1.645.0796 4.848.0058 3.203-.0055 3.5834-.061 4.848-.051 1.17-.245 1.8055-.408 2.2294-.216.5604-.4763.96-.8954 1.3814-.419.4215-.8181.6811-1.3783.9-.4224.1649-1.0577.3617-2.2262.4174-1.2656.0595-1.6448.072-4.8493.079-3.2045.007-3.5825-.006-4.848-.0608M16.953 5.5864A1.44 1.44 0 1 0 18.39 4.144a1.44 1.44 0 0 0-1.437 1.4424M5.8385 12.012c.0067 3.4032 2.7706 6.1557 6.173 6.1493 3.4026-.0065 6.157-2.7701 6.1506-6.1733-.0065-3.4032-2.771-6.1565-6.174-6.1498-3.403.0067-6.156 2.771-6.1496 6.1738M8 12.0077a4 4 0 1 1 4.008 3.9921A3.9996 3.9996 0 0 1 8 12.0077"
1384
+ );
1385
+ var LinkedinIcon = createIcon(
1386
+ "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.528zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.226.792 24 1.771 24h20.451C23.2 24 24 23.226 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"
1387
+ );
1388
+ var FacebookIcon = createIcon(
1389
+ "M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z"
1390
+ );
1391
+ var TelegramIcon = createIcon(
1392
+ "M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"
1393
+ );
1394
+ var DiscordIcon = createIcon(
1395
+ "M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"
1396
+ );
1397
+ var ThreadsIcon = createIcon(
1398
+ "M12.186 24h-.007c-3.581-.024-6.334-1.205-8.184-3.509C2.35 18.44 1.5 15.586 1.472 12.01v-.017c.03-3.579.879-6.43 2.525-8.482C5.845 1.205 8.6.024 12.18 0h.014c2.746.02 5.043.725 6.826 2.098 1.677 1.29 2.858 3.13 3.509 5.467l-2.04.569c-1.104-3.96-3.898-5.984-8.304-6.015-2.91.022-5.11.936-6.54 2.717C4.307 6.504 3.616 8.914 3.589 12c.027 3.086.718 5.496 2.057 7.164 1.43 1.783 3.631 2.698 6.54 2.717 2.623-.02 4.358-.631 5.8-2.045 1.647-1.613 1.618-3.593 1.09-4.798-.31-.71-.873-1.3-1.634-1.75-.192 1.352-.622 2.446-1.284 3.272-.886 1.102-2.14 1.704-3.73 1.79-1.202.065-2.361-.218-3.259-.801-1.063-.689-1.685-1.74-1.752-2.964-.065-1.19.408-2.285 1.33-3.082.88-.76 2.119-1.207 3.583-1.291a13.853 13.853 0 0 1 3.02.142c-.126-.742-.375-1.332-.75-1.757-.513-.586-1.308-.883-2.359-.89h-.029c-.844 0-1.992.232-2.721 1.32L7.734 7.847c.98-1.454 2.568-2.256 4.478-2.256h.044c3.194.02 5.097 1.975 5.287 5.388.108.046.216.094.321.142 1.49.7 2.58 1.761 3.154 3.07.797 1.82.871 4.79-1.548 7.158-1.85 1.81-4.094 2.628-7.277 2.65Zm1.003-11.69c-.242 0-.487.007-.739.021-1.836.103-2.98.946-2.916 2.143.067 1.256 1.452 1.839 2.784 1.767 1.224-.065 2.818-.543 3.086-3.71a10.5 10.5 0 0 0-2.215-.221z"
1399
+ );
1400
+ var MastodonIcon = createIcon(
1401
+ "M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"
1402
+ );
1403
+ var iconMap = {
1404
+ github: GithubIcon,
1405
+ gitlab: GitlabIcon,
1406
+ bitbucket: BitbucketIcon,
1407
+ npm: NpmIcon,
1408
+ youtube: YoutubeIcon,
1409
+ twitter: TwitterIcon,
1410
+ x: TwitterIcon,
1411
+ instagram: InstagramIcon,
1412
+ linkedin: LinkedinIcon,
1413
+ facebook: FacebookIcon,
1414
+ telegram: TelegramIcon,
1415
+ discord: DiscordIcon,
1416
+ threads: ThreadsIcon,
1417
+ mastodon: MastodonIcon
1418
+ };
1419
+ function getSocialIcon(name) {
1420
+ const key = name.toLowerCase().replace(/\s+/g, "");
1421
+ return iconMap[key] || null;
1422
+ }
1423
+ function Social({ className = "", size = 16 }) {
1424
+ const socialLinks = getSocialLinks();
1425
+ if (!socialLinks?.length) return null;
1426
+ return /* @__PURE__ */ jsx5("div", { className: `flex gap-1 ${className}`, children: socialLinks.map((link) => {
1427
+ const Icon = getSocialIcon(link.name);
1428
+ return /* @__PURE__ */ jsx5(
1429
+ "a",
1430
+ {
1431
+ href: link.url,
1432
+ target: "_blank",
1433
+ rel: "noopener noreferrer",
1434
+ "aria-label": link.name,
1435
+ className: "btn-xs btn-circle btn-ghost text-muted-foreground",
1436
+ children: Icon && /* @__PURE__ */ jsx5(Icon, { size })
1437
+ },
1438
+ link.name
1439
+ );
1440
+ }) });
1441
+ }
1442
+
1443
+ // .docu/components/Footer.tsx
1444
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
1445
+ function Footer() {
1446
+ return /* @__PURE__ */ jsxs3("footer", { className: "text-muted-foreground mt-auto flex w-full flex-col items-start gap-4 py-6 sm:flex-row sm:items-center sm:justify-between", children: [
1447
+ /* @__PURE__ */ jsx6(Social, {}),
1448
+ /* @__PURE__ */ jsx6("aside", { className: "sm:ml-auto", children: /* @__PURE__ */ jsxs3("p", { className: "text-xs", children: [
1449
+ "Made with",
1450
+ " ",
1451
+ /* @__PURE__ */ jsx6(
1452
+ "a",
1453
+ {
1454
+ href: "https://docubook.pro",
1455
+ target: "_blank",
1456
+ rel: "noopener noreferrer",
1457
+ className: "link link-hover text-muted-foreground font-medium",
1458
+ children: "DocuBook"
1459
+ }
1460
+ )
1461
+ ] }) })
1462
+ ] });
1463
+ }
1464
+
1465
+ // .docu/components/Toc.tsx
1466
+ import { useState as useState2, useCallback as useCallback2, useEffect as useEffect2, useRef } from "react";
1467
+ import { ListIcon } from "lucide-react";
1468
+
1469
+ // .docu/components/ScrollTo.tsx
1470
+ import { ArrowUpIcon } from "lucide-react";
1471
+ import { useEffect, useState, useCallback } from "react";
1472
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1473
+ function ScrollTo({ className, showIcon = true }) {
1474
+ const [isVisible, setIsVisible] = useState(false);
1475
+ const checkScroll = useCallback(() => {
1476
+ const container = document.getElementById("scroll-container");
1477
+ const scrollY = container ? container.scrollTop : window.scrollY;
1478
+ const scrollHeight = container ? container.scrollHeight : document.documentElement.scrollHeight;
1479
+ const threshold = scrollHeight * 0.3;
1480
+ const shouldShow = scrollY > threshold;
1481
+ if (shouldShow !== isVisible) {
1482
+ setIsVisible(shouldShow);
1483
+ }
1484
+ }, [isVisible]);
1485
+ useEffect(() => {
1486
+ let timeoutId;
1487
+ const handleScroll = () => {
1488
+ if (timeoutId) clearTimeout(timeoutId);
1489
+ timeoutId = setTimeout(checkScroll, 100);
1490
+ };
1491
+ const container = document.getElementById("scroll-container") || window;
1492
+ container.addEventListener("scroll", handleScroll, { passive: true });
1493
+ return () => {
1494
+ container.removeEventListener("scroll", handleScroll);
1495
+ if (timeoutId) clearTimeout(timeoutId);
1496
+ };
1497
+ }, [checkScroll]);
1498
+ const scrollToTop = useCallback((e) => {
1499
+ e.preventDefault();
1500
+ const container = document.getElementById("scroll-container");
1501
+ if (container) {
1502
+ container.scrollTo({ top: 0, behavior: "smooth" });
1503
+ } else {
1504
+ window.scrollTo({ top: 0, behavior: "smooth" });
1505
+ }
1506
+ }, []);
1507
+ return /* @__PURE__ */ jsx7(
1508
+ "div",
1509
+ {
1510
+ className: cn(
1511
+ "border-base-300 mt-4 border-t pt-4",
1512
+ "transition-opacity duration-300",
1513
+ isVisible ? "opacity-100" : "pointer-events-none opacity-0",
1514
+ className
1515
+ ),
1516
+ children: /* @__PURE__ */ jsxs4(
1517
+ "a",
1518
+ {
1519
+ href: "#",
1520
+ onClick: scrollToTop,
1521
+ className: cn(
1522
+ "inline-flex items-center text-sm",
1523
+ "link link-hover text-base-content/60 hover:text-base-content",
1524
+ "transition-all duration-200 hover:translate-y-px"
1525
+ ),
1526
+ "aria-label": "Scroll to top",
1527
+ children: [
1528
+ showIcon && /* @__PURE__ */ jsx7(ArrowUpIcon, { className: "mr-1 h-3.5 w-3.5 shrink-0" }),
1529
+ /* @__PURE__ */ jsx7("span", { children: "Scroll to Top" })
1530
+ ]
1531
+ }
1532
+ )
1533
+ }
1534
+ );
1535
+ }
1536
+
1537
+ // .docu/components/Toc.tsx
1538
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1539
+ function Toc({ tocs }) {
1540
+ const [activeId, setActiveId] = useState2(null);
1541
+ const clickedIdRef = useRef(null);
1542
+ const clickTimerRef = useRef(null);
1543
+ const activeIdRef = useRef(null);
1544
+ useEffect2(() => {
1545
+ activeIdRef.current = activeId;
1546
+ }, [activeId]);
1547
+ useEffect2(() => {
1548
+ if (typeof document === "undefined" || !tocs.length) return;
1549
+ const isDesktop = window.innerWidth >= 1024;
1550
+ const container = isDesktop ? document.getElementById("scroll-container") : null;
1551
+ const scrollTarget = container || window;
1552
+ const offset = isDesktop ? 80 : 100;
1553
+ const handleScroll = () => {
1554
+ if (clickedIdRef.current) return;
1555
+ let currentId = null;
1556
+ for (const toc of tocs) {
1557
+ const id = toc.href.slice(1);
1558
+ const el = document.getElementById(id);
1559
+ if (!el) continue;
1560
+ const top = container ? el.offsetTop - container.scrollTop : el.getBoundingClientRect().top;
1561
+ if (top <= offset) {
1562
+ currentId = id;
1563
+ } else {
1564
+ break;
1565
+ }
1566
+ }
1567
+ if (currentId && currentId !== activeIdRef.current) {
1568
+ setActiveId(currentId);
1569
+ history.replaceState(null, "", `#${currentId}`);
1570
+ }
1571
+ };
1572
+ handleScroll();
1573
+ let throttleTimer = null;
1574
+ const listener = tocs.length > 30 ? () => {
1575
+ if (throttleTimer) return;
1576
+ throttleTimer = setTimeout(() => {
1577
+ throttleTimer = null;
1578
+ handleScroll();
1579
+ }, 50);
1580
+ } : handleScroll;
1581
+ scrollTarget.addEventListener("scroll", listener, { passive: true });
1582
+ return () => {
1583
+ scrollTarget.removeEventListener("scroll", listener);
1584
+ if (throttleTimer) clearTimeout(throttleTimer);
1585
+ };
1586
+ }, [tocs]);
1587
+ const handleLinkClick = useCallback2((id) => {
1588
+ clickedIdRef.current = id;
1589
+ setActiveId(id);
1590
+ history.replaceState(null, "", `#${id}`);
1591
+ if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
1592
+ clickTimerRef.current = setTimeout(() => {
1593
+ clickedIdRef.current = null;
1594
+ }, 1e3);
1595
+ }, []);
1596
+ useEffect2(() => {
1597
+ return () => {
1598
+ if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
1599
+ };
1600
+ }, []);
1601
+ if (!tocs.length) return null;
1602
+ return /* @__PURE__ */ jsxs5("div", { className: "flex w-full flex-col gap-2", children: [
1603
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
1604
+ /* @__PURE__ */ jsx8(ListIcon, { className: "h-4 w-4" }),
1605
+ /* @__PURE__ */ jsx8("h3", { className: "text-sm font-medium", children: "On this page" })
1606
+ ] }),
1607
+ /* @__PURE__ */ jsx8("div", { className: "relative", children: /* @__PURE__ */ jsxs5("div", { className: "relative text-sm", children: [
1608
+ /* @__PURE__ */ jsx8("div", { className: "bg-base-300 absolute left-0 top-0 h-full w-px" }),
1609
+ /* @__PURE__ */ jsx8("div", { className: "flex flex-col", children: tocs.map(({ href, level, text }) => {
1610
+ const id = href.slice(1);
1611
+ const isActive = activeId === id;
1612
+ const levelPadding = (level - 2) * 16;
1613
+ return /* @__PURE__ */ jsxs5(
1614
+ "div",
1615
+ {
1616
+ className: cn(
1617
+ "relative flex items-center transition-all duration-200",
1618
+ isActive && "bg-primary/5"
1619
+ ),
1620
+ children: [
1621
+ /* @__PURE__ */ jsxs5(
1622
+ "div",
1623
+ {
1624
+ className: cn(
1625
+ "flex shrink-0 items-center px-1 py-2 transition-all duration-200",
1626
+ isActive && "border-primary -ml-px border-l-[3px]"
1627
+ ),
1628
+ children: [
1629
+ /* @__PURE__ */ jsx8(
1630
+ "div",
1631
+ {
1632
+ className: cn(
1633
+ "h-px transition-colors duration-200",
1634
+ isActive ? "bg-primary w-3" : "bg-base-300 w-2"
1635
+ )
1636
+ }
1637
+ ),
1638
+ /* @__PURE__ */ jsx8(
1639
+ "div",
1640
+ {
1641
+ className: cn(
1642
+ "h-1.5 w-1.5 shrink-0 rounded-full transition-colors duration-300",
1643
+ isActive ? "bg-primary" : "bg-base-300"
1644
+ )
1645
+ }
1646
+ )
1647
+ ]
1648
+ }
1649
+ ),
1650
+ /* @__PURE__ */ jsx8(
1651
+ "a",
1652
+ {
1653
+ href,
1654
+ onClick: (e) => {
1655
+ e.preventDefault();
1656
+ handleLinkClick(id);
1657
+ const el = document.getElementById(id);
1658
+ if (el) el.scrollIntoView({ behavior: "smooth" });
1659
+ },
1660
+ className: cn(
1661
+ "flex flex-1 items-center py-2 transition-all duration-200",
1662
+ isActive ? "text-primary font-medium" : "text-base-content/60 hover:text-base-content"
1663
+ ),
1664
+ style: { paddingLeft: `${levelPadding + 6}px` },
1665
+ children: /* @__PURE__ */ jsx8("span", { className: "line-clamp-2 break-words text-sm", children: text })
1666
+ }
1667
+ )
1668
+ ]
1669
+ },
1670
+ href
1671
+ );
1672
+ }) })
1673
+ ] }) }),
1674
+ /* @__PURE__ */ jsx8(ScrollTo, { className: "mt-2" })
1675
+ ] });
1676
+ }
1677
+
1678
+ // .docu/pages/docs/[[...slug]].tsx
1679
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
1680
+ function DocsPage({
1681
+ slug,
1682
+ title,
1683
+ description,
1684
+ date,
1685
+ content,
1686
+ tocs,
1687
+ filePath,
1688
+ repoUrl,
1689
+ compiledSource
1690
+ }) {
1691
+ const pathname = slug.join("/");
1692
+ const tocsJson = JSON.stringify(tocs);
1693
+ return /* @__PURE__ */ jsx9("div", { className: "flex w-full flex-1 px-0 pb-4 lg:h-[calc(100vh-4rem)] lg:px-8 lg:pb-8", children: /* @__PURE__ */ jsxs6(
1694
+ "div",
1695
+ {
1696
+ id: "scroll-container",
1697
+ 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",
1698
+ children: [
1699
+ /* @__PURE__ */ jsx9(
1700
+ "div",
1701
+ {
1702
+ id: "mobile-bar-island",
1703
+ className: "sticky top-0 z-50 w-full lg:hidden",
1704
+ "data-tocs": tocsJson,
1705
+ "data-title": title,
1706
+ "data-repo": repoUrl || ""
1707
+ }
1708
+ ),
1709
+ /* @__PURE__ */ jsxs6("div", { className: "w-full min-w-0 flex-[7] px-4 py-4 lg:px-8 lg:py-8", children: [
1710
+ /* @__PURE__ */ jsx9(DocsBreadcrumb, { paths: slug }),
1711
+ /* @__PURE__ */ jsxs6(Typography, { children: [
1712
+ /* @__PURE__ */ jsx9("h1", { className: "-mt-0.5 text-3xl", children: title }),
1713
+ description && /* @__PURE__ */ jsx9("p", { className: "text-muted-foreground -mt-4 text-[16.5px]", children: description }),
1714
+ /* @__PURE__ */ jsx9("div", { id: "mdx-content-island", children: content }),
1715
+ compiledSource && /* @__PURE__ */ jsx9(
1716
+ "script",
1717
+ {
1718
+ id: "mdx-compiled-source",
1719
+ type: "application/json",
1720
+ dangerouslySetInnerHTML: {
1721
+ __html: JSON.stringify(compiledSource).replace(/<\//g, "\\u003C/")
1722
+ }
1723
+ }
1724
+ ),
1725
+ /* @__PURE__ */ jsxs6("div", { className: "border-base-300 my-8 flex items-center border-b-2 border-dashed", children: [
1726
+ /* @__PURE__ */ jsx9(EditWith, { className: "text-muted-foreground", filePath }),
1727
+ date && /* @__PURE__ */ jsxs6("p", { className: "text-muted-foreground ml-auto text-[13px]", children: [
1728
+ "Last updated ",
1729
+ formatDate2(date)
1730
+ ] })
1731
+ ] }),
1732
+ /* @__PURE__ */ jsx9(
1733
+ Pagination,
1734
+ {
1735
+ pathname,
1736
+ prevIcon: /* @__PURE__ */ jsx9(ChevronLeft, { className: "h-3 w-3" }),
1737
+ nextIcon: /* @__PURE__ */ jsx9(ChevronRight, { className: "h-3 w-3" })
1738
+ }
1739
+ ),
1740
+ /* @__PURE__ */ jsx9(Footer, {})
1741
+ ] })
1742
+ ] }),
1743
+ tocs.length > 0 && /* @__PURE__ */ jsx9(
1744
+ "div",
1745
+ {
1746
+ id: "toc-island",
1747
+ "data-tocs": tocsJson,
1748
+ className: "sticky top-4 hidden h-[calc(100vh-8rem)] min-w-[240px] flex-[3] self-start lg:flex lg:px-4 lg:py-6",
1749
+ children: /* @__PURE__ */ jsx9(Toc, { tocs })
1750
+ }
1751
+ )
1752
+ ]
1753
+ }
1754
+ ) });
1755
+ }
1756
+
1757
+ // .docu/pages/404.tsx
1758
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1759
+ function NotFoundPage() {
1760
+ return /* @__PURE__ */ jsx10("div", { className: "flex w-full flex-1 px-4 py-8 lg:h-[calc(100vh-4rem)] lg:px-8 lg:py-4", children: /* @__PURE__ */ jsxs7("div", { className: "bg-base-100 border-base-300 flex min-h-[50vh] w-full flex-col items-center justify-center rounded-xl border shadow-md lg:min-h-0 lg:flex-1", children: [
1761
+ /* @__PURE__ */ jsx10("h1", { className: "text-6xl font-bold", children: "404" }),
1762
+ /* @__PURE__ */ jsx10("p", { className: "text-base-content/60 py-4 text-xl", children: "Page not found" }),
1763
+ /* @__PURE__ */ jsx10("a", { href: "/docs/", className: "btn btn-primary mt-2", children: "Go to Docs" })
1764
+ ] }) });
1765
+ }
1766
+
1767
+ // .docu/components/Lucide.tsx
1768
+ import * as LucideIcons from "lucide-react";
1769
+ import { jsx as jsx11 } from "react/jsx-runtime";
1770
+ function getLucideIcon(name) {
1771
+ if (!name) return null;
1772
+ const icon = LucideIcons[name];
1773
+ return icon || null;
1774
+ }
1775
+ function renderLucideIcon(name, className) {
1776
+ const Icon = getLucideIcon(name);
1777
+ return Icon ? /* @__PURE__ */ jsx11(Icon, { className }) : null;
1778
+ }
1779
+
1780
+ // .docu/components/home/Hero.tsx
1781
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
1782
+ function isExternalLink(link) {
1783
+ return /^https?:\/\//.test(link);
1784
+ }
1785
+ function renderActionButtonIcon(iconName, className) {
1786
+ if (!iconName) return null;
1787
+ const lucideIcon = renderLucideIcon(iconName, className);
1788
+ if (lucideIcon) return lucideIcon;
1789
+ const SocialIcon = getSocialIcon(iconName);
1790
+ if (SocialIcon) return /* @__PURE__ */ jsx12(SocialIcon, { className });
1791
+ return null;
1792
+ }
1793
+ function ActionButton({ action }) {
1794
+ const themeClasses = {
1795
+ primary: "bg-primary text-primary-content hover:bg-primary/90",
1796
+ secondary: "bg-secondary text-secondary-content hover:bg-secondary/90",
1797
+ ghost: "bg-transparent text-muted-foreground border border-base-300 hover:bg-base-200"
1798
+ };
1799
+ const isExternal = isExternalLink(action.link);
1800
+ return /* @__PURE__ */ jsxs8(
1801
+ "a",
1802
+ {
1803
+ href: action.link,
1804
+ target: isExternal ? "_blank" : void 0,
1805
+ rel: isExternal ? "noopener noreferrer" : void 0,
1806
+ className: cn(
1807
+ "inline-flex items-center gap-2 rounded-lg px-6 py-3 text-sm font-medium transition-colors",
1808
+ themeClasses[action.theme || "primary"]
1809
+ ),
1810
+ children: [
1811
+ renderActionButtonIcon(action.icon, "h-4 w-4"),
1812
+ action.text
1813
+ ]
1814
+ }
1815
+ );
1816
+ }
1817
+ function Hero({ hero, className }) {
1818
+ const { tagline, headline, description, actions } = hero;
1819
+ 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: [
1820
+ tagline && /* @__PURE__ */ jsx12("p", { className: "text-primary mb-4 text-lg font-semibold", children: tagline }),
1821
+ /* @__PURE__ */ jsx12("h1", { className: "text-balance text-5xl font-semibold tracking-tight sm:text-7xl", children: headline }),
1822
+ description && /* @__PURE__ */ jsx12("p", { className: "text-muted-foreground mt-8 text-pretty text-lg sm:text-xl", children: description }),
1823
+ 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)) })
1824
+ ] }) });
1825
+ }
1826
+
1827
+ // .docu/components/home/Features.tsx
1828
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
1829
+ function FeatureCard({ feature, index }) {
1830
+ const Wrapper = feature.link ? "a" : "div";
1831
+ const wrapperProps = feature.link ? { href: feature.link } : {};
1832
+ const patternId = `grid-${index}`;
1833
+ return /* @__PURE__ */ jsxs9(
1834
+ Wrapper,
1835
+ {
1836
+ ...wrapperProps,
1837
+ className: cn(
1838
+ "border-base-200 bg-base-100 hover:border-primary/40 group relative overflow-hidden rounded-2xl border p-6 transition-all hover:shadow-lg",
1839
+ feature.link && "cursor-pointer"
1840
+ ),
1841
+ children: [
1842
+ /* @__PURE__ */ jsxs9(
1843
+ "svg",
1844
+ {
1845
+ className: "absolute inset-0 h-full w-full",
1846
+ xmlns: "http://www.w3.org/2000/svg",
1847
+ style: { color: "var(--color-primary)" },
1848
+ "aria-hidden": "true",
1849
+ children: [
1850
+ /* @__PURE__ */ jsx13("defs", { children: /* @__PURE__ */ jsx13("pattern", { id: patternId, width: "40", height: "40", patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ jsx13(
1851
+ "path",
1852
+ {
1853
+ d: "M 40 0 L 0 0 0 40",
1854
+ fill: "none",
1855
+ stroke: "currentColor",
1856
+ strokeWidth: "0.5",
1857
+ opacity: "0.15"
1858
+ }
1859
+ ) }) }),
1860
+ /* @__PURE__ */ jsx13("rect", { width: "100%", height: "100%", fill: `url(#${patternId})` })
1861
+ ]
1862
+ }
1863
+ ),
1864
+ /* @__PURE__ */ jsxs9("div", { className: "relative z-10", children: [
1865
+ feature.icon && /* @__PURE__ */ jsx13("div", { className: "bg-primary/10 mb-4 inline-flex h-12 w-12 items-center justify-center rounded-lg", children: renderLucideIcon(feature.icon, "h-6 w-6 text-primary") }),
1866
+ /* @__PURE__ */ jsx13("h3", { className: "mb-2 text-lg font-semibold", children: feature.title }),
1867
+ /* @__PURE__ */ jsx13("p", { className: "text-muted-foreground text-sm", children: feature.description })
1868
+ ] })
1869
+ ]
1870
+ }
1871
+ );
1872
+ }
1873
+ function Features({ features, className }) {
1874
+ if (!features || features.length === 0) return null;
1875
+ return /* @__PURE__ */ jsx13("div", { className: cn("mx-auto max-w-5xl px-6 pb-24", className), children: /* @__PURE__ */ jsx13("div", { className: "grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3", children: features.map((feature, index) => /* @__PURE__ */ jsx13(FeatureCard, { feature, index }, index)) }) });
1876
+ }
1877
+
1878
+ // .docu/pages/index.tsx
1879
+ import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
1880
+ var docuConfig4 = loadDocuConfig();
1881
+ function IndexPage() {
1882
+ const { meta, home } = docuConfig4;
1883
+ const routes3 = docuConfig4.routes || [];
1884
+ const linkWithHtml = (link) => {
1885
+ if (isExternalUrl(link)) return link;
1886
+ if (link.startsWith("/docs/")) return `${link}.html`;
1887
+ return link;
1888
+ };
1889
+ const features = home?.features?.map((f) => ({
1890
+ ...f,
1891
+ link: f.link ? linkWithHtml(f.link) : void 0
1892
+ })) || routes3.filter((r) => r.context).map((route) => ({
1893
+ icon: route.context?.icon,
1894
+ title: route.context?.title || route.title,
1895
+ description: route.context?.description || "",
1896
+ link: docsHtmlHref(`/docs${route.href}${route.items?.[0]?.href || ""}`)
1897
+ }));
1898
+ const hero = home?.hero ? {
1899
+ ...home.hero,
1900
+ actions: home.hero.actions?.map((a) => ({
1901
+ ...a,
1902
+ link: linkWithHtml(a.link)
1903
+ }))
1904
+ } : {
1905
+ headline: meta.title,
1906
+ description: meta.description
1907
+ };
1908
+ return /* @__PURE__ */ jsxs10("div", { className: "bg-base-100 relative isolate min-h-screen overflow-hidden", children: [
1909
+ /* @__PURE__ */ jsx14("div", { className: "absolute right-4 top-4 z-10", id: "theme-island" }),
1910
+ /* @__PURE__ */ jsx14(
1911
+ "div",
1912
+ {
1913
+ "aria-hidden": "true",
1914
+ className: "pointer-events-none absolute -top-40 left-1/2 -z-10 -translate-x-1/2 blur-3xl sm:-top-80",
1915
+ children: /* @__PURE__ */ jsx14(
1916
+ "div",
1917
+ {
1918
+ style: {
1919
+ clipPath: "polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)"
1920
+ },
1921
+ className: "from-primary to-accent h-[40rem] w-[80rem] bg-gradient-to-tr opacity-20"
1922
+ }
1923
+ )
1924
+ }
1925
+ ),
1926
+ /* @__PURE__ */ jsx14(Hero, { hero }),
1927
+ /* @__PURE__ */ jsx14(Features, { features }),
1928
+ /* @__PURE__ */ jsx14(
1929
+ "div",
1930
+ {
1931
+ "aria-hidden": "true",
1932
+ className: "pointer-events-none absolute bottom-0 left-1/2 -z-10 translate-x-1/4 blur-3xl",
1933
+ children: /* @__PURE__ */ jsx14(
1934
+ "div",
1935
+ {
1936
+ style: {
1937
+ clipPath: "polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)"
1938
+ },
1939
+ className: "from-accent to-primary h-[30rem] w-[70rem] bg-gradient-to-tr opacity-20"
1940
+ }
1941
+ )
1942
+ }
1943
+ )
1944
+ ] });
1945
+ }
1946
+
1947
+ // .docu/components/DocsLayout.tsx
1948
+ import React2 from "react";
1949
+
1950
+ // .docu/components/Menu.tsx
1951
+ import { useState as useState4 } from "react";
1952
+
1953
+ // .docu/components/Sublink.tsx
1954
+ import { useState as useState3, useRef as useRef2, useEffect as useEffect3 } from "react";
1955
+ import { ChevronDown } from "lucide-react";
1956
+
1957
+ // .docu/components/Anchor.tsx
1958
+ import { ArrowUpRight } from "lucide-react";
1959
+ import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
1960
+ function Anchor({
1961
+ href = "",
1962
+ className = "",
1963
+ activeClassName = "",
1964
+ activeWhen,
1965
+ disabled = false,
1966
+ children,
1967
+ ...props
1968
+ }) {
1969
+ const isActive = (() => {
1970
+ if (!activeWhen || typeof window === "undefined") return false;
1971
+ const pathname = window.location.pathname;
1972
+ if (typeof activeWhen === "string")
1973
+ return pathname === activeWhen || pathname.endsWith(activeWhen);
1974
+ if (activeWhen instanceof RegExp) return activeWhen.test(pathname);
1975
+ if (typeof activeWhen === "function") return activeWhen(pathname);
1976
+ return false;
1977
+ })();
1978
+ const isExternal = isExternalUrl(href);
1979
+ const activeClass = isActive ? activeClassName : "";
1980
+ const baseClasses = cn(
1981
+ "hover:underline transition-colors",
1982
+ className,
1983
+ activeClass,
1984
+ disabled && "cursor-not-allowed opacity-50"
1985
+ );
1986
+ if (disabled) {
1987
+ return /* @__PURE__ */ jsx15("span", { className: baseClasses, children });
1988
+ }
1989
+ if (isExternal) {
1990
+ return /* @__PURE__ */ jsxs11("a", { href, className: baseClasses, target: "_blank", rel: "noopener noreferrer", ...props, children: [
1991
+ children,
1992
+ /* @__PURE__ */ jsx15(ArrowUpRight, { className: "ml-0.5 inline-block h-3.5 w-3.5" })
1993
+ ] });
1994
+ }
1995
+ return /* @__PURE__ */ jsx15("a", { href, className: baseClasses, ...props, children });
1996
+ }
1997
+
1998
+ // .docu/components/Sublink.tsx
1999
+ import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
2000
+ function Sublink({
2001
+ title,
2002
+ href,
2003
+ items,
2004
+ noLink,
2005
+ level,
2006
+ onNavigate,
2007
+ parentHref = "",
2008
+ pathname: pathnameProp
2009
+ }) {
2010
+ const fullHref = parentHref ? `${parentHref}${href}` : `/docs${href}`;
2011
+ const currentPathname = pathnameProp || (typeof window !== "undefined" ? window.location.pathname : "/docs");
2012
+ const [isOpen, setIsOpen] = useState3(() => {
2013
+ if (level === 0) return true;
2014
+ if (!items) return false;
2015
+ return currentPathname.startsWith(fullHref) && currentPathname !== fullHref;
2016
+ });
2017
+ const levelPadding = cn(level === 1 && "pl-2", level === 2 && "pl-4", level >= 3 && "pl-6");
2018
+ const isActive = currentPathname === fullHref || currentPathname === `${fullHref}.html`;
2019
+ const activeRef = useRef2(null);
2020
+ useEffect3(() => {
2021
+ if (isActive && activeRef.current) {
2022
+ activeRef.current.scrollIntoView({ block: "nearest" });
2023
+ }
2024
+ }, [isActive]);
2025
+ if (!items) {
2026
+ const link = /* @__PURE__ */ jsx16(
2027
+ Anchor,
2028
+ {
2029
+ href: docsHtmlHref(fullHref),
2030
+ className: "text-foreground hover:text-foreground/80 text-sm transition-colors",
2031
+ activeClassName: "text-primary font-medium",
2032
+ activeWhen: (path) => path === fullHref || path === `${fullHref}.html`,
2033
+ onClick: onNavigate,
2034
+ children: title
2035
+ }
2036
+ );
2037
+ return /* @__PURE__ */ jsx16(
2038
+ "div",
2039
+ {
2040
+ ref: activeRef,
2041
+ className: cn(
2042
+ "py-1",
2043
+ levelPadding,
2044
+ level >= 2 && "border-l-2",
2045
+ level >= 2 && (isActive ? "border-primary" : "border-base-300")
2046
+ ),
2047
+ children: link
2048
+ }
2049
+ );
2050
+ }
2051
+ return /* @__PURE__ */ jsxs12("div", { ref: isActive ? activeRef : void 0, className: cn("flex flex-col", levelPadding), children: [
2052
+ /* @__PURE__ */ jsxs12(
2053
+ "button",
2054
+ {
2055
+ type: "button",
2056
+ onClick: () => setIsOpen(!isOpen),
2057
+ className: cn(
2058
+ "flex w-full cursor-pointer items-center justify-between py-1 text-left text-sm transition-colors",
2059
+ noLink ? "text-base-content font-semibold" : "text-base-content/80 hover:text-base-content font-medium"
2060
+ ),
2061
+ children: [
2062
+ noLink ? /* @__PURE__ */ jsx16("span", { children: title }) : /* @__PURE__ */ jsx16(
2063
+ Anchor,
2064
+ {
2065
+ href: docsHtmlHref(fullHref),
2066
+ className: "text-foreground hover:text-foreground/80 transition-colors",
2067
+ activeClassName: "text-primary",
2068
+ activeWhen: (path) => path === fullHref || path === `${fullHref}.html`,
2069
+ onClick: onNavigate,
2070
+ children: title
2071
+ }
2072
+ ),
2073
+ /* @__PURE__ */ jsx16(
2074
+ ChevronDown,
2075
+ {
2076
+ className: cn(
2077
+ "text-base-content/40 h-4 w-4 shrink-0 transition-transform duration-200",
2078
+ isOpen && "rotate-180"
2079
+ )
2080
+ }
2081
+ )
2082
+ ]
2083
+ }
2084
+ ),
2085
+ isOpen && /* @__PURE__ */ jsx16("div", { className: "flex flex-col py-1", children: items.map((item) => /* @__PURE__ */ jsx16(
2086
+ Sublink,
2087
+ {
2088
+ ...item,
2089
+ href: item.href,
2090
+ level: level + 1,
2091
+ onNavigate,
2092
+ parentHref: fullHref,
2093
+ pathname: pathnameProp
2094
+ },
2095
+ `${fullHref}${item.href}`
2096
+ )) })
2097
+ ] });
2098
+ }
2099
+
2100
+ // .docu/components/SidebarGroupHeader.tsx
2101
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
2102
+ function SidebarGroupHeader({ icon, title }) {
2103
+ return /* @__PURE__ */ jsxs13("div", { className: "sidebar-group-header mb-1.5 flex items-center gap-2.5 font-medium text-gray-900 dark:text-gray-200", children: [
2104
+ icon && /* @__PURE__ */ jsx17("span", { className: "flex h-4 w-4 shrink-0 items-center justify-center", children: renderLucideIcon(icon, "h-3.5 w-3.5") }),
2105
+ /* @__PURE__ */ jsx17("h3", { className: "sidebar-title font-[inherit] text-[length:inherit] leading-[inherit]", children: /* @__PURE__ */ jsx17("span", { children: title }) })
2106
+ ] });
2107
+ }
2108
+
2109
+ // docu-config-runtime:../../docu.json
2110
+ import { readFileSync as readFileSync2 } from "node:fs";
2111
+ import { join as join4 } from "node:path";
2112
+ var config = JSON.parse(readFileSync2(join4(process.cwd(), "docu.json"), "utf-8"));
2113
+ var docu_default = config;
2114
+
2115
+ // .docu/node/client-routes.ts
2116
+ var routes2 = docu_default.routes || [];
2117
+ var config2 = docu_default;
2118
+
2119
+ // .docu/components/Menu.tsx
2120
+ import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
2121
+ function getCurrentContext(path) {
2122
+ if (!path.startsWith("/docs")) return void 0;
2123
+ const match = path.match(/^\/docs\/([^/]+)/);
2124
+ return match ? match[1] : void 0;
2125
+ }
2126
+ function getContextRoute(contextPath, routeList) {
2127
+ return routeList.find((route) => {
2128
+ const normalizedHref = route.href.replace(/^\/+|\/+$/, "");
2129
+ return normalizedHref === contextPath;
2130
+ });
2131
+ }
2132
+ function Menu({ onNavigate, className = "", pathname, routes: routes3 = [] }) {
2133
+ const menuRoutes = routes3;
2134
+ const [currentPath] = useState4(
2135
+ () => pathname || (typeof window !== "undefined" ? window.location.pathname : "/docs")
2136
+ );
2137
+ if (!currentPath.startsWith("/docs")) return null;
2138
+ const mode = config2.sidebar?.context || "dropdown";
2139
+ const isItemActive = (itemHref, parentRouteHref) => {
2140
+ const fullHref = `/docs${parentRouteHref}${itemHref}`;
2141
+ return currentPath === fullHref || currentPath === `${fullHref}.html`;
2142
+ };
2143
+ if (mode === "separator") {
2144
+ const contextRoutes = menuRoutes.filter((r) => r.context);
2145
+ if (contextRoutes.length === 0) {
2146
+ return /* @__PURE__ */ jsx18(
2147
+ "nav",
2148
+ {
2149
+ "aria-label": "Documentation navigation",
2150
+ className: cn("transition-all duration-200", className),
2151
+ children: /* @__PURE__ */ jsx18("ul", { className: "flex flex-col gap-0.5 py-4", children: menuRoutes.map((route) => /* @__PURE__ */ jsx18("li", { children: /* @__PURE__ */ jsx18(
2152
+ Sublink,
2153
+ {
2154
+ ...route,
2155
+ href: route.href,
2156
+ level: 0,
2157
+ onNavigate,
2158
+ parentHref: "/docs"
2159
+ }
2160
+ ) }, route.href)) })
2161
+ }
2162
+ );
2163
+ }
2164
+ return /* @__PURE__ */ jsx18(
2165
+ "nav",
2166
+ {
2167
+ "aria-label": "Documentation navigation",
2168
+ className: cn("transition-all duration-200", className),
2169
+ children: contextRoutes.map((route, i) => /* @__PURE__ */ jsxs14("div", { className: i > 0 ? "mt-6 lg:mt-8" : "", children: [
2170
+ /* @__PURE__ */ jsx18(
2171
+ SidebarGroupHeader,
2172
+ {
2173
+ icon: route.context?.icon,
2174
+ title: route.context?.title || route.title
2175
+ }
2176
+ ),
2177
+ /* @__PURE__ */ jsx18("ul", { className: "border-base-300 flex flex-col gap-0.5 border-l-2 pb-0.5 pl-3 pt-0.5", children: route.items?.map((item) => {
2178
+ const isActive = isItemActive(item.href, route.href);
2179
+ return /* @__PURE__ */ jsx18("li", { children: /* @__PURE__ */ jsx18(
2180
+ "div",
2181
+ {
2182
+ className: cn(
2183
+ "-ml-[14px] border-l-2",
2184
+ isActive ? "border-primary" : "border-transparent"
2185
+ ),
2186
+ children: /* @__PURE__ */ jsx18("div", { className: "pl-3", children: /* @__PURE__ */ jsx18(
2187
+ Sublink,
2188
+ {
2189
+ ...item,
2190
+ href: item.href,
2191
+ level: 0,
2192
+ onNavigate,
2193
+ parentHref: `/docs${route.href}`
2194
+ }
2195
+ ) })
2196
+ }
2197
+ ) }, item.href);
2198
+ }) })
2199
+ ] }, route.href))
2200
+ }
2201
+ );
2202
+ }
2203
+ const isDocsRoot = currentPath === "/docs" || currentPath === "/docs/";
2204
+ const currentContext = isDocsRoot ? menuRoutes[0]?.href.replace(/^\/+|\/+$/, "") : getCurrentContext(currentPath);
2205
+ const contextRoute = isDocsRoot && menuRoutes[0] ? currentContext ? getContextRoute(currentContext, menuRoutes) : menuRoutes[0] : currentContext ? getContextRoute(currentContext, menuRoutes) : void 0;
2206
+ if (!contextRoute) return null;
2207
+ return /* @__PURE__ */ jsx18(
2208
+ "nav",
2209
+ {
2210
+ "aria-label": "Documentation navigation",
2211
+ className: cn("transition-all duration-200", className),
2212
+ children: /* @__PURE__ */ jsx18("ul", { className: "flex flex-col gap-0.5 py-4", children: /* @__PURE__ */ jsx18("li", { children: /* @__PURE__ */ jsx18(
2213
+ Sublink,
2214
+ {
2215
+ ...contextRoute,
2216
+ href: contextRoute.href,
2217
+ level: 0,
2218
+ onNavigate,
2219
+ parentHref: "/docs"
2220
+ }
2221
+ ) }, contextRoute.title) })
2222
+ }
2223
+ );
2224
+ }
2225
+
2226
+ // .docu/components/DocsLayout.tsx
2227
+ var docuConfig5 = loadDocuConfig();
2228
+ function DocsLayout({ children, repoUrl, pathname = "/docs" }) {
2229
+ return React2.createElement(
2230
+ "div",
2231
+ { className: "docs-layout flex flex-col min-h-screen w-full" },
2232
+ React2.createElement(
2233
+ "div",
2234
+ { className: "flex flex-1 items-start w-full" },
2235
+ React2.createElement(
2236
+ "aside",
2237
+ {
2238
+ id: "sidebar-island",
2239
+ className: "sticky top-0 hidden h-screen w-[280px] shrink-0 flex-col lg:flex border-r border-base-200 bg-base-100",
2240
+ "data-tocs": "[]",
2241
+ "data-title": "",
2242
+ "data-repo": repoUrl || ""
2243
+ },
2244
+ // SSR sidebar content — Menu rendered server-side
2245
+ React2.createElement(
2246
+ "div",
2247
+ { className: "flex h-full flex-col overflow-y-auto px-4" },
2248
+ React2.createElement(Menu, { pathname, routes: docuConfig5.routes || [] })
2249
+ )
2250
+ ),
2251
+ React2.createElement(
2252
+ "main",
2253
+ { className: "flex-1 min-w-0 min-h-screen flex flex-col" },
2254
+ React2.createElement(
2255
+ "div",
2256
+ { className: "hidden lg:flex items-center justify-end gap-6 h-14 px-8" },
2257
+ React2.createElement(
2258
+ "nav",
2259
+ { className: "flex items-center gap-6 text-sm font-medium text-base-content/80" },
2260
+ ...(docuConfig5.navbar?.menu || []).map((item) => {
2261
+ const isExternal = /^https?:\/\//.test(item.href);
2262
+ const isDocsActive = item.href === "/docs";
2263
+ return React2.createElement(
2264
+ "a",
2265
+ {
2266
+ key: item.title,
2267
+ href: item.href,
2268
+ className: `flex items-center gap-1 hover:text-base-content transition-colors${isDocsActive ? " text-primary font-semibold" : ""}`,
2269
+ ...isExternal ? { target: "_blank", rel: "noopener noreferrer" } : {}
2270
+ },
2271
+ item.title,
2272
+ isExternal ? React2.createElement(
2273
+ "svg",
2274
+ {
2275
+ xmlns: "http://www.w3.org/2000/svg",
2276
+ width: "14",
2277
+ height: "14",
2278
+ viewBox: "0 0 24 24",
2279
+ fill: "none",
2280
+ stroke: "currentColor",
2281
+ strokeWidth: "2",
2282
+ strokeLinecap: "round",
2283
+ strokeLinejoin: "round"
2284
+ },
2285
+ React2.createElement("path", { d: "M7 7h10v10" }),
2286
+ React2.createElement("path", { d: "M7 17 17 7" })
2287
+ ) : null
2288
+ );
2289
+ })
2290
+ )
2291
+ ),
2292
+ React2.createElement("div", { className: "flex-1 w-full" }, children)
2293
+ )
2294
+ )
2295
+ );
2296
+ }
2297
+
2298
+ // .docu/node/escapeHtml.ts
2299
+ var ESCAPE_RE = /[&<>"']/g;
2300
+ var ESCAPE_MAP = {
2301
+ "&": "&amp;",
2302
+ "<": "&lt;",
2303
+ ">": "&gt;",
2304
+ '"': "&quot;",
2305
+ "'": "&#x27;"
2306
+ };
2307
+ function escapeHtml(input) {
2308
+ return input.replace(ESCAPE_RE, (ch) => ESCAPE_MAP[ch]);
2309
+ }
2310
+
2311
+ // .docu/node/html.shared.ts
2312
+ function htmlShell(opts) {
2313
+ const {
2314
+ title,
2315
+ description,
2316
+ body,
2317
+ favicon,
2318
+ css,
2319
+ js,
2320
+ nonce,
2321
+ csp,
2322
+ extraScripts,
2323
+ themeCss,
2324
+ depth = 0,
2325
+ headExtra,
2326
+ bodyExtra
2327
+ } = opts;
2328
+ const nonceAttr = nonce ? ` nonce="${escapeHtml(nonce)}"` : "";
2329
+ const themeStyle = themeCss ? `
2330
+ <style${nonceAttr}>${escapeHtml(themeCss)}</style>` : "";
2331
+ const headInjection = headExtra?.length ? `
2332
+ ${headExtra.join("\n ")}` : "";
2333
+ const bodyInjection = bodyExtra?.length ? `
2334
+ ${bodyExtra.join("\n ")}` : "";
2335
+ const depthPrefix = depth === 0 ? "" : "../".repeat(depth);
2336
+ const assetPrefix = depthPrefix + "assets/";
2337
+ const resolvePath = (path) => path.startsWith("/") ? depthPrefix + path.slice(1) : path;
2338
+ return `<!DOCTYPE html>
2339
+ <html lang="en">
2340
+ <head>
2341
+ <meta charset="UTF-8">
2342
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
2343
+ <title>${escapeHtml(title)}</title>
2344
+ <meta name="description" content="${escapeHtml(description)}">
2345
+ ${favicon ? `<link rel="icon" type="image/x-icon" href="${escapeHtml(resolvePath(favicon))}">` : ""}${themeStyle}
2346
+ <link rel="stylesheet" href="${escapeHtml(assetPrefix + css)}">
2347
+ ${csp ? `<meta http-equiv="Content-Security-Policy" content="${escapeHtml(csp)}">` : ""}
2348
+ <script${nonceAttr}>try{if(localStorage.getItem("theme")==="dark")document.documentElement.classList.add("dark")}catch(e){}</script>${headInjection}
2349
+ </head>
2350
+ <body>
2351
+ <div id="root">${body}</div>
2352
+ <script type="module"${nonceAttr} src="${escapeHtml(assetPrefix + js)}"></script>${extraScripts ? `
2353
+ ${extraScripts}` : ""}${bodyInjection}
2354
+ </body>
2355
+ </html>`;
2356
+ }
2357
+ function errorHtml(message, stack) {
2358
+ const msg = escapeHtml(message || "Unknown error");
2359
+ const st = escapeHtml(stack || "");
2360
+ return `<!DOCTYPE html>
2361
+ <html lang="en">
2362
+ <head>
2363
+ <meta charset="utf-8">
2364
+ <title>Server Error</title>
2365
+ <style>
2366
+ *{margin:0;padding:0;box-sizing:border-box}
2367
+ body{padding:2rem;font-family:ui-monospace,monospace;background:#1a1a2e;color:#e0e0e0}
2368
+ h1{color:#ff6b6b;font-size:1.5rem;margin-bottom:1rem}
2369
+ pre{background:#0d0d1a;border:1px solid #333;border-radius:8px;padding:1.5rem;overflow-x:auto;font-size:14px;line-height:1.6;white-space:pre-wrap;word-break:break-word}
2370
+ .msg{color:#ff6b6b;font-weight:bold}
2371
+ </style>
2372
+ </head>
2373
+ <body>
2374
+ <h1>\u{1F525} Server Error</h1>
2375
+ <pre><span class="msg">${msg}</span>${st ? `
2376
+
2377
+ ${st}` : ""}</pre>
2378
+ </body>
2379
+ </html>`;
2380
+ }
2381
+ function hmrScript(nonce) {
2382
+ return `<script nonce="${escapeHtml(nonce)}">
2383
+ (function(){
2384
+ const es = new EventSource("/__hmr");
2385
+ es.onmessage = function(e) {
2386
+ if (e.data === "reload") window.location.reload();
2387
+ };
2388
+ es.onerror = function() { es.close(); setTimeout(() => { window.location.reload(); }, 2000); };
2389
+ })();
2390
+ </script>`;
2391
+ }
2392
+
2393
+ export {
2394
+ loadPlugins,
2395
+ BuildPluginBuilder,
2396
+ computeInlineThemeCss,
2397
+ buildClientBundle,
2398
+ generateSearchIndex,
2399
+ initSentry,
2400
+ captureException,
2401
+ getGitLastModifiedBatch,
2402
+ compileMdx,
2403
+ DocsPage,
2404
+ NotFoundPage,
2405
+ IndexPage,
2406
+ DocsLayout,
2407
+ htmlShell,
2408
+ errorHtml,
2409
+ hmrScript
2410
+ };