boltdocs 2.8.3 → 2.9.0
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.
- package/bin/boltdocs.js +7 -6
- package/dist/{banner-MynZD_Ox.d.cts → banner-C_xkGxzI.d.cts} +1 -1
- package/dist/{banner-3N4Jd_L9.d.ts → banner-D2NddBfF.d.ts} +1 -1
- package/dist/cache-C2PiGyg8.cjs +6 -0
- package/dist/cache-DFW4R3S0.mjs +6 -0
- package/dist/chunk-Ds5LZdWN.cjs +6 -0
- package/dist/client/index.cjs +693 -488
- package/dist/client/index.d.cts +74 -8
- package/dist/client/index.d.ts +74 -8
- package/dist/client/index.js +692 -489
- package/dist/client/mdx.cjs +1 -1
- package/dist/client/mdx.js +1 -1
- package/dist/client/primitives.cjs +4 -4
- package/dist/client/primitives.d.cts +1 -1
- package/dist/client/primitives.d.ts +1 -1
- package/dist/client/primitives.js +4 -4
- package/dist/client/theme/neutral.css +5 -3
- package/dist/client/theme/reset.css +19 -0
- package/dist/{docs-layout-CwCq42Zt.cjs → docs-layout-3TpAt6MB.cjs} +302 -248
- package/dist/{docs-layout-Dn6S5g59.js → docs-layout-6FQ8__ok.js} +303 -249
- package/dist/doctor-BY1tSmQd.mjs +34 -0
- package/dist/{doctor-DyNUVe96.cjs → doctor-Bknon8f5.cjs} +1 -1
- package/dist/doctor-DcFLi4t1.cjs +34 -0
- package/dist/{doctor-aN_leTbh.mjs → doctor-wsPTzt8Y.mjs} +1 -1
- package/dist/{generator-CC2yHzhZ.cjs → generator-BEIF6MOx.cjs} +2 -2
- package/dist/{generator-BHCrLU6h.mjs → generator-DILs0EFN.mjs} +1 -1
- package/dist/handler-B31cxNw0.mjs +6 -0
- package/dist/handler-DfhH9D1b.mjs +28 -0
- package/dist/handler-Djy0HfsI.cjs +28 -0
- package/dist/{icons-dev-DvJ-hh9x.cjs → icons-dev-BryEFkJu.cjs} +96 -7
- package/dist/{icons-dev-Oju24Wjp.js → icons-dev-kJyGWNeg.js} +79 -8
- package/dist/{image-Ch4-GxdO.cjs → image-B9YA_oQ_.cjs} +24 -10
- package/dist/{image-Do8V9PCW.js → image-CjYu9Eml.js} +24 -10
- package/dist/{mdx-5oeCOFhH.js → mdx-Cy_c2zio.js} +116 -33
- package/dist/{mdx-BGM7LjW5.cjs → mdx-WXjAMS8Y.cjs} +121 -32
- package/dist/{meta-loader-Cv9O0Pzl.cjs → meta-loader-D0V6oOQt.cjs} +1 -1
- package/dist/node/cli-entry.cjs +3 -3
- package/dist/node/cli-entry.mjs +3 -3
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.d.cts +368 -24
- package/dist/node/index.d.mts +369 -24
- package/dist/node/index.mjs +1 -1
- package/dist/node/routes/worker.cjs +1 -1
- package/dist/node/routes/worker.mjs +1 -1
- package/dist/node-DV-EPtMU.mjs +160 -0
- package/dist/node-vOVNRABe.cjs +160 -0
- package/dist/{package-CHxJV4Jy.mjs → package-BK_82QLX.mjs} +1 -1
- package/dist/{package-B_SoaGeu.cjs → package-OxKE0p6j.cjs} +1 -1
- package/dist/{parser-B7-6PyQz.cjs → parser-C2Pw18LY.cjs} +1 -1
- package/dist/{parser-BzB-zCkF.cjs → parser-DJoEF96Y.cjs} +1 -1
- package/dist/{parser-D0PVghVt.mjs → parser-DScG_PTs.mjs} +1 -1
- package/dist/routes--LYHSHT7.cjs +6 -0
- package/dist/routes-BAsfsn_e.mjs +6 -0
- package/dist/routes-C6Kr2HFN.cjs +6 -0
- package/dist/routes-CKgGAy8P.mjs +6 -0
- package/dist/{search-dialog-BXVoecTx.cjs → search-dialog-BhXg05Ri.cjs} +1 -1
- package/dist/{search-dialog-BYhOov4S.cjs → search-dialog-BsAiQgnx.cjs} +2 -2
- package/dist/{search-dialog-C09riYmx.js → search-dialog-CaOO1Q4i.js} +2 -2
- package/dist/{search-dialog-CUeAfy-8.cjs → search-dialog-CzwvKoLW.cjs} +1 -1
- package/dist/{search-dialog-DHc_8FFX.js → search-dialog-DRNJsxOv.js} +1 -1
- package/dist/{search-dialog-D8gLkhUV.js → search-dialog-DaR66wl0.js} +1 -1
- package/dist/shiki-adapter-CUtj-xKv.cjs +6 -0
- package/dist/shiki-adapter-EZptJAmW.mjs +6 -0
- package/dist/shiki-adapter-F-P-lDon.mjs +6 -0
- package/dist/shiki-adapter-PQBl32AN.cjs +6 -0
- package/dist/{sidebar-Dlkgbxs6.d.cts → sidebar-BtJyWHy4.d.cts} +43 -4
- package/dist/{sidebar-DNq4_ZAa.d.ts → sidebar-C4fuRYYS.d.ts} +43 -4
- package/dist/{utils-BYITg7T5.mjs → utils-DC4Wq-S2.mjs} +1 -1
- package/dist/{utils-Cjmx1hhk.cjs → utils-wHud9ny5.cjs} +1 -1
- package/dist/worker-pool-BS1LX67P.cjs +6 -0
- package/dist/worker-pool-DRv8ef4z.mjs +6 -0
- package/package.json +7 -3
- package/src/shared/types.ts +36 -3
- package/dist/cache-BMUyNiiA.mjs +0 -6
- package/dist/cache-CKm45d2w.cjs +0 -6
- package/dist/chunk-CU-zTemE.cjs +0 -6
- package/dist/doctor-BArviV8X.cjs +0 -28
- package/dist/doctor-CgLA7_Uv.mjs +0 -28
- package/dist/node-MsTvOgJN.mjs +0 -159
- package/dist/node-h9HmhAFB.cjs +0 -159
- package/dist/routes-BW6Dif_K.mjs +0 -6
- package/dist/routes-DJNJ-rTt.cjs +0 -6
- package/dist/routes-DiYC4nD2.cjs +0 -6
- package/dist/routes-ERSGuFQK.mjs +0 -6
- package/dist/worker-pool-CtqklOXq.cjs +0 -6
- package/dist/worker-pool-k0DY6k8T.mjs +0 -6
- /package/dist/{meta-loader-DzwDFtdT.mjs → meta-loader-By22EnYa.mjs} +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright (c) 2026 Jesus Alcala
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
|
-
const require_icons_dev = require('./icons-dev-
|
|
6
|
+
const require_icons_dev = require('./icons-dev-BryEFkJu.cjs');
|
|
7
7
|
let react = require("react");
|
|
8
8
|
let react_router_dom = require("react-router-dom");
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -244,6 +244,235 @@ Tabs.Item = TabsItem;
|
|
|
244
244
|
Tabs.Content = TabsContent;
|
|
245
245
|
Tabs.Indicator = TabsIndicator;
|
|
246
246
|
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/client/hooks/use-sidebar.ts
|
|
249
|
+
const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1).replace(/-/g, " ");
|
|
250
|
+
const getCleanDirectoryMeta = (directoryMeta) => {
|
|
251
|
+
const meta = {};
|
|
252
|
+
if (!directoryMeta) return meta;
|
|
253
|
+
for (const [key, value] of Object.entries(directoryMeta)) {
|
|
254
|
+
const cleanKey = key.split("/").filter((part) => !part.startsWith("(") || !part.endsWith(")")).map((part) => part.replace(/^\d+-/, "")).join("/");
|
|
255
|
+
meta[cleanKey === "" ? "." : cleanKey] = value;
|
|
256
|
+
}
|
|
257
|
+
return meta;
|
|
258
|
+
};
|
|
259
|
+
const getOrCreateNode = (parts, rootMap, directoryMeta) => {
|
|
260
|
+
let currentMap = rootMap;
|
|
261
|
+
let parentPath = "";
|
|
262
|
+
let lastNode;
|
|
263
|
+
for (const segment of parts) {
|
|
264
|
+
const currentRelPath = parentPath ? `${parentPath}/${segment}` : segment;
|
|
265
|
+
if (!currentMap.has(segment)) {
|
|
266
|
+
const meta = directoryMeta[currentRelPath] || {};
|
|
267
|
+
const newNode = {
|
|
268
|
+
path: "#",
|
|
269
|
+
title: meta.title || capitalize(segment),
|
|
270
|
+
componentPath: "",
|
|
271
|
+
filePath: "",
|
|
272
|
+
icon: meta.icon,
|
|
273
|
+
groupPosition: typeof meta.order === "number" ? meta.order : 999,
|
|
274
|
+
collapsible: meta.collapsible,
|
|
275
|
+
collapsed: meta.collapsed,
|
|
276
|
+
hasCustomTitle: meta.title !== void 0,
|
|
277
|
+
subRoutes: [],
|
|
278
|
+
childrenMap: /* @__PURE__ */ new Map()
|
|
279
|
+
};
|
|
280
|
+
currentMap.set(segment, newNode);
|
|
281
|
+
}
|
|
282
|
+
lastNode = currentMap.get(segment);
|
|
283
|
+
currentMap = lastNode.childrenMap;
|
|
284
|
+
parentPath = currentRelPath;
|
|
285
|
+
}
|
|
286
|
+
return lastNode;
|
|
287
|
+
};
|
|
288
|
+
const getRoutePosition = (r) => r.sidebarPosition ?? r.order ?? 999;
|
|
289
|
+
const getNodePosition = (n) => n.sidebarPosition ?? n.groupPosition ?? 999;
|
|
290
|
+
const finalizeTree = (nodes) => {
|
|
291
|
+
return nodes.map((node) => {
|
|
292
|
+
if (node.childrenMap && node.childrenMap.size > 0) {
|
|
293
|
+
const childDirs = Array.from(node.childrenMap.values());
|
|
294
|
+
node.subRoutes = [...node.subRoutes || [], ...childDirs];
|
|
295
|
+
}
|
|
296
|
+
const { childrenMap, ...restNode } = node;
|
|
297
|
+
if (restNode.subRoutes && restNode.subRoutes.length > 0) restNode.subRoutes = finalizeTree(restNode.subRoutes);
|
|
298
|
+
return restNode;
|
|
299
|
+
}).sort((a, b) => {
|
|
300
|
+
const posA = getNodePosition(a);
|
|
301
|
+
const posB = getNodePosition(b);
|
|
302
|
+
return posA !== posB ? posA - posB : a.title.localeCompare(b.title);
|
|
303
|
+
});
|
|
304
|
+
};
|
|
305
|
+
function useSidebar(routes) {
|
|
306
|
+
const config = require_icons_dev.useConfig();
|
|
307
|
+
const { pathname } = (0, react_router_dom.useLocation)();
|
|
308
|
+
return (0, react.useMemo)(() => {
|
|
309
|
+
const currentPath = require_icons_dev.normalizePath(pathname);
|
|
310
|
+
const activeRoute = routes.find((r) => require_icons_dev.normalizePath(r.path) === currentPath);
|
|
311
|
+
const activeTabId = activeRoute?.tab?.toLowerCase();
|
|
312
|
+
const filteredRoutes = routes.filter((r) => !r.collection && !r.sidebarHidden && !r.fallback).filter((r) => !activeTabId || r.tab?.toLowerCase() === activeTabId).sort((a, b) => getRoutePosition(a) - getRoutePosition(b));
|
|
313
|
+
const directoryMeta = getCleanDirectoryMeta(config.directoryMeta);
|
|
314
|
+
const rootNodesMap = /* @__PURE__ */ new Map();
|
|
315
|
+
const ungrouped = [];
|
|
316
|
+
for (const route of filteredRoutes) {
|
|
317
|
+
const parts = route.slugParts || [];
|
|
318
|
+
const isIndex = /^index\.mdx?$/.test(route.filePath.split("/").pop() || "");
|
|
319
|
+
if (parts.length === 0) {
|
|
320
|
+
if (route.filePath) ungrouped.push(route);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
const containerNode = getOrCreateNode(parts, rootNodesMap, directoryMeta);
|
|
324
|
+
if (isIndex) Object.assign(containerNode, {
|
|
325
|
+
path: route.path,
|
|
326
|
+
title: containerNode.hasCustomTitle ? containerNode.title : route.title || containerNode.title,
|
|
327
|
+
icon: route.icon || containerNode.icon,
|
|
328
|
+
badge: route.badge,
|
|
329
|
+
sidebarPosition: route.sidebarPosition,
|
|
330
|
+
frontmatter: route.frontmatter,
|
|
331
|
+
filePath: route.filePath
|
|
332
|
+
});
|
|
333
|
+
else containerNode.subRoutes.push(route);
|
|
334
|
+
}
|
|
335
|
+
const finalizedTopNodes = finalizeTree(Array.from(rootNodesMap.values()));
|
|
336
|
+
const groups = [];
|
|
337
|
+
for (const node of finalizedTopNodes) if (node.subRoutes && node.subRoutes.length > 0) groups.push({
|
|
338
|
+
slug: node.title.toLowerCase().replace(/\s+/g, "-"),
|
|
339
|
+
title: node.title,
|
|
340
|
+
icon: node.icon,
|
|
341
|
+
path: node.path,
|
|
342
|
+
filePath: node.filePath,
|
|
343
|
+
routes: node.subRoutes,
|
|
344
|
+
sidebarPosition: node.sidebarPosition ?? node.groupPosition ?? 999,
|
|
345
|
+
collapsible: node.collapsible,
|
|
346
|
+
collapsed: node.collapsed
|
|
347
|
+
});
|
|
348
|
+
else ungrouped.push(node);
|
|
349
|
+
return {
|
|
350
|
+
groups,
|
|
351
|
+
ungrouped: finalizeTree(ungrouped),
|
|
352
|
+
activeRoute,
|
|
353
|
+
activePath: currentPath,
|
|
354
|
+
config
|
|
355
|
+
};
|
|
356
|
+
}, [
|
|
357
|
+
routes,
|
|
358
|
+
config,
|
|
359
|
+
pathname
|
|
360
|
+
]);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
//#endregion
|
|
364
|
+
//#region src/client/hooks/use-location.ts
|
|
365
|
+
const useLocation = () => {
|
|
366
|
+
return (0, react_router_dom.useLocation)();
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
//#endregion
|
|
370
|
+
//#region src/client/hooks/use-search-highlight.ts
|
|
371
|
+
/**
|
|
372
|
+
* Hook to highlight search terms based on the 'hl' query parameter.
|
|
373
|
+
*/
|
|
374
|
+
function useSearchHighlight(containerSelector = ".boltdocs-page") {
|
|
375
|
+
const { search } = useLocation();
|
|
376
|
+
const query = new URLSearchParams(search).get("hl");
|
|
377
|
+
(0, react.useEffect)(() => {
|
|
378
|
+
if (!query) {
|
|
379
|
+
clearHighlights(containerSelector);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const container = document.querySelector(containerSelector);
|
|
383
|
+
if (!container) return;
|
|
384
|
+
let rafId;
|
|
385
|
+
const observer = new MutationObserver((mutations) => {
|
|
386
|
+
if (mutations.some((m) => {
|
|
387
|
+
const addedNodes = Array.from(m.addedNodes);
|
|
388
|
+
const removedNodes = Array.from(m.removedNodes);
|
|
389
|
+
return addedNodes.some((n) => !(n instanceof HTMLElement && n.hasAttribute("data-search-highlight"))) || removedNodes.some((n) => !(n instanceof HTMLElement && n.hasAttribute("data-search-highlight")));
|
|
390
|
+
})) run();
|
|
391
|
+
});
|
|
392
|
+
function run() {
|
|
393
|
+
cancelAnimationFrame(rafId);
|
|
394
|
+
rafId = requestAnimationFrame(() => {
|
|
395
|
+
observer.disconnect();
|
|
396
|
+
clearHighlights(containerSelector);
|
|
397
|
+
const terms = query.split(/\s+/).map((t) => t.trim()).filter((t) => t.length >= 2);
|
|
398
|
+
if (terms.length > 0) highlightTerms(container, terms);
|
|
399
|
+
observer.observe(container, {
|
|
400
|
+
childList: true,
|
|
401
|
+
subtree: true
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
run();
|
|
406
|
+
return () => {
|
|
407
|
+
cancelAnimationFrame(rafId);
|
|
408
|
+
observer.disconnect();
|
|
409
|
+
clearHighlights(containerSelector);
|
|
410
|
+
};
|
|
411
|
+
}, [
|
|
412
|
+
query,
|
|
413
|
+
search,
|
|
414
|
+
containerSelector
|
|
415
|
+
]);
|
|
416
|
+
}
|
|
417
|
+
function clearHighlights(selector) {
|
|
418
|
+
document.querySelectorAll(`${selector} mark[data-search-highlight]`).forEach((mark) => {
|
|
419
|
+
try {
|
|
420
|
+
const parent = mark.parentNode;
|
|
421
|
+
if (parent && parent.contains(mark)) {
|
|
422
|
+
const text = mark.textContent || "";
|
|
423
|
+
parent.replaceChild(document.createTextNode(text), mark);
|
|
424
|
+
}
|
|
425
|
+
} catch (e) {}
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
function highlightTerms(container, terms) {
|
|
429
|
+
const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, { acceptNode: (node) => {
|
|
430
|
+
const parent = node.parentElement;
|
|
431
|
+
if (parent && (parent.tagName === "SCRIPT" || parent.tagName === "STYLE" || parent.tagName === "MARK" || parent.closest("pre") || parent.closest("code"))) return NodeFilter.FILTER_REJECT;
|
|
432
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
433
|
+
} });
|
|
434
|
+
const nodes = [];
|
|
435
|
+
let node;
|
|
436
|
+
while (node = walker.nextNode()) nodes.push(node);
|
|
437
|
+
const accentMap = {
|
|
438
|
+
a: "[aáàäâã]",
|
|
439
|
+
e: "[eéèëê]",
|
|
440
|
+
i: "[iíìïî]",
|
|
441
|
+
o: "[oóòöôõ]",
|
|
442
|
+
u: "[uúùüû]",
|
|
443
|
+
n: "[nñ]",
|
|
444
|
+
c: "[cç]"
|
|
445
|
+
};
|
|
446
|
+
const prepareRegex = (term) => {
|
|
447
|
+
let pattern = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
448
|
+
pattern = pattern.split("").map((char) => {
|
|
449
|
+
return accentMap[char.toLowerCase()] || char;
|
|
450
|
+
}).join("");
|
|
451
|
+
return pattern;
|
|
452
|
+
};
|
|
453
|
+
const combinedPattern = terms.map(prepareRegex).join("|");
|
|
454
|
+
const regex = new RegExp(`(${combinedPattern})`, "gi");
|
|
455
|
+
const matchRegexes = terms.map((term) => {
|
|
456
|
+
const p = prepareRegex(term);
|
|
457
|
+
return new RegExp(`^${p}$`, "i");
|
|
458
|
+
});
|
|
459
|
+
nodes.forEach((textNode) => {
|
|
460
|
+
const text = textNode.textContent;
|
|
461
|
+
if (text && regex.test(text)) {
|
|
462
|
+
const fragment = document.createDocumentFragment();
|
|
463
|
+
text.split(regex).forEach((part) => {
|
|
464
|
+
if (matchRegexes.some((rx) => rx.test(part))) {
|
|
465
|
+
const mark = document.createElement("mark");
|
|
466
|
+
mark.textContent = part;
|
|
467
|
+
mark.setAttribute("data-search-highlight", "true");
|
|
468
|
+
fragment.appendChild(mark);
|
|
469
|
+
} else if (part) fragment.appendChild(document.createTextNode(part));
|
|
470
|
+
});
|
|
471
|
+
if (textNode.parentNode) textNode.parentNode.replaceChild(fragment, textNode);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
247
476
|
//#endregion
|
|
248
477
|
//#region src/client/components/primitives/helpers/observer.ts
|
|
249
478
|
function getItemId(url) {
|
|
@@ -619,126 +848,6 @@ PageNav.Title = PageNavTitle;
|
|
|
619
848
|
PageNav.Description = PageNavDescription;
|
|
620
849
|
PageNav.Icon = PageNavIcon;
|
|
621
850
|
|
|
622
|
-
//#endregion
|
|
623
|
-
//#region src/client/hooks/use-sidebar.ts
|
|
624
|
-
function useSidebar(routes) {
|
|
625
|
-
const config = require_icons_dev.useConfig();
|
|
626
|
-
const currentPath = require_icons_dev.normalizePath((0, react_router_dom.useLocation)().pathname);
|
|
627
|
-
return (0, react.useMemo)(() => {
|
|
628
|
-
const activeRoute = routes.find((r) => require_icons_dev.normalizePath(r.path) === currentPath);
|
|
629
|
-
const activeTabId = activeRoute?.tab?.toLowerCase();
|
|
630
|
-
const noCollection = routes.filter((r) => !r.collection);
|
|
631
|
-
const filteredRoutes = activeTabId ? noCollection.filter((r) => !r.tab || r.tab.toLowerCase() === activeTabId) : noCollection;
|
|
632
|
-
const directoryMeta = {};
|
|
633
|
-
if (config.directoryMeta) for (const [key, value] of Object.entries(config.directoryMeta)) {
|
|
634
|
-
const cleanKey = key.split("/").filter((part) => !part.startsWith("(") || !part.endsWith(")")).map((part) => part.replace(/^\d+-/, "")).join("/");
|
|
635
|
-
directoryMeta[cleanKey === "" ? "." : cleanKey] = value;
|
|
636
|
-
}
|
|
637
|
-
const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1).replace(/-/g, " ");
|
|
638
|
-
const rootNodesMap = /* @__PURE__ */ new Map();
|
|
639
|
-
const ungrouped = [];
|
|
640
|
-
const getOrCreateNode = (parts, rootStore) => {
|
|
641
|
-
let currentMap = rootStore;
|
|
642
|
-
let parentPath = "";
|
|
643
|
-
let lastNode = null;
|
|
644
|
-
for (let i = 0; i < parts.length; i++) {
|
|
645
|
-
const segment = parts[i];
|
|
646
|
-
const currentRelPath = parentPath ? `${parentPath}/${segment}` : segment;
|
|
647
|
-
if (!currentMap.has(segment)) {
|
|
648
|
-
const meta = directoryMeta[currentRelPath] || {};
|
|
649
|
-
const newNode = {
|
|
650
|
-
path: "#",
|
|
651
|
-
title: meta.title || capitalize(segment),
|
|
652
|
-
componentPath: "",
|
|
653
|
-
filePath: "",
|
|
654
|
-
icon: meta.icon,
|
|
655
|
-
groupPosition: typeof meta.order === "number" ? meta.order : 999,
|
|
656
|
-
subRoutes: []
|
|
657
|
-
};
|
|
658
|
-
currentMap.set(segment, newNode);
|
|
659
|
-
}
|
|
660
|
-
lastNode = currentMap.get(segment);
|
|
661
|
-
if (!lastNode._subMap) lastNode._subMap = /* @__PURE__ */ new Map();
|
|
662
|
-
currentMap = lastNode._subMap;
|
|
663
|
-
parentPath = currentRelPath;
|
|
664
|
-
}
|
|
665
|
-
return lastNode;
|
|
666
|
-
};
|
|
667
|
-
const sortedRoutes = [...filteredRoutes].sort((a, b) => {
|
|
668
|
-
return (a.sidebarPosition ?? a.order ?? 999) - (b.sidebarPosition ?? b.order ?? 999);
|
|
669
|
-
});
|
|
670
|
-
for (const route of sortedRoutes) {
|
|
671
|
-
if (route.sidebarHidden) continue;
|
|
672
|
-
const parts = route.slugParts || [];
|
|
673
|
-
const fileName = route.filePath.split("/").pop() || "";
|
|
674
|
-
const isIndex = /^index\.mdx?$/.test(fileName);
|
|
675
|
-
if (parts.length === 0) {
|
|
676
|
-
if (route.filePath) ungrouped.push(route);
|
|
677
|
-
continue;
|
|
678
|
-
}
|
|
679
|
-
if (isIndex) {
|
|
680
|
-
const containerNode = getOrCreateNode(parts, rootNodesMap);
|
|
681
|
-
if (containerNode) {
|
|
682
|
-
containerNode.path = route.path;
|
|
683
|
-
containerNode.title = route.title || containerNode.title;
|
|
684
|
-
containerNode.icon = route.icon || containerNode.icon;
|
|
685
|
-
containerNode.badge = route.badge;
|
|
686
|
-
containerNode.sidebarPosition = route.sidebarPosition;
|
|
687
|
-
containerNode.frontmatter = route.frontmatter;
|
|
688
|
-
}
|
|
689
|
-
} else {
|
|
690
|
-
const parentNode = getOrCreateNode(parts, rootNodesMap);
|
|
691
|
-
if (parentNode) parentNode.subRoutes.push(route);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
const finalizeTree = (nodes, currentPathPrefix = "") => {
|
|
695
|
-
nodes.forEach((node) => {
|
|
696
|
-
if (node._subMap) {
|
|
697
|
-
const childDirs = Array.from(node._subMap.values());
|
|
698
|
-
node.subRoutes = [...node.subRoutes || [], ...childDirs];
|
|
699
|
-
delete node._subMap;
|
|
700
|
-
}
|
|
701
|
-
if (node.subRoutes && node.subRoutes.length > 0) node.subRoutes = finalizeTree(node.subRoutes);
|
|
702
|
-
});
|
|
703
|
-
return nodes.sort((a, b) => {
|
|
704
|
-
const posA = a.sidebarPosition ?? a.groupPosition ?? 999;
|
|
705
|
-
const posB = b.sidebarPosition ?? b.groupPosition ?? 999;
|
|
706
|
-
if (posA !== posB) return posA - posB;
|
|
707
|
-
return a.title.localeCompare(b.title);
|
|
708
|
-
});
|
|
709
|
-
};
|
|
710
|
-
const finalizedTopNodes = finalizeTree(Array.from(rootNodesMap.values()));
|
|
711
|
-
finalizedTopNodes.map((node) => {
|
|
712
|
-
return {
|
|
713
|
-
slug: node.title.toLowerCase().replace(/\s+/g, "-"),
|
|
714
|
-
title: node.title,
|
|
715
|
-
icon: node.icon,
|
|
716
|
-
routes: [node]
|
|
717
|
-
};
|
|
718
|
-
});
|
|
719
|
-
return {
|
|
720
|
-
groups: finalizedTopNodes.map((node) => {
|
|
721
|
-
if (node.subRoutes && node.subRoutes.length > 0) return {
|
|
722
|
-
slug: node.title.toLowerCase().replace(/\s+/g, "-"),
|
|
723
|
-
title: node.title,
|
|
724
|
-
icon: node.icon,
|
|
725
|
-
routes: node.subRoutes
|
|
726
|
-
};
|
|
727
|
-
ungrouped.push(node);
|
|
728
|
-
return null;
|
|
729
|
-
}).filter(Boolean),
|
|
730
|
-
ungrouped: finalizeTree(ungrouped),
|
|
731
|
-
activeRoute,
|
|
732
|
-
activePath: currentPath,
|
|
733
|
-
config
|
|
734
|
-
};
|
|
735
|
-
}, [
|
|
736
|
-
routes,
|
|
737
|
-
config,
|
|
738
|
-
currentPath
|
|
739
|
-
]);
|
|
740
|
-
}
|
|
741
|
-
|
|
742
851
|
//#endregion
|
|
743
852
|
//#region src/client/components/primitives/sidebar.tsx
|
|
744
853
|
let sidebarScrollPos = 0;
|
|
@@ -832,13 +941,29 @@ function SidebarContent({ children, className }) {
|
|
|
832
941
|
/**
|
|
833
942
|
* Navigation Group
|
|
834
943
|
*/
|
|
835
|
-
function SidebarGroup({ title, icon: Icon, children, className }) {
|
|
944
|
+
function SidebarGroup({ title, icon: Icon, children, className, collapsible = false, collapsed = false, active = false }) {
|
|
945
|
+
const [isOpen, setIsOpen] = (0, react.useState)(() => active || !collapsed);
|
|
946
|
+
const [prevActive, setPrevActive] = (0, react.useState)(active);
|
|
947
|
+
if (active !== prevActive) {
|
|
948
|
+
setPrevActive(active);
|
|
949
|
+
if (active) setIsOpen(true);
|
|
950
|
+
}
|
|
836
951
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
837
952
|
className,
|
|
838
|
-
children: [title && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
953
|
+
children: [title && (collapsible ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
954
|
+
onClick: () => setIsOpen(!isOpen),
|
|
955
|
+
className: "w-full text-left px-2 mb-2 flex items-center justify-between text-xs font-regular tracking-widest text-muted/50 hover:text-body transition-colors outline-none cursor-pointer group",
|
|
956
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
957
|
+
className: "flex items-center gap-2",
|
|
958
|
+
children: [Icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { size: 12 }), title]
|
|
959
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_icons_dev.ChevronRight, {
|
|
960
|
+
size: 12,
|
|
961
|
+
className: require_icons_dev.cn("transition-transform duration-200 text-muted/40 group-hover:text-body", isOpen && "rotate-90")
|
|
962
|
+
})]
|
|
963
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h4", {
|
|
839
964
|
className: "px-2 mb-2 flex items-center gap-2 text-[11px] font-bold uppercase tracking-widest text-muted/50",
|
|
840
965
|
children: [Icon && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { size: 12 }), title]
|
|
841
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
966
|
+
})), (!collapsible || isOpen) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
842
967
|
className: "flex flex-col gap-0.5",
|
|
843
968
|
children
|
|
844
969
|
})]
|
|
@@ -929,23 +1054,65 @@ function SidebarItem({ route, activePath, activeRoute, className }) {
|
|
|
929
1054
|
className
|
|
930
1055
|
});
|
|
931
1056
|
}
|
|
1057
|
+
const isRouteActive = (route, activePath, activeRoute) => {
|
|
1058
|
+
const normalizedPath = route.path.endsWith("/") ? route.path.slice(0, -1) : route.path;
|
|
1059
|
+
if ((activePath.endsWith("/") ? activePath.slice(0, -1) : activePath) === normalizedPath) return true;
|
|
1060
|
+
if (activeRoute?.filePath && route.filePath && activeRoute.filePath === route.filePath) return true;
|
|
1061
|
+
if (route.routes && route.routes.some((r) => isRouteActive(r, activePath, activeRoute))) return true;
|
|
1062
|
+
if (route.subRoutes && route.subRoutes.some((r) => isRouteActive(r, activePath, activeRoute))) return true;
|
|
1063
|
+
return false;
|
|
1064
|
+
};
|
|
932
1065
|
function SidebarItems({ routes, className }) {
|
|
933
1066
|
const { groups, ungrouped, activePath, activeRoute } = useSidebar(routes);
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1067
|
+
const mergedItems = [...ungrouped.map((route) => ({
|
|
1068
|
+
type: "link",
|
|
1069
|
+
position: route.sidebarPosition ?? 999,
|
|
1070
|
+
title: route.title,
|
|
1071
|
+
route
|
|
1072
|
+
})), ...groups.map((group) => ({
|
|
1073
|
+
type: "group",
|
|
1074
|
+
position: group.sidebarPosition ?? 999,
|
|
1075
|
+
title: group.title,
|
|
1076
|
+
group
|
|
1077
|
+
}))].sort((a, b) => {
|
|
1078
|
+
if (a.position !== b.position) return a.position - b.position;
|
|
1079
|
+
if (a.type !== b.type) return a.type === "link" ? -1 : 1;
|
|
1080
|
+
return a.title.localeCompare(b.title);
|
|
1081
|
+
});
|
|
1082
|
+
const renderedElements = [];
|
|
1083
|
+
let currentUngrouped = [];
|
|
1084
|
+
const pushUngrouped = () => {
|
|
1085
|
+
if (currentUngrouped.length > 0) {
|
|
1086
|
+
const routesToRender = [...currentUngrouped];
|
|
1087
|
+
renderedElements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarGroup, { children: routesToRender.map((route) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarItem, {
|
|
1088
|
+
route,
|
|
1089
|
+
activePath,
|
|
1090
|
+
activeRoute
|
|
1091
|
+
}, route.path)) }, `ungrouped-${routesToRender[0].path}`));
|
|
1092
|
+
currentUngrouped = [];
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
for (const item of mergedItems) if (item.type === "link") currentUngrouped.push(item.route);
|
|
1096
|
+
else {
|
|
1097
|
+
pushUngrouped();
|
|
1098
|
+
const isGroupActive = item.group.routes.some((route) => isRouteActive(route, activePath, activeRoute));
|
|
1099
|
+
renderedElements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarGroup, {
|
|
1100
|
+
title: item.group.title,
|
|
1101
|
+
icon: getIcon(item.group.icon),
|
|
1102
|
+
collapsible: item.group.collapsible,
|
|
1103
|
+
collapsed: item.group.collapsed,
|
|
1104
|
+
active: isGroupActive,
|
|
1105
|
+
children: item.group.routes.map((route) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarItem, {
|
|
944
1106
|
route,
|
|
945
1107
|
activePath,
|
|
946
1108
|
activeRoute
|
|
947
1109
|
}, route.path))
|
|
948
|
-
}, group.title))
|
|
1110
|
+
}, item.group.title));
|
|
1111
|
+
}
|
|
1112
|
+
pushUngrouped();
|
|
1113
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1114
|
+
className: require_icons_dev.cn("flex flex-col gap-6", className),
|
|
1115
|
+
children: renderedElements
|
|
949
1116
|
});
|
|
950
1117
|
}
|
|
951
1118
|
/**
|
|
@@ -963,119 +1130,6 @@ const Sidebar = Object.assign(SidebarRoot, {
|
|
|
963
1130
|
Items: SidebarItems
|
|
964
1131
|
});
|
|
965
1132
|
|
|
966
|
-
//#endregion
|
|
967
|
-
//#region src/client/hooks/use-location.ts
|
|
968
|
-
const useLocation = () => {
|
|
969
|
-
return (0, react_router_dom.useLocation)();
|
|
970
|
-
};
|
|
971
|
-
|
|
972
|
-
//#endregion
|
|
973
|
-
//#region src/client/hooks/use-search-highlight.ts
|
|
974
|
-
/**
|
|
975
|
-
* Hook to highlight search terms based on the 'hl' query parameter.
|
|
976
|
-
*/
|
|
977
|
-
function useSearchHighlight(containerSelector = ".boltdocs-page") {
|
|
978
|
-
const { search } = useLocation();
|
|
979
|
-
const query = new URLSearchParams(search).get("hl");
|
|
980
|
-
(0, react.useEffect)(() => {
|
|
981
|
-
if (!query) {
|
|
982
|
-
clearHighlights(containerSelector);
|
|
983
|
-
return;
|
|
984
|
-
}
|
|
985
|
-
const container = document.querySelector(containerSelector);
|
|
986
|
-
if (!container) return;
|
|
987
|
-
let rafId;
|
|
988
|
-
const observer = new MutationObserver((mutations) => {
|
|
989
|
-
if (mutations.some((m) => {
|
|
990
|
-
const addedNodes = Array.from(m.addedNodes);
|
|
991
|
-
const removedNodes = Array.from(m.removedNodes);
|
|
992
|
-
return addedNodes.some((n) => !(n instanceof HTMLElement && n.hasAttribute("data-search-highlight"))) || removedNodes.some((n) => !(n instanceof HTMLElement && n.hasAttribute("data-search-highlight")));
|
|
993
|
-
})) run();
|
|
994
|
-
});
|
|
995
|
-
function run() {
|
|
996
|
-
cancelAnimationFrame(rafId);
|
|
997
|
-
rafId = requestAnimationFrame(() => {
|
|
998
|
-
observer.disconnect();
|
|
999
|
-
clearHighlights(containerSelector);
|
|
1000
|
-
const terms = query.split(/\s+/).map((t) => t.trim()).filter((t) => t.length >= 2);
|
|
1001
|
-
if (terms.length > 0) highlightTerms(container, terms);
|
|
1002
|
-
observer.observe(container, {
|
|
1003
|
-
childList: true,
|
|
1004
|
-
subtree: true
|
|
1005
|
-
});
|
|
1006
|
-
});
|
|
1007
|
-
}
|
|
1008
|
-
run();
|
|
1009
|
-
return () => {
|
|
1010
|
-
cancelAnimationFrame(rafId);
|
|
1011
|
-
observer.disconnect();
|
|
1012
|
-
clearHighlights(containerSelector);
|
|
1013
|
-
};
|
|
1014
|
-
}, [
|
|
1015
|
-
query,
|
|
1016
|
-
search,
|
|
1017
|
-
containerSelector
|
|
1018
|
-
]);
|
|
1019
|
-
}
|
|
1020
|
-
function clearHighlights(selector) {
|
|
1021
|
-
document.querySelectorAll(`${selector} mark[data-search-highlight]`).forEach((mark) => {
|
|
1022
|
-
try {
|
|
1023
|
-
const parent = mark.parentNode;
|
|
1024
|
-
if (parent && parent.contains(mark)) {
|
|
1025
|
-
const text = mark.textContent || "";
|
|
1026
|
-
parent.replaceChild(document.createTextNode(text), mark);
|
|
1027
|
-
}
|
|
1028
|
-
} catch (e) {}
|
|
1029
|
-
});
|
|
1030
|
-
}
|
|
1031
|
-
function highlightTerms(container, terms) {
|
|
1032
|
-
const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, { acceptNode: (node) => {
|
|
1033
|
-
const parent = node.parentElement;
|
|
1034
|
-
if (parent && (parent.tagName === "SCRIPT" || parent.tagName === "STYLE" || parent.tagName === "MARK" || parent.closest("pre") || parent.closest("code"))) return NodeFilter.FILTER_REJECT;
|
|
1035
|
-
return NodeFilter.FILTER_ACCEPT;
|
|
1036
|
-
} });
|
|
1037
|
-
const nodes = [];
|
|
1038
|
-
let node;
|
|
1039
|
-
while (node = walker.nextNode()) nodes.push(node);
|
|
1040
|
-
const accentMap = {
|
|
1041
|
-
a: "[aáàäâã]",
|
|
1042
|
-
e: "[eéèëê]",
|
|
1043
|
-
i: "[iíìïî]",
|
|
1044
|
-
o: "[oóòöôõ]",
|
|
1045
|
-
u: "[uúùüû]",
|
|
1046
|
-
n: "[nñ]",
|
|
1047
|
-
c: "[cç]"
|
|
1048
|
-
};
|
|
1049
|
-
const prepareRegex = (term) => {
|
|
1050
|
-
let pattern = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1051
|
-
pattern = pattern.split("").map((char) => {
|
|
1052
|
-
return accentMap[char.toLowerCase()] || char;
|
|
1053
|
-
}).join("");
|
|
1054
|
-
return pattern;
|
|
1055
|
-
};
|
|
1056
|
-
const combinedPattern = terms.map(prepareRegex).join("|");
|
|
1057
|
-
const regex = new RegExp(`(${combinedPattern})`, "gi");
|
|
1058
|
-
const matchRegexes = terms.map((term) => {
|
|
1059
|
-
const p = prepareRegex(term);
|
|
1060
|
-
return new RegExp(`^${p}$`, "i");
|
|
1061
|
-
});
|
|
1062
|
-
nodes.forEach((textNode) => {
|
|
1063
|
-
const text = textNode.textContent;
|
|
1064
|
-
if (text && regex.test(text)) {
|
|
1065
|
-
const fragment = document.createDocumentFragment();
|
|
1066
|
-
text.split(regex).forEach((part) => {
|
|
1067
|
-
if (matchRegexes.some((rx) => rx.test(part))) {
|
|
1068
|
-
const mark = document.createElement("mark");
|
|
1069
|
-
mark.textContent = part;
|
|
1070
|
-
mark.setAttribute("data-search-highlight", "true");
|
|
1071
|
-
fragment.appendChild(mark);
|
|
1072
|
-
} else if (part) fragment.appendChild(document.createTextNode(part));
|
|
1073
|
-
});
|
|
1074
|
-
if (textNode.parentNode) textNode.parentNode.replaceChild(fragment, textNode);
|
|
1075
|
-
}
|
|
1076
|
-
});
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
1133
|
//#endregion
|
|
1080
1134
|
//#region src/client/components/ui-base/search-highlight.tsx
|
|
1081
1135
|
/**
|