@tangle-network/charts 2.0.0 → 2.0.1

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
@@ -6,6 +6,8 @@ The React entry also exports `DayBars`, `HBars`, `Histogram`, `Lines`, `Meter`,
6
6
 
7
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
+ `FlowWaterfall` accepts optional `graph` and `latestEvents` React nodes. Supplying `graph` adds Graph to its existing Agents/Steps view control, including `defaultView="graph"`. The trace clock and controlled selection survive switches. Graph hides waterfall-only controls; `latestEvents` stays mounted in the header across all views.
10
+
9
11
  `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
12
 
11
13
  Five figures cover the decisions a benchmark reader makes:
@@ -350,3 +352,4 @@ CHARTS_REACT_VERSION=19 CHARTS_PROOF_DIR=/tmp/charts-react19 node scripts/charts
350
352
  ```
351
353
 
352
354
  Consumers: agent-record adapts record provenance; agent-app supplies application trace producers; Creative consumes both. Intelligence already uses the row/axis primitives with its controlled virtualized tree. Tangle Website’s `RecordedRunWaterfall` uses the Header/Row primitives; Blueprint Agent’s execution timeline is a separately owned follow-up. Keep these existing primitives exported. Agent-record’s earlier agent-app dependency increased its installed graph from 134 to 256 package versions (tangle-tools #1674); adopting the lower chart owner removes that framework dependency, with the actual new graph measured by the deployment owner.
355
+
@@ -83,11 +83,13 @@
83
83
  .fw-root .fw-search-input::placeholder { color: var(--fw-muted); }
84
84
  .fw-root .fw-search-input::-webkit-search-cancel-button { display: none; }
85
85
 
86
- .fw-root .fw-summary { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); border-bottom: 1px solid var(--fw-border); }
86
+ .fw-root .fw-summary { display: flex; flex-wrap: wrap; align-items: center; border-bottom: 1px solid var(--fw-border); }
87
87
  .fw-root .fw-summary > div { padding: 12px 16px; min-width: 0; }
88
- .fw-root .fw-summary > div + div { border-left: 1px solid var(--fw-border); }
89
- .fw-root .fw-summary dt { font-size: 14px; line-height: 18px; color: var(--fw-muted); }
90
- .fw-root .fw-value { margin-top: 4px; font-size: 18px; line-height: 24px; font-weight: 600; letter-spacing: -.01em; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
88
+ .fw-root .fw-summary > div:nth-child(-n+2) { flex: 1; }
89
+ .fw-root .fw-summary > div:nth-child(n+3) { display: flex; align-items: baseline; gap: 4px; padding: 8px 6px; color: var(--fw-muted); }
90
+ .fw-root .fw-summary dt { font-size: 14px; line-height: 20px; color: var(--fw-muted); }
91
+ .fw-root .fw-value { margin-top: 4px; font-size: 28px; line-height: 34px; font-weight: 600; letter-spacing: -.02em; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
92
+ .fw-root .fw-summary > div:nth-child(n+3) .fw-value { margin: 0; font-size: 14px; line-height: 20px; font-weight: 500; letter-spacing: normal; }
91
93
  .fw-root .fw-has-errors .fw-value { color: var(--fw-failed); }
92
94
 
93
95
  .fw-root .fw-axis { display: grid; grid-template-columns: var(--fw-label-col) minmax(0, 1fr); height: 32px; color: var(--fw-muted); font-size: 14px; }
@@ -203,7 +205,7 @@
203
205
  .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; }
204
206
  .fw-root .fw-bar.fw-k-tool { background: var(--dim); box-shadow: inset 0 0 0 1px var(--dim-line); }
205
207
  .fw-root .fw-bar.fw-k-handoff { background: var(--line); height: 8px; top: calc(50% + var(--fw-bar-shift) - 4px); box-shadow: none; }
206
- .fw-root .fw-bar.fw-k-waiting { background: repeating-linear-gradient(-45deg, var(--fw-hatch) 0 1px, transparent 1px 5px); box-shadow: inset 0 0 0 1px var(--fw-hatch); }
208
+ .fw-root .fw-bar.fw-k-waiting, .fw-root .fw-kinds .fw-swatch-wait { background: repeating-linear-gradient(-45deg, var(--fw-hatch) 0 1px, transparent 1px 5px); box-shadow: inset 0 0 0 1px var(--fw-hatch); }
207
209
  .fw-root .fw-bar.fw-k-failed { background: color-mix(in oklab, var(--fw-failed) 22%, var(--fw-surface)); box-shadow: inset 0 0 0 1px var(--fw-failed); color: var(--fw-failed); }
208
210
  .fw-root .fw-bar.fw-k-pipeline { background: linear-gradient(90deg, var(--fw-neutral-bar), color-mix(in oklab, var(--fw-neutral-bar) 55%, var(--fw-surface))); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-text) 12%, transparent); }
