@vroomchart/core-wasm 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/README.md +56 -1
- package/assets/vroom_core.mjs +1 -1
- package/assets/vroom_core.wasm +0 -0
- package/dist/index.d.ts +104 -13
- package/dist/index.js +56 -9
- 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
|
@@ -15,7 +15,8 @@ declare enum ColorKey {
|
|
|
15
15
|
WickBull = 12,
|
|
16
16
|
WickBear = 13,
|
|
17
17
|
AccentBull = 14,
|
|
18
|
-
AccentBear = 15
|
|
18
|
+
AccentBear = 15,
|
|
19
|
+
Line = 16
|
|
19
20
|
}
|
|
20
21
|
/** Theme float slots — mirrors the `VroomFloatKey` enum in the C facade. */
|
|
21
22
|
declare enum FloatKey {
|
|
@@ -29,7 +30,8 @@ declare enum FloatKey {
|
|
|
29
30
|
IndicatorHeightFrac = 7,
|
|
30
31
|
CandleRadius = 8,
|
|
31
32
|
WickRoundCap = 9,
|
|
32
|
-
VolumeRadius = 10
|
|
33
|
+
VolumeRadius = 10,
|
|
34
|
+
LineWidth = 11
|
|
33
35
|
}
|
|
34
36
|
/** OHLCV readout for the candle under the crosshair. */
|
|
35
37
|
type CrosshairCandle = {
|
|
@@ -84,6 +86,19 @@ type DrawingSpec = {
|
|
|
84
86
|
color: number;
|
|
85
87
|
/** Stroke width in px. */
|
|
86
88
|
width: number;
|
|
89
|
+
/**
|
|
90
|
+
* Geometry: 0 = line (a→b), 1 = box (a and b are opposite corners),
|
|
91
|
+
* 2 = pencil (a freehand path — see `points`).
|
|
92
|
+
*/
|
|
93
|
+
kind: number;
|
|
94
|
+
/**
|
|
95
|
+
* The full path for a pencil stroke (kind 2), in draw order. Ignored for line
|
|
96
|
+
* and box, which are fully described by the a/b anchors.
|
|
97
|
+
*/
|
|
98
|
+
points?: {
|
|
99
|
+
timeMs: number;
|
|
100
|
+
price: number;
|
|
101
|
+
}[];
|
|
87
102
|
};
|
|
88
103
|
/** A single resting-liquidity band in the core's numeric encoding. */
|
|
89
104
|
type BandSpec = {
|
|
@@ -135,6 +150,14 @@ interface VroomChartHandle {
|
|
|
135
150
|
* Affects initial framing only; an explicit setVisibleRange takes precedence.
|
|
136
151
|
*/
|
|
137
152
|
setDefaultCandleWidth(px: number): void;
|
|
153
|
+
/** Render mode: 0 = candlesticks (default), 1 = line chart (close polyline). */
|
|
154
|
+
setChartType(mode: number): void;
|
|
155
|
+
/**
|
|
156
|
+
* Candle↔line morph blend for animated transitions. `collapse` folds candles
|
|
157
|
+
* toward their close; `fade` crossfades candles→line. Both 0 = candles,
|
|
158
|
+
* 1 = line. Driven per-frame by the host; setChartType snaps both.
|
|
159
|
+
*/
|
|
160
|
+
setMorph(collapse: number, fade: number): void;
|
|
138
161
|
/** The current visible time window. {startMs: 0, endMs: 0} = uninitialized. */
|
|
139
162
|
getVisibleRange(): {
|
|
140
163
|
startMs: number;
|
|
@@ -209,19 +232,65 @@ interface VroomChartHandle {
|
|
|
209
232
|
*/
|
|
210
233
|
setLiquidity(liquidity: LiquiditySpec): void;
|
|
211
234
|
/**
|
|
212
|
-
* Set the transient in-progress draft shown while placing a
|
|
213
|
-
* always shown; node B is shown when `hasB`. `guide` also draws the
|
|
214
|
-
*
|
|
215
|
-
*
|
|
235
|
+
* Set the transient in-progress draft shown while placing a drawing. Node A is
|
|
236
|
+
* always shown; node B is shown when `hasB`. `guide` also draws the live
|
|
237
|
+
* preview (a guideline for a line, a preview rectangle for a box); when false
|
|
238
|
+
* only the node dots show (the committed shape renders via setDrawings).
|
|
239
|
+
* `kind` (0 = line, 1 = box) selects the preview geometry. `color`/`width`
|
|
240
|
+
* style the preview.
|
|
241
|
+
*/
|
|
242
|
+
setDraft(aTime: number, aPrice: number, hasB: boolean, bTime: number, bPrice: number, guide: boolean, color: number, width: number, kind: number): void;
|
|
243
|
+
/**
|
|
244
|
+
* Begin a freehand (pencil) draft stroke, clearing any previous draft. Follow
|
|
245
|
+
* with `appendDraftPoint` per captured sample; the growing path renders live.
|
|
216
246
|
*/
|
|
217
|
-
|
|
218
|
-
/**
|
|
247
|
+
startDraftStroke(color: number, width: number): void;
|
|
248
|
+
/**
|
|
249
|
+
* Append one point to the in-progress freehand draft. Cheap enough to call on
|
|
250
|
+
* every pointer move — the whole path is never restated.
|
|
251
|
+
*/
|
|
252
|
+
appendDraftPoint(timeMs: number, price: number): void;
|
|
253
|
+
/** Clear the draft (hide in-progress node dots / guideline / stroke). */
|
|
219
254
|
clearDraft(): void;
|
|
255
|
+
/**
|
|
256
|
+
* Shift a whole committed drawing by a *relative* data-space delta — both
|
|
257
|
+
* anchors and, for a pencil, every path point. Used for live body dragging.
|
|
258
|
+
*/
|
|
259
|
+
translateDrawing(index: number, dTimeMs: number, dPrice: number): void;
|
|
260
|
+
/**
|
|
261
|
+
* Select a committed drawing (renders its endpoint handles). `index` -1
|
|
262
|
+
* clears the selection; `grabbedEndpoint` 0/1 renders that handle 50% larger
|
|
263
|
+
* while dragging (-1 for none).
|
|
264
|
+
*/
|
|
265
|
+
setSelectedDrawing(index: number, grabbedEndpoint: number): void;
|
|
266
|
+
/** Move one endpoint (0=A, 1=B) of a committed drawing to a new data anchor. */
|
|
267
|
+
moveDrawingEndpoint(index: number, endpoint: number, timeMs: number, price: number): void;
|
|
268
|
+
/**
|
|
269
|
+
* Hit-test pixel (x, y) against the committed drawings. Returns the drawing
|
|
270
|
+
* `index`, `part`, and `t`, or null on a miss. For a line, `part` is 0
|
|
271
|
+
* (endpoint A), 1 (endpoint B), or 2 (body) and `t` is the 0..1 grab position
|
|
272
|
+
* along the segment. For a box, `part` is 0..3 (the four corners) or 4 (body)
|
|
273
|
+
* and `t` is 0. Corner/endpoint hits are only reported for the selected
|
|
274
|
+
* drawing.
|
|
275
|
+
*/
|
|
276
|
+
hitTestDrawing(x: number, y: number): {
|
|
277
|
+
index: number;
|
|
278
|
+
part: number;
|
|
279
|
+
t: number;
|
|
280
|
+
} | null;
|
|
220
281
|
/**
|
|
221
282
|
* The continuous data coordinate at pixel (x, y) — not snapped to a candle
|
|
222
283
|
* slot. Null when there are no candles or the viewport is degenerate.
|
|
223
284
|
*/
|
|
224
285
|
coordAt(x: number, y: number): Coord | null;
|
|
286
|
+
/**
|
|
287
|
+
* Pixel position of a data coordinate — the inverse of coordAt. Null when
|
|
288
|
+
* there are no candles or the viewport is degenerate.
|
|
289
|
+
*/
|
|
290
|
+
project(timeMs: number, price: number): {
|
|
291
|
+
x: number;
|
|
292
|
+
y: number;
|
|
293
|
+
} | null;
|
|
225
294
|
/** True while any axis-label fade is in progress (drives the rAF loop). */
|
|
226
295
|
isAnimating(): boolean;
|
|
227
296
|
/** Paint the current state to the canvas. */
|
|
@@ -236,12 +305,29 @@ interface VroomModule {
|
|
|
236
305
|
}
|
|
237
306
|
|
|
238
307
|
type WasmConfig = {
|
|
239
|
-
/**
|
|
240
|
-
|
|
308
|
+
/**
|
|
309
|
+
* URL to the emscripten ES module (vroom_core.mjs). Omit to use the build
|
|
310
|
+
* bundled in this package — in which case `wasmUrl` and `fontUrl` default to
|
|
311
|
+
* the bundled assets too.
|
|
312
|
+
*/
|
|
313
|
+
moduleUrl?: string;
|
|
241
314
|
/** URL to vroom_core.wasm. Defaults to resolving next to the module. */
|
|
242
315
|
wasmUrl?: string;
|
|
243
316
|
/** URL to a .ttf/.otf used for axis/price/time labels. */
|
|
244
317
|
fontUrl?: string;
|
|
318
|
+
/**
|
|
319
|
+
* Custom loader for the emscripten ES module, if the default `import(url)`
|
|
320
|
+
* doesn't survive your bundler. Bundler magic comments only take effect in
|
|
321
|
+
* your own source, so this is the seam for supplying them:
|
|
322
|
+
*
|
|
323
|
+
* ```ts
|
|
324
|
+
* // webpack / Next.js
|
|
325
|
+
* importModule: (url) => import(/* webpackIgnore: true *\/ url)
|
|
326
|
+
* // Vite / Rollup
|
|
327
|
+
* importModule: (url) => import(/* @vite-ignore *\/ url)
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
importModule?: (url: string) => Promise<unknown>;
|
|
245
331
|
};
|
|
246
332
|
|
|
247
333
|
/** A single OHLCV bar. `candles` is an array of these. */
|
|
@@ -301,6 +387,10 @@ type VroomTheme = {
|
|
|
301
387
|
crosshair?: VroomColor;
|
|
302
388
|
/** Crosshair target — the hollow ring/dot at the intersection. */
|
|
303
389
|
crosshairTarget?: VroomColor;
|
|
390
|
+
/** Line-chart-mode close polyline color. Defaults to a neutral foreground. */
|
|
391
|
+
lineColor?: VroomColor;
|
|
392
|
+
/** Line-chart-mode polyline stroke width in px. Defaults to 1.5. */
|
|
393
|
+
lineWidth?: number;
|
|
304
394
|
};
|
|
305
395
|
|
|
306
396
|
declare const BYTES_PER_CANDLE = 48;
|
|
@@ -328,9 +418,10 @@ declare function argbToCss(argb: number): string;
|
|
|
328
418
|
/** Options for loading the core. */
|
|
329
419
|
type LoadVroomOptions = {
|
|
330
420
|
/**
|
|
331
|
-
* Override
|
|
332
|
-
*
|
|
333
|
-
*
|
|
421
|
+
* Override how the Skia-WASM core is loaded. By default it comes from the
|
|
422
|
+
* WASM build bundled in this package (turnkey, no asset hosting needed);
|
|
423
|
+
* set `moduleUrl` to self-host it, or `importModule` to keep the bundled
|
|
424
|
+
* assets but control the dynamic import your bundler emits.
|
|
334
425
|
*/
|
|
335
426
|
wasm?: WasmConfig;
|
|
336
427
|
};
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,12 @@ var WasmHandle = class {
|
|
|
26
26
|
setDefaultCandleWidth(px) {
|
|
27
27
|
this.wc.setDefaultCandleWidth(px);
|
|
28
28
|
}
|
|
29
|
+
setChartType(mode) {
|
|
30
|
+
this.wc.setChartType(mode);
|
|
31
|
+
}
|
|
32
|
+
setMorph(collapse, fade) {
|
|
33
|
+
this.wc.setMorph(collapse, fade);
|
|
34
|
+
}
|
|
29
35
|
getVisibleRange() {
|
|
30
36
|
return this.wc.getVisibleRange();
|
|
31
37
|
}
|
|
@@ -98,15 +104,36 @@ var WasmHandle = class {
|
|
|
98
104
|
sellColor: liquidity.sellColor >>> 0
|
|
99
105
|
});
|
|
100
106
|
}
|
|
101
|
-
setDraft(aTime, aPrice, hasB, bTime, bPrice, guide, color, width) {
|
|
102
|
-
this.wc.setDraft(aTime, aPrice, hasB, bTime, bPrice, guide, color >>> 0, width);
|
|
107
|
+
setDraft(aTime, aPrice, hasB, bTime, bPrice, guide, color, width, kind) {
|
|
108
|
+
this.wc.setDraft(aTime, aPrice, hasB, bTime, bPrice, guide, color >>> 0, width, kind);
|
|
109
|
+
}
|
|
110
|
+
startDraftStroke(color, width) {
|
|
111
|
+
this.wc.startDraftStroke(color >>> 0, width);
|
|
112
|
+
}
|
|
113
|
+
appendDraftPoint(timeMs, price) {
|
|
114
|
+
this.wc.appendDraftPoint(timeMs, price);
|
|
103
115
|
}
|
|
104
116
|
clearDraft() {
|
|
105
117
|
this.wc.clearDraft();
|
|
106
118
|
}
|
|
119
|
+
setSelectedDrawing(index, grabbedEndpoint) {
|
|
120
|
+
this.wc.setSelectedDrawing(index, grabbedEndpoint);
|
|
121
|
+
}
|
|
122
|
+
moveDrawingEndpoint(index, endpoint, timeMs, price) {
|
|
123
|
+
this.wc.moveDrawingEndpoint(index, endpoint, timeMs, price);
|
|
124
|
+
}
|
|
125
|
+
translateDrawing(index, dTimeMs, dPrice) {
|
|
126
|
+
this.wc.translateDrawing(index, dTimeMs, dPrice);
|
|
127
|
+
}
|
|
128
|
+
hitTestDrawing(x, y) {
|
|
129
|
+
return this.wc.hitTestDrawing(x, y);
|
|
130
|
+
}
|
|
107
131
|
coordAt(x, y) {
|
|
108
132
|
return this.wc.coordAt(x, y);
|
|
109
133
|
}
|
|
134
|
+
project(timeMs, price) {
|
|
135
|
+
return this.wc.project(timeMs, price);
|
|
136
|
+
}
|
|
110
137
|
isAnimating() {
|
|
111
138
|
return this.wc.isAnimating();
|
|
112
139
|
}
|
|
@@ -129,10 +156,23 @@ function makeWasmModule(module, fontBytes) {
|
|
|
129
156
|
}
|
|
130
157
|
|
|
131
158
|
// src/wasm/loadWasm.ts
|
|
132
|
-
var
|
|
159
|
+
var defaultImport = (url) => import(
|
|
160
|
+
/* webpackIgnore: true */
|
|
161
|
+
/* @vite-ignore */
|
|
162
|
+
url
|
|
163
|
+
);
|
|
133
164
|
async function loadWasmCore(cfg) {
|
|
134
|
-
const
|
|
135
|
-
|
|
165
|
+
const importModule = cfg.importModule ?? defaultImport;
|
|
166
|
+
let factory;
|
|
167
|
+
try {
|
|
168
|
+
factory = (await importModule(cfg.moduleUrl)).default;
|
|
169
|
+
} catch (e) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`[vroom] failed to import the WASM core from ${cfg.moduleUrl}. If your bundler rewrote or inlined the URL, pass a custom \`wasm.importModule\` (see @vroomchart/core-wasm README \u2192 Bundlers & CSP).`,
|
|
172
|
+
{ cause: e }
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const instance = await factory({
|
|
136
176
|
locateFile: (path) => path.endsWith(".wasm") && cfg.wasmUrl ? cfg.wasmUrl : path
|
|
137
177
|
});
|
|
138
178
|
let fontBytes = null;
|
|
@@ -172,6 +212,7 @@ var ColorKey = /* @__PURE__ */ ((ColorKey2) => {
|
|
|
172
212
|
ColorKey2[ColorKey2["WickBear"] = 13] = "WickBear";
|
|
173
213
|
ColorKey2[ColorKey2["AccentBull"] = 14] = "AccentBull";
|
|
174
214
|
ColorKey2[ColorKey2["AccentBear"] = 15] = "AccentBear";
|
|
215
|
+
ColorKey2[ColorKey2["Line"] = 16] = "Line";
|
|
175
216
|
return ColorKey2;
|
|
176
217
|
})(ColorKey || {});
|
|
177
218
|
var FloatKey = /* @__PURE__ */ ((FloatKey2) => {
|
|
@@ -186,6 +227,7 @@ var FloatKey = /* @__PURE__ */ ((FloatKey2) => {
|
|
|
186
227
|
FloatKey2[FloatKey2["CandleRadius"] = 8] = "CandleRadius";
|
|
187
228
|
FloatKey2[FloatKey2["WickRoundCap"] = 9] = "WickRoundCap";
|
|
188
229
|
FloatKey2[FloatKey2["VolumeRadius"] = 10] = "VolumeRadius";
|
|
230
|
+
FloatKey2[FloatKey2["LineWidth"] = 11] = "LineWidth";
|
|
189
231
|
return FloatKey2;
|
|
190
232
|
})(FloatKey || {});
|
|
191
233
|
|
|
@@ -238,12 +280,14 @@ var COLOR_KEYS = {
|
|
|
238
280
|
wickBull: 12 /* WickBull */,
|
|
239
281
|
wickBear: 13 /* WickBear */,
|
|
240
282
|
accentBull: 14 /* AccentBull */,
|
|
241
|
-
accentBear: 15 /* AccentBear
|
|
283
|
+
accentBear: 15 /* AccentBear */,
|
|
284
|
+
lineColor: 16 /* Line */
|
|
242
285
|
};
|
|
243
286
|
var FLOAT_KEYS = {
|
|
244
287
|
wickWidth: 1 /* WickWidth */,
|
|
245
288
|
candleRadius: 8 /* CandleRadius */,
|
|
246
|
-
volumeRadius: 10 /* VolumeRadius
|
|
289
|
+
volumeRadius: 10 /* VolumeRadius */,
|
|
290
|
+
lineWidth: 11 /* LineWidth */
|
|
247
291
|
};
|
|
248
292
|
var BOOL_KEYS = {
|
|
249
293
|
wickRoundCap: 9 /* WickRoundCap */
|
|
@@ -293,11 +337,14 @@ function bundledWasmConfig() {
|
|
|
293
337
|
fontUrl: new URL("../assets/VroomSans-Regular.ttf", import.meta.url).href
|
|
294
338
|
};
|
|
295
339
|
}
|
|
340
|
+
function resolveWasmConfig(cfg) {
|
|
341
|
+
if (cfg?.moduleUrl) return { ...cfg, moduleUrl: cfg.moduleUrl };
|
|
342
|
+
return { ...bundledWasmConfig(), ...cfg, moduleUrl: bundledWasmConfig().moduleUrl };
|
|
343
|
+
}
|
|
296
344
|
var modulePromise = null;
|
|
297
345
|
function loadVroom(opts) {
|
|
298
346
|
if (modulePromise) return modulePromise;
|
|
299
|
-
|
|
300
|
-
modulePromise = loadWasmCore(cfg).catch((err) => {
|
|
347
|
+
modulePromise = loadWasmCore(resolveWasmConfig(opts?.wasm)).catch((err) => {
|
|
301
348
|
modulePromise = null;
|
|
302
349
|
throw err;
|
|
303
350
|
});
|
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 Coord,\n CrosshairCandle,\n CrosshairInfo,\n DrawingSpec,\n LiquiditySpec,\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 setFloat(key: number, value: number): void;\n setVisibleRange(startMs: number, endMs: number): void;\n setDefaultCandleWidth(px: number): void;\n getVisibleRange(): { startMs: number; endMs: number };\n resetView(): void;\n resetPriceScale(): 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 setCrosshairData(timeMs: number, price: 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 setDrawings(drawings: DrawingSpec[]): void;\n setLiquidity(liquidity: LiquiditySpec): void;\n setDraft(\n aTime: number,\n aPrice: number,\n hasB: boolean,\n bTime: number,\n bPrice: number,\n guide: boolean,\n color: number,\n width: number,\n ): void;\n clearDraft(): void;\n coordAt(x: number, y: number): Coord | null;\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 setFloat(key: number, value: number): void {\n this.wc.setFloat(key, value);\n }\n setVisibleRange(startMs: number, endMs: number): void {\n this.wc.setVisibleRange(startMs, endMs);\n }\n setDefaultCandleWidth(px: number): void {\n this.wc.setDefaultCandleWidth(px);\n }\n getVisibleRange(): { startMs: number; endMs: number } {\n return this.wc.getVisibleRange();\n }\n resetView(): void {\n this.wc.resetView();\n }\n resetPriceScale(): void {\n this.wc.resetPriceScale();\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 setCrosshairData(timeMs: number, price: number): void {\n this.wc.setCrosshairData(timeMs, price);\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 setDrawings(drawings: DrawingSpec[]): void {\n this.wc.setDrawings(\n drawings.map((d) => ({ ...d, color: d.color >>> 0 })),\n );\n }\n setLiquidity(liquidity: LiquiditySpec): void {\n this.wc.setLiquidity({\n ...liquidity,\n buyColor: liquidity.buyColor >>> 0,\n sellColor: liquidity.sellColor >>> 0,\n });\n }\n setDraft(\n aTime: number,\n aPrice: number,\n hasB: boolean,\n bTime: number,\n bPrice: number,\n guide: boolean,\n color: number,\n width: number,\n ): void {\n this.wc.setDraft(aTime, aPrice, hasB, bTime, bPrice, guide, color >>> 0, width);\n }\n clearDraft(): void {\n this.wc.clearDraft();\n }\n coordAt(x: number, y: number): Coord | null {\n return this.wc.coordAt(x, y);\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/** Theme float slots — mirrors the `VroomFloatKey` enum in the C facade. */\nexport enum FloatKey {\n CandleWidthRatio = 0,\n WickWidth = 1,\n RightPadding = 2,\n AxisFontSize = 3,\n YAxisWidthRatio = 4,\n XAxisHeight = 5,\n VolumeOpacity = 6,\n IndicatorHeightFrac = 7,\n CandleRadius = 8,\n WickRoundCap = 9,\n VolumeRadius = 10,\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 /** Free price at the crosshair's horizontal line (the price-badge value). */\n price: 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 committed line drawing in the core's numeric encoding. Endpoints are in data\n * space (epoch ms + price) so the line tracks the candles on pan/zoom.\n */\nexport type DrawingSpec = {\n aTime: number;\n aPrice: number;\n bTime: number;\n bPrice: number;\n /** Packed 0xAARRGGBB. */\n color: number;\n /** Stroke width in px. */\n width: number;\n};\n\n/** A single resting-liquidity band in the core's numeric encoding. */\nexport type BandSpec = {\n minPrice: number;\n maxPrice: number;\n /** 0 = buy, 1 = sell. */\n side: number;\n volume: number;\n};\n\n/** Liquidity overlay bands + shared style, in the core's numeric encoding. */\nexport type LiquiditySpec = {\n bands: BandSpec[];\n /** Packed 0xAARRGGBB. */\n buyColor: number;\n /** Packed 0xAARRGGBB. */\n sellColor: number;\n /** Volume mapped to peak opacity; 0 = auto-scale to the largest band. */\n maxVolume: number;\n minOpacity: number;\n maxOpacity: number;\n /** Leftward reach from the axis in px. */\n widthPx: number;\n /** Leftward reach as a fraction of pane width. */\n widthFrac: number;\n};\n\n/** A continuous data coordinate at a pixel position (no candle snapping). */\nexport type Coord = {\n timeMs: number;\n price: 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 /** Override one theme float (e.g. wick width). */\n setFloat(key: FloatKey | number, value: number): void;\n /** Pass 0, 0 to show all candles. */\n setVisibleRange(startMs: number, endMs: number): void;\n /**\n * Target candle body width in px for the initial/reset framing. Larger = more\n * zoomed in; smaller = more zoomed out. 0 restores the default (~80 candles).\n * Affects initial framing only; an explicit setVisibleRange takes precedence.\n */\n setDefaultCandleWidth(px: number): void;\n // TODO(react-native): mirror getVisibleRange/resetView/resetPriceScale on\n // the JSI handle — currently web-only.\n /** The current visible time window. {startMs: 0, endMs: 0} = uninitialized. */\n getVisibleRange(): { startMs: number; endMs: number };\n /**\n * Reset to the fresh-mount view: frame the most recent ~80 candles and\n * re-enable continuous y auto-fit (the price range follows the visible\n * candles until the next manual y gesture). Use when the data series is\n * wholesale replaced — e.g. switching assets.\n */\n resetView(): void;\n /**\n * Re-enable continuous y auto-fit only; the time window is untouched. Use\n * after repositioning the window for a same-asset data swap (e.g. a\n * timeframe switch) so the price scale re-fits the newly visible candles.\n */\n resetPriceScale(): 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 /**\n * Show the crosshair at a data-space position (epoch ms + price) instead of\n * pixels — used to mirror one chart's crosshair onto another (cross-chart\n * sync). Converts to pixels via this chart's own window/price scale; the\n * vertical line snaps to the candle for `timeMs`, the horizontal line sits at\n * `price`. Does not emit any callback.\n */\n setCrosshairData(timeMs: number, price: 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 /** Replace the full set of committed line drawings. Pass [] to clear. */\n setDrawings(drawings: DrawingSpec[]): void;\n /**\n * Replace the resting-liquidity overlay (bands + shared style). Pass a spec\n * with an empty `bands` array to clear the overlay.\n */\n setLiquidity(liquidity: LiquiditySpec): void;\n /**\n * Set the transient in-progress draft shown while placing a line. Node A is\n * always shown; node B is shown when `hasB`. `guide` also draws the guideline\n * A→B (the live preview); when false only the node dots show (the committed\n * segment renders via setDrawings). `color`/`width` style the guideline.\n */\n setDraft(\n aTime: number,\n aPrice: number,\n hasB: boolean,\n bTime: number,\n bPrice: number,\n guide: boolean,\n color: number,\n width: number,\n ): void;\n /** Clear the draft (hide in-progress node dots / guideline). */\n clearDraft(): void;\n /**\n * The continuous data coordinate at pixel (x, y) — not snapped to a candle\n * slot. Null when there are no candles or the viewport is degenerate.\n */\n coordAt(x: number, y: number): Coord | null;\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, FloatKey, type VroomChartHandle } from './handle';\n\n// Maps each color 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: Partial<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// Maps each numeric VroomTheme field to its FloatKey.\nexport const FLOAT_KEYS: Partial<Record<keyof VroomTheme, FloatKey>> = {\n wickWidth: FloatKey.WickWidth,\n candleRadius: FloatKey.CandleRadius,\n volumeRadius: FloatKey.VolumeRadius,\n};\n\n// Maps each boolean VroomTheme field to its FloatKey (pushed as 0/1).\nexport const BOOL_KEYS: Partial<Record<keyof VroomTheme, FloatKey>> = {\n wickRoundCap: FloatKey.WickRoundCap,\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 + numeric float into the handle; skip bad 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 (typeof value !== 'string' && typeof value !== 'number') return;\n const argb = parseColor(value);\n if (argb == null) return;\n handle.setColor(COLOR_KEYS[field]!, argb);\n });\n (Object.keys(FLOAT_KEYS) as (keyof VroomTheme)[]).forEach((field) => {\n const value = theme[field];\n if (typeof value !== 'number' || !Number.isFinite(value)) return;\n handle.setFloat(FLOAT_KEYS[field]!, value);\n });\n (Object.keys(BOOL_KEYS) as (keyof VroomTheme)[]).forEach((field) => {\n const value = theme[field];\n if (typeof value !== 'boolean') return;\n handle.setFloat(BOOL_KEYS[field]!, value ? 1 : 0);\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 DrawingSpec,\n BandSpec,\n LiquiditySpec,\n Coord,\n} from './handle';\nexport { ColorKey, FloatKey } from './handle';\nexport { packCandles, unpackCandles, BYTES_PER_CANDLE } from './packCandles';\nexport { parseColor, applyTheme, argbToCss, COLOR_KEYS, FLOAT_KEYS, BOOL_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","FloatKey"]}
|
|
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 Coord,\n CrosshairCandle,\n CrosshairInfo,\n DrawingSpec,\n LiquiditySpec,\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).\n//\n// This interface — and WasmHandle's forwarding below — mirrors VroomChartHandle\n// argument-for-argument, so it MUST be updated whenever a handle signature\n// changes. TypeScript will NOT flag a dropped trailing parameter: a method with\n// fewer params still satisfies an interface requiring more, so the argument is\n// silently lost at runtime instead of failing to compile.\ninterface WebChartInstance {\n setCandles(bytes: Uint8Array): void;\n setSize(width: number, height: number, dpr: number): void;\n setColor(key: number, argb: number): void;\n setFloat(key: number, value: number): void;\n setVisibleRange(startMs: number, endMs: number): void;\n setDefaultCandleWidth(px: number): void;\n setChartType(mode: number): void;\n setMorph(collapse: number, fade: number): void;\n getVisibleRange(): { startMs: number; endMs: number };\n resetView(): void;\n resetPriceScale(): 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 setCrosshairData(timeMs: number, price: 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 setDrawings(drawings: DrawingSpec[]): void;\n setLiquidity(liquidity: LiquiditySpec): void;\n setDraft(\n aTime: number,\n aPrice: number,\n hasB: boolean,\n bTime: number,\n bPrice: number,\n guide: boolean,\n color: number,\n width: number,\n kind: number,\n ): void;\n startDraftStroke(color: number, width: number): void;\n appendDraftPoint(timeMs: number, price: number): void;\n clearDraft(): void;\n setSelectedDrawing(index: number, grabbedEndpoint: number): void;\n moveDrawingEndpoint(index: number, endpoint: number, timeMs: number, price: number): void;\n translateDrawing(index: number, dTimeMs: number, dPrice: number): void;\n hitTestDrawing(x: number, y: number): { index: number; part: number; t: number } | null;\n coordAt(x: number, y: number): Coord | null;\n project(timeMs: number, price: number): { x: number; y: number } | null;\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 setFloat(key: number, value: number): void {\n this.wc.setFloat(key, value);\n }\n setVisibleRange(startMs: number, endMs: number): void {\n this.wc.setVisibleRange(startMs, endMs);\n }\n setDefaultCandleWidth(px: number): void {\n this.wc.setDefaultCandleWidth(px);\n }\n setChartType(mode: number): void {\n this.wc.setChartType(mode);\n }\n setMorph(collapse: number, fade: number): void {\n this.wc.setMorph(collapse, fade);\n }\n getVisibleRange(): { startMs: number; endMs: number } {\n return this.wc.getVisibleRange();\n }\n resetView(): void {\n this.wc.resetView();\n }\n resetPriceScale(): void {\n this.wc.resetPriceScale();\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 setCrosshairData(timeMs: number, price: number): void {\n this.wc.setCrosshairData(timeMs, price);\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 setDrawings(drawings: DrawingSpec[]): void {\n this.wc.setDrawings(\n drawings.map((d) => ({ ...d, color: d.color >>> 0 })),\n );\n }\n setLiquidity(liquidity: LiquiditySpec): void {\n this.wc.setLiquidity({\n ...liquidity,\n buyColor: liquidity.buyColor >>> 0,\n sellColor: liquidity.sellColor >>> 0,\n });\n }\n setDraft(\n aTime: number,\n aPrice: number,\n hasB: boolean,\n bTime: number,\n bPrice: number,\n guide: boolean,\n color: number,\n width: number,\n kind: number,\n ): void {\n this.wc.setDraft(aTime, aPrice, hasB, bTime, bPrice, guide, color >>> 0, width, kind);\n }\n startDraftStroke(color: number, width: number): void {\n this.wc.startDraftStroke(color >>> 0, width);\n }\n appendDraftPoint(timeMs: number, price: number): void {\n this.wc.appendDraftPoint(timeMs, price);\n }\n clearDraft(): void {\n this.wc.clearDraft();\n }\n setSelectedDrawing(index: number, grabbedEndpoint: number): void {\n this.wc.setSelectedDrawing(index, grabbedEndpoint);\n }\n moveDrawingEndpoint(index: number, endpoint: number, timeMs: number, price: number): void {\n this.wc.moveDrawingEndpoint(index, endpoint, timeMs, price);\n }\n translateDrawing(index: number, dTimeMs: number, dPrice: number): void {\n this.wc.translateDrawing(index, dTimeMs, dPrice);\n }\n hitTestDrawing(x: number, y: number): { index: number; part: number; t: number } | null {\n return this.wc.hitTestDrawing(x, y);\n }\n coordAt(x: number, y: number): Coord | null {\n return this.wc.coordAt(x, y);\n }\n project(timeMs: number, price: number): { x: number; y: number } | null {\n return this.wc.project(timeMs, price);\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 pre-bundle the emscripten glue — it must stay an opaque asset.\n\nimport type { VroomModule } from '../handle';\nimport { makeWasmModule, type VroomWasmModule } from './adapter';\n\nexport type WasmConfig = {\n /**\n * URL to the emscripten ES module (vroom_core.mjs). Omit to use the build\n * bundled in this package — in which case `wasmUrl` and `fontUrl` default to\n * the bundled assets too.\n */\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 * Custom loader for the emscripten ES module, if the default `import(url)`\n * doesn't survive your bundler. Bundler magic comments only take effect in\n * your own source, so this is the seam for supplying them:\n *\n * ```ts\n * // webpack / Next.js\n * importModule: (url) => import(/* webpackIgnore: true *\\/ url)\n * // Vite / Rollup\n * importModule: (url) => import(/* @vite-ignore *\\/ url)\n * ```\n */\n importModule?: (url: string) => Promise<unknown>;\n};\n\n/** A WasmConfig with the module URL resolved. */\nexport type ResolvedWasmConfig = WasmConfig & { moduleUrl: string };\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// A plain dynamic import — NOT `new Function('return import(u)')`, which would\n// require a `script-src 'unsafe-eval'` CSP that production apps rightly refuse.\n//\n// The magic comments keep bundlers from trying to resolve/transform the URL at\n// build time: it points at an emitted asset, and the emscripten glue breaks if\n// it's pre-bundled. Both comments are inert everywhere else. Bundlers that honor\n// neither should be handed a `cfg.importModule` instead.\nconst defaultImport = (url: string): Promise<unknown> =>\n import(/* webpackIgnore: true */ /* @vite-ignore */ url);\n\nexport async function loadWasmCore(cfg: ResolvedWasmConfig): Promise<VroomModule> {\n const importModule = cfg.importModule ?? defaultImport;\n\n let factory: EmscriptenFactory;\n try {\n factory = ((await importModule(cfg.moduleUrl)) as { default: EmscriptenFactory }).default;\n } catch (e) {\n throw new Error(\n `[vroom] failed to import the WASM core from ${cfg.moduleUrl}. If your bundler ` +\n 'rewrote or inlined the URL, pass a custom `wasm.importModule` ' +\n '(see @vroomchart/core-wasm README → Bundlers & CSP).',\n { cause: e },\n );\n }\n\n const instance = await factory({\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 Line = 16,\n}\n\n/** Theme float slots — mirrors the `VroomFloatKey` enum in the C facade. */\nexport enum FloatKey {\n CandleWidthRatio = 0,\n WickWidth = 1,\n RightPadding = 2,\n AxisFontSize = 3,\n YAxisWidthRatio = 4,\n XAxisHeight = 5,\n VolumeOpacity = 6,\n IndicatorHeightFrac = 7,\n CandleRadius = 8,\n WickRoundCap = 9,\n VolumeRadius = 10,\n LineWidth = 11,\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 /** Free price at the crosshair's horizontal line (the price-badge value). */\n price: 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 committed line drawing in the core's numeric encoding. Endpoints are in data\n * space (epoch ms + price) so the line tracks the candles on pan/zoom.\n */\nexport type DrawingSpec = {\n aTime: number;\n aPrice: number;\n bTime: number;\n bPrice: number;\n /** Packed 0xAARRGGBB. */\n color: number;\n /** Stroke width in px. */\n width: number;\n /**\n * Geometry: 0 = line (a→b), 1 = box (a and b are opposite corners),\n * 2 = pencil (a freehand path — see `points`).\n */\n kind: number;\n /**\n * The full path for a pencil stroke (kind 2), in draw order. Ignored for line\n * and box, which are fully described by the a/b anchors.\n */\n points?: { timeMs: number; price: number }[];\n};\n\n/** A single resting-liquidity band in the core's numeric encoding. */\nexport type BandSpec = {\n minPrice: number;\n maxPrice: number;\n /** 0 = buy, 1 = sell. */\n side: number;\n volume: number;\n};\n\n/** Liquidity overlay bands + shared style, in the core's numeric encoding. */\nexport type LiquiditySpec = {\n bands: BandSpec[];\n /** Packed 0xAARRGGBB. */\n buyColor: number;\n /** Packed 0xAARRGGBB. */\n sellColor: number;\n /** Volume mapped to peak opacity; 0 = auto-scale to the largest band. */\n maxVolume: number;\n minOpacity: number;\n maxOpacity: number;\n /** Leftward reach from the axis in px. */\n widthPx: number;\n /** Leftward reach as a fraction of pane width. */\n widthFrac: number;\n};\n\n/** A continuous data coordinate at a pixel position (no candle snapping). */\nexport type Coord = {\n timeMs: number;\n price: 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 /** Override one theme float (e.g. wick width). */\n setFloat(key: FloatKey | number, value: number): void;\n /** Pass 0, 0 to show all candles. */\n setVisibleRange(startMs: number, endMs: number): void;\n /**\n * Target candle body width in px for the initial/reset framing. Larger = more\n * zoomed in; smaller = more zoomed out. 0 restores the default (~80 candles).\n * Affects initial framing only; an explicit setVisibleRange takes precedence.\n */\n setDefaultCandleWidth(px: number): void;\n /** Render mode: 0 = candlesticks (default), 1 = line chart (close polyline). */\n setChartType(mode: number): void;\n /**\n * Candle↔line morph blend for animated transitions. `collapse` folds candles\n * toward their close; `fade` crossfades candles→line. Both 0 = candles,\n * 1 = line. Driven per-frame by the host; setChartType snaps both.\n */\n setMorph(collapse: number, fade: number): void;\n // TODO(react-native): mirror getVisibleRange/resetView/resetPriceScale on\n // the JSI handle — currently web-only.\n /** The current visible time window. {startMs: 0, endMs: 0} = uninitialized. */\n getVisibleRange(): { startMs: number; endMs: number };\n /**\n * Reset to the fresh-mount view: frame the most recent ~80 candles and\n * re-enable continuous y auto-fit (the price range follows the visible\n * candles until the next manual y gesture). Use when the data series is\n * wholesale replaced — e.g. switching assets.\n */\n resetView(): void;\n /**\n * Re-enable continuous y auto-fit only; the time window is untouched. Use\n * after repositioning the window for a same-asset data swap (e.g. a\n * timeframe switch) so the price scale re-fits the newly visible candles.\n */\n resetPriceScale(): 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 /**\n * Show the crosshair at a data-space position (epoch ms + price) instead of\n * pixels — used to mirror one chart's crosshair onto another (cross-chart\n * sync). Converts to pixels via this chart's own window/price scale; the\n * vertical line snaps to the candle for `timeMs`, the horizontal line sits at\n * `price`. Does not emit any callback.\n */\n setCrosshairData(timeMs: number, price: 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 /** Replace the full set of committed line drawings. Pass [] to clear. */\n setDrawings(drawings: DrawingSpec[]): void;\n /**\n * Replace the resting-liquidity overlay (bands + shared style). Pass a spec\n * with an empty `bands` array to clear the overlay.\n */\n setLiquidity(liquidity: LiquiditySpec): void;\n /**\n * Set the transient in-progress draft shown while placing a drawing. Node A is\n * always shown; node B is shown when `hasB`. `guide` also draws the live\n * preview (a guideline for a line, a preview rectangle for a box); when false\n * only the node dots show (the committed shape renders via setDrawings).\n * `kind` (0 = line, 1 = box) selects the preview geometry. `color`/`width`\n * style the preview.\n */\n setDraft(\n aTime: number,\n aPrice: number,\n hasB: boolean,\n bTime: number,\n bPrice: number,\n guide: boolean,\n color: number,\n width: number,\n kind: number,\n ): void;\n /**\n * Begin a freehand (pencil) draft stroke, clearing any previous draft. Follow\n * with `appendDraftPoint` per captured sample; the growing path renders live.\n */\n startDraftStroke(color: number, width: number): void;\n /**\n * Append one point to the in-progress freehand draft. Cheap enough to call on\n * every pointer move — the whole path is never restated.\n */\n appendDraftPoint(timeMs: number, price: number): void;\n /** Clear the draft (hide in-progress node dots / guideline / stroke). */\n clearDraft(): void;\n /**\n * Shift a whole committed drawing by a *relative* data-space delta — both\n * anchors and, for a pencil, every path point. Used for live body dragging.\n */\n translateDrawing(index: number, dTimeMs: number, dPrice: number): void;\n /**\n * Select a committed drawing (renders its endpoint handles). `index` -1\n * clears the selection; `grabbedEndpoint` 0/1 renders that handle 50% larger\n * while dragging (-1 for none).\n */\n setSelectedDrawing(index: number, grabbedEndpoint: number): void;\n /** Move one endpoint (0=A, 1=B) of a committed drawing to a new data anchor. */\n moveDrawingEndpoint(index: number, endpoint: number, timeMs: number, price: number): void;\n /**\n * Hit-test pixel (x, y) against the committed drawings. Returns the drawing\n * `index`, `part`, and `t`, or null on a miss. For a line, `part` is 0\n * (endpoint A), 1 (endpoint B), or 2 (body) and `t` is the 0..1 grab position\n * along the segment. For a box, `part` is 0..3 (the four corners) or 4 (body)\n * and `t` is 0. Corner/endpoint hits are only reported for the selected\n * drawing.\n */\n hitTestDrawing(x: number, y: number): { index: number; part: number; t: number } | null;\n /**\n * The continuous data coordinate at pixel (x, y) — not snapped to a candle\n * slot. Null when there are no candles or the viewport is degenerate.\n */\n coordAt(x: number, y: number): Coord | null;\n /**\n * Pixel position of a data coordinate — the inverse of coordAt. Null when\n * there are no candles or the viewport is degenerate.\n */\n project(timeMs: number, price: number): { x: number; y: number } | null;\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, FloatKey, type VroomChartHandle } from './handle';\n\n// Maps each color 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: Partial<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 lineColor: ColorKey.Line,\n};\n\n// Maps each numeric VroomTheme field to its FloatKey.\nexport const FLOAT_KEYS: Partial<Record<keyof VroomTheme, FloatKey>> = {\n wickWidth: FloatKey.WickWidth,\n candleRadius: FloatKey.CandleRadius,\n volumeRadius: FloatKey.VolumeRadius,\n lineWidth: FloatKey.LineWidth,\n};\n\n// Maps each boolean VroomTheme field to its FloatKey (pushed as 0/1).\nexport const BOOL_KEYS: Partial<Record<keyof VroomTheme, FloatKey>> = {\n wickRoundCap: FloatKey.WickRoundCap,\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 + numeric float into the handle; skip bad 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 (typeof value !== 'string' && typeof value !== 'number') return;\n const argb = parseColor(value);\n if (argb == null) return;\n handle.setColor(COLOR_KEYS[field]!, argb);\n });\n (Object.keys(FLOAT_KEYS) as (keyof VroomTheme)[]).forEach((field) => {\n const value = theme[field];\n if (typeof value !== 'number' || !Number.isFinite(value)) return;\n handle.setFloat(FLOAT_KEYS[field]!, value);\n });\n (Object.keys(BOOL_KEYS) as (keyof VroomTheme)[]).forEach((field) => {\n const value = theme[field];\n if (typeof value !== 'boolean') return;\n handle.setFloat(BOOL_KEYS[field]!, value ? 1 : 0);\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 ResolvedWasmConfig, 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(): ResolvedWasmConfig {\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\n// Fill in whatever the caller didn't specify. Supplying `moduleUrl` means\n// self-hosting, so the other URLs are the caller's to provide too (an absent\n// wasmUrl resolves next to the module) — we must not point a self-hosted core at\n// this package's bundled .wasm. Everything else (notably `importModule`) layers\n// onto the bundled defaults, so overriding just the importer is a one-liner.\nfunction resolveWasmConfig(cfg?: WasmConfig): ResolvedWasmConfig {\n if (cfg?.moduleUrl) return { ...cfg, moduleUrl: cfg.moduleUrl };\n return { ...bundledWasmConfig(), ...cfg, moduleUrl: bundledWasmConfig().moduleUrl };\n}\n\nexport type {\n VroomModule,\n VroomChartHandle,\n AxisMetrics,\n CrosshairCandle,\n CrosshairInfo,\n OverlaySpec,\n DrawingSpec,\n BandSpec,\n LiquiditySpec,\n Coord,\n} from './handle';\nexport { ColorKey, FloatKey } from './handle';\nexport { packCandles, unpackCandles, BYTES_PER_CANDLE } from './packCandles';\nexport { parseColor, applyTheme, argbToCss, COLOR_KEYS, FLOAT_KEYS, BOOL_KEYS } from './color';\nexport type { WasmConfig } from './wasm/loadWasm';\n\n/** Options for loading the core. */\nexport type LoadVroomOptions = {\n /**\n * Override how the Skia-WASM core is loaded. By default it comes from the\n * WASM build bundled in this package (turnkey, no asset hosting needed);\n * set `moduleUrl` to self-host it, or `importModule` to keep the bundled\n * assets but control the dynamic import your bundler emits.\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 modulePromise = loadWasmCore(resolveWasmConfig(opts?.wasm)).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","FloatKey"]}
|
package/package.json
CHANGED