@tangle-network/charts 0.21.5 → 0.21.7
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/dist/react/flow-waterfall.css +14 -6
- package/dist/react/index.d.ts +4 -4
- package/dist/react/index.js +36 -19
- package/package.json +1 -1
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
font-family: var(--font-sans, Inter, ui-sans-serif, system-ui, sans-serif);
|
|
48
48
|
font-size: 14px;
|
|
49
49
|
line-height: 20px;
|
|
50
|
-
|
|
50
|
+
/* Tabular figures only on numeric cells: on names they widen hyphens ("v6 - battery", "glm - 5.3"). */
|
|
51
|
+
font-feature-settings: "cv11" 1;
|
|
51
52
|
-webkit-font-smoothing: antialiased;
|
|
52
53
|
}
|
|
53
54
|
.fw-root-agents { --fw-bar-h: 24px; --fw-label-col: min(var(--fw-name-w), 28%); border-radius: 16px; }
|
|
@@ -81,7 +82,7 @@
|
|
|
81
82
|
.fw-root .fw-state::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
|
|
82
83
|
.fw-root .fw-state-live { color: var(--fw-accent); background: color-mix(in oklab, var(--fw-accent) 12%, transparent); }
|
|
83
84
|
.fw-root .fw-state-live::before { animation: fw-pulse 1.6s var(--fw-ease) infinite; }
|
|
84
|
-
.fw-root .fw-headline { color: var(--fw-muted);
|
|
85
|
+
.fw-root .fw-headline { color: var(--fw-muted); display: flex; align-items: center; gap: 8px; }
|
|
85
86
|
.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; }
|
|
86
87
|
.fw-root .fw-tools { display: flex; align-items: center; gap: 8px; }
|
|
87
88
|
.fw-root .fw-views { display: inline-flex; padding: 4px; border-radius: 8px; background: var(--fw-well); border: 1px solid var(--fw-border); }
|
|
@@ -135,7 +136,8 @@
|
|
|
135
136
|
background: repeating-linear-gradient(-45deg, color-mix(in oklab, var(--fw-text) 9%, transparent) 0 1px, transparent 1px 6px), color-mix(in oklab, var(--fw-text) 4%, transparent);
|
|
136
137
|
border-inline: 1px dashed color-mix(in oklab, var(--fw-text) 24%, transparent); }
|
|
137
138
|
.fw-root .fw-pause-label b { font-weight: 600; color: var(--fw-text); }
|
|
138
|
-
.fw-root .fw-pause
|
|
139
|
+
.fw-root .fw-pause { overflow: visible; }
|
|
140
|
+
.fw-root .fw-pause-label { position: sticky; top: 4px; display: block; width: max-content; margin: 4px 0 0 50%; translate: -50% 0; padding: 0 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
139
141
|
border-radius: 4px; font-size: 12px; line-height: 20px; color: var(--fw-muted); background: var(--fw-surface); box-shadow: inset 0 0 0 1px var(--fw-border); }
|
|
140
142
|
/* The label alone takes the pointer, for its reason; the band stays click-through to the rows. */
|
|
141
143
|
.fw-root .fw-pause-label[title] { pointer-events: auto; cursor: help; }
|
|
@@ -234,7 +236,11 @@
|
|
|
234
236
|
/* An agent bar with no child marks says who it is by its lane color, never a neutral grey. */
|
|
235
237
|
/* In the agent view the bar's own extent between steps is waiting: hatched in the outcome tone, under the step segments. */
|
|
236
238
|
.fw-root .fw-view-agents .fw-bar.fw-k-pipeline.fw-has-marks { background: repeating-linear-gradient(-45deg, color-mix(in oklab, var(--tone) 28%, transparent) 0 1px, transparent 1px 5px), color-mix(in oklab, var(--tone) 9%, transparent); }
|
|
237
|
-
|
|
239
|
+
/* An agent lane is a tint of its color; full saturation is kept for its model and tool segments and its status dot, so a run never
|
|
240
|
+
reads as a wall of colored slabs. */
|
|
241
|
+
.fw-root .fw-view-agents .fw-bar.fw-k-pipeline:not(.fw-has-marks, .fw-has-agents) { background: color-mix(in oklab, var(--tone) 30%, var(--fw-surface)); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--tone) 55%, transparent); }
|
|
242
|
+
/* A family row without one shared outcome is neutral when bars mean outcomes, not a hue of its own. */
|
|
243
|
+
.fw-root.fw-fill-outcome .fw-view-agents .fw-row[data-span-id^="family:"]:not([data-outcome]), .fw-root.fw-fill-outcome .fw-view-agents .fw-row[data-span-id^="family:"]:not([data-outcome]) [data-tone] { --tone: var(--fw-h0); }
|
|
238
244
|
/* A host outcome re-tones the whole lane: dot, bar and marks. */
|
|
239
245
|
/* Muted agents (the default): one quiet tone for every agent bar, its steps and handoffs; the slowest agent alone takes amber, and
|
|
240
246
|
failure keeps its red glyph, so color only ever means state. */
|
|
@@ -265,7 +271,6 @@
|
|
|
265
271
|
.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); }
|
|
266
272
|
/* In the agent view a failed turn draws like any turn; the agent's own failure shows once, by its name and its end label. */
|
|
267
273
|
.fw-root .fw-view-agents .fw-seg-failed { background: var(--dim); box-shadow: inset 0 0 0 1px var(--dim-line); }
|
|
268
|
-
.fw-root .fw-view-agents .fw-alert { color: var(--fw-muted); }
|
|
269
274
|
.fw-root .fw-glyph { display: inline-flex; color: currentColor; }
|
|
270
275
|
.fw-root .fw-glyph svg { width: 11px; height: 11px; stroke-width: 2; }
|
|
271
276
|
.fw-root .fw-seg-model .fw-glyph, .fw-root .fw-k-model .fw-glyph { color: light-dark(var(--tone), #fff); }
|
|
@@ -304,7 +309,10 @@
|
|
|
304
309
|
.fw-root.fw-root-agents .fw-card, .fw-root.fw-root-agents .fw-card-above .fw-card { position: relative; left: auto !important; top: auto !important; transform: none; animation-name: fw-arrive; width: auto; max-width: 560px; }
|
|
305
310
|
.fw-root .fw-seg-alt { filter: brightness(.78); }
|
|
306
311
|
/* Agent view: the scrubber track shows how far the run has played, so at rest it reads full, not empty. */
|
|
307
|
-
.fw-root-agents .fw-slider { background: linear-gradient(90deg,
|
|
312
|
+
.fw-root.fw-root-agents .fw-slider { background: linear-gradient(90deg, var(--fw-accent) var(--fw-progress), var(--fw-well) var(--fw-progress)) 0 50% / 100% 6px no-repeat; }
|
|
313
|
+
/* The playhead: a round knob on a filled track, visible at rest as the references show it. */
|
|
314
|
+
.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); }
|
|
315
|
+
.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); }
|
|
308
316
|
.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);
|
|
309
317
|
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); }
|
|
310
318
|
/* A hover card never covers the next mark: only a pinned card (a dialog) takes the pointer. */
|
package/dist/react/index.d.ts
CHANGED
|
@@ -399,9 +399,9 @@ interface FlowWaterfallProps {
|
|
|
399
399
|
markers?: FlowMarker[];
|
|
400
400
|
/** The time the reader moved to, in trace ms; `null` when following live or showing the whole trace. */
|
|
401
401
|
onTimeChange?: (t: number | null) => void;
|
|
402
|
-
/** What an agent bar's color means. `
|
|
403
|
-
*
|
|
404
|
-
* bar takes its outcome's color. */
|
|
402
|
+
/** What an agent bar's color means. Default: `outcome` when any agent carries one, so bars match the outcome legend, else `muted`.
|
|
403
|
+
* `muted`: every agent one quiet tone, color spent on state only (red for failure, amber for the slowest). `agent`: each agent its
|
|
404
|
+
* own hue, shared by its steps and handoffs. `outcome`: the bar takes its outcome's color. */
|
|
405
405
|
agentFill?: 'muted' | 'outcome' | 'agent';
|
|
406
406
|
/** One line beside the title saying what the run was for, such as its goal. */
|
|
407
407
|
description?: string;
|
|
@@ -415,6 +415,6 @@ interface FlowWaterfallProps {
|
|
|
415
415
|
}
|
|
416
416
|
/** Shared trace inspector: the agent timeline and the span waterfall over one clock.
|
|
417
417
|
* Data remains producer-owned; playback, zoom and folding change presentation only. */
|
|
418
|
-
export declare function FlowWaterfall({ trace, live, startedAt, label, defaultCollapsed, defaultView, renderIcon, maxHeight, onSelect, markers, onTimeChange, agentFill, selectedId, onDeselect, description, summary: showSummary }: FlowWaterfallProps): import("react").JSX.Element;
|
|
418
|
+
export declare function FlowWaterfall({ trace, live, startedAt, label, defaultCollapsed, defaultView, renderIcon, maxHeight, onSelect, markers, onTimeChange, agentFill: fillProp, selectedId, onDeselect, description, summary: showSummary }: FlowWaterfallProps): import("react").JSX.Element;
|
|
419
419
|
//#endregion
|
|
420
420
|
export type { BandKind, ChartBand, ChartMark, DotMapLabel, DotMapProps, DotMapRect, Figure, FigureAtWidth, FigureViewProps, FlowWaterfallProps, GapBand, GapProps, LineSeries, MarkKind, MarkVerdict, MarkerShape, MeterSegment, PointFields, PointGlyph, Refusal, SparklineDirection, SparklineGeometry, SparklineGeometryOptions, SparklineLabelOptions, SparklinePoint, SparklineProps, SparklineValues, StackedBarBucket, StackedBarChartProps, StackedBarSegment, StackedBarSeries, TickOutcome, TimelineLane, TimelineOptions, TimelineSpan, TimelineTick, TimelineViewProps, WaterfallBarProps, WaterfallHeaderProps, WaterfallRowProps, WaterfallSpanGeometry, WaterfallTimeAxisProps, WaterfallWindow, XCategory, YScale, YTarget, YTick };
|
package/dist/react/index.js
CHANGED
|
@@ -2631,8 +2631,8 @@ function slowestRow(rows, clockMs) {
|
|
|
2631
2631
|
function agentCallouts(agents, clockMs) {
|
|
2632
2632
|
let slowest, slowestMs = -1, costliest, top = -1, priced = 0, timed = 0;
|
|
2633
2633
|
for (const row of agents) {
|
|
2634
|
-
if (row.durationLabel === "—" || row.span.startMs > clockMs) continue;
|
|
2635
|
-
if (row.
|
|
2634
|
+
if (row.durationLabel === "—" || row.span.startMs > clockMs || row.kind !== "pipeline" || row.id.startsWith("family:") || row.id.startsWith("run:")) continue;
|
|
2635
|
+
if (row.parentId !== void 0) {
|
|
2636
2636
|
const ms = (spanRunning(row.span) ? Math.max(row.span.endMs, clockMs) : Math.min(row.span.endMs, clockMs)) - row.span.startMs;
|
|
2637
2637
|
timed += 1;
|
|
2638
2638
|
if (ms > slowestMs) {
|
|
@@ -2755,6 +2755,7 @@ function agentFamilies(agents, open) {
|
|
|
2755
2755
|
const running = members.some((row) => spanRunning(row.span));
|
|
2756
2756
|
const known = members.map((row) => row.span.costUsd ?? row.rollupCostUsd).filter((cost) => cost !== void 0);
|
|
2757
2757
|
const failed = members.filter((row) => !row.ok).length;
|
|
2758
|
+
const outcome = members.every((row) => row.span.outcome && row.span.outcome.tone === members[0].span.outcome?.tone) ? members[0].span.outcome : void 0;
|
|
2758
2759
|
out.push({
|
|
2759
2760
|
costFloor: known.length < members.length || members.some((row) => row.costFloor),
|
|
2760
2761
|
id,
|
|
@@ -2784,6 +2785,7 @@ function agentFamilies(agents, open) {
|
|
|
2784
2785
|
startMs,
|
|
2785
2786
|
endMs,
|
|
2786
2787
|
status: running ? "running" : failed ? "failed" : "done",
|
|
2788
|
+
...outcome ? { outcome } : {},
|
|
2787
2789
|
detail: members[0].span.detail,
|
|
2788
2790
|
meta: {
|
|
2789
2791
|
failedAgents: failed,
|
|
@@ -3442,7 +3444,7 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
3442
3444
|
"aria-label": `${row.descendants} hidden steps`,
|
|
3443
3445
|
children: ["+", row.descendants]
|
|
3444
3446
|
}),
|
|
3445
|
-
!row.ok && /* @__PURE__ */ jsx("span", {
|
|
3447
|
+
!row.ok && !agentView && /* @__PURE__ */ jsx("span", {
|
|
3446
3448
|
className: "fw-alert",
|
|
3447
3449
|
children: /* @__PURE__ */ jsx(AlertGlyph, { label: "failed" })
|
|
3448
3450
|
})
|
|
@@ -3733,6 +3735,7 @@ function FlowBody(props) {
|
|
|
3733
3735
|
const ticks = clockTicks(win.startMs, tickEnd, Math.max(1, trackPx + (view === "spans" ? 72 : 0)));
|
|
3734
3736
|
const markerLabels = useMemo(() => {
|
|
3735
3737
|
const shown = /* @__PURE__ */ new Map();
|
|
3738
|
+
if (view === "agents") return shown;
|
|
3736
3739
|
let freeFrom = -Infinity;
|
|
3737
3740
|
for (const marker of [...props.markers ?? []].sort((a, b) => a.at - b.at)) {
|
|
3738
3741
|
const x = (marker.at - win.startMs) / msPerPx;
|
|
@@ -3748,7 +3751,24 @@ function FlowBody(props) {
|
|
|
3748
3751
|
props.markers,
|
|
3749
3752
|
win.startMs,
|
|
3750
3753
|
msPerPx,
|
|
3751
|
-
trackPx
|
|
3754
|
+
trackPx,
|
|
3755
|
+
view
|
|
3756
|
+
]);
|
|
3757
|
+
const pauseLabels = useMemo(() => {
|
|
3758
|
+
const shown = /* @__PURE__ */ new Set();
|
|
3759
|
+
let freeFrom = -Infinity;
|
|
3760
|
+
for (const pause of [...props.pauses ?? []].sort((a, b) => a.startMs - b.startMs)) {
|
|
3761
|
+
const width = duration(pause.endMs - pause.startMs).length * 7 + 24;
|
|
3762
|
+
const centre = ((pause.startMs + pause.endMs) / 2 - win.startMs) / msPerPx;
|
|
3763
|
+
if (centre - width / 2 < freeFrom) continue;
|
|
3764
|
+
shown.add(pause);
|
|
3765
|
+
freeFrom = centre + width / 2 + 8;
|
|
3766
|
+
}
|
|
3767
|
+
return shown;
|
|
3768
|
+
}, [
|
|
3769
|
+
props.pauses,
|
|
3770
|
+
win.startMs,
|
|
3771
|
+
msPerPx
|
|
3752
3772
|
]);
|
|
3753
3773
|
useEffect(() => {
|
|
3754
3774
|
const element = scroller.current;
|
|
@@ -4026,17 +4046,13 @@ function FlowBody(props) {
|
|
|
4026
4046
|
"--s": pause.startMs,
|
|
4027
4047
|
"--e": pause.endMs
|
|
4028
4048
|
},
|
|
4029
|
-
title: pause.label,
|
|
4030
|
-
children: /* @__PURE__ */
|
|
4049
|
+
title: `${pause.label}, ${duration(pause.endMs - pause.startMs)}`,
|
|
4050
|
+
children: pauseLabels.has(pause) && /* @__PURE__ */ jsx("span", {
|
|
4031
4051
|
className: "fw-pause-label",
|
|
4032
|
-
title: pause.detail,
|
|
4033
|
-
tabIndex:
|
|
4034
|
-
"aria-label": pause.
|
|
4035
|
-
children:
|
|
4036
|
-
/* @__PURE__ */ jsx("b", { children: duration(pause.endMs - pause.startMs) }),
|
|
4037
|
-
" ",
|
|
4038
|
-
pause.label
|
|
4039
|
-
]
|
|
4052
|
+
title: pause.detail ? `${pause.label}: ${pause.detail}` : pause.label,
|
|
4053
|
+
tabIndex: 0,
|
|
4054
|
+
"aria-label": `${pause.label}, ${duration(pause.endMs - pause.startMs)}${pause.detail ? `: ${pause.detail}` : ""}`,
|
|
4055
|
+
children: /* @__PURE__ */ jsx("b", { children: duration(pause.endMs - pause.startMs) })
|
|
4040
4056
|
})
|
|
4041
4057
|
}, `p-${pause.startMs}`)), props.markers?.map((marker) => /* @__PURE__ */ jsx("span", {
|
|
4042
4058
|
className: "fw-marker",
|
|
@@ -4318,6 +4334,7 @@ function PlaybackBar({ view, playing, limit, clockMs, speed, zoom, marks, slider
|
|
|
4318
4334
|
//#region src/react/flow-waterfall.tsx
|
|
4319
4335
|
const LANE_PAD = 12;
|
|
4320
4336
|
const AGENT_ROWS = 20;
|
|
4337
|
+
const UNFOLD_ROWS = 12;
|
|
4321
4338
|
const END_LABEL_GUTTER = {
|
|
4322
4339
|
spans: 120,
|
|
4323
4340
|
agents: 176
|
|
@@ -4428,7 +4445,7 @@ function OutcomeLegend({ agents }) {
|
|
|
4428
4445
|
}
|
|
4429
4446
|
/** Shared trace inspector: the agent timeline and the span waterfall over one clock.
|
|
4430
4447
|
* Data remains producer-owned; playback, zoom and folding change presentation only. */
|
|
4431
|
-
function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defaultCollapsed = false, defaultView = "spans", renderIcon, maxHeight, onSelect, markers, onTimeChange, agentFill
|
|
4448
|
+
function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defaultCollapsed = false, defaultView = "spans", renderIcon, maxHeight, onSelect, markers, onTimeChange, agentFill: fillProp, selectedId, onDeselect, description, summary: showSummary = true }) {
|
|
4432
4449
|
const root = useRef(null);
|
|
4433
4450
|
const search = useRef(null);
|
|
4434
4451
|
const slider = useRef(null);
|
|
@@ -4450,6 +4467,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4450
4467
|
const [laneElement, setLaneElement] = useState(null);
|
|
4451
4468
|
const observedTotal = Number.isFinite(trace.totalMs) && trace.totalMs >= 0 ? trace.totalMs : 0;
|
|
4452
4469
|
const isLive = live ?? trace.spans.some(spanRunning);
|
|
4470
|
+
const agentFill = fillProp ?? (trace.spans.some((span) => span.kind === "pipeline" && span.outcome) ? "outcome" : "muted");
|
|
4453
4471
|
const dataEnd = useMemo(() => Math.max(observedTotal, ...trace.spans.map((span) => Number.isFinite(span.endMs) ? span.endMs : 0)), [trace, observedTotal]);
|
|
4454
4472
|
const trackPx = Math.max(40, laneWidth - LANE_PAD - Math.min(END_LABEL_GUTTER[view], laneWidth * .3));
|
|
4455
4473
|
useEffect(() => {
|
|
@@ -4503,7 +4521,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4503
4521
|
}, [rows]);
|
|
4504
4522
|
const sharedDetail = agents.length > 1 && agents.every((row) => row.span.detail && row.span.detail === agents[0]?.span.detail) ? agents[0]?.span.detail : void 0;
|
|
4505
4523
|
const [flippedFamilies, setFlippedFamilies] = useState(/* @__PURE__ */ new Set());
|
|
4506
|
-
const openFamilies = useMemo(() => defaultOpenFamilies(agents,
|
|
4524
|
+
const openFamilies = useMemo(() => defaultOpenFamilies(agents, UNFOLD_ROWS, flippedFamilies), [agents, flippedFamilies]);
|
|
4507
4525
|
const [openSteps, setOpenSteps] = useState(/* @__PURE__ */ new Set());
|
|
4508
4526
|
const visible = useMemo(() => view === "agents" ? agentViewRows(agents, rows, openFamilies, openSteps) : visibleFlowWaterfallRows(rows, query, collapsed), [
|
|
4509
4527
|
view,
|
|
@@ -4718,8 +4736,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4718
4736
|
const cardAgent = cardRow && (() => {
|
|
4719
4737
|
for (let cursor = cardRow; cursor; cursor = cursor.parentId ? byId.get(cursor.parentId) : void 0) if (cursor.kind === "pipeline") return shown(cursor);
|
|
4720
4738
|
})();
|
|
4721
|
-
const
|
|
4722
|
-
const showCursor = moving || view === "agents";
|
|
4739
|
+
const showCursor = isLive || position !== null || playing || view === "agents";
|
|
4723
4740
|
const overviewMarks = useMemo(() => rows.filter((row) => !row.parentId).flatMap((row) => row.hasChildren ? row.segments : row.kind === "pipeline" ? [] : [{
|
|
4724
4741
|
startMs: row.span.startMs,
|
|
4725
4742
|
endMs: row.span.endMs,
|
|
@@ -4886,7 +4903,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4886
4903
|
collapsed,
|
|
4887
4904
|
searching: Boolean(query.trim()),
|
|
4888
4905
|
pauses: trace.pauses,
|
|
4889
|
-
cursorLine:
|
|
4906
|
+
cursorLine: position !== null || playing,
|
|
4890
4907
|
sharedDetail,
|
|
4891
4908
|
expandedIds: view === "agents" ? /* @__PURE__ */ new Set([...openFamilies, ...openSteps]) : void 0,
|
|
4892
4909
|
holdHeight: isLive,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/charts",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.7",
|
|
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",
|