@tangle-network/charts 0.21.0 → 0.21.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.
@@ -323,6 +323,8 @@ interface FlowSpan {
323
323
  icon?: string;
324
324
  /** Recorded values only. Absent means unknown and is shown as Unknown, never as zero. */
325
325
  costUsd?: number;
326
+ /** `lower`: `costUsd` is a floor and prints "≥". Spans inherit a trace's `costBound: 'lower'`. */
327
+ costBound?: 'lower' | 'upper';
326
328
  promptTokens?: number;
327
329
  completionTokens?: number;
328
330
  /** Short producer summaries shown in the span card. */
@@ -354,7 +356,10 @@ interface FlowSpanMark {
354
356
  interface FlowPause {
355
357
  startMs: number;
356
358
  endMs: number;
359
+ /** A few words on the band, such as 'Paused 4 h 40 m'. */
357
360
  label: string;
361
+ /** The full reason, shown on hover and focus of the label rather than printed across the rows. */
362
+ detail?: string;
358
363
  }
359
364
  /** A host verdict: `label` is the legend text, `tone` the status color. */
360
365
  interface FlowOutcome {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as BreakdownInput, B as Refusal, C as MarkKind, D as YScale, E as XCategory, F as Figure, H as Tier, I as Interval, L as IntervalMethod, M as CostBasis, N as Estimate, O as YTarget, P as Exclusion, R as MatrixCell, S as ChartMark, T as MarkerShape, U as TierCounts, V as Setup, W as isRefusal, _ as checkerColor, a as TimelineLane, b as BandKind, c as TimelineTick, d as ENDING_COLORS, f as ENDING_ORDER, g as TickOutcome, h as RunEnding, i as FlowTrace, j as ComparisonRow, k as YTick, l as timeline, m as OUTCOME_ORDER, n as FlowOutcome, o as TimelineOptions, p as OUTCOME_COLORS, r as FlowSpan, s as TimelineSpan, t as FlowMarker, u as CHECKER_HUES, v as PointFields, w as MarkVerdict, x as ChartBand, y as PointGlyph, z as RateRow } from "./flow-trace-DfSefpBM.js";
1
+ import { A as BreakdownInput, B as Refusal, C as MarkKind, D as YScale, E as XCategory, F as Figure, H as Tier, I as Interval, L as IntervalMethod, M as CostBasis, N as Estimate, O as YTarget, P as Exclusion, R as MatrixCell, S as ChartMark, T as MarkerShape, U as TierCounts, V as Setup, W as isRefusal, _ as checkerColor, a as TimelineLane, b as BandKind, c as TimelineTick, d as ENDING_COLORS, f as ENDING_ORDER, g as TickOutcome, h as RunEnding, i as FlowTrace, j as ComparisonRow, k as YTick, l as timeline, m as OUTCOME_ORDER, n as FlowOutcome, o as TimelineOptions, p as OUTCOME_COLORS, r as FlowSpan, s as TimelineSpan, t as FlowMarker, u as CHECKER_HUES, v as PointFields, w as MarkVerdict, x as ChartBand, y as PointGlyph, z as RateRow } from "./flow-trace-DBJY1YAA.js";
2
2
  //#region src/block.d.ts
3
3
  interface RenderFigureOptions {
4
4
  /** Chart uses a closed data disclosure; plot keeps only the SVG and accessible data. */
@@ -132,6 +132,8 @@
132
132
  border-inline: 1px dashed color-mix(in oklab, var(--fw-text) 24%, transparent); }
133
133
  .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;
134
134
  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
+ /* The label alone takes the pointer, for its reason; the band stays click-through to the rows. */
136
+ .fw-root .fw-pause-label[title] { pointer-events: auto; cursor: help; }
135
137
  .fw-root .fw-marker-label { position: absolute; top: 2px; left: 3px; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0 4px; border-radius: 4px; font-size: 12px; line-height: 16px; color: var(--fw-marker); background: var(--fw-surface); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-marker) 40%, transparent); }
136
138
  .fw-root .fw-marker-label-left { left: auto; right: 3px; }
137
139
  .fw-root .fw-marker[data-tone="warn"], .fw-root .fw-event[data-tone="warn"] { --fw-marker: var(--fw-amber); }
