liveline 0.0.5 → 0.0.7

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.
package/dist/index.cjs CHANGED
@@ -88,6 +88,33 @@ function resolveTheme(color, mode) {
88
88
  badgeFont: '500 11px "SF Mono", Menlo, monospace'
89
89
  };
90
90
  }
91
+ var SERIES_COLORS = [
92
+ "#3b82f6",
93
+ // blue
94
+ "#ef4444",
95
+ // red
96
+ "#22c55e",
97
+ // green
98
+ "#f59e0b",
99
+ // amber
100
+ "#8b5cf6",
101
+ // violet
102
+ "#ec4899",
103
+ // pink
104
+ "#06b6d4",
105
+ // cyan
106
+ "#f97316"
107
+ // orange
108
+ ];
109
+ function resolveSeriesPalettes(series, mode) {
110
+ const map = /* @__PURE__ */ new Map();
111
+ for (let i = 0; i < series.length; i++) {
112
+ const s = series[i];
113
+ const color = s.color || SERIES_COLORS[i % SERIES_COLORS.length];
114
+ map.set(s.id, resolveTheme(color, mode));
115
+ }
116
+ return map;
117
+ }
91
118
 
92
119
  // src/useLivelineEngine.ts
93
120
  var import_react = require("react");
@@ -513,6 +540,33 @@ function drawDot(ctx, x, y, palette, pulse = true, scrubAmount = 0, now_ms = per
513
540
  }
514
541
  ctx.fill();
515
542
  }
