@363045841yyt/klinechart-core 0.9.0-alpha.0 → 0.9.0-alpha.2

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 (372) hide show
  1. package/dist/controllers/createChartController.d.ts.map +1 -1
  2. package/dist/controllers/createChartController.js +31 -13
  3. package/dist/controllers/createChartController.js.map +1 -1
  4. package/dist/controllers/index.d.ts +1 -0
  5. package/dist/controllers/index.d.ts.map +1 -1
  6. package/dist/controllers/index.js.map +1 -1
  7. package/dist/controllers/types.d.ts +12 -0
  8. package/dist/controllers/types.d.ts.map +1 -1
  9. package/dist/engine/chart.d.ts +29 -11
  10. package/dist/engine/chart.d.ts.map +1 -1
  11. package/dist/engine/chart.js +147 -25
  12. package/dist/engine/chart.js.map +1 -1
  13. package/dist/engine/controller/interaction.d.ts +29 -4
  14. package/dist/engine/controller/interaction.d.ts.map +1 -1
  15. package/dist/engine/controller/interaction.js +78 -31
  16. package/dist/engine/controller/interaction.js.map +1 -1
  17. package/dist/engine/data/incrementalLoadHint.d.ts.map +1 -1
  18. package/dist/engine/data/incrementalLoadHint.js +5 -1
  19. package/dist/engine/data/incrementalLoadHint.js.map +1 -1
  20. package/dist/engine/drawing/DrawingState.d.ts +19 -34
  21. package/dist/engine/drawing/DrawingState.d.ts.map +1 -1
  22. package/dist/engine/drawing/DrawingState.js +110 -92
  23. package/dist/engine/drawing/DrawingState.js.map +1 -1
  24. package/dist/engine/drawing/index.d.ts +4 -1
  25. package/dist/engine/drawing/index.d.ts.map +1 -1
  26. package/dist/engine/drawing/index.js +9 -4
  27. package/dist/engine/drawing/index.js.map +1 -1
  28. package/dist/engine/drawing/interaction.d.ts +9 -29
  29. package/dist/engine/drawing/interaction.d.ts.map +1 -1
  30. package/dist/engine/drawing/interaction.js +23 -40
  31. package/dist/engine/drawing/interaction.js.map +1 -1
  32. package/dist/engine/indicators/chartIndicatorManager.d.ts.map +1 -1
  33. package/dist/engine/indicators/chartIndicatorManager.js +4 -12
  34. package/dist/engine/indicators/chartIndicatorManager.js.map +1 -1
  35. package/dist/engine/layout/chartPaneLayout.d.ts +0 -2
  36. package/dist/engine/layout/chartPaneLayout.d.ts.map +1 -1
  37. package/dist/engine/layout/chartPaneLayout.js +5 -9
  38. package/dist/engine/layout/chartPaneLayout.js.map +1 -1
  39. package/dist/engine/layout/pane.d.ts +0 -39
  40. package/dist/engine/layout/pane.d.ts.map +1 -1
  41. package/dist/engine/layout/pane.js +0 -9
  42. package/dist/engine/layout/pane.js.map +1 -1
  43. package/dist/engine/paneRenderer.d.ts +1 -10
  44. package/dist/engine/paneRenderer.d.ts.map +1 -1
  45. package/dist/engine/paneRenderer.js +2 -19
  46. package/dist/engine/paneRenderer.js.map +1 -1
  47. package/dist/engine/render/chartRenderer.d.ts +49 -8
  48. package/dist/engine/render/chartRenderer.d.ts.map +1 -1
  49. package/dist/engine/render/chartRenderer.js +143 -72
  50. package/dist/engine/render/chartRenderer.js.map +1 -1
  51. package/dist/engine/renderers/Indicator/atr.js +3 -13
  52. package/dist/engine/renderers/Indicator/atr.js.map +1 -1
  53. package/dist/engine/renderers/Indicator/boll.d.ts.map +1 -1
  54. package/dist/engine/renderers/Indicator/boll.js +12 -24
  55. package/dist/engine/renderers/Indicator/boll.js.map +1 -1
  56. package/dist/engine/renderers/Indicator/cci.js +6 -13
  57. package/dist/engine/renderers/Indicator/cci.js.map +1 -1
  58. package/dist/engine/renderers/Indicator/chaikinVol.js +3 -11
  59. package/dist/engine/renderers/Indicator/chaikinVol.js.map +1 -1
  60. package/dist/engine/renderers/Indicator/cmf.js +3 -11
  61. package/dist/engine/renderers/Indicator/cmf.js.map +1 -1
  62. package/dist/engine/renderers/Indicator/dema.js +3 -11
  63. package/dist/engine/renderers/Indicator/dema.js.map +1 -1
  64. package/dist/engine/renderers/Indicator/donchian.js +3 -11
  65. package/dist/engine/renderers/Indicator/donchian.js.map +1 -1
  66. package/dist/engine/renderers/Indicator/ene.d.ts.map +1 -1
  67. package/dist/engine/renderers/Indicator/ene.js +12 -24
  68. package/dist/engine/renderers/Indicator/ene.js.map +1 -1
  69. package/dist/engine/renderers/Indicator/expma.d.ts.map +1 -1
  70. package/dist/engine/renderers/Indicator/expma.js +5 -11
  71. package/dist/engine/renderers/Indicator/expma.js.map +1 -1
  72. package/dist/engine/renderers/Indicator/fastk.js +6 -13
  73. package/dist/engine/renderers/Indicator/fastk.js.map +1 -1
  74. package/dist/engine/renderers/Indicator/fib.js +3 -11
  75. package/dist/engine/renderers/Indicator/fib.js.map +1 -1
  76. package/dist/engine/renderers/Indicator/hma.js +3 -11
  77. package/dist/engine/renderers/Indicator/hma.js.map +1 -1
  78. package/dist/engine/renderers/Indicator/hv.js +3 -11
  79. package/dist/engine/renderers/Indicator/hv.js.map +1 -1
  80. package/dist/engine/renderers/Indicator/ichimoku.js +24 -18
  81. package/dist/engine/renderers/Indicator/ichimoku.js.map +1 -1
  82. package/dist/engine/renderers/Indicator/kama.js +3 -11
  83. package/dist/engine/renderers/Indicator/kama.js.map +1 -1
  84. package/dist/engine/renderers/Indicator/keltner.js +3 -11
  85. package/dist/engine/renderers/Indicator/keltner.js.map +1 -1
  86. package/dist/engine/renderers/Indicator/kst.js +9 -17
  87. package/dist/engine/renderers/Indicator/kst.js.map +1 -1
  88. package/dist/engine/renderers/Indicator/ma.d.ts.map +1 -1
  89. package/dist/engine/renderers/Indicator/ma.js +10 -21
  90. package/dist/engine/renderers/Indicator/ma.js.map +1 -1
  91. package/dist/engine/renderers/Indicator/macd.js +14 -44
  92. package/dist/engine/renderers/Indicator/macd.js.map +1 -1
  93. package/dist/engine/renderers/Indicator/mfi.js +3 -11
  94. package/dist/engine/renderers/Indicator/mfi.js.map +1 -1
  95. package/dist/engine/renderers/Indicator/mom.js +6 -13
  96. package/dist/engine/renderers/Indicator/mom.js.map +1 -1
  97. package/dist/engine/renderers/Indicator/obv.js +3 -11
  98. package/dist/engine/renderers/Indicator/obv.js.map +1 -1
  99. package/dist/engine/renderers/Indicator/parkinson.js +3 -11
  100. package/dist/engine/renderers/Indicator/parkinson.js.map +1 -1
  101. package/dist/engine/renderers/Indicator/pvt.js +3 -11
  102. package/dist/engine/renderers/Indicator/pvt.js.map +1 -1
  103. package/dist/engine/renderers/Indicator/roc.js +3 -11
  104. package/dist/engine/renderers/Indicator/roc.js.map +1 -1
  105. package/dist/engine/renderers/Indicator/rsi.js +12 -20
  106. package/dist/engine/renderers/Indicator/rsi.js.map +1 -1
  107. package/dist/engine/renderers/Indicator/shared/webglBand.d.ts +0 -2
  108. package/dist/engine/renderers/Indicator/shared/webglBand.d.ts.map +1 -1
  109. package/dist/engine/renderers/Indicator/shared/webglBand.js +0 -6
  110. package/dist/engine/renderers/Indicator/shared/webglBand.js.map +1 -1
  111. package/dist/engine/renderers/Indicator/stoch.js +9 -17
  112. package/dist/engine/renderers/Indicator/stoch.js.map +1 -1
  113. package/dist/engine/renderers/Indicator/tema.js +3 -11
  114. package/dist/engine/renderers/Indicator/tema.js.map +1 -1
  115. package/dist/engine/renderers/Indicator/trix.js +3 -11
  116. package/dist/engine/renderers/Indicator/trix.js.map +1 -1
  117. package/dist/engine/renderers/Indicator/vma.js +3 -11
  118. package/dist/engine/renderers/Indicator/vma.js.map +1 -1
  119. package/dist/engine/renderers/Indicator/vwap.js +3 -11
  120. package/dist/engine/renderers/Indicator/vwap.js.map +1 -1
  121. package/dist/engine/renderers/Indicator/wma.js +3 -11
  122. package/dist/engine/renderers/Indicator/wma.js.map +1 -1
  123. package/dist/engine/renderers/Indicator/wmsr.js +6 -13
  124. package/dist/engine/renderers/Indicator/wmsr.js.map +1 -1
  125. package/dist/engine/renderers/candle.d.ts.map +1 -1
  126. package/dist/engine/renderers/candle.js +21 -28
  127. package/dist/engine/renderers/candle.js.map +1 -1
  128. package/dist/engine/renderers/candleViaRenderer.d.ts +19 -0
  129. package/dist/engine/renderers/candleViaRenderer.d.ts.map +1 -0
  130. package/dist/engine/renderers/candleViaRenderer.js +15 -0
  131. package/dist/engine/renderers/candleViaRenderer.js.map +1 -0
  132. package/dist/engine/renderers/linesViaRenderer.d.ts +55 -0
  133. package/dist/engine/renderers/linesViaRenderer.d.ts.map +1 -0
  134. package/dist/engine/renderers/linesViaRenderer.js +169 -0
  135. package/dist/engine/renderers/linesViaRenderer.js.map +1 -0
  136. package/dist/engine/renderers/rectsViaRenderer.d.ts +18 -0
  137. package/dist/engine/renderers/rectsViaRenderer.d.ts.map +1 -0
  138. package/dist/engine/renderers/rectsViaRenderer.js +63 -0
  139. package/dist/engine/renderers/rectsViaRenderer.js.map +1 -0
  140. package/dist/engine/renderers/subVolume.js +7 -26
  141. package/dist/engine/renderers/subVolume.js.map +1 -1
  142. package/dist/engine/renderers/timeAxis.js +1 -1
  143. package/dist/engine/renderers/timeAxis.js.map +1 -1
  144. package/dist/engine/state/chartStateKernel.d.ts +11 -2
  145. package/dist/engine/state/chartStateKernel.d.ts.map +1 -1
  146. package/dist/engine/state/chartStateKernel.js +28 -12
  147. package/dist/engine/state/chartStateKernel.js.map +1 -1
  148. package/dist/engine/state/index.d.ts +1 -1
  149. package/dist/engine/state/index.d.ts.map +1 -1
  150. package/dist/engine/state/index.js +1 -1
  151. package/dist/engine/state/index.js.map +1 -1
  152. package/dist/engine/state/interactionState.d.ts +15 -7
  153. package/dist/engine/state/interactionState.d.ts.map +1 -1
  154. package/dist/engine/state/interactionState.js +55 -60
  155. package/dist/engine/state/interactionState.js.map +1 -1
  156. package/dist/engine/state/rendererState.d.ts +14 -0
  157. package/dist/engine/state/rendererState.d.ts.map +1 -0
  158. package/dist/engine/state/rendererState.js +25 -0
  159. package/dist/engine/state/rendererState.js.map +1 -0
  160. package/dist/engine/state/themeState.d.ts +11 -4
  161. package/dist/engine/state/themeState.d.ts.map +1 -1
  162. package/dist/engine/state/themeState.js +13 -5
  163. package/dist/engine/state/themeState.js.map +1 -1
  164. package/dist/engine/state/viewportState.d.ts.map +1 -1
  165. package/dist/engine/state/viewportState.js +7 -3
  166. package/dist/engine/state/viewportState.js.map +1 -1
  167. package/dist/engine/subPaneManager.d.ts.map +1 -1
  168. package/dist/engine/subPaneManager.js +2 -10
  169. package/dist/engine/subPaneManager.js.map +1 -1
  170. package/dist/foundation/config/chartSettings.d.ts +16 -4
  171. package/dist/foundation/config/chartSettings.d.ts.map +1 -1
  172. package/dist/foundation/config/chartSettings.js +21 -6
  173. package/dist/foundation/config/chartSettings.js.map +1 -1
  174. package/dist/foundation/plugin/index.d.ts +0 -1
  175. package/dist/foundation/plugin/index.d.ts.map +1 -1
  176. package/dist/foundation/plugin/rendererPluginManager.d.ts +1 -22
  177. package/dist/foundation/plugin/rendererPluginManager.d.ts.map +1 -1
  178. package/dist/foundation/plugin/rendererPluginManager.js +0 -84
  179. package/dist/foundation/plugin/rendererPluginManager.js.map +1 -1
  180. package/dist/foundation/plugin/types.d.ts +11 -17
  181. package/dist/foundation/plugin/types.d.ts.map +1 -1
  182. package/dist/foundation/plugin/types.js.map +1 -1
  183. package/dist/foundation/reactivity/frameTransaction.d.ts +71 -0
  184. package/dist/foundation/reactivity/frameTransaction.d.ts.map +1 -0
  185. package/dist/foundation/reactivity/frameTransaction.js +170 -0
  186. package/dist/foundation/reactivity/frameTransaction.js.map +1 -0
  187. package/dist/foundation/reactivity/index.d.ts +4 -2
  188. package/dist/foundation/reactivity/index.d.ts.map +1 -1
  189. package/dist/foundation/reactivity/index.js +2 -1
  190. package/dist/foundation/reactivity/index.js.map +1 -1
  191. package/dist/foundation/reactivity/signal.d.ts +20 -0
  192. package/dist/foundation/reactivity/signal.d.ts.map +1 -1
  193. package/dist/foundation/reactivity/signal.js +27 -0
  194. package/dist/foundation/reactivity/signal.js.map +1 -1
  195. package/dist/foundation/tokens/theme-dark.d.ts.map +1 -1
  196. package/dist/foundation/tokens/theme-dark.js +2 -1
  197. package/dist/foundation/tokens/theme-dark.js.map +1 -1
  198. package/dist/foundation/tokens/theme-light.d.ts.map +1 -1
  199. package/dist/foundation/tokens/theme-light.js +2 -1
  200. package/dist/foundation/tokens/theme-light.js.map +1 -1
  201. package/dist/rendering/render/Renderer.d.ts +29 -14
  202. package/dist/rendering/render/Renderer.d.ts.map +1 -1
  203. package/dist/rendering/render/Renderer.js +3 -10
  204. package/dist/rendering/render/Renderer.js.map +1 -1
  205. package/dist/rendering/render/SurfaceBackend.d.ts +5 -7
  206. package/dist/rendering/render/SurfaceBackend.d.ts.map +1 -1
  207. package/dist/rendering/render/SurfaceBackend.js +5 -7
  208. package/dist/rendering/render/SurfaceBackend.js.map +1 -1
  209. package/dist/rendering/render/createCanvas2DRenderer.d.ts +3 -0
  210. package/dist/rendering/render/createCanvas2DRenderer.d.ts.map +1 -0
  211. package/dist/rendering/render/createCanvas2DRenderer.js +74 -0
  212. package/dist/rendering/render/createCanvas2DRenderer.js.map +1 -0
  213. package/dist/rendering/render/createDefaultRendererHost.d.ts +4 -0
  214. package/dist/rendering/render/createDefaultRendererHost.d.ts.map +1 -0
  215. package/dist/rendering/render/createDefaultRendererHost.js +54 -0
  216. package/dist/rendering/render/createDefaultRendererHost.js.map +1 -0
  217. package/dist/rendering/render/createWebGLRenderer.d.ts.map +1 -1
  218. package/dist/rendering/render/createWebGLRenderer.js +42 -80
  219. package/dist/rendering/render/createWebGLRenderer.js.map +1 -1
  220. package/dist/rendering/render/createWebGPURenderer.d.ts +11 -0
  221. package/dist/rendering/render/createWebGPURenderer.d.ts.map +1 -0
  222. package/dist/rendering/render/createWebGPURenderer.js +619 -0
  223. package/dist/rendering/render/createWebGPURenderer.js.map +1 -0
  224. package/dist/rendering/render/createWebGPUSurfaceBackend.d.ts +15 -0
  225. package/dist/rendering/render/createWebGPUSurfaceBackend.d.ts.map +1 -0
  226. package/dist/rendering/render/createWebGPUSurfaceBackend.js +92 -0
  227. package/dist/rendering/render/createWebGPUSurfaceBackend.js.map +1 -0
  228. package/dist/rendering/render/frameMetrics.d.ts +19 -0
  229. package/dist/rendering/render/frameMetrics.d.ts.map +1 -0
  230. package/dist/rendering/render/frameMetrics.js +43 -0
  231. package/dist/rendering/render/frameMetrics.js.map +1 -0
  232. package/dist/rendering/render/index.d.ts +10 -0
  233. package/dist/rendering/render/index.d.ts.map +1 -1
  234. package/dist/rendering/render/index.js +6 -0
  235. package/dist/rendering/render/index.js.map +1 -1
  236. package/dist/rendering/render/rendererHost.d.ts +34 -0
  237. package/dist/rendering/render/rendererHost.d.ts.map +1 -0
  238. package/dist/rendering/render/rendererHost.js +144 -0
  239. package/dist/rendering/render/rendererHost.js.map +1 -0
  240. package/dist/rendering/render/webgpuResourceTable.d.ts +25 -0
  241. package/dist/rendering/render/webgpuResourceTable.d.ts.map +1 -0
  242. package/dist/rendering/render/webgpuResourceTable.js +54 -0
  243. package/dist/rendering/render/webgpuResourceTable.js.map +1 -0
  244. package/dist/rendering/scene/createLayerFromPlugin.d.ts.map +1 -1
  245. package/dist/rendering/scene/createLayerFromPlugin.js +10 -2
  246. package/dist/rendering/scene/createLayerFromPlugin.js.map +1 -1
  247. package/dist/rendering/scene/layerRegistry.d.ts +3 -5
  248. package/dist/rendering/scene/layerRegistry.d.ts.map +1 -1
  249. package/dist/rendering/scene/layerRegistry.js +1 -1
  250. package/dist/rendering/scene/layerRegistry.js.map +1 -1
  251. package/dist/rendering/scene/retainedScene.d.ts +60 -0
  252. package/dist/rendering/scene/retainedScene.d.ts.map +1 -0
  253. package/dist/rendering/scene/retainedScene.js +42 -0
  254. package/dist/rendering/scene/retainedScene.js.map +1 -0
  255. package/dist/version.d.ts +1 -1
  256. package/dist/version.js +1 -1
  257. package/package.json +2 -1
  258. package/src/__tests__/signal.test.ts +47 -1
  259. package/src/controllers/__tests__/createChartController.theme.test.ts +102 -0
  260. package/src/controllers/createChartController.ts +36 -12
  261. package/src/controllers/index.ts +5 -0
  262. package/src/controllers/types.ts +14 -0
  263. package/src/engine/__tests__/chart.dpr.test.ts +64 -0
  264. package/src/engine/__tests__/paneRenderer.resize.test.ts +0 -7
  265. package/src/engine/__tests__/renderSinglePath.test.ts +121 -0
  266. package/src/engine/chart.ts +172 -25
  267. package/src/engine/controller/__tests__/interaction.dpr.test.ts +45 -41
  268. package/src/engine/controller/interaction.ts +81 -30
  269. package/src/engine/data/__tests__/chartDataManager.incrementalLoad.test.ts +1 -0
  270. package/src/engine/data/incrementalLoadHint.ts +6 -1
  271. package/src/engine/drawing/DrawingState.ts +107 -89
  272. package/src/engine/drawing/__tests__/drawingState.workCopy.test.ts +95 -36
  273. package/src/engine/drawing/__tests__/drawingStore.projection.test.ts +22 -0
  274. package/src/engine/drawing/index.ts +12 -4
  275. package/src/engine/drawing/interaction.ts +25 -40
  276. package/src/engine/indicators/chartIndicatorManager.ts +6 -16
  277. package/src/engine/layout/chartPaneLayout.ts +6 -11
  278. package/src/engine/layout/pane.ts +0 -44
  279. package/src/engine/paneRenderer.ts +3 -34
  280. package/src/engine/render/chartRenderer.ts +181 -81
  281. package/src/engine/renderers/Indicator/atr.ts +5 -17
  282. package/src/engine/renderers/Indicator/boll.ts +15 -26
  283. package/src/engine/renderers/Indicator/cci.ts +7 -17
  284. package/src/engine/renderers/Indicator/chaikinVol.ts +3 -15
  285. package/src/engine/renderers/Indicator/cmf.ts +3 -15
  286. package/src/engine/renderers/Indicator/dema.ts +3 -15
  287. package/src/engine/renderers/Indicator/donchian.ts +3 -12
  288. package/src/engine/renderers/Indicator/ene.ts +15 -25
  289. package/src/engine/renderers/Indicator/expma.ts +4 -13
  290. package/src/engine/renderers/Indicator/fastk.ts +7 -17
  291. package/src/engine/renderers/Indicator/fib.ts +3 -12
  292. package/src/engine/renderers/Indicator/hma.ts +3 -15
  293. package/src/engine/renderers/Indicator/hv.ts +3 -15
  294. package/src/engine/renderers/Indicator/ichimoku.ts +21 -24
  295. package/src/engine/renderers/Indicator/kama.ts +3 -15
  296. package/src/engine/renderers/Indicator/keltner.ts +3 -12
  297. package/src/engine/renderers/Indicator/kst.ts +9 -20
  298. package/src/engine/renderers/Indicator/ma.ts +9 -21
  299. package/src/engine/renderers/Indicator/macd.ts +27 -102
  300. package/src/engine/renderers/Indicator/mfi.ts +3 -15
  301. package/src/engine/renderers/Indicator/mom.ts +7 -17
  302. package/src/engine/renderers/Indicator/obv.ts +3 -15
  303. package/src/engine/renderers/Indicator/parkinson.ts +3 -15
  304. package/src/engine/renderers/Indicator/pvt.ts +3 -15
  305. package/src/engine/renderers/Indicator/roc.ts +3 -15
  306. package/src/engine/renderers/Indicator/rsi.ts +12 -23
  307. package/src/engine/renderers/Indicator/shared/webglBand.ts +0 -13
  308. package/src/engine/renderers/Indicator/stoch.ts +9 -20
  309. package/src/engine/renderers/Indicator/tema.ts +3 -15
  310. package/src/engine/renderers/Indicator/trix.ts +3 -12
  311. package/src/engine/renderers/Indicator/vma.ts +3 -15
  312. package/src/engine/renderers/Indicator/vwap.ts +3 -15
  313. package/src/engine/renderers/Indicator/wma.ts +3 -15
  314. package/src/engine/renderers/Indicator/wmsr.ts +7 -17
  315. package/src/engine/renderers/__tests__/candle.sceneRenderer.test.ts +208 -0
  316. package/src/engine/renderers/__tests__/candleViaRenderer.test.ts +100 -0
  317. package/src/engine/renderers/__tests__/linesViaRenderer.test.ts +263 -0
  318. package/src/engine/renderers/__tests__/rectsViaRenderer.test.ts +87 -0
  319. package/src/engine/renderers/candle.ts +26 -69
  320. package/src/engine/renderers/candleViaRenderer.ts +38 -0
  321. package/src/engine/renderers/linesViaRenderer.ts +226 -0
  322. package/src/engine/renderers/rectsViaRenderer.ts +83 -0
  323. package/src/engine/renderers/subVolume.ts +9 -61
  324. package/src/engine/renderers/timeAxis.ts +1 -1
  325. package/src/engine/state/__tests__/effectiveTheme.test.ts +49 -0
  326. package/src/engine/state/__tests__/rendererState.test.ts +36 -0
  327. package/src/engine/state/__tests__/settingsState.test.ts +1 -1
  328. package/src/engine/state/chartStateKernel.ts +38 -17
  329. package/src/engine/state/index.ts +6 -1
  330. package/src/engine/state/interactionState.ts +73 -102
  331. package/src/engine/state/rendererState.ts +32 -0
  332. package/src/engine/state/themeState.ts +15 -8
  333. package/src/engine/state/viewportState.ts +6 -3
  334. package/src/engine/subPaneManager.ts +2 -16
  335. package/src/foundation/config/__tests__/chartSettings.test.ts +25 -0
  336. package/src/foundation/config/chartSettings.ts +24 -7
  337. package/src/foundation/plugin/index.ts +0 -1
  338. package/src/foundation/plugin/rendererPluginManager.ts +0 -99
  339. package/src/foundation/plugin/types.ts +11 -18
  340. package/src/foundation/reactivity/__tests__/frameTransaction.test.ts +306 -0
  341. package/src/foundation/reactivity/frameTransaction.ts +239 -0
  342. package/src/foundation/reactivity/index.ts +8 -0
  343. package/src/foundation/reactivity/signal.ts +39 -0
  344. package/src/foundation/tokens/__tests__/__snapshots__/baseline.test.ts.snap +2 -2
  345. package/src/foundation/tokens/theme-dark.ts +2 -1
  346. package/src/foundation/tokens/theme-light.ts +2 -1
  347. package/src/rendering/render/Renderer.ts +27 -14
  348. package/src/rendering/render/SurfaceBackend.ts +5 -7
  349. package/src/rendering/render/__tests__/canvas2DRenderer.test.ts +34 -0
  350. package/src/rendering/render/__tests__/contract.test.ts +2 -2
  351. package/src/rendering/render/__tests__/frameMetrics.test.ts +24 -0
  352. package/src/rendering/render/__tests__/rendererHost.test.ts +157 -0
  353. package/src/rendering/render/__tests__/webglRenderer.fallback.test.ts +14 -74
  354. package/src/rendering/render/__tests__/webglRenderer.test.ts +64 -6
  355. package/src/rendering/render/__tests__/webgpuRenderer.test.ts +370 -0
  356. package/src/rendering/render/__tests__/webgpuResourceTable.test.ts +62 -0
  357. package/src/rendering/render/__tests__/webgpuSurfaceBackend.test.ts +120 -0
  358. package/src/rendering/render/createCanvas2DRenderer.ts +83 -0
  359. package/src/rendering/render/createDefaultRendererHost.ts +74 -0
  360. package/src/rendering/render/createWebGLRenderer.ts +41 -87
  361. package/src/rendering/render/createWebGPURenderer.ts +677 -0
  362. package/src/rendering/render/createWebGPUSurfaceBackend.ts +106 -0
  363. package/src/rendering/render/frameMetrics.ts +54 -0
  364. package/src/rendering/render/index.ts +30 -0
  365. package/src/rendering/render/rendererHost.ts +210 -0
  366. package/src/rendering/render/webgpuResourceTable.ts +87 -0
  367. package/src/rendering/scene/__tests__/layerFromPlugin.test.ts +29 -2
  368. package/src/rendering/scene/__tests__/retainedScene.test.ts +59 -0
  369. package/src/rendering/scene/createLayerFromPlugin.ts +9 -2
  370. package/src/rendering/scene/layerRegistry.ts +4 -6
  371. package/src/rendering/scene/retainedScene.ts +101 -0
  372. package/src/version.ts +1 -1