@@ -1,4 +1,4 @@
1
- import { B as Refusal, C as MarkKind, D as YScale, E as XCategory, F as Figure, O as YTarget, S as ChartMark, T as MarkerShape, a as TimelineLane, b as BandKind, c as TimelineTick, g as TickOutcome, i as FlowTrace, k as YTick, o as TimelineOptions, r as FlowSpan, s as TimelineSpan, t as FlowMarker, v as PointFields, w as MarkVerdict, x as ChartBand, y as PointGlyph } from "../flow-trace-DfSefpBM.js";
1
+ import { B as Refusal, C as MarkKind, D as YScale, E as XCategory, F as Figure, O as YTarget, S as ChartMark, T as MarkerShape, a as TimelineLane, b as BandKind, c as TimelineTick, g as TickOutcome, i as FlowTrace, k as YTick, o as TimelineOptions, r as FlowSpan, s as TimelineSpan, t as FlowMarker, v as PointFields, w as MarkVerdict, x as ChartBand, y as PointGlyph } from "../flow-trace-DBJY1YAA.js";
2
2
  import { CSSProperties, ReactElement, ReactNode } from "react";
3
3
  //#region src/react/sparkline.d.ts
4
4
  type SparklineDirection = "rising" | "falling" | "flat";
@@ -2509,7 +2509,8 @@ function flowWaterfallRows(trace, resolutionMs) {
2509
2509
  segments: [],
2510
2510
  own: [],
2511
2511
  retry: span.retryOf !== void 0,
2512
- hasAgents: false
2512
+ hasAgents: false,
2513
+ ...span.costBound === "lower" || span.costBound === void 0 && trace.costBound === "lower" ? { costFloor: true } : {}
2513
2514
  };
2514
2515
  });
2515
2516
  const byId = new Map(rows.map((row) => [row.id, row]));
