@tangle-network/charts 0.21.9 → 0.21.11

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.
@@ -22,12 +22,16 @@
22
22
  --fw-failed: light-dark(#d4304a, #ff5c74);
23
23
  --fw-ok: var(--status-running, light-dark(#0d8a5f, #34c48c));
24
24
  --fw-amber: light-dark(#a35d00, #f2b14c);
25
+ /* Step kinds inside an agent bar: two hues far from each other and from the outcome greens and reds. */
26
+ --fw-kind-model: light-dark(#4f46e5, #8f91f7);
27
+ --fw-kind-tool: light-dark(#0e8fb0, #4cc9e6);
25
28
  --fw-neutral-bar: color-mix(in oklab, var(--fw-text) 15%, var(--fw-surface));
26
29
  --fw-hatch: color-mix(in oklab, var(--fw-text) 22%, transparent);
27
30
  /* Root agent neutral, then five agent hues. Each passes 3:1 against both surfaces as a mark. */
28
31
  --fw-h0: light-dark(#6c6f84, #a3a6bb);
29
32
  --fw-h1: light-dark(#4b55d6, #8f96ff);
30
- --fw-h2: light-dark(#c3570f, #f2913f);
33
+ /* No warm hue among the agents: amber is kept for the slowest and red for failure. */
34
+ --fw-h2: light-dark(#3d8a52, #6fcb8a);
31
35
  --fw-h3: light-dark(#0b87a0, #35c2db);
32
36
  --fw-h4: light-dark(#b0418a, #dc72bb);
33
37
  --fw-h5: light-dark(#6d44c7, #ab8cf7);
@@ -126,7 +130,7 @@
126
130
  .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; }
127
131
  /* An event's line is drawn behind the rows, with the gridlines, so bars and labels cover it; its label stays on top. */
128
132
  .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); }
129
- .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); }
133
+ .fw-root .fw-marker-line { --fw-marker: var(--fw-muted); opacity: .4; 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); }
130
134
  .fw-root .fw-marker-line[data-tone="ok"] { --fw-marker: var(--fw-ok); }
131
135
  .fw-root .fw-marker-line[data-tone="failed"] { --fw-marker: var(--fw-failed); }
132
136
  .fw-root .fw-marker[data-tone="ok"] { --fw-marker: var(--fw-ok); }
@@ -192,7 +196,7 @@
192
196
  .fw-root .fw-view-agents .fw-names { flex-direction: column; align-items: flex-start; gap: 0; }
193
197
  .fw-root .fw-view-agents .fw-span-name { font-weight: 600; line-height: 18px; }
194
198
  /* One 16 px line that wraps its last part out of sight: when the line is full the tool count drops whole, never "7 to…". */
195
- .fw-root .fw-view-agents .fw-subtitle { display: flex; flex-wrap: wrap; align-items: center; column-gap: 4px; height: 16px; overflow: hidden; max-width: 100%; min-width: 0; color: var(--fw-muted); font-size: 12px; line-height: 16px; }
199
+ .fw-root .fw-view-agents .fw-subtitle { display: flex; flex-wrap: wrap; align-items: center; column-gap: 4px; height: 16px; overflow: hidden; max-width: 100%; min-width: 0; color: var(--fw-muted); font-size: 13px; line-height: 16px; }
196
200
  .fw-root .fw-view-agents .fw-subtitle-extra { flex: none; white-space: nowrap; }
197
201
  .fw-root .fw-view-agents .fw-subtitle-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
198
202
  .fw-root .fw-view-agents .fw-detail-icon { flex: none; width: 16px; height: 16px; align-items: center; justify-content: center; overflow: hidden; }
@@ -246,17 +250,22 @@
246
250
  /* Muted agents (the default): one quiet tone for every agent bar, its steps and handoffs; the slowest agent alone takes amber, and
247
251
  failure keeps its red glyph, so color only ever means state. */
248
252
  .fw-root.fw-fill-muted .fw-view-agents [data-tone] { --tone: var(--fw-h0); }
249
- .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); }
253
+ .fw-root:is(.fw-fill-muted, .fw-fill-agent) .fw-view-agents .fw-row[data-slowest], .fw-root:is(.fw-fill-muted, .fw-fill-agent) .fw-view-agents .fw-row[data-slowest] [data-tone] { --tone: var(--fw-amber); }
250
254
  .fw-root [data-outcome="ok"], .fw-root [data-outcome="ok"] [data-tone] { --tone: var(--fw-ok); }
251
255
  .fw-root [data-outcome="failed"], .fw-root [data-outcome="failed"] [data-tone] { --tone: var(--fw-failed); }
252
256
  .fw-root [data-outcome="warn"], .fw-root [data-outcome="warn"] [data-tone] { --tone: var(--fw-amber); }
253
257
  .fw-root [data-outcome="neutral"], .fw-root [data-outcome="neutral"] [data-tone] { --tone: var(--fw-muted); }
254
258
  .fw-root .fw-legend { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 8px 0 0; padding: 0; list-style: none; color: var(--fw-muted); font-size: 12px; line-height: 16px; }
255
259
  .fw-root .fw-legend li { display: inline-flex; align-items: center; gap: 8px; }
256
- .fw-root .fw-kinds { margin: 0; padding: 4px 24px 8px; justify-content: flex-start; color: var(--fw-text); }
257
- .fw-root .fw-kinds .fw-swatch-model { width: 12px; height: 8px; border-radius: 2px; background: color-mix(in oklab, var(--fw-text) 40%, var(--fw-surface)); }
258
- .fw-root .fw-kinds .fw-swatch-tool { width: 12px; height: 8px; border-radius: 2px; background: color-mix(in oklab, var(--fw-text) 16%, var(--fw-surface)); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-text) 30%, transparent); }
259
- .fw-root .fw-kinds .fw-swatch-wait { width: 12px; height: 8px; border-radius: 2px; background: repeating-linear-gradient(-45deg, var(--fw-hatch) 0 1px, transparent 1px 4px); box-shadow: inset 0 0 0 1px var(--fw-hatch); }
260
+ .fw-root .fw-legends { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 24px; margin-top: 8px; }
261
+ .fw-root .fw-legends .fw-legend { margin: 0; }
262
+ .fw-root .fw-kinds { margin: 0; padding: 0; justify-content: flex-start; color: var(--fw-muted); }
263
+ .fw-root .fw-kinds .fw-swatch-model { width: 12px; height: 8px; border-radius: 2px; background: var(--fw-kind-model); }
264
+ /* With per-agent hues the key shows kind as shade, as the bars do: darker for a model call, lighter for a tool call. */
265
+ .fw-root.fw-fill-agent .fw-kinds .fw-swatch-model { background: color-mix(in oklab, var(--fw-text) 58%, var(--fw-surface)); }
266
+ .fw-root.fw-fill-agent .fw-kinds .fw-swatch-tool { background: color-mix(in oklab, var(--fw-text) 24%, var(--fw-surface)); }
267
+ .fw-root .fw-kinds .fw-swatch-tool { width: 12px; height: 8px; border-radius: 2px; background: var(--fw-kind-tool); }
268
+ .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); }
260
269
  .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; }