@@ -12,6 +12,7 @@ import {
12
12
  type RendererPlugin,
13
13
  type RendererPluginWithHost,
14
14
  } from '../foundation/plugin/index'
15
+ import { createLayerFromPlugin } from '../rendering/scene/createLayerFromPlugin'
15
16
  import {
16
17
  computed,
17
18
  type Computed,
@@ -22,6 +23,11 @@ import {
22
23
  import { InteractionController, type InteractionSnapshot } from './controller/interaction'
23
24
 
24
25
  import type { ChartSettings } from '../foundation/config/chartSettings'
26
+ import {
27
+ createDefaultRendererHostSync,
28
+ type RendererBackend,
29
+ type RendererHost,
30
+ } from '../rendering/render/index'
25
31
  import type { KLineData } from '../foundation/types/price'
26
32
 
27
33
  import type { IndicatorScheduler } from './indicators/scheduler'
@@ -41,7 +47,6 @@ import { KLineMode } from './modes/kLineMode'
41
47
  import { TimeShareMode } from './modes/timeShareMode'
42
48
  import { PaneRenderer } from './paneRenderer'
43
49
  import { ChartRenderer } from './render/chartRenderer'
44
- import { SharedWebGLSurface } from './renderers/webgl/sharedWebGLSurface'
45
50
  import { ChartStateKernel } from './state/chartStateKernel'
46
51
  import { ChartViewportManager } from './viewport/chartViewportManager'
47
52
  import { getVisibleRange } from './viewport/viewport'
@@ -101,8 +106,8 @@ export class Chart {
101
106
  /** 渲染器插件管理器 */
102
107
  private rendererPluginManager: RendererPluginManager
103
108
 
104
- /** Chart 级共享 WebGL canvas/context */
105
- private sharedWebGLSurface: SharedWebGLSurface
109
+ /** 具体渲染后端及其生命周期所有者 */
110
+ private rendererHost: RendererHost
106
111
 
107
112
  /** 缩放控制器 */
108
113
  private zoomController: ChartZoomController
@@ -196,13 +201,17 @@ export class Chart {
196
201
  * @param dom 由 Vue 组件传入的 DOM 句柄
197
202
  * @param opt 初始配置
198
203
  */
199
- constructor(dom: ChartDom, opt: ChartOptions) {
204
+ constructor(
205
+ dom: ChartDom,
206
+ opt: ChartOptions,
207
+ runtime?: { rendererHost?: RendererHost; initialSettings?: Partial<ChartSettings> },
208
+ ) {
200
209
  this.dom = dom
201
210
  const { kWidth: _kWidth, kGap: _kGap, ...restOpt } = opt
202
211
  this._activeMode = this._kLineMode
203
212
  this.pluginHost = createPluginHost()
204
213
  this.rendererPluginManager = new RendererPluginManager()
205
- this.sharedWebGLSurface = new SharedWebGLSurface()
214
+ this.rendererHost = runtime?.rendererHost ?? createDefaultRendererHostSync()
206
215
 
207
216
  // 注入依赖
208
217
  this.rendererPluginManager.setPluginHost(this.pluginHost)
@@ -218,14 +227,24 @@ export class Chart {
218
227
  zoomLevelCount,
219
228
  },
220
229
  initialZoomLevel,
230
+ initialSettings: runtime?.initialSettings,
231
+ initialRendererRuntime: this.rendererHost.runtime,
221
232
  scheduleDraw: (level) => this.scheduleDraw(level as UpdateLevel | undefined),
222
233
  })
234
+ this.rendererHost.setListeners({
235
+ onRuntimeChange: (rendererRuntime) => {
236
+ this.kernel.renderer.actions.setRuntime(rendererRuntime)
237
+ this.syncGpuSceneCanvas()
238
+ },
239
+ requestRedraw: () => this.scheduleDraw(UpdateLevel.All),
240
+ })
241
+ this.syncGpuSceneCanvas()
223
242
 
224
243
  // Inject DOM deps into kernel's viewportState (needed before init)
225
244
  this.kernel.setViewportDomDeps({
226
245
  getDom: () => this.dom,
227
246
  resizeSharedWebGLSurface: (plotWidth, plotHeight, dpr) =>
228
- this.sharedWebGLSurface.resize(plotWidth, plotHeight, dpr),
247
+ this.rendererHost.resize(plotWidth, plotHeight, dpr),
229
248
  })
230
249
 
231
250
  // ── ViewportManager (DOM lifecycle: ResizeObserver + scroll events) ──
@@ -257,7 +276,6 @@ export class Chart {
257
276
  }
258
277
  },
259
278
  getViewport: () => this.viewportManager.getViewport(),
260
- getSharedWebGLSurface: () => this.sharedWebGLSurface,
261
279
  setKnownPaneIds: (ids) => this.rendererPluginManager.setKnownPaneIds(ids),
262
280
  notifyPaneResize: (paneId, pane) =>
263
281
  this.rendererPluginManager.notifyResize(paneId, wrapPaneInfo(pane)),
@@ -435,10 +453,10 @@ export class Chart {
435
453
  },
436
454
  getPaneRenderers: () => this.paneRenderers,
437
455
  getInteraction: () => this.interaction,
438
- getSharedWebGLSurface: () => this.sharedWebGLSurface,
456
+ getSceneRenderer: () => this.rendererHost.renderer,
439
457
  getPluginHost: () => this.pluginHost,
440
458
  getRendererPluginManager: () => this.rendererPluginManager,
441
- getTheme: () => this.kernel.theme.readonly.theme.peek(),
459
+ getTheme: () => this.kernel.effectiveTheme$.peek(),
442
460
  getCurrentZoomLevel: () => this.kernel.zoom.readonly.zoomLevel.peek(),
443
461
  getZoomLevelCount: () => this.kernel.options.readonly.options.peek().zoomLevelCount,
444
462
  getViewportManager: () => this.viewportManager,
@@ -449,6 +467,7 @@ export class Chart {
449
467
  customMarkers$: this.kernel.marker.readonly.customMarkers,
450
468
  drawings$: this.kernel.drawing.readonly.drawings,
451
469
  selectedDrawingId$: this.kernel.drawing.readonly.selectedDrawingId,
470
+ getOverlay: () => this.drawingSession?.getPaintOverlay() ?? [],
452
471
  })
453
472
  this.renderer.registerDrawingPlugins()
454
473
  this.renderer.initCoreRenderers()
@@ -585,22 +604,80 @@ export class Chart {
585
604
  return this.pluginHost
586
605
  }
587
606
 
588
- // ========== 渲染器插件 API ==========
607
+ // ========== 渲染器插件 API(绘制只走 Scene;Manager 仅作注册表) ==========
608
+
609
+ private resolvePluginLayerTarget(plugin: RendererPlugin): string {
610
+ if (typeof plugin.paneId === 'symbol') {
611
+ return 'global'
612
+ }
613
+ return String(plugin.paneId)
614
+ }
615
+
616
+ private getContextForPluginLayer(targetPaneId: string) {
617
+ return () => {
618
+ if (!this.renderer) return null
619
+ const map = this.renderer.getPaneCtxMap()
620
+ if (targetPaneId === 'global') {
621
+ return map.get(this.renderer.getCurrentPaneId()) ?? null
622
+ }
623
+ return map.get(targetPaneId) ?? null
624
+ }
625
+ }
589
626
 
590
- /** 安装渲染器插件 */
627
+ /** 安装渲染器插件:注册元数据 + 挂 Scene Layer(唯一绘制路径;幂等) */
591
628
  useRenderer(
592
629
  plugin: RendererPlugin | RendererPluginWithHost,
593
630
  config?: Record<string, unknown>,
594
631
  ): void {
632
+ const layerId = `plugin:${plugin.name}`
633
+ const scene = this.renderer?.getScene()
634
+ const existingPlugin = this.rendererPluginManager.getPlugin(plugin.name)
635
+ const alreadyLayer = scene?.getLayer(layerId) != null
636
+
637
+ if (existingPlugin && alreadyLayer) {
638
+ if (config && existingPlugin.setConfig) existingPlugin.setConfig(config)
639
+ return
640
+ }
641
+
642
+ // 仅有注册表:补挂 Layer(不新建 plugin 实例)
643
+ if (existingPlugin && !alreadyLayer) {
644
+ if (config && existingPlugin.setConfig) existingPlugin.setConfig(config)
645
+ const targetPaneId = this.resolvePluginLayerTarget(existingPlugin)
646
+ scene?.addLayer(
647
+ createLayerFromPlugin(
648
+ existingPlugin,
649
+ this.getContextForPluginLayer(targetPaneId),
650
+ targetPaneId,
651
+ ),
652
+ )
653
+ return
654
+ }
655
+
656
+ // 仅有 Layer(core 预挂):不二次 register,避免 Manager 实例 ≠ 绘制实例
657
+ if (!existingPlugin && alreadyLayer) {
658
+ return
659
+ }
660
+
595
661
  this.rendererPluginManager.register(plugin)
596
662
  if (config && plugin.setConfig) {
597
663
  plugin.setConfig(config)
598
664
  }
665
+ const targetPaneId = this.resolvePluginLayerTarget(plugin)
666
+ const layer = createLayerFromPlugin(
667
+ plugin,
668
+ this.getContextForPluginLayer(targetPaneId),
669
+ targetPaneId,
670
+ )
671
+ scene?.addLayer(layer)
599
672
  }
600
673
 
601
- /** 移除渲染器插件 */
674
+ /**
675
+ * 移除渲染器插件。
676
+ * onUninstall 仅由 Manager.unregister 调用;Scene removeLayer 不 dispose,避免双调。
677
+ */
602
678
  removeRenderer(name: string): void {
603
679
  this.rendererPluginManager.unregister(name)
680
+ this.renderer?.getScene()?.removeLayer(`plugin:${name}`)
604
681
  }
605
682
 
606
683
  /** 获取渲染器插件 */
@@ -613,9 +690,18 @@ export class Chart {
613
690
  this.rendererPluginManager.updateConfig(name, config)
614
691
  }
615
692
 
616
- /** 启用/禁用渲染器 */
693
+ /** 启用/禁用渲染器(Scene Layer 显隐;Manager enabled 仅同步元数据) */
617
694
  setRendererEnabled(name: string, enabled: boolean): void {
618
- this.rendererPluginManager.setEnabled(name, enabled)
695
+ const inManager = this.rendererPluginManager.getPlugin(name) != null
696
+ if (inManager) {
697
+ // setEnabled → invalidate → scheduleDraw(勿再 schedule 双唤醒)
698
+ this.rendererPluginManager.setEnabled(name, enabled)
699
+ }
700
+ this.renderer?.getScene()?.setLayerVisibility(`plugin:${name}`, enabled)
701
+ // core-only Layer(如 candle)不在 Manager:需显式重绘
702
+ if (!inManager) {
703
+ this.scheduleDraw()
704
+ }
619
705
  }
620
706
 
621
707
  /** 获取所有渲染器 */
@@ -687,6 +773,14 @@ export class Chart {
687
773
  this.applyRightAxisTypeToKernel(settings.rightAxisType as string)
688
774
  }
689
775
 
776
+ if (prev.rendererBackend !== next.rendererBackend) {
777
+ void this.rendererHost.switchTo(next.rendererBackend as RendererBackend).then(() => {
778
+ this.syncGpuSceneCanvas()
779
+ this.scheduleDraw(UpdateLevel.All)
780
+ })
781
+ return
782
+ }
783
+
690
784
  this.scheduleDraw()
691
785
  }
692
786
 
@@ -826,9 +920,10 @@ export class Chart {
826
920
  this.indicatorManager.bindIndicatorToPane(paneId, indicatorId, params)
827
921
  }
828
922
 
829
- /** 更新绘图对象(写 kernel + 重绘) */
923
+ /** 更新绘图对象(写 kernel + 重绘);剥离会话预览 id */
830
924
  setDrawings(drawings: import('../foundation/plugin').DrawingObject[]): void {
831
- this.kernel.drawing.actions.setDrawings(drawings)
925
+ const committed = drawings.filter((d) => d.id !== '__preview__')
926
+ this.kernel.drawing.actions.setDrawings(committed)
832
927
  this.scheduleDraw()
833
928
  }
834
929
 
@@ -1166,14 +1261,50 @@ export class Chart {
1166
1261
  }
1167
1262
  }
