@tangle-network/charts 0.18.0 → 0.19.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. 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.
|
|
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 cells to its card. 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
|
|
|
@@ -334,6 +334,11 @@ interface FlowSpan {
|
|
|
334
334
|
handoffTo?: string;
|
|
335
335
|
/** The host's verdict on an agent span, such as shipped or crashed: colors its lane and joins the legend. */
|
|
336
336
|
outcome?: FlowOutcome;
|
|
337
|
+
/** Up to three host facts shown as extra key/value cells in the span card, such as releases or commits. */
|
|
338
|
+
facts?: readonly {
|
|
339
|
+
label: string;
|
|
340
|
+
value: string;
|
|
341
|
+
}[];
|
|
337
342
|
meta?: Record<string, unknown>;
|
|
338
343
|
}
|
|
339
344
|
/** A host verdict: `label` is the legend text, `tone` the status color. */
|
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-D6RDkVrJ.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. */
|
|
@@ -128,7 +128,7 @@
|
|
|
128
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); }
|
|
129
129
|
|
|
130
130
|
/* Rows */
|
|
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; }
|
|
131
|
+
.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; }
|
|
132
132
|
.fw-root .fw-row:hover { background: var(--fw-hover); }
|
|
133
133
|
.fw-root .fw-row.fw-active { background: color-mix(in oklab, var(--fw-accent) 7%, transparent); }
|
|
134
134
|
.fw-root .fw-row[aria-selected="true"] { background: color-mix(in oklab, var(--fw-accent) 11%, transparent); }
|
|
@@ -160,6 +160,7 @@
|
|
|
160
160
|
.fw-root .fw-view-agents .fw-label { padding-left: 16px; gap: 12px; border-right: 0; }
|
|
161
161
|
.fw-root .fw-view-agents .fw-names { flex-direction: column; align-items: flex-start; gap: 0; }
|
|
162
162
|
.fw-root .fw-view-agents .fw-span-name { font-weight: 600; line-height: 18px; }
|
|
163
|
+
.fw-root .fw-view-agents .fw-subtitle { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--fw-muted); font-size: 12px; line-height: 16px; }
|
|
163
164
|
.fw-root .fw-view-agents .fw-detail { font-size: 12px; line-height: 16px; }
|
|
164
165
|
.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); }
|
|
165
166
|
|
|
@@ -198,6 +199,8 @@
|
|
|
198
199
|
.fw-root .fw-legend li { display: inline-flex; align-items: center; gap: 8px; }
|
|
199
200
|
.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
201
|
.fw-root .fw-legend b { color: var(--fw-text); font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
202
|
+
.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); }
|
|
203
|
+
.fw-root .fw-legend .fw-swatch-step svg { width: 8px; height: 8px; }
|
|
201
204
|
.fw-root .fw-bar.fw-approx { opacity: calc(var(--on) * .7); }
|
|
202
205
|
.fw-root .fw-seg { width: max(calc(var(--w) - 2px), calc(var(--on) * 3px)); pointer-events: none; }
|
|
203
206
|
.fw-root .fw-view-agents .fw-seg { pointer-events: auto; }
|
|
@@ -221,6 +224,9 @@
|
|
|
221
224
|
opacity: clamp(0, (var(--fw-t) - var(--s)) * 1000, 1);
|
|
222
225
|
}
|
|
223
226
|
.fw-root .fw-view-spans .fw-link { opacity: calc(clamp(0, (var(--fw-t) - var(--s)) * 1000, 1) * .7); }
|
|
227
|
+
/* Agent-view connectors stay quiet at 30% until their agent is hovered, pinned or focused. */
|
|
228
|
+
.fw-root .fw-view-agents .fw-link:not(.fw-link-hot) { opacity: calc(clamp(0, (var(--fw-t) - var(--s)) * 1000, 1) * .3); }
|
|
229
|
+
.fw-root .fw-link { transition: opacity 160ms var(--fw-ease); }
|
|
224
230
|
.fw-root .fw-link-down { left: calc(var(--x) - 8px); border-left-width: 1.5px; border-bottom-left-radius: 6px; }
|
|
225
231
|
.fw-root .fw-link-up { left: var(--x); border-right-width: 1.5px; border-bottom-right-radius: 6px; }
|
|
226
232
|
.fw-root .fw-link::before { content: ""; position: absolute; top: -3.5px; width: 5px; height: 5px; border-radius: 50%; background: var(--line); box-shadow: 0 0 0 1.5px var(--fw-surface); }
|
|
@@ -231,6 +237,8 @@
|
|
|
231
237
|
.fw-root .fw-card-anchor { display: contents; }
|
|
232
238
|
.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);
|
|
233
239
|
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); }
|
|
240
|
+
/* A hover card never covers the next mark: only a pinned card (a dialog) takes the pointer. */
|
|
241
|
+
.fw-root .fw-card[role="tooltip"] { pointer-events: none; }
|
|
234
242
|
.fw-root .fw-card-above .fw-card { transform: translateY(-100%); animation-name: fw-arrive-above; }
|
|
235
243
|
.fw-root .fw-card-agent { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 12px; font-weight: 600; }
|
|
236
244
|
.fw-root .fw-card-agent .fw-agent-dot { margin-top: 0; align-self: center; }
|
|
@@ -255,7 +263,9 @@
|
|
|
255
263
|
.fw-root .fw-card-compact { width: 280px; padding: 12px; }
|
|
256
264
|
.fw-root .fw-card-compact .fw-card-agent { margin-bottom: 8px; font-size: 14px; line-height: 20px; }
|
|
257
265
|
.fw-root .fw-card-who { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
258
|
-
.fw-root .fw-card-
|
|
266
|
+
.fw-root .fw-card-detail { margin: -4px 0 8px 16px; color: var(--fw-muted); font-size: 12px; line-height: 16px; overflow-wrap: anywhere; }
|
|
267
|
+
.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); }
|
|
268
|
+
.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); }
|
|
259
269
|
.fw-root .fw-card-cells dt { font-size: 12px; line-height: 16px; color: var(--fw-muted); }
|
|
260
270
|
.fw-root .fw-card-cells dd { font-size: 14px; line-height: 20px; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
261
271
|
.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; }
|
|
@@ -327,12 +337,12 @@
|
|
|
327
337
|
@container (max-width: 899px) {
|
|
328
338
|
/* The root is the queried container, so the variables go on its children. */
|
|
329
339
|
.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
340
|
.fw-root-agents .fw-view-agents .fw-names { flex-direction: row; align-items: baseline; gap: 8px; min-width: 0; }
|
|
332
341
|
.fw-root-agents .fw-view-agents .fw-agent-dot { align-self: center; margin-top: 0; }
|
|
333
342
|
/* The label leaves the grid flow, so the lane must claim the track column itself. */
|
|
334
343
|
.fw-root-agents .fw-view-agents .fw-lane { grid-column: 2; }
|
|
335
|
-
/* Elbows pass behind the name, not through its letters.
|
|
336
|
-
|
|
337
|
-
.fw-root-agents .fw-view-agents .fw-row:
|
|
344
|
+
/* Elbows pass behind the name, not through its letters. A transform or layout containment would make each row
|
|
345
|
+
its own stacking context and trap the name under the connector layer, so stacked rows place by top instead. */
|
|
346
|
+
.fw-root-agents .fw-view-agents .fw-row { transform: none; top: var(--row-y); contain: style; }
|
|
347
|
+
.fw-root-agents .fw-view-agents .fw-label { position: absolute; z-index: 3; top: 4px; left: 0; height: 18px; max-width: calc(100% - var(--fw-pad)); padding: 0 8px 0 var(--fw-pad); gap: 8px; align-items: center; pointer-events: none; overflow: hidden; border-radius: 0 4px 4px 0; background: var(--fw-surface); }
|
|
338
348
|
}
|
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-D6RDkVrJ.js";
|
|
2
2
|
import { CSSProperties, ReactElement, ReactNode } from "react";
|
|
3
3
|
//#region src/react/sparkline.d.ts
|
|
4
4
|
type SparklineDirection = "rising" | "falling" | "flat";
|
package/dist/react/index.js
CHANGED
|
@@ -2663,16 +2663,22 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
|
|
|
2663
2663
|
const duration = Number.isFinite(end - span.startMs) && end >= span.startMs ? formatSpanDuration(end - span.startMs) : "—";
|
|
2664
2664
|
if (compact) {
|
|
2665
2665
|
const who = owner ?? row;
|
|
2666
|
+
const outcome = who.span.outcome;
|
|
2666
2667
|
const tokenCount = span.promptTokens !== void 0 || span.completionTokens !== void 0 ? (span.promptTokens ?? 0) + (span.completionTokens ?? 0) : void 0;
|
|
2667
2668
|
const cells = [[span.kind === "model" ? "Thinking" : KIND_LABEL[span.kind], duration]];
|
|
2669
|
+
for (const fact of (span.facts ?? who.span.facts ?? []).slice(0, 3)) cells.push([fact.label, fact.value]);
|
|
2670
|
+
cells.push(["Started", `+${formatSpanDuration(Math.max(0, span.startMs))}`]);
|
|
2668
2671
|
if (tokenCount !== void 0) cells.push(["Tokens", count(tokenCount)]);
|
|
2669
2672
|
if (span.costUsd !== void 0) cells.push(["Cost", formatCost(span.costUsd)]);
|
|
2673
|
+
cells.splice(6);
|
|
2674
|
+
const sentence = [span.output, span.id === who.id ? void 0 : span.name].find((text) => text && text !== who.name);
|
|
2670
2675
|
return /* @__PURE__ */ jsxs("div", {
|
|
2671
2676
|
id,
|
|
2672
2677
|
role: pinned ? "dialog" : "tooltip",
|
|
2673
2678
|
"aria-label": pinned ? `${span.name} details` : void 0,
|
|
2674
2679
|
className: "fw-card fw-card-compact",
|
|
2675
2680
|
"data-tone": toneKey(who.tone),
|
|
2681
|
+
"data-outcome": outcome?.tone,
|
|
2676
2682
|
style: {
|
|
2677
2683
|
left,
|
|
2678
2684
|
top
|
|
@@ -2695,7 +2701,12 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
|
|
|
2695
2701
|
className: "fw-card-who",
|
|
2696
2702
|
children: who.name
|
|
2697
2703
|
}),
|
|
2698
|
-
/* @__PURE__ */ jsx("span", {
|
|
2704
|
+
outcome ? /* @__PURE__ */ jsx("span", {
|
|
2705
|
+
className: "fw-pill fw-pill-outcome",
|
|
2706
|
+
"data-tone": "0",
|
|
2707
|
+
"data-outcome": outcome.tone,
|
|
2708
|
+
children: outcome.label
|
|
2709
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
2699
2710
|
className: `fw-pill fw-pill-${status.toLowerCase()}`,
|
|
2700
2711
|
children: status
|
|
2701
2712
|
}),
|
|
@@ -2713,13 +2724,17 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
|
|
|
2713
2724
|
})
|
|
2714
2725
|
]
|
|
2715
2726
|
}),
|
|
2727
|
+
who.span.detail && /* @__PURE__ */ jsx("p", {
|
|
2728
|
+
className: "fw-card-detail",
|
|
2729
|
+
children: who.span.detail
|
|
2730
|
+
}),
|
|
2716
2731
|
/* @__PURE__ */ jsx("dl", {
|
|
2717
2732
|
className: "fw-card-cells",
|
|
2718
2733
|
children: cells.map(([name, value]) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: name }), /* @__PURE__ */ jsx("dd", { children: value })] }, name))
|
|
2719
2734
|
}),
|
|
2720
|
-
/* @__PURE__ */ jsx("p", {
|
|
2735
|
+
sentence && /* @__PURE__ */ jsx("p", {
|
|
2721
2736
|
className: "fw-card-result",
|
|
2722
|
-
children:
|
|
2737
|
+
children: sentence
|
|
2723
2738
|
})
|
|
2724
2739
|
]
|
|
2725
2740
|
});
|
|
@@ -2876,7 +2891,7 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
2876
2891
|
"data-tone": toneKey(row.tone),
|
|
2877
2892
|
"data-outcome": span.outcome?.tone,
|
|
2878
2893
|
className: `fw-row${active ? " fw-active" : ""}${sticky ? " fw-sticky-row" : ""}${isAgent ? " fw-agent-row" : ""}`,
|
|
2879
|
-
style: {
|
|
2894
|
+
style: { "--row-y": `${top}px` },
|
|
2880
2895
|
onFocus: (event) => {
|
|
2881
2896
|
if (event.target === event.currentTarget) props.onActivate(row);
|
|
2882
2897
|
},
|
|
@@ -2918,21 +2933,28 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
2918
2933
|
/* @__PURE__ */ jsxs("span", {
|
|
2919
2934
|
className: "fw-names",
|
|
2920
2935
|
title: `${row.name}${span.detail ? ` · ${span.detail}` : ""} · ${KIND_LABEL[span.kind]}${running ? " · running" : ""}${row.ok ? "" : " · failed"}`,
|
|
2921
|
-
children: [
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
"
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
+
children: [
|
|
2937
|
+
/* @__PURE__ */ jsxs("span", {
|
|
2938
|
+
className: `fw-span-name${isAgent || row.hasChildren ? " fw-strong" : ""}`,
|
|
2939
|
+
children: [running && /* @__PURE__ */ jsx("span", {
|
|
2940
|
+
className: "fw-spinner",
|
|
2941
|
+
role: "status",
|
|
2942
|
+
"aria-label": "running"
|
|
2943
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2944
|
+
className: "fw-name-text",
|
|
2945
|
+
children: row.name
|
|
2946
|
+
})]
|
|
2947
|
+
}),
|
|
2948
|
+
agentView && span.detail && /* @__PURE__ */ jsx("span", {
|
|
2949
|
+
className: "fw-subtitle",
|
|
2950
|
+
children: span.detail
|
|
2951
|
+
}),
|
|
2952
|
+
agentView && renderIcon && /* @__PURE__ */ jsx("span", {
|
|
2953
|
+
className: "fw-detail-icon",
|
|
2954
|
+
"aria-hidden": true,
|
|
2955
|
+
children: renderIcon(span)
|
|
2956
|
+
})
|
|
2957
|
+
]
|
|
2936
2958
|
}),
|
|
2937
2959
|
folded && /* @__PURE__ */ jsxs("span", {
|
|
2938
2960
|
className: "fw-count",
|
|
@@ -2992,7 +3014,7 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
2992
3014
|
});
|
|
2993
3015
|
}, (a, b) => a.row === b.row && a.view === b.view && a.top === b.top && a.collapsed === b.collapsed && a.searching === b.searching && a.slowest === b.slowest && a.active === b.active && a.focusable === b.focusable && a.selected === b.selected && a.sticky === b.sticky && a.renderIcon === b.renderIcon && (a.clockMs === b.clockMs || !open(a.row, a.clockMs) && !open(b.row, b.clockMs)));
|
|
2994
3016
|
/** An elbow connector between two rows at one moment, drawn in the row's tone; appears when the clock reaches it. */
|
|
2995
|
-
function LinkMark({ link, rowHeight }) {
|
|
3017
|
+
function LinkMark({ link, rowHeight, hot }) {
|
|
2996
3018
|
const upper = Math.min(link.from, link.to), lower = Math.max(link.from, link.to);
|
|
2997
3019
|
const style = {
|
|
2998
3020
|
...at(link.atMs, link.atMs),
|
|
@@ -3000,7 +3022,7 @@ function LinkMark({ link, rowHeight }) {
|
|
|
3000
3022
|
height: (lower - upper) * rowHeight - 5
|
|
3001
3023
|
};
|
|
3002
3024
|
return /* @__PURE__ */ jsx("span", {
|
|
3003
|
-
className: `fw-link fw-link-${link.dir}`,
|
|
3025
|
+
className: `fw-link fw-link-${link.dir}${hot ? " fw-link-hot" : ""}`,
|
|
3004
3026
|
"data-tone": toneKey(link.tone),
|
|
3005
3027
|
style,
|
|
3006
3028
|
"aria-hidden": true
|
|
@@ -3397,7 +3419,8 @@ function FlowBody(props) {
|
|
|
3397
3419
|
"aria-hidden": true,
|
|
3398
3420
|
children: links.filter((link) => Math.max(link.from, link.to) >= first && Math.min(link.from, link.to) <= last).map((link) => /* @__PURE__ */ jsx(LinkMark, {
|
|
3399
3421
|
link,
|
|
3400
|
-
rowHeight
|
|
3422
|
+
rowHeight,
|
|
3423
|
+
hot: props.focusId !== void 0 && (rows[link.from]?.id === props.focusId || rows[link.to]?.id === props.focusId)
|
|
3401
3424
|
}, link.id))
|
|
3402
3425
|
}), rows.slice(first, last).map((item, offset) => row(item, first + offset))]
|
|
3403
3426
|
}), rows.length === 0 && /* @__PURE__ */ jsx("p", {
|
|
@@ -3718,10 +3741,11 @@ function OutcomeLegend({ agents }) {
|
|
|
3718
3741
|
items.set(outcome.label, item);
|
|
3719
3742
|
}
|
|
3720
3743
|
if (items.size === 0) return null;
|
|
3721
|
-
|
|
3744
|
+
const failedSteps = agents.reduce((sum, row) => sum + row.own.reduce((n, mark) => n + (mark.kind === "failed" ? mark.count : 0), 0), 0);
|
|
3745
|
+
return /* @__PURE__ */ jsxs("ul", {
|
|
3722
3746
|
className: "fw-legend",
|
|
3723
3747
|
"aria-label": "Outcomes",
|
|
3724
|
-
children: [...items].map(([label, item]) => /* @__PURE__ */ jsxs("li", { children: [
|
|
3748
|
+
children: [[...items].map(([label, item]) => /* @__PURE__ */ jsxs("li", { children: [
|
|
3725
3749
|
/* @__PURE__ */ jsx("span", {
|
|
3726
3750
|
className: "fw-swatch",
|
|
3727
3751
|
"data-tone": "0",
|
|
@@ -3731,7 +3755,15 @@ function OutcomeLegend({ agents }) {
|
|
|
3731
3755
|
label,
|
|
3732
3756
|
" ",
|
|
3733
3757
|
/* @__PURE__ */ jsx("b", { children: item.count })
|
|
3734
|
-
] }, label))
|
|
3758
|
+
] }, label)), failedSteps > 0 && /* @__PURE__ */ jsxs("li", { children: [
|
|
3759
|
+
/* @__PURE__ */ jsx("span", {
|
|
3760
|
+
className: "fw-swatch-step",
|
|
3761
|
+
"aria-hidden": true,
|
|
3762
|
+
children: /* @__PURE__ */ jsx(FailGlyph, {})
|
|
3763
|
+
}),
|
|
3764
|
+
"Failed step ",
|
|
3765
|
+
/* @__PURE__ */ jsx("b", { children: failedSteps })
|
|
3766
|
+
] })]
|
|
3735
3767
|
});
|
|
3736
3768
|
}
|
|
3737
3769
|
const headlineSeconds = (ms) => ms < 6e4 ? `${(ms / 1e3).toFixed(1)}s` : formatSpanDuration(ms);
|
|
@@ -4160,6 +4192,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4160
4192
|
slowestId: slowest?.id,
|
|
4161
4193
|
activeId,
|
|
4162
4194
|
selectedId: card?.pinned ? card.target : void 0,
|
|
4195
|
+
focusId: cardAgent?.id ?? (view === "agents" ? activeId : void 0),
|
|
4163
4196
|
clockMs,
|
|
4164
4197
|
window: win,
|
|
4165
4198
|
trackPx,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/charts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.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",
|