@tradecanvas/chart 1.3.0 → 1.4.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/LICENSE +21 -21
- package/README.md +858 -828
- package/dist/Chart.d.ts +124 -1
- package/dist/Chart.d.ts.map +1 -1
- package/dist/DragDropImporter-C5kgfbET.cjs +2 -0
- package/dist/DragDropImporter-C5kgfbET.cjs.map +1 -0
- package/dist/{DragDropImporter-cURSpfUb.js → DragDropImporter-Dd9Mud5J.js} +376 -160
- package/dist/DragDropImporter-Dd9Mud5J.js.map +1 -0
- package/dist/PaneManager.d.ts.map +1 -1
- package/dist/charts/ChartTypeStrategy.d.ts +6 -0
- package/dist/charts/ChartTypeStrategy.d.ts.map +1 -1
- package/dist/grid/ChartGrid.d.ts +1 -0
- package/dist/grid/ChartGrid.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +70 -85
- package/dist/index.js.map +1 -1
- package/dist/widget/ChartWidget.d.ts +71 -7
- package/dist/widget/ChartWidget.d.ts.map +1 -1
- package/dist/widget/WidgetCommandPalette.d.ts +3 -1
- package/dist/widget/WidgetCommandPalette.d.ts.map +1 -1
- package/dist/widget/WidgetDrawingSidebar.d.ts +12 -0
- package/dist/widget/WidgetDrawingSidebar.d.ts.map +1 -1
- package/dist/widget/WidgetGoToDate.d.ts +45 -0
- package/dist/widget/WidgetGoToDate.d.ts.map +1 -0
- package/dist/widget/WidgetHotkeySheet.d.ts +3 -1
- package/dist/widget/WidgetHotkeySheet.d.ts.map +1 -1
- package/dist/widget/WidgetIndicatorLegend.d.ts +68 -0
- package/dist/widget/WidgetIndicatorLegend.d.ts.map +1 -0
- package/dist/widget/WidgetLoadingOverlay.d.ts.map +1 -1
- package/dist/widget/WidgetReplayBar.d.ts +37 -7
- package/dist/widget/WidgetReplayBar.d.ts.map +1 -1
- package/dist/widget/WidgetSettings.d.ts +13 -1
- package/dist/widget/WidgetSettings.d.ts.map +1 -1
- package/dist/widget/WidgetStatusBar.d.ts +16 -1
- package/dist/widget/WidgetStatusBar.d.ts.map +1 -1
- package/dist/widget/WidgetSymbolSearch.d.ts +3 -1
- package/dist/widget/WidgetSymbolSearch.d.ts.map +1 -1
- package/dist/widget/WidgetToolbar.d.ts +12 -3
- package/dist/widget/WidgetToolbar.d.ts.map +1 -1
- package/dist/widget/WidgetTooltip.d.ts +43 -0
- package/dist/widget/WidgetTooltip.d.ts.map +1 -0
- package/dist/widget/escapeHtml.d.ts +3 -0
- package/dist/widget/escapeHtml.d.ts.map +1 -0
- package/dist/widget/i18n.d.ts +26 -0
- package/dist/widget/i18n.d.ts.map +1 -1
- package/dist/widget/iconSet.d.ts +26 -0
- package/dist/widget/iconSet.d.ts.map +1 -0
- package/dist/widget/icons.d.ts +5 -0
- package/dist/widget/icons.d.ts.map +1 -1
- package/dist/widget/index.cjs +3035 -2561
- package/dist/widget/index.cjs.map +1 -1
- package/dist/widget/index.d.ts +3 -0
- package/dist/widget/index.d.ts.map +1 -1
- package/dist/widget/index.js +1546 -585
- package/dist/widget/index.js.map +1 -1
- package/dist/widget/legendValues.d.ts +14 -0
- package/dist/widget/legendValues.d.ts.map +1 -0
- package/dist/widget/types.d.ts +34 -8
- package/dist/widget/types.d.ts.map +1 -1
- package/dist/widget/widgetConfig.d.ts +1 -5
- package/dist/widget/widgetConfig.d.ts.map +1 -1
- package/dist/widget/widgetTimeframes.d.ts +20 -0
- package/dist/widget/widgetTimeframes.d.ts.map +1 -0
- package/package.json +3 -3
- package/dist/DragDropImporter-BazeSQPD.cjs +0 -2
- package/dist/DragDropImporter-BazeSQPD.cjs.map +0 -1
- package/dist/DragDropImporter-cURSpfUb.js.map +0 -1
package/dist/widget/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { r as e, t } from "../DragDropImporter-
|
|
2
|
-
import { DARK_THEME as n, LIGHT_THEME as r,
|
|
1
|
+
import { r as e, t } from "../DragDropImporter-Dd9Mud5J.js";
|
|
2
|
+
import { DARK_THEME as n, LIGHT_THEME as r, formatPrice as i, timeframeBucketStart as a, timeframeToMs as o } from "@tradecanvas/commons";
|
|
3
|
+
import { RANGE_PRESETS as s } from "@tradecanvas/core";
|
|
3
4
|
//#region src/io/resampleOHLCV.ts
|
|
4
|
-
function
|
|
5
|
+
function c(e, t, n = {}) {
|
|
5
6
|
if (e.length === 0) return [];
|
|
6
|
-
let r = n.weekStartsOn ?? 1,
|
|
7
|
+
let r = n.weekStartsOn ?? 1, i = [], o = null, s = NaN;
|
|
7
8
|
for (let n of e) {
|
|
8
|
-
let e =
|
|
9
|
-
o === null || e !== s ? (o !== null &&
|
|
9
|
+
let e = a(n.time, t, r);
|
|
10
|
+
o === null || e !== s ? (o !== null && i.push(o), o = {
|
|
10
11
|
time: e,
|
|
11
12
|
open: n.open,
|
|
12
13
|
high: n.high,
|
|
@@ -22,9 +23,9 @@ function o(e, t, n = {}) {
|
|
|
22
23
|
volume: o.volume + n.volume
|
|
23
24
|
};
|
|
24
25
|
}
|
|
25
|
-
return o !== null &&
|
|
26
|
+
return o !== null && i.push(o), i;
|
|
26
27
|
}
|
|
27
|
-
function
|
|
28
|
+
function l(e) {
|
|
28
29
|
if (e.length < 2) return 0;
|
|
29
30
|
let t = [];
|
|
30
31
|
for (let n = 1; n < e.length; n++) {
|
|
@@ -35,32 +36,7 @@ function s(e) {
|
|
|
35
36
|
}
|
|
36
37
|
//#endregion
|
|
37
38
|
//#region src/widget/widgetConfig.ts
|
|
38
|
-
var
|
|
39
|
-
{
|
|
40
|
-
label: "1m",
|
|
41
|
-
value: "1m"
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
label: "5m",
|
|
45
|
-
value: "5m"
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
label: "15m",
|
|
49
|
-
value: "15m"
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
label: "1H",
|
|
53
|
-
value: "1h"
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
label: "4H",
|
|
57
|
-
value: "4h"
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
label: "1D",
|
|
61
|
-
value: "1d"
|
|
62
|
-
}
|
|
63
|
-
], l = [
|
|
39
|
+
var u = [
|
|
64
40
|
{
|
|
65
41
|
label: "Candlestick",
|
|
66
42
|
value: "candlestick"
|
|
@@ -109,7 +85,7 @@ var c = [
|
|
|
109
85
|
label: "Line + Markers",
|
|
110
86
|
value: "lineWithMarkers"
|
|
111
87
|
}
|
|
112
|
-
],
|
|
88
|
+
], d = [
|
|
113
89
|
{
|
|
114
90
|
label: "Lines",
|
|
115
91
|
tools: [
|
|
@@ -317,7 +293,7 @@ var c = [
|
|
|
317
293
|
}
|
|
318
294
|
]
|
|
319
295
|
}
|
|
320
|
-
],
|
|
296
|
+
], f = [
|
|
321
297
|
{
|
|
322
298
|
id: "sma",
|
|
323
299
|
name: "SMA",
|
|
@@ -668,7 +644,7 @@ var c = [
|
|
|
668
644
|
name: "Volume Profile",
|
|
669
645
|
type: "panel"
|
|
670
646
|
}
|
|
671
|
-
],
|
|
647
|
+
], ee = [
|
|
672
648
|
"sma",
|
|
673
649
|
"ema",
|
|
674
650
|
"bb",
|
|
@@ -679,7 +655,7 @@ var c = [
|
|
|
679
655
|
"atr",
|
|
680
656
|
"obv",
|
|
681
657
|
"ichimoku"
|
|
682
|
-
],
|
|
658
|
+
], te = [
|
|
683
659
|
"BTCUSDT",
|
|
684
660
|
"ETHUSDT",
|
|
685
661
|
"SOLUSDT",
|
|
@@ -712,68 +688,364 @@ var c = [
|
|
|
712
688
|
logScale: !1,
|
|
713
689
|
scaleMode: "regular",
|
|
714
690
|
autoScale: !0,
|
|
691
|
+
invertScale: !1,
|
|
715
692
|
crosshairMode: "magnet",
|
|
716
693
|
numberLocale: "en-US",
|
|
717
694
|
timezone: "local"
|
|
718
|
-
}, te = "/* === TradeCanvas Widget — Premium Styles === */\r\n\r\n/*\r\n * The HTML `hidden` attribute only sets `display: none` via the UA stylesheet,\r\n * which any author `display` rule (e.g. `.tcw-alerts-panel { display: flex }`)\r\n * silently overrides — leaving panels stuck open when JS sets `el.hidden = true`.\r\n * Force `hidden` to win for every namespaced widget element. Scoped to `tcw-`\r\n * so it never touches the host page.\r\n */\r\n[class^=\"tcw-\"][hidden],\r\n[class*=\" tcw-\"][hidden] {\r\n display: none !important;\r\n}\r\n\r\n.tcw-root {\r\n display: flex;\r\n flex-direction: column;\r\n width: 100%;\r\n height: 100%;\r\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif;\r\n font-feature-settings: 'cv11', 'ss01', 'ss03';\r\n font-size: 13px;\r\n line-height: 1.4;\r\n color: #d9dde4;\r\n background: #080b10;\r\n position: relative;\r\n overflow: hidden;\r\n -webkit-font-smoothing: antialiased;\r\n -moz-osx-font-smoothing: grayscale;\r\n\r\n /* --- Dark theme tokens (default) --- */\r\n --tcw-bg: #080b10;\r\n --tcw-bg-surface: #0c1016;\r\n --tcw-bg-elevated: #141922;\r\n --tcw-bg-overlay: rgba(20, 25, 34, 0.5);\r\n --tcw-border: #1f2630;\r\n --tcw-border-strong: #2a323e;\r\n --tcw-text: #e7e9ee;\r\n --tcw-text-dim: #aab1bd;\r\n --tcw-text-muted: #758091;\r\n\r\n /* TradeCanvas amber. --tcw-accent-ink is the text colour on an accent fill. */\r\n --tcw-accent: #f2a93b;\r\n --tcw-accent-hover: #f5b95c;\r\n --tcw-accent-ink: #1a1204;\r\n --tcw-accent-soft: rgba(242, 169, 59, 0.14);\r\n --tcw-accent-glow: rgba(242, 169, 59, 0.22);\r\n --tcw-accent-line: rgba(242, 169, 59, 0.55);\r\n\r\n --tcw-red: #e8505b;\r\n --tcw-red-soft: rgba(232, 80, 91, 0.12);\r\n --tcw-green: #1fa874;\r\n --tcw-green-soft: rgba(31, 168, 116, 0.12);\r\n --tcw-amber: #ff9f43;\r\n\r\n --tcw-hover-bg: rgba(255, 255, 255, 0.05);\r\n --tcw-active-bg: rgba(255, 255, 255, 0.08);\r\n --tcw-divider: rgba(255, 255, 255, 0.06);\r\n\r\n /* Motion */\r\n --tcw-ease: cubic-bezier(0.2, 0, 0, 1);\r\n --tcw-ease-out: cubic-bezier(0.16, 1, 0.3, 1);\r\n --tcw-dur-fast: 120ms;\r\n --tcw-dur-normal: 180ms;\r\n --tcw-dur-slow: 260ms;\r\n\r\n /* Shape */\r\n --tcw-radius-sm: 4px;\r\n --tcw-radius: 6px;\r\n --tcw-radius-lg: 10px;\r\n --tcw-radius-xl: 14px;\r\n\r\n /* Elevation */\r\n --tcw-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);\r\n --tcw-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35), 0 1px 3px rgba(0, 0, 0, 0.2);\r\n --tcw-shadow-lg: 0 12px 36px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.3);\r\n --tcw-shadow-xl: 0 24px 64px rgba(0, 0, 0, 0.55), 0 12px 24px rgba(0, 0, 0, 0.35);\r\n --tcw-ring: 0 0 0 2px rgba(242, 169, 59, 0.45);\r\n\r\n /* Type */\r\n --tcw-font-mono: 'JetBrains Mono', 'SF Mono', 'Cascadia Code', 'Fira Code', 'Consolas', ui-monospace, monospace;\r\n}\r\n\r\n.tcw-root[data-tcw-theme='light'] {\r\n color: #0c1016;\r\n background: #fcfcfd;\r\n\r\n --tcw-bg: #fcfcfd;\r\n --tcw-bg-surface: #ffffff;\r\n --tcw-bg-elevated: #ffffff;\r\n --tcw-bg-overlay: rgba(255, 255, 255, 0.75);\r\n --tcw-border: #e3e6eb;\r\n --tcw-border-strong: #d2d6dd;\r\n --tcw-text: #0c1016;\r\n --tcw-text-dim: #3f4754;\r\n --tcw-text-muted: #626a77;\r\n\r\n /* Deeper amber: readable as text on the light ground, white text readable on it. */\r\n --tcw-accent: #a0610a;\r\n --tcw-accent-hover: #874f07;\r\n --tcw-accent-ink: #ffffff;\r\n --tcw-accent-soft: rgba(160, 97, 10, 0.10);\r\n --tcw-accent-glow: rgba(160, 97, 10, 0.16);\r\n --tcw-accent-line: rgba(160, 97, 10, 0.45);\r\n --tcw-ring: 0 0 0 2px rgba(160, 97, 10, 0.4);\r\n\r\n /* Darker semantic colours: used as text on white. */\r\n --tcw-red: #c42f3e;\r\n --tcw-red-soft: rgba(196, 47, 62, 0.10);\r\n --tcw-green: #0b7d50;\r\n --tcw-green-soft: rgba(11, 125, 80, 0.10);\r\n\r\n --tcw-hover-bg: rgba(0, 0, 0, 0.04);\r\n --tcw-active-bg: rgba(160, 97, 10, 0.08);\r\n --tcw-divider: rgba(0, 0, 0, 0.06);\r\n\r\n --tcw-shadow-sm: 0 1px 2px rgba(20, 28, 56, 0.05);\r\n --tcw-shadow-md: 0 4px 12px rgba(20, 28, 56, 0.08), 0 1px 3px rgba(20, 28, 56, 0.04);\r\n --tcw-shadow-lg: 0 12px 36px rgba(20, 28, 56, 0.14), 0 4px 12px rgba(20, 28, 56, 0.08);\r\n --tcw-shadow-xl: 0 24px 64px rgba(20, 28, 56, 0.18), 0 12px 24px rgba(20, 28, 56, 0.1);\r\n}\r\n\r\n/* === Toolbar === */\r\n\r\n.tcw-toolbar {\r\n display: flex;\r\n align-items: center;\r\n gap: 0;\r\n padding: 0 10px;\r\n height: 44px;\r\n border-bottom: 1px solid var(--tcw-border);\r\n background:\r\n linear-gradient(180deg, var(--tcw-bg-elevated) 0%, var(--tcw-bg-surface) 100%);\r\n flex-shrink: 0;\r\n position: relative;\r\n z-index: 20;\r\n box-shadow: var(--tcw-shadow-sm);\r\n}\r\n\r\n.tcw-toolbar-symbol {\r\n font-size: 14px;\r\n font-weight: 650;\r\n color: var(--tcw-text);\r\n padding: 6px 12px;\r\n white-space: nowrap;\r\n letter-spacing: -0.015em;\r\n cursor: pointer;\r\n border: none;\r\n background: transparent;\r\n font-family: inherit;\r\n border-radius: var(--tcw-radius);\r\n transition: background var(--tcw-dur-fast) var(--tcw-ease);\r\n}\r\n\r\n.tcw-toolbar-symbol:hover {\r\n background: var(--tcw-hover-bg);\r\n}\r\n\r\n.tcw-toolbar-sep {\r\n width: 1px;\r\n height: 22px;\r\n background: var(--tcw-border);\r\n flex-shrink: 0;\r\n margin: 0 4px;\r\n}\r\n\r\n.tcw-toolbar-group {\r\n display: flex;\r\n align-items: center;\r\n gap: 2px;\r\n padding: 0 4px;\r\n}\r\n\r\n.tcw-toolbar-spacer {\r\n flex: 1;\r\n}\r\n\r\n.tcw-btn {\r\n padding: 6px 10px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n font-family: inherit;\r\n border: none;\r\n border-radius: var(--tcw-radius);\r\n background: transparent;\r\n color: var(--tcw-text-dim);\r\n cursor: pointer;\r\n transition:\r\n background var(--tcw-dur-fast) var(--tcw-ease),\r\n color var(--tcw-dur-fast) var(--tcw-ease),\r\n transform var(--tcw-dur-fast) var(--tcw-ease);\r\n white-space: nowrap;\r\n line-height: 1;\r\n letter-spacing: -0.005em;\r\n}\r\n\r\n.tcw-btn:hover {\r\n background: var(--tcw-hover-bg);\r\n color: var(--tcw-text);\r\n}\r\n\r\n.tcw-btn:active {\r\n transform: translateY(0.5px);\r\n}\r\n\r\n.tcw-btn.tcw-active {\r\n background: var(--tcw-accent-soft);\r\n color: var(--tcw-accent);\r\n font-weight: 600;\r\n box-shadow: inset 0 0 0 1px var(--tcw-accent-line);\r\n}\r\n\r\n.tcw-btn:focus-visible,\r\n.tcw-btn-icon:focus-visible,\r\n.tcw-sidebar-btn:focus-visible,\r\n.tcw-dropdown-trigger:focus-visible {\r\n outline: none;\r\n box-shadow: var(--tcw-ring);\r\n}\r\n\r\n.tcw-btn-icon {\r\n width: 32px;\r\n height: 32px;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-family: inherit;\r\n border: none;\r\n border-radius: var(--tcw-radius);\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n cursor: pointer;\r\n transition:\r\n background var(--tcw-dur-fast) var(--tcw-ease),\r\n color var(--tcw-dur-fast) var(--tcw-ease);\r\n line-height: 1;\r\n position: relative;\r\n}\r\n\r\n.tcw-btn-icon:hover {\r\n background: var(--tcw-hover-bg);\r\n color: var(--tcw-text);\r\n}\r\n\r\n.tcw-btn-icon svg {\r\n display: block;\r\n}\r\n\r\n.tcw-badge-count {\r\n font-size: 10px;\r\n font-weight: 600;\r\n color: var(--tcw-accent);\r\n background: var(--tcw-accent-soft);\r\n padding: 1px 5px;\r\n border-radius: 9px;\r\n font-family: var(--tcw-font-mono);\r\n}\r\n\r\n.tcw-indicator-chips {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 0 4px;\r\n flex-wrap: nowrap;\r\n overflow: hidden;\r\n}\r\n\r\n.tcw-indicator-chip {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 3px 6px 3px 9px;\r\n font-size: 11px;\r\n font-weight: 500;\r\n background: var(--tcw-accent-soft);\r\n color: var(--tcw-accent);\r\n border-radius: 9999px;\r\n white-space: nowrap;\r\n line-height: 1.4;\r\n letter-spacing: -0.005em;\r\n border: 1px solid transparent;\r\n transition: border-color var(--tcw-dur-fast) var(--tcw-ease);\r\n}\r\n\r\n.tcw-indicator-chip:hover {\r\n border-color: var(--tcw-accent-line);\r\n}\r\n\r\n.tcw-chip-remove {\r\n cursor: pointer;\r\n opacity: 0.55;\r\n padding: 0 2px;\r\n transition: opacity var(--tcw-dur-fast) var(--tcw-ease);\r\n border: none;\r\n background: transparent;\r\n color: inherit;\r\n display: inline-flex;\r\n align-items: center;\r\n}\r\n\r\n.tcw-chip-remove:hover {\r\n opacity: 1;\r\n}\r\n\r\n/* === Dropdown === */\r\n\r\n.tcw-dropdown-trigger {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 6px 10px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n font-family: inherit;\r\n border: none;\r\n border-radius: var(--tcw-radius);\r\n background: transparent;\r\n color: var(--tcw-text-dim);\r\n cursor: pointer;\r\n transition: background var(--tcw-dur-fast) var(--tcw-ease), color var(--tcw-dur-fast) var(--tcw-ease);\r\n white-space: nowrap;\r\n line-height: 1;\r\n}\r\n\r\n.tcw-dropdown-trigger:hover {\r\n background: var(--tcw-hover-bg);\r\n color: var(--tcw-text);\r\n}\r\n\r\n.tcw-dropdown {\r\n position: absolute;\r\n top: 100%;\r\n margin-top: 6px;\r\n background: var(--tcw-bg-elevated);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: var(--tcw-radius-lg);\r\n box-shadow: var(--tcw-shadow-lg);\r\n z-index: 50;\r\n padding: 6px;\r\n max-height: 420px;\r\n overflow-y: auto;\r\n backdrop-filter: blur(12px);\r\n -webkit-backdrop-filter: blur(12px);\r\n animation: tcw-pop-in var(--tcw-dur-normal) var(--tcw-ease-out);\r\n}\r\n\r\n@keyframes tcw-pop-in {\r\n from { opacity: 0; transform: translateY(-4px) scale(0.98); }\r\n to { opacity: 1; transform: translateY(0) scale(1); }\r\n}\r\n\r\n.tcw-dropdown-item {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 7px 10px;\r\n font-size: 12.5px;\r\n color: var(--tcw-text-dim);\r\n cursor: pointer;\r\n transition: background var(--tcw-dur-fast) var(--tcw-ease), color var(--tcw-dur-fast) var(--tcw-ease);\r\n width: 100%;\r\n border: none;\r\n background: transparent;\r\n font-family: inherit;\r\n text-align: left;\r\n border-radius: var(--tcw-radius-sm);\r\n gap: 12px;\r\n}\r\n\r\n.tcw-dropdown-item:hover {\r\n background: var(--tcw-hover-bg);\r\n color: var(--tcw-text);\r\n}\r\n\r\n.tcw-dropdown-item.tcw-active {\r\n color: var(--tcw-accent);\r\n background: var(--tcw-accent-soft);\r\n font-weight: 600;\r\n}\r\n\r\n.tcw-dropdown-label {\r\n text-transform: uppercase;\r\n font-size: 10px;\r\n color: var(--tcw-text-muted);\r\n letter-spacing: 0.08em;\r\n padding: 8px 10px 4px;\r\n font-weight: 600;\r\n}\r\n\r\n.tcw-dropdown-divider {\r\n border-top: 1px solid var(--tcw-divider);\r\n margin: 6px 4px;\r\n}\r\n\r\n.tcw-tag {\r\n font-size: 10px;\r\n font-family: var(--tcw-font-mono);\r\n color: var(--tcw-text-muted);\r\n padding: 2px 6px;\r\n border-radius: 4px;\r\n background: var(--tcw-hover-bg);\r\n letter-spacing: 0.02em;\r\n}\r\n\r\n/* === Body === */\r\n\r\n.tcw-body {\r\n display: flex;\r\n flex: 1;\r\n min-height: 0;\r\n background: var(--tcw-bg);\r\n}\r\n\r\n.tcw-chart-container {\r\n flex: 1;\r\n min-width: 0;\r\n min-height: 0;\r\n width: 100%;\r\n height: 100%;\r\n position: relative;\r\n}\r\n\r\n/* === Loading overlay (candle skeleton) === */\r\n\r\n.tcw-loading-overlay {\r\n /* Uses a modifier class rather than the `hidden` attribute — the global\r\n `[hidden] { display: none !important }` rule above would cut the\r\n opacity transition off before it can play. */\r\n position: absolute;\r\n inset: 0;\r\n z-index: 5;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: var(--tcw-bg);\r\n opacity: 1;\r\n transition: opacity var(--tcw-dur-slow) var(--tcw-ease-out);\r\n pointer-events: none;\r\n}\r\n\r\n.tcw-loading-overlay--hidden {\r\n opacity: 0;\r\n}\r\n\r\n.tcw-loading-card {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n gap: 14px;\r\n padding: 22px 26px 20px;\r\n border: 1px solid transparent;\r\n border-radius: var(--tcw-radius-lg);\r\n}\r\n\r\n/* A slow switch: the previous chart stays readable underneath, dimmed, so the\r\n click visibly registered without blanking the screen. */\r\n.tcw-loading-overlay--veil {\r\n background: color-mix(in srgb, var(--tcw-bg) 55%, transparent);\r\n}\r\n\r\n.tcw-loading-overlay--veil .tcw-loading-card {\r\n background: var(--tcw-bg-elevated);\r\n border-color: var(--tcw-border);\r\n box-shadow: var(--tcw-shadow-lg);\r\n}\r\n\r\n.tcw-loading-candles {\r\n position: relative;\r\n display: flex;\r\n gap: 7px;\r\n height: 64px;\r\n}\r\n\r\n/* One candle: the element is the wick's column, ::before the wick, ::after the body. */\r\n.tcw-loading-candle {\r\n position: relative;\r\n width: 8px;\r\n height: 100%;\r\n color: var(--tcw-red);\r\n opacity: 0.28;\r\n animation: tcw-candle-light 1.6s var(--tcw-ease) infinite;\r\n}\r\n\r\n.tcw-loading-candle--up { color: var(--tcw-green); }\r\n\r\n.tcw-loading-candle::before,\r\n.tcw-loading-candle::after {\r\n content: '';\r\n position: absolute;\r\n left: 50%;\r\n background: currentColor;\r\n transform: translateX(-50%);\r\n}\r\n\r\n.tcw-loading-candle::before {\r\n top: var(--tcw-wick-top);\r\n width: 1.5px;\r\n height: var(--tcw-wick-h);\r\n}\r\n\r\n.tcw-loading-candle::after {\r\n top: var(--tcw-body-top);\r\n width: 8px;\r\n height: var(--tcw-body-h);\r\n border-radius: 1.5px;\r\n}\r\n\r\n/* Each candle brightens and lifts slightly in turn, left to right. */\r\n@keyframes tcw-candle-light {\r\n 0%, 55%, 100% { opacity: 0.28; transform: translateY(0); }\r\n 20% { opacity: 1; transform: translateY(-2px); }\r\n}\r\n\r\n.tcw-loading-track {\r\n position: relative;\r\n width: 100%;\r\n min-width: 88px;\r\n height: 2px;\r\n border-radius: 1px;\r\n background: var(--tcw-border);\r\n overflow: hidden;\r\n}\r\n\r\n.tcw-loading-track > span {\r\n position: absolute;\r\n inset: 0;\r\n width: 40%;\r\n border-radius: 1px;\r\n background: linear-gradient(90deg, transparent, var(--tcw-accent), transparent);\r\n animation: tcw-loading-sweep 1.6s var(--tcw-ease) infinite;\r\n}\r\n\r\n@keyframes tcw-loading-sweep {\r\n from { transform: translateX(-100%); }\r\n to { transform: translateX(250%); }\r\n}\r\n\r\n/* The status text is announced, not shown — the candles say \"loading\". */\r\n.tcw-loading-label {\r\n position: absolute;\r\n width: 1px;\r\n height: 1px;\r\n overflow: hidden;\r\n clip-path: inset(50%);\r\n white-space: nowrap;\r\n}\r\n\r\n.tcw-loading-overlay--error .tcw-loading-candles,\r\n.tcw-loading-overlay--error .tcw-loading-track {\r\n display: none;\r\n}\r\n\r\n/* A failure is shown: why the chart is empty and that a retry is running. */\r\n.tcw-loading-overlay--error .tcw-loading-label {\r\n position: static;\r\n width: auto;\r\n height: auto;\r\n overflow: visible;\r\n clip-path: none;\r\n white-space: normal;\r\n max-width: 320px;\r\n text-align: center;\r\n color: var(--tcw-red);\r\n font-size: 13px;\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .tcw-loading-candle,\r\n .tcw-loading-track > span {\r\n animation: none;\r\n }\r\n .tcw-loading-candle { opacity: 0.6; }\r\n .tcw-loading-track > span { transform: translateX(75%); }\r\n /* Static candles don't say \"loading\" on their own: show the text. */\r\n .tcw-loading-label {\r\n position: static;\r\n width: auto;\r\n height: auto;\r\n overflow: visible;\r\n clip-path: none;\r\n font-size: 12px;\r\n color: var(--tcw-text-muted);\r\n }\r\n}\r\n\r\n/* === Sidebar === */\r\n\r\n.tcw-sidebar {\r\n width: 40px;\r\n flex-shrink: 0;\r\n background: linear-gradient(180deg, var(--tcw-bg-elevated) 0%, var(--tcw-bg-surface) 100%);\r\n border-right: 1px solid var(--tcw-border);\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n padding: 6px 0;\r\n overflow: visible;\r\n box-shadow: var(--tcw-shadow-sm);\r\n}\r\n\r\n.tcw-tool-group-wrap {\r\n position: relative;\r\n}\r\n\r\n.tcw-sidebar-btn {\r\n width: 32px;\r\n height: 32px;\r\n margin: 1px 0;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n border: none;\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n cursor: pointer;\r\n font-family: inherit;\r\n transition:\r\n background var(--tcw-dur-fast) var(--tcw-ease),\r\n color var(--tcw-dur-fast) var(--tcw-ease);\r\n position: relative;\r\n flex-shrink: 0;\r\n line-height: 1;\r\n border-radius: var(--tcw-radius);\r\n}\r\n\r\n.tcw-sidebar-btn:hover {\r\n color: var(--tcw-text);\r\n background: var(--tcw-hover-bg);\r\n}\r\n\r\n.tcw-sidebar-btn.tcw-active {\r\n color: var(--tcw-accent);\r\n background: var(--tcw-accent-soft);\r\n box-shadow: inset 2px 0 0 var(--tcw-accent);\r\n}\r\n\r\n.tcw-sidebar-btn.tcw-danger:hover {\r\n color: var(--tcw-red);\r\n background: var(--tcw-red-soft);\r\n}\r\n\r\n.tcw-multi-dot {\r\n position: absolute;\r\n bottom: 3px;\r\n right: 3px;\r\n width: 4px;\r\n height: 4px;\r\n border-radius: 50%;\r\n background: var(--tcw-text-muted);\r\n opacity: 0.7;\r\n}\r\n\r\n.tcw-sidebar-divider {\r\n width: 22px;\r\n height: 1px;\r\n background: var(--tcw-divider);\r\n margin: 6px 0;\r\n flex-shrink: 0;\r\n}\r\n\r\n.tcw-sidebar-spacer {\r\n flex: 1;\r\n}\r\n\r\n/* === Flyout === */\r\n\r\n.tcw-flyout {\r\n position: absolute;\r\n left: 100%;\r\n top: 0;\r\n margin-left: 6px;\r\n min-width: 180px;\r\n background: var(--tcw-bg-elevated);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: var(--tcw-radius-lg);\r\n box-shadow: var(--tcw-shadow-lg);\r\n z-index: 30;\r\n padding: 6px;\r\n backdrop-filter: blur(12px);\r\n -webkit-backdrop-filter: blur(12px);\r\n animation: tcw-slide-right var(--tcw-dur-normal) var(--tcw-ease-out);\r\n}\r\n\r\n@keyframes tcw-slide-right {\r\n from { opacity: 0; transform: translateX(-4px); }\r\n to { opacity: 1; transform: translateX(0); }\r\n}\r\n\r\n.tcw-flyout-header {\r\n text-transform: uppercase;\r\n font-size: 10px;\r\n color: var(--tcw-text-muted);\r\n letter-spacing: 0.08em;\r\n padding: 6px 10px 4px;\r\n font-weight: 600;\r\n}\r\n\r\n.tcw-flyout-item {\r\n padding: 7px 10px;\r\n font-size: 12.5px;\r\n color: var(--tcw-text-dim);\r\n cursor: pointer;\r\n width: 100%;\r\n display: block;\r\n border: none;\r\n background: transparent;\r\n text-align: left;\r\n font-family: inherit;\r\n border-radius: var(--tcw-radius-sm);\r\n transition: background var(--tcw-dur-fast) var(--tcw-ease), color var(--tcw-dur-fast) var(--tcw-ease);\r\n}\r\n\r\n.tcw-flyout-item:hover {\r\n background: var(--tcw-hover-bg);\r\n color: var(--tcw-text);\r\n}\r\n\r\n.tcw-flyout-item.tcw-active {\r\n color: var(--tcw-accent);\r\n background: var(--tcw-accent-soft);\r\n font-weight: 600;\r\n}\r\n\r\n/* Pinned (favorited) flyout item — a star marker on the right. */\r\n.tcw-flyout-item.tcw-flyout-faved::after {\r\n content: '★';\r\n float: right;\r\n color: var(--tcw-accent);\r\n font-size: 11px;\r\n line-height: 1.4;\r\n}\r\n\r\n/* Favorites strip — pinned drawing tools above the tool groups. */\r\n.tcw-sidebar-favorites {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 2px;\r\n}\r\n.tcw-sidebar-favorites .tcw-sidebar-btn {\r\n position: relative;\r\n}\r\n.tcw-sidebar-favorites .tcw-sidebar-btn::before {\r\n content: '';\r\n position: absolute;\r\n top: 3px;\r\n right: 3px;\r\n width: 4px;\r\n height: 4px;\r\n border-radius: 50%;\r\n background: var(--tcw-accent);\r\n opacity: 0.7;\r\n}\r\n\r\n/* === Modal === */\r\n\r\n.tcw-modal-backdrop {\r\n position: fixed;\r\n inset: 0;\r\n background: rgba(8, 10, 18, 0.55);\r\n backdrop-filter: blur(6px) saturate(140%);\r\n -webkit-backdrop-filter: blur(6px) saturate(140%);\r\n z-index: 100;\r\n animation: tcw-fade-in var(--tcw-dur-normal) var(--tcw-ease-out);\r\n}\r\n\r\n@keyframes tcw-fade-in {\r\n from { opacity: 0; }\r\n to { opacity: 1; }\r\n}\r\n\r\n.tcw-modal {\r\n position: fixed;\r\n z-index: 101;\r\n top: 50%;\r\n left: 50%;\r\n transform: translate(-50%, -50%);\r\n background: var(--tcw-bg-elevated);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: var(--tcw-radius-xl);\r\n width: 520px;\r\n max-width: 92vw;\r\n max-height: 82vh;\r\n display: flex;\r\n flex-direction: column;\r\n box-shadow: var(--tcw-shadow-xl);\r\n animation: tcw-modal-in var(--tcw-dur-slow) var(--tcw-ease-out);\r\n}\r\n\r\n@keyframes tcw-modal-in {\r\n from { opacity: 0; transform: translate(-50%, -48%) scale(0.96); }\r\n to { opacity: 1; transform: translate(-50%, -50%) scale(1); }\r\n}\r\n\r\n.tcw-modal-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 18px 22px;\r\n border-bottom: 1px solid var(--tcw-divider);\r\n}\r\n\r\n.tcw-modal-header h3 {\r\n font-size: 15px;\r\n font-weight: 650;\r\n margin: 0;\r\n color: var(--tcw-text);\r\n letter-spacing: -0.015em;\r\n}\r\n\r\n.tcw-modal-close {\r\n width: 28px;\r\n height: 28px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n border: none;\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n cursor: pointer;\r\n border-radius: var(--tcw-radius);\r\n transition: background var(--tcw-dur-fast) var(--tcw-ease), color var(--tcw-dur-fast) var(--tcw-ease);\r\n}\r\n\r\n.tcw-modal-close:hover {\r\n background: var(--tcw-hover-bg);\r\n color: var(--tcw-text);\r\n}\r\n\r\n.tcw-modal-tabs {\r\n display: flex;\r\n gap: 4px;\r\n padding: 0 18px;\r\n border-bottom: 1px solid var(--tcw-divider);\r\n}\r\n\r\n.tcw-modal-tab {\r\n padding: 11px 14px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n color: var(--tcw-text-muted);\r\n border: none;\r\n background: transparent;\r\n cursor: pointer;\r\n border-bottom: 2px solid transparent;\r\n margin-bottom: -1px;\r\n transition: color var(--tcw-dur-fast) var(--tcw-ease), border-color var(--tcw-dur-fast) var(--tcw-ease);\r\n font-family: inherit;\r\n text-transform: capitalize;\r\n letter-spacing: -0.005em;\r\n}\r\n\r\n.tcw-modal-tab:hover {\r\n color: var(--tcw-text-dim);\r\n}\r\n\r\n.tcw-modal-tab.tcw-active {\r\n color: var(--tcw-accent);\r\n border-bottom-color: var(--tcw-accent);\r\n font-weight: 600;\r\n}\r\n\r\n.tcw-modal-body {\r\n padding: 20px 22px;\r\n overflow-y: auto;\r\n flex: 1;\r\n}\r\n\r\n.tcw-settings-section {\r\n margin-bottom: 22px;\r\n}\r\n\r\n.tcw-settings-section:last-child {\r\n margin-bottom: 0;\r\n}\r\n\r\n.tcw-settings-section-title {\r\n font-size: 10.5px;\r\n font-weight: 600;\r\n text-transform: uppercase;\r\n letter-spacing: 0.09em;\r\n color: var(--tcw-text-muted);\r\n margin-bottom: 12px;\r\n}\r\n\r\n.tcw-settings-row {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 10px 0;\r\n}\r\n\r\n.tcw-settings-row + .tcw-settings-row {\r\n border-top: 1px solid var(--tcw-divider);\r\n}\r\n\r\n.tcw-settings-label {\r\n font-size: 13px;\r\n color: var(--tcw-text-dim);\r\n letter-spacing: -0.005em;\r\n}\r\n\r\n.tcw-color-picker-wrap {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.tcw-color-picker-wrap input[type='color'] {\r\n width: 28px;\r\n height: 28px;\r\n border: 1px solid var(--tcw-border-strong);\r\n border-radius: var(--tcw-radius-sm);\r\n background: transparent;\r\n cursor: pointer;\r\n padding: 2px;\r\n}\r\n\r\n.tcw-color-picker-wrap input[type='color']::-webkit-color-swatch-wrapper {\r\n padding: 0;\r\n}\r\n\r\n.tcw-color-picker-wrap input[type='color']::-webkit-color-swatch {\r\n border: none;\r\n border-radius: 2px;\r\n}\r\n\r\n.tcw-color-hex {\r\n font-size: 11px;\r\n font-family: var(--tcw-font-mono);\r\n color: var(--tcw-text-muted);\r\n min-width: 64px;\r\n letter-spacing: 0.01em;\r\n}\r\n\r\n.tcw-toggle {\r\n width: 36px;\r\n height: 20px;\r\n background: rgba(255, 255, 255, 0.12);\r\n border-radius: 10px;\r\n position: relative;\r\n cursor: pointer;\r\n transition: background var(--tcw-dur-fast) var(--tcw-ease);\r\n border: none;\r\n padding: 0;\r\n flex-shrink: 0;\r\n}\r\n\r\n.tcw-toggle::after {\r\n content: '';\r\n position: absolute;\r\n top: 2px;\r\n left: 2px;\r\n width: 16px;\r\n height: 16px;\r\n border-radius: 50%;\r\n background: var(--tcw-text-muted);\r\n transition:\r\n left var(--tcw-dur-normal) var(--tcw-ease-out),\r\n background var(--tcw-dur-fast) var(--tcw-ease);\r\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\r\n}\r\n\r\n.tcw-toggle.tcw-on {\r\n background: var(--tcw-accent);\r\n box-shadow: 0 0 0 2px var(--tcw-accent-glow);\r\n}\r\n\r\n.tcw-toggle.tcw-on::after {\r\n left: 18px;\r\n background: #fff;\r\n}\r\n\r\n.tcw-root[data-tcw-theme='light'] .tcw-toggle {\r\n background: rgba(0, 0, 0, 0.12);\r\n}\r\n\r\n.tcw-root[data-tcw-theme='light'] .tcw-toggle::after {\r\n background: #999;\r\n}\r\n\r\n.tcw-root[data-tcw-theme='light'] .tcw-toggle.tcw-on {\r\n background: var(--tcw-accent);\r\n}\r\n\r\n.tcw-root[data-tcw-theme='light'] .tcw-toggle.tcw-on::after {\r\n background: #fff;\r\n}\r\n\r\n.tcw-settings-select {\r\n padding: 6px 10px;\r\n font-size: 12px;\r\n font-family: inherit;\r\n background: var(--tcw-bg-surface);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: var(--tcw-radius);\r\n color: var(--tcw-text);\r\n cursor: pointer;\r\n transition: border-color var(--tcw-dur-fast) var(--tcw-ease);\r\n}\r\n\r\n.tcw-settings-select:hover {\r\n border-color: var(--tcw-border-strong);\r\n}\r\n\r\n.tcw-settings-select:focus-visible {\r\n outline: none;\r\n border-color: var(--tcw-accent);\r\n box-shadow: var(--tcw-ring);\r\n}\r\n\r\n.tcw-modal-footer {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 14px 22px;\r\n border-top: 1px solid var(--tcw-divider);\r\n background: var(--tcw-bg-surface);\r\n border-radius: 0 0 var(--tcw-radius-xl) var(--tcw-radius-xl);\r\n}\r\n\r\n.tcw-reset-link {\r\n font-size: 12px;\r\n color: var(--tcw-text-muted);\r\n cursor: pointer;\r\n border: none;\r\n background: transparent;\r\n font-family: inherit;\r\n text-decoration: underline;\r\n text-underline-offset: 2px;\r\n text-decoration-color: var(--tcw-border-strong);\r\n transition: color var(--tcw-dur-fast) var(--tcw-ease), text-decoration-color var(--tcw-dur-fast) var(--tcw-ease);\r\n}\r\n\r\n.tcw-reset-link:hover {\r\n color: var(--tcw-text-dim);\r\n text-decoration-color: currentColor;\r\n}\r\n\r\n.tcw-done-btn {\r\n padding: 8px 18px;\r\n font-size: 12.5px;\r\n font-weight: 600;\r\n background: var(--tcw-accent);\r\n color: var(--tcw-accent-ink);\r\n border: none;\r\n border-radius: var(--tcw-radius);\r\n cursor: pointer;\r\n font-family: inherit;\r\n transition:\r\n background var(--tcw-dur-fast) var(--tcw-ease),\r\n transform var(--tcw-dur-fast) var(--tcw-ease),\r\n box-shadow var(--tcw-dur-fast) var(--tcw-ease);\r\n letter-spacing: -0.005em;\r\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.15);\r\n}\r\n\r\n.tcw-done-btn:hover {\r\n background: var(--tcw-accent-hover);\r\n box-shadow: var(--tcw-shadow-md), 0 0 0 3px var(--tcw-accent-glow);\r\n}\r\n\r\n.tcw-done-btn:active {\r\n transform: translateY(0.5px);\r\n}\r\n\r\n/* === Status Bar === */\r\n\r\n.tcw-statusbar {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 7px 16px;\r\n border-top: 1px solid var(--tcw-border);\r\n background: var(--tcw-bg-surface);\r\n font-size: 11px;\r\n color: var(--tcw-text-muted);\r\n flex-shrink: 0;\r\n font-feature-settings: 'tnum';\r\n}\r\n\r\n.tcw-status-indicator {\r\n display: flex;\r\n align-items: center;\r\n gap: 7px;\r\n font-weight: 500;\r\n}\r\n\r\n.tcw-status-dot {\r\n width: 7px;\r\n height: 7px;\r\n border-radius: 50%;\r\n background: var(--tcw-text-muted);\r\n transition: background var(--tcw-dur-normal) var(--tcw-ease), box-shadow var(--tcw-dur-normal) var(--tcw-ease);\r\n position: relative;\r\n}\r\n\r\n.tcw-status-dot.tcw-connected {\r\n background: var(--tcw-green);\r\n box-shadow: 0 0 0 3px rgba(31, 168, 116, 0.18);\r\n}\r\n\r\n.tcw-status-dot.tcw-connected::after {\r\n content: '';\r\n position: absolute;\r\n inset: -3px;\r\n border-radius: 50%;\r\n border: 2px solid var(--tcw-green);\r\n opacity: 0;\r\n animation: tcw-pulse 2.2s ease-out infinite;\r\n}\r\n\r\n@keyframes tcw-pulse {\r\n 0% { opacity: 0.6; transform: scale(0.6); }\r\n 100% { opacity: 0; transform: scale(2.2); }\r\n}\r\n\r\n.tcw-status-dot.tcw-error {\r\n background: var(--tcw-red);\r\n box-shadow: 0 0 0 3px rgba(232, 80, 91, 0.2);\r\n}\r\n\r\n/* === Command Palette === */\r\n\r\n.tcw-cmd-palette {\r\n position: fixed;\r\n z-index: 102;\r\n top: 18%;\r\n left: 50%;\r\n transform: translateX(-50%);\r\n background: var(--tcw-bg-elevated);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: var(--tcw-radius-xl);\r\n width: 560px;\r\n max-width: 92vw;\r\n max-height: 62vh;\r\n display: flex;\r\n flex-direction: column;\r\n box-shadow: var(--tcw-shadow-xl);\r\n overflow: hidden;\r\n backdrop-filter: blur(20px);\r\n -webkit-backdrop-filter: blur(20px);\r\n animation: tcw-modal-in var(--tcw-dur-slow) var(--tcw-ease-out);\r\n}\r\n\r\n.tcw-cmd-header {\r\n display: flex;\r\n align-items: center;\r\n padding: 14px 18px;\r\n border-bottom: 1px solid var(--tcw-divider);\r\n gap: 12px;\r\n}\r\n\r\n.tcw-cmd-icon {\r\n font-size: 18px;\r\n color: var(--tcw-text-muted);\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n}\r\n\r\n.tcw-cmd-input {\r\n flex: 1;\r\n background: transparent;\r\n border: none;\r\n outline: none;\r\n font-size: 14.5px;\r\n font-family: inherit;\r\n color: var(--tcw-text);\r\n caret-color: var(--tcw-accent);\r\n letter-spacing: -0.005em;\r\n}\r\n\r\n.tcw-cmd-input::placeholder {\r\n color: var(--tcw-text-muted);\r\n}\r\n\r\n.tcw-cmd-hint {\r\n font-size: 10px;\r\n color: var(--tcw-text-muted);\r\n font-family: var(--tcw-font-mono);\r\n border: 1px solid var(--tcw-border);\r\n background: var(--tcw-bg-surface);\r\n padding: 3px 7px;\r\n border-radius: 4px;\r\n flex-shrink: 0;\r\n box-shadow: inset 0 -1px 0 var(--tcw-border);\r\n}\r\n\r\n.tcw-cmd-list {\r\n overflow-y: auto;\r\n flex: 1;\r\n padding: 6px;\r\n max-height: 380px;\r\n}\r\n\r\n.tcw-cmd-category {\r\n text-transform: uppercase;\r\n font-size: 10px;\r\n color: var(--tcw-text-muted);\r\n letter-spacing: 0.09em;\r\n padding: 10px 10px 4px;\r\n font-weight: 600;\r\n}\r\n\r\n.tcw-cmd-item {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 9px 12px;\r\n font-size: 13px;\r\n color: var(--tcw-text-dim);\r\n cursor: pointer;\r\n width: 100%;\r\n border: none;\r\n background: transparent;\r\n font-family: inherit;\r\n text-align: left;\r\n transition: background var(--tcw-dur-fast) var(--tcw-ease), color var(--tcw-dur-fast) var(--tcw-ease);\r\n gap: 10px;\r\n border-radius: var(--tcw-radius);\r\n letter-spacing: -0.005em;\r\n}\r\n\r\n.tcw-cmd-item:hover,\r\n.tcw-cmd-item.tcw-selected {\r\n background: var(--tcw-hover-bg);\r\n color: var(--tcw-text);\r\n}\r\n\r\n.tcw-cmd-item.tcw-selected {\r\n box-shadow: inset 2px 0 0 var(--tcw-accent);\r\n}\r\n\r\n.tcw-cmd-item.tcw-active .tcw-cmd-item-label {\r\n color: var(--tcw-accent);\r\n font-weight: 600;\r\n}\r\n\r\n.tcw-cmd-item-label {\r\n flex: 1;\r\n}\r\n\r\n.tcw-cmd-check {\r\n color: var(--tcw-accent);\r\n font-size: 14px;\r\n font-weight: 700;\r\n}\r\n\r\n.tcw-cmd-kbd {\r\n font-size: 10px;\r\n font-family: var(--tcw-font-mono);\r\n color: var(--tcw-text-muted);\r\n background: var(--tcw-bg-surface);\r\n border: 1px solid var(--tcw-border);\r\n padding: 2px 6px;\r\n border-radius: 4px;\r\n box-shadow: inset 0 -1px 0 var(--tcw-border);\r\n}\r\n\r\n.tcw-cmd-empty {\r\n padding: 32px 16px;\r\n text-align: center;\r\n color: var(--tcw-text-muted);\r\n font-size: 13px;\r\n}\r\n\r\n.tcw-cmd-footer {\r\n display: flex;\r\n align-items: center;\r\n gap: 18px;\r\n padding: 10px 18px;\r\n border-top: 1px solid var(--tcw-divider);\r\n font-size: 11px;\r\n color: var(--tcw-text-muted);\r\n background: var(--tcw-bg-surface);\r\n}\r\n\r\n/* === Watchlist === */\r\n\r\n.tcw-watchlist {\r\n width: 220px;\r\n flex-shrink: 0;\r\n background: var(--tcw-bg-surface);\r\n border-left: 1px solid var(--tcw-border);\r\n display: flex;\r\n flex-direction: column;\r\n overflow: hidden;\r\n box-shadow: var(--tcw-shadow-sm);\r\n}\r\n\r\n.tcw-watchlist-header {\r\n padding: 10px 14px;\r\n font-size: 11px;\r\n font-weight: 600;\r\n text-transform: uppercase;\r\n letter-spacing: 0.08em;\r\n color: var(--tcw-text-muted);\r\n border-bottom: 1px solid var(--tcw-divider);\r\n}\r\n\r\n.tcw-watchlist-list {\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 4px 0;\r\n}\r\n\r\n.tcw-watchlist-row {\r\n display: grid;\r\n grid-template-columns: 1fr auto auto;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 8px 12px;\r\n cursor: pointer;\r\n transition: background var(--tcw-dur-fast) var(--tcw-ease);\r\n border-left: 2px solid transparent;\r\n outline: none;\r\n}\r\n\r\n.tcw-watchlist-row:hover {\r\n background: var(--tcw-hover-bg);\r\n}\r\n\r\n.tcw-watchlist-row:focus-visible {\r\n background: var(--tcw-hover-bg);\r\n border-left-color: var(--tcw-accent);\r\n}\r\n\r\n.tcw-watchlist-row.tcw-watchlist-active {\r\n background: var(--tcw-accent-soft);\r\n border-left-color: var(--tcw-accent);\r\n}\r\n\r\n.tcw-watchlist-symbol {\r\n font-size: 12.5px;\r\n font-weight: 600;\r\n color: var(--tcw-text);\r\n letter-spacing: -0.005em;\r\n white-space: nowrap;\r\n}\r\n\r\n.tcw-watchlist-spark {\r\n width: 56px;\r\n height: 22px;\r\n align-self: center;\r\n}\r\n\r\n.tcw-watchlist-stats {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: flex-end;\r\n gap: 1px;\r\n font-variant-numeric: tabular-nums;\r\n font-family: var(--tcw-font-mono);\r\n}\r\n\r\n.tcw-watchlist-price {\r\n font-size: 12px;\r\n color: var(--tcw-text);\r\n font-weight: 500;\r\n}\r\n\r\n.tcw-watchlist-change {\r\n font-size: 10.5px;\r\n color: var(--tcw-text-muted);\r\n font-weight: 500;\r\n}\r\n\r\n.tcw-watchlist-change.tcw-up {\r\n color: var(--tcw-green);\r\n}\r\n\r\n.tcw-watchlist-change.tcw-down {\r\n color: var(--tcw-red);\r\n}\r\n\r\n@media (max-width: 768px) {\r\n .tcw-watchlist {\r\n display: none;\r\n }\r\n}\r\n\r\n/* === Toast === */\r\n\r\n.tcw-toast {\r\n position: absolute;\r\n bottom: 60px;\r\n left: 50%;\r\n transform: translate(-50%, 12px);\r\n z-index: 40;\r\n padding: 9px 14px;\r\n background: var(--tcw-bg-elevated);\r\n border: 1px solid var(--tcw-border-strong);\r\n border-radius: 9999px;\r\n box-shadow: var(--tcw-shadow-lg);\r\n font-size: 12.5px;\r\n font-weight: 500;\r\n color: var(--tcw-text);\r\n opacity: 0;\r\n transition: opacity var(--tcw-dur-normal) var(--tcw-ease-out), transform var(--tcw-dur-normal) var(--tcw-ease-out);\r\n pointer-events: none;\r\n max-width: calc(100% - 32px);\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n letter-spacing: -0.005em;\r\n}\r\n\r\n.tcw-toast.tcw-toast-in {\r\n opacity: 1;\r\n transform: translate(-50%, 0);\r\n}\r\n\r\n.tcw-toast-error {\r\n border-color: var(--tcw-red);\r\n color: var(--tcw-red);\r\n}\r\n\r\n/* === Replay scrubber === */\r\n\r\n.tcw-replay-bar {\r\n position: absolute;\r\n left: 50%;\r\n bottom: 16px;\r\n transform: translateX(-50%);\r\n z-index: 25;\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 6px 10px;\r\n background: var(--tcw-bg-elevated);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: 9999px;\r\n box-shadow: var(--tcw-shadow-lg);\r\n backdrop-filter: blur(12px);\r\n -webkit-backdrop-filter: blur(12px);\r\n font-size: 12px;\r\n color: var(--tcw-text);\r\n animation: tcw-pop-in var(--tcw-dur-normal) var(--tcw-ease-out);\r\n}\r\n\r\n.tcw-replay-btn {\r\n width: 28px;\r\n height: 28px;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n border: none;\r\n background: transparent;\r\n color: var(--tcw-text-dim);\r\n border-radius: 9999px;\r\n cursor: pointer;\r\n font-family: inherit;\r\n transition: background var(--tcw-dur-fast) var(--tcw-ease), color var(--tcw-dur-fast) var(--tcw-ease);\r\n}\r\n\r\n.tcw-replay-btn:hover {\r\n background: var(--tcw-hover-bg);\r\n color: var(--tcw-text);\r\n}\r\n\r\n.tcw-replay-btn.tcw-replay-play {\r\n background: var(--tcw-accent);\r\n color: var(--tcw-accent-ink);\r\n width: 32px;\r\n height: 32px;\r\n box-shadow: 0 0 0 3px var(--tcw-accent-glow);\r\n}\r\n\r\n.tcw-replay-btn.tcw-replay-play:hover {\r\n background: var(--tcw-accent-hover);\r\n}\r\n\r\n.tcw-replay-scrubber {\r\n appearance: none;\r\n -webkit-appearance: none;\r\n width: 220px;\r\n height: 4px;\r\n background: var(--tcw-border-strong);\r\n border-radius: 2px;\r\n outline: none;\r\n cursor: pointer;\r\n}\r\n\r\n.tcw-replay-scrubber::-webkit-slider-thumb {\r\n -webkit-appearance: none;\r\n appearance: none;\r\n width: 14px;\r\n height: 14px;\r\n border-radius: 50%;\r\n background: var(--tcw-accent);\r\n border: 2px solid var(--tcw-bg-elevated);\r\n box-shadow: 0 0 0 1px var(--tcw-accent), 0 2px 6px rgba(0, 0, 0, 0.3);\r\n cursor: pointer;\r\n}\r\n\r\n.tcw-replay-scrubber::-moz-range-thumb {\r\n width: 14px;\r\n height: 14px;\r\n border-radius: 50%;\r\n background: var(--tcw-accent);\r\n border: 2px solid var(--tcw-bg-elevated);\r\n box-shadow: 0 0 0 1px var(--tcw-accent), 0 2px 6px rgba(0, 0, 0, 0.3);\r\n cursor: pointer;\r\n}\r\n\r\n.tcw-replay-progress {\r\n font-size: 11px;\r\n font-family: var(--tcw-font-mono);\r\n color: var(--tcw-text-muted);\r\n min-width: 64px;\r\n text-align: center;\r\n font-feature-settings: 'tnum';\r\n}\r\n\r\n.tcw-replay-speed {\r\n padding: 4px 8px;\r\n font-size: 11px;\r\n font-family: var(--tcw-font-mono);\r\n background: var(--tcw-bg-surface);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: var(--tcw-radius);\r\n color: var(--tcw-text-dim);\r\n cursor: pointer;\r\n}\r\n\r\n.tcw-replay-speed:hover {\r\n border-color: var(--tcw-border-strong);\r\n}\r\n\r\n@media (max-width: 640px) {\r\n .tcw-replay-scrubber {\r\n width: 140px;\r\n }\r\n .tcw-replay-progress {\r\n display: none;\r\n }\r\n}\r\n\r\n/* === Hotkey sheet === */\r\n\r\n.tcw-hotkey-grid {\r\n display: grid;\r\n grid-template-columns: 1fr 1fr;\r\n gap: 24px 32px;\r\n}\r\n\r\n.tcw-hotkey-section .tcw-settings-section-title {\r\n margin-bottom: 8px;\r\n}\r\n\r\n.tcw-hotkey-row {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: 12px;\r\n padding: 6px 0;\r\n border-bottom: 1px solid var(--tcw-divider);\r\n}\r\n\r\n.tcw-hotkey-row:last-child {\r\n border-bottom: none;\r\n}\r\n\r\n.tcw-hotkey-keys {\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n flex-shrink: 0;\r\n}\r\n\r\n.tcw-hotkey-sep {\r\n font-size: 10px;\r\n color: var(--tcw-text-muted);\r\n}\r\n\r\n.tcw-hotkey-label {\r\n font-size: 12.5px;\r\n color: var(--tcw-text-dim);\r\n text-align: right;\r\n letter-spacing: -0.005em;\r\n}\r\n\r\n@media (max-width: 640px) {\r\n .tcw-hotkey-grid {\r\n grid-template-columns: 1fr;\r\n gap: 18px;\r\n }\r\n}\r\n\r\n/* === Scrollbar (subtle, premium feel) === */\r\n\r\n.tcw-dropdown::-webkit-scrollbar,\r\n.tcw-modal-body::-webkit-scrollbar,\r\n.tcw-cmd-list::-webkit-scrollbar {\r\n width: 8px;\r\n height: 8px;\r\n}\r\n\r\n.tcw-dropdown::-webkit-scrollbar-track,\r\n.tcw-modal-body::-webkit-scrollbar-track,\r\n.tcw-cmd-list::-webkit-scrollbar-track {\r\n background: transparent;\r\n}\r\n\r\n.tcw-dropdown::-webkit-scrollbar-thumb,\r\n.tcw-modal-body::-webkit-scrollbar-thumb,\r\n.tcw-cmd-list::-webkit-scrollbar-thumb {\r\n background: var(--tcw-border-strong);\r\n border-radius: 4px;\r\n border: 2px solid transparent;\r\n background-clip: padding-box;\r\n}\r\n\r\n.tcw-dropdown::-webkit-scrollbar-thumb:hover,\r\n.tcw-modal-body::-webkit-scrollbar-thumb:hover,\r\n.tcw-cmd-list::-webkit-scrollbar-thumb:hover {\r\n background: var(--tcw-text-muted);\r\n background-clip: padding-box;\r\n border: 2px solid transparent;\r\n}\r\n\r\n/* === Responsive === */\r\n\r\n@media (max-width: 768px) {\r\n .tcw-toolbar {\r\n padding: 0 4px;\r\n height: 44px; /* Apple HIG minimum touch target */\r\n }\r\n .tcw-toolbar-sep {\r\n display: none;\r\n }\r\n .tcw-indicator-chips {\r\n display: none;\r\n }\r\n .tcw-btn,\r\n .tcw-dropdown-trigger {\r\n padding: 10px 12px; /* fatter tap target */\r\n font-size: 13px;\r\n }\r\n .tcw-btn-icon {\r\n width: 40px;\r\n height: 40px;\r\n }\r\n .tcw-sidebar {\r\n width: 44px;\r\n }\r\n .tcw-sidebar-btn {\r\n width: 40px;\r\n height: 40px;\r\n }\r\n .tcw-modal,\r\n .tcw-cmd-palette {\r\n width: 94vw;\r\n border-radius: var(--tcw-radius-lg);\r\n }\r\n\r\n /* Hide the right-side watchlist on tablets too — already declared but\r\n * grouped here for clarity. */\r\n .tcw-watchlist {\r\n display: none;\r\n }\r\n\r\n /* Replay scrubber compact mode */\r\n .tcw-replay-scrubber {\r\n width: 120px;\r\n }\r\n .tcw-replay-progress {\r\n display: none;\r\n }\r\n}\r\n\r\n/* === Mobile bottom-sheet pattern ===\r\n * On phones, swap floating-centered modals for full-width sheets that slide\r\n * up from the bottom. Bigger hit targets, no awkward off-screen clipping,\r\n * matches how every modern iOS/Android app handles overflow UI.\r\n */\r\n@media (max-width: 640px) {\r\n .tcw-modal,\r\n .tcw-cmd-palette {\r\n top: auto;\r\n bottom: 0;\r\n left: 0;\r\n right: 0;\r\n transform: none;\r\n width: 100vw;\r\n max-width: 100vw;\r\n max-height: 92vh;\r\n border-radius: var(--tcw-radius-xl) var(--tcw-radius-xl) 0 0;\r\n border-bottom: none;\r\n /* Grab handle */\r\n padding-top: 8px;\r\n }\r\n\r\n /* Visual \"grab handle\" affordance — a short pill at the top of every sheet */\r\n .tcw-modal::before,\r\n .tcw-cmd-palette::before {\r\n content: '';\r\n display: block;\r\n width: 36px;\r\n height: 4px;\r\n border-radius: 2px;\r\n background: var(--tcw-border-strong);\r\n margin: 0 auto 8px;\r\n opacity: 0.7;\r\n }\r\n\r\n /* Reuse the existing modal-in animation but slide from below instead of\r\n * popping from center, so the entrance reads as a sheet, not a dialog. */\r\n @keyframes tcw-modal-in {\r\n from { opacity: 0; transform: translateY(20px); }\r\n to { opacity: 1; transform: translateY(0); }\r\n }\r\n\r\n .tcw-modal,\r\n .tcw-cmd-palette {\r\n animation: tcw-modal-in var(--tcw-dur-slow) var(--tcw-ease-out);\r\n }\r\n\r\n /* Safe-area-aware footer padding for iPhone notch / home indicator */\r\n .tcw-modal-footer,\r\n .tcw-cmd-footer {\r\n padding-bottom: max(14px, env(safe-area-inset-bottom));\r\n }\r\n\r\n /* Larger tap targets inside sheets */\r\n .tcw-modal-tab {\r\n padding: 14px 16px;\r\n }\r\n .tcw-cmd-item {\r\n padding: 12px 14px;\r\n font-size: 14px;\r\n }\r\n .tcw-dropdown-item {\r\n padding: 12px 12px;\r\n font-size: 14px;\r\n }\r\n\r\n /* Hotkey sheet — single column on phones */\r\n .tcw-hotkey-grid {\r\n grid-template-columns: 1fr;\r\n }\r\n}\r\n\r\n/* === Price alerts panel === */\r\n\r\n.tcw-alerts-panel {\r\n position: absolute;\r\n top: 50px;\r\n right: 12px;\r\n z-index: 40;\r\n width: 300px;\r\n max-width: calc(100vw - 24px);\r\n display: flex;\r\n flex-direction: column;\r\n background: var(--tcw-bg-elevated);\r\n border: 1px solid var(--tcw-border-strong);\r\n border-radius: var(--tcw-radius-lg);\r\n box-shadow: var(--tcw-shadow-lg);\r\n overflow: hidden;\r\n animation: tcw-alerts-in var(--tcw-dur, 160ms) var(--tcw-ease, ease) both;\r\n}\r\n\r\n@keyframes tcw-alerts-in {\r\n from { opacity: 0; transform: translateY(-6px) scale(0.98); }\r\n to { opacity: 1; transform: none; }\r\n}\r\n\r\n.tcw-alerts-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 10px 12px;\r\n font-weight: 600;\r\n font-size: 13px;\r\n color: var(--tcw-text);\r\n border-bottom: 1px solid var(--tcw-border);\r\n}\r\n\r\n.tcw-alerts-close {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 26px;\r\n height: 26px;\r\n border: none;\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n}\r\n.tcw-alerts-close:hover {\r\n background: var(--tcw-bg-surface);\r\n color: var(--tcw-text);\r\n}\r\n\r\n.tcw-alerts-form {\r\n display: grid;\r\n grid-template-columns: 1fr 1fr;\r\n gap: 8px;\r\n padding: 12px;\r\n border-bottom: 1px solid var(--tcw-border);\r\n}\r\n\r\n.tcw-alerts-form input,\r\n.tcw-alerts-form select {\r\n width: 100%;\r\n min-width: 0;\r\n box-sizing: border-box;\r\n height: 32px;\r\n padding: 0 8px;\r\n font-size: 12px;\r\n color: var(--tcw-text);\r\n background: var(--tcw-bg-surface);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: var(--tcw-radius-sm);\r\n outline: none;\r\n}\r\n.tcw-alerts-form input:focus,\r\n.tcw-alerts-form select:focus {\r\n border-color: var(--tcw-accent);\r\n box-shadow: 0 0 0 2px var(--tcw-accent-soft);\r\n}\r\n.tcw-alerts-message,\r\n.tcw-alerts-source {\r\n grid-column: 1 / -1;\r\n}\r\n\r\n.tcw-alerts-add {\r\n grid-column: 1 / -1;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 6px;\r\n height: 34px;\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--tcw-accent-ink);\r\n background: var(--tcw-accent);\r\n border: none;\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n}\r\n.tcw-alerts-add:hover {\r\n background: var(--tcw-accent-hover);\r\n}\r\n\r\n.tcw-alerts-list {\r\n max-height: 260px;\r\n overflow-y: auto;\r\n padding: 6px;\r\n}\r\n\r\n.tcw-alerts-empty {\r\n padding: 18px 12px;\r\n text-align: center;\r\n font-size: 12px;\r\n color: var(--tcw-text-muted);\r\n line-height: 1.5;\r\n}\r\n\r\n.tcw-alerts-row {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: 8px;\r\n padding: 8px 8px;\r\n border-radius: var(--tcw-radius-sm);\r\n}\r\n.tcw-alerts-row:hover {\r\n background: var(--tcw-bg-surface);\r\n}\r\n.tcw-alerts-triggered {\r\n background: var(--tcw-accent-soft);\r\n}\r\n\r\n.tcw-alerts-info {\r\n min-width: 0;\r\n}\r\n.tcw-alerts-row-main {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--tcw-text);\r\n font-variant-numeric: tabular-nums;\r\n}\r\n.tcw-alerts-row-note {\r\n margin-top: 2px;\r\n font-size: 11px;\r\n color: var(--tcw-text-muted);\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n.tcw-alerts-badge {\r\n font-size: 9px;\r\n font-weight: 700;\r\n text-transform: uppercase;\r\n letter-spacing: 0.04em;\r\n padding: 2px 5px;\r\n border-radius: 999px;\r\n color: var(--tcw-accent);\r\n background: var(--tcw-accent-soft);\r\n}\r\n\r\n.tcw-alerts-del {\r\n flex-shrink: 0;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 28px;\r\n height: 28px;\r\n border: none;\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n}\r\n.tcw-alerts-del:hover {\r\n background: var(--tcw-red-soft);\r\n color: var(--tcw-red);\r\n}\r\n\r\n@media (max-width: 640px) {\r\n .tcw-alerts-panel {\r\n top: auto;\r\n bottom: 0;\r\n left: 0;\r\n right: 0;\r\n width: 100vw;\r\n max-width: 100vw;\r\n border-radius: var(--tcw-radius-xl) var(--tcw-radius-xl) 0 0;\r\n }\r\n .tcw-alerts-list {\r\n max-height: 40vh;\r\n padding-bottom: max(8px, env(safe-area-inset-bottom));\r\n }\r\n}\r\n\r\n/* === Data Window === */\r\n\r\n.tcw-datawin {\r\n position: absolute;\r\n top: 50px;\r\n right: 12px;\r\n z-index: 38;\r\n width: 200px;\r\n display: flex;\r\n flex-direction: column;\r\n background: var(--tcw-bg-elevated);\r\n border: 1px solid var(--tcw-border-strong);\r\n border-radius: var(--tcw-radius-lg);\r\n box-shadow: var(--tcw-shadow-lg);\r\n overflow: hidden;\r\n animation: tcw-alerts-in var(--tcw-dur, 160ms) var(--tcw-ease, ease) both;\r\n}\r\n.tcw-datawin-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 9px 12px;\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--tcw-text);\r\n border-bottom: 1px solid var(--tcw-border);\r\n}\r\n.tcw-datawin-close {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 24px;\r\n height: 24px;\r\n border: none;\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n}\r\n.tcw-datawin-close:hover { background: var(--tcw-bg-surface); color: var(--tcw-text); }\r\n.tcw-datawin-body { padding: 6px 4px; max-height: 360px; overflow-y: auto; }\r\n.tcw-datawin-empty { padding: 14px; text-align: center; font-size: 12px; color: var(--tcw-text-muted); }\r\n.tcw-datawin-row {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: 10px;\r\n padding: 3px 8px;\r\n font-size: 12px;\r\n}\r\n.tcw-datawin-label { color: var(--tcw-text-muted); }\r\n.tcw-datawin-value { color: var(--tcw-text); font-variant-numeric: tabular-nums; font-weight: 600; }\r\n.tcw-datawin-value.tcw-up { color: var(--tcw-green); }\r\n.tcw-datawin-value.tcw-down { color: var(--tcw-red); }\r\n.tcw-datawin-sub {\r\n margin: 6px 8px 2px;\r\n font-size: 10px;\r\n font-weight: 700;\r\n text-transform: uppercase;\r\n letter-spacing: 0.04em;\r\n color: var(--tcw-text-muted);\r\n border-top: 1px solid var(--tcw-border);\r\n padding-top: 6px;\r\n}\r\n\r\n@media (max-width: 640px) {\r\n .tcw-datawin { top: auto; bottom: 0; left: 0; right: 0; width: 100vw; border-radius: var(--tcw-radius-xl) var(--tcw-radius-xl) 0 0; }\r\n}\r\n\r\n/* === Depth-of-market ladder === */\r\n\r\n.tcw-ladder {\r\n position: absolute;\r\n top: 50px;\r\n right: 12px;\r\n z-index: 41;\r\n width: 240px;\r\n display: flex;\r\n flex-direction: column;\r\n background: var(--tcw-bg-elevated);\r\n border: 1px solid var(--tcw-border-strong);\r\n border-radius: var(--tcw-radius-lg);\r\n box-shadow: var(--tcw-shadow-lg);\r\n overflow: hidden;\r\n animation: tcw-alerts-in var(--tcw-dur, 160ms) var(--tcw-ease, ease) both;\r\n}\r\n.tcw-ladder-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 10px 12px;\r\n font-size: 13px;\r\n font-weight: 600;\r\n color: var(--tcw-text);\r\n border-bottom: 1px solid var(--tcw-border);\r\n}\r\n.tcw-ladder-close {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 26px;\r\n height: 26px;\r\n border: none;\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n}\r\n.tcw-ladder-close:hover { background: var(--tcw-bg-surface); color: var(--tcw-text); }\r\n.tcw-ladder-cols {\r\n display: grid;\r\n grid-template-columns: 1fr 1fr 1fr;\r\n padding: 4px 0;\r\n font-size: 10px;\r\n font-weight: 700;\r\n text-transform: uppercase;\r\n letter-spacing: 0.04em;\r\n color: var(--tcw-text-muted);\r\n border-bottom: 1px solid var(--tcw-border);\r\n}\r\n.tcw-ladder-cols span { text-align: center; }\r\n.tcw-ladder-body { max-height: 360px; overflow-y: auto; }\r\n.tcw-ladder-empty { padding: 18px; text-align: center; font-size: 12px; color: var(--tcw-text-muted); }\r\n\r\n.tcw-ladder-row {\r\n display: grid;\r\n grid-template-columns: 1fr 1fr 1fr;\r\n height: 22px;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n.tcw-ladder-cell {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n border: none;\r\n background: transparent;\r\n font-size: 11px;\r\n cursor: pointer;\r\n font-variant-numeric: tabular-nums;\r\n overflow: hidden;\r\n}\r\n.tcw-ladder-cell:disabled { cursor: default; }\r\n/* Depth bar grows from the centre column outward. */\r\n.tcw-ladder-cell::before {\r\n content: '';\r\n position: absolute;\r\n top: 1px; bottom: 1px;\r\n width: var(--w, 0%);\r\n}\r\n.tcw-ladder-bid { justify-content: flex-end; padding-right: 8px; color: var(--tcw-green); }\r\n.tcw-ladder-bid::before { right: 0; background: var(--tcw-green-soft); }\r\n.tcw-ladder-bid span, .tcw-ladder-bid { position: relative; }\r\n.tcw-ladder-ask { justify-content: flex-start; padding-left: 8px; color: var(--tcw-red); }\r\n.tcw-ladder-ask::before { left: 0; background: var(--tcw-red-soft); }\r\n.tcw-ladder-cell:not(:disabled):hover::before { opacity: 0.7; filter: brightness(1.4); }\r\n.tcw-ladder-cell > * { position: relative; }\r\n.tcw-ladder-price {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--tcw-text-dim);\r\n border-left: 1px solid var(--tcw-border);\r\n border-right: 1px solid var(--tcw-border);\r\n}\r\n.tcw-ladder-price.tcw-ask { color: var(--tcw-red); }\r\n.tcw-ladder-price.tcw-bid { color: var(--tcw-green); }\r\n\r\n@media (max-width: 640px) {\r\n .tcw-ladder { top: auto; bottom: 0; left: 0; right: 0; width: 100vw; border-radius: var(--tcw-radius-xl) var(--tcw-radius-xl) 0 0; }\r\n .tcw-ladder-body { max-height: 45vh; padding-bottom: max(0px, env(safe-area-inset-bottom)); }\r\n}\r\n\r\n/* === Bracket order placement === */\r\n\r\n.tcw-btn-long:hover { color: var(--tcw-green) !important; }\r\n.tcw-btn-short:hover { color: var(--tcw-red) !important; }\r\n\r\n.tcw-bracket-bar {\r\n position: absolute;\r\n top: 52px;\r\n left: 50%;\r\n transform: translateX(-50%);\r\n z-index: 45;\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n padding: 7px 8px 7px 14px;\r\n background: var(--tcw-bg-elevated);\r\n border: 1px solid var(--tcw-border-strong);\r\n border-radius: 999px;\r\n box-shadow: var(--tcw-shadow-lg);\r\n animation: tcw-alerts-in var(--tcw-dur, 160ms) var(--tcw-ease, ease) both;\r\n}\r\n.tcw-bracket-label {\r\n font-size: 12px;\r\n font-weight: 700;\r\n}\r\n.tcw-bracket-label[data-side='buy'] { color: var(--tcw-green); }\r\n.tcw-bracket-label[data-side='sell'] { color: var(--tcw-red); }\r\n.tcw-bracket-hint {\r\n font-size: 11px;\r\n color: var(--tcw-text-muted);\r\n}\r\n.tcw-bracket-cancel, .tcw-bracket-confirm {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 5px;\r\n height: 30px;\r\n padding: 0 12px;\r\n font-size: 12px;\r\n font-weight: 600;\r\n border: none;\r\n border-radius: 999px;\r\n cursor: pointer;\r\n}\r\n.tcw-bracket-cancel {\r\n color: var(--tcw-text-dim);\r\n background: var(--tcw-bg-surface);\r\n}\r\n.tcw-bracket-cancel:hover { color: var(--tcw-text); }\r\n.tcw-bracket-confirm {\r\n color: var(--tcw-accent-ink);\r\n background: var(--tcw-accent);\r\n}\r\n.tcw-bracket-confirm:hover { background: var(--tcw-accent-hover); }\r\n\r\n@media (max-width: 640px) {\r\n .tcw-bracket-bar { width: calc(100vw - 24px); justify-content: space-between; }\r\n .tcw-bracket-hint { display: none; }\r\n}\r\n\r\n/* === Drawing style + templates popover === */\r\n\r\n.tcw-style-panel {\r\n position: absolute;\r\n bottom: 12px;\r\n left: 52px;\r\n z-index: 40;\r\n width: 220px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n padding: 12px;\r\n background: var(--tcw-bg-elevated);\r\n border: 1px solid var(--tcw-border-strong);\r\n border-radius: var(--tcw-radius-lg);\r\n box-shadow: var(--tcw-shadow-lg);\r\n animation: tcw-alerts-in var(--tcw-dur, 160ms) var(--tcw-ease, ease) both;\r\n}\r\n.tcw-style-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--tcw-text);\r\n}\r\n.tcw-style-close {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 24px;\r\n height: 24px;\r\n border: none;\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n}\r\n.tcw-style-close:hover { background: var(--tcw-bg-surface); color: var(--tcw-text); }\r\n\r\n.tcw-style-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\r\n.tcw-style-swatch {\r\n width: 18px;\r\n height: 18px;\r\n border-radius: 50%;\r\n border: 1px solid var(--tcw-border-strong);\r\n cursor: pointer;\r\n padding: 0;\r\n}\r\n.tcw-style-swatch.tcw-on { outline: 2px solid var(--tcw-accent); outline-offset: 1px; }\r\n.tcw-style-picker {\r\n width: 22px;\r\n height: 22px;\r\n padding: 0;\r\n border: 1px solid var(--tcw-border);\r\n border-radius: var(--tcw-radius-sm);\r\n background: transparent;\r\n cursor: pointer;\r\n}\r\n.tcw-style-width, .tcw-style-dash {\r\n flex: 1;\r\n min-width: 0;\r\n height: 26px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: var(--tcw-bg-surface);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: var(--tcw-radius-sm);\r\n color: var(--tcw-text-dim);\r\n cursor: pointer;\r\n font-size: 11px;\r\n}\r\n.tcw-style-width span { width: 16px; background: currentColor; border-radius: 2px; }\r\n.tcw-style-width.tcw-on, .tcw-style-dash.tcw-on {\r\n border-color: var(--tcw-accent);\r\n color: var(--tcw-accent);\r\n}\r\n\r\n.tcw-style-subhead {\r\n font-size: 10px;\r\n font-weight: 700;\r\n text-transform: uppercase;\r\n letter-spacing: 0.05em;\r\n color: var(--tcw-text-muted);\r\n margin-top: 2px;\r\n}\r\n.tcw-style-save { display: flex; gap: 6px; }\r\n.tcw-style-name {\r\n flex: 1;\r\n min-width: 0;\r\n height: 28px;\r\n padding: 0 8px;\r\n font-size: 12px;\r\n color: var(--tcw-text);\r\n background: var(--tcw-bg-surface);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: var(--tcw-radius-sm);\r\n outline: none;\r\n}\r\n.tcw-style-name:focus { border-color: var(--tcw-accent); }\r\n.tcw-style-savebtn {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 0 10px;\r\n height: 28px;\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: var(--tcw-accent-ink);\r\n background: var(--tcw-accent);\r\n border: none;\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n}\r\n.tcw-style-savebtn:hover { background: var(--tcw-accent-hover); }\r\n\r\n.tcw-style-templates { display: flex; flex-direction: column; gap: 2px; max-height: 140px; overflow-y: auto; }\r\n.tcw-style-empty { font-size: 11px; color: var(--tcw-text-muted); padding: 4px 0; }\r\n.tcw-style-template { display: flex; align-items: center; gap: 4px; }\r\n.tcw-style-apply {\r\n flex: 1;\r\n min-width: 0;\r\n display: flex;\r\n align-items: center;\r\n gap: 7px;\r\n padding: 5px 7px;\r\n font-size: 12px;\r\n color: var(--tcw-text);\r\n background: transparent;\r\n border: none;\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n text-align: left;\r\n}\r\n.tcw-style-apply:hover { background: var(--tcw-bg-surface); }\r\n.tcw-style-tdot { flex-shrink: 0; width: 9px; height: 9px; border-radius: 50%; }\r\n.tcw-style-tdel {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 26px;\r\n height: 26px;\r\n border: none;\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n}\r\n.tcw-style-tdel:hover { background: var(--tcw-red-soft); color: var(--tcw-red); }\r\n\r\n/* === Settings range slider === */\r\n\r\n.tcw-settings-range {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n.tcw-settings-range input[type='range'] {\r\n width: 110px;\r\n accent-color: var(--tcw-accent);\r\n cursor: pointer;\r\n}\r\n.tcw-settings-range-val {\r\n min-width: 38px;\r\n text-align: right;\r\n font-size: 12px;\r\n font-variant-numeric: tabular-nums;\r\n color: var(--tcw-text-dim);\r\n}\r\n\r\n/* === Indicator settings dialog === */\r\n\r\n.tcw-modal-narrow {\r\n max-width: 360px;\r\n}\r\n.tcw-indi-input {\r\n width: 110px;\r\n height: 30px;\r\n padding: 0 8px;\r\n font-size: 12px;\r\n color: var(--tcw-text);\r\n background: var(--tcw-bg-surface);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: var(--tcw-radius-sm);\r\n outline: none;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n.tcw-indi-input:focus {\r\n border-color: var(--tcw-accent);\r\n box-shadow: 0 0 0 2px var(--tcw-accent-soft);\r\n}\r\n.tcw-indi-color {\r\n width: 36px;\r\n height: 28px;\r\n padding: 2px;\r\n background: var(--tcw-bg-surface);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n}\r\n\r\n/* === Object tree panel === */\r\n\r\n.tcw-tree-panel {\r\n position: absolute;\r\n top: 50px;\r\n right: 12px;\r\n z-index: 39;\r\n width: 280px;\r\n max-width: calc(100vw - 24px);\r\n display: flex;\r\n flex-direction: column;\r\n background: var(--tcw-bg-elevated);\r\n border: 1px solid var(--tcw-border-strong);\r\n border-radius: var(--tcw-radius-lg);\r\n box-shadow: var(--tcw-shadow-lg);\r\n overflow: hidden;\r\n animation: tcw-alerts-in var(--tcw-dur, 160ms) var(--tcw-ease, ease) both;\r\n}\r\n\r\n.tcw-tree-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 10px 12px;\r\n font-weight: 600;\r\n font-size: 13px;\r\n color: var(--tcw-text);\r\n border-bottom: 1px solid var(--tcw-border);\r\n}\r\n.tcw-tree-close {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 26px;\r\n height: 26px;\r\n border: none;\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n}\r\n.tcw-tree-close:hover {\r\n background: var(--tcw-bg-surface);\r\n color: var(--tcw-text);\r\n}\r\n\r\n.tcw-tree-section {\r\n padding: 6px;\r\n border-bottom: 1px solid var(--tcw-border);\r\n}\r\n.tcw-tree-section:last-child {\r\n border-bottom: none;\r\n}\r\n.tcw-tree-section-head {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 6px 8px 4px;\r\n font-size: 10px;\r\n font-weight: 700;\r\n text-transform: uppercase;\r\n letter-spacing: 0.05em;\r\n color: var(--tcw-text-muted);\r\n}\r\n.tcw-tree-add {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 22px;\r\n height: 22px;\r\n border: none;\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n}\r\n.tcw-tree-add:hover {\r\n background: var(--tcw-accent-soft);\r\n color: var(--tcw-accent);\r\n}\r\n.tcw-tree-dot {\r\n flex-shrink: 0;\r\n width: 8px;\r\n height: 8px;\r\n border-radius: 50%;\r\n}\r\n.tcw-tree-list {\r\n max-height: 200px;\r\n overflow-y: auto;\r\n}\r\n\r\n.tcw-tree-row {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: 8px;\r\n padding: 6px 8px;\r\n border-radius: var(--tcw-radius-sm);\r\n}\r\n.tcw-tree-row:hover {\r\n background: var(--tcw-bg-surface);\r\n}\r\n.tcw-tree-hidden .tcw-tree-name {\r\n opacity: 0.5;\r\n}\r\n.tcw-tree-name {\r\n min-width: 0;\r\n font-size: 12px;\r\n color: var(--tcw-text);\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n.tcw-tree-actions {\r\n display: flex;\r\n align-items: center;\r\n gap: 2px;\r\n flex-shrink: 0;\r\n}\r\n.tcw-tree-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 26px;\r\n height: 26px;\r\n border: none;\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n border-radius: var(--tcw-radius-sm);\r\n cursor: pointer;\r\n}\r\n.tcw-tree-btn:hover {\r\n background: var(--tcw-bg-elevated);\r\n color: var(--tcw-text);\r\n}\r\n.tcw-tree-btn.tcw-tree-on {\r\n color: var(--tcw-accent);\r\n}\r\n.tcw-tree-btn.tcw-tree-del:hover {\r\n background: var(--tcw-red-soft);\r\n color: var(--tcw-red);\r\n}\r\n.tcw-tree-empty {\r\n padding: 8px;\r\n font-size: 12px;\r\n color: var(--tcw-text-muted);\r\n}\r\n\r\n@media (max-width: 640px) {\r\n .tcw-tree-panel {\r\n top: auto;\r\n bottom: 0;\r\n left: 0;\r\n right: 0;\r\n width: 100vw;\r\n max-width: 100vw;\r\n border-radius: var(--tcw-radius-xl) var(--tcw-radius-xl) 0 0;\r\n }\r\n .tcw-tree-list {\r\n max-height: 30vh;\r\n }\r\n .tcw-tree-section:last-child {\r\n padding-bottom: max(6px, env(safe-area-inset-bottom));\r\n }\r\n}\r\n\r\n/* === Reduced motion === */\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n .tcw-root *,\r\n .tcw-root *::before,\r\n .tcw-root *::after {\r\n animation-duration: 0.01ms !important;\r\n animation-iteration-count: 1 !important;\r\n transition-duration: 0.01ms !important;\r\n }\r\n}\r\n", m = 0, h = "tcw-styles";
|
|
719
|
-
function
|
|
720
|
-
if (
|
|
695
|
+
}, m = "/* === TradeCanvas Widget — Premium Styles === */\n\n/*\n * The HTML `hidden` attribute only sets `display: none` via the UA stylesheet,\n * which any author `display` rule (e.g. `.tcw-alerts-panel { display: flex }`)\n * silently overrides — leaving panels stuck open when JS sets `el.hidden = true`.\n * Force `hidden` to win for every namespaced widget element. Scoped to `tcw-`\n * so it never touches the host page.\n */\n[class^=\"tcw-\"][hidden],\n[class*=\" tcw-\"][hidden] {\n display: none !important;\n}\n\n/* Control tooltips (WidgetTooltip): quick to appear, slide in from the control. */\n.tcw-tooltip {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 60;\n max-width: 240px;\n padding: 5px 8px;\n border-radius: var(--tcw-radius-sm);\n background: var(--tcw-tooltip-bg);\n color: var(--tcw-tooltip-fg);\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.28);\n width: max-content;\n font-size: 11.5px;\n font-weight: 500;\n line-height: 1.35;\n overflow-wrap: anywhere;\n pointer-events: none;\n opacity: 0;\n visibility: hidden;\n transform: translate(var(--tcw-tip-dx, 0), var(--tcw-tip-dy, 0));\n transition:\n opacity 110ms var(--tcw-ease-out),\n transform 110ms var(--tcw-ease-out),\n visibility 0s linear 110ms;\n}\n\n.tcw-tooltip--visible {\n opacity: 1;\n visibility: visible;\n transform: none;\n transition:\n opacity 110ms var(--tcw-ease-out),\n transform 110ms var(--tcw-ease-out),\n visibility 0s;\n}\n\n/* The modal portal: carries the tokens and fonts, but no box of its own. */\n.tcw-root.tcw-portal {\n display: contents;\n}\n\n/* Fullscreen: the root fills the screen; the chart resizes with it. */\n.tcw-root:fullscreen {\n width: 100vw;\n height: 100vh;\n}\n\n.tcw-root {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif;\n font-feature-settings: 'cv11', 'ss01', 'ss03';\n font-size: 13px;\n line-height: 1.4;\n color: #d9dde4;\n background: #080b10;\n position: relative;\n overflow: hidden;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n\n /* --- Dark theme tokens (default) --- */\n --tcw-bg: #080b10;\n --tcw-bg-surface: #0c1016;\n --tcw-bg-elevated: #141922;\n --tcw-bg-overlay: rgba(20, 25, 34, 0.5);\n --tcw-border: #1f2630;\n --tcw-border-strong: #2a323e;\n --tcw-tooltip-bg: #262e3a;\n --tcw-tooltip-fg: #eef0f4;\n --tcw-text: #e7e9ee;\n --tcw-text-dim: #aab1bd;\n --tcw-text-muted: #758091;\n\n /* TradeCanvas amber. --tcw-accent-ink is the text colour on an accent fill. */\n --tcw-accent: #f2a93b;\n --tcw-accent-hover: #f5b95c;\n --tcw-accent-ink: #1a1204;\n --tcw-accent-soft: rgba(242, 169, 59, 0.14);\n --tcw-accent-glow: rgba(242, 169, 59, 0.22);\n --tcw-accent-line: rgba(242, 169, 59, 0.55);\n\n --tcw-red: #e8505b;\n --tcw-red-soft: rgba(232, 80, 91, 0.12);\n --tcw-green: #1fa874;\n --tcw-green-soft: rgba(31, 168, 116, 0.12);\n --tcw-amber: #ff9f43;\n\n --tcw-hover-bg: rgba(255, 255, 255, 0.05);\n --tcw-active-bg: rgba(255, 255, 255, 0.08);\n --tcw-divider: rgba(255, 255, 255, 0.06);\n\n /* Motion */\n --tcw-ease: cubic-bezier(0.2, 0, 0, 1);\n --tcw-ease-out: cubic-bezier(0.16, 1, 0.3, 1);\n --tcw-dur-fast: 120ms;\n --tcw-dur-normal: 180ms;\n --tcw-dur-slow: 260ms;\n\n /* Shape */\n --tcw-radius-sm: 4px;\n --tcw-radius: 6px;\n --tcw-radius-lg: 10px;\n --tcw-radius-xl: 14px;\n\n /* Elevation */\n --tcw-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);\n --tcw-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35), 0 1px 3px rgba(0, 0, 0, 0.2);\n --tcw-shadow-lg: 0 12px 36px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.3);\n --tcw-shadow-xl: 0 24px 64px rgba(0, 0, 0, 0.55), 0 12px 24px rgba(0, 0, 0, 0.35);\n --tcw-ring: 0 0 0 2px rgba(242, 169, 59, 0.45);\n\n /* Type */\n --tcw-font-mono: 'JetBrains Mono', 'SF Mono', 'Cascadia Code', 'Fira Code', 'Consolas', ui-monospace, monospace;\n}\n\n.tcw-root[data-tcw-theme='light'] {\n color: #0c1016;\n background: #fcfcfd;\n\n --tcw-bg: #fcfcfd;\n --tcw-bg-surface: #ffffff;\n --tcw-bg-elevated: #ffffff;\n --tcw-bg-overlay: rgba(255, 255, 255, 0.75);\n --tcw-border: #e3e6eb;\n --tcw-border-strong: #d2d6dd;\n --tcw-tooltip-bg: #1f2630;\n --tcw-tooltip-fg: #f4f5f8;\n --tcw-text: #0c1016;\n --tcw-text-dim: #3f4754;\n --tcw-text-muted: #626a77;\n\n /* Deeper amber: readable as text on the light ground, white text readable on it. */\n --tcw-accent: #a0610a;\n --tcw-accent-hover: #874f07;\n --tcw-accent-ink: #ffffff;\n --tcw-accent-soft: rgba(160, 97, 10, 0.10);\n --tcw-accent-glow: rgba(160, 97, 10, 0.16);\n --tcw-accent-line: rgba(160, 97, 10, 0.45);\n --tcw-ring: 0 0 0 2px rgba(160, 97, 10, 0.4);\n\n /* Darker semantic colours: used as text on white. */\n --tcw-red: #c42f3e;\n --tcw-red-soft: rgba(196, 47, 62, 0.10);\n --tcw-green: #0b7d50;\n --tcw-green-soft: rgba(11, 125, 80, 0.10);\n\n --tcw-hover-bg: rgba(0, 0, 0, 0.04);\n --tcw-active-bg: rgba(160, 97, 10, 0.08);\n --tcw-divider: rgba(0, 0, 0, 0.06);\n\n --tcw-shadow-sm: 0 1px 2px rgba(20, 28, 56, 0.05);\n --tcw-shadow-md: 0 4px 12px rgba(20, 28, 56, 0.08), 0 1px 3px rgba(20, 28, 56, 0.04);\n --tcw-shadow-lg: 0 12px 36px rgba(20, 28, 56, 0.14), 0 4px 12px rgba(20, 28, 56, 0.08);\n --tcw-shadow-xl: 0 24px 64px rgba(20, 28, 56, 0.18), 0 12px 24px rgba(20, 28, 56, 0.1);\n}\n\n/* === Toolbar === */\n\n.tcw-toolbar {\n display: flex;\n align-items: center;\n gap: 0;\n padding: 0 10px;\n height: 44px;\n border-bottom: 1px solid var(--tcw-border);\n background:\n linear-gradient(180deg, var(--tcw-bg-elevated) 0%, var(--tcw-bg-surface) 100%);\n flex-shrink: 0;\n position: relative;\n z-index: 20;\n box-shadow: var(--tcw-shadow-sm);\n}\n\n.tcw-toolbar-symbol {\n font-size: 14px;\n font-weight: 650;\n color: var(--tcw-text);\n padding: 6px 12px;\n white-space: nowrap;\n letter-spacing: -0.015em;\n cursor: pointer;\n border: none;\n background: transparent;\n font-family: inherit;\n border-radius: var(--tcw-radius);\n transition: background var(--tcw-dur-fast) var(--tcw-ease);\n}\n\n.tcw-toolbar-symbol:hover {\n background: var(--tcw-hover-bg);\n}\n\n.tcw-toolbar-sep {\n width: 1px;\n height: 22px;\n background: var(--tcw-border);\n flex-shrink: 0;\n margin: 0 4px;\n}\n\n.tcw-toolbar-group {\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 0 4px;\n}\n\n.tcw-toolbar-spacer {\n flex: 1;\n}\n\n.tcw-btn {\n padding: 6px 10px;\n font-size: 12px;\n font-weight: 500;\n font-family: inherit;\n border: none;\n border-radius: var(--tcw-radius);\n background: transparent;\n color: var(--tcw-text-dim);\n cursor: pointer;\n transition:\n background var(--tcw-dur-fast) var(--tcw-ease),\n color var(--tcw-dur-fast) var(--tcw-ease),\n transform var(--tcw-dur-fast) var(--tcw-ease);\n white-space: nowrap;\n line-height: 1;\n letter-spacing: -0.005em;\n}\n\n.tcw-btn:hover {\n background: var(--tcw-hover-bg);\n color: var(--tcw-text);\n}\n\n.tcw-btn:active {\n transform: translateY(0.5px);\n}\n\n.tcw-btn.tcw-active {\n background: var(--tcw-accent-soft);\n color: var(--tcw-accent);\n font-weight: 600;\n box-shadow: inset 0 0 0 1px var(--tcw-accent-line);\n}\n\n.tcw-btn:focus-visible,\n.tcw-btn-icon:focus-visible,\n.tcw-sidebar-btn:focus-visible,\n.tcw-dropdown-trigger:focus-visible {\n outline: none;\n box-shadow: var(--tcw-ring);\n}\n\n.tcw-btn-icon {\n width: 32px;\n height: 32px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: inherit;\n border: none;\n border-radius: var(--tcw-radius);\n background: transparent;\n color: var(--tcw-text-muted);\n cursor: pointer;\n transition:\n background var(--tcw-dur-fast) var(--tcw-ease),\n color var(--tcw-dur-fast) var(--tcw-ease);\n line-height: 1;\n position: relative;\n}\n\n.tcw-btn-icon:hover {\n background: var(--tcw-hover-bg);\n color: var(--tcw-text);\n}\n\n.tcw-btn-icon svg {\n display: block;\n}\n\n.tcw-badge-count {\n font-size: 10px;\n font-weight: 600;\n color: var(--tcw-accent);\n background: var(--tcw-accent-soft);\n padding: 1px 5px;\n border-radius: 9px;\n font-family: var(--tcw-font-mono);\n}\n\n/* === Dropdown === */\n\n.tcw-dropdown-trigger {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 6px 10px;\n font-size: 12px;\n font-weight: 500;\n font-family: inherit;\n border: none;\n border-radius: var(--tcw-radius);\n background: transparent;\n color: var(--tcw-text-dim);\n cursor: pointer;\n transition: background var(--tcw-dur-fast) var(--tcw-ease), color var(--tcw-dur-fast) var(--tcw-ease);\n white-space: nowrap;\n line-height: 1;\n}\n\n.tcw-dropdown-trigger:hover {\n background: var(--tcw-hover-bg);\n color: var(--tcw-text);\n}\n\n.tcw-dropdown {\n position: absolute;\n top: 100%;\n margin-top: 6px;\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius-lg);\n box-shadow: var(--tcw-shadow-lg);\n z-index: 50;\n padding: 6px;\n max-height: 420px;\n overflow-y: auto;\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n animation: tcw-pop-in var(--tcw-dur-normal) var(--tcw-ease-out);\n}\n\n@keyframes tcw-pop-in {\n from { opacity: 0; transform: translateY(-4px) scale(0.98); }\n to { opacity: 1; transform: translateY(0) scale(1); }\n}\n\n.tcw-dropdown-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 7px 10px;\n font-size: 12.5px;\n color: var(--tcw-text-dim);\n cursor: pointer;\n transition: background var(--tcw-dur-fast) var(--tcw-ease), color var(--tcw-dur-fast) var(--tcw-ease);\n width: 100%;\n border: none;\n background: transparent;\n font-family: inherit;\n text-align: left;\n border-radius: var(--tcw-radius-sm);\n gap: 12px;\n}\n\n.tcw-dropdown-item:hover {\n background: var(--tcw-hover-bg);\n color: var(--tcw-text);\n}\n\n/* A menu row led by an icon (chart types): icon then label, not spread apart. */\n.tcw-dropdown-item--icon {\n justify-content: flex-start;\n gap: 10px;\n}\n\n.tcw-dropdown-item svg,\n.tcw-flyout-item svg {\n flex-shrink: 0;\n}\n\n/* The drawing-group button's icon holder: a plain box for the swapped SVG. */\n.tcw-sidebar-icon {\n display: inline-flex;\n}\n\n.tcw-dropdown-item.tcw-active {\n color: var(--tcw-accent);\n background: var(--tcw-accent-soft);\n font-weight: 600;\n}\n\n/* Timeframe menu: pick on the left, pin (star) on the right. */\n.tcw-tf-more {\n padding: 6px 5px;\n}\n\n.tcw-tf-more svg {\n display: block;\n}\n\n.tcw-tf-row {\n display: flex;\n align-items: center;\n gap: 2px;\n}\n\n.tcw-tf-row .tcw-dropdown-item {\n flex: 1;\n min-width: 0;\n}\n\n.tcw-tf-star {\n display: inline-grid;\n place-items: center;\n width: 26px;\n height: 26px;\n flex-shrink: 0;\n border: none;\n border-radius: var(--tcw-radius-sm);\n background: transparent;\n color: var(--tcw-text-muted);\n cursor: pointer;\n transition: color var(--tcw-dur-fast) var(--tcw-ease), background var(--tcw-dur-fast) var(--tcw-ease);\n}\n\n.tcw-tf-star:hover {\n background: var(--tcw-hover-bg);\n color: var(--tcw-text);\n}\n\n.tcw-tf-star.tcw-active {\n color: var(--tcw-accent);\n}\n\n.tcw-tf-star.tcw-active svg {\n fill: currentColor;\n}\n\n.tcw-tf-star:focus-visible {\n outline: 2px solid var(--tcw-accent);\n outline-offset: -2px;\n}\n\n.tcw-dropdown-label {\n text-transform: uppercase;\n font-size: 10px;\n color: var(--tcw-text-muted);\n letter-spacing: 0.08em;\n padding: 8px 10px 4px;\n font-weight: 600;\n}\n\n.tcw-dropdown-divider {\n border-top: 1px solid var(--tcw-divider);\n margin: 6px 4px;\n}\n\n.tcw-tag {\n font-size: 10px;\n font-family: var(--tcw-font-mono);\n color: var(--tcw-text-muted);\n padding: 2px 6px;\n border-radius: 4px;\n background: var(--tcw-hover-bg);\n letter-spacing: 0.02em;\n}\n\n/* === Body === */\n\n.tcw-body {\n display: flex;\n flex: 1;\n min-height: 0;\n background: var(--tcw-bg);\n}\n\n.tcw-chart-container {\n flex: 1;\n min-width: 0;\n min-height: 0;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n/* === Loading overlay (candle skeleton) === */\n\n.tcw-loading-overlay {\n /* Uses a modifier class rather than the `hidden` attribute — the global\n `[hidden] { display: none !important }` rule above would cut the\n opacity transition off before it can play. */\n position: absolute;\n inset: 0;\n z-index: 5;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--tcw-bg);\n opacity: 1;\n transition: opacity var(--tcw-dur-slow) var(--tcw-ease-out);\n pointer-events: none;\n}\n\n.tcw-loading-overlay--hidden {\n opacity: 0;\n}\n\n.tcw-loading-card {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 14px;\n padding: 22px 26px 20px;\n border: 1px solid transparent;\n border-radius: var(--tcw-radius-lg);\n}\n\n/* A slow switch: the previous chart stays readable underneath, dimmed, so the\n click visibly registered without blanking the screen. */\n.tcw-loading-overlay--veil {\n background: color-mix(in srgb, var(--tcw-bg) 55%, transparent);\n}\n\n.tcw-loading-overlay--veil .tcw-loading-card {\n background: var(--tcw-bg-elevated);\n border-color: var(--tcw-border);\n box-shadow: var(--tcw-shadow-lg);\n}\n\n.tcw-loading-candles {\n position: relative;\n display: flex;\n gap: 7px;\n height: 64px;\n}\n\n/* One candle: the element is the wick's column, ::before the wick, ::after the body. */\n.tcw-loading-candle {\n position: relative;\n width: 8px;\n height: 100%;\n color: var(--tcw-red);\n opacity: 0.28;\n animation: tcw-candle-light 1.6s var(--tcw-ease) infinite;\n animation-delay: var(--tcw-delay, 0ms);\n}\n\n.tcw-loading-candle--up { color: var(--tcw-green); }\n\n.tcw-loading-candle::before,\n.tcw-loading-candle::after {\n content: '';\n position: absolute;\n left: 50%;\n background: currentColor;\n transform: translateX(-50%);\n}\n\n.tcw-loading-candle::before {\n top: var(--tcw-wick-top);\n width: 1.5px;\n height: var(--tcw-wick-h);\n}\n\n.tcw-loading-candle::after {\n top: var(--tcw-body-top);\n width: 8px;\n height: var(--tcw-body-h);\n border-radius: 1.5px;\n}\n\n/* Each candle brightens and lifts slightly in turn, left to right. */\n@keyframes tcw-candle-light {\n 0%, 55%, 100% { opacity: 0.28; transform: translateY(0); }\n 20% { opacity: 1; transform: translateY(-2px); }\n}\n\n.tcw-loading-track {\n position: relative;\n width: 100%;\n min-width: 88px;\n height: 2px;\n border-radius: 1px;\n background: var(--tcw-border);\n overflow: hidden;\n}\n\n.tcw-loading-track > span {\n position: absolute;\n inset: 0;\n width: 40%;\n border-radius: 1px;\n background: linear-gradient(90deg, transparent, var(--tcw-accent), transparent);\n animation: tcw-loading-sweep 1.6s var(--tcw-ease) infinite;\n}\n\n@keyframes tcw-loading-sweep {\n from { transform: translateX(-100%); }\n to { transform: translateX(250%); }\n}\n\n/* The status text is announced, not shown — the candles say \"loading\". */\n.tcw-loading-label {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n.tcw-loading-overlay--error .tcw-loading-candles,\n.tcw-loading-overlay--error .tcw-loading-track {\n display: none;\n}\n\n/* A failure is shown: why the chart is empty and that a retry is running. */\n.tcw-loading-overlay--error .tcw-loading-label {\n position: static;\n width: auto;\n height: auto;\n overflow: visible;\n clip-path: none;\n white-space: normal;\n max-width: 320px;\n text-align: center;\n color: var(--tcw-red);\n font-size: 13px;\n}\n\n/* Reduced motion: nothing slides or lifts, but the candles still glow in\n turn and the track breathes, so the wait never looks frozen. */\n@media (prefers-reduced-motion: reduce) {\n .tcw-loading-candle {\n animation: tcw-candle-glow 2.4s ease-in-out infinite;\n animation-delay: var(--tcw-delay, 0ms);\n }\n .tcw-loading-track > span {\n width: 100%;\n background: var(--tcw-accent);\n animation: tcw-loading-breathe 2.4s ease-in-out infinite;\n }\n}\n\n@keyframes tcw-candle-glow {\n 0%, 60%, 100% { opacity: 0.28; }\n 25% { opacity: 0.9; }\n}\n\n@keyframes tcw-loading-breathe {\n 0%, 100% { opacity: 0.15; }\n 50% { opacity: 0.7; }\n}\n\n/* === On-chart indicator legend (WidgetIndicatorLegend) === */\n\n.tcw-ind-legend {\n position: absolute;\n z-index: 3;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 1px;\n max-width: calc(100% - 140px);\n pointer-events: none;\n}\n\n.tcw-ind-legend--collapsed > .tcw-ind-legend-row {\n display: none;\n}\n\n.tcw-ind-legend-row {\n display: flex;\n align-items: center;\n gap: 8px;\n max-width: 100%;\n padding: 0 4px;\n border-radius: var(--tcw-radius-sm);\n font-size: 11.5px;\n line-height: 19px;\n font-feature-settings: 'tnum';\n white-space: nowrap;\n color: var(--tcw-text-dim);\n pointer-events: auto;\n cursor: default;\n /* A narrow pane: the values shrink first, then the row is clipped. */\n overflow: hidden;\n}\n\n/* A pane's indicator: at the top of its pane, where the canvas title was. */\n.tcw-ind-legend-row--pane {\n position: absolute;\n z-index: 3;\n}\n\n.tcw-ind-legend-row:hover,\n.tcw-ind-legend-row:focus-within {\n background: color-mix(in srgb, var(--tcw-bg-elevated) 90%, transparent);\n box-shadow: 0 0 0 1px var(--tcw-border);\n}\n\n.tcw-ind-legend-name {\n padding: 0;\n border: none;\n background: none;\n font: inherit;\n font-weight: 600;\n color: var(--tcw-text);\n cursor: pointer;\n}\n\n.tcw-ind-legend-name:hover {\n color: var(--tcw-accent);\n}\n\n.tcw-ind-legend-values {\n display: inline-flex;\n gap: 7px;\n min-width: 0;\n overflow: hidden;\n}\n\n.tcw-ind-legend-row--hidden .tcw-ind-legend-name,\n.tcw-ind-legend-row--hidden .tcw-ind-legend-values {\n opacity: 0.45;\n}\n\n/* Show / settings / remove: on hover or keyboard focus (always on touch). */\n.tcw-ind-legend-actions {\n display: none;\n gap: 1px;\n margin-left: -2px;\n}\n\n.tcw-ind-legend-row:hover .tcw-ind-legend-actions,\n.tcw-ind-legend-row:focus-within .tcw-ind-legend-actions {\n display: inline-flex;\n}\n\n@media (hover: none) {\n .tcw-ind-legend-actions {\n display: inline-flex;\n }\n\n /* Finger-sized targets. */\n .tcw-ind-legend-act,\n .tcw-ind-legend-toggle {\n min-width: 28px;\n min-height: 24px;\n }\n}\n\n.tcw-ind-legend-act {\n display: inline-grid;\n place-items: center;\n width: 20px;\n height: 18px;\n padding: 0;\n border: none;\n border-radius: var(--tcw-radius-sm);\n background: transparent;\n color: var(--tcw-text-muted);\n cursor: pointer;\n}\n\n.tcw-ind-legend-act:hover {\n color: var(--tcw-text);\n background: var(--tcw-hover-bg);\n}\n\n.tcw-ind-legend-act[data-act='remove']:hover {\n color: var(--tcw-red);\n}\n\n.tcw-ind-legend-toggle {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n margin-top: 1px;\n padding: 1px 5px;\n border: none;\n border-radius: var(--tcw-radius-sm);\n background: transparent;\n color: var(--tcw-text-muted);\n font: inherit;\n font-size: 11px;\n cursor: pointer;\n pointer-events: auto;\n}\n\n.tcw-ind-legend-toggle[hidden] {\n display: none;\n}\n\n.tcw-ind-legend-toggle:hover {\n color: var(--tcw-text);\n background: var(--tcw-hover-bg);\n}\n\n.tcw-ind-legend-name:focus-visible,\n.tcw-ind-legend-act:focus-visible,\n.tcw-ind-legend-toggle:focus-visible {\n outline: 2px solid var(--tcw-accent);\n outline-offset: -2px;\n}\n\n/* === Sidebar === */\n\n.tcw-sidebar {\n width: 40px;\n flex-shrink: 0;\n background: linear-gradient(180deg, var(--tcw-bg-elevated) 0%, var(--tcw-bg-surface) 100%);\n border-right: 1px solid var(--tcw-border);\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 6px 0;\n overflow: visible;\n box-shadow: var(--tcw-shadow-sm);\n}\n\n.tcw-tool-group-wrap {\n position: relative;\n}\n\n.tcw-sidebar-btn {\n width: 32px;\n height: 32px;\n margin: 1px 0;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: transparent;\n color: var(--tcw-text-muted);\n cursor: pointer;\n font-family: inherit;\n transition:\n background var(--tcw-dur-fast) var(--tcw-ease),\n color var(--tcw-dur-fast) var(--tcw-ease);\n position: relative;\n flex-shrink: 0;\n line-height: 1;\n border-radius: var(--tcw-radius);\n}\n\n.tcw-sidebar-btn:hover {\n color: var(--tcw-text);\n background: var(--tcw-hover-bg);\n}\n\n.tcw-sidebar-btn.tcw-active {\n color: var(--tcw-accent);\n background: var(--tcw-accent-soft);\n box-shadow: inset 2px 0 0 var(--tcw-accent);\n}\n\n.tcw-sidebar-btn.tcw-danger:hover {\n color: var(--tcw-red);\n background: var(--tcw-red-soft);\n}\n\n.tcw-multi-dot {\n position: absolute;\n bottom: 3px;\n right: 3px;\n width: 4px;\n height: 4px;\n border-radius: 50%;\n background: var(--tcw-text-muted);\n opacity: 0.7;\n}\n\n.tcw-sidebar-divider {\n width: 22px;\n height: 1px;\n background: var(--tcw-divider);\n margin: 6px 0;\n flex-shrink: 0;\n}\n\n.tcw-sidebar-spacer {\n flex: 1;\n}\n\n/* === Flyout === */\n\n.tcw-flyout {\n position: absolute;\n left: 100%;\n top: 0;\n margin-left: 6px;\n min-width: 180px;\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius-lg);\n box-shadow: var(--tcw-shadow-lg);\n z-index: 30;\n padding: 6px;\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n animation: tcw-slide-right var(--tcw-dur-normal) var(--tcw-ease-out);\n}\n\n/* Spans the gap to the toolbar button: crossing it stays inside the menu. */\n.tcw-flyout::before {\n content: '';\n position: absolute;\n top: -1px;\n bottom: -1px;\n right: 100%;\n width: 8px;\n}\n\n@keyframes tcw-slide-right {\n from { opacity: 0; transform: translateX(-4px); }\n to { opacity: 1; transform: translateX(0); }\n}\n\n.tcw-flyout-header {\n text-transform: uppercase;\n font-size: 10px;\n color: var(--tcw-text-muted);\n letter-spacing: 0.08em;\n padding: 6px 10px 4px;\n font-weight: 600;\n}\n\n.tcw-flyout-hint {\n padding: 6px 10px 2px;\n border-top: 1px solid var(--tcw-border);\n margin-top: 4px;\n font-size: 10.5px;\n color: var(--tcw-text-muted);\n}\n\n.tcw-flyout-item {\n padding: 7px 10px;\n font-size: 12.5px;\n color: var(--tcw-text-dim);\n cursor: pointer;\n width: 100%;\n display: flex;\n align-items: center;\n gap: 10px;\n border: none;\n background: transparent;\n text-align: left;\n font-family: inherit;\n border-radius: var(--tcw-radius-sm);\n transition: background var(--tcw-dur-fast) var(--tcw-ease), color var(--tcw-dur-fast) var(--tcw-ease);\n}\n\n.tcw-flyout-item:hover {\n background: var(--tcw-hover-bg);\n color: var(--tcw-text);\n}\n\n.tcw-flyout-item.tcw-active {\n color: var(--tcw-accent);\n background: var(--tcw-accent-soft);\n font-weight: 600;\n}\n\n/* Pinned (favorited) flyout item — a star marker on the right. */\n.tcw-flyout-item.tcw-flyout-faved::after {\n content: '★';\n float: right;\n color: var(--tcw-accent);\n font-size: 11px;\n line-height: 1.4;\n}\n\n/* Favorites strip — pinned drawing tools above the tool groups. */\n.tcw-sidebar-favorites {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.tcw-sidebar-favorites .tcw-sidebar-btn {\n position: relative;\n}\n.tcw-sidebar-favorites .tcw-sidebar-btn::before {\n content: '';\n position: absolute;\n top: 3px;\n right: 3px;\n width: 4px;\n height: 4px;\n border-radius: 50%;\n background: var(--tcw-accent);\n opacity: 0.7;\n}\n\n/* === Modal === */\n\n.tcw-modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(8, 10, 18, 0.55);\n backdrop-filter: blur(6px) saturate(140%);\n -webkit-backdrop-filter: blur(6px) saturate(140%);\n z-index: 100;\n animation: tcw-fade-in var(--tcw-dur-normal) var(--tcw-ease-out);\n}\n\n@keyframes tcw-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.tcw-modal {\n position: fixed;\n z-index: 101;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius-xl);\n width: 520px;\n max-width: 92vw;\n max-height: 82vh;\n display: flex;\n flex-direction: column;\n box-shadow: var(--tcw-shadow-xl);\n animation: tcw-modal-in var(--tcw-dur-slow) var(--tcw-ease-out);\n}\n\n@keyframes tcw-modal-in {\n from { opacity: 0; transform: translate(-50%, -48%) scale(0.96); }\n to { opacity: 1; transform: translate(-50%, -50%) scale(1); }\n}\n\n.tcw-modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 18px 22px;\n border-bottom: 1px solid var(--tcw-divider);\n}\n\n.tcw-modal-header h3 {\n font-size: 15px;\n font-weight: 650;\n margin: 0;\n color: var(--tcw-text);\n letter-spacing: -0.015em;\n}\n\n.tcw-modal-close {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: transparent;\n color: var(--tcw-text-muted);\n cursor: pointer;\n border-radius: var(--tcw-radius);\n transition: background var(--tcw-dur-fast) var(--tcw-ease), color var(--tcw-dur-fast) var(--tcw-ease);\n}\n\n.tcw-modal-close:hover {\n background: var(--tcw-hover-bg);\n color: var(--tcw-text);\n}\n\n.tcw-modal-tabs {\n display: flex;\n gap: 4px;\n padding: 0 18px;\n border-bottom: 1px solid var(--tcw-divider);\n}\n\n.tcw-modal-tab {\n padding: 11px 14px;\n font-size: 12px;\n font-weight: 500;\n color: var(--tcw-text-muted);\n border: none;\n background: transparent;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -1px;\n transition: color var(--tcw-dur-fast) var(--tcw-ease), border-color var(--tcw-dur-fast) var(--tcw-ease);\n font-family: inherit;\n text-transform: capitalize;\n letter-spacing: -0.005em;\n}\n\n.tcw-modal-tab:hover {\n color: var(--tcw-text-dim);\n}\n\n.tcw-modal-tab.tcw-active {\n color: var(--tcw-accent);\n border-bottom-color: var(--tcw-accent);\n font-weight: 600;\n}\n\n.tcw-modal-body {\n padding: 20px 22px;\n overflow-y: auto;\n flex: 1;\n}\n\n.tcw-settings-section {\n margin-bottom: 22px;\n}\n\n.tcw-settings-section:last-child {\n margin-bottom: 0;\n}\n\n.tcw-settings-section-title {\n font-size: 10.5px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.09em;\n color: var(--tcw-text-muted);\n margin-bottom: 12px;\n}\n\n.tcw-settings-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 0;\n}\n\n.tcw-settings-row + .tcw-settings-row {\n border-top: 1px solid var(--tcw-divider);\n}\n\n.tcw-settings-label {\n font-size: 13px;\n color: var(--tcw-text-dim);\n letter-spacing: -0.005em;\n}\n\n.tcw-color-picker-wrap {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.tcw-color-picker-wrap input[type='color'] {\n width: 28px;\n height: 28px;\n border: 1px solid var(--tcw-border-strong);\n border-radius: var(--tcw-radius-sm);\n background: transparent;\n cursor: pointer;\n padding: 2px;\n}\n\n.tcw-color-picker-wrap input[type='color']::-webkit-color-swatch-wrapper {\n padding: 0;\n}\n\n.tcw-color-picker-wrap input[type='color']::-webkit-color-swatch {\n border: none;\n border-radius: 2px;\n}\n\n.tcw-color-hex {\n font-size: 11px;\n font-family: var(--tcw-font-mono);\n color: var(--tcw-text-muted);\n min-width: 64px;\n letter-spacing: 0.01em;\n}\n\n.tcw-toggle {\n width: 36px;\n height: 20px;\n background: rgba(255, 255, 255, 0.12);\n border-radius: 10px;\n position: relative;\n cursor: pointer;\n transition: background var(--tcw-dur-fast) var(--tcw-ease);\n border: none;\n padding: 0;\n flex-shrink: 0;\n}\n\n.tcw-toggle::after {\n content: '';\n position: absolute;\n top: 2px;\n left: 2px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--tcw-text-muted);\n transition:\n left var(--tcw-dur-normal) var(--tcw-ease-out),\n background var(--tcw-dur-fast) var(--tcw-ease);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);\n}\n\n.tcw-toggle.tcw-on {\n background: var(--tcw-accent);\n box-shadow: 0 0 0 2px var(--tcw-accent-glow);\n}\n\n.tcw-toggle.tcw-on::after {\n left: 18px;\n background: #fff;\n}\n\n.tcw-root[data-tcw-theme='light'] .tcw-toggle {\n background: rgba(0, 0, 0, 0.12);\n}\n\n.tcw-root[data-tcw-theme='light'] .tcw-toggle::after {\n background: #999;\n}\n\n.tcw-root[data-tcw-theme='light'] .tcw-toggle.tcw-on {\n background: var(--tcw-accent);\n}\n\n.tcw-root[data-tcw-theme='light'] .tcw-toggle.tcw-on::after {\n background: #fff;\n}\n\n.tcw-settings-select {\n padding: 6px 10px;\n font-size: 12px;\n font-family: inherit;\n background: var(--tcw-bg-surface);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius);\n color: var(--tcw-text);\n cursor: pointer;\n transition: border-color var(--tcw-dur-fast) var(--tcw-ease);\n}\n\n.tcw-settings-select:hover {\n border-color: var(--tcw-border-strong);\n}\n\n.tcw-settings-select:focus-visible {\n outline: none;\n border-color: var(--tcw-accent);\n box-shadow: var(--tcw-ring);\n}\n\n.tcw-modal-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 22px;\n border-top: 1px solid var(--tcw-divider);\n background: var(--tcw-bg-surface);\n border-radius: 0 0 var(--tcw-radius-xl) var(--tcw-radius-xl);\n}\n\n.tcw-reset-link {\n font-size: 12px;\n color: var(--tcw-text-muted);\n cursor: pointer;\n border: none;\n background: transparent;\n font-family: inherit;\n text-decoration: underline;\n text-underline-offset: 2px;\n text-decoration-color: var(--tcw-border-strong);\n transition: color var(--tcw-dur-fast) var(--tcw-ease), text-decoration-color var(--tcw-dur-fast) var(--tcw-ease);\n}\n\n.tcw-reset-link:hover {\n color: var(--tcw-text-dim);\n text-decoration-color: currentColor;\n}\n\n.tcw-done-btn {\n padding: 8px 18px;\n font-size: 12.5px;\n font-weight: 600;\n background: var(--tcw-accent);\n color: var(--tcw-accent-ink);\n border: none;\n border-radius: var(--tcw-radius);\n cursor: pointer;\n font-family: inherit;\n transition:\n background var(--tcw-dur-fast) var(--tcw-ease),\n transform var(--tcw-dur-fast) var(--tcw-ease),\n box-shadow var(--tcw-dur-fast) var(--tcw-ease);\n letter-spacing: -0.005em;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.15);\n}\n\n.tcw-done-btn:hover {\n background: var(--tcw-accent-hover);\n box-shadow: var(--tcw-shadow-md), 0 0 0 3px var(--tcw-accent-glow);\n}\n\n.tcw-done-btn:active {\n transform: translateY(0.5px);\n}\n\n/* === Status Bar === */\n\n.tcw-statusbar {\n /* Above sidebar buttons that overflow a short chart; menus (z 30+) stay on top. */\n position: relative;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 7px 16px;\n border-top: 1px solid var(--tcw-border);\n background: var(--tcw-bg-surface);\n font-size: 11px;\n color: var(--tcw-text-muted);\n flex-shrink: 0;\n font-feature-settings: 'tnum';\n}\n\n.tcw-status-right {\n display: flex;\n align-items: center;\n gap: 14px;\n margin-left: auto;\n min-width: 0;\n white-space: nowrap;\n}\n\n/* Narrow screens: the toolbar already names the symbol and timeframe, so the\n range bar gets the room. */\n@media (max-width: 640px) {\n .tcw-status-info {\n display: none;\n }\n\n .tcw-range-btn {\n padding: 4px 5px;\n }\n}\n\n/* Range presets + go to date, bottom left */\n.tcw-range-bar {\n display: flex;\n align-items: center;\n gap: 1px;\n margin: -4px 12px -4px -8px;\n min-width: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n\n.tcw-range-bar::-webkit-scrollbar {\n display: none;\n}\n\n.tcw-range-btn {\n flex-shrink: 0;\n padding: 4px 7px;\n font: inherit;\n font-weight: 600;\n letter-spacing: 0.02em;\n color: var(--tcw-text-muted);\n background: transparent;\n border: none;\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n transition: color var(--tcw-dur-fast) var(--tcw-ease), background var(--tcw-dur-fast) var(--tcw-ease);\n}\n\n.tcw-range-btn:hover {\n color: var(--tcw-text);\n background: var(--tcw-hover-bg);\n}\n\n.tcw-range-btn:active {\n color: var(--tcw-accent);\n}\n\n.tcw-range-btn:focus-visible {\n outline: 2px solid var(--tcw-accent);\n outline-offset: -2px;\n}\n\n.tcw-range-goto {\n display: inline-grid;\n place-items: center;\n margin-left: 6px;\n padding: 4px 6px;\n}\n\n.tcw-range-goto svg {\n display: block;\n}\n\n/* Go-to-date popover, anchored above the range bar */\n.tcw-goto {\n position: absolute;\n left: 10px;\n bottom: 40px;\n z-index: 40;\n display: grid;\n gap: 10px;\n width: 232px;\n padding: 14px;\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius);\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);\n color: var(--tcw-text);\n font-size: 12px;\n}\n\n.tcw-goto-title {\n font-weight: 600;\n font-size: 13px;\n}\n\n.tcw-goto-field {\n display: grid;\n gap: 4px;\n color: var(--tcw-text-muted);\n font-size: 11px;\n}\n\n.tcw-goto-input {\n height: 30px;\n padding: 0 8px;\n font: inherit;\n font-size: 12.5px;\n color: var(--tcw-text);\n background: var(--tcw-bg);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius-sm);\n color-scheme: dark;\n}\n\n.tcw-root[data-tcw-theme='light'] .tcw-goto-input {\n color-scheme: light;\n}\n\n.tcw-goto-input:focus {\n outline: none;\n border-color: var(--tcw-accent);\n box-shadow: 0 0 0 2px var(--tcw-accent-soft);\n}\n\n.tcw-goto-actions {\n display: flex;\n justify-content: flex-end;\n gap: 6px;\n}\n\n.tcw-goto-submit {\n background: var(--tcw-accent);\n color: var(--tcw-accent-ink);\n font-weight: 600;\n}\n\n.tcw-goto-submit:hover {\n background: var(--tcw-accent);\n color: var(--tcw-accent-ink);\n filter: brightness(1.08);\n}\n\n.tcw-status-indicator {\n display: flex;\n align-items: center;\n gap: 7px;\n font-weight: 500;\n}\n\n.tcw-status-dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--tcw-text-muted);\n transition: background var(--tcw-dur-normal) var(--tcw-ease), box-shadow var(--tcw-dur-normal) var(--tcw-ease);\n position: relative;\n}\n\n.tcw-status-dot.tcw-connected {\n background: var(--tcw-green);\n box-shadow: 0 0 0 3px rgba(31, 168, 116, 0.18);\n}\n\n.tcw-status-dot.tcw-connected::after {\n content: '';\n position: absolute;\n inset: -3px;\n border-radius: 50%;\n border: 2px solid var(--tcw-green);\n opacity: 0;\n animation: tcw-pulse 2.2s ease-out infinite;\n}\n\n@keyframes tcw-pulse {\n 0% { opacity: 0.6; transform: scale(0.6); }\n 100% { opacity: 0; transform: scale(2.2); }\n}\n\n.tcw-status-dot.tcw-error {\n background: var(--tcw-red);\n box-shadow: 0 0 0 3px rgba(232, 80, 91, 0.2);\n}\n\n/* === Command Palette === */\n\n.tcw-cmd-palette {\n position: fixed;\n z-index: 102;\n top: 18%;\n left: 50%;\n transform: translateX(-50%);\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius-xl);\n width: 560px;\n max-width: 92vw;\n max-height: 62vh;\n display: flex;\n flex-direction: column;\n box-shadow: var(--tcw-shadow-xl);\n overflow: hidden;\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n animation: tcw-modal-in var(--tcw-dur-slow) var(--tcw-ease-out);\n}\n\n.tcw-cmd-header {\n display: flex;\n align-items: center;\n padding: 14px 18px;\n border-bottom: 1px solid var(--tcw-divider);\n gap: 12px;\n}\n\n.tcw-cmd-icon {\n font-size: 18px;\n color: var(--tcw-text-muted);\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n}\n\n.tcw-cmd-input {\n flex: 1;\n background: transparent;\n border: none;\n outline: none;\n font-size: 14.5px;\n font-family: inherit;\n color: var(--tcw-text);\n caret-color: var(--tcw-accent);\n letter-spacing: -0.005em;\n}\n\n.tcw-cmd-input::placeholder {\n color: var(--tcw-text-muted);\n}\n\n.tcw-cmd-hint {\n font-size: 10px;\n color: var(--tcw-text-muted);\n font-family: var(--tcw-font-mono);\n border: 1px solid var(--tcw-border);\n background: var(--tcw-bg-surface);\n padding: 3px 7px;\n border-radius: 4px;\n flex-shrink: 0;\n box-shadow: inset 0 -1px 0 var(--tcw-border);\n}\n\n.tcw-cmd-list {\n overflow-y: auto;\n flex: 1;\n padding: 6px;\n max-height: 380px;\n}\n\n.tcw-cmd-category {\n text-transform: uppercase;\n font-size: 10px;\n color: var(--tcw-text-muted);\n letter-spacing: 0.09em;\n padding: 10px 10px 4px;\n font-weight: 600;\n}\n\n.tcw-cmd-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 9px 12px;\n font-size: 13px;\n color: var(--tcw-text-dim);\n cursor: pointer;\n width: 100%;\n border: none;\n background: transparent;\n font-family: inherit;\n text-align: left;\n transition: background var(--tcw-dur-fast) var(--tcw-ease), color var(--tcw-dur-fast) var(--tcw-ease);\n gap: 10px;\n border-radius: var(--tcw-radius);\n letter-spacing: -0.005em;\n}\n\n.tcw-cmd-item:hover,\n.tcw-cmd-item.tcw-selected {\n background: var(--tcw-hover-bg);\n color: var(--tcw-text);\n}\n\n.tcw-cmd-item.tcw-selected {\n box-shadow: inset 2px 0 0 var(--tcw-accent);\n}\n\n.tcw-cmd-item.tcw-active .tcw-cmd-item-label {\n color: var(--tcw-accent);\n font-weight: 600;\n}\n\n.tcw-cmd-item-label {\n flex: 1;\n}\n\n.tcw-cmd-check {\n color: var(--tcw-accent);\n font-size: 14px;\n font-weight: 700;\n}\n\n.tcw-cmd-kbd {\n font-size: 10px;\n font-family: var(--tcw-font-mono);\n color: var(--tcw-text-muted);\n background: var(--tcw-bg-surface);\n border: 1px solid var(--tcw-border);\n padding: 2px 6px;\n border-radius: 4px;\n box-shadow: inset 0 -1px 0 var(--tcw-border);\n}\n\n.tcw-cmd-empty {\n padding: 32px 16px;\n text-align: center;\n color: var(--tcw-text-muted);\n font-size: 13px;\n}\n\n.tcw-cmd-footer {\n display: flex;\n align-items: center;\n gap: 18px;\n padding: 10px 18px;\n border-top: 1px solid var(--tcw-divider);\n font-size: 11px;\n color: var(--tcw-text-muted);\n background: var(--tcw-bg-surface);\n}\n\n/* === Watchlist === */\n\n.tcw-watchlist {\n width: 220px;\n flex-shrink: 0;\n background: var(--tcw-bg-surface);\n border-left: 1px solid var(--tcw-border);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n box-shadow: var(--tcw-shadow-sm);\n}\n\n.tcw-watchlist-header {\n padding: 10px 14px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--tcw-text-muted);\n border-bottom: 1px solid var(--tcw-divider);\n}\n\n.tcw-watchlist-list {\n flex: 1;\n overflow-y: auto;\n padding: 4px 0;\n}\n\n.tcw-watchlist-row {\n display: grid;\n grid-template-columns: 1fr auto auto;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n cursor: pointer;\n transition: background var(--tcw-dur-fast) var(--tcw-ease);\n border-left: 2px solid transparent;\n outline: none;\n}\n\n.tcw-watchlist-row:hover {\n background: var(--tcw-hover-bg);\n}\n\n.tcw-watchlist-row:focus-visible {\n background: var(--tcw-hover-bg);\n border-left-color: var(--tcw-accent);\n}\n\n.tcw-watchlist-row.tcw-watchlist-active {\n background: var(--tcw-accent-soft);\n border-left-color: var(--tcw-accent);\n}\n\n.tcw-watchlist-symbol {\n font-size: 12.5px;\n font-weight: 600;\n color: var(--tcw-text);\n letter-spacing: -0.005em;\n white-space: nowrap;\n}\n\n.tcw-watchlist-spark {\n width: 56px;\n height: 22px;\n align-self: center;\n}\n\n.tcw-watchlist-stats {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 1px;\n font-variant-numeric: tabular-nums;\n font-family: var(--tcw-font-mono);\n}\n\n.tcw-watchlist-price {\n font-size: 12px;\n color: var(--tcw-text);\n font-weight: 500;\n}\n\n.tcw-watchlist-change {\n font-size: 10.5px;\n color: var(--tcw-text-muted);\n font-weight: 500;\n}\n\n.tcw-watchlist-change.tcw-up {\n color: var(--tcw-green);\n}\n\n.tcw-watchlist-change.tcw-down {\n color: var(--tcw-red);\n}\n\n@media (max-width: 768px) {\n .tcw-watchlist {\n display: none;\n }\n}\n\n/* === Toast === */\n\n.tcw-toast {\n position: absolute;\n bottom: 60px;\n left: 50%;\n transform: translate(-50%, 12px);\n z-index: 40;\n padding: 9px 14px;\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border-strong);\n border-radius: 9999px;\n box-shadow: var(--tcw-shadow-lg);\n font-size: 12.5px;\n font-weight: 500;\n color: var(--tcw-text);\n opacity: 0;\n transition: opacity var(--tcw-dur-normal) var(--tcw-ease-out), transform var(--tcw-dur-normal) var(--tcw-ease-out);\n pointer-events: none;\n max-width: calc(100% - 32px);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n letter-spacing: -0.005em;\n}\n\n.tcw-toast.tcw-toast-in {\n opacity: 1;\n transform: translate(-50%, 0);\n}\n\n.tcw-toast-error {\n border-color: var(--tcw-red);\n color: var(--tcw-red);\n}\n\n/* === Replay scrubber === */\n\n/* Picking the replay start: the bars right of the pointer would be hidden. */\n.tcw-replay-shade {\n position: absolute;\n z-index: 4;\n pointer-events: none;\n background: color-mix(in srgb, var(--tcw-bg) 62%, transparent);\n border-left: 1.5px dashed var(--tcw-accent);\n}\n\n.tcw-replay-group {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.tcw-replay-group[hidden] { display: none; }\n\n.tcw-replay-hint {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 0 4px;\n color: var(--tcw-text-dim);\n white-space: nowrap;\n}\n\n.tcw-replay-text-btn {\n height: 26px;\n padding: 0 10px;\n border: 1px solid var(--tcw-border);\n border-radius: 9999px;\n background: transparent;\n color: var(--tcw-text);\n font: inherit;\n font-weight: 500;\n white-space: nowrap;\n cursor: pointer;\n transition: background var(--tcw-dur-fast), border-color var(--tcw-dur-fast), color var(--tcw-dur-fast);\n}\n\n.tcw-replay-text-btn:hover { background: var(--tcw-hover-bg); border-color: var(--tcw-border-strong); }\n.tcw-replay-text-btn:focus-visible { outline: none; box-shadow: var(--tcw-ring); }\n\n/* At the last bar: the way out is the next thing to do. */\n.tcw-replay-bar--ended .tcw-replay-realtime {\n background: var(--tcw-accent);\n border-color: var(--tcw-accent);\n color: var(--tcw-accent-ink);\n}\n\n.tcw-replay-btn:disabled { opacity: 0.4; cursor: default; }\n\n@media (max-width: 640px) {\n .tcw-replay-bar {\n flex-wrap: wrap;\n justify-content: center;\n max-width: calc(100% - 24px);\n border-radius: 14px;\n }\n .tcw-replay-hint { white-space: normal; }\n}\n\n.tcw-replay-bar {\n position: absolute;\n left: 50%;\n bottom: 16px;\n transform: translateX(-50%);\n z-index: 25;\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border);\n border-radius: 9999px;\n box-shadow: var(--tcw-shadow-lg);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n font-size: 12px;\n color: var(--tcw-text);\n animation: tcw-pop-in var(--tcw-dur-normal) var(--tcw-ease-out);\n}\n\n.tcw-replay-btn {\n width: 28px;\n height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: transparent;\n color: var(--tcw-text-dim);\n border-radius: 9999px;\n cursor: pointer;\n font-family: inherit;\n transition: background var(--tcw-dur-fast) var(--tcw-ease), color var(--tcw-dur-fast) var(--tcw-ease);\n}\n\n.tcw-replay-btn:hover {\n background: var(--tcw-hover-bg);\n color: var(--tcw-text);\n}\n\n.tcw-replay-btn.tcw-replay-play {\n background: var(--tcw-accent);\n color: var(--tcw-accent-ink);\n width: 32px;\n height: 32px;\n box-shadow: 0 0 0 3px var(--tcw-accent-glow);\n}\n\n.tcw-replay-btn.tcw-replay-play:hover {\n background: var(--tcw-accent-hover);\n}\n\n.tcw-replay-scrubber {\n appearance: none;\n -webkit-appearance: none;\n width: 220px;\n height: 4px;\n background: var(--tcw-border-strong);\n border-radius: 2px;\n outline: none;\n cursor: pointer;\n}\n\n.tcw-replay-scrubber::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--tcw-accent);\n border: 2px solid var(--tcw-bg-elevated);\n box-shadow: 0 0 0 1px var(--tcw-accent), 0 2px 6px rgba(0, 0, 0, 0.3);\n cursor: pointer;\n}\n\n.tcw-replay-scrubber::-moz-range-thumb {\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--tcw-accent);\n border: 2px solid var(--tcw-bg-elevated);\n box-shadow: 0 0 0 1px var(--tcw-accent), 0 2px 6px rgba(0, 0, 0, 0.3);\n cursor: pointer;\n}\n\n.tcw-replay-progress {\n font-size: 11px;\n font-family: var(--tcw-font-mono);\n color: var(--tcw-text-muted);\n min-width: 64px;\n text-align: center;\n font-feature-settings: 'tnum';\n}\n\n.tcw-replay-speed {\n padding: 4px 8px;\n font-size: 11px;\n font-family: var(--tcw-font-mono);\n background: var(--tcw-bg-surface);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius);\n color: var(--tcw-text-dim);\n cursor: pointer;\n}\n\n.tcw-replay-speed:hover {\n border-color: var(--tcw-border-strong);\n}\n\n@media (max-width: 640px) {\n .tcw-replay-scrubber {\n width: 140px;\n }\n .tcw-replay-progress {\n display: none;\n }\n}\n\n/* === Hotkey sheet === */\n\n.tcw-hotkey-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 24px 32px;\n}\n\n.tcw-hotkey-section .tcw-settings-section-title {\n margin-bottom: 8px;\n}\n\n.tcw-hotkey-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 6px 0;\n border-bottom: 1px solid var(--tcw-divider);\n}\n\n.tcw-hotkey-row:last-child {\n border-bottom: none;\n}\n\n.tcw-hotkey-keys {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-shrink: 0;\n}\n\n.tcw-hotkey-sep {\n font-size: 10px;\n color: var(--tcw-text-muted);\n}\n\n.tcw-hotkey-label {\n font-size: 12.5px;\n color: var(--tcw-text-dim);\n text-align: right;\n letter-spacing: -0.005em;\n}\n\n@media (max-width: 640px) {\n .tcw-hotkey-grid {\n grid-template-columns: 1fr;\n gap: 18px;\n }\n}\n\n/* === Scrollbar (subtle, premium feel) === */\n\n.tcw-dropdown::-webkit-scrollbar,\n.tcw-modal-body::-webkit-scrollbar,\n.tcw-cmd-list::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n.tcw-dropdown::-webkit-scrollbar-track,\n.tcw-modal-body::-webkit-scrollbar-track,\n.tcw-cmd-list::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.tcw-dropdown::-webkit-scrollbar-thumb,\n.tcw-modal-body::-webkit-scrollbar-thumb,\n.tcw-cmd-list::-webkit-scrollbar-thumb {\n background: var(--tcw-border-strong);\n border-radius: 4px;\n border: 2px solid transparent;\n background-clip: padding-box;\n}\n\n.tcw-dropdown::-webkit-scrollbar-thumb:hover,\n.tcw-modal-body::-webkit-scrollbar-thumb:hover,\n.tcw-cmd-list::-webkit-scrollbar-thumb:hover {\n background: var(--tcw-text-muted);\n background-clip: padding-box;\n border: 2px solid transparent;\n}\n\n/* === Responsive === */\n\n@media (max-width: 768px) {\n .tcw-toolbar {\n padding: 0 4px;\n height: 44px; /* Apple HIG minimum touch target */\n }\n .tcw-toolbar-sep {\n display: none;\n }\n .tcw-btn,\n .tcw-dropdown-trigger {\n padding: 10px 12px; /* fatter tap target */\n font-size: 13px;\n }\n .tcw-btn-icon {\n width: 40px;\n height: 40px;\n }\n .tcw-sidebar {\n width: 44px;\n }\n .tcw-sidebar-btn {\n width: 40px;\n height: 40px;\n }\n .tcw-modal,\n .tcw-cmd-palette {\n width: 94vw;\n border-radius: var(--tcw-radius-lg);\n }\n\n /* Hide the right-side watchlist on tablets too — already declared but\n * grouped here for clarity. */\n .tcw-watchlist {\n display: none;\n }\n\n /* Replay scrubber compact mode */\n .tcw-replay-scrubber {\n width: 120px;\n }\n .tcw-replay-progress {\n display: none;\n }\n}\n\n/* === Mobile bottom-sheet pattern ===\n * On phones, swap floating-centered modals for full-width sheets that slide\n * up from the bottom. Bigger hit targets, no awkward off-screen clipping,\n * matches how every modern iOS/Android app handles overflow UI.\n */\n@media (max-width: 640px) {\n .tcw-modal,\n .tcw-cmd-palette {\n top: auto;\n bottom: 0;\n left: 0;\n right: 0;\n transform: none;\n width: 100vw;\n max-width: 100vw;\n max-height: 92vh;\n border-radius: var(--tcw-radius-xl) var(--tcw-radius-xl) 0 0;\n border-bottom: none;\n /* Grab handle */\n padding-top: 8px;\n }\n\n /* Visual \"grab handle\" affordance — a short pill at the top of every sheet */\n .tcw-modal::before,\n .tcw-cmd-palette::before {\n content: '';\n display: block;\n width: 36px;\n height: 4px;\n border-radius: 2px;\n background: var(--tcw-border-strong);\n margin: 0 auto 8px;\n opacity: 0.7;\n }\n\n /* Reuse the existing modal-in animation but slide from below instead of\n * popping from center, so the entrance reads as a sheet, not a dialog. */\n @keyframes tcw-modal-in {\n from { opacity: 0; transform: translateY(20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n\n .tcw-modal,\n .tcw-cmd-palette {\n animation: tcw-modal-in var(--tcw-dur-slow) var(--tcw-ease-out);\n }\n\n /* Safe-area-aware footer padding for iPhone notch / home indicator */\n .tcw-modal-footer,\n .tcw-cmd-footer {\n padding-bottom: max(14px, env(safe-area-inset-bottom));\n }\n\n /* Larger tap targets inside sheets */\n .tcw-modal-tab {\n padding: 14px 16px;\n }\n .tcw-cmd-item {\n padding: 12px 14px;\n font-size: 14px;\n }\n .tcw-dropdown-item {\n padding: 12px 12px;\n font-size: 14px;\n }\n\n /* Hotkey sheet — single column on phones */\n .tcw-hotkey-grid {\n grid-template-columns: 1fr;\n }\n}\n\n/* === Price alerts panel === */\n\n.tcw-alerts-panel {\n position: absolute;\n top: 50px;\n right: 12px;\n z-index: 40;\n width: 300px;\n max-width: calc(100vw - 24px);\n display: flex;\n flex-direction: column;\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border-strong);\n border-radius: var(--tcw-radius-lg);\n box-shadow: var(--tcw-shadow-lg);\n overflow: hidden;\n animation: tcw-alerts-in var(--tcw-dur, 160ms) var(--tcw-ease, ease) both;\n}\n\n@keyframes tcw-alerts-in {\n from { opacity: 0; transform: translateY(-6px) scale(0.98); }\n to { opacity: 1; transform: none; }\n}\n\n.tcw-alerts-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n font-weight: 600;\n font-size: 13px;\n color: var(--tcw-text);\n border-bottom: 1px solid var(--tcw-border);\n}\n\n.tcw-alerts-close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n background: transparent;\n color: var(--tcw-text-muted);\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n}\n.tcw-alerts-close:hover {\n background: var(--tcw-bg-surface);\n color: var(--tcw-text);\n}\n\n.tcw-alerts-form {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 8px;\n padding: 12px;\n border-bottom: 1px solid var(--tcw-border);\n}\n\n.tcw-alerts-form input,\n.tcw-alerts-form select {\n width: 100%;\n min-width: 0;\n box-sizing: border-box;\n height: 32px;\n padding: 0 8px;\n font-size: 12px;\n color: var(--tcw-text);\n background: var(--tcw-bg-surface);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius-sm);\n outline: none;\n}\n.tcw-alerts-form input:focus,\n.tcw-alerts-form select:focus {\n border-color: var(--tcw-accent);\n box-shadow: 0 0 0 2px var(--tcw-accent-soft);\n}\n.tcw-alerts-message,\n.tcw-alerts-source {\n grid-column: 1 / -1;\n}\n\n.tcw-alerts-add {\n grid-column: 1 / -1;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: 34px;\n font-size: 12px;\n font-weight: 600;\n color: var(--tcw-accent-ink);\n background: var(--tcw-accent);\n border: none;\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n}\n.tcw-alerts-add:hover {\n background: var(--tcw-accent-hover);\n}\n\n.tcw-alerts-list {\n max-height: 260px;\n overflow-y: auto;\n padding: 6px;\n}\n\n.tcw-alerts-empty {\n padding: 18px 12px;\n text-align: center;\n font-size: 12px;\n color: var(--tcw-text-muted);\n line-height: 1.5;\n}\n\n.tcw-alerts-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 8px 8px;\n border-radius: var(--tcw-radius-sm);\n}\n.tcw-alerts-row:hover {\n background: var(--tcw-bg-surface);\n}\n.tcw-alerts-triggered {\n background: var(--tcw-accent-soft);\n}\n\n.tcw-alerts-info {\n min-width: 0;\n}\n.tcw-alerts-row-main {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n font-weight: 600;\n color: var(--tcw-text);\n font-variant-numeric: tabular-nums;\n}\n.tcw-alerts-row-note {\n margin-top: 2px;\n font-size: 11px;\n color: var(--tcw-text-muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.tcw-alerts-badge {\n font-size: 9px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n padding: 2px 5px;\n border-radius: 999px;\n color: var(--tcw-accent);\n background: var(--tcw-accent-soft);\n}\n\n.tcw-alerts-del {\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n border: none;\n background: transparent;\n color: var(--tcw-text-muted);\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n}\n.tcw-alerts-del:hover {\n background: var(--tcw-red-soft);\n color: var(--tcw-red);\n}\n\n@media (max-width: 640px) {\n .tcw-alerts-panel {\n top: auto;\n bottom: 0;\n left: 0;\n right: 0;\n width: 100vw;\n max-width: 100vw;\n border-radius: var(--tcw-radius-xl) var(--tcw-radius-xl) 0 0;\n }\n .tcw-alerts-list {\n max-height: 40vh;\n padding-bottom: max(8px, env(safe-area-inset-bottom));\n }\n}\n\n/* === Data Window === */\n\n.tcw-datawin {\n position: absolute;\n top: 50px;\n right: 12px;\n z-index: 38;\n width: 200px;\n display: flex;\n flex-direction: column;\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border-strong);\n border-radius: var(--tcw-radius-lg);\n box-shadow: var(--tcw-shadow-lg);\n overflow: hidden;\n animation: tcw-alerts-in var(--tcw-dur, 160ms) var(--tcw-ease, ease) both;\n}\n.tcw-datawin-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 9px 12px;\n font-size: 12px;\n font-weight: 600;\n color: var(--tcw-text);\n border-bottom: 1px solid var(--tcw-border);\n}\n.tcw-datawin-close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border: none;\n background: transparent;\n color: var(--tcw-text-muted);\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n}\n.tcw-datawin-close:hover { background: var(--tcw-bg-surface); color: var(--tcw-text); }\n.tcw-datawin-body { padding: 6px 4px; max-height: 360px; overflow-y: auto; }\n.tcw-datawin-empty { padding: 14px; text-align: center; font-size: 12px; color: var(--tcw-text-muted); }\n.tcw-datawin-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n padding: 3px 8px;\n font-size: 12px;\n}\n.tcw-datawin-label { color: var(--tcw-text-muted); }\n.tcw-datawin-value { color: var(--tcw-text); font-variant-numeric: tabular-nums; font-weight: 600; }\n.tcw-datawin-value.tcw-up { color: var(--tcw-green); }\n.tcw-datawin-value.tcw-down { color: var(--tcw-red); }\n.tcw-datawin-sub {\n margin: 6px 8px 2px;\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--tcw-text-muted);\n border-top: 1px solid var(--tcw-border);\n padding-top: 6px;\n}\n\n@media (max-width: 640px) {\n .tcw-datawin { top: auto; bottom: 0; left: 0; right: 0; width: 100vw; border-radius: var(--tcw-radius-xl) var(--tcw-radius-xl) 0 0; }\n}\n\n/* === Depth-of-market ladder === */\n\n.tcw-ladder {\n position: absolute;\n top: 50px;\n right: 12px;\n z-index: 41;\n width: 240px;\n display: flex;\n flex-direction: column;\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border-strong);\n border-radius: var(--tcw-radius-lg);\n box-shadow: var(--tcw-shadow-lg);\n overflow: hidden;\n animation: tcw-alerts-in var(--tcw-dur, 160ms) var(--tcw-ease, ease) both;\n}\n.tcw-ladder-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n font-size: 13px;\n font-weight: 600;\n color: var(--tcw-text);\n border-bottom: 1px solid var(--tcw-border);\n}\n.tcw-ladder-close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n background: transparent;\n color: var(--tcw-text-muted);\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n}\n.tcw-ladder-close:hover { background: var(--tcw-bg-surface); color: var(--tcw-text); }\n.tcw-ladder-cols {\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n padding: 4px 0;\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--tcw-text-muted);\n border-bottom: 1px solid var(--tcw-border);\n}\n.tcw-ladder-cols span { text-align: center; }\n.tcw-ladder-body { max-height: 360px; overflow-y: auto; }\n.tcw-ladder-empty { padding: 18px; text-align: center; font-size: 12px; color: var(--tcw-text-muted); }\n\n.tcw-ladder-row {\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n height: 22px;\n font-variant-numeric: tabular-nums;\n}\n.tcw-ladder-cell {\n position: relative;\n display: flex;\n align-items: center;\n border: none;\n background: transparent;\n font-size: 11px;\n cursor: pointer;\n font-variant-numeric: tabular-nums;\n overflow: hidden;\n}\n.tcw-ladder-cell:disabled { cursor: default; }\n/* Depth bar grows from the centre column outward. */\n.tcw-ladder-cell::before {\n content: '';\n position: absolute;\n top: 1px; bottom: 1px;\n width: var(--w, 0%);\n}\n.tcw-ladder-bid { justify-content: flex-end; padding-right: 8px; color: var(--tcw-green); }\n.tcw-ladder-bid::before { right: 0; background: var(--tcw-green-soft); }\n.tcw-ladder-bid span, .tcw-ladder-bid { position: relative; }\n.tcw-ladder-ask { justify-content: flex-start; padding-left: 8px; color: var(--tcw-red); }\n.tcw-ladder-ask::before { left: 0; background: var(--tcw-red-soft); }\n.tcw-ladder-cell:not(:disabled):hover::before { opacity: 0.7; filter: brightness(1.4); }\n.tcw-ladder-cell > * { position: relative; }\n.tcw-ladder-price {\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 11px;\n font-weight: 600;\n color: var(--tcw-text-dim);\n border-left: 1px solid var(--tcw-border);\n border-right: 1px solid var(--tcw-border);\n}\n.tcw-ladder-price.tcw-ask { color: var(--tcw-red); }\n.tcw-ladder-price.tcw-bid { color: var(--tcw-green); }\n\n@media (max-width: 640px) {\n .tcw-ladder { top: auto; bottom: 0; left: 0; right: 0; width: 100vw; border-radius: var(--tcw-radius-xl) var(--tcw-radius-xl) 0 0; }\n .tcw-ladder-body { max-height: 45vh; padding-bottom: max(0px, env(safe-area-inset-bottom)); }\n}\n\n/* === Bracket order placement === */\n\n.tcw-btn-long:hover { color: var(--tcw-green) !important; }\n.tcw-btn-short:hover { color: var(--tcw-red) !important; }\n\n.tcw-bracket-bar {\n position: absolute;\n top: 52px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 45;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 7px 8px 7px 14px;\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border-strong);\n border-radius: 999px;\n box-shadow: var(--tcw-shadow-lg);\n animation: tcw-alerts-in var(--tcw-dur, 160ms) var(--tcw-ease, ease) both;\n}\n.tcw-bracket-label {\n font-size: 12px;\n font-weight: 700;\n}\n.tcw-bracket-label[data-side='buy'] { color: var(--tcw-green); }\n.tcw-bracket-label[data-side='sell'] { color: var(--tcw-red); }\n.tcw-bracket-hint {\n font-size: 11px;\n color: var(--tcw-text-muted);\n}\n.tcw-bracket-cancel, .tcw-bracket-confirm {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n height: 30px;\n padding: 0 12px;\n font-size: 12px;\n font-weight: 600;\n border: none;\n border-radius: 999px;\n cursor: pointer;\n}\n.tcw-bracket-cancel {\n color: var(--tcw-text-dim);\n background: var(--tcw-bg-surface);\n}\n.tcw-bracket-cancel:hover { color: var(--tcw-text); }\n.tcw-bracket-confirm {\n color: var(--tcw-accent-ink);\n background: var(--tcw-accent);\n}\n.tcw-bracket-confirm:hover { background: var(--tcw-accent-hover); }\n\n@media (max-width: 640px) {\n .tcw-bracket-bar { width: calc(100vw - 24px); justify-content: space-between; }\n .tcw-bracket-hint { display: none; }\n}\n\n/* === Drawing style + templates popover === */\n\n.tcw-style-panel {\n position: absolute;\n bottom: 12px;\n left: 52px;\n z-index: 40;\n width: 220px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 12px;\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border-strong);\n border-radius: var(--tcw-radius-lg);\n box-shadow: var(--tcw-shadow-lg);\n animation: tcw-alerts-in var(--tcw-dur, 160ms) var(--tcw-ease, ease) both;\n}\n.tcw-style-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 12px;\n font-weight: 600;\n color: var(--tcw-text);\n}\n.tcw-style-close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border: none;\n background: transparent;\n color: var(--tcw-text-muted);\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n}\n.tcw-style-close:hover { background: var(--tcw-bg-surface); color: var(--tcw-text); }\n\n.tcw-style-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.tcw-style-swatch {\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: 1px solid var(--tcw-border-strong);\n cursor: pointer;\n padding: 0;\n}\n.tcw-style-swatch.tcw-on { outline: 2px solid var(--tcw-accent); outline-offset: 1px; }\n.tcw-style-picker {\n width: 22px;\n height: 22px;\n padding: 0;\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius-sm);\n background: transparent;\n cursor: pointer;\n}\n.tcw-style-width, .tcw-style-dash {\n flex: 1;\n min-width: 0;\n height: 26px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--tcw-bg-surface);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius-sm);\n color: var(--tcw-text-dim);\n cursor: pointer;\n font-size: 11px;\n}\n.tcw-style-width span { width: 16px; background: currentColor; border-radius: 2px; }\n.tcw-style-width.tcw-on, .tcw-style-dash.tcw-on {\n border-color: var(--tcw-accent);\n color: var(--tcw-accent);\n}\n\n.tcw-style-subhead {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--tcw-text-muted);\n margin-top: 2px;\n}\n.tcw-style-save { display: flex; gap: 6px; }\n.tcw-style-name {\n flex: 1;\n min-width: 0;\n height: 28px;\n padding: 0 8px;\n font-size: 12px;\n color: var(--tcw-text);\n background: var(--tcw-bg-surface);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius-sm);\n outline: none;\n}\n.tcw-style-name:focus { border-color: var(--tcw-accent); }\n.tcw-style-savebtn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0 10px;\n height: 28px;\n font-size: 12px;\n font-weight: 600;\n color: var(--tcw-accent-ink);\n background: var(--tcw-accent);\n border: none;\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n}\n.tcw-style-savebtn:hover { background: var(--tcw-accent-hover); }\n\n.tcw-style-templates { display: flex; flex-direction: column; gap: 2px; max-height: 140px; overflow-y: auto; }\n.tcw-style-empty { font-size: 11px; color: var(--tcw-text-muted); padding: 4px 0; }\n.tcw-style-template { display: flex; align-items: center; gap: 4px; }\n.tcw-style-apply {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 5px 7px;\n font-size: 12px;\n color: var(--tcw-text);\n background: transparent;\n border: none;\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n text-align: left;\n}\n.tcw-style-apply:hover { background: var(--tcw-bg-surface); }\n.tcw-style-tdot { flex-shrink: 0; width: 9px; height: 9px; border-radius: 50%; }\n.tcw-style-tdel {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n background: transparent;\n color: var(--tcw-text-muted);\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n}\n.tcw-style-tdel:hover { background: var(--tcw-red-soft); color: var(--tcw-red); }\n\n/* === Settings range slider === */\n\n.tcw-settings-range {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.tcw-settings-range input[type='range'] {\n width: 110px;\n accent-color: var(--tcw-accent);\n cursor: pointer;\n}\n.tcw-settings-range-val {\n min-width: 38px;\n text-align: right;\n font-size: 12px;\n font-variant-numeric: tabular-nums;\n color: var(--tcw-text-dim);\n}\n\n/* === Indicator settings dialog === */\n\n.tcw-modal-narrow {\n max-width: 360px;\n}\n.tcw-indi-input {\n width: 110px;\n height: 30px;\n padding: 0 8px;\n font-size: 12px;\n color: var(--tcw-text);\n background: var(--tcw-bg-surface);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius-sm);\n outline: none;\n font-variant-numeric: tabular-nums;\n}\n.tcw-indi-input:focus {\n border-color: var(--tcw-accent);\n box-shadow: 0 0 0 2px var(--tcw-accent-soft);\n}\n.tcw-indi-color {\n width: 36px;\n height: 28px;\n padding: 2px;\n background: var(--tcw-bg-surface);\n border: 1px solid var(--tcw-border);\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n}\n\n/* === Object tree panel === */\n\n.tcw-tree-panel {\n position: absolute;\n top: 50px;\n right: 12px;\n z-index: 39;\n width: 280px;\n max-width: calc(100vw - 24px);\n display: flex;\n flex-direction: column;\n background: var(--tcw-bg-elevated);\n border: 1px solid var(--tcw-border-strong);\n border-radius: var(--tcw-radius-lg);\n box-shadow: var(--tcw-shadow-lg);\n overflow: hidden;\n animation: tcw-alerts-in var(--tcw-dur, 160ms) var(--tcw-ease, ease) both;\n}\n\n.tcw-tree-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 12px;\n font-weight: 600;\n font-size: 13px;\n color: var(--tcw-text);\n border-bottom: 1px solid var(--tcw-border);\n}\n.tcw-tree-close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n background: transparent;\n color: var(--tcw-text-muted);\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n}\n.tcw-tree-close:hover {\n background: var(--tcw-bg-surface);\n color: var(--tcw-text);\n}\n\n.tcw-tree-section {\n padding: 6px;\n border-bottom: 1px solid var(--tcw-border);\n}\n.tcw-tree-section:last-child {\n border-bottom: none;\n}\n.tcw-tree-section-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 8px 4px;\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--tcw-text-muted);\n}\n.tcw-tree-add {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n border: none;\n background: transparent;\n color: var(--tcw-text-muted);\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n}\n.tcw-tree-add:hover {\n background: var(--tcw-accent-soft);\n color: var(--tcw-accent);\n}\n.tcw-tree-dot {\n flex-shrink: 0;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n}\n.tcw-tree-list {\n max-height: 200px;\n overflow-y: auto;\n}\n\n.tcw-tree-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 6px 8px;\n border-radius: var(--tcw-radius-sm);\n}\n.tcw-tree-row:hover {\n background: var(--tcw-bg-surface);\n}\n.tcw-tree-hidden .tcw-tree-name {\n opacity: 0.5;\n}\n.tcw-tree-name {\n min-width: 0;\n font-size: 12px;\n color: var(--tcw-text);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.tcw-tree-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n flex-shrink: 0;\n}\n.tcw-tree-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: none;\n background: transparent;\n color: var(--tcw-text-muted);\n border-radius: var(--tcw-radius-sm);\n cursor: pointer;\n}\n.tcw-tree-btn:hover {\n background: var(--tcw-bg-elevated);\n color: var(--tcw-text);\n}\n.tcw-tree-btn.tcw-tree-on {\n color: var(--tcw-accent);\n}\n.tcw-tree-btn.tcw-tree-del:hover {\n background: var(--tcw-red-soft);\n color: var(--tcw-red);\n}\n.tcw-tree-empty {\n padding: 8px;\n font-size: 12px;\n color: var(--tcw-text-muted);\n}\n\n@media (max-width: 640px) {\n .tcw-tree-panel {\n top: auto;\n bottom: 0;\n left: 0;\n right: 0;\n width: 100vw;\n max-width: 100vw;\n border-radius: var(--tcw-radius-xl) var(--tcw-radius-xl) 0 0;\n }\n .tcw-tree-list {\n max-height: 30vh;\n }\n .tcw-tree-section:last-child {\n padding-bottom: max(6px, env(safe-area-inset-bottom));\n }\n}\n\n/* === Reduced motion === */\n\n@media (prefers-reduced-motion: reduce) {\n .tcw-root *,\n .tcw-root *::before,\n .tcw-root *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n\n /* Except the loader's slow glow (opacity only): without it a long wait\n looks frozen. */\n .tcw-root .tcw-loading-candle,\n .tcw-root .tcw-loading-track > span {\n animation-duration: 2.4s !important;\n animation-iteration-count: infinite !important;\n animation-delay: var(--tcw-delay, 0ms) !important;\n }\n}\n", h = 0, g = "tcw-styles";
|
|
696
|
+
function ne() {
|
|
697
|
+
if (h++, h > 1 || document.getElementById(g)) return;
|
|
721
698
|
let e = document.createElement("style");
|
|
722
|
-
e.id =
|
|
699
|
+
e.id = g, e.textContent = m, document.head.appendChild(e);
|
|
723
700
|
}
|
|
724
|
-
function
|
|
725
|
-
if (
|
|
726
|
-
let e = document.getElementById(
|
|
701
|
+
function re() {
|
|
702
|
+
if (h--, h > 0) return;
|
|
703
|
+
let e = document.getElementById(g);
|
|
727
704
|
e && e.remove();
|
|
728
705
|
}
|
|
729
706
|
//#endregion
|
|
707
|
+
//#region src/widget/iconSet.ts
|
|
708
|
+
var _ = {
|
|
709
|
+
cursor: { d: "M6 3.5L18.5 12.5L12.6 13.6L9.7 19.4z" },
|
|
710
|
+
trendingUp: { d: "M3 17L9 11L13 14.5L21 6.5M15 6.5H21V12.5" },
|
|
711
|
+
trendingDown: { d: "M3 7L9 13L13 9.5L21 17.5M15 17.5H21V11.5" },
|
|
712
|
+
minus: { d: "M5 12H19" },
|
|
713
|
+
plus: { d: "M12 5V19M5 12H19" },
|
|
714
|
+
penLine: { d: "M14.5 5.5L18.5 9.5M4 20L5 15.5L15.8 4.7a2 2 0 0 1 2.83 0l.67.67a2 2 0 0 1 0 2.83L8.5 19z" },
|
|
715
|
+
zigzag: { d: "M3 17L8 9L13 15L21 6" },
|
|
716
|
+
hash: { d: "M5 9H19M5 15H19M10 4L8.5 20M15.5 4L14 20" },
|
|
717
|
+
square: { d: "M6.5 4.5h11a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2h-11a2 2 0 0 1-2-2v-11a2 2 0 0 1 2-2z" },
|
|
718
|
+
gitBranch: { d: "M6 3V15M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM18 9a9 9 0 0 1-9 9" },
|
|
719
|
+
ruler: { d: "M3.5 16.5L16.5 3.5L20.5 7.5L7.5 20.5zM6.5 13.5L8 15M9.5 10.5L11.5 12.5M12.5 7.5L14 9" },
|
|
720
|
+
type: { d: "M6 6V4.5H18V6M12 4.5V19.5M9.5 19.5H14.5" },
|
|
721
|
+
magnet: { d: "M6 4V11a6 6 0 0 0 12 0V4M6 4h3.5v7a2.5 2.5 0 0 0 5 0V4H18M6 8h3.5M14.5 8H18" },
|
|
722
|
+
undo: { d: "M9 14L4 9L9 4M4 9H14.5a5.5 5.5 0 0 1 0 11H11" },
|
|
723
|
+
redo: { d: "M15 14L20 9L15 4M20 9H9.5a5.5 5.5 0 0 0 0 11H13" },
|
|
724
|
+
trash: { d: "M4 7H20M9.5 7V4.5h5V7M6.5 7l.8 12.2A1.5 1.5 0 0 0 8.8 20.6h6.4a1.5 1.5 0 0 0 1.5-1.4L17.5 7M10 11v6M14 11v6" },
|
|
725
|
+
camera: { d: "M4.5 7.5h3L9 5h6l1.5 2.5h3A1.5 1.5 0 0 1 21 9v9.5a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 3 18.5V9a1.5 1.5 0 0 1 1.5-1.5zM15.5 13.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0z" },
|
|
726
|
+
settings: { d: "M4 7H14M18 7H20M4 17H8M12 17H20M18 7a2 2 0 1 1-4 0 2 2 0 0 1 4 0zM12 17a2 2 0 1 1-4 0 2 2 0 0 1 4 0z" },
|
|
727
|
+
moon: { d: "M20 14.5A8 8 0 1 1 9.5 4a6.5 6.5 0 0 0 10.5 10.5z" },
|
|
728
|
+
sun: { d: "M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0zM12 2.5v2M12 19.5v2M4.6 4.6l1.4 1.4M18 18l1.4 1.4M2.5 12h2M19.5 12h2M4.6 19.4L6 18M18 6l1.4-1.4" },
|
|
729
|
+
chevronDown: { d: "M6 9L12 15L18 9" },
|
|
730
|
+
chevronUp: { d: "M6 15L12 9L18 15" },
|
|
731
|
+
barChart: { d: "M5 20V13M12 20V5M19 20V10" },
|
|
732
|
+
x: { d: "M6 6L18 18M18 6L6 18" },
|
|
733
|
+
play: { d: "M7 4.5v15L19.5 12z" },
|
|
734
|
+
pause: { d: "M8 5V19M16 5V19" },
|
|
735
|
+
stop: { d: "M7.5 6h9A1.5 1.5 0 0 1 18 7.5v9a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 6 16.5v-9A1.5 1.5 0 0 1 7.5 6z" },
|
|
736
|
+
receipt: { d: "M6 3.5h12v17l-2-1.25-2 1.25-2-1.25-2 1.25-2-1.25-2 1.25zM9 8h6M9 12h6M9 16h3" },
|
|
737
|
+
bell: { d: "M6 16.5V11a6 6 0 0 1 12 0v5.5l1.5 2H4.5zM10 20.5a2.2 2.2 0 0 0 4 0" },
|
|
738
|
+
layers: { d: "M12 3.5L21 8.5L12 13.5L3 8.5zM3 12.5L12 17.5L21 12.5M3 16.5L12 21.5L21 16.5" },
|
|
739
|
+
eye: { d: "M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12zM15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0z" },
|
|
740
|
+
eyeOff: { d: "M4 4L20 20M9.9 5.8A9.6 9.6 0 0 1 12 5.5c6 0 9.5 6.5 9.5 6.5a16 16 0 0 1-2.3 3.1M6.6 7.1C3.9 8.9 2.5 12 2.5 12S6 18.5 12 18.5a9.3 9.3 0 0 0 4.7-1.3M10 10.1a3 3 0 0 0 4 4" },
|
|
741
|
+
lock: { d: "M7 10.5h10a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2zM8 10.5V7.5a4 4 0 0 1 8 0v3" },
|
|
742
|
+
unlock: { d: "M7 10.5h10a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2zM8 10.5V7.5a4 4 0 0 1 7.7-1.5" },
|
|
743
|
+
palette: {
|
|
744
|
+
d: "M12 3.5a8.5 8.5 0 0 0 0 17c1.1 0 1.6-.8 1.6-1.6 0-.5-.2-.9-.5-1.2a1.6 1.6 0 0 1 1.2-2.7h1.9a4.3 4.3 0 0 0 4.3-4.3C20.5 6.9 16.7 3.5 12 3.5z",
|
|
745
|
+
dots: [
|
|
746
|
+
[
|
|
747
|
+
7.5,
|
|
748
|
+
11.5,
|
|
749
|
+
1.25
|
|
750
|
+
],
|
|
751
|
+
[
|
|
752
|
+
9.5,
|
|
753
|
+
7.75,
|
|
754
|
+
1.25
|
|
755
|
+
],
|
|
756
|
+
[
|
|
757
|
+
14,
|
|
758
|
+
7.25,
|
|
759
|
+
1.25
|
|
760
|
+
]
|
|
761
|
+
]
|
|
762
|
+
},
|
|
763
|
+
check: { d: "M5 12.5L10 17.5L19 7" },
|
|
764
|
+
maximize: { d: "M8 4H5a1 1 0 0 0-1 1v3M16 4h3a1 1 0 0 1 1 1v3M20 16v3a1 1 0 0 1-1 1h-3M8 20H5a1 1 0 0 1-1-1v-3" },
|
|
765
|
+
minimize: { d: "M4 8h3a1 1 0 0 0 1-1V4M16 4v3a1 1 0 0 0 1 1h3M20 16h-3a1 1 0 0 0-1 1v3M8 20v-3a1 1 0 0 0-1-1H4" },
|
|
766
|
+
repeat: { d: "M17 3.5L20.5 7L17 10.5M3.5 11.5V10a3 3 0 0 1 3-3h14M7 20.5L3.5 17L7 13.5M20.5 12.5V14a3 3 0 0 1-3 3h-14" },
|
|
767
|
+
calendar: { d: "M5.5 5h13a2 2 0 0 1 2 2v11.5a2 2 0 0 1-2 2h-13a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2zM3.5 10H20.5M8 3V7M16 3V7" },
|
|
768
|
+
star: { d: "M12 3.5L14.6 9L20.5 9.6L16 13.6L17.3 19.5L12 16.5L6.7 19.5L8 13.6L3.5 9.6L9.4 9z" },
|
|
769
|
+
ladder: { d: "M4 6H11M13 6H17M6 10H11M13 10H20M8 14H11M13 14H15M5 18H11M13 18H19" }
|
|
770
|
+
}, v = {
|
|
771
|
+
trendLine: {
|
|
772
|
+
d: "M5 18L19 6",
|
|
773
|
+
dots: [[5, 18], [19, 6]]
|
|
774
|
+
},
|
|
775
|
+
ray: {
|
|
776
|
+
d: "M5 17L21 5M17.5 4.81L21 5L20.2 8.41",
|
|
777
|
+
dots: [[5, 17], [11, 12.5]]
|
|
778
|
+
},
|
|
779
|
+
extendedLine: {
|
|
780
|
+
d: "M3 18.5L21 5M17.5 4.81L21 5L20.2 8.41M6.5 18.69L3 18.5L3.8 15.09",
|
|
781
|
+
dots: [[9, 14], [15, 9.5]]
|
|
782
|
+
},
|
|
783
|
+
infoLine: {
|
|
784
|
+
d: "M5 20L19 10M4.5 3.5h9a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-9a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1zM6.5 6.5h5",
|
|
785
|
+
dots: [[5, 20], [19, 10]]
|
|
786
|
+
},
|
|
787
|
+
trendAngle: {
|
|
788
|
+
d: "M5 18L19 7M5 18H20M11 18A6 6 0 0 0 9.71 14.29",
|
|
789
|
+
dots: [[5, 18], [19, 7]]
|
|
790
|
+
},
|
|
791
|
+
horizontalLine: {
|
|
792
|
+
d: "M3 12H21",
|
|
793
|
+
dots: [[12, 12]]
|
|
794
|
+
},
|
|
795
|
+
horizontalRay: {
|
|
796
|
+
d: "M7 12H21M18.5 9.5L21 12L18.5 14.5",
|
|
797
|
+
dots: [[7, 12]]
|
|
798
|
+
},
|
|
799
|
+
verticalLine: {
|
|
800
|
+
d: "M12 3V21",
|
|
801
|
+
dots: [[12, 12]]
|
|
802
|
+
},
|
|
803
|
+
crossLine: {
|
|
804
|
+
d: "M3 12H21M12 3V21",
|
|
805
|
+
dots: [[12, 12]]
|
|
806
|
+
},
|
|
807
|
+
parallelChannel: {
|
|
808
|
+
d: "M3 15L15 5M9 19L21 9",
|
|
809
|
+
dots: [
|
|
810
|
+
[3, 15],
|
|
811
|
+
[15, 5],
|
|
812
|
+
[9, 19]
|
|
813
|
+
]
|
|
814
|
+
},
|
|
815
|
+
regressionChannel: {
|
|
816
|
+
d: "M3 12L21 4M3 20L21 12",
|
|
817
|
+
dash: "M3 16L21 8",
|
|
818
|
+
dots: [[3, 16], [21, 8]]
|
|
819
|
+
},
|
|
820
|
+
fibRetracement: {
|
|
821
|
+
d: "M3 4H21M3 9H21M3 13H21M3 20H21",
|
|
822
|
+
dots: [[5, 20], [19, 4]]
|
|
823
|
+
},
|
|
824
|
+
fibExtension: {
|
|
825
|
+
d: "M3 20L8 10L12 16M14 4H21M14 8H21M14 12H21",
|
|
826
|
+
dots: [
|
|
827
|
+
[3, 20],
|
|
828
|
+
[8, 10],
|
|
829
|
+
[12, 16]
|
|
830
|
+
]
|
|
831
|
+
},
|
|
832
|
+
fibChannel: {
|
|
833
|
+
d: "M3 9L21 3M3 13L21 7M3 16L21 10M3 21L21 15",
|
|
834
|
+
dots: [
|
|
835
|
+
[3, 21],
|
|
836
|
+
[21, 15],
|
|
837
|
+
[3, 9]
|
|
838
|
+
]
|
|
839
|
+
},
|
|
840
|
+
fibTimeZones: {
|
|
841
|
+
d: "M3 4V20M6 4V20M9.5 4V20M14 4V20M20.5 4V20",
|
|
842
|
+
dots: [[3, 20], [6, 20]]
|
|
843
|
+
},
|
|
844
|
+
fibSpeedResistanceFan: {
|
|
845
|
+
d: "M4 20L20 4M4 20L20 11M4 20L20 16M4 20L11 4",
|
|
846
|
+
dash: "M4 4H20V20",
|
|
847
|
+
dots: [[4, 20], [20, 4]]
|
|
848
|
+
},
|
|
849
|
+
rectangle: {
|
|
850
|
+
d: "M5.5 6h13A1.5 1.5 0 0 1 20 7.5v9a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 4 16.5v-9A1.5 1.5 0 0 1 5.5 6z",
|
|
851
|
+
dots: [[4, 6], [20, 18]]
|
|
852
|
+
},
|
|
853
|
+
circle: {
|
|
854
|
+
d: "M19.5 12a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0z",
|
|
855
|
+
dots: [[12, 12], [19.5, 12]]
|
|
856
|
+
},
|
|
857
|
+
ellipse: {
|
|
858
|
+
d: "M20.5 12c0 3-3.8 5.5-8.5 5.5S3.5 15 3.5 12 7.3 6.5 12 6.5s8.5 2.5 8.5 5.5z",
|
|
859
|
+
dots: [[3.5, 12], [20.5, 12]]
|
|
860
|
+
},
|
|
861
|
+
triangle: {
|
|
862
|
+
d: "M12 4.5L20 19H4z",
|
|
863
|
+
dots: [
|
|
864
|
+
[12, 4.5],
|
|
865
|
+
[20, 19],
|
|
866
|
+
[4, 19]
|
|
867
|
+
]
|
|
868
|
+
},
|
|
869
|
+
pitchfork: {
|
|
870
|
+
d: "M4 20L16.75 3M7 9L11.5 3M13 15L21 4.33M7 9L13 15",
|
|
871
|
+
dots: [
|
|
872
|
+
[4, 20],
|
|
873
|
+
[7, 9],
|
|
874
|
+
[13, 15]
|
|
875
|
+
]
|
|
876
|
+
},
|
|
877
|
+
schiffPitchfork: {
|
|
878
|
+
d: "M4 14.5L21 7.42M7 9L21 3.17M13 15L21 11.67M7 9L13 15",
|
|
879
|
+
dash: "M4 20V14.5",
|
|
880
|
+
dots: [
|
|
881
|
+
[4, 14.5],
|
|
882
|
+
[7, 9],
|
|
883
|
+
[13, 15]
|
|
884
|
+
]
|
|
885
|
+
},
|
|
886
|
+
modifiedSchiffPitchfork: {
|
|
887
|
+
d: "M5.5 14.5L21 5.89M7 9L17.8 3M13 15L21 10.56M7 9L13 15",
|
|
888
|
+
dash: "M4 20L5.5 14.5",
|
|
889
|
+
dots: [
|
|
890
|
+
[5.5, 14.5],
|
|
891
|
+
[7, 9],
|
|
892
|
+
[13, 15]
|
|
893
|
+
]
|
|
894
|
+
},
|
|
895
|
+
gannFan: {
|
|
896
|
+
d: "M4 20L20 4M4 20L20 12M4 20L12 4M4 20L20 16.5M4 20L7.5 4",
|
|
897
|
+
dots: [[4, 20], [20, 4]]
|
|
898
|
+
},
|
|
899
|
+
gannBox: {
|
|
900
|
+
d: "M4 4H20V20H4zM4 20L20 4M4 4L20 20M12 4V20",
|
|
901
|
+
dots: [[4, 20], [20, 4]]
|
|
902
|
+
},
|
|
903
|
+
cyclicLines: {
|
|
904
|
+
d: "M4 4V20M9 4V20M14 4V20M19 4V20",
|
|
905
|
+
dots: [[4, 20], [9, 20]]
|
|
906
|
+
},
|
|
907
|
+
xabcdPattern: {
|
|
908
|
+
d: "M3 18L7 6L11 13L15 8L21 19",
|
|
909
|
+
dash: "M3 18L11 13L21 19",
|
|
910
|
+
dots: [
|
|
911
|
+
[3, 18],
|
|
912
|
+
[7, 6],
|
|
913
|
+
[11, 13],
|
|
914
|
+
[15, 8],
|
|
915
|
+
[21, 19]
|
|
916
|
+
]
|
|
917
|
+
},
|
|
918
|
+
abcdPattern: {
|
|
919
|
+
d: "M4 19L9 8L13 14L20 4",
|
|
920
|
+
dots: [
|
|
921
|
+
[4, 19],
|
|
922
|
+
[9, 8],
|
|
923
|
+
[13, 14],
|
|
924
|
+
[20, 4]
|
|
925
|
+
]
|
|
926
|
+
},
|
|
927
|
+
headAndShoulders: {
|
|
928
|
+
d: "M3 19L6 11L8.5 15L12 5L15.5 15L18 11L21 19",
|
|
929
|
+
dash: "M4.5 15H19.5",
|
|
930
|
+
dots: [
|
|
931
|
+
[6, 11],
|
|
932
|
+
[12, 5],
|
|
933
|
+
[18, 11]
|
|
934
|
+
]
|
|
935
|
+
},
|
|
936
|
+
elliottWave: {
|
|
937
|
+
d: "M3 19L6 13L8.5 16L14 6L16 10L21 4",
|
|
938
|
+
dots: [[3, 19], [21, 4]]
|
|
939
|
+
},
|
|
940
|
+
priceRange: { d: "M5 4H19M5 20H19M12 6.5V17.5M9 9.5L12 6.5L15 9.5M9 14.5L12 17.5L15 14.5" },
|
|
941
|
+
dateRange: { d: "M4 5V19M20 5V19M6.5 12H17.5M9.5 9L6.5 12L9.5 15M14.5 9L17.5 12L14.5 15" },
|
|
942
|
+
dateAndPriceRange: {
|
|
943
|
+
d: "M6 18V4M3 7L6 4L9 7M6 18H20M17 15L20 18L17 21",
|
|
944
|
+
dash: "M6 4H20V18"
|
|
945
|
+
},
|
|
946
|
+
measure: { d: "M3.5 16.5L16.5 3.5L20.5 7.5L7.5 20.5zM6.5 13.5L8 15M9.5 10.5L11.5 12.5M12.5 7.5L14 9" },
|
|
947
|
+
text: { d: "M6 6V4.5H18V6M12 4.5V19.5M9.5 19.5H14.5" },
|
|
948
|
+
priceLabel: { d: "M9 5H19a1.5 1.5 0 0 1 1.5 1.5v11A1.5 1.5 0 0 1 19 19H9L3.5 12zM11 12H17" },
|
|
949
|
+
arrow: { d: "M5 19L19 5M10 5H19V14" },
|
|
950
|
+
riskReward: {
|
|
951
|
+
d: "M5 12V4H19V12M3 12H21",
|
|
952
|
+
dash: "M5 12V18H19V12",
|
|
953
|
+
dots: [[5, 12]]
|
|
954
|
+
},
|
|
955
|
+
anchoredVWAP: {
|
|
956
|
+
d: "M4 18C9 17 12 10 20 7M4 20.5V18",
|
|
957
|
+
dots: [[4, 18]]
|
|
958
|
+
},
|
|
959
|
+
volumeProfileRange: {
|
|
960
|
+
d: "M6 7H12M6 10.5H17M6 14H15M6 17.5H10",
|
|
961
|
+
dash: "M3.5 4V20M20.5 4V20",
|
|
962
|
+
dots: [[3.5, 20], [20.5, 20]]
|
|
963
|
+
}
|
|
964
|
+
}, y = {
|
|
965
|
+
candlestick: {
|
|
966
|
+
d: "M8 3.5V7M8 16V20.5M16 5V8.5M16 15.5V19",
|
|
967
|
+
fills: ["M6.5 7h3a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1z", "M14.5 8.5h3a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-5a1 1 0 0 1 1-1z"]
|
|
968
|
+
},
|
|
969
|
+
hollowCandle: { d: "M8 3.5V7M8 16V20.5M16 5V8.5M16 15.5V19M6.5 7h3a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1zM14.5 8.5h3a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-5a1 1 0 0 1 1-1z" },
|
|
970
|
+
heikinAshi: {
|
|
971
|
+
d: "M5.5 9.5V12M5.5 18V20M12 5.5V8M12 14V16M18.5 3V4.5M18.5 10.5V12.5",
|
|
972
|
+
fills: [
|
|
973
|
+
"M4 12h3a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5H4a.5.5 0 0 1-.5-.5v-5A.5.5 0 0 1 4 12z",
|
|
974
|
+
"M10.5 8h3a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-5a.5.5 0 0 1 .5-.5z",
|
|
975
|
+
"M17 4.5h3a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5V5a.5.5 0 0 1 .5-.5z"
|
|
976
|
+
]
|
|
977
|
+
},
|
|
978
|
+
bar: { d: "M8 4V20M5 8H8M8 16H11M16 6V18M13 15H16M16 9H19" },
|
|
979
|
+
volumeCandles: {
|
|
980
|
+
d: "M6.5 5V8M6.5 16V19M15.5 3.5V6M15.5 16V20",
|
|
981
|
+
fills: ["M5.5 8h2a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-7a.5.5 0 0 1 .5-.5z", "M11.5 6h8a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5z"]
|
|
982
|
+
},
|
|
983
|
+
equivolume: { d: "M4.5 10h2a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1zM10 6h5a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zM18.5 4h1a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1z" },
|
|
984
|
+
line: { d: "M3 17L8 11L12 14L21 5" },
|
|
985
|
+
area: {
|
|
986
|
+
d: "M3 17L8 11L12 14L21 5",
|
|
987
|
+
soft: "M3 17L8 11L12 14L21 5V20H3z"
|
|
988
|
+
},
|
|
989
|
+
baseline: {
|
|
990
|
+
d: "M3 16L7 9L11 14L15 7L21 15",
|
|
991
|
+
dash: "M3 12H21"
|
|
992
|
+
},
|
|
993
|
+
hlcArea: {
|
|
994
|
+
d: "M3 9L8 6L13 9L21 4M3 15L8 13L13 16L21 11",
|
|
995
|
+
dash: "M3 12L8 9.5L13 12.5L21 7.5",
|
|
996
|
+
soft: "M3 9L8 6L13 9L21 4V11L13 16L8 13L3 15z"
|
|
997
|
+
},
|
|
998
|
+
stepLine: { d: "M3 18H8V12H12V15H16V7H21" },
|
|
999
|
+
lineWithMarkers: {
|
|
1000
|
+
d: "M4 17L9 10L14 14L20 6",
|
|
1001
|
+
dots: [
|
|
1002
|
+
[4, 17],
|
|
1003
|
+
[9, 10],
|
|
1004
|
+
[14, 14],
|
|
1005
|
+
[20, 6]
|
|
1006
|
+
]
|
|
1007
|
+
}
|
|
1008
|
+
}, ie = 1.75, ae = .22, b = /* @__PURE__ */ new WeakMap();
|
|
1009
|
+
function oe(e) {
|
|
1010
|
+
let t = b.get(e);
|
|
1011
|
+
if (t) return t;
|
|
1012
|
+
let n = "";
|
|
1013
|
+
e.soft && (n += `<path d="${e.soft}" fill="currentColor" fill-opacity="${ae}" stroke="none"/>`);
|
|
1014
|
+
for (let t of e.fills ?? []) n += `<path d="${t}" fill="currentColor" stroke="none"/>`;
|
|
1015
|
+
e.d && (n += `<path d="${e.d}"/>`), e.dash && (n += `<path d="${e.dash}" stroke-dasharray="2 2.5"/>`);
|
|
1016
|
+
for (let [t, r, i = ie] of e.dots ?? []) n += `<circle cx="${t}" cy="${r}" r="${i}" fill="currentColor" stroke="none"/>`;
|
|
1017
|
+
return b.set(e, n), n;
|
|
1018
|
+
}
|
|
1019
|
+
function x(e, t) {
|
|
1020
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${t}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">${oe(e)}</svg>`;
|
|
1021
|
+
}
|
|
1022
|
+
//#endregion
|
|
730
1023
|
//#region src/widget/icons.ts
|
|
731
|
-
var
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
barChart: "<line x1=\"12\" y1=\"20\" x2=\"12\" y2=\"10\"/><line x1=\"18\" y1=\"20\" x2=\"18\" y2=\"4\"/><line x1=\"6\" y1=\"20\" x2=\"6\" y2=\"16\"/>",
|
|
753
|
-
x: "<path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/>",
|
|
754
|
-
play: "<polygon points=\"6 3 20 12 6 21 6 3\"/>",
|
|
755
|
-
pause: "<rect width=\"4\" height=\"16\" x=\"6\" y=\"4\"/><rect width=\"4\" height=\"16\" x=\"14\" y=\"4\"/>",
|
|
756
|
-
stop: "<rect width=\"14\" height=\"14\" x=\"5\" y=\"5\" rx=\"1\"/>",
|
|
757
|
-
receipt: "<path d=\"M4 2v20l2-1 2 1 2-1 2 1 2-1 2 1 2-1 2 1V2l-2 1-2-1-2 1-2-1-2 1-2-1-2 1Z\"/><path d=\"M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8\"/><path d=\"M12 17.5v-11\"/>",
|
|
758
|
-
bell: "<path d=\"M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9\"/><path d=\"M10.3 21a1.94 1.94 0 0 0 3.4 0\"/>",
|
|
759
|
-
plus: "<path d=\"M5 12h14\"/><path d=\"M12 5v14\"/>",
|
|
760
|
-
layers: "<path d=\"m12 2 9 5-9 5-9-5 9-5Z\"/><path d=\"m3 12 9 5 9-5\"/><path d=\"m3 17 9 5 9-5\"/>",
|
|
761
|
-
eye: "<path d=\"M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>",
|
|
762
|
-
eyeOff: "<path d=\"M9.9 4.24A9.12 9.12 0 0 1 12 4c6.5 0 10 7 10 7a13.2 13.2 0 0 1-1.67 2.68\"/><path d=\"M6.6 6.6A13.06 13.06 0 0 0 2 11s3.5 7 10 7a9.1 9.1 0 0 0 5.4-1.6\"/><path d=\"m2 2 20 20\"/>",
|
|
763
|
-
lock: "<rect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/>",
|
|
764
|
-
unlock: "<rect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"/>",
|
|
765
|
-
palette: "<circle cx=\"13.5\" cy=\"6.5\" r=\".5\" fill=\"currentColor\"/><circle cx=\"17.5\" cy=\"10.5\" r=\".5\" fill=\"currentColor\"/><circle cx=\"8.5\" cy=\"7.5\" r=\".5\" fill=\"currentColor\"/><circle cx=\"6.5\" cy=\"12.5\" r=\".5\" fill=\"currentColor\"/><path d=\"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2Z\"/>",
|
|
766
|
-
trendingDown: "<polyline points=\"22 17 13.5 8.5 8.5 13.5 2 7\"/><polyline points=\"16 17 22 17 22 11\"/>",
|
|
767
|
-
check: "<polyline points=\"20 6 9 17 4 12\"/>",
|
|
768
|
-
ladder: "<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"21\"/><line x1=\"3\" y1=\"8\" x2=\"21\" y2=\"8\"/><line x1=\"3\" y1=\"13\" x2=\"21\" y2=\"13\"/><line x1=\"3\" y1=\"18\" x2=\"21\" y2=\"18\"/>"
|
|
769
|
-
};
|
|
770
|
-
function y(e, t = 14) {
|
|
771
|
-
let n = v[e];
|
|
772
|
-
return n ? `<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${t}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${n}</svg>` : "";
|
|
1024
|
+
var S = (e, t) => Object.prototype.hasOwnProperty.call(e, t) ? e[t] : void 0;
|
|
1025
|
+
function C(e, t = 14) {
|
|
1026
|
+
let n = S(_, e);
|
|
1027
|
+
return n ? x(n, t) : "";
|
|
1028
|
+
}
|
|
1029
|
+
function w(e, t = 16) {
|
|
1030
|
+
return x(S(v, e) ?? _.penLine, t);
|
|
1031
|
+
}
|
|
1032
|
+
function T(e, t = 14) {
|
|
1033
|
+
return x(S(y, e) ?? _.barChart, t);
|
|
1034
|
+
}
|
|
1035
|
+
//#endregion
|
|
1036
|
+
//#region src/widget/escapeHtml.ts
|
|
1037
|
+
function E(e) {
|
|
1038
|
+
return e.replace(/[&<>"']/g, (e) => ({
|
|
1039
|
+
"&": "&",
|
|
1040
|
+
"<": "<",
|
|
1041
|
+
">": ">",
|
|
1042
|
+
"\"": """,
|
|
1043
|
+
"'": "'"
|
|
1044
|
+
})[e]);
|
|
773
1045
|
}
|
|
774
1046
|
//#endregion
|
|
775
1047
|
//#region src/widget/WidgetDropdown.ts
|
|
776
|
-
var
|
|
1048
|
+
var D = class {
|
|
777
1049
|
trigger;
|
|
778
1050
|
panel;
|
|
779
1051
|
_isOpen = !1;
|
|
@@ -804,37 +1076,39 @@ var b = class {
|
|
|
804
1076
|
destroy() {
|
|
805
1077
|
this.trigger.removeEventListener("click", this.onTriggerClick), document.removeEventListener("mousedown", this.onOutsideClick), document.removeEventListener("keydown", this.onEscapeKey), this.panel.remove();
|
|
806
1078
|
}
|
|
807
|
-
},
|
|
1079
|
+
}, se = class {
|
|
808
1080
|
config;
|
|
809
1081
|
callbacks;
|
|
810
1082
|
t;
|
|
811
1083
|
el;
|
|
812
1084
|
chartTypeDropdown = null;
|
|
813
1085
|
indicatorDropdown = null;
|
|
814
|
-
|
|
815
|
-
|
|
1086
|
+
tfGroup = null;
|
|
1087
|
+
tfDropdown = null;
|
|
1088
|
+
tfFavorites = [];
|
|
1089
|
+
tfRendered = "";
|
|
816
1090
|
themeBtn = null;
|
|
1091
|
+
fullscreenBtn = null;
|
|
817
1092
|
constructor(e, t, n, r) {
|
|
818
1093
|
this.config = t, this.callbacks = n, this.t = r, this.el = document.createElement("div"), this.el.className = "tcw-toolbar", this.build(), e.appendChild(this.el);
|
|
819
1094
|
}
|
|
820
1095
|
build() {
|
|
821
1096
|
let { config: e, callbacks: t, el: n } = this, r = document.createElement("button");
|
|
822
|
-
r.className = "tcw-toolbar-symbol", r.dataset.role = "symbol", r.addEventListener("click", t.onSymbolClick), n.appendChild(r), n.appendChild(this.sep())
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
e.className = "tcw-btn", e.textContent = n.label, e.dataset.tf = n.value, e.addEventListener("click", () => t.onTimeframe(n.value)), i.appendChild(e), this.tfButtons.push(e);
|
|
1097
|
+
if (r.className = "tcw-toolbar-symbol", r.dataset.role = "symbol", r.addEventListener("click", t.onSymbolClick), n.appendChild(r), n.appendChild(this.sep()), this.tfFavorites = [...e.timeframeFavorites ?? e.timeframes.map((e) => e.value)], this.tfGroup = document.createElement("div"), this.tfGroup.className = "tcw-toolbar-group", n.appendChild(this.tfGroup), t.onToggleTimeframeFavorite && e.timeframes.length > 0) {
|
|
1098
|
+
let e = document.createElement("div");
|
|
1099
|
+
e.style.position = "relative", e.style.display = "inline-flex";
|
|
1100
|
+
let t = document.createElement("button");
|
|
1101
|
+
t.className = "tcw-btn tcw-tf-more", t.dataset.role = "timeframes", t.title = this.t("toolbar.timeframes"), t.setAttribute("aria-label", this.t("toolbar.timeframes")), t.innerHTML = C("chevronDown", 12), e.appendChild(t), n.appendChild(e), this.tfDropdown = new D(e, { width: "150px" }), this.buildTimeframeMenu();
|
|
828
1102
|
}
|
|
829
|
-
n.appendChild(
|
|
830
|
-
let
|
|
831
|
-
|
|
832
|
-
let
|
|
833
|
-
|
|
834
|
-
let
|
|
835
|
-
|
|
836
|
-
let
|
|
837
|
-
if (
|
|
1103
|
+
n.appendChild(this.sep());
|
|
1104
|
+
let i = document.createElement("div");
|
|
1105
|
+
i.style.position = "relative", i.style.display = "inline-flex";
|
|
1106
|
+
let a = document.createElement("button");
|
|
1107
|
+
a.className = "tcw-dropdown-trigger", a.dataset.role = "charttype", i.appendChild(a), n.appendChild(i), this.chartTypeDropdown = new D(i, { width: "160px" }), this.buildChartTypeMenu(), n.appendChild(this.sep());
|
|
1108
|
+
let o = document.createElement("div");
|
|
1109
|
+
o.style.position = "relative", o.style.display = "inline-flex";
|
|
1110
|
+
let s = document.createElement("button");
|
|
1111
|
+
if (s.className = "tcw-dropdown-trigger", s.dataset.role = "indicators", o.appendChild(s), n.appendChild(o), this.indicatorDropdown = new D(o, { width: "280px" }), this.buildIndicatorMenu(), n.appendChild(this.spacer()), t.onToggleReplay) {
|
|
838
1112
|
let e = this.iconBtn("play", this.t("toolbar.replay"), t.onToggleReplay);
|
|
839
1113
|
e.dataset.role = "replay", n.appendChild(e);
|
|
840
1114
|
}
|
|
@@ -856,10 +1130,15 @@ var b = class {
|
|
|
856
1130
|
let e = this.iconBtn("bell", this.t("toolbar.priceAlerts"), t.onToggleAlerts);
|
|
857
1131
|
e.dataset.role = "alerts", n.appendChild(e);
|
|
858
1132
|
}
|
|
859
|
-
let
|
|
860
|
-
n.appendChild(
|
|
861
|
-
let
|
|
862
|
-
n.appendChild(
|
|
1133
|
+
let c = this.iconBtn("camera", this.t("toolbar.screenshot"), t.onScreenshot);
|
|
1134
|
+
n.appendChild(c);
|
|
1135
|
+
let l = this.iconBtn("settings", this.t("toolbar.settings"), t.onSettings);
|
|
1136
|
+
n.appendChild(l), this.themeBtn = this.iconBtn("moon", this.t("toolbar.toggleTheme"), t.onToggleTheme), this.themeBtn.dataset.role = "theme", n.appendChild(this.themeBtn), t.onToggleFullscreen && (this.fullscreenBtn = this.iconBtn("maximize", this.t("toolbar.fullscreen"), t.onToggleFullscreen), this.fullscreenBtn.dataset.role = "fullscreen", this.fullscreenBtn.setAttribute("aria-pressed", "false"), n.appendChild(this.fullscreenBtn));
|
|
1137
|
+
}
|
|
1138
|
+
setFullscreen(e) {
|
|
1139
|
+
if (!this.fullscreenBtn) return;
|
|
1140
|
+
let t = this.t(e ? "toolbar.exitFullscreen" : "toolbar.fullscreen");
|
|
1141
|
+
this.fullscreenBtn.innerHTML = C(e ? "minimize" : "maximize", 14), this.fullscreenBtn.title = t, this.fullscreenBtn.setAttribute("aria-pressed", String(e));
|
|
863
1142
|
}
|
|
864
1143
|
chartTypeLabel(e) {
|
|
865
1144
|
let t = `chartType.${e.value}`, n = this.t(t);
|
|
@@ -867,7 +1146,7 @@ var b = class {
|
|
|
867
1146
|
}
|
|
868
1147
|
buildChartTypeMenu() {
|
|
869
1148
|
if (!this.chartTypeDropdown) return;
|
|
870
|
-
let e = this.config.chartTypes.map((e) => `<button class="tcw-dropdown-item" data-ct="${e.value}">${this.chartTypeLabel(e)}</button>`).join("");
|
|
1149
|
+
let e = this.config.chartTypes.map((e) => `<button class="tcw-dropdown-item tcw-dropdown-item--icon" data-ct="${E(e.value)}">${T(e.value, 16)}<span>${E(this.chartTypeLabel(e))}</span></button>`).join("");
|
|
871
1150
|
this.chartTypeDropdown.setContent(e), this.chartTypeDropdown.panel.querySelectorAll(".tcw-dropdown-item").forEach((e) => {
|
|
872
1151
|
e.addEventListener("click", (e) => {
|
|
873
1152
|
let t = e.currentTarget.dataset.ct;
|
|
@@ -875,13 +1154,43 @@ var b = class {
|
|
|
875
1154
|
});
|
|
876
1155
|
});
|
|
877
1156
|
}
|
|
1157
|
+
setTimeframeFavorites(e) {
|
|
1158
|
+
this.tfFavorites = [...e], this.tfRendered = "", this.buildTimeframeMenu();
|
|
1159
|
+
}
|
|
1160
|
+
buildTimeframeMenu() {
|
|
1161
|
+
if (!this.tfDropdown) return;
|
|
1162
|
+
let e = E(this.t("toolbar.timeframes.pin")), t = `<div class="tcw-dropdown-label">${E(this.t("toolbar.timeframes"))}</div>`;
|
|
1163
|
+
for (let n of this.config.timeframes) {
|
|
1164
|
+
let r = this.tfFavorites.includes(n.value), [i, a] = [E(n.value), E(n.label)];
|
|
1165
|
+
t += `<div class="tcw-tf-row"><button class="tcw-dropdown-item" data-tf-pick="${i}">${a}</button><button class="tcw-tf-star${r ? " tcw-active" : ""}" data-tf-star="${i}" aria-pressed="${r}" title="${e}" aria-label="${e}: ${a}">${C("star", 12)}</button></div>`;
|
|
1166
|
+
}
|
|
1167
|
+
this.tfDropdown.setContent(t);
|
|
1168
|
+
let n = this.tfDropdown.panel;
|
|
1169
|
+
n.querySelectorAll("[data-tf-pick]").forEach((e) => {
|
|
1170
|
+
e.addEventListener("click", () => this.callbacks.onTimeframe(e.dataset.tfPick));
|
|
1171
|
+
}), n.querySelectorAll("[data-tf-star]").forEach((e) => {
|
|
1172
|
+
e.addEventListener("click", (t) => {
|
|
1173
|
+
t.stopPropagation(), this.callbacks.onToggleTimeframeFavorite?.(e.dataset.tfStar);
|
|
1174
|
+
});
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
renderTimeframes(e) {
|
|
1178
|
+
if (!this.tfGroup) return;
|
|
1179
|
+
let t = this.config.timeframes.filter((t) => this.tfFavorites.includes(t.value) || t.value === e), n = `${t.map((e) => e.value).join(",")}|${e}`;
|
|
1180
|
+
n !== this.tfRendered && (this.tfRendered = n, this.tfGroup.replaceChildren(...t.map((t) => {
|
|
1181
|
+
let n = document.createElement("button");
|
|
1182
|
+
return n.className = "tcw-btn", n.textContent = t.label, n.dataset.tf = t.value, n.classList.toggle("tcw-active", t.value === e), n.addEventListener("click", () => this.callbacks.onTimeframe(t.value)), n;
|
|
1183
|
+
})), (this.tfDropdown?.panel)?.querySelectorAll("[data-tf-pick]").forEach((t) => {
|
|
1184
|
+
t.classList.toggle("tcw-active", t.dataset.tfPick === e);
|
|
1185
|
+
}));
|
|
1186
|
+
}
|
|
878
1187
|
buildIndicatorMenu() {
|
|
879
1188
|
if (!this.indicatorDropdown) return;
|
|
880
|
-
let { indicators: e, popularIndicatorIds: t } = this.config, n = e.filter((e) => t.includes(e.id)), r = e.filter((e) => !t.includes(e.id)), i = (e) => this.t(`indicatorType.${e}`), a = `<div class="tcw-dropdown-label">${this.t("toolbar.indicators.popular")}</div>`;
|
|
881
|
-
for (let e of n)
|
|
882
|
-
|
|
883
|
-
for (let e of r)
|
|
884
|
-
this.indicatorDropdown.setContent(
|
|
1189
|
+
let { indicators: e, popularIndicatorIds: t } = this.config, n = e.filter((e) => t.includes(e.id)), r = e.filter((e) => !t.includes(e.id)), i = (e) => this.t(`indicatorType.${e}`), a = (e) => `<button class="tcw-dropdown-item" data-ind="${E(e.id)}"><span>${E(e.name)}</span><span class="tcw-tag">${E(i(e.type))}</span></button>`, o = `<div class="tcw-dropdown-label">${E(this.t("toolbar.indicators.popular"))}</div>`;
|
|
1190
|
+
for (let e of n) o += a(e);
|
|
1191
|
+
o += "<div class=\"tcw-dropdown-divider\"></div>", o += `<div class="tcw-dropdown-label">${E(this.t("toolbar.indicators.all"))}</div>`;
|
|
1192
|
+
for (let e of r) o += a(e);
|
|
1193
|
+
this.indicatorDropdown.setContent(o), this.indicatorDropdown.panel.querySelectorAll(".tcw-dropdown-item[data-ind]").forEach((e) => {
|
|
885
1194
|
e.addEventListener("click", (e) => {
|
|
886
1195
|
let t = e.currentTarget.dataset.ind;
|
|
887
1196
|
this.callbacks.onAddIndicator(t);
|
|
@@ -890,40 +1199,18 @@ var b = class {
|
|
|
890
1199
|
}
|
|
891
1200
|
update(e) {
|
|
892
1201
|
let t = this.el.querySelector("[data-role=\"symbol\"]");
|
|
893
|
-
t && (t.textContent = e.symbol);
|
|
894
|
-
for (let t of this.tfButtons) t.classList.toggle("tcw-active", t.dataset.tf === e.timeframe);
|
|
1202
|
+
t && (t.textContent = e.symbol), this.renderTimeframes(e.timeframe);
|
|
895
1203
|
let n = this.el.querySelector("[data-role=\"charttype\"]");
|
|
896
1204
|
if (n) {
|
|
897
1205
|
let t = this.config.chartTypes.find((t) => t.value === e.chartType), r = t ? this.chartTypeLabel(t) : e.chartType;
|
|
898
|
-
n.innerHTML = `${
|
|
1206
|
+
n.innerHTML = `${T(e.chartType, 14)} ${E(r)} ${C("chevronDown", 12)}`;
|
|
899
1207
|
}
|
|
900
1208
|
let r = this.el.querySelector("[data-role=\"indicators\"]");
|
|
901
1209
|
if (r) {
|
|
902
|
-
let t = e.activeIndicators.size, n = `${
|
|
1210
|
+
let t = e.activeIndicators.size, n = `${C("trendingUp", 14)} ${this.t("toolbar.indicators")}`;
|
|
903
1211
|
t > 0 && (n += ` <span class="tcw-badge-count">${t}</span>`), r.innerHTML = n;
|
|
904
1212
|
}
|
|
905
|
-
|
|
906
|
-
let t = this.getActiveIndicatorList(e);
|
|
907
|
-
this.chipsContainer.innerHTML = "";
|
|
908
|
-
for (let e of t) {
|
|
909
|
-
let t = document.createElement("div");
|
|
910
|
-
t.className = "tcw-indicator-chip";
|
|
911
|
-
let n = document.createElement("span");
|
|
912
|
-
n.textContent = e.label, t.appendChild(n);
|
|
913
|
-
let r = document.createElement("button");
|
|
914
|
-
r.className = "tcw-chip-remove", r.innerHTML = y("x", 10), r.addEventListener("click", () => this.callbacks.onRemoveIndicator(e.instanceId)), t.appendChild(r), this.chipsContainer.appendChild(t);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
this.themeBtn && (this.themeBtn.innerHTML = e.isDark ? y("moon", 14) : y("sun", 14));
|
|
918
|
-
}
|
|
919
|
-
getActiveIndicatorList(e) {
|
|
920
|
-
let t = [];
|
|
921
|
-
for (let [n, r] of e.activeIndicators.entries()) t.push({
|
|
922
|
-
instanceId: r,
|
|
923
|
-
id: n,
|
|
924
|
-
label: n.toUpperCase()
|
|
925
|
-
});
|
|
926
|
-
return t;
|
|
1213
|
+
this.themeBtn && (this.themeBtn.innerHTML = e.isDark ? C("moon", 14) : C("sun", 14));
|
|
927
1214
|
}
|
|
928
1215
|
sep() {
|
|
929
1216
|
let e = document.createElement("span");
|
|
@@ -935,84 +1222,33 @@ var b = class {
|
|
|
935
1222
|
}
|
|
936
1223
|
iconBtn(e, t, n) {
|
|
937
1224
|
let r = document.createElement("button");
|
|
938
|
-
return r.className = "tcw-btn-icon", r.title = t, r.innerHTML =
|
|
1225
|
+
return r.className = "tcw-btn-icon", r.title = t, r.innerHTML = C(e, 14), r.addEventListener("click", n), r;
|
|
939
1226
|
}
|
|
940
1227
|
destroy() {
|
|
941
1228
|
this.chartTypeDropdown?.destroy(), this.indicatorDropdown?.destroy(), this.el.remove();
|
|
942
1229
|
}
|
|
943
|
-
},
|
|
944
|
-
|
|
945
|
-
"minus",
|
|
946
|
-
"penLine",
|
|
947
|
-
"hash",
|
|
948
|
-
"square",
|
|
949
|
-
"gitBranch",
|
|
950
|
-
"zigzag",
|
|
951
|
-
"ruler",
|
|
952
|
-
"type",
|
|
953
|
-
"ladder"
|
|
954
|
-
], ie = {
|
|
955
|
-
trendLine: "trendingUp",
|
|
956
|
-
ray: "trendingUp",
|
|
957
|
-
extendedLine: "trendingUp",
|
|
958
|
-
infoLine: "trendingUp",
|
|
959
|
-
trendAngle: "trendingUp",
|
|
960
|
-
horizontalLine: "minus",
|
|
961
|
-
horizontalRay: "minus",
|
|
962
|
-
verticalLine: "penLine",
|
|
963
|
-
crossLine: "plus",
|
|
964
|
-
rectangle: "square",
|
|
965
|
-
circle: "square",
|
|
966
|
-
ellipse: "square",
|
|
967
|
-
triangle: "square",
|
|
968
|
-
arrow: "trendingUp",
|
|
969
|
-
parallelChannel: "gitBranch",
|
|
970
|
-
regressionChannel: "gitBranch",
|
|
971
|
-
pitchfork: "gitBranch",
|
|
972
|
-
schiffPitchfork: "gitBranch",
|
|
973
|
-
modifiedSchiffPitchfork: "gitBranch",
|
|
974
|
-
fibRetracement: "hash",
|
|
975
|
-
fibExtension: "hash",
|
|
976
|
-
fibTimeZones: "hash",
|
|
977
|
-
fibChannel: "hash",
|
|
978
|
-
fibSpeedResistanceFan: "hash",
|
|
979
|
-
gannBox: "hash",
|
|
980
|
-
gannFan: "gitBranch",
|
|
981
|
-
cyclicLines: "barChart",
|
|
982
|
-
elliottWave: "zigzag",
|
|
983
|
-
xabcdPattern: "zigzag",
|
|
984
|
-
abcdPattern: "zigzag",
|
|
985
|
-
headAndShoulders: "zigzag",
|
|
986
|
-
priceRange: "ruler",
|
|
987
|
-
dateRange: "ruler",
|
|
988
|
-
dateAndPriceRange: "ruler",
|
|
989
|
-
measure: "ruler",
|
|
990
|
-
text: "type",
|
|
991
|
-
textAnnotation: "type",
|
|
992
|
-
priceLabel: "type",
|
|
993
|
-
anchoredVWAP: "trendingUp",
|
|
994
|
-
volumeProfileRange: "barChart",
|
|
995
|
-
riskReward: "square"
|
|
996
|
-
};
|
|
997
|
-
function ae(e) {
|
|
998
|
-
return ie[e] ?? "penLine";
|
|
999
|
-
}
|
|
1000
|
-
function oe(e, t) {
|
|
1230
|
+
}, O = 16, ce = 150;
|
|
1231
|
+
function k(e, t) {
|
|
1001
1232
|
for (let n of e) {
|
|
1002
1233
|
let e = n.tools.find((e) => e.value === t);
|
|
1003
1234
|
if (e) return e.label;
|
|
1004
1235
|
}
|
|
1005
1236
|
return t;
|
|
1006
1237
|
}
|
|
1007
|
-
var
|
|
1238
|
+
var le = class {
|
|
1008
1239
|
config;
|
|
1009
1240
|
callbacks;
|
|
1010
1241
|
el;
|
|
1011
1242
|
groupWraps = [];
|
|
1012
1243
|
groupButtons = [];
|
|
1244
|
+
groupIcons = [];
|
|
1245
|
+
groupTools = [];
|
|
1013
1246
|
cursorBtn = null;
|
|
1014
1247
|
magnetBtn = null;
|
|
1248
|
+
stayBtn = null;
|
|
1015
1249
|
flyoutEl = null;
|
|
1250
|
+
flyoutIdx = -1;
|
|
1251
|
+
flyoutHideTimer = null;
|
|
1016
1252
|
favoritesEl = null;
|
|
1017
1253
|
favoritesDivider = null;
|
|
1018
1254
|
favorites = [];
|
|
@@ -1021,28 +1257,37 @@ var se = class {
|
|
|
1021
1257
|
}
|
|
1022
1258
|
build() {
|
|
1023
1259
|
let { config: e, callbacks: t, el: n } = this;
|
|
1024
|
-
this.cursorBtn = document.createElement("button"), this.cursorBtn.className = "tcw-sidebar-btn", this.cursorBtn.title = "Cursor", this.cursorBtn.innerHTML =
|
|
1260
|
+
this.cursorBtn = document.createElement("button"), this.cursorBtn.className = "tcw-sidebar-btn", this.cursorBtn.title = "Cursor", this.cursorBtn.innerHTML = C("cursor", 14), this.cursorBtn.addEventListener("click", t.onCancelDrawing), n.appendChild(this.cursorBtn), n.appendChild(this.divider()), t.onToggleFavorite && (this.favorites = [...e.favorites ?? []], this.favoritesEl = document.createElement("div"), this.favoritesEl.className = "tcw-sidebar-favorites", n.appendChild(this.favoritesEl), this.favoritesDivider = this.divider(), n.appendChild(this.favoritesDivider), this.renderFavorites()), e.drawingToolGroups.forEach((e, r) => {
|
|
1025
1261
|
let i = document.createElement("div");
|
|
1026
1262
|
i.className = "tcw-tool-group-wrap";
|
|
1027
1263
|
let a = document.createElement("button");
|
|
1028
|
-
|
|
1264
|
+
a.className = "tcw-sidebar-btn";
|
|
1265
|
+
let o = e.tools[0]?.value;
|
|
1266
|
+
if (!o) return;
|
|
1267
|
+
e.tools.length > 1 ? a.setAttribute("aria-label", `${e.label}: ${e.tools[0].label}`) : a.title = e.label;
|
|
1268
|
+
let s = document.createElement("span");
|
|
1269
|
+
if (s.className = "tcw-sidebar-icon", s.innerHTML = w(o, O), a.appendChild(s), this.groupIcons.push(s), this.groupTools.push(o), e.tools.length > 1) {
|
|
1029
1270
|
let e = document.createElement("span");
|
|
1030
1271
|
e.className = "tcw-multi-dot", a.appendChild(e);
|
|
1031
1272
|
}
|
|
1032
|
-
a.addEventListener("click", () => t.onDrawingTool(
|
|
1273
|
+
a.addEventListener("click", () => t.onDrawingTool(this.groupTools[r])), i.appendChild(a), i.addEventListener("mouseenter", () => this.showFlyout(r)), i.addEventListener("mouseleave", () => this.scheduleHideFlyout()), a.addEventListener("focus", () => {
|
|
1274
|
+
a.matches(":focus-visible") && this.showFlyout(r);
|
|
1275
|
+
}), i.addEventListener("focusout", (e) => {
|
|
1276
|
+
i.contains(e.relatedTarget) || this.scheduleHideFlyout();
|
|
1277
|
+
}), n.appendChild(i), this.groupWraps.push(i), this.groupButtons.push(a);
|
|
1033
1278
|
});
|
|
1034
1279
|
let r = document.createElement("div");
|
|
1035
1280
|
if (r.className = "tcw-sidebar-spacer", n.appendChild(r), n.appendChild(this.divider()), t.onToggleStyle) {
|
|
1036
1281
|
let e = document.createElement("button");
|
|
1037
|
-
e.className = "tcw-sidebar-btn", e.title = "Drawing style & templates", e.dataset.role = "style", e.innerHTML =
|
|
1282
|
+
e.className = "tcw-sidebar-btn", e.title = "Drawing style & templates", e.dataset.role = "style", e.innerHTML = C("palette", 14), e.addEventListener("click", t.onToggleStyle), n.appendChild(e);
|
|
1038
1283
|
}
|
|
1039
|
-
this.magnetBtn = document.createElement("button"), this.magnetBtn.className = "tcw-sidebar-btn", this.magnetBtn.title = "Magnet", this.magnetBtn.innerHTML =
|
|
1284
|
+
t.onToggleMagnet && (this.magnetBtn = document.createElement("button"), this.magnetBtn.className = "tcw-sidebar-btn", this.magnetBtn.title = "Magnet", this.magnetBtn.innerHTML = C("magnet", 14), this.magnetBtn.addEventListener("click", t.onToggleMagnet), n.appendChild(this.magnetBtn)), t.onToggleStayInDrawing && (this.stayBtn = document.createElement("button"), this.stayBtn.className = "tcw-sidebar-btn", this.stayBtn.dataset.role = "stay", this.stayBtn.title = "Stay in drawing mode", this.stayBtn.setAttribute("aria-pressed", "false"), this.stayBtn.innerHTML = C("repeat", 14), this.stayBtn.addEventListener("click", t.onToggleStayInDrawing), n.appendChild(this.stayBtn));
|
|
1040
1285
|
let i = document.createElement("button");
|
|
1041
|
-
i.className = "tcw-sidebar-btn", i.title = "Undo", i.innerHTML =
|
|
1286
|
+
i.className = "tcw-sidebar-btn", i.title = "Undo", i.innerHTML = C("undo", 14), i.addEventListener("click", t.onUndo), n.appendChild(i);
|
|
1042
1287
|
let a = document.createElement("button");
|
|
1043
|
-
a.className = "tcw-sidebar-btn", a.title = "Redo", a.innerHTML =
|
|
1288
|
+
a.className = "tcw-sidebar-btn", a.title = "Redo", a.innerHTML = C("redo", 14), a.addEventListener("click", t.onRedo), n.appendChild(a);
|
|
1044
1289
|
let o = document.createElement("button");
|
|
1045
|
-
o.className = "tcw-sidebar-btn tcw-danger", o.title = "Clear all", o.innerHTML =
|
|
1290
|
+
o.className = "tcw-sidebar-btn tcw-danger", o.title = "Clear all", o.innerHTML = C("trash", 14), o.addEventListener("click", t.onClearDrawings), n.appendChild(o);
|
|
1046
1291
|
}
|
|
1047
1292
|
setFavorites(e) {
|
|
1048
1293
|
this.favorites = [...e], this.renderFavorites();
|
|
@@ -1054,14 +1299,18 @@ var se = class {
|
|
|
1054
1299
|
this.favoritesEl.style.display = e ? "" : "none", this.favoritesDivider && (this.favoritesDivider.style.display = e ? "" : "none");
|
|
1055
1300
|
for (let e of this.favorites) {
|
|
1056
1301
|
let t = document.createElement("button");
|
|
1057
|
-
t.className = "tcw-sidebar-btn", t.title = `${
|
|
1302
|
+
t.className = "tcw-sidebar-btn", t.title = `${k(this.config.drawingToolGroups, e)} (right-click to unpin)`, t.dataset.toolValue = e, t.innerHTML = w(e, O), t.addEventListener("click", () => this.callbacks.onDrawingTool(e)), t.addEventListener("contextmenu", (t) => {
|
|
1058
1303
|
t.preventDefault(), this.callbacks.onToggleFavorite?.(e);
|
|
1059
1304
|
}), this.favoritesEl.appendChild(t);
|
|
1060
1305
|
}
|
|
1061
1306
|
}
|
|
1062
1307
|
showFlyout(e) {
|
|
1308
|
+
if (this.cancelHideFlyout(), this.flyoutEl && this.flyoutIdx === e) return;
|
|
1063
1309
|
let t = this.config.drawingToolGroups[e];
|
|
1064
|
-
if (!t || t.tools.length <= 1)
|
|
1310
|
+
if (!t || t.tools.length <= 1) {
|
|
1311
|
+
this.hideFlyout();
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1065
1314
|
this.hideFlyout();
|
|
1066
1315
|
let n = document.createElement("div");
|
|
1067
1316
|
n.className = "tcw-flyout";
|
|
@@ -1069,28 +1318,42 @@ var se = class {
|
|
|
1069
1318
|
r.className = "tcw-flyout-header", r.textContent = t.label, n.appendChild(r);
|
|
1070
1319
|
for (let e of t.tools) {
|
|
1071
1320
|
let t = document.createElement("button");
|
|
1072
|
-
t.className = "tcw-flyout-item", t.
|
|
1321
|
+
t.className = "tcw-flyout-item", t.innerHTML = w(e.value, O);
|
|
1322
|
+
let r = document.createElement("span");
|
|
1323
|
+
r.textContent = e.label, t.appendChild(r), t.dataset.toolValue = e.value, t.addEventListener("click", () => {
|
|
1073
1324
|
this.callbacks.onDrawingTool(e.value), this.hideFlyout();
|
|
1074
|
-
}), this.callbacks.onToggleFavorite && (this.favorites.includes(e.value) && t.classList.add("tcw-flyout-faved"), t.
|
|
1325
|
+
}), this.callbacks.onToggleFavorite && (this.favorites.includes(e.value) && t.classList.add("tcw-flyout-faved"), t.addEventListener("contextmenu", (t) => {
|
|
1075
1326
|
t.preventDefault(), this.callbacks.onToggleFavorite?.(e.value);
|
|
1076
1327
|
})), n.appendChild(t);
|
|
1077
1328
|
}
|
|
1078
|
-
|
|
1329
|
+
if (this.callbacks.onToggleFavorite) {
|
|
1330
|
+
let e = document.createElement("div");
|
|
1331
|
+
e.className = "tcw-flyout-hint", e.textContent = "Right-click a tool to pin it", n.appendChild(e);
|
|
1332
|
+
}
|
|
1333
|
+
this.flyoutEl = n, this.flyoutIdx = e, this.groupWraps[e].appendChild(n);
|
|
1334
|
+
}
|
|
1335
|
+
scheduleHideFlyout() {
|
|
1336
|
+
this.cancelHideFlyout(), this.flyoutHideTimer = setTimeout(() => {
|
|
1337
|
+
this.flyoutHideTimer = null, this.hideFlyout();
|
|
1338
|
+
}, ce);
|
|
1339
|
+
}
|
|
1340
|
+
cancelHideFlyout() {
|
|
1341
|
+
this.flyoutHideTimer !== null && (clearTimeout(this.flyoutHideTimer), this.flyoutHideTimer = null);
|
|
1079
1342
|
}
|
|
1080
1343
|
hideFlyout() {
|
|
1081
|
-
this.flyoutEl &&= (this.flyoutEl.remove(), null);
|
|
1344
|
+
this.cancelHideFlyout(), this.flyoutEl &&= (this.flyoutEl.remove(), null), this.flyoutIdx = -1;
|
|
1082
1345
|
}
|
|
1083
1346
|
update(e) {
|
|
1084
1347
|
this.cursorBtn && this.cursorBtn.classList.toggle("tcw-active", e.activeTool === null);
|
|
1085
1348
|
let { drawingToolGroups: t } = this.config;
|
|
1086
1349
|
for (let n = 0; n < t.length; n++) {
|
|
1087
1350
|
let r = t[n].tools.some((t) => t.value === e.activeTool);
|
|
1088
|
-
this.groupButtons[n].classList.toggle("tcw-active", r);
|
|
1351
|
+
this.groupButtons[n].classList.toggle("tcw-active", r), r && e.activeTool && e.activeTool !== this.groupTools[n] && (this.groupTools[n] = e.activeTool, this.groupIcons[n].innerHTML = w(e.activeTool, O), t[n].tools.length > 1 && this.groupButtons[n].setAttribute("aria-label", `${t[n].label}: ${k(t, e.activeTool)}`));
|
|
1089
1352
|
}
|
|
1090
1353
|
this.flyoutEl && this.flyoutEl.querySelectorAll(".tcw-flyout-item").forEach((t) => {
|
|
1091
1354
|
let n = t;
|
|
1092
1355
|
n.classList.toggle("tcw-active", n.dataset.toolValue === e.activeTool);
|
|
1093
|
-
}), this.magnetBtn && (this.magnetBtn.classList.toggle("tcw-active", e.magnetEnabled), this.magnetBtn.title = e.magnetEnabled ? "Magnet ON" : "Magnet OFF");
|
|
1356
|
+
}), this.magnetBtn && (this.magnetBtn.classList.toggle("tcw-active", e.magnetEnabled), this.magnetBtn.title = e.magnetEnabled ? "Magnet ON" : "Magnet OFF"), this.stayBtn && (this.stayBtn.classList.toggle("tcw-active", e.stayInDrawing), this.stayBtn.setAttribute("aria-pressed", String(e.stayInDrawing)));
|
|
1094
1357
|
}
|
|
1095
1358
|
divider() {
|
|
1096
1359
|
let e = document.createElement("div");
|
|
@@ -1099,11 +1362,11 @@ var se = class {
|
|
|
1099
1362
|
destroy() {
|
|
1100
1363
|
this.hideFlyout(), this.el.remove();
|
|
1101
1364
|
}
|
|
1102
|
-
},
|
|
1365
|
+
}, ue = [
|
|
1103
1366
|
"style",
|
|
1104
1367
|
"display",
|
|
1105
1368
|
"scale"
|
|
1106
|
-
],
|
|
1369
|
+
], de = class {
|
|
1107
1370
|
callbacks;
|
|
1108
1371
|
t;
|
|
1109
1372
|
backdrop = null;
|
|
@@ -1112,8 +1375,8 @@ var se = class {
|
|
|
1112
1375
|
currentSettings = null;
|
|
1113
1376
|
bodyEl = null;
|
|
1114
1377
|
tabButtons = [];
|
|
1115
|
-
constructor(e, t) {
|
|
1116
|
-
this.callbacks = e, this.t = t;
|
|
1378
|
+
constructor(e, t, n = {}, r = () => document.body) {
|
|
1379
|
+
this.available = n, this.host = r, this.callbacks = e, this.t = t;
|
|
1117
1380
|
}
|
|
1118
1381
|
open(e) {
|
|
1119
1382
|
this.currentSettings = { ...e }, this.currentTab = "style", this.buildModal();
|
|
@@ -1124,18 +1387,18 @@ var se = class {
|
|
|
1124
1387
|
buildModal() {
|
|
1125
1388
|
this.backdrop = document.createElement("div"), this.backdrop.className = "tcw-modal-backdrop", this.backdrop.addEventListener("click", () => {
|
|
1126
1389
|
this.callbacks.onClose(), this.close();
|
|
1127
|
-
}),
|
|
1390
|
+
}), this.host().appendChild(this.backdrop), this.modal = document.createElement("div"), this.modal.className = "tcw-modal";
|
|
1128
1391
|
let e = document.createElement("div");
|
|
1129
1392
|
e.className = "tcw-modal-header";
|
|
1130
1393
|
let t = document.createElement("h3");
|
|
1131
1394
|
t.textContent = this.t("settings.title"), e.appendChild(t);
|
|
1132
1395
|
let n = document.createElement("button");
|
|
1133
|
-
n.className = "tcw-modal-close", n.innerHTML =
|
|
1396
|
+
n.className = "tcw-modal-close", n.innerHTML = C("x", 16), n.addEventListener("click", () => {
|
|
1134
1397
|
this.callbacks.onClose(), this.close();
|
|
1135
1398
|
}), e.appendChild(n), this.modal.appendChild(e);
|
|
1136
1399
|
let r = document.createElement("div");
|
|
1137
1400
|
r.className = "tcw-modal-tabs", this.tabButtons = [];
|
|
1138
|
-
for (let e of
|
|
1401
|
+
for (let e of ue) {
|
|
1139
1402
|
let t = document.createElement("button");
|
|
1140
1403
|
t.className = "tcw-modal-tab", t.textContent = this.t(`settings.tab.${e}`), t.dataset.tab = e, e === this.currentTab && t.classList.add("tcw-active"), t.addEventListener("click", () => {
|
|
1141
1404
|
this.currentTab = e, this.tabButtons.forEach((t) => t.classList.toggle("tcw-active", t.dataset.tab === e)), this.renderTabContent();
|
|
@@ -1149,7 +1412,7 @@ var se = class {
|
|
|
1149
1412
|
let o = document.createElement("button");
|
|
1150
1413
|
o.className = "tcw-done-btn", o.textContent = this.t("settings.done"), o.addEventListener("click", () => {
|
|
1151
1414
|
this.callbacks.onClose(), this.close();
|
|
1152
|
-
}), i.appendChild(o), this.modal.appendChild(i),
|
|
1415
|
+
}), i.appendChild(o), this.modal.appendChild(i), this.host().appendChild(this.modal);
|
|
1153
1416
|
let s = (e) => {
|
|
1154
1417
|
e.key === "Escape" && (document.removeEventListener("keydown", s), this.callbacks.onClose(), this.close());
|
|
1155
1418
|
};
|
|
@@ -1184,7 +1447,7 @@ var se = class {
|
|
|
1184
1447
|
}, {
|
|
1185
1448
|
value: "week",
|
|
1186
1449
|
label: "Prior Week (PWH/PWL)"
|
|
1187
|
-
}], (e) => this.patch({ periodLevelsPeriod: e }))), t.appendChild(this.toggleRow("OHLC Legend", e.legendVisible, (e) => this.patch({ legendVisible: e }))), t.appendChild(this.toggleRow("Bar Countdown", e.barCountdown, (e) => this.patch({ barCountdown: e }))), t.appendChild(this.selectRow("Crosshair Mode", e.crosshairMode, [
|
|
1450
|
+
}], (e) => this.patch({ periodLevelsPeriod: e }))), t.appendChild(this.toggleRow("OHLC Legend", e.legendVisible, (e) => this.patch({ legendVisible: e }))), this.available.barCountdown !== !1 && t.appendChild(this.toggleRow("Bar Countdown", e.barCountdown, (e) => this.patch({ barCountdown: e }))), t.appendChild(this.selectRow("Crosshair Mode", e.crosshairMode, [
|
|
1188
1451
|
{
|
|
1189
1452
|
label: "Magnet",
|
|
1190
1453
|
value: "magnet"
|
|
@@ -1260,7 +1523,8 @@ var se = class {
|
|
|
1260
1523
|
renderScaleTab() {
|
|
1261
1524
|
if (!this.bodyEl || !this.currentSettings) return;
|
|
1262
1525
|
let e = this.currentSettings, t = this.section();
|
|
1263
|
-
t.appendChild(this.toggleRow("Auto Scale", e.autoScale, (e) => this.patch({ autoScale: e }))), t.appendChild(this.
|
|
1526
|
+
t.appendChild(this.toggleRow("Auto Scale", e.autoScale, (e) => this.patch({ autoScale: e }))), t.appendChild(this.toggleRow("Invert Scale (Alt+I)", e.invertScale, (e) => this.patch({ invertScale: e })));
|
|
1527
|
+
let n = [
|
|
1264
1528
|
{
|
|
1265
1529
|
value: "regular",
|
|
1266
1530
|
label: "Regular"
|
|
@@ -1277,7 +1541,8 @@ var se = class {
|
|
|
1277
1541
|
value: "indexedTo100",
|
|
1278
1542
|
label: "Indexed to 100"
|
|
1279
1543
|
}
|
|
1280
|
-
]
|
|
1544
|
+
].filter((e) => e.value !== "logarithmic" || this.available.logScale !== !1);
|
|
1545
|
+
t.appendChild(this.selectRow("Price Scale", e.scaleMode, n, (e) => this.patch({ scaleMode: e }))), this.bodyEl.appendChild(t);
|
|
1281
1546
|
}
|
|
1282
1547
|
patch(e) {
|
|
1283
1548
|
this.currentSettings && Object.assign(this.currentSettings, e), this.callbacks.onChange(e);
|
|
@@ -1346,15 +1611,17 @@ var se = class {
|
|
|
1346
1611
|
destroy() {
|
|
1347
1612
|
this.close();
|
|
1348
1613
|
}
|
|
1349
|
-
},
|
|
1614
|
+
}, fe = class {
|
|
1350
1615
|
el;
|
|
1351
1616
|
dotEl;
|
|
1352
1617
|
messageEl;
|
|
1353
1618
|
infoEl;
|
|
1354
|
-
constructor(e) {
|
|
1355
|
-
this.el = document.createElement("div"), this.el.className = "tcw-statusbar";
|
|
1356
|
-
let
|
|
1357
|
-
|
|
1619
|
+
constructor(e, t) {
|
|
1620
|
+
this.el = document.createElement("div"), this.el.className = "tcw-statusbar", t && this.el.appendChild(this.buildRange(t));
|
|
1621
|
+
let n = document.createElement("div");
|
|
1622
|
+
n.className = "tcw-status-right";
|
|
1623
|
+
let r = document.createElement("div");
|
|
1624
|
+
r.className = "tcw-status-indicator", this.dotEl = document.createElement("span"), this.dotEl.className = "tcw-status-dot", r.appendChild(this.dotEl), this.messageEl = document.createElement("span"), r.appendChild(this.messageEl), n.appendChild(r), this.infoEl = document.createElement("span"), this.infoEl.className = "tcw-status-info", n.appendChild(this.infoEl), this.el.appendChild(n), e.appendChild(this.el);
|
|
1358
1625
|
}
|
|
1359
1626
|
update(e) {
|
|
1360
1627
|
this.dotEl.className = "tcw-status-dot", e.connectionState === "connected" ? this.dotEl.classList.add("tcw-connected") : e.connectionState === "error" && this.dotEl.classList.add("tcw-error"), this.messageEl.textContent = e.message, this.infoEl.textContent = `${e.symbol} ${e.timeframe}`;
|
|
@@ -1362,7 +1629,17 @@ var se = class {
|
|
|
1362
1629
|
destroy() {
|
|
1363
1630
|
this.el.remove();
|
|
1364
1631
|
}
|
|
1365
|
-
|
|
1632
|
+
buildRange(e) {
|
|
1633
|
+
let t = document.createElement("div");
|
|
1634
|
+
t.className = "tcw-range-bar", t.setAttribute("role", "group"), t.setAttribute("aria-label", e.groupLabel);
|
|
1635
|
+
for (let n of e.presets) {
|
|
1636
|
+
let r = document.createElement("button");
|
|
1637
|
+
r.type = "button", r.className = "tcw-range-btn", r.dataset.range = n, r.textContent = e.presetLabels?.[n] ?? n, r.addEventListener("click", () => e.onPreset(n)), t.appendChild(r);
|
|
1638
|
+
}
|
|
1639
|
+
let n = document.createElement("button");
|
|
1640
|
+
return n.type = "button", n.className = "tcw-range-btn tcw-range-goto", n.dataset.role = "goto", n.title = `${e.goToLabel} (Alt+G)`, n.setAttribute("aria-label", e.goToLabel), n.innerHTML = C("calendar", 13), n.addEventListener("click", e.onGoTo), t.appendChild(n), t;
|
|
1641
|
+
}
|
|
1642
|
+
}, pe = class {
|
|
1366
1643
|
backdrop = null;
|
|
1367
1644
|
modal = null;
|
|
1368
1645
|
input = null;
|
|
@@ -1372,8 +1649,8 @@ var se = class {
|
|
|
1372
1649
|
selectedIndex = 0;
|
|
1373
1650
|
callbacks;
|
|
1374
1651
|
boundKeydown;
|
|
1375
|
-
constructor(e) {
|
|
1376
|
-
this.callbacks = e, this.boundKeydown = this.handleKeydown.bind(this);
|
|
1652
|
+
constructor(e, t = () => document.body) {
|
|
1653
|
+
this.host = t, this.callbacks = e, this.boundKeydown = this.handleKeydown.bind(this);
|
|
1377
1654
|
}
|
|
1378
1655
|
open(e) {
|
|
1379
1656
|
if (this.backdrop) return;
|
|
@@ -1385,7 +1662,7 @@ var se = class {
|
|
|
1385
1662
|
let r = document.createElement("span");
|
|
1386
1663
|
r.className = "tcw-cmd-hint", r.textContent = "ESC", t.appendChild(r), this.modal.appendChild(t), this.list = document.createElement("div"), this.list.className = "tcw-cmd-list", this.modal.appendChild(this.list);
|
|
1387
1664
|
let i = document.createElement("div");
|
|
1388
|
-
i.className = "tcw-cmd-footer", i.innerHTML = "<span>↑↓ Navigate</span><span>⏎ Select</span><span>Esc Close</span>", this.modal.appendChild(i),
|
|
1665
|
+
i.className = "tcw-cmd-footer", i.innerHTML = "<span>↑↓ Navigate</span><span>⏎ Select</span><span>Esc Close</span>", this.modal.appendChild(i), this.host().append(this.backdrop, this.modal), this.renderList(), this.input.focus(), document.addEventListener("keydown", this.boundKeydown);
|
|
1389
1666
|
}
|
|
1390
1667
|
close() {
|
|
1391
1668
|
document.removeEventListener("keydown", this.boundKeydown), this.backdrop?.remove(), this.modal?.remove(), this.backdrop = null, this.modal = null, this.input = null, this.list = null, this.callbacks.onClose();
|
|
@@ -1487,7 +1764,7 @@ var se = class {
|
|
|
1487
1764
|
destroy() {
|
|
1488
1765
|
this.close();
|
|
1489
1766
|
}
|
|
1490
|
-
},
|
|
1767
|
+
}, me = class {
|
|
1491
1768
|
backdrop = null;
|
|
1492
1769
|
modal = null;
|
|
1493
1770
|
input = null;
|
|
@@ -1499,8 +1776,8 @@ var se = class {
|
|
|
1499
1776
|
callbacks;
|
|
1500
1777
|
boundKeydown;
|
|
1501
1778
|
pickOverride = null;
|
|
1502
|
-
constructor(e) {
|
|
1503
|
-
this.callbacks = e, this.boundKeydown = this.handleKeydown.bind(this);
|
|
1779
|
+
constructor(e, t = () => document.body) {
|
|
1780
|
+
this.host = t, this.callbacks = e, this.boundKeydown = this.handleKeydown.bind(this);
|
|
1504
1781
|
}
|
|
1505
1782
|
open(e, t, n) {
|
|
1506
1783
|
if (this.backdrop) return;
|
|
@@ -1511,11 +1788,11 @@ var se = class {
|
|
|
1511
1788
|
let r = document.createElement("div");
|
|
1512
1789
|
r.className = "tcw-cmd-header";
|
|
1513
1790
|
let i = document.createElement("span");
|
|
1514
|
-
i.className = "tcw-cmd-icon", i.innerHTML =
|
|
1791
|
+
i.className = "tcw-cmd-icon", i.innerHTML = _e(), r.appendChild(i), this.input = document.createElement("input"), this.input.className = "tcw-cmd-input", this.input.type = "text", this.input.placeholder = "Search symbol… (e.g. BTC, ETH, AAPL)", this.input.autocomplete = "off", this.input.spellcheck = !1, this.input.addEventListener("input", () => this.filter()), r.appendChild(this.input);
|
|
1515
1792
|
let a = document.createElement("span");
|
|
1516
1793
|
a.className = "tcw-cmd-hint", a.textContent = "ESC", r.appendChild(a), this.modal.appendChild(r), this.list = document.createElement("div"), this.list.className = "tcw-cmd-list", this.modal.appendChild(this.list);
|
|
1517
1794
|
let o = document.createElement("div");
|
|
1518
|
-
o.className = "tcw-cmd-footer", o.innerHTML = "<span>↑↓ Navigate</span><span>⏎ Open</span><span>Esc Close</span>", this.modal.appendChild(o),
|
|
1795
|
+
o.className = "tcw-cmd-footer", o.innerHTML = "<span>↑↓ Navigate</span><span>⏎ Open</span><span>Esc Close</span>", this.modal.appendChild(o), this.host().append(this.backdrop, this.modal), this.renderList(), this.input.focus(), document.addEventListener("keydown", this.boundKeydown);
|
|
1519
1796
|
}
|
|
1520
1797
|
close() {
|
|
1521
1798
|
document.removeEventListener("keydown", this.boundKeydown), this.backdrop?.remove(), this.modal?.remove(), this.backdrop = null, this.modal = null, this.input = null, this.list = null, this.pickOverride = null, this.callbacks.onClose();
|
|
@@ -1530,7 +1807,7 @@ var se = class {
|
|
|
1530
1807
|
let e = (this.input?.value ?? "").toUpperCase().trim();
|
|
1531
1808
|
e ? this.filtered = this.symbols.map((t) => ({
|
|
1532
1809
|
symbol: t,
|
|
1533
|
-
score:
|
|
1810
|
+
score: he(t.toUpperCase(), e)
|
|
1534
1811
|
})).filter((e) => e.score > 0).sort((e, t) => t.score - e.score) : this.filtered = this.symbols.map((e) => ({
|
|
1535
1812
|
symbol: e,
|
|
1536
1813
|
score: 0
|
|
@@ -1548,7 +1825,7 @@ var se = class {
|
|
|
1548
1825
|
let { symbol: n } = this.filtered[t], r = document.createElement("button");
|
|
1549
1826
|
r.className = "tcw-cmd-item", t === this.selectedIndex && r.classList.add("tcw-selected"), n === this.current && r.classList.add("tcw-active");
|
|
1550
1827
|
let i = document.createElement("span");
|
|
1551
|
-
if (i.className = "tcw-cmd-item-label", i.innerHTML =
|
|
1828
|
+
if (i.className = "tcw-cmd-item-label", i.innerHTML = ge(n, e), r.appendChild(i), n === this.current) {
|
|
1552
1829
|
let e = document.createElement("span");
|
|
1553
1830
|
e.className = "tcw-cmd-check", e.textContent = "✓", r.appendChild(e);
|
|
1554
1831
|
}
|
|
@@ -1585,7 +1862,7 @@ var se = class {
|
|
|
1585
1862
|
}
|
|
1586
1863
|
}
|
|
1587
1864
|
};
|
|
1588
|
-
function
|
|
1865
|
+
function he(e, t) {
|
|
1589
1866
|
if (e === t) return 1e3;
|
|
1590
1867
|
if (e.startsWith(t)) return 500 + (50 - Math.min(50, e.length - t.length));
|
|
1591
1868
|
if (e.includes(t)) return 200 + (50 - Math.min(50, e.length - t.length));
|
|
@@ -1593,44 +1870,43 @@ function E(e, t) {
|
|
|
1593
1870
|
for (let a = 0; a < e.length && n < t.length; a++) e[a] === t[n] && (r >= 0 && (i += a - r - 1), r = a, n++);
|
|
1594
1871
|
return n < t.length ? 0 : 100 - Math.min(99, i);
|
|
1595
1872
|
}
|
|
1596
|
-
function
|
|
1597
|
-
if (!t) return
|
|
1873
|
+
function ge(e, t) {
|
|
1874
|
+
if (!t) return E(e);
|
|
1598
1875
|
let n = e.toUpperCase(), r = "", i = 0;
|
|
1599
|
-
for (let a = 0; a < e.length; a++) i < t.length && n[a] === t[i] ? (r += `<mark style="background:transparent;color:var(--tcw-accent);font-weight:700">${
|
|
1876
|
+
for (let a = 0; a < e.length; a++) i < t.length && n[a] === t[i] ? (r += `<mark style="background:transparent;color:var(--tcw-accent);font-weight:700">${E(e[a])}</mark>`, i++) : r += E(e[a]);
|
|
1600
1877
|
return r;
|
|
1601
1878
|
}
|
|
1602
|
-
function
|
|
1603
|
-
return e.replace(/[&<>"']/g, (e) => ({
|
|
1604
|
-
"&": "&",
|
|
1605
|
-
"<": "<",
|
|
1606
|
-
">": ">",
|
|
1607
|
-
"\"": """,
|
|
1608
|
-
"'": "'"
|
|
1609
|
-
})[e]);
|
|
1610
|
-
}
|
|
1611
|
-
function k() {
|
|
1879
|
+
function _e() {
|
|
1612
1880
|
return "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m21 21-4.3-4.3\"/></svg>";
|
|
1613
1881
|
}
|
|
1614
1882
|
//#endregion
|
|
1615
1883
|
//#region src/widget/WidgetHotkeySheet.ts
|
|
1616
|
-
var
|
|
1884
|
+
var ve = {
|
|
1617
1885
|
"Search & navigation": "hotkeys.group.searchNavigation",
|
|
1618
1886
|
"Chart manipulation": "hotkeys.group.chartManipulation",
|
|
1619
1887
|
"Touch (mobile / tablet)": "hotkeys.group.touch",
|
|
1620
1888
|
Keyboard: "hotkeys.group.keyboard",
|
|
1621
1889
|
Drawing: "hotkeys.group.drawing"
|
|
1622
|
-
},
|
|
1890
|
+
}, A = typeof navigator < "u" && /mac|iphone|ipad|ipod/i.test(navigator.platform || navigator.userAgent) ? "⌘" : "Ctrl", ye = [
|
|
1623
1891
|
{
|
|
1624
1892
|
title: "Search & navigation",
|
|
1625
1893
|
entries: [
|
|
1626
1894
|
{
|
|
1627
|
-
keys: [
|
|
1895
|
+
keys: [A, "K"],
|
|
1628
1896
|
label: "Command palette"
|
|
1629
1897
|
},
|
|
1630
1898
|
{
|
|
1631
|
-
keys: [
|
|
1899
|
+
keys: [A, "P"],
|
|
1632
1900
|
label: "Symbol search"
|
|
1633
1901
|
},
|
|
1902
|
+
{
|
|
1903
|
+
keys: ["Alt", "G"],
|
|
1904
|
+
label: "Go to date"
|
|
1905
|
+
},
|
|
1906
|
+
{
|
|
1907
|
+
keys: ["Alt", "I"],
|
|
1908
|
+
label: "Invert price scale"
|
|
1909
|
+
},
|
|
1634
1910
|
{
|
|
1635
1911
|
keys: ["?"],
|
|
1636
1912
|
label: "Show this sheet"
|
|
@@ -1645,11 +1921,11 @@ var A = {
|
|
|
1645
1921
|
label: "Pan — past the last bar into empty future space"
|
|
1646
1922
|
},
|
|
1647
1923
|
{
|
|
1648
|
-
keys: [
|
|
1924
|
+
keys: [A, "Drag"],
|
|
1649
1925
|
label: "Select drawings in a box"
|
|
1650
1926
|
},
|
|
1651
1927
|
{
|
|
1652
|
-
keys: [
|
|
1928
|
+
keys: [A, "Click"],
|
|
1653
1929
|
label: "Add / remove a drawing from the selection"
|
|
1654
1930
|
},
|
|
1655
1931
|
{
|
|
@@ -1728,12 +2004,12 @@ var A = {
|
|
|
1728
2004
|
title: "Drawing",
|
|
1729
2005
|
entries: [
|
|
1730
2006
|
{
|
|
1731
|
-
keys: [
|
|
2007
|
+
keys: [A, "Z"],
|
|
1732
2008
|
label: "Undo"
|
|
1733
2009
|
},
|
|
1734
2010
|
{
|
|
1735
2011
|
keys: [
|
|
1736
|
-
|
|
2012
|
+
A,
|
|
1737
2013
|
"Shift",
|
|
1738
2014
|
"Z"
|
|
1739
2015
|
],
|
|
@@ -1742,17 +2018,25 @@ var A = {
|
|
|
1742
2018
|
{
|
|
1743
2019
|
keys: ["Esc"],
|
|
1744
2020
|
label: "Cancel active drawing"
|
|
2021
|
+
},
|
|
2022
|
+
{
|
|
2023
|
+
keys: [A, "C"],
|
|
2024
|
+
label: "Copy selected drawings"
|
|
2025
|
+
},
|
|
2026
|
+
{
|
|
2027
|
+
keys: [A, "V"],
|
|
2028
|
+
label: "Paste drawings (into any chart on the page)"
|
|
1745
2029
|
}
|
|
1746
2030
|
]
|
|
1747
2031
|
}
|
|
1748
|
-
],
|
|
2032
|
+
], be = class {
|
|
1749
2033
|
backdrop = null;
|
|
1750
2034
|
modal = null;
|
|
1751
2035
|
callbacks;
|
|
1752
2036
|
t;
|
|
1753
2037
|
boundKeydown;
|
|
1754
|
-
constructor(e, t) {
|
|
1755
|
-
this.callbacks = e, this.t = t, this.boundKeydown = (e) => {
|
|
2038
|
+
constructor(e, t, n = () => document.body) {
|
|
2039
|
+
this.host = n, this.callbacks = e, this.t = t, this.boundKeydown = (e) => {
|
|
1756
2040
|
e.key === "Escape" && (e.preventDefault(), this.close());
|
|
1757
2041
|
};
|
|
1758
2042
|
}
|
|
@@ -1767,12 +2051,12 @@ var A = {
|
|
|
1767
2051
|
n.className = "tcw-modal-body";
|
|
1768
2052
|
let r = document.createElement("div");
|
|
1769
2053
|
r.className = "tcw-hotkey-grid";
|
|
1770
|
-
for (let e of
|
|
2054
|
+
for (let e of ye) {
|
|
1771
2055
|
let t = document.createElement("section");
|
|
1772
2056
|
t.className = "tcw-hotkey-section";
|
|
1773
2057
|
let n = document.createElement("div");
|
|
1774
2058
|
n.className = "tcw-settings-section-title";
|
|
1775
|
-
let i =
|
|
2059
|
+
let i = ve[e.title];
|
|
1776
2060
|
n.textContent = i ? this.t(i) : e.title, t.appendChild(n);
|
|
1777
2061
|
for (let n of e.entries) {
|
|
1778
2062
|
let e = document.createElement("div");
|
|
@@ -1797,7 +2081,7 @@ var A = {
|
|
|
1797
2081
|
let i = document.createElement("div");
|
|
1798
2082
|
i.className = "tcw-modal-footer", i.innerHTML = "<span style=\"font-size:11px;color:var(--tcw-text-muted)\">Press ? anytime to show this sheet</span>";
|
|
1799
2083
|
let a = document.createElement("button");
|
|
1800
|
-
a.className = "tcw-done-btn", a.textContent = this.t("hotkeys.gotIt"), a.addEventListener("click", () => this.close()), i.appendChild(a), this.modal.appendChild(i),
|
|
2084
|
+
a.className = "tcw-done-btn", a.textContent = this.t("hotkeys.gotIt"), a.addEventListener("click", () => this.close()), i.appendChild(a), this.modal.appendChild(i), this.host().append(this.backdrop, this.modal), document.addEventListener("keydown", this.boundKeydown);
|
|
1801
2085
|
}
|
|
1802
2086
|
close() {
|
|
1803
2087
|
document.removeEventListener("keydown", this.boundKeydown), this.backdrop?.remove(), this.modal?.remove(), this.backdrop = null, this.modal = null, this.callbacks.onClose();
|
|
@@ -1808,90 +2092,127 @@ var A = {
|
|
|
1808
2092
|
destroy() {
|
|
1809
2093
|
this.close();
|
|
1810
2094
|
}
|
|
1811
|
-
},
|
|
1812
|
-
|
|
2095
|
+
}, xe = {
|
|
2096
|
+
selectHint: "Click a bar to start the replay",
|
|
2097
|
+
random: "Random bar",
|
|
2098
|
+
realtime: "Back to realtime",
|
|
2099
|
+
barsPerSecond: "bars/s",
|
|
2100
|
+
play: "Play",
|
|
2101
|
+
pause: "Pause",
|
|
2102
|
+
stepBack: "Step back (Shift+←)",
|
|
2103
|
+
stepForward: "Step forward (Shift+→)"
|
|
2104
|
+
}, Se = [
|
|
1813
2105
|
1,
|
|
1814
2106
|
2,
|
|
2107
|
+
3,
|
|
1815
2108
|
5,
|
|
1816
2109
|
10,
|
|
1817
|
-
|
|
1818
|
-
50
|
|
1819
|
-
|
|
1820
|
-
], F = class {
|
|
2110
|
+
25,
|
|
2111
|
+
50
|
|
2112
|
+
], Ce = class {
|
|
1821
2113
|
root = null;
|
|
1822
2114
|
scrubber = null;
|
|
1823
2115
|
progressLabel = null;
|
|
1824
2116
|
playBtn = null;
|
|
2117
|
+
realtimeBtn = null;
|
|
1825
2118
|
state = "stopped";
|
|
2119
|
+
mode = "select";
|
|
2120
|
+
ended = !1;
|
|
1826
2121
|
total = 0;
|
|
1827
2122
|
speed = 5;
|
|
1828
|
-
|
|
1829
|
-
constructor(e) {
|
|
1830
|
-
this.callbacks = e
|
|
2123
|
+
labels;
|
|
2124
|
+
constructor(e, t = {}) {
|
|
2125
|
+
this.callbacks = e, this.labels = {
|
|
2126
|
+
...xe,
|
|
2127
|
+
...t
|
|
2128
|
+
};
|
|
1831
2129
|
}
|
|
1832
2130
|
mount(e, t) {
|
|
1833
|
-
this.root || (this.total = Math.max(0, t.total), this.speed = t.speed ?? 5, this.state = "paused", this.root = document.createElement("div"), this.root.className = "tcw-replay-bar", this.root.innerHTML = this.markup(), e.appendChild(this.root), this.wireEvents(), this.syncUi());
|
|
2131
|
+
this.root || (this.total = Math.max(0, t.total), this.speed = t.speed ?? 5, this.state = "paused", this.mode = t.mode ?? "select", this.root = document.createElement("div"), this.root.className = "tcw-replay-bar", this.root.setAttribute("role", "toolbar"), this.root.setAttribute("aria-label", "Replay"), this.root.innerHTML = this.markup(), e.appendChild(this.root), this.wireEvents(), this.syncUi());
|
|
1834
2132
|
}
|
|
1835
2133
|
unmount() {
|
|
1836
|
-
this.root?.remove(), this.root = null, this.scrubber = null, this.progressLabel = null, this.playBtn = null, this.state = "stopped";
|
|
2134
|
+
this.root?.remove(), this.root = null, this.scrubber = null, this.progressLabel = null, this.playBtn = null, this.realtimeBtn = null, this.state = "stopped", this.ended = !1;
|
|
1837
2135
|
}
|
|
1838
2136
|
isMounted() {
|
|
1839
2137
|
return this.root !== null;
|
|
1840
2138
|
}
|
|
2139
|
+
getMode() {
|
|
2140
|
+
return this.mode;
|
|
2141
|
+
}
|
|
2142
|
+
setMode(e) {
|
|
2143
|
+
let t = !!this.root?.contains(document.activeElement);
|
|
2144
|
+
this.mode = e, this.syncUi(), t && e === "replay" && this.playBtn?.focus();
|
|
2145
|
+
}
|
|
1841
2146
|
setState(e) {
|
|
1842
2147
|
this.state = e, this.syncUi();
|
|
1843
2148
|
}
|
|
2149
|
+
setEnded(e) {
|
|
2150
|
+
this.ended !== e && (this.ended = e, this.syncUi());
|
|
2151
|
+
}
|
|
1844
2152
|
setProgress(e, t) {
|
|
1845
|
-
this.total = t, this.scrubber && (this.scrubber.max = String(Math.max(0, t - 1)), this.scrubber.value = String(e)), this.progressLabel && (this.progressLabel.textContent = `${e + 1} / ${t}`);
|
|
2153
|
+
this.total = t, this.scrubber && (this.scrubber.max = String(Math.max(0, t - 1)), this.scrubber.value = String(e)), this.progressLabel && (this.progressLabel.textContent = `${Math.min(e + 1, t)} / ${t}`);
|
|
1846
2154
|
}
|
|
1847
2155
|
destroy() {
|
|
1848
2156
|
this.unmount();
|
|
1849
2157
|
}
|
|
1850
2158
|
markup() {
|
|
1851
|
-
let e =
|
|
2159
|
+
let e = this.labels, t = Se.map((t) => `<option value="${t}"${t === this.speed ? " selected" : ""}>${t} ${E(e.barsPerSecond)}</option>`).join("");
|
|
1852
2160
|
return `
|
|
1853
|
-
<
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
<
|
|
1858
|
-
|
|
1859
|
-
|
|
2161
|
+
<span class="tcw-replay-group" data-mode="select">
|
|
2162
|
+
<span class="tcw-replay-hint" role="status">${De()}${E(e.selectHint)}</span>
|
|
2163
|
+
<button class="tcw-replay-text-btn" data-act="random">${E(e.random)}</button>
|
|
2164
|
+
</span>
|
|
2165
|
+
<span class="tcw-replay-group" data-mode="replay">
|
|
2166
|
+
<button class="tcw-replay-btn" data-act="stepBack" title="${E(e.stepBack)}" aria-label="${E(e.stepBack)}">${Te()}</button>
|
|
2167
|
+
<button class="tcw-replay-btn tcw-replay-play" data-act="play" title="${E(e.play)}" aria-label="${E(e.play)}">${j()}</button>
|
|
2168
|
+
<button class="tcw-replay-btn" data-act="stepForward" title="${E(e.stepForward)}" aria-label="${E(e.stepForward)}">${Ee()}</button>
|
|
2169
|
+
<input type="range" class="tcw-replay-scrubber" data-act="seek" min="0" max="${Math.max(0, this.total - 1)}" value="0" step="1" aria-label="Replay position" />
|
|
2170
|
+
<span class="tcw-replay-progress" data-tcw="progress">1 / ${this.total}</span>
|
|
2171
|
+
<select class="tcw-replay-speed" data-act="speed" aria-label="Speed">${t}</select>
|
|
2172
|
+
</span>
|
|
2173
|
+
<button class="tcw-replay-text-btn tcw-replay-realtime" data-act="realtime">${E(e.realtime)}</button>
|
|
1860
2174
|
`;
|
|
1861
2175
|
}
|
|
1862
2176
|
wireEvents() {
|
|
1863
|
-
|
|
2177
|
+
let e = this.root;
|
|
2178
|
+
e && (this.scrubber = e.querySelector("input[data-act=\"seek\"]"), this.progressLabel = e.querySelector("[data-tcw=\"progress\"]"), this.playBtn = e.querySelector("button[data-act=\"play\"]"), this.realtimeBtn = e.querySelector("button[data-act=\"realtime\"]"), this.realtimeBtn?.addEventListener("click", () => this.callbacks.onClose()), e.querySelector("[data-act=\"random\"]")?.addEventListener("click", () => this.callbacks.onRandomStart()), e.querySelector("[data-act=\"stepBack\"]")?.addEventListener("click", () => this.callbacks.onStepBack()), e.querySelector("[data-act=\"stepForward\"]")?.addEventListener("click", () => this.callbacks.onStepForward()), this.playBtn?.addEventListener("click", () => {
|
|
1864
2179
|
this.state === "playing" ? this.callbacks.onPause() : this.callbacks.onPlay();
|
|
1865
|
-
}), this.scrubber?.addEventListener("input", () => {
|
|
1866
|
-
let e = Number(this.scrubber.value);
|
|
1867
|
-
this.callbacks.onSeek(e);
|
|
1868
|
-
}), this.root.querySelector("[data-act=\"speed\"]")?.addEventListener("change", (e) => {
|
|
2180
|
+
}), this.scrubber?.addEventListener("input", () => this.callbacks.onSeek(Number(this.scrubber.value))), e.querySelector("[data-act=\"speed\"]")?.addEventListener("change", (e) => {
|
|
1869
2181
|
let t = Number(e.target.value);
|
|
1870
2182
|
this.speed = t, this.callbacks.onSpeedChange(t);
|
|
1871
2183
|
}));
|
|
1872
2184
|
}
|
|
1873
2185
|
syncUi() {
|
|
1874
|
-
|
|
2186
|
+
let e = this.root;
|
|
2187
|
+
if (e) {
|
|
2188
|
+
for (let t of e.querySelectorAll(".tcw-replay-group")) t.hidden = t.dataset.mode !== this.mode;
|
|
2189
|
+
if (e.classList.toggle("tcw-replay-bar--ended", this.ended), this.playBtn) {
|
|
2190
|
+
let e = this.state === "playing";
|
|
2191
|
+
this.playBtn.innerHTML = e ? we() : j(), this.playBtn.title = e ? this.labels.pause : this.labels.play, this.playBtn.setAttribute("aria-label", this.playBtn.title);
|
|
2192
|
+
let t = this.ended && !e;
|
|
2193
|
+
t && document.activeElement === this.playBtn && this.realtimeBtn?.focus(), this.playBtn.disabled = t;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
1875
2196
|
}
|
|
1876
2197
|
};
|
|
1877
|
-
function
|
|
1878
|
-
return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 5v14l11-7L8 5z\"/></svg>";
|
|
2198
|
+
function j() {
|
|
2199
|
+
return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M8 5v14l11-7L8 5z\"/></svg>";
|
|
1879
2200
|
}
|
|
1880
|
-
function
|
|
1881
|
-
return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/></svg>";
|
|
2201
|
+
function we() {
|
|
2202
|
+
return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/></svg>";
|
|
1882
2203
|
}
|
|
1883
|
-
function
|
|
1884
|
-
return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M6 6h2v12H6zM20 6L9 12l11 6V6z\"/></svg>";
|
|
2204
|
+
function Te() {
|
|
2205
|
+
return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M6 6h2v12H6zM20 6L9 12l11 6V6z\"/></svg>";
|
|
1885
2206
|
}
|
|
1886
|
-
function
|
|
1887
|
-
return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M4 6l11 6L4 18V6zM16 6h2v12h-2z\"/></svg>";
|
|
2207
|
+
function Ee() {
|
|
2208
|
+
return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\"><path d=\"M4 6l11 6L4 18V6zM16 6h2v12h-2z\"/></svg>";
|
|
1888
2209
|
}
|
|
1889
|
-
function
|
|
1890
|
-
return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2
|
|
2210
|
+
function De() {
|
|
2211
|
+
return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M12 3v18M8 7l4-4 4 4M8 17l4 4 4-4\"/></svg>";
|
|
1891
2212
|
}
|
|
1892
2213
|
//#endregion
|
|
1893
2214
|
//#region src/widget/WidgetWatchlist.ts
|
|
1894
|
-
var
|
|
2215
|
+
var Oe = class {
|
|
1895
2216
|
el;
|
|
1896
2217
|
listEl;
|
|
1897
2218
|
rowMap = /* @__PURE__ */ new Map();
|
|
@@ -1935,7 +2256,7 @@ var V = class {
|
|
|
1935
2256
|
let t = document.createElement("div");
|
|
1936
2257
|
t.className = "tcw-watchlist-row", t.tabIndex = 0;
|
|
1937
2258
|
let n = document.createElement("div");
|
|
1938
|
-
n.className = "tcw-watchlist-symbol", n.textContent =
|
|
2259
|
+
n.className = "tcw-watchlist-symbol", n.textContent = ke(e);
|
|
1939
2260
|
let r = document.createElement("canvas");
|
|
1940
2261
|
r.className = "tcw-watchlist-spark", r.width = 64, r.height = 22;
|
|
1941
2262
|
let i = document.createElement("div");
|
|
@@ -1959,18 +2280,18 @@ var V = class {
|
|
|
1959
2280
|
renderRow(e) {
|
|
1960
2281
|
let t = this.rowMap.get(e), n = this.entries.get(e);
|
|
1961
2282
|
if (!(!t || !n)) {
|
|
1962
|
-
if (n.lastPrice !== void 0 && (t.priceEl.textContent =
|
|
2283
|
+
if (n.lastPrice !== void 0 && (t.priceEl.textContent = Ae(n.lastPrice, this.locale)), n.lastPrice !== void 0 && n.refPrice !== void 0 && n.refPrice !== 0) {
|
|
1963
2284
|
let e = n.lastPrice - n.refPrice, r = e / n.refPrice * 100, i = e >= 0 ? "+" : "-", a = Math.abs(r).toLocaleString(this.locale, {
|
|
1964
2285
|
minimumFractionDigits: 2,
|
|
1965
2286
|
maximumFractionDigits: 2
|
|
1966
2287
|
});
|
|
1967
2288
|
t.changeEl.textContent = `${i}${a}%`, t.changeEl.className = `tcw-watchlist-change ${e >= 0 ? "tcw-up" : "tcw-down"}`;
|
|
1968
2289
|
} else t.changeEl.textContent = "";
|
|
1969
|
-
n.sparkline && n.sparkline.length >= 2 &&
|
|
2290
|
+
n.sparkline && n.sparkline.length >= 2 && je(t.sparkCanvas, n.sparkline, n.refPrice ?? n.sparkline[0]);
|
|
1970
2291
|
}
|
|
1971
2292
|
}
|
|
1972
2293
|
};
|
|
1973
|
-
function
|
|
2294
|
+
function ke(e) {
|
|
1974
2295
|
for (let t of [
|
|
1975
2296
|
"USDT",
|
|
1976
2297
|
"USD",
|
|
@@ -1980,7 +2301,7 @@ function H(e) {
|
|
|
1980
2301
|
]) if (e.length > t.length && e.endsWith(t)) return e.slice(0, -t.length) + "/" + t;
|
|
1981
2302
|
return e;
|
|
1982
2303
|
}
|
|
1983
|
-
function
|
|
2304
|
+
function Ae(e, t) {
|
|
1984
2305
|
if (e === 0) return "0";
|
|
1985
2306
|
let n = Math.abs(e);
|
|
1986
2307
|
return n >= 1e3 ? e.toLocaleString(t, {
|
|
@@ -1991,7 +2312,7 @@ function U(e, t) {
|
|
|
1991
2312
|
maximumFractionDigits: 4
|
|
1992
2313
|
}) : Number(e.toPrecision(4)).toLocaleString(t, { maximumFractionDigits: 10 });
|
|
1993
2314
|
}
|
|
1994
|
-
function
|
|
2315
|
+
function je(e, t, n) {
|
|
1995
2316
|
let r = e.getContext("2d");
|
|
1996
2317
|
if (!r) return;
|
|
1997
2318
|
let i = window.devicePixelRatio || 1;
|
|
@@ -2009,7 +2330,7 @@ function W(e, t, n) {
|
|
|
2009
2330
|
}
|
|
2010
2331
|
//#endregion
|
|
2011
2332
|
//#region src/widget/WidgetAlertsPanel.ts
|
|
2012
|
-
var
|
|
2333
|
+
var M = [
|
|
2013
2334
|
{
|
|
2014
2335
|
value: "crossing",
|
|
2015
2336
|
label: "Crossing"
|
|
@@ -2030,15 +2351,15 @@ var G = [
|
|
|
2030
2351
|
value: "lessThan",
|
|
2031
2352
|
label: "Less than"
|
|
2032
2353
|
}
|
|
2033
|
-
],
|
|
2034
|
-
function
|
|
2354
|
+
], Me = new Map(M.map((e) => [e.value, e.label]));
|
|
2355
|
+
function Ne(e) {
|
|
2035
2356
|
return Math.round(e * 1e6) / 1e6;
|
|
2036
2357
|
}
|
|
2037
|
-
function
|
|
2358
|
+
function Pe(e) {
|
|
2038
2359
|
let t = Math.abs(e);
|
|
2039
2360
|
return t >= 1e3 || t >= 1 ? e.toFixed(2) : e.toPrecision(4);
|
|
2040
2361
|
}
|
|
2041
|
-
var
|
|
2362
|
+
var Fe = class {
|
|
2042
2363
|
el;
|
|
2043
2364
|
listEl;
|
|
2044
2365
|
priceInput;
|
|
@@ -2060,16 +2381,16 @@ var ue = class {
|
|
|
2060
2381
|
let r = document.createElement("span");
|
|
2061
2382
|
r.textContent = "Price Alerts";
|
|
2062
2383
|
let i = document.createElement("button");
|
|
2063
|
-
i.className = "tcw-alerts-close", i.type = "button", i.setAttribute("aria-label", "Close alerts"), i.innerHTML =
|
|
2384
|
+
i.className = "tcw-alerts-close", i.type = "button", i.setAttribute("aria-label", "Close alerts"), i.innerHTML = C("x", 14), i.addEventListener("click", () => this.close()), n.appendChild(r), n.appendChild(i), this.el.appendChild(n);
|
|
2064
2385
|
let a = document.createElement("form");
|
|
2065
2386
|
a.className = "tcw-alerts-form", this.sourceSelect = document.createElement("select"), this.sourceSelect.className = "tcw-alerts-source", this.sourceSelect.addEventListener("change", () => this.prefillPrice()), this.priceInput = document.createElement("input"), this.priceInput.type = "number", this.priceInput.step = "any", this.priceInput.placeholder = "Value", this.priceInput.className = "tcw-alerts-price", this.priceInput.required = !0, this.conditionSelect = document.createElement("select"), this.conditionSelect.className = "tcw-alerts-condition";
|
|
2066
|
-
for (let e of
|
|
2387
|
+
for (let e of M) {
|
|
2067
2388
|
let t = document.createElement("option");
|
|
2068
2389
|
t.value = e.value, t.textContent = e.label, this.conditionSelect.appendChild(t);
|
|
2069
2390
|
}
|
|
2070
2391
|
this.messageInput = document.createElement("input"), this.messageInput.type = "text", this.messageInput.placeholder = "Note (optional)", this.messageInput.className = "tcw-alerts-message";
|
|
2071
2392
|
let o = document.createElement("button");
|
|
2072
|
-
o.type = "submit", o.className = "tcw-alerts-add", o.innerHTML = `${
|
|
2393
|
+
o.type = "submit", o.className = "tcw-alerts-add", o.innerHTML = `${C("plus", 14)}<span>Add alert</span>`, a.appendChild(this.sourceSelect), a.appendChild(this.priceInput), a.appendChild(this.conditionSelect), a.appendChild(this.messageInput), a.appendChild(o), a.addEventListener("submit", (e) => {
|
|
2073
2394
|
e.preventDefault(), this.submitForm();
|
|
2074
2395
|
}), this.el.appendChild(a), this.listEl = document.createElement("div"), this.listEl.className = "tcw-alerts-list", this.el.appendChild(this.listEl), this.emptyEl = document.createElement("div"), this.emptyEl.className = "tcw-alerts-empty", this.emptyEl.textContent = "No alerts yet. Add one above, or right-click the chart.", this.listEl.appendChild(this.emptyEl), this.renderSources(), e.appendChild(this.el);
|
|
2075
2396
|
}
|
|
@@ -2099,7 +2420,7 @@ var ue = class {
|
|
|
2099
2420
|
}
|
|
2100
2421
|
prefillPrice() {
|
|
2101
2422
|
let e = this.callbacks.getChannelValue(this.sourceSelect.value || "price");
|
|
2102
|
-
this.priceInput.value = e === null ? "" : String(
|
|
2423
|
+
this.priceInput.value = e === null ? "" : String(Ne(e));
|
|
2103
2424
|
}
|
|
2104
2425
|
close() {
|
|
2105
2426
|
this.open = !1, this.el.hidden = !0;
|
|
@@ -2128,7 +2449,7 @@ var ue = class {
|
|
|
2128
2449
|
n.className = "tcw-alerts-info";
|
|
2129
2450
|
let r = document.createElement("div");
|
|
2130
2451
|
r.className = "tcw-alerts-row-main";
|
|
2131
|
-
let i =
|
|
2452
|
+
let i = Me.get(e.condition) ?? e.condition, a = e.channel && e.channel !== "price", o = a ? Pe(e.price) : this.callbacks.formatPrice(e.price);
|
|
2132
2453
|
if (r.textContent = `${a && e.label ? `${e.label} ` : ""}${i} ${o}`, n.appendChild(r), e.message) {
|
|
2133
2454
|
let t = document.createElement("div");
|
|
2134
2455
|
t.className = "tcw-alerts-row-note", t.textContent = e.message, n.appendChild(t);
|
|
@@ -2138,10 +2459,10 @@ var ue = class {
|
|
|
2138
2459
|
e.className = "tcw-alerts-badge", e.textContent = "triggered", r.appendChild(e);
|
|
2139
2460
|
}
|
|
2140
2461
|
let s = document.createElement("button");
|
|
2141
|
-
s.type = "button", s.className = "tcw-alerts-del", s.setAttribute("aria-label", "Delete alert"), s.innerHTML =
|
|
2462
|
+
s.type = "button", s.className = "tcw-alerts-del", s.setAttribute("aria-label", "Delete alert"), s.innerHTML = C("trash", 14), s.addEventListener("click", () => this.callbacks.onRemove(e.id)), t.appendChild(n), t.appendChild(s), this.listEl.appendChild(t);
|
|
2142
2463
|
}
|
|
2143
2464
|
}
|
|
2144
|
-
},
|
|
2465
|
+
}, Ie = class {
|
|
2145
2466
|
el;
|
|
2146
2467
|
indicatorsEl;
|
|
2147
2468
|
drawingsEl;
|
|
@@ -2155,7 +2476,7 @@ var ue = class {
|
|
|
2155
2476
|
let r = document.createElement("span");
|
|
2156
2477
|
r.textContent = "Objects";
|
|
2157
2478
|
let i = document.createElement("button");
|
|
2158
|
-
i.type = "button", i.className = "tcw-tree-close", i.setAttribute("aria-label", "Close objects"), i.innerHTML =
|
|
2479
|
+
i.type = "button", i.className = "tcw-tree-close", i.setAttribute("aria-label", "Close objects"), i.innerHTML = C("x", 14), i.addEventListener("click", () => this.close()), n.appendChild(r), n.appendChild(i), this.el.appendChild(n), this.indicatorsEl = this.makeSection("Indicators"), this.drawingsEl = this.makeSection("Drawings"), this.callbacks.onAddCompare && (this.compareEl = this.makeSection("Compare", () => this.callbacks.onAddCompare?.())), e.appendChild(this.el);
|
|
2159
2480
|
}
|
|
2160
2481
|
isOpen() {
|
|
2161
2482
|
return this.open;
|
|
@@ -2183,7 +2504,7 @@ var ue = class {
|
|
|
2183
2504
|
let i = document.createElement("span");
|
|
2184
2505
|
if (i.textContent = e, r.appendChild(i), t) {
|
|
2185
2506
|
let n = document.createElement("button");
|
|
2186
|
-
n.type = "button", n.className = "tcw-tree-add", n.setAttribute("aria-label", `Add ${e.toLowerCase()}`), n.title = `Add ${e.toLowerCase()}`, n.innerHTML =
|
|
2507
|
+
n.type = "button", n.className = "tcw-tree-add", n.setAttribute("aria-label", `Add ${e.toLowerCase()}`), n.title = `Add ${e.toLowerCase()}`, n.innerHTML = C("plus", 13), n.addEventListener("click", t), r.appendChild(n);
|
|
2187
2508
|
}
|
|
2188
2509
|
let a = document.createElement("div");
|
|
2189
2510
|
return a.className = "tcw-tree-list", n.appendChild(r), n.appendChild(a), this.el.appendChild(n), a;
|
|
@@ -2236,9 +2557,9 @@ var ue = class {
|
|
|
2236
2557
|
}
|
|
2237
2558
|
iconButton(e, t, n, r) {
|
|
2238
2559
|
let i = document.createElement("button");
|
|
2239
|
-
return i.type = "button", i.className = `tcw-tree-btn ${n}`.trim(), i.setAttribute("aria-label", t), i.title = t, i.innerHTML =
|
|
2560
|
+
return i.type = "button", i.className = `tcw-tree-btn ${n}`.trim(), i.setAttribute("aria-label", t), i.title = t, i.innerHTML = C(e, 14), i.addEventListener("click", r), i;
|
|
2240
2561
|
}
|
|
2241
|
-
},
|
|
2562
|
+
}, Le = {
|
|
2242
2563
|
trendLine: "Trend Line",
|
|
2243
2564
|
ray: "Ray",
|
|
2244
2565
|
extendedLine: "Extended Line",
|
|
@@ -2281,16 +2602,16 @@ var ue = class {
|
|
|
2281
2602
|
volumeProfileRange: "Volume Profile",
|
|
2282
2603
|
riskReward: "Long/Short Position"
|
|
2283
2604
|
};
|
|
2284
|
-
function
|
|
2285
|
-
return
|
|
2605
|
+
function Re(e) {
|
|
2606
|
+
return Le[e] ?? e;
|
|
2286
2607
|
}
|
|
2287
2608
|
//#endregion
|
|
2288
2609
|
//#region src/widget/WidgetIndicatorSettings.ts
|
|
2289
|
-
var
|
|
2290
|
-
function
|
|
2610
|
+
var ze = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
|
|
2611
|
+
function Be(e) {
|
|
2291
2612
|
return e.replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/^./, (e) => e.toUpperCase());
|
|
2292
2613
|
}
|
|
2293
|
-
var
|
|
2614
|
+
var Ve = class {
|
|
2294
2615
|
backdrop;
|
|
2295
2616
|
modal;
|
|
2296
2617
|
bodyEl;
|
|
@@ -2305,7 +2626,7 @@ var ge = class {
|
|
|
2305
2626
|
let n = document.createElement("div");
|
|
2306
2627
|
n.className = "tcw-modal-header", this.titleEl = document.createElement("h3"), this.titleEl.textContent = "Indicator";
|
|
2307
2628
|
let r = document.createElement("button");
|
|
2308
|
-
r.className = "tcw-modal-close", r.setAttribute("aria-label", "Close"), r.innerHTML =
|
|
2629
|
+
r.className = "tcw-modal-close", r.setAttribute("aria-label", "Close"), r.innerHTML = C("x", 16), r.addEventListener("click", () => this.close()), n.appendChild(this.titleEl), n.appendChild(r), this.bodyEl = document.createElement("div"), this.bodyEl.className = "tcw-modal-body";
|
|
2309
2630
|
let i = document.createElement("div");
|
|
2310
2631
|
i.className = "tcw-modal-footer";
|
|
2311
2632
|
let a = document.createElement("button");
|
|
@@ -2355,9 +2676,9 @@ var ge = class {
|
|
|
2355
2676
|
let t = document.createElement("div");
|
|
2356
2677
|
t.className = "tcw-settings-row";
|
|
2357
2678
|
let n = document.createElement("span");
|
|
2358
|
-
n.className = "tcw-settings-label", n.textContent =
|
|
2679
|
+
n.className = "tcw-settings-label", n.textContent = Be(e), t.appendChild(n);
|
|
2359
2680
|
let r = this.currentValue(e), i = typeof (this.target?.defaults[e] ?? r);
|
|
2360
|
-
return i === "boolean" ? t.appendChild(this.toggleControl(e, !!r)) : i === "number" ? t.appendChild(this.numberControl(e, Number(r))) : typeof r == "string" &&
|
|
2681
|
+
return i === "boolean" ? t.appendChild(this.toggleControl(e, !!r)) : i === "number" ? t.appendChild(this.numberControl(e, Number(r))) : typeof r == "string" && ze.test(r) ? t.appendChild(this.colorControl(e, r)) : t.appendChild(this.textControl(e, String(r ?? ""))), t;
|
|
2361
2682
|
}
|
|
2362
2683
|
toggleControl(e, t) {
|
|
2363
2684
|
let n = document.createElement("button");
|
|
@@ -2375,27 +2696,27 @@ var ge = class {
|
|
|
2375
2696
|
}
|
|
2376
2697
|
colorControl(e, t) {
|
|
2377
2698
|
let n = document.createElement("input");
|
|
2378
|
-
return n.type = "color", n.className = "tcw-indi-color", n.value = t.length === 4 ?
|
|
2699
|
+
return n.type = "color", n.className = "tcw-indi-color", n.value = t.length === 4 ? He(t) : t, n.addEventListener("input", () => this.setValue(e, n.value)), n;
|
|
2379
2700
|
}
|
|
2380
2701
|
textControl(e, t) {
|
|
2381
2702
|
let n = document.createElement("input");
|
|
2382
2703
|
return n.type = "text", n.className = "tcw-indi-input", n.value = t, n.addEventListener("change", () => this.setValue(e, n.value)), n;
|
|
2383
2704
|
}
|
|
2384
2705
|
};
|
|
2385
|
-
function
|
|
2706
|
+
function He(e) {
|
|
2386
2707
|
let t = e[1], n = e[2], r = e[3];
|
|
2387
2708
|
return `#${t}${t}${n}${n}${r}${r}`;
|
|
2388
2709
|
}
|
|
2389
2710
|
//#endregion
|
|
2390
2711
|
//#region src/widget/DrawingTemplateStore.ts
|
|
2391
|
-
function
|
|
2712
|
+
function Ue() {
|
|
2392
2713
|
try {
|
|
2393
2714
|
return typeof localStorage < "u" ? localStorage : null;
|
|
2394
2715
|
} catch {
|
|
2395
2716
|
return null;
|
|
2396
2717
|
}
|
|
2397
2718
|
}
|
|
2398
|
-
var
|
|
2719
|
+
var We = [
|
|
2399
2720
|
"color",
|
|
2400
2721
|
"lineWidth",
|
|
2401
2722
|
"lineStyle",
|
|
@@ -2403,15 +2724,15 @@ var ye = [
|
|
|
2403
2724
|
"fillOpacity",
|
|
2404
2725
|
"fontSize"
|
|
2405
2726
|
];
|
|
2406
|
-
function
|
|
2727
|
+
function N(e) {
|
|
2407
2728
|
if (!e || typeof e != "object") return {};
|
|
2408
2729
|
let t = {}, n = e;
|
|
2409
|
-
for (let e of
|
|
2730
|
+
for (let e of We) n[e] !== void 0 && (t[e] = n[e]);
|
|
2410
2731
|
return t;
|
|
2411
2732
|
}
|
|
2412
|
-
var
|
|
2733
|
+
var Ge = class {
|
|
2413
2734
|
templates = [];
|
|
2414
|
-
constructor(e = "tcw:draw-templates", t =
|
|
2735
|
+
constructor(e = "tcw:draw-templates", t = Ue()) {
|
|
2415
2736
|
this.storageKey = e, this.storage = t, this.load();
|
|
2416
2737
|
}
|
|
2417
2738
|
list() {
|
|
@@ -2430,7 +2751,7 @@ var be = class {
|
|
|
2430
2751
|
save(e, t) {
|
|
2431
2752
|
let n = e.trim();
|
|
2432
2753
|
if (!n) return;
|
|
2433
|
-
let r =
|
|
2754
|
+
let r = N(t);
|
|
2434
2755
|
this.templates = this.templates.filter((e) => e.name !== n), this.templates.push({
|
|
2435
2756
|
name: n,
|
|
2436
2757
|
style: r
|
|
@@ -2451,7 +2772,7 @@ var be = class {
|
|
|
2451
2772
|
if (!Array.isArray(t)) return;
|
|
2452
2773
|
this.templates = t.filter((e) => e && typeof e.name == "string").map((e) => ({
|
|
2453
2774
|
name: e.name,
|
|
2454
|
-
style:
|
|
2775
|
+
style: N(e.style)
|
|
2455
2776
|
}));
|
|
2456
2777
|
} catch {}
|
|
2457
2778
|
}
|
|
@@ -2460,7 +2781,7 @@ var be = class {
|
|
|
2460
2781
|
this.storage.setItem(this.storageKey, JSON.stringify(this.templates));
|
|
2461
2782
|
} catch {}
|
|
2462
2783
|
}
|
|
2463
|
-
},
|
|
2784
|
+
}, Ke = [
|
|
2464
2785
|
"#4c8dff",
|
|
2465
2786
|
"#1fa874",
|
|
2466
2787
|
"#e8505b",
|
|
@@ -2469,16 +2790,16 @@ var be = class {
|
|
|
2469
2790
|
"#1398a8",
|
|
2470
2791
|
"#ffffff",
|
|
2471
2792
|
"#7d8696"
|
|
2472
|
-
],
|
|
2793
|
+
], qe = [
|
|
2473
2794
|
1,
|
|
2474
2795
|
2,
|
|
2475
2796
|
3,
|
|
2476
2797
|
4
|
|
2477
|
-
],
|
|
2798
|
+
], Je = [
|
|
2478
2799
|
"solid",
|
|
2479
2800
|
"dashed",
|
|
2480
2801
|
"dotted"
|
|
2481
|
-
],
|
|
2802
|
+
], Ye = class {
|
|
2482
2803
|
el;
|
|
2483
2804
|
templatesEl;
|
|
2484
2805
|
nameInput;
|
|
@@ -2492,13 +2813,13 @@ var be = class {
|
|
|
2492
2813
|
let i = document.createElement("span");
|
|
2493
2814
|
i.textContent = "Drawing Style";
|
|
2494
2815
|
let a = document.createElement("button");
|
|
2495
|
-
a.type = "button", a.className = "tcw-style-close", a.setAttribute("aria-label", "Close"), a.innerHTML =
|
|
2816
|
+
a.type = "button", a.className = "tcw-style-close", a.setAttribute("aria-label", "Close"), a.innerHTML = C("x", 14), a.addEventListener("click", () => this.close()), r.appendChild(i), r.appendChild(a), this.el.appendChild(r), this.el.appendChild(this.colorRow()), this.el.appendChild(this.widthRow()), this.el.appendChild(this.lineStyleRow());
|
|
2496
2817
|
let o = document.createElement("div");
|
|
2497
2818
|
o.className = "tcw-style-subhead", o.textContent = "Templates", this.el.appendChild(o);
|
|
2498
2819
|
let s = document.createElement("div");
|
|
2499
2820
|
s.className = "tcw-style-save", this.nameInput = document.createElement("input"), this.nameInput.type = "text", this.nameInput.placeholder = "Template name", this.nameInput.className = "tcw-style-name";
|
|
2500
2821
|
let c = document.createElement("button");
|
|
2501
|
-
c.type = "button", c.className = "tcw-style-savebtn", c.innerHTML = `${
|
|
2822
|
+
c.type = "button", c.className = "tcw-style-savebtn", c.innerHTML = `${C("plus", 13)}<span>Save</span>`, c.addEventListener("click", () => this.saveTemplate()), s.appendChild(this.nameInput), s.appendChild(c), this.el.appendChild(s), this.templatesEl = document.createElement("div"), this.templatesEl.className = "tcw-style-templates", this.el.appendChild(this.templatesEl), e.appendChild(this.el), this.renderTemplates();
|
|
2502
2823
|
}
|
|
2503
2824
|
isOpen() {
|
|
2504
2825
|
return this.open;
|
|
@@ -2518,7 +2839,7 @@ var be = class {
|
|
|
2518
2839
|
colorRow() {
|
|
2519
2840
|
let e = document.createElement("div");
|
|
2520
2841
|
e.className = "tcw-style-row tcw-style-colors";
|
|
2521
|
-
for (let t of
|
|
2842
|
+
for (let t of Ke) {
|
|
2522
2843
|
let n = document.createElement("button");
|
|
2523
2844
|
n.type = "button", n.className = "tcw-style-swatch", n.style.background = t, n.dataset.color = t, n.setAttribute("aria-label", t), n.addEventListener("click", () => this.applyStyle({ color: t })), e.appendChild(n);
|
|
2524
2845
|
}
|
|
@@ -2528,7 +2849,7 @@ var be = class {
|
|
|
2528
2849
|
widthRow() {
|
|
2529
2850
|
let e = document.createElement("div");
|
|
2530
2851
|
e.className = "tcw-style-row tcw-style-widths";
|
|
2531
|
-
for (let t of
|
|
2852
|
+
for (let t of qe) {
|
|
2532
2853
|
let n = document.createElement("button");
|
|
2533
2854
|
n.type = "button", n.className = "tcw-style-width", n.dataset.width = String(t);
|
|
2534
2855
|
let r = document.createElement("span");
|
|
@@ -2539,7 +2860,7 @@ var be = class {
|
|
|
2539
2860
|
lineStyleRow() {
|
|
2540
2861
|
let e = document.createElement("div");
|
|
2541
2862
|
e.className = "tcw-style-row tcw-style-dashes";
|
|
2542
|
-
for (let t of
|
|
2863
|
+
for (let t of Je) {
|
|
2543
2864
|
let n = document.createElement("button");
|
|
2544
2865
|
n.type = "button", n.className = "tcw-style-dash", n.dataset.linestyle = t, n.textContent = t === "solid" ? "──" : t === "dashed" ? "╌╌" : "┄┄", n.title = t, n.addEventListener("click", () => this.applyStyle({ lineStyle: t })), e.appendChild(n);
|
|
2545
2866
|
}
|
|
@@ -2582,7 +2903,7 @@ var be = class {
|
|
|
2582
2903
|
this.callbacks.onStyleChange({ ...t.style }), this.syncActiveControls();
|
|
2583
2904
|
});
|
|
2584
2905
|
let a = document.createElement("button");
|
|
2585
|
-
a.type = "button", a.className = "tcw-style-tdel", a.setAttribute("aria-label", "Delete template"), a.innerHTML =
|
|
2906
|
+
a.type = "button", a.className = "tcw-style-tdel", a.setAttribute("aria-label", "Delete template"), a.innerHTML = C("trash", 13), a.addEventListener("click", () => {
|
|
2586
2907
|
this.store.remove(t.name), this.renderTemplates();
|
|
2587
2908
|
}), e.appendChild(n), e.appendChild(a), this.templatesEl.appendChild(e);
|
|
2588
2909
|
}
|
|
@@ -2590,16 +2911,16 @@ var be = class {
|
|
|
2590
2911
|
};
|
|
2591
2912
|
//#endregion
|
|
2592
2913
|
//#region src/widget/DrawingFavoritesStore.ts
|
|
2593
|
-
function
|
|
2914
|
+
function Xe() {
|
|
2594
2915
|
try {
|
|
2595
2916
|
return typeof localStorage < "u" ? localStorage : null;
|
|
2596
2917
|
} catch {
|
|
2597
2918
|
return null;
|
|
2598
2919
|
}
|
|
2599
2920
|
}
|
|
2600
|
-
var
|
|
2921
|
+
var P = class {
|
|
2601
2922
|
favorites = [];
|
|
2602
|
-
constructor(e = "tcw:draw-favorites", t =
|
|
2923
|
+
constructor(e = "tcw:draw-favorites", t = Xe()) {
|
|
2603
2924
|
this.storageKey = e, this.storage = t, this.load();
|
|
2604
2925
|
}
|
|
2605
2926
|
list() {
|
|
@@ -2633,7 +2954,397 @@ var Ee = class {
|
|
|
2633
2954
|
this.storage.setItem(this.storageKey, JSON.stringify(this.favorites));
|
|
2634
2955
|
} catch {}
|
|
2635
2956
|
}
|
|
2636
|
-
},
|
|
2957
|
+
}, Ze = [
|
|
2958
|
+
"1m",
|
|
2959
|
+
"3m",
|
|
2960
|
+
"5m",
|
|
2961
|
+
"15m",
|
|
2962
|
+
"30m",
|
|
2963
|
+
"1h",
|
|
2964
|
+
"2h",
|
|
2965
|
+
"4h",
|
|
2966
|
+
"1d",
|
|
2967
|
+
"1w",
|
|
2968
|
+
"1M"
|
|
2969
|
+
], Qe = [
|
|
2970
|
+
"1m",
|
|
2971
|
+
"5m",
|
|
2972
|
+
"15m",
|
|
2973
|
+
"1h",
|
|
2974
|
+
"4h",
|
|
2975
|
+
"1d"
|
|
2976
|
+
], $e = 6;
|
|
2977
|
+
function F(e) {
|
|
2978
|
+
let t = e.slice(-1);
|
|
2979
|
+
return t === "h" || t === "d" || t === "w" ? e.slice(0, -1) + t.toUpperCase() : e;
|
|
2980
|
+
}
|
|
2981
|
+
var et = (e) => Number.isFinite(o(e));
|
|
2982
|
+
function tt(e, t) {
|
|
2983
|
+
let n = t?.length ? t : null, r = e?.length ? e : n ?? Ze;
|
|
2984
|
+
return [...new Set(r)].filter((e) => et(e) && (!n || n.includes(e))).sort((e, t) => o(e) - o(t));
|
|
2985
|
+
}
|
|
2986
|
+
function nt(e, t, n = !1) {
|
|
2987
|
+
if (t?.length) return e.filter((e) => t.includes(e));
|
|
2988
|
+
if (n) return [...e];
|
|
2989
|
+
let r = e.filter((e) => Qe.includes(e));
|
|
2990
|
+
return r.length > 0 ? r : e.slice(0, $e);
|
|
2991
|
+
}
|
|
2992
|
+
//#endregion
|
|
2993
|
+
//#region src/widget/WidgetGoToDate.ts
|
|
2994
|
+
var I = 6e4, rt = /^(\d{4})-(\d{2})-(\d{2})$/, it = /^(\d{2}):(\d{2})$/;
|
|
2995
|
+
function at(e, t, n) {
|
|
2996
|
+
let r = rt.exec(e), i = t ? it.exec(t) : [
|
|
2997
|
+
"",
|
|
2998
|
+
"00",
|
|
2999
|
+
"00"
|
|
3000
|
+
];
|
|
3001
|
+
if (!r || !i) return null;
|
|
3002
|
+
let [a, o, s, c, l] = [
|
|
3003
|
+
+r[1],
|
|
3004
|
+
+r[2],
|
|
3005
|
+
+r[3],
|
|
3006
|
+
+i[1],
|
|
3007
|
+
+i[2]
|
|
3008
|
+
];
|
|
3009
|
+
if (o < 1 || o > 12 || s < 1 || s > 31 || c > 23 || l > 59) return null;
|
|
3010
|
+
if (n === null) {
|
|
3011
|
+
let e = new Date(a, o - 1, s, c, l);
|
|
3012
|
+
return e.getDate() === s ? e.getTime() : null;
|
|
3013
|
+
}
|
|
3014
|
+
let u = Date.UTC(a, o - 1, s, c, l);
|
|
3015
|
+
return new Date(u).getUTCDate() === s ? u - n * I : null;
|
|
3016
|
+
}
|
|
3017
|
+
function ot(e, t) {
|
|
3018
|
+
let n = (e) => String(e).padStart(2, "0");
|
|
3019
|
+
if (t === null) {
|
|
3020
|
+
let t = new Date(e);
|
|
3021
|
+
return {
|
|
3022
|
+
date: `${t.getFullYear()}-${n(t.getMonth() + 1)}-${n(t.getDate())}`,
|
|
3023
|
+
time: `${n(t.getHours())}:${n(t.getMinutes())}`
|
|
3024
|
+
};
|
|
3025
|
+
}
|
|
3026
|
+
let r = new Date(e + t * I);
|
|
3027
|
+
return {
|
|
3028
|
+
date: `${r.getUTCFullYear()}-${n(r.getUTCMonth() + 1)}-${n(r.getUTCDate())}`,
|
|
3029
|
+
time: `${n(r.getUTCHours())}:${n(r.getUTCMinutes())}`
|
|
3030
|
+
};
|
|
3031
|
+
}
|
|
3032
|
+
var st = class {
|
|
3033
|
+
form = null;
|
|
3034
|
+
returnFocus = null;
|
|
3035
|
+
opener = null;
|
|
3036
|
+
onOutside = (e) => {
|
|
3037
|
+
let t = e.target;
|
|
3038
|
+
this.form && !this.form.contains(t) && !this.opener?.contains(t) && this.close();
|
|
3039
|
+
};
|
|
3040
|
+
constructor(e, t, n) {
|
|
3041
|
+
this.host = e, this.labels = t, this.onSubmit = n;
|
|
3042
|
+
}
|
|
3043
|
+
isOpen() {
|
|
3044
|
+
return this.form !== null;
|
|
3045
|
+
}
|
|
3046
|
+
open(e, t = null) {
|
|
3047
|
+
this.close(), this.opener = t, this.returnFocus = document.activeElement;
|
|
3048
|
+
let n = document.createElement("form");
|
|
3049
|
+
n.className = "tcw-goto", n.setAttribute("role", "dialog"), n.setAttribute("aria-label", this.labels.title);
|
|
3050
|
+
let r = document.createElement("div");
|
|
3051
|
+
r.className = "tcw-goto-title", r.textContent = this.labels.title;
|
|
3052
|
+
let i = this.field(n, this.labels.date, "date", e.date);
|
|
3053
|
+
i.required = !0;
|
|
3054
|
+
let a = this.field(n, this.labels.time, "time", e.time), o = document.createElement("div");
|
|
3055
|
+
o.className = "tcw-goto-actions";
|
|
3056
|
+
let s = document.createElement("button");
|
|
3057
|
+
s.type = "button", s.className = "tcw-btn", s.textContent = this.labels.cancel, s.addEventListener("click", () => this.close());
|
|
3058
|
+
let c = document.createElement("button");
|
|
3059
|
+
c.type = "submit", c.className = "tcw-btn tcw-goto-submit", c.textContent = this.labels.submit, o.append(s, c), n.prepend(r), n.append(o), n.addEventListener("submit", (e) => {
|
|
3060
|
+
if (e.preventDefault(), !i.value) return;
|
|
3061
|
+
let t = {
|
|
3062
|
+
date: i.value,
|
|
3063
|
+
time: a.value
|
|
3064
|
+
};
|
|
3065
|
+
this.close(), this.onSubmit(t);
|
|
3066
|
+
}), n.addEventListener("keydown", (e) => {
|
|
3067
|
+
e.stopPropagation(), e.key === "Escape" && this.close();
|
|
3068
|
+
}), this.host.appendChild(n), this.form = n, document.addEventListener("mousedown", this.onOutside), i.focus();
|
|
3069
|
+
}
|
|
3070
|
+
close() {
|
|
3071
|
+
if (!this.form) return;
|
|
3072
|
+
document.removeEventListener("mousedown", this.onOutside), this.form.remove(), this.form = null;
|
|
3073
|
+
let e = this.returnFocus;
|
|
3074
|
+
this.returnFocus = null, e?.isConnected && e.focus();
|
|
3075
|
+
}
|
|
3076
|
+
destroy() {
|
|
3077
|
+
this.close();
|
|
3078
|
+
}
|
|
3079
|
+
field(e, t, n, r) {
|
|
3080
|
+
let i = document.createElement("label");
|
|
3081
|
+
i.className = "tcw-goto-field";
|
|
3082
|
+
let a = document.createElement("span");
|
|
3083
|
+
a.textContent = t;
|
|
3084
|
+
let o = document.createElement("input");
|
|
3085
|
+
return o.type = n, o.value = r, o.className = "tcw-goto-input", i.append(a, o), e.appendChild(i), o;
|
|
3086
|
+
}
|
|
3087
|
+
}, L = 8, R = 4, z = 3, ct = "[title]:not([title=\"\"]), [data-tip]:not([data-tip=\"\"])", lt = class {
|
|
3088
|
+
el;
|
|
3089
|
+
target = null;
|
|
3090
|
+
source = null;
|
|
3091
|
+
pressed = null;
|
|
3092
|
+
showTimer = null;
|
|
3093
|
+
watchFrame = 0;
|
|
3094
|
+
warmUntil = 0;
|
|
3095
|
+
observer;
|
|
3096
|
+
constructor(e) {
|
|
3097
|
+
this.root = e, this.el = document.createElement("div"), this.el.className = "tcw-tooltip", this.el.setAttribute("aria-hidden", "true"), e.appendChild(this.el), e.addEventListener("pointerover", this.onOver), e.addEventListener("pointerout", this.onOut), e.addEventListener("pointerdown", this.onPress, !0), e.addEventListener("keydown", this.onPress, !0), e.addEventListener("focusin", this.onFocus), e.addEventListener("focusout", this.onBlur), e.addEventListener("wheel", this.hide, { passive: !0 }), this.observer = new MutationObserver(() => {
|
|
3098
|
+
this.pressed?.hasAttribute("title") && B(this.pressed);
|
|
3099
|
+
let e = this.target;
|
|
3100
|
+
!e || this.source !== "pointer" || !e.hasAttribute("title") || (B(e), this.isVisible() && this.show(e));
|
|
3101
|
+
}), this.observer.observe(e, {
|
|
3102
|
+
subtree: !0,
|
|
3103
|
+
attributes: !0,
|
|
3104
|
+
attributeFilter: ["title"]
|
|
3105
|
+
});
|
|
3106
|
+
}
|
|
3107
|
+
destroy() {
|
|
3108
|
+
this.hide(), this.pressed && V(this.pressed), this.observer.disconnect(), this.root.removeEventListener("pointerover", this.onOver), this.root.removeEventListener("pointerout", this.onOut), this.root.removeEventListener("pointerdown", this.onPress, !0), this.root.removeEventListener("keydown", this.onPress, !0), this.root.removeEventListener("focusin", this.onFocus), this.root.removeEventListener("focusout", this.onBlur), this.root.removeEventListener("wheel", this.hide), this.el.remove();
|
|
3109
|
+
}
|
|
3110
|
+
onOver = (e) => {
|
|
3111
|
+
if (e.pointerType === "touch") return;
|
|
3112
|
+
let t = this.tipTarget(e.target);
|
|
3113
|
+
!t || t === this.pressed || t === this.target && this.source === "pointer" || (B(t), this.schedule(t, "pointer", Date.now() < this.warmUntil || this.isVisible()));
|
|
3114
|
+
};
|
|
3115
|
+
onOut = (e) => {
|
|
3116
|
+
let t = e.relatedTarget, n = (e) => !!e && !!t && e.contains(t);
|
|
3117
|
+
this.pressed && !n(this.pressed) && (V(this.pressed), this.pressed = null), this.source === "pointer" && !n(this.target) && this.hide();
|
|
3118
|
+
};
|
|
3119
|
+
onPress = () => {
|
|
3120
|
+
let e = this.target, t = this.source === "pointer";
|
|
3121
|
+
this.warmUntil = 0, this.clearTimer(), cancelAnimationFrame(this.watchFrame), this.el.classList.remove("tcw-tooltip--visible"), this.target = null, this.source = null, this.pressed && this.pressed !== e && V(this.pressed), this.pressed = t ? e : null;
|
|
3122
|
+
};
|
|
3123
|
+
onFocus = (e) => {
|
|
3124
|
+
let t = this.tipTarget(e.target);
|
|
3125
|
+
!t || !dt(t) || t === this.pressed || this.schedule(t, "focus", !0);
|
|
3126
|
+
};
|
|
3127
|
+
onBlur = (e) => {
|
|
3128
|
+
if (this.source !== "focus") return;
|
|
3129
|
+
let t = e.relatedTarget;
|
|
3130
|
+
this.target && t && this.target.contains(t) || this.hide();
|
|
3131
|
+
};
|
|
3132
|
+
hide = () => {
|
|
3133
|
+
this.clearTimer(), cancelAnimationFrame(this.watchFrame), this.isVisible() && (this.warmUntil = Date.now() + 500), this.el.classList.remove("tcw-tooltip--visible"), this.target && this.source === "pointer" && V(this.target), this.target = null, this.source = null;
|
|
3134
|
+
};
|
|
3135
|
+
tipTarget(e) {
|
|
3136
|
+
if (!(e instanceof Element)) return null;
|
|
3137
|
+
let t = e.closest(ct);
|
|
3138
|
+
return t && t !== this.el && this.root.contains(t) ? t : null;
|
|
3139
|
+
}
|
|
3140
|
+
schedule(e, t, n) {
|
|
3141
|
+
let r = this.target;
|
|
3142
|
+
if (this.clearTimer(), cancelAnimationFrame(this.watchFrame), r && r !== e && this.source === "pointer" && V(r), this.target = e, this.source = t, n) {
|
|
3143
|
+
this.show(e);
|
|
3144
|
+
return;
|
|
3145
|
+
}
|
|
3146
|
+
this.el.classList.remove("tcw-tooltip--visible"), this.showTimer = setTimeout(() => {
|
|
3147
|
+
this.showTimer = null, this.target === e && this.show(e);
|
|
3148
|
+
}, 300);
|
|
3149
|
+
}
|
|
3150
|
+
show(e) {
|
|
3151
|
+
if (!H(e)) {
|
|
3152
|
+
this.hide();
|
|
3153
|
+
return;
|
|
3154
|
+
}
|
|
3155
|
+
this.el.textContent = ut(e), this.place(e), this.el.classList.add("tcw-tooltip--visible"), this.watch(e);
|
|
3156
|
+
}
|
|
3157
|
+
watch(e) {
|
|
3158
|
+
cancelAnimationFrame(this.watchFrame), this.watchFrame = requestAnimationFrame(() => {
|
|
3159
|
+
this.target === e && (H(e) ? this.watch(e) : this.hide());
|
|
3160
|
+
});
|
|
3161
|
+
}
|
|
3162
|
+
place(e) {
|
|
3163
|
+
let t = this.root.getBoundingClientRect(), n = e.getBoundingClientRect(), r = this.el.offsetWidth, i = this.el.offsetHeight, a = n.left - t.left, o = n.top - t.top, s = ft(e);
|
|
3164
|
+
s === "bottom" && o + n.height + L + i > t.height ? s = "top" : s === "top" && o - L - i < 0 ? s = "bottom" : s === "right" && a + n.width + L + r > t.width && (s = "left");
|
|
3165
|
+
let c, l;
|
|
3166
|
+
s === "top" || s === "bottom" ? (c = a + n.width / 2 - r / 2, l = s === "bottom" ? o + n.height + L : o - L - i) : (c = s === "right" ? a + n.width + L : a - L - r, l = o + n.height / 2 - i / 2), c = Math.max(R, Math.min(t.width - r - R, c)), l = Math.max(R, Math.min(t.height - i - R, l)), this.el.style.left = `${Math.round(c)}px`, this.el.style.top = `${Math.round(l)}px`;
|
|
3167
|
+
let u = {
|
|
3168
|
+
top: [0, z],
|
|
3169
|
+
bottom: [0, -z],
|
|
3170
|
+
left: [z, 0],
|
|
3171
|
+
right: [-z, 0]
|
|
3172
|
+
}[s];
|
|
3173
|
+
this.el.style.setProperty("--tcw-tip-dx", `${u[0]}px`), this.el.style.setProperty("--tcw-tip-dy", `${u[1]}px`), this.el.dataset.side = s;
|
|
3174
|
+
}
|
|
3175
|
+
isVisible() {
|
|
3176
|
+
return this.el.classList.contains("tcw-tooltip--visible");
|
|
3177
|
+
}
|
|
3178
|
+
clearTimer() {
|
|
3179
|
+
this.showTimer !== null && (clearTimeout(this.showTimer), this.showTimer = null);
|
|
3180
|
+
}
|
|
3181
|
+
};
|
|
3182
|
+
function B(e) {
|
|
3183
|
+
let t = e.getAttribute("title");
|
|
3184
|
+
t !== null && (e.dataset.tip = t, e.removeAttribute("title"));
|
|
3185
|
+
}
|
|
3186
|
+
function V(e) {
|
|
3187
|
+
e.dataset.tip !== void 0 && !e.hasAttribute("title") && (e.setAttribute("title", e.dataset.tip), delete e.dataset.tip);
|
|
3188
|
+
}
|
|
3189
|
+
function ut(e) {
|
|
3190
|
+
return e.dataset.tip ?? e.getAttribute("title") ?? "";
|
|
3191
|
+
}
|
|
3192
|
+
function H(e) {
|
|
3193
|
+
return e.isConnected && e.getClientRects().length > 0 && !e.disabled;
|
|
3194
|
+
}
|
|
3195
|
+
function dt(e) {
|
|
3196
|
+
try {
|
|
3197
|
+
return e.matches(":focus-visible");
|
|
3198
|
+
} catch {
|
|
3199
|
+
return !1;
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
function ft(e) {
|
|
3203
|
+
return e.dataset.tipSide || (e.closest(".tcw-sidebar") ? "right" : e.closest(".tcw-statusbar, .tcw-replay-bar") ? "top" : "bottom");
|
|
3204
|
+
}
|
|
3205
|
+
//#endregion
|
|
3206
|
+
//#region src/widget/WidgetIndicatorLegend.ts
|
|
3207
|
+
var pt = 2, mt = class {
|
|
3208
|
+
stack;
|
|
3209
|
+
toggle;
|
|
3210
|
+
rows = /* @__PURE__ */ new Map();
|
|
3211
|
+
collapsed = !1;
|
|
3212
|
+
toggleKey = "";
|
|
3213
|
+
stackKey = "";
|
|
3214
|
+
constructor(e, t, n) {
|
|
3215
|
+
this.host = e, this.callbacks = t, this.labels = n, this.stack = document.createElement("div"), this.stack.className = "tcw-ind-legend", this.toggle = document.createElement("button"), this.toggle.type = "button", this.toggle.className = "tcw-ind-legend-toggle", this.toggle.addEventListener("click", () => {
|
|
3216
|
+
this.setCollapsed(!this.collapsed), this.renderToggle(this.overlayCount());
|
|
3217
|
+
}), this.stack.appendChild(this.toggle), e.appendChild(this.stack);
|
|
3218
|
+
}
|
|
3219
|
+
update(e, t) {
|
|
3220
|
+
let n = `${Math.round(t.left)},${Math.round(t.top)}`;
|
|
3221
|
+
n !== this.stackKey && (this.stackKey = n, this.stack.style.left = `${Math.round(t.left)}px`, this.stack.style.top = `${Math.round(t.top)}px`);
|
|
3222
|
+
let r = /* @__PURE__ */ new Set(), i = !1;
|
|
3223
|
+
for (let t of e) {
|
|
3224
|
+
r.add(t.instanceId);
|
|
3225
|
+
let e = this.rows.get(t.instanceId) ?? this.createRow(t.instanceId);
|
|
3226
|
+
this.fill(e, t), t.pane ? e.el.parentElement !== this.host && this.host.appendChild(e.el) : e.el.parentElement !== this.stack && (this.stack.insertBefore(e.el, this.toggle), i = !0), this.place(e, t.pane);
|
|
3227
|
+
}
|
|
3228
|
+
for (let [e, t] of this.rows) r.has(e) || (t.el.remove(), this.rows.delete(e));
|
|
3229
|
+
i && this.collapsed && this.setCollapsed(!1), this.renderToggle(e.filter((e) => !e.pane).length);
|
|
3230
|
+
}
|
|
3231
|
+
destroy() {
|
|
3232
|
+
for (let e of this.rows.values()) e.el.remove();
|
|
3233
|
+
this.rows.clear(), this.stack.remove();
|
|
3234
|
+
}
|
|
3235
|
+
createRow(e) {
|
|
3236
|
+
let t = document.createElement("div");
|
|
3237
|
+
t.className = "tcw-ind-legend-row", t.dataset.instance = e;
|
|
3238
|
+
let n = document.createElement("button");
|
|
3239
|
+
n.type = "button", n.className = "tcw-ind-legend-name", n.addEventListener("click", () => this.callbacks.onSettings(e));
|
|
3240
|
+
let r = document.createElement("span");
|
|
3241
|
+
r.className = "tcw-ind-legend-values";
|
|
3242
|
+
let i = document.createElement("span");
|
|
3243
|
+
i.className = "tcw-ind-legend-actions";
|
|
3244
|
+
let a = (e, t, n) => {
|
|
3245
|
+
let r = document.createElement("button");
|
|
3246
|
+
return r.type = "button", r.className = "tcw-ind-legend-act", r.dataset.act = e, r.innerHTML = C(t, 13), r.addEventListener("click", n), i.appendChild(r), r;
|
|
3247
|
+
}, o = a("visible", "eye", () => {
|
|
3248
|
+
this.callbacks.onToggleVisible(e, !l.visible);
|
|
3249
|
+
}), s = a("settings", "settings", () => this.callbacks.onSettings(e)), c = a("remove", "x", () => this.callbacks.onRemove(e));
|
|
3250
|
+
t.append(n, r, i);
|
|
3251
|
+
let l = {
|
|
3252
|
+
el: t,
|
|
3253
|
+
name: n,
|
|
3254
|
+
values: r,
|
|
3255
|
+
eye: o,
|
|
3256
|
+
settings: s,
|
|
3257
|
+
remove: c,
|
|
3258
|
+
visible: !0,
|
|
3259
|
+
valuesKey: "",
|
|
3260
|
+
state: "",
|
|
3261
|
+
placeKey: ""
|
|
3262
|
+
};
|
|
3263
|
+
return this.rows.set(e, l), l;
|
|
3264
|
+
}
|
|
3265
|
+
fill(e, t) {
|
|
3266
|
+
let n = `${t.label}|${t.visible}`;
|
|
3267
|
+
n !== e.state && (e.state = n, e.visible = t.visible, e.name.textContent = t.label, e.name.title = this.labels.settings, e.el.classList.toggle("tcw-ind-legend-row--hidden", !t.visible), e.eye.innerHTML = C(t.visible ? "eye" : "eyeOff", 13), U(e.eye, t.visible ? this.labels.hide : this.labels.show, t.label), U(e.settings, this.labels.settings, t.label), U(e.remove, this.labels.remove, t.label));
|
|
3268
|
+
let r = t.values.map((e) => `${e.text}${e.color ?? ""}`).join("");
|
|
3269
|
+
r !== e.valuesKey && (e.valuesKey = r, e.values.replaceChildren(...t.values.map((e) => {
|
|
3270
|
+
let t = document.createElement("span");
|
|
3271
|
+
return t.textContent = e.text, e.color && (t.style.color = e.color), t;
|
|
3272
|
+
})));
|
|
3273
|
+
}
|
|
3274
|
+
place(e, t) {
|
|
3275
|
+
let n = t ? `${Math.round(t.x)},${Math.round(t.y)},${Math.round(t.width)}` : "";
|
|
3276
|
+
n !== e.placeKey && (e.placeKey = n, e.el.classList.toggle("tcw-ind-legend-row--pane", t !== null), e.el.style.left = t ? `${Math.round(t.x)}px` : "", e.el.style.top = t ? `${Math.round(t.y)}px` : "", e.el.style.maxWidth = t ? `${Math.max(0, Math.round(t.width))}px` : "");
|
|
3277
|
+
}
|
|
3278
|
+
setCollapsed(e) {
|
|
3279
|
+
this.collapsed = e, this.stack.classList.toggle("tcw-ind-legend--collapsed", e);
|
|
3280
|
+
}
|
|
3281
|
+
overlayCount() {
|
|
3282
|
+
return this.stack.querySelectorAll(":scope > .tcw-ind-legend-row").length;
|
|
3283
|
+
}
|
|
3284
|
+
renderToggle(e) {
|
|
3285
|
+
let t = e >= pt;
|
|
3286
|
+
!t && this.collapsed && this.setCollapsed(!1);
|
|
3287
|
+
let n = `${t}|${this.collapsed}|${e}`;
|
|
3288
|
+
if (n === this.toggleKey || (this.toggleKey = n, this.toggle.hidden = !t, !t)) return;
|
|
3289
|
+
let r = this.collapsed ? this.labels.expand : this.labels.collapse;
|
|
3290
|
+
U(this.toggle, r, this.collapsed ? `(${e})` : void 0), this.toggle.setAttribute("aria-expanded", String(!this.collapsed));
|
|
3291
|
+
let i = C(this.collapsed ? "chevronDown" : "chevronUp", 12);
|
|
3292
|
+
this.toggle.innerHTML = this.collapsed ? `${i}<span>${e}</span>` : i;
|
|
3293
|
+
}
|
|
3294
|
+
};
|
|
3295
|
+
function U(e, t, n) {
|
|
3296
|
+
e.title = t, e.setAttribute("aria-label", n ? `${t} ${n}` : t);
|
|
3297
|
+
}
|
|
3298
|
+
//#endregion
|
|
3299
|
+
//#region src/widget/legendValues.ts
|
|
3300
|
+
var ht = {
|
|
3301
|
+
psar: ["trend"],
|
|
3302
|
+
supertrend: ["trend"],
|
|
3303
|
+
svwap: ["session"],
|
|
3304
|
+
ao: ["up"],
|
|
3305
|
+
voldelta: ["up"],
|
|
3306
|
+
chaikinOsc: ["adl"],
|
|
3307
|
+
adx: ["dx"],
|
|
3308
|
+
lrc: ["slope"],
|
|
3309
|
+
ichimoku: ["chikou"]
|
|
3310
|
+
}, gt = {
|
|
3311
|
+
psar: (e) => e.trend === 1,
|
|
3312
|
+
supertrend: (e) => e.trend === 1,
|
|
3313
|
+
ao: (e) => e.up === 1,
|
|
3314
|
+
voldelta: (e) => e.up === 1,
|
|
3315
|
+
chaikinOsc: (e) => (e.value ?? 0) >= 0,
|
|
3316
|
+
cmf: (e) => (e.value ?? 0) >= 0,
|
|
3317
|
+
dpo: (e) => (e.value ?? 0) >= 0
|
|
3318
|
+
}, _t = new Set(["ac"]);
|
|
3319
|
+
function W(e, t) {
|
|
3320
|
+
if (!t) return [];
|
|
3321
|
+
let n = ht[e], r = [];
|
|
3322
|
+
for (let e in t) {
|
|
3323
|
+
let i = t[e];
|
|
3324
|
+
typeof i == "number" && Number.isFinite(i) && !n?.includes(e) && r.push(i);
|
|
3325
|
+
}
|
|
3326
|
+
return r;
|
|
3327
|
+
}
|
|
3328
|
+
function vt(e, t, n) {
|
|
3329
|
+
if (_t.has(e)) return null;
|
|
3330
|
+
let r = gt[e];
|
|
3331
|
+
return r ? n ? (r(n) ? t[0] : t[1]) ?? null : null : t[0] ?? null;
|
|
3332
|
+
}
|
|
3333
|
+
var G = /* @__PURE__ */ new Map();
|
|
3334
|
+
function yt(e) {
|
|
3335
|
+
let t = G.get(e);
|
|
3336
|
+
return t || (t = new Intl.NumberFormat(e, {
|
|
3337
|
+
notation: "compact",
|
|
3338
|
+
maximumFractionDigits: 2
|
|
3339
|
+
}), G.set(e, t)), t;
|
|
3340
|
+
}
|
|
3341
|
+
function bt(e, t = "en-US") {
|
|
3342
|
+
let n = Math.abs(e);
|
|
3343
|
+
return n >= 1e6 ? yt(t).format(e) : i(e, n >= 1e3 ? 1 : n >= 1 ? 2 : n >= .01 ? 4 : 6, t);
|
|
3344
|
+
}
|
|
3345
|
+
//#endregion
|
|
3346
|
+
//#region src/widget/WidgetBracketBar.ts
|
|
3347
|
+
var xt = class {
|
|
2637
3348
|
el;
|
|
2638
3349
|
labelEl;
|
|
2639
3350
|
callbacks;
|
|
@@ -2643,9 +3354,9 @@ var Ee = class {
|
|
|
2643
3354
|
let n = document.createElement("span");
|
|
2644
3355
|
n.className = "tcw-bracket-hint", n.textContent = "drag lines to adjust", this.el.appendChild(n);
|
|
2645
3356
|
let r = document.createElement("button");
|
|
2646
|
-
r.type = "button", r.className = "tcw-bracket-cancel", r.innerHTML = `${
|
|
3357
|
+
r.type = "button", r.className = "tcw-bracket-cancel", r.innerHTML = `${C("x", 14)}<span>Cancel</span>`, r.addEventListener("click", () => this.callbacks.onCancel());
|
|
2647
3358
|
let i = document.createElement("button");
|
|
2648
|
-
i.type = "button", i.className = "tcw-bracket-confirm", i.innerHTML = `${
|
|
3359
|
+
i.type = "button", i.className = "tcw-bracket-confirm", i.innerHTML = `${C("check", 14)}<span>Place</span>`, i.addEventListener("click", () => this.callbacks.onConfirm()), this.el.appendChild(r), this.el.appendChild(i), e.appendChild(this.el);
|
|
2649
3360
|
}
|
|
2650
3361
|
show(e) {
|
|
2651
3362
|
this.visible = !0, this.labelEl.textContent = e === "buy" ? "Long bracket" : "Short bracket", this.labelEl.dataset.side = e, this.el.hidden = !1;
|
|
@@ -2659,7 +3370,7 @@ var Ee = class {
|
|
|
2659
3370
|
destroy() {
|
|
2660
3371
|
this.el.remove();
|
|
2661
3372
|
}
|
|
2662
|
-
},
|
|
3373
|
+
}, St = class {
|
|
2663
3374
|
options;
|
|
2664
3375
|
audioCtx = null;
|
|
2665
3376
|
audioEl = null;
|
|
@@ -2722,7 +3433,7 @@ var Ee = class {
|
|
|
2722
3433
|
};
|
|
2723
3434
|
//#endregion
|
|
2724
3435
|
//#region src/widget/ladderRows.ts
|
|
2725
|
-
function
|
|
3436
|
+
function Ct(e, t = 12) {
|
|
2726
3437
|
let n = [...e.asks].sort((e, t) => e.price - t.price).slice(0, t), r = [...e.bids].sort((e, t) => t.price - e.price).slice(0, t), i = n.length > 0 ? n[0].price : null, a = r.length > 0 ? r[0].price : null, o = i !== null && a !== null ? (i + a) / 2 : null, s = /* @__PURE__ */ new Map(), c = (e) => {
|
|
2727
3438
|
let t = s.get(e);
|
|
2728
3439
|
return t || (t = {
|
|
@@ -2743,7 +3454,7 @@ function ke(e, t = 12) {
|
|
|
2743
3454
|
}
|
|
2744
3455
|
//#endregion
|
|
2745
3456
|
//#region src/widget/WidgetDepthLadder.ts
|
|
2746
|
-
var
|
|
3457
|
+
var wt = class {
|
|
2747
3458
|
el;
|
|
2748
3459
|
bodyEl;
|
|
2749
3460
|
callbacks;
|
|
@@ -2756,7 +3467,7 @@ var Ae = class {
|
|
|
2756
3467
|
let i = document.createElement("span");
|
|
2757
3468
|
i.textContent = "Depth Ladder";
|
|
2758
3469
|
let a = document.createElement("button");
|
|
2759
|
-
a.type = "button", a.className = "tcw-ladder-close", a.setAttribute("aria-label", "Close"), a.innerHTML =
|
|
3470
|
+
a.type = "button", a.className = "tcw-ladder-close", a.setAttribute("aria-label", "Close"), a.innerHTML = C("x", 14), a.addEventListener("click", () => this.close()), r.appendChild(i), r.appendChild(a), this.el.appendChild(r);
|
|
2760
3471
|
let o = document.createElement("div");
|
|
2761
3472
|
o.className = "tcw-ladder-cols", o.innerHTML = "<span>Bid</span><span>Price</span><span>Ask</span>", this.el.appendChild(o), this.bodyEl = document.createElement("div"), this.bodyEl.className = "tcw-ladder-body", this.el.appendChild(this.bodyEl), e.appendChild(this.el);
|
|
2762
3473
|
}
|
|
@@ -2777,7 +3488,7 @@ var Ae = class {
|
|
|
2777
3488
|
this.renderEmpty();
|
|
2778
3489
|
return;
|
|
2779
3490
|
}
|
|
2780
|
-
this.render(
|
|
3491
|
+
this.render(Ct(e, this.levels));
|
|
2781
3492
|
}
|
|
2782
3493
|
destroy() {
|
|
2783
3494
|
this.el.remove();
|
|
@@ -2797,20 +3508,20 @@ var Ae = class {
|
|
|
2797
3508
|
let e = n.askVolume > 0, r = document.createElement("div");
|
|
2798
3509
|
r.className = "tcw-ladder-row";
|
|
2799
3510
|
let i = document.createElement("button");
|
|
2800
|
-
i.type = "button", i.className = "tcw-ladder-cell tcw-ladder-bid", n.bidVolume > 0 ? (i.textContent =
|
|
3511
|
+
i.type = "button", i.className = "tcw-ladder-cell tcw-ladder-bid", n.bidVolume > 0 ? (i.textContent = K(n.bidVolume), i.style.setProperty("--w", `${(n.bidVolume * t * 100).toFixed(1)}%`), i.title = `Sell @ ${this.callbacks.formatPrice(n.price)}`, i.addEventListener("click", () => this.callbacks.onTrade("sell", n.price))) : i.disabled = !0;
|
|
2801
3512
|
let a = document.createElement("div");
|
|
2802
3513
|
a.className = `tcw-ladder-price ${e ? "tcw-ask" : "tcw-bid"}`, a.textContent = this.callbacks.formatPrice(n.price);
|
|
2803
3514
|
let o = document.createElement("button");
|
|
2804
|
-
o.type = "button", o.className = "tcw-ladder-cell tcw-ladder-ask", n.askVolume > 0 ? (o.textContent =
|
|
3515
|
+
o.type = "button", o.className = "tcw-ladder-cell tcw-ladder-ask", n.askVolume > 0 ? (o.textContent = K(n.askVolume), o.style.setProperty("--w", `${(n.askVolume * t * 100).toFixed(1)}%`), o.title = `Buy @ ${this.callbacks.formatPrice(n.price)}`, o.addEventListener("click", () => this.callbacks.onTrade("buy", n.price))) : o.disabled = !0, r.appendChild(i), r.appendChild(a), r.appendChild(o), this.bodyEl.appendChild(r);
|
|
2805
3516
|
}
|
|
2806
3517
|
}
|
|
2807
3518
|
};
|
|
2808
|
-
function
|
|
3519
|
+
function K(e) {
|
|
2809
3520
|
return e >= 1e3 ? (e / 1e3).toFixed(1) + "k" : e >= 1 ? e.toFixed(2).replace(/\.00$/, "") : e.toPrecision(2);
|
|
2810
3521
|
}
|
|
2811
3522
|
//#endregion
|
|
2812
3523
|
//#region src/widget/WidgetDataWindow.ts
|
|
2813
|
-
var
|
|
3524
|
+
var Tt = class {
|
|
2814
3525
|
el;
|
|
2815
3526
|
bodyEl;
|
|
2816
3527
|
callbacks;
|
|
@@ -2822,7 +3533,7 @@ var je = class {
|
|
|
2822
3533
|
let r = document.createElement("span");
|
|
2823
3534
|
r.textContent = "Data Window";
|
|
2824
3535
|
let i = document.createElement("button");
|
|
2825
|
-
i.type = "button", i.className = "tcw-datawin-close", i.setAttribute("aria-label", "Close"), i.innerHTML =
|
|
3536
|
+
i.type = "button", i.className = "tcw-datawin-close", i.setAttribute("aria-label", "Close"), i.innerHTML = C("x", 14), i.addEventListener("click", () => this.close()), n.appendChild(r), n.appendChild(i), this.el.appendChild(n), this.bodyEl = document.createElement("div"), this.bodyEl.className = "tcw-datawin-body", this.el.appendChild(this.bodyEl), e.appendChild(this.el);
|
|
2826
3537
|
}
|
|
2827
3538
|
isOpen() {
|
|
2828
3539
|
return this.open;
|
|
@@ -2849,11 +3560,11 @@ var je = class {
|
|
|
2849
3560
|
let t = this.callbacks.formatPrice, n = e.ohlc;
|
|
2850
3561
|
this.bodyEl.appendChild(this.row("Open", t(n.open))), this.bodyEl.appendChild(this.row("High", t(n.high))), this.bodyEl.appendChild(this.row("Low", t(n.low))), this.bodyEl.appendChild(this.row("Close", t(n.close)));
|
|
2851
3562
|
let r = e.change > 0 ? "up" : e.change < 0 ? "down" : "", i = e.change > 0 ? "+" : "";
|
|
2852
|
-
this.bodyEl.appendChild(this.row("Change", `${i}${t(e.change)} (${i}${e.changePct.toFixed(2)}%)`, r)), this.bodyEl.appendChild(this.row("Volume",
|
|
3563
|
+
this.bodyEl.appendChild(this.row("Change", `${i}${t(e.change)} (${i}${e.changePct.toFixed(2)}%)`, r)), this.bodyEl.appendChild(this.row("Volume", Dt(n.volume)));
|
|
2853
3564
|
for (let t of e.indicators) {
|
|
2854
3565
|
let e = document.createElement("div");
|
|
2855
3566
|
e.className = "tcw-datawin-sub", e.textContent = t.name, this.bodyEl.appendChild(e);
|
|
2856
|
-
for (let e of t.values) this.bodyEl.appendChild(this.row(e.key,
|
|
3567
|
+
for (let e of t.values) this.bodyEl.appendChild(this.row(e.key, Et(e.value)));
|
|
2857
3568
|
}
|
|
2858
3569
|
}
|
|
2859
3570
|
row(e, t, n = "") {
|
|
@@ -2865,43 +3576,43 @@ var je = class {
|
|
|
2865
3576
|
return a.className = `tcw-datawin-value${n ? ` tcw-${n}` : ""}`, a.textContent = t, r.appendChild(i), r.appendChild(a), r;
|
|
2866
3577
|
}
|
|
2867
3578
|
};
|
|
2868
|
-
function
|
|
3579
|
+
function Et(e) {
|
|
2869
3580
|
if (!Number.isFinite(e)) return "—";
|
|
2870
3581
|
let t = Math.abs(e);
|
|
2871
3582
|
return t >= 1e3 ? e.toFixed(2) : t >= 1 ? e.toFixed(4) : e.toPrecision(4);
|
|
2872
3583
|
}
|
|
2873
|
-
function
|
|
3584
|
+
function Dt(e) {
|
|
2874
3585
|
return e >= 1e9 ? (e / 1e9).toFixed(2) + "B" : e >= 1e6 ? (e / 1e6).toFixed(2) + "M" : e >= 1e3 ? (e / 1e3).toFixed(2) + "K" : e.toFixed(0);
|
|
2875
3586
|
}
|
|
2876
3587
|
//#endregion
|
|
2877
3588
|
//#region src/widget/widgetShareState.ts
|
|
2878
|
-
var
|
|
2879
|
-
function
|
|
2880
|
-
if (
|
|
3589
|
+
var q = globalThis.Buffer;
|
|
3590
|
+
function Ot(e) {
|
|
3591
|
+
if (q) return q.from(e, "utf8").toString("base64");
|
|
2881
3592
|
let t = new TextEncoder().encode(e), n = "";
|
|
2882
3593
|
for (let e of t) n += String.fromCharCode(e);
|
|
2883
3594
|
return btoa(n);
|
|
2884
3595
|
}
|
|
2885
|
-
function
|
|
2886
|
-
if (
|
|
3596
|
+
function kt(e) {
|
|
3597
|
+
if (q) return q.from(e, "base64").toString("utf8");
|
|
2887
3598
|
let t = atob(e), n = new Uint8Array(t.length);
|
|
2888
3599
|
for (let e = 0; e < t.length; e++) n[e] = t.charCodeAt(e);
|
|
2889
3600
|
return new TextDecoder().decode(n);
|
|
2890
3601
|
}
|
|
2891
|
-
function
|
|
3602
|
+
function At(e) {
|
|
2892
3603
|
return e.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
2893
3604
|
}
|
|
2894
|
-
function
|
|
3605
|
+
function jt(e) {
|
|
2895
3606
|
let t = e.replace(/-/g, "+").replace(/_/g, "/");
|
|
2896
3607
|
return t + (t.length % 4 == 0 ? "" : "=".repeat(4 - t.length % 4));
|
|
2897
3608
|
}
|
|
2898
|
-
function
|
|
2899
|
-
return
|
|
3609
|
+
function Mt(e) {
|
|
3610
|
+
return At(Ot(JSON.stringify(e)));
|
|
2900
3611
|
}
|
|
2901
|
-
function
|
|
3612
|
+
function Nt(e) {
|
|
2902
3613
|
if (!e) return null;
|
|
2903
3614
|
try {
|
|
2904
|
-
let t =
|
|
3615
|
+
let t = kt(jt(e)), n = JSON.parse(t);
|
|
2905
3616
|
return !n || n.v !== 1 || typeof n.symbol != "string" ? null : {
|
|
2906
3617
|
v: 1,
|
|
2907
3618
|
symbol: n.symbol,
|
|
@@ -2915,28 +3626,36 @@ function ze(e) {
|
|
|
2915
3626
|
return null;
|
|
2916
3627
|
}
|
|
2917
3628
|
}
|
|
2918
|
-
var
|
|
2919
|
-
function
|
|
3629
|
+
var J = "tcw";
|
|
3630
|
+
function Pt(e) {
|
|
2920
3631
|
let t = e.startsWith("#") ? e.slice(1) : e;
|
|
2921
3632
|
for (let e of t.split("&")) {
|
|
2922
3633
|
let t = e.indexOf("=");
|
|
2923
|
-
if (t > 0 && e.slice(0, t) ===
|
|
3634
|
+
if (t > 0 && e.slice(0, t) === J) return decodeURIComponent(e.slice(t + 1));
|
|
2924
3635
|
}
|
|
2925
3636
|
return null;
|
|
2926
3637
|
}
|
|
2927
|
-
function
|
|
3638
|
+
function Ft(e, t) {
|
|
2928
3639
|
let [n] = e.split("#");
|
|
2929
|
-
return `${n}#${
|
|
3640
|
+
return `${n}#${J}=${t}`;
|
|
2930
3641
|
}
|
|
2931
3642
|
//#endregion
|
|
2932
3643
|
//#region src/widget/i18n.ts
|
|
2933
|
-
var
|
|
3644
|
+
var Y = {
|
|
2934
3645
|
"toolbar.indicators": "Indicators",
|
|
2935
3646
|
"toolbar.indicators.popular": "Popular",
|
|
2936
3647
|
"toolbar.indicators.all": "All",
|
|
2937
3648
|
"indicatorType.overlay": "overlay",
|
|
2938
3649
|
"indicatorType.panel": "panel",
|
|
2939
3650
|
"toolbar.replay": "Bar replay",
|
|
3651
|
+
"replay.selectHint": "Click a bar to start the replay",
|
|
3652
|
+
"replay.random": "Random bar",
|
|
3653
|
+
"replay.realtime": "Back to realtime",
|
|
3654
|
+
"replay.barsPerSecond": "bars/s",
|
|
3655
|
+
"replay.play": "Play",
|
|
3656
|
+
"replay.pause": "Pause",
|
|
3657
|
+
"replay.stepBack": "Step back (Shift+←)",
|
|
3658
|
+
"replay.stepForward": "Step forward (Shift+→)",
|
|
2940
3659
|
"toolbar.longBracket": "Long bracket",
|
|
2941
3660
|
"toolbar.shortBracket": "Short bracket",
|
|
2942
3661
|
"toolbar.depthLadder": "Depth ladder",
|
|
@@ -2945,6 +3664,24 @@ var Z = {
|
|
|
2945
3664
|
"toolbar.screenshot": "Screenshot",
|
|
2946
3665
|
"toolbar.settings": "Chart Settings",
|
|
2947
3666
|
"toolbar.toggleTheme": "Toggle theme",
|
|
3667
|
+
"toolbar.timeframes": "Timeframes",
|
|
3668
|
+
"toolbar.timeframes.pin": "Pin to toolbar",
|
|
3669
|
+
"toolbar.fullscreen": "Fullscreen",
|
|
3670
|
+
"legend.show": "Show",
|
|
3671
|
+
"legend.hide": "Hide",
|
|
3672
|
+
"legend.settings": "Settings",
|
|
3673
|
+
"legend.remove": "Remove",
|
|
3674
|
+
"legend.collapse": "Collapse indicators",
|
|
3675
|
+
"legend.expand": "Show indicators",
|
|
3676
|
+
"toolbar.exitFullscreen": "Exit fullscreen",
|
|
3677
|
+
"range.presets": "Visible range",
|
|
3678
|
+
"range.all": "All",
|
|
3679
|
+
"range.goTo": "Go to date",
|
|
3680
|
+
"range.goToSubmit": "Go to",
|
|
3681
|
+
"range.date": "Date",
|
|
3682
|
+
"range.time": "Time",
|
|
3683
|
+
"range.cancel": "Cancel",
|
|
3684
|
+
"range.beforeData": "Showing the earliest loaded bar",
|
|
2948
3685
|
"chartType.candlestick": "Candlestick",
|
|
2949
3686
|
"chartType.line": "Line",
|
|
2950
3687
|
"chartType.area": "Area",
|
|
@@ -2978,8 +3715,8 @@ var Z = {
|
|
|
2978
3715
|
"hotkeys.group.touch": "Touch (mobile / tablet)",
|
|
2979
3716
|
"hotkeys.group.keyboard": "Keyboard",
|
|
2980
3717
|
"hotkeys.group.drawing": "Drawing"
|
|
2981
|
-
},
|
|
2982
|
-
en:
|
|
3718
|
+
}, It = {
|
|
3719
|
+
en: Y,
|
|
2983
3720
|
vi: {
|
|
2984
3721
|
"toolbar.indicators": "Chỉ báo",
|
|
2985
3722
|
"toolbar.indicators.popular": "Phổ biến",
|
|
@@ -2987,6 +3724,14 @@ var Z = {
|
|
|
2987
3724
|
"indicatorType.overlay": "phủ lên",
|
|
2988
3725
|
"indicatorType.panel": "bảng riêng",
|
|
2989
3726
|
"toolbar.replay": "Phát lại",
|
|
3727
|
+
"replay.selectHint": "Bấm vào một nến để bắt đầu phát lại",
|
|
3728
|
+
"replay.random": "Nến ngẫu nhiên",
|
|
3729
|
+
"replay.realtime": "Về thời gian thực",
|
|
3730
|
+
"replay.barsPerSecond": "nến/giây",
|
|
3731
|
+
"replay.play": "Phát",
|
|
3732
|
+
"replay.pause": "Tạm dừng",
|
|
3733
|
+
"replay.stepBack": "Lùi một nến (Shift+←)",
|
|
3734
|
+
"replay.stepForward": "Tiến một nến (Shift+→)",
|
|
2990
3735
|
"toolbar.longBracket": "Lệnh Long",
|
|
2991
3736
|
"toolbar.shortBracket": "Lệnh Short",
|
|
2992
3737
|
"toolbar.depthLadder": "Sổ lệnh",
|
|
@@ -2995,6 +3740,24 @@ var Z = {
|
|
|
2995
3740
|
"toolbar.screenshot": "Chụp màn hình",
|
|
2996
3741
|
"toolbar.settings": "Cài đặt biểu đồ",
|
|
2997
3742
|
"toolbar.toggleTheme": "Đổi giao diện",
|
|
3743
|
+
"toolbar.timeframes": "Khung thời gian",
|
|
3744
|
+
"toolbar.timeframes.pin": "Ghim lên thanh công cụ",
|
|
3745
|
+
"toolbar.fullscreen": "Toàn màn hình",
|
|
3746
|
+
"legend.show": "Hiện",
|
|
3747
|
+
"legend.hide": "Ẩn",
|
|
3748
|
+
"legend.settings": "Cài đặt",
|
|
3749
|
+
"legend.remove": "Xoá",
|
|
3750
|
+
"legend.collapse": "Thu gọn chỉ báo",
|
|
3751
|
+
"legend.expand": "Hiện chỉ báo",
|
|
3752
|
+
"toolbar.exitFullscreen": "Thoát toàn màn hình",
|
|
3753
|
+
"range.presets": "Khoảng hiển thị",
|
|
3754
|
+
"range.all": "Tất cả",
|
|
3755
|
+
"range.goTo": "Đi tới ngày",
|
|
3756
|
+
"range.goToSubmit": "Đi tới",
|
|
3757
|
+
"range.date": "Ngày",
|
|
3758
|
+
"range.time": "Giờ",
|
|
3759
|
+
"range.cancel": "Huỷ",
|
|
3760
|
+
"range.beforeData": "Đang hiện nến sớm nhất đã tải",
|
|
2998
3761
|
"chartType.candlestick": "Nến",
|
|
2999
3762
|
"chartType.line": "Đường",
|
|
3000
3763
|
"chartType.area": "Vùng",
|
|
@@ -3030,14 +3793,14 @@ var Z = {
|
|
|
3030
3793
|
"hotkeys.group.drawing": "Vẽ"
|
|
3031
3794
|
}
|
|
3032
3795
|
};
|
|
3033
|
-
function
|
|
3034
|
-
let n = { ...
|
|
3796
|
+
function Lt(e, t) {
|
|
3797
|
+
let n = { ...Y }, r = e ? It[e] : void 0;
|
|
3035
3798
|
return r && Object.assign(n, r), t && Object.assign(n, t), n;
|
|
3036
3799
|
}
|
|
3037
|
-
function
|
|
3038
|
-
return (t) => e[t] ??
|
|
3800
|
+
function Rt(e) {
|
|
3801
|
+
return (t) => e[t] ?? Y[t] ?? t;
|
|
3039
3802
|
}
|
|
3040
|
-
var
|
|
3803
|
+
var zt = [
|
|
3041
3804
|
[
|
|
3042
3805
|
34,
|
|
3043
3806
|
16,
|
|
@@ -3094,7 +3857,7 @@ var We = [
|
|
|
3094
3857
|
30,
|
|
3095
3858
|
!0
|
|
3096
3859
|
]
|
|
3097
|
-
],
|
|
3860
|
+
], Bt = class {
|
|
3098
3861
|
el;
|
|
3099
3862
|
label;
|
|
3100
3863
|
showTimer = null;
|
|
@@ -3104,9 +3867,9 @@ var We = [
|
|
|
3104
3867
|
let n = document.createElement("div");
|
|
3105
3868
|
n.className = "tcw-loading-card";
|
|
3106
3869
|
let r = document.createElement("div");
|
|
3107
|
-
r.className = "tcw-loading-candles", r.setAttribute("aria-hidden", "true"),
|
|
3870
|
+
r.className = "tcw-loading-candles", r.setAttribute("aria-hidden", "true"), zt.forEach(([e, t, n, i, a], o) => {
|
|
3108
3871
|
let s = document.createElement("span");
|
|
3109
|
-
s.className = a ? "tcw-loading-candle tcw-loading-candle--up" : "tcw-loading-candle", s.style.setProperty("--tcw-body-top", `${e}px`), s.style.setProperty("--tcw-body-h", `${t}px`), s.style.setProperty("--tcw-wick-top", `${n}px`), s.style.setProperty("--tcw-wick-h", `${i}px`), s.style.
|
|
3872
|
+
s.className = a ? "tcw-loading-candle tcw-loading-candle--up" : "tcw-loading-candle", s.style.setProperty("--tcw-body-top", `${e}px`), s.style.setProperty("--tcw-body-h", `${t}px`), s.style.setProperty("--tcw-wick-top", `${n}px`), s.style.setProperty("--tcw-wick-h", `${i}px`), s.style.setProperty("--tcw-delay", `${o * 110}ms`), r.appendChild(s);
|
|
3110
3873
|
}), n.appendChild(r);
|
|
3111
3874
|
let i = document.createElement("div");
|
|
3112
3875
|
i.className = "tcw-loading-track", i.setAttribute("aria-hidden", "true"), i.appendChild(document.createElement("span")), n.appendChild(i), this.label = document.createElement("div"), this.label.className = "tcw-loading-label", this.label.textContent = t, n.appendChild(this.label), this.el.appendChild(n), e.appendChild(this.el);
|
|
@@ -3141,33 +3904,51 @@ var We = [
|
|
|
3141
3904
|
clearTimer() {
|
|
3142
3905
|
this.showTimer !== null && (clearTimeout(this.showTimer), this.showTimer = null);
|
|
3143
3906
|
}
|
|
3144
|
-
},
|
|
3145
|
-
function
|
|
3907
|
+
}, Vt = 48, Ht = 5e4, Ut = 100;
|
|
3908
|
+
function Wt() {
|
|
3146
3909
|
return new Promise((e) => {
|
|
3147
3910
|
if (typeof requestAnimationFrame != "function" || document.hidden) {
|
|
3148
3911
|
setTimeout(e, 0);
|
|
3149
3912
|
return;
|
|
3150
3913
|
}
|
|
3151
|
-
let t = setTimeout(e,
|
|
3914
|
+
let t = setTimeout(e, Ut);
|
|
3152
3915
|
requestAnimationFrame(() => setTimeout(() => {
|
|
3153
3916
|
clearTimeout(t), e();
|
|
3154
3917
|
}, 0));
|
|
3155
3918
|
});
|
|
3156
3919
|
}
|
|
3157
|
-
var
|
|
3920
|
+
var X = [
|
|
3158
3921
|
"#4c8dff",
|
|
3159
3922
|
"#a57cff",
|
|
3160
3923
|
"#1398a8",
|
|
3161
3924
|
"#e25592",
|
|
3162
3925
|
"#8a93a3",
|
|
3163
3926
|
"#62c895"
|
|
3164
|
-
],
|
|
3927
|
+
], Z = 4, Q = null, $ = (e) => (e[e.length - 1]?.time ?? 0) > 0xe8d4a51000 ? 1 : 1e3, Gt = () => {
|
|
3928
|
+
let e = document.activeElement;
|
|
3929
|
+
return !!e && (e.tagName === "INPUT" || e.tagName === "TEXTAREA" || e.tagName === "SELECT" || e.isContentEditable);
|
|
3930
|
+
}, Kt = class {
|
|
3165
3931
|
chart;
|
|
3166
3932
|
state;
|
|
3167
3933
|
toolbar = null;
|
|
3168
3934
|
sidebar = null;
|
|
3169
3935
|
settings = null;
|
|
3170
3936
|
statusBar = null;
|
|
3937
|
+
goToDate = null;
|
|
3938
|
+
tooltip;
|
|
3939
|
+
indicatorLegend = null;
|
|
3940
|
+
legendFrame = 0;
|
|
3941
|
+
portal = document.createElement("div");
|
|
3942
|
+
overlayHost = () => {
|
|
3943
|
+
let e = document.fullscreenElement === this.root ? this.root : document.body;
|
|
3944
|
+
return this.portal.parentElement !== e && e.appendChild(this.portal), this.portal;
|
|
3945
|
+
};
|
|
3946
|
+
onRootPointerDown = () => {
|
|
3947
|
+
Q = this;
|
|
3948
|
+
};
|
|
3949
|
+
onFullscreenChange = () => {
|
|
3950
|
+
this.toolbar?.setFullscreen(document.fullscreenElement === this.root), this.portal.isConnected && this.overlayHost();
|
|
3951
|
+
};
|
|
3171
3952
|
commandPalette = null;
|
|
3172
3953
|
symbolSearch = null;
|
|
3173
3954
|
hotkeySheet = null;
|
|
@@ -3182,15 +3963,18 @@ var $ = [
|
|
|
3182
3963
|
depthLadder = null;
|
|
3183
3964
|
dataWindow = null;
|
|
3184
3965
|
lastHoverIndex = null;
|
|
3185
|
-
|
|
3966
|
+
legendHoverIndex = null;
|
|
3967
|
+
favoritesStore = new P();
|
|
3968
|
+
timeframeFavorites = new P("tcw:tf-favorites");
|
|
3969
|
+
timeframes = [];
|
|
3186
3970
|
watchlist = null;
|
|
3187
3971
|
watchlistSparkBuffer = /* @__PURE__ */ new Map();
|
|
3188
3972
|
sessionRefPrice = null;
|
|
3189
3973
|
hostWatchlistRefPrice = /* @__PURE__ */ new Map();
|
|
3190
3974
|
watchlistInterval = null;
|
|
3191
|
-
replayOriginalData = null;
|
|
3192
3975
|
replayPollInterval = null;
|
|
3193
3976
|
replaySpeed = 5;
|
|
3977
|
+
replayShade = null;
|
|
3194
3978
|
layoutKeyPrefix = null;
|
|
3195
3979
|
layoutDebounceMs = 1500;
|
|
3196
3980
|
activeLayoutKey = null;
|
|
@@ -3211,36 +3995,64 @@ var $ = [
|
|
|
3211
3995
|
baseTimeframeMs = 0;
|
|
3212
3996
|
compares = [];
|
|
3213
3997
|
constructor(n, r = {}) {
|
|
3214
|
-
if (this.options = r, this.symbols = r.symbols ??
|
|
3998
|
+
if (this.options = r, this.symbols = r.symbols ?? te, this.settingsState = {
|
|
3215
3999
|
...p,
|
|
3216
4000
|
...r.chartOptions?.numberLocale ? { numberLocale: r.chartOptions.numberLocale } : {}
|
|
3217
|
-
}, this.t =
|
|
4001
|
+
}, this.t = Rt(Lt(r.locale, r.messages)), r.persistLayouts) {
|
|
3218
4002
|
let e = r.persistLayouts === !0 ? {} : r.persistLayouts;
|
|
3219
4003
|
this.layoutKeyPrefix = e.keyPrefix ?? "tcw:layout:", this.layoutDebounceMs = e.debounceMs ?? 1500;
|
|
3220
4004
|
}
|
|
3221
|
-
let i = this.resolveIsDark(r.theme), a = this.resolveTheme(r.theme)
|
|
4005
|
+
let i = this.resolveIsDark(r.theme), a = this.resolveTheme(r.theme), o = {
|
|
4006
|
+
drawings: !0,
|
|
4007
|
+
drawingMagnet: !0,
|
|
4008
|
+
drawingUndoRedo: !0,
|
|
4009
|
+
indicators: !0,
|
|
4010
|
+
trading: r.trading !== !1,
|
|
4011
|
+
tradingContextMenu: !1,
|
|
4012
|
+
volume: !0,
|
|
4013
|
+
legend: !0,
|
|
4014
|
+
crosshair: !0,
|
|
4015
|
+
crosshairTooltip: !1,
|
|
4016
|
+
keyboard: !0,
|
|
4017
|
+
screenshot: !0,
|
|
4018
|
+
alerts: !0,
|
|
4019
|
+
barCountdown: !0,
|
|
4020
|
+
logScale: !0,
|
|
4021
|
+
watermark: !0,
|
|
4022
|
+
...r.chartOptions?.features
|
|
4023
|
+
};
|
|
4024
|
+
if (this.timeframes = tt(r.timeframes, o.timeframes), !this.timeframes.some((e) => this.timeframeFavorites.has(e))) {
|
|
4025
|
+
let e = nt(this.timeframes, o.defaultTimeframeFavorites, !!r.timeframes?.length);
|
|
4026
|
+
for (let t of e) this.timeframeFavorites.add(t);
|
|
4027
|
+
}
|
|
4028
|
+
let c = r.timeframe ?? "5m", l = !o.timeframes?.length || o.timeframes.includes(c) ? c : this.timeframes[0] ?? c;
|
|
3222
4029
|
this.state = {
|
|
3223
4030
|
symbol: r.symbol ?? this.symbols[0] ?? "BTCUSDT",
|
|
3224
|
-
timeframe:
|
|
4031
|
+
timeframe: l,
|
|
3225
4032
|
chartType: r.chartOptions?.chartType ?? "candlestick",
|
|
3226
4033
|
isDark: i,
|
|
3227
4034
|
activeIndicators: /* @__PURE__ */ new Map(),
|
|
3228
4035
|
activeTool: null,
|
|
3229
|
-
magnetEnabled: !
|
|
4036
|
+
magnetEnabled: o.drawingMagnet !== !1,
|
|
4037
|
+
stayInDrawing: !1,
|
|
3230
4038
|
connectionState: r.adapter ? "connecting" : "disconnected",
|
|
3231
4039
|
connectionMessage: r.adapter ? this.t("status.connecting") : ""
|
|
3232
|
-
},
|
|
4040
|
+
}, ne(), this.root = document.createElement("div"), this.root.className = "tcw-root", this.root.dataset.tcwTheme = i ? "dark" : "light", n.appendChild(this.root), this.tooltip = new lt(this.root), this.portal.className = "tcw-root tcw-portal", this.portal.dataset.tcwTheme = this.root.dataset.tcwTheme, r.toolbar !== !1 && (this.toolbar = new se(this.root, {
|
|
3233
4041
|
symbols: this.symbols,
|
|
3234
|
-
timeframes:
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
4042
|
+
timeframes: this.timeframes.map((e) => ({
|
|
4043
|
+
value: e,
|
|
4044
|
+
label: F(e)
|
|
4045
|
+
})),
|
|
4046
|
+
timeframeFavorites: this.pinnedTimeframes(),
|
|
4047
|
+
chartTypes: r.chartTypes ? u.filter((e) => r.chartTypes.includes(e.value)) : u,
|
|
4048
|
+
indicators: f,
|
|
4049
|
+
popularIndicatorIds: ee
|
|
3238
4050
|
}, {
|
|
3239
4051
|
onSymbolClick: () => this.handleSymbolClick(),
|
|
3240
4052
|
onTimeframe: (e) => this.handleTimeframe(e),
|
|
4053
|
+
onToggleTimeframeFavorite: (e) => this.handleToggleTimeframeFavorite(e),
|
|
3241
4054
|
onChartType: (e) => this.handleChartType(e),
|
|
3242
4055
|
onAddIndicator: (e) => this.handleAddIndicator(e),
|
|
3243
|
-
onRemoveIndicator: (e) => this.handleRemoveIndicator(e),
|
|
3244
4056
|
onScreenshot: () => this.chart.screenshot(),
|
|
3245
4057
|
onSettings: () => this.openSettings(),
|
|
3246
4058
|
onToggleTheme: () => this.handleToggleTheme(),
|
|
@@ -3248,16 +4060,17 @@ var $ = [
|
|
|
3248
4060
|
onToggleAlerts: r.alerts === !1 ? void 0 : () => this.toggleAlerts(),
|
|
3249
4061
|
onToggleObjects: r.objectTree === !1 ? void 0 : () => this.toggleObjects(),
|
|
3250
4062
|
onBracket: r.trading === !1 ? void 0 : (e) => this.startBracket(e),
|
|
3251
|
-
onToggleLadder: r.trading !== !1 && r.depthLadder ? () => this.depthLadder?.toggle() : void 0
|
|
4063
|
+
onToggleLadder: r.trading !== !1 && r.depthLadder ? () => this.depthLadder?.toggle() : void 0,
|
|
4064
|
+
onToggleFullscreen: r.fullscreen !== !1 && typeof document < "u" && document.fullscreenEnabled ? () => this.toggleFullscreen() : void 0
|
|
3252
4065
|
}, this.t));
|
|
3253
|
-
let
|
|
3254
|
-
if (
|
|
3255
|
-
drawingToolGroups:
|
|
4066
|
+
let m = document.createElement("div");
|
|
4067
|
+
if (m.className = "tcw-body", r.drawingTools !== !1 && (r.drawingFavorites && this.favoritesStore.seedDefaults(r.drawingFavorites), this.sidebar = new le(m, {
|
|
4068
|
+
drawingToolGroups: d,
|
|
3256
4069
|
favorites: this.favoritesStore.list()
|
|
3257
4070
|
}, {
|
|
3258
4071
|
onDrawingTool: (e) => this.handleDrawingTool(e),
|
|
3259
4072
|
onCancelDrawing: () => this.handleCancelDrawing(),
|
|
3260
|
-
onToggleMagnet: () => this.handleToggleMagnet(),
|
|
4073
|
+
onToggleMagnet: o.drawingMagnet === !1 ? void 0 : () => this.handleToggleMagnet(),
|
|
3261
4074
|
onToggleFavorite: (e) => this.handleToggleFavorite(e),
|
|
3262
4075
|
onUndo: () => this.chart.undo(),
|
|
3263
4076
|
onRedo: () => this.chart.redo(),
|
|
@@ -3267,34 +4080,17 @@ var $ = [
|
|
|
3267
4080
|
activeTool: null
|
|
3268
4081
|
}, this.updateUI();
|
|
3269
4082
|
},
|
|
3270
|
-
onToggleStyle: () => this.drawingStyle?.toggle()
|
|
3271
|
-
|
|
4083
|
+
onToggleStyle: () => this.drawingStyle?.toggle(),
|
|
4084
|
+
onToggleStayInDrawing: () => this.handleToggleStayInDrawing()
|
|
4085
|
+
})), this.chartContainer = document.createElement("div"), this.chartContainer.className = "tcw-chart-container", m.appendChild(this.chartContainer), this.loading = new Bt(this.chartContainer, this.t("status.loading")), r.watchlist && (this.watchlist = new Oe(m, this.symbols, { onSelect: (e) => {
|
|
3272
4086
|
this.setSymbol(e);
|
|
3273
|
-
} }, this.t("watchlist.title")), this.watchlist.setActive(this.state.symbol), this.watchlist.setLocale(this.settingsState.numberLocale || void 0)), this.root.appendChild(
|
|
4087
|
+
} }, this.t("watchlist.title")), this.watchlist.setActive(this.state.symbol), this.watchlist.setLocale(this.settingsState.numberLocale || void 0)), this.root.appendChild(m), this.chart = new e(this.chartContainer, {
|
|
3274
4088
|
chartType: this.state.chartType,
|
|
3275
4089
|
theme: a,
|
|
3276
4090
|
autoScale: !0,
|
|
3277
4091
|
crosshair: { mode: "magnet" },
|
|
3278
4092
|
...r.chartOptions,
|
|
3279
|
-
features:
|
|
3280
|
-
drawings: !0,
|
|
3281
|
-
drawingMagnet: !0,
|
|
3282
|
-
drawingUndoRedo: !0,
|
|
3283
|
-
indicators: !0,
|
|
3284
|
-
trading: r.trading !== !1,
|
|
3285
|
-
tradingContextMenu: !1,
|
|
3286
|
-
volume: !0,
|
|
3287
|
-
legend: !0,
|
|
3288
|
-
crosshair: !0,
|
|
3289
|
-
crosshairTooltip: !1,
|
|
3290
|
-
keyboard: !0,
|
|
3291
|
-
screenshot: !0,
|
|
3292
|
-
alerts: !0,
|
|
3293
|
-
barCountdown: !0,
|
|
3294
|
-
logScale: !0,
|
|
3295
|
-
watermark: !0,
|
|
3296
|
-
...r.chartOptions?.features
|
|
3297
|
-
}
|
|
4093
|
+
features: o
|
|
3298
4094
|
}), this.chart.on("dataUpdate", (e) => this.handleDataUpdate(e.payload)), r.dragDropImport !== !1 && (this.dragDrop = new t(this.chartContainer, {
|
|
3299
4095
|
onData: (e, t, n) => {
|
|
3300
4096
|
this.setData(e), this.toast(`Loaded ${t.data.length} bars from ${n.name}` + (t.skipped > 0 ? ` (${t.skipped} skipped)` : ""));
|
|
@@ -3302,28 +4098,92 @@ var $ = [
|
|
|
3302
4098
|
onError: (e, t) => {
|
|
3303
4099
|
this.toast(`${t.name}: ${e.message}`, "error");
|
|
3304
4100
|
}
|
|
3305
|
-
}), this.dragDrop.attach()), r.statusBar !== !1
|
|
4101
|
+
}), this.dragDrop.attach()), r.statusBar !== !1) {
|
|
4102
|
+
let e = r.rangeBar !== !1;
|
|
4103
|
+
this.statusBar = new fe(this.root, e ? {
|
|
4104
|
+
presets: s,
|
|
4105
|
+
presetLabels: { All: this.t("range.all") },
|
|
4106
|
+
groupLabel: this.t("range.presets"),
|
|
4107
|
+
goToLabel: this.t("range.goTo"),
|
|
4108
|
+
onPreset: (e) => this.chart.setVisibleRangePreset(e),
|
|
4109
|
+
onGoTo: () => this.toggleGoToDate()
|
|
4110
|
+
} : void 0), e && (this.goToDate = new st(this.root, {
|
|
4111
|
+
title: this.t("range.goTo"),
|
|
4112
|
+
date: this.t("range.date"),
|
|
4113
|
+
time: this.t("range.time"),
|
|
4114
|
+
submit: this.t("range.goToSubmit"),
|
|
4115
|
+
cancel: this.t("range.cancel")
|
|
4116
|
+
}, ({ date: e, time: t }) => this.goToWallTime(e, t)));
|
|
4117
|
+
}
|
|
4118
|
+
if (r.settings !== !1 && (this.settings = new de({
|
|
3306
4119
|
onChange: (e) => this.applySettings(e),
|
|
3307
4120
|
onReset: () => this.resetSettings(),
|
|
3308
4121
|
onClose: () => {}
|
|
3309
|
-
}, this.t
|
|
4122
|
+
}, this.t, {
|
|
4123
|
+
barCountdown: o.barCountdown,
|
|
4124
|
+
logScale: o.logScale
|
|
4125
|
+
}, this.overlayHost)), this.symbolSearch = new me({
|
|
3310
4126
|
onPick: (e) => {
|
|
3311
4127
|
this.setSymbol(e);
|
|
3312
4128
|
},
|
|
3313
4129
|
onClose: () => {}
|
|
3314
|
-
}), this.hotkeySheet = new
|
|
4130
|
+
}, this.overlayHost), this.hotkeySheet = new be({ onClose: () => {} }, this.t, this.overlayHost), this.chart.on("drawingToolChange", (e) => {
|
|
4131
|
+
let t = e.payload.tool;
|
|
4132
|
+
t !== this.state.activeTool && (this.state = {
|
|
4133
|
+
...this.state,
|
|
4134
|
+
activeTool: t
|
|
4135
|
+
}, this.updateUI());
|
|
4136
|
+
}), this.chart.on("indicatorAdd", () => this.syncIndicatorsFromChart()), this.chart.on("indicatorRemove", () => this.syncIndicatorsFromChart()), r.indicatorLegend !== !1) {
|
|
4137
|
+
this.chart.setPaneTitlesVisible(!1), this.indicatorLegend = new mt(this.chartContainer, {
|
|
4138
|
+
onToggleVisible: (e, t) => {
|
|
4139
|
+
this.chart.setIndicatorVisible(e, t), this.scheduleLegend(), this.objectTree?.isOpen() && this.refreshObjects();
|
|
4140
|
+
},
|
|
4141
|
+
onSettings: (e) => this.openIndicatorSettings(e),
|
|
4142
|
+
onRemove: (e) => this.handleRemoveIndicator(e)
|
|
4143
|
+
}, {
|
|
4144
|
+
show: this.t("legend.show"),
|
|
4145
|
+
hide: this.t("legend.hide"),
|
|
4146
|
+
settings: this.t("legend.settings"),
|
|
4147
|
+
remove: this.t("legend.remove"),
|
|
4148
|
+
collapse: this.t("legend.collapse"),
|
|
4149
|
+
expand: this.t("legend.expand")
|
|
4150
|
+
}), this.chart.on("crosshairMove", (e) => {
|
|
4151
|
+
let t = e.payload;
|
|
4152
|
+
this.legendHoverIndex = typeof t.barIndex == "number" ? t.barIndex : null, this.scheduleLegend();
|
|
4153
|
+
}), this.chart.on("crosshairLeave", () => {
|
|
4154
|
+
this.legendHoverIndex = null, this.scheduleLegend();
|
|
4155
|
+
});
|
|
4156
|
+
for (let e of [
|
|
4157
|
+
"indicatorUpdate",
|
|
4158
|
+
"dataUpdate",
|
|
4159
|
+
"resize",
|
|
4160
|
+
"paneResize",
|
|
4161
|
+
"themeChange"
|
|
4162
|
+
]) this.chart.on(e, () => this.scheduleLegend());
|
|
4163
|
+
}
|
|
4164
|
+
if (this.chart.on("barClick", (e) => {
|
|
3315
4165
|
if (!this.replayBar?.isMounted()) return;
|
|
3316
4166
|
let t = e.payload.barIndex;
|
|
3317
|
-
typeof t == "number"
|
|
3318
|
-
|
|
4167
|
+
if (typeof t == "number") {
|
|
4168
|
+
if (this.replayBar.getMode() === "select") {
|
|
4169
|
+
this.startReplayAt(t);
|
|
4170
|
+
return;
|
|
4171
|
+
}
|
|
4172
|
+
this.chart.getReplayState() === "playing" && this.chart.replayPause(), this.chart.replaySeek(t), this.replayBar.setState("paused");
|
|
4173
|
+
}
|
|
4174
|
+
}), this.chart.on("crosshairMove", (e) => {
|
|
4175
|
+
if (this.replayBar?.getMode() !== "select" || !this.replayBar.isMounted()) return;
|
|
4176
|
+
let t = e.payload.point;
|
|
4177
|
+
this.positionReplayShade(t?.x ?? null);
|
|
4178
|
+
}), this.dataWindow = new Tt(this.root, { formatPrice: (e) => this.formatAlertPrice(e) }), this.chart.on("crosshairMove", (e) => {
|
|
3319
4179
|
let t = e.payload;
|
|
3320
4180
|
this.lastHoverIndex = typeof t.barIndex == "number" ? t.barIndex : null, this.dataWindow?.isOpen() && this.dataWindow.render(this.buildDataWindowModel());
|
|
3321
|
-
}), r.drawingTools !== !1 && (this.drawingStyle = new
|
|
4181
|
+
}), r.drawingTools !== !1 && (this.drawingStyle = new Ye(this.root, {
|
|
3322
4182
|
onStyleChange: (e) => {
|
|
3323
4183
|
this.chart.setDrawingStyle(e), this.chart.setSelectedDrawingStyle(e);
|
|
3324
4184
|
},
|
|
3325
4185
|
getStyle: () => this.chart.getDrawingStyle()
|
|
3326
|
-
}, new
|
|
4186
|
+
}, new Ge())), r.trading !== !1 && (this.bracketBar = new xt(this.root, {
|
|
3327
4187
|
onConfirm: () => this.chart.confirmBracket(),
|
|
3328
4188
|
onCancel: () => {
|
|
3329
4189
|
this.chart.cancelBracket(), this.bracketBar?.hide();
|
|
@@ -3331,7 +4191,7 @@ var $ = [
|
|
|
3331
4191
|
}), this.chart.on("bracketPlace", (e) => {
|
|
3332
4192
|
let t = e.payload;
|
|
3333
4193
|
this.bracketBar?.hide(), this.toast(`${t.side === "buy" ? "Long" : "Short"} bracket placed · ${t.riskReward.toFixed(2)}R`);
|
|
3334
|
-
}), r.depthLadder && (this.depthLadder = new
|
|
4194
|
+
}), r.depthLadder && (this.depthLadder = new wt(this.root, {
|
|
3335
4195
|
onTrade: (e, t) => {
|
|
3336
4196
|
this.chart.placeOrderIntent({
|
|
3337
4197
|
side: e,
|
|
@@ -3343,7 +4203,7 @@ var $ = [
|
|
|
3343
4203
|
})), this.chart.on("dataUpdate", (e) => {
|
|
3344
4204
|
let t = e.payload;
|
|
3345
4205
|
t && t.bracket === "cancelled" && this.bracketBar?.hide();
|
|
3346
|
-
})), r.alerts !== !1 && (this.alertsPanel = new
|
|
4206
|
+
})), r.alerts !== !1 && (this.alertsPanel = new Fe(this.root, {
|
|
3347
4207
|
onAdd: (e, t, n, r, i) => {
|
|
3348
4208
|
this.chart.addAlert(e, t, n, r, i);
|
|
3349
4209
|
},
|
|
@@ -3351,18 +4211,20 @@ var $ = [
|
|
|
3351
4211
|
onClear: () => this.chart.clearAlerts(),
|
|
3352
4212
|
getChannelValue: (e) => this.getAlertChannelValue(e),
|
|
3353
4213
|
formatPrice: (e) => this.formatAlertPrice(e)
|
|
3354
|
-
}), this.chart.on("alertAdd", () => this.refreshAlerts()), this.chart.on("alertRemove", () => this.refreshAlerts()), this.chart.on("alertUpdate", () => this.refreshAlerts()), r.alertNotifications && (this.alertNotifier = new
|
|
4214
|
+
}), this.chart.on("alertAdd", () => this.refreshAlerts()), this.chart.on("alertRemove", () => this.refreshAlerts()), this.chart.on("alertUpdate", () => this.refreshAlerts()), r.alertNotifications && (this.alertNotifier = new St(r.alertNotifications)), this.chart.on("alertTriggered", (e) => {
|
|
3355
4215
|
let t = e.payload, n = `Price ${this.formatAlertPrice(t.price)}${t.message ? ` — ${t.message}` : ""}`;
|
|
3356
4216
|
this.toast(`🔔 Alert: ${n}`, "info"), this.alertNotifier?.notify(n), this.refreshAlerts();
|
|
3357
4217
|
})), r.objectTree !== !1) {
|
|
3358
|
-
this.indicatorSettings = new
|
|
3359
|
-
onApply: (e, t) =>
|
|
4218
|
+
this.indicatorSettings = new Ve(this.root, {
|
|
4219
|
+
onApply: (e, t) => {
|
|
4220
|
+
this.chart.updateIndicator(e, t), this.syncIndicatorsFromChart();
|
|
4221
|
+
},
|
|
3360
4222
|
onClose: () => {}
|
|
3361
|
-
}), this.objectTree = new
|
|
4223
|
+
}), this.objectTree = new Ie(this.root, {
|
|
3362
4224
|
onRemoveIndicator: (e) => this.handleRemoveIndicator(e),
|
|
3363
4225
|
onConfigureIndicator: (e) => this.openIndicatorSettings(e),
|
|
3364
4226
|
onToggleIndicatorVisible: (e, t) => {
|
|
3365
|
-
this.chart.setIndicatorVisible(e, t), this.refreshObjects();
|
|
4227
|
+
this.chart.setIndicatorVisible(e, t), this.refreshObjects(), this.scheduleLegend();
|
|
3366
4228
|
},
|
|
3367
4229
|
onRemoveDrawing: (e) => {
|
|
3368
4230
|
this.chart.removeDrawing(e), this.refreshObjects();
|
|
@@ -3373,7 +4235,7 @@ var $ = [
|
|
|
3373
4235
|
onToggleDrawingLocked: (e, t) => {
|
|
3374
4236
|
this.chart.setDrawingLocked(e, t), this.refreshObjects();
|
|
3375
4237
|
},
|
|
3376
|
-
onAddCompare: () => this.handleAddCompare(),
|
|
4238
|
+
onAddCompare: o.compareSymbols === !1 ? void 0 : () => this.handleAddCompare(),
|
|
3377
4239
|
onRemoveCompare: (e) => this.handleRemoveCompare(e)
|
|
3378
4240
|
});
|
|
3379
4241
|
let e = () => {
|
|
@@ -3381,23 +4243,28 @@ var $ = [
|
|
|
3381
4243
|
};
|
|
3382
4244
|
this.chart.on("drawingCreate", e), this.chart.on("drawingRemove", e), this.chart.on("indicatorAdd", e), this.chart.on("indicatorRemove", e);
|
|
3383
4245
|
}
|
|
3384
|
-
if (this.commandPalette = new
|
|
4246
|
+
if (this.commandPalette = new pe({
|
|
3385
4247
|
onIndicator: (e) => this.handleAddIndicator(e),
|
|
3386
4248
|
onChartType: (e) => this.handleChartType(e),
|
|
3387
4249
|
onDrawingTool: (e) => this.handleDrawingTool(e),
|
|
3388
4250
|
onTimeframe: (e) => this.handleTimeframe(e),
|
|
3389
4251
|
onAction: (e) => this.handleAction(e),
|
|
3390
4252
|
onClose: () => {}
|
|
3391
|
-
}), this.boundGlobalKeydown = (e) => {
|
|
3392
|
-
if ((
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
4253
|
+
}, this.overlayHost), this.boundGlobalKeydown = (e) => {
|
|
4254
|
+
if (!(this.replayBar?.isMounted() && this.handleReplayKey(e))) {
|
|
4255
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "k") e.preventDefault(), this.toggleCommandPalette();
|
|
4256
|
+
else if ((e.ctrlKey || e.metaKey) && (e.key === "p" || e.key === "P")) e.preventDefault(), this.symbolSearch?.open(this.symbols, this.state.symbol);
|
|
4257
|
+
else if (e.altKey && !e.ctrlKey && !e.metaKey && (e.code === "KeyI" || e.code === "KeyG")) {
|
|
4258
|
+
if (Gt() || Q !== null && Q !== this) return;
|
|
4259
|
+
e.code === "KeyI" ? (e.preventDefault(), this.applySettings({ invertScale: !this.settingsState.invertScale })) : this.goToDate && (e.preventDefault(), this.toggleGoToDate());
|
|
4260
|
+
} else if (e.key === "?" && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
4261
|
+
let t = document.activeElement;
|
|
4262
|
+
if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return;
|
|
4263
|
+
e.preventDefault(), this.hotkeySheet?.open();
|
|
4264
|
+
}
|
|
3398
4265
|
}
|
|
3399
|
-
}, document.addEventListener("keydown", this.boundGlobalKeydown), r.adapter && (this.adapter = r.adapter, this.connectStream()), this.watchlist && (this.watchlistInterval = setInterval(() => this.tickWatchlist(), 1e3)), this.updateUI(), r.onReady?.(this.chart), r.shareUrl && typeof location < "u") {
|
|
3400
|
-
let e =
|
|
4266
|
+
}, document.addEventListener("keydown", this.boundGlobalKeydown), document.addEventListener("fullscreenchange", this.onFullscreenChange), this.root.addEventListener("pointerdown", this.onRootPointerDown, !0), r.adapter && (this.adapter = r.adapter, this.connectStream()), this.watchlist && (this.watchlistInterval = setInterval(() => this.tickWatchlist(), 1e3)), this.updateUI(), r.onReady?.(this.chart), r.shareUrl && typeof location < "u") {
|
|
4267
|
+
let e = Pt(location.hash);
|
|
3401
4268
|
e && this.importState(e);
|
|
3402
4269
|
}
|
|
3403
4270
|
}
|
|
@@ -3427,19 +4294,21 @@ var $ = [
|
|
|
3427
4294
|
});
|
|
3428
4295
|
}
|
|
3429
4296
|
async setTimeframe(e) {
|
|
3430
|
-
if (this.
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
if (
|
|
3439
|
-
|
|
3440
|
-
|
|
4297
|
+
if (this.chart.isTimeframeAllowed(e)) {
|
|
4298
|
+
if (this.state = {
|
|
4299
|
+
...this.state,
|
|
4300
|
+
timeframe: e
|
|
4301
|
+
}, this.options.onTimeframeChange?.(e), this.updateUI(), this.adapter) {
|
|
4302
|
+
await this.connectStream();
|
|
4303
|
+
return;
|
|
4304
|
+
}
|
|
4305
|
+
if (this.options.resampleTimeframes !== !1) {
|
|
4306
|
+
if (!this.baseSeries) {
|
|
4307
|
+
let e = this.chart.getData();
|
|
4308
|
+
e.length > 0 && (this.baseSeries = e, this.baseTimeframeMs = l(e));
|
|
4309
|
+
}
|
|
4310
|
+
await this.switchLocalTimeframe();
|
|
3441
4311
|
}
|
|
3442
|
-
await this.switchLocalTimeframe();
|
|
3443
4312
|
}
|
|
3444
4313
|
}
|
|
3445
4314
|
setTheme(e) {
|
|
@@ -3447,13 +4316,13 @@ var $ = [
|
|
|
3447
4316
|
this.state = {
|
|
3448
4317
|
...this.state,
|
|
3449
4318
|
isDark: t
|
|
3450
|
-
}, this.root.dataset.tcwTheme = t ? "dark" : "light", this.chart.setTheme(n), this.updateUI();
|
|
4319
|
+
}, this.root.dataset.tcwTheme = t ? "dark" : "light", this.portal.dataset.tcwTheme = this.root.dataset.tcwTheme, this.chart.setTheme(n), this.updateUI();
|
|
3451
4320
|
}
|
|
3452
4321
|
getChart() {
|
|
3453
4322
|
return this.chart;
|
|
3454
4323
|
}
|
|
3455
4324
|
destroy() {
|
|
3456
|
-
this.destroyed || (this.destroyed = !0, this.boundGlobalKeydown && document.removeEventListener("keydown", this.boundGlobalKeydown), this.flushActiveLayout(), this.commandPalette?.destroy(), this.symbolSearch?.destroy(), this.hotkeySheet?.destroy(), this.replayPollInterval && clearInterval(this.replayPollInterval), this.replayBar?.destroy(), this.dragDrop?.detach(), this.alertsPanel?.destroy(), this.objectTree?.destroy(), this.indicatorSettings?.destroy(), this.drawingStyle?.destroy(), this.bracketBar?.destroy(), this.alertNotifier?.destroy(), this.depthLadder?.destroy(), this.dataWindow?.destroy(), this.watchlistInterval && clearInterval(this.watchlistInterval), this.watchlist?.destroy(), this.toolbar?.destroy(), this.sidebar?.destroy(), this.settings?.destroy(), this.statusBar?.destroy(), this.loading.destroy(), this.chart.destroy(), this.root.remove(),
|
|
4325
|
+
this.destroyed || (this.destroyed = !0, this.boundGlobalKeydown && document.removeEventListener("keydown", this.boundGlobalKeydown), document.removeEventListener("fullscreenchange", this.onFullscreenChange), this.root.removeEventListener("pointerdown", this.onRootPointerDown, !0), this.tooltip.destroy(), this.legendFrame && cancelAnimationFrame(this.legendFrame), this.indicatorLegend?.destroy(), Q === this && (Q = null), this.portal.remove(), document.fullscreenElement === this.root && document.exitFullscreen().catch(() => {}), this.flushActiveLayout(), this.commandPalette?.destroy(), this.symbolSearch?.destroy(), this.hotkeySheet?.destroy(), this.replayPollInterval && clearInterval(this.replayPollInterval), this.replayShade?.remove(), this.replayBar?.destroy(), this.dragDrop?.detach(), this.alertsPanel?.destroy(), this.objectTree?.destroy(), this.indicatorSettings?.destroy(), this.drawingStyle?.destroy(), this.bracketBar?.destroy(), this.alertNotifier?.destroy(), this.depthLadder?.destroy(), this.dataWindow?.destroy(), this.watchlistInterval && clearInterval(this.watchlistInterval), this.watchlist?.destroy(), this.toolbar?.destroy(), this.sidebar?.destroy(), this.settings?.destroy(), this.statusBar?.destroy(), this.goToDate?.destroy(), this.loading.destroy(), this.chart.destroy(), this.root.remove(), re());
|
|
3457
4326
|
}
|
|
3458
4327
|
handleSymbolClick() {
|
|
3459
4328
|
this.symbolSearch?.open(this.symbols, this.state.symbol);
|
|
@@ -3461,14 +4330,49 @@ var $ = [
|
|
|
3461
4330
|
handleTimeframe(e) {
|
|
3462
4331
|
this.setTimeframe(e);
|
|
3463
4332
|
}
|
|
4333
|
+
toggleFullscreen() {
|
|
4334
|
+
if (document.fullscreenElement === this.root) {
|
|
4335
|
+
document.exitFullscreen().catch(() => {});
|
|
4336
|
+
return;
|
|
4337
|
+
}
|
|
4338
|
+
this.root.requestFullscreen().catch((e) => {
|
|
4339
|
+
this.toast(e instanceof Error ? e.message : "Fullscreen is not available", "error");
|
|
4340
|
+
});
|
|
4341
|
+
}
|
|
4342
|
+
displayTzOffset() {
|
|
4343
|
+
let e = this.settingsState.timezone;
|
|
4344
|
+
return e === "local" || !Number.isFinite(Number(e)) ? null : Number(e);
|
|
4345
|
+
}
|
|
4346
|
+
toggleGoToDate() {
|
|
4347
|
+
if (!this.goToDate) return;
|
|
4348
|
+
if (this.goToDate.isOpen()) {
|
|
4349
|
+
this.goToDate.close();
|
|
4350
|
+
return;
|
|
4351
|
+
}
|
|
4352
|
+
let e = this.chart.getData();
|
|
4353
|
+
e.length !== 0 && this.goToDate.open(ot(e[e.length - 1].time * $(e), this.displayTzOffset()), this.root.querySelector("[data-role=\"goto\"]"));
|
|
4354
|
+
}
|
|
4355
|
+
goToWallTime(e, t) {
|
|
4356
|
+
let n = at(e, t, this.displayTzOffset()), r = this.chart.getData();
|
|
4357
|
+
if (n === null || r.length === 0) return;
|
|
4358
|
+
let i = n / $(r);
|
|
4359
|
+
this.chart.goToTime(i), i < r[0].time && this.toast(this.t("range.beforeData"));
|
|
4360
|
+
}
|
|
4361
|
+
pinnedTimeframes() {
|
|
4362
|
+
let e = this.timeframeFavorites.list();
|
|
4363
|
+
return this.timeframes.filter((t) => e.includes(t));
|
|
4364
|
+
}
|
|
4365
|
+
handleToggleTimeframeFavorite(e) {
|
|
4366
|
+
this.timeframeFavorites.toggle(e), this.toolbar?.setTimeframeFavorites(this.pinnedTimeframes()), this.updateUI();
|
|
4367
|
+
}
|
|
3464
4368
|
setData(e) {
|
|
3465
|
-
this.localSeq++, this.baseSeries = e, this.baseTimeframeMs =
|
|
4369
|
+
this.localSeq++, this.baseSeries = e, this.baseTimeframeMs = l(e), this.applyTimeframeData(), this.adapter || this.loading.end();
|
|
3466
4370
|
}
|
|
3467
4371
|
async switchLocalTimeframe() {
|
|
3468
4372
|
if (!this.baseSeries) return;
|
|
3469
|
-
let e = ++this.localSeq, t = this.lastLocalSwitchMs >=
|
|
4373
|
+
let e = ++this.localSeq, t = this.lastLocalSwitchMs >= Vt || this.baseSeries.length >= Ht;
|
|
3470
4374
|
try {
|
|
3471
|
-
if (t && (this.loading.begin(this.chart.getData().length > 0, !0), await
|
|
4375
|
+
if (t && (this.loading.begin(this.chart.getData().length > 0, !0), await Wt(), e !== this.localSeq || this.destroyed)) return;
|
|
3472
4376
|
this.applyTimeframeData();
|
|
3473
4377
|
} finally {
|
|
3474
4378
|
e === this.localSeq && this.loading.end();
|
|
@@ -3476,10 +4380,10 @@ var $ = [
|
|
|
3476
4380
|
}
|
|
3477
4381
|
applyTimeframeData() {
|
|
3478
4382
|
if (!this.baseSeries) return;
|
|
3479
|
-
let e = performance.now(), t =
|
|
4383
|
+
let e = performance.now(), t = o(this.state.timeframe);
|
|
3480
4384
|
if (this.baseTimeframeMs > 0 && t > this.baseTimeframeMs) {
|
|
3481
4385
|
let e = this.options.weekStartsOn ?? 1;
|
|
3482
|
-
this.chart.setData(
|
|
4386
|
+
this.chart.setData(c(this.baseSeries, this.state.timeframe, { weekStartsOn: e }));
|
|
3483
4387
|
} else this.chart.setData(this.baseSeries);
|
|
3484
4388
|
this.lastLocalSwitchMs = performance.now() - e;
|
|
3485
4389
|
}
|
|
@@ -3525,7 +4429,7 @@ var $ = [
|
|
|
3525
4429
|
id: e.id,
|
|
3526
4430
|
params: e.params
|
|
3527
4431
|
}));
|
|
3528
|
-
return
|
|
4432
|
+
return Mt({
|
|
3529
4433
|
v: 1,
|
|
3530
4434
|
symbol: this.state.symbol,
|
|
3531
4435
|
timeframe: this.state.timeframe,
|
|
@@ -3536,26 +4440,19 @@ var $ = [
|
|
|
3536
4440
|
});
|
|
3537
4441
|
}
|
|
3538
4442
|
async importState(e) {
|
|
3539
|
-
let t =
|
|
4443
|
+
let t = Nt(e);
|
|
3540
4444
|
if (!t) return !1;
|
|
3541
4445
|
t.symbol && t.symbol !== this.state.symbol && await this.setSymbol(t.symbol), t.timeframe && t.timeframe !== this.state.timeframe && await this.setTimeframe(t.timeframe), t.chartType && this.handleChartType(t.chartType), this.chart.setScaleMode(t.scaleMode);
|
|
3542
|
-
for (let
|
|
3543
|
-
let
|
|
3544
|
-
|
|
3545
|
-
let t = this.chart.addIndicator(e.id, e.params);
|
|
3546
|
-
t && n.set(e.id, t);
|
|
3547
|
-
}
|
|
3548
|
-
return this.state = {
|
|
3549
|
-
...this.state,
|
|
3550
|
-
activeIndicators: n
|
|
3551
|
-
}, this.chart.setDrawings(t.drawings), this.updateUI(), !0;
|
|
4446
|
+
for (let e of [...this.state.activeIndicators.keys()]) this.chart.removeIndicator(e);
|
|
4447
|
+
for (let e of t.indicators) this.chart.addIndicator(e.id, e.params);
|
|
4448
|
+
return this.chart.setDrawings(t.drawings), this.updateUI(), !0;
|
|
3552
4449
|
}
|
|
3553
4450
|
async copyChartImage() {
|
|
3554
4451
|
let e = await this.chart.copyScreenshot();
|
|
3555
4452
|
this.toast(e ? "Chart image copied" : "Copy failed — image clipboard unavailable", e ? "info" : "error");
|
|
3556
4453
|
}
|
|
3557
4454
|
async copyShareLink() {
|
|
3558
|
-
let e =
|
|
4455
|
+
let e = Ft(typeof location < "u" ? location.href : "", this.exportState());
|
|
3559
4456
|
try {
|
|
3560
4457
|
await navigator.clipboard.writeText(e), this.toast("Share link copied");
|
|
3561
4458
|
} catch {
|
|
@@ -3623,7 +4520,7 @@ var $ = [
|
|
|
3623
4520
|
visible: e.visible
|
|
3624
4521
|
})), t = this.chart.getDrawings().map((e) => ({
|
|
3625
4522
|
id: e.id,
|
|
3626
|
-
label:
|
|
4523
|
+
label: Re(e.type),
|
|
3627
4524
|
visible: e.visible,
|
|
3628
4525
|
locked: e.locked
|
|
3629
4526
|
})), n = this.compares.map((e) => ({
|
|
@@ -3645,7 +4542,7 @@ var $ = [
|
|
|
3645
4542
|
}
|
|
3646
4543
|
async addCompareSymbol(e) {
|
|
3647
4544
|
if (!this.adapter || this.compares.some((t) => t.symbol === e)) return;
|
|
3648
|
-
let t =
|
|
4545
|
+
let t = X[this.compares.length % X.length], n = `cmp_${e}`;
|
|
3649
4546
|
try {
|
|
3650
4547
|
let r = await this.adapter.fetchHistory(e, this.state.timeframe, this.options.historyLimit ?? 500);
|
|
3651
4548
|
this.compares.length === 0 && this.chart.setCompareMode("percent"), this.chart.addCompareSymbol(n, e, r, t), this.compares = [...this.compares, {
|
|
@@ -3719,37 +4616,10 @@ var $ = [
|
|
|
3719
4616
|
}, this.chart.setChartType(e), this.updateUI();
|
|
3720
4617
|
}
|
|
3721
4618
|
handleAddIndicator(e) {
|
|
3722
|
-
|
|
3723
|
-
let t = this.state.activeIndicators.get(e);
|
|
3724
|
-
this.chart.removeIndicator(t);
|
|
3725
|
-
let n = new Map(this.state.activeIndicators);
|
|
3726
|
-
n.delete(e), this.state = {
|
|
3727
|
-
...this.state,
|
|
3728
|
-
activeIndicators: n
|
|
3729
|
-
};
|
|
3730
|
-
} else {
|
|
3731
|
-
let t = this.chart.addIndicator(e);
|
|
3732
|
-
if (t) {
|
|
3733
|
-
let n = new Map(this.state.activeIndicators);
|
|
3734
|
-
n.set(e, t), this.state = {
|
|
3735
|
-
...this.state,
|
|
3736
|
-
activeIndicators: n
|
|
3737
|
-
};
|
|
3738
|
-
}
|
|
3739
|
-
}
|
|
3740
|
-
this.updateUI();
|
|
4619
|
+
this.chart.addIndicator(e);
|
|
3741
4620
|
}
|
|
3742
4621
|
handleRemoveIndicator(e) {
|
|
3743
4622
|
this.chart.removeIndicator(e);
|
|
3744
|
-
let t = new Map(this.state.activeIndicators);
|
|
3745
|
-
for (let [n, r] of t.entries()) if (r === e) {
|
|
3746
|
-
t.delete(n);
|
|
3747
|
-
break;
|
|
3748
|
-
}
|
|
3749
|
-
this.state = {
|
|
3750
|
-
...this.state,
|
|
3751
|
-
activeIndicators: t
|
|
3752
|
-
}, this.updateUI();
|
|
3753
4623
|
}
|
|
3754
4624
|
toggleCommandPalette() {
|
|
3755
4625
|
if (this.commandPalette?.isOpen()) {
|
|
@@ -3760,29 +4630,29 @@ var $ = [
|
|
|
3760
4630
|
}
|
|
3761
4631
|
buildCommandItems() {
|
|
3762
4632
|
let e = [];
|
|
3763
|
-
for (let t of
|
|
4633
|
+
for (let t of f) e.push({
|
|
3764
4634
|
id: t.id,
|
|
3765
4635
|
label: t.name,
|
|
3766
4636
|
category: "indicator",
|
|
3767
|
-
active: this.state.activeIndicators.
|
|
4637
|
+
active: [...this.state.activeIndicators.values()].some((e) => e.id === t.id)
|
|
3768
4638
|
});
|
|
3769
|
-
for (let t of
|
|
4639
|
+
for (let t of u) e.push({
|
|
3770
4640
|
id: t.value,
|
|
3771
4641
|
label: t.label,
|
|
3772
4642
|
category: "chartType",
|
|
3773
4643
|
active: this.state.chartType === t.value
|
|
3774
4644
|
});
|
|
3775
|
-
for (let t of
|
|
4645
|
+
for (let t of d) for (let n of t.tools) e.push({
|
|
3776
4646
|
id: n.value,
|
|
3777
4647
|
label: n.label,
|
|
3778
4648
|
category: "drawing",
|
|
3779
4649
|
active: this.state.activeTool === n.value
|
|
3780
4650
|
});
|
|
3781
|
-
for (let t of
|
|
3782
|
-
id: t
|
|
3783
|
-
label: t
|
|
4651
|
+
for (let t of this.timeframes) e.push({
|
|
4652
|
+
id: t,
|
|
4653
|
+
label: F(t),
|
|
3784
4654
|
category: "timeframe",
|
|
3785
|
-
active: this.state.timeframe === t
|
|
4655
|
+
active: this.state.timeframe === t
|
|
3786
4656
|
});
|
|
3787
4657
|
return e.push({
|
|
3788
4658
|
id: "screenshot",
|
|
@@ -3864,6 +4734,13 @@ var $ = [
|
|
|
3864
4734
|
activeTool: null
|
|
3865
4735
|
}, this.chart.setDrawingTool(null), this.updateUI();
|
|
3866
4736
|
}
|
|
4737
|
+
handleToggleStayInDrawing() {
|
|
4738
|
+
let e = !this.state.stayInDrawing;
|
|
4739
|
+
this.state = {
|
|
4740
|
+
...this.state,
|
|
4741
|
+
stayInDrawing: e
|
|
4742
|
+
}, this.chart.setStayInDrawingMode(e), this.updateUI();
|
|
4743
|
+
}
|
|
3867
4744
|
handleToggleMagnet() {
|
|
3868
4745
|
let e = !this.state.magnetEnabled;
|
|
3869
4746
|
this.state = {
|
|
@@ -3876,7 +4753,7 @@ var $ = [
|
|
|
3876
4753
|
this.state = {
|
|
3877
4754
|
...this.state,
|
|
3878
4755
|
isDark: e
|
|
3879
|
-
}, this.root.dataset.tcwTheme = e ? "dark" : "light", this.chart.setTheme(e ? n : r), this.chart.setWatermark(this.state.symbol.replace("USDT", " / USDT"), {
|
|
4756
|
+
}, this.root.dataset.tcwTheme = e ? "dark" : "light", this.portal.dataset.tcwTheme = this.root.dataset.tcwTheme, this.chart.setTheme(e ? n : r), this.chart.setWatermark(this.state.symbol.replace("USDT", " / USDT"), {
|
|
3880
4757
|
fontSize: 48,
|
|
3881
4758
|
color: e ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.03)"
|
|
3882
4759
|
}), this.updateUI();
|
|
@@ -3902,7 +4779,7 @@ var $ = [
|
|
|
3902
4779
|
let t = this.layoutKeyPrefix + e;
|
|
3903
4780
|
this.activeLayoutKey = t;
|
|
3904
4781
|
try {
|
|
3905
|
-
this.chart.loadStateFromStorage(t) && this.
|
|
4782
|
+
this.chart.loadStateFromStorage(t) && this.syncIndicatorsFromChart();
|
|
3906
4783
|
} catch {}
|
|
3907
4784
|
this.chart.setAutoSave(t, this.layoutDebounceMs);
|
|
3908
4785
|
}
|
|
@@ -3914,9 +4791,12 @@ var $ = [
|
|
|
3914
4791
|
this.chart.disableAutoSave(), this.activeLayoutKey = null;
|
|
3915
4792
|
}
|
|
3916
4793
|
}
|
|
3917
|
-
|
|
4794
|
+
syncIndicatorsFromChart() {
|
|
3918
4795
|
let e = /* @__PURE__ */ new Map();
|
|
3919
|
-
for (let t of this.chart.getActiveIndicators()) e.set(t.
|
|
4796
|
+
for (let t of this.chart.getActiveIndicators()) e.set(t.instanceId, {
|
|
4797
|
+
id: t.id,
|
|
4798
|
+
label: qt(t.id, t.params, t.descriptor.defaultConfig)
|
|
4799
|
+
});
|
|
3920
4800
|
this.state = {
|
|
3921
4801
|
...this.state,
|
|
3922
4802
|
activeIndicators: e
|
|
@@ -3926,61 +4806,109 @@ var $ = [
|
|
|
3926
4806
|
this.replayBar?.isMounted() ? this.exitReplay() : this.enterReplay();
|
|
3927
4807
|
}
|
|
3928
4808
|
enterReplay() {
|
|
3929
|
-
|
|
3930
|
-
e
|
|
4809
|
+
if (this.replayTotal() < 2) return;
|
|
4810
|
+
let e = this.t;
|
|
4811
|
+
this.replayBar = new Ce({
|
|
3931
4812
|
onPlay: () => {
|
|
3932
|
-
this.
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
}
|
|
4813
|
+
if (this.replayBar?.getMode() === "select") {
|
|
4814
|
+
this.startReplayAt(Math.max(1, this.replayTotal() - 100), !0);
|
|
4815
|
+
return;
|
|
4816
|
+
}
|
|
4817
|
+
this.chart.getReplayState() === "paused" && this.chart.replayResume(), this.replayBar?.setState("playing");
|
|
3936
4818
|
},
|
|
3937
4819
|
onPause: () => {
|
|
3938
4820
|
this.chart.replayPause(), this.replayBar?.setState("paused");
|
|
3939
4821
|
},
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
},
|
|
3943
|
-
onStepBack: () => {
|
|
3944
|
-
let e = this.chart.getReplayProgress();
|
|
3945
|
-
this.chart.getReplayState() === "playing" && this.chart.replayPause(), this.chart.replaySeek(Math.max(0, e.current - 1)), this.replayBar?.setState("paused");
|
|
3946
|
-
},
|
|
3947
|
-
onStepForward: () => {
|
|
3948
|
-
let e = this.chart.getReplayProgress();
|
|
3949
|
-
this.chart.getReplayState() === "playing" && this.chart.replayPause(), this.chart.replaySeek(Math.min(e.total - 1, e.current + 1)), this.replayBar?.setState("paused");
|
|
3950
|
-
},
|
|
4822
|
+
onStepBack: () => this.stepReplay(-1),
|
|
4823
|
+
onStepForward: () => this.stepReplay(1),
|
|
3951
4824
|
onSeek: (e) => {
|
|
3952
4825
|
this.chart.getReplayState() === "playing" && this.chart.replayPause(), this.chart.replaySeek(e), this.replayBar?.setState("paused");
|
|
3953
4826
|
},
|
|
3954
4827
|
onSpeedChange: (e) => {
|
|
3955
4828
|
this.replaySpeed = e, this.chart.setReplaySpeed(e);
|
|
3956
4829
|
},
|
|
4830
|
+
onRandomStart: () => {
|
|
4831
|
+
let e = this.replayTotal(), t = Math.max(1, Math.floor(e * .1)), n = Math.max(t, Math.floor(e * .9));
|
|
4832
|
+
this.startReplayAt(t + Math.floor(Math.random() * (n - t + 1)));
|
|
4833
|
+
},
|
|
3957
4834
|
onClose: () => this.exitReplay()
|
|
4835
|
+
}, {
|
|
4836
|
+
selectHint: e("replay.selectHint"),
|
|
4837
|
+
random: e("replay.random"),
|
|
4838
|
+
realtime: e("replay.realtime"),
|
|
4839
|
+
barsPerSecond: e("replay.barsPerSecond"),
|
|
4840
|
+
play: e("replay.play"),
|
|
4841
|
+
pause: e("replay.pause"),
|
|
4842
|
+
stepBack: e("replay.stepBack"),
|
|
4843
|
+
stepForward: e("replay.stepForward")
|
|
3958
4844
|
}), this.replayBar.mount(this.chartContainer, {
|
|
3959
|
-
total:
|
|
3960
|
-
speed: this.replaySpeed
|
|
3961
|
-
}), this.chart.replayStart({
|
|
4845
|
+
total: this.replayTotal(),
|
|
3962
4846
|
speed: this.replaySpeed,
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
4847
|
+
mode: "select"
|
|
4848
|
+
}), this.replayShade = document.createElement("div"), this.replayShade.className = "tcw-replay-shade", this.replayShade.hidden = !0, this.chartContainer.appendChild(this.replayShade), this.chartContainer.addEventListener("mouseleave", this.hideReplayShade);
|
|
4849
|
+
}
|
|
4850
|
+
hideReplayShade = () => this.positionReplayShade(null);
|
|
4851
|
+
replayFrom(e, t = !1) {
|
|
4852
|
+
this.replayBar?.isMounted() || this.enterReplay(), this.startReplayAt(e, t);
|
|
4853
|
+
}
|
|
4854
|
+
startReplayAt(e, t = !1) {
|
|
4855
|
+
let n = this.replayBar;
|
|
4856
|
+
if (!n) return;
|
|
4857
|
+
let r = this.replayTotal(), i = Math.max(0, Math.min(Number.isFinite(e) ? Math.floor(e) : 0, r - 1));
|
|
4858
|
+
this.replayShade?.remove(), this.replayShade = null, this.chartContainer.removeEventListener("mouseleave", this.hideReplayShade), n.setMode("replay"), this.chart.replayStart({
|
|
4859
|
+
speed: this.replaySpeed,
|
|
4860
|
+
interval: 1e3,
|
|
4861
|
+
startIndex: i,
|
|
4862
|
+
paused: !0
|
|
4863
|
+
}), t && this.chart.replayResume(), n.setState(t ? "playing" : "paused"), this.syncReplayBar(), this.replayPollInterval && clearInterval(this.replayPollInterval), this.replayPollInterval = setInterval(() => this.syncReplayBar(), 150);
|
|
4864
|
+
}
|
|
4865
|
+
replayTotal() {
|
|
4866
|
+
return this.chart.isReplayActive() ? this.chart.getReplayProgress().total : this.chart.getData().length;
|
|
4867
|
+
}
|
|
4868
|
+
syncReplayBar() {
|
|
4869
|
+
let e = this.replayBar;
|
|
4870
|
+
if (!e?.isMounted() || e.getMode() !== "replay") return;
|
|
4871
|
+
if (!this.chart.isReplayActive()) {
|
|
4872
|
+
this.teardownReplayUi();
|
|
4873
|
+
return;
|
|
4874
|
+
}
|
|
4875
|
+
let t = this.chart.getReplayProgress(), n = this.chart.getReplayState();
|
|
4876
|
+
e.setProgress(t.current, t.total), e.setState(n === "stopped" ? "paused" : n), e.setEnded(n !== "playing" && t.total > 0 && t.current >= t.total - 1);
|
|
4877
|
+
}
|
|
4878
|
+
stepReplay(e) {
|
|
4879
|
+
if (this.replayBar?.getMode() !== "replay") return;
|
|
4880
|
+
let t = this.chart.getReplayProgress();
|
|
4881
|
+
this.chart.getReplayState() === "playing" && this.chart.replayPause(), this.chart.replaySeek(Math.max(0, Math.min(t.total - 1, t.current + e))), this.replayBar.setState("paused"), this.syncReplayBar();
|
|
4882
|
+
}
|
|
4883
|
+
handleReplayKey(e) {
|
|
4884
|
+
if (e.defaultPrevented) return !1;
|
|
4885
|
+
let t = document.activeElement;
|
|
4886
|
+
return t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.tagName === "SELECT" || t.isContentEditable) ? !1 : e.shiftKey && (e.key === "ArrowRight" || e.key === "ArrowLeft") && this.replayBar?.getMode() === "replay" ? (e.preventDefault(), e.stopPropagation(), this.stepReplay(e.key === "ArrowRight" ? 1 : -1), !0) : e.key === "Escape" && this.replayBar?.getMode() === "select" ? (this.exitReplay(), !0) : !1;
|
|
4887
|
+
}
|
|
4888
|
+
positionReplayShade(e) {
|
|
4889
|
+
let t = this.replayShade;
|
|
4890
|
+
if (!t) return;
|
|
4891
|
+
let n = this.chart.getPlotRect();
|
|
4892
|
+
if (e === null || e < n.x || e > n.x + n.width) {
|
|
4893
|
+
t.hidden = !0;
|
|
4894
|
+
return;
|
|
4895
|
+
}
|
|
4896
|
+
t.hidden = !1, t.style.left = `${e}px`, t.style.top = `${n.y}px`, t.style.width = `${n.x + n.width - e}px`, t.style.height = `${n.height}px`;
|
|
3972
4897
|
}
|
|
3973
4898
|
exitReplay() {
|
|
3974
|
-
this.
|
|
4899
|
+
this.teardownReplayUi(), this.chart.replayStop();
|
|
4900
|
+
}
|
|
4901
|
+
teardownReplayUi() {
|
|
4902
|
+
this.replayPollInterval &&= (clearInterval(this.replayPollInterval), null), this.replayShade?.remove(), this.replayShade = null, this.chartContainer.removeEventListener("mouseleave", this.hideReplayShade), this.replayBar?.unmount(), this.replayBar = null;
|
|
3975
4903
|
}
|
|
3976
4904
|
applySettings(e) {
|
|
3977
4905
|
this.settingsState = {
|
|
3978
4906
|
...this.settingsState,
|
|
3979
4907
|
...e
|
|
3980
|
-
}, e.gridVisible !== void 0 && this.chart.setGridVisible(e.gridVisible), e.volumeVisible !== void 0 && this.chart.setVolumeVisible(e.volumeVisible), e.volumeProfileVisible !== void 0 && this.chart.setVolumeProfileVisible(e.volumeProfileVisible), e.marketProfileVisible !== void 0 && this.chart.setMarketProfileVisible(e.marketProfileVisible), e.marketProfileSplit !== void 0 && this.chart.setMarketProfileConfig({ splitBySession: e.marketProfileSplit }), e.marketProfileLetters !== void 0 && this.chart.setMarketProfileConfig({ letters: e.marketProfileLetters }), e.marketProfileBuckets !== void 0 && this.chart.setMarketProfileConfig({ buckets: e.marketProfileBuckets }), e.marketProfileOpacity !== void 0 && this.chart.setMarketProfileConfig({ opacity: e.marketProfileOpacity }), e.depthHeatmapVisible !== void 0 && this.chart.setDepthHeatmapVisible(e.depthHeatmapVisible), e.depthHeatmapOpacity !== void 0 && this.chart.setDepthHeatmapConfig({ opacity: e.depthHeatmapOpacity }), e.sessionShadingVisible !== void 0 && this.chart.setSessionShadingVisible(e.sessionShadingVisible), e.pivotMarkersVisible !== void 0 && this.chart.setPivotMarkersVisible(e.pivotMarkersVisible), e.pivotStrength !== void 0 && this.chart.setPivotMarkersConfig({
|
|
4908
|
+
}, this.scheduleLegend(), e.gridVisible !== void 0 && this.chart.setGridVisible(e.gridVisible), e.volumeVisible !== void 0 && this.chart.setVolumeVisible(e.volumeVisible), e.volumeProfileVisible !== void 0 && this.chart.setVolumeProfileVisible(e.volumeProfileVisible), e.marketProfileVisible !== void 0 && this.chart.setMarketProfileVisible(e.marketProfileVisible), e.marketProfileSplit !== void 0 && this.chart.setMarketProfileConfig({ splitBySession: e.marketProfileSplit }), e.marketProfileLetters !== void 0 && this.chart.setMarketProfileConfig({ letters: e.marketProfileLetters }), e.marketProfileBuckets !== void 0 && this.chart.setMarketProfileConfig({ buckets: e.marketProfileBuckets }), e.marketProfileOpacity !== void 0 && this.chart.setMarketProfileConfig({ opacity: e.marketProfileOpacity }), e.depthHeatmapVisible !== void 0 && this.chart.setDepthHeatmapVisible(e.depthHeatmapVisible), e.depthHeatmapOpacity !== void 0 && this.chart.setDepthHeatmapConfig({ opacity: e.depthHeatmapOpacity }), e.sessionShadingVisible !== void 0 && this.chart.setSessionShadingVisible(e.sessionShadingVisible), e.pivotMarkersVisible !== void 0 && this.chart.setPivotMarkersVisible(e.pivotMarkersVisible), e.pivotStrength !== void 0 && this.chart.setPivotMarkersConfig({
|
|
3981
4909
|
left: e.pivotStrength,
|
|
3982
4910
|
right: e.pivotStrength
|
|
3983
|
-
}), e.pivotStructureLabels !== void 0 && this.chart.setPivotMarkersConfig({ structureLabels: e.pivotStructureLabels }), e.periodLevelsVisible !== void 0 && this.chart.setPeriodLevelsVisible(e.periodLevelsVisible), e.periodLevelsPeriod !== void 0 && this.chart.setPeriodLevelsPeriod(e.periodLevelsPeriod), e.crosshairMode !== void 0 && this.chart.setCrosshairMode(e.crosshairMode), e.autoScale !== void 0 && this.chart.setAutoScale(e.autoScale), e.scaleMode === void 0 ? e.logScale !== void 0 && (this.chart.setLogScale(e.logScale), this.settingsState = {
|
|
4911
|
+
}), e.pivotStructureLabels !== void 0 && this.chart.setPivotMarkersConfig({ structureLabels: e.pivotStructureLabels }), e.periodLevelsVisible !== void 0 && this.chart.setPeriodLevelsVisible(e.periodLevelsVisible), e.periodLevelsPeriod !== void 0 && this.chart.setPeriodLevelsPeriod(e.periodLevelsPeriod), e.crosshairMode !== void 0 && this.chart.setCrosshairMode(e.crosshairMode), e.autoScale !== void 0 && this.chart.setAutoScale(e.autoScale), e.invertScale !== void 0 && this.chart.setInvertScale(e.invertScale), e.scaleMode === void 0 ? e.logScale !== void 0 && (this.chart.setLogScale(e.logScale), this.settingsState = {
|
|
3984
4912
|
...this.settingsState,
|
|
3985
4913
|
scaleMode: e.logScale ? "logarithmic" : "regular"
|
|
3986
4914
|
}) : (this.chart.setScaleMode(e.scaleMode), this.settingsState = {
|
|
@@ -4029,8 +4957,37 @@ var $ = [
|
|
|
4029
4957
|
}
|
|
4030
4958
|
this.updateUI();
|
|
4031
4959
|
}
|
|
4960
|
+
scheduleLegend() {
|
|
4961
|
+
!this.indicatorLegend || this.legendFrame || (this.legendFrame = requestAnimationFrame(() => {
|
|
4962
|
+
this.legendFrame = 0, this.renderLegend();
|
|
4963
|
+
}));
|
|
4964
|
+
}
|
|
4965
|
+
renderLegend() {
|
|
4966
|
+
if (!this.indicatorLegend || this.destroyed) return;
|
|
4967
|
+
let e = this.chart.getData().length - 1, t = this.chart.isTimeAligned() ? this.legendHoverIndex : null, n = t === null ? e : Math.min(Math.max(t, 0), e), r = new Map(this.chart.getIndicatorPanes().map((e) => [e.instanceId, e.rect])), i = this.settingsState.numberLocale || "en-US", a = this.chart.getActiveIndicators().map((t) => {
|
|
4968
|
+
let a = r.get(t.instanceId) ?? null, o = this.chart.getIndicatorOutput(t.instanceId)?.series, s = n >= 0 ? o?.[n] : null, c = W(t.id, s), l = (n === e ? c.length : W(t.id, e >= 0 ? o?.[e] : null).length) === 1 ? vt(t.id, this.chart.getIndicatorStyle(t.instanceId)?.colors ?? [], s) : null;
|
|
4969
|
+
return {
|
|
4970
|
+
instanceId: t.instanceId,
|
|
4971
|
+
label: qt(t.id, t.params, t.descriptor.defaultConfig),
|
|
4972
|
+
visible: t.visible,
|
|
4973
|
+
values: c.map((e) => ({
|
|
4974
|
+
text: a ? bt(e, i) : this.chart.formatPrice(e),
|
|
4975
|
+
color: l
|
|
4976
|
+
})),
|
|
4977
|
+
pane: a ? {
|
|
4978
|
+
x: a.x + Z,
|
|
4979
|
+
y: a.y + 1,
|
|
4980
|
+
width: a.width - 2 * Z
|
|
4981
|
+
} : null
|
|
4982
|
+
};
|
|
4983
|
+
}), o = this.chart.getPlotRect();
|
|
4984
|
+
this.indicatorLegend.update(a, {
|
|
4985
|
+
left: o.x + Z,
|
|
4986
|
+
top: this.chart.getLegendBottom() + 1
|
|
4987
|
+
});
|
|
4988
|
+
}
|
|
4032
4989
|
updateUI() {
|
|
4033
|
-
this.toolbar?.update(this.state), this.sidebar?.update(this.state), this.statusBar?.update({
|
|
4990
|
+
this.scheduleLegend(), this.toolbar?.update(this.state), this.sidebar?.update(this.state), this.statusBar?.update({
|
|
4034
4991
|
connectionState: this.state.connectionState,
|
|
4035
4992
|
message: this.state.connectionMessage,
|
|
4036
4993
|
symbol: this.state.symbol,
|
|
@@ -4051,7 +5008,11 @@ var $ = [
|
|
|
4051
5008
|
return !0;
|
|
4052
5009
|
}
|
|
4053
5010
|
};
|
|
5011
|
+
function qt(e, t, n) {
|
|
5012
|
+
let r = Object.keys(n || t).map((e) => t[e]).filter((e) => typeof e == "number" && Number.isFinite(e)).slice(0, 3).map((e) => String(Number(e.toFixed(4))));
|
|
5013
|
+
return [e.toUpperCase(), ...r].join(" ");
|
|
5014
|
+
}
|
|
4054
5015
|
//#endregion
|
|
4055
|
-
export {
|
|
5016
|
+
export { y as CHART_TYPE_ICONS, Kt as ChartWidget, v as DRAWING_TOOL_ICONS, _ as UI_ICONS, T as createChartTypeIcon, C as createIcon, w as createToolIcon, x as iconSvg };
|
|
4056
5017
|
|
|
4057
5018
|
//# sourceMappingURL=index.js.map
|