@suzumiyaaoba/mdxr 0.4.0 → 0.5.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/dist/cli.mjs +121 -431
- package/dist/components.d.mts +5 -5
- package/dist/components.mjs +2 -2
- package/dist/{config-DQeZaek7.mjs → config-C-pcUAoU.mjs} +1 -1
- package/dist/{doc-context-B_H-VT1p.d.mts → doc-context-CWOZ0eDq.d.mts} +7 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{ui-D89i0Cq-.mjs → ui-k-VrmhPJ.mjs} +268 -51
- package/package.json +3 -2
- package/skill/references/components/output.md +3 -1
- package/skill/references/components/reports.md +60 -0
- package/skill/references/components.md +2 -0
- package/src/ascii/index.ts +2 -0
- package/src/ascii/research.ts +90 -0
- package/src/assets/css.ts +36 -1
- package/src/html.ts +0 -20
- package/src/hydrate/runtime-module.ts +0 -1
- package/src/hydrate.ts +0 -5
- package/src/mdx.ts +0 -4
- package/src/remark/directives.ts +2 -0
- package/src/remark/headings.ts +1 -23
- package/src/remark/references.ts +37 -10
- package/src/render.ts +0 -51
- package/src/research.ts +147 -0
- package/src/ui/graph-viewport.tsx +52 -0
- package/src/ui/graph.tsx +35 -28
- package/src/ui/index.ts +6 -0
- package/src/ui/performance-target.tsx +48 -0
- package/src/ui/research-claim.tsx +69 -0
- package/src/components/ui/toc.tsx +0 -560
- package/src/page-toc-js.ts +0 -46
- package/src/page-toc-runtime.ts +0 -22
- package/src/ui/page-toc.tsx +0 -54
package/dist/cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { a as loadUserModule, c as srcDir, i as importBundledCode, n as loadConfig, o as resolveModuleEntry, r as SHARED_PACKAGES, s as pkgRoot } from "./config-
|
|
2
|
+
import { $d as reportModel, Bp as splitPathLines, Cu as CLAIM_LABELS, Du as performanceDetails, Eu as RESEARCH_CLAIM_SCHEMA, Fp as parseProps, Gp as isRecord, Jp as safeHref, Kp as nonEmpty, Lp as fenceFilename, Ml as PlanHeader, Mp as takeUsedIcons, Np as DocContext, Ou as performanceResult, Qd as REPORT_SPECS, Rp as fenceLang, Tp as numOf, Tu as PERFORMANCE_TARGET_SCHEMA, Up as langForPath, V as shadcnModules, Vp as SKIP_LANGS, Wp as isComponent, Yd as calculate, Yp as urlScheme, _f as readRecords, dc as queryPlanModel, ef as comparisonModel, ep as STATUSES, fc as diagramModel, ff as columnsOf, gf as parseJson, hf as numberValue, kf as parseDiff, lc as bundleModel, mf as isDataRecord, mp as textList, np as isStatus, pc as flameModel, pf as display, pp as numList, qp as own, t as builtinComponents, tf as schemaRows, uc as dependencyMatrixModel, vf as records, vl as PLOT_NAMES, wp as attrTrue, wu as PERFORMANCE_LABELS, yf as tableRecords, yl as plotModel, zp as parseLineRange } from "./ui-k-VrmhPJ.mjs";
|
|
3
|
+
import { a as loadUserModule, c as srcDir, i as importBundledCode, n as loadConfig, o as resolveModuleEntry, r as SHARED_PACKAGES, s as pkgRoot } from "./config-C-pcUAoU.mjs";
|
|
4
4
|
import module from "node:module";
|
|
5
5
|
import fsp, { access, readFile, readdir, writeFile } from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
@@ -23,18 +23,16 @@ import { visit } from "unist-util-visit";
|
|
|
23
23
|
import fs, { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
24
24
|
import { visitParents } from "unist-util-visit-parents";
|
|
25
25
|
import * as valibot from "valibot";
|
|
26
|
-
import { Fragment,
|
|
27
|
-
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
26
|
+
import { Fragment, createElement } from "react";
|
|
28
27
|
import { icons } from "@iconify-json/lucide";
|
|
29
28
|
import { icons as icons$1 } from "@iconify-json/vscode-icons";
|
|
30
29
|
import { differenceInCalendarDays, isValid, parseISO } from "date-fns";
|
|
31
|
-
import { cn } from "cn";
|
|
32
30
|
import { build } from "esbuild";
|
|
33
31
|
import os from "node:os";
|
|
34
32
|
import { spawn } from "node:child_process";
|
|
35
33
|
import { once } from "node:events";
|
|
36
|
-
import { renderToStaticMarkup, renderToString } from "react-dom/server";
|
|
37
34
|
import { compile } from "@mdx-js/mdx";
|
|
35
|
+
import { renderToStaticMarkup, renderToString } from "react-dom/server";
|
|
38
36
|
import rehypeKatex from "rehype-katex";
|
|
39
37
|
import { transformerMetaHighlight, transformerMetaWordHighlight, transformerNotationDiff, transformerNotationErrorLevel, transformerNotationFocus, transformerNotationHighlight, transformerNotationWordHighlight, transformerRemoveNotationEscape } from "@shikijs/transformers";
|
|
40
38
|
import { createHighlighter } from "shiki";
|
|
@@ -1003,12 +1001,14 @@ const CONTAINER_COMPONENTS = {
|
|
|
1003
1001
|
package: "Package",
|
|
1004
1002
|
packages: "Packages",
|
|
1005
1003
|
pathway: "Pathway",
|
|
1004
|
+
performancetarget: "PerformanceTarget",
|
|
1006
1005
|
phase: "Phase",
|
|
1007
1006
|
piechart: "PieChart",
|
|
1008
1007
|
plan: "Plan",
|
|
1009
1008
|
quadrant: "Quadrant",
|
|
1010
1009
|
radar: "Radar",
|
|
1011
1010
|
release: "Release",
|
|
1011
|
+
researchclaim: "ResearchClaim",
|
|
1012
1012
|
review: "Review",
|
|
1013
1013
|
sankey: "Sankey",
|
|
1014
1014
|
scatter: "Scatter",
|
|
@@ -1180,11 +1180,8 @@ const toList = (items) => ({
|
|
|
1180
1180
|
* 2. `<Toc>` elements (`:::toc` included) get a nested link list injected as
|
|
1181
1181
|
* children — `depth`/`min` attributes bound the heading levels included
|
|
1182
1182
|
* (defaults: h2–h3, i.e. min=2 depth=3).
|
|
1183
|
-
*
|
|
1184
|
-
* The flat heading list is also left on `file.data.mdxrHeadings` for the
|
|
1185
|
-
* page-level sidebar ToC.
|
|
1186
1183
|
*/
|
|
1187
|
-
const remarkMdxrHeadings = () => (tree
|
|
1184
|
+
const remarkMdxrHeadings = () => (tree) => {
|
|
1188
1185
|
const counts = /* @__PURE__ */ new Map();
|
|
1189
1186
|
const used = /* @__PURE__ */ new Set();
|
|
1190
1187
|
const headings = [];
|
|
@@ -1215,11 +1212,6 @@ const remarkMdxrHeadings = () => (tree, file) => {
|
|
|
1215
1212
|
const items = headings.filter((h) => h.depth >= minDepth && h.depth <= maxDepth && h.text !== "");
|
|
1216
1213
|
node.children = items.length === 0 ? [] : [toList(nest(items))];
|
|
1217
1214
|
});
|
|
1218
|
-
file.data.mdxrHeadings = headings.filter((h) => h.text !== "").map(({ depth, slug, text }) => ({
|
|
1219
|
-
depth,
|
|
1220
|
-
slug,
|
|
1221
|
-
text
|
|
1222
|
-
}));
|
|
1223
1215
|
};
|
|
1224
1216
|
//#endregion
|
|
1225
1217
|
//#region src/remark/no-js.ts
|
|
@@ -1347,6 +1339,7 @@ const INLINE_REFERENCES = /* @__PURE__ */ new Set([
|
|
|
1347
1339
|
const REFERENCE_FEATURES = /* @__PURE__ */ new Set([
|
|
1348
1340
|
...INLINE_REFERENCES,
|
|
1349
1341
|
"Source",
|
|
1342
|
+
"ResearchClaim",
|
|
1350
1343
|
"TableOfFigures",
|
|
1351
1344
|
"NumberedEquation",
|
|
1352
1345
|
"Theorem"
|
|
@@ -1407,6 +1400,26 @@ const resolveCitation = (node, state) => {
|
|
|
1407
1400
|
ids.push(id);
|
|
1408
1401
|
state.backlinks.set(target, ids);
|
|
1409
1402
|
};
|
|
1403
|
+
/** Claims reuse the bibliography's numbering and backlink contract. */
|
|
1404
|
+
const resolveClaimCitation = (node, state) => {
|
|
1405
|
+
const source = jsxAttr(node, "source");
|
|
1406
|
+
if (source === void 0) return;
|
|
1407
|
+
const citation = {
|
|
1408
|
+
attributes: [{
|
|
1409
|
+
name: "source",
|
|
1410
|
+
type: "mdxJsxAttribute",
|
|
1411
|
+
value: source
|
|
1412
|
+
}],
|
|
1413
|
+
children: [],
|
|
1414
|
+
name: "Cite",
|
|
1415
|
+
position: node.position,
|
|
1416
|
+
type: "mdxJsxTextElement"
|
|
1417
|
+
};
|
|
1418
|
+
resolveCitation(citation, state);
|
|
1419
|
+
set(node, "citationId", jsxAttr(citation, "id") ?? "");
|
|
1420
|
+
set(node, "sourceHref", jsxAttr(citation, "href") ?? "");
|
|
1421
|
+
set(node, "sourceLabel", jsxAttr(citation, "label") ?? "");
|
|
1422
|
+
};
|
|
1410
1423
|
const resolveCrossReference = (node, state) => {
|
|
1411
1424
|
const target = jsxAttr(node, "target") ?? (node.name === "TermRef" ? `term-${jsxAttr(node, "term") ?? ""}` : "");
|
|
1412
1425
|
const reference = state.references.get(target);
|
|
@@ -1453,6 +1466,13 @@ const addLegacyAnchor = (node) => {
|
|
|
1453
1466
|
type: "mdxJsxTextElement"
|
|
1454
1467
|
});
|
|
1455
1468
|
};
|
|
1469
|
+
const resolveReference = (node, state) => {
|
|
1470
|
+
if (node.name === "ResearchClaim") resolveClaimCitation(node, state);
|
|
1471
|
+
if (node.name === "Cite") resolveCitation(node, state);
|
|
1472
|
+
if (node.name === "CrossRef" || node.name === "TermRef") resolveCrossReference(node, state);
|
|
1473
|
+
if (node.name === "TableOfFigures") listFigures(node, state.references);
|
|
1474
|
+
addLegacyAnchor(node);
|
|
1475
|
+
};
|
|
1456
1476
|
/** Two passes support forward references without changing older documents. */
|
|
1457
1477
|
const remarkReferences = () => (tree, file) => {
|
|
1458
1478
|
const elements = [];
|
|
@@ -1469,12 +1489,7 @@ const remarkReferences = () => (tree, file) => {
|
|
|
1469
1489
|
sources: /* @__PURE__ */ new Map()
|
|
1470
1490
|
};
|
|
1471
1491
|
for (const node of elements) registerReference(node, state);
|
|
1472
|
-
for (const node of elements)
|
|
1473
|
-
if (node.name === "Cite") resolveCitation(node, state);
|
|
1474
|
-
if (node.name === "CrossRef" || node.name === "TermRef") resolveCrossReference(node, state);
|
|
1475
|
-
if (node.name === "TableOfFigures") listFigures(node, state.references);
|
|
1476
|
-
addLegacyAnchor(node);
|
|
1477
|
-
}
|
|
1492
|
+
for (const node of elements) resolveReference(node, state);
|
|
1478
1493
|
for (const [source, ids] of state.backlinks) {
|
|
1479
1494
|
const reference = state.sources.get(source);
|
|
1480
1495
|
if (reference) set(reference.node, "backlinks", ids.join(","));
|
|
@@ -3712,6 +3727,48 @@ const reportRenderers = {
|
|
|
3712
3727
|
]), ...ctx.children(n)] }
|
|
3713
3728
|
};
|
|
3714
3729
|
//#endregion
|
|
3730
|
+
//#region src/ascii/research.ts
|
|
3731
|
+
const attributes = (node, names) => Object.fromEntries(names.map((name) => [name, attr(node, name)]));
|
|
3732
|
+
const researchRenderers = {
|
|
3733
|
+
PerformanceTarget: { flow: (node, ctx) => {
|
|
3734
|
+
const props = parseProps(PERFORMANCE_TARGET_SCHEMA, attributes(node, [
|
|
3735
|
+
"actual",
|
|
3736
|
+
"better",
|
|
3737
|
+
"conditions",
|
|
3738
|
+
"measuredAt",
|
|
3739
|
+
"name",
|
|
3740
|
+
"statistic",
|
|
3741
|
+
"status",
|
|
3742
|
+
"target",
|
|
3743
|
+
"unit"
|
|
3744
|
+
]), "PerformanceTarget");
|
|
3745
|
+
return [
|
|
3746
|
+
...caption(props.name, ` — ${PERFORMANCE_LABELS[performanceResult(props)]}`),
|
|
3747
|
+
table(["Field", "Value"], performanceDetails(props)),
|
|
3748
|
+
...ctx.children(node)
|
|
3749
|
+
];
|
|
3750
|
+
} },
|
|
3751
|
+
ResearchClaim: { flow: (node, ctx) => {
|
|
3752
|
+
const props = parseProps(RESEARCH_CLAIM_SCHEMA, attributes(node, [
|
|
3753
|
+
"checked",
|
|
3754
|
+
"citationId",
|
|
3755
|
+
"id",
|
|
3756
|
+
"kind",
|
|
3757
|
+
"source",
|
|
3758
|
+
"sourceHref",
|
|
3759
|
+
"sourceLabel",
|
|
3760
|
+
"title"
|
|
3761
|
+
]), "ResearchClaim");
|
|
3762
|
+
return [
|
|
3763
|
+
...caption(props.title, ` — ${CLAIM_LABELS[props.kind]}`),
|
|
3764
|
+
...ctx.children(node),
|
|
3765
|
+
...props.source === void 0 || props.citationId === void 0 ? [] : [html(`<a id="${props.citationId.replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">")}"></a>`)],
|
|
3766
|
+
...props.source === void 0 ? [] : [para([txt("Source: "), link(props.sourceHref ?? `#${props.source}`, [txt(props.sourceLabel ?? props.source)])])],
|
|
3767
|
+
...props.checked === void 0 ? [] : [para([txt(`Checked: ${props.checked}`)])]
|
|
3768
|
+
];
|
|
3769
|
+
} }
|
|
3770
|
+
};
|
|
3771
|
+
//#endregion
|
|
3715
3772
|
//#region src/ascii/shadcn.ts
|
|
3716
3773
|
/** `ctx.inline` result wrapped so empty children fall back to `alt`. */
|
|
3717
3774
|
const inlineOr = (node, ctx, alt) => {
|
|
@@ -4111,6 +4168,7 @@ const asciiRenderers = {
|
|
|
4111
4168
|
...investigationRenderers,
|
|
4112
4169
|
...outputRenderers,
|
|
4113
4170
|
...reportRenderers,
|
|
4171
|
+
...researchRenderers,
|
|
4114
4172
|
...chartRenderers,
|
|
4115
4173
|
...layoutRenderers,
|
|
4116
4174
|
...formRenderers,
|
|
@@ -4551,7 +4609,7 @@ const openInBrowser = async (target) => {
|
|
|
4551
4609
|
};
|
|
4552
4610
|
//#endregion
|
|
4553
4611
|
//#region src/client-js.ts
|
|
4554
|
-
let cache
|
|
4612
|
+
let cache;
|
|
4555
4613
|
const buildClientJs = async () => {
|
|
4556
4614
|
return (await build({
|
|
4557
4615
|
absWorkingDir: pkgRoot,
|
|
@@ -4579,11 +4637,11 @@ const buildClientJs = async () => {
|
|
|
4579
4637
|
* bundle is identical for every render in a process.
|
|
4580
4638
|
*/
|
|
4581
4639
|
const clientJs = async () => {
|
|
4582
|
-
cache
|
|
4640
|
+
cache ??= buildClientJs();
|
|
4583
4641
|
try {
|
|
4584
|
-
return await cache
|
|
4642
|
+
return await cache;
|
|
4585
4643
|
} catch (error) {
|
|
4586
|
-
cache
|
|
4644
|
+
cache = void 0;
|
|
4587
4645
|
throw error;
|
|
4588
4646
|
}
|
|
4589
4647
|
};
|
|
@@ -4677,338 +4735,6 @@ addEventListener('DOMContentLoaded', function () {
|
|
|
4677
4735
|
});
|
|
4678
4736
|
`;
|
|
4679
4737
|
//#endregion
|
|
4680
|
-
//#region src/components/ui/toc.tsx
|
|
4681
|
-
/**
|
|
4682
|
-
* Table of contents with active-anchor tracking and depth-aware line styling.
|
|
4683
|
-
* Adapted from the Crux UI / ABUI registry (`@abui/toc`), itself derived from
|
|
4684
|
-
* fumadocs' TOC. Two list variants: `TOCItems` (straight line) and
|
|
4685
|
-
* `ClerkTOCItems` (line that follows the heading hierarchy).
|
|
4686
|
-
*/
|
|
4687
|
-
const ActiveAnchorContext = createContext([]);
|
|
4688
|
-
const ScrollContext = createContext({ current: null });
|
|
4689
|
-
const TOCContext = createContext([]);
|
|
4690
|
-
/** The ids of visible anchors. */
|
|
4691
|
-
const useActiveAnchors = () => useContext(ActiveAnchorContext);
|
|
4692
|
-
const useTOCItems = () => useContext(TOCContext);
|
|
4693
|
-
const mergeRefs = (...refs) => (value) => {
|
|
4694
|
-
for (const ref of refs) if (typeof ref === "function") ref(value);
|
|
4695
|
-
else if (ref !== null && ref !== void 0) ref.current = value;
|
|
4696
|
-
};
|
|
4697
|
-
/** Track which of the `watch` heading ids are on screen. */
|
|
4698
|
-
const useAnchorObserver = (watch, single) => {
|
|
4699
|
-
const observerRef = useRef(null);
|
|
4700
|
-
const visibleRef = useRef(/* @__PURE__ */ new Set());
|
|
4701
|
-
const [activeAnchor, setActiveAnchor] = useState([]);
|
|
4702
|
-
const onChange = useCallback((entries) => {
|
|
4703
|
-
const visible = visibleRef.current;
|
|
4704
|
-
for (const entry of entries) if (entry.isIntersecting) visible.add(entry.target.id);
|
|
4705
|
-
else visible.delete(entry.target.id);
|
|
4706
|
-
if (visible.size > 0) {
|
|
4707
|
-
const items = watch.filter((item) => visible.has(item));
|
|
4708
|
-
setActiveAnchor(single ? items.slice(0, 1) : items);
|
|
4709
|
-
return;
|
|
4710
|
-
}
|
|
4711
|
-
const viewTop = entries[0]?.rootBounds?.top ?? 0;
|
|
4712
|
-
let fallback;
|
|
4713
|
-
let min = -1;
|
|
4714
|
-
for (const id of watch) {
|
|
4715
|
-
const element = document.getElementById(id);
|
|
4716
|
-
if (element === null) continue;
|
|
4717
|
-
const d = Math.abs(viewTop - element.getBoundingClientRect().top);
|
|
4718
|
-
if (min === -1 || d < min) {
|
|
4719
|
-
fallback = element;
|
|
4720
|
-
min = d;
|
|
4721
|
-
}
|
|
4722
|
-
}
|
|
4723
|
-
setActiveAnchor(fallback === void 0 ? [] : [fallback.id]);
|
|
4724
|
-
}, [watch, single]);
|
|
4725
|
-
useEffect(() => {
|
|
4726
|
-
if (observerRef.current !== null) return;
|
|
4727
|
-
observerRef.current = new IntersectionObserver(onChange, {
|
|
4728
|
-
rootMargin: "0px",
|
|
4729
|
-
threshold: .98
|
|
4730
|
-
});
|
|
4731
|
-
return () => {
|
|
4732
|
-
observerRef.current?.disconnect();
|
|
4733
|
-
observerRef.current = null;
|
|
4734
|
-
};
|
|
4735
|
-
}, [onChange]);
|
|
4736
|
-
useEffect(() => {
|
|
4737
|
-
const observer = observerRef.current;
|
|
4738
|
-
if (observer === null) return;
|
|
4739
|
-
const elements = watch.flatMap((heading) => document.getElementById(heading) ?? []);
|
|
4740
|
-
for (const element of elements) observer.observe(element);
|
|
4741
|
-
return () => {
|
|
4742
|
-
for (const element of elements) observer.unobserve(element);
|
|
4743
|
-
};
|
|
4744
|
-
}, [watch]);
|
|
4745
|
-
return activeAnchor;
|
|
4746
|
-
};
|
|
4747
|
-
const TOCProvider = ({ toc, single = false, children }) => {
|
|
4748
|
-
const headings = useMemo(() => toc.map((item) => item.url.split("#")[1] ?? ""), [toc]);
|
|
4749
|
-
const activeAnchors = useAnchorObserver(headings, single);
|
|
4750
|
-
return /* @__PURE__ */ jsx(TOCContext.Provider, {
|
|
4751
|
-
value: toc,
|
|
4752
|
-
children: /* @__PURE__ */ jsx(ActiveAnchorContext.Provider, {
|
|
4753
|
-
value: activeAnchors,
|
|
4754
|
-
children
|
|
4755
|
-
})
|
|
4756
|
-
});
|
|
4757
|
-
};
|
|
4758
|
-
/** Scrollable container; the first active item is kept in view. */
|
|
4759
|
-
const TOCScrollArea = ({ ref, className, children, ...props }) => {
|
|
4760
|
-
const viewRef = useRef(null);
|
|
4761
|
-
return /* @__PURE__ */ jsx("div", {
|
|
4762
|
-
className: cn("relative ms-px min-h-0 [scrollbar-width:none] overflow-auto [mask-image:linear-gradient(to_bottom,transparent,white_16px,white_calc(100%-16px),transparent)] py-3 text-sm", className),
|
|
4763
|
-
ref: mergeRefs(viewRef, ref),
|
|
4764
|
-
...props,
|
|
4765
|
-
children: /* @__PURE__ */ jsx(ScrollContext.Provider, {
|
|
4766
|
-
value: viewRef,
|
|
4767
|
-
children
|
|
4768
|
-
})
|
|
4769
|
-
});
|
|
4770
|
-
};
|
|
4771
|
-
const calcThumb = (container, active) => {
|
|
4772
|
-
if (active.length === 0 || container.clientHeight === 0) return [0, 0];
|
|
4773
|
-
let upper = Number.MAX_VALUE;
|
|
4774
|
-
let lower = 0;
|
|
4775
|
-
for (const item of active) {
|
|
4776
|
-
const element = container.querySelector(`a[href="#${CSS.escape(item)}"]`);
|
|
4777
|
-
if (element === null) continue;
|
|
4778
|
-
const styles = getComputedStyle(element);
|
|
4779
|
-
upper = Math.min(upper, element.offsetTop + Number.parseFloat(styles.paddingTop));
|
|
4780
|
-
lower = Math.max(lower, element.offsetTop + element.clientHeight - Number.parseFloat(styles.paddingBottom));
|
|
4781
|
-
}
|
|
4782
|
-
return [upper, lower - upper];
|
|
4783
|
-
};
|
|
4784
|
-
const updateThumb = (element, [top, height]) => {
|
|
4785
|
-
element.style.setProperty("--toc-top", `${top}px`);
|
|
4786
|
-
element.style.setProperty("--toc-height", `${height}px`);
|
|
4787
|
-
};
|
|
4788
|
-
/** Active indicator that slides alongside the visible entries. */
|
|
4789
|
-
const TocThumb = ({ containerRef, ...props }) => {
|
|
4790
|
-
const thumbRef = useRef(null);
|
|
4791
|
-
const active = useActiveAnchors();
|
|
4792
|
-
useEffect(() => {
|
|
4793
|
-
const container = containerRef.current;
|
|
4794
|
-
if (container === null) return;
|
|
4795
|
-
const onUpdate = () => {
|
|
4796
|
-
if (thumbRef.current !== null) updateThumb(thumbRef.current, calcThumb(container, active));
|
|
4797
|
-
};
|
|
4798
|
-
const observer = new ResizeObserver(onUpdate);
|
|
4799
|
-
observer.observe(container);
|
|
4800
|
-
onUpdate();
|
|
4801
|
-
return () => {
|
|
4802
|
-
observer.disconnect();
|
|
4803
|
-
};
|
|
4804
|
-
}, [containerRef, active]);
|
|
4805
|
-
return /* @__PURE__ */ jsx("div", {
|
|
4806
|
-
ref: thumbRef,
|
|
4807
|
-
role: "none",
|
|
4808
|
-
...props
|
|
4809
|
-
});
|
|
4810
|
-
};
|
|
4811
|
-
const TOCItem = ({ ref, onActiveChange, children, ...props }) => {
|
|
4812
|
-
const containerRef = useContext(ScrollContext);
|
|
4813
|
-
const anchorRef = useRef(null);
|
|
4814
|
-
const activeOrder = useActiveAnchors().indexOf(props.href.slice(1));
|
|
4815
|
-
const isActive = activeOrder !== -1;
|
|
4816
|
-
const shouldScroll = activeOrder === 0;
|
|
4817
|
-
useLayoutEffect(() => {
|
|
4818
|
-
const anchor = anchorRef.current;
|
|
4819
|
-
const container = containerRef.current;
|
|
4820
|
-
if (container === null || anchor === null || !shouldScroll || !anchor.isConnected) return;
|
|
4821
|
-
const containerRect = container.getBoundingClientRect();
|
|
4822
|
-
const anchorRect = anchor.getBoundingClientRect();
|
|
4823
|
-
if (!(anchorRect.top < containerRect.top || anchorRect.bottom > containerRect.bottom)) return;
|
|
4824
|
-
const anchorCenter = anchor.offsetTop - container.offsetTop + anchor.offsetHeight / 2;
|
|
4825
|
-
container.scrollTo({
|
|
4826
|
-
behavior: "smooth",
|
|
4827
|
-
top: Math.max(0, anchorCenter - container.clientHeight / 2)
|
|
4828
|
-
});
|
|
4829
|
-
}, [containerRef, shouldScroll]);
|
|
4830
|
-
useEffect(() => {
|
|
4831
|
-
onActiveChange?.(isActive);
|
|
4832
|
-
}, [isActive, onActiveChange]);
|
|
4833
|
-
return /* @__PURE__ */ jsx("a", {
|
|
4834
|
-
"data-active": isActive,
|
|
4835
|
-
ref: mergeRefs(anchorRef, ref),
|
|
4836
|
-
...props,
|
|
4837
|
-
children
|
|
4838
|
-
});
|
|
4839
|
-
};
|
|
4840
|
-
const EMPTY_CLS = "rounded-lg border bg-card p-3 text-xs text-muted-foreground";
|
|
4841
|
-
const ITEM_CLS = "py-1.5 text-sm text-muted-foreground transition-colors [overflow-wrap:anywhere] first:pt-0 last:pb-0 hover:text-accent-foreground data-[active=true]:text-primary";
|
|
4842
|
-
const SimpleTOCItem = ({ item }) => /* @__PURE__ */ jsx(TOCItem, {
|
|
4843
|
-
className: cn(ITEM_CLS, item.depth <= 2 && "ps-3", item.depth === 3 && "ps-6", item.depth >= 4 && "ps-8"),
|
|
4844
|
-
href: item.url,
|
|
4845
|
-
children: item.title
|
|
4846
|
-
});
|
|
4847
|
-
/** TOC list with a straight border line. */
|
|
4848
|
-
const TOCItems = ({ ref, className, emptyText = "No Headings", ...props }) => {
|
|
4849
|
-
const containerRef = useRef(null);
|
|
4850
|
-
const items = useTOCItems();
|
|
4851
|
-
if (items.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
4852
|
-
className: EMPTY_CLS,
|
|
4853
|
-
children: emptyText
|
|
4854
|
-
});
|
|
4855
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(TocThumb, {
|
|
4856
|
-
className: "bg-primary absolute top-(--toc-top) h-(--toc-height) w-px transition-all",
|
|
4857
|
-
containerRef
|
|
4858
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
4859
|
-
className: cn("border-foreground/10 flex flex-col border-s", className),
|
|
4860
|
-
ref: mergeRefs(ref, containerRef),
|
|
4861
|
-
...props,
|
|
4862
|
-
children: items.map((item) => /* @__PURE__ */ jsx(SimpleTOCItem, { item }, item.url))
|
|
4863
|
-
})] });
|
|
4864
|
-
};
|
|
4865
|
-
const getItemOffset = (depth) => {
|
|
4866
|
-
if (depth <= 2) return 14;
|
|
4867
|
-
return depth === 3 ? 26 : 36;
|
|
4868
|
-
};
|
|
4869
|
-
const getLineOffset = (depth) => depth >= 3 ? 10 : 0;
|
|
4870
|
-
/** One vertical segment per entry, shifted right for nested headings. */
|
|
4871
|
-
const clerkLine = (container, items) => {
|
|
4872
|
-
let width = 0;
|
|
4873
|
-
let height = 0;
|
|
4874
|
-
const d = [];
|
|
4875
|
-
for (const [i, item] of items.entries()) {
|
|
4876
|
-
const element = container.querySelector(`a[href="${CSS.escape(item.url)}"]`);
|
|
4877
|
-
if (element === null) continue;
|
|
4878
|
-
const styles = getComputedStyle(element);
|
|
4879
|
-
const offset = getLineOffset(item.depth) + 1;
|
|
4880
|
-
const top = element.offsetTop + Number.parseFloat(styles.paddingTop);
|
|
4881
|
-
const bottom = element.offsetTop + element.clientHeight - Number.parseFloat(styles.paddingBottom);
|
|
4882
|
-
width = Math.max(offset, width);
|
|
4883
|
-
height = Math.max(height, bottom);
|
|
4884
|
-
d.push(`${i === 0 ? "M" : "L"}${offset} ${top}`, `L${offset} ${bottom}`);
|
|
4885
|
-
}
|
|
4886
|
-
return {
|
|
4887
|
-
height,
|
|
4888
|
-
path: d.join(" "),
|
|
4889
|
-
width: width + 1
|
|
4890
|
-
};
|
|
4891
|
-
};
|
|
4892
|
-
const ClerkTOCItemElement = ({ item, upper = item.depth, lower = item.depth }) => {
|
|
4893
|
-
const offset = getLineOffset(item.depth);
|
|
4894
|
-
const upperOffset = getLineOffset(upper);
|
|
4895
|
-
const lowerOffset = getLineOffset(lower);
|
|
4896
|
-
return /* @__PURE__ */ jsxs(TOCItem, {
|
|
4897
|
-
className: cn("relative", ITEM_CLS),
|
|
4898
|
-
href: item.url,
|
|
4899
|
-
style: { paddingInlineStart: getItemOffset(item.depth) },
|
|
4900
|
-
children: [
|
|
4901
|
-
offset === upperOffset ? null : /* @__PURE__ */ jsx("svg", {
|
|
4902
|
-
"aria-hidden": "true",
|
|
4903
|
-
className: "absolute start-0 -top-1.5 size-4 rtl:-scale-x-100",
|
|
4904
|
-
viewBox: "0 0 16 16",
|
|
4905
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
4906
|
-
children: /* @__PURE__ */ jsx("line", {
|
|
4907
|
-
className: "stroke-foreground/10",
|
|
4908
|
-
strokeWidth: "1",
|
|
4909
|
-
x1: upperOffset,
|
|
4910
|
-
x2: offset,
|
|
4911
|
-
y1: "0",
|
|
4912
|
-
y2: "12"
|
|
4913
|
-
})
|
|
4914
|
-
}),
|
|
4915
|
-
/* @__PURE__ */ jsx("div", {
|
|
4916
|
-
className: cn("bg-foreground/10 absolute inset-y-0 w-px", offset !== upperOffset && "top-1.5", offset !== lowerOffset && "bottom-1.5"),
|
|
4917
|
-
style: { insetInlineStart: offset }
|
|
4918
|
-
}),
|
|
4919
|
-
item.title
|
|
4920
|
-
]
|
|
4921
|
-
});
|
|
4922
|
-
};
|
|
4923
|
-
/** TOC list whose line bends to follow the heading hierarchy. */
|
|
4924
|
-
const ClerkTOCItems = ({ ref, className, emptyText = "No Headings", ...props }) => {
|
|
4925
|
-
const containerRef = useRef(null);
|
|
4926
|
-
const items = useTOCItems();
|
|
4927
|
-
const [svg, setSvg] = useState();
|
|
4928
|
-
useEffect(() => {
|
|
4929
|
-
const container = containerRef.current;
|
|
4930
|
-
if (container === null) return;
|
|
4931
|
-
const onResize = () => {
|
|
4932
|
-
if (container.clientHeight > 0) setSvg(clerkLine(container, items));
|
|
4933
|
-
};
|
|
4934
|
-
const observer = new ResizeObserver(onResize);
|
|
4935
|
-
onResize();
|
|
4936
|
-
observer.observe(container);
|
|
4937
|
-
return () => {
|
|
4938
|
-
observer.disconnect();
|
|
4939
|
-
};
|
|
4940
|
-
}, [items]);
|
|
4941
|
-
if (items.length === 0) return /* @__PURE__ */ jsx("div", {
|
|
4942
|
-
className: EMPTY_CLS,
|
|
4943
|
-
children: emptyText
|
|
4944
|
-
});
|
|
4945
|
-
const mask = svg && {
|
|
4946
|
-
height: svg.height,
|
|
4947
|
-
maskImage: `url("data:image/svg+xml,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${svg.width} ${svg.height}"><path d="${svg.path}" stroke="black" stroke-width="1" fill="none" /></svg>`)}")`,
|
|
4948
|
-
width: svg.width
|
|
4949
|
-
};
|
|
4950
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [mask ? /* @__PURE__ */ jsx("div", {
|
|
4951
|
-
className: "absolute start-0 top-0 rtl:-scale-x-100",
|
|
4952
|
-
style: mask,
|
|
4953
|
-
children: /* @__PURE__ */ jsx(TocThumb, {
|
|
4954
|
-
className: "bg-primary mt-(--toc-top) h-(--toc-height) transition-all",
|
|
4955
|
-
containerRef
|
|
4956
|
-
})
|
|
4957
|
-
}) : null, /* @__PURE__ */ jsx("div", {
|
|
4958
|
-
className: cn("flex flex-col", className),
|
|
4959
|
-
ref: mergeRefs(containerRef, ref),
|
|
4960
|
-
...props,
|
|
4961
|
-
children: items.map((item, i) => /* @__PURE__ */ jsx(ClerkTOCItemElement, {
|
|
4962
|
-
item,
|
|
4963
|
-
lower: items[i + 1]?.depth,
|
|
4964
|
-
upper: items[i - 1]?.depth
|
|
4965
|
-
}, item.url))
|
|
4966
|
-
})] });
|
|
4967
|
-
};
|
|
4968
|
-
const PageTOC = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
4969
|
-
className: cn("flex flex-col gap-3", className),
|
|
4970
|
-
...props
|
|
4971
|
-
});
|
|
4972
|
-
const PageTOCItems = ({ variant = "default", ...props }) => /* @__PURE__ */ jsx(TOCScrollArea, { children: variant === "clerk" ? /* @__PURE__ */ jsx(ClerkTOCItems, { ...props }) : /* @__PURE__ */ jsx(TOCItems, { ...props }) });
|
|
4973
|
-
//#endregion
|
|
4974
|
-
//#region src/ui/page-toc.tsx
|
|
4975
|
-
/** `<aside>` holding the sidebar and the JSON island with its headings. */
|
|
4976
|
-
const PAGE_TOC_ID = "mdxr-page-toc";
|
|
4977
|
-
const PAGE_TOC_DATA_ID = "mdxr-page-toc-data";
|
|
4978
|
-
/** Heading levels listed in the sidebar (h2–h3, like `:::toc`). */
|
|
4979
|
-
const MIN_DEPTH = 2;
|
|
4980
|
-
const MAX_DEPTH = 3;
|
|
4981
|
-
/** A single entry isn't worth a sidebar. */
|
|
4982
|
-
const MIN_ENTRIES = 2;
|
|
4983
|
-
/** The headings the sidebar lists — empty when the page gets no sidebar. */
|
|
4984
|
-
const pageTocHeadings = (headings) => {
|
|
4985
|
-
const items = headings.filter((h) => h.depth >= MIN_DEPTH && h.depth <= MAX_DEPTH);
|
|
4986
|
-
return items.length < MIN_ENTRIES ? [] : items;
|
|
4987
|
-
};
|
|
4988
|
-
/**
|
|
4989
|
-
* Page-level "On this page" sidebar built on the Crux UI ToC: a clerk-style
|
|
4990
|
-
* outline whose thumb follows the headings currently on screen. Rendered next
|
|
4991
|
-
* to `<main>` by `htmlDocument` (not part of the MDX tree) and hydrated by its
|
|
4992
|
-
* own root, so it works with or without the document hydration bundle.
|
|
4993
|
-
*/
|
|
4994
|
-
const PageToc = ({ headings }) => {
|
|
4995
|
-
const toc = useMemo(() => headings.map((h) => ({
|
|
4996
|
-
depth: h.depth,
|
|
4997
|
-
title: h.text,
|
|
4998
|
-
url: `#${h.slug}`
|
|
4999
|
-
})), [headings]);
|
|
5000
|
-
return /* @__PURE__ */ jsx(TOCProvider, {
|
|
5001
|
-
toc,
|
|
5002
|
-
children: /* @__PURE__ */ jsxs(PageTOC, {
|
|
5003
|
-
className: "min-h-0",
|
|
5004
|
-
children: [/* @__PURE__ */ jsx("p", {
|
|
5005
|
-
className: "text-muted-foreground m-0 text-xs font-semibold",
|
|
5006
|
-
children: "On this page"
|
|
5007
|
-
}), /* @__PURE__ */ jsx(PageTOCItems, { variant: "clerk" })]
|
|
5008
|
-
})
|
|
5009
|
-
});
|
|
5010
|
-
};
|
|
5011
|
-
//#endregion
|
|
5012
4738
|
//#region src/html.ts
|
|
5013
4739
|
/**
|
|
5014
4740
|
* JS destined for an inline `<script>` element: neutralize the two byte
|
|
@@ -5044,10 +4770,6 @@ const iconSvg = (name) => {
|
|
|
5044
4770
|
* `handleDocEvent`); THEME_JS restores the stored choice before paint.
|
|
5045
4771
|
*/
|
|
5046
4772
|
const THEME_TOGGLE_HTML = `<button type="button" class="mdxr-theme" data-mdxr-theme data-mode="auto" title="Theme: auto" aria-label="Switch theme (current: auto)"><span class="mdxr-theme-i mdxr-theme-i-auto">${iconSvg("sun-moon")}</span><span class="mdxr-theme-i mdxr-theme-i-light">${iconSvg("sun")}</span><span class="mdxr-theme-i mdxr-theme-i-dark">${iconSvg("moon")}</span></button>`;
|
|
5047
|
-
/** JSON for an inline `<script type="application/json">` island. */
|
|
5048
|
-
const jsonIsland = (data) => JSON.stringify(data).replaceAll("<", String.raw`\u003c`);
|
|
5049
|
-
const pageTocHtml = (toc) => toc === void 0 ? "" : `<aside id="${PAGE_TOC_ID}" aria-label="On this page" class="fixed top-20 left-[calc(50%+25.5rem)] hidden max-h-[calc(100vh-7rem)] w-52 flex-col xl:flex print:hidden">${toc.html}</aside>
|
|
5050
|
-
<script type="application/json" id="${PAGE_TOC_DATA_ID}">${jsonIsland(toc.headings)}<\/script>`;
|
|
5051
4773
|
const htmlDocument = (o) => `<!doctype html>
|
|
5052
4774
|
<html lang="en">
|
|
5053
4775
|
<head>
|
|
@@ -5062,12 +4784,10 @@ ${o.needsKatex === true ? `<link rel="stylesheet" href="${KATEX_CDN_URL}">` : ""
|
|
|
5062
4784
|
<body class="bg-white text-neutral-900 antialiased dark:bg-neutral-950 dark:text-neutral-100">
|
|
5063
4785
|
${THEME_TOGGLE_HTML}
|
|
5064
4786
|
<main id="mdxr-root" class="prose prose-neutral dark:prose-invert mx-auto max-w-3xl px-6 py-10">${o.body}</main>
|
|
5065
|
-
${pageTocHtml(o.pageToc)}
|
|
5066
4787
|
<script>${inlineScript(o.clientJs)}<\/script>
|
|
5067
4788
|
${o.needsMermaid ? `<script type="module">${inlineScript(MERMAID_JS)}<\/script>` : ""}
|
|
5068
4789
|
${o.liveReload === true ? `<script>${inlineScript(LIVE_RELOAD_JS)}<\/script>` : ""}
|
|
5069
4790
|
${o.hydrateJs === void 0 ? "" : `<script>${inlineScript(o.hydrateJs)}<\/script>`}
|
|
5070
|
-
${o.pageTocJs === void 0 ? "" : `<script>${inlineScript(o.pageTocJs)}<\/script>`}
|
|
5071
4791
|
</body>
|
|
5072
4792
|
</html>
|
|
5073
4793
|
`;
|
|
@@ -5428,8 +5148,7 @@ const scanMdxrImports = async (importer) => {
|
|
|
5428
5148
|
const EXTRA_MODULES = {
|
|
5429
5149
|
builtinComponents: path.join(srcDir, "ui/index.js"),
|
|
5430
5150
|
defineConfig: path.join(srcDir, "config.js"),
|
|
5431
|
-
mountDocument: path.join(srcDir, "hydrate-runtime.js")
|
|
5432
|
-
mountPageToc: path.join(srcDir, "page-toc-runtime.js")
|
|
5151
|
+
mountDocument: path.join(srcDir, "hydrate-runtime.js")
|
|
5433
5152
|
};
|
|
5434
5153
|
/**
|
|
5435
5154
|
* The virtual module's `v` export. `export * as v` would materialize the
|
|
@@ -5527,7 +5246,6 @@ const buildHydrateScript = async (spec) => {
|
|
|
5527
5246
|
};
|
|
5528
5247
|
const mountDocument = bind("mountDocument");
|
|
5529
5248
|
const planHeader = spec.header === void 0 ? "undefined" : bind("PlanHeader");
|
|
5530
|
-
const mountPageToc = spec.pageToc === true ? `${bind("mountPageToc")}();` : "";
|
|
5531
5249
|
for (const name of spec.usedComponents) {
|
|
5532
5250
|
if (!Object.hasOwn(builtinComponents, name)) continue;
|
|
5533
5251
|
const exported = name === "pre" ? "Pre" : name;
|
|
@@ -5546,7 +5264,6 @@ ${mountDocument}({
|
|
|
5546
5264
|
planHeader: ${planHeader},
|
|
5547
5265
|
userModule: ${spec.componentsPath === void 0 ? "undefined" : "__mdxrUser"},
|
|
5548
5266
|
});
|
|
5549
|
-
${mountPageToc}
|
|
5550
5267
|
`;
|
|
5551
5268
|
const result = await build({
|
|
5552
5269
|
absWorkingDir: pkgRoot,
|
|
@@ -6048,7 +5765,6 @@ const mdxToHtml = async (source, components, filePath = "document.mdx", opts = {
|
|
|
6048
5765
|
context,
|
|
6049
5766
|
fileLinks: Object.fromEntries(fileLinks),
|
|
6050
5767
|
frontmatter,
|
|
6051
|
-
headings: compiled.data.mdxrHeadings ?? [],
|
|
6052
5768
|
hydrated,
|
|
6053
5769
|
renderWithHeader: (header) => {
|
|
6054
5770
|
try {
|
|
@@ -6068,44 +5784,6 @@ const mdxToHtml = async (source, components, filePath = "document.mdx", opts = {
|
|
|
6068
5784
|
};
|
|
6069
5785
|
};
|
|
6070
5786
|
//#endregion
|
|
6071
|
-
//#region src/page-toc-js.ts
|
|
6072
|
-
let cache;
|
|
6073
|
-
const buildPageTocJs = async () => {
|
|
6074
|
-
return (await build({
|
|
6075
|
-
absWorkingDir: pkgRoot,
|
|
6076
|
-
bundle: true,
|
|
6077
|
-
define: { "process.env.NODE_ENV": "\"production\"" },
|
|
6078
|
-
format: "iife",
|
|
6079
|
-
jsx: "automatic",
|
|
6080
|
-
jsxImportSource: "react",
|
|
6081
|
-
logLevel: "silent",
|
|
6082
|
-
minify: true,
|
|
6083
|
-
platform: "browser",
|
|
6084
|
-
stdin: {
|
|
6085
|
-
contents: "import { mountPageToc } from \"./page-toc-runtime.js\"; mountPageToc();",
|
|
6086
|
-
loader: "js",
|
|
6087
|
-
resolveDir: path.join(pkgRoot, "src"),
|
|
6088
|
-
sourcefile: "mdxr-page-toc.js"
|
|
6089
|
-
},
|
|
6090
|
-
target: "es2022",
|
|
6091
|
-
write: false
|
|
6092
|
-
})).outputFiles[0]?.text ?? "";
|
|
6093
|
-
};
|
|
6094
|
-
/**
|
|
6095
|
-
* Standalone bundle that hydrates the sidebar ToC — used only when the
|
|
6096
|
-
* document has no hydration bundle of its own (that one mounts the ToC with
|
|
6097
|
-
* the React copy it already ships). Memoized: identical for every render.
|
|
6098
|
-
*/
|
|
6099
|
-
const pageTocJs = async () => {
|
|
6100
|
-
cache ??= buildPageTocJs();
|
|
6101
|
-
try {
|
|
6102
|
-
return await cache;
|
|
6103
|
-
} catch (error) {
|
|
6104
|
-
cache = void 0;
|
|
6105
|
-
throw error;
|
|
6106
|
-
}
|
|
6107
|
-
};
|
|
6108
|
-
//#endregion
|
|
6109
5787
|
//#region src/assets/css.ts
|
|
6110
5788
|
/** Base CSS appended after Tailwind utilities (covers what utilities can't). */
|
|
6111
5789
|
const BASE_CSS = `
|
|
@@ -6115,6 +5793,20 @@ const BASE_CSS = `
|
|
|
6115
5793
|
.task-list-item { list-style: none; }
|
|
6116
5794
|
ul.contains-task-list { padding-left: 1.25rem; }
|
|
6117
5795
|
.task-list-item input[type='checkbox'] { margin-right: 0.4em; }
|
|
5796
|
+
/* Graph fitting is native SVG sizing: readable minimum scale on screen,
|
|
5797
|
+
* full-width containment in print, and a keyboard-operable actual-size toggle. */
|
|
5798
|
+
.mdxr-graph-view:has(.mdxr-graph-actual:checked) .mdxr-graph-image {
|
|
5799
|
+
max-width: none !important;
|
|
5800
|
+
}
|
|
5801
|
+
.mdxr-graph-tools:focus-within { outline: 2px solid rgb(14 165 233); outline-offset: -2px; }
|
|
5802
|
+
@media print {
|
|
5803
|
+
.mdxr-graph-tools { display: none; }
|
|
5804
|
+
.mdxr-graph-scroll { overflow: visible; }
|
|
5805
|
+
.mdxr-graph-view:has(.mdxr-graph-actual:checked) .mdxr-graph-image,
|
|
5806
|
+
.mdxr-graph-view .mdxr-graph-image {
|
|
5807
|
+
max-width: 100% !important; min-width: 0 !important; height: auto !important;
|
|
5808
|
+
}
|
|
5809
|
+
}
|
|
6118
5810
|
/* --- Interaction feedback -------------------------------------------
|
|
6119
5811
|
* Every [data-copy] button carries an idle and a done icon
|
|
6120
5812
|
* (.mdxr-copy-idle/.mdxr-copy-done); the delegated event handler
|
|
@@ -6391,7 +6083,7 @@ details[open] > summary .mdxr-chev { transform: rotate(90deg); }
|
|
|
6391
6083
|
.dark .mdxr-toc-body > ul > li > a { color: rgb(212 212 212); }
|
|
6392
6084
|
.mdxr-toc-body > ul > li > p > a::before,
|
|
6393
6085
|
.mdxr-toc-body > ul > li > a::before {
|
|
6394
|
-
content: counter(mdxr-toc); min-width: 1em;
|
|
6086
|
+
content: counter(mdxr-toc); min-width: 1em; flex-shrink: 0; white-space: nowrap;
|
|
6395
6087
|
font-size: 0.72rem; font-weight: 400; font-variant-numeric: tabular-nums;
|
|
6396
6088
|
color: rgb(163 163 163);
|
|
6397
6089
|
}
|
|
@@ -6403,6 +6095,27 @@ details[open] > summary .mdxr-chev { transform: rotate(90deg); }
|
|
|
6403
6095
|
}
|
|
6404
6096
|
.dark .mdxr-toc-body ul ul { border-color: rgb(64 64 64); }
|
|
6405
6097
|
.mdxr-toc-body ul ul a { font-size: 0.8125rem; padding: 0.2rem 0.45rem; }
|
|
6098
|
+
/* A 64rem viewport fits the usual 45rem article, a 14rem ToC, a 2rem
|
|
6099
|
+
* gap, and 1.5rem outer gutters. Only the first document-level ToC docks;
|
|
6100
|
+
* additional or nested tables stay in the document flow. Print stays inline. */
|
|
6101
|
+
@media screen and (min-width: 64rem) {
|
|
6102
|
+
#mdxr-root:has(> .mdxr-toc, > article:only-of-type > .mdxr-toc) {
|
|
6103
|
+
max-width: 64rem;
|
|
6104
|
+
padding-right: 17.5rem;
|
|
6105
|
+
}
|
|
6106
|
+
#mdxr-root > .mdxr-toc:nth-child(1 of .mdxr-toc),
|
|
6107
|
+
#mdxr-root:not(:has(> .mdxr-toc)) > article:only-of-type > .mdxr-toc:nth-child(1 of .mdxr-toc) {
|
|
6108
|
+
position: fixed;
|
|
6109
|
+
top: 4rem;
|
|
6110
|
+
right: max(1.5rem, calc((100vw - 64rem) / 2 + 1.5rem));
|
|
6111
|
+
width: 14rem;
|
|
6112
|
+
max-height: calc(100dvh - 5.5rem);
|
|
6113
|
+
margin: 0;
|
|
6114
|
+
overflow-y: auto;
|
|
6115
|
+
overscroll-behavior: contain;
|
|
6116
|
+
overflow-wrap: anywhere;
|
|
6117
|
+
}
|
|
6118
|
+
}
|
|
6406
6119
|
/* Ask: native form controls stay interactive without hydration. The real
|
|
6407
6120
|
* inputs are visually hidden; state is styled through :checked/~ siblings. */
|
|
6408
6121
|
.mdxr-choice {
|
|
@@ -6613,7 +6326,6 @@ const buildHydrateBundle = async (args) => {
|
|
|
6613
6326
|
fileLinks: args.fileLinks,
|
|
6614
6327
|
header: args.headerProps,
|
|
6615
6328
|
now: args.now,
|
|
6616
|
-
pageToc: args.pageToc,
|
|
6617
6329
|
usedComponents: args.usedComponents,
|
|
6618
6330
|
usedIcons: args.usedIcons
|
|
6619
6331
|
});
|
|
@@ -6650,23 +6362,6 @@ const frontmatterHeader = (fm, body) => {
|
|
|
6650
6362
|
version: fm("version")
|
|
6651
6363
|
};
|
|
6652
6364
|
};
|
|
6653
|
-
/** Sidebar ToC entries — frontmatter `toc: false` opts the page out. */
|
|
6654
|
-
const sidebarHeadings = (frontmatter, headings) => frontmatter.toc === false || frontmatter.toc === "false" ? [] : pageTocHeadings(headings);
|
|
6655
|
-
/**
|
|
6656
|
-
* SSR markup for the sidebar ToC plus the script that hydrates it. The
|
|
6657
|
-
* document bundle mounts the ToC itself when there is one; otherwise the
|
|
6658
|
-
* standalone bundle does. Without JS the sidebar stays plain anchor links.
|
|
6659
|
-
*/
|
|
6660
|
-
const renderPageToc = async (headings, interactive, hasDocumentBundle) => {
|
|
6661
|
-
if (headings.length === 0) return {};
|
|
6662
|
-
return {
|
|
6663
|
-
pageToc: {
|
|
6664
|
-
headings,
|
|
6665
|
-
html: (interactive ? renderToString : renderToStaticMarkup)(createElement(PageToc, { headings }))
|
|
6666
|
-
},
|
|
6667
|
-
pageTocJs: interactive && !hasDocumentBundle ? await pageTocJs() : void 0
|
|
6668
|
-
};
|
|
6669
|
-
};
|
|
6670
6365
|
/** Render MDX source text to a standalone HTML document. */
|
|
6671
6366
|
const render = async (source, opts = {}) => {
|
|
6672
6367
|
const dir = path.resolve(opts.dir ?? process.cwd());
|
|
@@ -6679,7 +6374,7 @@ const render = async (source, opts = {}) => {
|
|
|
6679
6374
|
...builtinComponents,
|
|
6680
6375
|
...user.components
|
|
6681
6376
|
};
|
|
6682
|
-
const { body, code, fileLinks, frontmatter,
|
|
6377
|
+
const { body, code, fileLinks, frontmatter, renderedAt, renderWithHeader, usedComponents, usedIcons } = await mdxToHtml(source, components, filePath, {
|
|
6683
6378
|
editor: config.editor,
|
|
6684
6379
|
hydrate: opts.hydrate
|
|
6685
6380
|
});
|
|
@@ -6728,7 +6423,6 @@ const render = async (source, opts = {}) => {
|
|
|
6728
6423
|
];
|
|
6729
6424
|
const { css, dependencies } = await buildCss(sources, config.themePath);
|
|
6730
6425
|
opts.onDependencies?.(dependencies);
|
|
6731
|
-
const tocHeadings = sidebarHeadings(frontmatter, headings);
|
|
6732
6426
|
const [js, hydrateJs] = await Promise.all([clientJs(), buildHydrateBundle({
|
|
6733
6427
|
code,
|
|
6734
6428
|
config,
|
|
@@ -6736,11 +6430,9 @@ const render = async (source, opts = {}) => {
|
|
|
6736
6430
|
headerProps,
|
|
6737
6431
|
hydrate: opts.hydrate,
|
|
6738
6432
|
now: renderedAt,
|
|
6739
|
-
pageToc: tocHeadings.length > 0,
|
|
6740
6433
|
usedComponents,
|
|
6741
6434
|
usedIcons: docIcons
|
|
6742
6435
|
})]);
|
|
6743
|
-
const { pageToc, pageTocJs: tocJs } = await renderPageToc(tocHeadings, opts.hydrate ?? true, hydrateJs !== void 0);
|
|
6744
6436
|
return htmlDocument({
|
|
6745
6437
|
body: docBody,
|
|
6746
6438
|
clientJs: js,
|
|
@@ -6749,8 +6441,6 @@ const render = async (source, opts = {}) => {
|
|
|
6749
6441
|
liveReload: opts.liveReload,
|
|
6750
6442
|
needsKatex: /class="[^"]*katex/u.test(docBody),
|
|
6751
6443
|
needsMermaid: /class="[^"]*mermaid/u.test(docBody),
|
|
6752
|
-
pageToc,
|
|
6753
|
-
pageTocJs: tocJs,
|
|
6754
6444
|
title
|
|
6755
6445
|
});
|
|
6756
6446
|
};
|