@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retainedScene.d.ts","sourceRoot":"","sources":["../../../src/rendering/scene/retainedScene.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,MAAM,CAAA;AAEjC,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,OAAO,CAAA;IACb,GAAG,EAAE,YAAY,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,YAAY,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,CAAC,EAAE,MAAM,CAAA;IACT,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,OAAO,CAAA;IACb,GAAG,EAAE,YAAY,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,aAAa,CAAC;QACpB,MAAM,EAAE,aAAa,CAAC;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;QAC/C,KAAK,EAAE,MAAM,CAAA;QACb,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAC,CAAA;IACF,UAAU,EAAE,MAAM,CAAA;IAClB,CAAC,EAAE,MAAM,CAAA;IACT,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,YAAY,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,aAAa,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC9C,KAAK,EAAE,aAAa,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,CAAC,EAAE,MAAM,CAAA;IACT,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;AAMxD,MAAM,MAAM,oBAAoB,GAAG;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAA;IAC7B,cAAc,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,CAAA;IAC5C,QAAQ,IAAI,IAAI,CAAA;IAChB,KAAK,IAAI,MAAM,EAAE,CAAA;IACjB,KAAK,IAAI,IAAI,CAAA;CACd,CAAA;AAED,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,aAAa,CAwCrF"}
@@ -0,0 +1,42 @@
1
+ export function createRetainedScene(options = {}) {
2
+ const staleFrames = options.staleFrames ?? 2;
3
+ const nodes = new Map();
4
+ let frameNumber = 0;
5
+ return {
6
+ beginFrame(nextFrameNumber) {
7
+ frameNumber = nextFrameNumber;
8
+ },
9
+ upsert(node) {
10
+ nodes.set(node.key, {
11
+ ...node,
12
+ lastTouchedFrame: frameNumber,
13
+ });
14
+ },
15
+ collectVisible(paneId) {
16
+ const result = [];
17
+ for (const stored of nodes.values()) {
18
+ if (paneId !== undefined && stored.paneId !== paneId)
19
+ continue;
20
+ const { lastTouchedFrame: _lastTouchedFrame, ...node } = stored;
21
+ result.push(node);
22
+ }
23
+ result.sort((a, b) => a.z - b.z || a.key.localeCompare(b.key));
24
+ return result;
25
+ },
26
+ endFrame() { },
27
+ prune() {
28
+ const removed = [];
29
+ for (const [key, stored] of nodes) {
30
+ if (frameNumber - stored.lastTouchedFrame >= staleFrames) {
31
+ nodes.delete(key);
32
+ removed.push(key);
33
+ }
34
+ }
35
+ return removed;
36
+ },
37
+ clear() {
38
+ nodes.clear();
39
+ },
40
+ };
41
+ }
42
+ //# sourceMappingURL=retainedScene.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retainedScene.js","sourceRoot":"","sources":["../../../src/rendering/scene/retainedScene.ts"],"names":[],"mappings":"AA4DA,MAAM,UAAU,mBAAmB,CAAC,UAAgC,EAAE;IACpE,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,CAAA;IAC5C,MAAM,KAAK,GAAG,IAAI,GAAG,EAA4B,CAAA;IACjD,IAAI,WAAW,GAAG,CAAC,CAAA;IAEnB,OAAO;QACL,UAAU,CAAC,eAAe;YACxB,WAAW,GAAG,eAAe,CAAA;QAC/B,CAAC;QACD,MAAM,CAAC,IAAI;YACT,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE;gBAClB,GAAG,IAAI;gBACP,gBAAgB,EAAE,WAAW;aAC9B,CAAC,CAAA;QACJ,CAAC;QACD,cAAc,CAAC,MAAM;YACnB,MAAM,MAAM,GAAgB,EAAE,CAAA;YAC9B,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;gBACpC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM;oBAAE,SAAQ;gBAC9D,MAAM,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAA;gBAC/D,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACnB,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YAC9D,OAAO,MAAM,CAAA;QACf,CAAC;QACD,QAAQ,KAAU,CAAC;QACnB,KAAK;YACH,MAAM,OAAO,GAAa,EAAE,CAAA;YAC5B,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBAClC,IAAI,WAAW,GAAG,MAAM,CAAC,gBAAgB,IAAI,WAAW,EAAE,CAAC;oBACzD,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;oBACjB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;gBACnB,CAAC;YACH,CAAC;YACD,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,KAAK;YACH,KAAK,CAAC,KAAK,EAAE,CAAA;QACf,CAAC;KACF,CAAA;AACH,CAAC"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.9.0-alpha.0";
1
+ export declare const VERSION = "0.9.0-alpha.2";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const VERSION = '0.9.0-alpha.0';
1
+ export const VERSION = '0.9.0-alpha.2';
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@363045841yyt/klinechart-core",
3
- "version": "0.9.0-alpha.0",
3
+ "version": "0.9.0-alpha.2",
4
4
  "description": "Headless K-line chart engine + reactive controllers. Zero framework dependencies.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -126,6 +126,7 @@
