@tangle-network/charts 0.21.14 → 0.21.16
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 +2 -2
- package/dist/{flow-trace-DBJY1YAA.d.ts → flow-trace-CSy-mQ31.d.ts} +4 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -2
- package/dist/react/flow-waterfall.css +126 -103
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +293 -233
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Benchmark figures as static SVG and HTML tables, plus optional React timing prim
|
|
|
4
4
|
|
|
5
5
|
The React entry also exports `DayBars`, `HBars`, `Histogram`, `Lines`, `Meter`, `Spark`, and `Gantt` for live operational pages. Import `@tangle-network/charts/react/live.css` for their layout and set `--chart-live-*` theme tokens on an ancestor. Data, state colors, destination links, labels, and interpretation remain with the caller. `DayBars` accepts an `onOpen(index)` action with keyboard activation.
|
|
6
6
|
|
|
7
|
-
`FlowWaterfall` from `@tangle-network/charts/react` is the shared live trace inspector: an agent timeline (one row per agent, elbow handoffs, hatched waiting) and a virtualized span waterfall over one clock. It owns hierarchy layout, folding, search, playback, zoom, span cards and running-span growth. Producers own identity, `parentId`, explicit `status` and observed totals. Import `@tangle-network/charts/react/flow-waterfall.css`; its `fw-*` classes and six-field summary support hosts without a Tailwind build. The React-free root exports the `FlowSpan` and `FlowTrace` types.
|
|
7
|
+
`FlowWaterfall` from `@tangle-network/charts/react` is the shared live trace inspector: an agent timeline (one row per agent, elbow handoffs, hatched waiting) and a virtualized span waterfall over one clock. It owns hierarchy layout, folding, search, playback, zoom, span cards and running-span growth. Producers own identity, `parentId`, explicit `status` and observed totals. Import `@tangle-network/charts/react/flow-waterfall.css`; its `fw-*` classes and six-field summary support hosts without a Tailwind build. Every color, radius and shadow in it is a Brand role (text, card, hairline, well, chart grid, status inks, the categorical tones for agents), so the host loads `@tangle-network/brand` `tokens.css` (or `legacy-light.css`) and sets its theme; the stylesheet has no color of its own. The React-free root exports the `FlowSpan` and `FlowTrace` types.
|
|
8
8
|
|
|
9
9
|
`WaterfallHeader`, `WaterfallTimeAxis`, `WaterfallBar` and `WaterfallRow` remain available for existing consumers that own their own hierarchy and selection. All use the same `waterfallSpanGeometry` and `waterfallTicks`. Existing fixed-width cells, tones, formatting, attribution slots and unordered-time `—` marks are preserved. Optional fluid tracks, inner-bar colour/class, overlay and clipping fields support the shared inspector without changing existing calls. The primitives were extracted from Intelligence under [brand #211](https://github.com/tangle-network/brand/pull/211); the shared inspector extends that presentation ownership while leaving failure interpretation with producers.
|
|
10
10
|
|
|
@@ -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. Give an agent span an `outcome` (`{ tone, label }`) and its lane takes the outcome color and the header a legend. `detail` becomes the agent's subtitle, and `facts` (`[{ label, value }]`) adds
|
|
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. `detail` becomes the agent's subtitle. The card is a table of what the row recorded, every value with its unit: model, duration, tokens in, out and cached (the span's `promptTokens`, `completionTokens` and `cachedTokens`, else the sum of its children's), LLM latency (median and p95 of its model calls), cost, LLM calls, tool calls by tool (the name before " · "), failed steps, and retries and restarts (`retryOf`, `marks` of kind `restart`); a value nobody recorded is left out. `facts` (`[{ label, value }]`) adds the host's rows after it. The card opens at the pointer, above or below its row, whichever side fits the viewport. `onTimeChange` reports where the reader scrubbed (null when live), and markers double as an event track (click, N or P to jump). A partly priced trace shows its floor and coverage (`costCoverage`), and `billedUsd` shows the real charge beside the list price. 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
|
|
|
@@ -327,6 +327,9 @@ interface FlowSpan {
|
|
|
327
327
|
costBound?: 'lower' | 'upper';
|
|
328
328
|
promptTokens?: number;
|
|
329
329
|
completionTokens?: number;
|
|
330
|
+
/** Input tokens the provider served from its cache, as the producer recorded them. A span without its own token fields shows
|
|
331
|
+
* the sum of its descendants' in the card. */
|
|
332
|
+
cachedTokens?: number;
|
|
330
333
|
/** Short producer summaries shown in the span card. */
|
|
331
334
|
input?: string;
|
|
332
335
|
output?: string;
|
|
@@ -336,7 +339,7 @@ interface FlowSpan {
|
|
|
336
339
|
handoffTo?: string;
|
|
337
340
|
/** The host's verdict on an agent span, such as shipped or crashed: colors its lane and joins the legend. */
|
|
338
341
|
outcome?: FlowOutcome;
|
|
339
|
-
/**
|
|
342
|
+
/** Host facts shown as extra rows of the card's table, such as releases or commits; a label the table already has is skipped. */
|
|
340
343
|
facts?: readonly {
|
|
341
344
|
label: string;
|
|
342
345
|
value: string;
|
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-
|
|
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";
|
|
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. */
|
package/dist/index.js
CHANGED
|
@@ -3338,12 +3338,12 @@ function unitDotLayout(groups, width, height, o = {}) {
|
|
|
3338
3338
|
//#endregion
|
|
3339
3339
|
//#region src/otlp-trace.ts
|
|
3340
3340
|
/** Attributes that carry cached input apart from fresh input (OpenTelemetry GenAI and Anthropic-style names). */
|
|
3341
|
-
const
|
|
3341
|
+
const CACHE_READ_KEYS = [
|
|
3342
3342
|
"gen_ai.usage.cache_read_input_tokens",
|
|
3343
|
-
"gen_ai.usage.cache_creation_input_tokens",
|
|
3344
3343
|
"gen_ai.usage.input_tokens.cached",
|
|
3345
3344
|
"tangle.llm.cache_read_tokens"
|
|
3346
3345
|
];
|
|
3346
|
+
const CACHE_KEYS = [...CACHE_READ_KEYS, "gen_ai.usage.cache_creation_input_tokens"];
|
|
3347
3347
|
/** Price model calls from an OpenAI-compatible model catalog (`data[].pricing.prompt|completion`, USD per token), such as
|
|
3348
3348
|
* the Router's `/v1/models`. Read the catalog at runtime; never copy its numbers. */
|
|
3349
3349
|
function catalogPrice(catalog) {
|
|
@@ -3532,6 +3532,7 @@ function otlpFlowTrace(input, options = {}) {
|
|
|
3532
3532
|
const model = pick(a, MODEL_KEYS);
|
|
3533
3533
|
const prompt = num(a["gen_ai.usage.input_tokens"]);
|
|
3534
3534
|
const completion = num(a["gen_ai.usage.output_tokens"]);
|
|
3535
|
+
const cached = CACHE_READ_KEYS.map((key) => num(a[key])).find((n) => n !== void 0);
|
|
3535
3536
|
let kind = span.kind === "LLM" ? "model" : span.kind === "TOOL" ? "tool" : "pipeline";
|
|
3536
3537
|
if (kind === "tool" && (a["traces.agent.spawn"] === true || spawnTarget.has(span.span_id) || handoffTo.has(span.span_id))) kind = "handoff";
|
|
3537
3538
|
let name = str(a["gen_ai.tool.name"]) ?? span.name;
|
|
@@ -3564,6 +3565,7 @@ function otlpFlowTrace(input, options = {}) {
|
|
|
3564
3565
|
...listPrice !== void 0 ? { costUsd: listPrice } : {},
|
|
3565
3566
|
...prompt !== void 0 ? { promptTokens: prompt } : {},
|
|
3566
3567
|
...completion !== void 0 ? { completionTokens: completion } : {},
|
|
3568
|
+
...cached !== void 0 ? { cachedTokens: cached } : {},
|
|
3567
3569
|
...span.status?.message ? { output: span.status.message } : {},
|
|
3568
3570
|
...spawnTarget.has(span.span_id) ? { handoffTo: spawnTarget.get(span.span_id) } : handoffTo.has(span.span_id) ? { handoffTo: handoffTo.get(span.span_id) } : {}
|
|
3569
3571
|
});
|
|
@@ -1,40 +1,39 @@
|
|
|
1
|
-
/* Standalone surface:
|
|
1
|
+
/* Standalone surface: no Tailwind build required. Every color, radius and shadow is a Brand role from
|
|
2
|
+
* @tangle-network/brand tokens.css, which the host loads with its theme (data-theme or the light/dark class, which also
|
|
3
|
+
* set color-scheme for the light-dark() mixes below); nothing here is a color of its own, so a named theme carries in.
|
|
2
4
|
* Geometry reads three variables the clock writes every frame: --fw-t (clock ms), --fw-v0 (window start ms)
|
|
3
5
|
* and --fw-k (px per ms). A mark carries --s and --e (its start and end ms), so growth, playback and zoom
|
|
4
6
|
* move marks without a React render.
|
|
5
|
-
* Type uses the 12/14
|
|
6
|
-
/* light-dark() follows the host's Brand theme (data-theme or the light/dark class), not the OS: a light page
|
|
7
|
-
on a dark OS otherwise resolves the dark fallbacks and muted text drops to 2.4:1. */
|
|
8
|
-
:is(:root[data-theme="light"], .light) .fw-root { color-scheme: light; }
|
|
9
|
-
:is(:root[data-theme="dark"], .dark) .fw-root { color-scheme: dark; }
|
|
7
|
+
* Type uses the 12/14 px scale; spacing sits on a 4 px grid. */
|
|
10
8
|
.fw-root {
|
|
11
|
-
--fw-text: var(--text-primary
|
|
12
|
-
--fw-muted: var(--text-muted
|
|
13
|
-
--fw-
|
|
14
|
-
--fw-
|
|
15
|
-
|
|
16
|
-
--fw-border: var(--border
|
|
17
|
-
--fw-grid:
|
|
18
|
-
--fw-guide:
|
|
19
|
-
|
|
20
|
-
--fw-
|
|
21
|
-
--fw-
|
|
22
|
-
--fw-
|
|
23
|
-
--fw-
|
|
24
|
-
--fw-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
--fw-kind-
|
|
9
|
+
--fw-text: var(--text-primary);
|
|
10
|
+
--fw-muted: var(--text-muted);
|
|
11
|
+
--fw-surface: var(--bg-card);
|
|
12
|
+
--fw-raised: var(--bg-elevated);
|
|
13
|
+
/* One hairline for the card edge and every rule inside it; the plot grid is the chart grid. */
|
|
14
|
+
--fw-border: hsl(var(--hsl-border));
|
|
15
|
+
--fw-grid: var(--chart-grid);
|
|
16
|
+
--fw-guide: var(--chart-rule);
|
|
17
|
+
/* The trace is a card, so its wells are the in-card well. */
|
|
18
|
+
--fw-well: var(--well-card);
|
|
19
|
+
--fw-hover: var(--bg-hover);
|
|
20
|
+
--fw-accent: var(--accent-text);
|
|
21
|
+
--fw-failed: var(--surface-danger-text);
|
|
22
|
+
--fw-ok: var(--surface-success-text);
|
|
23
|
+
--fw-amber: var(--surface-warning-text);
|
|
24
|
+
/* Step kinds inside an agent bar: the chart accent and a categorical hue, far from the outcome greens and reds. */
|
|
25
|
+
--fw-kind-model: var(--chart-accent);
|
|
26
|
+
--fw-kind-tool: var(--tone-cyan-icon);
|
|
28
27
|
--fw-neutral-bar: color-mix(in oklab, var(--fw-text) 15%, var(--fw-surface));
|
|
29
28
|
--fw-hatch: color-mix(in oklab, var(--fw-text) 22%, transparent);
|
|
30
|
-
/* Root agent neutral, then five
|
|
31
|
-
|
|
32
|
-
--fw-
|
|
33
|
-
|
|
34
|
-
--fw-h2:
|
|
35
|
-
--fw-h3:
|
|
36
|
-
--fw-h4:
|
|
37
|
-
--fw-h5:
|
|
29
|
+
/* Root agent neutral, then five of Brand's categorical hues, each 3:1 or more against the card in both modes. No warm hue
|
|
30
|
+
among the agents (amber is kept for the slowest and red for failure), and neighbours alternate cool and warm-leaning. */
|
|
31
|
+
--fw-h0: var(--chart-muted);
|
|
32
|
+
--fw-h1: var(--tone-blue-icon);
|
|
33
|
+
--fw-h2: var(--tone-teal-icon);
|
|
34
|
+
--fw-h3: var(--tone-pink-icon);
|
|
35
|
+
--fw-h4: var(--tone-violet-icon);
|
|
36
|
+
--fw-h5: var(--tone-lime-icon);
|
|
38
37
|
--fw-ease: cubic-bezier(.2, .8, .2, 1);
|
|
39
38
|
/* Bar height = row height - 16, so a centered bar sits 8 px from the row edge: 20 px in a 36 px step row. */
|
|
40
39
|
--fw-bar-h: 20px;
|
|
@@ -47,15 +46,15 @@
|
|
|
47
46
|
color: var(--fw-text);
|
|
48
47
|
background: var(--fw-surface);
|
|
49
48
|
border: 1px solid var(--fw-border);
|
|
50
|
-
border-radius:
|
|
51
|
-
font-family: var(--font-sans
|
|
49
|
+
border-radius: var(--radius-xl);
|
|
50
|
+
font-family: var(--font-sans);
|
|
52
51
|
font-size: 14px;
|
|
53
52
|
line-height: 20px;
|
|
54
53
|
/* Tabular figures only on numeric cells: on names they widen hyphens ("v6 - battery", "glm - 5.3"). */
|
|
55
54
|
font-feature-settings: "cv11" 1;
|
|
56
55
|
-webkit-font-smoothing: antialiased;
|
|
57
56
|
}
|
|
58
|
-
.fw-root-agents { --fw-bar-h: 24px; --fw-label-col: min(var(--fw-name-w), 28%);
|
|
57
|
+
.fw-root-agents { --fw-bar-h: 24px; --fw-label-col: min(var(--fw-name-w), 28%); }
|
|
59
58
|
.fw-root *, .fw-root *::before, .fw-root *::after { box-sizing: border-box; }
|
|
60
59
|
.fw-root :is(p, dl, dd, h3, ul) { margin: 0; padding: 0; }
|
|
61
60
|
.fw-root :is(button, input, select) { font: inherit; color: inherit; }
|
|
@@ -67,13 +66,14 @@
|
|
|
67
66
|
.fw-root [data-tone="3"] { --tone: var(--fw-h3); }
|
|
68
67
|
.fw-root [data-tone="4"] { --tone: var(--fw-h4); }
|
|
69
68
|
.fw-root [data-tone="5"] { --tone: var(--fw-h5); }
|
|
70
|
-
/*
|
|
71
|
-
the
|
|
69
|
+
/* The outline is the full hue, which passes 3:1 against the card, so every mark keeps its edge; on a dark card the fill sits a
|
|
70
|
+
step toward the canvas, so a bar reads apart from a handoff pill in the full hue. The dim shade (tool calls) mixes the hue
|
|
71
|
+
toward the card, more of it on a dark card. */
|
|
72
72
|
.fw-root [data-tone] {
|
|
73
|
-
--fill: light-dark(var(--tone), color-mix(in oklab, var(--tone) 94%,
|
|
74
|
-
--line:
|
|
75
|
-
--dim: light-dark(color-mix(in oklab, var(--tone) 62%,
|
|
76
|
-
--dim-line: light-dark(color-mix(in oklab, var(--tone) 70%,
|
|
73
|
+
--fill: light-dark(var(--tone), color-mix(in oklab, var(--tone) 94%, hsl(var(--hsl-background))));
|
|
74
|
+
--line: var(--tone);
|
|
75
|
+
--dim: light-dark(color-mix(in oklab, var(--tone) 62%, var(--fw-surface)), color-mix(in oklab, var(--tone) 40%, var(--fw-surface)));
|
|
76
|
+
--dim-line: light-dark(color-mix(in oklab, var(--tone) 70%, var(--fw-surface)), color-mix(in oklab, var(--tone) 55%, var(--fw-surface)));
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/* Header */
|
|
@@ -82,20 +82,20 @@
|
|
|
82
82
|
.fw-root .fw-title-line { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
83
83
|
.fw-root .fw-title-glyph { display: inline-flex; color: var(--fw-accent); }
|
|
84
84
|
.fw-root .fw-title { font-size: 14px; line-height: 20px; font-weight: 600; letter-spacing: -.005em; white-space: nowrap; }
|
|
85
|
-
.fw-root .fw-state { display: inline-flex; align-items: center; gap: 8px; height: 20px; padding: 0 8px; border-radius:
|
|
85
|
+
.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; }
|
|
86
86
|
.fw-root .fw-state::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
|
|
87
87
|
.fw-root .fw-state-live { color: var(--fw-accent); background: color-mix(in oklab, var(--fw-accent) 12%, transparent); }
|
|
88
88
|
.fw-root .fw-state-live::before { animation: fw-pulse 1.6s var(--fw-ease) infinite; }
|
|
89
89
|
.fw-root .fw-headline { color: var(--fw-muted); display: flex; align-items: center; gap: 8px; }
|
|
90
90
|
.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; }
|
|
91
91
|
.fw-root .fw-tools { display: flex; align-items: center; gap: 8px; }
|
|
92
|
-
.fw-root .fw-views { display: inline-flex; padding: 4px; border-radius:
|
|
93
|
-
.fw-root .fw-view-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0 12px; border-radius:
|
|
92
|
+
.fw-root .fw-views { display: inline-flex; padding: 4px; border-radius: var(--radius-md); background: var(--fw-well); border: 1px solid var(--fw-border); }
|
|
93
|
+
.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); }
|
|
94
94
|
/* The shown view is a state, not a control: no click target, no pointer. */
|
|
95
|
-
.fw-root .fw-view-button[aria-checked="true"] { opacity: 1; cursor: default; background: var(--fw-surface); color: var(--fw-text); box-shadow:
|
|
95
|
+
.fw-root .fw-view-button[aria-checked="true"] { opacity: 1; cursor: default; background: var(--fw-surface); color: var(--fw-text); box-shadow: var(--shadow-card), 0 0 0 1px var(--fw-border); }
|
|
96
96
|
.fw-root .fw-search { position: relative; display: flex; align-items: center; }
|
|
97
97
|
.fw-root .fw-search-glyph { position: absolute; left: 8px; display: inline-flex; color: var(--fw-muted); pointer-events: none; }
|
|
98
|
-
.fw-root .fw-search-input { appearance: none; width: 200px; height: 32px; padding: 0 12px 0 28px; border: 1px solid var(--fw-border); border-radius:
|
|
98
|
+
.fw-root .fw-search-input { appearance: none; width: 200px; height: 32px; padding: 0 12px 0 28px; border: 1px solid var(--fw-border); border-radius: var(--radius-md); background: var(--fw-well); font-size: 14px; }
|
|
99
99
|
.fw-root .fw-search-input::placeholder { color: var(--fw-muted); }
|
|
100
100
|
.fw-root .fw-search-input::-webkit-search-cancel-button { display: none; }
|
|
101
101
|
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
.fw-root .fw-axis-top { border-bottom: 1px solid var(--fw-border); }
|
|
113
113
|
.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; }
|
|
114
114
|
.fw-root .fw-branch-controls { margin-left: auto; display: flex; gap: 4px; }
|
|
115
|
-
.fw-root .fw-text-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0 8px; border-radius:
|
|
115
|
+
.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; }
|
|
116
116
|
.fw-root .fw-text-button:hover { background: var(--fw-hover); color: var(--fw-text); }
|
|
117
117
|
.fw-root .fw-resize { position: absolute; top: 0; right: -4px; width: 8px; height: 100%; cursor: col-resize; z-index: 2; }
|
|
118
118
|
.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; }
|
|
@@ -135,48 +135,49 @@
|
|
|
135
135
|
.fw-root .fw-marker-line[data-tone="failed"] { --fw-marker: var(--fw-failed); }
|
|
136
136
|
.fw-root .fw-marker[data-tone="ok"] { --fw-marker: var(--fw-ok); }
|
|
137
137
|
.fw-root .fw-marker[data-tone="failed"] { --fw-marker: var(--fw-failed); }
|
|
138
|
-
/* A pause: the run stood still. A
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
.fw-root .fw-pause-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
/* The label takes the pointer, for its reason; the band stays click-through to the rows. */
|
|
146
|
-
.fw-root .fw-pause-label[title] { pointer-events: auto; cursor: help; }
|
|
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: 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); }
|
|
138
|
+
/* A pause: the run stood still. A faint band behind the rows (they stay readable over it), and on the axis a hatched strip under
|
|
139
|
+
the ticks that names its length and reason on hover and focus; a chip over the axis hid the hour ticks. */
|
|
140
|
+
.fw-root :is(.fw-pause, .fw-pause-mark) { position: absolute; left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); width: calc(max(0, var(--e) - var(--s)) * var(--fw-k) * 1px); }
|
|
141
|
+
.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); }
|
|
142
|
+
.fw-root .fw-pause-mark { bottom: 3px; height: 6px; border-radius: 2px; cursor: help;
|
|
143
|
+
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); }
|
|
144
|
+
.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); }
|
|
148
145
|
.fw-root .fw-marker-label-left { left: auto; right: 3px; }
|
|
149
146
|
.fw-root .fw-marker[data-tone="warn"], .fw-root .fw-event[data-tone="warn"] { --fw-marker: var(--fw-amber); }
|
|
150
147
|
/* The event track: one dot per marker on the axis; click or N/P moves the cursor there. */
|
|
151
148
|
.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); }
|
|
152
149
|
.fw-root .fw-event[data-tone="ok"] { --fw-marker: var(--fw-ok); }
|
|
153
150
|
.fw-root .fw-event[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--fw-surface), 0 0 0 4px var(--fw-marker); }
|
|
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:
|
|
151
|
+
.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; }
|
|
155
152
|
.fw-root .fw-event[data-tone="failed"] { --fw-marker: var(--fw-failed); }
|
|
156
153
|
.fw-root .fw-event:hover, .fw-root .fw-event:focus-visible { transform: scale(1.4); outline: 0; }
|
|
157
154
|
.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); }
|
|
158
155
|
.fw-root .fw-empty { padding: 40px 16px; text-align: center; color: var(--fw-muted); }
|
|
159
|
-
.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),
|
|
156
|
+
.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), var(--shadow-card); }
|
|
160
157
|
|
|
161
158
|
/* Rows */
|
|
162
159
|
.fw-root .fw-row { position: absolute; top: 0; left: 0; right: 0; transform: translateY(var(--row-y)); height: var(--fw-row-h); display: grid; grid-template-columns: var(--fw-label-col) minmax(0, 1fr); outline: 0; contain: layout style; }
|
|
163
|
-
|
|
164
|
-
.fw-root .fw-row
|
|
165
|
-
.fw-root .fw-row
|
|
166
|
-
|
|
160
|
+
/* A row's tint is one variable, so the chips on it (an end label, a stacked name) paint the same plane as the row. */
|
|
161
|
+
.fw-root .fw-row { background: var(--fw-tint, transparent); }
|
|
162
|
+
.fw-root .fw-row:hover { --fw-tint: var(--fw-hover); }
|
|
163
|
+
.fw-root .fw-row.fw-active { --fw-tint: color-mix(in oklab, var(--fw-accent) 7%, transparent); }
|
|
164
|
+
.fw-root .fw-row[aria-selected="true"] { --fw-tint: var(--ix-sel-bg); }
|
|
165
|
+
/* A selected agent lifts: an accent edge on its row, and its peers' marks recede; their names and labels keep the muted ink, which
|
|
166
|
+
reads at 4.5:1, where fading whole cells dropped them to 2.2:1 and let connectors and pause bands show through the text. */
|
|
167
167
|
.fw-root-agents[data-selection] .fw-row[aria-selected="true"] { box-shadow: inset 3px 0 0 var(--fw-accent); }
|
|
168
|
-
.fw-root-agents[data-selection] .fw-row:not([aria-selected="true"])
|
|
169
|
-
.fw-root-agents[data-selection] .fw-row:not([aria-selected="true"]):hover
|
|
168
|
+
.fw-root-agents[data-selection] .fw-row:not([aria-selected="true"]) { --fw-recede: .4; }
|
|
169
|
+
.fw-root-agents[data-selection] .fw-row:not([aria-selected="true"]):hover { --fw-recede: .8; }
|
|
170
|
+
.fw-root-agents[data-selection] .fw-row:not([aria-selected="true"]) .fw-span-name { color: var(--fw-muted); }
|
|
170
171
|
.fw-root .fw-row:focus-visible { box-shadow: inset 2px 0 0 var(--fw-accent); }
|
|
171
172
|
.fw-root .fw-sticky-row { background: var(--fw-surface); }
|
|
172
173
|
.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); }
|
|
173
174
|
.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); }
|
|
174
175
|
.fw-root .fw-toggle, .fw-root .fw-toggle-space { flex: 0 0 16px; width: 16px; height: 16px; }
|
|
175
|
-
.fw-root .fw-toggle { appearance: none; display: grid; place-items: center; padding: 0; border: 0; border-radius:
|
|
176
|
+
.fw-root .fw-toggle { appearance: none; display: grid; place-items: center; padding: 0; border: 0; border-radius: var(--radius-tag); background: transparent; color: var(--fw-muted); cursor: pointer; }
|
|
176
177
|
.fw-root .fw-toggle svg { width: 12px; height: 12px; transition: transform 160ms var(--fw-ease); }
|
|
177
178
|
.fw-root .fw-toggle.fw-open svg { transform: rotate(90deg); }
|
|
178
179
|
.fw-root .fw-toggle:hover { background: var(--fw-hover); color: var(--fw-text); }
|
|
179
|
-
.fw-root .fw-icon-tile { flex: 0 0 20px; display: grid; place-items: center; width: 20px; height: 20px; border-radius:
|
|
180
|
+
.fw-root .fw-icon-tile { flex: 0 0 20px; display: grid; place-items: center; width: 20px; height: 20px; border-radius: var(--radius-sm); border: 1px solid var(--fw-border); background: var(--fw-surface); color: var(--line, var(--fw-muted)); }
|
|
180
181
|
.fw-root .fw-icon-tile :is(svg, img) { width: 13px; height: 13px; }
|
|
181
182
|
.fw-root .fw-row .fw-names { font-size: 14px; }
|
|
182
183
|
.fw-root .fw-names { display: flex; align-items: baseline; gap: 8px; min-width: 0; overflow: hidden; white-space: nowrap; }
|
|
@@ -186,7 +187,7 @@
|
|
|
186
187
|
.fw-root .fw-detail { color: var(--fw-muted); overflow: hidden; min-width: 0; display: inline-flex; align-items: center; gap: 4px; max-width: 100%; }
|
|
187
188
|
.fw-root .fw-detail-icon { display: inline-flex; }
|
|
188
189
|
.fw-root .fw-detail-icon :is(svg, img) { width: 12px; height: 12px; }
|
|
189
|
-
.fw-root .fw-count { flex-shrink: 0; height: 18px; padding: 0 8px; border-radius:
|
|
190
|
+
.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; }
|
|
190
191
|
.fw-root .fw-alert { display: inline-flex; color: var(--fw-failed); flex-shrink: 0; }
|
|
191
192
|
.fw-root .fw-alert svg { width: 13px; height: 13px; }
|
|
192
193
|
.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; }
|
|
@@ -195,9 +196,11 @@
|
|
|
195
196
|
.fw-root .fw-view-agents .fw-label { padding-left: 16px; gap: 12px; border-right: 0; }
|
|
196
197
|
.fw-root .fw-view-agents .fw-names { flex-direction: column; align-items: flex-start; gap: 0; }
|
|
197
198
|
.fw-root .fw-view-agents .fw-span-name { font-weight: 600; line-height: 18px; }
|
|
198
|
-
/* One 16 px line that wraps its last
|
|
199
|
-
.fw-root .fw-view-agents .fw-subtitle { display: flex;
|
|
199
|
+
/* One 16 px line that wraps its last parts out of sight: when the line is full a count drops whole, never "48 …". */
|
|
200
|
+
.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; }
|
|
200
201
|
.fw-root .fw-view-agents .fw-subtitle-extra { flex: none; white-space: nowrap; }
|
|
202
|
+
/* Only the tail wraps, so the first part is always on its line and shrinks with an ellipsis; later parts wrap out of sight. */
|
|
203
|
+
.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; }
|
|
201
204
|
.fw-root .fw-view-agents .fw-subtitle-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
202
205
|
.fw-root .fw-view-agents .fw-detail-icon { flex: none; width: 16px; height: 16px; align-items: center; justify-content: center; overflow: hidden; }
|
|
203
206
|
.fw-root .fw-view-agents .fw-detail { font-size: 12px; line-height: 16px; }
|
|
@@ -215,6 +218,7 @@
|
|
|
215
218
|
left: var(--x0);
|
|
216
219
|
opacity: var(--on);
|
|
217
220
|
}
|
|
221
|
+
.fw-root :is(.fw-bar, .fw-seg, .fw-mark) { opacity: calc(var(--on) * var(--fw-recede, 1)); transition: opacity 160ms var(--fw-ease); }
|
|
218
222
|
.fw-root .fw-enter > [role="gridcell"] { animation: fw-unfold 220ms var(--fw-ease) var(--fw-enter-delay) both; }
|
|
219
223
|
.fw-root .fw-enter :is(.fw-bar, .fw-seg) { animation: fw-grow 420ms var(--fw-ease) var(--fw-enter-delay) both; }
|
|
220
224
|
.fw-root .fw-enter .fw-end { animation: fw-land 220ms var(--fw-ease) calc(var(--fw-enter-delay) + 300ms) both; }
|
|
@@ -229,9 +233,9 @@
|
|
|
229
233
|
.fw-root .fw-row[data-search="match"] .fw-name-text { font-weight: 600; }
|
|
230
234
|
/* A restart: an amber notch through the bar at that moment; the bar keeps its own state. */
|
|
231
235
|
.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);
|
|
232
|
-
border-radius: 2px; background: var(--fw-amber); box-shadow: 0 0 0 1px var(--fw-surface);
|
|
236
|
+
border-radius: 2px; background: var(--fw-amber); box-shadow: 0 0 0 1px var(--fw-surface); pointer-events: auto; }
|
|
233
237
|
/* Centered by top, not a negative margin, so measured spacing stays on the grid. */
|
|
234
|
-
.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:
|
|
238
|
+
.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; }
|
|
235
239
|
.fw-root .fw-bar { background: var(--fill); box-shadow: inset 0 0 0 1px var(--line); }
|
|
236
240
|
/* The mark under the pointer lifts: a brighter fill and a ring in its own tone. */
|
|
237
241
|
.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; }
|
|
@@ -272,13 +276,12 @@
|
|
|
272
276
|
.fw-root .fw-kinds .fw-swatch-tool { width: 12px; height: 8px; border-radius: 2px; background: var(--fw-kind-tool); }
|
|
273
277
|
.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); }
|
|
274
278
|
.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; }
|
|
275
|
-
.fw-root .fw-kinds .fw-swatch-failed { width: 12px; height: 8px; border-radius: 2px; box-shadow: inset 0 0 0 1px var(--fw-failed); }
|
|
276
279
|
.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; }
|
|
277
280
|
.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); }
|
|
278
281
|
.fw-root .fw-legend b { color: var(--fw-text); font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
279
282
|
.fw-root .fw-legend .fw-swatch-step { display: inline-grid; place-items: center; width: 12px; height: 12px; border-radius: 3px; background: var(--fw-well); color: var(--fw-failed); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-failed) 50%, transparent); }
|
|
280
283
|
.fw-root .fw-legend .fw-swatch-step svg { width: 8px; height: 8px; }
|
|
281
|
-
.fw-root .fw-bar.fw-approx { opacity: calc(var(--on) * .7); }
|
|
284
|
+
.fw-root .fw-bar.fw-approx { opacity: calc(var(--on) * .7 * var(--fw-recede, 1)); }
|
|
282
285
|
.fw-root .fw-seg { width: max(calc(var(--w) - 2px), calc(var(--on) * 3px)); pointer-events: none; }
|
|
283
286
|
.fw-root .fw-view-agents .fw-seg { pointer-events: auto; }
|
|
284
287
|
.fw-root .fw-seg-model { background: var(--fill); box-shadow: inset 0 0 0 1px var(--line); }
|
|
@@ -287,8 +290,14 @@
|
|
|
287
290
|
agent's own hue (the rules above), so each agent stays one color. */
|
|
288
291
|
.fw-root:is(.fw-fill-muted, .fw-fill-outcome) .fw-view-agents .fw-seg-model { background: var(--fw-kind-model); box-shadow: none; }
|
|
289
292
|
.fw-root:is(.fw-fill-muted, .fw-fill-outcome) .fw-view-agents .fw-seg-tool { background: var(--fw-kind-tool); box-shadow: none; }
|
|
293
|
+
/* Per-agent hues at full saturation (blind round 26: muted per-agent bars "blur together", "wash out on white"): kind is a darker or
|
|
294
|
+
lighter step of the agent's own saturated hue, every solid segment at least 3:1 against the card; waiting stays the only pale fill. */
|
|
295
|
+
.fw-root.fw-fill-agent .fw-view-agents .fw-seg-model { background: light-dark(color-mix(in oklab, var(--tone) 75%, var(--fw-text)), var(--tone)); box-shadow: none; }
|
|
296
|
+
.fw-root.fw-fill-agent .fw-view-agents .fw-seg-tool { background: light-dark(var(--tone), color-mix(in oklab, var(--tone) 72%, var(--fw-text))); box-shadow: none; }
|
|
297
|
+
/* A failed turn is still the agent's work: it keeps the agent's saturated hue, and the red cross on the ending turn says it failed. */
|
|
298
|
+
.fw-root.fw-fill-agent .fw-view-agents .fw-seg-failed { background: light-dark(var(--tone), color-mix(in oklab, var(--tone) 72%, var(--fw-text))); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-failed) 55%, transparent); }
|
|
290
299
|
.fw-root .fw-view-agents .fw-seg-tool { container-type: inline-size; justify-content: flex-start; }
|
|
291
|
-
.fw-root .fw-seg-name { padding: 0 6px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size:
|
|
300
|
+
.fw-root .fw-seg-name { padding: 0 6px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 12px; line-height: 16px; font-weight: 600; color: light-dark(var(--fw-surface), hsl(var(--hsl-background))); pointer-events: none; }
|
|
292
301
|
@container (max-width: 56px) { .fw-root .fw-seg-name { display: none; } }
|
|
293
302
|
.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); }
|
|
294
303
|
.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); }
|
|
@@ -296,14 +305,15 @@
|
|
|
296
305
|
.fw-root .fw-view-agents .fw-seg-failed { background: var(--dim); box-shadow: inset 0 0 0 1px var(--dim-line); }
|
|
297
306
|
.fw-root .fw-glyph { display: inline-flex; color: currentColor; }
|
|
298
307
|
.fw-root .fw-glyph svg { width: 11px; height: 11px; stroke-width: 2; }
|
|
299
|
-
.fw-root .fw-seg-model .fw-glyph, .fw-root .fw-k-model .fw-glyph { color: light-dark(var(--tone),
|
|
308
|
+
.fw-root .fw-seg-model .fw-glyph, .fw-root .fw-k-model .fw-glyph { color: light-dark(var(--tone), var(--fw-text)); }
|
|
300
309
|
/* End labels sit on the surface, so connectors and event lines stop at them, and never run past the lane: a label that would
|
|
301
310
|
be cut at the right edge slides back over its bar's tail instead. */
|
|
302
311
|
.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);
|
|
303
|
-
left: min(calc(var(--x0) + max(var(--w), 3px) + 4px), calc(100% - var(--fw-end-w, 0px) - 4px)); padding: 0 4px; border-radius:
|
|
312
|
+
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);
|
|
313
|
+
background: linear-gradient(var(--fw-tint, transparent) 0 0), var(--fw-surface); white-space: nowrap; pointer-events: none; }
|
|
304
314
|
.fw-root .fw-duration { font-size: 12px; color: var(--fw-muted); font-variant-numeric: tabular-nums; }
|
|
305
315
|
.fw-root .fw-k-failed ~ .fw-end .fw-duration { color: var(--fw-failed); }
|
|
306
|
-
.fw-root .fw-badge { height: 18px; padding: 0 8px; border-radius:
|
|
316
|
+
.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); }
|
|
307
317
|
|
|
308
318
|
/* Handoff elbows: 1.5 px stroke, 6 px radius, a 5 px dot where the handoff leaves its sender. */
|
|
309
319
|
/* Under the rows (which follow it), so end labels and badges always sit above the connectors. */
|
|
@@ -332,39 +342,41 @@
|
|
|
332
342
|
/* The playhead: a round knob on a thin neutral track, visible at rest without outweighing the chart. */
|
|
333
343
|
.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); }
|
|
334
344
|
.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); }
|
|
335
|
-
.fw-root .fw-card { position: absolute; z-index: 20; width: 320px; padding: 12px; border-radius:
|
|
336
|
-
box-shadow:
|
|
345
|
+
.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);
|
|
346
|
+
box-shadow: var(--shadow-dropdown); animation: fw-arrive 140ms var(--fw-ease); }
|
|
337
347
|
/* A hover card never covers the next mark: only a pinned card (a dialog) takes the pointer. */
|
|
338
348
|
.fw-root .fw-card[role="tooltip"] { pointer-events: none; }
|
|
339
349
|
.fw-root .fw-card-above .fw-card { transform: translateY(-100%); animation-name: fw-arrive-above; }
|
|
340
350
|
.fw-root .fw-card-agent { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 12px; font-weight: 600; }
|
|
341
351
|
.fw-root .fw-card-agent .fw-agent-dot { align-self: center; }
|
|
342
|
-
.fw-root .fw-card-head { display: flex; align-items:
|
|
343
|
-
.fw-root .fw-card-head .fw-icon-tile { flex-basis: 28px; width: 28px; height: 28px; border-radius:
|
|
352
|
+
.fw-root .fw-card-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
|
|
353
|
+
.fw-root .fw-card-head .fw-icon-tile { flex-basis: 28px; width: 28px; height: 28px; border-radius: var(--radius-md); }
|
|
344
354
|
.fw-root .fw-card-head .fw-icon-tile :is(svg, img) { width: 16px; height: 16px; }
|
|
345
|
-
.fw-root .fw-card-
|
|
346
|
-
.fw-root .fw-
|
|
347
|
-
.fw-root .fw-muted { color: var(--fw-muted); }
|
|
348
|
-
.fw-root .fw-pill { flex-shrink: 0; height: 20px; padding: 0 8px; border-radius: 999px; font-size: 12px; line-height: 20px; font-weight: 600; }
|
|
355
|
+
.fw-root .fw-card-name { flex: 1; min-width: 0; font-size: 14px; line-height: 20px; font-weight: 600; overflow-wrap: anywhere; }
|
|
356
|
+
.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; }
|
|
349
357
|
.fw-root .fw-pill-done { color: var(--fw-ok); background: color-mix(in oklab, var(--fw-ok) 13%, transparent); }
|
|
350
358
|
.fw-root .fw-pill-running { color: var(--fw-accent); background: color-mix(in oklab, var(--fw-accent) 13%, transparent); }
|
|
351
359
|
.fw-root .fw-pill-failed { color: var(--fw-failed); background: color-mix(in oklab, var(--fw-failed) 13%, transparent); }
|
|
352
|
-
.fw-root .fw-icon-button { appearance: none; display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 0; border-radius:
|
|
360
|
+
.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; }
|
|
353
361
|
.fw-root .fw-icon-button:hover:not(:disabled) { background: var(--fw-hover); color: var(--fw-text); }
|
|
354
|
-
.fw-root .fw-card-
|
|
355
|
-
|
|
356
|
-
.fw-root .fw-card-
|
|
357
|
-
.fw-root .fw-card-
|
|
358
|
-
.fw-root .fw-
|
|
362
|
+
.fw-root .fw-card-close { width: 20px; height: 20px; margin-right: -4px; }
|
|
363
|
+
/* The card's table: label and value on one line each, every value with its unit; a tool breakdown sits under its total. */
|
|
364
|
+
.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; }
|
|
365
|
+
.fw-root .fw-card-table > div { display: contents; }
|
|
366
|
+
.fw-root .fw-card-table dt, .fw-root .fw-card-label { font-size: 12px; line-height: 16px; color: var(--fw-muted); }
|
|
367
|
+
.fw-root .fw-card-table dd { min-width: 0; font-weight: 500; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
|
|
368
|
+
/* A value of several parts wraps between parts, never inside one ("4.0M" / "cached"). */
|
|
369
|
+
.fw-root .fw-card-parts { display: flex; flex-wrap: wrap; column-gap: 4px; }
|
|
370
|
+
.fw-root .fw-card-parts > * { white-space: nowrap; }
|
|
371
|
+
.fw-root .fw-card-parts > * + *::before { content: "· "; color: var(--fw-muted); }
|
|
372
|
+
.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; }
|
|
373
|
+
.fw-root .fw-card-tools > span { display: contents; }
|
|
374
|
+
.fw-root .fw-card-tools > span > :first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
375
|
+
.fw-root .fw-card-tools > span > :last-child { text-align: right; }
|
|
359
376
|
.fw-root .fw-card-text { margin-top: 12px; }
|
|
360
|
-
.fw-root .fw-card-compact {
|
|
361
|
-
.fw-root .fw-card-compact .fw-card-agent { margin-bottom: 8px; font-size: 14px; line-height: 20px; }
|
|
377
|
+
.fw-root .fw-card-compact .fw-card-agent { font-size: 14px; line-height: 20px; }
|
|
362
378
|
.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; }
|
|
363
|
-
.fw-root .fw-card-detail { margin: -4px 0 8px 16px; color: var(--fw-muted); font-size: 12px; line-height: 16px; overflow-wrap: anywhere; }
|
|
364
379
|
.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); }
|
|
365
|
-
.fw-root .fw-card-cells { display: grid; grid-template-columns: repeat(auto-fit, minmax(72px, 1fr)); gap: 8px; padding: 8px 12px; border-radius: 8px; background: var(--fw-well); }
|
|
366
|
-
.fw-root .fw-card-cells dt { font-size: 12px; line-height: 16px; color: var(--fw-muted); }
|
|
367
|
-
.fw-root .fw-card-cells dd { font-size: 14px; line-height: 20px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
368
380
|
.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; }
|
|
369
381
|
.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; }
|
|
370
382
|
.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; }
|
|
@@ -374,9 +386,11 @@
|
|
|
374
386
|
.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); }
|
|
375
387
|
.fw-root .fw-round-button:hover:not(:disabled) { background: var(--fw-hover); }
|
|
376
388
|
.fw-root .fw-round-button:active:not(:disabled) { transform: scale(.94); }
|
|
377
|
-
.fw-root .fw-speed
|
|
389
|
+
.fw-root .fw-speed { position: relative; display: inline-flex; }
|
|
390
|
+
.fw-root .fw-speed::after { content: ""; position: absolute; top: 50%; right: 11px; width: 5px; height: 5px; border: solid var(--fw-muted); border-width: 0 1.5px 1.5px 0; transform: translateY(-70%) rotate(45deg); pointer-events: none; }
|
|
391
|
+
.fw-root .fw-speed select { appearance: none; height: 28px; padding: 0 28px 0 12px; border-radius: var(--radius-md); border: 1px solid var(--fw-border); background: var(--fw-well); font-size: 14px; font-variant-numeric: tabular-nums; cursor: pointer; }
|
|
378
392
|
.fw-root .fw-overview { position: relative; flex: 1 1 160px; min-width: 120px; height: 28px; }
|
|
379
|
-
.fw-root .fw-overview-marks { position: absolute; left: 0; right: 0; top: 8px; height: 12px; border-radius:
|
|
393
|
+
.fw-root .fw-overview-marks { position: absolute; left: 0; right: 0; top: 8px; height: 12px; border-radius: var(--radius-tag); background: var(--fw-well); overflow: hidden; box-shadow: inset 0 0 0 1px var(--fw-border); }
|
|
380
394
|
.fw-root .fw-ov { position: absolute; top: 2px; bottom: 2px; border-radius: 1px; }
|
|
381
395
|
.fw-root .fw-ov.fw-seg-model, .fw-root .fw-ov.fw-seg-tool { background: var(--line); opacity: .75; }
|
|
382
396
|
.fw-root .fw-ov.fw-seg-tool { opacity: .45; }
|
|
@@ -434,9 +448,9 @@
|
|
|
434
448
|
/* The agent view keeps scrubbing on a hairline track; the lanes above already show the activity. */
|
|
435
449
|
.fw-root-agents .fw-overview-marks { top: 13px; height: 2px; box-shadow: none; }
|
|
436
450
|
.fw-root-agents .fw-overview-marks > * { display: none; }
|
|
437
|
-
.fw-root .fw-segmented { display: inline-flex; padding: 4px; border-radius:
|
|
438
|
-
.fw-root .fw-segmented button { appearance: none; border: 0; background: transparent; height: 26px; min-width: 36px; padding: 0 12px; border-radius:
|
|
439
|
-
.fw-root .fw-segmented button[aria-checked="true"] { background: var(--fw-surface); color: var(--fw-text); box-shadow:
|
|
451
|
+
.fw-root .fw-segmented { display: inline-flex; padding: 4px; border-radius: var(--radius-md); background: var(--fw-well); border: 1px solid var(--fw-border); }
|
|
452
|
+
.fw-root .fw-segmented button { appearance: none; border: 0; background: transparent; height: 26px; min-width: 36px; padding: 0 12px; border-radius: var(--radius-sm); 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); }
|
|
453
|
+
.fw-root .fw-segmented button[aria-checked="true"] { background: var(--fw-surface); color: var(--fw-text); box-shadow: var(--shadow-card), 0 0 0 1px var(--fw-border); }
|
|
440
454
|
|
|
441
455
|
/* Below 900 px the agent's name takes its own line above a full-width lane: nothing truncates and the bar keeps the width.
|
|
442
456
|
(At 768 px a 28% column cut 15 of 17 names on a real 17-agent run.) */
|
|
@@ -450,5 +464,14 @@
|
|
|
450
464
|
/* Elbows pass behind the name, not through its letters. A transform or layout containment would make each row
|
|
451
465
|
its own stacking context and trap the name under the connector layer, so stacked rows place by top instead. */
|
|
452
466
|
.fw-root-agents .fw-view-agents .fw-row { transform: none; top: var(--row-y); contain: style; }
|
|
453
|
-
|
|
467
|
+
/* The name row is a strip across the lane on the row's own plane, so gridlines, pauses and connectors show only in the bar row. */
|
|
468
|
+
.fw-root-agents .fw-view-agents .fw-label { position: absolute; z-index: 3; top: 0; left: 0; right: 0; height: 20px; padding: 0 calc(var(--fw-end-w, 0px) + 12px) 0 var(--fw-pad); gap: 8px; align-items: center; pointer-events: none; overflow: hidden;
|
|
469
|
+
background: linear-gradient(var(--fw-tint, transparent) 0 0), var(--fw-surface); }
|
|
470
|
+
/* The duration and its badges take the strip's right end, past the name, so they never sit on a bar or across a connector. */
|
|
471
|
+
.fw-root-agents .fw-view-agents .fw-end { z-index: 4; top: 0; height: 20px; left: auto; right: 4px; }
|
|
472
|
+
.fw-root-agents .fw-view-agents .fw-mark { top: calc(50% + var(--fw-bar-shift) - var(--fw-bar-h) / 2 - 2px); height: calc(var(--fw-bar-h) + 4px); }
|
|
473
|
+
}
|
|
474
|
+
/* On a phone the name row holds the name and the duration; the outcome is the dot's color and the counts are the card's. */
|
|
475
|
+
@container (max-width: 479px) {
|
|
476
|
+
.fw-root-agents .fw-view-agents .fw-subtitle { display: none; }
|
|
454
477
|
}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -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-
|
|
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";
|
|
2
2
|
import { CSSProperties, ReactElement, ReactNode } from "react";
|
|
3
3
|
//#region src/react/sparkline.d.ts
|
|
4
4
|
type SparklineDirection = "rising" | "falling" | "flat";
|