velo-plot 1.12.0 → 1.19.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.
Files changed (153) hide show
  1. package/README.md +14 -0
  2. package/dist/{index.core-xiNcuFfJ.js → ChartCore-DQ691eXH.js} +1807 -1287
  3. package/dist/ChartCore-DQ691eXH.js.map +1 -0
  4. package/dist/assets/downsample.worker-C70QJ04y.js +2 -0
  5. package/dist/assets/downsample.worker-C70QJ04y.js.map +1 -0
  6. package/dist/assets/indicator.worker-bv0zC4EX.js +2 -0
  7. package/dist/assets/indicator.worker-bv0zC4EX.js.map +1 -0
  8. package/dist/core/OverlayRenderer.d.ts +2 -0
  9. package/dist/core/chart/ChartAlerts.d.ts +42 -0
  10. package/dist/core/chart/ChartCore.d.ts +20 -1
  11. package/dist/core/chart/ChartRenderLoop.d.ts +4 -2
  12. package/dist/core/chart/ChartRenderer.d.ts +3 -2
  13. package/dist/core/chart/candlestickMarkers.d.ts +23 -0
  14. package/dist/core/chart/exporter/SVGExporter.d.ts +7 -2
  15. package/dist/core/chart/heikinAshi.d.ts +10 -0
  16. package/dist/core/chart/series/types.d.ts +2 -2
  17. package/dist/core/chart/types.d.ts +9 -0
  18. package/dist/core/indicator/addIndicator.d.ts +36 -0
  19. package/dist/core/indicator/index.d.ts +2 -0
  20. package/dist/core/indicator/indicatorPresets.d.ts +37 -0
  21. package/dist/core/render/pixelSnap.d.ts +8 -0
  22. package/dist/core/series/Series.d.ts +3 -0
  23. package/dist/core/stacked/index.d.ts +3 -1
  24. package/dist/core/stacked/paneResize.d.ts +15 -18
  25. package/dist/core/stacked/stackExport.d.ts +18 -0
  26. package/dist/core/stacked/types.d.ts +42 -5
  27. package/dist/core/time/TimeScale.d.ts +31 -0
  28. package/dist/createStackedChart-BPooTQ9z.js +1164 -0
  29. package/dist/createStackedChart-BPooTQ9z.js.map +1 -0
  30. package/dist/gpu/adapter/gpuRenderer.d.ts +21 -0
  31. package/dist/gpu/backends/webgpu/WebGPUBackend.d.ts +2 -1
  32. package/dist/gpuRenderer-BpaMYz-k.js +1848 -0
  33. package/dist/gpuRenderer-BpaMYz-k.js.map +1 -0
  34. package/dist/index-CUGS98p4.js +277 -0
  35. package/dist/index-CUGS98p4.js.map +1 -0
  36. package/dist/index-D7dTq6VB.js +503 -0
  37. package/dist/index-D7dTq6VB.js.map +1 -0
  38. package/dist/{index-Ce2JgkH_.js → index-DoYxf1cV.js} +86 -43
  39. package/dist/{index-Ce2JgkH_.js.map → index-DoYxf1cV.js.map} +1 -1
  40. package/dist/index.core-BtGFYMOu.js +167 -0
  41. package/dist/index.core-BtGFYMOu.js.map +1 -0
  42. package/dist/index.d.ts +12 -4
  43. package/dist/indicators--eJGmVox.js +302 -0
  44. package/dist/indicators--eJGmVox.js.map +1 -0
  45. package/dist/math-Ddm2EZjj.js +85 -0
  46. package/dist/math-Ddm2EZjj.js.map +1 -0
  47. package/dist/plugins/analysis.js +543 -818
  48. package/dist/plugins/analysis.js.map +1 -1
  49. package/dist/plugins/anomaly-detection.d.ts +1 -0
  50. package/dist/plugins/anomaly-detection.js +221 -0
  51. package/dist/plugins/anomaly-detection.js.map +1 -0
  52. package/dist/plugins/broken-axis.d.ts +3 -0
  53. package/dist/plugins/broken-axis.js +262 -0
  54. package/dist/plugins/broken-axis.js.map +1 -0
  55. package/dist/plugins/caching.d.ts +3 -0
  56. package/dist/plugins/caching.js +187 -0
  57. package/dist/plugins/caching.js.map +1 -0
  58. package/dist/plugins/data-transform.d.ts +3 -0
  59. package/dist/plugins/data-transform.js +146 -0
  60. package/dist/plugins/data-transform.js.map +1 -0
  61. package/dist/plugins/debug.js +12 -11
  62. package/dist/plugins/debug.js.map +1 -1
  63. package/dist/plugins/drawing-tools/index.d.ts +16 -0
  64. package/dist/plugins/forecasting.d.ts +1 -0
  65. package/dist/plugins/forecasting.js +236 -0
  66. package/dist/plugins/forecasting.js.map +1 -0
  67. package/dist/plugins/gpu.js +202 -1895
  68. package/dist/plugins/gpu.js.map +1 -1
  69. package/dist/plugins/index.d.ts +2 -0
  70. package/dist/plugins/keyboard/index.d.ts +1 -7
  71. package/dist/plugins/keyboard.js +1 -1
  72. package/dist/plugins/latex.d.ts +1 -0
  73. package/dist/plugins/latex.js +454 -0
  74. package/dist/plugins/latex.js.map +1 -0
  75. package/dist/plugins/lazy-load/types.d.ts +7 -0
  76. package/dist/plugins/lazy-load.d.ts +3 -0
  77. package/dist/plugins/lazy-load.js +212 -0
  78. package/dist/plugins/lazy-load.js.map +1 -0
  79. package/dist/plugins/ml-integration.d.ts +3 -0
  80. package/dist/plugins/ml-integration.js +355 -0
  81. package/dist/plugins/ml-integration.js.map +1 -0
  82. package/dist/plugins/offscreen.d.ts +3 -0
  83. package/dist/plugins/offscreen.js +169 -0
  84. package/dist/plugins/offscreen.js.map +1 -0
  85. package/dist/plugins/pattern-recognition.d.ts +3 -0
  86. package/dist/plugins/pattern-recognition.js +690 -0
  87. package/dist/plugins/pattern-recognition.js.map +1 -0
  88. package/dist/plugins/radar.d.ts +3 -0
  89. package/dist/plugins/radar.js +103 -0
  90. package/dist/plugins/radar.js.map +1 -0
  91. package/dist/plugins/regression.d.ts +3 -0
  92. package/dist/plugins/regression.js +634 -0
  93. package/dist/plugins/regression.js.map +1 -0
  94. package/dist/plugins/replay/index.d.ts +19 -0
  95. package/dist/plugins/roi.d.ts +1 -0
  96. package/dist/plugins/roi.js +223 -0
  97. package/dist/plugins/roi.js.map +1 -0
  98. package/dist/plugins/snapshot/types.d.ts +1 -1
  99. package/dist/plugins/snapshot.d.ts +3 -0
  100. package/dist/plugins/snapshot.js +90 -0
  101. package/dist/plugins/snapshot.js.map +1 -0
  102. package/dist/plugins/sync/index.d.ts +2 -1
  103. package/dist/plugins/sync.js +13 -269
  104. package/dist/plugins/sync.js.map +1 -1
  105. package/dist/plugins/video-recorder.d.ts +3 -0
  106. package/dist/plugins/video-recorder.js +104 -0
  107. package/dist/plugins/video-recorder.js.map +1 -0
  108. package/dist/plugins/virtualization/types.d.ts +12 -0
  109. package/dist/plugins/virtualization.d.ts +3 -0
  110. package/dist/plugins/virtualization.js +6 -0
  111. package/dist/plugins/virtualization.js.map +1 -0
  112. package/dist/pool-15HMCNv8.js +88 -0
  113. package/dist/pool-15HMCNv8.js.map +1 -0
  114. package/dist/react.d.ts +1 -0
  115. package/dist/react.js +31 -0
  116. package/dist/react.js.map +1 -0
  117. package/dist/renderer/CandlestickRenderer.d.ts +1 -1
  118. package/dist/renderer/ChartSeriesRenderer.d.ts +6 -0
  119. package/dist/renderer/GpuChartRenderer.d.ts +23 -0
  120. package/dist/renderer/index.d.ts +3 -0
  121. package/dist/renderer/spike/WebGLGridSpike.d.ts +40 -0
  122. package/dist/testing/baselines/v1.15.0.json.d.ts +51 -0
  123. package/dist/testing/gridSpikeBenchmark.d.ts +49 -0
  124. package/dist/testing/index.d.ts +5 -0
  125. package/dist/testing/rendererBenchmark.d.ts +13 -0
  126. package/dist/testing/stage1BrowserBench.d.ts +42 -0
  127. package/dist/types.d.ts +21 -1
  128. package/dist/utils-BK5UUXxj.js +172 -0
  129. package/dist/utils-BK5UUXxj.js.map +1 -0
  130. package/dist/velo-plot.full.js +1382 -5932
  131. package/dist/velo-plot.full.js.map +1 -1
  132. package/dist/velo-plot.js +26 -25
  133. package/dist/velo-plot.js.map +1 -1
  134. package/dist/workers/downsample.d.ts +38 -0
  135. package/dist/workers/downsampleAsync.d.ts +6 -0
  136. package/dist/workers/indicator.worker.d.ts +32 -0
  137. package/dist/workers/indicatorsAsync.d.ts +26 -0
  138. package/dist/workers/pool.d.ts +36 -0
  139. package/package.json +19 -2
  140. package/dist/core/OverlayRenderer.test.d.ts +0 -1
  141. package/dist/core/chart/ChartScaling.test.d.ts +0 -1
  142. package/dist/core/chart/NavigationUtils.test.d.ts +0 -1
  143. package/dist/core/format/axisFormat.test.d.ts +0 -1
  144. package/dist/core/indicator/buildIndicatorSeries.test.d.ts +0 -1
  145. package/dist/core/series/SeriesBounds.test.d.ts +0 -1
  146. package/dist/core/stacked/createStackedChart.test.d.ts +0 -1
  147. package/dist/core/stacked/paneAxis.test.d.ts +0 -1
  148. package/dist/core/stacked/paneResize.test.d.ts +0 -1
  149. package/dist/core/sync/index.test.d.ts +0 -1
  150. package/dist/index.core-xiNcuFfJ.js.map +0 -1
  151. package/dist/plugins/tools/tooltip/format.test.d.ts +0 -1
  152. package/dist/utils-Dbro9-qM.js +0 -254
  153. package/dist/utils-Dbro9-qM.js.map +0 -1