1168
1263
 
1264
+ /**
1265
+ * M2:将 WebGPU canvas 挂到 plot 区(main 与 overlay 之间),非 webgpu 时移除。
1266
+ * 多 pane 共用一张 canvas,region.y + scissor 区分。
1267
+ */
1268
+ private syncGpuSceneCanvas(): void {
1269
+ const layer = this.dom.canvasLayer
1270
+ if (!layer) return
1271
+ const effective = this.rendererHost.runtime.effective
1272
+ const existing = layer.querySelector('canvas.gpu-scene-canvas') as HTMLCanvasElement | null
1273
+
1274
+ if (effective !== 'webgpu') {
1275
+ existing?.remove()
1276
+ return
1277
+ }
1278
+
1279
+ const surface = this.rendererHost.renderer.surface as { canvas?: HTMLCanvasElement }
1280
+ const canvas = surface.canvas
1281
+ if (!canvas) return
1282
+
1283
+ canvas.classList.add('gpu-scene-canvas')
1284
+ canvas.style.position = 'absolute'
1285
+ canvas.style.left = '0'
1286
+ canvas.style.top = '0'
1287
+ canvas.style.pointerEvents = 'none'
1288
+ canvas.style.zIndex = '1'
1289
+ canvas.style.backgroundColor = 'transparent'
1290
+
1291
+ if (existing !== canvas) {
1292
+ existing?.remove()
1293
+ if (!canvas.isConnected) layer.appendChild(canvas)
1294
+ }
1295
+ }
1296
+
1169
1297
  /** 销毁图表实例 */
