@tangle-network/charts 0.21.21 → 0.21.23

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.
@@ -366,8 +366,11 @@ interface FlowPause {
366
366
  }
367
367
  /** A host verdict: `label` is the legend text, `tone` the status color. */
368
368
  interface FlowOutcome {
369
- tone: 'ok' | 'neutral' | 'failed' | 'warn';
369
+ /** `live` is a running agent: the one running hue the host uses for every live mark. */
370
+ tone: 'ok' | 'live' | 'neutral' | 'failed' | 'warn';
370
371
  label: string;
372
+ /** A hollow ring in the tone instead of a filled dot: a second outcome on the same tone (Done beside Shipped, Stopped beside a plain end). */
373
+ ring?: boolean;
371
374
  }
372
375
  /** A labelled instant on the trace clock, such as a release or an alert; drawn as a line across the rows. */
373
376
  interface FlowMarker {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as BreakdownInput, B as Refusal, C as MarkKind, D as YScale, E as XCategory, F as Figure, H as Tier, I as Interval, L as IntervalMethod, M as CostBasis, N as Estimate, O as YTarget, P as Exclusion, R as MatrixCell, S as ChartMark, T as MarkerShape, U as TierCounts, V as Setup, W as isRefusal, _ as checkerColor, a as TimelineLane, b as BandKind, c as TimelineTick, d as ENDING_COLORS, f as ENDING_ORDER, g as TickOutcome, h as RunEnding, i as FlowTrace, j as ComparisonRow, k as YTick, l as timeline, m as OUTCOME_ORDER, n as FlowOutcome, o as TimelineOptions, p as OUTCOME_COLORS, r as FlowSpan, s as TimelineSpan, t as FlowMarker, u as CHECKER_HUES, v as PointFields, w as MarkVerdict, x as ChartBand, y as PointGlyph, z as RateRow } from "./flow-trace-CSy-mQ31.js";
1
+ import { A as BreakdownInput, B as Refusal, C as MarkKind, D as YScale, E as XCategory, F as Figure, H as Tier, I as Interval, L as IntervalMethod, M as CostBasis, N as Estimate, O as YTarget, P as Exclusion, R as MatrixCell, S as ChartMark, T as MarkerShape, U as TierCounts, V as Setup, W as isRefusal, _ as checkerColor, a as TimelineLane, b as BandKind, c as TimelineTick, d as ENDING_COLORS, f as ENDING_ORDER, g as TickOutcome, h as RunEnding, i as FlowTrace, j as ComparisonRow, k as YTick, l as timeline, m as OUTCOME_ORDER, n as FlowOutcome, o as TimelineOptions, p as OUTCOME_COLORS, r as FlowSpan, s as TimelineSpan, t as FlowMarker, u as CHECKER_HUES, v as PointFields, w as MarkVerdict, x as ChartBand, y as PointGlyph, z as RateRow } from "./flow-trace-KGCtujbr.js";
2
2
  //#region src/block.d.ts
3
3
  interface RenderFigureOptions {
4
4
  /** Chart uses a closed data disclosure; plot keeps only the SVG and accessible data. */
@@ -21,6 +21,8 @@
21
21
  --fw-failed: var(--surface-danger-text);
22
22
  --fw-ok: var(--surface-success-text);
23
23
  --fw-amber: var(--surface-warning-text);
24
+ /* The running hue: a live agent's dot, outcome and pill. The host sets it to the one running ink its other pages use. */
25
+ --fw-live: var(--fw-accent);
24
26
  /* Step kinds inside an agent bar: the chart accent and a categorical hue, far from the outcome greens and reds. */
25
27
  --fw-kind-model: var(--chart-accent);
26
28
  --fw-kind-tool: var(--tone-cyan-icon);
@@ -83,12 +85,12 @@
83
85
  .fw-root .fw-title-line { display: flex; align-items: center; gap: 8px; min-width: 0; }
84
86
  .fw-root .fw-title-glyph { display: inline-flex; color: var(--fw-accent); }
85
87
  .fw-root .fw-title { font-size: 14px; line-height: 20px; font-weight: 600; letter-spacing: -.005em; white-space: nowrap; }
86
- .fw-root .fw-state { display: inline-flex; align-items: center; gap: 8px; height: 20px; padding: 0 8px; border-radius: var(--radius-pill); font-size: 12px; font-weight: 600; color: var(--fw-muted); background: var(--fw-well); flex-shrink: 0; }
88
+ .fw-root .fw-state { display: inline-flex; align-items: center; gap: 8px; height: 20px; padding: 0 8px; border-radius: var(--radius-pill); font-size: 14px; font-weight: 600; color: var(--fw-muted); background: var(--fw-well); flex-shrink: 0; }
87
89
  .fw-root .fw-state::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
88
- .fw-root .fw-state-live { color: var(--fw-accent); background: color-mix(in oklab, var(--fw-accent) 12%, transparent); }
90
+ .fw-root .fw-state-live { color: var(--fw-live); background: color-mix(in oklab, var(--fw-live) 12%, transparent); }
89
91
  .fw-root .fw-state-live::before { animation: fw-pulse 1.6s var(--fw-ease) infinite; }
90
- .fw-root .fw-headline { color: var(--fw-muted); display: flex; align-items: center; gap: 8px; }
91
- .fw-root .fw-done-mark { display: inline-grid; place-items: center; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid var(--fw-ok); color: var(--fw-ok); font-size: 12px; line-height: 1; }
92
+ .fw-root .fw-headline { color: var(--fw-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
93
+ .fw-root .fw-headline::before { content: "·"; margin-right: 8px; }
92
94
  .fw-root .fw-tools { display: flex; align-items: center; gap: 8px; }
93
95
  .fw-root .fw-views { display: inline-flex; padding: 4px; border-radius: var(--radius-md); background: var(--fw-well); border: 1px solid var(--fw-border); }
94
96
  .fw-root .fw-view-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0 12px; border-radius: var(--radius-sm); font-size: 14px; color: var(--fw-muted); cursor: pointer; transition: background-color 120ms var(--fw-ease), color 120ms var(--fw-ease); }
@@ -104,16 +106,16 @@
104
106
  .fw-root .fw-summary { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); border-bottom: 1px solid var(--fw-border); }
105
107
  .fw-root .fw-summary > div { padding: 12px 16px; min-width: 0; }
106
108
  .fw-root .fw-summary > div + div { border-left: 1px solid var(--fw-border); }
107
- .fw-root .fw-summary dt { font-size: 12px; line-height: 16px; color: var(--fw-muted); }
109
+ .fw-root .fw-summary dt { font-size: 14px; line-height: 18px; color: var(--fw-muted); }
108
110
  .fw-root .fw-value { margin-top: 4px; font-size: 18px; line-height: 24px; font-weight: 600; letter-spacing: -.01em; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
109
111
  .fw-root .fw-has-errors .fw-value { color: var(--fw-failed); }
110
112
 
111
113
  /* Axis */
112
- .fw-root .fw-axis { display: grid; grid-template-columns: var(--fw-label-col) minmax(0, 1fr); height: 32px; color: var(--fw-muted); font-size: 12px; }
114
+ .fw-root .fw-axis { display: grid; grid-template-columns: var(--fw-label-col) minmax(0, 1fr); height: 32px; color: var(--fw-muted); font-size: 14px; }
113
115
  .fw-root .fw-axis-top { border-bottom: 1px solid var(--fw-border); }
114
116
  .fw-root .fw-axis-label { position: relative; display: flex; align-items: center; gap: 8px; padding: 0 12px 0 16px; border-right: 1px solid var(--fw-border); min-width: 0; }
115
117
  .fw-root .fw-branch-controls { margin-left: auto; display: flex; gap: 4px; }
116
- .fw-root .fw-text-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0 8px; border-radius: var(--radius-sm); font-size: 12px; color: var(--fw-muted); cursor: pointer; white-space: nowrap; }
118
+ .fw-root .fw-text-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0 8px; border-radius: var(--radius-sm); font-size: 14px; color: var(--fw-muted); cursor: pointer; white-space: nowrap; }
117
119
  .fw-root .fw-text-button:hover { background: var(--fw-hover); color: var(--fw-text); }