@@ -1,285 +1,29 @@
1
- var u = Object.defineProperty;
2
- var l = (r, t, i) => t in r ? u(r, t, { enumerable: !0, configurable: !0, writable: !0, value: i }) : r[t] = i;
3
- var o = (r, t, i) => l(r, typeof t != "symbol" ? t + "" : t, i);
4
- class f {
5
- constructor(t) {
6
- o(this, "charts", /* @__PURE__ */ new Map());
7
- o(this, "options");
8
- o(this, "eventHandlers", /* @__PURE__ */ new Map());
9
- o(this, "isUpdating", !1);
10
- o(this, "debounceTimers", /* @__PURE__ */ new Map());
11
- o(this, "rafTimers", /* @__PURE__ */ new Map());
12
- this.options = {
13
- axis: "x",
14
- syncCursor: !0,
15
- syncSelection: !1,
16
- syncZoom: !0,
17
- syncPan: !0,
18
- debounce: 0,
19
- bidirectional: !0,
20
- masterId: void 0,
21
- ...t
22
- };
23
- }
24
- add(t) {
25
- const i = t.getId();
26
- return this.charts.has(i) ? (console.warn(`[ChartGroup] Chart ${i} is already in the group`), this) : (this.charts.set(i, t), this.attachEventHandlers(t), this);
27
- }
28
- addAll(...t) {
29
- for (const i of t) this.add(i);
30
- return this;
31
- }
32
- remove(t) {
33
- const i = t.getId();
34
- return this.charts.has(i) ? (this.detachEventHandlers(t), this.charts.delete(i), this) : this;
35
- }
36
- getCharts() {
37
- return Array.from(this.charts.values());
38
- }
39
- size() {
40
- return this.charts.size;
41
- }
42
- has(t) {
43
- return this.charts.has(t.getId());
44
- }
45
- syncAxis(t) {
46
- return this.options.axis = t, this;
47
- }
48
- syncZoom(t) {
49
- return this.options.syncZoom === t ? this : this.updateOptions({ syncZoom: t });
50
- }
51
- syncPan(t) {
52
- return this.options.syncPan === t ? this : this.updateOptions({ syncPan: t });
53
- }
54
- syncCursor(t) {
55
- return this.options.syncCursor = t, this;
56
- }
57
- syncSelection(t) {
58
- return this.options.syncSelection = t, this;
59
- }
60
- /** Update sync options and re-bind event handlers on all charts */
61
- updateOptions(t) {
62
- Object.assign(this.options, t);
63
- for (const i of this.charts.values())
64
- this.detachEventHandlers(i), this.attachEventHandlers(i);
65
- return this;
66
- }
67
- getOptions() {
68
- return { ...this.options };
69
- }
70
- syncTo(t, i) {
71
- this.propagateZoom(i || "", t);
72
- }
73
- resetAll() {
74
- this.batch(() => {
75
- for (const t of this.charts.values())
76
- t.fit ? t.fit() : t.zoom({ x: void 0, y: void 0 });
77
- });
78
- }
79
- fitAll(t) {
80
- this.batch(() => {
81
- let i = t == null ? void 0 : t.x;
82
- const n = this.options.masterId, s = n ? this.charts.get(n) : this.charts.values().next().value;
83
- if (!i && (s != null && s.fit)) {
84
- s.fit({ padding: t == null ? void 0 : t.padding });
85
- const e = s.getViewBounds();
86
- this.hasValidViewBounds(e) && (i = [e.xMin, e.xMax]);
87
- }
88
- for (const [e, a] of this.charts.entries())
89
- a.fit && e !== n && a.fit(
90
- i ? { x: i, padding: t == null ? void 0 : t.padding } : { padding: t == null ? void 0 : t.padding }
91
- );
92
- });
93
- }
94
- batch(t) {
95
- const i = this.isUpdating;
96
- this.isUpdating = !0;
97
- try {
98
- return t();
99
- } finally {
100
- this.isUpdating = i;
101
- }
102
- }
103
- clearAllSelections() {
104
- var t;
105
- for (const i of this.charts.values())
106
- (t = i.clearSelection) == null || t.call(i);
107
- }
108
- destroy() {
109
- for (const t of this.charts.values())
110
- this.detachEventHandlers(t);
111
- this.charts.clear(), this.eventHandlers.clear();
112
- for (const t of this.debounceTimers.values()) clearTimeout(t);
113
- this.debounceTimers.clear();
114
- for (const t of this.rafTimers.values()) cancelAnimationFrame(t);
115
- this.rafTimers.clear();
116
- }
117
- isSyncSource(t) {
118
- return this.options.bidirectional ? !0 : this.options.masterId ? t === this.options.masterId : !0;
119
- }
120
- canPropagateFrom(t) {
121
- return this.options.bidirectional ? !0 : this.options.masterId ? t === this.options.masterId : !0;
122
- }
123
- hasValidViewBounds(t) {
124
- const i = t.xMax - t.xMin, n = t.yMax - t.yMin;
125
- return !(!Number.isFinite(i) || !Number.isFinite(n) || i <= 0 && n <= 0);
126
- }
127
- attachEventHandlers(t) {
128
- const i = t.getId(), n = /* @__PURE__ */ new Map();
129
- if (this.options.syncZoom && this.isSyncSource(i)) {
130
- const s = (...e) => {
131
- this.handleZoom(i, e[0]);
132
- };
133
- t.on("zoom", s), n.set("zoom", s);
134
- }
135
- if (this.options.syncPan && this.isSyncSource(i)) {
136
- const s = (...e) => {
137
- this.handlePan(i, e[0]);
138
- };
139
- t.on("pan", s), n.set("pan", s);
140
- }
141
- if (this.options.syncCursor) {
142
- const s = (...e) => {
143
- this.handleCursor(i, e[0]);
144
- };
145
- t.on("hover", s), n.set("hover", s);
146
- }
147
- if (this.options.syncSelection) {
148
- const s = (...e) => {
149
- this.handleSelection(i, e[0]);
150
- };
151
- t.on("selectionChange", s), n.set("selectionChange", s);
152
- }
153
- this.eventHandlers.set(i, n);
154
- }
155
- detachEventHandlers(t) {
156
- const i = t.getId(), n = this.eventHandlers.get(i);
157
- if (n) {
158
- for (const [s, e] of n.entries())
159
- t.off(s, e);
160
- this.eventHandlers.delete(i);
161
- }
162
- }
163
- handleZoom(t, i) {
164
- if (this.isUpdating || !this.canPropagateFrom(t)) return;
165
- const n = this.charts.get(t);
166
- if (n && !this.hasValidViewBounds(n.getViewBounds())) return;
167
- const s = {};
168
- if ((this.options.axis === "x" || this.options.axis === "xy") && (s.xMin = i.x[0], s.xMax = i.x[1]), (this.options.axis === "y" || this.options.axis === "xy") && (s.yMin = i.y[0], s.yMax = i.y[1]), s.xMin !== void 0 && s.xMax !== void 0) {
169
- const e = s.xMax - s.xMin;
170
- if (!Number.isFinite(e) || e <= 0) return;
171
- }
172
- this.scheduleSyncAction(`zoom-${t}`, () => {
173
- this.propagateZoom(t, s);
174
- });
175
- }
176
- handlePan(t, i) {
177
- if (this.isUpdating || !this.canPropagateFrom(t)) return;
178
- const n = this.options.axis === "x" || this.options.axis === "xy" ? i.deltaX : 0, s = this.options.axis === "y" || this.options.axis === "xy" ? i.deltaY : 0;
179
- n === 0 && s === 0 || this.scheduleSyncAction(`pan-${t}`, () => {
180
- this.propagatePan(t, n, s);
181
- });
182
- }
183
- handleCursor(t, i) {
184
- var n, s;
185
- if (!this.isUpdating)
186
- for (const [e, a] of this.charts.entries())
187
- e !== t && (i != null && i.point ? (n = a.setExternalCursor) == null || n.call(a, i.point.x, i.point.y) : (s = a.clearExternalCursor) == null || s.call(a));
188
- }
189
- handleSelection(t, i) {
190
- this.isUpdating || this.options.syncSelection;
191
- }
192
- propagateZoom(t, i) {
193
- if (!this.isUpdating) {
194
- this.isUpdating = !0;
195
- try {
196
- for (const [n, s] of this.charts.entries()) {
197
- if (n === t) continue;
198
- const e = { animate: !1 };
199
- i.xMin !== void 0 && i.xMax !== void 0 && (e.x = [i.xMin, i.xMax]), i.yMin !== void 0 && i.yMax !== void 0 && (e.y = [i.yMin, i.yMax]), s.zoom(e);
200
- }
201
- } finally {
202
- this.isUpdating = !1;
203
- }
204
- }
205
- }
206
- propagatePan(t, i, n) {
207
- if (!this.isUpdating) {
208
- this.isUpdating = !0;
209
- try {
210
- const s = this.charts.get(t);
211
- if (!s || !this.hasValidViewBounds(s.getViewBounds())) return;
212
- const e = s.getViewBounds();
213
- for (const [a, d] of this.charts.entries()) {
214
- if (a === t) continue;
215
- const h = { animate: !1 };
216
- (this.options.axis === "x" || this.options.axis === "xy") && (h.x = [e.xMin, e.xMax]), (this.options.axis === "y" || this.options.axis === "xy") && (h.y = [e.yMin, e.yMax]), (h.x || h.y) && d.zoom(h);
217
- }
218
- } finally {
219
- this.isUpdating = !1;
220
- }
221
- }
222
- }
223
- scheduleSyncAction(t, i) {
224
- if (this.options.debounce > 0) {
225
- this.debounceAction(t, i);
226
- return;
227
- }
228
- if (this.rafTimers.has(t)) return;
229
- const n = requestAnimationFrame(() => {
230
- this.rafTimers.delete(t), i();
231
- });
232
- this.rafTimers.set(t, n);
233
- }
234
- debounceAction(t, i) {
235
- const n = this.debounceTimers.get(t);
236
- n && clearTimeout(n);
237
- const s = window.setTimeout(() => {
238
- this.debounceTimers.delete(t), i();
239
- }, this.options.debounce);
240
- this.debounceTimers.set(t, s);
241
- }
242
- }
243
- function c(r, t) {
244
- const i = new f(t);
245
- return i.addAll(...r), i;
246
- }
247
- function y(r, t, i) {
248
- return c([r, t], i);
249
- }
250
- function m(r, t, i = "x") {
251
- return c([r, t], {
252
- axis: i,
253
- bidirectional: !1,
254
- masterId: r.getId(),
255
- syncCursor: !0,
256
- syncZoom: !0,
257
- syncPan: !0
258
- });
259
- }
260
- const p = {
1
+ import { C as c, c as s, a as i, l } from "../index-CUGS98p4.js";
2
+ const n = {
261
3
  name: "velo-plot-sync",
262
4
  version: "1.0.0",
263
5
  description: "Multi-chart synchronization for velo-plot",
264
6
  provides: ["interaction"],
265
7
  tags: ["sync", "multi-chart", "coordination"]
266
8
  };
267
- function g(r = {}) {
9
+ function r(o = {}) {
268
10
  return {
269
- manifest: p,
11
+ manifest: n,
270
12
  onInit(t) {
271
- r.groupId;
13
+ console.warn(
14
+ "[PluginSync] Deprecated: use ChartGroup / createChartGroup from the core sync module instead."
15
+ );
272
16
  },
273
17
  onDestroy(t) {
274
18
  }
275
19
  };
276
20
  }
277
21
  export {
278
- f as ChartGroup,
279
- g as PluginSync,
280
- c as createChartGroup,
281
- m as createMasterSlave,
282
- g as default,
283
- y as linkCharts
22
+ c as ChartGroup,
23
+ r as PluginSync,
24
+ s as createChartGroup,
25
+ i as createMasterSlave,
26
+ r as default,
27
+ l as linkCharts
284
28
  };
285
29
  //# sourceMappingURL=sync.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync.js","sources":["../../src/core/sync/index.ts","../../src/plugins/sync/index.ts"],"sourcesContent":["/**\n * Sci Plot - Chart Synchronization Module\n *\n * @module sync\n */\n\nimport type { Bounds, Range } from \"../../types\";\n\nexport type SyncAxis = 'x' | 'y' | 'xy' | 'none';\n\nexport interface SyncOptions {\n axis?: SyncAxis;\n syncCursor?: boolean;\n syncSelection?: boolean;\n syncZoom?: boolean;\n syncPan?: boolean;\n debounce?: number;\n bidirectional?: boolean;\n /** When set, used for fitAll; with bidirectional sync any pane can drive pan/zoom */\n masterId?: string;\n}\n\nexport interface ChartLike {\n getId(): string;\n getViewBounds(): Bounds;\n zoom(options: { x?: Range; y?: Range; animate?: boolean }): void;\n pan(dx: number, dy: number): void;\n fit?(options?: { x?: Range; y?: Range; padding?: number | { x?: number; y?: number } }): void;\n getCursorPosition?(): { x: number; y: number } | null;\n setExternalCursor?(x: number, y: number): void;\n clearExternalCursor?(): void;\n getSelectedPoints?(): { seriesId: string; indices: number[] }[];\n selectPoints?(points: { seriesId: string; indices: number[] }[]): void;\n clearSelection?(): void;\n on(event: string, callback: (...args: unknown[]) => void): void;\n off(event: string, callback: (...args: unknown[]) => void): void;\n}\n\nexport interface SyncEvent {\n sourceId: string;\n type: 'zoom' | 'pan' | 'cursor' | 'selection' | 'bounds';\n data: unknown;\n}\n\ntype RequiredSyncOptions = Required<Omit<SyncOptions, 'masterId'>> & Pick<SyncOptions, 'masterId'>;\n\nexport class ChartGroup {\n private charts: Map<string, ChartLike> = new Map();\n private options: RequiredSyncOptions;\n private eventHandlers: Map<string, Map<string, (...args: unknown[]) => void>> = new Map();\n private isUpdating: boolean = false;\n private debounceTimers: Map<string, number> = new Map();\n private rafTimers: Map<string, number> = new Map();\n\n constructor(options?: SyncOptions) {\n this.options = {\n axis: 'x',\n syncCursor: true,\n syncSelection: false,\n syncZoom: true,\n syncPan: true,\n debounce: 0,\n bidirectional: true,\n masterId: undefined,\n ...options,\n };\n }\n\n add(chart: ChartLike): this {\n const chartId = chart.getId();\n if (this.charts.has(chartId)) {\n console.warn(`[ChartGroup] Chart ${chartId} is already in the group`);\n return this;\n }\n this.charts.set(chartId, chart);\n this.attachEventHandlers(chart);\n return this;\n }\n\n addAll(...charts: ChartLike[]): this {\n for (const chart of charts) this.add(chart);\n return this;\n }\n\n remove(chart: ChartLike): this {\n const chartId = chart.getId();\n if (!this.charts.has(chartId)) return this;\n this.detachEventHandlers(chart);\n this.charts.delete(chartId);\n return this;\n }\n\n getCharts(): ChartLike[] {\n return Array.from(this.charts.values());\n }\n\n size(): number {\n return this.charts.size;\n }\n\n has(chart: ChartLike): boolean {\n return this.charts.has(chart.getId());\n }\n\n syncAxis(axis: SyncAxis): this {\n this.options.axis = axis;\n return this;\n }\n\n syncZoom(enabled: boolean): this {\n if (this.options.syncZoom === enabled) return this;\n return this.updateOptions({ syncZoom: enabled });\n }\n\n syncPan(enabled: boolean): this {\n if (this.options.syncPan === enabled) return this;\n return this.updateOptions({ syncPan: enabled });\n }\n\n syncCursor(enabled: boolean): this {\n this.options.syncCursor = enabled;\n return this;\n }\n\n syncSelection(enabled: boolean): this {\n this.options.syncSelection = enabled;\n return this;\n }\n\n /** Update sync options and re-bind event handlers on all charts */\n updateOptions(partial: Partial<SyncOptions>): this {\n Object.assign(this.options, partial);\n for (const chart of this.charts.values()) {\n this.detachEventHandlers(chart);\n this.attachEventHandlers(chart);\n }\n return this;\n }\n\n getOptions(): Readonly<SyncOptions> {\n return { ...this.options };\n }\n\n syncTo(bounds: Partial<Bounds>, excludeChartId?: string): void {\n this.propagateZoom(excludeChartId || '', bounds);\n }\n\n resetAll(): void {\n this.batch(() => {\n for (const chart of this.charts.values()) {\n if (chart.fit) chart.fit();\n else chart.zoom({ x: undefined, y: undefined });\n }\n });\n }\n\n fitAll(options?: { x?: Range; padding?: number }): void {\n this.batch(() => {\n let sharedX: Range | undefined = options?.x;\n const masterId = this.options.masterId;\n const master = masterId ? this.charts.get(masterId) : this.charts.values().next().value;\n\n if (!sharedX && master?.fit) {\n master.fit({ padding: options?.padding });\n const b = master.getViewBounds();\n if (this.hasValidViewBounds(b)) sharedX = [b.xMin, b.xMax];\n }\n\n for (const [chartId, chart] of this.charts.entries()) {\n if (!chart.fit) continue;\n if (chartId === masterId) continue;\n chart.fit(\n sharedX\n ? { x: sharedX, padding: options?.padding }\n : { padding: options?.padding },\n );\n }\n });\n }\n\n batch<T>(fn: () => T): T {\n const prev = this.isUpdating;\n this.isUpdating = true;\n try {\n return fn();\n } finally {\n this.isUpdating = prev;\n }\n }\n\n clearAllSelections(): void {\n for (const chart of this.charts.values()) {\n chart.clearSelection?.();\n }\n }\n\n destroy(): void {\n for (const chart of this.charts.values()) {\n this.detachEventHandlers(chart);\n }\n this.charts.clear();\n this.eventHandlers.clear();\n for (const timerId of this.debounceTimers.values()) clearTimeout(timerId);\n this.debounceTimers.clear();\n for (const rafId of this.rafTimers.values()) cancelAnimationFrame(rafId);\n this.rafTimers.clear();\n }\n\n private isSyncSource(chartId: string): boolean {\n if (this.options.bidirectional) return true;\n if (this.options.masterId) return chartId === this.options.masterId;\n return true;\n }\n\n private canPropagateFrom(sourceId: string): boolean {\n if (this.options.bidirectional) return true;\n if (this.options.masterId) return sourceId === this.options.masterId;\n return true;\n }\n\n private hasValidViewBounds(bounds: Bounds): boolean {\n const xSpan = bounds.xMax - bounds.xMin;\n const ySpan = bounds.yMax - bounds.yMin;\n if (!Number.isFinite(xSpan) || !Number.isFinite(ySpan)) return false;\n if (xSpan <= 0 && ySpan <= 0) return false;\n return true;\n }\n\n private attachEventHandlers(chart: ChartLike): void {\n const chartId = chart.getId();\n const handlers = new Map<string, (...args: unknown[]) => void>();\n\n if (this.options.syncZoom && this.isSyncSource(chartId)) {\n const zoomHandler = (...args: unknown[]) => {\n this.handleZoom(chartId, args[0] as { x: Range; y: Range });\n };\n chart.on('zoom', zoomHandler);\n handlers.set('zoom', zoomHandler);\n }\n\n if (this.options.syncPan && this.isSyncSource(chartId)) {\n const panHandler = (...args: unknown[]) => {\n this.handlePan(chartId, args[0] as { deltaX: number; deltaY: number });\n };\n chart.on('pan', panHandler);\n handlers.set('pan', panHandler);\n }\n\n if (this.options.syncCursor) {\n const hoverHandler = (...args: unknown[]) => {\n this.handleCursor(chartId, args[0] as { point?: { x: number; y: number } } | null);\n };\n chart.on('hover', hoverHandler);\n handlers.set('hover', hoverHandler);\n }\n\n if (this.options.syncSelection) {\n const selectionHandler = (...args: unknown[]) => {\n this.handleSelection(chartId, args[0] as { selected: unknown[] });\n };\n chart.on('selectionChange', selectionHandler);\n handlers.set('selectionChange', selectionHandler);\n }\n\n this.eventHandlers.set(chartId, handlers);\n }\n\n private detachEventHandlers(chart: ChartLike): void {\n const chartId = chart.getId();\n const handlers = this.eventHandlers.get(chartId);\n if (!handlers) return;\n for (const [event, handler] of handlers.entries()) {\n chart.off(event, handler);\n }\n this.eventHandlers.delete(chartId);\n }\n\n private handleZoom(sourceId: string, event: { x: Range; y: Range }): void {\n if (this.isUpdating) return;\n if (!this.canPropagateFrom(sourceId)) return;\n\n const sourceChart = this.charts.get(sourceId);\n if (sourceChart && !this.hasValidViewBounds(sourceChart.getViewBounds())) return;\n\n const bounds: Partial<Bounds> = {};\n if (this.options.axis === 'x' || this.options.axis === 'xy') {\n bounds.xMin = event.x[0];\n bounds.xMax = event.x[1];\n }\n if (this.options.axis === 'y' || this.options.axis === 'xy') {\n bounds.yMin = event.y[0];\n bounds.yMax = event.y[1];\n }\n\n if (bounds.xMin !== undefined && bounds.xMax !== undefined) {\n const xSpan = bounds.xMax - bounds.xMin;\n if (!Number.isFinite(xSpan) || xSpan <= 0) return;\n }\n\n this.scheduleSyncAction(`zoom-${sourceId}`, () => {\n this.propagateZoom(sourceId, bounds);\n });\n }\n\n private handlePan(sourceId: string, event: { deltaX: number; deltaY: number }): void {\n if (this.isUpdating) return;\n if (!this.canPropagateFrom(sourceId)) return;\n\n const dx = (this.options.axis === 'x' || this.options.axis === 'xy') ? event.deltaX : 0;\n const dy = (this.options.axis === 'y' || this.options.axis === 'xy') ? event.deltaY : 0;\n if (dx === 0 && dy === 0) return;\n\n this.scheduleSyncAction(`pan-${sourceId}`, () => {\n this.propagatePan(sourceId, dx, dy);\n });\n }\n\n private handleCursor(sourceId: string, event: { point?: { x: number; y: number } } | null): void {\n if (this.isUpdating) return;\n for (const [chartId, chart] of this.charts.entries()) {\n if (chartId === sourceId) continue;\n if (event?.point) chart.setExternalCursor?.(event.point.x, event.point.y);\n else chart.clearExternalCursor?.();\n }\n }\n\n private handleSelection(_sourceId: string, _event: { selected: unknown[] }): void {\n if (this.isUpdating || !this.options.syncSelection) return;\n }\n\n private propagateZoom(sourceId: string, bounds: Partial<Bounds>): void {\n if (this.isUpdating) return;\n this.isUpdating = true;\n try {\n for (const [chartId, chart] of this.charts.entries()) {\n if (chartId === sourceId) continue;\n const zoomOptions: { x?: Range; y?: Range; animate?: boolean } = { animate: false };\n if (bounds.xMin !== undefined && bounds.xMax !== undefined) {\n zoomOptions.x = [bounds.xMin, bounds.xMax];\n }\n if (bounds.yMin !== undefined && bounds.yMax !== undefined) {\n zoomOptions.y = [bounds.yMin, bounds.yMax];\n }\n chart.zoom(zoomOptions);\n }\n } finally {\n this.isUpdating = false;\n }\n }\n\n private propagatePan(sourceId: string, _dx: number, _dy: number): void {\n if (this.isUpdating) return;\n this.isUpdating = true;\n try {\n const sourceChart = this.charts.get(sourceId);\n if (!sourceChart || !this.hasValidViewBounds(sourceChart.getViewBounds())) return;\n\n const masterBounds = sourceChart.getViewBounds();\n\n for (const [chartId, chart] of this.charts.entries()) {\n if (chartId === sourceId) continue;\n\n const zoomOptions: { x?: Range; y?: Range; animate?: boolean } = { animate: false };\n\n if (this.options.axis === 'x' || this.options.axis === 'xy') {\n zoomOptions.x = [masterBounds.xMin, masterBounds.xMax];\n }\n\n if (this.options.axis === 'y' || this.options.axis === 'xy') {\n zoomOptions.y = [masterBounds.yMin, masterBounds.yMax];\n }\n\n if (zoomOptions.x || zoomOptions.y) {\n chart.zoom(zoomOptions);\n }\n }\n } finally {\n this.isUpdating = false;\n }\n }\n\n private scheduleSyncAction(key: string, action: () => void): void {\n if (this.options.debounce > 0) {\n this.debounceAction(key, action);\n return;\n }\n if (this.rafTimers.has(key)) return;\n const rafId = requestAnimationFrame(() => {\n this.rafTimers.delete(key);\n action();\n });\n this.rafTimers.set(key, rafId);\n }\n\n private debounceAction(key: string, action: () => void): void {\n const existingTimer = this.debounceTimers.get(key);\n if (existingTimer) clearTimeout(existingTimer);\n const timerId = window.setTimeout(() => {\n this.debounceTimers.delete(key);\n action();\n }, this.options.debounce);\n this.debounceTimers.set(key, timerId);\n }\n}\n\nexport function createChartGroup(charts: ChartLike[], options?: SyncOptions): ChartGroup {\n const group = new ChartGroup(options);\n group.addAll(...charts);\n return group;\n}\n\nexport function linkCharts(chart1: ChartLike, chart2: ChartLike, options?: SyncOptions): ChartGroup {\n return createChartGroup([chart1, chart2], options);\n}\n\nexport function createMasterSlave(\n master: ChartLike,\n slave: ChartLike,\n axis: SyncAxis = 'x',\n): ChartGroup {\n return createChartGroup([master, slave], {\n axis,\n bidirectional: false,\n masterId: master.getId(),\n syncCursor: true,\n syncZoom: true,\n syncPan: true,\n });\n}\n","/**\n * SciPlot Engine - Chart Sync Plugin\n * \n * Provides synchronization between multiple chart instances.\n * \n * @module plugins/sync\n */\n\nexport * from \"../../core/sync\";\n\nimport type { PluginManifest, ChartPlugin, PluginContext } from \"../types\";\n\nexport interface PluginSyncConfig {\n /** Group ID to join */\n groupId?: string;\n /** Sync axes: 'x' | 'y' | 'both' */\n syncAxes?: \"x\" | \"y\" | \"both\";\n}\n\nconst manifestSync: PluginManifest = {\n name: \"velo-plot-sync\",\n version: \"1.0.0\",\n description: \"Multi-chart synchronization for velo-plot\",\n provides: [\"interaction\"],\n tags: [\"sync\", \"multi-chart\", \"coordination\"],\n};\n\n/**\n * SciPlot Sync Plugin\n * \n * Enables smooth coordination and synchronization between separate chart instances.\n */\nexport function PluginSync(config: PluginSyncConfig = {}): ChartPlugin<PluginSyncConfig> {\n void config;\n return {\n manifest: manifestSync,\n\n onInit(_ctx: PluginContext) { \n if (config.groupId) {\n // Join the specified group\n }\n },\n\n onDestroy(_ctx: PluginContext) {\n }\n };\n}\n\nexport default PluginSync;\n"],"names":["ChartGroup","options","__publicField","chart","chartId","charts","axis","enabled","partial","bounds","excludeChartId","sharedX","masterId","master","b","fn","prev","_a","timerId","rafId","sourceId","xSpan","ySpan","handlers","zoomHandler","args","panHandler","hoverHandler","selectionHandler","event","handler","sourceChart","dx","dy","_b","_sourceId","_event","zoomOptions","_dx","_dy","masterBounds","key","action","existingTimer","createChartGroup","group","linkCharts","chart1","chart2","createMasterSlave","slave","manifestSync","PluginSync","config","_ctx"],"mappings":"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"}
1
+ {"version":3,"file":"sync.js","sources":["../../src/plugins/sync/index.ts"],"sourcesContent":["/**\n * SciPlot Engine - Chart Sync Plugin\n * \n * Provides synchronization between multiple chart instances.\n * \n * @module plugins/sync\n */\n\nexport * from \"../../core/sync\";\n\nimport type { PluginManifest, ChartPlugin, PluginContext } from \"../types\";\n\nexport interface PluginSyncConfig {\n /** Group ID to join */\n groupId?: string;\n /** Sync axes: 'x' | 'y' | 'both' */\n syncAxes?: \"x\" | \"y\" | \"both\";\n}\n\nconst manifestSync: PluginManifest = {\n name: \"velo-plot-sync\",\n version: \"1.0.0\",\n description: \"Multi-chart synchronization for velo-plot\",\n provides: [\"interaction\"],\n tags: [\"sync\", \"multi-chart\", \"coordination\"],\n};\n\n/**\n * SciPlot Sync Plugin\n *\n * @deprecated Use `ChartGroup` / `createChartGroup` from `velo-plot` (or `velo-plot/full`) instead.\n * This plugin stub does not perform synchronization. Kept for backward-compatible imports only.\n */\nexport function PluginSync(config: PluginSyncConfig = {}): ChartPlugin<PluginSyncConfig> {\n void config;\n return {\n manifest: manifestSync,\n\n onInit(_ctx: PluginContext) {\n console.warn(\n \"[PluginSync] Deprecated: use ChartGroup / createChartGroup from the core sync module instead.\",\n );\n },\n\n onDestroy(_ctx: PluginContext) {\n },\n };\n}\n\nexport default PluginSync;\n"],"names":["manifestSync","PluginSync","config","_ctx"],"mappings":";AAmBA,MAAMA,IAA+B;AAAA,EACjC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,aAAa;AAAA,EACb,UAAU,CAAC,aAAa;AAAA,EACxB,MAAM,CAAC,QAAQ,eAAe,cAAc;AAChD;AAQO,SAASC,EAAWC,IAA2B,IAAmC;AAEvF,SAAO;AAAA,IACL,UAAUF;AAAA,IAEV,OAAOG,GAAqB;AAC1B,cAAQ;AAAA,QACN;AAAA,MAAA;AAAA,IAEJ;AAAA,IAEA,UAAUA,GAAqB;AAAA,IAC/B;AAAA,EAAA;AAEJ;"}
@@ -0,0 +1,3 @@
1
+ export * from './video-recorder/index'
2
+ import SciPlot from './video-recorder/index'
3
+ export default SciPlot
@@ -0,0 +1,104 @@
1
+ const w = {
2
+ name: "velo-plot-video-recorder",
3
+ version: "1.0.0",
4
+ description: "Capture chart animations and export to video files",
5
+ provides: ["video-export", "media-tools"],
6
+ tags: ["export", "video", "animation"]
7
+ }, R = {
8
+ format: "webm",
9
+ fps: 30,
10
+ bitrate: 25e5,
11
+ fillBackground: !0,
12
+ backgroundColor: "#ffffff",
13
+ quality: 0.9,
14
+ autoDownload: !1,
15
+ filename: "chart-recording",
16
+ debug: !1
17
+ };
18
+ function F(b = {}) {
19
+ const a = { ...R, ...b };
20
+ let s = null, t = null, m = [], r = !1, l = !1, n = null, d = null, o = null;
21
+ function h() {
22
+ var i;
23
+ if (!s) return;
24
+ const e = (i = s.render.gl) == null ? void 0 : i.canvas;
25
+ e && (n = document.createElement("canvas"), n.width = e.width, n.height = e.height, d = n.getContext("2d"));
26
+ }
27
+ function u() {
28
+ var c, f, p, g, v;
29
+ if (!s || !d || !n) return;
30
+ const e = (c = s.render.gl) == null ? void 0 : c.canvas, i = (f = s.render.ctx2d) == null ? void 0 : f.canvas;
31
+ a.fillBackground ? (d.fillStyle = a.backgroundColor || ((v = (g = (p = s.chart).getTheme) == null ? void 0 : g.call(p)) == null ? void 0 : v.background) || "#ffffff", d.fillRect(0, 0, n.width, n.height)) : d.clearRect(0, 0, n.width, n.height), e && d.drawImage(e, 0, 0), i && d.drawImage(i, 0, 0), r && !l && (o && cancelAnimationFrame(o), o = requestAnimationFrame(u));
32
+ }
33
+ return {
34
+ manifest: w,
35
+ onInit(e) {
36
+ s = e;
37
+ },
38
+ onDestroy() {
39
+ r && (t == null || t.stop(), o && cancelAnimationFrame(o)), s = null;
40
+ },
41
+ api: {
42
+ start() {
43
+ if (r || !s) return;
44
+ if (h(), !n) {
45
+ console.error("[VideoRecorder] Could not initialize capture canvas");
46
+ return;
47
+ }
48
+ m = [];
49
+ const e = n.captureStream(a.fps), i = {
50
+ mimeType: a.format === "mp4" ? "video/mp4;codecs=h264" : "video/webm;codecs=vp9",
51
+ videoBitsPerSecond: a.bitrate
52
+ };
53
+ MediaRecorder.isTypeSupported(i.mimeType) || (i.mimeType = "video/webm");
54
+ try {
55
+ t = new MediaRecorder(e, i), t.ondataavailable = (c) => {
56
+ c.data.size > 0 && m.push(c.data);
57
+ }, t.onstop = () => {
58
+ r = !1, o && cancelAnimationFrame(o);
59
+ }, r = !0, l = !1, t.start(100), o && cancelAnimationFrame(o), u(), a.debug && console.log("[VideoRecorder] Recording started");
60
+ } catch (c) {
61
+ console.error("[VideoRecorder] Failed to start MediaRecorder:", c);
62
+ }
63
+ },
64
+ async stop() {
65
+ return new Promise((e) => {
66
+ if (!t || !r) {
67
+ e(new Blob([], { type: "video/webm" }));
68
+ return;
69
+ }
70
+ t.onstop = () => {
71
+ const i = new Blob(m, {
72
+ type: a.format === "mp4" ? "video/mp4" : "video/webm"
73
+ });
74
+ if (a.autoDownload) {
75
+ const c = URL.createObjectURL(i), f = document.createElement("a");
76
+ f.href = c, f.download = `${a.filename}.${a.format}`, f.click(), setTimeout(() => URL.revokeObjectURL(c), 100);
77
+ }
78
+ e(i), r = !1, o && cancelAnimationFrame(o), a.debug && console.log("[VideoRecorder] Recording stopped, size:", i.size);
79
+ }, t.stop();
80
+ });
81
+ },
82
+ pause() {
83
+ t && r && !l && (t.pause(), l = !0);
84
+ },
85
+ resume() {
86
+ t && r && l && (t.resume(), l = !1, o && cancelAnimationFrame(o), u());
87
+ },
88
+ isRecording() {
89
+ return r;
90
+ },
91
+ isPaused() {
92
+ return l;
93
+ },
94
+ updateConfig(e) {
95
+ Object.assign(a, e);
96
+ }
97
+ }
98
+ };
99
+ }
100
+ export {
101
+ F as PluginVideoRecorder,
102
+ F as default
103
+ };
104
+ //# sourceMappingURL=video-recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-recorder.js","sources":["../../src/plugins/video-recorder/index.ts"],"sourcesContent":["/**\n * @fileoverview Video Recorder plugin for capturing chart animations.\n * @module plugins/video-recorder\n */\n\nimport type {\n PluginVideoRecorderConfig,\n VideoRecorderAPI,\n VideoRecorderOptions,\n} from './types';\nimport type {\n ChartPlugin,\n PluginContext,\n PluginManifest,\n} from '../types';\n\nconst manifest: PluginManifest = {\n name: 'velo-plot-video-recorder',\n version: '1.0.0',\n description: 'Capture chart animations and export to video files',\n provides: ['video-export', 'media-tools'],\n tags: ['export', 'video', 'animation'],\n};\n\nconst DEFAULT_CONFIG: Required<PluginVideoRecorderConfig> = {\n format: 'webm',\n fps: 30,\n bitrate: 2500000,\n fillBackground: true,\n backgroundColor: '#ffffff',\n quality: 0.9,\n autoDownload: false,\n filename: 'chart-recording',\n debug: false,\n};\n\nexport function PluginVideoRecorder(\n userConfig: Partial<PluginVideoRecorderConfig> = {}\n): ChartPlugin<PluginVideoRecorderConfig> {\n const config = { ...DEFAULT_CONFIG, ...userConfig };\n let ctx: PluginContext | null = null;\n \n let mediaRecorder: MediaRecorder | null = null;\n let recordedChunks: Blob[] = [];\n let isRecording = false;\n let isPaused = false;\n \n // Composite canvas for capturing both WebGL and Overlay\n let compositeCanvas: HTMLCanvasElement | null = null;\n let compositeCtx: CanvasRenderingContext2D | null = null;\n let animationFrameId: number | null = null;\n\n function createCompositeCanvas() {\n if (!ctx) return;\n \n // Get canvases from the rendering context\n const mainCanvas = ctx.render.gl?.canvas as HTMLCanvasElement;\n \n if (!mainCanvas) return;\n\n compositeCanvas = document.createElement('canvas');\n compositeCanvas.width = mainCanvas.width;\n compositeCanvas.height = mainCanvas.height;\n compositeCtx = compositeCanvas.getContext('2d');\n }\n\n function updateComposite() {\n if (!ctx || !compositeCtx || !compositeCanvas) return;\n \n const mainCanvas = ctx.render.gl?.canvas as HTMLCanvasElement;\n const overlayCanvas = ctx.render.ctx2d?.canvas as HTMLCanvasElement;\n \n // Clear/Background\n if (config.fillBackground) {\n compositeCtx.fillStyle = config.backgroundColor || (ctx.chart as any).getTheme?.()?.background || '#ffffff';\n compositeCtx.fillRect(0, 0, compositeCanvas.width, compositeCanvas.height);\n } else {\n compositeCtx.clearRect(0, 0, compositeCanvas.width, compositeCanvas.height);\n }\n\n // Draw all chart canvases in order\n if (mainCanvas) compositeCtx.drawImage(mainCanvas, 0, 0);\n if (overlayCanvas) compositeCtx.drawImage(overlayCanvas, 0, 0);\n\n if (isRecording && !isPaused) {\n if (animationFrameId) cancelAnimationFrame(animationFrameId);\n animationFrameId = requestAnimationFrame(updateComposite);\n }\n }\n\n const api: VideoRecorderAPI = {\n start() {\n if (isRecording || !ctx) return;\n \n createCompositeCanvas();\n if (!compositeCanvas) {\n console.error('[VideoRecorder] Could not initialize capture canvas');\n return;\n }\n\n recordedChunks = [];\n const stream = compositeCanvas.captureStream(config.fps);\n \n const options: MediaRecorderOptions = {\n mimeType: config.format === 'mp4' ? 'video/mp4;codecs=h264' : 'video/webm;codecs=vp9',\n videoBitsPerSecond: config.bitrate\n };\n\n // Check for browser support and fallback\n if (!MediaRecorder.isTypeSupported(options.mimeType!)) {\n options.mimeType = 'video/webm';\n }\n\n try {\n mediaRecorder = new MediaRecorder(stream, options);\n \n mediaRecorder.ondataavailable = (event) => {\n if (event.data.size > 0) {\n recordedChunks.push(event.data);\n }\n };\n\n mediaRecorder.onstop = () => {\n isRecording = false;\n if (animationFrameId) cancelAnimationFrame(animationFrameId);\n };\n\n isRecording = true;\n isPaused = false;\n mediaRecorder.start(100); // Collect data every 100ms\n \n if (animationFrameId) cancelAnimationFrame(animationFrameId);\n updateComposite(); // Start render loop\n \n if (config.debug) console.log('[VideoRecorder] Recording started');\n } catch (e) {\n console.error('[VideoRecorder] Failed to start MediaRecorder:', e);\n }\n },\n\n async stop(): Promise<Blob> {\n return new Promise((resolve) => {\n if (!mediaRecorder || !isRecording) {\n resolve(new Blob([], { type: 'video/webm' }));\n return;\n }\n\n mediaRecorder.onstop = () => {\n const blob = new Blob(recordedChunks, {\n type: config.format === 'mp4' ? 'video/mp4' : 'video/webm'\n });\n \n if (config.autoDownload) {\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `${config.filename}.${config.format}`;\n a.click();\n setTimeout(() => URL.revokeObjectURL(url), 100);\n }\n\n resolve(blob);\n isRecording = false;\n if (animationFrameId) cancelAnimationFrame(animationFrameId);\n if (config.debug) console.log('[VideoRecorder] Recording stopped, size:', blob.size);\n };\n\n mediaRecorder.stop();\n });\n },\n\n pause() {\n if (mediaRecorder && isRecording && !isPaused) {\n mediaRecorder.pause();\n isPaused = true;\n }\n },\n\n resume() {\n if (mediaRecorder && isRecording && isPaused) {\n mediaRecorder.resume();\n isPaused = false;\n if (animationFrameId) cancelAnimationFrame(animationFrameId);\n updateComposite();\n }\n },\n\n isRecording() { return isRecording; },\n isPaused() { return isPaused; },\n \n updateConfig(newConfig: Partial<VideoRecorderOptions>) {\n Object.assign(config, newConfig);\n }\n };\n\n const pluginApi: VideoRecorderAPI & Record<string, unknown> = api as any;\n\n return {\n manifest,\n onInit(pCtx) {\n ctx = pCtx;\n },\n onDestroy() {\n if (isRecording) {\n mediaRecorder?.stop();\n if (animationFrameId) cancelAnimationFrame(animationFrameId);\n }\n ctx = null;\n },\n api: pluginApi\n };\n}\n\nexport default PluginVideoRecorder;\n\n// Type exports\nexport type {\n PluginVideoRecorderConfig,\n VideoRecorderAPI,\n VideoRecorderOptions,\n} from './types';\n"],"names":["manifest","DEFAULT_CONFIG","PluginVideoRecorder","userConfig","config","ctx","mediaRecorder","recordedChunks","isRecording","isPaused","compositeCanvas","compositeCtx","animationFrameId","createCompositeCanvas","_a","mainCanvas","updateComposite","_b","_c","_d","_e","overlayCanvas","pCtx","stream","options","event","e","resolve","blob","url","a","newConfig"],"mappings":"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"}
@@ -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,3 @@
1
+ export * from './virtualization/index'
2
+ import SciPlot from './virtualization/index'
3
+ export default SciPlot
@@ -0,0 +1,6 @@
1
+ import { P as t, P as l } from "../index-D7dTq6VB.js";
2
+ export {
3
+ t as PluginVirtualization,
4
+ l as default
5
+ };
6
+ //# sourceMappingURL=virtualization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"virtualization.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -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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"}
@@ -0,0 +1 @@
1
+ export * from './react/index'