209
211
  .fw-root .fw-bar.fw-k-pipeline.fw-has-marks { background: repeating-linear-gradient(-45deg, var(--fw-hatch) 0 1px, transparent 1px 5px); box-shadow: inset 0 0 0 1px var(--fw-hatch); }
@@ -227,7 +229,7 @@
227
229
  .fw-root.fw-fill-agent .fw-kinds .fw-swatch-model { background: var(--model-fill); }
228
230
  .fw-root.fw-fill-agent .fw-kinds .fw-swatch-tool { background: var(--tool-fill); }
229
231
  .fw-root .fw-kinds .fw-swatch-tool { width: 12px; height: 8px; border-radius: 2px; background: var(--fw-kind-tool); }
230
- .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); }
232
+ .fw-root .fw-kinds .fw-swatch-wait { width: 12px; height: 8px; border-radius: 2px; }
231
233
  .fw-root .fw-kinds .fw-swatch-event { width: 8px; height: 8px; border-radius: 50%; background: var(--fw-muted); box-shadow: 0 0 0 2px var(--fw-surface); }
232
234
  .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; }
233
235
  .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; }
@@ -384,10 +386,7 @@
384
386
  .fw-root .fw-card .fw-agent-dot:not([data-outcome]) { background: transparent; box-shadow: inset 0 0 0 1.5px var(--fw-muted); }