1170
1298
  async destroy() {
1171
1299
  this.indicatorManager.destroy()
1300
+ // onUninstall 由 Manager 单点负责;须在 scene.dispose 之前 clear
1301
+ this.rendererPluginManager.clear()
1172
1302
  this.renderer.destroy()
1173
1303
  this.dataManager.destroy()
1174
1304
  this.viewportManager.destroy()
1175
1305
  this.layoutManager.destroy()
1176
- this.sharedWebGLSurface.destroy()
1306
+ this.dom.canvasLayer?.querySelector('canvas.gpu-scene-canvas')?.remove()
1307
+ this.rendererHost.dispose()
1177
1308
  this.kernel.dispose()
1178
1309
  this.alertController.dispose()
1179
1310
  await this.pluginHost.destroy()
@@ -1236,9 +1367,9 @@ export class Chart {
1236
1367
  return this.dataManager.comparisonLoading
1237
1368
  }
1238
1369
 
1239
- /** 主题信号 */
1370
+ /** 生效主题信号(settings.theme + systemTheme 推导) */
1240
1371
  get theme(): ReadonlySignal<'light' | 'dark'> {
1241
- return this.kernel.theme.readonly.theme
1372
+ return this.kernel.effectiveTheme$
1242
1373
  }
1243
1374
 
1244
1375
  /** 指标实例列表信号(派生信号,自动随主/副图状态更新) */
@@ -1365,13 +1496,23 @@ export class Chart {
1365
1496
  // ---------- Theme ----------
1366
1497
 
1367
1498
  /**
1368
- * 设置主题(高层 API
1499
+ * 设置主题偏好 light|dark(写 settings.theme
1369
1500
  */
1370
1501
  setTheme(theme: 'light' | 'dark'): void {
1371
- this.kernel.theme.actions.setTheme(theme)
1502
+ this.kernel.settings.actions.patch({ theme })
1372
1503
  this.scheduleDraw()
1373
1504
  }
1374
1505
 
1506
+ /**
1507
+ * 注入系统主题(仅 settings.theme === auto 时影响 effectiveTheme)
1508
+ */
1509
+ setSystemTheme(theme: 'light' | 'dark'): void {
1510
+ this.kernel.systemTheme.actions.setSystemTheme(theme)
1511
+ if (this.kernel.settings.readonly.settings.peek().theme === 'auto') {
1512
+ this.scheduleDraw()
1513
+ }
1514
+ }
1515
+
1375
1516
  // ---------- Zoom ----------
1376
1517
 
1377
1518
  /**
@@ -1557,12 +1698,18 @@ export class Chart {
1557
1698
  }
1558
1699
 
1559
1700
  /**
1560
- * 移除绘图(高层 API
1701
+ * 移除绘图(高层 API)—— 优先会话工作副本,否则只写 kernel
1561
1702
  * @param drawingId 绘图 ID
1562
1703
  */
1563
- removeDrawing(_drawingId: string): void {
1564
- // TODO: 实现绘图移除
1565
- console.warn('[Chart] removeDrawing not fully implemented yet')
1704
+ removeDrawing(drawingId: string): void {
1705
+ if (this.drawingSession) {
1706
+ this.drawingSession.removeDrawing(drawingId)
1707
+ return
1708
+ }
1709
+ const next = this.kernel.drawing.readonly.drawings
1710
+ .peek()
1711
+ .filter((d) => d.id !== drawingId)
1712
+ this.setDrawings([...next])
1566
1713
  }
1567
1714
 
1568
1715
  /**
@@ -9,6 +9,7 @@ function createMockInteractionState() {
9
9
  const signals = {
10
10
  crosshairPos: writableRef<{ x: number; y: number } | null>(null),
11
11
  crosshairPrice: writableRef<number | null>(null),
12
+ crosshairIndex: writableRef<number | null>(null),
12
13
  hoveredIndex: writableRef<number | null>(null),
13
14
  activePaneId: writableRef<string | null>(null),
14
15
  isDragging: writableRef(false),
@@ -20,38 +21,13 @@ function createMockInteractionState() {
20
21
  hoveredMarkerData: writableRef<any>(null),
21
22
  hoveredCustomMarker: writableRef<any>(null),
22
23
  hoveredMarkerId: writableRef<string | null>(null),
23
- kLinePositions: writableRef<number[] | null>(null),
24
- kLineCenters: writableRef<number[] | null>(null),
25
- kWidthPx: writableRef<number | null>(null),
26
24
  }
27
25
 
28
- function computeCrosshairIndex(): number | null {
29
- const pos = signals.crosshairPos.peek()
30
- const positions = signals.kLinePositions.peek()
31
- const kwp = signals.kWidthPx.peek()
32
- if (!pos || !positions || !kwp) return null
33
- const worldX = pos.x
34
- let minDist = Infinity
35
- let minIdx = -1
36
- for (let i = 0; i < positions.length; i++) {
37
- const dist = Math.abs(positions[i]! - worldX)
38
- if (dist < minDist) {
39
- minDist = dist
40
- minIdx = i
41
- }
42
- }
43
- return minIdx >= 0 ? minIdx : null
44
- }
45
-
46
- const crosshairIndexSignal = {
47
- peek: () => computeCrosshairIndex(),
48
- subscribe: () => () => {},
49
- } as any
50
-
51
26
  return {
52
27
  readonly: {
53
28
  crosshairPos: signals.crosshairPos,
54
29
  crosshairPrice: signals.crosshairPrice,
30
+ crosshairIndex: signals.crosshairIndex,
55
31
  hoveredIndex: signals.hoveredIndex,
56
32
  activePaneId: signals.activePaneId,
57
33
  isDragging: signals.isDragging,
@@ -63,14 +39,10 @@ function createMockInteractionState() {
63
39
  hoveredMarkerData: signals.hoveredMarkerData,
64
40
  hoveredCustomMarker: signals.hoveredCustomMarker,
65
41
  hoveredMarkerId: signals.hoveredMarkerId,
66
- kLinePositions: signals.kLinePositions,
67
- kLineCenters: signals.kLineCenters,
68
- kWidthPx: signals.kWidthPx,
69
- crosshairIndex: crosshairIndexSignal,
70
42
  interactionSnapshot: {
71
43
  peek: () => ({
72
44
  crosshairPos: signals.crosshairPos.peek(),
73
- crosshairIndex: crosshairIndexSignal.peek(),
45
+ crosshairIndex: signals.crosshairIndex.peek(),
74
46
  crosshairPrice: signals.crosshairPrice.peek(),
75
47
  hoveredIndex: signals.hoveredIndex.peek(),
76
48
  activePaneId: signals.activePaneId.peek(),
@@ -87,9 +59,13 @@ function createMockInteractionState() {
87
59
  } as any,
88
60
  },
89
61
  actions: {
90
- updateCrosshair(pos: any, price: any) {
62
+ updateCrosshair(pos: any, price: any, index?: any) {
91
63
  signals.crosshairPos.set(pos)
92
64
  signals.crosshairPrice.set(price)
65
+ if (index !== undefined) signals.crosshairIndex.set(index)
66
+ },
67
+ setCrosshairIndex(index: any) {
68
+ signals.crosshairIndex.set(index)
93
69
  },
94
70
  setHoveredIndex(index: any) {
95
71
  signals.hoveredIndex.set(index)
@@ -101,11 +77,6 @@ function createMockInteractionState() {
101
77
  signals.hoveredIndex.set(index)
102
78
  signals.activePaneId.set(paneId)
103
79
  },
104
- updateFramePositions(positions: any, centers: any, kWidthPxVal: any) {
105
- signals.kLinePositions.set(positions)
106
- signals.kLineCenters.set(centers)
107
- signals.kWidthPx.set(kWidthPxVal)
108
- },
109
80
  startDrag(mode: any) {
110
81
  signals.isDragging.set(true)
111
82
  signals.dragMode.set(mode)
@@ -135,6 +106,7 @@ function createMockInteractionState() {
135
106
  reset() {
136
107
  signals.crosshairPos.set(null)
137
108
  signals.crosshairPrice.set(null)
109
+ signals.crosshairIndex.set(null)
138
110
  signals.hoveredIndex.set(null)
139
111
  signals.activePaneId.set(null)
140
112
  signals.isDragging.set(false)
@@ -146,10 +118,6 @@ function createMockInteractionState() {
146
118
  signals.hoveredMarkerData.set(null)
147
119
  signals.hoveredCustomMarker.set(null)
148
120
  signals.hoveredMarkerId.set(null)
149
- signals.kLinePositions.set(null)
150
- signals.kLineCenters.set(null)
151
- signals.kWidthPx.set(null)
152
- crosshairIndexSignal.set(null)
153
121
  },
154
122
  },
155
123
  dispose() {},
@@ -283,9 +251,11 @@ describe('InteractionController DPR consumption', () => {
283
251
  interaction.setKLinePositions([0, 10], { start: 0, end: 2 }, 10)
284
252
 
285
253
  interaction.onPointerMove({ clientX: 50, clientY: 40, isPrimary: true } as PointerEvent)
254
+ interaction.flushPendingHover()
286
255
  expect(interaction.crosshairPos).not.toBeNull()
287
256
 
288
257
  interaction.onPointerMove({ clientX: 120, clientY: 40, isPrimary: true } as PointerEvent)
258
+ interaction.flushPendingHover()
289
259
  expect(interaction.crosshairPos).toBeNull()
290
260
  expect(interaction.crosshairIndex).toBeNull()
291
261
  })
@@ -296,6 +266,7 @@ describe('InteractionController DPR consumption', () => {
296
266
  interactionDpr1.setKLinePositions([0, 10], { start: 0, end: 2 }, 10)
297
267
 
298
268
  interactionDpr1.onPointerMove({ clientX: 8, clientY: 40, isPrimary: true } as PointerEvent)
269
+ interactionDpr1.flushPendingHover()
299
270
  expect(interactionDpr1.crosshairIndex).toBe(0)
300
271
 
301
272
  const chartDpr2 = createChartStub({ dpr: 2, plotWidth: 300, plotHeight: 160 })
@@ -303,8 +274,34 @@ describe('InteractionController DPR consumption', () => {
303
274
  interactionDpr2.setKLinePositions([0, 10], { start: 0, end: 2 }, 10)
304
275
 
305
276
  interactionDpr2.onPointerMove({ clientX: 8, clientY: 40, isPrimary: true } as PointerEvent)
277
+ interactionDpr2.flushPendingHover()
306
278
  expect(interactionDpr2.crosshairIndex).toBe(1)
307
279
  })
280
+
281
+ it('pointermove does not write crosshair until flushPendingHover', () => {
282
+ const chart = createChartStub({ dpr: 1, plotWidth: 100, plotHeight: 80 })
283
+ const interaction = new InteractionController(chart as never, createMockInteractionState())
284
+ interaction.setKLinePositions([0, 10], { start: 0, end: 2 }, 10)
285
+
286
+ interaction.onPointerMove({ clientX: 50, clientY: 40, isPrimary: true } as PointerEvent)
287
+ expect(interaction.crosshairPos).toBeNull()
288
+
289
+ interaction.flushPendingHover()
290
+ expect(interaction.crosshairPos).not.toBeNull()
291
+ })
292
+
293
+ it('pointerleave cancels pending hover so flush does not restore crosshair', () => {
294
+ const chart = createChartStub({ dpr: 1, plotWidth: 100, plotHeight: 80 })
295
+ const interaction = new InteractionController(chart as never, createMockInteractionState())
296
+ interaction.setKLinePositions([0, 10], { start: 0, end: 2 }, 10)
297
+
298
+ interaction.onPointerMove({ clientX: 50, clientY: 40, isPrimary: true } as PointerEvent)
299
+ interaction.onPointerLeave({ isPrimary: true } as PointerEvent)
300
+ interaction.flushPendingHover()
301
+
302
+ expect(interaction.crosshairPos).toBeNull()
303
+ expect(interaction.crosshairIndex).toBeNull()
304
+ })
308
305
  })
309
306
 
310
307
  describe('InteractionController pane capability gating', () => {
@@ -322,6 +319,7 @@ describe('InteractionController pane capability gating', () => {
322
319
 
323
320
  interaction.setKLinePositions([0, 10], { start: 0, end: 2 }, 10)
324
321
  interaction.onPointerMove({ clientX: 5, clientY: 140, isPrimary: true } as PointerEvent)
322
+ interaction.flushPendingHover()
325
323
 
326
324
  expect(interaction.activePaneId).toBe('sub_MACD')
327
325
  expect(interaction.hoveredIndex).toBeNull()
@@ -341,6 +339,7 @@ describe('InteractionController pane capability gating', () => {
341
339
 
342
340
  interaction.setKLinePositions([0, 10], { start: 0, end: 2 }, 10)
343
341
  interaction.onPointerMove({ clientX: 5, clientY: 10, isPrimary: true } as PointerEvent)
342
+ interaction.flushPendingHover()
344
343
 
345
344
  expect(interaction.activePaneId).toBe('main')
346
345
  expect(interaction.crosshairIndex).not.toBeNull()
@@ -361,9 +360,11 @@ describe('InteractionController pane capability gating', () => {
361
360
 
362
361
  interaction.setKLinePositions([0, 10], { start: 0, end: 2 }, 10)
363
362
  interaction.onPointerMove({ clientX: 5, clientY: 10, isPrimary: true } as PointerEvent)
363
+ interaction.flushPendingHover()
364
364
  expect(interaction.hoveredIndex).toBe(interaction.crosshairIndex)
365
365
 
366
366
  interaction.onPointerMove({ clientX: 5, clientY: 140, isPrimary: true } as PointerEvent)
367
+ interaction.flushPendingHover()
367
368
  expect(interaction.activePaneId).toBe('sub_MACD')
368
369
  expect(interaction.hoveredIndex).toBeNull()
369
370
  })
@@ -386,6 +387,7 @@ describe('InteractionController hover snapshot', () => {
386
387
  const interaction = new InteractionController(chart as never, createMockInteractionState())
387
388
 
388
389
  interaction.onPointerMove({ clientX: 20, clientY: 20, isPrimary: true } as PointerEvent)
390
+ interaction.flushPendingHover()
389
391
  expect(interaction.getInteractionSnapshot().hoveredMarkerData).toBe(marker)
390
392
 
391
393
  interaction.onScroll()
@@ -414,11 +416,13 @@ describe('InteractionController hover snapshot', () => {
414
416
  const interaction = new InteractionController(chart as never, createMockInteractionState())
415
417
 
416
418
  interaction.onPointerMove({ clientX: 20, clientY: 20, isPrimary: true } as PointerEvent)
419
+ interaction.flushPendingHover()
417
420
  expect(interaction.getInteractionSnapshot().hoveredCustomMarker).toBe(customMarker)
418
421
 
419
422
  hoveringCustom = false
420
423
  interaction.setKLinePositions([0, 10], { start: 0, end: 2 }, 10)
421
424
  interaction.onPointerMove({ clientX: 20, clientY: 20, isPrimary: true } as PointerEvent)
425
+ interaction.flushPendingHover()
422
426
 
423
427
  expect(interaction.getInteractionSnapshot().hoveredCustomMarker).toBeNull()
424
428
  })