@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,106 @@
1
+ import type { CompositeOptions, SurfaceBackend, SurfaceRegion } from './SurfaceBackend'
2
+
3
+ export type WebGPUSurfaceBackend = SurfaceBackend & {
4
+ readonly canvas: HTMLCanvasElement
5
+ readonly device: GPUDevice
6
+ readonly format: GPUTextureFormat
7
+ getBoundRegion(): SurfaceRegion | null
8
+ getCurrentTextureView(): GPUTextureView | null
9
+ }
10
+
11
+ export type WebGPUSurfaceBackendOptions = {
12
+ canvas: HTMLCanvasElement
13
+ device: GPUDevice
14
+ format: GPUTextureFormat
15
+ }
16
+
17
+ export function createWebGPUSurfaceBackend(
18
+ options: WebGPUSurfaceBackendOptions,
19
+ ): WebGPUSurfaceBackend {
20
+ const { canvas, device, format } = options
21
+ const context = canvas.getContext('webgpu')
22
+ if (!context) throw new Error('WebGPU canvas context unavailable')
23
+
24
+ const renderAttachmentUsage = globalThis.GPUTextureUsage?.RENDER_ATTACHMENT ?? 0x10
25
+ context.configure({
26
+ device,
27
+ format,
28
+ alphaMode: 'premultiplied',
29
+ usage: renderAttachmentUsage,
30
+ })
31
+
32
+ let disposed = false
33
+ let boundRegion: SurfaceRegion | null = null
34
+
35
+ return {
36
+ canvas,
37
+ device,
38
+ format,
39
+ isAvailable(): boolean {
40
+ return !disposed
41
+ },
42
+ resize(widthLogical: number, heightLogical: number, dpr: number): void {
43
+ if (disposed) return
44
+ const width = Math.max(1, Math.round(widthLogical * dpr))
45
+ const height = Math.max(1, Math.round(heightLogical * dpr))
46
+ if (canvas.width !== width) canvas.width = width
47
+ if (canvas.height !== height) canvas.height = height
48
+ // 可见 DOM 合成:CSS 尺寸跟逻辑 plot,buffer 跟物理像素
49
+ if (canvas.style) {
50
+ canvas.style.width = `${Math.max(0, widthLogical)}px`
51
+ canvas.style.height = `${Math.max(0, heightLogical)}px`
52
+ }
53
+ },
54
+ bindRegion(region: SurfaceRegion): boolean {
55
+ if (disposed || region.width <= 0 || region.height <= 0 || region.dpr <= 0) return false
56
+ boundRegion = { ...region }
57
+ return true
58
+ },
59
+ clearRegion(_region: SurfaceRegion): void {
60
+ if (disposed) return
61
+ // 空数据/清屏:提交透明 clear,避免 hybrid 可见 canvas 残留上一帧
62
+ try {
63
+ const view = context.getCurrentTexture().createView()
64
+ const encoder = device.createCommandEncoder()
65
+ const pass = encoder.beginRenderPass({
66
+ colorAttachments: [
67
+ {
68
+ view,
69
+ clearValue: { r: 0, g: 0, b: 0, a: 0 },
70
+ loadOp: 'clear',
71
+ storeOp: 'store',
72
+ },
73
+ ],
74
+ })
75
+ pass.end()
76
+ device.queue.submit([encoder.finish()])
77
+ } catch {
78
+ // device lost / texture unavailable:忽略,下帧 beginFrame 会重建
79
+ }
80
+ },
81
+ compositeTo(
82
+ _targetCtx: CanvasRenderingContext2D,
83
+ _region: SurfaceRegion,
84
+ _compositeOptions?: CompositeOptions,
85
+ ): void {
86
+ // M2:WebGPU 可见 canvas 直接参与 DOM 分层,禁止 GPU→2D drawImage
87
+ },
88
+ getBoundRegion(): SurfaceRegion | null {
89
+ return boundRegion ? { ...boundRegion } : null
90
+ },
91
+ getCurrentTextureView(): GPUTextureView | null {
92
+ if (disposed) return null
93
+ try {
94
+ return context.getCurrentTexture().createView()
95
+ } catch {
96
+ return null
97
+ }
98
+ },
99
+ dispose(): void {
100
+ if (disposed) return
101
+ disposed = true
102
+ boundRegion = null
103
+ context.unconfigure()
104
+ },
105
+ }
106
+ }
@@ -0,0 +1,54 @@
1
+ export type FrameMetricsSnapshot = {
2
+ bufferCreateCount: number
3
+ bufferUploadBytes: number
4
+ drawCallCount: number
5
+ queueSubmitCount: number
6
+ compositeCount: number
7
+ }
8
+
9
+ function empty(): FrameMetricsSnapshot {
10
+ return {
11
+ bufferCreateCount: 0,
12
+ bufferUploadBytes: 0,
13
+ drawCallCount: 0,
14
+ queueSubmitCount: 0,
15
+ compositeCount: 0,
16
+ }
17
+ }
18
+
19
+ let snapshot: FrameMetricsSnapshot = empty()
20
+
21
+ export function resetFrameMetrics(): void {
22
+ snapshot = empty()
23
+ }
24
+
25
+ export function getFrameMetrics(): FrameMetricsSnapshot {
26
+ return { ...snapshot }
27
+ }
28
+
29
+ export function createFrameMetrics() {
30
+ let current = empty()
31
+ return {
32
+ beginFrame(): void {
33
+ current = empty()
34
+ },
35
+ recordBufferCreate(): void {
36
+ current.bufferCreateCount += 1
37
+ },
38
+ recordUpload(bytes: number): void {
39
+ current.bufferUploadBytes += bytes
40
+ },
41
+ recordDraw(): void {
42
+ current.drawCallCount += 1
43
+ },
44
+ recordSubmit(): void {
45
+ current.queueSubmitCount += 1
46
+ },
47
+ recordComposite(): void {
48
+ current.compositeCount += 1
49
+ },
50
+ endFrame(): void {
51
+ snapshot = { ...current }
52
+ },
53
+ }
54
+ }
@@ -9,6 +9,36 @@ export type { SurfaceBackend, SurfaceRegion, CompositeOptions } from './SurfaceB
9
9
 
