@363045841yyt/klinechart 0.7.1 → 0.7.3-alpha.0

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 (236) hide show
  1. package/README.md +10 -6
  2. package/dist/components/DrawingStyleToolbar.vue.d.ts +28 -0
  3. package/dist/components/DrawingStyleToolbar.vue.d.ts.map +1 -0
  4. package/dist/{src/components → components}/IndicatorParams.vue.d.ts +3 -3
  5. package/dist/components/IndicatorParams.vue.d.ts.map +1 -0
  6. package/dist/{src/components → components}/IndicatorSelector.vue.d.ts +4 -4
  7. package/dist/components/IndicatorSelector.vue.d.ts.map +1 -0
  8. package/dist/components/KLineChart.vue.d.ts +63 -0
  9. package/dist/components/KLineChart.vue.d.ts.map +1 -0
  10. package/dist/components/KLineTooltip.vue.d.ts +30 -0
  11. package/dist/components/KLineTooltip.vue.d.ts.map +1 -0
  12. package/dist/{src/components → components}/LeftToolbar.vue.d.ts +3 -2
  13. package/dist/components/LeftToolbar.vue.d.ts.map +1 -0
  14. package/dist/components/MarkerTooltip.vue.d.ts +26 -0
  15. package/dist/components/MarkerTooltip.vue.d.ts.map +1 -0
  16. package/dist/components/index.d.ts +8 -0
  17. package/dist/components/index.d.ts.map +1 -0
  18. package/dist/{src/composables → composables}/useFullscreenTeleportTarget.d.ts +1 -0
  19. package/dist/composables/useFullscreenTeleportTarget.d.ts.map +1 -0
  20. package/dist/{src/debug → debug}/canvasProfiler.d.ts +1 -0
  21. package/dist/debug/canvasProfiler.d.ts.map +1 -0
  22. package/dist/index.cjs +2 -49
  23. package/dist/index.d.cts +124 -0
  24. package/dist/index.d.ts +124 -2
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +1390 -23809
  27. package/dist/klinechart.css +1 -1
  28. package/dist/version.d.ts +3 -0
  29. package/dist/version.d.ts.map +1 -0
  30. package/package.json +75 -112
  31. package/src/__tests__/_mockController.ts +192 -0
  32. package/src/__tests__/contract.test.ts +132 -0
  33. package/src/components/DrawingStyleToolbar.vue +199 -0
  34. package/src/components/IndicatorParams.vue +570 -0
  35. package/src/components/IndicatorSelector.vue +1042 -0
  36. package/src/components/KLineChart.vue +1570 -0
  37. package/src/components/KLineTooltip.vue +200 -0
  38. package/src/components/LeftToolbar.vue +844 -0
  39. package/src/components/MarkerTooltip.vue +155 -0
  40. package/src/components/index.ts +7 -0
  41. package/src/composables/useFullscreenTeleportTarget.ts +18 -0
  42. package/src/debug/canvasProfiler.ts +296 -0
  43. package/src/index.ts +402 -0
  44. package/src/version.ts +3 -0
  45. package/LICENSE +0 -21
  46. package/dist/favicon.ico +0 -0
  47. package/dist/mock-stock-data.json +0 -1
  48. package/dist/schema-CzmPW09E.cjs +0 -1
  49. package/dist/schema-DBMGp6Af.js +0 -437
  50. package/dist/src/App.vue.d.ts +0 -4
  51. package/dist/src/api/data/baostock.d.ts +0 -90
  52. package/dist/src/api/data/baostock.integration.test.d.ts +0 -1
  53. package/dist/src/api/data/index.d.ts +0 -26
  54. package/dist/src/api/data/kLine.d.ts +0 -11
  55. package/dist/src/api/data/types.d.ts +0 -33
  56. package/dist/src/api/data/unified.d.ts +0 -37
  57. package/dist/src/components/DrawingStyleToolbar.vue.d.ts +0 -12
  58. package/dist/src/components/KLineChart.vue.d.ts +0 -100
  59. package/dist/src/components/KLineTooltip.vue.d.ts +0 -17
  60. package/dist/src/components/MarkerTooltip.vue.d.ts +0 -13
  61. package/dist/src/components/index.d.ts +0 -2
  62. package/dist/src/config/chartSettings.d.ts +0 -69
  63. package/dist/src/core/chart-store.d.ts +0 -74
  64. package/dist/src/core/chart.d.ts +0 -617
  65. package/dist/src/core/controller/interaction.d.ts +0 -167
  66. package/dist/src/core/controller/markerInteraction.d.ts +0 -28
  67. package/dist/src/core/controller/pinchTracker.d.ts +0 -18
  68. package/dist/src/core/controller/tooltipPosition.d.ts +0 -21
  69. package/dist/src/core/draw/pixelAlign.d.ts +0 -114
  70. package/dist/src/core/drawing/index.d.ts +0 -47
  71. package/dist/src/core/drawing/interaction.d.ts +0 -75
  72. package/dist/src/core/drawing/plugin.d.ts +0 -27
  73. package/dist/src/core/indicators/atrState.d.ts +0 -16
  74. package/dist/src/core/indicators/bollState.d.ts +0 -34
  75. package/dist/src/core/indicators/calculators.d.ts +0 -465
  76. package/dist/src/core/indicators/cciState.d.ts +0 -15
  77. package/dist/src/core/indicators/chaikinVolState.d.ts +0 -18
  78. package/dist/src/core/indicators/cmfState.d.ts +0 -16
  79. package/dist/src/core/indicators/demaState.d.ts +0 -16
  80. package/dist/src/core/indicators/donchianState.d.ts +0 -23
  81. package/dist/src/core/indicators/eneState.d.ts +0 -30
  82. package/dist/src/core/indicators/expmaState.d.ts +0 -30
  83. package/dist/src/core/indicators/fastkState.d.ts +0 -15
  84. package/dist/src/core/indicators/fibState.d.ts +0 -26
  85. package/dist/src/core/indicators/hmaState.d.ts +0 -16
  86. package/dist/src/core/indicators/hvState.d.ts +0 -18
  87. package/dist/src/core/indicators/ichimokuState.d.ts +0 -44
  88. package/dist/src/core/indicators/indicator.worker.d.ts +0 -5
  89. package/dist/src/core/indicators/indicatorDefinitionRegistry.d.ts +0 -30
  90. package/dist/src/core/indicators/indicatorMetadata.d.ts +0 -81
  91. package/dist/src/core/indicators/indicatorRegistry.d.ts +0 -57
  92. package/dist/src/core/indicators/indicatorRuntime.d.ts +0 -126
  93. package/dist/src/core/indicators/kamaState.d.ts +0 -20
  94. package/dist/src/core/indicators/keltnerState.d.ts +0 -27
  95. package/dist/src/core/indicators/kstState.d.ts +0 -21
  96. package/dist/src/core/indicators/maState.d.ts +0 -26
  97. package/dist/src/core/indicators/macdState.d.ts +0 -58
  98. package/dist/src/core/indicators/mfiState.d.ts +0 -16
  99. package/dist/src/core/indicators/momState.d.ts +0 -15
  100. package/dist/src/core/indicators/obvState.d.ts +0 -14
  101. package/dist/src/core/indicators/parkinsonState.d.ts +0 -18
  102. package/dist/src/core/indicators/pivotState.d.ts +0 -29
  103. package/dist/src/core/indicators/pvtState.d.ts +0 -14
  104. package/dist/src/core/indicators/rocState.d.ts +0 -16
  105. package/dist/src/core/indicators/rsiState.d.ts +0 -43
  106. package/dist/src/core/indicators/sarState.d.ts +0 -26
  107. package/dist/src/core/indicators/scheduler.d.ts +0 -262
  108. package/dist/src/core/indicators/soa.d.ts +0 -115
  109. package/dist/src/core/indicators/stateComposer.d.ts +0 -146
  110. package/dist/src/core/indicators/stochState.d.ts +0 -18
  111. package/dist/src/core/indicators/structureState.d.ts +0 -43
  112. package/dist/src/core/indicators/supertrendState.d.ts +0 -22
  113. package/dist/src/core/indicators/temaState.d.ts +0 -16
  114. package/dist/src/core/indicators/trixState.d.ts +0 -20
  115. package/dist/src/core/indicators/vmaState.d.ts +0 -16
  116. package/dist/src/core/indicators/volumeProfileState.d.ts +0 -34
  117. package/dist/src/core/indicators/vwapState.d.ts +0 -16
  118. package/dist/src/core/indicators/wmaState.d.ts +0 -16
  119. package/dist/src/core/indicators/wmsrState.d.ts +0 -15
  120. package/dist/src/core/indicators/workerProtocol.d.ts +0 -496
  121. package/dist/src/core/indicators/zonesState.d.ts +0 -26
  122. package/dist/src/core/layout/pane.d.ts +0 -103
  123. package/dist/src/core/marker/registry.d.ts +0 -174
  124. package/dist/src/core/paneRenderer.d.ts +0 -45
  125. package/dist/src/core/renderers/Indicator/atr.d.ts +0 -17
  126. package/dist/src/core/renderers/Indicator/boll.d.ts +0 -2
  127. package/dist/src/core/renderers/Indicator/cci.d.ts +0 -22
  128. package/dist/src/core/renderers/Indicator/chaikinVol.d.ts +0 -4
  129. package/dist/src/core/renderers/Indicator/cmf.d.ts +0 -4
  130. package/dist/src/core/renderers/Indicator/dema.d.ts +0 -5
  131. package/dist/src/core/renderers/Indicator/donchian.d.ts +0 -5
  132. package/dist/src/core/renderers/Indicator/ene.d.ts +0 -2
  133. package/dist/src/core/renderers/Indicator/expma.d.ts +0 -2
  134. package/dist/src/core/renderers/Indicator/fastk.d.ts +0 -22
  135. package/dist/src/core/renderers/Indicator/fib.d.ts +0 -4
  136. package/dist/src/core/renderers/Indicator/hma.d.ts +0 -5
  137. package/dist/src/core/renderers/Indicator/hv.d.ts +0 -4
  138. package/dist/src/core/renderers/Indicator/ichimoku.d.ts +0 -5
  139. package/dist/src/core/renderers/Indicator/index.d.ts +0 -59
  140. package/dist/src/core/renderers/Indicator/indicatorData.d.ts +0 -13
  141. package/dist/src/core/renderers/Indicator/kama.d.ts +0 -5
  142. package/dist/src/core/renderers/Indicator/keltner.d.ts +0 -5
  143. package/dist/src/core/renderers/Indicator/kst.d.ts +0 -22
  144. package/dist/src/core/renderers/Indicator/ma.d.ts +0 -3
  145. package/dist/src/core/renderers/Indicator/macd.d.ts +0 -50
  146. package/dist/src/core/renderers/Indicator/macdLegend.d.ts +0 -12
  147. package/dist/src/core/renderers/Indicator/mainIndicatorLegend.d.ts +0 -10
  148. package/dist/src/core/renderers/Indicator/mfi.d.ts +0 -4
  149. package/dist/src/core/renderers/Indicator/mom.d.ts +0 -22
  150. package/dist/src/core/renderers/Indicator/obv.d.ts +0 -4
  151. package/dist/src/core/renderers/Indicator/parkinson.d.ts +0 -4
  152. package/dist/src/core/renderers/Indicator/pivot.d.ts +0 -4
  153. package/dist/src/core/renderers/Indicator/pvt.d.ts +0 -4
  154. package/dist/src/core/renderers/Indicator/roc.d.ts +0 -5
  155. package/dist/src/core/renderers/Indicator/rsi.d.ts +0 -33
  156. package/dist/src/core/renderers/Indicator/sar.d.ts +0 -5
  157. package/dist/src/core/renderers/Indicator/scale/atr_scale.d.ts +0 -11
  158. package/dist/src/core/renderers/Indicator/scale/cci_scale.d.ts +0 -11
  159. package/dist/src/core/renderers/Indicator/scale/fastk_scale.d.ts +0 -11
  160. package/dist/src/core/renderers/Indicator/scale/indicator_scale.d.ts +0 -38
  161. package/dist/src/core/renderers/Indicator/scale/kst_scale.d.ts +0 -11
  162. package/dist/src/core/renderers/Indicator/scale/macd_scale.d.ts +0 -14
  163. package/dist/src/core/renderers/Indicator/scale/mom_scale.d.ts +0 -11
  164. package/dist/src/core/renderers/Indicator/scale/rsi_scale.d.ts +0 -11
  165. package/dist/src/core/renderers/Indicator/scale/stoch_scale.d.ts +0 -11
  166. package/dist/src/core/renderers/Indicator/scale/volume_scale.d.ts +0 -14
  167. package/dist/src/core/renderers/Indicator/scale/wmsr_scale.d.ts +0 -11
  168. package/dist/src/core/renderers/Indicator/stoch.d.ts +0 -22
  169. package/dist/src/core/renderers/Indicator/structure.d.ts +0 -4
  170. package/dist/src/core/renderers/Indicator/subPaneConfig.d.ts +0 -9
  171. package/dist/src/core/renderers/Indicator/supertrend.d.ts +0 -5
  172. package/dist/src/core/renderers/Indicator/tema.d.ts +0 -5
  173. package/dist/src/core/renderers/Indicator/trix.d.ts +0 -5
  174. package/dist/src/core/renderers/Indicator/vma.d.ts +0 -4
  175. package/dist/src/core/renderers/Indicator/volumeProfile.d.ts +0 -4
  176. package/dist/src/core/renderers/Indicator/vwap.d.ts +0 -4
  177. package/dist/src/core/renderers/Indicator/wma.d.ts +0 -5
  178. package/dist/src/core/renderers/Indicator/wmsr.d.ts +0 -22
  179. package/dist/src/core/renderers/Indicator/zones.d.ts +0 -4
  180. package/dist/src/core/renderers/candle.d.ts +0 -21
  181. package/dist/src/core/renderers/crosshair.d.ts +0 -17
  182. package/dist/src/core/renderers/customMarkers.d.ts +0 -6
  183. package/dist/src/core/renderers/extremaMarkers.d.ts +0 -5
  184. package/dist/src/core/renderers/gridLines.d.ts +0 -7
  185. package/dist/src/core/renderers/lastPrice.d.ts +0 -9
  186. package/dist/src/core/renderers/paneTitle.d.ts +0 -40
  187. package/dist/src/core/renderers/subVolume.d.ts +0 -13
  188. package/dist/src/core/renderers/timeAxis.d.ts +0 -14
  189. package/dist/src/core/renderers/webgl/candleSurface.d.ts +0 -80
  190. package/dist/src/core/renderers/webgl/sharedWebGLSurface.d.ts +0 -33
  191. package/dist/src/core/renderers/yAxis.d.ts +0 -14
  192. package/dist/src/core/scale/logFormula.d.ts +0 -66
  193. package/dist/src/core/scale/price.d.ts +0 -11
  194. package/dist/src/core/scale/priceScale.d.ts +0 -87
  195. package/dist/src/core/subPaneManager.d.ts +0 -22
  196. package/dist/src/core/theme/colors.d.ts +0 -222
  197. package/dist/src/core/theme/fonts.d.ts +0 -12
  198. package/dist/src/core/utils/klineConfig.d.ts +0 -28
  199. package/dist/src/core/utils/tickCount.d.ts +0 -8
  200. package/dist/src/core/utils/tickPosition.d.ts +0 -24
  201. package/dist/src/core/utils/zoom.d.ts +0 -30
  202. package/dist/src/core/viewport/viewport.d.ts +0 -31
  203. package/dist/src/index.d.ts +0 -8
  204. package/dist/src/main.d.ts +0 -0
  205. package/dist/src/plugin/ConfigManager.d.ts +0 -31
  206. package/dist/src/plugin/EventBus.d.ts +0 -34
  207. package/dist/src/plugin/HookSystem.d.ts +0 -28
  208. package/dist/src/plugin/PluginHost.d.ts +0 -57
  209. package/dist/src/plugin/PluginRegistry.d.ts +0 -40
  210. package/dist/src/plugin/StateStore.d.ts +0 -37
  211. package/dist/src/plugin/index.d.ts +0 -11
  212. package/dist/src/plugin/rendererPluginManager.d.ts +0 -77
  213. package/dist/src/plugin/stateKeys.d.ts +0 -6
  214. package/dist/src/plugin/types.d.ts +0 -445
  215. package/dist/src/semantic/controller.d.ts +0 -35
  216. package/dist/src/semantic/drawShape.d.ts +0 -14
  217. package/dist/src/semantic/index.d.ts +0 -8
  218. package/dist/src/semantic/types.d.ts +0 -298
  219. package/dist/src/semantic/validator.d.ts +0 -43
  220. package/dist/src/test-setup.d.ts +0 -6
  221. package/dist/src/types/kLine.d.ts +0 -3
  222. package/dist/src/types/price.d.ts +0 -31
  223. package/dist/src/types/volumePrice.d.ts +0 -26
  224. package/dist/src/utils/cache.d.ts +0 -33
  225. package/dist/src/utils/dateFormat.d.ts +0 -83
  226. package/dist/src/utils/http.d.ts +0 -14
  227. package/dist/src/utils/kLineDraw/MA.d.ts +0 -7
  228. package/dist/src/utils/kLineDraw/axis.d.ts +0 -150
  229. package/dist/src/utils/kLineDraw/grid.d.ts +0 -30
  230. package/dist/src/utils/kLineDraw/kLine.d.ts +0 -15
  231. package/dist/src/utils/kline/format.d.ts +0 -11
  232. package/dist/src/utils/kline/viewport.d.ts +0 -10
  233. package/dist/src/utils/logger.d.ts +0 -5
  234. package/dist/src/utils/mock/genRandomPriceData.d.ts +0 -3
  235. package/dist/src/utils/priceToY.d.ts +0 -7
  236. package/dist/src/utils/volumePrice.d.ts +0 -54
