@tangle-network/charts 0.21.4 → 0.21.6
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 +20 -5
- package/dist/react/index.d.ts +5 -4
- package/dist/react/index.js +42 -22
- package/package.json +1 -1
|
@@ -123,14 +123,20 @@
|
|
|
123
123
|
.fw-root .fw-gridline { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--fw-grid); left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); }
|
|
124
124
|
.fw-root .fw-cursor-layer { z-index: 3; }
|
|
125
125
|
.fw-root .fw-marker-layer { position: absolute; top: 0; bottom: 0; left: var(--fw-label-col); right: 0; overflow: hidden; pointer-events: none; z-index: 2; }
|
|
126
|
-
|
|
126
|
+
/* An event's line is drawn behind the rows, with the gridlines, so bars and labels cover it; its label stays on top. */
|
|
127
|
+
.fw-root .fw-marker { --fw-marker: var(--fw-muted); position: absolute; top: 0; bottom: 0; width: 0; left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); }
|
|
128
|
+
.fw-root .fw-marker-line { --fw-marker: var(--fw-muted); position: absolute; top: 0; bottom: 0; border-left: 1px dashed var(--fw-marker); left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); }
|
|
129
|
+
.fw-root .fw-marker-line[data-tone="ok"] { --fw-marker: var(--fw-ok); }
|
|
130
|
+
.fw-root .fw-marker-line[data-tone="failed"] { --fw-marker: var(--fw-failed); }
|
|
127
131
|
.fw-root .fw-marker[data-tone="ok"] { --fw-marker: var(--fw-ok); }
|
|
128
132
|
.fw-root .fw-marker[data-tone="failed"] { --fw-marker: var(--fw-failed); }
|
|
129
133
|
/* A pause: the run stood still. A hatched band across every row with its reason on top, never blank space. */
|
|
130
134
|
.fw-root .fw-pause { position: absolute; top: 0; bottom: 0; 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);
|
|
131
135
|
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);
|
|
132
136
|
border-inline: 1px dashed color-mix(in oklab, var(--fw-text) 24%, transparent); }
|
|
133
|
-
.fw-root .fw-pause-label {
|
|
137
|
+
.fw-root .fw-pause-label b { font-weight: 600; color: var(--fw-text); }
|
|
138
|
+
.fw-root .fw-pause { overflow: visible; }
|
|
139
|
+
.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;
|
|
134
140
|
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); }
|
|
135
141
|
/* The label alone takes the pointer, for its reason; the band stays click-through to the rows. */
|
|
136
142
|
.fw-root .fw-pause-label[title] { pointer-events: auto; cursor: help; }
|
|
@@ -231,6 +237,10 @@
|
|
|
231
237
|
.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); }
|
|
232
238
|
.fw-root .fw-view-agents .fw-bar.fw-k-pipeline:not(.fw-has-marks, .fw-has-agents) { background: var(--fill); box-shadow: inset 0 0 0 1px var(--line); }
|
|
233
239
|
/* A host outcome re-tones the whole lane: dot, bar and marks. */
|
|
240
|
+
/* Muted agents (the default): one quiet tone for every agent bar, its steps and handoffs; the slowest agent alone takes amber, and
|
|
241
|
+
failure keeps its red glyph, so color only ever means state. */
|
|
242
|
+
.fw-root.fw-fill-muted .fw-view-agents [data-tone] { --tone: var(--fw-h0); }
|
|
243
|
+
.fw-root.fw-fill-muted .fw-view-agents .fw-row[data-slowest], .fw-root.fw-fill-muted .fw-view-agents .fw-row[data-slowest] [data-tone] { --tone: var(--fw-amber); }
|
|
234
244
|
.fw-root [data-outcome="ok"], .fw-root [data-outcome="ok"] [data-tone] { --tone: var(--fw-ok); }
|
|
235
245
|
.fw-root [data-outcome="failed"], .fw-root [data-outcome="failed"] [data-tone] { --tone: var(--fw-failed); }
|
|
236
246
|
.fw-root [data-outcome="warn"], .fw-root [data-outcome="warn"] [data-tone] { --tone: var(--fw-amber); }
|
|
@@ -256,11 +266,13 @@
|
|
|
256
266
|
.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); }
|
|
257
267
|
/* 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. */
|
|
258
268
|
.fw-root .fw-view-agents .fw-seg-failed { background: var(--dim); box-shadow: inset 0 0 0 1px var(--dim-line); }
|
|
259
|
-
.fw-root .fw-view-agents .fw-alert { color: var(--fw-muted); }
|
|
260
269
|
.fw-root .fw-glyph { display: inline-flex; color: currentColor; }
|
|
261
270
|
.fw-root .fw-glyph svg { width: 11px; height: 11px; stroke-width: 2; }
|
|
262
271
|
.fw-root .fw-seg-model .fw-glyph, .fw-root .fw-k-model .fw-glyph { color: light-dark(var(--tone), #fff); }
|
|
263
|
-
|
|
272
|
+
/* End labels sit on the surface, so connectors and event lines stop at them, and never run past the lane: a label that would
|
|
273
|
+
be cut at the right edge slides back over its bar's tail instead. */
|
|
274
|
+
.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);
|
|
275
|
+
left: min(calc(var(--x0) + max(var(--w), 3px) + 4px), calc(100% - var(--fw-end-w, 0px) - 4px)); padding: 0 4px; border-radius: 4px; background: var(--fw-surface); white-space: nowrap; pointer-events: none; }
|
|
264
276
|
.fw-root .fw-duration { font-size: 12px; color: var(--fw-muted); font-variant-numeric: tabular-nums; }
|
|
265
277
|
.fw-root .fw-k-failed ~ .fw-end .fw-duration { color: var(--fw-failed); }
|
|
266
278
|
.fw-root .fw-badge { height: 18px; padding: 0 8px; border-radius: 999px; font-size: 12px; line-height: 16px; font-weight: 600; color: var(--fw-amber); background: color-mix(in oklab, var(--fw-amber) 13%, transparent); border: 1px solid color-mix(in oklab, var(--fw-amber) 28%, transparent); }
|
|
@@ -292,7 +304,10 @@
|
|
|
292
304
|
.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; }
|
|
293
305
|
.fw-root .fw-seg-alt { filter: brightness(.78); }
|
|
294
306
|
/* Agent view: the scrubber track shows how far the run has played, so at rest it reads full, not empty. */
|
|
295
|
-
.fw-root-agents .fw-slider { background: linear-gradient(90deg,
|
|
307
|
+
.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; }
|
|
308
|
+
/* The playhead: a round knob on a filled track, visible at rest as the references show it. */
|
|
309
|
+
.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); }
|
|
310
|
+
.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); }
|
|
296
311
|
.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);
|
|
297
312
|
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); }
|
|
298
313
|
/* 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,10 @@ 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
|
|
403
|
-
*
|
|
404
|
-
|
|
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
|
+
agentFill?: 'muted' | 'outcome' | 'agent';
|
|
405
406
|
/** One line beside the title saying what the run was for, such as its goal. */
|
|
406
407
|
description?: string;
|
|
407
408
|
/** The row the host has selected, such as the agent whose transcript is open: it lifts and, in the agent view, its peers dim.
|
|
@@ -414,6 +415,6 @@ interface FlowWaterfallProps {
|
|
|
414
415
|
}
|
|
415
416
|
/** Shared trace inspector: the agent timeline and the span waterfall over one clock.
|
|
416
417
|
* Data remains producer-owned; playback, zoom and folding change presentation only. */
|
|
417
|
-
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;
|
|
418
419
|
//#endregion
|
|
419
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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as hatchPath, J as markerPath, b as isRefusal, h as eventsGap, t as timeline, u as MARK_HEADS, y as markName } from "../timeline-B7c-cede.js";
|
|
1
|
+
import { G as hatchPath, J as markerPath, T as duration, b as isRefusal, h as eventsGap, t as timeline, u as MARK_HEADS, y as markName } from "../timeline-B7c-cede.js";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Fragment as Fragment$1, memo, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
//#region src/react/sparkline.tsx
|
|
@@ -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) {
|
|
@@ -2724,6 +2724,13 @@ const roleOf = (name) => name.replace(/[-\s]\d+$/, "");
|
|
|
2724
2724
|
* Agents sharing a role fold into one family row until opened: a 17-agent run reads as its few roles, and the bar of a
|
|
2725
2725
|
* family carries every member's work in each member's hue. Members follow their opened family, marked with `family`.
|
|
2726
2726
|
*/
|
|
2727
|
+
/** The families open by default: all of them when every agent fits in `rows`, so a reader sees each agent and its steps (the
|
|
2728
|
+
* blind reviews preferred agent rows to `role ×N`); none on bigger runs. `flipped` holds the families the reader toggled. */
|
|
2729
|
+
function defaultOpenFamilies(agents, rows, flipped) {
|
|
2730
|
+
const unfold = agents.length <= rows;
|
|
2731
|
+
const ids = agentFamilies(agents, /* @__PURE__ */ new Set()).filter((row) => row.id.startsWith("family:")).map((row) => row.id);
|
|
2732
|
+
return new Set(ids.filter((id) => unfold !== flipped.has(id)));
|
|
2733
|
+
}
|
|
2727
2734
|
function agentFamilies(agents, open) {
|
|
2728
2735
|
const groups = /* @__PURE__ */ new Map();
|
|
2729
2736
|
for (const row of agents) {
|
|
@@ -3272,6 +3279,7 @@ const at = (startMs, endMs) => ({
|
|
|
3272
3279
|
"--s": startMs,
|
|
3273
3280
|
"--e": Number.isFinite(endMs) ? endMs : OPEN_END
|
|
3274
3281
|
});
|
|
3282
|
+
const endLabelWidth = (label, slowest, costliest) => label.length * 7 + 8 + (slowest ? 74 : 0) + (costliest ? 84 : 0);
|
|
3275
3283
|
function Segment({ mark, events, agentView, ending }) {
|
|
3276
3284
|
const glyph = mark.kind === "failed" ? agentView && !ending ? null : /* @__PURE__ */ jsx(FailGlyph, {}) : mark.retry ? /* @__PURE__ */ jsx(RetryGlyph, {}) : null;
|
|
3277
3285
|
return /* @__PURE__ */ jsx("span", {
|
|
@@ -3434,7 +3442,7 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
3434
3442
|
"aria-label": `${row.descendants} hidden steps`,
|
|
3435
3443
|
children: ["+", row.descendants]
|
|
3436
3444
|
}),
|
|
3437
|
-
!row.ok && /* @__PURE__ */ jsx("span", {
|
|
3445
|
+
!row.ok && !agentView && /* @__PURE__ */ jsx("span", {
|
|
3438
3446
|
className: "fw-alert",
|
|
3439
3447
|
children: /* @__PURE__ */ jsx(AlertGlyph, { label: "failed" })
|
|
3440
3448
|
})
|
|
@@ -3483,7 +3491,10 @@ const FlowRow = memo(function FlowRow(props) {
|
|
|
3483
3491
|
}, `${mark.id}-${i}`)),
|
|
3484
3492
|
/* @__PURE__ */ jsxs("span", {
|
|
3485
3493
|
className: "fw-end",
|
|
3486
|
-
style:
|
|
3494
|
+
style: {
|
|
3495
|
+
...at(span.startMs, running ? OPEN_END : span.endMs),
|
|
3496
|
+
"--fw-end-w": `${endLabelWidth(label, slowest, costliest)}px`
|
|
3497
|
+
},
|
|
3487
3498
|
children: [
|
|
3488
3499
|
/* @__PURE__ */ jsx("span", {
|
|
3489
3500
|
className: "fw-duration",
|
|
@@ -3722,6 +3733,7 @@ function FlowBody(props) {
|
|
|
3722
3733
|
const ticks = clockTicks(win.startMs, tickEnd, Math.max(1, trackPx + (view === "spans" ? 72 : 0)));
|
|
3723
3734
|
const markerLabels = useMemo(() => {
|
|
3724
3735
|
const shown = /* @__PURE__ */ new Map();
|
|
3736
|
+
if (view === "agents") return shown;
|
|
3725
3737
|
let freeFrom = -Infinity;
|
|
3726
3738
|
for (const marker of [...props.markers ?? []].sort((a, b) => a.at - b.at)) {
|
|
3727
3739
|
const x = (marker.at - win.startMs) / msPerPx;
|
|
@@ -3737,7 +3749,8 @@ function FlowBody(props) {
|
|
|
3737
3749
|
props.markers,
|
|
3738
3750
|
win.startMs,
|
|
3739
3751
|
msPerPx,
|
|
3740
|
-
trackPx
|
|
3752
|
+
trackPx,
|
|
3753
|
+
view
|
|
3741
3754
|
]);
|
|
3742
3755
|
useEffect(() => {
|
|
3743
3756
|
const element = scroller.current;
|
|
@@ -3952,13 +3965,17 @@ function FlowBody(props) {
|
|
|
3952
3965
|
children: [axis, /* @__PURE__ */ jsxs("div", {
|
|
3953
3966
|
className: "fw-viewport",
|
|
3954
3967
|
children: [
|
|
3955
|
-
/* @__PURE__ */
|
|
3968
|
+
/* @__PURE__ */ jsxs("div", {
|
|
3956
3969
|
className: "fw-gridlayer",
|
|
3957
3970
|
"aria-hidden": true,
|
|
3958
|
-
children: ticks.map((tick) => /* @__PURE__ */ jsx("span", {
|
|
3971
|
+
children: [ticks.map((tick) => /* @__PURE__ */ jsx("span", {
|
|
3959
3972
|
className: "fw-gridline",
|
|
3960
3973
|
style: { "--s": tick }
|
|
3961
|
-
}, tick))
|
|
3974
|
+
}, tick)), props.markers?.map((marker) => /* @__PURE__ */ jsx("span", {
|
|
3975
|
+
className: "fw-marker-line",
|
|
3976
|
+
"data-tone": marker.tone ?? "neutral",
|
|
3977
|
+
style: { "--s": marker.at }
|
|
3978
|
+
}, `l-${marker.at}-${marker.label}`))]
|
|
3962
3979
|
}),
|
|
3963
3980
|
/* @__PURE__ */ jsxs("div", {
|
|
3964
3981
|
ref: scroller,
|
|
@@ -4014,10 +4031,10 @@ function FlowBody(props) {
|
|
|
4014
4031
|
title: pause.label,
|
|
4015
4032
|
children: /* @__PURE__ */ jsx("span", {
|
|
4016
4033
|
className: "fw-pause-label",
|
|
4017
|
-
title: pause.detail,
|
|
4018
|
-
tabIndex:
|
|
4019
|
-
"aria-label": pause.
|
|
4020
|
-
children: pause.
|
|
4034
|
+
title: pause.detail ? `${pause.label}: ${pause.detail}` : pause.label,
|
|
4035
|
+
tabIndex: 0,
|
|
4036
|
+
"aria-label": `${pause.label}, ${duration(pause.endMs - pause.startMs)}${pause.detail ? `: ${pause.detail}` : ""}`,
|
|
4037
|
+
children: /* @__PURE__ */ jsx("b", { children: duration(pause.endMs - pause.startMs) })
|
|
4021
4038
|
})
|
|
4022
4039
|
}, `p-${pause.startMs}`)), props.markers?.map((marker) => /* @__PURE__ */ jsx("span", {
|
|
4023
4040
|
className: "fw-marker",
|
|
@@ -4298,6 +4315,8 @@ function PlaybackBar({ view, playing, limit, clockMs, speed, zoom, marks, slider
|
|
|
4298
4315
|
//#endregion
|
|
4299
4316
|
//#region src/react/flow-waterfall.tsx
|
|
4300
4317
|
const LANE_PAD = 12;
|
|
4318
|
+
const AGENT_ROWS = 20;
|
|
4319
|
+
const UNFOLD_ROWS = 12;
|
|
4301
4320
|
const END_LABEL_GUTTER = {
|
|
4302
4321
|
spans: 120,
|
|
4303
4322
|
agents: 176
|
|
@@ -4408,7 +4427,7 @@ function OutcomeLegend({ agents }) {
|
|
|
4408
4427
|
}
|
|
4409
4428
|
/** Shared trace inspector: the agent timeline and the span waterfall over one clock.
|
|
4410
4429
|
* Data remains producer-owned; playback, zoom and folding change presentation only. */
|
|
4411
|
-
function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defaultCollapsed = false, defaultView = "spans", renderIcon, maxHeight, onSelect, markers, onTimeChange, agentFill
|
|
4430
|
+
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 }) {
|
|
4412
4431
|
const root = useRef(null);
|
|
4413
4432
|
const search = useRef(null);
|
|
4414
4433
|
const slider = useRef(null);
|
|
@@ -4430,6 +4449,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4430
4449
|
const [laneElement, setLaneElement] = useState(null);
|
|
4431
4450
|
const observedTotal = Number.isFinite(trace.totalMs) && trace.totalMs >= 0 ? trace.totalMs : 0;
|
|
4432
4451
|
const isLive = live ?? trace.spans.some(spanRunning);
|
|
4452
|
+
const agentFill = fillProp ?? (trace.spans.some((span) => span.kind === "pipeline" && span.outcome) ? "outcome" : "muted");
|
|
4433
4453
|
const dataEnd = useMemo(() => Math.max(observedTotal, ...trace.spans.map((span) => Number.isFinite(span.endMs) ? span.endMs : 0)), [trace, observedTotal]);
|
|
4434
4454
|
const trackPx = Math.max(40, laneWidth - LANE_PAD - Math.min(END_LABEL_GUTTER[view], laneWidth * .3));
|
|
4435
4455
|
useEffect(() => {
|
|
@@ -4482,7 +4502,8 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4482
4502
|
return pipelines.length ? pipelines : rows.filter((row) => !row.parentId);
|
|
4483
4503
|
}, [rows]);
|
|
4484
4504
|
const sharedDetail = agents.length > 1 && agents.every((row) => row.span.detail && row.span.detail === agents[0]?.span.detail) ? agents[0]?.span.detail : void 0;
|
|
4485
|
-
const [
|
|
4505
|
+
const [flippedFamilies, setFlippedFamilies] = useState(/* @__PURE__ */ new Set());
|
|
4506
|
+
const openFamilies = useMemo(() => defaultOpenFamilies(agents, UNFOLD_ROWS, flippedFamilies), [agents, flippedFamilies]);
|
|
4486
4507
|
const [openSteps, setOpenSteps] = useState(/* @__PURE__ */ new Set());
|
|
4487
4508
|
const visible = useMemo(() => view === "agents" ? agentViewRows(agents, rows, openFamilies, openSteps) : visibleFlowWaterfallRows(rows, query, collapsed), [
|
|
4488
4509
|
view,
|
|
@@ -4499,7 +4520,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4499
4520
|
visible,
|
|
4500
4521
|
clockMs
|
|
4501
4522
|
]);
|
|
4502
|
-
const rowsHeight = maxHeight ?? (view === "agents" ? ROW_HEIGHT.agents *
|
|
4523
|
+
const rowsHeight = maxHeight ?? (view === "agents" ? ROW_HEIGHT.agents * AGENT_ROWS : 640);
|
|
4503
4524
|
const resized = useRef(false);
|
|
4504
4525
|
useLayoutEffect(() => {
|
|
4505
4526
|
if (view !== "agents" || resized.current || !root.current) return;
|
|
@@ -4600,7 +4621,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4600
4621
|
const toggleNow = (row) => {
|
|
4601
4622
|
const flip = (open) => new Set(open.has(row.id) ? [...open].filter((id) => id !== row.id) : [...open, row.id]);
|
|
4602
4623
|
if (row.id.startsWith("family:")) {
|
|
4603
|
-
|
|
4624
|
+
setFlippedFamilies(flip);
|
|
4604
4625
|
return;
|
|
4605
4626
|
}
|
|
4606
4627
|
onSelect?.(row.span);
|
|
@@ -4697,8 +4718,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4697
4718
|
const cardAgent = cardRow && (() => {
|
|
4698
4719
|
for (let cursor = cardRow; cursor; cursor = cursor.parentId ? byId.get(cursor.parentId) : void 0) if (cursor.kind === "pipeline") return shown(cursor);
|
|
4699
4720
|
})();
|
|
4700
|
-
const
|
|
4701
|
-
const showCursor = moving || view === "agents";
|
|
4721
|
+
const showCursor = isLive || position !== null || playing || view === "agents";
|
|
4702
4722
|
const overviewMarks = useMemo(() => rows.filter((row) => !row.parentId).flatMap((row) => row.hasChildren ? row.segments : row.kind === "pipeline" ? [] : [{
|
|
4703
4723
|
startMs: row.span.startMs,
|
|
4704
4724
|
endMs: row.span.endMs,
|
|
@@ -4737,7 +4757,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4737
4757
|
ref: root,
|
|
4738
4758
|
"data-flow-waterfall": true,
|
|
4739
4759
|
"aria-label": label,
|
|
4740
|
-
className: `fw-root fw-root-${view}`,
|
|
4760
|
+
className: `fw-root fw-root-${view} fw-fill-${agentFill}`,
|
|
4741
4761
|
"data-selection": selectedId ? "" : void 0,
|
|
4742
4762
|
style: {
|
|
4743
4763
|
"--fw-name-w": `${nameWidth}px`,
|
|
@@ -4865,7 +4885,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4865
4885
|
collapsed,
|
|
4866
4886
|
searching: Boolean(query.trim()),
|
|
4867
4887
|
pauses: trace.pauses,
|
|
4868
|
-
cursorLine:
|
|
4888
|
+
cursorLine: position !== null || playing,
|
|
4869
4889
|
sharedDetail,
|
|
4870
4890
|
expandedIds: view === "agents" ? /* @__PURE__ */ new Set([...openFamilies, ...openSteps]) : void 0,
|
|
4871
4891
|
holdHeight: isLive,
|
|
@@ -4879,7 +4899,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
|
|
|
4879
4899
|
trackPx,
|
|
4880
4900
|
showCursor,
|
|
4881
4901
|
markers,
|
|
4882
|
-
fillByAgent: agentFill
|
|
4902
|
+
fillByAgent: agentFill !== "outcome",
|
|
4883
4903
|
zoomed: zoom !== null,
|
|
4884
4904
|
renderIcon,
|
|
4885
4905
|
emptyText: rows.length === 0 ? "No spans yet. Waiting for trace events." : `No matching steps for “${query}”.`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/charts",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.6",
|
|
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",
|