10
10
  export { createWebGLSurfaceBackend } from './createWebGLSurfaceBackend'
11
11
  export { createWebGLRenderer } from './createWebGLRenderer'
12
+ export { createWebGPUSurfaceBackend } from './createWebGPUSurfaceBackend'
13
+ export { createWebGPURenderer } from './createWebGPURenderer'
14
+ export { createCanvas2DRenderer } from './createCanvas2DRenderer'
15
+ export { createRendererHost, createRendererHostFromRenderer } from './rendererHost'
16
+ export {
17
+ createDefaultRendererHost,
18
+ createDefaultRendererHostSync,
19
+ } from './createDefaultRendererHost'
20
+
21
+ export type {
22
+ RendererBackend,
23
+ RendererBackendStatus,
24
+ RendererBackendRuntime,
25
+ RendererFactory,
26
+ RendererHostDependencies,
27
+ RendererHostListeners,
28
+ RendererHost,
29
+ } from './rendererHost'
30
+
31
+ export type {
32
+ WebGPUSurfaceBackend,
33
+ WebGPUSurfaceBackendOptions,
34
+ } from './createWebGPUSurfaceBackend'
35
+ export type { CreateWebGPURendererOptions } from './createWebGPURenderer'
36
+ export {
37
+ createFrameMetrics,
38
+ getFrameMetrics,
39
+ resetFrameMetrics,
40
+ } from './frameMetrics'
41
+ export type { FrameMetricsSnapshot } from './frameMetrics'
12
42
 
