@tradecanvas/chart 1.81.0 → 1.82.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 (106) hide show
  1. package/dist/Chart.d.ts +14 -1
  2. package/dist/Chart.d.ts.map +1 -1
  3. package/dist/{LayoutSession-C4Lp1X6t.js → LayoutSession-CLlUszTA.js} +388 -370
  4. package/dist/LayoutSession-CLlUszTA.js.map +1 -0
  5. package/dist/LayoutSession-Cs8SJ1LU.cjs +2 -0
  6. package/dist/LayoutSession-Cs8SJ1LU.cjs.map +1 -0
  7. package/dist/{WidgetCommandPalette-B1jaXJ-I.cjs → WidgetCommandPalette-BNBL1mO2.cjs} +2 -2
  8. package/dist/{WidgetCommandPalette-B1jaXJ-I.cjs.map → WidgetCommandPalette-BNBL1mO2.cjs.map} +1 -1
  9. package/dist/{WidgetCommandPalette-Bvr6Od3p.js → WidgetCommandPalette-p4DQCe57.js} +2 -2
  10. package/dist/{WidgetCommandPalette-Bvr6Od3p.js.map → WidgetCommandPalette-p4DQCe57.js.map} +1 -1
  11. package/dist/{WidgetDrawingSettings-B8JKpke1.cjs → WidgetDrawingSettings-BqGuAMz3.cjs} +2 -2
  12. package/dist/{WidgetDrawingSettings-B8JKpke1.cjs.map → WidgetDrawingSettings-BqGuAMz3.cjs.map} +1 -1
  13. package/dist/{WidgetDrawingSettings-CsR3GuFG.js → WidgetDrawingSettings-D7l2NkVj.js} +2 -2
  14. package/dist/{WidgetDrawingSettings-CsR3GuFG.js.map → WidgetDrawingSettings-D7l2NkVj.js.map} +1 -1
  15. package/dist/{WidgetIndicatorPicker-B72LdJy1.cjs → WidgetIndicatorPicker-BOxIKUn-.cjs} +2 -2
  16. package/dist/{WidgetIndicatorPicker-B72LdJy1.cjs.map → WidgetIndicatorPicker-BOxIKUn-.cjs.map} +1 -1
  17. package/dist/{WidgetIndicatorPicker-Cb2m62oH.js → WidgetIndicatorPicker-Bk0y6c35.js} +2 -2
  18. package/dist/{WidgetIndicatorPicker-Cb2m62oH.js.map → WidgetIndicatorPicker-Bk0y6c35.js.map} +1 -1
  19. package/dist/{WidgetIndicatorSettings-BiX1ZOhS.js → WidgetIndicatorSettings-BmOqq2Fv.js} +3 -3
  20. package/dist/{WidgetIndicatorSettings-BiX1ZOhS.js.map → WidgetIndicatorSettings-BmOqq2Fv.js.map} +1 -1
  21. package/dist/{WidgetIndicatorSettings-DhFQasHT.cjs → WidgetIndicatorSettings-DrPcNSxS.cjs} +2 -2
  22. package/dist/{WidgetIndicatorSettings-DhFQasHT.cjs.map → WidgetIndicatorSettings-DrPcNSxS.cjs.map} +1 -1
  23. package/dist/{WidgetSettings-bO72SYQ6.cjs → WidgetSettings-B57_tUke.cjs} +2 -2
  24. package/dist/WidgetSettings-B57_tUke.cjs.map +1 -0
  25. package/dist/{WidgetSettings-CkvK-3d9.js → WidgetSettings-C6m55YF0.js} +17 -4
  26. package/dist/WidgetSettings-C6m55YF0.js.map +1 -0
  27. package/dist/{WidgetSymbolSearch-DeBxGj4a.cjs → WidgetSymbolSearch-8HMKUv2P.cjs} +2 -2
  28. package/dist/{WidgetSymbolSearch-DeBxGj4a.cjs.map → WidgetSymbolSearch-8HMKUv2P.cjs.map} +1 -1
  29. package/dist/{WidgetSymbolSearch-WOskcBJN.js → WidgetSymbolSearch-BdI1egoq.js} +2 -2
  30. package/dist/{WidgetSymbolSearch-WOskcBJN.js.map → WidgetSymbolSearch-BdI1egoq.js.map} +1 -1
  31. package/dist/{i18n-Ci4pfuXw.js → i18n-BMzvClhl.js} +11 -1
  32. package/dist/i18n-BMzvClhl.js.map +1 -0
  33. package/dist/i18n-Dq-eMISy.cjs +2 -0
  34. package/dist/i18n-Dq-eMISy.cjs.map +1 -0
  35. package/dist/index.cjs +1 -1
  36. package/dist/index.js +1 -1
  37. package/dist/{indicatorInputWords-BfWHWL0K.cjs → indicatorInputWords-DGObUFDn.cjs} +2 -2
  38. package/dist/{indicatorInputWords-BfWHWL0K.cjs.map → indicatorInputWords-DGObUFDn.cjs.map} +1 -1
  39. package/dist/{indicatorInputWords-DpkxBon4.js → indicatorInputWords-Dn_gEPug.js} +2 -2
  40. package/dist/{indicatorInputWords-DpkxBon4.js.map → indicatorInputWords-Dn_gEPug.js.map} +1 -1
  41. package/dist/widget/ChartWidget.d.ts +9 -1
  42. package/dist/widget/ChartWidget.d.ts.map +1 -1
  43. package/dist/widget/WidgetChartSettings.d.ts +2 -0
  44. package/dist/widget/WidgetChartSettings.d.ts.map +1 -1
  45. package/dist/widget/WidgetDrawingSidebar.d.ts +27 -0
  46. package/dist/widget/WidgetDrawingSidebar.d.ts.map +1 -1
  47. package/dist/widget/WidgetSettings.d.ts.map +1 -1
  48. package/dist/widget/chartSettingsStore.d.ts.map +1 -1
  49. package/dist/widget/index.cjs +12 -4
  50. package/dist/widget/index.cjs.map +1 -1
  51. package/dist/widget/index.js +1157 -1034
  52. package/dist/widget/index.js.map +1 -1
  53. package/dist/widget/locales/ar.d.ts.map +1 -1
  54. package/dist/widget/locales/cs.d.ts.map +1 -1
  55. package/dist/widget/locales/da.d.ts.map +1 -1
  56. package/dist/widget/locales/de.d.ts.map +1 -1
  57. package/dist/widget/locales/el.d.ts.map +1 -1
  58. package/dist/widget/locales/en.d.ts +5 -0
  59. package/dist/widget/locales/en.d.ts.map +1 -1
  60. package/dist/widget/locales/es.d.ts.map +1 -1
  61. package/dist/widget/locales/et.d.ts.map +1 -1
  62. package/dist/widget/locales/fa.d.ts.map +1 -1
  63. package/dist/widget/locales/fr.d.ts.map +1 -1
  64. package/dist/widget/locales/he.d.ts.map +1 -1
  65. package/dist/widget/locales/hu.d.ts.map +1 -1
  66. package/dist/widget/locales/id.d.ts.map +1 -1
  67. package/dist/widget/locales/index.cjs +1 -1
  68. package/dist/widget/locales/index.cjs.map +1 -1
  69. package/dist/widget/locales/index.js +141 -1
  70. package/dist/widget/locales/index.js.map +1 -1
  71. package/dist/widget/locales/it.d.ts.map +1 -1
  72. package/dist/widget/locales/ja.d.ts.map +1 -1
  73. package/dist/widget/locales/ko.d.ts.map +1 -1
  74. package/dist/widget/locales/ms.d.ts.map +1 -1
  75. package/dist/widget/locales/nb.d.ts.map +1 -1
  76. package/dist/widget/locales/nl.d.ts.map +1 -1
  77. package/dist/widget/locales/pl.d.ts.map +1 -1
  78. package/dist/widget/locales/pt.d.ts.map +1 -1
  79. package/dist/widget/locales/ro.d.ts.map +1 -1
  80. package/dist/widget/locales/ru.d.ts.map +1 -1
  81. package/dist/widget/locales/sk.d.ts.map +1 -1
  82. package/dist/widget/locales/sv.d.ts.map +1 -1
  83. package/dist/widget/locales/th.d.ts.map +1 -1
  84. package/dist/widget/locales/tr.d.ts.map +1 -1
  85. package/dist/widget/locales/vi.d.ts.map +1 -1
  86. package/dist/widget/locales/zh.d.ts.map +1 -1
  87. package/dist/widget/locales/zhHant.d.ts.map +1 -1
  88. package/dist/widget/types.d.ts +4 -0
  89. package/dist/widget/types.d.ts.map +1 -1
  90. package/dist/widget/widgetConfig.d.ts.map +1 -1
  91. package/dist/{widgetLocales-D-QYU_9U.js → widgetLocales-BX7VRdl5.js} +2 -1
  92. package/dist/widgetLocales-BX7VRdl5.js.map +1 -0
  93. package/dist/widgetLocales-C_XTkni5.cjs +2 -0
  94. package/dist/widgetLocales-C_XTkni5.cjs.map +1 -0
  95. package/package.json +3 -3
  96. package/dist/LayoutSession-C4Lp1X6t.js.map +0 -1
  97. package/dist/LayoutSession-CeayqT91.cjs +0 -2
  98. package/dist/LayoutSession-CeayqT91.cjs.map +0 -1
  99. package/dist/WidgetSettings-CkvK-3d9.js.map +0 -1
  100. package/dist/WidgetSettings-bO72SYQ6.cjs.map +0 -1
  101. package/dist/i18n-Ci4pfuXw.js.map +0 -1
  102. package/dist/i18n-hMXCt55K.cjs +0 -2
  103. package/dist/i18n-hMXCt55K.cjs.map +0 -1
  104. package/dist/widgetLocales-D-QYU_9U.js.map +0 -1
  105. package/dist/widgetLocales-fhdzLs5l.cjs +0 -2
  106. package/dist/widgetLocales-fhdzLs5l.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LayoutSession-Cs8SJ1LU.cjs","names":[],"sources":["../src/regularHours.ts","../src/chartA11y.ts","../src/charts/ChartTypeStrategy.ts","../src/state/AutoSaveScheduler.ts","../src/DataManager.ts","../src/SeriesStore.ts","../src/model/PriceFit.ts","../src/model/DisplaySeries.ts","../src/model/PaneLayout.ts","../src/model/IndicatorHistory.ts","../src/model/IndicatorPanes.ts","../src/model/IndicatorSetup.ts","../src/model/SymbolRequests.ts","../src/model/DrawingPanes.ts","../src/HistoryPager.ts","../src/ThemeManager.ts","../src/style/styleOptions.ts","../src/layout/LayoutManager.ts","../src/layout/priceAxisWidth.ts","../src/layout/labelWidths.ts","../src/interaction/verticalPanGate.ts","../src/plugins/contracts.ts","../src/plugins/registry.ts","../src/plugins/PluginManager.ts","../src/trading/wireExecution.ts","../src/replay/ReplayStepLoader.ts","../src/replay/intrabarPath.ts","../src/replay/replayStep.ts","../src/model/AlertWatch.ts","../src/model/ReplayMarks.ts","../src/Chart.ts","../src/io/parseOHLCV.ts","../src/io/DragDropImporter.ts","../src/state/layoutStorage.ts","../src/state/LayoutSession.ts"],"sourcesContent":["import type { SymbolInfo } from '@tradecanvas/commons';\nimport { barTimeUnit, isValidTimeZone, sessionMinute, symbolTimeZone } from '@tradecanvas/commons';\nimport { isRegularSession } from '@tradecanvas/core';\n\nconst DAY_MS = 86_400_000;\n/** Bars looked at to tell the interval (a weekend gap between two of them doesn't fool it). */\nconst SPACING_SAMPLE = 10;\n\n/**\n * Which bar times fall in `info`'s regular hours, for bars like `data`'s\n * (by the time each bar opens). Null when there is nothing to leave out:\n * the symbol has no hours or no zone, or the bars are a day or longer.\n */\nexport function regularHoursFilter(\n info: SymbolInfo | null,\n data: readonly { time: number }[],\n): ((time: number) => boolean) | null {\n const zone = symbolTimeZone(info);\n if (!zone || !isValidTimeZone(zone) || data.length < 2) return null;\n const windows = (info?.sessions ?? []).flatMap((session) => {\n const startMinute = sessionMinute(session.start);\n const endMinute = sessionMinute(session.end);\n return startMinute === null || endMinute === null ? [] : [{ startMinute, endMinute }];\n });\n if (windows.length === 0) return null;\n const unit = barTimeUnit(data);\n let spacing = Infinity;\n for (let i = Math.max(1, data.length - SPACING_SAMPLE); i < data.length; i++) {\n const gap = (data[i].time - data[i - 1].time) * unit;\n if (gap > 0 && gap < spacing) spacing = gap;\n }\n if (!(spacing < DAY_MS)) return null;\n const config = { timeZone: zone, windows, startMinute: windows[0].startMinute, endMinute: windows[windows.length - 1].endMinute };\n return (time) => isRegularSession(time * unit, config);\n}\n","import type { ChartA11yLabels } from '@tradecanvas/commons';\n\nexport type { ChartA11yLabels };\n\nexport const DEFAULT_A11Y_LABELS: ChartA11yLabels = {\n role: 'chart',\n summary: '{what}. Last price {close}.',\n empty: 'Chart, no data.',\n view: 'Showing {count} bars, {from} to {to}. High {high}, low {low}.',\n bar: '{time}: open {open}, high {high}, low {low}, close {close}, volume {volume}.',\n keys: 'Arrow keys scroll and zoom, Home and End go to the start and the end. Comma and period read the bars one at a time.',\n};\n\n/** What the chart tells its reader, asked when needed. */\nexport interface ChartA11ySource {\n symbol: () => string;\n timeframe: () => string;\n chartType: () => string;\n bars: () => readonly { time: number; open: number; high: number; low: number; close: number; volume?: number }[];\n visibleRange: () => { from: number; to: number };\n formatPrice: (price: number) => string;\n formatTime: (time: number) => string;\n /** Show the bar being read (a time crosshair); null clears it. */\n showBar: (time: number | null) => void;\n}\n\nconst fill = (template: string, values: Record<string, string | number>): string =>\n template.replace(/\\{(\\w+)\\}/g, (m, name: string) => (name in values ? String(values[name]) : m));\n\nconst A11Y_ATTRIBUTES = ['role', 'aria-roledescription', 'aria-label', 'aria-describedby'] as const;\n\n/** Wait after the last key before saying what is on screen. */\nconst VIEW_ANNOUNCE_MS = 400;\n/** The summary follows live data at most this often. */\nconst SUMMARY_MS = 1000;\n\n/**\n * The chart for screen readers: the container is an application with a\n * summary (symbol, type, timeframe, last price) and key help; after the keys\n * move the view it says what is on screen, and comma and period read the\n * bars one at a time.\n */\nexport class ChartA11y {\n private readonly live: HTMLDivElement;\n private readonly help: HTMLDivElement;\n private labels: ChartA11yLabels;\n private cursor: number | null = null;\n private viewTimer: ReturnType<typeof setTimeout> | null = null;\n /** The container's own attributes before, put back on destroy. */\n private readonly before = new Map<string, string | null>();\n private summaryAt = 0;\n private summaryTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly onBlur = () => {\n this.cursor = null;\n this.source.showBar(null);\n };\n\n constructor(\n private readonly container: HTMLElement,\n private readonly source: ChartA11ySource,\n labels: Partial<ChartA11yLabels> = {},\n ) {\n this.labels = { ...DEFAULT_A11Y_LABELS, ...labels };\n for (const attr of A11Y_ATTRIBUTES) this.before.set(attr, container.getAttribute(attr));\n container.setAttribute('role', 'application');\n\n this.help = hidden('div', 'tc-sr-only');\n this.help.id = `tc-chart-keys-${Math.random().toString(36).slice(2, 10)}`;\n container.setAttribute('aria-describedby', this.help.id);\n\n this.live = hidden('div', 'tc-sr-only tc-sr-live');\n this.live.setAttribute('aria-live', 'polite');\n this.live.setAttribute('aria-atomic', 'true');\n\n container.append(this.help, this.live);\n container.addEventListener('blur', this.onBlur);\n this.applyLabels();\n }\n\n setLabels(labels: Partial<ChartA11yLabels>): void {\n this.labels = { ...DEFAULT_A11Y_LABELS, ...labels };\n this.applyLabels();\n }\n\n /** The summary again (new data, type, symbol): at once, or soon after when live data streams. */\n refresh(force = false): void {\n const wait = SUMMARY_MS - (Date.now() - this.summaryAt);\n // The first bars are told at once; a stream's ticks at most once a second.\n if (force || wait <= 0 || this.container.getAttribute('aria-label') === this.labels.empty) {\n if (this.summaryTimer) clearTimeout(this.summaryTimer);\n this.summaryTimer = null;\n this.summaryAt = Date.now();\n this.container.setAttribute('aria-label', this.summary());\n return;\n }\n this.summaryTimer ??= setTimeout(() => {\n this.summaryTimer = null;\n this.refresh(true);\n }, wait);\n }\n\n /** The keys moved the view: soon, say what is on screen. */\n viewMoved(): void {\n this.cursor = null;\n if (this.viewTimer) clearTimeout(this.viewTimer);\n this.viewTimer = setTimeout(() => {\n this.viewTimer = null;\n this.say(this.viewText());\n }, VIEW_ANNOUNCE_MS);\n }\n\n /** Read the next (1) or previous (-1) bar; the first read starts at the last bar on screen. */\n readBar(step: 1 | -1): void {\n const bars = this.source.bars();\n if (bars.length === 0) {\n this.say(this.labels.empty);\n return;\n }\n const { to } = this.source.visibleRange();\n const start = Math.max(0, Math.min(bars.length - 1, Math.floor(to)));\n this.cursor = this.cursor === null ? start : Math.max(0, Math.min(bars.length - 1, this.cursor + step));\n const bar = bars[this.cursor];\n this.source.showBar(bar.time);\n const p = this.source.formatPrice;\n this.say(fill(this.labels.bar, {\n time: this.source.formatTime(bar.time),\n open: p(bar.open),\n high: p(bar.high),\n low: p(bar.low),\n close: p(bar.close),\n volume: formatVolume(bar.volume ?? 0),\n }));\n }\n\n destroy(): void {\n if (this.viewTimer) clearTimeout(this.viewTimer);\n if (this.summaryTimer) clearTimeout(this.summaryTimer);\n this.container.removeEventListener('blur', this.onBlur);\n for (const [attr, value] of this.before) {\n if (value === null) this.container.removeAttribute(attr);\n else this.container.setAttribute(attr, value);\n }\n this.help.remove();\n this.live.remove();\n }\n\n private applyLabels(): void {\n this.container.setAttribute('aria-roledescription', this.labels.role);\n this.help.textContent = this.labels.keys;\n this.refresh(true);\n }\n\n private summary(): string {\n const bars = this.source.bars();\n if (bars.length === 0) return this.labels.empty;\n const type = this.source.chartType();\n const what = [this.source.symbol(), this.labels.typeName ? this.labels.typeName(type) : type, this.source.timeframe()]\n .filter(Boolean)\n .join(', ');\n return fill(this.labels.summary, { what, close: this.source.formatPrice(bars[bars.length - 1].close) });\n }\n\n private viewText(): string {\n const bars = this.source.bars();\n if (bars.length === 0) return this.labels.empty;\n const range = this.source.visibleRange();\n const from = Math.max(0, Math.min(bars.length - 1, Math.ceil(range.from)));\n const to = Math.max(from, Math.min(bars.length - 1, Math.floor(range.to)));\n let high = -Infinity;\n let low = Infinity;\n for (let i = from; i <= to; i++) {\n high = Math.max(high, bars[i].high);\n low = Math.min(low, bars[i].low);\n }\n return fill(this.labels.view, {\n count: to - from + 1,\n from: this.source.formatTime(bars[from].time),\n to: this.source.formatTime(bars[to].time),\n high: this.source.formatPrice(high),\n low: this.source.formatPrice(low),\n });\n }\n\n private say(text: string): void {\n this.live.textContent = text;\n }\n}\n\n/** An element screen readers read and the eye doesn't see. */\nfunction hidden<K extends keyof HTMLElementTagNameMap>(tag: K, className: string): HTMLElementTagNameMap[K] {\n const el = document.createElement(tag);\n el.className = className;\n Object.assign(el.style, {\n position: 'absolute',\n width: '1px',\n height: '1px',\n margin: '-1px',\n padding: '0',\n overflow: 'hidden',\n clip: 'rect(0 0 0 0)',\n whiteSpace: 'nowrap',\n border: '0',\n });\n return el;\n}\n\nfunction formatVolume(v: number): string {\n return Number.isInteger(v) ? String(v) : v.toFixed(2);\n}\n","import type { ChartType, ChartTypeOptions, DataSeries } from '@tradecanvas/commons';\nimport { niceNumber } from '@tradecanvas/commons';\nimport {\n AreaRenderer,\n BarRenderer,\n BaselineRenderer,\n CandlestickRenderer,\n EquivolumeRenderer,\n HiLoRenderer,\n HLCAreaRenderer,\n HollowCandleRenderer,\n KagiRenderer,\n LineRenderer,\n LineWithMarkersRenderer,\n PointAndFigureRenderer,\n RenkoRenderer,\n StepLineRenderer,\n VolumeCandleRenderer,\n pointAndFigureColumns,\n toHeikinAshi,\n toKagi,\n toLineBreak,\n toRangeBars,\n toRenko,\n} from '@tradecanvas/core';\nimport type { ChartRendererInterface, PointAndFigureColumns, ReshapeMemo } from '@tradecanvas/core';\nimport type { ChartTypePlugin } from '../plugins/contracts.js';\n\n/**\n * Pure mapping from `ChartType` to the renderer class that draws it. Several\n * chart types reuse `CandlestickRenderer` against transformed data\n * (heikinAshi, lineBreak, rangeBars).\n */\nexport function createRendererFor(type: ChartType): ChartRendererInterface {\n switch (type) {\n case 'candlestick':\n case 'heikinAshi':\n case 'lineBreak':\n case 'rangeBars':\n return new CandlestickRenderer();\n case 'line':\n return new LineRenderer();\n case 'area':\n return new AreaRenderer();\n case 'bar':\n return new BarRenderer();\n case 'hollowCandle':\n return new HollowCandleRenderer();\n case 'baseline':\n return new BaselineRenderer();\n case 'renko':\n return new RenkoRenderer();\n case 'kagi':\n return new KagiRenderer();\n case 'pointAndFigure':\n return new PointAndFigureRenderer();\n case 'volumeCandles':\n return new VolumeCandleRenderer();\n case 'equivolume':\n return new EquivolumeRenderer();\n case 'hlcArea':\n return new HLCAreaRenderer();\n case 'stepLine':\n return new StepLineRenderer();\n case 'lineWithMarkers':\n return new LineWithMarkersRenderer();\n case 'hiLo':\n return new HiLoRenderer();\n default:\n return new CandlestickRenderer();\n }\n}\n\n/**\n * Transform raw OHLC bars into the display series for a given chart type,\n * with its settings (`options`; what isn't set is worked out from the data).\n * `step` is the market's price step (`rangeBarsStep`), when there is one:\n * the automatic range of range bars is a multiple of it. `memo` keeps the\n * size a reshaped type was raised to, for the next tick (`ReshapeMemo`).\n * Returns the input unchanged for chart types that render raw OHLC directly.\n *\n * Centralized so adding a new chart type only requires editing one file.\n */\nexport function transformDisplayData(\n type: ChartType,\n raw: DataSeries,\n options: ChartTypeOptions = {},\n step?: number,\n memo?: ReshapeMemo,\n): DataSeries {\n if (raw.length === 0) return raw;\n\n switch (type) {\n case 'heikinAshi':\n return toHeikinAshi(raw);\n case 'renko': {\n const box = options.renko?.boxSize;\n return typeof box === 'number'\n ? toRenko(raw, { brickSize: box }, memo)\n : toRenko(raw, { brickSize: 0, useATR: true, atrPeriod: options.renko?.atrPeriod ?? 14 }, memo);\n }\n case 'lineBreak':\n return toLineBreak(raw, options.lineBreak?.lines ?? 3);\n case 'kagi':\n return toKagi(raw, options.kagi?.reversal ?? 4, options.kagi?.reversalType ?? 'percent', memo);\n case 'pointAndFigure':\n return pointAndFigureDisplay(raw, options, memo).columns;\n case 'rangeBars':\n // The bar still forming is drawn too, so the chart ends at the last price and is never blank.\n return toRangeBars(raw, { rangeSize: rangeBarsSize(raw, options, step), includeForming: true }, memo);\n default:\n return raw;\n }\n}\n\n/** Chart types that materially transform raw OHLC into a different series. */\nexport function isTransformedChartType(type: ChartType): boolean {\n switch (type) {\n case 'heikinAshi':\n case 'renko':\n case 'lineBreak':\n case 'kagi':\n case 'pointAndFigure':\n case 'rangeBars':\n return true;\n default:\n return false;\n }\n}\n\n/**\n * Transformed chart types that redraw the series into bars of their own\n * (bricks, lines, columns), so a drawn bar is not a data bar. Heikin-Ashi\n * keeps one bar per data bar.\n */\nexport function isReshapedChartType(type: ChartType): boolean {\n return type !== 'heikinAshi' && isTransformedChartType(type);\n}\n\n/**\n * A point & figure chart's columns and the box they were built on: the box\n * set, or the automatic one (`pointAndFigureBox`), raised when the columns\n * would pass 100,000, so the renderer draws the boxes the columns were built with.\n */\nexport function pointAndFigureDisplay(raw: DataSeries, options: ChartTypeOptions = {}, memo?: ReshapeMemo): PointAndFigureColumns {\n return pointAndFigureColumns(raw, pointAndFigureBox(raw, options), options.pointAndFigure?.reversal ?? 3, memo);\n}\n\n/** Boxes the automatic box makes of the series' price range: enough for columns that fill the view. */\nconst POINT_AND_FIGURE_AUTO_BOXES = 50;\n/** The automatic box of a series too flat to measure: about this share of the last close. */\nconst POINT_AND_FIGURE_PRICE_SHARE = 0.01;\n\n/**\n * The box of a point & figure chart: the one set, as it is, or for 'auto' a\n * fiftieth of the range from the lowest low to the highest high, rounded to\n * a 1-2-5 number, so the columns span the scale whatever the prices (a\n * share of the price would make one column of a stock that moved 7%). The\n * bar still forming is left out, so a tick doesn't reshape the history. A\n * series with no range: a hundredth of the last close, or 1 at 0.\n */\nexport function pointAndFigureBox(raw: DataSeries, options: ChartTypeOptions = {}): number {\n const box = options.pointAndFigure?.boxSize;\n if (typeof box === 'number') return box;\n const range = priceRange(raw, raw.length > 1 ? raw.length - 1 : raw.length);\n if (range > 0) return cleanRange(niceNumber(range / POINT_AND_FIGURE_AUTO_BOXES, true));\n return Math.abs(raw[raw.length - 1]?.close ?? 0) * POINT_AND_FIGURE_PRICE_SHARE || 1;\n}\n\n/** The highest high less the lowest low of the first `end` bars; 0 without a finite one. */\nfunction priceRange(raw: DataSeries, end: number): number {\n let high = -Infinity;\n let low = Infinity;\n for (let i = 0; i < end; i++) {\n if (raw[i].high > high) high = raw[i].high;\n if (raw[i].low < low) low = raw[i].low;\n }\n const range = high - low;\n return Number.isFinite(range) ? range : 0;\n}\n\n/** Closed bars the automatic range of range bars averages the true range of. */\nconst RANGE_BARS_AUTO_BARS = 100;\n/** Bars it takes to have a closed bar with a true range: fewer use a share of the price. */\nconst RANGE_BARS_MIN_BARS = 3;\n/** The automatic range without a true range to go by: about this share of the last close. */\nconst RANGE_BARS_PRICE_SHARE = 0.01;\n/** Digits a range is kept to, so five steps of 0.1 read 0.5. */\nconst RANGE_DIGITS = 15;\n\nconst cleanRange = (size: number): number => Number(size.toPrecision(RANGE_DIGITS));\n\n/**\n * The range of range bars: the one set, as it is, or for 'auto' the mean true\n * range of the last 100 closed bars (all of them when there are fewer; the\n * bar still forming is left out, so a tick doesn't reshape the history). It\n * is rounded to a 1-2-5 number, so it holds still as bars close, then to a\n * whole number of price steps (`step`, one at least), so bars close on prices\n * the market can trade. With 2 bars or fewer, about a hundredth of the last close.\n * `toRangeBars` raises either kind when the closed bars would make more than\n * 100,000 range bars, or the last bar that many on its own; one under half\n * the 15th significant digit of the largest price closes no bar.\n */\nexport function rangeBarsSize(raw: DataSeries, options: ChartTypeOptions = {}, step?: number): number {\n const range = options.rangeBars?.range;\n if (typeof range === 'number') return range;\n const trueRange = raw.length >= RANGE_BARS_MIN_BARS ? meanTrueRange(raw) : 0;\n const lastClose = Math.abs(raw[raw.length - 1]?.close ?? 0);\n const size = trueRange > 0 ? trueRange : lastClose * RANGE_BARS_PRICE_SHARE || 1;\n return onSteps(cleanRange(niceNumber(size, true)), step);\n}\n\n/** The closed bars the automatic range is measured on, `from` to before `end`: the last 100, the bar still forming left out. */\nfunction autoWindow(raw: DataSeries): { from: number; end: number } {\n const end = raw.length - 1;\n return { from: Math.max(0, end - RANGE_BARS_AUTO_BARS), end };\n}\n\n/** The mean true range of the closed bars the automatic range is measured on. */\nfunction meanTrueRange(raw: DataSeries): number {\n const { from, end } = autoWindow(raw);\n let sum = 0;\n for (let i = from; i < end; i++) {\n const { high, low } = raw[i];\n const prev = i > 0 ? raw[i - 1].close : low;\n sum += Math.max(high - low, Math.abs(high - prev), Math.abs(low - prev));\n }\n return end > from ? sum / (end - from) : 0;\n}\n\n/**\n * The price step automatic range bars go on: the coarsest the market has over\n * the closed bars the range is measured on (on a market whose step depends on\n * the price, the step at their highest or their lowest price), so a last price\n * that crosses a band edge doesn't change the range of the whole history. With\n * one bar, the step at its close; undefined when the market has no step.\n */\nexport function rangeBarsStep(raw: DataSeries, stepAt: (price: number) => number | undefined): number | undefined {\n const { from, end } = autoWindow(raw);\n if (end <= from) return stepAt(raw[raw.length - 1]?.close ?? 0);\n let high = -Infinity;\n let low = Infinity;\n for (let i = from; i < end; i++) {\n high = Math.max(high, raw[i].high);\n low = Math.min(low, raw[i].low);\n }\n return coarser(stepAt(high), stepAt(low));\n}\n\nfunction coarser(a: number | undefined, b: number | undefined): number | undefined {\n if (a === undefined) return b;\n return b === undefined ? a : Math.max(a, b);\n}\n\n/** `size` as a whole number of price steps, one at least; as it is without a step. */\nfunction onSteps(size: number, step?: number): number {\n if (step === undefined || !(step > 0)) return size;\n return cleanRange(Math.max(1, Math.round(size / step)) * step);\n}\n\n/** Look up a registered custom chart-type plugin by its `type` string. */\nexport type ChartTypeLookup = (type: string) => ChartTypePlugin | undefined;\n\n/**\n * Resolve the renderer for a chart type, preferring a registered\n * `ChartTypePlugin` over the built-in mapping.\n */\nexport function resolveRenderer(type: string, lookup?: ChartTypeLookup): ChartRendererInterface {\n const plugin = lookup?.(type);\n if (plugin) return plugin.createRenderer();\n return createRendererFor(type as ChartType);\n}\n\n/**\n * Resolve the display series for a chart type, preferring a plugin's\n * `transform` over the built-in transforms.\n */\nexport function resolveDisplayData(\n type: string,\n raw: DataSeries,\n lookup?: ChartTypeLookup,\n options?: ChartTypeOptions,\n step?: number,\n memo?: ReshapeMemo,\n): DataSeries {\n if (raw.length === 0) return raw;\n const plugin = lookup?.(type);\n if (plugin?.transform) return plugin.transform(raw);\n return transformDisplayData(type as ChartType, raw, options, step, memo);\n}\n","/**\n * Debounced auto-save scheduler. Coalesces a burst of `schedule()` calls into\n * a single `save(key)` invocation after `delayMs` of quiet time. Pure state\n * machine — owner injects the actual save callback so this stays testable.\n */\nexport class AutoSaveScheduler {\n private timer: ReturnType<typeof setTimeout> | null = null;\n private delayMs = 0;\n private key: string | null = null;\n\n constructor(private save: (key: string) => void) {}\n\n /** Enable auto-save with the given storage key and quiet-time debounce. */\n enable(key: string, delayMs: number): void {\n this.key = key;\n this.delayMs = delayMs;\n }\n\n /** Disable auto-save and cancel any pending fire. */\n disable(): void {\n this.key = null;\n this.delayMs = 0;\n this.cancel();\n }\n\n /**\n * Reset the quiet-time window. If currently disabled (no key or delay ≤ 0),\n * this is a no-op. Subsequent calls within the window restart the timer.\n */\n schedule(): void {\n if (this.delayMs <= 0 || !this.key) return;\n if (this.timer) clearTimeout(this.timer);\n this.timer = setTimeout(() => {\n this.timer = null;\n if (this.key) this.save(this.key);\n }, this.delayMs);\n }\n\n /** Cancel any pending fire without changing enabled state. */\n cancel(): void {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n isEnabled(): boolean {\n return this.delayMs > 0 && this.key !== null;\n }\n\n hasPending(): boolean {\n return this.timer !== null;\n }\n}\n","import type { OHLCBar, DataSeries } from '@tradecanvas/commons';\nimport { barTimeUnit, mergeBar, normalizeBarTime } from '@tradecanvas/commons';\n\n/** Whether a bar is one the chart takes: its time and prices numbers, none below zero, its high not under its low, its volume (if any) not below zero. */\nexport function isValidBar(bar: OHLCBar): boolean {\n if (!bar || typeof bar.time !== 'number' || !Number.isFinite(bar.time)) return false;\n const { open, high, low, close } = bar;\n // Reject NaN / Infinity\n if (!isFinite(open) || !isFinite(high) || !isFinite(low) || !isFinite(close)) return false;\n // Reject negative prices\n if (open < 0 || high < 0 || low < 0 || close < 0) return false;\n // High must be >= low\n if (high < low) return false;\n // Volume (optional) must be non-negative if present\n if (bar.volume !== undefined && (!isFinite(bar.volume) || bar.volume < 0)) return false;\n return true;\n}\n\n/** Whether a valid bar already satisfies everything `sanitizeBar` enforces. */\nfunction isSanitized(bar: OHLCBar): boolean {\n return bar.volume !== undefined\n && bar.high >= bar.open && bar.high >= bar.close\n && bar.low <= bar.open && bar.low <= bar.close;\n}\n\n/** Sanitize a bar: clamp high/low to envelope OHLC values, a missing volume 0. */\nexport function sanitizeBar(bar: OHLCBar): OHLCBar {\n return {\n ...bar,\n high: Math.max(bar.open, bar.high, bar.low, bar.close),\n low: Math.min(bar.open, bar.high, bar.low, bar.close),\n volume: bar.volume !== undefined ? Math.max(0, bar.volume) : 0,\n };\n}\n\n/** Whether two bars have the same time, prices and volume. */\nexport function sameBar(a: OHLCBar, b: OHLCBar): boolean {\n return a === b || (a.time === b.time && a.open === b.open && a.high === b.high && a.low === b.low && a.close === b.close && a.volume === b.volume);\n}\n\n/**\n * The index of the bar at `time` in `series` (in its unit), looking back from\n * the newest: corrections are to recent bars, so this costs their distance\n * from the end. -1 when no bar has that time.\n */\nexport function lastIndexOfTime(series: readonly { time: number }[], time: number): number {\n for (let i = series.length - 1; i >= 0; i--) {\n if (series[i].time === time) return i;\n if (series[i].time < time) return -1;\n }\n return -1;\n}\n\n/** What `DataManager.replaceBar` did: the bar's index (-1: not loaded, or a bar it can't take), and whether its values changed. */\nexport interface BarReplacement {\n readonly index: number;\n readonly changed: boolean;\n}\n\nconst NOT_LOADED: BarReplacement = { index: -1, changed: false };\n\n/**\n * `time` in the unit of `series`. A series has one unit, read off its newest\n * bar; a bar or tick arriving in the other (a stream in milliseconds on a\n * series in seconds) is converted, or the series would read as that unit and\n * date every other bar wrong. A time on its own tells its unit by its size\n * (milliseconds from 1973 on).\n */\nexport function timeInUnitOf(series: readonly { time: number }[], time: number): number {\n if (series.length === 0) return time;\n const ms = normalizeBarTime(time);\n return barTimeUnit(series) === 1 ? ms : ms / 1000;\n}\n\n/** A bar or tick with its time in the unit of `series`: itself when it is already. */\nexport function withTimeOf<T extends { time: number }>(series: readonly { time: number }[], item: T): T {\n const time = timeInUnitOf(series, item.time);\n return time === item.time ? item : { ...item, time };\n}\n\nexport class DataManager {\n private data: DataSeries = [];\n /** Moves on every write to the bars, in place or not. */\n private changes = 0;\n\n private withSeriesTime<T extends { time: number }>(item: T): T {\n return withTimeOf(this.data, item);\n }\n\n getData(): DataSeries {\n return this.data;\n }\n\n /**\n * Moves on every write to the bars: what is made from them (the series a\n * chart type draws) is current while it is the same.\n */\n version(): number {\n return this.changes;\n }\n\n setData(data: DataSeries): void {\n // Drop invalid bars and sanitize the rest in one pass. Bars that are\n // already well-formed (the norm for exchange data) are kept as-is rather\n // than copied — bar objects are never mutated here, only replaced, so\n // sharing them with the caller is safe, and it saves allocating a fresh\n // object per bar on every symbol/timeframe switch.\n const out: OHLCBar[] = [];\n for (let i = 0; i < data.length; i++) {\n const bar = data[i];\n if (!isValidBar(bar)) continue;\n out.push(isSanitized(bar) ? bar : sanitizeBar(bar));\n }\n this.data = out;\n this.changes++;\n }\n\n /**\n * Add older bars in front: those before the first loaded bar, in time\n * order, one per time, repaired like `setData`. Returns how many were added.\n */\n prependBars(bars: readonly OHLCBar[]): number {\n const first = this.data.length > 0 ? this.data[0].time : Infinity;\n const older: OHLCBar[] = [];\n for (let i = 0; i < bars.length; i++) {\n if (!isValidBar(bars[i])) continue;\n const bar = this.withSeriesTime(bars[i]);\n if (bar.time >= first) continue;\n older.push(isSanitized(bar) ? bar : sanitizeBar(bar));\n }\n if (older.length === 0) return 0;\n older.sort((a, b) => a.time - b.time);\n let kept = 0;\n for (let i = 0; i < older.length; i++) {\n if (kept > 0 && older[kept - 1].time === older[i].time) continue;\n older[kept++] = older[i];\n }\n older.length = kept;\n this.data = older.concat(this.data);\n this.changes++;\n return kept;\n }\n\n appendBar(bar: OHLCBar): void {\n if (!isValidBar(bar)) return;\n this.data.push(sanitizeBar(this.withSeriesTime(bar)));\n this.changes++;\n }\n\n updateLastBar(bar: OHLCBar): void {\n if (!isValidBar(bar)) return;\n const sanitized = sanitizeBar(this.withSeriesTime(bar));\n this.changes++;\n if (this.data.length === 0) {\n this.data.push(sanitized);\n return;\n }\n this.data[this.data.length - 1] = sanitized;\n }\n\n /**\n * The loaded bar with the same time, given new values, repaired as\n * `setData` repairs a bar. A bar that, repaired, is the one loaded is\n * not written (`changed: false`): a feed sending its closed bars again,\n * without volume or with a high under its close, costs nothing.\n */\n replaceBar(bar: OHLCBar): BarReplacement {\n if (!isValidBar(bar)) return NOT_LOADED;\n const timed = this.withSeriesTime(bar);\n const index = lastIndexOfTime(this.data, timed.time);\n if (index < 0) return NOT_LOADED;\n const repaired = sanitizeBar(timed);\n if (sameBar(this.data[index], repaired)) return { index, changed: false };\n this.data[index] = repaired;\n this.changes++;\n return { index, changed: true };\n }\n\n updateLastBarFromTick(tick: { price: number; volume?: number; time: number }): void {\n if (this.data.length === 0) return;\n if (!isFinite(tick.price) || tick.price < 0) return;\n if (tick.volume !== undefined && (!isFinite(tick.volume) || tick.volume < 0)) return;\n this.data[this.data.length - 1] = mergeBar(this.data[this.data.length - 1], this.withSeriesTime(tick));\n this.changes++;\n }\n\n getLength(): number {\n return this.data.length;\n }\n\n clear(): void {\n this.data = [];\n this.changes++;\n }\n}\n","import type { DataSeries, OHLCBar } from '@tradecanvas/commons';\nimport { normalizeBarTime } from '@tradecanvas/commons';\nimport { DataManager, isValidBar, lastIndexOfTime, sameBar, sanitizeBar, timeInUnitOf, withTimeOf } from './DataManager.js';\n\n/** Which bar times the chart shows, for bars like these; null: all of them. */\nexport type HoursFilter = (bars: readonly { time: number }[]) => ((time: number) => boolean) | null;\n\n/** What a write did with a bar. */\nexport interface SeriesWrite {\n /**\n * Where the bar went: kept aside during a replay, into the whole series\n * only (outside the hours shown), onto the bars shown, or nowhere (older\n * than the last, from a stream).\n */\n readonly to: 'aside' | 'hidden' | 'shown' | 'none';\n /** The number of bars shown before it went on, after any filtering it set off. */\n readonly before: number;\n /** Whether the bars shown were filtered again first: any copy of them is stale. */\n readonly refiltered: boolean;\n}\n\n/** What a correction did with a bar (`replace`). */\nexport interface SeriesCorrection {\n /**\n * Where the bar was: in the live series kept aside during a replay, in the\n * whole series only (outside the hours shown), among the bars shown, or\n * nowhere (no loaded bar has its time, or the chart can't take it).\n */\n readonly to: 'aside' | 'hidden' | 'shown' | 'none';\n /** Its index among the bars shown; -1 when it isn't one of them. */\n readonly index: number;\n /** Whether its values changed: false for the bar loaded sent again, after the repairs every write makes. */\n readonly changed: boolean;\n}\n\nconst NOWHERE: SeriesCorrection = { to: 'none', index: -1, changed: false };\n\n/** What a batch of new bars did: how many the chart shows, from how many before. */\nexport interface SeriesBatch {\n /** Bars that went onto the bars shown (none during a replay). */\n readonly shown: number;\n /** The number of bars shown before them, after any filtering they set off. */\n readonly before: number;\n readonly refiltered: boolean;\n /** Whether they were kept aside, a replay running. */\n readonly aside: boolean;\n}\n\n/** What a page of older bars did: the bars it added to those shown, and all it took, hidden ones too. */\nexport interface SeriesPage {\n readonly added: number;\n readonly taken: number;\n}\n\n/**\n * How the live series changed during a replay, other than at its end: a\n * new one (`series`: `load`, a reconnect's snapshot), older bars in front\n * (`older`, with how many it took), the hours shown (`hours`), or a bar\n * corrected (`bar`, at its time). A replay follows each by time.\n */\nexport type LiveChange =\n | { readonly kind: 'series' }\n | { readonly kind: 'older'; readonly taken: number }\n | { readonly kind: 'hours' }\n | { readonly kind: 'bar'; readonly time: number };\n\n/** A bar onto a series by its time: the last one again, a new one after it, or an older one. */\nexport function barPlacement(series: readonly OHLCBar[], bar: OHLCBar): 'append' | 'update' | 'stale' {\n const last = series[series.length - 1];\n if (!last) return 'append';\n // Live bars are recent, so each time's own size tells its unit (a stream\n // in ms may feed a series in seconds).\n const at = normalizeBarTime(bar.time);\n const lastAt = normalizeBarTime(last.time);\n return at > lastAt ? 'append' : at === lastAt ? 'update' : 'stale';\n}\n\n/** Whether a bar's time and prices are all numbers. */\nexport function isWholeBar(b: OHLCBar): boolean {\n return [b.time, b.open, b.high, b.low, b.close].every(Number.isFinite);\n}\n\n/**\n * The chart's bars, and every way they change. It holds the bars the chart\n * shows; while the hours outside the symbol's regular ones are hidden, the\n * whole series beside them; and during a replay the live series, which every\n * live write goes to until the replay ends. Writes that take a bar say what\n * they did with it, for the chart to follow.\n */\nexport class SeriesStore {\n private readonly shown = new DataManager();\n /** Every bar, while some are hidden; null while all are shown. */\n private whole: OHLCBar[] | null = null;\n /** The live series during a replay, which the bars shown no longer follow. */\n private aside: DataManager | null = null;\n /** Bumped by every bar shown replaced in place (`replace`): what keeps a copy of the bars by their array writes them again. */\n private shownRevision = 0;\n\n /**\n * `onLiveChange` hears every change to the live series during a replay\n * other than at its end, whoever wrote it, so the replay can follow.\n */\n constructor(\n private readonly hoursFilter: HoursFilter,\n private readonly onLiveChange: (change: LiveChange) => void = () => {},\n ) {}\n\n /** The bars the chart shows. */\n bars(): DataSeries {\n return this.shown.getData();\n }\n\n length(): number {\n return this.shown.getLength();\n }\n\n /**\n * Moves when a bar shown was replaced in place, the array the same: a copy\n * kept by the array and a few bars it samples (the GPU's) is stale.\n */\n revision(): number {\n return this.shownRevision;\n }\n\n /** Moves on every write to the bars shown: see `DataManager.version`. */\n version(): number {\n return this.shown.version();\n }\n\n /** The live series: kept aside during a replay, else every bar, hidden ones too. */\n liveBars(): DataSeries {\n return this.aside?.getData() ?? this.whole ?? this.shown.getData();\n }\n\n // --- A whole series ---\n\n /**\n * A new series: the bars shown are those of `bars` in the hours shown.\n * During a replay it is the live series kept aside.\n */\n load(bars: DataSeries): void {\n if (this.aside) {\n this.aside.setData(bars);\n this.onLiveChange({ kind: 'series' });\n return;\n }\n this.shown.setData(this.regularOnly(bars));\n }\n\n /**\n * Older bars in front of the series held: those before its first, once\n * each. Returns how many the chart shows (`added`) and how many it took,\n * hidden ones too (`taken`).\n */\n prepend(bars: readonly OHLCBar[]): SeriesPage {\n if (this.aside) {\n const taken = this.aside.prependBars(bars);\n if (taken > 0) this.onLiveChange({ kind: 'older', taken });\n return { added: 0, taken };\n }\n let taken: number | null = null;\n let onShown: readonly OHLCBar[] = bars;\n if (this.whole) {\n const first = this.whole[0]?.time;\n const seen = new Set<number>();\n const older = bars\n .filter(isWholeBar)\n .map((b) => this.inSeriesUnit(b))\n .filter((b) => (first === undefined || b.time < first) && !seen.has(b.time) && seen.add(b.time))\n .sort((a, b) => a.time - b.time);\n this.whole = [...older, ...this.whole];\n const keep = this.hoursFilter(this.whole);\n taken = older.length;\n onShown = keep ? older.filter((b) => keep(b.time)) : older;\n }\n const added = this.shown.prependBars(onShown);\n return { added, taken: taken ?? added };\n }\n\n /**\n * A stream's snapshot for the series held (a reconnect): the older bars\n * paged in before it are kept. False when it doesn't reach back over them\n * (after a longer outage it starts later, and joining them would hide a\n * gap): load it instead.\n */\n mergeSnapshot(bars: DataSeries): boolean {\n if (this.aside) {\n // During a replay the live series kept aside takes it, merged the same way.\n this.aside.setData(mergedSnapshot(this.aside.getData(), bars) ?? bars);\n this.onLiveChange({ kind: 'series' });\n return true;\n }\n const merged = mergedSnapshot(this.whole ?? this.shown.getData(), bars);\n if (!merged) return false;\n this.shown.setData(this.regularOnly(merged));\n return true;\n }\n\n /** The bars shown again after the extended hours or the symbol's hours changed: whether they did. */\n refilter(): boolean {\n if (this.aside) {\n // The replay filters the live series again itself; its end brings the series back through the filter.\n this.onLiveChange({ kind: 'hours' });\n return false;\n }\n const all = this.whole ?? this.shown.getData().slice();\n if (!this.whole && !this.hoursFilter(all)) return false; // nothing hidden before or now\n this.shown.setData(this.regularOnly(all));\n return true;\n }\n\n // --- Live bars ---\n\n /** A new bar after the last, from the host: on the bars shown unless outside the hours shown. */\n append(given: OHLCBar): SeriesWrite {\n if (this.aside) {\n this.aside.appendBar(given);\n return this.wrote('aside', false);\n }\n const bar = this.inSeriesUnit(given);\n const refiltered = this.startWholeSeries(bar, 'append');\n if (!this.take(bar, 'append')) return this.wrote('hidden', refiltered);\n const before = this.shown.getLength();\n this.shown.appendBar(bar);\n return { to: 'shown', before, refiltered };\n }\n\n /** New bars after the last, from the host: the ones shown and what the first write set off. */\n appendMany(bars: readonly OHLCBar[]): SeriesBatch {\n if (this.aside) {\n for (const bar of bars) this.aside.appendBar(bar);\n return { shown: 0, before: this.shown.getLength(), refiltered: false, aside: true };\n }\n let refiltered = false;\n const onShown: OHLCBar[] = [];\n for (const given of bars) {\n const bar = this.inSeriesUnit(given);\n refiltered = this.startWholeSeries(bar, 'append') || refiltered;\n if (this.take(bar, 'append')) onShown.push(bar);\n }\n const before = this.shown.getLength();\n for (const bar of onShown) this.shown.appendBar(bar);\n return { shown: onShown.length, before, refiltered, aside: false };\n }\n\n /** The forming bar again, from the host. */\n updateLast(given: OHLCBar): SeriesWrite {\n if (this.aside) {\n this.aside.updateLastBar(given);\n return this.wrote('aside', false);\n }\n const bar = this.inSeriesUnit(given);\n const refiltered = this.startWholeSeries(bar, 'update');\n if (!this.take(bar, 'update')) return this.wrote('hidden', refiltered);\n const before = this.shown.getLength();\n this.shown.updateLastBar(bar);\n return { to: 'shown', before, refiltered };\n }\n\n /** A trade into the forming bar. One outside the hours shown moves no bar. */\n mergeTick(tick: { price: number; volume?: number; time: number }): SeriesWrite {\n if (this.aside) {\n this.aside.updateLastBarFromTick(tick);\n return this.wrote('aside', false);\n }\n const keep = this.whole ? this.hoursFilter(this.whole) : null;\n if (keep && this.whole && !keep(timeInUnitOf(this.whole, tick.time))) return this.wrote('hidden', false);\n const before = this.shown.getLength();\n this.shown.updateLastBarFromTick(tick);\n // While extended hours are hidden, the whole series keeps the same bar.\n const whole = this.whole;\n const merged = this.shown.getData()[this.shown.getLength() - 1];\n if (whole && merged && whole.length > 0 && whole[whole.length - 1].time === merged.time) whole[whole.length - 1] = merged;\n return { to: 'shown', before, refiltered: false };\n }\n\n /**\n * A stream's bar, placed by its time: the last bar again, a new one after\n * it, or none when it is older. During a replay it goes onto the live\n * series kept aside the same way.\n */\n placeStreamBar(given: OHLCBar): SeriesWrite {\n if (this.aside) {\n const how = barPlacement(this.aside.getData(), given);\n if (how === 'append') this.aside.appendBar(given);\n else if (how === 'update') this.aside.updateLastBar(given);\n return this.wrote(how === 'stale' ? 'none' : 'aside', false);\n }\n const bar = this.inSeriesUnit(given);\n const how = barPlacement(this.whole ?? this.shown.getData(), bar);\n if (how === 'stale') return this.wrote('none', false);\n const refiltered = this.startWholeSeries(bar, how);\n if (!this.take(bar, how)) return this.wrote('hidden', refiltered);\n const before = this.shown.getLength();\n const onShown = barPlacement(this.shown.getData(), bar);\n if (onShown === 'stale') return { to: 'none', before, refiltered };\n if (onShown === 'append') this.shown.appendBar(bar);\n else this.shown.updateLastBar(bar);\n return { to: 'shown', before, refiltered };\n }\n\n /**\n * A loaded bar again, with new values: the bar with the same time,\n * wherever it is, repaired as every write repairs a bar. During a replay\n * it is the live series kept aside; outside the hours shown, the whole\n * series only. Nowhere when no loaded bar has its time: a bar the chart\n * left out (it couldn't take it) isn't loaded either. Bar times don't\n * change, so the hours shown need no filtering again.\n */\n replace(given: OHLCBar): SeriesCorrection {\n if (this.aside) {\n const { index, changed } = this.aside.replaceBar(given);\n if (index < 0) return NOWHERE;\n if (changed) this.onLiveChange({ kind: 'bar', time: this.aside.getData()[index].time });\n return { to: 'aside', index: -1, changed };\n }\n if (!isValidBar(given)) return NOWHERE;\n const bar = this.inSeriesUnit(given);\n const whole = this.whole;\n const at = whole ? lastIndexOfTime(whole, bar.time) : -1;\n if (whole) {\n if (at < 0 || !isValidBar(whole[at])) return NOWHERE;\n const keep = this.hoursFilter(whole);\n if (keep && !keep(bar.time)) return this.replaceHidden(whole, at, bar);\n }\n const { index, changed } = this.shown.replaceBar(bar);\n if (index < 0) return NOWHERE;\n if (changed) {\n // The whole series holds the same bar as the one shown, as a tick leaves it.\n if (whole) whole[at] = this.shown.getData()[index];\n this.shownRevision++;\n }\n return { to: 'shown', index, changed };\n }\n\n // --- A replay ---\n\n /** A replay begins: the live series is kept aside, and the bars shown are the replay's. */\n beginReplay(): void {\n if (this.aside) return;\n this.aside = new DataManager();\n this.aside.setData(this.whole ?? this.shown.getData());\n }\n\n /** The replay ends: the live series kept aside, to load again; null without a replay. */\n endReplay(): DataSeries | null {\n const aside = this.aside;\n this.aside = null;\n return aside ? aside.getData() : null;\n }\n\n /** The bars of `all` the chart would show in the hours shown (no state kept). */\n inHoursShown(all: DataSeries): OHLCBar[] {\n const keep = this.hoursFilter(all);\n return keep ? all.filter((b) => keep(b.time)) : all.slice();\n }\n\n /** The replay's bars as far as it has come. */\n showReplayed(bars: DataSeries): void {\n this.shown.setData(bars);\n }\n\n /** The replay steps on: a bar after the last. */\n appendReplayed(bar: OHLCBar): void {\n this.shown.appendBar(bar);\n }\n\n /** The replay's forming bar again. */\n updateReplayed(bar: OHLCBar): void {\n this.shown.updateLastBar(bar);\n }\n\n // --- Hidden hours ---\n\n /**\n * A bar in the unit of the series held (the whole one while some bars are\n * hidden), so the whole series never mixes seconds and milliseconds.\n */\n private inSeriesUnit(bar: OHLCBar): OHLCBar {\n return withTimeOf(this.whole ?? this.shown.getData(), bar);\n }\n\n /** A bar outside the hours shown, corrected in the whole series only: it shows with the hours. */\n private replaceHidden(whole: OHLCBar[], at: number, bar: OHLCBar): SeriesCorrection {\n const repaired = sanitizeBar(bar);\n const changed = !sameBar(whole[at], repaired);\n if (changed) whole[at] = repaired;\n return { to: 'hidden', index: -1, changed };\n }\n\n private wrote(to: SeriesWrite['to'], refiltered: boolean): SeriesWrite {\n return { to, before: this.shown.getLength(), refiltered };\n }\n\n /** The bars of `all` shown; while some are hidden, `all` is kept as the whole series. */\n private regularOnly(all: DataSeries): DataSeries {\n const keep = this.hoursFilter(all);\n this.whole = keep ? all.slice() : null;\n return keep ? all.filter((b) => keep(b.time)) : all;\n }\n\n /**\n * Hidden hours with no whole series yet (too few bars to tell the\n * interval): start one now if the bar tells it. Whether that filtered the\n * bars shown.\n */\n private startWholeSeries(bar: OHLCBar, how: 'append' | 'update'): boolean {\n if (this.whole) return false;\n const data = this.shown.getData();\n // The interval is told by the last bars: no need to copy the rest.\n const tail = data.slice(-12);\n const all = how === 'append' || tail.length === 0 ? [...tail, bar] : [...tail.slice(0, -1), bar];\n if (!this.hoursFilter(all)) return false;\n this.whole = data.slice();\n const keep = this.hoursFilter(this.whole.length > 1 ? this.whole : all);\n if (!keep || !data.some((b) => !keep(b.time))) return false;\n this.shown.setData(data.filter((b) => keep(b.time)));\n return true;\n }\n\n /** A new or updated bar into the whole series (while one is kept): whether the chart shows it. */\n private take(bar: OHLCBar, how: 'append' | 'update'): boolean {\n const whole = this.whole;\n if (!whole) return true;\n if (how === 'append' || whole.length === 0) whole.push(bar);\n else whole[whole.length - 1] = bar;\n const keep = this.hoursFilter(whole);\n return !keep || keep(bar.time);\n }\n}\n\n/**\n * A reconnect's snapshot onto `data`: the bars before its first kept, the\n * rest its own. Null when it doesn't reach back over them (after a longer\n * outage it starts later, and joining them would hide a gap).\n */\nfunction mergedSnapshot(data: DataSeries, bars: DataSeries): DataSeries | null {\n const first = bars[0]?.time;\n let keep = 0;\n while (first !== undefined && keep < data.length && data[keep].time < first) keep++;\n if (keep === 0 || keep === data.length) return null;\n return data.slice(0, keep).concat(bars);\n}\n","import { PHI, computePriceRange, type DataSeries, type ViewportState } from '@tradecanvas/commons';\nimport { PRICE_SCALE_MARGIN } from '@tradecanvas/core';\n\n/** Between the volume band and the lowest candle. */\nconst VOLUME_GAP = PHI ** -7;\n\nexport interface PriceRange {\n min: number;\n max: number;\n}\n\n/** The part of the viewport a fit writes to. */\nexport interface FitTarget {\n isInvertScale(): boolean;\n setScaleMargins(margins: { top: number; bottom: number }): void;\n fitToPrices(min: number, max: number): void;\n}\n\n/** What else is on the price scale besides the bars, read at fit time. */\nexport interface FitSources {\n /** Overlay indicators (Bollinger, Ichimoku…) over the bars from `from` to `to`. */\n overlayRange(from: number, to: number): PriceRange | null;\n /** The compare lines on view, or null when none are drawn. */\n compareRange(data: DataSeries, vs: ViewportState): PriceRange | null;\n /** The volume band's share of the plot when volume is on view, else null. */\n volumeRatio(data: DataSeries, from: number, to: number): number | null;\n}\n\n/**\n * When the main price scale is fitted to the bars, and what it is fitted to.\n * No DOM: the chart hands it the viewport and the other ranges on the scale.\n *\n * Auto-scale on fits every frame. Off, a new series (the first bars,\n * `setData`'s, a symbol or timeframe switch) is still fitted once, then held:\n * otherwise a chart would keep its empty 0 to 1, or the last symbol's prices.\n * Bars that come one by one into an empty chart are fitted until they fill it.\n */\nexport class PriceFit {\n private auto: boolean;\n /**\n * The `autoScale` asked for (the option, or `setAutoScale`), which a new\n * series goes back to. `auto` alone is also turned off by a drag of the\n * price axis or a vertical pan, which hold the range for this series only.\n */\n private preferred: boolean;\n /** A new series whose prices aren't on the axis yet. */\n private pending = true;\n /** The series began with no bars, so they come one by one: see `settle`. */\n private arriving = true;\n\n constructor(autoScale: boolean) {\n this.auto = autoScale;\n this.preferred = autoScale;\n }\n\n isAuto(): boolean {\n return this.auto;\n }\n\n /** Whether this frame fits the scale. */\n shouldFit(): boolean {\n return this.auto || this.pending;\n }\n\n /** The host's choice, kept by the series that come after. */\n setAuto(enabled: boolean, hasBars: boolean): void {\n this.auto = enabled;\n this.preferred = enabled;\n // Off once bars are coming in: held from what is on screen now, not after the next.\n if (!enabled && hasBars) this.arriving = false;\n }\n\n /** A drag of the price axis or a vertical pan: the range on screen is the user's, held from here. */\n hold(): void {\n this.auto = false;\n this.pending = false;\n }\n\n /** Auto-scale back on (a double-click of the price axis, a replay), the preference left as it is. */\n resume(): void {\n this.auto = true;\n }\n\n /** A new series replaced the bars: it goes back to the preference and is fitted once. */\n newSeries(empty: boolean): void {\n this.auto = this.preferred;\n this.pending = true;\n this.arriving = empty;\n }\n\n /**\n * After a frame was fitted: the new series' prices are on the axis once\n * some of its bars were on a plot with room for them, so auto-scale off\n * holds them from here. A chart with no size yet (a hidden tab or panel)\n * has none on view and fits them once it gets one.\n */\n settle(vs: ViewportState, shown: number): void {\n if (!this.pending || shown === 0 || vs.chartRect.width <= 0) return;\n const allOnView = vs.offset <= 0 && shown * (vs.barWidth + vs.barSpacing) - vs.offset <= vs.chartRect.width;\n if (!(this.arriving && allOnView)) this.pending = false;\n }\n\n /**\n * Fit the scale to the bars on view, the overlay indicators and the compare\n * lines, in margins of φ⁻⁵ above and below; with volume on view, the band\n * at the bottom is left to it.\n */\n fit(target: FitTarget, data: DataSeries, vs: ViewportState, sources: FitSources): void {\n if (data.length === 0) return;\n const from = Math.max(0, vs.visibleRange.from);\n const to = Math.min(vs.visibleRange.to, data.length - 1);\n const range = joinRanges(\n joinRanges(computePriceRange(data, from, to, 0), sources.overlayRange(from, to)),\n sources.compareRange(data, vs),\n )!;\n const ratio = sources.volumeRatio(data, from, to);\n const band = ratio === null ? PRICE_SCALE_MARGIN : ratio + VOLUME_GAP;\n // The band is at the screen's bottom: on an upside-down scale that is above the highest price.\n target.setScaleMargins(target.isInvertScale()\n ? { top: band, bottom: PRICE_SCALE_MARGIN }\n : { top: PRICE_SCALE_MARGIN, bottom: band });\n target.fitToPrices(range.min, range.max);\n }\n}\n\n/** Whether any bar from `from` to `to` traded. */\nexport function hasVolume(data: DataSeries, from: number, to: number): boolean {\n for (let i = from; i <= to; i++) if (data[i].volume > 0) return true;\n return false;\n}\n\n/** Both ranges in one (either may be null). */\nexport function joinRanges(a: PriceRange | null, b: PriceRange | null): PriceRange | null {\n if (!a) return b;\n if (!b) return a;\n return { min: Math.min(a.min, b.min), max: Math.max(a.max, b.max) };\n}\n","import type { ChartTypeOptions, DataSeries } from '@tradecanvas/commons';\nimport { ReshapeMemo } from '@tradecanvas/core';\nimport { pointAndFigureDisplay, rangeBarsStep, resolveDisplayData, type ChartTypeLookup } from '../charts/ChartTypeStrategy.js';\n\n/** What the series a chart type draws is made of, read each time it is asked for. */\nexport interface DisplaySources {\n /** The bars shown. */\n bars(): DataSeries;\n /** Moves on every write to them, in place or not (`SeriesStore.version`). */\n version(): number;\n chartType(): string;\n /** Replaced, never changed in place, when the options change. */\n chartTypeOptions(): ChartTypeOptions;\n /** A chart type of a plugin's, which draws its own series. */\n lookup: ChartTypeLookup;\n /** The market's price step at a price: range bars are built on it. */\n stepAt(price: number): number | undefined;\n /** The box the built-in point & figure columns were made on, for its renderer. */\n onBoxSize(box: number): void;\n}\n\n/**\n * The series the chart type draws (Heikin-Ashi candles, Renko bricks, Kagi\n * lines…), made from the bars when something it is made of changed and\n * kept between frames otherwise. It sees every change itself: a write to\n * the bars moves their version, so no writer has to remember to drop it.\n * No DOM.\n */\nexport class DisplaySeries {\n private series: DataSeries | null = null;\n private madeAt: { version: number; type: string; options: ChartTypeOptions; step: number | undefined; epoch: number } | null = null;\n /** Moves on `invalidate`, for a change the sources don't show. */\n private epoch = 0;\n /** What Renko, Kagi and point & figure keep from one build to the next, to make the end again only. */\n private readonly memo = new ReshapeMemo();\n\n constructor(private readonly sources: DisplaySources) {}\n\n get(): DataSeries {\n const raw = this.sources.bars();\n if (raw.length === 0) return raw;\n const type = this.sources.chartType();\n const options = this.sources.chartTypeOptions();\n // Range bars go on the market's own grid, not the decimals the axis shows, which follow the view: the\n // coarsest step of the bars their range is measured on, so a last price over a band edge rebuilds nothing.\n const step = type === 'rangeBars' ? rangeBarsStep(raw, (price) => this.sources.stepAt(price)) : undefined;\n const version = this.sources.version();\n const at = this.madeAt;\n if (this.series && at && at.version === version && at.type === type && at.options === options && at.step === step && at.epoch === this.epoch) {\n return this.series;\n }\n this.series = this.make(type, raw, options, step);\n this.madeAt = { version, type, options, step, epoch: this.epoch };\n return this.series;\n }\n\n /** Made again on the next `get`: something it is made of changed where the sources don't show it. */\n invalidate(): void {\n this.epoch++;\n }\n\n /**\n * A new series, a new chart type or new hours shown: nothing kept from the\n * last build is of use, the end made again included.\n */\n reset(): void {\n this.invalidate();\n this.memo.clear();\n }\n\n /**\n * The built-in point & figure's columns come with the box they were built\n * on, which its renderer draws them at, in the one walk (a plugin's chart\n * type keeps its own).\n */\n private make(type: string, raw: DataSeries, options: ChartTypeOptions, step: number | undefined): DataSeries {\n const { lookup } = this.sources;\n if (type !== 'pointAndFigure' || lookup(type)) return resolveDisplayData(type, raw, lookup, options, step, this.memo);\n const { columns, boxSize } = pointAndFigureDisplay(raw, options, this.memo);\n this.sources.onBoxSize(boxSize);\n return columns;\n }\n}\n","import type { PaneStyle, ResolvedLayout, ViewportState } from '@tradecanvas/commons';\nimport type { IndicatorEngine, PanelRenderInfo } from '@tradecanvas/core';\nimport type { LayoutManager } from '../layout/LayoutManager.js';\n\n/** The pane's title and the line under it, above its plot. */\nexport const PANE_HEADER_HEIGHT = 20;\n/** A pane's scale with no values on view. */\nconst DEFAULT_PANE_RANGE = { min: 0, max: 100 } as const;\n\n/** What the panes read of the indicators drawn in them. */\nexport type PaneSources = Pick<\n IndicatorEngine,\n 'getPaneValueRange' | 'getPaneMembers' | 'getOutput' | 'getIndicatorDescriptor' | 'getActiveIndicators'\n>;\n\n/** Where the hovered bar is reported: from the top of the highest pane beside the price pane to the bottom of the lowest. */\nexport interface ReportBounds {\n top: number;\n bottom: number;\n}\n\n/**\n * The indicator panes as the renderer draws them: their place from the\n * layout, each on its own value scale, with its own style. Worked out once\n * a frame and kept until the layout or the values move. No DOM.\n */\nexport class PaneLayout {\n private layout: ResolvedLayout | null = null;\n private infos: PanelRenderInfo[] | null = null;\n private readonly styles = new Map<string, PaneStyle>();\n\n constructor(\n private readonly manager: LayoutManager,\n private readonly sources: PaneSources,\n /** Told each new layout's report bounds, for the crosshair. */\n private readonly onLayout: (bounds: ReportBounds) => void,\n ) {}\n\n resolved(): ResolvedLayout {\n if (!this.layout) {\n this.layout = this.manager.resolve();\n this.onLayout(reportBounds(this.layout));\n }\n return this.layout;\n }\n\n /** The layout changed (a size, a pane, an axis width): worked out again on the next read. */\n invalidate(): void {\n this.layout = null;\n this.infos = null;\n }\n\n /** The panes' values moved (a tick, a new indicator, a style): their scales are worked out again. */\n invalidateScales(): void {\n this.infos = null;\n }\n\n /** Each pane as drawn under the main viewport, read only when they are worked out again. */\n panels(mainState: () => ViewportState): PanelRenderInfo[] {\n if (this.infos) return this.infos;\n const main = mainState();\n const { from, to } = main.visibleRange;\n this.infos = this.resolved().panels.map((panel) => {\n const id = panel.config.id;\n // The pane's one value scale: plots, axis, crosshair and levels all use it.\n // On a log scale only while all its values are above 0.\n const logRange = panel.config.logScale ? this.sources.getPaneValueRange(id, from, to, true) : null;\n const priceRange = logRange ?? this.sources.getPaneValueRange(id, from, to) ?? { ...DEFAULT_PANE_RANGE };\n const style = this.styles.get(id);\n return {\n instanceId: id,\n members: this.sources.getPaneMembers(id),\n ...(style ? { style } : {}),\n rect: panel.rect,\n viewport: {\n ...main,\n // Below the pane's header.\n chartRect: {\n x: panel.rect.x,\n y: panel.rect.y + PANE_HEADER_HEIGHT,\n width: panel.rect.width,\n height: Math.max(0, panel.rect.height - PANE_HEADER_HEIGHT),\n },\n priceRange,\n logScale: logRange !== null,\n // In percent: labels only, of its first value on screen.\n scaleMode: panel.config.percentScale ? 'percentage' as const : 'regular' as const,\n scaleBaseline: panel.config.percentScale ? this.baseline(id, from, to) : undefined,\n // A pane's values are its own, not prices.\n formatPrice: undefined,\n pricePrecision: undefined,\n priceUnit: undefined,\n invertScale: !!panel.config.invertScale,\n },\n };\n });\n return this.infos;\n }\n\n /** Where the time axis starts: below the main chart and every pane under it. */\n timeAxisTop(): number {\n const { mainChartRect, panels } = this.resolved();\n const below = panels.filter((p) => p.config.position === 'bottom').reduce((sum, p) => sum + p.rect.height, 0);\n return mainChartRect.y + mainChartRect.height + below;\n }\n\n /**\n * The panes with nothing to draw on this chart type: every indicator in\n * them is left off it (a compare in its own pane, a correlation, on\n * Renko). They are laid out as if they weren't there, so no empty pane\n * with a made-up scale takes the plot's room; back on candles they return.\n */\n offChartType(): Set<string> {\n const active = this.sources.getActiveIndicators();\n const off = new Set(active.filter((ind) => ind.offChartType).map((ind) => ind.instanceId));\n const aside = new Set<string>();\n for (const panel of this.manager.getPanels()) {\n const members = active.filter((ind) => ind.instanceId === panel.id || ind.pane === panel.id);\n if (members.length > 0 && members.every((ind) => off.has(ind.instanceId))) aside.add(panel.id);\n }\n return aside;\n }\n\n // --- A pane's own style ---\n\n style(paneId: string): PaneStyle | undefined {\n return this.styles.get(paneId);\n }\n\n /** Set (or with null, take off) a pane's style: whether it changed. */\n setStyle(paneId: string, style: PaneStyle | null): boolean {\n if (JSON.stringify(style) === JSON.stringify(this.styles.get(paneId) ?? null)) return false;\n if (style) this.styles.set(paneId, style);\n else this.styles.delete(paneId);\n this.infos = null;\n return true;\n }\n\n /** A pane's style goes with the pane: to the indicator that takes it over, or away with it (`heir` null). */\n passStyle(paneId: string, heir: string | null): void {\n const style = this.styles.get(paneId);\n this.styles.delete(paneId);\n if (heir && style) this.styles.set(heir, style);\n this.infos = null;\n }\n\n /** A pane's first value on screen (its own indicator's first line), the 0% of a percent scale. */\n private baseline(paneId: string, from: number, to: number): number | undefined {\n const series = this.sources.getOutput(paneId)?.series;\n const descriptor = this.sources.getIndicatorDescriptor(paneId);\n if (!series || !descriptor) return undefined;\n const key = descriptor.plots?.[0]?.key;\n for (let i = Math.max(0, from); i <= to && i < series.length; i++) {\n const point = series[i];\n if (!point) continue;\n const value = key !== undefined ? point[key] : Object.values(point).find((v) => typeof v === 'number' && Number.isFinite(v));\n if (typeof value === 'number' && Number.isFinite(value) && value !== 0) return value;\n }\n return undefined;\n }\n}\n\n/** The hovered bar is reported over the price pane and the panes above and below it. */\nexport function reportBounds(layout: ResolvedLayout): ReportBounds {\n const main = layout.mainChartRect;\n let top = main.y;\n let bottom = main.y + main.height;\n for (const panel of layout.panels) {\n if (panel.config.position !== 'top' && panel.config.position !== 'bottom') continue;\n top = Math.min(top, panel.rect.y);\n bottom = Math.max(bottom, panel.rect.y + panel.rect.height);\n }\n return { top, bottom };\n}\n","import type { SnapshotIndicator, UndoRedoManager } from '@tradecanvas/core';\n\n/** Changes to one indicator closer together than this are one undo step (a colour dragged, a period typed). */\nexport const INDICATOR_EDIT_MERGE_MS = 800;\n\n/** The undo history the indicator steps go in. */\nexport type IndicatorSteps = Pick<UndoRedoManager, 'push' | 'last' | 'replaceLast' | 'dropLast'>;\n\nexport interface IndicatorHistoryOptions {\n steps: IndicatorSteps;\n /** The chart's indicators as a layout keeps them. */\n snapshot: () => SnapshotIndicator[];\n /** Whether undo is on (`features.drawingUndoRedo`). */\n enabled: () => boolean;\n now?: () => number;\n}\n\n/** An indicator step's pane renames, when it made any. */\nfunction withRenames(renames: [string, string][]): { renames?: [string, string][] } {\n return renames.length > 0 ? { renames } : {};\n}\n\nconst same = (a: unknown, b: unknown): boolean => JSON.stringify(a) === JSON.stringify(b);\n\n/**\n * The indicators' undo steps: each change as the indicators before and\n * after it, a burst of quick changes to one indicator merged into one step,\n * an edit (a settings dialog) as one step from its begin to its end. The\n * panes another indicator took over in a step go with it, so the drawings\n * on them can follow. No DOM.\n */\nexport class IndicatorHistory {\n private readonly steps: IndicatorSteps;\n private readonly snapshot: () => SnapshotIndicator[];\n private readonly enabled: () => boolean;\n private readonly now: () => number;\n /** Above 0 while a change is being recorded, or runs quietly: nested changes add no steps. */\n private depth = 0;\n private edit: { subject: string; before: SnapshotIndicator[] } | null = null;\n /** The panes renamed by the change being recorded, null outside one. */\n private renames: [string, string][] | null = null;\n\n constructor(options: IndicatorHistoryOptions) {\n this.steps = options.steps;\n this.snapshot = options.snapshot;\n this.enabled = options.enabled;\n this.now = options.now ?? (() => performance.now());\n }\n\n /**\n * Run an indicator change as one undo step. Quick changes about the same\n * `subject` (an indicator, a pane) merge into one step; a burst that ends\n * where it began leaves none.\n */\n record<T>(subject: string | undefined, change: () => T): T {\n if (this.depth > 0 || !this.enabled()) return change();\n const before = this.snapshot();\n const renames: [string, string][] = [];\n const outer = this.renames;\n this.renames = renames;\n let result: T;\n try {\n result = this.quietly(change);\n } finally {\n this.renames = outer;\n }\n const after = this.snapshot();\n if (same(before, after)) return result;\n this.push(subject, before, after, renames);\n return result;\n }\n\n private push(subject: string | undefined, before: SnapshotIndicator[], after: SnapshotIndicator[], renames: [string, string][]): void {\n const at = this.now();\n const last = this.steps.last();\n const burst = last?.type === 'indicators' ? last.indicators : undefined;\n const merge = subject !== undefined && burst?.subject === subject\n && at - (burst.at ?? 0) < INDICATOR_EDIT_MERGE_MS\n && same(burst.after, before);\n const step = { type: 'indicators' as const, before: null, after: null };\n if (merge && burst) {\n // Back where the burst began (hidden, then shown again): no step left.\n if (same(burst.before, after)) this.steps.dropLast();\n else this.steps.replaceLast({ ...step, indicators: { ...burst, after, at, ...withRenames([...(burst.renames ?? []), ...renames]) } });\n } else {\n this.steps.push({ ...step, indicators: { before, after, subject, at, ...withRenames(renames) } });\n }\n }\n\n /** Run `change` without recording undo steps for the indicators it changes. */\n quietly<T>(change: () => T): T {\n this.depth++;\n try {\n return change();\n } finally {\n this.depth--;\n }\n }\n\n /** Pane `from` was handed to the indicator `to` in the change being recorded. */\n renamed(from: string, to: string): void {\n this.renames?.push([from, to]);\n }\n\n /**\n * Start an edit of one indicator: its changes until `end` are one undo\n * step, and none when they end where they began (a cancel that put\n * everything back). An edit still open is ended first.\n */\n begin(subject: string): void {\n this.end();\n if (!this.enabled()) return;\n this.edit = { subject, before: this.snapshot() };\n this.depth++;\n }\n\n /** End the edit `begin` started, as one undo step when anything changed. */\n end(): void {\n const edit = this.edit;\n if (!edit) return;\n this.edit = null;\n this.depth--;\n const after = this.snapshot();\n if (same(after, edit.before)) return;\n this.steps.push({ type: 'indicators', before: null, after: null, indicators: { before: edit.before, after, subject: edit.subject, at: this.now() } });\n }\n}\n","import { parseIndicatorSource, type IndicatorDescriptor } from '@tradecanvas/commons';\nimport { sourceParam, type IndicatorEngine } from '@tradecanvas/core';\nimport type { LayoutManager } from '../layout/LayoutManager.js';\n\n/** What the panes read and change of the indicators. */\nexport type PaneMembers = Pick<\n IndicatorEngine,\n 'getIndicatorConfig' | 'getIndicatorDescriptor' | 'getPaneMembers' | 'getDependents' | 'setPane' | 'removeIndicator'\n>;\n\n/** The panes' places in the layout. */\nexport type PaneSlots = Pick<LayoutManager, 'getPanels' | 'addPanel' | 'removePanel'>;\n\n/** What goes with a pane when another indicator takes it over, or when it goes. */\nexport interface PaneHandover {\n /** The pane's own style to `heir`, or away with the pane (null). */\n passStyle(from: string, heir: string | null): void;\n /** Hand pane `from`, its place and its drawings, to the indicator `to`. */\n renamePane(from: string, to: string): void;\n /** The panes' value scales are to be worked out again. */\n invalidateScales(): void;\n}\n\n/** Where a move sends an indicator: an indicator drawn in the pane, `'new'` a pane of its own, `'price'` the price pane. */\nexport type PaneTarget = string;\n\n/**\n * Which pane each indicator is drawn in: its own, another indicator's (on\n * that pane's scale), or the price pane; who takes a pane over when its\n * indicator goes or moves away, and who follows it. No DOM.\n */\nexport class IndicatorPanes {\n constructor(\n private readonly members: PaneMembers,\n private readonly slots: PaneSlots,\n private readonly handover: PaneHandover,\n ) {}\n\n /** Whether the indicator has a pane of its own in the layout. */\n ownsPane(instanceId: string): boolean {\n return this.slots.getPanels().some((p) => p.id === instanceId);\n }\n\n /** The pane an indicator is drawn in: its own, or the one it shares. */\n paneOf(instanceId: string): string {\n return this.members.getIndicatorConfig(instanceId)?.pane ?? instanceId;\n }\n\n /** The instance owning the pane `instanceId` is drawn in; null for the price pane. */\n hostOf(instanceId: string, self?: string): string | null {\n if (instanceId === self) return null;\n if (this.ownsPane(instanceId)) return instanceId;\n const pane = this.members.getIndicatorConfig(instanceId)?.pane;\n return pane && pane !== self ? pane : null;\n }\n\n /**\n * The pane an indicator is drawn in, other than its own: the pane of the\n * instance `requested`, or, for a price-pane indicator computed from a pane\n * indicator's line, that indicator's pane. Null = the price pane (overlays)\n * or a pane of its own (panels).\n */\n paneFor(\n descriptor: Pick<IndicatorDescriptor, 'placement' | 'inputs'>,\n params: Readonly<Record<string, unknown>>,\n requested?: string,\n self?: string,\n ): string | null {\n if (requested) return this.hostOf(requested, self);\n if (descriptor.placement !== 'overlay') return null;\n const name = sourceParam(descriptor);\n const line = name ? parseIndicatorSource(params[name]) : null;\n return line ? this.hostOf(line.instanceId, self) : null;\n }\n\n /** The value of the input an indicator reads its source from (a price, another indicator's line); undefined without one. */\n sourceOf(instanceId: string): unknown {\n const descriptor = this.members.getIndicatorDescriptor(instanceId);\n const name = descriptor ? sourceParam(descriptor) : null;\n return name ? this.members.getIndicatorConfig(instanceId)?.params[name] : undefined;\n }\n\n /**\n * After an indicator's inputs changed: a price-pane indicator follows a\n * new source into its pane, and leaves it when it no longer reads from it;\n * otherwise it stays where it is. `before` is its source before the\n * change (`sourceOf`). True when it changed panes.\n */\n followSource(instanceId: string, before: unknown): boolean {\n const descriptor = this.members.getIndicatorDescriptor(instanceId);\n const config = this.members.getIndicatorConfig(instanceId);\n const name = descriptor ? sourceParam(descriptor) : null;\n if (descriptor?.placement !== 'overlay' || !config || !name || config.params[name] === before) return false;\n const line = parseIndicatorSource(config.params[name]);\n const pane = line ? this.hostOf(line.instanceId, instanceId)\n : parseIndicatorSource(before) ? null : config.pane ?? null;\n if ((pane ?? undefined) === config.pane) return false;\n this.members.setPane(instanceId, pane);\n return true;\n }\n\n /** Where a move would put the indicator: the pane's host, `null` (its own or the price pane), or `undefined` when it can't go. */\n moveHost(instanceId: string, target: PaneTarget): string | null | undefined {\n const config = this.members.getIndicatorConfig(instanceId);\n const descriptor = this.members.getIndicatorDescriptor(instanceId);\n if (!config || !descriptor) return undefined;\n // An overlay computed from another indicator's line stays with that line.\n const name = sourceParam(descriptor);\n if (descriptor.placement === 'overlay' && name && parseIndicatorSource(config.params[name])) return undefined;\n if (target === 'price') return descriptor.placement === 'overlay' && config.pane ? null : undefined;\n if (target === 'new') return descriptor.placement === 'panel' && !this.ownsPane(instanceId) ? null : undefined;\n const host = this.hostOf(target, instanceId);\n return host && host !== config.pane && host !== instanceId ? host : undefined;\n }\n\n private isPanel(instanceId: string): boolean {\n return this.members.getIndicatorDescriptor(instanceId)?.placement === 'panel';\n }\n\n /**\n * Remove one indicator. The first pane indicator drawn in its pane takes\n * the pane over and the others stay in it; without one (only price-pane\n * indicators were drawn there) they go back to the price pane. `moved`\n * hears of each of them as soon as it is in its new pane, before the next\n * one moves. Returns them, or null for an unknown indicator.\n */\n detach(instanceId: string, moved: (member: string) => void = () => undefined): string[] | null {\n if (!this.members.getIndicatorConfig(instanceId)) return null;\n const members = this.members.getPaneMembers(instanceId);\n const heir = this.ownsPane(instanceId) ? members.find((m) => this.isPanel(m)) ?? null : null;\n this.members.removeIndicator(instanceId);\n // A pane's own style goes with the pane: to its heir, or away with it.\n this.handover.passStyle(instanceId, heir);\n if (heir) {\n this.members.setPane(heir, null);\n this.handover.renamePane(instanceId, heir);\n } else {\n this.slots.removePanel(instanceId);\n }\n for (const member of members) {\n if (member !== heir) this.members.setPane(member, heir);\n moved(member);\n }\n return members;\n }\n\n /**\n * Move an indicator to another pane (see `moveHost` for `target`).\n * Indicators drawn in its own pane stay there (one of them takes the pane\n * over) or follow it when they read its lines. Returns every indicator\n * whose pane changed, or null when it can't go there.\n */\n move(instanceId: string, target: PaneTarget): string[] | null {\n const host = this.moveHost(instanceId, target);\n const config = this.members.getIndicatorConfig(instanceId);\n if (host === undefined || !config) return null;\n // Where it was, before the moves below change the config.\n const formerPane = config.pane;\n const changed = new Set<string>([instanceId]);\n if (this.ownsPane(instanceId)) for (const m of this.leavePane(instanceId, host)) changed.add(m);\n this.members.setPane(instanceId, host);\n if (target === 'new') {\n this.slots.addPanel(instanceId, 'bottom');\n // Those reading its lines come along into its new pane.\n for (const m of this.members.getDependents(instanceId)) {\n if (this.members.getIndicatorConfig(m)?.pane === formerPane) {\n this.members.setPane(m, instanceId);\n changed.add(m);\n }\n }\n }\n return [...changed];\n }\n\n /**\n * The indicator leaves the pane it owns for `host`: those reading its\n * lines go with it; of the rest, the first that draws in a pane takes the\n * pane over. Returns the pane's members.\n */\n private leavePane(instanceId: string, host: string | null): string[] {\n const members = this.members.getPaneMembers(instanceId);\n const readers = new Set(this.members.getDependents(instanceId));\n const staying = members.filter((m) => !readers.has(m));\n const heir = staying.find((m) => this.isPanel(m)) ?? null;\n // Its pane's own style goes with the pane: to the heir, or away with it.\n this.handover.passStyle(instanceId, heir);\n if (heir) {\n this.members.setPane(heir, null);\n this.handover.renamePane(instanceId, heir);\n for (const m of staying) if (m !== heir) this.members.setPane(m, heir);\n } else {\n this.slots.removePanel(instanceId);\n for (const m of staying) this.members.setPane(m, null);\n }\n this.handover.invalidateScales();\n for (const m of members) if (readers.has(m)) this.members.setPane(m, host ?? instanceId);\n return members;\n }\n}\n","import { indicatorSource, parseIndicatorSource, type PaneStyle } from '@tradecanvas/commons';\nimport type { IndicatorEngine, SnapshotIndicator } from '@tradecanvas/core';\nimport type { LayoutManager } from '../layout/LayoutManager.js';\n\n/** What a layout keeps of the indicators. */\nexport type SetupSources = Pick<IndicatorEngine, 'getActiveIndicators' | 'getIndicatorConfig' | 'getIndicatorStyle' | 'getIntervals'>;\n\n/** What a layout keeps of the panes. */\nexport type SetupPanes = Pick<LayoutManager, 'getPanels' | 'getMaximizedPanel'>;\n\n/** What putting the panes back changes. */\nexport type RestoredPanes = Pick<LayoutManager, 'setPanelSize' | 'setPanelCollapsed' | 'setPanelScale' | 'orderPanels' | 'setMaximizedPanel'>;\n\n/**\n * The indicators as a layout or a template keeps them: inputs, style,\n * levels, scale and panes (shared, size, order, fold, maximise, own style).\n */\nexport function captureIndicatorSetup(\n engine: SetupSources,\n layout: SetupPanes,\n paneStyle: (instanceId: string) => PaneStyle | null,\n): SnapshotIndicator[] {\n const panels = layout.getPanels();\n const maximized = layout.getMaximizedPanel();\n return engine.getActiveIndicators().map((ind) => {\n const panel = panels.find((p) => p.id === ind.instanceId);\n const config = engine.getIndicatorConfig(ind.instanceId);\n const style = panel ? paneStyle(ind.instanceId) : null;\n return {\n id: ind.id,\n instanceId: ind.instanceId,\n params: { ...ind.params },\n position: panel?.position,\n style: engine.getIndicatorStyle(ind.instanceId) ?? undefined,\n visible: ind.visible,\n levels: config?.levels?.slice(),\n pane: ind.pane,\n scale: config?.scale,\n ...(config?.intervals ? { intervals: engine.getIntervals(ind.instanceId) ?? undefined } : {}),\n ...(style ? { paneStyle: { ...style } } : {}),\n ...(panel\n ? {\n paneSize: panel.size,\n paneOrder: panels.indexOf(panel),\n ...(panel.collapsed ? { paneCollapsed: true } : {}),\n ...(maximized === ind.instanceId ? { paneMaximized: true } : {}),\n ...(panel.logScale ? { paneLog: true } : {}),\n ...(panel.invertScale ? { paneInvert: true } : {}),\n ...(panel.percentScale ? { panePercent: true } : {}),\n }\n : {}),\n };\n });\n}\n\n/** The panes as they were: size first (it opens a fold), then fold, scale, order, maximise. */\nexport function restorePanes(layout: RestoredPanes, restored: readonly (readonly [SnapshotIndicator, string])[]): void {\n for (const [ind, instanceId] of restored) {\n if (ind.paneSize !== undefined) layout.setPanelSize(instanceId, ind.paneSize);\n if (ind.paneSize !== undefined || ind.paneCollapsed) layout.setPanelCollapsed(instanceId, !!ind.paneCollapsed);\n if (ind.paneSize !== undefined) layout.setPanelScale(instanceId, { log: !!ind.paneLog, invert: !!ind.paneInvert, percent: !!ind.panePercent });\n }\n const ordered = restored\n .filter(([ind]) => ind.paneOrder !== undefined)\n .sort(([a], [b]) => (a.paneOrder ?? 0) - (b.paneOrder ?? 0))\n .map(([, instanceId]) => instanceId);\n if (ordered.length > 1) layout.orderPanels(ordered);\n const maximized = restored.find(([ind]) => ind.paneMaximized);\n layout.setMaximizedPanel(maximized ? maximized[1] : null);\n}\n\n/**\n * `list` with each indicator after the one whose pane it is drawn in and the\n * ones whose lines it reads (otherwise in the order given).\n */\nexport function dependencyOrder(list: readonly SnapshotIndicator[]): SnapshotIndicator[] {\n const byId = new Map(list.map((ind) => [ind.instanceId, ind]));\n const out: SnapshotIndicator[] = [];\n const done = new Set<string>();\n const visiting = new Set<string>();\n const visit = (ind: SnapshotIndicator) => {\n if (done.has(ind.instanceId) || visiting.has(ind.instanceId)) return;\n visiting.add(ind.instanceId);\n const needs = [ind.pane, ...Object.values(ind.params).map((v) => parseIndicatorSource(v)?.instanceId)];\n for (const id of needs) {\n const dep = id ? byId.get(id) : undefined;\n if (dep) visit(dep);\n }\n visiting.delete(ind.instanceId);\n done.add(ind.instanceId);\n out.push(ind);\n };\n for (const ind of list) visit(ind);\n return out;\n}\n\n/** The same indicator in the same place: kind, the pane it is drawn in, its scale, and a pane of its own or not. */\nexport function sameKindAndPlace(a: SnapshotIndicator, b: SnapshotIndicator): boolean {\n return a.id === b.id\n && (a.pane ?? null) === (b.pane ?? null)\n && (a.scale ?? null) === (b.scale ?? null)\n && (a.position === undefined) === (b.position === undefined);\n}\n\n/**\n * The indicators of `current` an undo may change in place: in `wanted` under\n * the same id, alike in kind and place, with the same inputs (changing their\n * values only: an input one has and the other lacks can't be taken away in place),\n * in a pane whose host is kept too: one drawn in a pane taken down would\n * fall back to the price pane, so it is put back with its host.\n */\nexport function keptInPlace(current: readonly SnapshotIndicator[], wanted: readonly SnapshotIndicator[]): Set<string> {\n const byId = new Map(wanted.map((ind) => [ind.instanceId, ind]));\n const kept = new Set<string>();\n for (const cur of current) {\n const want = byId.get(cur.instanceId);\n if (want && sameKindAndPlace(cur, want) && sameInputs(cur, want)) kept.add(cur.instanceId);\n }\n // A pane's host owns its pane and is drawn in no other, so one pass does\n // here; a host taken down later with the source it reads is the caller's.\n for (const cur of current) if (cur.pane && !kept.has(cur.pane)) kept.delete(cur.instanceId);\n return kept;\n}\n\n/** Whether two indicators have the same inputs, whatever their values. */\nfunction sameInputs(a: SnapshotIndicator, b: SnapshotIndicator): boolean {\n const names = Object.keys(a.params);\n return names.length === Object.keys(b.params).length && names.every((name) => name in b.params);\n}\n\n/** `params` with each line read from another indicator following that indicator's new id (`ids`: old → new). */\nexport function renameSources(\n params: Readonly<Record<string, unknown>>,\n ids: ReadonlyMap<string, string>,\n): Record<string, number | string | boolean> {\n const out = { ...params } as Record<string, number | string | boolean>;\n for (const [name, value] of Object.entries(out)) {\n const line = parseIndicatorSource(value);\n const to = line && ids.get(line.instanceId);\n if (to) out[name] = indicatorSource(to, line.key);\n }\n return out;\n}\n","import { SYMBOL_INDICATOR_IDS, type IndicatorEngine } from '@tradecanvas/core';\n\n/** What the requests read of the indicators. */\nexport type SymbolReaders = Pick<IndicatorEngine, 'getActiveIndicators' | 'getIndicatorConfig'>;\n\n/** The other symbols' bars the chart holds. */\nexport interface HeldSymbols {\n has(symbol: string): boolean;\n}\n\n/** The other symbol an indicator reads ('compareSymbol', 'spread'…), trimmed; '' for none. */\nfunction symbolOf(id: string, params: Readonly<Record<string, unknown>>): string {\n const symbol = params.symbol;\n return SYMBOL_INDICATOR_IDS.has(id) && typeof symbol === 'string' ? symbol.trim() : '';\n}\n\n/**\n * The other symbols the indicators read, and which of them the chart has\n * asked its app for (`symbolSeriesRequest`) and not been given yet: each\n * is asked for once. No DOM.\n */\nexport class SymbolRequests {\n private readonly asked = new Set<string>();\n\n constructor(\n private readonly readers: SymbolReaders,\n private readonly held: HeldSymbols,\n ) {}\n\n /** The symbols the indicators read, whether their bars are here or not. */\n required(): string[] {\n const out = new Set<string>();\n for (const ind of this.readers.getActiveIndicators()) {\n const symbol = symbolOf(ind.id, ind.params);\n if (symbol) out.add(symbol);\n }\n return [...out];\n }\n\n /** The indicators reading `symbol`. */\n readersOf(symbol: string): string[] {\n const wanted = symbol.trim();\n return this.readers.getActiveIndicators()\n .filter((ind) => SYMBOL_INDICATOR_IDS.has(ind.id) && String(ind.params.symbol ?? '').trim() === wanted)\n .map((ind) => ind.instanceId);\n }\n\n /** The bars of `symbol` came: it is no longer waited for. */\n given(symbol: string): void {\n this.asked.delete(symbol.trim());\n }\n\n /** A symbol no indicator reads any more may be asked for again later. */\n forgetUnread(): void {\n const required = new Set(this.required());\n for (const symbol of this.asked) if (!required.has(symbol)) this.asked.delete(symbol);\n }\n\n /**\n * The symbol to ask for once for the indicator just added or changed: the\n * one it reads, when the chart hasn't got it and hasn't asked (not those\n * other indicators read: one whose fetch failed is asked for again only\n * by an indicator on it). Null when there is nothing to ask.\n */\n toAsk(instanceId: string): string | null {\n this.forgetUnread();\n const config = this.readers.getIndicatorConfig(instanceId);\n const symbol = config ? symbolOf(config.id, config.params) : '';\n if (!symbol || this.held.has(symbol) || this.asked.has(symbol)) return null;\n this.asked.add(symbol);\n return symbol;\n }\n}\n","import type { DataSeries, PanelConfig, Rect, ViewportState } from '@tradecanvas/commons';\n\n/** A pane with less plot than this, px, takes no drawings: there is no room to place one. */\nexport const MIN_DRAWING_PANE_HEIGHT = 24;\n\nconst inside = (pos: { x: number; y: number }, r: Rect): boolean =>\n pos.x >= r.x && pos.x <= r.x + r.width && pos.y >= r.y && pos.y <= r.y + r.height;\n\n/**\n * The pane a drawing begun at `pos` goes on: undefined the price pane\n * (whose plot is `priceRect`), an indicator pane's id, null where none\n * takes drawings (an axis, a header, a side or folded pane).\n */\nexport function drawingPaneAt(\n pos: { x: number; y: number },\n priceRect: Rect,\n panels: readonly Pick<PanelConfig, 'id'>[],\n viewportOf: (paneId: string) => ViewportState | null,\n): string | null | undefined {\n if (inside(pos, priceRect)) return undefined;\n for (const panel of panels) {\n const viewport = viewportOf(panel.id);\n if (viewport && inside(pos, viewport.chartRect)) return panel.id;\n }\n return null;\n}\n\n/**\n * The viewport a pane's drawings are drawn and read with: its plot under\n * the header, its value scale, the bars' times. Null for a pane that takes\n * none: on a side (its x is not the time), folded, or too short to draw in.\n * `paneViewport` and `data` are read only for a pane that takes them: this\n * runs on every pointer move with a drawing tool, and a stale series is\n * worked out again when read.\n */\nexport function drawingPaneViewport(\n panel: Pick<PanelConfig, 'position'> | undefined,\n shownCollapsed: boolean,\n paneViewport: () => ViewportState | undefined,\n data: () => DataSeries,\n): ViewportState | null {\n if (!panel || (panel.position !== 'top' && panel.position !== 'bottom')) return null;\n if (shownCollapsed) return null;\n const viewport = paneViewport();\n if (!viewport || viewport.chartRect.height < MIN_DRAWING_PANE_HEIGHT) return null;\n return { ...viewport, data: data() };\n}\n\n/** Whether pane `paneId` is among `panels` and takes a drawing: no price-only tool goes on a pane. */\nexport function takesDrawing(panels: readonly Pick<PanelConfig, 'id'>[], paneId: string, priceOnly: boolean): boolean {\n return !priceOnly && panels.some((panel) => panel.id === paneId);\n}\n","import type { HistoryLoadPayload, OHLCBar } from '@tradecanvas/commons';\n\nexport type { HistoryLoadPayload };\n\n/**\n * Loads up to `limit` bars older than `before` (the oldest loaded bar's time,\n * in the bars' own unit), oldest first. An empty result means the history\n * starts here.\n */\nexport type HistoryLoader = (before: number, limit: number) => Promise<OHLCBar[]>;\n\n/** How long to wait after a failed page before the view asks again; doubles with each failure. */\nexport const HISTORY_RETRY_MS = 5_000;\n\n/** Longest wait between automatic retries. */\nexport const HISTORY_RETRY_MAX_MS = 60_000;\n\n/** Failures in a row after which only an explicit `loadMore` tries again. */\nexport const HISTORY_MAX_AUTO_FAILURES = 5;\n\nexport const DEFAULT_HISTORY_PAGE_SIZE = 500;\n\nexport interface HistoryPagerHost {\n /** Time of the oldest loaded bar, or null without data. */\n oldestTime(): number | null;\n /** Put a page in front of the data; returns how many bars it added. */\n prepend(bars: OHLCBar[]): number;\n emit(payload: HistoryLoadPayload): void;\n now?(): number;\n}\n\n/**\n * Pages older bars in as the view nears the start of the loaded data. One\n * request at a time; a page that arrives after the series changed (`reset`)\n * or the loader was replaced is dropped. An empty page ends the paging until\n * the next series. After a failure the view waits before asking again,\n * longer each time, and gives up asking after a few failures in a row.\n */\nexport class HistoryPager {\n private loader: HistoryLoader | null = null;\n private pageSize = DEFAULT_HISTORY_PAGE_SIZE;\n private loading = false;\n private exhausted = false;\n private failures = 0;\n private retryAt = 0;\n /** Bumped whenever an in-flight page must be dropped. */\n private seq = 0;\n /** The page on its way: a loadMore meanwhile waits for it rather than finding none. */\n private inFlight: Promise<number> | null = null;\n\n constructor(private readonly host: HistoryPagerHost) {}\n\n setLoader(loader: HistoryLoader | null, pageSize = DEFAULT_HISTORY_PAGE_SIZE): void {\n this.loader = loader;\n this.pageSize = Math.max(1, Math.floor(pageSize));\n this.reset();\n }\n\n hasLoader(): boolean {\n return this.loader !== null;\n }\n\n /**\n * A new series: drop any page in flight and allow paging again. A page\n * dropped is reported as loaded with no bars, so a 'loading' told for it\n * doesn't stay open.\n */\n reset(): void {\n this.seq++;\n const dropped = this.loading;\n this.loading = false;\n this.exhausted = false;\n this.failures = 0;\n this.retryAt = 0;\n if (dropped) this.emit({ state: 'loaded', count: 0 });\n }\n\n isLoading(): boolean {\n return this.loading;\n }\n\n /** False once a page came back empty. */\n hasMore(): boolean {\n return !this.exhausted;\n }\n\n /** Load a page when fewer than `ahead` bars are left of the first visible one. */\n maybeLoad(firstVisible: number, ahead: number): void {\n if (firstVisible >= ahead || !this.canLoad()) return;\n if (this.failures >= HISTORY_MAX_AUTO_FAILURES || this.now() < this.retryAt) return;\n void this.loadMore();\n }\n\n /**\n * Load one page now, whatever the wait after a failure. Resolves to the\n * number of bars added; while a page is on its way, to that page's.\n */\n loadMore(): Promise<number> {\n if (this.loading && this.inFlight) return this.inFlight;\n const page = this.loadPage();\n this.inFlight = page;\n return page;\n }\n\n private async loadPage(): Promise<number> {\n const loader = this.loader;\n const before = this.host.oldestTime();\n if (!loader || !this.canLoad() || before === null) return 0;\n\n const seq = this.seq;\n this.loading = true;\n this.emit({ state: 'loading', count: 0 });\n let page: OHLCBar[];\n try {\n page = await loader(before, this.pageSize);\n } catch (err: unknown) {\n if (seq !== this.seq) return 0;\n this.loading = false;\n // Cancelled by its feed (the stream reconnected): no failure, nothing to wait out.\n if (err instanceof Error && err.name === 'AbortError') {\n this.emit({ state: 'loaded', count: 0 });\n return 0;\n }\n this.failures++;\n this.retryAt = this.now() + Math.min(HISTORY_RETRY_MAX_MS, HISTORY_RETRY_MS * 2 ** (this.failures - 1));\n this.emit({ state: 'error', count: 0, error: err instanceof Error ? err.message : String(err) });\n return 0;\n }\n if (seq !== this.seq) return 0;\n this.loading = false;\n this.failures = 0;\n this.retryAt = 0;\n const added = this.host.prepend(page);\n if (added === 0) {\n this.exhausted = true;\n this.emit({ state: 'end', count: 0 });\n } else {\n this.emit({ state: 'loaded', count: added });\n }\n return added;\n }\n\n private canLoad(): boolean {\n return this.loader !== null && !this.loading && !this.exhausted;\n }\n\n /** A listener that throws must not stall the paging: report it and go on. */\n private emit(payload: HistoryLoadPayload): void {\n try {\n this.host.emit(payload);\n } catch (err: unknown) {\n console.error('[TradeCanvas] A historyLoad listener threw:', err);\n }\n }\n\n private now(): number {\n return this.host.now ? this.host.now() : Date.now();\n }\n}\n","import type {\n ChartStyleKey,\n ChartStyleOverrides,\n ChartStyleOverridesPatch,\n ChartType,\n FontConfig,\n ShapeConfig,\n StyleLayer,\n Theme,\n ThemeName,\n} from '@tradecanvas/commons';\nimport { DARK_THEME, LIGHT_THEME, chartStyleValue, resolveChartTheme } from '@tradecanvas/commons';\n\n/** Whether two fonts are the same face and sizes. */\nfunction sameFont(a: FontConfig, b: FontConfig): boolean {\n return a.family === b.family && a.sizeSmall === b.sizeSmall && a.sizeMedium === b.sizeMedium && a.sizeLarge === b.sizeLarge;\n}\n\n/**\n * The chart's theme and its style overrides. The theme drawn with\n * (`getTheme`) is the base theme with the overrides on it, worked out once\n * per change: the host's layer, then the user's for the base theme's name\n * (what someone chose for the dark theme stays with the dark theme).\n */\nexport class ThemeManager {\n private base: Theme;\n /** Shapes set apart from the theme (`setShapes`): they stay through theme switches. */\n private shape: ShapeConfig | null = null;\n /** A font set apart from the theme (`setFont`), over the theme's own; it stays through theme switches. */\n private font: Partial<FontConfig> | null = null;\n /** The font of the theme as set, under `font`. */\n private themeFont: FontConfig;\n private chartType: ChartType = 'candlestick';\n private host: ChartStyleOverrides = {};\n /** The user's overrides, by the name of the theme they were made on. */\n private user = new Map<string, ChartStyleOverrides>();\n private effective: Theme | null = null;\n\n constructor(themeOrName?: ThemeName | Theme) {\n this.base = ThemeManager.read(themeOrName);\n this.themeFont = this.base.font;\n }\n\n private static read(themeOrName?: ThemeName | Theme): Theme {\n if (!themeOrName || themeOrName === 'dark') return { ...DARK_THEME };\n if (themeOrName === 'light') return { ...LIGHT_THEME };\n // A theme as drawn brings its resolved style along: it is worked out again here.\n const { style: _style, ...theme } = themeOrName;\n return theme;\n }\n\n /** The theme as drawn: the base with the overrides on it. */\n getTheme(): Theme {\n this.effective ??= resolveChartTheme(this.base, this.chartType, this.merged());\n return this.effective;\n }\n\n /** The theme as set, without the overrides. */\n getBaseTheme(): Theme {\n return this.base;\n }\n\n setTheme(themeOrName: ThemeName | Theme): void {\n const next = ThemeManager.read(themeOrName);\n // A theme made from the one drawn (a market's colours, `{ ...getTheme(), … }`)\n // carries the font set over it: the theme's own font stays the one under it.\n const fromDrawn = this.font !== null && sameFont(next.font, this.base.font);\n this.base = next;\n if (!fromDrawn) this.themeFont = next.font;\n if (this.shape) this.base = { ...this.base, shape: { ...this.base.shape, ...this.shape } };\n if (this.font) this.base = { ...this.base, font: { ...this.themeFont, ...this.font } };\n this.effective = null;\n }\n\n /** A font over every theme from now on, on top of the one set before (null: each theme's own). */\n setFont(font: Partial<FontConfig> | null): void {\n this.font = font ? { ...this.font, ...font } : null;\n this.base = { ...this.base, font: this.font ? { ...this.themeFont, ...this.font } : this.themeFont };\n this.effective = null;\n }\n\n /** Shapes over every theme from now on (null: each theme's own). */\n setShape(shape: ShapeConfig | null): void {\n this.shape = shape ? { ...shape } : null;\n this.base = { ...this.base, shape: shape ? { ...this.base.shape, ...shape } : this.base.shape };\n this.effective = null;\n }\n\n /** The main series' type: its `series.<type>.*` keys apply. */\n setChartType(type: ChartType): void {\n if (type === this.chartType) return;\n this.chartType = type;\n this.effective = null;\n }\n\n /** Set (a value) or take away (`null`) overrides on a layer; true when that changed anything. */\n applyOverrides(patch: ChartStyleOverridesPatch, layer: StyleLayer): boolean {\n const current = this.layer(layer);\n const next: Record<string, unknown> = { ...current };\n for (const [key, value] of Object.entries(patch)) {\n if (value === null || value === undefined) delete next[key];\n else next[key] = value;\n }\n return this.setLayer(layer, next as ChartStyleOverrides);\n }\n\n /** `overrides` in place of a layer's; true when that changed anything. */\n setOverrides(overrides: ChartStyleOverrides, layer: StyleLayer): boolean {\n return this.setLayer(layer, { ...overrides });\n }\n\n /** Take away `keys` (all of them without) from a layer; true when that changed anything. */\n resetOverrides(layer: StyleLayer, keys?: readonly ChartStyleKey[]): boolean {\n if (!keys) return this.setLayer(layer, {});\n const next: Record<string, unknown> = { ...this.layer(layer) };\n for (const key of keys) delete next[key];\n return this.setLayer(layer, next as ChartStyleOverrides);\n }\n\n /** A layer's overrides (the user's for the current theme). */\n getOverrides(layer: StyleLayer): ChartStyleOverrides {\n return { ...this.layer(layer) };\n }\n\n /** The user's overrides on every theme, by theme name, for saving. */\n getUserOverrides(): Record<string, ChartStyleOverrides> {\n const out: Record<string, ChartStyleOverrides> = {};\n for (const [name, overrides] of this.user) if (Object.keys(overrides).length > 0) out[name] = { ...overrides };\n return out;\n }\n\n /** The user's overrides in place of theirs, by theme name (a layout loaded); true when that changed anything. */\n setUserOverrides(byTheme: Record<string, ChartStyleOverrides>): boolean {\n if (JSON.stringify(byTheme) === JSON.stringify(this.getUserOverrides())) return false;\n this.user = new Map(Object.entries(byTheme).map(([name, overrides]) => [name, { ...overrides }]));\n this.effective = null;\n return true;\n }\n\n /** What `key` resolves to now. */\n getStyleValue(key: ChartStyleKey): string | number | boolean | null {\n return chartStyleValue(this.base, this.merged(), key, this.chartType);\n }\n\n private layer(layer: StyleLayer): ChartStyleOverrides {\n return layer === 'host' ? this.host : this.user.get(this.base.name) ?? {};\n }\n\n private setLayer(layer: StyleLayer, next: ChartStyleOverrides): boolean {\n if (JSON.stringify(next) === JSON.stringify(this.layer(layer))) return false;\n if (layer === 'host') this.host = next;\n else if (Object.keys(next).length > 0) this.user.set(this.base.name, next);\n else this.user.delete(this.base.name);\n this.effective = null;\n return true;\n }\n\n private merged(): ChartStyleOverrides {\n return { ...this.host, ...this.user.get(this.base.name) };\n }\n}\n","import type { ChartOptions, ChartStyleOverridesPatch, CrosshairLineOptions } from '@tradecanvas/commons';\n\n/** A crosshair line's options as `crosshair.<axis>.*` keys. */\nfunction crosshairLine(axis: 'horizontal' | 'vertical', line: CrosshairLineOptions | undefined): Record<string, unknown> {\n if (!line) return {};\n return {\n ...(line.visible !== undefined ? { [`crosshair.${axis}.visible`]: line.visible } : {}),\n ...(line.color !== undefined ? { [`crosshair.${axis}.color`]: line.color } : {}),\n ...(line.style !== undefined ? { [`crosshair.${axis}.style`]: line.style } : {}),\n ...(line.width !== undefined ? { [`crosshair.${axis}.width`]: line.width } : {}),\n };\n}\n\n/**\n * The overrides a chart starts with: the grid's and crosshair's line options\n * read as their keys (they are shorthand for them), then `overrides`, which\n * win where both set a key.\n */\nexport function startOverrides(options: Pick<ChartOptions, 'grid' | 'crosshair' | 'overrides'>): ChartStyleOverridesPatch {\n const { grid, crosshair } = options;\n const labelBackground = crosshair?.vLine?.labelBackground ?? crosshair?.hLine?.labelBackground;\n return {\n ...(grid?.hLineColor !== undefined ? { 'grid.horizontal.color': grid.hLineColor } : {}),\n ...(grid?.hLineStyle !== undefined ? { 'grid.horizontal.style': grid.hLineStyle } : {}),\n ...(grid?.vLineColor !== undefined ? { 'grid.vertical.color': grid.vLineColor } : {}),\n ...(grid?.vLineStyle !== undefined ? { 'grid.vertical.style': grid.vLineStyle } : {}),\n ...crosshairLine('horizontal', crosshair?.hLine),\n ...crosshairLine('vertical', crosshair?.vLine),\n ...(labelBackground !== undefined ? { 'crosshair.labelBackground': labelBackground } : {}),\n ...options.overrides,\n } as ChartStyleOverridesPatch;\n}\n","import type { PanelPosition, PanelConfig, ResolvedLayout, ResolvedPanel, DividerRect, Rect } from '@tradecanvas/commons';\nimport { DEFAULT_PANEL_HEIGHT, MIN_PANEL_HEIGHT, PRICE_AXIS_WIDTH, TIME_AXIS_HEIGHT } from '@tradecanvas/commons';\n\nconst DEFAULT_PANEL_WIDTH = 200;\nconst MIN_PANEL_WIDTH = 80;\n/** What a folded pane keeps: its header, with the indicator's name and the pane's buttons. */\nexport const COLLAPSED_PANE_HEIGHT = 20;\n/** The least the price pane keeps while a pane is maximised. */\nexport const MIN_MAIN_HEIGHT_WITH_MAXIMIZED = 48;\n\nconst isHorizontal = (p: PanelConfig): boolean => p.position === 'top' || p.position === 'bottom';\n\nexport class LayoutManager {\n private panels: PanelConfig[] = [];\n private containerWidth = 0;\n private containerHeight = 0;\n private priceAxisWidth = PRICE_AXIS_WIDTH;\n /** Width of the left price scale; 0 while it is hidden. */\n private leftAxisWidth = 0;\n /** The pane that takes the room of the others (they fold to their headers), or null. */\n private maximized: string | null = null;\n /** Panes set aside: kept with their size and place, but not laid out (nothing drawn in them on this chart type). */\n private aside: ReadonlySet<string> = new Set();\n\n resize(width: number, height: number): void {\n this.containerWidth = width;\n this.containerHeight = height;\n }\n\n addPanel(instanceId: string, position: PanelPosition = 'bottom', size?: number): void {\n // A new pane shows: the maximised layout goes back to normal.\n this.maximized = null;\n const isVertical = position === 'left' || position === 'right';\n this.panels.push({\n id: instanceId,\n position,\n size: size ?? (isVertical ? DEFAULT_PANEL_WIDTH : DEFAULT_PANEL_HEIGHT),\n minSize: isVertical ? MIN_PANEL_WIDTH : MIN_PANEL_HEIGHT,\n content: { type: 'indicator', indicatorInstanceId: instanceId },\n });\n }\n\n /** Hand a pane to another indicator, keeping its place and size. */\n renamePanel(fromId: string, toId: string): void {\n const panel = this.panels.find((p) => p.id === fromId);\n if (!panel) return;\n panel.id = toId;\n panel.content = { ...panel.content, indicatorInstanceId: toId };\n if (this.maximized === fromId) this.maximized = toId;\n }\n\n removePanel(instanceId: string): void {\n this.panels = this.panels.filter((p) => p.id !== instanceId);\n if (this.maximized === instanceId) this.maximized = null;\n }\n\n /**\n * Fold a top or bottom pane to its header, or open it again. While a pane\n * is maximised this puts the layout back to normal first (the other panes\n * show folded then). False when nothing changed.\n */\n setPanelCollapsed(instanceId: string, collapsed: boolean): boolean {\n const panel = this.panels.find((p) => p.id === instanceId);\n if (!panel || !isHorizontal(panel)) return false;\n if (this.maximized === null && !!panel.collapsed === collapsed) return false;\n this.maximized = null;\n panel.collapsed = collapsed || undefined;\n return true;\n }\n\n /** Folded as set (see `isPanelShownCollapsed` for how it shows). */\n isPanelCollapsed(instanceId: string): boolean {\n return !!this.panels.find((p) => p.id === instanceId)?.collapsed;\n }\n\n /** Shown folded: folded as set, or another pane is maximised. */\n isPanelShownCollapsed(instanceId: string): boolean {\n const panel = this.panels.find((p) => p.id === instanceId);\n if (!panel || !isHorizontal(panel)) return false;\n return this.maximized !== null ? this.maximized !== instanceId : !!panel.collapsed;\n }\n\n /** A pane's value scale: logarithmic, upside down, in percent. False when nothing changed. */\n setPanelScale(instanceId: string, scale: { log?: boolean; invert?: boolean; percent?: boolean }): boolean {\n const panel = this.panels.find((p) => p.id === instanceId);\n if (!panel) return false;\n let changed = false;\n if (scale.log !== undefined && !!panel.logScale !== scale.log) {\n panel.logScale = scale.log || undefined;\n changed = true;\n }\n if (scale.invert !== undefined && !!panel.invertScale !== scale.invert) {\n panel.invertScale = scale.invert || undefined;\n changed = true;\n }\n if (scale.percent !== undefined && !!panel.percentScale !== scale.percent) {\n panel.percentScale = scale.percent || undefined;\n changed = true;\n }\n return changed;\n }\n\n /** Whether `movePanel(instanceId, delta)` would move it. */\n canMovePanel(instanceId: string, delta: -1 | 1): boolean {\n const panel = this.panels.find((p) => p.id === instanceId);\n if (!panel) return false;\n const group = this.panels.filter((p) => p.position === panel.position);\n return group[group.indexOf(panel) + delta] !== undefined;\n }\n\n /**\n * Let one top or bottom pane take the room: the others fold to their\n * headers and the price pane keeps a strip. `null` puts them back.\n */\n setMaximizedPanel(instanceId: string | null): boolean {\n const panel = instanceId === null ? null : this.panels.find((p) => p.id === instanceId);\n if (instanceId !== null && (!panel || !isHorizontal(panel))) return false;\n if (this.maximized === instanceId) return false;\n this.maximized = instanceId;\n if (panel) panel.collapsed = undefined;\n return true;\n }\n\n getMaximizedPanel(): string | null {\n return this.maximized;\n }\n\n /**\n * Move a pane one place up (`-1`) or down (`1`) among the panes on its side\n * of the price pane. False at the end of the line.\n */\n movePanel(instanceId: string, delta: -1 | 1): boolean {\n const panel = this.panels.find((p) => p.id === instanceId);\n if (!panel) return false;\n const group = this.panels.filter((p) => p.position === panel.position);\n const at = group.indexOf(panel);\n const other = group[at + delta];\n if (!other) return false;\n const i = this.panels.indexOf(panel);\n const j = this.panels.indexOf(other);\n const next = [...this.panels];\n next[i] = other;\n next[j] = panel;\n this.panels = next;\n return true;\n }\n\n /** Put the panes on each side in the order of `ids` (others keep their place after them). */\n orderPanels(ids: readonly string[]): void {\n const rank = (p: PanelConfig) => {\n const at = ids.indexOf(p.id);\n return at < 0 ? ids.length + this.panels.indexOf(p) : at;\n };\n this.panels = [...this.panels].sort((a, b) => rank(a) - rank(b));\n }\n\n /** A pane's height (or width) on screen: folded, maximised or as set. */\n private shownSize(panel: PanelConfig, maximizedSize: number): number {\n if (!isHorizontal(panel)) return panel.size;\n if (this.maximized) return panel.id === this.maximized ? maximizedSize : COLLAPSED_PANE_HEIGHT;\n return panel.collapsed ? COLLAPSED_PANE_HEIGHT : panel.size;\n }\n\n setPanelPosition(instanceId: string, position: PanelPosition): void {\n const panel = this.panels.find((p) => p.id === instanceId);\n if (panel) {\n const wasVertical = panel.position === 'left' || panel.position === 'right';\n const isVertical = position === 'left' || position === 'right';\n panel.position = position;\n if (wasVertical !== isVertical) {\n panel.size = isVertical ? DEFAULT_PANEL_WIDTH : DEFAULT_PANEL_HEIGHT;\n panel.minSize = isVertical ? MIN_PANEL_WIDTH : MIN_PANEL_HEIGHT;\n }\n // Side panes neither fold nor maximise.\n if (isVertical) {\n panel.collapsed = undefined;\n if (this.maximized === instanceId) this.maximized = null;\n }\n }\n }\n\n /**\n * Resize a pane; a folded pane opens, and a maximised layout goes back to\n * normal. A height is kept within the room there is (a stored layout may ask\n * for more).\n */\n setPanelSize(instanceId: string, size: number): void {\n const panel = this.panels.find((p) => p.id === instanceId);\n if (panel) {\n const room = isHorizontal(panel) ? this.containerHeight - TIME_AXIS_HEIGHT - MIN_MAIN_HEIGHT_WITH_MAXIMIZED : this.containerWidth / 2;\n const capped = room > panel.minSize ? Math.min(size, room) : size;\n panel.size = Math.max(panel.minSize, capped);\n panel.collapsed = undefined;\n this.maximized = null;\n }\n }\n\n /** Width reserved for the price axis right of the main chart. */\n setPriceAxisWidth(width: number): void {\n this.priceAxisWidth = width;\n }\n\n /** Width reserved left of the plot for the left price scale (0 = none). */\n setLeftAxisWidth(width: number): void {\n this.leftAxisWidth = Math.max(0, width);\n }\n\n getPanels(): PanelConfig[] {\n return this.panels;\n }\n\n /** Set the panes laid out as if they weren't there, and bring back the others; true when that changed any. */\n setPanelsAside(ids: ReadonlySet<string>): boolean {\n const changed = ids.size !== this.aside.size || [...ids].some((id) => !this.aside.has(id));\n this.aside = new Set(ids);\n return changed;\n }\n\n resolve(): ResolvedLayout {\n const laidOut = this.panels.filter((p) => !this.aside.has(p.id));\n const leftPanels = laidOut.filter((p) => p.position === 'left');\n const rightPanels = laidOut.filter((p) => p.position === 'right');\n const topPanels = laidOut.filter((p) => p.position === 'top');\n const bottomPanels = laidOut.filter((p) => p.position === 'bottom');\n\n // A maximised pane takes what the price pane and the folded panes leave.\n const folded = (topPanels.length + bottomPanels.length - 1) * COLLAPSED_PANE_HEIGHT;\n const maximizedPanel = this.maximized ? laidOut.find((p) => p.id === this.maximized) : undefined;\n const maximizedSize = Math.max(\n maximizedPanel?.minSize ?? 0,\n this.containerHeight - TIME_AXIS_HEIGHT - MIN_MAIN_HEIGHT_WITH_MAXIMIZED - folded,\n );\n const size = (p: PanelConfig) => this.shownSize(p, maximizedSize);\n\n const leftWidth = leftPanels.reduce((s, p) => s + p.size, 0);\n const rightWidth = rightPanels.reduce((s, p) => s + p.size, 0);\n const topHeight = topPanels.reduce((s, p) => s + size(p), 0);\n const bottomHeight = bottomPanels.reduce((s, p) => s + size(p), 0);\n\n const plotX = leftWidth + this.leftAxisWidth;\n const mainChartRect: Rect = {\n x: plotX,\n y: topHeight,\n width: Math.max(0, this.containerWidth - plotX - rightWidth - this.priceAxisWidth),\n height: Math.max(0, this.containerHeight - topHeight - bottomHeight - TIME_AXIS_HEIGHT),\n };\n\n const resolvedPanels: ResolvedPanel[] = [];\n const dividers: DividerRect[] = [];\n\n // Left panels (stacked vertically)\n let x = 0;\n for (const panel of leftPanels) {\n resolvedPanels.push({\n config: panel,\n rect: { x, y: 0, width: panel.size, height: this.containerHeight - TIME_AXIS_HEIGHT },\n });\n dividers.push({\n panelId: panel.id,\n rect: { x: x + panel.size - 2, y: 0, width: 4, height: this.containerHeight },\n orientation: 'vertical',\n });\n x += panel.size;\n }\n\n // Top panels (stacked horizontally, full width minus left/right)\n let y = 0;\n for (const panel of topPanels) {\n const height = size(panel);\n resolvedPanels.push({\n config: panel,\n rect: { x: plotX, y, width: mainChartRect.width, height },\n });\n dividers.push({\n panelId: panel.id,\n rect: { x: plotX, y: y + height - 2, width: mainChartRect.width, height: 4 },\n orientation: 'horizontal',\n });\n y += height;\n }\n\n // Bottom panels\n y = mainChartRect.y + mainChartRect.height;\n for (const panel of bottomPanels) {\n const height = size(panel);\n resolvedPanels.push({\n config: panel,\n rect: { x: plotX, y, width: mainChartRect.width, height },\n });\n dividers.push({\n panelId: panel.id,\n rect: { x: plotX, y: y - 2, width: mainChartRect.width, height: 4 },\n orientation: 'horizontal',\n });\n y += height;\n }\n\n // Right panels\n x = plotX + mainChartRect.width + this.priceAxisWidth;\n for (const panel of rightPanels) {\n resolvedPanels.push({\n config: panel,\n rect: { x, y: 0, width: panel.size, height: this.containerHeight - TIME_AXIS_HEIGHT },\n });\n dividers.push({\n panelId: panel.id,\n rect: { x: x - 2, y: 0, width: 4, height: this.containerHeight },\n orientation: 'vertical',\n });\n x += panel.size;\n }\n\n return { mainChartRect, panels: resolvedPanels, dividers };\n }\n\n getMainChartRect(): Rect {\n return this.resolve().mainChartRect;\n }\n}\n","import { PRICE_AXIS_WIDTH, autoPricePrecision, formatPrice } from '@tradecanvas/commons';\n\n/** Axis label: 8px tick notch + gap on the left, 6px margin on the right. */\nconst LABEL_CHROME = 14;\n/** Last-price tag: 12px badge padding + 2px margin. */\nconst PRICE_TAG_CHROME = 14;\n/** Crosshair pill: 16px padding + 5px notch + 1px margin. */\nconst CROSSHAIR_PILL_CHROME = 22;\n/** Widths snap to this grid so a one-pixel label change doesn't relayout. */\nconst WIDTH_GRANULARITY = 4;\n/** Shrinking waits until this much room is spare, so panning doesn't make the axis twitch. */\nexport const AXIS_SHRINK_SLACK = 12;\n\nexport interface PriceAxisWidthInput {\n min: number;\n max: number;\n lastPrice: number | null;\n /** Decimals for the scale and its tags (a market's or symbol's `pricePrecision`), or null to fit the range. */\n tagPrecision: number | null;\n locale: string;\n /** The chart's price format, when it has one: labels and tags print with it. */\n format?: ((price: number) => string) | null;\n /** The scale's label for a price as the axis writes it (its decimals, percent, a fraction), when known. */\n scaleLabel?: ((price: number) => string) | null;\n fontFamily: string;\n fontSizeSmall: number;\n measure: (text: string, font: string) => number;\n}\n\n/**\n * Width the price axis needs for its widest label — tick labels, the\n * last-price tag and the crosshair pill — never below `PRICE_AXIS_WIDTH`.\n * A sub-cent asset (\"0,000004349\") needs ~90px where BTC fits the default.\n */\nexport function requiredPriceAxisWidth(input: PriceAxisWidthInput): number {\n const { min, max, lastPrice, locale, fontFamily, fontSizeSmall, measure } = input;\n const axisPrecision = input.tagPrecision ?? autoPricePrecision(min, max);\n const tagPrecision = axisPrecision;\n const prices = lastPrice !== null ? [min, max, lastPrice] : [min, max];\n\n let need = PRICE_AXIS_WIDTH;\n for (const price of prices) {\n if (!Number.isFinite(price)) continue;\n const label = input.scaleLabel ? input.scaleLabel(price) : input.format ? input.format(price) : formatPrice(price, axisPrecision, locale);\n const tag = input.format ? label : formatPrice(price, tagPrecision, locale);\n need = Math.max(\n need,\n measure(label, `500 ${fontSizeSmall}px ${fontFamily}`) + LABEL_CHROME,\n measure(tag, `bold 11px ${fontFamily}`) + PRICE_TAG_CHROME,\n measure(tag, `600 ${fontSizeSmall}px ${fontFamily}`) + CROSSHAIR_PILL_CHROME,\n );\n }\n // The default stays exact; only widened axes snap to the grid.\n return need <= PRICE_AXIS_WIDTH ? PRICE_AXIS_WIDTH : Math.ceil(need / WIDTH_GRANULARITY) * WIDTH_GRANULARITY;\n}\n\n/** The axis width to use next: grow at once, shrink only once enough room is spare. */\nexport function nextPriceAxisWidth(current: number, required: number): number {\n if (required > current) return required;\n if (required <= current - AXIS_SHRINK_SLACK) return required;\n return current;\n}\n","/**\n * Most widths kept. The price axis measures a few labels in three fonts each\n * time it fits; this many means the prices have moved on, so start again.\n */\nconst MAX_WIDTHS = 512;\n\n/**\n * Label widths by font and text, each measured once. Panning fits the price\n * axis frame after frame to the same few labels, and a measure on the chart's\n * own canvas, in the page, costs more than one on a canvas out of it. A web\n * font that finishes loading changes the widths, so all are measured again.\n */\nexport class LabelWidths {\n private readonly widths = new Map<string, number>();\n private readonly fonts: FontFaceSet | null;\n private readonly forget = (): void => this.widths.clear();\n\n constructor() {\n this.fonts = typeof document !== 'undefined' ? document.fonts ?? null : null;\n this.fonts?.addEventListener('loadingdone', this.forget);\n }\n\n /** The width of `text` in `font`: `measure`'s answer the first time, kept after. */\n get(text: string, font: string, measure: () => number): number {\n // A font string holds no line break, so the key can't be read two ways.\n const key = `${font}\\n${text}`;\n const known = this.widths.get(key);\n if (known !== undefined) return known;\n const width = measure();\n if (this.widths.size >= MAX_WIDTHS) this.widths.clear();\n this.widths.set(key, width);\n return width;\n }\n\n destroy(): void {\n this.fonts?.removeEventListener('loadingdone', this.forget);\n this.widths.clear();\n }\n}\n","/** Net vertical travel (px) a drag needs before it may move the price scale. */\nexport const VERTICAL_PAN_ENGAGE_PX = 24;\n/** …and that travel must be at least this share of the horizontal travel. */\nexport const VERTICAL_PAN_MIN_RATIO = 0.5;\n\n/**\n * Decides, per drag gesture, when a chart-body drag should also pan the price\n * scale (which turns auto-scale off). A horizontal drag always drifts a few\n * pixels vertically; engaging on that drift knocked the chart off auto-scale\n * mid-pan, so the price scale stopped following the bars and wobbled with the\n * hand — the drag felt stuck. Now it takes a deliberate vertical move.\n * With auto-scale already off the price scale pans freely, as before.\n */\nexport class VerticalPanGate {\n private netY = 0;\n private travelX = 0;\n private engaged = false;\n\n /** Start of a new drag gesture. */\n reset(): void {\n this.netY = 0;\n this.travelX = 0;\n this.engaged = false;\n }\n\n /** Feed one drag step; true once vertical panning is engaged for this gesture. */\n step(deltaX: number, deltaY: number, autoScaleOn: boolean): boolean {\n if (this.engaged) return true;\n this.netY += deltaY;\n this.travelX += Math.abs(deltaX);\n if (!autoScaleOn) {\n this.engaged = true;\n } else {\n const dy = Math.abs(this.netY);\n this.engaged = dy > VERTICAL_PAN_ENGAGE_PX && dy >= this.travelX * VERTICAL_PAN_MIN_RATIO;\n }\n return this.engaged;\n }\n}\n","import type {\n IndicatorPlugin,\n DrawingPlugin,\n DataSeries,\n ViewportState,\n Theme,\n Point,\n} from '@tradecanvas/commons';\nimport type { ChartRendererInterface } from '@tradecanvas/core';\n\n/**\n * Plugin SDK contracts — FROZEN for 1.0. Authors extend the chart along four\n * axes. `IndicatorPlugin` and `DrawingPlugin` are re-used verbatim from\n * commons; `ChartTypePlugin` and `OverlayPlugin` are defined here because they\n * reference core's renderer interface.\n */\nexport const PLUGIN_API_VERSION = 1;\n\n/** A custom chart type: a renderer plus an optional data transform. */\nexport interface ChartTypePlugin {\n descriptor: { type: string; name: string };\n createRenderer(): ChartRendererInterface;\n /** Derive the displayed series from raw OHLC (e.g. Heikin-Ashi, Renko). */\n transform?(data: DataSeries): DataSeries;\n}\n\n/** Render context handed to overlay plugins each frame. */\nexport interface OverlayRenderContext {\n viewport: ViewportState;\n data: DataSeries;\n theme: Theme;\n}\n\n/** A custom data overlay (e.g. a volume-profile-style layer). */\nexport interface OverlayPlugin {\n descriptor: { id: string; name: string; layer?: 'main' | 'overlay' | 'ui' };\n /**\n * False: the overlay stays registered but isn't drawn. Read on every frame:\n * after switching it, call `chart.requestRender()` to show the change at once.\n */\n enabled?: boolean;\n render(ctx: CanvasRenderingContext2D, context: OverlayRenderContext): void;\n hitTest?(point: Point, context: OverlayRenderContext): boolean;\n}\n\n/** Discriminated union accepted by `registerPlugin` and `chart.plugins.register`. */\nexport type ChartPlugin =\n | { kind: 'indicator'; plugin: IndicatorPlugin }\n | { kind: 'drawing'; plugin: DrawingPlugin }\n | { kind: 'chartType'; plugin: ChartTypePlugin }\n | { kind: 'overlay'; plugin: OverlayPlugin };\n\n/** Overlays drawn on `layer` (the default layer is `overlay`), leaving out those switched off. */\nexport function overlaysForLayer(\n overlays: OverlayPlugin[],\n layer: 'main' | 'overlay' | 'ui',\n): OverlayPlugin[] {\n return overlays.filter((o) => o.enabled !== false && (o.descriptor.layer ?? 'overlay') === layer);\n}\n\n/** Stable identity for a plugin, used for registry de-duplication and `list()`. */\nexport function pluginKey(p: ChartPlugin): string {\n switch (p.kind) {\n case 'indicator':\n return `indicator:${p.plugin.descriptor.id}`;\n case 'drawing':\n return `drawing:${p.plugin.descriptor.type}`;\n case 'chartType':\n return `chartType:${p.plugin.descriptor.type}`;\n case 'overlay':\n return `overlay:${p.plugin.descriptor.id}`;\n }\n}\n","import type { ChartPlugin } from './contracts.js';\nimport { pluginKey } from './contracts.js';\n\n/**\n * Global default plugin registry. Every `Chart` created *after* a plugin is\n * registered inherits it (unless `inheritGlobal: false`). For per-chart scoping\n * use the constructor `plugins` option or `chart.plugins.register`.\n */\nconst globalPlugins = new Map<string, ChartPlugin>();\n\n/** Register a plugin globally. Re-registering the same key replaces it. */\nexport function registerPlugin(plugin: ChartPlugin): void {\n globalPlugins.set(pluginKey(plugin), plugin);\n}\n\n/** Remove a global plugin by its `pluginKey`. Returns true if it existed. */\nexport function unregisterPlugin(key: string): boolean {\n return globalPlugins.delete(key);\n}\n\n/** Snapshot of all globally registered plugins. */\nexport function getGlobalPlugins(): ChartPlugin[] {\n return [...globalPlugins.values()];\n}\n\n/** Clear the global registry (test/teardown helper). */\nexport function clearGlobalPlugins(): void {\n globalPlugins.clear();\n}\n","import type { IndicatorPlugin } from '@tradecanvas/commons';\nimport type { IndicatorEngine } from '@tradecanvas/core';\nimport type { ChartPlugin, ChartTypePlugin, OverlayPlugin } from './contracts.js';\nimport { pluginKey } from './contracts.js';\nimport { getGlobalPlugins } from './registry.js';\n\nexport interface PluginManagerOptions {\n /** Per-instance plugins (from `new Chart(el, { plugins })`); merged over globals. */\n plugins?: ChartPlugin[];\n /** Inherit the global registry at construction (default: true). */\n inheritGlobal?: boolean;\n}\n\n/**\n * Per-chart plugin registry. Resolves plugins in precedence order:\n * global defaults → constructor `plugins` → imperative `register()`.\n * Indicator plugins additionally flow into the `IndicatorEngine` so\n * `chart.addIndicator(id)` can find them by descriptor id.\n */\nexport class PluginManager {\n private plugins = new Map<string, ChartPlugin>();\n\n constructor(\n private indicatorEngine: IndicatorEngine,\n options: PluginManagerOptions = {},\n ) {\n if (options.inheritGlobal !== false) {\n for (const p of getGlobalPlugins()) this.add(p);\n }\n for (const p of options.plugins ?? []) this.add(p);\n }\n\n /** Register a plugin on this chart instance. */\n register(plugin: ChartPlugin): void {\n this.add(plugin);\n }\n\n /** Remove a plugin by its `pluginKey`. Returns true if it existed. */\n unregister(key: string): boolean {\n return this.plugins.delete(key);\n }\n\n /** All plugins active on this instance. */\n list(): ChartPlugin[] {\n return [...this.plugins.values()];\n }\n\n /** Back-compat shorthand for registering an indicator plugin. */\n registerIndicator(plugin: IndicatorPlugin): void {\n this.add({ kind: 'indicator', plugin });\n }\n\n /** Look up a registered custom chart type by its `type` string. */\n getChartType(type: string): ChartTypePlugin | undefined {\n const p = this.plugins.get(`chartType:${type}`);\n return p?.kind === 'chartType' ? p.plugin : undefined;\n }\n\n /** All registered overlay plugins. */\n getOverlays(): OverlayPlugin[] {\n const out: OverlayPlugin[] = [];\n for (const p of this.plugins.values()) {\n if (p.kind === 'overlay') out.push(p.plugin);\n }\n return out;\n }\n\n private add(plugin: ChartPlugin): void {\n this.plugins.set(pluginKey(plugin), plugin);\n // Indicator plugins must reach the engine so they're resolvable by id.\n if (plugin.kind === 'indicator') {\n this.indicatorEngine.register(plugin.plugin);\n }\n }\n}\n","import type {\n ExecutionAdapter,\n ExecutionEvent,\n ExecutionError,\n TradingOrder,\n TradingPosition,\n OrderPlaceIntent,\n OrderModifyIntent,\n OrderCancelIntent,\n PositionModifyIntent,\n PositionCloseIntent,\n PositionReverseIntent,\n FillEvent,\n} from '@tradecanvas/commons';\n\n/** The order/position intents the chart emits and an adapter consumes. */\nexport type ExecutionIntentType =\n | 'orderPlace'\n | 'orderModify'\n | 'orderCancel'\n | 'positionModify'\n | 'positionClose'\n | 'positionReverse';\n\n/**\n * Surface the host (the Chart) provides so execution can be bridged without\n * the helper knowing anything about chart internals — which keeps it pure and\n * unit-testable.\n */\nexport interface ExecutionHost {\n /** Subscribe to an emitted intent; returns an unsubscribe function. */\n onIntent(type: ExecutionIntentType, handler: (payload: unknown) => void): () => void;\n /** Render the adapter's authoritative pending orders. */\n setOrders(orders: TradingOrder[]): void;\n /** Render the adapter's authoritative open positions. */\n setPositions(positions: TradingPosition[]): void;\n /** Surface an execution failure (adapter-reported or a failed command). */\n onError(error: ExecutionError): void;\n /** An order filled or a position closed (the adapter's `fill`). */\n onFill?(fill: FillEvent): void;\n /** The positions on the chart, until the adapter reports its own. */\n getPositions?(): readonly TradingPosition[];\n}\n\n/** What a command can read: the latest positions, and the reverses under way. */\ninterface CommandContext {\n positions: () => readonly TradingPosition[];\n reversing: Set<string>;\n}\n\nconst COMMANDS: Record<\n ExecutionIntentType,\n (adapter: ExecutionAdapter, payload: unknown, context: CommandContext) => Promise<unknown>\n> = {\n orderPlace: (a, p) => a.placeOrder(p as OrderPlaceIntent),\n orderModify: (a, p) => a.modifyOrder(p as OrderModifyIntent),\n orderCancel: (a, p) => a.cancelOrder(p as OrderCancelIntent),\n positionModify: (a, p) => a.modifyPosition(p as PositionModifyIntent),\n positionClose: (a, p) => a.closePosition(p as PositionCloseIntent),\n positionReverse: (a, p, context) => reverse(a, p as PositionReverseIntent, context),\n};\n\n/**\n * Reverse a position: the adapter's own `reversePosition`, or else close it\n * and place a market order the other way for the size that was open. A\n * second reverse of the same position while one is under way is ignored.\n */\nasync function reverse(adapter: ExecutionAdapter, intent: PositionReverseIntent, context: CommandContext): Promise<void> {\n const { reversing } = context;\n if (reversing.has(intent.positionId)) return;\n reversing.add(intent.positionId);\n try {\n if (adapter.reversePosition) {\n await adapter.reversePosition(intent);\n return;\n }\n const position = context.positions().find((p) => p.id === intent.positionId);\n if (!position) throw new Error(`No position ${intent.positionId} to reverse`);\n const quantity = position.quantity - (position.closedQuantity ?? 0);\n if (!(quantity > 0)) throw new Error(`Position ${intent.positionId} has nothing open to reverse`);\n await adapter.closePosition({ positionId: position.id });\n try {\n await adapter.placeOrder({\n side: position.side === 'buy' ? 'sell' : 'buy',\n type: 'market',\n price: position.entryPrice,\n quantity,\n });\n } catch (cause) {\n throw new Error(`Position ${intent.positionId} was closed, but the order the other way failed`, { cause });\n }\n } finally {\n reversing.delete(intent.positionId);\n }\n}\n\n/**\n * Bridge an `ExecutionAdapter` to a host: render the adapter's authoritative\n * orders/positions, and route the host's emitted intents into adapter\n * commands. The adapter is the single source of truth. Returns a teardown that\n * removes every subscription.\n */\nexport function wireExecution(adapter: ExecutionAdapter, host: ExecutionHost): () => void {\n const teardowns: Array<() => void> = [];\n\n // Adapter → host: authoritative state + errors. The positions are kept to\n // reverse one when the adapter can't.\n let positions: readonly TradingPosition[] | null = null;\n const context: CommandContext = {\n positions: () => positions ?? host.getPositions?.() ?? [],\n reversing: new Set(),\n };\n const onOrders = (e: ExecutionEvent<TradingOrder[]>) => host.setOrders(e.data ?? []);\n const onPositions = (e: ExecutionEvent<TradingPosition[]>) => {\n positions = e.data ?? [];\n host.setPositions([...positions]);\n };\n const onError = (e: ExecutionEvent<ExecutionError>) =>\n host.onError(e.data ?? { message: 'Execution error' });\n const onFill = (e: ExecutionEvent<FillEvent>) => {\n if (e.data) host.onFill?.(e.data);\n };\n adapter.on<TradingOrder[]>('orders', onOrders);\n adapter.on<TradingPosition[]>('positions', onPositions);\n adapter.on<ExecutionError>('error', onError);\n adapter.on<FillEvent>('fill', onFill);\n teardowns.push(\n () => adapter.off('orders', onOrders),\n () => adapter.off('positions', onPositions),\n () => adapter.off('error', onError),\n () => adapter.off('fill', onFill),\n );\n\n // Host → adapter: route each emitted intent into the matching command.\n for (const type of Object.keys(COMMANDS) as ExecutionIntentType[]) {\n const command = COMMANDS[type];\n const unsub = host.onIntent(type, (payload) => {\n Promise.resolve(command(adapter, payload, context)).catch((cause) =>\n host.onError({ message: `Execution command failed: ${type}`, cause }),\n );\n });\n teardowns.push(unsub);\n }\n\n return () => {\n for (const teardown of teardowns) teardown();\n };\n}\n","import type { DataSeries, OHLCBar, ReplayStepsLoadPayload, TimeFrame } from '@tradecanvas/commons';\n\n/** Finer bars ending before `before`, at most `limit` of them (a feed's page back). */\nexport type FinerSource = (before: number, limit: number) => Promise<DataSeries>;\n\n/** Finer bars asked for in one request: the resampler's page and most venues' cap. */\nexport const REPLAY_PAGE_STEPS = 1000;\n/** The least time between two requests (ms): at most two a second, kind to a venue's rate limit. */\nexport const MIN_REPLAY_ASK_GAP_MS = 500;\n\nexport interface ReplayStepLoaderOptions {\n /** Where the finer bars come from; null when the feed can't page back. */\n source: FinerSource | null;\n step: TimeFrame;\n /** Finer bars in one bar of the chart. */\n ratio: number;\n /** How long one bar of the chart is (ms), for the end of the last one. */\n barMs: number;\n /** The finer bars of the hours the chart shows. */\n keep: (bars: DataSeries) => DataSeries;\n report: (payload: ReplayStepsLoadPayload) => void;\n now?: () => number;\n}\n\n/**\n * The finer bars that shape a replay's bars as they form, fetched a page at\n * a time for the bars it is about to play and kept by bar time, so a series\n * rebuilt under the replay keeps them. A bar gets them only when its page\n * reached back to its start; one request at a time; pages that land after\n * `dispose()` are dropped (a feed can't be told to stop).\n */\nexport class ReplayStepLoader {\n private readonly pieces = new Map<number, OHLCBar[]>();\n /** Bars a page was asked for: not asked again until they are left behind. */\n private readonly asked = new Set<number>();\n private inFlight = false;\n private lastAsk = -Infinity;\n /** No more pages: the feed failed, can't page back, or the replay went. */\n private done = false;\n private readonly now: () => number;\n\n constructor(private readonly options: ReplayStepLoaderOptions) {\n this.now = options.now ?? (() => Date.now());\n }\n\n /** The finer bars of the bar opening at `time`, when they are in. */\n piecesOf(time: number): readonly OHLCBar[] | undefined {\n return this.pieces.get(time);\n }\n\n /** Have the finer bars of `coarse[from]` and the `ahead` bars after it on their way. */\n ensure(coarse: DataSeries, from: number, ahead: number): void {\n if (this.done || this.inFlight) return;\n if (!this.options.source) {\n this.finish('end', 0);\n return;\n }\n const until = Math.min(coarse.length, from + Math.max(1, ahead));\n let a = from;\n while (a < until && this.asked.has(coarse[a].time)) a++;\n if (a >= until) return;\n const now = this.now();\n if (now - this.lastAsk < MIN_REPLAY_ASK_GAP_MS) return;\n this.lastAsk = now;\n this.ask(coarse, a);\n }\n\n /** Forget the bars before `time`: the replay is past them (a seek back asks again). */\n evictBefore(time: number): void {\n for (const t of this.pieces.keys()) if (t < time) this.pieces.delete(t);\n for (const t of this.asked) if (t < time) this.asked.delete(t);\n }\n\n dispose(): void {\n this.done = true;\n }\n\n /** One page: the bars from `coarse[a]` that `REPLAY_PAGE_STEPS` finer ones cover, backward from the end of the last. */\n private ask(coarse: DataSeries, a: number): void {\n const { ratio, barMs, step, report } = this.options;\n const per = Math.max(1, Math.floor(REPLAY_PAGE_STEPS / ratio));\n const end = Math.min(coarse.length, a + per);\n const before = end < coarse.length ? coarse[end].time : coarse[coarse.length - 1].time + barMs;\n const bars = coarse.slice(a, end);\n for (const bar of bars) this.asked.add(bar.time);\n this.inFlight = true;\n report({ state: 'loading', step, count: 0 });\n this.options.source!(before, bars.length * ratio).then(\n (page) => this.take(bars, before, page),\n (err: unknown) => {\n if (this.done) return;\n this.inFlight = false;\n this.finish('error', 0, err instanceof Error ? err.message : String(err));\n },\n );\n }\n\n /** A page in: each of `bars` its finer bars, when the page reached back to its start. */\n private take(bars: OHLCBar[], before: number, page: DataSeries): void {\n if (this.done) return;\n this.inFlight = false;\n // None for these bars (a venue keeps only recent ones, say): later bars may have some.\n if (page.length === 0) {\n this.options.report({ state: 'end', step: this.options.step, count: 0 });\n return;\n }\n const earliest = page.reduce((t, b) => Math.min(t, b.time), Infinity);\n const kept = this.options.keep([...page].sort((x, y) => x.time - y.time));\n let count = 0;\n let k = 0;\n bars.forEach((bar, i) => {\n const stop = i + 1 < bars.length ? bars[i + 1].time : before;\n while (k < kept.length && kept[k].time < bar.time) k++;\n const from = k;\n while (k < kept.length && kept[k].time < stop) k++;\n if (k > from && earliest <= bar.time) {\n this.pieces.set(bar.time, kept.slice(from, k));\n count += k - from;\n }\n });\n this.options.report({ state: 'loaded', step: this.options.step, count });\n }\n\n private finish(state: 'end' | 'error', count: number, error?: string): void {\n this.done = true;\n this.options.report({ state, step: this.options.step, count, ...(error !== undefined ? { error } : {}) });\n }\n}\n","import type { OHLCBar } from '@tradecanvas/commons';\n\n/**\n * How one bar of the chart forms in a replay. The bar walks a path of\n * prices: from its finer bars when they are loaded, else from the bar itself\n * (open, the extreme its direction says came first, the other, close). The\n * replay's position is a bar and a fraction of it, so finer bars that land\n * late change how the next bars form, never which bar is which.\n */\nexport interface PathPoint {\n price: number;\n /** When it traded, as far as the bars tell: the finer bar's time. */\n time: number;\n /** How far into the bar (0..1) the replay is when it reaches this point. */\n at: number;\n}\n\nexport interface IntrabarPath {\n bar: OHLCBar;\n points: PathPoint[];\n /** Volume traded by the end of each finer piece, scaled to the bar's own volume. */\n volumeAt: { at: number; volume: number }[];\n}\n\n/** Each of a piece's three legs takes this share of it when the piece doesn't move. */\nconst FLAT_LEG = 1 / 3;\n/** How far outside the bar's range (as a share of it) a finer bar may close and still be the bar's. */\nconst PIECE_CLOSE_SLACK = 0.25;\n/** The share of finer bars that may close outside it before they are taken for another series. */\nconst MAX_STRAY_PIECES = 0.5;\n\n/**\n * Whether the finer bars trade where the bar does. A feed that makes up\n * each interval on its own (a demo feed, say) gives minutes far from the\n * hour; pulled onto the hour they would sit at its high or low for most of\n * it, and the bar would look whole a moment after it starts.\n */\nfunction piecesFit(bar: OHLCBar, pieces: readonly OHLCBar[]): boolean {\n const slack = (bar.high - bar.low) * PIECE_CLOSE_SLACK;\n const stray = pieces.filter((p) => p.close > bar.high + slack || p.close < bar.low - slack).length;\n return stray <= pieces.length * MAX_STRAY_PIECES;\n}\n\n/** A bar's own path: down first when it rose (or stayed), up first when it fell (as paper fills decide). */\nfunction ownPath(bar: OHLCBar): number[] {\n return bar.close >= bar.open ? [bar.open, bar.low, bar.high, bar.close] : [bar.open, bar.high, bar.low, bar.close];\n}\n\n/** The points of finer piece `j` of `n`: its legs take time in proportion to the price they cover. */\nfunction piecePoints(piece: OHLCBar, j: number, n: number): PathPoint[] {\n const prices = ownPath(piece);\n const legs = [1, 2, 3].map((k) => Math.abs(prices[k] - prices[k - 1]));\n const total = legs[0] + legs[1] + legs[2];\n const out: PathPoint[] = [];\n let walked = 0;\n prices.forEach((price, k) => {\n if (k > 0) walked += total > 0 ? legs[k - 1] / total : FLAT_LEG;\n // A piece's open is the previous piece's close in time: skip it when they meet.\n if (k === 0 && j > 0) return;\n out.push({ price, time: piece.time, at: (j + walked) / n });\n });\n return out;\n}\n\n/**\n * The path bar `bar` takes, from `finer` (its finer bars, sorted, all inside\n * it) or from itself (also when they trade somewhere else). Each finer bar takes an equal share of the bar's time,\n * and inside it the price moves at an even pace. The path is made to agree\n * with `bar`: it starts at its open, ends at its close and reaches its high\n * and low, so the last frame is the bar as the series has it (venues differ\n * between intervals: auction closes, trades missing from the minutes).\n */\nexport function intrabarPath(bar: OHLCBar, finer: readonly OHLCBar[] = []): IntrabarPath {\n const pieces = finer.length > 0 && piecesFit(bar, finer) ? finer : [bar];\n const n = pieces.length;\n const points = reconcile(pieces.flatMap((piece, j) => piecePoints(piece, j, n)), bar);\n const sum = pieces.reduce((s, p) => s + (p.volume ?? 0), 0);\n let acc = 0;\n const volumeAt = pieces.map((p, j) => {\n acc += sum > 0 ? ((p.volume ?? 0) / sum) * (bar.volume ?? 0) : (bar.volume ?? 0) / n;\n return { at: (j + 1) / n, volume: acc };\n });\n return { bar, points, volumeAt };\n}\n\n/** The path pulled onto the bar: its open, its close, inside its range and touching both ends of it. */\nfunction reconcile(raw: PathPoint[], bar: OHLCBar): PathPoint[] {\n const last = raw.length - 1;\n const points = raw.map((pt, i) => ({\n ...pt,\n price: i === 0 ? bar.open : i === last ? bar.close : Math.min(bar.high, Math.max(bar.low, pt.price)),\n }));\n // An extreme the open or close already is needs no point of its own; else the path's\n // highest (lowest) point inside the bar becomes it.\n const touch = (extreme: number, better: (a: number, b: number) => boolean): void => {\n if (points[0].price === extreme || points[last].price === extreme || last < 2) return;\n let at = 1;\n for (let i = 2; i < last; i++) if (better(points[i].price, points[at].price)) at = i;\n points[at] = { ...points[at], price: extreme };\n };\n touch(bar.high, (a, b) => a > b);\n touch(bar.low, (a, b) => a < b);\n return points;\n}\n\n/** The price `f` (0..1) of the way through the path. */\nfunction priceAt(path: IntrabarPath, f: number): number {\n const p = path.points;\n if (f <= p[0].at) return p[0].price;\n for (let i = 1; i < p.length; i++) {\n if (f <= p[i].at) {\n const span = p[i].at - p[i - 1].at;\n return span > 0 ? p[i - 1].price + ((p[i].price - p[i - 1].price) * (f - p[i - 1].at)) / span : p[i].price;\n }\n }\n return p[p.length - 1].price;\n}\n\n/** `fraction` clamped to 0..1 (NaN is the start). */\nconst clampFraction = (fraction: number): number => (Number.isFinite(fraction) ? Math.min(1, Math.max(0, fraction)) : 0);\n\n/** The volume traded `f` of the way through the path. */\nfunction volumeAt(path: IntrabarPath, f: number): number {\n let done = 0;\n let start = 0;\n for (const v of path.volumeAt) {\n if (f >= v.at) {\n done = v.volume;\n start = v.at;\n continue;\n }\n return done + ((v.volume - done) * (f - start)) / (v.at - start);\n }\n return done;\n}\n\n/** The bar as it stands `fraction` of the way through it (clamped; NaN is its start). */\nexport function formingAt(path: IntrabarPath, fraction: number): OHLCBar {\n const f = clampFraction(fraction);\n if (f >= 1) return { ...path.bar };\n const close = priceAt(path, f);\n let high = Math.max(path.points[0].price, close);\n let low = Math.min(path.points[0].price, close);\n for (const pt of path.points) {\n if (pt.at > f) break;\n high = Math.max(high, pt.price);\n low = Math.min(low, pt.price);\n }\n return { time: path.bar.time, open: path.points[0].price, high, low, close, volume: volumeAt(path, f) };\n}\n\n/**\n * Where reduced motion shows the bar `fraction` of the way through it: at\n * the last point of the path reached by then, so the price steps from one\n * point to the next without sliding between them.\n */\nexport function pathStop(path: IntrabarPath, fraction: number): number {\n const f = clampFraction(fraction);\n let at = 0;\n for (const pt of path.points) {\n if (pt.at > f) break;\n at = pt.at;\n }\n return at;\n}\n\n/** The prices passed after `from` up to `to`, with their times, then where it is at `to` (for a paper account). */\nexport function pathMarks(path: IntrabarPath, from: number, to: number): [number, number][] {\n const out: [number, number][] = [];\n let lastTime = path.bar.time;\n for (const pt of path.points) {\n if (pt.at > to) break;\n lastTime = pt.time;\n if (pt.at > from) out.push([pt.price, pt.time]);\n }\n const here = priceAt(path, to);\n if (out.length === 0 || out[out.length - 1][0] !== here) out.push([here, lastTime]);\n return out;\n}\n","import type { TimeFrame } from '@tradecanvas/commons';\nimport { timeframeToMs } from '@tradecanvas/commons';\n\n/** Finer intervals a replay may step through (those dividing the chart's interval are offered). */\nexport const REPLAY_STEP_CANDIDATES: readonly TimeFrame[] = ['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '1d'];\n\n/**\n * The most finer bars `'auto'` puts in one bar of the chart. At a bar a\n * second a bar lasts about 60 frames, so finer ones add nothing to see; it\n * also keeps a page of 1,000 to 16 hourly bars.\n */\nexport const MAX_STEPS_PER_BAR = 60;\n\n/** Finer intervals than `chart` that divide it and that `has` (the feed serves, or the bars loaded build), finest first. */\nexport function replayStepChoices(chart: TimeFrame, has: (tf: TimeFrame) => boolean): TimeFrame[] {\n const current = timeframeToMs(chart);\n if (!(current > 0)) return [];\n return REPLAY_STEP_CANDIDATES.filter((tf) => {\n const ms = timeframeToMs(tf);\n return ms < current && current % ms === 0 && has(tf);\n });\n}\n\n/**\n * The interval of the finer bars for a replay of a `chart` chart: `'auto'`\n * is the finest of the choices with at most MAX_STEPS_PER_BAR to a bar; an\n * interval given is taken when it is one of them. Null when there is none.\n */\nexport function resolveReplayStep(step: TimeFrame | 'auto', chart: TimeFrame, has: (tf: TimeFrame) => boolean): TimeFrame | null {\n const choices = replayStepChoices(chart, has);\n if (step !== 'auto') return choices.includes(step) ? step : null;\n const current = timeframeToMs(chart);\n return choices.find((tf) => current / timeframeToMs(tf) <= MAX_STEPS_PER_BAR) ?? null;\n}\n","import { readAlertOptions, type AlertManager, type PriceAlert } from '@tradecanvas/core';\nimport type { IndicatorOutput } from '@tradecanvas/commons';\n\n/** What checking the alerts needs of them. */\nexport type WatchedAlerts = Pick<AlertManager, 'getAlerts' | 'setBarTime' | 'checkChannels' | 'checkDrawings'>;\n\n/** An indicator's values at each bar, by its id (undefined: no such indicator). */\nexport type IndicatorLines = (instanceId: string) => IndicatorOutput['series'];\n\n/** A moment the alerts are checked at. */\nexport interface AlertMoment {\n /** The live price. */\n price: number;\n /** The time of the latest live bar; null with none. */\n barTime: number | null;\n /** The indicators' lines, or null when alerts on them wait (a replay, no bars). */\n lines: IndicatorLines | null;\n}\n\n/**\n * Alerts against a live price, and against the indicator lines they watch,\n * all as of the same moment (a line crossing another compares like with\n * like). A line against a line fires only with both of this moment.\n */\nexport function checkAlerts(alerts: WatchedAlerts, moment: AlertMoment): void {\n alerts.setBarTime(moment.barTime);\n const values = new Map<string, number>([['price', moment.price]]);\n if (moment.lines) collectLineValues(alerts.getAlerts(), moment.lines, values);\n alerts.checkChannels(values, { together: true });\n alerts.checkDrawings(moment.price);\n}\n\n/**\n * The latest values of the indicator lines alerts watch or compare with.\n * Channel format: `<instanceId>:<key>`; instanceIds are `tc_<id>_<n>` and\n * built-in keys are colon-free, so the first colon splits them. Values come\n * from the recalculated series, so indicator alerts evaluate at bar cadence\n * (the live intrabar tick doesn't repaint closed-bar indicator values).\n * Cheap: only walks alerts whose channel isn't `'price'`, usually none.\n */\nfunction collectLineValues(alerts: readonly PriceAlert[], lines: IndicatorLines, into: Map<string, number>): void {\n const channels = alerts.flatMap((a) => (a.target ? [a.channel, a.target] : [a.channel]));\n for (const channel of channels) {\n if (into.has(channel)) continue;\n const sep = channel.indexOf(':');\n if (sep < 0) continue;\n // The line's last value: at the last bar, or on Renko and the like the last brick.\n const series = lines(channel.slice(0, sep));\n const point = series?.[series.length - 1];\n const value = point?.[channel.slice(sep + 1)];\n if (typeof value === 'number' && Number.isFinite(value)) into.set(channel, value);\n }\n}\n\n/** What moving alerts to other indicators needs of them. */\nexport type MovedAlerts = Pick<AlertManager, 'getAlerts' | 'removeAlert' | 'addAlert'>;\n\n/**\n * Alerts on the lines of indicators that went (`before`: id → kind) move to\n * the one new indicator of the same kind (`added`), when there is just one;\n * others stay as they are, as alerts on a removed indicator do. `kindOf` is\n * an indicator's kind now, undefined for one not on the chart.\n */\nexport function moveAlertsToHeirs(\n alerts: MovedAlerts,\n before: ReadonlyMap<string, string>,\n added: readonly string[],\n kindOf: (instanceId: string) => string | undefined,\n): void {\n // A channel on a replaced indicator → the one new indicator of its kind (or null: leave it).\n const heirOf = (channel: string): string | null => {\n const sep = channel.indexOf(':');\n if (sep <= 0) return null;\n const from = channel.slice(0, sep);\n const kind = before.get(from);\n if (!kind || kindOf(from) !== undefined) return null;\n const heirs = added.filter((id) => kindOf(id) === kind);\n return heirs.length === 1 ? heirs[0] + channel.slice(sep) : null;\n };\n for (const alert of alerts.getAlerts()) {\n const channel = heirOf(alert.channel);\n const target = alert.target ? heirOf(alert.target) : null;\n if (!channel && !target) continue;\n const { percent, bars, onBarClose, expiresAt } = alert;\n alerts.removeAlert(alert.id);\n alerts.addAlert(alert.price, alert.condition, alert.message, alert.repeating, channel ?? alert.channel, alert.label, {\n ...(alert.target ? { target: target ?? alert.target } : {}),\n ...(percent !== undefined ? { percent } : {}),\n ...(bars !== undefined ? { bars } : {}),\n ...(onBarClose ? { onBarClose } : {}),\n ...(expiresAt !== undefined ? { expiresAt } : {}),\n });\n }\n}\n\n/** A `<instanceId>:<key>` channel following its indicator to its new id (`ids`: old → new); others as they are. */\nexport function followChannel(channel: string, ids: ReadonlyMap<string, string>): string {\n const sep = channel.indexOf(':');\n const renamed = sep > 0 ? ids.get(channel.slice(0, sep)) : undefined;\n return renamed ? renamed + channel.slice(sep) : channel;\n}\n\n/** What putting a layout's alerts back needs of them. */\nexport type RestoredAlerts = Pick<AlertManager, 'clearAlerts' | 'addAlert' | 'addDrawingAlert'>;\n\nconst DRAWING_CONDITIONS: readonly PriceAlert['condition'][] = ['crossing', 'crossingUp', 'crossingDown'];\n\n/**\n * A layout's alerts in place of the chart's: their lines following the\n * indicators to their new ids (`ids`), an alert on a drawing only when the\n * drawing has a line to cross (`crossable`). A one-shot alert that already\n * fired stays fired (as AlertManager's own storage does), an expired one\n * stays gone, and one that no longer fits together goes to `leftOut`.\n */\nexport function restoreLayoutAlerts(\n alerts: RestoredAlerts,\n saved: readonly PriceAlert[],\n ids: ReadonlyMap<string, string>,\n crossable: (drawingId: string) => boolean,\n leftOut: (error: unknown) => void,\n): void {\n alerts.clearAlerts();\n for (const a of saved) {\n if (a.triggered && !a.repeating) continue;\n if (a.expired) continue;\n if (a.drawingId) {\n if (crossable(a.drawingId) && DRAWING_CONDITIONS.includes(a.condition)) {\n alerts.addDrawingAlert(a.drawingId, a.condition, a.message, a.repeating, a.label);\n }\n continue;\n }\n const options = { ...readAlertOptions(a as unknown as Record<string, unknown>), ...(a.target ? { target: followChannel(a.target, ids) } : {}) };\n try {\n alerts.addAlert(a.price, a.condition, a.message, a.repeating, followChannel(a.channel, ids), a.label, options);\n } catch (err) {\n leftOut(err);\n }\n }\n}\n\n/** What keeping a drawing's alerts needs of them. */\nexport type StashedAlerts = Pick<AlertManager, 'takeDrawingAlerts' | 'restoreAlerts' | 'pruneDrawingAlerts'>;\n\n/**\n * A drawing's alerts go with it, and come back with it when an undo brings\n * the drawing back.\n */\nexport class DrawingAlertStash {\n private readonly kept = new Map<string, PriceAlert[]>();\n\n constructor(private readonly alerts: StashedAlerts) {}\n\n /** The drawing went: its alerts are kept aside. */\n removed(drawingId: string): void {\n const taken = this.alerts.takeDrawingAlerts(drawingId);\n if (taken.length > 0) this.kept.set(drawingId, taken);\n }\n\n /** The drawing is (back) on the chart: the alerts kept for it come back. */\n created(drawingId: string): void {\n const back = this.kept.get(drawingId);\n if (!back) return;\n this.kept.delete(drawingId);\n this.alerts.restoreAlerts(back);\n }\n\n /** The drawings were replaced: alerts on drawings not in `drawingIds` go, kept ones too. How many went. */\n prune(drawingIds: ReadonlySet<string>): number {\n this.kept.clear();\n return this.alerts.pruneDrawingAlerts(drawingIds);\n }\n}\n","import type { DataSeries, OHLCBar } from '@tradecanvas/commons';\nimport { formingAt, pathMarks, type IntrabarPath } from '../replay/intrabarPath.js';\n\n/** A long jump goes to a paper account in at most this many chunks of bars, each giving its extremes and its close. */\nexport const MAX_REPLAY_CHUNKS = 500;\n\n/** A price a paper account trades on, and its time: `[price, time]`. */\nexport type ReplayMark = [number, number];\n\n/** How far a paper account has been given the replay: the latest time, and how far into that bar (1: all of it). */\nexport interface ReplayFed {\n time: number | null;\n fraction: number;\n}\n\n/** The marks to give a paper account, in order, and how far it has been given the replay after them. */\nexport interface ReplayFeed {\n marks: ReplayMark[];\n fed: ReplayFed;\n}\n\n/** Index of the first bar at or after `time` (the length when none is). */\nexport function firstAtOrAfter(series: DataSeries, time: number): number {\n let lo = 0;\n let hi = series.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (series[mid].time < time) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n}\n\n/** The first bar of `series` after `time` (its length when none is). */\nexport function firstAfter(series: DataSeries, time: number): number {\n let lo = 0;\n let hi = series.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n if (series[mid].time <= time) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n}\n\n/**\n * The prices a run of bars went through, in order, as far as the bars tell:\n * each extreme with its time, the earlier one first (within one bar, a rising\n * bar dipped first), then the close.\n */\nexport function replayMarks(run: readonly OHLCBar[]): ReplayMark[] {\n let lowAt = 0;\n let highAt = 0;\n for (let i = 1; i < run.length; i++) {\n if (run[i].low < run[lowAt].low) lowAt = i;\n if (run[i].high > run[highAt].high) highAt = i;\n }\n const low: ReplayMark = [run[lowAt].low, run[lowAt].time];\n const high: ReplayMark = [run[highAt].high, run[highAt].time];\n const lowFirst = lowAt !== highAt ? lowAt < highAt : run[lowAt].close >= run[lowAt].open;\n const last = run[run.length - 1];\n return [...(lowFirst ? [low, high] : [high, low]), [last.close, last.time]];\n}\n\n/** The marks of `run`, in chunks of at most `MAX_REPLAY_CHUNKS` of them. */\nfunction chunkedMarks(run: readonly OHLCBar[]): ReplayMark[] {\n const marks: ReplayMark[] = [];\n const size = Math.ceil(run.length / MAX_REPLAY_CHUNKS);\n for (let i = 0; i < run.length; i += size) marks.push(...replayMarks(run.slice(i, i + size)));\n return marks;\n}\n\n/**\n * A paper account trades on the replayed prices and times, forward only:\n * it gets every step past the latest time it has seen (a jump ahead passes\n * them all, a long one in chunks), and nothing after a seek back or a\n * restart from an earlier bar until the replay is past that time again\n * (null). At the start its mark is where the replay begins.\n */\nexport function stepFeed(series: DataSeries, index: number, fed: ReplayFed): ReplayFeed | null {\n const bar = series[index];\n if (!bar) return null;\n if (fed.time === null) return { marks: [[bar.close, bar.time]], fed: { time: bar.time, fraction: fed.fraction } };\n if (bar.time <= fed.time) return null;\n const run = series.slice(firstAfter(series, fed.time), index + 1);\n return { marks: chunkedMarks(run), fed: { time: bar.time, fraction: fed.fraction } };\n}\n\n/**\n * A paper account trades on the prices a forming bar has been drawn\n * through, forward only by (time, fraction): the rest of a bar given in\n * part (along `pathOf` it), the bars a jump passes, then bar `index` of\n * `coarse` along `path` up to `fraction`. Null when that is behind.\n */\nexport function formingFeed(\n coarse: DataSeries,\n index: number,\n path: IntrabarPath,\n fraction: number,\n fed: ReplayFed,\n pathOf: (coarse: DataSeries, index: number) => IntrabarPath,\n): ReplayFeed | null {\n const bar = coarse[index];\n if (!bar) return null;\n const at = { time: bar.time, fraction };\n if (fed.time === null) return { marks: [[formingAt(path, fraction).close, bar.time]], fed: at };\n if (bar.time < fed.time || (bar.time === fed.time && fraction <= fed.fraction)) return null;\n if (bar.time === fed.time) return { marks: pathMarks(path, fed.fraction, fraction), fed: at };\n const marks: ReplayMark[] = [];\n const part = firstAtOrAfter(coarse, fed.time);\n if (coarse[part]?.time === fed.time && fed.fraction < 1) marks.push(...pathMarks(pathOf(coarse, part), fed.fraction, 1));\n marks.push(...chunkedMarks(coarse.slice(firstAfter(coarse, fed.time), index)));\n marks.push(...pathMarks(path, -1, fraction));\n return { marks, fed: at };\n}\n","import type {\n ChartOptions,\n ChartType,\n OHLCBar,\n DataSeries,\n Theme,\n ThemeName,\n ChartEventType,\n ChartEvent,\n ChartEventMap,\n TauriBridgeOptions,\n IndicatorPlugin,\n IndicatorDescriptor,\n IndicatorOutput,\n ResolvedIndicatorStyle,\n DrawingToolType,\n DrawingState,\n DrawingStyle,\n DrawingOptions,\n DrawingOptionDefs,\n DrawingDescriptor,\n DrawingPlugin,\n PanelPosition,\n TradingOrder,\n TradingPosition,\n DepthData,\n TradingConfig,\n MarketConfig,\n PriceLimitInfo,\n Locale,\n StreamConfig,\n ConnectionState,\n ConnectionInfo,\n TimeFrame,\n DataAdapter,\n TimeZoneSetting,\n SymbolInfo,\n OverlayScale,\n ViewportState,\n FeaturesConfig,\n ExecutionAdapter,\n ExecutionConfig,\n CrosshairCursor,\n} from '@tradecanvas/commons';\nimport { isValidTimeZone, sessionMinute, symbolTimeZone, LayerType, setLocale as setGlobalLocale, computePriceLimits, createMarketTheme, priceStepAt, PRICE_AXIS_WIDTH, autoPricePrecision, formatPrice, stepDecimals, priceFormatterFor, fractionTick } from '@tradecanvas/commons';\nimport type { BarLayout, ChartTypeOptions, DataUpdatePayload, FontConfig, IndicatorId, IndicatorParamsFor, PriceFormatter, PriceFraction, ShapeConfig, TimeFormatter } from '@tradecanvas/commons';\nimport type { ResolvedLayout, ChartStyleKey, ChartStyleOverrides, ChartStyleOverridesPatch, IndicatorIntervals, IndicatorStyleConfig, PaneStyle, ResolvedChartStyle, StyleLayer } from '@tradecanvas/commons';\nimport { readChartTypeOptions, tickBarCount, barTimeUnit, readChartStyleOverrides, readIndicatorPlotStyles, readPaneStyle } from '@tradecanvas/commons';\nimport { PriceLines, type BidAsk, SymbolSeriesStore, CompareSymbolIndicator, CompareSymbolPaneIndicator, SpreadIndicator, CorrelationCoefficientIndicator, CorrelationLogIndicator, axisTimes, HiLoRenderer, PointAndFigureRenderer, isPriceLevelAlert, loadWebGL, gpuContextsLeft, whenGpuContextFree, idleCssCursor, isCrosshairCursor } from '@tradecanvas/core';\nimport type { GpuRenderer, RendererMode } from '@tradecanvas/core';\nimport { regularHoursFilter } from './regularHours.js';\nimport { ChartA11y } from './chartA11y.js';\nimport { barDateZone, barsAreDaily, zonedDateFormatter } from '@tradecanvas/commons';\nimport { indicatorChipLabel } from './indicatorLabel.js';\nimport type { ExportColumn } from '@tradecanvas/core';\nimport {\n RenderEngine,\n Viewport,\n ResamplingAdapter,\n withResampling,\n servesTimeframe,\n streamAdapterOf,\n SYMBOL_ZONE_WAIT_MS,\n GridRenderer,\n PriceAxis,\n TimeAxis,\n InteractionManager,\n PaneResizeHandler,\n PanHandler,\n ZoomHandler,\n AxisDragHandler,\n AlertDragHandler,\n CrosshairHandler,\n IndicatorEngine,\n registerBuiltInIndicators,\n EventBus,\n DrawingManager,\n DrawingRenderer,\n registerBuiltInDrawingTools,\n TradingManager,\n TradingRenderer,\n StreamManager,\n ChartLegend,\n Screenshot,\n Watermark,\n BarCountdown,\n VolumeRenderer,\n VolumeProfileRenderer,\n MarketProfileRenderer,\n DepthHeatmapRenderer,\n PeriodLevelsRenderer,\n PivotMarkersRenderer,\n AlertManager,\n SignalMarkerManager,\n TradeZoneManager,\n MeasureOverlay,\n SelectionBoxOverlay,\n timestampToBarIndex,\n barTimeStepMs,\n rangePresetStart,\n ReplayManager,\n ChartStateManager,\n UndoRedoManager,\n Animator,\n KeyboardHandler,\n CrosshairTooltip,\n PinnedTooltip,\n DataExporter,\n SessionBreaks,\n SessionShading,\n DEFAULT_SESSION_HOURS,\n mergeSessionHours,\n CompareRenderer,\n CurrentPriceLine,\n xToBarIndex,\n xToTime,\n yToPrice,\n PriceAxisAddButton,\n PRICE_AXIS_ADD_STRIP,\n findDominantSwing,\n} from '@tradecanvas/core';\nimport type { ChartRendererInterface, CompareValue, RangePreset, SessionHoursConfig, DrawingPatch, DrawingOrderMove } from '@tradecanvas/core';\nimport { timeframeToMs } from '@tradecanvas/commons';\nimport { resolveRenderer, isReshapedChartType } from './charts/ChartTypeStrategy.js';\nimport { AutoSaveScheduler } from './state/AutoSaveScheduler.js';\nimport { isValidBar } from './DataManager.js';\nimport { SeriesStore, isWholeBar, type LiveChange } from './SeriesStore.js';\nimport { PriceFit, hasVolume } from './model/PriceFit.js';\nimport { DisplaySeries } from './model/DisplaySeries.js';\nimport { PaneLayout } from './model/PaneLayout.js';\nimport { IndicatorHistory, INDICATOR_EDIT_MERGE_MS } from './model/IndicatorHistory.js';\nimport { IndicatorPanes } from './model/IndicatorPanes.js';\nimport { captureIndicatorSetup, dependencyOrder, keptInPlace, renameSources, restorePanes } from './model/IndicatorSetup.js';\nimport { SymbolRequests } from './model/SymbolRequests.js';\nimport { drawingPaneAt, drawingPaneViewport, takesDrawing } from './model/DrawingPanes.js';\nimport { HistoryPager, type HistoryLoader } from './HistoryPager.js';\nimport { ThemeManager } from './ThemeManager.js';\nimport { startOverrides } from './style/styleOptions.js';\nimport { LayoutManager } from './layout/LayoutManager.js';\nimport { requiredPriceAxisWidth, nextPriceAxisWidth } from './layout/priceAxisWidth.js';\nimport { LabelWidths } from './layout/labelWidths.js';\nimport { VerticalPanGate } from './interaction/verticalPanGate.js';\nimport { PluginManager } from './plugins/PluginManager.js';\nimport { wireExecution } from './trading/wireExecution.js';\nimport { overlaysForLayer, type ChartPlugin } from './plugins/contracts.js';\nimport { ReplayStepLoader } from './replay/ReplayStepLoader.js';\nimport { formingAt, intrabarPath, pathStop, type IntrabarPath } from './replay/intrabarPath.js';\nimport { resolveReplayStep } from './replay/replayStep.js';\nimport { checkAlerts, DrawingAlertStash, moveAlertsToHeirs, restoreLayoutAlerts } from './model/AlertWatch.js';\nimport { firstAfter, firstAtOrAfter, formingFeed, stepFeed, type ReplayMark } from './model/ReplayMarks.js';\n\n// Replaced at build time by Vite `define` (see vite.config.ts). The `typeof`\n// guard keeps this safe when the source runs un-bundled (tests, ts-node).\ndeclare const __TC_VERSION__: string;\n\n/** `setTimeZone(EXCHANGE_TIME_ZONE)` shows the zone of the symbol's exchange, else of its market (`getExchangeTimeZone`). */\nexport const EXCHANGE_TIME_ZONE = 'exchange';\n/** @deprecated Use `EXCHANGE_TIME_ZONE`. */\nexport const EXCHANGE_TIMEZONE = EXCHANGE_TIME_ZONE;\n/** Throws a RangeError for a time zone the browser doesn't know or an offset that isn't a number. */\nfunction assertTimeZone(tz: TimeZoneSetting): void {\n if (typeof tz === 'string' && tz !== EXCHANGE_TIME_ZONE && !isValidTimeZone(tz)) {\n throw new RangeError(`Unknown time zone: ${tz}`);\n }\n if (typeof tz === 'number' && !Number.isFinite(tz)) throw new RangeError(`Invalid UTC offset: ${tz}`);\n}\n/**\n * A symbol's zone under both names: `timeZone`, and the older `timezone` that\n * code may still read. When the two disagree, the one that moved from `held`\n * (the zone the chart has for this symbol) is the new zone, so\n * `{ ...getSymbolInfo(), timezone: zone }` changes it as `timeZone: zone` does;\n * otherwise `timeZone` is.\n */\nfunction withBothZoneSpellings(info: SymbolInfo, held: string | undefined): SymbolInfo {\n const { timeZone, timezone } = info;\n const moved = timeZone !== undefined && timezone !== undefined && timeZone !== timezone && timeZone === held;\n const zone = moved ? timezone : symbolTimeZone(info);\n return zone === undefined ? info : { ...info, timeZone: zone, timezone: zone };\n}\n\n/** Room above and below the left scale's overlays, as a share of their range. */\nconst LEFT_SCALE_PADDING = 0.08;\n/** Fewest bars ahead of the view that trigger the next history page. */\nconst HISTORY_AHEAD_MIN_BARS = 20;\n\n/** Narrowest box (px) the zoom-area tool zooms into. */\nconst MIN_ZOOM_BOX_PX = 8;\n/** Arrow keys on selected drawings: a bar across or a pixel up or down; Shift moves this many. */\nconst NUDGE_SHIFT_STEPS = 10;\n/** Each arrow key's step for selected drawings: bars across, pixels down. */\nconst NUDGE_KEYS: Record<string, [bars: number, px: number]> = {\n ArrowLeft: [-1, 0],\n ArrowRight: [1, 0],\n ArrowUp: [0, -1],\n ArrowDown: [0, 1],\n};\n\n/** Drawings drawn across the whole plot's width, or its height, whatever their anchors. */\nconst FULL_WIDTH_DRAWINGS = new Set(['horizontalLine', 'crossLine']);\nconst FULL_HEIGHT_DRAWINGS = new Set(['verticalLine', 'crossLine']);\n\nexport class Chart {\n static version = typeof __TC_VERSION__ !== 'undefined' ? __TC_VERSION__ : '0.0.0-dev';\n\n private engine: RenderEngine;\n private viewport: Viewport;\n /** The bars: those shown, the whole series while some hours are hidden, the live one during a replay. */\n private readonly series = new SeriesStore((bars) => this.sessionKeep(bars), (change) => this.followLiveChange(change));\n private themeManager: ThemeManager;\n private layoutManager: LayoutManager;\n private pluginManager: PluginManager;\n private indicatorEngine: IndicatorEngine;\n private drawingManager: DrawingManager;\n private drawingRenderer: DrawingRenderer;\n private tradingManager: TradingManager;\n private tradingRenderer: TradingRenderer;\n private eventBus: EventBus;\n private streamManager: StreamManager | null = null;\n private executionAdapter: ExecutionAdapter | null = null;\n private execTeardown: (() => void) | null = null;\n private autoScrollOnNewBar = true;\n /** The drawn series (Renko bricks…) the indicators were last worked out on; null: the data bars. */\n private indicatorsOnDrawn: DataSeries | null = null;\n /** The series the chart type draws, kept between frames. */\n private readonly display: DisplaySeries;\n /** Earliest bar of an `indicatorUpdate` waiting to be emitted; null = none pending. */\n private indicatorUpdateFrom: number | null = null;\n /** The indicator panes as drawn: their layout, scales and own styles. */\n private readonly panes: PaneLayout;\n /** Which pane each indicator is drawn in, and who takes a pane over. */\n private readonly indicatorPanes: IndicatorPanes;\n /** The indicators' undo steps: a burst of edits as one, an edit (a settings dialog) as one. */\n private readonly indicatorHistory: IndicatorHistory;\n /** The background last put on the container (setTheme, a style change), so a host's own stays. */\n private lastBackground: string | null = null;\n private renderScheduled = false;\n /** A live tick's refit left for when the chart draws again (it is paused). */\n private renderDeferred = false;\n /** Current price-axis width; grows to fit long (e.g. sub-cent) labels. */\n private priceAxisWidth = PRICE_AXIS_WIDTH;\n /** A canvas out of the page, to measure on when the chart has none of its own (destroyed). */\n private measureCtx: CanvasRenderingContext2D | null | undefined;\n /** The axis labels' widths as measured on the chart's canvas in the page, kept while panning. */\n private readonly labelWidths = new LabelWidths();\n private containerSizeCache: { width: number; height: number } | null = null;\n private containerSizeCacheTime = 0;\n // Last-emitted viewport values, used to fire visibleRangeChange /\n // priceRangeChange / zoomChange only when something actually moved.\n private lastEmittedRange: { from: number; to: number } | null = null;\n private lastEmittedPriceRange: { min: number; max: number } | null = null;\n private lastEmittedBarWidth: number | null = null;\n private chartLegend: ChartLegend;\n /** What a host lays under the legend (`setLegendStack`), for drawing labels to keep clear of. */\n private legendStack: { width: number; height: number } | null = null;\n private watermark: Watermark;\n private barCountdown: BarCountdown;\n /**\n * Indicators of the loaded layout this chart could not add (an unknown or\n * not yet registered id, a whitelist): saved back as they were, so saving\n * the layout again doesn't drop them.\n */\n private unrestoredIndicators: import('@tradecanvas/core').SnapshotIndicator[] = [];\n /** Alerts of deleted drawings, put back if an undo brings the drawing back (none before the alerts are made). */\n private drawingAlerts: DrawingAlertStash | null = null;\n /** The zoom-area tool is waiting for its box. */\n private zoomAreaMode = false;\n /** The \"+\" by the price axis. */\n private priceAxisAddButton = new PriceAxisAddButton();\n /** The time zone shown: the setting, with 'exchange' resolved (`getExchangeTimeZone`, else UTC). */\n private displayTz: TimeZoneSetting = null;\n /** What `setTimeZone` was given; 'exchange' follows the symbol, else its market. */\n private timeZoneSetting: TimeZoneSetting = null;\n /** What the feed (or host) said about the symbol on the chart. */\n private symbolInfo: SymbolInfo | null = null;\n /** Bumped per symbol asked about: an answer about a symbol the chart left is dropped. */\n private symbolInfoSeq = 0;\n /**\n * While the stream asks about its next symbol, the indicators, the\n * prior-day lines, the market profile and the bars built from trades keep\n * the last symbol's zone: the next one most often trades on the same\n * exchange, so its bars compute once, in the zone they keep.\n */\n private studyZoneHeld = false;\n /** The zone `applyStudyZone` last gave: the one held while `studyZoneHeld`. */\n private studyZone: string | null = null;\n /** Ends the hold when the feed says nothing of the next symbol for as long as its built days wait (`SYMBOL_ZONE_WAIT_MS`). */\n private studyZoneTimer: ReturnType<typeof setTimeout> | null = null;\n /** A disconnect's end of the hold, waiting to see whether a connect follows at once. */\n private studyZoneReleaseQueued = false;\n private sessionBreaks: SessionBreaks;\n private sessionShading: SessionShading;\n /** The session hours the host set (or the default); a symbol's own hours show over them. */\n private hostSessionHours: SessionHoursConfig = { ...DEFAULT_SESSION_HOURS };\n private compareRenderer: CompareRenderer;\n private countdownInterval: ReturnType<typeof setInterval> | null = null;\n /** Why drawing is paused: the host's `suspend()`, a hidden page, the chart off screen. */\n private pausedFor = { host: false, page: false, offscreen: false };\n /** Undoes the page listeners `watchVisibility` set. */\n private stopWatchingVisibility: (() => void) | null = null;\n private volumeRenderer: VolumeRenderer;\n private volumeProfile: VolumeProfileRenderer;\n private marketProfile: MarketProfileRenderer;\n private depthHeatmap: DepthHeatmapRenderer;\n private periodLevels: PeriodLevelsRenderer;\n private pivotMarkers: PivotMarkersRenderer;\n private alertManager: AlertManager;\n private signalMarkerManager: SignalMarkerManager;\n private tradeZoneManager: TradeZoneManager;\n private measureOverlay: MeasureOverlay;\n private selectionBoxOverlay = new SelectionBoxOverlay();\n private replayManager: ReplayManager;\n private replayBarUnsub: (() => void) | null = null;\n /** True while `replaySeek` runs: its bar is a jump, not a replay step. */\n private replaySeeking = false;\n /**\n * While a replay runs. The live series keeps moving aside in the series\n * store — stream and host updates land there instead of the replayed\n * slice — and comes back on `replayStop()`. `price` is the latest live tick.\n */\n private replaySession: ReplaySession | null = null;\n /** Why the replay is being rebuilt, while it is: its step says so. */\n private replayCause: 'series' | 'older' | null = null;\n /** During a replay in finer steps: the steps, and the series they build. */\n private replayStepsSeries: DataSeries | null = null;\n private replayCoarse: DataSeries | null = null;\n /** Whether the running replay shows markers and trades only as it reaches them. */\n private replayRevealMarks = true;\n /** Bars of the series left out before a replay's window (`hideHistory`). */\n private replayOffset = 0;\n /** With `animate`: the host's finer bars by the time of the bar they shape. */\n private replayPieces: Map<number, OHLCBar[]> | null = null;\n /** With `animate` and a `step`: the finer bars fetched for the bars ahead. */\n private replayLoader: { key: string; loader: ReplayStepLoader } | null = null;\n /** The bar forming on screen and the path it began with (it keeps it: a page landing late shapes later bars). */\n private replayForming: { index: number; path: IntrabarPath } | null = null;\n private replayPhaseUnsub: (() => void) | null = null;\n private undoRedoManager: UndoRedoManager;\n private autoSaveScheduler = new AutoSaveScheduler((key) => this.saveState(key));\n private animator: Animator;\n private crosshairTooltip: CrosshairTooltip;\n private pinnedTooltip: PinnedTooltip;\n private interactionManager: InteractionManager;\n private crosshairHandler: CrosshairHandler;\n private chartRenderer: ChartRendererInterface;\n /** The latest `setRenderer` call: an earlier one that resolves later is dropped. */\n private rendererToken = 0;\n /** Stops waiting for a WebGL context to come free (the charts held all they may). */\n private stopWaitingForGpu: (() => void) | null = null;\n private disposed = false;\n /** Settings of the chart types that build their own bars. */\n private chartTypeOptions: ChartTypeOptions = {};\n private mainSeriesVisible = true;\n /** The visible high and low, the bid and the ask. */\n private priceLines = new PriceLines();\n /** Other symbols' bars, for the indicators that compare with them. */\n private symbolSeries = new SymbolSeriesStore();\n /** Bars outside the symbol's regular hours shown. */\n private extendedHours = true;\n /** A replay being restarted (or the chart going): its stop is not reported. */\n private replayRestarting = false;\n /** The other symbols the indicators read, and those asked for (`symbolSeriesRequest`) and not given yet. */\n private readonly symbolRequests: SymbolRequests;\n private gridRenderer: GridRenderer;\n private priceAxis: PriceAxis;\n /** The left price scale: overlays put on it, else a mirror of the price scale. */\n private leftPriceAxis = new PriceAxis();\n private leftScaleRequested = false;\n private leftAxisWidth = PRICE_AXIS_WIDTH;\n /** The left scale's viewport this frame; null while it is hidden. */\n private leftViewport: ViewportState | null = null;\n private timeAxis: TimeAxis;\n private options: ChartOptions & { chartType: ChartType };\n private features: Required<FeaturesConfig>;\n private marketConfig: MarketConfig | null = null;\n private container: HTMLElement;\n private currentPriceLine: import('@tradecanvas/core').CurrentPriceLine;\n private numberLocale: string;\n /** The market's price precision, when set: otherwise it follows the visible range. */\n private marketPricePrecision: number | null = null;\n /** The chart's price format (`priceFormat`), or null for decimals. */\n private priceFormatter: PriceFormatter | null = null;\n /** A fraction format's smallest step (a 32nd), or null. */\n private priceUnit: number | null = null;\n private paneTitles = true;\n /** When the price scale is fitted to the bars, and to what. */\n private readonly priceFit: PriceFit;\n private keyboardHandler: KeyboardHandler | null = null;\n private a11y: ChartA11y | null = null;\n private onWindowKeyDown: ((e: KeyboardEvent) => void) | null = null;\n private currentSymbol: string = '';\n /** Timeframe and adapter of the connected stream, for paging its history. */\n private streamTimeframe: TimeFrame | null = null;\n /** The stream draws tick bars ('100T'), which are not evenly spaced in time. */\n private tickStream = false;\n /** The symbol and timeframe last told (symbolChange, timeframeChange). */\n private announcedSymbol: string | null = null;\n private announcedTimeframe: TimeFrame | null = null;\n private streamAdapter: DataAdapter | null = null;\n /** Pages older bars in as the view nears the oldest one. */\n private history: HistoryPager;\n /** Whether the history loader is the connected stream's (and goes with it). */\n private historyFromStream = false;\n /** Whether the host set a loader of its own: the stream's never replaces it. */\n private hostHistoryLoader = false;\n /** Symbol and timeframe of the series the last stream snapshot loaded. */\n private snapshotKey: string | null = null;\n private historyCheckQueued = false;\n\n\n constructor(container: HTMLElement, options: ChartOptions & {\n /** Plugins to install with the chart: indicators, drawing tools, chart types and overlays of your own. */\n plugins?: ChartPlugin[];\n }) {\n // Checked before anything is built: a throw later would leave the DOM and listeners behind.\n if (options.timeZone != null) assertTimeZone(options.timeZone);\n this.container = container;\n this.options = { ...options, chartType: options.chartType ?? 'candlestick' };\n this.numberLocale = options.numberLocale ?? 'en-US';\n this.priceFit = new PriceFit(options.autoScale !== false);\n this.display = new DisplaySeries({\n bars: () => this.series.bars(),\n version: () => this.series.version(),\n chartType: () => this.options.chartType,\n chartTypeOptions: () => this.chartTypeOptions,\n lookup: (t) => this.pluginManager.getChartType(t),\n stepAt: (price) => this.stepAt(price),\n onBoxSize: (box) => {\n if (this.chartRenderer instanceof PointAndFigureRenderer) this.chartRenderer.setBoxSize(box);\n },\n });\n\n // Resolve feature flags (all default to true)\n const f = options.features ?? {};\n this.features = {\n drawings: f.drawings ?? true,\n drawingTools: f.drawingTools ?? [],\n drawingMagnet: f.drawingMagnet ?? true,\n drawingUndoRedo: f.drawingUndoRedo ?? true,\n trading: f.trading ?? true,\n // Off by default: right-click is the browser's, and a stray right-click\n // shouldn't be one step from an order. Opt in with features.tradingContextMenu.\n tradingContextMenu: f.tradingContextMenu ?? false,\n priceAxisAddButton: f.priceAxisAddButton ?? false,\n indicators: f.indicators ?? true,\n indicatorIds: f.indicatorIds ?? [],\n panning: f.panning ?? true,\n zooming: f.zooming ?? true,\n crosshair: f.crosshair ?? true,\n crosshairTooltip: f.crosshairTooltip ?? true,\n keyboard: f.keyboard ?? true,\n priceAxis: f.priceAxis ?? true,\n timeAxis: f.timeAxis ?? true,\n grid: f.grid ?? (options.grid?.visible ?? true),\n legend: f.legend ?? true,\n indicatorValueLabels: f.indicatorValueLabels ?? false,\n alignAxisLabels: f.alignAxisLabels ?? true,\n drawingAxisSpans: f.drawingAxisSpans ?? true,\n volume: f.volume ?? true,\n watermark: f.watermark ?? true,\n saveLoad: f.saveLoad ?? true,\n screenshot: f.screenshot ?? true,\n alerts: f.alerts ?? true,\n replay: f.replay ?? true,\n sessionBreaks: f.sessionBreaks ?? true,\n barCountdown: f.barCountdown ?? true,\n compareSymbols: f.compareSymbols ?? true,\n dataExport: f.dataExport ?? true,\n logScale: f.logScale ?? true,\n timeframes: f.timeframes ?? [],\n defaultTimeframeFavorites: f.defaultTimeframeFavorites ?? [],\n };\n\n container.style.position = 'relative';\n container.style.overflow = 'hidden';\n container.tabIndex = 0; // Allow keyboard events\n container.style.outline = 'none';\n // Nothing on the chart is text to select: a double-click would otherwise\n // select its hidden screen-reader text, and the next press inside that\n // selection would start the browser's own drag instead of a pan.\n container.style.userSelect = 'none';\n container.style.webkitUserSelect = 'none';\n\n // Initialize managers\n this.history = new HistoryPager({\n // During a replay the live series pages, not the bars replayed.\n oldestTime: () => (this.replaySession ? this.series.liveBars() : this.series.bars())[0]?.time ?? null,\n prepend: (bars) => this.prependBars(bars),\n emit: (payload) => this.eventBus.emit('historyLoad', payload),\n });\n this.themeManager = new ThemeManager(options.theme);\n this.themeManager.setChartType(this.options.chartType ?? 'candlestick');\n this.applyStyle(startOverrides(options), 'host', false);\n // The canvases are see-through: the theme's background from the start, as setTheme gives it\n // later, so a dark chart on a light page isn't drawn on white. A background the host gave its\n // element stays.\n if (!this.container.style.backgroundColor) {\n this.container.style.backgroundColor = this.themeManager.getTheme().background;\n this.lastBackground = this.themeManager.getTheme().background;\n }\n this.layoutManager = new LayoutManager();\n this.indicatorEngine = new IndicatorEngine();\n this.panes = new PaneLayout(this.layoutManager, this.indicatorEngine, (bounds) => this.crosshairHandler.setReportBounds(bounds));\n this.indicatorPanes = new IndicatorPanes(this.indicatorEngine, this.layoutManager, {\n passStyle: (from, heir) => this.panes.passStyle(from, heir),\n renamePane: (from, to) => this.renamePane(from, to),\n invalidateScales: () => this.panes.invalidateScales(),\n });\n this.symbolRequests = new SymbolRequests(this.indicatorEngine, this.symbolSeries);\n this.pluginManager = new PluginManager(this.indicatorEngine, { plugins: options.plugins });\n this.eventBus = new EventBus();\n\n registerBuiltInIndicators(this.indicatorEngine);\n // Indicators on other symbols' bars, kept by this chart.\n this.indicatorEngine.register(new CompareSymbolIndicator(this.symbolSeries));\n this.indicatorEngine.register(new CompareSymbolPaneIndicator(this.symbolSeries));\n this.indicatorEngine.register(new SpreadIndicator(this.symbolSeries));\n this.indicatorEngine.register(new CorrelationCoefficientIndicator(this.symbolSeries));\n this.indicatorEngine.register(new CorrelationLogIndicator(this.symbolSeries));\n\n // Drawing tools\n this.drawingManager = new DrawingManager();\n registerBuiltInDrawingTools(this.drawingManager);\n this.drawingRenderer = new DrawingRenderer(this.drawingManager);\n this.drawingRenderer.setKeepOut(() => this.legendKeepOut());\n this.drawingRenderer.setTimeFormat((time) => this.crosshairHandler.formatTime(time, this.getDisplayData()));\n this.drawingManager.setRequestRender(() => {\n // Sync entire render context so drawings render with same viewport as candles\n this.syncRenderContext();\n this.engine.requestRender(LayerType.Overlay);\n this.scheduleAutoSave();\n });\n this.drawingManager.setEventCallback((event, data) => {\n this.followDrawingAlerts(event, (data as { id?: string } | null)?.id);\n this.eventBus.emit(event as ChartEventType, data);\n if (event === 'drawingCreate' || event === 'drawingUpdate' || event === 'drawingRemove') this.announceStateChange();\n });\n // Pasted drawings obey the same switches as drawing tools.\n this.drawingManager.setToolFilter((type) =>\n this.features.drawings && (this.features.drawingTools.length === 0 || this.features.drawingTools.includes(type)));\n // Undo/redo\n this.undoRedoManager = new UndoRedoManager();\n this.indicatorHistory = new IndicatorHistory({\n steps: this.undoRedoManager,\n snapshot: () => this.getIndicatorSetup(),\n enabled: () => this.features.drawingUndoRedo,\n });\n let history = '';\n this.undoRedoManager.setOnChange(() => {\n const { canUndo, canRedo } = this.undoRedoManager.getState();\n const now = `${canUndo}|${canRedo}`;\n if (now === history) return;\n history = now;\n this.eventBus.emit('historyChange', { canUndo, canRedo });\n });\n this.drawingManager.setUndoRedoManager(this.undoRedoManager);\n this.drawingManager.setSelectionListener((ids) => this.eventBus.emit('drawingSelect', { ids, primary: ids[0] ?? null }));\n this.drawingManager.setForeignHistory((action, direction) => {\n if (action.type === 'custom') action.custom?.[direction]();\n else this.applyIndicatorStep(action, direction);\n });\n this.drawingManager.setDataGetter(() => this.series.bars(), () => this.series.revision());\n this.drawingManager.setDisplayDataGetter(() => this.getDisplayData());\n // Magnet mode\n if (options.crosshair?.mode === 'magnet' && this.features.drawingMagnet) {\n this.drawingManager.setMagnetMode('magnet');\n }\n\n // Trading\n this.tradingManager = new TradingManager({\n enabled: this.features.trading,\n contextMenu: { enabled: this.features.tradingContextMenu },\n });\n this.tradingRenderer = new TradingRenderer(this.tradingManager);\n this.tradingManager.setContainer(container);\n this.tradingManager.setRequestRender(() => this.engine.requestRender(LayerType.Overlay));\n this.tradingManager.setEventCallback((event, data) => {\n this.eventBus.emit(event as ChartEventType, data);\n });\n\n // Rendering\n this.engine = new RenderEngine(container, options.maxPixelRatio);\n const size = this.engine.dprManager.getContainerSize();\n this.viewport = new Viewport(\n size.width,\n size.height,\n options.minBarSpacing ?? 2,\n options.maxBarSpacing ?? 30,\n options.rightMargin ?? 5,\n );\n if (options.barLayout) this.viewport.setBarLayout(options.barLayout);\n this.viewport.setPanLimits({\n freePan: options.freePan ?? true,\n minVisibleBars: options.panLimits?.minVisibleBars ?? 3,\n });\n if (options.minBarUnit !== undefined) this.viewport.setMinBarUnit(options.minBarUnit);\n this.layoutManager.resize(size.width, size.height);\n\n // Sync viewport + layout when the container resizes. Without this hook\n // the chart's viewport keeps its construction-time chartRect forever\n // (which is wrong if the container started at 0 or got resized since),\n // and `scrollToEnd` / clampOffset compute against stale dimensions —\n // chart sticks at the wrong offset and the user can't scroll to the\n // latest bar.\n this.engine.onContainerResize = (newSize) => {\n if (newSize.width <= 0 || newSize.height <= 0) return;\n const wasAtEnd = this.viewport.isAtEnd();\n this.viewport.resize(newSize.width, newSize.height);\n this.layoutManager.resize(newSize.width, newSize.height);\n // wasAtEnd carries the user's intent: if they were tracking the live\n // edge before the resize, keep them there afterwards. Otherwise leave\n // their scroll position alone.\n this.updateViewportAndRender(wasAtEnd);\n // A window resize, a phone rotation or a panel beside the chart changes\n // the container without a call to resize(); what lays itself out over\n // the chart (the widget's replay bar, its toasts) waits for this event.\n this.eventBus.emit('resize', newSize);\n };\n\n // Chart renderer\n this.chartRenderer = this.createChartRenderer(this.options.chartType);\n this.gridRenderer = new GridRenderer();\n if (options.grid?.visible === false) this.gridRenderer.setVisible(false);\n this.priceAxis = new PriceAxis();\n this.priceAxis.setLocale(this.numberLocale);\n this.leftPriceAxis.setLocale(this.numberLocale);\n // Tags beside the axis (orders, positions, alerts) group prices as it does.\n this.viewport.setNumberLocale(this.numberLocale);\n this.timeAxis = new TimeAxis();\n this.timeAxis.setLocale(this.numberLocale);\n\n // Crosshair\n this.crosshairHandler = new CrosshairHandler();\n this.crosshairHandler.setLocale(this.numberLocale);\n // A Renko-like chart type from the options has no times past its bricks.\n this.applyTimeSpacing();\n if (options.crosshair?.mode) {\n this.crosshairHandler.setMode(options.crosshair.mode);\n }\n if (isCrosshairCursor(options.crosshair?.cursor)) this.crosshairHandler.setCursor(options.crosshair.cursor);\n // Crosshair callback — fired via microtask AFTER render, only when the bar\n // changes: no extra render requests, and the tooltip's one measurement\n // happens once per bar, not per mouse move.\n this.crosshairHandler.setDataSource(() => this.crosshairSeries());\n this.crosshairHandler.setCallback((barIndex, point) => {\n if (barIndex !== null && point) {\n const data = this.crosshairSeries();\n const bar = barIndex >= 0 && barIndex < data.length ? data[barIndex] : undefined;\n const prevClose = barIndex > 0 ? data[barIndex - 1]?.close : undefined;\n // A brick's own time only counts bricks: the tooltip and listeners get the date the axis gives it.\n const times = axisTimes(data);\n const time = bar ? times[barIndex]?.time ?? bar.time : undefined;\n\n // The legend is drawn in the frame that found this bar, before it knew it: one more\n // paint of the pointer's layer shows it, or a pointer that comes to rest leaves the\n // legend on the bar before while the data window and the indicator rows show this one.\n this.chartLegend.setHoverBar(bar ?? null, barIndex > 0 ? data[barIndex - 1] ?? null : null);\n if (this.features.legend) this.engine.requestRender(LayerType.Hover);\n\n // Update tooltip (DOM, lightweight update only when bar changes).\n // Over an indicator pane there is no price crosshair to sit beside.\n const vs = this.viewport.getState();\n const inPlot = point.y >= vs.chartRect.y && point.y <= vs.chartRect.y + vs.chartRect.height;\n if (bar && inPlot && this.features.crosshairTooltip) {\n const dated = time === bar.time ? bar : { ...bar, time: time ?? bar.time };\n this.crosshairTooltip.show(point, dated, this.themeManager.getTheme(), this.cachedContainerSize(), {\n prevClose,\n priceRange: vs.priceRange,\n plot: vs.chartRect,\n barStepMs: barTimeStepMs(times),\n timeUnit: barTimeUnit(times),\n });\n } else {\n this.crosshairTooltip.hide();\n }\n\n // Refresh the pinned tooltip's delta strip against the hovered bar.\n // Cheap when there's nothing pinned (early returns inside reposition).\n if (this.pinnedTooltip.isPinned()) {\n this.pinnedTooltip.reposition(\n this.viewport.getState(),\n bar ?? null,\n barIndex,\n this.themeManager.getTheme(),\n );\n }\n\n // Emit for external consumers\n this.eventBus.emit('crosshairMove', { point, bar, barIndex, ...(time !== undefined ? { time } : {}), ...(prevClose !== undefined ? { prevClose } : {}) });\n } else {\n this.crosshairTooltip.hide();\n this.chartLegend.setHoverBar(null);\n if (this.features.legend) this.engine.requestRender(LayerType.Hover);\n this.eventBus.emit('crosshairLeave', {});\n // Another chart's crosshair still mirrored here: back to its bar, not the latest.\n const synced = this.crosshairHandler.getSyncedSlot();\n if (synced !== null) this.showSyncedBar(synced);\n }\n });\n\n // Chart legend (OHLCV overlay)\n this.chartLegend = new ChartLegend();\n this.chartLegend.setChartType(this.options.chartType);\n this.chartLegend.setLocale(this.numberLocale);\n // Drawn taller or shorter: what a host stacks under it (the widget's indicator rows) moves,\n // and the drawings' labels are placed again around it.\n this.chartLegend.setResizeListener(() => {\n this.eventBus.emit('legendResize', { bottom: this.getLegendBottom() });\n this.engine.requestRender();\n });\n\n // Watermark + Volume\n this.watermark = new Watermark();\n if (options.watermark) this.watermark.setConfig(options.watermark);\n this.volumeRenderer = new VolumeRenderer();\n this.volumeProfile = new VolumeProfileRenderer();\n this.marketProfile = new MarketProfileRenderer();\n this.depthHeatmap = new DepthHeatmapRenderer();\n this.periodLevels = new PeriodLevelsRenderer();\n this.pivotMarkers = new PivotMarkersRenderer();\n\n // Bar countdown timer\n this.barCountdown = new BarCountdown();\n this.barCountdown.setVisible(this.features.barCountdown);\n this.sessionBreaks = new SessionBreaks();\n this.sessionShading = new SessionShading();\n this.compareRenderer = new CompareRenderer();\n\n // Apply session break config from options\n if (options.sessionBreaks) {\n this.sessionBreaks.setConfig({\n visible: options.sessionBreaks.visible ?? true,\n color: options.sessionBreaks.color,\n lineStyle: options.sessionBreaks.lineStyle,\n lineWidth: options.sessionBreaks.lineWidth,\n });\n } else if (options.features?.sessionBreaks !== false) {\n this.sessionBreaks.setVisible(true);\n }\n\n // Apply log scale from options\n if (options.logScale && this.features.logScale) {\n this.viewport.setLogScale(true);\n }\n\n // Animation\n this.animator = new Animator();\n\n // Crosshair tooltip (DOM)\n this.crosshairTooltip = new CrosshairTooltip();\n this.crosshairTooltip.create(container);\n this.crosshairTooltip.setLocale(this.numberLocale);\n this.pinnedTooltip = new PinnedTooltip();\n this.pinnedTooltip.create(container);\n this.pinnedTooltip.setLocale(this.numberLocale);\n if (options.priceFormat) this.applyPriceFormat(options.priceFormat);\n if (options.chartTypeOptions) this.chartTypeOptions = readChartTypeOptions(options.chartTypeOptions);\n if (options.highLowLines) this.priceLines.setHighLow(true);\n if (options.shapes) this.themeManager.setShape(readShapes(options.shapes));\n if (options.font) this.themeManager.setFont(readFont(options.font));\n // Their tags in the chart's precision, locale and format.\n this.priceLines.setPriceText((p) => this.formatPrice(p));\n if (options.extendedHours === false) this.extendedHours = false;\n if (options.timeFormatter) this.applyTimeFormatter(options.timeFormatter);\n\n // Keyboard navigation\n this.keyboardHandler = new KeyboardHandler({\n scrollBars: (count) => {\n const barUnit = this.viewport.getState().barWidth + this.viewport.getState().barSpacing;\n this.viewport.scrollBy(count * barUnit);\n this.updateViewportAndRender();\n },\n zoom: (delta) => {\n const chartWidth = this.viewport.getState().chartRect.width;\n this.viewport.zoom(delta, chartWidth / 2);\n this.updateViewportAndRender();\n },\n goToStart: () => {\n this.viewport.scrollBy(-Infinity);\n this.updateViewportAndRender();\n },\n goToEnd: () => {\n this.viewport.scrollToEnd();\n this.updateViewportAndRender();\n },\n fitContent: () => this.fitContent(),\n });\n this.keyboardHandler.setEnabled(this.features.keyboard);\n\n // Screen readers: a summary, the view after a key moves it, the bars one by one.\n if (options.a11y !== false) {\n this.a11y = new ChartA11y(container, {\n symbol: () => this.currentSymbol || this.symbolInfo?.symbol || '',\n timeframe: () => this.streamTimeframe ?? '',\n chartType: () => this.options.chartType ?? 'candlestick',\n bars: () => this.getDisplayData(),\n visibleRange: () => this.viewport.getState().visibleRange,\n formatPrice: (price) => this.formatPrice(price),\n formatTime: (time) => this.spokenBarTime(time),\n showBar: (time) => this.setCrosshairTime(time),\n }, options.a11y?.labels);\n for (const event of ['dataUpdate', 'chartTypeChange', 'symbolChange', 'timeframeChange'] as const) {\n this.eventBus.on(event, () => this.a11y?.refresh(event !== 'dataUpdate'));\n }\n }\n\n // Attach keyboard listener to window. Only consume the event when the\n // chart actually handles the key AND the container is the focused element\n // (or an ancestor) — this prevents the chart from swallowing keystrokes\n // intended for inputs elsewhere on the page.\n this.onWindowKeyDown = (e: KeyboardEvent) => {\n if (!this.keyboardHandler) return;\n const active = document.activeElement as HTMLElement | null;\n if (active && active !== this.container && !this.container.contains(active)) {\n return;\n }\n // Ignore when typing in a form field that happens to live inside the chart.\n if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.isContentEditable)) {\n return;\n }\n // A control inside the chart (a button over it) takes its own keys: Space presses it.\n if (active && active !== this.container && active.matches('button, a[href], select, [role=\"button\"], [role=\"menuitem\"]')) return;\n // Comma and period read the bars one at a time.\n if (this.a11y && this.features.keyboard && (e.key === ',' || e.key === '.') && !e.ctrlKey && !e.metaKey && !e.altKey) {\n e.preventDefault();\n this.a11y.readBar(e.key === '.' ? 1 : -1);\n return;\n }\n // With drawings selected, the arrows move them instead of the chart.\n if (this.nudgeSelectedDrawings(e)) {\n e.preventDefault();\n return;\n }\n if (this.keyboardHandler.handleKey(e)) {\n e.preventDefault();\n this.a11y?.viewMoved();\n }\n };\n window.addEventListener('keydown', this.onWindowKeyDown);\n\n // Current price line (standalone, works without StreamManager)\n this.currentPriceLine = new CurrentPriceLine();\n this.currentPriceLine.setLocale(this.numberLocale);\n this.priceAxis.setReservedPriceProvider(() =>\n this.currentPriceLine.isVisible() ? this.currentPriceLine.getPrice() : null);\n\n // Alerts\n this.alertManager = new AlertManager();\n // Alerts on drawings check the drawing's lines where they are at the latest bar.\n this.alertManager.setDrawingLevels((id) => this.drawingLevelsNow(id));\n this.alertManager.setRequestRender(() => this.engine.requestRender(LayerType.Overlay));\n this.drawingAlerts = new DrawingAlertStash(this.alertManager);\n // A compare's label at its line's end keeps clear of an alert's label at the plot's right edge.\n this.compareRenderer.setReservedLevels(() => this.alertManager.getAlerts().filter(isPriceLevelAlert).map((a) => a.price));\n this.alertManager.on('triggered', (alert) => {\n this.eventBus.emit('alertTriggered', alertPayload(alert));\n // Back-compat: legacy listeners keyed off dataUpdate.\n this.eventBus.emit('dataUpdate', { kind: 'alert', alert: 'triggered', alertId: alert.id, price: alert.price, message: alert.message } satisfies DataUpdatePayload);\n });\n this.alertManager.on('added', (alert) => {\n this.eventBus.emit('alertAdd', alertPayload(alert));\n });\n this.alertManager.on('removed', (id) => {\n this.eventBus.emit('alertRemove', { id });\n });\n this.alertManager.on('expired', (alert) => {\n this.eventBus.emit('alertExpired', alertPayload(alert));\n });\n this.alertManager.on('updated', (alert) => {\n this.eventBus.emit('alertUpdate', alertPayload(alert));\n });\n\n // Signal markers\n this.signalMarkerManager = new SignalMarkerManager();\n this.signalMarkerManager.setRequestRender(() => this.engine.requestRender(LayerType.Overlay));\n this.signalMarkerManager.setDataGetter(() => this.series.bars());\n this.signalMarkerManager.on('added', (m) => {\n this.eventBus.emit('signalMarkerAdd' as ChartEventType, { id: m.id, source: m.source, direction: m.direction });\n });\n this.signalMarkerManager.on('removed', (id) => {\n this.eventBus.emit('signalMarkerRemove' as ChartEventType, { id });\n });\n\n // Trade zones\n this.tradeZoneManager = new TradeZoneManager();\n this.tradeZoneManager.setRequestRender(() => this.engine.requestRender(LayerType.Overlay));\n this.tradeZoneManager.setDataGetter(() => this.series.bars());\n this.tradeZoneManager.on('added', (z) => {\n this.eventBus.emit('tradeZoneAdd' as ChartEventType, { id: z.id, direction: z.direction });\n });\n this.tradeZoneManager.on('removed', (id) => {\n this.eventBus.emit('tradeZoneRemove' as ChartEventType, { id });\n });\n\n // Replay\n this.replayManager = new ReplayManager();\n this.replayManager.on('complete', () => {\n const barIndex = this.getReplayBarIndex();\n const bar = this.replayCoarse?.[barIndex];\n if (bar) this.eventBus.emit('replayComplete', { barIndex, time: bar.time });\n });\n this.replayManager.on('stateChange', (state) => {\n if (!this.replayRestarting) this.eventBus.emit('replayState', { state });\n });\n\n // Measure overlay (shift-drag ruler)\n this.measureOverlay = new MeasureOverlay();\n\n // Interaction\n this.interactionManager = new InteractionManager(container);\n if (this.features.panning) {\n // Decides per gesture when a drag also moves the price scale.\n const verticalGate = new VerticalPanGate();\n this.interactionManager.setPanHandler(\n new PanHandler(\n (deltaX, deltaY) => {\n if (deltaX) this.viewport.scrollBy(deltaX);\n\n // Grab-and-drag the price scale vertically. Auto-scale would just\n // recompute the range on the next frame, so engaging the drag\n // turns it off (double-click the price axis to restore), mirroring\n // the price-axis drag-scale gesture.\n if (verticalGate.step(deltaX, deltaY, this.priceFit.isAuto()) && deltaY) {\n this.holdPriceScale();\n this.viewport.panPriceRange(deltaY);\n }\n\n this.updateViewportAndRender();\n },\n () => verticalGate.reset(),\n ),\n );\n }\n // The time axis sits below the panes; the left scale's strip carries\n // overlays' labels only, or mirrors the price scale and scales it.\n this.interactionManager.setAxisStrips(() => ({\n plot: this.viewport.getState().chartRect,\n timeAxisTop: this.panes.timeAxisTop(),\n left: {\n width: this.isLeftPriceScaleShown() ? this.leftAxisWidth : 0,\n scales: !this.indicatorEngine.hasLeftScaleOverlays(),\n },\n }));\n if (this.features.zooming) {\n this.interactionManager.setZoomHandler(\n new ZoomHandler((delta, centerX) => {\n // The pointer is in chart coordinates; zoom anchors in plot ones.\n this.viewport.zoom(delta, centerX - this.viewport.getState().chartRect.x);\n this.updateViewportAndRender();\n }),\n );\n\n // Axis drag-scaling:\n // drag price axis → scalePriceRange(factor), disables autoScale\n // drag time axis → zoom around chart center\n // dblclick axis → reset (price: re-enable autoScale; time: fitContent)\n const axisDrag = new AxisDragHandler(\n (factor) => {\n this.holdPriceScale();\n this.viewport.scalePriceRange(factor);\n this.syncRenderContext();\n this.engine.requestRender();\n this.emitViewportEvents();\n },\n (factor) => {\n const cw = this.viewport.getState().chartRect.width;\n // factor > 1 means zoom OUT (compress time). Map to negative delta\n // because Viewport.zoom uses (1 + delta) on barWidth — positive\n // delta zooms in.\n this.viewport.zoom(1 / factor - 1, cw / 2);\n this.updateViewportAndRender();\n },\n );\n this.interactionManager.setAxisDragHandler(\n axisDrag,\n () => this.viewport.getState(),\n (axis) => {\n if (axis === 'price') {\n this.priceFit.resume();\n this.updateViewportAndRender();\n } else {\n this.fitContent();\n }\n },\n );\n }\n if (this.features.crosshair) {\n this.interactionManager.setCrosshairHandler(this.crosshairHandler);\n this.applyIdleCursor();\n }\n if (this.features.drawings) {\n this.interactionManager.setDrawingManager(\n this.drawingManager,\n // Inject current data so drawing anchors are stored/resolved as real\n // timestamps (survives timeframe/symbol switches).\n () => ({ ...this.viewport.getState(), data: this.getDisplayData() }),\n );\n // Drawings begun on an indicator pane are made and drawn on its scale.\n this.drawingManager.setPanes({\n paneAt: (pos) => this.drawingPaneAt(pos),\n viewportOf: (id) => this.drawingPaneViewport(id),\n hasPane: (id) => this.layoutManager.getPanels().some((panel) => panel.id === id),\n valuesAt: (id, barIndex) => this.indicatorEngine.getPaneValuesAt(id, barIndex),\n });\n this.interactionManager.setDrawingDoubleClick((id) => this.eventBus.emit('drawingDoubleClick', { id }));\n this.interactionManager.setDrawingContextMenu((id, pos) => {\n if (this.drawingManager.select(id)) this.eventBus.emit('drawingContextMenu', { id, x: pos.x, y: pos.y });\n });\n }\n // Off any drawing: the host's menu for where it was, when one listens.\n this.interactionManager.setChartContextMenu((area, pos) => {\n if (!this.eventBus.hasListeners('chartContextMenu')) return false;\n this.eventBus.emit('chartContextMenu', this.contextAt(area, pos));\n return true;\n });\n this.applyPriceAxisAdd();\n this.interactionManager.setPriceAxisAddButton(this.priceAxisAddButton, (pos) => {\n const price = yToPrice(pos.y, this.viewport.getState());\n if (Number.isFinite(price)) this.eventBus.emit('priceAxisAdd', { price, x: pos.x, y: pos.y });\n });\n if (this.features.trading) {\n this.interactionManager.setTradingManager(\n this.tradingManager,\n () => ({ ...this.viewport.getState(), data: this.getDisplayData() }),\n );\n }\n if (this.features.alerts) {\n const alertDrag = new AlertDragHandler(this.alertManager, () => this.viewport.getState());\n this.interactionManager.setAlertDragHandler(alertDrag);\n }\n\n // Drag pane dividers to resize indicator panels.\n const paneResize = new PaneResizeHandler();\n paneResize.configure(\n () => this.getResolvedLayout(),\n (panelId, size) => this.setPanelSize(panelId, size),\n );\n this.interactionManager.setPaneResizeHandler(paneResize);\n // Alt-click pins the OHLC tooltip at the bar under the cursor. Hitting\n // it a second time on the same bar unpins.\n this.interactionManager.setAltClickHandler((pos) => {\n const data = this.getDisplayData();\n if (data.length === 0) return;\n const vp = this.viewport.getState();\n const barUnit = vp.barWidth + vp.barSpacing;\n const idx = Math.round((vp.offset + pos.x) / barUnit);\n if (idx < 0 || idx >= data.length) return;\n if (this.pinnedTooltip.isPinned() && this.pinnedTooltip.getPinnedIndex() === idx) {\n this.pinnedTooltip.unpin();\n } else {\n this.pinnedTooltip.pin(data[idx], idx, this.themeManager.getTheme(), barTimeUnit(data), barsAreDaily(data) === true);\n }\n this.pinnedTooltip.reposition(\n this.viewport.getState(),\n this.series.bars()[idx] ?? null,\n idx,\n this.themeManager.getTheme(),\n );\n this.engine.requestRender(LayerType.Overlay);\n });\n\n // Signal markers react to the pointer, when someone listens.\n this.interactionManager.setSignalMarkerHitTest(\n (pos) => (this.eventBus.hasListeners('signalMarkerHover') || this.eventBus.hasListeners('signalMarkerClick')\n ? this.signalMarkerManager.markerAt(pos, this.viewport.getState())\n : null),\n (marker, pos) => this.eventBus.emit('signalMarkerHover', { marker: marker as import('@tradecanvas/commons').SignalMarker | null, x: pos.x, y: pos.y }),\n // A hand only where a click does something.\n () => this.eventBus.hasListeners('signalMarkerClick'),\n );\n this.interactionManager.setClickHandler((pos) => {\n const data = this.getDisplayData();\n const marker = this.eventBus.hasListeners('signalMarkerClick') ? this.signalMarkerManager.markerAt(pos, this.viewport.getState()) : null;\n if (marker) {\n this.eventBus.emit('signalMarkerClick', { marker, x: pos.x, y: pos.y });\n return;\n }\n this.eventBus.emit('click', { x: pos.x, y: pos.y });\n if (data.length === 0) return;\n // Canonical bar mapping (accounts for chartRect.x + bar centering) so the\n // clicked bar matches the crosshair / drawings.\n const idx = xToBarIndex(pos.x, this.viewport.getState());\n if (idx < 0 || idx >= data.length) return;\n this.eventBus.emit('barClick', { bar: data[idx], barIndex: idx, point: pos });\n });\n\n this.interactionManager.setEscapeHandler(() => {\n if (this.zoomAreaMode) {\n this.setZoomAreaMode(false);\n return;\n }\n if (this.features.trading && this.tradingManager.isBracketActive()) {\n this.tradingManager.cancelBracket();\n this.eventBus.emit('dataUpdate', { kind: 'bracket', bracket: 'cancelled' } satisfies DataUpdatePayload);\n return;\n }\n if (this.pinnedTooltip.isPinned()) {\n this.pinnedTooltip.unpin();\n this.engine.requestRender(LayerType.Overlay);\n }\n });\n\n this.interactionManager.setConfirmHandler(() => {\n if (this.features.trading && this.tradingManager.isBracketActive()) {\n this.tradingManager.confirmBracket();\n return true;\n }\n return false;\n });\n\n // Shift-drag measure tool — transient overlay. Coords resolved via\n // viewport + displayData so the ruler sticks to the data during zoom.\n this.interactionManager.setMeasureHandlers({\n begin: (pos) => {\n this.measureOverlay.begin(pos, this.viewport.getState(), this.getDisplayData());\n },\n move: (pos) => {\n this.measureOverlay.update(pos, this.viewport.getState(), this.getDisplayData());\n },\n end: () => {\n this.measureOverlay.end();\n },\n });\n\n // Ctrl/⌘-drag: select every drawing the box covers; Ctrl/⌘-click adds or\n // removes one. The box is kept inside the plot it began in, the price\n // pane's or an indicator pane's (moves are followed off-chart), so the\n // selection matches what the box showed.\n let boxPane: string | null | undefined;\n const inPlot = (pos: { x: number; y: number }) => {\n const pane = boxPane ? this.drawingPaneViewport(boxPane) : null;\n const r = pane ? pane.chartRect : this.viewport.getState().chartRect;\n return {\n x: Math.max(r.x, Math.min(r.x + r.width, pos.x)),\n y: Math.max(r.y, Math.min(r.y + r.height, pos.y)),\n };\n };\n this.interactionManager.setBoxSelectHandlers({\n begin: (pos) => {\n // Zooming to an area is the price pane's; a selection, the pane pressed.\n boxPane = this.zoomAreaMode ? undefined : this.drawingPaneAt(pos);\n this.selectionBoxOverlay.begin(inPlot(pos));\n },\n move: (pos) => this.selectionBoxOverlay.update(inPlot(pos)),\n end: () => {\n const box = this.selectionBoxOverlay.end();\n if (box && this.zoomAreaMode) {\n // A click, or a box too narrow to hold a bar or two, keeps the tool waiting.\n if (box.x1 - box.x0 < MIN_ZOOM_BOX_PX) return;\n this.zoomToBox(box);\n this.setZoomAreaMode(false);\n return;\n }\n if (!box || !this.features.drawings) return;\n // With the bar series, so anchors resolve as timestamps (as for drawing hit-tests).\n const vs = { ...this.viewport.getState(), data: this.getDisplayData() };\n if (boxPane === null) return;\n if (box.isClick) this.drawingManager.toggleSelectionAt({ x: box.x1, y: box.y1 }, vs);\n else this.drawingManager.selectInRect(box, vs, boxPane);\n this.engine.requestRender(LayerType.Overlay);\n },\n cancel: () => {\n this.selectionBoxOverlay.cancel();\n this.setZoomAreaMode(false);\n },\n });\n\n // A plain hover only moves pointer-tied visuals (crosshair, its axis\n // pills, measure ruler, selection box): repaint just the top canvas.\n this.interactionManager.setOverlayDirtyCallback((hoverOnly) => {\n this.engine.requestRender(hoverOnly ? LayerType.Hover : LayerType.Overlay);\n });\n this.interactionManager.setPageScroll(options.pageScroll ?? false);\n this.interactionManager.attach();\n\n if (options.timeZone !== undefined && options.timeZone !== null) this.setTimeZone(options.timeZone);\n if (options.leftPriceScale) this.leftScaleRequested = true;\n\n // Set render context\n this.syncRenderContext();\n this.engine.start();\n this.watchVisibility(container, options.pauseWhenHidden !== false);\n // A lost GPU context: the engine is back on Canvas 2D, until it comes back.\n this.engine.onGpuLost = () => this.eventBus.emit('rendererChange', { renderer: 'canvas', reason: 'contextLost' });\n this.engine.onGpuRestored = () => this.eventBus.emit('rendererChange', { renderer: 'webgl', reason: 'contextRestored' });\n if (this.options.renderer && this.options.renderer !== 'canvas') void this.setRenderer(this.options.renderer);\n }\n\n // --- Data ---\n\n setData(data: DataSeries): void {\n this.loadSeries(data, false);\n }\n\n /**\n * `setData`; `restoring` puts the live series back after a replay, which\n * goes on as it was for alerts (their bars and last values stay).\n */\n private loadSeries(data: DataSeries, restoring: boolean): void {\n // A replay goes on over the new series, at the same time; one a paper\n // account trades ends when the symbol changes (its orders are of the old one).\n const session = this.replaySession;\n if (session && !restoring && data.length > 0 && !this.replayEndsFor(this.currentSymbol)) {\n this.loadDuringReplay(data);\n return;\n }\n // A full replace is a new series: a stream's reconnect no longer merges into it.\n this.snapshotKey = null;\n this.endReplaySession();\n this.history.reset();\n this.series.load(data);\n this.crosshairHandler.setData(this.series.bars());\n this.display.reset();\n this.sessionBreaks.invalidateCache();\n // A full data replace means a new series (symbol/timeframe switch, not\n // a live tick — those go through appendBar/updateLastBar instead), so\n // un-freeze the Y-axis the same way scrollToEnd below resets the X-axis.\n // Otherwise a price-axis drag or vertical pan on the old symbol silently\n // keeps the new symbol's chart stuck on the old price range. Auto-scale\n // set off fits the new series once too, then holds it.\n this.priceFit.newSeries(this.series.length() === 0);\n // New data context (symbol / timeframe) — drop stale alert prev-values so\n // the next tick seeds cleanly instead of crossing against the old series.\n // The bars kept: a bar the chart can't take is left out.\n const kept = this.series.bars();\n const lastKept = kept[kept.length - 1];\n if (!restoring) {\n this.alertManager.clearLastValues();\n // The first live price after the bars crosses a level from their last close.\n if (lastKept) this.alertManager.seedLastValue('price', lastKept.close);\n }\n this.recalcIndicators(kept);\n // Auto-set current price line from last bar's close\n if (lastKept) this.currentPriceLine.setPrice(lastKept.close);\n this.updateViewportAndRender(true);\n this.fitFewColumns();\n this.emitBars(kept.length);\n }\n\n /**\n * A new series during a replay: the live series kept aside, which the\n * replay carries over to (the store tells it). Alerts start again from\n * its last close, as they follow the live market.\n */\n private loadDuringReplay(data: DataSeries): void {\n this.snapshotKey = null;\n this.history.reset();\n this.alertManager.clearLastValues();\n const last = data[data.length - 1];\n if (last && isValidBar(last)) this.alertManager.seedLastValue('price', last.close);\n if (this.replaySession) this.replaySession.price = null; // the old series' live price\n this.series.load(data);\n }\n\n /**\n * Add older bars in front of the data — e.g. a page of history loaded as\n * the user scrolls back. Bars at or after the first loaded bar are skipped.\n * The same bars stay on screen. During a replay they join the live series\n * and the replay, at the same time. Returns how many bars were added.\n */\n prependBars(bars: OHLCBar[]): number {\n if (this.replaySession) return this.series.prepend(bars).taken;\n // The bar at the left edge, by time: Renko bricks and the like are rebuilt\n // from the whole series, so counting what was added would not find it.\n const shown = this.getDisplayData();\n const leftIndex = Math.max(0, Math.min(shown.length - 1, Math.floor(this.viewport.getState().visibleRange.from)));\n const leftTime = shown[leftIndex]?.time;\n // Hidden extended hours: the older bars all go to the whole series, the\n // regular ones on the chart too; the page counts as loaded either way.\n const { added, taken } = this.series.prepend(bars);\n if (added === 0) return taken;\n const data = this.series.bars();\n this.crosshairHandler.setData(data);\n this.display.reset();\n this.sessionBreaks.invalidateCache();\n // Both hold bar positions that just moved.\n if (this.pinnedTooltip.isPinned()) this.pinnedTooltip.unpin();\n this.measureOverlay.end();\n this.recalcIndicators(data);\n if (leftTime !== undefined) {\n const now = Math.round(timestampToBarIndex(leftTime, this.getDisplayData()));\n this.viewport.prependBars(now - leftIndex);\n }\n this.updateViewportAndRender();\n return taken;\n }\n\n /**\n * A stream snapshot for the series already on the chart (a reconnect): keep\n * the older bars paged in and the view, replace the rest.\n */\n private mergeSnapshot(bars: OHLCBar[]): void {\n // The viewport learns the new length when it is fitted, below.\n const follow = this.viewport.isAtEnd();\n if (!this.series.mergeSnapshot(bars)) {\n this.setData(bars);\n return;\n }\n const merged = this.series.bars();\n this.crosshairHandler.setData(merged);\n this.display.reset();\n this.sessionBreaks.invalidateCache();\n // The same series goes on (a reconnect): alerts keep their bars and last values.\n this.recalcIndicators(merged);\n if (merged.length > 0) this.currentPriceLine.setPrice(merged[merged.length - 1].close);\n this.updateViewportAndRender(follow);\n this.emitBars(merged.length);\n }\n\n /**\n * Load older bars as the user scrolls back: `loader(before, limit)` gets the\n * oldest loaded bar's time and returns up to `limit` older bars (an empty\n * array once the history starts). A connected stream whose adapter has\n * `fetchHistoryBefore` sets this up by itself. Pass null to stop.\n */\n setHistoryLoader(loader: HistoryLoader | null, options?: { pageSize?: number }): void {\n this.historyFromStream = false;\n this.hostHistoryLoader = loader !== null;\n this.history.setLoader(loader, options?.pageSize);\n this.checkHistory();\n }\n\n /** Load one page of older bars now. Resolves to the number of bars added (while a page is on its way, that page's). */\n loadMoreHistory(): Promise<number> {\n if (!this.replayPages()) return Promise.resolve(0);\n return this.history.loadMore();\n }\n\n /** Whether older bars can still be loaded (a loader is set and the start wasn't reached). */\n hasMoreHistory(): boolean {\n return this.history.hasLoader() && this.history.hasMore();\n }\n\n isLoadingHistory(): boolean {\n return this.history.isLoading();\n }\n\n /**\n * Page older bars in when less than a screen of them is left of the view —\n * after the current update, so a page that just landed reports itself\n * before the next one starts. Not for chart types that reshape the bars\n * (Renko, Kagi…): a handful of bricks can stand for a whole page.\n */\n private checkHistory(): void {\n if (this.historyCheckQueued || !this.history.hasLoader()) return;\n this.historyCheckQueued = true;\n queueMicrotask(() => {\n this.historyCheckQueued = false;\n if (!this.replayPages() || !this.history.hasLoader() || isReshapedChartType(this.options.chartType)) return;\n const { visibleRange, chartRect } = this.viewport.getState();\n // No size (hidden: a grid's tab not shown, a collapsed panel), no view to\n // fill: a chart 0 wide would page back through the whole history. The\n // check comes again when it gets one.\n if (chartRect.width <= 0) return;\n const { from, to } = visibleRange;\n this.history.maybeLoad(Math.max(0, Math.floor(from)), Math.max(HISTORY_AHEAD_MIN_BARS, to - from));\n });\n }\n\n /** Page the connected stream's history through its adapter, if it can. */\n private useStreamHistory(feed: DataAdapter, pageSize: number | undefined, check = true): void {\n if (this.hostHistoryLoader) return;\n // The same wrapper the stream uses: pages of a 7m chart are 7m bars.\n const adapter = withResampling(feed);\n const fetchBefore = adapter.fetchHistoryBefore;\n const timeframe = this.streamTimeframe;\n // Tick bars come from recent trades: a feed keeps no older ones to page in.\n if (!fetchBefore || timeframe === null || tickBarCount(timeframe) !== null) {\n if (this.historyFromStream) this.history.setLoader(null);\n this.historyFromStream = false;\n return;\n }\n const symbol = this.currentSymbol;\n this.history.setLoader((before, limit) => fetchBefore.call(adapter, symbol, timeframe, before, limit), pageSize);\n this.historyFromStream = true;\n // Not before the new bars are in: a page would go in front of the old ones.\n if (check) this.checkHistory();\n }\n\n /**\n * A new bar after the last, from your own feed. Its close is the live\n * price: the price line, the orders' P&L and the alerts follow it, as\n * they do `setCurrentPrice` and a connected stream. Sends `dataUpdate`\n * (`{ kind: 'bars', length }`), before the alerts it sets off. A bar the\n * chart can't take (a price that isn't a number, below zero, a high under\n * its low) is left out, price and all.\n */\n appendBar(bar: OHLCBar): void {\n if (!isValidBar(bar)) return;\n const write = this.series.append(bar);\n if (write.to !== 'shown') {\n this.takeLivePrice(bar.close);\n if (write.to === 'hidden') this.scheduleRender();\n return;\n }\n // Follow the live edge only if the view is already there — browsing\n // history shouldn't be yanked back to the end on every new bar.\n const follow = this.autoScrollOnNewBar && this.viewport.isAtEnd();\n const before = write.before;\n const data = this.series.bars();\n this.crosshairHandler.setData(data);\n // Re-finalise the bar that just closed (its last tick may differ from the\n // final close) and compute the new one; everything older is untouched.\n this.recalcIndicatorsFrom(data, data.length - 2);\n // Measured from the close before it, as a stream measures a new bar: one opening there is level.\n this.currentPriceLine.setPrice(bar.close, data[data.length - 2]?.close);\n this.updateViewportAndRender(follow);\n if (data.length !== before) this.emitBars(data.length);\n // Last: an alert's listener sees the chart with its new bar.\n this.followLivePrice(bar.close);\n }\n\n /**\n * Several new bars at once (a catch-up after a reconnect), the indicators\n * computed once. The last bar's close is the live price, as with\n * `appendBar`; one `dataUpdate` for them all.\n */\n appendBars(bars: OHLCBar[]): void {\n bars = bars.filter(isValidBar);\n if (bars.length === 0) return;\n const price = bars[bars.length - 1].close;\n const write = this.series.appendMany(bars);\n if (write.shown === 0) {\n this.takeLivePrice(price);\n if (!write.aside) this.scheduleRender();\n return;\n }\n // As `appendBar`: the live edge only while the view is there and auto-scroll is on.\n const follow = this.autoScrollOnNewBar && this.viewport.isAtEnd();\n const before = write.before;\n const data = this.series.bars();\n this.recalcIndicatorsFrom(data, before - 1);\n this.crosshairHandler.setData(data);\n this.currentPriceLine.setPrice(price, data[data.length - 2]?.close);\n this.updateViewportAndRender(follow);\n if (data.length !== before) this.emitBars(data.length);\n this.followLivePrice(price);\n }\n\n /**\n * The forming bar again, with its latest values (same `time` as the last\n * bar). Its close is the live price: the price line, the orders' P&L and\n * the alerts follow it. A tick sends no `dataUpdate`; `indicatorUpdate`\n * says the indicators moved. A bar the chart can't take is left out.\n */\n updateLastBar(bar: OHLCBar): void {\n if (!isValidBar(bar)) return;\n const write = this.series.updateLast(bar);\n if (write.to !== 'shown') {\n this.takeLivePrice(bar.close);\n if (write.to === 'hidden') this.scheduleRender();\n return;\n }\n this.currentPriceLine.setPrice(bar.close);\n // Keep indicator lines in sync with the forming bar. Without this, panel\n // + overlay indicators freeze until bar close. Only the last bar changed.\n const data = this.series.bars();\n this.recalcIndicatorsFrom(data, data.length - 1);\n // After the indicators: an alert on a line compares with its value at this bar.\n this.followLivePrice(bar.close);\n this.scheduleRender();\n }\n\n /** A trade's price into the forming bar (its high, low, close and volume): the live price, as with `updateLastBar`. */\n updateLastBarFromTick(tick: { price: number; volume?: number; time: number }): void {\n if (!Number.isFinite(tick.price) || tick.price < 0) return;\n if (tick.volume !== undefined && !(Number.isFinite(tick.volume) && tick.volume >= 0)) return;\n // A tick outside regular hours, while they are hidden, moves the price line and what follows the price, not a bar.\n const write = this.series.mergeTick(tick);\n if (write.to !== 'shown') {\n this.takeLivePrice(tick.price);\n if (write.to === 'hidden') this.scheduleRender();\n return;\n }\n this.currentPriceLine.setPrice(tick.price);\n const data = this.series.bars();\n this.recalcIndicatorsFrom(data, data.length - 1);\n this.followLivePrice(tick.price);\n this.scheduleRender();\n }\n\n /**\n * A loaded bar again, with new values: the bar with the same `time`,\n * wherever it is in the series (a feed learned a closed bar was off). The\n * indicators are computed again from it on, the chart types built from\n * the bars too, and the view stays. Several corrections before the next\n * frame are computed once, from the earliest. Sends `dataUpdate`\n * (`{ kind: 'correction', time, index }`).\n *\n * It is no live price: the orders and the alerts don't follow it, and no\n * alert fires. A corrected last bar is the price line's, measured from the\n * bar before it, and the price alerts' last value, as after `setData`.\n * During a replay it corrects the live series: a bar the replay has\n * passed shows corrected at once (the replay carries over at the same\n * time), a later one when the replay reaches it; a bar in the extended\n * hours, while they are hidden, shows when they are shown. The bar loaded sent again (after the repairs every\n * write makes: a missing volume is 0) changes nothing.\n *\n * False, and nothing changes, when no loaded bar has that time (a new bar\n * is `appendBar`'s, older ones `prependBars`') or the chart can't take the\n * bar (a price that isn't a number, below zero, a high under its low).\n */\n updateBar(bar: OHLCBar): boolean {\n if (!isValidBar(bar)) return false;\n const write = this.series.replace(bar);\n if (write.to === 'none') return false;\n // Kept aside during a replay, or outside the hours shown: it shows when they do.\n if (write.to === 'shown' && write.changed) this.showCorrection(write.index);\n return true;\n }\n\n /** The bar shown at `i` was corrected: what follows from its values does, the view as it was. */\n private showCorrection(i: number): void {\n const data = this.series.bars();\n const bar = data[i];\n this.display.reset();\n // Deferred: corrections before the next read cost one computation, from the earliest.\n this.indicatorEngine.deferRecalculateFrom(data, i);\n this.announceIndicatorUpdate(i);\n if (i === data.length - 1) {\n // As `setData` leaves the last bar: the price line's, and where the next price crosses from.\n this.currentPriceLine.setPrice(bar.close, data[i - 1]?.close);\n this.alertManager.seedLastValue('price', bar.close);\n }\n this.refreshShownBar(i);\n this.eventBus.emit('dataUpdate', { kind: 'correction', time: bar.time, index: i } satisfies DataUpdatePayload);\n this.scheduleRender();\n }\n\n /**\n * The tooltips, the legend and the crosshair's listeners hold the bar\n * they were given: give them the corrected one. The bar after it moves\n * too, its change measured from this one's close; with bricks or boxes,\n * any bar can.\n */\n private refreshShownBar(i: number): void {\n const pinned = this.pinnedTooltip.isPinned() ? this.pinnedTooltip.getPinnedIndex() : -1;\n const hovered = this.crosshairHandler.getReportedBarIndex();\n // Nothing holds a bar from it on: no need to build the bars shown before the frame does.\n if (pinned < i && hovered === null) return;\n const aligned = this.isTimeAligned();\n // Heikin-Ashi bars after it are built from it.\n if (aligned && pinned >= i) {\n const shown = this.getDisplayData();\n if (shown[pinned]) this.pinnedTooltip.pin(shown[pinned], pinned, this.themeManager.getTheme(), barTimeUnit(shown), barsAreDaily(shown) === true);\n }\n if (hovered !== null && (!aligned || hovered === i || hovered === i + 1)) this.crosshairHandler.refresh();\n }\n\n // --- Chart type ---\n\n /** The type the chart is drawn as: what `setChartType`, the `chartType` option or a loaded layout set; `'candlestick'` by default. */\n getChartType(): ChartType | (string & {}) {\n return this.options.chartType ?? 'candlestick';\n }\n\n setChartType(type: ChartType | (string & {})): void {\n const previous = this.options.chartType ?? 'candlestick';\n this.options.chartType = type as ChartType;\n // The series' own style keys for this type.\n this.themeManager.setChartType(type as ChartType);\n this.chartRenderer = this.createChartRenderer(type);\n // A new renderer, even for the type set: it takes what the series is built on (the point & figure box) on the next build.\n this.display.invalidate();\n this.chartLegend.setChartType(type as ChartType);\n this.applyTimeSpacing();\n this.updateViewportAndRender(true);\n this.fitFewColumns();\n this.markStateChanged();\n if (type !== previous) this.eventBus.emit('chartTypeChange', { type, previous });\n }\n\n /**\n * A point & figure chart has a column for each reversal, often a few dozen\n * for a whole series, and Renko, Kagi, line break and range bars a brick\n * or a line for each move of their size: at the width of a bar they would\n * sit in a corner of an empty plot. Fewer than the view holds are fitted\n * to it, on a new type, box or series; more are left at the width they\n * are, as candles are.\n */\n private fitFewColumns(): void {\n const type = this.options.chartType;\n if (!isReshapedChartType(type) || this.pluginManager.getChartType(type)) return;\n const n = this.getDisplayData().length;\n const { chartRect, barWidth, barSpacing } = this.viewport.getState();\n const slots = chartRect.width / (barWidth + barSpacing);\n if (n > 0 && slots > n + this.viewport.getRightMargin()) this.fitContent();\n }\n\n /**\n * Times for the empty slots past the data only when bars are evenly spaced\n * in time: not for tick bars, nor for Renko-like charts that redraw them.\n */\n private applyTimeSpacing(): void {\n const even = !this.tickStream && !isReshapedChartType(this.options.chartType);\n this.timeAxis.setTimesPastData(even);\n this.crosshairHandler.setTimesPastData(even);\n }\n\n /**\n * Settings of Renko, Line Break, Kagi, Point & Figure and range bars, by\n * type: each type given replaces that type's settings (`{}` puts it back\n * to the defaults). Values it can't use are left out.\n */\n setChartTypeOptions(options: ChartTypeOptions): void {\n this.chartTypeOptions = { ...this.chartTypeOptions, ...readChartTypeOptions(options) };\n this.updateViewportAndRender(true);\n this.fitFewColumns();\n // New settings build other bars: a screen reader hears the new summary, as after a new type.\n this.a11y?.refresh(true);\n this.markStateChanged();\n }\n\n getChartTypeOptions(): ChartTypeOptions {\n return readChartTypeOptions(this.chartTypeOptions);\n }\n\n /**\n * Show or hide the bars outside the symbol's regular hours\n * (`SymbolInfo.sessions`, in its `timeZone`): pre- and post-market. Hidden,\n * they are kept aside and come back when shown again. Bars a day or longer,\n * and symbols without hours, are left as they are.\n */\n setExtendedHours(show: boolean): void {\n if (show === this.extendedHours) return;\n this.extendedHours = show;\n this.reapplyExtendedHours();\n this.markStateChanged();\n }\n\n isExtendedHoursVisible(): boolean {\n return this.extendedHours;\n }\n\n /** Which bar times to show, for bars like `data`'s; null: all of them. */\n private sessionKeep(data: readonly { time: number }[]): ((time: number) => boolean) | null {\n return this.extendedHours ? null : regularHoursFilter(this.symbolInfo, data);\n }\n\n /** The bars shown again after the extended hours or the symbol's hours changed. */\n private reapplyExtendedHours(): void {\n if (!this.series.refilter()) return;\n const data = this.series.bars();\n this.crosshairHandler.setData(data);\n this.display.reset();\n this.sessionBreaks.invalidateCache();\n this.recalcIndicators(data);\n this.updateViewportAndRender(true);\n this.emitBars(data.length);\n }\n\n /** The price lines for this frame: during a replay without the live bid and ask. */\n private quoteLines(): PriceLines {\n this.priceLines.setQuoteShown(this.replaySession === null);\n return this.priceLines;\n }\n\n /**\n * The shapes the chart draws: `tagRadius` rounds its price tags, axis\n * pills and order badges (0: square, 999: pills). Kept through theme\n * changes.\n */\n setShapes(shapes: ShapeConfig): void {\n const read = readShapes(shapes);\n if (read.tagRadius === undefined) return;\n this.themeManager.setShape({ ...this.themeManager.getTheme().shape, ...read });\n this.syncRenderContext();\n this.engine.requestRender();\n }\n\n getShapes(): ShapeConfig {\n return { ...this.themeManager.getTheme().shape };\n }\n\n /**\n * The face and sizes of the chart's own text (the axes, the tags, the\n * crosshair's pills), over every theme and on top of the font set before:\n * `{ family: SANS_FONT_FAMILY }` for proportional figures. The legend takes\n * the face, unless `setLegend` names one, at its own size (`setLegend`); in a\n * widget its face and size come from the look (`ui.font`). Null goes back to\n * each theme's own. A family that is empty or a size that isn't a number\n * above zero is left out.\n */\n setFont(font: Partial<FontConfig> | null): void {\n if (font === null) {\n this.themeManager.setFont(null);\n } else {\n const read = readFont(font);\n if (Object.keys(read).length === 0) return;\n this.themeManager.setFont(read);\n }\n // The price axis is as wide as its labels: fitted again for the new face and size.\n this.updateViewportAndRender();\n }\n\n /** The chart's text as drawn: the theme's font with any set by `setFont` over it. */\n getFont(): FontConfig {\n return { ...this.themeManager.getTheme().font };\n }\n\n /**\n * How a bar fills its slot: `'golden'` (the body φ⁻¹ of the slot at every\n * zoom) or `'classic'` (a 2 px gap). Over bars on screen the zoom stays.\n */\n setBarLayout(layout: BarLayout): void {\n // The same layout again runs nothing: no pass, no events (a widget sets it on every look).\n if ((layout !== 'golden' && layout !== 'classic') || layout === this.viewport.getBarLayout()) return;\n this.viewport.setBarLayout(layout);\n this.updateViewportAndRender();\n }\n\n getBarLayout(): BarLayout {\n return this.viewport.getBarLayout();\n }\n\n /** Show or hide the main series (its bars, candles or line); the rest of the chart stays. */\n setMainSeriesVisible(visible: boolean): void {\n this.mainSeriesVisible = visible;\n this.syncRenderContext();\n this.engine.requestRender();\n }\n\n isMainSeriesVisible(): boolean {\n return this.mainSeriesVisible;\n }\n\n /** Mark the highest high and lowest low on screen with a line and a price tag. */\n setHighLowLines(visible: boolean): void {\n this.priceLines.setHighLow(visible);\n this.engine.requestRender();\n }\n\n isHighLowLinesVisible(): boolean {\n return this.priceLines.isHighLowVisible();\n }\n\n /**\n * Mark the best bid and ask with lines and price tags (null takes them\n * off). A connected feed whose ticks carry `bid` / `ask` keeps them up to date.\n */\n setBidAsk(quote: BidAsk | null): void {\n this.priceLines.setBidAsk(quote);\n this.engine.requestRender(LayerType.Overlay);\n }\n\n getBidAsk(): BidAsk | null {\n return this.priceLines.getBidAsk();\n }\n\n // --- Indicators ---\n\n /**\n * Add an indicator. Panel indicators get a pane of their own at `position`.\n * `options.pane` draws it in another indicator's pane instead, on that\n * pane's scale; a price-pane indicator computed from a pane indicator's\n * line (`source: 'ind:<instanceId>:<key>'`) goes there by itself.\n *\n * A built-in's id and inputs are typed: `BuiltInIndicators` lists every\n * one with its inputs, their defaults, and the keys of its lines.\n */\n addIndicator<Id extends IndicatorId>(\n id: Id,\n params?: IndicatorParamsFor<Id>,\n position: PanelPosition = 'bottom',\n options: { pane?: string; scale?: OverlayScale } = {},\n ): string | null {\n return this.indicatorHistory.record(undefined, () => this.addIndicatorNow(id, params ?? {}, position, options));\n }\n\n private addIndicatorNow(\n id: string,\n params: Record<string, number | string | boolean>,\n position: PanelPosition,\n options: { pane?: string; scale?: OverlayScale; instanceId?: string },\n ): string | null {\n if (!this.isIndicatorAllowed(id)) return null;\n const descriptor = this.indicatorEngine.getAvailableIndicators().find((d) => d.id === id);\n const pane = descriptor ? this.indicatorPanes.paneFor(descriptor, params, options.pane) : null;\n const instanceId = this.indicatorEngine.addIndicator(id, params, this.indicatorBars(), {\n ...(pane ? { pane } : {}),\n ...(options.scale === 'left' ? { scale: 'left' as const } : {}),\n ...(options.instanceId ? { instanceId: options.instanceId } : {}),\n });\n if (descriptor?.placement === 'panel' && !pane) this.layoutManager.addPanel(instanceId, position);\n // The layout (a new pane) or the price scale (a new overlay) may change.\n this.updateViewportAndRender();\n this.eventBus.emit('indicatorAdd', { instanceId, id });\n this.markStateChanged();\n this.requestMissingSymbols(instanceId);\n return instanceId;\n }\n\n // --- Other symbols' bars ---\n\n /**\n * Another symbol's bars, for the indicators that read it ('compareSymbol',\n * 'spread', 'correlation', 'correlationlog'): they line them up with the\n * chart's bars by time. Give them\n * again after a timeframe change; null forgets them.\n */\n setSymbolSeries(symbol: string, bars: DataSeries | null): void {\n symbol = symbol.trim();\n this.symbolSeries.set(symbol, bars);\n this.symbolRequests.given(symbol);\n const readers = this.symbolRequests.readersOf(symbol);\n if (readers.length === 0) return;\n const data = this.indicatorBars();\n for (const instanceId of readers) this.indicatorEngine.updateIndicator(instanceId, {}, data);\n this.announceIndicatorUpdate(0);\n this.updateViewportAndRender();\n }\n\n /** The bars given for `symbol`, or null. */\n getSymbolSeries(symbol: string): DataSeries | null {\n return this.symbolSeries.get(symbol) ?? null;\n }\n\n /** The symbols the chart's indicators read, whether their bars are here or not. */\n getRequiredSymbols(): string[] {\n return this.symbolRequests.required();\n }\n\n /**\n * Ask once for the symbol an indicator just added or changed reads, when\n * the chart hasn't got it (not for symbols other indicators read: one\n * whose fetch failed is asked for again only by an indicator on it).\n */\n private requestMissingSymbols(instanceId: string): void {\n const symbol = this.symbolRequests.toAsk(instanceId);\n if (symbol) this.eventBus.emit('symbolSeriesRequest', { symbol });\n }\n\n /**\n * Change an indicator's inputs. A source that would make it read from its\n * own lines (through other indicators) is ignored.\n */\n updateIndicator(instanceId: string, params: Record<string, number | string | boolean>): void {\n this.indicatorHistory.record(instanceId, () => this.updateIndicatorNow(instanceId, params));\n }\n\n private updateIndicatorNow(instanceId: string, params: Record<string, number | string | boolean>): void {\n const before = this.indicatorPanes.sourceOf(instanceId);\n this.indicatorEngine.updateIndicator(instanceId, params, this.indicatorBars());\n this.announceIndicatorUpdate(0);\n this.markStateChanged();\n this.eventBus.emit('indicatorChange', { instanceId, change: 'params' });\n this.requestMissingSymbols(instanceId);\n // A price-pane indicator follows a new source into its pane, and leaves\n // it when it no longer reads from it; otherwise it stays where it is.\n if (this.indicatorPanes.followSource(instanceId, before)) this.eventBus.emit('indicatorChange', { instanceId, change: 'pane' });\n this.updateViewportAndRender();\n }\n\n /** An indicator's reference levels (RSI 30 / 70): its own, else its indicator's defaults. */\n getIndicatorLevels(instanceId: string): number[] {\n return this.indicatorEngine.getLevels(instanceId);\n }\n\n /**\n * Put a price-pane overlay on the left price scale (fit to its own values,\n * the scale shows by itself) or back on the price scale. False when the\n * indicator isn't a price-pane overlay.\n */\n setIndicatorScale(instanceId: string, scale: OverlayScale): boolean {\n return this.indicatorHistory.record(instanceId, () => this.setIndicatorScaleNow(instanceId, scale));\n }\n\n private setIndicatorScaleNow(instanceId: string, scale: OverlayScale): boolean {\n if (!this.indicatorEngine.setScale(instanceId, scale)) return false;\n this.updateViewportAndRender();\n this.markStateChanged();\n this.eventBus.emit('indicatorChange', { instanceId, change: 'scale' });\n return true;\n }\n\n getIndicatorScale(instanceId: string): OverlayScale {\n return this.indicatorEngine.getIndicatorConfig(instanceId)?.scale === 'left' ? 'left' : 'right';\n }\n\n /** Show the left price scale even without overlays on it (it then mirrors the price scale). */\n setLeftPriceScaleVisible(visible: boolean): void {\n this.leftScaleRequested = visible;\n this.updateViewportAndRender();\n }\n\n /** Whether `setLeftPriceScaleVisible` asked for the left scale; it also shows while an overlay is on it (`isLeftPriceScaleShown`). */\n isLeftPriceScaleVisible(): boolean {\n return this.leftScaleRequested;\n }\n\n /** Whether the left price scale is on screen: asked for, or carrying an overlay. */\n isLeftPriceScaleShown(): boolean {\n return this.features.priceAxis !== false && (this.leftScaleRequested || this.indicatorEngine.hasLeftScaleOverlays());\n }\n\n /** The left price scale's range on screen, or null while it is hidden. */\n getLeftPriceRange(): { min: number; max: number } | null {\n return this.leftViewport ? { ...this.leftViewport.priceRange } : null;\n }\n\n /**\n * The left scale's viewport for this frame: fit to the overlays on it over\n * the visible bars (on a plain linear scale), or the price scale's when\n * none is.\n */\n private computeLeftViewport(): ViewportState | null {\n if (!this.isLeftPriceScaleShown()) return null;\n const vs = this.viewport.getState();\n if (!this.indicatorEngine.hasLeftScaleOverlays()) return vs;\n const range = this.indicatorEngine.getOverlayPriceRange(vs.visibleRange.from, vs.visibleRange.to, 'left');\n if (!range) return vs;\n const span = range.max - range.min || Math.abs(range.max) || 1;\n return {\n ...vs,\n priceRange: { min: range.min - span * LEFT_SCALE_PADDING, max: range.max + span * LEFT_SCALE_PADDING },\n logScale: false,\n scaleMode: 'regular',\n invertScale: false,\n // The overlays' own numbers, not the price's format or decimals.\n formatPrice: undefined,\n pricePrecision: undefined,\n priceUnit: undefined,\n };\n }\n\n /** Set an indicator's reference levels; `null` restores its indicator's defaults. */\n setIndicatorLevels(instanceId: string, levels: readonly number[] | null): void {\n this.indicatorHistory.record(instanceId, () => this.setIndicatorLevelsNow(instanceId, levels));\n }\n\n private setIndicatorLevelsNow(instanceId: string, levels: readonly number[] | null): void {\n if (!this.indicatorEngine.setLevels(instanceId, levels)) return;\n this.updateViewportAndRender();\n this.markStateChanged();\n this.eventBus.emit('indicatorChange', { instanceId, change: 'levels' });\n }\n\n /**\n * Remove an indicator, and the indicators computed from its lines with it.\n * Others drawn in its pane keep the pane (a pane indicator takes it over)\n * or go back to the price pane.\n */\n removeIndicator(instanceId: string): void {\n this.indicatorHistory.record(undefined, () => this.removeIndicatorNow(instanceId));\n }\n\n /** Remove several indicators, each as `removeIndicator` does, in one undo step. */\n removeIndicators(instanceIds: readonly string[]): void {\n this.indicatorHistory.record(undefined, () => {\n for (const id of instanceIds) this.removeIndicatorNow(id);\n });\n }\n\n private removeIndicatorNow(instanceId: string): void {\n if (!this.indicatorEngine.getIndicatorConfig(instanceId)) return;\n // It and everything computed from its lines, the last readers first.\n const doomed = [instanceId, ...this.indicatorEngine.getDependents(instanceId)];\n for (const id of doomed.reverse()) this.detachIndicator(id);\n this.markStateChanged();\n this.updateViewportAndRender();\n }\n\n /**\n * Remove one indicator. The first pane indicator drawn in its pane takes the\n * pane over and the others stay in it.\n */\n private detachIndicator(instanceId: string): void {\n // Each indicator drawn in its pane is reported as it changes panes.\n const members = this.indicatorPanes.detach(instanceId, (member) => this.eventBus.emit('indicatorChange', { instanceId: member, change: 'pane' }));\n if (!members) return;\n this.eventBus.emit('indicatorRemove', { instanceId });\n this.symbolRequests.forgetUnread();\n }\n\n /** The colours, line widths and opacity an indicator draws with (a copy), or null. */\n getIndicatorStyle(instanceId: string): ResolvedIndicatorStyle | null {\n return this.indicatorEngine.getIndicatorStyle(instanceId);\n }\n\n /**\n * The style indicators of kind `id` start with from now on: colours, line\n * widths, opacity, each plot's dash and visibility (null: their own\n * again). Indicators already on the chart keep theirs. Not saved: it is\n * your app's.\n */\n setIndicatorDefaults(id: string, style: IndicatorStyleConfig | null): void {\n if (!style) {\n this.indicatorEngine.setStyleDefaults(id, null);\n return;\n }\n const { plots, ...rest } = style;\n const read = plots !== undefined ? readIndicatorPlotStyles(plots) : undefined;\n this.indicatorEngine.setStyleDefaults(id, { ...rest, ...(read ? { plots: read } : {}) });\n }\n\n /**\n * An indicator pane's own background and separator, over the panes' style\n * (`panes.*` overrides); null takes them away. Colours it can't use are\n * left out. Saved with the pane's indicator, and undone with it.\n */\n setPaneStyle(instanceId: string, style: PaneStyle | null): void {\n const pane = this.indicatorPanes.paneOf(instanceId);\n if (!this.layoutManager.getPanels().some((p) => p.id === pane)) return;\n this.indicatorHistory.record(pane, () => this.setPaneStyleNow(pane, style ? readPaneStyle(style) : null));\n }\n\n private setPaneStyleNow(instanceId: string, style: PaneStyle | null): void {\n if (!this.panes.setStyle(instanceId, style)) return;\n this.syncRenderContext();\n this.engine.requestRender();\n this.markStateChanged();\n }\n\n /** The own style of the pane `instanceId` is drawn in, or null when it has none. */\n getPaneStyle(instanceId: string): PaneStyle | null {\n const style = this.panes.style(this.indicatorPanes.paneOf(instanceId));\n return style ? { ...style } : null;\n }\n\n /**\n * An indicator's values, by the instance id `addIndicator` returned; null\n * for an unknown one. `series[i]` is its value at bar `i` of `getData()`,\n * an object keyed by its lines (`{ value }` for RSI, `{ macd, signal,\n * histogram }` for MACD: `BuiltInIndicators[id]['lines']`), or null where\n * it has none yet (before its period fills). `values` holds the same by\n * bar time.\n *\n * @example\n * const rsi = chart.addIndicator('rsi', { period: 14 })!;\n * const out = chart.getIndicatorOutput(rsi);\n * const latest = out?.series?.[chart.getData().length - 1]?.value;\n */\n getIndicatorOutput(instanceId: string): IndicatorOutput | null {\n return this.indicatorEngine.getOutput(instanceId);\n }\n\n registerIndicator(plugin: IndicatorPlugin): void {\n this.pluginManager.registerIndicator(plugin);\n }\n\n /** The chart's plugin registry — register custom indicators / drawings / chart types / overlays. */\n get plugins(): PluginManager {\n return this.pluginManager;\n }\n\n static indicators(): IndicatorDescriptor[] {\n const engine = new IndicatorEngine();\n registerBuiltInIndicators(engine);\n return engine.getAvailableIndicators();\n }\n\n // --- Panel layout ---\n\n setPanelPosition(instanceId: string, position: PanelPosition): void {\n this.layoutManager.setPanelPosition(instanceId, position);\n this.updateViewportAndRender();\n this.eventBus.emit('indicatorChange', { instanceId, change: 'pane' });\n }\n\n setPanelSize(instanceId: string, size: number): void {\n this.layoutManager.setPanelSize(instanceId, size);\n this.updateViewportAndRender();\n this.eventBus.emit('paneResize', { instanceId, size });\n this.markStateChanged();\n }\n\n /**\n * Move an indicator to another pane: `target` is an indicator drawn there\n * (it joins that pane, on its scale), `'new'` a pane of its own (indicators\n * that draw in a pane), or `'price'` the price pane (overlays). Indicators\n * drawn in its own pane stay there (one of them takes the pane over) or\n * follow it when they read its lines. False when it can't go there.\n */\n moveIndicatorToPane(instanceId: string, target: string): boolean {\n return this.indicatorHistory.record(instanceId, () => this.moveIndicatorToPaneNow(instanceId, target));\n }\n\n /** Whether `moveIndicatorToPane(instanceId, target)` would move it. */\n canMoveIndicatorToPane(instanceId: string, target: string): boolean {\n return this.indicatorPanes.moveHost(instanceId, target) !== undefined;\n }\n\n private moveIndicatorToPaneNow(instanceId: string, target: string): boolean {\n const changed = this.indicatorPanes.move(instanceId, target);\n if (!changed) return false;\n for (const id of changed) this.eventBus.emit('indicatorChange', { instanceId: id, change: 'pane' });\n this.updateViewportAndRender();\n this.markStateChanged();\n return true;\n }\n\n /**\n * Fold a pane to its header, or open it again: the pane `instanceId` is\n * drawn in. While a pane is maximised, this puts the panes back first.\n */\n setPaneCollapsed(instanceId: string, collapsed: boolean): boolean {\n const pane = this.indicatorPanes.paneOf(instanceId);\n return this.indicatorHistory.record(pane, () => this.paneChange(pane, 'collapsed', () => this.layoutManager.setPanelCollapsed(pane, collapsed)));\n }\n\n /** Whether the pane `instanceId` is drawn in shows folded (folded, or another pane is maximised). */\n isPaneCollapsed(instanceId: string): boolean {\n return this.layoutManager.isPanelShownCollapsed(this.indicatorPanes.paneOf(instanceId));\n }\n\n /** Let one pane take the room (the others fold, the price pane keeps a strip); `null` puts them back. */\n setMaximizedPane(instanceId: string | null): boolean {\n const pane = instanceId === null ? null : this.indicatorPanes.paneOf(instanceId);\n const subject = pane ?? this.layoutManager.getMaximizedPanel() ?? '';\n return this.indicatorHistory.record(subject, () => this.paneChange(subject, 'maximized', () => this.layoutManager.setMaximizedPanel(pane)));\n }\n\n getMaximizedPane(): string | null {\n return this.layoutManager.getMaximizedPanel();\n }\n\n /**\n * A pane's value scale: logarithmic (used while all its values are above 0;\n * linear otherwise), upside down. The pane `instanceId` is drawn in.\n */\n setPaneScale(instanceId: string, scale: { log?: boolean; invert?: boolean; percent?: boolean }): boolean {\n const pane = this.indicatorPanes.paneOf(instanceId);\n return this.indicatorHistory.record(pane, () => this.paneChange(pane, 'scale', () => this.layoutManager.setPanelScale(pane, scale)));\n }\n\n /** The pane's value scale as set (see `setPaneScale`). */\n getPaneScale(instanceId: string): { log: boolean; invert: boolean; percent: boolean } {\n const panel = this.layoutManager.getPanels().find((p) => p.id === this.indicatorPanes.paneOf(instanceId));\n return { log: !!panel?.logScale, invert: !!panel?.invertScale, percent: !!panel?.percentScale };\n }\n\n /** Move a pane one place up (`-1`) or down (`1`) among the panes on its side. */\n movePane(instanceId: string, delta: -1 | 1): boolean {\n const pane = this.indicatorPanes.paneOf(instanceId);\n return this.indicatorHistory.record(pane, () => this.paneChange(pane, 'order', () => this.layoutManager.movePanel(pane, delta)));\n }\n\n /** Whether `movePane(instanceId, delta)` would move it. */\n canMovePane(instanceId: string, delta: -1 | 1): boolean {\n return this.layoutManager.canMovePanel(this.indicatorPanes.paneOf(instanceId), delta);\n }\n\n private paneChange(instanceId: string, change: 'collapsed' | 'maximized' | 'order' | 'scale', apply: () => boolean): boolean {\n if (!apply()) return false;\n this.updateViewportAndRender();\n this.eventBus.emit('paneChange', { instanceId, change });\n this.markStateChanged();\n return true;\n }\n\n /**\n * Indicator panes, in CSS pixels relative to the container: the whole pane,\n * its 20 px header (where the name is written) included.\n */\n getIndicatorPanes(): { instanceId: string; instanceIds: string[]; rect: { x: number; y: number; width: number; height: number } }[] {\n return this.buildPanelRenderInfos().map((p) => ({\n instanceId: p.instanceId,\n instanceIds: [p.instanceId, ...(p.members ?? [])],\n rect: { ...p.rect },\n }));\n }\n\n /** Tag each indicator line's latest value on its value axis (off by default). */\n setIndicatorValueLabelsVisible(visible: boolean): void {\n this.features.indicatorValueLabels = visible;\n this.syncRenderContext();\n this.engine.requestRender();\n }\n\n isIndicatorValueLabelsVisible(): boolean {\n return this.features.indicatorValueLabels;\n }\n\n /** Move the price axis's tags apart where they would cover one another, the last price's staying on its line (on by default). */\n setAxisLabelsAligned(aligned: boolean): void {\n this.features.alignAxisLabels = aligned;\n this.syncRenderContext();\n this.engine.requestRender();\n }\n\n isAxisLabelsAligned(): boolean {\n return this.features.alignAxisLabels;\n }\n\n /** Mark the selected (or the drawn) drawing's span on the axes, its ends labelled (on by default). */\n setDrawingAxisSpansVisible(visible: boolean): void {\n this.features.drawingAxisSpans = visible;\n this.syncRenderContext();\n this.engine.requestRender();\n }\n\n isDrawingAxisSpansVisible(): boolean {\n return this.features.drawingAxisSpans;\n }\n\n /** Write each pane's indicator name and hovered values in its header (default); off when you label panes yourself. */\n setPaneTitlesVisible(visible: boolean): void {\n this.paneTitles = visible;\n this.engine.requestRender();\n }\n\n /**\n * The y (CSS px, container-relative) just below the OHLCV legend — where\n * content stacked under it starts. The plot's top edge when it is hidden.\n */\n getLegendBottom(): number {\n const top = this.viewport.getState().chartRect.y;\n return this.features.legend ? top + this.chartLegend.getHeight() : top;\n }\n\n /**\n * The size of what you lay over the chart under the legend (rows of your\n * own, as ChartWidget's indicator rows are), from the plot's left edge and\n * `getLegendBottom()` down, in CSS px: drawing labels keep clear of it as\n * they keep clear of the legend. `null` when nothing is there.\n */\n setLegendStack(size: { width: number; height: number } | null): void {\n const next = size && size.width > 0 && size.height > 0 ? { width: size.width, height: size.height } : null;\n const same = next === this.legendStack\n || (next !== null && this.legendStack !== null && next.width === this.legendStack.width && next.height === this.legendStack.height);\n if (same) return;\n this.legendStack = next;\n this.engine.requestRender();\n }\n\n /** The legend's rows as drawn and what is stacked under them: drawing labels keep clear of these. */\n private legendKeepOut(): { x: number; y: number; width: number; height: number }[] {\n const plot = this.viewport.getState().chartRect;\n const bottom = this.getLegendBottom();\n const boxes: { x: number; y: number; width: number; height: number }[] = [];\n const width = this.features.legend ? this.chartLegend.getWidth() : 0;\n if (width > 0 && bottom > plot.y) boxes.push({ x: plot.x, y: plot.y, width, height: bottom - plot.y });\n if (this.legendStack) boxes.push({ x: plot.x, y: bottom, ...this.legendStack });\n return boxes;\n }\n\n /**\n * Whether each drawn bar is one data bar (candles, lines, Heikin-Ashi…).\n * Renko, Kagi, point & figure, line break and range bars redraw the series,\n * so a bar index on screen is not an index into the data.\n */\n isTimeAligned(): boolean {\n if (isReshapedChartType(this.options.chartType)) return false;\n return this.getDisplayData().length === this.series.length();\n }\n\n /**\n * A price on the market's grid: a multiple of its smallest step (`minTick`),\n * else rounded to its precision, else to the decimals the axis shows.\n */\n roundPrice(price: number): number {\n if (!Number.isFinite(price)) return price;\n const tick = this.stepAt(price);\n if (tick !== undefined) return Number((Math.round(price / tick) * tick).toFixed(stepDecimals(tick)));\n const { min, max } = this.viewport.getState().priceRange;\n return Number(price.toFixed(this.marketPricePrecision ?? autoPricePrecision(min, max)));\n }\n\n /** The newest bar's close, 0 without bars. */\n private lastClose(): number {\n const bars = this.series.bars();\n return bars[bars.length - 1]?.close ?? 0;\n }\n\n /** The symbol's `minTick`, a fraction's unit, or the market's step at `price` (its band). */\n private stepAt(price: number): number | undefined {\n const tick = this.symbolInfo?.minTick ?? this.priceUnit ?? undefined;\n if (tick !== undefined && tick > 0) return tick;\n return this.marketConfig ? priceStepAt(price, this.marketConfig) : undefined;\n }\n\n /**\n * The smallest step prices move in: the symbol's `minTick`, a fraction's\n * unit (`priceFormat: { denominator: 32 }`), the market's step at `price`\n * (at the last close without one: HOSE's step depends on the price), else\n * one unit of the decimals prices show. `roundPrice` puts prices on it; a\n * plugin can size price rows by it.\n */\n getPriceStep(price?: number): number {\n const tick = this.stepAt(price ?? this.lastClose());\n if (tick !== undefined) return tick;\n const { min, max } = this.viewport.getState().priceRange;\n const decimals = this.marketPricePrecision ?? autoPricePrecision(min, max);\n return Number((10 ** -decimals).toFixed(decimals));\n }\n\n /**\n * How prices read on the price scale and everything that prints one: a\n * function of yours, fractions of a point (`{ denominator: 32 }` prints\n * `101'16`), or null for decimals. Indicator panes keep their own numbers.\n */\n setPriceFormat(format: PriceFormatter | PriceFraction | null): void {\n this.applyPriceFormat(format);\n // The axis may need room for the new labels, and the frame its new format.\n this.updateViewportAndRender();\n }\n\n /** The chart's price format as a function (`priceFormat`), or null for decimals. */\n getPriceFormatter(): PriceFormatter | null {\n return this.priceFormatter;\n }\n\n private applyPriceFormat(format: PriceFormatter | PriceFraction | null): void {\n this.priceFormatter = priceFormatterFor(format);\n this.priceUnit = format && typeof format !== 'function' ? fractionTick(format) : null;\n this.viewport.setPriceFormat(this.priceFormatter, this.priceUnit);\n this.crosshairTooltip.setPriceFormatter(this.priceFormatter);\n this.pinnedTooltip.setPriceFormatter(this.priceFormatter);\n }\n\n /** How times read on the time axis, the crosshair and the tooltip; null for the chart's own. */\n setTimeFormatter(formatter: TimeFormatter | null): void {\n this.applyTimeFormatter(formatter);\n this.engine.requestRender();\n }\n\n private applyTimeFormatter(formatter: TimeFormatter | null): void {\n this.timeAxis.setTimeFormatter(formatter);\n this.crosshairHandler.setTimeFormatter(formatter);\n this.crosshairTooltip.setTimeFormatter(formatter);\n }\n\n /**\n * A price as the price axis writes it: in the chart's price format when it\n * has one (`priceFormat`), else at the market's precision or the visible\n * range's, in the number locale.\n */\n formatPrice(price: number): string {\n if (this.priceFormatter) return this.priceFormatter(price);\n const { min, max } = this.viewport.getState().priceRange;\n return formatPrice(price, this.marketPricePrecision ?? autoPricePrecision(min, max), this.numberLocale);\n }\n\n // --- Drawing tools ---\n\n setDrawingTool(type: DrawingToolType | null): void {\n if (!this.features.drawings) return;\n // If whitelist is set, check it\n if (type && this.features.drawingTools.length > 0 && !this.features.drawingTools.includes(type)) return;\n // A tool picked puts the eraser and the zoom box away (the manager does the eraser).\n if (type) this.setZoomAreaMode(false);\n this.drawingManager.setActiveTool(type);\n }\n\n getDrawingTool(): DrawingToolType | null {\n return this.drawingManager.getActiveTool();\n }\n\n /** Keep the drawing tool after each drawing, to draw several in a row (Esc ends). */\n setStayInDrawingMode(enabled: boolean): void {\n this.drawingManager.setStayInDrawingMode(enabled);\n }\n\n isStayInDrawingMode(): boolean {\n return this.drawingManager.isStayInDrawingMode();\n }\n\n /** Copy the selected drawings (also Ctrl/⌘+C on the chart). Returns how many. */\n copyDrawings(): number {\n return this.drawingManager.copySelection();\n }\n\n /**\n * Paste copied drawings (also Ctrl/⌘+V) — from this chart or another one on\n * the page. Returns the new drawings' ids.\n */\n pasteDrawings(): string[] {\n if (!this.features.drawings) return [];\n return this.drawingManager.paste();\n }\n\n /** Whether `pasteDrawings()` would paste: drawings copied (here or on another chart of the page). */\n canPasteDrawings(): boolean {\n return this.features.drawings !== false && this.drawingManager.canPaste();\n }\n\n setDrawingStyle(style: Partial<DrawingStyle>): void {\n this.drawingManager.setStyle(style);\n }\n\n getDrawingStyle(): DrawingStyle {\n return this.drawingManager.getActiveStyle();\n }\n\n getSelectedDrawingId(): string | null {\n return this.drawingManager.getSelectedDrawingId();\n }\n\n /** Restyle the selected drawing (or one by id). Returns true if applied. */\n setSelectedDrawingStyle(style: Partial<DrawingStyle>, id?: string): boolean {\n return this.drawingManager.setSelectedDrawingStyle(style, id);\n }\n\n getDrawings(): DrawingState[] {\n return this.drawingManager.getDrawings();\n }\n\n setDrawings(drawings: DrawingState[]): void {\n this.drawingManager.setDrawings(drawings);\n this.dropOrphanDrawingAlerts();\n this.announceStateChange();\n }\n\n /** Append a drawing (id auto-assigned, active style applied). Returns the id. */\n addDrawing(state: {\n type: import('@tradecanvas/commons').DrawingToolType;\n anchors: import('@tradecanvas/commons').AnchorPoint[];\n style?: Partial<DrawingStyle>;\n /** The tool's own settings (see `getDrawingOptionDefs`); its defaults fill the rest. */\n options?: DrawingOptions;\n visible?: boolean;\n locked?: boolean;\n /** The intervals it shows on (`{ minutes: false }`: not on minute charts); unset: all. */\n intervals?: IndicatorIntervals;\n /** The indicator pane it goes on (an id from `getIndicatorPanes()`): its anchors' prices are that pane's values. Unset: the price pane. */\n paneId?: string;\n /** Its name in the object tree (at most 80 characters); unset: its tool's. */\n name?: string;\n meta?: Record<string, unknown>;\n }): string | null {\n if (!this.features.drawings) return null;\n if (state.paneId !== undefined && !this.takesDrawing(state.paneId, state.type)) return null;\n const id = this.drawingManager.addDrawing(state);\n this.markStateChanged();\n return id;\n }\n\n /**\n * Draw a Fibonacci retracement over the dominant swing (extreme high/low) in\n * the visible range. Returns the new drawing's id, or null if no swing.\n */\n autoFib(): string | null {\n if (!this.features.drawings) return null;\n const data = this.getDisplayData();\n const vs = this.viewport.getState();\n const swing = findDominantSwing(data, vs.visibleRange.from, vs.visibleRange.to);\n if (!swing) return null;\n return this.addDrawing({ type: 'fibRetracement', anchors: [swing[0], swing[1]] });\n }\n\n removeDrawing(id: string): void {\n this.drawingManager.removeDrawing(id);\n }\n\n /** Remove several drawings as one undo step; locked ones stay. Returns how many went. */\n removeDrawings(ids: readonly string[]): number {\n return this.drawingManager.removeDrawings(ids);\n }\n\n /**\n * An arrow key with drawings selected: move them a bar across or a pixel\n * up or down (Shift: ten), one undo step for a run of presses. False when\n * it is not an arrow, or nothing selected can move.\n */\n private nudgeSelectedDrawings(e: KeyboardEvent): boolean {\n const step = NUDGE_KEYS[e.key];\n if (!step || !this.features.drawings || e.ctrlKey || e.metaKey || e.altKey) return false;\n const times = e.shiftKey ? NUDGE_SHIFT_STEPS : 1;\n const viewport = { ...this.viewport.getState(), data: this.getDisplayData() };\n return this.drawingManager.nudge(step[0] * times, step[1] * times, viewport);\n }\n\n /**\n * Where a drawing's text box was drawn last, its padding included, in the\n * chart's own pixels (as `getDrawingRect`); null for a drawing that writes\n * in no box, a hidden one, or one not drawn yet. To lay a text box of yours\n * over its words.\n */\n getDrawingTextRect(id: string): { x: number; y: number; width: number; height: number } | null {\n if (!this.drawingManager.isShown(id)) return null;\n return this.drawingManager.textBoxOf(id, { ...this.viewport.getState(), data: this.getDisplayData() });\n }\n\n /**\n * Change a drawing just made within the step that made it (its words typed\n * in place): one undo takes the drawing and the change away. Once another\n * step came after it, the change is a step of its own.\n */\n amendNewDrawing(id: string, patch: DrawingPatch): boolean {\n const changed = this.drawingManager.amendNew(id, patch);\n if (changed) this.markStateChanged();\n return changed;\n }\n\n /**\n * Take back a drawing just made, as a \"Cancel\" right after it: with the\n * step that made it when that is the history's last, so nothing is left to\n * undo or redo; else removed as a step of its own.\n */\n takeBackDrawing(id: string): void {\n if (!this.features.drawings) return;\n this.drawingManager.takeBack(id);\n this.markStateChanged();\n }\n\n /**\n * Remove every drawing (`'drawings'`), every indicator (`'indicators'`) or\n * both (`'all'`, the default) as one undo step. Locked drawings stay, as\n * they do when a selection is removed. Returns how many of each went.\n */\n removeAll(what: 'drawings' | 'indicators' | 'all' = 'all'): { drawings: number; indicators: number } {\n return this.undoRedoManager.group(() => {\n const drawings = what === 'indicators' ? 0 : this.removeDrawings(this.getDrawings().map((d) => d.id));\n const ids = what === 'drawings' ? [] : this.getActiveIndicators().map((i) => i.instanceId);\n if (ids.length > 0) this.removeIndicators(ids);\n return { drawings, indicators: ids.length };\n });\n }\n\n /** A drawing tool's name, anchors, options and style fields; null for an unknown tool. */\n getDrawingToolDescriptor(type: DrawingToolType): DrawingDescriptor | null {\n const descriptor = this.drawingManager.getDescriptor(type);\n return descriptor ? structuredClone(descriptor) : null;\n }\n\n /** The settings a drawing tool offers beyond the shared style (levels, extend…), with defaults. */\n getDrawingOptionDefs(type: DrawingToolType): DrawingOptionDefs {\n return this.drawingManager.getOptionDefs(type);\n }\n\n /** Every option of a drawing, its tool's defaults filled in. */\n getDrawingOptions(id: string): DrawingOptions {\n return this.drawingManager.getDrawingOptions(id);\n }\n\n /** Change some of a drawing's options; invalid ones are dropped. False for an unknown drawing. */\n setDrawingOptions(id: string, options: DrawingOptions): boolean {\n return this.updateDrawing(id, { options });\n }\n\n /**\n * Change a drawing's anchors (as many as it has), style, options or the\n * intervals it shows on (`intervals: null`: all of them), as one undo step\n * — or as part of the edit begun with `beginDrawingEdit`. Applies to locked\n * drawings too. False for an unknown drawing or wrong anchors.\n */\n updateDrawing(id: string, patch: DrawingPatch): boolean {\n const changed = this.drawingManager.updateDrawing(id, patch);\n if (changed) this.markStateChanged();\n return changed;\n }\n\n /**\n * Whether a drawing is on the chart: not hidden, and kept to no intervals\n * or to the chart's (see `DrawingState.intervals`). False for an unknown one.\n */\n isDrawingShown(id: string): boolean {\n return this.drawingManager.isShown(id);\n }\n\n /** Start an edit (a settings dialog): the `updateDrawing` calls until `endDrawingEdit` are one undo step. */\n beginDrawingEdit(id: string): boolean {\n return this.drawingManager.beginEdit(id);\n }\n\n /** Keep the edit as one undo step, or with `cancel` put the drawing back as it was. */\n endDrawingEdit(id: string, options: { cancel?: boolean } = {}): void {\n this.drawingManager.endEdit(id, options);\n this.markStateChanged();\n }\n\n /** Options new drawings of `type` start with; null clears them. */\n setDrawingToolDefaults(type: DrawingToolType, options: DrawingOptions | null): void {\n this.drawingManager.setToolDefaults(type, options);\n }\n\n getDrawingToolDefaults(type: DrawingToolType): DrawingOptions {\n return this.drawingManager.getToolDefaults(type);\n }\n\n setDrawingVisible(id: string, visible: boolean): void {\n this.drawingManager.setDrawingVisible(id, visible);\n }\n\n setDrawingLocked(id: string, locked: boolean): void {\n this.drawingManager.setDrawingLocked(id, locked);\n }\n\n /** Show or hide several drawings (a selection, say) as one undo step. */\n setDrawingsVisible(ids: readonly string[], visible: boolean): void {\n this.drawingManager.setDrawingsVisible(ids, visible);\n }\n\n /** Lock or unlock several drawings as one undo step. */\n setDrawingsLocked(ids: readonly string[], locked: boolean): void {\n this.drawingManager.setDrawingsLocked(ids, locked);\n }\n\n clearDrawings(): void {\n this.drawingManager.clearDrawings();\n this.dropOrphanDrawingAlerts();\n }\n\n registerDrawingTool(plugin: DrawingPlugin): void {\n this.drawingManager.register(plugin);\n }\n\n // --- Undo/Redo ---\n\n undo(): boolean {\n if (!this.features.drawingUndoRedo) return false;\n return this.drawingManager.undo();\n }\n\n redo(): boolean {\n if (!this.features.drawingUndoRedo) return false;\n return this.drawingManager.redo();\n }\n\n /** Select a drawing (and the rest of its group); false for one it can't (unknown, hidden). */\n selectDrawing(id: string): boolean {\n const ok = this.drawingManager.select(id);\n if (ok) this.engine.requestRender();\n return ok;\n }\n\n getUndoRedoState(): { canUndo: boolean; canRedo: boolean } {\n return this.undoRedoManager.getState();\n }\n\n /**\n * Put a change of yours in the chart's undo history, with its drawings and\n * indicators: undo (Ctrl/Cmd+Z) calls `undo`, redo calls `redo`. Call it\n * after making the change. Changes about the same `subject` close together\n * merge into one step (a colour dragged across a picker). Ignored when\n * `features.drawingUndoRedo` is off.\n */\n recordUndo(step: { undo: () => void; redo: () => void; subject?: string }): void {\n if (!this.features.drawingUndoRedo || typeof step?.undo !== 'function' || typeof step.redo !== 'function') return;\n const at = performance.now();\n const last = this.undoRedoManager.last();\n if (step.subject !== undefined && last?.type === 'custom' && last.custom?.subject === step.subject && at - last.custom.at < INDICATOR_EDIT_MERGE_MS) {\n // Undo goes back to before the burst; redo to its end.\n this.undoRedoManager.replaceLast({ ...last, custom: { undo: last.custom.undo, redo: step.redo, subject: step.subject, at } });\n return;\n }\n this.undoRedoManager.push({ type: 'custom', before: null, after: null, custom: { undo: step.undo, redo: step.redo, subject: step.subject, at } });\n }\n\n // --- Drawing magnet ---\n\n /** The weak magnet on or off. Ignored (stays off) when `features.drawingMagnet` is false. */\n setDrawingMagnet(enabled: boolean): void {\n this.setDrawingMagnetMode(enabled ? 'weak' : 'off');\n }\n\n /** Whether a magnet (weak or strong) is on. */\n getDrawingMagnet(): boolean {\n return this.drawingManager.getMagnetMode() !== 'none';\n }\n\n /**\n * Snap new anchors to the bar's open, high, low or close: 'weak' when the\n * pointer is near one, 'strong' always. Stays off when\n * `features.drawingMagnet` is false.\n */\n setDrawingMagnetMode(mode: 'off' | 'weak' | 'strong'): void {\n const on = this.features.drawingMagnet && mode !== 'off';\n this.drawingManager.setMagnetMode(!on ? 'none' : mode === 'strong' ? 'strong' : 'magnet');\n }\n\n getDrawingMagnetMode(): 'off' | 'weak' | 'strong' {\n const mode = this.drawingManager.getMagnetMode();\n return mode === 'none' ? 'off' : mode === 'strong' ? 'strong' : 'weak';\n }\n\n // --- Right-click and the \"+\" by the price axis ---\n\n /** The price and bar time at a right-click, as far as its area has them. */\n private contextAt(area: import('@tradecanvas/commons').ChartContextArea, pos: { x: number; y: number }): import('@tradecanvas/commons').ChartContextMenuPayload {\n const vs = { ...this.viewport.getState(), data: this.getDisplayData() };\n const payload: import('@tradecanvas/commons').ChartContextMenuPayload = { area, x: pos.x, y: pos.y };\n if (area === 'plot' || area === 'priceAxis') payload.price = yToPrice(pos.y, vs);\n if ((area === 'plot' || area === 'timeAxis') && vs.data.length > 0) payload.time = xToTime(pos.x, vs);\n if (area === 'pane') {\n const pane = this.getIndicatorPanes().find((p) => pos.y >= p.rect.y && pos.y <= p.rect.y + p.rect.height);\n if (pane) payload.pane = pane.instanceId;\n }\n return payload;\n }\n\n /**\n * The \"+\" rides on the crosshair: without one it is neither drawn nor\n * pressed. While it shows, the trading tags end left of its strip, so it\n * never covers their buttons.\n */\n private applyPriceAxisAdd(): void {\n const on = this.features.priceAxisAddButton && this.features.crosshair;\n this.priceAxisAddButton.setEnabled(on);\n this.tradingManager.setTagInset(on ? PRICE_AXIS_ADD_STRIP : 0);\n }\n\n /** Show or hide the \"+\" by the price axis (`features.priceAxisAddButton`). */\n setPriceAxisAddButton(visible: boolean): void {\n this.features.priceAxisAddButton = visible;\n this.applyPriceAxisAdd();\n this.engine.requestRender(LayerType.Overlay);\n }\n\n // --- Eraser and zoom area ---\n\n /**\n * The eraser: while on, clicking a drawing removes it (each one undoable).\n * Picking a drawing tool or pressing Escape turns it off. `toolModeChange`\n * reports `{ eraser }`.\n */\n setEraserMode(on: boolean): void {\n if (on) this.setZoomAreaMode(false);\n this.drawingManager.setEraser(on && this.features.drawings);\n }\n\n isEraserMode(): boolean {\n return this.drawingManager.isEraser();\n }\n\n /**\n * The zoom-area tool: the next drag draws a box and the chart zooms to its\n * bars; then it turns itself off. `toolModeChange` reports `{ zoomArea }`.\n */\n setZoomAreaMode(on: boolean): void {\n if (on === this.zoomAreaMode) return;\n this.zoomAreaMode = on;\n if (on) {\n this.drawingManager.setEraser(false);\n this.drawingManager.setActiveTool(null);\n }\n this.interactionManager?.setZoomAreaMode(on);\n this.eventBus.emit('toolModeChange', { zoomArea: on });\n }\n\n isZoomAreaMode(): boolean {\n return this.zoomAreaMode;\n }\n\n /** Zoom to the bars under a box dragged in zoom-area mode. */\n private zoomToBox(box: { x0: number; x1: number }): void {\n const vs = { ...this.viewport.getState(), data: this.getDisplayData() };\n this.setVisibleRange(xToTime(box.x0, vs), xToTime(box.x1, vs));\n }\n\n // --- Bulk drawing operations ---\n\n lockAllDrawings(): void {\n this.drawingManager.lockAllDrawings();\n }\n\n unlockAllDrawings(): void {\n this.drawingManager.unlockAllDrawings();\n }\n\n hideAllDrawings(): void {\n this.drawingManager.hideAllDrawings();\n }\n\n showAllDrawings(): void {\n this.drawingManager.showAllDrawings();\n }\n\n // --- Drawing duplication ---\n\n duplicateDrawing(id?: string): string | null {\n const targetId = id ?? this.drawingManager.getSelectedDrawingId();\n if (!targetId) return null;\n return this.drawingManager.duplicateDrawing(targetId);\n }\n\n // --- Data export ---\n\n /** Does nothing when `features.dataExport` is false. */\n /**\n * Download the bars on screen as CSV or JSON, each indicator line in a\n * column of its own (`indicators: false` leaves them out). Does nothing\n * when `features.dataExport` is false.\n */\n exportVisibleData(format: 'csv' | 'json' = 'csv', filename?: string, options: { indicators?: boolean } = {}): void {\n if (!this.features.dataExport) return;\n this.download(format, filename, { ...options, range: 'visible' });\n }\n\n /** `exportVisibleData` for every bar loaded. Does nothing when `features.dataExport` is false. */\n exportAllData(format: 'csv' | 'json' = 'csv', filename?: string, options: { indicators?: boolean } = {}): void {\n if (!this.features.dataExport) return;\n this.download(format, filename, { ...options, range: 'all' });\n }\n\n /**\n * The bars and their indicator lines, one column per line named as the\n * legend names it (\"SMA 20\", \"MACD 12 26 9 Signal\"): every bar loaded, or\n * those on screen (`range: 'visible'`).\n */\n getExportData(options: { range?: 'all' | 'visible'; indicators?: boolean } = {}): { bars: OHLCBar[]; columns: ExportColumn[] } {\n const data = this.exportBars();\n let from = 0;\n let to = data.length - 1;\n if (options.range === 'visible') {\n const { visibleRange } = this.viewport.getState();\n from = Math.max(0, visibleRange.from);\n to = Math.min(data.length - 1, visibleRange.to);\n }\n const bars = data.slice(from, to + 1);\n if (options.indicators === false) return { bars, columns: [] };\n const columns: ExportColumn[] = [];\n for (const ind of this.indicatorEngine.getActiveIndicators()) {\n const series = this.indicatorEngine.getOutput(ind.instanceId)?.series;\n if (!series) continue;\n const name = indicatorChipLabel(ind.id, ind.params, ind.descriptor.defaultConfig, ind.descriptor.shortName);\n const plots = ind.descriptor.plots ?? Object.keys(series.find((p) => p) ?? {}).map((key) => ({ key, title: key }));\n for (const plot of plots) {\n const title = plots.length > 1 ? ` ${plot.title ?? plot.key}` : '';\n columns.push({ name: `${name}${title}`, values: bars.map((_, i) => series[from + i]?.[plot.key] ?? null) });\n }\n }\n // Two alike (an SMA 20 on the close and one on RSI) get a number each, so no column is lost.\n const seen = new Map<string, number>();\n for (const column of columns) {\n const count = (seen.get(column.name) ?? 0) + 1;\n seen.set(column.name, count);\n if (count > 1) column.name = `${column.name} (${count})`;\n }\n return { bars, columns };\n }\n\n /** `getExportData` as CSV or JSON text. */\n getExportText(format: 'csv' | 'json' = 'csv', options: { range?: 'all' | 'visible'; indicators?: boolean } = {}): string {\n const { bars, columns } = this.getExportData(options);\n return format === 'json' ? DataExporter.toJSON(bars, columns) : DataExporter.toCSV(bars, columns);\n }\n\n private download(format: 'csv' | 'json', filename: string | undefined, options: { range: 'all' | 'visible'; indicators?: boolean }): void {\n const text = this.getExportText(format, options);\n if (format === 'json') DataExporter.download(text, filename ?? 'chart-data.json', 'application/json');\n else DataExporter.download(text, filename ?? 'chart-data.csv', 'text/csv');\n }\n\n // --- Auto-save ---\n\n /**\n * Save the layout to `localStorage[key]` by itself, as `saveState(key)`\n * does: `delayMs` (above 0) after the last change to what it holds (a\n * drawing added, moved or removed, an indicator, an alert, the chart type,\n * the theme, a style override), the changes of that time at once.\n * `loadStateFromStorage(key)` brings it back; `disableAutoSave()` stops it,\n * as `destroy()` does: a change in the last `delayMs` before it is not\n * saved (`saveState(key)` first, to keep it).\n */\n setAutoSave(key: string, delayMs = 5000): void {\n this.autoSaveScheduler.enable(key, delayMs);\n }\n\n disableAutoSave(): void {\n this.autoSaveScheduler.disable();\n }\n\n private scheduleAutoSave(): void {\n this.autoSaveScheduler.schedule();\n }\n\n /** Something a saved layout holds changed: auto-save it, and say so. */\n private markStateChanged(): void {\n this.scheduleAutoSave();\n this.announceStateChange();\n }\n\n private announceStateChange(): void {\n if (this.eventBus.hasListeners('stateChange')) this.eventBus.emit('stateChange', {});\n }\n\n // --- Indicator introspection ---\n\n getAvailableIndicators(): IndicatorDescriptor[] {\n return this.indicatorEngine.getAvailableIndicators();\n }\n\n getIndicatorInputs(id: string): { id: string; params: Record<string, unknown> } | null {\n const indicators = this.indicatorEngine.getAvailableIndicators();\n const desc = indicators.find(i => i.id === id);\n if (!desc) return null;\n return { id: desc.id, params: desc.defaultConfig };\n }\n\n /** Get all active indicator instances with their current params */\n getActiveIndicators(): import('@tradecanvas/core').ActiveIndicatorInfo[] {\n return this.indicatorEngine.getActiveIndicators();\n }\n\n /** Show or hide an indicator without removing it. */\n setIndicatorVisible(instanceId: string, visible: boolean): void {\n this.indicatorHistory.record(instanceId, () => this.setIndicatorVisibleNow(instanceId, visible));\n }\n\n private setIndicatorVisibleNow(instanceId: string, visible: boolean): void {\n if (this.indicatorEngine.setVisible(instanceId, visible) !== null) {\n this.updateViewportAndRender();\n this.markStateChanged();\n this.eventBus.emit('indicatorChange', { instanceId, change: 'visible' });\n }\n }\n\n /**\n * The intervals an indicator shows on (null: all of them): `{ minutes:\n * false }` hides it on every minute chart, `{ hours: [1, 4] }` shows it on\n * 1h to 4h only. The chart reads them against the interval of the stream\n * it is connected to, or the one `setDataTimeframe` gave; with neither it\n * shows the indicator.\n */\n setIndicatorIntervals(instanceId: string, intervals: IndicatorIntervals | null): void {\n this.indicatorHistory.record(instanceId, () => this.setIndicatorIntervalsNow(instanceId, intervals));\n }\n\n private setIndicatorIntervalsNow(instanceId: string, intervals: IndicatorIntervals | null): void {\n if (!this.indicatorEngine.setIntervals(instanceId, intervals)) return;\n this.updateViewportAndRender();\n this.markStateChanged();\n this.eventBus.emit('indicatorChange', { instanceId, change: 'intervals' });\n }\n\n /** The intervals an indicator shows on; null when all of them. */\n getIndicatorIntervals(instanceId: string): IndicatorIntervals | null {\n return this.indicatorEngine.getIntervals(instanceId);\n }\n\n /** Get current config for a specific indicator instance */\n getIndicatorConfig(instanceId: string): { id: string; params: Record<string, unknown> } | null {\n const config = this.indicatorEngine.getIndicatorConfig(instanceId);\n if (!config) return null;\n return { id: config.id, params: { ...config.params } };\n }\n\n /**\n * An indicator's colours, line widths and opacity, and each plot's own\n * dash and visibility (`plots`, by plot key, merged into what it had).\n */\n updateIndicatorStyle(instanceId: string, style: IndicatorStyleConfig): void {\n this.indicatorHistory.record(instanceId, () => this.updateIndicatorStyleNow(instanceId, style));\n }\n\n private updateIndicatorStyleNow(instanceId: string, style: IndicatorStyleConfig): void {\n const { plots, ...rest } = style;\n const read = plots !== undefined ? readIndicatorPlotStyles(plots) : undefined;\n this.indicatorEngine.updateIndicatorStyle(instanceId, { ...rest, ...(read ? { plots: read } : {}) });\n this.engine.requestRender();\n this.markStateChanged();\n this.eventBus.emit('indicatorChange', { instanceId, change: 'style' });\n }\n\n // --- Trading ---\n\n setOrders(orders: TradingOrder[]): void {\n if (!this.features.trading) return;\n this.tradingManager.setOrders(orders);\n this.eventBus.emit('ordersChange', { orders });\n }\n\n /**\n * Begin placing a draggable bracket order (entry + stop-loss + take-profit).\n * Defaults the entry to the latest close when `entry` is omitted. Drag the\n * three lines to adjust; confirm with Enter (emits `bracketPlace`) or cancel\n * with Esc. Returns false if trading is disabled or no price is available.\n */\n startBracket(side: import('@tradecanvas/commons').OrderSide, entry?: number): boolean {\n if (!this.features.trading) return false;\n const data = this.series.bars();\n const price = entry ?? (data.length > 0 ? data[data.length - 1].close : null);\n if (price === null) return false;\n this.tradingManager.startBracket(side, price);\n return true;\n }\n\n cancelBracket(): void {\n if (this.features.trading) this.tradingManager.cancelBracket();\n }\n\n confirmBracket(): boolean {\n return this.features.trading ? this.tradingManager.confirmBracket() : false;\n }\n\n isBracketActive(): boolean {\n return this.features.trading && this.tradingManager.isBracketActive();\n }\n\n /**\n * Begin a drag-to-create single order at `price` (default: latest close) for\n * `side`. Drag the line to a level — the order type (limit/stop) is inferred\n * from the level vs the current price. Confirm with `confirmOrderDraft()`\n * (emits `orderPlace`) or cancel with `cancelOrderDraft()`. Returns false if\n * trading is disabled or no price is available.\n */\n startOrderDraft(side: import('@tradecanvas/commons').OrderSide, price?: number): boolean {\n if (!this.features.trading) return false;\n const data = this.series.bars();\n const p = price ?? (data.length > 0 ? data[data.length - 1].close : null);\n if (p === null) return false;\n this.tradingManager.startOrderDraft(side, p);\n return true;\n }\n\n cancelOrderDraft(): void {\n if (this.features.trading) this.tradingManager.cancelOrderDraft();\n }\n\n confirmOrderDraft(): boolean {\n return this.features.trading ? this.tradingManager.confirmOrderDraft() : false;\n }\n\n isOrderDraftActive(): boolean {\n return this.features.trading && this.tradingManager.isOrderDraftActive();\n }\n\n /**\n * Emit an `orderPlace` intent (e.g. from a depth-ladder click). The chart\n * does not create the order itself — the host listens via\n * `chart.on('orderPlace', …)` and submits to its OMS.\n */\n placeOrderIntent(intent: import('@tradecanvas/commons').OrderPlaceIntent): void {\n if (!this.features.trading) return;\n this.eventBus.emit('orderPlace', intent);\n }\n\n setPositions(positions: TradingPosition[]): void {\n if (!this.features.trading) return;\n this.tradingManager.setPositions(positions);\n this.eventBus.emit('positionsChange', { positions });\n }\n\n /** The working orders on the chart (`setOrders`, or the execution adapter's). */\n getOrders(): TradingOrder[] {\n return this.tradingManager.getOrders();\n }\n\n /** The open positions on the chart (`setPositions`, or the execution adapter's). */\n getPositions(): TradingPosition[] {\n return this.tradingManager.getPositions();\n }\n\n /** Fills marked on the chart, oldest first: the execution adapter's, or `addFill`'s. */\n getFills(): import('@tradecanvas/commons').FillEvent[] {\n return this.tradingManager.getFills();\n }\n\n /** The P&L the fills realised since the last `clearFills`, all of them (the marks keep the latest 1000). */\n getRealisedPnl(): number {\n return this.tradingManager.getRealisedPnl();\n }\n\n /** Mark a fill on the chart (without an execution adapter, a host reports its own). */\n addFill(fill: import('@tradecanvas/commons').FillEvent): void {\n if (!this.features.trading) return;\n this.tradingManager.addFill(fill);\n this.eventBus.emit('executionFill', fill);\n }\n\n clearFills(): void {\n this.tradingManager.setFills([]);\n }\n\n /**\n * Ask for an order to be cancelled: emits `orderCancel`, which an execution\n * adapter carries out (as the × on the order's line does).\n */\n cancelOrderIntent(orderId: string): void {\n if (this.features.trading) this.eventBus.emit('orderCancel', { orderId });\n }\n\n /** Ask for a position to be closed: emits `positionClose`. */\n closePositionIntent(positionId: string): void {\n if (this.features.trading) this.eventBus.emit('positionClose', { positionId });\n }\n\n /** Ask for a position to be reversed (closed, then the same size the other way): emits `positionReverse`. */\n reversePositionIntent(positionId: string): void {\n if (this.features.trading) this.eventBus.emit('positionReverse', { positionId });\n }\n\n /** Ask for a position's stop-loss or take-profit to change (null removes it): emits `positionModify`. */\n modifyPositionIntent(intent: import('@tradecanvas/commons').PositionModifyIntent): void {\n if (this.features.trading) this.eventBus.emit('positionModify', intent);\n }\n\n setDepthData(depth: DepthData | null): void {\n if (!this.features.trading) return;\n this.tradingManager.setDepthData(depth);\n }\n\n setCurrentPrice(price: number, _pulseColor?: string): void {\n this.followLivePrice(price);\n if (this.replaySession) {\n // During a replay the price line shows the replayed close; the live\n // price comes back with replayStop(). Orders and alerts stay live.\n this.replaySession.price = { price };\n } else {\n // Also update standalone price line (visible even without trading feature)\n this.currentPriceLine.setPrice(price);\n this.scheduleRender();\n }\n }\n\n /** Whether a paper account trades on the replay (it takes a mark price) rather than on the live price. */\n private replayTrades(): boolean {\n return this.replaySession !== null && this.executionAdapter?.setMarkPrice !== undefined;\n }\n\n /**\n * A price from the host's own feed: what follows the live price, and the\n * price line (during a replay, kept for when it ends).\n */\n private takeLivePrice(price: number): void {\n this.followLivePrice(price);\n if (this.replaySession) this.replaySession.price = { price };\n else this.currentPriceLine.setPrice(price);\n }\n\n /** `dataUpdate` for the bars: the series is `length` bars now. */\n private emitBars(length: number): void {\n this.eventBus.emit('dataUpdate', { kind: 'bars', length } satisfies DataUpdatePayload);\n // A mirrored crosshair stays on its slot: the legend reads the bar there now, not the old series'.\n const synced = this.crosshairHandler.getSyncedSlot();\n if (synced !== null) this.showSyncedBar(synced);\n }\n\n /** The orders and the alerts follow a live price (a paper account in a replay follows the replay). */\n private followLivePrice(price: number): void {\n if (!this.replayTrades()) this.tradingManager.setCurrentPrice(price);\n if (this.features.alerts) this.checkAlerts(price);\n }\n\n /**\n * Alerts against a live price, and against the indicator lines they watch,\n * all as of the same moment (a line crossing another compares like with\n * like). During a replay they keep watching the live market: the bars are\n * the live ones, and indicator lines — computed on the replayed bars — wait.\n */\n private checkAlerts(price: number): void {\n const live = this.replaySession ? this.series.liveBars() : this.series.bars();\n const lines = this.replaySession || this.series.length() === 0 ? null : (id: string) => this.indicatorEngine.getOutput(id)?.series;\n checkAlerts(this.alertManager, { price, barTime: live.length > 0 ? live[live.length - 1].time : null, lines });\n }\n\n setTradingConfig(config: Partial<TradingConfig>): void {\n if (!this.features.trading) return;\n this.tradingManager.setConfig(config);\n }\n\n // --- Real-time Streaming ---\n\n /**\n * Connect to a real-time data source.\n * Loads history, starts streaming, manages reconnection automatically.\n *\n * @example\n * import { BinanceAdapter } from '@tradecanvas/core';\n * chart.connect({\n * adapter: new BinanceAdapter(),\n * symbol: 'BTCUSDT',\n * timeframe: '1m',\n * });\n *\n * Or from a feed the chart pulls from (`DataFeed`, `ManualFeed`):\n * `chart.connect({ feed, symbol: 'AAPL', timeframe: '1d' })`.\n * Throws when the config has both an adapter and a feed, or neither.\n */\n async connect(options: StreamConfig): Promise<void> {\n // One adapter for the whole connection: a feed's is made once, here.\n const config: StreamConfig & { adapter: DataAdapter } = { ...options, adapter: streamAdapterOf(options), feed: undefined };\n if (!servesTimeframe(config.adapter, config.timeframe)) {\n throw new RangeError(`${config.adapter.name} cannot serve the ${config.timeframe} timeframe`);\n }\n this.snapshotKey = null;\n // A replay waits for the new series and goes on over it (a failed load\n // keeps it waiting for the reconnect's; replayStop() brings the live one back).\n this.holdReplayFor(config.symbol);\n this.disconnectStream();\n\n this.streamManager = new StreamManager();\n this.streamManager.setTimeZone(this.studyZoneHeld ? this.studyZone : this.getExchangeTimeZone());\n\n this.streamManager.on('snapshot', (bars) => {\n const key = this.streamSeriesKey(config.adapter);\n if (key === this.snapshotKey && this.replaySession) {\n this.series.mergeSnapshot(bars); // a reconnect during a replay: into the live series kept aside\n } else if (key === this.snapshotKey && this.series.length() > 0) {\n this.mergeSnapshot(bars); // a reconnect: keep the paged-in history and the view\n } else {\n // A replay older than the new bars pages back as they land: the stream's pages must be there by then.\n if (this.replaySession) this.useStreamHistory(config.adapter, config.historyPageSize, false);\n this.setData(bars);\n }\n this.snapshotKey = key;\n this.useStreamHistory(config.adapter, config.historyPageSize);\n });\n\n // Each bar lands by its time: the last bar again (its latest values, a\n // close included), a new one after it, or none when it is older. A\n // closed one older than the last is a correction of the bar loaded at\n // its time: final, even when the host's own bars run ahead of the stream.\n this.streamManager.on('barClose', (bar) => {\n if (!isValidBar(bar)) return;\n const write = this.series.placeStreamBar(bar);\n if (write.to === 'none') {\n this.updateBar(bar);\n return;\n }\n // Its final close is a live price the stream's priceChange doesn't send.\n if (write.to === 'aside') this.followLivePrice(bar.close);\n if (write.to !== 'shown') return;\n const follow = this.autoScrollOnNewBar && this.viewport.isAtEnd();\n const before = write.before;\n const data = this.series.bars();\n this.crosshairHandler.setData(data);\n this.recalcIndicatorsFrom(data, data.length - 2);\n this.updateViewportAndRender(follow);\n if (data.length !== before) this.emitBars(data.length);\n this.followLivePrice(bar.close);\n });\n\n // The feed's last word on a bar already past: no live price.\n this.streamManager.on('barCorrection', (bar) => {\n this.updateBar(bar);\n });\n\n this.streamManager.on('barUpdate', (bar) => {\n if (!isValidBar(bar)) return;\n const write = this.series.placeStreamBar(bar);\n // A frame of a bar older than the last is stale: it moves nothing, the price line included.\n if (write.to === 'none') return;\n if (!this.replaySession) this.currentPriceLine.setPrice(bar.close);\n // During a replay the bars shown are the replay's: nothing to draw again.\n if (this.replaySession) return;\n const before = write.before;\n if (write.to !== 'shown') {\n this.scheduleRender();\n return;\n }\n // Indicators follow the forming bar instead of freezing until it\n // closes — incrementally, since only the last bar changed, and once\n // before the next read (the frame, an alert, the legend) for all the\n // ticks that came since, not once a tick.\n const data = this.series.bars();\n this.indicatorEngine.deferRecalculateFrom(data, data.length - 1);\n this.announceIndicatorUpdate(data.length - 1);\n // A new bar comes forming first (most feeds): the series grew. The price follows from priceChange.\n if (data.length !== before) this.emitBars(data.length);\n this.scheduleRender();\n });\n\n this.setBidAsk(null); // the last symbol's quote\n // A tick may carry one side only: the other stays.\n this.streamManager.on('quote', (quote) => {\n const now = this.priceLines.getBidAsk();\n this.setBidAsk({ bid: quote.bid ?? now?.bid, ask: quote.ask ?? now?.ask });\n });\n\n this.streamManager.on('priceChange', ({ price, previousClose }) => {\n if (!Number.isFinite(price) || price < 0) return;\n // Orders and alerts keep tracking the live market during a replay; the\n // price line shows the replayed close instead.\n this.followLivePrice(price);\n if (this.replaySession) {\n this.replaySession.price = { price, previousClose: previousClose ?? undefined };\n return;\n }\n this.currentPriceLine.setPrice(price, previousClose ?? undefined);\n this.engine.requestRender(LayerType.Overlay);\n });\n\n this.streamManager.on('connectionChange', (info) => {\n this.eventBus.emit('dataUpdate', { kind: 'connection', connection: info } satisfies DataUpdatePayload);\n });\n\n this.streamManager.on('error', (err) => {\n this.eventBus.emit('dataUpdate', { kind: 'error', error: err.message } satisfies DataUpdatePayload);\n });\n\n // The feed learned more about the symbol (after it was asked, or later): this chart's, it applies.\n this.streamManager.on('symbolInfo', (info) => this.applyFeedSymbolInfo(info));\n\n this.autoScrollOnNewBar = config.autoScroll !== false;\n this.setStreamTarget(config.symbol, config.timeframe);\n this.streamAdapter = config.adapter;\n\n const manager = this.streamManager;\n this.resolveStreamSymbol(config.adapter, config.symbol);\n await manager.connect(config);\n // Superseded by a newer connect() (fast symbol/timeframe switching) or a\n // disconnect while history was loading — leave the countdown to it.\n if (this.streamManager !== manager) return;\n\n // Set up bar countdown timer based on timeframe\n const tfMs = timeframeToMs(config.timeframe);\n this.barCountdown.setTimeframeMs(tfMs);\n\n // Start countdown refresh interval\n if (this.countdownInterval) clearInterval(this.countdownInterval);\n this.countdownInterval = setInterval(() => {\n if (this.barCountdown.isVisible()) {\n this.engine.requestRender(LayerType.Hover);\n }\n }, 1000);\n }\n\n /**\n * The series a stream's history is of: its symbol, its timeframe and the\n * zone whose midnight the days the feed's wrapper builds for it start at.\n * The same series again (a reconnect, or a switch back before the next\n * one's history landed) is merged with the bars on screen; days built in\n * a zone the feed has named since replace them, so two zones' days never mix.\n */\n private streamSeriesKey(feed: DataAdapter): string {\n const adapter = withResampling(feed);\n const timeframe = this.streamTimeframe;\n const days = adapter instanceof ResamplingAdapter && timeframe !== null ? adapter.bucketFor(this.currentSymbol, timeframe) : null;\n return `${this.currentSymbol}|${timeframe}|${days?.timeZone ?? ''}`;\n }\n\n /** The stream's symbol and timeframe, told when they change. */\n private setStreamTarget(symbol: string, timeframe: TimeFrame): void {\n const previousSymbol = this.announcedSymbol;\n this.currentSymbol = symbol;\n this.streamTimeframe = timeframe;\n this.tickStream = tickBarCount(timeframe) !== null;\n this.applyTimeSpacing();\n // The legend's title line names them (a name set with setSymbolName stands until the symbol changes).\n if (symbol !== previousSymbol) this.chartLegend.setSymbol(symbol);\n if (symbol !== previousSymbol) {\n this.announcedSymbol = symbol;\n this.eventBus.emit('symbolChange', { symbol, previous: previousSymbol });\n }\n this.useTimeframe(timeframe);\n }\n\n /**\n * The interval of the bars given with `setData`, where no stream says it:\n * the legend's title line names it, `timeframeChange` reports it, and\n * indicators kept to some intervals follow it. Null: not known (they all\n * show). A connected stream's own interval stands.\n */\n setDataTimeframe(timeframe: TimeFrame | null): void {\n if (this.streamManager) return;\n this.useTimeframe(timeframe);\n this.engine.requestRender(LayerType.Hover);\n }\n\n /** The chart's interval: the legend names it, the indicators kept to some come and go with it. */\n private useTimeframe(timeframe: TimeFrame | null): void {\n const previous = this.announcedTimeframe;\n this.chartLegend.setTimeframe(timeframe ?? '');\n if (this.indicatorEngine.setTimeframe(timeframe)) this.updateViewportAndRender();\n this.drawingManager.setTimeframe(timeframe);\n if (timeframe === previous) return;\n this.announcedTimeframe = timeframe;\n if (timeframe) this.eventBus.emit('timeframeChange', { timeframe, previous });\n }\n\n /**\n * Switch symbol or timeframe on an active stream without full reconnect.\n */\n async switchStream(symbol: string, timeframe: TimeFrame): Promise<void> {\n if (!this.streamManager) return;\n const adapter = this.streamAdapter;\n if (adapter && !servesTimeframe(adapter, timeframe)) {\n throw new RangeError(`${adapter.name} cannot serve the ${timeframe} timeframe`);\n }\n this.holdReplayFor(symbol);\n this.setStreamTarget(symbol, timeframe);\n // Until the new series is in, a page would be the new symbol's bars in\n // front of the old one's.\n if (this.historyFromStream) this.history.setLoader(null);\n this.barCountdown.setTimeframeMs(timeframeToMs(timeframe));\n if (adapter) this.resolveStreamSymbol(adapter, symbol);\n await this.streamManager.switchTo(symbol, timeframe);\n }\n\n /** Whether `addIndicator(id)` may add one: indicators on, and `id` listed when `features.indicatorIds` lists any. */\n isIndicatorAllowed(id: string): boolean {\n const allowed = this.features.indicatorIds;\n return this.features.indicators && (allowed.length === 0 || allowed.includes(id));\n }\n\n /** False when `features.timeframes` is set and does not list `timeframe`. */\n isTimeframeAllowed(timeframe: TimeFrame): boolean {\n const allowed = this.features.timeframes;\n return allowed.length === 0 || allowed.includes(timeframe);\n }\n\n /**\n * Switch to a new timeframe. Requires an active stream connection.\n * Ignored when `features.timeframes` does not list it.\n * Internally calls switchStream with the current symbol.\n */\n async setTimeframe(timeframe: TimeFrame): Promise<void> {\n if (!this.streamManager) throw new Error('No active stream. Call connect() first.');\n if (!this.isTimeframeAllowed(timeframe)) return;\n await this.switchStream(this.currentSymbol, timeframe);\n }\n\n /**\n * Disconnect the real-time stream.\n */\n disconnectStream(): void {\n this.symbolInfoSeq++; // a lookup still on its way no longer applies\n this.releaseStudyZoneAfterCaller();\n this.priceLines.setBidAsk(null);\n if (this.streamManager) {\n this.streamManager.dispose();\n this.streamManager = null;\n }\n if (this.historyFromStream) {\n this.history.setLoader(null);\n this.historyFromStream = false;\n }\n // A replay's finer bars came from this feed: the next one connected brings its own.\n this.replayLoader?.loader.dispose();\n this.replayLoader = null;\n this.streamAdapter = null;\n // Bars set by hand after this aren't tick bars.\n this.tickStream = false;\n this.applyTimeSpacing();\n }\n\n /**\n * Connect an execution adapter, turning the trading overlay into a live\n * trading surface. The adapter is the source of truth: the chart routes its\n * emitted order/position intents into the adapter and renders the\n * `orders` / `positions` the adapter emits back. With no adapter connected,\n * those intents remain plain events (the default). Listen for failures via\n * `chart.on('executionError', …)`.\n */\n connectExecution(adapter: ExecutionAdapter, config: ExecutionConfig = {}): void {\n if (!this.features.trading) {\n throw new Error('connectExecution requires features.trading to be enabled');\n }\n this.disconnectExecution();\n this.executionAdapter = adapter;\n this.execTeardown = wireExecution(adapter, {\n onIntent: (type, handler) => {\n const wrapped = (e: ChartEvent) => handler(e.payload);\n this.eventBus.on(type, wrapped);\n return () => this.eventBus.off(type, wrapped);\n },\n setOrders: (orders) => this.setOrders(orders),\n setPositions: (positions) => this.setPositions(positions),\n onError: (error) => this.eventBus.emit('executionError', error),\n onFill: (fill) => {\n this.tradingManager.addFill(fill);\n this.eventBus.emit('executionFill', fill);\n },\n getPositions: () => this.tradingManager.getPositions(),\n });\n Promise.resolve(adapter.connect(config)).catch((cause) =>\n this.eventBus.emit('executionError', { message: 'Execution connect failed', cause }),\n );\n }\n\n /** Tear down the active execution adapter (if any) and stop routing intents. */\n disconnectExecution(): void {\n this.returnMarkToLive();\n if (this.execTeardown) {\n this.execTeardown();\n this.execTeardown = null;\n }\n if (this.executionAdapter) {\n this.executionAdapter.disconnect();\n this.executionAdapter = null;\n }\n }\n\n /** The connected execution adapter, or null (e.g. for adapter-specific calls). */\n getExecutionAdapter(): ExecutionAdapter | null {\n return this.executionAdapter;\n }\n\n /** Ignored (stays hidden) when `features.barCountdown` is false. */\n setBarCountdownVisible(visible: boolean): void {\n this.barCountdown.setVisible(visible && this.features.barCountdown);\n this.engine.requestRender(LayerType.Hover);\n }\n\n isBarCountdownVisible(): boolean {\n return this.barCountdown.isVisible();\n }\n\n setSessionBreaksVisible(visible: boolean): void {\n this.sessionBreaks.setVisible(visible);\n this.engine.requestRender(LayerType.Background);\n }\n\n // --- Compare symbols ---\n\n /** Does nothing when `features.compareSymbols` is false. */\n addCompareSymbol(id: string, label: string, data: DataSeries, color: string): void {\n if (!this.features.compareSymbols) return;\n this.compareRenderer.addSymbol({ id, label, data, color, visible: true });\n // They count in the auto scale.\n this.updateViewportAndRender();\n this.eventBus.emit('compareChange', { id, change: 'add' });\n }\n\n removeCompareSymbol(id: string): void {\n this.compareRenderer.removeSymbol(id);\n this.updateViewportAndRender();\n this.eventBus.emit('compareChange', { id, change: 'remove' });\n }\n\n updateCompareData(id: string, data: DataSeries): void {\n this.compareRenderer.setSymbolData(id, data);\n this.updateViewportAndRender();\n this.eventBus.emit('compareChange', { id, change: 'data' });\n }\n\n setCompareMode(mode: 'percent' | 'absolute'): void {\n this.compareRenderer.setMode(mode);\n this.updateViewportAndRender();\n }\n\n /** Show or hide a compare line (`addCompareSymbol`'s id); a hidden one leaves the auto scale. */\n setCompareVisible(id: string, visible: boolean): void {\n this.compareRenderer.setSymbolVisible(id, visible);\n this.updateViewportAndRender();\n this.eventBus.emit('compareChange', { id, change: 'visible' });\n }\n\n /**\n * Each compare line at bar `barIndex` (the last bar by default), for a\n * legend: the other symbol's close at or before it, and its change in\n * percent since its first bar on screen, which is what its line reads on\n * a percent price scale (`setScaleMode('percentage')`). Past the compare's\n * last bar (bars you fetched once, the chart streaming on), its last close,\n * with `stale` true. Where the chart draws no compare lines, on Renko,\n * Kagi, line break, point & figure and range bars (`isTimeAligned()`\n * false), each one's close at the latest data bar, whatever `barIndex`,\n * and no change.\n */\n getCompareValues(barIndex?: number): CompareValue[] {\n if (!this.comparesDrawn()) {\n const bars = this.series.bars();\n return this.compareRenderer.valuesAt(bars, null, bars.length - 1);\n }\n const data = this.getDisplayData();\n return this.compareRenderer.valuesAt(data, this.viewport.getState(), barIndex ?? data.length - 1);\n }\n\n /**\n * Compare lines line up with the bars by time: not on the chart types\n * that redraw them (Renko bricks, Kagi lines…), whose times only count\n * them, so a line there would read the other symbol at the wrong bars or\n * at none.\n */\n private comparesDrawn(): boolean {\n return this.isTimeAligned();\n }\n\n /** Take every compare line off, each told as a `compareChange` of `'remove'`. */\n clearCompareSymbols(): void {\n const ids = this.compareRenderer.getSymbols().map((sym) => sym.id);\n this.compareRenderer.clear();\n this.updateViewportAndRender();\n for (const id of ids) this.eventBus.emit('compareChange', { id, change: 'remove' });\n }\n\n // --- Price scale ---\n\n /** Turning it on is ignored when `features.logScale` is false. */\n setLogScale(enabled: boolean): void {\n if (enabled && !this.features.logScale) return;\n this.viewport.setLogScale(enabled);\n this.updateViewportAndRender();\n }\n\n isLogScale(): boolean {\n return this.viewport.isLogScale();\n }\n\n /** Turn the price scale upside down: higher prices lower on screen. */\n setInvertScale(inverted: boolean): void {\n this.viewport.setInvertScale(inverted);\n this.updateViewportAndRender();\n }\n\n isInvertScale(): boolean {\n return this.viewport.isInvertScale();\n }\n\n /**\n * Set the price-scale presentation: `regular`, `logarithmic`, `percentage`\n * (axis labels show % change from the first visible bar), or `indexedTo100`\n * (rebased so the first visible bar reads as 100).\n */\n setScaleMode(mode: import('@tradecanvas/commons').PriceScaleMode): void {\n if (mode === 'logarithmic' && !this.features.logScale) return;\n this.viewport.setScaleMode(mode);\n this.updateViewportAndRender();\n }\n\n getScaleMode(): import('@tradecanvas/commons').PriceScaleMode {\n return this.viewport.getScaleMode();\n }\n\n setSessionBreaksConfig(config: { color?: string; lineStyle?: 'solid' | 'dashed' | 'dotted'; lineWidth?: number }): void {\n this.sessionBreaks.setConfig(config);\n this.engine.requestRender(LayerType.Background);\n }\n\n getConnectionState(): ConnectionState {\n return this.streamManager?.getConnectionState() ?? 'disconnected';\n }\n\n getConnectionInfo(): ConnectionInfo {\n return this.streamManager?.getConnectionInfo() ?? { state: 'disconnected' };\n }\n\n setAutoScroll(enabled: boolean): void {\n this.autoScrollOnNewBar = enabled;\n }\n\n // --- Viewport ---\n\n scrollTo(timestamp: number): void {\n const data = this.series.bars();\n for (let i = 0; i < data.length; i++) {\n if (data[i].time >= timestamp) {\n const barUnit = this.viewport.getState().barWidth + this.viewport.getState().barSpacing;\n this.viewport.scrollBy(i * barUnit - this.viewport.getState().offset);\n this.updateViewportAndRender();\n return;\n }\n }\n }\n\n /**\n * Centre the bar that contains `time` — in the bars' own unit, ms or s —\n * (the first or last bar when `time` lies outside the data), keeping the zoom. Returns that bar's index, or -1\n * without data.\n */\n goToTime(time: number): number {\n const data = this.getDisplayData(); // the series on screen (renko bricks, kagi lines…)\n if (data.length === 0 || !Number.isFinite(time)) return -1;\n const index = Math.max(0, Math.min(data.length - 1, Math.floor(timestampToBarIndex(time, data))));\n const vs = this.viewport.getState();\n const centredOffset = index * (vs.barWidth + vs.barSpacing) + vs.barWidth / 2 - vs.chartRect.width / 2;\n this.viewport.scrollBy(centredOffset - vs.offset);\n this.updateViewportAndRender();\n return index;\n }\n\n /**\n * Show a span that ends at the last bar: 1D, 5D, 1M, 3M, 6M, YTD, 1Y, 5Y or\n * All. Months and YTD follow the calendar in the display timezone. Only\n * loaded bars can be shown, so a span longer than the data shows all of it.\n */\n setVisibleRangePreset(preset: RangePreset): void {\n const data = this.getDisplayData();\n if (data.length === 0) return;\n const last = data.length - 1;\n // Bars may carry seconds rather than milliseconds; the calendar works in ms.\n const unit = barTimeUnit(data);\n const startMs = rangePresetStart(preset, data[last].time * unit, this.displayTz);\n const start = startMs === null ? null : startMs / unit;\n if (start === null || start < data[0].time) {\n this.fitContent();\n return;\n }\n // The first bar after `start`, so \"1D\" on 1-minute bars is 1440 bars, not\n // 1441; YTD starts on the year's first bar, the one at 1 January 00:00.\n const at = timestampToBarIndex(start, data);\n const first = Math.min(last, preset === 'YTD' ? Math.ceil(at) : Math.floor(at) + 1);\n this.viewport.zoomToBarRange(first, last + this.viewport.getRightMargin());\n this.viewport.scrollToEnd();\n this.updateViewportAndRender();\n }\n\n scrollToEnd(): void {\n this.viewport.scrollToEnd();\n this.updateViewportAndRender();\n }\n\n /** Scroll by `bars` bars: later for a positive count, earlier for a negative one. */\n scrollBars(bars: number): void {\n if (!Number.isFinite(bars) || bars === 0) return;\n const { barWidth, barSpacing } = this.viewport.getState();\n this.viewport.scrollBy(bars * (barWidth + barSpacing));\n this.updateViewportAndRender();\n }\n\n /**\n * The times of the first and last bars on screen, in the bars' own unit:\n * of the series drawn (a Renko chart's bricks, not the bars under them).\n * Null without data.\n */\n getVisibleTimeRange(): { from: number; to: number } | null {\n const data = this.getDisplayData();\n if (data.length === 0) return null;\n // The bars whose middles are inside the plot (the viewport's range keeps a bar more each side to draw).\n const { offset, barWidth, barSpacing, chartRect } = this.viewport.getState();\n const unit = barWidth + barSpacing;\n if (unit <= 0) return null;\n const first = Math.ceil((offset - barWidth / 2) / unit);\n const last = Math.floor((offset + chartRect.width - barWidth / 2) / unit);\n const at = (index: number) => data[Math.max(0, Math.min(data.length - 1, index))].time;\n return { from: at(first), to: at(Math.max(first, last)) };\n }\n\n /**\n * Show the bars between two timestamps edge to edge. Times past either end\n * of the data are allowed (they map into the empty future / past).\n */\n setVisibleRange(fromTimestamp: number, toTimestamp: number): void {\n // The series on screen: bricks, not the bars under them, on Renko and the like.\n const data = this.getDisplayData();\n if (data.length === 0) return;\n const a = timestampToBarIndex(Math.min(fromTimestamp, toTimestamp), data);\n const b = timestampToBarIndex(Math.max(fromTimestamp, toTimestamp), data);\n // Previously this only changed the bar width around the centre, so the\n // requested range was not actually what ended up on screen.\n this.viewport.zoomToBarRange(a, b);\n this.updateViewportAndRender();\n }\n\n /**\n * Pause drawing, for a host that knows better than the page: a WebView\n * whose app went to the background, a tab of its own UI out of view. Data,\n * indicators and alerts keep up; `resume()` draws what changed.\n */\n suspend(): void {\n this.setPaused('host', true);\n }\n\n resume(): void {\n this.setPaused('host', false);\n }\n\n /** Whether the chart isn't drawing now: suspended, scrolled off screen, or on a hidden page. */\n isSuspended(): boolean {\n return this.pausedFor.host || this.pausedFor.page || this.pausedFor.offscreen;\n }\n\n private setPaused(reason: keyof Chart['pausedFor'], on: boolean): void {\n const was = this.isSuspended();\n this.pausedFor = { ...this.pausedFor, [reason]: on };\n const now = this.isSuspended();\n if (was === now || this.disposed) return;\n if (now) {\n this.engine.renderLoop.pause();\n return;\n }\n if (this.renderDeferred) {\n this.renderDeferred = false;\n this.scheduleRender();\n }\n this.engine.renderLoop.resume();\n }\n\n /**\n * With `pause`, stop drawing while the chart is off screen or the page\n * hidden: no frames for no one, which on a phone is battery, and in a page\n * of many charts, the frames of those in view. Either way, a page back in\n * view or a network back wakes the stream (a phone asleep leaves a dead\n * socket behind).\n */\n private watchVisibility(container: HTMLElement, pause: boolean): void {\n const stops: (() => void)[] = [];\n if (pause && typeof IntersectionObserver !== 'undefined') {\n const observer = new IntersectionObserver((entries) => {\n const last = entries[entries.length - 1];\n if (last) this.setPaused('offscreen', !last.isIntersecting);\n });\n observer.observe(container);\n stops.push(() => observer.disconnect());\n }\n if (typeof document !== 'undefined') {\n const onVisibility = () => {\n if (pause) this.setPaused('page', document.hidden);\n if (!document.hidden) this.streamManager?.wake('visible');\n };\n document.addEventListener('visibilitychange', onVisibility);\n stops.push(() => document.removeEventListener('visibilitychange', onVisibility));\n if (pause && document.hidden) this.setPaused('page', true);\n }\n if (typeof window !== 'undefined') {\n const onOnline = () => this.streamManager?.wake('online');\n window.addEventListener('online', onOnline);\n stops.push(() => window.removeEventListener('online', onOnline));\n }\n this.stopWatchingVisibility = () => stops.forEach((stop) => stop());\n }\n\n /**\n * Leave vertical swipes and the plain wheel to the page (see\n * `ChartOptions.pageScroll`); the chart pans sideways and zooms with two\n * fingers or Ctrl + wheel.\n */\n setPageScroll(on: boolean): void {\n this.options.pageScroll = on;\n this.interactionManager.setPageScroll(on);\n }\n\n getPageScroll(): boolean {\n return this.options.pageScroll ?? false;\n }\n\n /**\n * The highest device pixel ratio to draw at (see `ChartOptions.maxPixelRatio`):\n * 2 by default, `Infinity` for the screen's own. The canvases resize at once.\n */\n setMaxPixelRatio(max: number): void {\n this.engine.dprManager.setMaxPixelRatio(max);\n }\n\n getMaxPixelRatio(): number {\n return this.engine.dprManager.getMaxPixelRatio();\n }\n\n /**\n * How narrow a bar slot may get to fit a long series, px (see\n * `ChartOptions.minBarUnit`): 0.25 by default, down to 0.02.\n */\n setMinBarUnit(px: number): void {\n this.viewport.setMinBarUnit(px);\n this.updateViewportAndRender();\n }\n\n getMinBarUnit(): number {\n return this.viewport.getMinBarUnit();\n }\n\n /** Change free-panning bounds at runtime — see `ChartOptions.freePan` / `panLimits`. */\n setPanLimits(limits: { freePan?: boolean; minVisibleBars?: number }): void {\n this.viewport.setPanLimits(limits);\n this.updateViewportAndRender();\n }\n\n zoomIn(): void {\n const chartWidth = this.viewport.getState().chartRect.width;\n this.viewport.zoom(0.2, chartWidth / 2);\n this.updateViewportAndRender();\n }\n\n zoomOut(): void {\n const chartWidth = this.viewport.getState().chartRect.width;\n this.viewport.zoom(-0.2, chartWidth / 2);\n this.updateViewportAndRender();\n }\n\n /**\n * Fit all bars on screen with the usual right margin, resting at the live\n * edge — so new bars keep following afterwards. If they can't all fit at\n * the minimum bar width, the newest ones are shown.\n */\n fitContent(): void {\n const n = this.getDisplayData().length;\n if (n === 0) return;\n this.viewport.zoomToBarRange(0, n - 1 + this.viewport.getRightMargin());\n this.viewport.scrollToEnd();\n this.updateViewportAndRender();\n }\n\n // --- Events ---\n\n on<K extends ChartEventType>(type: K, handler: (event: ChartEvent<K extends keyof ChartEventMap ? ChartEventMap[K] : unknown>) => void): void {\n this.eventBus.on(type, handler as (event: ChartEvent) => void);\n }\n\n off<K extends ChartEventType>(type: K, handler: (event: ChartEvent<K extends keyof ChartEventMap ? ChartEventMap[K] : unknown>) => void): void {\n this.eventBus.off(type, handler as (event: ChartEvent) => void);\n }\n\n enableTauriBridge(options?: Partial<TauriBridgeOptions>): void {\n this.eventBus.enableTauriBridge({ enabled: true, ...options });\n }\n\n disableTauriBridge(): void {\n this.eventBus.disableTauriBridge();\n }\n\n // --- Theme ---\n\n setTheme(themeOrName: ThemeName | Theme): void {\n this.themeManager.setTheme(themeOrName);\n this.syncRenderContext();\n this.applyIdleCursor();\n this.container.style.backgroundColor = this.themeManager.getTheme().background;\n this.lastBackground = this.themeManager.getTheme().background;\n this.engine.requestRender();\n this.eventBus.emit('themeChange', { theme: themeOrName });\n this.markStateChanged();\n }\n\n /**\n * The theme as set. The style overrides go on it apart: what is drawn\n * reads back from `getStyleValue` and `getStyle`.\n */\n getTheme(): Theme {\n return this.themeManager.getBaseTheme();\n }\n\n // --- Style overrides ---\n\n /**\n * Override any part of the chart's look, by key: the grid's lines each way,\n * the crosshair, the axes, the panes, the legend, the last price, the\n * volume, the main series as each chart type draws it, and more\n * (`CHART_STYLE_KEYS` lists them). A value sets a key, `null` takes it away.\n *\n * Candles: `series.candlestick.upColor` and `downColor` colour the bodies\n * and the wicks with them; `wickUpColor` and `wickDownColor` give the\n * wicks colours of their own. The other chart types take their up and\n * down colours from these when they have none of their own.\n *\n * `layer: 'host'` (default) is your app's: kept through theme changes,\n * never saved. `layer: 'user'` is what the person using the chart chose:\n * kept with the theme it was made on and saved with `saveState`. The\n * user's win over the host's. Unknown keys and values are left out with a\n * warning. Emits `styleChange`.\n */\n applyOverrides(patch: ChartStyleOverridesPatch, options: { layer?: StyleLayer } = {}): void {\n this.applyStyle(patch, options.layer ?? 'host', true);\n }\n\n /**\n * `overrides` in place of all of a layer's (the wrappers' `overrides` prop\n * does this): keys left out are taken away.\n */\n setOverrides(overrides: ChartStyleOverrides, options: { layer?: StyleLayer } = {}): void {\n const layer = options.layer ?? 'host';\n const { overrides: read, rejected } = readChartStyleOverrides(overrides);\n if (rejected.length > 0) console.warn(`Style overrides left out (unknown key or value): ${rejected.join(', ')}`);\n const values = Object.fromEntries(Object.entries(read).filter(([, v]) => v !== null)) as ChartStyleOverrides;\n if (this.themeManager.setOverrides(values, layer)) this.styleChanged(layer);\n }\n\n /** Take away `keys` from a layer, or all of its overrides without. */\n resetOverrides(keys?: readonly ChartStyleKey[], options: { layer?: StyleLayer } = {}): void {\n const layer = options.layer ?? 'host';\n if (this.themeManager.resetOverrides(layer, keys)) this.styleChanged(layer);\n }\n\n /** A layer's overrides (the user's: those for the current theme). */\n getOverrides(options: { layer?: StyleLayer } = {}): ChartStyleOverrides {\n return this.themeManager.getOverrides(options.layer ?? 'host');\n }\n\n /** The finer looks as resolved from the theme and the overrides. */\n getStyle(): Readonly<ResolvedChartStyle> {\n return this.themeManager.getTheme().style!;\n }\n\n /** What a key resolves to now: its override, or what it falls back to. */\n getStyleValue(key: ChartStyleKey): string | number | boolean | null {\n return this.themeManager.getStyleValue(key);\n }\n\n private applyStyle(patch: ChartStyleOverridesPatch, layer: StyleLayer, notify: boolean): void {\n const { overrides, rejected } = readChartStyleOverrides(patch);\n if (rejected.length > 0) console.warn(`Style overrides left out (unknown key or value): ${rejected.join(', ')}`);\n if (this.themeManager.applyOverrides(overrides, layer) && notify) this.styleChanged(layer);\n }\n\n private styleChanged(layer: StyleLayer): void {\n this.syncRenderContext();\n this.applyIdleCursor();\n // The container's background only when the chart's changed: a host's own stays otherwise.\n const background = this.themeManager.getTheme().background;\n if (background !== this.lastBackground) {\n this.container.style.backgroundColor = background;\n this.lastBackground = background;\n }\n this.engine.requestRender();\n this.eventBus.emit('styleChange', { layer });\n // The user's are part of the chart's state; the host's are not.\n if (layer === 'user') this.markStateChanged();\n }\n\n // --- Watermark ---\n\n setWatermark(text: string, opts?: { color?: string; fontSize?: number }): void {\n this.watermark.setConfig({ text, ...opts });\n this.engine.requestRender(LayerType.Background);\n }\n\n // --- Auto Scale ---\n\n /**\n * Fit the price axis to the bars on view as they move, or hold the range\n * on screen (`false`), across `setData` too. Off, a new series (the first\n * bars, `setData`'s, a stream's symbol or timeframe switch) is still fitted\n * once, then held; bars that come one by one into an empty chart are\n * fitted until they fill it.\n */\n setAutoScale(enabled: boolean): void {\n // Unlike the drag-to-scale/vertical-pan gesture freeze, an explicit call\n // here is a deliberate preference: it survives the next setData() the\n // same way a constructor-time `autoScale: false` would.\n this.priceFit.setAuto(enabled, this.series.length() > 0);\n this.updateViewportAndRender();\n }\n\n /** A drag of the price axis or a vertical pan: the range on screen is the user's, held from here. */\n private holdPriceScale(): void {\n this.priceFit.hold();\n }\n\n isAutoScale(): boolean {\n return this.priceFit.isAuto();\n }\n\n // --- Crosshair ---\n\n setCrosshairMode(mode: 'normal' | 'magnet' | 'hidden'): void {\n this.crosshairHandler.setMode(mode);\n this.engine.requestRender(LayerType.Hover);\n }\n\n getCrosshairMode(): string {\n return this.crosshairHandler.getMode();\n }\n\n /**\n * What the pointer looks like over the chart: `'cross'` (the crosshair's\n * lines, a crosshair cursor), `'dot'` (the lines, a small dot for a cursor)\n * or `'arrow'` (no lines, the arrow cursor; the price and the time still\n * follow the pointer on the axes). Emits `crosshairCursorChange`.\n */\n setCrosshairCursor(cursor: CrosshairCursor): void {\n if (!isCrosshairCursor(cursor) || cursor === this.crosshairHandler.getCursor()) return;\n this.crosshairHandler.setCursor(cursor);\n this.applyIdleCursor();\n this.engine.requestRender(LayerType.Hover);\n this.eventBus.emit('crosshairCursorChange', { cursor });\n }\n\n getCrosshairCursor(): CrosshairCursor {\n return this.crosshairHandler.getCursor();\n }\n\n /** The CSS cursor over the plot for the cursor kind, in the crosshair's colour as now resolved. */\n private applyIdleCursor(): void {\n const theme = this.themeManager.getTheme();\n const color = theme.style?.crosshair.vertical.color ?? theme.crosshair;\n this.interactionManager.setIdleCursor(idleCssCursor(this.crosshairHandler.getCursor(), color, theme.background));\n }\n\n setCrosshairPosition(point: { x: number; y: number } | null): void {\n if (point) {\n this.crosshairHandler.onPointerMove(point);\n } else {\n this.crosshairHandler.onPointerLeave();\n }\n this.engine.requestRender(LayerType.Hover);\n }\n\n /**\n * Show a time-only crosshair — a vertical line and time label on the bar\n * that contains `time` — as when mirroring another chart; null clears it.\n * The legend reads that bar, and `crosshairSync` says which one; no\n * `crosshairMove` fires, so linked charts don't echo each other.\n */\n setCrosshairTime(time: number | null): void {\n const data = this.getDisplayData();\n const slot = time === null || data.length === 0 ? null : Math.floor(timestampToBarIndex(time, data));\n if (slot === this.crosshairHandler.getSyncedSlot()) return;\n this.crosshairHandler.setSyncedSlot(slot);\n this.showSyncedBar(slot);\n this.engine.requestRender(LayerType.Hover);\n }\n\n /**\n * The legend follows a mirrored crosshair as it follows the pointer, or the\n * other charts of a grid keep showing their latest bar. The pointer's own\n * crosshair, when there is one here, keeps the legend.\n */\n private showSyncedBar(slot: number | null): void {\n if (slot === null) {\n if (!this.crosshairHandler.getPosition()) this.chartLegend.setHoverBar(null);\n this.eventBus.emit('crosshairSync', { barIndex: null });\n return;\n }\n const series = this.crosshairSeries();\n const bar = slot >= 0 && slot < series.length ? series[slot] : undefined;\n const prev = bar && slot > 0 ? series[slot - 1] : undefined;\n if (!this.crosshairHandler.getPosition()) this.chartLegend.setHoverBar(bar ?? null, prev ?? null);\n const time = bar ? axisTimes(series)[slot]?.time ?? bar.time : undefined;\n this.eventBus.emit('crosshairSync', {\n barIndex: slot,\n ...(bar ? { bar } : {}),\n ...(time !== undefined ? { time } : {}),\n ...(prev ? { prevClose: prev.close } : {}),\n });\n }\n\n getData(): DataSeries {\n return this.series.bars();\n }\n\n // --- Grid ---\n\n setGridVisible(visible: boolean): void {\n this.gridRenderer.setVisible(visible);\n this.engine.requestRender(LayerType.Background);\n }\n\n isGridVisible(): boolean {\n return this.gridRenderer.isVisible();\n }\n\n // --- Volume ---\n\n setVolumeVisible(visible: boolean): void {\n this.volumeRenderer.setVisible(visible);\n // The scale gives the band back to the candles, or takes it again.\n this.updateViewportAndRender();\n }\n\n /** Whether the volume bars are drawn: switched on, and `features.volume` not off. */\n isVolumeVisible(): boolean {\n return this.features.volume && this.volumeRenderer.isVisible();\n }\n\n // --- Volume Profile ---\n\n setVolumeProfileVisible(visible: boolean): void {\n this.volumeProfile.setVisible(visible);\n this.engine.requestRender(LayerType.Main);\n }\n\n isVolumeProfileVisible(): boolean {\n return this.volumeProfile.isVisible();\n }\n\n setVolumeProfileConfig(config: { buckets?: number; widthRatio?: number; opacity?: number; highlightPoC?: boolean }): void {\n if (config.buckets !== undefined) this.volumeProfile.setBuckets(config.buckets);\n if (config.widthRatio !== undefined) this.volumeProfile.setWidthRatio(config.widthRatio);\n if (config.opacity !== undefined) this.volumeProfile.setOpacity(config.opacity);\n if (config.highlightPoC !== undefined) this.volumeProfile.setHighlightPoC(config.highlightPoC);\n this.engine.requestRender(LayerType.Main);\n }\n\n // --- Market Profile (TPO) ---\n\n setMarketProfileVisible(visible: boolean): void {\n this.marketProfile.setVisible(visible);\n this.engine.requestRender(LayerType.Main);\n }\n\n isMarketProfileVisible(): boolean {\n return this.marketProfile.isVisible();\n }\n\n setMarketProfileConfig(config: { buckets?: number; widthRatio?: number; opacity?: number; valueAreaPct?: number; highlightPoC?: boolean; showStats?: boolean; splitBySession?: boolean; letters?: boolean }): void {\n if (config.buckets !== undefined) this.marketProfile.setBuckets(config.buckets);\n if (config.widthRatio !== undefined) this.marketProfile.setWidthRatio(config.widthRatio);\n if (config.opacity !== undefined) this.marketProfile.setOpacity(config.opacity);\n if (config.valueAreaPct !== undefined) this.marketProfile.setValueAreaPct(config.valueAreaPct);\n if (config.highlightPoC !== undefined) this.marketProfile.setHighlightPoC(config.highlightPoC);\n if (config.showStats !== undefined) this.marketProfile.setShowStats(config.showStats);\n if (config.splitBySession !== undefined) this.marketProfile.setSplitBySession(config.splitBySession);\n if (config.letters !== undefined) this.marketProfile.setLetters(config.letters);\n this.engine.requestRender(LayerType.Main);\n }\n\n /** How the Market Profile is drawn, as `setMarketProfileConfig` left it. */\n getMarketProfileConfig(): import('@tradecanvas/core').MarketProfileConfig {\n return this.marketProfile.getConfig();\n }\n\n /** POC / VAH / VAL of the currently rendered Market Profile, or null. */\n getMarketProfileStats(): import('@tradecanvas/core').MarketProfileStats | null {\n return this.marketProfile.getStats();\n }\n\n // --- Liquidity heatmap ---\n\n setDepthHeatmapVisible(visible: boolean): void {\n this.depthHeatmap.setVisible(visible);\n this.engine.requestRender(LayerType.Main);\n }\n\n isDepthHeatmapVisible(): boolean {\n return this.depthHeatmap.isVisible();\n }\n\n setDepthHeatmapConfig(config: { opacity?: number; capacity?: number }): void {\n if (config.opacity !== undefined) this.depthHeatmap.setOpacity(config.opacity);\n if (config.capacity !== undefined) this.depthHeatmap.setCapacity(config.capacity);\n this.engine.requestRender(LayerType.Main);\n }\n\n getDepthHeatmapConfig(): { opacity: number; capacity: number } {\n return this.depthHeatmap.getConfig();\n }\n\n /**\n * Record an order-book snapshot for the liquidity heatmap, stamped at\n * `time`, or else at the latest bar's time. Call alongside `setDepthData`\n * on each book update; pass `time` to fill in books recorded for older\n * bars, oldest first.\n */\n pushDepthSnapshot(depth: import('@tradecanvas/commons').DepthData, time?: number): void {\n const data = this.series.bars();\n const at = time ?? data[data.length - 1]?.time;\n if (at === undefined || !Number.isFinite(at)) return;\n this.depthHeatmap.push(at, depth);\n if (this.depthHeatmap.isVisible()) this.engine.requestRender(LayerType.Main);\n }\n\n clearDepthHeatmap(): void {\n this.depthHeatmap.clear();\n this.engine.requestRender(LayerType.Main);\n }\n\n // --- Prior-period levels (PDH / PDL / PDC) ---\n\n setPeriodLevelsVisible(visible: boolean): void {\n this.periodLevels.setVisible(visible);\n this.engine.requestRender(LayerType.Main);\n }\n\n isPeriodLevelsVisible(): boolean {\n return this.periodLevels.isVisible();\n }\n\n setPeriodLevelsPeriod(period: import('@tradecanvas/core').LevelPeriod): void {\n this.periodLevels.setPeriod(period);\n this.engine.requestRender(LayerType.Main);\n }\n\n getPeriodLevelsPeriod(): import('@tradecanvas/core').LevelPeriod {\n return this.periodLevels.getPeriod();\n }\n\n /**\n * The time zone for the time axis, crosshair, tooltip, day breaks and range\n * presets: an IANA zone (`'America/New_York'`, daylight saving included), a\n * fixed offset in minutes east of UTC (`-300`, `330`), `'exchange'` for the\n * symbol's zone (`setSymbolInfo`), else its market's (`setMarket`), else\n * UTC, or null for the browser's. Throws a RangeError for a zone the\n * browser doesn't know. The `timeZone` option sets it at the start.\n *\n * @example\n * chart.setTimeZone('America/New_York'); // New York's time, daylight saving included\n * chart.setTimeZone(330); // UTC+5:30, in minutes\n * chart.setTimeZone('exchange'); // the symbol's zone, else its market's\n * chart.setTimeZone(null); // the browser's\n */\n setTimeZone(tz: TimeZoneSetting): void {\n assertTimeZone(tz);\n this.timeZoneSetting = tz;\n this.applyTimeZone();\n }\n\n /** What `setTimeZone` was given — `'exchange'` included. */\n getTimeZone(): TimeZoneSetting {\n return this.timeZoneSetting;\n }\n\n /** The zone the chart shows: `'exchange'` resolved to the symbol's zone, else its market's (UTC when neither names one). */\n getEffectiveTimeZone(): TimeZoneSetting {\n return this.displayTz;\n }\n\n /**\n * The zone `'exchange'` stands for: the symbol's (`setSymbolInfo`, or the\n * stream's adapter), else its market's (`setMarket`); null when neither\n * names one the browser knows, and `'exchange'` then shows UTC.\n */\n getExchangeTimeZone(): string | null {\n return [symbolTimeZone(this.symbolInfo), this.marketConfig?.timeZone].find((zone) => zone && isValidTimeZone(zone)) ?? null;\n }\n\n /** @deprecated Use `setTimeZone`, spelled as the `timeZone` option is. */\n setTimezone(tz: TimeZoneSetting): void {\n this.setTimeZone(tz);\n }\n\n /** @deprecated Use `getTimeZone`. */\n getTimezone(): TimeZoneSetting {\n return this.getTimeZone();\n }\n\n /** @deprecated Use `getEffectiveTimeZone`. */\n getEffectiveTimezone(): TimeZoneSetting {\n return this.getEffectiveTimeZone();\n }\n\n /** @deprecated Use `getExchangeTimeZone`. */\n getExchangeTimezone(): string | null {\n return this.getExchangeTimeZone();\n }\n\n /** @deprecated Use `setTimeZone`, which takes an offset in minutes east of UTC, or null for the browser's zone. */\n setTimezoneOffset(minutes: number | null): void {\n this.setTimeZone(minutes);\n }\n\n /**\n * The exchange's zone where it counts beyond the zone shown: daily bars\n * built at its midnight are dated there, and so are days built from ticks.\n * Indicators that start again each day, week or month, the prior-day lines\n * and the market profile's sessions count their days there too.\n */\n private applyExchangeZone(): void {\n const zone = this.getExchangeTimeZone();\n this.timeAxis.setExchangeTimeZone(zone);\n this.crosshairHandler.setExchangeTimeZone(zone);\n this.crosshairTooltip.setExchangeTimeZone(zone);\n this.pinnedTooltip.setExchangeTimeZone(zone);\n if (!this.studyZoneHeld) this.applyStudyZone(zone);\n }\n\n /**\n * The zone the indicators, the prior-day lines, the market profile and the\n * bars built from trades count their days in. Held through a switch, the\n * next symbol's trades go on building the day its history ends on when it\n * trades on the same exchange, rather than starting UTC's day beside it.\n */\n private applyStudyZone(zone: string | null): void {\n this.studyZone = zone;\n this.streamManager?.setTimeZone(zone);\n this.periodLevels.setExchangeTimeZone(zone);\n this.marketProfile.setExchangeTimeZone(zone);\n const bars = this.series.bars();\n // With no bars yet, the data that comes computes them. Otherwise only\n // the indicators that count days in the zone, before the next read, not\n // now: the bars the symbol's hours filter again follow in setSymbolInfo,\n // and computing them computes everything once.\n if (this.indicatorEngine.setExchangeTimeZone(zone, bars.length > 0 ? bars : undefined) && bars.length > 0) {\n this.announceIndicatorUpdate(0);\n }\n }\n\n /**\n * The indicators keep the last symbol's zone while the feed is asked about\n * the next, for at most as long as the days it builds wait for the answer.\n */\n private holdStudyZone(): void {\n this.endStudyZoneWait();\n this.studyZoneHeld = true;\n this.studyZoneTimer = setTimeout(() => this.releaseStudyZone(), SYMBOL_ZONE_WAIT_MS);\n }\n\n private endStudyZoneWait(): void {\n this.studyZoneReleaseQueued = false;\n if (this.studyZoneTimer === null) return;\n clearTimeout(this.studyZoneTimer);\n this.studyZoneTimer = null;\n }\n\n /** No word on the next symbol is coming: the indicators and the rest take the chart's zone. */\n private releaseStudyZone(): void {\n this.endStudyZoneWait();\n if (!this.studyZoneHeld) return;\n this.studyZoneHeld = false;\n this.applyStudyZone(this.getExchangeTimeZone());\n if (!this.disposed) this.updateViewportAndRender();\n }\n\n /**\n * A disconnect ends the hold once the code that called it has run, unless\n * a connect came in it: a symbol switch made of `disconnectStream()` and\n * `connect()`, as the widget's are, asks about the next symbol in that\n * connect, which holds the zone on from its question, as `switchStream` does.\n */\n private releaseStudyZoneAfterCaller(): void {\n if (!this.studyZoneHeld) return;\n this.endStudyZoneWait();\n this.studyZoneReleaseQueued = true;\n queueMicrotask(() => {\n if (!this.studyZoneReleaseQueued || this.disposed) return;\n this.releaseStudyZone();\n });\n }\n\n /**\n * A bar's time as a screen reader hears it, in the chart's locale: a daily\n * bar's date, the one it stands for in any zone shown (`barDateZone`), as\n * the axis gives it; any other bar's date and time in the zone shown.\n */\n private spokenBarTime(time: number): string {\n const data = this.getDisplayData();\n const ms = time * barTimeUnit(data);\n const locale = this.numberLocale || 'en-US';\n if (barsAreDaily(data) === true) {\n return zonedDateFormatter(locale, { dateStyle: 'medium' }, barDateZone(ms, this.displayTz, this.getExchangeTimeZone()))(ms);\n }\n return zonedDateFormatter(locale, { dateStyle: 'medium', timeStyle: 'short' }, this.displayTz)(ms);\n }\n\n private applyTimeZone(): void {\n const tz = this.timeZoneSetting === EXCHANGE_TIME_ZONE ? (this.getExchangeTimeZone() ?? 'UTC') : this.timeZoneSetting;\n this.displayTz = tz;\n this.timeAxis.setTimezoneOffset(tz);\n this.crosshairHandler.setTimezoneOffset(tz);\n this.crosshairTooltip.setTimezoneOffset(tz);\n this.pinnedTooltip.setTimezone(tz);\n this.sessionBreaks.setTimezone(tz);\n this.engine.requestRender();\n }\n\n // --- Symbol info ---\n\n /**\n * What is known about the symbol on the chart — usually from the stream's\n * adapter (`resolveSymbol`), which a connected chart asks by itself. Its\n * price precision applies unless `setMarket` set one; its zone\n * (`timeZone`) backs `setTimeZone('exchange')` and dates daily bars; its\n * hours feed the session shading. `getSymbolInfo()` gives the zone as `timeZone` and as the older\n * `timezone`; given both with different zones, `timeZone` is the one, unless\n * it is the zone the chart already has for the symbol and `timezone` is new\n * (`{ ...chart.getSymbolInfo(), timezone: zone }` changes it).\n */\n setSymbolInfo(info: SymbolInfo | null): void {\n // From the host or the feed: the indicators take the zone it brings, or none.\n this.studyZoneHeld = false;\n this.endStudyZoneWait();\n this.useSymbolInfo(info);\n }\n\n /** `setSymbolInfo`, leaving the indicators' zone held or not as it is. */\n private useSymbolInfo(info: SymbolInfo | null): void {\n const held = info && this.symbolInfo?.symbol === info.symbol ? symbolTimeZone(this.symbolInfo) : undefined;\n this.symbolInfo = info ? withBothZoneSpellings({ ...info, sessions: info.sessions?.map((session) => ({ ...session })) }, held) : null;\n this.chartLegend.setExchange(this.symbolInfo?.exchange ?? '');\n this.engine.requestRender(LayerType.Hover);\n this.priceAxis.setPriceTick(this.symbolInfo?.minTick ?? null);\n if (this.marketConfig?.pricePrecision === undefined) {\n this.applyPricePrecision(this.symbolInfo?.pricePrecision ?? null);\n }\n const windows = (this.symbolInfo?.sessions ?? []).flatMap((session) => {\n const startMinute = sessionMinute(session.start);\n const endMinute = sessionMinute(session.end);\n return startMinute === null || endMinute === null ? [] : [{ startMinute, endMinute }];\n });\n const zone = symbolTimeZone(this.symbolInfo);\n // A symbol without hours of its own goes back to the host's.\n this.sessionShading.replaceConfig(windows.length > 0 && zone && isValidTimeZone(zone)\n ? { timeZone: zone, windows, startMinute: windows[0].startMinute, endMinute: windows[windows.length - 1].endMinute }\n : this.hostSessionHours);\n if (this.timeZoneSetting === EXCHANGE_TIME_ZONE) this.applyTimeZone();\n this.applyExchangeZone();\n this.reapplyExtendedHours();\n this.updateViewportAndRender();\n this.eventBus.emit('symbolInfoChange', { info: this.symbolInfo });\n }\n\n getSymbolInfo(): SymbolInfo | null {\n return this.symbolInfo;\n }\n\n /**\n * What the feed says about the symbol after it was asked: this chart's\n * (any case), over what is known of it, so a partial message keeps the rest.\n * The same again changes nothing.\n */\n private applyFeedSymbolInfo(info: Partial<SymbolInfo> | null | undefined): void {\n if (!info || typeof info !== 'object') return;\n const symbol = this.currentSymbol;\n const sameSymbol = (s: string | undefined) => !s || s.toUpperCase() === symbol.toUpperCase();\n if (!sameSymbol(info.symbol)) return;\n const known = this.symbolInfo && sameSymbol(this.symbolInfo.symbol) ? this.symbolInfo : null;\n // A zone in either spelling moves it: the known one is in both.\n const next: SymbolInfo = withBothZoneSpellings({ ...known, ...info, symbol: info.symbol || known?.symbol || symbol }, symbolTimeZone(known));\n if (known && JSON.stringify(next) === JSON.stringify(known)) return;\n // An answer to resolveSymbol still on its way is older than this.\n this.symbolInfoSeq++;\n this.setSymbolInfo(next);\n }\n\n /** Ask the stream's adapter about `symbol`, if it can tell; a late answer about another symbol is dropped. */\n private resolveStreamSymbol(adapter: DataAdapter, symbol: string): void {\n // Through the stream's own wrapper: the question it asks to build the symbol's days is this one.\n const source = withResampling(adapter);\n const resolve = source.resolveSymbol?.bind(source);\n // Another symbol's details go, whether or not the feed can be asked (it may send them later).\n // Its zone stays with the indicators until the answer comes.\n if (this.symbolInfo && this.symbolInfo.symbol !== symbol) {\n if (resolve) this.holdStudyZone();\n else this.studyZoneHeld = false;\n this.useSymbolInfo(null);\n } else if (resolve && this.studyZoneHeld) {\n // A switch on before the last symbol's answer came: the hold lasts as\n // long from this question, as the days the feed builds wait for it.\n this.holdStudyZone();\n }\n if (!resolve) {\n this.releaseStudyZone();\n return;\n }\n const seq = ++this.symbolInfoSeq;\n // Taken as a promise: an adapter may throw, or answer without one.\n Promise.resolve().then(() => resolve(symbol)).then(\n (info) => {\n // A newer question, or a disconnect, settles the zone instead.\n if (seq !== this.symbolInfoSeq) return;\n // No answer leaves what the host set for the symbol, if anything, and the chart's zone.\n if (info && this.currentSymbol === symbol) this.setSymbolInfo(info);\n else this.releaseStudyZone();\n },\n (err: unknown) => {\n if (seq !== this.symbolInfoSeq) return;\n // Only the symbol's details are missing; the feed itself is fine.\n this.releaseStudyZone();\n console.warn(`No symbol info for \"${symbol}\":`, err);\n },\n );\n }\n\n // --- Pivot / swing markers ---\n\n setPivotMarkersVisible(visible: boolean): void {\n this.pivotMarkers.setVisible(visible);\n this.engine.requestRender(LayerType.Overlay);\n }\n\n isPivotMarkersVisible(): boolean {\n return this.pivotMarkers.isVisible();\n }\n\n setPivotMarkersConfig(config: { left?: number; right?: number; showLabels?: boolean; structureLabels?: boolean }): void {\n if (config.left !== undefined || config.right !== undefined) {\n this.pivotMarkers.setStrength(config.left ?? 5, config.right ?? config.left ?? 5);\n }\n if (config.showLabels !== undefined) this.pivotMarkers.setShowLabels(config.showLabels);\n if (config.structureLabels !== undefined) this.pivotMarkers.setStructureLabels(config.structureLabels);\n this.engine.requestRender(LayerType.Overlay);\n }\n\n getPivotMarkersConfig(): import('@tradecanvas/core').PivotMarkersConfig {\n return this.pivotMarkers.getConfig();\n }\n\n // --- Session (RTH) shading ---\n\n setSessionShadingVisible(visible: boolean): void {\n this.sessionShading.setVisible(visible);\n this.engine.requestRender(LayerType.Background);\n }\n\n isSessionShadingVisible(): boolean {\n return this.sessionShading.isVisible();\n }\n\n /**\n * Configure the regular session window (minutes-of-day + tz offset). Pass\n * `windows` for a split session — a market with a midday recess (e.g. SET's\n * 10:00–12:30 and 14:30–16:30) — so the lunch break dims like pre-/post-market.\n */\n setSessionShadingConfig(config: Partial<SessionHoursConfig>): void {\n // The host's hours win over the symbol's, until the next symbol brings its own.\n this.hostSessionHours = mergeSessionHours(this.hostSessionHours, config);\n this.sessionShading.replaceConfig(this.hostSessionHours);\n this.engine.requestRender(LayerType.Background);\n }\n\n // --- Tooltip ---\n\n setTooltipVisible(visible: boolean): void {\n if (!visible) this.crosshairTooltip.hide();\n }\n\n // --- Legend ---\n\n setLegend(config: Partial<import('@tradecanvas/core').LegendConfig>): void {\n this.chartLegend.setConfig(config);\n this.engine.requestRender(LayerType.Hover);\n }\n\n /** Whether the legend is drawn: shown by `setLegend`, and `features.legend` not off. */\n isLegendVisible(): boolean {\n return this.features.legend && this.chartLegend.getConfig().visible;\n }\n\n setSymbolName(symbol: string): void {\n this.chartLegend.setSymbol(symbol);\n this.engine.requestRender(LayerType.Hover);\n }\n\n /**\n * Set a status text displayed in the chart legend area (e.g., \"LIVE . 8ms\").\n * Pass null to clear.\n */\n setStatusText(text: string | null): void {\n this.chartLegend.setStatusText(text);\n this.engine.requestRender(LayerType.Hover);\n }\n\n // --- Renderer ---\n\n /**\n * Draw the bars and volume with WebGL 2 (`'webgl'`; `'auto'` only on a real\n * GPU, not a software one) or with Canvas 2D (`'canvas'`). Resolves to what\n * draws now: Canvas 2D where WebGL 2 can't be had, or while the charts on\n * the page hold as many WebGL contexts as they may (`setMaxWebGLCharts`;\n * WebGL follows once one is let go). The WebGL code loads on first use.\n * Emits `rendererChange`.\n */\n async setRenderer(mode: RendererMode): Promise<'canvas' | 'webgl'> {\n const token = ++this.rendererToken;\n this.waitForGpu(null);\n if (mode === 'canvas') {\n this.applyRenderer(null);\n return 'canvas';\n }\n // Already on the GPU: keep that context rather than make a second one.\n if (this.engine.getGpu()) return 'webgl';\n return this.takeGpu(mode, token, true);\n }\n\n /** WebGL for `mode`; or Canvas 2D, waiting for a context to come free when the charts hold all they may. */\n private async takeGpu(mode: 'webgl' | 'auto', token: number, report: boolean): Promise<'canvas' | 'webgl'> {\n const { gpu, reason } = await loadWebGL(mode);\n // Superseded by a later call, or the chart is gone.\n if (token !== this.rendererToken || this.disposed) {\n gpu?.destroy();\n return this.getRenderer();\n }\n if (reason === 'limit') {\n // One may have come free since it was asked for (a superseded call let go).\n if (gpuContextsLeft() > 0) return this.takeGpu(mode, token, report);\n this.waitForGpu(() => void this.takeGpu(mode, token, false));\n if (report) this.applyRenderer(null, 'limit');\n return this.getRenderer();\n }\n // A retry that found no WebGL after all says nothing: it said 'limit' already.\n if (gpu || report) this.applyRenderer(gpu, reason);\n return this.getRenderer();\n }\n\n private waitForGpu(retry: (() => void) | null): void {\n this.stopWaitingForGpu?.();\n this.stopWaitingForGpu = retry ? whenGpuContextFree(retry) : null;\n }\n\n /** What draws the bars and volume now. */\n getRenderer(): 'canvas' | 'webgl' {\n return this.engine.getGpu() ? 'webgl' : 'canvas';\n }\n\n private applyRenderer(gpu: GpuRenderer | null, reason?: 'unsupported' | 'limit'): void {\n const before = this.getRenderer();\n if (gpu) this.engine.attachGpu(gpu);\n else this.engine.detachGpu();\n const now = this.getRenderer();\n if (now !== before || reason) this.eventBus.emit('rendererChange', reason ? { renderer: now, reason } : { renderer: now });\n }\n\n /**\n * Draw right before reading the canvases: the GPU canvas is cleared once\n * shown, and a paused chart hasn't drawn what changed since.\n */\n private freshFrame(): void {\n if (this.isSuspended()) {\n if (this.renderDeferred) {\n this.renderDeferred = false;\n this.refitForTick();\n }\n this.engine.renderNow();\n return;\n }\n if (this.engine.getGpu()) this.engine.renderNow();\n }\n\n // --- Screenshot ---\n\n screenshot(filename?: string): void {\n if (!this.features.screenshot) return;\n this.freshFrame();\n Screenshot.download(this.container, filename, this.themeManager.getTheme().background);\n }\n\n /** Copy the chart image to the clipboard. Returns false if unsupported/blocked. */\n async copyScreenshot(): Promise<boolean> {\n if (!this.features.screenshot) return false;\n const ClipboardItemCtor = (globalThis as { ClipboardItem?: typeof ClipboardItem }).ClipboardItem;\n // A runtime with no navigator has no clipboard either: that is a false, not an error.\n const clipboard = typeof navigator === 'undefined' ? undefined : navigator.clipboard;\n if (!clipboard?.write || !ClipboardItemCtor) return false;\n try {\n this.freshFrame();\n const png = Screenshot.toBlob(this.container, this.themeManager.getTheme().background).then((blob) => {\n if (!blob) throw new Error('No chart image to copy');\n return blob;\n });\n // The write reports a missing image; this keeps it from also showing as an unhandled rejection.\n png.catch(() => {});\n // The item is made, and written, before anything is awaited, while the\n // image still encodes: Safari lets a page write the clipboard only while\n // the click that asked for it is being handled.\n let item: ClipboardItem;\n try {\n item = new ClipboardItemCtor({ 'image/png': png });\n } catch (err) {\n // Older browsers take only a Blob here, so they wait for the encoding.\n if (!(err instanceof TypeError)) throw err;\n item = new ClipboardItemCtor({ 'image/png': await png });\n }\n await clipboard.write([item]);\n return true;\n } catch {\n return false;\n }\n }\n\n screenshotDataURL(): string | null {\n if (!this.features.screenshot) return null;\n this.freshFrame();\n return Screenshot.toDataURL(this.container, this.themeManager.getTheme().background);\n }\n\n async screenshotBlob(): Promise<Blob | null> {\n if (!this.features.screenshot) return null;\n this.freshFrame();\n return Screenshot.toBlob(this.container, this.themeManager.getTheme().background);\n }\n\n // --- Alerts ---\n\n /**\n * Add an alert on `channel` (`'price'`, or an indicator line\n * `'<instanceId>:<key>'`) at `price`. `options` compare with another line\n * instead (`target`), measure a move (`movesUp` / `movesDown` with\n * `percent` and `bars`), look only at closed bars (`onBarClose`), or end it\n * (`expiresAt`). Throws a RangeError for options that don't fit together.\n */\n addAlert(\n price: number,\n condition: import('@tradecanvas/core').AlertCondition = 'crossing',\n message?: string,\n channel = 'price',\n label?: string,\n options: import('@tradecanvas/core').AlertOptions = {},\n ): string | null {\n if (!this.features.alerts) return null;\n const id = this.alertManager.addAlert(price, condition, message, false, channel, label, options);\n this.markStateChanged();\n return id;\n }\n\n removeAlert(id: string): void {\n this.alertManager.removeAlert(id);\n this.markStateChanged();\n }\n\n // --- Drawing order and groups ---\n\n /** Ids of the selected drawings, the one clicked last first. */\n getSelectedDrawingIds(): string[] {\n return this.drawingManager.getSelectedDrawingIds();\n }\n\n /**\n * Draw a drawing on top of the others, under them, one step up or down, or\n * at a place in the order (`getDrawings()` lists them bottom first, 0 the\n * bottom). False when already there.\n */\n moveDrawing(id: string, to: DrawingOrderMove): boolean {\n const moved = this.drawingManager.moveDrawing(id, to);\n if (moved) this.markStateChanged();\n return moved;\n }\n\n /** Group drawings (two or more): they are then selected, hidden and locked together. Returns the group id. */\n groupDrawings(ids: readonly string[], name?: string): string | null {\n const group = this.drawingManager.groupDrawings(ids, name);\n if (group) this.markStateChanged();\n return group;\n }\n\n ungroupDrawings(groupId: string): boolean {\n return this.afterDrawingChange(this.drawingManager.ungroup(groupId));\n }\n\n renameDrawingGroup(groupId: string, name: string): boolean {\n return this.afterDrawingChange(this.drawingManager.renameGroup(groupId, name));\n }\n\n setDrawingGroupVisible(groupId: string, visible: boolean): boolean {\n return this.afterDrawingChange(this.drawingManager.setGroupVisible(groupId, visible));\n }\n\n setDrawingGroupLocked(groupId: string, locked: boolean): boolean {\n return this.afterDrawingChange(this.drawingManager.setGroupLocked(groupId, locked));\n }\n\n /** Every drawing group, its drawings bottom to top. */\n getDrawingGroups(): { id: string; name: string; ids: string[] }[] {\n return this.drawingManager.getGroups();\n }\n\n private afterDrawingChange(changed: boolean): boolean {\n if (changed) this.markStateChanged();\n return changed;\n }\n\n /** Whether a drawing has lines an alert can cross (trend lines, rays, horizontals, channels). */\n canAddDrawingAlert(drawingId: string): boolean {\n return this.features.alerts && this.drawingLevelsNow(drawingId) !== null;\n }\n\n /**\n * Where a drawing's lines are at the latest bar, as drawn on the chart's\n * scale; null when it has none there (a trend line that ends before it).\n */\n private drawingLevelsNow(drawingId: string): number[] | null {\n // A pane's drawing is at the pane's values, not at prices.\n if (this.drawingManager.getDrawings().find((d) => d.id === drawingId)?.paneId !== undefined) return null;\n // Alerts watch the live market, during a replay too.\n const data = this.replaySession ? this.series.liveBars() : this.getDisplayData();\n if (data.length === 0) return null;\n const viewport = { ...this.viewport.getState(), data };\n return this.drawingManager.priceAt(drawingId, data[data.length - 1].time, viewport);\n }\n\n /**\n * A drawing's alerts go with it, and come back with it when an undo\n * brings the drawing back.\n */\n private followDrawingAlerts(event: string, drawingId: string | undefined): void {\n if (!drawingId || !this.drawingAlerts) return;\n if (event === 'drawingRemove') this.drawingAlerts.removed(drawingId);\n else if (event === 'drawingCreate') this.drawingAlerts.created(drawingId);\n }\n\n /** Remove alerts on drawings no longer on the chart (drawings replaced or cleared). */\n private dropOrphanDrawingAlerts(): void {\n if (!this.drawingAlerts) return;\n const removed = this.drawingAlerts.prune(new Set(this.drawingManager.getDrawings().map((d) => d.id)));\n if (removed > 0) this.markStateChanged();\n }\n\n /**\n * Alert when the price crosses a drawing's line(s): a trend line where it\n * is at the latest bar, any line of a channel. Removed with the drawing.\n * Null when the drawing has no line to cross.\n */\n addDrawingAlert(\n drawingId: string,\n options: { condition?: 'crossing' | 'crossingUp' | 'crossingDown'; message?: string; repeating?: boolean; label?: string } = {},\n ): string | null {\n if (!this.canAddDrawingAlert(drawingId)) return null;\n const id = this.alertManager.addDrawingAlert(drawingId, options.condition ?? 'crossing', options.message, options.repeating ?? false, options.label);\n this.markStateChanged();\n return id;\n }\n\n getAlerts(): import('@tradecanvas/core').PriceAlert[] {\n return this.alertManager.getAlerts();\n }\n\n clearAlerts(): void {\n this.alertManager.clearAlerts();\n this.markStateChanged();\n }\n\n saveAlerts(key: string): void {\n this.alertManager.saveToStorage(key);\n }\n\n /** Load alerts saved with `saveAlerts`. Alerts on drawings that aren't on the chart are dropped. */\n loadAlerts(key: string): void {\n this.alertManager.loadFromStorage(key);\n this.alertManager.pruneDrawingAlerts(new Set(this.drawingManager.getDrawings().map((d) => d.id)));\n this.engine.requestRender(LayerType.Overlay);\n }\n\n // --- Signal Markers ---\n\n addSignalMarker(marker: Omit<import('@tradecanvas/commons').SignalMarker, 'id'> & { id?: string }): string {\n return this.signalMarkerManager.addMarker(marker);\n }\n\n removeSignalMarker(id: string): void {\n this.signalMarkerManager.removeMarker(id);\n }\n\n getSignalMarkers(): import('@tradecanvas/commons').SignalMarker[] {\n return this.signalMarkerManager.getMarkers();\n }\n\n setSignalMarkers(markers: import('@tradecanvas/commons').SignalMarker[]): void {\n this.signalMarkerManager.setMarkers(markers);\n }\n\n clearSignalMarkers(): void {\n this.signalMarkerManager.clearMarkers();\n }\n\n setSignalMarkerStyle(style: Partial<import('@tradecanvas/commons').SignalMarkerStyle>): void {\n this.signalMarkerManager.setStyle(style);\n }\n\n // --- Trade Zones ---\n\n addTradeZone(zone: Omit<import('@tradecanvas/commons').TradeZone, 'id'> & { id?: string }): string {\n return this.tradeZoneManager.addZone(zone);\n }\n\n updateTradeZone(id: string, updates: Partial<Omit<import('@tradecanvas/commons').TradeZone, 'id'>>): void {\n this.tradeZoneManager.updateZone(id, updates);\n }\n\n removeTradeZone(id: string): void {\n this.tradeZoneManager.removeZone(id);\n }\n\n getTradeZones(): import('@tradecanvas/commons').TradeZone[] {\n return this.tradeZoneManager.getZones();\n }\n\n setTradeZones(zones: import('@tradecanvas/commons').TradeZone[]): void {\n this.tradeZoneManager.setZones(zones);\n }\n\n clearTradeZones(): void {\n this.tradeZoneManager.clearZones();\n }\n\n setTradeZoneStyle(style: Partial<import('@tradecanvas/commons').TradeZoneStyle>): void {\n this.tradeZoneManager.setStyle(style);\n }\n\n // --- Replay ---\n\n /**\n * Replay the series bar by bar. While it runs the chart is detached from\n * live data: stream ticks and `appendBar`/`updateLastBar` calls are kept\n * aside (orders still track the live price), the price line shows the\n * replayed close, and auto-scale refits every step. `replayStop()` brings\n * back the live series, including everything that arrived meanwhile.\n *\n * `startIndex` is the first bar it reveals, an index of `getData()`: the\n * bars before it show at once, the later ones as it reaches them.\n * `interval` (ms, 500 by default) and `speed` (1) pace it at one bar\n * every `interval / speed` ms: `{ startIndex: 60, interval: 250 }` plays\n * from bar 60 at four bars a second. `paused: true` waits at the start.\n *\n * With `steps` (finer bars), each step grows the forming bar from them.\n * `animate: true` grows each bar over its time instead, and with `step`\n * (`'auto'` or an interval) the chart fetches the finer bars that shape it\n * through the connected feed; `step` needs `animate`.\n * A connected execution adapter that takes a mark price (a paper one)\n * trades on the replayed price and time.\n *\n * Signal markers and trade zones show as the replay reaches them\n * (`revealMarks: false` shows them all). `startTime` starts at a time\n * instead of a bar, `hideHistory` leaves the bars before the start out,\n * and `duration` plays to the end in about that long, however many bars.\n *\n * The replay is a time over the live series: when that changes (`setData`,\n * `connect` or `switchStream` to another symbol or timeframe, older bars,\n * the extended hours, a correction) it goes on over the new bars at the\n * same time, held while they load, and its indices change with them (a\n * load that fails keeps it held, still telling the state it was asked to\n * keep, until a later load comes in or `replayStop()`). It\n * ends there only when a paper account trades it and the symbol changes.\n * Call `replayStop()` first to end it as before.\n */\n replayStart(config?: Partial<import('@tradecanvas/core').ReplayConfig>): void {\n if (!this.features.replay) return;\n // A restart of a replay without its history: bar indices count in its window.\n const restartWindow = this.replaySession ? this.replayOffset : 0;\n if (!this.replaySession) {\n if (this.series.length() === 0) return; // nothing to replay\n this.series.beginReplay();\n this.replaySession = newReplaySession(this.currentSymbol);\n }\n const session = this.replaySession;\n // Replay the live series as it stands now (a restart includes bars that\n // arrived during the previous run).\n const { steps: rawSteps, startTime, hideHistory, revealMarks, step, ...playConfig } = config ?? {};\n // Set each start: the clock keeps what it was given before.\n playConfig.animate = playConfig.animate === true;\n const full = this.series.inHoursShown(this.series.liveBars());\n // Where it starts, as a bar of the whole series: from a time, or an index.\n // A restart within a window (no `startTime`, history not asked back) keeps\n // the window, and its index counts in it, as the replay's indices do.\n const inWindow = restartWindow > 0 && startTime === undefined && hideHistory !== false;\n let start = playConfig.startIndex;\n if (startTime !== undefined && start === undefined) start = firstAtOrAfter(full, startTime);\n else if (inWindow && start !== undefined) start += restartWindow;\n const windowStart = inWindow\n ? restartWindow\n : hideHistory && start !== undefined && start > 0 ? Math.min(Math.floor(start), full.length - 1) : 0;\n const coarse = windowStart > 0 ? full.slice(windowStart) : full;\n if (start !== undefined) playConfig.startIndex = start - windowStart;\n session.windowTime = windowStart > 0 ? coarse[0]?.time ?? null : null;\n session.steps = rawSteps ?? null;\n session.stepsSymbol = this.currentSymbol;\n session.until = null;\n session.animate = playConfig.animate;\n session.step = playConfig.animate ? step ?? null : null;\n this.replayRevealMarks = revealMarks !== false;\n this.useReplayLoader(session, true);\n const steps = rawSteps ? this.replayStepsOver(session, coarse, true) : null;\n // A replay fits each step; a price-axis drag from before shouldn't pin it.\n this.priceFit.resume();\n this.buildReplay(coarse, steps, windowStart);\n const stepped = this.replayStepsSeries;\n if (stepped && playConfig.startIndex !== undefined) playConfig.startIndex = lastStepOfBar(stepped, coarse, playConfig.startIndex);\n const series = stepped ?? coarse;\n // Markers and trades as far as the start, or all of them.\n const first = series[Math.max(0, Math.min(Math.floor(Number(playConfig.startIndex ?? 0)) || 0, series.length - 1))];\n this.revealReplayMarks(this.replayRevealMarks ? first?.time ?? null : null);\n // Started while the next series loads: it waits there, to go on as asked.\n if (session.held) {\n session.held.state = playConfig.paused ? 'paused' : 'playing';\n playConfig.paused = true;\n }\n // Playing from a bar: the cut shows at once, not only after the first step.\n if (!playConfig.paused && playConfig.startIndex !== undefined && playConfig.startIndex >= 1) this.replaySeek(playConfig.startIndex - 1);\n this.replayManager.play(playConfig);\n }\n\n /**\n * Load a replay of `coarse` (in `steps`, finer bars, when given) into the\n * clock, stopped at its start, and show each of its steps. The bars before\n * `windowStart` of the series are left out (`hideHistory`).\n */\n private buildReplay(coarse: DataSeries, finer: DataSeries | null, windowStart: number): void {\n // With `animate` the clock counts the chart's bars; finer bars only shape them.\n const animate = this.replaySession?.animate === true;\n const steps = animate ? null : finer;\n this.replayPieces = animate && finer ? piecesByBar(finer, coarse) : null;\n this.replayForming = null;\n this.replayOffset = windowStart;\n this.replayStepsSeries = steps;\n this.replayCoarse = coarse;\n // Loading stops the run before: that is a restart, not the replay ending.\n const restarting = this.replayRestarting;\n this.replayRestarting = true;\n try {\n this.replayManager.load(steps ?? coarse);\n } finally {\n this.replayRestarting = restarting;\n }\n // Each replayStart used to stack another 'bar' listener, so a restarted\n // replay ran every step once per previous start.\n this.replayBarUnsub?.();\n this.replayPhaseUnsub?.();\n let loaded = -1; // bars of `coarse` shown, as of the last step (the forming one included)\n let lastStep = -1;\n let forming: OHLCBar | null = null;\n const series = steps ?? coarse;\n this.replayBarUnsub = this.replayManager.on('bar', ({ index }) => {\n // Which bar of the chart's series forms now, and how it looks.\n let barIndex = index;\n let shownBar: OHLCBar = coarse[index];\n if (steps) {\n const step = steps[index];\n barIndex = barAt(coarse, step.time);\n const sameBar = forming !== null && lastStep === index - 1 && barIndex === loaded - 1;\n forming = sameBar && forming ? mergeBar(forming, step) : formingBar(coarse[barIndex], steps, index);\n // Its last step ends the bar as the series has it: finer bars can disagree with it.\n shownBar = closesBar(steps, coarse, index, barIndex) ? coarse[barIndex] : forming;\n }\n const formed = this.replayForming;\n this.replayForming = null;\n lastStep = index;\n const nextLen = barIndex + 1;\n const forward = loaded > 0 && nextLen >= loaded && this.series.length() === loaded;\n // Like live data: a replay step follows the newest bar while the view\n // rests at the end, and leaves it alone while the user looks at history.\n // The first step and any seek (back or forward) show the replay\n // position. (Read before the new bars land: `isAtEnd` compares against\n // the old length.)\n const stepForward = forward && !this.replaySeeking;\n let follow = !stepForward || (this.autoScrollOnNewBar && this.viewport.isAtEnd());\n const shownBefore = this.series.length();\n if (forward) {\n // Forward: the bar that was forming closes as it is in the series, the\n // newly revealed bars are appended, and indicators update from there.\n // (With `animate` it was drawn part way; a bar already whole is written again unchanged.)\n if (nextLen > loaded) this.series.updateReplayed(coarse[loaded - 1]);\n for (let j = loaded; j < nextLen - 1; j++) this.series.appendReplayed(coarse[j]);\n if (nextLen > loaded) this.series.appendReplayed(shownBar);\n else this.series.updateReplayed(shownBar);\n this.recalcIndicatorsFrom(this.series.bars(), Math.max(0, loaded - 1));\n } else {\n // First step, or a seek: reload the prefix.\n this.series.showReplayed([...coarse.slice(0, barIndex), shownBar]);\n this.recalcIndicators(this.series.bars());\n }\n loaded = nextLen;\n // Older bars came in front: the view stays on the same bars.\n if (this.replayCause === 'older') {\n this.viewport.prependBars(nextLen - shownBefore);\n follow = false;\n }\n // The price line follows the replay, not the live market.\n this.currentPriceLine.setPrice(shownBar.close, barIndex > 0 ? coarse[barIndex - 1].close : undefined);\n this.crosshairHandler.setData(this.series.bars());\n // Up to when it has shown: the next step's time (past the last one, its spacing on).\n const next = series[index + 1]?.time;\n const until = next ?? series[index].time + (index > 0 ? series[index].time - series[index - 1].time : 1);\n if (this.replaySession) this.replaySession.until = until;\n // Markers and trades as far as the replay has shown, in the same frame as its bars.\n if (this.replayRevealMarks) this.revealReplayMarks(until);\n // The replay's bars are a series of their own: what was kept is of no use.\n this.display.reset();\n this.updateViewportAndRender(follow);\n if (animate) this.feedReplayForming(coarse, index, formed?.index === index ? formed.path : this.replayPathOf(coarse, index), 1);\n else this.feedReplaySteps(series, index);\n const cause = this.replayCause !== null ? 'series' : stepForward ? 'play' : 'seek';\n this.eventBus.emit('replayStep', { barIndex, time: coarse[barIndex].time, stepTime: series[index].time, until, total: coarse.length, cause });\n });\n if (!animate) return;\n this.replayPhaseUnsub = this.replayManager.on('phase', ({ index, fraction }) => {\n const bar = coarse[index];\n // Shown as far as the bar before it (a new one appends), or forming it now.\n if (!bar || this.series.length() !== loaded || (loaded !== index && loaded !== index + 1)) return;\n const path = this.replayForming?.index === index ? this.replayForming.path : this.replayPathOf(coarse, index);\n const f = prefersReducedMotion() ? pathStop(path, fraction) : fraction;\n const appended = loaded === index;\n // A new bar follows the newest while the view rests at the end (read before it lands).\n const follow = appended && this.autoScrollOnNewBar && this.viewport.isAtEnd();\n const shown = formingAt(path, f);\n if (appended) this.series.appendReplayed(shown);\n else this.series.updateReplayed(shown);\n loaded = index + 1;\n this.replayForming = { index, path };\n this.drawFormingBar(shown, index > 0 ? coarse[index - 1].close : undefined, appended, follow);\n this.feedReplayForming(coarse, index, path, f);\n this.loadReplaySteps(coarse, index);\n });\n }\n\n /**\n * The bar forming in a replay drawn again, as `updateLastBar` draws a live\n * one, without the live price: alerts and orders keep watching the market.\n * A new bar fits the view as a replay step does.\n */\n private drawFormingBar(bar: OHLCBar, previousClose: number | undefined, appended: boolean, follow: boolean): void {\n this.currentPriceLine.setPrice(bar.close, previousClose);\n const data = this.series.bars();\n this.recalcIndicatorsFrom(data, data.length - 1);\n if (!appended) {\n this.scheduleRender();\n return;\n }\n this.crosshairHandler.setData(data);\n this.display.reset();\n this.updateViewportAndRender(follow);\n }\n\n /** The path bar `index` of `coarse` forms along: its finer bars when they are in, else its own prices. */\n private replayPathOf(coarse: DataSeries, index: number): IntrabarPath {\n const bar = coarse[index];\n const finer = this.replayPieces?.get(bar.time) ?? this.replayLoader?.loader.piecesOf(bar.time) ?? [];\n return intrabarPath(bar, finer);\n }\n\n /** Finer bars for the bars the next few seconds of playback show, and none kept for those behind. */\n private loadReplaySteps(coarse: DataSeries, index: number): void {\n const loader = this.replayLoader?.loader;\n if (!loader) return;\n loader.evictBefore(coarse[index].time);\n const barMs = this.replayManager.getStepMs();\n loader.ensure(coarse, index, Number.isFinite(barMs) && barMs > 0 ? Math.ceil(REPLAY_LOOKAHEAD_MS / barMs) : 1);\n }\n\n /**\n * The loader of finer bars for a replay with `animate` and a `step`, kept\n * while the symbol and the interval are (`fresh`: a new one, at a start).\n * None without a connected feed, or a step it serves.\n */\n private useReplayLoader(session: ReplaySession, fresh: boolean): void {\n const adapter = this.streamAdapter;\n const chartTf = this.streamTimeframe;\n const step = session.step && adapter && chartTf !== null\n ? resolveReplayStep(session.step, chartTf, (tf) => servesTimeframe(adapter, tf))\n : null;\n const key = `${this.currentSymbol}|${chartTf}|${step}`;\n if (!fresh && this.replayLoader?.key === key) return;\n this.replayLoader?.loader.dispose();\n this.replayLoader = null;\n if (!step || !adapter || chartTf === null) return;\n const feed = withResampling(adapter);\n const fetchBefore = feed.fetchHistoryBefore;\n const symbol = this.currentSymbol;\n const barMs = timeframeToMs(chartTf);\n const loader = new ReplayStepLoader({\n source: fetchBefore ? (before, limit) => fetchBefore.call(feed, symbol, step, before, limit) : null,\n step,\n ratio: Math.max(1, Math.round(barMs / timeframeToMs(step))),\n barMs,\n keep: (bars) => this.series.inHoursShown(bars),\n report: (payload) => this.eventBus.emit('replayStepsLoad', payload),\n });\n this.replayLoader = { key, loader };\n }\n\n /**\n * A paper account trades on the prices a forming bar has been drawn\n * through, forward only by (time, fraction): the rest of a bar given in\n * part, the bars a jump passes, then this one up to `fraction`.\n */\n private feedReplayForming(coarse: DataSeries, index: number, path: IntrabarPath, fraction: number): void {\n const session = this.replaySession;\n if (!session || !this.executionAdapter?.setMarkPrice) return;\n const fed = { time: session.fedTime, fraction: session.fedFraction };\n const feed = formingFeed(coarse, index, path, fraction, fed, (c, i) => this.replayPathOf(c, i));\n if (!feed) return;\n session.fedTime = feed.fed.time;\n session.fedFraction = feed.fed.fraction;\n this.feedReplayMarks(feed.marks);\n }\n\n /** Marks of a replay to a paper account in order, and the orders on the chart to the last of them. */\n private feedReplayMarks(marks: readonly ReplayMark[]): void {\n const adapter = this.executionAdapter;\n if (!adapter?.setMarkPrice) return;\n for (const [price, time] of marks) adapter.setMarkPrice(price, time);\n const price = marks[marks.length - 1]?.[0];\n if (price !== undefined) this.tradingManager.setCurrentPrice(price);\n }\n\n /** The store says the live series changed during a replay: the replay follows it, by time. */\n private followLiveChange(change: LiveChange): void {\n const session = this.replaySession;\n if (!session) return;\n // A bar corrected that the replay hasn't reached yet shows when it does.\n if (change.kind === 'bar' && !(session.until !== null && change.time < session.until)) return;\n this.rebaseReplay(change.kind);\n }\n\n /**\n * Rebuild the replay on the live series as it is now, at the time it had\n * reached: the last bar (or step) that closed by then, so nothing after it\n * shows. Indices are worked out again from the time. A time older than the\n * series pages older bars in first, while a history loader has them. A\n * replay held for a new series goes on as it was asked to; one held while\n * the hours change or a bar is corrected stays held.\n */\n private rebaseReplay(kind: LiveChange['kind']): void {\n const session = this.replaySession;\n if (!session) return;\n const full = this.series.inHoursShown(this.series.liveBars());\n if (full.length === 0) return;\n const windowStart = session.windowTime === null ? 0 : Math.min(firstAtOrAfter(full, session.windowTime), full.length - 1);\n const coarse = windowStart > 0 ? full.slice(windowStart) : full;\n this.useReplayLoader(session, false);\n const steps = this.replayStepsOver(session, coarse);\n const stepped = session.animate ? null : steps;\n const at = session.until === null ? 0 : positionAt(stepped ?? coarse, session.until);\n if (at < 0 && this.canPageBackForReplay(session)) {\n this.holdReplay();\n if (session.held) session.held.pagesBack++;\n queueMicrotask(() => void this.pageBackForReplay(session));\n return;\n }\n const reported = this.getReplayState();\n const stayHeld = session.held !== null && (kind === 'hours' || kind === 'bar');\n const goOn = session.held?.state ?? reported;\n if (!stayHeld) session.held = null;\n session.symbol = this.currentSymbol;\n // A `duration` keeps the time it had left (read before the clock takes the new steps).\n const remaining = this.replayManager.remainingMs();\n this.clearBarHolders();\n if (session.animate) mapReplayFeed(session, this.replayCoarse, coarse);\n const restarting = this.replayRestarting;\n this.replayRestarting = true;\n this.replayCause = kind === 'older' ? 'older' : 'series';\n try {\n this.buildReplay(coarse, steps, windowStart);\n this.restartReplayAt(Math.max(0, at), remaining, !stayHeld && goOn === 'playing');\n } finally {\n this.replayRestarting = restarting;\n this.replayCause = null;\n }\n const state = this.getReplayState();\n if (state !== reported && !restarting) this.eventBus.emit('replayState', { state });\n this.emitBars(this.series.length());\n }\n\n /** The replay just rebuilt, at step `at`: a `duration` pace takes the `remaining` ms it had left. */\n private restartReplayAt(at: number, remaining: number | null, play: boolean): void {\n this.replayManager.play({ paused: true, startIndex: at, ...(remaining !== null ? { duration: remaining } : {}) });\n if (play) this.replayManager.resume();\n }\n\n /** The pinned tooltip and the ruler hold bar positions, which a rebuilt replay moves. */\n private clearBarHolders(): void {\n if (this.pinnedTooltip.isPinned()) this.pinnedTooltip.unpin();\n this.measureOverlay.end();\n this.sessionBreaks.invalidateCache();\n }\n\n /** The host's finer steps over `coarse`, while they are of its symbol and fit its bars; else none. */\n private replayStepsOver(session: ReplaySession, coarse: DataSeries, starting = false): DataSeries | null {\n if (!session.steps || session.stepsSymbol !== this.currentSymbol) return null;\n // Only the hours shown: after-hours steps never reach the last regular bar.\n const steps = replaySteps(this.series.inHoursShown(session.steps), coarse);\n return steps && (starting || stepsFit(steps, coarse)) ? steps : null;\n }\n\n /** Whether a replay's time older than the series can page older bars in to reach it. */\n private canPageBackForReplay(session: ReplaySession): boolean {\n return this.history.hasLoader() && this.history.hasMore() && (session.held?.pagesBack ?? 0) < MAX_REPLAY_PAGES_BACK;\n }\n\n /** One page of older bars for a held replay; when none comes, it goes on from the first bar there is. */\n private async pageBackForReplay(session: ReplaySession): Promise<void> {\n if (this.replaySession !== session || !session.held) return;\n // A page that lands rebuilds the replay itself (the store tells it).\n const added = await this.history.loadMore();\n if (added > 0 || this.replaySession !== session || !session.held) return;\n session.held.pagesBack = MAX_REPLAY_PAGES_BACK;\n this.rebaseReplay('older');\n }\n\n /** A replay waits while a new series loads: its clock stops, untold, and it goes on as it was once the series is in. */\n private holdReplay(): void {\n const session = this.replaySession;\n if (!session || session.held) return;\n const state = this.replayManager.getState() === 'playing' ? 'playing' : 'paused';\n const restarting = this.replayRestarting;\n this.replayRestarting = true;\n try {\n this.replayManager.pause();\n } finally {\n this.replayRestarting = restarting;\n }\n session.held = { state, pagesBack: 0 };\n }\n\n /** A series of `symbol` is on its way: a replay ends if a paper account trades it on another symbol, else waits for it. */\n private holdReplayFor(symbol: string): void {\n if (!this.replaySession) return;\n if (this.replayEndsFor(symbol)) this.replayStop();\n else this.holdReplay();\n }\n\n /** Whether a replay ends on a series of `symbol`: a paper account trades it, and the symbol is another. */\n private replayEndsFor(symbol: string): boolean {\n const session = this.replaySession;\n return session !== null && this.replayTrades() && symbol !== session.symbol;\n }\n\n /** Whether history pages in now: always outside a replay; in one, over the whole series and not held. */\n private replayPages(): boolean {\n const session = this.replaySession;\n return session === null || (session.windowTime === null && session.held === null);\n }\n\n /** Markers and trades as far as a replay has shown, from its window on; `null` shows them all. */\n private revealReplayMarks(until: number | null): void {\n const from = until !== null && this.replayOffset > 0 ? this.replayCoarse?.[0]?.time ?? -Infinity : -Infinity;\n this.signalMarkerManager.setRevealUntil(until, from);\n this.tradeZoneManager.setRevealUntil(until, from);\n }\n\n /** Jump the replay to show the bars (or finer steps) that closed by `time`. */\n replaySeekToTime(time: number): void {\n if (!this.replaySession || !this.replayCoarse) return;\n // The last bar (or step) that closed by then: a coarser chart following a\n // finer one never shows a bar whole before it closes.\n this.replaySeek(Math.max(0, positionAt(this.replayStepsSeries ?? this.replayCoarse, time)));\n }\n\n /** A replay step's price to an adapter that takes a mark (a paper account), and to the orders on the chart. */\n private feedReplayPrice(price: number, time?: number): void {\n const adapter = this.executionAdapter;\n if (!adapter?.setMarkPrice) return;\n adapter.setMarkPrice(price, time);\n this.tradingManager.setCurrentPrice(price);\n }\n\n /** A paper account trading on the replay goes back to the live price (the orders too), and its fills to the clock. */\n private returnMarkToLive(): void {\n const session = this.replaySession;\n if (!session || !this.replayTrades()) return;\n const live = this.series.liveBars();\n const price = session.price?.price ?? live[live.length - 1]?.close;\n if (price !== undefined) this.feedReplayPrice(price);\n }\n\n /**\n * A paper account trades on the replayed prices and times, forward only:\n * it gets every step past the latest time it has seen (a jump ahead passes\n * them all, a long one in chunks), and nothing after a seek back or a\n * restart from an earlier bar until the replay is past that time again.\n */\n private feedReplaySteps(series: DataSeries, index: number): void {\n const session = this.replaySession;\n if (!session || !this.executionAdapter?.setMarkPrice) return;\n const feed = stepFeed(series, index, { time: session.fedTime, fraction: session.fedFraction });\n if (!feed) return;\n session.fedTime = feed.fed.time;\n this.feedReplayMarks(feed.marks);\n }\n\n /**\n * The bar of the chart's series the replay is on (the forming one), or -1\n * without a replay.\n */\n getReplayBarIndex(): number {\n if (!this.replaySession) return -1;\n if (this.replayForming) return this.replayForming.index;\n const { current } = this.replayManager.getProgress();\n const steps = this.replayStepsSeries;\n return steps && this.replayCoarse ? barAt(this.replayCoarse, steps[current]?.time ?? -Infinity) : current;\n }\n\n /** Jump the replay to the end of bar `barIndex` of the chart's series (a click on it). */\n replaySeekToBar(barIndex: number): void {\n const steps = this.replayStepsSeries;\n this.replaySeek(steps && this.replayCoarse ? lastStepOfBar(steps, this.replayCoarse, barIndex) : barIndex);\n }\n\n replayPause(): void {\n if (!this.setHeldState('paused')) this.replayManager.pause();\n }\n replayResume(): void {\n if (!this.setHeldState('playing')) this.replayManager.resume();\n }\n\n /** A replay held for a new series goes on in `state` once the series is in. False when none is held. */\n private setHeldState(state: 'playing' | 'paused'): boolean {\n const held = this.replaySession?.held;\n if (!held) return false;\n if (held.state !== state) {\n held.state = state;\n this.eventBus.emit('replayState', { state });\n }\n return true;\n }\n /** End the replay and return to the live series (with updates that arrived meanwhile). */\n replayStop(): void {\n const session = this.replaySession;\n const live = this.endReplaySession();\n if (!session || !live) return;\n this.loadSeries(live, true);\n if (session.price) this.currentPriceLine.setPrice(session.price.price, session.price.previousClose);\n }\n\n /** Whether a replay session is open (playing or paused). */\n isReplayActive(): boolean {\n return this.replaySession !== null;\n }\n\n /** The plot area in CSS pixels, relative to the chart container (no axes). */\n getPlotRect(): { x: number; y: number; width: number; height: number } {\n const { x, y, width, height } = this.viewport.getState().chartRect;\n return { x, y, width, height };\n }\n\n /**\n * The box a drawing covers on screen, in CSS pixels relative to the chart\n * container, cut to the plot: where a host puts a toolbar or a note over\n * it. Lines that run across the whole plot (horizontal, vertical, cross)\n * span it on that side. Its tags and handles count once it has been drawn,\n * where they sit off its points. Null for an unknown or hidden drawing (or one kept\n * off this interval), or one that is all off screen.\n */\n getDrawingRect(id: string): { x: number; y: number; width: number; height: number } | null {\n const drawing = this.drawingManager.getDrawings().find((d) => d.id === id);\n if (!drawing || !this.drawingManager.isShown(id) || drawing.anchors.length === 0) return null;\n const vs = drawing.paneId === undefined ? { ...this.viewport.getState(), data: this.getDisplayData() } : this.drawingPaneViewport(drawing.paneId);\n if (!vs) return null;\n const plot = vs.chartRect;\n let left = Infinity;\n let right = -Infinity;\n let top = Infinity;\n let bottom = -Infinity;\n for (const anchor of drawing.anchors) {\n // A drawing kept to the screen holds its points as shares of the plot.\n const { x, y } = this.drawingManager.anchorPoint(drawing.type, anchor, vs);\n left = Math.min(left, x);\n right = Math.max(right, x);\n top = Math.min(top, y);\n bottom = Math.max(bottom, y);\n }\n if (FULL_WIDTH_DRAWINGS.has(drawing.type)) [left, right] = [plot.x, plot.x + plot.width];\n else if (drawing.type === 'horizontalRay') right = plot.x + plot.width;\n if (FULL_HEIGHT_DRAWINGS.has(drawing.type)) [top, bottom] = [plot.y, plot.y + plot.height];\n // All off screen: its tags, kept on the plot, don't make it shown.\n if (right < plot.x || left > plot.x + plot.width || bottom < plot.y || top > plot.y + plot.height) return null;\n // Its tags and handles where they sit off its points: a toolbar over it keeps clear of them too.\n const drawn = this.drawingManager.drawnExtent(id);\n if (drawn) {\n left = Math.min(left, drawn.x);\n right = Math.max(right, drawn.x + drawn.width);\n top = Math.min(top, drawn.y);\n bottom = Math.max(bottom, drawn.y + drawn.height);\n }\n left = Math.max(left, plot.x);\n right = Math.min(right, plot.x + plot.width);\n top = Math.max(top, plot.y);\n bottom = Math.min(bottom, plot.y + plot.height);\n if (!(left <= right && top <= bottom)) return null;\n return { x: left, y: top, width: right - left, height: bottom - top };\n }\n\n /**\n * Stop the replay clock and drop the session without touching the bars\n * shown. Returns the live series it kept aside; null without a replay.\n */\n private endReplaySession(): DataSeries | null {\n if (!this.replaySession) return null;\n this.returnMarkToLive();\n const live = this.series.endReplay();\n this.replaySession = null;\n this.replayStepsSeries = null;\n this.replayCoarse = null;\n this.replayBarUnsub?.();\n this.replayBarUnsub = null;\n this.replayPhaseUnsub?.();\n this.replayPhaseUnsub = null;\n this.replayLoader?.loader.dispose();\n this.replayLoader = null;\n this.replayPieces = null;\n this.replayForming = null;\n this.replayManager.stop();\n this.replayOffset = 0;\n this.revealReplayMarks(null);\n return live;\n }\n\n replaySeek(index: number): void {\n this.replaySeeking = true;\n try {\n this.replayManager.seekTo(index);\n } finally {\n this.replaySeeking = false;\n }\n }\n setReplaySpeed(speed: number): void { this.replayManager.setSpeed(speed); }\n /** A replay held while the next series loads reports the state it goes on in. */\n getReplayState(): 'playing' | 'paused' | 'stopped' {\n return this.replaySession?.held?.state ?? this.replayManager.getState();\n }\n\n /**\n * Where the replay is: `current` and `total` count its steps, `time` is\n * when the bar forming now opened and `until` the time it has shown\n * everything before (null without a replay, or before its first step).\n * Indices are of the series replayed now and change when it does; the\n * time doesn't: `replaySeekToTime(until)` comes back to it.\n */\n getReplayProgress(): { current: number; total: number; percent: number; time: number | null; until: number | null } {\n const session = this.replaySession;\n const bar = session ? this.replayCoarse?.[this.getReplayBarIndex()] : undefined;\n return { ...this.replayManager.getProgress(), time: session?.until != null && bar ? bar.time : null, until: session?.until ?? null };\n }\n\n // --- Save / Load ---\n\n /** Everything `loadState` restores: chart type, theme, drawings, indicators, alerts. */\n private captureSnapshot(): import('@tradecanvas/core').ChartSnapshot {\n return ChartStateManager.capture(\n {\n // A drawing of a pane that is gone is kept for undo, not saved: a\n // reload's new ids could put it on another pane.\n getDrawings: () => [\n ...this.getDrawings().filter((d) => d.paneId === undefined || this.layoutManager.getPanels().some((panel) => panel.id === d.paneId)),\n ...this.keptPaneDrawings,\n ],\n // The theme as set: the overrides go apart, the user's saved by theme.\n getTheme: () => this.themeManager.getBaseTheme(),\n getAlerts: () => this.getAlerts(),\n getIndicators: () => this.getIndicatorSetup(),\n getOverrides: () => this.themeManager.getUserOverrides(),\n },\n { chartType: this.options.chartType, chartTypeOptions: this.getChartTypeOptions(), symbol: this.currentSymbol || undefined },\n );\n }\n\n /**\n * Put `list` in place of the chart's indicators (a layout, a template, an\n * undo), with their panes as they were: size, order, fold, maximise.\n * `keepIds` keeps each instance's id (an undo): the indicators still alike\n * are changed in place, not taken down and put back. Otherwise the ids are\n * new and the map says old → new.\n */\n private restoreIndicators(list: readonly SnapshotIndicator[], keepIds: boolean): Map<string, string> {\n // Hosts and sources first, so a pane member finds its pane and a reader its line.\n const ordered = dependencyOrder(list);\n const instanceIds = new Map<string, string>();\n this.unrestoredIndicators = [];\n const known = new Set(this.indicatorEngine.getAvailableIndicators().map((d) => d.id));\n const restored: [SnapshotIndicator, string][] = [];\n const kept = new Set<string>();\n\n if (keepIds) {\n // Take down only what differs in kind or place; the rest is edited in place.\n const current = this.getIndicatorSetup();\n for (const id of keptInPlace(current, ordered)) kept.add(id);\n for (const cur of current) {\n if (!kept.has(cur.instanceId)) this.removeIndicator(cur.instanceId);\n }\n // A reader taken down with its source comes back with it, and so does\n // one drawn in the pane of an indicator taken down (it would stay where\n // the take-down left it): until none is left to take down.\n const hostOf = new Map(current.map((cur) => [cur.instanceId, cur.pane]));\n for (let changed = true; changed;) {\n changed = false;\n for (const id of [...kept]) {\n const host = hostOf.get(id);\n if (this.indicatorEngine.getIndicatorConfig(id) && !(host && !kept.has(host))) continue;\n if (this.indicatorEngine.getIndicatorConfig(id)) this.removeIndicator(id);\n kept.delete(id);\n changed = true;\n }\n }\n } else {\n for (const active of this.indicatorEngine.getActiveIndicators()) this.removeIndicator(active.instanceId);\n }\n\n // Lines read from other indicators and shared panes refer to old ids: follow them.\n const renamed = (params: Record<string, unknown>) => renameSources(params, instanceIds);\n\n for (const ind of ordered) {\n let instanceId: string | null = null;\n if (kept.has(ind.instanceId)) {\n instanceId = ind.instanceId;\n const params = this.indicatorEngine.getIndicatorConfig(instanceId)?.params ?? {};\n const changed: Record<string, number | string | boolean> = {};\n for (const [name, value] of Object.entries(renamed(ind.params))) if (params[name] !== value) changed[name] = value;\n if (Object.keys(changed).length > 0) this.updateIndicator(instanceId, changed);\n } else {\n const position = (['top', 'bottom', 'left', 'right'] as const).find((p) => p === ind.position) ?? 'bottom';\n try {\n if (!known.has(ind.id)) throw new Error('no such indicator is registered');\n const pane = ind.pane ? instanceIds.get(ind.pane) : undefined;\n instanceId = this.addIndicatorNow(ind.id, renamed(ind.params), position, {\n ...(pane ? { pane } : {}),\n ...(ind.scale === 'left' ? { scale: 'left' as const } : {}),\n ...(keepIds ? { instanceId: ind.instanceId } : {}),\n });\n } catch (err) {\n console.warn(`Layout indicator \"${ind.id}\" kept but not shown:`, err);\n }\n }\n if (!instanceId) {\n this.unrestoredIndicators.push(ind);\n continue;\n }\n instanceIds.set(ind.instanceId, instanceId);\n restored.push([ind, instanceId]);\n this.restoreLooks(instanceId, ind);\n }\n // Restored hosts and sources first, they go back in the order they were\n // listed in: the legend, the drawing order and a pane's stack stay as saved.\n this.indicatorEngine.orderIndicators(list.flatMap((ind) => instanceIds.get(ind.instanceId) ?? []));\n restorePanes(this.layoutManager, restored);\n this.updateViewportAndRender();\n return instanceIds;\n }\n\n /** An indicator's style, visibility and levels as `ind` has them, changing only what differs. */\n private restoreLooks(instanceId: string, ind: SnapshotIndicator): void {\n const { plots: havePlots, ...have } = this.indicatorEngine.getIndicatorStyle(instanceId) ?? { colors: [], lineWidths: [], opacity: 1 };\n const { plots: wantPlots, ...want } = ind.style ?? {};\n if (ind.style && JSON.stringify({ ...have, ...want }) !== JSON.stringify(have)) this.updateIndicatorStyle(instanceId, want);\n // Plot styles are merged by updateIndicatorStyle: here they are put back whole.\n if (JSON.stringify(wantPlots ?? {}) !== JSON.stringify(havePlots ?? {})) {\n this.indicatorEngine.setPlotStyles(instanceId, wantPlots ?? null);\n this.engine.requestRender();\n }\n const config = this.indicatorEngine.getIndicatorConfig(instanceId);\n if ((ind.visible ?? true) !== (config?.visible ?? true)) this.setIndicatorVisible(instanceId, ind.visible ?? true);\n const levels = ind.levels ?? null;\n if (JSON.stringify(levels) !== JSON.stringify(config?.levels ?? null)) this.setIndicatorLevels(instanceId, levels);\n const intervals = ind.intervals ?? null;\n if (JSON.stringify(intervals) !== JSON.stringify(config?.intervals ?? null)) this.setIndicatorIntervalsNow(instanceId, intervals);\n this.setPaneStyleNow(instanceId, ind.paneStyle ? readPaneStyle(ind.paneStyle) : null);\n }\n\n /**\n * The chart's indicators as a layout or a template keeps them: inputs,\n * style, levels, scale and panes (shared, size, order, fold, maximise).\n */\n getIndicatorSetup(): import('@tradecanvas/core').SnapshotIndicator[] {\n return [\n ...captureIndicatorSetup(this.indicatorEngine, this.layoutManager, (id) => this.panes.style(id) ?? null),\n ...this.unrestoredIndicators,\n ];\n }\n\n /**\n * Put a set of indicators (from `getIndicatorSetup`, a template) in place\n * of the chart's, as one undo step.\n */\n applyIndicatorSetup(list: readonly SnapshotIndicator[]): void {\n if (!this.features.indicators) return;\n this.indicatorHistory.record(undefined, () => {\n const before = new Map(this.indicatorEngine.getActiveIndicators().map((a) => [a.instanceId, a.id]));\n const instanceIds = this.indicatorHistory.quietly(() => this.restoreIndicators(list, false));\n this.followIndicatorAlerts(before, [...instanceIds.values()]);\n this.markStateChanged();\n });\n }\n\n /**\n * Alerts on the lines of indicators that went (`before`: id → kind) move to\n * the one new indicator of the same kind (`added`), when there is just one;\n * others stay as they are, as alerts on a removed indicator do.\n */\n private followIndicatorAlerts(before: ReadonlyMap<string, string>, added: readonly string[]): void {\n if (!this.features.alerts) return;\n moveAlertsToHeirs(this.alertManager, before, added, (id) => this.indicatorEngine.getIndicatorConfig(id)?.id);\n }\n\n /**\n * Start an edit of one indicator, as its settings dialog does: its changes\n * until `endIndicatorEdit` are one undo step, and none when they end where\n * they began (a cancel that put everything back).\n */\n beginIndicatorEdit(instanceId: string): void {\n this.indicatorHistory.begin(instanceId);\n }\n\n /** End the edit `beginIndicatorEdit` started, as one undo step when anything changed. */\n endIndicatorEdit(): void {\n this.indicatorHistory.end();\n }\n\n /** Undo or redo an indicator step: the indicators as they were, under their own ids. */\n private applyIndicatorStep(action: import('@tradecanvas/core').UndoableAction, direction: 'undo' | 'redo'): void {\n const step = action.indicators;\n if (!step) return;\n // Indicators taken down on the way hand their panes on, but the drawings\n // stay put: the step's own renames below are where they belong.\n this.replayingIndicatorStep = true;\n try {\n this.indicatorHistory.quietly(() => this.restoreIndicators(direction === 'undo' ? step.before : step.after, true));\n } finally {\n this.replayingIndicatorStep = false;\n }\n // The drawings on a pane another indicator took over go with it, either way.\n const renames = step.renames ?? [];\n const walk = direction === 'undo' ? [...renames].reverse().map(([from, to]) => [to, from] as const) : renames;\n for (const [from, to] of walk) this.drawingManager.renamePanes(new Map([[from, to]]));\n this.markStateChanged();\n }\n\n /** Drawings of indicators a loaded layout could not restore: not on the chart, saved again as they were. */\n private keptPaneDrawings: DrawingState[] = [];\n /** An undo or a redo is putting the indicators back: their drawings move only by the step's renames. */\n private replayingIndicatorStep = false;\n\n /** Whether pane `paneId` is on the chart and takes a `type` drawing: no price-only tool goes on a pane. */\n private takesDrawing(paneId: string, type: string): boolean {\n return takesDrawing(this.layoutManager.getPanels(), paneId, !!this.drawingManager.getDescriptor(type)?.priceOnly);\n }\n\n /** Hand pane `from` to the indicator `to`: its layout and its drawings. */\n private renamePane(from: string, to: string): void {\n this.layoutManager.renamePanel(from, to);\n if (!this.replayingIndicatorStep) this.drawingManager.renamePanes(new Map([[from, to]]));\n this.indicatorHistory.renamed(from, to);\n }\n\n /**\n * The pane a drawing begun at `pos` goes on: undefined the price pane, an\n * indicator pane's id, null where none takes drawings (an axis, a header,\n * a side or folded pane).\n */\n private drawingPaneAt(pos: { x: number; y: number }): string | null | undefined {\n return drawingPaneAt(pos, this.viewport.getState().chartRect, this.layoutManager.getPanels(), (id) => this.drawingPaneViewport(id));\n }\n\n /**\n * The viewport a pane's drawings are drawn and read with: its plot under\n * the header, its value scale, the bars' times. Null for a pane that takes\n * none: on a side (its x is not the time), folded, or too short to draw in.\n */\n private drawingPaneViewport(paneId: string): ViewportState | null {\n const panel = this.layoutManager.getPanels().find((p) => p.id === paneId);\n return drawingPaneViewport(\n panel,\n !!panel && this.layoutManager.isPanelShownCollapsed(paneId),\n () => this.buildPanelRenderInfos().find((p) => p.instanceId === paneId)?.viewport,\n () => this.getDisplayData(),\n );\n }\n\n /**\n * The layout as JSON: the drawings, the indicators and their panes, the\n * alerts, the chart type and its settings, the theme and the style\n * overrides, the symbol; not the bars. With `key`, also written to\n * `localStorage[key]`, the key as given (it throws where localStorage does:\n * blocked, or full). Null with `features.saveLoad` off.\n */\n saveState(key?: string): string | null {\n if (!this.features.saveLoad) return null;\n const snapshot = this.captureSnapshot();\n const json = ChartStateManager.serialize(snapshot);\n if (key) ChartStateManager.saveToStorage(key, snapshot);\n return json;\n }\n\n /**\n * A layout `saveState` gave, in place of the chart's own drawings,\n * indicators, alerts, chart type and style overrides. The bars and the\n * symbol stay; so does the theme, unless the layout's is `'dark'` or\n * `'light'`.\n */\n loadState(json: string): void {\n if (!this.features.saveLoad) return;\n const snapshot = ChartStateManager.deserialize(json);\n // The saved type's settings (none saved: the defaults).\n this.chartTypeOptions = snapshot.chartTypeOptions ?? {};\n this.display.reset();\n if (snapshot.chartType) this.setChartType(snapshot.chartType);\n if (snapshot.drawings) this.setDrawings(snapshot.drawings);\n // A save names its theme: a built-in one loads, another (only its name was\n // kept) leaves the chart's theme as it is.\n if (snapshot.theme === 'dark' || snapshot.theme === 'light') this.setTheme(snapshot.theme);\n // The user's overrides as saved. A save from before them (version 1 or 2) leaves them alone.\n if (snapshot.version >= 3 && this.themeManager.setUserOverrides(snapshot.overrides ?? {})) this.styleChanged('user');\n\n // Indicators get new instance ids: remember old → new for alert channels.\n // Version-1 saves never captured indicators, so they leave them alone.\n const instanceIds = snapshot.version >= 2 && this.features.indicators\n ? this.indicatorHistory.quietly(() => this.restoreIndicators(snapshot.indicators, false))\n : new Map<string, string>();\n // A drawing on a pane follows the pane's indicator to its new id. One of\n // an indicator the layout could not restore is kept aside, unseen, and\n // saved again as it was; one of a pane the layout lacks goes.\n this.drawingManager.renamePanes(instanceIds);\n const unrestored = new Set(this.unrestoredIndicators.map((ind) => ind.instanceId));\n const drawings = this.drawingManager.getDrawings();\n const placed = drawings.filter((d) => d.paneId === undefined || this.takesDrawing(d.paneId, d.type));\n this.keptPaneDrawings = drawings.filter((d) => d.paneId !== undefined && unrestored.has(d.paneId));\n if (placed.length !== drawings.length) this.drawingManager.setDrawings(placed);\n // Another layout: the old steps no longer apply to it.\n this.undoRedoManager.clear();\n\n if (snapshot.alerts && this.features.alerts) {\n restoreLayoutAlerts(this.alertManager, snapshot.alerts, instanceIds, (id) => this.drawingManager.hasPriceLevels(id), (err) => {\n console.warn('Layout alert left out:', err);\n });\n }\n this.markStateChanged();\n }\n\n /** The layout saved under `localStorage[key]` (`saveState(key)`, `setAutoSave(key)`); false when there is none, and it throws on one that isn't JSON. */\n loadStateFromStorage(key: string): boolean {\n if (!this.features.saveLoad) return false;\n const snapshot = ChartStateManager.loadFromStorage(key);\n if (!snapshot) return false;\n this.loadState(ChartStateManager.serialize(snapshot));\n return true;\n }\n\n downloadState(filename?: string): void {\n if (!this.features.saveLoad) return;\n ChartStateManager.downloadFile(this.captureSnapshot(), filename);\n }\n\n async loadStateFromFile(): Promise<void> {\n if (!this.features.saveLoad) return;\n const snapshot = await ChartStateManager.loadFromFile();\n this.loadState(ChartStateManager.serialize(snapshot));\n }\n\n // --- Locale ---\n\n setLocale(locale: Locale): void {\n setGlobalLocale(locale);\n this.engine.requestRender();\n }\n\n /**\n * Update the BCP 47 locale used for number formatting\n * (e.g. price axis labels, crosshair value badges, month names on the time axis).\n * Defaults to 'en-US'. Example values: 'de-DE', 'vi-VN', 'fr-FR'.\n */\n setNumberLocale(locale: string): void {\n this.numberLocale = locale;\n this.viewport.setNumberLocale(locale);\n this.priceAxis.setLocale(locale);\n this.leftPriceAxis.setLocale(locale);\n this.crosshairHandler.setLocale(locale);\n this.timeAxis.setLocale(locale);\n this.chartLegend.setLocale(locale);\n this.crosshairTooltip.setLocale(locale);\n this.pinnedTooltip.setLocale(locale);\n this.currentPriceLine.setLocale(locale);\n // Separators change label widths; the axis may need to refit.\n this.updateViewportAndRender();\n }\n\n getNumberLocale(): string {\n return this.numberLocale;\n }\n\n // --- Market ---\n\n /**\n * The rules of the market the symbol trades on (`MARKET_HOSE`,\n * `MARKET_NYSE`… or your own): its price steps for `roundPrice` and the\n * order ticket, its decimals on every price, its colours on the candles,\n * its daily limits once `setPriceLimits` gives the reference.\n *\n * The axis keeps its zone: `setTimeZone('exchange')` shows the market's\n * (the symbol's own, when `setSymbolInfo` gave one). That zone also dates\n * daily bars, and starts the days a `ChartWidget` builds from its bars.\n * Numbers keep the chart's locale (`numberLocale`). Sends `marketChange`.\n *\n * @example\n * chart.setMarket(MARKET_HOSE);\n * chart.setTimeZone('exchange'); // Asia/Ho_Chi_Minh\n * chart.setPriceLimits(25.35); // yesterday's close: ceiling 27.1, floor 23.6\n */\n setMarket(config: MarketConfig): void {\n this.marketConfig = config;\n // The host names the zone: the indicators wait for no symbol's.\n this.studyZoneHeld = false;\n this.endStudyZoneWait();\n // `'exchange'` shows the market's zone when the symbol names none.\n if (this.timeZoneSetting === EXCHANGE_TIME_ZONE) this.applyTimeZone();\n this.applyExchangeZone();\n\n // The market's colours on the candles; volume keeps the theme's own.\n if (config.colorScheme) {\n this.themeManager.setTheme(createMarketTheme(this.themeManager.getBaseTheme(), config.colorScheme));\n }\n\n // Apply price precision to trading, alerts, and price line\n if (config.pricePrecision !== undefined) this.applyPricePrecision(config.pricePrecision);\n\n // Longer price labels may need a wider axis.\n this.updateViewportAndRender();\n this.eventBus.emit('marketChange', { market: config });\n }\n\n /** Prices show `precision` decimals everywhere; null goes back to fitting the price range. */\n private applyPricePrecision(precision: number | null): void {\n this.marketPricePrecision = precision;\n // On the viewport for what draws from it alone: drawing labels, price\n // limits, pivots, the measure, the market profile and plugins.\n this.viewport.setPricePrecision(precision);\n this.pinnedTooltip.setPricePrecision(precision);\n this.priceAxis.setPricePrecision(precision);\n this.tradingManager.setConfig({ pricePrecision: precision ?? undefined });\n this.alertManager.setPricePrecision(precision);\n this.tradeZoneManager.setPricePrecision(precision);\n this.streamManager?.priceLine.setPricePrecision(precision);\n this.currentPriceLine.setPricePrecision(precision);\n this.crosshairHandler.setPricePrecision(precision);\n this.chartLegend.setPricePrecision(precision);\n this.crosshairTooltip.setPricePrecision(precision);\n }\n\n getMarket(): MarketConfig | null {\n return this.marketConfig;\n }\n\n /**\n * The day's ceiling, floor and reference prices, from the market's limits\n * and the reference `setPriceLimits` gave; null without them.\n */\n getPriceLimits(): PriceLimitInfo | null {\n const market = this.marketConfig;\n if (!market?.priceLimits?.enabled || !market.priceLimits.referencePrice) return null;\n return computePriceLimits(market.priceLimits.referencePrice, market);\n }\n\n /** The day's reference price, for the limits; a later `setMarket` starts without one. */\n setPriceLimits(referencePrice: number): void {\n if (!this.marketConfig?.priceLimits) return;\n // A copy: the market given may be a preset other charts share.\n this.marketConfig = {\n ...this.marketConfig,\n priceLimits: { ...this.marketConfig.priceLimits, referencePrice },\n };\n // The lines are drawn from the render context, not read each frame: give it the new ones.\n this.syncRenderContext();\n this.engine.requestRender();\n }\n\n // --- Features ---\n\n /** Get the resolved feature configuration */\n getFeatures(): Readonly<Required<FeaturesConfig>> {\n return this.features;\n }\n\n /** Update feature flags at runtime. Only provided keys are changed. */\n setFeatures(patch: Partial<FeaturesConfig>): void {\n const legendBottom = this.getLegendBottom();\n Object.assign(this.features, patch);\n // The legend switched off or on: what is stacked under it moves, as when it is drawn another height.\n if (patch.legend !== undefined && this.getLegendBottom() !== legendBottom) {\n this.eventBus.emit('legendResize', { bottom: this.getLegendBottom() });\n }\n\n // Apply immediate side-effects\n if (patch.crosshair === false || patch.crosshairTooltip === false) {\n this.crosshairTooltip.hide();\n }\n if (patch.priceAxisAddButton !== undefined || patch.crosshair !== undefined) {\n this.applyPriceAxisAdd();\n this.engine.requestRender(LayerType.Overlay);\n }\n if (patch.grid !== undefined) {\n this.engine.requestRender(LayerType.Background);\n }\n if (patch.volume !== undefined) {\n this.volumeRenderer.setVisible(patch.volume);\n this.updateViewportAndRender();\n }\n if (patch.drawings === false) {\n this.drawingManager.setActiveTool(null);\n }\n if (patch.trading === false) {\n this.tradingManager.setOrders([]);\n this.tradingManager.setPositions([]);\n }\n if (patch.keyboard !== undefined) {\n this.keyboardHandler?.setEnabled(patch.keyboard);\n }\n\n this.engine.requestRender();\n }\n\n // --- Lifecycle ---\n\n resize(): void {\n const size = this.engine.dprManager.readContainerSize();\n if (size.width <= 0 || size.height <= 0) return;\n this.containerSizeCache = size;\n this.containerSizeCacheTime = Date.now();\n // Resize canvas layers immediately (don't wait for DPRManager's debounced callback)\n const dpr = this.engine.dprManager.getDpr();\n this.engine.layerManager.resize(size, dpr);\n // Check if viewport was at the end before resize\n const wasAtEnd = this.viewport.isAtEnd();\n this.viewport.resize(size.width, size.height);\n this.layoutManager.resize(size.width, size.height);\n this.updateViewportAndRender(wasAtEnd);\n this.eventBus.emit('resize', size);\n }\n\n destroy(): void {\n this.disposed = true;\n this.stopWatchingVisibility?.();\n this.stopWatchingVisibility = null;\n this.waitForGpu(null);\n if (this.countdownInterval) clearInterval(this.countdownInterval);\n // A loader of the host's own: no page lands in, or is asked for by, a destroyed chart.\n this.history.setLoader(null);\n this.disableAutoSave();\n this.disconnectStream();\n this.disconnectExecution();\n if (this.onWindowKeyDown) {\n window.removeEventListener('keydown', this.onWindowKeyDown);\n this.onWindowKeyDown = null;\n }\n this.keyboardHandler = null;\n this.a11y?.destroy();\n this.a11y = null;\n this.interactionManager.detach();\n this.tradingManager.destroy();\n this.animator.dispose();\n this.crosshairTooltip.destroy();\n this.pinnedTooltip.destroy();\n this.replayRestarting = true; // going away: no word to the others\n this.replayManager.dispose();\n this.alertManager.dispose();\n this.undoRedoManager.clear();\n this.engine.destroy();\n this.eventBus.destroy();\n this.labelWidths.destroy();\n this.container.innerHTML = '';\n }\n\n // --- Internal ---\n\n private createChartRenderer(type: ChartType | string): ChartRendererInterface {\n const renderer = resolveRenderer(type, (t) => this.pluginManager.getChartType(t));\n if (renderer instanceof HiLoRenderer) renderer.setPriceText((p) => this.formatPrice(p));\n return renderer;\n }\n\n /**\n * The bars the crosshair reports: the data bars where each drawn bar is one\n * (candles, Heikin-Ashi: the real prices), the series drawn where it isn't\n * (Renko bricks, Kagi lines), whose slots are not data indices.\n */\n private crosshairSeries(): DataSeries {\n return this.isTimeAligned() ? this.series.bars() : this.getDisplayData();\n }\n\n private getDisplayData(): DataSeries {\n return this.display.get();\n }\n\n /** Recompute indicators from bar `from` on, and say so (live ticks, closes, replay steps). */\n private recalcIndicatorsFrom(data: DataSeries, from: number): void {\n // A new bar can add, move or take away bricks anywhere near the end: all of them again.\n if (!this.isTimeAligned()) return this.recalcIndicators(data);\n this.indicatorEngine.recalculateFrom(data, from);\n this.indicatorsOnDrawn = null;\n this.announceIndicatorUpdate(from);\n }\n\n private recalcIndicators(data: DataSeries): void {\n const bars = this.indicatorBars(data);\n this.indicatorEngine.recalculateAll(bars);\n this.indicatorsOnDrawn = bars === data ? null : bars;\n this.announceIndicatorUpdate(0);\n }\n\n /**\n * The bars an export writes, beside the indicator values worked out on\n * them: on Renko and the like the bricks drawn, each dated as the axis\n * dates it (by the bar it was finished on), not the bars under them.\n */\n private exportBars(): DataSeries {\n if (this.isTimeAligned()) return this.series.bars();\n const drawn = this.getDisplayData();\n const times = axisTimes(drawn);\n return drawn.map((bar, i) => ({ ...bar, time: times[i]?.time ?? bar.time }));\n }\n\n /**\n * The bars indicators are worked out on: the data bars where each drawn\n * bar is one, the series drawn on Renko, Kagi and the like, so a line sits\n * on the bricks and reads their closes, as the legend and the data window\n * read it at a brick.\n */\n private indicatorBars(data: DataSeries = this.series.bars()): DataSeries {\n return this.isTimeAligned() ? data : this.getDisplayData();\n }\n\n /** A new chart type, box or series drawn: the indicators follow the bars drawn. */\n private syncIndicatorBars(displayData: DataSeries): void {\n const aligned = this.isTimeAligned();\n if (aligned ? this.indicatorsOnDrawn === null : this.indicatorsOnDrawn === displayData) return;\n this.recalcIndicators(this.series.bars());\n }\n\n /**\n * Emit `indicatorUpdate` once per task, from the earliest changed bar, after\n * the update that caused it has finished and scheduled its redraw: a\n * listener can neither interrupt a live tick nor read the layout before\n * the chart has updated it.\n */\n private announceIndicatorUpdate(from: number): void {\n if (this.indicatorUpdateFrom !== null) {\n this.indicatorUpdateFrom = Math.min(this.indicatorUpdateFrom, from);\n return;\n }\n this.indicatorUpdateFrom = from;\n queueMicrotask(() => {\n const first = this.indicatorUpdateFrom;\n this.indicatorUpdateFrom = null;\n if (first !== null) this.eventBus.emit('indicatorUpdate', { from: first });\n });\n }\n\n /**\n * Draw a frame soon, though nothing the chart knows of changed: after a\n * plugin's own settings changed, say (an overlay's display mode), in a\n * quiet market with no ticks to redraw it. The frame refits the scales to\n * the data as a tick's would. Requests before the frame make one frame; a\n * destroyed chart ignores them.\n */\n requestRender(): void {\n this.scheduleRender();\n }\n\n /** Lightweight render for streaming updates. No layout resolve, no indicator recalc. */\n private scheduleRender(): void {\n if (this.renderScheduled || this.disposed) return;\n // Paused: nothing draws, so the refit waits for resume (or a screenshot).\n if (this.isSuspended()) {\n this.renderDeferred = true;\n return;\n }\n this.renderScheduled = true;\n requestAnimationFrame(() => {\n this.renderScheduled = false;\n if (this.disposed) return;\n if (this.isSuspended()) {\n this.renderDeferred = true;\n return;\n }\n this.refitForTick();\n });\n }\n\n private refitForTick(): void {\n // A tick moves indicator values: refit the panes' scales.\n this.panes.invalidateScales();\n this.fitDataAndAxes();\n this.syncRenderContext();\n this.engine.requestRender();\n this.emitViewportEvents();\n }\n\n /**\n * Push the display data into the viewport and apply auto-scale. The single\n * place auto-scale is computed — both the live-tick path (`scheduleRender`)\n * and the pan/zoom/data path (`updateViewportAndRender`) go through it.\n * They used to diverge: ticks fit candles only while pan/zoom also fit\n * overlay indicators (Bollinger, Keltner, Ichimoku…), so the price scale\n * jumped on every tick and jumped back on every pan frame.\n */\n private applyDataToViewport(scrollToEnd = false): DataSeries {\n const displayData = this.getDisplayData();\n this.syncIndicatorBars(displayData);\n\n // updateData first so dataLength is current for scrollToEnd — but only\n // fit the price range once the FINAL visible window is known. Fitting\n // before scrollToEnd measured the old window (e.g. deep history) and left\n // every bar of a freshly loaded series off-screen.\n this.viewport.updateData(displayData, false);\n if (scrollToEnd) {\n this.viewport.scrollToEnd();\n this.viewport.updateData(displayData, false);\n }\n\n const vs = this.viewport.getState();\n\n // Baseline for percentage / indexed-to-100 axis labels: the close of the\n // first visible bar. Ignored by other scale modes.\n if (displayData.length > 0) {\n const baseIdx = Math.max(0, Math.min(vs.visibleRange.from, displayData.length - 1));\n this.viewport.setScaleBaseline(displayData[baseIdx]?.close);\n }\n\n // Fitted on the new baseline: percent compare lines start there.\n if (this.priceFit.shouldFit()) this.fitPriceScale(displayData, this.viewport.getState());\n return displayData;\n }\n\n /** Fit the price scale to the bars on view and what else is on it: see `PriceFit.fit`. */\n private fitPriceScale(displayData: DataSeries, vs: ViewportState): void {\n this.priceFit.fit(this.viewport, displayData, vs, {\n overlayRange: (from, to) => this.indicatorEngine.getOverlayPriceRange(from, to),\n compareRange: (data, state) => (this.features.compareSymbols && this.comparesDrawn() ? this.compareRenderer.getPriceRange(data, state) : null),\n volumeRatio: (data, from, to) =>\n (this.features.volume && this.volumeRenderer.isVisible() && hasVolume(data, from, to) ? this.volumeRenderer.getHeightRatio() : null),\n });\n }\n\n /** Full update: resolve layout, update viewport, sync context, request render. */\n private updateViewportAndRender(scrollToEnd = false): void {\n // Before the layout and the scales: an indicator that reads another symbol by time is not drawn on bricks.\n this.indicatorEngine.setBarsTimed(this.comparesDrawn());\n this.layoutManager.setPanelsAside(this.panes.offChartType());\n this.panes.invalidate();\n\n this.layoutManager.setLeftAxisWidth(this.isLeftPriceScaleShown() ? this.leftAxisWidth : 0);\n const resolved = this.getResolvedLayout();\n this.viewport.setChartRect(resolved.mainChartRect);\n\n this.fitDataAndAxes(scrollToEnd);\n\n this.syncRenderContext();\n this.engine.requestRender();\n\n this.emitViewportEvents();\n }\n\n /**\n * Fit the data to the plot, then size both price scales to their labels. A\n * scale that changes width changes the plot, so the data is fit again —\n * before anything reads the layout for this frame.\n */\n private fitDataAndAxes(scrollToEnd = false): void {\n const shown = this.applyDataToViewport(scrollToEnd);\n const priceResized = this.fitPriceAxisWidth();\n this.leftViewport = this.computeLeftViewport();\n if (this.fitLeftAxisWidth() || priceResized) this.applyDataToViewport(scrollToEnd);\n // The new series' prices are on the axis: auto-scale off holds them from here.\n this.priceFit.settle(this.viewport.getState(), shown.length);\n }\n\n /** Size the left scale to its widest label; true when the layout changed. */\n private fitLeftAxisWidth(): boolean {\n const left = this.leftViewport;\n if (!left) return false;\n const theme = this.themeManager.getTheme();\n const required = requiredPriceAxisWidth({\n min: left.priceRange.min,\n max: left.priceRange.max,\n lastPrice: null,\n tagPrecision: null,\n locale: this.numberLocale,\n fontFamily: theme.font.family,\n fontSizeSmall: theme.font.sizeSmall,\n measure: (text, font) => this.measureText(text, font),\n });\n const next = nextPriceAxisWidth(this.leftAxisWidth, required);\n if (next === this.leftAxisWidth) return false;\n this.leftAxisWidth = next;\n this.layoutManager.setLeftAxisWidth(next);\n this.panes.invalidate();\n this.viewport.setChartRect(this.getResolvedLayout().mainChartRect);\n return true;\n }\n\n /**\n * Size the price axis to its widest label (tick labels, last-price tag,\n * crosshair pill), as an auto-width scale — so a sub-cent\n * price isn't clipped. Returns true when the width, and so the layout,\n * changed.\n */\n private fitPriceAxisWidth(): boolean {\n if (!this.features.priceAxis) return false;\n const { priceRange } = this.viewport.getState();\n const theme = this.themeManager.getTheme();\n const required = requiredPriceAxisWidth({\n min: priceRange.min,\n max: priceRange.max,\n lastPrice: this.currentPriceLine.getPrice(),\n tagPrecision: this.marketPricePrecision,\n locale: this.numberLocale,\n format: this.priceFormatter,\n scaleLabel: (price) => this.priceAxis.labelText(price, this.viewport.getState()),\n fontFamily: theme.font.family,\n fontSizeSmall: theme.font.sizeSmall,\n measure: (text, font) => this.measureText(text, font),\n });\n const next = nextPriceAxisWidth(this.priceAxisWidth, required);\n if (next === this.priceAxisWidth) return false;\n\n this.priceAxisWidth = next;\n this.layoutManager.setPriceAxisWidth(next);\n this.viewport.setPriceAxisWidth(next);\n this.panes.invalidate();\n this.viewport.setChartRect(this.getResolvedLayout().mainChartRect);\n return true;\n }\n\n /**\n * The width of a label as the chart draws it. It measures on the chart's own\n * canvas: in Chromium a canvas's text takes what the page passes down to the\n * element (the widget's tabular figures, wider in a proportional face),\n * which a canvas out of the page doesn't get, so the axis would be sized too\n * narrow for its crosshair pill. Such a measure costs more than one out of\n * the page, so its widths are kept; a canvas not yet in the page measures\n * afresh each time, as its widths change once it is.\n */\n private measureText(text: string, font: string): number {\n const scene = this.engine.layerManager.getLayer(LayerType.Main)?.ctx;\n if (scene?.canvas.isConnected) return this.labelWidths.get(text, font, () => measureWith(scene, text, font));\n const ctx = scene ?? this.detachedMeasureContext();\n if (!ctx) return text.length * 7; // no canvas (SSR/tests): a generous estimate\n return measureWith(ctx, text, font);\n }\n\n private detachedMeasureContext(): CanvasRenderingContext2D | null {\n if (this.measureCtx === undefined) {\n this.measureCtx = typeof document !== 'undefined'\n ? document.createElement('canvas').getContext('2d')\n : null;\n }\n return this.measureCtx;\n }\n\n /**\n * Fire the public viewport events (`visibleRangeChange`, `priceRangeChange`,\n * `zoomChange`) whenever the corresponding piece of viewport state changed\n * since the last render. Called at the end of every updateViewportAndRender,\n * so panning, zooming, resizing, and data updates all surface to consumers.\n */\n private emitViewportEvents(): void {\n const vs = this.viewport.getState();\n\n const from = Math.max(0, Math.floor(vs.visibleRange.from));\n const to = Math.max(from, Math.ceil(vs.visibleRange.to));\n if (\n !this.lastEmittedRange ||\n this.lastEmittedRange.from !== from ||\n this.lastEmittedRange.to !== to\n ) {\n this.lastEmittedRange = { from, to };\n this.eventBus.emit('visibleRangeChange', { from, to });\n }\n\n const { min, max } = vs.priceRange;\n if (\n !this.lastEmittedPriceRange ||\n this.lastEmittedPriceRange.min !== min ||\n this.lastEmittedPriceRange.max !== max\n ) {\n this.lastEmittedPriceRange = { min, max };\n this.eventBus.emit('priceRangeChange', { min, max });\n }\n\n // `barWidth` here is pixels-per-bar — the value Viewport.zoom() mutates.\n if (this.lastEmittedBarWidth !== vs.barWidth) {\n this.lastEmittedBarWidth = vs.barWidth;\n this.eventBus.emit('zoomChange', { barWidth: vs.barWidth });\n }\n\n this.checkHistory();\n }\n\n private getResolvedLayout(): ResolvedLayout {\n return this.panes.resolved();\n }\n\n /** Cache container size for 500ms to avoid layout thrashing on rapid calls */\n private cachedContainerSize(): { width: number; height: number } {\n const now = Date.now();\n if (!this.containerSizeCache || now - this.containerSizeCacheTime > 500) {\n this.containerSizeCache = this.engine.dprManager.getContainerSize();\n this.containerSizeCacheTime = now;\n }\n return this.containerSizeCache;\n }\n\n private buildPanelRenderInfos(): import('@tradecanvas/core').PanelRenderInfo[] {\n return this.panes.panels(() => this.viewport.getState());\n }\n\n private syncRenderContext(): void {\n // Keep the pinned tooltip anchored to its bar through pans / zooms.\n if (this.pinnedTooltip.isPinned()) {\n this.pinnedTooltip.reposition(\n this.viewport.getState(),\n null,\n null,\n this.themeManager.getTheme(),\n );\n }\n\n const panels = this.features.indicators ? this.buildPanelRenderInfos() : [];\n const timeAxisY = this.panes.timeAxisTop();\n\n const displayData = this.getDisplayData();\n this.leftViewport = this.computeLeftViewport();\n this.engine.setRenderContext({\n leftViewport: this.leftViewport,\n leftPriceAxis: this.leftPriceAxis,\n leftAxisWidth: this.leftAxisWidth,\n chartRenderer: this.mainSeriesVisible ? this.chartRenderer : null,\n priceLines: this.quoteLines(),\n gridRenderer: this.features.grid ? this.gridRenderer : null,\n priceAxis: this.features.priceAxis ? this.priceAxis : null,\n timeAxis: this.features.timeAxis ? this.timeAxis : null,\n crosshairHandler: this.features.crosshair ? this.crosshairHandler : null,\n indicatorEngine: this.features.indicators ? this.indicatorEngine : null,\n drawingRenderer: this.features.drawings ? this.drawingRenderer : null,\n tradingRenderer: this.features.trading ? this.tradingRenderer : null,\n currentPriceLine: this.currentPriceLine,\n chartLegend: this.features.legend ? this.chartLegend : null,\n volumeRenderer: this.features.volume ? this.volumeRenderer : null,\n volumeProfile: this.volumeProfile,\n marketProfile: this.marketProfile,\n depthHeatmap: this.depthHeatmap,\n periodLevels: this.periodLevels,\n pivotMarkers: this.pivotMarkers,\n watermark: this.features.watermark ? this.watermark : null,\n barCountdown: this.barCountdown,\n sessionBreaks: this.sessionBreaks,\n sessionShading: this.sessionShading,\n compareRenderer: this.comparesDrawn() ? this.compareRenderer : null,\n alertManager: this.features.alerts ? this.alertManager : null,\n measureOverlay: this.measureOverlay,\n selectionBoxOverlay: this.selectionBoxOverlay,\n priceAxisAddButton: this.priceAxisAddButton,\n signalMarkerManager: this.signalMarkerManager,\n tradeZoneManager: this.tradeZoneManager,\n panels,\n priceLimits: this.buildPriceLimits(),\n timeAxisY,\n // Attach data to the viewport so drawings/indicators can treat\n // `anchor.time` as a real timestamp and convert via timestampToBarIndex.\n viewport: { ...this.viewport.getState(), data: displayData },\n theme: this.themeManager.getTheme(),\n data: displayData,\n // A bar replaced in place leaves the array the same: the GPU's copy needs telling.\n seriesRevision: this.series.revision(),\n numberLocale: this.numberLocale,\n paneTitles: this.paneTitles,\n indicatorValueLabels: this.features.indicatorValueLabels,\n alignAxisLabels: this.features.alignAxisLabels,\n drawingAxisSpans: this.features.drawingAxisSpans,\n formatPrice: (price) => this.formatPrice(price),\n renderOverlayPlugins: (c, layer) => this.drawOverlayPlugins(c, layer),\n });\n }\n\n /** Draw registered overlay plugins for a layer (called by the engine). */\n private drawOverlayPlugins(\n ctx: CanvasRenderingContext2D,\n layer: 'main' | 'overlay' | 'ui',\n ): void {\n const overlays = overlaysForLayer(this.pluginManager.getOverlays(), layer);\n if (overlays.length === 0) return;\n const context = {\n viewport: this.viewport.getState(),\n data: this.getDisplayData(),\n theme: this.themeManager.getTheme(),\n };\n for (const overlay of overlays) {\n overlay.render(ctx, context);\n }\n }\n\n private buildPriceLimits() {\n const limits = this.getPriceLimits();\n if (!limits || !this.marketConfig) return null;\n return {\n ...limits,\n colors: this.marketConfig.colorScheme ? {\n ceiling: this.marketConfig.colorScheme.ceiling,\n floor: this.marketConfig.colorScheme.floor,\n reference: this.marketConfig.colorScheme.reference,\n } : undefined,\n };\n }\n}\n\ntype SnapshotIndicator = import('@tradecanvas/core').SnapshotIndicator;\n\n/** The width of `text` in `font` on `ctx`, whose own font is put back after. */\nfunction measureWith(ctx: CanvasRenderingContext2D, text: string, font: string): number {\n const drawing = ctx.font;\n ctx.font = font;\n const width = ctx.measureText(text).width;\n ctx.font = drawing;\n return width;\n}\n\n/** The longest family list `setFont` takes, characters. */\nconst MAX_FONT_FAMILY = 300;\n\n/** A font the chart can draw with: a family that isn't empty, sizes above zero. */\nfunction readFont(font: Partial<FontConfig>): Partial<FontConfig> {\n const out: Partial<FontConfig> = {};\n // A list of faces, nothing a style sheet could read more into (the family also reaches CSS text).\n const face = (v: unknown): v is string => typeof v === 'string' && v.trim() !== '' && v.length <= MAX_FONT_FAMILY && !/[;{}<>\\\\]|\\/\\*/.test(v);\n if (face(font.family)) out.family = font.family;\n if (face(font.textFamily)) out.textFamily = font.textFamily;\n for (const key of ['sizeSmall', 'sizeMedium', 'sizeLarge'] as const) {\n const size = font[key];\n if (typeof size === 'number' && Number.isFinite(size) && size > 0) out[key] = size;\n }\n return out;\n}\n\n/** Shapes the chart can draw: a corner radius from 0 to 999 px. */\nfunction readShapes(shapes: ShapeConfig): ShapeConfig {\n const r = shapes.tagRadius;\n return typeof r === 'number' && Number.isFinite(r) && r >= 0 ? { tagRadius: Math.min(r, 999) } : {};\n}\n\n/** An alert as its events carry it. */\nfunction alertPayload(alert: import('@tradecanvas/core').PriceAlert): import('@tradecanvas/commons').AlertPayload {\n return {\n id: alert.id,\n price: alert.price,\n condition: alert.condition,\n message: alert.message,\n triggered: alert.triggered,\n channel: alert.channel,\n label: alert.label,\n target: alert.target,\n drawingId: alert.drawingId,\n percent: alert.percent,\n bars: alert.bars,\n };\n}\n\n/** How far ahead of the bar forming a replay has its finer bars fetched, in playback time (ms). */\nconst REPLAY_LOOKAHEAD_MS = 8000;\n\n/** At most this many pages of older bars a replay loads to reach its time on a new series. */\nconst MAX_REPLAY_PAGES_BACK = 10;\n\n/** How many of the last finer steps are checked against the bars they build. */\nconst STEPS_FIT_SAMPLES = 50;\n\n/** How far outside its bar's range a step's close may lie (a share of the range), for rounding. */\nconst STEPS_FIT_TOLERANCE = 0.001;\n\n/**\n * A replay of the live series, which the stream and host updates keep\n * moving aside in the series store while the bars shown are the replay's;\n * they come back on `replayStop()`.\n */\ninterface ReplaySession {\n /** The latest live price, for when the replay ends. */\n price: { price: number; previousClose?: number } | null;\n /** The latest replayed time a paper account has been given (it only goes forward). */\n fedTime: number | null;\n /** How far into the bar at `fedTime` it has been given (1: all of it). */\n fedFraction: number;\n /** Whether bars form as they play (`animate`), and the finer interval that shapes them. */\n animate: boolean;\n step: TimeFrame | 'auto' | null;\n /** Up to when the replay has shown everything (its last step's `until`); null before its first step. */\n until: number | null;\n /** Where a `hideHistory` window starts, by time; null without one. */\n windowTime: number | null;\n /** The host's finer bars, and the symbol they are of. */\n steps: DataSeries | null;\n stepsSymbol: string;\n /** The symbol replayed. */\n symbol: string;\n /**\n * Held while the next series loads, or older bars page in to reach its\n * time: the state it goes on in, and the pages asked for.\n */\n held: { state: 'playing' | 'paused'; pagesBack: number } | null;\n}\n\nfunction newReplaySession(symbol: string): ReplaySession {\n return { price: null, fedTime: null, fedFraction: 1, animate: false, step: null, until: null, windowTime: null, steps: null, stepsSymbol: symbol, symbol, held: null };\n}\n\n/**\n * The last of `series` that closed by `until` (the next one opened by then;\n * the last one, its span passed): -1 when none did.\n */\n/** How long bar `i` of `series` lasts: to the next bar, or the last bars' spacing. */\nfunction barSpanAt(series: DataSeries, i: number): number {\n const next = series[i + 1];\n return next ? next.time - series[i].time : lastBarSpan(series);\n}\n\n/**\n * Where a paper account has been fed to in a replay, moved from the bars of\n * `before` onto those of `after` by time: on another timeframe it is fed the\n * rest of the bar it was in, and nothing it was given again.\n */\nfunction mapReplayFeed(session: ReplaySession, before: DataSeries | null, after: DataSeries): void {\n const fedTime = session.fedTime;\n if (fedTime === null || !before || before.length === 0 || after.length === 0) return;\n const span = barSpanAt(before, barAt(before, fedTime));\n const at = fedTime + (Number.isFinite(span) ? session.fedFraction * span : 0);\n const k = barAt(after, at);\n const start = after[k].time;\n if (start > at) return; // older than the new bars: they are all ahead of it\n const into = barSpanAt(after, k);\n // At a bar's open exactly: the bar before it, all given.\n if (at === start && k > 0) {\n session.fedTime = after[k - 1].time;\n session.fedFraction = 1;\n return;\n }\n session.fedTime = start;\n session.fedFraction = Number.isFinite(into) && into > 0 ? Math.min(1, (at - start) / into) : 0;\n}\n\nfunction positionAt(series: DataSeries, until: number): number {\n const next = firstAfter(series, until);\n const last = series.length - 1;\n if (next > last && last >= 0 && series[last].time + lastBarSpan(series) <= until) return last;\n return Math.max(-1, next - 2);\n}\n\n/** The smallest gap between the last bars of `series`, or Infinity. */\nfunction recentSpacing(series: DataSeries): number {\n let gap = Infinity;\n for (let i = Math.max(1, series.length - 10); i < series.length; i++) gap = Math.min(gap, series[i].time - series[i - 1].time);\n return gap;\n}\n\n/**\n * Whether finer steps fit `coarse`: finer than its bars, and the bars they\n * build hold their prices (another symbol's steps don't).\n */\nfunction stepsFit(steps: DataSeries, coarse: DataSeries): boolean {\n if (!(recentSpacing(steps) < recentSpacing(coarse))) return false;\n const from = Math.max(0, steps.length - STEPS_FIT_SAMPLES);\n for (let i = from; i < steps.length; i++) {\n const bar = coarse[barAt(coarse, steps[i].time)];\n const slack = (bar.high - bar.low) * STEPS_FIT_TOLERANCE + Number.EPSILON;\n if (steps[i].close > bar.high + slack || steps[i].close < bar.low - slack) return false;\n }\n return true;\n}\n\n/** How far the last of `coarse` reaches: its widest recent spacing (months differ, weekends skip). */\nfunction lastBarSpan(coarse: DataSeries): number {\n let span = 0;\n for (let i = Math.max(1, coarse.length - 10); i < coarse.length; i++) span = Math.max(span, coarse[i].time - coarse[i - 1].time);\n return span > 0 ? span : Infinity;\n}\n\n/**\n * Finer bars for a replay of `coarse`: sorted, inside its span (from its\n * first bar to the end of its last one), with no repeated times. Where they\n * start later than `coarse` (a feed's history is shorter), its earlier bars\n * come first as whole steps, so a bar index means the same bar either way.\n */\nfunction replaySteps(steps: DataSeries, coarse: DataSeries): DataSeries | null {\n if (coarse.length === 0) return null;\n const end = coarse[coarse.length - 1].time + lastBarSpan(coarse);\n const sorted = steps\n .filter((b) => isWholeBar(b) && b.time >= coarse[0].time && b.time < end)\n .sort((a, b) => a.time - b.time);\n const fine: OHLCBar[] = [];\n for (const bar of sorted) if (fine.length === 0 || fine[fine.length - 1].time !== bar.time) fine.push(bar);\n if (fine.length === 0) return null;\n // The first coarse bar the finer ones cover from its start; the ones before are whole steps.\n const from = coarse.findIndex((b) => b.time >= fine[0].time);\n if (from < 0) return null; // they only reach into the last bar\n const head = coarse.slice(0, from);\n const tail = fine.filter((b) => b.time >= coarse[from].time);\n return [...head, ...tail];\n}\n\n/** The bar of `coarse` holding `time`: the last one starting at or before it (0 before the first). */\nfunction barAt(coarse: DataSeries, time: number): number {\n let lo = 0;\n let hi = coarse.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (coarse[mid].time <= time) lo = mid;\n else hi = mid - 1;\n }\n return lo;\n}\n\n/** The last step inside bar `barIndex` of `coarse` (where that bar is whole), or the nearest before it. */\nfunction lastStepOfBar(steps: DataSeries, coarse: DataSeries, barIndex: number): number {\n const bar = Math.max(0, Math.min(Math.floor(Number.isFinite(barIndex) ? barIndex : 0), coarse.length - 1));\n const end = bar + 1 < coarse.length ? coarse[bar + 1].time : Infinity;\n let at = -1;\n for (let i = 0; i < steps.length && steps[i].time < end; i++) at = i;\n return Math.max(0, at);\n}\n\n/** Bar `bar` as it stood after step `index`: built from its steps so far. */\nfunction formingBar(bar: OHLCBar, steps: DataSeries, index: number): OHLCBar {\n let i = index;\n while (i > 0 && steps[i - 1].time >= bar.time) i--;\n let out: OHLCBar = { ...steps[i], time: bar.time };\n for (let j = i + 1; j <= index; j++) out = mergeBar(out, steps[j]);\n return out;\n}\n\n/** Whether step `index` is the last of bar `barIndex` of `coarse`: the next step is in a later bar. */\nfunction closesBar(steps: DataSeries, coarse: DataSeries, index: number, barIndex: number): boolean {\n const next = steps[index + 1];\n const nextBar = coarse[barIndex + 1];\n return next !== undefined && nextBar !== undefined && next.time >= nextBar.time;\n}\n\n/** Finer bars grouped by the bar of `coarse` they are in. */\nfunction piecesByBar(steps: DataSeries, coarse: DataSeries): Map<number, OHLCBar[]> {\n const out = new Map<number, OHLCBar[]>();\n for (const step of steps) {\n const time = coarse[barAt(coarse, step.time)].time;\n const list = out.get(time);\n if (list) list.push(step);\n else out.set(time, [step]);\n }\n return out;\n}\n\n/** Whether the system asks for less motion: a forming bar then steps between its prices. */\nfunction prefersReducedMotion(): boolean {\n return typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n}\n\n/** `bar` with one more step in it. */\nfunction mergeBar(bar: OHLCBar, step: OHLCBar): OHLCBar {\n return {\n time: bar.time,\n open: bar.open,\n high: Math.max(bar.high, step.high),\n low: Math.min(bar.low, step.low),\n close: step.close,\n volume: (bar.volume ?? 0) + (step.volume ?? 0),\n };\n}\n","import type { OHLCBar, DataSeries } from '@tradecanvas/commons';\nimport { normalizeBarTime } from '@tradecanvas/commons';\n\n/**\n * Best-effort parser for OHLCV CSV / JSON drops.\n *\n * Recognises:\n * - CSV (header-driven, comma / semicolon / tab delimiters)\n * - JSON array of objects (`[{ time, open, high, low, close, volume }]`)\n * - JSON array of arrays (`[[time, open, high, low, close, volume]]`)\n *\n * Time columns auto-detect ISO strings, unix-seconds, and unix-ms. Returned\n * bars are sorted ascending by time and de-duplicated on collision.\n */\nexport interface ParseResult {\n data: DataSeries;\n /** Total rows seen — useful for \"imported 4321 of 5000 rows\" style toasts. */\n rowCount: number;\n /** Rows skipped because of missing/invalid required fields. */\n skipped: number;\n}\n\nconst TIME_KEYS = ['time', 'timestamp', 'date', 'datetime', 't'];\nconst OPEN_KEYS = ['open', 'o'];\nconst HIGH_KEYS = ['high', 'h'];\nconst LOW_KEYS = ['low', 'l'];\nconst CLOSE_KEYS = ['close', 'c'];\nconst VOL_KEYS = ['volume', 'vol', 'v'];\n\nexport function parseOHLCV(text: string): ParseResult {\n const trimmed = text.trim();\n if (!trimmed) return { data: [], rowCount: 0, skipped: 0 };\n\n if (trimmed[0] === '[' || trimmed[0] === '{') {\n return parseJson(trimmed);\n }\n return parseCsv(trimmed);\n}\n\nfunction parseJson(text: string): ParseResult {\n let raw: unknown;\n try {\n raw = JSON.parse(text);\n } catch (e) {\n throw new Error(`Invalid JSON: ${(e as Error).message}`);\n }\n if (!Array.isArray(raw)) {\n throw new Error('Expected a JSON array of OHLCV rows');\n }\n const bars: OHLCBar[] = [];\n let skipped = 0;\n for (const row of raw) {\n const bar = Array.isArray(row) ? coerceArrayRow(row) : coerceObjectRow(row as Record<string, unknown>);\n if (bar) bars.push(bar); else skipped++;\n }\n return finalize(bars, raw.length, skipped);\n}\n\nfunction parseCsv(text: string): ParseResult {\n const lines = text.split(/\\r?\\n/).filter(l => l.trim().length > 0);\n if (lines.length === 0) return { data: [], rowCount: 0, skipped: 0 };\n\n const delimiter = detectDelimiter(lines[0]);\n const header = splitLine(lines[0], delimiter).map(h => h.trim().toLowerCase());\n\n // If the first row doesn't look like a header (all numeric), assume the\n // canonical order: time, open, high, low, close, volume.\n const looksLikeHeader = header.some(h => isNaN(Number(h)) && h.length > 0);\n const colIdx = looksLikeHeader\n ? buildColumnMap(header)\n : { time: 0, open: 1, high: 2, low: 3, close: 4, volume: 5 };\n\n if (colIdx.time < 0 || colIdx.open < 0 || colIdx.high < 0 || colIdx.low < 0 || colIdx.close < 0) {\n throw new Error('CSV is missing required column(s): time, open, high, low, close');\n }\n\n const bars: OHLCBar[] = [];\n let skipped = 0;\n for (let i = looksLikeHeader ? 1 : 0; i < lines.length; i++) {\n const cols = splitLine(lines[i], delimiter);\n const time = parseTimeCell(cols[colIdx.time]);\n // Empty cells parse to `Number('') === 0`, which would silently produce\n // bogus zero bars. Treat empty as invalid up-front so the row gets\n // skipped instead of polluting the series.\n const open = numCell(cols[colIdx.open]);\n const high = numCell(cols[colIdx.high]);\n const low = numCell(cols[colIdx.low]);\n const close = numCell(cols[colIdx.close]);\n const volume = colIdx.volume >= 0 ? Number(cols[colIdx.volume]) : 0;\n if (time === null || ![open, high, low, close].every(Number.isFinite)) {\n skipped++;\n continue;\n }\n bars.push({ time, open: open!, high: high!, low: low!, close: close!, volume: Number.isFinite(volume) ? volume : 0 });\n }\n\n return finalize(bars, lines.length - (looksLikeHeader ? 1 : 0), skipped);\n}\n\nfunction detectDelimiter(headerLine: string): string {\n const counts: Record<string, number> = { ',': 0, ';': 0, '\\t': 0, '|': 0 };\n for (const ch of headerLine) {\n if (ch in counts) counts[ch]++;\n }\n let best = ',';\n let bestCount = 0;\n for (const [d, n] of Object.entries(counts)) {\n if (n > bestCount) { best = d; bestCount = n; }\n }\n return best;\n}\n\n/** Minimal CSV splitter — handles double-quoted fields, doesn't try to be RFC-perfect. */\nfunction splitLine(line: string, delimiter: string): string[] {\n const out: string[] = [];\n let buf = '';\n let inQuote = false;\n for (let i = 0; i < line.length; i++) {\n const ch = line[i];\n if (ch === '\"') {\n if (inQuote && line[i + 1] === '\"') { buf += '\"'; i++; }\n else inQuote = !inQuote;\n } else if (ch === delimiter && !inQuote) {\n out.push(buf);\n buf = '';\n } else {\n buf += ch;\n }\n }\n out.push(buf);\n return out;\n}\n\nfunction buildColumnMap(header: string[]): { time: number; open: number; high: number; low: number; close: number; volume: number } {\n const find = (keys: string[]) => header.findIndex(h => keys.includes(h));\n return {\n time: find(TIME_KEYS),\n open: find(OPEN_KEYS),\n high: find(HIGH_KEYS),\n low: find(LOW_KEYS),\n close: find(CLOSE_KEYS),\n volume: find(VOL_KEYS),\n };\n}\n\nfunction coerceObjectRow(row: Record<string, unknown>): OHLCBar | null {\n const get = (keys: string[]) => {\n for (const k of keys) {\n if (row[k] !== undefined) return row[k];\n const upper = k.toUpperCase();\n if (row[upper] !== undefined) return row[upper];\n }\n return undefined;\n };\n const time = parseTimeCell(String(get(TIME_KEYS) ?? ''));\n const open = Number(get(OPEN_KEYS));\n const high = Number(get(HIGH_KEYS));\n const low = Number(get(LOW_KEYS));\n const close = Number(get(CLOSE_KEYS));\n const volume = Number(get(VOL_KEYS) ?? 0);\n if (time === null || ![open, high, low, close].every(Number.isFinite)) return null;\n return { time, open, high, low, close, volume: Number.isFinite(volume) ? volume : 0 };\n}\n\nfunction coerceArrayRow(row: unknown[]): OHLCBar | null {\n if (row.length < 5) return null;\n const time = parseTimeCell(String(row[0]));\n const open = Number(row[1]);\n const high = Number(row[2]);\n const low = Number(row[3]);\n const close = Number(row[4]);\n const volume = row.length >= 6 ? Number(row[5]) : 0;\n if (time === null || ![open, high, low, close].every(Number.isFinite)) return null;\n return { time, open, high, low, close, volume: Number.isFinite(volume) ? volume : 0 };\n}\n\n/**\n * Returns milliseconds since epoch, or null if unparseable:\n * - numeric from 1e11 up → already ms (March 1973 on)\n * - numeric below → seconds, multiplied by 1000\n * - non-numeric → Date.parse fallback (handles ISO, RFC 2822, etc.)\n */\nfunction numCell(raw: string | undefined): number {\n if (raw === undefined || raw.trim() === '') return NaN;\n return Number(raw);\n}\n\nfunction parseTimeCell(raw: string): number | null {\n const trimmed = raw.trim();\n if (!trimmed) return null;\n const asNum = Number(trimmed);\n if (Number.isFinite(asNum) && asNum > 0) {\n return normalizeBarTime(asNum);\n }\n const parsed = Date.parse(trimmed);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\nfunction finalize(bars: OHLCBar[], rowCount: number, skipped: number): ParseResult {\n bars.sort((a, b) => a.time - b.time);\n // De-dup on identical time — keep the last (later rows are typically more\n // authoritative if the source updated a bar after publishing it).\n const out: OHLCBar[] = [];\n for (const bar of bars) {\n if (out.length > 0 && out[out.length - 1].time === bar.time) {\n out[out.length - 1] = bar;\n } else {\n out.push(bar);\n }\n }\n return { data: out, rowCount, skipped };\n}\n","import type { DataSeries } from '@tradecanvas/commons';\nimport { parseOHLCV, type ParseResult } from './parseOHLCV.js';\n\nexport interface DragDropImporterCallbacks {\n /** Called with the parsed series. Hook into chart.setData() here. */\n onData: (data: DataSeries, result: ParseResult, file: File) => void;\n /** Called when parsing throws. Hook a toast/banner here. */\n onError?: (err: Error, file: File) => void;\n}\n\n/**\n * Hooks `dragover` / `drop` on a host element so users can drop a CSV / JSON\n * file onto the chart and get it loaded. A subtle dashed-outline overlay\n * shows up during the drag for visual confirmation.\n *\n * The host owns calling `chart.setData()` via the `onData` callback — the\n * importer stays decoupled from Chart so it can be reused in other surfaces\n * (backtester UI, comparison views, etc.).\n */\nexport class DragDropImporter {\n private host: HTMLElement;\n private overlay: HTMLDivElement | null = null;\n private callbacks: DragDropImporterCallbacks;\n private bound: { dragenter: (e: DragEvent) => void; dragover: (e: DragEvent) => void; dragleave: (e: DragEvent) => void; drop: (e: DragEvent) => void };\n private depth = 0;\n\n constructor(host: HTMLElement, callbacks: DragDropImporterCallbacks) {\n this.host = host;\n this.callbacks = callbacks;\n this.bound = {\n dragenter: (e) => this.onDragEnter(e),\n dragover: (e) => this.onDragOver(e),\n dragleave: (e) => this.onDragLeave(e),\n drop: (e) => this.onDrop(e),\n };\n }\n\n attach(): void {\n this.host.addEventListener('dragenter', this.bound.dragenter);\n this.host.addEventListener('dragover', this.bound.dragover);\n this.host.addEventListener('dragleave', this.bound.dragleave);\n this.host.addEventListener('drop', this.bound.drop);\n }\n\n detach(): void {\n this.host.removeEventListener('dragenter', this.bound.dragenter);\n this.host.removeEventListener('dragover', this.bound.dragover);\n this.host.removeEventListener('dragleave', this.bound.dragleave);\n this.host.removeEventListener('drop', this.bound.drop);\n this.hideOverlay();\n }\n\n private onDragEnter(e: DragEvent): void {\n if (!hasFiles(e)) return;\n e.preventDefault();\n this.depth++;\n this.showOverlay();\n }\n\n private onDragOver(e: DragEvent): void {\n if (!hasFiles(e)) return;\n e.preventDefault();\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';\n }\n\n private onDragLeave(e: DragEvent): void {\n if (!hasFiles(e)) return;\n this.depth = Math.max(0, this.depth - 1);\n if (this.depth === 0) this.hideOverlay();\n }\n\n private async onDrop(e: DragEvent): Promise<void> {\n e.preventDefault();\n this.depth = 0;\n this.hideOverlay();\n\n const file = e.dataTransfer?.files?.[0];\n if (!file) return;\n\n try {\n const text = await file.text();\n const result = parseOHLCV(text);\n if (result.data.length === 0) {\n throw new Error(`No valid OHLCV rows found in \"${file.name}\"`);\n }\n this.callbacks.onData(result.data, result, file);\n } catch (err) {\n this.callbacks.onError?.(err as Error, file);\n }\n }\n\n private showOverlay(): void {\n if (this.overlay) return;\n this.overlay = document.createElement('div');\n Object.assign(this.overlay.style, {\n position: 'absolute',\n inset: '8px',\n zIndex: '90',\n pointerEvents: 'none',\n borderRadius: '10px',\n border: '2px dashed var(--tcw-accent, #f2a93b)',\n background: 'rgba(79, 136, 255, 0.06)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: 'var(--tcw-accent, #f2a93b)',\n fontSize: '14px',\n fontWeight: '600',\n letterSpacing: '-0.005em',\n fontFamily: 'inherit',\n backdropFilter: 'blur(2px)',\n });\n this.overlay.textContent = 'Drop CSV or JSON to load chart data';\n // The host needs `position: relative` for absolute inset to work; Chart\n // already sets this, but guard for arbitrary hosts.\n const cs = getComputedStyle(this.host);\n if (cs.position === 'static') this.host.style.position = 'relative';\n this.host.appendChild(this.overlay);\n }\n\n private hideOverlay(): void {\n this.overlay?.remove();\n this.overlay = null;\n }\n}\n\nfunction hasFiles(e: DragEvent): boolean {\n return e.dataTransfer?.types?.includes('Files') ?? false;\n}\n","/** A saved layout as a list shows it. */\nexport interface SavedLayoutSummary {\n id: string;\n name: string;\n /** The symbol it opens on. */\n symbol?: string;\n /** The interval it opens on. */\n timeframe?: string;\n /** When it was last saved, in ms since the epoch. */\n updatedAt: number;\n /** What saved it: `'chart'` (a widget, the default) or `'grid'` (a grid of charts). */\n kind?: string;\n}\n\n/** A saved layout: its summary and what it holds, as the widget wrote it (JSON). */\nexport interface SavedLayout extends SavedLayoutSummary {\n content: string;\n}\n\ntype MaybePromise<T> = T | Promise<T>;\n\n/**\n * Where named layouts live. The widget ships one for `localStorage` and one in\n * memory; a host keeps layouts on its server by implementing these four calls\n * (each may return a promise).\n */\nexport interface LayoutStorage {\n /** Every layout, newest first. */\n list(): MaybePromise<SavedLayoutSummary[]>;\n /** One layout, or `null` when there is no such layout. */\n load(id: string): MaybePromise<SavedLayout | null>;\n /** Save a layout, replacing one with the same id. */\n save(layout: SavedLayout): MaybePromise<void>;\n remove(id: string): MaybePromise<void>;\n}\n\nfunction summary(layout: SavedLayoutSummary): SavedLayoutSummary {\n const out: SavedLayoutSummary = { id: layout.id, name: layout.name, updatedAt: layout.updatedAt };\n if (layout.symbol !== undefined) out.symbol = layout.symbol;\n if (layout.timeframe !== undefined) out.timeframe = layout.timeframe;\n if (layout.kind !== undefined) out.kind = layout.kind;\n return out;\n}\n\nconst newestFirst = (a: SavedLayoutSummary, b: SavedLayoutSummary): number => b.updatedAt - a.updatedAt;\n\n/** A summary read back from storage (or a host's server), or null when it is not one. */\nexport function readLayoutSummary(value: unknown): SavedLayoutSummary | null {\n if (typeof value !== 'object' || value === null) return null;\n const v = value as Record<string, unknown>;\n if (typeof v.id !== 'string' || !v.id || typeof v.name !== 'string' || typeof v.updatedAt !== 'number' || !Number.isFinite(v.updatedAt)) return null;\n return summary({\n id: v.id,\n name: v.name,\n updatedAt: v.updatedAt,\n symbol: typeof v.symbol === 'string' ? v.symbol : undefined,\n timeframe: typeof v.timeframe === 'string' ? v.timeframe : undefined,\n kind: typeof v.kind === 'string' ? v.kind : undefined,\n });\n}\n\n/** A whole layout read back from storage, or null when it is not one. */\nexport function readSavedLayout(value: unknown): SavedLayout | null {\n const found = readLayoutSummary(value);\n const content = found ? (value as Record<string, unknown>).content : undefined;\n return found && typeof content === 'string' ? { ...found, content } : null;\n}\n\n/** Layouts kept in memory: for tests, or a page that does not keep them. */\nexport function memoryLayouts(initial: readonly SavedLayout[] = []): LayoutStorage {\n const layouts = new Map(initial.map((l) => [l.id, { ...l }]));\n return {\n list: () => [...layouts.values()].map(summary).sort(newestFirst),\n load: (id) => {\n const found = layouts.get(id);\n return found ? { ...found } : null;\n },\n save: (layout) => {\n layouts.set(layout.id, { ...layout });\n },\n remove: (id) => {\n layouts.delete(id);\n },\n };\n}\n\n/**\n * Layouts in the browser's `localStorage`: an index of summaries under\n * `<prefix>index` and each layout's content under `<prefix>item:<id>`.\n * Entries it cannot read are skipped.\n */\nexport function localStorageLayouts(prefix = 'tcw:layouts:'): LayoutStorage {\n const indexKey = `${prefix}index`;\n const itemKey = (id: string) => `${prefix}item:${id}`;\n\n const readIndex = (): SavedLayoutSummary[] => {\n let raw: unknown;\n try {\n raw = JSON.parse(localStorage.getItem(indexKey) ?? '[]');\n } catch {\n return [];\n }\n if (!Array.isArray(raw)) return [];\n return raw.map(readLayoutSummary).filter((s): s is SavedLayoutSummary => s !== null);\n };\n const writeIndex = (index: readonly SavedLayoutSummary[]) => localStorage.setItem(indexKey, JSON.stringify(index));\n\n return {\n list: () => readIndex().sort(newestFirst),\n load: (id) => {\n const found = readIndex().find((s) => s.id === id);\n const content = found ? localStorage.getItem(itemKey(id)) : null;\n return found && content !== null ? { ...found, content } : null;\n },\n save: (layout) => {\n // The content first: an index entry never points at nothing.\n localStorage.setItem(itemKey(layout.id), layout.content);\n writeIndex([...readIndex().filter((s) => s.id !== layout.id), summary(layout)]);\n },\n remove: (id) => {\n writeIndex(readIndex().filter((s) => s.id !== id));\n localStorage.removeItem(itemKey(id));\n },\n };\n}\n","import { readLayoutSummary, readSavedLayout, type LayoutStorage, type SavedLayout, type SavedLayoutSummary } from './layoutStorage.js';\n\n/** The longest a layout's name may be. */\nexport const MAX_LAYOUT_NAME = 80;\n\n/** A layout name trimmed, with runs of spaces folded and capped in length; `null` when nothing is left. */\nexport function cleanLayoutName(name: string): string | null {\n const clean = name.replace(/\\s+/g, ' ').trim().slice(0, MAX_LAYOUT_NAME).trim();\n return clean || null;\n}\n\n/** What a session saves and opens: the widget, or a grid of widgets. */\nexport interface LayoutSessionHost {\n /** What to save now, with the symbol and interval a list shows. */\n capture(): { content: string; symbol?: string; timeframe?: string };\n /** Show a saved layout; throw when its content can't be shown. */\n apply(layout: SavedLayout): Promise<void> | void;\n}\n\nexport interface LayoutSessionOptions {\n /** Save the current layout by itself once changes settle. Default `true`. */\n autoSave?: boolean;\n /** How long changes must settle before an auto-save. Default 1500 ms. */\n debounceMs?: number;\n /**\n * What this session saves (`'chart'`, the default, or `'grid'`): it lists\n * and opens only layouts of its kind, so both can share one storage.\n */\n kind?: string;\n /** The current layout, its saved state or the auto-save switch changed. */\n onChange?: () => void;\n /** An auto-save failed (a full storage, a server down). */\n onError?: (error: unknown) => void;\n now?: () => number;\n newId?: () => string;\n}\n\nfunction randomId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n return c?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction summaryOf(layout: SavedLayoutSummary): SavedLayoutSummary {\n const { id, name, symbol, timeframe, updatedAt, kind } = layout;\n return {\n id,\n name,\n ...(symbol !== undefined ? { symbol } : {}),\n ...(timeframe !== undefined ? { timeframe } : {}),\n updatedAt,\n ...(kind !== undefined ? { kind } : {}),\n };\n}\n\ntype Captured = ReturnType<LayoutSessionHost['capture']>;\n\n/**\n * Named layouts over a {@link LayoutStorage}: the one showing now, save it,\n * save it under a new name, open, rename and remove. Changes reported with\n * `changed()` are auto-saved into the current layout once they settle, and\n * only when what would be saved differs from what was.\n *\n * Every operation runs in turn, and reads the current layout and captures\n * the chart when its turn comes: a save never lands in a layout opened after\n * it was asked for, and never holds a half-opened chart. What storage hands\n * back is checked before it is used.\n */\nexport class LayoutSession {\n private currentLayout: SavedLayoutSummary | null = null;\n /** What the current layout holds in storage (as captured), to tell a real change. */\n private savedContent: string | null = null;\n private dirty = false;\n private autoSave: boolean;\n private readonly debounceMs: number;\n private readonly kind: string;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private applying = 0;\n /** Operations waiting or running; a change while one runs is looked at after. */\n private busy = 0;\n private changedWhileBusy = false;\n private tail: Promise<unknown> = Promise.resolve();\n private destroyed = false;\n private readonly now: () => number;\n private readonly newId: () => string;\n\n constructor(\n private readonly storage: LayoutStorage,\n private readonly host: LayoutSessionHost,\n private readonly options: LayoutSessionOptions = {},\n ) {\n this.autoSave = options.autoSave ?? true;\n this.debounceMs = options.debounceMs ?? 1500;\n this.kind = options.kind ?? 'chart';\n this.now = options.now ?? Date.now;\n this.newId = options.newId ?? randomId;\n }\n\n /** The layout showing now, or `null` before one is saved or opened. */\n current(): SavedLayoutSummary | null {\n return this.currentLayout;\n }\n\n /** The chart differs from the current layout as saved (auto-save off, failed, or not yet run). */\n isDirty(): boolean {\n return this.dirty;\n }\n\n isAutoSave(): boolean {\n return this.autoSave;\n }\n\n setAutoSave(on: boolean): void {\n if (this.autoSave === on) return;\n this.autoSave = on;\n this.notify();\n if (on && this.dirty) this.changed();\n }\n\n /** Every saved layout of this session's kind, newest first; entries that aren't layouts are left out. */\n async list(): Promise<SavedLayoutSummary[]> {\n const all: unknown = await this.storage.list();\n if (!Array.isArray(all)) return [];\n return all\n .map(readLayoutSummary)\n .filter((s): s is SavedLayoutSummary => s !== null && (s.kind ?? 'chart') === this.kind)\n .sort((a, b) => b.updatedAt - a.updatedAt);\n }\n\n /** Save into the current layout; `null` when there is none yet (ask for a name, then `saveAs`). */\n save(): Promise<SavedLayoutSummary | null> {\n return this.queue(() => {\n const current = this.currentLayout;\n return current ? this.write(current.id, current.name) : null;\n });\n }\n\n /** Save as a new layout called `name`, and make it the current one. */\n saveAs(name: string): Promise<SavedLayoutSummary> {\n const clean = cleanLayoutName(name);\n if (!clean) return Promise.reject(new Error('A layout needs a name'));\n return this.queue(() => this.write(this.newId(), clean));\n }\n\n /**\n * Show a saved layout. `false` when there is no such layout (or it isn't\n * one of this kind); rejects when it could not be shown, and then no layout\n * is current (the chart may be part way).\n */\n open(id: string): Promise<boolean> {\n return this.queue(async () => {\n const layout = readSavedLayout(await this.storage.load(id));\n if (!layout || layout.id !== id || (layout.kind ?? 'chart') !== this.kind || this.destroyed) return false;\n this.cancelTimer();\n this.applying++;\n try {\n await this.host.apply(layout);\n } catch (err) {\n this.forget();\n throw err;\n } finally {\n this.applying--;\n }\n this.cancelTimer();\n this.currentLayout = summaryOf(layout);\n // Restored state can read differently (new ids): what shows now counts as saved.\n this.savedContent = this.host.capture().content;\n this.dirty = false;\n this.notify();\n return true;\n });\n }\n\n rename(id: string, name: string): Promise<void> {\n const clean = cleanLayoutName(name);\n if (!clean) return Promise.reject(new Error('A layout needs a name'));\n return this.queue(async () => {\n const layout = readSavedLayout(await this.storage.load(id));\n if (!layout || layout.id !== id) return;\n await this.storage.save({ ...layout, name: clean });\n if (this.currentLayout?.id === id) this.currentLayout = { ...this.currentLayout, name: clean };\n this.notify();\n });\n }\n\n remove(id: string): Promise<void> {\n return this.queue(async () => {\n await this.storage.remove(id);\n if (this.currentLayout?.id === id) this.forget();\n });\n }\n\n /** Something a layout holds may have changed: check once things settle, and auto-save. */\n changed(): void {\n if (this.destroyed || this.applying > 0) return;\n if (!this.currentLayout) {\n // A first save under way: look again once it has a layout to compare with.\n if (this.busy > 0) this.changedWhileBusy = true;\n return;\n }\n this.cancelTimer();\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.queue(() => this.settle());\n }, this.debounceMs);\n }\n\n /** Stop. A change still waiting to be auto-saved is saved now. */\n destroy(): void {\n if (this.destroyed) return;\n const pending = this.timer !== null;\n this.cancelTimer();\n const current = this.currentLayout;\n if (pending && current && this.autoSave) {\n // Capture now: the chart is about to go.\n let captured: Captured | null = null;\n try {\n captured = this.host.capture();\n } catch (err) {\n this.options.onError?.(err);\n }\n if (captured && captured.content !== this.savedContent) {\n const last = captured;\n void this.queue(() => this.write(current.id, current.name, last)).catch((err: unknown) => this.options.onError?.(err));\n }\n }\n this.destroyed = true;\n }\n\n /** The changes have settled: save them into the current layout, or mark it changed. */\n private async settle(): Promise<void> {\n const current = this.currentLayout;\n if (this.destroyed || !current) return;\n let captured: Captured;\n try {\n captured = this.host.capture();\n } catch (err) {\n this.options.onError?.(err);\n return;\n }\n const differs = captured.content !== this.savedContent;\n if (differs && this.autoSave) {\n try {\n await this.write(current.id, current.name, captured);\n } catch (err) {\n this.dirty = true;\n this.notify();\n this.options.onError?.(err);\n }\n return;\n }\n if (differs !== this.dirty) {\n this.dirty = differs;\n this.notify();\n }\n }\n\n /** Store the chart as layout `id` and make it the current one (runs inside the queue). */\n private async write(id: string, name: string, captured: Captured = this.host.capture()): Promise<SavedLayoutSummary> {\n this.cancelTimer();\n const layout: SavedLayout = {\n id,\n name,\n ...(captured.symbol !== undefined ? { symbol: captured.symbol } : {}),\n ...(captured.timeframe !== undefined ? { timeframe: captured.timeframe } : {}),\n updatedAt: this.now(),\n kind: this.kind,\n content: captured.content,\n };\n await this.storage.save(layout);\n const summary = summaryOf(layout);\n this.currentLayout = summary;\n this.savedContent = captured.content;\n this.dirty = false;\n this.notify();\n return summary;\n }\n\n /** No layout is current any more. */\n private forget(): void {\n this.cancelTimer();\n this.currentLayout = null;\n this.savedContent = null;\n this.dirty = false;\n this.notify();\n }\n\n /** Run operations one after another; a failed one does not stop the next. */\n private queue<T>(task: () => T | Promise<T>): Promise<T> {\n this.busy++;\n const run = this.tail.then(task).finally(() => {\n this.busy--;\n if (this.busy === 0 && this.changedWhileBusy) {\n this.changedWhileBusy = false;\n this.changed();\n }\n });\n this.tail = run.catch(() => undefined);\n return run;\n }\n\n private cancelTimer(): void {\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private notify(): void {\n if (!this.destroyed) this.options.onChange?.();\n }\n}\n"],"mappings":"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"}