@vroomchart/react 0.2.0 → 0.5.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 +125 -13
- package/dist/index.js +765 -49
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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 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"]}
|
|
1
|
+
{"version":3,"sources":["../src/useChartCore.ts","../src/dataTransitions.ts","../src/useGestures.ts","../src/simplify.ts","../src/useManagedDrawings.ts","../src/drawingStorage.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 // a/b are the first and last anchor — the two endpoints of a line, the two\n // opposite corners of a box, or the ends of a pencil path (whose full point\n // list rides along in `points`).\n const first = d.points[0];\n const last = d.points[d.points.length - 1];\n return {\n aTime: first.timeMs,\n aPrice: first.price,\n bTime: last.timeMs,\n bPrice: last.price,\n color: (d.color != null ? parseColor(d.color) : null) ?? 0xff2962ff,\n width: d.width ?? 2,\n kind: d.type === 'box' ? 1 : d.type === 'pencil' ? 2 : 0,\n ...(d.type === 'pencil'\n ? { points: d.points.map((p) => ({ timeMs: p.timeMs, price: p.price })) }\n : {}),\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 chartType,\n transitionMs,\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 // Animate the candle↔line transition when `chartType` changes. The core is\n // driven per-frame with a (collapse, fade) blend; we own the eased clock here\n // in JS (see plan) so it works identically on the web direct-draw path. A new\n // handle applies the target instantly; reduced motion / transitionMs=0 snaps\n // (reduced motion still cross-fades opacity but skips the vertical collapse).\n const morphRafRef = useRef<number | null>(null);\n const morphFadeRef = useRef<number | null>(null);\n const morphHandleRef = useRef<VroomChartHandle | null>(null);\n useEffect(() => {\n if (!ready) return;\n const h = handleRef.current;\n if (!h) return;\n const target = chartType === 'line' ? 1 : 0;\n\n // Fresh handle (first load or remount): snap, don't animate.\n if (morphHandleRef.current !== h || morphFadeRef.current == null) {\n morphHandleRef.current = h;\n morphFadeRef.current = target;\n h.setChartType(target);\n scheduleRender();\n return;\n }\n if (morphFadeRef.current === target) return;\n\n const reduce = !!(\n typeof window !== 'undefined' &&\n window.matchMedia &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches\n );\n const dur = Math.max(0, transitionMs ?? 300);\n\n if (morphRafRef.current != null) {\n cancelAnimationFrame(morphRafRef.current);\n morphRafRef.current = null;\n }\n if (dur === 0) {\n morphFadeRef.current = target;\n h.setChartType(target);\n scheduleRender();\n return;\n }\n\n const from = morphFadeRef.current;\n const start = performance.now();\n const step = (now: number) => {\n const p = Math.min(1, (now - start) / dur);\n const e = p * p * (3 - 2 * p); // smoothstep ease-in-out\n const fade = from + (target - from) * e;\n morphFadeRef.current = fade;\n h.setMorph(reduce ? 0 : fade, fade);\n h.present();\n if (p < 1) {\n morphRafRef.current = requestAnimationFrame(step);\n } else {\n morphRafRef.current = null;\n morphFadeRef.current = target;\n h.setChartType(target); // lock the exact endpoint\n h.present();\n }\n };\n morphRafRef.current = requestAnimationFrame(step);\n\n return () => {\n if (morphRafRef.current != null) {\n cancelAnimationFrame(morphRafRef.current);\n morphRafRef.current = null;\n }\n };\n }, [ready, chartType, transitionMs, 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 { useCallback, useEffect, useRef } from 'react';\nimport type { ChartMode, CrosshairEvent, Drawing, DrawPoint, DrawTool } from '@vroomchart/types';\nimport type { VroomChartHandle } from '@vroomchart/core-wasm';\n\nimport { simplifyIndices } from './simplify';\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 /** Committed drawings (controlled). Used to hit-test/select/drag/delete. */\n drawings?: Drawing[];\n /** Fired with the finished line when the user places its second point. */\n onDrawingComplete?: (drawing: Drawing) => void;\n /** Fired after a selected line's endpoint is dragged (same id, new points). */\n onDrawingChange?: (drawing: Drawing) => void;\n /** Fired when the selected line is deleted (Backspace/Delete). */\n onDrawingDelete?: (id: string) => 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;\n\n// Freehand capture tuning. PENCIL_MIN_DIST drops samples the pointer barely\n// moved between (browsers fire moves far faster than the stroke changes);\n// PENCIL_EPSILON is the RDP tolerance applied on commit, in px — roughly \"no\n// point may pull the rendered stroke more than a pixel off\".\nconst PENCIL_MIN_DIST = 2;\nconst PENCIL_EPSILON = 1;\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// The core's hit-test `part` that means \"the body of this shape\" (as opposed to\n// a grab handle). See packages/core/src/drawings.h for the full part encoding.\nfunction bodyPart(type: Drawing['type']): number {\n return type === 'box' ? 4 : type === 'pencil' ? 5 : 2;\n}\n\n// Rebuilds a drawing of `type` from a point array. `Drawing` is a discriminated\n// union where line/box carry an exact two-point tuple and only pencil is\n// variable-length, so this is where an array is narrowed back to the right\n// variant. Returns null for a degenerate (< 2 point) shape.\nfunction makeDrawing(\n base: { id: string; color?: Drawing['color']; width?: number },\n type: Drawing['type'],\n pts: DrawPoint[],\n): Drawing | null {\n if (pts.length < 2) return null;\n const style = {\n ...(base.color != null ? { color: base.color } : {}),\n ...(base.width != null ? { width: base.width } : {}),\n };\n if (type === 'pencil') return { id: base.id, type, points: pts, ...style };\n return { id: base.id, type, points: [pts[0]!, pts[pts.length - 1]!], ...style };\n}\n\n// The four corners of a box (given its two opposite-corner `points`), in the\n// same order the core's hit-test reports: a, (b.time,a.price), b,\n// (a.time,b.price). Corner k's diagonal is corner (k+2)%4.\nfunction boxCorners(\n points: readonly [DrawPoint, DrawPoint],\n): [DrawPoint, DrawPoint, DrawPoint, DrawPoint] {\n const [a, b] = points;\n return [\n { timeMs: a.timeMs, price: a.price },\n { timeMs: b.timeMs, price: a.price },\n { timeMs: b.timeMs, price: b.price },\n { timeMs: a.timeMs, price: b.price },\n ];\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 (live in refs so the mode-change effect can reset it and\n // it survives the gesture effect's stable-listener lifecycle).\n // drawAnchor — first point placed, awaiting the second (data coords).\n const drawAnchorRef = useRef<{ timeMs: number; price: number } | null>(null);\n\n // Editing state for committed drawings (pan-mode select / drag / delete).\n // selectedId — the selected Drawing.id (data-space identity; survives\n // pan/zoom + list changes). The core is told the matching index.\n // grab — the endpoint being dragged { index, endpoint: 0|1 }, or null.\n // dragLine — the whole selected line being translated by its body, or null.\n // downHit — the hit under the last pointerdown, resolved to select/deselect\n // on a stationary release.\n // lastCoord — the last coordAt during a handle drag, for the release payload.\n const selectedIdRef = useRef<string | null>(null);\n // The endpoint/corner being dragged. `fixed` is the anchor that stays put (the\n // other line endpoint, or — for a box — the diagonally opposite corner). For a\n // box we normalize the core so `endpoint` is always 0 (the grabbed corner) and\n // `fixed` is stored as endpoint 1, so the line's endpoint-drag path drives it.\n const grabRef = useRef<{\n index: number;\n endpoint: number;\n fixed: DrawPoint;\n isBox: boolean;\n } | null>(null);\n // A whole shape being translated by its body. Line/box restate both anchors\n // absolutely from a0/b0; a pencil can't (hundreds of points), so it carries\n // `points0` and is driven by relative translateDrawing calls instead.\n const dragLineRef = useRef<{\n index: number;\n startTime: number;\n startPrice: number;\n a0: DrawPoint;\n b0: DrawPoint;\n lastA: DrawPoint;\n lastB: DrawPoint;\n points0?: DrawPoint[];\n /** Total delta applied to the core so far, for the incremental translate. */\n appliedTime?: number;\n appliedPrice?: number;\n } | null>(null);\n // An in-progress freehand stroke: the captured data-space points plus their\n // pixel positions, which drive the min-distance sampling and the RDP pass.\n const pencilRef = useRef<{ pts: DrawPoint[]; px: { x: number; y: number }[] } | null>(\n null,\n );\n const downHitRef = useRef<{ index: number; part: number; t: number } | null>(null);\n const lastCoordRef = useRef<{ timeMs: number; price: number } | null>(null);\n // Where along the selected line it was grabbed (0..1, A→B), and the in-memory\n // clipboard for copy/paste (color/width + the grab t + the crosshair at copy).\n const selectedTRef = useRef(0.5);\n const clipboardRef = useRef<{\n type: Drawing['type'];\n points: DrawPoint[];\n color?: Drawing['color'];\n width?: number;\n t: number;\n crosshair: { timeMs: number; price: number } | null;\n } | null>(null);\n\n // Push the current selection (id → core index) and grabbed-handle state into\n // the core. Reads live refs/opts so a captured instance is never stale. Runs\n // after useChartCore's setDrawings (declared earlier in VroomChart) so the\n // index is valid whenever the drawings list changes.\n const applySelection = useCallback(() => {\n const h = handleRef.current;\n if (!h) return;\n const id = selectedIdRef.current;\n const list = optsRef.current.drawings ?? [];\n let index = -1;\n if (id != null) {\n index = list.findIndex((d) => d.id === id);\n if (index < 0) selectedIdRef.current = null;\n }\n h.setSelectedDrawing(index, index >= 0 ? (grabRef.current?.endpoint ?? -1) : -1);\n scheduleRender();\n }, [handleRef, scheduleRender]);\n\n // Re-assert selection whenever the drawings list changes (add / delete / the\n // just-dragged update), so the core index tracks the selected id.\n useEffect(() => {\n applySelection();\n }, [opts.drawings, applySelection]);\n\n // Keyboard: delete the selected line (Backspace/Delete) and copy/paste it\n // (Cmd/Ctrl+C / +V). Skipped while focus is in a field / a real text selection.\n useEffect(() => {\n const isEditableTarget = () => {\n const ae = document.activeElement as HTMLElement | null;\n const tag = ae?.tagName;\n return tag === 'INPUT' || tag === 'TEXTAREA' || !!ae?.isContentEditable;\n };\n const findSelected = (): Drawing | null => {\n const id = selectedIdRef.current;\n if (id == null) return null;\n return (optsRef.current.drawings ?? []).find((x) => x.id === id) ?? null;\n };\n\n // Points for a pasted copy: under the crosshair (intersecting at the same\n // grab t), else offset above/below the original, kept within the viewport.\n // Works for any point count — a line/box's two anchors or a whole pencil path.\n const placePaste = (clip: NonNullable<typeof clipboardRef.current>): DrawPoint[] | null => {\n const h = handleRef.current;\n if (!h) return null;\n const pts = clip.points;\n if (pts.length < 2) return null;\n const a = pts[0]!;\n const b = pts[pts.length - 1]!;\n const t = clip.t;\n // Reference point that lands under the cursor: where the shape was grabbed\n // along first→last (t is 0 for a pencil, i.e. its starting point).\n const pSel = {\n timeMs: a.timeMs + t * (b.timeMs - a.timeMs),\n price: a.price + t * (b.price - a.price),\n };\n const cur = h.getCrosshairInfo();\n const moved =\n cur != null &&\n (clip.crosshair == null ||\n cur.timeMs !== clip.crosshair.timeMs ||\n cur.price !== clip.crosshair.price);\n if (moved && cur) {\n const dt = cur.timeMs - pSel.timeMs;\n const dp = cur.price - pSel.price;\n return pts.map((q) => ({ timeMs: q.timeMs + dt, price: q.price + dp }));\n }\n // Above/below the original. Derive the visible price range + px scale.\n const el = containerRef.current;\n const shift = (dp: number): DrawPoint[] =>\n pts.map((q) => ({ timeMs: q.timeMs, price: q.price + dp }));\n // Full price extent of the shape, so the copy clears the original.\n const prices = pts.map((q) => q.price);\n const span = Math.max(...prices) - Math.min(...prices);\n if (!el) return shift(-(span || 1));\n const rect = el.getBoundingClientRect();\n const m = h.getAxisMetrics();\n const priceBottom = rect.height - m.xAxisHeight - m.indicatorHeight;\n const top = h.coordAt(rect.width / 2, 0);\n const bot = h.coordAt(rect.width / 2, priceBottom);\n if (!top || !bot || priceBottom <= 0 || top.price <= bot.price) return shift(-(span || 1));\n const vTop = top.price; // higher price = screen top\n const vBot = bot.price; // lower price = screen bottom\n const pricePerPx = (vTop - vBot) / priceBottom;\n const GAP_PX = 30;\n const offset = (span / pricePerPx + GAP_PX) * pricePerPx; // clear the original\n const pMin = Math.min(...prices);\n const pMax = Math.max(...prices);\n const upFits = pMax + offset <= vTop;\n const downFits = pMin - offset >= vBot;\n let dir: 1 | -1;\n if (upFits && !downFits) dir = 1;\n else if (downFits && !upFits) dir = -1;\n else if (upFits && downFits) {\n dir = vTop - (pMax + offset) >= pMin - offset - vBot ? 1 : -1; // more edge margin\n } else {\n const upVis = Math.min(pMax + offset, vTop) - Math.max(pMin + offset, vBot);\n const dnVis = Math.min(pMax - offset, vTop) - Math.max(pMin - offset, vBot);\n dir = upVis >= dnVis ? 1 : -1; // larger visible portion\n }\n return shift(dir * offset);\n };\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (isEditableTarget()) return;\n\n // Mid-draw (first anchor placed, awaiting the second): Escape/Delete/\n // Backspace cancels the in-progress anchor but keeps draw mode active.\n if (\n drawAnchorRef.current &&\n (e.key === 'Escape' || e.key === 'Backspace' || e.key === 'Delete')\n ) {\n e.preventDefault();\n drawAnchorRef.current = null;\n const h = handleRef.current;\n if (h) {\n h.clearDraft();\n scheduleRender();\n }\n return;\n }\n\n if (e.key === 'Backspace' || e.key === 'Delete') {\n const id = selectedIdRef.current;\n if (id == null) return;\n e.preventDefault();\n optsRef.current.onDrawingDelete?.(id);\n selectedIdRef.current = null;\n grabRef.current = null;\n dragLineRef.current = null;\n const h = handleRef.current;\n if (h) {\n h.setSelectedDrawing(-1, -1);\n scheduleRender();\n }\n return;\n }\n\n if (!(e.metaKey || e.ctrlKey)) return;\n const key = e.key.toLowerCase();\n if (key === 'c') {\n if (window.getSelection()?.toString()) return; // don't hijack a real text copy\n const d = findSelected();\n if (!d) return;\n e.preventDefault();\n const info = handleRef.current?.getCrosshairInfo() ?? null;\n clipboardRef.current = {\n type: d.type,\n points: d.points.map((q) => ({ timeMs: q.timeMs, price: q.price })),\n color: d.color,\n width: d.width,\n t: selectedTRef.current,\n crosshair: info ? { timeMs: info.timeMs, price: info.price } : null,\n };\n } else if (key === 'v') {\n const clip = clipboardRef.current;\n if (!clip) return;\n const pts = placePaste(clip);\n if (!pts) return;\n const id = drawingId();\n const copy = makeDrawing(\n { id, color: clip.color, width: clip.width },\n clip.type,\n pts,\n );\n if (!copy) return;\n e.preventDefault();\n optsRef.current.onDrawingComplete?.(copy);\n // The drawings-change effect selects the new id once it's appended.\n selectedIdRef.current = id;\n selectedTRef.current = clip.t;\n }\n };\n window.addEventListener('keydown', onKeyDown);\n return () => window.removeEventListener('keydown', onKeyDown);\n }, [containerRef, handleRef, scheduleRender]);\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 no draw tool is active (e.g. the host\n // toggled the tool off, or switched tools, mid-draw), so re-entering draw mode\n // starts clean.\n useEffect(() => {\n const active =\n opts.mode === 'draw' &&\n (opts.tool === 'line' || opts.tool === 'box' || opts.tool === 'pencil');\n if (active) return;\n drawAnchorRef.current = null;\n pencilRef.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 // Shift-constrain state: whether Shift is held, and the last cursor position\n // (so a Shift press/release with the mouse still can re-snap live).\n let shiftHeld = false;\n let lastX = 0;\n let lastY = 0;\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 // Data coord to use for a moving point at pixel (x,y) relative to `fixed`.\n // With Shift held, snaps to the nearest 45° *screen* angle from `fixed`.\n const snapToLine = (\n fixed: DrawPoint,\n x: number,\n y: number,\n ): { timeMs: number; price: number } | null => {\n const h = handleRef.current;\n if (!h) return null;\n const free = h.coordAt(x, y);\n if (!shiftHeld || !free) return free;\n const f = h.project(fixed.timeMs, fixed.price);\n if (!f) return free;\n const dx = x - f.x;\n const dy = y - f.y;\n if (dx === 0 && dy === 0) return free;\n const step = Math.PI / 4;\n const ang = Math.round(Math.atan2(dy, dx) / step) * step; // nearest 45°\n const ux = Math.cos(ang);\n const uy = Math.sin(ang);\n const len = dx * ux + dy * uy; // project the cursor onto the snapped ray\n return h.coordAt(f.x + len * ux, f.y + len * uy) ?? free;\n };\n\n // Data coord for a moving box corner at pixel (x,y), opposite the `fixed`\n // corner. With Shift held, constrains the box to a perfect square (equal side\n // lengths in px), enclosing the cursor — the box analogue of snapToLine's 45°.\n const snapToSquare = (\n fixed: DrawPoint,\n x: number,\n y: number,\n ): { timeMs: number; price: number } | null => {\n const h = handleRef.current;\n if (!h) return null;\n const free = h.coordAt(x, y);\n if (!shiftHeld || !free) return free;\n const f = h.project(fixed.timeMs, fixed.price);\n if (!f) return free;\n const dx = x - f.x;\n const dy = y - f.y;\n const side = Math.max(Math.abs(dx), Math.abs(dy)); // equal side = larger delta\n if (side === 0) return free;\n const nx = f.x + (dx < 0 ? -side : side);\n const ny = f.y + (dy < 0 ? -side : side);\n return h.coordAt(nx, ny) ?? free;\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 a drawing tool should own input (suppress pan/zoom/crosshair).\n const drawActive = () =>\n optsRef.current.mode === 'draw' &&\n (optsRef.current.tool === 'line' ||\n optsRef.current.tool === 'box' ||\n optsRef.current.tool === 'pencil');\n\n // Whether the active draw tool is the box (vs the line). `1`/`0` maps to the\n // core's draft/drawing `kind`.\n const drawIsBox = () => optsRef.current.tool === 'box';\n\n // The pencil is drag-driven rather than click-click, so it takes its own\n // path through pointerdown/move/up instead of the two-click draft flow.\n const drawIsPencil = () => optsRef.current.tool === 'pencil';\n\n // Snap the moving anchor relative to `fixed`, using the constraint that fits\n // the shape: square for a box, 45° for a line.\n const snapMoving = (isBox: boolean, fixed: DrawPoint, x: number, y: number) =>\n isBox ? snapToSquare(fixed, x, y) : snapToLine(fixed, x, y);\n\n // While placing the second point, keep the live preview glued to the cursor.\n const updateGuideline = (x: number, y: number) => {\n const h = handleRef.current;\n if (!h) return;\n const a = drawAnchorRef.current;\n if (!a) return;\n const isBox = drawIsBox();\n const c = snapMoving(isBox, a, x, y);\n if (!c) return;\n h.setDraft(a.timeMs, a.price, true, c.timeMs, c.price, true, DRAW_COLOR, DRAW_WIDTH, isBox ? 1 : 0);\n scheduleRender();\n };\n\n // Draw-tool tap: place the first point, then commit on the second. Works for\n // both the line (two endpoints) and the box (two opposite corners).\n const handleDrawClick = (x: number, y: number) => {\n const h = handleRef.current;\n if (!h) return;\n\n const isBox = drawIsBox();\n if (!drawAnchorRef.current) {\n // First point: show node A; the preview follows on the next move.\n const coord = h.coordAt(x, y);\n if (!coord) return;\n drawAnchorRef.current = coord;\n h.setDraft(coord.timeMs, coord.price, false, 0, 0, true, DRAW_COLOR, DRAW_WIDTH, isBox ? 1 : 0);\n scheduleRender();\n } else {\n // Second point: commit the shape (Shift-snapped to match the preview)\n // and clear the draft. Editing happens in pan mode via the committed shape.\n const a = drawAnchorRef.current;\n const coord = snapMoving(isBox, a, x, y);\n if (!coord) return;\n optsRef.current.onDrawingComplete?.({\n id: drawingId(),\n type: isBox ? 'box' : 'line',\n points: [\n { timeMs: a.timeMs, price: a.price },\n { timeMs: coord.timeMs, price: coord.price },\n ],\n });\n drawAnchorRef.current = null;\n h.clearDraft();\n scheduleRender();\n }\n };\n\n // --- Pencil (freehand) capture ---------------------------------------\n // The stroke is captured in two parallel arrays: data-space points (what\n // gets committed, so the stroke tracks the candles) and their pixel\n // positions (what drives min-distance sampling and the RDP pass, so both\n // tolerances mean \"on screen\" regardless of zoom or price scale).\n\n const pencilStart = (x: number, y: number) => {\n const h = handleRef.current;\n if (!h) return;\n const c = h.coordAt(x, y);\n if (!c) return;\n pencilRef.current = { pts: [c], px: [{ x, y }] };\n h.startDraftStroke(DRAW_COLOR, DRAW_WIDTH);\n h.appendDraftPoint(c.timeMs, c.price);\n scheduleRender();\n };\n\n const pencilMove = (x: number, y: number) => {\n const h = handleRef.current;\n const s = pencilRef.current;\n if (!h || !s) return;\n // Skip samples the pointer barely moved between — pointermove fires far\n // faster than the stroke actually changes shape.\n const last = s.px[s.px.length - 1]!;\n if (Math.hypot(x - last.x, y - last.y) < PENCIL_MIN_DIST) return;\n const c = h.coordAt(x, y);\n if (!c) return;\n s.pts.push(c);\n s.px.push({ x, y });\n h.appendDraftPoint(c.timeMs, c.price);\n scheduleRender();\n };\n\n // Price precision at which the rounding error stays well under a pixel, so\n // the persisted stroke renders identically to the drawn one while shedding\n // float noise (54812.34567890123 → 54812.345679) from the stored payload.\n const priceDecimals = (): number => {\n const h = handleRef.current;\n const el = containerRef.current;\n if (!h || !el) return 6;\n const rect = el.getBoundingClientRect();\n const m = h.getAxisMetrics();\n const priceBottom = rect.height - m.xAxisHeight - m.indicatorHeight;\n if (priceBottom <= 0) return 6;\n const top = h.coordAt(rect.width / 2, 0);\n const bot = h.coordAt(rect.width / 2, priceBottom);\n if (!top || !bot || top.price <= bot.price) return 6;\n const pricePerPx = (top.price - bot.price) / priceBottom;\n const d = Math.ceil(-Math.log10(pricePerPx / 100));\n return Math.max(0, Math.min(12, Number.isFinite(d) ? d : 6));\n };\n\n // Quantize a stroke's points so the persisted payload stays compact. Applied\n // wherever a stroke is emitted (commit and translate) so stored coordinates\n // never re-accumulate float noise.\n const roundPoints = (pts: DrawPoint[]): DrawPoint[] => {\n const dp = priceDecimals();\n return pts.map((p) => ({\n timeMs: Math.round(p.timeMs),\n price: Number(p.price.toFixed(dp)),\n }));\n };\n\n // Commit the stroke: thin it with RDP, round the coordinates, and hand it to\n // the host. Sub-threshold strokes (a stray click) are dropped, not committed\n // as a speck.\n const pencilEnd = () => {\n const h = handleRef.current;\n const s = pencilRef.current;\n pencilRef.current = null;\n if (!h) return;\n h.clearDraft();\n scheduleRender();\n if (!s || s.pts.length < 2) return;\n\n const keep = simplifyIndices(s.px, PENCIL_EPSILON);\n if (keep.length < 2) return;\n const stroke = makeDrawing(\n { id: drawingId() },\n 'pencil',\n roundPoints(keep.map((i) => s.pts[i]!)),\n );\n if (stroke) optsRef.current.onDrawingComplete?.(stroke);\n };\n\n // Reposition the grabbed endpoint at pixel (x,y), Shift-snapping to 45°\n // relative to the fixed (other) endpoint.\n const moveGrab = (x: number, y: number) => {\n const h = handleRef.current;\n const g = grabRef.current;\n if (!h || !g) return;\n const c = snapMoving(g.isBox, g.fixed, x, y);\n if (!c) return;\n lastCoordRef.current = c;\n h.moveDrawingEndpoint(g.index, g.endpoint, c.timeMs, c.price);\n scheduleRender();\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 shiftHeld = e.shiftKey;\n lastX = x;\n lastY = y;\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\n // Pencil: the press itself starts the stroke and owns the pointer for its\n // whole duration — no long-press crosshair, no pan.\n if (drawIsPencil() && drawActive() && panMode === 'chart') {\n pencilStart(x, y);\n return;\n }\n\n // Editing (pan mode, chart area): grab a handle to drag it now; otherwise\n // record the hit so a stationary release can select a body / deselect.\n downHitRef.current = null;\n if (!drawActive() && panMode === 'chart') {\n const hit = h.hitTestDrawing(x, y);\n const gd = hit ? (optsRef.current.drawings ?? [])[hit.index] : undefined;\n // Handle/corner grab: line endpoints (part 0/1) or box corners (part\n // 0..3). A pencil has no handles at all — its end anchors translate the\n // stroke like any other part of it — so it never takes this path.\n if (hit && gd && gd.type === 'box' && hit.part <= 3) {\n // A box corner: the diagonally opposite corner stays fixed. Normalize\n // the core so endpoint 0 = grabbed corner, endpoint 1 = diagonal, then\n // the line's endpoint-drag path resizes it (all corners stay 90°).\n const corners = boxCorners(gd.points);\n const grabbed = corners[hit.part];\n const diagonal = corners[(hit.part + 2) % 4];\n h.moveDrawingEndpoint(hit.index, 0, grabbed.timeMs, grabbed.price);\n h.moveDrawingEndpoint(hit.index, 1, diagonal.timeMs, diagonal.price);\n grabRef.current = { index: hit.index, endpoint: 0, fixed: diagonal, isBox: true };\n lastCoordRef.current = grabbed;\n applySelection(); // enlarge the grabbed handle\n return; // the handle drag owns this pointer — no pan / long-press\n }\n if (hit && gd && gd.type === 'line' && hit.part <= 1) {\n grabRef.current = {\n index: hit.index,\n endpoint: hit.part,\n fixed: gd.points[hit.part === 0 ? 1 : 0], // the other end stays put\n isBox: false,\n };\n lastCoordRef.current = null;\n applySelection(); // enlarge the grabbed handle\n return; // the handle drag owns this pointer — no pan / long-press\n }\n // Body of the already-selected shape → translate the whole shape on drag.\n // Line body is part 2, box body (interior/edge) part 4, pencil stroke 5.\n const isBody = hit != null && gd != null && hit.part === bodyPart(gd.type);\n if (hit && isBody) {\n const list = optsRef.current.drawings ?? [];\n const selIdx = selectedIdRef.current\n ? list.findIndex((d) => d.id === selectedIdRef.current)\n : -1;\n if (hit.index === selIdx && selIdx >= 0) {\n const start = h.coordAt(x, y);\n const d = list[selIdx];\n if (start && d) {\n const first = d.points[0]!;\n const last = d.points[d.points.length - 1]!;\n dragLineRef.current = {\n index: selIdx,\n startTime: start.timeMs,\n startPrice: start.price,\n a0: first,\n b0: last,\n lastA: first,\n lastB: last,\n // A stroke can't be restated cheaply each frame, so it keeps its\n // original points and is nudged with relative translate calls.\n ...(d.type === 'pencil'\n ? { points0: d.points, appliedTime: 0, appliedPrice: 0 }\n : {}),\n };\n el.style.cursor = 'move';\n return; // the shape drag owns this pointer — no pan / long-press\n }\n }\n }\n downHitRef.current = hit; // non-selected body or miss → resolved on release\n }\n\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 shiftHeld = e.shiftKey;\n lastX = x;\n lastY = y;\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 // Freehand stroke in progress: extend it. Deliberately ahead of the\n // MOVE_THRESH gate below — a pencil records from the very first pixel, so\n // waiting for a 6px \"is this a drag?\" threshold would clip every stroke's\n // start.\n if (pencilRef.current) {\n pencilMove(x, y);\n return;\n }\n\n // Handle drag (pan-mode editing): reposition the grabbed endpoint live,\n // with no move threshold, and never pan.\n if (grabRef.current) {\n moveGrab(x, y);\n return;\n }\n\n // Shape translate (pan-mode editing): shift the whole drawing by the\n // pointer's data-space delta, live, no threshold, never pan.\n if (dragLineRef.current) {\n const g = dragLineRef.current;\n const c = h.coordAt(x, y);\n if (c) {\n const dT = c.timeMs - g.startTime;\n const dP = c.price - g.startPrice;\n if (g.points0) {\n // Pencil: nudge the core by the change since the last move, since\n // restating a whole path every frame would be wasteful. Only the\n // live preview accumulates; the committed payload below is computed\n // from the originals, so no float drift is persisted.\n h.translateDrawing(g.index, dT - (g.appliedTime ?? 0), dP - (g.appliedPrice ?? 0));\n g.appliedTime = dT;\n g.appliedPrice = dP;\n } else {\n g.lastA = { timeMs: g.a0.timeMs + dT, price: g.a0.price + dP };\n g.lastB = { timeMs: g.b0.timeMs + dT, price: g.b0.price + dP };\n h.moveDrawingEndpoint(g.index, 0, g.lastA.timeMs, g.lastA.price);\n h.moveDrawingEndpoint(g.index, 1, g.lastB.timeMs, g.lastB.price);\n }\n if (!moved && Math.hypot(x - downX, y - downY) > MOVE_THRESH) moved = true;\n scheduleRender();\n }\n return;\n }\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 shiftHeld = e.shiftKey; // so a Shift-held draw commit matches the guideline\n const had = pointers.delete(e.pointerId);\n clearLongPress();\n if (pointers.size < 2) pinch.active = false;\n if (!had) return;\n\n // Pencil release: commit the stroke (simplified + rounded). The tool stays\n // active so the next press starts another stroke.\n if (pencilRef.current && pointers.size === 0) {\n pencilEnd();\n el.style.cursor = 'crosshair';\n return;\n }\n\n // Handle-drag release (editing): persist the moved endpoint, keep it\n // selected, and un-grab (drops the 50% enlarge).\n if (grabRef.current && pointers.size === 0) {\n const g = grabRef.current;\n grabRef.current = null;\n const list = optsRef.current.drawings ?? [];\n const d = list[g.index];\n const c = lastCoordRef.current;\n if (d && c) {\n // Box: the two opposite corners are the dragged corner + its fixed\n // diagonal (order-independent). Line: replace the grabbed endpoint,\n // keeping the other in place.\n const movedPt = { timeMs: c.timeMs, price: c.price };\n const pts: DrawPoint[] = g.isBox\n ? [movedPt, g.fixed]\n : [d.points[0]!, d.points[1]!];\n if (!g.isBox) pts[g.endpoint] = movedPt;\n const next = makeDrawing({ id: d.id, color: d.color, width: d.width }, d.type, pts);\n if (next) optsRef.current.onDrawingChange?.(next);\n }\n applySelection();\n el.style.cursor = '';\n return;\n }\n\n // Shape-translate release: persist the moved drawing, keep it selected.\n if (dragLineRef.current && pointers.size === 0) {\n const g = dragLineRef.current;\n dragLineRef.current = null;\n if (moved) {\n const list = optsRef.current.drawings ?? [];\n const d = list[g.index];\n if (d) {\n // Pencil: recompute every point from the originals + the total\n // delta, so what's persisted carries none of the live preview's\n // accumulated float error.\n const pts = g.points0\n ? roundPoints(\n g.points0.map((p) => ({\n timeMs: p.timeMs + (g.appliedTime ?? 0),\n price: p.price + (g.appliedPrice ?? 0),\n })),\n )\n : [g.lastA, g.lastB];\n const next = makeDrawing({ id: d.id, color: d.color, width: d.width }, d.type, pts);\n if (next) optsRef.current.onDrawingChange?.(next);\n }\n }\n applySelection();\n el.style.cursor = '';\n return;\n }\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 // Editing tap (pan mode): a stationary click on a line body selects it;\n // on empty space it deselects. (Skip a long-press, which owns the crosshair.)\n if (pointers.size === 0 && !moved && panMode === 'chart' && crosshairSource !== 'press') {\n const hit = downHitRef.current;\n const list = optsRef.current.drawings ?? [];\n const hitDrawing = hit ? list[hit.index] : undefined;\n // Body hit: line 2, box 4, pencil 5.\n const isBodyHit =\n hit != null && hitDrawing != null && hit.part === bodyPart(hitDrawing.type);\n if (hit && isBodyHit && hitDrawing) {\n selectedTRef.current = hit.t; // remember where along the line it was grabbed\n if (selectedIdRef.current !== hitDrawing.id) {\n selectedIdRef.current = hitDrawing.id;\n applySelection();\n }\n } else if (!hit && selectedIdRef.current != null) {\n selectedIdRef.current = null;\n applySelection();\n }\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 // Shift press/release re-applies the 45° constraint live at the last cursor\n // position — even with the mouse still — while drawing or dragging a handle.\n const onShiftKey = (e: KeyboardEvent) => {\n if (e.key !== 'Shift') return;\n const held = e.type === 'keydown';\n if (held === shiftHeld) return;\n shiftHeld = held;\n if (drawAnchorRef.current) updateGuideline(lastX, lastY);\n else if (grabRef.current) moveGrab(lastX, lastY);\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 window.addEventListener('keydown', onShiftKey);\n window.addEventListener('keyup', onShiftKey);\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 window.removeEventListener('keydown', onShiftKey);\n window.removeEventListener('keyup', onShiftKey);\n };\n }, [containerRef, handleRef, scheduleRender]);\n}\n","// Ramer–Douglas–Peucker polyline simplification, used to thin a freehand pencil\n// stroke before it is committed and persisted.\n//\n// A drag captures a sample every few pixels, so a single stroke can arrive with\n// hundreds of points — most of them redundant along near-straight runs. RDP\n// keeps only the points that actually carry the shape: it finds the sample\n// farthest from the chord between the current endpoints and, if that distance\n// exceeds `epsilon`, keeps it and recurses on both halves; otherwise the whole\n// span collapses to its endpoints.\n//\n// We run this in *pixel* space (not data space) so `epsilon` means \"visible\n// deviation on screen\" regardless of the chart's zoom or price scale, then map\n// the surviving indices back to the captured data-space points.\n\nexport type Pt = { x: number; y: number };\n\n/** Perpendicular distance from `p` to the (infinite) line through `a` and `b`. */\nfunction perpendicularDistance(p: Pt, a: Pt, b: Pt): number {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n // Degenerate chord (a === b): fall back to plain point distance.\n if (dx === 0 && dy === 0) return Math.hypot(p.x - a.x, p.y - a.y);\n // |cross product| / |chord| — the triangle-area formulation, no division by\n // zero and no need to normalize the direction vector first.\n return Math.abs(dy * p.x - dx * p.y + b.x * a.y - b.y * a.x) / Math.hypot(dx, dy);\n}\n\n/**\n * Indices of the points to keep, in ascending order, always including the first\n * and last. `epsilon` is the maximum allowed deviation in the same units as the\n * input (px); `0` keeps everything.\n *\n * Iterative rather than recursive so a pathological stroke can't blow the stack.\n */\nexport function simplifyIndices(points: readonly Pt[], epsilon: number): number[] {\n const n = points.length;\n if (n <= 2) return points.map((_, i) => i);\n if (epsilon <= 0) return points.map((_, i) => i);\n\n const keep = new Uint8Array(n);\n keep[0] = 1;\n keep[n - 1] = 1;\n\n // Explicit stack of [start, end] spans still to examine.\n const stack: [number, number][] = [[0, n - 1]];\n while (stack.length > 0) {\n const [start, end] = stack.pop()!;\n if (end <= start + 1) continue; // nothing between the endpoints\n\n let farthest = -1;\n let maxDist = -1;\n for (let i = start + 1; i < end; i++) {\n const d = perpendicularDistance(points[i]!, points[start]!, points[end]!);\n if (d > maxDist) {\n maxDist = d;\n farthest = i;\n }\n }\n\n // Within tolerance: every point between start and end is redundant.\n if (maxDist <= epsilon || farthest < 0) continue;\n\n keep[farthest] = 1;\n stack.push([start, farthest], [farthest, end]);\n }\n\n const out: number[] = [];\n for (let i = 0; i < n; i++) if (keep[i]) out.push(i);\n return out;\n}\n\n/** Convenience wrapper returning the kept points themselves. */\nexport function simplify(points: readonly Pt[], epsilon: number): Pt[] {\n return simplifyIndices(points, epsilon).map((i) => points[i]!);\n}\n","// Managed drawing persistence. When a `drawingStore` is provided, the chart owns\n// the drawings array itself and loads/saves it through the store, keyed by\n// `seriesKey` (the market). Drawings are data-space anchored, so keying by market\n// means they persist across timeframe changes but not across markets.\n//\n// Returns the internal drawings plus the three edit handlers to feed into the\n// gesture layer (in place of the controlled props). A no-op when `store` is\n// undefined, so it can be called unconditionally (rules of hooks).\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { Drawing, DrawingStore } from '@vroomchart/types';\n\nimport { serializeDrawings, deserializeDrawings } from './drawingStorage';\n\nconst SAVE_DEBOUNCE_MS = 400;\n\nexport type ManagedDrawings = {\n drawings: Drawing[];\n onDrawingComplete: (d: Drawing) => void;\n onDrawingChange: (d: Drawing) => void;\n onDrawingDelete: (id: string) => void;\n};\n\nexport function useManagedDrawings(\n seriesKey: string | undefined,\n store: DrawingStore | undefined,\n): ManagedDrawings {\n const [drawings, setDrawings] = useState<Drawing[]>([]);\n\n // Latest-committed refs so the load effect / debounce read current values\n // without re-subscribing.\n const drawingsRef = useRef<Drawing[]>([]);\n const storeRef = useRef(store);\n storeRef.current = store;\n // The market whose drawings are currently in state — so we never save one\n // market's drawings under another's key during an async load race.\n const loadedKeyRef = useRef<string | undefined>(undefined);\n const loadTokenRef = useRef(0);\n const saveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const warnedRef = useRef(false);\n\n const setBoth = (list: Drawing[]) => {\n drawingsRef.current = list;\n setDrawings(list);\n };\n\n // Persist immediately (cancelling any pending debounce). Best-effort.\n const flushSave = useCallback((key: string | undefined, list: Drawing[]) => {\n if (saveTimerRef.current != null) {\n clearTimeout(saveTimerRef.current);\n saveTimerRef.current = null;\n }\n const s = storeRef.current;\n if (!s || key == null) return;\n try {\n // The store is an opaque string KV — the library owns the versioned\n // envelope, so serialize here and hand over bytes.\n const r = s.save(key, serializeDrawings(list));\n if (r && typeof (r as Promise<void>).catch === 'function') {\n (r as Promise<void>).catch(() => {});\n }\n } catch {\n /* best-effort */\n }\n }, []);\n\n const scheduleSave = useCallback(() => {\n const key = seriesKey;\n if (!storeRef.current || key == null) return;\n if (saveTimerRef.current != null) clearTimeout(saveTimerRef.current);\n saveTimerRef.current = setTimeout(() => {\n saveTimerRef.current = null;\n flushSave(key, drawingsRef.current);\n }, SAVE_DEBOUNCE_MS);\n }, [seriesKey, flushSave]);\n\n // Load on mount / market change; flush-save the outgoing market on the way out.\n useEffect(() => {\n const s = storeRef.current;\n if (!s) return;\n if (seriesKey == null) {\n if (!warnedRef.current && typeof console !== 'undefined') {\n console.warn(\n '[vroom] drawingStore is set but seriesKey is missing — drawings will render but not persist.',\n );\n warnedRef.current = true;\n }\n setBoth([]);\n loadedKeyRef.current = undefined;\n return;\n }\n\n const token = ++loadTokenRef.current;\n const result = s.load(seriesKey);\n if (result && typeof (result as Promise<string | null>).then === 'function') {\n // Async: clear now so stale drawings don't show, apply when resolved.\n setBoth([]);\n loadedKeyRef.current = undefined;\n (result as Promise<string | null | undefined>)\n .then((raw) => {\n if (token !== loadTokenRef.current) return; // superseded by a newer switch\n loadedKeyRef.current = seriesKey;\n setBoth(deserializeDrawings(raw));\n })\n .catch(() => {\n if (token !== loadTokenRef.current) return;\n loadedKeyRef.current = seriesKey;\n setBoth([]);\n });\n } else {\n loadedKeyRef.current = seriesKey;\n setBoth(deserializeDrawings(result as string | null | undefined));\n }\n\n // On the next market switch (or unmount), save this market's drawings — but\n // only if the in-memory set actually belongs to this key (async-load guard).\n return () => {\n if (loadedKeyRef.current === seriesKey) {\n flushSave(seriesKey, drawingsRef.current);\n } else if (saveTimerRef.current != null) {\n clearTimeout(saveTimerRef.current);\n saveTimerRef.current = null;\n }\n };\n }, [seriesKey, flushSave]);\n\n const onDrawingComplete = useCallback(\n (d: Drawing) => {\n setDrawings((p) => {\n const next = [...p, d];\n drawingsRef.current = next;\n return next;\n });\n scheduleSave();\n },\n [scheduleSave],\n );\n const onDrawingChange = useCallback(\n (d: Drawing) => {\n setDrawings((p) => {\n const next = p.map((x) => (x.id === d.id ? d : x));\n drawingsRef.current = next;\n return next;\n });\n scheduleSave();\n },\n [scheduleSave],\n );\n const onDrawingDelete = useCallback(\n (id: string) => {\n setDrawings((p) => {\n const next = p.filter((x) => x.id !== id);\n drawingsRef.current = next;\n return next;\n });\n scheduleSave();\n },\n [scheduleSave],\n );\n\n return { drawings, onDrawingComplete, onDrawingChange, onDrawingDelete };\n}\n","// Versioned envelope for persisted drawings. The `DrawingStore` adapter is an\n// opaque string key-value store; this module owns everything inside that string\n// so the adapter never has to change as the schema evolves:\n//\n// drawings ──serialize──▶ '{\"v\":1,\"drawings\":[…]}' ──store.save──▶ bytes\n// bytes ──store.load──▶ string ──deserialize (+migrate)──▶ drawings\n//\n// Evolving the schema without breaking clients:\n// • New drawing tool → add its `type` to KNOWN_TYPES + the Drawing union (and\n// to FIXED_POINT_TYPES if it has exactly two anchors). Old payloads still\n// parse; payloads carrying an unknown tool (e.g. written by a newer version,\n// then read by an older one) are dropped, not crashed.\n// • New persisted field → add it to the envelope, bump DRAWINGS_VERSION, and\n// register a MIGRATIONS[oldVersion] that upgrades an old envelope to the new\n// shape. Old stored strings migrate transparently on the next load.\n// The adapter interface (string in / string out) stays fixed through all of it.\n\nimport type { Drawing } from '@vroomchart/types';\n\n/** Current envelope schema version. Bump when the persisted shape changes. */\nexport const DRAWINGS_VERSION = 1;\n\n/** Drawing `type`s this build understands. Extend as tools are added. */\nconst KNOWN_TYPES = new Set<Drawing['type']>(['line', 'box', 'pencil']);\n\n/** How many points each type must carry; pencil is variable (2 or more). */\nconst FIXED_POINT_TYPES = new Set(['line', 'box']);\n\nfunction isPoint(p: unknown): boolean {\n if (p == null || typeof p !== 'object') return false;\n const { timeMs, price } = p as { timeMs?: unknown; price?: unknown };\n return (\n typeof timeMs === 'number' &&\n Number.isFinite(timeMs) &&\n typeof price === 'number' &&\n Number.isFinite(price)\n );\n}\n\n/**\n * Whether a parsed entry is a drawing this build can actually render. Checks the\n * `type` *and* the shape of `points` — the store is an opaque, consumer-supplied\n * blob, and since a pencil stroke is a variable-length array we can no longer\n * assume two well-formed anchors. Anything malformed is dropped exactly like an\n * unknown type, rather than reaching the renderer as garbage.\n */\nfunction isValidDrawing(d: unknown): d is Drawing {\n if (d == null || typeof d !== 'object') return false;\n const { type, points } = d as { type?: unknown; points?: unknown };\n if (typeof type !== 'string' || !KNOWN_TYPES.has(type as Drawing['type'])) {\n return false;\n }\n if (!Array.isArray(points)) return false;\n if (FIXED_POINT_TYPES.has(type) ? points.length !== 2 : points.length < 2) {\n return false;\n }\n return points.every(isPoint);\n}\n\ntype Envelope = { v: number; drawings: Drawing[] };\n\n/**\n * Migration map: `MIGRATIONS[n]` upgrades a version-`n` envelope to version\n * `n+1`. Applied in sequence until the payload reaches DRAWINGS_VERSION, so a\n * very old blob walks up one step at a time. Add an entry each time the schema\n * changes; never mutate the input.\n *\n * Example (when v2 arrives):\n * [1]: (e) => ({ v: 2, drawings: e.drawings.map((d) => ({ ...d, opacity: 1 })) }),\n */\nconst MIGRATIONS: Record<number, (e: Envelope) => Envelope> = {};\n\n/** Wrap the current drawings in a versioned envelope and stringify. */\nexport function serializeDrawings(drawings: Drawing[]): string {\n const env: Envelope = { v: DRAWINGS_VERSION, drawings };\n return JSON.stringify(env);\n}\n\n/**\n * Parse a stored string back into drawings, migrating older envelopes and\n * dropping anything malformed or of an unknown drawing `type`. Never throws —\n * returns `[]` for null/empty/garbage input.\n */\nexport function deserializeDrawings(raw: string | null | undefined): Drawing[] {\n if (raw == null || raw === '') return [];\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return [];\n }\n\n // Accept a bare array as the pre-envelope v0 shape, so payloads written before\n // versioning (or by a hand-rolled adapter) still load and get upgraded.\n let env: Envelope;\n if (Array.isArray(parsed)) {\n env = { v: 0, drawings: parsed as Drawing[] };\n } else if (parsed != null && typeof parsed === 'object') {\n const obj = parsed as Partial<Envelope>;\n env = {\n v: typeof obj.v === 'number' ? obj.v : 0,\n drawings: Array.isArray(obj.drawings) ? obj.drawings : [],\n };\n } else {\n return [];\n }\n\n // Walk the migration chain up to the current version. Stop (keep what we have)\n // if a step is missing or the payload is somehow newer than we understand.\n let guard = 0;\n while (env.v < DRAWINGS_VERSION && guard++ < 100) {\n const up = MIGRATIONS[env.v];\n if (!up) break;\n env = up(env);\n }\n\n // Forward-compat: keep only drawings this build can render — a known `type`\n // carrying a well-formed `points` array. A newer file with an unknown tool (or\n // a corrupt entry) loads cleanly minus that entry, rather than throwing or\n // drawing garbage.\n return (Array.isArray(env.drawings) ? env.drawings : []).filter(isValidDrawing);\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 { useManagedDrawings } from './useManagedDrawings';\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 drawings,\n onCrosshair,\n onViewportChange,\n onDrawingComplete,\n onDrawingChange,\n onDrawingDelete,\n onModeChange,\n drawingStore,\n seriesKey,\n } = props;\n\n // Managed persistence: when a `drawingStore` is provided the chart owns the\n // drawings array itself (keyed by seriesKey) instead of the controlled props.\n // The hook is always called (rules of hooks) but no-ops without a store.\n const managed = useManagedDrawings(seriesKey, drawingStore);\n const stored = drawingStore != null;\n const effectiveDrawings = stored ? managed.drawings : drawings;\n\n const { containerRef, canvasRef, handleRef, scheduleRender } = useChartCore(\n stored ? { ...props, drawings: effectiveDrawings } : props,\n wasm ? { wasm } : undefined,\n );\n\n useGestures(containerRef, handleRef, scheduleRender, {\n crosshairOffset,\n crosshairOverride,\n mode,\n tool,\n drawings: effectiveDrawings,\n onCrosshair,\n onViewportChange,\n onDrawingComplete: stored ? managed.onDrawingComplete : onDrawingComplete,\n onDrawingChange: stored ? managed.onDrawingChange : onDrawingChange,\n onDrawingDelete: stored ? managed.onDrawingDelete : onDrawingDelete,\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":["useCallback","useEffect","useRef","useRef","useCallback","useEffect","el","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vroomchart/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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.
|
|
43
|
+
"@vroomchart/core-wasm": "0.5.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"react": ">=18",
|