@suzumiyaaoba/mdxr 0.3.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 +407 -212
- package/dist/components.d.mts +10 -10
- package/dist/components.mjs +2 -2
- package/dist/{config-DhBFpSdB.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-DOxuV3n8.mjs → ui-k-VrmhPJ.mjs} +661 -349
- 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/ast.ts +129 -92
- package/src/ascii/charts.ts +2 -3
- package/src/ascii/document.ts +2 -4
- package/src/ascii/extended.ts +7 -4
- package/src/ascii/glyphs.ts +3 -1
- package/src/ascii/index.ts +2 -0
- package/src/ascii/investigation.ts +38 -36
- package/src/ascii/output.ts +12 -4
- package/src/ascii/parts.ts +12 -1
- package/src/ascii/planning.ts +151 -87
- package/src/ascii/reports.ts +29 -23
- package/src/ascii/research.ts +90 -0
- package/src/ascii/shadcn.ts +26 -32
- package/src/assets/css.ts +36 -1
- package/src/catalog.ts +60 -44
- package/src/client/doc-events.ts +12 -13
- package/src/components/ui/calendar.tsx +1 -0
- package/src/components/ui/carousel.tsx +39 -17
- package/src/components/ui/chart.tsx +2 -1
- package/src/components/ui/pagination.tsx +0 -1
- package/src/components/ui/slider.tsx +3 -6
- package/src/components/ui/tabs.tsx +1 -0
- package/src/components/ui/toggle-group.tsx +6 -3
- package/src/extended/data.ts +6 -4
- package/src/extended/diagrams.ts +3 -2
- package/src/extended/differences.ts +13 -9
- package/src/extended/plots.ts +36 -16
- package/src/extended/profiles.ts +3 -1
- package/src/extended/reports.ts +10 -5
- package/src/hydrate/import-scan.ts +9 -6
- package/src/lines.ts +5 -1
- package/src/remark/directives.ts +2 -0
- package/src/remark/references.ts +37 -10
- package/src/research.ts +147 -0
- package/src/ui/attrs.ts +5 -2
- package/src/ui/bar-chart.tsx +11 -18
- package/src/ui/bridge.tsx +15 -15
- package/src/ui/chart.ts +113 -34
- package/src/ui/children.ts +21 -1
- package/src/ui/comments.tsx +9 -8
- package/src/ui/data-context.ts +2 -1
- package/src/ui/data-table.tsx +3 -2
- package/src/ui/data-view.tsx +2 -1
- package/src/ui/diff-parse.ts +19 -1
- package/src/ui/diffstat.tsx +6 -5
- package/src/ui/document-inputs.tsx +13 -10
- package/src/ui/document-media.tsx +3 -2
- package/src/ui/document-tabs.tsx +4 -3
- package/src/ui/file-icon.ts +1 -1
- package/src/ui/funnel.tsx +12 -12
- package/src/ui/gantt.tsx +24 -9
- package/src/ui/graph-viewport.tsx +52 -0
- package/src/ui/graph.tsx +36 -29
- package/src/ui/index.ts +6 -0
- package/src/ui/line-chart.tsx +14 -17
- package/src/ui/matrix.tsx +16 -7
- package/src/ui/performance-target.tsx +48 -0
- package/src/ui/pie-chart.tsx +12 -12
- package/src/ui/props.tsx +15 -13
- package/src/ui/quadrant.tsx +8 -16
- package/src/ui/radar.tsx +9 -12
- package/src/ui/release.tsx +11 -5
- package/src/ui/research-claim.tsx +69 -0
- package/src/ui/sankey.tsx +4 -4
- package/src/ui/scatter.tsx +14 -17
- package/src/ui/series.tsx +2 -3
- package/src/ui/spark.tsx +2 -13
- package/src/ui/summary.tsx +1 -1
- package/src/ui/table-controls.tsx +9 -8
- package/src/ui/tests.tsx +4 -4
- package/src/ui/treemap.tsx +17 -17
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";
|
|
@@ -26,6 +26,7 @@ import * as valibot from "valibot";
|
|
|
26
26
|
import { Fragment, createElement } from "react";
|
|
27
27
|
import { icons } from "@iconify-json/lucide";
|
|
28
28
|
import { icons as icons$1 } from "@iconify-json/vscode-icons";
|
|
29
|
+
import { differenceInCalendarDays, isValid, parseISO } from "date-fns";
|
|
29
30
|
import { build } from "esbuild";
|
|
30
31
|
import os from "node:os";
|
|
31
32
|
import { spawn } from "node:child_process";
|
|
@@ -1000,12 +1001,14 @@ const CONTAINER_COMPONENTS = {
|
|
|
1000
1001
|
package: "Package",
|
|
1001
1002
|
packages: "Packages",
|
|
1002
1003
|
pathway: "Pathway",
|
|
1004
|
+
performancetarget: "PerformanceTarget",
|
|
1003
1005
|
phase: "Phase",
|
|
1004
1006
|
piechart: "PieChart",
|
|
1005
1007
|
plan: "Plan",
|
|
1006
1008
|
quadrant: "Quadrant",
|
|
1007
1009
|
radar: "Radar",
|
|
1008
1010
|
release: "Release",
|
|
1011
|
+
researchclaim: "ResearchClaim",
|
|
1009
1012
|
review: "Review",
|
|
1010
1013
|
sankey: "Sankey",
|
|
1011
1014
|
scatter: "Scatter",
|
|
@@ -1336,6 +1339,7 @@ const INLINE_REFERENCES = /* @__PURE__ */ new Set([
|
|
|
1336
1339
|
const REFERENCE_FEATURES = /* @__PURE__ */ new Set([
|
|
1337
1340
|
...INLINE_REFERENCES,
|
|
1338
1341
|
"Source",
|
|
1342
|
+
"ResearchClaim",
|
|
1339
1343
|
"TableOfFigures",
|
|
1340
1344
|
"NumberedEquation",
|
|
1341
1345
|
"Theorem"
|
|
@@ -1396,6 +1400,26 @@ const resolveCitation = (node, state) => {
|
|
|
1396
1400
|
ids.push(id);
|
|
1397
1401
|
state.backlinks.set(target, ids);
|
|
1398
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
|
+
};
|
|
1399
1423
|
const resolveCrossReference = (node, state) => {
|
|
1400
1424
|
const target = jsxAttr(node, "target") ?? (node.name === "TermRef" ? `term-${jsxAttr(node, "term") ?? ""}` : "");
|
|
1401
1425
|
const reference = state.references.get(target);
|
|
@@ -1442,6 +1466,13 @@ const addLegacyAnchor = (node) => {
|
|
|
1442
1466
|
type: "mdxJsxTextElement"
|
|
1443
1467
|
});
|
|
1444
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
|
+
};
|
|
1445
1476
|
/** Two passes support forward references without changing older documents. */
|
|
1446
1477
|
const remarkReferences = () => (tree, file) => {
|
|
1447
1478
|
const elements = [];
|
|
@@ -1458,12 +1489,7 @@ const remarkReferences = () => (tree, file) => {
|
|
|
1458
1489
|
sources: /* @__PURE__ */ new Map()
|
|
1459
1490
|
};
|
|
1460
1491
|
for (const node of elements) registerReference(node, state);
|
|
1461
|
-
for (const node of elements)
|
|
1462
|
-
if (node.name === "Cite") resolveCitation(node, state);
|
|
1463
|
-
if (node.name === "CrossRef" || node.name === "TermRef") resolveCrossReference(node, state);
|
|
1464
|
-
if (node.name === "TableOfFigures") listFigures(node, state.references);
|
|
1465
|
-
addLegacyAnchor(node);
|
|
1466
|
-
}
|
|
1492
|
+
for (const node of elements) resolveReference(node, state);
|
|
1467
1493
|
for (const [source, ids] of state.backlinks) {
|
|
1468
1494
|
const reference = state.sources.get(source);
|
|
1469
1495
|
if (reference) set(reference.node, "backlinks", ids.join(","));
|
|
@@ -1483,6 +1509,8 @@ const flag = (node, name) => {
|
|
|
1483
1509
|
for (const a of node.attributes) if (isRecord(a) && a.name === name) return a.value === null || a.value === true || attrTrue(a.value);
|
|
1484
1510
|
return false;
|
|
1485
1511
|
};
|
|
1512
|
+
/** Attribute explicitly set to the string `"false"` (open="false" opt-outs). */
|
|
1513
|
+
const flagOff = (node, name) => attr(node, name) === "false";
|
|
1486
1514
|
/** Numeric part of an attribute — `"120ms"` → 120. */
|
|
1487
1515
|
const num = (node, name) => numOf(attr(node, name));
|
|
1488
1516
|
/** Comma-separated attribute list (`labels="a,b"`). */
|
|
@@ -1501,36 +1529,29 @@ const hoistable = (c) => c.type === "paragraph" && isParent(c) && c.children.eve
|
|
|
1501
1529
|
* elements hoisted out of element-only paragraphs (see `hoistable`).
|
|
1502
1530
|
*/
|
|
1503
1531
|
const els = (node, name) => {
|
|
1504
|
-
|
|
1505
|
-
for (const c of node.children ?? []) {
|
|
1506
|
-
if (isMdxEl(c)) {
|
|
1507
|
-
if (name === void 0 || c.name === name) out.push(c);
|
|
1508
|
-
continue;
|
|
1509
|
-
}
|
|
1510
|
-
if (hoistable(c)) {
|
|
1511
|
-
for (const cc of c.children) if (isMdxEl(cc) && (name === void 0 || cc.name === name)) out.push(cc);
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
return out;
|
|
1532
|
+
return (node.children ?? []).flatMap((c) => hoistable(c) ? c.children : [c]).filter((c) => isMdxEl(c) && (name === void 0 || c.name === name));
|
|
1515
1533
|
};
|
|
1516
1534
|
/**
|
|
1517
1535
|
* `node` minus the named JSX children — direct elements removed, matching
|
|
1518
1536
|
* elements stripped out of paragraphs (now-empty paragraphs dropped).
|
|
1519
1537
|
*/
|
|
1520
|
-
const withoutEls = (node, names) =>
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1538
|
+
const withoutEls = (node, names) => {
|
|
1539
|
+
const removed = new Set(names);
|
|
1540
|
+
return {
|
|
1541
|
+
...node,
|
|
1542
|
+
children: (node.children ?? []).flatMap((c) => {
|
|
1543
|
+
if (isMdxEl(c)) return removed.has(c.name ?? "") ? [] : [c];
|
|
1544
|
+
if (hoistable(c)) {
|
|
1545
|
+
const kids = c.children.filter((cc) => !(isMdxEl(cc) && removed.has(cc.name ?? "")));
|
|
1546
|
+
return kids.every((cc) => cc.type === "text" && "value" in cc && typeof cc.value === "string" && cc.value.trim() === "") ? [] : [{
|
|
1547
|
+
...c,
|
|
1548
|
+
children: kids
|
|
1549
|
+
}];
|
|
1550
|
+
}
|
|
1551
|
+
return [c];
|
|
1552
|
+
})
|
|
1553
|
+
};
|
|
1554
|
+
};
|
|
1534
1555
|
/** All literal text under `node` (text + inlineCode values, tree order). */
|
|
1535
1556
|
const textOf = (node) => Array.isArray(node) ? node.map(textContent$1).join("") : textContent$1(node);
|
|
1536
1557
|
const txt = (value) => ({
|
|
@@ -1663,6 +1684,53 @@ const phrasing = (nodes) => nodes.flatMap((n) => {
|
|
|
1663
1684
|
const t = deepText([n]).trim();
|
|
1664
1685
|
return t === "" ? [] : [txt(t)];
|
|
1665
1686
|
});
|
|
1687
|
+
const boldHtml = (node, ctx) => [strong(ctx.inline(node))];
|
|
1688
|
+
const emphasisHtml = (node, ctx) => [em(ctx.inline(node))];
|
|
1689
|
+
const deleteHtml = (node, ctx) => [del(ctx.inline(node))];
|
|
1690
|
+
const codeHtml = (node) => [icode(deepText(node.children ?? []))];
|
|
1691
|
+
const HTML_INLINE = {
|
|
1692
|
+
a: (node, ctx) => {
|
|
1693
|
+
const href = attr(node, "href");
|
|
1694
|
+
const inner = ctx.inline(node);
|
|
1695
|
+
return href === void 0 ? inner : [link(href, inner)];
|
|
1696
|
+
},
|
|
1697
|
+
b: boldHtml,
|
|
1698
|
+
br: () => [brk()],
|
|
1699
|
+
code: codeHtml,
|
|
1700
|
+
del: deleteHtml,
|
|
1701
|
+
em: emphasisHtml,
|
|
1702
|
+
i: emphasisHtml,
|
|
1703
|
+
img: (node) => {
|
|
1704
|
+
return [{
|
|
1705
|
+
alt: attr(node, "alt"),
|
|
1706
|
+
type: "image",
|
|
1707
|
+
url: attr(node, "src") ?? ""
|
|
1708
|
+
}];
|
|
1709
|
+
},
|
|
1710
|
+
kbd: codeHtml,
|
|
1711
|
+
s: deleteHtml,
|
|
1712
|
+
samp: codeHtml,
|
|
1713
|
+
strike: deleteHtml,
|
|
1714
|
+
strong: boldHtml
|
|
1715
|
+
};
|
|
1716
|
+
/** Wrap adjacent inline nodes while retaining nested block structure. */
|
|
1717
|
+
const blocksOf = (nodes) => {
|
|
1718
|
+
const blocks = [];
|
|
1719
|
+
let inline = [];
|
|
1720
|
+
for (const node of nodes) {
|
|
1721
|
+
if (PHRASING.has(node.type)) {
|
|
1722
|
+
inline.push(...phrasing([node]));
|
|
1723
|
+
continue;
|
|
1724
|
+
}
|
|
1725
|
+
if (inline.length > 0) {
|
|
1726
|
+
blocks.push(para(inline));
|
|
1727
|
+
inline = [];
|
|
1728
|
+
}
|
|
1729
|
+
blocks.push(node);
|
|
1730
|
+
}
|
|
1731
|
+
if (inline.length > 0) blocks.push(para(inline));
|
|
1732
|
+
return blocks;
|
|
1733
|
+
};
|
|
1666
1734
|
/**
|
|
1667
1735
|
* Raw HTML elements written into a document are mdxJsx elements too —
|
|
1668
1736
|
* give the meaningful ones a markdown form (`<a>` → link, `<code>` →
|
|
@@ -1672,49 +1740,52 @@ const htmlEl = (node, ctx, inline) => {
|
|
|
1672
1740
|
const name = node.name ?? "";
|
|
1673
1741
|
const kids = () => phrasing(ctx.children(node));
|
|
1674
1742
|
const blocks = () => ctx.children(node);
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
const inner = kids();
|
|
1678
|
-
return href === void 0 ? inner : [link(href, inner)];
|
|
1679
|
-
}
|
|
1680
|
-
if (name === "img") return [{
|
|
1681
|
-
alt: attr(node, "alt"),
|
|
1682
|
-
type: "image",
|
|
1683
|
-
url: attr(node, "src") ?? ""
|
|
1684
|
-
}];
|
|
1685
|
-
if (name === "br") return [brk()];
|
|
1686
|
-
if (name === "strong" || name === "b") return [strong(kids())];
|
|
1687
|
-
if (name === "em" || name === "i") return [em(kids())];
|
|
1688
|
-
if (name === "del" || name === "s" || name === "strike") return [del(kids())];
|
|
1689
|
-
if (name === "code" || name === "kbd" || name === "samp") return [icode(deepText(node.children ?? []))];
|
|
1743
|
+
const inlineRenderer = own(HTML_INLINE, name);
|
|
1744
|
+
if (inlineRenderer !== void 0) return inlineRenderer(node, ctx);
|
|
1690
1745
|
if (name === "hr") return [thematic()];
|
|
1691
1746
|
if (name === "p") return [para(kids())];
|
|
1692
1747
|
if (/^h[1-6]$/u.test(name)) return [heading(Number(name[1]), kids())];
|
|
1693
1748
|
if (name === "blockquote") return [quote(blocks())];
|
|
1694
1749
|
if (name === "pre") return [pre(deepText(node.children ?? []))];
|
|
1695
1750
|
if (name === "ul" || name === "ol") {
|
|
1696
|
-
const items = els(node, "li").map((li) => item(
|
|
1751
|
+
const items = els(node, "li").map((li) => item(blocksOf(ctx.children(li))));
|
|
1697
1752
|
return items.length === 0 ? blocks() : [list(items, name === "ol")];
|
|
1698
1753
|
}
|
|
1699
1754
|
return inline ? kids() : blocks();
|
|
1700
1755
|
};
|
|
1756
|
+
const renderMdxElement = (node, ctx, registry) => {
|
|
1757
|
+
const name = node.name ?? "";
|
|
1758
|
+
const entry = own(registry, name);
|
|
1759
|
+
if (entry === void 0) {
|
|
1760
|
+
if (/^[a-z]/u.test(name)) return htmlEl(node, ctx, node.type === "mdxJsxTextElement");
|
|
1761
|
+
ctx.warn(name);
|
|
1762
|
+
return ctx.children(node);
|
|
1763
|
+
}
|
|
1764
|
+
if (node.type === "mdxJsxTextElement") return entry.text?.(node, ctx) ?? phrasing(entry.flow?.(node, ctx) ?? ctx.children(node));
|
|
1765
|
+
if (entry.flow !== void 0) return entry.flow(node, ctx);
|
|
1766
|
+
const inlineOut = entry.text?.(node, ctx);
|
|
1767
|
+
return inlineOut === void 0 ? ctx.children(node) : [para(inlineOut)];
|
|
1768
|
+
};
|
|
1701
1769
|
/** Replace every mdxJsx element under `root` with its ASCII representation. */
|
|
1702
1770
|
const transformAscii = (root, registry, serialize, warn) => {
|
|
1703
1771
|
const transformElement = (n, ctx) => {
|
|
1704
|
-
if (isMdxEl(n))
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1772
|
+
if (isMdxEl(n)) return renderMdxElement(n, ctx, registry);
|
|
1773
|
+
if (isParent(n)) {
|
|
1774
|
+
if (hoistable(n)) {
|
|
1775
|
+
const children = n.children.filter(isMdxEl).map((child) => ({
|
|
1776
|
+
...child,
|
|
1777
|
+
type: "mdxJsxFlowElement"
|
|
1778
|
+
}));
|
|
1779
|
+
return blocksOf(ctx.children({
|
|
1780
|
+
...n,
|
|
1781
|
+
children
|
|
1782
|
+
}));
|
|
1711
1783
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1784
|
+
return [{
|
|
1785
|
+
...n,
|
|
1786
|
+
children: ctx.children(n)
|
|
1787
|
+
}];
|
|
1716
1788
|
}
|
|
1717
|
-
if (isParent(n)) n.children = ctx.children(n);
|
|
1718
1789
|
return [n];
|
|
1719
1790
|
};
|
|
1720
1791
|
const transformNodes = (nodes, ctx) => nodes.flatMap((node) => {
|
|
@@ -1784,7 +1855,7 @@ const STATUS_ICON = {
|
|
|
1784
1855
|
unverified: "?",
|
|
1785
1856
|
up: "✓"
|
|
1786
1857
|
};
|
|
1787
|
-
const statusIcon = (status) => status === void 0 ? "•" : STATUS_ICON
|
|
1858
|
+
const statusIcon = (status) => status === void 0 ? "•" : own(STATUS_ICON, status.toLowerCase()) ?? "•";
|
|
1788
1859
|
/** Left-align `s` in a `width`-wide column (no padding past the width). */
|
|
1789
1860
|
const pad = (s, width) => s.length >= width ? s : s + " ".repeat(width - s.length);
|
|
1790
1861
|
//#endregion
|
|
@@ -1796,6 +1867,11 @@ const suffix = (parts) => {
|
|
|
1796
1867
|
};
|
|
1797
1868
|
/** Bare `a · b · c` joining (no leading separator). */
|
|
1798
1869
|
const joined = (parts) => parts.filter(nonEmpty).join(" · ");
|
|
1870
|
+
/** A disclosure with literal summary text and an already serialized body. */
|
|
1871
|
+
const detailsBlock = (summary, body) => {
|
|
1872
|
+
const escaped = summary.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
1873
|
+
return html(`<details>\n<summary>${escaped}</summary>\n\n${body.trim()}\n\n</details>`);
|
|
1874
|
+
};
|
|
1799
1875
|
/**
|
|
1800
1876
|
* `**Title — extra**` caption paragraph, emitted only when something is
|
|
1801
1877
|
* there. A title-less caption drops a leading ` — `/space so `extra` alone
|
|
@@ -1824,7 +1900,7 @@ const tally$1 = (entries, key) => {
|
|
|
1824
1900
|
//#endregion
|
|
1825
1901
|
//#region src/ascii/charts.ts
|
|
1826
1902
|
/** Attribute → number list (`values="12, 8, 4"`). */
|
|
1827
|
-
const nums = (node, key) =>
|
|
1903
|
+
const nums = (node, key) => numList(attr(node, key));
|
|
1828
1904
|
/** `label ██████░░ 42` fence rows from name/value pairs. */
|
|
1829
1905
|
const barRows = (rows, max) => {
|
|
1830
1906
|
const W = 24;
|
|
@@ -2190,7 +2266,7 @@ const term = (node, ctx) => [para([strong([txt(attr(node, "name") ?? "")]), ...c
|
|
|
2190
2266
|
const details = (node, ctx) => {
|
|
2191
2267
|
const summary = attr(node, "summary") ?? "Details";
|
|
2192
2268
|
const body = ctx.serialize(ctx.children(node)).trim();
|
|
2193
|
-
return [
|
|
2269
|
+
return [detailsBlock(summary, body)];
|
|
2194
2270
|
};
|
|
2195
2271
|
const documentRenderers = {
|
|
2196
2272
|
Callout: { flow: (n, ctx) => {
|
|
@@ -2286,7 +2362,7 @@ const nodeRows = (node) => {
|
|
|
2286
2362
|
if (grid && isParent(grid)) return tableRecords(grid.children.map((row) => isParent(row) ? row.children.map(textOf) : []));
|
|
2287
2363
|
return els(node, "RecordItem").map(nodeAttributes);
|
|
2288
2364
|
};
|
|
2289
|
-
const recordsTable = (rows, cols) => rows.length ? [table(cols ?? columnsOf(rows), rows.map((row) => (cols ?? columnsOf(rows)).map((key) => display(row
|
|
2365
|
+
const recordsTable = (rows, cols) => rows.length ? [table(cols ?? columnsOf(rows), rows.map((row) => (cols ?? columnsOf(rows)).map((key) => display(own(row, key)) || "—")))] : [para([txt("No data")])];
|
|
2290
2366
|
const narrative = (node, ctx) => {
|
|
2291
2367
|
if (attr(node, "data") !== void 0) return ctx.children(node);
|
|
2292
2368
|
const children = node.children?.filter((child) => {
|
|
@@ -2411,14 +2487,14 @@ Object.assign(extendedRenderers, {
|
|
|
2411
2487
|
"pnpm",
|
|
2412
2488
|
"yarn",
|
|
2413
2489
|
"bun"
|
|
2414
|
-
].map((manager) => `${manager} ${manager === "npm" ? "install" : "add"}${
|
|
2490
|
+
].map((manager) => `${manager} ${manager === "npm" ? "install" : "add"}${flag(node, "dev") ? " -D" : ""} ${attr(node, "packages") ?? ""}`).join("\n"), "sh")] },
|
|
2415
2491
|
PageBreak: { flow: () => [{ type: "thematicBreak" }] },
|
|
2416
2492
|
PdfPreview: { flow: (node) => sourceLink(node, "src", `${attr(node, "title") ?? "PDF document"} (page ${attr(node, "page") ?? "1"})`) },
|
|
2417
2493
|
PrintLayout: { flow: (node, ctx) => ctx.children(node) },
|
|
2418
2494
|
PromptTemplate: { flow: (node) => {
|
|
2419
2495
|
const rows = nodeRows(node);
|
|
2420
2496
|
const defaults = Object.fromEntries(rows.map((row) => [display(row.name), display(row.value)]));
|
|
2421
|
-
const prompt = (attr(node, "template") ?? "").replaceAll(/\{\{\s*(?<variable>[\w.-]+)\s*\}\}/gu, (match, name) => defaults
|
|
2497
|
+
const prompt = (attr(node, "template") ?? "").replaceAll(/\{\{\s*(?<variable>[\w.-]+)\s*\}\}/gu, (match, name) => own(defaults, name) ?? match);
|
|
2422
2498
|
return [
|
|
2423
2499
|
...caption(attr(node, "title") ?? "Prompt template"),
|
|
2424
2500
|
...recordsTable(rows),
|
|
@@ -2534,28 +2610,20 @@ const numbered = (entries, statusKey, ctx) => list(entries.map((e) => item([para
|
|
|
2534
2610
|
])])), true);
|
|
2535
2611
|
const NOTE_RE = /\s+(?:—|#)\s+/u;
|
|
2536
2612
|
const PLACEHOLDER_RE = /^(?:\.{3}|…)$/u;
|
|
2537
|
-
const
|
|
2538
|
-
const
|
|
2539
|
-
const
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
const labelText = li.children.filter((c) => c.type !== "list").map((c) => textOf(c)).join("").trim();
|
|
2545
|
-
const m = NOTE_RE.exec(labelText);
|
|
2546
|
-
out.push({
|
|
2547
|
-
children: treeEntries(nested),
|
|
2548
|
-
name: (m === null ? labelText : labelText.slice(0, m.index)).trim(),
|
|
2549
|
-
...m === null ? {} : { note: labelText.slice(m.index + m[0].length).trim() }
|
|
2550
|
-
});
|
|
2551
|
-
}
|
|
2552
|
-
return;
|
|
2553
|
-
}
|
|
2554
|
-
if (isParent(n)) for (const c of n.children) walk(c);
|
|
2613
|
+
const treeEntry = (node, children) => {
|
|
2614
|
+
const label = node.children.filter((child) => child.type !== "list").map(textOf).join("").trim();
|
|
2615
|
+
const note = NOTE_RE.exec(label);
|
|
2616
|
+
return {
|
|
2617
|
+
children,
|
|
2618
|
+
name: (note === null ? label : label.slice(0, note.index)).trim(),
|
|
2619
|
+
...note === null ? {} : { note: label.slice(note.index + note[0].length).trim() }
|
|
2555
2620
|
};
|
|
2556
|
-
for (const c of nodes) walk(c);
|
|
2557
|
-
return out;
|
|
2558
2621
|
};
|
|
2622
|
+
const treeEntries = (nodes) => nodes.flatMap((node) => {
|
|
2623
|
+
if (!isParent(node)) return [];
|
|
2624
|
+
if (node.type === "list") return node.children.filter((child) => child.type === "listItem" && isParent(child)).map((child) => treeEntry(child, treeEntries(child.children.filter((nested) => nested.type === "list"))));
|
|
2625
|
+
return treeEntries(node.children);
|
|
2626
|
+
});
|
|
2559
2627
|
const treeLines = (entries, prefix) => entries.flatMap((e, i) => {
|
|
2560
2628
|
const last = i === entries.length - 1;
|
|
2561
2629
|
const name = PLACEHOLDER_RE.test(e.name) ? "…" : e.name;
|
|
@@ -2652,14 +2720,18 @@ const investigationRenderers = {
|
|
|
2652
2720
|
...nonEmpty(attr(n, "path")) ? [txt(" — "), icode(pathAt(n))] : [],
|
|
2653
2721
|
...ctx.inline(n).length === 0 ? [] : [txt(": "), ...ctx.inline(n)]
|
|
2654
2722
|
])] },
|
|
2655
|
-
Hypotheses: { flow: (n, ctx) => [
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2723
|
+
Hypotheses: { flow: (n, ctx) => [
|
|
2724
|
+
...caption(attr(n, "title")),
|
|
2725
|
+
list(els(n, "Hypothesis").map((h) => {
|
|
2726
|
+
const status = (attr(h, "status") ?? "untested").toLowerCase();
|
|
2727
|
+
return item([para([
|
|
2728
|
+
txt(`${statusIcon(status)} `),
|
|
2729
|
+
...nonEmpty(attr(h, "title")) ? [strong([txt(attr(h, "title") ?? "")])] : [],
|
|
2730
|
+
...ctx.inline(h).length === 0 ? [] : [txt(" — "), ...ctx.inline(h)]
|
|
2731
|
+
])], status === "supported" ? true : void 0);
|
|
2732
|
+
})),
|
|
2733
|
+
...ctx.children(withoutEls(n, ["Hypothesis"]))
|
|
2734
|
+
] },
|
|
2663
2735
|
Hypothesis: { flow: (n, ctx) => [para([txt(`${statusIcon(attr(n, "status"))} `), ...nonEmpty(attr(n, "title")) ? [strong([txt(attr(n, "title") ?? "")])] : []]), ...ctx.children(n)] },
|
|
2664
2736
|
Prop: { flow: (n, ctx) => [para([
|
|
2665
2737
|
icode(attr(n, "name") ?? ""),
|
|
@@ -2784,12 +2856,13 @@ const comment = (node, ctx) => {
|
|
|
2784
2856
|
const graph = (node, ctx) => {
|
|
2785
2857
|
const nodes = els(node, "Node");
|
|
2786
2858
|
const edges = els(node, "Edge");
|
|
2859
|
+
const connected = new Set(edges.flatMap((edge) => [attr(edge, "from"), attr(edge, "to")]));
|
|
2787
2860
|
const nodeNotes = nodes.flatMap((nd) => {
|
|
2788
2861
|
const label = attr(nd, "label");
|
|
2789
2862
|
const note = attr(nd, "note");
|
|
2790
2863
|
const path = attr(nd, "path");
|
|
2791
2864
|
const status = attr(nd, "status");
|
|
2792
|
-
if ((label === void 0 || label === attr(nd, "id")) && note === void 0 && path === void 0 && status === void 0) return [];
|
|
2865
|
+
if ((label === void 0 || label === attr(nd, "id")) && note === void 0 && path === void 0 && status === void 0 && connected.has(attr(nd, "id"))) return [];
|
|
2793
2866
|
const head = [
|
|
2794
2867
|
icode(attr(nd, "id") ?? ""),
|
|
2795
2868
|
txt(" = "),
|
|
@@ -2912,9 +2985,10 @@ const waterfall = (node, ctx) => {
|
|
|
2912
2985
|
const title = attr(node, "title");
|
|
2913
2986
|
if (nonEmpty(title)) lines.push(`${title} (0 → ${total}${unit})`);
|
|
2914
2987
|
for (const s of spans) {
|
|
2915
|
-
const
|
|
2916
|
-
const
|
|
2917
|
-
const
|
|
2988
|
+
const scale = Math.max(1, total);
|
|
2989
|
+
const from = Math.max(0, Math.min(W, Math.round(s.start / scale * W)));
|
|
2990
|
+
const to = Math.max(from, Math.min(W, Math.round((s.start + Math.max(0, s.duration)) / scale * W)));
|
|
2991
|
+
const cells = `${" ".repeat(from)}${"█".repeat(to - from)}`;
|
|
2918
2992
|
lines.push(`${pad(s.name, nameW)} ${cells} ${s.durationRaw || `${s.duration}${unit}`}${nonEmpty(s.note) ? ` ${s.note}` : ""}`);
|
|
2919
2993
|
}
|
|
2920
2994
|
return [pre(lines.join("\n")), ...ctx.children(withoutEls(node, ["Span"]))];
|
|
@@ -2970,6 +3044,7 @@ const outputRenderers = {
|
|
|
2970
3044
|
};
|
|
2971
3045
|
//#endregion
|
|
2972
3046
|
//#region src/ascii/planning.ts
|
|
3047
|
+
/** ASCII renderers for planning & status components. */
|
|
2973
3048
|
/** `owner` attr → a normalized `@name` chip (leading `@`s collapsed). */
|
|
2974
3049
|
const ownerChip = (node) => {
|
|
2975
3050
|
const owner = attr(node, "owner");
|
|
@@ -3009,8 +3084,8 @@ const approvalStatus = (s) => {
|
|
|
3009
3084
|
const DAY_MS = 864e5;
|
|
3010
3085
|
const dayOf = (iso) => {
|
|
3011
3086
|
if (iso === void 0) return;
|
|
3012
|
-
const
|
|
3013
|
-
return
|
|
3087
|
+
const date = parseISO(iso);
|
|
3088
|
+
return isValid(date) ? differenceInCalendarDays(date, new Date(1970, 0, 1)) : void 0;
|
|
3014
3089
|
};
|
|
3015
3090
|
const TASK_CHAR = {
|
|
3016
3091
|
blocked: "▒",
|
|
@@ -3018,56 +3093,75 @@ const TASK_CHAR = {
|
|
|
3018
3093
|
done: "█",
|
|
3019
3094
|
todo: "░"
|
|
3020
3095
|
};
|
|
3096
|
+
const GANTT_WIDTH = 40;
|
|
3097
|
+
const ganttTasks = (node) => els(node, "Task").flatMap((el) => {
|
|
3098
|
+
const start = dayOf(attr(el, "start"));
|
|
3099
|
+
if (start === void 0) return [];
|
|
3100
|
+
return [{
|
|
3101
|
+
el,
|
|
3102
|
+
end: Math.max(start, dayOf(attr(el, "end")) ?? start),
|
|
3103
|
+
name: attr(el, "name") ?? "",
|
|
3104
|
+
progress: num(el, "progress"),
|
|
3105
|
+
start,
|
|
3106
|
+
status: (attr(el, "status") ?? "todo").toLowerCase()
|
|
3107
|
+
}];
|
|
3108
|
+
});
|
|
3109
|
+
const ganttMilestones = (node) => els(node, "Milestone").flatMap((el) => {
|
|
3110
|
+
const date = dayOf(attr(el, "date"));
|
|
3111
|
+
return date === void 0 ? [] : [{
|
|
3112
|
+
date,
|
|
3113
|
+
el,
|
|
3114
|
+
name: attr(el, "name") ?? ""
|
|
3115
|
+
}];
|
|
3116
|
+
});
|
|
3117
|
+
const ganttRange = (node, tasks, milestones) => {
|
|
3118
|
+
const start = dayOf(attr(node, "start"));
|
|
3119
|
+
const end = dayOf(attr(node, "end"));
|
|
3120
|
+
let first = start ?? Math.min(...tasks.map((task) => task.start), ...milestones.map((milestone) => milestone.date));
|
|
3121
|
+
let last = end ?? Math.max(...tasks.map((task) => task.end), ...milestones.map((milestone) => milestone.date));
|
|
3122
|
+
if (last < first) {
|
|
3123
|
+
if (end !== void 0 && start === void 0) first = last;
|
|
3124
|
+
else last = first;
|
|
3125
|
+
}
|
|
3126
|
+
return {
|
|
3127
|
+
first,
|
|
3128
|
+
last
|
|
3129
|
+
};
|
|
3130
|
+
};
|
|
3131
|
+
const ganttColumn = (day, range) => Math.max(0, Math.min(GANTT_WIDTH, Math.round((day - range.first) / (range.last - range.first + 1) * GANTT_WIDTH)));
|
|
3132
|
+
const ganttTaskLine = (task, range, nameWidth) => {
|
|
3133
|
+
const from = ganttColumn(task.start, range);
|
|
3134
|
+
const to = Math.max(from + 1, ganttColumn(task.end + 1, range));
|
|
3135
|
+
const outside = task.end < range.first || task.start > range.last;
|
|
3136
|
+
const glyph = own(TASK_CHAR, task.status) ?? "░";
|
|
3137
|
+
const filled = (to - from) * Math.min(100, Math.max(0, task.progress ?? 0)) / 100;
|
|
3138
|
+
const cells = Array.from({ length: GANTT_WIDTH }, (_, i) => {
|
|
3139
|
+
if (outside || i < from || i >= to) return " ";
|
|
3140
|
+
return i - from < filled ? "█" : glyph;
|
|
3141
|
+
}).join("");
|
|
3142
|
+
const tail = joined([ownerChip(task.el), attr(task.el, "note")]);
|
|
3143
|
+
return `${pad(task.name, nameWidth)} ${cells}${tail === "" ? "" : ` ${tail}`}`;
|
|
3144
|
+
};
|
|
3145
|
+
const ganttMilestoneLine = (milestone, range, nameWidth) => {
|
|
3146
|
+
const position = milestone.date < range.first || milestone.date > range.last ? -1 : Math.min(39, ganttColumn(milestone.date, range));
|
|
3147
|
+
const cells = Array.from({ length: GANTT_WIDTH }, (_, i) => i === position ? "◆" : " ").join("");
|
|
3148
|
+
const tail = joined([attr(milestone.el, "status"), attr(milestone.el, "note")]);
|
|
3149
|
+
return `${pad(milestone.name, nameWidth)} ${cells}${tail === "" ? "" : ` ${tail}`}`;
|
|
3150
|
+
};
|
|
3021
3151
|
const gantt = (node, ctx) => {
|
|
3022
|
-
const tasks =
|
|
3023
|
-
|
|
3024
|
-
return start === void 0 ? [] : [{
|
|
3025
|
-
el: t,
|
|
3026
|
-
end: dayOf(attr(t, "end")) ?? start,
|
|
3027
|
-
name: attr(t, "name") ?? "",
|
|
3028
|
-
progress: num(t, "progress"),
|
|
3029
|
-
start,
|
|
3030
|
-
status: (attr(t, "status") ?? "todo").toLowerCase()
|
|
3031
|
-
}];
|
|
3032
|
-
});
|
|
3033
|
-
const milestones = els(node, "Milestone").flatMap((m) => {
|
|
3034
|
-
const date = dayOf(attr(m, "date"));
|
|
3035
|
-
return date === void 0 ? [] : [{
|
|
3036
|
-
date,
|
|
3037
|
-
el: m,
|
|
3038
|
-
name: attr(m, "name") ?? ""
|
|
3039
|
-
}];
|
|
3040
|
-
});
|
|
3152
|
+
const tasks = ganttTasks(node);
|
|
3153
|
+
const milestones = ganttMilestones(node);
|
|
3041
3154
|
if (tasks.length === 0 && milestones.length === 0) return ctx.children(node);
|
|
3042
|
-
const
|
|
3043
|
-
const
|
|
3044
|
-
const
|
|
3045
|
-
const W = 40;
|
|
3046
|
-
const at = (day) => Math.max(0, Math.min(W, Math.round((day - lo) / span * W)));
|
|
3047
|
-
const nameW = Math.min(24, Math.max(8, ...tasks.map((t) => t.name.length), ...milestones.map((m) => m.name.length)));
|
|
3048
|
-
const iso = (d) => (/* @__PURE__ */ new Date(d * DAY_MS)).toISOString().slice(0, 10);
|
|
3049
|
-
const lines = [];
|
|
3155
|
+
const range = ganttRange(node, tasks, milestones);
|
|
3156
|
+
const nameWidth = Math.min(24, Math.max(8, ...tasks.map((task) => task.name.length), ...milestones.map((milestone) => milestone.name.length)));
|
|
3157
|
+
const iso = (day) => (/* @__PURE__ */ new Date(day * DAY_MS)).toISOString().slice(0, 10);
|
|
3050
3158
|
const title = attr(node, "title");
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
if (i < from || i >= to) return " ";
|
|
3058
|
-
const ch = TASK_CHAR[t.status] ?? "░";
|
|
3059
|
-
if (t.progress === void 0 || t.progress <= 0) return ch;
|
|
3060
|
-
return i - from < (to - from) * Math.min(100, t.progress) / 100 ? "█" : ch;
|
|
3061
|
-
}).join("");
|
|
3062
|
-
const owner = attr(t.el, "owner");
|
|
3063
|
-
const tail = joined([nonEmpty(owner) ? `@${owner.replace(/^@+/u, "")}` : void 0, attr(t.el, "note")]);
|
|
3064
|
-
lines.push(`${pad(t.name, nameW)} ${cells}${tail === "" ? "" : ` ${tail}`}`);
|
|
3065
|
-
}
|
|
3066
|
-
for (const m of milestones) {
|
|
3067
|
-
const cells = Array.from({ length: W }, (_, i) => i === at(m.date) ? "◆" : " ").join("");
|
|
3068
|
-
const tail = joined([attr(m.el, "status"), attr(m.el, "note")]);
|
|
3069
|
-
lines.push(`${pad(m.name, nameW)} ${cells}${tail === "" ? "" : ` ${tail}`}`);
|
|
3070
|
-
}
|
|
3159
|
+
const lines = [
|
|
3160
|
+
...nonEmpty(title) ? [title] : [],
|
|
3161
|
+
`${pad("", nameWidth)} ${iso(range.first)} → ${iso(range.last)}`,
|
|
3162
|
+
...tasks.map((task) => ganttTaskLine(task, range, nameWidth)),
|
|
3163
|
+
...milestones.map((milestone) => ganttMilestoneLine(milestone, range, nameWidth))
|
|
3164
|
+
];
|
|
3071
3165
|
return [pre(lines.join("\n")), ...ctx.children(withoutEls(node, ["Task", "Milestone"]))];
|
|
3072
3166
|
};
|
|
3073
3167
|
const CELL_GLYPH = {
|
|
@@ -3103,7 +3197,7 @@ const CELL_GLYPH = {
|
|
|
3103
3197
|
};
|
|
3104
3198
|
const matrixCell = (cell) => {
|
|
3105
3199
|
const key = cell.trim().toLowerCase();
|
|
3106
|
-
return key === "" ? "—" : CELL_GLYPH
|
|
3200
|
+
return key === "" ? "—" : own(CELL_GLYPH, key) ?? cell.trim();
|
|
3107
3201
|
};
|
|
3108
3202
|
/** `- label | a | b` list items → matrix rows. */
|
|
3109
3203
|
const matrixRows = (node) => {
|
|
@@ -3135,12 +3229,16 @@ const planningRenderers = {
|
|
|
3135
3229
|
attr(a, "role"),
|
|
3136
3230
|
attr(a, "status"),
|
|
3137
3231
|
attr(a, "date")
|
|
3138
|
-
])],
|
|
3232
|
+
])], ctx.children(a)))), ...ctx.children(withoutEls(n, ["Approval"]))] },
|
|
3139
3233
|
Board: { flow: (n, ctx) => [
|
|
3140
3234
|
...caption(attr(n, "title")),
|
|
3141
3235
|
...els(n, "Lane").flatMap((lane) => {
|
|
3142
3236
|
const cards = els(lane, "BoardCard");
|
|
3143
|
-
return [
|
|
3237
|
+
return [
|
|
3238
|
+
para([txt(`${statusIcon(attr(lane, "status"))} `), strong([txt(`${attr(lane, "title") ?? ""} (${cards.length})`)])]),
|
|
3239
|
+
list(cards.map((c) => boardCard(c, ctx))),
|
|
3240
|
+
...ctx.children(withoutEls(lane, ["BoardCard"]))
|
|
3241
|
+
];
|
|
3144
3242
|
}),
|
|
3145
3243
|
...ctx.children(withoutEls(n, ["Lane"]))
|
|
3146
3244
|
] },
|
|
@@ -3202,11 +3300,11 @@ const planningRenderers = {
|
|
|
3202
3300
|
])],
|
|
3203
3301
|
text: (n) => [strong([txt(attr(n, "value") ?? "")]), txt(` ${attr(n, "label") ?? ""}`)]
|
|
3204
3302
|
},
|
|
3205
|
-
Stats: { flow: (n) => [list(els(n, "Stat").map((s) => item([para([
|
|
3303
|
+
Stats: { flow: (n, ctx) => [list(els(n, "Stat").map((s) => item([para([
|
|
3206
3304
|
strong([txt(attr(s, "value") ?? "")]),
|
|
3207
3305
|
txt(` ${attr(s, "label") ?? ""}`),
|
|
3208
3306
|
...nonEmpty(attr(s, "delta")) ? [txt(` (${attr(s, "delta")})`)] : []
|
|
3209
|
-
])])))] },
|
|
3307
|
+
])]))), ...ctx.children(withoutEls(n, ["Stat"]))] },
|
|
3210
3308
|
StatusBadge: { text: (n) => [icode(attr(n, "status") ?? "")] },
|
|
3211
3309
|
Step: { flow: (n, ctx) => [para([
|
|
3212
3310
|
txt(`${statusIcon(attr(n, "status"))} `),
|
|
@@ -3269,7 +3367,7 @@ const VERDICT_LABEL = {
|
|
|
3269
3367
|
pass: "Passed",
|
|
3270
3368
|
warn: "Warning"
|
|
3271
3369
|
};
|
|
3272
|
-
const verdictLabel = (s) => s === void 0 ? void 0 : VERDICT_LABEL
|
|
3370
|
+
const verdictLabel = (s) => s === void 0 ? void 0 : own(VERDICT_LABEL, s.toLowerCase()) ?? s;
|
|
3273
3371
|
const DURATION_MS = {
|
|
3274
3372
|
h: 36e5,
|
|
3275
3373
|
m: 6e4,
|
|
@@ -3318,9 +3416,14 @@ const DAY_CELL = {
|
|
|
3318
3416
|
const uptimeStrip = (node) => {
|
|
3319
3417
|
const days = els(node, "Day");
|
|
3320
3418
|
if (days.length === 0) return "";
|
|
3321
|
-
return days.map((d) => DAY_CELL
|
|
3419
|
+
return days.map((d) => own(DAY_CELL, (attr(d, "status") ?? "up").toLowerCase()) ?? "█").join("");
|
|
3420
|
+
};
|
|
3421
|
+
const percentText = (value) => nonEmpty(value) && !value.endsWith("%") ? `${value}%` : value;
|
|
3422
|
+
const serviceRow = (node, ctx) => statusItem$1(attr(node, "status"), [txt(attr(node, "name") ?? ""), ...suffix([attr(node, "status"), percentText(attr(node, "uptime"))])], ctx.children(node));
|
|
3423
|
+
const releaseVersion = (node) => {
|
|
3424
|
+
const version = attr(node, "version") ?? "";
|
|
3425
|
+
return version.startsWith("v") ? version : `v${version}`;
|
|
3322
3426
|
};
|
|
3323
|
-
const serviceRow = (node) => statusItem$1(attr(node, "status"), [txt(attr(node, "name") ?? ""), ...suffix([attr(node, "status"), nonEmpty(attr(node, "uptime")) ? `${attr(node, "uptime")}%` : void 0])], []);
|
|
3324
3427
|
const entryRow = (node, ctx) => {
|
|
3325
3428
|
const scope = attr(node, "scope");
|
|
3326
3429
|
return item([para([
|
|
@@ -3368,7 +3471,7 @@ const reportRenderers = {
|
|
|
3368
3471
|
attr(b, "before") ?? "",
|
|
3369
3472
|
attr(b, "after") ?? "",
|
|
3370
3473
|
d,
|
|
3371
|
-
attr(b, "note")
|
|
3474
|
+
joined([attr(b, "note"), ctx.text(b)])
|
|
3372
3475
|
];
|
|
3373
3476
|
})),
|
|
3374
3477
|
...ctx.children(withoutEls(n, ["Bench"]))
|
|
@@ -3439,7 +3542,8 @@ const reportRenderers = {
|
|
|
3439
3542
|
"field",
|
|
3440
3543
|
"type",
|
|
3441
3544
|
"flags",
|
|
3442
|
-
"default"
|
|
3545
|
+
"default",
|
|
3546
|
+
"description"
|
|
3443
3547
|
], els(n, "DbField").map((f) => {
|
|
3444
3548
|
const flags = [
|
|
3445
3549
|
flag(f, "pk") ? "pk" : void 0,
|
|
@@ -3451,7 +3555,8 @@ const reportRenderers = {
|
|
|
3451
3555
|
[icode(attr(f, "name") ?? "")],
|
|
3452
3556
|
[icode(attr(f, "type") ?? "")],
|
|
3453
3557
|
flags,
|
|
3454
|
-
attr(f, "default") ?? ""
|
|
3558
|
+
attr(f, "default") ?? "",
|
|
3559
|
+
ctx.text(f)
|
|
3455
3560
|
];
|
|
3456
3561
|
})),
|
|
3457
3562
|
...ctx.children(withoutEls(n, ["DbField"]))
|
|
@@ -3464,7 +3569,7 @@ const reportRenderers = {
|
|
|
3464
3569
|
EnvVar: { flow: (n, ctx) => [para([
|
|
3465
3570
|
icode(attr(n, "name") ?? ""),
|
|
3466
3571
|
...flag(n, "required") ? [txt(" "), icode("required")] : [],
|
|
3467
|
-
...suffix([flag(n, "secret") ? "secret" : attr(n, "value"), nonEmpty(attr(n, "default")) ? `default ${attr(n, "default")}` : void 0]),
|
|
3572
|
+
...suffix([flag(n, "secret") ? "secret" : attr(n, "value"), !flag(n, "secret") && nonEmpty(attr(n, "default")) ? `default ${attr(n, "default")}` : void 0]),
|
|
3468
3573
|
...ctx.inline(n).length === 0 ? [] : [txt(" — "), ...ctx.inline(n)]
|
|
3469
3574
|
])] },
|
|
3470
3575
|
EnvVars: { flow: (n, ctx) => [
|
|
@@ -3479,7 +3584,7 @@ const reportRenderers = {
|
|
|
3479
3584
|
[icode(attr(e, "name") ?? "")],
|
|
3480
3585
|
flag(e, "required") ? "yes" : "",
|
|
3481
3586
|
flag(e, "secret") ? "•••" : attr(e, "value") ?? "",
|
|
3482
|
-
attr(e, "default") ?? "",
|
|
3587
|
+
flag(e, "secret") ? "•••" : attr(e, "default") ?? "",
|
|
3483
3588
|
ctx.text(e)
|
|
3484
3589
|
])),
|
|
3485
3590
|
...ctx.children(withoutEls(n, ["EnvVar"]))
|
|
@@ -3553,7 +3658,7 @@ const reportRenderers = {
|
|
|
3553
3658
|
attr(p, "version") ?? "",
|
|
3554
3659
|
attr(p, "kind") ?? "",
|
|
3555
3660
|
attr(p, "license") ?? "",
|
|
3556
|
-
ctx.text(p)
|
|
3661
|
+
joined([attr(p, "note"), ctx.text(p)])
|
|
3557
3662
|
])),
|
|
3558
3663
|
...ctx.children(withoutEls(n, ["Package"]))
|
|
3559
3664
|
] },
|
|
@@ -3563,17 +3668,17 @@ const reportRenderers = {
|
|
|
3563
3668
|
...ctx.children(withoutEls(n, ["Stop"]))
|
|
3564
3669
|
] },
|
|
3565
3670
|
Release: { flow: (n, ctx) => [
|
|
3566
|
-
heading(3, [...nonEmpty(attr(n, "href")) ? [link(attr(n, "href") ?? "", [txt(
|
|
3671
|
+
heading(3, [...nonEmpty(attr(n, "href")) ? [link(attr(n, "href") ?? "", [txt(releaseVersion(n))])] : [icode(releaseVersion(n))], ...nonEmpty(attr(n, "title")) ? [txt(` — ${attr(n, "title")}`)] : []]),
|
|
3567
3672
|
...nonEmpty(attr(n, "date")) ? [para([em([txt(attr(n, "date") ?? "")])])] : [],
|
|
3568
3673
|
...els(n, "Entry").length === 0 ? ctx.children(n) : [list(els(n, "Entry").map((e) => entryRow(e, ctx))), ...ctx.children(withoutEls(n, ["Entry"]))]
|
|
3569
3674
|
] },
|
|
3570
3675
|
Review: { flow: (n, ctx) => [...caption(attr(n, "title"), `${nonEmpty(attr(n, "verdict")) ? ` — ${verdictLabel(attr(n, "verdict"))}` : ""}${tally$1(els(n, "Comment"), "severity")}`), ...ctx.children(n)] },
|
|
3571
3676
|
Schema: { flow: (n, ctx) => [...caption(attr(n, "title"), nonEmpty(attr(n, "engine")) ? ` (${attr(n, "engine")})` : ""), ...ctx.children(n)] },
|
|
3572
|
-
Service: { flow: (n) => [para([
|
|
3677
|
+
Service: { flow: (n, ctx) => [para([
|
|
3573
3678
|
txt(`${statusIcon(attr(n, "status"))} `),
|
|
3574
3679
|
txt(attr(n, "name") ?? ""),
|
|
3575
|
-
...suffix([attr(n, "status"),
|
|
3576
|
-
])] },
|
|
3680
|
+
...suffix([attr(n, "status"), percentText(attr(n, "uptime"))])
|
|
3681
|
+
]), ...ctx.children(n)] },
|
|
3577
3682
|
Severity: { text: (n) => [icode((attr(n, "level") ?? "").toUpperCase())] },
|
|
3578
3683
|
StatusPage: { flow: (n, ctx) => {
|
|
3579
3684
|
const services = els(n, "Service");
|
|
@@ -3588,7 +3693,7 @@ const reportRenderers = {
|
|
|
3588
3693
|
...suffix([[attr(u, "from"), attr(u, "to")].filter(nonEmpty).join(" → ")])
|
|
3589
3694
|
]), ...strip === "" ? [] : [pre(strip)]];
|
|
3590
3695
|
}),
|
|
3591
|
-
...services.length === 0 ? [] : [list(services.map(serviceRow))],
|
|
3696
|
+
...services.length === 0 ? [] : [list(services.map((service) => serviceRow(service, ctx)))],
|
|
3592
3697
|
...ctx.children(withoutEls(n, ["Service", "Uptime"]))
|
|
3593
3698
|
];
|
|
3594
3699
|
} },
|
|
@@ -3622,6 +3727,48 @@ const reportRenderers = {
|
|
|
3622
3727
|
]), ...ctx.children(n)] }
|
|
3623
3728
|
};
|
|
3624
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
|
|
3625
3772
|
//#region src/ascii/shadcn.ts
|
|
3626
3773
|
/** `ctx.inline` result wrapped so empty children fall back to `alt`. */
|
|
3627
3774
|
const inlineOr = (node, ctx, alt) => {
|
|
@@ -3661,33 +3808,39 @@ const menuItem = {
|
|
|
3661
3808
|
text: (n, ctx) => ctx.inline(n)
|
|
3662
3809
|
};
|
|
3663
3810
|
/** `[x]`/`[ ]`/`( )` control from a checked-ish attribute. */
|
|
3811
|
+
const controlOn = (n) => {
|
|
3812
|
+
if (flag(n, "checked") || flagOff(n, "checked")) return flag(n, "checked");
|
|
3813
|
+
return flag(n, "defaultChecked") || flag(n, "pressed");
|
|
3814
|
+
};
|
|
3664
3815
|
const control = (mark) => ({
|
|
3665
3816
|
flow: (n, ctx) => {
|
|
3666
3817
|
const inner = ctx.inline(n);
|
|
3667
3818
|
const label = attr(n, "label");
|
|
3668
3819
|
return [para([
|
|
3669
|
-
icode(mark(
|
|
3820
|
+
icode(mark(controlOn(n))),
|
|
3670
3821
|
...inner.length === 0 ? [] : [txt(" "), ...inner],
|
|
3671
3822
|
...nonEmpty(label) ? [txt(` ${label}`)] : []
|
|
3672
3823
|
])];
|
|
3673
3824
|
},
|
|
3674
|
-
text: (n, ctx) => [icode(mark(
|
|
3825
|
+
text: (n, ctx) => [icode(mark(controlOn(n))), ...ctx.inline(n).length === 0 ? [] : [txt(" "), ...ctx.inline(n)]]
|
|
3675
3826
|
});
|
|
3676
3827
|
/** `` `[____]` ``-style empty input stand-in. */
|
|
3828
|
+
const inputText = (n) => {
|
|
3829
|
+
const value = attr(n, "value") ?? attr(n, "defaultValue");
|
|
3830
|
+
if (value !== void 0) return attr(n, "type") === "password" ? "••••" : value;
|
|
3831
|
+
return attr(n, "placeholder") ?? attr(n, "label") ?? "____";
|
|
3832
|
+
};
|
|
3677
3833
|
const input = {
|
|
3678
|
-
flow: (n) => [pre(`[ ${
|
|
3679
|
-
text: (n) => [icode(`[${
|
|
3834
|
+
flow: (n) => [pre(`[ ${inputText(n)} ]`)],
|
|
3835
|
+
text: (n) => [icode(`[${inputText(n)}]`)]
|
|
3680
3836
|
};
|
|
3681
3837
|
/** `<details>` disclosure — summary from AccordionTrigger/collapsible label. */
|
|
3682
3838
|
const disclosure = (trigger, fallback) => ({ flow: (n, ctx) => {
|
|
3683
3839
|
const [head] = els(n, trigger);
|
|
3684
3840
|
const summary = head === void 0 ? attr(n, "label") ?? fallback : ctx.text(head);
|
|
3685
|
-
const rest = ctx.children(
|
|
3686
|
-
...n,
|
|
3687
|
-
children: (n.children ?? []).filter((c) => !named(c, trigger))
|
|
3688
|
-
});
|
|
3841
|
+
const rest = ctx.children(withoutEls(n, [trigger]));
|
|
3689
3842
|
const body = ctx.serialize(rest).trim();
|
|
3690
|
-
return [
|
|
3843
|
+
return [detailsBlock(summary, body)];
|
|
3691
3844
|
} });
|
|
3692
3845
|
/** `<Tabs>` → `**tab**` headings + content sections. */
|
|
3693
3846
|
const tabs = { flow: (n, ctx) => {
|
|
@@ -3762,7 +3915,7 @@ const frac = (n) => {
|
|
|
3762
3915
|
const max = num(n, "max") ?? 100;
|
|
3763
3916
|
return {
|
|
3764
3917
|
text: `${attr(n, "value") ?? Math.round(value)}${nonEmpty(attr(n, "max")) ? `/${attr(n, "max")}` : "%"}`,
|
|
3765
|
-
v: max
|
|
3918
|
+
v: max <= 0 ? 0 : Math.max(0, Math.min(1, value / max))
|
|
3766
3919
|
};
|
|
3767
3920
|
};
|
|
3768
3921
|
const progress = {
|
|
@@ -3813,7 +3966,7 @@ const otp = {
|
|
|
3813
3966
|
return [pre(`[ ${"_ ".repeat(count).trim()} ]`)];
|
|
3814
3967
|
},
|
|
3815
3968
|
text: (n) => {
|
|
3816
|
-
const count = num(n, "maxLength") ?? 6;
|
|
3969
|
+
const count = Math.max(0, num(n, "maxLength") ?? 6);
|
|
3817
3970
|
return [icode(`[${"_".repeat(count)}]`)];
|
|
3818
3971
|
}
|
|
3819
3972
|
};
|
|
@@ -4015,6 +4168,7 @@ const asciiRenderers = {
|
|
|
4015
4168
|
...investigationRenderers,
|
|
4016
4169
|
...outputRenderers,
|
|
4017
4170
|
...reportRenderers,
|
|
4171
|
+
...researchRenderers,
|
|
4018
4172
|
...chartRenderers,
|
|
4019
4173
|
...layoutRenderers,
|
|
4020
4174
|
...formRenderers,
|
|
@@ -4091,50 +4245,54 @@ const mdxToAscii = async (source, filePath = "document.mdx") => {
|
|
|
4091
4245
|
};
|
|
4092
4246
|
//#endregion
|
|
4093
4247
|
//#region src/catalog.ts
|
|
4248
|
+
const OPTIONAL_TYPES = /* @__PURE__ */ new Set([
|
|
4249
|
+
"exact_optional",
|
|
4250
|
+
"nullish",
|
|
4251
|
+
"optional"
|
|
4252
|
+
]);
|
|
4253
|
+
const describeLiteral = (value) => {
|
|
4254
|
+
if (typeof value === "bigint") return `${value}n`;
|
|
4255
|
+
return JSON.stringify(value) ?? String(value);
|
|
4256
|
+
};
|
|
4257
|
+
const pipeConstraint = (schema) => {
|
|
4258
|
+
if (!Array.isArray(schema.pipe)) return;
|
|
4259
|
+
return schema.pipe.find((s) => isRecord(s) && (s.type === "picklist" || s.type === "union" || s.type === "literal"));
|
|
4260
|
+
};
|
|
4094
4261
|
/** Structural view of a valibot schema — enough for catalog introspection. */
|
|
4095
4262
|
const describeSchema = (schema) => {
|
|
4096
4263
|
if (!isRecord(schema)) return "unknown";
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4264
|
+
switch (schema.type) {
|
|
4265
|
+
case "nullable":
|
|
4266
|
+
case "nullish": return `${describeSchema(schema.wrapped)} | null`;
|
|
4267
|
+
case "optional":
|
|
4268
|
+
case "exact_optional": return describeSchema(schema.wrapped);
|
|
4269
|
+
case "array": {
|
|
4270
|
+
const item = describeSchema(schema.item);
|
|
4271
|
+
return item.includes(" | ") ? `(${item})[]` : `${item}[]`;
|
|
4272
|
+
}
|
|
4273
|
+
case "literal": return describeLiteral(schema.literal);
|
|
4274
|
+
case "picklist":
|
|
4275
|
+
case "union": if (Array.isArray(schema.options)) {
|
|
4276
|
+
const describe = schema.type === "picklist" ? describeLiteral : describeSchema;
|
|
4277
|
+
return schema.options.map(describe).join(" | ");
|
|
4278
|
+
}
|
|
4104
4279
|
}
|
|
4280
|
+
const constrained = pipeConstraint(schema);
|
|
4281
|
+
if (constrained !== void 0) return describeSchema(constrained);
|
|
4105
4282
|
return typeof schema.type === "string" ? schema.type : "unknown";
|
|
4106
4283
|
};
|
|
4107
|
-
const WRAPPER_TYPES = /* @__PURE__ */ new Set([
|
|
4108
|
-
"nullable",
|
|
4109
|
-
"nullish",
|
|
4110
|
-
"optional"
|
|
4111
|
-
]);
|
|
4112
|
-
const unwrapProp = (raw) => {
|
|
4113
|
-
let s = isRecord(raw) ? raw : {};
|
|
4114
|
-
let required = true;
|
|
4115
|
-
let def;
|
|
4116
|
-
while (typeof s.type === "string" && WRAPPER_TYPES.has(s.type)) {
|
|
4117
|
-
required = false;
|
|
4118
|
-
def ??= s.default;
|
|
4119
|
-
s = isRecord(s.wrapped) ? s.wrapped : {};
|
|
4120
|
-
}
|
|
4121
|
-
return {
|
|
4122
|
-
def,
|
|
4123
|
-
required,
|
|
4124
|
-
s
|
|
4125
|
-
};
|
|
4126
|
-
};
|
|
4127
4284
|
const propsOf = (schema) => {
|
|
4128
4285
|
if (!isRecord(schema) || !isRecord(schema.entries)) return {};
|
|
4129
4286
|
const out = {};
|
|
4130
4287
|
for (const [key, raw] of Object.entries(schema.entries)) {
|
|
4131
|
-
const
|
|
4288
|
+
const optional = isRecord(raw) && typeof raw.type === "string" && OPTIONAL_TYPES.has(raw.type);
|
|
4289
|
+
const def = optional ? raw.default : void 0;
|
|
4132
4290
|
Object.defineProperty(out, key, {
|
|
4133
4291
|
configurable: true,
|
|
4134
4292
|
enumerable: true,
|
|
4135
4293
|
value: {
|
|
4136
|
-
required,
|
|
4137
|
-
type: describeSchema(
|
|
4294
|
+
required: !optional,
|
|
4295
|
+
type: describeSchema(raw),
|
|
4138
4296
|
...def === void 0 ? {} : { default: def }
|
|
4139
4297
|
},
|
|
4140
4298
|
writable: true
|
|
@@ -4883,14 +5041,16 @@ const iconSets = (usedIcons) => ({
|
|
|
4883
5041
|
*/
|
|
4884
5042
|
const scanVProps = (stripped, alias) => {
|
|
4885
5043
|
const esc = alias.replaceAll(/[$()*+.?[\\\]^{|}]/gu, "\\$&");
|
|
4886
|
-
const
|
|
5044
|
+
const identifierPart = String.raw`[\p{ID_Continue}$\u200C\u200D]`;
|
|
5045
|
+
const start = `(?<!${identifierPart})${esc}`;
|
|
5046
|
+
const access = new RegExp(`${start}\\s*\\??\\.\\s*(\\w+)`, "gu");
|
|
4887
5047
|
const props = /* @__PURE__ */ new Set();
|
|
4888
5048
|
for (const use of stripped.matchAll(access)) {
|
|
4889
|
-
const prop = use
|
|
5049
|
+
const [, prop] = use;
|
|
4890
5050
|
if (prop !== void 0) props.add(prop);
|
|
4891
5051
|
}
|
|
4892
5052
|
const leftover = stripped.replaceAll(access, "");
|
|
4893
|
-
return new RegExp(
|
|
5053
|
+
return new RegExp(`${start}(?!${identifierPart})`, "u").test(leftover) ? "all" : props;
|
|
4894
5054
|
};
|
|
4895
5055
|
/**
|
|
4896
5056
|
* `/* … *\/` and `// …` are legal inside a multiline import clause, and a
|
|
@@ -5633,6 +5793,20 @@ const BASE_CSS = `
|
|
|
5633
5793
|
.task-list-item { list-style: none; }
|
|
5634
5794
|
ul.contains-task-list { padding-left: 1.25rem; }
|
|
5635
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
|
+
}
|
|
5636
5810
|
/* --- Interaction feedback -------------------------------------------
|
|
5637
5811
|
* Every [data-copy] button carries an idle and a done icon
|
|
5638
5812
|
* (.mdxr-copy-idle/.mdxr-copy-done); the delegated event handler
|
|
@@ -5909,7 +6083,7 @@ details[open] > summary .mdxr-chev { transform: rotate(90deg); }
|
|
|
5909
6083
|
.dark .mdxr-toc-body > ul > li > a { color: rgb(212 212 212); }
|
|
5910
6084
|
.mdxr-toc-body > ul > li > p > a::before,
|
|
5911
6085
|
.mdxr-toc-body > ul > li > a::before {
|
|
5912
|
-
content: counter(mdxr-toc); min-width: 1em;
|
|
6086
|
+
content: counter(mdxr-toc); min-width: 1em; flex-shrink: 0; white-space: nowrap;
|
|
5913
6087
|
font-size: 0.72rem; font-weight: 400; font-variant-numeric: tabular-nums;
|
|
5914
6088
|
color: rgb(163 163 163);
|
|
5915
6089
|
}
|
|
@@ -5921,6 +6095,27 @@ details[open] > summary .mdxr-chev { transform: rotate(90deg); }
|
|
|
5921
6095
|
}
|
|
5922
6096
|
.dark .mdxr-toc-body ul ul { border-color: rgb(64 64 64); }
|
|
5923
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
|
+
}
|
|
5924
6119
|
/* Ask: native form controls stay interactive without hydration. The real
|
|
5925
6120
|
* inputs are visually hidden; state is styled through :checked/~ siblings. */
|
|
5926
6121
|
.mdxr-choice {
|