@tangle-network/charts 2.0.9 → 2.0.12

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.
@@ -223,7 +223,11 @@ interface MeterSegment {
223
223
  }
224
224
  /** A used-of-total meter with its numbers. With `segments` it is a part-of-whole bar: the remainder of `total` that no
225
225
  * segment accounts for is drawn hatched and labelled `untraced`, and a missing total hatches the whole track. */
226
- export declare function Meter({ used, total, format, tone, text, segments, untraced }: {
226
+ export declare function Meter({ used, total, format, tone, text, segments, steps, untraced }: {
227
+ steps?: {
228
+ label: string;
229
+ state: 'pass' | 'fail' | 'undecided' | 'unrun';
230
+ }[];
227
231
  used?: number | null | undefined;
228
232
  total: number | null | undefined;
229
233
  format?: (v: number) => string;
@@ -260,7 +264,11 @@ export declare function Events({ at, from, to, label, color, ticks, axis, time }
260
264
  * state; hovering a span names it, its state and its span. Times are epoch ms; `axis` labels the time axis.
261
265
  * `lanes` give a row several spans (the same shape as the static `timeline`); a span with `end: null` is open and runs
262
266
  * to `now`. `marks` draw recorded events across every lane, and states in `hatch` are drawn hatched (no data). */
263
- export declare function Gantt({ rows, lanes: laneInput, colors, axis, span, marks, now, hatch }: {
267
+ export declare function Gantt({ rows, lanes: laneInput, colors, axis, span, marks, now, hatch, selectedId, onSelect, bestMark, maxHeight }: {
268
+ selectedId?: string;
269
+ onSelect?: (id: string) => void;
270
+ bestMark?: string;
271
+ maxHeight?: number;
264
272
  rows?: {
265
273
  label: string;
266
274
  start: number;
@@ -1385,7 +1385,19 @@ const SEGMENT_TONES = [
1385
1385
  ];
1386
1386
  /** A used-of-total meter with its numbers. With `segments` it is a part-of-whole bar: the remainder of `total` that no
1387
1387
  * segment accounts for is drawn hatched and labelled `untraced`, and a missing total hatches the whole track. */
1388
- function Meter({ used, total, format = formatCompact, tone, text, segments, untraced = "untraced" }) {
1388
+ function Meter({ used, total, format = formatCompact, tone, text, segments, steps, untraced = "untraced" }) {
1389
+ if (steps) {
1390
+ const current = steps.findIndex((step) => step.state !== "pass");
1391
+ return /* @__PURE__ */ jsx("ol", {
1392
+ className: "ov-meter-steps",
1393
+ "aria-label": text ?? "Check steps",
1394
+ children: steps.map((step, i) => /* @__PURE__ */ jsxs("li", {
1395
+ "data-state": step.state,
1396
+ "aria-current": i === current ? "step" : void 0,
1397
+ children: [/* @__PURE__ */ jsx("strong", { children: i + 1 }), /* @__PURE__ */ jsx("span", { children: step.label })]
1398
+ }, i))
1399
+ });
1400
+ }
1389
1401
  if (segments) return /* @__PURE__ */ jsx(SegmentMeter, {
1390
1402
  segments,
1391
1403
  total,
@@ -1586,9 +1598,11 @@ function Events({ at, from, to, label, color = LIVE_SERIES[0], ticks = 7, axis =
1586
1598
  * state; hovering a span names it, its state and its span. Times are epoch ms; `axis` labels the time axis.
1587
1599
  * `lanes` give a row several spans (the same shape as the static `timeline`); a span with `end: null` is open and runs
1588
1600
  * to `now`. `marks` draw recorded events across every lane, and states in `hatch` are drawn hatched (no data). */
1589
- function Gantt({ rows, lanes: laneInput, colors, axis, span, marks = [], now, hatch = ["missing", "untraced"] }) {
1601
+ function Gantt({ rows, lanes: laneInput, colors, axis, span, marks = [], now, hatch = ["missing", "untraced"], selectedId, onSelect, bestMark, maxHeight }) {
1590
1602
  const [ref, width, em] = useWidth();
1591
1603
  const [tip, setTip] = useState(null);
1604
+ const scroller = useRef(null);
1605
+ const [scrollTop, setScrollTop] = useState(0);
1592
1606
  const lanes = laneInput ?? (rows ?? []).map((row, i) => ({
1593
1607
  id: `${row.label}-${i}`,
1594
1608
  label: row.label,
@@ -1612,9 +1626,21 @@ function Gantt({ rows, lanes: laneInput, colors, axis, span, marks = [], now, ha
1612
1626
  children: "No agent has started."
1613
1627
  });
1614
1628
  const ends = (s) => s.end ?? now;
1615
- const rowH = Math.round(1.25 * em);
1616
- const labelW = Math.min(Math.round(13 * em), Math.round(width * .32));
1617
- const top = marks.length ? 4 + Math.round(Math.min(3, marks.length) * em + .3 * em) : 4;
1629
+ const phone = width < 600;
1630
+ const rowH = Math.round((phone ? 2.8 : 2.2) * em);
1631
+ const labelW = phone ? 8 : Math.min(Math.round(16 * em), Math.round(width * .28));
1632
+ const top = maxHeight ? 4 : marks.length ? 48 : 8;
1633
+ const fractions = phone ? [
1634
+ 0,
1635
+ .5,
1636
+ 1
1637
+ ] : [
1638
+ 0,
1639
+ .25,
1640
+ .5,
1641
+ .75,
1642
+ 1
1643
+ ];
1618
1644
  const bottom = Math.round(1.8 * em);
1619
1645
  const height = top + lanes.length * rowH + bottom;
1620
1646
  const times = [
@@ -1629,131 +1655,233 @@ function Gantt({ rows, lanes: laneInput, colors, axis, span, marks = [], now, ha
1629
1655
  const fallback = colors.unknown ?? "var(--chart-live-faint, var(--chart-muted))";
1630
1656
  const hatched = new Set(hatch);
1631
1657
  const plotBottom = top + lanes.length * rowH;
1658
+ const first = maxHeight && lanes.length > 100 ? Math.max(0, Math.floor((scrollTop - top) / rowH) - 6) : 0;
1659
+ const visible = maxHeight && lanes.length > 100 ? lanes.slice(first, first + Math.ceil(maxHeight / rowH) + 14) : lanes;
1660
+ const select = (id) => onSelect?.(id);
1661
+ const focusLane = (index) => {
1662
+ const at = Math.max(0, Math.min(lanes.length - 1, index));
1663
+ const position = at === 0 ? 0 : top + at * rowH - rowH;
1664
+ if (scroller.current) scroller.current.scrollTop = position;
1665
+ setScrollTop(Math.max(0, position));
1666
+ requestAnimationFrame(() => scroller.current?.querySelector(`[data-lane-index="${at}"]`)?.focus());
1667
+ };
1668
+ const tipY = (y) => Math.max(0, y - (maxHeight ? scrollTop - 52 : 0) - em);
1669
+ const tipX = (x) => Math.max(0, Math.min(x + 8, width - Math.min(240, width)));
1632
1670
  return /* @__PURE__ */ jsxs("div", {
1633
1671
  ref,
1634
- className: "ov-plot",
1672
+ className: "ov-plot ov-gantt",
1673
+ "data-featured": bestMark ? true : void 0,
1635
1674
  onMouseLeave: () => setTip(null),
1636
1675
  children: [
1637
- width > 0 && /* @__PURE__ */ jsxs("svg", {
1676
+ maxHeight && width > 0 && /* @__PURE__ */ jsxs("svg", {
1638
1677
  width,
1639
- height,
1640
- role: "img",
1641
- "aria-label": `${lanes.length} ${laneInput ? "lanes" : "agents"} over time`,
1642
- children: [
1643
- [
1644
- 0,
1645
- .25,
1646
- .5,
1647
- .75,
1648
- 1
1649
- ].map((f) => /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("line", {
1650
- x1: labelW + f * plotW,
1651
- x2: labelW + f * plotW,
1652
- y1: top,
1653
- y2: plotBottom,
1654
- className: "ov-gridline"
1655
- }), /* @__PURE__ */ jsx("text", {
1656
- x: labelW + f * plotW,
1657
- y: height - em * .35,
1658
- className: "ov-axis",
1659
- textAnchor: f === 0 ? "start" : f === 1 ? "end" : "middle",
1660
- children: axis(t0 + f * (t1 - t0))
1661
- })] }, f)),
1662
- lanes.map((lane, i) => {
1663
- const y = top + i * rowH;
1664
- return /* @__PURE__ */ jsxs("g", {
1665
- "data-lane": lane.id,
1666
- children: [
1667
- /* @__PURE__ */ jsx("rect", {
1668
- x: 0,
1669
- y,
1670
- width,
1671
- height: rowH,
1672
- fill: "transparent",
1673
- onMouseEnter: () => setTip({
1674
- x: Math.min(x(ends(lane.spans.at(-1) ?? { end: t1 })) + 8, width - em * 16),
1675
- y: Math.max(0, y - em),
1676
- title: lane.label,
1677
- lines: lane.spans.flatMap((s) => [{
1678
- label: s.end === null ? `${s.state} (open)` : s.state,
1679
- value: span(s.start, ends(s)),
1680
- color: hatched.has(s.state) ? void 0 : colors[s.state] ?? fallback
1681
- }, ...s.note ? [{
1682
- label: s.note,
1683
- value: ""
1684
- }] : []])
1685
- })
1686
- }),
1687
- /* @__PURE__ */ jsx("text", {
1688
- x: labelW - em * .5,
1689
- y: y + rowH / 2,
1690
- dominantBaseline: "middle",
1691
- textAnchor: "end",
1692
- className: "ov-axis ov-gantt-label",
1693
- children: lane.label.length > 26 ? `${lane.label.slice(0, 25)}…` : lane.label
1694
- }),
1695
- lane.spans.map((s, j) => {
1696
- const color = colors[s.state] ?? fallback;
1697
- const bx = x(s.start), bw = Math.max(2, x(ends(s)) - bx), by = y + rowH * .18, bh = rowH * .64;
1698
- const open = s.end === null;
1699
- return /* @__PURE__ */ jsx("g", {
1700
- "data-state": s.state,
1701
- "data-open": open ? "true" : void 0,
1702
- "data-hatched": hatched.has(s.state) ? "true" : void 0,
1678
+ height: 52,
1679
+ "aria-label": "Releases and time axis",
1680
+ children: [marks.map((mark, i) => /* @__PURE__ */ jsxs("g", {
1681
+ "data-best": mark.label === bestMark || void 0,
1682
+ children: [/* @__PURE__ */ jsx(Mark, {
1683
+ mark,
1684
+ x: x(mark.at),
1685
+ top: 10,
1686
+ bottom: 28,
1687
+ row: 0,
1688
+ em,
1689
+ flip: x(mark.at) > width * .62
1690
+ }), mark.label === bestMark && /* @__PURE__ */ jsx("circle", {
1691
+ cx: x(mark.at),
1692
+ cy: 14,
1693
+ r: 8,
1694
+ fill: "none",
1695
+ stroke: "var(--accent-text)",
1696
+ strokeWidth: 2
1697
+ })]
1698
+ }, i)), fractions.map((f) => /* @__PURE__ */ jsx("text", {
1699
+ x: labelW + f * plotW,
1700
+ y: 46,
1701
+ className: "ov-axis",
1702
+ textAnchor: f === 0 ? "start" : f === 1 ? "end" : "middle",
1703
+ children: axis(t0 + f * (t1 - t0))
1704
+ }, f))]
1705
+ }),
1706
+ /* @__PURE__ */ jsx("div", {
1707
+ ref: scroller,
1708
+ className: "ov-gantt-scroll",
1709
+ style: {
1710
+ maxHeight,
1711
+ overflow: maxHeight ? "auto" : void 0
1712
+ },
1713
+ onScroll: (event) => {
1714
+ setScrollTop(event.currentTarget.scrollTop);
1715
+ setTip(null);
1716
+ },
1717
+ children: width > 0 && /* @__PURE__ */ jsxs("svg", {
1718
+ width,
1719
+ height,
1720
+ role: onSelect ? "group" : "img",
1721
+ "aria-label": `${lanes.length} ${laneInput ? "lanes" : "agents"} over time`,
1722
+ children: [
1723
+ fractions.map((f) => /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("line", {
1724
+ x1: labelW + f * plotW,
1725
+ x2: labelW + f * plotW,
1726
+ y1: top,
1727
+ y2: plotBottom,
1728
+ className: "ov-gridline"
1729
+ }), !maxHeight && /* @__PURE__ */ jsx("text", {
1730
+ x: labelW + f * plotW,
1731
+ y: height - em * .35,
1732
+ className: "ov-axis",
1733
+ textAnchor: f === 0 ? "start" : f === 1 ? "end" : "middle",
1734
+ children: axis(t0 + f * (t1 - t0))
1735
+ })] }, f)),
1736
+ visible.map((lane, offset) => {
1737
+ const i = first + offset, y = top + i * rowH;
1738
+ return /* @__PURE__ */ jsxs("g", {
1739
+ "data-lane": lane.id,
1740
+ "data-lane-index": i,
1741
+ role: onSelect ? "button" : void 0,
1742
+ tabIndex: onSelect ? 0 : void 0,
1743
+ "aria-label": lane.label,
1744
+ "aria-pressed": onSelect ? selectedId === lane.id : void 0,
1745
+ onClick: () => select(lane.id),
1746
+ onKeyDown: (event) => {
1747
+ if (event.key === "Enter" || event.key === " ") {
1748
+ event.preventDefault();
1749
+ select(lane.id);
1750
+ }
1751
+ const next = {
1752
+ ArrowDown: i + 1,
1753
+ ArrowUp: i - 1,
1754
+ Home: 0,
1755
+ End: lanes.length - 1
1756
+ }[event.key];
1757
+ if (next !== void 0) {
1758
+ event.preventDefault();
1759
+ focusLane(next);
1760
+ }
1761
+ },
1762
+ children: [
1763
+ selectedId === lane.id && /* @__PURE__ */ jsx("rect", {
1764
+ x: 0,
1765
+ y,
1766
+ width,
1767
+ height: rowH,
1768
+ fill: "var(--ix-sel-bg)",
1769
+ rx: 4
1770
+ }),
1771
+ /* @__PURE__ */ jsx("rect", {
1772
+ x: 0,
1773
+ y,
1774
+ width,
1775
+ height: rowH,
1776
+ fill: "transparent",
1703
1777
  onMouseEnter: () => setTip({
1704
- x: Math.min(bx + bw + 8, width - em * 16),
1705
- y: Math.max(0, y - em),
1778
+ x: tipX(x(ends(lane.spans.at(-1) ?? { end: t1 }))),
1779
+ y: tipY(y),
1706
1780
  title: lane.label,
1707
- lines: [{
1708
- label: open ? `${s.state} (open)` : s.state,
1781
+ lines: lane.spans.flatMap((s) => [{
1782
+ label: s.end === null ? `${s.state} (open)` : s.state,
1709
1783
  value: span(s.start, ends(s)),
1710
- color: hatched.has(s.state) ? void 0 : color
1784
+ color: hatched.has(s.state) ? void 0 : colors[s.state] ?? fallback
1711
1785
  }, ...s.note ? [{
1712
1786
  label: s.note,
1713
1787
  value: ""
1714
- }] : []]
1715
- }),
1716
- children: hatched.has(s.state) ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("rect", {
1717
- x: bx,
1718
- y: by,
1719
- width: bw,
1720
- height: bh,
1721
- className: "ov-hatch-bg"
1722
- }), /* @__PURE__ */ jsx("path", {
1723
- d: hatchPath(bx, by, bw, bh, 4),
1724
- className: "ov-hatch"
1725
- })] }) : /* @__PURE__ */ jsx("rect", {
1726
- x: bx,
1727
- y: by,
1728
- width: bw,
1729
- height: bh,
1730
- rx: 2,
1731
- fill: color,
1732
- className: open ? "ov-gantt-open" : void 0
1788
+ }] : []])
1733
1789
  })
1734
- }, j);
1790
+ }),
1791
+ /* @__PURE__ */ jsxs("text", {
1792
+ x: phone ? 8 : labelW - em * .5,
1793
+ y: phone ? y + 13 : y + rowH / 2,
1794
+ dominantBaseline: "middle",
1795
+ textAnchor: phone ? "start" : "end",
1796
+ className: "ov-axis ov-gantt-label",
1797
+ children: [/* @__PURE__ */ jsx("title", { children: lane.label }), lane.label.length > 26 ? `${lane.label.slice(0, 25)}…` : lane.label]
1798
+ }),
1799
+ lane.spans.map((s, j) => {
1800
+ const color = colors[s.state] ?? fallback;
1801
+ const bx = x(s.start), bw = Math.max(2, x(ends(s)) - bx), by = y + (phone ? 24 : rowH * .25), bh = rowH * .5;
1802
+ const open = s.end === null;
1803
+ return /* @__PURE__ */ jsx("g", {
1804
+ "data-state": s.state,
1805
+ "data-open": open ? "true" : void 0,
1806
+ "data-hatched": hatched.has(s.state) ? "true" : void 0,
1807
+ onMouseEnter: () => setTip({
1808
+ x: tipX(bx + bw),
1809
+ y: tipY(y),
1810
+ title: lane.label,
1811
+ lines: [{
1812
+ label: open ? `${s.state} (open)` : s.state,
1813
+ value: span(s.start, ends(s)),
1814
+ color: hatched.has(s.state) ? void 0 : color
1815
+ }, ...s.note ? [{
1816
+ label: s.note,
1817
+ value: ""
1818
+ }] : []]
1819
+ }),
1820
+ children: hatched.has(s.state) ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("rect", {
1821
+ x: bx,
1822
+ y: by,
1823
+ width: bw,
1824
+ height: bh,
1825
+ className: "ov-hatch-bg"
1826
+ }), /* @__PURE__ */ jsx("path", {
1827
+ d: hatchPath(bx, by, bw, bh, 4),
1828
+ className: "ov-hatch"
1829
+ })] }) : /* @__PURE__ */ jsx("rect", {
1830
+ x: bx,
1831
+ y: by,
1832
+ width: bw,
1833
+ height: bh,
1834
+ rx: 2,
1835
+ fill: color,
1836
+ className: open ? "ov-gantt-open" : void 0
1837
+ })
1838
+ }, j);
1839
+ })
1840
+ ]
1841
+ }, lane.id);
1842
+ }),
1843
+ now !== void 0 && /* @__PURE__ */ jsx("line", {
1844
+ x1: x(now),
1845
+ x2: x(now),
1846
+ y1: top,
1847
+ y2: plotBottom,
1848
+ className: "ov-now",
1849
+ "data-now": "true"
1850
+ }),
1851
+ [...marks].sort((a, b) => a.at - b.at).map((mark, i) => /* @__PURE__ */ jsxs("g", {
1852
+ className: mark.verdict ? `ov-verdict-${mark.verdict}` : void 0,
1853
+ "data-best": mark.label === bestMark || void 0,
1854
+ style: maxHeight ? { pointerEvents: "none" } : void 0,
1855
+ children: [
1856
+ maxHeight && /* @__PURE__ */ jsx("line", {
1857
+ x1: x(mark.at),
1858
+ x2: x(mark.at),
1859
+ y1: top,
1860
+ y2: plotBottom,
1861
+ className: `ov-mark ov-mark-${mark.kind}`,
1862
+ style: { opacity: mark.label === bestMark ? .8 : .3 }
1863
+ }),
1864
+ !maxHeight && /* @__PURE__ */ jsx(Mark, {
1865
+ mark,
1866
+ x: x(mark.at),
1867
+ top: 4,
1868
+ bottom: plotBottom,
1869
+ row: i % 2,
1870
+ em,
1871
+ flip: x(mark.at) > labelW + plotW * .62
1872
+ }),
1873
+ !maxHeight && mark.label === bestMark && /* @__PURE__ */ jsx("circle", {
1874
+ cx: x(mark.at),
1875
+ cy: 4 + Math.max(3, em / 4),
1876
+ r: 8,
1877
+ fill: "none",
1878
+ stroke: "var(--accent-text)",
1879
+ strokeWidth: 2
1735
1880
  })
1736
1881
  ]
1737
- }, lane.id);
1738
- }),
1739
- now !== void 0 && /* @__PURE__ */ jsx("line", {
1740
- x1: x(now),
1741
- x2: x(now),
1742
- y1: top,
1743
- y2: plotBottom,
1744
- className: "ov-now",
1745
- "data-now": "true"
1746
- }),
1747
- [...marks].sort((a, b) => a.at - b.at).map((mark, i) => /* @__PURE__ */ jsx(Mark, {
1748
- mark,
1749
- x: x(mark.at),
1750
- top: 4,
1751
- bottom: plotBottom,
1752
- row: i % 3,
1753
- em,
1754
- flip: x(mark.at) > labelW + plotW * .62
1755
- }, `mark-${i}`))
1756
- ]
1882
+ }, `mark-${i}`))
1883
+ ]
1884
+ })
1757
1885
  }),
1758
1886
  /* @__PURE__ */ jsx(Tooltip, { tip }),
1759
1887
  /* @__PURE__ */ jsx(EventList, {
@@ -9088,8 +9216,8 @@ function FlowWaterfall({ trace, live, startedAt, label = "Trace waterfall", defa
9088
9216
  if (view !== "agents" || resized.current || !root.current) return;
9089
9217
  let widest = 0;
9090
9218
  for (const text of root.current.querySelectorAll(".fw-view-agents .fw-row:not(.fw-sticky-row) .fw-name-text")) {
9091
- const label = text.closest(".fw-label");
9092
- if (label) widest = Math.max(widest, text.scrollWidth + label.clientWidth - text.clientWidth);
9219
+ const label = text.closest(".fw-label"), name = text.parentElement;
9220
+ if (label) widest = Math.max(widest, name.getBoundingClientRect().right - label.getBoundingClientRect().left + text.scrollWidth - text.clientWidth);
9093
9221
  }
9094
9222
  if (widest > 0) setNameWidth(Math.max(140, Math.min(560, Math.ceil(widest) + 16)));
9095
9223
  }, [view, agents]);
@@ -97,3 +97,28 @@ line.ov-gridline { stroke: color-mix(in srgb, var(--chart-live-faint, #777) 22%,
97
97
  .ov-gap-stretch { fill: none; stroke: var(--chart-live-pass, #2f9e6e); stroke-width: 1; stroke-dasharray: 3 3; }
98
98
  .ov-gap-span { stroke-width: 2; stroke-dasharray: 2 4; stroke-linecap: round; }
99
99
  .ov-gap-value { cursor: default; }
100
+
101
+ .ov-gantt { font: 14px/20px var(--font-sans, sans-serif); }
102
+ .ov-gantt .ov-axis { fill: var(--chart-live-muted, var(--text-muted, #666)); font-size: 13px; }
103
+ .ov-gantt .ov-mark-label { fill: var(--chart-live-fg, var(--text-primary, currentColor)); font-size: 13px; }
104
+ .ov-gantt .ov-tip { background: var(--chart-live-raised, var(--bg-elevated, #fff)); border-color: var(--chart-live-border, var(--border-color, #aaa)); max-width: min(240px, 100%); box-sizing: border-box; }
105
+ .ov-gantt-scroll { overscroll-behavior: contain; }
106
+ .ov-gantt [role='button'] { cursor: pointer; outline: none; }
107
+ .ov-gantt [role='button']:hover > rect:first-child { fill: var(--ix-hover-bg); }
108
+ .ov-gantt [role='button']:focus-visible > rect:first-child { stroke: var(--accent-text); stroke-width: 2; }
109
+ .ov-gantt [data-state] rect { transition: width var(--motion-stream), fill var(--motion-control); }
110
+ .ov-gantt [data-open='true'] rect { animation: ov-gantt-live var(--motion-ambient) infinite; }
111
+ .ov-gantt[data-featured] [data-mark] text { display: none; }
112
+ .ov-gantt[data-featured] [data-best] text { display: block; }
113
+ .ov-gantt[data-featured] .ov-events { display: none; }
114
+ @keyframes ov-gantt-live { 50% { opacity: .55; } }
115
+ @media (prefers-reduced-motion: reduce) { .ov-gantt [data-state] rect { transition: none; animation: none; } }
116
+
117
+ .ov-meter-steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 6px; padding: 0; margin: 0; list-style: none; }
118
+ .ov-meter-steps li { display: flex; flex-direction: column; justify-content: space-between; gap: 12px; min-height: 104px; padding: 16px; border-radius: var(--radius-lg); background: var(--bg-elevated); }
119
+ .ov-meter-steps strong { font-size: 26px; line-height: 30px; }
120
+ .ov-meter-steps [data-state='pass'] { background: var(--surface-success-bg); }
121
+ .ov-meter-steps [aria-current='step'] { background: var(--surface-warning-bg); color: var(--surface-warning-text); }
122
+
123
+ .ov-meter-steps span { overflow-wrap: anywhere; }
124
+ @media (max-width: 720px) { .ov-meter-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); } .ov-meter-steps li { padding: 12px; min-height: 94px; } }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/charts",
3
- "version": "2.0.9",
3
+ "version": "2.0.12",
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",