@363045841yyt/klinechart-core 0.7.3 → 0.7.5-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 (231) hide show
  1. package/README.md +201 -201
  2. package/README.zh-CN.md +201 -201
  3. package/dist/engine/renderers/webgl/candleSurface.js +47 -47
  4. package/dist/version.d.ts +1 -1
  5. package/dist/version.d.ts.map +1 -1
  6. package/dist/version.js +1 -2
  7. package/dist/version.js.map +1 -1
  8. package/package.json +129 -122
  9. package/src/__tests__/signal.test.ts +124 -124
  10. package/src/config/chartSettings.ts +66 -66
  11. package/src/controllers/__tests__/drawing.test.ts +214 -214
  12. package/src/controllers/__tests__/indicatorSelector.test.ts +481 -481
  13. package/src/controllers/__tests__/toolbar.test.ts +225 -225
  14. package/src/controllers/createChartController.ts +665 -665
  15. package/src/controllers/createDrawingController.ts +96 -96
  16. package/src/controllers/createIndicatorSelectorController.ts +307 -307
  17. package/src/controllers/createToolbarController.ts +146 -146
  18. package/src/controllers/index.ts +19 -19
  19. package/src/controllers/types.ts +284 -284
  20. package/src/engine/__tests__/chart.dpr.test.ts +401 -401
  21. package/src/engine/__tests__/paneRenderer.resize.test.ts +92 -92
  22. package/src/engine/chart-store.ts +121 -121
  23. package/src/engine/chart.d.ts +617 -617
  24. package/src/engine/chart.ts +2815 -2815
  25. package/src/engine/controller/__tests__/interaction.dpr.test.ts +259 -259
  26. package/src/engine/controller/interaction.ts +722 -722
  27. package/src/engine/controller/markerInteraction.ts +130 -130
  28. package/src/engine/controller/pinchTracker.ts +82 -82
  29. package/src/engine/controller/tooltipPosition.ts +48 -48
  30. package/src/engine/draw/__tests__/pixelAlign.spec.ts +176 -176
  31. package/src/engine/draw/pixelAlign.ts +259 -259
  32. package/src/engine/drawing/index.ts +655 -655
  33. package/src/engine/drawing/interaction.ts +842 -842
  34. package/src/engine/drawing/plugin.ts +343 -343
  35. package/src/engine/indicators/__tests__/__fixtures__/golden/atr.json +38 -38
  36. package/src/engine/indicators/__tests__/__fixtures__/golden/dema.json +14 -14
  37. package/src/engine/indicators/__tests__/__fixtures__/golden/hma.json +14 -14
  38. package/src/engine/indicators/__tests__/__fixtures__/golden/index.ts +55 -55
  39. package/src/engine/indicators/__tests__/__fixtures__/golden/kama.json +14 -14
  40. package/src/engine/indicators/__tests__/__fixtures__/golden/tema.json +14 -14
  41. package/src/engine/indicators/__tests__/__fixtures__/golden/wma.json +40 -40
  42. package/src/engine/indicators/__tests__/__fixtures__/synthetic.ts +65 -65
  43. package/src/engine/indicators/__tests__/_propertyAssertions.ts +76 -76
  44. package/src/engine/indicators/__tests__/atr.test.ts +153 -153
  45. package/src/engine/indicators/__tests__/calculators.test.ts +614 -614
  46. package/src/engine/indicators/__tests__/cmf-mfi.test.ts +100 -100
  47. package/src/engine/indicators/__tests__/dema.test.ts +73 -73
  48. package/src/engine/indicators/__tests__/donchian.test.ts +70 -70
  49. package/src/engine/indicators/__tests__/hma.test.ts +73 -73
  50. package/src/engine/indicators/__tests__/ichimoku.test.ts +105 -105
  51. package/src/engine/indicators/__tests__/kama.test.ts +80 -80
  52. package/src/engine/indicators/__tests__/keltner.test.ts +65 -65
  53. package/src/engine/indicators/__tests__/pivot-fib.test.ts +110 -110
  54. package/src/engine/indicators/__tests__/roc.test.ts +68 -68
  55. package/src/engine/indicators/__tests__/sar.test.ts +86 -86
  56. package/src/engine/indicators/__tests__/scheduler.test.ts +831 -831
  57. package/src/engine/indicators/__tests__/soa.test.ts +533 -533
  58. package/src/engine/indicators/__tests__/structure.test.ts +110 -110
  59. package/src/engine/indicators/__tests__/supertrend.test.ts +65 -65
  60. package/src/engine/indicators/__tests__/tema.test.ts +68 -68
  61. package/src/engine/indicators/__tests__/trix.test.ts +70 -70
  62. package/src/engine/indicators/__tests__/volatility.test.ts +117 -117
  63. package/src/engine/indicators/__tests__/volume.test.ts +115 -115
  64. package/src/engine/indicators/__tests__/volumeProfile.test.ts +74 -74
  65. package/src/engine/indicators/__tests__/vwap.test.ts +69 -69
  66. package/src/engine/indicators/__tests__/wma.test.ts +112 -112
  67. package/src/engine/indicators/__tests__/zones.test.ts +95 -95
  68. package/src/engine/indicators/atrState.ts +27 -27
  69. package/src/engine/indicators/bollState.ts +51 -51
  70. package/src/engine/indicators/calculators.ts +2593 -2593
  71. package/src/engine/indicators/cciState.ts +25 -25
  72. package/src/engine/indicators/chaikinVolState.ts +32 -32
  73. package/src/engine/indicators/cmfState.ts +27 -27
  74. package/src/engine/indicators/demaState.ts +27 -27
  75. package/src/engine/indicators/donchianState.ts +43 -43
  76. package/src/engine/indicators/eneState.ts +43 -43
  77. package/src/engine/indicators/expmaState.ts +43 -43
  78. package/src/engine/indicators/fastkState.ts +25 -25
  79. package/src/engine/indicators/fibState.ts +41 -41
  80. package/src/engine/indicators/hmaState.ts +27 -27
  81. package/src/engine/indicators/hvState.ts +28 -28
  82. package/src/engine/indicators/ichimokuState.ts +70 -70
  83. package/src/engine/indicators/indicator.worker.ts +169 -169
  84. package/src/engine/indicators/indicatorDefinitionRegistry.ts +62 -62
  85. package/src/engine/indicators/indicatorMetadata.ts +110 -110
  86. package/src/engine/indicators/indicatorRegistry.ts +106 -106
  87. package/src/engine/indicators/indicatorRuntime.ts +1548 -1548
  88. package/src/engine/indicators/kamaState.ts +34 -34
  89. package/src/engine/indicators/keltnerState.ts +49 -49
  90. package/src/engine/indicators/kstState.ts +42 -42
  91. package/src/engine/indicators/maState.ts +36 -36
  92. package/src/engine/indicators/macdState.ts +76 -76
  93. package/src/engine/indicators/mfiState.ts +27 -27
  94. package/src/engine/indicators/momState.ts +25 -25
  95. package/src/engine/indicators/obvState.ts +25 -25
  96. package/src/engine/indicators/parkinsonState.ts +28 -28
  97. package/src/engine/indicators/pivotState.ts +51 -51
  98. package/src/engine/indicators/pvtState.ts +25 -25
  99. package/src/engine/indicators/rocState.ts +27 -27
  100. package/src/engine/indicators/rsiState.ts +65 -65
  101. package/src/engine/indicators/sarState.ts +41 -41
  102. package/src/engine/indicators/scheduler.ts +1205 -1205
  103. package/src/engine/indicators/soa.ts +352 -352
  104. package/src/engine/indicators/stateComposer.ts +1262 -1262
  105. package/src/engine/indicators/stochState.ts +26 -26
  106. package/src/engine/indicators/structureState.ts +69 -69
  107. package/src/engine/indicators/supertrendState.ts +37 -37
  108. package/src/engine/indicators/temaState.ts +27 -27
  109. package/src/engine/indicators/trixState.ts +35 -35
  110. package/src/engine/indicators/vmaState.ts +27 -27
  111. package/src/engine/indicators/volumeProfileState.ts +63 -63
  112. package/src/engine/indicators/vwapState.ts +29 -29
  113. package/src/engine/indicators/wmaState.ts +27 -27
  114. package/src/engine/indicators/wmsrState.ts +25 -25
  115. package/src/engine/indicators/workerProtocol.ts +613 -613
  116. package/src/engine/indicators/zonesState.ts +47 -47
  117. package/src/engine/layout/pane.ts +161 -161
  118. package/src/engine/marker/registry.ts +265 -265
  119. package/src/engine/paneRenderer.ts +169 -169
  120. package/src/engine/renderers/Indicator/atr.ts +237 -237
  121. package/src/engine/renderers/Indicator/boll.ts +317 -317
  122. package/src/engine/renderers/Indicator/cci.ts +275 -275
  123. package/src/engine/renderers/Indicator/chaikinVol.ts +138 -138
  124. package/src/engine/renderers/Indicator/cmf.ts +137 -137
  125. package/src/engine/renderers/Indicator/dema.ts +136 -136
  126. package/src/engine/renderers/Indicator/donchian.ts +137 -137
  127. package/src/engine/renderers/Indicator/ene.ts +271 -271
  128. package/src/engine/renderers/Indicator/expma.ts +197 -197
  129. package/src/engine/renderers/Indicator/fastk.ts +316 -316
  130. package/src/engine/renderers/Indicator/fib.ts +141 -141
  131. package/src/engine/renderers/Indicator/hma.ts +136 -136
  132. package/src/engine/renderers/Indicator/hv.ts +124 -124
  133. package/src/engine/renderers/Indicator/ichimoku.ts +181 -181
  134. package/src/engine/renderers/Indicator/index.ts +241 -241
  135. package/src/engine/renderers/Indicator/indicatorData.ts +650 -650
  136. package/src/engine/renderers/Indicator/kama.ts +136 -136
  137. package/src/engine/renderers/Indicator/keltner.ts +137 -137
  138. package/src/engine/renderers/Indicator/kst.ts +302 -302
  139. package/src/engine/renderers/Indicator/ma.ts +200 -200
  140. package/src/engine/renderers/Indicator/macd.ts +477 -477
  141. package/src/engine/renderers/Indicator/macdLegend.ts +141 -141
  142. package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +272 -272
  143. package/src/engine/renderers/Indicator/mfi.ts +142 -142
  144. package/src/engine/renderers/Indicator/mom.ts +311 -311
  145. package/src/engine/renderers/Indicator/obv.ts +123 -123
  146. package/src/engine/renderers/Indicator/parkinson.ts +124 -124
  147. package/src/engine/renderers/Indicator/pivot.ts +131 -131
  148. package/src/engine/renderers/Indicator/pvt.ts +123 -123
  149. package/src/engine/renderers/Indicator/roc.ts +143 -143
  150. package/src/engine/renderers/Indicator/rsi.ts +390 -390
  151. package/src/engine/renderers/Indicator/sar.ts +113 -113
  152. package/src/engine/renderers/Indicator/scale/atr_scale.ts +19 -19
  153. package/src/engine/renderers/Indicator/scale/cci_scale.ts +19 -19
  154. package/src/engine/renderers/Indicator/scale/fastk_scale.ts +19 -19
  155. package/src/engine/renderers/Indicator/scale/indicator_scale.ts +204 -204
  156. package/src/engine/renderers/Indicator/scale/kst_scale.ts +19 -19
  157. package/src/engine/renderers/Indicator/scale/macd_scale.ts +22 -22
  158. package/src/engine/renderers/Indicator/scale/mom_scale.ts +19 -19
  159. package/src/engine/renderers/Indicator/scale/rsi_scale.ts +19 -19
  160. package/src/engine/renderers/Indicator/scale/stoch_scale.ts +19 -19
  161. package/src/engine/renderers/Indicator/scale/volume_scale.ts +26 -26
  162. package/src/engine/renderers/Indicator/scale/wmsr_scale.ts +19 -19
  163. package/src/engine/renderers/Indicator/stoch.ts +359 -359
  164. package/src/engine/renderers/Indicator/structure.ts +126 -126
  165. package/src/engine/renderers/Indicator/subPaneConfig.ts +265 -265
  166. package/src/engine/renderers/Indicator/supertrend.ts +115 -115
  167. package/src/engine/renderers/Indicator/tema.ts +136 -136
  168. package/src/engine/renderers/Indicator/trix.ts +158 -158
  169. package/src/engine/renderers/Indicator/vma.ts +124 -124
  170. package/src/engine/renderers/Indicator/volumeProfile.ts +125 -125
  171. package/src/engine/renderers/Indicator/vwap.ts +123 -123
  172. package/src/engine/renderers/Indicator/wma.ts +136 -136
  173. package/src/engine/renderers/Indicator/wmsr.ts +328 -328
  174. package/src/engine/renderers/Indicator/zones.ts +104 -104
  175. package/src/engine/renderers/__tests__/boll.renderer.test.ts +314 -314
  176. package/src/engine/renderers/__tests__/ene.renderer.test.ts +305 -305
  177. package/src/engine/renderers/__tests__/expma.renderer.test.ts +279 -279
  178. package/src/engine/renderers/__tests__/ma.renderer.test.ts +426 -426
  179. package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +502 -502
  180. package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +173 -173
  181. package/src/engine/renderers/candle.ts +459 -459
  182. package/src/engine/renderers/crosshair.ts +69 -69
  183. package/src/engine/renderers/customMarkers.ts +162 -162
  184. package/src/engine/renderers/extremaMarkers.ts +246 -246
  185. package/src/engine/renderers/gridLines.ts +90 -90
  186. package/src/engine/renderers/lastPrice.ts +97 -97
  187. package/src/engine/renderers/paneTitle.ts +136 -136
  188. package/src/engine/renderers/subVolume.ts +236 -236
  189. package/src/engine/renderers/timeAxis.ts +121 -121
  190. package/src/engine/renderers/webgl/candleSurface.ts +955 -955
  191. package/src/engine/renderers/webgl/sharedWebGLSurface.ts +146 -146
  192. package/src/engine/renderers/yAxis.ts +105 -105
  193. package/src/engine/scale/__tests__/logFormula.spec.ts +148 -148
  194. package/src/engine/scale/logFormula.ts +130 -130
  195. package/src/engine/scale/price.ts +39 -39
  196. package/src/engine/scale/priceScale.ts +264 -264
  197. package/src/engine/subPaneManager.ts +427 -427
  198. package/src/engine/theme/colors.ts +642 -642
  199. package/src/engine/theme/fonts.ts +20 -20
  200. package/src/engine/utils/klineConfig.ts +49 -49
  201. package/src/engine/utils/tickCount.ts +11 -11
  202. package/src/engine/utils/tickPosition.ts +214 -214
  203. package/src/engine/utils/zoom.ts +83 -83
  204. package/src/engine/viewport/viewport.ts +67 -67
  205. package/src/index.ts +3 -3
  206. package/src/plugin/ConfigManager.ts +93 -93
  207. package/src/plugin/EventBus.ts +77 -77
  208. package/src/plugin/HookSystem.ts +106 -106
  209. package/src/plugin/PluginHost.ts +243 -243
  210. package/src/plugin/PluginRegistry.ts +92 -92
  211. package/src/plugin/StateStore.ts +73 -73
  212. package/src/plugin/index.ts +19 -19
  213. package/src/plugin/rendererPluginManager.ts +368 -368
  214. package/src/plugin/stateKeys.ts +8 -8
  215. package/src/plugin/types.ts +526 -526
  216. package/src/reactivity/index.ts +2 -2
  217. package/src/reactivity/signal.ts +119 -119
  218. package/src/semantic/controller.ts +251 -251
  219. package/src/semantic/drawShape.ts +260 -260
  220. package/src/semantic/index.ts +28 -28
  221. package/src/semantic/schema.json +256 -256
  222. package/src/semantic/types.ts +251 -251
  223. package/src/semantic/validator.ts +349 -349
  224. package/src/types/kLine.ts +13 -13
  225. package/src/types/price.ts +56 -56
  226. package/src/types/volumePrice.ts +33 -33
  227. package/src/utils/dateFormat.ts +208 -208
  228. package/src/utils/kLineDraw/axis.ts +562 -562
  229. package/src/utils/priceToY.ts +34 -34
  230. package/src/utils/volumePrice.ts +202 -202
  231. 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
+ }