261
270
  .fw-root .fw-kinds .fw-swatch-failed { width: 12px; height: 8px; border-radius: 2px; box-shadow: inset 0 0 0 1px var(--fw-failed); }
262
271
  .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; }
@@ -269,6 +278,14 @@
269
278
  .fw-root .fw-view-agents .fw-seg { pointer-events: auto; }
270
279
  .fw-root .fw-seg-model { background: var(--fill); box-shadow: inset 0 0 0 1px var(--line); }
271
280
  .fw-root .fw-seg-tool { background: var(--dim); box-shadow: inset 0 0 0 1px var(--dim-line); }
281
+ /* Without per-agent hues (muted or outcome fill), a step's kind owns its color; with them, kind is the darker and lighter shade of the
282
+ agent's own hue (the rules above), so each agent stays one color. */
283
+ .fw-root:is(.fw-fill-muted, .fw-fill-outcome) .fw-view-agents .fw-seg-model { background: var(--fw-kind-model); box-shadow: none; }
284
+ .fw-root:is(.fw-fill-muted, .fw-fill-outcome) .fw-view-agents .fw-seg-tool { background: var(--fw-kind-tool); box-shadow: none; }
285
+ .fw-root .fw-view-agents .fw-seg-tool { container-type: inline-size; justify-content: flex-start; }
286
+ .fw-root .fw-seg-name { padding: 0 6px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 11px; line-height: 16px; font-weight: 600; color: light-dark(#fff, #06222b); pointer-events: none; }
287
+ @container (max-width: 56px) { .fw-root .fw-seg-name { display: none; } }
288
+ .fw-root:is(.fw-fill-muted, .fw-fill-outcome) .fw-view-agents :is(.fw-seg-alt.fw-seg-model, .fw-seg-alt.fw-seg-tool) { filter: brightness(.88); }
272
289
  .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); }
273
290
  /* 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. */
274
291
  .fw-root .fw-view-agents .fw-seg-failed { background: var(--dim); box-shadow: inset 0 0 0 1px var(--dim-line); }
@@ -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. 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. */
402
+ /** What an agent bar's color means. `agent` (default): each agent or family its own muted hue across dot, bar and handoffs, with
403
+ * model and tool steps as darker and lighter shades of it; the outcome stays on the row dot and amber marks the slowest (blind round
404
+ * 25: global step hues made agents blur together). `muted`: one quiet tone, color on state only. `outcome`: the bar takes its outcome. */
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;
@@ -2586,7 +2586,8 @@ function segmentOf(row) {
2586
2586
  tone: row.tone,
2587
2587
  id: row.id,
2588
2588
  count: 1,
2589
- retry: row.retry
2589
+ retry: row.retry,
2590
+ name: row.name
2590
2591
  };
2591
2592
  }
2592
2593
  /** Search keeps ancestor context and deliberately reveals matches inside collapsed branches. */
