@tangle-network/charts 0.20.2 → 0.21.0

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.
@@ -262,7 +262,8 @@
262
262
  .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); }
263
263
 
264
264
  /* Handoff elbows: 1.5 px stroke, 6 px radius, a 5 px dot where the handoff leaves its sender. */
265
- .fw-root .fw-links { position: absolute; top: 0; bottom: 0; left: var(--fw-label-col); right: 0; overflow: hidden; pointer-events: none; z-index: 1; }
265
+ /* Under the rows (which follow it), so end labels and badges always sit above the connectors. */
266
+ .fw-root .fw-links { position: absolute; top: 0; bottom: 0; left: var(--fw-label-col); right: 0; overflow: hidden; pointer-events: none; z-index: 0; }
266
267
  .fw-root .fw-link {
267
268
  --x: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px);
268
269
  position: absolute; width: 8px; border: 0 solid var(--line); border-bottom-width: 1.5px; translate: 0 var(--fw-bar-shift);
@@ -277,6 +277,31 @@ export declare function Gantt({ rows, lanes: laneInput, colors, axis, span, mark
277
277
  hatch?: string[];
278
278
  }): import("react").JSX.Element;
279
279
  //#endregion
280
+ //#region src/react/gap.d.ts
281
+ interface GapBand {
282
+ min: number;
283
+ max: number;
284
+ }
285
+ interface GapProps {
286
+ value: number;
287
+ /** A bound, not a measurement: the true value is at most (upper) or at least (lower) `value`; drawn as an open arrow. */
288
+ bound?: 'upper' | 'lower' | null;
289
+ target: GapBand;
290
+ stretch?: GapBand | null;
291
+ scale?: 'log' | 'linear';
292
+ format?: (value: number) => string;
293
+ /** The value mark's color; the caller's state color. */
294
+ tone?: string;
295
+ /** Hover text for each mark; defaults name the numbers. */
296
+ titles?: {
297
+ value?: string;
298
+ target?: string;
299
+ stretch?: string;
300
+ };
301
+ label?: string;
302
+ }
303
+ export declare function Gap({ value, bound, target, stretch, scale, format, tone, titles, label }: GapProps): import("react").JSX.Element;
304
+ //#endregion
280
305
  //#region src/react/figure-view.d.ts
281
306
  /** A figure drawn for a width: FigureView calls it with its container's measured width (at least 720 px). */
282
307
  type FigureAtWidth = (width: number) => Figure | Refusal;
@@ -391,4 +416,4 @@ interface FlowWaterfallProps {
391
416
  * Data remains producer-owned; playback, zoom and folding change presentation only. */
392
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;
393
418
  //#endregion
394
- export type { BandKind, ChartBand, ChartMark, DotMapLabel, DotMapProps, DotMapRect, Figure, FigureAtWidth, FigureViewProps, FlowWaterfallProps, 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 };
419
+ 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 };
@@ -1765,6 +1765,118 @@ function Gantt({ rows, lanes: laneInput, colors, axis, span, marks = [], now, ha
1765
1765
  });
1766
1766
  }
1767
1767
  //#endregion
