@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
@@ -77,6 +77,18 @@ export class InteractionController {
77
77
  private touchStartY = 0
78
78
  private pinchTracker = new PinchTracker()
79
79
  private lastClientPos: { x: number; y: number } | null = null
80
+ /**
81
+ * 空闲 hover 是否有未推导的指针输入。
82
+ * pointermove 只置位;flushPendingHover 每帧最多推导一次并写 kernel。
83
+ */
84
+ private hoverFlushPending = false
85
+ /**
86
+ * 本帧封存的 K 线几何(非 kernel signal,避免每帧广播大数组)。
87
+ * 仅 InteractionController hover 读取;绘制走 ChartRenderer FrameContext。
88
+ */
89
+ private framePositions: number[] | null = null
90
+ private frameCenters: number[] | null = null
91
+ private frameKWidthPx: number | null = null
80
92
  private markerState = new MarkerInteractionState()
81
93
  private lastHoverRenderKey = ''
82
94
  private useTooltipAnchorPositioning = false
@@ -322,6 +334,9 @@ export class InteractionController {
322
334
  this.activePaneIdOnDrag = null
323
335
  this.clearSeparatorState()
324
336
  if (!this.isTouchSession) {
337
+ // 取消已排队 hover,避免后续帧用 lastClientPos 把十字线刷回来
338
+ this.hoverFlushPending = false
339
+ this.lastClientPos = null
325
340
  this.clearHover()
326
341
  this.chart.scheduleDraw()
327
342
  }
@@ -330,7 +345,10 @@ export class InteractionController {
330
345
 
331
346
  /** 处理滚动事件 */
332
347
  onScroll(options: { scheduleDraw?: boolean } = {}) {
333
- this._state.actions.updateFramePositions(null, null, null)
348
+ this.framePositions = null
349
+ this.frameCenters = null
350
+ this.frameKWidthPx = null
351
+ this.hoverFlushPending = false
334
352
  this.clearHover()
335
353
  if (options.scheduleDraw !== false) {
336
354
  this.chart.scheduleDraw()
@@ -382,15 +400,14 @@ export class InteractionController {
382
400
  const dy = Math.abs(e.clientY - this.touchStartY)
383
401
  if (elapsed >= InteractionController.LONG_PRESS_MS && dx < 10 && dy < 10) {
384
402
  this._state.actions.setDragMode('explore')
385
- this.updatePlotHoverFromPoint(e.clientX, e.clientY)
386
- this.chart.scheduleDraw()
403
+ this.queueHoverFlush()
387
404
  return
388
405
  }
389
406
  }
390
407
 
391
408
  if (this._state.readonly.dragMode.peek() === 'explore') {
392
- this.updatePlotHoverFromPoint(e.clientX, e.clientY)
393
- this.chart.scheduleDraw()
409
+ // explore 跟手:仍走 pending,由本帧 flush 推导(与空闲 hover 同路径)
410
+ this.queueHoverFlush()
394
411
  return
395
412
  }
396
413
 
@@ -415,36 +432,66 @@ export class InteractionController {
415
432
  return
416
433
  }
417
434
 
418
- const location = this.getPlotPointerLocation(e.clientX, e.clientY)
419
- if (!location) return
420
- this._state.actions.setSeparatorHover(this.hitTestPaneSeparator(location.mouseY))
435
+ // 空闲 hover:只记录指针,不写 crosshair/tooltip Signal
436
+ this.queueHoverFlush()
437
+ }
421
438
 
422
- this.updatePlotHoverFromPoint(e.clientX, e.clientY)
439
+ /**
440
+ * 标记有待推导的 hover 输入,并申请 Overlay 帧。
441
+ * 真正的 updatePlotHoverFromPoint 在 flushPendingHover 中执行。
442
+ */
443
+ private queueHoverFlush(): void {
444
+ this.hoverFlushPending = true
445
+ this.chart.scheduleDraw(UpdateLevel.Overlay)
446
+ }
447
+
448
+ /**
449
+ * 将 pending 指针推导为 crosshair/hover/tooltip 并写入 kernel。
450
+ * ChartRenderer 在 seal 几何之后、paint 之前调用,保证与本帧几何同代。
451
+ * 无 pending 时为 no-op。
452
+ */
453
+ flushPendingHover(): void {
454
+ if (!this.hoverFlushPending) return
455
+ this.hoverFlushPending = false
456
+ if (!this.lastClientPos) return
457
+
458
+ this.updatePlotHoverFromPoint(this.lastClientPos.x, this.lastClientPos.y)
423
459
  const hoverRenderKey = this.getHoverRenderKey()
424
460
  if (hoverRenderKey !== this.lastHoverRenderKey) {
425
461
  this.lastHoverRenderKey = hoverRenderKey
426
- this.chart.scheduleDraw(UpdateLevel.Overlay)
427
462
  }
428
463
  }
429
464
 
430
465
  /**
431
- * 设置当前帧的 K 线起始 x 坐标数组和可见范围
466
+ * 封存本帧 K 线几何到 controller 私有字段(非 kernel signal)。
467
+ * ChartRenderer 在 paint 前调用;引用未变则跳过 hover 重算。
468
+ *
432
469
  * @param positions K 线起始 x 坐标数组
433
- * @param visibleRange 可见 K 线索引范围
470
+ * @param visibleRange 保留兼容签名(hover viewport)
434
471
  * @param kWidthPx K 线宽度(物理像素)
435
- * @param centers K 线中心 x 坐标数组(物理像素对齐后)
472
+ * @param centers K 线中心 x 坐标数组
436
473
  */
437
474
  setKLinePositions(
438
475
  positions: number[] | null,
439
- visibleRange: { start: number; end: number } | null,
476
+ _visibleRange: { start: number; end: number } | null,
440
477
  kWidthPx?: number,
441
478
  centers?: number[] | null,
442
479
  ) {
443
- this._state.actions.updateFramePositions(positions, centers ?? null, kWidthPx ?? null)
480
+ const nextWidth = kWidthPx ?? null
481
+ const nextCenters = centers ?? null
482
+ const unchanged =
483
+ this.framePositions === positions &&
484
+ this.frameCenters === nextCenters &&
485
+ this.frameKWidthPx === nextWidth
486
+
487
+ this.framePositions = positions
488
+ this.frameCenters = nextCenters
489
+ this.frameKWidthPx = nextWidth
444
490
 
445
- // 十字线自动同步:positions 刷新后用最新鼠标位置重算,防止缩放/滚动后索引滞后
446
- if (this.lastClientPos && !this._state.readonly.isDragging.peek()) {
447
- this.updatePlotHoverFromPoint(this.lastClientPos.x, this.lastClientPos.y)
491
+ // 几何变化时标记 hover 待刷新;由 flushPendingHover 与 paint 同帧完成
492
+ if (!unchanged && this.lastClientPos && !this._state.readonly.isDragging.peek()) {
493
+ this.hoverFlushPending = true
494
+ this.flushPendingHover()
448
495
  }
449
496
  }
450
497
 
@@ -566,15 +613,16 @@ export class InteractionController {
566
613
  this.activePaneIdOnDrag = pane.id
567
614
  this._state.actions.setRightAxisHover(pane.id)
568
615
  this._state.actions.setSeparatorHover(null)
569
- this._state.actions.updateCrosshair(null, null)
616
+ this._state.actions.updateCrosshair(null, null, null)
570
617
  this._state.actions.updateHover(null, pane.id)
571
618
  return true
572
619
  }
573
620
 
574
621
  clearHover() {
622
+ this.hoverFlushPending = false
575
623
  this.lastHoverRenderKey = ''
576
624
  this._state.actions.setRightAxisHover(null)
577
- this._state.actions.updateCrosshair(null, null)
625
+ this._state.actions.updateCrosshair(null, null, null)
578
626
  this._state.actions.updateHover(null, null)
579
627
  this._state.actions.updateMarkerHover(null, null, null)
580
628
  this.markerState.clearAll(this.chart.getMarkerManager())
@@ -608,7 +656,7 @@ export class InteractionController {
608
656
  const pane = this.getPaneByY(mouseY)
609
657
  this._state.actions.setRightAxisHover(pane?.id || null)
610
658
  this._state.actions.setSeparatorHover(null)
611
- this._state.actions.updateCrosshair(null, null)
659
+ this._state.actions.updateCrosshair(null, null, null)
612
660
  this._state.actions.updateHover(null, pane?.id || null)
613
661
  }
614
662
 
@@ -704,7 +752,7 @@ if (this.tooltipPositionMode === 'adaptive') {
704
752
  const separatorUpperPaneId = this.hitTestPaneSeparator(ctx.mouseY)
705
753
  this._state.actions.setSeparatorHover(separatorUpperPaneId)
706
754
  if (separatorUpperPaneId) {
707
- this._state.actions.updateCrosshair(null, null)
755
+ this._state.actions.updateCrosshair(null, null, null)
708
756
  this._state.actions.updateHover(null, null)
709
757
  return true
710
758
  }
@@ -721,7 +769,7 @@ if (this.tooltipPositionMode === 'adaptive') {
721
769
  const result = this.markerState.updateHoverFromPoint(ctx.worldX, ctx.mouseX, ctx.mouseY, markerManager)
722
770
  this._state.actions.updateMarkerHover(result.hitMarkerId, result.hitMarkerData, result.hitCustomMarker)
723
771
  if (result.hit) {
724
- this._state.actions.updateCrosshair(null, null)
772
+ this._state.actions.updateCrosshair(null, null, null)
725
773
  this._state.actions.setHoveredIndex(null)
726
774
  return true
727
775
  }
@@ -735,8 +783,8 @@ if (this.tooltipPositionMode === 'adaptive') {
735
783
  * 若无 kLinePositions、visibleRange 或 kWidthPx,返回 null。
736
784
  */
737
785
  private findNearestBar(ctx: HoverContext): NearestBar | null {
738
- const kLinePositions = this._state.readonly.kLinePositions.peek()
739
- const kWidthPx = this._state.readonly.kWidthPx.peek()
786
+ const kLinePositions = this.framePositions
787
+ const kWidthPx = this.frameKWidthPx
740
788
  const vp = this.chart.viewport.peek()
741
789
  const visibleRange = vp ? { start: vp.visibleFrom, end: vp.visibleTo } : null
742
790
  if (!kLinePositions || !visibleRange || !kWidthPx) return null
@@ -799,16 +847,16 @@ if (this.tooltipPositionMode === 'adaptive') {
799
847
  * 索引无效时清除十字线状态。
800
848
  */
801
849
  private positionCrosshair(ctx: HoverContext, bar: NearestBar): void {
802
- const { mouseX, mouseY, scrollLeft, plotWidth, plotHeight, dpr } = ctx
850
+ const { mouseY, scrollLeft, plotWidth, plotHeight } = ctx
803
851
  const pane = this.getPaneByY(mouseY)
804
852
  this._state.actions.setActivePaneId(pane?.id || null)
805
853
 
806
854
  if (bar.globalIdx < 0 || bar.globalIdx >= this.chart.getInternalData().length) {
807
- this._state.actions.updateCrosshair(null, null)
855
+ this._state.actions.updateCrosshair(null, null, null)
808
856
  return
809
857
  }
810
858
 
811
- const kLineCenters = this._state.readonly.kLineCenters.peek()
859
+ const kLineCenters = this.frameCenters
812
860
  const centerX = kLineCenters?.[bar.localIdx] ?? bar.kLineStartX + bar.widthLogical / 2
813
861
  const snappedX = centerX - scrollLeft
814
862
 
@@ -819,6 +867,7 @@ if (this.tooltipPositionMode === 'adaptive') {
819
867
  y: Math.min(Math.max(mouseY, 0), plotHeight),
820
868
  },
821
869
  price,
870
+ bar.globalIdx,
822
871
  )
823
872
  }
824
873
 
@@ -938,10 +987,12 @@ if (this.tooltipPositionMode === 'adaptive') {
938
987
  this.clearSeparatorState()
939
988
  this.isTouchSession = false
940
989
  this.pinchTracker.reset()
941
- this._state.actions.updateCrosshair(null, null)
990
+ this._state.actions.updateCrosshair(null, null, null)
942
991
  this._state.actions.updateHover(null, null)
943
992
  this.markerState.reset()
944
- this._state.actions.updateFramePositions(null, null, null)
993
+ this.framePositions = null
994
+ this.frameCenters = null
995
+ this.frameKWidthPx = null
945
996
  this.lastHoverRenderKey = ''
946
997
  }
947
998
 
@@ -123,6 +123,7 @@ describe('ChartDataManager incremental load', () => {
123
123
  expect(hint).not.toBeNull()
124
124
  expect(hint!.style.opacity).toBe('1')
125
125
  expect(hint!.style.left).toBe('800px')
126
+ expect(hint!.style.background).toContain('--klc-color-selection-fill')
126
127
  expect(fetchCount).toBe(2)
127
128
  })
128
129
  })
@@ -1,6 +1,9 @@
1
1
  import type { ChartDom } from '../chartTypes'
2
2
  import { getPhysicalKLineConfig } from '../utils/klineConfig'
3
3
 
4
+ /** 与 theme selectionFill 对齐:light #2D7FF933 / dark #4A9EFF33;fallback 为 light 默认 */
5
+ const INCREMENTAL_LOAD_HINT_BG = 'var(--klc-color-selection-fill, #2D7FF933)'
6
+
4
7
  export interface HintDeps {
5
8
  getOption: () => { kWidth: number; kGap: number }
6
9
  getEffectiveDpr: () => number
@@ -33,6 +36,8 @@ export class IncrementalLoadHint {
33
36
 
34
37
  this._cancelFadeOut(entry)
35
38
  this._updateGeometry(hint, count, leftBufferWidth)
39
+ // 每次 show 刷新,跟随 light/dark / color preset
40
+ hint.style.background = INCREMENTAL_LOAD_HINT_BG
36
41
 
37
42
  if (!hint.isConnected) {
38
43
  host.appendChild(hint)
@@ -74,7 +79,7 @@ export class IncrementalLoadHint {
74
79
  hint.style.opacity = '0'
75
80
  hint.style.filter = 'blur(10px)'
76
81
  hint.style.transition = 'opacity 420ms ease, filter 420ms ease'
77
- hint.style.background = 'rgba(71, 91, 132, 0.5)'
82
+ hint.style.background = INCREMENTAL_LOAD_HINT_BG
78
83
  hint.style.zIndex = '3'
79
84
  hint.style.willChange = 'opacity, filter, width'
80
85
  return { el: hint, timer: 0, removeTimer: 0, onTransitionEnd: null }
@@ -4,150 +4,168 @@ import type { DrawingObject, DrawingStyle } from '../../foundation/plugin/index'
4
4
  const PREVIEW_ID = '__preview__'
5
5
 
6
6
  /**
7
- * 交互会话层 CRUD。本地 drawings 数组是工作副本;
8
- * 持久业务 SSOT 是 kernel.drawing,经 DrawingChartAdapter.setDrawings 同步。
9
- * 禁止直接写 DrawingStore。
10
- *
11
- * 所有变更操作会自动同步到 DrawingChartAdapter(触发渲染)。
12
- * 不处理事件、不处理命中检测、不处理拖拽逻辑,只维护数据一致性。
7
+ * 交互会话层:只持预览与拖拽覆盖,不持完整图元列表。
8
+ * 已确认图元唯一 SSOT 是 kernel.drawing,经 adapter 读写。
13
9
  */
14
10
  export class DrawingState {
15
- private drawings: DrawingObject[] = []
16
- private selectedDrawingId: string | null = null
11
+ private preview: DrawingObject | null = null
12
+ private dragOverride: DrawingObject | null = null
17
13
 
18
14
  constructor(private adapter: DrawingChartAdapter) {}
19
15
 
20
16
  // ---- Read ----
21
17
 
22
- /** 返回全部图元(含预览) */
18
+ /** kernel 已确认图元(不含预览) */
19
+ private committed(): DrawingObject[] {
20
+ return this.adapter.getFullDrawings().filter((d) => d.id !== PREVIEW_ID)
21
+ }
22
+
23
+ /** 渲染用:拖拽覆盖 + 预览(顺序:override 先,preview 后) */
24
+ getPaintOverlay(): DrawingObject[] {
25
+ const out: DrawingObject[] = []
26
+ if (this.dragOverride) out.push(this.dragOverride)
27
+ if (this.preview) out.push(this.preview)
28
+ return out
29
+ }
30
+
31
+ /** 已确认 ⊕ 会话覆盖(UI / getDrawings) */
23
32
  getAll(): DrawingObject[] {
24
- return this.drawings
33
+ return mergePaint(this.committed(), this.getPaintOverlay())
25
34
  }
26
35
 
27
- /** 返回非预览图元(用于命中检测) */
36
+ /** 命中检测用:已确认 + 拖拽覆盖,不含预览 */
28
37
  getNonPreview(): DrawingObject[] {
29
- return this.drawings.filter((d) => d.id !== PREVIEW_ID)
38
+ return mergePaint(this.committed(), this.dragOverride ? [this.dragOverride] : [])
30
39
  }
31
40
 
32
- /** 按 ID 查找图元 */
33
41
  getById(id: string): DrawingObject | undefined {
34
- return this.drawings.find((d) => d.id === id)
42
+ if (this.dragOverride?.id === id) return this.dragOverride
43
+ if (this.preview?.id === id) return this.preview
44
+ return this.committed().find((d) => d.id === id)
35
45
  }
36
46
 
37
- /** 是否有预览图元 */
38
47
  hasPreview(): boolean {
39
- return this.drawings.some((d) => d.id === PREVIEW_ID)
48
+ return this.preview !== null
40
49
  }
41
50
 
42
- /** 返回当前选中图元 */
43
51
  getSelected(): DrawingObject | null {
44
- if (!this.selectedDrawingId) return null
45
- return this.drawings.find((d) => d.id === this.selectedDrawingId) ?? null
52
+ const id = this.getSelectedId()
53
+ if (!id) return null
54
+ return this.getById(id) ?? null
46
55
  }
47
56
 
48
- /** 返回当前选中图元的 ID */
49
57
  getSelectedId(): string | null {
50
- return this.selectedDrawingId
58
+ return this.adapter.getSelectedDrawingId()
51
59
  }
52
60
 
53
- // ---- Write ----
54
- // 所有 write 方法都会调用 adapter.setDrawings() 触发渲染
61
+ // ---- Session (no kernel write) ----
55
62
 
56
- /**
57
- * 工作副本必须可变:kernel deepFreezeSnapshot 会冻结数组与元素。
58
- * 浅 slice 仍会让 anchors/style 不可写,故做一层 plain 深拷贝。
59
- */
60
- private adoptWorkCopy(drawings: ReadonlyArray<DrawingObject>): DrawingObject[] {
61
- return drawings.map((d) => ({
62
- ...d,
63
- anchors: d.anchors.map((a) => ({ ...a })),
64
- params: { ...d.params },
65
- style: { ...d.style },
66
- }))
63
+ setPreview(preview: DrawingObject): void {
64
+ this.preview = preview
65
+ this.adapter.requestDraw?.()
67
66
  }
68
67
 
69
- /** 整体替换图元列表;选中 id 不在列表时同步清本地与 kernel */
68
+ removePreview(): void {
69
+ if (!this.preview) return
70
+ this.preview = null
71
+ this.adapter.requestDraw?.()
72
+ }
73
+
74
+ setDragOverride(drawing: DrawingObject): void {
75
+ this.dragOverride = drawing
76
+ this.adapter.requestDraw?.()
77
+ }
78
+
79
+ clearDragOverride(): void {
80
+ if (!this.dragOverride) return
81
+ this.dragOverride = null
82
+ }
83
+
84
+ /** pointerup:把拖拽结果写入 kernel,清会话覆盖 */
85
+ commitDrag(): void {
86
+ if (!this.dragOverride) return
87
+ const next = mergePaint(this.committed(), [this.dragOverride])
88
+ this.dragOverride = null
89
+ this.adapter.setDrawings(next)
90
+ }
91
+
92
+ // ---- Committed writes (kernel) ----
93
+
70
94
  setDrawings(drawings: DrawingObject[]): void {
71
- this.drawings = this.adoptWorkCopy(drawings)
72
- if (this.selectedDrawingId && !this.drawings.some((d) => d.id === this.selectedDrawingId)) {
73
- this.selectedDrawingId = null
74
- this.adapter.setSelectedDrawingId(null)
75
- }
76
- this.adapter.setDrawings(this.drawings)
95
+ this.preview = null
96
+ this.dragOverride = null
97
+ const committed = drawings.filter((d) => d.id !== PREVIEW_ID)
98
+ this.clearSelectionIfMissing(committed)
99
+ this.adapter.setDrawings(committed)
77
100
  }
78
101
 
79
- /** 替换图元列表,若选中项被移除则自动清除选中(含 kernel) */
80
102
  replaceDrawings(drawings: DrawingObject[]): void {
81
- this.drawings = this.adoptWorkCopy(drawings)
82
- if (this.selectedDrawingId && !this.drawings.some((d) => d.id === this.selectedDrawingId)) {
83
- this.selectedDrawingId = null
84
- this.adapter.setSelectedDrawingId(null)
85
- }
86
- this.adapter.setDrawings(this.drawings)
103
+ this.setDrawings(drawings)
87
104
  }
88
105
 
89
- /** 添加或更新单个图元(id 相同则替换) */
90
106
  addOrUpdate(drawing: DrawingObject): void {
91
- const idx = this.drawings.findIndex((d) => d.id === drawing.id)
92
- if (idx >= 0) {
93
- const next = this.drawings.slice()
94
- next[idx] = drawing
95
- this.drawings = next
96
- } else {
97
- this.drawings = [...this.drawings, drawing]
107
+ if (drawing.id === PREVIEW_ID) {
108
+ this.setPreview(drawing)
109
+ return
98
110
  }
99
- this.adapter.setDrawings(this.drawings)
111
+ const next = mergePaint(this.committed(), [drawing])
112
+ this.adapter.setDrawings(next)
100
113
  }
101
114
 
102
- /** 删除图元,若为选中项则清除选中 */
103
115
  removeDrawing(drawingId: string): void {
104
- this.drawings = this.drawings.filter((d) => d.id !== drawingId)
105
- if (this.selectedDrawingId === drawingId) {
106
- this.selectedDrawingId = null
107
- }
108
- this.adapter.setDrawings(this.drawings)
116
+ if (this.dragOverride?.id === drawingId) this.dragOverride = null
117
+ if (this.preview?.id === drawingId) this.preview = null
118
+ const next = this.committed().filter((d) => d.id !== drawingId)
119
+ this.clearSelectionIfMissing(next)
120
+ this.adapter.setDrawings(next)
109
121
  }
110
122
 
111
- /** 更新图元样式(合并到已有样式) */
112
123
  updateDrawingStyle(drawingId: string, style: Partial<DrawingStyle>): void {
113
- this.drawings = this.drawings.map((d) =>
124
+ const next = this.committed().map((d) =>
114
125
  d.id === drawingId ? { ...d, style: { ...d.style, ...style } } : d,
115
126
  )
116
- this.adapter.setDrawings(this.drawings)
127
+ if (this.dragOverride?.id === drawingId) {
128
+ this.dragOverride = { ...this.dragOverride, style: { ...this.dragOverride.style, ...style } }
129
+ }
130
+ this.adapter.setDrawings(next)
117
131
  }
118
132
 
119
- /**
120
- * 设置选中图元。
121
- * 仅在选中 ID 确实变化时才触发 adapter 同步。
122
- * 不触发 onDrawingSelected 回调 —— 由调用方(controller)管理。
123
- */
124
133
  setSelected(drawing: DrawingObject | null): void {
125
134
  const newId = drawing?.id ?? null
126
- if (this.selectedDrawingId === newId) return
127
- this.selectedDrawingId = newId
135
+ if (this.adapter.getSelectedDrawingId() === newId) return
128
136
  this.adapter.setSelectedDrawingId(newId)
129
137
  }
130
138
 
131
- /** 删除预览图元(__preview__) */
132
- removePreview(): void {
133
- if (!this.hasPreview()) return
134
- this.drawings = this.drawings.filter((d) => d.id !== PREVIEW_ID)
135
- this.adapter.setDrawings(this.drawings)
136
- }
137
-
138
- /** 设置预览图元(替换已有的 __preview__) */
139
- setPreview(preview: DrawingObject): void {
140
- this.drawings = [...this.drawings.filter((d) => d.id !== PREVIEW_ID), preview]
141
- this.adapter.setDrawings(this.drawings)
142
- }
143
-
144
- /** 清空所有图元并清除选中 */
145
139
  clear(): void {
146
- this.drawings = []
147
- this.selectedDrawingId = null
140
+ this.preview = null
141
+ this.dragOverride = null
148
142
  this.adapter.setDrawings([])
149
143
  this.adapter.setSelectedDrawingId(null)
150
144
  }
145
+
146
+ private clearSelectionIfMissing(list: DrawingObject[]): void {
147
+ const selected = this.adapter.getSelectedDrawingId()
148
+ if (selected && !list.some((d) => d.id === selected)) {
149
+ this.adapter.setSelectedDrawingId(null)
150
+ }
151
+ }
152
+ }
153
+
154
+ /** 以 id 合并;overlay 覆盖同 id;__preview__ 追加在末尾 */
155
+ export function mergePaint(
156
+ committed: ReadonlyArray<DrawingObject>,
157
+ overlay: ReadonlyArray<DrawingObject>,
158
+ ): DrawingObject[] {
159
+ const byId = new Map<string, DrawingObject>()
160
+ for (const d of committed) {
161
+ if (d.id !== PREVIEW_ID) byId.set(d.id, d)
162
+ }
163
+ const previews: DrawingObject[] = []
164
+ for (const o of overlay) {
165
+ if (o.id === PREVIEW_ID) previews.push(o)
166
+ else byId.set(o.id, o)
167
+ }
168
+ return [...byId.values(), ...previews]
151
169
  }
152
170
 
153
171
  export { PREVIEW_ID }