@363045841yyt/klinechart-core 0.9.0-alpha.2 → 0.9.0-alpha.4

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 (234) hide show
  1. package/dist/controllers/createChartController.d.ts.map +1 -1
  2. package/dist/controllers/createChartController.js +9 -8
  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 +5 -0
  8. package/dist/controllers/types.d.ts.map +1 -1
  9. package/dist/data/binance.d.ts +1 -1
  10. package/dist/data/binance.d.ts.map +1 -1
  11. package/dist/data/binance.js +1 -1
  12. package/dist/data/binance.js.map +1 -1
  13. package/dist/data/timeShareBuffer.d.ts +3 -0
  14. package/dist/data/timeShareBuffer.d.ts.map +1 -1
  15. package/dist/data/timeShareBuffer.js +13 -0
  16. package/dist/data/timeShareBuffer.js.map +1 -1
  17. package/dist/engine/chart.d.ts +9 -9
  18. package/dist/engine/chart.d.ts.map +1 -1
  19. package/dist/engine/chart.js +87 -101
  20. package/dist/engine/chart.js.map +1 -1
  21. package/dist/engine/chartTypes.d.ts +5 -0
  22. package/dist/engine/chartTypes.d.ts.map +1 -1
  23. package/dist/engine/controller/interaction.d.ts +4 -2
  24. package/dist/engine/controller/interaction.d.ts.map +1 -1
  25. package/dist/engine/controller/interaction.js +18 -10
  26. package/dist/engine/controller/interaction.js.map +1 -1
  27. package/dist/engine/data/chartDataManager.d.ts +14 -18
  28. package/dist/engine/data/chartDataManager.d.ts.map +1 -1
  29. package/dist/engine/data/chartDataManager.js +55 -37
  30. package/dist/engine/data/chartDataManager.js.map +1 -1
  31. package/dist/engine/data/incrementalLoadHint.d.ts +2 -4
  32. package/dist/engine/data/incrementalLoadHint.d.ts.map +1 -1
  33. package/dist/engine/data/incrementalLoadHint.js +5 -2
  34. package/dist/engine/data/incrementalLoadHint.js.map +1 -1
  35. package/dist/engine/data/scrollCompensator.d.ts +3 -13
  36. package/dist/engine/data/scrollCompensator.d.ts.map +1 -1
  37. package/dist/engine/data/scrollCompensator.js +13 -14
  38. package/dist/engine/data/scrollCompensator.js.map +1 -1
  39. package/dist/engine/indicators/chartIndicatorManager.d.ts +16 -17
  40. package/dist/engine/indicators/chartIndicatorManager.d.ts.map +1 -1
  41. package/dist/engine/indicators/chartIndicatorManager.js +37 -37
  42. package/dist/engine/indicators/chartIndicatorManager.js.map +1 -1
  43. package/dist/engine/layout/chartPaneLayout.d.ts +11 -7
  44. package/dist/engine/layout/chartPaneLayout.d.ts.map +1 -1
  45. package/dist/engine/layout/chartPaneLayout.js +58 -16
  46. package/dist/engine/layout/chartPaneLayout.js.map +1 -1
  47. package/dist/engine/modes/timeShareMath.d.ts +56 -0
  48. package/dist/engine/modes/timeShareMath.d.ts.map +1 -0
  49. package/dist/engine/modes/timeShareMath.js +90 -0
  50. package/dist/engine/modes/timeShareMath.js.map +1 -0
  51. package/dist/engine/modes/timeShareMode.d.ts +5 -0
  52. package/dist/engine/modes/timeShareMode.d.ts.map +1 -1
  53. package/dist/engine/modes/timeShareMode.js +27 -27
  54. package/dist/engine/modes/timeShareMode.js.map +1 -1
  55. package/dist/engine/paneRenderer.d.ts +2 -0
  56. package/dist/engine/paneRenderer.d.ts.map +1 -1
  57. package/dist/engine/paneRenderer.js +15 -5
  58. package/dist/engine/paneRenderer.js.map +1 -1
  59. package/dist/engine/render/chartRenderer.d.ts +28 -22
  60. package/dist/engine/render/chartRenderer.d.ts.map +1 -1
  61. package/dist/engine/render/chartRenderer.js +143 -85
  62. package/dist/engine/render/chartRenderer.js.map +1 -1
  63. package/dist/engine/render/layers/leftYAxisLayer.d.ts +9 -2
  64. package/dist/engine/render/layers/leftYAxisLayer.d.ts.map +1 -1
  65. package/dist/engine/render/layers/leftYAxisLayer.js +11 -2
  66. package/dist/engine/render/layers/leftYAxisLayer.js.map +1 -1
  67. package/dist/engine/render/layers/mainIndicatorLegendLayer.d.ts +6 -4
  68. package/dist/engine/render/layers/mainIndicatorLegendLayer.d.ts.map +1 -1
  69. package/dist/engine/render/layers/mainIndicatorLegendLayer.js +6 -9
  70. package/dist/engine/render/layers/mainIndicatorLegendLayer.js.map +1 -1
  71. package/dist/engine/render/layers/mainIndicatorLegendLayer.test.d.ts +2 -0
  72. package/dist/engine/render/layers/mainIndicatorLegendLayer.test.d.ts.map +1 -0
  73. package/dist/engine/render/layers/mainIndicatorLegendLayer.test.js +19 -0
  74. package/dist/engine/render/layers/mainIndicatorLegendLayer.test.js.map +1 -0
  75. package/dist/engine/render/layers/yAxisLayer.d.ts +9 -2
  76. package/dist/engine/render/layers/yAxisLayer.d.ts.map +1 -1
  77. package/dist/engine/render/layers/yAxisLayer.js +11 -2
  78. package/dist/engine/render/layers/yAxisLayer.js.map +1 -1
  79. package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts +7 -3
  80. package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts.map +1 -1
  81. package/dist/engine/renderers/Indicator/mainIndicatorLegend.js +234 -330
  82. package/dist/engine/renderers/Indicator/mainIndicatorLegend.js.map +1 -1
  83. package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.d.ts +73 -0
  84. package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.d.ts.map +1 -0
  85. package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.js +182 -0
  86. package/dist/engine/renderers/Indicator/mainIndicatorLegendContext.js.map +1 -0
  87. package/dist/engine/renderers/leftYAxis.d.ts +15 -2
  88. package/dist/engine/renderers/leftYAxis.d.ts.map +1 -1
  89. package/dist/engine/renderers/leftYAxis.js +56 -16
  90. package/dist/engine/renderers/leftYAxis.js.map +1 -1
  91. package/dist/engine/renderers/timeShare.d.ts.map +1 -1
  92. package/dist/engine/renderers/timeShare.js +20 -9
  93. package/dist/engine/renderers/timeShare.js.map +1 -1
  94. package/dist/engine/renderers/yAxis.d.ts +16 -6
  95. package/dist/engine/renderers/yAxis.d.ts.map +1 -1
  96. package/dist/engine/renderers/yAxis.js +49 -16
  97. package/dist/engine/renderers/yAxis.js.map +1 -1
  98. package/dist/engine/state/viewportState.d.ts +4 -1
  99. package/dist/engine/state/viewportState.d.ts.map +1 -1
  100. package/dist/engine/state/viewportState.js +40 -8
  101. package/dist/engine/state/viewportState.js.map +1 -1
  102. package/dist/engine/subPaneManager.d.ts +5 -3
  103. package/dist/engine/subPaneManager.d.ts.map +1 -1
  104. package/dist/engine/subPaneManager.js +3 -2
  105. package/dist/engine/subPaneManager.js.map +1 -1
  106. package/dist/engine/utils/chartZoomController.d.ts +9 -7
  107. package/dist/engine/utils/chartZoomController.d.ts.map +1 -1
  108. package/dist/engine/utils/chartZoomController.js +13 -12
  109. package/dist/engine/utils/chartZoomController.js.map +1 -1
  110. package/dist/engine/viewport/chartViewportManager.d.ts +5 -16
  111. package/dist/engine/viewport/chartViewportManager.d.ts.map +1 -1
  112. package/dist/engine/viewport/chartViewportManager.js +7 -48
  113. package/dist/engine/viewport/chartViewportManager.js.map +1 -1
  114. package/dist/engine/viewport/viewport.d.ts +14 -0
  115. package/dist/engine/viewport/viewport.d.ts.map +1 -1
  116. package/dist/engine/viewport/viewport.js +10 -0
  117. package/dist/engine/viewport/viewport.js.map +1 -1
  118. package/dist/foundation/config/chartSettings.d.ts +0 -5
  119. package/dist/foundation/config/chartSettings.d.ts.map +1 -1
  120. package/dist/foundation/config/chartSettings.js +14 -0
  121. package/dist/foundation/config/chartSettings.js.map +1 -1
  122. package/dist/foundation/plugin/rendererPluginManager.test.js +15 -0
  123. package/dist/foundation/plugin/rendererPluginManager.test.js.map +1 -1
  124. package/dist/foundation/plugin/types.d.ts +3 -0
  125. package/dist/foundation/plugin/types.d.ts.map +1 -1
  126. package/dist/foundation/plugin/types.js.map +1 -1
  127. package/dist/foundation/reactivity/frameTransaction.d.ts +11 -0
  128. package/dist/foundation/reactivity/frameTransaction.d.ts.map +1 -1
  129. package/dist/foundation/reactivity/frameTransaction.js +17 -2
  130. package/dist/foundation/reactivity/frameTransaction.js.map +1 -1
  131. package/dist/foundation/utils/kLineDraw/axis.d.ts +3 -0
  132. package/dist/foundation/utils/kLineDraw/axis.d.ts.map +1 -1
  133. package/dist/foundation/utils/kLineDraw/axis.js +40 -9
  134. package/dist/foundation/utils/kLineDraw/axis.js.map +1 -1
  135. package/dist/foundation/utils/sessionTimeLabels.d.ts +59 -0
  136. package/dist/foundation/utils/sessionTimeLabels.d.ts.map +1 -0
  137. package/dist/foundation/utils/sessionTimeLabels.js +170 -0
  138. package/dist/foundation/utils/sessionTimeLabels.js.map +1 -0
  139. package/dist/foundation/utils/timeShareAxisLabels.d.ts +27 -0
  140. package/dist/foundation/utils/timeShareAxisLabels.d.ts.map +1 -0
  141. package/dist/foundation/utils/timeShareAxisLabels.js +45 -0
  142. package/dist/foundation/utils/timeShareAxisLabels.js.map +1 -0
  143. package/dist/rendering/render/Renderer.d.ts +58 -74
  144. package/dist/rendering/render/Renderer.d.ts.map +1 -1
  145. package/dist/rendering/render/Renderer.js +8 -18
  146. package/dist/rendering/render/Renderer.js.map +1 -1
  147. package/dist/rendering/render/createWebGPURenderer.d.ts.map +1 -1
  148. package/dist/rendering/render/createWebGPURenderer.js +30 -17
  149. package/dist/rendering/render/createWebGPURenderer.js.map +1 -1
  150. package/dist/rendering/render/createWebGPUSurfaceBackend.js +3 -3
  151. package/dist/rendering/render/createWebGPUSurfaceBackend.js.map +1 -1
  152. package/dist/rendering/render/physicalLine.d.ts +4 -0
  153. package/dist/rendering/render/physicalLine.d.ts.map +1 -0
  154. package/dist/rendering/render/physicalLine.js +37 -0
  155. package/dist/rendering/render/physicalLine.js.map +1 -0
  156. package/dist/rendering/render/physicalRegion.d.ts +14 -0
  157. package/dist/rendering/render/physicalRegion.d.ts.map +1 -0
  158. package/dist/rendering/render/physicalRegion.js +15 -0
  159. package/dist/rendering/render/physicalRegion.js.map +1 -0
  160. package/dist/rendering/scene/createLayerFromPlugin.d.ts.map +1 -1
  161. package/dist/rendering/scene/createLayerFromPlugin.js +8 -3
  162. package/dist/rendering/scene/createLayerFromPlugin.js.map +1 -1
  163. package/dist/rendering/scene/createScene.d.ts.map +1 -1
  164. package/dist/rendering/scene/createScene.js +4 -3
  165. package/dist/rendering/scene/createScene.js.map +1 -1
  166. package/dist/version.d.ts +1 -1
  167. package/dist/version.js +1 -1
  168. package/package.json +1 -1
  169. package/src/__tests__/stateKernel.test.ts +1 -0
  170. package/src/controllers/createChartController.ts +9 -8
  171. package/src/controllers/index.ts +11 -0
  172. package/src/controllers/types.ts +7 -0
  173. package/src/data/__tests__/timeShareBuffer.test.ts +45 -0
  174. package/src/data/binance.ts +1 -1
  175. package/src/data/timeShareBuffer.ts +15 -0
  176. package/src/engine/__tests__/chart.dpr.test.ts +31 -2
  177. package/src/engine/__tests__/paneRenderer.resize.test.ts +52 -54
  178. package/src/engine/__tests__/subPaneManager.test.ts +5 -3
  179. package/src/engine/chart.ts +105 -115
  180. package/src/engine/chartTypes.ts +5 -0
  181. package/src/engine/controller/__tests__/interaction.dpr.test.ts +39 -2
  182. package/src/engine/controller/interaction.ts +18 -10
  183. package/src/engine/data/__tests__/chartDataManager.incrementalLoad.test.ts +37 -17
  184. package/src/engine/data/__tests__/scrollCompensator.geometry.test.ts +87 -75
  185. package/src/engine/data/chartDataManager.ts +64 -59
  186. package/src/engine/data/incrementalLoadHint.ts +8 -7
  187. package/src/engine/data/scrollCompensator.ts +16 -24
  188. package/src/engine/indicators/__tests__/chartIndicatorManager.test.ts +18 -26
  189. package/src/engine/indicators/chartIndicatorManager.ts +62 -62
  190. package/src/engine/layout/chartPaneLayout.ts +73 -21
  191. package/src/engine/modes/__tests__/timeShareMath.test.ts +185 -0
  192. package/src/engine/modes/__tests__/timeShareMode.test.ts +81 -0
  193. package/src/engine/modes/timeShareMath.ts +150 -0
  194. package/src/engine/modes/timeShareMode.ts +35 -28
  195. package/src/engine/paneRenderer.ts +17 -10
  196. package/src/engine/render/chartRenderer.ts +214 -145
  197. package/src/engine/render/layers/leftYAxisLayer.ts +29 -7
  198. package/src/engine/render/layers/mainIndicatorLegendLayer.test.ts +26 -0
  199. package/src/engine/render/layers/mainIndicatorLegendLayer.ts +13 -10
  200. package/src/engine/render/layers/yAxisLayer.ts +29 -7
  201. package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +270 -403
  202. package/src/engine/renderers/Indicator/mainIndicatorLegendContext.ts +290 -0
  203. package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +73 -2
  204. package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +10 -6
  205. package/src/engine/renderers/leftYAxis.ts +60 -18
  206. package/src/engine/renderers/timeShare.ts +25 -9
  207. package/src/engine/renderers/yAxis.ts +57 -22
  208. package/src/engine/state/__tests__/settingsState.test.ts +9 -0
  209. package/src/engine/state/viewportState.ts +44 -8
  210. package/src/engine/subPaneManager.ts +8 -5
  211. package/src/engine/utils/chartZoomController.ts +22 -19
  212. package/src/engine/viewport/__tests__/visibleRange.clamp.test.ts +50 -0
  213. package/src/engine/viewport/chartViewportManager.ts +9 -60
  214. package/src/engine/viewport/viewport.ts +14 -0
  215. package/src/foundation/config/chartSettings.ts +13 -0
  216. package/src/foundation/plugin/rendererPluginManager.test.ts +18 -0
  217. package/src/foundation/plugin/types.ts +3 -0
  218. package/src/foundation/reactivity/frameTransaction.ts +20 -6
  219. package/src/foundation/utils/__tests__/sessionTimeLabels.test.ts +91 -0
  220. package/src/foundation/utils/kLineDraw/axis.ts +50 -9
  221. package/src/foundation/utils/sessionTimeLabels.ts +237 -0
  222. package/src/foundation/utils/timeShareAxisLabels.ts +94 -0
  223. package/src/rendering/render/Renderer.ts +69 -85
  224. package/src/rendering/render/__tests__/physicalLine.test.ts +51 -0
  225. package/src/rendering/render/__tests__/physicalRegion.test.ts +23 -0
  226. package/src/rendering/render/__tests__/webgpuRenderer.test.ts +31 -1
  227. package/src/rendering/render/__tests__/webgpuSurfaceBackend.test.ts +11 -0
  228. package/src/rendering/render/createWebGPURenderer.ts +30 -17
  229. package/src/rendering/render/createWebGPUSurfaceBackend.ts +3 -3
  230. package/src/rendering/render/physicalLine.ts +45 -0
  231. package/src/rendering/render/physicalRegion.ts +32 -0
  232. package/src/rendering/scene/createLayerFromPlugin.ts +8 -3
  233. package/src/rendering/scene/createScene.ts +4 -3
  234. package/src/version.ts +1 -1
