@tradecanvas/chart 0.8.2 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +86 -4
  2. package/dist/Chart.d.ts +121 -1
  3. package/dist/Chart.d.ts.map +1 -1
  4. package/dist/{Chart-Cn_TSMO7.js → DragDropImporter-Br2ez5Gz.js} +560 -91
  5. package/dist/DragDropImporter-Br2ez5Gz.js.map +1 -0
  6. package/dist/DragDropImporter-ycI_Tcs6.cjs +2 -0
  7. package/dist/DragDropImporter-ycI_Tcs6.cjs.map +1 -0
  8. package/dist/finance/PerformanceDashboard.d.ts +43 -0
  9. package/dist/finance/PerformanceDashboard.d.ts.map +1 -0
  10. package/dist/finance/index.d.ts +4 -0
  11. package/dist/finance/index.d.ts.map +1 -1
  12. package/dist/finance/performanceData.d.ts +39 -0
  13. package/dist/finance/performanceData.d.ts.map +1 -0
  14. package/dist/index.cjs +1 -1
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.ts +5 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +287 -30
  19. package/dist/index.js.map +1 -1
  20. package/dist/io/DragDropImporter.d.ts +34 -0
  21. package/dist/io/DragDropImporter.d.ts.map +1 -0
  22. package/dist/io/index.d.ts +7 -0
  23. package/dist/io/index.d.ts.map +1 -0
  24. package/dist/io/parseOHLCV.d.ts +21 -0
  25. package/dist/io/parseOHLCV.d.ts.map +1 -0
  26. package/dist/io/resampleOHLCV.d.ts +29 -0
  27. package/dist/io/resampleOHLCV.d.ts.map +1 -0
  28. package/dist/widget/AlertNotifier.d.ts +32 -0
  29. package/dist/widget/AlertNotifier.d.ts.map +1 -0
  30. package/dist/widget/ChartWidget.d.ts +100 -2
  31. package/dist/widget/ChartWidget.d.ts.map +1 -1
  32. package/dist/widget/DrawingFavoritesStore.d.ts +22 -0
  33. package/dist/widget/DrawingFavoritesStore.d.ts.map +1 -0
  34. package/dist/widget/DrawingTemplateStore.d.ts +29 -0
  35. package/dist/widget/DrawingTemplateStore.d.ts.map +1 -0
  36. package/dist/widget/WidgetAlertsPanel.d.ts +58 -0
  37. package/dist/widget/WidgetAlertsPanel.d.ts.map +1 -0
  38. package/dist/widget/WidgetBracketBar.d.ts +21 -0
  39. package/dist/widget/WidgetBracketBar.d.ts.map +1 -0
  40. package/dist/widget/WidgetDataWindow.d.ts +41 -0
  41. package/dist/widget/WidgetDataWindow.d.ts.map +1 -0
  42. package/dist/widget/WidgetDepthLadder.d.ts +29 -0
  43. package/dist/widget/WidgetDepthLadder.d.ts.map +1 -0
  44. package/dist/widget/WidgetDrawingSidebar.d.ts +6 -0
  45. package/dist/widget/WidgetDrawingSidebar.d.ts.map +1 -1
  46. package/dist/widget/WidgetDrawingStyle.d.ts +36 -0
  47. package/dist/widget/WidgetDrawingStyle.d.ts.map +1 -0
  48. package/dist/widget/WidgetHotkeySheet.d.ts +20 -0
  49. package/dist/widget/WidgetHotkeySheet.d.ts.map +1 -0
  50. package/dist/widget/WidgetIndicatorSettings.d.ts +42 -0
  51. package/dist/widget/WidgetIndicatorSettings.d.ts.map +1 -0
  52. package/dist/widget/WidgetObjectTree.d.ts +59 -0
  53. package/dist/widget/WidgetObjectTree.d.ts.map +1 -0
  54. package/dist/widget/WidgetReplayBar.d.ts +41 -0
  55. package/dist/widget/WidgetReplayBar.d.ts.map +1 -0
  56. package/dist/widget/WidgetSettings.d.ts +1 -0
  57. package/dist/widget/WidgetSettings.d.ts.map +1 -1
  58. package/dist/widget/WidgetSymbolSearch.d.ts +34 -0
  59. package/dist/widget/WidgetSymbolSearch.d.ts.map +1 -0
  60. package/dist/widget/WidgetToolbar.d.ts.map +1 -1
  61. package/dist/widget/WidgetWatchlist.d.ts +39 -0
  62. package/dist/widget/WidgetWatchlist.d.ts.map +1 -0
  63. package/dist/widget/icons.d.ts.map +1 -1
  64. package/dist/widget/index.cjs +2372 -877
  65. package/dist/widget/index.cjs.map +1 -1
  66. package/dist/widget/index.js +2455 -185
  67. package/dist/widget/index.js.map +1 -1
  68. package/dist/widget/ladderRows.d.ts +21 -0
  69. package/dist/widget/ladderRows.d.ts.map +1 -0
  70. package/dist/widget/types.d.ts +91 -1
  71. package/dist/widget/types.d.ts.map +1 -1
  72. package/dist/widget/widgetConfig.d.ts.map +1 -1
  73. package/dist/widget/widgetShareState.d.ts +26 -0
  74. package/dist/widget/widgetShareState.d.ts.map +1 -0
  75. package/package.json +3 -3
  76. package/dist/Chart-Br_N-BCL.cjs +0 -2
  77. package/dist/Chart-Br_N-BCL.cjs.map +0 -1
  78. package/dist/Chart-Cn_TSMO7.js.map +0 -1
@@ -1,7 +1,41 @@
1
- import { t as e } from "../Chart-Cn_TSMO7.js";
2
- import { DARK_THEME as t, LIGHT_THEME as n } from "@tradecanvas/commons";
1
+ import { r as e, t } from "../DragDropImporter-Br2ez5Gz.js";
2
+ import { DARK_THEME as n, LIGHT_THEME as r, timeframeBucketStart as i, timeframeToMs as a } from "@tradecanvas/commons";
3
+ //#region src/io/resampleOHLCV.ts
4
+ function o(e, t, n = {}) {
5
+ if (e.length === 0) return [];
6
+ let r = n.weekStartsOn ?? 1, a = [], o = null, s = NaN;
7
+ for (let n of e) {
8
+ let e = i(n.time, t, r);
9
+ o === null || e !== s ? (o !== null && a.push(o), o = {
10
+ time: e,
11
+ open: n.open,
12
+ high: n.high,
13
+ low: n.low,
14
+ close: n.close,
15
+ volume: n.volume
16
+ }, s = e) : o = {
17
+ time: o.time,
18
+ open: o.open,
19
+ high: Math.max(o.high, n.high),
20
+ low: Math.min(o.low, n.low),
21
+ close: n.close,
22
+ volume: o.volume + n.volume
23
+ };
24
+ }
25
+ return o !== null && a.push(o), a;
26
+ }
27
+ function s(e) {
28
+ if (e.length < 2) return 0;
29
+ let t = [];
30
+ for (let n = 1; n < e.length; n++) {
31
+ let r = e[n].time - e[n - 1].time;
32
+ r > 0 && t.push(r);
33
+ }
34
+ return t.length === 0 ? 0 : (t.sort((e, t) => e - t), t[Math.floor(t.length / 2)]);
35
+ }
36
+ //#endregion
3
37
  //#region src/widget/widgetConfig.ts
