@tangle-network/charts 2.0.17 → 2.0.19
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 +4 -1
- package/dist/{AgentGraphCanvas-B74GN1o6.js → AgentGraphCanvas-C5Ee8Z6q.js} +61 -42
- package/dist/{elk-loader-CrLO557o.d.ts → elk-loader-uOcDmUG_.d.ts} +3 -3
- package/dist/figure.css +2 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +46 -67
- package/dist/{latest-events-iiWwpIck.js → latest-events-CmbyqEIe.js} +26 -29
- package/dist/react/agent-graph/index.d.ts +1 -1
- package/dist/react/agent-graph/index.js +6 -6
- package/dist/react/agent-graph/presentation.d.ts +1 -1
- package/dist/react/agent-graph.css +31 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -138,9 +138,12 @@ const figure = metricBars([
|
|
|
138
138
|
const html = isRefusal(figure) ? renderRefusals([figure]) : renderFigure(figure, 1, { presentation: "chart" });
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
`timeSeries` draws recorded scalar values at
|
|
141
|
+
`timeSeries` draws recorded scalar values at finite positions in order; equal positions, such as two runs recorded at one time, share a place and are never shifted.
|
|
142
142
|
The caller includes every configured position, with null for a missing observation.
|
|
143
143
|
Null breaks the line; the library never interpolates a missing value.
|
|
144
|
+
`secondary: true` de-emphasises a series beside a primary in the same `colorIndex`, such as a max beside its p50.
|
|
145
|
+
`crosshair: true` draws a vertical rule at the point whose tip shows.
|
|
146
|
+
A host with its own pointer rule, such as nearest point within 48 px, sets `data-near` on that `[data-point]` group to show its tip, and sets `pointer-events: none` on `.tgc-series-observation` so hover does not show a second one; keyboard focus still shows each tip.
|
|
144
147
|
|
|
145
148
|
```ts
|
|
146
149
|
import { isRefusal, renderFigure, renderRefusals, timeSeries } from "@tangle-network/charts";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { D as statusMeta, O as createLucideIcon, T as RunStatusIcon } from "./flow-waterfall-model-DvrgPLGS.js";
|
|
2
|
-
import { a as LIVE_AGENT_CARD, c as AgentMark, d as Wrench, n as AGENT_CARD, o as buildAgentTopology, s as layoutAgents, t as LatestEvents } from "./latest-events-
|
|
2
|
+
import { a as LIVE_AGENT_CARD, c as AgentMark, d as Wrench, n as AGENT_CARD, o as buildAgentTopology, s as layoutAgents, t as LatestEvents } from "./latest-events-CmbyqEIe.js";
|
|
3
3
|
import { C as fmtCost, E as fmtTokenTotal, T as fmtEventTime, _ as syncAmbient, b as useMotionAllowed, g as ambientStyle, h as agentQuietActivity, i as SELECTION_OUTLINE, n as RESTING_SHADOW, o as useColorMode, r as edgeColor, t as RESTING_BORDER, u as shortModel, v as tokenMilliseconds, x as useRunNow, y as useAttention } from "./graph-style-fealnLLn.js";
|
|
4
4
|
import { t as MOTION_CONTROL } from "./motion-DILryczn.js";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -116,13 +116,10 @@ function AgentCardView({ agent, selected, direction, now: fixed, onOpen, parentL
|
|
|
116
116
|
/* @__PURE__ */ jsxs("div", {
|
|
117
117
|
className: "flex min-w-0 items-center gap-2",
|
|
118
118
|
children: [
|
|
119
|
-
/* @__PURE__ */ jsx(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
layout: "tree",
|
|
124
|
-
size: 18
|
|
125
|
-
})
|
|
119
|
+
/* @__PURE__ */ jsx(AgentMark, {
|
|
120
|
+
agent,
|
|
121
|
+
layout: "tree",
|
|
122
|
+
size: 24
|
|
126
123
|
}),
|
|
127
124
|
/* @__PURE__ */ jsxs("span", {
|
|
128
125
|
className: "min-w-0 flex-1 truncate text-sm font-semibold leading-[18px]",
|
|
@@ -132,35 +129,50 @@ function AgentCardView({ agent, selected, direction, now: fixed, onOpen, parentL
|
|
|
132
129
|
}), agent.label]
|
|
133
130
|
}),
|
|
134
131
|
/* @__PURE__ */ jsx("span", {
|
|
135
|
-
className: "text-
|
|
132
|
+
className: "text-[13px] text-muted-foreground tabular-nums",
|
|
136
133
|
title: agent.costUsd == null ? "Cost not reported" : "Cost",
|
|
137
134
|
children: fmtCost(agent.costUsd ?? void 0) ?? "—"
|
|
138
135
|
})
|
|
139
136
|
]
|
|
140
137
|
}),
|
|
141
138
|
/* @__PURE__ */ jsx("p", {
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
"data-agent-identity": "",
|
|
140
|
+
className: "m-0 truncate text-[13px] leading-[18px] text-muted-foreground",
|
|
141
|
+
title: [agent.harness, agent.model].filter(Boolean).join(" · "),
|
|
142
|
+
children: [agent.harness, agent.model ? shortModel(agent.model) : null].filter(Boolean).join(" · ") || "Identity not recorded"
|
|
143
|
+
}),
|
|
144
|
+
agent.status === "failed" && /* @__PURE__ */ jsx("p", {
|
|
145
|
+
className: "m-0 line-clamp-2 text-[13px] leading-4",
|
|
146
|
+
children: agent.result ?? agent.turn ?? "Failure cause not recorded"
|
|
144
147
|
}),
|
|
145
148
|
/* @__PURE__ */ jsxs("div", {
|
|
146
|
-
className: "mt-auto flex items-center justify-between gap-1 text-
|
|
149
|
+
className: "mt-auto flex items-center justify-between gap-1 text-[13px]",
|
|
147
150
|
style: { color: quiet ? "var(--surface-warning-text)" : s.color },
|
|
148
|
-
children: [
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
},
|
|
162
|
-
|
|
163
|
-
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ jsxs("span", {
|
|
153
|
+
className: "flex items-center gap-1",
|
|
154
|
+
children: [/* @__PURE__ */ jsx(StatusGlyph, {
|
|
155
|
+
status: agent.status,
|
|
156
|
+
size: 16
|
|
157
|
+
}), quiet?.label ?? s.label]
|
|
158
|
+
}),
|
|
159
|
+
/* @__PURE__ */ jsx("span", {
|
|
160
|
+
"data-agent-tokens": "",
|
|
161
|
+
title: agent.tokens == null ? "Tokens not recorded" : "Tokens",
|
|
162
|
+
className: "text-muted-foreground tabular-nums whitespace-nowrap",
|
|
163
|
+
children: fmtTokenTotal(agent.tokens) ?? "—"
|
|
164
|
+
}),
|
|
165
|
+
agent.status === "needs-you" && /* @__PURE__ */ jsx("button", {
|
|
166
|
+
type: "button",
|
|
167
|
+
className: "ag-live-respond nodrag nopan",
|
|
168
|
+
disabled: !onOpen,
|
|
169
|
+
onClick: (event) => {
|
|
170
|
+
event.stopPropagation();
|
|
171
|
+
onOpen?.();
|
|
172
|
+
},
|
|
173
|
+
children: "Respond"
|
|
174
|
+
})
|
|
175
|
+
]
|
|
164
176
|
})
|
|
165
177
|
]
|
|
166
178
|
});
|
|
@@ -174,14 +186,14 @@ function AgentCard({ data }) {
|
|
|
174
186
|
function AgentFrameBox({ data }) {
|
|
175
187
|
const { frame, direction } = data;
|
|
176
188
|
return /* @__PURE__ */ jsxs("div", {
|
|
177
|
-
className: "relative h-full w-full rounded-2xl border",
|
|
189
|
+
className: "ag-team-frame relative h-full w-full rounded-2xl border",
|
|
178
190
|
style: {
|
|
179
191
|
background: "color-mix(in srgb, hsl(var(--muted-foreground)) 7%, transparent)",
|
|
180
192
|
borderColor: "hsl(var(--foreground) / 0.08)"
|
|
181
193
|
},
|
|
182
194
|
children: [/* @__PURE__ */ jsx(Handles, { direction }), /* @__PURE__ */ jsxs("button", {
|
|
183
195
|
type: "button",
|
|
184
|
-
className:
|
|
196
|
+
className: `nodrag nopan absolute top-2 flex h-16 items-center justify-center gap-2 rounded text-sm text-muted-foreground hover:bg-muted focus-visible:outline ${data.groupParents ? "right-3 w-8 flex-col" : "left-3 right-3 justify-start"}`,
|
|
185
197
|
"aria-label": `${frame.collapsed ? "Expand" : "Collapse"} ${data.label} team`,
|
|
186
198
|
"aria-expanded": !frame.collapsed,
|
|
187
199
|
title: `${data.label} · ${frame.group.memberIds.length} agents`,
|
|
@@ -189,10 +201,14 @@ function AgentFrameBox({ data }) {
|
|
|
189
201
|
event.stopPropagation();
|
|
190
202
|
data.onToggle();
|
|
191
203
|
},
|
|
192
|
-
children: [
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
204
|
+
children: [
|
|
205
|
+
/* @__PURE__ */ jsx(ChevronDown, {
|
|
206
|
+
size: 16,
|
|
207
|
+
className: frame.collapsed ? "-rotate-90" : void 0
|
|
208
|
+
}),
|
|
209
|
+
!data.groupParents && /* @__PURE__ */ jsxs("strong", { children: [data.label, " team"] }),
|
|
210
|
+
/* @__PURE__ */ jsx("span", { children: frame.group.memberIds.length })
|
|
211
|
+
]
|
|
196
212
|
})]
|
|
197
213
|
});
|
|
198
214
|
}
|
|
@@ -528,7 +544,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
528
544
|
const columnRef = useRef(null);
|
|
529
545
|
if (canvas && !narrow && columnRef.current?.key !== columnKey) columnRef.current = {
|
|
530
546
|
key: columnKey,
|
|
531
|
-
columns: Math.max(1, Math.min(6, Math.floor((canvas.width - 32) / (card.width + 10))))
|
|
547
|
+
columns: Math.max(1, Math.min(6, Math.floor((canvas.width - 32 - (!live && direction === "LR" ? card.width + 70 : 0)) / (card.width + 10))))
|
|
532
548
|
};
|
|
533
549
|
const columns = columnRef.current?.columns ?? 1;
|
|
534
550
|
const layoutKey = JSON.stringify([
|
|
@@ -542,7 +558,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
542
558
|
useEffect(() => {
|
|
543
559
|
if (narrow) return;
|
|
544
560
|
let active = true;
|
|
545
|
-
layoutAgents(topology, direction, columns, card, collapsed).then((layout) => {
|
|
561
|
+
layoutAgents(topology, direction, columns, card, collapsed, live).then((layout) => {
|
|
546
562
|
if (active) setPlaced({
|
|
547
563
|
key: layoutKey,
|
|
548
564
|
layout
|
|
@@ -583,6 +599,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
583
599
|
data: {
|
|
584
600
|
frame,
|
|
585
601
|
direction,
|
|
602
|
+
groupParents: live,
|
|
586
603
|
label: byId.get(frame.group.parentId)?.label ?? "Agent",
|
|
587
604
|
onToggle: () => {
|
|
588
605
|
if (!frame.collapsed && selectedId && frame.group.memberIds.includes(selectedId)) onSelect?.(frame.group.parentId);
|
|
@@ -599,7 +616,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
599
616
|
const agent = byId.get(id);
|
|
600
617
|
const position = layout.positions.get(id);
|
|
601
618
|
if (!agent || !position) continue;
|
|
602
|
-
const selected = selectedId === id, owned = layout.frames.find((frame) => frame.group.parentId === id);
|
|
619
|
+
const selected = selectedId === id, owned = live && layout.frames.find((frame) => frame.group.parentId === id);
|
|
603
620
|
const width = owned ? owned.width - 64 : card.width, height = owned ? 64 : card.height;
|
|
604
621
|
const measured = rfRef.current?.getInternalNode(id)?.measured;
|
|
605
622
|
cards.push({
|
|
@@ -646,7 +663,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
646
663
|
const out = [];
|
|
647
664
|
const seen = /* @__PURE__ */ new Set();
|
|
648
665
|
for (const s of topology.spawns) {
|
|
649
|
-
const source = topology.groups.find((group) => group.parentId === s.from)?.id
|
|
666
|
+
const source = live && topology.groups.find((group) => group.parentId === s.from)?.id || s.from, target = topology.groupOf.get(s.to) ?? s.to;
|
|
650
667
|
if (source === target) continue;
|
|
651
668
|
const id = `spawn:${source}:${target}`;
|
|
652
669
|
if (seen.has(id)) continue;
|
|
@@ -720,7 +737,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
720
737
|
}, [narrow]);
|
|
721
738
|
const layoutIdentity = layout ? `${layoutKey}:${layout.columns}` : null;
|
|
722
739
|
const cameraKey = `${direction}:${topology.key}`;
|
|
723
|
-
const minZoom = layout && canvas ? Math.max(.02, Math.min(.2, (canvas.width - 32) / layout.width, (canvas.height - 32) / layout.height)) : .2;
|
|
740
|
+
const minZoom = !live ? 1 : layout && canvas ? Math.max(.02, Math.min(.2, (canvas.width - 32) / layout.width, (canvas.height - 32) / layout.height)) : .2;
|
|
724
741
|
useEffect(() => {
|
|
725
742
|
const inst = rfRef.current;
|
|
726
743
|
if (!ready || !inst || !layoutIdentity || !canvas) return;
|
|
@@ -888,7 +905,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
888
905
|
wrapperRef.current = el;
|
|
889
906
|
measureCanvas(el);
|
|
890
907
|
},
|
|
891
|
-
className: `agent-graph ag-live relative flex flex-col ${className ?? ""}`,
|
|
908
|
+
className: `agent-graph ag-live${live ? "" : " ag-topology"} relative flex flex-col ${className ?? ""}`,
|
|
892
909
|
tabIndex: -1,
|
|
893
910
|
onKeyDownCapture: (event) => {
|
|
894
911
|
if (event.target instanceof HTMLElement && event.target.closest("[data-agent-metrics]")) return;
|
|
@@ -949,7 +966,7 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
949
966
|
select(agent.id);
|
|
950
967
|
}
|
|
951
968
|
},
|
|
952
|
-
children: live
|
|
969
|
+
children: live ? /* @__PURE__ */ jsx(LiveAgentCardView, {
|
|
953
970
|
agent,
|
|
954
971
|
parentLabel: byId.get(parentOf.get(agent.id) ?? "")?.label,
|
|
955
972
|
selected: selectedId === agent.id,
|
|
@@ -1060,7 +1077,9 @@ function AgentGraphCanvas({ nodes, edges, selectedId, onSelect, direction = "TB"
|
|
|
1060
1077
|
position: "bottom-right",
|
|
1061
1078
|
fitViewOptions: {
|
|
1062
1079
|
padding: `${FIT_PADDING}px`,
|
|
1063
|
-
|
|
1080
|
+
minZoom,
|
|
1081
|
+
maxZoom: 1,
|
|
1082
|
+
nodes: live ? void 0 : topology.agentIds.slice(0, 2).map((id) => ({ id }))
|
|
1064
1083
|
}
|
|
1065
1084
|
})
|
|
1066
1085
|
]
|
|
@@ -67,8 +67,8 @@ interface AgentGraphEdge {
|
|
|
67
67
|
type AgentGraphDirection = "TB" | "LR";
|
|
68
68
|
/** Every agent card's size. Fixed, so a status or turn update never moves a card. */
|
|
69
69
|
declare const AGENT_CARD: {
|
|
70
|
-
readonly width:
|
|
71
|
-
readonly height:
|
|
70
|
+
readonly width: 232;
|
|
71
|
+
readonly height: 120;
|
|
72
72
|
};
|
|
73
73
|
/** A live card: room for the header, a two-line thinking line and the last three events. Fixed for the same reason. */
|
|
74
74
|
declare const LIVE_AGENT_CARD: {
|
|
@@ -125,7 +125,7 @@ interface AgentLayout {
|
|
|
125
125
|
height: number;
|
|
126
126
|
}
|
|
127
127
|
/** Lay the run out with every grid `columns` wide. */
|
|
128
|
-
declare function layoutAgents(topology: AgentTopology, direction: AgentGraphDirection, columns: number, card?: CardSize, collapsed?: ReadonlySet<string
|
|
128
|
+
declare function layoutAgents(topology: AgentTopology, direction: AgentGraphDirection, columns: number, card?: CardSize, collapsed?: ReadonlySet<string>, groupParents?: boolean): Promise<AgentLayout>;
|
|
129
129
|
//#endregion
|
|
130
130
|
//#region src/react/agent-graph/format.d.ts
|
|
131
131
|
/**
|
package/dist/figure.css
CHANGED
|
@@ -329,6 +329,8 @@
|
|
|
329
329
|
.tgc-series-observation:focus-visible { outline: none; }
|
|
330
330
|
.tgc-series-observation:focus-visible .tgc-series-hit { stroke: var(--chart-ink); stroke-width: 2; }
|
|
331
331
|
.tgc-series-tip { opacity: 0; pointer-events: none; }
|
|
332
|
+
.tgc-series-secondary { opacity: .45; }
|
|
333
|
+
.tgc-crosshair { stroke: var(--chart-rule); stroke-width: 1; }
|
|
332
334
|
|
|
333
335
|
.tgc-scalar-table { min-width: 620px; }
|
|
334
336
|
.tgc-scalar-table tbody th { min-width: 180px; max-width: 260px; }
|
package/dist/index.d.ts
CHANGED
|
@@ -199,6 +199,8 @@ interface TimeSeriesRow {
|
|
|
199
199
|
points: TimePoint[];
|
|
200
200
|
/** Draw this series in checker `colorIndex`'s hue (0 is the current checker); past the fourth hue it is muted. */
|
|
201
201
|
colorIndex?: number;
|
|
202
|
+
/** De-emphasise this series beside a primary in the same hue, such as a max beside its p50. */
|
|
203
|
+
secondary?: boolean;
|
|
202
204
|
}
|
|
203
205
|
interface TimeSeriesOptions {
|
|
204
206
|
x: {
|
|
@@ -236,6 +238,8 @@ interface TimeSeriesOptions {
|
|
|
236
238
|
width?: number;
|
|
237
239
|
/** false leaves out the series legend under the plot, for a caller that names its series itself. The verdict key stays. */
|
|
238
240
|
legend?: boolean;
|
|
241
|
+
/** Draw a vertical rule at the x of the point whose tip shows. */
|
|
242
|
+
crosshair?: boolean;
|
|
239
243
|
id?: string;
|
|
240
244
|
}
|
|
241
245
|
export declare function timeSeries(rows: TimeSeriesRow[], o: TimeSeriesOptions): Figure | Refusal;
|
package/dist/index.js
CHANGED
|
@@ -1137,7 +1137,7 @@ function plot$1(costed, unplaced, drawLine, o) {
|
|
|
1137
1137
|
//#endregion
|
|
1138
1138
|
//#region src/css.generated.ts
|
|
1139
1139
|
/** The figure block and mark styles (src/figure.css), for documents that inline their CSS. */
|
|
1140
|
-
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: 18px;\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: 14px;\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: 20px;\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: 14px;\n line-height: 20px;\n color: var(--chart-ink-muted);\n}\n\n.tgc-data {\n margin: 8px 0 0;\n font-size: 14px;\n line-height: 20px;\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: 14px;\n line-height: 20px;\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: 8px 12px 8px 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: 14px;\n line-height: 20px;\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: 2;\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\n/* Chart-led surfaces keep supporting text in the native disclosure. */\n.tgc-figure-chart { max-width: none; margin: 0; }\n.tgc-figure-chart .tgc-wide svg { width: 100%; height: auto; }\n.tgc-wide:has(.tgc-vertical), .tgc-narrow:has(.tgc-vertical) { overflow-x: auto; }\n.tgc-vertical { width: 100%; }\n.tgc-narrow .tgc-vertical { max-width: none; }\n.tgc-column-hit { fill: transparent; stroke: transparent; stroke-width: 2; }\n.tgc-column:focus-visible { outline: none; }\n.tgc-column:focus-visible .tgc-column-hit { stroke: var(--chart-accent); }\n.tgc-vertical-bar { fill: var(--chart-accent); }\n/* Identity assets keep their approved colors on a white plate in either theme. */\n.tgc-logo-plate { fill: #fff; stroke: var(--chart-rule); }\n.tgc-column-tip { opacity: 0; pointer-events: none; }\n.tgc-tip-bg { fill: var(--chart-ink); }\n.tgc-tip-ink { fill: var(--chart-canvas); }\n\n.tgc-series-line { fill: none; stroke: var(--tgc-series, var(--chart-accent)); stroke-width: 2; stroke-linejoin: round; }\n.tgc-series-point, .tgc-series-key { fill: var(--tgc-series, var(--chart-accent)); stroke: var(--chart-canvas); stroke-width: 2; }\n\n/* Semantic states stay tied to Brand status tokens. No-data has its own neutral mark. */\n.tgc-heat-pass { fill: color-mix(in srgb, var(--chart-pass, var(--status-running)) 19%, var(--chart-canvas)); stroke: var(--chart-pass, var(--status-running)); stroke-width: .8; }\n.tgc-heat-breach { fill: color-mix(in srgb, var(--chart-breach, var(--status-error)) 17%, var(--chart-canvas)); stroke: var(--chart-breach, var(--status-error)); stroke-width: .8; }\n.tgc-heat-no-data { fill: var(--chart-grid); stroke: var(--chart-rule); stroke-width: .8; }\n.tgc-heat-label { fill: var(--chart-ink); }\n.tgc-heat-missing { fill: var(--chart-ink-muted); }\n.tgc-wide:has(.tgc-heat-pass), .tgc-wide:has(.tgc-heat-breach) { overflow-x: auto; }\n.tgc-range-pass { fill: var(--chart-pass, var(--status-running)); }\n.tgc-range-breach { fill: var(--chart-breach, var(--status-error)); }\n.tgc-range-dot-pass { fill: var(--chart-canvas); stroke: var(--chart-pass, var(--status-running)); stroke-width: 3; }\n.tgc-range-dot-breach { fill: var(--chart-canvas); stroke: var(--chart-breach, var(--status-error)); stroke-width: 3; }\n.tgc-waterfall-bar { fill: var(--chart-accent); }\n.tgc-series-hit { fill: transparent; stroke: transparent; }\n.tgc-series-observation:focus-visible { outline: none; }\n.tgc-series-observation:focus-visible .tgc-series-hit { stroke: var(--chart-ink); stroke-width: 2; }\n.tgc-series-tip { opacity: 0; pointer-events: none; }\n\n.tgc-scalar-table { min-width: 620px; }\n.tgc-scalar-table tbody th { min-width: 180px; max-width: 260px; }\n\n/* Plot presentation keeps the same complete data available to assistive technology. */\n.tgc-accessible-data { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }\n.tgc-figure-plot .tgc-wide svg { box-sizing: content-box; width: calc(100% - 48px); padding: 0 24px; }\n/* One plot text size on the 12/14/18/32 scale; numbers differ by tabular figures, not size. */\n.tgc-figure-plot svg text { font-size: 14px; }\n/* Larger tooltip text needs padding outside the original report backdrop. */\n.tgc-figure-plot .tgc-tip-bg { stroke: var(--chart-ink); stroke-width: 24px; }\n.tgc-figure-plot .tgc-phone-plot svg { min-width: var(--tgc-phone-width); }\n.tgc-figure-plot .tgc-wide, .tgc-figure-plot .tgc-narrow { overflow-x: auto; }\n\n.tgc-figure-plot .tgc-wide-fallback svg { min-width: var(--tgc-fallback-width); }\n.tgc-phone-plot:focus-visible, .tgc-wide-fallback:focus-visible { outline: 2px solid var(--chart-accent); outline-offset: 2px; }\n\n/* Recorded events and spans. Kinds differ by stroke pattern as well as token, so they read without colour. */\n.tgc-mark { stroke-width: 1.5; }\n.tgc-mark-pass { stroke: var(--chart-pass, var(--status-running)); }\n.tgc-mark-deadline { stroke: var(--chart-ink); stroke-dasharray: 5 3; }\n.tgc-mark-died { stroke: var(--chart-breach, var(--status-error)); stroke-width: 2; }\n.tgc-mark-event { stroke: var(--chart-ink-muted); stroke-dasharray: 1 3; stroke-linecap: round; }\n.tgc-mark-release { stroke: var(--chart-ink-muted); }\n.tgc-mark-steer { stroke: var(--chart-accent); stroke-dasharray: 1 3; stroke-linecap: round; }\n.tgc-mark-ruling { stroke: var(--chart-ink); stroke-dasharray: 6 2 1 2; }\n.tgc-mark-wake { stroke: var(--chart-ink-muted); stroke-dasharray: 1 3; stroke-linecap: round; }\n.tgc-mark-head { fill: var(--chart-ink-muted); stroke: var(--chart-canvas); stroke-width: 1; }\n[data-mark=\"steer\"] .tgc-mark-head { fill: var(--chart-accent); }\n[data-mark=\"ruling\"] .tgc-mark-head { fill: var(--chart-ink); }\n/* A recorded verdict colours the rule and head: pass and fail take the status tones, unknown the muted tone. */\n.tgc-verdict-pass .tgc-mark { stroke: var(--chart-pass, var(--status-running)); }\n.tgc-verdict-pass .tgc-mark-head { fill: var(--chart-pass, var(--status-running)); }\n.tgc-verdict-fail .tgc-mark { stroke: var(--chart-breach, var(--status-error)); }\n.tgc-verdict-fail .tgc-mark-head { fill: var(--chart-breach, var(--status-error)); }\n.tgc-verdict-unknown .tgc-mark { stroke: var(--chart-muted); }\n.tgc-verdict-unknown .tgc-mark-head { fill: var(--chart-muted); }\n.tgc-mark-label { fill: var(--chart-ink); paint-order: stroke; stroke: var(--chart-canvas); stroke-width: 3px; stroke-linejoin: round; }\n.tgc-mark-edge { fill: var(--chart-ink-muted); }\n.tgc-band-after-pass { fill: color-mix(in srgb, var(--chart-pass, var(--status-running)) 12%, transparent); }\n.tgc-band-idle { fill: var(--chart-shade); }\n/* A system-wide wall, such as a quota window: a breach wash across every lane, topped by a solid edge. */\n.tgc-band-blocked { fill: color-mix(in srgb, var(--chart-breach, var(--status-error)) 9%, transparent); }\n.tgc-band-wall { stroke: var(--chart-breach, var(--status-error)); stroke-width: 2; }\n/* Missing data: a neutral wash under hatch lines, never a zero-height mark. */\n.tgc-hatch-bg { fill: color-mix(in srgb, var(--chart-muted) 10%, transparent); }\n.tgc-hatch { fill: none; stroke: var(--chart-muted); stroke-width: 1; }\n.tgc-span { fill: var(--chart-accent); }\n.tgc-span-muted { fill: var(--chart-muted); }\n.tgc-span-open { stroke: var(--chart-ink); stroke-width: 1.5; stroke-dasharray: 3 2; }\n.tgc-now { stroke: var(--chart-ink); stroke-width: 1.5; }\n/* Timeline trees and attempts. Ok ticks rise above the bar and failed ticks drop below it, so they read without colour. */\n.tgc-tree { fill: none; stroke: var(--chart-muted); stroke-width: 1; }\n.tgc-caret { fill: var(--chart-ink-muted); }\n.tgc-lane-toggle { cursor: pointer; }\n.tgc-lane-toggle:focus-visible { outline: none; }\n.tgc-lane-hit { fill: transparent; stroke: transparent; stroke-width: 2; }\n.tgc-lane-toggle:hover .tgc-lane-hit { fill: var(--chart-shade); }\n.tgc-lane-toggle:focus-visible .tgc-lane-hit { stroke: var(--chart-accent); }\n.tgc-tick { stroke-width: 1.5; }\n.tgc-tick-ok { stroke: var(--chart-outcome-ok); }\n.tgc-tick-quota { stroke: var(--chart-outcome-quota); }\n.tgc-tick-lost { stroke: var(--chart-outcome-lost); }\n.tgc-tick-rate-limit { stroke: var(--chart-outcome-rate-limit); }\n.tgc-tick-oom { stroke: var(--chart-outcome-oom); }\n.tgc-tick-refused { stroke: var(--chart-outcome-refused); }\n.tgc-tick-aborted { stroke: var(--chart-outcome-neutral); }\n.tgc-tick-other { stroke: var(--chart-outcome-other); }\n/* Lane labels and counts keep a canvas halo, so rules and ticks under them never strike through the text. */\n.tgc-lane-label, .tgc-count { paint-order: stroke; stroke: var(--chart-canvas); stroke-width: 3px; stroke-linejoin: round; }\n.tgc-count { fill: var(--chart-ink); }\n.tgc-count-ok { fill: var(--chart-pass, var(--status-running)); }\n.tgc-count-failed { fill: var(--chart-breach, var(--status-error)); }\n\n/* FigureView (React) picks the render by measured width; the wide plot fills its container. */\n.tgc-figure-view { max-width: none; margin: 0; }\n.tgc-view-plot svg { display: block; overflow: visible; }\n.tgc-view-wide svg { width: 100%; height: auto; }\n.tgc-view-narrow svg { max-width: 100%; height: auto; }\n\n/*\n * One fixed palette in every view (src/outcomes.ts names them), each a light-dark() pair picked by the page's\n * color-scheme. Green, red and amber are status colours and mean one thing everywhere: green is a turn or run that\n * worked (an `ok` attempt, a solved run), red a run that died, amber a run stalled or a turn rate-limited. Outcomes\n * pass the dataviz colour-vision checks as adjacent pairs in stack order (ok, quota, lost, rate-limit, oom, refused) on\n * the light surface and on #161616; `aborted` and `other` are two distinct greys. Checkers take hues no status uses\n * (blue for the current checker, then magenta, cyan and violet for older ones) and pass as adjacent series; their\n * marks also carry a vN label. Run endings reuse those colours. `scripts/palette-check.mjs` re-runs the validator. A\n * page may override any of these properties; :where() keeps them at zero specificity.\n */\n:where(:root) {\n --chart-outcome-ok: light-dark(#1baf7a, #199e70);\n --chart-outcome-quota: light-dark(#eb6834, #d95926);\n --chart-outcome-lost: light-dark(#0f9fb8, #1a9cb3);\n --chart-outcome-rate-limit: light-dark(#eda100, #c98500);\n --chart-outcome-oom: light-dark(#e87ba4, #d55181);\n --chart-outcome-refused: light-dark(#6250d6, #9085e9);\n --chart-outcome-neutral: #898781;\n --chart-outcome-other: #b5b3ac;\n --chart-checker-0: light-dark(#2a78d6, #3987e5);\n --chart-checker-1: light-dark(#b8478f, #c2408f);\n --chart-checker-2: light-dark(#0f9fb8, #1a9cb3);\n --chart-checker-3: light-dark(#6250d6, #8c7ce6);\n /* How a run ended: solved green, died red, stalled amber, live blue; not solved and cancelled are neutral. */\n --chart-ending-solved: var(--chart-outcome-ok);\n --chart-ending-died: light-dark(#d03b3b, #e05252);\n --chart-ending-stalled: var(--chart-outcome-rate-limit);\n --chart-ending-live: var(--chart-checker-0);\n --chart-ending-not-solved: light-dark(#6b7a90, #8494ab);\n --chart-ending-cancelled: var(--chart-outcome-neutral);\n --chart-ending-never-started: color-mix(in srgb, var(--chart-outcome-other) 45%, transparent);\n}\n/* A series drawn in a checker's hue; past the fourth hue it is muted and named by its label. */\n.tgc-checker-0 { --tgc-series: var(--chart-checker-0); }\n.tgc-checker-1 { --tgc-series: var(--chart-checker-1); }\n.tgc-checker-2 { --tgc-series: var(--chart-checker-2); }\n.tgc-checker-3 { --tgc-series: var(--chart-checker-3); }\n.tgc-checker-other { --tgc-series: var(--chart-muted); }\n/* Recorded verdicts: a pass is filled, a fail or an incomparable value is an outline, an undecided one dashed. */\n.tgc-series-point.tgc-point-outline { fill: var(--chart-canvas); stroke: var(--tgc-series, var(--chart-accent)); stroke-width: 1.75; }\n.tgc-series-point.tgc-point-undecided { fill: var(--chart-canvas); stroke: var(--tgc-series, var(--chart-accent)); stroke-width: 1.5; stroke-dasharray: 2 1.5; }\n.tgc-strip-hatch .tgc-hatch { stroke: var(--tgc-series, var(--chart-muted)); }\n/* A failing verdict without a value is a neutral ring: in its checker's hue it read as that checker passing. */\n.tgc-series-point.tgc-point-outline.tgc-strip-fail { stroke: var(--chart-ink-muted); }\n.tgc-strip-edge { fill: none; stroke: var(--tgc-series, var(--chart-muted)); stroke-width: 1.25; }\n.tgc-strip-observation:focus-visible { outline: none; }\n.tgc-strip-observation:focus-visible .tgc-series-hit { stroke: var(--chart-ink); stroke-width: 2; }\n.tgc-point-badge { paint-order: stroke; stroke: var(--chart-canvas); stroke-width: 3px; stroke-linejoin: round; font-family: var(--font-mono, \"JetBrains Mono\", ui-monospace, monospace); }\n.tgc-point-label { fill: var(--chart-ink-muted); }\n.tgc-glyph-note { fill: var(--chart-ink); font-weight: 600; }\n/* Status colours appear only on these glyphs. */\n.tgc-glyph-check { fill: var(--chart-pass, var(--status-running)); font-weight: 600; }\n.tgc-glyph-cross { fill: var(--chart-breach, var(--status-error)); font-weight: 600; }\n/* Recorded targets sit under the data: a target is a soft band between solid edges, a stretch a dashed outline. */\n.tgc-target-target { fill: color-mix(in srgb, var(--chart-ink) 7%, transparent); }\n.tgc-target-stretch { fill: none; }\n.tgc-target-target-edge, .tgc-target-target.tgc-target-line { stroke: var(--chart-ink-muted); stroke-width: 1; }\n.tgc-target-stretch-edge, .tgc-target-stretch.tgc-target-line { stroke: var(--chart-ink-muted); stroke-width: 1; stroke-dasharray: 4 3; }\n.tgc-target-label { fill: var(--chart-ink-muted); paint-order: stroke; stroke: var(--chart-canvas); stroke-width: 3px; stroke-linejoin: round; }\n/* Group headers: a labelled bracket above the plot and a light rule at each inner edge. */\n.tgc-group-rule { stroke: var(--chart-ink-muted); stroke-width: 1.5; }\n.tgc-group-edge { stroke: var(--chart-rule); stroke-width: 1; }\n.tgc-group-label { fill: var(--chart-ink); paint-order: stroke; stroke: var(--chart-canvas); stroke-width: 3px; stroke-linejoin: round; }\n/* The verdict key: glyphs in the neutral ink, so they describe a mark's form, not a series. */\n.tgc-key-mark { stroke-width: 1.5; }\n.tgc-key-filled { fill: var(--chart-ink-muted); stroke: none; }\n.tgc-key-outline { fill: none; stroke: var(--chart-ink-muted); }\n.tgc-key-dashed { fill: none; stroke: var(--chart-ink-muted); stroke-dasharray: 2 1.5; }\n";
|
|
1140
|
+
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: 18px;\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: 14px;\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: 20px;\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: 14px;\n line-height: 20px;\n color: var(--chart-ink-muted);\n}\n\n.tgc-data {\n margin: 8px 0 0;\n font-size: 14px;\n line-height: 20px;\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: 14px;\n line-height: 20px;\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: 8px 12px 8px 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: 14px;\n line-height: 20px;\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: 2;\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\n/* Chart-led surfaces keep supporting text in the native disclosure. */\n.tgc-figure-chart { max-width: none; margin: 0; }\n.tgc-figure-chart .tgc-wide svg { width: 100%; height: auto; }\n.tgc-wide:has(.tgc-vertical), .tgc-narrow:has(.tgc-vertical) { overflow-x: auto; }\n.tgc-vertical { width: 100%; }\n.tgc-narrow .tgc-vertical { max-width: none; }\n.tgc-column-hit { fill: transparent; stroke: transparent; stroke-width: 2; }\n.tgc-column:focus-visible { outline: none; }\n.tgc-column:focus-visible .tgc-column-hit { stroke: var(--chart-accent); }\n.tgc-vertical-bar { fill: var(--chart-accent); }\n/* Identity assets keep their approved colors on a white plate in either theme. */\n.tgc-logo-plate { fill: #fff; stroke: var(--chart-rule); }\n.tgc-column-tip { opacity: 0; pointer-events: none; }\n.tgc-tip-bg { fill: var(--chart-ink); }\n.tgc-tip-ink { fill: var(--chart-canvas); }\n\n.tgc-series-line { fill: none; stroke: var(--tgc-series, var(--chart-accent)); stroke-width: 2; stroke-linejoin: round; }\n.tgc-series-point, .tgc-series-key { fill: var(--tgc-series, var(--chart-accent)); stroke: var(--chart-canvas); stroke-width: 2; }\n\n/* Semantic states stay tied to Brand status tokens. No-data has its own neutral mark. */\n.tgc-heat-pass { fill: color-mix(in srgb, var(--chart-pass, var(--status-running)) 19%, var(--chart-canvas)); stroke: var(--chart-pass, var(--status-running)); stroke-width: .8; }\n.tgc-heat-breach { fill: color-mix(in srgb, var(--chart-breach, var(--status-error)) 17%, var(--chart-canvas)); stroke: var(--chart-breach, var(--status-error)); stroke-width: .8; }\n.tgc-heat-no-data { fill: var(--chart-grid); stroke: var(--chart-rule); stroke-width: .8; }\n.tgc-heat-label { fill: var(--chart-ink); }\n.tgc-heat-missing { fill: var(--chart-ink-muted); }\n.tgc-wide:has(.tgc-heat-pass), .tgc-wide:has(.tgc-heat-breach) { overflow-x: auto; }\n.tgc-range-pass { fill: var(--chart-pass, var(--status-running)); }\n.tgc-range-breach { fill: var(--chart-breach, var(--status-error)); }\n.tgc-range-dot-pass { fill: var(--chart-canvas); stroke: var(--chart-pass, var(--status-running)); stroke-width: 3; }\n.tgc-range-dot-breach { fill: var(--chart-canvas); stroke: var(--chart-breach, var(--status-error)); stroke-width: 3; }\n.tgc-waterfall-bar { fill: var(--chart-accent); }\n.tgc-series-hit { fill: transparent; stroke: transparent; }\n.tgc-series-observation:focus-visible { outline: none; }\n.tgc-series-observation:focus-visible .tgc-series-hit { stroke: var(--chart-ink); stroke-width: 2; }\n.tgc-series-tip { opacity: 0; pointer-events: none; }\n.tgc-series-secondary { opacity: .45; }\n.tgc-crosshair { stroke: var(--chart-rule); stroke-width: 1; }\n\n.tgc-scalar-table { min-width: 620px; }\n.tgc-scalar-table tbody th { min-width: 180px; max-width: 260px; }\n\n/* Plot presentation keeps the same complete data available to assistive technology. */\n.tgc-accessible-data { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }\n.tgc-figure-plot .tgc-wide svg { box-sizing: content-box; width: calc(100% - 48px); padding: 0 24px; }\n/* One plot text size on the 12/14/18/32 scale; numbers differ by tabular figures, not size. */\n.tgc-figure-plot svg text { font-size: 14px; }\n/* Larger tooltip text needs padding outside the original report backdrop. */\n.tgc-figure-plot .tgc-tip-bg { stroke: var(--chart-ink); stroke-width: 24px; }\n.tgc-figure-plot .tgc-phone-plot svg { min-width: var(--tgc-phone-width); }\n.tgc-figure-plot .tgc-wide, .tgc-figure-plot .tgc-narrow { overflow-x: auto; }\n\n.tgc-figure-plot .tgc-wide-fallback svg { min-width: var(--tgc-fallback-width); }\n.tgc-phone-plot:focus-visible, .tgc-wide-fallback:focus-visible { outline: 2px solid var(--chart-accent); outline-offset: 2px; }\n\n/* Recorded events and spans. Kinds differ by stroke pattern as well as token, so they read without colour. */\n.tgc-mark { stroke-width: 1.5; }\n.tgc-mark-pass { stroke: var(--chart-pass, var(--status-running)); }\n.tgc-mark-deadline { stroke: var(--chart-ink); stroke-dasharray: 5 3; }\n.tgc-mark-died { stroke: var(--chart-breach, var(--status-error)); stroke-width: 2; }\n.tgc-mark-event { stroke: var(--chart-ink-muted); stroke-dasharray: 1 3; stroke-linecap: round; }\n.tgc-mark-release { stroke: var(--chart-ink-muted); }\n.tgc-mark-steer { stroke: var(--chart-accent); stroke-dasharray: 1 3; stroke-linecap: round; }\n.tgc-mark-ruling { stroke: var(--chart-ink); stroke-dasharray: 6 2 1 2; }\n.tgc-mark-wake { stroke: var(--chart-ink-muted); stroke-dasharray: 1 3; stroke-linecap: round; }\n.tgc-mark-head { fill: var(--chart-ink-muted); stroke: var(--chart-canvas); stroke-width: 1; }\n[data-mark=\"steer\"] .tgc-mark-head { fill: var(--chart-accent); }\n[data-mark=\"ruling\"] .tgc-mark-head { fill: var(--chart-ink); }\n/* A recorded verdict colours the rule and head: pass and fail take the status tones, unknown the muted tone. */\n.tgc-verdict-pass .tgc-mark { stroke: var(--chart-pass, var(--status-running)); }\n.tgc-verdict-pass .tgc-mark-head { fill: var(--chart-pass, var(--status-running)); }\n.tgc-verdict-fail .tgc-mark { stroke: var(--chart-breach, var(--status-error)); }\n.tgc-verdict-fail .tgc-mark-head { fill: var(--chart-breach, var(--status-error)); }\n.tgc-verdict-unknown .tgc-mark { stroke: var(--chart-muted); }\n.tgc-verdict-unknown .tgc-mark-head { fill: var(--chart-muted); }\n.tgc-mark-label { fill: var(--chart-ink); paint-order: stroke; stroke: var(--chart-canvas); stroke-width: 3px; stroke-linejoin: round; }\n.tgc-mark-edge { fill: var(--chart-ink-muted); }\n.tgc-band-after-pass { fill: color-mix(in srgb, var(--chart-pass, var(--status-running)) 12%, transparent); }\n.tgc-band-idle { fill: var(--chart-shade); }\n/* A system-wide wall, such as a quota window: a breach wash across every lane, topped by a solid edge. */\n.tgc-band-blocked { fill: color-mix(in srgb, var(--chart-breach, var(--status-error)) 9%, transparent); }\n.tgc-band-wall { stroke: var(--chart-breach, var(--status-error)); stroke-width: 2; }\n/* Missing data: a neutral wash under hatch lines, never a zero-height mark. */\n.tgc-hatch-bg { fill: color-mix(in srgb, var(--chart-muted) 10%, transparent); }\n.tgc-hatch { fill: none; stroke: var(--chart-muted); stroke-width: 1; }\n.tgc-span { fill: var(--chart-accent); }\n.tgc-span-muted { fill: var(--chart-muted); }\n.tgc-span-open { stroke: var(--chart-ink); stroke-width: 1.5; stroke-dasharray: 3 2; }\n.tgc-now { stroke: var(--chart-ink); stroke-width: 1.5; }\n/* Timeline trees and attempts. Ok ticks rise above the bar and failed ticks drop below it, so they read without colour. */\n.tgc-tree { fill: none; stroke: var(--chart-muted); stroke-width: 1; }\n.tgc-caret { fill: var(--chart-ink-muted); }\n.tgc-lane-toggle { cursor: pointer; }\n.tgc-lane-toggle:focus-visible { outline: none; }\n.tgc-lane-hit { fill: transparent; stroke: transparent; stroke-width: 2; }\n.tgc-lane-toggle:hover .tgc-lane-hit { fill: var(--chart-shade); }\n.tgc-lane-toggle:focus-visible .tgc-lane-hit { stroke: var(--chart-accent); }\n.tgc-tick { stroke-width: 1.5; }\n.tgc-tick-ok { stroke: var(--chart-outcome-ok); }\n.tgc-tick-quota { stroke: var(--chart-outcome-quota); }\n.tgc-tick-lost { stroke: var(--chart-outcome-lost); }\n.tgc-tick-rate-limit { stroke: var(--chart-outcome-rate-limit); }\n.tgc-tick-oom { stroke: var(--chart-outcome-oom); }\n.tgc-tick-refused { stroke: var(--chart-outcome-refused); }\n.tgc-tick-aborted { stroke: var(--chart-outcome-neutral); }\n.tgc-tick-other { stroke: var(--chart-outcome-other); }\n/* Lane labels and counts keep a canvas halo, so rules and ticks under them never strike through the text. */\n.tgc-lane-label, .tgc-count { paint-order: stroke; stroke: var(--chart-canvas); stroke-width: 3px; stroke-linejoin: round; }\n.tgc-count { fill: var(--chart-ink); }\n.tgc-count-ok { fill: var(--chart-pass, var(--status-running)); }\n.tgc-count-failed { fill: var(--chart-breach, var(--status-error)); }\n\n/* FigureView (React) picks the render by measured width; the wide plot fills its container. */\n.tgc-figure-view { max-width: none; margin: 0; }\n.tgc-view-plot svg { display: block; overflow: visible; }\n.tgc-view-wide svg { width: 100%; height: auto; }\n.tgc-view-narrow svg { max-width: 100%; height: auto; }\n\n/*\n * One fixed palette in every view (src/outcomes.ts names them), each a light-dark() pair picked by the page's\n * color-scheme. Green, red and amber are status colours and mean one thing everywhere: green is a turn or run that\n * worked (an `ok` attempt, a solved run), red a run that died, amber a run stalled or a turn rate-limited. Outcomes\n * pass the dataviz colour-vision checks as adjacent pairs in stack order (ok, quota, lost, rate-limit, oom, refused) on\n * the light surface and on #161616; `aborted` and `other` are two distinct greys. Checkers take hues no status uses\n * (blue for the current checker, then magenta, cyan and violet for older ones) and pass as adjacent series; their\n * marks also carry a vN label. Run endings reuse those colours. `scripts/palette-check.mjs` re-runs the validator. A\n * page may override any of these properties; :where() keeps them at zero specificity.\n */\n:where(:root) {\n --chart-outcome-ok: light-dark(#1baf7a, #199e70);\n --chart-outcome-quota: light-dark(#eb6834, #d95926);\n --chart-outcome-lost: light-dark(#0f9fb8, #1a9cb3);\n --chart-outcome-rate-limit: light-dark(#eda100, #c98500);\n --chart-outcome-oom: light-dark(#e87ba4, #d55181);\n --chart-outcome-refused: light-dark(#6250d6, #9085e9);\n --chart-outcome-neutral: #898781;\n --chart-outcome-other: #b5b3ac;\n --chart-checker-0: light-dark(#2a78d6, #3987e5);\n --chart-checker-1: light-dark(#b8478f, #c2408f);\n --chart-checker-2: light-dark(#0f9fb8, #1a9cb3);\n --chart-checker-3: light-dark(#6250d6, #8c7ce6);\n /* How a run ended: solved green, died red, stalled amber, live blue; not solved and cancelled are neutral. */\n --chart-ending-solved: var(--chart-outcome-ok);\n --chart-ending-died: light-dark(#d03b3b, #e05252);\n --chart-ending-stalled: var(--chart-outcome-rate-limit);\n --chart-ending-live: var(--chart-checker-0);\n --chart-ending-not-solved: light-dark(#6b7a90, #8494ab);\n --chart-ending-cancelled: var(--chart-outcome-neutral);\n --chart-ending-never-started: color-mix(in srgb, var(--chart-outcome-other) 45%, transparent);\n}\n/* A series drawn in a checker's hue; past the fourth hue it is muted and named by its label. */\n.tgc-checker-0 { --tgc-series: var(--chart-checker-0); }\n.tgc-checker-1 { --tgc-series: var(--chart-checker-1); }\n.tgc-checker-2 { --tgc-series: var(--chart-checker-2); }\n.tgc-checker-3 { --tgc-series: var(--chart-checker-3); }\n.tgc-checker-other { --tgc-series: var(--chart-muted); }\n/* Recorded verdicts: a pass is filled, a fail or an incomparable value is an outline, an undecided one dashed. */\n.tgc-series-point.tgc-point-outline { fill: var(--chart-canvas); stroke: var(--tgc-series, var(--chart-accent)); stroke-width: 1.75; }\n.tgc-series-point.tgc-point-undecided { fill: var(--chart-canvas); stroke: var(--tgc-series, var(--chart-accent)); stroke-width: 1.5; stroke-dasharray: 2 1.5; }\n.tgc-strip-hatch .tgc-hatch { stroke: var(--tgc-series, var(--chart-muted)); }\n/* A failing verdict without a value is a neutral ring: in its checker's hue it read as that checker passing. */\n.tgc-series-point.tgc-point-outline.tgc-strip-fail { stroke: var(--chart-ink-muted); }\n.tgc-strip-edge { fill: none; stroke: var(--tgc-series, var(--chart-muted)); stroke-width: 1.25; }\n.tgc-strip-observation:focus-visible { outline: none; }\n.tgc-strip-observation:focus-visible .tgc-series-hit { stroke: var(--chart-ink); stroke-width: 2; }\n.tgc-point-badge { paint-order: stroke; stroke: var(--chart-canvas); stroke-width: 3px; stroke-linejoin: round; font-family: var(--font-mono, \"JetBrains Mono\", ui-monospace, monospace); }\n.tgc-point-label { fill: var(--chart-ink-muted); }\n.tgc-glyph-note { fill: var(--chart-ink); font-weight: 600; }\n/* Status colours appear only on these glyphs. */\n.tgc-glyph-check { fill: var(--chart-pass, var(--status-running)); font-weight: 600; }\n.tgc-glyph-cross { fill: var(--chart-breach, var(--status-error)); font-weight: 600; }\n/* Recorded targets sit under the data: a target is a soft band between solid edges, a stretch a dashed outline. */\n.tgc-target-target { fill: color-mix(in srgb, var(--chart-ink) 7%, transparent); }\n.tgc-target-stretch { fill: none; }\n.tgc-target-target-edge, .tgc-target-target.tgc-target-line { stroke: var(--chart-ink-muted); stroke-width: 1; }\n.tgc-target-stretch-edge, .tgc-target-stretch.tgc-target-line { stroke: var(--chart-ink-muted); stroke-width: 1; stroke-dasharray: 4 3; }\n.tgc-target-label { fill: var(--chart-ink-muted); paint-order: stroke; stroke: var(--chart-canvas); stroke-width: 3px; stroke-linejoin: round; }\n/* Group headers: a labelled bracket above the plot and a light rule at each inner edge. */\n.tgc-group-rule { stroke: var(--chart-ink-muted); stroke-width: 1.5; }\n.tgc-group-edge { stroke: var(--chart-rule); stroke-width: 1; }\n.tgc-group-label { fill: var(--chart-ink); paint-order: stroke; stroke: var(--chart-canvas); stroke-width: 3px; stroke-linejoin: round; }\n/* The verdict key: glyphs in the neutral ink, so they describe a mark's form, not a series. */\n.tgc-key-mark { stroke-width: 1.5; }\n.tgc-key-filled { fill: var(--chart-ink-muted); stroke: none; }\n.tgc-key-outline { fill: none; stroke: var(--chart-ink-muted); }\n.tgc-key-dashed { fill: none; stroke: var(--chart-ink-muted); stroke-dasharray: 2 1.5; }\n";
|
|
1141
1141
|
/** --chart-* resolved per theme, for documents without brand tokens: light, dark by preference or [data-theme="dark"], light in print. */
|
|
1142
1142
|
const chartTokensCss = ":root{--chart-ink:hsl(0 0% 24.7059%);--chart-ink-muted:hsl(0 0% 41.9608%);--chart-muted:hsl(0 0% 36.4706%);--chart-grid:hsl(225 28.5714% 94.5098%);--chart-rule:hsl(222.8571 6.6667% 79.4118%);--chart-shade:color-mix(in srgb, hsl(246.2264 92.1739% 54.902%) 7%, transparent);--chart-accent:hsl(245.9504 54.2601% 56.2745%);--chart-tier-c:hsl(246.2687 54.4715% 75.8824%);--chart-tier-b:hsl(246.055 54.7739% 60.9804%);--chart-tier-a:hsl(246.0465 54.8936% 46.0784%);--chart-tier-s:hsl(245.5172 54.717% 31.1765%);--chart-pass:hsl(154.8148 64.8% 24.5098%);--chart-breach:hsl(0 75.1244% 39.4118%);--chart-canvas:hsl(0 0% 100%);color-scheme:light}\n@media (prefers-color-scheme: dark){:root:not([data-theme=\"light\"]){--chart-ink:hsl(0 0% 84.3137%);--chart-ink-muted:hsl(0 0% 60.3922%);--chart-muted:hsl(0 0% 67.0588%);--chart-grid:hsl(243.3333 20.4545% 17.2549%);--chart-rule:hsl(245 18.4615% 25.4902%);--chart-shade:color-mix(in srgb, hsl(245.5556 93.1034% 88.6275%) 7%, transparent);--chart-accent:hsl(246.0465 85.4305% 70.3922%);--chart-tier-c:hsl(246.1017 55.1402% 41.9608%);--chart-tier-b:hsl(246.1905 54.7826% 54.902%);--chart-tier-a:hsl(245.3933 54.6012% 68.0392%);--chart-tier-s:hsl(245.6604 54.6392% 80.9804%);--chart-pass:hsl(155.1429 64.8148% 78.8235%);--chart-breach:hsl(0 74.4681% 90.7843%);--chart-canvas:hsl(246.6667 31.0345% 5.6863%);color-scheme:dark}}\n:root[data-theme=\"dark\"]{--chart-ink:hsl(0 0% 84.3137%);--chart-ink-muted:hsl(0 0% 60.3922%);--chart-muted:hsl(0 0% 67.0588%);--chart-grid:hsl(243.3333 20.4545% 17.2549%);--chart-rule:hsl(245 18.4615% 25.4902%);--chart-shade:color-mix(in srgb, hsl(245.5556 93.1034% 88.6275%) 7%, transparent);--chart-accent:hsl(246.0465 85.4305% 70.3922%);--chart-tier-c:hsl(246.1017 55.1402% 41.9608%);--chart-tier-b:hsl(246.1905 54.7826% 54.902%);--chart-tier-a:hsl(245.3933 54.6012% 68.0392%);--chart-tier-s:hsl(245.6604 54.6392% 80.9804%);--chart-pass:hsl(155.1429 64.8148% 78.8235%);--chart-breach:hsl(0 74.4681% 90.7843%);--chart-canvas:hsl(246.6667 31.0345% 5.6863%);color-scheme:dark}\n@media print{:root,:root:not([data-theme=\"light\"]),:root[data-theme=\"dark\"]{--chart-ink:hsl(0 0% 24.7059%);--chart-ink-muted:hsl(0 0% 41.9608%);--chart-muted:hsl(0 0% 36.4706%);--chart-grid:hsl(225 28.5714% 94.5098%);--chart-rule:hsl(222.8571 6.6667% 79.4118%);--chart-shade:color-mix(in srgb, hsl(246.2264 92.1739% 54.902%) 7%, transparent);--chart-accent:hsl(245.9504 54.2601% 56.2745%);--chart-tier-c:hsl(246.2687 54.4715% 75.8824%);--chart-tier-b:hsl(246.055 54.7739% 60.9804%);--chart-tier-a:hsl(246.0465 54.8936% 46.0784%);--chart-tier-s:hsl(245.5172 54.717% 31.1765%);--chart-pass:hsl(154.8148 64.8% 24.5098%);--chart-breach:hsl(0 75.1244% 39.4118%);--chart-canvas:hsl(0 0% 100%);color-scheme:light}}";
|
|
1143
1143
|
//#endregion
|
|
@@ -2617,16 +2617,11 @@ function verdictKey(points, markers, x, y, width) {
|
|
|
2617
2617
|
/** Recorded trajectories. Explicit null observations break lines without interpolation. */
|
|
2618
2618
|
function timeSeries(rows, o) {
|
|
2619
2619
|
const id = o.id ?? "time-series";
|
|
2620
|
-
const gap = domainGap(o.x.domain) ?? domainGap(o.y.domain) ?? categoriesGap(o.x.categories, o.x.domain) ?? yAxisGap(o.y);
|
|
2620
|
+
const gap = domainGap(o.x.domain) ?? domainGap(o.y.domain) ?? categoriesGap(o.x.categories, o.x.domain) ?? yAxisGap(o.y) ?? eventsGap(o.marks, o.bands, o.x.domain);
|
|
2621
2621
|
if (gap) return {
|
|
2622
2622
|
id,
|
|
2623
2623
|
refused: gap
|
|
2624
2624
|
};
|
|
2625
|
-
const events = eventsGap(o.marks, o.bands, o.x.domain);
|
|
2626
|
-
if (events) return {
|
|
2627
|
-
id,
|
|
2628
|
-
refused: events
|
|
2629
|
-
};
|
|
2630
2625
|
if (!rows.length || rows.every((row) => !row.points.length)) return {
|
|
2631
2626
|
id,
|
|
2632
2627
|
refused: "No trajectory observations recorded."
|
|
@@ -2635,26 +2630,16 @@ function timeSeries(rows, o) {
|
|
|
2635
2630
|
id,
|
|
2636
2631
|
refused: "Duplicate trajectory ids."
|
|
2637
2632
|
};
|
|
2638
|
-
const columns = o.x.categories?.length ? new Set(o.x.categories.map((c) => c.at)) : null;
|
|
2639
2633
|
for (const row of rows) {
|
|
2640
2634
|
if (row.colorIndex !== void 0 && (!Number.isInteger(row.colorIndex) || row.colorIndex < 0)) return {
|
|
2641
2635
|
id,
|
|
2642
2636
|
refused: `${row.label}: a colour index is a whole number of at least 0.`
|
|
2643
2637
|
};
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
refused: columns ? `${row.label}: x values must be categories, in order.` : `${row.label}: x values must be finite, strictly increasing and inside the axis domain.`
|
|
2650
|
-
};
|
|
2651
|
-
previous = point.x;
|
|
2652
|
-
const problem = scalarGap(point.y, point.interval, point.observations, o.y.domain) ?? logValueGap(point.y, o.y.scale) ?? logValueGap(point.interval?.lower ?? null, o.y.scale) ?? pointFieldsGap(point);
|
|
2653
|
-
if (problem) return {
|
|
2654
|
-
id,
|
|
2655
|
-
refused: `${row.label} at ${point.x}: ${problem}`
|
|
2656
|
-
};
|
|
2657
|
-
}
|
|
2638
|
+
const problem = pointsGap(row.label, row.points, o.x, o.y.domain, o.y.scale);
|
|
2639
|
+
if (problem) return {
|
|
2640
|
+
id,
|
|
2641
|
+
refused: problem
|
|
2642
|
+
};
|
|
2658
2643
|
}
|
|
2659
2644
|
const category = new Map((o.x.categories ?? []).map((c) => [c.at, c.label]));
|
|
2660
2645
|
const xLabel = (x) => o.formatX ? o.formatX(x) : category.get(x) ?? String(x);
|
|
@@ -2664,12 +2649,9 @@ function timeSeries(rows, o) {
|
|
|
2664
2649
|
row,
|
|
2665
2650
|
point
|
|
2666
2651
|
})));
|
|
2667
|
-
const measured = flat.filter(({ point }) => point.y !== null);
|
|
2668
2652
|
const stripped = flat.filter(({ point }) => point.y === null && point.verdict !== void 0);
|
|
2669
2653
|
const recorded = hasPointFields(flat.map(({ point }) => point));
|
|
2670
2654
|
const marks = o.marks ?? [], bands = o.bands ?? [];
|
|
2671
|
-
const hasEvents = marks.length + bands.length > 0;
|
|
2672
|
-
const groups = bands.some((band) => band.kind === "group");
|
|
2673
2655
|
const stripName = o.y.strip?.trim() || "no value";
|
|
2674
2656
|
if (axisGutter(grid) >= 270) return {
|
|
2675
2657
|
id,
|
|
@@ -2717,8 +2699,8 @@ function timeSeries(rows, o) {
|
|
|
2717
2699
|
return {
|
|
2718
2700
|
id,
|
|
2719
2701
|
finding: `${capitalize(o.y.label)} by ${o.x.label}.`,
|
|
2720
|
-
lede: `${
|
|
2721
|
-
read: `Points show recorded values. Lines connect adjacent measured positions; missing positions break the line. Whiskers show recorded intervals.${recorded ? " A filled mark is a pass, an outline a fail, a dashed outline undecided; * is not comparable with the goal and ? of unknown comparability; ×n counts the recorded rows a mark stands for." : ""}${stripped.length ? ` Hatched squares under the plot are verdicts recorded without a value (${stripName}).` : ""}${o.y.targets?.length ? " Shaded bands are recorded targets; dashed bands are stretch targets." : ""}${
|
|
2702
|
+
lede: `${flat.filter(({ point }) => point.y !== null).length} of ${flat.length} positions have a recorded value.${stripped.length ? ` ${stripped.length} recorded ${plural(stripped.length, "verdict")} without a value ${stripped.length === 1 ? "sits" : "sit"} in the strip under the plot.` : ""}`,
|
|
2703
|
+
read: `Points show recorded values. Lines connect adjacent measured positions; missing positions break the line. Whiskers show recorded intervals.${recorded ? " A filled mark is a pass, an outline a fail, a dashed outline undecided; * is not comparable with the goal and ? of unknown comparability; ×n counts the recorded rows a mark stands for." : ""}${stripped.length ? ` Hatched squares under the plot are verdicts recorded without a value (${stripName}).` : ""}${o.y.targets?.length ? " Shaded bands are recorded targets; dashed bands are stretch targets." : ""}${bands.some((band) => band.kind === "group") ? " Brackets above the plot group positions, such as one run's releases." : ""}${marks.length + bands.length ? ` ${EVENTS_READ}` : ""}`,
|
|
2722
2704
|
svg: {
|
|
2723
2705
|
wide: plot(rows, o, wideWidth(o.width), xLabel, yLabel, grid, facts, stripName),
|
|
2724
2706
|
narrow: plot(rows, o, 358, xLabel, yLabel, grid, facts, stripName),
|
|
@@ -2772,18 +2754,8 @@ function plot(rows, o, width, xLabel, yLabel, grid, facts, stripName) {
|
|
|
2772
2754
|
cls: "tgc-ink-muted",
|
|
2773
2755
|
size: 12
|
|
2774
2756
|
}) + line(left, bottom + strip, right, bottom + strip, "tgc-gridline");
|
|
2775
|
-
const
|
|
2776
|
-
|
|
2777
|
-
for (const row of rows) for (const point of row.points) if (point.y === null === inStrip && (!inStrip || point.verdict !== void 0)) byX.set(point.x, [...byX.get(point.x) ?? [], point]);
|
|
2778
|
-
const at = /* @__PURE__ */ new Map();
|
|
2779
|
-
for (const [x, list] of byX) {
|
|
2780
|
-
const room = axis.column(x);
|
|
2781
|
-
const step = room === null ? 0 : Math.min(10, room / (list.length + 1));
|
|
2782
|
-
list.forEach((point, k) => at.set(point, px(x) + (k - (list.length - 1) / 2) * step));
|
|
2783
|
-
}
|
|
2784
|
-
return at;
|
|
2785
|
-
};
|
|
2786
|
-
const plotX = spread(false), stripX = spread(true);
|
|
2757
|
+
const all = rows.flatMap((row) => row.points);
|
|
2758
|
+
const plotX = spreadX(all.filter((point) => point.y !== null), axis, 10), stripX = spreadX(all.filter((point) => point.y === null && point.verdict !== void 0), axis, 10);
|
|
2787
2759
|
let legendY = bottom + strip + turn + 72;
|
|
2788
2760
|
let points = "", tips = "", rules = "";
|
|
2789
2761
|
const placed = [];
|
|
@@ -2793,7 +2765,7 @@ function plot(rows, o, width, xLabel, yLabel, grid, facts, stripName) {
|
|
|
2793
2765
|
for (const [seriesIndex, row] of rows.entries()) {
|
|
2794
2766
|
const ord = row.colorIndex ?? seriesIndex;
|
|
2795
2767
|
const dash = ord === 0 ? "" : `${2 + ord * 2} 3`;
|
|
2796
|
-
const group = checkerClass(row.colorIndex);
|
|
2768
|
+
const group = `${checkerClass(row.colorIndex)}${row.secondary ? " tgc-series-secondary" : ""}`.trim();
|
|
2797
2769
|
const wrapSeries = (inner) => group ? `<g class="${group}">${inner}</g>` : inner;
|
|
2798
2770
|
let segment = [];
|
|
2799
2771
|
let lines = "";
|
|
@@ -2846,9 +2818,9 @@ function plot(rows, o, width, xLabel, yLabel, grid, facts, stripName) {
|
|
|
2846
2818
|
point
|
|
2847
2819
|
});
|
|
2848
2820
|
const tipWidth = Math.min(280, width - 24), tipX = Math.max(12, Math.min(width - tipWidth - 12, x - tipWidth / 2));
|
|
2849
|
-
const tipLines = pointFacts.flatMap((fact) => wrap(fact, tipWidth - 24,
|
|
2850
|
-
tips += `<g class="tgc-series-tip" data-series-tip="${key}" aria-hidden="true">${rect(tipX, top, tipWidth, tipLines.length * 17 + 24, "tgc-tip-bg", "rx=\"7\"")}${textBlock(tipLines, tipX + 12, top + 20, 17, "class=\"tgc-tip-ink\" font-size=\"12\"")}</g>`;
|
|
2851
|
-
rules += `.tgc-trajectories:has([data-point="${key}"]:hover) [data-series-tip="${key}"],.tgc-trajectories:not(:has(.tgc-series-observation:hover)):has([data-point="${key}"]:focus-visible) [data-series-tip="${key}"]{opacity:1}`;
|
|
2821
|
+
const tipLines = pointFacts.flatMap((fact) => wrap(fact, tipWidth - 24, 14, "sans", 8).lines);
|
|
2822
|
+
tips += `<g class="tgc-series-tip" data-series-tip="${key}" aria-hidden="true">${o.crosshair ? line(x, top, x, bottom, "tgc-crosshair") : ""}${rect(tipX, top, tipWidth, tipLines.length * 17 + 24, "tgc-tip-bg", "rx=\"7\"")}${textBlock(tipLines, tipX + 12, top + 20, 17, "class=\"tgc-tip-ink\" font-size=\"12\"")}</g>`;
|
|
2823
|
+
rules += `.tgc-trajectories:has([data-point="${key}"]:is(:hover,[data-near])) [data-series-tip="${key}"],.tgc-trajectories:not(:has(.tgc-series-observation:hover)):has([data-point="${key}"]:focus-visible) [data-series-tip="${key}"]{opacity:1}`;
|
|
2852
2824
|
}
|
|
2853
2825
|
flush();
|
|
2854
2826
|
marks += wrapSeries(lines);
|
|
@@ -2867,6 +2839,29 @@ function plot(rows, o, width, xLabel, yLabel, grid, facts, stripName) {
|
|
|
2867
2839
|
const events = drawMarks(o.marks ?? [], px, top - groupRowHeight(o.bands), bottom, left, right, xLabel, 22);
|
|
2868
2840
|
return `<svg class="tgc-svg tgc-trajectories" xmlns="http://www.w3.org/2000/svg" width="${width}" height="${legendY + 12}" viewBox="0 0 ${width} ${legendY + 12}" role="group" aria-label="${esc(o.y.label)} by ${esc(o.x.label)}"><style>${rules}</style>${marks}${events}${groups}${points}${badges}${tips}</svg>`;
|
|
2869
2841
|
}
|
|
2842
|
+
/** Several points in one column (a check and a gate, two checkers) are spread across it in input order. */
|
|
2843
|
+
function spreadX(points, axis, maxStep) {
|
|
2844
|
+
const byX = /* @__PURE__ */ new Map();
|
|
2845
|
+
for (const point of points) byX.set(point.x, [...byX.get(point.x) ?? [], point]);
|
|
2846
|
+
const at = /* @__PURE__ */ new Map();
|
|
2847
|
+
for (const [x, list] of byX) {
|
|
2848
|
+
const room = axis.column(x), step = room === null ? 0 : Math.min(maxStep, room / (list.length + 1));
|
|
2849
|
+
list.forEach((point, k) => at.set(point, axis.px(x) + (k - (list.length - 1) / 2) * step));
|
|
2850
|
+
}
|
|
2851
|
+
return at;
|
|
2852
|
+
}
|
|
2853
|
+
/** Why recorded points cannot be drawn on these axes, or null; `timeSeries` and `smallMultiples` refuse alike. */
|
|
2854
|
+
function pointsGap(label, points, x, domain, scale) {
|
|
2855
|
+
const columns = x.categories?.length ? new Set(x.categories.map((c) => c.at)) : null;
|
|
2856
|
+
let previous = -Infinity;
|
|
2857
|
+
for (const point of points) {
|
|
2858
|
+
if (!Number.isFinite(point.x) || point.x < previous || columns && !columns.has(point.x) || point.x < x.domain[0] || point.x > x.domain[1]) return columns ? `${label}: x values must be categories, in order.` : `${label}: x values must be finite, in order and inside the axis domain.`;
|
|
2859
|
+
previous = point.x;
|
|
2860
|
+
const problem = scalarGap(point.y, point.interval, point.observations, domain) ?? logValueGap(point.y, scale) ?? logValueGap(point.interval?.lower ?? null, scale) ?? pointFieldsGap(point);
|
|
2861
|
+
if (problem) return `${label} at ${point.x}: ${problem}`;
|
|
2862
|
+
}
|
|
2863
|
+
return null;
|
|
2864
|
+
}
|
|
2870
2865
|
function axisGutter(ticks, stripName) {
|
|
2871
2866
|
return Math.max(64, ...ticks.map((tick) => Math.ceil(textWidth(tick.label, 12, "mono")) + 20), stripName ? Math.ceil(textWidth(stripName, 12)) + 14 : 0);
|
|
2872
2867
|
}
|
|
@@ -2892,7 +2887,6 @@ function smallMultiples(panels, o) {
|
|
|
2892
2887
|
id,
|
|
2893
2888
|
refused: "Columns must be a whole number of at least 1."
|
|
2894
2889
|
};
|
|
2895
|
-
const columns = o.x.categories?.length ? new Set(o.x.categories.map((c) => c.at)) : null;
|
|
2896
2890
|
if (!panels.length) return {
|
|
2897
2891
|
id,
|
|
2898
2892
|
refused: "No panels recorded."
|
|
@@ -2952,20 +2946,11 @@ function smallMultiples(panels, o) {
|
|
|
2952
2946
|
id,
|
|
2953
2947
|
refused: `${panel.label}: ${own}`
|
|
2954
2948
|
};
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
refused: columns ? `${panel.label}: x values must be categories, in order.` : `${panel.label}: x values must be finite, strictly increasing and inside the shared domain.`
|
|
2961
|
-
};
|
|
2962
|
-
previous = point.x;
|
|
2963
|
-
const problem = scalarGap(point.y, point.interval, point.observations, domain) ?? logValueGap(point.y, panel.scale) ?? pointFieldsGap(point);
|
|
2964
|
-
if (problem) return {
|
|
2965
|
-
id,
|
|
2966
|
-
refused: `${panel.label} at ${point.x}: ${problem}`
|
|
2967
|
-
};
|
|
2968
|
-
}
|
|
2949
|
+
const problem = pointsGap(panel.label, panel.points, o.x, domain, panel.scale);
|
|
2950
|
+
if (problem) return {
|
|
2951
|
+
id,
|
|
2952
|
+
refused: problem
|
|
2953
|
+
};
|
|
2969
2954
|
if (panel.end) {
|
|
2970
2955
|
const endGap = eventsGap([{
|
|
2971
2956
|
at: panel.end.at,
|
|
@@ -2976,7 +2961,7 @@ function smallMultiples(panels, o) {
|
|
|
2976
2961
|
id,
|
|
2977
2962
|
refused: `${panel.label}: ${endGap}`
|
|
2978
2963
|
};
|
|
2979
|
-
if (
|
|
2964
|
+
if (panel.points.at(-1).x > panel.end.at) return {
|
|
2980
2965
|
id,
|
|
2981
2966
|
refused: `${panel.label}: points are recorded after the panel's end.`
|
|
2982
2967
|
};
|
|
@@ -3144,13 +3129,7 @@ function draw(panels, o, width, xLabel, yLabelOf, marksOf) {
|
|
|
3144
3129
|
anchor: "end",
|
|
3145
3130
|
cls: "tgc-ink-muted tgc-num"
|
|
3146
3131
|
});
|
|
3147
|
-
const
|
|
3148
|
-
for (const point of panel.points) if (point.y !== null) shared.set(point.x, [...shared.get(point.x) ?? [], point]);
|
|
3149
|
-
const xOf = (point) => {
|
|
3150
|
-
const list = shared.get(point.x), room = axis.column(point.x);
|
|
3151
|
-
const step = room === null ? 0 : Math.min(9, room / (list.length + 1));
|
|
3152
|
-
return px(point.x) + (list.indexOf(point) - (list.length - 1) / 2) * step;
|
|
3153
|
-
};
|
|
3132
|
+
const xOf = spreadX(panel.points.filter((point) => point.y !== null), axis, 9);
|
|
3154
3133
|
const placed = [];
|
|
3155
3134
|
const pending = [];
|
|
3156
3135
|
let segment = [];
|
|
@@ -3163,7 +3142,7 @@ function draw(panels, o, width, xLabel, yLabelOf, marksOf) {
|
|
|
3163
3142
|
flush();
|
|
3164
3143
|
continue;
|
|
3165
3144
|
}
|
|
3166
|
-
const x = xOf(point), y = py(point.y);
|
|
3145
|
+
const x = xOf.get(point), y = py(point.y);
|
|
3167
3146
|
segment.push([x, y]);
|
|
3168
3147
|
if (!hasPointFields([point]) && point.marker === void 0) {
|
|
3169
3148
|
body += titled(`${panel.label} · ${o.x.label} ${xLabel(point.x)} · ${yLabel(point.y)} · ${observationLabel(point.observations)}`, circle(x, y, 2.5, "tgc-series-point"));
|
|
@@ -85,8 +85,8 @@ function ToolMark({ tool, label, size }) {
|
|
|
85
85
|
//#region src/react/agent-graph/agent-graph-layout.ts
|
|
86
86
|
/** Every agent card's size. Fixed, so a status or turn update never moves a card. */
|
|
87
87
|
const AGENT_CARD = {
|
|
88
|
-
width:
|
|
89
|
-
height:
|
|
88
|
+
width: 232,
|
|
89
|
+
height: 120
|
|
90
90
|
};
|
|
91
91
|
/** A live card: room for the header, a two-line thinking line and the last three events. Fixed for the same reason. */
|
|
92
92
|
const LIVE_AGENT_CARD = {
|
|
@@ -160,35 +160,29 @@ function gridSize(members, columns, card, collapsed = false) {
|
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
162
|
/** The boxes the layouter places: each ungrouped agent, and each grid as one block. */
|
|
163
|
-
function layoutUnits(topology, columns, card, collapsed) {
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
for (const id of topology.agentIds) if (!topology.groupOf.has(id) && !ownerGroup.has(id)) units.push({
|
|
163
|
+
function layoutUnits(topology, columns, card, collapsed, groupParents) {
|
|
164
|
+
const ownerGroup = new Map(groupParents ? topology.groups.map((group) => [group.parentId, group.id]) : []);
|
|
165
|
+
const units = topology.agentIds.filter((id) => !topology.groupOf.has(id) && !ownerGroup.has(id)).map((id) => ({
|
|
167
166
|
id,
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
});
|
|
167
|
+
...card
|
|
168
|
+
}));
|
|
169
|
+
units.push(...topology.groups.map((group) => ({
|
|
170
|
+
id: group.id,
|
|
171
|
+
...gridSize(group.memberIds.length, columns, card, collapsed.has(group.id))
|
|
172
|
+
})));
|
|
175
173
|
const unitOf = (id) => topology.groupOf.get(id) ?? ownerGroup.get(id) ?? id;
|
|
176
174
|
const seen = /* @__PURE__ */ new Set();
|
|
177
|
-
const links = [];
|
|
178
|
-
for (const s of topology.spawns) {
|
|
179
|
-
const from = unitOf(s.from);
|
|
180
|
-
const to = unitOf(s.to);
|
|
181
|
-
const key = `${from}\u0000${to}`;
|
|
182
|
-
if (from === to || seen.has(key)) continue;
|
|
183
|
-
seen.add(key);
|
|
184
|
-
links.push({
|
|
185
|
-
from,
|
|
186
|
-
to
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
175
|
return {
|
|
190
176
|
units,
|
|
191
|
-
links
|
|
177
|
+
links: topology.spawns.flatMap((spawn) => {
|
|
178
|
+
const from = unitOf(spawn.from), to = unitOf(spawn.to), key = `${from}\u0000${to}`;
|
|
179
|
+
if (from === to || seen.has(key)) return [];
|
|
180
|
+
seen.add(key);
|
|
181
|
+
return [{
|
|
182
|
+
from,
|
|
183
|
+
to
|
|
184
|
+
}];
|
|
185
|
+
})
|
|
192
186
|
};
|
|
193
187
|
}
|
|
194
188
|
async function placeWithElk(units, links, direction) {
|
|
@@ -271,8 +265,8 @@ function placeByDepth(units, links, direction) {
|
|
|
271
265
|
return at;
|
|
272
266
|
}
|
|
273
267
|
/** Lay the run out with every grid `columns` wide. */
|
|
274
|
-
async function layoutAgents(topology, direction, columns, card = AGENT_CARD, collapsed = /* @__PURE__ */ new Set()) {
|
|
275
|
-
const { units, links } = layoutUnits(topology, columns, card, collapsed);
|
|
268
|
+
async function layoutAgents(topology, direction, columns, card = AGENT_CARD, collapsed = /* @__PURE__ */ new Set(), groupParents = true) {
|
|
269
|
+
const { units, links } = layoutUnits(topology, columns, card, collapsed, groupParents);
|
|
276
270
|
const placed = await placeWithElk(units, links, direction) ?? placeByDepth(units, links, direction);
|
|
277
271
|
const positions = /* @__PURE__ */ new Map();
|
|
278
272
|
const frames = [];
|
|
@@ -301,9 +295,12 @@ async function layoutAgents(topology, direction, columns, card = AGENT_CARD, col
|
|
|
301
295
|
...size,
|
|
302
296
|
collapsed: closed
|
|
303
297
|
});
|
|
304
|
-
positions.set(g.parentId, {
|
|
298
|
+
positions.set(g.parentId, groupParents ? {
|
|
305
299
|
x: origin.x + FRAME_PAD,
|
|
306
300
|
y: origin.y + 8
|
|
301
|
+
} : {
|
|
302
|
+
...positions.get(g.parentId),
|
|
303
|
+
y: origin.y
|
|
307
304
|
});
|
|
308
305
|
if (closed) continue;
|
|
309
306
|
const cols = Math.max(1, Math.min(columns, g.memberIds.length));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as AGENT_CARD, B as AgentLeafGroup, C as shortModel, D as fmtEventTime, E as fmtDuration, F as AgentGraphEdgeKind, G as LIVE_AGENT_CARD, H as CardSize, I as AgentGraphEvent, K as buildAgentTopology, L as AgentGraphNode, M as AgentGraphActivity, N as AgentGraphDirection, O as fmtTokenTotal, P as AgentGraphEdge, R as AgentGraphStatus, S as parseActionPath, T as fmtCost, U as FRAME_CAPTION, V as AgentTopology, W as GRID_MIN_LEAVES, _ as useRunNow, a as RESTING_SHADOW, b as actionPathLabel, c as motionAllowed, d as agentQuietActivity, f as ambientStyle, g as useMotionAllowed, h as useAttention, i as RESTING_BORDER, j as AgentFrame, k as fmtTokens, l as useColorMode, m as tokenMilliseconds, n as GraphErrorBoundary, o as edgeColor, p as syncAmbient, q as layoutAgents, r as retryImport, s as SELECTION_OUTLINE, t as loadElk, u as activityTiming, v as ActionPathParts, w as clampPreview, x as humanizeIdentifier, y as LeadCase, z as AgentLayout } from "../../elk-loader-
|
|
1
|
+
import { A as AGENT_CARD, B as AgentLeafGroup, C as shortModel, D as fmtEventTime, E as fmtDuration, F as AgentGraphEdgeKind, G as LIVE_AGENT_CARD, H as CardSize, I as AgentGraphEvent, K as buildAgentTopology, L as AgentGraphNode, M as AgentGraphActivity, N as AgentGraphDirection, O as fmtTokenTotal, P as AgentGraphEdge, R as AgentGraphStatus, S as parseActionPath, T as fmtCost, U as FRAME_CAPTION, V as AgentTopology, W as GRID_MIN_LEAVES, _ as useRunNow, a as RESTING_SHADOW, b as actionPathLabel, c as motionAllowed, d as agentQuietActivity, f as ambientStyle, g as useMotionAllowed, h as useAttention, i as RESTING_BORDER, j as AgentFrame, k as fmtTokens, l as useColorMode, m as tokenMilliseconds, n as GraphErrorBoundary, o as edgeColor, p as syncAmbient, q as layoutAgents, r as retryImport, s as SELECTION_OUTLINE, t as loadElk, u as activityTiming, v as ActionPathParts, w as clampPreview, x as humanizeIdentifier, y as LeadCase, z as AgentLayout } from "../../elk-loader-uOcDmUG_.js";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
//#region src/react/agent-graph/agent-hub-layout.d.ts
|
|
4
4
|
/** A tool or service an agent calls: drawn as an outer node with its brand mark. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { D as statusMeta, E as runStatus, O as createLucideIcon, T as RunStatusIcon } from "../../flow-waterfall-model-DvrgPLGS.js";
|
|
2
|
-
import { a as LIVE_AGENT_CARD, c as AgentMark, i as GRID_MIN_LEAVES, l as GraphMarksProvider, n as AGENT_CARD, o as buildAgentTopology, r as FRAME_CAPTION, s as layoutAgents, t as LatestEvents, u as ToolMark } from "../../latest-events-
|
|
2
|
+
import { a as LIVE_AGENT_CARD, c as AgentMark, i as GRID_MIN_LEAVES, l as GraphMarksProvider, n as AGENT_CARD, o as buildAgentTopology, r as FRAME_CAPTION, s as layoutAgents, t as LatestEvents, u as ToolMark } from "../../latest-events-CmbyqEIe.js";
|
|
3
3
|
import { C as fmtCost, D as fmtTokens, E as fmtTokenTotal, S as clampPreview, T as fmtEventTime, _ as syncAmbient, a as motionAllowed, b as useMotionAllowed, c as humanizeIdentifier, d as loadElk, f as GraphErrorBoundary, g as ambientStyle, h as agentQuietActivity, i as SELECTION_OUTLINE, l as parseActionPath, m as activityTiming, n as RESTING_SHADOW, o as useColorMode, p as retryImport, r as edgeColor, s as actionPathLabel, t as RESTING_BORDER, u as shortModel, v as tokenMilliseconds, w as fmtDuration, x as useRunNow, y as useAttention } from "../../graph-style-fealnLLn.js";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { Suspense, lazy, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
@@ -1122,7 +1122,7 @@ function AgentHubCanvas(props) {
|
|
|
1122
1122
|
* taking the pane down.
|
|
1123
1123
|
*/
|
|
1124
1124
|
function lazyCanvas() {
|
|
1125
|
-
return lazy(() => retryImport(() => import("../../AgentGraphCanvas-
|
|
1125
|
+
return lazy(() => retryImport(() => import("../../AgentGraphCanvas-C5Ee8Z6q.js").then((m) => ({ default: m.AgentGraphCanvas }))));
|
|
1126
1126
|
}
|
|
1127
1127
|
/** The agents as a list, for when the canvas cannot load. */
|
|
1128
1128
|
function AgentListFallback({ nodes, selectedId, onSelect, className, onRetry }) {
|
|
@@ -1131,12 +1131,12 @@ function AgentListFallback({ nodes, selectedId, onSelect, className, onRetry })
|
|
|
1131
1131
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
1132
1132
|
className: "mb-1 flex items-center justify-between gap-2",
|
|
1133
1133
|
children: [/* @__PURE__ */ jsx("p", {
|
|
1134
|
-
className: "text-muted-foreground text-
|
|
1134
|
+
className: "text-muted-foreground text-[13px]",
|
|
1135
1135
|
children: "Couldn't draw the graph — showing the agents."
|
|
1136
1136
|
}), /* @__PURE__ */ jsx("button", {
|
|
1137
1137
|
type: "button",
|
|
1138
1138
|
onClick: onRetry,
|
|
1139
|
-
className: "shrink-0 rounded border border-border px-1.5 py-0.5 text-muted-foreground text-
|
|
1139
|
+
className: "shrink-0 rounded border border-border px-1.5 py-0.5 text-muted-foreground text-[13px] transition hover:text-foreground",
|
|
1140
1140
|
children: "Retry"
|
|
1141
1141
|
})]
|
|
1142
1142
|
}), /* @__PURE__ */ jsx("ul", {
|
|
@@ -1145,7 +1145,7 @@ function AgentListFallback({ nodes, selectedId, onSelect, className, onRetry })
|
|
|
1145
1145
|
type: "button",
|
|
1146
1146
|
"aria-pressed": selectedId === n.id,
|
|
1147
1147
|
onClick: () => onSelect?.(n.id),
|
|
1148
|
-
className: "flex w-full items-center justify-between gap-2 rounded px-2 py-1 text-left text-foreground text-
|
|
1148
|
+
className: "flex w-full items-center justify-between gap-2 rounded px-2 py-1 text-left text-foreground text-[13px] hover:bg-muted aria-pressed:bg-muted",
|
|
1149
1149
|
children: [/* @__PURE__ */ jsx("span", {
|
|
1150
1150
|
className: "truncate",
|
|
1151
1151
|
children: n.label
|
|
@@ -1174,7 +1174,7 @@ function AgentGraphTree(props) {
|
|
|
1174
1174
|
}),
|
|
1175
1175
|
children: /* @__PURE__ */ jsx(Suspense, {
|
|
1176
1176
|
fallback: /* @__PURE__ */ jsx("div", {
|
|
1177
|
-
className: `agent-graph flex items-center justify-center rounded-lg border border-border border-dashed text-muted-foreground text-
|
|
1177
|
+
className: `agent-graph flex items-center justify-center rounded-lg border border-border border-dashed text-muted-foreground text-[13px] ${props.className ?? ""}`,
|
|
1178
1178
|
children: "Loading graph…"
|
|
1179
1179
|
}),
|
|
1180
1180
|
children: /* @__PURE__ */ jsx(Canvas, { ...props })
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as shortModel, D as fmtEventTime, E as fmtDuration, O as fmtTokenTotal, S as parseActionPath, T as fmtCost, _ as useRunNow, a as RESTING_SHADOW, b as actionPathLabel, c as motionAllowed, d as agentQuietActivity, f as ambientStyle, g as useMotionAllowed, h as useAttention, i as RESTING_BORDER, k as fmtTokens, l as useColorMode, m as tokenMilliseconds, n as GraphErrorBoundary, o as edgeColor, p as syncAmbient, r as retryImport, s as SELECTION_OUTLINE, t as loadElk, u as activityTiming, v as ActionPathParts, w as clampPreview, x as humanizeIdentifier, y as LeadCase } from "../../elk-loader-
|
|
1
|
+
import { C as shortModel, D as fmtEventTime, E as fmtDuration, O as fmtTokenTotal, S as parseActionPath, T as fmtCost, _ as useRunNow, a as RESTING_SHADOW, b as actionPathLabel, c as motionAllowed, d as agentQuietActivity, f as ambientStyle, g as useMotionAllowed, h as useAttention, i as RESTING_BORDER, k as fmtTokens, l as useColorMode, m as tokenMilliseconds, n as GraphErrorBoundary, o as edgeColor, p as syncAmbient, r as retryImport, s as SELECTION_OUTLINE, t as loadElk, u as activityTiming, v as ActionPathParts, w as clampPreview, x as humanizeIdentifier, y as LeadCase } from "../../elk-loader-uOcDmUG_.js";
|
|
2
2
|
//#region src/react/agent-graph/motion.d.ts
|
|
3
3
|
/** Brand owns complete duration/easing roles. Keep properties explicit. */
|
|
4
4
|
export declare const MOTION_CONTROL = "[transition:color_var(--motion-control),background-color_var(--motion-control),border-color_var(--motion-control),box-shadow_var(--motion-control),transform_var(--motion-control),scale_var(--motion-control),opacity_var(--motion-control)] motion-reduce:transition-none";
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
--radius-full: 999px;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
@layer base {
|
|
26
|
+
@layer utilities.agent-graph-base {
|
|
27
27
|
@scope (.agent-graph, .ag-root, .ag-latest, [data-agent-metrics]) {
|
|
28
28
|
*, :scope:is(*), ::after, :scope:is(::after), ::before, :scope:is(::before), ::backdrop, :scope:is(::backdrop), ::file-selector-button, :scope:is(::file-selector-button) {
|
|
29
29
|
box-sizing: border-box;
|
|
@@ -173,7 +173,7 @@
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
|
-
@layer utilities {
|
|
176
|
+
@layer utilities.agent-graph-utilities {
|
|
177
177
|
@scope (.agent-graph, .ag-root, .ag-latest, [data-agent-metrics]) {
|
|
178
178
|
.pointer-events-auto, :scope:is(.pointer-events-auto) {
|
|
179
179
|
pointer-events: auto;
|
|
@@ -210,6 +210,9 @@
|
|
|
210
210
|
.right-3, :scope:is(.right-3) {
|
|
211
211
|
right: calc(var(--spacing) * 3);
|
|
212
212
|
}
|
|
213
|
+
.left-3, :scope:is(.left-3) {
|
|
214
|
+
left: calc(var(--spacing) * 3);
|
|
215
|
+
}
|
|
213
216
|
.container, :scope:is(.container) {
|
|
214
217
|
width: 100%;
|
|
215
218
|
@media (width >= 40rem) {
|
|
@@ -279,9 +282,6 @@
|
|
|
279
282
|
.min-h-0, :scope:is(.min-h-0) {
|
|
280
283
|
min-height: 0px;
|
|
281
284
|
}
|
|
282
|
-
.min-h-4, :scope:is(.min-h-4) {
|
|
283
|
-
min-height: calc(var(--spacing) * 4);
|
|
284
|
-
}
|
|
285
285
|
.min-h-10, :scope:is(.min-h-10) {
|
|
286
286
|
min-height: calc(var(--spacing) * 10);
|
|
287
287
|
}
|
|
@@ -336,6 +336,9 @@
|
|
|
336
336
|
.justify-center, :scope:is(.justify-center) {
|
|
337
337
|
justify-content: center;
|
|
338
338
|
}
|
|
339
|
+
.justify-start, :scope:is(.justify-start) {
|
|
340
|
+
justify-content: flex-start;
|
|
341
|
+
}
|
|
339
342
|
.gap-0\.5, :scope:is(.gap-0\.5) {
|
|
340
343
|
gap: calc(var(--spacing) * 0.5);
|
|
341
344
|
}
|
|
@@ -466,6 +469,9 @@
|
|
|
466
469
|
.text-\[10px\], :scope:is(.text-\[10px\]) {
|
|
467
470
|
font-size: 10px;
|
|
468
471
|
}
|
|
472
|
+
.text-\[13px\], :scope:is(.text-\[13px\]) {
|
|
473
|
+
font-size: 13px;
|
|
474
|
+
}
|
|
469
475
|
.leading-4, :scope:is(.leading-4) {
|
|
470
476
|
--tw-leading: calc(var(--spacing) * 4);
|
|
471
477
|
line-height: calc(var(--spacing) * 4);
|
|
@@ -1568,6 +1574,26 @@ svg.react-flow__connectionline {
|
|
|
1568
1574
|
min-width: 44px;
|
|
1569
1575
|
}
|
|
1570
1576
|
}
|
|
1577
|
+
.ag-topology {
|
|
1578
|
+
--ag-surface: hsl(var(--hsl-raised));
|
|
1579
|
+
--font-size-xs: 13px;
|
|
1580
|
+
--text-xs: 13px;
|
|
1581
|
+
}
|
|
1582
|
+
.ag-topology :is(.text-xs, .ag-parent-label) {
|
|
1583
|
+
font-size: 13px;
|
|
1584
|
+
}
|
|
1585
|
+
.ag-topology .ag-team-frame {
|
|
1586
|
+
background: hsl(var(--hsl-card)) !important;
|
|
1587
|
+
}
|
|
1588
|
+
.ag-topology .ag-agent-card {
|
|
1589
|
+
background: var(--ag-surface);
|
|
1590
|
+
}
|
|
1591
|
+
.ag-topology .ag-agent-card[data-selected] {
|
|
1592
|
+
background: hsl(var(--hsl-raised-2));
|
|
1593
|
+
}
|
|
1594
|
+
.ag-topology .react-flow__node-agentFrame .react-flow__handle-left {
|
|
1595
|
+
top: 40px;
|
|
1596
|
+
}
|
|
1571
1597
|
@property --tw-rotate-x {
|
|
1572
1598
|
syntax: "*";
|
|
1573
1599
|
inherits: false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/charts",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.19",
|
|
4
4
|
"description": "Static SVG and HTML figures for benchmarks and operational timing. Draws from producer data and refuses what it cannot draw honestly.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|