@@ -2,10 +2,12 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2
2
  import { JSDOM } from 'jsdom'
3
3
 
4
4
  import type { DataFetcher, KLineData, SymbolSpec } from '../../../controllers/types'
5
- import { computed, createSignal } from '../../../foundation/reactivity/signal'
5
+ import { createSignal } from '../../../foundation/reactivity/signal'
6
6
  import type { ChartDom } from '../../chartTypes'
7
+ import { createComparisonState } from '../../state/comparisonState'
7
8
  import { createDataManagerState } from '../../state/dataManagerState'
8
9
  import { createDataState } from '../../state/dataState'
10
+ import type { ViewportStateModule } from '../../state/viewportState'
9
11
  import { ChartDataManager, type DataDependencies } from '../chartDataManager'
10
12
 
11
13
  const MS_PER_DAY = 86_400_000
@@ -21,26 +23,48 @@ function makeKLine(timestamp: number): KLineData {
21
23
  }
22
24
  }
23
25
 
26
+ function createMockViewport(scrollLeft = 800): ViewportStateModule {
27
+ let scroll = scrollLeft
28
+ return {
29
+ readonly: {
30
+ dpr: { peek: () => 1 },
31
+ scrollLeft: { peek: () => scroll },
32
+ scrollLeftLogical: { peek: () => scroll },
33
+ leftLoadBufferWidth: { peek: () => 800 },
34
+ contentWidth: { peek: () => 1600 },
35
+ viewWidth: { peek: () => 800 },
36
+ viewHeight: { peek: () => 600 },
37
+ visibleRange: { peek: () => ({ start: 0, end: 0 }) },
38
+ rawVisibleRange: { peek: () => ({ start: 0, end: 0 }) },
39
+ viewport: {
40
+ peek: () => ({
41
+ viewWidth: 800,
42
+ viewHeight: 600,
43
+ plotWidth: 800,
44
+ plotHeight: 600,
45
+ scrollLeft: scroll,
46
+ dpr: 1,
47
+ }),
48
+ },
49
+ },
50
+ actions: {
51
+ scrollTo: (v: number) => {
52
+ scroll = v
53
+ },
54
+ },
55
+ } as unknown as ViewportStateModule
56
+ }
57
+
24
58
  function createDependencies(
25
59
  dom: ChartDom,
26
60
  setSymbols: (symbols: ReadonlyArray<SymbolSpec>) => void,
27
61
  symbols$: ReturnType<typeof createSignal<ReadonlyArray<SymbolSpec>>>,
28
62
  ): DataDependencies {
29
- let scrollLeft = 800
30
63
  return {
31
64
  getOption: () => ({ kWidth: 8, kGap: 2 }),
32
- getEffectiveDpr: () => 1,
33
- getLogicalScrollLeft: () => scrollLeft,
34
- getCachedScrollLeft: () => scrollLeft,
35
- setScrollLeft: (value) => {
36
- scrollLeft = value
37
- },
38
65
  getDom: () => dom,
39
- getObservedSize: () => ({ width: 800, height: 600 }),
40
- getViewport: () => null,
41
- getVisibleRange: () => null,
42
- getLeftLoadBufferWidth: () => 800,
43
- getContentWidth: () => 1600,
66
+ viewport: createMockViewport(),
67
+ comparison: createComparisonState({ symbols$ }),
44
68
  scheduleDraw: () => {},
45
69
  resetInteraction: () => {},
46
70
  getIndicatorScheduler: () => ({
@@ -50,10 +74,6 @@ function createDependencies(
50
74
  isPointerDown: () => false,
51
75
  onTimeShareDataReady: () => {},
52
76
  setSymbols,
53
- setComparisonLoading: () => {},
54
- comparisonSpecs$: computed(() => symbols$().slice(1)),
55
- comparisonColors$: createSignal(new Map() as ReadonlyMap<string, string>),
56
- comparisonLoading$: createSignal(false),
57
77
  }
58
78
  }
59
79
 
@@ -1,18 +1,56 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
 
3
- import type { ChartDom, Viewport } from '../../chartTypes'
4
3
  import { getPhysicalKLineConfig } from '../../utils/klineConfig'
5
4
  import {
6
5
  computeContentWidth,
7
6
  computeLeftLoadBufferWidth,
8
7
  computeMaxScrollLeft,
9
8
  } from '../../state/contentGeometry'
9
+ import type { ViewportStateModule } from '../../state/viewportState'
10
10
  import { ScrollCompensator, type ScrollDeps } from '../scrollCompensator'
11
11
 
12
- function makeDom(): ChartDom {
12
+ function makeViewportModule(options: {
13
+ scrollLeft?: number
14
+ leftBuffer?: number
15
+ contentWidth?: number
16
+ viewWidth?: number
17
+ dpr?: number
18
+ }): { viewport: ViewportStateModule; getScrollLeft: () => number } {
19
+ let scrollLeft = options.scrollLeft ?? 0
20
+ const viewWidth = options.viewWidth ?? 800
21
+ const dpr = options.dpr ?? 1
22
+ const leftBuffer = options.leftBuffer ?? 800
23
+ const contentWidth = options.contentWidth ?? 1600
24
+
25
+ const viewport = {
26
+ readonly: {
27
+ dpr: { peek: () => dpr },
28
+ scrollLeft: { peek: () => scrollLeft },
29
+ leftLoadBufferWidth: { peek: () => leftBuffer },
30
+ contentWidth: { peek: () => contentWidth },
31
+ viewWidth: { peek: () => viewWidth },
32
+ viewHeight: { peek: () => 600 },
33
+ viewport: {
34
+ peek: () => ({
35
+ viewWidth,
36
+ viewHeight: 600,
37
+ plotWidth: viewWidth,
38
+ plotHeight: 600,
39
+ scrollLeft,
40
+ dpr,
41
+ }),
42
+ },
43
+ },
44
+ actions: {
45
+ scrollTo: (v: number) => {
46
+ scrollLeft = v
47
+ },
48
+ },
49
+ } as unknown as ViewportStateModule
50
+
13
51
  return {
14
- container: { clientWidth: 800 } as HTMLDivElement,
15
- scrollContent: undefined,
52
+ viewport,
53
+ getScrollLeft: () => scrollLeft,
16
54
  }
17
55
  }
18
56
 
@@ -23,7 +61,6 @@ function makeDeps(options: {
23
61
  viewWidth?: number
24
62
  dataLength?: number
25
63
  } = {}): { deps: ScrollDeps; getScrollLeft: () => number } {
26
- let scrollLeft = options.scrollLeft ?? 0
27
64
  const viewWidth = options.viewWidth ?? 800
28
65
  const dataLength = options.dataLength ?? 100
29
66
  const kWidth = 8
@@ -53,32 +90,20 @@ function makeDeps(options: {
53
90
  kGap,
54
91
  })
55
92
 
56
- const viewport: Viewport = {
93
+ const { viewport, getScrollLeft } = makeViewportModule({
94
+ scrollLeft: options.scrollLeft ?? 0,
95
+ leftBuffer,
96
+ contentWidth,
57
97
  viewWidth,
58
- viewHeight: 600,
59
- plotWidth: viewWidth,
60
- plotHeight: 600,
61
- scrollLeft,
62
98
  dpr,
63
- }
64
-
65
- const deps: ScrollDeps = {
66
- getOption: () => ({ kWidth, kGap }),
67
- getEffectiveDpr: () => dpr,
68
- getCachedScrollLeft: () => scrollLeft,
69
- setScrollLeft: (v) => {
70
- scrollLeft = v
71
- },
72
- getDom: () => makeDom(),
73
- getObservedSize: () => ({ width: viewWidth, height: 600 }),
74
- getViewport: () => viewport,
75
- getLeftLoadBufferWidth: () => leftBuffer,
76
- getContentWidth: () => contentWidth,
77
- }
99
+ })
78
100
 
79
101
  return {
80
- deps,
81
- getScrollLeft: () => scrollLeft,
102
+ deps: {
103
+ getOption: () => ({ kWidth, kGap }),
104
+ viewport,
105
+ },
106
+ getScrollLeft,
82
107
  }
83
108
  }
84
109
 
@@ -89,7 +114,7 @@ describe('ScrollCompensator geometry SSOT', () => {
89
114
  const kWidth = 8
90
115
  const kGap = 2
91
116
  const dpr = 1
92
- const input = {
117
+ const leftBuffer = computeLeftLoadBufferWidth({
93
118
  viewWidth,
94
119
  plotWidth: viewWidth,
95
120
  dataLength,
@@ -97,27 +122,28 @@ describe('ScrollCompensator geometry SSOT', () => {
97
122
  dpr,
98
123
  kWidth,
99
124
  kGap,
100
- }
101
- const leftBuffer = computeLeftLoadBufferWidth(input)
102
- const contentWidth = computeContentWidth(input)
125
+ })
126
+ const contentWidth = computeContentWidth({
127
+ viewWidth,
128
+ plotWidth: viewWidth,
129
+ dataLength,
130
+ period: 'daily',
131
+ dpr,
132
+ kWidth,
133
+ kGap,
134
+ })
103
135
  const maxScroll = computeMaxScrollLeft(contentWidth, viewWidth)
104
136
 
105
137
  const { deps, getScrollLeft } = makeDeps({
106
- viewWidth,
107
- dataLength,
138
+ scrollLeft: 0,
108
139
  leftBuffer,
109
140
  contentWidth,
110
- scrollLeft: 0,
141
+ viewWidth,
142
+ dataLength,
111
143
  })
112
-
113
- const compensator = new ScrollCompensator(deps)
114
- compensator.scrollToRight(dataLength)
115
-
144
+ new ScrollCompensator(deps).scrollToRight(dataLength)
116
145
  const scrollLeft = getScrollLeft()
117
- expect(scrollLeft).toBeLessThanOrEqual(maxScroll)
118
- expect(scrollLeft).toBeGreaterThanOrEqual(0)
119
146
 
120
- // target 计算应与 clamp 后一致(数据足够长时贴右)
121
147
  const { unitPx, startXPx } = getPhysicalKLineConfig(kWidth, kGap, dpr)
122
148
  const lastKLineEndPx = (startXPx + dataLength * unitPx) / dpr
123
149
  const rawTarget = leftBuffer + (lastKLineEndPx - viewWidth)
@@ -125,60 +151,46 @@ describe('ScrollCompensator geometry SSOT', () => {
125
151
  expect(scrollLeft).toBe(expected)
126
152
  })
127
153
 
128
- it('scrollToRight uses deps.getContentWidth / getLeftLoadBufferWidth, not local formulas', () => {
154
+ it('scrollToRight uses viewport contentWidth / leftLoadBufferWidth, not local formulas', () => {
129
155
  const injectedLeft = 111
130
156
  const injectedContent = 500
131
157
  const viewWidth = 400
132
- let scrollLeft = 0
158
+ const { viewport, getScrollLeft } = makeViewportModule({
159
+ scrollLeft: 0,
160
+ leftBuffer: injectedLeft,
161
+ contentWidth: injectedContent,
162
+ viewWidth,
163
+ dpr: 1,
164
+ })
133
165
 
134
166
  const deps: ScrollDeps = {
135
167
  getOption: () => ({ kWidth: 8, kGap: 2 }),
136
- getEffectiveDpr: () => 1,
137
- getCachedScrollLeft: () => scrollLeft,
138
- setScrollLeft: (v) => {
139
- scrollLeft = v
140
- },
141
- getDom: () => makeDom(),
142
- getObservedSize: () => ({ width: viewWidth, height: 600 }),
143
- getViewport: () => ({
144
- viewWidth,
145
- viewHeight: 600,
146
- plotWidth: viewWidth,
147
- plotHeight: 600,
148
- scrollLeft: 0,
149
- dpr: 1,
150
- }),
151
- getLeftLoadBufferWidth: () => injectedLeft,
152
- getContentWidth: () => injectedContent,
168
+ viewport,
153
169
  }
154
170
 
155
171
  const compensator = new ScrollCompensator(deps)
156
172
  compensator.scrollToRight(50)
157
173
 
158
174
  const maxScroll = Math.max(0, injectedContent - viewWidth)
159
- expect(scrollLeft).toBeLessThanOrEqual(maxScroll)
160
- // leftBuffer 来自注入值,目标会基于 111 计算并 clamp
161
- expect(scrollLeft).toBe(maxScroll)
175
+ expect(getScrollLeft()).toBeLessThanOrEqual(maxScroll)
176
+ expect(getScrollLeft()).toBe(maxScroll)
162
177
  })
163
178
 
164
179
  it('adjustScrollAfterDataChange uses injected left buffer when scrollLeft <= 0', () => {
165
- let scrollLeft = 0
166
180
  const injectedLeft = 640
181
+ const { viewport, getScrollLeft } = makeViewportModule({
182
+ scrollLeft: 0,
183
+ leftBuffer: injectedLeft,
184
+ contentWidth: 2000,
185
+ viewWidth: 800,
186
+ dpr: 1,
187
+ })
167
188
  const deps: ScrollDeps = {
168
189
  getOption: () => ({ kWidth: 8, kGap: 2 }),
169
- getEffectiveDpr: () => 1,
170
- getCachedScrollLeft: () => scrollLeft,
171
- setScrollLeft: (v) => {
172
- scrollLeft = v
173
- },
174
- getDom: () => makeDom(),
175
- getObservedSize: () => ({ width: 800, height: 600 }),
176
- getViewport: () => null,
177
- getLeftLoadBufferWidth: () => injectedLeft,
178
- getContentWidth: () => 2000,
190
+ viewport,
179
191
  }
180
192
 
181
193
  new ScrollCompensator(deps).adjustScrollAfterDataChange(10)
182
- expect(scrollLeft).toBe(injectedLeft)
194
+ expect(getScrollLeft()).toBe(injectedLeft)
183
195
  })
184
196
  })
@@ -6,12 +6,13 @@ import { TimeShareBuffer } from '../../data/timeShareBuffer'
6
6
  import type { TimeShareFetcherFn } from '../../data/types'
7
7
  import { createSignal, type ReadonlySignal, type Signal } from '../../foundation/reactivity/signal'
8
8
  import type { KLineData, TimeShareData } from '../../foundation/types/price'
9
- import type { ChartDom, Viewport } from '../chartTypes'
9
+ import type { ChartDom } from '../chartTypes'
10
10
  import type { VisibleRange, UpdateLevel } from '../layout/pane'
11
11
  import { getPhysicalKLineConfig } from '../utils/klineConfig'
12
- import { getVisibleRange } from '../viewport/viewport'
13
12
  import type { DataStateModule } from '../state/dataState'
14
13
  import type { DataManagerStateModule } from '../state/dataManagerState'
14
+ import type { ViewportStateModule } from '../state/viewportState'
15
+ import type { ComparisonStateModule } from '../state/comparisonState'
15
16
 
16
17
  import { ComparisonManager } from './comparisonManager'
17
18
  import { FetchBatchScheduler } from './fetchBatchScheduler'
@@ -20,17 +21,11 @@ import { ScrollCompensator } from './scrollCompensator'
20
21
 
21
22
  export interface DataDependencies {
22
23
  getOption: () => { kWidth: number; kGap: number }
23
- getEffectiveDpr: () => number
24
- getLogicalScrollLeft: () => number
25
- getCachedScrollLeft: () => number
26
- setScrollLeft: (v: number) => void
27
24
  getDom: () => ChartDom
28
- getObservedSize: () => { width: number; height: number }
29
- getViewport: () => Viewport | null
30
- getVisibleRange: () => VisibleRange | null
31
- /** 几何 SSOT:由 kernel viewport.readonly 注入 */
32
- getLeftLoadBufferWidth: () => number
33
- getContentWidth: () => number
25
+ /** scroll / dpr / 可见区间 / 几何 SSOT */
26
+ viewport: ViewportStateModule
27
+ /** 对比叠加状态 SSOT */
28
+ comparison: ComparisonStateModule
34
29
  scheduleDraw: (level?: UpdateLevel) => void
35
30
  resetInteraction: () => void
36
31
  getIndicatorScheduler: () => {
@@ -40,11 +35,8 @@ export interface DataDependencies {
40
35
  isPointerDown: () => boolean
41
36
  onTimeShareDataReady: (dataLength: number) => void
42
37
  onDataProcessed?: (data: KLineData[], range: VisibleRange) => void
38
+ /** 写 symbols 选择(含 primary + comparison) */
43
39
  setSymbols: (symbols: ReadonlyArray<SymbolSpec>) => void
44
- setComparisonLoading: (loading: boolean) => void
45
- comparisonSpecs$: ReadonlySignal<ReadonlyArray<SymbolSpec>>
46
- comparisonColors$: ReadonlySignal<ReadonlyMap<string, string>>
47
- comparisonLoading$: ReadonlySignal<boolean>
48
40
  }
49
41
 
50
42
  const BUF_PRIMARY = 'main'
@@ -95,10 +87,10 @@ export class ChartDataManager {
95
87
  getKLineBuffer: (key) => this._klineBuffers.get(key),
96
88
  getKLineBufferKeys: () => [...this._klineBuffers.keys()],
97
89
  scheduleDraw: () => this.deps.scheduleDraw(),
98
- getSpecs: () => this.deps.comparisonSpecs$.peek(),
99
- setLoading: (loading) => this.deps.setComparisonLoading(loading),
90
+ getSpecs: () => this.deps.comparison.readonly.specs.peek(),
91
+ setLoading: (loading) => this.deps.comparison.actions.setLoading(loading),
100
92
  })
101
- this._comparisonSpecsUnsub = this.deps.comparisonSpecs$.subscribe(() => {
93
+ this._comparisonSpecsUnsub = this.deps.comparison.readonly.specs.subscribe(() => {
102
94
  this.reconcileComparisonBuffers()
103
95
  })
104
96
  this.reconcileComparisonBuffers()
@@ -305,7 +297,7 @@ export class ChartDataManager {
305
297
  this._dmState.actions.setRangeInitialized(true)
306
298
  }
307
299
 
308
- let currentRange = this.deps.getVisibleRange()
300
+ let currentRange = this.getVisibleRangeOrNull()
309
301
  if (!currentRange && this._dmState.readonly.rangeInitialized.peek() && bufferData.length > 0) {
310
302
  currentRange = { start: 0, end: bufferData.length }
311
303
  }
@@ -325,7 +317,7 @@ export class ChartDataManager {
325
317
  }
326
318
 
327
319
  private recordIncrementalLoad(prependedCount: number): void {
328
- this._dmState.actions.recordIncrementalLoad(prependedCount, this.deps.getLeftLoadBufferWidth())
320
+ this._dmState.actions.recordIncrementalLoad(prependedCount, this.deps.viewport.readonly.leftLoadBufferWidth.peek())
329
321
  }
330
322
 
331
323
  private scheduleIncrementalLoadHintFlush(key: string): void {
@@ -370,7 +362,7 @@ export class ChartDataManager {
370
362
  // ── Internal helpers ──
371
363
 
372
364
  getLeftLoadBufferWidth(): number {
373
- return this.deps.getLeftLoadBufferWidth()
365
+ return this.deps.viewport.readonly.leftLoadBufferWidth.peek()
374
366
  }
375
367
 
376
368
  private getActiveKLineLength(): number {
@@ -378,9 +370,21 @@ export class ChartDataManager {
378
370
  return buf ? buf.getRawData().length : 0
379
371
  }
380
372
 
373
+ /** 无 viewport / 无数据时返回 null;clamped 可索引区间(start>=0) */
374
+ private getVisibleRangeOrNull(): VisibleRange | null {
375
+ if (this.deps.viewport.readonly.viewWidth.peek() === 0) return null
376
+ return this.deps.viewport.readonly.visibleRange.peek()
377
+ }
378
+
379
+ /** raw 可见区间(含左右扩窗,start 可能为 -1);供增量加载左缘检测 */
380
+ private getRawVisibleRangeOrNull(): VisibleRange | null {
381
+ if (this.deps.viewport.readonly.viewWidth.peek() === 0) return null
382
+ return this.deps.viewport.readonly.rawVisibleRange.peek()
383
+ }
384
+
381
385
  /** 当前可见范围(on-demand 实时计算,消除 stale 缓存) */
382
386
  getCurrentVisibleRange(): VisibleRange | null {
383
- return this.deps.getVisibleRange()
387
+ return this.getVisibleRangeOrNull()
384
388
  }
385
389
 
386
390
  /** Unified data signal — always reflects the active buffer's data */
@@ -448,6 +452,16 @@ export class ChartDataManager {
448
452
  return buf ? buf.getRawData() : []
449
453
  }
450
454
 
455
+ getTimeSharePreClose(): number | null {
456
+ const buf = this.getActiveTimeShareBuffer()
457
+ return buf?.getPreClose() ?? null
458
+ }
459
+
460
+ setTimeSharePreClose(preClose: number | null): void {
461
+ const buf = this.getActiveTimeShareBuffer()
462
+ if (buf) buf.setPreClose(preClose)
463
+ }
464
+
451
465
  getTimeShareSignal(): ReadonlySignal<ReadonlyArray<TimeShareData>> {
452
466
  const buf = this.getActiveTimeShareBuffer()
453
467
  return (buf?.data ?? createSignal<ReadonlyArray<TimeShareData>>([])) as ReadonlySignal<
@@ -469,7 +483,7 @@ export class ChartDataManager {
469
483
  }
470
484
 
471
485
  getComparisonSpecs(): SymbolSpec[] {
472
- return this.deps.comparisonSpecs$.peek().map((spec) => ({ ...spec }))
486
+ return this.deps.comparison.readonly.specs.peek().map((spec) => ({ ...spec }))
473
487
  }
474
488
 
475
489
  get dataBuffer(): KLineBuffer {
@@ -485,15 +499,15 @@ export class ChartDataManager {
485
499
  }
486
500
 
487
501
  get comparisonColors(): ReadonlySignal<ReadonlyMap<string, string>> {
488
- return this.deps.comparisonColors$
502
+ return this.deps.comparison.readonly.colors
489
503
  }
490
504
 
491
505
  get comparisonLoading(): ReadonlySignal<boolean> {
492
- return this.deps.comparisonLoading$
506
+ return this.deps.comparison.readonly.loading
493
507
  }
494
508
 
495
509
  getComparisonColors(): Map<string, string> {
496
- return new Map(this.deps.comparisonColors$.peek())
510
+ return new Map(this.deps.comparison.readonly.colors.peek())
497
511
  }
498
512
 
499
513
  // ── Data updates (KLine) ──
@@ -554,20 +568,22 @@ export class ChartDataManager {
554
568
  if (data.length === 0) return
555
569
  const window = buf.loadedWindow
556
570
  if (!window) return
557
- const range = this.deps.getVisibleRange()
558
- if (!range) return
571
+ // 左缘扩窗检测必须用 raw(start 可为 -1);数据下标用 clamped
572
+ const rawRange = this.getRawVisibleRangeOrNull()
573
+ const range = this.getVisibleRangeOrNull()
574
+ if (!rawRange || !range) return
559
575
 
560
576
  const MS_PER_DAY = 86_400_000
561
577
  const spec = buf.currentSpec
562
578
  const gapDays = getPeriodDays(spec?.period)
563
579
  let firstVisibleTs: number | undefined
564
580
 
565
- if (range.start < 0 && this._dataFetcher) {
581
+ if (rawRange.start < 0 && this._dataFetcher) {
566
582
  const earlierThanEarliest = window.earliestTs - gapDays * MS_PER_DAY
567
583
  buf.ensureRange(earlierThanEarliest, window.earliestTs)
568
584
  firstVisibleTs = data[0]?.timestamp
569
585
  } else if (range.start < data.length) {
570
- firstVisibleTs = data[Math.max(0, range.start)]?.timestamp
586
+ firstVisibleTs = data[range.start]?.timestamp
571
587
  if (firstVisibleTs !== undefined && firstVisibleTs < window.earliestTs) {
572
588
  buf.ensureRange(firstVisibleTs, window.earliestTs)
573
589
  }
@@ -587,18 +603,18 @@ export class ChartDataManager {
587
603
 
588
604
  addComparisonSymbol(spec: SymbolSpec): void {
589
605
  const primary = this._dataState.readonly.symbols.peek()[0]
590
- if (!primary || this.deps.comparisonSpecs$.peek().some((item) => item.symbol === spec.symbol))
606
+ if (!primary || this.deps.comparison.readonly.specs.peek().some((item) => item.symbol === spec.symbol))
591
607
  return
592
- this.deps.setSymbols([primary, ...this.deps.comparisonSpecs$.peek(), spec])
608
+ this.deps.setSymbols([primary, ...this.deps.comparison.readonly.specs.peek(), spec])
593
609
  }
594
610
 
595
611
  setComparisonData(symbol: string, data: KLineData[]): void {
596
612
  const primary = this._dataState.readonly.symbols.peek()[0]
597
613
  if (!primary) return
598
- if (!this.deps.comparisonSpecs$.peek().some((spec) => spec.symbol === symbol)) {
614
+ if (!this.deps.comparison.readonly.specs.peek().some((spec) => spec.symbol === symbol)) {
599
615
  this.deps.setSymbols([
600
616
  primary,
601
- ...this.deps.comparisonSpecs$.peek(),
617
+ ...this.deps.comparison.readonly.specs.peek(),
602
618
  { symbol, period: 'daily' },
603
619
  ])
604
620
  }
@@ -607,11 +623,11 @@ export class ChartDataManager {
607
623
 
608
624
  removeComparisonSymbol(symbol: string): void {
609
625
  const primary = this._dataState.readonly.symbols.peek()[0]
610
- if (!primary || !this.deps.comparisonSpecs$.peek().some((spec) => spec.symbol === symbol))
626
+ if (!primary || !this.deps.comparison.readonly.specs.peek().some((spec) => spec.symbol === symbol))
611
627
  return
612
628
  this.deps.setSymbols([
613
629
  primary,
614
- ...this.deps.comparisonSpecs$.peek().filter((spec) => spec.symbol !== symbol),
630
+ ...this.deps.comparison.readonly.specs.peek().filter((spec) => spec.symbol !== symbol),
615
631
  ])
616
632
  this.deps.scheduleDraw()
617
633
  }
@@ -636,19 +652,8 @@ export class ChartDataManager {
636
652
  const dataLen = kRaw?.length ?? 0
637
653
  let visibleStart = 0
638
654
  if (dataLen > 0) {
639
- const vp = this.deps.getViewport()
640
- if (vp) {
641
- const opt = this.deps.getOption()
642
- const vRange = getVisibleRange(
643
- vp.scrollLeft,
644
- vp.plotWidth,
645
- opt.kWidth,
646
- opt.kGap,
647
- dataLen,
648
- vp.dpr,
649
- )
650
- visibleStart = vRange ? Math.max(0, vRange.start) : 0
651
- }
655
+ const vRange = this.getVisibleRangeOrNull()
656
+ visibleStart = vRange ? Math.max(0, vRange.start) : 0
652
657
  }
653
658
  // 双路径守卫:
654
659
  // - switchToTimeShareForDate 路径:setTimeShareQueryDate 已在
@@ -693,7 +698,7 @@ export class ChartDataManager {
693
698
  return
694
699
  }
695
700
  const next = { ...current, period }
696
- this.setSymbols([next, ...this.deps.comparisonSpecs$.peek()])
701
+ this.setSymbols([next, ...this.deps.comparison.readonly.specs.peek()])
697
702
  }
698
703
 
699
704
  /**
@@ -725,7 +730,7 @@ export class ChartDataManager {
725
730
  incremental: false,
726
731
  source: source.source ?? 'custom',
727
732
  }
728
- this.setSymbols([spec, ...this.deps.comparisonSpecs$.peek()])
733
+ this.setSymbols([spec, ...this.deps.comparison.readonly.specs.peek()])
729
734
 
730
735
  const symbolCode = spec.symbol
731
736
  if (symbolCode) {
@@ -760,7 +765,7 @@ export class ChartDataManager {
760
765
  this._dataState.actions.setData([])
761
766
  this._dmState.actions.setRangeInitialized(false)
762
767
  this._dmState.actions.setSavedScrollTimestamp(null)
763
- this.setSymbols([spec, ...this.deps.comparisonSpecs$.peek()])
768
+ this.setSymbols([spec, ...this.deps.comparison.readonly.specs.peek()])
764
769
  }
765
770
 
766
771
  getPreCustomSpec(): SymbolSpec | null {
@@ -872,12 +877,12 @@ export class ChartDataManager {
872
877
  )
873
878
  this._dmState.actions.setSavedScrollTimestamp(null)
874
879
  if (idx >= 0) {
875
- const dpr = this.deps.getEffectiveDpr()
880
+ const dpr = this.deps.viewport.readonly.dpr.peek()
876
881
  const opt = this.deps.getOption()
877
882
  const { unitPx, startXPx } = getPhysicalKLineConfig(opt.kWidth, opt.kGap, dpr)
878
883
  const leftBuffer = this.getLeftLoadBufferWidth()
879
884
  const scrollLeft = ((idx + 1) * unitPx + startXPx) / dpr + leftBuffer
880
- this.deps.setScrollLeft(scrollLeft)
885
+ this.deps.viewport.actions.scrollTo(scrollLeft)
881
886
  return true
882
887
  }
883
888
  return false
@@ -886,7 +891,7 @@ export class ChartDataManager {
886
891
  // ── Content width ──
887
892
 
888
893
  getContentWidth(): number {
889
- return this.deps.getContentWidth()
894
+ return this.deps.viewport.readonly.contentWidth.peek()
890
895
  }
891
896
 
892
897
  scrollToRight(): void {
@@ -962,12 +967,12 @@ export class ChartDataManager {
962
967
  }
963
968
 
964
969
  getLogicalIndexAtX(mouseX: number): number | null {
965
- const vp = this.deps.getViewport()
966
- if (!vp) return null
970
+ if (this.deps.viewport.readonly.viewWidth.peek() === 0) return null
971
+ const vp = this.deps.viewport.readonly.viewport.peek()
967
972
  const buf = this.getActiveDataBuffer()
968
973
  const data = buf ? buf.getRawData() : []
969
974
  if (data.length === 0) return null
970
- const dpr = this.deps.getEffectiveDpr()
975
+ const dpr = this.deps.viewport.readonly.dpr.peek()
971
976
  const opt = this.deps.getOption()
972
977
  const { startXPx, unitPx } = getPhysicalKLineConfig(opt.kWidth, opt.kGap, dpr)
973
978
  const worldX = Math.round((vp.scrollLeft + mouseX) * dpr)
@@ -1,14 +1,14 @@
1
1
  import type { ChartDom } from '../chartTypes'
2
2
  import { getPhysicalKLineConfig } from '../utils/klineConfig'
3
+ import type { ViewportStateModule } from '../state/viewportState'
3
4
 
4
5
  /** 与 theme selectionFill 对齐:light #2D7FF933 / dark #4A9EFF33;fallback 为 light 默认 */
5
6
  const INCREMENTAL_LOAD_HINT_BG = 'var(--klc-color-selection-fill, #2D7FF933)'
6
7
 
7
8
  export interface HintDeps {
8
9
  getOption: () => { kWidth: number; kGap: number }
9
- getEffectiveDpr: () => number
10
10
  getDom: () => ChartDom
11
- getViewport: () => { viewHeight: number } | null
11
+ viewport: ViewportStateModule
12
12
  }
13
13
 
14
14
  interface ActiveHint {
@@ -86,17 +86,18 @@ export class IncrementalLoadHint {
86
86
  }
87
87
 
88
88
  private _updateGeometry(hint: HTMLDivElement, count: number, leftBufferWidth: number): void {
89
- const dpr = this.deps.getEffectiveDpr()
89
+ const dpr = this.deps.viewport.readonly.dpr.peek()
90
90
  const opt = this.deps.getOption()
91
91
  const { unitPx, startXPx } = getPhysicalKLineConfig(opt.kWidth, opt.kGap, dpr)
92
92
 
93
93
  hint.style.left = `${leftBufferWidth}px`
94
94
  const width = (startXPx + count * unitPx) / dpr
95
95
  hint.style.width = `${Math.max(0, width)}px`
96
- hint.style.height = `${Math.max(
97
- 0,
98
- this.deps.getViewport()?.viewHeight ?? this.deps.getDom().container?.clientHeight ?? 0,
99
- )}px`
96
+ const viewHeight =
97
+ this.deps.viewport.readonly.viewHeight.peek() ||
98
+ this.deps.getDom().container?.clientHeight ||
99
+ 0
100
+ hint.style.height = `${Math.max(0, viewHeight)}px`
100
101
  }
101
102
 
102
103
  private _fadeOutAndRemove(entry: ActiveHint): void {