4
- var r = [
38
+ var c = [
5
39
  {
6
40
  label: "1m",
7
41
  value: "1m"
@@ -26,7 +60,7 @@ var r = [
26
60
  label: "1D",
27
61
  value: "1d"
28
62
  }
29
- ], i = [
63
+ ], l = [
30
64
  {
31
65
  label: "Candlestick",
32
66
  value: "candlestick"
@@ -75,7 +109,7 @@ var r = [
75
109
  label: "Line + Markers",
76
110
  value: "lineWithMarkers"
77
111
  }
78
- ], a = [
112
+ ], u = [
79
113
  {
80
114
  label: "Lines",
81
115
  tools: [
@@ -188,7 +222,7 @@ var r = [
188
222
  value: "arrow"
189
223
  }]
190
224
  }
191
- ], o = [
225
+ ], d = [
192
226
  {
193
227
  id: "sma",
194
228
  name: "SMA",
@@ -204,6 +238,11 @@ var r = [
204
238
  name: "Hull MA",
205
239
  type: "overlay"
206
240
  },
241
+ {
242
+ id: "mtfma",
243
+ name: "MTF Moving Average",
244
+ type: "overlay"
245
+ },
207
246
  {
208
247
  id: "bb",
209
248
  name: "Bollinger Bands",
@@ -219,6 +258,11 @@ var r = [
219
258
  name: "Anchored VWAP",
220
259
  type: "overlay"
221
260
  },
261
+ {
262
+ id: "svwap",
263
+ name: "Session VWAP",
264
+ type: "overlay"
265
+ },
222
266
  {
223
267
  id: "ichimoku",
224
268
  name: "Ichimoku Cloud",
@@ -259,6 +303,16 @@ var r = [
259
303
  name: "Linear Regression Channel",
260
304
  type: "overlay"
261
305
  },
306
+ {
307
+ id: "chandelier",
308
+ name: "Chandelier Exit",
309
+ type: "overlay"
310
+ },
311
+ {
312
+ id: "alligator",
313
+ name: "Alligator",
314
+ type: "overlay"
315
+ },
262
316
  {
263
317
  id: "rsi",
264
318
  name: "RSI",
@@ -349,12 +403,157 @@ var r = [
349
403
  name: "Chaikin Oscillator",
350
404
  type: "panel"
351
405
  },
406
+ {
407
+ id: "voldelta",
408
+ name: "Volume Delta",
409
+ type: "panel"
410
+ },
411
+ {
412
+ id: "vortex",
413
+ name: "Vortex Indicator",
414
+ type: "panel"
415
+ },
416
+ {
417
+ id: "chop",
418
+ name: "Choppiness Index",
419
+ type: "panel"
420
+ },
421
+ {
422
+ id: "uo",
423
+ name: "Ultimate Oscillator",
424
+ type: "panel"
425
+ },
426
+ {
427
+ id: "fi",
428
+ name: "Force Index",
429
+ type: "panel"
430
+ },
431
+ {
432
+ id: "crsi",
433
+ name: "Connors RSI",
434
+ type: "panel"
435
+ },
436
+ {
437
+ id: "coppock",
438
+ name: "Coppock Curve",
439
+ type: "panel"
440
+ },
441
+ {
442
+ id: "kst",
443
+ name: "Know Sure Thing",
444
+ type: "panel"
445
+ },
446
+ {
447
+ id: "elderray",
448
+ name: "Elder Ray",
449
+ type: "panel"
450
+ },
451
+ {
452
+ id: "stc",
453
+ name: "Schaff Trend Cycle",
454
+ type: "panel"
455
+ },
456
+ {
457
+ id: "kvo",
458
+ name: "Klinger Oscillator",
459
+ type: "panel"
460
+ },
461
+ {
462
+ id: "fisher",
463
+ name: "Fisher Transform",
464
+ type: "panel"
465
+ },
466
+ {
467
+ id: "dpo",
468
+ name: "Detrended Price Osc",
469
+ type: "panel"
470
+ },
471
+ {
472
+ id: "bop",
473
+ name: "Balance of Power",
474
+ type: "panel"
475
+ },
476
+ {
477
+ id: "massindex",
478
+ name: "Mass Index",
479
+ type: "panel"
480
+ },
481
+ {
482
+ id: "cmo",
483
+ name: "Chande Momentum",
484
+ type: "panel"
485
+ },
486
+ {
487
+ id: "trix",
488
+ name: "TRIX",
489
+ type: "panel"
490
+ },
491
+ {
492
+ id: "emv",
493
+ name: "Ease of Movement",
494
+ type: "panel"
495
+ },
496
+ {
497
+ id: "pvt",
498
+ name: "Price Volume Trend",
499
+ type: "panel"
500
+ },
501
+ {
502
+ id: "wad",
503
+ name: "Williams A/D",
504
+ type: "panel"
505
+ },
506
+ {
507
+ id: "chaikinvol",
508
+ name: "Chaikin Volatility",
509
+ type: "panel"
510
+ },
511
+ {
512
+ id: "rvi",
513
+ name: "Relative Vigor Index",
514
+ type: "panel"
515
+ },
516
+ {
517
+ id: "stochrsi",
518
+ name: "Stochastic RSI",
519
+ type: "panel"
520
+ },
521
+ {
522
+ id: "ppo",
523
+ name: "Percentage Price Oscillator",
524
+ type: "panel"
525
+ },
526
+ {
527
+ id: "ac",
528
+ name: "Accelerator Oscillator",
529
+ type: "panel"
530
+ },
531
+ {
532
+ id: "rmi",
533
+ name: "Relative Momentum Index",
534
+ type: "panel"
535
+ },
536
+ {
537
+ id: "disparity",
538
+ name: "Disparity Index",
539
+ type: "panel"
540
+ },
541
+ {
542
+ id: "qstick",
543
+ name: "Qstick",
544
+ type: "panel"
545
+ },
546
+ {
547
+ id: "pgo",
548
+ name: "Pretty Good Oscillator",
549
+ type: "panel"
550
+ },
352
551
  {
353
552
  id: "volumeProfile",
354
553
  name: "Volume Profile",
355
554
  type: "panel"
356
555
  }
357
- ], s = [
556
+ ], f = [
358
557
  "sma",
359
558
  "ema",
360
559
  "bb",
@@ -365,12 +564,12 @@ var r = [
365
564
  "atr",
366
565
  "obv",
367
566
  "ichimoku"
368
- ], c = [
567
+ ], ee = [
369
568
  "BTCUSDT",
370
569
  "ETHUSDT",
371
570
  "SOLUSDT",
372
571
  "BNBUSDT"
373
- ], l = {
572
+ ], p = {
374
573
  candleUpColor: "#26A69A",
375
574
  candleDownColor: "#EF5350",
376
575
  candleUpWick: "#26A69A",
@@ -379,26 +578,42 @@ var r = [
379
578
  gridColor: "#1E222D",
380
579
  gridVisible: !0,
381
580
  volumeVisible: !0,
581
+ volumeProfileVisible: !1,
582
+ marketProfileVisible: !1,
583
+ marketProfileSplit: !1,
584
+ marketProfileLetters: !1,
585
+ marketProfileBuckets: 48,
586
+ marketProfileOpacity: .32,
587
+ depthHeatmapVisible: !1,
588
+ depthHeatmapOpacity: .7,
589
+ sessionShadingVisible: !1,
590
+ pivotMarkersVisible: !1,
591
+ pivotStrength: 5,
592
+ pivotStructureLabels: !1,
593
+ periodLevelsVisible: !1,
594
+ periodLevelsPeriod: "day",
382
595
  legendVisible: !0,
383
596
  barCountdown: !0,
384
597
  logScale: !1,
598
+ scaleMode: "regular",
385
599
  autoScale: !0,
386
600
  crosshairMode: "magnet",
387
- numberLocale: "en-US"
388
- }, u = "/* === TradeCanvas Widget Styles === */\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: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\r\n font-size: 13px;\r\n color: #d1d4dc;\r\n background: #131722;\r\n position: relative;\r\n overflow: hidden;\r\n\r\n /* Dark theme tokens (default) */\r\n --tcw-bg: #131722;\r\n --tcw-bg-elevated: #1E222D;\r\n --tcw-border: #2A2E39;\r\n --tcw-text: #d1d4dc;\r\n --tcw-text-dim: #b2b5be;\r\n --tcw-text-muted: #787b86;\r\n --tcw-accent: #2962ff;\r\n --tcw-accent-dim: rgba(41,98,255,0.15);\r\n --tcw-accent-glow: rgba(41,98,255,0.12);\r\n --tcw-red: #ef5350;\r\n --tcw-green: #26a69a;\r\n --tcw-transition: 0.15s ease;\r\n --tcw-radius-lg: 8px;\r\n --tcw-font-mono: 'SF Mono', 'Fira Code', 'Consolas', monospace;\r\n --tcw-hover-bg: rgba(255,255,255,0.04);\r\n}\r\n\r\n.tcw-root[data-tcw-theme=\"light\"] {\r\n color: #131722;\r\n background: #ffffff;\r\n\r\n --tcw-bg: #ffffff;\r\n --tcw-bg-elevated: #f0f3fa;\r\n --tcw-border: #e0e3eb;\r\n --tcw-text: #131722;\r\n --tcw-text-dim: #434651;\r\n --tcw-text-muted: #787b86;\r\n --tcw-accent: #2962ff;\r\n --tcw-accent-dim: rgba(41,98,255,0.1);\r\n --tcw-accent-glow: rgba(41,98,255,0.08);\r\n --tcw-red: #ef5350;\r\n --tcw-green: #26a69a;\r\n --tcw-hover-bg: rgba(0,0,0,0.04);\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 12px;\r\n height: 40px;\r\n border-bottom: 1px solid var(--tcw-border);\r\n background: var(--tcw-bg-elevated);\r\n flex-shrink: 0;\r\n position: relative;\r\n z-index: 20;\r\n}\r\n\r\n.tcw-toolbar-symbol {\r\n font-size: 13px;\r\n font-weight: 700;\r\n color: var(--tcw-text);\r\n padding: 0 10px;\r\n white-space: nowrap;\r\n letter-spacing: -0.01em;\r\n cursor: pointer;\r\n border: none;\r\n background: none;\r\n font-family: inherit;\r\n}\r\n\r\n.tcw-toolbar-sep {\r\n width: 1px;\r\n height: 20px;\r\n background: var(--tcw-border);\r\n flex-shrink: 0;\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 8px;\r\n}\r\n\r\n.tcw-toolbar-spacer {\r\n flex: 1;\r\n}\r\n\r\n.tcw-btn {\r\n padding: 4px 8px;\r\n font-size: 12px;\r\n font-family: inherit;\r\n border: none;\r\n border-radius: 4px;\r\n background: transparent;\r\n color: var(--tcw-text-dim);\r\n cursor: pointer;\r\n transition: all var(--tcw-transition);\r\n white-space: nowrap;\r\n line-height: 1;\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.tcw-active {\r\n background: var(--tcw-accent);\r\n color: white;\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: 4px;\r\n background: transparent;\r\n color: var(--tcw-text-muted);\r\n cursor: pointer;\r\n transition: all var(--tcw-transition);\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-badge-count {\r\n font-size: 10px;\r\n color: var(--tcw-accent);\r\n background: var(--tcw-accent-dim);\r\n padding: 0 4px;\r\n border-radius: 8px;\r\n font-weight: 600;\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: 2px 6px 2px 8px;\r\n font-size: 11px;\r\n font-weight: 500;\r\n background: var(--tcw-accent-dim);\r\n color: var(--tcw-accent);\r\n border-radius: 4px;\r\n white-space: nowrap;\r\n line-height: 1.4;\r\n}\r\n\r\n.tcw-chip-remove {\r\n cursor: pointer;\r\n opacity: 0.6;\r\n padding: 0 2px;\r\n transition: opacity var(--tcw-transition);\r\n border: none;\r\n background: none;\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: 4px 8px;\r\n font-size: 12px;\r\n font-family: inherit;\r\n border: none;\r\n border-radius: 4px;\r\n background: transparent;\r\n color: var(--tcw-text-dim);\r\n cursor: pointer;\r\n transition: all var(--tcw-transition);\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: 4px;\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: 0 8px 32px rgba(0,0,0,0.4);\r\n z-index: 50;\r\n padding: 4px 0;\r\n max-height: 400px;\r\n overflow-y: auto;\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: 6px 12px;\r\n font-size: 12px;\r\n color: var(--tcw-text-dim);\r\n cursor: pointer;\r\n transition: background var(--tcw-transition), color var(--tcw-transition);\r\n width: 100%;\r\n border: none;\r\n background: none;\r\n font-family: inherit;\r\n text-align: left;\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}\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.05em;\r\n padding: 6px 12px;\r\n font-weight: 600;\r\n}\r\n\r\n.tcw-dropdown-divider {\r\n border-top: 1px solid var(--tcw-border);\r\n margin: 4px 0;\r\n}\r\n\r\n.tcw-tag {\r\n font-size: 10px;\r\n color: var(--tcw-text-muted);\r\n padding: 1px 5px;\r\n border-radius: 3px;\r\n background: var(--tcw-hover-bg);\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}\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}\r\n\r\n/* === Sidebar === */\r\n\r\n.tcw-sidebar {\r\n width: 36px;\r\n flex-shrink: 0;\r\n background: var(--tcw-bg-elevated);\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: 4px 0;\r\n overflow: visible;\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: 36px;\r\n height: 32px;\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: all var(--tcw-transition);\r\n position: relative;\r\n flex-shrink: 0;\r\n line-height: 1;\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-glow);\r\n}\r\n\r\n.tcw-sidebar-btn.tcw-danger:hover {\r\n color: var(--tcw-red);\r\n background: rgba(239,68,68,0.1);\r\n}\r\n\r\n.tcw-multi-dot {\r\n position: absolute;\r\n bottom: 2px;\r\n right: 2px;\r\n width: 4px;\r\n height: 4px;\r\n border-radius: 50%;\r\n background: var(--tcw-text-muted);\r\n}\r\n\r\n.tcw-sidebar-divider {\r\n width: 20px;\r\n height: 1px;\r\n background: var(--tcw-border);\r\n margin: 4px 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: 2px;\r\n min-width: 160px;\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: 0 8px 32px rgba(0,0,0,0.4);\r\n z-index: 30;\r\n padding: 4px 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.05em;\r\n padding: 6px 12px;\r\n font-weight: 600;\r\n}\r\n\r\n.tcw-flyout-item {\r\n padding: 6px 12px;\r\n font-size: 12px;\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: none;\r\n text-align: left;\r\n font-family: inherit;\r\n transition: background var(--tcw-transition), color var(--tcw-transition);\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}\r\n\r\n/* === Modal === */\r\n\r\n.tcw-modal-backdrop {\r\n position: fixed;\r\n inset: 0;\r\n background: rgba(0,0,0,0.5);\r\n backdrop-filter: blur(4px);\r\n -webkit-backdrop-filter: blur(4px);\r\n z-index: 100;\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-lg);\r\n width: 480px;\r\n max-width: 90vw;\r\n max-height: 80vh;\r\n display: flex;\r\n flex-direction: column;\r\n box-shadow: 0 24px 64px rgba(0,0,0,0.5);\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: 16px 20px;\r\n border-bottom: 1px solid var(--tcw-border);\r\n}\r\n\r\n.tcw-modal-header h3 {\r\n font-size: 15px;\r\n font-weight: 600;\r\n margin: 0;\r\n color: var(--tcw-text);\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: 4px;\r\n transition: all var(--tcw-transition);\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: 0;\r\n padding: 0 20px;\r\n border-bottom: 1px solid var(--tcw-border);\r\n}\r\n\r\n.tcw-modal-tab {\r\n padding: 10px 16px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n color: var(--tcw-text-muted);\r\n border: none;\r\n background: none;\r\n cursor: pointer;\r\n border-bottom: 2px solid transparent;\r\n margin-bottom: -1px;\r\n transition: color var(--tcw-transition);\r\n font-family: inherit;\r\n text-transform: capitalize;\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}\r\n\r\n.tcw-modal-body {\r\n padding: 20px;\r\n overflow-y: auto;\r\n flex: 1;\r\n}\r\n\r\n.tcw-settings-section {\r\n margin-bottom: 20px;\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: 11px;\r\n font-weight: 600;\r\n text-transform: uppercase;\r\n letter-spacing: 0.05em;\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: 8px 0;\r\n}\r\n\r\n.tcw-settings-row + .tcw-settings-row {\r\n border-top: 1px solid rgba(255,255,255,0.03);\r\n}\r\n\r\n.tcw-root[data-tcw-theme=\"light\"] .tcw-settings-row + .tcw-settings-row {\r\n border-top: 1px solid rgba(0,0,0,0.04);\r\n}\r\n\r\n.tcw-settings-label {\r\n font-size: 13px;\r\n color: var(--tcw-text-dim);\r\n}\r\n\r\n.tcw-color-picker-wrap {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\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);\r\n border-radius: 4px;\r\n background: none;\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: 60px;\r\n}\r\n\r\n.tcw-toggle {\r\n width: 36px;\r\n height: 20px;\r\n background: rgba(255,255,255,0.1);\r\n border-radius: 10px;\r\n position: relative;\r\n cursor: pointer;\r\n transition: background var(--tcw-transition);\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: all var(--tcw-transition);\r\n}\r\n\r\n.tcw-toggle.tcw-on {\r\n background: var(--tcw-accent);\r\n}\r\n\r\n.tcw-toggle.tcw-on::after {\r\n left: 18px;\r\n background: white;\r\n}\r\n\r\n.tcw-root[data-tcw-theme=\"light\"] .tcw-toggle {\r\n background: rgba(0,0,0,0.1);\r\n}\r\n\r\n.tcw-root[data-tcw-theme=\"light\"] .tcw-toggle::after {\r\n background: #888;\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: white;\r\n}\r\n\r\n.tcw-settings-select {\r\n padding: 4px 8px;\r\n font-size: 12px;\r\n font-family: inherit;\r\n background: var(--tcw-bg);\r\n border: 1px solid var(--tcw-border);\r\n border-radius: 4px;\r\n color: var(--tcw-text);\r\n cursor: pointer;\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: 12px 20px;\r\n border-top: 1px solid var(--tcw-border);\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: none;\r\n font-family: inherit;\r\n text-decoration: underline;\r\n text-underline-offset: 2px;\r\n transition: color var(--tcw-transition);\r\n}\r\n\r\n.tcw-reset-link:hover {\r\n color: var(--tcw-text-dim);\r\n}\r\n\r\n.tcw-done-btn {\r\n padding: 6px 16px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n background: var(--tcw-accent);\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n font-family: inherit;\r\n transition: background var(--tcw-transition);\r\n}\r\n\r\n.tcw-done-btn:hover {\r\n background: #2563eb;\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: 6px 16px;\r\n border-top: 1px solid var(--tcw-border);\r\n background: var(--tcw-bg-elevated);\r\n font-size: 11px;\r\n color: var(--tcw-text-muted);\r\n flex-shrink: 0;\r\n}\r\n\r\n.tcw-status-indicator {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n}\r\n\r\n.tcw-status-dot {\r\n width: 6px;\r\n height: 6px;\r\n border-radius: 50%;\r\n background: var(--tcw-text-muted);\r\n transition: background 0.3s;\r\n}\r\n\r\n.tcw-status-dot.tcw-connected {\r\n background: var(--tcw-green);\r\n}\r\n\r\n.tcw-status-dot.tcw-error {\r\n background: var(--tcw-red);\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: 20%;\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-lg);\r\n width: 520px;\r\n max-width: 90vw;\r\n max-height: 60vh;\r\n display: flex;\r\n flex-direction: column;\r\n box-shadow: 0 24px 64px rgba(0,0,0,0.5);\r\n overflow: hidden;\r\n}\r\n\r\n.tcw-cmd-header {\r\n display: flex;\r\n align-items: center;\r\n padding: 12px 16px;\r\n border-bottom: 1px solid var(--tcw-border);\r\n gap: 10px;\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}\r\n\r\n.tcw-cmd-input {\r\n flex: 1;\r\n background: none;\r\n border: none;\r\n outline: none;\r\n font-size: 14px;\r\n font-family: inherit;\r\n color: var(--tcw-text);\r\n caret-color: var(--tcw-accent);\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 border: 1px solid var(--tcw-border);\r\n padding: 2px 6px;\r\n border-radius: 3px;\r\n flex-shrink: 0;\r\n}\r\n\r\n.tcw-cmd-list {\r\n overflow-y: auto;\r\n flex: 1;\r\n padding: 4px 0;\r\n max-height: 360px;\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.05em;\r\n padding: 8px 16px 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: 8px 16px;\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: none;\r\n font-family: inherit;\r\n text-align: left;\r\n transition: background 80ms ease;\r\n gap: 8px;\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-active .tcw-cmd-item-label {\r\n color: var(--tcw-accent);\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: 600;\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);\r\n border: 1px solid var(--tcw-border);\r\n padding: 1px 5px;\r\n border-radius: 3px;\r\n}\r\n\r\n.tcw-cmd-empty {\r\n padding: 24px 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: 16px;\r\n padding: 8px 16px;\r\n border-top: 1px solid var(--tcw-border);\r\n font-size: 11px;\r\n color: var(--tcw-text-muted);\r\n}\r\n\r\n/* === Responsive === */\r\n\r\n@media (max-width: 768px) {\r\n .tcw-toolbar {\r\n padding: 0 8px;\r\n height: 36px;\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-sidebar {\r\n width: 32px;\r\n }\r\n .tcw-sidebar-btn {\r\n width: 32px;\r\n height: 28px;\r\n }\r\n}\r\n", d = 0, f = "tcw-styles";
389
- function p() {
390
- if (d++, d > 1 || document.getElementById(f)) return;
601
+ numberLocale: "en-US",
602
+ timezone: "local"
603
+ }, te = "/* === TradeCanvas Widget — Premium Styles === */\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: #d1d4dc;\n background: #0d0f17;\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: #0d0f17;\n --tcw-bg-surface: #131722;\n --tcw-bg-elevated: #1a1e2d;\n --tcw-bg-overlay: #20253480;\n --tcw-border: #2a2e39;\n --tcw-border-strong: #363a45;\n --tcw-text: #e9ebf0;\n --tcw-text-dim: #b2b5be;\n --tcw-text-muted: #6b6f7a;\n\n --tcw-accent: #4f88ff;\n --tcw-accent-hover: #6b9aff;\n --tcw-accent-soft: rgba(79, 136, 255, 0.14);\n --tcw-accent-glow: rgba(79, 136, 255, 0.22);\n --tcw-accent-line: rgba(79, 136, 255, 0.55);\n\n --tcw-red: #f23645;\n --tcw-red-soft: rgba(242, 54, 69, 0.12);\n --tcw-green: #26a17b;\n --tcw-green-soft: rgba(38, 161, 123, 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(79, 136, 255, 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: #131722;\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: #e7e9ef;\n --tcw-border-strong: #d4d7e0;\n --tcw-text: #131722;\n --tcw-text-dim: #50545e;\n --tcw-text-muted: #8b8f99;\n\n --tcw-accent: #2962ff;\n --tcw-accent-hover: #1e4fd6;\n --tcw-accent-soft: rgba(41, 98, 255, 0.10);\n --tcw-accent-glow: rgba(41, 98, 255, 0.16);\n --tcw-accent-line: rgba(41, 98, 255, 0.45);\n\n --tcw-hover-bg: rgba(0, 0, 0, 0.04);\n --tcw-active-bg: rgba(41, 98, 255, 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.tcw-indicator-chips {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 0 4px;\n flex-wrap: nowrap;\n overflow: hidden;\n}\n\n.tcw-indicator-chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 6px 3px 9px;\n font-size: 11px;\n font-weight: 500;\n background: var(--tcw-accent-soft);\n color: var(--tcw-accent);\n border-radius: 9999px;\n white-space: nowrap;\n line-height: 1.4;\n letter-spacing: -0.005em;\n border: 1px solid transparent;\n transition: border-color var(--tcw-dur-fast) var(--tcw-ease);\n}\n\n.tcw-indicator-chip:hover {\n border-color: var(--tcw-accent-line);\n}\n\n.tcw-chip-remove {\n cursor: pointer;\n opacity: 0.55;\n padding: 0 2px;\n transition: opacity var(--tcw-dur-fast) var(--tcw-ease);\n border: none;\n background: transparent;\n color: inherit;\n display: inline-flex;\n align-items: center;\n}\n\n.tcw-chip-remove:hover {\n opacity: 1;\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.tcw-dropdown-item.tcw-active {\n color: var(--tcw-accent);\n background: var(--tcw-accent-soft);\n font-weight: 600;\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/* === 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@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-item {\n padding: 7px 10px;\n font-size: 12.5px;\n color: var(--tcw-text-dim);\n cursor: pointer;\n width: 100%;\n display: block;\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: #fff;\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 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-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(38, 161, 123, 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(242, 54, 69, 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.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: #fff;\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-indicator-chips {\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 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: #fff;\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: #fff;\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: #fff;\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", m = 0, h = "tcw-styles";
604
+ function g() {
605
+ if (m++, m > 1 || document.getElementById(h)) return;
391
606
  let e = document.createElement("style");
392
- e.id = f, e.textContent = u, document.head.appendChild(e);
607
+ e.id = h, e.textContent = te, document.head.appendChild(e);
393
608
  }
394
- function m() {
395
- if (d--, d > 0) return;
396
- let e = document.getElementById(f);
609
+ function _() {
610
+ if (m--, m > 0) return;
611
+ let e = document.getElementById(h);
397
612
  e && e.remove();
398
613
  }
399
614
  //#endregion
400
615
  //#region src/widget/icons.ts
401
- var h = {
616
+ var v = {
402
617
  cursor: "<path d=\"M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z\"/><path d=\"M13 13l6 6\"/>",
403
618
  trendingUp: "<polyline points=\"22 7 13.5 15.5 8.5 10.5 2 17\"/><polyline points=\"16 7 22 7 22 13\"/>",
404
619
  minus: "<path d=\"M5 12h14\"/>",
@@ -423,15 +638,26 @@ var h = {
423
638
  play: "<polygon points=\"6 3 20 12 6 21 6 3\"/>",
424
639
  pause: "<rect width=\"4\" height=\"16\" x=\"6\" y=\"4\"/><rect width=\"4\" height=\"16\" x=\"14\" y=\"4\"/>",
425
640
  stop: "<rect width=\"14\" height=\"14\" x=\"5\" y=\"5\" rx=\"1\"/>",
426
- 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\"/>"
641
+ 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\"/>",
642
+ 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\"/>",
643
+ plus: "<path d=\"M5 12h14\"/><path d=\"M12 5v14\"/>",
644
+ 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\"/>",
645
+ 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\"/>",
646
+ 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\"/>",
647
+ lock: "<rect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/>",
648
+ unlock: "<rect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"/>",
649
+ 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\"/>",
650
+ trendingDown: "<polyline points=\"22 17 13.5 8.5 8.5 13.5 2 7\"/><polyline points=\"16 17 22 17 22 11\"/>",
651
+ check: "<polyline points=\"20 6 9 17 4 12\"/>",
652
+ 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\"/>"
427
653
  };
428
- function g(e, t = 14) {
429
- let n = h[e];
654
+ function y(e, t = 14) {
655
+ let n = v[e];
430
656
  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>` : "";
431
657
  }
432
658
  //#endregion
433
659
  //#region src/widget/WidgetDropdown.ts
434
- var _ = class {
660
+ var b = class {
435
661
  trigger;
436
662
  panel;
437
663
  _isOpen = !1;
@@ -462,7 +688,7 @@ var _ = class {
462
688
  destroy() {
463
689
  this.trigger.removeEventListener("click", this.onTriggerClick), document.removeEventListener("mousedown", this.onOutsideClick), document.removeEventListener("keydown", this.onEscapeKey), this.panel.remove();
464
690
  }
465
- }, v = class {
691
+ }, ne = class {
466
692
  config;
467
693
  callbacks;
468
694
  el;
@@ -487,11 +713,32 @@ var _ = class {
487
713
  let a = document.createElement("div");
488
714
  a.style.position = "relative", a.style.display = "inline-flex";
489
715
  let o = document.createElement("button");
490
- o.className = "tcw-dropdown-trigger", o.dataset.role = "charttype", a.appendChild(o), n.appendChild(a), this.chartTypeDropdown = new _(a, { width: "160px" }), this.buildChartTypeMenu(), n.appendChild(this.sep());
716
+ o.className = "tcw-dropdown-trigger", o.dataset.role = "charttype", a.appendChild(o), n.appendChild(a), this.chartTypeDropdown = new b(a, { width: "160px" }), this.buildChartTypeMenu(), n.appendChild(this.sep());
491
717
  let s = document.createElement("div");
492
718
  s.style.position = "relative", s.style.display = "inline-flex";
493
719
  let c = document.createElement("button");
494
- c.className = "tcw-dropdown-trigger", c.dataset.role = "indicators", s.appendChild(c), n.appendChild(s), this.indicatorDropdown = new _(s, { width: "280px" }), this.buildIndicatorMenu(), this.chipsContainer = document.createElement("div"), this.chipsContainer.className = "tcw-indicator-chips", n.appendChild(this.chipsContainer), n.appendChild(this.spacer());
720
+ if (c.className = "tcw-dropdown-trigger", c.dataset.role = "indicators", s.appendChild(c), n.appendChild(s), this.indicatorDropdown = new b(s, { width: "280px" }), this.buildIndicatorMenu(), this.chipsContainer = document.createElement("div"), this.chipsContainer.className = "tcw-indicator-chips", n.appendChild(this.chipsContainer), n.appendChild(this.spacer()), t.onToggleReplay) {
721
+ let e = this.iconBtn("play", "Bar replay", t.onToggleReplay);
722
+ e.dataset.role = "replay", n.appendChild(e);
723
+ }
724
+ if (t.onBracket) {
725
+ let e = t.onBracket, r = this.iconBtn("trendingUp", "Long bracket", () => e("buy"));
726
+ r.dataset.role = "long", r.classList.add("tcw-btn-long"), n.appendChild(r);
727
+ let i = this.iconBtn("trendingDown", "Short bracket", () => e("sell"));
728
+ i.dataset.role = "short", i.classList.add("tcw-btn-short"), n.appendChild(i);
729
+ }
730
+ if (t.onToggleLadder) {
731
+ let e = this.iconBtn("ladder", "Depth ladder", t.onToggleLadder);
732
+ e.dataset.role = "ladder", n.appendChild(e);
733
+ }
734
+ if (t.onToggleObjects) {
735
+ let e = this.iconBtn("layers", "Objects", t.onToggleObjects);
736
+ e.dataset.role = "objects", n.appendChild(e);
737
+ }
738
+ if (t.onToggleAlerts) {
739
+ let e = this.iconBtn("bell", "Price alerts", t.onToggleAlerts);
740
+ e.dataset.role = "alerts", n.appendChild(e);
741
+ }
495
742
  let l = this.iconBtn("camera", "Screenshot", t.onScreenshot);
496
743
  n.appendChild(l);
497
744
  let u = this.iconBtn("settings", "Chart Settings", t.onSettings);
@@ -527,11 +774,11 @@ var _ = class {
527
774
  let n = this.el.querySelector("[data-role=\"charttype\"]");
528
775
  if (n) {
529
776
  let t = this.config.chartTypes.find((t) => t.value === e.chartType)?.label ?? "Candles";
530
- n.innerHTML = `${g("barChart", 14)} ${t} ${g("chevronDown", 12)}`;
777
+ n.innerHTML = `${y("barChart", 14)} ${t} ${y("chevronDown", 12)}`;
531
778
  }
532
779
  let r = this.el.querySelector("[data-role=\"indicators\"]");
533
780
  if (r) {
534
- let t = e.activeIndicators.size, n = `${g("trendingUp", 14)} Indicators`;
781
+ let t = e.activeIndicators.size, n = `${y("trendingUp", 14)} Indicators`;
535
782
  t > 0 && (n += ` <span class="tcw-badge-count">${t}</span>`), r.innerHTML = n;
536
783
  }
537
784
  if (this.chipsContainer) {
@@ -543,10 +790,10 @@ var _ = class {
543
790
  let n = document.createElement("span");
544
791
  n.textContent = e.label, t.appendChild(n);
545
792
  let r = document.createElement("button");
546
- r.className = "tcw-chip-remove", r.innerHTML = g("x", 10), r.addEventListener("click", () => this.callbacks.onRemoveIndicator(e.instanceId)), t.appendChild(r), this.chipsContainer.appendChild(t);
793
+ 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);
547
794
  }
548
795
  }
549
- this.themeBtn && (this.themeBtn.innerHTML = e.isDark ? g("moon", 14) : g("sun", 14));
796
+ this.themeBtn && (this.themeBtn.innerHTML = e.isDark ? y("moon", 14) : y("sun", 14));
550
797
  }
551
798
  getActiveIndicatorList(e) {
552
799
  let t = [];
@@ -567,12 +814,12 @@ var _ = class {
567
814
  }
568
815
  iconBtn(e, t, n) {
569
816
  let r = document.createElement("button");
570
- return r.className = "tcw-btn-icon", r.title = t, r.innerHTML = g(e, 14), r.addEventListener("click", n), r;
817
+ return r.className = "tcw-btn-icon", r.title = t, r.innerHTML = y(e, 14), r.addEventListener("click", n), r;
571
818
  }
572
819
  destroy() {
573
820
  this.chartTypeDropdown?.destroy(), this.indicatorDropdown?.destroy(), this.el.remove();
574
821
  }
575
- }, y = [
822
+ }, re = [
576
823
  "trendingUp",
577
824
  "minus",
578
825
  "penLine",
@@ -581,7 +828,43 @@ var _ = class {
581
828
  "gitBranch",
582
829
  "ruler",
583
830
  "type"
584
- ], b = class {
831
+ ], ie = {
832
+ trendLine: "trendingUp",
833
+ ray: "trendingUp",
834
+ extendedLine: "trendingUp",
835
+ horizontalLine: "minus",
836
+ verticalLine: "penLine",
837
+ rectangle: "square",
838
+ ellipse: "square",
839
+ triangle: "square",
840
+ arrow: "trendingUp",
841
+ parallelChannel: "gitBranch",
842
+ regressionChannel: "gitBranch",
843
+ pitchfork: "gitBranch",
844
+ fibRetracement: "hash",
845
+ fibExtension: "hash",
846
+ fibTimeZones: "hash",
847
+ gannBox: "hash",
848
+ gannFan: "gitBranch",
849
+ elliottWave: "trendingUp",
850
+ priceRange: "ruler",
851
+ dateRange: "ruler",
852
+ measure: "ruler",
853
+ textAnnotation: "type",
854
+ anchoredVWAP: "trendingUp",
855
+ volumeProfileRange: "barChart"
856
+ };
857
+ function ae(e) {
858
+ return ie[e] ?? "penLine";
859
+ }
860
+ function oe(e, t) {
861
+ for (let n of e) {
862
+ let e = n.tools.find((e) => e.value === t);
863
+ if (e) return e.label;
864
+ }
865
+ return t;
866
+ }
867
+ var x = class {
585
868
  config;
586
869
  callbacks;
587
870
  el;
@@ -590,29 +873,51 @@ var _ = class {
590
873
  cursorBtn = null;
591
874
  magnetBtn = null;
592
875
  flyoutEl = null;
876
+ favoritesEl = null;
877
+ favoritesDivider = null;
878
+ favorites = [];
593
879
  constructor(e, t, n) {
594
880
  this.config = t, this.callbacks = n, this.el = document.createElement("div"), this.el.className = "tcw-sidebar", this.build(), e.appendChild(this.el);
595
881
  }
596
882
  build() {
597
883
  let { config: e, callbacks: t, el: n } = this;
598
- this.cursorBtn = document.createElement("button"), this.cursorBtn.className = "tcw-sidebar-btn", this.cursorBtn.title = "Cursor", this.cursorBtn.innerHTML = g("cursor", 14), this.cursorBtn.addEventListener("click", t.onCancelDrawing), n.appendChild(this.cursorBtn), n.appendChild(this.divider()), e.drawingToolGroups.forEach((e, r) => {
884
+ this.cursorBtn = document.createElement("button"), this.cursorBtn.className = "tcw-sidebar-btn", this.cursorBtn.title = "Cursor", this.cursorBtn.innerHTML = y("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) => {
599
885
  let i = document.createElement("div");
600
886
  i.className = "tcw-tool-group-wrap";
601
887
  let a = document.createElement("button");
602
- if (a.className = "tcw-sidebar-btn", a.title = e.label, a.innerHTML = g(y[r] ?? "square", 14), e.tools.length > 1) {
888
+ if (a.className = "tcw-sidebar-btn", a.title = e.label, a.innerHTML = y(re[r] ?? "square", 14), e.tools.length > 1) {
603
889
  let e = document.createElement("span");
604
890
  e.className = "tcw-multi-dot", a.appendChild(e);
605
891
  }
606
892
  a.addEventListener("click", () => t.onDrawingTool(e.tools[0].value)), i.appendChild(a), i.addEventListener("mouseenter", () => this.showFlyout(r)), i.addEventListener("mouseleave", () => this.hideFlyout()), n.appendChild(i), this.groupWraps.push(i), this.groupButtons.push(a);
607
893
  });
608
894
  let r = document.createElement("div");
609
- r.className = "tcw-sidebar-spacer", n.appendChild(r), n.appendChild(this.divider()), this.magnetBtn = document.createElement("button"), this.magnetBtn.className = "tcw-sidebar-btn", this.magnetBtn.title = "Magnet", this.magnetBtn.innerHTML = g("magnet", 14), this.magnetBtn.addEventListener("click", t.onToggleMagnet), n.appendChild(this.magnetBtn);
895
+ if (r.className = "tcw-sidebar-spacer", n.appendChild(r), n.appendChild(this.divider()), t.onToggleStyle) {
896
+ let e = document.createElement("button");
897
+ e.className = "tcw-sidebar-btn", e.title = "Drawing style & templates", e.dataset.role = "style", e.innerHTML = y("palette", 14), e.addEventListener("click", t.onToggleStyle), n.appendChild(e);
898
+ }
899
+ this.magnetBtn = document.createElement("button"), this.magnetBtn.className = "tcw-sidebar-btn", this.magnetBtn.title = "Magnet", this.magnetBtn.innerHTML = y("magnet", 14), this.magnetBtn.addEventListener("click", t.onToggleMagnet), n.appendChild(this.magnetBtn);
610
900
  let i = document.createElement("button");
611
- i.className = "tcw-sidebar-btn", i.title = "Undo", i.innerHTML = g("undo", 14), i.addEventListener("click", t.onUndo), n.appendChild(i);
901
+ i.className = "tcw-sidebar-btn", i.title = "Undo", i.innerHTML = y("undo", 14), i.addEventListener("click", t.onUndo), n.appendChild(i);
612
902
  let a = document.createElement("button");
613
- a.className = "tcw-sidebar-btn", a.title = "Redo", a.innerHTML = g("redo", 14), a.addEventListener("click", t.onRedo), n.appendChild(a);
903
+ a.className = "tcw-sidebar-btn", a.title = "Redo", a.innerHTML = y("redo", 14), a.addEventListener("click", t.onRedo), n.appendChild(a);
614
904
  let o = document.createElement("button");
615
- o.className = "tcw-sidebar-btn tcw-danger", o.title = "Clear all", o.innerHTML = g("trash", 14), o.addEventListener("click", t.onClearDrawings), n.appendChild(o);
905
+ o.className = "tcw-sidebar-btn tcw-danger", o.title = "Clear all", o.innerHTML = y("trash", 14), o.addEventListener("click", t.onClearDrawings), n.appendChild(o);
906
+ }
907
+ setFavorites(e) {
908
+ this.favorites = [...e], this.renderFavorites();
909
+ }
910
+ renderFavorites() {
911
+ if (!this.favoritesEl) return;
912
+ this.favoritesEl.replaceChildren();
913
+ let e = this.favorites.length > 0;
914
+ this.favoritesEl.style.display = e ? "" : "none", this.favoritesDivider && (this.favoritesDivider.style.display = e ? "" : "none");
915
+ for (let e of this.favorites) {
916
+ let t = document.createElement("button");
917
+ t.className = "tcw-sidebar-btn", t.title = `${oe(this.config.drawingToolGroups, e)} (right-click to unpin)`, t.dataset.toolValue = e, t.innerHTML = y(ae(e), 14), t.addEventListener("click", () => this.callbacks.onDrawingTool(e)), t.addEventListener("contextmenu", (t) => {
918
+ t.preventDefault(), this.callbacks.onToggleFavorite?.(e);
919
+ }), this.favoritesEl.appendChild(t);
920
+ }
616
921
  }
617
922
  showFlyout(e) {
618
923
  let t = this.config.drawingToolGroups[e];
@@ -626,7 +931,9 @@ var _ = class {
626
931
  let t = document.createElement("button");
627
932
  t.className = "tcw-flyout-item", t.textContent = e.label, t.dataset.toolValue = e.value, t.addEventListener("click", () => {
628
933
  this.callbacks.onDrawingTool(e.value), this.hideFlyout();
629
- }), n.appendChild(t);
934
+ }), this.callbacks.onToggleFavorite && (this.favorites.includes(e.value) && t.classList.add("tcw-flyout-faved"), t.title = "Right-click to pin/unpin", t.addEventListener("contextmenu", (t) => {
935
+ t.preventDefault(), this.callbacks.onToggleFavorite?.(e.value);
936
+ })), n.appendChild(t);
630
937
  }
631
938
  this.flyoutEl = n, this.groupWraps[e].appendChild(n);
632
939
  }
@@ -652,11 +959,11 @@ var _ = class {
652
959
  destroy() {
653
960
  this.hideFlyout(), this.el.remove();
654
961
  }
655
- }, x = [
962
+ }, S = [
656
963
  "style",
657
964
  "display",
658
965
  "scale"
659
- ], S = class {
966
+ ], C = class {
660
967
  callbacks;
661
968
  backdrop = null;
662
969
  modal = null;
@@ -682,12 +989,12 @@ var _ = class {
682
989
  let t = document.createElement("h3");
683
990
  t.textContent = "Chart Settings", e.appendChild(t);
684
991
  let n = document.createElement("button");
685
- n.className = "tcw-modal-close", n.innerHTML = g("x", 16), n.addEventListener("click", () => {
992
+ n.className = "tcw-modal-close", n.innerHTML = y("x", 16), n.addEventListener("click", () => {
686
993
  this.callbacks.onClose(), this.close();
687
994
  }), e.appendChild(n), this.modal.appendChild(e);
688
995
  let r = document.createElement("div");
689
996
  r.className = "tcw-modal-tabs", this.tabButtons = [];
690
- for (let e of x) {
997
+ for (let e of S) {
691
998
  let t = document.createElement("button");
692
999
  t.className = "tcw-modal-tab", t.textContent = e, t.dataset.tab = e, e === this.currentTab && t.classList.add("tcw-active"), t.addEventListener("click", () => {
693
1000
  this.currentTab = e, this.tabButtons.forEach((t) => t.classList.toggle("tcw-active", t.dataset.tab === e)), this.renderTabContent();
@@ -730,7 +1037,13 @@ var _ = class {
730
1037
  renderDisplayTab() {
731
1038
  if (!this.bodyEl || !this.currentSettings) return;
732
1039
  let e = this.currentSettings, t = this.section();
733
- t.appendChild(this.toggleRow("Grid Lines", e.gridVisible, (e) => this.patch({ gridVisible: e }))), t.appendChild(this.toggleRow("Volume", e.volumeVisible, (e) => this.patch({ volumeVisible: 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, [
1040
+ t.appendChild(this.toggleRow("Grid Lines", e.gridVisible, (e) => this.patch({ gridVisible: e }))), t.appendChild(this.toggleRow("Volume", e.volumeVisible, (e) => this.patch({ volumeVisible: e }))), t.appendChild(this.toggleRow("Volume Profile", e.volumeProfileVisible, (e) => this.patch({ volumeProfileVisible: e }))), t.appendChild(this.toggleRow("Market Profile (TPO)", e.marketProfileVisible, (e) => this.patch({ marketProfileVisible: e }))), t.appendChild(this.toggleRow("MP split by session", e.marketProfileSplit, (e) => this.patch({ marketProfileSplit: e }))), t.appendChild(this.toggleRow("MP letters (TPO)", e.marketProfileLetters, (e) => this.patch({ marketProfileLetters: e }))), t.appendChild(this.rangeRow("MP buckets", e.marketProfileBuckets, 8, 200, 1, (e) => this.patch({ marketProfileBuckets: e }))), t.appendChild(this.rangeRow("MP opacity", e.marketProfileOpacity, .05, 1, .05, (e) => this.patch({ marketProfileOpacity: e }), (e) => `${Math.round(e * 100)}%`)), t.appendChild(this.toggleRow("Liquidity heatmap", e.depthHeatmapVisible, (e) => this.patch({ depthHeatmapVisible: e }))), t.appendChild(this.rangeRow("Heatmap opacity", e.depthHeatmapOpacity, .1, 1, .05, (e) => this.patch({ depthHeatmapOpacity: e }), (e) => `${Math.round(e * 100)}%`)), t.appendChild(this.toggleRow("Swing markers", e.pivotMarkersVisible, (e) => this.patch({ pivotMarkersVisible: e }))), t.appendChild(this.rangeRow("Swing strength", e.pivotStrength, 2, 20, 1, (e) => this.patch({ pivotStrength: e }))), t.appendChild(this.toggleRow("Swing structure (HH/HL)", e.pivotStructureLabels, (e) => this.patch({ pivotStructureLabels: e }))), t.appendChild(this.toggleRow("Session shading (RTH)", e.sessionShadingVisible, (e) => this.patch({ sessionShadingVisible: e }))), t.appendChild(this.toggleRow("Prior-period levels", e.periodLevelsVisible, (e) => this.patch({ periodLevelsVisible: e }))), t.appendChild(this.selectRow("Period levels basis", e.periodLevelsPeriod, [{
1041
+ value: "day",
1042
+ label: "Prior Day (PDH/PDL)"
1043
+ }, {
1044
+ value: "week",
1045
+ label: "Prior Week (PWH/PWL)"
1046
+ }], (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, [
734
1047
  {
735
1048
  label: "Magnet",
736
1049
  value: "magnet"
@@ -743,7 +1056,40 @@ var _ = class {
743
1056
  label: "Hidden",
744
1057
  value: "hidden"
745
1058
  }
746
- ], (e) => this.patch({ crosshairMode: e }))), t.appendChild(this.selectRow("Number Locale", e.numberLocale, [
1059
+ ], (e) => this.patch({ crosshairMode: e }))), t.appendChild(this.selectRow("Timezone", e.timezone, [
1060
+ {
1061
+ value: "local",
1062
+ label: "Local"
1063
+ },
1064
+ {
1065
+ value: "0",
1066
+ label: "UTC / London"
1067
+ },
1068
+ {
1069
+ value: "-300",
1070
+ label: "New York (UTC-5)"
1071
+ },
1072
+ {
1073
+ value: "-360",
1074
+ label: "Chicago (UTC-6)"
1075
+ },
1076
+ {
1077
+ value: "60",
1078
+ label: "Frankfurt (UTC+1)"
1079
+ },
1080
+ {
1081
+ value: "480",
1082
+ label: "Singapore/HK (UTC+8)"
1083
+ },
1084
+ {
1085
+ value: "540",
1086
+ label: "Tokyo (UTC+9)"
1087
+ },
1088
+ {
1089
+ value: "330",
1090
+ label: "Mumbai (UTC+5:30)"
1091
+ }
1092
+ ], (e) => this.patch({ timezone: e }))), t.appendChild(this.selectRow("Number Locale", e.numberLocale, [
747
1093
  {
748
1094
  label: "en-US (65,234.00)",
749
1095
  value: "en-US"
@@ -773,7 +1119,24 @@ var _ = class {
773
1119
  renderScaleTab() {
774
1120
  if (!this.bodyEl || !this.currentSettings) return;
775
1121
  let e = this.currentSettings, t = this.section();
776
- t.appendChild(this.toggleRow("Auto Scale", e.autoScale, (e) => this.patch({ autoScale: e }))), t.appendChild(this.toggleRow("Log Scale", e.logScale, (e) => this.patch({ logScale: e }))), this.bodyEl.appendChild(t);
1122
+ t.appendChild(this.toggleRow("Auto Scale", e.autoScale, (e) => this.patch({ autoScale: e }))), t.appendChild(this.selectRow("Price Scale", e.scaleMode, [
1123
+ {
1124
+ value: "regular",
1125
+ label: "Regular"
1126
+ },
1127
+ {
1128
+ value: "logarithmic",
1129
+ label: "Logarithmic"
1130
+ },
1131
+ {
1132
+ value: "percentage",
1133
+ label: "Percentage"
1134
+ },
1135
+ {
1136
+ value: "indexedTo100",
1137
+ label: "Indexed to 100"
1138
+ }
1139
+ ], (e) => this.patch({ scaleMode: e }))), this.bodyEl.appendChild(t);
777
1140
  }
778
1141
  patch(e) {
779
1142
  this.currentSettings && Object.assign(this.currentSettings, e), this.callbacks.onChange(e);
@@ -824,10 +1187,25 @@ var _ = class {
824
1187
  }
825
1188
  return o.addEventListener("change", () => r(o.value)), i.appendChild(o), i;
826
1189
  }
1190
+ rangeRow(e, t, n, r, i, a, o = (e) => String(e)) {
1191
+ let s = document.createElement("div");
1192
+ s.className = "tcw-settings-row";
1193
+ let c = document.createElement("span");
1194
+ c.className = "tcw-settings-label", c.textContent = e, s.appendChild(c);
1195
+ let l = document.createElement("div");
1196
+ l.className = "tcw-settings-range";
1197
+ let u = document.createElement("input");
1198
+ u.type = "range", u.min = String(n), u.max = String(r), u.step = String(i), u.value = String(t);
1199
+ let d = document.createElement("span");
1200
+ return d.className = "tcw-settings-range-val", d.textContent = o(t), u.addEventListener("input", () => {
1201
+ let e = Number(u.value);
1202
+ d.textContent = o(e), a(e);
1203
+ }), l.appendChild(u), l.appendChild(d), s.appendChild(l), s;
1204
+ }
827
1205
  destroy() {
828
1206
  this.close();
829
1207
  }
830
- }, C = class {
1208
+ }, w = class {
831
1209
  el;
832
1210
  dotEl;
833
1211
  messageEl;
@@ -843,7 +1221,7 @@ var _ = class {
843
1221
  destroy() {
844
1222
  this.el.remove();
845
1223
  }
846
- }, w = class {
1224
+ }, T = class {
847
1225
  backdrop = null;
848
1226
  modal = null;
849
1227
  input = null;
@@ -968,145 +1346,1909 @@ var _ = class {
968
1346
  destroy() {
969
1347
  this.close();
970
1348
  }
971
- }, T = class {
972
- chart;
973
- state;
974
- toolbar = null;
975
- sidebar = null;
976
- settings = null;
977
- statusBar = null;
978
- commandPalette = null;
979
- root;
980
- chartContainer;
981
- destroyed = !1;
982
- options;
983
- symbolIndex = 0;
984
- symbols;
985
- settingsState;
986
- adapter = null;
987
- boundGlobalKeydown = null;
988
- constructor(t, n = {}) {
989
- this.options = n, this.symbols = n.symbols ?? c, this.settingsState = { ...l };
990
- let u = this.resolveIsDark(n.theme), d = this.resolveTheme(n.theme);
991
- this.state = {
992
- symbol: n.symbol ?? this.symbols[0] ?? "BTCUSDT",
993
- timeframe: n.timeframe ?? "5m",
994
- chartType: n.chartOptions?.chartType ?? "candlestick",
995
- isDark: u,
996
- activeIndicators: /* @__PURE__ */ new Map(),
997
- activeTool: null,
998
- magnetEnabled: !0,
999
- connectionState: "connecting",
1000
- connectionMessage: "Connecting..."
1001
- };
1002
- let f = this.symbols.indexOf(this.state.symbol);
1003
- f >= 0 && (this.symbolIndex = f), p(), this.root = document.createElement("div"), this.root.className = "tcw-root", this.root.dataset.tcwTheme = u ? "dark" : "light", t.appendChild(this.root), n.toolbar !== !1 && (this.toolbar = new v(this.root, {
1004
- symbols: this.symbols,
1005
- timeframes: n.timeframes ? r.filter((e) => n.timeframes.includes(e.value)) : r,
1006
- chartTypes: n.chartTypes ? i.filter((e) => n.chartTypes.includes(e.value)) : i,
1007
- indicators: o,
1008
- popularIndicatorIds: s
1009
- }, {
1010
- onSymbolClick: () => this.handleSymbolClick(),
1011
- onTimeframe: (e) => this.handleTimeframe(e),
1012
- onChartType: (e) => this.handleChartType(e),
1013
- onAddIndicator: (e) => this.handleAddIndicator(e),
1014
- onRemoveIndicator: (e) => this.handleRemoveIndicator(e),
1015
- onScreenshot: () => this.chart.screenshot(),
1016
- onSettings: () => this.openSettings(),
1017
- onToggleTheme: () => this.handleToggleTheme()
1018
- }));
1019
- let m = document.createElement("div");
1020
- m.className = "tcw-body", n.drawingTools !== !1 && (this.sidebar = new b(m, { drawingToolGroups: a }, {
1021
- onDrawingTool: (e) => this.handleDrawingTool(e),
1022
- onCancelDrawing: () => this.handleCancelDrawing(),
1023
- onToggleMagnet: () => this.handleToggleMagnet(),
1024
- onUndo: () => this.chart.undo(),
1025
- onRedo: () => this.chart.redo(),
1026
- onClearDrawings: () => {
1027
- this.chart.clearDrawings(), this.state = {
1028
- ...this.state,
1029
- activeTool: null
1030
- }, this.updateUI();
1031
- }
1032
- })), this.chartContainer = document.createElement("div"), this.chartContainer.className = "tcw-chart-container", m.appendChild(this.chartContainer), this.root.appendChild(m), this.chart = new e(this.chartContainer, {
1033
- chartType: this.state.chartType,
1034
- theme: d,
1035
- autoScale: !0,
1036
- crosshair: { mode: "magnet" },
1037
- features: {
1038
- drawings: !0,
1039
- drawingMagnet: !0,
1040
- drawingUndoRedo: !0,
1041
- indicators: !0,
1042
- trading: n.trading !== !1,
1043
- tradingContextMenu: n.trading !== !1,
1044
- volume: !0,
1045
- legend: !0,
1046
- crosshair: !0,
1047
- keyboard: !0,
1048
- screenshot: !0,
1049
- alerts: !0,
1050
- barCountdown: !0,
1051
- logScale: !0,
1052
- watermark: !0
1053
- },
1054
- ...n.chartOptions
1055
- }), n.statusBar !== !1 && (this.statusBar = new C(this.root)), n.settings !== !1 && (this.settings = new S({
1056
- onChange: (e) => this.applySettings(e),
1057
- onReset: () => this.resetSettings(),
1058
- onClose: () => {}
1059
- })), this.commandPalette = new w({
1060
- onIndicator: (e) => this.handleAddIndicator(e),
1061
- onChartType: (e) => this.handleChartType(e),
1062
- onDrawingTool: (e) => this.handleDrawingTool(e),
1063
- onTimeframe: (e) => this.handleTimeframe(e),
1064
- onAction: (e) => this.handleAction(e),
1065
- onClose: () => {}
1066
- }), this.boundGlobalKeydown = (e) => {
1067
- (e.ctrlKey || e.metaKey) && e.key === "k" && (e.preventDefault(), this.toggleCommandPalette());
1068
- }, document.addEventListener("keydown", this.boundGlobalKeydown), n.adapter && (this.adapter = n.adapter, this.connectStream()), this.updateUI(), n.onReady?.(this.chart);
1069
- }
1070
- async setSymbol(e) {
1071
- this.state = {
1072
- ...this.state,
1073
- symbol: e
1074
- };
1075
- let t = this.symbols.indexOf(e);
1076
- t >= 0 && (this.symbolIndex = t), this.options.onSymbolChange?.(e), this.updateUI(), this.adapter && await this.connectStream();
1349
+ }, E = class {
1350
+ backdrop = null;
1351
+ modal = null;
1352
+ input = null;
1353
+ list = null;
1354
+ symbols = [];
1355
+ current = "";
1356
+ filtered = [];
1357
+ selectedIndex = 0;
1358
+ callbacks;
1359
+ boundKeydown;
1360
+ pickOverride = null;
1361
+ constructor(e) {
1362
+ this.callbacks = e, this.boundKeydown = this.handleKeydown.bind(this);
1077
1363
  }
1078
- async setTimeframe(e) {
1079
- this.state = {
1080
- ...this.state,
1081
- timeframe: e
1082
- }, this.options.onTimeframeChange?.(e), this.updateUI(), this.adapter && await this.connectStream();
1364
+ open(e, t, n) {
1365
+ if (this.backdrop) return;
1366
+ this.pickOverride = n ?? null, this.symbols = e, this.current = t, this.filtered = e.map((e) => ({
1367
+ symbol: e,
1368
+ score: 0
1369
+ })), this.selectedIndex = Math.max(0, e.indexOf(t)), this.backdrop = document.createElement("div"), this.backdrop.className = "tcw-modal-backdrop", this.backdrop.addEventListener("click", () => this.close()), this.modal = document.createElement("div"), this.modal.className = "tcw-cmd-palette";
1370
+ let r = document.createElement("div");
1371
+ r.className = "tcw-cmd-header";
1372
+ let i = document.createElement("span");
1373
+ i.className = "tcw-cmd-icon", i.innerHTML = A(), 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);
1374
+ let a = document.createElement("span");
1375
+ 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);
1376
+ let o = document.createElement("div");
1377
+ o.className = "tcw-cmd-footer", o.innerHTML = "<span>↑↓ Navigate</span><span>⏎ Open</span><span>Esc Close</span>", this.modal.appendChild(o), document.body.appendChild(this.backdrop), document.body.appendChild(this.modal), this.renderList(), this.input.focus(), document.addEventListener("keydown", this.boundKeydown);
1083
1378
  }
1084
- setTheme(e) {
1085
- let t = this.resolveIsDark(e), n = this.resolveTheme(e);
1086
- this.state = {
1087
- ...this.state,
1088
- isDark: t
1089
- }, this.root.dataset.tcwTheme = t ? "dark" : "light", this.chart.setTheme(n), this.updateUI();
1379
+ close() {
1380
+ 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();
1090
1381
  }
1091
- getChart() {
1092
- return this.chart;
1382
+ isOpen() {
1383
+ return this.backdrop !== null;
1093
1384
  }
1094
1385
  destroy() {
1095
- this.destroyed || (this.destroyed = !0, this.boundGlobalKeydown && document.removeEventListener("keydown", this.boundGlobalKeydown), this.commandPalette?.destroy(), this.toolbar?.destroy(), this.sidebar?.destroy(), this.settings?.destroy(), this.statusBar?.destroy(), this.chart.destroy(), this.root.remove(), m());
1386
+ this.close();
1387
+ }
1388
+ filter() {
1389
+ let e = (this.input?.value ?? "").toUpperCase().trim();
1390
+ e ? this.filtered = this.symbols.map((t) => ({
1391
+ symbol: t,
1392
+ score: D(t.toUpperCase(), e)
1393
+ })).filter((e) => e.score > 0).sort((e, t) => t.score - e.score) : this.filtered = this.symbols.map((e) => ({
1394
+ symbol: e,
1395
+ score: 0
1396
+ })), this.selectedIndex = 0, this.renderList();
1397
+ }
1398
+ renderList() {
1399
+ if (!this.list) return;
1400
+ if (this.list.innerHTML = "", this.filtered.length === 0) {
1401
+ let e = document.createElement("div");
1402
+ e.className = "tcw-cmd-empty", e.textContent = "No symbols match", this.list.appendChild(e);
1403
+ return;
1404
+ }
1405
+ let e = (this.input?.value ?? "").toUpperCase().trim();
1406
+ for (let t = 0; t < this.filtered.length; t++) {
1407
+ let { symbol: n } = this.filtered[t], r = document.createElement("button");
1408
+ r.className = "tcw-cmd-item", t === this.selectedIndex && r.classList.add("tcw-selected"), n === this.current && r.classList.add("tcw-active");
1409
+ let i = document.createElement("span");
1410
+ if (i.className = "tcw-cmd-item-label", i.innerHTML = O(n, e), r.appendChild(i), n === this.current) {
1411
+ let e = document.createElement("span");
1412
+ e.className = "tcw-cmd-check", e.textContent = "✓", r.appendChild(e);
1413
+ }
1414
+ r.addEventListener("click", () => this.pick(t)), r.addEventListener("mouseenter", () => {
1415
+ this.selectedIndex = t, this.updateSelection();
1416
+ }), this.list.appendChild(r);
1417
+ }
1418
+ }
1419
+ updateSelection() {
1420
+ if (!this.list) return;
1421
+ let e = this.list.querySelectorAll(".tcw-cmd-item");
1422
+ e.forEach((e, t) => e.classList.toggle("tcw-selected", t === this.selectedIndex)), e[this.selectedIndex]?.scrollIntoView({ block: "nearest" });
1423
+ }
1424
+ pick(e) {
1425
+ let t = this.filtered[e];
1426
+ if (!t) return;
1427
+ let n = this.pickOverride;
1428
+ n ? n(t.symbol) : this.callbacks.onPick(t.symbol), this.close();
1429
+ }
1430
+ handleKeydown(e) {
1431
+ switch (e.key) {
1432
+ case "Escape":
1433
+ e.preventDefault(), this.close();
1434
+ break;
1435
+ case "ArrowDown":
1436
+ e.preventDefault(), this.filtered.length > 0 && (this.selectedIndex = (this.selectedIndex + 1) % this.filtered.length, this.updateSelection());
1437
+ break;
1438
+ case "ArrowUp":
1439
+ e.preventDefault(), this.filtered.length > 0 && (this.selectedIndex = (this.selectedIndex - 1 + this.filtered.length) % this.filtered.length, this.updateSelection());
1440
+ break;
1441
+ case "Enter":
1442
+ e.preventDefault(), this.pick(this.selectedIndex);
1443
+ break;
1444
+ }
1445
+ }
1446
+ };
1447
+ function D(e, t) {
1448
+ if (e === t) return 1e3;
1449
+ if (e.startsWith(t)) return 500 + (50 - Math.min(50, e.length - t.length));
1450
+ if (e.includes(t)) return 200 + (50 - Math.min(50, e.length - t.length));
1451
+ let n = 0, r = -1, i = 0;
1452
+ for (let a = 0; a < e.length && n < t.length; a++) e[a] === t[n] && (r >= 0 && (i += a - r - 1), r = a, n++);
1453
+ return n < t.length ? 0 : 100 - Math.min(99, i);
1454
+ }
1455
+ function O(e, t) {
1456
+ if (!t) return k(e);
1457
+ let n = e.toUpperCase(), r = "", i = 0;
1458
+ 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">${k(e[a])}</mark>`, i++) : r += k(e[a]);
1459
+ return r;
1460
+ }
1461
+ function k(e) {
1462
+ return e.replace(/[&<>"']/g, (e) => ({
1463
+ "&": "&amp;",
1464
+ "<": "&lt;",
1465
+ ">": "&gt;",
1466
+ "\"": "&quot;",
1467
+ "'": "&#39;"
1468
+ })[e]);
1469
+ }
1470
+ function A() {
1471
+ 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>";
1472
+ }
1473
+ //#endregion
1474
+ //#region src/widget/WidgetHotkeySheet.ts
1475
+ var j = typeof navigator < "u" && /mac|iphone|ipad|ipod/i.test(navigator.platform || navigator.userAgent) ? "⌘" : "Ctrl", M = [
1476
+ {
1477
+ title: "Search & navigation",
1478
+ entries: [
1479
+ {
1480
+ keys: [j, "K"],
1481
+ label: "Command palette"
1482
+ },
1483
+ {
1484
+ keys: [j, "P"],
1485
+ label: "Symbol search"
1486
+ },
1487
+ {
1488
+ keys: ["?"],
1489
+ label: "Show this sheet"
1490
+ }
1491
+ ]
1492
+ },
1493
+ {
1494
+ title: "Chart manipulation",
1495
+ entries: [
1496
+ {
1497
+ keys: ["Shift", "Drag"],
1498
+ label: "Measure tool (bars × price Δ)"
1499
+ },
1500
+ {
1501
+ keys: ["Drag", "Price axis"],
1502
+ label: "Compress / expand vertical scale"
1503
+ },
1504
+ {
1505
+ keys: ["Drag", "Time axis"],
1506
+ label: "Zoom time axis"
1507
+ },
1508
+ {
1509
+ keys: ["Double-click", "Price axis"],
1510
+ label: "Re-enable auto-scale"
1511
+ },
1512
+ {
1513
+ keys: ["Double-click", "Time axis"],
1514
+ label: "Fit content"
1515
+ },
1516
+ {
1517
+ keys: ["Scroll"],
1518
+ label: "Zoom around cursor"
1519
+ }
1520
+ ]
1521
+ },
1522
+ {
1523
+ title: "Touch (mobile / tablet)",
1524
+ entries: [
1525
+ {
1526
+ keys: ["1 finger", "Drag"],
1527
+ label: "Pan / move crosshair"
1528
+ },
1529
+ {
1530
+ keys: ["2 fingers", "Pinch"],
1531
+ label: "Zoom around midpoint"
1532
+ },
1533
+ {
1534
+ keys: ["Long-press"],
1535
+ label: "Pin OHLC tooltip at bar"
1536
+ },
1537
+ {
1538
+ keys: ["Drag", "Axis strip"],
1539
+ label: "Scale price / time axis"
1540
+ }
1541
+ ]
1542
+ },
1543
+ {
1544
+ title: "Keyboard",
1545
+ entries: [
1546
+ {
1547
+ keys: ["←", "→"],
1548
+ label: "Pan one bar"
1549
+ },
1550
+ {
1551
+ keys: ["+", "−"],
1552
+ label: "Zoom in / out"
1553
+ },
1554
+ {
1555
+ keys: ["Home"],
1556
+ label: "Scroll to start"
1557
+ },
1558
+ {
1559
+ keys: ["End"],
1560
+ label: "Scroll to end (live edge)"
1561
+ },
1562
+ {
1563
+ keys: ["F"],
1564
+ label: "Fit content"
1565
+ }
1566
+ ]
1567
+ },
1568
+ {
1569
+ title: "Drawing",
1570
+ entries: [
1571
+ {
1572
+ keys: [j, "Z"],
1573
+ label: "Undo"
1574
+ },
1575
+ {
1576
+ keys: [
1577
+ j,
1578
+ "Shift",
1579
+ "Z"
1580
+ ],
1581
+ label: "Redo"
1582
+ },
1583
+ {
1584
+ keys: ["Esc"],
1585
+ label: "Cancel active drawing"
1586
+ }
1587
+ ]
1588
+ }
1589
+ ], N = class {
1590
+ backdrop = null;
1591
+ modal = null;
1592
+ callbacks;
1593
+ boundKeydown;
1594
+ constructor(e) {
1595
+ this.callbacks = e, this.boundKeydown = (e) => {
1596
+ e.key === "Escape" && (e.preventDefault(), this.close());
1597
+ };
1598
+ }
1599
+ open() {
1600
+ if (this.backdrop) return;
1601
+ this.backdrop = document.createElement("div"), this.backdrop.className = "tcw-modal-backdrop", this.backdrop.addEventListener("click", () => this.close()), this.modal = document.createElement("div"), this.modal.className = "tcw-modal tcw-hotkey-sheet", this.modal.style.width = "640px";
1602
+ let e = document.createElement("div");
1603
+ e.className = "tcw-modal-header", e.innerHTML = "<h3>Keyboard shortcuts</h3>";
1604
+ let t = document.createElement("button");
1605
+ t.className = "tcw-modal-close", t.setAttribute("aria-label", "Close"), t.innerHTML = "×", t.addEventListener("click", () => this.close()), e.appendChild(t), this.modal.appendChild(e);
1606
+ let n = document.createElement("div");
1607
+ n.className = "tcw-modal-body";
1608
+ let r = document.createElement("div");
1609
+ r.className = "tcw-hotkey-grid";
1610
+ for (let e of M) {
1611
+ let t = document.createElement("section");
1612
+ t.className = "tcw-hotkey-section";
1613
+ let n = document.createElement("div");
1614
+ n.className = "tcw-settings-section-title", n.textContent = e.title, t.appendChild(n);
1615
+ for (let n of e.entries) {
1616
+ let e = document.createElement("div");
1617
+ e.className = "tcw-hotkey-row";
1618
+ let r = document.createElement("div");
1619
+ r.className = "tcw-hotkey-keys";
1620
+ for (let e = 0; e < n.keys.length; e++) {
1621
+ if (e > 0) {
1622
+ let e = document.createElement("span");
1623
+ e.className = "tcw-hotkey-sep", e.textContent = "+", r.appendChild(e);
1624
+ }
1625
+ let t = document.createElement("kbd");
1626
+ t.className = "tcw-cmd-kbd", t.textContent = n.keys[e], r.appendChild(t);
1627
+ }
1628
+ e.appendChild(r);
1629
+ let i = document.createElement("span");
1630
+ i.className = "tcw-hotkey-label", i.textContent = n.label, e.appendChild(i), t.appendChild(e);
1631
+ }
1632
+ r.appendChild(t);
1633
+ }
1634
+ n.appendChild(r), this.modal.appendChild(n);
1635
+ let i = document.createElement("div");
1636
+ i.className = "tcw-modal-footer", i.innerHTML = "<span style=\"font-size:11px;color:var(--tcw-text-muted)\">Press ? anytime to show this sheet</span>";
1637
+ let a = document.createElement("button");
1638
+ a.className = "tcw-done-btn", a.textContent = "Got it", a.addEventListener("click", () => this.close()), i.appendChild(a), this.modal.appendChild(i), document.body.appendChild(this.backdrop), document.body.appendChild(this.modal), document.addEventListener("keydown", this.boundKeydown);
1639
+ }
1640
+ close() {
1641
+ document.removeEventListener("keydown", this.boundKeydown), this.backdrop?.remove(), this.modal?.remove(), this.backdrop = null, this.modal = null, this.callbacks.onClose();
1642
+ }
1643
+ isOpen() {
1644
+ return this.backdrop !== null;
1645
+ }
1646
+ destroy() {
1647
+ this.close();
1648
+ }
1649
+ }, P = [
1650
+ .5,
1651
+ 1,
1652
+ 2,
1653
+ 5,
1654
+ 10,
1655
+ 20,
1656
+ 50,
1657
+ 100
1658
+ ], F = class {
1659
+ root = null;
1660
+ scrubber = null;
1661
+ progressLabel = null;
1662
+ playBtn = null;
1663
+ state = "stopped";
1664
+ total = 0;
1665
+ speed = 5;
1666
+ callbacks;
1667
+ constructor(e) {
1668
+ this.callbacks = e;
1669
+ }
1670
+ mount(e, t) {
1671
+ 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());
1672
+ }
1673
+ unmount() {
1674
+ this.root?.remove(), this.root = null, this.scrubber = null, this.progressLabel = null, this.playBtn = null, this.state = "stopped";
1675
+ }
1676
+ isMounted() {
1677
+ return this.root !== null;
1678
+ }
1679
+ setState(e) {
1680
+ this.state = e, this.syncUi();
1681
+ }
1682
+ setProgress(e, t) {
1683
+ 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}`);
1684
+ }
1685
+ destroy() {
1686
+ this.unmount();
1687
+ }
1688
+ markup() {
1689
+ let e = P.map((e) => `<option value="${e}"${e === this.speed ? " selected" : ""}>${e}× bars/s</option>`).join("");
1690
+ return `
1691
+ <button class="tcw-replay-btn" data-act="close" title="Exit replay" aria-label="Exit replay">${B()}</button>
1692
+ <button class="tcw-replay-btn" data-act="stepBack" title="Step back" aria-label="Step back">${R()}</button>
1693
+ <button class="tcw-replay-btn tcw-replay-play" data-act="play" title="Play / pause" aria-label="Play / pause">${I()}</button>
1694
+ <button class="tcw-replay-btn" data-act="stepForward" title="Step forward" aria-label="Step forward">${z()}</button>
1695
+ <input type="range" class="tcw-replay-scrubber" data-act="seek" min="0" max="${Math.max(0, this.total - 1)}" value="0" step="1" />
1696
+ <span class="tcw-replay-progress" data-tcw="progress">1 / ${this.total}</span>
1697
+ <select class="tcw-replay-speed" data-act="speed">${e}</select>
1698
+ `;
1699
+ }
1700
+ wireEvents() {
1701
+ this.root && (this.scrubber = this.root.querySelector("input[data-act=\"seek\"]"), this.progressLabel = this.root.querySelector("[data-tcw=\"progress\"]"), this.playBtn = this.root.querySelector("button[data-act=\"play\"]"), this.root.querySelector("[data-act=\"close\"]")?.addEventListener("click", () => this.callbacks.onClose()), this.root.querySelector("[data-act=\"stepBack\"]")?.addEventListener("click", () => this.callbacks.onStepBack()), this.root.querySelector("[data-act=\"stepForward\"]")?.addEventListener("click", () => this.callbacks.onStepForward()), this.playBtn?.addEventListener("click", () => {
1702
+ this.state === "playing" ? this.callbacks.onPause() : this.callbacks.onPlay();
1703
+ }), this.scrubber?.addEventListener("input", () => {
1704
+ let e = Number(this.scrubber.value);
1705
+ this.callbacks.onSeek(e);
1706
+ }), this.root.querySelector("[data-act=\"speed\"]")?.addEventListener("change", (e) => {
1707
+ let t = Number(e.target.value);
1708
+ this.speed = t, this.callbacks.onSpeedChange(t);
1709
+ }));
1710
+ }
1711
+ syncUi() {
1712
+ this.playBtn && (this.playBtn.innerHTML = this.state === "playing" ? L() : I(), this.playBtn.title = this.state === "playing" ? "Pause" : "Play");
1713
+ }
1714
+ };
1715
+ function I() {
1716
+ return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 5v14l11-7L8 5z\"/></svg>";
1717
+ }
1718
+ function L() {
1719
+ return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/></svg>";
1720
+ }
1721
+ function R() {
1722
+ return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M6 6h2v12H6zM20 6L9 12l11 6V6z\"/></svg>";
1723
+ }
1724
+ function z() {
1725
+ return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M4 6l11 6L4 18V6zM16 6h2v12h-2z\"/></svg>";
1726
+ }
1727
+ function B() {
1728
+ return "<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6L6 18M6 6l12 12\"/></svg>";
1729
+ }
1730
+ //#endregion
1731
+ //#region src/widget/WidgetWatchlist.ts
1732
+ var V = class {
1733
+ el;
1734
+ listEl;
1735
+ rowMap = /* @__PURE__ */ new Map();
1736
+ entries = /* @__PURE__ */ new Map();
1737
+ active = null;
1738
+ callbacks;
1739
+ constructor(e, t, n) {
1740
+ this.callbacks = n, this.el = document.createElement("div"), this.el.className = "tcw-watchlist";
1741
+ let r = document.createElement("div");
1742
+ r.className = "tcw-watchlist-header", r.textContent = "Watchlist", this.el.appendChild(r), this.listEl = document.createElement("div"), this.listEl.className = "tcw-watchlist-list", this.el.appendChild(this.listEl);
1743
+ for (let e of t) this.entries.set(e, { symbol: e }), this.appendRow(e);
1744
+ e.appendChild(this.el);
1745
+ }
1746
+ setSymbols(e) {
1747
+ let t = new Set(e);
1748
+ for (let e of this.rowMap.keys()) t.has(e) || this.removeRow(e);
1749
+ for (let t of e) this.rowMap.has(t) || (this.entries.set(t, this.entries.get(t) ?? { symbol: t }), this.appendRow(t));
1750
+ }
1751
+ setActive(e) {
1752
+ if (this.active === e) return;
1753
+ this.active && this.rowMap.has(this.active) && this.rowMap.get(this.active).row.classList.remove("tcw-watchlist-active"), this.active = e;
1754
+ let t = this.rowMap.get(e);
1755
+ t && t.row.classList.add("tcw-watchlist-active");
1756
+ }
1757
+ setEntry(e, t) {
1758
+ let n = {
1759
+ ...this.entries.get(e) ?? { symbol: e },
1760
+ ...t
1761
+ };
1762
+ this.entries.set(e, n), this.renderRow(e);
1763
+ }
1764
+ destroy() {
1765
+ this.el.remove(), this.rowMap.clear(), this.entries.clear();
1766
+ }
1767
+ appendRow(e) {
1768
+ let t = document.createElement("div");
1769
+ t.className = "tcw-watchlist-row", t.tabIndex = 0;
1770
+ let n = document.createElement("div");
1771
+ n.className = "tcw-watchlist-symbol", n.textContent = H(e);
1772
+ let r = document.createElement("canvas");
1773
+ r.className = "tcw-watchlist-spark", r.width = 64, r.height = 22;
1774
+ let i = document.createElement("div");
1775
+ i.className = "tcw-watchlist-stats";
1776
+ let a = document.createElement("span");
1777
+ a.className = "tcw-watchlist-price", a.textContent = "—";
1778
+ let o = document.createElement("span");
1779
+ o.className = "tcw-watchlist-change", o.textContent = "", i.appendChild(a), i.appendChild(o), t.appendChild(n), t.appendChild(r), t.appendChild(i), t.addEventListener("click", () => this.callbacks.onSelect(e)), t.addEventListener("keydown", (t) => {
1780
+ (t.key === "Enter" || t.key === " ") && (t.preventDefault(), this.callbacks.onSelect(e));
1781
+ }), this.listEl.appendChild(t), this.rowMap.set(e, {
1782
+ row: t,
1783
+ priceEl: a,
1784
+ changeEl: o,
1785
+ sparkCanvas: r
1786
+ }), e === this.active && t.classList.add("tcw-watchlist-active");
1787
+ }
1788
+ removeRow(e) {
1789
+ let t = this.rowMap.get(e);
1790
+ t && (t.row.remove(), this.rowMap.delete(e));
1791
+ }
1792
+ renderRow(e) {
1793
+ let t = this.rowMap.get(e), n = this.entries.get(e);
1794
+ if (!(!t || !n)) {
1795
+ if (n.lastPrice !== void 0 && (t.priceEl.textContent = U(n.lastPrice)), n.lastPrice !== void 0 && n.refPrice !== void 0 && n.refPrice !== 0) {
1796
+ let e = n.lastPrice - n.refPrice, r = e / n.refPrice * 100, i = e >= 0 ? "+" : "";
1797
+ t.changeEl.textContent = `${i}${r.toFixed(2)}%`, t.changeEl.className = `tcw-watchlist-change ${e >= 0 ? "tcw-up" : "tcw-down"}`;
1798
+ } else t.changeEl.textContent = "";
1799
+ n.sparkline && n.sparkline.length >= 2 && W(t.sparkCanvas, n.sparkline, n.refPrice ?? n.sparkline[0]);
1800
+ }
1801
+ }
1802
+ };
1803
+ function H(e) {
1804
+ for (let t of [
1805
+ "USDT",
1806
+ "USD",
1807
+ "BTC",
1808
+ "ETH",
1809
+ "EUR"
1810
+ ]) if (e.length > t.length && e.endsWith(t)) return e.slice(0, -t.length) + "/" + t;
1811
+ return e;
1812
+ }
1813
+ function U(e) {
1814
+ if (e === 0) return "0";
1815
+ let t = Math.abs(e);
1816
+ return t >= 1e3 ? e.toFixed(2) : t >= 1 ? e.toFixed(4) : e.toPrecision(4);
1817
+ }
1818
+ function W(e, t, n) {
1819
+ let r = e.getContext("2d");
1820
+ if (!r) return;
1821
+ let i = window.devicePixelRatio || 1;
1822
+ e.width !== e.clientWidth * i && (e.width = (e.clientWidth || 64) * i, e.height = (e.clientHeight || 22) * i);
1823
+ let a = e.width, o = e.height, s = Infinity, c = -Infinity;
1824
+ for (let e of t) e < s && (s = e), e > c && (c = e);
1825
+ let l = Math.max(c - s, 1e-9), u = t[t.length - 1] >= n ? "#26a17b" : "#f23645";
1826
+ r.clearRect(0, 0, a, o), r.lineWidth = 1.4 * i, r.strokeStyle = u, r.beginPath();
1827
+ let d = a / (t.length - 1);
1828
+ for (let e = 0; e < t.length; e++) {
1829
+ let n = e * d, i = o - (t[e] - s) / l * (o - 4) - 2;
1830
+ e === 0 ? r.moveTo(n, i) : r.lineTo(n, i);
1831
+ }
1832
+ r.stroke();
1833
+ }
1834
+ //#endregion
1835
+ //#region src/widget/WidgetAlertsPanel.ts
1836
+ var G = [
1837
+ {
1838
+ value: "crossing",
1839
+ label: "Crossing"
1840
+ },
1841
+ {
1842
+ value: "crossingUp",
1843
+ label: "Crossing up"
1844
+ },
1845
+ {
1846
+ value: "crossingDown",
1847
+ label: "Crossing down"
1848
+ },
1849
+ {
1850
+ value: "greaterThan",
1851
+ label: "Greater than"
1852
+ },
1853
+ {
1854
+ value: "lessThan",
1855
+ label: "Less than"
1856
+ }
1857
+ ], K = new Map(G.map((e) => [e.value, e.label]));
1858
+ function q(e) {
1859
+ return Math.round(e * 1e6) / 1e6;
1860
+ }
1861
+ function se(e) {
1862
+ let t = Math.abs(e);
1863
+ return t >= 1e3 || t >= 1 ? e.toFixed(2) : e.toPrecision(4);
1864
+ }
1865
+ var ce = class {
1866
+ el;
1867
+ listEl;
1868
+ priceInput;
1869
+ conditionSelect;
1870
+ sourceSelect;
1871
+ messageInput;
1872
+ emptyEl;
1873
+ callbacks;
1874
+ open = !1;
1875
+ alerts = [];
1876
+ sources = [{
1877
+ channel: "price",
1878
+ label: "Price"
1879
+ }];
1880
+ constructor(e, t) {
1881
+ this.callbacks = t, this.el = document.createElement("div"), this.el.className = "tcw-alerts-panel", this.el.hidden = !0;
1882
+ let n = document.createElement("div");
1883
+ n.className = "tcw-alerts-header";
1884
+ let r = document.createElement("span");
1885
+ r.textContent = "Price Alerts";
1886
+ let i = document.createElement("button");
1887
+ i.className = "tcw-alerts-close", i.type = "button", i.setAttribute("aria-label", "Close alerts"), i.innerHTML = y("x", 14), i.addEventListener("click", () => this.close()), n.appendChild(r), n.appendChild(i), this.el.appendChild(n);
1888
+ let a = document.createElement("form");
1889
+ 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";
1890
+ for (let e of G) {
1891
+ let t = document.createElement("option");
1892
+ t.value = e.value, t.textContent = e.label, this.conditionSelect.appendChild(t);
1893
+ }
1894
+ this.messageInput = document.createElement("input"), this.messageInput.type = "text", this.messageInput.placeholder = "Note (optional)", this.messageInput.className = "tcw-alerts-message";
1895
+ let o = document.createElement("button");
1896
+ o.type = "submit", o.className = "tcw-alerts-add", o.innerHTML = `${y("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) => {
1897
+ e.preventDefault(), this.submitForm();
1898
+ }), 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);
1899
+ }
1900
+ setSources(e) {
1901
+ this.sources = e.length > 0 ? e : [{
1902
+ channel: "price",
1903
+ label: "Price"
1904
+ }], this.renderSources();
1905
+ }
1906
+ renderSources() {
1907
+ let e = this.sourceSelect.value || "price";
1908
+ this.sourceSelect.replaceChildren();
1909
+ for (let e of this.sources) {
1910
+ let t = document.createElement("option");
1911
+ t.value = e.channel, t.textContent = e.label, this.sourceSelect.appendChild(t);
1912
+ }
1913
+ this.sourceSelect.value = this.sources.some((t) => t.channel === e) ? e : "price";
1914
+ }
1915
+ isOpen() {
1916
+ return this.open;
1917
+ }
1918
+ toggle() {
1919
+ this.open ? this.close() : this.openPanel();
1920
+ }
1921
+ openPanel() {
1922
+ this.open = !0, this.el.hidden = !1, this.priceInput.value === "" && this.prefillPrice();
1923
+ }
1924
+ prefillPrice() {
1925
+ let e = this.callbacks.getChannelValue(this.sourceSelect.value || "price");
1926
+ this.priceInput.value = e === null ? "" : String(q(e));
1927
+ }
1928
+ close() {
1929
+ this.open = !1, this.el.hidden = !0;
1930
+ }
1931
+ setAlerts(e) {
1932
+ this.alerts = e, this.renderList();
1933
+ }
1934
+ destroy() {
1935
+ this.el.remove();
1936
+ }
1937
+ submitForm() {
1938
+ let e = Number(this.priceInput.value);
1939
+ if (!Number.isFinite(e)) return;
1940
+ let t = this.conditionSelect.value, n = this.messageInput.value.trim() || void 0, r = this.sourceSelect.value || "price", i = this.sources.find((e) => e.channel === r)?.label ?? "Price";
1941
+ this.callbacks.onAdd(e, t, n, r, i), this.messageInput.value = "", this.prefillPrice();
1942
+ }
1943
+ renderList() {
1944
+ if (this.listEl.replaceChildren(), this.alerts.length === 0) {
1945
+ this.listEl.appendChild(this.emptyEl);
1946
+ return;
1947
+ }
1948
+ for (let e of [...this.alerts].reverse()) {
1949
+ let t = document.createElement("div");
1950
+ t.className = "tcw-alerts-row" + (e.triggered ? " tcw-alerts-triggered" : "");
1951
+ let n = document.createElement("div");
1952
+ n.className = "tcw-alerts-info";
1953
+ let r = document.createElement("div");
1954
+ r.className = "tcw-alerts-row-main";
1955
+ let i = K.get(e.condition) ?? e.condition, a = e.channel && e.channel !== "price", o = a ? se(e.price) : this.callbacks.formatPrice(e.price);
1956
+ if (r.textContent = `${a && e.label ? `${e.label} ` : ""}${i} ${o}`, n.appendChild(r), e.message) {
1957
+ let t = document.createElement("div");
1958
+ t.className = "tcw-alerts-row-note", t.textContent = e.message, n.appendChild(t);
1959
+ }
1960
+ if (e.triggered) {
1961
+ let e = document.createElement("span");
1962
+ e.className = "tcw-alerts-badge", e.textContent = "triggered", r.appendChild(e);
1963
+ }
1964
+ let s = document.createElement("button");
1965
+ s.type = "button", s.className = "tcw-alerts-del", s.setAttribute("aria-label", "Delete alert"), s.innerHTML = y("trash", 14), s.addEventListener("click", () => this.callbacks.onRemove(e.id)), t.appendChild(n), t.appendChild(s), this.listEl.appendChild(t);
1966
+ }
1967
+ }
1968
+ }, le = class {
1969
+ el;
1970
+ indicatorsEl;
1971
+ drawingsEl;
1972
+ compareEl = null;
1973
+ callbacks;
1974
+ open = !1;
1975
+ constructor(e, t) {
1976
+ this.callbacks = t, this.el = document.createElement("div"), this.el.className = "tcw-tree-panel", this.el.hidden = !0;
1977
+ let n = document.createElement("div");
1978
+ n.className = "tcw-tree-header";
1979
+ let r = document.createElement("span");
1980
+ r.textContent = "Objects";
1981
+ let i = document.createElement("button");
1982
+ i.type = "button", i.className = "tcw-tree-close", i.setAttribute("aria-label", "Close objects"), i.innerHTML = y("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);
1983
+ }
1984
+ isOpen() {
1985
+ return this.open;
1986
+ }
1987
+ toggle() {
1988
+ this.open ? this.close() : this.openPanel();
1989
+ }
1990
+ openPanel() {
1991
+ this.open = !0, this.el.hidden = !1;
1992
+ }
1993
+ close() {
1994
+ this.open = !1, this.el.hidden = !0;
1995
+ }
1996
+ destroy() {
1997
+ this.el.remove();
1998
+ }
1999
+ setObjects(e, t, n = []) {
2000
+ this.renderIndicators(e), this.renderDrawings(t), this.renderCompares(n);
2001
+ }
2002
+ makeSection(e, t) {
2003
+ let n = document.createElement("div");
2004
+ n.className = "tcw-tree-section";
2005
+ let r = document.createElement("div");
2006
+ r.className = "tcw-tree-section-head";
2007
+ let i = document.createElement("span");
2008
+ if (i.textContent = e, r.appendChild(i), t) {
2009
+ let n = document.createElement("button");
2010
+ n.type = "button", n.className = "tcw-tree-add", n.setAttribute("aria-label", `Add ${e.toLowerCase()}`), n.title = `Add ${e.toLowerCase()}`, n.innerHTML = y("plus", 13), n.addEventListener("click", t), r.appendChild(n);
2011
+ }
2012
+ let a = document.createElement("div");
2013
+ return a.className = "tcw-tree-list", n.appendChild(r), n.appendChild(a), this.el.appendChild(n), a;
2014
+ }
2015
+ renderIndicators(e) {
2016
+ if (this.indicatorsEl.replaceChildren(), e.length === 0) {
2017
+ this.indicatorsEl.appendChild(this.emptyRow("No indicators"));
2018
+ return;
2019
+ }
2020
+ for (let t of e) {
2021
+ let e = this.row(t.name);
2022
+ t.visible || e.classList.add("tcw-tree-hidden");
2023
+ let n = document.createElement("div");
2024
+ n.className = "tcw-tree-actions", this.callbacks.onToggleIndicatorVisible && n.appendChild(this.iconButton(t.visible ? "eye" : "eyeOff", t.visible ? "Hide" : "Show", "", () => this.callbacks.onToggleIndicatorVisible?.(t.instanceId, !t.visible))), this.callbacks.onConfigureIndicator && n.appendChild(this.iconButton("settings", "Indicator settings", "", () => this.callbacks.onConfigureIndicator?.(t.instanceId))), n.appendChild(this.iconButton("trash", "Remove indicator", "tcw-tree-del", () => this.callbacks.onRemoveIndicator(t.instanceId))), e.appendChild(n), this.indicatorsEl.appendChild(e);
2025
+ }
2026
+ }
2027
+ renderDrawings(e) {
2028
+ if (this.drawingsEl.replaceChildren(), e.length === 0) {
2029
+ this.drawingsEl.appendChild(this.emptyRow("No drawings"));
2030
+ return;
2031
+ }
2032
+ for (let t of e) {
2033
+ let e = this.row(t.label);
2034
+ t.visible || e.classList.add("tcw-tree-hidden");
2035
+ let n = document.createElement("div");
2036
+ n.className = "tcw-tree-actions", n.appendChild(this.iconButton(t.visible ? "eye" : "eyeOff", t.visible ? "Hide" : "Show", "", () => this.callbacks.onToggleDrawingVisible(t.id, !t.visible))), n.appendChild(this.iconButton(t.locked ? "lock" : "unlock", t.locked ? "Unlock" : "Lock", t.locked ? "tcw-tree-on" : "", () => this.callbacks.onToggleDrawingLocked(t.id, !t.locked))), n.appendChild(this.iconButton("trash", "Remove drawing", "tcw-tree-del", () => this.callbacks.onRemoveDrawing(t.id))), e.appendChild(n), this.drawingsEl.appendChild(e);
2037
+ }
2038
+ }
2039
+ renderCompares(e) {
2040
+ if (this.compareEl) {
2041
+ if (this.compareEl.replaceChildren(), e.length === 0) {
2042
+ this.compareEl.appendChild(this.emptyRow("No comparisons"));
2043
+ return;
2044
+ }
2045
+ for (let t of e) {
2046
+ let e = this.row(t.label), n = document.createElement("span");
2047
+ n.className = "tcw-tree-dot", n.style.background = t.color, e.insertBefore(n, e.firstChild), this.callbacks.onRemoveCompare && e.appendChild(this.iconButton("trash", "Remove comparison", "tcw-tree-del", () => this.callbacks.onRemoveCompare?.(t.id))), this.compareEl.appendChild(e);
2048
+ }
2049
+ }
2050
+ }
2051
+ row(e) {
2052
+ let t = document.createElement("div");
2053
+ t.className = "tcw-tree-row";
2054
+ let n = document.createElement("span");
2055
+ return n.className = "tcw-tree-name", n.textContent = e, n.title = e, t.appendChild(n), t;
2056
+ }
2057
+ emptyRow(e) {
2058
+ let t = document.createElement("div");
2059
+ return t.className = "tcw-tree-empty", t.textContent = e, t;
2060
+ }
2061
+ iconButton(e, t, n, r) {
2062
+ let i = document.createElement("button");
2063
+ return i.type = "button", i.className = `tcw-tree-btn ${n}`.trim(), i.setAttribute("aria-label", t), i.title = t, i.innerHTML = y(e, 14), i.addEventListener("click", r), i;
2064
+ }
2065
+ }, ue = {
2066
+ trendLine: "Trend Line",
2067
+ ray: "Ray",
2068
+ extendedLine: "Extended Line",
2069
+ horizontalLine: "Horizontal Line",
2070
+ verticalLine: "Vertical Line",
2071
+ rectangle: "Rectangle",
2072
+ ellipse: "Ellipse",
2073
+ triangle: "Triangle",
2074
+ arrow: "Arrow",
2075
+ parallelChannel: "Parallel Channel",
2076
+ regressionChannel: "Regression Channel",
2077
+ pitchfork: "Pitchfork",
2078
+ fibRetracement: "Fib Retracement",
2079
+ fibExtension: "Fib Extension",
2080
+ fibTimeZones: "Fib Time Zones",
2081
+ gannBox: "Gann Box",
2082
+ gannFan: "Gann Fan",
2083
+ elliottWave: "Elliott Wave",
2084
+ priceRange: "Price Range",
2085
+ dateRange: "Date Range",
2086
+ measure: "Measure",
2087
+ textAnnotation: "Text",
2088
+ anchoredVWAP: "Anchored VWAP",
2089
+ volumeProfileRange: "Volume Profile"
2090
+ };
2091
+ function de(e) {
2092
+ return ue[e] ?? e;
2093
+ }
2094
+ //#endregion
2095
+ //#region src/widget/WidgetIndicatorSettings.ts
2096
+ var fe = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
2097
+ function pe(e) {
2098
+ return e.replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/^./, (e) => e.toUpperCase());
2099
+ }
2100
+ var me = class {
2101
+ backdrop;
2102
+ modal;
2103
+ bodyEl;
2104
+ titleEl;
2105
+ callbacks;
2106
+ target = null;
2107
+ draft = {};
2108
+ constructor(e, t) {
2109
+ this.callbacks = t, this.backdrop = document.createElement("div"), this.backdrop.className = "tcw-modal-backdrop", this.backdrop.hidden = !0, this.backdrop.addEventListener("click", (e) => {
2110
+ e.target === this.backdrop && this.close();
2111
+ }), this.modal = document.createElement("div"), this.modal.className = "tcw-modal tcw-modal-narrow";
2112
+ let n = document.createElement("div");
2113
+ n.className = "tcw-modal-header", this.titleEl = document.createElement("h3"), this.titleEl.textContent = "Indicator";
2114
+ let r = document.createElement("button");
2115
+ r.className = "tcw-modal-close", r.setAttribute("aria-label", "Close"), r.innerHTML = y("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";
2116
+ let i = document.createElement("div");
2117
+ i.className = "tcw-modal-footer";
2118
+ let a = document.createElement("button");
2119
+ a.className = "tcw-reset-link", a.textContent = "Reset to defaults", a.addEventListener("click", () => this.resetDefaults());
2120
+ let o = document.createElement("button");
2121
+ o.className = "tcw-done-btn", o.textContent = "Done", o.addEventListener("click", () => this.close()), i.appendChild(a), i.appendChild(o), this.modal.appendChild(n), this.modal.appendChild(this.bodyEl), this.modal.appendChild(i), this.backdrop.appendChild(this.modal), e.appendChild(this.backdrop);
2122
+ }
2123
+ open(e) {
2124
+ this.target = e, this.titleEl.textContent = `${e.name} settings`, this.draft = {}, this.renderFields(), this.backdrop.hidden = !1;
2125
+ }
2126
+ close() {
2127
+ this.backdrop.hidden = !0, this.target = null, this.callbacks.onClose();
2128
+ }
2129
+ isOpen() {
2130
+ return !this.backdrop.hidden;
2131
+ }
2132
+ destroy() {
2133
+ this.backdrop.remove();
2134
+ }
2135
+ currentValue(e) {
2136
+ if (e in this.draft) return this.draft[e];
2137
+ let t = this.target?.params[e], n = this.target?.defaults[e];
2138
+ return t ?? n;
2139
+ }
2140
+ setValue(e, t) {
2141
+ this.target && (this.draft[e] = t, this.callbacks.onApply(this.target.instanceId, { ...this.draft }));
2142
+ }
2143
+ resetDefaults() {
2144
+ if (!this.target) return;
2145
+ let e = {};
2146
+ for (let [t, n] of Object.entries(this.target.defaults)) e[t] = n;
2147
+ this.draft = e, this.callbacks.onApply(this.target.instanceId, { ...e }), this.renderFields();
2148
+ }
2149
+ renderFields() {
2150
+ if (this.bodyEl.replaceChildren(), !this.target) return;
2151
+ let e = document.createElement("div");
2152
+ e.className = "tcw-settings-section";
2153
+ let t = Object.keys(this.target.defaults);
2154
+ if (t.length === 0) {
2155
+ let t = document.createElement("div");
2156
+ t.className = "tcw-settings-label", t.textContent = "This indicator has no adjustable parameters.", e.appendChild(t);
2157
+ }
2158
+ for (let n of t) e.appendChild(this.fieldRow(n));
2159
+ this.bodyEl.appendChild(e);
2160
+ }
2161
+ fieldRow(e) {
2162
+ let t = document.createElement("div");
2163
+ t.className = "tcw-settings-row";
2164
+ let n = document.createElement("span");
2165
+ n.className = "tcw-settings-label", n.textContent = pe(e), t.appendChild(n);
2166
+ let r = this.currentValue(e), i = typeof (this.target?.defaults[e] ?? r);
2167
+ return i === "boolean" ? t.appendChild(this.toggleControl(e, !!r)) : i === "number" ? t.appendChild(this.numberControl(e, Number(r))) : typeof r == "string" && fe.test(r) ? t.appendChild(this.colorControl(e, r)) : t.appendChild(this.textControl(e, String(r ?? ""))), t;
2168
+ }
2169
+ toggleControl(e, t) {
2170
+ let n = document.createElement("button");
2171
+ return n.className = `tcw-toggle${t ? " tcw-on" : ""}`, n.type = "button", n.addEventListener("click", () => {
2172
+ let t = !n.classList.contains("tcw-on");
2173
+ n.classList.toggle("tcw-on", t), this.setValue(e, t);
2174
+ }), n;
2175
+ }
2176
+ numberControl(e, t) {
2177
+ let n = document.createElement("input");
2178
+ return n.type = "number", n.className = "tcw-indi-input", n.value = String(t), n.step = Number.isInteger(t) ? "1" : "0.1", n.addEventListener("change", () => {
2179
+ let t = Number(n.value);
2180
+ Number.isFinite(t) && this.setValue(e, t);
2181
+ }), n;
2182
+ }
2183
+ colorControl(e, t) {
2184
+ let n = document.createElement("input");
2185
+ 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;
2186
+ }
2187
+ textControl(e, t) {
2188
+ let n = document.createElement("input");
2189
+ return n.type = "text", n.className = "tcw-indi-input", n.value = t, n.addEventListener("change", () => this.setValue(e, n.value)), n;
2190
+ }
2191
+ };
2192
+ function he(e) {
2193
+ let t = e[1], n = e[2], r = e[3];
2194
+ return `#${t}${t}${n}${n}${r}${r}`;
2195
+ }
2196
+ //#endregion
2197
+ //#region src/widget/DrawingTemplateStore.ts
2198
+ function ge() {
2199
+ try {
2200
+ return typeof localStorage < "u" ? localStorage : null;
2201
+ } catch {
2202
+ return null;
2203
+ }
2204
+ }
2205
+ var _e = [
2206
+ "color",
2207
+ "lineWidth",
2208
+ "lineStyle",
2209
+ "fillColor",
2210
+ "fillOpacity",
2211
+ "fontSize"
2212
+ ];
2213
+ function J(e) {
2214
+ if (!e || typeof e != "object") return {};
2215
+ let t = {}, n = e;
2216
+ for (let e of _e) n[e] !== void 0 && (t[e] = n[e]);
2217
+ return t;
2218
+ }
2219
+ var ve = class {
2220
+ templates = [];
2221
+ constructor(e = "tcw:draw-templates", t = ge()) {
2222
+ this.storageKey = e, this.storage = t, this.load();
2223
+ }
2224
+ list() {
2225
+ return this.templates.map((e) => ({
2226
+ name: e.name,
2227
+ style: { ...e.style }
2228
+ }));
2229
+ }
2230
+ get(e) {
2231
+ let t = this.templates.find((t) => t.name === e);
2232
+ return t ? {
2233
+ name: t.name,
2234
+ style: { ...t.style }
2235
+ } : null;
2236
+ }
2237
+ save(e, t) {
2238
+ let n = e.trim();
2239
+ if (!n) return;
2240
+ let r = J(t);
2241
+ this.templates = this.templates.filter((e) => e.name !== n), this.templates.push({
2242
+ name: n,
2243
+ style: r
2244
+ }), this.persist();
2245
+ }
2246
+ remove(e) {
2247
+ let t = this.templates.filter((t) => t.name !== e);
2248
+ t.length !== this.templates.length && (this.templates = t, this.persist());
2249
+ }
2250
+ clear() {
2251
+ this.templates = [], this.persist();
2252
+ }
2253
+ load() {
2254
+ if (this.storage) try {
2255
+ let e = this.storage.getItem(this.storageKey);
2256
+ if (!e) return;
2257
+ let t = JSON.parse(e);
2258
+ if (!Array.isArray(t)) return;
2259
+ this.templates = t.filter((e) => e && typeof e.name == "string").map((e) => ({
2260
+ name: e.name,
2261
+ style: J(e.style)
2262
+ }));
2263
+ } catch {}
2264
+ }
2265
+ persist() {
2266
+ if (this.storage) try {
2267
+ this.storage.setItem(this.storageKey, JSON.stringify(this.templates));
2268
+ } catch {}
2269
+ }
2270
+ }, ye = [
2271
+ "#2962ff",
2272
+ "#089981",
2273
+ "#f23645",
2274
+ "#ff9800",
2275
+ "#ab47bc",
2276
+ "#26c6da",
2277
+ "#ffffff",
2278
+ "#787b86"
2279
+ ], be = [
2280
+ 1,
2281
+ 2,
2282
+ 3,
2283
+ 4
2284
+ ], xe = [
2285
+ "solid",
2286
+ "dashed",
2287
+ "dotted"
2288
+ ], Se = class {
2289
+ el;
2290
+ templatesEl;
2291
+ nameInput;
2292
+ callbacks;
2293
+ store;
2294
+ open = !1;
2295
+ constructor(e, t, n) {
2296
+ this.callbacks = t, this.store = n, this.el = document.createElement("div"), this.el.className = "tcw-style-panel", this.el.hidden = !0;
2297
+ let r = document.createElement("div");
2298
+ r.className = "tcw-style-header";
2299
+ let i = document.createElement("span");
2300
+ i.textContent = "Drawing Style";
2301
+ let a = document.createElement("button");
2302
+ a.type = "button", a.className = "tcw-style-close", a.setAttribute("aria-label", "Close"), a.innerHTML = y("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());
2303
+ let o = document.createElement("div");
2304
+ o.className = "tcw-style-subhead", o.textContent = "Templates", this.el.appendChild(o);
2305
+ let s = document.createElement("div");
2306
+ 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";
2307
+ let c = document.createElement("button");
2308
+ c.type = "button", c.className = "tcw-style-savebtn", c.innerHTML = `${y("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();
2309
+ }
2310
+ isOpen() {
2311
+ return this.open;
2312
+ }
2313
+ toggle() {
2314
+ this.open ? this.close() : this.openPanel();
2315
+ }
2316
+ openPanel() {
2317
+ this.open = !0, this.el.hidden = !1, this.syncActiveControls(), this.renderTemplates();
2318
+ }
2319
+ close() {
2320
+ this.open = !1, this.el.hidden = !0;
2321
+ }
2322
+ destroy() {
2323
+ this.el.remove();
2324
+ }
2325
+ colorRow() {
2326
+ let e = document.createElement("div");
2327
+ e.className = "tcw-style-row tcw-style-colors";
2328
+ for (let t of ye) {
2329
+ let n = document.createElement("button");
2330
+ 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);
2331
+ }
2332
+ let t = document.createElement("input");
2333
+ return t.type = "color", t.className = "tcw-style-picker", t.addEventListener("input", () => this.applyStyle({ color: t.value })), e.appendChild(t), e;
2334
+ }
2335
+ widthRow() {
2336
+ let e = document.createElement("div");
2337
+ e.className = "tcw-style-row tcw-style-widths";
2338
+ for (let t of be) {
2339
+ let n = document.createElement("button");
2340
+ n.type = "button", n.className = "tcw-style-width", n.dataset.width = String(t);
2341
+ let r = document.createElement("span");
2342
+ r.style.height = `${t}px`, n.appendChild(r), n.addEventListener("click", () => this.applyStyle({ lineWidth: t })), e.appendChild(n);
2343
+ }
2344
+ return e;
2345
+ }
2346
+ lineStyleRow() {
2347
+ let e = document.createElement("div");
2348
+ e.className = "tcw-style-row tcw-style-dashes";
2349
+ for (let t of xe) {
2350
+ let n = document.createElement("button");
2351
+ 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);
2352
+ }
2353
+ return e;
2354
+ }
2355
+ applyStyle(e) {
2356
+ this.callbacks.onStyleChange(e), this.syncActiveControls();
2357
+ }
2358
+ syncActiveControls() {
2359
+ let e = this.callbacks.getStyle();
2360
+ this.el.querySelectorAll(".tcw-style-swatch").forEach((t) => {
2361
+ t.classList.toggle("tcw-on", t.dataset.color?.toLowerCase() === e.color?.toLowerCase());
2362
+ }), this.el.querySelectorAll(".tcw-style-width").forEach((t) => {
2363
+ t.classList.toggle("tcw-on", Number(t.dataset.width) === e.lineWidth);
2364
+ }), this.el.querySelectorAll(".tcw-style-dash").forEach((t) => {
2365
+ t.classList.toggle("tcw-on", t.dataset.linestyle === e.lineStyle);
2366
+ });
2367
+ }
2368
+ saveTemplate() {
2369
+ let e = this.nameInput.value.trim();
2370
+ e && (this.store.save(e, this.callbacks.getStyle()), this.nameInput.value = "", this.renderTemplates());
2371
+ }
2372
+ renderTemplates() {
2373
+ this.templatesEl.replaceChildren();
2374
+ let e = this.store.list();
2375
+ if (e.length === 0) {
2376
+ let e = document.createElement("div");
2377
+ e.className = "tcw-style-empty", e.textContent = "No saved templates", this.templatesEl.appendChild(e);
2378
+ return;
2379
+ }
2380
+ for (let t of e) {
2381
+ let e = document.createElement("div");
2382
+ e.className = "tcw-style-template";
2383
+ let n = document.createElement("button");
2384
+ n.type = "button", n.className = "tcw-style-apply";
2385
+ let r = document.createElement("span");
2386
+ r.className = "tcw-style-tdot", r.style.background = t.style.color ?? "var(--tcw-text-muted)";
2387
+ let i = document.createElement("span");
2388
+ i.textContent = t.name, n.appendChild(r), n.appendChild(i), n.addEventListener("click", () => {
2389
+ this.callbacks.onStyleChange({ ...t.style }), this.syncActiveControls();
2390
+ });
2391
+ let a = document.createElement("button");
2392
+ a.type = "button", a.className = "tcw-style-tdel", a.setAttribute("aria-label", "Delete template"), a.innerHTML = y("trash", 13), a.addEventListener("click", () => {
2393
+ this.store.remove(t.name), this.renderTemplates();
2394
+ }), e.appendChild(n), e.appendChild(a), this.templatesEl.appendChild(e);
2395
+ }
2396
+ }
2397
+ };
2398
+ //#endregion
2399
+ //#region src/widget/DrawingFavoritesStore.ts
2400
+ function Ce() {
2401
+ try {
2402
+ return typeof localStorage < "u" ? localStorage : null;
2403
+ } catch {
2404
+ return null;
2405
+ }
2406
+ }
2407
+ var we = class {
2408
+ favorites = [];
2409
+ constructor(e = "tcw:draw-favorites", t = Ce()) {
2410
+ this.storageKey = e, this.storage = t, this.load();
2411
+ }
2412
+ list() {
2413
+ return [...this.favorites];
2414
+ }
2415
+ has(e) {
2416
+ return this.favorites.includes(e);
2417
+ }
2418
+ add(e) {
2419
+ !e || this.favorites.includes(e) || (this.favorites = [...this.favorites, e], this.persist());
2420
+ }
2421
+ remove(e) {
2422
+ this.favorites.includes(e) && (this.favorites = this.favorites.filter((t) => t !== e), this.persist());
2423
+ }
2424
+ toggle(e) {
2425
+ return this.has(e) ? (this.remove(e), !1) : (this.add(e), !0);
2426
+ }
2427
+ seedDefaults(e) {
2428
+ this.favorites.length > 0 || (this.favorites = [...new Set(e.filter(Boolean))], this.persist());
2429
+ }
2430
+ load() {
2431
+ if (this.storage) try {
2432
+ let e = this.storage.getItem(this.storageKey);
2433
+ if (!e) return;
2434
+ let t = JSON.parse(e);
2435
+ Array.isArray(t) && (this.favorites = [...new Set(t.filter((e) => typeof e == "string"))]);
2436
+ } catch {}
2437
+ }
2438
+ persist() {
2439
+ if (this.storage) try {
2440
+ this.storage.setItem(this.storageKey, JSON.stringify(this.favorites));
2441
+ } catch {}
2442
+ }
2443
+ }, Te = class {
2444
+ el;
2445
+ labelEl;
2446
+ callbacks;
2447
+ visible = !1;
2448
+ constructor(e, t) {
2449
+ this.callbacks = t, this.el = document.createElement("div"), this.el.className = "tcw-bracket-bar", this.el.hidden = !0, this.labelEl = document.createElement("span"), this.labelEl.className = "tcw-bracket-label", this.el.appendChild(this.labelEl);
2450
+ let n = document.createElement("span");
2451
+ n.className = "tcw-bracket-hint", n.textContent = "drag lines to adjust", this.el.appendChild(n);
2452
+ let r = document.createElement("button");
2453
+ r.type = "button", r.className = "tcw-bracket-cancel", r.innerHTML = `${y("x", 14)}<span>Cancel</span>`, r.addEventListener("click", () => this.callbacks.onCancel());
2454
+ let i = document.createElement("button");
2455
+ i.type = "button", i.className = "tcw-bracket-confirm", i.innerHTML = `${y("check", 14)}<span>Place</span>`, i.addEventListener("click", () => this.callbacks.onConfirm()), this.el.appendChild(r), this.el.appendChild(i), e.appendChild(this.el);
2456
+ }
2457
+ show(e) {
2458
+ this.visible = !0, this.labelEl.textContent = e === "buy" ? "Long bracket" : "Short bracket", this.labelEl.dataset.side = e, this.el.hidden = !1;
2459
+ }
2460
+ hide() {
2461
+ this.visible = !1, this.el.hidden = !0;
2462
+ }
2463
+ isVisible() {
2464
+ return this.visible;
2465
+ }
2466
+ destroy() {
2467
+ this.el.remove();
2468
+ }
2469
+ }, Ee = class {
2470
+ options;
2471
+ audioCtx = null;
2472
+ audioEl = null;
2473
+ permissionAsked = !1;
2474
+ constructor(e = {}) {
2475
+ if (this.options = e, typeof e.sound == "string") try {
2476
+ this.audioEl = new Audio(e.sound), this.audioEl.preload = "auto";
2477
+ } catch {
2478
+ this.audioEl = null;
2479
+ }
2480
+ e.desktop && this.ensurePermission();
2481
+ }
2482
+ setOptions(e) {
2483
+ this.options = {
2484
+ ...this.options,
2485
+ ...e
2486
+ };
2487
+ }
2488
+ notify(e) {
2489
+ this.options.sound && this.playSound(), this.options.desktop && this.showDesktop(e);
2490
+ }
2491
+ playSound() {
2492
+ if (this.audioEl) {
2493
+ this.audioEl.currentTime = 0, this.audioEl.play().catch(() => {});
2494
+ return;
2495
+ }
2496
+ this.beep();
2497
+ }
2498
+ beep() {
2499
+ try {
2500
+ let e = window.AudioContext ?? window.webkitAudioContext;
2501
+ if (!e) return;
2502
+ this.audioCtx ||= new e();
2503
+ let t = this.audioCtx, n = t.currentTime, r = t.createGain();
2504
+ r.gain.setValueAtTime(1e-4, n), r.gain.exponentialRampToValueAtTime(.2, n + .01), r.gain.exponentialRampToValueAtTime(1e-4, n + .35), r.connect(t.destination);
2505
+ let i = t.createOscillator();
2506
+ i.type = "sine", i.frequency.setValueAtTime(880, n), i.frequency.setValueAtTime(1320, n + .16), i.connect(r), i.start(n), i.stop(n + .36);
2507
+ } catch {}
2508
+ }
2509
+ ensurePermission() {
2510
+ this.permissionAsked || (this.permissionAsked = !0, !(typeof Notification > "u") && Notification.permission === "default" && Notification.requestPermission().catch(() => {}));
2511
+ }
2512
+ showDesktop(e) {
2513
+ if (!(typeof Notification > "u")) {
2514
+ if (Notification.permission !== "granted") {
2515
+ this.ensurePermission();
2516
+ return;
2517
+ }
2518
+ try {
2519
+ new Notification(this.options.title ?? "Price alert", { body: e });
2520
+ } catch {}
2521
+ }
1096
2522
  }
1097
- handleSymbolClick() {
1098
- this.symbolIndex = (this.symbolIndex + 1) % this.symbols.length;
1099
- let e = this.symbols[this.symbolIndex];
2523
+ destroy() {
2524
+ try {
2525
+ this.audioCtx?.close();
2526
+ } catch {}
2527
+ this.audioCtx = null, this.audioEl = null;
2528
+ }
2529
+ };
2530
+ //#endregion
2531
+ //#region src/widget/ladderRows.ts
2532
+ function De(e, t = 12) {
2533
+ 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) => {
2534
+ let t = s.get(e);
2535
+ return t || (t = {
2536
+ price: e,
2537
+ bidVolume: 0,
2538
+ askVolume: 0
2539
+ }, s.set(e, t)), t;
2540
+ };
2541
+ for (let e of n) c(e.price).askVolume += e.volume;
2542
+ for (let e of r) c(e.price).bidVolume += e.volume;
2543
+ let l = [...s.values()].sort((e, t) => t.price - e.price), u = 0;
2544
+ for (let e of l) u = Math.max(u, e.bidVolume, e.askVolume);
2545
+ return {
2546
+ rows: l,
2547
+ maxVolume: u,
2548
+ mid: o
2549
+ };
2550
+ }
2551
+ //#endregion
2552
+ //#region src/widget/WidgetDepthLadder.ts
2553
+ var Oe = class {
2554
+ el;
2555
+ bodyEl;
2556
+ callbacks;
2557
+ open = !1;
2558
+ levels;
2559
+ constructor(e, t, n = 12) {
2560
+ this.callbacks = t, this.levels = n, this.el = document.createElement("div"), this.el.className = "tcw-ladder", this.el.hidden = !0;
2561
+ let r = document.createElement("div");
2562
+ r.className = "tcw-ladder-header";
2563
+ let i = document.createElement("span");
2564
+ i.textContent = "Depth Ladder";
2565
+ let a = document.createElement("button");
2566
+ a.type = "button", a.className = "tcw-ladder-close", a.setAttribute("aria-label", "Close"), a.innerHTML = y("x", 14), a.addEventListener("click", () => this.close()), r.appendChild(i), r.appendChild(a), this.el.appendChild(r);
2567
+ let o = document.createElement("div");
2568
+ 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);
2569
+ }
2570
+ isOpen() {
2571
+ return this.open;
2572
+ }
2573
+ toggle() {
2574
+ this.open ? this.close() : this.openPanel();
2575
+ }
2576
+ openPanel() {
2577
+ this.open = !0, this.el.hidden = !1;
2578
+ }
2579
+ close() {
2580
+ this.open = !1, this.el.hidden = !0;
2581
+ }
2582
+ setData(e) {
2583
+ if (!e) {
2584
+ this.renderEmpty();
2585
+ return;
2586
+ }
2587
+ this.render(De(e, this.levels));
2588
+ }
2589
+ destroy() {
2590
+ this.el.remove();
2591
+ }
2592
+ renderEmpty() {
2593
+ this.bodyEl.replaceChildren();
2594
+ let e = document.createElement("div");
2595
+ e.className = "tcw-ladder-empty", e.textContent = "No order-book data", this.bodyEl.appendChild(e);
2596
+ }
2597
+ render(e) {
2598
+ if (this.bodyEl.replaceChildren(), e.rows.length === 0) {
2599
+ this.renderEmpty();
2600
+ return;
2601
+ }
2602
+ let t = e.maxVolume > 0 ? 1 / e.maxVolume : 0;
2603
+ for (let n of e.rows) {
2604
+ let e = n.askVolume > 0, r = document.createElement("div");
2605
+ r.className = "tcw-ladder-row";
2606
+ let i = document.createElement("button");
2607
+ i.type = "button", i.className = "tcw-ladder-cell tcw-ladder-bid", n.bidVolume > 0 ? (i.textContent = Y(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;
2608
+ let a = document.createElement("div");
2609
+ a.className = `tcw-ladder-price ${e ? "tcw-ask" : "tcw-bid"}`, a.textContent = this.callbacks.formatPrice(n.price);
2610
+ let o = document.createElement("button");
2611
+ o.type = "button", o.className = "tcw-ladder-cell tcw-ladder-ask", n.askVolume > 0 ? (o.textContent = Y(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);
2612
+ }
2613
+ }
2614
+ };
2615
+ function Y(e) {
2616
+ return e >= 1e3 ? (e / 1e3).toFixed(1) + "k" : e >= 1 ? e.toFixed(2).replace(/\.00$/, "") : e.toPrecision(2);
2617
+ }
2618
+ //#endregion
2619
+ //#region src/widget/WidgetDataWindow.ts
2620
+ var ke = class {
2621
+ el;
2622
+ bodyEl;
2623
+ callbacks;
2624
+ open = !1;
2625
+ constructor(e, t) {
2626
+ this.callbacks = t, this.el = document.createElement("div"), this.el.className = "tcw-datawin", this.el.hidden = !0;
2627
+ let n = document.createElement("div");
2628
+ n.className = "tcw-datawin-header";
2629
+ let r = document.createElement("span");
2630
+ r.textContent = "Data Window";
2631
+ let i = document.createElement("button");
2632
+ i.type = "button", i.className = "tcw-datawin-close", i.setAttribute("aria-label", "Close"), i.innerHTML = y("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);
2633
+ }
2634
+ isOpen() {
2635
+ return this.open;
2636
+ }
2637
+ toggle() {
2638
+ this.open ? this.close() : this.openPanel();
2639
+ }
2640
+ openPanel() {
2641
+ this.open = !0, this.el.hidden = !1;
2642
+ }
2643
+ close() {
2644
+ this.open = !1, this.el.hidden = !0;
2645
+ }
2646
+ destroy() {
2647
+ this.el.remove();
2648
+ }
2649
+ render(e) {
2650
+ if (!this.open) return;
2651
+ if (this.bodyEl.replaceChildren(), !e.ohlc) {
2652
+ let e = document.createElement("div");
2653
+ e.className = "tcw-datawin-empty", e.textContent = "Hover the chart", this.bodyEl.appendChild(e);
2654
+ return;
2655
+ }
2656
+ let t = this.callbacks.formatPrice, n = e.ohlc;
2657
+ 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)));
2658
+ let r = e.change > 0 ? "up" : e.change < 0 ? "down" : "", i = e.change > 0 ? "+" : "";
2659
+ this.bodyEl.appendChild(this.row("Change", `${i}${t(e.change)} (${i}${e.changePct.toFixed(2)}%)`, r)), this.bodyEl.appendChild(this.row("Volume", X(n.volume)));
2660
+ for (let t of e.indicators) {
2661
+ let e = document.createElement("div");
2662
+ e.className = "tcw-datawin-sub", e.textContent = t.name, this.bodyEl.appendChild(e);
2663
+ for (let e of t.values) this.bodyEl.appendChild(this.row(e.key, Ae(e.value)));
2664
+ }
2665
+ }
2666
+ row(e, t, n = "") {
2667
+ let r = document.createElement("div");
2668
+ r.className = "tcw-datawin-row";
2669
+ let i = document.createElement("span");
2670
+ i.className = "tcw-datawin-label", i.textContent = e;
2671
+ let a = document.createElement("span");
2672
+ return a.className = `tcw-datawin-value${n ? ` tcw-${n}` : ""}`, a.textContent = t, r.appendChild(i), r.appendChild(a), r;
2673
+ }
2674
+ };
2675
+ function Ae(e) {
2676
+ if (!Number.isFinite(e)) return "—";
2677
+ let t = Math.abs(e);
2678
+ return t >= 1e3 ? e.toFixed(2) : t >= 1 ? e.toFixed(4) : e.toPrecision(4);
2679
+ }
2680
+ function X(e) {
2681
+ 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);
2682
+ }
2683
+ //#endregion
2684
+ //#region src/widget/widgetShareState.ts
2685
+ var Z = globalThis.Buffer;
2686
+ function je(e) {
2687
+ if (Z) return Z.from(e, "utf8").toString("base64");
2688
+ let t = new TextEncoder().encode(e), n = "";
2689
+ for (let e of t) n += String.fromCharCode(e);
2690
+ return btoa(n);
2691
+ }
2692
+ function Me(e) {
2693
+ if (Z) return Z.from(e, "base64").toString("utf8");
2694
+ let t = atob(e), n = new Uint8Array(t.length);
2695
+ for (let e = 0; e < t.length; e++) n[e] = t.charCodeAt(e);
2696
+ return new TextDecoder().decode(n);
2697
+ }
2698
+ function Ne(e) {
2699
+ return e.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
2700
+ }
2701
+ function Pe(e) {
2702
+ let t = e.replace(/-/g, "+").replace(/_/g, "/");
2703
+ return t + (t.length % 4 == 0 ? "" : "=".repeat(4 - t.length % 4));
2704
+ }
2705
+ function Fe(e) {
2706
+ return Ne(je(JSON.stringify(e)));
2707
+ }
2708
+ function Ie(e) {
2709
+ if (!e) return null;
2710
+ try {
2711
+ let t = Me(Pe(e)), n = JSON.parse(t);
2712
+ return !n || n.v !== 1 || typeof n.symbol != "string" ? null : {
2713
+ v: 1,
2714
+ symbol: n.symbol,
2715
+ timeframe: n.timeframe,
2716
+ chartType: n.chartType,
2717
+ scaleMode: n.scaleMode ?? "regular",
2718
+ indicators: Array.isArray(n.indicators) ? n.indicators : [],
2719
+ drawings: Array.isArray(n.drawings) ? n.drawings : []
2720
+ };
2721
+ } catch {
2722
+ return null;
2723
+ }
2724
+ }
2725
+ var Q = "tcw";
2726
+ function Le(e) {
2727
+ let t = e.startsWith("#") ? e.slice(1) : e;
2728
+ for (let e of t.split("&")) {
2729
+ let t = e.indexOf("=");
2730
+ if (t > 0 && e.slice(0, t) === Q) return decodeURIComponent(e.slice(t + 1));
2731
+ }
2732
+ return null;
2733
+ }
2734
+ function Re(e, t) {
2735
+ let [n] = e.split("#");
2736
+ return `${n}#${Q}=${t}`;
2737
+ }
2738
+ //#endregion
2739
+ //#region src/widget/ChartWidget.ts
2740
+ var $ = [
2741
+ "#f7931a",
2742
+ "#627eea",
2743
+ "#26a17b",
2744
+ "#e84142",
2745
+ "#8247e5",
2746
+ "#f3ba2f"
2747
+ ], ze = class {
2748
+ chart;
2749
+ state;
2750
+ toolbar = null;
2751
+ sidebar = null;
2752
+ settings = null;
2753
+ statusBar = null;
2754
+ commandPalette = null;
2755
+ symbolSearch = null;
2756
+ hotkeySheet = null;
2757
+ replayBar = null;
2758
+ dragDrop = null;
2759
+ alertsPanel = null;
2760
+ objectTree = null;
2761
+ indicatorSettings = null;
2762
+ drawingStyle = null;
2763
+ bracketBar = null;
2764
+ alertNotifier = null;
2765
+ depthLadder = null;
2766
+ dataWindow = null;
2767
+ lastHoverIndex = null;
2768
+ favoritesStore = new we();
2769
+ watchlist = null;
2770
+ watchlistSparkBuffer = /* @__PURE__ */ new Map();
2771
+ sessionRefPrice = null;
2772
+ watchlistInterval = null;
2773
+ replayOriginalData = null;
2774
+ replayPollInterval = null;
2775
+ replaySpeed = 5;
2776
+ layoutKeyPrefix = null;
2777
+ layoutDebounceMs = 1500;
2778
+ activeLayoutKey = null;
2779
+ root;
2780
+ chartContainer;
2781
+ destroyed = !1;
2782
+ options;
2783
+ symbols;
2784
+ settingsState;
2785
+ adapter = null;
2786
+ boundGlobalKeydown = null;
2787
+ baseSeries = null;
2788
+ baseTimeframeMs = 0;
2789
+ compares = [];
2790
+ constructor(n, r = {}) {
2791
+ if (this.options = r, this.symbols = r.symbols ?? ee, this.settingsState = { ...p }, r.persistLayouts) {
2792
+ let e = r.persistLayouts === !0 ? {} : r.persistLayouts;
2793
+ this.layoutKeyPrefix = e.keyPrefix ?? "tcw:layout:", this.layoutDebounceMs = e.debounceMs ?? 1500;
2794
+ }
2795
+ let i = this.resolveIsDark(r.theme), a = this.resolveTheme(r.theme);
1100
2796
  this.state = {
2797
+ symbol: r.symbol ?? this.symbols[0] ?? "BTCUSDT",
2798
+ timeframe: r.timeframe ?? "5m",
2799
+ chartType: r.chartOptions?.chartType ?? "candlestick",
2800
+ isDark: i,
2801
+ activeIndicators: /* @__PURE__ */ new Map(),
2802
+ activeTool: null,
2803
+ magnetEnabled: !0,
2804
+ connectionState: "connecting",
2805
+ connectionMessage: "Connecting..."
2806
+ }, g(), this.root = document.createElement("div"), this.root.className = "tcw-root", this.root.dataset.tcwTheme = i ? "dark" : "light", n.appendChild(this.root), r.toolbar !== !1 && (this.toolbar = new ne(this.root, {
2807
+ symbols: this.symbols,
2808
+ timeframes: r.timeframes ? c.filter((e) => r.timeframes.includes(e.value)) : c,
2809
+ chartTypes: r.chartTypes ? l.filter((e) => r.chartTypes.includes(e.value)) : l,
2810
+ indicators: d,
2811
+ popularIndicatorIds: f
2812
+ }, {
2813
+ onSymbolClick: () => this.handleSymbolClick(),
2814
+ onTimeframe: (e) => this.handleTimeframe(e),
2815
+ onChartType: (e) => this.handleChartType(e),
2816
+ onAddIndicator: (e) => this.handleAddIndicator(e),
2817
+ onRemoveIndicator: (e) => this.handleRemoveIndicator(e),
2818
+ onScreenshot: () => this.chart.screenshot(),
2819
+ onSettings: () => this.openSettings(),
2820
+ onToggleTheme: () => this.handleToggleTheme(),
2821
+ onToggleReplay: () => this.toggleReplay(),
2822
+ onToggleAlerts: r.alerts === !1 ? void 0 : () => this.toggleAlerts(),
2823
+ onToggleObjects: r.objectTree === !1 ? void 0 : () => this.toggleObjects(),
2824
+ onBracket: r.trading === !1 ? void 0 : (e) => this.startBracket(e),
2825
+ onToggleLadder: r.trading !== !1 && r.depthLadder ? () => this.depthLadder?.toggle() : void 0
2826
+ }));
2827
+ let o = document.createElement("div");
2828
+ if (o.className = "tcw-body", r.drawingTools !== !1 && (r.drawingFavorites && this.favoritesStore.seedDefaults(r.drawingFavorites), this.sidebar = new x(o, {
2829
+ drawingToolGroups: u,
2830
+ favorites: this.favoritesStore.list()
2831
+ }, {
2832
+ onDrawingTool: (e) => this.handleDrawingTool(e),
2833
+ onCancelDrawing: () => this.handleCancelDrawing(),
2834
+ onToggleMagnet: () => this.handleToggleMagnet(),
2835
+ onToggleFavorite: (e) => this.handleToggleFavorite(e),
2836
+ onUndo: () => this.chart.undo(),
2837
+ onRedo: () => this.chart.redo(),
2838
+ onClearDrawings: () => {
2839
+ this.chart.clearDrawings(), this.state = {
2840
+ ...this.state,
2841
+ activeTool: null
2842
+ }, this.updateUI();
2843
+ },
2844
+ onToggleStyle: () => this.drawingStyle?.toggle()
2845
+ })), this.chartContainer = document.createElement("div"), this.chartContainer.className = "tcw-chart-container", o.appendChild(this.chartContainer), r.watchlist && (this.watchlist = new V(o, this.symbols, { onSelect: (e) => {
2846
+ this.setSymbol(e);
2847
+ } }), this.watchlist.setActive(this.state.symbol)), this.root.appendChild(o), this.chart = new e(this.chartContainer, {
2848
+ chartType: this.state.chartType,
2849
+ theme: a,
2850
+ autoScale: !0,
2851
+ crosshair: { mode: "magnet" },
2852
+ features: {
2853
+ drawings: !0,
2854
+ drawingMagnet: !0,
2855
+ drawingUndoRedo: !0,
2856
+ indicators: !0,
2857
+ trading: r.trading !== !1,
2858
+ tradingContextMenu: r.trading !== !1,
2859
+ volume: !0,
2860
+ legend: !0,
2861
+ crosshair: !0,
2862
+ keyboard: !0,
2863
+ screenshot: !0,
2864
+ alerts: !0,
2865
+ barCountdown: !0,
2866
+ logScale: !0,
2867
+ watermark: !0
2868
+ },
2869
+ ...r.chartOptions
2870
+ }), r.dragDropImport !== !1 && (this.dragDrop = new t(this.chartContainer, {
2871
+ onData: (e, t, n) => {
2872
+ this.setData(e), this.toast(`Loaded ${t.data.length} bars from ${n.name}` + (t.skipped > 0 ? ` (${t.skipped} skipped)` : ""));
2873
+ },
2874
+ onError: (e, t) => {
2875
+ this.toast(`${t.name}: ${e.message}`, "error");
2876
+ }
2877
+ }), this.dragDrop.attach()), r.statusBar !== !1 && (this.statusBar = new w(this.root)), r.settings !== !1 && (this.settings = new C({
2878
+ onChange: (e) => this.applySettings(e),
2879
+ onReset: () => this.resetSettings(),
2880
+ onClose: () => {}
2881
+ })), this.symbolSearch = new E({
2882
+ onPick: (e) => {
2883
+ this.setSymbol(e);
2884
+ },
2885
+ onClose: () => {}
2886
+ }), this.hotkeySheet = new N({ onClose: () => {} }), this.chart.on("barClick", (e) => {
2887
+ if (!this.replayBar?.isMounted()) return;
2888
+ let t = e.payload.barIndex;
2889
+ typeof t == "number" && (this.chart.getReplayState() === "playing" && this.chart.replayPause(), this.chart.replaySeek(t), this.replayBar.setState("paused"));
2890
+ }), this.dataWindow = new ke(this.root, { formatPrice: (e) => this.formatAlertPrice(e) }), this.chart.on("crosshairMove", (e) => {
2891
+ let t = e.payload;
2892
+ this.lastHoverIndex = typeof t.barIndex == "number" ? t.barIndex : null, this.dataWindow?.isOpen() && this.dataWindow.render(this.buildDataWindowModel());
2893
+ }), r.drawingTools !== !1 && (this.drawingStyle = new Se(this.root, {
2894
+ onStyleChange: (e) => {
2895
+ this.chart.setDrawingStyle(e), this.chart.setSelectedDrawingStyle(e);
2896
+ },
2897
+ getStyle: () => this.chart.getDrawingStyle()
2898
+ }, new ve())), r.trading !== !1 && (this.bracketBar = new Te(this.root, {
2899
+ onConfirm: () => this.chart.confirmBracket(),
2900
+ onCancel: () => {
2901
+ this.chart.cancelBracket(), this.bracketBar?.hide();
2902
+ }
2903
+ }), this.chart.on("bracketPlace", (e) => {
2904
+ let t = e.payload;
2905
+ this.bracketBar?.hide(), this.toast(`${t.side === "buy" ? "Long" : "Short"} bracket placed · ${t.riskReward.toFixed(2)}R`);
2906
+ }), r.depthLadder && (this.depthLadder = new Oe(this.root, {
2907
+ onTrade: (e, t) => {
2908
+ this.chart.placeOrderIntent({
2909
+ side: e,
2910
+ type: "limit",
2911
+ price: t
2912
+ }), this.toast(`${e === "buy" ? "Buy" : "Sell"} limit @ ${this.formatAlertPrice(t)}`);
2913
+ },
2914
+ formatPrice: (e) => this.formatAlertPrice(e)
2915
+ })), this.chart.on("dataUpdate", (e) => {
2916
+ let t = e.payload;
2917
+ t && t.bracket === "cancelled" && this.bracketBar?.hide();
2918
+ })), r.alerts !== !1 && (this.alertsPanel = new ce(this.root, {
2919
+ onAdd: (e, t, n, r, i) => {
2920
+ this.chart.addAlert(e, t, n, r, i);
2921
+ },
2922
+ onRemove: (e) => this.chart.removeAlert(e),
2923
+ onClear: () => this.chart.clearAlerts(),
2924
+ getChannelValue: (e) => this.getAlertChannelValue(e),
2925
+ formatPrice: (e) => this.formatAlertPrice(e)
2926
+ }), this.chart.on("alertAdd", () => this.refreshAlerts()), this.chart.on("alertRemove", () => this.refreshAlerts()), this.chart.on("alertUpdate", () => this.refreshAlerts()), r.alertNotifications && (this.alertNotifier = new Ee(r.alertNotifications)), this.chart.on("alertTriggered", (e) => {
2927
+ let t = e.payload, n = `Price ${this.formatAlertPrice(t.price)}${t.message ? ` — ${t.message}` : ""}`;
2928
+ this.toast(`🔔 Alert: ${n}`, "info"), this.alertNotifier?.notify(n), this.refreshAlerts();
2929
+ })), r.objectTree !== !1) {
2930
+ this.indicatorSettings = new me(this.root, {
2931
+ onApply: (e, t) => this.chart.updateIndicator(e, t),
2932
+ onClose: () => {}
2933
+ }), this.objectTree = new le(this.root, {
2934
+ onRemoveIndicator: (e) => this.handleRemoveIndicator(e),
2935
+ onConfigureIndicator: (e) => this.openIndicatorSettings(e),
2936
+ onToggleIndicatorVisible: (e, t) => {
2937
+ this.chart.setIndicatorVisible(e, t), this.refreshObjects();
2938
+ },
2939
+ onRemoveDrawing: (e) => {
2940
+ this.chart.removeDrawing(e), this.refreshObjects();
2941
+ },
2942
+ onToggleDrawingVisible: (e, t) => {
2943
+ this.chart.setDrawingVisible(e, t), this.refreshObjects();
2944
+ },
2945
+ onToggleDrawingLocked: (e, t) => {
2946
+ this.chart.setDrawingLocked(e, t), this.refreshObjects();
2947
+ },
2948
+ onAddCompare: () => this.handleAddCompare(),
2949
+ onRemoveCompare: (e) => this.handleRemoveCompare(e)
2950
+ });
2951
+ let e = () => {
2952
+ this.objectTree?.isOpen() && this.refreshObjects();
2953
+ };
2954
+ this.chart.on("drawingCreate", e), this.chart.on("drawingRemove", e), this.chart.on("indicatorAdd", e), this.chart.on("indicatorRemove", e);
2955
+ }
2956
+ if (this.commandPalette = new T({
2957
+ onIndicator: (e) => this.handleAddIndicator(e),
2958
+ onChartType: (e) => this.handleChartType(e),
2959
+ onDrawingTool: (e) => this.handleDrawingTool(e),
2960
+ onTimeframe: (e) => this.handleTimeframe(e),
2961
+ onAction: (e) => this.handleAction(e),
2962
+ onClose: () => {}
2963
+ }), this.boundGlobalKeydown = (e) => {
2964
+ if ((e.ctrlKey || e.metaKey) && e.key === "k") e.preventDefault(), this.toggleCommandPalette();
2965
+ else if ((e.ctrlKey || e.metaKey) && (e.key === "p" || e.key === "P")) e.preventDefault(), this.symbolSearch?.open(this.symbols, this.state.symbol);
2966
+ else if (e.key === "?" && !e.ctrlKey && !e.metaKey && !e.altKey) {
2967
+ let t = document.activeElement;
2968
+ if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return;
2969
+ e.preventDefault(), this.hotkeySheet?.open();
2970
+ }
2971
+ }, 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") {
2972
+ let e = Le(location.hash);
2973
+ e && this.importState(e);
2974
+ }
2975
+ }
2976
+ setSymbols(e) {
2977
+ this.symbols = e, this.watchlist?.setSymbols(e);
2978
+ }
2979
+ async setSymbol(e) {
2980
+ this.flushActiveLayout(), this.state = {
1101
2981
  ...this.state,
1102
2982
  symbol: e
1103
- }, this.options.onSymbolChange?.(e), this.updateUI(), this.adapter && this.connectStream();
2983
+ }, this.sessionRefPrice = null, this.options.onSymbolChange?.(e), this.updateUI(), this.watchlist?.setActive(e), this.adapter && await this.connectStream();
2984
+ }
2985
+ setWatchlistEntry(e, t) {
2986
+ this.watchlist?.setEntry(e, t);
2987
+ }
2988
+ tickWatchlist() {
2989
+ if (!this.watchlist) return;
2990
+ let e = this.chart.getData();
2991
+ if (e.length === 0) return;
2992
+ let t = e[e.length - 1];
2993
+ this.sessionRefPrice === null && (this.sessionRefPrice = e[0].open);
2994
+ let n = this.watchlistSparkBuffer.get(this.state.symbol) ?? [];
2995
+ n.push(t.close), n.length > 40 && n.shift(), this.watchlistSparkBuffer.set(this.state.symbol, n), this.watchlist.setEntry(this.state.symbol, {
2996
+ lastPrice: t.close,
2997
+ refPrice: this.sessionRefPrice,
2998
+ sparkline: n.slice()
2999
+ });
1104
3000
  }
1105
- handleTimeframe(e) {
3001
+ async setTimeframe(e) {
3002
+ if (this.state = {
3003
+ ...this.state,
3004
+ timeframe: e
3005
+ }, this.options.onTimeframeChange?.(e), this.updateUI(), this.adapter) {
3006
+ await this.connectStream();
3007
+ return;
3008
+ }
3009
+ this.options.resampleTimeframes !== !1 && this.applyTimeframeData();
3010
+ }
3011
+ setTheme(e) {
3012
+ let t = this.resolveIsDark(e), n = this.resolveTheme(e);
1106
3013
  this.state = {
3014
+ ...this.state,
3015
+ isDark: t
3016
+ }, this.root.dataset.tcwTheme = t ? "dark" : "light", this.chart.setTheme(n), this.updateUI();
3017
+ }
3018
+ getChart() {
3019
+ return this.chart;
3020
+ }
3021
+ destroy() {
3022
+ 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.chart.destroy(), this.root.remove(), _());
3023
+ }
3024
+ handleSymbolClick() {
3025
+ this.symbolSearch?.open(this.symbols, this.state.symbol);
3026
+ }
3027
+ handleTimeframe(e) {
3028
+ if (this.state = {
1107
3029
  ...this.state,
1108
3030
  timeframe: e
1109
- }, this.options.onTimeframeChange?.(e), this.updateUI(), this.adapter && this.connectStream();
3031
+ }, this.options.onTimeframeChange?.(e), this.updateUI(), this.adapter) {
3032
+ this.connectStream();
3033
+ return;
3034
+ }
3035
+ if (this.options.resampleTimeframes !== !1) {
3036
+ if (!this.baseSeries) {
3037
+ let e = this.chart.getData();
3038
+ e.length > 0 && (this.baseSeries = e, this.baseTimeframeMs = s(e));
3039
+ }
3040
+ this.applyTimeframeData();
3041
+ }
3042
+ }
3043
+ setData(e) {
3044
+ this.baseSeries = e, this.baseTimeframeMs = s(e), this.applyTimeframeData();
3045
+ }
3046
+ applyTimeframeData() {
3047
+ if (!this.baseSeries) return;
3048
+ let e = a(this.state.timeframe);
3049
+ if (this.baseTimeframeMs > 0 && e > this.baseTimeframeMs) {
3050
+ let e = this.options.weekStartsOn ?? 1;
3051
+ this.chart.setData(o(this.baseSeries, this.state.timeframe, { weekStartsOn: e }));
3052
+ } else this.chart.setData(this.baseSeries);
3053
+ }
3054
+ toggleAlerts() {
3055
+ this.alertsPanel && (this.alertsPanel.toggle(), this.alertsPanel.isOpen() && this.refreshAlerts());
3056
+ }
3057
+ handleToggleFavorite(e) {
3058
+ let t = this.favoritesStore.toggle(e);
3059
+ this.sidebar?.setFavorites(this.favoritesStore.list()), this.toast(t ? "Pinned to favorites" : "Unpinned");
3060
+ }
3061
+ exportState() {
3062
+ let e = this.chart.getActiveIndicators().map((e) => ({
3063
+ id: e.id,
3064
+ params: e.params
3065
+ }));
3066
+ return Fe({
3067
+ v: 1,
3068
+ symbol: this.state.symbol,
3069
+ timeframe: this.state.timeframe,
3070
+ chartType: this.state.chartType,
3071
+ scaleMode: this.chart.getScaleMode(),
3072
+ indicators: e,
3073
+ drawings: this.chart.getDrawings()
3074
+ });
3075
+ }
3076
+ async importState(e) {
3077
+ let t = Ie(e);
3078
+ if (!t) return !1;
3079
+ 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);
3080
+ for (let [, e] of this.state.activeIndicators) this.chart.removeIndicator(e);
3081
+ let n = /* @__PURE__ */ new Map();
3082
+ for (let e of t.indicators) {
3083
+ let t = this.chart.addIndicator(e.id, e.params);
3084
+ t && n.set(e.id, t);
3085
+ }
3086
+ return this.state = {
3087
+ ...this.state,
3088
+ activeIndicators: n
3089
+ }, this.chart.setDrawings(t.drawings), this.updateUI(), !0;
3090
+ }
3091
+ async copyChartImage() {
3092
+ let e = await this.chart.copyScreenshot();
3093
+ this.toast(e ? "Chart image copied" : "Copy failed — image clipboard unavailable", e ? "info" : "error");
3094
+ }
3095
+ async copyShareLink() {
3096
+ let e = Re(typeof location < "u" ? location.href : "", this.exportState());
3097
+ try {
3098
+ await navigator.clipboard.writeText(e), this.toast("Share link copied");
3099
+ } catch {
3100
+ this.toast("Copy failed — clipboard unavailable", "error");
3101
+ }
3102
+ }
3103
+ setDepth(e) {
3104
+ this.chart.setDepthData(e), this.depthLadder?.setData(e), e && this.chart.pushDepthSnapshot(e);
3105
+ }
3106
+ startBracket(e) {
3107
+ this.chart.startBracket(e) && this.bracketBar?.show(e);
3108
+ }
3109
+ buildDataWindowModel() {
3110
+ let e = this.chart.getData(), t = this.lastHoverIndex;
3111
+ if (t === null || t < 0 || t >= e.length) return {
3112
+ ohlc: null,
3113
+ change: 0,
3114
+ changePct: 0,
3115
+ indicators: []
3116
+ };
3117
+ let n = e[t], r = t > 0 ? e[t - 1].close : n.open, i = n.close - r, a = r === 0 ? 0 : i / r * 100, o = this.chart.getActiveIndicators().map((e) => {
3118
+ let n = this.chart.getIndicatorOutput(e.instanceId)?.series?.[t] ?? null, r = n ? Object.entries(n).filter(([, e]) => typeof e == "number" && Number.isFinite(e)).map(([e, t]) => ({
3119
+ key: e,
3120
+ value: t
3121
+ })) : [];
3122
+ return {
3123
+ name: e.descriptor.name,
3124
+ values: r
3125
+ };
3126
+ }).filter((e) => e.values.length > 0);
3127
+ return {
3128
+ ohlc: {
3129
+ open: n.open,
3130
+ high: n.high,
3131
+ low: n.low,
3132
+ close: n.close,
3133
+ volume: n.volume
3134
+ },
3135
+ change: i,
3136
+ changePct: a,
3137
+ indicators: o
3138
+ };
3139
+ }
3140
+ toggleDataWindow() {
3141
+ this.dataWindow && (this.dataWindow.toggle(), this.dataWindow.isOpen() && this.dataWindow.render(this.buildDataWindowModel()));
3142
+ }
3143
+ toggleObjects() {
3144
+ this.objectTree && (this.objectTree.toggle(), this.objectTree.isOpen() && this.refreshObjects());
3145
+ }
3146
+ openIndicatorSettings(e) {
3147
+ if (!this.indicatorSettings) return;
3148
+ let t = this.chart.getActiveIndicators().find((t) => t.instanceId === e);
3149
+ t && this.indicatorSettings.open({
3150
+ instanceId: e,
3151
+ name: t.descriptor.name,
3152
+ defaults: t.descriptor.defaultConfig,
3153
+ params: t.params
3154
+ });
3155
+ }
3156
+ refreshObjects() {
3157
+ if (!this.objectTree) return;
3158
+ let e = this.chart.getActiveIndicators().map((e) => ({
3159
+ instanceId: e.instanceId,
3160
+ name: e.descriptor.name,
3161
+ visible: e.visible
3162
+ })), t = this.chart.getDrawings().map((e) => ({
3163
+ id: e.id,
3164
+ label: de(e.type),
3165
+ visible: e.visible,
3166
+ locked: e.locked
3167
+ })), n = this.compares.map((e) => ({
3168
+ id: e.id,
3169
+ label: e.symbol,
3170
+ color: e.color
3171
+ }));
3172
+ this.objectTree.setObjects(e, t, n);
3173
+ }
3174
+ async handleAddCompare() {
3175
+ if (!this.adapter) {
3176
+ this.toast("Comparison needs a live data adapter", "error");
3177
+ return;
3178
+ }
3179
+ let e = new Set([this.state.symbol, ...this.compares.map((e) => e.symbol)]), t = this.symbols.filter((t) => !e.has(t));
3180
+ this.symbolSearch?.open(t.length ? t : this.symbols, this.state.symbol, (e) => {
3181
+ this.addCompareSymbol(e);
3182
+ });
3183
+ }
3184
+ async addCompareSymbol(e) {
3185
+ if (!this.adapter || this.compares.some((t) => t.symbol === e)) return;
3186
+ let t = $[this.compares.length % $.length], n = `cmp_${e}`;
3187
+ try {
3188
+ let r = await this.adapter.fetchHistory(e, this.state.timeframe, this.options.historyLimit ?? 500);
3189
+ this.compares.length === 0 && this.chart.setCompareMode("percent"), this.chart.addCompareSymbol(n, e, r, t), this.compares = [...this.compares, {
3190
+ id: n,
3191
+ symbol: e,
3192
+ color: t
3193
+ }], this.refreshObjects(), this.toast(`Comparing ${e}`);
3194
+ } catch (t) {
3195
+ this.toast(`${e}: ${t instanceof Error ? t.message : "failed to load"}`, "error");
3196
+ }
3197
+ }
3198
+ handleRemoveCompare(e) {
3199
+ this.chart.removeCompareSymbol(e), this.compares = this.compares.filter((t) => t.id !== e), this.refreshObjects();
3200
+ }
3201
+ async refetchCompares() {
3202
+ if (!this.adapter || this.compares.length === 0) return;
3203
+ let e = this.compares.filter((e) => e.symbol === this.state.symbol);
3204
+ for (let t of e) this.handleRemoveCompare(t.id);
3205
+ for (let e of this.compares) try {
3206
+ let t = await this.adapter.fetchHistory(e.symbol, this.state.timeframe, this.options.historyLimit ?? 500);
3207
+ this.chart.updateCompareData(e.id, t);
3208
+ } catch {}
3209
+ }
3210
+ refreshAlerts() {
3211
+ this.alertsPanel && (this.alertsPanel.setSources(this.buildAlertSources()), this.alertsPanel.setAlerts(this.chart.getAlerts().map((e) => ({
3212
+ id: e.id,
3213
+ price: e.price,
3214
+ condition: e.condition,
3215
+ message: e.message,
3216
+ triggered: e.triggered,
3217
+ channel: e.channel,
3218
+ label: e.label
3219
+ }))));
3220
+ }
3221
+ buildAlertSources() {
3222
+ let e = [{
3223
+ channel: "price",
3224
+ label: "Price"
3225
+ }];
3226
+ for (let t of this.chart.getActiveIndicators()) {
3227
+ let n = this.chart.getIndicatorOutput(t.instanceId)?.series, r = n && n.length > 0 ? n[n.length - 1] : null, i = r ? Object.keys(r).filter((e) => typeof r[e] == "number") : [];
3228
+ for (let n of i) {
3229
+ let r = i.length > 1 ? `${t.descriptor.name} ${n}` : t.descriptor.name;
3230
+ e.push({
3231
+ channel: `${t.instanceId}:${n}`,
3232
+ label: r
3233
+ });
3234
+ }
3235
+ }
3236
+ return e;
3237
+ }
3238
+ getAlertChannelValue(e) {
3239
+ let t = this.chart.getData();
3240
+ if (e === "price") return t.length > 0 ? t[t.length - 1].close : null;
3241
+ let n = e.indexOf(":");
3242
+ if (n < 0 || t.length === 0) return null;
3243
+ let r = e.slice(0, n), i = e.slice(n + 1), a = this.chart.getIndicatorOutput(r)?.series?.[t.length - 1]?.[i];
3244
+ return typeof a == "number" && Number.isFinite(a) ? a : null;
3245
+ }
3246
+ formatAlertPrice(e) {
3247
+ let t = Math.abs(e), n = t >= 1e3 ? 2 : t >= 1 ? 4 : 6, r = this.settingsState.numberLocale || void 0;
3248
+ return e.toLocaleString(r, {
3249
+ minimumFractionDigits: 2,
3250
+ maximumFractionDigits: n
3251
+ });
1110
3252
  }
1111
3253
  handleChartType(e) {
1112
3254
  this.state = {
@@ -1156,25 +3298,25 @@ var _ = class {
1156
3298
  }
1157
3299
  buildCommandItems() {
1158
3300
  let e = [];
1159
- for (let t of o) e.push({
3301
+ for (let t of d) e.push({
1160
3302
  id: t.id,
1161
3303
  label: t.name,
1162
3304
  category: "indicator",
1163
3305
  active: this.state.activeIndicators.has(t.id)
1164
3306
  });
1165
- for (let t of i) e.push({
3307
+ for (let t of l) e.push({
1166
3308
  id: t.value,
1167
3309
  label: t.label,
1168
3310
  category: "chartType",
1169
3311
  active: this.state.chartType === t.value
1170
3312
  });
1171
- for (let t of a) for (let n of t.tools) e.push({
3313
+ for (let t of u) for (let n of t.tools) e.push({
1172
3314
  id: n.value,
1173
3315
  label: n.label,
1174
3316
  category: "drawing",
1175
3317
  active: this.state.activeTool === n.value
1176
3318
  });
1177
- for (let t of r) e.push({
3319
+ for (let t of c) e.push({
1178
3320
  id: t.value,
1179
3321
  label: t.label,
1180
3322
  category: "timeframe",
@@ -1185,6 +3327,10 @@ var _ = class {
1185
3327
  label: "Screenshot",
1186
3328
  category: "action",
1187
3329
  shortcut: ""
3330
+ }, {
3331
+ id: "copyImage",
3332
+ label: "Copy Chart Image",
3333
+ category: "action"
1188
3334
  }, {
1189
3335
  id: "toggleTheme",
1190
3336
  label: "Toggle Theme",
@@ -1193,6 +3339,18 @@ var _ = class {
1193
3339
  id: "settings",
1194
3340
  label: "Settings",
1195
3341
  category: "action"
3342
+ }, {
3343
+ id: "shareView",
3344
+ label: "Share View (copy link)",
3345
+ category: "action"
3346
+ }, {
3347
+ id: "autoFib",
3348
+ label: "Auto Fibonacci (visible swing)",
3349
+ category: "action"
3350
+ }, {
3351
+ id: "dataWindow",
3352
+ label: "Toggle Data Window",
3353
+ category: "action"
1196
3354
  }, {
1197
3355
  id: "clearDrawings",
1198
3356
  label: "Clear All Drawings",
@@ -1210,6 +3368,20 @@ var _ = class {
1210
3368
  case "settings":
1211
3369
  this.openSettings();
1212
3370
  break;
3371
+ case "copyImage":
3372
+ this.copyChartImage();
3373
+ break;
3374
+ case "shareView":
3375
+ this.copyShareLink();
3376
+ break;
3377
+ case "autoFib": {
3378
+ let e = this.chart.autoFib();
3379
+ this.toast(e ? "Auto Fibonacci added" : "No clear swing in view");
3380
+ break;
3381
+ }
3382
+ case "dataWindow":
3383
+ this.toggleDataWindow();
3384
+ break;
1213
3385
  case "clearDrawings":
1214
3386
  this.chart.clearDrawings(), this.state = {
1215
3387
  ...this.state,
@@ -1242,7 +3414,7 @@ var _ = class {
1242
3414
  this.state = {
1243
3415
  ...this.state,
1244
3416
  isDark: e
1245
- }, this.root.dataset.tcwTheme = e ? "dark" : "light", this.chart.setTheme(e ? t : n), this.chart.setWatermark(this.state.symbol.replace("USDT", " / USDT"), {
3417
+ }, this.root.dataset.tcwTheme = e ? "dark" : "light", this.chart.setTheme(e ? n : r), this.chart.setWatermark(this.state.symbol.replace("USDT", " / USDT"), {
1246
3418
  fontSize: 48,
1247
3419
  color: e ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.03)"
1248
3420
  }), this.updateUI();
@@ -1250,16 +3422,114 @@ var _ = class {
1250
3422
  openSettings() {
1251
3423
  this.settings?.open(this.settingsState);
1252
3424
  }
3425
+ toast(e, t = "info") {
3426
+ let n = document.createElement("div");
3427
+ n.className = `tcw-toast ${t === "error" ? "tcw-toast-error" : ""}`, n.textContent = e, this.root.appendChild(n), requestAnimationFrame(() => n.classList.add("tcw-toast-in")), setTimeout(() => {
3428
+ n.classList.remove("tcw-toast-in"), n.addEventListener("transitionend", () => n.remove(), { once: !0 }), setTimeout(() => n.remove(), 400);
3429
+ }, 3500);
3430
+ }
3431
+ clearSavedLayout(e) {
3432
+ if (!this.layoutKeyPrefix) return;
3433
+ let t = e ?? this.state.symbol;
3434
+ try {
3435
+ localStorage.removeItem(this.layoutKeyPrefix + t);
3436
+ } catch {}
3437
+ }
3438
+ applySymbolLayout(e) {
3439
+ if (!this.layoutKeyPrefix) return;
3440
+ let t = this.layoutKeyPrefix + e;
3441
+ this.activeLayoutKey = t;
3442
+ try {
3443
+ this.chart.loadStateFromStorage(t) && this.rebuildActiveIndicatorsMapFromChart();
3444
+ } catch {}
3445
+ this.chart.setAutoSave(t, this.layoutDebounceMs);
3446
+ }
3447
+ flushActiveLayout() {
3448
+ if (this.activeLayoutKey) {
3449
+ try {
3450
+ this.chart.saveState(this.activeLayoutKey);
3451
+ } catch {}
3452
+ this.chart.disableAutoSave(), this.activeLayoutKey = null;
3453
+ }
3454
+ }
3455
+ rebuildActiveIndicatorsMapFromChart() {
3456
+ let e = /* @__PURE__ */ new Map();
3457
+ for (let t of this.chart.getActiveIndicators()) e.set(t.id, t.instanceId);
3458
+ this.state = {
3459
+ ...this.state,
3460
+ activeIndicators: e
3461
+ }, this.updateUI();
3462
+ }
3463
+ toggleReplay() {
3464
+ this.replayBar?.isMounted() ? this.exitReplay() : this.enterReplay();
3465
+ }
3466
+ enterReplay() {
3467
+ let e = this.chart.getData();
3468
+ e.length < 2 || (this.replayOriginalData = e.slice(), this.replayBar = new F({
3469
+ onPlay: () => {
3470
+ this.chart.getReplayState() === "paused" ? this.chart.replayResume() : this.chart.replay({
3471
+ speed: this.replaySpeed,
3472
+ interval: 200
3473
+ }), this.replayBar?.setState("playing");
3474
+ },
3475
+ onPause: () => {
3476
+ this.chart.replayPause(), this.replayBar?.setState("paused");
3477
+ },
3478
+ onStop: () => {
3479
+ this.chart.replayStop(), this.replayBar?.setState("paused");
3480
+ },
3481
+ onStepBack: () => {
3482
+ let e = this.chart.getReplayProgress();
3483
+ this.chart.getReplayState() === "playing" && this.chart.replayPause(), this.chart.replaySeek(Math.max(0, e.current - 1)), this.replayBar?.setState("paused");
3484
+ },
3485
+ onStepForward: () => {
3486
+ let e = this.chart.getReplayProgress();
3487
+ this.chart.getReplayState() === "playing" && this.chart.replayPause(), this.chart.replaySeek(Math.min(e.total - 1, e.current + 1)), this.replayBar?.setState("paused");
3488
+ },
3489
+ onSeek: (e) => {
3490
+ this.chart.getReplayState() === "playing" && this.chart.replayPause(), this.chart.replaySeek(e), this.replayBar?.setState("paused");
3491
+ },
3492
+ onSpeedChange: (e) => {
3493
+ this.replaySpeed = e, this.chart.setReplaySpeed(e);
3494
+ },
3495
+ onClose: () => this.exitReplay()
3496
+ }), this.replayBar.mount(this.chartContainer, {
3497
+ total: e.length,
3498
+ speed: this.replaySpeed
3499
+ }), this.chart.replay({
3500
+ speed: this.replaySpeed,
3501
+ interval: 200,
3502
+ startIndex: 1
3503
+ }), this.chart.replayPause(), this.replayBar.setState("paused"), this.replayPollInterval = setInterval(() => {
3504
+ if (!this.replayBar?.isMounted()) return;
3505
+ let e = this.chart.getReplayProgress();
3506
+ this.replayBar.setProgress(e.current, e.total);
3507
+ let t = this.chart.getReplayState();
3508
+ t === "stopped" ? this.replayBar.setState("paused") : this.replayBar.setState(t);
3509
+ }, 150));
3510
+ }
3511
+ exitReplay() {
3512
+ this.replayPollInterval &&= (clearInterval(this.replayPollInterval), null), this.chart.replayStop(), this.replayBar?.unmount(), this.replayBar = null, this.replayOriginalData &&= (this.chart.setData(this.replayOriginalData), null);
3513
+ }
1253
3514
  applySettings(e) {
1254
3515
  this.settingsState = {
1255
3516
  ...this.settingsState,
1256
3517
  ...e
1257
- }, e.gridVisible !== void 0 && this.chart.setGridVisible(e.gridVisible), e.volumeVisible !== void 0 && this.chart.setVolumeVisible(e.volumeVisible), e.crosshairMode !== void 0 && this.chart.setCrosshairMode(e.crosshairMode), e.autoScale !== void 0 && this.chart.setAutoScale(e.autoScale), e.logScale !== void 0 && this.chart.setLogScale(e.logScale), e.numberLocale !== void 0 && this.chart.setNumberLocale(e.numberLocale);
3518
+ }, 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({
3519
+ left: e.pivotStrength,
3520
+ right: e.pivotStrength
3521
+ }), 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 = {
3522
+ ...this.settingsState,
3523
+ scaleMode: e.logScale ? "logarithmic" : "regular"
3524
+ }) : (this.chart.setScaleMode(e.scaleMode), this.settingsState = {
3525
+ ...this.settingsState,
3526
+ logScale: e.scaleMode === "logarithmic"
3527
+ }), e.numberLocale !== void 0 && this.chart.setNumberLocale(e.numberLocale), e.timezone !== void 0 && this.chart.setTimezoneOffset(e.timezone === "local" ? null : Number(e.timezone));
1258
3528
  let t = { ...this.chart.getTheme() }, n = !1;
1259
3529
  e.candleUpColor !== void 0 && (t.candleUp = e.candleUpColor, n = !0), e.candleDownColor !== void 0 && (t.candleDown = e.candleDownColor, n = !0), e.candleUpWick !== void 0 && (t.candleUpWick = e.candleUpWick, n = !0), e.candleDownWick !== void 0 && (t.candleDownWick = e.candleDownWick, n = !0), e.backgroundColor !== void 0 && (t.background = e.backgroundColor, n = !0), e.gridColor !== void 0 && (t.grid = e.gridColor, n = !0), n && this.chart.setTheme(t);
1260
3530
  }
1261
3531
  resetSettings() {
1262
- this.settingsState = { ...l }, this.applySettings(this.settingsState);
3532
+ this.settingsState = { ...p }, this.applySettings(this.settingsState);
1263
3533
  }
1264
3534
  async connectStream() {
1265
3535
  if (this.adapter) {
@@ -1277,11 +3547,11 @@ var _ = class {
1277
3547
  }), this.chart.setWatermark(this.state.symbol.replace("USDT", " / USDT"), {
1278
3548
  fontSize: 48,
1279
3549
  color: this.state.isDark ? "rgba(255,255,255,0.03)" : "rgba(0,0,0,0.03)"
1280
- }), this.state = {
3550
+ }), this.applySymbolLayout(this.state.symbol), this.state = {
1281
3551
  ...this.state,
1282
3552
  connectionState: "connected",
1283
3553
  connectionMessage: "Live"
1284
- };
3554
+ }, this.refetchCompares();
1285
3555
  } catch (e) {
1286
3556
  this.state = {
1287
3557
  ...this.state,
@@ -1304,7 +3574,7 @@ var _ = class {
1304
3574
  return e === void 0 || e === "dark" ? !0 : e === "light" ? !1 : typeof e == "object" && e.background ? this.isColorDark(e.background) : !0;
1305
3575
  }
1306
3576
  resolveTheme(e) {
1307
- return e === void 0 || e === "dark" ? t : e === "light" ? n : typeof e == "object" ? e : t;
3577
+ return e === void 0 || e === "dark" ? n : e === "light" ? r : typeof e == "object" ? e : n;
1308
3578
  }
1309
3579
  isColorDark(e) {
1310
3580
  if (e.startsWith("#") && e.length >= 7) {
@@ -1315,6 +3585,6 @@ var _ = class {
1315
3585
  }
1316
3586
  };
1317
3587
  //#endregion
1318
- export { T as ChartWidget };
3588
+ export { ze as ChartWidget };
1319
3589
 
1320
3590
  //# sourceMappingURL=index.js.map