118
120
  .fw-root .fw-resize { position: absolute; top: 0; right: -4px; width: 8px; height: 100%; cursor: col-resize; z-index: 2; }
119
121
  .fw-root .fw-resize:hover, .fw-root .fw-resize:focus-visible { background: linear-gradient(90deg, transparent 3px, var(--fw-accent) 3px 5px, transparent 5px); outline: 0; }
@@ -142,14 +144,14 @@
142
144
  .fw-root .fw-pause { top: 0; bottom: 0; background: color-mix(in oklab, var(--fw-text) 4%, transparent); border-inline: 1px dashed color-mix(in oklab, var(--fw-text) 18%, transparent); }
143
145
  .fw-root .fw-pause-mark { bottom: 3px; height: 6px; border-radius: 2px; cursor: help;
144
146
  background: repeating-linear-gradient(-45deg, var(--fw-hatch) 0 1px, transparent 1px 4px); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-text) 18%, transparent); }
145
- .fw-root .fw-marker-label { position: absolute; top: 2px; left: 3px; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0 4px; border-radius: var(--radius-tag); font-size: 12px; line-height: 16px; color: var(--fw-marker); background: var(--fw-surface); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-marker) 40%, transparent); }
147
+ .fw-root .fw-marker-label { position: absolute; top: 2px; left: 3px; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0 4px; border-radius: var(--radius-tag); font-size: 14px; line-height: 18px; color: var(--fw-marker); background: var(--fw-surface); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-marker) 40%, transparent); }
146
148
  .fw-root .fw-marker-label-left { left: auto; right: 3px; }
147
149
  .fw-root .fw-marker[data-tone="warn"], .fw-root .fw-event[data-tone="warn"] { --fw-marker: var(--fw-amber); }
148
150
  /* The event track: one dot per marker on the axis; click or N/P moves the cursor there. */
149
151
  .fw-root .fw-event { --fw-marker: var(--fw-muted); position: absolute; bottom: 2px; width: 8px; height: 8px; margin-left: -4px; padding: 0; border: 0; border-radius: 50%; background: var(--fw-marker); box-shadow: 0 0 0 2px var(--fw-surface); cursor: pointer; z-index: 4; left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); }
150
152
  .fw-root .fw-event[data-tone="ok"] { --fw-marker: var(--fw-ok); }
151
153
  .fw-root .fw-event[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--fw-surface), 0 0 0 4px var(--fw-marker); }
152
- .fw-root .fw-event-label { position: absolute; bottom: 14px; z-index: 5; transform: translateX(-50%); left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); padding: 0 8px; border-radius: var(--radius-tag); background: var(--fw-surface); box-shadow: inset 0 0 0 1px var(--fw-border); color: var(--fw-text); font-size: 12px; line-height: 20px; white-space: nowrap; pointer-events: none; }
154
+ .fw-root .fw-event-label { position: absolute; bottom: 14px; z-index: 5; transform: translateX(-50%); left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); padding: 0 8px; border-radius: var(--radius-tag); background: var(--fw-surface); box-shadow: inset 0 0 0 1px var(--fw-border); color: var(--fw-text); font-size: 14px; line-height: 20px; white-space: nowrap; pointer-events: none; }
153
155
  .fw-root .fw-event[data-tone="failed"] { --fw-marker: var(--fw-failed); }
154
156
  .fw-root .fw-event:hover, .fw-root .fw-event:focus-visible { transform: scale(1.4); outline: 0; }
155
157
  .fw-root .fw-cursor { position: absolute; top: 0; bottom: 0; width: 1px; background: color-mix(in oklab, var(--fw-text) 70%, transparent); left: calc(var(--fw-pad) + (var(--fw-t) - var(--fw-v0)) * var(--fw-k) * 1px); }
@@ -188,7 +190,7 @@
188
190
  .fw-root .fw-detail { color: var(--fw-muted); overflow: hidden; min-width: 0; display: inline-flex; align-items: center; gap: 4px; max-width: 100%; }
189
191
  .fw-root .fw-detail-icon { display: inline-flex; }
190
192
  .fw-root .fw-detail-icon :is(svg, img) { width: 12px; height: 12px; }
191
- .fw-root .fw-count { flex-shrink: 0; height: 18px; padding: 0 8px; border-radius: var(--radius-pill); background: var(--fw-well); border: 1px solid var(--fw-border); color: var(--fw-muted); font-size: 12px; line-height: 16px; font-variant-numeric: tabular-nums; }
193
+ .fw-root .fw-count { flex-shrink: 0; height: 18px; padding: 0 8px; border-radius: var(--radius-pill); background: var(--fw-well); border: 1px solid var(--fw-border); color: var(--fw-muted); font-size: 14px; line-height: 18px; font-variant-numeric: tabular-nums; }
192
194
  .fw-root .fw-alert { display: inline-flex; color: var(--fw-failed); flex-shrink: 0; }
193
195
  .fw-root .fw-alert svg { width: 13px; height: 13px; }
194
196
  .fw-root .fw-spinner { width: 10px; height: 10px; border-radius: 50%; border: 1.5px solid color-mix(in oklab, var(--fw-accent) 30%, transparent); border-top-color: var(--fw-accent); animation: fw-spin .8s linear infinite; flex-shrink: 0; }
@@ -198,16 +200,18 @@
198
200
  .fw-root .fw-view-agents .fw-names { flex-direction: column; align-items: flex-start; gap: 0; }
199
201
  .fw-root .fw-view-agents .fw-span-name { font-weight: 600; line-height: 18px; }
200
202
  /* One 16 px line that wraps its last parts out of sight: when the line is full a count drops whole, never "48 …". */
201
- .fw-root .fw-view-agents .fw-subtitle { display: flex; align-items: center; column-gap: 4px; height: 16px; overflow: hidden; max-width: 100%; min-width: 0; color: var(--fw-muted); font-size: 12px; line-height: 16px; }
203
+ .fw-root .fw-view-agents .fw-subtitle { display: flex; align-items: center; column-gap: 4px; height: 18px; overflow: hidden; max-width: 100%; min-width: 0; color: var(--fw-muted); font-size: 14px; line-height: 18px; }
202
204
  .fw-root .fw-view-agents .fw-subtitle-extra { flex: none; white-space: nowrap; }
203
205
  /* Only the tail wraps, so the first part is always on its line and shrinks with an ellipsis; later parts wrap out of sight. */
204
- .fw-root .fw-view-agents .fw-subtitle-tail { display: flex; flex-wrap: wrap; column-gap: 4px; height: 16px; overflow: hidden; min-width: 0; flex: 0 1 auto; }
206
+ .fw-root .fw-view-agents .fw-subtitle-tail { display: flex; flex-wrap: wrap; column-gap: 4px; height: 18px; overflow: hidden; min-width: 0; flex: 0 1 auto; }
205
207
  .fw-root .fw-view-agents .fw-subtitle-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
206
208
  .fw-root .fw-view-agents .fw-detail-icon { flex: none; width: 16px; height: 16px; align-items: center; justify-content: center; overflow: hidden; }
207
- .fw-root .fw-view-agents .fw-detail { font-size: 12px; line-height: 16px; }
209
+ .fw-root .fw-view-agents .fw-detail { font-size: 14px; line-height: 18px; }
208
210
  .fw-root .fw-agent-dot { flex: 0 0 8px; width: 8px; height: 8px; border-radius: 50%; background: var(--line, var(--fw-muted)); }
209
211
  /* The dot is outcome only (critique P0 #3): an agent with no recorded outcome gets a hollow ring, never its lane hue. */