1768
+ //#region src/react/gap.tsx
1769
+ /** One reading against its target band on one scale: how far a value sits from where it should be. Values, words and
1770
+ * colors belong to callers; the chart only places them. */
1771
+ const HEIGHT = 24;
1772
+ const MID = HEIGHT / 2;
1773
+ function Gap({ value, bound = null, target, stretch = null, scale = "log", format = formatCompact, tone = "var(--chart-live-accent, #8b7cf6)", titles = {}, label }) {
1774
+ const [ref, width] = useWidth();
1775
+ const log = scale === "log" && value > 0 && target.min > 0;
1776
+ const f = (v) => log ? Math.log10(v) : v;
1777
+ const ends = [
1778
+ value,
1779
+ target.min,
1780
+ target.max,
1781
+ ...stretch ? [stretch.min, stretch.max] : []
1782
+ ].map(f);
1783
+ const lo = Math.min(...ends);
1784
+ const hi = Math.max(...ends);
1785
+ const pad = Math.max((hi - lo) * .08, log ? .15 : Math.abs(hi) * .05 || 1);
1786
+ const x = (v) => width ? (f(v) - (lo - pad)) / (hi - lo + 2 * pad) * width : 0;
1787
+ const band = (b) => ({
1788
+ x: x(b.min),
1789
+ w: Math.max(4, x(b.max) - x(b.min))
1790
+ });
1791
+ const t = band(target);
1792
+ const s = stretch ? band(stretch) : null;
1793
+ const near = value < target.min ? x(target.min) : value > target.max ? x(target.max) : x(value);
1794
+ const vx = x(value);
1795
+ const range = (b) => b.min === b.max ? `≈${format(b.min)}` : `${format(b.min)}–${format(b.max)}`;
1796
+ const decades = log && width ? Array.from({ length: Math.floor(hi + pad) - Math.ceil(lo - pad) + 1 }, (_, i) => Math.ceil(lo - pad) + i) : [];
1797
+ const arrow = bound === "upper" ? `M${vx + 4},6 L${vx - 4},${MID} L${vx + 4},18` : `M${vx - 4},6 L${vx + 4},${MID} L${vx - 4},18`;
1798
+ return /* @__PURE__ */ jsx("div", {
1799
+ ref,
1800
+ className: "ov-gap",
1801
+ role: "img",
1802
+ "aria-label": label ?? `${bound === "upper" ? "≤" : bound === "lower" ? "≥" : ""}${format(value)} against ${range(target)}`,
1803
+ children: width > 0 && /* @__PURE__ */ jsxs("svg", {
1804
+ width,
1805
+ height: HEIGHT,
1806
+ viewBox: `0 0 ${width} ${HEIGHT}`,
1807
+ "aria-hidden": true,
1808
+ children: [
1809
+ /* @__PURE__ */ jsx("line", {
1810
+ className: "ov-gap-track",
1811
+ x1: 0,
1812
+ x2: width,
1813
+ y1: MID,
1814
+ y2: MID
1815
+ }),
1816
+ decades.map((d) => /* @__PURE__ */ jsx("line", {
1817
+ className: "ov-gap-tick",
1818
+ x1: x(10 ** d),
1819
+ x2: x(10 ** d),
1820
+ y1: 8,
1821
+ y2: 16
1822
+ }, d)),
1823
+ s && /* @__PURE__ */ jsx("rect", {
1824
+ className: "ov-gap-stretch",
1825
+ x: s.x,
1826
+ y: 4,
1827
+ width: s.w,
1828
+ height: 16,
1829
+ rx: 4,
1830
+ children: /* @__PURE__ */ jsx("title", { children: titles.stretch ?? `stretch ${range(stretch)}` })
1831
+ }),
1832
+ /* @__PURE__ */ jsx("rect", {
1833
+ className: "ov-gap-target",
1834
+ x: t.x,
1835
+ y: 4,
1836
+ width: t.w,
1837
+ height: 16,
1838
+ rx: 4,
1839
+ children: /* @__PURE__ */ jsx("title", { children: titles.target ?? `target ${range(target)}` })
1840
+ }),
1841
+ Math.abs(near - vx) > 1 && /* @__PURE__ */ jsx("line", {
1842
+ className: "ov-gap-span",
1843
+ x1: vx,
1844
+ x2: near,
1845
+ y1: MID,
1846
+ y2: MID,
1847
+ style: { stroke: tone }
1848
+ }),
1849
+ /* @__PURE__ */ jsxs("g", {
1850
+ className: "ov-gap-value",
1851
+ children: [
1852
+ /* @__PURE__ */ jsx("title", { children: titles.value ?? `${bound === "upper" ? "≤" : bound === "lower" ? "≥" : ""}${format(value)}` }),
1853
+ bound ? /* @__PURE__ */ jsx("path", {
1854
+ d: arrow,
1855
+ fill: "none",
1856
+ stroke: tone,
1857
+ strokeWidth: 2,
1858
+ strokeLinecap: "round",
1859
+ strokeLinejoin: "round"
1860
+ }) : /* @__PURE__ */ jsx("circle", {
1861
+ cx: vx,
1862
+ cy: MID,
1863
+ r: 5,
1864
+ fill: tone
1865
+ }),
1866
+ /* @__PURE__ */ jsx("rect", {
1867
+ x: vx - 8,
1868
+ y: 0,
1869
+ width: 16,
1870
+ height: HEIGHT,
1871
+ fill: "transparent"
1872
+ })
1873
+ ]
1874
+ })
1875
+ ]
1876
+ })
1877
+ });
1878
+ }
1879
+ //#endregion
1768
1880
  //#region src/react/figure-view.tsx
1769
1881
  /** Embeds a static Figure or Refusal in React: the wide or phone render chosen by the container's width, the data
1770
1882
  * table in a disclosure. The SVG and table strings come from this library's figure functions, which escape input. */
@@ -2663,7 +2775,8 @@ function agentFamilies(agents, open) {
2663
2775
  detail: members[0].span.detail,
2664
2776
  meta: {
2665
2777
  failedAgents: failed,
2666
- costPartial: known.length < members.length
2778
+ costPartial: known.length < members.length,
2779
+ members: members.map((row) => row.id)
2667
2780
  }
2668
2781
  }
2669
2782
  });
@@ -2780,6 +2893,16 @@ function stepRuns(steps) {
2780
2893
  }
2781
2894
  return out;
2782
2895
  }
2896
+ /** A row as the view draws it: its drawn copy, or for a member of a folded family, itself in the family's hue. */
2897
+ function drawnRow(row, visible) {
2898
+ const drawn = visible.find((item) => item.id === row.id);
2899
+ if (drawn) return drawn;
2900
+ const family = visible.find((item) => (item.span.meta?.members)?.includes(row.id));
2901
+ return family ? {
2902
+ ...row,
2903
+ tone: family.tone
2904
+ } : row;
2905
+ }
2783
2906
  //#endregion
