velo-plot 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (265) hide show
  1. package/README.md +15 -8
  2. package/dist/{ChartCore-BhQ4j7f5.js → ChartCore-BpzRvN4Y.js} +998 -855
  3. package/dist/ChartCore-BpzRvN4Y.js.map +1 -0
  4. package/dist/a11y-Bw6urYpr.js +105 -0
  5. package/dist/a11y-Bw6urYpr.js.map +1 -0
  6. package/dist/angular/hooks-entry.d.ts +1 -0
  7. package/dist/angular/hooks.d.ts +26 -0
  8. package/dist/angular/index.d.ts +8 -0
  9. package/dist/angular/velo-plot.component.d.ts +46 -0
  10. package/dist/angular-hooks.d.ts +1 -0
  11. package/dist/angular-hooks.js +9 -0
  12. package/dist/angular-hooks.js.map +1 -0
  13. package/dist/angular.d.ts +1 -0
  14. package/dist/angular.js +166 -0
  15. package/dist/angular.js.map +1 -0
  16. package/dist/assets/indicator.worker-bv0zC4EX.js.map +1 -1
  17. package/dist/astro/StackedPlot.astro +19 -0
  18. package/dist/astro/VeloPlot.astro +23 -0
  19. package/dist/{axisFormat-SPX-CD5s.js → axisFormat-CzumxpCL.js} +3 -3
  20. package/dist/axisFormat-CzumxpCL.js.map +1 -0
  21. package/dist/bindings/shared/a11y.d.ts +26 -0
  22. package/dist/bindings/shared/chartLifecycle.d.ts +16 -0
  23. package/dist/bindings/shared/chartSyncBridge.d.ts +8 -0
  24. package/dist/bindings/shared/dimensions.d.ts +1 -0
  25. package/dist/bindings/shared/index.d.ts +9 -0
  26. package/dist/bindings/shared/indicatorBridge.d.ts +11 -0
  27. package/dist/bindings/shared/optionsSync.d.ts +7 -0
  28. package/dist/bindings/shared/seriesDiff.d.ts +8 -0
  29. package/dist/bindings/shared/stackedSync.d.ts +9 -0
  30. package/dist/bindings/shared/types.d.ts +46 -0
  31. package/dist/bindings/test-utils/index.d.ts +35 -0
  32. package/dist/bindings/test-utils/mockLifecycle.d.ts +120 -0
  33. package/dist/bindings/test-utils/vitestSveltePlugin.d.ts +3 -0
  34. package/dist/chartSyncBridge-DbWrwtyP.js +57 -0
  35. package/dist/chartSyncBridge-DbWrwtyP.js.map +1 -0
  36. package/dist/core/Chart.d.ts +1 -1
  37. package/dist/core/InteractionManager.d.ts +7 -1
  38. package/dist/core/OverlayRenderer.d.ts +5 -0
  39. package/dist/core/annotations/types.d.ts +1 -1
  40. package/dist/core/chart/ChartAxisManager.d.ts +2 -0
  41. package/dist/core/chart/ChartCore.d.ts +17 -2
  42. package/dist/core/chart/ChartPluginBridge.d.ts +1 -0
  43. package/dist/core/chart/ChartRenderLoop.d.ts +2 -0
  44. package/dist/core/chart/ChartStateManager.d.ts +2 -0
  45. package/dist/core/chart/types.d.ts +6 -1
  46. package/dist/core/clipboard/index.d.ts +1 -1
  47. package/dist/core/debug/index.d.ts +1 -1
  48. package/dist/core/keybindings/index.d.ts +1 -1
  49. package/dist/core/loading/index.d.ts +1 -1
  50. package/dist/core/locale/index.d.ts +1 -1
  51. package/dist/core/stacked/paneResize.d.ts +3 -0
  52. package/dist/core/stacked/types.d.ts +10 -0
  53. package/dist/core/sync/index.d.ts +0 -1
  54. package/dist/core/theme-editor/index.d.ts +1 -1
  55. package/dist/createStackedChart-Dm-YqUT7.js +756 -0
  56. package/dist/createStackedChart-Dm-YqUT7.js.map +1 -0
  57. package/dist/examples/PluginDemonstration.d.ts +1 -1
  58. package/dist/gpuRenderer-BpaMYz-k.js.map +1 -1
  59. package/dist/hooks-CYPCxoZx.js +69 -0
  60. package/dist/hooks-CYPCxoZx.js.map +1 -0
  61. package/dist/{index-D7dTq6VB.js → index-0InEyj0q.js} +130 -134
  62. package/dist/index-0InEyj0q.js.map +1 -0
  63. package/dist/index-BMiZoKmm.js.map +1 -1
  64. package/dist/{index-DoYxf1cV.js → index-CWxIHdwX.js} +38 -98
  65. package/dist/index-CWxIHdwX.js.map +1 -0
  66. package/dist/{index-CUGS98p4.js → index-CaXWSOTk.js} +3 -6
  67. package/dist/index-CaXWSOTk.js.map +1 -0
  68. package/dist/index-Dag88bW4.js.map +1 -1
  69. package/dist/index-qhscKTDy.js.map +1 -1
  70. package/dist/{index-CWipqOLP.js → index-qunX30Xu.js} +2 -2
  71. package/dist/index-qunX30Xu.js.map +1 -0
  72. package/dist/index-x4stH3jD.js.map +1 -1
  73. package/dist/{index.core-fpZ1dYN0.js → index.core-BmZe2jRQ.js} +30 -29
  74. package/dist/index.core-BmZe2jRQ.js.map +1 -0
  75. package/dist/index.core.d.ts +1 -1
  76. package/dist/index.d.ts +3 -3
  77. package/dist/indicators--eJGmVox.js.map +1 -1
  78. package/dist/{mockDatafeed-Cdg5hKEo.js → mockDatafeed-DHIDPcpX.js} +150 -146
  79. package/dist/mockDatafeed-DHIDPcpX.js.map +1 -0
  80. package/dist/optionsSync-QUiN4Inw.js +119 -0
  81. package/dist/optionsSync-QUiN4Inw.js.map +1 -0
  82. package/dist/plugins/3d/index.d.ts +2 -2
  83. package/dist/plugins/3d.d.ts +2 -2
  84. package/dist/plugins/3d.js.map +1 -1
  85. package/dist/plugins/analysis/contours.d.ts +31 -0
  86. package/dist/plugins/analysis/index.d.ts +1 -1
  87. package/dist/plugins/analysis/indicators.d.ts +1 -1
  88. package/dist/plugins/analysis.d.ts +2 -2
  89. package/dist/plugins/analysis.js +495 -408
  90. package/dist/plugins/analysis.js.map +1 -1
  91. package/dist/plugins/annotations/index.d.ts +1 -1
  92. package/dist/plugins/annotations.d.ts +2 -2
  93. package/dist/plugins/annotations.js.map +1 -1
  94. package/dist/plugins/anomaly-detection.js +1 -1
  95. package/dist/plugins/anomaly-detection.js.map +1 -1
  96. package/dist/plugins/broken-axis.d.ts +2 -2
  97. package/dist/plugins/broken-axis.js +170 -181
  98. package/dist/plugins/broken-axis.js.map +1 -1
  99. package/dist/plugins/builtins/crosshair/CrosshairPlugin.d.ts +1 -1
  100. package/dist/plugins/builtins/index.d.ts +1 -1
  101. package/dist/plugins/caching.d.ts +2 -2
  102. package/dist/plugins/clipboard/index.d.ts +2 -2
  103. package/dist/plugins/clipboard.d.ts +2 -2
  104. package/dist/plugins/clipboard.js.map +1 -1
  105. package/dist/plugins/context-menu/index.d.ts +1 -1
  106. package/dist/plugins/context-menu.d.ts +2 -2
  107. package/dist/plugins/context-menu.js +1 -1
  108. package/dist/plugins/context-menu.js.map +1 -1
  109. package/dist/plugins/data-export/index.d.ts +1 -1
  110. package/dist/plugins/data-export.d.ts +2 -2
  111. package/dist/plugins/data-export.js +2 -2
  112. package/dist/plugins/data-export.js.map +1 -1
  113. package/dist/plugins/data-transform.d.ts +2 -2
  114. package/dist/plugins/data-transform.js +19 -19
  115. package/dist/plugins/data-transform.js.map +1 -1
  116. package/dist/plugins/debug/index.d.ts +2 -2
  117. package/dist/plugins/debug.d.ts +2 -2
  118. package/dist/plugins/debug.js +2 -2
  119. package/dist/plugins/debug.js.map +1 -1
  120. package/dist/plugins/forecasting/algorithms.d.ts +7 -2
  121. package/dist/plugins/forecasting/index.d.ts +1 -1
  122. package/dist/plugins/forecasting/types.d.ts +13 -3
  123. package/dist/plugins/forecasting.js +355 -175
  124. package/dist/plugins/forecasting.js.map +1 -1
  125. package/dist/plugins/gpu/index.d.ts +2 -2
  126. package/dist/plugins/gpu.d.ts +2 -2
  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.map +1 -1
  131. package/dist/plugins/index.d.ts +1 -1
  132. package/dist/plugins/keyboard.d.ts +2 -2
  133. package/dist/plugins/keyboard.js +65 -5
  134. package/dist/plugins/keyboard.js.map +1 -1
  135. package/dist/plugins/latex/symbols.d.ts +50 -0
  136. package/dist/plugins/latex/types.d.ts +13 -1
  137. package/dist/plugins/latex.js +351 -331
  138. package/dist/plugins/latex.js.map +1 -1
  139. package/dist/plugins/lazy-load.d.ts +2 -2
  140. package/dist/plugins/loading/index.d.ts +1 -1
  141. package/dist/plugins/loading.d.ts +2 -2
  142. package/dist/plugins/ml-integration/native-algorithms.d.ts +24 -4
  143. package/dist/plugins/ml-integration/types.d.ts +19 -0
  144. package/dist/plugins/ml-integration.d.ts +2 -2
  145. package/dist/plugins/ml-integration.js +190 -139
  146. package/dist/plugins/ml-integration.js.map +1 -1
  147. package/dist/plugins/offscreen.d.ts +2 -2
  148. package/dist/plugins/pattern-recognition/types.d.ts +28 -1
  149. package/dist/plugins/pattern-recognition.d.ts +2 -2
  150. package/dist/plugins/pattern-recognition.js +417 -344
  151. package/dist/plugins/pattern-recognition.js.map +1 -1
  152. package/dist/plugins/radar.d.ts +2 -2
  153. package/dist/plugins/regression.d.ts +2 -2
  154. package/dist/plugins/regression.js +1 -1
  155. package/dist/plugins/regression.js.map +1 -1
  156. package/dist/plugins/roi.js +9 -9
  157. package/dist/plugins/roi.js.map +1 -1
  158. package/dist/plugins/snapshot/index.d.ts +1 -1
  159. package/dist/plugins/snapshot/types.d.ts +1 -1
  160. package/dist/plugins/snapshot.d.ts +2 -2
  161. package/dist/plugins/snapshot.js +36 -36
  162. package/dist/plugins/snapshot.js.map +1 -1
  163. package/dist/plugins/streaming/index.d.ts +2 -2
  164. package/dist/plugins/streaming.d.ts +2 -2
  165. package/dist/plugins/sync/index.d.ts +3 -2
  166. package/dist/plugins/sync.d.ts +2 -2
  167. package/dist/plugins/sync.js +1 -1
  168. package/dist/plugins/sync.js.map +1 -1
  169. package/dist/plugins/theme-editor/index.d.ts +1 -1
  170. package/dist/plugins/theme-editor.d.ts +2 -2
  171. package/dist/plugins/tools/index.d.ts +1 -1
  172. package/dist/plugins/tools/tooltip/index.d.ts +1 -1
  173. package/dist/plugins/tools.d.ts +2 -2
  174. package/dist/plugins/tools.js +1 -1
  175. package/dist/plugins/types.d.ts +1 -1
  176. package/dist/plugins/video-recorder.d.ts +2 -2
  177. package/dist/plugins/virtualization.d.ts +2 -2
  178. package/dist/plugins/virtualization.js +1 -1
  179. package/dist/pool-DZ8shSqv.js +109 -0
  180. package/dist/pool-DZ8shSqv.js.map +1 -0
  181. package/dist/react/StackedPlot.d.ts +31 -0
  182. package/dist/react/VeloPlot.d.ts +29 -0
  183. package/dist/react/index.d.ts +25 -3
  184. package/dist/react/useChartSync.d.ts +8 -0
  185. package/dist/react/useIndicator.d.ts +12 -0
  186. package/dist/react/useVeloPlot.d.ts +19 -0
  187. package/dist/react.js +328 -24
  188. package/dist/react.js.map +1 -1
  189. package/dist/scientific/index.d.ts +48 -0
  190. package/dist/scientific.d.ts +1 -0
  191. package/dist/scientific.js +91 -0
  192. package/dist/scientific.js.map +1 -0
  193. package/dist/solid/VeloPlot.d.ts +16 -0
  194. package/dist/solid/index.d.ts +13 -0
  195. package/dist/solid/useChartSync.d.ts +6 -0
  196. package/dist/solid/useIndicator.d.ts +11 -0
  197. package/dist/solid/useStackedPlot.d.ts +14 -0
  198. package/dist/solid/useVeloPlot.d.ts +18 -0
  199. package/dist/solid.d.ts +1 -0
  200. package/dist/solid.js +172 -0
  201. package/dist/solid.js.map +1 -0
  202. package/dist/stackedSync-tnrunZMt.js +100 -0
  203. package/dist/stackedSync-tnrunZMt.js.map +1 -0
  204. package/dist/svelte/StackedPlot.svelte +29 -0
  205. package/dist/svelte/StackedPlot.test.ts +114 -0
  206. package/dist/svelte/VeloPlot.svelte +44 -0
  207. package/dist/svelte/VeloPlot.test.ts +105 -0
  208. package/dist/svelte/index.d.ts +11 -0
  209. package/dist/svelte/index.test.ts +20 -0
  210. package/dist/svelte/index.ts +15 -0
  211. package/dist/svelte/useChartSync.d.ts +8 -0
  212. package/dist/svelte/useChartSync.test.ts +49 -0
  213. package/dist/svelte/useChartSync.ts +29 -0
  214. package/dist/svelte/useIndicator.d.ts +13 -0
  215. package/dist/svelte/useIndicator.test.ts +67 -0
  216. package/dist/svelte/useIndicator.ts +43 -0
  217. package/dist/svelte/useStackedPlot.d.ts +16 -0
  218. package/dist/svelte/useStackedPlot.test.ts +76 -0
  219. package/dist/svelte/useStackedPlot.ts +54 -0
  220. package/dist/svelte/useVeloPlot.d.ts +21 -0
  221. package/dist/svelte/useVeloPlot.test.ts +100 -0
  222. package/dist/svelte/useVeloPlot.ts +81 -0
  223. package/dist/svelte.d.ts +1 -0
  224. package/dist/svelte.js +143 -0
  225. package/dist/svelte.js.map +1 -0
  226. package/dist/symbols-DT0eLlpW.js +568 -0
  227. package/dist/symbols-DT0eLlpW.js.map +1 -0
  228. package/dist/testing/index.d.ts +1 -1
  229. package/dist/theme/index.d.ts +2 -1
  230. package/dist/trading.js +7 -7
  231. package/dist/types.d.ts +1 -1
  232. package/dist/{utils-BK5UUXxj.js → utils-Dx0h0bgQ.js} +9 -9
  233. package/dist/{utils-BK5UUXxj.js.map → utils-Dx0h0bgQ.js.map} +1 -1
  234. package/dist/velo-plot.full.js +444 -433
  235. package/dist/velo-plot.full.js.map +1 -1
  236. package/dist/velo-plot.js +15 -15
  237. package/dist/vue/StackedPlot.vue.d.ts +48 -0
  238. package/dist/vue/VeloPlot.vue.d.ts +64 -0
  239. package/dist/vue/index.d.ts +250 -0
  240. package/dist/vue/useChartSync.d.ts +52 -0
  241. package/dist/vue/useIndicator.d.ts +23 -0
  242. package/dist/vue/useStackedPlot.d.ts +15 -0
  243. package/dist/vue/useVeloPlot.d.ts +20 -0
  244. package/dist/vue.d.ts +1 -0
  245. package/dist/vue.js +312 -0
  246. package/dist/vue.js.map +1 -0
  247. package/dist/workers/indicatorsAsync.d.ts +26 -0
  248. package/dist/workers/pool.d.ts +9 -0
  249. package/package.json +88 -10
  250. package/dist/ChartCore-BhQ4j7f5.js.map +0 -1
  251. package/dist/SciPlot-GJvw7GJo.js +0 -463
  252. package/dist/SciPlot-GJvw7GJo.js.map +0 -1
  253. package/dist/axisFormat-SPX-CD5s.js.map +0 -1
  254. package/dist/createStackedChart-DJSmqerD.js +0 -706
  255. package/dist/createStackedChart-DJSmqerD.js.map +0 -1
  256. package/dist/index-CUGS98p4.js.map +0 -1
  257. package/dist/index-CWipqOLP.js.map +0 -1
  258. package/dist/index-D7dTq6VB.js.map +0 -1
  259. package/dist/index-DoYxf1cV.js.map +0 -1
  260. package/dist/index.core-fpZ1dYN0.js.map +0 -1
  261. package/dist/mockDatafeed-Cdg5hKEo.js.map +0 -1
  262. package/dist/pool-15HMCNv8.js +0 -88
  263. package/dist/pool-15HMCNv8.js.map +0 -1
  264. package/dist/react/SciPlot.d.ts +0 -60
  265. package/dist/react/useSciPlot.d.ts +0 -56