210
212
  .fw-root .fw-row:not([data-outcome]) .fw-agent-dot:not([data-outcome]) { background: transparent; box-shadow: inset 0 0 0 1.5px var(--fw-muted); }
213
+ .fw-root :is(.fw-agent-dot, .fw-legend .fw-swatch)[data-ring] { background: transparent; box-shadow: inset 0 0 0 2px var(--line, var(--fw-muted)); }
214
+ .fw-root :is(.fw-agent-dot, .fw-legend .fw-swatch)[data-outcome="live"]:not([data-ring]) { box-shadow: 0 0 0 3px color-mix(in oklab, var(--fw-live) 22%, transparent); }
211
215
 
212
216
  /* Lanes and marks */
213
217
  .fw-root .fw-lane { position: relative; overflow: hidden; min-width: 0; }
@@ -234,9 +238,9 @@
234
238
  /* While searching, ancestors shown only for context recede and the matching names stand out. */
235
239
  .fw-root .fw-row[data-search="context"] .fw-label { opacity: 0.55; }
236
240
  .fw-root .fw-row[data-search="match"] .fw-name-text { font-weight: 600; }
237
- /* A restart: an amber notch through the bar at that moment; the bar keeps its own state. */
241
+ /* A restart: a notch in the failure ink through the bar at that moment (the run overview's ✕); the bar keeps its own state. */
238
242
  .fw-root .fw-mark { position: absolute; z-index: 2; left: calc(var(--x0) - 1px); width: 2px; top: calc(50% + var(--fw-bar-shift) - var(--fw-bar-h) / 2 - 4px); height: calc(var(--fw-bar-h) + 8px);
239
- border-radius: 2px; background: var(--fw-amber); box-shadow: 0 0 0 1px var(--fw-surface); pointer-events: auto; }
243
+ border-radius: 2px; background: var(--fw-failed); box-shadow: 0 0 0 1px var(--fw-surface); pointer-events: auto; }
240
244
  /* Centered by top, not a negative margin, so measured spacing stays on the grid. */
241
245
  .fw-root :is(.fw-bar, .fw-seg) { top: calc(50% + var(--fw-bar-shift) - var(--fw-bar-h) / 2); height: var(--fw-bar-h); width: max(var(--w), calc(var(--on) * 3px)); border-radius: var(--radius-tag); cursor: pointer; display: flex; align-items: center; justify-content: center; overflow: hidden; }
242
246
  .fw-root .fw-bar { background: var(--fill); box-shadow: inset 0 0 0 1px var(--line); }
@@ -263,12 +267,12 @@
263
267
  failure keeps its red glyph, so color only ever means state. */
264
268
  .fw-root.fw-fill-muted .fw-view-agents [data-tone] { --tone: var(--fw-h0); }
265
269
  /* With per-agent hues the slowest agent keeps its own hue and says so with the amber pill (round 27: an amber-brown bar read "muddy"). */
266
- .fw-root.fw-fill-muted .fw-view-agents .fw-row[data-slowest], .fw-root.fw-fill-muted .fw-view-agents .fw-row[data-slowest] [data-tone] { --tone: var(--fw-amber); }
267
270
  .fw-root [data-outcome="ok"], .fw-root [data-outcome="ok"] [data-tone] { --tone: var(--fw-ok); }
268
271
  .fw-root [data-outcome="failed"], .fw-root [data-outcome="failed"] [data-tone] { --tone: var(--fw-failed); }
269
272
  .fw-root [data-outcome="warn"], .fw-root [data-outcome="warn"] [data-tone] { --tone: var(--fw-amber); }
273
+ .fw-root [data-outcome="live"], .fw-root [data-outcome="live"] [data-tone] { --tone: var(--fw-live); }
270
274
  .fw-root [data-outcome="neutral"], .fw-root [data-outcome="neutral"] [data-tone] { --tone: var(--fw-muted); }
271
- .fw-root .fw-legend { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 8px 0 0; padding: 0; list-style: none; color: var(--fw-muted); font-size: 12px; line-height: 16px; }
275
+ .fw-root .fw-legend { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 8px 0 0; padding: 0; list-style: none; color: var(--fw-muted); font-size: 14px; line-height: 18px; }
272
276
  .fw-root .fw-legend li { display: inline-flex; align-items: center; gap: 8px; }
273
277
  .fw-root .fw-legends { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 24px; margin-top: 8px; }
274
278
  .fw-root .fw-legends .fw-legend { margin: 0; }
@@ -279,6 +283,7 @@
279
283
  .fw-root.fw-fill-agent .fw-kinds .fw-swatch-tool { background: light-dark(oklch(0.80 0 0), oklch(0.64 0 0)); }
280
284
  .fw-root .fw-kinds .fw-swatch-tool { width: 12px; height: 8px; border-radius: 2px; background: var(--fw-kind-tool); }
281
285
  .fw-root .fw-kinds .fw-swatch-wait { width: 12px; height: 8px; border-radius: 2px; background: color-mix(in oklab, var(--fw-text) 12%, transparent); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-text) 18%, transparent); }
286
+ .fw-root .fw-kinds .fw-swatch-event { width: 8px; height: 8px; border-radius: 50%; background: var(--fw-muted); box-shadow: 0 0 0 2px var(--fw-surface); }
282
287
  .fw-root .fw-kinds .fw-swatch-handoff { width: 10px; height: 8px; border-left: 1.5px solid var(--fw-muted); border-bottom: 1.5px solid var(--fw-muted); border-bottom-left-radius: 3px; }
283
288
  .fw-root .fw-description { margin: 0; color: var(--fw-muted); font-size: 14px; line-height: 20px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
284
289
  .fw-root .fw-legend .fw-swatch { width: 8px; height: 8px; border-radius: 50%; background: var(--line); }
@@ -303,7 +308,7 @@
303
308
  .fw-root.fw-fill-agent .fw-view-agents :is(.fw-seg-tool, .fw-seg-failed) { background: light-dark(oklch(from var(--tone) 0.80 min(c, 0.10) h), oklch(from var(--tone) 0.64 min(c, 0.10) h)); box-shadow: none; }
304
309
  /* A failed turn is still the agent's work: it keeps the agent's hue, and the red cross on the ending turn says it failed. */
305
310
  .fw-root.fw-fill-agent .fw-view-agents .fw-seg-failed { box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-failed) 55%, transparent); }
306
- .fw-root .fw-view-agents .fw-seg { container-type: inline-size; justify-content: flex-start; font-size: 12px; line-height: 16px; white-space: nowrap; color: var(--fw-seg-ink); }
311
+ .fw-root .fw-view-agents .fw-seg { container-type: inline-size; justify-content: flex-start; font-size: 14px; line-height: 18px; white-space: nowrap; color: var(--fw-seg-ink); }
307
312
  /* Label ink holds 4.5:1 on its fill: page-light on the dark light-theme model step (L 0.50), page-dark on every other step
308
313
  (light tool 0.80, dark pastels 0.82 and 0.64). */
309
314
  .fw-root .fw-view-agents .fw-seg-model { --fw-seg-ink: light-dark(var(--fw-surface), hsl(var(--hsl-background))); }
@@ -317,9 +322,9 @@
317
322
  .fw-root .fw-seg-chip { padding: 0 4px; border-radius: var(--radius-pill); background: color-mix(in oklab, currentColor 18%, transparent); font-weight: 600; font-variant-numeric: tabular-nums; }
318
323
  .fw-root .fw-seg-meta { font-variant-numeric: tabular-nums; }
319
324
  @container (max-width: 24px) { .fw-root :is(.fw-seg-label, .fw-view-agents .fw-seg .fw-glyph) { display: none; } }