@@ -1,2 +1,2 @@
1
- .kline-tooltip[data-v-623c9424]{z-index:10;color:#000000c7;pointer-events:none;backdrop-filter:blur(6px);background:#ffffffeb;border:1px solid #0000001f;border-radius:8px;min-width:200px;max-width:260px;padding:10px 12px;font-size:12px;line-height:1.4;position:absolute;box-shadow:0 6px 18px #0000001f}.kline-tooltip__title[data-v-623c9424]{justify-content:space-between;gap:10px;margin-bottom:6px;font-weight:600;display:flex}.kline-tooltip__grid[data-v-623c9424]{grid-template-columns:1fr;gap:2px;display:grid}.kline-tooltip__grid .row[data-v-623c9424]{justify-content:space-between;gap:10px;display:flex}.kline-tooltip__grid .row span[data-v-623c9424]:first-child{color:#0000008f}@supports (anchor-name:--kmap-anchor) and (position-anchor:--kmap-anchor){.kline-tooltip.use-anchor[data-v-623c9424]{position-anchor:--kline-tooltip-anchor;left:anchor(left);top:anchor(top);position:absolute}.kline-tooltip.use-anchor.anchor-right-bottom[data-v-623c9424]{transform:translate(14px,14px)}.kline-tooltip.use-anchor.anchor-left-bottom[data-v-623c9424]{transform:translate(calc(-100% - 14px),14px)}}.marker-tooltip[data-v-5574cc25]{z-index:10;color:#000000c7;pointer-events:none;backdrop-filter:blur(6px);background:#ffffffeb;border:1px solid #0000001f;border-radius:8px;min-width:180px;max-width:260px;padding:10px 12px;font-size:12px;line-height:1.4;position:absolute;box-shadow:0 6px 18px #0000001f}.marker-tooltip__title[data-v-5574cc25]{justify-content:space-between;gap:10px;margin-bottom:6px;font-weight:600;display:flex}.marker-tooltip__content[data-v-5574cc25]{grid-template-columns:1fr;gap:2px;display:grid}.marker-tooltip__content .row[data-v-5574cc25]{justify-content:space-between;gap:10px;display:flex}.marker-tooltip__content .row span[data-v-5574cc25]:first-child{color:#0000008f}@supports (anchor-name:--kmap-anchor) and (position-anchor:--kmap-anchor){.marker-tooltip.use-anchor[data-v-5574cc25]{position-anchor:--marker-tooltip-anchor;left:anchor(left);top:anchor(top);position:absolute}.marker-tooltip.use-anchor.anchor-right-bottom[data-v-5574cc25]{transform:translate(12px,12px)}.marker-tooltip.use-anchor.anchor-left-bottom[data-v-5574cc25]{transform:translate(calc(-100% - 12px),12px)}}.params-overlay[data-v-bb1d1eb3]{backdrop-filter:blur(4px);z-index:1000;background:#0000004d;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.indicator-params[data-v-bb1d1eb3]{background:#fff;border:1px solid #e0e0e0;border-radius:12px;width:90vw;min-width:340px;max-width:420px;overflow:hidden;box-shadow:0 8px 40px #00000026}.params-header[data-v-bb1d1eb3]{background:#f8f8f8;border-bottom:1px solid #e8e8e8;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.header-left[data-v-bb1d1eb3]{align-items:baseline;gap:8px;display:flex}.header-right[data-v-bb1d1eb3]{align-items:center;gap:8px;display:flex}.params-title[data-v-bb1d1eb3]{color:#1a1a1a;letter-spacing:.2px;font-size:14px;font-weight:600}.params-subtitle[data-v-bb1d1eb3]{color:#999;font-size:11px}.toggle-desc-btn[data-v-bb1d1eb3]{cursor:pointer;color:#888;background:#fff;border:1px solid #e0e0e0;border-radius:6px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;transition:all .2s;display:flex}.toggle-desc-btn[data-v-bb1d1eb3]:hover{color:#555;background:#f0f0f0;border-color:#ccc}.toggle-desc-btn.active[data-v-bb1d1eb3]{color:#fff;background:#1a1a1a;border-color:#1a1a1a}.toggle-desc-btn svg[data-v-bb1d1eb3]{width:14px;height:14px}.params-close[data-v-bb1d1eb3]{cursor:pointer;color:#888;background:#fff;border:1px solid #e0e0e0;border-radius:6px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;transition:background .15s,color .15s,border-color .15s;display:flex}.params-close[data-v-bb1d1eb3]:hover{color:#333;background:#f0f0f0;border-color:#ccc}.params-close svg[data-v-bb1d1eb3]{width:14px;height:14px}.indicator-description[data-v-bb1d1eb3]{background:#f0f7ff;border-bottom:1px solid #d6e8f5;padding:12px 20px}.indicator-description p[data-v-bb1d1eb3]{color:#2c5282;margin:0;font-size:12px;line-height:1.6}.params-body[data-v-bb1d1eb3]{flex-direction:column;gap:10px;padding:16px 20px;display:flex}.param-item[data-v-bb1d1eb3]{background:#f8f8f8;border:1px solid #e8e8e8;border-radius:8px;padding:10px 14px;transition:border-color .2s}.param-item[data-v-bb1d1eb3]:has(.param-input:focus){border-color:#bbb}.param-item.has-desc[data-v-bb1d1eb3]{padding:10px 14px 8px}.param-header[data-v-bb1d1eb3]{justify-content:space-between;align-items:center;gap:16px;display:flex}.param-label[data-v-bb1d1eb3]{flex-direction:column;gap:3px;display:flex}.param-label-text[data-v-bb1d1eb3]{color:#333;font-size:13px;font-weight:500}.param-range[data-v-bb1d1eb3]{color:#999;font-size:11px}.param-description[data-v-bb1d1eb3]{color:#666;border-top:1px dashed #e0e0e0;margin-top:8px;padding-top:8px;font-size:11px;line-height:1.5}.input-wrapper[data-v-bb1d1eb3]{background:#fff;border:1px solid #d0d0d0;border-radius:7px;align-items:stretch;height:32px;transition:border-color .2s;display:flex;overflow:hidden}.input-wrapper[data-v-bb1d1eb3]:focus-within{border-color:#999}.stepper-btn[data-v-bb1d1eb3]{cursor:pointer;color:#666;background:#f0f0f0;border:none;flex-shrink:0;justify-content:center;align-items:center;width:28px;font-size:15px;font-weight:400;line-height:1;transition:background .15s,color .15s;display:flex}.stepper-btn[data-v-bb1d1eb3]:hover:not(:disabled){color:#333;background:#e0e0e0}.stepper-btn[data-v-bb1d1eb3]:disabled{color:#ccc;cursor:not-allowed}.param-input[data-v-bb1d1eb3]{text-align:center;color:#1a1a1a;appearance:textfield;background:0 0;border:none;border-left:1px solid #e8e8e8;border-right:1px solid #e8e8e8;width:60px;font-size:13px;font-weight:600}.param-input[data-v-bb1d1eb3]::-webkit-inner-spin-button{-webkit-appearance:none}.param-input[data-v-bb1d1eb3]::-webkit-outer-spin-button{-webkit-appearance:none}.param-input[data-v-bb1d1eb3]:focus{outline:none}.params-footer[data-v-bb1d1eb3]{background:#f8f8f8;border-top:1px solid #e8e8e8;justify-content:space-between;align-items:center;padding:12px 20px;display:flex}.footer-right[data-v-bb1d1eb3]{gap:8px;display:flex}.params-btn[data-v-bb1d1eb3]{cursor:pointer;border:1px solid #0000;border-radius:7px;align-items:center;gap:5px;padding:6px 14px;font-size:13px;font-weight:500;line-height:1.4;transition:all .15s;display:flex}.params-btn svg[data-v-bb1d1eb3]{flex-shrink:0;width:12px;height:12px}.params-btn.reset[data-v-bb1d1eb3]{color:#666;background:0 0;border-color:#d0d0d0}.params-btn.reset[data-v-bb1d1eb3]:hover{color:#e74c3c;background:#e74c3c14;border-color:#c0392b}.params-btn.cancel[data-v-bb1d1eb3]{color:#666;background:0 0;border-color:#d0d0d0}.params-btn.cancel[data-v-bb1d1eb3]:hover{color:#333;background:#f0f0f0;border-color:#bbb}.params-btn.confirm[data-v-bb1d1eb3]{color:#fff;background:#1a1a1a;border-color:#1a1a1a}.params-btn.confirm[data-v-bb1d1eb3]:hover{background:#333;border-color:#333;transform:translateY(-1px);box-shadow:0 2px 10px #00000026}.params-btn.confirm[data-v-bb1d1eb3]:active{box-shadow:none;transform:translateY(0)}.overlay-enter-active[data-v-bb1d1eb3],.overlay-leave-active[data-v-bb1d1eb3]{transition:opacity .2s}.overlay-enter-from[data-v-bb1d1eb3],.overlay-leave-to[data-v-bb1d1eb3]{opacity:0}.modal-enter-active[data-v-bb1d1eb3]{transition:all .22s cubic-bezier(.34,1.56,.64,1)}.modal-leave-active[data-v-bb1d1eb3]{transition:all .16s ease-in}.modal-enter-from[data-v-bb1d1eb3]{opacity:0;transform:scale(.88)translateY(-16px)}.modal-leave-to[data-v-bb1d1eb3]{opacity:0;transform:scale(.94)translateY(8px)}.slide-enter-active[data-v-bb1d1eb3],.slide-leave-active[data-v-bb1d1eb3]{transition:all .2s;overflow:hidden}.slide-enter-from[data-v-bb1d1eb3],.slide-leave-to[data-v-bb1d1eb3]{opacity:0;max-height:0;margin-top:0;padding-top:0;padding-bottom:0}.indicator-selector[data-v-e9eb306c]{width:80%;margin:20px;position:relative}.indicator-scroll-container[data-v-e9eb306c]{scrollbar-width:none;-webkit-overflow-scrolling:touch;text-align:center;width:100%;overflow:auto hidden}.indicator-scroll-container[data-v-e9eb306c]::-webkit-scrollbar{display:none}.indicator-list[data-v-e9eb306c]{gap:8px;margin:0 auto;padding:2px;display:inline-flex}.indicator-divider[data-v-e9eb306c]{background:#d9d9d9;align-self:center;width:1px;height:20px}.indicator-item[data-v-e9eb306c]{align-items:center;gap:4px;display:flex}.indicator-item.draggable[data-v-e9eb306c],.indicator-item.draggable .indicator-btn[data-v-e9eb306c],.indicator-item.draggable[data-v-e9eb306c]:hover,.indicator-item.draggable:hover .indicator-btn[data-v-e9eb306c]{cursor:move}.indicator-item.is-dragging[data-v-e9eb306c]{opacity:.6}.indicator-item.drag-over .indicator-btn[data-v-e9eb306c]{border-color:#1a1a1a;box-shadow:0 0 0 2px #1a1a1a1f}.indicator-btn-wrapper[data-v-e9eb306c]{position:relative}.indicator-btn[data-v-e9eb306c]{color:#666;cursor:pointer;white-space:nowrap;background:#fff;border:1px solid #e0e0e0;border-radius:16px;flex-shrink:0;justify-content:center;align-items:center;gap:4px;padding:6px 16px;font-size:13px;font-weight:500;transition:all .3s;display:flex;position:relative;overflow:hidden}.indicator-btn[data-v-e9eb306c]:hover:not(.hovering){color:#333;background:#f8f8f8;border-color:#ccc}.indicator-btn.active[data-v-e9eb306c]{color:#1a1a1a;background:#f8f8f8;border-color:#1a1a1a}.indicator-btn.active[data-v-e9eb306c]:hover:not(.hovering){background:#f0f0f0;border-color:#333}.btn-content[data-v-e9eb306c]{z-index:1;position:relative}.param-hint[data-v-e9eb306c]{opacity:.85;font-size:11px}.hover-overlay[data-v-e9eb306c]{backdrop-filter:blur(4px);z-index:2;background:#ffffffd9;border-radius:16px;justify-content:center;align-items:center;gap:4px;display:flex;position:absolute;inset:0}.action-btn[data-v-e9eb306c]{color:#666;cursor:pointer;background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;width:24px;height:24px;padding:0;transition:all .2s;display:flex}.action-btn[data-v-e9eb306c]:hover{color:#333;background:#0000000f}.settings-btn[data-v-e9eb306c]:hover{color:#1a1a1a}.remove-btn[data-v-e9eb306c]:hover{color:#ff4d4f}.divider[data-v-e9eb306c]{background:#e0e0e0;width:1px;height:14px}.add-btn[data-v-e9eb306c]{color:#999;cursor:pointer;background:0 0;border:1px dashed #d9d9d9;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;width:32px;height:32px;padding:0;transition:all .3s;display:flex}.add-btn[data-v-e9eb306c]:hover{color:#1a1a1a;background:#1a1a1a0a;border-color:#1a1a1a}.selector-overlay[data-v-e9eb306c]{backdrop-filter:blur(4px);z-index:1000;background:#0000004d;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.selector-modal[data-v-e9eb306c]{background:#fff;border:1px solid #e0e0e0;border-radius:12px;flex-direction:column;width:90vw;max-width:860px;max-height:85vh;display:flex;overflow:hidden;box-shadow:0 8px 40px #00000026}.modal-header[data-v-e9eb306c]{background:#f8f8f8;border-bottom:1px solid #e8e8e8;flex-shrink:0;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.header-title[data-v-e9eb306c]{flex-direction:column;gap:2px;display:flex}.title-text[data-v-e9eb306c]{color:#1a1a1a;letter-spacing:.2px;font-size:14px;font-weight:600}.title-sub[data-v-e9eb306c]{color:#999;font-size:11px}.modal-close[data-v-e9eb306c]{cursor:pointer;color:#888;background:#fff;border:1px solid #e0e0e0;border-radius:6px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;transition:all .15s;display:flex}.modal-close[data-v-e9eb306c]:hover{color:#333;background:#f0f0f0;border-color:#ccc}.modal-close svg[data-v-e9eb306c]{width:14px;height:14px}.header-actions[data-v-e9eb306c]{align-items:center;gap:8px;display:flex}.view-toggle-btn[data-v-e9eb306c]{cursor:pointer;color:#888;background:#fff;border:1px solid #e0e0e0;border-radius:6px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;transition:all .15s;display:flex}.view-toggle-btn[data-v-e9eb306c]:hover{color:#333;background:#f0f0f0;border-color:#ccc}.view-toggle-btn.active[data-v-e9eb306c]{color:#fff;background:#1a1a1a;border-color:#1a1a1a}.modal-body[data-v-e9eb306c]{flex-direction:column;flex:1;gap:20px;padding:20px;display:flex;overflow-y:auto}.indicator-section[data-v-e9eb306c]{flex-direction:column;gap:12px;display:flex}.section-header[data-v-e9eb306c]{align-items:center;gap:8px;display:flex}.section-title[data-v-e9eb306c]{color:#1a1a1a;font-size:13px;font-weight:600}.section-count[data-v-e9eb306c]{color:#999;background:#f0f0f0;border-radius:10px;padding:2px 8px;font-size:11px}.indicator-grid[data-v-e9eb306c]{grid-template-columns:repeat(auto-fill,minmax(195px,1fr));gap:10px;display:grid}.indicator-grid.compact[data-v-e9eb306c]{flex-wrap:wrap;gap:8px;display:flex}.indicator-grid.compact .indicator-card[data-v-e9eb306c]{white-space:nowrap;border-radius:16px;justify-content:center;align-items:center;min-height:32px;padding:6px 14px;display:inline-flex;position:relative}.indicator-grid.compact .indicator-card .card-tooltip[data-v-e9eb306c]{color:#fff;white-space:nowrap;pointer-events:none;opacity:0;z-index:10;background:#333;border-radius:6px;padding:4px 10px;font-size:12px;transition:opacity .15s;position:absolute;bottom:calc(100% + 6px);left:50%;transform:translate(-50%)}.indicator-grid.compact .indicator-card:hover .card-tooltip[data-v-e9eb306c]{opacity:1}.indicator-grid.compact .indicator-card .card-label[data-v-e9eb306c]{font-size:12px;font-weight:500}.indicator-card[data-v-e9eb306c]{cursor:pointer;text-align:left;background:#fff;border:1px solid #e8e8e8;border-radius:8px;flex-direction:column;gap:4px;padding:12px 14px;transition:all .15s;display:flex}.indicator-card[data-v-e9eb306c]:hover:not(.disabled){background:#fafafa;border-color:#1a1a1a;transform:translateY(-1px);box-shadow:0 2px 8px #0000000f}.indicator-card.active[data-v-e9eb306c]{background:#f8f8f8;border-color:#1a1a1a}.card-header[data-v-e9eb306c]{justify-content:space-between;align-items:center;gap:8px;display:flex}.card-label[data-v-e9eb306c]{color:#1a1a1a;font-size:13px;font-weight:600}.card-header-actions[data-v-e9eb306c]{align-items:center;gap:4px;display:flex}.card-settings-btn[data-v-e9eb306c]{color:#bbb;cursor:pointer;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;width:20px;height:20px;padding:0;transition:all .15s;display:flex}.card-settings-btn[data-v-e9eb306c]:hover{color:#555;background:#f0f0f0}.card-name[data-v-e9eb306c]{color:#666;font-size:11px;line-height:1.4}.card-params[data-v-e9eb306c]{color:#999;margin-top:2px;font-size:10px}.section-divider[data-v-e9eb306c]{background:linear-gradient(90deg,#0000,#e0e0e0,#0000);height:1px;margin:4px 0}.modal-footer[data-v-e9eb306c]{background:#f8f8f8;border-top:1px solid #e8e8e8;flex-shrink:0;justify-content:space-between;align-items:center;padding:12px 20px;display:flex}.footer-info[data-v-e9eb306c]{color:#666;font-size:12px}.info-text[data-v-e9eb306c]{color:#999}.btn[data-v-e9eb306c]{cursor:pointer;border:1px solid #0000;border-radius:7px;align-items:center;gap:5px;padding:6px 16px;font-size:13px;font-weight:500;line-height:1.4;transition:all .15s;display:flex}.btn-confirm[data-v-e9eb306c]{color:#fff;background:#1a1a1a;border-color:#1a1a1a}.btn-confirm[data-v-e9eb306c]:hover{background:#333;border-color:#333;transform:translateY(-1px);box-shadow:0 2px 10px #00000026}.fade-enter-active[data-v-e9eb306c],.fade-leave-active[data-v-e9eb306c]{transition:opacity .2s}.fade-enter-from[data-v-e9eb306c],.fade-leave-to[data-v-e9eb306c]{opacity:0}.overlay-enter-active[data-v-e9eb306c],.overlay-leave-active[data-v-e9eb306c]{transition:opacity .2s}.overlay-enter-from[data-v-e9eb306c],.overlay-leave-to[data-v-e9eb306c]{opacity:0}.modal-enter-active[data-v-e9eb306c]{transition:all .22s cubic-bezier(.34,1.56,.64,1)}.modal-leave-active[data-v-e9eb306c]{transition:all .16s ease-in}.modal-enter-from[data-v-e9eb306c]{opacity:0;transform:scale(.88)translateY(-16px)}.modal-leave-to[data-v-e9eb306c]{opacity:0;transform:scale(.94)translateY(8px)}@media (width<=640px){.selector-modal[data-v-e9eb306c]{width:95vw;max-height:90vh}.indicator-grid[data-v-e9eb306c]{grid-template-columns:1fr}.modal-body[data-v-e9eb306c]{padding:16px}}.drawing-style-toolbar[data-v-92699cb2]{-webkit-backdrop-filter:blur(8px);z-index:100;user-select:none;pointer-events:auto;background:#fafbfce0;border:1px solid #e5e7eb;border-radius:6px;align-items:center;gap:6px;height:32px;padding:4px 8px;display:flex;position:absolute;top:8px;left:50%;transform:translate(-50%);box-shadow:0 1px 3px #0000000f}.toolbar-item[data-v-92699cb2]{justify-content:center;align-items:center;display:inline-flex}.color-item[data-v-92699cb2]{width:24px;height:24px;position:relative}.color-swatch[data-v-92699cb2]{cursor:pointer;border:1px solid #d1d5db;border-radius:4px;width:100%;height:100%;display:block}.color-input[data-v-92699cb2]{opacity:0;cursor:pointer;width:100%;height:100%;position:absolute;inset:0}.toolbar-select[data-v-92699cb2]{color:#374151;cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:4px;outline:none;height:24px;padding:0 4px;font-size:12px}.toolbar-select[data-v-92699cb2]:hover{border-color:#9ca3af}.toolbar-btn[data-v-92699cb2]{color:#6b7280;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:4px;justify-content:center;align-items:center;width:24px;height:24px;padding:0;transition:border-color .15s,background .15s,color .15s;display:inline-flex}.toolbar-btn[data-v-92699cb2]:hover{color:#374151;background:#f3f4f6;border-color:#d1d5db}.delete-btn[data-v-92699cb2]:hover{color:#dc2626;background:#fef2f2;border-color:#fca5a5}.delete-icon[data-v-92699cb2]{width:14px;height:14px}.left-toolbar[data-v-20a39cb3]{box-sizing:border-box;user-select:none;background:#fafbfc;border:1px solid #e5e7eb;border-radius:6px;flex-direction:column;flex:0 0 40px;align-items:center;gap:6px;padding:8px 5px;display:flex;box-shadow:0 1px 3px #0000000f}.left-toolbar__group[data-v-20a39cb3]{flex-direction:column;gap:4px;display:flex}.left-toolbar__divider[data-v-20a39cb3]{background:#e5e7eb;width:18px;height:1px}.left-toolbar__button[data-v-20a39cb3]{color:#6b7280;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:4px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;transition:border-color .15s,background .15s,color .15s;display:inline-flex;position:relative}.left-toolbar__button[data-v-20a39cb3]:hover{color:#374151;background:#f3f4f6;border-color:#d1d5db}.left-toolbar__button.active[data-v-20a39cb3]{color:#1f2937;background:#e5e7eb;border-color:#9ca3af}.left-toolbar__button[data-v-20a39cb3]:focus-visible{border-color:#6b7280;outline:none}.tool-icon[data-v-20a39cb3]{width:16px;height:16px}.corner-indicator[data-v-20a39cb3]{cursor:pointer;width:8px;height:8px;position:absolute;bottom:0;right:0;overflow:hidden}.corner-indicator[data-v-20a39cb3]:after{content:"";opacity:.45;border-bottom:5px solid;border-left:5px solid #0000;width:0;height:0;transition:opacity .15s;position:absolute;bottom:0;right:0}.left-toolbar__button:hover .corner-indicator[data-v-20a39cb3]:after,.left-toolbar__button.active .corner-indicator[data-v-20a39cb3]:after{opacity:.7}.corner-indicator.open[data-v-20a39cb3]:after{opacity:.8}.tool-dropdown[data-v-20a39cb3]{-webkit-backdrop-filter:blur(8px);box-sizing:border-box;z-index:100;background:#fafbfcd1;border:1px solid #e5e7eb;border-radius:6px;flex-direction:row;align-items:center;gap:4px;height:40px;padding:0 5px;display:flex;position:absolute;top:50%;left:calc(100% + 13px);transform:translateY(-50%);box-shadow:0 1px 3px #0000000f}.tool-item[data-v-20a39cb3]{position:relative}.dropdown-enter-active[data-v-20a39cb3],.dropdown-leave-active[data-v-20a39cb3]{transition:opacity .15s,transform .15s}.dropdown-enter-from[data-v-20a39cb3],.dropdown-leave-to[data-v-20a39cb3]{opacity:0;transform:translateY(-50%)translate(-6px)}@media (width<=768px),(height<=640px){.left-toolbar[data-v-20a39cb3]{border-radius:5px;flex-basis:36px;gap:5px;padding:6px 4px}.left-toolbar__group[data-v-20a39cb3]{gap:3px}.left-toolbar__button[data-v-20a39cb3]{border-radius:3px;width:26px;height:26px}.left-toolbar__divider[data-v-20a39cb3]{width:16px}.corner-indicator[data-v-20a39cb3]{width:7px;height:7px}.corner-indicator[data-v-20a39cb3]:after{border-bottom-width:4px;border-left-width:4px}.tool-dropdown[data-v-20a39cb3]{height:36px}}.settings-overlay[data-v-20a39cb3]{backdrop-filter:blur(4px);z-index:1000;background:#0000004d;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.settings-modal[data-v-20a39cb3]{background:#fff;border:1px solid #e0e0e0;border-radius:12px;width:90vw;min-width:340px;max-width:420px;overflow:hidden;box-shadow:0 8px 40px #00000026}.settings-header[data-v-20a39cb3]{background:#f8f8f8;border-bottom:1px solid #e8e8e8;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.header-left[data-v-20a39cb3]{align-items:baseline;gap:8px;display:flex}.header-right[data-v-20a39cb3]{align-items:center;gap:8px;display:flex}.settings-title[data-v-20a39cb3]{color:#1a1a1a;letter-spacing:.2px;font-size:14px;font-weight:600}.settings-subtitle[data-v-20a39cb3]{color:#999;font-size:11px}.settings-close[data-v-20a39cb3]{cursor:pointer;color:#888;background:#fff;border:1px solid #e0e0e0;border-radius:6px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;transition:background .15s,color .15s,border-color .15s;display:flex}.settings-close[data-v-20a39cb3]:hover{color:#333;background:#f0f0f0;border-color:#ccc}.settings-close svg[data-v-20a39cb3]{width:14px;height:14px}.settings-body[data-v-20a39cb3]{flex-direction:column;gap:10px;padding:16px 20px;display:flex}.settings-item[data-v-20a39cb3]{background:#f8f8f8;border:1px solid #e8e8e8;border-radius:8px;padding:8px 12px}.settings-label[data-v-20a39cb3]{color:#333;cursor:pointer;justify-content:space-between;align-items:center;font-size:13px;display:flex}.settings-checkbox[data-v-20a39cb3]{cursor:pointer;accent-color:#1a1a1a;width:16px;height:16px}.settings-select[data-v-20a39cb3]{color:#333;cursor:pointer;background:#fff;border:1px solid #d0d0d0;border-radius:6px;outline:none;min-width:140px;padding:4px 8px;font-size:12px}.settings-select[data-v-20a39cb3]:hover{border-color:#9ca3af}.settings-select[data-v-20a39cb3]:focus{border-color:#6b7280;box-shadow:0 0 0 2px #6b728026}.settings-section-divider[data-v-20a39cb3]{align-items:center;gap:8px;margin-top:4px;display:flex}.settings-section-divider[data-v-20a39cb3]:before,.settings-section-divider[data-v-20a39cb3]:after{content:"";border-top:1px solid #e0e0e0;flex:1}.settings-section-label[data-v-20a39cb3]{color:#999;white-space:nowrap;font-size:11px}.settings-item.experimental[data-v-20a39cb3]{background:#fdf8f3;border-color:#f0e0d0}.settings-footer[data-v-20a39cb3]{background:#f8f8f8;border-top:1px solid #e8e8e8;justify-content:space-between;align-items:center;padding:12px 20px;display:flex}.footer-right[data-v-20a39cb3]{gap:8px;display:flex}.settings-btn[data-v-20a39cb3]{cursor:pointer;border:1px solid #0000;border-radius:7px;align-items:center;gap:5px;padding:6px 14px;font-size:13px;font-weight:500;line-height:1.4;transition:all .15s;display:flex}.settings-btn svg[data-v-20a39cb3]{flex-shrink:0;width:12px;height:12px}.settings-btn.reset[data-v-20a39cb3]{color:#666;background:0 0;border-color:#d0d0d0}.settings-btn.reset[data-v-20a39cb3]:hover{color:#e74c3c;background:#e74c3c14;border-color:#c0392b}.settings-btn.cancel[data-v-20a39cb3]{color:#666;background:0 0;border-color:#d0d0d0}.settings-btn.cancel[data-v-20a39cb3]:hover{color:#333;background:#f0f0f0;border-color:#bbb}.settings-btn.confirm[data-v-20a39cb3]{color:#fff;background:#1a1a1a;border-color:#1a1a1a}.settings-btn.confirm[data-v-20a39cb3]:hover{background:#333;border-color:#333;transform:translateY(-1px);box-shadow:0 2px 10px #00000026}.settings-btn.confirm[data-v-20a39cb3]:active{box-shadow:none;transform:translateY(0)}.chart-wrapper[data-v-9448d063]{--kmap-height:var(--kmap-chart-height,100%);--kmap-width:var(--kmap-chart-width,100%);--chart-bg:#fff;--chart-bg-secondary:#f8f9fa;--chart-border:#e5e7eb;--chart-border-active:#3b82f6;--chart-text:#374151;--chart-text-secondary:#6b7280;width:var(--kmap-width);height:var(--kmap-height);flex-direction:column;justify-content:center;align-items:center;min-height:300px;display:flex}.chart-wrapper[data-theme=dark][data-v-9448d063]{--chart-bg:#1a1a2e;--chart-bg-secondary:#16162a;--chart-border:#2d2d44;--chart-border-active:#60a5fa;--chart-text:#e5e7eb;--chart-text-secondary:#9ca3af}.chart-stage[data-v-9448d063]{align-items:stretch;gap:8px;width:95%;height:85%;min-height:255px;display:flex}.chart-main[data-v-9448d063]{flex:auto;align-items:stretch;gap:0;min-width:0;height:100%;display:flex;position:relative}.pane-separator-layer[data-v-9448d063]{pointer-events:none;z-index:20;position:absolute;inset:0}.pane-separator-line[data-v-9448d063]{border-top:1px solid var(--chart-border);opacity:1;box-sizing:border-box;height:0;transition:border-top-color .12s,border-top-width .12s,margin-top .12s,opacity .12s;position:absolute;left:0;right:0}.pane-separator-line.is-active[data-v-9448d063]{border-top-color:var(--chart-border-active);border-top-width:2px;margin-top:-1px}.chart-stage.is-resizing-pane[data-v-9448d063],.chart-stage.is-hovering-pane-separator[data-v-9448d063]{cursor:ns-resize}.chart-stage.is-hovering-kline[data-v-9448d063]{cursor:pointer}.chart-stage.is-hovering-right-axis[data-v-9448d063]{cursor:ns-resize}.chart-stage.is-dragging[data-v-9448d063]{cursor:grabbing}.chart-container[data-v-9448d063]{height:100%;min-height:inherit;scrollbar-width:none;-ms-overflow-style:none;border:1px solid var(--chart-border);box-sizing:border-box;background:var(--chart-bg);-webkit-touch-callout:none;user-select:none;touch-action:none;border-right:0;border-radius:6px 0 0 6px;flex:auto;position:relative;overflow:auto hidden}.chart-container[data-v-9448d063]::-webkit-scrollbar{display:none}.right-axis-host[data-v-9448d063]{height:100%;min-height:inherit;box-sizing:border-box;background:var(--chart-bg);border:1px solid var(--chart-border);-webkit-touch-callout:none;user-select:none;touch-action:none;border-top-right-radius:6px;border-bottom-right-radius:6px;flex:none;position:relative;overflow:visible}.scroll-content[data-v-9448d063]{height:100%;min-height:inherit;position:relative}.canvas-layer[data-v-9448d063]{pointer-events:none;position:sticky;top:0;left:0}.tooltip-layer[data-v-9448d063]{pointer-events:none;z-index:30;position:absolute;inset:0}.tooltip-anchor[data-v-9448d063]{pointer-events:none;width:1px;height:1px;position:absolute}.tooltip-anchor.kline-tooltip-anchor.use-anchor[data-v-9448d063]{anchor-name:--kline-tooltip-anchor}.tooltip-anchor.marker-tooltip-anchor.use-anchor[data-v-9448d063]{anchor-name:--marker-tooltip-anchor}@media (width<=768px),(height<=640px){.chart-stage[data-v-9448d063]{gap:6px}}.plot-canvas{display:block;position:absolute;top:0;left:0}.right-axis{display:block;position:absolute;left:0}.x-axis-canvas{z-index:10;display:block;position:absolute;bottom:0;left:0}.right-axis{z-index:15}
1
+ .drawing-style-toolbar[data-v-87e91714]{-webkit-backdrop-filter:blur(8px);z-index:100;user-select:none;pointer-events:auto;background:#fafbfce0;border:1px solid #e5e7eb;border-radius:6px;align-items:center;gap:6px;height:32px;padding:4px 8px;display:flex;position:absolute;top:8px;left:50%;transform:translate(-50%);box-shadow:0 1px 3px #0000000f}.toolbar-item[data-v-87e91714]{justify-content:center;align-items:center;display:inline-flex}.color-item[data-v-87e91714]{width:24px;height:24px;position:relative}.color-swatch[data-v-87e91714]{cursor:pointer;border:1px solid #d1d5db;border-radius:4px;width:100%;height:100%;display:block}.color-input[data-v-87e91714]{opacity:0;cursor:pointer;width:100%;height:100%;position:absolute;inset:0}.toolbar-select[data-v-87e91714]{color:#374151;cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:4px;outline:none;height:24px;padding:0 4px;font-size:12px}.toolbar-select[data-v-87e91714]:hover{border-color:#9ca3af}.toolbar-btn[data-v-87e91714]{color:#6b7280;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:4px;justify-content:center;align-items:center;width:24px;height:24px;padding:0;transition:border-color .15s,background .15s,color .15s;display:inline-flex}.toolbar-btn[data-v-87e91714]:hover{color:#374151;background:#f3f4f6;border-color:#d1d5db}.delete-btn[data-v-87e91714]:hover{color:#dc2626;background:#fef2f2;border-color:#fca5a5}.delete-icon[data-v-87e91714]{width:14px;height:14px}.params-overlay[data-v-2ffc8f4d]{backdrop-filter:blur(4px);z-index:1000;background:#0000004d;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.indicator-params[data-v-2ffc8f4d]{background:#fff;border:1px solid #e0e0e0;border-radius:12px;width:90vw;min-width:340px;max-width:420px;overflow:hidden;box-shadow:0 8px 40px #00000026}.params-header[data-v-2ffc8f4d]{background:#f8f8f8;border-bottom:1px solid #e8e8e8;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.header-left[data-v-2ffc8f4d]{align-items:baseline;gap:8px;display:flex}.header-right[data-v-2ffc8f4d]{align-items:center;gap:8px;display:flex}.params-title[data-v-2ffc8f4d]{color:#1a1a1a;letter-spacing:.2px;font-size:14px;font-weight:600}.params-subtitle[data-v-2ffc8f4d]{color:#999;font-size:11px}.toggle-desc-btn[data-v-2ffc8f4d]{cursor:pointer;color:#888;background:#fff;border:1px solid #e0e0e0;border-radius:6px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;transition:all .2s;display:flex}.toggle-desc-btn[data-v-2ffc8f4d]:hover{color:#555;background:#f0f0f0;border-color:#ccc}.toggle-desc-btn.active[data-v-2ffc8f4d]{color:#fff;background:#1a1a1a;border-color:#1a1a1a}.toggle-desc-btn svg[data-v-2ffc8f4d]{width:14px;height:14px}.params-close[data-v-2ffc8f4d]{cursor:pointer;color:#888;background:#fff;border:1px solid #e0e0e0;border-radius:6px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;transition:background .15s,color .15s,border-color .15s;display:flex}.params-close[data-v-2ffc8f4d]:hover{color:#333;background:#f0f0f0;border-color:#ccc}.params-close svg[data-v-2ffc8f4d]{width:14px;height:14px}.indicator-description[data-v-2ffc8f4d]{background:#f0f7ff;border-bottom:1px solid #d6e8f5;padding:12px 20px}.indicator-description p[data-v-2ffc8f4d]{color:#2c5282;margin:0;font-size:12px;line-height:1.6}.params-body[data-v-2ffc8f4d]{flex-direction:column;gap:10px;padding:16px 20px;display:flex}.param-item[data-v-2ffc8f4d]{background:#f8f8f8;border:1px solid #e8e8e8;border-radius:8px;padding:10px 14px;transition:border-color .2s}.param-item[data-v-2ffc8f4d]:has(.param-input:focus){border-color:#bbb}.param-item.has-desc[data-v-2ffc8f4d]{padding:10px 14px 8px}.param-header[data-v-2ffc8f4d]{justify-content:space-between;align-items:center;gap:16px;display:flex}.param-label[data-v-2ffc8f4d]{flex-direction:column;gap:3px;display:flex}.param-label-text[data-v-2ffc8f4d]{color:#333;font-size:13px;font-weight:500}.param-range[data-v-2ffc8f4d]{color:#999;font-size:11px}.param-description[data-v-2ffc8f4d]{color:#666;border-top:1px dashed #e0e0e0;margin-top:8px;padding-top:8px;font-size:11px;line-height:1.5}.input-wrapper[data-v-2ffc8f4d]{background:#fff;border:1px solid #d0d0d0;border-radius:7px;align-items:stretch;height:32px;transition:border-color .2s;display:flex;overflow:hidden}.input-wrapper[data-v-2ffc8f4d]:focus-within{border-color:#999}.stepper-btn[data-v-2ffc8f4d]{cursor:pointer;color:#666;background:#f0f0f0;border:none;flex-shrink:0;justify-content:center;align-items:center;width:28px;font-size:15px;font-weight:400;line-height:1;transition:background .15s,color .15s;display:flex}.stepper-btn[data-v-2ffc8f4d]:hover:not(:disabled){color:#333;background:#e0e0e0}.stepper-btn[data-v-2ffc8f4d]:disabled{color:#ccc;cursor:not-allowed}.param-input[data-v-2ffc8f4d]{text-align:center;color:#1a1a1a;appearance:textfield;background:0 0;border:none;border-left:1px solid #e8e8e8;border-right:1px solid #e8e8e8;width:60px;font-size:13px;font-weight:600}.param-input[data-v-2ffc8f4d]::-webkit-inner-spin-button{-webkit-appearance:none}.param-input[data-v-2ffc8f4d]::-webkit-outer-spin-button{-webkit-appearance:none}.param-input[data-v-2ffc8f4d]:focus{outline:none}.params-footer[data-v-2ffc8f4d]{background:#f8f8f8;border-top:1px solid #e8e8e8;justify-content:space-between;align-items:center;padding:12px 20px;display:flex}.footer-right[data-v-2ffc8f4d]{gap:8px;display:flex}.params-btn[data-v-2ffc8f4d]{cursor:pointer;border:1px solid #0000;border-radius:7px;align-items:center;gap:5px;padding:6px 14px;font-size:13px;font-weight:500;line-height:1.4;transition:all .15s;display:flex}.params-btn svg[data-v-2ffc8f4d]{flex-shrink:0;width:12px;height:12px}.params-btn.reset[data-v-2ffc8f4d]{color:#666;background:0 0;border-color:#d0d0d0}.params-btn.reset[data-v-2ffc8f4d]:hover{color:#e74c3c;background:#e74c3c14;border-color:#c0392b}.params-btn.cancel[data-v-2ffc8f4d]{color:#666;background:0 0;border-color:#d0d0d0}.params-btn.cancel[data-v-2ffc8f4d]:hover{color:#333;background:#f0f0f0;border-color:#bbb}.params-btn.confirm[data-v-2ffc8f4d]{color:#fff;background:#1a1a1a;border-color:#1a1a1a}.params-btn.confirm[data-v-2ffc8f4d]:hover{background:#333;border-color:#333;transform:translateY(-1px);box-shadow:0 2px 10px #00000026}.params-btn.confirm[data-v-2ffc8f4d]:active{box-shadow:none;transform:translateY(0)}.overlay-enter-active[data-v-2ffc8f4d],.overlay-leave-active[data-v-2ffc8f4d]{transition:opacity .2s}.overlay-enter-from[data-v-2ffc8f4d],.overlay-leave-to[data-v-2ffc8f4d]{opacity:0}.modal-enter-active[data-v-2ffc8f4d]{transition:all .22s cubic-bezier(.34,1.56,.64,1)}.modal-leave-active[data-v-2ffc8f4d]{transition:all .16s ease-in}.modal-enter-from[data-v-2ffc8f4d]{opacity:0;transform:scale(.88)translateY(-16px)}.modal-leave-to[data-v-2ffc8f4d]{opacity:0;transform:scale(.94)translateY(8px)}.slide-enter-active[data-v-2ffc8f4d],.slide-leave-active[data-v-2ffc8f4d]{transition:all .2s;overflow:hidden}.slide-enter-from[data-v-2ffc8f4d],.slide-leave-to[data-v-2ffc8f4d]{opacity:0;max-height:0;margin-top:0;padding-top:0;padding-bottom:0}.indicator-selector[data-v-09f65e18]{width:80%;margin:20px;position:relative}.indicator-scroll-container[data-v-09f65e18]{scrollbar-width:none;-webkit-overflow-scrolling:touch;text-align:center;width:100%;overflow:auto hidden}.indicator-scroll-container[data-v-09f65e18]::-webkit-scrollbar{display:none}.indicator-list[data-v-09f65e18]{gap:8px;margin:0 auto;padding:2px;display:inline-flex}.indicator-divider[data-v-09f65e18]{background:#d9d9d9;align-self:center;width:1px;height:20px}.indicator-item[data-v-09f65e18]{align-items:center;gap:4px;display:flex}.indicator-item.draggable[data-v-09f65e18],.indicator-item.draggable .indicator-btn[data-v-09f65e18],.indicator-item.draggable[data-v-09f65e18]:hover,.indicator-item.draggable:hover .indicator-btn[data-v-09f65e18]{cursor:move}.indicator-item.is-dragging[data-v-09f65e18]{opacity:.6}.indicator-item.drag-over .indicator-btn[data-v-09f65e18]{border-color:#1a1a1a;box-shadow:0 0 0 2px #1a1a1a1f}.indicator-btn-wrapper[data-v-09f65e18]{position:relative}.indicator-btn[data-v-09f65e18]{color:#666;cursor:pointer;white-space:nowrap;background:#fff;border:1px solid #e0e0e0;border-radius:16px;flex-shrink:0;justify-content:center;align-items:center;gap:4px;padding:6px 16px;font-size:13px;font-weight:500;transition:all .3s;display:flex;position:relative;overflow:hidden}.indicator-btn[data-v-09f65e18]:hover:not(.hovering){color:#333;background:#f8f8f8;border-color:#ccc}.indicator-btn.active[data-v-09f65e18]{color:#1a1a1a;background:#f8f8f8;border-color:#1a1a1a}.indicator-btn.active[data-v-09f65e18]:hover:not(.hovering){background:#f0f0f0;border-color:#333}.btn-content[data-v-09f65e18]{z-index:1;position:relative}.param-hint[data-v-09f65e18]{opacity:.85;font-size:11px}.hover-overlay[data-v-09f65e18]{backdrop-filter:blur(4px);z-index:2;background:#ffffffd9;border-radius:16px;justify-content:center;align-items:center;gap:4px;display:flex;position:absolute;inset:0}.action-btn[data-v-09f65e18]{color:#666;cursor:pointer;background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;width:24px;height:24px;padding:0;transition:all .2s;display:flex}.action-btn[data-v-09f65e18]:hover{color:#333;background:#0000000f}.settings-btn[data-v-09f65e18]:hover{color:#1a1a1a}.remove-btn[data-v-09f65e18]:hover{color:#ff4d4f}.divider[data-v-09f65e18]{background:#e0e0e0;width:1px;height:14px}.add-btn[data-v-09f65e18]{color:#999;cursor:pointer;background:0 0;border:1px dashed #d9d9d9;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;width:32px;height:32px;padding:0;transition:all .3s;display:flex}.add-btn[data-v-09f65e18]:hover{color:#1a1a1a;background:#1a1a1a0a;border-color:#1a1a1a}.selector-overlay[data-v-09f65e18]{backdrop-filter:blur(4px);z-index:1000;background:#0000004d;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.selector-modal[data-v-09f65e18]{background:#fff;border:1px solid #e0e0e0;border-radius:12px;flex-direction:column;width:90vw;max-width:860px;max-height:85vh;display:flex;overflow:hidden;box-shadow:0 8px 40px #00000026}.modal-header[data-v-09f65e18]{background:#f8f8f8;border-bottom:1px solid #e8e8e8;flex-shrink:0;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.header-title[data-v-09f65e18]{flex-direction:column;gap:2px;display:flex}.title-text[data-v-09f65e18]{color:#1a1a1a;letter-spacing:.2px;font-size:14px;font-weight:600}.title-sub[data-v-09f65e18]{color:#999;font-size:11px}.modal-close[data-v-09f65e18]{cursor:pointer;color:#888;background:#fff;border:1px solid #e0e0e0;border-radius:6px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;transition:all .15s;display:flex}.modal-close[data-v-09f65e18]:hover{color:#333;background:#f0f0f0;border-color:#ccc}.modal-close svg[data-v-09f65e18]{width:14px;height:14px}.header-actions[data-v-09f65e18]{align-items:center;gap:8px;display:flex}.view-toggle-btn[data-v-09f65e18]{cursor:pointer;color:#888;background:#fff;border:1px solid #e0e0e0;border-radius:6px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;transition:all .15s;display:flex}.view-toggle-btn[data-v-09f65e18]:hover{color:#333;background:#f0f0f0;border-color:#ccc}.view-toggle-btn.active[data-v-09f65e18]{color:#fff;background:#1a1a1a;border-color:#1a1a1a}.modal-body[data-v-09f65e18]{flex-direction:column;flex:1;gap:20px;padding:20px;display:flex;overflow-y:auto}.indicator-section[data-v-09f65e18]{flex-direction:column;gap:12px;display:flex}.section-header[data-v-09f65e18]{align-items:center;gap:8px;display:flex}.section-title[data-v-09f65e18]{color:#1a1a1a;font-size:13px;font-weight:600}.section-count[data-v-09f65e18]{color:#999;background:#f0f0f0;border-radius:10px;padding:2px 8px;font-size:11px}.indicator-grid[data-v-09f65e18]{grid-template-columns:repeat(auto-fill,minmax(195px,1fr));gap:10px;display:grid}.indicator-grid.compact[data-v-09f65e18]{flex-wrap:wrap;gap:8px;display:flex}.indicator-grid.compact .indicator-card[data-v-09f65e18]{white-space:nowrap;border-radius:16px;justify-content:center;align-items:center;min-height:32px;padding:6px 14px;display:inline-flex;position:relative}.indicator-grid.compact .indicator-card .card-tooltip[data-v-09f65e18]{color:#fff;white-space:nowrap;pointer-events:none;opacity:0;z-index:10;background:#333;border-radius:6px;padding:4px 10px;font-size:12px;transition:opacity .15s;position:absolute;bottom:calc(100% + 6px);left:50%;transform:translate(-50%)}.indicator-grid.compact .indicator-card:hover .card-tooltip[data-v-09f65e18]{opacity:1}.indicator-grid.compact .indicator-card .card-label[data-v-09f65e18]{font-size:12px;font-weight:500}.indicator-card[data-v-09f65e18]{cursor:pointer;text-align:left;background:#fff;border:1px solid #e8e8e8;border-radius:8px;flex-direction:column;gap:4px;padding:12px 14px;transition:all .15s;display:flex}.indicator-card[data-v-09f65e18]:hover:not(.disabled){background:#fafafa;border-color:#1a1a1a;transform:translateY(-1px);box-shadow:0 2px 8px #0000000f}.indicator-card.active[data-v-09f65e18]{background:#f8f8f8;border-color:#1a1a1a}.card-header[data-v-09f65e18]{justify-content:space-between;align-items:center;gap:8px;display:flex}.card-label[data-v-09f65e18]{color:#1a1a1a;font-size:13px;font-weight:600}.card-header-actions[data-v-09f65e18]{align-items:center;gap:4px;display:flex}.card-settings-btn[data-v-09f65e18]{color:#bbb;cursor:pointer;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;width:20px;height:20px;padding:0;transition:all .15s;display:flex}.card-settings-btn[data-v-09f65e18]:hover{color:#555;background:#f0f0f0}.card-name[data-v-09f65e18]{color:#666;font-size:11px;line-height:1.4}.card-params[data-v-09f65e18]{color:#999;margin-top:2px;font-size:10px}.section-divider[data-v-09f65e18]{background:linear-gradient(90deg,#0000,#e0e0e0,#0000);height:1px;margin:4px 0}.modal-footer[data-v-09f65e18]{background:#f8f8f8;border-top:1px solid #e8e8e8;flex-shrink:0;justify-content:space-between;align-items:center;padding:12px 20px;display:flex}.footer-info[data-v-09f65e18]{color:#666;font-size:12px}.info-text[data-v-09f65e18]{color:#999}.btn[data-v-09f65e18]{cursor:pointer;border:1px solid #0000;border-radius:7px;align-items:center;gap:5px;padding:6px 16px;font-size:13px;font-weight:500;line-height:1.4;transition:all .15s;display:flex}.btn-confirm[data-v-09f65e18]{color:#fff;background:#1a1a1a;border-color:#1a1a1a}.btn-confirm[data-v-09f65e18]:hover{background:#333;border-color:#333;transform:translateY(-1px);box-shadow:0 2px 10px #00000026}.fade-enter-active[data-v-09f65e18],.fade-leave-active[data-v-09f65e18]{transition:opacity .2s}.fade-enter-from[data-v-09f65e18],.fade-leave-to[data-v-09f65e18]{opacity:0}.overlay-enter-active[data-v-09f65e18],.overlay-leave-active[data-v-09f65e18]{transition:opacity .2s}.overlay-enter-from[data-v-09f65e18],.overlay-leave-to[data-v-09f65e18]{opacity:0}.modal-enter-active[data-v-09f65e18]{transition:all .22s cubic-bezier(.34,1.56,.64,1)}.modal-leave-active[data-v-09f65e18]{transition:all .16s ease-in}.modal-enter-from[data-v-09f65e18]{opacity:0;transform:scale(.88)translateY(-16px)}.modal-leave-to[data-v-09f65e18]{opacity:0;transform:scale(.94)translateY(8px)}@media (width<=640px){.selector-modal[data-v-09f65e18]{width:95vw;max-height:90vh}.indicator-grid[data-v-09f65e18]{grid-template-columns:1fr}.modal-body[data-v-09f65e18]{padding:16px}}.kline-tooltip[data-v-90b1aa6d]{z-index:10;color:#000000c7;pointer-events:none;backdrop-filter:blur(6px);background:#ffffffeb;border:1px solid #0000001f;border-radius:8px;min-width:200px;max-width:260px;padding:10px 12px;font-size:12px;line-height:1.4;position:absolute;box-shadow:0 6px 18px #0000001f}.kline-tooltip__title[data-v-90b1aa6d]{justify-content:space-between;gap:10px;margin-bottom:6px;font-weight:600;display:flex}.kline-tooltip__grid[data-v-90b1aa6d]{grid-template-columns:1fr;gap:2px;display:grid}.kline-tooltip__grid .row[data-v-90b1aa6d]{justify-content:space-between;gap:10px;display:flex}.kline-tooltip__grid .row span[data-v-90b1aa6d]:first-child{color:#0000008f}@supports (anchor-name:--kmap-anchor) and (position-anchor:--kmap-anchor){.kline-tooltip.use-anchor[data-v-90b1aa6d]{position-anchor:--kline-tooltip-anchor;left:anchor(left);top:anchor(top);position:absolute}.kline-tooltip.use-anchor.anchor-right-bottom[data-v-90b1aa6d]{transform:translate(14px,14px)}.kline-tooltip.use-anchor.anchor-left-bottom[data-v-90b1aa6d]{transform:translate(calc(-100% - 14px),14px)}}.marker-tooltip[data-v-057b55c9]{z-index:10;color:#000000c7;pointer-events:none;backdrop-filter:blur(6px);background:#ffffffeb;border:1px solid #0000001f;border-radius:8px;min-width:180px;max-width:260px;padding:10px 12px;font-size:12px;line-height:1.4;position:absolute;box-shadow:0 6px 18px #0000001f}.marker-tooltip__title[data-v-057b55c9]{justify-content:space-between;gap:10px;margin-bottom:6px;font-weight:600;display:flex}.marker-tooltip__content[data-v-057b55c9]{grid-template-columns:1fr;gap:2px;display:grid}.marker-tooltip__content .row[data-v-057b55c9]{justify-content:space-between;gap:10px;display:flex}.marker-tooltip__content .row span[data-v-057b55c9]:first-child{color:#0000008f}@supports (anchor-name:--kmap-anchor) and (position-anchor:--kmap-anchor){.marker-tooltip.use-anchor[data-v-057b55c9]{position-anchor:--marker-tooltip-anchor;left:anchor(left);top:anchor(top);position:absolute}.marker-tooltip.use-anchor.anchor-right-bottom[data-v-057b55c9]{transform:translate(12px,12px)}.marker-tooltip.use-anchor.anchor-left-bottom[data-v-057b55c9]{transform:translate(calc(-100% - 12px),12px)}}.left-toolbar[data-v-c0526cfb]{box-sizing:border-box;user-select:none;background:#fafbfc;border:1px solid #e5e7eb;border-radius:6px;flex-direction:column;flex:0 0 40px;align-items:center;gap:6px;padding:8px 5px;display:flex;box-shadow:0 1px 3px #0000000f}.left-toolbar__group[data-v-c0526cfb]{flex-direction:column;gap:4px;display:flex}.left-toolbar__divider[data-v-c0526cfb]{background:#e5e7eb;width:18px;height:1px}.left-toolbar__button[data-v-c0526cfb]{color:#6b7280;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:4px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;transition:border-color .15s,background .15s,color .15s;display:inline-flex;position:relative}.left-toolbar__button[data-v-c0526cfb]:hover{color:#374151;background:#f3f4f6;border-color:#d1d5db}.left-toolbar__button.active[data-v-c0526cfb]{color:#1f2937;background:#e5e7eb;border-color:#9ca3af}.left-toolbar__button[data-v-c0526cfb]:focus-visible{border-color:#6b7280;outline:none}.tool-icon[data-v-c0526cfb]{width:16px;height:16px}.corner-indicator[data-v-c0526cfb]{cursor:pointer;width:8px;height:8px;position:absolute;bottom:0;right:0;overflow:hidden}.corner-indicator[data-v-c0526cfb]:after{content:"";opacity:.45;border-bottom:5px solid;border-left:5px solid #0000;width:0;height:0;transition:opacity .15s;position:absolute;bottom:0;right:0}.left-toolbar__button:hover .corner-indicator[data-v-c0526cfb]:after,.left-toolbar__button.active .corner-indicator[data-v-c0526cfb]:after{opacity:.7}.corner-indicator.open[data-v-c0526cfb]:after{opacity:.8}.tool-dropdown[data-v-c0526cfb]{-webkit-backdrop-filter:blur(8px);box-sizing:border-box;z-index:100;background:#fafbfcd1;border:1px solid #e5e7eb;border-radius:6px;flex-direction:row;align-items:center;gap:4px;height:40px;padding:0 5px;display:flex;position:absolute;top:50%;left:calc(100% + 13px);transform:translateY(-50%);box-shadow:0 1px 3px #0000000f}.tool-item[data-v-c0526cfb]{position:relative}.dropdown-enter-active[data-v-c0526cfb],.dropdown-leave-active[data-v-c0526cfb]{transition:opacity .15s,transform .15s}.dropdown-enter-from[data-v-c0526cfb],.dropdown-leave-to[data-v-c0526cfb]{opacity:0;transform:translateY(-50%)translate(-6px)}@media (width<=768px),(height<=640px){.left-toolbar[data-v-c0526cfb]{border-radius:5px;flex-basis:36px;gap:5px;padding:6px 4px}.left-toolbar__group[data-v-c0526cfb]{gap:3px}.left-toolbar__button[data-v-c0526cfb]{border-radius:3px;width:26px;height:26px}.left-toolbar__divider[data-v-c0526cfb]{width:16px}.corner-indicator[data-v-c0526cfb]{width:7px;height:7px}.corner-indicator[data-v-c0526cfb]:after{border-bottom-width:4px;border-left-width:4px}.tool-dropdown[data-v-c0526cfb]{height:36px}}.settings-overlay[data-v-c0526cfb]{backdrop-filter:blur(4px);z-index:1000;background:#0000004d;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.settings-modal[data-v-c0526cfb]{background:#fff;border:1px solid #e0e0e0;border-radius:12px;width:90vw;min-width:340px;max-width:420px;overflow:hidden;box-shadow:0 8px 40px #00000026}.settings-header[data-v-c0526cfb]{background:#f8f8f8;border-bottom:1px solid #e8e8e8;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.header-left[data-v-c0526cfb]{align-items:baseline;gap:8px;display:flex}.header-right[data-v-c0526cfb]{align-items:center;gap:8px;display:flex}.settings-title[data-v-c0526cfb]{color:#1a1a1a;letter-spacing:.2px;font-size:14px;font-weight:600}.settings-subtitle[data-v-c0526cfb]{color:#999;font-size:11px}.settings-close[data-v-c0526cfb]{cursor:pointer;color:#888;background:#fff;border:1px solid #e0e0e0;border-radius:6px;justify-content:center;align-items:center;width:28px;height:28px;padding:0;transition:background .15s,color .15s,border-color .15s;display:flex}.settings-close[data-v-c0526cfb]:hover{color:#333;background:#f0f0f0;border-color:#ccc}.settings-close svg[data-v-c0526cfb]{width:14px;height:14px}.settings-body[data-v-c0526cfb]{flex-direction:column;gap:10px;padding:16px 20px;display:flex}.settings-item[data-v-c0526cfb]{background:#f8f8f8;border:1px solid #e8e8e8;border-radius:8px;padding:8px 12px}.settings-label[data-v-c0526cfb]{color:#333;cursor:pointer;justify-content:space-between;align-items:center;font-size:13px;display:flex}.settings-checkbox[data-v-c0526cfb]{cursor:pointer;accent-color:#1a1a1a;width:16px;height:16px}.settings-select[data-v-c0526cfb]{color:#333;cursor:pointer;background:#fff;border:1px solid #d0d0d0;border-radius:6px;outline:none;min-width:140px;padding:4px 8px;font-size:12px}.settings-select[data-v-c0526cfb]:hover{border-color:#9ca3af}.settings-select[data-v-c0526cfb]:focus{border-color:#6b7280;box-shadow:0 0 0 2px #6b728026}.settings-section-divider[data-v-c0526cfb]{align-items:center;gap:8px;margin-top:4px;display:flex}.settings-section-divider[data-v-c0526cfb]:before,.settings-section-divider[data-v-c0526cfb]:after{content:"";border-top:1px solid #e0e0e0;flex:1}.settings-section-label[data-v-c0526cfb]{color:#999;white-space:nowrap;font-size:11px}.settings-item.experimental[data-v-c0526cfb]{background:#fdf8f3;border-color:#f0e0d0}.settings-footer[data-v-c0526cfb]{background:#f8f8f8;border-top:1px solid #e8e8e8;justify-content:space-between;align-items:center;padding:12px 20px;display:flex}.footer-right[data-v-c0526cfb]{gap:8px;display:flex}.settings-btn[data-v-c0526cfb]{cursor:pointer;border:1px solid #0000;border-radius:7px;align-items:center;gap:5px;padding:6px 14px;font-size:13px;font-weight:500;line-height:1.4;transition:all .15s;display:flex}.settings-btn svg[data-v-c0526cfb]{flex-shrink:0;width:12px;height:12px}.settings-btn.reset[data-v-c0526cfb]{color:#666;background:0 0;border-color:#d0d0d0}.settings-btn.reset[data-v-c0526cfb]:hover{color:#e74c3c;background:#e74c3c14;border-color:#c0392b}.settings-btn.cancel[data-v-c0526cfb]{color:#666;background:0 0;border-color:#d0d0d0}.settings-btn.cancel[data-v-c0526cfb]:hover{color:#333;background:#f0f0f0;border-color:#bbb}.settings-btn.confirm[data-v-c0526cfb]{color:#fff;background:#1a1a1a;border-color:#1a1a1a}.settings-btn.confirm[data-v-c0526cfb]:hover{background:#333;border-color:#333;transform:translateY(-1px);box-shadow:0 2px 10px #00000026}.settings-btn.confirm[data-v-c0526cfb]:active{box-shadow:none;transform:translateY(0)}.chart-wrapper[data-v-28c25ee4]{--kmap-height:var(--kmap-chart-height,100%);--kmap-width:var(--kmap-chart-width,100%);--chart-bg:#fff;--chart-bg-secondary:#f8f9fa;--chart-border:#e5e7eb;--chart-border-active:#3b82f6;--chart-text:#374151;--chart-text-secondary:#6b7280;width:var(--kmap-width);height:var(--kmap-height);flex-direction:column;justify-content:center;align-items:center;min-height:300px;display:flex}.chart-wrapper[data-theme=dark][data-v-28c25ee4]{--chart-bg:#1a1a2e;--chart-bg-secondary:#16162a;--chart-border:#2d2d44;--chart-border-active:#60a5fa;--chart-text:#e5e7eb;--chart-text-secondary:#9ca3af}.chart-stage[data-v-28c25ee4]{align-items:stretch;gap:8px;width:95%;height:85%;min-height:255px;display:flex}.chart-main[data-v-28c25ee4]{flex:auto;align-items:stretch;gap:0;min-width:0;height:100%;display:flex;position:relative}.pane-separator-layer[data-v-28c25ee4]{pointer-events:none;z-index:20;position:absolute;inset:0}.pane-separator-line[data-v-28c25ee4]{border-top:1px solid var(--chart-border);opacity:1;box-sizing:border-box;height:0;transition:border-top-color .12s,border-top-width .12s,margin-top .12s,opacity .12s;position:absolute;left:0;right:0}.pane-separator-line.is-active[data-v-28c25ee4]{border-top-color:var(--chart-border-active);border-top-width:2px;margin-top:-1px}.chart-stage.is-resizing-pane[data-v-28c25ee4],.chart-stage.is-hovering-pane-separator[data-v-28c25ee4]{cursor:ns-resize}.chart-stage.is-hovering-kline[data-v-28c25ee4]{cursor:pointer}.chart-stage.is-hovering-right-axis[data-v-28c25ee4]{cursor:ns-resize}.chart-stage.is-dragging[data-v-28c25ee4]{cursor:grabbing}.chart-container[data-v-28c25ee4]{height:100%;min-height:inherit;scrollbar-width:none;-ms-overflow-style:none;border:1px solid var(--chart-border);box-sizing:border-box;background:var(--chart-bg);-webkit-touch-callout:none;user-select:none;touch-action:none;border-right:0;border-radius:6px 0 0 6px;flex:auto;position:relative;overflow:auto hidden}.chart-container[data-v-28c25ee4]::-webkit-scrollbar{display:none}.right-axis-host[data-v-28c25ee4]{height:100%;min-height:inherit;box-sizing:border-box;background:var(--chart-bg);border:1px solid var(--chart-border);-webkit-touch-callout:none;user-select:none;touch-action:none;border-top-right-radius:6px;border-bottom-right-radius:6px;flex:none;position:relative;overflow:visible}.scroll-content[data-v-28c25ee4]{height:100%;min-height:inherit;position:relative}.canvas-layer[data-v-28c25ee4]{pointer-events:none;position:sticky;top:0;left:0}.tooltip-layer[data-v-28c25ee4]{pointer-events:none;z-index:30;position:absolute;inset:0}.tooltip-anchor[data-v-28c25ee4]{pointer-events:none;width:1px;height:1px;position:absolute}.tooltip-anchor.kline-tooltip-anchor.use-anchor[data-v-28c25ee4]{anchor-name:--kline-tooltip-anchor}.tooltip-anchor.marker-tooltip-anchor.use-anchor[data-v-28c25ee4]{anchor-name:--marker-tooltip-anchor}@media (width<=768px),(height<=640px){.chart-stage[data-v-28c25ee4]{gap:6px}}.plot-canvas{display:block;position:absolute;top:0;left:0}.right-axis{display:block;position:absolute;left:0}.x-axis-canvas{z-index:10;display:block;position:absolute;bottom:0;left:0}.right-axis{z-index:15}
2
2
  /*$vite$:1*/
@@ -0,0 +1,3 @@
1
+ export declare const VERSION: string;
2
+ export { VERSION as CORE_VERSION } from '../../core/src';
3
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,OAAO,QAAc,CAAA;AAClC,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,+BAA+B,CAAA"}
package/package.json CHANGED
@@ -1,112 +1,75 @@
1
- {
2
- "name": "@363045841yyt/klinechart",
3
- "version": "0.7.1",
4
- "description": "A lightweight financial K-line charting library with first-class AI Agent support, crisp ResizeObserver-driven rendering, and plugin-based architecture. Focused on quantitative trading scenarios with TradingView-level interaction experience.",
5
- "author": "363045841 <slslswbsy@qq.com>",
6
- "license": "MIT",
7
- "repository": {
8
- "type": "git",
9
- "url": "https://github.com/363045841/KLineChartQuant.git"
10
- },
11
- "bugs": "https://github.com/363045841/KLineChartQuant/issues",
12
- "homepage": "https://363045841.github.io/KLineChartQuant/",
13
- "keywords": [
14
- "kline-chart",
15
- "plugin-architecture",
16
- "webgl",
17
- "high-performance",
18
- "ai",
19
- "crisp-rendering",
20
- "sharp-rendering",
21
- "agent",
22
- "ai-chart",
23
- "modern-ui",
24
- "device-pixel-ratio",
25
- "candlestick-chart",
26
- "financial-chart",
27
- "tradingview",
28
- "canvas",
29
- "rendering-engine",
30
- "resizeobserver",
31
- "device-pixel-content-box",
32
- "drawing-tools",
33
- "chart-drawing",
34
- "trend-line",
35
- "fibonacci",
36
- "pixel-perfect",
37
- "responsive",
38
- "visualization",
39
- "quantitative-trading",
40
- "technical-analysis",
41
- "typescript"
42
- ],
43
- "type": "module",
44
- "engines": {
45
- "node": "^20.19.0 || >=22.12.0"
46
- },
47
- "scripts": {
48
- "dev": "vite",
49
- "dev:lan": "vite --host 0.0.0.0",
50
- "stockbao": "cd .. && cd stockbao && uv run python ./server.py",
51
- "aktools": "cd .. && cd aktoolshttp && uv run python -m aktools",
52
- "build": "run-p type-check \"build-only {@}\" --",
53
- "build-only": "vite build",
54
- "build:demo": "vite build --config vite.demo.config.ts",
55
- "preview": "vite preview",
56
- "preview:demo": "vite preview --config vite.demo.config.ts",
57
- "type-check": "vue-tsc --build",
58
- "test:unit": "vitest",
59
- "test:packages": "pnpm -r test",
60
- "size:packages": "pnpm -r --workspace-concurrency=4 size",
61
- "lint:publish": "pnpm -r --workspace-concurrency=4 lint:publish",
62
- "lint:types": "pnpm -r --workspace-concurrency=4 lint:types",
63
- "format": "prettier --write --experimental-cli src/",
64
- "prepublishOnly": "pnpm run build-only"
65
- },
66
- "main": "./dist/index.cjs",
67
- "module": "./dist/index.js",
68
- "types": "./dist/index.d.ts",
69
- "exports": {
70
- ".": {
71
- "types": "./dist/index.d.ts",
72
- "import": "./dist/index.js",
73
- "require": "./dist/index.cjs"
74
- },
75
- "./style.css": "./dist/klinechart.css"
76
- },
77
- "files": [
78
- "dist"
79
- ],
80
- "peerDependencies": {
81
- "vue": "^3.5.0"
82
- },
83
- "dependencies": {
84
- "ajv": "^8.20.0"
85
- },
86
- "devDependencies": {
87
- "@babel/core": "^7.29.0",
88
- "@babel/plugin-proposal-decorators": "^7.29.0",
89
- "@babel/plugin-transform-typescript": "^7.29.7",
90
- "@iconify-json/tabler": "^1.2.34",
91
- "@tsconfig/node24": "^24.0.4",
92
- "@types/jsdom": "^28.0.1",
93
- "@types/node": "^25.9.1",
94
- "@vitejs/plugin-vue": "^6.0.6",
95
- "@vitest/coverage-v8": "^4.1.5",
96
- "@vue/test-utils": "^2.4.10",
97
- "@vue/tsconfig": "^0.9.1",
98
- "jsdom": "^29.1.1",
99
- "npm-run-all2": "^8.0.4",
100
- "prettier": "3.8.3",
101
- "typedoc": "^0.28.19",
102
- "typescript": "~6.0.3",
103
- "unplugin-icons": "^23.0.1",
104
- "unplugin-vue-components": "^32.1.0",
105
- "vite": "^8.0.10",
106
- "vite-plugin-babel": "^1.3.2",
107
- "vite-plugin-dts": "^4.5.4",
108
- "vite-plugin-vue-devtools": "^8.1.1",
109
- "vitest": "^4.1.5",
110
- "vue-tsc": "^3.3.2"
111
- }
112
- }
1
+ {
2
+ "name": "@363045841yyt/klinechart",
3
+ "version": "0.7.3-alpha.0",
4
+ "description": "Vue 3 bindings for @363045841yyt/klinechart-core. Idiomatic composables, SFC components.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "engines": {
8
+ "node": "^20.19.0 || >=22.12.0"
9
+ },
10
+ "sideEffects": false,
11
+ "main": "./dist/index.js",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "require": {
21
+ "types": "./dist/index.d.cts",
22
+ "default": "./dist/index.cjs"
23
+ }
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "src"
29
+ ],
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "scripts": {
34
+ "dev": "vite --config preview/vite.config.ts",
35
+ "build": "vite build && node -e \"require('fs').copyFileSync('dist/index.d.ts','dist/index.d.cts')\"",
36
+ "postbuild": "node -e \"require('fs').copyFileSync('dist/index.d.ts','dist/index.d.cts')\"",
37
+ "test": "vitest run",
38
+ "test:watch": "vitest",
39
+ "size": "size-limit",
40
+ "lint:publish": "publint --strict .",
41
+ "lint:types": "attw --pack ."
42
+ },
43
+ "size-limit": [
44
+ {
45
+ "name": "vue (entry, pre-build src measurement)",
46
+ "path": "src/index.ts",
47
+ "limit": "8 KB",
48
+ "gzip": true,
49
+ "ignore": [
50
+ "vue",
51
+ "@363045841yyt/klinechart-core"
52
+ ]
53
+ }
54
+ ],
55
+ "peerDependencies": {
56
+ "@363045841yyt/klinechart-core": "workspace:^",
57
+ "vue": "^3.4.0"
58
+ },
59
+ "devDependencies": {
60
+ "@arethetypeswrong/cli": "^0.18.3",
61
+ "@size-limit/preset-small-lib": "^12.1.0",
62
+ "@vitejs/plugin-vue": "^6.0.0",
63
+ "@vue/test-utils": "^2.4.10",
64
+ "jsdom": "^29.1.1",
65
+ "publint": "^0.3.0",
66
+ "size-limit": "^12.1.0",
67
+ "typescript": "~6.0.3",
68
+ "unplugin-icons": "^23.0.1",
69
+ "vite": "^8.0.14",
70
+ "vite-plugin-babel": "^1.3.2",
71
+ "vite-plugin-dts": "^5.0.1",
72
+ "vitest": "^4.1.5",
73
+ "vue": "^3.5.0"
74
+ }
75
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Mock ChartController for Vue adapter tests.
3
+ *
4
+ * Mirrors the framework-agnostic signal-bearing shape from
5
+ * @363045841yyt/klinechart-core without spinning up the real Chart engine.
6
+ *
7
+ * To keep this test file runnable from the repo root vitest (which does not
8
+ * alias @363045841yyt/klinechart-core), we inline a tiny `Signal` implementation
9
+ * that is shape-compatible with `packages/core/src/reactivity/signal.ts`.
10
+ */
11
+
12
+ import type { Signal } from '@363045841yyt/klinechart-core/reactivity'
13
+ import type {
14
+ ActiveIndicator,
15
+ ChartController,
16
+ ChartMountOptions,
17
+ ChartViewport,
18
+ DrawingController,
19
+ DrawingState,
20
+ IndicatorDefinition,
21
+ IndicatorSelectorController,
22
+ KLineData,
23
+ ToolbarController,
24
+ ToolDefinition,
25
+ ToolId,
26
+ } from '@363045841yyt/klinechart-core'
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Inline mini-signal �?Object.is-equality, sync notify. Drop-in compatible
30
+ // with `@363045841yyt/klinechart-core/reactivity` for shape-only test purposes.
31
+ // ---------------------------------------------------------------------------
32
+
33
+ function createSignal<T>(initial: T): Signal<T> {
34
+ let value = initial
35
+ const subs = new Set<() => void>()
36
+ const read = (): T => value
37
+ const peek = (): T => value
38
+ const set = (next: T): void => {
39
+ if (Object.is(value, next)) return
40
+ value = next
41
+ for (const listener of [...subs]) listener()
42
+ }
43
+ const subscribe = (listener: () => void): (() => void) => {
44
+ subs.add(listener)
45
+ return () => {
46
+ subs.delete(listener)
47
+ }
48
+ }
49
+ return Object.assign(read, { peek, set, subscribe }) as Signal<T>
50
+ }
51
+
52
+ export interface MockChartController extends ChartController {
53
+ /** spy: how many times `dispose` was called */
54
+ disposeCalls: () => number
55
+ /** test-only signal mutators */
56
+ _setViewport: (vp: ChartViewport) => void
57
+ _setData: (data: ReadonlyArray<KLineData>) => void
58
+ }
59
+
60
+ function createMockIndicatorSelector(): IndicatorSelectorController {
61
+ const catalog = createSignal<ReadonlyArray<IndicatorDefinition>>([])
62
+ const active = createSignal<ReadonlyArray<ActiveIndicator>>([])
63
+ const menuOpen = createSignal<boolean>(false)
64
+ const searchQuery = createSignal<string>('')
65
+ const filteredMain = createSignal<ReadonlyArray<IndicatorDefinition>>([])
66
+ const filteredSub = createSignal<ReadonlyArray<IndicatorDefinition>>([])
67
+
68
+ return {
69
+ catalog,
70
+ active,
71
+ menuOpen,
72
+ searchQuery,
73
+ filteredMain,
74
+ filteredSub,
75
+ add: () => null,
76
+ remove: () => false,
77
+ updateParams: () => false,
78
+ reorder: () => false,
79
+ openMenu: () => menuOpen.set(true),
80
+ closeMenu: () => menuOpen.set(false),
81
+ toggleMenu: () => menuOpen.set(!menuOpen.peek()),
82
+ setSearchQuery: (q: string) => searchQuery.set(q),
83
+ isActive: () => false,
84
+ dispose: () => {},
85
+ }
86
+ }
87
+
88
+ function createMockToolbar(): ToolbarController {
89
+ const tools = createSignal<ReadonlyArray<ToolDefinition>>([])
90
+ const activeTool = createSignal<ToolId | null>(null)
91
+ const disabledTools = createSignal<ReadonlySet<ToolId>>(new Set())
92
+ return {
93
+ tools,
94
+ activeTool,
95
+ disabledTools,
96
+ selectTool: (id) => activeTool.set(id),
97
+ clearSelection: () => activeTool.set(null),
98
+ setDisabled: () => {},
99
+ dispose: () => {},
100
+ }
101
+ }
102
+
103
+ function createMockDrawing(): DrawingController {
104
+ const state = createSignal<DrawingState>({
105
+ activeTool: null,
106
+ drawingCount: 0,
107
+ })
108
+ return {
109
+ state,
110
+ setActiveTool: (tool) =>
111
+ state.set({ ...state.peek(), activeTool: tool }),
112
+ clearAll: () => state.set({ ...state.peek(), drawingCount: 0 }),
113
+ deleteLast: () =>
114
+ state.set({
115
+ ...state.peek(),
116
+ drawingCount: Math.max(0, state.peek().drawingCount - 1),
117
+ }),
118
+ dispose: () => {},
119
+ }
120
+ }
121
+
122
+ export function createMockChartController(
123
+ opts: Partial<ChartMountOptions> = {},
124
+ ): MockChartController {
125
+ let disposeCalls = 0
126
+
127
+ const viewport = createSignal<ChartViewport>({
128
+ zoomLevel: opts.initialZoomLevel ?? 3,
129
+ kWidth: 6,
130
+ visibleFrom: 0,
131
+ visibleTo: 0,
132
+ })
133
+ const data = createSignal<ReadonlyArray<KLineData>>(opts.data ?? [])
134
+ const theme = createSignal<'light' | 'dark'>(opts.theme ?? 'light')
135
+ const indicatorSelector = createMockIndicatorSelector()
136
+ const toolbar = createMockToolbar()
137
+ const drawing = createMockDrawing()
138
+
139
+ return {
140
+ viewport,
141
+ data,
142
+ theme,
143
+ indicatorSelector,
144
+ toolbar,
145
+ drawing,
146
+ setData: (next) => data.set(next),
147
+ appendData: (next) => data.set([...data.peek(), ...next]),
148
+ updateData: (next) => data.set(next),
149
+ setTheme: (next) => theme.set(next),
150
+ zoomToLevel: (level) =>
151
+ viewport.set({ ...viewport.peek(), zoomLevel: level }),
152
+ zoomIn: () =>
153
+ viewport.set({
154
+ ...viewport.peek(),
155
+ zoomLevel: viewport.peek().zoomLevel + 1,
156
+ }),
157
+ zoomOut: () =>
158
+ viewport.set({
159
+ ...viewport.peek(),
160
+ zoomLevel: viewport.peek().zoomLevel - 1,
161
+ }),
162
+ handlePointerEvent: () => false,
163
+ handleWheelEvent: () => {},
164
+ handleScrollEvent: () => {},
165
+ handlePinchZoom: () => {},
166
+ addIndicator: () => null,
167
+ removeIndicator: () => false,
168
+ updateIndicatorParams: () => false,
169
+ updateRendererConfig: () => {},
170
+ setDrawingTool: (tool) => drawing.setActiveTool(tool),
171
+ clearDrawings: () => drawing.clearAll(),
172
+ removeDrawing: () => {},
173
+ resizeSubPane: () => false,
174
+ createSubPane: () => false,
175
+ clearSubPanes: () => {},
176
+ updateCustomMarkers: () => {},
177
+ clearCustomMarkers: () => {},
178
+ updateSettingsFacade: () => {},
179
+ updateOptionsFacade: () => {},
180
+ dispose: () => {
181
+ disposeCalls += 1
182
+ },
183
+ disposeCalls: () => disposeCalls,
184
+ _setViewport: (vp) => viewport.set(vp),
185
+ _setData: (next) => data.set(next),
186
+ }
187
+ }
188
+
189
+ /** Signal helper used by reactivity bridge tests. */
190
+ export function createTestSignal<T>(initial: T): Signal<T> {
191
+ return createSignal(initial)
192
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Contract test for @363045841yyt/klinechart.
3
+ *
4
+ * Phase 1D agent's brief: make these pass without weakening assertions,
5
+ * preserving the legacy KMapPlugin.install signature.
6
+ */
7
+
8
+ import { describe, it, expect, vi, afterEach } from 'vitest'
9
+ import { defineComponent, h, nextTick, ref, shallowRef } from 'vue'
10
+ import { mount } from '@vue/test-utils'
11
+ import * as VueAdapter from '../index'
12
+ import { coreSignalToVueRef } from '../index'
13
+ import { createMockChartController, createTestSignal } from './_mockController'
14
+
15
+ describe('@363045841yyt/klinechart �?public API surface', () => {
16
+ it('exports createChart, useChart, useIndicatorSelector, KLineChart, KMapPlugin', () => {
17
+ expect(typeof VueAdapter.createChart).toBe('function')
18
+ expect(typeof VueAdapter.useChart).toBe('function')
19
+ expect(typeof VueAdapter.useIndicatorSelector).toBe('function')
20
+ expect(VueAdapter.KLineChart).toBeDefined()
21
+ expect(typeof VueAdapter.KMapPlugin.install).toBe('function')
22
+ })
23
+
24
+ it('KMapPlugin.install is callable with a mock app and registers KLineChart', () => {
25
+ const registered: Record<string, unknown> = {}
26
+ const mockApp = {
27
+ component(name: string, comp: unknown) {
28
+ registered[name] = comp
29
+ },
30
+ } as unknown as Parameters<typeof VueAdapter.KMapPlugin.install>[0]
31
+ VueAdapter.KMapPlugin.install(mockApp)
32
+ expect(registered.KLineChart).toBe(VueAdapter.KLineChart)
33
+ })
34
+ })
35
+
36
+ describe('@363045841yyt/klinechart �?SSR safety', () => {
37
+ it('module import does not touch window or document', () => {
38
+ // Import above ran in node env without jsdom. If it touched window, this
39
+ // file would not have loaded. Test documents the contract.
40
+ expect(true).toBe(true)
41
+ })
42
+ })
43
+
44
+ describe('@363045841yyt/klinechart �?useChart lifecycle', () => {
45
+ afterEach(() => {
46
+ // Reset the injected factory so other tests start clean.
47
+ VueAdapter.__setControllerFactory(null)
48
+ })
49
+
50
+ it('mounts on first render via template ref', async () => {
51
+ const mockController = createMockChartController({ data: [] })
52
+ const factorySpy = vi.fn(() => mockController)
53
+ VueAdapter.__setControllerFactory(factorySpy)
54
+
55
+ const HostComponent = defineComponent({
56
+ name: 'Host',
57
+ setup() {
58
+ const containerRef = ref<HTMLElement | null>(null)
59
+ const { chart } = VueAdapter.useChart(containerRef, { data: [] })
60
+ return { containerRef, chart }
61
+ },
62
+ render() {
63
+ return h('div', { ref: 'containerRef' })
64
+ },
65
+ })
66
+
67
+ const wrapper = mount(HostComponent, { attachTo: document.body })
68
+ await nextTick()
69
+
70
+ expect(factorySpy).toHaveBeenCalledTimes(1)
71
+ const factoryArg = factorySpy.mock.calls[0]?.[0]
72
+ expect(factoryArg?.container).toBeInstanceOf(HTMLElement)
73
+ expect(wrapper.vm.chart).toBe(mockController)
74
+
75
+ wrapper.unmount()
76
+ })
77
+
78
+ it('disposes on unmount', async () => {
79
+ const mockController = createMockChartController({ data: [] })
80
+ VueAdapter.__setControllerFactory(() => mockController)
81
+
82
+ const HostComponent = defineComponent({
83
+ name: 'Host',
84
+ setup() {
85
+ const containerRef = ref<HTMLElement | null>(null)
86
+ const { chart } = VueAdapter.useChart(containerRef, { data: [] })
87
+ return { containerRef, chart }
88
+ },
89
+ render() {
90
+ return h('div', { ref: 'containerRef' })
91
+ },
92
+ })
93
+
94
+ const wrapper = mount(HostComponent, { attachTo: document.body })
95
+ await nextTick()
96
+
97
+ expect(mockController.disposeCalls()).toBe(0)
98
+ wrapper.unmount()
99
+ // Allow lifecycle hooks to settle.
100
+ await nextTick()
101
+ expect(mockController.disposeCalls()).toBe(1)
102
+ })
103
+
104
+ it('reactivity bridge: signal change updates returned ref', async () => {
105
+ // Mount a tiny scoped component so coreSignalToVueRef can register
106
+ // its onScopeDispose cleanup. Without a setup scope the ref is still
107
+ // wired up correctly, but cleanup would not be automatic.
108
+ const signal = createTestSignal<number>(1)
109
+ const bridgedRef = shallowRef<{ value: number } | null>(null)
110
+
111
+ const HostComponent = defineComponent({
112
+ name: 'BridgeHost',
113
+ setup() {
114
+ const r = coreSignalToVueRef(signal)
115
+ bridgedRef.value = r as unknown as { value: number }
116
+ return () => h('div', String(r.value))
117
+ },
118
+ })
119
+
120
+ const wrapper = mount(HostComponent, { attachTo: document.body })
121
+ expect(bridgedRef.value?.value).toBe(1)
122
+ expect(wrapper.text()).toBe('1')
123
+
124
+ signal.set(42)
125
+ await nextTick()
126
+
127
+ expect(bridgedRef.value?.value).toBe(42)
128
+ expect(wrapper.text()).toBe('42')
129
+
130
+ wrapper.unmount()
131
+ })
132
+ })