@tangle-network/charts 0.1.2 → 0.1.4
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/README.md +3 -1
- package/dist/index.js +17 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,9 +18,11 @@ A figure function returns a `Refusal` in place of a figure it cannot draw honest
|
|
|
18
18
|
The producer's `estimate` controls rate marks: `descriptive` draws a rate bar and may show a named Wilson score interval when that estimate meets the producer's unit threshold.
|
|
19
19
|
Task-clustered bootstrap intervals require a `bootstrap` estimate.
|
|
20
20
|
Intervals without support are marked as not drawn in the data table and note.
|
|
21
|
-
|
|
21
|
+
Cost-per-pass figures print `unknown` for missing, zero, or unproven cost; zero passes print `no pass`.
|
|
22
|
+
A matrix preserves an observed zero cost as $0 and omits a null cost.
|
|
22
23
|
Rank numbers require every row to carry both a rank and a `bootstrap` estimate.
|
|
23
24
|
Malformed rates, intervals, matrix counts, and breakdown counts return a refusal instead of a misleading mark.
|
|
25
|
+
Unmeasured and flagged matrix attempts keep distinct marks and table counts, even without tier grades.
|
|
24
26
|
An interval whisker is omitted when its bounds exclude the row's rate.
|
|
25
27
|
|
|
26
28
|
It has no runtime dependencies and uses no DOM, so it runs in a Worker, a build step or a browser.
|
package/dist/index.js
CHANGED
|
@@ -1247,7 +1247,7 @@ function plot(costed, unplaced, drawLine, o) {
|
|
|
1247
1247
|
/** The figure block and mark styles (src/figure.css), for documents that inline their CSS. */
|
|
1248
1248
|
const figureCss = "/* @tangle-network/charts — figure block and mark styles.\n *\n * Every colour is a --chart-* token from @tangle-network/brand tokens.css, so a\n * figure follows the page theme. A standalone document without brand tokens\n * embeds `chartTokensCss` first. Fonts fall back to Inter and JetBrains Mono.\n *\n * Each figure carries a 720 px and a 358 px render. The swap follows the\n * figure's own width (a container query), so a narrow column shows the phone\n * render even on a wide screen. */\n\n.tgc-figure {\n margin: 32px 0 0;\n padding: 0;\n max-width: 720px;\n color: var(--chart-ink);\n font-family: var(--font-sans, Inter, ui-sans-serif, system-ui, sans-serif);\n}\n\n.tgc-finding {\n margin: 0 0 8px;\n font-size: 16px;\n line-height: 24px;\n font-weight: 600;\n color: var(--chart-ink);\n}\n\n.tgc-noteref {\n margin-left: 2px;\n font-size: 12px;\n font-weight: 400;\n color: var(--chart-ink-muted);\n text-decoration: none;\n font-family: var(--font-mono, \"JetBrains Mono\", ui-monospace, monospace);\n}\n.tgc-noteref:hover,\n.tgc-noteref:focus-visible {\n color: var(--chart-accent);\n text-decoration: underline;\n}\n\n.tgc-lede {\n margin: 0 0 16px;\n font-size: 14px;\n line-height: 22px;\n color: var(--text-secondary, var(--chart-ink));\n}\n\n.tgc-plot {\n container-type: inline-size;\n}\n.tgc-wide svg,\n.tgc-narrow svg {\n display: block;\n overflow: visible;\n}\n.tgc-narrow {\n display: none;\n}\n.tgc-narrow svg {\n max-width: 100%;\n height: auto;\n}\n@container (max-width: 719.98px) {\n .tgc-wide {\n display: none;\n }\n .tgc-narrow {\n display: block;\n }\n}\n\n.tgc-read {\n margin: 8px 0 0;\n font-size: 12px;\n line-height: 16px;\n color: var(--chart-ink-muted);\n}\n\n.tgc-data {\n margin: 8px 0 0;\n font-size: 12px;\n line-height: 16px;\n}\n.tgc-data > summary {\n cursor: pointer;\n color: var(--chart-ink-muted);\n width: fit-content;\n}\n.tgc-data > summary:hover {\n color: var(--chart-ink);\n}\n.tgc-data[open] > summary {\n margin-bottom: 8px;\n}\n\n.tgc-scroll {\n overflow-x: auto;\n}\n.tgc-scroll:focus-visible {\n outline: 2px solid var(--chart-accent);\n outline-offset: 2px;\n}\n\n.tgc-table {\n border-collapse: collapse;\n min-width: 100%;\n font-size: 12px;\n line-height: 16px;\n color: var(--chart-ink);\n /* A host page's table rules must not restyle the figure's table. */\n border: 0;\n border-radius: 0;\n background: none;\n}\n.tgc-table caption {\n text-align: left;\n padding: 0 0 8px;\n color: var(--chart-ink-muted);\n}\n.tgc-table th,\n.tgc-table td {\n padding: 6px 12px 6px 0;\n border-bottom: 1px solid var(--chart-rule);\n text-align: left;\n vertical-align: top;\n font-weight: 400;\n font-size: inherit;\n color: inherit;\n background: none;\n text-transform: none;\n letter-spacing: normal;\n}\n.tgc-table thead th {\n color: var(--chart-ink-muted);\n white-space: nowrap;\n}\n.tgc-table tbody th {\n position: sticky;\n left: 0;\n background: var(--chart-canvas, var(--depth-1, transparent));\n}\n.tgc-table .tgc-n {\n font-family: var(--font-mono, \"JetBrains Mono\", ui-monospace, monospace);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n.tgc-table thead .tgc-n {\n font-family: inherit;\n}\n.tgc-table tbody tr:hover {\n background: var(--chart-shade);\n}\n.tgc-narrow-table .tgc-table {\n display: block;\n overflow-x: auto;\n}\n\n.tgc-notes {\n margin: 16px 0 0;\n padding-left: 20px;\n font-size: 12px;\n line-height: 16px;\n color: var(--chart-ink-muted);\n}\n.tgc-notes li {\n margin: 0 0 8px;\n padding-left: 4px;\n}\n.tgc-notes li:target {\n color: var(--chart-ink);\n}\n.tgc-backlink {\n color: var(--chart-ink-muted);\n text-decoration: none;\n}\n\n.tgc-refusals {\n margin: 8px 0 0;\n padding-left: 20px;\n font-size: 14px;\n line-height: 22px;\n color: var(--chart-ink-muted);\n}\n\n/* Marks */\n.tgc-svg {\n font-family: var(--font-sans, Inter, ui-sans-serif, system-ui, sans-serif);\n}\n.tgc-svg .tgc-num {\n font-family: var(--font-mono, \"JetBrains Mono\", ui-monospace, monospace);\n font-variant-numeric: tabular-nums;\n}\n.tgc-ink {\n fill: var(--chart-ink);\n}\n.tgc-ink-muted {\n fill: var(--chart-ink-muted);\n}\n.tgc-bar,\n.tgc-ci,\n.tgc-pt-front {\n fill: var(--chart-accent);\n}\n.tgc-gridline {\n stroke: var(--chart-grid);\n stroke-width: 1;\n}\n.tgc-rule {\n stroke: var(--chart-rule);\n stroke-width: 1;\n}\n.tgc-axis,\n.tgc-zero {\n stroke: var(--chart-muted);\n stroke-width: 1;\n}\n.tgc-whisker,\n.tgc-tick {\n stroke: var(--chart-ink);\n stroke-width: 1.5;\n stroke-linecap: round;\n}\n.tgc-threshold {\n stroke: var(--chart-ink);\n stroke-width: 1.5;\n stroke-dasharray: 3 3;\n}\n.tgc-front-line {\n fill: none;\n stroke: var(--chart-ink);\n stroke-width: 1.5;\n stroke-linejoin: round;\n}\n.tgc-pt,\n.tgc-open {\n fill: none;\n stroke: var(--chart-muted);\n stroke-width: 1.5;\n}\n.tgc-pt-est {\n fill: none;\n stroke: var(--chart-accent);\n stroke-width: 2;\n}\n.tgc-pt-ci,\n.tgc-strike {\n stroke: var(--chart-muted);\n stroke-width: 1.5;\n}\n.tgc-leader {\n stroke: var(--chart-muted);\n stroke-width: 1;\n}\n.tgc-ci-exact,\n.tgc-dot {\n fill: var(--chart-muted);\n}\n.tgc-dashed {\n stroke-dasharray: 2 2;\n}\n.tgc-tier-c {\n fill: var(--chart-tier-c);\n}\n.tgc-tier-b {\n fill: var(--chart-tier-b);\n}\n.tgc-tier-a {\n fill: var(--chart-tier-a);\n}\n.tgc-tier-s {\n fill: var(--chart-tier-s);\n}\n/* Neutral tint for counts: never the accent, capped at half strength so ink keeps 6:1. */\n.tgc-tint {\n fill: color-mix(in srgb, var(--chart-muted) calc(var(--s, 0) * 50%), transparent);\n}\n\n@media print {\n .tgc-plot {\n container-type: normal;\n }\n .tgc-wide {\n display: block;\n }\n .tgc-narrow {\n display: none;\n }\n .tgc-wide svg {\n width: 100%;\n height: auto;\n }\n .tgc-figure {\n break-inside: avoid;\n }\n}\n";
|
|
1249
1249
|
/** --chart-* resolved per theme, for documents without brand tokens: light, dark by preference or [data-theme="dark"], light in print. */
|
|
1250
|
-
const chartTokensCss = ":root{--chart-ink:#
|
|
1250
|
+
const chartTokensCss = ":root{--chart-ink:#2d2d2d;--chart-ink-muted:#686868;--chart-muted:#787878;--chart-grid:#f2f2f2;--chart-rule:#d0d0d0;--chart-shade:rgba(76, 63, 198, 0.06);--chart-accent:hsl(246 54% 51%);--chart-tier-c:#9b9ef0;--chart-tier-b:#7c7fe3;--chart-tier-a:#5b5bd6;--chart-tier-s:#272962;--chart-canvas:#ffffff;color-scheme:light}\n@media (prefers-color-scheme: dark){:root:not([data-theme=\"light\"]){--chart-ink:#e6e6e6;--chart-ink-muted:#a5a5a5;--chart-muted:#8e8e8e;--chart-grid:#353535;--chart-rule:#4c4c4c;--chart-shade:rgba(165, 180, 252, 0.07);--chart-accent:#818CF8;--chart-tier-c:#4a4a95;--chart-tier-b:#5b5bd6;--chart-tier-a:#8b87f0;--chart-tier-s:#b1a9ff;--chart-canvas:#161616;color-scheme:dark}}\n:root[data-theme=\"dark\"]{--chart-ink:#e6e6e6;--chart-ink-muted:#a5a5a5;--chart-muted:#8e8e8e;--chart-grid:#353535;--chart-rule:#4c4c4c;--chart-shade:rgba(165, 180, 252, 0.07);--chart-accent:#818CF8;--chart-tier-c:#4a4a95;--chart-tier-b:#5b5bd6;--chart-tier-a:#8b87f0;--chart-tier-s:#b1a9ff;--chart-canvas:#161616;color-scheme:dark}\n@media print{:root,:root:not([data-theme=\"light\"]),:root[data-theme=\"dark\"]{--chart-ink:#2d2d2d;--chart-ink-muted:#686868;--chart-muted:#787878;--chart-grid:#f2f2f2;--chart-rule:#d0d0d0;--chart-shade:rgba(76, 63, 198, 0.06);--chart-accent:hsl(246 54% 51%);--chart-tier-c:#9b9ef0;--chart-tier-b:#7c7fe3;--chart-tier-a:#5b5bd6;--chart-tier-s:#272962;--chart-canvas:#ffffff;color-scheme:light}}";
|
|
1251
1251
|
//#endregion
|
|
1252
1252
|
//#region src/ranked-rates.ts
|
|
1253
1253
|
/**
|
|
@@ -1596,8 +1596,17 @@ function tilesOf(cell) {
|
|
|
1596
1596
|
while (out.length < attempts) out.push("ungraded");
|
|
1597
1597
|
return out;
|
|
1598
1598
|
}
|
|
1599
|
-
if (cell.solved !== null) return [
|
|
1600
|
-
|
|
1599
|
+
if (cell.solved !== null) return [
|
|
1600
|
+
...Array(cell.solved).fill("solved"),
|
|
1601
|
+
...Array(attempts - cell.solved - (cell.flagged ?? 0) - (cell.unmeasured ?? 0)).fill("open"),
|
|
1602
|
+
...Array(cell.flagged ?? 0).fill("flagged"),
|
|
1603
|
+
...Array(cell.unmeasured ?? 0).fill("unmeasured")
|
|
1604
|
+
];
|
|
1605
|
+
return [
|
|
1606
|
+
...Array(attempts - (cell.flagged ?? 0) - (cell.unmeasured ?? 0)).fill("ungraded"),
|
|
1607
|
+
...Array(cell.flagged ?? 0).fill("flagged"),
|
|
1608
|
+
...Array(cell.unmeasured ?? 0).fill("unmeasured")
|
|
1609
|
+
];
|
|
1601
1610
|
}
|
|
1602
1611
|
function tile(kind, x, y) {
|
|
1603
1612
|
switch (kind) {
|
|
@@ -1619,7 +1628,7 @@ function cellText(cell, means) {
|
|
|
1619
1628
|
if (cell.attempts === 0) return "none counted";
|
|
1620
1629
|
if (cell.solved !== null) return kn(cell.solved, cell.attempts);
|
|
1621
1630
|
if (cell.tiers) return tierSummary(cell.tiers, cell.unmeasured, cell.flagged);
|
|
1622
|
-
return `${cell.attempts}`;
|
|
1631
|
+
return tierSummary(null, cell.unmeasured, cell.flagged) ? `${cell.attempts} · ${tierSummary(null, cell.unmeasured, cell.flagged)}` : `${cell.attempts}`;
|
|
1623
1632
|
}
|
|
1624
1633
|
function describe(task, setup, cell, means) {
|
|
1625
1634
|
if (!cell || (means ? cell.mean === null : cell.attempts === null)) return `${task} · ${setup.label}: not run`;
|
|
@@ -1684,7 +1693,7 @@ function taskMatrix(tasks, setupsIn, cells, o = {}) {
|
|
|
1684
1693
|
refused: `${cell.task} × ${cell.setup}: cost is invalid.`
|
|
1685
1694
|
};
|
|
1686
1695
|
if (!means && cell.attempts !== null) {
|
|
1687
|
-
if (tierTotal(cell.tiers) + (cell.flagged ?? 0) + (cell.unmeasured ?? 0) > cell.attempts || cell.solved !== null && cell.solved > cell.attempts) return {
|
|
1696
|
+
if (tierTotal(cell.tiers) + (cell.flagged ?? 0) + (cell.unmeasured ?? 0) > cell.attempts || cell.solved !== null && cell.solved + (cell.flagged ?? 0) + (cell.unmeasured ?? 0) > cell.attempts) return {
|
|
1688
1697
|
id,
|
|
1689
1698
|
refused: `${cell.task} × ${cell.setup}: grades or passes exceed its ${cell.attempts} attempts.`
|
|
1690
1699
|
};
|
|
@@ -1761,7 +1770,7 @@ function taskMatrix(tasks, setupsIn, cells, o = {}) {
|
|
|
1761
1770
|
const kinds = /* @__PURE__ */ new Set();
|
|
1762
1771
|
if (!means) for (const cell of cells) for (const k of tilesOf(cell)) kinds.add(k);
|
|
1763
1772
|
const tiered = cells.some((c) => c.tiers !== null);
|
|
1764
|
-
const read = means ? `Each cell is one ${noun.setup}'s mean on one ${noun.task}, printed and drawn as a bar on a shared scale; · marks a pair never scored.` : tiered ? `One tile per ${noun.attempt}, best first: tiles fill by App Grade tier and an outline is F${o.passTier ? `; ${o.passTier} or better passes` : ""}. Tile order is not run order.` : `One tile per ${noun.attempt}: a filled tile passed and
|
|
1773
|
+
const read = means ? `Each cell is one ${noun.setup}'s mean on one ${noun.task}, printed and drawn as a bar on a shared scale; · marks a pair never scored.` : tiered ? `One tile per ${noun.attempt}, best first: tiles fill by App Grade tier and an outline is F${o.passTier ? `; ${o.passTier} or better passes` : ""}. Tile order is not run order.` : `One tile per ${noun.attempt}: a filled tile passed and a plain outline did not. Dashed, dotted, and struck tiles keep their separate observation states. Tile order is not run order.`;
|
|
1765
1774
|
const method = means ? `Each cell is the producer's mean score for one ${noun.setup} on one ${noun.task}.` : [
|
|
1766
1775
|
tiered ? `Tiles show the App Grade tier of each ${noun.attempt}${o.passTier ? `, and ${o.passTier} or better counts as a pass` : ""}.` : `Filled tiles are the ${plural(2, noun.attempt)} the producer marks solved.`,
|
|
1767
1776
|
kinds.has("unmeasured") ? `U marks ${noun.attempt === "attempt" ? "an attempt" : `a ${noun.attempt}`} the grader could not measure.` : "",
|
|
@@ -1774,8 +1783,8 @@ function taskMatrix(tasks, setupsIn, cells, o = {}) {
|
|
|
1774
1783
|
}))], tasks.map((task) => [task, ...setups.map((s) => {
|
|
1775
1784
|
const cell = at(task, s);
|
|
1776
1785
|
const main = cellText(cell, means);
|
|
1777
|
-
const extra = !means && cell && cell.solved !== null && cell.tiers ? ` · ${tierSummary(cell.tiers, cell.unmeasured, cell.flagged)}` : "";
|
|
1778
|
-
const cost = cell && cell.costUsd !== null ? ` · ${
|
|
1786
|
+
const extra = !means && cell && cell.solved !== null && (cell.tiers || cell.unmeasured || cell.flagged) ? ` · ${tierSummary(cell.tiers, cell.unmeasured, cell.flagged)}` : "";
|
|
1787
|
+
const cost = cell && cell.costUsd !== null ? ` · ${usd(cell.costUsd)}` : "";
|
|
1779
1788
|
return main === "·" ? "not run" : `${main}${extra}${cost}`;
|
|
1780
1789
|
})]));
|
|
1781
1790
|
const scaleMax = Math.max(1, ...cells.map((c) => c.mean ?? 0));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/charts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Benchmark figures as static SVG and HTML tables: ranked rates, cost frontier, comparison intervals, task matrix and breakdown. Draws from producer data and refuses what it cannot draw honestly.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|