velo-plot 2.20.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/README.md +26 -12
  2. package/dist/ChartCore-AnR_3Xkb.js +7338 -0
  3. package/dist/ChartCore-AnR_3Xkb.js.map +1 -0
  4. package/dist/GpuChartRenderer-Dxc0hrQA.js +75 -0
  5. package/dist/GpuChartRenderer-Dxc0hrQA.js.map +1 -0
  6. package/dist/{index.core-COREBDNq.js → PluginRegistry-CzMHebYP.js} +24 -38
  7. package/dist/PluginRegistry-CzMHebYP.js.map +1 -0
  8. package/dist/a11y-Bw6urYpr.js +105 -0
  9. package/dist/a11y-Bw6urYpr.js.map +1 -0
  10. package/dist/angular/hooks-entry.d.ts +1 -0
  11. package/dist/angular/hooks.d.ts +26 -0
  12. package/dist/angular/index.d.ts +8 -0
  13. package/dist/angular/velo-plot.component.d.ts +46 -0
  14. package/dist/angular-hooks.d.ts +1 -0
  15. package/dist/angular-hooks.js +9 -0
  16. package/dist/angular-hooks.js.map +1 -0
  17. package/dist/angular.d.ts +1 -0
  18. package/dist/angular.js +166 -0
  19. package/dist/angular.js.map +1 -0
  20. package/dist/assets/indicator.worker-bv0zC4EX.js.map +1 -1
  21. package/dist/astro/StackedPlot.astro +19 -0
  22. package/dist/astro/VeloPlot.astro +23 -0
  23. package/dist/{axisFormat-CzumxpCL.js → axisFormat-Sz4WIJfQ.js} +2 -2
  24. package/dist/{axisFormat-CzumxpCL.js.map → axisFormat-Sz4WIJfQ.js.map} +1 -1
  25. package/dist/bindings/shared/a11y.d.ts +26 -0
  26. package/dist/bindings/shared/chartLifecycle.d.ts +16 -0
  27. package/dist/bindings/shared/chartSyncBridge.d.ts +8 -0
  28. package/dist/bindings/shared/dimensions.d.ts +1 -0
  29. package/dist/bindings/shared/index.d.ts +9 -0
  30. package/dist/bindings/shared/indicatorBridge.d.ts +11 -0
  31. package/dist/bindings/shared/optionsSync.d.ts +7 -0
  32. package/dist/bindings/shared/seriesDiff.d.ts +8 -0
  33. package/dist/bindings/shared/stackedSync.d.ts +9 -0
  34. package/dist/bindings/shared/types.d.ts +46 -0
  35. package/dist/bindings/test-utils/index.d.ts +35 -0
  36. package/dist/bindings/test-utils/mockLifecycle.d.ts +120 -0
  37. package/dist/bindings/test-utils/vitestSveltePlugin.d.ts +3 -0
  38. package/dist/chartSyncBridge-D_rCtbFq.js +58 -0
  39. package/dist/chartSyncBridge-D_rCtbFq.js.map +1 -0
  40. package/dist/core/Chart.d.ts +1 -1
  41. package/dist/core/ChartStatistics.d.ts +1 -1
  42. package/dist/core/InteractionManager.d.ts +7 -1
  43. package/dist/core/OverlayRenderer.d.ts +5 -0
  44. package/dist/core/annotations/types.d.ts +1 -1
  45. package/dist/core/chart/ChartCore.d.ts +14 -19
  46. package/dist/core/chart/ChartFeatureHooks.d.ts +7 -0
  47. package/dist/core/chart/chartExportPatch.d.ts +2 -0
  48. package/dist/core/chart/overlaySeriesRegistry.d.ts +7 -0
  49. package/dist/core/chart/series/SeriesActions.d.ts +1 -0
  50. package/dist/core/chart/series/SeriesBufferLite.d.ts +13 -0
  51. package/dist/core/chart/series/seriesOptionsRegistry.d.ts +8 -0
  52. package/dist/core/chart/series/seriesTypeRegistry.d.ts +5 -0
  53. package/dist/core/chart/types.d.ts +1 -1
  54. package/dist/core/clipboard/index.d.ts +1 -1
  55. package/dist/core/debug/index.d.ts +1 -1
  56. package/dist/core/keybindings/index.d.ts +1 -1
  57. package/dist/core/loading/index.d.ts +1 -1
  58. package/dist/core/locale/index.d.ts +1 -1
  59. package/dist/core/stacked/paneResize.d.ts +3 -0
  60. package/dist/core/stacked/types.d.ts +10 -0
  61. package/dist/core/theme-editor/index.d.ts +1 -1
  62. package/dist/createStackedChart-DqcLCFGS.js +1338 -0
  63. package/dist/createStackedChart-DqcLCFGS.js.map +1 -0
  64. package/dist/examples/PluginDemonstration.d.ts +1 -1
  65. package/dist/hooks-DH-3bewj.js +69 -0
  66. package/dist/hooks-DH-3bewj.js.map +1 -0
  67. package/dist/{index-BBpJwOct.js → index-0InEyj0q.js} +119 -121
  68. package/dist/index-0InEyj0q.js.map +1 -0
  69. package/dist/{index-qunX30Xu.js → index-B7PTX94B.js} +2133 -2133
  70. package/dist/index-B7PTX94B.js.map +1 -0
  71. package/dist/{index-BMiZoKmm.js → index-BgrvqltV.js} +67 -67
  72. package/dist/index-BgrvqltV.js.map +1 -0
  73. package/dist/{index-DoYxf1cV.js → index-CWxIHdwX.js} +38 -98
  74. package/dist/index-CWxIHdwX.js.map +1 -0
  75. package/dist/index-CaXWSOTk.js.map +1 -1
  76. package/dist/{mockDatafeed-927foCM4.js → index-Cv6ZDLWf.js} +227 -217
  77. package/dist/index-Cv6ZDLWf.js.map +1 -0
  78. package/dist/index-Dag88bW4.js.map +1 -1
  79. package/dist/index-qhscKTDy.js.map +1 -1
  80. package/dist/index.core.d.ts +9 -7
  81. package/dist/index.d.ts +2 -15
  82. package/dist/indicators--eJGmVox.js.map +1 -1
  83. package/dist/optionsSync-QUiN4Inw.js +119 -0
  84. package/dist/optionsSync-QUiN4Inw.js.map +1 -0
  85. package/dist/plugins/3d/index.d.ts +2 -2
  86. package/dist/plugins/3d.d.ts +2 -2
  87. package/dist/plugins/3d.js +3 -1
  88. package/dist/plugins/3d.js.map +1 -1
  89. package/dist/plugins/analysis/index.d.ts +1 -1
  90. package/dist/plugins/analysis/indicators.d.ts +1 -1
  91. package/dist/plugins/analysis.d.ts +2 -2
  92. package/dist/plugins/analysis.js +2 -2
  93. package/dist/plugins/analysis.js.map +1 -1
  94. package/dist/plugins/annotations/index.d.ts +1 -1
  95. package/dist/plugins/annotations.d.ts +2 -2
  96. package/dist/plugins/annotations.js.map +1 -1
  97. package/dist/plugins/anomaly-detection.js +1 -1
  98. package/dist/plugins/anomaly-detection.js.map +1 -1
  99. package/dist/plugins/broken-axis.d.ts +2 -2
  100. package/dist/plugins/builtins/crosshair/CrosshairPlugin.d.ts +1 -1
  101. package/dist/plugins/builtins/index.d.ts +1 -1
  102. package/dist/plugins/caching.d.ts +2 -2
  103. package/dist/plugins/clipboard/index.d.ts +2 -2
  104. package/dist/plugins/clipboard.d.ts +2 -2
  105. package/dist/plugins/clipboard.js.map +1 -1
  106. package/dist/plugins/context-menu/index.d.ts +1 -1
  107. package/dist/plugins/context-menu.d.ts +2 -2
  108. package/dist/plugins/context-menu.js +1 -1
  109. package/dist/plugins/context-menu.js.map +1 -1
  110. package/dist/plugins/createPlugin.d.ts +8 -0
  111. package/dist/plugins/data-export/index.d.ts +1 -1
  112. package/dist/plugins/data-export.d.ts +2 -2
  113. package/dist/plugins/data-export.js +2 -2
  114. package/dist/plugins/data-export.js.map +1 -1
  115. package/dist/plugins/data-transform.d.ts +2 -2
  116. package/dist/plugins/data-transform.js +19 -19
  117. package/dist/plugins/data-transform.js.map +1 -1
  118. package/dist/plugins/debug/index.d.ts +2 -2
  119. package/dist/plugins/debug.d.ts +2 -2
  120. package/dist/plugins/debug.js +33 -33
  121. package/dist/plugins/debug.js.map +1 -1
  122. package/dist/plugins/forecasting/index.d.ts +1 -1
  123. package/dist/plugins/forecasting.js.map +1 -1
  124. package/dist/plugins/gpu/index.d.ts +2 -2
  125. package/dist/plugins/gpu.d.ts +2 -2
  126. package/dist/plugins/gpu.js +2033 -209
  127. package/dist/plugins/gpu.js.map +1 -1
  128. package/dist/plugins/i18n/index.d.ts +2 -2
  129. package/dist/plugins/i18n.d.ts +2 -2
  130. package/dist/plugins/i18n.js +286 -23
  131. package/dist/plugins/i18n.js.map +1 -1
  132. package/dist/plugins/index.d.ts +1 -1
  133. package/dist/plugins/keyboard.d.ts +2 -2
  134. package/dist/plugins/keyboard.js +65 -5
  135. package/dist/plugins/keyboard.js.map +1 -1
  136. package/dist/plugins/latex.js +766 -207
  137. package/dist/plugins/latex.js.map +1 -1
  138. package/dist/plugins/lazy-load.d.ts +2 -2
  139. package/dist/plugins/loading/index.d.ts +2 -3
  140. package/dist/plugins/loading.d.ts +2 -2
  141. package/dist/plugins/loading.js +3 -4
  142. package/dist/plugins/ml-integration.d.ts +2 -2
  143. package/dist/plugins/ml-integration.js +5 -5
  144. package/dist/plugins/ml-integration.js.map +1 -1
  145. package/dist/plugins/offscreen.d.ts +2 -2
  146. package/dist/plugins/offscreen.js +11 -10
  147. package/dist/plugins/offscreen.js.map +1 -1
  148. package/dist/plugins/pattern-recognition.d.ts +2 -2
  149. package/dist/plugins/pattern-recognition.js +1 -1
  150. package/dist/plugins/pattern-recognition.js.map +1 -1
  151. package/dist/plugins/radar.d.ts +2 -2
  152. package/dist/plugins/regression.d.ts +2 -2
  153. package/dist/plugins/regression.js +255 -257
  154. package/dist/plugins/regression.js.map +1 -1
  155. package/dist/plugins/roi.js +9 -9
  156. package/dist/plugins/roi.js.map +1 -1
  157. package/dist/plugins/snapshot/index.d.ts +1 -1
  158. package/dist/plugins/snapshot/types.d.ts +1 -1
  159. package/dist/plugins/snapshot.d.ts +2 -2
  160. package/dist/plugins/snapshot.js.map +1 -1
  161. package/dist/plugins/streaming/index.d.ts +2 -2
  162. package/dist/plugins/streaming.d.ts +2 -2
  163. package/dist/plugins/streaming.js +1 -1
  164. package/dist/plugins/sync/index.d.ts +3 -2
  165. package/dist/plugins/sync.d.ts +2 -2
  166. package/dist/plugins/sync.js.map +1 -1
  167. package/dist/plugins/theme-editor/index.d.ts +1 -1
  168. package/dist/plugins/theme-editor.d.ts +2 -2
  169. package/dist/plugins/tools/index.d.ts +1 -1
  170. package/dist/plugins/tools/tooltip/index.d.ts +1 -1
  171. package/dist/plugins/tools.d.ts +2 -2
  172. package/dist/plugins/tools.js +1 -1
  173. package/dist/plugins/types.d.ts +1 -1
  174. package/dist/plugins/video-recorder.d.ts +2 -2
  175. package/dist/plugins/virtualization.d.ts +2 -2
  176. package/dist/plugins/virtualization.js +1 -1
  177. package/dist/pool-DZ8shSqv.js +109 -0
  178. package/dist/pool-DZ8shSqv.js.map +1 -0
  179. package/dist/react/StackedPlot.d.ts +31 -0
  180. package/dist/react/VeloPlot.d.ts +29 -0
  181. package/dist/react/index.d.ts +25 -3
  182. package/dist/react/useChartSync.d.ts +8 -0
  183. package/dist/react/useIndicator.d.ts +12 -0
  184. package/dist/react/useVeloPlot.d.ts +19 -0
  185. package/dist/react.js +325 -24
  186. package/dist/react.js.map +1 -1
  187. package/dist/renderer/NativeWebGLRenderer.d.ts +2 -1
  188. package/dist/renderer/native/NativeWebGLRenderer.d.ts +5 -1
  189. package/dist/renderer/native/draw.d.ts +2 -47
  190. package/dist/renderer/native/drawCore.d.ts +27 -0
  191. package/dist/renderer/native/drawExtended.d.ts +29 -0
  192. package/dist/renderer/native/frameRenderExtended.d.ts +1 -0
  193. package/dist/renderer/native/frameRenderRegistry.d.ts +27 -0
  194. package/dist/renderer/native/programFactoryCore.d.ts +6 -0
  195. package/dist/renderer/native/programFactoryExtended.d.ts +4 -0
  196. package/dist/renderer/native/utils.d.ts +3 -29
  197. package/dist/renderer/native/utilsCore.d.ts +14 -0
  198. package/dist/renderer/native/utilsExtended.d.ts +17 -0
  199. package/dist/renderer/overlaySeriesExtended.d.ts +1 -0
  200. package/dist/renderer/registerExtendedSeries.d.ts +1 -0
  201. package/dist/renderer/registerWebGPU.d.ts +2 -0
  202. package/dist/renderer/seriesBufferExtended.d.ts +4 -0
  203. package/dist/scientific/index.d.ts +44 -0
  204. package/dist/scientific/registerScientific.d.ts +1 -0
  205. package/dist/scientific.d.ts +1 -0
  206. package/dist/scientific.js +91 -0
  207. package/dist/scientific.js.map +1 -0
  208. package/dist/solid/VeloPlot.d.ts +16 -0
  209. package/dist/solid/index.d.ts +13 -0
  210. package/dist/solid/useChartSync.d.ts +6 -0
  211. package/dist/solid/useIndicator.d.ts +11 -0
  212. package/dist/solid/useStackedPlot.d.ts +14 -0
  213. package/dist/solid/useVeloPlot.d.ts +18 -0
  214. package/dist/solid.d.ts +1 -0
  215. package/dist/solid.js +169 -0
  216. package/dist/solid.js.map +1 -0
  217. package/dist/stackedSync-tnrunZMt.js +100 -0
  218. package/dist/stackedSync-tnrunZMt.js.map +1 -0
  219. package/dist/svelte/StackedPlot.svelte +29 -0
  220. package/dist/svelte/StackedPlot.test.ts +114 -0
  221. package/dist/svelte/VeloPlot.svelte +44 -0
  222. package/dist/svelte/VeloPlot.test.ts +105 -0
  223. package/dist/svelte/index.d.ts +11 -0
  224. package/dist/svelte/index.test.ts +20 -0
  225. package/dist/svelte/index.ts +15 -0
  226. package/dist/svelte/useChartSync.d.ts +8 -0
  227. package/dist/svelte/useChartSync.test.ts +49 -0
  228. package/dist/svelte/useChartSync.ts +29 -0
  229. package/dist/svelte/useIndicator.d.ts +13 -0
  230. package/dist/svelte/useIndicator.test.ts +67 -0
  231. package/dist/svelte/useIndicator.ts +43 -0
  232. package/dist/svelte/useStackedPlot.d.ts +16 -0
  233. package/dist/svelte/useStackedPlot.test.ts +76 -0
  234. package/dist/svelte/useStackedPlot.ts +54 -0
  235. package/dist/svelte/useVeloPlot.d.ts +21 -0
  236. package/dist/svelte/useVeloPlot.test.ts +100 -0
  237. package/dist/svelte/useVeloPlot.ts +81 -0
  238. package/dist/svelte.d.ts +1 -0
  239. package/dist/svelte.js +140 -0
  240. package/dist/svelte.js.map +1 -0
  241. package/dist/testing/index.d.ts +1 -1
  242. package/dist/theme/index.d.ts +2 -1
  243. package/dist/trading/index.d.ts +1 -4
  244. package/dist/trading/registerTrading.d.ts +3 -0
  245. package/dist/trading.js +42 -42
  246. package/dist/types.d.ts +4 -4
  247. package/dist/{utils-BK5UUXxj.js → utils-Dx0h0bgQ.js} +9 -9
  248. package/dist/{utils-BK5UUXxj.js.map → utils-Dx0h0bgQ.js.map} +1 -1
  249. package/dist/velo-plot.full.js +2216 -1346
  250. package/dist/velo-plot.full.js.map +1 -1
  251. package/dist/velo-plot.js +33 -28
  252. package/dist/velo-plot.js.map +1 -1
  253. package/dist/vue/StackedPlot.vue.d.ts +48 -0
  254. package/dist/vue/VeloPlot.vue.d.ts +64 -0
  255. package/dist/vue/index.d.ts +250 -0
  256. package/dist/vue/useChartSync.d.ts +52 -0
  257. package/dist/vue/useIndicator.d.ts +23 -0
  258. package/dist/vue/useStackedPlot.d.ts +15 -0
  259. package/dist/vue/useVeloPlot.d.ts +20 -0
  260. package/dist/vue.d.ts +1 -0
  261. package/dist/vue.js +309 -0
  262. package/dist/vue.js.map +1 -0
  263. package/dist/workers/indicatorsAsync.d.ts +2 -0
  264. package/dist/workers/pool.d.ts +9 -0
  265. package/package.json +96 -10
  266. package/dist/ChartCore-ivzLqxVb.js +0 -8350
  267. package/dist/ChartCore-ivzLqxVb.js.map +0 -1
  268. package/dist/SciPlot-CEU4ApgD.js +0 -463
  269. package/dist/SciPlot-CEU4ApgD.js.map +0 -1
  270. package/dist/createStackedChart-DxNW0VkF.js +0 -705
  271. package/dist/createStackedChart-DxNW0VkF.js.map +0 -1
  272. package/dist/gpuRenderer-BpaMYz-k.js +0 -1848
  273. package/dist/gpuRenderer-BpaMYz-k.js.map +0 -1
  274. package/dist/index-BBpJwOct.js.map +0 -1
  275. package/dist/index-BMiZoKmm.js.map +0 -1
  276. package/dist/index-DoYxf1cV.js.map +0 -1
  277. package/dist/index-qunX30Xu.js.map +0 -1
  278. package/dist/index-x4stH3jD.js +0 -282
  279. package/dist/index-x4stH3jD.js.map +0 -1
  280. package/dist/index.core-COREBDNq.js.map +0 -1
  281. package/dist/mockDatafeed-927foCM4.js.map +0 -1
  282. package/dist/pool-15HMCNv8.js +0 -88
  283. package/dist/pool-15HMCNv8.js.map +0 -1
  284. package/dist/react/SciPlot.d.ts +0 -60
  285. package/dist/react/useSciPlot.d.ts +0 -56
  286. package/dist/symbols-DT0eLlpW.js +0 -568
  287. package/dist/symbols-DT0eLlpW.js.map +0 -1