320
- /* Fail and retry read as filled chips inside the bar, red and amber, as the references draw them. */
325
+ /* Fail and retry read as filled chips inside the bar: red, and a quiet grey for a retry that recovered. */
321
326
  .fw-root .fw-view-agents .fw-seg .fw-glyph { flex: none; margin-left: 6px; display: grid; place-items: center; width: 14px; height: 14px; border-radius: 50%; background: var(--fw-failed); color: light-dark(var(--fw-surface), hsl(var(--hsl-background))); }
322
- .fw-root .fw-view-agents .fw-seg .fw-glyph[data-glyph="retry"] { background: var(--fw-amber); }
327
+ .fw-root .fw-view-agents .fw-seg .fw-glyph[data-glyph="retry"] { background: var(--fw-muted); }
323
328
  .fw-root .fw-view-agents .fw-seg .fw-glyph svg { width: 9px; height: 9px; stroke-width: 2.5; }
324
329
  .fw-root:is(.fw-fill-muted, .fw-fill-outcome) .fw-view-agents :is(.fw-seg-alt.fw-seg-model, .fw-seg-alt.fw-seg-tool) { filter: brightness(.88); }
325
330
  .fw-root .fw-seg-failed { background: color-mix(in oklab, var(--fw-failed) 18%, var(--fw-surface)); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-failed) 70%, transparent); color: var(--fw-failed); }
@@ -333,9 +338,9 @@
333
338
  .fw-root .fw-end { z-index: 2; display: inline-flex; align-items: center; gap: 8px; top: calc(50% + var(--fw-bar-shift) - var(--fw-bar-h) / 2); height: var(--fw-bar-h);
334
339
  left: min(calc(var(--x0) + max(var(--w), 3px) + 4px), calc(100% - var(--fw-end-w, 0px) - 4px)); padding: 0 4px; border-radius: var(--radius-tag);
335
340
  background: linear-gradient(var(--fw-tint, transparent) 0 0), var(--fw-surface); white-space: nowrap; pointer-events: none; }
336
- .fw-root .fw-duration { font-size: 12px; color: var(--fw-muted); font-variant-numeric: tabular-nums; }
341
+ .fw-root .fw-duration { font-size: 14px; color: var(--fw-muted); font-variant-numeric: tabular-nums; }
337
342
  .fw-root .fw-k-failed ~ .fw-end .fw-duration { color: var(--fw-failed); }
338
- .fw-root .fw-badge { height: 18px; padding: 0 8px; border-radius: var(--radius-pill); font-size: 12px; line-height: 16px; font-weight: 600; color: var(--fw-amber); background: color-mix(in oklab, var(--fw-amber) 13%, transparent); border: 1px solid color-mix(in oklab, var(--fw-amber) 28%, transparent); }
343
+ .fw-root .fw-badge { height: 20px; padding: 0 8px; border-radius: var(--radius-pill); font-size: 14px; line-height: 20px; font-weight: 500; color: var(--fw-muted); background: transparent; box-shadow: inset 0 0 0 1px var(--fw-border); }
339
344
 
340
345
  /* Handoff elbows: 1.5 px stroke, 6 px radius, a 5 px dot where the handoff leaves its sender. */
341
346
  /* Under the rows (which follow it), so end labels and badges always sit above the connectors. */
@@ -364,33 +369,34 @@
364
369
  /* The playhead: a round knob on a thin neutral track, visible at rest without outweighing the chart. */
365
370
  .fw-root.fw-root-agents .fw-slider::-webkit-slider-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--fw-text); box-shadow: 0 0 0 3px var(--fw-surface); }
366
371
  .fw-root.fw-root-agents .fw-slider::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--fw-text); box-shadow: 0 0 0 3px var(--fw-surface); }
