@vroomchart/core-wasm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,597 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/handle.ts
6
+ var ColorKey = /* @__PURE__ */ ((ColorKey2) => {
7
+ ColorKey2[ColorKey2["Background"] = 0] = "Background";
8
+ ColorKey2[ColorKey2["Bull"] = 1] = "Bull";
9
+ ColorKey2[ColorKey2["Bear"] = 2] = "Bear";
10
+ ColorKey2[ColorKey2["Wick"] = 3] = "Wick";
11
+ ColorKey2[ColorKey2["Grid"] = 4] = "Grid";
12
+ ColorKey2[ColorKey2["AxisText"] = 5] = "AxisText";
13
+ ColorKey2[ColorKey2["Crosshair"] = 6] = "Crosshair";
14
+ ColorKey2[ColorKey2["TooltipBg"] = 7] = "TooltipBg";
15
+ ColorKey2[ColorKey2["TooltipText"] = 8] = "TooltipText";
16
+ ColorKey2[ColorKey2["CrosshairTarget"] = 9] = "CrosshairTarget";
17
+ return ColorKey2;
18
+ })(ColorKey || {});
19
+
20
+ // src/color.ts
21
+ var COLOR_KEYS = {
22
+ background: 0 /* Background */,
23
+ bull: 1 /* Bull */,
24
+ bear: 2 /* Bear */,
25
+ grid: 4 /* Grid */,
26
+ axisText: 5 /* AxisText */,
27
+ crosshair: 6 /* Crosshair */,
28
+ crosshairTarget: 9 /* CrosshairTarget */
29
+ };
30
+ function parseColor(value) {
31
+ if (typeof value === "number") {
32
+ return Number.isFinite(value) ? value >>> 0 : null;
33
+ }
34
+ let s = value.trim();
35
+ if (s.startsWith("#")) s = s.slice(1);
36
+ if (s.length === 6) s = `ff${s}`;
37
+ if (s.length !== 8 || !/^[0-9a-fA-F]{8}$/.test(s)) return null;
38
+ return parseInt(s, 16) >>> 0;
39
+ }
40
+ function applyTheme(handle, theme) {
41
+ Object.keys(COLOR_KEYS).forEach((field) => {
42
+ const value = theme[field];
43
+ if (value == null) return;
44
+ const argb = parseColor(value);
45
+ if (argb == null) return;
46
+ handle.setColor(COLOR_KEYS[field], argb);
47
+ });
48
+ }
49
+ function argbToCss(argb) {
50
+ const a = (argb >>> 24 & 255) / 255;
51
+ const r = argb >>> 16 & 255;
52
+ const g = argb >>> 8 & 255;
53
+ const b = argb & 255;
54
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
55
+ }
56
+
57
+ // src/packCandles.ts
58
+ var BYTES_PER_CANDLE = 48;
59
+ function packCandles(candles) {
60
+ const buf = new ArrayBuffer(candles.length * BYTES_PER_CANDLE);
61
+ const view = new DataView(buf);
62
+ for (let i = 0; i < candles.length; i++) {
63
+ const c = candles[i];
64
+ const off = i * BYTES_PER_CANDLE;
65
+ view.setBigInt64(off, BigInt(Math.trunc(c.timeMs)), true);
66
+ view.setFloat64(off + 8, c.open, true);
67
+ view.setFloat64(off + 16, c.high, true);
68
+ view.setFloat64(off + 24, c.low, true);
69
+ view.setFloat64(off + 32, c.close, true);
70
+ view.setFloat64(off + 40, c.volume, true);
71
+ }
72
+ return buf;
73
+ }
74
+ function unpackCandles(buf) {
75
+ const view = new DataView(buf);
76
+ const n = Math.floor(buf.byteLength / BYTES_PER_CANDLE);
77
+ const out = new Array(n);
78
+ for (let i = 0; i < n; i++) {
79
+ const off = i * BYTES_PER_CANDLE;
80
+ out[i] = {
81
+ timeMs: Number(view.getBigInt64(off, true)),
82
+ open: view.getFloat64(off + 8, true),
83
+ high: view.getFloat64(off + 16, true),
84
+ low: view.getFloat64(off + 24, true),
85
+ close: view.getFloat64(off + 32, true),
86
+ volume: view.getFloat64(off + 40, true)
87
+ };
88
+ }
89
+ return out;
90
+ }
91
+
92
+ // src/stub/stubModule.ts
93
+ var DEFAULT_VISIBLE = 80;
94
+ var Y_AXIS_W = 58;
95
+ var X_AXIS_H = 22;
96
+ var RIGHT_PAD_CANDLES = 4;
97
+ var DEFAULT_COLORS = {
98
+ [0 /* Background */]: 4279046423,
99
+ [1 /* Bull */]: 4280723098,
100
+ [2 /* Bear */]: 4293874512,
101
+ [3 /* Wick */]: 4286086022,
102
+ [4 /* Grid */]: 4280231472,
103
+ [5 /* AxisText */]: 4287337630,
104
+ [6 /* Crosshair */]: 4291416537,
105
+ [9 /* CrosshairTarget */]: 4291416537
106
+ };
107
+ function median(xs) {
108
+ if (xs.length === 0) return 0;
109
+ const s = [...xs].sort((a, b) => a - b);
110
+ const mid = s.length >> 1;
111
+ return s.length % 2 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
112
+ }
113
+ function niceStep(raw) {
114
+ if (raw <= 0 || !Number.isFinite(raw)) return 1;
115
+ const mag = Math.pow(10, Math.floor(Math.log10(raw)));
116
+ const norm = raw / mag;
117
+ const step = norm <= 1 ? 1 : norm <= 2 ? 2 : norm <= 5 ? 5 : 10;
118
+ return step * mag;
119
+ }
120
+ var StubChart = class {
121
+ constructor(canvas) {
122
+ __publicField(this, "canvas", canvas);
123
+ __publicField(this, "ctx");
124
+ __publicField(this, "candles", []);
125
+ __publicField(this, "durationMs", 6e4);
126
+ __publicField(this, "wCss", 0);
127
+ __publicField(this, "hCss", 0);
128
+ __publicField(this, "dpr", 1);
129
+ __publicField(this, "visStart", 0);
130
+ __publicField(this, "visEnd", 0);
131
+ __publicField(this, "priceMin", 0);
132
+ __publicField(this, "priceMax", 1);
133
+ __publicField(this, "rangeInit", false);
134
+ __publicField(this, "crosshair", false);
135
+ __publicField(this, "chX", 0);
136
+ __publicField(this, "chY", 0);
137
+ __publicField(this, "colors", { ...DEFAULT_COLORS });
138
+ const ctx = canvas.getContext("2d");
139
+ if (!ctx) throw new Error("VroomChart stub: 2D canvas context unavailable");
140
+ this.ctx = ctx;
141
+ }
142
+ // --- geometry helpers ---------------------------------------------------
143
+ get plotW() {
144
+ return Math.max(0, this.wCss - Y_AXIS_W);
145
+ }
146
+ get plotH() {
147
+ return Math.max(0, this.hCss - X_AXIS_H);
148
+ }
149
+ xOf(timeMs) {
150
+ const span = this.visEnd - this.visStart || 1;
151
+ return (timeMs - this.visStart) / span * this.plotW;
152
+ }
153
+ yOf(price) {
154
+ const span = this.priceMax - this.priceMin || 1;
155
+ return (this.priceMax - price) / span * this.plotH;
156
+ }
157
+ color(key) {
158
+ return argbToCss(this.colors[key] ?? DEFAULT_COLORS[key] ?? 4278190080);
159
+ }
160
+ // --- data ---------------------------------------------------------------
161
+ setCandles(packed) {
162
+ this.candles = unpackCandles(packed);
163
+ if (this.candles.length >= 2) {
164
+ const diffs = [];
165
+ for (let i = 1; i < this.candles.length; i++) {
166
+ diffs.push(this.candles[i].timeMs - this.candles[i - 1].timeMs);
167
+ }
168
+ const m = median(diffs);
169
+ if (m > 0) this.durationMs = m;
170
+ }
171
+ if (!this.rangeInit) this.resetWindow();
172
+ this.recomputePriceBounds();
173
+ }
174
+ resetWindow() {
175
+ const n = this.candles.length;
176
+ if (n === 0) {
177
+ this.visStart = 0;
178
+ this.visEnd = 1;
179
+ return;
180
+ }
181
+ const last = this.candles[n - 1].timeMs;
182
+ this.visEnd = last + this.durationMs * RIGHT_PAD_CANDLES;
183
+ this.visStart = this.visEnd - this.durationMs * (DEFAULT_VISIBLE + RIGHT_PAD_CANDLES);
184
+ this.rangeInit = true;
185
+ }
186
+ recomputePriceBounds() {
187
+ let lo = Infinity;
188
+ let hi = -Infinity;
189
+ for (const c of this.candles) {
190
+ if (c.timeMs < this.visStart || c.timeMs > this.visEnd) continue;
191
+ if (c.low < lo) lo = c.low;
192
+ if (c.high > hi) hi = c.high;
193
+ }
194
+ if (!Number.isFinite(lo) || !Number.isFinite(hi) || hi <= lo) {
195
+ if (this.priceMax > this.priceMin) return;
196
+ lo = 0;
197
+ hi = 1;
198
+ }
199
+ const pad = (hi - lo) * 0.08;
200
+ this.priceMin = lo - pad;
201
+ this.priceMax = hi + pad;
202
+ }
203
+ // --- sizing -------------------------------------------------------------
204
+ setSize(width, height, dpr) {
205
+ this.wCss = width;
206
+ this.hCss = height;
207
+ this.dpr = dpr || 1;
208
+ this.canvas.width = Math.max(1, Math.round(width * this.dpr));
209
+ this.canvas.height = Math.max(1, Math.round(height * this.dpr));
210
+ this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);
211
+ }
212
+ setColor(key, argb) {
213
+ this.colors[key] = argb >>> 0;
214
+ }
215
+ setVisibleRange(startMs, endMs) {
216
+ if (startMs === 0 && endMs === 0) {
217
+ this.rangeInit = false;
218
+ this.resetWindow();
219
+ } else {
220
+ this.visStart = startMs;
221
+ this.visEnd = endMs;
222
+ this.rangeInit = true;
223
+ }
224
+ this.recomputePriceBounds();
225
+ }
226
+ // --- viewport mutators --------------------------------------------------
227
+ pan(dx, dy) {
228
+ this.translate(dx, dy);
229
+ }
230
+ translate(dx, dy) {
231
+ const span = this.visEnd - this.visStart;
232
+ const dt = dx / (this.plotW || 1) * span;
233
+ this.visStart -= dt;
234
+ this.visEnd -= dt;
235
+ if (dy) {
236
+ const pspan = this.priceMax - this.priceMin;
237
+ const dp = dy / (this.plotH || 1) * pspan;
238
+ this.priceMin += dp;
239
+ this.priceMax += dp;
240
+ }
241
+ }
242
+ zoom(scaleX, scaleY, fx, fy) {
243
+ if (scaleX !== 1) {
244
+ const span = this.visEnd - this.visStart;
245
+ const focalT = this.visStart + fx / (this.plotW || 1) * span;
246
+ const newSpan = span / scaleX;
247
+ this.visStart = focalT - fx / (this.plotW || 1) * newSpan;
248
+ this.visEnd = this.visStart + newSpan;
249
+ }
250
+ if (scaleY !== 1) {
251
+ const pspan = this.priceMax - this.priceMin;
252
+ const focalP = this.priceMax - fy / (this.plotH || 1) * pspan;
253
+ const newSpan = pspan / scaleY;
254
+ this.priceMax = focalP + fy / (this.plotH || 1) * newSpan;
255
+ this.priceMin = this.priceMax - newSpan;
256
+ }
257
+ }
258
+ scalePriceAxis(dy) {
259
+ const factor = Math.max(0.1, 1 + dy / (this.plotH || 1));
260
+ const center = (this.priceMin + this.priceMax) / 2;
261
+ const half = (this.priceMax - this.priceMin) / 2 * factor;
262
+ this.priceMin = center - half;
263
+ this.priceMax = center + half;
264
+ }
265
+ scaleTimeAxis(dx) {
266
+ const factor = Math.max(0.1, 1 + dx / (this.plotW || 1));
267
+ const span = (this.visEnd - this.visStart) * factor;
268
+ this.visStart = this.visEnd - span;
269
+ }
270
+ getAxisMetrics() {
271
+ return { yAxisWidth: Y_AXIS_W, xAxisHeight: X_AXIS_H, indicatorHeight: 0 };
272
+ }
273
+ // --- crosshair ----------------------------------------------------------
274
+ setCrosshair(x, y) {
275
+ this.crosshair = true;
276
+ this.chX = x;
277
+ this.chY = y;
278
+ }
279
+ clearCrosshair() {
280
+ this.crosshair = false;
281
+ }
282
+ snapIndex() {
283
+ if (this.candles.length === 0) return -1;
284
+ const span = this.visEnd - this.visStart || 1;
285
+ const t = this.visStart + this.chX / (this.plotW || 1) * span;
286
+ let best = -1;
287
+ let bestD = Infinity;
288
+ for (let i = 0; i < this.candles.length; i++) {
289
+ const d = Math.abs(this.candles[i].timeMs - t);
290
+ if (d < bestD) {
291
+ bestD = d;
292
+ best = i;
293
+ }
294
+ }
295
+ return best;
296
+ }
297
+ getCrosshairCandle() {
298
+ if (!this.crosshair) return null;
299
+ const i = this.snapIndex();
300
+ if (i < 0) return null;
301
+ const c = this.candles[i];
302
+ return { ...c };
303
+ }
304
+ // --- indicators (accepted, not drawn in the stub) -----------------------
305
+ setRSI() {
306
+ }
307
+ setMACD() {
308
+ }
309
+ setOverlays(_overlays) {
310
+ }
311
+ setVWAP() {
312
+ }
313
+ isAnimating() {
314
+ return false;
315
+ }
316
+ // --- rendering ----------------------------------------------------------
317
+ present() {
318
+ const ctx = this.ctx;
319
+ const w = this.wCss;
320
+ const h = this.hCss;
321
+ if (w <= 0 || h <= 0) return;
322
+ ctx.clearRect(0, 0, w, h);
323
+ ctx.fillStyle = this.color(0 /* Background */);
324
+ ctx.fillRect(0, 0, w, h);
325
+ this.drawPriceAxis();
326
+ this.drawTimeAxis();
327
+ this.drawCandles();
328
+ if (this.crosshair) this.drawCrosshair();
329
+ }
330
+ priceTicks() {
331
+ const span = this.priceMax - this.priceMin;
332
+ if (span <= 0) return [];
333
+ const step = niceStep(span / 5);
334
+ const first = Math.ceil(this.priceMin / step) * step;
335
+ const out = [];
336
+ for (let p = first; p <= this.priceMax; p += step) out.push(p);
337
+ return out;
338
+ }
339
+ fmtPrice(p) {
340
+ const span = this.priceMax - this.priceMin;
341
+ const decimals = span < 1 ? 4 : span < 100 ? 2 : 0;
342
+ return p.toFixed(decimals);
343
+ }
344
+ drawPriceAxis() {
345
+ const ctx = this.ctx;
346
+ const plotW = this.plotW;
347
+ ctx.lineWidth = 1;
348
+ ctx.strokeStyle = this.color(4 /* Grid */);
349
+ ctx.fillStyle = this.color(5 /* AxisText */);
350
+ ctx.font = '11px -apple-system, system-ui, "Segoe UI", sans-serif';
351
+ ctx.textAlign = "left";
352
+ ctx.textBaseline = "middle";
353
+ for (const p of this.priceTicks()) {
354
+ const y = this.yOf(p);
355
+ if (y < 0 || y > this.plotH) continue;
356
+ ctx.beginPath();
357
+ ctx.moveTo(0, Math.round(y) + 0.5);
358
+ ctx.lineTo(plotW, Math.round(y) + 0.5);
359
+ ctx.stroke();
360
+ ctx.fillText(this.fmtPrice(p), plotW + 6, y);
361
+ }
362
+ }
363
+ fmtTime(timeMs) {
364
+ const d = new Date(timeMs);
365
+ const span = this.visEnd - this.visStart;
366
+ const twoDays = 2 * 24 * 3600 * 1e3;
367
+ const pad = (n) => String(n).padStart(2, "0");
368
+ if (span < twoDays) return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
369
+ return `${d.getMonth() + 1}/${d.getDate()}`;
370
+ }
371
+ drawTimeAxis() {
372
+ const ctx = this.ctx;
373
+ const span = this.visEnd - this.visStart;
374
+ if (span <= 0) return;
375
+ const targetPx = 80;
376
+ const stepMs = niceStep(span / this.plotW * targetPx);
377
+ const first = Math.ceil(this.visStart / stepMs) * stepMs;
378
+ ctx.fillStyle = this.color(5 /* AxisText */);
379
+ ctx.font = '11px -apple-system, system-ui, "Segoe UI", sans-serif';
380
+ ctx.textAlign = "center";
381
+ ctx.textBaseline = "middle";
382
+ const y = this.plotH + X_AXIS_H / 2;
383
+ for (let t = first; t <= this.visEnd; t += stepMs) {
384
+ const x = this.xOf(t);
385
+ if (x < 0 || x > this.plotW) continue;
386
+ ctx.fillText(this.fmtTime(t), x, y);
387
+ }
388
+ }
389
+ drawCandles() {
390
+ const ctx = this.ctx;
391
+ const span = this.visEnd - this.visStart || 1;
392
+ const slotW = this.durationMs / span * this.plotW;
393
+ const bodyW = Math.max(1, slotW * 0.7);
394
+ const bull = this.color(1 /* Bull */);
395
+ const bear = this.color(2 /* Bear */);
396
+ const wick = this.color(3 /* Wick */);
397
+ for (const c of this.candles) {
398
+ const x = this.xOf(c.timeMs);
399
+ if (x < -slotW || x > this.plotW + slotW) continue;
400
+ const up = c.close >= c.open;
401
+ const yHigh = this.yOf(c.high);
402
+ const yLow = this.yOf(c.low);
403
+ const yOpen = this.yOf(c.open);
404
+ const yClose = this.yOf(c.close);
405
+ ctx.strokeStyle = wick;
406
+ ctx.lineWidth = 1;
407
+ ctx.beginPath();
408
+ ctx.moveTo(Math.round(x) + 0.5, yHigh);
409
+ ctx.lineTo(Math.round(x) + 0.5, yLow);
410
+ ctx.stroke();
411
+ ctx.fillStyle = up ? bull : bear;
412
+ const top = Math.min(yOpen, yClose);
413
+ const bodyH = Math.max(1, Math.abs(yClose - yOpen));
414
+ ctx.fillRect(x - bodyW / 2, top, bodyW, bodyH);
415
+ }
416
+ }
417
+ drawCrosshair() {
418
+ const ctx = this.ctx;
419
+ const i = this.snapIndex();
420
+ if (i < 0) return;
421
+ const c = this.candles[i];
422
+ const x = this.xOf(c.timeMs);
423
+ const y = Math.max(0, Math.min(this.plotH, this.chY));
424
+ ctx.save();
425
+ ctx.strokeStyle = this.color(6 /* Crosshair */);
426
+ ctx.lineWidth = 1;
427
+ ctx.setLineDash([4, 4]);
428
+ ctx.beginPath();
429
+ ctx.moveTo(Math.round(x) + 0.5, 0);
430
+ ctx.lineTo(Math.round(x) + 0.5, this.plotH);
431
+ ctx.moveTo(0, Math.round(y) + 0.5);
432
+ ctx.lineTo(this.plotW, Math.round(y) + 0.5);
433
+ ctx.stroke();
434
+ ctx.setLineDash([]);
435
+ const ty = this.yOf(c.close);
436
+ ctx.strokeStyle = this.color(9 /* CrosshairTarget */);
437
+ ctx.beginPath();
438
+ ctx.arc(x, ty, 3.5, 0, Math.PI * 2);
439
+ ctx.stroke();
440
+ ctx.restore();
441
+ }
442
+ destroy() {
443
+ this.candles = [];
444
+ }
445
+ };
446
+ function createStubModule() {
447
+ return {
448
+ create(canvas) {
449
+ return new StubChart(canvas);
450
+ }
451
+ };
452
+ }
453
+
454
+ // src/wasm/adapter.ts
455
+ var uid = 0;
456
+ var WasmHandle = class {
457
+ constructor(wc) {
458
+ __publicField(this, "wc", wc);
459
+ }
460
+ setCandles(packed) {
461
+ this.wc.setCandles(new Uint8Array(packed));
462
+ }
463
+ setSize(width, height, dpr) {
464
+ this.wc.setSize(width, height, dpr);
465
+ }
466
+ setColor(key, argb) {
467
+ this.wc.setColor(key, argb >>> 0);
468
+ }
469
+ setVisibleRange(startMs, endMs) {
470
+ this.wc.setVisibleRange(startMs, endMs);
471
+ }
472
+ pan(dx, dy) {
473
+ this.wc.pan(dx, dy);
474
+ }
475
+ translate(dx, dy) {
476
+ this.wc.translate(dx, dy);
477
+ }
478
+ zoom(scaleX, scaleY, fx, fy) {
479
+ this.wc.zoom(scaleX, scaleY, fx, fy);
480
+ }
481
+ scalePriceAxis(dy) {
482
+ this.wc.scalePriceAxis(dy);
483
+ }
484
+ scaleTimeAxis(dx) {
485
+ this.wc.scaleTimeAxis(dx);
486
+ }
487
+ getAxisMetrics() {
488
+ return this.wc.getAxisMetrics();
489
+ }
490
+ setCrosshair(x, y) {
491
+ this.wc.setCrosshair(x, y);
492
+ }
493
+ clearCrosshair() {
494
+ this.wc.clearCrosshair();
495
+ }
496
+ getCrosshairCandle() {
497
+ return this.wc.getCrosshairCandle();
498
+ }
499
+ setRSI(enabled, period, upperBand, lowerBand, maEnabled, maPeriod) {
500
+ this.wc.setRSI(enabled, period, upperBand, lowerBand, maEnabled, maPeriod);
501
+ }
502
+ setMACD(enabled, fast, slow, signal) {
503
+ this.wc.setMACD(enabled, fast, slow, signal);
504
+ }
505
+ setOverlays(overlays) {
506
+ this.wc.setOverlays(overlays);
507
+ }
508
+ setVWAP(enabled, resetOffsetMin, color, width) {
509
+ this.wc.setVWAP(enabled, resetOffsetMin, color >>> 0, width);
510
+ }
511
+ isAnimating() {
512
+ return this.wc.isAnimating();
513
+ }
514
+ present() {
515
+ this.wc.present();
516
+ }
517
+ destroy() {
518
+ this.wc.delete();
519
+ }
520
+ };
521
+ function makeWasmModule(module, fontBytes) {
522
+ return {
523
+ create(canvas) {
524
+ if (!canvas.id) canvas.id = `vroom-canvas-${uid++}`;
525
+ const wc = new module.WebChart(`#${canvas.id}`);
526
+ if (fontBytes) wc.setTypeface(fontBytes);
527
+ return new WasmHandle(wc);
528
+ }
529
+ };
530
+ }
531
+
532
+ // src/wasm/loadWasm.ts
533
+ var dynamicImport = new Function("u", "return import(u)");
534
+ async function loadWasmCore(cfg) {
535
+ const mod = await dynamicImport(cfg.moduleUrl);
536
+ const instance = await mod.default({
537
+ locateFile: (path) => path.endsWith(".wasm") && cfg.wasmUrl ? cfg.wasmUrl : path
538
+ });
539
+ let fontBytes = null;
540
+ if (cfg.fontUrl) {
541
+ try {
542
+ const res = await fetch(cfg.fontUrl);
543
+ if (res.ok) {
544
+ fontBytes = new Uint8Array(await res.arrayBuffer());
545
+ console.info(`[vroom] axis font loaded (${cfg.fontUrl}, ${fontBytes.length} bytes)`);
546
+ } else {
547
+ console.warn(`[vroom] axis font fetch ${cfg.fontUrl} \u2192 HTTP ${res.status}; labels will be blank.`);
548
+ }
549
+ } catch (e) {
550
+ console.warn(`[vroom] axis font fetch failed (${cfg.fontUrl}); labels will be blank.`, e);
551
+ }
552
+ } else {
553
+ console.warn("[vroom] no fontUrl provided; text labels will be blank.");
554
+ }
555
+ return makeWasmModule(instance, fontBytes);
556
+ }
557
+
558
+ // src/index.ts
559
+ function bundledWasmConfig() {
560
+ return {
561
+ moduleUrl: new URL("../assets/vroom_core.mjs", import.meta.url).href,
562
+ wasmUrl: new URL("../assets/vroom_core.wasm", import.meta.url).href,
563
+ fontUrl: new URL("../assets/VroomSans-Regular.ttf", import.meta.url).href
564
+ };
565
+ }
566
+ var modulePromise = null;
567
+ function loadVroom(opts) {
568
+ if (modulePromise) return modulePromise;
569
+ if (opts?.forceStub) {
570
+ modulePromise = Promise.resolve(createStubModule());
571
+ } else {
572
+ const cfg = opts?.wasm ?? bundledWasmConfig();
573
+ const fallback = opts?.fallbackToStub !== false;
574
+ modulePromise = loadWasmCore(cfg).catch((err) => {
575
+ if (!fallback) throw err;
576
+ console.warn("[vroom] Skia-WASM core failed to load; using Canvas2D stub.", err);
577
+ return createStubModule();
578
+ });
579
+ }
580
+ return modulePromise;
581
+ }
582
+ function resetVroomForTesting() {
583
+ modulePromise = null;
584
+ }
585
+ export {
586
+ BYTES_PER_CANDLE,
587
+ COLOR_KEYS,
588
+ ColorKey,
589
+ applyTheme,
590
+ argbToCss,
591
+ loadVroom,
592
+ packCandles,
593
+ parseColor,
594
+ resetVroomForTesting,
595
+ unpackCandles
596
+ };
597
+ //# sourceMappingURL=index.js.map