@@ -2760,7 +2761,7 @@ function agentFamilies(agents, open) {
2760
2761
  costFloor: known.length < members.length || members.some((row) => row.costFloor),
2761
2762
  id,
2762
2763
  parentId: members[0].parentId,
2763
- name: `${role} ×${members.length}`,
2764
+ name: role,
2764
2765
  kind: "pipeline",
2765
2766
  durationLabel: formatSpanDuration(endMs - startMs),
2766
2767
  approx: members.some((row) => row.approx),
@@ -2895,7 +2896,8 @@ function stepRuns(steps) {
2895
2896
  tone: first.tone,
2896
2897
  id: step.id,
2897
2898
  count: 1,
2898
- retry: step.retry
2899
+ retry: step.retry,
2900
+ name: step.name
2899
2901
  })),
2900
2902
  ...costs.length ? { rollupCostUsd: costs.reduce((sum, cost) => sum + cost, 0) } : {},
2901
2903
  span: {
@@ -3289,18 +3291,23 @@ const at = (startMs, endMs) => ({
3289
3291
  const endLabelWidth = (label, slowest, costliest) => label.length * 7 + 8 + (slowest ? 74 : 0) + (costliest ? 84 : 0);
3290
3292
  function Segment({ mark, events, agentView, ending }) {
3291
3293
  const glyph = mark.kind === "failed" ? agentView && !ending ? null : /* @__PURE__ */ jsx(FailGlyph, {}) : mark.retry ? /* @__PURE__ */ jsx(RetryGlyph, {}) : null;
3294
+ const named = agentView && mark.kind === "tool" && mark.count === 1 && mark.name;
3292
3295
  return /* @__PURE__ */ jsx("span", {
3293
3296
  className: `fw-seg fw-seg-${mark.kind}${mark.alt ? " fw-seg-alt" : ""}`,
3294
3297
  "data-tone": toneKey(mark.tone),
3295
3298
  style: at(mark.startMs, mark.endMs),
3299
+ title: mark.name ? `${mark.name}${mark.count > 1 ? ` +${mark.count - 1} more` : ""}` : void 0,
3296
3300
  onPointerEnter: agentView ? (event) => events.onInspect(mark.id, event.currentTarget) : void 0,
3297
3301
  onClick: agentView ? (event) => {
3298
3302
  event.stopPropagation();
3299
3303
  events.onPin(mark.id, event.currentTarget);
3300
3304
  } : void 0,
3301
- children: glyph && /* @__PURE__ */ jsx("span", {
3305
+ children: glyph ? /* @__PURE__ */ jsx("span", {
3302
3306
  className: "fw-glyph",
3303
3307
  children: glyph
3308
+ }) : named && /* @__PURE__ */ jsx("span", {
3309
+ className: "fw-seg-name",
3310
+ children: mark.name
3304
3311
  })
3305
3312
  });
3306
3313
  }
@@ -4489,7 +4496,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
4489
4496
  const [laneElement, setLaneElement] = useState(null);
4490
4497
  const observedTotal = Number.isFinite(trace.totalMs) && trace.totalMs >= 0 ? trace.totalMs : 0;
4491
4498
  const isLive = live ?? trace.spans.some(spanRunning);
4492
- const agentFill = fillProp ?? (trace.spans.some((span) => span.kind === "pipeline" && span.outcome) ? "outcome" : "muted");
4499
+ const agentFill = fillProp ?? "agent";
4493
4500
  const dataEnd = useMemo(() => Math.max(observedTotal, ...trace.spans.map((span) => Number.isFinite(span.endMs) ? span.endMs : 0)), [trace, observedTotal]);
4494
4501
  const trackPx = Math.max(40, laneWidth - LANE_PAD - Math.min(END_LABEL_GUTTER[view], laneWidth * .3));
4495
4502
  useEffect(() => {
@@ -4849,7 +4856,10 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
4849
4856
  done && speedup >= 1.05 ? ` · ${speedup.toFixed(1)}x parallel` : ""
4850
4857
  ]
4851
4858
  }),
4852
- /* @__PURE__ */ jsx(OutcomeLegend, { agents })
4859
+ /* @__PURE__ */ jsxs("div", {
4860
+ className: "fw-legends",
4861
+ children: [/* @__PURE__ */ jsx(OutcomeLegend, { agents }), view === "agents" && /* @__PURE__ */ jsx(KindLegend, { rows })]
4862
+ })
4853
4863
  ]
4854
4864
  }), /* @__PURE__ */ jsxs("div", {
4855
4865
  className: "fw-tools",
@@ -4915,7 +4925,6 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
4915
4925
  }, name))
4916
4926
  }),
4917
4927
  view === "agents" && playback,
4918
- view === "agents" && /* @__PURE__ */ jsx(KindLegend, { rows }),
4919
4928
  /* @__PURE__ */ jsx(FlowBody, {
4920
4929
  rows: visible,
4921
4930
  byId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/charts",
3
- "version": "0.21.9",
3
+ "version": "0.21.11",
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",