@@ -0,0 +1,114 @@
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
+ import { mount, flushSync, unmount } from "svelte";
3
+ import StackedPlot from "./StackedPlot.svelte";
4
+
5
+ const mockStack = {
6
+ destroy: vi.fn(),
7
+ whenReady: () => Promise.resolve(),
8
+ fitAll: vi.fn(),
9
+ resetAll: vi.fn(),
10
+ };
11
+
12
+ let paneKey = "a";
13
+
14
+ vi.mock("../core/stacked", () => ({
15
+ createStackedChart: vi.fn(() => mockStack),
16
+ }));
17
+
18
+ vi.mock("../bindings/shared", async (importOriginal) => {
19
+ const actual = await importOriginal<typeof import("../bindings/shared")>();
20
+ return {
21
+ ...actual,
22
+ stackedStructureKey: () => paneKey,
23
+ syncStackedPaneSeries: () => new Map(),
24
+ syncStackedOptions: vi.fn(),
25
+ };
26
+ });
27
+
28
+ describe("StackedPlot.svelte", () => {
29
+ afterEach(() => {
30
+ document.body.replaceChildren();
31
+ vi.clearAllMocks();
32
+ });
33
+
34
+ it("renders with empty class by default", () => {
35
+ const target = document.createElement("div");
36
+ document.body.appendChild(target);
37
+ const component = mount(StackedPlot, {
38
+ target,
39
+ props: {
40
+ panes: [{ id: "p", height: 1, series: [] }],
41
+ },
42
+ });
43
+ flushSync();
44
+ const el = target.querySelector(".velo-plot-stacked") as HTMLElement;
45
+ expect(el.className).toBe("velo-plot-stacked");
46
+ unmount(component);
47
+ });
48
+
49
+ it("renders with custom class", () => {
50
+ const target = document.createElement("div");
51
+ document.body.appendChild(target);
52
+ const component = mount(StackedPlot, {
53
+ target,
54
+ props: {
55
+ panes: [{ id: "p", height: 1, series: [] }],
56
+ class: "stack-panel",
57
+ width: "100%",
58
+ height: 480,
59
+ },
60
+ });
61
+ flushSync();
62
+ expect(target.querySelector(".velo-plot-stacked.stack-panel")).toBeTruthy();
63
+ unmount(component);
64
+ });
65
+
66
+ it("renders with string height and numeric width", () => {
67
+ const target = document.createElement("div");
68
+ document.body.appendChild(target);
69
+ const component = mount(StackedPlot, {
70
+ target,
71
+ props: {
72
+ panes: [{ id: "p", height: 1, series: [] }],
73
+ width: 500,
74
+ height: "50%",
75
+ },
76
+ });
77
+ flushSync();
78
+ const el = target.querySelector(".velo-plot-stacked") as HTMLElement;
79
+ const style = el.getAttribute("style") ?? "";
80
+ expect(style.includes("500px") || el.style.width === "500px").toBe(true);
81
+ expect(style.includes("50%") || el.style.height === "50%").toBe(true);
82
+ unmount(component);
83
+ });
84
+
85
+ it("renders stacked container and syncs options", () => {
86
+ paneKey = "a";
87
+ const target = document.createElement("div");
88
+ document.body.appendChild(target);
89
+ const first = mount(StackedPlot, {
90
+ target,
91
+ props: {
92
+ panes: [{ id: "p", height: 1, series: [] }],
93
+ width: 500,
94
+ height: 300,
95
+ class: "stacked",
96
+ },
97
+ });
98
+ flushSync();
99
+ expect(target.querySelector(".velo-plot-stacked.stacked")).toBeTruthy();
100
+ unmount(first);
101
+ paneKey = "b";
102
+ mount(StackedPlot, {
103
+ target,
104
+ props: {
105
+ panes: [{ id: "q", height: 1, series: [] }],
106
+ stackOptions: { theme: "dark" },
107
+ width: "100%",
108
+ height: 480,
109
+ },
110
+ });
111
+ flushSync();
112
+ expect(mockStack.destroy).toHaveBeenCalled();
113
+ });
114
+ });
@@ -0,0 +1,44 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy } from "svelte";
3
+ import { createVeloPlot, type UseVeloPlotOptions } from "./useVeloPlot";
4
+ import { diffSeries, type VeloPlotSeries } from "../bindings/shared";
5
+
6
+ export let series: VeloPlotSeries[] = [];
7
+ export let width: number | string = "100%";
8
+ export let height: number | string = 400;
9
+ export let chartOptions: UseVeloPlotOptions = {};
10
+ let className = "";
11
+ export { className as class };
12
+
13
+ const api = createVeloPlot(chartOptions);
14
+ const { isReady, chart, mount, unmount, updateOptions, addSeries, updateSeries, removeSeries } =
15
+ api;
16
+ let container: HTMLDivElement;
17
+ let previous = new Map<string, VeloPlotSeries>();
18
+
19
+ onMount(() => mount(container));
20
+ onDestroy(() => unmount());
21
+
22
+ $: if ($isReady && $chart) {
23
+ updateOptions(chartOptions);
24
+ previous = diffSeries(
25
+ {
26
+ addSeries,
27
+ updateSeries,
28
+ removeSeries,
29
+ autoScale: () => $chart.autoScale(),
30
+ },
31
+ series,
32
+ previous,
33
+ );
34
+ }
35
+ </script>
36
+
37
+ <div
38
+ bind:this={container}
39
+ class={`velo-plot-container${className ? ` ${className}` : ""}`}
40
+ role="img"
41
+ style:position="relative"
42
+ style:width={typeof width === "number" ? `${width}px` : width}
43
+ style:height={typeof height === "number" ? `${height}px` : height}
44
+ />
@@ -0,0 +1,105 @@
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
+ import { mount, flushSync, unmount } from "svelte";
3
+ import VeloPlot from "./VeloPlot.svelte";
4
+ import { buildMockChart } from "../bindings/test-utils";
5
+
6
+ const mockChart = buildMockChart();
7
+
8
+ vi.mock("../bindings/shared/chartLifecycle", () => ({
9
+ createChartLifecycle: vi.fn(() => ({
10
+ chart: mockChart,
11
+ getBounds: () => mockChart.getViewBounds(),
12
+ destroy: mockChart.destroy,
13
+ })),
14
+ pickSyncableOptions: (o: object) => o,
15
+ optionsChanged: () => false,
16
+ syncChartOptions: vi.fn(),
17
+ }));
18
+
19
+ describe("VeloPlot.svelte", () => {
20
+ afterEach(() => {
21
+ document.body.replaceChildren();
22
+ vi.clearAllMocks();
23
+ });
24
+
25
+ it("renders with empty class by default", () => {
26
+ const target = document.createElement("div");
27
+ document.body.appendChild(target);
28
+ const component = mount(VeloPlot, {
29
+ target,
30
+ props: { series: [] },
31
+ });
32
+ flushSync();
33
+ const el = target.querySelector(".velo-plot-container") as HTMLElement;
34
+ expect(el.className).toBe("velo-plot-container");
35
+ unmount(component);
36
+ });
37
+
38
+ it("renders with custom class and string height", () => {
39
+ const target = document.createElement("div");
40
+ document.body.appendChild(target);
41
+ const component = mount(VeloPlot, {
42
+ target,
43
+ props: {
44
+ series: [],
45
+ class: "chart-panel",
46
+ width: 640,
47
+ height: "50vh",
48
+ },
49
+ });
50
+ flushSync();
51
+ const el = target.querySelector(".velo-plot-container.chart-panel") as HTMLElement;
52
+ expect(el).toBeTruthy();
53
+ const style = el.getAttribute("style") ?? "";
54
+ expect(style.includes("50vh") || el.style.height === "50vh").toBe(true);
55
+ unmount(component);
56
+ });
57
+
58
+ it("renders with string width and numeric height", () => {
59
+ const target = document.createElement("div");
60
+ document.body.appendChild(target);
61
+ const component = mount(VeloPlot, {
62
+ target,
63
+ props: {
64
+ series: [],
65
+ width: "100%",
66
+ height: 320,
67
+ },
68
+ });
69
+ flushSync();
70
+ const el = target.querySelector(".velo-plot-container") as HTMLElement;
71
+ expect(el.style.width || el.getAttribute("style")).toContain("100%");
72
+ expect(el.style.height || el.getAttribute("style")).toContain("320px");
73
+ unmount(component);
74
+ });
75
+
76
+ it("mounts chart container and syncs series", () => {
77
+ const target = document.createElement("div");
78
+ document.body.appendChild(target);
79
+ const component = mount(VeloPlot, {
80
+ target,
81
+ props: {
82
+ series: [{ id: "a", x: new Float32Array([0]), y: new Float32Array([0]) }],
83
+ width: 640,
84
+ height: 320,
85
+ class: "custom",
86
+ chartOptions: { animations: false },
87
+ },
88
+ });
89
+ flushSync();
90
+ expect(target.querySelector(".velo-plot-container.custom")).toBeTruthy();
91
+ unmount(component);
92
+ const updated = mount(VeloPlot, {
93
+ target,
94
+ props: {
95
+ series: [{ id: "b", x: new Float32Array([0, 1]), y: new Float32Array([0, 1]) }],
96
+ width: "100%",
97
+ height: 400,
98
+ chartOptions: { theme: "dark" },
99
+ },
100
+ });
101
+ flushSync();
102
+ expect(mockChart.addSeries).toHaveBeenCalled();
103
+ unmount(updated);
104
+ });
105
+ });
@@ -0,0 +1,11 @@
1
+ import { useVeloPlot as useVeloPlotImpl } from './useVeloPlot';
2
+ export { createVeloPlot, useVeloPlot, type UseVeloPlotOptions } from './useVeloPlot';
3
+ export { createStackedPlot, useStackedPlot } from './useStackedPlot';
4
+ export { useIndicator } from './useIndicator';
5
+ export { useChartSync, useChartGroup } from './useChartSync';
6
+ /** @deprecated Use `useVeloPlot` / `createVeloPlot` instead. **Removed in v4.0.** */
7
+ export declare const useSciPlot: typeof useVeloPlotImpl;
8
+ /** @deprecated Use `createVeloPlot` instead. **Removed in v4.0.** */
9
+ export declare const createSciPlot: typeof useVeloPlotImpl;
10
+ /** @deprecated Use `useVeloPlot` instead. **Removed in v4.0.** */
11
+ export declare const useSciChart: typeof useVeloPlotImpl;
@@ -0,0 +1,20 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import * as svelte from "./index";
3
+
4
+ describe("svelte index", () => {
5
+ it("re-exports public svelte bindings", () => {
6
+ expect(svelte.createVeloPlot).toBeTypeOf("function");
7
+ expect(svelte.useVeloPlot).toBe(svelte.createVeloPlot);
8
+ expect(svelte.createStackedPlot).toBeTypeOf("function");
9
+ expect(svelte.useStackedPlot).toBe(svelte.createStackedPlot);
10
+ expect(svelte.useIndicator).toBeTypeOf("function");
11
+ expect(svelte.useChartSync).toBeTypeOf("function");
12
+ expect(svelte.useChartGroup).toBe(svelte.useChartSync);
13
+ });
14
+
15
+ it("keeps SciPlot aliases for backward compatibility", () => {
16
+ expect(svelte.useSciPlot).toBe(svelte.useVeloPlot);
17
+ expect(svelte.createSciPlot).toBe(svelte.createVeloPlot);
18
+ expect(svelte.useSciChart).toBe(svelte.useVeloPlot);
19
+ });
20
+ });
@@ -0,0 +1,15 @@
1
+ export { createVeloPlot, useVeloPlot, type UseVeloPlotOptions } from "./useVeloPlot";
2
+ export { createStackedPlot, useStackedPlot } from "./useStackedPlot";
3
+ export { useIndicator } from "./useIndicator";
4
+ export { useChartSync, useChartGroup } from "./useChartSync";
5
+
6
+ import { useVeloPlot as useVeloPlotImpl, createVeloPlot as createVeloPlotImpl } from "./useVeloPlot";
7
+
8
+ /** @deprecated Use `useVeloPlot` / `createVeloPlot` instead. **Removed in v4.0.** */
9
+ export const useSciPlot = useVeloPlotImpl;
10
+ /** @deprecated Use `createVeloPlot` instead. **Removed in v4.0.** */
11
+ export const createSciPlot = createVeloPlotImpl;
12
+ /** @deprecated Use `useVeloPlot` instead. **Removed in v4.0.** */
13
+ export const useSciChart = useVeloPlotImpl;
14
+
15
+ // Svelte components: import from 'velo-plot/svelte/VeloPlot.svelte' (shipped as source)
@@ -0,0 +1,8 @@
1
+ import { ChartLike, SyncOptions, ChartGroup } from '../core/sync';
2
+
3
+ export declare function useChartSync(getCharts: () => ChartLike[], options?: SyncOptions): {
4
+ group: import('svelte/store').Writable<ChartGroup | null>;
5
+ sync: () => void;
6
+ destroy: () => void;
7
+ };
8
+ export declare const useChartGroup: typeof useChartSync;
@@ -0,0 +1,49 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { get } from "svelte/store";
3
+ import { useChartSync } from "./useChartSync";
4
+ import { buildMockChart } from "../bindings/test-utils";
5
+
6
+ const destroy = vi.fn();
7
+ const mockGroup = { destroy };
8
+
9
+ vi.mock("../bindings/shared", async (importOriginal) => {
10
+ const actual = await importOriginal<typeof import("../bindings/shared")>();
11
+ return {
12
+ ...actual,
13
+ createChartSync: vi.fn(() => ({ group: mockGroup, destroy })),
14
+ };
15
+ });
16
+
17
+ describe("useChartSync svelte", () => {
18
+ it("re-sync destroys previous chart sync handle", () => {
19
+ const charts = [buildMockChart("a"), buildMockChart("b")];
20
+ const api = useChartSync(() => charts, { axis: "x" });
21
+ api.sync();
22
+ api.sync();
23
+ expect(destroy).toHaveBeenCalled();
24
+ });
25
+
26
+ it("syncs multiple charts with options", () => {
27
+ const charts = [buildMockChart("a"), buildMockChart("b")];
28
+ const api = useChartSync(() => charts, { axis: "y" });
29
+ api.sync();
30
+ expect(get(api.group)).toBe(mockGroup);
31
+ api.destroy();
32
+ expect(destroy).toHaveBeenCalled();
33
+ });
34
+
35
+ it("syncs multiple charts and destroys", () => {
36
+ const charts = [buildMockChart("a"), buildMockChart("b")];
37
+ const api = useChartSync(() => charts);
38
+ api.sync();
39
+ expect(get(api.group)).toBe(mockGroup);
40
+ api.destroy();
41
+ expect(destroy).toHaveBeenCalled();
42
+ });
43
+
44
+ it("skips sync for single chart", () => {
45
+ const api = useChartSync(() => [buildMockChart()]);
46
+ api.sync();
47
+ expect(get(api.group)).toBeNull();
48
+ });
49
+ });
@@ -0,0 +1,29 @@
1
+ import { writable } from "svelte/store";
2
+ import type { ChartLike, SyncOptions } from "../core/sync";
3
+ import { ChartGroup } from "../core/sync";
4
+ import { createChartSync } from "../bindings/shared";
5
+
6
+ export function useChartSync(getCharts: () => ChartLike[], options?: SyncOptions) {
7
+ const group = writable<ChartGroup | null>(null);
8
+ let handle: ReturnType<typeof createChartSync> | null = null;
9
+
10
+ const sync = () => {
11
+ handle?.destroy();
12
+ const charts = getCharts();
13
+ if (charts.length < 2) {
14
+ group.set(null);
15
+ return;
16
+ }
17
+ handle = createChartSync(charts, options);
18
+ group.set(handle.group);
19
+ };
20
+
21
+ const destroy = () => {
22
+ handle?.destroy();
23
+ group.set(null);
24
+ };
25
+
26
+ return { group, sync, destroy };
27
+ }
28
+
29
+ export const useChartGroup = useChartSync;
@@ -0,0 +1,13 @@
1
+ import { AddIndicatorOptions, AddIndicatorResult } from '../core/indicator/addIndicator';
2
+ import { IndicatorPresetName } from '../core/indicator/indicatorPresets';
3
+ import { IndicatorHost } from '../bindings/shared';
4
+
5
+ export declare function useIndicator(getHost: () => IndicatorHost, preset: IndicatorPresetName, options?: AddIndicatorOptions): {
6
+ result: import('svelte/store').Writable<(AddIndicatorResult & {
7
+ paneId?: string;
8
+ }) | null>;
9
+ isLoading: import('svelte/store').Writable<boolean>;
10
+ error: import('svelte/store').Writable<Error | null>;
11
+ run: () => Promise<void>;
12
+ cleanup: () => void;
13
+ };
@@ -0,0 +1,67 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { get } from "svelte/store";
3
+ import { useIndicator } from "./useIndicator";
4
+ import { buildMockChart } from "../bindings/test-utils";
5
+
6
+ vi.mock("../bindings/shared", async (importOriginal) => {
7
+ const actual = await importOriginal<typeof import("../bindings/shared")>();
8
+ return {
9
+ ...actual,
10
+ addIndicatorToHost: vi.fn(async () => ({ id: "rsi", seriesIds: ["rsi"] })),
11
+ removeIndicatorFromChart: vi.fn(),
12
+ isStackedChart: vi.fn(() => false),
13
+ };
14
+ });
15
+
16
+ describe("useIndicator svelte", () => {
17
+ beforeEach(() => {
18
+ vi.clearAllMocks();
19
+ });
20
+
21
+ it("loads indicator and cleans up on chart host", async () => {
22
+ const chart = buildMockChart();
23
+ const api = useIndicator(() => chart, "rsi");
24
+ await api.run();
25
+ expect(get(api.result)?.id).toBe("rsi");
26
+ api.cleanup();
27
+ const { removeIndicatorFromChart } = await import("../bindings/shared");
28
+ expect(removeIndicatorFromChart).toHaveBeenCalledWith(chart, "rsi");
29
+ });
30
+
31
+ it("records errors", async () => {
32
+ const { addIndicatorToHost } = await import("../bindings/shared");
33
+ vi.mocked(addIndicatorToHost).mockRejectedValueOnce(new Error("fail"));
34
+ const api = useIndicator(() => buildMockChart(), "macd");
35
+ await api.run();
36
+ expect(get(api.error)?.message).toBe("fail");
37
+ });
38
+
39
+ it("skips when host is null", async () => {
40
+ const api = useIndicator(() => null, "rsi");
41
+ await api.run();
42
+ expect(get(api.result)).toBeNull();
43
+ });
44
+
45
+ it("cleanup no-ops without indicator result", () => {
46
+ const api = useIndicator(() => buildMockChart(), "rsi");
47
+ api.cleanup();
48
+ });
49
+
50
+ it("skips cleanup for stacked chart hosts", async () => {
51
+ const { isStackedChart, removeIndicatorFromChart } = await import("../bindings/shared");
52
+ vi.mocked(isStackedChart).mockReturnValueOnce(true);
53
+ const chart = buildMockChart();
54
+ const api = useIndicator(() => chart, "ema");
55
+ await api.run();
56
+ api.cleanup();
57
+ expect(removeIndicatorFromChart).not.toHaveBeenCalled();
58
+ });
59
+
60
+ it("wraps non-error rejections", async () => {
61
+ const { addIndicatorToHost } = await import("../bindings/shared");
62
+ vi.mocked(addIndicatorToHost).mockRejectedValueOnce("plain failure");
63
+ const api = useIndicator(() => buildMockChart(), "macd");
64
+ await api.run();
65
+ expect(get(api.error)?.message).toBe("plain failure");
66
+ });
67
+ });
@@ -0,0 +1,43 @@
1
+ import { writable } from "svelte/store";
2
+ import type { AddIndicatorOptions, AddIndicatorResult } from "../core/indicator/addIndicator";
3
+ import type { IndicatorPresetName } from "../core/indicator/indicatorPresets";
4
+ import { addIndicatorToHost, isStackedChart, removeIndicatorFromChart, type IndicatorHost } from "../bindings/shared";
5
+ import type { Chart } from "../core/chart/types";
6
+
7
+ export function useIndicator(
8
+ getHost: () => IndicatorHost,
9
+ preset: IndicatorPresetName,
10
+ options: AddIndicatorOptions = {},
11
+ ) {
12
+ const result = writable<(AddIndicatorResult & { paneId?: string }) | null>(null);
13
+ const isLoading = writable(false);
14
+ const error = writable<Error | null>(null);
15
+
16
+ const run = async () => {
17
+ const host = getHost();
18
+ if (!host) return;
19
+ isLoading.set(true);
20
+ try {
21
+ const res = await addIndicatorToHost(host, preset, options);
22
+ result.set(res);
23
+ isLoading.set(false);
24
+ } catch (e) {
25
+ error.set(e instanceof Error ? e : new Error(String(e)));
26
+ isLoading.set(false);
27
+ }
28
+ };
29
+
30
+ const cleanupIndicator = () => {
31
+ const host = getHost();
32
+ let currentId: string | null = null;
33
+ const unsub = result.subscribe((r) => {
34
+ currentId = r?.id ?? null;
35
+ });
36
+ unsub();
37
+ if (currentId && host && !isStackedChart(host)) {
38
+ removeIndicatorFromChart(host as Chart, currentId);
39
+ }
40
+ };
41
+
42
+ return { result, isLoading, error, run, cleanup: cleanupIndicator };
43
+ }
@@ -0,0 +1,16 @@
1
+ import { StackedChart, StackedChartOptions } from '../core/stacked';
2
+ import { Range } from '../types';
3
+
4
+ export declare function createStackedPlot(options: Omit<StackedChartOptions, "container">): {
5
+ stack: import('svelte/store').Writable<StackedChart | null>;
6
+ isReady: import('svelte/store').Writable<boolean>;
7
+ mount: (container: HTMLDivElement) => void;
8
+ unmount: () => void;
9
+ sync: (next: Omit<StackedChartOptions, "container">) => void;
10
+ fitAll: (opts?: {
11
+ x?: Range;
12
+ padding?: number;
13
+ }) => void | undefined;
14
+ resetAll: () => void | undefined;
15
+ };
16
+ export { createStackedPlot as useStackedPlot };
@@ -0,0 +1,76 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { get } from "svelte/store";
3
+ import { createStackedPlot } from "./useStackedPlot";
4
+
5
+ const mockStack = {
6
+ destroy: vi.fn(),
7
+ whenReady: () => Promise.resolve(),
8
+ container: document.createElement("div"),
9
+ fitAll: vi.fn(),
10
+ resetAll: vi.fn(),
11
+ };
12
+
13
+ let paneKey = "a";
14
+
15
+ vi.mock("../core/stacked", () => ({
16
+ createStackedChart: vi.fn(() => mockStack),
17
+ }));
18
+
19
+ vi.mock("../bindings/shared", async (importOriginal) => {
20
+ const actual = await importOriginal<typeof import("../bindings/shared")>();
21
+ return {
22
+ ...actual,
23
+ stackedStructureKey: () => paneKey,
24
+ syncStackedPaneSeries: vi.fn(() => new Map()),
25
+ syncStackedOptions: vi.fn(),
26
+ };
27
+ });
28
+
29
+ describe("createStackedPlot svelte", () => {
30
+ beforeEach(() => {
31
+ vi.clearAllMocks();
32
+ paneKey = "a";
33
+ });
34
+
35
+ it("mounts stacked chart", () => {
36
+ const el = document.createElement("div");
37
+ const api = createStackedPlot({ panes: [{ id: "p", height: 1, series: [] }] });
38
+ api.mount(el);
39
+ expect(get(api.stack)).toBe(mockStack);
40
+ api.fitAll();
41
+ api.resetAll();
42
+ api.unmount();
43
+ expect(mockStack.destroy).toHaveBeenCalled();
44
+ });
45
+
46
+ it("syncs options when structure is unchanged", async () => {
47
+ const { syncStackedOptions } = await import("../bindings/shared");
48
+ paneKey = "same";
49
+ const el = document.createElement("div");
50
+ const api = createStackedPlot({ panes: [{ id: "p", height: 1, series: [] }], theme: "dark" });
51
+ api.mount(el);
52
+ await mockStack.whenReady();
53
+ api.sync({ panes: [{ id: "p", height: 1, series: [] }], theme: "light" });
54
+ expect(syncStackedOptions).toHaveBeenCalled();
55
+ api.unmount();
56
+ });
57
+
58
+ it("skips sync when stack is not ready", async () => {
59
+ const { syncStackedOptions } = await import("../bindings/shared");
60
+ paneKey = "same";
61
+ const api = createStackedPlot({ panes: [{ id: "p", height: 1, series: [] }] });
62
+ api.sync({ panes: [{ id: "p", height: 1, series: [] }], theme: "dark" });
63
+ expect(syncStackedOptions).not.toHaveBeenCalled();
64
+ });
65
+
66
+ it("remounts when pane structure changes", async () => {
67
+ paneKey = "a";
68
+ const el = document.createElement("div");
69
+ const api = createStackedPlot({ panes: [{ id: "p", height: 1, series: [] }] });
70
+ api.mount(el);
71
+ await mockStack.whenReady();
72
+ paneKey = "b";
73
+ api.sync({ panes: [{ id: "q", height: 1, series: [] }] });
74
+ expect(mockStack.destroy).toHaveBeenCalled();
75
+ });
76
+ });
@@ -0,0 +1,54 @@
1
+ import { writable, get } from "svelte/store";
2
+ import { createStackedChart } from "../core/stacked";
3
+ import type { StackedChart, StackedChartOptions } from "../core/stacked";
4
+ import type { Range } from "../types";
5
+ import { stackedStructureKey, syncStackedOptions, syncStackedPaneSeries } from "../bindings/shared";
6
+ import type { VeloPlotSeries } from "../bindings/shared";
7
+
8
+ export function createStackedPlot(options: Omit<StackedChartOptions, "container">) {
9
+ const stack = writable<StackedChart | null>(null);
10
+ const isReady = writable(false);
11
+ let destroy: (() => void) | null = null;
12
+ let structureKey = stackedStructureKey(options.panes);
13
+ let paneSeries = new Map<string, Map<string, VeloPlotSeries>>();
14
+
15
+ const mount = (container: HTMLDivElement) => {
16
+ destroy?.();
17
+ const created = createStackedChart({ ...options, container });
18
+ stack.set(created);
19
+ destroy = () => created.destroy();
20
+ structureKey = stackedStructureKey(options.panes);
21
+ paneSeries = syncStackedPaneSeries(created, options.panes, new Map());
22
+ created.whenReady().then(() => isReady.set(true));
23
+ };
24
+
25
+ const sync = (next: Omit<StackedChartOptions, "container">) => {
26
+ const s = get(stack);
27
+ if (!s || !get(isReady)) return;
28
+ const nextKey = stackedStructureKey(next.panes);
29
+ if (nextKey !== structureKey) {
30
+ mount(s.container);
31
+ return;
32
+ }
33
+ syncStackedOptions(s, next);
34
+ paneSeries = syncStackedPaneSeries(s, next.panes, paneSeries);
35
+ };
36
+
37
+ const unmount = () => {
38
+ destroy?.();
39
+ stack.set(null);
40
+ isReady.set(false);
41
+ };
42
+
43
+ return {
44
+ stack,
45
+ isReady,
46
+ mount,
47
+ unmount,
48
+ sync,
49
+ fitAll: (opts?: { x?: Range; padding?: number }) => get(stack)?.fitAll(opts),
50
+ resetAll: () => get(stack)?.resetAll(),
51
+ };
52
+ }
53
+
54
+ export { createStackedPlot as useStackedPlot };