@363045841yyt/klinechart 0.6.2 → 0.6.5

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.
@@ -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-f3fbebbd]{width:80%;margin:20px;position:relative}.indicator-scroll-container[data-v-f3fbebbd]{scrollbar-width:none;-webkit-overflow-scrolling:touch;text-align:center;width:100%;overflow:auto hidden}.indicator-scroll-container[data-v-f3fbebbd]::-webkit-scrollbar{display:none}.indicator-list[data-v-f3fbebbd]{gap:8px;margin:0 auto;padding:2px;display:inline-flex}.indicator-divider[data-v-f3fbebbd]{background:#d9d9d9;align-self:center;width:1px;height:20px}.indicator-item[data-v-f3fbebbd]{align-items:center;gap:4px;display:flex}.indicator-item.draggable[data-v-f3fbebbd],.indicator-item.draggable .indicator-btn[data-v-f3fbebbd],.indicator-item.draggable[data-v-f3fbebbd]:hover,.indicator-item.draggable:hover .indicator-btn[data-v-f3fbebbd]{cursor:move}.indicator-item.is-dragging[data-v-f3fbebbd]{opacity:.6}.indicator-item.drag-over .indicator-btn[data-v-f3fbebbd]{border-color:#1a1a1a;box-shadow:0 0 0 2px #1a1a1a1f}.indicator-btn-wrapper[data-v-f3fbebbd]{position:relative}.indicator-btn[data-v-f3fbebbd]{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-f3fbebbd]:hover:not(.hovering){color:#333;background:#f8f8f8;border-color:#ccc}.indicator-btn.active[data-v-f3fbebbd]{color:#1a1a1a;background:#f8f8f8;border-color:#1a1a1a}.indicator-btn.active[data-v-f3fbebbd]:hover:not(.hovering){background:#f0f0f0;border-color:#333}.btn-content[data-v-f3fbebbd]{z-index:1;position:relative}.param-hint[data-v-f3fbebbd]{opacity:.85;font-size:11px}.hover-overlay[data-v-f3fbebbd]{-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-f3fbebbd]{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-f3fbebbd]:hover{color:#333;background:#0000000f}.settings-btn[data-v-f3fbebbd]:hover{color:#1a1a1a}.remove-btn[data-v-f3fbebbd]:hover{color:#ff4d4f}.divider[data-v-f3fbebbd]{background:#e0e0e0;width:1px;height:14px}.add-btn[data-v-f3fbebbd]{anchor-name:--indicator-add-btn;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-f3fbebbd]:hover{color:#1a1a1a;background:#1a1a1a0a;border-color:#1a1a1a}.add-menu[data-v-f3fbebbd]{white-space:nowrap;z-index:9999;background:#fff;border-radius:8px;padding:8px 0;position:fixed;transform:translate(-50%);box-shadow:0 6px 16px #00000014,0 3px 6px -4px #0000001f,0 9px 28px 8px #0000000d}@supports (anchor-name:--kmap-anchor) and (position-anchor:--kmap-anchor){.add-menu.use-anchor[data-v-f3fbebbd]{position-anchor:--indicator-add-btn;left:anchor(center);top:anchor(top);max-width:calc(100vw - 16px);position:fixed;transform:translate(-50%,calc(-100% - 8px))}}.menu-section[data-v-f3fbebbd]{padding:4px 0}.menu-section[data-v-f3fbebbd]:not(:last-child){border-bottom:1px solid #f0f0f0}.menu-title[data-v-f3fbebbd]{color:#999;padding:4px 16px;font-size:12px;font-weight:500}.menu-items[data-v-f3fbebbd]{flex-direction:column;gap:2px;display:flex}.menu-item[data-v-f3fbebbd]{text-align:left;color:#333;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;width:100%;padding:8px 16px;font-size:13px;transition:background .2s;display:flex}.menu-item[data-v-f3fbebbd]:hover:not(.disabled){background:#f5f5f5}.menu-item.disabled[data-v-f3fbebbd]{color:#999;cursor:not-allowed}.menu-item .param-hint[data-v-f3fbebbd]{color:#999;font-size:11px}.active-tag[data-v-f3fbebbd]{color:#1a1a1a;align-items:center;margin-left:auto;display:flex}.fade-enter-active[data-v-f3fbebbd],.fade-leave-active[data-v-f3fbebbd]{transition:opacity .2s}.fade-enter-from[data-v-f3fbebbd],.fade-leave-to[data-v-f3fbebbd]{opacity:0}.slide-enter-active[data-v-f3fbebbd],.slide-leave-active[data-v-f3fbebbd]{transition:all .2s}.slide-enter-from[data-v-f3fbebbd],.slide-leave-to[data-v-f3fbebbd]{opacity:0;transform:translate(-50%)translateY(8px)}.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-f47a57b0]{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-f47a57b0]{flex-direction:column;gap:4px;display:flex}.left-toolbar__divider[data-v-f47a57b0]{background:#e5e7eb;width:18px;height:1px}.left-toolbar__button[data-v-f47a57b0]{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-f47a57b0]:hover{color:#374151;background:#f3f4f6;border-color:#d1d5db}.left-toolbar__button.active[data-v-f47a57b0]{color:#1f2937;background:#e5e7eb;border-color:#9ca3af}.left-toolbar__button[data-v-f47a57b0]:focus-visible{border-color:#6b7280;outline:none}.tool-icon[data-v-f47a57b0]{width:16px;height:16px}.corner-indicator[data-v-f47a57b0]{cursor:pointer;width:8px;height:8px;position:absolute;bottom:0;right:0;overflow:hidden}.corner-indicator[data-v-f47a57b0]: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-f47a57b0]:after,.left-toolbar__button.active .corner-indicator[data-v-f47a57b0]:after{opacity:.7}.corner-indicator.open[data-v-f47a57b0]:after{opacity:.8}.tool-dropdown[data-v-f47a57b0]{-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-f47a57b0]{position:relative}.dropdown-enter-active[data-v-f47a57b0],.dropdown-leave-active[data-v-f47a57b0]{transition:opacity .15s,transform .15s}.dropdown-enter-from[data-v-f47a57b0],.dropdown-leave-to[data-v-f47a57b0]{opacity:0;transform:translateY(-50%)translate(-6px)}@media (width<=768px),(height<=640px){.left-toolbar[data-v-f47a57b0]{border-radius:5px;flex-basis:36px;gap:5px;padding:6px 4px}.left-toolbar__group[data-v-f47a57b0]{gap:3px}.left-toolbar__button[data-v-f47a57b0]{border-radius:3px;width:26px;height:26px}.left-toolbar__divider[data-v-f47a57b0]{width:16px}.corner-indicator[data-v-f47a57b0]{width:7px;height:7px}.corner-indicator[data-v-f47a57b0]:after{border-bottom-width:4px;border-left-width:4px}.tool-dropdown[data-v-f47a57b0]{height:36px}}.settings-overlay[data-v-f47a57b0]{-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-f47a57b0]{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-f47a57b0]{background:#f8f8f8;border-bottom:1px solid #e8e8e8;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.header-left[data-v-f47a57b0]{align-items:baseline;gap:8px;display:flex}.header-right[data-v-f47a57b0]{align-items:center;gap:8px;display:flex}.settings-title[data-v-f47a57b0]{color:#1a1a1a;letter-spacing:.2px;font-size:14px;font-weight:600}.settings-subtitle[data-v-f47a57b0]{color:#999;font-size:11px}.settings-close[data-v-f47a57b0]{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-f47a57b0]:hover{color:#333;background:#f0f0f0;border-color:#ccc}.settings-close svg[data-v-f47a57b0]{width:14px;height:14px}.settings-body[data-v-f47a57b0]{flex-direction:column;gap:10px;padding:16px 20px;display:flex}.settings-item[data-v-f47a57b0]{background:#f8f8f8;border:1px solid #e8e8e8;border-radius:8px;padding:8px 12px}.settings-label[data-v-f47a57b0]{color:#333;cursor:pointer;justify-content:space-between;align-items:center;font-size:13px;display:flex}.settings-checkbox[data-v-f47a57b0]{cursor:pointer;accent-color:#1a1a1a;width:16px;height:16px}.settings-section-divider[data-v-f47a57b0]{align-items:center;gap:8px;margin-top:4px;display:flex}.settings-section-divider[data-v-f47a57b0]:before,.settings-section-divider[data-v-f47a57b0]:after{content:"";border-top:1px solid #e0e0e0;flex:1}.settings-section-label[data-v-f47a57b0]{color:#999;white-space:nowrap;font-size:11px}.settings-item.experimental[data-v-f47a57b0]{background:#fdf8f3;border-color:#f0e0d0}.settings-footer[data-v-f47a57b0]{background:#f8f8f8;border-top:1px solid #e8e8e8;justify-content:space-between;align-items:center;padding:12px 20px;display:flex}.footer-right[data-v-f47a57b0]{gap:8px;display:flex}.settings-btn[data-v-f47a57b0]{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-f47a57b0]{flex-shrink:0;width:12px;height:12px}.settings-btn.reset[data-v-f47a57b0]{color:#666;background:0 0;border-color:#d0d0d0}.settings-btn.reset[data-v-f47a57b0]:hover{color:#e74c3c;background:#e74c3c14;border-color:#c0392b}.settings-btn.cancel[data-v-f47a57b0]{color:#666;background:0 0;border-color:#d0d0d0}.settings-btn.cancel[data-v-f47a57b0]:hover{color:#333;background:#f0f0f0;border-color:#bbb}.settings-btn.confirm[data-v-f47a57b0]{color:#fff;background:#1a1a1a;border-color:#1a1a1a}.settings-btn.confirm[data-v-f47a57b0]:hover{background:#333;border-color:#333;transform:translateY(-1px);box-shadow:0 2px 10px #00000026}.settings-btn.confirm[data-v-f47a57b0]:active{box-shadow:none;transform:translateY(0)}.chart-wrapper[data-v-b9893341]{--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-b9893341]{align-items:stretch;gap:8px;width:95%;height:85%;min-height:255px;display:flex}.chart-main[data-v-b9893341]{flex:auto;align-items:stretch;gap:0;min-width:0;height:100%;display:flex;position:relative}.pane-separator-layer[data-v-b9893341]{pointer-events:none;z-index:20;position:absolute;inset:0}.pane-separator-line[data-v-b9893341]{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-b9893341]{border-top-width:2px;border-top-color:#3b82f6;margin-top:-1px}.chart-stage.is-resizing-pane[data-v-b9893341],.chart-stage.is-hovering-pane-separator[data-v-b9893341]{cursor:ns-resize}.chart-stage.is-hovering-kline[data-v-b9893341]{cursor:pointer}.chart-stage.is-hovering-right-axis[data-v-b9893341]{cursor:ns-resize}.chart-stage.is-dragging[data-v-b9893341]{cursor:grabbing}.chart-container[data-v-b9893341]{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-b9893341]::-webkit-scrollbar{display:none}.chart-container[data-v-b9893341]:hover{cursor:crosshair}.chart-stage.is-resizing-pane .chart-container[data-v-b9893341],.chart-stage.is-hovering-pane-separator .chart-container[data-v-b9893341]{cursor:ns-resize}.chart-stage.is-hovering-kline .chart-container[data-v-b9893341]{cursor:pointer}.chart-stage.is-dragging .chart-container[data-v-b9893341]{cursor:grabbing}.right-axis-host[data-v-b9893341]{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-b9893341]{height:100%;min-height:inherit;position:relative}.canvas-layer[data-v-b9893341]{pointer-events:none;position:sticky;top:0;left:0}.tooltip-layer[data-v-b9893341]{pointer-events:none;z-index:30;position:absolute;inset:0}.tooltip-anchor[data-v-b9893341]{pointer-events:none;width:1px;height:1px;position:absolute}.tooltip-anchor.kline-tooltip-anchor.use-anchor[data-v-b9893341]{anchor-name:--kline-tooltip-anchor}.tooltip-anchor.marker-tooltip-anchor.use-anchor[data-v-b9893341]{anchor-name:--marker-tooltip-anchor}@media (width<=768px),(height<=640px){.chart-stage[data-v-b9893341]{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
+ .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-f3fbebbd]{width:80%;margin:20px;position:relative}.indicator-scroll-container[data-v-f3fbebbd]{scrollbar-width:none;-webkit-overflow-scrolling:touch;text-align:center;width:100%;overflow:auto hidden}.indicator-scroll-container[data-v-f3fbebbd]::-webkit-scrollbar{display:none}.indicator-list[data-v-f3fbebbd]{gap:8px;margin:0 auto;padding:2px;display:inline-flex}.indicator-divider[data-v-f3fbebbd]{background:#d9d9d9;align-self:center;width:1px;height:20px}.indicator-item[data-v-f3fbebbd]{align-items:center;gap:4px;display:flex}.indicator-item.draggable[data-v-f3fbebbd],.indicator-item.draggable .indicator-btn[data-v-f3fbebbd],.indicator-item.draggable[data-v-f3fbebbd]:hover,.indicator-item.draggable:hover .indicator-btn[data-v-f3fbebbd]{cursor:move}.indicator-item.is-dragging[data-v-f3fbebbd]{opacity:.6}.indicator-item.drag-over .indicator-btn[data-v-f3fbebbd]{border-color:#1a1a1a;box-shadow:0 0 0 2px #1a1a1a1f}.indicator-btn-wrapper[data-v-f3fbebbd]{position:relative}.indicator-btn[data-v-f3fbebbd]{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-f3fbebbd]:hover:not(.hovering){color:#333;background:#f8f8f8;border-color:#ccc}.indicator-btn.active[data-v-f3fbebbd]{color:#1a1a1a;background:#f8f8f8;border-color:#1a1a1a}.indicator-btn.active[data-v-f3fbebbd]:hover:not(.hovering){background:#f0f0f0;border-color:#333}.btn-content[data-v-f3fbebbd]{z-index:1;position:relative}.param-hint[data-v-f3fbebbd]{opacity:.85;font-size:11px}.hover-overlay[data-v-f3fbebbd]{-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-f3fbebbd]{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-f3fbebbd]:hover{color:#333;background:#0000000f}.settings-btn[data-v-f3fbebbd]:hover{color:#1a1a1a}.remove-btn[data-v-f3fbebbd]:hover{color:#ff4d4f}.divider[data-v-f3fbebbd]{background:#e0e0e0;width:1px;height:14px}.add-btn[data-v-f3fbebbd]{anchor-name:--indicator-add-btn;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-f3fbebbd]:hover{color:#1a1a1a;background:#1a1a1a0a;border-color:#1a1a1a}.add-menu[data-v-f3fbebbd]{white-space:nowrap;z-index:9999;background:#fff;border-radius:8px;padding:8px 0;position:fixed;transform:translate(-50%);box-shadow:0 6px 16px #00000014,0 3px 6px -4px #0000001f,0 9px 28px 8px #0000000d}@supports (anchor-name:--kmap-anchor) and (position-anchor:--kmap-anchor){.add-menu.use-anchor[data-v-f3fbebbd]{position-anchor:--indicator-add-btn;left:anchor(center);top:anchor(top);max-width:calc(100vw - 16px);position:fixed;transform:translate(-50%,calc(-100% - 8px))}}.menu-section[data-v-f3fbebbd]{padding:4px 0}.menu-section[data-v-f3fbebbd]:not(:last-child){border-bottom:1px solid #f0f0f0}.menu-title[data-v-f3fbebbd]{color:#999;padding:4px 16px;font-size:12px;font-weight:500}.menu-items[data-v-f3fbebbd]{flex-direction:column;gap:2px;display:flex}.menu-item[data-v-f3fbebbd]{text-align:left;color:#333;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;width:100%;padding:8px 16px;font-size:13px;transition:background .2s;display:flex}.menu-item[data-v-f3fbebbd]:hover:not(.disabled){background:#f5f5f5}.menu-item.disabled[data-v-f3fbebbd]{color:#999;cursor:not-allowed}.menu-item .param-hint[data-v-f3fbebbd]{color:#999;font-size:11px}.active-tag[data-v-f3fbebbd]{color:#1a1a1a;align-items:center;margin-left:auto;display:flex}.fade-enter-active[data-v-f3fbebbd],.fade-leave-active[data-v-f3fbebbd]{transition:opacity .2s}.fade-enter-from[data-v-f3fbebbd],.fade-leave-to[data-v-f3fbebbd]{opacity:0}.slide-enter-active[data-v-f3fbebbd],.slide-leave-active[data-v-f3fbebbd]{transition:all .2s}.slide-enter-from[data-v-f3fbebbd],.slide-leave-to[data-v-f3fbebbd]{opacity:0;transform:translate(-50%)translateY(8px)}.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-3bc8355a]{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-3bc8355a]{flex-direction:column;gap:4px;display:flex}.left-toolbar__divider[data-v-3bc8355a]{background:#e5e7eb;width:18px;height:1px}.left-toolbar__button[data-v-3bc8355a]{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-3bc8355a]:hover{color:#374151;background:#f3f4f6;border-color:#d1d5db}.left-toolbar__button.active[data-v-3bc8355a]{color:#1f2937;background:#e5e7eb;border-color:#9ca3af}.left-toolbar__button[data-v-3bc8355a]:focus-visible{border-color:#6b7280;outline:none}.tool-icon[data-v-3bc8355a]{width:16px;height:16px}.corner-indicator[data-v-3bc8355a]{cursor:pointer;width:8px;height:8px;position:absolute;bottom:0;right:0;overflow:hidden}.corner-indicator[data-v-3bc8355a]: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-3bc8355a]:after,.left-toolbar__button.active .corner-indicator[data-v-3bc8355a]:after{opacity:.7}.corner-indicator.open[data-v-3bc8355a]:after{opacity:.8}.tool-dropdown[data-v-3bc8355a]{-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-3bc8355a]{position:relative}.dropdown-enter-active[data-v-3bc8355a],.dropdown-leave-active[data-v-3bc8355a]{transition:opacity .15s,transform .15s}.dropdown-enter-from[data-v-3bc8355a],.dropdown-leave-to[data-v-3bc8355a]{opacity:0;transform:translateY(-50%)translate(-6px)}@media (width<=768px),(height<=640px){.left-toolbar[data-v-3bc8355a]{border-radius:5px;flex-basis:36px;gap:5px;padding:6px 4px}.left-toolbar__group[data-v-3bc8355a]{gap:3px}.left-toolbar__button[data-v-3bc8355a]{border-radius:3px;width:26px;height:26px}.left-toolbar__divider[data-v-3bc8355a]{width:16px}.corner-indicator[data-v-3bc8355a]{width:7px;height:7px}.corner-indicator[data-v-3bc8355a]:after{border-bottom-width:4px;border-left-width:4px}.tool-dropdown[data-v-3bc8355a]{height:36px}}.settings-overlay[data-v-3bc8355a]{-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-3bc8355a]{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-3bc8355a]{background:#f8f8f8;border-bottom:1px solid #e8e8e8;justify-content:space-between;align-items:center;padding:16px 20px;display:flex}.header-left[data-v-3bc8355a]{align-items:baseline;gap:8px;display:flex}.header-right[data-v-3bc8355a]{align-items:center;gap:8px;display:flex}.settings-title[data-v-3bc8355a]{color:#1a1a1a;letter-spacing:.2px;font-size:14px;font-weight:600}.settings-subtitle[data-v-3bc8355a]{color:#999;font-size:11px}.settings-close[data-v-3bc8355a]{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-3bc8355a]:hover{color:#333;background:#f0f0f0;border-color:#ccc}.settings-close svg[data-v-3bc8355a]{width:14px;height:14px}.settings-body[data-v-3bc8355a]{flex-direction:column;gap:10px;padding:16px 20px;display:flex}.settings-item[data-v-3bc8355a]{background:#f8f8f8;border:1px solid #e8e8e8;border-radius:8px;padding:8px 12px}.settings-label[data-v-3bc8355a]{color:#333;cursor:pointer;justify-content:space-between;align-items:center;font-size:13px;display:flex}.settings-checkbox[data-v-3bc8355a]{cursor:pointer;accent-color:#1a1a1a;width:16px;height:16px}.settings-section-divider[data-v-3bc8355a]{align-items:center;gap:8px;margin-top:4px;display:flex}.settings-section-divider[data-v-3bc8355a]:before,.settings-section-divider[data-v-3bc8355a]:after{content:"";border-top:1px solid #e0e0e0;flex:1}.settings-section-label[data-v-3bc8355a]{color:#999;white-space:nowrap;font-size:11px}.settings-item.experimental[data-v-3bc8355a]{background:#fdf8f3;border-color:#f0e0d0}.settings-footer[data-v-3bc8355a]{background:#f8f8f8;border-top:1px solid #e8e8e8;justify-content:space-between;align-items:center;padding:12px 20px;display:flex}.footer-right[data-v-3bc8355a]{gap:8px;display:flex}.settings-btn[data-v-3bc8355a]{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-3bc8355a]{flex-shrink:0;width:12px;height:12px}.settings-btn.reset[data-v-3bc8355a]{color:#666;background:0 0;border-color:#d0d0d0}.settings-btn.reset[data-v-3bc8355a]:hover{color:#e74c3c;background:#e74c3c14;border-color:#c0392b}.settings-btn.cancel[data-v-3bc8355a]{color:#666;background:0 0;border-color:#d0d0d0}.settings-btn.cancel[data-v-3bc8355a]:hover{color:#333;background:#f0f0f0;border-color:#bbb}.settings-btn.confirm[data-v-3bc8355a]{color:#fff;background:#1a1a1a;border-color:#1a1a1a}.settings-btn.confirm[data-v-3bc8355a]:hover{background:#333;border-color:#333;transform:translateY(-1px);box-shadow:0 2px 10px #00000026}.settings-btn.confirm[data-v-3bc8355a]:active{box-shadow:none;transform:translateY(0)}.chart-wrapper[data-v-e5d29a39]{--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-e5d29a39]{align-items:stretch;gap:8px;width:95%;height:85%;min-height:255px;display:flex}.chart-main[data-v-e5d29a39]{flex:auto;align-items:stretch;gap:0;min-width:0;height:100%;display:flex;position:relative}.pane-separator-layer[data-v-e5d29a39]{pointer-events:none;z-index:20;position:absolute;inset:0}.pane-separator-line[data-v-e5d29a39]{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-e5d29a39]{border-top-width:2px;border-top-color:#3b82f6;margin-top:-1px}.chart-stage.is-resizing-pane[data-v-e5d29a39],.chart-stage.is-hovering-pane-separator[data-v-e5d29a39]{cursor:ns-resize}.chart-stage.is-hovering-kline[data-v-e5d29a39]{cursor:pointer}.chart-stage.is-hovering-right-axis[data-v-e5d29a39]{cursor:ns-resize}.chart-stage.is-dragging[data-v-e5d29a39]{cursor:grabbing}.chart-container[data-v-e5d29a39]{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-e5d29a39]::-webkit-scrollbar{display:none}.chart-container[data-v-e5d29a39]:hover{cursor:crosshair}.chart-stage.is-resizing-pane .chart-container[data-v-e5d29a39],.chart-stage.is-hovering-pane-separator .chart-container[data-v-e5d29a39]{cursor:ns-resize}.chart-stage.is-hovering-kline .chart-container[data-v-e5d29a39]{cursor:pointer}.chart-stage.is-dragging .chart-container[data-v-e5d29a39]{cursor:grabbing}.right-axis-host[data-v-e5d29a39]{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-e5d29a39]{height:100%;min-height:inherit;position:relative}.canvas-layer[data-v-e5d29a39]{pointer-events:none;position:sticky;top:0;left:0}.tooltip-layer[data-v-e5d29a39]{pointer-events:none;z-index:30;position:absolute;inset:0}.tooltip-anchor[data-v-e5d29a39]{pointer-events:none;width:1px;height:1px;position:absolute}.tooltip-anchor.kline-tooltip-anchor.use-anchor[data-v-e5d29a39]{anchor-name:--kline-tooltip-anchor}.tooltip-anchor.marker-tooltip-anchor.use-anchor[data-v-e5d29a39]{anchor-name:--marker-tooltip-anchor}@media (width<=768px),(height<=640px){.chart-stage[data-v-e5d29a39]{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*/
@@ -8,7 +8,45 @@ export interface SettingItem {
8
8
  default: boolean;
9
9
  group?: string;
10
10
  }
11
+ /**
12
+ * 检测是否为移动端设备
13
+ */
14
+ export declare function isMobileDevice(): boolean;
11
15
  /** 默认设置配置 */
12
- export declare const DEFAULT_SETTINGS: SettingItem[];
16
+ export declare const DEFAULT_SETTINGS: readonly [{
17
+ readonly key: "showVolumePriceMarkers";
18
+ readonly label: "显示量价关系标记";
19
+ readonly type: "boolean";
20
+ readonly default: false;
21
+ readonly group: "main";
22
+ }, {
23
+ readonly key: "logarithmicScale";
24
+ readonly label: "对数价格轴";
25
+ readonly type: "boolean";
26
+ readonly default: false;
27
+ readonly group: "main";
28
+ }, {
29
+ readonly key: "enableWebGLRendering";
30
+ readonly label: "启用 WebGL 硬件加速渲染";
31
+ readonly type: "boolean";
32
+ readonly default: true;
33
+ readonly group: "main";
34
+ }, {
35
+ readonly key: "disableMainPaneVerticalScroll";
36
+ readonly label: "主图纵轴刻度自适应调整";
37
+ readonly type: "boolean";
38
+ readonly default: true;
39
+ readonly group: "experimental";
40
+ }, {
41
+ readonly key: "performanceTest10kKlines";
42
+ readonly label: "万条K线性能测试";
43
+ readonly type: "boolean";
44
+ readonly default: false;
45
+ readonly group: "experimental";
46
+ }];
47
+ /** 图表设置类型(从 DEFAULT_SETTINGS 自动推导,同时兼容扩展) */
48
+ export type ChartSettings = {
49
+ [K in (typeof DEFAULT_SETTINGS)[number]['key']]?: boolean;
50
+ } & Record<string, boolean>;
13
51
  /** localStorage 存储键名 */
14
52
  export declare const SETTINGS_STORAGE_KEY = "kline-chart-settings";
@@ -1,4 +1,5 @@
1
1
  import { KLineData } from '../types/price';
2
+ import { ChartSettings } from '../config/chartSettings';
2
3
  import { VisibleRange, UpdateLevel } from './layout/pane';
3
4
  import { InteractionController } from './controller/interaction';
4
5
  import { PaneRenderer } from './paneRenderer';
@@ -106,14 +107,22 @@ export declare class Chart {
106
107
  private preciseDpr;
107
108
  /** 统一监听容器尺寸与 DPR 变化 */
108
109
  private resizeObserver?;
110
+ /** scroll 事件处理器引用(用于 cleanup) */
111
+ private onScroll?;
109
112
  /** 最近一次观测到的容器尺寸 */
110
113
  private observedSize;
114
+ /** 缓存的 scrollLeft(通过 scroll 事件同步,避免每帧读取 DOM 触发强制回流) */
115
+ private cachedScrollLeft;
116
+ /** overlay 上一帧是否有十字线(用于判断何时需要清除) */
117
+ private overlayHadCrosshair;
111
118
  /** 用户设置配置(传递给渲染器) */
112
119
  private settings;
113
120
  /** pane ratio 状态(按 paneId 维护,sum=1 仅对可见 pane) */
114
121
  private paneRatios;
115
122
  /** 视口变化回调(供外部同步 DPR/尺寸) */
116
123
  private onViewportChange?;
124
+ /** 共享 X 轴上下文缓存 */
125
+ private xAxisCtx;
117
126
  /** pane 布局回流回调(Chart -> UI 单向) */
118
127
  private onPaneLayoutChange?;
119
128
  /** 数据变化回调(供外部同步 dataLength) */
@@ -128,6 +137,8 @@ export declare class Chart {
128
137
  private indicatorScheduler;
129
138
  /** 上次可见范围(用于检测视口变化) */
130
139
  private lastVisibleRange;
140
+ /** Overlay 帧复用的最近主渲染结果 */
141
+ private cachedDrawFrame;
131
142
  /** 当前激活的主图指标列表(如 ['boll', 'ma']) */
132
143
  private activeMainIndicators;
133
144
  /** 主图指标参数配置 */
@@ -203,6 +214,8 @@ export declare class Chart {
203
214
  private getEffectiveDpr;
204
215
  getViewport(): Viewport | null;
205
216
  getCurrentDpr(): number;
217
+ /** 获取缓存的 scrollLeft(避免读取 DOM 触发强制回流) */
218
+ getCachedScrollLeft(): number;
206
219
  /** 获取插件宿主 */
207
220
  get plugin(): PluginHostImpl;
208
221
  /** 安装渲染器插件 */
@@ -218,7 +231,7 @@ export declare class Chart {
218
231
  /** 获取所有渲染器 */
219
232
  getAllRenderers(): RendererPlugin[];
220
233
  /** 更新用户设置(触发重绘) */
221
- updateSettings(settings: Record<string, boolean>): void;
234
+ updateSettings(settings: ChartSettings): void;
222
235
  /**
223
236
  * 绘制一帧
224
237
  * @param level 更新级别,决定渲染哪些层
@@ -1,5 +1,6 @@
1
1
  import { Chart } from '../chart';
2
2
  import { MarkerEntity, CustomMarkerEntity } from '../marker/registry';
3
+ import { ChartSettings } from '../../config/chartSettings';
3
4
  export interface InteractionSnapshot {
4
5
  crosshairPos: {
5
6
  x: number;
@@ -102,16 +103,19 @@ export declare class InteractionController {
102
103
  private kLinePositions;
103
104
  /** 当前帧的可见 K 线索引范围 */
104
105
  private visibleRange;
106
+ /** hover 去重快照 */
107
+ private lastHoverRenderKey;
105
108
  /** K 线宽度(物理像素),用于计算 K 线中心偏移 */
106
109
  private kWidthPx;
107
110
  constructor(chart: Chart);
108
111
  /** 设置捏合缩放回调 */
109
112
  setOnPinchZoom(callback: (delta: number, centerX: number) => void): void;
110
113
  /** 更新用户设置 */
111
- updateSettings(settings: Record<string, boolean>): void;
114
+ updateSettings(settings: ChartSettings): void;
112
115
  getInteractionSnapshot(): InteractionSnapshot;
113
116
  setOnInteractionChange(callback: (snapshot: InteractionSnapshot) => void): void;
114
117
  private notifyInteractionChange;
118
+ private getHoverRenderKey;
115
119
  /**
116
120
  * [触屏]:处理 Pointer 按下事件
117
121
  * @param e PointerEvent
@@ -1,4 +1,5 @@
1
1
  import { KLineData } from '../../types/price';
2
+ import { KLineSoALayout } from './soa';
2
3
  /**
3
4
  * MA 周期配置标志
4
5
  */
@@ -127,3 +128,142 @@ export interface KSTPoint {
127
128
  export declare function calcKSTData(data: KLineData[], roc1: number, roc2: number, roc3: number, roc4: number, signalPeriod: number): KSTPoint[];
128
129
  export declare const DEFAULT_FASTK_PERIOD = 9;
129
130
  export declare function calcFASTKData(data: KLineData[], period: number): (number | undefined)[];
131
+ /**
132
+ * MACD 数据点
133
+ */
134
+ export interface MACDPoint {
135
+ /** DIF 线值 */
136
+ dif: number;
137
+ /** DEA 线值 */
138
+ dea: number;
139
+ /** MACD 柱状图值 */
140
+ macd: number;
141
+ }
142
+ /**
143
+ * 默认 MACD 参数
144
+ */
145
+ export declare const DEFAULT_MACD_FAST_PERIOD = 12;
146
+ export declare const DEFAULT_MACD_SLOW_PERIOD = 26;
147
+ export declare const DEFAULT_MACD_SIGNAL_PERIOD = 9;
148
+ /**
149
+ * 计算 EMA(指数移动平均)值
150
+ * EMA(today) = close × K + EMA(yesterday) × (1 - K)
151
+ * K = 2 / (period + 1)
152
+ * @param data K线数据数组
153
+ * @param period 周期
154
+ * @returns EMA 值数组,第一个值使用第一个收盘价
155
+ */
156
+ export declare function calcEMA(data: KLineData[], period: number): number[];
157
+ /**
158
+ * 基于数值数组计算 EMA
159
+ * @param values 数值数组(可能包含 undefined)
160
+ * @param period 周期
161
+ * @returns EMA 值数组
162
+ */
163
+ export declare function calcEMAFromArray(values: (number | undefined)[], period: number): (number | undefined)[];
164
+ /**
165
+ * 计算 MACD 数据
166
+ * DIF = EMA(close, fastPeriod) - EMA(close, slowPeriod)
167
+ * DEA = EMA(DIF, signalPeriod)
168
+ * MACD = (DIF - DEA) × 2
169
+ * @param data K线数据数组
170
+ * @param fastPeriod 快线周期(默认12)
171
+ * @param slowPeriod 慢线周期(默认26)
172
+ * @param signalPeriod 信号线周期(默认9)
173
+ * @returns MACD 数据点数组,前 slowPeriod-1 个可能为 undefined
174
+ */
175
+ export declare function calcMACDData(data: KLineData[], fastPeriod: number, slowPeriod: number, signalPeriod: number): MACDPoint[];
176
+ /**
177
+ * 从 SoA 布局计算 BOLL 数据(验证用包装函数)
178
+ * @param layout SoA 布局
179
+ * @param period 周期
180
+ * @param multiplier 标准差倍数
181
+ * @returns BOLL 数据点数组
182
+ */
183
+ export declare function calcBOLLDataSoA(layout: KLineSoALayout, period: number, multiplier: number): BOLLPoint[];
184
+ /**
185
+ * 从 SoA 布局计算 EXPMA 数据(验证用包装函数)
186
+ * @param layout SoA 布局
187
+ * @param fastPeriod 快线周期
188
+ * @param slowPeriod 慢线周期
189
+ * @returns EXPMA 数据点数组
190
+ */
191
+ export declare function calcEXPMADataSoA(layout: KLineSoALayout, fastPeriod: number, slowPeriod: number): EXPMAPoint[];
192
+ /**
193
+ * 从 SoA 布局计算 ENE 数据(验证用包装函数)
194
+ * @param layout SoA 布局
195
+ * @param period 周期
196
+ * @param deviation 偏离率百分比
197
+ * @returns ENE 数据点数组
198
+ */
199
+ export declare function calcENEDataSoA(layout: KLineSoALayout, period: number, deviation: number): ENEPoint[];
200
+ /**
201
+ * 从 SoA 布局计算 MA 数据(验证用包装函数)
202
+ * @param layout SoA 布局
203
+ * @param period MA周期
204
+ * @returns MA 值数组
205
+ */
206
+ export declare function calcMADataSoA(layout: KLineSoALayout, period: number): (number | undefined)[];
207
+ /**
208
+ * 从 SoA 布局计算 RSI 数据(验证用包装函数)
209
+ * @param layout SoA 布局
210
+ * @param period RSI周期
211
+ * @returns RSI 值数组
212
+ */
213
+ export declare function calcRSIDataSoA(layout: KLineSoALayout, period: number): (number | undefined)[];
214
+ /**
215
+ * 从 SoA 布局计算 CCI 数据(验证用包装函数)
216
+ * @param layout SoA 布局
217
+ * @param period 周期
218
+ * @returns CCI 值数组
219
+ */
220
+ export declare function calcCCIDataSoA(layout: KLineSoALayout, period: number): (number | undefined)[];
221
+ /**
222
+ * 从 SoA 布局计算 STOCH 数据(验证用包装函数)
223
+ * @param layout SoA 布局
224
+ * @param n RSV周期
225
+ * @param m K的M日移动平均周期
226
+ * @returns STOCH 数据点数组
227
+ */
228
+ export declare function calcSTOCHDataSoA(layout: KLineSoALayout, n: number, m: number): STOCHPoint[];
229
+ /**
230
+ * 从 SoA 布局计算 MOM 数据(验证用包装函数)
231
+ * @param layout SoA 布局
232
+ * @param period 周期
233
+ * @returns MOM 值数组
234
+ */
235
+ export declare function calcMOMDataSoA(layout: KLineSoALayout, period: number): (number | undefined)[];
236
+ /**
237
+ * 从 SoA 布局计算 WMSR 数据(验证用包装函数)
238
+ * @param layout SoA 布局
239
+ * @param period 周期
240
+ * @returns WMSR 值数组
241
+ */
242
+ export declare function calcWMSRDataSoA(layout: KLineSoALayout, period: number): (number | undefined)[];
243
+ /**
244
+ * 从 SoA 布局计算 KST 数据(验证用包装函数)
245
+ * @param layout SoA 布局
246
+ * @param roc1 第一个ROC周期
247
+ * @param roc2 第二个ROC周期
248
+ * @param roc3 第三个ROC周期
249
+ * @param roc4 第四个ROC周期
250
+ * @param signalPeriod 信号线周期
251
+ * @returns KST 数据点数组
252
+ */
253
+ export declare function calcKSTDataSoA(layout: KLineSoALayout, roc1: number, roc2: number, roc3: number, roc4: number, signalPeriod: number): KSTPoint[];
254
+ /**
255
+ * 从 SoA 布局计算 FASTK 数据(验证用包装函数)
256
+ * @param layout SoA 布局
257
+ * @param period 周期
258
+ * @returns FASTK 值数组
259
+ */
260
+ export declare function calcFASTKDataSoA(layout: KLineSoALayout, period: number): (number | undefined)[];
261
+ /**
262
+ * 从 SoA 布局计算 MACD 数据(验证用包装函数)
263
+ * @param layout SoA 布局
264
+ * @param fastPeriod 快线周期
265
+ * @param slowPeriod 慢线周期
266
+ * @param signalPeriod 信号线周期
267
+ * @returns MACD 数据点数组
268
+ */
269
+ export declare function calcMACDDataSoA(layout: KLineSoALayout, fastPeriod: number, slowPeriod: number, signalPeriod: number): MACDPoint[];
@@ -0,0 +1,58 @@
1
+ import { BaseIndicatorState } from '../../plugin';
2
+ import { MACDPoint } from './calculators';
3
+ /**
4
+ * MACD 调度器配置
5
+ */
6
+ export interface MACDSchedulerConfig {
7
+ /** 快线周期(默认 12) */
8
+ fastPeriod: number;
9
+ /** 慢线周期(默认 26) */
10
+ slowPeriod: number;
11
+ /** DEA 周期(默认 9) */
12
+ signalPeriod: number;
13
+ /** 是否显示 DIF 线 */
14
+ showDIF: boolean;
15
+ /** 是否显示 DEA 线 */
16
+ showDEA: boolean;
17
+ /** 是否显示 MACD 柱 */
18
+ showBAR: boolean;
19
+ }
20
+ /**
21
+ * MACD 渲染器状态
22
+ * 用 Record 而非 Map,兼容 JSON 序列化和 postMessage
23
+ */
24
+ export interface MACDRenderState extends BaseIndicatorState {
25
+ /** MACD 系列数据,与 K 线数据同长度 */
26
+ series: MACDPoint[];
27
+ /** 配置参数 */
28
+ params: MACDSchedulerConfig;
29
+ /** 可视范围内的最小值 */
30
+ visibleMin: number;
31
+ /** 可视范围内的最大值 */
32
+ visibleMax: number;
33
+ /** 固定数值范围最小值 */
34
+ valueMin: number;
35
+ /** 固定数值范围最大值 */
36
+ valueMax: number;
37
+ /** 最新值 */
38
+ latestValues?: {
39
+ dif: number;
40
+ dea: number;
41
+ macd: number;
42
+ };
43
+ }
44
+ /**
45
+ * MACD State 的命名空间 key
46
+ * 格式: indicator:macd:{paneId}
47
+ */
48
+ export declare const MACD_STATE_KEY = "indicator:macd";
49
+ /**
50
+ * 创建 MACD State Key
51
+ * @param paneId pane ID,如 'sub_MACD'
52
+ */
53
+ export declare function createMACDStateKey(paneId: string): string;
54
+ /**
55
+ * 空 MACD State(哨兵值)
56
+ * visibleMin > visibleMax 表示无有效数据
57
+ */
58
+ export declare const EMPTY_MACD_STATE: MACDRenderState;
@@ -1,6 +1,7 @@
1
1
  import { PluginHost } from '../../plugin';
2
2
  import { KLineData } from '../../types/price';
3
3
  import { MAFlags } from './calculators';
4
+ import { MACDSchedulerConfig } from './macdState';
4
5
  /**
5
6
  * 可见范围
6
7
  */
@@ -138,6 +139,9 @@ export declare class IndicatorScheduler {
138
139
  private fastkConfig;
139
140
  private fastkPaneId;
140
141
  private cachedFastkSeries;
142
+ private macdConfig;
143
+ private macdPaneId;
144
+ private cachedMacdSeries;
141
145
  private dirtyData;
142
146
  private dirtyRange;
143
147
  private dirtyBollConfig;
@@ -150,6 +154,7 @@ export declare class IndicatorScheduler {
150
154
  private dirtyWmsrConfig;
151
155
  private dirtyKstConfig;
152
156
  private dirtyFastkConfig;
157
+ private dirtyMacdConfig;
153
158
  private dirtyRsiState;
154
159
  private dirtyCciState;
155
160
  private dirtyStochState;
@@ -157,6 +162,7 @@ export declare class IndicatorScheduler {
157
162
  private dirtyWmsrState;
158
163
  private dirtyKstState;
159
164
  private dirtyFastkState;
165
+ private dirtyMacdState;
160
166
  /** 当前激活的主图指标列表 */
161
167
  private activeMainIndicators;
162
168
  /**
@@ -236,6 +242,12 @@ export declare class IndicatorScheduler {
236
242
  * @param paneId FASTK pane ID(可选,默认 'sub_FASTK')
237
243
  */
238
244
  updateFASTKConfig(config: Partial<FASTKSchedulerConfig>, paneId?: string): void;
245
+ /**
246
+ * MACD 配置变更时调用
247
+ * @param config 新的 MACD 配置
248
+ * @param paneId MACD pane ID(可选,默认 'sub_MACD')
249
+ */
250
+ updateMACDConfig(config: Partial<MACDSchedulerConfig>, paneId?: string): void;
239
251
  /**
240
252
  * 视口变更时调用
241
253
  * @param visibleRange 新的可见范围
@@ -0,0 +1,115 @@
1
+ import { KLineData } from '../../types/price';
2
+ /**
3
+ * KLineData 的 SoA (Structure of Arrays) 布局
4
+ * 用于零拷贝传输到 Web Worker
5
+ *
6
+ * 内存布局:
7
+ * | timestamps | opens | highs | lows | closes | volumes | turnovers |
8
+ * | 8*N | 8*N | 8*N | 8*N | 8*N | 8*N | 8*N |
9
+ *
10
+ * 其中 N 为数据长度,每列 8 字节(Float64)
11
+ */
12
+ export interface KLineSoALayout {
13
+ /** 底层缓冲区(SharedArrayBuffer 或 ArrayBuffer) */
14
+ buffer: SharedArrayBuffer | ArrayBuffer;
15
+ /** 数据长度 */
16
+ length: number;
17
+ /** 是否使用 SharedArrayBuffer */
18
+ isShared: boolean;
19
+ /** 是否有成交量数据 */
20
+ hasVolume: boolean;
21
+ /** 是否有成交额数据 */
22
+ hasTurnover: boolean;
23
+ /** 时间戳数组(毫秒) */
24
+ timestamps: Float64Array;
25
+ /** 开盘价数组 */
26
+ opens: Float64Array;
27
+ /** 最高价数组 */
28
+ highs: Float64Array;
29
+ /** 最低价数组 */
30
+ lows: Float64Array;
31
+ /** 收盘价数组 */
32
+ closes: Float64Array;
33
+ /** 成交量数组(无值时为 0) */
34
+ volumes: Float64Array;
35
+ /** 成交额数组(无值时为 0) */
36
+ turnovers: Float64Array;
37
+ }
38
+ /**
39
+ * SharedKLineBuffer - K线数据的 SoA 管理类
40
+ *
41
+ * 提供 AoS (KLineData[]) 与 SoA 布局之间的转换,
42
+ * 支持 SharedArrayBuffer(零拷贝)和 ArrayBuffer(降级)
43
+ */
44
+ export declare class SharedKLineBuffer {
45
+ /**
46
+ * 检测 SharedArrayBuffer 是否可用
47
+ * 需要页面有 COOP/COEP 头支持
48
+ */
49
+ static detectSupport(): boolean;
50
+ /**
51
+ * 计算 SoA 布局所需的总字节数
52
+ */
53
+ static calculateByteLength(length: number): number;
54
+ /**
55
+ * 将 KLineData[] 转换为 SoA 布局
56
+ * @param data K线数据数组(AoS 格式)
57
+ * @param preferShared 是否优先使用 SharedArrayBuffer(默认 true)
58
+ * @returns SoA 布局对象
59
+ */
60
+ static fromKLineData(data: KLineData[], preferShared?: boolean): KLineSoALayout;
61
+ /**
62
+ * 更新现有 SoA 布局的数据(尽可能复用缓冲区)
63
+ * 如果新数据长度超过原缓冲区,会创建新缓冲区
64
+ *
65
+ * @param layout 现有 SoA 布局
66
+ * @param data 新的 K线数据
67
+ * @returns 更新后的 SoA 布局(可能是新对象)
68
+ */
69
+ static updateExisting(layout: KLineSoALayout, data: KLineData[]): KLineSoALayout;
70
+ /**
71
+ * 将 SoA 布局转换回 KLineData[](用于测试和兼容性)
72
+ * @param layout SoA 布局
73
+ * @returns K线数据数组
74
+ */
75
+ static toKLineData(layout: KLineSoALayout): KLineData[];
76
+ /**
77
+ * 创建子视图(用于 Worker 中处理部分数据范围)
78
+ * @param layout 原 SoA 布局
79
+ * @param start 起始索引(包含)
80
+ * @param end 结束索引(不包含)
81
+ * @returns 子视图对象(共享同一缓冲区)
82
+ */
83
+ static createSubview(layout: KLineSoALayout, start: number, end: number): KLineSoALayout;
84
+ /**
85
+ * 获取缓冲区信息(用于调试和序列化)
86
+ */
87
+ static getBufferInfo(layout: KLineSoALayout): {
88
+ byteLength: number;
89
+ isShared: boolean;
90
+ length: number;
91
+ columns: string[];
92
+ };
93
+ }
94
+ /**
95
+ * 获取 SoA 布局中 closes 列的视图(最常用列)
96
+ * 用于指标计算中快速访问收盘价
97
+ */
98
+ export declare function getClosesView(layout: KLineSoALayout): Float64Array;
99
+ /**
100
+ * 获取 SoA 布局中 highs/lows 列的视图
101
+ * 用于需要高低价的指标(如 BOLL、STOCH、WMSR)
102
+ */
103
+ export declare function getHighsLowsViews(layout: KLineSoALayout): {
104
+ highs: Float64Array;
105
+ lows: Float64Array;
106
+ };
107
+ /**
108
+ * 获取 SoA 布局中 OHLC 四价视图
109
+ */
110
+ export declare function getOHLCViews(layout: KLineSoALayout): {
111
+ opens: Float64Array;
112
+ highs: Float64Array;
113
+ lows: Float64Array;
114
+ closes: Float64Array;
115
+ };
@@ -1,22 +1,36 @@
1
+ import { CandleWebGLSurface, LineWebGLSurface } from './renderers/webgl/candleSurface';
1
2
  export type PaneRendererDom = {
2
3
  mainCanvas: HTMLCanvasElement;
3
4
  overlayCanvas: HTMLCanvasElement;
4
5
  yAxisCanvas: HTMLCanvasElement;
5
6
  };
7
+ export type PaneRendererContexts = {
8
+ mainCtx: CanvasRenderingContext2D | null;
9
+ overlayCtx: CanvasRenderingContext2D | null;
10
+ yAxisCtx: CanvasRenderingContext2D | null;
11
+ };
6
12
  export type PaneRendererOptions = {
7
13
  rightAxisWidth: number;
8
14
  yPaddingPx: number;
9
15
  priceLabelWidth?: number;
10
16
  };
17
+ export type PaneRendererWebGLHandles = {
18
+ candleSurface: CandleWebGLSurface | null;
19
+ lineSurface: LineWebGLSurface | null;
20
+ };
11
21
  export declare class PaneRenderer {
12
22
  private dom;
13
23
  private pane;
14
24
  private opt;
25
+ private contexts;
26
+ private webgl;
15
27
  constructor(dom: PaneRendererDom, pane: import('./layout/pane').Pane, opt: PaneRendererOptions);
16
28
  /** 获取关联的 Pane 实例 */
17
29
  getPane(): import('./layout/pane').Pane;
18
30
  /** 获取 DOM 元素 */
19
31
  getDom(): PaneRendererDom;
32
+ getContexts(): PaneRendererContexts;
33
+ getWebGL(): PaneRendererWebGLHandles;
20
34
  /**
21
35
  * 调整 Canvas 尺寸
22
36
  * @param width pane 宽度(逻辑像素)
@@ -1,11 +1,2 @@
1
1
  import { RendererPluginWithHost } from '../../../plugin';
2
- /**
3
- * 创建 BOLL(布林带)渲染器插件(无状态版本)
4
- *
5
- * 设计原则:
6
- * 1. 不持有任何计算缓存或配置状态
7
- * 2. 所有数据从 StateStore 读取(通过 BOLL_STATE_KEY)
8
- * 3. 配置变更通过外部 IndicatorScheduler 处理
9
- * 4. 纯绘制函数,无副作用
10
- */
11
2
  export declare function createBOLLRendererPlugin(): RendererPluginWithHost;
@@ -1,11 +1,2 @@
1
1
  import { RendererPluginWithHost } from '../../../plugin';
2
- /**
3
- * 创建 EXPMA(指数平滑移动平均线)渲染器插件(无状态版本)
4
- *
5
- * 设计原则:
6
- * 1. 不持有任何计算缓存或配置状态
7
- * 2. 所有数据从 StateStore 读取(通过 EXPMA_STATE_KEY)
8
- * 3. 配置变更通过外部 IndicatorScheduler 处理
9
- * 4. 纯绘制函数,无副作用
10
- */
11
2
  export declare function createEXPMARendererPlugin(): RendererPluginWithHost;