@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,6 +2,8 @@ import type { PaneRole } from '../../foundation/plugin/index'
2
2
  import type { ChartDom, PaneSpec, Viewport } from '../chartTypes'
3
3
  import { PaneRenderer } from '../paneRenderer'
4
4
  import type { ScaleType } from '../utils/tickPosition'
5
+ import type { PaneStateModule } from '../state/paneState'
6
+ import type { ViewportStateModule } from '../state/viewportState'
5
7
 
6
8
  import { Pane, UpdateLevel } from './pane'
7
9
  import { normalizeVisiblePaneRatios as pureNormalizeVisiblePaneRatios } from './paneRatioMath'
@@ -16,14 +18,15 @@ export interface PaneLayoutDependencies {
16
18
  paneGap?: number
17
19
  defaultPaneMinHeightPx?: number
18
20
  }
19
- getViewport: () => Viewport | null
21
+ /** scroll / plot 几何 SSOT */
22
+ viewport: ViewportStateModule
20
23
  setKnownPaneIds: (ids: string[]) => void
21
24
  notifyPaneResize: (paneId: string, pane: Pane) => void
22
25
  scheduleDraw: (level?: UpdateLevel) => void
23
- getPaneRatios: () => Record<string, number>
24
- /** kernel.paneScaleTypes 投影源;initPanes 优先于此 */
25
- getPaneScaleTypes: () => ReadonlyMap<string, ScaleType>
26
- commitLayout: (ratios: Record<string, number>, specs: PaneSpec[]) => void
26
+ /** pane ratios / specs / scaleTypes SSOT */
27
+ pane: PaneStateModule
28
+ /** commitLayout 写入 kernel 后的副作用(如 ensurePaneScaleTypes) */
29
+ afterCommitLayout?: () => void
27
30
  }
28
31
 
29
32
  /**
@@ -63,7 +66,7 @@ export class ChartPaneLayout {
63
66
  * 公共读:specs 结构来自工作副本定义,ratio 字段取自 kernel(不写工作副本)。
64
67
  */
