@363045841yyt/klinechart 0.7.0 → 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.
- package/README.md +10 -6
- package/dist/components/DrawingStyleToolbar.vue.d.ts +28 -0
- package/dist/components/DrawingStyleToolbar.vue.d.ts.map +1 -0
- package/dist/{src/components → components}/IndicatorParams.vue.d.ts +3 -3
- package/dist/components/IndicatorParams.vue.d.ts.map +1 -0
- package/dist/{src/components → components}/IndicatorSelector.vue.d.ts +4 -4
- package/dist/components/IndicatorSelector.vue.d.ts.map +1 -0
- package/dist/components/KLineChart.vue.d.ts +63 -0
- package/dist/components/KLineChart.vue.d.ts.map +1 -0
- package/dist/components/KLineTooltip.vue.d.ts +30 -0
- package/dist/components/KLineTooltip.vue.d.ts.map +1 -0
- package/dist/{src/components → components}/LeftToolbar.vue.d.ts +3 -2
- package/dist/components/LeftToolbar.vue.d.ts.map +1 -0
- package/dist/components/MarkerTooltip.vue.d.ts +26 -0
- package/dist/components/MarkerTooltip.vue.d.ts.map +1 -0
- package/dist/components/index.d.ts +8 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/{src/composables → composables}/useFullscreenTeleportTarget.d.ts +1 -0
- package/dist/composables/useFullscreenTeleportTarget.d.ts.map +1 -0
- package/dist/{src/debug → debug}/canvasProfiler.d.ts +1 -0
- package/dist/debug/canvasProfiler.d.ts.map +1 -0
- package/dist/index.cjs +2 -49
- package/dist/index.d.cts +124 -0
- package/dist/index.d.ts +124 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1470 -16916
- package/dist/klinechart.css +1 -1
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +52 -81
- package/src/__tests__/_mockController.ts +192 -0
- package/src/__tests__/contract.test.ts +132 -0
- package/src/components/DrawingStyleToolbar.vue +199 -0
- package/src/components/IndicatorParams.vue +570 -0
- package/src/components/IndicatorSelector.vue +1042 -0
- package/src/components/KLineChart.vue +1570 -0
- package/src/components/KLineTooltip.vue +200 -0
- package/src/components/LeftToolbar.vue +844 -0
- package/src/components/MarkerTooltip.vue +155 -0
- package/src/components/index.ts +7 -0
- package/src/composables/useFullscreenTeleportTarget.ts +18 -0
- package/src/debug/canvasProfiler.ts +296 -0
- package/src/index.ts +402 -0
- package/src/version.ts +3 -0
- package/LICENSE +0 -21
- package/dist/favicon.ico +0 -0
- package/dist/mock-stock-data.json +0 -1
- package/dist/schema-CzmPW09E.cjs +0 -1
- package/dist/schema-DBMGp6Af.js +0 -437
- package/dist/src/App.vue.d.ts +0 -4
- package/dist/src/api/data/baostock.d.ts +0 -90
- package/dist/src/api/data/baostock.integration.test.d.ts +0 -1
- package/dist/src/api/data/index.d.ts +0 -26
- package/dist/src/api/data/kLine.d.ts +0 -11
- package/dist/src/api/data/types.d.ts +0 -33
- package/dist/src/api/data/unified.d.ts +0 -37
- package/dist/src/components/DrawingStyleToolbar.vue.d.ts +0 -12
- package/dist/src/components/KLineChart.vue.d.ts +0 -100
- package/dist/src/components/KLineTooltip.vue.d.ts +0 -17
- package/dist/src/components/MarkerTooltip.vue.d.ts +0 -13
- package/dist/src/components/index.d.ts +0 -2
- package/dist/src/config/chartSettings.d.ts +0 -69
- package/dist/src/core/chart-store.d.ts +0 -74
- package/dist/src/core/chart.d.ts +0 -420
- package/dist/src/core/controller/interaction.d.ts +0 -167
- package/dist/src/core/controller/markerInteraction.d.ts +0 -28
- package/dist/src/core/controller/pinchTracker.d.ts +0 -18
- package/dist/src/core/controller/tooltipPosition.d.ts +0 -21
- package/dist/src/core/draw/pixelAlign.d.ts +0 -114
- package/dist/src/core/drawing/index.d.ts +0 -47
- package/dist/src/core/drawing/interaction.d.ts +0 -75
- package/dist/src/core/drawing/plugin.d.ts +0 -27
- package/dist/src/core/indicators/atrState.d.ts +0 -16
- package/dist/src/core/indicators/bollState.d.ts +0 -34
- package/dist/src/core/indicators/calculators.d.ts +0 -465
- package/dist/src/core/indicators/cciState.d.ts +0 -15
- package/dist/src/core/indicators/chaikinVolState.d.ts +0 -18
- package/dist/src/core/indicators/cmfState.d.ts +0 -16
- package/dist/src/core/indicators/demaState.d.ts +0 -16
- package/dist/src/core/indicators/donchianState.d.ts +0 -23
- package/dist/src/core/indicators/eneState.d.ts +0 -30
- package/dist/src/core/indicators/expmaState.d.ts +0 -30
- package/dist/src/core/indicators/fastkState.d.ts +0 -15
- package/dist/src/core/indicators/fibState.d.ts +0 -26
- package/dist/src/core/indicators/hmaState.d.ts +0 -16
- package/dist/src/core/indicators/hvState.d.ts +0 -18
- package/dist/src/core/indicators/ichimokuState.d.ts +0 -44
- package/dist/src/core/indicators/indicator.worker.d.ts +0 -5
- package/dist/src/core/indicators/indicatorRuntime.d.ts +0 -126
- package/dist/src/core/indicators/kamaState.d.ts +0 -20
- package/dist/src/core/indicators/keltnerState.d.ts +0 -27
- package/dist/src/core/indicators/kstState.d.ts +0 -21
- package/dist/src/core/indicators/maState.d.ts +0 -26
- package/dist/src/core/indicators/macdState.d.ts +0 -58
- package/dist/src/core/indicators/mfiState.d.ts +0 -16
- package/dist/src/core/indicators/momState.d.ts +0 -15
- package/dist/src/core/indicators/obvState.d.ts +0 -14
- package/dist/src/core/indicators/parkinsonState.d.ts +0 -18
- package/dist/src/core/indicators/pivotState.d.ts +0 -29
- package/dist/src/core/indicators/pvtState.d.ts +0 -14
- package/dist/src/core/indicators/rocState.d.ts +0 -16
- package/dist/src/core/indicators/rsiState.d.ts +0 -39
- package/dist/src/core/indicators/sarState.d.ts +0 -26
- package/dist/src/core/indicators/scheduler.d.ts +0 -237
- package/dist/src/core/indicators/soa.d.ts +0 -115
- package/dist/src/core/indicators/stateComposer.d.ts +0 -146
- package/dist/src/core/indicators/stochState.d.ts +0 -18
- package/dist/src/core/indicators/structureState.d.ts +0 -43
- package/dist/src/core/indicators/supertrendState.d.ts +0 -22
- package/dist/src/core/indicators/temaState.d.ts +0 -16
- package/dist/src/core/indicators/trixState.d.ts +0 -20
- package/dist/src/core/indicators/vmaState.d.ts +0 -16
- package/dist/src/core/indicators/volumeProfileState.d.ts +0 -34
- package/dist/src/core/indicators/vwapState.d.ts +0 -16
- package/dist/src/core/indicators/wmaState.d.ts +0 -16
- package/dist/src/core/indicators/wmsrState.d.ts +0 -15
- package/dist/src/core/indicators/workerProtocol.d.ts +0 -496
- package/dist/src/core/indicators/zonesState.d.ts +0 -26
- package/dist/src/core/layout/pane.d.ts +0 -103
- package/dist/src/core/marker/registry.d.ts +0 -174
- package/dist/src/core/paneRenderer.d.ts +0 -45
- package/dist/src/core/renderers/Indicator/atr.d.ts +0 -17
- package/dist/src/core/renderers/Indicator/boll.d.ts +0 -2
- package/dist/src/core/renderers/Indicator/cci.d.ts +0 -21
- package/dist/src/core/renderers/Indicator/chaikinVol.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/cmf.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/dema.d.ts +0 -5
- package/dist/src/core/renderers/Indicator/donchian.d.ts +0 -5
- package/dist/src/core/renderers/Indicator/ene.d.ts +0 -10
- package/dist/src/core/renderers/Indicator/expma.d.ts +0 -2
- package/dist/src/core/renderers/Indicator/fastk.d.ts +0 -21
- package/dist/src/core/renderers/Indicator/fib.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/hma.d.ts +0 -5
- package/dist/src/core/renderers/Indicator/hv.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/ichimoku.d.ts +0 -5
- package/dist/src/core/renderers/Indicator/index.d.ts +0 -59
- package/dist/src/core/renderers/Indicator/indicatorData.d.ts +0 -13
- package/dist/src/core/renderers/Indicator/kama.d.ts +0 -5
- package/dist/src/core/renderers/Indicator/keltner.d.ts +0 -5
- package/dist/src/core/renderers/Indicator/kst.d.ts +0 -21
- package/dist/src/core/renderers/Indicator/ma.d.ts +0 -3
- package/dist/src/core/renderers/Indicator/macd.d.ts +0 -49
- package/dist/src/core/renderers/Indicator/macdLegend.d.ts +0 -12
- package/dist/src/core/renderers/Indicator/mainIndicatorLegend.d.ts +0 -10
- package/dist/src/core/renderers/Indicator/mfi.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/mom.d.ts +0 -21
- package/dist/src/core/renderers/Indicator/obv.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/parkinson.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/pivot.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/pvt.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/roc.d.ts +0 -5
- package/dist/src/core/renderers/Indicator/rsi.d.ts +0 -32
- package/dist/src/core/renderers/Indicator/sar.d.ts +0 -5
- package/dist/src/core/renderers/Indicator/scale/atr_scale.d.ts +0 -11
- package/dist/src/core/renderers/Indicator/scale/cci_scale.d.ts +0 -11
- package/dist/src/core/renderers/Indicator/scale/fastk_scale.d.ts +0 -11
- package/dist/src/core/renderers/Indicator/scale/indicator_scale.d.ts +0 -35
- package/dist/src/core/renderers/Indicator/scale/kst_scale.d.ts +0 -11
- package/dist/src/core/renderers/Indicator/scale/macd_scale.d.ts +0 -14
- package/dist/src/core/renderers/Indicator/scale/mom_scale.d.ts +0 -11
- package/dist/src/core/renderers/Indicator/scale/rsi_scale.d.ts +0 -11
- package/dist/src/core/renderers/Indicator/scale/stoch_scale.d.ts +0 -11
- package/dist/src/core/renderers/Indicator/scale/volume_scale.d.ts +0 -14
- package/dist/src/core/renderers/Indicator/scale/wmsr_scale.d.ts +0 -11
- package/dist/src/core/renderers/Indicator/stoch.d.ts +0 -21
- package/dist/src/core/renderers/Indicator/structure.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/subPaneConfig.d.ts +0 -9
- package/dist/src/core/renderers/Indicator/supertrend.d.ts +0 -5
- package/dist/src/core/renderers/Indicator/tema.d.ts +0 -5
- package/dist/src/core/renderers/Indicator/trix.d.ts +0 -5
- package/dist/src/core/renderers/Indicator/vma.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/volumeProfile.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/vwap.d.ts +0 -4
- package/dist/src/core/renderers/Indicator/wma.d.ts +0 -5
- package/dist/src/core/renderers/Indicator/wmsr.d.ts +0 -21
- package/dist/src/core/renderers/Indicator/zones.d.ts +0 -4
- package/dist/src/core/renderers/candle.d.ts +0 -20
- package/dist/src/core/renderers/crosshair.d.ts +0 -17
- package/dist/src/core/renderers/customMarkers.d.ts +0 -6
- package/dist/src/core/renderers/extremaMarkers.d.ts +0 -5
- package/dist/src/core/renderers/gridLines.d.ts +0 -7
- package/dist/src/core/renderers/lastPrice.d.ts +0 -9
- package/dist/src/core/renderers/paneTitle.d.ts +0 -40
- package/dist/src/core/renderers/subVolume.d.ts +0 -13
- package/dist/src/core/renderers/timeAxis.d.ts +0 -14
- package/dist/src/core/renderers/webgl/candleSurface.d.ts +0 -80
- package/dist/src/core/renderers/webgl/sharedWebGLSurface.d.ts +0 -33
- package/dist/src/core/renderers/yAxis.d.ts +0 -14
- package/dist/src/core/scale/logFormula.d.ts +0 -66
- package/dist/src/core/scale/price.d.ts +0 -11
- package/dist/src/core/scale/priceScale.d.ts +0 -87
- package/dist/src/core/subPaneManager.d.ts +0 -22
- package/dist/src/core/theme/colors.d.ts +0 -243
- package/dist/src/core/theme/fonts.d.ts +0 -12
- package/dist/src/core/utils/klineConfig.d.ts +0 -28
- package/dist/src/core/utils/tickCount.d.ts +0 -8
- package/dist/src/core/utils/tickPosition.d.ts +0 -24
- package/dist/src/core/utils/zoom.d.ts +0 -30
- package/dist/src/core/viewport/viewport.d.ts +0 -31
- package/dist/src/index.d.ts +0 -8
- package/dist/src/main.d.ts +0 -0
- package/dist/src/plugin/ConfigManager.d.ts +0 -31
- package/dist/src/plugin/EventBus.d.ts +0 -34
- package/dist/src/plugin/HookSystem.d.ts +0 -28
- package/dist/src/plugin/PluginHost.d.ts +0 -54
- package/dist/src/plugin/PluginRegistry.d.ts +0 -40
- package/dist/src/plugin/StateStore.d.ts +0 -37
- package/dist/src/plugin/index.d.ts +0 -11
- package/dist/src/plugin/rendererPluginManager.d.ts +0 -77
- package/dist/src/plugin/stateKeys.d.ts +0 -6
- package/dist/src/plugin/types.d.ts +0 -439
- package/dist/src/semantic/controller.d.ts +0 -35
- package/dist/src/semantic/drawShape.d.ts +0 -14
- package/dist/src/semantic/index.d.ts +0 -8
- package/dist/src/semantic/types.d.ts +0 -298
- package/dist/src/semantic/validator.d.ts +0 -43
- package/dist/src/test-setup.d.ts +0 -6
- package/dist/src/types/kLine.d.ts +0 -3
- package/dist/src/types/price.d.ts +0 -31
- package/dist/src/types/volumePrice.d.ts +0 -26
- package/dist/src/utils/cache.d.ts +0 -33
- package/dist/src/utils/dateFormat.d.ts +0 -83
- package/dist/src/utils/http.d.ts +0 -14
- package/dist/src/utils/kLineDraw/MA.d.ts +0 -12
- package/dist/src/utils/kLineDraw/axis.d.ts +0 -150
- package/dist/src/utils/kLineDraw/grid.d.ts +0 -30
- package/dist/src/utils/kLineDraw/kLine.d.ts +0 -15
- package/dist/src/utils/kline/format.d.ts +0 -14
- package/dist/src/utils/kline/viewport.d.ts +0 -10
- package/dist/src/utils/logger.d.ts +0 -5
- package/dist/src/utils/mock/genRandomPriceData.d.ts +0 -3
- package/dist/src/utils/priceToY.d.ts +0 -7
- package/dist/src/utils/volumePrice.d.ts +0 -54
package/dist/klinechart.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
.kline-tooltip[data-v-d0fe85e6]{z-index:10;color:#000000c7;pointer-events:none;-webkit-backdrop-filter:blur(6px);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-d0fe85e6]{justify-content:space-between;gap:10px;margin-bottom:6px;font-weight:600;display:flex}.kline-tooltip__grid[data-v-d0fe85e6]{grid-template-columns:1fr;gap:2px;display:grid}.kline-tooltip__grid .row[data-v-d0fe85e6]{justify-content:space-between;gap:10px;display:flex}.kline-tooltip__grid .row span[data-v-d0fe85e6]:first-child{color:#0000008f}@supports (anchor-name:--kmap-anchor) and (position-anchor:--kmap-anchor){.kline-tooltip.use-anchor[data-v-d0fe85e6]{position-anchor:--kline-tooltip-anchor;left:anchor(left);top:anchor(top);position:absolute}.kline-tooltip.use-anchor.anchor-right-bottom[data-v-d0fe85e6]{transform:translate(14px,14px)}.kline-tooltip.use-anchor.anchor-left-bottom[data-v-d0fe85e6]{transform:translate(calc(-100% - 14px),14px)}}.marker-tooltip[data-v-5574cc25]{z-index:10;color:#000000c7;pointer-events:none;-webkit-backdrop-filter:blur(6px);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]{-webkit-backdrop-filter:blur(4px);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]{-webkit-backdrop-filter:blur(4px);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]{-webkit-backdrop-filter:blur(4px);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;-webkit-user-select:none;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;-webkit-user-select:none;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]{-webkit-backdrop-filter:blur(4px);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-6e29f63e]{--kmap-height:var(--kmap-chart-height,100%);--kmap-width:var(--kmap-chart-width,100%);width:var(--kmap-width);height:var(--kmap-height);flex-direction:column;justify-content:center;align-items:center;min-height:300px;display:flex}.chart-stage[data-v-6e29f63e]{align-items:stretch;gap:8px;width:95%;height:85%;min-height:255px;display:flex}.chart-main[data-v-6e29f63e]{flex:auto;align-items:stretch;gap:0;min-width:0;height:100%;display:flex;position:relative}.pane-separator-layer[data-v-6e29f63e]{pointer-events:none;z-index:20;position:absolute;inset:0}.pane-separator-line[data-v-6e29f63e]{opacity:1;box-sizing:border-box;border-top:1px solid #e5e7eb;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-6e29f63e]{border-top-width:2px;border-top-color:#3b82f6;margin-top:-1px}.chart-stage.is-resizing-pane[data-v-6e29f63e],.chart-stage.is-hovering-pane-separator[data-v-6e29f63e]{cursor:ns-resize}.chart-stage.is-hovering-kline[data-v-6e29f63e]{cursor:pointer}.chart-stage.is-hovering-right-axis[data-v-6e29f63e]{cursor:ns-resize}.chart-stage.is-dragging[data-v-6e29f63e]{cursor:grabbing}.chart-container[data-v-6e29f63e]{height:100%;min-height:inherit;scrollbar-width:none;-ms-overflow-style:none;box-sizing:border-box;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;touch-action:none;background:#fff;border:1px solid #e5e7eb;border-right:0;border-radius:6px 0 0 6px;flex:auto;position:relative;overflow:auto hidden}.chart-container[data-v-6e29f63e]::-webkit-scrollbar{display:none}.right-axis-host[data-v-6e29f63e]{height:100%;min-height:inherit;box-sizing:border-box;-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;touch-action:none;background:#fff;border:1px solid #e5e7eb;border-top-right-radius:6px;border-bottom-right-radius:6px;flex:none;position:relative;overflow:visible}.scroll-content[data-v-6e29f63e]{height:100%;min-height:inherit;position:relative}.canvas-layer[data-v-6e29f63e]{pointer-events:none;position:sticky;top:0;left:0}.tooltip-layer[data-v-6e29f63e]{pointer-events:none;z-index:30;position:absolute;inset:0}.tooltip-anchor[data-v-6e29f63e]{pointer-events:none;width:1px;height:1px;position:absolute}.tooltip-anchor.kline-tooltip-anchor.use-anchor[data-v-6e29f63e]{anchor-name:--kline-tooltip-anchor}.tooltip-anchor.marker-tooltip-anchor.use-anchor[data-v-6e29f63e]{anchor-name:--marker-tooltip-anchor}@media (width<=768px),(height<=640px){.chart-stage[data-v-6e29f63e]{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 @@
|
|
|
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,104 +1,75 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@363045841yyt/klinechart",
|
|
3
|
-
"version": "0.7.0",
|
|
4
|
-
"description": "
|
|
5
|
-
"author": "363045841 <slslswbsy@qq.com>",
|
|
3
|
+
"version": "0.7.3-alpha.0",
|
|
4
|
+
"description": "Vue 3 bindings for @363045841yyt/klinechart-core. Idiomatic composables, SFC components.",
|
|
6
5
|
"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
6
|
"type": "module",
|
|
44
7
|
"engines": {
|
|
45
8
|
"node": "^20.19.0 || >=22.12.0"
|
|
46
9
|
},
|
|
47
|
-
"
|
|
48
|
-
|
|
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
|
-
"format": "prettier --write --experimental-cli src/",
|
|
60
|
-
"prepublishOnly": "pnpm run build-only"
|
|
61
|
-
},
|
|
62
|
-
"main": "./dist/index.cjs",
|
|
10
|
+
"sideEffects": false,
|
|
11
|
+
"main": "./dist/index.js",
|
|
63
12
|
"module": "./dist/index.js",
|
|
64
13
|
"types": "./dist/index.d.ts",
|
|
65
14
|
"exports": {
|
|
66
15
|
".": {
|
|
67
|
-
"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
+
}
|
|
72
25
|
},
|
|
73
26
|
"files": [
|
|
74
|
-
"dist"
|
|
27
|
+
"dist",
|
|
28
|
+
"src"
|
|
75
29
|
],
|
|
76
|
-
"
|
|
77
|
-
"
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
78
32
|
},
|
|
79
|
-
"
|
|
80
|
-
"
|
|
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"
|
|
81
58
|
},
|
|
82
59
|
"devDependencies": {
|
|
83
|
-
"@
|
|
84
|
-
"@
|
|
85
|
-
"@
|
|
86
|
-
"@types/node": "^25.6.0",
|
|
87
|
-
"@vitejs/plugin-vue": "^6.0.6",
|
|
88
|
-
"@vitest/coverage-v8": "^4.1.5",
|
|
60
|
+
"@arethetypeswrong/cli": "^0.18.3",
|
|
61
|
+
"@size-limit/preset-small-lib": "^12.1.0",
|
|
62
|
+
"@vitejs/plugin-vue": "^6.0.0",
|
|
89
63
|
"@vue/test-utils": "^2.4.10",
|
|
90
|
-
"@vue/tsconfig": "^0.9.1",
|
|
91
64
|
"jsdom": "^29.1.1",
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"typedoc": "^0.28.19",
|
|
65
|
+
"publint": "^0.3.0",
|
|
66
|
+
"size-limit": "^12.1.0",
|
|
95
67
|
"typescript": "~6.0.3",
|
|
96
68
|
"unplugin-icons": "^23.0.1",
|
|
97
|
-
"
|
|
98
|
-
"vite": "^
|
|
99
|
-
"vite-plugin-dts": "^
|
|
100
|
-
"vite-plugin-vue-devtools": "^8.1.1",
|
|
69
|
+
"vite": "^8.0.14",
|
|
70
|
+
"vite-plugin-babel": "^1.3.2",
|
|
71
|
+
"vite-plugin-dts": "^5.0.1",
|
|
101
72
|
"vitest": "^4.1.5",
|
|
102
|
-
"vue
|
|
73
|
+
"vue": "^3.5.0"
|
|
103
74
|
}
|
|
104
|
-
}
|
|
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
|
+
})
|