@tangle-network/charts 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -335,7 +335,7 @@ const trace = otlpFlowTrace(spans, { price: catalogPrice(await (await fetch(cata
|
|
|
335
335
|
<FlowWaterfall trace={trace} live={running} defaultView="agents" renderIcon={brandMark} />;
|
|
336
336
|
```
|
|
337
337
|
|
|
338
|
-
`defaultView="agents"` opens the agent timeline: one row per agent with its model, thinking and tool marks, hatched waiting while it delegates, elbow connectors to each agent it spawned and back, retry and failure glyphs, and a headline with the measured speedup over sequential (agent busy time over wall time). Expanding an agent opens the span view with that agent unfolded. The span view virtualizes rows (5,000 spans scroll at 58+ fps in headless Chromium), pins ancestors while their children scroll, and labels each bar with its duration and recorded cost. Hover shows a span card; click pins it and zooms the axis to a span too narrow to read. `onSelect(span)` hears each opened row and pinned bar, so a host can open its own drill-down; `markers={[{ at, label, tone }]}` draws labelled instants, such as releases, across the rows. Ctrl/Cmd+wheel or `+`/`-`/`0` zoom, Shift+wheel pans, the scrub track under the controls shows the whole trace, and arrow keys walk the tree.
|
|
338
|
+
`defaultView="agents"` opens the agent timeline: one row per agent with its model, thinking and tool marks, hatched waiting while it delegates, elbow connectors to each agent it spawned and back, retry and failure glyphs, and a headline with the measured speedup over sequential (agent busy time over wall time). Expanding an agent opens the span view with that agent unfolded. The span view virtualizes rows (5,000 spans scroll at 58+ fps in headless Chromium), pins ancestors while their children scroll, and labels each bar with its duration and recorded cost. Hover shows a span card; click pins it and zooms the axis to a span too narrow to read. `onSelect(span)` hears each opened row and pinned bar, so a host can open its own drill-down; `markers={[{ at, label, tone }]}` draws labelled instants, such as releases, across the rows. Give an agent span an `outcome` (`{ tone, label }`) and its lane takes the outcome color and the header a legend. Ctrl/Cmd+wheel or `+`/`-`/`0` zoom, Shift+wheel pans, the scrub track under the controls shows the whole trace, and arrow keys walk the tree.
|
|
339
339
|
|
|
340
340
|
Live views append spans as they arrive: `traces watch <id|run-dir> --format jsonl --spans` emits a `traces.stream.span` event per new or changed span, and `otlpFlowTrace` keeps the last span per `span_id`. Running agent spans grow to the clock between deltas without React renders (CSS variables carry the clock). Search supports Cmd/Ctrl+F and keeps ancestor context. Missing cost and authoritative LLM counts stay Unknown; a cost priced from a catalog is labelled list price. Invalid intervals retain a per-row `—` timing mark. The static phase waterfall remains a separate chart for disjoint phases.
|
|
341
341
|
|
|
@@ -332,8 +332,15 @@ interface FlowSpan {
|
|
|
332
332
|
retryOf?: string;
|
|
333
333
|
/** For a handoff, the span that received the work; a connector joins the two rows. */
|
|
334
334
|
handoffTo?: string;
|
|
335
|
+
/** The host's verdict on an agent span, such as shipped or crashed: colors its lane and joins the legend. */
|
|
336
|
+
outcome?: FlowOutcome;
|
|
335
337
|
meta?: Record<string, unknown>;
|
|
336
338
|
}
|
|
339
|
+
/** A host verdict: `label` is the legend text, `tone` the status color. */
|
|
340
|
+
interface FlowOutcome {
|
|
341
|
+
tone: 'ok' | 'neutral' | 'failed' | 'warn';
|
|
342
|
+
label: string;
|
|
343
|
+
}
|
|
337
344
|
/** A labelled instant on the trace clock, such as a release or an alert; drawn as a line across the rows. */
|
|
338
345
|
interface FlowMarker {
|
|
339
346
|
/** Trace-relative milliseconds, on the same clock as span startMs. */
|
|
@@ -357,4 +364,4 @@ interface FlowTrace {
|
|
|
357
364
|
errors?: number;
|
|
358
365
|
}
|
|
359
366
|
//#endregion
|
|
360
|
-
export {
|
|
367
|
+
export { BreakdownInput as A, Refusal as B, MarkKind as C, YScale as D, XCategory as E, Figure as F, Tier as H, Interval as I, IntervalMethod as L, CostBasis as M, Estimate as N, YTarget as O, Exclusion as P, MatrixCell as R, ChartMark as S, MarkerShape as T, TierCounts as U, Setup as V, isRefusal as W, checkerColor as _, TimelineLane as a, BandKind as b, TimelineTick as c, ENDING_COLORS as d, ENDING_ORDER as f, TickOutcome as g, RunEnding as h, FlowTrace as i, ComparisonRow as j, YTick as k, timeline as l, OUTCOME_ORDER as m, FlowOutcome as n, TimelineOptions as o, OUTCOME_COLORS as p, FlowSpan as r, TimelineSpan as s, FlowMarker as t, CHECKER_HUES as u, PointFields as v, MarkVerdict as w, ChartBand as x, PointGlyph as y, RateRow as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
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-BYKE-PMb.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. */
|
|
@@ -398,4 +398,4 @@ export declare function otlpFlowTrace(input: OtlpSpan[], options?: OtlpTraceOpti
|
|
|
398
398
|
omitted: number;
|
|
399
399
|
};
|
|
400
400
|
//#endregion
|
|
401
|
-
export { type BandKind, type BreakdownInput, type BreakdownOptions, CHECKER_HUES, type ChartBand, type ChartMark, type ComparisonIntervalsOptions, type ComparisonRow, type CostBasis, type CostFrontierOptions, type DotGroup, type DotGroupRect, ENDING_COLORS, ENDING_ORDER, type Estimate, type Exclusion, type Figure, type FlowMarker, type FlowSpan, type FlowTrace, type HeatmapCell, type HeatmapColumn, type HeatmapOptions, type HeatmapRow, type HeatmapStatus, type Interval, type IntervalMethod, type LatencyRangeOptions, type LatencyRangeRow, type MarkKind, type MarkVerdict, type MatrixCell, type MetricBarsOptions, type MetricRow, type MultiplePanel, OUTCOME_COLORS, OUTCOME_ORDER, type Observations, type OtlpSpan, type OtlpTraceOptions, type PhaseWaterfallOptions, type PointFields, type PointGlyph, type RankedRatesOptions, type RateIdentity, type RateRow, type Refusal, type RenderFigureOptions, type RunEnding, type Setup, type SmallMultiplesOptions, type TaskMatrixOptions, type TickOutcome, type Tier, type TierCounts, type TimePoint, type TimeSeriesOptions, type TimeSeriesRow, type TimelineLane, type TimelineOptions, type TimelineSpan, type TimelineTick, type UnitDotLayout, type UnitDotOptions, type WaterfallPhase, type XCategory, type YScale, type YTarget, type YTick, checkerColor, isRefusal, timeline };
|
|
401
|
+
export { type BandKind, type BreakdownInput, type BreakdownOptions, CHECKER_HUES, type ChartBand, type ChartMark, type ComparisonIntervalsOptions, type ComparisonRow, type CostBasis, type CostFrontierOptions, type DotGroup, type DotGroupRect, ENDING_COLORS, ENDING_ORDER, type Estimate, type Exclusion, type Figure, type FlowMarker, type FlowOutcome, type FlowSpan, type FlowTrace, type HeatmapCell, type HeatmapColumn, type HeatmapOptions, type HeatmapRow, type HeatmapStatus, type Interval, type IntervalMethod, type LatencyRangeOptions, type LatencyRangeRow, type MarkKind, type MarkVerdict, type MatrixCell, type MetricBarsOptions, type MetricRow, type MultiplePanel, OUTCOME_COLORS, OUTCOME_ORDER, type Observations, type OtlpSpan, type OtlpTraceOptions, type PhaseWaterfallOptions, type PointFields, type PointGlyph, type RankedRatesOptions, type RateIdentity, type RateRow, type Refusal, type RenderFigureOptions, type RunEnding, type Setup, type SmallMultiplesOptions, type TaskMatrixOptions, type TickOutcome, type Tier, type TierCounts, type TimePoint, type TimeSeriesOptions, type TimeSeriesRow, type TimelineLane, type TimelineOptions, type TimelineSpan, type TimelineTick, type UnitDotLayout, type UnitDotOptions, type WaterfallPhase, type XCategory, type YScale, type YTarget, type YTick, checkerColor, isRefusal, timeline };
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
--fw-h5: light-dark(#6d44c7, #ab8cf7);
|
|
30
30
|
--fw-ease: cubic-bezier(.2, .8, .2, 1);
|
|
31
31
|
--fw-bar-h: 16px;
|
|
32
|
+
/* One label column for rows, axis and every lane layer; the agent view caps it lower and drops it on phones. */
|
|
33
|
+
--fw-label-col: min(var(--fw-name-w), 42%);
|
|
34
|
+
--fw-bar-shift: 0px;
|
|
32
35
|
position: relative;
|
|
33
36
|
container-type: inline-size;
|
|
34
37
|
min-width: 0;
|
|
@@ -37,12 +40,12 @@
|
|
|
37
40
|
border: 1px solid var(--fw-border);
|
|
38
41
|
border-radius: 12px;
|
|
39
42
|
font-family: var(--font-sans, Inter, ui-sans-serif, system-ui, sans-serif);
|
|
40
|
-
font-size:
|
|
43
|
+
font-size: 14px;
|
|
41
44
|
line-height: 20px;
|
|
42
45
|
font-feature-settings: "tnum" 1, "cv11" 1;
|
|
43
46
|
-webkit-font-smoothing: antialiased;
|
|
44
47
|
}
|
|
45
|
-
.fw-root-agents { --fw-bar-h: 20px; border-radius: 16px; }
|
|
48
|
+
.fw-root-agents { --fw-bar-h: 20px; --fw-label-col: min(var(--fw-name-w), 28%); border-radius: 16px; }
|
|
46
49
|
.fw-root *, .fw-root *::before, .fw-root *::after { box-sizing: border-box; }
|
|
47
50
|
.fw-root :is(p, dl, dd, h3, ul) { margin: 0; padding: 0; }
|
|
48
51
|
.fw-root :is(button, input, select) { font: inherit; color: inherit; }
|
|
@@ -63,23 +66,23 @@
|
|
|
63
66
|
|
|
64
67
|
/* Header */
|
|
65
68
|
.fw-root .fw-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 16px; border-bottom: 1px solid var(--fw-border); flex-wrap: wrap; }
|
|
66
|
-
.fw-root .fw-title-block { min-width: 0; display: grid; gap:
|
|
69
|
+
.fw-root .fw-title-block { min-width: 0; display: grid; gap: 4px; }
|
|
67
70
|
.fw-root .fw-title-line { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
68
71
|
.fw-root .fw-title-glyph { display: inline-flex; color: var(--fw-accent); }
|
|
69
72
|
.fw-root .fw-title { font-size: 14px; line-height: 20px; font-weight: 600; letter-spacing: -.005em; white-space: nowrap; }
|
|
70
|
-
.fw-root .fw-state { display: inline-flex; align-items: center; gap:
|
|
73
|
+
.fw-root .fw-state { display: inline-flex; align-items: center; gap: 8px; height: 20px; padding: 0 8px; border-radius: 999px; font-size: 12px; font-weight: 600; color: var(--fw-muted); background: var(--fw-well); flex-shrink: 0; }
|
|
71
74
|
.fw-root .fw-state::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
|
|
72
75
|
.fw-root .fw-state-live { color: var(--fw-accent); background: color-mix(in oklab, var(--fw-accent) 12%, transparent); }
|
|
73
76
|
.fw-root .fw-state-live::before { animation: fw-pulse 1.6s var(--fw-ease) infinite; }
|
|
74
|
-
.fw-root .fw-headline { color: var(--fw-muted); font-variant-numeric: tabular-nums; display: flex; align-items: center; gap:
|
|
75
|
-
.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:
|
|
77
|
+
.fw-root .fw-headline { color: var(--fw-muted); font-variant-numeric: tabular-nums; display: flex; align-items: center; gap: 8px; }
|
|
78
|
+
.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; }
|
|
76
79
|
.fw-root .fw-tools { display: flex; align-items: center; gap: 8px; }
|
|
77
|
-
.fw-root .fw-views { display: inline-flex; padding:
|
|
78
|
-
.fw-root .fw-view-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0
|
|
80
|
+
.fw-root .fw-views { display: inline-flex; padding: 4px; border-radius: 8px; background: var(--fw-well); border: 1px solid var(--fw-border); }
|
|
81
|
+
.fw-root .fw-view-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0 12px; border-radius: 6px; font-size: 14px; color: var(--fw-muted); cursor: pointer; transition: background-color 120ms var(--fw-ease), color 120ms var(--fw-ease); }
|
|
79
82
|
.fw-root .fw-view-button[aria-checked="true"] { background: var(--fw-surface); color: var(--fw-text); box-shadow: 0 1px 2px rgb(0 0 0 / .12), 0 0 0 1px var(--fw-border); }
|
|
80
83
|
.fw-root .fw-search { position: relative; display: flex; align-items: center; }
|
|
81
84
|
.fw-root .fw-search-glyph { position: absolute; left: 8px; display: inline-flex; color: var(--fw-muted); pointer-events: none; }
|
|
82
|
-
.fw-root .fw-search-input { appearance: none; width: 200px; height: 32px; padding: 0
|
|
85
|
+
.fw-root .fw-search-input { appearance: none; width: 200px; height: 32px; padding: 0 12px 0 28px; border: 1px solid var(--fw-border); border-radius: 8px; background: var(--fw-well); font-size: 14px; }
|
|
83
86
|
.fw-root .fw-search-input::placeholder { color: var(--fw-muted); }
|
|
84
87
|
.fw-root .fw-search-input::-webkit-search-cancel-button { display: none; }
|
|
85
88
|
|
|
@@ -87,18 +90,18 @@
|
|
|
87
90
|
.fw-root .fw-summary { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); border-bottom: 1px solid var(--fw-border); }
|
|
88
91
|
.fw-root .fw-summary > div { padding: 12px 16px; min-width: 0; }
|
|
89
92
|
.fw-root .fw-summary > div + div { border-left: 1px solid var(--fw-border); }
|
|
90
|
-
.fw-root .fw-summary dt { font-size:
|
|
91
|
-
.fw-root .fw-value { margin-top: 4px; font-size:
|
|
93
|
+
.fw-root .fw-summary dt { font-size: 12px; line-height: 16px; color: var(--fw-muted); }
|
|
94
|
+
.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; }
|
|
92
95
|
.fw-root .fw-has-errors .fw-value { color: var(--fw-failed); }
|
|
93
96
|
|
|
94
97
|
/* Axis */
|
|
95
|
-
.fw-root .fw-axis { display: grid; grid-template-columns:
|
|
98
|
+
.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; }
|
|
96
99
|
.fw-root .fw-axis-top { border-bottom: 1px solid var(--fw-border); }
|
|
97
100
|
.fw-root .fw-axis-bottom { height: 28px; border-top: 1px solid var(--fw-border); }
|
|
98
|
-
.fw-root .fw-axis-label { position: relative; display: flex; align-items: center; gap:
|
|
101
|
+
.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; }
|
|
99
102
|
.fw-root .fw-axis-bottom .fw-axis-label { border-right-color: transparent; }
|
|
100
|
-
.fw-root .fw-branch-controls { margin-left: auto; display: flex; gap:
|
|
101
|
-
.fw-root .fw-text-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0
|
|
103
|
+
.fw-root .fw-branch-controls { margin-left: auto; display: flex; gap: 4px; }
|
|
104
|
+
.fw-root .fw-text-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0 8px; border-radius: 6px; font-size: 12px; color: var(--fw-muted); cursor: pointer; white-space: nowrap; }
|
|
102
105
|
.fw-root .fw-text-button:hover { background: var(--fw-hover); color: var(--fw-text); }
|
|
103
106
|
.fw-root .fw-resize { position: absolute; top: 0; right: -4px; width: 8px; height: 100%; cursor: col-resize; z-index: 2; }
|
|
104
107
|
.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; }
|
|
@@ -111,26 +114,27 @@
|
|
|
111
114
|
.fw-root .fw-viewport { position: relative; }
|
|
112
115
|
.fw-root .fw-scroller { position: relative; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; scrollbar-width: thin; outline: 0; }
|
|
113
116
|
.fw-root .fw-canvas { position: relative; }
|
|
114
|
-
.fw-root .fw-gridlayer, .fw-root .fw-cursor-layer { position: absolute; top: 0; bottom: 0; left:
|
|
117
|
+
.fw-root .fw-gridlayer, .fw-root .fw-cursor-layer { position: absolute; top: 0; bottom: 0; left: var(--fw-label-col); right: 0; overflow: hidden; pointer-events: none; }
|
|
115
118
|
.fw-root .fw-gridline { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--fw-grid); left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); }
|
|
116
119
|
.fw-root .fw-cursor-layer { z-index: 3; }
|
|
117
|
-
.fw-root .fw-marker-layer { position: absolute; top: 0; bottom: 0; left:
|
|
120
|
+
.fw-root .fw-marker-layer { position: absolute; top: 0; bottom: 0; left: var(--fw-label-col); right: 0; overflow: hidden; pointer-events: none; z-index: 2; }
|
|
118
121
|
.fw-root .fw-marker { --fw-marker: var(--fw-muted); position: absolute; top: 0; bottom: 0; width: 0; border-left: 1px dashed var(--fw-marker); left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); }
|
|
119
122
|
.fw-root .fw-marker[data-tone="ok"] { --fw-marker: var(--fw-ok); }
|
|
120
123
|
.fw-root .fw-marker[data-tone="failed"] { --fw-marker: var(--fw-failed); }
|
|
121
|
-
.fw-root .fw-marker-label { position: absolute; top: 2px; left: 3px; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0
|
|
124
|
+
.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: 4px; 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); }
|
|
125
|
+
.fw-root .fw-marker-label-left { left: auto; right: 3px; }
|
|
122
126
|
.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); }
|
|
123
127
|
.fw-root .fw-empty { padding: 40px 16px; text-align: center; color: var(--fw-muted); }
|
|
124
128
|
.fw-root .fw-sticky { position: absolute; top: 0; left: 0; right: 0; z-index: 2; background: var(--fw-surface); box-shadow: 0 1px 0 var(--fw-border), 0 6px 12px -8px rgb(0 0 0 / .35); }
|
|
125
129
|
|
|
126
130
|
/* Rows */
|
|
127
|
-
.fw-root .fw-row { position: absolute; top: 0; left: 0; right: 0; height: var(--fw-row-h); display: grid; grid-template-columns:
|
|
131
|
+
.fw-root .fw-row { position: absolute; top: 0; left: 0; right: 0; height: var(--fw-row-h); display: grid; grid-template-columns: var(--fw-label-col) minmax(0, 1fr); outline: 0; contain: layout style; }
|
|
128
132
|
.fw-root .fw-row:hover { background: var(--fw-hover); }
|
|
129
133
|
.fw-root .fw-row.fw-active { background: color-mix(in oklab, var(--fw-accent) 7%, transparent); }
|
|
130
134
|
.fw-root .fw-row[aria-selected="true"] { background: color-mix(in oklab, var(--fw-accent) 11%, transparent); }
|
|
131
135
|
.fw-root .fw-row:focus-visible { box-shadow: inset 2px 0 0 var(--fw-accent); }
|
|
132
136
|
.fw-root .fw-sticky-row { background: var(--fw-surface); }
|
|
133
|
-
.fw-root .fw-label { position: relative; display: flex; align-items: center; gap:
|
|
137
|
+
.fw-root .fw-label { position: relative; display: flex; align-items: center; gap: 8px; min-width: 0; padding: 0 12px 0 calc(12px + var(--depth) * 16px); border-right: 1px solid var(--fw-border); }
|
|
134
138
|
.fw-root .fw-guides { position: absolute; top: 0; bottom: 0; left: 20px; width: calc(var(--depth) * 16px); background: repeating-linear-gradient(90deg, var(--fw-guide) 0 1px, transparent 1px 16px); }
|
|
135
139
|
.fw-root .fw-toggle, .fw-root .fw-toggle-space { flex: 0 0 16px; width: 16px; height: 16px; }
|
|
136
140
|
.fw-root .fw-toggle { appearance: none; display: grid; place-items: center; padding: 0; border: 0; border-radius: 4px; background: transparent; color: var(--fw-muted); cursor: pointer; }
|
|
@@ -140,24 +144,24 @@
|
|
|
140
144
|
.fw-root .fw-icon-tile { flex: 0 0 20px; display: grid; place-items: center; width: 20px; height: 20px; border-radius: 6px; border: 1px solid var(--fw-border); background: var(--fw-surface); color: var(--line, var(--fw-muted)); }
|
|
141
145
|
.fw-root .fw-icon-tile :is(svg, img) { width: 13px; height: 13px; }
|
|
142
146
|
.fw-root .fw-row .fw-names { font-size: 14px; }
|
|
143
|
-
.fw-root .fw-names { display: flex; align-items: baseline; gap:
|
|
144
|
-
.fw-root .fw-span-name { display: inline-flex; align-items: center; gap:
|
|
147
|
+
.fw-root .fw-names { display: flex; align-items: baseline; gap: 8px; min-width: 0; overflow: hidden; white-space: nowrap; }
|
|
148
|
+
.fw-root .fw-span-name { display: inline-flex; align-items: center; gap: 8px; overflow: hidden; flex-shrink: 0; max-width: 100%; min-width: 0; }
|
|
145
149
|
.fw-root .fw-name-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
146
150
|
.fw-root .fw-strong { font-weight: 600; }
|
|
147
151
|
.fw-root .fw-detail { color: var(--fw-muted); overflow: hidden; min-width: 0; display: inline-flex; align-items: center; gap: 4px; max-width: 100%; }
|
|
148
152
|
.fw-root .fw-detail-icon { display: inline-flex; }
|
|
149
153
|
.fw-root .fw-detail-icon :is(svg, img) { width: 12px; height: 12px; }
|
|
150
|
-
.fw-root .fw-count { flex-shrink: 0; height: 18px; padding: 0
|
|
154
|
+
.fw-root .fw-count { flex-shrink: 0; height: 18px; padding: 0 8px; border-radius: 999px; 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; }
|
|
151
155
|
.fw-root .fw-alert { display: inline-flex; color: var(--fw-failed); flex-shrink: 0; }
|
|
152
156
|
.fw-root .fw-alert svg { width: 13px; height: 13px; }
|
|
153
157
|
.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; }
|
|
154
158
|
|
|
155
159
|
/* Agent rows: a dot, the agent and its model on a second line. */
|
|
156
|
-
.fw-root .fw-view-agents .fw-label { padding-left: 16px; gap:
|
|
160
|
+
.fw-root .fw-view-agents .fw-label { padding-left: 16px; gap: 12px; border-right: 0; }
|
|
157
161
|
.fw-root .fw-view-agents .fw-names { flex-direction: column; align-items: flex-start; gap: 0; }
|
|
158
|
-
.fw-root .fw-view-agents .fw-span-name { font-weight:
|
|
159
|
-
.fw-root .fw-view-agents .fw-detail { font-size:
|
|
160
|
-
.fw-root .fw-agent-dot { flex: 0 0 8px; width: 8px; height: 8px; border-radius: 50%; background: var(--line, var(--fw-muted)); align-self: flex-start; margin-top: calc((var(--fw-row-h) - 34px) / 2 +
|
|
162
|
+
.fw-root .fw-view-agents .fw-span-name { font-weight: 600; line-height: 18px; }
|
|
163
|
+
.fw-root .fw-view-agents .fw-detail { font-size: 12px; line-height: 16px; }
|
|
164
|
+
.fw-root .fw-agent-dot { flex: 0 0 8px; width: 8px; height: 8px; border-radius: 50%; background: var(--line, var(--fw-muted)); align-self: flex-start; margin-top: calc((var(--fw-row-h) - 34px) / 2 + 8px); }
|
|
161
165
|
|
|
162
166
|
/* Lanes and marks */
|
|
163
167
|
.fw-root .fw-lane { position: relative; overflow: hidden; min-width: 0; }
|
|
@@ -171,16 +175,29 @@
|
|
|
171
175
|
left: var(--x0);
|
|
172
176
|
opacity: var(--on);
|
|
173
177
|
}
|
|
174
|
-
.fw-root :is(.fw-bar, .fw-seg) { height: var(--fw-bar-h); margin-top: calc(var(--fw-bar-h) / -2); width: max(var(--w), calc(var(--on) * 3px)); border-radius: 4px; cursor: pointer; display: flex; align-items: center; justify-content: center; overflow: hidden; }
|
|
178
|
+
.fw-root :is(.fw-bar, .fw-seg) { height: var(--fw-bar-h); margin-top: calc(var(--fw-bar-h) / -2 + var(--fw-bar-shift)); width: max(var(--w), calc(var(--on) * 3px)); border-radius: 4px; cursor: pointer; display: flex; align-items: center; justify-content: center; overflow: hidden; }
|
|
175
179
|
.fw-root .fw-bar { background: var(--fill); box-shadow: inset 0 0 0 1px var(--line); }
|
|
180
|
+
/* The mark under the pointer lifts: a brighter fill and a ring in its own tone. */
|
|
181
|
+
.fw-root :is(.fw-bar, .fw-seg):hover { filter: brightness(1.18); box-shadow: inset 0 0 0 1px var(--line), 0 0 0 2px color-mix(in oklab, var(--line) 55%, transparent); z-index: 1; }
|
|
176
182
|
.fw-root .fw-bar.fw-k-tool { background: var(--dim); box-shadow: inset 0 0 0 1px var(--dim-line); }
|
|
177
183
|
/* A handoff is a moment: a slim pill in the receiving tone, not a block of work. */
|
|
178
|
-
.fw-root .fw-bar.fw-k-handoff { background: var(--line); height: 6px; margin-top: -
|
|
184
|
+
.fw-root .fw-bar.fw-k-handoff { background: var(--line); height: 6px; margin-top: -4px; box-shadow: none; }
|
|
179
185
|
.fw-root .fw-bar.fw-k-waiting { background: repeating-linear-gradient(-45deg, var(--fw-hatch) 0 1px, transparent 1px 5px); box-shadow: inset 0 0 0 1px var(--fw-hatch); }
|
|
180
186
|
.fw-root .fw-bar.fw-k-failed { background: color-mix(in oklab, var(--fw-failed) 22%, var(--fw-surface)); box-shadow: inset 0 0 0 1px var(--fw-failed); color: var(--fw-failed); }
|
|
181
187
|
.fw-root .fw-bar.fw-k-pipeline { background: linear-gradient(90deg, var(--fw-neutral-bar), color-mix(in oklab, var(--fw-neutral-bar) 55%, var(--fw-surface))); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-text) 12%, transparent); }
|
|
182
188
|
.fw-root .fw-bar.fw-k-pipeline.fw-has-marks { background: color-mix(in oklab, var(--tone) 9%, transparent); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--tone) 18%, transparent); }
|
|
183
189
|
.fw-root .fw-bar.fw-k-pipeline.fw-has-agents { background: repeating-linear-gradient(-45deg, var(--fw-hatch) 0 1px, transparent 1px 5px), color-mix(in oklab, var(--fw-text) 4%, transparent); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-text) 12%, transparent); }
|
|
190
|
+
/* An agent bar with no child marks says who it is by its lane color, never a neutral grey. */
|
|
191
|
+
.fw-root .fw-view-agents .fw-bar.fw-k-pipeline:not(.fw-has-marks, .fw-has-agents) { background: var(--fill); box-shadow: inset 0 0 0 1px var(--line); }
|
|
192
|
+
/* A host outcome re-tones the whole lane: dot, bar and marks. */
|
|
193
|
+
.fw-root [data-outcome="ok"], .fw-root [data-outcome="ok"] [data-tone] { --tone: var(--fw-ok); }
|
|
194
|
+
.fw-root [data-outcome="failed"], .fw-root [data-outcome="failed"] [data-tone] { --tone: var(--fw-failed); }
|
|
195
|
+
.fw-root [data-outcome="warn"], .fw-root [data-outcome="warn"] [data-tone] { --tone: var(--fw-amber); }
|
|
196
|
+
.fw-root [data-outcome="neutral"], .fw-root [data-outcome="neutral"] [data-tone] { --tone: var(--fw-muted); }
|
|
197
|
+
.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; }
|
|
198
|
+
.fw-root .fw-legend li { display: inline-flex; align-items: center; gap: 8px; }
|
|
199
|
+
.fw-root .fw-legend .fw-swatch { width: 12px; height: 8px; border-radius: 2px; background: var(--fill); box-shadow: inset 0 0 0 1px var(--line); }
|
|
200
|
+
.fw-root .fw-legend b { color: var(--fw-text); font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
184
201
|
.fw-root .fw-bar.fw-approx { opacity: calc(var(--on) * .7); }
|
|
185
202
|
.fw-root .fw-seg { width: max(calc(var(--w) - 2px), calc(var(--on) * 3px)); pointer-events: none; }
|
|
186
203
|
.fw-root .fw-view-agents .fw-seg { pointer-events: auto; }
|
|
@@ -191,16 +208,16 @@
|
|
|
191
208
|
.fw-root .fw-glyph { display: inline-flex; color: currentColor; }
|
|
192
209
|
.fw-root .fw-glyph svg { width: 11px; height: 11px; stroke-width: 2; }
|
|
193
210
|
.fw-root .fw-seg-model .fw-glyph, .fw-root .fw-k-model .fw-glyph { color: light-dark(var(--tone), #fff); }
|
|
194
|
-
.fw-root .fw-end { display: inline-flex; align-items: center; gap: 8px; margin-top: -
|
|
195
|
-
.fw-root .fw-duration { font-size:
|
|
211
|
+
.fw-root .fw-end { display: inline-flex; align-items: center; gap: 8px; margin-top: -12px; height: 20px; left: calc(var(--x0) + max(var(--w), 3px) + 8px); white-space: nowrap; pointer-events: none; }
|
|
212
|
+
.fw-root .fw-duration { font-size: 12px; color: var(--fw-muted); font-variant-numeric: tabular-nums; }
|
|
196
213
|
.fw-root .fw-k-failed ~ .fw-end .fw-duration { color: var(--fw-failed); }
|
|
197
|
-
.fw-root .fw-badge { height: 18px; padding: 0
|
|
214
|
+
.fw-root .fw-badge { height: 18px; padding: 0 8px; border-radius: 999px; 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); }
|
|
198
215
|
|
|
199
216
|
/* Handoff elbows: 1.5 px stroke, 6 px radius, a 5 px dot where the handoff leaves its sender. */
|
|
200
|
-
.fw-root .fw-links { position: absolute; top: 0; bottom: 0; left:
|
|
217
|
+
.fw-root .fw-links { position: absolute; top: 0; bottom: 0; left: var(--fw-label-col); right: 0; overflow: hidden; pointer-events: none; z-index: 1; }
|
|
201
218
|
.fw-root .fw-link {
|
|
202
219
|
--x: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px);
|
|
203
|
-
position: absolute; width: 8px; border: 0 solid var(--line); border-bottom-width: 1.5px;
|
|
220
|
+
position: absolute; width: 8px; border: 0 solid var(--line); border-bottom-width: 1.5px; translate: 0 var(--fw-bar-shift);
|
|
204
221
|
opacity: clamp(0, (var(--fw-t) - var(--s)) * 1000, 1);
|
|
205
222
|
}
|
|
206
223
|
.fw-root .fw-view-spans .fw-link { opacity: calc(clamp(0, (var(--fw-t) - var(--s)) * 1000, 1) * .7); }
|
|
@@ -215,7 +232,7 @@
|
|
|
215
232
|
.fw-root .fw-card { position: absolute; z-index: 20; width: 320px; padding: 12px; border-radius: 12px; background: var(--fw-raised); border: 1px solid var(--fw-border);
|
|
216
233
|
box-shadow: 0 12px 32px -8px rgb(0 0 0 / .28), 0 2px 6px rgb(0 0 0 / .08); animation: fw-arrive 140ms var(--fw-ease); }
|
|
217
234
|
.fw-root .fw-card-above .fw-card { transform: translateY(-100%); animation-name: fw-arrive-above; }
|
|
218
|
-
.fw-root .fw-card-agent { display: flex; align-items: center; gap:
|
|
235
|
+
.fw-root .fw-card-agent { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 12px; font-weight: 600; }
|
|
219
236
|
.fw-root .fw-card-agent .fw-agent-dot { margin-top: 0; align-self: center; }
|
|
220
237
|
.fw-root .fw-card-head { display: flex; align-items: flex-start; gap: 8px; }
|
|
221
238
|
.fw-root .fw-card-head .fw-icon-tile { flex-basis: 28px; width: 28px; height: 28px; border-radius: 8px; }
|
|
@@ -223,32 +240,33 @@
|
|
|
223
240
|
.fw-root .fw-card-title { flex: 1; min-width: 0; }
|
|
224
241
|
.fw-root .fw-card-name { font-size: 14px; line-height: 20px; font-weight: 600; overflow-wrap: anywhere; }
|
|
225
242
|
.fw-root .fw-muted { color: var(--fw-muted); }
|
|
226
|
-
.fw-root .fw-pill { flex-shrink: 0; height: 20px; padding: 0 8px; border-radius: 999px; font-size:
|
|
243
|
+
.fw-root .fw-pill { flex-shrink: 0; height: 20px; padding: 0 8px; border-radius: 999px; font-size: 12px; line-height: 20px; font-weight: 600; }
|
|
227
244
|
.fw-root .fw-pill-done { color: var(--fw-ok); background: color-mix(in oklab, var(--fw-ok) 13%, transparent); }
|
|
228
245
|
.fw-root .fw-pill-running { color: var(--fw-accent); background: color-mix(in oklab, var(--fw-accent) 13%, transparent); }
|
|
229
246
|
.fw-root .fw-pill-failed { color: var(--fw-failed); background: color-mix(in oklab, var(--fw-failed) 13%, transparent); }
|
|
230
247
|
.fw-root .fw-icon-button { appearance: none; display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 0; border-radius: 8px; background: transparent; color: var(--fw-muted); cursor: pointer; flex-shrink: 0; }
|
|
231
248
|
.fw-root .fw-icon-button:hover:not(:disabled) { background: var(--fw-hover); color: var(--fw-text); }
|
|
232
|
-
.fw-root .fw-card-head .fw-icon-button { width: 20px; height: 20px; margin: 0 -4px 0 -
|
|
249
|
+
.fw-root .fw-card-head .fw-icon-button { width: 20px; height: 20px; margin: 0 -4px 0 -4px; }
|
|
233
250
|
.fw-root .fw-card-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px 8px; margin-top: 12px; padding: 12px; border-radius: 8px; background: var(--fw-well); }
|
|
234
|
-
.fw-root .fw-card-stats dt, .fw-root .fw-card-label { font-size:
|
|
251
|
+
.fw-root .fw-card-stats dt, .fw-root .fw-card-label { font-size: 12px; line-height: 16px; color: var(--fw-muted); }
|
|
235
252
|
.fw-root .fw-card-stats dd { font-size: 14px; line-height: 20px; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
236
253
|
.fw-root .fw-unknown { color: var(--fw-muted); font-weight: 400; }
|
|
237
254
|
.fw-root .fw-card-text { margin-top: 12px; }
|
|
238
|
-
.fw-root .fw-card-compact { width: 280px; padding:
|
|
239
|
-
.fw-root .fw-card-compact .fw-card-agent { margin-bottom:
|
|
240
|
-
.fw-root .fw-card-
|
|
241
|
-
.fw-root .fw-card-
|
|
242
|
-
.fw-root .fw-card-
|
|
243
|
-
.fw-root .fw-card-
|
|
244
|
-
.fw-root .fw-card-
|
|
255
|
+
.fw-root .fw-card-compact { width: 280px; padding: 12px; }
|
|
256
|
+
.fw-root .fw-card-compact .fw-card-agent { margin-bottom: 8px; font-size: 14px; line-height: 20px; }
|
|
257
|
+
.fw-root .fw-card-who { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
258
|
+
.fw-root .fw-card-cells { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 8px; padding: 8px 12px; border-radius: 8px; background: var(--fw-well); }
|
|
259
|
+
.fw-root .fw-card-cells dt { font-size: 12px; line-height: 16px; color: var(--fw-muted); }
|
|
260
|
+
.fw-root .fw-card-cells dd { font-size: 14px; line-height: 20px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
261
|
+
.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; }
|
|
262
|
+
.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; }
|
|
245
263
|
|
|
246
264
|
/* Playback */
|
|
247
|
-
.fw-root .fw-playback { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding:
|
|
265
|
+
.fw-root .fw-playback { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--fw-border); }
|
|
248
266
|
.fw-root .fw-round-button { appearance: none; display: grid; place-items: center; width: 32px; height: 32px; padding: 0; border-radius: 50%; border: 1px solid var(--fw-border); background: var(--fw-well); color: var(--fw-text); cursor: pointer; transition: background-color 120ms var(--fw-ease), transform 120ms var(--fw-ease); }
|
|
249
267
|
.fw-root .fw-round-button:hover:not(:disabled) { background: var(--fw-hover); }
|
|
250
268
|
.fw-root .fw-round-button:active:not(:disabled) { transform: scale(.94); }
|
|
251
|
-
.fw-root .fw-speed select { appearance: none; height: 28px; padding: 0 24px 0
|
|
269
|
+
.fw-root .fw-speed select { appearance: none; height: 28px; padding: 0 24px 0 12px; border-radius: 8px; border: 1px solid var(--fw-border); background: var(--fw-well) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cpath d='m2 3.5 3 3 3-3' fill='none' stroke='%23888' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat right 8px center; font-size: 14px; font-variant-numeric: tabular-nums; cursor: pointer; }
|
|
252
270
|
.fw-root .fw-overview { position: relative; flex: 1 1 160px; min-width: 120px; height: 28px; }
|
|
253
271
|
.fw-root .fw-overview-marks { position: absolute; left: 0; right: 0; top: 8px; height: 12px; border-radius: 4px; background: var(--fw-well); overflow: hidden; box-shadow: inset 0 0 0 1px var(--fw-border); }
|
|
254
272
|
.fw-root .fw-ov { position: absolute; top: 2px; bottom: 2px; border-radius: 1px; }
|
|
@@ -259,7 +277,7 @@
|
|
|
259
277
|
.fw-root .fw-slider { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; appearance: none; -webkit-appearance: none; background: linear-gradient(90deg, transparent var(--fw-progress), color-mix(in oklab, var(--fw-surface) 55%, transparent) var(--fw-progress)) 0 8px / 100% 12px no-repeat; cursor: pointer; }
|
|
260
278
|
.fw-root .fw-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 4px; height: 22px; border-radius: 2px; background: var(--fw-text); box-shadow: 0 0 0 2px var(--fw-surface); }
|
|
261
279
|
.fw-root .fw-slider::-moz-range-thumb { width: 4px; height: 22px; border: 0; border-radius: 2px; background: var(--fw-text); box-shadow: 0 0 0 2px var(--fw-surface); }
|
|
262
|
-
.fw-root .fw-time { min-width: 48px; font-size:
|
|
280
|
+
.fw-root .fw-time { min-width: 48px; font-size: 14px; color: var(--fw-muted); font-variant-numeric: tabular-nums; }
|
|
263
281
|
.fw-root .fw-zoom { display: inline-flex; gap: 0; }
|
|
264
282
|
.fw-root .fw-sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
|
|
265
283
|
|
|
@@ -300,6 +318,21 @@
|
|
|
300
318
|
/* The agent view keeps scrubbing on a hairline track; the lanes above already show the activity. */
|
|
301
319
|
.fw-root-agents .fw-overview-marks { top: 13px; height: 2px; box-shadow: none; }
|
|
302
320
|
.fw-root-agents .fw-overview-marks > * { display: none; }
|
|
303
|
-
.fw-root .fw-segmented { display: inline-flex; padding:
|
|
304
|
-
.fw-root .fw-segmented button { appearance: none; border: 0; background: transparent; height: 26px; min-width: 36px; padding: 0
|
|
321
|
+
.fw-root .fw-segmented { display: inline-flex; padding: 4px; border-radius: 10px; background: var(--fw-well); border: 1px solid var(--fw-border); }
|
|
322
|
+
.fw-root .fw-segmented button { appearance: none; border: 0; background: transparent; height: 26px; min-width: 36px; padding: 0 12px; border-radius: 8px; font-size: 14px; color: var(--fw-muted); cursor: pointer; font-variant-numeric: tabular-nums; transition: background-color 120ms var(--fw-ease), color 120ms var(--fw-ease); }
|
|
305
323
|
.fw-root .fw-segmented button[aria-checked="true"] { background: var(--fw-surface); color: var(--fw-text); box-shadow: 0 1px 2px rgb(0 0 0 / .14), 0 0 0 1px var(--fw-border); }
|
|
324
|
+
|
|
325
|
+
/* Below 900 px the agent's name takes its own line above a full-width lane: nothing truncates and the bar keeps the width.
|
|
326
|
+
(At 768 px a 28% column cut 15 of 17 names on a real 17-agent run.) */
|
|
327
|
+
@container (max-width: 899px) {
|
|
328
|
+
/* The root is the queried container, so the variables go on its children. */
|
|
329
|
+
.fw-root-agents > * { --fw-label-col: 0px; --fw-bar-shift: 8px; }
|
|
330
|
+
.fw-root-agents .fw-view-agents .fw-label { position: absolute; z-index: 2; top: 4px; left: 0; right: 0; height: 18px; padding: 0 var(--fw-pad); gap: 8px; align-items: center; pointer-events: none; overflow: hidden; }
|
|
331
|
+
.fw-root-agents .fw-view-agents .fw-names { flex-direction: row; align-items: baseline; gap: 8px; min-width: 0; }
|
|
332
|
+
.fw-root-agents .fw-view-agents .fw-agent-dot { align-self: center; margin-top: 0; }
|
|
333
|
+
/* The label leaves the grid flow, so the lane must claim the track column itself. */
|
|
334
|
+
.fw-root-agents .fw-view-agents .fw-lane { grid-column: 2; }
|
|
335
|
+
/* Elbows pass behind the name, not through its letters. */
|
|
336
|
+
.fw-root-agents .fw-view-agents .fw-names { padding-right: 4px; border-radius: 4px; background: var(--fw-surface); }
|
|
337
|
+
.fw-root-agents .fw-view-agents .fw-row:hover .fw-names { background: transparent; }
|
|
338
|
+
}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as
|
|
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-BYKE-PMb.js";
|
|
2
2
|
import { CSSProperties, ReactElement, ReactNode } from "react";
|
|
3
3
|
//#region src/react/sparkline.d.ts
|
|
4
4
|
type SparklineDirection = "rising" | "falling" | "flat";
|
|
@@ -365,7 +365,7 @@ interface FlowWaterfallProps {
|
|
|
365
365
|
defaultView?: FlowView;
|
|
366
366
|
/** Host brand marks by span; the inspector draws kind glyphs without it. */
|
|
367
367
|
renderIcon?: (span: FlowSpan) => ReactNode;
|
|
368
|
-
/** Scroll height of the rows; the tree is virtualized beyond it. */
|
|
368
|
+
/** Scroll height of the rows; the tree is virtualized beyond it. Defaults to 20 agent rows, or 640 px of steps. */
|
|
369
369
|
maxHeight?: number;
|
|
370
370
|
/** Called when a row is opened or a bar is pinned, with that row's span; the built-in behavior still runs. */
|
|
371
371
|
onSelect?: (span: FlowSpan) => void;
|
package/dist/react/index.js
CHANGED
|
@@ -2634,7 +2634,6 @@ const ZoomOutGlyph = () => svg(/* @__PURE__ */ jsxs(Fragment, { children: [/* @_
|
|
|
2634
2634
|
r: "4.25"
|
|
2635
2635
|
}), /* @__PURE__ */ jsx("path", { d: "m10.25 10.25 3.25 3.25M5 7h4" })] }));
|
|
2636
2636
|
const FitGlyph = () => svg(/* @__PURE__ */ jsx("path", { d: "M2.5 6V3.5a1 1 0 0 1 1-1H6M10 2.5h2.5a1 1 0 0 1 1 1V6M13.5 10v2.5a1 1 0 0 1-1 1H10M6 13.5H3.5a1 1 0 0 1-1-1V10" }));
|
|
2637
|
-
const ReturnGlyph = () => svg(/* @__PURE__ */ jsx("path", { d: "M6 3.5 3 6.5l3 3M3 6.5h6.5a3.5 3.5 0 0 1 3.5 3.5v2" }));
|
|
2638
2637
|
//#endregion
|
|
2639
2638
|
//#region src/react/flow-waterfall-card.tsx
|
|
2640
2639
|
const KIND_LABEL = {
|
|
@@ -2665,12 +2664,9 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
|
|
|
2665
2664
|
if (compact) {
|
|
2666
2665
|
const who = owner ?? row;
|
|
2667
2666
|
const tokenCount = span.promptTokens !== void 0 || span.completionTokens !== void 0 ? (span.promptTokens ?? 0) + (span.completionTokens ?? 0) : void 0;
|
|
2668
|
-
const
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
tokenCount !== void 0 ? `${tokenCount.toLocaleString()} tokens` : void 0,
|
|
2672
|
-
span.costUsd !== void 0 ? formatCost(span.costUsd) : void 0
|
|
2673
|
-
];
|
|
2667
|
+
const cells = [[span.kind === "model" ? "Thinking" : KIND_LABEL[span.kind], duration]];
|
|
2668
|
+
if (tokenCount !== void 0) cells.push(["Tokens", count(tokenCount)]);
|
|
2669
|
+
if (span.costUsd !== void 0) cells.push(["Cost", formatCost(span.costUsd)]);
|
|
2674
2670
|
return /* @__PURE__ */ jsxs("div", {
|
|
2675
2671
|
id,
|
|
2676
2672
|
role: pinned ? "dialog" : "tooltip",
|
|
@@ -2695,21 +2691,18 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
|
|
|
2695
2691
|
className: "fw-agent-dot",
|
|
2696
2692
|
"aria-hidden": true
|
|
2697
2693
|
}),
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
children: [" ", who.span.detail]
|
|
2694
|
+
/* @__PURE__ */ jsx("span", {
|
|
2695
|
+
className: "fw-card-who",
|
|
2696
|
+
children: who.name
|
|
2702
2697
|
}),
|
|
2703
|
-
|
|
2704
|
-
className:
|
|
2705
|
-
|
|
2706
|
-
children: "Failed"
|
|
2698
|
+
/* @__PURE__ */ jsx("span", {
|
|
2699
|
+
className: `fw-pill fw-pill-${status.toLowerCase()}`,
|
|
2700
|
+
children: status
|
|
2707
2701
|
}),
|
|
2708
2702
|
pinned && /* @__PURE__ */ jsx("button", {
|
|
2709
2703
|
type: "button",
|
|
2710
2704
|
className: "fw-icon-button",
|
|
2711
2705
|
style: {
|
|
2712
|
-
marginLeft: row.ok ? "auto" : 0,
|
|
2713
2706
|
width: 20,
|
|
2714
2707
|
height: 20
|
|
2715
2708
|
},
|
|
@@ -2720,17 +2713,13 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
|
|
|
2720
2713
|
})
|
|
2721
2714
|
]
|
|
2722
2715
|
}),
|
|
2723
|
-
/* @__PURE__ */ jsx("
|
|
2716
|
+
/* @__PURE__ */ jsx("dl", {
|
|
2717
|
+
className: "fw-card-cells",
|
|
2718
|
+
children: cells.map(([name, value]) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: name }), /* @__PURE__ */ jsx("dd", { children: value })] }, name))
|
|
2719
|
+
}),
|
|
2720
|
+
/* @__PURE__ */ jsx("p", {
|
|
2724
2721
|
className: "fw-card-result",
|
|
2725
2722
|
children: span.output ?? span.name
|
|
2726
|
-
}),
|
|
2727
|
-
/* @__PURE__ */ jsx("div", {
|
|
2728
|
-
className: "fw-card-meta",
|
|
2729
|
-
children: meta.filter(Boolean).join(" · ")
|
|
2730
|
-
}),
|
|
2731
|
-
span.input && /* @__PURE__ */ jsxs("div", {
|
|
2732
|
-
className: "fw-card-message",
|
|
2733
|
-
children: [/* @__PURE__ */ jsx(ReturnGlyph, {}), /* @__PURE__ */ jsx("span", { children: span.input })]
|
|
2734
2723
|
})
|
|
2735
2724
|
]
|
|
2736
2725
|
});
|
|
@@ -2885,6 +2874,7 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
2885
2874
|
"aria-selected": selected,
|
|
2886
2875
|
tabIndex: focusable ? 0 : -1,
|
|
2887
2876
|
"data-tone": toneKey(row.tone),
|
|
2877
|
+
"data-outcome": span.outcome?.tone,
|
|
2888
2878
|
className: `fw-row${active ? " fw-active" : ""}${sticky ? " fw-sticky-row" : ""}${isAgent ? " fw-agent-row" : ""}`,
|
|
2889
2879
|
style: { transform: `translateY(${top}px)` },
|
|
2890
2880
|
onFocus: (event) => {
|
|
@@ -3172,6 +3162,25 @@ function FlowBody(props) {
|
|
|
3172
3162
|
const msPerPx = (win.endMs - win.startMs) / Math.max(1, trackPx);
|
|
3173
3163
|
const tickEnd = win.startMs + (trackPx + (view === "spans" ? 72 : 0)) * msPerPx;
|
|
3174
3164
|
const ticks = clockTicks(win.startMs, tickEnd, Math.max(1, trackPx + (view === "spans" ? 72 : 0)));
|
|
3165
|
+
const markerLabels = useMemo(() => {
|
|
3166
|
+
const shown = /* @__PURE__ */ new Map();
|
|
3167
|
+
let freeFrom = -Infinity;
|
|
3168
|
+
for (const marker of [...props.markers ?? []].sort((a, b) => a.at - b.at)) {
|
|
3169
|
+
const x = (marker.at - win.startMs) / msPerPx;
|
|
3170
|
+
const width = marker.label.length * 7 + 16;
|
|
3171
|
+
const side = x + width > trackPx ? "left" : "right";
|
|
3172
|
+
const left = side === "right" ? x : x - width;
|
|
3173
|
+
if (x < 0 || x > trackPx || left < freeFrom) continue;
|
|
3174
|
+
shown.set(marker, side);
|
|
3175
|
+
freeFrom = left + width + 8;
|
|
3176
|
+
}
|
|
3177
|
+
return shown;
|
|
3178
|
+
}, [
|
|
3179
|
+
props.markers,
|
|
3180
|
+
win.startMs,
|
|
3181
|
+
msPerPx,
|
|
3182
|
+
trackPx
|
|
3183
|
+
]);
|
|
3175
3184
|
useEffect(() => {
|
|
3176
3185
|
const element = scroller.current;
|
|
3177
3186
|
if (!element) return;
|
|
@@ -3410,8 +3419,9 @@ function FlowBody(props) {
|
|
|
3410
3419
|
className: "fw-marker",
|
|
3411
3420
|
"data-tone": marker.tone ?? "neutral",
|
|
3412
3421
|
style: { "--s": marker.at },
|
|
3413
|
-
|
|
3414
|
-
|
|
3422
|
+
title: marker.label,
|
|
3423
|
+
children: markerLabels.has(marker) && /* @__PURE__ */ jsx("span", {
|
|
3424
|
+
className: `fw-marker-label${markerLabels.get(marker) === "left" ? " fw-marker-label-left" : ""}`,
|
|
3415
3425
|
children: marker.label
|
|
3416
3426
|
})
|
|
3417
3427
|
}, `${marker.at}-${marker.label}`))
|
|
@@ -3694,10 +3704,40 @@ const NAME_WIDTH = {
|
|
|
3694
3704
|
spans: 320,
|
|
3695
3705
|
agents: 240
|
|
3696
3706
|
};
|
|
3707
|
+
/** One legend item per outcome label the agents carry, with its count; nothing when no agent has an outcome. */
|
|
3708
|
+
function OutcomeLegend({ agents }) {
|
|
3709
|
+
const items = /* @__PURE__ */ new Map();
|
|
3710
|
+
for (const row of agents) {
|
|
3711
|
+
const outcome = row.span.outcome;
|
|
3712
|
+
if (!outcome) continue;
|
|
3713
|
+
const item = items.get(outcome.label) ?? {
|
|
3714
|
+
tone: outcome.tone,
|
|
3715
|
+
count: 0
|
|
3716
|
+
};
|
|
3717
|
+
item.count += 1;
|
|
3718
|
+
items.set(outcome.label, item);
|
|
3719
|
+
}
|
|
3720
|
+
if (items.size === 0) return null;
|
|
3721
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
3722
|
+
className: "fw-legend",
|
|
3723
|
+
"aria-label": "Outcomes",
|
|
3724
|
+
children: [...items].map(([label, item]) => /* @__PURE__ */ jsxs("li", { children: [
|
|
3725
|
+
/* @__PURE__ */ jsx("span", {
|
|
3726
|
+
className: "fw-swatch",
|
|
3727
|
+
"data-tone": "0",
|
|
3728
|
+
"data-outcome": item.tone,
|
|
3729
|
+
"aria-hidden": true
|
|
3730
|
+
}),
|
|
3731
|
+
label,
|
|
3732
|
+
" ",
|
|
3733
|
+
/* @__PURE__ */ jsx("b", { children: item.count })
|
|
3734
|
+
] }, label))
|
|
3735
|
+
});
|
|
3736
|
+
}
|
|
3697
3737
|
const headlineSeconds = (ms) => ms < 6e4 ? `${(ms / 1e3).toFixed(1)}s` : formatSpanDuration(ms);
|
|
3698
3738
|
/** Shared trace inspector: the agent timeline and the span waterfall over one clock.
|
|
3699
3739
|
* Data remains producer-owned; playback, zoom and folding change presentation only. */
|
|
3700
|
-
function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defaultCollapsed = false, defaultView = "spans", renderIcon, maxHeight
|
|
3740
|
+
function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defaultCollapsed = false, defaultView = "spans", renderIcon, maxHeight, onSelect, markers }) {
|
|
3701
3741
|
const root = useRef(null);
|
|
3702
3742
|
const search = useRef(null);
|
|
3703
3743
|
const slider = useRef(null);
|
|
@@ -3778,6 +3818,17 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
3778
3818
|
collapsed
|
|
3779
3819
|
]);
|
|
3780
3820
|
const slowest = view === "spans" ? slowestRow(rows, clockMs) : void 0;
|
|
3821
|
+
const rowsHeight = maxHeight ?? (view === "agents" ? ROW_HEIGHT.agents * 20 : 640);
|
|
3822
|
+
const resized = useRef(false);
|
|
3823
|
+
useLayoutEffect(() => {
|
|
3824
|
+
if (view !== "agents" || resized.current || !root.current) return;
|
|
3825
|
+
let widest = 0;
|
|
3826
|
+
for (const text of root.current.querySelectorAll(".fw-view-agents .fw-row:not(.fw-sticky-row) .fw-name-text")) {
|
|
3827
|
+
const label = text.closest(".fw-label");
|
|
3828
|
+
if (label) widest = Math.max(widest, text.scrollWidth + label.clientWidth - text.clientWidth);
|
|
3829
|
+
}
|
|
3830
|
+
if (widest > 0) setNameWidth(Math.max(140, Math.min(560, Math.ceil(widest) + 16)));
|
|
3831
|
+
}, [view, agents]);
|
|
3781
3832
|
useEffect(() => {
|
|
3782
3833
|
const keydown = (event) => {
|
|
3783
3834
|
const active = document.activeElement;
|
|
@@ -3991,47 +4042,51 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
3991
4042
|
className: "fw-heading",
|
|
3992
4043
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
3993
4044
|
className: "fw-title-block",
|
|
3994
|
-
children: [
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
"
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4045
|
+
children: [
|
|
4046
|
+
/* @__PURE__ */ jsxs("div", {
|
|
4047
|
+
className: "fw-title-line",
|
|
4048
|
+
children: [
|
|
4049
|
+
/* @__PURE__ */ jsx("span", {
|
|
4050
|
+
className: "fw-title-glyph",
|
|
4051
|
+
"aria-hidden": true,
|
|
4052
|
+
children: /* @__PURE__ */ jsx(TraceGlyph, {})
|
|
4053
|
+
}),
|
|
4054
|
+
/* @__PURE__ */ jsx("h3", {
|
|
4055
|
+
className: "fw-title",
|
|
4056
|
+
children: label
|
|
4057
|
+
}),
|
|
4058
|
+
isLive && /* @__PURE__ */ jsx("span", {
|
|
4059
|
+
className: "fw-state fw-state-live",
|
|
4060
|
+
children: "Live"
|
|
4061
|
+
})
|
|
4062
|
+
]
|
|
4063
|
+
}),
|
|
4064
|
+
view === "agents" && /* @__PURE__ */ jsx("p", {
|
|
4065
|
+
className: "fw-headline",
|
|
4066
|
+
"aria-live": "polite",
|
|
4067
|
+
children: done ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4068
|
+
/* @__PURE__ */ jsx("span", {
|
|
4069
|
+
className: "fw-done-mark",
|
|
4070
|
+
"aria-label": "done",
|
|
4071
|
+
children: "✓"
|
|
4072
|
+
}),
|
|
4073
|
+
agents.length,
|
|
4074
|
+
" ",
|
|
4075
|
+
agents.length === 1 ? "agent" : "agents",
|
|
4076
|
+
" · ",
|
|
4077
|
+
headlineSeconds(limit),
|
|
4078
|
+
speedup >= 1.05 ? ` · ${speedup.toFixed(1)}x parallel` : "",
|
|
4079
|
+
trace.costUsd !== void 0 ? ` · ${formatCost(trace.costUsd)}` : ""
|
|
4080
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4081
|
+
headlineSeconds(clockMs),
|
|
4082
|
+
" · ",
|
|
4083
|
+
tokens.toLocaleString(),
|
|
4084
|
+
" tokens",
|
|
4085
|
+
trace.costUsd !== void 0 ? ` · ${formatCost(trace.costUsd)}` : ""
|
|
4086
|
+
] })
|
|
4087
|
+
}),
|
|
4088
|
+
/* @__PURE__ */ jsx(OutcomeLegend, { agents })
|
|
4089
|
+
]
|
|
4035
4090
|
}), /* @__PURE__ */ jsxs("div", {
|
|
4036
4091
|
className: "fw-tools",
|
|
4037
4092
|
children: [/* @__PURE__ */ jsx("div", {
|
|
@@ -4099,7 +4154,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4099
4154
|
byId,
|
|
4100
4155
|
totalRows: rows.length,
|
|
4101
4156
|
view,
|
|
4102
|
-
maxHeight,
|
|
4157
|
+
maxHeight: rowsHeight,
|
|
4103
4158
|
collapsed,
|
|
4104
4159
|
searching: Boolean(query.trim()),
|
|
4105
4160
|
slowestId: slowest?.id,
|
|
@@ -4123,7 +4178,10 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4123
4178
|
onZoom: zoomBy,
|
|
4124
4179
|
onPan: pan,
|
|
4125
4180
|
nameWidth,
|
|
4126
|
-
onNameWidth: (px) =>
|
|
4181
|
+
onNameWidth: (px) => {
|
|
4182
|
+
resized.current = true;
|
|
4183
|
+
setNameWidth(Math.max(140, Math.min(560, px)));
|
|
4184
|
+
},
|
|
4127
4185
|
onTrack: setLaneElement,
|
|
4128
4186
|
onExpandAll: rows.some((row) => row.hasChildren) ? () => setCollapsed(/* @__PURE__ */ new Set()) : void 0,
|
|
4129
4187
|
onCollapseAll: () => setCollapsed(new Set(rows.filter((row) => row.hasChildren).map((row) => row.id)))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/charts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
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",
|