385
387
  @container flow-waterfall (max-width: 599px) {
386
388
  .fw-root > * { --fw-label-col: 0px; --fw-bar-shift: 8px; --fw-bar-h: 8px; --fw-row-h: 44px; }
387
- .fw-root .fw-summary { grid-template-columns: repeat(3, minmax(0, 1fr)); }
388
- .fw-root .fw-summary > div { padding: 8px; }
389
- .fw-root .fw-summary > div:nth-child(4) { border-left: 0; }
390
- .fw-root .fw-summary > div:nth-child(n+4) { border-top: 1px solid var(--fw-border); }
389
+ .fw-root .fw-summary > div:nth-child(-n+2) { flex-basis: 50%; padding: 6px 8px; }
391
390
  .fw-root :is(.fw-zoom, .fw-branch-controls, .fw-resize, .fw-links, .fw-gridlayer, .fw-marker-layer, .fw-subtitle, .fw-badge, .fw-icon-tile) { display: none; }
392
391
  .fw-root .fw-row-state { display: inline; }
393
392
  .fw-root .fw-key > summary { display: flex; align-items: center; min-height: 32px; cursor: pointer; }
@@ -459,7 +459,7 @@ export declare function flowWaterfallRows(trace: FlowTrace, resolutionMs?: numbe
459
459
  export declare function criticalPathIds(rows: readonly FlowWaterfallRow[]): ReadonlySet<string>;
460
460
  //#endregion
461
461
  //#region src/react/flow-waterfall-row.d.ts
462
- type FlowView = 'spans' | 'agents';
462
+ type FlowView = 'spans' | 'agents' | 'graph';
463
463
  //#endregion
464
464
  //#region src/react/flow-waterfall-time.d.ts
465
465
  interface ViewWindow {
@@ -480,6 +480,10 @@ interface FlowWaterfallProps {
480
480
  defaultCollapsed?: boolean;
481
481
  /** `agents` shows one row per agent (the agent timeline); expanding a row opens the full span tree. */
482
482
  defaultView?: FlowView;
483
+ /** Host graph rendered by the same view control, retaining the trace clock and selection. */
484
+ graph?: ReactNode;
485
+ /** Shared header feed, kept mounted across Agents, Steps and Graph. */
486
+ latestEvents?: ReactNode;
483
487
  /** Host brand marks by span; the inspector draws kind glyphs without it. */
484
488
  renderIcon?: (span: FlowSpan) => ReactNode;
485
489
  /** Scroll height of the rows; the tree is virtualized beyond it. Defaults to 20 agent rows, or 640 px of steps. */
@@ -519,7 +523,7 @@ interface FlowWaterfallProps {
519
523
  }
520
524
  /** Shared trace inspector: the agent timeline and the span waterfall over one clock.
521
525
  * Data remains producer-owned; playback, zoom and folding change presentation only. */
522
- export declare function FlowWaterfall({ trace, live, startedAt, label, defaultCollapsed, defaultView, renderIcon, maxHeight, onSelect, markers, onTimeChange, agentFill: fillProp, selectedId, onDeselect, description, summary: showSummary, criticalPath, query: queryProp, onQueryChange, collapsed: collapsedProp, onCollapsedChange, zoom: zoomProp, onZoomChange, matchSpan }: FlowWaterfallProps): import("react").JSX.Element;
526
+ export declare function FlowWaterfall({ trace, live, startedAt, label, defaultCollapsed, defaultView, graph, latestEvents, renderIcon, maxHeight, onSelect, markers, onTimeChange, agentFill: fillProp, selectedId, onDeselect, description, summary: showSummary, criticalPath, query: queryProp, onQueryChange, collapsed: collapsedProp, onCollapsedChange, zoom: zoomProp, onZoomChange, matchSpan }: FlowWaterfallProps): import("react").JSX.Element;
523
527
  //#endregion
524
528
  //#region src/react/flow-waterfall-icons.d.ts
525
529
  /** Kind glyphs drawn when the host supplies no `renderIcon`. Brand marks belong to the host. */
@@ -6934,10 +6934,15 @@ function agentFamilies(agents, open) {
6934
6934
  const hue = (row) => row.tone <= 0 || ++next > 5 ? 0 : 1 + [...roleOf(row.name)].reduce((hash, c) => hash * 31 + c.charCodeAt(0) >>> 0, 0) % 5;
6935
6935
  for (const [role, members] of groups) {
6936
6936
  if (members.length < 2) {
6937
- out.push(...members.map((row) => ({
6937
+ const row = members[0], tone = hue(row);
6938
+ out.push({
6938
6939
  ...row,
6939
- tone: hue(row)
6940
- })));
6940
+ tone,
6941
+ own: row.own.map((mark) => ({
6942
+ ...mark,
6943
+ tone
6944
+ }))
6945
+ });
6941
6946
  continue;
6942
6947
  }
6943
6948
  const tone = hue(members[0]);
@@ -7960,7 +7965,8 @@ function clampWindow(view, limitMs) {
7960
7965
  //#region src/react/flow-waterfall-body.tsx
7961
7966
  const ROW_HEIGHT = {
7962
7967
  spans: 36,
7963
- agents: 40
7968
+ agents: 40,
7969
+ graph: 40
7964
7970
  };
7965
7971
  const OVERSCAN = 8;
7966
7972
  /** Rows re-render once per CHUNK rows scrolled; overscan covers the rows in between. */
@@ -8690,11 +8696,13 @@ const AGENT_ROWS = 20;
8690
8696
  const UNFOLD_ROWS = 12;
8691
8697
  const END_LABEL_GUTTER = {
8692
8698
  spans: 120,
8693
- agents: 176
8699
+ agents: 176,
8700
+ graph: 176
8694
8701
  };
8695
8702
  const NAME_WIDTH = {
8696
8703
  spans: 320,
8697
- agents: 240
8704
+ agents: 240,
8705
+ graph: 240
8698
8706
  };
8699
8707
  /** State the host may own: its value wins when given, and every change is reported either way. */
8700
8708
  function useHostable(value, onChange, initial) {
@@ -8771,7 +8779,7 @@ const TONE_ORDER = [
8771
8779
  "ok"
8772
8780
  ];
8773
8781
  /** What each mark means, as the agent view draws it; a key only for kinds the run has. */
8774
- function KindLegend({ rows }) {
8782
+ function KindLegend({ rows, fill }) {
8775
8783
  const has = (kind) => rows.some((row) => row.own.some((mark) => mark.kind === kind) || row.span.kind === kind);
8776
8784
  const shown = [
8777
8785
  ["model", "Model call"],
@@ -8780,13 +8788,19 @@ function KindLegend({ rows }) {
8780
8788
  ["handoff", "Handoff"]
8781
8789
  ].filter(([kind]) => kind === "waiting" ? rows.some((row) => row.kind === "pipeline") : kind === "handoff" ? rows.some((row) => row.span.handoffTo || row.hasAgents) : has(kind));
8782
8790
  if (shown.length === 0) return null;
8783
- return /* @__PURE__ */ jsx("ul", {
8791
+ const example = rows.find((row) => toneKey(row.tone) > 0 && row.own.some((mark) => mark.kind === "model")) ?? rows.find((row) => row.own.length);
8792
+ return /* @__PURE__ */ jsxs("ul", {
8784
8793
  className: "fw-legend fw-kinds",
8785
8794
  "aria-label": "Key",
8786
- children: shown.map(([kind, name]) => /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("span", {
8795
+ "data-tone": fill === "agent" ? toneKey(example?.tone ?? 0) : void 0,
8796
+ "data-example": example?.id,
8797
+ children: [shown.map(([kind, name]) => /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("span", {
8787
8798
  className: `fw-swatch-${kind === "waiting" ? "wait" : kind}`,
8788
8799
  "aria-hidden": true
8789
- }), name] }, kind))
8800
+ }), name] }, kind)), /* @__PURE__ */ jsx("li", {
8801
+ className: "fw-kind-rule",
8802
+ children: fill === "agent" ? "Bar color identifies family; shade identifies work." : "Shade identifies work."
8803
+ })]
8790
8804
  });
8791
8805
  }
8792
8806
  /** The axis dots and the dashed lines they drop: one key naming what they mark (a release, or the kinds the host gave). */
@@ -8886,17 +8900,37 @@ function OutcomeLegend({ agents, selected, onFilter, neutral }) {
8886
8900
  ]
8887
8901
  });
8888
8902
  }
8903
+ function ViewControl({ view, graph, onChange }) {
8904
+ return /* @__PURE__ */ jsx("div", {
8905
+ className: "fw-views",
8906
+ role: "radiogroup",
8907
+ "aria-label": "Trace view",
8908
+ children: [
8909
+ "agents",
8910
+ "spans",
8911
+ ...graph ? ["graph"] : []
8912
+ ].map((option) => /* @__PURE__ */ jsx("button", {
8913
+ type: "button",
8914
+ role: "radio",
8915
+ "aria-checked": view === option,
8916
+ disabled: view === option,
8917
+ className: "fw-view-button",
8918
+ onClick: () => onChange(option),
8919
+ children: option === "agents" ? "Agents" : option === "spans" ? "Steps" : "Graph"
8920
+ }, option))
8921
+ });
8922
+ }
8889
8923
  /** Shared trace inspector: the agent timeline and the span waterfall over one clock.
8890
8924
  * Data remains producer-owned; playback, zoom and folding change presentation only. */
8891
- 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, criticalPath = false, query: queryProp, onQueryChange, collapsed: collapsedProp, onCollapsedChange, zoom: zoomProp, onZoomChange, matchSpan }) {
8925
+ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defaultCollapsed = false, defaultView = "spans", graph, latestEvents, renderIcon, maxHeight, onSelect, markers, onTimeChange, agentFill: fillProp, selectedId, onDeselect, description, summary: showSummary = true, criticalPath = false, query: queryProp, onQueryChange, collapsed: collapsedProp, onCollapsedChange, zoom: zoomProp, onZoomChange, matchSpan }) {
8892
8926
  const root = useRef(null);
8893
8927
  const search = useRef(null);
8894
8928
  const slider = useRef(null);
8895
8929
  const timeOutput = useRef(null);
8896
8930
  const hideTimer = useRef(void 0);
8897
- const searchId = useId();
8898
- const cardId = useId();
8899
- const [view, setView] = useState(defaultView);
8931
+ const searchId = useId(), cardId = useId();
8932
+ const [requestedView, setView] = useState(defaultView);
8933
+ const view = requestedView === "graph" && graph == null ? "agents" : requestedView;
8900
8934
  const [query, setQuery] = useHostable(queryProp, onQueryChange, "");
8901
8935
  const [collapsed, setCollapsed] = useHostable(collapsedProp, onCollapsedChange, () => new Set(defaultCollapsed ? trace.spans.flatMap((span) => span.parentId ? [span.parentId] : []) : []));
8902
8936
  const [speed, setSpeed] = useState(1);
@@ -9012,6 +9046,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
9012
9046
  }, [view, agents]);