@@ -2745,6 +2746,7 @@ function agentFamilies(agents, open) {
2745
2746
  const known = members.map((row) => row.span.costUsd ?? row.rollupCostUsd).filter((cost) => cost !== void 0);
2746
2747
  const failed = members.filter((row) => !row.ok).length;
2747
2748
  out.push({
2749
+ costFloor: known.length < members.length || members.some((row) => row.costFloor),
2748
2750
  id,
2749
2751
  parentId: members[0].parentId,
2750
2752
  name: `${role} ×${members.length}`,
@@ -2903,6 +2905,8 @@ function drawnRow(row, visible) {
2903
2905
  tone: family.tone
2904
2906
  } : row;
2905
2907
  }
2908
+ /** A row's cost as printed: "≥" when it is a floor. */
2909
+ const rowCostText = (row, cost) => `${row.costFloor ? "≥" : ""}${formatCost(cost)}`;
2906
2910
  //#endregion
2907
2911
  //#region src/react/flow-waterfall-icons.tsx
2908
2912
  const svg = (children, label) => /* @__PURE__ */ jsx("svg", {
@@ -3086,7 +3090,7 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
3086
3090
  for (const fact of (subject.facts ?? []).slice(0, 3)) cells.push([fact.label, fact.value]);
3087
3091
  cells.push(["Started", `+${formatSpanDuration(Math.max(0, subject.startMs))}`]);
3088
3092
  if (tokenCount !== void 0) cells.push(["Tokens", count(tokenCount)]);
3089
- if (cost !== void 0) cells.push(["Cost", formatCost(cost)]);
3093
+ if (cost !== void 0) cells.push(["Cost", rowCostText(who, cost)]);
3090
3094
  cells.splice(6);
3091
3095
  const kind = span.kind === "model" ? "Thinking" : KIND_LABEL[span.kind];
3092
3096
  const step = owner && span.endMs > span.startMs ? `${kind}${span.name && span.name !== kind && span.name.toLowerCase() !== span.kind ? ` ${span.name}` : ""} · ${duration}${row.ok ? "" : " · failed"}` : void 0;
@@ -3233,7 +3237,7 @@ function SpanCard({ id, row, agent, clockMs, left, top, pinned, onClose, renderI
3233
3237
  children: [
3234
3238
  /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Duration" }), /* @__PURE__ */ jsx("dd", { children: duration })] }),
3235
3239
  /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Started" }), /* @__PURE__ */ jsxs("dd", { children: ["+", formatSpanDuration(Math.max(0, span.startMs))] })] }),
3236
- span.costUsd !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Cost" }), /* @__PURE__ */ jsx("dd", { children: formatCost(span.costUsd) })] }),
3240
+ span.costUsd !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Cost" }), /* @__PURE__ */ jsx("dd", { children: rowCostText(row, span.costUsd) })] }),
3237
3241
  tokens && span.promptTokens !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Tokens in" }), /* @__PURE__ */ jsx("dd", { children: count(span.promptTokens) })] }),
3238
3242
  tokens && span.completionTokens !== void 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: "Tokens out" }), /* @__PURE__ */ jsx("dd", { children: count(span.completionTokens) })] }),
3239
3243
  (span.facts ?? []).slice(0, 3).map((fact) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", { children: fact.label }), /* @__PURE__ */ jsx("dd", { children: fact.value })] }, fact.label)),
@@ -3287,11 +3291,11 @@ const open = (row, clockMs) => spanRunning(row.span) || row.span.startMs <= cloc
3287
3291
  function endLabel(row, clockMs) {
3288
3292
  const span = row.span;
3289
3293
  if (row.durationLabel === "—") return "—";
3290
- if (row.approx && span.endMs === span.startMs && !spanRunning(span)) return span.costUsd === void 0 ? "" : formatCost(span.costUsd);
3294
+ if (row.approx && span.endMs === span.startMs && !spanRunning(span)) return span.costUsd === void 0 ? "" : rowCostText(row, span.costUsd);
3291
3295
  if (!spanRunning(span) && clockMs < span.endMs) return `${formatSpanDuration(Math.max(0, clockMs - span.startMs))}${row.approx ? "~" : ""}`;
3292
3296
  const duration = spanRunning(span) ? `${formatSpanDuration(Math.max(span.endMs, clockMs) - span.startMs)}${row.approx ? "~" : ""}` : row.durationLabel;
3293
3297
  const cost = span.costUsd ?? (row.rollupCostUsd ? row.rollupCostUsd : void 0);
3294
- return cost === void 0 ? duration : `${duration} · ${span.meta?.costPartial ? "≥" : ""}${formatCost(cost)}`;
3298
+ return cost === void 0 ? duration : `${duration} · ${rowCostText(row, cost)}`;
3295
3299
  }
3296
3300
  /** One trace row. Geometry comes from CSS variables, so a row re-renders only when its own state changes. */
3297
3301
  const FlowRow = memo(function FlowRow(props) {
@@ -3893,6 +3897,10 @@ function FlowBody(props) {
3893
3897
  "aria-pressed": jumped === marker,
3894
3898
  onPointerDown: (event) => event.stopPropagation(),
3895
3899
  onClick: () => {
3900
+ if (jumped === marker) {
3901
+ setJumped(void 0);
3902
+ return;
3903
+ }
3896
3904
  setJumped(marker);
3897
3905
  props.onScrub(marker.at);
3898
3906
  }
@@ -4002,6 +4010,9 @@ function FlowBody(props) {
4002
4010
  title: pause.label,
4003
4011
  children: /* @__PURE__ */ jsx("span", {
4004
4012
  className: "fw-pause-label",
4013
+ title: pause.detail,
4014
+ tabIndex: pause.detail ? 0 : void 0,
4015
+ "aria-label": pause.detail ? `${pause.label}: ${pause.detail}` : void 0,
4005
4016
  children: pause.label
4006
4017
  })
4007
4018
  }, `p-${pause.startMs}`)), props.markers?.map((marker) => /* @__PURE__ */ jsx("span", {
@@ -4636,6 +4647,10 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
4636
4647
  return () => document.removeEventListener("pointermove", move);
4637
4648
  }, [card, leave]);
4638
4649
  latest.current.pin = (target, element) => {
4650
+ if (card?.pinned && card.target === target) {
4651
+ closeCard();
4652
+ return;
4653
+ }
4639
4654
  place(target, element, true);
4640
4655
  const row = byId.get(target);
4641
4656
  if (row) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/charts",
3
- "version": "0.21.0",
3
+ "version": "0.21.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",