2784
2907
  //#region src/react/flow-waterfall-icons.tsx
2785
2908
  const svg = (children, label) => /* @__PURE__ */ jsx("svg", {
@@ -4550,7 +4673,7 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
4550
4673
  const busy = agents.reduce((sum, row) => sum + busyMs(row, limit), 0);
4551
4674
  const speedup = limit > 0 ? busy / limit : 0;
4552
4675
  const done = !isLive && !trace.spans.some(spanRunning);
4553
- const shown = (row) => row && (visible.find((item) => item.id === row.id) ?? row);
4676
+ const shown = (row) => row && drawnRow(row, visible);
4554
4677
  const cardRow = shown(card ? byId.get(card.target) : void 0);
4555
4678
  const cardAgent = cardRow && (() => {
4556
4679
  for (let cursor = cardRow; cursor; cursor = cursor.parentId ? byId.get(cursor.parentId) : void 0) if (cursor.kind === "pipeline") return shown(cursor);
@@ -4781,4 +4904,4 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
4781
4904
  });
4782
4905
  }
4783
4906
  //#endregion
4784
- export { DEFAULT_SPARKLINE_EMPTY_LABEL, DEFAULT_SPARKLINE_HEIGHT, DEFAULT_SPARKLINE_LABEL, DEFAULT_SPARKLINE_UNAVAILABLE_LABEL, DEFAULT_SPARKLINE_WIDTH, DOT_MAP_CATEGORIES, DayBars, DotMap, Events, FigureView, FlowWaterfall, Gantt, HBars, Histogram, LIVE_SERIES, Lines, Meter, Spark, Sparkline, StackedBarChart, TimelineView, WaterfallBar, WaterfallHeader, WaterfallRow, WaterfallTimeAxis, formatCompact, formatSparklineValue, formatWaterfallDuration, sparklineGeometry, sparklineLabel, sparklinePointsAttribute, sparklineReadings, waterfallSpanGeometry, waterfallTicks };
4907
+ export { DEFAULT_SPARKLINE_EMPTY_LABEL, DEFAULT_SPARKLINE_HEIGHT, DEFAULT_SPARKLINE_LABEL, DEFAULT_SPARKLINE_UNAVAILABLE_LABEL, DEFAULT_SPARKLINE_WIDTH, DOT_MAP_CATEGORIES, DayBars, DotMap, Events, FigureView, FlowWaterfall, Gantt, Gap, HBars, Histogram, LIVE_SERIES, Lines, Meter, Spark, Sparkline, StackedBarChart, TimelineView, WaterfallBar, WaterfallHeader, WaterfallRow, WaterfallTimeAxis, formatCompact, formatSparklineValue, formatWaterfallDuration, sparklineGeometry, sparklineLabel, sparklinePointsAttribute, sparklineReadings, waterfallSpanGeometry, waterfallTicks };
@@ -89,3 +89,11 @@ line.ov-gridline { stroke: color-mix(in srgb, var(--chart-live-faint, #777) 22%,
89
89
  .tgc-dot-map { color: var(--chart-live-muted, #8a8a8a); }
90
90
  .tgc-dot-map canvas:focus-visible { outline: 2px solid var(--chart-accent, #7760bd); outline-offset: 2px; }
91
91
  .tgc-dot-map-tip { z-index: 2; max-width: 300px; padding: 6px 9px; border-radius: 6px; background: var(--chart-tip-bg, #262626); color: var(--chart-tip-ink, #eee); font: 12px/1.4 ui-sans-serif, system-ui, sans-serif; white-space: pre-line; box-shadow: 0 4px 14px rgb(0 0 0 / .3); }
92
+ .ov-gap { width: 100%; min-width: 96px; height: 24px; }
93
+ .ov-gap svg { display: block; overflow: visible; }
94
+ .ov-gap-track { stroke: color-mix(in srgb, var(--chart-live-faint, #777) 40%, transparent); stroke-width: 1; }
95
+ .ov-gap-tick { stroke: color-mix(in srgb, var(--chart-live-faint, #777) 60%, transparent); stroke-width: 1; }
96
+ .ov-gap-target { fill: color-mix(in srgb, var(--chart-live-pass, #2f9e6e) 28%, transparent); stroke: var(--chart-live-pass, #2f9e6e); stroke-width: 1; }
97
+ .ov-gap-stretch { fill: none; stroke: var(--chart-live-pass, #2f9e6e); stroke-width: 1; stroke-dasharray: 3 3; }
98
+ .ov-gap-span { stroke-width: 2; stroke-dasharray: 2 4; stroke-linecap: round; }
99
+ .ov-gap-value { cursor: default; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/charts",
3
- "version": "0.20.2",
3
+ "version": "0.21.0",
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",