@363045841yyt/klinechart-core 0.7.3 → 0.7.5

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 (247) hide show
  1. package/README.md +201 -201
  2. package/README.zh-CN.md +201 -201
  3. package/dist/controllers/index.d.ts +1 -0
  4. package/dist/controllers/index.d.ts.map +1 -1
  5. package/dist/controllers/index.js +1 -0
  6. package/dist/controllers/index.js.map +1 -1
  7. package/dist/engine/chart.d.ts +11 -19
  8. package/dist/engine/chart.d.ts.map +1 -1
  9. package/dist/engine/chart.js +92 -109
  10. package/dist/engine/chart.js.map +1 -1
  11. package/dist/engine/renderers/Indicator/indicatorData.d.ts +1 -0
  12. package/dist/engine/renderers/Indicator/indicatorData.d.ts.map +1 -1
  13. package/dist/engine/renderers/Indicator/indicatorData.js +1 -1
  14. package/dist/engine/renderers/Indicator/indicatorData.js.map +1 -1
  15. package/dist/engine/renderers/webgl/candleSurface.js +47 -47
  16. package/dist/engine/subPaneManager.d.ts +4 -0
  17. package/dist/engine/subPaneManager.d.ts.map +1 -1
  18. package/dist/engine/subPaneManager.js +13 -0
  19. package/dist/engine/subPaneManager.js.map +1 -1
  20. package/dist/version.d.ts +1 -1
  21. package/dist/version.d.ts.map +1 -1
  22. package/dist/version.js +1 -2
  23. package/dist/version.js.map +1 -1
  24. package/package.json +129 -122
  25. package/src/__tests__/signal.test.ts +124 -124
  26. package/src/config/chartSettings.ts +66 -66
  27. package/src/controllers/__tests__/drawing.test.ts +214 -214
  28. package/src/controllers/__tests__/indicatorSelector.test.ts +481 -481
  29. package/src/controllers/__tests__/toolbar.test.ts +225 -225
  30. package/src/controllers/createChartController.ts +665 -665
  31. package/src/controllers/createDrawingController.ts +96 -96
  32. package/src/controllers/createIndicatorSelectorController.ts +307 -307
  33. package/src/controllers/createToolbarController.ts +146 -146
  34. package/src/controllers/index.ts +20 -19
  35. package/src/controllers/types.ts +284 -284
  36. package/src/engine/__tests__/chart.dpr.test.ts +401 -401
  37. package/src/engine/__tests__/paneRenderer.resize.test.ts +92 -92
  38. package/src/engine/chart-store.ts +121 -121
  39. package/src/engine/chart.d.ts +617 -617
  40. package/src/engine/chart.ts +2803 -2815
  41. package/src/engine/controller/__tests__/interaction.dpr.test.ts +259 -259
  42. package/src/engine/controller/interaction.ts +722 -722
  43. package/src/engine/controller/markerInteraction.ts +130 -130
  44. package/src/engine/controller/pinchTracker.ts +82 -82
  45. package/src/engine/controller/tooltipPosition.ts +48 -48
  46. package/src/engine/draw/__tests__/pixelAlign.spec.ts +176 -176
  47. package/src/engine/draw/pixelAlign.ts +259 -259
  48. package/src/engine/drawing/index.ts +655 -655
  49. package/src/engine/drawing/interaction.ts +842 -842
  50. package/src/engine/drawing/plugin.ts +343 -343
  51. package/src/engine/indicators/__tests__/__fixtures__/golden/atr.json +38 -38
  52. package/src/engine/indicators/__tests__/__fixtures__/golden/dema.json +14 -14
  53. package/src/engine/indicators/__tests__/__fixtures__/golden/hma.json +14 -14
  54. package/src/engine/indicators/__tests__/__fixtures__/golden/index.ts +55 -55
  55. package/src/engine/indicators/__tests__/__fixtures__/golden/kama.json +14 -14
  56. package/src/engine/indicators/__tests__/__fixtures__/golden/tema.json +14 -14
  57. package/src/engine/indicators/__tests__/__fixtures__/golden/wma.json +40 -40
  58. package/src/engine/indicators/__tests__/__fixtures__/synthetic.ts +65 -65
  59. package/src/engine/indicators/__tests__/_propertyAssertions.ts +76 -76
  60. package/src/engine/indicators/__tests__/atr.test.ts +153 -153
  61. package/src/engine/indicators/__tests__/calculators.test.ts +614 -614
  62. package/src/engine/indicators/__tests__/cmf-mfi.test.ts +100 -100
  63. package/src/engine/indicators/__tests__/dema.test.ts +73 -73
  64. package/src/engine/indicators/__tests__/donchian.test.ts +70 -70
  65. package/src/engine/indicators/__tests__/hma.test.ts +73 -73
  66. package/src/engine/indicators/__tests__/ichimoku.test.ts +105 -105
  67. package/src/engine/indicators/__tests__/kama.test.ts +80 -80
  68. package/src/engine/indicators/__tests__/keltner.test.ts +65 -65
  69. package/src/engine/indicators/__tests__/pivot-fib.test.ts +110 -110
  70. package/src/engine/indicators/__tests__/roc.test.ts +68 -68
  71. package/src/engine/indicators/__tests__/sar.test.ts +86 -86
  72. package/src/engine/indicators/__tests__/scheduler.test.ts +831 -831
  73. package/src/engine/indicators/__tests__/soa.test.ts +533 -533
  74. package/src/engine/indicators/__tests__/structure.test.ts +110 -110
  75. package/src/engine/indicators/__tests__/supertrend.test.ts +65 -65
  76. package/src/engine/indicators/__tests__/tema.test.ts +68 -68
  77. package/src/engine/indicators/__tests__/trix.test.ts +70 -70
  78. package/src/engine/indicators/__tests__/volatility.test.ts +117 -117
  79. package/src/engine/indicators/__tests__/volume.test.ts +115 -115
  80. package/src/engine/indicators/__tests__/volumeProfile.test.ts +74 -74
  81. package/src/engine/indicators/__tests__/vwap.test.ts +69 -69
  82. package/src/engine/indicators/__tests__/wma.test.ts +112 -112
  83. package/src/engine/indicators/__tests__/zones.test.ts +95 -95
  84. package/src/engine/indicators/atrState.ts +27 -27
  85. package/src/engine/indicators/bollState.ts +51 -51
  86. package/src/engine/indicators/calculators.ts +2593 -2593
  87. package/src/engine/indicators/cciState.ts +25 -25
  88. package/src/engine/indicators/chaikinVolState.ts +32 -32
  89. package/src/engine/indicators/cmfState.ts +27 -27
  90. package/src/engine/indicators/demaState.ts +27 -27
  91. package/src/engine/indicators/donchianState.ts +43 -43
  92. package/src/engine/indicators/eneState.ts +43 -43
  93. package/src/engine/indicators/expmaState.ts +43 -43
  94. package/src/engine/indicators/fastkState.ts +25 -25
  95. package/src/engine/indicators/fibState.ts +41 -41
  96. package/src/engine/indicators/hmaState.ts +27 -27
  97. package/src/engine/indicators/hvState.ts +28 -28
  98. package/src/engine/indicators/ichimokuState.ts +70 -70
  99. package/src/engine/indicators/indicator.worker.ts +169 -169
  100. package/src/engine/indicators/indicatorDefinitionRegistry.ts +62 -62
  101. package/src/engine/indicators/indicatorMetadata.ts +110 -110
  102. package/src/engine/indicators/indicatorRegistry.ts +106 -106
  103. package/src/engine/indicators/indicatorRuntime.ts +1548 -1548
  104. package/src/engine/indicators/kamaState.ts +34 -34
  105. package/src/engine/indicators/keltnerState.ts +49 -49
  106. package/src/engine/indicators/kstState.ts +42 -42
  107. package/src/engine/indicators/maState.ts +36 -36
  108. package/src/engine/indicators/macdState.ts +76 -76
  109. package/src/engine/indicators/mfiState.ts +27 -27
  110. package/src/engine/indicators/momState.ts +25 -25
  111. package/src/engine/indicators/obvState.ts +25 -25
  112. package/src/engine/indicators/parkinsonState.ts +28 -28
  113. package/src/engine/indicators/pivotState.ts +51 -51
  114. package/src/engine/indicators/pvtState.ts +25 -25
  115. package/src/engine/indicators/rocState.ts +27 -27
  116. package/src/engine/indicators/rsiState.ts +65 -65
  117. package/src/engine/indicators/sarState.ts +41 -41
  118. package/src/engine/indicators/scheduler.ts +1205 -1205
  119. package/src/engine/indicators/soa.ts +352 -352
  120. package/src/engine/indicators/stateComposer.ts +1262 -1262
  121. package/src/engine/indicators/stochState.ts +26 -26
  122. package/src/engine/indicators/structureState.ts +69 -69
  123. package/src/engine/indicators/supertrendState.ts +37 -37
  124. package/src/engine/indicators/temaState.ts +27 -27
  125. package/src/engine/indicators/trixState.ts +35 -35
  126. package/src/engine/indicators/vmaState.ts +27 -27
  127. package/src/engine/indicators/volumeProfileState.ts +63 -63
  128. package/src/engine/indicators/vwapState.ts +29 -29
  129. package/src/engine/indicators/wmaState.ts +27 -27
  130. package/src/engine/indicators/wmsrState.ts +25 -25
  131. package/src/engine/indicators/workerProtocol.ts +613 -613
  132. package/src/engine/indicators/zonesState.ts +47 -47
  133. package/src/engine/layout/pane.ts +161 -161
  134. package/src/engine/marker/registry.ts +265 -265
  135. package/src/engine/paneRenderer.ts +169 -169
  136. package/src/engine/renderers/Indicator/atr.ts +237 -237
  137. package/src/engine/renderers/Indicator/boll.ts +317 -317
  138. package/src/engine/renderers/Indicator/cci.ts +275 -275
  139. package/src/engine/renderers/Indicator/chaikinVol.ts +138 -138
  140. package/src/engine/renderers/Indicator/cmf.ts +137 -137
  141. package/src/engine/renderers/Indicator/dema.ts +136 -136
  142. package/src/engine/renderers/Indicator/donchian.ts +137 -137
  143. package/src/engine/renderers/Indicator/ene.ts +271 -271
  144. package/src/engine/renderers/Indicator/expma.ts +197 -197
  145. package/src/engine/renderers/Indicator/fastk.ts +316 -316
  146. package/src/engine/renderers/Indicator/fib.ts +141 -141
  147. package/src/engine/renderers/Indicator/hma.ts +136 -136
  148. package/src/engine/renderers/Indicator/hv.ts +124 -124
  149. package/src/engine/renderers/Indicator/ichimoku.ts +181 -181
  150. package/src/engine/renderers/Indicator/index.ts +241 -241
  151. package/src/engine/renderers/Indicator/indicatorData.ts +650 -650
  152. package/src/engine/renderers/Indicator/kama.ts +136 -136
  153. package/src/engine/renderers/Indicator/keltner.ts +137 -137
  154. package/src/engine/renderers/Indicator/kst.ts +302 -302
  155. package/src/engine/renderers/Indicator/ma.ts +200 -200
  156. package/src/engine/renderers/Indicator/macd.ts +477 -477
  157. package/src/engine/renderers/Indicator/macdLegend.ts +141 -141
  158. package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +272 -272
  159. package/src/engine/renderers/Indicator/mfi.ts +142 -142
  160. package/src/engine/renderers/Indicator/mom.ts +311 -311
  161. package/src/engine/renderers/Indicator/obv.ts +123 -123
  162. package/src/engine/renderers/Indicator/parkinson.ts +124 -124
  163. package/src/engine/renderers/Indicator/pivot.ts +131 -131
  164. package/src/engine/renderers/Indicator/pvt.ts +123 -123
  165. package/src/engine/renderers/Indicator/roc.ts +143 -143
  166. package/src/engine/renderers/Indicator/rsi.ts +390 -390
  167. package/src/engine/renderers/Indicator/sar.ts +113 -113
  168. package/src/engine/renderers/Indicator/scale/atr_scale.ts +19 -19
  169. package/src/engine/renderers/Indicator/scale/cci_scale.ts +19 -19
  170. package/src/engine/renderers/Indicator/scale/fastk_scale.ts +19 -19
  171. package/src/engine/renderers/Indicator/scale/indicator_scale.ts +204 -204
  172. package/src/engine/renderers/Indicator/scale/kst_scale.ts +19 -19
  173. package/src/engine/renderers/Indicator/scale/macd_scale.ts +22 -22
  174. package/src/engine/renderers/Indicator/scale/mom_scale.ts +19 -19
  175. package/src/engine/renderers/Indicator/scale/rsi_scale.ts +19 -19
  176. package/src/engine/renderers/Indicator/scale/stoch_scale.ts +19 -19
  177. package/src/engine/renderers/Indicator/scale/volume_scale.ts +26 -26
  178. package/src/engine/renderers/Indicator/scale/wmsr_scale.ts +19 -19
  179. package/src/engine/renderers/Indicator/stoch.ts +359 -359
  180. package/src/engine/renderers/Indicator/structure.ts +126 -126
  181. package/src/engine/renderers/Indicator/subPaneConfig.ts +265 -265
  182. package/src/engine/renderers/Indicator/supertrend.ts +115 -115
  183. package/src/engine/renderers/Indicator/tema.ts +136 -136
  184. package/src/engine/renderers/Indicator/trix.ts +158 -158
  185. package/src/engine/renderers/Indicator/vma.ts +124 -124
  186. package/src/engine/renderers/Indicator/volumeProfile.ts +125 -125
  187. package/src/engine/renderers/Indicator/vwap.ts +123 -123
  188. package/src/engine/renderers/Indicator/wma.ts +136 -136
  189. package/src/engine/renderers/Indicator/wmsr.ts +328 -328
  190. package/src/engine/renderers/Indicator/zones.ts +104 -104
  191. package/src/engine/renderers/__tests__/boll.renderer.test.ts +314 -314
  192. package/src/engine/renderers/__tests__/ene.renderer.test.ts +305 -305
  193. package/src/engine/renderers/__tests__/expma.renderer.test.ts +279 -279
  194. package/src/engine/renderers/__tests__/ma.renderer.test.ts +426 -426
  195. package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +502 -502
  196. package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +173 -173
  197. package/src/engine/renderers/candle.ts +459 -459
  198. package/src/engine/renderers/crosshair.ts +69 -69
  199. package/src/engine/renderers/customMarkers.ts +162 -162
  200. package/src/engine/renderers/extremaMarkers.ts +246 -246
  201. package/src/engine/renderers/gridLines.ts +90 -90
  202. package/src/engine/renderers/lastPrice.ts +97 -97
  203. package/src/engine/renderers/paneTitle.ts +136 -136
  204. package/src/engine/renderers/subVolume.ts +236 -236
  205. package/src/engine/renderers/timeAxis.ts +121 -121
  206. package/src/engine/renderers/webgl/candleSurface.ts +955 -955
  207. package/src/engine/renderers/webgl/sharedWebGLSurface.ts +146 -146
  208. package/src/engine/renderers/yAxis.ts +105 -105
  209. package/src/engine/scale/__tests__/logFormula.spec.ts +148 -148
  210. package/src/engine/scale/logFormula.ts +130 -130
  211. package/src/engine/scale/price.ts +39 -39
  212. package/src/engine/scale/priceScale.ts +264 -264
  213. package/src/engine/subPaneManager.ts +442 -427
  214. package/src/engine/theme/colors.ts +642 -642
  215. package/src/engine/theme/fonts.ts +20 -20
  216. package/src/engine/utils/klineConfig.ts +49 -49
  217. package/src/engine/utils/tickCount.ts +11 -11
  218. package/src/engine/utils/tickPosition.ts +214 -214
  219. package/src/engine/utils/zoom.ts +83 -83
  220. package/src/engine/viewport/viewport.ts +67 -67
  221. package/src/index.ts +3 -3
  222. package/src/plugin/ConfigManager.ts +93 -93
  223. package/src/plugin/EventBus.ts +77 -77
  224. package/src/plugin/HookSystem.ts +106 -106
  225. package/src/plugin/PluginHost.ts +243 -243
  226. package/src/plugin/PluginRegistry.ts +92 -92
  227. package/src/plugin/StateStore.ts +73 -73
  228. package/src/plugin/index.ts +19 -19
  229. package/src/plugin/rendererPluginManager.ts +368 -368
  230. package/src/plugin/stateKeys.ts +8 -8
  231. package/src/plugin/types.ts +526 -526
  232. package/src/reactivity/index.ts +2 -2
  233. package/src/reactivity/signal.ts +119 -119
  234. package/src/semantic/controller.ts +251 -251
  235. package/src/semantic/drawShape.ts +260 -260
  236. package/src/semantic/index.ts +28 -28
  237. package/src/semantic/schema.json +256 -256
  238. package/src/semantic/types.ts +251 -251
  239. package/src/semantic/validator.ts +349 -349
  240. package/src/types/kLine.ts +13 -13
  241. package/src/types/price.ts +56 -56
  242. package/src/types/volumePrice.ts +33 -33
  243. package/src/utils/dateFormat.ts +208 -208
  244. package/src/utils/kLineDraw/axis.ts +562 -562
  245. package/src/utils/priceToY.ts +34 -34
  246. package/src/utils/volumePrice.ts +202 -202
  247. package/src/version.ts +1 -1