367
- .fw-root .fw-card { position: absolute; z-index: 20; width: min(320px, calc(100% - 16px)); padding: 12px; border-radius: var(--radius-xl); background: var(--fw-raised); border: 1px solid var(--fw-border);
372
+ .fw-root .fw-card { position: absolute; z-index: 20; width: min(320px, calc(100% - 16px)); max-height: min(60vh, 560px); overflow-y: auto; overscroll-behavior: contain; padding: 12px; border-radius: var(--radius-xl); background: var(--fw-raised); border: 1px solid var(--fw-border);
368
373
  box-shadow: var(--shadow-dropdown); animation: fw-arrive 140ms var(--fw-ease); }
369
374
  /* A hover card never covers the next mark: only a pinned card (a dialog) takes the pointer. */
370
375
  .fw-root .fw-card[role="tooltip"] { pointer-events: none; }
371
376
  .fw-root .fw-card-above .fw-card { transform: translateY(-100%); animation-name: fw-arrive-above; }
372
- .fw-root .fw-card-agent { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 12px; font-weight: 600; }
377
+ .fw-root .fw-card-agent { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 14px; font-weight: 600; }
373
378
  .fw-root .fw-card-agent .fw-agent-dot { align-self: center; }
374
379
  .fw-root .fw-card-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
375
380
  .fw-root .fw-card-head .fw-icon-tile { flex-basis: 28px; width: 28px; height: 28px; border-radius: var(--radius-md); }
376
381
  .fw-root .fw-card-head .fw-icon-tile :is(svg, img) { width: 16px; height: 16px; }
377
382
  .fw-root .fw-card-name { flex: 1; min-width: 0; font-size: 14px; line-height: 20px; font-weight: 600; overflow-wrap: anywhere; }
378
- .fw-root .fw-pill { flex-shrink: 0; height: 20px; padding: 0 8px; border-radius: var(--radius-pill); font-size: 12px; line-height: 20px; font-weight: 600; }
383
+ .fw-root .fw-pill { flex-shrink: 0; height: 20px; padding: 0 8px; border-radius: var(--radius-pill); font-size: 14px; line-height: 20px; font-weight: 600; }
379
384
  .fw-root .fw-pill-done { color: var(--fw-ok); background: color-mix(in oklab, var(--fw-ok) 13%, transparent); }
380
- .fw-root .fw-pill-running { color: var(--fw-accent); background: color-mix(in oklab, var(--fw-accent) 13%, transparent); }
385
+ .fw-root .fw-pill-running { color: var(--fw-live); background: color-mix(in oklab, var(--fw-live) 13%, transparent); }
381
386
  .fw-root .fw-pill-failed { color: var(--fw-failed); background: color-mix(in oklab, var(--fw-failed) 13%, transparent); }
382
387
  .fw-root .fw-icon-button { appearance: none; display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 0; border-radius: var(--radius-md); background: transparent; color: var(--fw-muted); cursor: pointer; flex-shrink: 0; }
383
388
  .fw-root .fw-icon-button:hover:not(:disabled) { background: var(--fw-hover); color: var(--fw-text); }
384
389
  .fw-root .fw-card-close { width: 20px; height: 20px; margin-right: -4px; }
385
390
  /* The card's table: label and value on one line each, every value with its unit; a tool breakdown sits under its total. */
386
- .fw-root .fw-card-table { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 6px 16px; padding-top: 8px; border-top: 1px solid var(--fw-border); font-size: 12px; line-height: 16px; }
391
+ .fw-root .fw-card-table { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 6px 16px; padding-top: 8px; border-top: 1px solid var(--fw-border); font-size: 14px; line-height: 18px; }
387
392
  .fw-root .fw-card-table > div { display: contents; }
388
- .fw-root .fw-card-table dt, .fw-root .fw-card-label { font-size: 12px; line-height: 16px; color: var(--fw-muted); }
393
+ .fw-root .fw-card-table dt, .fw-root .fw-card-label { font-size: 14px; line-height: 18px; color: var(--fw-muted); }
389
394
  .fw-root .fw-card-table dd { min-width: 0; font-weight: 500; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
390
395
  /* A value of several parts wraps between parts, never inside one ("4.0M" / "cached"). */
391
396
  .fw-root .fw-card-parts { display: flex; flex-wrap: wrap; column-gap: 4px; }
392
397
  .fw-root .fw-card-parts > * { white-space: nowrap; }
393
- .fw-root .fw-card-parts > * + *::before { content: "· "; color: var(--fw-muted); }
398
+ .fw-root .fw-card-parts:not(.fw-card-stack) > * + *::before { content: "· "; color: var(--fw-muted); }
399
+ .fw-root .fw-card-stack { flex-direction: column; }
394
400
  .fw-root .fw-card-tools { display: grid; grid-template-columns: minmax(0, max-content) max-content; justify-content: start; column-gap: 16px; margin-top: 4px; color: var(--fw-muted); font-weight: 400; }
395
401
  .fw-root .fw-card-tools > span { display: contents; }
396
402
  .fw-root .fw-card-tools > span > :first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@@ -399,7 +405,7 @@
399
405
  .fw-root .fw-card-compact .fw-card-agent { font-size: 14px; line-height: 20px; }
400
406
  .fw-root .fw-card-who { min-width: 0; flex: 1; overflow: hidden; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
401
407
  .fw-root .fw-pill-outcome { color: var(--line); background: color-mix(in oklab, var(--tone) 16%, transparent); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--tone) 35%, transparent); }
402
- .fw-root .fw-card-step { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--fw-border); color: var(--fw-muted); font-size: 12px; line-height: 16px; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
408
+ .fw-root .fw-card-step { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--fw-border); color: var(--fw-muted); font-size: 14px; line-height: 18px; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
403
409
  .fw-root .fw-card-result { margin-top: 8px; font-size: 14px; line-height: 20px; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
404
410
  .fw-root .fw-card-text p { margin-top: 4px; color: var(--fw-text); overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
405
411
 
@@ -1,4 +1,4 @@
1
- import { B as Refusal, C as MarkKind, D as YScale, E as XCategory, F as Figure, O as YTarget, S as ChartMark, T as MarkerShape, a as TimelineLane, b as BandKind, c as TimelineTick, g as TickOutcome, i as FlowTrace, k as YTick, o as TimelineOptions, r as FlowSpan, s as TimelineSpan, t as FlowMarker, v as PointFields, w as MarkVerdict, x as ChartBand, y as PointGlyph } from "../flow-trace-CSy-mQ31.js";
1
+ import { B as Refusal, C as MarkKind, D as YScale, E as XCategory, F as Figure, O as YTarget, S as ChartMark, T as MarkerShape, a as TimelineLane, b as BandKind, c as TimelineTick, g as TickOutcome, i as FlowTrace, k as YTick, o as TimelineOptions, r as FlowSpan, s as TimelineSpan, t as FlowMarker, v as PointFields, w as MarkVerdict, x as ChartBand, y as PointGlyph } from "../flow-trace-KGCtujbr.js";
2
2
  import { CSSProperties, ReactElement, ReactNode } from "react";
3
3
  //#region src/react/sparkline.d.ts
4
4
  type SparklineDirection = "rising" | "falling" | "flat";
@@ -2450,7 +2450,7 @@ function formatCost(usd) {
2450
2450
  /** 9.8k, 754.4k, 1.2M with the exact count left to a title. */
2451
2451
  function formatCount(n) {
2452
2452
  if (n < 1e3) return String(n);
2453
- if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
2453
+ if (n < 1e6) return `${(n / 1e3).toFixed(1)}K`;
2454
2454
  return `${(n / 1e6).toFixed(1)}M`;
2455
2455
  }
2456
2456
  /** Merge marks closer than `gapMs` so a dense row stays a readable strip of at most `cap` marks. Marks narrower
@@ -2859,7 +2859,7 @@ function agentFamilies(agents, open) {
2859
2859
  const running = members.some((row) => spanRunning(row.span));
2860
2860
  const known = members.map((row) => row.span.costUsd ?? row.rollupCostUsd).filter((cost) => cost !== void 0);
2861
2861
  const failed = members.filter((row) => row.span.outcome ? row.span.outcome.tone === "failed" : !row.ok).length;
2862
- const outcome = members.every((row) => row.span.outcome && row.span.outcome.tone === members[0].span.outcome?.tone) ? members[0].span.outcome : void 0;
2862
+ const outcome = members.every((row) => row.span.outcome && row.span.outcome.tone === members[0].span.outcome?.tone && !row.span.outcome.ring === !members[0].span.outcome?.ring) ? members[0].span.outcome : void 0;
2863
2863
  out.push({
2864
2864
  costFloor: known.length < members.length || members.some((row) => row.costFloor),
2865
2865
  id,
@@ -2913,7 +2913,7 @@ function agentFamilies(agents, open) {
2913
2913
  }
2914
2914
  return out;
2915
2915
  }
2916
- /** The second line of an agent row in parts: how it ended and how much it did (failed, 3 LLM calls, 412 tool calls; 6 agents, 4 failed).
2916
+ /** The second line of an agent row in parts: how it ended and how much it did (failed, 3 model calls, 412 tool calls; 6 agents, 4 failed).
2917
2917
  * The row shows the parts in order and drops the last ones whole when the line is full, never half a word. */
2918
2918
  function agentSummary(row) {
2919
2919
  if (row.id.startsWith("family:")) {
@@ -2928,7 +2928,7 @@ function agentSummary(row) {
2928
2928
  const tools = row.counts?.tool ?? 0;
2929
2929
  return [
2930
2930
  row.span.outcome?.label ?? (spanRunning(row.span) ? "running" : !row.ok ? "failed" : row.kind === "pipeline" ? "" : "done"),
2931
- calls ? `${calls} LLM ${calls === 1 ? "call" : "calls"}` : "",
2931
+ calls ? `${calls} model ${calls === 1 ? "call" : "calls"}` : "",
2932
2932
  tools ? `${tools} tool ${tools === 1 ? "call" : "calls"}` : ""
2933
2933
  ].filter(Boolean);
2934
2934
  }
@@ -3170,7 +3170,7 @@ const FitGlyph = () => svg(/* @__PURE__ */ jsx("path", { d: "M2.5 6V3.5a1 1 0 0
3170
3170
  const keepWords = (name) => name.replace(/-/g, "‑");
3171
3171
  const KIND_LABEL = {
3172
3172
  pipeline: "Agent",
3173
- model: "LLM call",
3173
+ model: "Model call",
3174
3174
  tool: "Tool call",
3175
3175
  waiting: "Waiting",
3176
3176
  handoff: "Handoff"
@@ -3203,19 +3203,19 @@ function metrics(row, clockMs, full) {
3203
3203
  ["cached", "cached"]
3204
3204
  ].flatMap(([key, unit]) => tokens[key] === void 0 ? [] : [count(tokens[key], unit)]);
3205
3205
  out.push(["Tokens", /* @__PURE__ */ jsx("span", {
3206
- className: "fw-card-parts",
3206
+ className: `fw-card-parts${parts.length > 2 ? " fw-card-stack" : ""}`,
3207
3207
  children: parts.map((part, i) => /* @__PURE__ */ jsx("span", { children: part }, i))
3208
3208
  })]);
3209
3209
  }
3210
3210
  const latency = row.modelMs;
3211
- if (latency?.length) out.push(["LLM latency", /* @__PURE__ */ jsxs("span", {
3211
+ if (latency?.length) out.push(["Model latency", /* @__PURE__ */ jsxs("span", {
3212
3212
  className: "fw-card-parts",
3213
3213
  children: [/* @__PURE__ */ jsxs("span", { children: [formatSpanDuration(percentile(latency, .5)), " median"] }), latency.length >= 10 && /* @__PURE__ */ jsxs("span", { children: [formatSpanDuration(percentile(latency, .95)), " p95"] })]
3214
3214
  })]);
3215
3215
  const cost = span.costUsd ?? row.rollupCostUsd;
3216
3216
  if (cost !== void 0) out.push(["Cost", rowCostText(row, cost)]);
3217
3217
  const counts = row.counts;
3218
- if (counts?.model) out.push(["LLM calls", counts.model.toLocaleString()]);
3218
+ if (counts?.model) out.push(["Model calls", counts.model.toLocaleString()]);
3219
3219
  if (counts?.tool) {
3220
3220
  const tools = Object.entries(counts.tools).sort(([a, x], [b, y]) => y - x || a.localeCompare(b));
3221
3221
  const rest = tools.length - TOOLS_SHOWN;
@@ -3285,6 +3285,7 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
3285
3285
  children: [
3286
3286
  /* @__PURE__ */ jsx("span", {
3287
3287
  className: "fw-agent-dot",
3288
+ "data-ring": outcome?.ring || void 0,
3288
3289
  "aria-hidden": true
3289
3290
  }),
3290
3291
  /* @__PURE__ */ jsx("span", {
@@ -3547,13 +3548,14 @@ const FlowRow = memo(function FlowRow(props) {
3547
3548
  className: "fw-agent-dot",
3548
3549
  "data-tone": "0",
3549
3550
  "data-outcome": span.outcome.tone,
3551
+ "data-ring": span.outcome.ring || void 0,
3550
3552
  title: span.outcome.label,
3551
3553
  "aria-hidden": true
3552
3554
  }) : agentView && !step && /* @__PURE__ */ jsx("span", {
3553
3555
  className: "fw-agent-dot",
3554
3556
  "aria-hidden": true
3555
3557
  }),
3556
- running && /* @__PURE__ */ jsx("span", {
3558
+ running && !(fillByAgent && isAgent && span.outcome) && /* @__PURE__ */ jsx("span", {
3557
3559
  className: "fw-spinner",
3558
3560
  role: "status",
3559
3561
  "aria-label": "running"
@@ -3656,8 +3658,9 @@ const FlowRow = memo(function FlowRow(props) {
3656
3658
  });
3657
3659
  }, (a, b) => a.row === b.row && a.view === b.view && a.top === b.top && a.collapsed === b.collapsed && a.searching === b.searching && a.slowest === b.slowest && a.costliest === b.costliest && a.critical === b.critical && a.matched === b.matched && a.active === b.active && a.focusable === b.focusable && a.selected === b.selected && a.sticky === b.sticky && a.motion === b.motion && a.renderIcon === b.renderIcon && a.fillByAgent === b.fillByAgent && a.sharedDetail === b.sharedDetail && a.sharedOutcome === b.sharedOutcome && a.expanded === b.expanded && (a.clockMs === b.clockMs || !open(a.row, a.clockMs) && !open(b.row, b.clockMs)));
3658
3660
  /** An elbow connector between two rows at one moment, drawn in the row's tone; appears when the clock reaches it. */
3659
- function LinkMark({ link, rowHeight, hot }) {
3660
- const upper = Math.min(link.from, link.to), lower = Math.max(link.from, link.to);
3661
+ /** `short`: only the last row of the connector, an elbow into the row it reaches. */
3662
+ function LinkMark({ link, rowHeight, hot, short }) {
3663
+ const lower = Math.max(link.from, link.to), upper = short ? Math.max(Math.min(link.from, link.to), lower - 1) : Math.min(link.from, link.to);
3661
3664
  const style = {
3662
3665
  ...at(link.atMs, link.atMs),
3663
3666
  top: upper * rowHeight + rowHeight / 2 + 5,
@@ -4187,11 +4190,16 @@ function FlowBody(props) {
4187
4190
  children: [/* @__PURE__ */ jsx("div", {
4188
4191
  className: "fw-links",
4189
4192
  "aria-hidden": true,
4190
- children: links.filter((link) => Math.max(link.from, link.to) >= first && Math.min(link.from, link.to) <= last).map((link) => /* @__PURE__ */ jsx(LinkMark, {
4191
- link,
4192
- rowHeight,
4193
- hot: props.focusId !== void 0 && (rows[link.from]?.id === props.focusId || rows[link.to]?.id === props.focusId)
4194
- }, link.id))
4193
+ children: links.filter((link) => Math.max(link.from, link.to) >= first && Math.min(link.from, link.to) <= last).map((link) => {
4194
+ const hot = props.focusId !== void 0 && (rows[link.from]?.id === props.focusId || rows[link.to]?.id === props.focusId);
4195
+ if (view === "agents" && !hot && link.id.startsWith("r-")) return null;
4196
+ return /* @__PURE__ */ jsx(LinkMark, {
4197
+ link,
4198
+ rowHeight,
4199
+ hot,
4200
+ short: view === "agents" && !hot
4201
+ }, link.id);
4202
+ })
4195
4203
  }), rows.slice(first, last).map((item, offset) => row(item, first + offset))]
4196
4204
  }), rows.length === 0 && /* @__PURE__ */ jsx("p", {
4197
4205
  role: "status",
@@ -4544,9 +4552,9 @@ function summaryCells(trace, at, durationMs) {
4544
4552
  `${tokens.toLocaleString()} tokens`
4545
4553
  ],
4546
4554
  [
4547
- "LLM calls",
4555
+ "Model calls",
4548
4556
  modelCalls === void 0 ? "—" : modelCalls.toLocaleString(),
4549
- modelCalls === void 0 ? "Model output windows do not establish the number of LLM requests" : void 0
4557
+ modelCalls === void 0 ? "Model output windows do not establish the number of model requests" : void 0
4550
4558
  ],
4551
4559
  [
4552
4560
  "Tool calls",
@@ -4559,6 +4567,7 @@ function summaryCells(trace, at, durationMs) {
4559
4567
  }
4560
4568
  /** One legend item per outcome label the agents carry, with its count; nothing when no agent has an outcome. */
4561
4569
  const TONE_ORDER = [
4570
+ "live",
4562
4571
  "ok",
4563
4572
  "warn",
4564
4573
  "failed",
@@ -4583,6 +4592,25 @@ function KindLegend({ rows }) {
4583
4592
  }), name] }, kind))
4584
4593
  });
4585
4594
  }
4595
+ /** The axis dots and the dashed lines they drop: one key naming what they mark (a release, or the kinds the host gave). */
4596
+ function MarkerLegend({ markers }) {
4597
+ if (!markers?.length) return null;
4598
+ const kinds = [...new Set(markers.map((marker) => marker.kind ?? "event"))];
4599
+ const name = kinds.length === 1 ? `${kinds[0][0].toUpperCase()}${kinds[0].slice(1)}` : "Event";
4600
+ return /* @__PURE__ */ jsx("ul", {
4601
+ className: "fw-legend fw-kinds",
4602
+ "aria-label": "Markers",
4603
+ children: /* @__PURE__ */ jsxs("li", { children: [
4604
+ /* @__PURE__ */ jsx("span", {
4605
+ className: "fw-swatch-event",
4606
+ "aria-hidden": true
4607
+ }),
4608
+ name,
4609
+ " ",
4610
+ /* @__PURE__ */ jsx("b", { children: markers.length })
4611
+ ] })
4612
+ });
4613
+ }
4586
4614
  function OutcomeLegend({ agents }) {
4587
4615
  const items = /* @__PURE__ */ new Map();
4588
4616
  for (const row of agents) {
@@ -4590,6 +4618,7 @@ function OutcomeLegend({ agents }) {
4590
4618
  if (!outcome) continue;
4591
4619
  const item = items.get(outcome.label) ?? {
4592
4620
  tone: outcome.tone,
4621
+ ring: outcome.ring,
4593
4622
  count: 0
4594
4623
  };
4595
4624
  item.count += 1;
@@ -4605,6 +4634,7 @@ function OutcomeLegend({ agents }) {
4605
4634
  className: "fw-swatch",
4606
4635
  "data-tone": "0",
4607
4636
  "data-outcome": item.tone,
4637
+ "data-ring": item.ring || void 0,
4608
4638
  "aria-hidden": true
4609
4639
  }),
4610
4640
  label,
@@ -4861,15 +4891,19 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
4861
4891
  if (!box) return;
4862
4892
  const hostTop = root.current.getBoundingClientRect().top;
4863
4893
  const room = {
4864
- above: hostTop + card.anchorTop - 16,
4894
+ above: card.anchorTop - 16,
4865
4895
  below: globalThis.innerHeight - (hostTop + card.anchorBottom) - 16
4866
4896
  };
4867
4897
  const preferred = card.above ? room.above : room.below;
4868
4898
  const other = card.above ? room.below : room.above;
4899
+ const above = box.height <= preferred || box.height > other && preferred >= other ? card.above : !card.above;
4900
+ const floor = globalThis.innerHeight - hostTop - 8 - box.height;
4901
+ const top = Math.max(Math.min(above ? card.anchorTop - 8 - box.height : card.anchorBottom + 8, floor), Math.max(8, 8 - hostTop));
4869
4902
  setCard({
4870
4903
  ...card,
4871
4904
  fitted: true,
4872
- above: box.height <= preferred || box.height > other && preferred >= other ? card.above : !card.above
4905
+ above,
4906
+ top
4873
4907
  });
4874
4908
  }, [card, cardId]);
4875
4909
  latest.current.inspect = (target, element, x) => {
@@ -4986,51 +5020,47 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
4986
5020
  className: "fw-heading",
4987
5021
  children: [/* @__PURE__ */ jsxs("div", {
4988
5022
  className: "fw-title-block",
4989
- children: [
4990
- /* @__PURE__ */ jsxs("div", {
4991
- className: "fw-title-line",
4992
- children: [
4993
- /* @__PURE__ */ jsx("span", {
4994
- className: "fw-title-glyph",
4995
- "aria-hidden": true,
4996
- children: /* @__PURE__ */ jsx(TraceGlyph, {})
4997
- }),
4998
- /* @__PURE__ */ jsx("h3", {
4999
- className: "fw-title",
5000
- children: label
5001
- }),
5002
- description && /* @__PURE__ */ jsx("p", {
5003
- className: "fw-description",
5004
- title: description,
5005
- children: description
5006
- }),
5007
- isLive && /* @__PURE__ */ jsx("span", {
5008
- className: "fw-state fw-state-live",
5009
- children: "Live"
5010
- })
5011
- ]
5012
- }),
5013
- view === "agents" && /* @__PURE__ */ jsxs("p", {
5014
- className: "fw-headline",
5015
- "aria-live": "polite",
5016
- children: [
5017
- done && /* @__PURE__ */ jsx("span", {
5018
- className: "fw-done-mark",
5019
- "aria-label": "done",
5020
- children: "✓"
5021
- }),
5022
- agents.length,
5023
- " ",
5024
- agents.length === 1 ? "agent" : "agents",
5025
- sharedDetail ? ` · ${agents.every((row) => row.span.detail === sharedDetail) ? "" : "mostly "}${sharedDetail}` : "",
5026
- done && speedup >= 1.05 ? ` · ${speedup.toFixed(1)}x parallel` : ""
5027
- ]
5028
- }),
5029
- /* @__PURE__ */ jsxs("div", {
5030
- className: "fw-legends",
5031
- children: [/* @__PURE__ */ jsx(OutcomeLegend, { agents }), view === "agents" && /* @__PURE__ */ jsx(KindLegend, { rows })]
5032
- })
5033
- ]
5023
+ children: [/* @__PURE__ */ jsxs("div", {
5024
+ className: "fw-title-line",
5025
+ children: [
5026
+ /* @__PURE__ */ jsx("span", {
5027
+ className: "fw-title-glyph",
5028
+ "aria-hidden": true,
5029
+ children: /* @__PURE__ */ jsx(TraceGlyph, {})
5030
+ }),
5031
+ /* @__PURE__ */ jsx("h3", {
5032
+ className: "fw-title",
5033
+ children: label
5034
+ }),
5035
+ description && /* @__PURE__ */ jsx("p", {
5036
+ className: "fw-description",
5037
+ title: description,
5038
+ children: description
5039
+ }),
5040
+ view === "agents" && /* @__PURE__ */ jsxs("p", {
5041
+ className: "fw-headline",
5042
+ "aria-live": "polite",
5043
+ children: [
5044
+ agents.length,
5045
+ " ",
5046
+ agents.length === 1 ? "agent" : "agents",
5047
+ sharedDetail ? ` · ${agents.every((row) => row.span.detail === sharedDetail) ? "" : "mostly "}${sharedDetail}` : "",
5048
+ done && speedup >= 1.05 ? ` · ${speedup.toFixed(1)}x parallel` : ""
5049
+ ]
5050
+ }),
5051
+ isLive && /* @__PURE__ */ jsx("span", {
5052
+ className: "fw-state fw-state-live",
5053
+ children: "Live"
5054
+ })
5055
+ ]
5056
+ }), /* @__PURE__ */ jsxs("div", {
5057
+ className: "fw-legends",
5058
+ children: [
5059
+ /* @__PURE__ */ jsx(OutcomeLegend, { agents }),
5060
+ view === "agents" && /* @__PURE__ */ jsx(KindLegend, { rows }),
5061
+ /* @__PURE__ */ jsx(MarkerLegend, { markers })
5062
+ ]
5063
+ })]
5034
5064
  }), /* @__PURE__ */ jsxs("div", {
5035
5065
  className: "fw-tools",
5036
5066
  children: [/* @__PURE__ */ jsx("div", {
@@ -5145,7 +5175,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
5145
5175
  onCollapseAll: () => setCollapsed(new Set(rows.filter((row) => row.hasChildren).map((row) => row.id)))
5146
5176
  }),
5147
5177
  card && cardRow && /* @__PURE__ */ jsx("div", {
5148
- className: `fw-card-anchor${card.above ? " fw-card-above" : ""}`,
5178
+ className: `fw-card-anchor${card.above && card.top === void 0 ? " fw-card-above" : ""}`,
5149
5179
  onPointerEnter: () => clearTimeout(hideTimer.current),
5150
5180
  onPointerLeave: card.pinned ? void 0 : leave,
5151
5181
  children: /* @__PURE__ */ jsx(SpanCard, {
@@ -5154,7 +5184,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
5154
5184
  agent: cardAgent,
5155
5185
  clockMs,
5156
5186
  left: card.left,
5157
- top: card.above ? card.anchorTop - 8 : card.anchorBottom + 8,
5187
+ top: card.top ?? (card.above ? card.anchorTop - 8 : card.anchorBottom + 8),
5158
5188
  pinned: card.pinned,
5159
5189
  onClose: closeCard,
5160
5190
  renderIcon,
@@ -1,30 +1,30 @@
1
1
  /* Shared live charts. Consumers may set --chart-live-* tokens on an ancestor. Sizes are px on the 12/14 type scale and a
2
2
  4 px grid, so a host's font size never rescales them. */
3
- .ov-empty { margin: 0; color: var(--chart-live-faint, #777); font-size: 12px; }
3
+ .ov-empty { margin: 0; color: var(--chart-live-faint, #777); font-size: 14px; }
4
4
  .ov-plot { position: relative; min-width: 0; }
5
5
  .ov-plot svg { display: block; overflow: visible; }
6
6
  line.ov-gridline { stroke: color-mix(in srgb, var(--chart-live-faint, #777) 22%, transparent); stroke-width: 1; }
7
7
  .ov-axis { fill: var(--chart-live-muted, #666); font: 12px/16px var(--chart-live-font-mono, monospace); font-variant-numeric: tabular-nums; }
8
8
  .ov-crosshair { stroke: var(--chart-live-faint, #777); stroke-dasharray: 3 3; }
9
- .ov-tip { position: absolute; z-index: 4; display: grid; gap: 4px; min-width: 160px; max-width: 240px; padding: 8px 12px; background: var(--chart-live-raised, #fff); border: 1px solid var(--chart-live-border, #aaa); border-radius: 8px; box-shadow: 0 10px 24px -12px #000; font-size: 12px; line-height: 16px; pointer-events: none; }
9
+ .ov-tip { position: absolute; z-index: 4; display: grid; gap: 4px; min-width: 160px; max-width: 240px; padding: 8px 12px; background: var(--chart-live-raised, #fff); border: 1px solid var(--chart-live-border, #aaa); border-radius: 8px; box-shadow: 0 10px 24px -12px #000; font-size: 14px; line-height: 20px; pointer-events: none; }
10
10
  .ov-tip-line { display: grid; grid-template-columns: auto 1fr auto; gap: 8px; align-items: center; color: var(--chart-live-muted, #666); }
11
11
  .ov-tip-line b { color: var(--chart-live-fg, #222); font-variant-numeric: tabular-nums; }
12
12
  .ov-hbars { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, max-content) minmax(33%, 1fr) max-content; gap: 4px 12px; }
13
13
  .ov-hbars li { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; }
14
14
  .ov-hbars li > a, .ov-hbars li > div { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; row-gap: 0; align-items: center; padding: 4px; border-radius: 6px; color: inherit; text-decoration: none; }
15
- .ov-hbars-more { grid-column: 1 / -1; padding: 0 4px; font-size: 12px; color: var(--chart-live-faint, #777); }
15
+ .ov-hbars-more { grid-column: 1 / -1; padding: 0 4px; font-size: 14px; color: var(--chart-live-faint, #777); }
16
16
  .ov-hbars li > a:hover, .ov-hbars li > div:hover { background: var(--chart-live-hover, #eee); text-decoration: none; }
17
17
  .ov-hbar-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; line-height: 20px; color: var(--chart-live-fg, #222); }
18
18
  .ov-hbar-track { height: 8px; border-radius: 3px; background: color-mix(in srgb, var(--chart-live-faint, #777) 14%, transparent); overflow: hidden; }
19
19
  .ov-hbar-track > span { display: block; height: 100%; border-radius: 3px; }
20
20
  .ov-hbar-value { font: 12px/16px var(--chart-live-font-mono, monospace); font-variant-numeric: tabular-nums; text-align: right; color: var(--chart-live-fg, #222); }
21
- .ov-hbar-note { grid-column: 1 / -1; font-size: 12px; color: var(--chart-live-faint, #777); }
21
+ .ov-hbar-note { grid-column: 1 / -1; font-size: 14px; color: var(--chart-live-faint, #777); }
22
22
  .ov-meter { display: grid; gap: 4px; min-width: 0; }
23
23
  .ov-meter-track { display: block; height: 8px; border-radius: 3px; background: color-mix(in srgb, var(--chart-live-faint, #777) 14%, transparent); overflow: hidden; }
24
24
  .ov-meter-track > span { display: block; height: 100%; border-radius: 3px; }
25
25
  .ov-meter-text { font: 12px/16px var(--chart-live-font-mono, monospace); font-variant-numeric: tabular-nums; color: var(--chart-live-muted, #666); }
26
26
  .ov-spark { display: block; width: 112px; height: 24px; }
27
- .ov-spark-empty { font-size: 12px; color: var(--chart-live-faint, #777); }
27
+ .ov-spark-empty { font-size: 14px; color: var(--chart-live-faint, #777); }
28
28
  .ov-tip-line i { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 4px; vertical-align: 0; }
29
29
  /* Recorded events and spans. Colours come from Brand tokens; kinds also differ by stroke pattern and glyph. */
30
30
  .ov-mark { stroke-width: 1.5; fill: none; }
@@ -45,7 +45,7 @@ line.ov-gridline { stroke: color-mix(in srgb, var(--chart-live-faint, #777) 22%,
45
45
  .ov-verdict-fail .ov-mark-head { fill: var(--chart-live-danger, var(--chart-breach, var(--status-error))); }
46
46
  .ov-verdict-unknown .ov-mark { stroke: var(--chart-live-faint, var(--chart-muted)); }
47
47
  .ov-verdict-unknown .ov-mark-head { fill: var(--chart-live-faint, var(--chart-muted)); }
48
- .ov-mark-label { fill: var(--chart-live-fg, var(--chart-ink)); font-size: 12px; paint-order: stroke; stroke: var(--chart-live-surface, var(--chart-canvas)); stroke-width: 3px; stroke-linejoin: round; }
48
+ .ov-mark-label { fill: var(--chart-live-fg, var(--chart-ink)); font-size: 14px; paint-order: stroke; stroke: var(--chart-live-surface, var(--chart-canvas)); stroke-width: 3px; stroke-linejoin: round; }
49
49
  .ov-band-after-pass { fill: color-mix(in srgb, var(--chart-live-pass, var(--chart-pass, var(--status-running))) 12%, transparent); }
50
50
  .ov-band-idle { fill: var(--chart-live-hover, var(--chart-shade)); }
51
51
  .ov-band-blocked { fill: color-mix(in srgb, var(--chart-live-danger, var(--chart-breach, var(--status-error))) 9%, transparent); }
@@ -57,7 +57,7 @@ line.ov-gridline { stroke: color-mix(in srgb, var(--chart-live-faint, #777) 22%,
57
57
  .ov-hatched { background: repeating-linear-gradient(135deg, var(--chart-live-faint, var(--chart-muted)) 0 1px, transparent 1px 5px), color-mix(in srgb, var(--chart-live-faint, var(--chart-muted)) 10%, transparent); }
58
58
  .ov-now { stroke: var(--chart-live-fg, var(--chart-ink)); stroke-width: 1.5; }
59
59
  .ov-gantt-open { stroke: var(--chart-live-fg, var(--chart-ink)); stroke-width: 1.5; stroke-dasharray: 3 2; }
60
- .ov-events, .ov-key, .ov-meter-key { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 4px 16px; font-size: 12px; line-height: 16px; color: var(--chart-live-muted, var(--chart-ink-muted)); }
60
+ .ov-events, .ov-key, .ov-meter-key { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 4px 16px; font-size: 14px; line-height: 20px; color: var(--chart-live-muted, var(--chart-ink-muted)); }
61
61
  .ov-key li, .ov-events li, .ov-meter-key li { display: inline-flex; align-items: center; gap: 8px; }
62
62
  .ov-meter-key b { font-weight: 600; color: var(--chart-live-fg, var(--chart-ink)); font-family: var(--chart-live-font-mono, monospace); font-variant-numeric: tabular-nums; }
63
63
  .ov-meter-key i, .ov-event-swatch { display: inline-block; width: 12px; height: 12px; border-radius: 2px; }
@@ -73,7 +73,7 @@ line.ov-gridline { stroke: color-mix(in srgb, var(--chart-live-faint, #777) 22%,
73
73
  .ov-event-after-pass { background: color-mix(in srgb, var(--chart-live-pass, var(--chart-pass, var(--status-running))) 25%, transparent); }
74
74
  .ov-event-idle { background: var(--chart-live-hover, var(--chart-shade)); }
75
75
  .ov-event-missing { background: repeating-linear-gradient(135deg, var(--chart-live-faint, var(--chart-muted)) 0 1px, transparent 1px 4px); }
76
- .ov-refusal { margin: 0; color: var(--chart-live-muted, var(--chart-ink-muted)); font-size: 12px; }
76
+ .ov-refusal { margin: 0; color: var(--chart-live-muted, var(--chart-ink-muted)); font-size: 14px; }
77
77
 
78
78
  /*
79
79
  * DotMap: thousands of dots on one canvas. A categorical lens names at most three categories in colour, the first
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/charts",
3
- "version": "0.21.21",
3
+ "version": "0.21.23",
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",