@vroomchart/core-wasm 0.1.1 → 0.1.2
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/assets/vroom_core.wasm +0 -0
- package/dist/index.d.ts +33 -3
- package/dist/index.js +19 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/assets/vroom_core.wasm
CHANGED
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,13 @@ declare enum ColorKey {
|
|
|
9
9
|
Crosshair = 6,
|
|
10
10
|
TooltipBg = 7,
|
|
11
11
|
TooltipText = 8,
|
|
12
|
-
CrosshairTarget = 9
|
|
12
|
+
CrosshairTarget = 9,
|
|
13
|
+
BorderBull = 10,
|
|
14
|
+
BorderBear = 11,
|
|
15
|
+
WickBull = 12,
|
|
16
|
+
WickBear = 13,
|
|
17
|
+
AccentBull = 14,
|
|
18
|
+
AccentBear = 15
|
|
13
19
|
}
|
|
14
20
|
/** OHLCV readout for the candle under the crosshair. */
|
|
15
21
|
type CrosshairCandle = {
|
|
@@ -72,6 +78,18 @@ interface VroomChartHandle {
|
|
|
72
78
|
scalePriceAxis(dy: number): void;
|
|
73
79
|
/** Drag-on-x-axis time scaling; dx>0 widens the time window. */
|
|
74
80
|
scaleTimeAxis(dx: number): void;
|
|
81
|
+
/**
|
|
82
|
+
* Drag the separator between the price pane and the below-chart indicator
|
|
83
|
+
* band. dy>0 (drag down) grows the price pane and shrinks the indicator band,
|
|
84
|
+
* preserving candle pixel scale (the price range adjusts instead).
|
|
85
|
+
*/
|
|
86
|
+
resizeIndicatorPane(dy: number): void;
|
|
87
|
+
/**
|
|
88
|
+
* Scale the y-axis of the below-chart indicator pane containing `y` (px).
|
|
89
|
+
* dy>0 (drag down) zooms out (widens the value range); dy<0 zooms in. RSI
|
|
90
|
+
* scales about 50, MACD about its zero line. No-op when `y` is not over a pane.
|
|
91
|
+
*/
|
|
92
|
+
scaleIndicatorAxis(y: number, dy: number): void;
|
|
75
93
|
/** Current axis dimensions for hit-testing in JS gestures. */
|
|
76
94
|
getAxisMetrics(): AxisMetrics;
|
|
77
95
|
/** Show the crosshair at (x, y) CSS px (y already lifted above the pointer). */
|
|
@@ -137,10 +155,22 @@ type VroomColor = string | number;
|
|
|
137
155
|
type VroomTheme = {
|
|
138
156
|
/** Chart + axis-strip background. */
|
|
139
157
|
background?: VroomColor;
|
|
140
|
-
/** Up
|
|
158
|
+
/** Up candle body fill. Wick and border default to this unless overridden. */
|
|
141
159
|
bull?: VroomColor;
|
|
142
|
-
/** Down
|
|
160
|
+
/** Down candle body fill. Wick and border default to this unless overridden. */
|
|
143
161
|
bear?: VroomColor;
|
|
162
|
+
/** Generic up color for the price indicator, volume bars, and MACD histogram. Defaults to teal-green; independent of `bull`. */
|
|
163
|
+
accentBull?: VroomColor;
|
|
164
|
+
/** Generic down color for the price indicator, volume bars, and MACD histogram. Defaults to red; independent of `bear`. */
|
|
165
|
+
accentBear?: VroomColor;
|
|
166
|
+
/** Up candle body 1px border. Defaults to the bull fill color. */
|
|
167
|
+
borderBull?: VroomColor;
|
|
168
|
+
/** Down candle body 1px border. Defaults to the bear fill color. */
|
|
169
|
+
borderBear?: VroomColor;
|
|
170
|
+
/** Up candle wick color. Defaults to the bull fill color. */
|
|
171
|
+
wickBull?: VroomColor;
|
|
172
|
+
/** Down candle wick color. Defaults to the bear fill color. */
|
|
173
|
+
wickBear?: VroomColor;
|
|
144
174
|
/** Gridlines. */
|
|
145
175
|
grid?: VroomColor;
|
|
146
176
|
/** Axis label text (price + time). */
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,12 @@ var WasmHandle = class {
|
|
|
35
35
|
scaleTimeAxis(dx) {
|
|
36
36
|
this.wc.scaleTimeAxis(dx);
|
|
37
37
|
}
|
|
38
|
+
resizeIndicatorPane(dy) {
|
|
39
|
+
this.wc.resizeIndicatorPane(dy);
|
|
40
|
+
}
|
|
41
|
+
scaleIndicatorAxis(y, dy) {
|
|
42
|
+
this.wc.scaleIndicatorAxis(y, dy);
|
|
43
|
+
}
|
|
38
44
|
getAxisMetrics() {
|
|
39
45
|
return this.wc.getAxisMetrics();
|
|
40
46
|
}
|
|
@@ -121,6 +127,12 @@ var ColorKey = /* @__PURE__ */ ((ColorKey2) => {
|
|
|
121
127
|
ColorKey2[ColorKey2["TooltipBg"] = 7] = "TooltipBg";
|
|
122
128
|
ColorKey2[ColorKey2["TooltipText"] = 8] = "TooltipText";
|
|
123
129
|
ColorKey2[ColorKey2["CrosshairTarget"] = 9] = "CrosshairTarget";
|
|
130
|
+
ColorKey2[ColorKey2["BorderBull"] = 10] = "BorderBull";
|
|
131
|
+
ColorKey2[ColorKey2["BorderBear"] = 11] = "BorderBear";
|
|
132
|
+
ColorKey2[ColorKey2["WickBull"] = 12] = "WickBull";
|
|
133
|
+
ColorKey2[ColorKey2["WickBear"] = 13] = "WickBear";
|
|
134
|
+
ColorKey2[ColorKey2["AccentBull"] = 14] = "AccentBull";
|
|
135
|
+
ColorKey2[ColorKey2["AccentBear"] = 15] = "AccentBear";
|
|
124
136
|
return ColorKey2;
|
|
125
137
|
})(ColorKey || {});
|
|
126
138
|
|
|
@@ -167,7 +179,13 @@ var COLOR_KEYS = {
|
|
|
167
179
|
grid: 4 /* Grid */,
|
|
168
180
|
axisText: 5 /* AxisText */,
|
|
169
181
|
crosshair: 6 /* Crosshair */,
|
|
170
|
-
crosshairTarget: 9 /* CrosshairTarget
|
|
182
|
+
crosshairTarget: 9 /* CrosshairTarget */,
|
|
183
|
+
borderBull: 10 /* BorderBull */,
|
|
184
|
+
borderBear: 11 /* BorderBear */,
|
|
185
|
+
wickBull: 12 /* WickBull */,
|
|
186
|
+
wickBear: 13 /* WickBear */,
|
|
187
|
+
accentBull: 14 /* AccentBull */,
|
|
188
|
+
accentBear: 15 /* AccentBear */
|
|
171
189
|
};
|
|
172
190
|
function parseColor(value) {
|
|
173
191
|
if (typeof value === "number") {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/wasm/adapter.ts","../src/wasm/loadWasm.ts","../src/handle.ts","../src/packCandles.ts","../src/color.ts","../src/index.ts"],"sourcesContent":["// Adapts the emscripten `WebChart` (embind class from web/vroom_web.cpp) to the\n// VroomChartHandle / VroomModule contract that @vroomchart/react consumes. This is\n// the only place that knows the WASM module's shape — swapping the stub for the\n// real core changes nothing above this line.\n\nimport type {\n AxisMetrics,\n CrosshairCandle,\n CrosshairInfo,\n OverlaySpec,\n VroomChartHandle,\n VroomModule,\n} from '../handle';\n\n// The embind class instance. Methods mirror web/vroom_web.cpp; `delete()` frees\n// the C++ object (embind requires explicit disposal).\ninterface WebChartInstance {\n setCandles(bytes: Uint8Array): void;\n setSize(width: number, height: number, dpr: number): void;\n setColor(key: number, argb: number): void;\n setVisibleRange(startMs: number, endMs: number): void;\n pan(dx: number, dy: number): void;\n translate(dx: number, dy: number): void;\n zoom(sx: number, sy: number, fx: number, fy: number): void;\n scalePriceAxis(dy: number): void;\n scaleTimeAxis(dx: number): void;\n getAxisMetrics(): AxisMetrics;\n setCrosshair(x: number, y: number): void;\n clearCrosshair(): void;\n getCrosshairCandle(): CrosshairCandle | null;\n getCrosshairInfo(): CrosshairInfo | null;\n setRSI(e: boolean, p: number, u: number, l: number, mae: boolean, map: number): void;\n setMACD(e: boolean, f: number, s: number, sig: number): void;\n setOverlays(overlays: OverlaySpec[]): void;\n setVWAP(e: boolean, reset: number, color: number, width: number): void;\n setTypeface(bytes: Uint8Array): void;\n isAnimating(): boolean;\n present(): void;\n delete(): void;\n}\n\n/** The shape of the instantiated emscripten module we depend on. */\nexport interface VroomWasmModule {\n WebChart: new (canvasSelector: string) => WebChartInstance;\n}\n\nlet uid = 0;\n\nclass WasmHandle implements VroomChartHandle {\n constructor(private wc: WebChartInstance) {}\n\n setCandles(packed: ArrayBuffer): void {\n this.wc.setCandles(new Uint8Array(packed));\n }\n setSize(width: number, height: number, dpr: number): void {\n this.wc.setSize(width, height, dpr);\n }\n setColor(key: number, argb: number): void {\n this.wc.setColor(key, argb >>> 0);\n }\n setVisibleRange(startMs: number, endMs: number): void {\n this.wc.setVisibleRange(startMs, endMs);\n }\n pan(dx: number, dy: number): void {\n this.wc.pan(dx, dy);\n }\n translate(dx: number, dy: number): void {\n this.wc.translate(dx, dy);\n }\n zoom(scaleX: number, scaleY: number, fx: number, fy: number): void {\n this.wc.zoom(scaleX, scaleY, fx, fy);\n }\n scalePriceAxis(dy: number): void {\n this.wc.scalePriceAxis(dy);\n }\n scaleTimeAxis(dx: number): void {\n this.wc.scaleTimeAxis(dx);\n }\n getAxisMetrics(): AxisMetrics {\n return this.wc.getAxisMetrics();\n }\n setCrosshair(x: number, y: number): void {\n this.wc.setCrosshair(x, y);\n }\n clearCrosshair(): void {\n this.wc.clearCrosshair();\n }\n getCrosshairCandle(): CrosshairCandle | null {\n return this.wc.getCrosshairCandle();\n }\n getCrosshairInfo(): CrosshairInfo | null {\n return this.wc.getCrosshairInfo();\n }\n setRSI(\n enabled: boolean,\n period: number,\n upperBand: number,\n lowerBand: number,\n maEnabled: boolean,\n maPeriod: number,\n ): void {\n this.wc.setRSI(enabled, period, upperBand, lowerBand, maEnabled, maPeriod);\n }\n setMACD(enabled: boolean, fast: number, slow: number, signal: number): void {\n this.wc.setMACD(enabled, fast, slow, signal);\n }\n setOverlays(overlays: OverlaySpec[]): void {\n this.wc.setOverlays(overlays);\n }\n setVWAP(enabled: boolean, resetOffsetMin: number, color: number, width: number): void {\n this.wc.setVWAP(enabled, resetOffsetMin, color >>> 0, width);\n }\n isAnimating(): boolean {\n return this.wc.isAnimating();\n }\n present(): void {\n this.wc.present();\n }\n destroy(): void {\n this.wc.delete();\n }\n}\n\n/**\n * Wrap an instantiated WASM module as a VroomModule. `fontBytes` (a .ttf/.otf)\n * is installed as the axis typeface on each chart — required for text labels,\n * since the WASM sandbox has no system fonts.\n */\nexport function makeWasmModule(\n module: VroomWasmModule,\n fontBytes: Uint8Array | null,\n): VroomModule {\n return {\n create(canvas: HTMLCanvasElement) {\n if (!canvas.id) canvas.id = `vroom-canvas-${uid++}`;\n const wc = new module.WebChart(`#${canvas.id}`);\n if (fontBytes) wc.setTypeface(fontBytes);\n return new WasmHandle(wc);\n },\n };\n}\n","// Instantiates the Skia-WASM core (the emscripten ES module built from\n// packages/core/web + CMake VROOM_WASM) and adapts it to a VroomModule.\n//\n// The module URL is resolved at runtime (not statically imported) so bundlers\n// don't try to resolve a not-yet-built artifact — keeping the stub-only build\n// green until the WASM file actually exists.\n\nimport type { VroomModule } from '../handle';\nimport { makeWasmModule, type VroomWasmModule } from './adapter';\n\nexport type WasmConfig = {\n /** URL to the emscripten ES module (vroom_core.mjs). */\n moduleUrl: string;\n /** URL to vroom_core.wasm. Defaults to resolving next to the module. */\n wasmUrl?: string;\n /** URL to a .ttf/.otf used for axis/price/time labels. */\n fontUrl?: string;\n};\n\n// The emscripten MODULARIZE/EXPORT_ES6 default export is a factory:\n// createVroomCore(moduleOverrides) => Promise<Module>\ntype EmscriptenFactory = (overrides?: Record<string, unknown>) => Promise<VroomWasmModule>;\n\n// Import the emscripten ES module from a runtime URL WITHOUT the bundler trying\n// to resolve/transform it. A plain import(url) (even with /* @vite-ignore */) is\n// still intercepted by Vite's \"don't import from /public\" guard and by other\n// bundlers' static analysis. Routing through Function keeps it fully opaque.\n// (Requires 'unsafe-eval' under a strict CSP — self-host the module differently\n// if that's a constraint.)\nconst dynamicImport = new Function('u', 'return import(u)') as (\n u: string,\n) => Promise<{ default: EmscriptenFactory }>;\n\nexport async function loadWasmCore(cfg: WasmConfig): Promise<VroomModule> {\n const mod = await dynamicImport(cfg.moduleUrl);\n const instance = await mod.default({\n locateFile: (path: string) =>\n path.endsWith('.wasm') && cfg.wasmUrl ? cfg.wasmUrl : path,\n });\n\n let fontBytes: Uint8Array | null = null;\n if (cfg.fontUrl) {\n try {\n const res = await fetch(cfg.fontUrl);\n if (res.ok) {\n fontBytes = new Uint8Array(await res.arrayBuffer());\n console.info(`[vroom] axis font loaded (${cfg.fontUrl}, ${fontBytes.length} bytes)`);\n } else {\n console.warn(`[vroom] axis font fetch ${cfg.fontUrl} → HTTP ${res.status}; labels will be blank.`);\n }\n } catch (e) {\n console.warn(`[vroom] axis font fetch failed (${cfg.fontUrl}); labels will be blank.`, e);\n }\n } else {\n console.warn('[vroom] no fontUrl provided; text labels will be blank.');\n }\n\n return makeWasmModule(instance, fontBytes);\n}\n","// The low-level contract for one chart instance on the web.\n//\n// This mirrors the C facade (packages/core/include/vroom/vroom_chart.h) and the\n// native JSI handle (packages/react-native/src/jsi.d.ts) — but WITHOUT the\n// SkPicture return values. On web the core owns the <canvas> and its rendering\n// surface, so mutations are `void` and `present()` paints. Backed by the\n// Skia-WASM module (./wasm).\n\n/** Theme color slots — mirrors the `VroomColorKey` enum in the C facade. */\nexport enum ColorKey {\n Background = 0,\n Bull = 1,\n Bear = 2,\n Wick = 3,\n Grid = 4,\n AxisText = 5,\n Crosshair = 6,\n TooltipBg = 7,\n TooltipText = 8,\n CrosshairTarget = 9,\n}\n\n/** OHLCV readout for the candle under the crosshair. */\nexport type CrosshairCandle = {\n timeMs: number;\n open: number;\n high: number;\n low: number;\n close: number;\n volume: number;\n};\n\n/**\n * What the crosshair currently snaps to. `timeMs` is the snapped slot's\n * period-start time — set even in the empty space ahead of the most recent\n * candle. `candle` holds the OHLCV when a real candle sits at that slot, and is\n * null when the crosshair is parked on a future candle-aligned slot.\n */\nexport type CrosshairInfo = {\n timeMs: number;\n candle: CrosshairCandle | null;\n};\n\n/** Axis region sizes in CSS px, for hit-testing gestures on the JS side. */\nexport type AxisMetrics = {\n yAxisWidth: number;\n xAxisHeight: number;\n /** Below-chart indicator pane height; 0 when no pane is shown. */\n indicatorHeight: number;\n};\n\n/** A moving-average overlay line, in the core's numeric encoding. */\nexport type OverlaySpec = {\n /** 0 = SMA, 1 = EMA. */\n kind: number;\n period: number;\n /** 0=close,1=open,2=high,3=low,4=hl2,5=hlc3,6=ohlc4 */\n source: number;\n /** Packed 0xAARRGGBB. */\n color: number;\n /** Stroke width in px. */\n width: number;\n};\n\n/**\n * A single chart instance bound to a canvas. Method semantics match the native\n * handle one-to-one (see jsi.d.ts) so the two platforms behave identically.\n */\nexport interface VroomChartHandle {\n /** Replace the full candle series (packed buffer; see packCandles). */\n setCandles(packed: ArrayBuffer): void;\n /** Set the drawing size in CSS px plus the device pixel ratio. */\n setSize(width: number, height: number, dpr: number): void;\n /** Override one theme color. `argb` is packed 0xAARRGGBB. */\n setColor(key: ColorKey | number, argb: number): void;\n /** Pass 0, 0 to show all candles. */\n setVisibleRange(startMs: number, endMs: number): void;\n\n /** Shift the visible range by dx/dy CSS px. */\n pan(dx: number, dy: number): void;\n /** Shift the time window and price bounds without rescaling. */\n translate(dx: number, dy: number): void;\n /** Directional zoom by per-axis factors around focus (fx, fy) in px. */\n zoom(scaleX: number, scaleY: number, fx: number, fy: number): void;\n /** Drag-on-y-axis price scaling; dy>0 widens the price range. */\n scalePriceAxis(dy: number): void;\n /** Drag-on-x-axis time scaling; dx>0 widens the time window. */\n scaleTimeAxis(dx: number): void;\n\n /** Current axis dimensions for hit-testing in JS gestures. */\n getAxisMetrics(): AxisMetrics;\n\n /** Show the crosshair at (x, y) CSS px (y already lifted above the pointer). */\n setCrosshair(x: number, y: number): void;\n /** Hide the crosshair. */\n clearCrosshair(): void;\n /** OHLCV of the candle the crosshair snaps to, or null when inactive. */\n getCrosshairCandle(): CrosshairCandle | null;\n /**\n * The slot the crosshair snaps to (real candle or future candle-aligned\n * slot), or null when inactive. Unlike getCrosshairCandle this reports a\n * timeMs even in the empty space ahead of the most recent candle.\n */\n getCrosshairInfo(): CrosshairInfo | null;\n\n setRSI(\n enabled: boolean,\n period: number,\n upperBand: number,\n lowerBand: number,\n maEnabled: boolean,\n maPeriod: number,\n ): void;\n setMACD(enabled: boolean, fast: number, slow: number, signal: number): void;\n setOverlays(overlays: OverlaySpec[]): void;\n setVWAP(\n enabled: boolean,\n resetOffsetMin: number,\n color: number,\n width: number,\n ): void;\n\n /** True while any axis-label fade is in progress (drives the rAF loop). */\n isAnimating(): boolean;\n /** Paint the current state to the canvas. */\n present(): void;\n /** Release resources; the handle is unusable afterward. */\n destroy(): void;\n}\n\n/** Factory for chart instances. The loaded core module implements this. */\nexport interface VroomModule {\n /** Create a chart bound to `canvas`, attaching its rendering surface. */\n create(canvas: HTMLCanvasElement): VroomChartHandle;\n}\n","import type { Candle } from '@vroomchart/types';\n\n// Wire format must match `VroomCandle` in packages/core/include/vroom/vroom_chart.h:\n// int64_t time_ms; double open, high, low, close, volume;\n// = 48 bytes per candle, 8-byte aligned, little-endian.\n//\n// This is a deliberate mirror of packages/react-native/src/packCandles.ts so\n// the web side speaks the same binary ABI the Skia-WASM core will decode. Keep\n// the two in sync with the C struct.\nexport const BYTES_PER_CANDLE = 48;\n\n/** Serialize candles into the packed little-endian buffer the core expects. */\nexport function packCandles(candles: Candle[]): ArrayBuffer {\n const buf = new ArrayBuffer(candles.length * BYTES_PER_CANDLE);\n const view = new DataView(buf);\n for (let i = 0; i < candles.length; i++) {\n const c = candles[i]!;\n const off = i * BYTES_PER_CANDLE;\n view.setBigInt64(off, BigInt(Math.trunc(c.timeMs)), true);\n view.setFloat64(off + 8, c.open, true);\n view.setFloat64(off + 16, c.high, true);\n view.setFloat64(off + 24, c.low, true);\n view.setFloat64(off + 32, c.close, true);\n view.setFloat64(off + 40, c.volume, true);\n }\n return buf;\n}\n\n/** Decode the packed buffer back to candles (inverse of packCandles). */\nexport function unpackCandles(buf: ArrayBuffer): Candle[] {\n const view = new DataView(buf);\n const n = Math.floor(buf.byteLength / BYTES_PER_CANDLE);\n const out: Candle[] = new Array(n);\n for (let i = 0; i < n; i++) {\n const off = i * BYTES_PER_CANDLE;\n out[i] = {\n timeMs: Number(view.getBigInt64(off, true)),\n open: view.getFloat64(off + 8, true),\n high: view.getFloat64(off + 16, true),\n low: view.getFloat64(off + 24, true),\n close: view.getFloat64(off + 32, true),\n volume: view.getFloat64(off + 40, true),\n };\n }\n return out;\n}\n","import type { VroomColor, VroomTheme } from '@vroomchart/types';\nimport { ColorKey, type VroomChartHandle } from './handle';\n\n// Maps each VroomTheme field to its ColorKey. Mirror of\n// packages/react-native/src/theme.ts (which keys off the same C enum).\nexport const COLOR_KEYS: Record<keyof VroomTheme, ColorKey> = {\n background: ColorKey.Background,\n bull: ColorKey.Bull,\n bear: ColorKey.Bear,\n grid: ColorKey.Grid,\n axisText: ColorKey.AxisText,\n crosshair: ColorKey.Crosshair,\n crosshairTarget: ColorKey.CrosshairTarget,\n};\n\n/**\n * Parse a color into a packed 0xAARRGGBB integer (Skia's ARGB order).\n * - number → taken as already-packed ARGB\n * - 6-digit hex → opaque (alpha forced to ff)\n * - 8-digit hex → interpreted as AARRGGBB\n * Returns null for anything malformed so the caller can skip it.\n */\nexport function parseColor(value: VroomColor): number | null {\n if (typeof value === 'number') {\n return Number.isFinite(value) ? value >>> 0 : null;\n }\n let s = value.trim();\n if (s.startsWith('#')) s = s.slice(1);\n if (s.length === 6) s = `ff${s}`;\n if (s.length !== 8 || !/^[0-9a-fA-F]{8}$/.test(s)) return null;\n return parseInt(s, 16) >>> 0;\n}\n\n/** Push every provided theme color into the handle; skip unparseable ones. */\nexport function applyTheme(handle: VroomChartHandle, theme: VroomTheme): void {\n (Object.keys(COLOR_KEYS) as (keyof VroomTheme)[]).forEach((field) => {\n const value = theme[field];\n if (value == null) return;\n const argb = parseColor(value);\n if (argb == null) return;\n handle.setColor(COLOR_KEYS[field], argb);\n });\n}\n\n/** Split a packed 0xAARRGGBB integer into a CSS `rgba(...)` string. */\nexport function argbToCss(argb: number): string {\n const a = ((argb >>> 24) & 0xff) / 255;\n const r = (argb >>> 16) & 0xff;\n const g = (argb >>> 8) & 0xff;\n const b = argb & 0xff;\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n}\n","// @vroomchart/core-wasm — the framework-agnostic web core for the vroom chart.\n//\n// Exposes loadVroom(): Promise<VroomModule>, which yields a factory for chart\n// instances bound to a <canvas>. It resolves to the Skia-WASM core bundled in\n// this package; pass `wasm` to load it from your own URLs instead.\n\nimport type { VroomModule } from './handle';\nimport { loadWasmCore, type WasmConfig } from './wasm/loadWasm';\n\n// URLs of the WASM assets bundled in this package. Defined here (one level under\n// the package root, matching the built dist/index.js) so the relative paths\n// resolve correctly both from source in dev and from the bundle in production.\n// Static `new URL(..., import.meta.url)` so bundlers (Vite, webpack 5, …) emit\n// and rewrite them automatically — no asset hosting needed by consumers.\nfunction bundledWasmConfig(): WasmConfig {\n return {\n moduleUrl: new URL('../assets/vroom_core.mjs', import.meta.url).href,\n wasmUrl: new URL('../assets/vroom_core.wasm', import.meta.url).href,\n fontUrl: new URL('../assets/VroomSans-Regular.ttf', import.meta.url).href,\n };\n}\n\nexport type {\n VroomModule,\n VroomChartHandle,\n AxisMetrics,\n CrosshairCandle,\n CrosshairInfo,\n OverlaySpec,\n} from './handle';\nexport { ColorKey } from './handle';\nexport { packCandles, unpackCandles, BYTES_PER_CANDLE } from './packCandles';\nexport { parseColor, applyTheme, argbToCss, COLOR_KEYS } from './color';\nexport type { WasmConfig } from './wasm/loadWasm';\n\n/** Options for loading the core. */\nexport type LoadVroomOptions = {\n /**\n * Override where the Skia-WASM core is loaded from. By default the core uses\n * the WASM build bundled in this package (turnkey, no asset hosting needed);\n * pass `wasm` to load it from your own URLs instead.\n */\n wasm?: WasmConfig;\n};\n\nlet modulePromise: Promise<VroomModule> | null = null;\n\n/**\n * Load the chart core. Cached: repeated calls share one module instance.\n *\n * Resolves to the Skia-WASM core bundled in this package, or the core at the\n * URLs in `opts.wasm`. The returned promise rejects if the WASM module fails to\n * load (and the cache is cleared so a later call can retry).\n */\nexport function loadVroom(opts?: LoadVroomOptions): Promise<VroomModule> {\n if (modulePromise) return modulePromise;\n\n const cfg = opts?.wasm ?? bundledWasmConfig();\n modulePromise = loadWasmCore(cfg).catch((err) => {\n modulePromise = null; // allow a retry on the next call\n throw err;\n });\n return modulePromise;\n}\n\n/** Test/HMR helper: drop the cached module so the next loadVroom() re-creates it. */\nexport function resetVroomForTesting(): void {\n modulePromise = null;\n}\n"],"mappings":"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","names":["ColorKey"]}
|
|
1
|
+
{"version":3,"sources":["../src/wasm/adapter.ts","../src/wasm/loadWasm.ts","../src/handle.ts","../src/packCandles.ts","../src/color.ts","../src/index.ts"],"sourcesContent":["// Adapts the emscripten `WebChart` (embind class from web/vroom_web.cpp) to the\n// VroomChartHandle / VroomModule contract that @vroomchart/react consumes. This is\n// the only place that knows the WASM module's shape — swapping the stub for the\n// real core changes nothing above this line.\n\nimport type {\n AxisMetrics,\n CrosshairCandle,\n CrosshairInfo,\n OverlaySpec,\n VroomChartHandle,\n VroomModule,\n} from '../handle';\n\n// The embind class instance. Methods mirror web/vroom_web.cpp; `delete()` frees\n// the C++ object (embind requires explicit disposal).\ninterface WebChartInstance {\n setCandles(bytes: Uint8Array): void;\n setSize(width: number, height: number, dpr: number): void;\n setColor(key: number, argb: number): void;\n setVisibleRange(startMs: number, endMs: number): void;\n pan(dx: number, dy: number): void;\n translate(dx: number, dy: number): void;\n zoom(sx: number, sy: number, fx: number, fy: number): void;\n scalePriceAxis(dy: number): void;\n scaleTimeAxis(dx: number): void;\n resizeIndicatorPane(dy: number): void;\n scaleIndicatorAxis(y: number, dy: number): void;\n getAxisMetrics(): AxisMetrics;\n setCrosshair(x: number, y: number): void;\n clearCrosshair(): void;\n getCrosshairCandle(): CrosshairCandle | null;\n getCrosshairInfo(): CrosshairInfo | null;\n setRSI(e: boolean, p: number, u: number, l: number, mae: boolean, map: number): void;\n setMACD(e: boolean, f: number, s: number, sig: number): void;\n setOverlays(overlays: OverlaySpec[]): void;\n setVWAP(e: boolean, reset: number, color: number, width: number): void;\n setTypeface(bytes: Uint8Array): void;\n isAnimating(): boolean;\n present(): void;\n delete(): void;\n}\n\n/** The shape of the instantiated emscripten module we depend on. */\nexport interface VroomWasmModule {\n WebChart: new (canvasSelector: string) => WebChartInstance;\n}\n\nlet uid = 0;\n\nclass WasmHandle implements VroomChartHandle {\n constructor(private wc: WebChartInstance) {}\n\n setCandles(packed: ArrayBuffer): void {\n this.wc.setCandles(new Uint8Array(packed));\n }\n setSize(width: number, height: number, dpr: number): void {\n this.wc.setSize(width, height, dpr);\n }\n setColor(key: number, argb: number): void {\n this.wc.setColor(key, argb >>> 0);\n }\n setVisibleRange(startMs: number, endMs: number): void {\n this.wc.setVisibleRange(startMs, endMs);\n }\n pan(dx: number, dy: number): void {\n this.wc.pan(dx, dy);\n }\n translate(dx: number, dy: number): void {\n this.wc.translate(dx, dy);\n }\n zoom(scaleX: number, scaleY: number, fx: number, fy: number): void {\n this.wc.zoom(scaleX, scaleY, fx, fy);\n }\n scalePriceAxis(dy: number): void {\n this.wc.scalePriceAxis(dy);\n }\n scaleTimeAxis(dx: number): void {\n this.wc.scaleTimeAxis(dx);\n }\n resizeIndicatorPane(dy: number): void {\n this.wc.resizeIndicatorPane(dy);\n }\n scaleIndicatorAxis(y: number, dy: number): void {\n this.wc.scaleIndicatorAxis(y, dy);\n }\n getAxisMetrics(): AxisMetrics {\n return this.wc.getAxisMetrics();\n }\n setCrosshair(x: number, y: number): void {\n this.wc.setCrosshair(x, y);\n }\n clearCrosshair(): void {\n this.wc.clearCrosshair();\n }\n getCrosshairCandle(): CrosshairCandle | null {\n return this.wc.getCrosshairCandle();\n }\n getCrosshairInfo(): CrosshairInfo | null {\n return this.wc.getCrosshairInfo();\n }\n setRSI(\n enabled: boolean,\n period: number,\n upperBand: number,\n lowerBand: number,\n maEnabled: boolean,\n maPeriod: number,\n ): void {\n this.wc.setRSI(enabled, period, upperBand, lowerBand, maEnabled, maPeriod);\n }\n setMACD(enabled: boolean, fast: number, slow: number, signal: number): void {\n this.wc.setMACD(enabled, fast, slow, signal);\n }\n setOverlays(overlays: OverlaySpec[]): void {\n this.wc.setOverlays(overlays);\n }\n setVWAP(enabled: boolean, resetOffsetMin: number, color: number, width: number): void {\n this.wc.setVWAP(enabled, resetOffsetMin, color >>> 0, width);\n }\n isAnimating(): boolean {\n return this.wc.isAnimating();\n }\n present(): void {\n this.wc.present();\n }\n destroy(): void {\n this.wc.delete();\n }\n}\n\n/**\n * Wrap an instantiated WASM module as a VroomModule. `fontBytes` (a .ttf/.otf)\n * is installed as the axis typeface on each chart — required for text labels,\n * since the WASM sandbox has no system fonts.\n */\nexport function makeWasmModule(\n module: VroomWasmModule,\n fontBytes: Uint8Array | null,\n): VroomModule {\n return {\n create(canvas: HTMLCanvasElement) {\n if (!canvas.id) canvas.id = `vroom-canvas-${uid++}`;\n const wc = new module.WebChart(`#${canvas.id}`);\n if (fontBytes) wc.setTypeface(fontBytes);\n return new WasmHandle(wc);\n },\n };\n}\n","// Instantiates the Skia-WASM core (the emscripten ES module built from\n// packages/core/web + CMake VROOM_WASM) and adapts it to a VroomModule.\n//\n// The module URL is resolved at runtime (not statically imported) so bundlers\n// don't try to resolve a not-yet-built artifact — keeping the stub-only build\n// green until the WASM file actually exists.\n\nimport type { VroomModule } from '../handle';\nimport { makeWasmModule, type VroomWasmModule } from './adapter';\n\nexport type WasmConfig = {\n /** URL to the emscripten ES module (vroom_core.mjs). */\n moduleUrl: string;\n /** URL to vroom_core.wasm. Defaults to resolving next to the module. */\n wasmUrl?: string;\n /** URL to a .ttf/.otf used for axis/price/time labels. */\n fontUrl?: string;\n};\n\n// The emscripten MODULARIZE/EXPORT_ES6 default export is a factory:\n// createVroomCore(moduleOverrides) => Promise<Module>\ntype EmscriptenFactory = (overrides?: Record<string, unknown>) => Promise<VroomWasmModule>;\n\n// Import the emscripten ES module from a runtime URL WITHOUT the bundler trying\n// to resolve/transform it. A plain import(url) (even with /* @vite-ignore */) is\n// still intercepted by Vite's \"don't import from /public\" guard and by other\n// bundlers' static analysis. Routing through Function keeps it fully opaque.\n// (Requires 'unsafe-eval' under a strict CSP — self-host the module differently\n// if that's a constraint.)\nconst dynamicImport = new Function('u', 'return import(u)') as (\n u: string,\n) => Promise<{ default: EmscriptenFactory }>;\n\nexport async function loadWasmCore(cfg: WasmConfig): Promise<VroomModule> {\n const mod = await dynamicImport(cfg.moduleUrl);\n const instance = await mod.default({\n locateFile: (path: string) =>\n path.endsWith('.wasm') && cfg.wasmUrl ? cfg.wasmUrl : path,\n });\n\n let fontBytes: Uint8Array | null = null;\n if (cfg.fontUrl) {\n try {\n const res = await fetch(cfg.fontUrl);\n if (res.ok) {\n fontBytes = new Uint8Array(await res.arrayBuffer());\n console.info(`[vroom] axis font loaded (${cfg.fontUrl}, ${fontBytes.length} bytes)`);\n } else {\n console.warn(`[vroom] axis font fetch ${cfg.fontUrl} → HTTP ${res.status}; labels will be blank.`);\n }\n } catch (e) {\n console.warn(`[vroom] axis font fetch failed (${cfg.fontUrl}); labels will be blank.`, e);\n }\n } else {\n console.warn('[vroom] no fontUrl provided; text labels will be blank.');\n }\n\n return makeWasmModule(instance, fontBytes);\n}\n","// The low-level contract for one chart instance on the web.\n//\n// This mirrors the C facade (packages/core/include/vroom/vroom_chart.h) and the\n// native JSI handle (packages/react-native/src/jsi.d.ts) — but WITHOUT the\n// SkPicture return values. On web the core owns the <canvas> and its rendering\n// surface, so mutations are `void` and `present()` paints. Backed by the\n// Skia-WASM module (./wasm).\n\n/** Theme color slots — mirrors the `VroomColorKey` enum in the C facade. */\nexport enum ColorKey {\n Background = 0,\n Bull = 1,\n Bear = 2,\n Wick = 3,\n Grid = 4,\n AxisText = 5,\n Crosshair = 6,\n TooltipBg = 7,\n TooltipText = 8,\n CrosshairTarget = 9,\n BorderBull = 10,\n BorderBear = 11,\n WickBull = 12,\n WickBear = 13,\n AccentBull = 14,\n AccentBear = 15,\n}\n\n/** OHLCV readout for the candle under the crosshair. */\nexport type CrosshairCandle = {\n timeMs: number;\n open: number;\n high: number;\n low: number;\n close: number;\n volume: number;\n};\n\n/**\n * What the crosshair currently snaps to. `timeMs` is the snapped slot's\n * period-start time — set even in the empty space ahead of the most recent\n * candle. `candle` holds the OHLCV when a real candle sits at that slot, and is\n * null when the crosshair is parked on a future candle-aligned slot.\n */\nexport type CrosshairInfo = {\n timeMs: number;\n candle: CrosshairCandle | null;\n};\n\n/** Axis region sizes in CSS px, for hit-testing gestures on the JS side. */\nexport type AxisMetrics = {\n yAxisWidth: number;\n xAxisHeight: number;\n /** Below-chart indicator pane height; 0 when no pane is shown. */\n indicatorHeight: number;\n};\n\n/** A moving-average overlay line, in the core's numeric encoding. */\nexport type OverlaySpec = {\n /** 0 = SMA, 1 = EMA. */\n kind: number;\n period: number;\n /** 0=close,1=open,2=high,3=low,4=hl2,5=hlc3,6=ohlc4 */\n source: number;\n /** Packed 0xAARRGGBB. */\n color: number;\n /** Stroke width in px. */\n width: number;\n};\n\n/**\n * A single chart instance bound to a canvas. Method semantics match the native\n * handle one-to-one (see jsi.d.ts) so the two platforms behave identically.\n */\nexport interface VroomChartHandle {\n /** Replace the full candle series (packed buffer; see packCandles). */\n setCandles(packed: ArrayBuffer): void;\n /** Set the drawing size in CSS px plus the device pixel ratio. */\n setSize(width: number, height: number, dpr: number): void;\n /** Override one theme color. `argb` is packed 0xAARRGGBB. */\n setColor(key: ColorKey | number, argb: number): void;\n /** Pass 0, 0 to show all candles. */\n setVisibleRange(startMs: number, endMs: number): void;\n\n /** Shift the visible range by dx/dy CSS px. */\n pan(dx: number, dy: number): void;\n /** Shift the time window and price bounds without rescaling. */\n translate(dx: number, dy: number): void;\n /** Directional zoom by per-axis factors around focus (fx, fy) in px. */\n zoom(scaleX: number, scaleY: number, fx: number, fy: number): void;\n /** Drag-on-y-axis price scaling; dy>0 widens the price range. */\n scalePriceAxis(dy: number): void;\n /** Drag-on-x-axis time scaling; dx>0 widens the time window. */\n scaleTimeAxis(dx: number): void;\n /**\n * Drag the separator between the price pane and the below-chart indicator\n * band. dy>0 (drag down) grows the price pane and shrinks the indicator band,\n * preserving candle pixel scale (the price range adjusts instead).\n */\n resizeIndicatorPane(dy: number): void;\n /**\n * Scale the y-axis of the below-chart indicator pane containing `y` (px).\n * dy>0 (drag down) zooms out (widens the value range); dy<0 zooms in. RSI\n * scales about 50, MACD about its zero line. No-op when `y` is not over a pane.\n */\n scaleIndicatorAxis(y: number, dy: number): void;\n\n /** Current axis dimensions for hit-testing in JS gestures. */\n getAxisMetrics(): AxisMetrics;\n\n /** Show the crosshair at (x, y) CSS px (y already lifted above the pointer). */\n setCrosshair(x: number, y: number): void;\n /** Hide the crosshair. */\n clearCrosshair(): void;\n /** OHLCV of the candle the crosshair snaps to, or null when inactive. */\n getCrosshairCandle(): CrosshairCandle | null;\n /**\n * The slot the crosshair snaps to (real candle or future candle-aligned\n * slot), or null when inactive. Unlike getCrosshairCandle this reports a\n * timeMs even in the empty space ahead of the most recent candle.\n */\n getCrosshairInfo(): CrosshairInfo | null;\n\n setRSI(\n enabled: boolean,\n period: number,\n upperBand: number,\n lowerBand: number,\n maEnabled: boolean,\n maPeriod: number,\n ): void;\n setMACD(enabled: boolean, fast: number, slow: number, signal: number): void;\n setOverlays(overlays: OverlaySpec[]): void;\n setVWAP(\n enabled: boolean,\n resetOffsetMin: number,\n color: number,\n width: number,\n ): void;\n\n /** True while any axis-label fade is in progress (drives the rAF loop). */\n isAnimating(): boolean;\n /** Paint the current state to the canvas. */\n present(): void;\n /** Release resources; the handle is unusable afterward. */\n destroy(): void;\n}\n\n/** Factory for chart instances. The loaded core module implements this. */\nexport interface VroomModule {\n /** Create a chart bound to `canvas`, attaching its rendering surface. */\n create(canvas: HTMLCanvasElement): VroomChartHandle;\n}\n","import type { Candle } from '@vroomchart/types';\n\n// Wire format must match `VroomCandle` in packages/core/include/vroom/vroom_chart.h:\n// int64_t time_ms; double open, high, low, close, volume;\n// = 48 bytes per candle, 8-byte aligned, little-endian.\n//\n// This is a deliberate mirror of packages/react-native/src/packCandles.ts so\n// the web side speaks the same binary ABI the Skia-WASM core will decode. Keep\n// the two in sync with the C struct.\nexport const BYTES_PER_CANDLE = 48;\n\n/** Serialize candles into the packed little-endian buffer the core expects. */\nexport function packCandles(candles: Candle[]): ArrayBuffer {\n const buf = new ArrayBuffer(candles.length * BYTES_PER_CANDLE);\n const view = new DataView(buf);\n for (let i = 0; i < candles.length; i++) {\n const c = candles[i]!;\n const off = i * BYTES_PER_CANDLE;\n view.setBigInt64(off, BigInt(Math.trunc(c.timeMs)), true);\n view.setFloat64(off + 8, c.open, true);\n view.setFloat64(off + 16, c.high, true);\n view.setFloat64(off + 24, c.low, true);\n view.setFloat64(off + 32, c.close, true);\n view.setFloat64(off + 40, c.volume, true);\n }\n return buf;\n}\n\n/** Decode the packed buffer back to candles (inverse of packCandles). */\nexport function unpackCandles(buf: ArrayBuffer): Candle[] {\n const view = new DataView(buf);\n const n = Math.floor(buf.byteLength / BYTES_PER_CANDLE);\n const out: Candle[] = new Array(n);\n for (let i = 0; i < n; i++) {\n const off = i * BYTES_PER_CANDLE;\n out[i] = {\n timeMs: Number(view.getBigInt64(off, true)),\n open: view.getFloat64(off + 8, true),\n high: view.getFloat64(off + 16, true),\n low: view.getFloat64(off + 24, true),\n close: view.getFloat64(off + 32, true),\n volume: view.getFloat64(off + 40, true),\n };\n }\n return out;\n}\n","import type { VroomColor, VroomTheme } from '@vroomchart/types';\nimport { ColorKey, type VroomChartHandle } from './handle';\n\n// Maps each VroomTheme field to its ColorKey. Mirror of\n// packages/react-native/src/theme.ts (which keys off the same C enum).\nexport const COLOR_KEYS: Record<keyof VroomTheme, ColorKey> = {\n background: ColorKey.Background,\n bull: ColorKey.Bull,\n bear: ColorKey.Bear,\n grid: ColorKey.Grid,\n axisText: ColorKey.AxisText,\n crosshair: ColorKey.Crosshair,\n crosshairTarget: ColorKey.CrosshairTarget,\n borderBull: ColorKey.BorderBull,\n borderBear: ColorKey.BorderBear,\n wickBull: ColorKey.WickBull,\n wickBear: ColorKey.WickBear,\n accentBull: ColorKey.AccentBull,\n accentBear: ColorKey.AccentBear,\n};\n\n/**\n * Parse a color into a packed 0xAARRGGBB integer (Skia's ARGB order).\n * - number → taken as already-packed ARGB\n * - 6-digit hex → opaque (alpha forced to ff)\n * - 8-digit hex → interpreted as AARRGGBB\n * Returns null for anything malformed so the caller can skip it.\n */\nexport function parseColor(value: VroomColor): number | null {\n if (typeof value === 'number') {\n return Number.isFinite(value) ? value >>> 0 : null;\n }\n let s = value.trim();\n if (s.startsWith('#')) s = s.slice(1);\n if (s.length === 6) s = `ff${s}`;\n if (s.length !== 8 || !/^[0-9a-fA-F]{8}$/.test(s)) return null;\n return parseInt(s, 16) >>> 0;\n}\n\n/** Push every provided theme color into the handle; skip unparseable ones. */\nexport function applyTheme(handle: VroomChartHandle, theme: VroomTheme): void {\n (Object.keys(COLOR_KEYS) as (keyof VroomTheme)[]).forEach((field) => {\n const value = theme[field];\n if (value == null) return;\n const argb = parseColor(value);\n if (argb == null) return;\n handle.setColor(COLOR_KEYS[field], argb);\n });\n}\n\n/** Split a packed 0xAARRGGBB integer into a CSS `rgba(...)` string. */\nexport function argbToCss(argb: number): string {\n const a = ((argb >>> 24) & 0xff) / 255;\n const r = (argb >>> 16) & 0xff;\n const g = (argb >>> 8) & 0xff;\n const b = argb & 0xff;\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n}\n","// @vroomchart/core-wasm — the framework-agnostic web core for the vroom chart.\n//\n// Exposes loadVroom(): Promise<VroomModule>, which yields a factory for chart\n// instances bound to a <canvas>. It resolves to the Skia-WASM core bundled in\n// this package; pass `wasm` to load it from your own URLs instead.\n\nimport type { VroomModule } from './handle';\nimport { loadWasmCore, type WasmConfig } from './wasm/loadWasm';\n\n// URLs of the WASM assets bundled in this package. Defined here (one level under\n// the package root, matching the built dist/index.js) so the relative paths\n// resolve correctly both from source in dev and from the bundle in production.\n// Static `new URL(..., import.meta.url)` so bundlers (Vite, webpack 5, …) emit\n// and rewrite them automatically — no asset hosting needed by consumers.\nfunction bundledWasmConfig(): WasmConfig {\n return {\n moduleUrl: new URL('../assets/vroom_core.mjs', import.meta.url).href,\n wasmUrl: new URL('../assets/vroom_core.wasm', import.meta.url).href,\n fontUrl: new URL('../assets/VroomSans-Regular.ttf', import.meta.url).href,\n };\n}\n\nexport type {\n VroomModule,\n VroomChartHandle,\n AxisMetrics,\n CrosshairCandle,\n CrosshairInfo,\n OverlaySpec,\n} from './handle';\nexport { ColorKey } from './handle';\nexport { packCandles, unpackCandles, BYTES_PER_CANDLE } from './packCandles';\nexport { parseColor, applyTheme, argbToCss, COLOR_KEYS } from './color';\nexport type { WasmConfig } from './wasm/loadWasm';\n\n/** Options for loading the core. */\nexport type LoadVroomOptions = {\n /**\n * Override where the Skia-WASM core is loaded from. By default the core uses\n * the WASM build bundled in this package (turnkey, no asset hosting needed);\n * pass `wasm` to load it from your own URLs instead.\n */\n wasm?: WasmConfig;\n};\n\nlet modulePromise: Promise<VroomModule> | null = null;\n\n/**\n * Load the chart core. Cached: repeated calls share one module instance.\n *\n * Resolves to the Skia-WASM core bundled in this package, or the core at the\n * URLs in `opts.wasm`. The returned promise rejects if the WASM module fails to\n * load (and the cache is cleared so a later call can retry).\n */\nexport function loadVroom(opts?: LoadVroomOptions): Promise<VroomModule> {\n if (modulePromise) return modulePromise;\n\n const cfg = opts?.wasm ?? bundledWasmConfig();\n modulePromise = loadWasmCore(cfg).catch((err) => {\n modulePromise = null; // allow a retry on the next call\n throw err;\n });\n return modulePromise;\n}\n\n/** Test/HMR helper: drop the cached module so the next loadVroom() re-creates it. */\nexport function resetVroomForTesting(): void {\n modulePromise = null;\n}\n"],"mappings":"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","names":["ColorKey"]}
|
package/package.json
CHANGED