@tangle-network/charts 0.21.5 → 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.
@@ -135,7 +135,8 @@
135
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);
136
136
  border-inline: 1px dashed color-mix(in oklab, var(--fw-text) 24%, transparent); }
137
137
  .fw-root .fw-pause-label b { font-weight: 600; color: var(--fw-text); }
138
- .fw-root .fw-pause-label { position: sticky; top: 4px; display: block; width: max-content; max-width: calc(100% - 8px); margin: 4px auto 0; padding: 0 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
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;
139
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); }
140
141
  /* The label alone takes the pointer, for its reason; the band stays click-through to the rows. */
141
142
  .fw-root .fw-pause-label[title] { pointer-events: auto; cursor: help; }
@@ -265,7 +266,6 @@
265
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); }
266
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. */
267
268
  .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
269
  .fw-root .fw-glyph { display: inline-flex; color: currentColor; }
270
270
  .fw-root .fw-glyph svg { width: 11px; height: 11px; stroke-width: 2; }
271
271
  .fw-root .fw-seg-model .fw-glyph, .fw-root .fw-k-model .fw-glyph { color: light-dark(var(--tone), #fff); }
@@ -304,7 +304,10 @@
304
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; }
305
305
  .fw-root .fw-seg-alt { filter: brightness(.78); }
306
306
  /* 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, color-mix(in oklab, var(--fw-accent) 45%, transparent) var(--fw-progress), var(--fw-well) var(--fw-progress)) 0 50% / 100% 4px no-repeat; }
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); }
308
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);
309
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); }
310
313
  /* A hover card never covers the next mark: only a pinned card (a dialog) takes the pointer. */
@@ -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. `muted` (default): every agent one quiet tone, with color spent on state only: the outcome on
403
- * the row dot, red for failure, amber for the slowest. `agent`: each agent its own hue, shared by its steps and handoffs. `outcome`: the
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 };
@@ -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.kind === "pipeline" && row.parentId !== void 0) {
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) {
@@ -3442,7 +3442,7 @@ const FlowRow = memo(function FlowRow(props) {
3442
3442
  "aria-label": `${row.descendants} hidden steps`,
3443
3443
  children: ["+", row.descendants]
3444
3444
  }),
3445
- !row.ok && /* @__PURE__ */ jsx("span", {
3445
+ !row.ok && !agentView && /* @__PURE__ */ jsx("span", {
3446
3446
  className: "fw-alert",
3447
3447
  children: /* @__PURE__ */ jsx(AlertGlyph, { label: "failed" })
3448
3448
  })
@@ -3733,6 +3733,7 @@ function FlowBody(props) {
3733
3733
  const ticks = clockTicks(win.startMs, tickEnd, Math.max(1, trackPx + (view === "spans" ? 72 : 0)));
3734
3734
  const markerLabels = useMemo(() => {
3735
3735
  const shown = /* @__PURE__ */ new Map();
3736
+ if (view === "agents") return shown;
3736
3737
  let freeFrom = -Infinity;
3737
3738
  for (const marker of [...props.markers ?? []].sort((a, b) => a.at - b.at)) {
3738
3739
  const x = (marker.at - win.startMs) / msPerPx;
@@ -3748,7 +3749,8 @@ function FlowBody(props) {
3748
3749
  props.markers,
3749
3750
  win.startMs,
3750
3751
  msPerPx,
3751
- trackPx
3752
+ trackPx,
3753
+ view
3752
3754
  ]);
3753
3755
  useEffect(() => {
3754
3756
  const element = scroller.current;
@@ -4027,16 +4029,12 @@ function FlowBody(props) {
4027
4029
  "--e": pause.endMs
4028
4030
  },
4029
4031
  title: pause.label,
4030
- children: /* @__PURE__ */ jsxs("span", {
4032
+ children: /* @__PURE__ */ jsx("span", {
4031
4033
  className: "fw-pause-label",
4032
- title: pause.detail,
4033
- tabIndex: pause.detail ? 0 : void 0,
4034
- "aria-label": pause.detail ? `${pause.label}: ${pause.detail}` : void 0,
4035
- children: [
4036
- /* @__PURE__ */ jsx("b", { children: duration(pause.endMs - pause.startMs) }),
4037
- " ",
4038
- pause.label
4039
- ]
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) })
4040
4038
  })
4041
4039
  }, `p-${pause.startMs}`)), props.markers?.map((marker) => /* @__PURE__ */ jsx("span", {
4042
4040
  className: "fw-marker",
@@ -4318,6 +4316,7 @@ function PlaybackBar({ view, playing, limit, clockMs, speed, zoom, marks, slider
4318
4316
  //#region src/react/flow-waterfall.tsx
4319
4317
  const LANE_PAD = 12;
4320
4318
  const AGENT_ROWS = 20;
4319
+ const UNFOLD_ROWS = 12;
4321
4320
  const END_LABEL_GUTTER = {
4322
4321
  spans: 120,
4323
4322
  agents: 176
@@ -4428,7 +4427,7 @@ function OutcomeLegend({ agents }) {
4428
4427
  }
4429
4428
  /** Shared trace inspector: the agent timeline and the span waterfall over one clock.
4430
4429
  * 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 = "muted", selectedId, onDeselect, description, summary: showSummary = true }) {
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 }) {
4432
4431
  const root = useRef(null);
4433
4432
  const search = useRef(null);
4434
4433
  const slider = useRef(null);
@@ -4450,6 +4449,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
4450
4449
  const [laneElement, setLaneElement] = useState(null);
4451
4450
  const observedTotal = Number.isFinite(trace.totalMs) && trace.totalMs >= 0 ? trace.totalMs : 0;
4452
4451
  const isLive = live ?? trace.spans.some(spanRunning);
4452
+ const agentFill = fillProp ?? (trace.spans.some((span) => span.kind === "pipeline" && span.outcome) ? "outcome" : "muted");
4453
4453
  const dataEnd = useMemo(() => Math.max(observedTotal, ...trace.spans.map((span) => Number.isFinite(span.endMs) ? span.endMs : 0)), [trace, observedTotal]);
4454
4454
  const trackPx = Math.max(40, laneWidth - LANE_PAD - Math.min(END_LABEL_GUTTER[view], laneWidth * .3));
4455
4455
  useEffect(() => {
@@ -4503,7 +4503,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
4503
4503
  }, [rows]);
4504
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;
4505
4505
  const [flippedFamilies, setFlippedFamilies] = useState(/* @__PURE__ */ new Set());
4506
- const openFamilies = useMemo(() => defaultOpenFamilies(agents, AGENT_ROWS, flippedFamilies), [agents, flippedFamilies]);
4506
+ const openFamilies = useMemo(() => defaultOpenFamilies(agents, UNFOLD_ROWS, flippedFamilies), [agents, flippedFamilies]);
4507
4507
  const [openSteps, setOpenSteps] = useState(/* @__PURE__ */ new Set());
4508
4508
  const visible = useMemo(() => view === "agents" ? agentViewRows(agents, rows, openFamilies, openSteps) : visibleFlowWaterfallRows(rows, query, collapsed), [
4509
4509
  view,
@@ -4718,8 +4718,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
4718
4718
  const cardAgent = cardRow && (() => {
4719
4719
  for (let cursor = cardRow; cursor; cursor = cursor.parentId ? byId.get(cursor.parentId) : void 0) if (cursor.kind === "pipeline") return shown(cursor);
4720
4720
  })();
4721
- const moving = isLive || position !== null || playing;
4722
- const showCursor = moving || view === "agents";
4721
+ const showCursor = isLive || position !== null || playing || view === "agents";
4723
4722
  const overviewMarks = useMemo(() => rows.filter((row) => !row.parentId).flatMap((row) => row.hasChildren ? row.segments : row.kind === "pipeline" ? [] : [{
4724
4723
  startMs: row.span.startMs,
4725
4724
  endMs: row.span.endMs,
@@ -4886,7 +4885,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
4886
4885
  collapsed,
4887
4886
  searching: Boolean(query.trim()),
4888
4887
  pauses: trace.pauses,
4889
- cursorLine: moving,
4888
+ cursorLine: position !== null || playing,
4890
4889
  sharedDetail,
4891
4890
  expandedIds: view === "agents" ? /* @__PURE__ */ new Set([...openFamilies, ...openSteps]) : void 0,
4892
4891
  holdHeight: isLive,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/charts",
3
- "version": "0.21.5",
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",