65
68
  getPaneSpecs(): PaneSpec[] {
66
- const kernelRatios = this.deps.getPaneRatios()
69
+ const kernelRatios = this.deps.pane.readonly.paneRatios.peek()
67
70
  return this._paneSpecs.map((spec) => ({
68
71
  ...spec,
69
72
  ratio: kernelRatios[spec.id] ?? spec.ratio,
@@ -75,11 +78,11 @@ export class ChartPaneLayout {
75
78
  * 公共读:直接返回 kernel paneRatios 副本,不触碰算法工作副本。
76
79
  */
77
80
  getInternalPaneRatios(): Map<string, number> {
78
- return new Map(Object.entries(this.deps.getPaneRatios()))
81
+ return new Map(Object.entries(this.deps.pane.readonly.paneRatios.peek()))
79
82
  }
80
83
 
81
84
  private syncRatiosFromKernel(): void {
82
- const kernelRatios = this.deps.getPaneRatios()
85
+ const kernelRatios = this.deps.pane.readonly.paneRatios.peek()
83
86
  this._internalPaneRatios = new Map(Object.entries(kernelRatios))
84
87
  }
85
88
 
@@ -100,17 +103,36 @@ export class ChartPaneLayout {
100
103
  return index === 0 ? 'price' : 'indicator'
101
104
  }
102
105
 
103
- private createAxisCanvas(spec: PaneSpec, pane: Pane, side: 'left' | 'right'): HTMLCanvasElement {
106
+ private createAxisCanvas(
107
+ spec: PaneSpec,
108
+ side: 'left' | 'right',
109
+ kind: 'base' | 'overlay' = 'base',
110
+ ): HTMLCanvasElement {
104
111
  const canvas = document.createElement('canvas')
105
- canvas.id = `${spec.id}-${side}Axis`
106
- canvas.className = side === 'right' ? 'right-axis' : 'left-axis'
112
+ const isRight = side === 'right'
113
+ const isOverlay = kind === 'overlay'
114
+ canvas.id = `${spec.id}-${side}Axis${isOverlay ? 'Overlay' : ''}`
115
+ canvas.className = isRight
116
+ ? isOverlay
117
+ ? 'right-axis-overlay'
118
+ : 'right-axis'
119
+ : isOverlay
120
+ ? 'left-axis-overlay'
121
+ : 'left-axis'
107
122
  canvas.style.position = 'absolute'
108
123
  canvas.style.left = '0'
124
+ if (isOverlay) {
125
+ canvas.style.pointerEvents = 'none'
126
+ canvas.style.backgroundColor = 'transparent'
127
+ canvas.style.zIndex = '1'
128
+ } else {
129
+ canvas.style.zIndex = '0'
130
+ }
109
131
  return canvas
110
132
  }
111
133
 
112
134
  private initPanes() {
113
- const kernelScaleTypes = this.deps.getPaneScaleTypes()
135
+ const kernelScaleTypes = this.deps.pane.readonly.paneScaleTypes.peek()
114
136
  const prevScaleTypes = new Map<string, ScaleType>()
115
137
  for (const r of this.paneRenderers) {
116
138
  prevScaleTypes.set(r.getPane().id, r.getPane().yAxis.getScaleType())
@@ -129,7 +151,8 @@ export class ChartPaneLayout {
129
151
 
130
152
  const mainCanvas = document.createElement('canvas')
131
153
  const overlayCanvas = document.createElement('canvas')
132
- const yAxisCanvas = this.createAxisCanvas(spec, pane, 'right')
154
+ const yAxisCanvas = this.createAxisCanvas(spec, 'right', 'base')
155
+ const yAxisOverlayCanvas = this.createAxisCanvas(spec, 'right', 'overlay')
133
156
 
134
157
  const isMain = pane.role === 'price'
135
158
 
@@ -149,10 +172,18 @@ export class ChartPaneLayout {
149
172
  overlayCanvas.style.backgroundColor = 'transparent'
150
173
  overlayCanvas.style.zIndex = '2'
151
174
 
152
- const leftYAxisCanvas = this.createAxisCanvas(spec, pane, 'left')
175
+ const leftYAxisCanvas = this.createAxisCanvas(spec, 'left', 'base')
176
+ const leftYAxisOverlayCanvas = this.createAxisCanvas(spec, 'left', 'overlay')
153
177
 
154
178
  const renderer = new PaneRenderer(
155
- { mainCanvas, overlayCanvas, yAxisCanvas, leftYAxisCanvas },
179
+ {
180
+ mainCanvas,
181
+ overlayCanvas,
182
+ yAxisCanvas,
183
+ yAxisOverlayCanvas,
184
+ leftYAxisCanvas,
185
+ leftYAxisOverlayCanvas,
186
+ },
156
187
  pane,
157
188
  {
158
189
  rightAxisWidth: this.deps.getOption().rightAxisWidth,
@@ -177,11 +208,15 @@ export class ChartPaneLayout {
177
208
  existingCanvases.forEach((canvas) => canvas.remove())
178
209
  }
179
210
  if (rightAxisLayer) {
180
- const existingAxisCanvases = rightAxisLayer.querySelectorAll('canvas.right-axis')
211
+ const existingAxisCanvases = rightAxisLayer.querySelectorAll(
212
+ 'canvas.right-axis, canvas.right-axis-overlay',
213
+ )
181
214
  existingAxisCanvases.forEach((canvas) => canvas.remove())
182
215
  }
183
216
  if (leftAxisLayer) {
184
- const existingLeftAxisCanvases = leftAxisLayer.querySelectorAll('canvas.left-axis')
217
+ const existingLeftAxisCanvases = leftAxisLayer.querySelectorAll(
218
+ 'canvas.left-axis, canvas.left-axis-overlay',
219
+ )
185
220
  existingLeftAxisCanvases.forEach((canvas) => canvas.remove())
186
221
  }
187
222
 
@@ -190,8 +225,12 @@ export class ChartPaneLayout {
190
225
  canvasLayer.appendChild(domEls.mainCanvas)
191
226
  canvasLayer.appendChild(domEls.overlayCanvas)
192
227
  rightAxisLayer.appendChild(domEls.yAxisCanvas)
228
+ rightAxisLayer.appendChild(domEls.yAxisOverlayCanvas)
193
229
  if (leftAxisLayer && domEls.leftYAxisCanvas) {
194
230
  leftAxisLayer.appendChild(domEls.leftYAxisCanvas)
231
+ if (domEls.leftYAxisOverlayCanvas) {
232
+ leftAxisLayer.appendChild(domEls.leftYAxisOverlayCanvas)
233
+ }
195
234
  }
196
235
  })
197
236
 
@@ -299,10 +338,16 @@ export class ChartPaneLayout {
299
338
  return heights
300
339
  }
301
340
 
341
+ /** viewWidth 为 0 表示尚未完成首帧尺寸 */
342
+ private peekViewport(): Viewport | null {
343
+ if (this.deps.viewport.readonly.viewWidth.peek() === 0) return null
344
+ return this.deps.viewport.readonly.viewport.peek()
345
+ }
346
+
302
347
  layoutPanes(options?: { commit?: boolean }) {
303
348
  this.syncRatiosFromKernel()
304
349
 
305
- const vp = this.deps.getViewport()
350
+ const vp = this.peekViewport()
306
351
  if (!vp) return
307
352
 
308
353
  const visibleSpecs = this._paneSpecs.filter((p) => p.visible !== false)
@@ -338,10 +383,16 @@ export class ChartPaneLayout {
338
383
  domEls.overlayCanvas.style.top = `${y}px`
339
384
  domEls.yAxisCanvas.style.top = `${y}px`
340
385
  domEls.yAxisCanvas.style.left = '0px'
386
+ domEls.yAxisOverlayCanvas.style.top = `${y}px`
387
+ domEls.yAxisOverlayCanvas.style.left = '0px'
341
388
  if (domEls.leftYAxisCanvas) {
342
389
  domEls.leftYAxisCanvas.style.top = `${y}px`
343
390
  domEls.leftYAxisCanvas.style.left = '0px'
344
391
  }
392
+ if (domEls.leftYAxisOverlayCanvas) {
393
+ domEls.leftYAxisOverlayCanvas.style.top = `${y}px`
394
+ domEls.leftYAxisOverlayCanvas.style.left = '0px'
395
+ }
345
396
 
346
397
  y += h + gap
347
398
  }
@@ -388,7 +439,7 @@ export class ChartPaneLayout {
388
439
  * commitLayout 走 buildLayoutSpecsFromWorkingCopy,避免公共读污染算法中间态。
389
440
  */
390
441
  getPaneLayoutSpecs(): PaneSpec[] {
391
- return this.buildLayoutSpecs(this.deps.getPaneRatios())
442
+ return this.buildLayoutSpecs(this.deps.pane.readonly.paneRatios.peek())
392
443
  }
393
444
 
394
445
  /** 仅算法/commit 使用:读当前工作副本 ratios,不读 kernel */
@@ -422,7 +473,8 @@ export class ChartPaneLayout {
422
473
  this._internalPaneRatios.forEach((ratio, id) => {
423
474
  ratios[id] = ratio
424
475
  })
425
- this.deps.commitLayout(ratios, this.buildLayoutSpecsFromWorkingCopy())
476
+ this.deps.pane.actions.commitLayout(ratios, this.buildLayoutSpecsFromWorkingCopy())
477
+ this.deps.afterCommitLayout?.()
426
478
  }
427
479
 
428
480
  applyPaneLayoutSpecs(panes: PaneSpec[], options?: { preferIncomingRatios?: boolean }): void {
@@ -499,7 +551,7 @@ export class ChartPaneLayout {
499
551
 
500
552
  resizePaneBoundary(upperPaneId: string, deltaY: number): boolean {
501
553
  if (!Number.isFinite(deltaY) || deltaY === 0) return false
502
- const vp = this.deps.getViewport()
554
+ const vp = this.peekViewport()
503
555
  if (!vp) return false
504
556
 
505
557
  this.syncRatiosFromKernel()
@@ -0,0 +1,185 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import {
4
+ resolveTimeShareSlotTimestamp,
5
+ timeShareSlotCenterX,
6
+ } from '../../../foundation/utils/timeShareAxisLabels'
7
+ import {
8
+ ASHARE_TIMESHARE_SESSION_SLOTS,
9
+ computeTimeShareBarMetrics,
10
+ computeTimeSharePaneLayout,
11
+ computeTimeSharePriceRange,
12
+ computeTimeShareTimeLabelIndices,
13
+ computeTimeShareXLayout,
14
+ resolveTimeShareSessionSlots,
15
+ resolveTimeShareBaseline,
16
+ } from '../timeShareMath'
17
+
18
+ describe('resolveTimeShareBaseline', () => {
19
+ it('prefers finite non-zero preClose over first trade price', () => {
20
+ expect(resolveTimeShareBaseline({ preClose: 10.5, firstPrice: 11 })).toBe(10.5)
21
+ })
22
+
23
+ it('falls back to first price when preClose is missing or zero', () => {
24
+ expect(resolveTimeShareBaseline({ preClose: 0, firstPrice: 11 })).toBe(11)
25
+ expect(resolveTimeShareBaseline({ firstPrice: 11 })).toBe(11)
26
+ expect(resolveTimeShareBaseline({ preClose: null, firstPrice: 9 })).toBe(9)
27
+ })
28
+
29
+ it('returns null when no valid baseline exists', () => {
30
+ expect(resolveTimeShareBaseline({ preClose: 0, firstPrice: 0 })).toBeNull()
31
+ expect(resolveTimeShareBaseline({})).toBeNull()
32
+ expect(resolveTimeShareBaseline({ preClose: NaN, firstPrice: Infinity })).toBeNull()
33
+ })
34
+ })
35
+
36
+ describe('computeTimeSharePriceRange', () => {
37
+ it('uses preClose baseline and pads around max absolute percent move', () => {
38
+ // open gap up: first trade 11, preClose 10 → +10%; later 10.5 → 5%
39
+ const range = computeTimeSharePriceRange([11, 10.5, 10.2], 10)
40
+ expect(range).not.toBeNull()
41
+ // maxAbsPct=10, padding=max(1, 0.5)=1 → display 11%
42
+ expect(range!.maxPrice).toBeCloseTo(10 * 1.11, 8)
43
+ expect(range!.minPrice).toBeCloseTo(10 * 0.89, 8)
44
+ })
45
+
46
+ it('still applies minimum padding when all prices equal baseline (flat day)', () => {
47
+ const range = computeTimeSharePriceRange([10, 10, 10], 10)
48
+ expect(range).not.toBeNull()
49
+ // maxAbsPct=0, padding=min 0.5% → ±0.5%
50
+ expect(range!.maxPrice).toBeCloseTo(10.05, 8)
51
+ expect(range!.minPrice).toBeCloseTo(9.95, 8)
52
+ })
53
+
54
+ it('returns null for invalid baseline', () => {
55
+ expect(computeTimeSharePriceRange([10], 0)).toBeNull()
56
+ expect(computeTimeSharePriceRange([10], NaN)).toBeNull()
57
+ })
58
+ })
59
+
60
+ describe('resolveTimeShareSessionSlots', () => {
61
+ it('defaults to A-share 240 one-minute slots (sessions SSOT)', () => {
62
+ expect(ASHARE_TIMESHARE_SESSION_SLOTS).toBe(240)
63
+ expect(resolveTimeShareSessionSlots(0)).toBe(240)
64
+ expect(resolveTimeShareSessionSlots(60)).toBe(240)
65
+ })
66
+
67
+ it('does not expand when arrived points exceed session slots', () => {
68
+ expect(resolveTimeShareSessionSlots(300)).toBe(240)
69
+ })
70
+ })
71
+
72
+ describe('computeTimeShareBarMetrics', () => {
73
+ it('divides viewWidth by full session slots, not arrived point count', () => {
74
+ // 盘中仅 60 点:宽度仍按 240 槽划分,不把 60 点拉满屏
75
+ const partial = computeTimeShareBarMetrics(60, 320, 1)
76
+ const full = computeTimeShareBarMetrics(240, 320, 1)
77
+ expect(partial).not.toBeNull()
78
+ expect(full).not.toBeNull()
79
+ expect(partial!.kWidth).toBeCloseTo(full!.kWidth, 8)
80
+ expect(partial!.kGap).toBeCloseTo(full!.kGap, 8)
81
+ expect((partial!.kWidth + partial!.kGap) * 240).toBeCloseTo(320, 6)
82
+ })
83
+
84
+ it('fits full session into viewWidth even when per-bar width is sub-pixel (DPR=1 narrow)', () => {
85
+ const m = computeTimeShareBarMetrics(240, 320, 1)
86
+ expect(m).not.toBeNull()
87
+ const unit = m!.kWidth + m!.kGap
88
+ expect(unit * 240).toBeCloseTo(320, 6)
89
+ expect(m!.kWidth).toBeGreaterThan(0)
90
+ expect(m!.kGap).toBeGreaterThanOrEqual(0)
91
+ })
92
+
93
+ it('keeps full session visible at high DPR', () => {
94
+ const m = computeTimeShareBarMetrics(240, 320, 2)
95
+ expect(m).not.toBeNull()
96
+ expect((m!.kWidth + m!.kGap) * 240).toBeCloseTo(320, 6)
97
+ })
98
+
99
+ it('returns null for empty data or invalid width', () => {
100
+ expect(computeTimeShareBarMetrics(0, 320, 1)).toBeNull()
101
+ expect(computeTimeShareBarMetrics(10, 0, 1)).toBeNull()
102
+ })
103
+ })
104
+
105
+ describe('computeTimeShareXLayout', () => {
106
+ it('places partial-day points on session timeline leaving right-side blank', () => {
107
+ const layout = computeTimeShareXLayout({
108
+ arrivedCount: 60,
109
+ sessionSlots: 240,
110
+ totalWidth: 480,
111
+ dpr: 1,
112
+ })
113
+ expect(layout).not.toBeNull()
114
+ // step = 480/240 = 2;第 0 点中心 1,第 59 点中心 119,未到右缘 480
115
+ expect(layout!.centers[0]).toBeCloseTo(1, 6)
116
+ expect(layout!.centers[59]).toBeCloseTo(119, 6)
117
+ expect(layout!.centers[59]!).toBeLessThan(480 * 0.3)
118
+ expect(layout!.step).toBeCloseTo(2, 6)
119
+ })
120
+
121
+ it('fills full width only when arrivedCount covers full session', () => {
122
+ const layout = computeTimeShareXLayout({
123
+ arrivedCount: 240,
124
+ sessionSlots: 240,
125
+ totalWidth: 480,
126
+ dpr: 1,
127
+ })
128
+ expect(layout).not.toBeNull()
129
+ expect(layout!.centers[0]).toBeCloseTo(1, 6)
130
+ expect(layout!.centers[239]).toBeCloseTo(479, 6)
131
+ })
132
+ })
133
+
134
+ describe('computeTimeSharePaneLayout', () => {
135
+ it('splits price area above volume area without overlap', () => {
136
+ const layout = computeTimeSharePaneLayout(400, 0.25)
137
+ expect(layout.priceAreaHeight).toBe(300)
138
+ expect(layout.volumeAreaHeight).toBe(100)
139
+ expect(layout.priceTop).toBe(0)
140
+ expect(layout.volumeTop).toBe(300)
141
+ expect(layout.priceTop + layout.priceAreaHeight).toBe(layout.volumeTop)
142
+ })
143
+ })
144
+
145
+ describe('computeTimeShareTimeLabelIndices', () => {
146
+ it('only session closed-side endpoints: 9:30 / 13:00 / 15:00', () => {
147
+ const labels = computeTimeShareTimeLabelIndices({
148
+ axisWidth: 800,
149
+ sessionSlots: 240,
150
+ })
151
+ // 9:30 → 0;13:00 → 120;15:00 → 239
152
+ expect(labels).toEqual([0, 120, 239])
153
+ })
154
+
155
+ it('returns empty for invalid axis width', () => {
156
+ expect(
157
+ computeTimeShareTimeLabelIndices({
158
+ axisWidth: 0,
159
+ sessionSlots: 240,
160
+ }),
161
+ ).toEqual([])
162
+ })
163
+ })
164
+
165
+ describe('timeShare slot time/x helpers', () => {
166
+ it('maps A-share slots across lunch break', () => {
167
+ // 2026-07-21 local
168
+ const day = new Date(2026, 6, 21, 10, 0, 0, 0).getTime()
169
+ expect(new Date(resolveTimeShareSlotTimestamp(day, 0)).getHours()).toBe(9)
170
+ expect(new Date(resolveTimeShareSlotTimestamp(day, 0)).getMinutes()).toBe(30)
171
+ // slot 120 = 13:00
172
+ expect(new Date(resolveTimeShareSlotTimestamp(day, 120)).getHours()).toBe(13)
173
+ expect(new Date(resolveTimeShareSlotTimestamp(day, 120)).getMinutes()).toBe(0)
174
+ // slot 239 = 14:59
175
+ expect(new Date(resolveTimeShareSlotTimestamp(day, 239)).getHours()).toBe(14)
176
+ expect(new Date(resolveTimeShareSlotTimestamp(day, 239)).getMinutes()).toBe(59)
177
+ })
178
+
179
+ it('places last session slot near right edge of axis', () => {
180
+ const x0 = timeShareSlotCenterX(0, 480, 240, 1)
181
+ const xLast = timeShareSlotCenterX(239, 480, 240, 1)
182
+ expect(x0).toBeCloseTo(1, 6)
183
+ expect(xLast).toBeCloseTo(479, 6)
184
+ })
185
+ })
@@ -0,0 +1,81 @@
1
+ import { describe, expect, it, vi } from 'vitest'
2
+
3
+ import { Pane } from '../../layout/pane'
4
+ import { TimeShareMode } from '../timeShareMode'
5
+ import type { TimeShareData } from '../../../foundation/types/price'
6
+
7
+ function ts(price: number, i = 0): TimeShareData {
8
+ return { timestamp: i, price, average: price, volume: 1, amount: price }
9
+ }
10
+
11
+ function mockDm(points: TimeShareData[], preClose: number | null = null) {
12
+ return {
13
+ getTimeShareData: () => points,
14
+ getTimeSharePreClose: () => preClose,
15
+ } as unknown as import('../../data/chartDataManager').ChartDataManager
16
+ }
17
+
18
+ describe('TimeShareMode', () => {
19
+ it('computeKWidth fits full session into narrow view (no DPR truncation)', () => {
20
+ const mode = new TimeShareMode()
21
+ const m = mode.computeKWidth(240, 320, 1)
22
+ expect(m).not.toBeNull()
23
+ expect((m!.kWidth + m!.kGap) * 240).toBeCloseTo(320, 6)
24
+ })
25
+
26
+ it('computeKWidth for partial day matches full-session bar size', () => {
27
+ const mode = new TimeShareMode()
28
+ const partial = mode.computeKWidth(60, 320, 1)
29
+ const full = mode.computeKWidth(240, 320, 1)
30
+ expect(partial).not.toBeNull()
31
+ expect(full).not.toBeNull()
32
+ expect(partial!.kWidth).toBeCloseTo(full!.kWidth, 8)
33
+ expect(partial!.kGap).toBeCloseTo(full!.kGap, 8)
34
+ })
35
+
36
+ it('setMarketSession switches bar metrics to HK 330 slots', () => {
37
+ const mode = new TimeShareMode()
38
+ const ashare = mode.computeKWidth(100, 330, 1)!
39
+ mode.setMarketSession({
40
+ timeZone: 'Asia/Hong_Kong',
41
+ sessions: [
42
+ { open: 9 * 60 + 30, close: 12 * 60 },
43
+ { open: 13 * 60, close: 16 * 60 },
44
+ ],
45
+ slotMinutes: 1,
46
+ })
47
+ const hk = mode.computeKWidth(100, 330, 1)!
48
+ // A 股 240 槽 unit=330/240;港股 330 槽 unit=1
49
+ expect(ashare.kWidth + ashare.kGap).toBeCloseTo(330 / 240, 6)
50
+ expect(hk.kWidth + hk.kGap).toBeCloseTo(1, 6)
51
+ })
52
+
53
+ it('updatePaneRange uses preClose as basePrice and covers open gap', () => {
54
+ const mode = new TimeShareMode()
55
+ const pane = new Pane('main')
56
+ const setBase = vi.spyOn(pane.yAxis, 'setBasePrice')
57
+ const setRange = vi.spyOn(pane.yAxis, 'setRange')
58
+
59
+ // open gap: first trade 11, preClose 10
60
+ mode.updatePaneRange(pane, { start: 0, end: 3 }, mockDm([ts(11), ts(10.5), ts(10.2)], 10))
61
+
62
+ expect(setBase).toHaveBeenCalledWith(10)
63
+ expect(setRange).toHaveBeenCalled()
64
+ const range = setRange.mock.calls[0]![0] as { minPrice: number; maxPrice: number }
65
+ // maxAbs 10% + 1% padding → ±11%
66
+ expect(range.maxPrice).toBeCloseTo(11.1, 6)
67
+ expect(range.minPrice).toBeCloseTo(8.9, 6)
68
+ })
69
+
70
+ it('updatePaneRange still sets range on flat day', () => {
71
+ const mode = new TimeShareMode()
72
+ const pane = new Pane('main')
73
+ const setRange = vi.spyOn(pane.yAxis, 'setRange')
74
+
75
+ mode.updatePaneRange(pane, { start: 0, end: 2 }, mockDm([ts(10), ts(10)], 10))
76
+ expect(setRange).toHaveBeenCalled()
77
+ const range = setRange.mock.calls[0]![0] as { minPrice: number; maxPrice: number }
78
+ expect(range.maxPrice).toBeCloseTo(10.05, 6)
79
+ expect(range.minPrice).toBeCloseTo(9.95, 6)
80
+ })
81
+ })
@@ -0,0 +1,150 @@
1
+ import {
2
+ ASHARE_MARKET_SESSION,
3
+ resolveMarketSessionSlots,
4
+ type MarketSessionConfig,
5
+ } from '../../foundation/utils/timeShareAxisLabels'
6
+
7
+ export type TimeShareBaselineInput = {
8
+ preClose?: number | null
9
+ firstPrice?: number | null
10
+ }
11
+
12
+ export function resolveTimeShareBaseline(input: TimeShareBaselineInput): number | null {
13
+ const candidates = [input.preClose, input.firstPrice]
14
+ for (const v of candidates) {
15
+ if (typeof v === 'number' && Number.isFinite(v) && v !== 0) return v
16
+ }
17
+ return null
18
+ }
19
+
20
+ export type TimeSharePriceRange = {
21
+ minPrice: number
22
+ maxPrice: number
23
+ }
24
+
25
+ /**
26
+ * 分时 Y 轴价格区间:以 baseline(昨收)为中心,对称覆盖可见最大绝对涨跌幅并加 padding。
27
+ * 全天平盘时仍保留最小 0.5% 边距,避免 range 退化。
28
+ */
29
+ export function computeTimeSharePriceRange(
30
+ prices: ReadonlyArray<number | undefined | null>,
31
+ baseline: number,
32
+ ): TimeSharePriceRange | null {
33
+ if (!Number.isFinite(baseline) || baseline === 0) return null
34
+
35
+ let maxAbsPct = 0
36
+ for (const p of prices) {
37
+ if (typeof p !== 'number' || !Number.isFinite(p)) continue
38
+ const pct = Math.abs((p - baseline) / baseline) * 100
39
+ if (pct > maxAbsPct) maxAbsPct = pct
40
+ }
41
+
42
+ const padding = Math.max(maxAbsPct * 0.1, 0.5)
43
+ const displayPct = maxAbsPct + padding
44
+ return {
45
+ minPrice: baseline * (1 - displayPct / 100),
46
+ maxPrice: baseline * (1 + displayPct / 100),
47
+ }
48
+ }
49
+
50
+ /** A 股默认全天 1 分钟槽位数(兼容旧导出) */
51
+ export const ASHARE_TIMESHARE_SESSION_SLOTS = resolveMarketSessionSlots(ASHARE_MARKET_SESSION)
52
+
53
+ /**
54
+ * 全天交易槽位数:以 marketSession 为 SSOT,不因 arrivedCount 放大。
55
+ * @deprecated 优先用 resolveMarketSessionSlots(config)
56
+ */
57
+ export function resolveTimeShareSessionSlots(
58
+ arrivedCount: number,
59
+ marketSession: MarketSessionConfig = ASHARE_MARKET_SESSION,
60
+ ): number {
61
+ void arrivedCount
62
+ return resolveMarketSessionSlots(marketSession)
63
+ }
64
+
65
+ /**
66
+ * 分时 bar 宽度:按全天 sessionSlots 均分 viewWidth,而非按已到达点数。
67
+ * 盘中部分数据时右侧留白;允许亚像素,避免窄屏截断。
68
+ */
69
+ export function computeTimeShareBarMetrics(
70
+ dataLength: number,
71
+ viewWidth: number,
72
+ dpr: number,
73
+ marketSession: MarketSessionConfig = ASHARE_MARKET_SESSION,
74
+ ): { kWidth: number; kGap: number } | null {
75
+ if (dataLength <= 0 || viewWidth <= 0 || !(dpr > 0)) return null
76
+
77
+ const sessionSlots = resolveMarketSessionSlots(marketSession)
78
+ if (sessionSlots <= 0) return null
79
+ const unit = viewWidth / sessionSlots
80
+ const preferredGap = 1 / dpr
81
+ const kGap = Math.min(preferredGap, unit * 0.2)
82
+ const kWidth = Math.max(unit - kGap, unit * 0.01)
83
+ return { kWidth, kGap: unit - kWidth }
84
+ }
85
+
86
+ export type TimeShareXLayoutInput = {
87
+ arrivedCount: number
88
+ sessionSlots: number
89
+ totalWidth: number
90
+ dpr: number
91
+ }
92
+
93
+ export type TimeShareXLayout = {
94
+ step: number
95
+ centers: number[]
96
+ lefts: number[]
97
+ barWidth: number
98
+ kWidthPx: number
99
+ }
100
+
101
+ /**
102
+ * 分时 X 布局:step = totalWidth / sessionSlots,已到达点按 slot 索引落位,未到时段留白。
103
+ */
104
+ export function computeTimeShareXLayout(input: TimeShareXLayoutInput): TimeShareXLayout | null {
105
+ const { arrivedCount, sessionSlots, totalWidth, dpr } = input
106
+ if (arrivedCount <= 0 || sessionSlots <= 0 || totalWidth <= 0 || !(dpr > 0)) return null
107
+
108
+ const step = totalWidth / sessionSlots
109
+ const centers: number[] = new Array(arrivedCount)
110
+ const lefts: number[] = new Array(arrivedCount)
111
+ for (let i = 0; i < arrivedCount; i++) {
112
+ centers[i] = Math.round((i + 0.5) * step * dpr) / dpr
113
+ lefts[i] = Math.round(i * step * dpr) / dpr
114
+ }
115
+
116
+ const logicalBarWidth = Math.max(step * 0.6, 1 / dpr)
117
+ const barWidthPx = Math.max(1, Math.round(logicalBarWidth * dpr))
118
+ const barWidth = barWidthPx / dpr
119
+ const kWidthPx = Math.max(1, Math.round(step * dpr))
120
+
121
+ return { step, centers, lefts, barWidth, kWidthPx }
122
+ }
123
+
124
+ export type TimeSharePaneLayout = {
125
+ priceTop: number
126
+ priceAreaHeight: number
127
+ volumeTop: number
128
+ volumeAreaHeight: number
129
+ }
130
+
131
+ export function computeTimeSharePaneLayout(
132
+ paneHeight: number,
133
+ volumeRatio: number,
134
+ ): TimeSharePaneLayout {
135
+ const ratio = Math.min(1, Math.max(0, volumeRatio))
136
+ const volumeAreaHeight = paneHeight * ratio
137
+ const priceAreaHeight = paneHeight - volumeAreaHeight
138
+ return {
139
+ priceTop: 0,
140
+ priceAreaHeight,
141
+ volumeTop: priceAreaHeight,
142
+ volumeAreaHeight,
143
+ }
144
+ }
145
+
146
+ export {
147
+ TIMESHARE_MIN_LABEL_SPACING_PX,
148
+ computeTimeShareTimeLabelIndices,
149
+ type TimeShareTimeLabelInput,
150
+ } from '../../foundation/utils/timeShareAxisLabels'