9013
9047
  useEffect(() => {
9014
9048
  const keydown = (event) => {
9049
+ if (view === "graph") return;
9015
9050
  const active = document.activeElement;
9016
9051
  const ownsFocus = root.current?.contains(active);
9017
9052
  if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "f") {
@@ -9326,7 +9361,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
9326
9361
  done && speedup >= 1.05 ? ` · ${speedup.toFixed(1)}x parallel` : ""
9327
9362
  ]
9328
9363
  }),
9329
- failedAgents > 0 && /* @__PURE__ */ jsxs("button", {
9364
+ view !== "graph" && failedAgents > 0 && /* @__PURE__ */ jsxs("button", {
9330
9365
  type: "button",
9331
9366
  className: "fw-text-button fw-alert",
9332
9367
  "aria-pressed": filter === "__errors",
@@ -9338,7 +9373,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
9338
9373
  " failed"
9339
9374
  ]
9340
9375
  }),
9341
- quietAgents > 0 && /* @__PURE__ */ jsxs("button", {
9376
+ view !== "graph" && quietAgents > 0 && /* @__PURE__ */ jsxs("button", {
9342
9377
  type: "button",
9343
9378
  className: "fw-text-button fw-quiet",
9344
9379
  "aria-pressed": filter === "__quiet",
@@ -9351,7 +9386,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
9351
9386
  children: "Live"
9352
9387
  })
9353
9388
  ]
9354
- }), /* @__PURE__ */ jsxs("details", {
9389
+ }), view !== "graph" && /* @__PURE__ */ jsxs("details", {
9355
9390
  className: "fw-key",
9356
9391
  open: root.current ? root.current.clientWidth >= 600 : true,
9357
9392
  children: [/* @__PURE__ */ jsxs("summary", { children: ["Key", /* @__PURE__ */ jsx(ChevronGlyph, {})] }), /* @__PURE__ */ jsxs("div", {
@@ -9363,30 +9398,24 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
9363
9398
  selected: filter,
9364
9399
  onFilter: applyFilter
9365
9400
  }),
9366
- view === "agents" && /* @__PURE__ */ jsx(KindLegend, { rows }),
9401
+ view === "agents" && /* @__PURE__ */ jsx(KindLegend, {
9402
+ rows: visible,
9403
+ fill: agentFill
9404
+ }),
9367
9405
  /* @__PURE__ */ jsx(MarkerLegend, { markers })
9368
9406
  ]
9369
9407
  })]