@@ -1,368 +1,368 @@
1
- /**
2
- * 渲染器插件管理器
3
- */
4
-
5
- import type {
6
- RendererPlugin,
7
- RenderContext,
8
- PaneInfo,
9
- RendererPluginWithHost,
10
- PluginHost,
11
- } from './types'
12
- import { UpdateLevel } from '../engine/layout/pane'
13
-
14
- /** 渲染器错误事件(裁剪后,不含大数据) */
15
- export interface RendererErrorEvent {
16
- name: string
17
- error: { message: string; stack?: string }
18
- paneId: string
19
- timestamp: number
20
- }
21
-
22
- /** 内部缓存 key(模块私有,避免与外部 paneId 冲突) */
23
- const GLOBAL_CACHE_KEY = Symbol('renderer:global-cache')
24
-
25
- /**
26
- * 渲染器插件管理器
27
- *
28
- * 启用状态优先级:
29
- * 1. setEnabled() 运行时设置的状态(enabledState 中存在)
30
- * 2. 插件初始 enabled 字段
31
- * 3. 默认启用(enabled !== false)
32
- */
33
- export class RendererPluginManager {
34
- private plugins: Map<string, RendererPlugin> = new Map()
35
- private pluginHost: PluginHost | null = null
36
-
37
- // 启用状态(独立存储,避免修改原始插件对象,支持多实例隔离)
38
- private enabledState: Map<string, boolean> = new Map()
39
-
40
- // 分组缓存:paneId -> 渲染器列表
41
- private groupCache: Map<string | symbol, RendererPlugin[]> = new Map()
42
-
43
- // 合并缓存:paneId -> pane+global 合并后的渲染器列表
44
- private mergedCache: Map<string | symbol, RendererPlugin[]> = new Map()
45
-
46
- // 已知的 paneId 集合(用于动态 pane 管理)
47
- private knownPaneIds: Set<string> = new Set()
48
-
49
- private cacheInvalid = true
50
- private onInvalidate: (() => void) | null = null
51
-
52
- /** 设置重绘回调(由 Chart 注入) */
53
- setInvalidateCallback(cb: () => void): void {
54
- this.onInvalidate = cb
55
- }
56
-
57
- /** 设置 PluginHost(用于支持 RendererPluginWithHost) */
58
- setPluginHost(host: PluginHost): void {
59
- this.pluginHost = host
60
- }
61
-
62
- /** 添加已知的 paneId */
63
- addKnownPaneId(paneId: string): void {
64
- this.knownPaneIds.add(paneId)
65
- this.cacheInvalid = true
66
- }
67
-
68
- /** 移除 paneId */
69
- removeKnownPaneId(paneId: string): void {
70
- this.knownPaneIds.delete(paneId)
71
- this.cacheInvalid = true
72
- }
73
-
74
-
75
- /** 覆盖已知 paneId 集合 */
76
- setKnownPaneIds(paneIds: string[]): void {
77
- this.knownPaneIds = new Set(paneIds)
78
- this.cacheInvalid = true
79
- }
80
-
81
- /** 注册渲染器插件 */
82
- register(plugin: RendererPlugin | RendererPluginWithHost): void {
83
- if (this.plugins.has(plugin.name)) {
84
- console.warn(`Renderer plugin "${plugin.name}" already registered`)
85
- return
86
- }
87
-
88
- this.plugins.set(plugin.name, plugin)
89
- // 初始化启用状态(仅当初始值有定义时存储)
90
- if (plugin.enabled !== undefined) {
91
- this.enabledState.set(plugin.name, plugin.enabled)
92
- }
93
- this.cacheInvalid = true
94
-
95
- // 如果是 RendererPluginWithHost,调用 onInstall
96
- const withHost = plugin as RendererPluginWithHost
97
- if (withHost.onInstall && this.pluginHost) {
98
- try {
99
- withHost.onInstall(this.pluginHost)
100
- } catch (e) {
101
- console.error(`[RendererPlugin] ${plugin.name} onInstall error:`, e)
102
- }
103
- }
104
-
105
- // 记录声明的状态命名空间(用于自动清理)
106
- const namespaces = withHost.getDeclaredNamespaces?.()
107
- if (namespaces && this.pluginHost) {
108
- this.pluginHost.registerStateOwner(plugin.name, namespaces)
109
- }
110
-
111
- // 注册后自动触发重绘
112
- this.onInvalidate?.()
113
- }
114
-
115
- /** 移除渲染器插件 */
116
- unregister(name: string): void {
117
- const plugin = this.plugins.get(name)
118
- if (!plugin) return
119
-
120
- // 自动清理状态(在 onUninstall 之前),仅当插件声明过命名空间时
121
- const withHost = plugin as RendererPluginWithHost
122
- if (withHost.getDeclaredNamespaces) {
123
- this.pluginHost?.clearByOwner(name)
124
- }
125
-
126
- // 调用卸载回调
127
- if (plugin.onUninstall) {
128
- try {
129
- plugin.onUninstall()
130
- } catch (e) {
131
- console.error(`[RendererPlugin] ${plugin.name} onUninstall error:`, e)
132
- }
133
- }
134
-
135
- this.plugins.delete(name)
136
- this.enabledState.delete(name)
137
- this.cacheInvalid = true
138
-
139
- // 卸载后自动触发重绘
140
- this.onInvalidate?.()
141
- }
142
-
143
- /** 清空所有插件 */
144
- clear(): void {
145
- for (const plugin of this.plugins.values()) {
146
- if (plugin.onUninstall) {
147
- try {
148
- plugin.onUninstall()
149
- } catch (e) {
150
- console.error(`[RendererPlugin] ${plugin.name} onUninstall error:`, e)
151
- }
152
- }
153
- }
154
- this.plugins.clear()
155
- this.enabledState.clear()
156
- this.groupCache.clear()
157
- this.mergedCache.clear()
158
- this.cacheInvalid = false
159
- }
160
-
161
- /**
162
- * 归并两个已排序数组 O(n)
163
- * 优先级相同时,pane 专属渲染器(a)先于 global 渲染器(b)
164
- */
165
- private mergeSorted(a: RendererPlugin[], b: RendererPlugin[]): RendererPlugin[] {
166
- const result: RendererPlugin[] = []
167
- let i = 0,
168
- j = 0
169
- while (i < a.length && j < b.length) {
170
- // 优先级相同时,a(pane 专属)优先
171
- if (a[i]!.priority <= b[j]!.priority) result.push(a[i++]!)
172
- else result.push(b[j++]!)
173
- }
174
- return [...result, ...a.slice(i), ...b.slice(j)]
175
- }
176
-
177
- /** 重建缓存(统一管理所有缓存逻辑) */
178
- private rebuildCache(): void {
179
- if (!this.cacheInvalid) return
180
-
181
- this.groupCache.clear()
182
- this.mergedCache.clear()
183
-
184
- // 按 paneId 分组
185
- for (const plugin of this.plugins.values()) {
186
- const cacheKey = typeof plugin.paneId === 'symbol' ? GLOBAL_CACHE_KEY : plugin.paneId
187
- if (!this.groupCache.has(cacheKey)) {
188
- this.groupCache.set(cacheKey, [])
189
- }
190
- this.groupCache.get(cacheKey)!.push(plugin)
191
- }
192
-
193
- // 对每组排序
194
- for (const [, list] of this.groupCache) {
195
- list.sort((a, b) => a.priority - b.priority)
196
- }
197
-
198
- // 预构建合并缓存
199
- const globalRenderers = this.groupCache.get(GLOBAL_CACHE_KEY) ?? []
200
-
201
- // 为每个已知 paneId 构建合并缓存
202
- for (const paneId of this.knownPaneIds) {
203
- const paneRenderers = this.groupCache.get(paneId) ?? []
204
- const merged = this.mergeSorted(paneRenderers, globalRenderers)
205
- this.mergedCache.set(paneId, merged)
206
- }
207
-
208
- // 缓存纯 global 渲染器作为 fallback
209
- this.mergedCache.set(GLOBAL_CACHE_KEY, [...globalRenderers])
210
-
211
- this.cacheInvalid = false
212
- }
213
-
214
- /** 判断渲染器是否启用 */
215
- private isRendererEnabled(plugin: RendererPlugin): boolean {
216
- const state = this.enabledState.get(plugin.name)
217
- return state !== undefined ? state : plugin.enabled !== false
218
- }
219
-
220
- /** 获取指定 pane 的合并渲染器(包含 system 渲染器) */
221
- private getMergedRenderers(paneId: string): RendererPlugin[] {
222
- this.rebuildCache()
223
-
224
- let cached = this.mergedCache.get(paneId)
225
- if (!cached) {
226
- const paneRenderers = this.groupCache.get(paneId) ?? []
227
- const globalRenderers = this.groupCache.get(GLOBAL_CACHE_KEY) ?? []
228
- cached = this.mergeSorted(paneRenderers, globalRenderers)
229
- this.mergedCache.set(paneId, cached)
230
- }
231
-
232
- return cached
233
- }
234
-
235
- /** 获取指定 pane 的渲染器(已缓存,无穿透) */
236
- getRenderers(paneId: string): RendererPlugin[] {
237
- const cached = this.getMergedRenderers(paneId)
238
-
239
- // 根据启用状态过滤,同时排除系统渲染器
240
- return cached.filter((p) => {
241
- // 系统渲染器不通过 getRenderers 返回,只能通过 renderPlugin 单独渲染
242
- if (p.isSystem) return false
243
- return this.isRendererEnabled(p)
244
- })
245
- }
246
-
247
-
248
- /** 渲染指定 pane(带错误隔离,支持按 UpdateLevel 过滤) */
249
- render(
250
- paneId: string,
251
- context: RenderContext,
252
- level?: UpdateLevel
253
- ): RendererErrorEvent[] {
254
- const renderers = this.getRenderers(paneId)
255
- const errors: RendererErrorEvent[] = []
256
-
257
- for (const renderer of renderers) {
258
- // UpdateLevel 过滤:未传 level 或为 All 时不过滤
259
- if (level) {
260
- const rendererLayer = renderer.layer ?? 'main'
261
- if (level === UpdateLevel.Overlay && rendererLayer !== 'overlay') {
262
- continue // Overlay 更新时跳过非 overlay 渲染器
263
- }
264
- if (level === UpdateLevel.Main && rendererLayer === 'overlay') {
265
- continue // Main 更新时跳过 overlay 渲染器
266
- }
267
- }
268
-
269
- try {
270
- renderer.draw(context)
271
- } catch (e) {
272
- const error = e instanceof Error ? e : new Error(String(e))
273
- console.error(`[RendererPlugin] ${renderer.name} draw error:`, error)
274
- // 裁剪错误事件,不含大数据
275
- errors.push({
276
- name: renderer.name,
277
- error: { message: error.message, stack: error.stack },
278
- paneId: context.pane.id,
279
- timestamp: Date.now(),
280
- })
281
- }
282
- }
283
-
284
- return errors
285
- }
286
-
287
- /** 渲染指定名称的插件(带错误隔离,用于系统渲染器) */
288
- renderPlugin(name: string, context: RenderContext): RendererErrorEvent[] {
289
- const plugin = this.plugins.get(name)
290
- if (!plugin) return []
291
-
292
- // 检查启用状态
293
- if (!this.isRendererEnabled(plugin)) return []
294
-
295
- const errors: RendererErrorEvent[] = []
296
- try {
297
- plugin.draw(context)
298
- } catch (e) {
299
- const error = e instanceof Error ? e : new Error(String(e))
300
- console.error(`[RendererPlugin] ${name} draw error:`, error)
301
- errors.push({
302
- name,
303
- error: { message: error.message, stack: error.stack },
304
- paneId: context.pane.id,
305
- timestamp: Date.now(),
306
- })
307
- }
308
-
309
- return errors
310
- }
311
-
312
- /** 启用/禁用渲染器(修改独立状态,不影响原始插件对象) */
313
- setEnabled(name: string, enabled: boolean): void {
314
- if (!this.plugins.has(name)) return
315
- this.enabledState.set(name, enabled)
316
- this.onInvalidate?.()
317
- }
318
-
319
- /** 更新配置(自动触发重绘) */
320
- updateConfig(name: string, config: Record<string, unknown>): boolean {
321
- const plugin = this.plugins.get(name)
322
- if (!plugin?.setConfig) return false
323
-
324
- plugin.setConfig(config)
325
- this.onInvalidate?.()
326
- return true
327
- }
328
-
329
- /** 获取所有渲染器插件 */
330
- getAllPlugins(): RendererPlugin[] {
331
- return Array.from(this.plugins.values())
332
- }
333
-
334
- /** 获取指定渲染器 */
335
- getPlugin<T extends RendererPlugin = RendererPlugin>(name: string): T | undefined {
336
- return this.plugins.get(name) as T | undefined
337
- }
338
-
339
- /* 调用 onDataUpdate 钩子通知数据更新 */
340
- notifyDataUpdate(data: unknown[], range: { start: number; end: number }): void {
341
- for (const plugin of this.plugins.values()) {
342
- if (!plugin.onDataUpdate) continue
343
-
344
- // 检查启用状态,跳过禁用的插件
345
- if (!this.isRendererEnabled(plugin)) continue
346
-
347
- try {
348
- plugin.onDataUpdate(data, range)
349
- } catch (e) {
350
- console.error(`[RendererPlugin] ${plugin.name} onDataUpdate error:`, e)
351
- }
352
- }
353
- }
354
-
355
- /** 通知尺寸变化 */
356
- notifyResize(paneId: string, pane: PaneInfo): void {
357
- const renderers = this.getMergedRenderers(paneId).filter((renderer) => this.isRendererEnabled(renderer))
358
- for (const renderer of renderers) {
359
- if (renderer.onResize) {
360
- try {
361
- renderer.onResize(pane)
362
- } catch (e) {
363
- console.error(`[RendererPlugin] ${renderer.name} onResize error:`, e)
364
- }
365
- }
366
- }
367
- }
368
- }
1
+ /**
2
+ * 渲染器插件管理器
3
+ */
4
+
5
+ import type {
6
+ RendererPlugin,
7
+ RenderContext,
8
+ PaneInfo,
9
+ RendererPluginWithHost,
10
+ PluginHost,
11
+ } from './types'
12
+ import { UpdateLevel } from '../engine/layout/pane'
13
+
14
+ /** 渲染器错误事件(裁剪后,不含大数据) */
15
+ export interface RendererErrorEvent {
16
+ name: string
17
+ error: { message: string; stack?: string }
18
+ paneId: string
19
+ timestamp: number
20
+ }
21
+
22
+ /** 内部缓存 key(模块私有,避免与外部 paneId 冲突) */
23
+ const GLOBAL_CACHE_KEY = Symbol('renderer:global-cache')
24
+
25
+ /**
26
+ * 渲染器插件管理器
27
+ *
28
+ * 启用状态优先级:
29
+ * 1. setEnabled() 运行时设置的状态(enabledState 中存在)
30
+ * 2. 插件初始 enabled 字段
31
+ * 3. 默认启用(enabled !== false)
32
+ */
33
+ export class RendererPluginManager {
34
+ private plugins: Map<string, RendererPlugin> = new Map()
35
+ private pluginHost: PluginHost | null = null
36
+
37
+ // 启用状态(独立存储,避免修改原始插件对象,支持多实例隔离)
38
+ private enabledState: Map<string, boolean> = new Map()
39
+
40
+ // 分组缓存:paneId -> 渲染器列表
41
+ private groupCache: Map<string | symbol, RendererPlugin[]> = new Map()
42
+
43
+ // 合并缓存:paneId -> pane+global 合并后的渲染器列表
44
+ private mergedCache: Map<string | symbol, RendererPlugin[]> = new Map()
45
+
46
+ // 已知的 paneId 集合(用于动态 pane 管理)
47
+ private knownPaneIds: Set<string> = new Set()
48
+
49
+ private cacheInvalid = true
50
+ private onInvalidate: (() => void) | null = null
51
+
52
+ /** 设置重绘回调(由 Chart 注入) */
53
+ setInvalidateCallback(cb: () => void): void {
54
+ this.onInvalidate = cb
55
+ }
56
+
57
+ /** 设置 PluginHost(用于支持 RendererPluginWithHost) */
58
+ setPluginHost(host: PluginHost): void {
59
+ this.pluginHost = host
60
+ }
61
+
62
+ /** 添加已知的 paneId */
63
+ addKnownPaneId(paneId: string): void {
64
+ this.knownPaneIds.add(paneId)
65
+ this.cacheInvalid = true
66
+ }
67
+
68
+ /** 移除 paneId */
69
+ removeKnownPaneId(paneId: string): void {
70
+ this.knownPaneIds.delete(paneId)
71
+ this.cacheInvalid = true
72
+ }
73
+
74
+
75
+ /** 覆盖已知 paneId 集合 */
76
+ setKnownPaneIds(paneIds: string[]): void {
77
+ this.knownPaneIds = new Set(paneIds)
78
+ this.cacheInvalid = true
79
+ }
80
+
81
+ /** 注册渲染器插件 */
82
+ register(plugin: RendererPlugin | RendererPluginWithHost): void {
83
+ if (this.plugins.has(plugin.name)) {
84
+ console.warn(`Renderer plugin "${plugin.name}" already registered`)
85
+ return
86
+ }
87
+
88
+ this.plugins.set(plugin.name, plugin)
89
+ // 初始化启用状态(仅当初始值有定义时存储)
90
+ if (plugin.enabled !== undefined) {
91
+ this.enabledState.set(plugin.name, plugin.enabled)
92
+ }
93
+ this.cacheInvalid = true
94
+
95
+ // 如果是 RendererPluginWithHost,调用 onInstall
96
+ const withHost = plugin as RendererPluginWithHost
97
+ if (withHost.onInstall && this.pluginHost) {
98
+ try {
99
+ withHost.onInstall(this.pluginHost)
100
+ } catch (e) {
101
+ console.error(`[RendererPlugin] ${plugin.name} onInstall error:`, e)
102
+ }
103
+ }
104
+
105
+ // 记录声明的状态命名空间(用于自动清理)
106
+ const namespaces = withHost.getDeclaredNamespaces?.()
107
+ if (namespaces && this.pluginHost) {
108
+ this.pluginHost.registerStateOwner(plugin.name, namespaces)
109
+ }
110
+
111
+ // 注册后自动触发重绘
112
+ this.onInvalidate?.()
113
+ }
114
+
115
+ /** 移除渲染器插件 */
116
+ unregister(name: string): void {
117
+ const plugin = this.plugins.get(name)
118
+ if (!plugin) return
119
+
120
+ // 自动清理状态(在 onUninstall 之前),仅当插件声明过命名空间时
121
+ const withHost = plugin as RendererPluginWithHost
122
+ if (withHost.getDeclaredNamespaces) {
123
+ this.pluginHost?.clearByOwner(name)
124
+ }
125
+
126
+ // 调用卸载回调
127
+ if (plugin.onUninstall) {
128
+ try {
129
+ plugin.onUninstall()
130
+ } catch (e) {
131
+ console.error(`[RendererPlugin] ${plugin.name} onUninstall error:`, e)
132
+ }
133
+ }
134
+
135
+ this.plugins.delete(name)
136
+ this.enabledState.delete(name)
137
+ this.cacheInvalid = true
138
+
139
+ // 卸载后自动触发重绘
140
+ this.onInvalidate?.()
141
+ }
142
+
143
+ /** 清空所有插件 */
144
+ clear(): void {
145
+ for (const plugin of this.plugins.values()) {
146
+ if (plugin.onUninstall) {
147
+ try {
148
+ plugin.onUninstall()
149
+ } catch (e) {
150
+ console.error(`[RendererPlugin] ${plugin.name} onUninstall error:`, e)
151
+ }
152
+ }
153
+ }
154
+ this.plugins.clear()
155
+ this.enabledState.clear()
156
+ this.groupCache.clear()
157
+ this.mergedCache.clear()
158
+ this.cacheInvalid = false
159
+ }
160
+
161
+ /**
162
+ * 归并两个已排序数组 O(n)
163
+ * 优先级相同时,pane 专属渲染器(a)先于 global 渲染器(b)
164
+ */
165
+ private mergeSorted(a: RendererPlugin[], b: RendererPlugin[]): RendererPlugin[] {
166
+ const result: RendererPlugin[] = []
167
+ let i = 0,
168
+ j = 0
169
+ while (i < a.length && j < b.length) {
170
+ // 优先级相同时,a(pane 专属)优先
171
+ if (a[i]!.priority <= b[j]!.priority) result.push(a[i++]!)
172
+ else result.push(b[j++]!)
173
+ }
174
+ return [...result, ...a.slice(i), ...b.slice(j)]
175
+ }
176
+
177
+ /** 重建缓存(统一管理所有缓存逻辑) */
178
+ private rebuildCache(): void {
179
+ if (!this.cacheInvalid) return
180
+
181
+ this.groupCache.clear()
182
+ this.mergedCache.clear()
183
+
184
+ // 按 paneId 分组
185
+ for (const plugin of this.plugins.values()) {
186
+ const cacheKey = typeof plugin.paneId === 'symbol' ? GLOBAL_CACHE_KEY : plugin.paneId
187
+ if (!this.groupCache.has(cacheKey)) {
188
+ this.groupCache.set(cacheKey, [])
189
+ }
190
+ this.groupCache.get(cacheKey)!.push(plugin)
191
+ }
192
+
193
+ // 对每组排序
194
+ for (const [, list] of this.groupCache) {
195
+ list.sort((a, b) => a.priority - b.priority)
196
+ }
197
+
198
+ // 预构建合并缓存
199
+ const globalRenderers = this.groupCache.get(GLOBAL_CACHE_KEY) ?? []
200
+
201
+ // 为每个已知 paneId 构建合并缓存
202
+ for (const paneId of this.knownPaneIds) {
203
+ const paneRenderers = this.groupCache.get(paneId) ?? []
204
+ const merged = this.mergeSorted(paneRenderers, globalRenderers)
205
+ this.mergedCache.set(paneId, merged)
206
+ }
207
+
208
+ // 缓存纯 global 渲染器作为 fallback
209
+ this.mergedCache.set(GLOBAL_CACHE_KEY, [...globalRenderers])
210
+
211
+ this.cacheInvalid = false
212
+ }
213
+
214
+ /** 判断渲染器是否启用 */
215
+ private isRendererEnabled(plugin: RendererPlugin): boolean {
216
+ const state = this.enabledState.get(plugin.name)
217
+ return state !== undefined ? state : plugin.enabled !== false
218
+ }
219
+
220
+ /** 获取指定 pane 的合并渲染器(包含 system 渲染器) */
221
+ private getMergedRenderers(paneId: string): RendererPlugin[] {
222
+ this.rebuildCache()
223
+
224
+ let cached = this.mergedCache.get(paneId)
225
+ if (!cached) {
226
+ const paneRenderers = this.groupCache.get(paneId) ?? []
227
+ const globalRenderers = this.groupCache.get(GLOBAL_CACHE_KEY) ?? []
228
+ cached = this.mergeSorted(paneRenderers, globalRenderers)
229
+ this.mergedCache.set(paneId, cached)
230
+ }
231
+
232
+ return cached
233
+ }
234
+
235
+ /** 获取指定 pane 的渲染器(已缓存,无穿透) */
236
+ getRenderers(paneId: string): RendererPlugin[] {
237
+ const cached = this.getMergedRenderers(paneId)
238
+
239
+ // 根据启用状态过滤,同时排除系统渲染器
240
+ return cached.filter((p) => {
241
+ // 系统渲染器不通过 getRenderers 返回,只能通过 renderPlugin 单独渲染
242
+ if (p.isSystem) return false
243
+ return this.isRendererEnabled(p)
244
+ })
245
+ }
246
+
247
+
248
+ /** 渲染指定 pane(带错误隔离,支持按 UpdateLevel 过滤) */
249
+ render(
250
+ paneId: string,
251
+ context: RenderContext,
252
+ level?: UpdateLevel
253
+ ): RendererErrorEvent[] {
254
+ const renderers = this.getRenderers(paneId)
255
+ const errors: RendererErrorEvent[] = []
256
+
257
+ for (const renderer of renderers) {
258
+ // UpdateLevel 过滤:未传 level 或为 All 时不过滤
259
+ if (level) {
260
+ const rendererLayer = renderer.layer ?? 'main'
261
+ if (level === UpdateLevel.Overlay && rendererLayer !== 'overlay') {
262
+ continue // Overlay 更新时跳过非 overlay 渲染器
263
+ }
264
+ if (level === UpdateLevel.Main && rendererLayer === 'overlay') {
265
+ continue // Main 更新时跳过 overlay 渲染器
266
+ }
267
+ }
268
+
269
+ try {
270
+ renderer.draw(context)
271
+ } catch (e) {
272
+ const error = e instanceof Error ? e : new Error(String(e))
273
+ console.error(`[RendererPlugin] ${renderer.name} draw error:`, error)
274
+ // 裁剪错误事件,不含大数据
275
+ errors.push({
276
+ name: renderer.name,
277
+ error: { message: error.message, stack: error.stack },
278
+ paneId: context.pane.id,
279
+ timestamp: Date.now(),
280
+ })
281
+ }
282
+ }
283
+
284
+ return errors
285
+ }
286
+
287
+ /** 渲染指定名称的插件(带错误隔离,用于系统渲染器) */
288
+ renderPlugin(name: string, context: RenderContext): RendererErrorEvent[] {
289
+ const plugin = this.plugins.get(name)
290
+ if (!plugin) return []
291
+
292
+ // 检查启用状态
293
+ if (!this.isRendererEnabled(plugin)) return []
294
+
295
+ const errors: RendererErrorEvent[] = []
296
+ try {
297
+ plugin.draw(context)
298
+ } catch (e) {
299
+ const error = e instanceof Error ? e : new Error(String(e))
300
+ console.error(`[RendererPlugin] ${name} draw error:`, error)
301
+ errors.push({
302
+ name,
303
+ error: { message: error.message, stack: error.stack },
304
+ paneId: context.pane.id,
305
+ timestamp: Date.now(),
306
+ })
307
+ }
308
+
309
+ return errors
310
+ }
311
+
312
+ /** 启用/禁用渲染器(修改独立状态,不影响原始插件对象) */
313
+ setEnabled(name: string, enabled: boolean): void {
314
+ if (!this.plugins.has(name)) return
315
+ this.enabledState.set(name, enabled)
316
+ this.onInvalidate?.()
317
+ }
318
+
319
+ /** 更新配置(自动触发重绘) */
320
+ updateConfig(name: string, config: Record<string, unknown>): boolean {
321
+ const plugin = this.plugins.get(name)
322
+ if (!plugin?.setConfig) return false
323
+
324
+ plugin.setConfig(config)
325
+ this.onInvalidate?.()
326
+ return true
327
+ }
328
+
329
+ /** 获取所有渲染器插件 */
330
+ getAllPlugins(): RendererPlugin[] {
331
+ return Array.from(this.plugins.values())
332
+ }
333
+
334
+ /** 获取指定渲染器 */
335
+ getPlugin<T extends RendererPlugin = RendererPlugin>(name: string): T | undefined {
336
+ return this.plugins.get(name) as T | undefined
337
+ }
338
+
339
+ /* 调用 onDataUpdate 钩子通知数据更新 */
340
+ notifyDataUpdate(data: unknown[], range: { start: number; end: number }): void {
341
+ for (const plugin of this.plugins.values()) {
342
+ if (!plugin.onDataUpdate) continue
343
+
344
+ // 检查启用状态,跳过禁用的插件
345
+ if (!this.isRendererEnabled(plugin)) continue
346
+
347
+ try {
348
+ plugin.onDataUpdate(data, range)
349
+ } catch (e) {
350
+ console.error(`[RendererPlugin] ${plugin.name} onDataUpdate error:`, e)
351
+ }
352
+ }
353
+ }
354
+
355
+ /** 通知尺寸变化 */
356
+ notifyResize(paneId: string, pane: PaneInfo): void {
357
+ const renderers = this.getMergedRenderers(paneId).filter((renderer) => this.isRendererEnabled(renderer))
358
+ for (const renderer of renderers) {
359
+ if (renderer.onResize) {
360
+ try {
361
+ renderer.onResize(pane)
362
+ } catch (e) {
363
+ console.error(`[RendererPlugin] ${renderer.name} onResize error:`, e)
364
+ }
365
+ }
366
+ }
367
+ }
368
+ }