126
126
  "@arethetypeswrong/cli": "^0.18.3",
127
127
  "@size-limit/preset-small-lib": "^12.1.0",
128
128
  "@types/jsdom": "^28.0.0",
129
+ "@webgpu/types": "^0.1.71",
129
130
  "publint": "^0.3.0",
130
131
  "size-limit": "^12.1.0",
131
132
  "typescript": "~6.0.3",
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, vi } from 'vitest'
2
2
 
3
- import { createSignal, effect, computed } from '../foundation/reactivity/signal'
3
+ import { createSignal, effect, computed, selectSignal } from '../foundation/reactivity/signal'
4
4
 
5
5
  describe('createSignal', () => {
6
6
  it('reads initial value', () => {
@@ -123,3 +123,49 @@ describe('computed', () => {
123
123
  expect(listener).toHaveBeenCalledTimes(1)
124
124
  })
125
125
  })
126
+
127
+ describe('selectSignal', () => {
128
+ it('notifies only when selected value changes', () => {
129
+ const source = createSignal({ a: 1, b: 2 })
130
+ const selected = selectSignal(source, (s) => s.a)
131
+ const listener = vi.fn()
132
+ selected.subscribe(listener)
133
+
134
+ source.set({ a: 1, b: 9 })
135
+ expect(listener).not.toHaveBeenCalled()
136
+ expect(selected.peek()).toBe(1)
137
+
138
+ source.set({ a: 3, b: 9 })
139
+ expect(listener).toHaveBeenCalledTimes(1)
140
+ expect(selected.peek()).toBe(3)
141
+ })
142
+
143
+ it('supports custom equality for structural fields', () => {
144
+ const source = createSignal({ pos: { x: 1, y: 2 } as { x: number; y: number } | null })
145
+ const selected = selectSignal(
146
+ source,
147
+ (s) => s.pos,
148
+ (a, b) =>
149
+ a === b || (a !== null && b !== null && a.x === b.x && a.y === b.y) || (a === null && b === null),
150
+ )
151
+ const listener = vi.fn()
152
+ selected.subscribe(listener)
153
+
154
+ source.set({ pos: { x: 1, y: 2 } })
155
+ expect(listener).not.toHaveBeenCalled()
156
+
157
+ source.set({ pos: { x: 2, y: 2 } })
158
+ expect(listener).toHaveBeenCalledTimes(1)
159
+ })
160
+
161
+ it('dispose stops following source updates', () => {
162
+ const source = createSignal({ a: 1, b: 0 })
163
+ const selected = selectSignal(source, (s) => s.a)
164
+ const listener = vi.fn()
165
+ selected.subscribe(listener)
166
+ selected.dispose()
167
+ source.set({ a: 9, b: 0 })
168
+ expect(listener).not.toHaveBeenCalled()
169
+ expect(selected.peek()).toBe(1)
170
+ })
171
+ })
@@ -0,0 +1,102 @@
1
+ // @vitest-environment jsdom
2
+ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
3
+
4
+ import { createChartController } from '../createChartController'
5
+ import { loadBuiltinIndicators } from '../../engine/indicators/registerBuiltins'
6
+
7
+ class ResizeObserverMock {
8
+ observe = vi.fn()
9
+ disconnect = vi.fn()
10
+ unobserve = vi.fn()
11
+ }
12
+
13
+ function createCanvasContextStub() {
14
+ return {
15
+ setTransform: vi.fn(),
16
+ scale: vi.fn(),
17
+ clearRect: vi.fn(),
18
+ save: vi.fn(),
19
+ restore: vi.fn(),
20
+ beginPath: vi.fn(),
21
+ moveTo: vi.fn(),
22
+ lineTo: vi.fn(),
23
+ stroke: vi.fn(),
24
+ fillRect: vi.fn(),
25
+ strokeRect: vi.fn(),
26
+ fillText: vi.fn(),
27
+ measureText: vi.fn(() => ({ width: 40 })),
28
+ } as unknown as CanvasRenderingContext2D
29
+ }
30
+
31
+ function createWebGLStub(): WebGL2RenderingContext {
32
+ const noop = () => {}
33
+ return new Proxy({} as unknown as WebGL2RenderingContext, {
34
+ get(_, prop) {
35
+ if (typeof prop !== 'string') return undefined
36
+ if (/^[A-Z][A-Z0-9_]*$/.test(prop)) return 0
37
+ if (prop === 'getShaderInfoLog' || prop === 'getProgramInfoLog') return () => ''
38
+ if (prop === 'getShaderParameter' || prop === 'getProgramParameter') return () => true
39
+ if (prop === 'getError') return () => 0
40
+ if (prop === 'getSupportedExtensions') return () => []
41
+ if (prop === 'getContextAttributes') return () => ({})
42
+ if (prop === 'getParameter') return () => 0
43
+ if (prop === 'getUniformLocation' || prop === 'getAttribLocation') return () => 0
44
+ if (prop.startsWith('create') || prop === 'getExtension') return () => ({ __webglStub: true })
45
+ if (prop === 'drawingBufferWidth' || prop === 'drawingBufferHeight') return 300
46
+ return noop
47
+ },
48
+ })
49
+ }
50
+
51
+ describe('createChartController mount theme', () => {
52
+ beforeAll(async () => {
53
+ await loadBuiltinIndicators()
54
+ })
55
+
56
+ beforeEach(() => {
57
+ vi.stubGlobal('ResizeObserver', ResizeObserverMock)
58
+ HTMLCanvasElement.prototype.getContext = vi.fn((type: string) => {
59
+ if (type === '2d') return createCanvasContextStub()
60
+ if (type === 'webgl2' || type === 'webgl') return createWebGLStub()
61
+ return null
62
+ }) as never
63
+ })
64
+
65
+ afterEach(() => {
66
+ vi.unstubAllGlobals()
67
+ vi.restoreAllMocks()
68
+ })
69
+
70
+ it('opts.theme light overrides settings default dark', async () => {
71
+ const container = document.createElement('div')
72
+ Object.defineProperty(container, 'clientWidth', { value: 800, configurable: true })
73
+ Object.defineProperty(container, 'clientHeight', { value: 600, configurable: true })
74
+ document.body.appendChild(container)
75
+
76
+ const ctrl = await createChartController({
77
+ container,
78
+ theme: 'light',
79
+ })
80
+
81
+ expect(ctrl.settings.peek().theme).toBe('light')
82
+ expect(ctrl.theme.peek()).toBe('light')
83
+
84
+ ctrl.dispose()
85
+ container.remove()
86
+ })
87
+
88
+ it('without opts.theme keeps default dark preference', async () => {
89
+ const container = document.createElement('div')
90
+ Object.defineProperty(container, 'clientWidth', { value: 800, configurable: true })
91
+ Object.defineProperty(container, 'clientHeight', { value: 600, configurable: true })
92
+ document.body.appendChild(container)
93
+
94
+ const ctrl = await createChartController({ container })
95
+
96
+ expect(ctrl.settings.peek().theme).toBe('dark')
97
+ expect(ctrl.theme.peek()).toBe('dark')
98
+
99
+ ctrl.dispose()
100
+ container.remove()
101
+ })
102
+ })
@@ -28,6 +28,10 @@ import { zoomLevelToKWidth, kGapFromKWidth } from '../engine/utils/zoom'
28
28
  import { KLineChartError } from '../errors'
29
29
  import { ChartBridge } from '../features/mcp/chartBridge'
30
30
  import { computed, type ReadonlySignal } from '../foundation/reactivity/index'
31
+ import {
32
+ createDefaultRendererHost,
33
+ type RendererBackend,
34
+ } from '../rendering/render/index'
31
35
 
32
36
  import type {
33
37
  ChartController,
@@ -334,6 +338,10 @@ export async function createChartController(opts: ChartMountOptions): Promise<Ch
334
338
  initialZoomLevel,
335
339
  }
336
340
 
341
+ const initialSettings = resolveSettings(opts.settings)
342
+ const rendererHost = await createDefaultRendererHost(
343
+ initialSettings.rendererBackend as RendererBackend,
344
+ )
337
345
  const chart = new Chart(
338
346
  {
339
347
  container: mounted.container,
@@ -344,8 +352,13 @@ export async function createChartController(opts: ChartMountOptions): Promise<Ch
344
352
  xAxisCanvas: mounted.xAxisCanvas,
345
353
  },
346
354
  chartOptions,
355
+ { rendererHost, initialSettings },
347
356
  )
348
357
 
358
+ if (import.meta.env?.MODE !== 'production' && typeof window !== 'undefined') {
359
+ ;(window as any).__chart = chart
360
+ }
361
+
349
362
  const currentDpr =
350
363
  typeof window !== 'undefined' && window.devicePixelRatio > 0 ? window.devicePixelRatio : 1
351
364
  const currentKWidth = zoomLevelToKWidth(initialZoomLevel, {
@@ -373,8 +386,9 @@ export async function createChartController(opts: ChartMountOptions): Promise<Ch
373
386
  const comparisonLoading = chart.comparisonLoading
374
387
 
375
388
  // Signals from ChartStateKernel — no wrapper needed
376
- const themeSignal: ReadonlySignal<'light' | 'dark'> = chart.kernel.theme.readonly.theme
389
+ const themeSignal: ReadonlySignal<'light' | 'dark'> = chart.theme
377
390
  const settingsSignal = chart.kernel.settings.readonly.settings
391
+ const rendererRuntimeSignal = chart.kernel.renderer.readonly.runtime
378
392
  const chartModeSignal = chart.kernel.mode.readonly.chartMode
379
393
  const drawingTool = chart.kernel.drawing.readonly.drawingTool
380
394
  // drawings need type mapping (plugin DrawingObject → controller DrawingObject)
@@ -414,8 +428,8 @@ export async function createChartController(opts: ChartMountOptions): Promise<Ch
414
428
  chart.setSymbols(opts.symbols)
415
429
  }
416
430
 
417
- // Apply initial theme if non-default
418
- if (opts.theme && opts.theme !== 'light') {
431
+ // Apply mount theme preference (settings default may be dark — always honor explicit opts.theme)
432
+ if (opts.theme) {
419
433
  try {
420
434
  chart.setTheme(opts.theme)
421
435
  } catch {
@@ -423,15 +437,6 @@ export async function createChartController(opts: ChartMountOptions): Promise<Ch
423
437
  }
424
438
  }
425
439
 
426
- // Apply initial settings (partial, merged with defaults)
427
- if (opts.settings) {
428
- try {
429
- chart.updateSettingsFacade(resolveSettings(opts.settings))
430
- } catch {
431
- /* tolerate first-paint racing */
432
- }
433
- }
434
-
435
440
  // -------------------------------------------------------------------
436
441
  // Apply initial render state + seed data
437
442
  // -------------------------------------------------------------------
@@ -537,6 +542,11 @@ export async function createChartController(opts: ChartMountOptions): Promise<Ch
537
542
  chart.setTheme(nextTheme)
538
543
  }
539
544
 
545
+ function setSystemTheme(nextTheme: 'light' | 'dark'): void {
546
+ if (disposed) return
547
+ chart.setSystemTheme(nextTheme)
548
+ }
549
+
540
550
  function zoomToLevel(level: number, anchorX?: number): void {
541
551
  if (disposed) return
542
552
  chart.zoomToLevel(level, anchorX)
@@ -714,11 +724,21 @@ export async function createChartController(opts: ChartMountOptions): Promise<Ch
714
724
  return chart.drawings() as any[]
715
725
  }
716
726
 
727
+ function requestDraw(): void {
728
+ if (disposed) return
729
+ chart.scheduleDraw()
730
+ }
731
+
717
732
  function setSelectedDrawingId(id: string | null): void {
718
733
  if (disposed) return
719
734
  chart.setSelectedDrawingId(id)
720
735
  }
721
736
 
737
+ function getSelectedDrawingId(): string | null {
738
+ if (disposed) return null
739
+ return chart.kernel.drawing.readonly.selectedDrawingId.peek()
740
+ }
741
+
722
742
  function getViewport(): { scrollLeft: number; plotWidth: number; plotHeight: number } | null {
723
743
  if (disposed) return null
724
744
  const vp = chart.getViewport()
@@ -891,6 +911,7 @@ export async function createChartController(opts: ChartMountOptions): Promise<Ch
891
911
  symbols,
892
912
  theme: themeSignal,
893
913
  settings: settingsSignal,
914
+ rendererRuntime: rendererRuntimeSignal,
894
915
  chartMode: chartModeSignal,
895
916
  indicators,
896
917
  subPanes,
@@ -924,6 +945,7 @@ export async function createChartController(opts: ChartMountOptions): Promise<Ch
924
945
  getData,
925
946
  getZoomLevelCount,
926
947
  setTheme,
948
+ setSystemTheme,
927
949
  zoomToLevel,
928
950
  zoomIn,
929
951
  zoomOut,
@@ -949,7 +971,9 @@ export async function createChartController(opts: ChartMountOptions): Promise<Ch
949
971
  removeDrawing,
950
972
  setDrawings,
951
973
  getFullDrawings,
974
+ requestDraw,
952
975
  setSelectedDrawingId,
976
+ getSelectedDrawingId,
953
977
  getViewport,
954
978
  getKWidthKGap,
955
979
  getCurrentDpr,
@@ -31,6 +31,11 @@ export type {
31
31
  DataFetcher,
32
32
  CustomDataSource,
33
33
  } from './types'
34
+ export type {
35
+ RendererBackend,
36
+ RendererBackendRuntime,
37
+ RendererBackendStatus,
38
+ } from '../rendering/render/rendererHost'
34
39
 
35
40
  export { createChartController } from './createChartController'
36
41
  export { createIndicatorSelectorController } from './createIndicatorSelectorController'
@@ -202,10 +202,17 @@ export interface DrawingChartAdapter {
202
202
  getFullDrawings(): any[]
203
203
  /** highlight a drawing by ID */
204
204
  setSelectedDrawingId(id: string | null): void
205
+ /** read selected drawing id from kernel */
206
+ getSelectedDrawingId(): string | null
205
207
  /** write drawing tool id via Chart (kernel SSOT + session side effects) */
206
208
  setDrawingToolId(toolId: import('../engine/drawing/toolConfig').DrawingToolId): void
207
209
  /** read current drawing tool id from kernel */
208
210
  getDrawingToolId(): import('../engine/drawing/toolConfig').DrawingToolId
211
+ /**
212
+ * 会话态变更后请求重绘(不写 kernel)。
213
+ * 预览 / 拖拽中间态只改会话层时调用。
214
+ */
215
+ requestDraw?(): void
209
216
  /** current viewport (nullable if chart not ready) */
210
217
  getViewport(): DrawingChartViewport | null
211
218
  /** resolved chart options (kWidth, kGap) */
@@ -291,6 +298,10 @@ export interface ChartController extends DrawingChartAdapter {
291
298
  readonly settings: ReadonlySignal<
292
299
  Readonly<import('../foundation/config/chartSettings').ChartSettings>
293
300
  >
301
+ /** 当前有效 renderer、切换状态和最近错误。 */
302
+ readonly rendererRuntime: ReadonlySignal<
303
+ Readonly<import('../rendering/render/rendererHost').RendererBackendRuntime>
304
+ >
294
305
  /** 图表模式 id:kline | timeshare */
295
306
  readonly chartMode: ReadonlySignal<'kline' | 'timeshare'>
296
307
  readonly indicators: ReadonlySignal<ReadonlyArray<IndicatorInstance>>
@@ -343,7 +354,10 @@ export interface ChartController extends DrawingChartAdapter {
343
354
  ensureDataRange(startTs: number): void
344
355
 
345
356
  // ---- Theme ----
357
+ /** 设置主题偏好 light|dark(写 settings) */
346
358
  setTheme(theme: 'light' | 'dark'): void
359
+ /** 注入系统主题(settings.theme === auto 时驱动 effectiveTheme) */
360
+ setSystemTheme(theme: 'light' | 'dark'): void
347
361
 
348
362
  // ---- Zoom ----
349
363
  zoomToLevel(level: number, anchorX?: number): void
@@ -521,6 +521,70 @@ describe('Chart pane layout regressions', () => {
521
521
  await chart.destroy()
522
522
  })
523
523
 
524
+ it('removeDrawing drops id from kernel and clears selection', async () => {
525
+ const chart = new Chart(createDom(1000, 600), defaultOptions)
526
+ const d1 = {
527
+ id: 'd1',
528
+ kind: 'trend-line' as const,
529
+ paneId: 'main',
530
+ visible: true,
531
+ anchors: [],
532
+ params: {},
533
+ style: { stroke: '#f00' },
534
+ }
535
+ const d2 = { ...d1, id: 'd2' }
536
+ chart.setDrawings([d1, d2])
537
+ chart.setSelectedDrawingId('d1')
538
+ chart.removeDrawing('d1')
539
+ expect(chart.kernel.drawing.readonly.drawings.peek().map((d) => d.id)).toEqual(['d2'])
540
+ expect(chart.kernel.drawing.readonly.selectedDrawingId.peek()).toBeNull()
541
+ await chart.destroy()
542
+ })
543
+
544
+ it('removeDrawing with registered session updates kernel only', async () => {
545
+ const { DrawingInteractionController } = await import('../drawing/interaction')
546
+ const chart = new Chart(createDom(1000, 600), defaultOptions)
547
+ const d1 = {
548
+ id: 'd1',
549
+ kind: 'trend-line' as const,
550
+ paneId: 'main',
551
+ visible: true,
552
+ anchors: [],
553
+ params: {},
554
+ style: { stroke: '#f00' },
555
+ }
556
+ const d2 = { ...d1, id: 'd2' }
557
+ chart.setDrawings([d1, d2])
558
+ chart.setSelectedDrawingId('d1')
559
+
560
+ const adapter = {
561
+ setDrawings: (list: typeof d1[]) => chart.setDrawings(list),
562
+ getFullDrawings: () => [...chart.kernel.drawing.readonly.drawings.peek()],
563
+ setSelectedDrawingId: (id: string | null) => chart.setSelectedDrawingId(id),
564
+ getSelectedDrawingId: () => chart.kernel.drawing.readonly.selectedDrawingId.peek(),
565
+ setDrawingToolId: (id: import('../drawing/toolConfig').DrawingToolId) =>
566
+ chart.setDrawingTool(id),
567
+ getDrawingToolId: () => chart.kernel.drawing.readonly.drawingTool.peek(),
568
+ requestDraw: () => chart.scheduleDraw(),
569
+ getViewport: () => null,
570
+ getKWidthKGap: () => ({ kWidth: 6, kGap: 2 }),
571
+ getCurrentDpr: () => 1,
572
+ getData: () => [],
573
+ getLogicalIndexAtX: () => null,
574
+ getTimestampAtLogicalIndex: () => null,
575
+ priceToY: () => 0,
576
+ yToPrice: () => 0,
577
+ getPaneInfo: () => undefined,
578
+ }
579
+ const session = new DrawingInteractionController(adapter)
580
+ chart.registerDrawingSession(session)
581
+ chart.setSelectedDrawingId('d1')
582
+ chart.removeDrawing('d1')
583
+ expect(chart.kernel.drawing.readonly.drawings.peek().map((d) => d.id)).toEqual(['d2'])
584
+ expect(chart.kernel.drawing.readonly.selectedDrawingId.peek()).toBeNull()
585
+ chart.registerDrawingSession(null)
586
+ await chart.destroy()
587
+ })
524
588
  it('setDrawingTool writes DrawingToolId to kernel', async () => {
525
589
  const chart = new Chart(createDom(1000, 600), defaultOptions)
526
590
  expect(chart.kernel.drawing.readonly.drawingTool.peek()).toBe('cursor')
@@ -3,7 +3,6 @@ import { describe, expect, it } from 'vitest'
3
3
 
4
4
  import { Pane } from '@/core/layout/pane'
5
5
  import { PaneRenderer } from '@/core/paneRenderer'
6
- import { SharedWebGLSurface } from '@/core/renderers/webgl/sharedWebGLSurface'
7
6
 
8
7
  describe('PaneRenderer resize DPR mapping', () => {
9
8
  it('maps logical plot size to physical canvas size and keeps CSS size logical', () => {
@@ -11,7 +10,6 @@ describe('PaneRenderer resize DPR mapping', () => {
11
10
  const overlayCanvas = document.createElement('canvas')
12
11
  const yAxisCanvas = document.createElement('canvas')
13
12
  const pane = new Pane('main')
14
- const sharedWebGLSurface = new SharedWebGLSurface()
15
13
  const renderer = new PaneRenderer(
16
14
  { mainCanvas, overlayCanvas, yAxisCanvas },
17
15
  pane,
@@ -21,7 +19,6 @@ describe('PaneRenderer resize DPR mapping', () => {
21
19
  yPaddingPx: 0,
22
20
  priceLabelWidth: 60,
23
21
  },
24
- sharedWebGLSurface,
25
22
  )
26
23
 
27
24
  renderer.resize(500, 240, 2)
@@ -40,7 +37,6 @@ describe('PaneRenderer resize DPR mapping', () => {
40
37
  const overlayCanvas = document.createElement('canvas')
41
38
  const yAxisCanvas = document.createElement('canvas')
42
39
  const pane = new Pane('main')
43
- const sharedWebGLSurface = new SharedWebGLSurface()
44
40
  const renderer = new PaneRenderer(
45
41
  { mainCanvas, overlayCanvas, yAxisCanvas },
46
42
  pane,
@@ -50,7 +46,6 @@ describe('PaneRenderer resize DPR mapping', () => {
50
46
  yPaddingPx: 0,
51
47
  priceLabelWidth: 70,
52
48
  },
53
- sharedWebGLSurface,
54
49
  )
55
50
 
56
51
  renderer.resize(500, 200, 1.5)
@@ -74,7 +69,6 @@ describe('PaneRenderer resize DPR mapping', () => {
74
69
  host.appendChild(yAxisCanvas)
75
70
 
76
71
  const pane = new Pane('main')
77
- const sharedWebGLSurface = new SharedWebGLSurface()
78
72
  const renderer = new PaneRenderer(
79
73
  { mainCanvas, overlayCanvas, yAxisCanvas },
80
74
  pane,
@@ -84,7 +78,6 @@ describe('PaneRenderer resize DPR mapping', () => {
84
78
  yPaddingPx: 0,
85
79
  priceLabelWidth: 70,
86
80
  },
87
- sharedWebGLSurface,
88
81
  )
89
82
 
90
83
  renderer.resize(500, 200, 1.5)
@@ -0,0 +1,121 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
2
+
3
+ import { createScene } from '../../rendering/scene/createScene'
4
+ import { createLayerFromPlugin } from '../../rendering/scene/createLayerFromPlugin'
5
+ import { RendererPluginManager } from '../../foundation/plugin/rendererPluginManager'
6
+ import type { RendererPlugin, RenderContext } from '../../foundation/plugin/index'
7
+ import { RENDERER_PRIORITY } from '../../foundation/plugin/index'
8
+
9
+ /**
10
+ * Phase 0 契约:绘制只走 Scene;Manager 仅注册表;
11
+ * useRenderer 桥 = register + addLayer;setRendererEnabled 驱动 Layer 显隐。
12
+ */
13
+ describe('render single-path (Phase 0)', () => {
14
+ let scene: ReturnType<typeof createScene>
15
+ let manager: RendererPluginManager
16
+ let consoleError: ReturnType<typeof vi.spyOn>
17
+
18
+ beforeEach(() => {
19
+ scene = createScene()
20
+ manager = new RendererPluginManager()
21
+ consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
22
+ })
23
+
24
+ afterEach(() => {
25
+ scene.dispose()
26
+ manager.clear()
27
+ consoleError.mockRestore()
28
+ })
29
+
30
+ function makePlugin(name: string, draw = vi.fn()): RendererPlugin {
31
+ return {
32
+ name,
33
+ paneId: 'main',
34
+ priority: RENDERER_PRIORITY.MAIN,
35
+ enabled: true,
36
+ draw,
37
+ }
38
+ }
39
+
40
+ function paintMain() {
41
+ scene.paintPane({
42
+ renderer: {} as never,
43
+ region: { x: 0, y: 0, width: 100, height: 100, dpr: 1 },
44
+ paneRole: 'main',
45
+ paneId: 'main',
46
+ frameNumber: 1,
47
+ deltaMs: 0,
48
+ })
49
+ }
50
+
51
+ it('bridge: register + createLayerFromPlugin paints via scene only', () => {
52
+ const draw = vi.fn()
53
+ const plugin = makePlugin('bridge-a', draw)
54
+ const ctx = { theme: 'dark' } as unknown as RenderContext
55
+ manager.register(plugin)
56
+ scene.addLayer(createLayerFromPlugin(plugin, () => ctx, 'main'))
57
+
58
+ paintMain()
59
+ expect(draw).toHaveBeenCalledOnce()
60
+ expect(draw).toHaveBeenCalledWith(ctx)
61
+ })
62
+
63
+ it('setLayerVisibility false skips paint (timeshare candle path)', () => {
64
+ const draw = vi.fn()
65
+ const plugin = makePlugin('candle', draw)
66
+ scene.addLayer(createLayerFromPlugin(plugin, () => ({}) as RenderContext, 'main'))
67
+
68
+ expect(scene.setLayerVisibility('plugin:candle', false)).toBe(true)
69
+ paintMain()
70
+ expect(draw).not.toHaveBeenCalled()
71
+
72
+ scene.setLayerVisibility('plugin:candle', true)
73
+ paintMain()
74
+ expect(draw).toHaveBeenCalledOnce()
75
+ })
76
+
77
+ it('remove path: Manager.unregister owns onUninstall; removeLayer does not dispose-call it', () => {
78
+ const onUninstall = vi.fn()
79
+ const plugin = makePlugin('to-remove', vi.fn())
80
+ plugin.onUninstall = onUninstall
81
+ manager.register(plugin)
82
+ scene.addLayer(createLayerFromPlugin(plugin, () => null, 'main'))
83
+
84
+ manager.unregister('to-remove')
85
+ expect(onUninstall).toHaveBeenCalledOnce()
86
+
87
+ scene.removeLayer('plugin:to-remove')
88
+ // layer.dispose empty — no second onUninstall
89
+ expect(onUninstall).toHaveBeenCalledOnce()
90
+ })
91
+
92
+ it('one layer throw does not abort sibling layers', () => {
93
+ const ok = vi.fn()
94
+ const boom = vi.fn(() => {
95
+ throw new Error('boom')
96
+ })
97
+ scene.addLayer(
98
+ createLayerFromPlugin(makePlugin('boom', boom), () => ({}) as RenderContext, 'main'),
99
+ )
100
+ scene.addLayer(
101
+ createLayerFromPlugin(makePlugin('ok', ok), () => ({}) as RenderContext, 'main'),
102
+ )
103
+
104
+ expect(() => paintMain()).not.toThrow()
105
+ expect(boom).toHaveBeenCalledOnce()
106
+ expect(ok).toHaveBeenCalledOnce()
107
+ })
108
+
109
+ it('Scene paints without Manager draw API (single path)', () => {
110
+ const draw = vi.fn()
111
+ const plugin = makePlugin('only-scene', draw)
112
+ manager.register(plugin)
113
+ manager.setEnabled(plugin.name, false)
114
+ scene.addLayer(createLayerFromPlugin(plugin, () => ({}) as RenderContext, 'main'))
115
+
116
+ // Manager 无 render 入口;Scene 仍画
117
+ expect(typeof (manager as { render?: unknown }).render).toBe('undefined')
118
+ paintMain()
119
+ expect(draw).toHaveBeenCalledOnce()
120
+ })
121
+ })