543
+ function drawMultiDot(ctx, x, y, color, pulse = true, now_ms = performance.now(), radius = 3) {
544
+ const baseAlpha = ctx.globalAlpha;
545
+ if (pulse) {
546
+ const t = now_ms % PULSE_INTERVAL / PULSE_DURATION;
547
+ if (t < 1) {
548
+ const ringRadius = 9 + t * 10;
549
+ const pulseAlpha = 0.3 * (1 - t);
550
+ ctx.beginPath();
551
+ ctx.arc(x, y, ringRadius, 0, Math.PI * 2);
552
+ ctx.strokeStyle = color;
553
+ ctx.lineWidth = 1.5;
554
+ ctx.globalAlpha = baseAlpha * pulseAlpha;
555
+ ctx.stroke();
556
+ }
557
+ }
558
+ ctx.globalAlpha = baseAlpha;
559
+ ctx.beginPath();
560
+ ctx.arc(x, y, radius, 0, Math.PI * 2);
561
+ ctx.fillStyle = color;
562
+ ctx.fill();
563
+ }
564
+ function drawSimpleDot(ctx, x, y, color, radius = 3) {
565
+ ctx.beginPath();
566
+ ctx.arc(x, y, radius, 0, Math.PI * 2);
567
+ ctx.fillStyle = color;
568
+ ctx.fill();
569
+ }
516
570
  function drawArrows(ctx, x, y, momentum, palette, arrows, dt, now_ms = performance.now()) {
517
571
  const baseAlpha = ctx.globalAlpha;
518
572
  const upTarget = momentum === "up" ? 1 : 0;
@@ -611,6 +665,90 @@ function drawCrosshair(ctx, layout, palette, hoverX, hoverValue, hoverTime, form
611
665
  ctx.fillText(separator + timeText, tx + valueW, ty);
612
666
  ctx.restore();
613
667
  }
668
+ function drawMultiCrosshair(ctx, layout, palette, hoverX, hoverTime, entries, formatValue, formatTime, scrubOpacity, tooltipY, tooltipOutline, liveDotX) {
669
+ if (scrubOpacity < 0.01 || entries.length === 0) return;
670
+ const { h, pad, toY } = layout;
671
+ ctx.save();
672
+ ctx.globalAlpha = scrubOpacity * 0.5;
673
+ ctx.strokeStyle = palette.crosshairLine;
674
+ ctx.lineWidth = 1;
675
+ ctx.beginPath();
676
+ ctx.moveTo(hoverX, pad.top);
677
+ ctx.lineTo(hoverX, h - pad.bottom);
678
+ ctx.stroke();
679
+ ctx.restore();
680
+ const dotRadius = 4 * Math.min(scrubOpacity * 3, 1);
681
+ if (dotRadius > 0.5) {
682
+ ctx.globalAlpha = 1;
683
+ for (const entry of entries) {
684
+ const y = toY(entry.value);
685
+ ctx.beginPath();
686
+ ctx.arc(hoverX, y, dotRadius, 0, Math.PI * 2);
687
+ ctx.fillStyle = entry.color;
688
+ ctx.fill();
689
+ }
690
+ }
691
+ if (scrubOpacity < 0.1 || layout.w < 300) return;
692
+ ctx.save();
693
+ ctx.globalAlpha = scrubOpacity;
694
+ ctx.font = '400 13px "SF Mono", Menlo, monospace';
695
+ ctx.textAlign = "left";
696
+ const timeText = formatTime(hoverTime);
697
+ const sep = " \xB7 ";
698
+ const dotInline = " ";
699
+ const segments = [
700
+ { text: timeText, color: palette.gridLabel }
701
+ ];
702
+ for (const e of entries) {
703
+ segments.push({ text: sep, color: palette.gridLabel });
704
+ segments.push({ text: dotInline, color: e.color, isDot: true });
705
+ const label = e.label ? `${e.label} ` : "";
706
+ if (label) segments.push({ text: label, color: palette.gridLabel });
707
+ segments.push({ text: formatValue(e.value), color: palette.tooltipText });
708
+ }
709
+ let totalW = 0;
710
+ const segWidths = [];
711
+ for (const seg of segments) {
712
+ const w = seg.isDot ? 12 : ctx.measureText(seg.text).width;
713
+ segWidths.push(w);
714
+ totalW += w;
715
+ }
716
+ let tx = hoverX - totalW / 2;
717
+ const minX = pad.left + 4;
718
+ const dotRightEdge = liveDotX != null ? liveDotX + 7 : layout.w - pad.right;
719
+ const maxX = dotRightEdge - totalW;
720
+ if (tx < minX) tx = minX;
721
+ if (tx > maxX) tx = maxX;
722
+ const ty = pad.top + (tooltipY ?? 14) + 10;
723
+ if (tooltipOutline !== false) {
724
+ ctx.strokeStyle = palette.tooltipBg;
725
+ ctx.lineWidth = 3;
726
+ ctx.lineJoin = "round";
727
+ let ox2 = tx;
728
+ for (let i = 0; i < segments.length; i++) {
729
+ const seg = segments[i];
730
+ if (!seg.isDot) {
731
+ ctx.strokeText(seg.text, ox2, ty);
732
+ }
733
+ ox2 += segWidths[i];
734
+ }
735
+ }
736
+ let ox = tx;
737
+ for (let i = 0; i < segments.length; i++) {
738
+ const seg = segments[i];
739
+ if (seg.isDot) {
740
+ ctx.beginPath();
741
+ ctx.arc(ox + 4, ty - 4, 3.5, 0, Math.PI * 2);
742
+ ctx.fillStyle = seg.color;
743
+ ctx.fill();
744
+ } else {
745
+ ctx.fillStyle = seg.color;
746
+ ctx.fillText(seg.text, ox, ty);
747
+ }
748
+ ox += segWidths[i];
749
+ }
750
+ ctx.restore();
751
+ }
614
752
 
615
753
  // src/draw/referenceLine.ts
616
754
  function drawReferenceLine(ctx, layout, palette, ref) {
@@ -1457,6 +1595,124 @@ function drawFrame(ctx, layout, palette, opts) {
1457
1595
  ctx.restore();
1458
1596
  }
1459
1597
  }
1598
+ function drawMultiFrame(ctx, layout, opts) {
1599
+ const palette = opts.primaryPalette;
1600
+ const reveal = opts.chartReveal;
1601
+ const revealRamp = (start, end) => {
1602
+ const t = Math.max(0, Math.min(1, (reveal - start) / (end - start)));
1603
+ return t * t * (3 - 2 * t);
1604
+ };
1605
+ if (opts.referenceLine && reveal > 0.01) {
1606
+ ctx.save();
1607
+ if (reveal < 1) ctx.globalAlpha = reveal;
1608
+ drawReferenceLine(ctx, layout, palette, opts.referenceLine);
1609
+ ctx.restore();
1610
+ }
1611
+ if (opts.showGrid) {
1612
+ const gridAlpha = reveal < 1 ? revealRamp(0.15, 0.7) : 1;
1613
+ if (gridAlpha > 0.01) {
1614
+ ctx.save();
1615
+ if (gridAlpha < 1) ctx.globalAlpha = gridAlpha;
1616
+ drawGrid(ctx, layout, palette, opts.formatValue, opts.gridState, opts.dt);
1617
+ ctx.restore();
1618
+ }
1619
+ }
1620
+ const scrubX = opts.scrubAmount > 0.05 ? opts.hoverX : null;
1621
+ const allPts = [];
1622
+ for (let si = 0; si < opts.series.length; si++) {
1623
+ const s = opts.series[si];
1624
+ const seriesAlpha = s.alpha ?? 1;
1625
+ const secondaryFade = si > 0 && reveal < 1 ? Math.min(1, reveal * 2) : 1;
1626
+ const combinedAlpha = secondaryFade * seriesAlpha;
1627
+ if (combinedAlpha < 0.01) continue;
1628
+ ctx.save();
1629
+ if (combinedAlpha < 1) ctx.globalAlpha = combinedAlpha;
1630
+ const pts = drawLine(
1631
+ ctx,
1632
+ layout,
1633
+ s.palette,
1634
+ s.visible,
1635
+ s.smoothValue,
1636
+ opts.now,
1637
+ false,
1638
+ // no fill
1639
+ scrubX,
1640
+ opts.scrubAmount,
1641
+ reveal,
1642
+ opts.now_ms
1643
+ );
1644
+ ctx.restore();
1645
+ if (pts && pts.length > 0) {
1646
+ allPts.push({ pts, palette: s.palette, label: s.label, alpha: seriesAlpha });
1647
+ }
1648
+ }
1649
+ {
1650
+ const timeAlpha = reveal < 1 ? revealRamp(0.15, 0.7) : 1;
1651
+ if (timeAlpha > 0.01) {
1652
+ ctx.save();
1653
+ if (timeAlpha < 1) ctx.globalAlpha = timeAlpha;
1654
+ drawTimeAxis(ctx, layout, palette, opts.windowSecs, opts.targetWindowSecs, opts.formatTime, opts.timeAxisState, opts.dt);
1655
+ ctx.restore();
1656
+ }
1657
+ }
1658
+ if (reveal > 0.3 && allPts.length > 0) {
1659
+ const dotAlpha = (reveal - 0.3) / 0.7;
1660
+ const showPulse = opts.showPulse && reveal > 0.6 && opts.pauseProgress < 0.5;
1661
+ for (const entry of allPts) {
1662
+ if (entry.alpha < 0.01) continue;
1663
+ const lastPt = entry.pts[entry.pts.length - 1];
1664
+ ctx.save();
1665
+ ctx.globalAlpha = dotAlpha * entry.alpha;
1666
+ if (showPulse && entry.alpha > 0.5) {
1667
+ drawMultiDot(ctx, lastPt[0], lastPt[1], entry.palette.line, true, opts.now_ms, 3);
1668
+ } else {
1669
+ drawSimpleDot(ctx, lastPt[0], lastPt[1], entry.palette.line, 3);
1670
+ }
1671
+ if (entry.label) {
1672
+ ctx.font = '600 10px -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif';
1673
+ ctx.textAlign = "left";
1674
+ ctx.fillStyle = entry.palette.line;
1675
+ ctx.fillText(entry.label, lastPt[0] + 6, lastPt[1] + 3.5);
1676
+ }
1677
+ ctx.restore();
1678
+ }
1679
+ }
1680
+ ctx.save();
1681
+ ctx.globalCompositeOperation = "destination-out";
1682
+ const fadeGrad = ctx.createLinearGradient(layout.pad.left, 0, layout.pad.left + FADE_EDGE_WIDTH, 0);
1683
+ fadeGrad.addColorStop(0, "rgba(0, 0, 0, 1)");
1684
+ fadeGrad.addColorStop(1, "rgba(0, 0, 0, 0)");
1685
+ ctx.fillStyle = fadeGrad;
1686
+ ctx.fillRect(0, 0, layout.pad.left + FADE_EDGE_WIDTH, layout.h);
1687
+ ctx.restore();
1688
+ if (opts.hoverX !== null && opts.hoverTime !== null && opts.hoverEntries.length > 0 && allPts.length > 0 && opts.scrubAmount > 0.01) {
1689
+ let maxLiveDotX = 0;
1690
+ for (const entry of allPts) {
1691
+ if (entry.alpha < 0.01) continue;
1692
+ const lastX = entry.pts[entry.pts.length - 1][0];
1693
+ if (lastX > maxLiveDotX) maxLiveDotX = lastX;
1694
+ }
1695
+ const distToLive = maxLiveDotX - opts.hoverX;
1696
+ const fadeStart = Math.min(80, layout.chartW * 0.3);
1697
+ const scrubOpacity = distToLive < CROSSHAIR_FADE_MIN_PX ? 0 : distToLive >= fadeStart ? opts.scrubAmount : (distToLive - CROSSHAIR_FADE_MIN_PX) / (fadeStart - CROSSHAIR_FADE_MIN_PX) * opts.scrubAmount;
1698
+ if (scrubOpacity > 0.01) {
1699
+ drawMultiCrosshair(
1700
+ ctx,
1701
+ layout,
1702
+ palette,
1703
+ opts.hoverX,
1704
+ opts.hoverTime,
1705
+ opts.hoverEntries,
1706
+ opts.formatValue,
1707
+ opts.formatTime,
1708
+ scrubOpacity,
1709
+ opts.tooltipY,
1710
+ opts.tooltipOutline,
1711
+ maxLiveDotX
1712
+ );
1713
+ }
1714
+ }
1715
+ }
1460
1716
  function drawCandleFrame(ctx, layout, palette, opts) {
1461
1717
  const { w, h, pad, chartW, chartH } = layout;
1462
1718
  const reveal = opts.chartReveal;
@@ -1729,6 +1985,7 @@ var BADGE_Y_LERP_TRANSITIONING = 0.5;
1729
1985
  var MOMENTUM_COLOR_LERP = 0.12;
1730
1986
  var WINDOW_TRANSITION_MS = 750;
1731
1987
  var WINDOW_BUFFER = 0.05;
1988
+ var WINDOW_BUFFER_NO_BADGE = 0.015;
1732
1989
  var VALUE_SNAP_THRESHOLD = 1e-3;
1733
1990
  var ADAPTIVE_SPEED_BOOST = 0.2;
1734
1991
  var MOMENTUM_GREEN = [34, 197, 94];
@@ -1739,6 +1996,7 @@ var PAUSE_PROGRESS_SPEED = 0.12;
1739
1996
  var PAUSE_CATCHUP_SPEED = 0.08;
1740
1997
  var PAUSE_CATCHUP_SPEED_FAST = 0.22;
1741
1998
  var LOADING_ALPHA_SPEED = 0.14;
1999
+ var SERIES_TOGGLE_SPEED = 0.1;
1742
2000
  var CANDLE_LERP_SPEED = 0.25;
1743
2001
  var CANDLE_WIDTH_TRANS_MS = 300;
1744
2002
  var LINE_MORPH_MS = 500;
@@ -1749,7 +2007,7 @@ var LINE_ADAPTIVE_BOOST = 0.2;
1749
2007
  var LINE_SNAP_THRESHOLD = 1e-3;
1750
2008
  var RANGE_LERP_SPEED = 0.15;
1751
2009
  var RANGE_ADAPTIVE_BOOST = 0.2;
1752
- var CANDLE_BUFFER = 0.05;
2010
+ var CANDLE_BUFFER_NO_BADGE = 0.015;
1753
2011
  function computeAdaptiveSpeed(value, displayValue, displayMin, displayMax, lerpSpeed, noMotion) {
1754
2012
  const valGap = Math.abs(value - displayValue);
1755
2013
  const prevRange = displayMax - displayMin || 1;
@@ -2062,6 +2320,8 @@ function useLivelineEngine(canvasRef, containerRef, config) {
2062
2320
  const configRef = (0, import_react.useRef)(config);
2063
2321
  configRef.current = config;
2064
2322
  const displayValueRef = (0, import_react.useRef)(config.value);
2323
+ const displayValuesRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
2324
+ const seriesAlphaRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
2065
2325
  const displayMinRef = (0, import_react.useRef)(0);
2066
2326
  const displayMaxRef = (0, import_react.useRef)(0);
2067
2327
  const targetMinRef = (0, import_react.useRef)(0);
@@ -2094,12 +2354,15 @@ function useLivelineEngine(canvasRef, containerRef, config) {
2094
2354
  const hoverXRef = (0, import_react.useRef)(null);
2095
2355
  const scrubAmountRef = (0, import_react.useRef)(0);
2096
2356
  const lastHoverRef = (0, import_react.useRef)(null);
2357
+ const lastHoverEntriesRef = (0, import_react.useRef)([]);
2097
2358
  const chartRevealRef = (0, import_react.useRef)(0);
2098
2359
  const pauseProgressRef = (0, import_react.useRef)(0);
2099
2360
  const timeDebtRef = (0, import_react.useRef)(0);
2100
2361
  const lastDataRef = (0, import_react.useRef)([]);
2362
+ const lastMultiSeriesRef = (0, import_react.useRef)([]);
2101
2363
  const frozenNowRef = (0, import_react.useRef)(0);
2102
2364
  const pausedDataRef = (0, import_react.useRef)(null);
2365
+ const pausedMultiDataRef = (0, import_react.useRef)(null);
2103
2366
  const loadingAlphaRef = (0, import_react.useRef)(config.loading ? 1 : 0);
2104
2367
  const displayCandleRef = (0, import_react.useRef)(null);
2105
2368
  const liveBirthAlphaRef = (0, import_react.useRef)(1);
@@ -2279,6 +2542,17 @@ function useLivelineEngine(canvasRef, containerRef, config) {
2279
2542
  pausedLineDataRef.current = null;
2280
2543
  pausedLineValueRef.current = null;
2281
2544
  }
2545
+ } else if (cfg.isMultiSeries && cfg.multiSeries) {
2546
+ if (cfg.paused && pausedMultiDataRef.current === null) {
2547
+ const snap = /* @__PURE__ */ new Map();
2548
+ for (const s of cfg.multiSeries) {
2549
+ if (s.data.length >= 2) snap.set(s.id, { data: s.data.slice(), value: s.value });
2550
+ }
2551
+ if (snap.size > 0) pausedMultiDataRef.current = snap;
2552
+ }
2553
+ if (!cfg.paused) {
2554
+ pausedMultiDataRef.current = null;
2555
+ }
2282
2556
  } else {
2283
2557
  if (cfg.paused && pausedDataRef.current === null && cfg.data.length >= 2) {
2284
2558
  pausedDataRef.current = cfg.data.slice();
@@ -2289,7 +2563,8 @@ function useLivelineEngine(canvasRef, containerRef, config) {
2289
2563
  }
2290
2564
  const points = isCandle ? [] : pausedDataRef.current ?? cfg.data;
2291
2565
  const effectiveCandles = isCandle ? pausedCandlesRef.current ?? (cfg.candles ?? []) : [];
2292
- const hasData = isCandle ? effectiveCandles.length >= 2 : points.length >= 2;
2566
+ const hasMultiData = cfg.isMultiSeries && cfg.multiSeries ? cfg.multiSeries.some((s) => s.data.length >= 2) : false;
2567
+ const hasData = isCandle ? effectiveCandles.length >= 2 : hasMultiData || points.length >= 2;
2293
2568
  const pad = cfg.padding;
2294
2569
  const chartH = h - pad.top - pad.bottom;
2295
2570
  const pauseTarget = cfg.paused ? 1 : 0;
@@ -2325,11 +2600,23 @@ function useLivelineEngine(canvasRef, containerRef, config) {
2325
2600
  rangeInitedRef.current = false;
2326
2601
  }
2327
2602
  let useStash;
2603
+ let useMultiStash = false;
2328
2604
  if (isCandle) {
2329
2605
  useStash = !hasData && chartReveal > 5e-3 && lastCandlesRef.current.length > 0;
2330
2606
  } else {
2331
- useStash = !hasData && chartReveal > 5e-3 && lastDataRef.current.length >= 2;
2332
- if (hasData) lastDataRef.current = points;
2607
+ useMultiStash = !hasData && chartReveal > 5e-3 && lastMultiSeriesRef.current.length > 0;
2608
+ if (hasMultiData && cfg.multiSeries) {
2609
+ lastMultiSeriesRef.current = cfg.multiSeries.map((s) => ({
2610
+ id: s.id,
2611
+ data: s.data.slice(),
2612
+ value: s.value,
2613
+ palette: s.palette,
2614
+ label: s.label
2615
+ }));
2616
+ }
2617
+ if (hasData && !cfg.isMultiSeries) lastMultiSeriesRef.current = [];
2618
+ useStash = !useMultiStash && !hasData && chartReveal > 5e-3 && lastDataRef.current.length >= 2;
2619
+ if (hasData && !cfg.isMultiSeries) lastDataRef.current = points;
2333
2620
  }
2334
2621
  if (isCandle) {
2335
2622
  const lmt = lineModeTransRef.current;
@@ -2351,8 +2638,8 @@ function useLivelineEngine(canvasRef, containerRef, config) {
2351
2638
  lineModeProgRef.current = lmt.to;
2352
2639
  }
2353
2640
  }
2354
- if (!hasData && !useStash) {
2355
- const loadingColor = isCandle ? cfg.palette.gridLabel : void 0;
2641
+ if (!hasData && !useStash && !useMultiStash) {
2642
+ const loadingColor = isCandle || cfg.isMultiSeries || lastMultiSeriesRef.current.length > 0 ? cfg.palette.gridLabel : void 0;
2356
2643
  if (loadingAlpha > 0.01) {
2357
2644
  drawLoading(ctx, w, h, pad, cfg.palette, now_ms, loadingAlpha, loadingColor);
2358
2645
  }
@@ -2372,6 +2659,7 @@ function useLivelineEngine(canvasRef, containerRef, config) {
2372
2659
  return;
2373
2660
  }
2374
2661
  if (isCandle) {
2662
+ const candleBuffer = CANDLE_BUFFER_NO_BADGE;
2375
2663
  if (hasData) frozenNowRef.current = Date.now() / 1e3 - timeDebtRef.current;
2376
2664
  const now = hasData || chartReveal < 5e-3 ? Date.now() / 1e3 - timeDebtRef.current : frozenNowRef.current;
2377
2665
  const rawLive = pausedCandlesRef.current ? pausedLiveRef.current ?? void 0 : cfg.liveCandle;
@@ -2414,7 +2702,7 @@ function useLivelineEngine(canvasRef, containerRef, config) {
2414
2702
  cwt.rangeFromMin = displayMinRef.current;
2415
2703
  cwt.rangeFromMax = displayMaxRef.current;
2416
2704
  const curWindow = displayWindowRef.current;
2417
- const re = now + curWindow * CANDLE_BUFFER;
2705
+ const re = now + curWindow * candleBuffer;
2418
2706
  const le = re - curWindow;
2419
2707
  const targetVis = [];
2420
2708
  for (const c of effectiveCandles) {
@@ -2465,13 +2753,13 @@ function useLivelineEngine(canvasRef, containerRef, config) {
2465
2753
  effectiveCandles,
2466
2754
  rawLive,
2467
2755
  candleWidthSecs,
2468
- CANDLE_BUFFER
2756
+ candleBuffer
2469
2757
  );
2470
2758
  displayWindowRef.current = windowResult.windowSecs;
2471
2759
  const windowSecs = windowResult.windowSecs;
2472
2760
  const windowTransProgress = windowResult.windowTransProgress;
2473
2761
  const isWindowTransitioning = transition.startMs > 0;
2474
- const rightEdge = now + windowSecs * CANDLE_BUFFER;
2762
+ const rightEdge = now + windowSecs * candleBuffer;
2475
2763
  const leftEdge = rightEdge - windowSecs;
2476
2764
  let smoothLive;
2477
2765
  if (rawLive) {
@@ -2775,6 +3063,250 @@ function useLivelineEngine(canvasRef, containerRef, config) {
2775
3063
  badgeRef.current.container.style.display = "none";
2776
3064
  }
2777
3065
  }
3066
+ } else if (cfg.isMultiSeries && cfg.multiSeries && cfg.multiSeries.length > 0 || useMultiStash) {
3067
+ const effectiveMultiSeries = useMultiStash ? lastMultiSeriesRef.current : cfg.multiSeries;
3068
+ let labelReserve = 0;
3069
+ if (effectiveMultiSeries.some((s) => s.label)) {
3070
+ ctx.font = '600 10px -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif';
3071
+ let maxLabelW = 0;
3072
+ for (const s of effectiveMultiSeries) {
3073
+ if (s.label) {
3074
+ const lw = ctx.measureText(s.label).width;
3075
+ if (lw > maxLabelW) maxLabelW = lw;
3076
+ }
3077
+ }
3078
+ labelReserve = Math.max(0, maxLabelW - 2) * chartReveal;
3079
+ }
3080
+ const chartW = w - pad.left - pad.right - labelReserve;
3081
+ const buffer = cfg.showBadge ? WINDOW_BUFFER : WINDOW_BUFFER_NO_BADGE;
3082
+ if (!useMultiStash) {
3083
+ const currentIds = new Set(effectiveMultiSeries.map((s) => s.id));
3084
+ for (const key of displayValuesRef.current.keys()) {
3085
+ if (!currentIds.has(key)) displayValuesRef.current.delete(key);
3086
+ }
3087
+ }
3088
+ const firstSeries = effectiveMultiSeries[0];
3089
+ const transition = windowTransitionRef.current;
3090
+ if (hasData) frozenNowRef.current = Date.now() / 1e3 - timeDebtRef.current;
3091
+ const now = useMultiStash ? frozenNowRef.current : Date.now() / 1e3 - timeDebtRef.current;
3092
+ const smoothValues = /* @__PURE__ */ new Map();
3093
+ for (const s of effectiveMultiSeries) {
3094
+ let dv = displayValuesRef.current.get(s.id);
3095
+ if (dv === void 0) dv = s.value;
3096
+ if (!useMultiStash) {
3097
+ const adaptiveSpeed2 = computeAdaptiveSpeed(
3098
+ s.value,
3099
+ dv,
3100
+ displayMinRef.current,
3101
+ displayMaxRef.current,
3102
+ cfg.lerpSpeed,
3103
+ noMotion
3104
+ );
3105
+ dv = lerp(dv, s.value, adaptiveSpeed2, pausedDt);
3106
+ const prevRange = displayMaxRef.current - displayMinRef.current || 1;
3107
+ if (Math.abs(dv - s.value) < prevRange * VALUE_SNAP_THRESHOLD) dv = s.value;
3108
+ displayValuesRef.current.set(s.id, dv);
3109
+ }
3110
+ smoothValues.set(s.id, dv);
3111
+ }
3112
+ const hiddenIds = cfg.hiddenSeriesIds;
3113
+ const seriesAlphas = seriesAlphaRef.current;
3114
+ for (const s of effectiveMultiSeries) {
3115
+ let alpha = seriesAlphas.get(s.id) ?? 1;
3116
+ const target = hiddenIds?.has(s.id) ? 0 : 1;
3117
+ alpha = noMotion ? target : lerp(alpha, target, SERIES_TOGGLE_SPEED, pausedDt);
3118
+ if (alpha < 0.01) alpha = 0;
3119
+ if (alpha > 0.99) alpha = 1;
3120
+ seriesAlphas.set(s.id, alpha);
3121
+ }
3122
+ const firstData = pausedMultiDataRef.current?.get(firstSeries.id)?.data ?? firstSeries.data;
3123
+ const windowResult = updateWindowTransition(
3124
+ cfg,
3125
+ transition,
3126
+ displayWindowRef.current,
3127
+ displayMinRef.current,
3128
+ displayMaxRef.current,
3129
+ noMotion,
3130
+ now_ms,
3131
+ now,
3132
+ firstData,
3133
+ smoothValues.get(firstSeries.id) ?? firstSeries.value,
3134
+ buffer
3135
+ );
3136
+ if (transition.startMs > 0 && effectiveMultiSeries.length > 1) {
3137
+ const targetRightEdge = now + cfg.windowSecs * buffer;
3138
+ const targetLeftEdge = targetRightEdge - cfg.windowSecs;
3139
+ let unionMin = Infinity;
3140
+ let unionMax = -Infinity;
3141
+ for (const s of effectiveMultiSeries) {
3142
+ const sData = pausedMultiDataRef.current?.get(s.id)?.data ?? s.data;
3143
+ const sv = smoothValues.get(s.id) ?? s.value;
3144
+ const targetVisible = [];
3145
+ for (const p of sData) {
3146
+ if (p.time >= targetLeftEdge - 2 && p.time <= targetRightEdge) targetVisible.push(p);
3147
+ }
3148
+ if (targetVisible.length > 0) {
3149
+ const range = computeRange(targetVisible, sv, cfg.referenceLine?.value, cfg.exaggerate);
3150
+ if (range.min < unionMin) unionMin = range.min;
3151
+ if (range.max > unionMax) unionMax = range.max;
3152
+ }
3153
+ }
3154
+ if (isFinite(unionMin) && isFinite(unionMax)) {
3155
+ transition.rangeToMin = unionMin;
3156
+ transition.rangeToMax = unionMax;
3157
+ }
3158
+ }
3159
+ displayWindowRef.current = windowResult.windowSecs;
3160
+ const windowSecs = windowResult.windowSecs;
3161
+ const windowTransProgress = windowResult.windowTransProgress;
3162
+ const isWindowTransitioning = transition.startMs > 0;
3163
+ const rightEdge = now + windowSecs * buffer;
3164
+ const leftEdge = rightEdge - windowSecs;
3165
+ const filterRight = rightEdge - (rightEdge - now) * pauseProgress;
3166
+ const seriesEntries = [];
3167
+ let globalMin = Infinity;
3168
+ let globalMax = -Infinity;
3169
+ for (const s of effectiveMultiSeries) {
3170
+ const snap = pausedMultiDataRef.current?.get(s.id);
3171
+ const seriesData = snap?.data ?? s.data;
3172
+ const visible = [];
3173
+ for (const p of seriesData) {
3174
+ if (p.time >= leftEdge - 2 && p.time <= filterRight) visible.push(p);
3175
+ }
3176
+ const sv = smoothValues.get(s.id) ?? s.value;
3177
+ const alpha = seriesAlphas.get(s.id) ?? 1;
3178
+ if (visible.length >= 2) {
3179
+ if (alpha > 0.01) {
3180
+ const range = computeRange(visible, sv, cfg.referenceLine?.value, cfg.exaggerate);
3181
+ if (range.min < globalMin) globalMin = range.min;
3182
+ if (range.max > globalMax) globalMax = range.max;
3183
+ }
3184
+ seriesEntries.push({ visible, smoothValue: sv, palette: s.palette, label: s.label, alpha });
3185
+ }
3186
+ }
3187
+ if (seriesEntries.length === 0) {
3188
+ if (loadingAlpha > 0.01) {
3189
+ drawLoading(ctx, w, h, pad, cfg.palette, now_ms, loadingAlpha, cfg.palette.gridLabel);
3190
+ }
3191
+ if (1 - loadingAlpha > 0.01) {
3192
+ drawEmpty(ctx, w, h, pad, cfg.palette, 1 - loadingAlpha, now_ms, false, cfg.emptyText);
3193
+ }
3194
+ ctx.save();
3195
+ ctx.globalCompositeOperation = "destination-out";
3196
+ const fadeGrad = ctx.createLinearGradient(pad.left, 0, pad.left + FADE_EDGE_WIDTH, 0);
3197
+ fadeGrad.addColorStop(0, "rgba(0, 0, 0, 1)");
3198
+ fadeGrad.addColorStop(1, "rgba(0, 0, 0, 0)");
3199
+ ctx.fillStyle = fadeGrad;
3200
+ ctx.fillRect(0, 0, pad.left + FADE_EDGE_WIDTH, h);
3201
+ ctx.restore();
3202
+ if (badgeRef.current) badgeRef.current.container.style.display = "none";
3203
+ rafRef.current = requestAnimationFrame(draw);
3204
+ return;
3205
+ }
3206
+ const computedRange = { min: isFinite(globalMin) ? globalMin : 0, max: isFinite(globalMax) ? globalMax : 1 };
3207
+ const adaptiveSpeed = cfg.lerpSpeed + ADAPTIVE_SPEED_BOOST * 0.5;
3208
+ const rangeResult = updateRange(
3209
+ computedRange,
3210
+ rangeInitedRef.current,
3211
+ targetMinRef.current,
3212
+ targetMaxRef.current,
3213
+ displayMinRef.current,
3214
+ displayMaxRef.current,
3215
+ isWindowTransitioning,
3216
+ windowTransProgress,
3217
+ transition,
3218
+ adaptiveSpeed,
3219
+ chartH,
3220
+ pausedDt
3221
+ );
3222
+ rangeInitedRef.current = rangeResult.rangeInited;
3223
+ targetMinRef.current = rangeResult.targetMin;
3224
+ targetMaxRef.current = rangeResult.targetMax;
3225
+ displayMinRef.current = rangeResult.displayMin;
3226
+ displayMaxRef.current = rangeResult.displayMax;
3227
+ const { minVal, maxVal, valRange } = rangeResult;
3228
+ const layout = {
3229
+ w,
3230
+ h,
3231
+ pad,
3232
+ chartW,
3233
+ chartH,
3234
+ leftEdge,
3235
+ rightEdge,
3236
+ minVal,
3237
+ maxVal,
3238
+ valRange,
3239
+ toX: (t) => pad.left + (t - leftEdge) / (rightEdge - leftEdge) * chartW,
3240
+ toY: (v) => pad.top + (1 - (v - minVal) / valRange) * chartH
3241
+ };
3242
+ const hoverPx = hoverXRef.current;
3243
+ let drawHoverX = null;
3244
+ let drawHoverTime = null;
3245
+ let isActiveHover = false;
3246
+ let hoverEntries = [];
3247
+ if (hoverPx !== null && hoverPx >= pad.left && hoverPx <= w - pad.right) {
3248
+ const maxHoverX = layout.toX(now);
3249
+ const clampedX = Math.min(hoverPx, maxHoverX);
3250
+ const t = leftEdge + (clampedX - pad.left) / chartW * (rightEdge - leftEdge);
3251
+ drawHoverX = clampedX;
3252
+ drawHoverTime = t;
3253
+ isActiveHover = true;
3254
+ for (const entry of seriesEntries) {
3255
+ if ((entry.alpha ?? 1) < 0.5) continue;
3256
+ const v = interpolateAtTime(entry.visible, t);
3257
+ if (v !== null) {
3258
+ hoverEntries.push({ color: entry.palette.line, label: entry.label ?? "", value: v });
3259
+ }
3260
+ }
3261
+ lastHoverRef.current = { x: clampedX, value: hoverEntries[0]?.value ?? 0, time: t };
3262
+ lastHoverEntriesRef.current = hoverEntries;
3263
+ cfg.onHover?.({ time: t, value: hoverEntries[0]?.value ?? 0, x: clampedX, y: layout.toY(hoverEntries[0]?.value ?? 0) });
3264
+ }
3265
+ const scrubTarget = isActiveHover ? 1 : 0;
3266
+ if (noMotion) {
3267
+ scrubAmountRef.current = scrubTarget;
3268
+ } else {
3269
+ scrubAmountRef.current += (scrubTarget - scrubAmountRef.current) * SCRUB_LERP_SPEED;
3270
+ if (scrubAmountRef.current < 0.01) scrubAmountRef.current = 0;
3271
+ if (scrubAmountRef.current > 0.99) scrubAmountRef.current = 1;
3272
+ }
3273
+ if (!isActiveHover && scrubAmountRef.current > 0 && lastHoverRef.current) {
3274
+ drawHoverX = lastHoverRef.current.x;
3275
+ drawHoverTime = lastHoverRef.current.time;
3276
+ hoverEntries = lastHoverEntriesRef.current;
3277
+ }
3278
+ drawMultiFrame(ctx, layout, {
3279
+ series: seriesEntries,
3280
+ now,
3281
+ showGrid: cfg.showGrid,
3282
+ showPulse: cfg.showPulse,
3283
+ referenceLine: cfg.referenceLine,
3284
+ hoverX: drawHoverX,
3285
+ hoverTime: drawHoverTime,
3286
+ hoverEntries,
3287
+ scrubAmount: scrubAmountRef.current,
3288
+ windowSecs,
3289
+ formatValue: cfg.formatValue,
3290
+ formatTime: cfg.formatTime,
3291
+ gridState: gridStateRef.current,
3292
+ timeAxisState: timeAxisStateRef.current,
3293
+ dt,
3294
+ targetWindowSecs: cfg.windowSecs,
3295
+ tooltipY: cfg.tooltipY,
3296
+ tooltipOutline: cfg.tooltipOutline,
3297
+ chartReveal,
3298
+ pauseProgress,
3299
+ now_ms,
3300
+ primaryPalette: cfg.palette
3301
+ });
3302
+ const bgAlpha = 1 - chartReveal;
3303
+ if (bgAlpha > 0.01 && revealTarget === 0 && !cfg.loading) {
3304
+ const bgEmptyAlpha = (1 - loadingAlpha) * bgAlpha;
3305
+ if (bgEmptyAlpha > 0.01) {
3306
+ drawEmpty(ctx, w, h, pad, cfg.palette, bgEmptyAlpha, now_ms, true, cfg.emptyText);
3307
+ }
3308
+ }
3309
+ if (badgeRef.current) badgeRef.current.container.style.display = "none";
2778
3310
  } else {
2779
3311
  const effectivePoints = useStash ? lastDataRef.current : points;
2780
3312
  const adaptiveSpeed = computeAdaptiveSpeed(
@@ -2796,8 +3328,9 @@ function useLivelineEngine(canvasRef, containerRef, config) {
2796
3328
  }
2797
3329
  const smoothValue = displayValueRef.current;
2798
3330
  const chartW = w - pad.left - pad.right;
2799
- const needsArrowRoom = cfg.showMomentum;
2800
- const buffer = needsArrowRoom ? Math.max(WINDOW_BUFFER, 37 / Math.max(chartW, 1)) : WINDOW_BUFFER;
3331
+ const baseBuffer = cfg.showBadge ? WINDOW_BUFFER : WINDOW_BUFFER_NO_BADGE;
3332
+ const needsArrowRoom = cfg.showMomentum && cfg.showBadge;
3333
+ const buffer = needsArrowRoom ? Math.max(baseBuffer, 37 / Math.max(chartW, 1)) : baseBuffer;
2801
3334
  const transition = windowTransitionRef.current;
2802
3335
  if (hasData) frozenNowRef.current = Date.now() / 1e3 - timeDebtRef.current;
2803
3336
  const now = useStash ? frozenNowRef.current : Date.now() / 1e3 - timeDebtRef.current;
@@ -2984,6 +3517,7 @@ var defaultFormatTime = (t) => {
2984
3517
  function Liveline({
2985
3518
  data,
2986
3519
  value,
3520
+ series: seriesProp,
2987
3521
  theme = "dark",
2988
3522
  color = "#3b82f6",
2989
3523
  window: windowSecs = 30,
@@ -3023,6 +3557,9 @@ function Liveline({
3023
3557
  lineData,
3024
3558
  lineValue,
3025
3559
  onModeChange,
3560
+ onSeriesToggle,
3561
+ seriesToggleCompact = false,
3562
+ lineWidth,
3026
3563
  className,
3027
3564
  style
3028
3565
  }) {
@@ -3035,13 +3572,37 @@ function Liveline({
3035
3572
  const modeBarRef = (0, import_react2.useRef)(null);
3036
3573
  const modeBtnRefs = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
3037
3574
  const [modeIndicatorStyle, setModeIndicatorStyle] = (0, import_react2.useState)(null);
3038
- const palette = (0, import_react2.useMemo)(() => resolveTheme(color, theme), [color, theme]);
3575
+ const [hiddenSeries, setHiddenSeries] = (0, import_react2.useState)(/* @__PURE__ */ new Set());
3576
+ const lastSeriesPropRef = (0, import_react2.useRef)(seriesProp);
3577
+ if (seriesProp && seriesProp.length > 0) lastSeriesPropRef.current = seriesProp;
3578
+ const palette = (0, import_react2.useMemo)(() => {
3579
+ const p = resolveTheme(color, theme);
3580
+ if (lineWidth != null) p.lineWidth = lineWidth;
3581
+ return p;
3582
+ }, [color, theme, lineWidth]);
3039
3583
  const isDark = theme === "dark";
3584
+ const isMultiSeries = seriesProp != null && seriesProp.length > 0;
3585
+ const showSeriesToggle = (lastSeriesPropRef.current?.length ?? 0) > 1;
3586
+ const seriesPalettes = (0, import_react2.useMemo)(() => {
3587
+ if (!seriesProp || seriesProp.length === 0) return null;
3588
+ return resolveSeriesPalettes(seriesProp, theme);
3589
+ }, [seriesProp, theme]);
3590
+ const multiSeries = (0, import_react2.useMemo)(() => {
3591
+ if (!seriesProp || !seriesPalettes) return void 0;
3592
+ return seriesProp.map((s, i) => ({
3593
+ id: s.id,
3594
+ data: s.data,
3595
+ value: s.value,
3596
+ palette: seriesPalettes.get(s.id) ?? resolveTheme(s.color || SERIES_COLORS[i % SERIES_COLORS.length], theme),
3597
+ label: s.label
3598
+ }));
3599
+ }, [seriesProp, seriesPalettes, theme]);
3040
3600
  const showMomentum = momentum !== false;
3041
3601
  const momentumOverride = typeof momentum === "string" ? momentum : void 0;
3602
+ const defaultRight = badge ? 80 : grid ? 54 : 12;
3042
3603
  const pad = {
3043
3604
  top: paddingOverride?.top ?? 12,
3044
- right: paddingOverride?.right ?? 80,
3605
+ right: paddingOverride?.right ?? defaultRight,
3045
3606
  bottom: paddingOverride?.bottom ?? 28,
3046
3607
  left: paddingOverride?.left ?? 12
3047
3608
  };
@@ -3078,6 +3639,22 @@ function Liveline({
3078
3639
  });
3079
3640
  }
3080
3641
  }, [activeMode, onModeChange]);
3642
+ const handleSeriesToggle = (0, import_react2.useCallback)((id) => {
3643
+ setHiddenSeries((prev) => {
3644
+ const next = new Set(prev);
3645
+ if (next.has(id)) {
3646
+ next.delete(id);
3647
+ onSeriesToggle?.(id, true);
3648
+ } else {
3649
+ const totalSeries = seriesProp?.length ?? 0;
3650
+ const visibleCount = totalSeries - next.size;
3651
+ if (visibleCount <= 1) return prev;
3652
+ next.add(id);
3653
+ onSeriesToggle?.(id, false);
3654
+ }
3655
+ return next;
3656
+ });
3657
+ }, [seriesProp?.length, onSeriesToggle]);
3081
3658
  const ws = windowStyle ?? "default";
3082
3659
  useLivelineEngine(canvasRef, containerRef, {
3083
3660
  data,
@@ -3086,10 +3663,10 @@ function Liveline({
3086
3663
  windowSecs: effectiveWindowSecs,
3087
3664
  lerpSpeed,
3088
3665
  showGrid: grid,
3089
- showBadge: badge,
3090
- showMomentum,
3666
+ showBadge: isMultiSeries ? false : badge,
3667
+ showMomentum: isMultiSeries ? false : showMomentum,
3091
3668
  momentumOverride,
3092
- showFill: fill,
3669
+ showFill: isMultiSeries ? false : fill,
3093
3670
  referenceLine,
3094
3671
  formatValue,
3095
3672
  formatTime,
@@ -3098,7 +3675,7 @@ function Liveline({
3098
3675
  showPulse: pulse,
3099
3676
  scrub,
3100
3677
  exaggerate,
3101
- degenOptions,
3678
+ degenOptions: isMultiSeries ? void 0 : degenOptions,
3102
3679
  badgeTail,
3103
3680
  badgeVariant,
3104
3681
  tooltipY,
@@ -3115,7 +3692,10 @@ function Liveline({
3115
3692
  liveCandle,
3116
3693
  lineMode,
3117
3694
  lineData,
3118
- lineValue
3695
+ lineValue,
3696
+ multiSeries,
3697
+ isMultiSeries,
3698
+ hiddenSeriesIds: hiddenSeries
3119
3699
  });
3120
3700
  const cursorStyle = scrub ? cursor : "default";
3121
3701
  const activeColor = isDark ? "rgba(255,255,255,0.7)" : "rgba(0,0,0,0.55)";
@@ -3139,7 +3719,7 @@ function Liveline({
3139
3719
  }
3140
3720
  }
3141
3721
  ),
3142
- (windows && windows.length > 0 || onModeChange) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 6, marginLeft: pad.left }, children: [
3722
+ (windows && windows.length > 0 || onModeChange || showSeriesToggle) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 6, marginLeft: pad.left }, children: [
3143
3723
  windows && windows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
3144
3724
  "div",
3145
3725
  {
@@ -3207,20 +3787,20 @@ function Liveline({
3207
3787
  style: {
3208
3788
  position: "relative",
3209
3789
  display: "inline-flex",
3210
- gap: 2,
3211
- background: isDark ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.02)",
3212
- borderRadius: 6,
3213
- padding: 2
3790
+ gap: ws === "text" ? 4 : 2,
3791
+ background: ws === "text" ? "transparent" : isDark ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.02)",
3792
+ borderRadius: ws === "rounded" ? 999 : 6,
3793
+ padding: ws === "text" ? 0 : ws === "rounded" ? 3 : 2
3214
3794
  },
3215
3795
  children: [
3216
- modeIndicatorStyle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
3796
+ ws !== "text" && modeIndicatorStyle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
3217
3797
  position: "absolute",
3218
- top: 2,
3798
+ top: ws === "rounded" ? 3 : 2,
3219
3799
  left: modeIndicatorStyle.left,
3220
3800
  width: modeIndicatorStyle.width,
3221
- height: "calc(100% - 4px)",
3801
+ height: ws === "rounded" ? "calc(100% - 6px)" : "calc(100% - 4px)",
3222
3802
  background: isDark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.035)",
3223
- borderRadius: 4,
3803
+ borderRadius: ws === "rounded" ? 999 : 4,
3224
3804
  transition: "left 0.25s cubic-bezier(0.4, 0, 0.2, 1), width 0.25s cubic-bezier(0.4, 0, 0.2, 1)",
3225
3805
  pointerEvents: "none"
3226
3806
  } }),
@@ -3236,7 +3816,7 @@ function Liveline({
3236
3816
  position: "relative",
3237
3817
  zIndex: 1,
3238
3818
  padding: "5px 7px",
3239
- borderRadius: 4,
3819
+ borderRadius: ws === "rounded" ? 999 : 4,
3240
3820
  border: "none",
3241
3821
  cursor: "pointer",
3242
3822
  background: "transparent",
@@ -3267,7 +3847,7 @@ function Liveline({
3267
3847
  position: "relative",
3268
3848
  zIndex: 1,
3269
3849
  padding: "5px 7px",
3270
- borderRadius: 4,
3850
+ borderRadius: ws === "rounded" ? 999 : 4,
3271
3851
  border: "none",
3272
3852
  cursor: "pointer",
3273
3853
  background: "transparent",
@@ -3324,7 +3904,58 @@ function Liveline({
3324
3904
  )
3325
3905
  ]
3326
3906
  }
3327
- )
3907
+ ),
3908
+ showSeriesToggle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
3909
+ display: "inline-flex",
3910
+ gap: ws === "text" ? 4 : 2,
3911
+ background: ws === "text" ? "transparent" : isDark ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.02)",
3912
+ borderRadius: ws === "rounded" ? 999 : 6,
3913
+ padding: ws === "text" ? 0 : ws === "rounded" ? 3 : 2,
3914
+ opacity: isMultiSeries ? 1 : 0,
3915
+ transition: "opacity 0.4s",
3916
+ pointerEvents: isMultiSeries ? "auto" : "none"
3917
+ }, children: (lastSeriesPropRef.current ?? []).map((s, si) => {
3918
+ const isHidden = hiddenSeries.has(s.id);
3919
+ const seriesColor = s.color || SERIES_COLORS[si % SERIES_COLORS.length];
3920
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
3921
+ "button",
3922
+ {
3923
+ onClick: () => handleSeriesToggle(s.id),
3924
+ style: {
3925
+ position: "relative",
3926
+ zIndex: 1,
3927
+ fontSize: 11,
3928
+ padding: seriesToggleCompact ? ws === "text" ? "2px 4px" : "5px 7px" : ws === "text" ? "2px 6px" : "3px 8px",
3929
+ borderRadius: ws === "rounded" ? 999 : 4,
3930
+ border: "none",
3931
+ cursor: "pointer",
3932
+ fontFamily: "system-ui, -apple-system, sans-serif",
3933
+ fontWeight: 500,
3934
+ background: isHidden ? "transparent" : ws === "text" ? "transparent" : isDark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.035)",
3935
+ color: isHidden ? inactiveColor : activeColor,
3936
+ opacity: isHidden ? 0.4 : 1,
3937
+ transition: "opacity 0.2s, background 0.15s, color 0.2s",
3938
+ lineHeight: "16px",
3939
+ display: "flex",
3940
+ alignItems: "center",
3941
+ gap: seriesToggleCompact ? 0 : 4
3942
+ },
3943
+ children: [
3944
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: {
3945
+ width: seriesToggleCompact ? 8 : 6,
3946
+ height: seriesToggleCompact ? 8 : 6,
3947
+ borderRadius: "50%",
3948
+ background: seriesColor,
3949
+ flexShrink: 0,
3950
+ opacity: isHidden ? 0.4 : 1,
3951
+ transition: "opacity 0.2s"
3952
+ } }),
3953
+ !seriesToggleCompact && (s.label ?? s.id)
3954
+ ]
3955
+ },
3956
+ s.id
3957
+ );
3958
+ }) })
3328
3959
  ] }),
3329
3960
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3330
3961
  "div",