nimbus-docs 0.1.11 → 0.1.12
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/dist/cli/index.js +8 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/content.d.ts +3 -1
- package/dist/content.d.ts.map +1 -1
- package/dist/index.d.ts +99 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +409 -23
- package/dist/index.js.map +1 -1
- package/dist/schemas.d.ts +4 -1
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +1 -0
- package/dist/schemas.js.map +1 -1
- package/dist/types.d.ts +60 -10
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -8
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { satteri } from "@astrojs/markdown-satteri";
|
|
|
11
11
|
import sitemap from "@astrojs/sitemap";
|
|
12
12
|
import fs$1, { cp, mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
13
13
|
import { z } from "astro/zod";
|
|
14
|
-
import {
|
|
14
|
+
import { transformerNotationDiff, transformerNotationErrorLevel, transformerNotationFocus, transformerNotationHighlight, transformerNotationWordHighlight } from "@shikijs/transformers";
|
|
15
15
|
import { createHash } from "node:crypto";
|
|
16
16
|
|
|
17
17
|
//#region src/_internal/runtime-config.ts
|
|
@@ -419,6 +419,13 @@ function resolveConfigItems(configItems, entriesByCollection, primaryCollection,
|
|
|
419
419
|
badge: item.badge,
|
|
420
420
|
children
|
|
421
421
|
};
|
|
422
|
+
const landing = item.landing;
|
|
423
|
+
const segment = item.segment;
|
|
424
|
+
if (segment !== void 0) group.segment = segment;
|
|
425
|
+
if (landing !== void 0) {
|
|
426
|
+
group.indexHref = toBrowserHref(landing);
|
|
427
|
+
group.indexIsCurrent = toRouteKey(currentPath) === toRouteKey(landing) || void 0;
|
|
428
|
+
}
|
|
422
429
|
result.push(group);
|
|
423
430
|
}
|
|
424
431
|
}
|
|
@@ -459,6 +466,70 @@ function scopeToCurrentSection(items, currentPath) {
|
|
|
459
466
|
}
|
|
460
467
|
return items;
|
|
461
468
|
}
|
|
469
|
+
/**
|
|
470
|
+
* Return the ancestor chain from the top of the tree to the node that owns
|
|
471
|
+
* `currentPath` (inclusive). Matches by route key, so a chain can be
|
|
472
|
+
* resolved for any path (e.g. a catalog route's section) regardless of the
|
|
473
|
+
* page the tree was built for. A group that only *contains* the match is
|
|
474
|
+
* included with no href (a non-interactive crumb). Returns `[]` on no match.
|
|
475
|
+
*/
|
|
476
|
+
function findActivePath(items, currentPath) {
|
|
477
|
+
const key = toRouteKey(currentPath);
|
|
478
|
+
function search(nodes) {
|
|
479
|
+
for (const item of nodes) if (item.type === "link") {
|
|
480
|
+
if (toRouteKey(item.href) === key) return [item];
|
|
481
|
+
} else if (item.type === "group") {
|
|
482
|
+
if (item.indexHref && !item.indexIsExternal && toRouteKey(item.indexHref) === key) return [item];
|
|
483
|
+
const childPath = search(item.children);
|
|
484
|
+
if (childPath) return [item, ...childPath];
|
|
485
|
+
}
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
return search(items) ?? [];
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Descend a section-scoped tree to the sub-tree under the current path's
|
|
492
|
+
* boundary. A glob like `"guides/*"` (`*` = one segment) sets the prefix
|
|
493
|
+
* depth; the rail is replaced by the children of the shallowest group
|
|
494
|
+
* fully contained under that prefix. Matching is by descendant href, so it
|
|
495
|
+
* works for index-less section folders. Returns the input unchanged on no
|
|
496
|
+
* match.
|
|
497
|
+
*/
|
|
498
|
+
function isolateToBoundary(items, currentPath, boundaries) {
|
|
499
|
+
const segs = toRouteKey(currentPath).split("/").filter(Boolean);
|
|
500
|
+
for (const glob of boundaries) {
|
|
501
|
+
const globSegs = glob.split("/").filter(Boolean);
|
|
502
|
+
if (segs.length < globSegs.length || globSegs.length === 0) continue;
|
|
503
|
+
if (!globSegs.every((g, i) => g === "*" || g === segs[i])) continue;
|
|
504
|
+
const group = findBoundaryGroup(items, toBrowserHref("/" + segs.slice(0, globSegs.length).join("/")));
|
|
505
|
+
if (group) return group.children;
|
|
506
|
+
}
|
|
507
|
+
return items;
|
|
508
|
+
}
|
|
509
|
+
/** Shallowest group whose every flattened descendant href is under `prefix`. */
|
|
510
|
+
function findBoundaryGroup(items, prefix) {
|
|
511
|
+
for (const item of items) {
|
|
512
|
+
if (item.type !== "group") continue;
|
|
513
|
+
const flat = flattenSidebar([item]);
|
|
514
|
+
if (flat.length > 0 && flat.every((l) => l.href.startsWith(prefix))) return item;
|
|
515
|
+
const nested = findBoundaryGroup(item.children, prefix);
|
|
516
|
+
if (nested) return nested;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Context for the `getSidebar` transform: `sectionSlug` (seg0), `module`
|
|
521
|
+
* (seg1), and `indexEntryId` — the landing entry id of the first group on
|
|
522
|
+
* the active path, or `undefined` for an index-less section.
|
|
523
|
+
*/
|
|
524
|
+
function deriveTransformCtx(fullTree, currentSlug) {
|
|
525
|
+
const segs = currentSlug.split("/").filter(Boolean);
|
|
526
|
+
const sectionGroup = findActivePath(fullTree, currentSlug).find((n) => n.type === "group");
|
|
527
|
+
return {
|
|
528
|
+
sectionSlug: segs[0] ?? "",
|
|
529
|
+
module: segs[1],
|
|
530
|
+
indexEntryId: sectionGroup?._indexId
|
|
531
|
+
};
|
|
532
|
+
}
|
|
462
533
|
function hasActivePage(item, currentPath) {
|
|
463
534
|
if (item.type === "link") return item.isCurrent === true;
|
|
464
535
|
if (item.type === "external") return false;
|
|
@@ -581,7 +652,8 @@ function processHideChildren(items, entries) {
|
|
|
581
652
|
continue;
|
|
582
653
|
}
|
|
583
654
|
if (item._indexId && item.indexHref) {
|
|
584
|
-
|
|
655
|
+
const entry = entryById.get(item._indexId);
|
|
656
|
+
if (entry?.data.sidebar?.hideChildren || entry?.data.hideChildren) {
|
|
585
657
|
const replacement = item.indexIsExternal ? {
|
|
586
658
|
type: "external",
|
|
587
659
|
label: item.label,
|
|
@@ -871,7 +943,68 @@ function renderEntryAsMarkdown(entry, options = {}) {
|
|
|
871
943
|
|
|
872
944
|
//#endregion
|
|
873
945
|
//#region src/_internal/navigation.ts
|
|
874
|
-
|
|
946
|
+
/**
|
|
947
|
+
* The in-site href a node links to, or `undefined` for a non-interactive
|
|
948
|
+
* crumb (index-less groups and off-site landings).
|
|
949
|
+
*/
|
|
950
|
+
function nodeHref(node) {
|
|
951
|
+
if (node.type === "link") return node.href;
|
|
952
|
+
if (node.type === "external") return void 0;
|
|
953
|
+
return node.indexIsExternal ? void 0 : node.indexHref;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Build the trail from the root crumb and per-node labels. `labels[i]`
|
|
957
|
+
* pairs with `path[i]`: `null` drops the crumb, `undefined` keeps the
|
|
958
|
+
* node label. Deduplicated by href (first wins); hrefless crumbs are
|
|
959
|
+
* never merged.
|
|
960
|
+
*/
|
|
961
|
+
function assembleBreadcrumbs(root, path, labels) {
|
|
962
|
+
const crumbs = [{
|
|
963
|
+
label: root.label,
|
|
964
|
+
href: root.href
|
|
965
|
+
}];
|
|
966
|
+
path.forEach((node, i) => {
|
|
967
|
+
const override = labels[i];
|
|
968
|
+
if (override === null) return;
|
|
969
|
+
const label = override ?? node.label;
|
|
970
|
+
const href = nodeHref(node);
|
|
971
|
+
crumbs.push(href ? {
|
|
972
|
+
label,
|
|
973
|
+
href
|
|
974
|
+
} : { label });
|
|
975
|
+
});
|
|
976
|
+
const seen = /* @__PURE__ */ new Set();
|
|
977
|
+
return crumbs.filter((c) => {
|
|
978
|
+
if (c.href === void 0) return true;
|
|
979
|
+
if (seen.has(c.href)) return false;
|
|
980
|
+
seen.add(c.href);
|
|
981
|
+
return true;
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* Append `trail` items to a section's ancestry crumbs (a leaf with no
|
|
986
|
+
* `href` is the current page) and deduplicate by href, so a trail crumb
|
|
987
|
+
* that repeats an ancestry crumb's URL collapses to one (first wins). The
|
|
988
|
+
* pure core of `getRouteNavigation`.
|
|
989
|
+
*/
|
|
990
|
+
function composeRouteBreadcrumbs(sectionCrumbs, trail) {
|
|
991
|
+
const combined = [...sectionCrumbs, ...trail.map((t) => t.href ? {
|
|
992
|
+
label: t.label,
|
|
993
|
+
href: t.href
|
|
994
|
+
} : { label: t.label })];
|
|
995
|
+
const seen = /* @__PURE__ */ new Set();
|
|
996
|
+
return combined.filter((c) => {
|
|
997
|
+
if (c.href === void 0) return true;
|
|
998
|
+
if (seen.has(c.href)) return false;
|
|
999
|
+
seen.add(c.href);
|
|
1000
|
+
return true;
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* URL-segment fallback for pages with no node in the tree, so a stray
|
|
1005
|
+
* page still gets a root-anchored trail.
|
|
1006
|
+
*/
|
|
1007
|
+
function breadcrumbsFromUrl(slug, homeLabel = "Home") {
|
|
875
1008
|
const parts = slug.split("/").filter(Boolean);
|
|
876
1009
|
const crumbs = [{
|
|
877
1010
|
label: homeLabel,
|
|
@@ -1028,30 +1161,48 @@ function transformAdmonitions(source, options = {}) {
|
|
|
1028
1161
|
};
|
|
1029
1162
|
const { frontmatter, body, bodyOffset: _ } = splitFrontmatter(source);
|
|
1030
1163
|
const { stashed, restore } = stashCodeBlocks(body);
|
|
1031
|
-
return frontmatter + restore(stashed.replace(ADMONITION_PATTERN, (match, rawType, rawTitle, rawContent) => {
|
|
1164
|
+
return frontmatter + restore(stashed.replace(ADMONITION_PATTERN, (match, rawIndent, rawType, rawTitle, rawContent) => {
|
|
1032
1165
|
const aside = typeMap[String(rawType).toLowerCase()];
|
|
1033
1166
|
if (!aside) return match;
|
|
1167
|
+
const indent = typeof rawIndent === "string" ? rawIndent : "";
|
|
1034
1168
|
const title = typeof rawTitle === "string" ? rawTitle.trim() : "";
|
|
1035
|
-
|
|
1036
|
-
return `\n\n<Aside type="${aside}"${title ? ` title=${JSON.stringify(title)}` : ""}>\n\n${content}\n\n</Aside>\n\n`;
|
|
1169
|
+
return `\n\n${indent}<Aside type="${aside}"${title ? ` title=${JSON.stringify(title)}` : ""}>\n\n${reindentBody(String(rawContent), indent)}\n\n${indent}</Aside>\n\n`;
|
|
1037
1170
|
}));
|
|
1038
1171
|
}
|
|
1039
1172
|
/**
|
|
1040
1173
|
* Match `:::type[optional title] body :::` with non-greedy body.
|
|
1041
1174
|
*
|
|
1042
1175
|
* Components:
|
|
1176
|
+
* - `^([ \t]*)` leading indentation of the opener line (captured) so the
|
|
1177
|
+
* emitted `<Aside>` can be re-indented to the same depth —
|
|
1178
|
+
* load-bearing for directives nested inside indented JSX
|
|
1179
|
+
* (e.g. `:::note` inside a `<TabItem>`). The `m` flag makes
|
|
1180
|
+
* `^`/`$` match line boundaries. Line-anchoring also stops
|
|
1181
|
+
* a stray mid-line `:::` from being treated as an opener.
|
|
1043
1182
|
* - `:::` literal opener
|
|
1044
1183
|
* - `([a-zA-Z]+)` type token (captured, case-insensitive lookup at use site)
|
|
1045
1184
|
* - `(?:\[(...)\])?` optional bracketed title; brackets stripped from capture
|
|
1046
|
-
* - `\
|
|
1185
|
+
* - `\n|[ \t]+` at least one whitespace before content (avoids matching
|
|
1047
1186
|
* `:::foo:::` directly)
|
|
1048
1187
|
* - `([\s\S]*?)` non-greedy body, may span newlines
|
|
1049
|
-
* - `\n
|
|
1188
|
+
* - `\n?[ \t]*:::[ \t]*$` closer, possibly indented, at end of its line
|
|
1050
1189
|
*
|
|
1051
1190
|
* Non-greedy body + global flag means adjacent admonitions don't merge
|
|
1052
1191
|
* (the engine finds the *nearest* `:::` closer for each opener).
|
|
1053
1192
|
*/
|
|
1054
|
-
const ADMONITION_PATTERN =
|
|
1193
|
+
const ADMONITION_PATTERN = /^([ \t]*):::([a-zA-Z]+)(?:\[([^\]]*)\])?[ \t]*(?:\n|[ \t]+)([\s\S]*?)\n?[ \t]*:::[ \t]*$/gm;
|
|
1194
|
+
/**
|
|
1195
|
+
* Dedent the captured admonition body to a common baseline (preserving
|
|
1196
|
+
* relative structure like nested lists / JSX), then re-prefix every
|
|
1197
|
+
* non-blank line with the directive's own indentation so the emitted
|
|
1198
|
+
* `<Aside>…</Aside>` block sits at the same depth as the directive.
|
|
1199
|
+
*/
|
|
1200
|
+
function reindentBody(content, indent) {
|
|
1201
|
+
const lines = content.replace(/^\n+/, "").replace(/\n+$/, "").split("\n");
|
|
1202
|
+
const widths = lines.filter((l) => l.trim() !== "").map((l) => l.match(/^[ \t]*/)?.[0].length ?? 0);
|
|
1203
|
+
const common = widths.length ? Math.min(...widths) : 0;
|
|
1204
|
+
return lines.map((l) => l.trim() === "" ? "" : indent + l.slice(common)).join("\n");
|
|
1205
|
+
}
|
|
1055
1206
|
function splitFrontmatter(source) {
|
|
1056
1207
|
const match = source.match(/^---\n[\s\S]*?\n---\n?/);
|
|
1057
1208
|
if (!match) return {
|
|
@@ -1711,6 +1862,118 @@ function parseTitle(meta) {
|
|
|
1711
1862
|
return (meta.match(/\btitle="([^"]+)"/) ?? meta.match(/\btitle='([^']+)'/))?.[1];
|
|
1712
1863
|
}
|
|
1713
1864
|
/**
|
|
1865
|
+
* Expand a (possibly space-padded) line-range spec like `5-16, 21-40` or
|
|
1866
|
+
* `1,3-5` into an explicit list of 1-based line numbers. Tolerates spaces
|
|
1867
|
+
* anywhere — `{5-16, 21-40}` expands to the same set as `{5-16,21-40}`.
|
|
1868
|
+
*/
|
|
1869
|
+
function expandRanges(spec) {
|
|
1870
|
+
const out = [];
|
|
1871
|
+
for (const partRaw of spec.split(",")) {
|
|
1872
|
+
const part = partRaw.trim();
|
|
1873
|
+
if (!part) continue;
|
|
1874
|
+
const range = part.match(/^(\d+)\s*-\s*(\d+)$/);
|
|
1875
|
+
if (range) {
|
|
1876
|
+
const a = Number.parseInt(range[1], 10);
|
|
1877
|
+
const b = Number.parseInt(range[2], 10);
|
|
1878
|
+
const lo = Math.min(a, b);
|
|
1879
|
+
const hi = Math.max(a, b);
|
|
1880
|
+
for (let i = lo; i <= hi; i++) out.push(i);
|
|
1881
|
+
} else if (/^\d+$/.test(part)) out.push(Number.parseInt(part, 10));
|
|
1882
|
+
}
|
|
1883
|
+
return out;
|
|
1884
|
+
}
|
|
1885
|
+
/**
|
|
1886
|
+
* Parse an EC-style fence meta string into {@link NimbusMeta}.
|
|
1887
|
+
*
|
|
1888
|
+
* Order matters — each step consumes (blanks out) what it matched so later,
|
|
1889
|
+
* looser patterns can't re-grab it:
|
|
1890
|
+
* 1. `frame="…"` (quoted)
|
|
1891
|
+
* 2. `title="…"` (quoted; value resolved separately)
|
|
1892
|
+
* 3. `ins="…"` / `del="…"` (quoted tokens)
|
|
1893
|
+
* 4. `ins={…}` / `del={…}` / `collapse={…}` (brace ranges)
|
|
1894
|
+
* 5. standalone `"…"`/`'…'` (search words — what's left of the quotes)
|
|
1895
|
+
* 6. `wrap` (bare keyword)
|
|
1896
|
+
* 7. bare `{…}` (plain line-highlight — only what survives)
|
|
1897
|
+
*/
|
|
1898
|
+
function parseNimbusMeta(raw) {
|
|
1899
|
+
const meta = {
|
|
1900
|
+
highlightLines: /* @__PURE__ */ new Set(),
|
|
1901
|
+
insLines: /* @__PURE__ */ new Set(),
|
|
1902
|
+
delLines: /* @__PURE__ */ new Set(),
|
|
1903
|
+
insTokens: [],
|
|
1904
|
+
delTokens: [],
|
|
1905
|
+
searchWords: [],
|
|
1906
|
+
collapseLines: /* @__PURE__ */ new Set(),
|
|
1907
|
+
wrap: false,
|
|
1908
|
+
frame: void 0
|
|
1909
|
+
};
|
|
1910
|
+
if (!raw) return meta;
|
|
1911
|
+
let s = raw;
|
|
1912
|
+
s = s.replace(/\bframe=(?:"([^"]*)"|'([^']*)')/g, (_m, d, sg) => {
|
|
1913
|
+
const v = d ?? sg;
|
|
1914
|
+
if (v) meta.frame = v;
|
|
1915
|
+
return " ";
|
|
1916
|
+
});
|
|
1917
|
+
s = s.replace(/\btitle=(?:"([^"]*)"|'([^']*)')/g, () => " ");
|
|
1918
|
+
s = s.replace(/\bins=(?:"([^"]*)"|'([^']*)')/g, (_m, d, sg) => {
|
|
1919
|
+
const v = d ?? sg;
|
|
1920
|
+
if (v) meta.insTokens.push(v);
|
|
1921
|
+
return " ";
|
|
1922
|
+
});
|
|
1923
|
+
s = s.replace(/\bdel=(?:"([^"]*)"|'([^']*)')/g, (_m, d, sg) => {
|
|
1924
|
+
const v = d ?? sg;
|
|
1925
|
+
if (v) meta.delTokens.push(v);
|
|
1926
|
+
return " ";
|
|
1927
|
+
});
|
|
1928
|
+
s = s.replace(/\bins=\{([^}]*)\}/g, (_m, spec) => {
|
|
1929
|
+
for (const n of expandRanges(spec)) meta.insLines.add(n);
|
|
1930
|
+
return " ";
|
|
1931
|
+
});
|
|
1932
|
+
s = s.replace(/\bdel=\{([^}]*)\}/g, (_m, spec) => {
|
|
1933
|
+
for (const n of expandRanges(spec)) meta.delLines.add(n);
|
|
1934
|
+
return " ";
|
|
1935
|
+
});
|
|
1936
|
+
s = s.replace(/\bcollapse=\{([^}]*)\}/g, (_m, spec) => {
|
|
1937
|
+
for (const n of expandRanges(spec)) meta.collapseLines.add(n);
|
|
1938
|
+
return " ";
|
|
1939
|
+
});
|
|
1940
|
+
for (const m of s.matchAll(/"([^"]*)"|'([^']*)'/g)) {
|
|
1941
|
+
const v = m[1] ?? m[2];
|
|
1942
|
+
if (v) meta.searchWords.push(v);
|
|
1943
|
+
}
|
|
1944
|
+
s = s.replace(/"[^"]*"|'[^']*'/g, " ");
|
|
1945
|
+
s = s.replace(/\bwrap\b/g, () => {
|
|
1946
|
+
meta.wrap = true;
|
|
1947
|
+
return " ";
|
|
1948
|
+
});
|
|
1949
|
+
s = s.replace(/\{([^}]*)\}/g, (_m, spec) => {
|
|
1950
|
+
for (const n of expandRanges(spec)) meta.highlightLines.add(n);
|
|
1951
|
+
return " ";
|
|
1952
|
+
});
|
|
1953
|
+
return meta;
|
|
1954
|
+
}
|
|
1955
|
+
/** Collect the plain-text content of a hast line node (concatenates spans). */
|
|
1956
|
+
function lineText(node) {
|
|
1957
|
+
if (node.type === "text") return node.value ?? "";
|
|
1958
|
+
let out = "";
|
|
1959
|
+
if ("children" in node && node.children) for (const child of node.children) out += lineText(child);
|
|
1960
|
+
return out;
|
|
1961
|
+
}
|
|
1962
|
+
/** Find every (non-overlapping) start index of `substr` in `str`. */
|
|
1963
|
+
function findAllSubstringIndexes(str, substr) {
|
|
1964
|
+
const out = [];
|
|
1965
|
+
if (!substr) return out;
|
|
1966
|
+
let cursor = 0;
|
|
1967
|
+
for (;;) {
|
|
1968
|
+
const index = str.indexOf(substr, cursor);
|
|
1969
|
+
if (index === -1) break;
|
|
1970
|
+
out.push(index);
|
|
1971
|
+
cursor = index + substr.length;
|
|
1972
|
+
}
|
|
1973
|
+
return out;
|
|
1974
|
+
}
|
|
1975
|
+
const META_SYMBOL = Symbol("nimbus-meta");
|
|
1976
|
+
/**
|
|
1714
1977
|
* The canonical Shiki transformer chain for Nimbus. Returns a fresh
|
|
1715
1978
|
* array each call so callers don't accidentally mutate a shared list.
|
|
1716
1979
|
*
|
|
@@ -1727,11 +1990,57 @@ function defaultCodeTransformers() {
|
|
|
1727
1990
|
transformerNotationFocus(),
|
|
1728
1991
|
transformerNotationErrorLevel(),
|
|
1729
1992
|
transformerNotationWordHighlight(),
|
|
1730
|
-
|
|
1731
|
-
transformerMetaWordHighlight(),
|
|
1993
|
+
nimbusMetaTransformer(),
|
|
1732
1994
|
titleAndLangTransformer()
|
|
1733
1995
|
];
|
|
1734
1996
|
}
|
|
1997
|
+
/**
|
|
1998
|
+
* Nimbus-owned fence-meta transformer. Owns bare-brace highlight
|
|
1999
|
+
* (space-tolerant), `ins=`/`del=` (brace + quoted-string forms),
|
|
2000
|
+
* quoted-search word highlight, `wrap`, `collapse` (neutral), and a
|
|
2001
|
+
* `frame=` hook. Replaces the stock meta-highlight + meta-word-highlight
|
|
2002
|
+
* transformers, which double-fired and hijacked braces.
|
|
2003
|
+
*/
|
|
2004
|
+
function nimbusMetaTransformer() {
|
|
2005
|
+
function getMeta(ctx) {
|
|
2006
|
+
const carrier = ctx.meta ?? {};
|
|
2007
|
+
if (!carrier[META_SYMBOL]) carrier[META_SYMBOL] = parseNimbusMeta(ctx.options.meta?.__raw);
|
|
2008
|
+
return carrier[META_SYMBOL];
|
|
2009
|
+
}
|
|
2010
|
+
return {
|
|
2011
|
+
name: "nimbus:meta",
|
|
2012
|
+
preprocess(code, options) {
|
|
2013
|
+
if (!this.options.meta?.__raw) return;
|
|
2014
|
+
const meta = getMeta(this);
|
|
2015
|
+
if (meta.searchWords.length === 0) return;
|
|
2016
|
+
options.decorations ||= [];
|
|
2017
|
+
for (const word of meta.searchWords) for (const index of findAllSubstringIndexes(code, word)) options.decorations.push({
|
|
2018
|
+
start: index,
|
|
2019
|
+
end: index + word.length,
|
|
2020
|
+
properties: { class: "highlighted-word" }
|
|
2021
|
+
});
|
|
2022
|
+
},
|
|
2023
|
+
line(node, lineNumber) {
|
|
2024
|
+
if (!this.options.meta?.__raw) return;
|
|
2025
|
+
const meta = getMeta(this);
|
|
2026
|
+
if (meta.highlightLines.has(lineNumber)) this.addClassToHast(node, "highlighted");
|
|
2027
|
+
if (meta.insLines.has(lineNumber)) this.addClassToHast(node, "diff add");
|
|
2028
|
+
if (meta.delLines.has(lineNumber)) this.addClassToHast(node, "diff remove");
|
|
2029
|
+
if (meta.insTokens.length || meta.delTokens.length) {
|
|
2030
|
+
const text = lineText(node);
|
|
2031
|
+
if (meta.insTokens.some((t) => text.includes(t))) this.addClassToHast(node, "diff add");
|
|
2032
|
+
if (meta.delTokens.some((t) => text.includes(t))) this.addClassToHast(node, "diff remove");
|
|
2033
|
+
}
|
|
2034
|
+
},
|
|
2035
|
+
pre(preNode) {
|
|
2036
|
+
if (!this.options.meta?.__raw) return;
|
|
2037
|
+
const meta = getMeta(this);
|
|
2038
|
+
preNode.properties = preNode.properties ?? {};
|
|
2039
|
+
if (meta.wrap) preNode.properties["data-nb-wrap"] = "";
|
|
2040
|
+
if (meta.frame) preNode.properties["data-nb-frame"] = meta.frame;
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
1735
2044
|
function titleAndLangTransformer() {
|
|
1736
2045
|
return {
|
|
1737
2046
|
name: "nimbus:title-and-lang",
|
|
@@ -1741,6 +2050,8 @@ function titleAndLangTransformer() {
|
|
|
1741
2050
|
const title = parseTitle(meta);
|
|
1742
2051
|
preNode.properties = preNode.properties ?? {};
|
|
1743
2052
|
preNode.properties["data-nb-lang"] = lang;
|
|
2053
|
+
const wrap = preNode.properties["data-nb-wrap"] !== void 0;
|
|
2054
|
+
const frame = preNode.properties["data-nb-frame"];
|
|
1744
2055
|
const children = [];
|
|
1745
2056
|
if (title) children.push({
|
|
1746
2057
|
type: "element",
|
|
@@ -1765,13 +2076,16 @@ function titleAndLangTransformer() {
|
|
|
1765
2076
|
}]
|
|
1766
2077
|
});
|
|
1767
2078
|
children.push(preNode);
|
|
2079
|
+
const figureProps = {
|
|
2080
|
+
class: title ? "nb-code-figure nb-code-figure-titled" : "nb-code-figure",
|
|
2081
|
+
"data-nb-lang": lang
|
|
2082
|
+
};
|
|
2083
|
+
if (wrap) figureProps["data-nb-wrap"] = "";
|
|
2084
|
+
if (typeof frame === "string") figureProps["data-nb-frame"] = frame;
|
|
1768
2085
|
return {
|
|
1769
2086
|
type: "element",
|
|
1770
2087
|
tagName: "figure",
|
|
1771
|
-
properties:
|
|
1772
|
-
class: title ? "nb-code-figure nb-code-figure-titled" : "nb-code-figure",
|
|
1773
|
-
"data-nb-lang": lang
|
|
1774
|
-
},
|
|
2088
|
+
properties: figureProps,
|
|
1775
2089
|
children
|
|
1776
2090
|
};
|
|
1777
2091
|
}
|
|
@@ -4201,8 +4515,19 @@ async function getIndexedTopLevel() {
|
|
|
4201
4515
|
*/
|
|
4202
4516
|
async function getSidebar(currentSlug, options) {
|
|
4203
4517
|
const config = await loadNimbusConfig();
|
|
4204
|
-
const
|
|
4205
|
-
|
|
4518
|
+
const fullTree = await buildFullSidebarTree(currentSlug, options?.collection);
|
|
4519
|
+
let tree = config.sidebar?.scope === "section" ? scopeToCurrentSection(fullTree, currentSlug) : fullTree;
|
|
4520
|
+
const boundaries = config.sidebar?.isolate?.boundaries;
|
|
4521
|
+
if (boundaries && boundaries.length > 0) tree = isolateToBoundary(tree, currentSlug, boundaries);
|
|
4522
|
+
if (options?.transform) {
|
|
4523
|
+
const ctx = deriveTransformCtx(fullTree, currentSlug);
|
|
4524
|
+
tree = await options.transform({
|
|
4525
|
+
tree,
|
|
4526
|
+
currentSlug,
|
|
4527
|
+
...ctx
|
|
4528
|
+
});
|
|
4529
|
+
}
|
|
4530
|
+
return tree;
|
|
4206
4531
|
}
|
|
4207
4532
|
/**
|
|
4208
4533
|
* Derive one section per top-level group in the sidebar — used by
|
|
@@ -4293,13 +4618,74 @@ async function getPrevNext(currentSlug, options) {
|
|
|
4293
4618
|
return getPrevNext$1(currentSlug, tree, options?.overrides, validInternalLinks);
|
|
4294
4619
|
}
|
|
4295
4620
|
/**
|
|
4296
|
-
* Build breadcrumb trail from
|
|
4297
|
-
*
|
|
4298
|
-
*
|
|
4299
|
-
*
|
|
4621
|
+
* Build the breadcrumb trail from the active node's ancestry in the nav
|
|
4622
|
+
* tree. Labels come from nav nodes, hrefs from each node's landing — so a
|
|
4623
|
+
* section crumb links to its real landing page and segments with no node
|
|
4624
|
+
* never appear. Index-less folders render as non-interactive crumbs.
|
|
4625
|
+
*
|
|
4626
|
+
* - `collection` — the page's Astro collection; pass `entry.collection` so
|
|
4627
|
+
* versioned pages get version-prefixed hrefs.
|
|
4628
|
+
* - `root` — the leading crumb (default `{ label: "Home", href: "/" }`).
|
|
4629
|
+
* - `resolveLabel` — override a crumb label, or return `null` to drop it.
|
|
4630
|
+
*
|
|
4631
|
+
* Falls back to URL-segment derivation when the page has no node in the
|
|
4632
|
+
* tree, so a stray page still gets a root-anchored trail.
|
|
4300
4633
|
*/
|
|
4301
4634
|
async function getBreadcrumbs(currentSlug, options) {
|
|
4302
|
-
|
|
4635
|
+
const path = findActivePath(await buildFullSidebarTree(currentSlug, options?.collection), currentSlug);
|
|
4636
|
+
if (path.length > 0) return assembleBreadcrumbs(options?.root ?? {
|
|
4637
|
+
label: "Home",
|
|
4638
|
+
href: "/"
|
|
4639
|
+
}, path, await Promise.all(path.map((node) => Promise.resolve(options?.resolveLabel?.({
|
|
4640
|
+
node,
|
|
4641
|
+
slug: currentSlug
|
|
4642
|
+
})))));
|
|
4643
|
+
return breadcrumbsFromUrl(currentSlug, options?.root?.label ?? "Home");
|
|
4644
|
+
}
|
|
4645
|
+
/**
|
|
4646
|
+
* Resolve a section's display title(s) for the current page, decoupled so
|
|
4647
|
+
* the rail header and the breadcrumb can differ.
|
|
4648
|
+
*
|
|
4649
|
+
* Derives `sectionSlug` (seg0) and `module` (seg1) from the slug and passes
|
|
4650
|
+
* them to a caller-supplied resolver. The resolver is an argument rather
|
|
4651
|
+
* than config because config is JSON-serialized and cannot carry functions.
|
|
4652
|
+
* `indexEntryId` is currently always `undefined`.
|
|
4653
|
+
*/
|
|
4654
|
+
async function getSectionTitle(currentSlug, resolve) {
|
|
4655
|
+
const segs = currentSlug.split("/").filter(Boolean);
|
|
4656
|
+
const sectionSlug = segs[0];
|
|
4657
|
+
if (!sectionSlug) return void 0;
|
|
4658
|
+
return resolve({
|
|
4659
|
+
sectionSlug,
|
|
4660
|
+
module: segs[1],
|
|
4661
|
+
indexEntryId: void 0
|
|
4662
|
+
});
|
|
4663
|
+
}
|
|
4664
|
+
/**
|
|
4665
|
+
* Navigation (breadcrumbs, sidebar active-state, optional prev/next) for a
|
|
4666
|
+
* data-driven route with no content entry of its own — e.g. a catalog page
|
|
4667
|
+
* under `src/pages/[...].astro`.
|
|
4668
|
+
*
|
|
4669
|
+
* Builds the breadcrumb trail to `section` (a real nav node) and appends
|
|
4670
|
+
* `trail` (the leaf). The sidebar is built with `section` as the active
|
|
4671
|
+
* path, so the section node highlights even though the leaf is not in the
|
|
4672
|
+
* tree — the leaf is never injected, keeping the tree and prev/next clean.
|
|
4673
|
+
*/
|
|
4674
|
+
async function getRouteNavigation(options) {
|
|
4675
|
+
const { section, trail = [], prevNext = false, collection, resolveLabel } = options;
|
|
4676
|
+
const sidebar = await getSidebar(section, { collection });
|
|
4677
|
+
const breadcrumbs = composeRouteBreadcrumbs(await getBreadcrumbs(section, {
|
|
4678
|
+
collection,
|
|
4679
|
+
resolveLabel
|
|
4680
|
+
}), trail);
|
|
4681
|
+
let pn;
|
|
4682
|
+
if (prevNext) pn = await getPrevNext(section, { sidebarTree: sidebar });
|
|
4683
|
+
return {
|
|
4684
|
+
breadcrumbs,
|
|
4685
|
+
sidebar,
|
|
4686
|
+
activeHref: section,
|
|
4687
|
+
prevNext: pn
|
|
4688
|
+
};
|
|
4303
4689
|
}
|
|
4304
4690
|
/**
|
|
4305
4691
|
* Build an edit URL for a content entry using `config.editPattern`.
|
|
@@ -4651,5 +5037,5 @@ async function getVersionStatus(collectionId) {
|
|
|
4651
5037
|
}
|
|
4652
5038
|
|
|
4653
5039
|
//#endregion
|
|
4654
|
-
export { nimbus as default, defaultCodeTransformers, defineConfig, getBreadcrumbs, getCanonicalUrl, getCollectionLlmsUrl, getCollectionPageProps, getCollectionStaticPaths, getCurrentVersion, getDocsPageProps, getDocsStaticPaths, getEditUrl, getIndexedEntries, getIndexedTopLevel, getLastUpdated, getPrevNext, getSidebar, getSidebarSections, getTOC, getVersionAlternates, getVersionLandingUrl, getVersionStatus, getVersions, getVisibleEntries, renderEntryAsMarkdown, sidebarHash };
|
|
5040
|
+
export { nimbus as default, defaultCodeTransformers, defineConfig, getBreadcrumbs, getCanonicalUrl, getCollectionLlmsUrl, getCollectionPageProps, getCollectionStaticPaths, getCurrentVersion, getDocsPageProps, getDocsStaticPaths, getEditUrl, getIndexedEntries, getIndexedTopLevel, getLastUpdated, getPrevNext, getRouteNavigation, getSectionTitle, getSidebar, getSidebarSections, getTOC, getVersionAlternates, getVersionLandingUrl, getVersionStatus, getVersions, getVisibleEntries, renderEntryAsMarkdown, sidebarHash };
|
|
4655
5041
|
//# sourceMappingURL=index.js.map
|