@vroomchart/react 0.1.7 → 0.2.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.
package/dist/index.d.ts CHANGED
@@ -70,14 +70,22 @@ type VroomTheme = {
70
70
  accentBull?: VroomColor;
71
71
  /** Generic down color for the price indicator, volume bars, and MACD histogram. Defaults to red; independent of `bear`. */
72
72
  accentBear?: VroomColor;
73
- /** Up candle body 1px border. Defaults to the bull fill color. */
73
+ /** Up candle body border (1px, drawn *inside* the body so it never changes candle width). Omit or set to the bull fill color to hide it. */
74
74
  borderBull?: VroomColor;
75
- /** Down candle body 1px border. Defaults to the bear fill color. */
75
+ /** Down candle body border (1px, drawn *inside* the body so it never changes candle width). Omit or set to the bear fill color to hide it. */
76
76
  borderBear?: VroomColor;
77
77
  /** Up candle wick color. Defaults to the bull fill color. */
78
78
  wickBull?: VroomColor;
79
79
  /** Down candle wick color. Defaults to the bear fill color. */
80
80
  wickBear?: VroomColor;
81
+ /** Wick stroke width in px (applies to both up and down wicks). Defaults to 1. */
82
+ wickWidth?: number;
83
+ /** Corner radius (px) of candle bodies. Defaults to 0 (square). */
84
+ candleRadius?: number;
85
+ /** Round the wick end caps. Defaults to false. */
86
+ wickRoundCap?: boolean;
87
+ /** Corner radius (px) of the *top* of volume bars. Defaults to 0 (square). */
88
+ volumeRadius?: number;
81
89
  /** Gridlines. */
82
90
  grid?: VroomColor;
83
91
  /** Axis label text (price + time). */
@@ -251,6 +259,14 @@ type VroomChartCoreProps = {
251
259
  height?: number;
252
260
  /** Time window to render. Omit (or both 0) to show every candle. */
253
261
  visibleRange?: VisibleRange;
262
+ /**
263
+ * Target candle *body* width in logical px for the INITIAL framing. Larger →
264
+ * more zoomed in (fewer candles); smaller → more zoomed out. Only affects
265
+ * first load / reset; an explicit `visibleRange` takes precedence. Omit for
266
+ * the default (~80 candles). Useful to keep candles a consistent size across
267
+ * devices of different widths.
268
+ */
269
+ defaultCandleWidth?: number;
254
270
  theme?: VroomTheme;
255
271
  /** RSI indicator (pane below the candles). Omit/disable to hide it. */
256
272
  rsi?: RSIConfig;
package/dist/index.js CHANGED
@@ -121,6 +121,7 @@ function useChartCore(props, loadOpts) {
121
121
  width: widthProp,
122
122
  height: heightProp,
123
123
  visibleRange,
124
+ defaultCandleWidth,
124
125
  theme,
125
126
  rsi,
126
127
  macd,
@@ -209,6 +210,9 @@ function useChartCore(props, loadOpts) {
209
210
  tfArgs = { oldWindow, oldStepMs, oldLastMs: prev.candles[prev.candles.length - 1].timeMs };
210
211
  }
211
212
  }
213
+ if (freshHandle && !explicit && defaultCandleWidth != null && defaultCandleWidth > 0) {
214
+ h.setDefaultCandleWidth(defaultCandleWidth);
215
+ }
212
216
  h.setCandles(packCandles(candles));
213
217
  if (transition === "timeframe") {
214
218
  const newStepMs = inferStepMs(candles);
@@ -252,7 +256,7 @@ function useChartCore(props, loadOpts) {
252
256
  liquidity?.bands?.length ? liquidityToSpec(liquidity) : EMPTY_LIQUIDITY
253
257
  );
254
258
  scheduleRender();
255
- }, [ready, width, height, candles, seriesKey, explicit, startMs, endMs, themeKey, rsiKey, macdKey, maKey, vwapKey, drawingsKey, liquidityKey, scheduleRender]);
259
+ }, [ready, width, height, candles, seriesKey, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, drawingsKey, liquidityKey, scheduleRender]);
256
260
  return { containerRef, canvasRef, handleRef, scheduleRender, size: { width, height } };
257
261
  }
