velo-plot 1.15.0 → 2.0.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/{ChartCore-BimVvUEg.js → ChartCore-BhQ4j7f5.js} +2213 -1489
- package/dist/ChartCore-BhQ4j7f5.js.map +1 -0
- package/dist/SciPlot-GJvw7GJo.js +463 -0
- package/dist/SciPlot-GJvw7GJo.js.map +1 -0
- package/dist/assets/downsample.worker-C70QJ04y.js +2 -0
- package/dist/assets/downsample.worker-C70QJ04y.js.map +1 -0
- package/dist/assets/indicator.worker-bv0zC4EX.js +2 -0
- package/dist/assets/indicator.worker-bv0zC4EX.js.map +1 -0
- package/dist/axisFormat-SPX-CD5s.js +200 -0
- package/dist/axisFormat-SPX-CD5s.js.map +1 -0
- package/dist/core/InteractionManager.d.ts +1 -0
- package/dist/core/OverlayRenderer.d.ts +13 -0
- package/dist/core/chart/ChartAlerts.d.ts +42 -0
- package/dist/core/chart/ChartCore.d.ts +24 -1
- package/dist/core/chart/ChartRenderLoop.d.ts +9 -2
- package/dist/core/chart/ChartRenderer.d.ts +8 -2
- package/dist/core/chart/candlestickMarkers.d.ts +23 -0
- package/dist/core/chart/heikinAshi.d.ts +10 -0
- package/dist/core/chart/positionLines.d.ts +12 -0
- package/dist/core/chart/series/types.d.ts +2 -2
- package/dist/core/chart/types.d.ts +9 -0
- package/dist/core/format/axisFormat.d.ts +2 -1
- package/dist/core/indicator/addIndicator.d.ts +36 -0
- package/dist/core/indicator/index.d.ts +2 -0
- package/dist/core/indicator/indicatorPresets.d.ts +45 -0
- package/dist/core/series/Series.d.ts +3 -0
- package/dist/core/stacked/types.d.ts +8 -0
- package/dist/core/time/TimeScale.d.ts +31 -0
- package/dist/core/time/applyTimeScale.d.ts +10 -0
- package/dist/createStackedChart-DJSmqerD.js +706 -0
- package/dist/createStackedChart-DJSmqerD.js.map +1 -0
- package/dist/gpu/adapter/gpuRenderer.d.ts +21 -0
- package/dist/gpu/backends/webgpu/WebGPUBackend.d.ts +2 -1
- package/dist/gpuRenderer-BpaMYz-k.js +1848 -0
- package/dist/gpuRenderer-BpaMYz-k.js.map +1 -0
- package/dist/{index-DjeWClO9.js → index-CWipqOLP.js} +2 -2
- package/dist/{index-DjeWClO9.js.map → index-CWipqOLP.js.map} +1 -1
- package/dist/index-D7dTq6VB.js +503 -0
- package/dist/index-D7dTq6VB.js.map +1 -0
- package/dist/{index-Ce2JgkH_.js → index-DoYxf1cV.js} +86 -43
- package/dist/{index-Ce2JgkH_.js.map → index-DoYxf1cV.js.map} +1 -1
- package/dist/{index.core-C_-CFftD.js → index.core-fpZ1dYN0.js} +3 -3
- package/dist/{index.core-C_-CFftD.js.map → index.core-fpZ1dYN0.js.map} +1 -1
- package/dist/index.d.ts +17 -4
- package/dist/indicators--eJGmVox.js +302 -0
- package/dist/indicators--eJGmVox.js.map +1 -0
- package/dist/mockDatafeed-Cdg5hKEo.js +511 -0
- package/dist/mockDatafeed-Cdg5hKEo.js.map +1 -0
- package/dist/plugins/analysis.js +530 -807
- package/dist/plugins/analysis.js.map +1 -1
- package/dist/plugins/caching.js +26 -21
- package/dist/plugins/caching.js.map +1 -1
- package/dist/plugins/drawing-tools/index.d.ts +29 -0
- package/dist/plugins/drawing-tools/measure.d.ts +21 -0
- package/dist/plugins/drawing-tools/snapToCandle.d.ts +21 -0
- package/dist/plugins/gpu.js +202 -1895
- package/dist/plugins/gpu.js.map +1 -1
- package/dist/plugins/index.d.ts +2 -0
- package/dist/plugins/keyboard/index.d.ts +1 -7
- package/dist/plugins/keyboard.js +1 -1
- package/dist/plugins/lazy-load/types.d.ts +7 -0
- package/dist/plugins/lazy-load.js +131 -108
- package/dist/plugins/lazy-load.js.map +1 -1
- package/dist/plugins/replay/index.d.ts +19 -0
- package/dist/plugins/tools.js +1 -1
- package/dist/plugins/virtualization/types.d.ts +12 -0
- package/dist/plugins/virtualization.js +1 -1
- package/dist/pool-15HMCNv8.js +88 -0
- package/dist/pool-15HMCNv8.js.map +1 -0
- package/dist/react.js +5 -5
- package/dist/renderer/CandlestickRenderer.d.ts +1 -1
- package/dist/renderer/ChartSeriesRenderer.d.ts +6 -0
- package/dist/renderer/GpuChartRenderer.d.ts +23 -0
- package/dist/renderer/index.d.ts +3 -0
- package/dist/renderer/spike/WebGLGridSpike.d.ts +40 -0
- package/dist/testing/baselines/v1.15.0.json.d.ts +51 -0
- package/dist/testing/gridSpikeBenchmark.d.ts +49 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/rendererBenchmark.d.ts +13 -0
- package/dist/testing/stage1BrowserBench.d.ts +42 -0
- package/dist/trading/datafeed.d.ts +48 -0
- package/dist/trading/index.d.ts +29 -0
- package/dist/trading/mockDatafeed.d.ts +9 -0
- package/dist/trading/ohlcvGenerator.d.ts +25 -0
- package/dist/trading.d.ts +1 -0
- package/dist/trading.js +95 -0
- package/dist/trading.js.map +1 -0
- package/dist/types.d.ts +22 -2
- package/dist/velo-plot.full.js +1088 -776
- package/dist/velo-plot.full.js.map +1 -1
- package/dist/velo-plot.js +15 -15
- package/dist/workers/downsample.d.ts +38 -0
- package/dist/workers/downsampleAsync.d.ts +6 -0
- package/dist/workers/indicator.worker.d.ts +32 -0
- package/dist/workers/indicatorsAsync.d.ts +26 -0
- package/dist/workers/pool.d.ts +36 -0
- package/package.json +23 -1
- package/dist/ChartCore-BimVvUEg.js.map +0 -1
- package/dist/axisFormat-B7o_uIEA.js +0 -127
- package/dist/axisFormat-B7o_uIEA.js.map +0 -1
- package/dist/createStackedChart-CjsKcOvX.js +0 -1088
- package/dist/createStackedChart-CjsKcOvX.js.map +0 -1
- package/dist/index-ChgIdk3N.js +0 -229
- package/dist/index-ChgIdk3N.js.map +0 -1
package/dist/plugins/index.d.ts
CHANGED
|
@@ -51,6 +51,8 @@ export { PluginBrokenAxis, type PluginBrokenAxisConfig, type BrokenAxisAPI, type
|
|
|
51
51
|
export { PluginVideoRecorder, type PluginVideoRecorderConfig, type VideoRecorderAPI, type VideoRecorderOptions } from './video-recorder';
|
|
52
52
|
export { PluginOffscreen, type PluginOffscreenConfig, type OffscreenAPI, type OffscreenStats, type OffscreenMode, type OffscreenTransferMode, type OffscreenFallbackMode } from './offscreen';
|
|
53
53
|
export { PluginVirtualization, type PluginVirtualizationConfig, type VirtualizationAPI, type VirtualizationStats, type VirtualizationMode, type VirtualizationStrategy } from './virtualization';
|
|
54
|
+
export { PluginDrawingTools, type PluginDrawingToolsConfig, type DrawingToolsAPI, type DrawingMode } from './drawing-tools';
|
|
55
|
+
export { PluginReplay, type PluginReplayConfig, type ReplayAPI } from './replay';
|
|
54
56
|
export { PluginROI, type PluginROIConfig, type RoiAPI, type RoiRegion, type RoiPoint, type RoiMaskResult, type RoiTool, type RoiEvent, type RoiSelectedEvent } from './roi';
|
|
55
57
|
/**
|
|
56
58
|
* Helper function to create a plugin with proper typing
|
|
@@ -2,14 +2,8 @@ import { KeyBindingManager, DEFAULT_KEY_BINDINGS, KeyBinding, KeyBindingManagerO
|
|
|
2
2
|
import { ChartPlugin } from '../types';
|
|
3
3
|
|
|
4
4
|
export interface PluginKeyboardConfig extends Partial<KeyBindingManagerOptions> {
|
|
5
|
-
/** Additional shortcuts to register */
|
|
6
5
|
extraShortcuts?: KeyBinding[];
|
|
7
6
|
}
|
|
8
|
-
|
|
9
|
-
* SciPlot Keyboard Plugin
|
|
10
|
-
*
|
|
11
|
-
* Adds support for keyboard shortcuts and hotkeys.
|
|
12
|
-
*/
|
|
13
|
-
export declare function PluginKeyboard(_config?: PluginKeyboardConfig): ChartPlugin<PluginKeyboardConfig>;
|
|
7
|
+
export declare function PluginKeyboard(config?: PluginKeyboardConfig): ChartPlugin<PluginKeyboardConfig>;
|
|
14
8
|
export { KeyBindingManager, DEFAULT_KEY_BINDINGS };
|
|
15
9
|
export default PluginKeyboard;
|
package/dist/plugins/keyboard.js
CHANGED
|
@@ -107,6 +107,13 @@ export interface LazyLoadAPI {
|
|
|
107
107
|
loadVisible(): Promise<void>;
|
|
108
108
|
/** Unload distant chunks */
|
|
109
109
|
unloadDistant(): number;
|
|
110
|
+
/** Load only a visible data window (+ buffer) */
|
|
111
|
+
setDataWindow(opts: {
|
|
112
|
+
from: number;
|
|
113
|
+
to: number;
|
|
114
|
+
buffer?: number;
|
|
115
|
+
seriesId?: string;
|
|
116
|
+
}): Promise<void>;
|
|
110
117
|
/** Get loading status */
|
|
111
118
|
getLoadingStatus(seriesId: string): {
|
|
112
119
|
loadedPoints: number;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
const
|
|
1
|
+
const D = {
|
|
2
2
|
name: "velo-plot-lazy-load",
|
|
3
3
|
version: "1.0.0",
|
|
4
4
|
description: "Lazy loading for large datasets with viewport-based chunking",
|
|
5
5
|
provides: ["lazy-load", "data-streaming"],
|
|
6
6
|
tags: ["performance", "data", "optimization"]
|
|
7
|
-
},
|
|
7
|
+
}, T = {
|
|
8
8
|
enabled: !0,
|
|
9
9
|
chunkSize: 1e4,
|
|
10
10
|
viewportBuffer: 2,
|
|
@@ -23,167 +23,190 @@ const w = {
|
|
|
23
23
|
},
|
|
24
24
|
debug: !1
|
|
25
25
|
};
|
|
26
|
-
function
|
|
27
|
-
const
|
|
28
|
-
let
|
|
29
|
-
const
|
|
30
|
-
function
|
|
31
|
-
return Math.floor(t /
|
|
26
|
+
function B(w = {}) {
|
|
27
|
+
const a = { ...T, ...w };
|
|
28
|
+
let l = null;
|
|
29
|
+
const h = /* @__PURE__ */ new Map();
|
|
30
|
+
function L(t) {
|
|
31
|
+
return Math.floor(t / a.chunkSize);
|
|
32
32
|
}
|
|
33
|
-
function
|
|
34
|
-
if (!
|
|
35
|
-
const t =
|
|
33
|
+
function m() {
|
|
34
|
+
if (!l) return null;
|
|
35
|
+
const t = l.data.getViewBounds();
|
|
36
36
|
return t ? { xMin: t.xMin, xMax: t.xMax } : null;
|
|
37
37
|
}
|
|
38
|
-
async function
|
|
39
|
-
if (!(t.chunks.has(
|
|
40
|
-
t.loading.add(
|
|
38
|
+
async function M(t, n) {
|
|
39
|
+
if (!(t.chunks.has(n) || t.loading.has(n))) {
|
|
40
|
+
t.loading.add(n);
|
|
41
41
|
try {
|
|
42
|
-
const
|
|
43
|
-
|
|
42
|
+
const o = n * a.chunkSize, r = Math.min(
|
|
43
|
+
o + a.chunkSize,
|
|
44
44
|
t.totalPoints
|
|
45
45
|
);
|
|
46
|
-
|
|
47
|
-
const e = await t.provider.loadChunk(
|
|
48
|
-
t.chunks.set(
|
|
49
|
-
const
|
|
50
|
-
(
|
|
46
|
+
a.debug && console.log(`[LazyLoad] Loading chunk ${n} for ${t.id} (${o}-${r})`);
|
|
47
|
+
const e = await t.provider.loadChunk(o, r);
|
|
48
|
+
t.chunks.set(n, e), t.loading.delete(n), await z(t);
|
|
49
|
+
const s = Array.from(t.chunks.values()).reduce(
|
|
50
|
+
(c, f) => c + (f.endIndex - f.startIndex),
|
|
51
51
|
0
|
|
52
|
-
),
|
|
52
|
+
), i = Math.ceil(t.totalPoints / a.chunkSize), u = {
|
|
53
53
|
seriesId: t.id,
|
|
54
|
-
loadedPoints:
|
|
54
|
+
loadedPoints: s,
|
|
55
55
|
totalPoints: t.totalPoints,
|
|
56
|
-
progress:
|
|
57
|
-
currentChunk:
|
|
58
|
-
totalChunks:
|
|
56
|
+
progress: s / t.totalPoints,
|
|
57
|
+
currentChunk: n,
|
|
58
|
+
totalChunks: i
|
|
59
59
|
};
|
|
60
|
-
|
|
61
|
-
} catch (
|
|
62
|
-
t.loading.delete(
|
|
60
|
+
s === t.totalPoints ? a.onLoadComplete(u) : a.onLoadProgress(u);
|
|
61
|
+
} catch (o) {
|
|
62
|
+
t.loading.delete(n), a.onLoadError(o, t.id), a.debug && console.error(`[LazyLoad] Error loading chunk ${n}:`, o);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
-
async function
|
|
67
|
-
var
|
|
68
|
-
if (!
|
|
69
|
-
const
|
|
70
|
-
if (
|
|
71
|
-
let
|
|
72
|
-
for (const [,
|
|
73
|
-
|
|
74
|
-
const r = new Float32Array(
|
|
75
|
-
let
|
|
76
|
-
for (const [,
|
|
77
|
-
r.set(
|
|
78
|
-
(
|
|
66
|
+
async function z(t) {
|
|
67
|
+
var i, u;
|
|
68
|
+
if (!l) return;
|
|
69
|
+
const n = Array.from(t.chunks.entries()).sort((c, f) => c[0] - f[0]);
|
|
70
|
+
if (n.length === 0) return;
|
|
71
|
+
let o = 0;
|
|
72
|
+
for (const [, c] of n)
|
|
73
|
+
o += c.x.length;
|
|
74
|
+
const r = new Float32Array(o), e = new Float32Array(o);
|
|
75
|
+
let s = 0;
|
|
76
|
+
for (const [, c] of n)
|
|
77
|
+
r.set(c.x, s), e.set(c.y, s), s += c.x.length;
|
|
78
|
+
(u = (i = l.chart).updateSeries) == null || u.call(i, t.id, { x: r, y: e });
|
|
79
79
|
}
|
|
80
80
|
async function g() {
|
|
81
|
-
if (!
|
|
82
|
-
const t =
|
|
81
|
+
if (!a.enabled || !l) return;
|
|
82
|
+
const t = m();
|
|
83
83
|
if (!t) return;
|
|
84
|
-
const
|
|
85
|
-
xMin: t.xMin -
|
|
86
|
-
xMax: t.xMax +
|
|
84
|
+
const n = a.viewportBuffer, o = t.xMax - t.xMin, r = {
|
|
85
|
+
xMin: t.xMin - o * n,
|
|
86
|
+
xMax: t.xMax + o * n
|
|
87
87
|
};
|
|
88
|
-
for (const [, e] of
|
|
89
|
-
const
|
|
90
|
-
for (let d =
|
|
91
|
-
!e.chunks.has(d) && !e.loading.has(d) &&
|
|
92
|
-
const f = Math.min(
|
|
88
|
+
for (const [, e] of h) {
|
|
89
|
+
const s = [], i = Math.ceil(e.totalPoints / a.chunkSize), u = Math.max(0, Math.floor(r.xMin / a.chunkSize)), c = Math.min(i - 1, Math.floor(r.xMax / a.chunkSize));
|
|
90
|
+
for (let d = u; d <= c; d++)
|
|
91
|
+
!e.chunks.has(d) && !e.loading.has(d) && s.push(d);
|
|
92
|
+
const f = Math.min(s.length, 10);
|
|
93
93
|
for (let d = 0; d < f; d++)
|
|
94
|
-
await
|
|
95
|
-
if (
|
|
96
|
-
const d =
|
|
97
|
-
!e.chunks.has(d) && !e.loading.has(d) &&
|
|
94
|
+
await M(e, s[d]);
|
|
95
|
+
if (a.preloadAdjacent && c + 1 < i) {
|
|
96
|
+
const d = c + 1;
|
|
97
|
+
!e.chunks.has(d) && !e.loading.has(d) && M(e, d);
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
-
function
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
101
|
+
function b(t, n) {
|
|
102
|
+
const o = a.viewportBuffer, r = n.xMax - n.xMin, e = {
|
|
103
|
+
xMin: n.xMin - r * o,
|
|
104
|
+
xMax: n.xMax + r * o
|
|
105
|
+
}, s = Math.ceil(t.totalPoints / a.chunkSize), i = Math.max(0, Math.floor(e.xMin / a.chunkSize)), u = Math.min(s - 1, Math.floor(e.xMax / a.chunkSize));
|
|
106
|
+
return { startChunk: i, endChunk: u };
|
|
107
|
+
}
|
|
108
|
+
function x() {
|
|
109
|
+
if (!a.autoUnload || !l) return 0;
|
|
110
|
+
const t = m();
|
|
111
|
+
if (!t) return 0;
|
|
112
|
+
let n = 0;
|
|
113
|
+
for (const [, o] of h) {
|
|
114
|
+
let r = 0;
|
|
115
|
+
const { startChunk: e, endChunk: s } = b(o, t), i = Math.max(1, s - e + 1), u = Math.ceil(a.unloadThreshold * i), c = Math.max(0, e - u), f = s + u;
|
|
116
|
+
for (const d of Array.from(o.chunks.keys()))
|
|
117
|
+
(d < c || d > f) && (o.chunks.delete(d), r++);
|
|
118
|
+
if (o.chunks.size > a.maxLoadedChunks) {
|
|
119
|
+
const d = Array.from(o.chunks.entries()).sort(
|
|
120
|
+
(k, $) => k[1].loadedAt - $[1].loadedAt
|
|
121
|
+
), U = o.chunks.size - a.maxLoadedChunks;
|
|
122
|
+
for (let k = 0; k < U; k++)
|
|
123
|
+
o.chunks.delete(d[k][0]), r++;
|
|
111
124
|
}
|
|
112
|
-
|
|
125
|
+
r > 0 && (z(o), n += r);
|
|
126
|
+
}
|
|
127
|
+
return a.debug && n > 0 && console.log(`[LazyLoad] Unloaded ${n} chunks`), n;
|
|
113
128
|
}
|
|
114
|
-
function
|
|
115
|
-
const
|
|
116
|
-
|
|
129
|
+
function P(t, n) {
|
|
130
|
+
const o = n.getTotalCount();
|
|
131
|
+
h.set(t, {
|
|
117
132
|
id: t,
|
|
118
|
-
provider:
|
|
133
|
+
provider: n,
|
|
119
134
|
chunks: /* @__PURE__ */ new Map(),
|
|
120
135
|
loading: /* @__PURE__ */ new Set(),
|
|
121
|
-
totalPoints:
|
|
122
|
-
}),
|
|
136
|
+
totalPoints: o
|
|
137
|
+
}), a.debug && console.log(`[LazyLoad] Registered series ${t} with ${o} points`), a.onLoadStart({
|
|
123
138
|
seriesId: t,
|
|
124
139
|
loadedPoints: 0,
|
|
125
|
-
totalPoints:
|
|
140
|
+
totalPoints: o,
|
|
126
141
|
progress: 0,
|
|
127
142
|
currentChunk: 0,
|
|
128
|
-
totalChunks: Math.ceil(
|
|
129
|
-
}),
|
|
143
|
+
totalChunks: Math.ceil(o / a.chunkSize)
|
|
144
|
+
}), a.autoLoad && g();
|
|
130
145
|
}
|
|
131
|
-
function
|
|
132
|
-
|
|
146
|
+
function v(t) {
|
|
147
|
+
h.delete(t), a.debug && console.log(`[LazyLoad] Unregistered series ${t}`);
|
|
133
148
|
}
|
|
134
|
-
async function
|
|
135
|
-
const r =
|
|
149
|
+
async function C(t, n, o) {
|
|
150
|
+
const r = h.get(t);
|
|
136
151
|
if (!r) return;
|
|
137
|
-
const e =
|
|
138
|
-
for (let
|
|
139
|
-
await
|
|
152
|
+
const e = L(n), s = L(o);
|
|
153
|
+
for (let i = e; i <= s; i++)
|
|
154
|
+
await M(r, i);
|
|
140
155
|
}
|
|
141
|
-
function
|
|
142
|
-
const
|
|
143
|
-
return
|
|
144
|
-
loadedPoints: Array.from(
|
|
156
|
+
function A(t) {
|
|
157
|
+
const n = h.get(t);
|
|
158
|
+
return n ? {
|
|
159
|
+
loadedPoints: Array.from(n.chunks.values()).reduce(
|
|
145
160
|
(r, e) => r + (e.endIndex - e.startIndex),
|
|
146
161
|
0
|
|
147
162
|
),
|
|
148
|
-
totalPoints:
|
|
149
|
-
loadedChunks:
|
|
150
|
-
totalChunks: Math.ceil(
|
|
163
|
+
totalPoints: n.totalPoints,
|
|
164
|
+
loadedChunks: n.chunks.size,
|
|
165
|
+
totalChunks: Math.ceil(n.totalPoints / a.chunkSize)
|
|
151
166
|
} : null;
|
|
152
167
|
}
|
|
153
168
|
function y() {
|
|
154
|
-
for (const [, t] of
|
|
169
|
+
for (const [, t] of h)
|
|
155
170
|
t.chunks.clear(), t.loading.clear();
|
|
156
171
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
172
|
+
async function p(t) {
|
|
173
|
+
if (!l || !a.enabled) return;
|
|
174
|
+
const n = t.buffer ?? a.viewportBuffer, o = t.to - t.from, r = Math.max(0, Math.floor(t.from - o * n)), e = Math.ceil(t.to + o * n), s = t.seriesId ? [[t.seriesId, h.get(t.seriesId)]] : Array.from(h.entries()).map(([i, u]) => [i, u]);
|
|
175
|
+
for (const [i, u] of s)
|
|
176
|
+
u && await C(i, r, e);
|
|
177
|
+
a.autoUnload && x();
|
|
178
|
+
}
|
|
179
|
+
const S = {
|
|
180
|
+
registerSeries: P,
|
|
181
|
+
unregisterSeries: v,
|
|
182
|
+
loadRange: C,
|
|
161
183
|
loadVisible: g,
|
|
162
|
-
unloadDistant:
|
|
163
|
-
|
|
184
|
+
unloadDistant: x,
|
|
185
|
+
setDataWindow: p,
|
|
186
|
+
getLoadingStatus: A,
|
|
164
187
|
clear: y,
|
|
165
188
|
updateConfig(t) {
|
|
166
|
-
Object.assign(
|
|
189
|
+
Object.assign(a, t);
|
|
167
190
|
}
|
|
168
191
|
};
|
|
169
192
|
return {
|
|
170
|
-
manifest:
|
|
193
|
+
manifest: D,
|
|
171
194
|
onInit(t) {
|
|
172
|
-
var
|
|
173
|
-
|
|
174
|
-
g(),
|
|
175
|
-
}), (e = (r =
|
|
176
|
-
g(),
|
|
195
|
+
var n, o, r, e;
|
|
196
|
+
l = t, l.chart.lazyLoad = S, l.chart.setDataWindow = (s) => p(s), a.autoLoad && ((o = (n = l.chart).on) == null || o.call(n, "zoom", () => {
|
|
197
|
+
g(), a.autoUnload && x();
|
|
198
|
+
}), (e = (r = l.chart).on) == null || e.call(r, "pan", () => {
|
|
199
|
+
g(), a.autoUnload && x();
|
|
177
200
|
}));
|
|
178
201
|
},
|
|
179
202
|
onDestroy(t) {
|
|
180
|
-
y(), delete t.chart.lazyLoad,
|
|
203
|
+
y(), delete t.chart.lazyLoad, delete t.chart.setDataWindow, l = null;
|
|
181
204
|
},
|
|
182
|
-
api:
|
|
205
|
+
api: S
|
|
183
206
|
};
|
|
184
207
|
}
|
|
185
208
|
export {
|
|
186
|
-
|
|
187
|
-
|
|
209
|
+
B as PluginLazyLoad,
|
|
210
|
+
B as default
|
|
188
211
|
};
|
|
189
212
|
//# sourceMappingURL=lazy-load.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazy-load.js","sources":["../../src/plugins/lazy-load/index.ts"],"sourcesContent":["/**\n * @fileoverview Lazy loading plugin for large datasets\n * @module plugins/lazy-load\n */\n\nimport type {\n PluginLazyLoadConfig,\n LazyLoadAPI,\n DataProvider,\n LazyLoadedSeries,\n LoadProgressEvent,\n} from './types';\nimport type {\n ChartPlugin,\n PluginContext,\n PluginManifest,\n} from '../types';\n\nconst manifest: PluginManifest = {\n name: 'velo-plot-lazy-load',\n version: '1.0.0',\n description: 'Lazy loading for large datasets with viewport-based chunking',\n provides: ['lazy-load', 'data-streaming'],\n tags: ['performance', 'data', 'optimization'],\n};\n\nconst DEFAULT_CONFIG: Required<PluginLazyLoadConfig> = {\n enabled: true,\n chunkSize: 10000,\n viewportBuffer: 2,\n autoLoad: true,\n autoUnload: true,\n unloadThreshold: 5,\n preloadAdjacent: true,\n maxLoadedChunks: 100,\n onLoadStart: () => {},\n onLoadProgress: () => {},\n onLoadComplete: () => {},\n onLoadError: () => {},\n debug: false,\n};\n\nexport function PluginLazyLoad(\n userConfig: Partial<PluginLazyLoadConfig> = {}\n): ChartPlugin<PluginLazyLoadConfig> {\n const config = { ...DEFAULT_CONFIG, ...userConfig };\n let ctx: PluginContext | null = null;\n\n // Registered series\n const series = new Map<string, LazyLoadedSeries>();\n\n /**\n * Calculate chunk index from point index\n */\n function getChunkIndex(pointIndex: number): number {\n return Math.floor(pointIndex / config.chunkSize);\n }\n\n /**\n * Get visible range from chart\n */\n function getVisibleRange(): { xMin: number; xMax: number } | null {\n if (!ctx) return null;\n \n // Get current view bounds from data context\n const bounds = ctx.data.getViewBounds();\n if (bounds) {\n return { xMin: bounds.xMin, xMax: bounds.xMax };\n }\n \n return null;\n }\n\n /**\n * Load a specific chunk\n */\n async function loadChunk(\n seriesInfo: LazyLoadedSeries,\n chunkIndex: number\n ): Promise<void> {\n // Already loaded or loading\n if (seriesInfo.chunks.has(chunkIndex) || seriesInfo.loading.has(chunkIndex)) {\n return;\n }\n\n seriesInfo.loading.add(chunkIndex);\n\n try {\n const startIndex = chunkIndex * config.chunkSize;\n const endIndex = Math.min(\n startIndex + config.chunkSize,\n seriesInfo.totalPoints\n );\n\n if (config.debug) {\n console.log(`[LazyLoad] Loading chunk ${chunkIndex} for ${seriesInfo.id} (${startIndex}-${endIndex})`);\n }\n\n const chunk = await seriesInfo.provider.loadChunk(startIndex, endIndex);\n \n seriesInfo.chunks.set(chunkIndex, chunk);\n seriesInfo.loading.delete(chunkIndex);\n\n // Update chart data\n await updateSeriesData(seriesInfo);\n\n // Fire progress event\n const loadedPoints = Array.from(seriesInfo.chunks.values()).reduce(\n (sum, c) => sum + (c.endIndex - c.startIndex),\n 0\n );\n\n const totalChunks = Math.ceil(seriesInfo.totalPoints / config.chunkSize);\n \n const event: LoadProgressEvent = {\n seriesId: seriesInfo.id,\n loadedPoints,\n totalPoints: seriesInfo.totalPoints,\n progress: loadedPoints / seriesInfo.totalPoints,\n currentChunk: chunkIndex,\n totalChunks,\n };\n\n if (loadedPoints === seriesInfo.totalPoints) {\n config.onLoadComplete(event);\n } else {\n config.onLoadProgress(event);\n }\n\n } catch (error) {\n seriesInfo.loading.delete(chunkIndex);\n config.onLoadError(error as Error, seriesInfo.id);\n \n if (config.debug) {\n console.error(`[LazyLoad] Error loading chunk ${chunkIndex}:`, error);\n }\n }\n }\n\n /**\n * Update series data from loaded chunks\n */\n async function updateSeriesData(seriesInfo: LazyLoadedSeries): Promise<void> {\n if (!ctx) return;\n\n // Merge all loaded chunks\n const sortedChunks = Array.from(seriesInfo.chunks.entries()).sort((a, b) => a[0] - b[0]);\n \n if (sortedChunks.length === 0) return;\n\n // Calculate total size\n let totalSize = 0;\n for (const [, chunk] of sortedChunks) {\n totalSize += chunk.x.length;\n }\n\n // Allocate arrays\n const x = new Float32Array(totalSize);\n const y = new Float32Array(totalSize);\n\n // Copy data\n let offset = 0;\n for (const [, chunk] of sortedChunks) {\n x.set(chunk.x, offset);\n y.set(chunk.y, offset);\n offset += chunk.x.length;\n }\n\n // Update series\n (ctx.chart as any).updateSeries?.(seriesInfo.id, { x, y });\n }\n\n /**\n * Load visible data\n */\n async function loadVisible(): Promise<void> {\n if (!config.enabled || !ctx) return;\n\n const visibleRange = getVisibleRange();\n if (!visibleRange) return;\n\n const buffer = config.viewportBuffer;\n const rangeWidth = visibleRange.xMax - visibleRange.xMin;\n const buffered = {\n xMin: visibleRange.xMin - rangeWidth * buffer,\n xMax: visibleRange.xMax + rangeWidth * buffer,\n };\n\n for (const [, seriesInfo] of series) {\n const chunksToLoad: number[] = [];\n const totalChunks = Math.ceil(seriesInfo.totalPoints / config.chunkSize);\n\n // Identify chunks within the buffered viewport\n const startIdx = Math.max(0, Math.floor(buffered.xMin / config.chunkSize));\n const endIdx = Math.min(totalChunks - 1, Math.floor(buffered.xMax / config.chunkSize));\n\n for (let i = startIdx; i <= endIdx; i++) {\n if (!seriesInfo.chunks.has(i) && !seriesInfo.loading.has(i)) {\n chunksToLoad.push(i);\n }\n }\n\n // Load chunks sequentially (limit to avoid flood)\n const maxToLoad = Math.min(chunksToLoad.length, 10);\n for (let i = 0; i < maxToLoad; i++) {\n await loadChunk(seriesInfo, chunksToLoad[i]);\n }\n\n // Preload next adjacent chunk if enabled\n if (config.preloadAdjacent && endIdx + 1 < totalChunks) {\n const nextChunk = endIdx + 1;\n if (!seriesInfo.chunks.has(nextChunk) && !seriesInfo.loading.has(nextChunk)) {\n loadChunk(seriesInfo, nextChunk); // Fire and forget\n }\n }\n }\n }\n\n /**\n * Unload distant chunks\n */\n function unloadDistant(): number {\n if (!config.autoUnload || !ctx) return 0;\n\n const visibleRange = getVisibleRange();\n if (!visibleRange) return 0;\n\n let unloadedCount = 0;\n\n for (const [, seriesInfo] of series) {\n // TODO: Implement distance-based unloading logic\n // For now, only maxLoadedChunks handles eviction\n\n // Enforce max chunks limit\n if (seriesInfo.chunks.size > config.maxLoadedChunks) {\n const sortedChunks = Array.from(seriesInfo.chunks.entries()).sort(\n (a, b) => a[1].loadedAt - b[1].loadedAt\n );\n \n const toRemove = seriesInfo.chunks.size - config.maxLoadedChunks;\n for (let i = 0; i < toRemove; i++) {\n seriesInfo.chunks.delete(sortedChunks[i][0]);\n unloadedCount++;\n }\n }\n }\n\n if (config.debug && unloadedCount > 0) {\n console.log(`[LazyLoad] Unloaded ${unloadedCount} chunks`);\n }\n\n return unloadedCount;\n }\n\n /**\n * Register a series\n */\n function registerSeries(seriesId: string, provider: DataProvider): void {\n const totalPoints = provider.getTotalCount();\n \n series.set(seriesId, {\n id: seriesId,\n provider,\n chunks: new Map(),\n loading: new Set(),\n totalPoints,\n });\n\n if (config.debug) {\n console.log(`[LazyLoad] Registered series ${seriesId} with ${totalPoints} points`);\n }\n\n // Fire load start event\n config.onLoadStart({\n seriesId,\n loadedPoints: 0,\n totalPoints,\n progress: 0,\n currentChunk: 0,\n totalChunks: Math.ceil(totalPoints / config.chunkSize),\n });\n\n // Auto-load if enabled\n if (config.autoLoad) {\n loadVisible();\n }\n }\n\n /**\n * Unregister a series\n */\n function unregisterSeries(seriesId: string): void {\n series.delete(seriesId);\n \n if (config.debug) {\n console.log(`[LazyLoad] Unregistered series ${seriesId}`);\n }\n }\n\n /**\n * Load specific range\n */\n async function loadRange(\n seriesId: string,\n startIndex: number,\n endIndex: number\n ): Promise<void> {\n const seriesInfo = series.get(seriesId);\n if (!seriesInfo) return;\n\n const startChunk = getChunkIndex(startIndex);\n const endChunk = getChunkIndex(endIndex);\n\n for (let i = startChunk; i <= endChunk; i++) {\n await loadChunk(seriesInfo, i);\n }\n }\n\n /**\n * Get loading status\n */\n function getLoadingStatus(seriesId: string) {\n const seriesInfo = series.get(seriesId);\n if (!seriesInfo) return null;\n\n const loadedPoints = Array.from(seriesInfo.chunks.values()).reduce(\n (sum, c) => sum + (c.endIndex - c.startIndex),\n 0\n );\n\n return {\n loadedPoints,\n totalPoints: seriesInfo.totalPoints,\n loadedChunks: seriesInfo.chunks.size,\n totalChunks: Math.ceil(seriesInfo.totalPoints / config.chunkSize),\n };\n }\n\n /**\n * Clear all data\n */\n function clear(): void {\n for (const [, seriesInfo] of series) {\n seriesInfo.chunks.clear();\n seriesInfo.loading.clear();\n }\n }\n\n // API implementation\n const api: LazyLoadAPI & Record<string, unknown> = {\n registerSeries,\n unregisterSeries,\n loadRange,\n loadVisible,\n unloadDistant,\n getLoadingStatus,\n clear,\n updateConfig(newConfig: Partial<PluginLazyLoadConfig>) {\n Object.assign(config, newConfig);\n },\n };\n\n return {\n manifest,\n\n onInit(pluginCtx: PluginContext) {\n ctx = pluginCtx;\n\n // Attach to chart API\n (ctx.chart as any).lazyLoad = api;\n\n // Listen for zoom/pan if auto-load enabled\n if (config.autoLoad) {\n ctx.chart.on?.('zoom', () => {\n loadVisible();\n if (config.autoUnload) {\n unloadDistant();\n }\n });\n\n ctx.chart.on?.('pan', () => {\n loadVisible();\n if (config.autoUnload) {\n unloadDistant();\n }\n });\n }\n },\n\n onDestroy(pluginCtx: PluginContext) {\n // Clear all data\n clear();\n\n // Remove from chart API\n delete (pluginCtx.chart as any).lazyLoad;\n\n ctx = null;\n },\n\n api,\n };\n}\n\nexport default PluginLazyLoad;\n\n// Type exports\nexport type {\n PluginLazyLoadConfig,\n LazyLoadAPI,\n DataProvider,\n DataChunk,\n LoadProgressEvent,\n} from './types';\n"],"names":["manifest","DEFAULT_CONFIG","PluginLazyLoad","userConfig","config","ctx","series","getChunkIndex","pointIndex","getVisibleRange","bounds","loadChunk","seriesInfo","chunkIndex","startIndex","endIndex","chunk","updateSeriesData","loadedPoints","sum","c","totalChunks","event","error","_a","_b","sortedChunks","a","b","totalSize","x","y","offset","loadVisible","visibleRange","buffer","rangeWidth","buffered","chunksToLoad","startIdx","endIdx","i","maxToLoad","nextChunk","unloadDistant","unloadedCount","toRemove","registerSeries","seriesId","provider","totalPoints","unregisterSeries","loadRange","startChunk","endChunk","getLoadingStatus","clear","api","newConfig","pluginCtx","_c","_d"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lazy-load.js","sources":["../../src/plugins/lazy-load/index.ts"],"sourcesContent":["/**\n * @fileoverview Lazy loading plugin for large datasets\n * @module plugins/lazy-load\n */\n\nimport type {\n PluginLazyLoadConfig,\n LazyLoadAPI,\n DataProvider,\n LazyLoadedSeries,\n LoadProgressEvent,\n} from './types';\nimport type {\n ChartPlugin,\n PluginContext,\n PluginManifest,\n} from '../types';\n\nconst manifest: PluginManifest = {\n name: 'velo-plot-lazy-load',\n version: '1.0.0',\n description: 'Lazy loading for large datasets with viewport-based chunking',\n provides: ['lazy-load', 'data-streaming'],\n tags: ['performance', 'data', 'optimization'],\n};\n\nconst DEFAULT_CONFIG: Required<PluginLazyLoadConfig> = {\n enabled: true,\n chunkSize: 10000,\n viewportBuffer: 2,\n autoLoad: true,\n autoUnload: true,\n unloadThreshold: 5,\n preloadAdjacent: true,\n maxLoadedChunks: 100,\n onLoadStart: () => {},\n onLoadProgress: () => {},\n onLoadComplete: () => {},\n onLoadError: () => {},\n debug: false,\n};\n\nexport function PluginLazyLoad(\n userConfig: Partial<PluginLazyLoadConfig> = {}\n): ChartPlugin<PluginLazyLoadConfig> {\n const config = { ...DEFAULT_CONFIG, ...userConfig };\n let ctx: PluginContext | null = null;\n\n // Registered series\n const series = new Map<string, LazyLoadedSeries>();\n\n /**\n * Calculate chunk index from point index\n */\n function getChunkIndex(pointIndex: number): number {\n return Math.floor(pointIndex / config.chunkSize);\n }\n\n /**\n * Get visible range from chart\n */\n function getVisibleRange(): { xMin: number; xMax: number } | null {\n if (!ctx) return null;\n \n // Get current view bounds from data context\n const bounds = ctx.data.getViewBounds();\n if (bounds) {\n return { xMin: bounds.xMin, xMax: bounds.xMax };\n }\n \n return null;\n }\n\n /**\n * Load a specific chunk\n */\n async function loadChunk(\n seriesInfo: LazyLoadedSeries,\n chunkIndex: number\n ): Promise<void> {\n // Already loaded or loading\n if (seriesInfo.chunks.has(chunkIndex) || seriesInfo.loading.has(chunkIndex)) {\n return;\n }\n\n seriesInfo.loading.add(chunkIndex);\n\n try {\n const startIndex = chunkIndex * config.chunkSize;\n const endIndex = Math.min(\n startIndex + config.chunkSize,\n seriesInfo.totalPoints\n );\n\n if (config.debug) {\n console.log(`[LazyLoad] Loading chunk ${chunkIndex} for ${seriesInfo.id} (${startIndex}-${endIndex})`);\n }\n\n const chunk = await seriesInfo.provider.loadChunk(startIndex, endIndex);\n \n seriesInfo.chunks.set(chunkIndex, chunk);\n seriesInfo.loading.delete(chunkIndex);\n\n // Update chart data\n await updateSeriesData(seriesInfo);\n\n // Fire progress event\n const loadedPoints = Array.from(seriesInfo.chunks.values()).reduce(\n (sum, c) => sum + (c.endIndex - c.startIndex),\n 0\n );\n\n const totalChunks = Math.ceil(seriesInfo.totalPoints / config.chunkSize);\n \n const event: LoadProgressEvent = {\n seriesId: seriesInfo.id,\n loadedPoints,\n totalPoints: seriesInfo.totalPoints,\n progress: loadedPoints / seriesInfo.totalPoints,\n currentChunk: chunkIndex,\n totalChunks,\n };\n\n if (loadedPoints === seriesInfo.totalPoints) {\n config.onLoadComplete(event);\n } else {\n config.onLoadProgress(event);\n }\n\n } catch (error) {\n seriesInfo.loading.delete(chunkIndex);\n config.onLoadError(error as Error, seriesInfo.id);\n \n if (config.debug) {\n console.error(`[LazyLoad] Error loading chunk ${chunkIndex}:`, error);\n }\n }\n }\n\n /**\n * Update series data from loaded chunks\n */\n async function updateSeriesData(seriesInfo: LazyLoadedSeries): Promise<void> {\n if (!ctx) return;\n\n // Merge all loaded chunks\n const sortedChunks = Array.from(seriesInfo.chunks.entries()).sort((a, b) => a[0] - b[0]);\n \n if (sortedChunks.length === 0) return;\n\n // Calculate total size\n let totalSize = 0;\n for (const [, chunk] of sortedChunks) {\n totalSize += chunk.x.length;\n }\n\n // Allocate arrays\n const x = new Float32Array(totalSize);\n const y = new Float32Array(totalSize);\n\n // Copy data\n let offset = 0;\n for (const [, chunk] of sortedChunks) {\n x.set(chunk.x, offset);\n y.set(chunk.y, offset);\n offset += chunk.x.length;\n }\n\n // Update series\n (ctx.chart as any).updateSeries?.(seriesInfo.id, { x, y });\n }\n\n /**\n * Load visible data\n */\n async function loadVisible(): Promise<void> {\n if (!config.enabled || !ctx) return;\n\n const visibleRange = getVisibleRange();\n if (!visibleRange) return;\n\n const buffer = config.viewportBuffer;\n const rangeWidth = visibleRange.xMax - visibleRange.xMin;\n const buffered = {\n xMin: visibleRange.xMin - rangeWidth * buffer,\n xMax: visibleRange.xMax + rangeWidth * buffer,\n };\n\n for (const [, seriesInfo] of series) {\n const chunksToLoad: number[] = [];\n const totalChunks = Math.ceil(seriesInfo.totalPoints / config.chunkSize);\n\n // Identify chunks within the buffered viewport\n const startIdx = Math.max(0, Math.floor(buffered.xMin / config.chunkSize));\n const endIdx = Math.min(totalChunks - 1, Math.floor(buffered.xMax / config.chunkSize));\n\n for (let i = startIdx; i <= endIdx; i++) {\n if (!seriesInfo.chunks.has(i) && !seriesInfo.loading.has(i)) {\n chunksToLoad.push(i);\n }\n }\n\n // Load chunks sequentially (limit to avoid flood)\n const maxToLoad = Math.min(chunksToLoad.length, 10);\n for (let i = 0; i < maxToLoad; i++) {\n await loadChunk(seriesInfo, chunksToLoad[i]);\n }\n\n // Preload next adjacent chunk if enabled\n if (config.preloadAdjacent && endIdx + 1 < totalChunks) {\n const nextChunk = endIdx + 1;\n if (!seriesInfo.chunks.has(nextChunk) && !seriesInfo.loading.has(nextChunk)) {\n loadChunk(seriesInfo, nextChunk); // Fire and forget\n }\n }\n }\n }\n\n /**\n * Chunk index range that should stay loaded for the current viewport.\n */\n function getKeepChunkRange(\n seriesInfo: LazyLoadedSeries,\n visibleRange: { xMin: number; xMax: number },\n ): { startChunk: number; endChunk: number } {\n const buffer = config.viewportBuffer;\n const rangeWidth = visibleRange.xMax - visibleRange.xMin;\n const buffered = {\n xMin: visibleRange.xMin - rangeWidth * buffer,\n xMax: visibleRange.xMax + rangeWidth * buffer,\n };\n const totalChunks = Math.ceil(seriesInfo.totalPoints / config.chunkSize);\n const startChunk = Math.max(0, Math.floor(buffered.xMin / config.chunkSize));\n const endChunk = Math.min(totalChunks - 1, Math.floor(buffered.xMax / config.chunkSize));\n return { startChunk, endChunk };\n }\n\n /**\n * Unload distant chunks\n */\n function unloadDistant(): number {\n if (!config.autoUnload || !ctx) return 0;\n\n const visibleRange = getVisibleRange();\n if (!visibleRange) return 0;\n\n let unloadedCount = 0;\n\n for (const [, seriesInfo] of series) {\n let seriesUnloaded = 0;\n const { startChunk, endChunk } = getKeepChunkRange(seriesInfo, visibleRange);\n const visibleSpan = Math.max(1, endChunk - startChunk + 1);\n const thresholdChunks = Math.ceil(config.unloadThreshold * visibleSpan);\n const keepStart = Math.max(0, startChunk - thresholdChunks);\n const keepEnd = endChunk + thresholdChunks;\n\n for (const chunkIndex of Array.from(seriesInfo.chunks.keys())) {\n if (chunkIndex < keepStart || chunkIndex > keepEnd) {\n seriesInfo.chunks.delete(chunkIndex);\n seriesUnloaded++;\n }\n }\n\n // Enforce max chunks limit (LRU eviction)\n if (seriesInfo.chunks.size > config.maxLoadedChunks) {\n const sortedChunks = Array.from(seriesInfo.chunks.entries()).sort(\n (a, b) => a[1].loadedAt - b[1].loadedAt,\n );\n\n const toRemove = seriesInfo.chunks.size - config.maxLoadedChunks;\n for (let i = 0; i < toRemove; i++) {\n seriesInfo.chunks.delete(sortedChunks[i][0]);\n seriesUnloaded++;\n }\n }\n\n if (seriesUnloaded > 0) {\n void updateSeriesData(seriesInfo);\n unloadedCount += seriesUnloaded;\n }\n }\n\n if (config.debug && unloadedCount > 0) {\n console.log(`[LazyLoad] Unloaded ${unloadedCount} chunks`);\n }\n\n return unloadedCount;\n }\n\n /**\n * Register a series\n */\n function registerSeries(seriesId: string, provider: DataProvider): void {\n const totalPoints = provider.getTotalCount();\n \n series.set(seriesId, {\n id: seriesId,\n provider,\n chunks: new Map(),\n loading: new Set(),\n totalPoints,\n });\n\n if (config.debug) {\n console.log(`[LazyLoad] Registered series ${seriesId} with ${totalPoints} points`);\n }\n\n // Fire load start event\n config.onLoadStart({\n seriesId,\n loadedPoints: 0,\n totalPoints,\n progress: 0,\n currentChunk: 0,\n totalChunks: Math.ceil(totalPoints / config.chunkSize),\n });\n\n // Auto-load if enabled\n if (config.autoLoad) {\n loadVisible();\n }\n }\n\n /**\n * Unregister a series\n */\n function unregisterSeries(seriesId: string): void {\n series.delete(seriesId);\n \n if (config.debug) {\n console.log(`[LazyLoad] Unregistered series ${seriesId}`);\n }\n }\n\n /**\n * Load specific range\n */\n async function loadRange(\n seriesId: string,\n startIndex: number,\n endIndex: number\n ): Promise<void> {\n const seriesInfo = series.get(seriesId);\n if (!seriesInfo) return;\n\n const startChunk = getChunkIndex(startIndex);\n const endChunk = getChunkIndex(endIndex);\n\n for (let i = startChunk; i <= endChunk; i++) {\n await loadChunk(seriesInfo, i);\n }\n }\n\n /**\n * Get loading status\n */\n function getLoadingStatus(seriesId: string) {\n const seriesInfo = series.get(seriesId);\n if (!seriesInfo) return null;\n\n const loadedPoints = Array.from(seriesInfo.chunks.values()).reduce(\n (sum, c) => sum + (c.endIndex - c.startIndex),\n 0\n );\n\n return {\n loadedPoints,\n totalPoints: seriesInfo.totalPoints,\n loadedChunks: seriesInfo.chunks.size,\n totalChunks: Math.ceil(seriesInfo.totalPoints / config.chunkSize),\n };\n }\n\n /**\n * Clear all data\n */\n function clear(): void {\n for (const [, seriesInfo] of series) {\n seriesInfo.chunks.clear();\n seriesInfo.loading.clear();\n }\n }\n\n /**\n * Load a data window with optional buffer (viewport-aware windowing API).\n */\n async function setDataWindow(opts: {\n from: number;\n to: number;\n buffer?: number;\n seriesId?: string;\n }): Promise<void> {\n if (!ctx || !config.enabled) return;\n\n const buffer = opts.buffer ?? config.viewportBuffer;\n const span = opts.to - opts.from;\n const startIndex = Math.max(0, Math.floor(opts.from - span * buffer));\n const endIndex = Math.ceil(opts.to + span * buffer);\n\n const targets = opts.seriesId\n ? [[opts.seriesId, series.get(opts.seriesId)] as const]\n : Array.from(series.entries()).map(([id, info]) => [id, info] as const);\n\n for (const [seriesId, seriesInfo] of targets) {\n if (!seriesInfo) continue;\n await loadRange(seriesId, startIndex, endIndex);\n }\n\n if (config.autoUnload) {\n unloadDistant();\n }\n }\n\n // API implementation\n const api: LazyLoadAPI & Record<string, unknown> = {\n registerSeries,\n unregisterSeries,\n loadRange,\n loadVisible,\n unloadDistant,\n setDataWindow,\n getLoadingStatus,\n clear,\n updateConfig(newConfig: Partial<PluginLazyLoadConfig>) {\n Object.assign(config, newConfig);\n },\n };\n\n return {\n manifest,\n\n onInit(pluginCtx: PluginContext) {\n ctx = pluginCtx;\n\n // Attach to chart API\n (ctx.chart as any).lazyLoad = api;\n (ctx.chart as any).setDataWindow = (opts: {\n from: number;\n to: number;\n buffer?: number;\n seriesId?: string;\n }) => setDataWindow(opts);\n\n // Listen for zoom/pan if auto-load enabled\n if (config.autoLoad) {\n ctx.chart.on?.('zoom', () => {\n loadVisible();\n if (config.autoUnload) {\n unloadDistant();\n }\n });\n\n ctx.chart.on?.('pan', () => {\n loadVisible();\n if (config.autoUnload) {\n unloadDistant();\n }\n });\n }\n },\n\n onDestroy(pluginCtx: PluginContext) {\n // Clear all data\n clear();\n\n // Remove from chart API\n delete (pluginCtx.chart as any).lazyLoad;\n delete (pluginCtx.chart as any).setDataWindow;\n\n ctx = null;\n },\n\n api,\n };\n}\n\nexport default PluginLazyLoad;\n\n// Type exports\nexport type {\n PluginLazyLoadConfig,\n LazyLoadAPI,\n DataProvider,\n DataChunk,\n LoadProgressEvent,\n} from './types';\n"],"names":["manifest","DEFAULT_CONFIG","PluginLazyLoad","userConfig","config","ctx","series","getChunkIndex","pointIndex","getVisibleRange","bounds","loadChunk","seriesInfo","chunkIndex","startIndex","endIndex","chunk","updateSeriesData","loadedPoints","sum","c","totalChunks","event","error","_a","_b","sortedChunks","a","b","totalSize","x","y","offset","loadVisible","visibleRange","buffer","rangeWidth","buffered","chunksToLoad","startIdx","endIdx","i","maxToLoad","nextChunk","getKeepChunkRange","startChunk","endChunk","unloadDistant","unloadedCount","seriesUnloaded","visibleSpan","thresholdChunks","keepStart","keepEnd","toRemove","registerSeries","seriesId","provider","totalPoints","unregisterSeries","loadRange","getLoadingStatus","clear","setDataWindow","opts","span","targets","id","info","api","newConfig","pluginCtx","_c","_d"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ChartPlugin } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface PluginReplayConfig {
|
|
4
|
+
/** Series id to replay */
|
|
5
|
+
seriesId: string;
|
|
6
|
+
/** Milliseconds between frames at 1x speed */
|
|
7
|
+
frameMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ReplayAPI {
|
|
10
|
+
play(speed?: number): void;
|
|
11
|
+
pause(): void;
|
|
12
|
+
step(count?: number): void;
|
|
13
|
+
seek(index: number): void;
|
|
14
|
+
getIndex(): number;
|
|
15
|
+
getLength(): number;
|
|
16
|
+
isPlaying(): boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function PluginReplay(config: PluginReplayConfig): ChartPlugin<PluginReplayConfig>;
|
|
19
|
+
export default PluginReplay;
|
package/dist/plugins/tools.js
CHANGED
|
@@ -7,6 +7,12 @@ export type VirtualizationStrategy = "lttb" | "minmax";
|
|
|
7
7
|
export interface PluginVirtualizationConfig {
|
|
8
8
|
/** Enable virtualization (default: true) */
|
|
9
9
|
enabled?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Data fidelity mode (default: `'lod'`).
|
|
12
|
+
* - `'lod'`: downsample to canvas budget (recommended for large datasets).
|
|
13
|
+
* - `'full'`: skip downsampling and send all points to the GPU (opt-out for fidelity-critical views).
|
|
14
|
+
*/
|
|
15
|
+
precision?: "lod" | "full";
|
|
10
16
|
/** Virtualization mode (default: 'lod') */
|
|
11
17
|
mode?: VirtualizationMode;
|
|
12
18
|
/** Target points or 'auto' (default: 'auto') */
|
|
@@ -27,6 +33,12 @@ export interface PluginVirtualizationConfig {
|
|
|
27
33
|
excludeSeries?: string[];
|
|
28
34
|
/** Debug logging (default: false) */
|
|
29
35
|
debug?: boolean;
|
|
36
|
+
/** Viewport buffer as fraction of visible x-range when slicing before downsample (default: 0.5) */
|
|
37
|
+
viewportBuffer?: number;
|
|
38
|
+
/** Downsample off main thread when Workers are available (default: true) */
|
|
39
|
+
useWorker?: boolean;
|
|
40
|
+
/** Point count threshold before using worker path (default: 250000) */
|
|
41
|
+
workerThreshold?: number;
|
|
30
42
|
}
|
|
31
43
|
export interface VirtualizationStats {
|
|
32
44
|
seriesId: string;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
var c = Object.defineProperty;
|
|
2
|
+
var l = (o, e, r) => e in o ? c(o, e, { enumerable: !0, configurable: !0, writable: !0, value: r }) : o[e] = r;
|
|
3
|
+
var n = (o, e, r) => l(o, typeof e != "symbol" ? e + "" : e, r);
|
|
4
|
+
class d {
|
|
5
|
+
constructor(e, r = {}) {
|
|
6
|
+
n(this, "workers", []);
|
|
7
|
+
n(this, "pending", /* @__PURE__ */ new Map());
|
|
8
|
+
n(this, "nextWorker", 0);
|
|
9
|
+
n(this, "poolSize");
|
|
10
|
+
n(this, "syncFallback");
|
|
11
|
+
n(this, "workerFactory");
|
|
12
|
+
n(this, "syncHandler");
|
|
13
|
+
n(this, "destroyed", !1);
|
|
14
|
+
if (this.workerFactory = e, this.poolSize = r.poolSize ?? 2, this.syncFallback = r.syncFallback ?? !0, this.syncHandler = r.syncHandler, typeof Worker < "u")
|
|
15
|
+
for (let s = 0; s < this.poolSize; s++)
|
|
16
|
+
this.spawnWorker();
|
|
17
|
+
}
|
|
18
|
+
removeWorker(e) {
|
|
19
|
+
const r = this.workers.indexOf(e);
|
|
20
|
+
r >= 0 && this.workers.splice(r, 1), e.terminate();
|
|
21
|
+
}
|
|
22
|
+
workerErrorMessage(e) {
|
|
23
|
+
const r = e;
|
|
24
|
+
return typeof r.message == "string" && r.message ? r.message : "Worker error";
|
|
25
|
+
}
|
|
26
|
+
rejectPendingWithSyncFallback(e, r) {
|
|
27
|
+
this.removeWorker(e);
|
|
28
|
+
const s = this.workerErrorMessage(r), t = [...this.pending.entries()];
|
|
29
|
+
this.pending.clear();
|
|
30
|
+
for (const [, i] of t) {
|
|
31
|
+
if (this.syncFallback && this.syncHandler) {
|
|
32
|
+
Promise.resolve(this.syncHandler(i.task)).then(i.resolve).catch(i.reject);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
i.reject(new Error(s));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
spawnWorker() {
|
|
39
|
+
const e = this.workerFactory();
|
|
40
|
+
e.onmessage = (r) => {
|
|
41
|
+
const s = r.data;
|
|
42
|
+
if (!(s != null && s.id)) return;
|
|
43
|
+
const t = this.pending.get(s.id);
|
|
44
|
+
if (t) {
|
|
45
|
+
if (this.pending.delete(s.id), s.type === "error" || s.error) {
|
|
46
|
+
if (this.syncFallback && this.syncHandler) {
|
|
47
|
+
Promise.resolve(this.syncHandler(t.task)).then(t.resolve).catch(t.reject);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
t.reject(new Error(s.error ?? "Worker task failed"));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
t.resolve(s);
|
|
54
|
+
}
|
|
55
|
+
}, e.onerror = (r) => {
|
|
56
|
+
this.rejectPendingWithSyncFallback(e, r);
|
|
57
|
+
}, this.workers.push(e);
|
|
58
|
+
}
|
|
59
|
+
/** Dispatch a task to the pool. Falls back to syncHandler when no workers exist. */
|
|
60
|
+
run(e) {
|
|
61
|
+
return this.destroyed ? Promise.reject(new Error("WorkerPool destroyed")) : this.workers.length === 0 ? this.syncFallback && this.syncHandler ? Promise.resolve(this.syncHandler(e)) : Promise.reject(new Error("No workers available and sync fallback disabled")) : new Promise((r, s) => {
|
|
62
|
+
this.pending.set(e.id, { resolve: r, reject: s, task: e });
|
|
63
|
+
const t = this.nextWorker;
|
|
64
|
+
this.nextWorker = (this.nextWorker + 1) % this.workers.length, this.workers[t].postMessage(e);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
size() {
|
|
68
|
+
return this.workers.length;
|
|
69
|
+
}
|
|
70
|
+
pendingCount() {
|
|
71
|
+
return this.pending.size;
|
|
72
|
+
}
|
|
73
|
+
destroy() {
|
|
74
|
+
this.destroyed = !0;
|
|
75
|
+
for (const e of this.workers)
|
|
76
|
+
e.terminate();
|
|
77
|
+
this.workers = [], this.pending.clear();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
let a = 0;
|
|
81
|
+
function k(o = "task") {
|
|
82
|
+
return a += 1, `${o}-${a}-${Date.now()}`;
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
d as W,
|
|
86
|
+
k as n
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=pool-15HMCNv8.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pool-15HMCNv8.js","sources":["../src/workers/pool.ts"],"sourcesContent":["/**\n * Generic worker pool with round-robin dispatch and sync fallback.\n * @module workers/pool\n */\n\nexport interface WorkerPoolOptions {\n /** Number of workers (default: 2) */\n poolSize?: number;\n /** Run tasks on main thread when Worker is unavailable */\n syncFallback?: boolean;\n}\n\nexport class WorkerPool<TMessage extends { id: string }, TResult = unknown> {\n private workers: Worker[] = [];\n private pending = new Map<\n string,\n {\n resolve: (value: TResult) => void;\n reject: (error: Error) => void;\n task: TMessage;\n }\n >();\n private nextWorker = 0;\n private readonly poolSize: number;\n private readonly syncFallback: boolean;\n private readonly workerFactory: () => Worker;\n private readonly syncHandler?: (task: TMessage) => TResult | Promise<TResult>;\n private destroyed = false;\n\n constructor(\n workerFactory: () => Worker,\n options: WorkerPoolOptions & {\n syncHandler?: (task: TMessage) => TResult | Promise<TResult>;\n } = {},\n ) {\n this.workerFactory = workerFactory;\n this.poolSize = options.poolSize ?? 2;\n this.syncFallback = options.syncFallback ?? true;\n this.syncHandler = options.syncHandler;\n\n if (typeof Worker !== \"undefined\") {\n for (let i = 0; i < this.poolSize; i++) {\n this.spawnWorker();\n }\n }\n }\n\n private removeWorker(worker: Worker): void {\n const index = this.workers.indexOf(worker);\n if (index >= 0) this.workers.splice(index, 1);\n worker.terminate();\n }\n\n private workerErrorMessage(event: Event): string {\n const maybe = event as { message?: string };\n if (typeof maybe.message === \"string\" && maybe.message) return maybe.message;\n return \"Worker error\";\n }\n\n private rejectPendingWithSyncFallback(failedWorker: Worker, event: Event): void {\n this.removeWorker(failedWorker);\n const message = this.workerErrorMessage(event);\n const entries = [...this.pending.entries()];\n this.pending.clear();\n\n for (const [, entry] of entries) {\n if (this.syncFallback && this.syncHandler) {\n Promise.resolve(this.syncHandler(entry.task)).then(entry.resolve).catch(entry.reject);\n continue;\n }\n entry.reject(new Error(message));\n }\n }\n\n private spawnWorker(): void {\n const worker = this.workerFactory();\n worker.onmessage = (event: MessageEvent) => {\n const data = event.data as { id?: string; type?: string; error?: string };\n if (!data?.id) return;\n\n const task = this.pending.get(data.id);\n if (!task) return;\n\n this.pending.delete(data.id);\n\n if (data.type === \"error\" || data.error) {\n if (this.syncFallback && this.syncHandler) {\n Promise.resolve(this.syncHandler(task.task)).then(task.resolve).catch(task.reject);\n return;\n }\n task.reject(new Error(data.error ?? \"Worker task failed\"));\n return;\n }\n\n task.resolve(data as TResult);\n };\n\n worker.onerror = (event) => {\n this.rejectPendingWithSyncFallback(worker, event);\n };\n\n this.workers.push(worker);\n }\n\n /** Dispatch a task to the pool. Falls back to syncHandler when no workers exist. */\n run(task: TMessage): Promise<TResult> {\n if (this.destroyed) {\n return Promise.reject(new Error(\"WorkerPool destroyed\"));\n }\n\n if (this.workers.length === 0) {\n if (this.syncFallback && this.syncHandler) {\n return Promise.resolve(this.syncHandler(task));\n }\n return Promise.reject(new Error(\"No workers available and sync fallback disabled\"));\n }\n\n return new Promise((resolve, reject) => {\n this.pending.set(task.id, { resolve, reject, task });\n const workerIndex = this.nextWorker;\n this.nextWorker = (this.nextWorker + 1) % this.workers.length;\n this.workers[workerIndex].postMessage(task);\n });\n }\n\n size(): number {\n return this.workers.length;\n }\n\n pendingCount(): number {\n return this.pending.size;\n }\n\n destroy(): void {\n this.destroyed = true;\n for (const worker of this.workers) {\n worker.terminate();\n }\n this.workers = [];\n this.pending.clear();\n }\n}\n\nlet taskCounter = 0;\n\n/** Generate a unique task id */\nexport function nextTaskId(prefix = \"task\"): string {\n taskCounter += 1;\n return `${prefix}-${taskCounter}-${Date.now()}`;\n}\n"],"names":["WorkerPool","workerFactory","options","__publicField","i","worker","index","event","maybe","failedWorker","message","entries","entry","data","task","resolve","reject","workerIndex","taskCounter","nextTaskId","prefix"],"mappings":"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"}
|