13
43
  export type {
14
44
  Renderer,
@@ -0,0 +1,210 @@
1
+ import type { Renderer } from './Renderer'
2
+
3
+ export type RendererBackend = 'webgpu' | 'webgl' | 'canvas'
4
+
5
+ export type RendererBackendStatus = 'initializing' | 'ready' | 'switching' | 'degraded' | 'failed'
6
+
7
+ export type RendererBackendRuntime = {
8
+ effective: RendererBackend
9
+ status: RendererBackendStatus
10
+ error: string | null
11
+ }
12
+
13
+ export type RendererFactory = () => Renderer | Promise<Renderer>
14
+
15
+ export type RendererHostDependencies = {
16
+ createWebGPU: RendererFactory
17
+ createWebGL: RendererFactory
18
+ createCanvas: RendererFactory
19
+ onRuntimeChange?: (runtime: Readonly<RendererBackendRuntime>) => void
20
+ requestRedraw?: () => void
21
+ }
22
+
23
+ export type RendererHostListeners = Pick<
24
+ RendererHostDependencies,
25
+ 'onRuntimeChange' | 'requestRedraw'
26
+ >
27
+
28
+ export interface RendererHost {
29
+ readonly renderer: Renderer
30
+ readonly runtime: Readonly<RendererBackendRuntime>
31
+ resize(widthLogical: number, heightLogical: number, dpr: number): void
32
+ switchTo(preference: RendererBackend): Promise<void>
33
+ handleDeviceLost(error: string): Promise<void>
34
+ setListeners(listeners: RendererHostListeners): void
35
+ dispose(): void
36
+ }
37
+
38
+ export type PreparedRenderer = {
39
+ renderer: Renderer
40
+ effective: RendererBackend
41
+ error: string | null
42
+ }
43
+
44
+ function errorMessage(error: unknown): string {
45
+ return error instanceof Error ? error.message : String(error)
46
+ }
47
+
48
+ async function prepareRenderer(
49
+ preference: RendererBackend,
50
+ deps: RendererHostDependencies,
51
+ ): Promise<PreparedRenderer> {
52
+ const attempts: Array<[RendererBackend, RendererFactory]> =
53
+ preference === 'webgpu'
54
+ ? [
55
+ ['webgpu', deps.createWebGPU],
56
+ ['webgl', deps.createWebGL],
57
+ ['canvas', deps.createCanvas],
58
+ ]
59
+ : preference === 'webgl'
60
+ ? [
61
+ ['webgl', deps.createWebGL],
62
+ ['canvas', deps.createCanvas],
63
+ ]
64
+ : [['canvas', deps.createCanvas]]
65
+ let firstError: string | null = null
66
+
67
+ for (const [effective, factory] of attempts) {
68
+ try {
69
+ return {
70
+ renderer: await factory(),
71
+ effective,
72
+ error: firstError,
73
+ }
74
+ } catch (error) {
75
+ firstError ??= errorMessage(error)
76
+ }
77
+ }
78
+
79
+ throw new Error(firstError ?? `Unable to create ${preference} renderer`)
80
+ }
81
+
82
+ export async function createRendererHost(
83
+ preference: RendererBackend,
84
+ deps: RendererHostDependencies,
85
+ ): Promise<RendererHost> {
86
+ const initial = await prepareRenderer(preference, deps)
87
+ return createRendererHostFromRenderer(preference, initial, deps)
88
+ }
89
+
90
+ export function createRendererHostFromRenderer(
91
+ preference: RendererBackend,
92
+ initial: PreparedRenderer,
93
+ deps: RendererHostDependencies,
94
+ ): RendererHost {
95
+ let activeRenderer = initial.renderer
96
+ let runtime: RendererBackendRuntime = {
97
+ effective: initial.effective,
98
+ status: initial.effective === preference ? 'ready' : 'degraded',
99
+ error: initial.error,
100
+ }
101
+ let generation = 0
102
+ let disposed = false
103
+ let surfaceSize: { widthLogical: number; heightLogical: number; dpr: number } | null = null
104
+ let listeners: RendererHostListeners = {
105
+ onRuntimeChange: deps.onRuntimeChange,
106
+ requestRedraw: deps.requestRedraw,
107
+ }
108
+
109
+ function publish(next: RendererBackendRuntime): void {
110
+ runtime = next
111
+ listeners.onRuntimeChange?.(runtime)
112
+ }
113
+
114
+ const host: RendererHost = {
115
+ get renderer() {
116
+ return activeRenderer
117
+ },
118
+ get runtime() {
119
+ return runtime
120
+ },
121
+ resize(widthLogical, heightLogical, dpr): void {
122
+ if (disposed) return
123
+ surfaceSize = { widthLogical, heightLogical, dpr }
124
+ activeRenderer.surface.resize(widthLogical, heightLogical, dpr)
125
+ },
126
+ async switchTo(nextPreference): Promise<void> {
127
+ if (disposed) return
128
+ const requestGeneration = ++generation
129
+ publish({ ...runtime, status: 'switching', error: null })
130
+
131
+ let prepared: PreparedRenderer
132
+ try {
133
+ prepared = await prepareRenderer(nextPreference, deps)
134
+ } catch (error) {
135
+ if (disposed || requestGeneration !== generation) return
136
+ publish({ ...runtime, status: 'degraded', error: errorMessage(error) })
137
+ return
138
+ }
139
+
140
+ if (disposed || requestGeneration !== generation) {
141
+ prepared.renderer.dispose()
142
+ return
143
+ }
144
+
145
+ if (surfaceSize) {
146
+ prepared.renderer.surface.resize(
147
+ surfaceSize.widthLogical,
148
+ surfaceSize.heightLogical,
149
+ surfaceSize.dpr,
150
+ )
151
+ }
152
+
153
+ const previous = activeRenderer
154
+ activeRenderer = prepared.renderer
155
+ publish({
156
+ effective: prepared.effective,
157
+ status: prepared.effective === nextPreference ? 'ready' : 'degraded',
158
+ error: prepared.error,
159
+ })
160
+ listeners.requestRedraw?.()
161
+ if (previous !== activeRenderer) previous.dispose()
162
+ },
163
+ async handleDeviceLost(error): Promise<void> {
164
+ if (disposed || runtime.effective !== 'webgpu') return
165
+ const requestGeneration = ++generation
166
+ publish({ ...runtime, status: 'degraded', error })
167
+
168
+ let prepared: PreparedRenderer
169
+ try {
170
+ prepared = await prepareRenderer('webgl', deps)
171
+ } catch (fallbackError) {
172
+ if (disposed || requestGeneration !== generation) return
173
+ publish({ ...runtime, status: 'failed', error: errorMessage(fallbackError) })
174
+ return
175
+ }
176
+
177
+ if (disposed || requestGeneration !== generation) {
178
+ prepared.renderer.dispose()
179
+ return
180
+ }
181
+
182
+ if (surfaceSize) {
183
+ prepared.renderer.surface.resize(
184
+ surfaceSize.widthLogical,
185
+ surfaceSize.heightLogical,
186
+ surfaceSize.dpr,
187
+ )
188
+ }
189
+
190
+ const previous = activeRenderer
191
+ activeRenderer = prepared.renderer
192
+ publish({ effective: prepared.effective, status: 'degraded', error })
193
+ listeners.requestRedraw?.()
194
+ if (previous !== activeRenderer) previous.dispose()
195
+ },
196
+ setListeners(nextListeners): void {
197
+ listeners = { ...nextListeners }
198
+ listeners.onRuntimeChange?.(runtime)
199
+ },
200
+ dispose(): void {
201
+ if (disposed) return
202
+ disposed = true
203
+ generation += 1
204
+ activeRenderer.dispose()
205
+ },
206
+ }
207
+
208
+ listeners.onRuntimeChange?.(runtime)
209
+ return host
210
+ }
@@ -0,0 +1,87 @@
1
+ import type { createFrameMetrics } from './frameMetrics'
2
+
3
+ type Metrics = ReturnType<typeof createFrameMetrics>
4
+
5
+ export type ResourceUsage = 'vertex' | 'instance' | 'uniform'
6
+
7
+ export type EnsureUploadedParams = {
8
+ key: string
9
+ revision: number
10
+ data: Float32Array
11
+ usage: ResourceUsage
12
+ }
13
+
14
+ export type GpuResourceHandle = {
15
+ buffer: GPUBuffer
16
+ capacity: number
17
+ lastRevision: number
18
+ }
19
+
20
+ export type WebGPUResourceTable = {
21
+ ensureUploaded(params: EnsureUploadedParams): GpuResourceHandle
22
+ destroyKey(key: string): void
23
+ destroyAll(): void
24
+ }
25
+
26
+ const GPU_BUFFER_COPY_DST = globalThis.GPUBufferUsage?.COPY_DST ?? 0x0008
27
+ const GPU_BUFFER_VERTEX = globalThis.GPUBufferUsage?.VERTEX ?? 0x0020
28
+ const GPU_BUFFER_UNIFORM = globalThis.GPUBufferUsage?.UNIFORM ?? 0x0040
29
+
30
+ function gpuUsage(usage: ResourceUsage): GPUBufferUsageFlags {
31
+ if (usage === 'uniform') return GPU_BUFFER_UNIFORM | GPU_BUFFER_COPY_DST
32
+ return GPU_BUFFER_VERTEX | GPU_BUFFER_COPY_DST
33
+ }
34
+
35
+ function growCapacity(needed: number, existing: number): number {
36
+ let capacity = Math.max(4, existing || 4)
37
+ while (capacity < needed) capacity = Math.ceil(capacity * 1.5)
38
+ return Math.ceil(capacity / 4) * 4
39
+ }
40
+
41
+ export function createWebGPUResourceTable(options: {
42
+ device: GPUDevice
43
+ metrics?: Metrics
44
+ }): WebGPUResourceTable {
45
+ const { device, metrics } = options
46
+ const resources = new Map<string, GpuResourceHandle>()
47
+
48
+ return {
49
+ ensureUploaded({ key, revision, data, usage }): GpuResourceHandle {
50
+ const byteLength = data.byteLength
51
+ let resource = resources.get(key)
52
+ if (!resource || resource.capacity < byteLength) {
53
+ resource?.buffer.destroy()
54
+ const capacity = growCapacity(byteLength, resource?.capacity ?? 0)
55
+ const buffer = device.createBuffer({
56
+ size: capacity,
57
+ usage: gpuUsage(usage),
58
+ })
59
+ metrics?.recordBufferCreate()
60
+ resource = { buffer, capacity, lastRevision: -1 }
61
+ resources.set(key, resource)
62
+ }
63
+ if (resource.lastRevision !== revision) {
64
+ device.queue.writeBuffer(
65
+ resource.buffer,
66
+ 0,
67
+ data.buffer as ArrayBuffer,
68
+ data.byteOffset,
69
+ data.byteLength,
70
+ )
71
+ metrics?.recordUpload(byteLength)
72
+ resource.lastRevision = revision
73
+ }
74
+ return resource
75
+ },
76
+ destroyKey(key): void {
77
+ const resource = resources.get(key)
78
+ if (!resource) return
79
+ resource.buffer.destroy()
80
+ resources.delete(key)
81
+ },
82
+ destroyAll(): void {
83
+ for (const resource of resources.values()) resource.buffer.destroy()
84
+ resources.clear()
85
+ },
86
+ }
87
+ }
@@ -70,6 +70,33 @@ describe('createLayerFromPlugin', () => {
70
70
  expect(plugin.draw).toHaveBeenCalledWith(context)
71
71
  })
72
72
 
73
+ it('injects PaintContext.renderer as RenderContext.sceneRenderer before draw', () => {
74
+ const plugin = makeMockPlugin({
75
+ draw: vi.fn((c: RenderContext) => {
76
+ expect(c.sceneRenderer).toBe(stubPaintCtx.renderer)
77
+ }),
78
+ })
79
+ const context = makeMockContext()
80
+ const layer = createLayerFromPlugin(plugin, () => context, 'main')
81
+ layer.paint(stubPaintCtx)
82
+ expect(plugin.draw).toHaveBeenCalledOnce()
83
+ })
84
+
85
+ it('isolates plugin.draw errors so paint does not throw', () => {
86
+ const plugin = makeMockPlugin({
87
+ draw: vi.fn(() => {
88
+ throw new Error('draw boom')
89
+ }),
90
+ })
91
+ const context = makeMockContext()
92
+ const layer = createLayerFromPlugin(plugin, () => context, 'main')
93
+ const err = vi.spyOn(console, 'error').mockImplementation(() => {})
94
+
95
+ expect(() => layer.paint(stubPaintCtx)).not.toThrow()
96
+ expect(plugin.draw).toHaveBeenCalledOnce()
97
+ err.mockRestore()
98
+ })
99
+
73
100
  it('skips plugin.draw when getContext returns null', () => {
74
101
  const plugin = makeMockPlugin()
75
102
  const getContext = vi.fn(() => null)
@@ -93,7 +120,7 @@ describe('createLayerFromPlugin', () => {
93
120
  expect(plugin.draw).not.toHaveBeenCalled()
94
121
  })
95
122
 
96
- it('calls plugin.onUninstall on dispose', () => {
123
+ it('does not call plugin.onUninstall on dispose (owned by removeRenderer)', () => {
97
124
  const onUninstall = vi.fn()
98
125
  const plugin = makeMockPlugin({ onUninstall })
99
126
  const getContext = vi.fn(() => makeMockContext())
@@ -101,7 +128,7 @@ describe('createLayerFromPlugin', () => {
101
128
 
102
129
  layer.dispose()
103
130
 
104
- expect(onUninstall).toHaveBeenCalledOnce()
131
+ expect(onUninstall).not.toHaveBeenCalled()
105
132
  })
106
133
 
107
134
  it('does not throw when dispose is called without onUninstall', () => {
@@ -0,0 +1,59 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { createRetainedScene } from '../retainedScene'
4
+
5
+ describe('createRetainedScene', () => {
6
+ it('upserts by key and replaces geometry when revision changes', () => {
7
+ const scene = createRetainedScene()
8
+ scene.beginFrame(1)
9
+ scene.upsert({
10
+ kind: 'rects',
11
+ key: 'main/candle/upBody',
12
+ revision: 1,
13
+ instances: new Float32Array([0, 0, 1, 2]),
14
+ count: 1,
15
+ color: '#0f0',
16
+ scrollLeft: 0,
17
+ z: 10,
18
+ paneId: 'main',
19
+ })
20
+ scene.upsert({
21
+ kind: 'rects',
22
+ key: 'main/candle/upBody',
23
+ revision: 2,
24
+ instances: new Float32Array([1, 1, 1, 2]),
25
+ count: 1,
26
+ color: '#0f0',
27
+ scrollLeft: 5,
28
+ z: 10,
29
+ paneId: 'main',
30
+ })
31
+ const nodes = scene.collectVisible('main')
32
+ expect(nodes).toHaveLength(1)
33
+ expect(nodes[0]?.revision).toBe(2)
34
+ expect(nodes[0]?.scrollLeft).toBe(5)
35
+ })
36
+
37
+ it('prunes keys not touched for N frames', () => {
38
+ const scene = createRetainedScene({ staleFrames: 2 })
39
+ scene.beginFrame(1)
40
+ scene.upsert({
41
+ kind: 'rects',
42
+ key: 'a',
43
+ revision: 1,
44
+ instances: new Float32Array(4),
45
+ count: 1,
46
+ color: '#fff',
47
+ scrollLeft: 0,
48
+ z: 0,
49
+ paneId: 'main',
50
+ })
51
+ scene.endFrame()
52
+ scene.beginFrame(2)
53
+ scene.endFrame()
54
+ scene.beginFrame(3)
55
+ const removed = scene.prune()
56
+ expect(removed).toEqual(['a'])
57
+ expect(scene.collectVisible('main')).toEqual([])
58
+ })
59
+ })
@@ -29,10 +29,17 @@ export function createLayerFromPlugin(
29
29
  if (paneRole === 'sub' && ctx.paneId !== targetPaneId) return
30
30
  const context = getContext()
31
31
  if (!context) return
32
- plugin.draw(context)
32
+ // 注入本帧 Scene Renderer,供已迁路径(candle 等)走统一画笔
33
+ context.sceneRenderer = ctx.renderer
34
+ try {
35
+ plugin.draw(context)
36
+ } catch (e) {
37
+ // 隔离单层异常,避免中断同 pane 后续 Layer(对齐旧 Manager.render)
38
+ console.error(`[RendererPlugin] ${plugin.name} draw error:`, e)
39
+ }
33
40
  },
34
41
  dispose() {
35
- plugin.onUninstall?.()
42
+ // onUninstall 由 removeRenderer / Manager.unregister 单点负责,避免双调
36
43
  },
37
44
  }
38
45
  }
@@ -15,11 +15,9 @@ import { KLineChartError } from '../../errors'
15
15
  * avoids forcing every consumer through a single "register + auto-add"
16
16
  * pipeline that would be hard to compose with config-driven scenes.
17
17
  *
18
- * This PR does NOT pre-register any factories that's a follow-up PR
19
- * once the legacy WebGL renderers have adapters that satisfy the `Layer`
20
- * interface. This file ships the registry mechanism plus the canonical
21
- * `typeId` constants so the rest of the codebase can reference layer types
22
- * by symbol rather than magic string.
18
+ * Factories are registered by chart wiring / plugins; this file ships the
19
+ * registry mechanism plus the canonical `typeId` constants so the rest of
20
+ * the codebase can reference layer types by symbol rather than magic string.
23
21
  */
24
22
 
25
23
  import type { Layer, LayerRole } from './types'
@@ -98,7 +96,7 @@ export function createLayerRegistry(): LayerRegistry {
98
96
  * string literal types without an enum import — useful for config blobs.
99
97
  */
100
98
  export const BUILTIN_LAYER_TYPES = {
101
- // Existing in v0 (handled by legacy renderers)
99
+ // Built-in chart layers
102
100
  CANDLE: 'builtin:candle',
103
101
  VOLUME: 'builtin:volume',
104
102
  INDICATOR_MA: 'builtin:indicator:ma',