9370
9408
  })]
9371
9409
  }), /* @__PURE__ */ jsxs("div", {
9372
9410
  className: "fw-tools",
9373
- children: [/* @__PURE__ */ jsx("div", {
9374
- className: "fw-views",
9375
- role: "radiogroup",
9376
- "aria-label": "Trace view",
9377
- children: ["agents", "spans"].map((option) => /* @__PURE__ */ jsx("button", {
9378
- type: "button",
9379
- role: "radio",
9380
- "aria-checked": view === option,
9381
- disabled: view === option,
9382
- className: "fw-view-button",
9383
- onClick: () => {
9384
- setView(option);
9385
- setNameWidth(NAME_WIDTH[option]);
9386
- setCard(null);
9387
- },
9388
- children: option === "agents" ? "Agents" : "Steps"
9389
- }, option))
9411
+ children: [/* @__PURE__ */ jsx(ViewControl, {
9412
+ view,
9413
+ graph: graph != null,
9414
+ onChange: (option) => {
9415
+ setView(option);
9416
+ setNameWidth(NAME_WIDTH[option]);
9417
+ setCard(null);
9418
+ }
9390
9419
  }), view === "spans" && /* @__PURE__ */ jsxs("div", {
9391
9420
  className: "fw-search",
9392
9421
  children: [
@@ -9420,91 +9449,94 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
9420
9449
  })]
9421
9450
  })]
9422
9451
  }),
9423
- showSummary && /* @__PURE__ */ jsx("dl", {
9424
- className: "fw-summary",
9425
- children: summary.map(([name, value, title]) => /* @__PURE__ */ jsxs("div", {
9426
- className: name === "Errors" && errors !== void 0 && errors > 0 ? "fw-has-errors" : void 0,
9427
- children: [/* @__PURE__ */ jsx("dt", { children: name }), /* @__PURE__ */ jsx("dd", {
9428
- className: "fw-value",
9429
- title,
9430
- children: name === "Errors" ? /* @__PURE__ */ jsx("button", {
9431
- type: "button",
9432
- "aria-pressed": filter === "__errors",
9433
- onClick: () => applyFilter("__errors"),
9434
- children: value
9435
- }) : value
9436
- })]
9437
- }, name))
9438
- }),
9439
- view === "agents" && playback,
9440
- /* @__PURE__ */ jsx(FlowBody, {
9441
- rows: visible,
9442
- byId,
9443
- totalRows: rows.length,
9444
- view,
9445
- maxHeight: rowsHeight,
9446
- collapsed,
9447
- searching: Boolean(query.trim()),
9448
- query,
9449
- matchSpan,
9450
- pauses: trace.pauses,
9451
- errorTimes,
9452
- cursorLine: isLive || position !== null || playing,
9453
- sharedDetail,
9454
- sharedOutcome,
9455
- expandedIds: view === "agents" ? /* @__PURE__ */ new Set([...openFamilies, ...openSteps]) : void 0,
9456
- holdHeight: isLive,
9457
- slowestId: slowest?.id ?? callouts.slowestId,
9458
- costliestId: callouts.costliestId,
9459
- criticalIds: criticalPath && view === "spans" ? critical : void 0,
9460
- activeId,
9461
- selectedId: selection,
9462
- focusId: cardAgent?.id ?? (view === "agents" ? activeId : void 0),
9463
- clockMs,
9464
- window: win,
9465
- trackPx,
9466
- showCursor,
9467
- markers,
9468
- fillByAgent: agentFill !== "outcome",
9469
- zoomed: zoom !== null,
9470
- renderIcon,
9471
- emptyText: rows.length === 0 ? "No spans yet. Waiting for trace events." : `No matching steps for “${query}”.`,
9472
- onToggle: toggle,
9473
- onInspect: inspect,
9474
- onLeave: leave,
9475
- onPin: pin,
9476
- onActivate: (row) => setActiveId(row.id),
9477
- onActiveChange: setActiveId,
9478
- onScrub: scrubTo,
9479
- onZoom: zoomBy,
9480
- onPan: pan,
9481
- nameWidth,
9482
- onNameWidth: (px) => {
9483
- resized.current = true;
9484
- setNameWidth(Math.max(140, Math.min(560, px)));
9485
- },
9486
- onTrack: setLaneElement,
9487
- onExpandAll: rows.some((row) => row.hasChildren) ? () => setCollapsed(/* @__PURE__ */ new Set()) : void 0,
9488
- onCollapseAll: () => setCollapsed(new Set(rows.filter((row) => row.hasChildren).map((row) => row.id)))
9489
- }),
9490
- card && cardRow && /* @__PURE__ */ jsx("div", {
9491
- className: `fw-card-anchor${card.above && card.top === void 0 ? " fw-card-above" : ""}`,
9492
- onPointerEnter: () => clearTimeout(hideTimer.current),
9493
- onPointerLeave: card.pinned ? void 0 : leave,
9494
- children: /* @__PURE__ */ jsx(SpanCard, {
9495
- id: cardId,
9496
- row: cardRow,
9497
- agent: cardAgent,
9452
+ latestEvents,
9453
+ view === "graph" ? graph : /* @__PURE__ */ jsxs(Fragment, { children: [
9454
+ showSummary && /* @__PURE__ */ jsx("dl", {
9455
+ className: "fw-summary",
9456
+ children: summary.map(([name, value, title]) => /* @__PURE__ */ jsxs("div", {
9457
+ className: name === "Errors" && errors !== void 0 && errors > 0 ? "fw-has-errors" : void 0,
9458
+ children: [/* @__PURE__ */ jsx("dt", { children: name }), /* @__PURE__ */ jsx("dd", {
9459
+ className: "fw-value",
9460
+ title,
9461
+ children: name === "Errors" ? /* @__PURE__ */ jsx("button", {
9462
+ type: "button",
9463
+ "aria-pressed": filter === "__errors",
9464
+ onClick: () => applyFilter("__errors"),
9465
+ children: value
9466
+ }) : value
9467
+ })]
9468
+ }, name))
9469
+ }),
9470
+ view === "agents" && playback,
9471
+ /* @__PURE__ */ jsx(FlowBody, {
9472
+ rows: visible,
9473
+ byId,
9474
+ totalRows: rows.length,
9475
+ view,
9476
+ maxHeight: rowsHeight,
9477
+ collapsed,
9478
+ searching: Boolean(query.trim()),
9479
+ query,
9480
+ matchSpan,
9481
+ pauses: trace.pauses,
9482
+ errorTimes,
9483
+ cursorLine: isLive || position !== null || playing,
9484
+ sharedDetail,
9485
+ sharedOutcome,
9486
+ expandedIds: view === "agents" ? /* @__PURE__ */ new Set([...openFamilies, ...openSteps]) : void 0,
9487
+ holdHeight: isLive,
9488
+ slowestId: slowest?.id ?? callouts.slowestId,
9489
+ costliestId: callouts.costliestId,
9490
+ criticalIds: criticalPath && view === "spans" ? critical : void 0,
9491
+ activeId,
9492
+ selectedId: selection,
9493
+ focusId: cardAgent?.id ?? (view === "agents" ? activeId : void 0),
9498
9494
  clockMs,
9499
- left: card.left,
9500
- top: card.top ?? (card.above ? card.anchorTop - 8 : card.anchorBottom + 8),
9501
- pinned: card.pinned,
9502
- onClose: closeCard,
9495
+ window: win,
9496
+ trackPx,
9497
+ showCursor,
9498
+ markers,
9499
+ fillByAgent: agentFill !== "outcome",
9500
+ zoomed: zoom !== null,
9503
9501
  renderIcon,
9504
- compact: view === "agents"
9505
- })
9506
- }),
9507
- view === "spans" && playback
9502
+ emptyText: rows.length === 0 ? "No spans yet. Waiting for trace events." : `No matching steps for “${query}”.`,
9503
+ onToggle: toggle,
9504
+ onInspect: inspect,
9505
+ onLeave: leave,
9506
+ onPin: pin,
9507
+ onActivate: (row) => setActiveId(row.id),
9508
+ onActiveChange: setActiveId,
9509
+ onScrub: scrubTo,
9510
+ onZoom: zoomBy,
9511
+ onPan: pan,
9512
+ nameWidth,
9513
+ onNameWidth: (px) => {
9514
+ resized.current = true;
9515
+ setNameWidth(Math.max(140, Math.min(560, px)));
9516
+ },
9517
+ onTrack: setLaneElement,
9518
+ onExpandAll: rows.some((row) => row.hasChildren) ? () => setCollapsed(/* @__PURE__ */ new Set()) : void 0,
9519
+ onCollapseAll: () => setCollapsed(new Set(rows.filter((row) => row.hasChildren).map((row) => row.id)))
9520
+ }),
9521
+ card && cardRow && /* @__PURE__ */ jsx("div", {
9522
+ className: `fw-card-anchor${card.above && card.top === void 0 ? " fw-card-above" : ""}`,
9523
+ onPointerEnter: () => clearTimeout(hideTimer.current),
9524
+ onPointerLeave: card.pinned ? void 0 : leave,
9525
+ children: /* @__PURE__ */ jsx(SpanCard, {
9526
+ id: cardId,
9527
+ row: cardRow,
9528
+ agent: cardAgent,
9529
+ clockMs,
9530
+ left: card.left,
9531
+ top: card.top ?? (card.above ? card.anchorTop - 8 : card.anchorBottom + 8),
9532
+ pinned: card.pinned,
9533
+ onClose: closeCard,
9534
+ renderIcon,
9535
+ compact: view === "agents"
9536
+ })
9537
+ }),
9538
+ view === "spans" && playback
9539
+ ] })
9508
9540
  ]
9509
9541
  });
9510
9542
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/charts",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
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",