@@ -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 };
@@ -0,0 +1,21 @@
1
+ import { Chart } from '../core/Chart';
2
+ import { SeriesOptions, SeriesUpdateData, ZoomOptions, Bounds } from '../types';
3
+ import { ChartBindingOptions } from '../bindings/shared';
4
+
5
+ export interface UseVeloPlotOptions extends ChartBindingOptions {
6
+ }
7
+ export declare function createVeloPlot(options?: UseVeloPlotOptions): {
8
+ chart: import('svelte/store').Writable<Chart | null>;
9
+ isReady: import('svelte/store').Writable<boolean>;
10
+ error: import('svelte/store').Writable<Error | null>;
11
+ bounds: import('svelte/store').Writable<Bounds | null>;
12
+ mount: (container: HTMLDivElement) => void;
13
+ unmount: () => void;
14
+ updateOptions: (next: UseVeloPlotOptions) => void;
15
+ addSeries: (opts: SeriesOptions) => void;
16
+ updateSeries: (id: string, data: SeriesUpdateData) => void | undefined;
17
+ removeSeries: (id: string) => void | undefined;
18
+ zoom: (z: ZoomOptions) => void | undefined;
19
+ resetZoom: () => void;
20
+ };
21
+ export { createVeloPlot as useVeloPlot };
@@ -0,0 +1,100 @@
1
+ import { describe, it, expect, vi, afterEach } from "vitest";
2
+ import { get } from "svelte/store";
3
+ import { createVeloPlot } from "./useVeloPlot";
4
+ import { buildMockChart } from "../bindings/test-utils";
5
+ import * as shared from "../bindings/shared";
6
+
7
+ const mockChart = buildMockChart();
8
+
9
+ vi.mock("../bindings/shared/chartLifecycle", () => ({
10
+ createChartLifecycle: vi.fn(() => ({
11
+ chart: mockChart,
12
+ getBounds: () => mockChart.getViewBounds(),
13
+ destroy: mockChart.destroy,
14
+ })),
15
+ }));
16
+
17
+ vi.spyOn(shared, "optionsChanged").mockReturnValue(true);
18
+ vi.spyOn(shared, "syncChartOptions");
19
+
20
+ describe("createVeloPlot svelte", () => {
21
+ afterEach(() => {
22
+ document.body.replaceChildren();
23
+ vi.clearAllMocks();
24
+ vi.mocked(shared.optionsChanged).mockReturnValue(true);
25
+ });
26
+
27
+ it("mounts and exercises chart API", () => {
28
+ const el = document.createElement("div");
29
+ document.body.appendChild(el);
30
+ const api = createVeloPlot({ animations: false });
31
+ api.mount(el);
32
+ expect(get(api.isReady)).toBe(true);
33
+ api.addSeries({ id: "s", type: "line", data: { x: new Float32Array(), y: new Float32Array() } });
34
+ api.updateSeries("s", { x: new Float32Array(), y: new Float32Array() });
35
+ api.removeSeries("s");
36
+ api.zoom({ x: [0, 1] });
37
+ api.resetZoom();
38
+ api.updateOptions({ theme: "dark" });
39
+ expect(shared.syncChartOptions).toHaveBeenCalled();
40
+ api.unmount();
41
+ expect(mockChart.destroy).toHaveBeenCalled();
42
+ el.remove();
43
+ });
44
+
45
+ it("remounts chart on second mount call", () => {
46
+ const el = document.createElement("div");
47
+ const api = createVeloPlot({});
48
+ api.mount(el);
49
+ api.mount(el);
50
+ expect(mockChart.destroy).toHaveBeenCalled();
51
+ api.unmount();
52
+ });
53
+
54
+ it("invokes onBoundsChange from lifecycle", async () => {
55
+ const el = document.createElement("div");
56
+ const api = createVeloPlot({});
57
+ api.mount(el);
58
+ const { createChartLifecycle } = await import("../bindings/shared/chartLifecycle");
59
+ const callbacks = vi.mocked(createChartLifecycle).mock.calls.at(-1)?.[2] ?? {};
60
+ callbacks.onBoundsChange?.(mockChart.getViewBounds());
61
+ expect(get(api.bounds)).toEqual(mockChart.getViewBounds());
62
+ api.unmount();
63
+ });
64
+
65
+ it("updateOptions skips sync when chart is missing", () => {
66
+ const api = createVeloPlot({ theme: "dark" });
67
+ vi.mocked(shared.syncChartOptions).mockClear();
68
+ api.updateOptions({ theme: "light" });
69
+ expect(shared.syncChartOptions).not.toHaveBeenCalled();
70
+ });
71
+
72
+ it("updateOptions skips sync when options are unchanged", () => {
73
+ vi.mocked(shared.optionsChanged).mockReturnValue(false);
74
+ const el = document.createElement("div");
75
+ const api = createVeloPlot({ theme: "dark" });
76
+ api.mount(el);
77
+ vi.mocked(shared.syncChartOptions).mockClear();
78
+ api.updateOptions({ theme: "dark" });
79
+ expect(shared.syncChartOptions).not.toHaveBeenCalled();
80
+ api.unmount();
81
+ });
82
+
83
+ it("records lifecycle errors", async () => {
84
+ const { createChartLifecycle } = await import("../bindings/shared/chartLifecycle");
85
+ vi.mocked(createChartLifecycle).mockImplementationOnce((_el, _opts, callbacks = {}) => {
86
+ queueMicrotask(() => callbacks.onError?.(new Error("svelte lifecycle")));
87
+ return {
88
+ chart: mockChart,
89
+ getBounds: () => mockChart.getViewBounds(),
90
+ destroy: mockChart.destroy,
91
+ };
92
+ });
93
+ const el = document.createElement("div");
94
+ const api = createVeloPlot({});
95
+ api.mount(el);
96
+ await Promise.resolve();
97
+ expect(get(api.error)?.message).toBe("svelte lifecycle");
98
+ api.unmount();
99
+ });
100
+ });
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Svelte composables for velo-plot (exported as functions).
3
+ */
4
+
5
+ import { writable, get } from "svelte/store";
6
+ import type { Chart } from "../core/Chart";
7
+ import type { SeriesOptions, SeriesUpdateData, ZoomOptions, Bounds } from "../types";
8
+ import {
9
+ createChartLifecycle,
10
+ type ChartBindingOptions,
11
+ optionsChanged,
12
+ syncChartOptions,
13
+ pickSyncableOptions,
14
+ } from "../bindings/shared";
15
+
16
+ export interface UseVeloPlotOptions extends ChartBindingOptions {}
17
+
18
+ export function createVeloPlot(options: UseVeloPlotOptions = {}) {
19
+ const chart = writable<Chart | null>(null);
20
+ const isReady = writable(false);
21
+ const error = writable<Error | null>(null);
22
+ const bounds = writable<Bounds | null>(null);
23
+
24
+ let destroy: (() => void) | null = null;
25
+ let prevSync = pickSyncableOptions(options);
26
+
27
+ const mount = (container: HTMLDivElement) => {
28
+ destroy?.();
29
+ const handle = createChartLifecycle(container, options, {
30
+ onBoundsChange: (b) => bounds.set(b),
31
+ onError: (e) => error.set(e),
32
+ });
33
+ chart.set(handle.chart);
34
+ destroy = handle.destroy;
35
+ bounds.set(handle.getBounds());
36
+ isReady.set(true);
37
+ prevSync = pickSyncableOptions(options);
38
+ };
39
+
40
+ const updateOptions = (next: UseVeloPlotOptions) => {
41
+ const c = get(chart);
42
+ if (!c) return;
43
+ const picked = pickSyncableOptions(next);
44
+ if (optionsChanged(prevSync, picked)) {
45
+ syncChartOptions(c, prevSync, picked);
46
+ prevSync = picked;
47
+ }
48
+ };
49
+
50
+ const unmount = () => {
51
+ destroy?.();
52
+ destroy = null;
53
+ chart.set(null);
54
+ isReady.set(false);
55
+ };
56
+
57
+ return {
58
+ chart,
59
+ isReady,
60
+ error,
61
+ bounds,
62
+ mount,
63
+ unmount,
64
+ updateOptions,
65
+ addSeries: (opts: SeriesOptions) => {
66
+ get(chart)?.addSeries(opts);
67
+ const c = get(chart);
68
+ if (c) bounds.set(c.getViewBounds());
69
+ },
70
+ updateSeries: (id: string, data: SeriesUpdateData) => get(chart)?.updateSeries(id, data),
71
+ removeSeries: (id: string) => get(chart)?.removeSeries(id),
72
+ zoom: (z: ZoomOptions) => get(chart)?.zoom(z),
73
+ resetZoom: () => {
74
+ get(chart)?.resetZoom();
75
+ const c = get(chart);
76
+ if (c) bounds.set(c.getViewBounds());
77
+ },
78
+ };
79
+ }
80
+
81
+ export { createVeloPlot as useVeloPlot };
@@ -0,0 +1 @@
1
+ export * from './svelte/index'