258
262
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useChartCore.ts","../src/dataTransitions.ts","../src/useGestures.ts","../src/VroomChart.tsx"],"sourcesContent":["// Owns one VroomChartHandle bound to a <canvas>: loads the core, measures the\n// container (ResizeObserver), pushes data/size/theme/indicators on change, and\n// drives a rAF-batched present() loop. The web analogue of\n// packages/react-native/src/useChartCore.ts — but it paints the canvas via\n// handle.present() instead of producing an SkPicture.\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n loadVroom,\n packCandles,\n applyTheme,\n parseColor,\n type LoadVroomOptions,\n type OverlaySpec,\n type DrawingSpec,\n type LiquiditySpec,\n type VroomChartHandle,\n} from '@vroomchart/core-wasm';\nimport type { VroomChartCoreProps } from '@vroomchart/types';\nimport {\n classifyTransition,\n inferStepMs,\n timeframeWindow,\n type DataTransition,\n} from './dataTransitions';\n\n// Mirrors vroom::ma::Source order (packages/core/src/ma.h).\nconst MA_SOURCES = ['close', 'open', 'high', 'low', 'hl2', 'hlc3', 'ohlc4'] as const;\n\nfunction overlayToNumeric(\n o: NonNullable<VroomChartCoreProps['movingAverages']>[number],\n): OverlaySpec {\n const srcIdx = o.source ? MA_SOURCES.indexOf(o.source) : 0;\n return {\n kind: o.kind === 'ema' ? 1 : 0,\n period: o.length,\n source: srcIdx < 0 ? 0 : srcIdx,\n color: (o.color != null ? parseColor(o.color) : null) ?? 0xff2962ff,\n width: o.width ?? 1.5,\n };\n}\n\nfunction drawingToSpec(\n d: NonNullable<VroomChartCoreProps['drawings']>[number],\n): DrawingSpec {\n return {\n aTime: d.points[0].timeMs,\n aPrice: d.points[0].price,\n bTime: d.points[1].timeMs,\n bPrice: d.points[1].price,\n color: (d.color != null ? parseColor(d.color) : null) ?? 0xff2962ff,\n width: d.width ?? 2,\n };\n}\n\n// Default buy/sell colors (teal-green / red), matching the accent palette.\nconst DEFAULT_BUY_COLOR = 0xff26a69a;\nconst DEFAULT_SELL_COLOR = 0xffef5350;\n\nfunction liquidityToSpec(\n cfg: NonNullable<VroomChartCoreProps['liquidity']>,\n): LiquiditySpec {\n return {\n bands: cfg.bands.map((b) => ({\n minPrice: b.minPrice,\n maxPrice: b.maxPrice,\n side: b.side === 'sell' ? 1 : 0,\n volume: b.volume,\n })),\n buyColor: (cfg.buyColor != null ? parseColor(cfg.buyColor) : null) ?? DEFAULT_BUY_COLOR,\n sellColor:\n (cfg.sellColor != null ? parseColor(cfg.sellColor) : null) ?? DEFAULT_SELL_COLOR,\n maxVolume: cfg.maxVolume ?? 0,\n minOpacity: cfg.minOpacity ?? 0.05,\n maxOpacity: cfg.maxOpacity ?? 0.8,\n widthPx: cfg.widthPx ?? 300,\n widthFrac: cfg.widthFrac ?? 0.25,\n };\n}\n\n// Cleared overlay: an empty band set (style values are irrelevant when there are\n// no bands, but the spec shape requires them).\nconst EMPTY_LIQUIDITY: LiquiditySpec = {\n bands: [],\n buyColor: DEFAULT_BUY_COLOR,\n sellColor: DEFAULT_SELL_COLOR,\n maxVolume: 0,\n minOpacity: 0.05,\n maxOpacity: 0.8,\n widthPx: 300,\n widthFrac: 0.25,\n};\n\nexport type UseChartCore = {\n containerRef: React.RefObject<HTMLDivElement | null>;\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n handleRef: React.RefObject<VroomChartHandle | null>;\n /** Schedules a rAF-batched repaint (and keeps ticking while animating). */\n scheduleRender: () => void;\n /** Measured CSS size of the container (0 until first layout). */\n size: { width: number; height: number };\n};\n\nexport function useChartCore(\n props: VroomChartCoreProps,\n loadOpts?: LoadVroomOptions,\n): UseChartCore {\n const {\n candles,\n seriesKey,\n width: widthProp,\n height: heightProp,\n visibleRange,\n theme,\n rsi,\n macd,\n movingAverages,\n vwap,\n drawings,\n liquidity,\n } = props;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const handleRef = useRef<VroomChartHandle | null>(null);\n // What the core currently holds, for classifying the next data change.\n // Keyed by handle so a recreated core is treated as a fresh initial load.\n const prevDataRef = useRef<{\n handle: VroomChartHandle;\n candles: VroomChartCoreProps['candles'];\n seriesKey?: string;\n } | null>(null);\n const rafRef = useRef<number | null>(null);\n const [ready, setReady] = useState(false);\n const [measured, setMeasured] = useState({ width: 0, height: 0 });\n\n // Captured at mount: which core to load (stub vs Skia-WASM). Changing it after\n // mount has no effect — the core is created once and shared process-wide.\n const loadOptsRef = useRef(loadOpts);\n loadOptsRef.current = loadOpts;\n\n const width = widthProp ?? measured.width;\n const height = heightProp ?? measured.height;\n\n const scheduleRender = useCallback(() => {\n if (rafRef.current != null) return;\n rafRef.current = requestAnimationFrame(() => {\n rafRef.current = null;\n const h = handleRef.current;\n if (!h) return;\n h.present();\n if (h.isAnimating()) scheduleRender();\n });\n }, []);\n\n // Create the handle once the canvas exists; destroy on unmount.\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n let disposed = false;\n loadVroom(loadOptsRef.current).then((mod) => {\n if (disposed) return;\n handleRef.current = mod.create(canvas);\n setReady(true);\n });\n return () => {\n disposed = true;\n if (rafRef.current != null) cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n handleRef.current?.destroy();\n handleRef.current = null;\n setReady(false);\n };\n }, []);\n\n // Measure the container (unless both dims are pinned via props).\n useEffect(() => {\n const el = containerRef.current;\n if (!el || (widthProp != null && heightProp != null)) return;\n const ro = new ResizeObserver((entries) => {\n const r = entries[0]?.contentRect;\n if (!r) return;\n const w = Math.round(r.width);\n const h = Math.round(r.height);\n setMeasured((prev) => (prev.width === w && prev.height === h ? prev : { width: w, height: h }));\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, [widthProp, heightProp]);\n\n // Stable deps so inline object/array literals don't re-run every render.\n const themeKey = theme ? JSON.stringify(theme) : '';\n const rsiKey = rsi ? JSON.stringify(rsi) : '';\n const macdKey = macd ? JSON.stringify(macd) : '';\n const maKey = movingAverages ? JSON.stringify(movingAverages) : '';\n const vwapKey = vwap ? JSON.stringify(vwap) : '';\n const drawingsKey = drawings ? JSON.stringify(drawings) : '';\n const liquidityKey = liquidity ? JSON.stringify(liquidity) : '';\n const explicit = visibleRange != null;\n const startMs = visibleRange?.startMs ?? 0;\n const endMs = visibleRange?.endMs ?? 0;\n\n // Push everything into the core whenever data/size/config changes, then paint.\n useEffect(() => {\n const h = handleRef.current;\n if (!h || width <= 0 || height <= 0) return;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n h.setSize(width, height, dpr);\n if (candles.length > 0) {\n const prev = prevDataRef.current;\n const freshHandle = prev == null || prev.handle !== h;\n if (freshHandle || prev.candles !== candles || prev.seriesKey !== seriesKey) {\n // A fresh core frames itself (its window starts at 0/0); an explicit\n // visibleRange prop overrides any auto behavior, so treat the change\n // like a stream and let the range application below win.\n const transition: DataTransition = freshHandle\n ? 'initial'\n : explicit\n ? 'stream'\n : classifyTransition(prev.candles, candles, seriesKey !== prev.seriesKey);\n\n // Capture the outgoing view before setCandles re-infers the candle\n // period from the new data.\n let tfArgs: { oldWindow: { startMs: number; endMs: number }; oldStepMs: number; oldLastMs: number } | null =\n null;\n if (transition === 'timeframe' && prev != null) {\n const oldWindow = h.getVisibleRange();\n const oldStepMs = inferStepMs(prev.candles);\n if (oldWindow.endMs > oldWindow.startMs && oldStepMs != null) {\n tfArgs = { oldWindow, oldStepMs, oldLastMs: prev.candles[prev.candles.length - 1].timeMs };\n }\n }\n\n h.setCandles(packCandles(candles));\n\n if (transition === 'timeframe') {\n const newStepMs = inferStepMs(candles);\n if (tfArgs && newStepMs != null) {\n const w = timeframeWindow(\n tfArgs.oldWindow,\n tfArgs.oldStepMs,\n tfArgs.oldLastMs,\n newStepMs,\n candles[candles.length - 1].timeMs,\n );\n h.setVisibleRange(w.startMs, w.endMs);\n }\n h.resetPriceScale();\n } else if (transition === 'reset') {\n h.resetView();\n }\n prevDataRef.current = { handle: h, candles, seriesKey };\n }\n }\n if (explicit) h.setVisibleRange(startMs, endMs);\n if (theme) applyTheme(h, theme);\n h.setRSI(\n rsi?.enabled ?? false,\n rsi?.period ?? 14,\n rsi?.upperBand ?? 70,\n rsi?.lowerBand ?? 30,\n rsi?.maEnabled ?? true,\n rsi?.maPeriod ?? 14,\n );\n h.setMACD(macd?.enabled ?? false, macd?.fast ?? 12, macd?.slow ?? 26, macd?.signal ?? 9);\n h.setOverlays((movingAverages ?? []).map(overlayToNumeric));\n h.setVWAP(\n vwap?.enabled ?? false,\n vwap?.resetMinutes ?? 0,\n (vwap?.color != null ? parseColor(vwap.color) : null) ?? 0xff00bcd4,\n vwap?.width ?? 1.5,\n );\n h.setDrawings((drawings ?? []).map(drawingToSpec));\n h.setLiquidity(\n liquidity?.bands?.length ? liquidityToSpec(liquidity) : EMPTY_LIQUIDITY,\n );\n scheduleRender();\n // theme/rsi/macd/movingAverages/vwap/drawings/liquidity tracked via *Key deps.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ready, width, height, candles, seriesKey, explicit, startMs, endMs, themeKey, rsiKey, macdKey, maKey, vwapKey, drawingsKey, liquidityKey, scheduleRender]);\n\n return { containerRef, canvasRef, handleRef, scheduleRender, size: { width, height } };\n}\n","// Classifies how a new `candles` prop relates to the previous one so the chart\n// can react appropriately: leave the viewport alone for streaming updates,\n// re-anchor the time window for a timeframe switch, or fully reset the view\n// for a different asset. Pure functions, no React — see useChartCore for the\n// orchestration.\n\nimport type { Candle, VisibleRange } from '@vroomchart/types';\n\nexport type DataTransition = 'initial' | 'stream' | 'timeframe' | 'reset';\n\n// A step change below this ratio is treated as the same timeframe. Real steps\n// are exact integer ms; the tolerance only absorbs rounding/DST quirks (the\n// smallest real timeframe jump, 1m -> 2m, is 100% apart).\nconst STEP_TOLERANCE = 0.01;\n\n// Same-asset check for a timeframe switch: both series end \"now\", so their\n// last closes must be close. No asset moves 25% between two consecutive prop\n// pushes; distinct assets within 25% of each other are what `seriesKey` is for.\nconst MAX_SAME_ASSET_CLOSE_RATIO = 1.25;\n\n// A coarser bucketing can shift the final bar's open by up to one coarse bar;\n// allow that plus an in-flight bar when checking the two series end together.\nconst MAX_END_DRIFT_STEPS = 3;\n\n// Streaming pushes may batch a few bars (e.g. a throttled background tab), but\n// a jump of more than this many steps means the data was re-fetched elsewhere.\nconst MAX_STREAM_ADVANCE_STEPS = 5;\n\n/**\n * The candle period in ms, inferred as the median of the first few intervals\n * (robust to a single gap). Null when there are fewer than two candles.\n */\nexport function inferStepMs(candles: Candle[]): number | null {\n if (candles.length < 2) return null;\n const k = Math.min(candles.length - 1, 8);\n const diffs: number[] = [];\n for (let i = 0; i < k; i++) diffs.push(candles[i + 1].timeMs - candles[i].timeMs);\n diffs.sort((a, b) => a - b);\n const median = diffs[Math.floor(diffs.length / 2)];\n return median > 0 ? median : null;\n}\n\n// Index of the candle whose timeMs exactly equals `t`, or -1. Binary search over\n// the ascending-by-time series, so it tolerates interior gaps (missing bars from\n// downtime / illiquid periods) — unlike a uniform-grid index computed from the\n// step, which assumes a hole-free grid.\nfunction indexByTime(candles: Candle[], t: number): number {\n let lo = 0;\n let hi = candles.length - 1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n const v = candles[mid].timeMs;\n if (v === t) return mid;\n if (v < t) lo = mid + 1;\n else hi = mid - 1;\n }\n return -1;\n}\n\n/**\n * Classify a candles-prop change. `prev` is the previously rendered array\n * (null on first render); `seriesKeyChanged` forces `reset` regardless of the\n * data (the explicit escape hatch).\n *\n * Constraint: detection compares two immutable snapshots. An array mutated in\n * place (same reference) never reaches this code — React props must change\n * identity to re-render.\n */\nexport function classifyTransition(\n prev: Candle[] | null,\n next: Candle[],\n seriesKeyChanged: boolean,\n): DataTransition {\n if (!prev || prev.length === 0) return 'initial';\n if (next.length === 0) return 'stream'; // nothing to reframe against\n if (seriesKeyChanged) return 'reset';\n\n const prevStep = inferStepMs(prev);\n const nextStep = inferStepMs(next);\n if (prevStep == null || nextStep == null) return 'reset'; // too little data to reason\n\n const prevLast = prev[prev.length - 1];\n const nextLast = next[next.length - 1];\n\n if (Math.abs(nextStep - prevStep) <= prevStep * STEP_TOLERANCE) {\n // Same step: streaming iff prev's last bar still appears in next (covers\n // append, update-last, and rolling buffers that drop old bars from the\n // front) and the series only advanced by a few bars. Locate that bar by\n // timestamp, not by a step-derived index — real series have interior gaps\n // (downtime / illiquid periods), so a uniform-grid index would miss it and\n // misread a harmless in-place tick as a reset.\n // Time alignment alone isn't enough: two assets on the same exchange share\n // the bar grid, so the bar at the shared timestamp must also be (nearly) the\n // same bar — update-last moves the close, but never by the same-asset ratio.\n const idx = indexByTime(next, prevLast.timeMs);\n const aligned = idx >= 0;\n const sharedBarRatio =\n aligned && next[idx].close > 0 && prevLast.close > 0\n ? Math.max(next[idx].close / prevLast.close, prevLast.close / next[idx].close)\n : Infinity;\n const advanced =\n nextLast.timeMs >= prevLast.timeMs &&\n nextLast.timeMs - prevLast.timeMs <= MAX_STREAM_ADVANCE_STEPS * nextStep;\n return sharedBarRatio <= MAX_SAME_ASSET_CLOSE_RATIO && advanced ? 'stream' : 'reset';\n }\n\n // Step changed: a timeframe switch iff it still looks like the same asset —\n // last closes near each other and both series ending around the same time.\n const closeRatio =\n prevLast.close > 0 && nextLast.close > 0\n ? Math.max(nextLast.close / prevLast.close, prevLast.close / nextLast.close)\n : Infinity;\n const prevEnd = prevLast.timeMs + prevStep;\n const nextEnd = nextLast.timeMs + nextStep;\n const endsTogether =\n Math.abs(nextEnd - prevEnd) <= MAX_END_DRIFT_STEPS * Math.max(prevStep, nextStep);\n return closeRatio <= MAX_SAME_ASSET_CLOSE_RATIO && endsTogether ? 'timeframe' : 'reset';\n}\n\n/**\n * The visible window to apply after a timeframe switch so each candle keeps\n * the exact pixel width it had before: the visible slot count is preserved and\n * the right edge re-anchors on the newest candle (any future-gap overshoot is\n * carried over in slots, clamped to the core's 3/4-window cap). The new start\n * may precede the first candle — that gap is intentional, width wins.\n */\nexport function timeframeWindow(\n oldWindow: VisibleRange,\n oldStepMs: number,\n oldLastMs: number,\n newStepMs: number,\n newLastMs: number,\n): VisibleRange {\n const slots = (oldWindow.endMs - oldWindow.startMs) / oldStepMs;\n const offsetRaw = (oldWindow.endMs - oldLastMs) / oldStepMs;\n const offsetSlots = Math.min(Math.max(offsetRaw, 0), slots * 0.75);\n const endMs = Math.round(newLastMs + offsetSlots * newStepMs);\n return { startMs: Math.round(endMs - slots * newStepMs), endMs };\n}\n","// Pointer/wheel gesture controller for the web chart. Maps input to the same\n// VroomChartHandle mutators the React Native gestures use (see\n// packages/react-native/src/VroomChart.tsx), so behavior matches across\n// platforms. Listeners are attached natively (not via React props) so wheel can\n// be non-passive and call preventDefault.\n\nimport { useEffect, useRef } from 'react';\nimport type { ChartMode, CrosshairEvent, Drawing, DrawTool } from '@vroomchart/types';\nimport type { VroomChartHandle } from '@vroomchart/core-wasm';\n\ntype Region = 'chart' | 'price-axis' | 'time-axis' | 'indicator' | 'separator' | 'indicator-axis';\n\nexport type GestureOptions = {\n crosshairOffset: number;\n /** Interaction mode. In 'draw' mode panning/zooming/crosshair are suppressed. */\n mode?: ChartMode;\n /** Active drawing tool while in 'draw' mode. */\n tool?: DrawTool;\n onCrosshair?: (e: CrosshairEvent) => void;\n /** External crosshair to mirror (data space), or null. See VroomChartCoreProps. */\n crosshairOverride?: { timeMs: number; price: number } | null;\n onViewportChange?: (startMs: number, endMs: number) => void;\n /** Fired with the finished line when the user places its second point. */\n onDrawingComplete?: (drawing: Drawing) => void;\n /** Fired when the chart wants the host to change mode (e.g. exit on click-away). */\n onRequestMode?: (mode: ChartMode) => void;\n};\n\nconst MIN_SPAN = 24; // px — minimum two-finger span for an axis to scale\nconst AXIS_RATIO = 0.5; // an axis scales only if its span ≥ this × the other's\nconst LONG_PRESS_MS = 350;\nconst MOVE_THRESH = 6; // px before a press becomes a drag\nconst WHEEL_K = 0.0015; // wheel delta → zoom factor exponent\nconst SEP_HIT = 4; // px band around the indicator separator for hit-testing\n\n// Drawing-tool styling: the guideline (and committed line) default to solid blue\n// at 2px, matching the core's default and useChartCore's drawing color.\nconst DRAW_COLOR = 0xff2962ff;\nconst DRAW_WIDTH = 2;\nconst DRAW_HIT = 6; // px tolerance for \"clicked on the selected line\" vs. away\n\n// Stable unique id for a freshly drawn line.\nfunction drawingId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `draw-${Math.random().toString(36).slice(2)}`;\n}\n\n// Distance in px from point (px,py) to the segment (ax,ay)-(bx,by).\nfunction distToSegment(\n px: number,\n py: number,\n ax: number,\n ay: number,\n bx: number,\n by: number,\n): number {\n const dx = bx - ax;\n const dy = by - ay;\n const len2 = dx * dx + dy * dy;\n let t = len2 > 0 ? ((px - ax) * dx + (py - ay) * dy) / len2 : 0;\n t = Math.max(0, Math.min(1, t));\n return Math.hypot(px - (ax + t * dx), py - (ay + t * dy));\n}\n\nexport function useGestures(\n containerRef: React.RefObject<HTMLElement | null>,\n handleRef: React.RefObject<VroomChartHandle | null>,\n scheduleRender: () => void,\n opts: GestureOptions,\n): void {\n // Keep latest opts in a ref so the effect's listeners stay stable.\n const optsRef = useRef(opts);\n optsRef.current = opts;\n\n // Drawing-tool state. Lives in refs (not effect-local) so the mode-change\n // effect below can reset it when the host leaves draw mode, and so it survives\n // the gesture effect's stable-listener lifecycle.\n // drawAnchor* — first point placed, awaiting the second (data + px coords).\n // drawSelectedPx — set once a line is committed: its px endpoints, used to\n // hit-test \"clicked on the line\" vs. \"clicked away to exit\".\n const drawAnchorRef = useRef<{ timeMs: number; price: number } | null>(null);\n const drawAnchorPxRef = useRef<{ x: number; y: number } | null>(null);\n const drawSelectedPxRef = useRef<{\n ax: number;\n ay: number;\n bx: number;\n by: number;\n } | null>(null);\n\n // True while a local pointer (hover/press) owns the crosshair. Lets the\n // crosshair-override effect below know to stand down (local input wins).\n const localCrosshairActiveRef = useRef(false);\n\n // Reset the in-progress draft whenever draw+line mode is not active (e.g. the\n // host toggled the tool off mid-draw), so re-entering draw mode starts clean.\n useEffect(() => {\n const active = opts.mode === 'draw' && opts.tool === 'line';\n if (active) return;\n drawAnchorRef.current = null;\n drawAnchorPxRef.current = null;\n drawSelectedPxRef.current = null;\n const h = handleRef.current;\n if (h) {\n h.clearDraft();\n scheduleRender();\n }\n }, [opts.mode, opts.tool, handleRef, scheduleRender]);\n\n // Apply the controlled crosshair override (cross-chart sync). A local\n // hover/press always wins, so we stand down while one is active; the override\n // is (re)applied on local release inside hideCrosshair. This path is silent —\n // it never calls onCrosshair — so two charts driving each other can't loop.\n // (If the override is set before the WASM handle finishes loading it applies\n // on its next change; in the sync use case the initial value is null.)\n useEffect(() => {\n const h = handleRef.current;\n if (!h || localCrosshairActiveRef.current) return;\n const ov = opts.crosshairOverride;\n if (ov) h.setCrosshairData(ov.timeMs, ov.price);\n else h.clearCrosshair();\n scheduleRender();\n }, [opts.crosshairOverride, handleRef, scheduleRender]);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n const pointers = new Map<number, { x: number; y: number }>();\n let panMode: Region = 'chart';\n let crosshairActive = false;\n let crosshairSource: 'press' | 'hover' | null = null;\n let lastCrosshairTime: number | null = null;\n let lastCrosshairPrice: number | null = null;\n let longPressTimer: ReturnType<typeof setTimeout> | null = null;\n let downX = 0;\n let downY = 0;\n let moved = false;\n const pinch = { spanX: 1, spanY: 1, ratioX: 1, ratioY: 1, enableX: false, enableY: false, active: false };\n\n const rel = (e: PointerEvent | WheelEvent) => {\n const r = el.getBoundingClientRect();\n return { x: e.clientX - r.left, y: e.clientY - r.top };\n };\n\n const regionAt = (x: number, y: number): Region => {\n const h = handleRef.current;\n const r = el.getBoundingClientRect();\n if (!h) return 'chart';\n const { yAxisWidth, xAxisHeight, indicatorHeight } = h.getAxisMetrics();\n if (x > r.width - yAxisWidth) {\n // The y-axis strip beside an indicator pane scales that pane's y-axis;\n // the rest of the strip scales the main price axis.\n const priceBottom = r.height - xAxisHeight - indicatorHeight;\n if (indicatorHeight > 0 && y > priceBottom && y < r.height - xAxisHeight) {\n return 'indicator-axis';\n }\n return 'price-axis';\n }\n // Separator sits at the top edge of the indicator band, within the candle\n // area width — a narrow grab band for resizing the panes.\n const sepY = r.height - xAxisHeight - indicatorHeight;\n if (indicatorHeight > 0 && x <= r.width - yAxisWidth && Math.abs(y - sepY) <= SEP_HIT) {\n return 'separator';\n }\n if (y > r.height - xAxisHeight) return 'time-axis';\n if (indicatorHeight > 0 && y > r.height - xAxisHeight - indicatorHeight) return 'indicator';\n return 'chart';\n };\n\n const reportCrosshair = (reason: CrosshairEvent['reason']) => {\n const h = handleRef.current;\n if (!h) return;\n // Snapped slot — has a timeMs even in the empty space ahead of the last\n // candle, where `candle` is null.\n const info = h.getCrosshairInfo();\n const t = info?.timeMs ?? null;\n const p = info?.price ?? null;\n // Fire on any positional change — a different candle (time) OR a vertical\n // move within the same candle (price). Deduping on time alone would freeze\n // the reported price mid-candle, which breaks cross-chart price sync.\n if (reason === 'move' && t === lastCrosshairTime && p === lastCrosshairPrice) return;\n lastCrosshairTime = t;\n lastCrosshairPrice = p;\n optsRef.current.onCrosshair?.({\n active: reason !== 'hide',\n candle: info?.candle ?? null,\n timeMs: t,\n price: reason === 'hide' ? null : p,\n reason,\n });\n };\n\n const clearLongPress = () => {\n if (longPressTimer != null) {\n clearTimeout(longPressTimer);\n longPressTimer = null;\n }\n };\n\n const showCrosshair = (x: number, y: number, source: 'press' | 'hover', reason: 'show' | 'move') => {\n const h = handleRef.current;\n if (!h) return;\n crosshairActive = true;\n localCrosshairActiveRef.current = true;\n crosshairSource = source;\n const lift = source === 'press' ? optsRef.current.crosshairOffset : 0;\n h.setCrosshair(x, y - lift);\n scheduleRender();\n reportCrosshair(reason);\n };\n\n const hideCrosshair = () => {\n const h = handleRef.current;\n if (!h || !crosshairActive) return;\n crosshairActive = false;\n localCrosshairActiveRef.current = false;\n crosshairSource = null;\n // Fall back to the synced crosshair (if any) rather than clearing, so\n // releasing a local hover on a follower chart restores the driver's line.\n const ov = optsRef.current.crosshairOverride;\n if (ov) h.setCrosshairData(ov.timeMs, ov.price);\n else h.clearCrosshair();\n scheduleRender();\n reportCrosshair('hide');\n };\n\n // True while the line tool should own input (suppress pan/zoom/crosshair).\n const drawActive = () =>\n optsRef.current.mode === 'draw' && optsRef.current.tool === 'line';\n\n // While placing the second point, keep the guideline glued to the cursor.\n const updateGuideline = (x: number, y: number) => {\n const h = handleRef.current;\n if (!h) return;\n if (!drawAnchorRef.current || drawSelectedPxRef.current) return;\n const c = h.coordAt(x, y);\n if (!c) return;\n const a = drawAnchorRef.current;\n h.setDraft(a.timeMs, a.price, true, c.timeMs, c.price, true, DRAW_COLOR, DRAW_WIDTH);\n scheduleRender();\n };\n\n // A tap in the chart area: place the next point, or exit on click-away.\n const handleDrawClick = (x: number, y: number) => {\n const h = handleRef.current;\n if (!h) return;\n const o = optsRef.current;\n\n // A committed line is selected: clicking on it keeps it; clicking away\n // hides the nodes and asks the host to return to pan mode.\n const sel = drawSelectedPxRef.current;\n if (sel) {\n if (distToSegment(x, y, sel.ax, sel.ay, sel.bx, sel.by) <= DRAW_HIT) return;\n drawAnchorRef.current = null;\n drawAnchorPxRef.current = null;\n drawSelectedPxRef.current = null;\n h.clearDraft();\n scheduleRender();\n el.style.cursor = '';\n o.onRequestMode?.('pan');\n return;\n }\n\n const coord = h.coordAt(x, y);\n if (!coord) return;\n\n if (!drawAnchorRef.current) {\n // First point: show node A; the guideline follows on the next move.\n drawAnchorRef.current = coord;\n drawAnchorPxRef.current = { x, y };\n h.setDraft(coord.timeMs, coord.price, false, 0, 0, true, DRAW_COLOR, DRAW_WIDTH);\n scheduleRender();\n } else {\n // Second point: commit the line and keep both nodes shown (selected).\n const a = drawAnchorRef.current;\n const apx = drawAnchorPxRef.current!;\n o.onDrawingComplete?.({\n id: drawingId(),\n type: 'line',\n points: [\n { timeMs: a.timeMs, price: a.price },\n { timeMs: coord.timeMs, price: coord.price },\n ],\n });\n drawSelectedPxRef.current = { ax: apx.x, ay: apx.y, bx: x, by: y };\n h.setDraft(a.timeMs, a.price, true, coord.timeMs, coord.price, false, DRAW_COLOR, DRAW_WIDTH);\n scheduleRender();\n }\n };\n\n const onPointerDown = (e: PointerEvent) => {\n const h = handleRef.current;\n if (!h) return;\n if (e.pointerType === 'mouse' && e.button !== 0) return; // left button only\n const { x, y } = rel(e);\n el.setPointerCapture(e.pointerId);\n pointers.set(e.pointerId, { x, y });\n\n if (pointers.size === 2) {\n clearLongPress();\n hideCrosshair();\n const pts = [...pointers.values()];\n const sx = Math.abs(pts[0]!.x - pts[1]!.x);\n const sy = Math.abs(pts[0]!.y - pts[1]!.y);\n pinch.spanX = sx;\n pinch.spanY = sy;\n pinch.ratioX = 1;\n pinch.ratioY = 1;\n pinch.enableX = sx >= MIN_SPAN && sx >= sy * AXIS_RATIO;\n pinch.enableY = sy >= MIN_SPAN && sy >= sx * AXIS_RATIO;\n pinch.active = true;\n return;\n }\n\n // Single pointer: classify region; arm long-press → crosshair (touch/pen).\n downX = x;\n downY = y;\n moved = false;\n panMode = regionAt(x, y);\n if (e.pointerType !== 'mouse' && panMode === 'chart' && !drawActive()) {\n longPressTimer = setTimeout(() => {\n longPressTimer = null;\n if (!moved) showCrosshair(downX, downY, 'press', 'show');\n }, LONG_PRESS_MS);\n }\n };\n\n const onPointerMove = (e: PointerEvent) => {\n const h = handleRef.current;\n if (!h) return;\n const { x, y } = rel(e);\n\n // Hover (mouse, no button) → crosshair follows the cursor on the chart.\n if (!pointers.has(e.pointerId)) {\n if (e.pointerType === 'mouse' && pointers.size === 0) {\n // Draw mode: no crosshair; the guideline tracks the cursor instead.\n if (drawActive()) {\n el.style.cursor = 'crosshair';\n updateGuideline(x, y);\n return;\n }\n const region = regionAt(x, y);\n el.style.cursor =\n region === 'separator'\n ? 'row-resize'\n : region === 'indicator-axis'\n ? 'ns-resize'\n : '';\n if (region === 'chart') {\n showCrosshair(x, y, 'hover', crosshairActive ? 'move' : 'show');\n } else {\n hideCrosshair();\n }\n }\n return;\n }\n\n const prev = pointers.get(e.pointerId)!;\n const dx = x - prev.x;\n const dy = y - prev.y;\n pointers.set(e.pointerId, { x, y });\n\n // Two-finger directional pinch (disabled while drawing).\n if (pointers.size >= 2 && pinch.active && !drawActive()) {\n const pts = [...pointers.values()];\n const focalX = (pts[0]!.x + pts[1]!.x) / 2;\n const focalY = (pts[0]!.y + pts[1]!.y) / 2;\n let frameX = 1;\n if (pinch.enableX) {\n const r = Math.max(Math.abs(pts[0]!.x - pts[1]!.x), MIN_SPAN) / pinch.spanX;\n frameX = r / pinch.ratioX;\n pinch.ratioX = r;\n }\n let frameY = 1;\n if (pinch.enableY) {\n const r = Math.max(Math.abs(pts[0]!.y - pts[1]!.y), MIN_SPAN) / pinch.spanY;\n frameY = r / pinch.ratioY;\n pinch.ratioY = r;\n }\n if (frameX !== 1 || frameY !== 1) {\n h.zoom(frameX, frameY, focalX, focalY);\n scheduleRender();\n }\n return;\n }\n\n if (!moved && Math.hypot(x - downX, y - downY) > MOVE_THRESH) {\n moved = true;\n clearLongPress();\n }\n if (!moved) return;\n\n // Draw mode: a press-drag neither pans nor moves a crosshair; if a point\n // is down, keep the guideline tracking the cursor so a drag still previews.\n if (drawActive()) {\n if (panMode === 'chart') updateGuideline(x, y);\n return;\n }\n\n // Chart-area drag with the (press) crosshair up moves the crosshair.\n if (crosshairActive && crosshairSource === 'press' && panMode === 'chart') {\n showCrosshair(x, y, 'press', 'move');\n return;\n }\n\n if (panMode === 'price-axis') h.scalePriceAxis(dy);\n else if (panMode === 'time-axis') h.scaleTimeAxis(dx);\n else if (panMode === 'separator') h.resizeIndicatorPane(dy);\n else if (panMode === 'indicator-axis') h.scaleIndicatorAxis(downY, dy);\n else if (panMode === 'indicator') h.pan(dx, 0);\n else h.translate(dx, dy);\n\n // Keep a hover crosshair glued to the cursor while dragging — the chart\n // pans under it, so re-snap to whatever candle is now beneath the cursor.\n if (crosshairActive && crosshairSource === 'hover') {\n h.setCrosshair(x, y);\n reportCrosshair('move');\n }\n scheduleRender();\n };\n\n const endPointer = (e: PointerEvent) => {\n const had = pointers.delete(e.pointerId);\n clearLongPress();\n if (pointers.size < 2) pinch.active = false;\n if (!had) return;\n\n // Draw mode: a stationary tap in the chart places a point (or exits on\n // click-away). Drags are ignored (no pan). Crosshair logic is skipped.\n if (drawActive() && pointers.size === 0) {\n if (!moved && panMode === 'chart') handleDrawClick(downX, downY);\n else el.style.cursor = 'crosshair';\n return;\n }\n\n // Press crosshair is active only while held — release dismisses it.\n if (crosshairSource === 'press' && pointers.size === 0) {\n hideCrosshair();\n } else if (moved && (panMode === 'chart' || panMode === 'indicator')) {\n optsRef.current.onViewportChange?.(0, 0);\n }\n if (pointers.size === 0) el.style.cursor = '';\n };\n\n const onPointerLeave = () => {\n if (crosshairSource === 'hover') hideCrosshair();\n el.style.cursor = '';\n };\n\n const onWheel = (e: WheelEvent) => {\n const h = handleRef.current;\n if (!h) return;\n e.preventDefault();\n // Draw mode: no pan/zoom from the wheel (keeps placed points stable).\n if (drawActive()) return;\n const { x, y } = rel(e);\n if (e.ctrlKey || e.metaKey) {\n // Trackpad pinch (sent as ctrl+wheel) / ctrl+wheel → zoom both axes.\n const f = Math.exp(-e.deltaY * WHEEL_K);\n h.zoom(f, f, x, y);\n } else if (e.shiftKey) {\n // Shift+wheel → horizontal pan (mouse-wheel-only users).\n h.pan(-(e.deltaX || e.deltaY), 0);\n } else if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) {\n // Horizontal scroll → pan the time axis (scroll right = forward in time).\n h.pan(-e.deltaX, 0);\n } else {\n // Vertical scroll → zoom the time window around the cursor.\n const f = Math.exp(-e.deltaY * WHEEL_K);\n h.zoom(f, 1, x, y);\n }\n scheduleRender();\n };\n\n el.addEventListener('pointerdown', onPointerDown);\n el.addEventListener('pointermove', onPointerMove);\n el.addEventListener('pointerup', endPointer);\n el.addEventListener('pointercancel', endPointer);\n el.addEventListener('pointerleave', onPointerLeave);\n el.addEventListener('wheel', onWheel, { passive: false });\n\n return () => {\n clearLongPress();\n el.removeEventListener('pointerdown', onPointerDown);\n el.removeEventListener('pointermove', onPointerMove);\n el.removeEventListener('pointerup', endPointer);\n el.removeEventListener('pointercancel', endPointer);\n el.removeEventListener('pointerleave', onPointerLeave);\n el.removeEventListener('wheel', onWheel);\n };\n }, [containerRef, handleRef, scheduleRender]);\n}\n","// VroomChart (web) — DOM React component that renders the candlestick chart to\n// a <canvas> via @vroomchart/core-wasm and wires pointer/wheel gestures. API-matched\n// to the React Native component (same props from @vroomchart/types).\n\nimport React from 'react';\nimport type { CSSProperties } from 'react';\n\nimport { useChartCore } from './useChartCore';\nimport { useGestures } from './useGestures';\nimport type { VroomChartProps } from './types';\n\nconst FILL: CSSProperties = { position: 'relative', width: '100%', height: '100%' };\nconst CANVAS_STYLE: CSSProperties = {\n display: 'block',\n width: '100%',\n height: '100%',\n touchAction: 'none', // we own pan/zoom; don't let the browser scroll/zoom\n};\n\n/**\n * Skia-quality candlestick chart for the web. Pass OHLCV `candles` and size it\n * via `style`/`width`/`height` (it fills its parent by default). Drag to pan,\n * pinch or wheel to zoom, drag the price/time axes to rescale, hover (mouse) or\n * long-press (touch) for the crosshair. Optional indicators (`rsi`, `macd`,\n * `movingAverages`, `vwap`), colors (`theme`), and events (`onCrosshair`,\n * `onViewportChange`) are configured through props.\n *\n * @see {@link VroomChartProps} for the full prop reference.\n */\nexport function VroomChart(props: VroomChartProps) {\n const {\n className,\n style,\n wasm,\n crosshairOffset = 40,\n crosshairOverride,\n mode,\n tool,\n onCrosshair,\n onViewportChange,\n onDrawingComplete,\n onModeChange,\n } = props;\n const { containerRef, canvasRef, handleRef, scheduleRender } = useChartCore(\n props,\n wasm ? { wasm } : undefined,\n );\n\n useGestures(containerRef, handleRef, scheduleRender, {\n crosshairOffset,\n crosshairOverride,\n mode,\n tool,\n onCrosshair,\n onViewportChange,\n onDrawingComplete,\n onRequestMode: onModeChange,\n });\n\n const rootStyle: CSSProperties = {\n ...FILL,\n ...(props.width != null ? { width: props.width } : null),\n ...(props.height != null ? { height: props.height } : null),\n ...style,\n };\n\n return (\n <div ref={containerRef} className={className} style={rootStyle}>\n <canvas ref={canvasRef} style={CANVAS_STYLE} />\n </div>\n );\n}\n"],"mappings":"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","names":["useEffect","useRef"]}
1
+ {"version":3,"sources":["../src/useChartCore.ts","../src/dataTransitions.ts","../src/useGestures.ts","../src/VroomChart.tsx"],"sourcesContent":["// Owns one VroomChartHandle bound to a <canvas>: loads the core, measures the\n// container (ResizeObserver), pushes data/size/theme/indicators on change, and\n// drives a rAF-batched present() loop. The web analogue of\n// packages/react-native/src/useChartCore.ts — but it paints the canvas via\n// handle.present() instead of producing an SkPicture.\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n loadVroom,\n packCandles,\n applyTheme,\n parseColor,\n type LoadVroomOptions,\n type OverlaySpec,\n type DrawingSpec,\n type LiquiditySpec,\n type VroomChartHandle,\n} from '@vroomchart/core-wasm';\nimport type { VroomChartCoreProps } from '@vroomchart/types';\nimport {\n classifyTransition,\n inferStepMs,\n timeframeWindow,\n type DataTransition,\n} from './dataTransitions';\n\n// Mirrors vroom::ma::Source order (packages/core/src/ma.h).\nconst MA_SOURCES = ['close', 'open', 'high', 'low', 'hl2', 'hlc3', 'ohlc4'] as const;\n\nfunction overlayToNumeric(\n o: NonNullable<VroomChartCoreProps['movingAverages']>[number],\n): OverlaySpec {\n const srcIdx = o.source ? MA_SOURCES.indexOf(o.source) : 0;\n return {\n kind: o.kind === 'ema' ? 1 : 0,\n period: o.length,\n source: srcIdx < 0 ? 0 : srcIdx,\n color: (o.color != null ? parseColor(o.color) : null) ?? 0xff2962ff,\n width: o.width ?? 1.5,\n };\n}\n\nfunction drawingToSpec(\n d: NonNullable<VroomChartCoreProps['drawings']>[number],\n): DrawingSpec {\n return {\n aTime: d.points[0].timeMs,\n aPrice: d.points[0].price,\n bTime: d.points[1].timeMs,\n bPrice: d.points[1].price,\n color: (d.color != null ? parseColor(d.color) : null) ?? 0xff2962ff,\n width: d.width ?? 2,\n };\n}\n\n// Default buy/sell colors (teal-green / red), matching the accent palette.\nconst DEFAULT_BUY_COLOR = 0xff26a69a;\nconst DEFAULT_SELL_COLOR = 0xffef5350;\n\nfunction liquidityToSpec(\n cfg: NonNullable<VroomChartCoreProps['liquidity']>,\n): LiquiditySpec {\n return {\n bands: cfg.bands.map((b) => ({\n minPrice: b.minPrice,\n maxPrice: b.maxPrice,\n side: b.side === 'sell' ? 1 : 0,\n volume: b.volume,\n })),\n buyColor: (cfg.buyColor != null ? parseColor(cfg.buyColor) : null) ?? DEFAULT_BUY_COLOR,\n sellColor:\n (cfg.sellColor != null ? parseColor(cfg.sellColor) : null) ?? DEFAULT_SELL_COLOR,\n maxVolume: cfg.maxVolume ?? 0,\n minOpacity: cfg.minOpacity ?? 0.05,\n maxOpacity: cfg.maxOpacity ?? 0.8,\n widthPx: cfg.widthPx ?? 300,\n widthFrac: cfg.widthFrac ?? 0.25,\n };\n}\n\n// Cleared overlay: an empty band set (style values are irrelevant when there are\n// no bands, but the spec shape requires them).\nconst EMPTY_LIQUIDITY: LiquiditySpec = {\n bands: [],\n buyColor: DEFAULT_BUY_COLOR,\n sellColor: DEFAULT_SELL_COLOR,\n maxVolume: 0,\n minOpacity: 0.05,\n maxOpacity: 0.8,\n widthPx: 300,\n widthFrac: 0.25,\n};\n\nexport type UseChartCore = {\n containerRef: React.RefObject<HTMLDivElement | null>;\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n handleRef: React.RefObject<VroomChartHandle | null>;\n /** Schedules a rAF-batched repaint (and keeps ticking while animating). */\n scheduleRender: () => void;\n /** Measured CSS size of the container (0 until first layout). */\n size: { width: number; height: number };\n};\n\nexport function useChartCore(\n props: VroomChartCoreProps,\n loadOpts?: LoadVroomOptions,\n): UseChartCore {\n const {\n candles,\n seriesKey,\n width: widthProp,\n height: heightProp,\n visibleRange,\n defaultCandleWidth,\n theme,\n rsi,\n macd,\n movingAverages,\n vwap,\n drawings,\n liquidity,\n } = props;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const handleRef = useRef<VroomChartHandle | null>(null);\n // What the core currently holds, for classifying the next data change.\n // Keyed by handle so a recreated core is treated as a fresh initial load.\n const prevDataRef = useRef<{\n handle: VroomChartHandle;\n candles: VroomChartCoreProps['candles'];\n seriesKey?: string;\n } | null>(null);\n const rafRef = useRef<number | null>(null);\n const [ready, setReady] = useState(false);\n const [measured, setMeasured] = useState({ width: 0, height: 0 });\n\n // Captured at mount: which core to load (stub vs Skia-WASM). Changing it after\n // mount has no effect — the core is created once and shared process-wide.\n const loadOptsRef = useRef(loadOpts);\n loadOptsRef.current = loadOpts;\n\n const width = widthProp ?? measured.width;\n const height = heightProp ?? measured.height;\n\n const scheduleRender = useCallback(() => {\n if (rafRef.current != null) return;\n rafRef.current = requestAnimationFrame(() => {\n rafRef.current = null;\n const h = handleRef.current;\n if (!h) return;\n h.present();\n if (h.isAnimating()) scheduleRender();\n });\n }, []);\n\n // Create the handle once the canvas exists; destroy on unmount.\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n let disposed = false;\n loadVroom(loadOptsRef.current).then((mod) => {\n if (disposed) return;\n handleRef.current = mod.create(canvas);\n setReady(true);\n });\n return () => {\n disposed = true;\n if (rafRef.current != null) cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n handleRef.current?.destroy();\n handleRef.current = null;\n setReady(false);\n };\n }, []);\n\n // Measure the container (unless both dims are pinned via props).\n useEffect(() => {\n const el = containerRef.current;\n if (!el || (widthProp != null && heightProp != null)) return;\n const ro = new ResizeObserver((entries) => {\n const r = entries[0]?.contentRect;\n if (!r) return;\n const w = Math.round(r.width);\n const h = Math.round(r.height);\n setMeasured((prev) => (prev.width === w && prev.height === h ? prev : { width: w, height: h }));\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, [widthProp, heightProp]);\n\n // Stable deps so inline object/array literals don't re-run every render.\n const themeKey = theme ? JSON.stringify(theme) : '';\n const rsiKey = rsi ? JSON.stringify(rsi) : '';\n const macdKey = macd ? JSON.stringify(macd) : '';\n const maKey = movingAverages ? JSON.stringify(movingAverages) : '';\n const vwapKey = vwap ? JSON.stringify(vwap) : '';\n const drawingsKey = drawings ? JSON.stringify(drawings) : '';\n const liquidityKey = liquidity ? JSON.stringify(liquidity) : '';\n const explicit = visibleRange != null;\n const startMs = visibleRange?.startMs ?? 0;\n const endMs = visibleRange?.endMs ?? 0;\n\n // Push everything into the core whenever data/size/config changes, then paint.\n useEffect(() => {\n const h = handleRef.current;\n if (!h || width <= 0 || height <= 0) return;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n h.setSize(width, height, dpr);\n if (candles.length > 0) {\n const prev = prevDataRef.current;\n const freshHandle = prev == null || prev.handle !== h;\n if (freshHandle || prev.candles !== candles || prev.seriesKey !== seriesKey) {\n // A fresh core frames itself (its window starts at 0/0); an explicit\n // visibleRange prop overrides any auto behavior, so treat the change\n // like a stream and let the range application below win.\n const transition: DataTransition = freshHandle\n ? 'initial'\n : explicit\n ? 'stream'\n : classifyTransition(prev.candles, candles, seriesKey !== prev.seriesKey);\n\n // Capture the outgoing view before setCandles re-infers the candle\n // period from the new data.\n let tfArgs: { oldWindow: { startMs: number; endMs: number }; oldStepMs: number; oldLastMs: number } | null =\n null;\n if (transition === 'timeframe' && prev != null) {\n const oldWindow = h.getVisibleRange();\n const oldStepMs = inferStepMs(prev.candles);\n if (oldWindow.endMs > oldWindow.startMs && oldStepMs != null) {\n tfArgs = { oldWindow, oldStepMs, oldLastMs: prev.candles[prev.candles.length - 1].timeMs };\n }\n }\n\n // Drive the initial zoom from a target candle width, but only on a\n // fresh handle and only when the caller isn't explicitly controlling the\n // range. Pushed before setCandles so the core's default framing (run\n // inside setCandles while the window is still 0/0) picks it up.\n if (\n freshHandle &&\n !explicit &&\n defaultCandleWidth != null &&\n defaultCandleWidth > 0\n ) {\n h.setDefaultCandleWidth(defaultCandleWidth);\n }\n\n h.setCandles(packCandles(candles));\n\n if (transition === 'timeframe') {\n const newStepMs = inferStepMs(candles);\n if (tfArgs && newStepMs != null) {\n const w = timeframeWindow(\n tfArgs.oldWindow,\n tfArgs.oldStepMs,\n tfArgs.oldLastMs,\n newStepMs,\n candles[candles.length - 1].timeMs,\n );\n h.setVisibleRange(w.startMs, w.endMs);\n }\n h.resetPriceScale();\n } else if (transition === 'reset') {\n h.resetView();\n }\n prevDataRef.current = { handle: h, candles, seriesKey };\n }\n }\n if (explicit) h.setVisibleRange(startMs, endMs);\n if (theme) applyTheme(h, theme);\n h.setRSI(\n rsi?.enabled ?? false,\n rsi?.period ?? 14,\n rsi?.upperBand ?? 70,\n rsi?.lowerBand ?? 30,\n rsi?.maEnabled ?? true,\n rsi?.maPeriod ?? 14,\n );\n h.setMACD(macd?.enabled ?? false, macd?.fast ?? 12, macd?.slow ?? 26, macd?.signal ?? 9);\n h.setOverlays((movingAverages ?? []).map(overlayToNumeric));\n h.setVWAP(\n vwap?.enabled ?? false,\n vwap?.resetMinutes ?? 0,\n (vwap?.color != null ? parseColor(vwap.color) : null) ?? 0xff00bcd4,\n vwap?.width ?? 1.5,\n );\n h.setDrawings((drawings ?? []).map(drawingToSpec));\n h.setLiquidity(\n liquidity?.bands?.length ? liquidityToSpec(liquidity) : EMPTY_LIQUIDITY,\n );\n scheduleRender();\n // theme/rsi/macd/movingAverages/vwap/drawings/liquidity tracked via *Key deps.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ready, width, height, candles, seriesKey, explicit, startMs, endMs, defaultCandleWidth, themeKey, rsiKey, macdKey, maKey, vwapKey, drawingsKey, liquidityKey, scheduleRender]);\n\n return { containerRef, canvasRef, handleRef, scheduleRender, size: { width, height } };\n}\n","// Classifies how a new `candles` prop relates to the previous one so the chart\n// can react appropriately: leave the viewport alone for streaming updates,\n// re-anchor the time window for a timeframe switch, or fully reset the view\n// for a different asset. Pure functions, no React — see useChartCore for the\n// orchestration.\n\nimport type { Candle, VisibleRange } from '@vroomchart/types';\n\nexport type DataTransition = 'initial' | 'stream' | 'timeframe' | 'reset';\n\n// A step change below this ratio is treated as the same timeframe. Real steps\n// are exact integer ms; the tolerance only absorbs rounding/DST quirks (the\n// smallest real timeframe jump, 1m -> 2m, is 100% apart).\nconst STEP_TOLERANCE = 0.01;\n\n// Same-asset check for a timeframe switch: both series end \"now\", so their\n// last closes must be close. No asset moves 25% between two consecutive prop\n// pushes; distinct assets within 25% of each other are what `seriesKey` is for.\nconst MAX_SAME_ASSET_CLOSE_RATIO = 1.25;\n\n// A coarser bucketing can shift the final bar's open by up to one coarse bar;\n// allow that plus an in-flight bar when checking the two series end together.\nconst MAX_END_DRIFT_STEPS = 3;\n\n// Streaming pushes may batch a few bars (e.g. a throttled background tab), but\n// a jump of more than this many steps means the data was re-fetched elsewhere.\nconst MAX_STREAM_ADVANCE_STEPS = 5;\n\n/**\n * The candle period in ms, inferred as the median of the first few intervals\n * (robust to a single gap). Null when there are fewer than two candles.\n */\nexport function inferStepMs(candles: Candle[]): number | null {\n if (candles.length < 2) return null;\n const k = Math.min(candles.length - 1, 8);\n const diffs: number[] = [];\n for (let i = 0; i < k; i++) diffs.push(candles[i + 1].timeMs - candles[i].timeMs);\n diffs.sort((a, b) => a - b);\n const median = diffs[Math.floor(diffs.length / 2)];\n return median > 0 ? median : null;\n}\n\n// Index of the candle whose timeMs exactly equals `t`, or -1. Binary search over\n// the ascending-by-time series, so it tolerates interior gaps (missing bars from\n// downtime / illiquid periods) — unlike a uniform-grid index computed from the\n// step, which assumes a hole-free grid.\nfunction indexByTime(candles: Candle[], t: number): number {\n let lo = 0;\n let hi = candles.length - 1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n const v = candles[mid].timeMs;\n if (v === t) return mid;\n if (v < t) lo = mid + 1;\n else hi = mid - 1;\n }\n return -1;\n}\n\n/**\n * Classify a candles-prop change. `prev` is the previously rendered array\n * (null on first render); `seriesKeyChanged` forces `reset` regardless of the\n * data (the explicit escape hatch).\n *\n * Constraint: detection compares two immutable snapshots. An array mutated in\n * place (same reference) never reaches this code — React props must change\n * identity to re-render.\n */\nexport function classifyTransition(\n prev: Candle[] | null,\n next: Candle[],\n seriesKeyChanged: boolean,\n): DataTransition {\n if (!prev || prev.length === 0) return 'initial';\n if (next.length === 0) return 'stream'; // nothing to reframe against\n if (seriesKeyChanged) return 'reset';\n\n const prevStep = inferStepMs(prev);\n const nextStep = inferStepMs(next);\n if (prevStep == null || nextStep == null) return 'reset'; // too little data to reason\n\n const prevLast = prev[prev.length - 1];\n const nextLast = next[next.length - 1];\n\n if (Math.abs(nextStep - prevStep) <= prevStep * STEP_TOLERANCE) {\n // Same step: streaming iff prev's last bar still appears in next (covers\n // append, update-last, and rolling buffers that drop old bars from the\n // front) and the series only advanced by a few bars. Locate that bar by\n // timestamp, not by a step-derived index — real series have interior gaps\n // (downtime / illiquid periods), so a uniform-grid index would miss it and\n // misread a harmless in-place tick as a reset.\n // Time alignment alone isn't enough: two assets on the same exchange share\n // the bar grid, so the bar at the shared timestamp must also be (nearly) the\n // same bar — update-last moves the close, but never by the same-asset ratio.\n const idx = indexByTime(next, prevLast.timeMs);\n const aligned = idx >= 0;\n const sharedBarRatio =\n aligned && next[idx].close > 0 && prevLast.close > 0\n ? Math.max(next[idx].close / prevLast.close, prevLast.close / next[idx].close)\n : Infinity;\n const advanced =\n nextLast.timeMs >= prevLast.timeMs &&\n nextLast.timeMs - prevLast.timeMs <= MAX_STREAM_ADVANCE_STEPS * nextStep;\n return sharedBarRatio <= MAX_SAME_ASSET_CLOSE_RATIO && advanced ? 'stream' : 'reset';\n }\n\n // Step changed: a timeframe switch iff it still looks like the same asset —\n // last closes near each other and both series ending around the same time.\n const closeRatio =\n prevLast.close > 0 && nextLast.close > 0\n ? Math.max(nextLast.close / prevLast.close, prevLast.close / nextLast.close)\n : Infinity;\n const prevEnd = prevLast.timeMs + prevStep;\n const nextEnd = nextLast.timeMs + nextStep;\n const endsTogether =\n Math.abs(nextEnd - prevEnd) <= MAX_END_DRIFT_STEPS * Math.max(prevStep, nextStep);\n return closeRatio <= MAX_SAME_ASSET_CLOSE_RATIO && endsTogether ? 'timeframe' : 'reset';\n}\n\n/**\n * The visible window to apply after a timeframe switch so each candle keeps\n * the exact pixel width it had before: the visible slot count is preserved and\n * the right edge re-anchors on the newest candle (any future-gap overshoot is\n * carried over in slots, clamped to the core's 3/4-window cap). The new start\n * may precede the first candle — that gap is intentional, width wins.\n */\nexport function timeframeWindow(\n oldWindow: VisibleRange,\n oldStepMs: number,\n oldLastMs: number,\n newStepMs: number,\n newLastMs: number,\n): VisibleRange {\n const slots = (oldWindow.endMs - oldWindow.startMs) / oldStepMs;\n const offsetRaw = (oldWindow.endMs - oldLastMs) / oldStepMs;\n const offsetSlots = Math.min(Math.max(offsetRaw, 0), slots * 0.75);\n const endMs = Math.round(newLastMs + offsetSlots * newStepMs);\n return { startMs: Math.round(endMs - slots * newStepMs), endMs };\n}\n","// Pointer/wheel gesture controller for the web chart. Maps input to the same\n// VroomChartHandle mutators the React Native gestures use (see\n// packages/react-native/src/VroomChart.tsx), so behavior matches across\n// platforms. Listeners are attached natively (not via React props) so wheel can\n// be non-passive and call preventDefault.\n\nimport { useEffect, useRef } from 'react';\nimport type { ChartMode, CrosshairEvent, Drawing, DrawTool } from '@vroomchart/types';\nimport type { VroomChartHandle } from '@vroomchart/core-wasm';\n\ntype Region = 'chart' | 'price-axis' | 'time-axis' | 'indicator' | 'separator' | 'indicator-axis';\n\nexport type GestureOptions = {\n crosshairOffset: number;\n /** Interaction mode. In 'draw' mode panning/zooming/crosshair are suppressed. */\n mode?: ChartMode;\n /** Active drawing tool while in 'draw' mode. */\n tool?: DrawTool;\n onCrosshair?: (e: CrosshairEvent) => void;\n /** External crosshair to mirror (data space), or null. See VroomChartCoreProps. */\n crosshairOverride?: { timeMs: number; price: number } | null;\n onViewportChange?: (startMs: number, endMs: number) => void;\n /** Fired with the finished line when the user places its second point. */\n onDrawingComplete?: (drawing: Drawing) => void;\n /** Fired when the chart wants the host to change mode (e.g. exit on click-away). */\n onRequestMode?: (mode: ChartMode) => void;\n};\n\nconst MIN_SPAN = 24; // px — minimum two-finger span for an axis to scale\nconst AXIS_RATIO = 0.5; // an axis scales only if its span ≥ this × the other's\nconst LONG_PRESS_MS = 350;\nconst MOVE_THRESH = 6; // px before a press becomes a drag\nconst WHEEL_K = 0.0015; // wheel delta → zoom factor exponent\nconst SEP_HIT = 4; // px band around the indicator separator for hit-testing\n\n// Drawing-tool styling: the guideline (and committed line) default to solid blue\n// at 2px, matching the core's default and useChartCore's drawing color.\nconst DRAW_COLOR = 0xff2962ff;\nconst DRAW_WIDTH = 2;\nconst DRAW_HIT = 6; // px tolerance for \"clicked on the selected line\" vs. away\n\n// Stable unique id for a freshly drawn line.\nfunction drawingId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `draw-${Math.random().toString(36).slice(2)}`;\n}\n\n// Distance in px from point (px,py) to the segment (ax,ay)-(bx,by).\nfunction distToSegment(\n px: number,\n py: number,\n ax: number,\n ay: number,\n bx: number,\n by: number,\n): number {\n const dx = bx - ax;\n const dy = by - ay;\n const len2 = dx * dx + dy * dy;\n let t = len2 > 0 ? ((px - ax) * dx + (py - ay) * dy) / len2 : 0;\n t = Math.max(0, Math.min(1, t));\n return Math.hypot(px - (ax + t * dx), py - (ay + t * dy));\n}\n\nexport function useGestures(\n containerRef: React.RefObject<HTMLElement | null>,\n handleRef: React.RefObject<VroomChartHandle | null>,\n scheduleRender: () => void,\n opts: GestureOptions,\n): void {\n // Keep latest opts in a ref so the effect's listeners stay stable.\n const optsRef = useRef(opts);\n optsRef.current = opts;\n\n // Drawing-tool state. Lives in refs (not effect-local) so the mode-change\n // effect below can reset it when the host leaves draw mode, and so it survives\n // the gesture effect's stable-listener lifecycle.\n // drawAnchor* — first point placed, awaiting the second (data + px coords).\n // drawSelectedPx — set once a line is committed: its px endpoints, used to\n // hit-test \"clicked on the line\" vs. \"clicked away to exit\".\n const drawAnchorRef = useRef<{ timeMs: number; price: number } | null>(null);\n const drawAnchorPxRef = useRef<{ x: number; y: number } | null>(null);\n const drawSelectedPxRef = useRef<{\n ax: number;\n ay: number;\n bx: number;\n by: number;\n } | null>(null);\n\n // True while a local pointer (hover/press) owns the crosshair. Lets the\n // crosshair-override effect below know to stand down (local input wins).\n const localCrosshairActiveRef = useRef(false);\n\n // Reset the in-progress draft whenever draw+line mode is not active (e.g. the\n // host toggled the tool off mid-draw), so re-entering draw mode starts clean.\n useEffect(() => {\n const active = opts.mode === 'draw' && opts.tool === 'line';\n if (active) return;\n drawAnchorRef.current = null;\n drawAnchorPxRef.current = null;\n drawSelectedPxRef.current = null;\n const h = handleRef.current;\n if (h) {\n h.clearDraft();\n scheduleRender();\n }\n }, [opts.mode, opts.tool, handleRef, scheduleRender]);\n\n // Apply the controlled crosshair override (cross-chart sync). A local\n // hover/press always wins, so we stand down while one is active; the override\n // is (re)applied on local release inside hideCrosshair. This path is silent —\n // it never calls onCrosshair — so two charts driving each other can't loop.\n // (If the override is set before the WASM handle finishes loading it applies\n // on its next change; in the sync use case the initial value is null.)\n useEffect(() => {\n const h = handleRef.current;\n if (!h || localCrosshairActiveRef.current) return;\n const ov = opts.crosshairOverride;\n if (ov) h.setCrosshairData(ov.timeMs, ov.price);\n else h.clearCrosshair();\n scheduleRender();\n }, [opts.crosshairOverride, handleRef, scheduleRender]);\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n\n const pointers = new Map<number, { x: number; y: number }>();\n let panMode: Region = 'chart';\n let crosshairActive = false;\n let crosshairSource: 'press' | 'hover' | null = null;\n let lastCrosshairTime: number | null = null;\n let lastCrosshairPrice: number | null = null;\n let longPressTimer: ReturnType<typeof setTimeout> | null = null;\n let downX = 0;\n let downY = 0;\n let moved = false;\n const pinch = { spanX: 1, spanY: 1, ratioX: 1, ratioY: 1, enableX: false, enableY: false, active: false };\n\n const rel = (e: PointerEvent | WheelEvent) => {\n const r = el.getBoundingClientRect();\n return { x: e.clientX - r.left, y: e.clientY - r.top };\n };\n\n const regionAt = (x: number, y: number): Region => {\n const h = handleRef.current;\n const r = el.getBoundingClientRect();\n if (!h) return 'chart';\n const { yAxisWidth, xAxisHeight, indicatorHeight } = h.getAxisMetrics();\n if (x > r.width - yAxisWidth) {\n // The y-axis strip beside an indicator pane scales that pane's y-axis;\n // the rest of the strip scales the main price axis.\n const priceBottom = r.height - xAxisHeight - indicatorHeight;\n if (indicatorHeight > 0 && y > priceBottom && y < r.height - xAxisHeight) {\n return 'indicator-axis';\n }\n return 'price-axis';\n }\n // Separator sits at the top edge of the indicator band, within the candle\n // area width — a narrow grab band for resizing the panes.\n const sepY = r.height - xAxisHeight - indicatorHeight;\n if (indicatorHeight > 0 && x <= r.width - yAxisWidth && Math.abs(y - sepY) <= SEP_HIT) {\n return 'separator';\n }\n if (y > r.height - xAxisHeight) return 'time-axis';\n if (indicatorHeight > 0 && y > r.height - xAxisHeight - indicatorHeight) return 'indicator';\n return 'chart';\n };\n\n const reportCrosshair = (reason: CrosshairEvent['reason']) => {\n const h = handleRef.current;\n if (!h) return;\n // Snapped slot — has a timeMs even in the empty space ahead of the last\n // candle, where `candle` is null.\n const info = h.getCrosshairInfo();\n const t = info?.timeMs ?? null;\n const p = info?.price ?? null;\n // Fire on any positional change — a different candle (time) OR a vertical\n // move within the same candle (price). Deduping on time alone would freeze\n // the reported price mid-candle, which breaks cross-chart price sync.\n if (reason === 'move' && t === lastCrosshairTime && p === lastCrosshairPrice) return;\n lastCrosshairTime = t;\n lastCrosshairPrice = p;\n optsRef.current.onCrosshair?.({\n active: reason !== 'hide',\n candle: info?.candle ?? null,\n timeMs: t,\n price: reason === 'hide' ? null : p,\n reason,\n });\n };\n\n const clearLongPress = () => {\n if (longPressTimer != null) {\n clearTimeout(longPressTimer);\n longPressTimer = null;\n }\n };\n\n const showCrosshair = (x: number, y: number, source: 'press' | 'hover', reason: 'show' | 'move') => {\n const h = handleRef.current;\n if (!h) return;\n crosshairActive = true;\n localCrosshairActiveRef.current = true;\n crosshairSource = source;\n const lift = source === 'press' ? optsRef.current.crosshairOffset : 0;\n h.setCrosshair(x, y - lift);\n scheduleRender();\n reportCrosshair(reason);\n };\n\n const hideCrosshair = () => {\n const h = handleRef.current;\n if (!h || !crosshairActive) return;\n crosshairActive = false;\n localCrosshairActiveRef.current = false;\n crosshairSource = null;\n // Fall back to the synced crosshair (if any) rather than clearing, so\n // releasing a local hover on a follower chart restores the driver's line.\n const ov = optsRef.current.crosshairOverride;\n if (ov) h.setCrosshairData(ov.timeMs, ov.price);\n else h.clearCrosshair();\n scheduleRender();\n reportCrosshair('hide');\n };\n\n // True while the line tool should own input (suppress pan/zoom/crosshair).\n const drawActive = () =>\n optsRef.current.mode === 'draw' && optsRef.current.tool === 'line';\n\n // While placing the second point, keep the guideline glued to the cursor.\n const updateGuideline = (x: number, y: number) => {\n const h = handleRef.current;\n if (!h) return;\n if (!drawAnchorRef.current || drawSelectedPxRef.current) return;\n const c = h.coordAt(x, y);\n if (!c) return;\n const a = drawAnchorRef.current;\n h.setDraft(a.timeMs, a.price, true, c.timeMs, c.price, true, DRAW_COLOR, DRAW_WIDTH);\n scheduleRender();\n };\n\n // A tap in the chart area: place the next point, or exit on click-away.\n const handleDrawClick = (x: number, y: number) => {\n const h = handleRef.current;\n if (!h) return;\n const o = optsRef.current;\n\n // A committed line is selected: clicking on it keeps it; clicking away\n // hides the nodes and asks the host to return to pan mode.\n const sel = drawSelectedPxRef.current;\n if (sel) {\n if (distToSegment(x, y, sel.ax, sel.ay, sel.bx, sel.by) <= DRAW_HIT) return;\n drawAnchorRef.current = null;\n drawAnchorPxRef.current = null;\n drawSelectedPxRef.current = null;\n h.clearDraft();\n scheduleRender();\n el.style.cursor = '';\n o.onRequestMode?.('pan');\n return;\n }\n\n const coord = h.coordAt(x, y);\n if (!coord) return;\n\n if (!drawAnchorRef.current) {\n // First point: show node A; the guideline follows on the next move.\n drawAnchorRef.current = coord;\n drawAnchorPxRef.current = { x, y };\n h.setDraft(coord.timeMs, coord.price, false, 0, 0, true, DRAW_COLOR, DRAW_WIDTH);\n scheduleRender();\n } else {\n // Second point: commit the line and keep both nodes shown (selected).\n const a = drawAnchorRef.current;\n const apx = drawAnchorPxRef.current!;\n o.onDrawingComplete?.({\n id: drawingId(),\n type: 'line',\n points: [\n { timeMs: a.timeMs, price: a.price },\n { timeMs: coord.timeMs, price: coord.price },\n ],\n });\n drawSelectedPxRef.current = { ax: apx.x, ay: apx.y, bx: x, by: y };\n h.setDraft(a.timeMs, a.price, true, coord.timeMs, coord.price, false, DRAW_COLOR, DRAW_WIDTH);\n scheduleRender();\n }\n };\n\n const onPointerDown = (e: PointerEvent) => {\n const h = handleRef.current;\n if (!h) return;\n if (e.pointerType === 'mouse' && e.button !== 0) return; // left button only\n const { x, y } = rel(e);\n el.setPointerCapture(e.pointerId);\n pointers.set(e.pointerId, { x, y });\n\n if (pointers.size === 2) {\n clearLongPress();\n hideCrosshair();\n const pts = [...pointers.values()];\n const sx = Math.abs(pts[0]!.x - pts[1]!.x);\n const sy = Math.abs(pts[0]!.y - pts[1]!.y);\n pinch.spanX = sx;\n pinch.spanY = sy;\n pinch.ratioX = 1;\n pinch.ratioY = 1;\n pinch.enableX = sx >= MIN_SPAN && sx >= sy * AXIS_RATIO;\n pinch.enableY = sy >= MIN_SPAN && sy >= sx * AXIS_RATIO;\n pinch.active = true;\n return;\n }\n\n // Single pointer: classify region; arm long-press → crosshair (touch/pen).\n downX = x;\n downY = y;\n moved = false;\n panMode = regionAt(x, y);\n if (e.pointerType !== 'mouse' && panMode === 'chart' && !drawActive()) {\n longPressTimer = setTimeout(() => {\n longPressTimer = null;\n if (!moved) showCrosshair(downX, downY, 'press', 'show');\n }, LONG_PRESS_MS);\n }\n };\n\n const onPointerMove = (e: PointerEvent) => {\n const h = handleRef.current;\n if (!h) return;\n const { x, y } = rel(e);\n\n // Hover (mouse, no button) → crosshair follows the cursor on the chart.\n if (!pointers.has(e.pointerId)) {\n if (e.pointerType === 'mouse' && pointers.size === 0) {\n // Draw mode: no crosshair; the guideline tracks the cursor instead.\n if (drawActive()) {\n el.style.cursor = 'crosshair';\n updateGuideline(x, y);\n return;\n }\n const region = regionAt(x, y);\n el.style.cursor =\n region === 'separator'\n ? 'row-resize'\n : region === 'indicator-axis'\n ? 'ns-resize'\n : '';\n if (region === 'chart') {\n showCrosshair(x, y, 'hover', crosshairActive ? 'move' : 'show');\n } else {\n hideCrosshair();\n }\n }\n return;\n }\n\n const prev = pointers.get(e.pointerId)!;\n const dx = x - prev.x;\n const dy = y - prev.y;\n pointers.set(e.pointerId, { x, y });\n\n // Two-finger directional pinch (disabled while drawing).\n if (pointers.size >= 2 && pinch.active && !drawActive()) {\n const pts = [...pointers.values()];\n const focalX = (pts[0]!.x + pts[1]!.x) / 2;\n const focalY = (pts[0]!.y + pts[1]!.y) / 2;\n let frameX = 1;\n if (pinch.enableX) {\n const r = Math.max(Math.abs(pts[0]!.x - pts[1]!.x), MIN_SPAN) / pinch.spanX;\n frameX = r / pinch.ratioX;\n pinch.ratioX = r;\n }\n let frameY = 1;\n if (pinch.enableY) {\n const r = Math.max(Math.abs(pts[0]!.y - pts[1]!.y), MIN_SPAN) / pinch.spanY;\n frameY = r / pinch.ratioY;\n pinch.ratioY = r;\n }\n if (frameX !== 1 || frameY !== 1) {\n h.zoom(frameX, frameY, focalX, focalY);\n scheduleRender();\n }\n return;\n }\n\n if (!moved && Math.hypot(x - downX, y - downY) > MOVE_THRESH) {\n moved = true;\n clearLongPress();\n }\n if (!moved) return;\n\n // Draw mode: a press-drag neither pans nor moves a crosshair; if a point\n // is down, keep the guideline tracking the cursor so a drag still previews.\n if (drawActive()) {\n if (panMode === 'chart') updateGuideline(x, y);\n return;\n }\n\n // Chart-area drag with the (press) crosshair up moves the crosshair.\n if (crosshairActive && crosshairSource === 'press' && panMode === 'chart') {\n showCrosshair(x, y, 'press', 'move');\n return;\n }\n\n if (panMode === 'price-axis') h.scalePriceAxis(dy);\n else if (panMode === 'time-axis') h.scaleTimeAxis(dx);\n else if (panMode === 'separator') h.resizeIndicatorPane(dy);\n else if (panMode === 'indicator-axis') h.scaleIndicatorAxis(downY, dy);\n else if (panMode === 'indicator') h.pan(dx, 0);\n else h.translate(dx, dy);\n\n // Keep a hover crosshair glued to the cursor while dragging — the chart\n // pans under it, so re-snap to whatever candle is now beneath the cursor.\n if (crosshairActive && crosshairSource === 'hover') {\n h.setCrosshair(x, y);\n reportCrosshair('move');\n }\n scheduleRender();\n };\n\n const endPointer = (e: PointerEvent) => {\n const had = pointers.delete(e.pointerId);\n clearLongPress();\n if (pointers.size < 2) pinch.active = false;\n if (!had) return;\n\n // Draw mode: a stationary tap in the chart places a point (or exits on\n // click-away). Drags are ignored (no pan). Crosshair logic is skipped.\n if (drawActive() && pointers.size === 0) {\n if (!moved && panMode === 'chart') handleDrawClick(downX, downY);\n else el.style.cursor = 'crosshair';\n return;\n }\n\n // Press crosshair is active only while held — release dismisses it.\n if (crosshairSource === 'press' && pointers.size === 0) {\n hideCrosshair();\n } else if (moved && (panMode === 'chart' || panMode === 'indicator')) {\n optsRef.current.onViewportChange?.(0, 0);\n }\n if (pointers.size === 0) el.style.cursor = '';\n };\n\n const onPointerLeave = () => {\n if (crosshairSource === 'hover') hideCrosshair();\n el.style.cursor = '';\n };\n\n const onWheel = (e: WheelEvent) => {\n const h = handleRef.current;\n if (!h) return;\n e.preventDefault();\n // Draw mode: no pan/zoom from the wheel (keeps placed points stable).\n if (drawActive()) return;\n const { x, y } = rel(e);\n if (e.ctrlKey || e.metaKey) {\n // Trackpad pinch (sent as ctrl+wheel) / ctrl+wheel → zoom both axes.\n const f = Math.exp(-e.deltaY * WHEEL_K);\n h.zoom(f, f, x, y);\n } else if (e.shiftKey) {\n // Shift+wheel → horizontal pan (mouse-wheel-only users).\n h.pan(-(e.deltaX || e.deltaY), 0);\n } else if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) {\n // Horizontal scroll → pan the time axis (scroll right = forward in time).\n h.pan(-e.deltaX, 0);\n } else {\n // Vertical scroll → zoom the time window around the cursor.\n const f = Math.exp(-e.deltaY * WHEEL_K);\n h.zoom(f, 1, x, y);\n }\n scheduleRender();\n };\n\n el.addEventListener('pointerdown', onPointerDown);\n el.addEventListener('pointermove', onPointerMove);\n el.addEventListener('pointerup', endPointer);\n el.addEventListener('pointercancel', endPointer);\n el.addEventListener('pointerleave', onPointerLeave);\n el.addEventListener('wheel', onWheel, { passive: false });\n\n return () => {\n clearLongPress();\n el.removeEventListener('pointerdown', onPointerDown);\n el.removeEventListener('pointermove', onPointerMove);\n el.removeEventListener('pointerup', endPointer);\n el.removeEventListener('pointercancel', endPointer);\n el.removeEventListener('pointerleave', onPointerLeave);\n el.removeEventListener('wheel', onWheel);\n };\n }, [containerRef, handleRef, scheduleRender]);\n}\n","// VroomChart (web) — DOM React component that renders the candlestick chart to\n// a <canvas> via @vroomchart/core-wasm and wires pointer/wheel gestures. API-matched\n// to the React Native component (same props from @vroomchart/types).\n\nimport React from 'react';\nimport type { CSSProperties } from 'react';\n\nimport { useChartCore } from './useChartCore';\nimport { useGestures } from './useGestures';\nimport type { VroomChartProps } from './types';\n\nconst FILL: CSSProperties = { position: 'relative', width: '100%', height: '100%' };\nconst CANVAS_STYLE: CSSProperties = {\n display: 'block',\n width: '100%',\n height: '100%',\n touchAction: 'none', // we own pan/zoom; don't let the browser scroll/zoom\n};\n\n/**\n * Skia-quality candlestick chart for the web. Pass OHLCV `candles` and size it\n * via `style`/`width`/`height` (it fills its parent by default). Drag to pan,\n * pinch or wheel to zoom, drag the price/time axes to rescale, hover (mouse) or\n * long-press (touch) for the crosshair. Optional indicators (`rsi`, `macd`,\n * `movingAverages`, `vwap`), colors (`theme`), and events (`onCrosshair`,\n * `onViewportChange`) are configured through props.\n *\n * @see {@link VroomChartProps} for the full prop reference.\n */\nexport function VroomChart(props: VroomChartProps) {\n const {\n className,\n style,\n wasm,\n crosshairOffset = 40,\n crosshairOverride,\n mode,\n tool,\n onCrosshair,\n onViewportChange,\n onDrawingComplete,\n onModeChange,\n } = props;\n const { containerRef, canvasRef, handleRef, scheduleRender } = useChartCore(\n props,\n wasm ? { wasm } : undefined,\n );\n\n useGestures(containerRef, handleRef, scheduleRender, {\n crosshairOffset,\n crosshairOverride,\n mode,\n tool,\n onCrosshair,\n onViewportChange,\n onDrawingComplete,\n onRequestMode: onModeChange,\n });\n\n const rootStyle: CSSProperties = {\n ...FILL,\n ...(props.width != null ? { width: props.width } : null),\n ...(props.height != null ? { height: props.height } : null),\n ...style,\n };\n\n return (\n <div ref={containerRef} className={className} style={rootStyle}>\n <canvas ref={canvasRef} style={CANVAS_STYLE} />\n </div>\n );\n}\n"],"mappings":"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","names":["useEffect","useRef"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vroomchart/react",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "Skia-quality candlestick chart for the web (React DOM).",
5
5
  "license": "MIT",
6
6
  "author": "Darion Welch",
@@ -40,7 +40,7 @@
40
40
  "access": "public"
41
41
  },
42
42
  "dependencies": {
43
- "@vroomchart/core-wasm": "0.1.5"
43
+ "@vroomchart/core-wasm": "0.2.0"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "react": ">=18",