@tradecanvas/chart 1.4.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +910 -858
- package/dist/Chart.d.ts +606 -23
- package/dist/Chart.d.ts.map +1 -1
- package/dist/DataManager.d.ts +5 -0
- package/dist/DataManager.d.ts.map +1 -1
- package/dist/HistoryPager.d.ts +58 -0
- package/dist/HistoryPager.d.ts.map +1 -0
- package/dist/LayoutSession-CTZB90NX.cjs +2 -0
- package/dist/LayoutSession-CTZB90NX.cjs.map +1 -0
- package/dist/LayoutSession-CWn3sdbs.js +3967 -0
- package/dist/LayoutSession-CWn3sdbs.js.map +1 -0
- package/dist/ThemeManager.d.ts +5 -1
- package/dist/ThemeManager.d.ts.map +1 -1
- package/dist/chartA11y.d.ts +59 -0
- package/dist/chartA11y.d.ts.map +1 -0
- package/dist/charts/ChartTypeStrategy.d.ts +5 -4
- package/dist/charts/ChartTypeStrategy.d.ts.map +1 -1
- package/dist/grid/ChartGrid.d.ts +6 -1
- package/dist/grid/ChartGrid.d.ts.map +1 -1
- package/dist/i18n-CiQ8EDVs.js +1469 -0
- package/dist/i18n-CiQ8EDVs.js.map +1 -0
- package/dist/i18n-WsLA9SmD.cjs +2 -0
- package/dist/i18n-WsLA9SmD.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +22 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -31
- package/dist/index.js.map +1 -1
- package/dist/indicatorLabel.d.ts +7 -0
- package/dist/indicatorLabel.d.ts.map +1 -0
- package/dist/layout/LayoutManager.d.ts +50 -0
- package/dist/layout/LayoutManager.d.ts.map +1 -1
- package/dist/layout/priceAxisWidth.d.ts +2 -0
- package/dist/layout/priceAxisWidth.d.ts.map +1 -1
- package/dist/regularHours.d.ts +10 -0
- package/dist/regularHours.d.ts.map +1 -0
- package/dist/state/LayoutSession.d.ts +101 -0
- package/dist/state/LayoutSession.d.ts.map +1 -0
- package/dist/state/layoutStorage.d.ts +46 -0
- package/dist/state/layoutStorage.d.ts.map +1 -0
- package/dist/trading/wireExecution.d.ts +7 -3
- package/dist/trading/wireExecution.d.ts.map +1 -1
- package/dist/widget/ChartWidget.d.ts +219 -10
- package/dist/widget/ChartWidget.d.ts.map +1 -1
- package/dist/widget/ChartWidgetGrid.d.ts +166 -0
- package/dist/widget/ChartWidgetGrid.d.ts.map +1 -0
- package/dist/widget/DrawingTemplateStore.d.ts +29 -8
- package/dist/widget/DrawingTemplateStore.d.ts.map +1 -1
- package/dist/widget/WatchlistStore.d.ts +65 -0
- package/dist/widget/WatchlistStore.d.ts.map +1 -0
- package/dist/widget/WidgetAccountPanel.d.ts +72 -0
- package/dist/widget/WidgetAccountPanel.d.ts.map +1 -0
- package/dist/widget/WidgetAlertsPanel.d.ts +50 -3
- package/dist/widget/WidgetAlertsPanel.d.ts.map +1 -1
- package/dist/widget/WidgetBracketBar.d.ts +3 -1
- package/dist/widget/WidgetBracketBar.d.ts.map +1 -1
- package/dist/widget/WidgetChartNav.d.ts +36 -0
- package/dist/widget/WidgetChartNav.d.ts.map +1 -0
- package/dist/widget/WidgetCommandPalette.d.ts +3 -1
- package/dist/widget/WidgetCommandPalette.d.ts.map +1 -1
- package/dist/widget/WidgetContextMenu.d.ts +40 -0
- package/dist/widget/WidgetContextMenu.d.ts.map +1 -0
- package/dist/widget/WidgetDataWindow.d.ts +3 -1
- package/dist/widget/WidgetDataWindow.d.ts.map +1 -1
- package/dist/widget/WidgetDepthLadder.d.ts +3 -1
- package/dist/widget/WidgetDepthLadder.d.ts.map +1 -1
- package/dist/widget/WidgetDrawingSettings.d.ts +108 -0
- package/dist/widget/WidgetDrawingSettings.d.ts.map +1 -0
- package/dist/widget/WidgetDrawingSidebar.d.ts +14 -1
- package/dist/widget/WidgetDrawingSidebar.d.ts.map +1 -1
- package/dist/widget/WidgetDrawingStyle.d.ts +3 -1
- package/dist/widget/WidgetDrawingStyle.d.ts.map +1 -1
- package/dist/widget/WidgetDropdown.d.ts.map +1 -1
- package/dist/widget/WidgetGoToDate.d.ts +5 -4
- package/dist/widget/WidgetGoToDate.d.ts.map +1 -1
- package/dist/widget/WidgetHistoryPill.d.ts +23 -0
- package/dist/widget/WidgetHistoryPill.d.ts.map +1 -0
- package/dist/widget/WidgetHotkeySheet.d.ts.map +1 -1
- package/dist/widget/WidgetIndicatorLegend.d.ts +31 -1
- package/dist/widget/WidgetIndicatorLegend.d.ts.map +1 -1
- package/dist/widget/WidgetIndicatorSettings.d.ts +62 -7
- package/dist/widget/WidgetIndicatorSettings.d.ts.map +1 -1
- package/dist/widget/WidgetIntervalInput.d.ts +34 -0
- package/dist/widget/WidgetIntervalInput.d.ts.map +1 -0
- package/dist/widget/WidgetLayoutsDialog.d.ts +39 -0
- package/dist/widget/WidgetLayoutsDialog.d.ts.map +1 -0
- package/dist/widget/WidgetLayoutsUI.d.ts +37 -0
- package/dist/widget/WidgetLayoutsUI.d.ts.map +1 -0
- package/dist/widget/WidgetNamePrompt.d.ts +31 -0
- package/dist/widget/WidgetNamePrompt.d.ts.map +1 -0
- package/dist/widget/WidgetObjectTree.d.ts +21 -3
- package/dist/widget/WidgetObjectTree.d.ts.map +1 -1
- package/dist/widget/WidgetOrderTicket.d.ts +49 -0
- package/dist/widget/WidgetOrderTicket.d.ts.map +1 -0
- package/dist/widget/WidgetReplayBar.d.ts +15 -0
- package/dist/widget/WidgetReplayBar.d.ts.map +1 -1
- package/dist/widget/WidgetSettings.d.ts +13 -1
- package/dist/widget/WidgetSettings.d.ts.map +1 -1
- package/dist/widget/WidgetStatusBar.d.ts +3 -0
- package/dist/widget/WidgetStatusBar.d.ts.map +1 -1
- package/dist/widget/WidgetStyles.d.ts +3 -1
- package/dist/widget/WidgetStyles.d.ts.map +1 -1
- package/dist/widget/WidgetSymbolInfo.d.ts +64 -0
- package/dist/widget/WidgetSymbolInfo.d.ts.map +1 -0
- package/dist/widget/WidgetSymbolSearch.d.ts +26 -5
- package/dist/widget/WidgetSymbolSearch.d.ts.map +1 -1
- package/dist/widget/WidgetToolbar.d.ts +30 -1
- package/dist/widget/WidgetToolbar.d.ts.map +1 -1
- package/dist/widget/WidgetWatchlist.d.ts +53 -13
- package/dist/widget/WidgetWatchlist.d.ts.map +1 -1
- package/dist/widget/chartMenu.d.ts +40 -0
- package/dist/widget/chartMenu.d.ts.map +1 -0
- package/dist/widget/drawingMenu.d.ts +21 -0
- package/dist/widget/drawingMenu.d.ts.map +1 -0
- package/dist/widget/focusTrap.d.ts +8 -0
- package/dist/widget/focusTrap.d.ts.map +1 -0
- package/dist/widget/i18n.d.ts +20 -89
- package/dist/widget/i18n.d.ts.map +1 -1
- package/dist/widget/iconSet.d.ts.map +1 -1
- package/dist/widget/index.cjs +4294 -3028
- package/dist/widget/index.cjs.map +1 -1
- package/dist/widget/index.d.ts +15 -1
- package/dist/widget/index.d.ts.map +1 -1
- package/dist/widget/index.js +6326 -1185
- package/dist/widget/index.js.map +1 -1
- package/dist/widget/indicatorTemplates.d.ts +24 -0
- package/dist/widget/indicatorTemplates.d.ts.map +1 -0
- package/dist/widget/keyTarget.d.ts +11 -0
- package/dist/widget/keyTarget.d.ts.map +1 -0
- package/dist/widget/legendValues.d.ts +11 -6
- package/dist/widget/legendValues.d.ts.map +1 -1
- package/dist/widget/locales/ar.d.ts +4 -0
- package/dist/widget/locales/ar.d.ts.map +1 -0
- package/dist/widget/locales/de.d.ts +4 -0
- package/dist/widget/locales/de.d.ts.map +1 -0
- package/dist/widget/locales/en.d.ts +722 -0
- package/dist/widget/locales/en.d.ts.map +1 -0
- package/dist/widget/locales/es.d.ts +4 -0
- package/dist/widget/locales/es.d.ts.map +1 -0
- package/dist/widget/locales/fr.d.ts +4 -0
- package/dist/widget/locales/fr.d.ts.map +1 -0
- package/dist/widget/locales/he.d.ts +4 -0
- package/dist/widget/locales/he.d.ts.map +1 -0
- package/dist/widget/locales/id.d.ts +4 -0
- package/dist/widget/locales/id.d.ts.map +1 -0
- package/dist/widget/locales/index.cjs +2 -0
- package/dist/widget/locales/index.cjs.map +1 -0
- package/dist/widget/locales/index.d.ts +33 -0
- package/dist/widget/locales/index.d.ts.map +1 -0
- package/dist/widget/locales/index.js +10089 -0
- package/dist/widget/locales/index.js.map +1 -0
- package/dist/widget/locales/ja.d.ts +4 -0
- package/dist/widget/locales/ja.d.ts.map +1 -0
- package/dist/widget/locales/ko.d.ts +4 -0
- package/dist/widget/locales/ko.d.ts.map +1 -0
- package/dist/widget/locales/pt.d.ts +4 -0
- package/dist/widget/locales/pt.d.ts.map +1 -0
- package/dist/widget/locales/ru.d.ts +4 -0
- package/dist/widget/locales/ru.d.ts.map +1 -0
- package/dist/widget/locales/th.d.ts +4 -0
- package/dist/widget/locales/th.d.ts.map +1 -0
- package/dist/widget/locales/tr.d.ts +4 -0
- package/dist/widget/locales/tr.d.ts.map +1 -0
- package/dist/widget/locales/vi.d.ts +4 -0
- package/dist/widget/locales/vi.d.ts.map +1 -0
- package/dist/widget/locales/zh.d.ts +4 -0
- package/dist/widget/locales/zh.d.ts.map +1 -0
- package/dist/widget/locales/zhHant.d.ts +4 -0
- package/dist/widget/locales/zhHant.d.ts.map +1 -0
- package/dist/widget/orderTicket.d.ts +27 -0
- package/dist/widget/orderTicket.d.ts.map +1 -0
- package/dist/widget/settingsControls.d.ts +24 -0
- package/dist/widget/settingsControls.d.ts.map +1 -0
- package/dist/widget/types.d.ts +182 -6
- package/dist/widget/types.d.ts.map +1 -1
- package/dist/widget/widgetConfig.d.ts.map +1 -1
- package/dist/widget/widgetLayout.d.ts +22 -0
- package/dist/widget/widgetLayout.d.ts.map +1 -0
- package/dist/widget/widgetLocales.d.ts +17 -0
- package/dist/widget/widgetLocales.d.ts.map +1 -0
- package/dist/widget/widgetTimeframes.d.ts +8 -0
- package/dist/widget/widgetTimeframes.d.ts.map +1 -1
- package/dist/widget/widgetTimezones.d.ts +32 -0
- package/dist/widget/widgetTimezones.d.ts.map +1 -0
- package/dist/widget/widgetUI.d.ts +126 -0
- package/dist/widget/widgetUI.d.ts.map +1 -0
- package/package.json +8 -3
- package/dist/DragDropImporter-C5kgfbET.cjs +0 -2
- package/dist/DragDropImporter-C5kgfbET.cjs.map +0 -1
- package/dist/DragDropImporter-Dd9Mud5J.js +0 -2099
- package/dist/DragDropImporter-Dd9Mud5J.js.map +0 -1
- package/dist/charts/IndicatorPriceRange.d.ts +0 -20
- package/dist/charts/IndicatorPriceRange.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LayoutSession-CTZB90NX.cjs","names":[],"sources":["../src/regularHours.ts","../src/chartA11y.ts","../src/indicatorLabel.ts","../src/charts/ChartTypeStrategy.ts","../src/state/AutoSaveScheduler.ts","../src/DataManager.ts","../src/HistoryPager.ts","../src/ThemeManager.ts","../src/layout/LayoutManager.ts","../src/layout/priceAxisWidth.ts","../src/interaction/verticalPanGate.ts","../src/plugins/contracts.ts","../src/plugins/registry.ts","../src/plugins/PluginManager.ts","../src/trading/wireExecution.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';\r\nimport { isValidTimeZone, normalizeBarTime, sessionMinute } from '@tradecanvas/commons';\r\nimport { isRegularSession } from '@tradecanvas/core';\r\n\r\nconst DAY_MS = 86_400_000;\r\n/** Bars looked at to tell the interval (a weekend gap between two of them doesn't fool it). */\r\nconst SPACING_SAMPLE = 10;\r\n\r\n/**\r\n * Which bar times fall in `info`'s regular hours, for bars like `data`'s\r\n * (by the time each bar opens). Null when there is nothing to leave out:\r\n * the symbol has no hours or no zone, or the bars are a day or longer.\r\n */\r\nexport function regularHoursFilter(\r\n info: SymbolInfo | null,\r\n data: readonly { time: number }[],\r\n): ((time: number) => boolean) | null {\r\n const zone = info?.timezone;\r\n if (!zone || !isValidTimeZone(zone) || data.length < 2) return null;\r\n const windows = (info?.sessions ?? []).flatMap((session) => {\r\n const startMinute = sessionMinute(session.start);\r\n const endMinute = sessionMinute(session.end);\r\n return startMinute === null || endMinute === null ? [] : [{ startMinute, endMinute }];\r\n });\r\n if (windows.length === 0) return null;\r\n let spacing = Infinity;\r\n for (let i = Math.max(1, data.length - SPACING_SAMPLE); i < data.length; i++) {\r\n const gap = normalizeBarTime(data[i].time) - normalizeBarTime(data[i - 1].time);\r\n if (gap > 0 && gap < spacing) spacing = gap;\r\n }\r\n if (!(spacing < DAY_MS)) return null;\r\n const config = { timeZone: zone, windows, startMinute: windows[0].startMinute, endMinute: windows[windows.length - 1].endMinute };\r\n return (time) => isRegularSession(normalizeBarTime(time), config);\r\n}\r\n","import type { ChartA11yLabels } from '@tradecanvas/commons';\r\n\r\nexport type { ChartA11yLabels };\r\n\r\nexport const DEFAULT_A11Y_LABELS: ChartA11yLabels = {\r\n role: 'chart',\r\n summary: '{what}. Last price {close}.',\r\n empty: 'Chart, no data.',\r\n view: 'Showing {count} bars, {from} to {to}. High {high}, low {low}.',\r\n bar: '{time}: open {open}, high {high}, low {low}, close {close}, volume {volume}.',\r\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.',\r\n};\r\n\r\n/** What the chart tells its reader, asked when needed. */\r\nexport interface ChartA11ySource {\r\n symbol: () => string;\r\n timeframe: () => string;\r\n chartType: () => string;\r\n bars: () => readonly { time: number; open: number; high: number; low: number; close: number; volume?: number }[];\r\n visibleRange: () => { from: number; to: number };\r\n formatPrice: (price: number) => string;\r\n formatTime: (time: number) => string;\r\n /** Show the bar being read (a time crosshair); null clears it. */\r\n showBar: (time: number | null) => void;\r\n}\r\n\r\nconst fill = (template: string, values: Record<string, string | number>): string =>\r\n template.replace(/\\{(\\w+)\\}/g, (m, name: string) => (name in values ? String(values[name]) : m));\r\n\r\nconst A11Y_ATTRIBUTES = ['role', 'aria-roledescription', 'aria-label', 'aria-describedby'] as const;\r\n\r\n/** Wait after the last key before saying what is on screen. */\r\nconst VIEW_ANNOUNCE_MS = 400;\r\n/** The summary follows live data at most this often. */\r\nconst SUMMARY_MS = 1000;\r\n\r\n/**\r\n * The chart for screen readers: the container is an application with a\r\n * summary (symbol, type, timeframe, last price) and key help; after the keys\r\n * move the view it says what is on screen, and comma and period read the\r\n * bars one at a time.\r\n */\r\nexport class ChartA11y {\r\n private readonly live: HTMLDivElement;\r\n private readonly help: HTMLDivElement;\r\n private labels: ChartA11yLabels;\r\n private cursor: number | null = null;\r\n private viewTimer: ReturnType<typeof setTimeout> | null = null;\r\n /** The container's own attributes before, put back on destroy. */\r\n private readonly before = new Map<string, string | null>();\r\n private summaryAt = 0;\r\n private summaryTimer: ReturnType<typeof setTimeout> | null = null;\r\n private readonly onBlur = () => {\r\n this.cursor = null;\r\n this.source.showBar(null);\r\n };\r\n\r\n constructor(\r\n private readonly container: HTMLElement,\r\n private readonly source: ChartA11ySource,\r\n labels: Partial<ChartA11yLabels> = {},\r\n ) {\r\n this.labels = { ...DEFAULT_A11Y_LABELS, ...labels };\r\n for (const attr of A11Y_ATTRIBUTES) this.before.set(attr, container.getAttribute(attr));\r\n container.setAttribute('role', 'application');\r\n\r\n this.help = hidden('div', 'tc-sr-only');\r\n this.help.id = `tc-chart-keys-${Math.random().toString(36).slice(2, 10)}`;\r\n container.setAttribute('aria-describedby', this.help.id);\r\n\r\n this.live = hidden('div', 'tc-sr-only tc-sr-live');\r\n this.live.setAttribute('aria-live', 'polite');\r\n this.live.setAttribute('aria-atomic', 'true');\r\n\r\n container.append(this.help, this.live);\r\n container.addEventListener('blur', this.onBlur);\r\n this.applyLabels();\r\n }\r\n\r\n setLabels(labels: Partial<ChartA11yLabels>): void {\r\n this.labels = { ...DEFAULT_A11Y_LABELS, ...labels };\r\n this.applyLabels();\r\n }\r\n\r\n /** The summary again (new data, type, symbol): at once, or soon after when live data streams. */\r\n refresh(force = false): void {\r\n const wait = SUMMARY_MS - (Date.now() - this.summaryAt);\r\n // The first bars are told at once; a stream's ticks at most once a second.\r\n if (force || wait <= 0 || this.container.getAttribute('aria-label') === this.labels.empty) {\r\n if (this.summaryTimer) clearTimeout(this.summaryTimer);\r\n this.summaryTimer = null;\r\n this.summaryAt = Date.now();\r\n this.container.setAttribute('aria-label', this.summary());\r\n return;\r\n }\r\n this.summaryTimer ??= setTimeout(() => {\r\n this.summaryTimer = null;\r\n this.refresh(true);\r\n }, wait);\r\n }\r\n\r\n /** The keys moved the view: soon, say what is on screen. */\r\n viewMoved(): void {\r\n this.cursor = null;\r\n if (this.viewTimer) clearTimeout(this.viewTimer);\r\n this.viewTimer = setTimeout(() => {\r\n this.viewTimer = null;\r\n this.say(this.viewText());\r\n }, VIEW_ANNOUNCE_MS);\r\n }\r\n\r\n /** Read the next (1) or previous (-1) bar; the first read starts at the last bar on screen. */\r\n readBar(step: 1 | -1): void {\r\n const bars = this.source.bars();\r\n if (bars.length === 0) {\r\n this.say(this.labels.empty);\r\n return;\r\n }\r\n const { to } = this.source.visibleRange();\r\n const start = Math.max(0, Math.min(bars.length - 1, Math.floor(to)));\r\n this.cursor = this.cursor === null ? start : Math.max(0, Math.min(bars.length - 1, this.cursor + step));\r\n const bar = bars[this.cursor];\r\n this.source.showBar(bar.time);\r\n const p = this.source.formatPrice;\r\n this.say(fill(this.labels.bar, {\r\n time: this.source.formatTime(bar.time),\r\n open: p(bar.open),\r\n high: p(bar.high),\r\n low: p(bar.low),\r\n close: p(bar.close),\r\n volume: formatVolume(bar.volume ?? 0),\r\n }));\r\n }\r\n\r\n destroy(): void {\r\n if (this.viewTimer) clearTimeout(this.viewTimer);\r\n if (this.summaryTimer) clearTimeout(this.summaryTimer);\r\n this.container.removeEventListener('blur', this.onBlur);\r\n for (const [attr, value] of this.before) {\r\n if (value === null) this.container.removeAttribute(attr);\r\n else this.container.setAttribute(attr, value);\r\n }\r\n this.help.remove();\r\n this.live.remove();\r\n }\r\n\r\n private applyLabels(): void {\r\n this.container.setAttribute('aria-roledescription', this.labels.role);\r\n this.help.textContent = this.labels.keys;\r\n this.refresh(true);\r\n }\r\n\r\n private summary(): string {\r\n const bars = this.source.bars();\r\n if (bars.length === 0) return this.labels.empty;\r\n const type = this.source.chartType();\r\n const what = [this.source.symbol(), this.labels.typeName ? this.labels.typeName(type) : type, this.source.timeframe()]\r\n .filter(Boolean)\r\n .join(', ');\r\n return fill(this.labels.summary, { what, close: this.source.formatPrice(bars[bars.length - 1].close) });\r\n }\r\n\r\n private viewText(): string {\r\n const bars = this.source.bars();\r\n if (bars.length === 0) return this.labels.empty;\r\n const range = this.source.visibleRange();\r\n const from = Math.max(0, Math.min(bars.length - 1, Math.ceil(range.from)));\r\n const to = Math.max(from, Math.min(bars.length - 1, Math.floor(range.to)));\r\n let high = -Infinity;\r\n let low = Infinity;\r\n for (let i = from; i <= to; i++) {\r\n high = Math.max(high, bars[i].high);\r\n low = Math.min(low, bars[i].low);\r\n }\r\n return fill(this.labels.view, {\r\n count: to - from + 1,\r\n from: this.source.formatTime(bars[from].time),\r\n to: this.source.formatTime(bars[to].time),\r\n high: this.source.formatPrice(high),\r\n low: this.source.formatPrice(low),\r\n });\r\n }\r\n\r\n private say(text: string): void {\r\n this.live.textContent = text;\r\n }\r\n}\r\n\r\n/** An element screen readers read and the eye doesn't see. */\r\nfunction hidden<K extends keyof HTMLElementTagNameMap>(tag: K, className: string): HTMLElementTagNameMap[K] {\r\n const el = document.createElement(tag);\r\n el.className = className;\r\n Object.assign(el.style, {\r\n position: 'absolute',\r\n width: '1px',\r\n height: '1px',\r\n margin: '-1px',\r\n padding: '0',\r\n overflow: 'hidden',\r\n clip: 'rect(0 0 0 0)',\r\n whiteSpace: 'nowrap',\r\n border: '0',\r\n });\r\n return el;\r\n}\r\n\r\nfunction formatVolume(v: number): string {\r\n return Number.isInteger(v) ? String(v) : v.toFixed(2);\r\n}\r\n","/**\r\n * \"EMA 20\", \"BB 20 2\", \"MACD 12 26 9\": the short name (`shortName`, else the\r\n * id in capitals) plus up to three numeric parameters, in the indicator's own\r\n * order, so two instances of the same indicator can be told apart.\r\n */\r\nexport function indicatorChipLabel(\r\n id: string,\r\n params: Record<string, unknown>,\r\n defaults?: Record<string, unknown>,\r\n shortName?: string,\r\n): string {\r\n const order = defaults ? Object.keys(defaults) : Object.keys(params);\r\n const numbers = order\r\n .map((k) => params[k])\r\n .filter((v): v is number => typeof v === 'number' && Number.isFinite(v))\r\n .slice(0, 3)\r\n .map((v) => String(Number(v.toFixed(4))));\r\n // An indicator on another symbol is named after it (\"Compare ETHUSDT\", \"Ratio ETHUSDT\").\r\n const symbol = typeof params.symbol === 'string' && params.symbol ? [params.symbol] : [];\r\n const name = id === 'spread' && params.mode === 'ratio' ? 'Ratio' : shortName ?? id.toUpperCase();\r\n return [name, ...symbol, ...numbers].join(' ');\r\n}\r\n","import type { ChartType, ChartTypeOptions, DataSeries } from '@tradecanvas/commons';\r\nimport {\r\n AreaRenderer,\r\n BarRenderer,\r\n BaselineRenderer,\r\n CandlestickRenderer,\r\n EquivolumeRenderer,\r\n HiLoRenderer,\r\n HLCAreaRenderer,\r\n HollowCandleRenderer,\r\n KagiRenderer,\r\n LineRenderer,\r\n LineWithMarkersRenderer,\r\n PointAndFigureRenderer,\r\n RenkoRenderer,\r\n StepLineRenderer,\r\n VolumeCandleRenderer,\r\n toHeikinAshi,\r\n toKagi,\r\n toLineBreak,\r\n toPointAndFigure,\r\n toRangeBars,\r\n toRenko,\r\n} from '@tradecanvas/core';\r\nimport type { ChartRendererInterface } from '@tradecanvas/core';\r\nimport type { ChartTypePlugin } from '../plugins/contracts.js';\r\n\r\n/**\r\n * Pure mapping from `ChartType` to the renderer class that draws it. Several\r\n * chart types reuse `CandlestickRenderer` against transformed data\r\n * (heikinAshi, lineBreak, rangeBars).\r\n */\r\nexport function createRendererFor(type: ChartType): ChartRendererInterface {\r\n switch (type) {\r\n case 'candlestick':\r\n case 'heikinAshi':\r\n case 'lineBreak':\r\n case 'rangeBars':\r\n return new CandlestickRenderer();\r\n case 'line':\r\n return new LineRenderer();\r\n case 'area':\r\n return new AreaRenderer();\r\n case 'bar':\r\n return new BarRenderer();\r\n case 'hollowCandle':\r\n return new HollowCandleRenderer();\r\n case 'baseline':\r\n return new BaselineRenderer();\r\n case 'renko':\r\n return new RenkoRenderer();\r\n case 'kagi':\r\n return new KagiRenderer();\r\n case 'pointAndFigure':\r\n return new PointAndFigureRenderer();\r\n case 'volumeCandles':\r\n return new VolumeCandleRenderer();\r\n case 'equivolume':\r\n return new EquivolumeRenderer();\r\n case 'hlcArea':\r\n return new HLCAreaRenderer();\r\n case 'stepLine':\r\n return new StepLineRenderer();\r\n case 'lineWithMarkers':\r\n return new LineWithMarkersRenderer();\r\n case 'hiLo':\r\n return new HiLoRenderer();\r\n default:\r\n return new CandlestickRenderer();\r\n }\r\n}\r\n\r\n/**\r\n * Transform raw OHLC bars into the display series for a given chart type,\r\n * with its settings (`options`; what isn't set is worked out from the data).\r\n * Returns the input unchanged for chart types that render raw OHLC directly.\r\n *\r\n * Centralized so adding a new chart type only requires editing one file.\r\n */\r\nexport function transformDisplayData(type: ChartType, raw: DataSeries, options: ChartTypeOptions = {}): DataSeries {\r\n if (raw.length === 0) return raw;\r\n\r\n switch (type) {\r\n case 'heikinAshi':\r\n return toHeikinAshi(raw);\r\n case 'renko': {\r\n const box = options.renko?.boxSize;\r\n return typeof box === 'number'\r\n ? toRenko(raw, { brickSize: box })\r\n : toRenko(raw, { brickSize: 0, useATR: true, atrPeriod: options.renko?.atrPeriod ?? 14 });\r\n }\r\n case 'lineBreak':\r\n return toLineBreak(raw, options.lineBreak?.lines ?? 3);\r\n case 'kagi':\r\n return toKagi(raw, options.kagi?.reversal ?? 4, options.kagi?.reversalType ?? 'percent');\r\n case 'pointAndFigure': {\r\n const box = options.pointAndFigure?.boxSize;\r\n return toPointAndFigure(raw, typeof box === 'number' ? box : averageClose(raw) * 0.01, options.pointAndFigure?.reversal ?? 3);\r\n }\r\n case 'rangeBars': {\r\n const range = options.rangeBars?.range;\r\n return toRangeBars(raw, { rangeSize: typeof range === 'number' ? range : averageClose(raw) * 0.005 });\r\n }\r\n default:\r\n return raw;\r\n }\r\n}\r\n\r\n/** Chart types that materially transform raw OHLC into a different series. */\r\nexport function isTransformedChartType(type: ChartType): boolean {\r\n switch (type) {\r\n case 'heikinAshi':\r\n case 'renko':\r\n case 'lineBreak':\r\n case 'kagi':\r\n case 'pointAndFigure':\r\n case 'rangeBars':\r\n return true;\r\n default:\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Transformed chart types that redraw the series into bars of their own\r\n * (bricks, lines, columns), so a drawn bar is not a data bar. Heikin-Ashi\r\n * keeps one bar per data bar.\r\n */\r\nexport function isReshapedChartType(type: ChartType): boolean {\r\n return type !== 'heikinAshi' && isTransformedChartType(type);\r\n}\r\n\r\nfunction averageClose(raw: DataSeries): number {\r\n let sum = 0;\r\n for (const b of raw) sum += b.close;\r\n return sum / raw.length;\r\n}\r\n\r\n/** Look up a registered custom chart-type plugin by its `type` string. */\r\nexport type ChartTypeLookup = (type: string) => ChartTypePlugin | undefined;\r\n\r\n/**\r\n * Resolve the renderer for a chart type, preferring a registered\r\n * `ChartTypePlugin` over the built-in mapping.\r\n */\r\nexport function resolveRenderer(type: string, lookup?: ChartTypeLookup): ChartRendererInterface {\r\n const plugin = lookup?.(type);\r\n if (plugin) return plugin.createRenderer();\r\n return createRendererFor(type as ChartType);\r\n}\r\n\r\n/**\r\n * Resolve the display series for a chart type, preferring a plugin's\r\n * `transform` over the built-in transforms.\r\n */\r\nexport function resolveDisplayData(\r\n type: string,\r\n raw: DataSeries,\r\n lookup?: ChartTypeLookup,\r\n options?: ChartTypeOptions,\r\n): DataSeries {\r\n if (raw.length === 0) return raw;\r\n const plugin = lookup?.(type);\r\n if (plugin?.transform) return plugin.transform(raw);\r\n return transformDisplayData(type as ChartType, raw, options);\r\n}\r\n","/**\r\n * Debounced auto-save scheduler. Coalesces a burst of `schedule()` calls into\r\n * a single `save(key)` invocation after `delayMs` of quiet time. Pure state\r\n * machine — owner injects the actual save callback so this stays testable.\r\n */\r\nexport class AutoSaveScheduler {\r\n private timer: ReturnType<typeof setTimeout> | null = null;\r\n private delayMs = 0;\r\n private key: string | null = null;\r\n\r\n constructor(private save: (key: string) => void) {}\r\n\r\n /** Enable auto-save with the given storage key and quiet-time debounce. */\r\n enable(key: string, delayMs: number): void {\r\n this.key = key;\r\n this.delayMs = delayMs;\r\n }\r\n\r\n /** Disable auto-save and cancel any pending fire. */\r\n disable(): void {\r\n this.key = null;\r\n this.delayMs = 0;\r\n this.cancel();\r\n }\r\n\r\n /**\r\n * Reset the quiet-time window. If currently disabled (no key or delay ≤ 0),\r\n * this is a no-op. Subsequent calls within the window restart the timer.\r\n */\r\n schedule(): void {\r\n if (this.delayMs <= 0 || !this.key) return;\r\n if (this.timer) clearTimeout(this.timer);\r\n this.timer = setTimeout(() => {\r\n this.timer = null;\r\n if (this.key) this.save(this.key);\r\n }, this.delayMs);\r\n }\r\n\r\n /** Cancel any pending fire without changing enabled state. */\r\n cancel(): void {\r\n if (this.timer) {\r\n clearTimeout(this.timer);\r\n this.timer = null;\r\n }\r\n }\r\n\r\n isEnabled(): boolean {\r\n return this.delayMs > 0 && this.key !== null;\r\n }\r\n\r\n hasPending(): boolean {\r\n return this.timer !== null;\r\n }\r\n}\r\n","import type { OHLCBar, DataSeries } from '@tradecanvas/commons';\r\nimport { mergeBar } from '@tradecanvas/commons';\r\n\r\n/** Validate a single OHLC bar. Returns true if bar is usable. */\r\nfunction isValidBar(bar: OHLCBar): boolean {\r\n if (!bar || typeof bar.time !== 'number' || !Number.isFinite(bar.time)) return false;\r\n const { open, high, low, close } = bar;\r\n // Reject NaN / Infinity\r\n if (!isFinite(open) || !isFinite(high) || !isFinite(low) || !isFinite(close)) return false;\r\n // Reject negative prices\r\n if (open < 0 || high < 0 || low < 0 || close < 0) return false;\r\n // High must be >= low\r\n if (high < low) return false;\r\n // Volume (optional) must be non-negative if present\r\n if (bar.volume !== undefined && (!isFinite(bar.volume) || bar.volume < 0)) return false;\r\n return true;\r\n}\r\n\r\n/** Whether a valid bar already satisfies everything `sanitizeBar` enforces. */\r\nfunction isSanitized(bar: OHLCBar): boolean {\r\n return bar.volume !== undefined\r\n && bar.high >= bar.open && bar.high >= bar.close\r\n && bar.low <= bar.open && bar.low <= bar.close;\r\n}\r\n\r\n/** Sanitize a bar: clamp high/low to envelope OHLC values. */\r\nfunction sanitizeBar(bar: OHLCBar): OHLCBar {\r\n return {\r\n ...bar,\r\n high: Math.max(bar.open, bar.high, bar.low, bar.close),\r\n low: Math.min(bar.open, bar.high, bar.low, bar.close),\r\n volume: bar.volume !== undefined ? Math.max(0, bar.volume) : 0,\r\n };\r\n}\r\n\r\nexport class DataManager {\r\n private data: DataSeries = [];\r\n\r\n getData(): DataSeries {\r\n return this.data;\r\n }\r\n\r\n setData(data: DataSeries): void {\r\n // Drop invalid bars and sanitize the rest in one pass. Bars that are\r\n // already well-formed (the norm for exchange data) are kept as-is rather\r\n // than copied — bar objects are never mutated here, only replaced, so\r\n // sharing them with the caller is safe, and it saves allocating a fresh\r\n // object per bar on every symbol/timeframe switch.\r\n const out: OHLCBar[] = [];\r\n for (let i = 0; i < data.length; i++) {\r\n const bar = data[i];\r\n if (!isValidBar(bar)) continue;\r\n out.push(isSanitized(bar) ? bar : sanitizeBar(bar));\r\n }\r\n this.data = out;\r\n }\r\n\r\n /**\r\n * Add older bars in front: those before the first loaded bar, in time\r\n * order, one per time, repaired like `setData`. Returns how many were added.\r\n */\r\n prependBars(bars: readonly OHLCBar[]): number {\r\n const first = this.data.length > 0 ? this.data[0].time : Infinity;\r\n const older: OHLCBar[] = [];\r\n for (let i = 0; i < bars.length; i++) {\r\n const bar = bars[i];\r\n if (!isValidBar(bar) || bar.time >= first) continue;\r\n older.push(isSanitized(bar) ? bar : sanitizeBar(bar));\r\n }\r\n if (older.length === 0) return 0;\r\n older.sort((a, b) => a.time - b.time);\r\n let kept = 0;\r\n for (let i = 0; i < older.length; i++) {\r\n if (kept > 0 && older[kept - 1].time === older[i].time) continue;\r\n older[kept++] = older[i];\r\n }\r\n older.length = kept;\r\n this.data = older.concat(this.data);\r\n return kept;\r\n }\r\n\r\n appendBar(bar: OHLCBar): void {\r\n if (!isValidBar(bar)) return;\r\n this.data.push(sanitizeBar(bar));\r\n }\r\n\r\n updateLastBar(bar: OHLCBar): void {\r\n if (!isValidBar(bar)) return;\r\n const sanitized = sanitizeBar(bar);\r\n if (this.data.length === 0) {\r\n this.data.push(sanitized);\r\n return;\r\n }\r\n this.data[this.data.length - 1] = sanitized;\r\n }\r\n\r\n updateLastBarFromTick(tick: { price: number; volume?: number; time: number }): void {\r\n if (this.data.length === 0) return;\r\n if (!isFinite(tick.price) || tick.price < 0) return;\r\n if (tick.volume !== undefined && (!isFinite(tick.volume) || tick.volume < 0)) return;\r\n this.data[this.data.length - 1] = mergeBar(this.data[this.data.length - 1], tick);\r\n }\r\n\r\n getLength(): number {\r\n return this.data.length;\r\n }\r\n\r\n clear(): void {\r\n this.data = [];\r\n }\r\n}\r\n","import type { HistoryLoadPayload, OHLCBar } from '@tradecanvas/commons';\r\n\r\nexport type { HistoryLoadPayload };\r\n\r\n/**\r\n * Loads up to `limit` bars older than `before` (the oldest loaded bar's time,\r\n * in the bars' own unit), oldest first. An empty result means the history\r\n * starts here.\r\n */\r\nexport type HistoryLoader = (before: number, limit: number) => Promise<OHLCBar[]>;\r\n\r\n/** How long to wait after a failed page before the view asks again; doubles with each failure. */\r\nexport const HISTORY_RETRY_MS = 5_000;\r\n\r\n/** Longest wait between automatic retries. */\r\nexport const HISTORY_RETRY_MAX_MS = 60_000;\r\n\r\n/** Failures in a row after which only an explicit `loadMore` tries again. */\r\nexport const HISTORY_MAX_AUTO_FAILURES = 5;\r\n\r\nexport const DEFAULT_HISTORY_PAGE_SIZE = 500;\r\n\r\nexport interface HistoryPagerHost {\r\n /** Time of the oldest loaded bar, or null without data. */\r\n oldestTime(): number | null;\r\n /** Put a page in front of the data; returns how many bars it added. */\r\n prepend(bars: OHLCBar[]): number;\r\n emit(payload: HistoryLoadPayload): void;\r\n now?(): number;\r\n}\r\n\r\n/**\r\n * Pages older bars in as the view nears the start of the loaded data. One\r\n * request at a time; a page that arrives after the series changed (`reset`)\r\n * or the loader was replaced is dropped. An empty page ends the paging until\r\n * the next series. After a failure the view waits before asking again,\r\n * longer each time, and gives up asking after a few failures in a row.\r\n */\r\nexport class HistoryPager {\r\n private loader: HistoryLoader | null = null;\r\n private pageSize = DEFAULT_HISTORY_PAGE_SIZE;\r\n private loading = false;\r\n private exhausted = false;\r\n private failures = 0;\r\n private retryAt = 0;\r\n /** Bumped whenever an in-flight page must be dropped. */\r\n private seq = 0;\r\n\r\n constructor(private readonly host: HistoryPagerHost) {}\r\n\r\n setLoader(loader: HistoryLoader | null, pageSize = DEFAULT_HISTORY_PAGE_SIZE): void {\r\n this.loader = loader;\r\n this.pageSize = Math.max(1, Math.floor(pageSize));\r\n this.reset();\r\n }\r\n\r\n hasLoader(): boolean {\r\n return this.loader !== null;\r\n }\r\n\r\n /** A new series: drop any page in flight and allow paging again. */\r\n reset(): void {\r\n this.seq++;\r\n this.loading = false;\r\n this.exhausted = false;\r\n this.failures = 0;\r\n this.retryAt = 0;\r\n }\r\n\r\n isLoading(): boolean {\r\n return this.loading;\r\n }\r\n\r\n /** False once a page came back empty. */\r\n hasMore(): boolean {\r\n return !this.exhausted;\r\n }\r\n\r\n /** Load a page when fewer than `ahead` bars are left of the first visible one. */\r\n maybeLoad(firstVisible: number, ahead: number): void {\r\n if (firstVisible >= ahead || !this.canLoad()) return;\r\n if (this.failures >= HISTORY_MAX_AUTO_FAILURES || this.now() < this.retryAt) return;\r\n void this.loadMore();\r\n }\r\n\r\n /** Load one page now, whatever the wait after a failure. Resolves to the number of bars added. */\r\n async loadMore(): Promise<number> {\r\n const loader = this.loader;\r\n const before = this.host.oldestTime();\r\n if (!loader || !this.canLoad() || before === null) return 0;\r\n\r\n const seq = this.seq;\r\n this.loading = true;\r\n this.emit({ state: 'loading', count: 0 });\r\n let page: OHLCBar[];\r\n try {\r\n page = await loader(before, this.pageSize);\r\n } catch (err: unknown) {\r\n if (seq !== this.seq) return 0;\r\n this.loading = false;\r\n this.failures++;\r\n this.retryAt = this.now() + Math.min(HISTORY_RETRY_MAX_MS, HISTORY_RETRY_MS * 2 ** (this.failures - 1));\r\n this.emit({ state: 'error', count: 0, error: err instanceof Error ? err.message : String(err) });\r\n return 0;\r\n }\r\n if (seq !== this.seq) return 0;\r\n this.loading = false;\r\n this.failures = 0;\r\n this.retryAt = 0;\r\n const added = this.host.prepend(page);\r\n if (added === 0) {\r\n this.exhausted = true;\r\n this.emit({ state: 'end', count: 0 });\r\n } else {\r\n this.emit({ state: 'loaded', count: added });\r\n }\r\n return added;\r\n }\r\n\r\n private canLoad(): boolean {\r\n return this.loader !== null && !this.loading && !this.exhausted;\r\n }\r\n\r\n /** A listener that throws must not stall the paging: report it and go on. */\r\n private emit(payload: HistoryLoadPayload): void {\r\n try {\r\n this.host.emit(payload);\r\n } catch (err: unknown) {\r\n console.error('[TradeCanvas] A historyLoad listener threw:', err);\r\n }\r\n }\r\n\r\n private now(): number {\r\n return this.host.now ? this.host.now() : Date.now();\r\n }\r\n}\r\n","import type { ShapeConfig, Theme, ThemeName } from '@tradecanvas/commons';\r\nimport { DARK_THEME, LIGHT_THEME } from '@tradecanvas/commons';\r\n\r\nexport class ThemeManager {\r\n private theme: Theme;\r\n /** Shapes set apart from the theme (`setShapes`): they stay through theme switches. */\r\n private shape: ShapeConfig | null = null;\r\n\r\n constructor(themeOrName?: ThemeName | Theme) {\r\n if (!themeOrName || themeOrName === 'dark') {\r\n this.theme = { ...DARK_THEME };\r\n } else if (themeOrName === 'light') {\r\n this.theme = { ...LIGHT_THEME };\r\n } else {\r\n this.theme = { ...themeOrName };\r\n }\r\n }\r\n\r\n getTheme(): Theme {\r\n return this.theme;\r\n }\r\n\r\n setTheme(themeOrName: ThemeName | Theme): void {\r\n if (themeOrName === 'dark') {\r\n this.theme = { ...DARK_THEME };\r\n } else if (themeOrName === 'light') {\r\n this.theme = { ...LIGHT_THEME };\r\n } else {\r\n this.theme = { ...themeOrName };\r\n }\r\n if (this.shape) this.theme = { ...this.theme, shape: { ...this.theme.shape, ...this.shape } };\r\n }\r\n\r\n /** Shapes over every theme from now on (null: each theme's own). */\r\n setShape(shape: ShapeConfig | null): void {\r\n this.shape = shape ? { ...shape } : null;\r\n this.theme = { ...this.theme, shape: shape ? { ...this.theme.shape, ...shape } : this.theme.shape };\r\n }\r\n}\r\n","import type { PanelPosition, PanelConfig, ResolvedLayout, ResolvedPanel, DividerRect, Rect } from '@tradecanvas/commons';\r\nimport { DEFAULT_PANEL_HEIGHT, MIN_PANEL_HEIGHT, PRICE_AXIS_WIDTH, TIME_AXIS_HEIGHT } from '@tradecanvas/commons';\r\n\r\nconst DEFAULT_PANEL_WIDTH = 200;\r\nconst MIN_PANEL_WIDTH = 80;\r\n/** What a folded pane keeps: its header, with the indicator's name and the pane's buttons. */\r\nexport const COLLAPSED_PANE_HEIGHT = 20;\r\n/** The least the price pane keeps while a pane is maximised. */\r\nexport const MIN_MAIN_HEIGHT_WITH_MAXIMIZED = 48;\r\n\r\nconst isHorizontal = (p: PanelConfig): boolean => p.position === 'top' || p.position === 'bottom';\r\n\r\nexport class LayoutManager {\r\n private panels: PanelConfig[] = [];\r\n private containerWidth = 0;\r\n private containerHeight = 0;\r\n private priceAxisWidth = PRICE_AXIS_WIDTH;\r\n /** Width of the left price scale; 0 while it is hidden. */\r\n private leftAxisWidth = 0;\r\n /** The pane that takes the room of the others (they fold to their headers), or null. */\r\n private maximized: string | null = null;\r\n\r\n resize(width: number, height: number): void {\r\n this.containerWidth = width;\r\n this.containerHeight = height;\r\n }\r\n\r\n addPanel(instanceId: string, position: PanelPosition = 'bottom', size?: number): void {\r\n // A new pane shows: the maximised layout goes back to normal.\r\n this.maximized = null;\r\n const isVertical = position === 'left' || position === 'right';\r\n this.panels.push({\r\n id: instanceId,\r\n position,\r\n size: size ?? (isVertical ? DEFAULT_PANEL_WIDTH : DEFAULT_PANEL_HEIGHT),\r\n minSize: isVertical ? MIN_PANEL_WIDTH : MIN_PANEL_HEIGHT,\r\n content: { type: 'indicator', indicatorInstanceId: instanceId },\r\n });\r\n }\r\n\r\n /** Hand a pane to another indicator, keeping its place and size. */\r\n renamePanel(fromId: string, toId: string): void {\r\n const panel = this.panels.find((p) => p.id === fromId);\r\n if (!panel) return;\r\n panel.id = toId;\r\n panel.content = { ...panel.content, indicatorInstanceId: toId };\r\n if (this.maximized === fromId) this.maximized = toId;\r\n }\r\n\r\n removePanel(instanceId: string): void {\r\n this.panels = this.panels.filter((p) => p.id !== instanceId);\r\n if (this.maximized === instanceId) this.maximized = null;\r\n }\r\n\r\n /**\r\n * Fold a top or bottom pane to its header, or open it again. While a pane\r\n * is maximised this puts the layout back to normal first (the other panes\r\n * show folded then). False when nothing changed.\r\n */\r\n setPanelCollapsed(instanceId: string, collapsed: boolean): boolean {\r\n const panel = this.panels.find((p) => p.id === instanceId);\r\n if (!panel || !isHorizontal(panel)) return false;\r\n if (this.maximized === null && !!panel.collapsed === collapsed) return false;\r\n this.maximized = null;\r\n panel.collapsed = collapsed || undefined;\r\n return true;\r\n }\r\n\r\n /** Folded as set (see `isPanelShownCollapsed` for how it shows). */\r\n isPanelCollapsed(instanceId: string): boolean {\r\n return !!this.panels.find((p) => p.id === instanceId)?.collapsed;\r\n }\r\n\r\n /** Shown folded: folded as set, or another pane is maximised. */\r\n isPanelShownCollapsed(instanceId: string): boolean {\r\n const panel = this.panels.find((p) => p.id === instanceId);\r\n if (!panel || !isHorizontal(panel)) return false;\r\n return this.maximized !== null ? this.maximized !== instanceId : !!panel.collapsed;\r\n }\r\n\r\n /** A pane's value scale: logarithmic, upside down, in percent. False when nothing changed. */\r\n setPanelScale(instanceId: string, scale: { log?: boolean; invert?: boolean; percent?: boolean }): boolean {\r\n const panel = this.panels.find((p) => p.id === instanceId);\r\n if (!panel) return false;\r\n let changed = false;\r\n if (scale.log !== undefined && !!panel.logScale !== scale.log) {\r\n panel.logScale = scale.log || undefined;\r\n changed = true;\r\n }\r\n if (scale.invert !== undefined && !!panel.invertScale !== scale.invert) {\r\n panel.invertScale = scale.invert || undefined;\r\n changed = true;\r\n }\r\n if (scale.percent !== undefined && !!panel.percentScale !== scale.percent) {\r\n panel.percentScale = scale.percent || undefined;\r\n changed = true;\r\n }\r\n return changed;\r\n }\r\n\r\n /** Whether `movePanel(instanceId, delta)` would move it. */\r\n canMovePanel(instanceId: string, delta: -1 | 1): boolean {\r\n const panel = this.panels.find((p) => p.id === instanceId);\r\n if (!panel) return false;\r\n const group = this.panels.filter((p) => p.position === panel.position);\r\n return group[group.indexOf(panel) + delta] !== undefined;\r\n }\r\n\r\n /**\r\n * Let one top or bottom pane take the room: the others fold to their\r\n * headers and the price pane keeps a strip. `null` puts them back.\r\n */\r\n setMaximizedPanel(instanceId: string | null): boolean {\r\n const panel = instanceId === null ? null : this.panels.find((p) => p.id === instanceId);\r\n if (instanceId !== null && (!panel || !isHorizontal(panel))) return false;\r\n if (this.maximized === instanceId) return false;\r\n this.maximized = instanceId;\r\n if (panel) panel.collapsed = undefined;\r\n return true;\r\n }\r\n\r\n getMaximizedPanel(): string | null {\r\n return this.maximized;\r\n }\r\n\r\n /**\r\n * Move a pane one place up (`-1`) or down (`1`) among the panes on its side\r\n * of the price pane. False at the end of the line.\r\n */\r\n movePanel(instanceId: string, delta: -1 | 1): boolean {\r\n const panel = this.panels.find((p) => p.id === instanceId);\r\n if (!panel) return false;\r\n const group = this.panels.filter((p) => p.position === panel.position);\r\n const at = group.indexOf(panel);\r\n const other = group[at + delta];\r\n if (!other) return false;\r\n const i = this.panels.indexOf(panel);\r\n const j = this.panels.indexOf(other);\r\n const next = [...this.panels];\r\n next[i] = other;\r\n next[j] = panel;\r\n this.panels = next;\r\n return true;\r\n }\r\n\r\n /** Put the panes on each side in the order of `ids` (others keep their place after them). */\r\n orderPanels(ids: readonly string[]): void {\r\n const rank = (p: PanelConfig) => {\r\n const at = ids.indexOf(p.id);\r\n return at < 0 ? ids.length + this.panels.indexOf(p) : at;\r\n };\r\n this.panels = [...this.panels].sort((a, b) => rank(a) - rank(b));\r\n }\r\n\r\n /** A pane's height (or width) on screen: folded, maximised or as set. */\r\n private shownSize(panel: PanelConfig, maximizedSize: number): number {\r\n if (!isHorizontal(panel)) return panel.size;\r\n if (this.maximized) return panel.id === this.maximized ? maximizedSize : COLLAPSED_PANE_HEIGHT;\r\n return panel.collapsed ? COLLAPSED_PANE_HEIGHT : panel.size;\r\n }\r\n\r\n setPanelPosition(instanceId: string, position: PanelPosition): void {\r\n const panel = this.panels.find((p) => p.id === instanceId);\r\n if (panel) {\r\n const wasVertical = panel.position === 'left' || panel.position === 'right';\r\n const isVertical = position === 'left' || position === 'right';\r\n panel.position = position;\r\n if (wasVertical !== isVertical) {\r\n panel.size = isVertical ? DEFAULT_PANEL_WIDTH : DEFAULT_PANEL_HEIGHT;\r\n panel.minSize = isVertical ? MIN_PANEL_WIDTH : MIN_PANEL_HEIGHT;\r\n }\r\n // Side panes neither fold nor maximise.\r\n if (isVertical) {\r\n panel.collapsed = undefined;\r\n if (this.maximized === instanceId) this.maximized = null;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Resize a pane; a folded pane opens, and a maximised layout goes back to\r\n * normal. A height is kept within the room there is (a stored layout may ask\r\n * for more).\r\n */\r\n setPanelSize(instanceId: string, size: number): void {\r\n const panel = this.panels.find((p) => p.id === instanceId);\r\n if (panel) {\r\n const room = isHorizontal(panel) ? this.containerHeight - TIME_AXIS_HEIGHT - MIN_MAIN_HEIGHT_WITH_MAXIMIZED : this.containerWidth / 2;\r\n const capped = room > panel.minSize ? Math.min(size, room) : size;\r\n panel.size = Math.max(panel.minSize, capped);\r\n panel.collapsed = undefined;\r\n this.maximized = null;\r\n }\r\n }\r\n\r\n /** Width reserved for the price axis right of the main chart. */\r\n setPriceAxisWidth(width: number): void {\r\n this.priceAxisWidth = width;\r\n }\r\n\r\n /** Width reserved left of the plot for the left price scale (0 = none). */\r\n setLeftAxisWidth(width: number): void {\r\n this.leftAxisWidth = Math.max(0, width);\r\n }\r\n\r\n getPanels(): PanelConfig[] {\r\n return this.panels;\r\n }\r\n\r\n resolve(): ResolvedLayout {\r\n const leftPanels = this.panels.filter((p) => p.position === 'left');\r\n const rightPanels = this.panels.filter((p) => p.position === 'right');\r\n const topPanels = this.panels.filter((p) => p.position === 'top');\r\n const bottomPanels = this.panels.filter((p) => p.position === 'bottom');\r\n\r\n // A maximised pane takes what the price pane and the folded panes leave.\r\n const folded = (topPanels.length + bottomPanels.length - 1) * COLLAPSED_PANE_HEIGHT;\r\n const maximizedPanel = this.maximized ? this.panels.find((p) => p.id === this.maximized) : undefined;\r\n const maximizedSize = Math.max(\r\n maximizedPanel?.minSize ?? 0,\r\n this.containerHeight - TIME_AXIS_HEIGHT - MIN_MAIN_HEIGHT_WITH_MAXIMIZED - folded,\r\n );\r\n const size = (p: PanelConfig) => this.shownSize(p, maximizedSize);\r\n\r\n const leftWidth = leftPanels.reduce((s, p) => s + p.size, 0);\r\n const rightWidth = rightPanels.reduce((s, p) => s + p.size, 0);\r\n const topHeight = topPanels.reduce((s, p) => s + size(p), 0);\r\n const bottomHeight = bottomPanels.reduce((s, p) => s + size(p), 0);\r\n\r\n const plotX = leftWidth + this.leftAxisWidth;\r\n const mainChartRect: Rect = {\r\n x: plotX,\r\n y: topHeight,\r\n width: Math.max(0, this.containerWidth - plotX - rightWidth - this.priceAxisWidth),\r\n height: Math.max(0, this.containerHeight - topHeight - bottomHeight - TIME_AXIS_HEIGHT),\r\n };\r\n\r\n const resolvedPanels: ResolvedPanel[] = [];\r\n const dividers: DividerRect[] = [];\r\n\r\n // Left panels (stacked vertically)\r\n let x = 0;\r\n for (const panel of leftPanels) {\r\n resolvedPanels.push({\r\n config: panel,\r\n rect: { x, y: 0, width: panel.size, height: this.containerHeight - TIME_AXIS_HEIGHT },\r\n });\r\n dividers.push({\r\n panelId: panel.id,\r\n rect: { x: x + panel.size - 2, y: 0, width: 4, height: this.containerHeight },\r\n orientation: 'vertical',\r\n });\r\n x += panel.size;\r\n }\r\n\r\n // Top panels (stacked horizontally, full width minus left/right)\r\n let y = 0;\r\n for (const panel of topPanels) {\r\n const height = size(panel);\r\n resolvedPanels.push({\r\n config: panel,\r\n rect: { x: plotX, y, width: mainChartRect.width, height },\r\n });\r\n dividers.push({\r\n panelId: panel.id,\r\n rect: { x: plotX, y: y + height - 2, width: mainChartRect.width, height: 4 },\r\n orientation: 'horizontal',\r\n });\r\n y += height;\r\n }\r\n\r\n // Bottom panels\r\n y = mainChartRect.y + mainChartRect.height;\r\n for (const panel of bottomPanels) {\r\n const height = size(panel);\r\n resolvedPanels.push({\r\n config: panel,\r\n rect: { x: plotX, y, width: mainChartRect.width, height },\r\n });\r\n dividers.push({\r\n panelId: panel.id,\r\n rect: { x: plotX, y: y - 2, width: mainChartRect.width, height: 4 },\r\n orientation: 'horizontal',\r\n });\r\n y += height;\r\n }\r\n\r\n // Right panels\r\n x = plotX + mainChartRect.width + this.priceAxisWidth;\r\n for (const panel of rightPanels) {\r\n resolvedPanels.push({\r\n config: panel,\r\n rect: { x, y: 0, width: panel.size, height: this.containerHeight - TIME_AXIS_HEIGHT },\r\n });\r\n dividers.push({\r\n panelId: panel.id,\r\n rect: { x: x - 2, y: 0, width: 4, height: this.containerHeight },\r\n orientation: 'vertical',\r\n });\r\n x += panel.size;\r\n }\r\n\r\n return { mainChartRect, panels: resolvedPanels, dividers };\r\n }\r\n\r\n getMainChartRect(): Rect {\r\n return this.resolve().mainChartRect;\r\n }\r\n}\r\n","import { PRICE_AXIS_WIDTH, autoPricePrecision, formatPrice } from '@tradecanvas/commons';\r\n\r\n/** Axis label: 8px tick notch + gap on the left, 6px margin on the right. */\r\nconst LABEL_CHROME = 14;\r\n/** Last-price tag: 12px badge padding + 2px margin. */\r\nconst PRICE_TAG_CHROME = 14;\r\n/** Crosshair pill: 16px padding + 5px notch + 1px margin. */\r\nconst CROSSHAIR_PILL_CHROME = 22;\r\n/** Widths snap to this grid so a one-pixel label change doesn't relayout. */\r\nconst WIDTH_GRANULARITY = 4;\r\n/** Shrinking waits until this much room is spare, so panning doesn't make the axis twitch. */\r\nexport const AXIS_SHRINK_SLACK = 12;\r\n\r\nexport interface PriceAxisWidthInput {\r\n min: number;\r\n max: number;\r\n lastPrice: number | null;\r\n /** Decimals for the price tags (a market's `pricePrecision`), or null to follow the axis. */\r\n tagPrecision: number | null;\r\n locale: string;\r\n /** The chart's price format, when it has one: labels and tags print with it. */\r\n format?: ((price: number) => string) | null;\r\n fontFamily: string;\r\n fontSizeSmall: number;\r\n measure: (text: string, font: string) => number;\r\n}\r\n\r\n/**\r\n * Width the price axis needs for its widest label — tick labels, the\r\n * last-price tag and the crosshair pill — never below `PRICE_AXIS_WIDTH`.\r\n * A sub-cent asset (\"0,000004349\") needs ~90px where BTC fits the default.\r\n */\r\nexport function requiredPriceAxisWidth(input: PriceAxisWidthInput): number {\r\n const { min, max, lastPrice, locale, fontFamily, fontSizeSmall, measure } = input;\r\n const axisPrecision = autoPricePrecision(min, max);\r\n const tagPrecision = input.tagPrecision ?? axisPrecision;\r\n const prices = lastPrice !== null ? [min, max, lastPrice] : [min, max];\r\n\r\n let need = PRICE_AXIS_WIDTH;\r\n for (const price of prices) {\r\n if (!Number.isFinite(price)) continue;\r\n const label = input.format ? input.format(price) : formatPrice(price, axisPrecision, locale);\r\n const tag = input.format ? label : formatPrice(price, tagPrecision, locale);\r\n need = Math.max(\r\n need,\r\n measure(label, `500 ${fontSizeSmall}px ${fontFamily}`) + LABEL_CHROME,\r\n measure(tag, `bold 11px ${fontFamily}`) + PRICE_TAG_CHROME,\r\n measure(tag, `600 ${fontSizeSmall}px ${fontFamily}`) + CROSSHAIR_PILL_CHROME,\r\n );\r\n }\r\n // The default stays exact; only widened axes snap to the grid.\r\n return need <= PRICE_AXIS_WIDTH ? PRICE_AXIS_WIDTH : Math.ceil(need / WIDTH_GRANULARITY) * WIDTH_GRANULARITY;\r\n}\r\n\r\n/** The axis width to use next: grow at once, shrink only once enough room is spare. */\r\nexport function nextPriceAxisWidth(current: number, required: number): number {\r\n if (required > current) return required;\r\n if (required <= current - AXIS_SHRINK_SLACK) return required;\r\n return current;\r\n}\r\n","/** Net vertical travel (px) a drag needs before it may move the price scale. */\r\nexport const VERTICAL_PAN_ENGAGE_PX = 24;\r\n/** …and that travel must be at least this share of the horizontal travel. */\r\nexport const VERTICAL_PAN_MIN_RATIO = 0.5;\r\n\r\n/**\r\n * Decides, per drag gesture, when a chart-body drag should also pan the price\r\n * scale (which turns auto-scale off). A horizontal drag always drifts a few\r\n * pixels vertically; engaging on that drift knocked the chart off auto-scale\r\n * mid-pan, so the price scale stopped following the bars and wobbled with the\r\n * hand — the drag felt stuck. Now it takes a deliberate vertical move.\r\n * With auto-scale already off the price scale pans freely, as before.\r\n */\r\nexport class VerticalPanGate {\r\n private netY = 0;\r\n private travelX = 0;\r\n private engaged = false;\r\n\r\n /** Start of a new drag gesture. */\r\n reset(): void {\r\n this.netY = 0;\r\n this.travelX = 0;\r\n this.engaged = false;\r\n }\r\n\r\n /** Feed one drag step; true once vertical panning is engaged for this gesture. */\r\n step(deltaX: number, deltaY: number, autoScaleOn: boolean): boolean {\r\n if (this.engaged) return true;\r\n this.netY += deltaY;\r\n this.travelX += Math.abs(deltaX);\r\n if (!autoScaleOn) {\r\n this.engaged = true;\r\n } else {\r\n const dy = Math.abs(this.netY);\r\n this.engaged = dy > VERTICAL_PAN_ENGAGE_PX && dy >= this.travelX * VERTICAL_PAN_MIN_RATIO;\r\n }\r\n return this.engaged;\r\n }\r\n}\r\n","import type {\r\n IndicatorPlugin,\r\n DrawingPlugin,\r\n DataSeries,\r\n ViewportState,\r\n Theme,\r\n Point,\r\n} from '@tradecanvas/commons';\r\nimport type { ChartRendererInterface } from '@tradecanvas/core';\r\n\r\n/**\r\n * Plugin SDK contracts — FROZEN for 1.0. Authors extend the chart along four\r\n * axes. `IndicatorPlugin` and `DrawingPlugin` are re-used verbatim from\r\n * commons; `ChartTypePlugin` and `OverlayPlugin` are defined here because they\r\n * reference core's renderer interface.\r\n */\r\nexport const PLUGIN_API_VERSION = 1;\r\n\r\n/** A custom chart type: a renderer plus an optional data transform. */\r\nexport interface ChartTypePlugin {\r\n descriptor: { type: string; name: string };\r\n createRenderer(): ChartRendererInterface;\r\n /** Derive the displayed series from raw OHLC (e.g. Heikin-Ashi, Renko). */\r\n transform?(data: DataSeries): DataSeries;\r\n}\r\n\r\n/** Render context handed to overlay plugins each frame. */\r\nexport interface OverlayRenderContext {\r\n viewport: ViewportState;\r\n data: DataSeries;\r\n theme: Theme;\r\n}\r\n\r\n/** A custom data overlay (e.g. a volume-profile-style layer). */\r\nexport interface OverlayPlugin {\r\n descriptor: { id: string; name: string; layer?: 'main' | 'overlay' | 'ui' };\r\n render(ctx: CanvasRenderingContext2D, context: OverlayRenderContext): void;\r\n hitTest?(point: Point, context: OverlayRenderContext): boolean;\r\n}\r\n\r\n/** Discriminated union accepted by `registerPlugin` and `chart.plugins.register`. */\r\nexport type ChartPlugin =\r\n | { kind: 'indicator'; plugin: IndicatorPlugin }\r\n | { kind: 'drawing'; plugin: DrawingPlugin }\r\n | { kind: 'chartType'; plugin: ChartTypePlugin }\r\n | { kind: 'overlay'; plugin: OverlayPlugin };\r\n\r\n/** Overlays whose target layer matches `layer` (the default layer is `overlay`). */\r\nexport function overlaysForLayer(\r\n overlays: OverlayPlugin[],\r\n layer: 'main' | 'overlay' | 'ui',\r\n): OverlayPlugin[] {\r\n return overlays.filter((o) => (o.descriptor.layer ?? 'overlay') === layer);\r\n}\r\n\r\n/** Stable identity for a plugin, used for registry de-duplication and `list()`. */\r\nexport function pluginKey(p: ChartPlugin): string {\r\n switch (p.kind) {\r\n case 'indicator':\r\n return `indicator:${p.plugin.descriptor.id}`;\r\n case 'drawing':\r\n return `drawing:${p.plugin.descriptor.type}`;\r\n case 'chartType':\r\n return `chartType:${p.plugin.descriptor.type}`;\r\n case 'overlay':\r\n return `overlay:${p.plugin.descriptor.id}`;\r\n }\r\n}\r\n","import type { ChartPlugin } from './contracts.js';\r\nimport { pluginKey } from './contracts.js';\r\n\r\n/**\r\n * Global default plugin registry. Every `Chart` created *after* a plugin is\r\n * registered inherits it (unless `inheritGlobal: false`). For per-chart scoping\r\n * use the constructor `plugins` option or `chart.plugins.register`.\r\n */\r\nconst globalPlugins = new Map<string, ChartPlugin>();\r\n\r\n/** Register a plugin globally. Re-registering the same key replaces it. */\r\nexport function registerPlugin(plugin: ChartPlugin): void {\r\n globalPlugins.set(pluginKey(plugin), plugin);\r\n}\r\n\r\n/** Remove a global plugin by its `pluginKey`. Returns true if it existed. */\r\nexport function unregisterPlugin(key: string): boolean {\r\n return globalPlugins.delete(key);\r\n}\r\n\r\n/** Snapshot of all globally registered plugins. */\r\nexport function getGlobalPlugins(): ChartPlugin[] {\r\n return [...globalPlugins.values()];\r\n}\r\n\r\n/** Clear the global registry (test/teardown helper). */\r\nexport function clearGlobalPlugins(): void {\r\n globalPlugins.clear();\r\n}\r\n","import type { IndicatorPlugin } from '@tradecanvas/commons';\r\nimport type { IndicatorEngine } from '@tradecanvas/core';\r\nimport type { ChartPlugin, ChartTypePlugin, OverlayPlugin } from './contracts.js';\r\nimport { pluginKey } from './contracts.js';\r\nimport { getGlobalPlugins } from './registry.js';\r\n\r\nexport interface PluginManagerOptions {\r\n /** Per-instance plugins (from `new Chart(el, { plugins })`); merged over globals. */\r\n plugins?: ChartPlugin[];\r\n /** Inherit the global registry at construction (default: true). */\r\n inheritGlobal?: boolean;\r\n}\r\n\r\n/**\r\n * Per-chart plugin registry. Resolves plugins in precedence order:\r\n * global defaults → constructor `plugins` → imperative `register()`.\r\n * Indicator plugins additionally flow into the `IndicatorEngine` so\r\n * `chart.addIndicator(id)` can find them by descriptor id.\r\n */\r\nexport class PluginManager {\r\n private plugins = new Map<string, ChartPlugin>();\r\n\r\n constructor(\r\n private indicatorEngine: IndicatorEngine,\r\n options: PluginManagerOptions = {},\r\n ) {\r\n if (options.inheritGlobal !== false) {\r\n for (const p of getGlobalPlugins()) this.add(p);\r\n }\r\n for (const p of options.plugins ?? []) this.add(p);\r\n }\r\n\r\n /** Register a plugin on this chart instance. */\r\n register(plugin: ChartPlugin): void {\r\n this.add(plugin);\r\n }\r\n\r\n /** Remove a plugin by its `pluginKey`. Returns true if it existed. */\r\n unregister(key: string): boolean {\r\n return this.plugins.delete(key);\r\n }\r\n\r\n /** All plugins active on this instance. */\r\n list(): ChartPlugin[] {\r\n return [...this.plugins.values()];\r\n }\r\n\r\n /** Back-compat shorthand for registering an indicator plugin. */\r\n registerIndicator(plugin: IndicatorPlugin): void {\r\n this.add({ kind: 'indicator', plugin });\r\n }\r\n\r\n /** Look up a registered custom chart type by its `type` string. */\r\n getChartType(type: string): ChartTypePlugin | undefined {\r\n const p = this.plugins.get(`chartType:${type}`);\r\n return p?.kind === 'chartType' ? p.plugin : undefined;\r\n }\r\n\r\n /** All registered overlay plugins. */\r\n getOverlays(): OverlayPlugin[] {\r\n const out: OverlayPlugin[] = [];\r\n for (const p of this.plugins.values()) {\r\n if (p.kind === 'overlay') out.push(p.plugin);\r\n }\r\n return out;\r\n }\r\n\r\n private add(plugin: ChartPlugin): void {\r\n this.plugins.set(pluginKey(plugin), plugin);\r\n // Indicator plugins must reach the engine so they're resolvable by id.\r\n if (plugin.kind === 'indicator') {\r\n this.indicatorEngine.register(plugin.plugin);\r\n }\r\n }\r\n}\r\n","import type {\r\n ExecutionAdapter,\r\n ExecutionEvent,\r\n ExecutionError,\r\n TradingOrder,\r\n TradingPosition,\r\n OrderPlaceIntent,\r\n OrderModifyIntent,\r\n OrderCancelIntent,\r\n PositionModifyIntent,\r\n PositionCloseIntent,\r\n PositionReverseIntent,\r\n FillEvent,\r\n} from '@tradecanvas/commons';\r\n\r\n/** The order/position intents the chart emits and an adapter consumes. */\r\nexport type ExecutionIntentType =\r\n | 'orderPlace'\r\n | 'orderModify'\r\n | 'orderCancel'\r\n | 'positionModify'\r\n | 'positionClose'\r\n | 'positionReverse';\r\n\r\n/**\r\n * Surface the host (the Chart) provides so execution can be bridged without\r\n * the helper knowing anything about chart internals — which keeps it pure and\r\n * unit-testable.\r\n */\r\nexport interface ExecutionHost {\r\n /** Subscribe to an emitted intent; returns an unsubscribe function. */\r\n onIntent(type: ExecutionIntentType, handler: (payload: unknown) => void): () => void;\r\n /** Render the adapter's authoritative pending orders. */\r\n setOrders(orders: TradingOrder[]): void;\r\n /** Render the adapter's authoritative open positions. */\r\n setPositions(positions: TradingPosition[]): void;\r\n /** Surface an execution failure (adapter-reported or a failed command). */\r\n onError(error: ExecutionError): void;\r\n /** An order filled or a position closed (the adapter's `fill`). */\r\n onFill?(fill: FillEvent): void;\r\n /** The positions on the chart, until the adapter reports its own. */\r\n getPositions?(): readonly TradingPosition[];\r\n}\r\n\r\n/** What a command can read: the latest positions, and the reverses under way. */\r\ninterface CommandContext {\r\n positions: () => readonly TradingPosition[];\r\n reversing: Set<string>;\r\n}\r\n\r\nconst COMMANDS: Record<\r\n ExecutionIntentType,\r\n (adapter: ExecutionAdapter, payload: unknown, context: CommandContext) => Promise<unknown>\r\n> = {\r\n orderPlace: (a, p) => a.placeOrder(p as OrderPlaceIntent),\r\n orderModify: (a, p) => a.modifyOrder(p as OrderModifyIntent),\r\n orderCancel: (a, p) => a.cancelOrder(p as OrderCancelIntent),\r\n positionModify: (a, p) => a.modifyPosition(p as PositionModifyIntent),\r\n positionClose: (a, p) => a.closePosition(p as PositionCloseIntent),\r\n positionReverse: (a, p, context) => reverse(a, p as PositionReverseIntent, context),\r\n};\r\n\r\n/**\r\n * Reverse a position: the adapter's own `reversePosition`, or else close it\r\n * and place a market order the other way for the size that was open. A\r\n * second reverse of the same position while one is under way is ignored.\r\n */\r\nasync function reverse(adapter: ExecutionAdapter, intent: PositionReverseIntent, context: CommandContext): Promise<void> {\r\n const { reversing } = context;\r\n if (reversing.has(intent.positionId)) return;\r\n reversing.add(intent.positionId);\r\n try {\r\n if (adapter.reversePosition) {\r\n await adapter.reversePosition(intent);\r\n return;\r\n }\r\n const position = context.positions().find((p) => p.id === intent.positionId);\r\n if (!position) throw new Error(`No position ${intent.positionId} to reverse`);\r\n const quantity = position.quantity - (position.closedQuantity ?? 0);\r\n if (!(quantity > 0)) throw new Error(`Position ${intent.positionId} has nothing open to reverse`);\r\n await adapter.closePosition({ positionId: position.id });\r\n try {\r\n await adapter.placeOrder({\r\n side: position.side === 'buy' ? 'sell' : 'buy',\r\n type: 'market',\r\n price: position.entryPrice,\r\n quantity,\r\n });\r\n } catch (cause) {\r\n throw new Error(`Position ${intent.positionId} was closed, but the order the other way failed`, { cause });\r\n }\r\n } finally {\r\n reversing.delete(intent.positionId);\r\n }\r\n}\r\n\r\n/**\r\n * Bridge an `ExecutionAdapter` to a host: render the adapter's authoritative\r\n * orders/positions, and route the host's emitted intents into adapter\r\n * commands. The adapter is the single source of truth. Returns a teardown that\r\n * removes every subscription.\r\n */\r\nexport function wireExecution(adapter: ExecutionAdapter, host: ExecutionHost): () => void {\r\n const teardowns: Array<() => void> = [];\r\n\r\n // Adapter → host: authoritative state + errors. The positions are kept to\r\n // reverse one when the adapter can't.\r\n let positions: readonly TradingPosition[] | null = null;\r\n const context: CommandContext = {\r\n positions: () => positions ?? host.getPositions?.() ?? [],\r\n reversing: new Set(),\r\n };\r\n const onOrders = (e: ExecutionEvent<TradingOrder[]>) => host.setOrders(e.data ?? []);\r\n const onPositions = (e: ExecutionEvent<TradingPosition[]>) => {\r\n positions = e.data ?? [];\r\n host.setPositions([...positions]);\r\n };\r\n const onError = (e: ExecutionEvent<ExecutionError>) =>\r\n host.onError(e.data ?? { message: 'Execution error' });\r\n const onFill = (e: ExecutionEvent<FillEvent>) => {\r\n if (e.data) host.onFill?.(e.data);\r\n };\r\n adapter.on<TradingOrder[]>('orders', onOrders);\r\n adapter.on<TradingPosition[]>('positions', onPositions);\r\n adapter.on<ExecutionError>('error', onError);\r\n adapter.on<FillEvent>('fill', onFill);\r\n teardowns.push(\r\n () => adapter.off('orders', onOrders),\r\n () => adapter.off('positions', onPositions),\r\n () => adapter.off('error', onError),\r\n () => adapter.off('fill', onFill),\r\n );\r\n\r\n // Host → adapter: route each emitted intent into the matching command.\r\n for (const type of Object.keys(COMMANDS) as ExecutionIntentType[]) {\r\n const command = COMMANDS[type];\r\n const unsub = host.onIntent(type, (payload) => {\r\n Promise.resolve(command(adapter, payload, context)).catch((cause) =>\r\n host.onError({ message: `Execution command failed: ${type}`, cause }),\r\n );\r\n });\r\n teardowns.push(unsub);\r\n }\r\n\r\n return () => {\r\n for (const teardown of teardowns) teardown();\r\n };\r\n}\r\n","import type {\r\n ChartOptions,\r\n ChartType,\r\n OHLCBar,\r\n DataSeries,\r\n Theme,\r\n ThemeName,\r\n ChartEventType,\r\n ChartEvent,\r\n ChartEventMap,\r\n TauriBridgeOptions,\r\n IndicatorPlugin,\r\n IndicatorDescriptor,\r\n IndicatorOutput,\r\n ResolvedIndicatorStyle,\r\n DrawingToolType,\r\n DrawingState,\r\n DrawingStyle,\r\n DrawingOptions,\r\n DrawingOptionDefs,\r\n DrawingDescriptor,\r\n DrawingPlugin,\r\n PanelPosition,\r\n TradingOrder,\r\n TradingPosition,\r\n DepthData,\r\n TradingConfig,\r\n MarketConfig,\r\n Locale,\r\n StreamConfig,\r\n ConnectionState,\r\n ConnectionInfo,\r\n TimeFrame,\r\n DataAdapter,\r\n TimeZoneSetting,\r\n SymbolInfo,\r\n OverlayScale,\r\n ViewportState,\r\n FeaturesConfig,\r\n ExecutionAdapter,\r\n ExecutionConfig,\r\n} from '@tradecanvas/commons';\r\nimport { isValidTimeZone, sessionMinute, LayerType, setLocale as setGlobalLocale, computePriceLimits, PRICE_AXIS_WIDTH, autoPricePrecision, formatPrice, parseIndicatorSource, indicatorSource, stepDecimals, priceFormatterFor, fractionTick } from '@tradecanvas/commons';\r\nimport type { ChartTypeOptions, PriceFormatter, PriceFraction, ShapeConfig, TimeFormatter } from '@tradecanvas/commons';\r\nimport { readChartTypeOptions, tickBarCount, normalizeBarTime } from '@tradecanvas/commons';\r\nimport { PriceLines, type BidAsk, SymbolSeriesStore, CompareSymbolIndicator, SpreadIndicator, HiLoRenderer } from '@tradecanvas/core';\r\nimport { regularHoursFilter } from './regularHours.js';\r\nimport { ChartA11y } from './chartA11y.js';\r\nimport { zonedDateFormatter } from '@tradecanvas/commons';\r\nimport { indicatorChipLabel } from './indicatorLabel.js';\r\nimport type { ExportColumn } from '@tradecanvas/core';\r\nimport {\r\n RenderEngine,\r\n Viewport,\r\n withResampling,\r\n servesTimeframe,\r\n GridRenderer,\r\n PriceAxis,\r\n TimeAxis,\r\n InteractionManager,\r\n PaneResizeHandler,\r\n PanHandler,\r\n ZoomHandler,\r\n AxisDragHandler,\r\n AlertDragHandler,\r\n CrosshairHandler,\r\n IndicatorEngine,\r\n registerBuiltInIndicators,\r\n sourceParam,\r\n EventBus,\r\n DrawingManager,\r\n DrawingRenderer,\r\n registerBuiltInDrawingTools,\r\n TradingManager,\r\n TradingRenderer,\r\n StreamManager,\r\n ChartLegend,\r\n Screenshot,\r\n Watermark,\r\n BarCountdown,\r\n VolumeRenderer,\r\n VolumeProfileRenderer,\r\n MarketProfileRenderer,\r\n DepthHeatmapRenderer,\r\n PeriodLevelsRenderer,\r\n PivotMarkersRenderer,\r\n AlertManager,\r\n SignalMarkerManager,\r\n TradeZoneManager,\r\n MeasureOverlay,\r\n SelectionBoxOverlay,\r\n timestampToBarIndex,\r\n barTimeStepMs,\r\n rangePresetStart,\r\n ReplayManager,\r\n ChartStateManager,\r\n UndoRedoManager,\r\n Animator,\r\n KeyboardHandler,\r\n CrosshairTooltip,\r\n PinnedTooltip,\r\n DataExporter,\r\n SessionBreaks,\r\n SessionShading,\r\n DEFAULT_SESSION_HOURS,\r\n mergeSessionHours,\r\n CompareRenderer,\r\n CurrentPriceLine,\r\n xToBarIndex,\r\n xToTime,\r\n yToPrice,\r\n PriceAxisAddButton,\r\n findDominantSwing,\r\n readAlertOptions,\r\n} from '@tradecanvas/core';\r\nimport type { ChartRendererInterface, RangePreset, SessionHoursConfig, DrawingPatch, DrawingOrderMove } from '@tradecanvas/core';\r\nimport { timeframeToMs } from '@tradecanvas/commons';\r\nimport { resolveRenderer, resolveDisplayData, isReshapedChartType } from './charts/ChartTypeStrategy.js';\r\nimport { AutoSaveScheduler } from './state/AutoSaveScheduler.js';\r\nimport { DataManager } from './DataManager.js';\r\nimport { HistoryPager, type HistoryLoader } from './HistoryPager.js';\r\nimport { ThemeManager } from './ThemeManager.js';\r\nimport { LayoutManager } from './layout/LayoutManager.js';\r\nimport { requiredPriceAxisWidth, nextPriceAxisWidth } from './layout/priceAxisWidth.js';\r\nimport { VerticalPanGate } from './interaction/verticalPanGate.js';\r\nimport { PluginManager } from './plugins/PluginManager.js';\r\nimport { wireExecution } from './trading/wireExecution.js';\r\nimport { overlaysForLayer, type ChartPlugin } from './plugins/contracts.js';\r\n\r\n// Replaced at build time by Vite `define` (see vite.config.ts). The `typeof`\r\n// guard keeps this safe when the source runs un-bundled (tests, ts-node).\r\ndeclare const __TC_VERSION__: string;\r\n\r\n/** The scale of an indicator pane with nothing to fit yet. */\r\nconst DEFAULT_PANE_RANGE = { min: 0, max: 100 } as const;\r\n\r\n/** Bar times at or below this are seconds, not milliseconds (as `normalizeBarTime`). */\r\nconst SECONDS_TIME_LIMIT = 1e12;\r\n/** `setTimezone(EXCHANGE_TIMEZONE)` shows the time zone of the symbol's exchange. */\r\nexport const EXCHANGE_TIMEZONE = 'exchange';\r\n/** Throws a RangeError for a time zone the browser doesn't know or an offset that isn't a number. */\r\nfunction assertTimezone(tz: TimeZoneSetting): void {\r\n if (typeof tz === 'string' && tz !== EXCHANGE_TIMEZONE && !isValidTimeZone(tz)) {\r\n throw new RangeError(`Unknown time zone: ${tz}`);\r\n }\r\n if (typeof tz === 'number' && !Number.isFinite(tz)) throw new RangeError(`Invalid UTC offset: ${tz}`);\r\n}\r\n\r\n/** Decimals of order and alert prices when neither the market nor the symbol sets them. */\r\nconst DEFAULT_ORDER_PRECISION = 2;\r\n/** Room above and below the left scale's overlays, as a share of their range. */\r\nconst LEFT_SCALE_PADDING = 0.08;\r\n/** Changes to one indicator closer together than this are one undo step (a colour dragged, a period typed). */\r\nconst INDICATOR_EDIT_MERGE_MS = 800;\r\n/** Fewest bars ahead of the view that trigger the next history page. */\r\nconst HISTORY_AHEAD_MIN_BARS = 20;\r\n\r\n/** Narrowest box (px) the zoom-area tool zooms into. */\r\nconst MIN_ZOOM_BOX_PX = 8;\r\n\r\nexport class Chart {\r\n static version = typeof __TC_VERSION__ !== 'undefined' ? __TC_VERSION__ : '0.0.0-dev';\r\n\r\n private engine: RenderEngine;\r\n private viewport: Viewport;\r\n private dataManager: DataManager;\r\n private themeManager: ThemeManager;\r\n private layoutManager: LayoutManager;\r\n private pluginManager: PluginManager;\r\n private indicatorEngine: IndicatorEngine;\r\n private drawingManager: DrawingManager;\r\n private drawingRenderer: DrawingRenderer;\r\n private tradingManager: TradingManager;\r\n private tradingRenderer: TradingRenderer;\r\n private eventBus: EventBus;\r\n private streamManager: StreamManager | null = null;\r\n private executionAdapter: ExecutionAdapter | null = null;\r\n private execTeardown: (() => void) | null = null;\r\n private autoScrollOnNewBar = true;\r\n private displayDataCache: DataSeries | null = null;\r\n /** Earliest bar of an `indicatorUpdate` waiting to be emitted; null = none pending. */\r\n private indicatorUpdateFrom: number | null = null;\r\n private resolvedLayoutCache: import('@tradecanvas/commons').ResolvedLayout | null = null;\r\n private panelInfoCache: import('@tradecanvas/core').PanelRenderInfo[] | null = null;\r\n private renderScheduled = false;\r\n /** Current price-axis width; grows to fit long (e.g. sub-cent) labels. */\r\n private priceAxisWidth = PRICE_AXIS_WIDTH;\r\n private measureCtx: CanvasRenderingContext2D | null | undefined;\r\n private containerSizeCache: { width: number; height: number } | null = null;\r\n private containerSizeCacheTime = 0;\r\n // Last-emitted viewport values, used to fire visibleRangeChange /\r\n // priceRangeChange / zoomChange only when something actually moved.\r\n private lastEmittedRange: { from: number; to: number } | null = null;\r\n private lastEmittedPriceRange: { min: number; max: number } | null = null;\r\n private lastEmittedBarWidth: number | null = null;\r\n private chartLegend: ChartLegend;\r\n private watermark: Watermark;\r\n private barCountdown: BarCountdown;\r\n /**\r\n * Indicators of the loaded layout this chart could not add (an unknown or\r\n * not yet registered id, a whitelist): saved back as they were, so saving\r\n * the layout again doesn't drop them.\r\n */\r\n private unrestoredIndicators: import('@tradecanvas/core').SnapshotIndicator[] = [];\r\n /** Above 0 while indicators change as a whole (a layout, an undo): no undo steps are recorded. */\r\n private indicatorHistoryDepth = 0;\r\n /** Alerts of deleted drawings, put back if an undo brings the drawing back. */\r\n private removedDrawingAlerts = new Map<string, import('@tradecanvas/core').PriceAlert[]>();\r\n /** The zoom-area tool is waiting for its box. */\r\n private zoomAreaMode = false;\r\n /** The \"+\" by the price axis. */\r\n private priceAxisAddButton = new PriceAxisAddButton();\r\n /** Display timezone, minutes east of UTC; null = the browser's. */\r\n /** The time zone shown: the setting, with 'exchange' resolved to the symbol's zone. */\r\n private displayTz: TimeZoneSetting = null;\r\n /** What `setTimezone` was given; 'exchange' follows the symbol. */\r\n private timezoneSetting: TimeZoneSetting = null;\r\n /** What the feed (or host) said about the symbol on the chart. */\r\n private symbolInfo: SymbolInfo | null = null;\r\n /** Bumped per symbol asked about: an answer about a symbol the chart left is dropped. */\r\n private symbolInfoSeq = 0;\r\n private sessionBreaks: SessionBreaks;\r\n private sessionShading: SessionShading;\r\n /** The session hours the host set (or the default); a symbol's own hours show over them. */\r\n private hostSessionHours: SessionHoursConfig = { ...DEFAULT_SESSION_HOURS };\r\n private compareRenderer: CompareRenderer;\r\n private countdownInterval: ReturnType<typeof setInterval> | null = null;\r\n private volumeRenderer: VolumeRenderer;\r\n private volumeProfile: VolumeProfileRenderer;\r\n private marketProfile: MarketProfileRenderer;\r\n private depthHeatmap: DepthHeatmapRenderer;\r\n private periodLevels: PeriodLevelsRenderer;\r\n private pivotMarkers: PivotMarkersRenderer;\r\n private alertManager: AlertManager;\r\n private signalMarkerManager: SignalMarkerManager;\r\n private tradeZoneManager: TradeZoneManager;\r\n private measureOverlay: MeasureOverlay;\r\n private selectionBoxOverlay = new SelectionBoxOverlay();\r\n private replayManager: ReplayManager;\r\n private replayBarUnsub: (() => void) | null = null;\r\n /** True while `replaySeek` runs: its bar is a jump, not a replay step. */\r\n private replaySeeking = false;\r\n /**\r\n * While a replay runs, the live series keeps moving here — stream and\r\n * host updates land in `live` instead of the replayed slice — and comes\r\n * back on `replayStop()`. `price` is the latest live tick.\r\n */\r\n private replaySession: {\r\n live: DataManager;\r\n price: { price: number; previousClose?: number } | null;\r\n /** The latest replayed time a paper account has been given (it only goes forward). */\r\n fedTime: number | null;\r\n } | null = null;\r\n /** During a replay in finer steps: the steps, and the series they build. */\r\n private replayStepsSeries: DataSeries | null = null;\r\n private replayCoarse: DataSeries | null = null;\r\n private undoRedoManager: UndoRedoManager;\r\n private autoSaveScheduler = new AutoSaveScheduler((key) => this.saveState(key));\r\n private animator: Animator;\r\n private crosshairTooltip: CrosshairTooltip;\r\n private pinnedTooltip: PinnedTooltip;\r\n private interactionManager: InteractionManager;\r\n private crosshairHandler: CrosshairHandler;\r\n private chartRenderer: ChartRendererInterface;\r\n /** Settings of the chart types that build their own bars. */\r\n private chartTypeOptions: ChartTypeOptions = {};\r\n private mainSeriesVisible = true;\r\n /** The visible high and low, the bid and the ask. */\r\n private priceLines = new PriceLines();\r\n /** Other symbols' bars, for the indicators that compare with them. */\r\n private symbolSeries = new SymbolSeriesStore();\r\n /** Bars outside the symbol's regular hours shown. */\r\n private extendedHours = true;\r\n /** A replay being restarted (or the chart going): its stop is not reported. */\r\n private replayRestarting = false;\r\n /** While they are hidden: the whole series, theirs included (the data manager has the rest). */\r\n private fullSeries: OHLCBar[] | null = null;\r\n /** Symbols asked for (`symbolSeriesRequest`) and not given yet. */\r\n private requestedSymbols = new Set<string>();\r\n private gridRenderer: GridRenderer;\r\n private priceAxis: PriceAxis;\r\n /** The left price scale: overlays put on it, else a mirror of the price scale. */\r\n private leftPriceAxis = new PriceAxis();\r\n private leftScaleRequested = false;\r\n private leftAxisWidth = PRICE_AXIS_WIDTH;\r\n /** The left scale's viewport this frame; null while it is hidden. */\r\n private leftViewport: ViewportState | null = null;\r\n private timeAxis: TimeAxis;\r\n private options: ChartOptions & { chartType: ChartType };\r\n private features: Required<FeaturesConfig>;\r\n private marketConfig: MarketConfig | null = null;\r\n private container: HTMLElement;\r\n private currentPriceLine: import('@tradecanvas/core').CurrentPriceLine;\r\n private numberLocale: string;\r\n /** The market's price precision, when set: otherwise it follows the visible range. */\r\n private marketPricePrecision: number | null = null;\r\n /** The chart's price format (`priceFormat`), or null for decimals. */\r\n private priceFormatter: PriceFormatter | null = null;\r\n /** A fraction format's smallest step (a 32nd), or null. */\r\n private priceUnit: number | null = null;\r\n private paneTitles = true;\r\n /**\r\n * The `autoScale` the chart was constructed with — distinct from\r\n * `this.options.autoScale`, which drag-to-scale/vertical-pan mutate at\r\n * runtime to freeze the Y-axis. `setData()` restores this default so a\r\n * freeze on the old symbol doesn't silently carry over to a new one.\r\n */\r\n private defaultAutoScale: boolean;\r\n private keyboardHandler: KeyboardHandler | null = null;\r\n private a11y: ChartA11y | null = null;\r\n private onWindowKeyDown: ((e: KeyboardEvent) => void) | null = null;\r\n private currentSymbol: string = '';\r\n /** Timeframe and adapter of the connected stream, for paging its history. */\r\n private streamTimeframe: TimeFrame | null = null;\r\n /** The symbol and timeframe last told (symbolChange, timeframeChange). */\r\n private announcedSymbol: string | null = null;\r\n private announcedTimeframe: TimeFrame | null = null;\r\n private streamAdapter: DataAdapter | null = null;\r\n /** Pages older bars in as the view nears the oldest one. */\r\n private history: HistoryPager;\r\n /** Whether the history loader is the connected stream's (and goes with it). */\r\n private historyFromStream = false;\r\n /** Whether the host set a loader of its own: the stream's never replaces it. */\r\n private hostHistoryLoader = false;\r\n /** Symbol and timeframe of the series the last stream snapshot loaded. */\r\n private snapshotKey: string | null = null;\r\n private historyCheckQueued = false;\r\n\r\n\r\n constructor(container: HTMLElement, options: ChartOptions & { plugins?: ChartPlugin[] }) {\r\n // Checked before anything is built: a throw later would leave the DOM and listeners behind.\r\n if (options.timeZone != null) assertTimezone(options.timeZone);\r\n this.container = container;\r\n this.options = { ...options, chartType: options.chartType ?? 'candlestick' };\r\n this.numberLocale = options.numberLocale ?? 'en-US';\r\n this.defaultAutoScale = options.autoScale !== false;\r\n\r\n // Resolve feature flags (all default to true)\r\n const f = options.features ?? {};\r\n this.features = {\r\n drawings: f.drawings ?? true,\r\n drawingTools: f.drawingTools ?? [],\r\n drawingMagnet: f.drawingMagnet ?? true,\r\n drawingUndoRedo: f.drawingUndoRedo ?? true,\r\n trading: f.trading ?? true,\r\n // Off by default: right-click is the browser's, and a stray right-click\r\n // shouldn't be one step from an order. Opt in with features.tradingContextMenu.\r\n tradingContextMenu: f.tradingContextMenu ?? false,\r\n priceAxisAddButton: f.priceAxisAddButton ?? false,\r\n indicators: f.indicators ?? true,\r\n indicatorIds: f.indicatorIds ?? [],\r\n panning: f.panning ?? true,\r\n zooming: f.zooming ?? true,\r\n crosshair: f.crosshair ?? true,\r\n crosshairTooltip: f.crosshairTooltip ?? true,\r\n keyboard: f.keyboard ?? true,\r\n priceAxis: f.priceAxis ?? true,\r\n timeAxis: f.timeAxis ?? true,\r\n grid: f.grid ?? (options.grid?.visible ?? true),\r\n legend: f.legend ?? true,\r\n indicatorValueLabels: f.indicatorValueLabels ?? true,\r\n volume: f.volume ?? true,\r\n watermark: f.watermark ?? true,\r\n saveLoad: f.saveLoad ?? true,\r\n screenshot: f.screenshot ?? true,\r\n alerts: f.alerts ?? true,\r\n replay: f.replay ?? true,\r\n sessionBreaks: f.sessionBreaks ?? true,\r\n barCountdown: f.barCountdown ?? true,\r\n compareSymbols: f.compareSymbols ?? true,\r\n dataExport: f.dataExport ?? true,\r\n logScale: f.logScale ?? true,\r\n timeframes: f.timeframes ?? [],\r\n defaultTimeframeFavorites: f.defaultTimeframeFavorites ?? [],\r\n };\r\n\r\n container.style.position = 'relative';\r\n container.style.overflow = 'hidden';\r\n container.tabIndex = 0; // Allow keyboard events\r\n container.style.outline = 'none';\r\n\r\n // Initialize managers\r\n this.dataManager = new DataManager();\r\n this.history = new HistoryPager({\r\n oldestTime: () => this.dataManager.getData()[0]?.time ?? null,\r\n prepend: (bars) => this.prependBars(bars),\r\n emit: (payload) => this.eventBus.emit('historyLoad', payload),\r\n });\r\n this.themeManager = new ThemeManager(options.theme);\r\n this.layoutManager = new LayoutManager();\r\n this.indicatorEngine = new IndicatorEngine();\r\n this.pluginManager = new PluginManager(this.indicatorEngine, { plugins: options.plugins });\r\n this.eventBus = new EventBus();\r\n\r\n registerBuiltInIndicators(this.indicatorEngine);\r\n // Indicators on other symbols' bars, kept by this chart.\r\n this.indicatorEngine.register(new CompareSymbolIndicator(this.symbolSeries));\r\n this.indicatorEngine.register(new SpreadIndicator(this.symbolSeries));\r\n\r\n // Drawing tools\r\n this.drawingManager = new DrawingManager();\r\n registerBuiltInDrawingTools(this.drawingManager);\r\n this.drawingRenderer = new DrawingRenderer(this.drawingManager);\r\n this.drawingManager.setRequestRender(() => {\r\n // Sync entire render context so drawings render with same viewport as candles\r\n this.syncRenderContext();\r\n this.engine.requestRender(LayerType.Overlay);\r\n this.scheduleAutoSave();\r\n });\r\n this.drawingManager.setEventCallback((event, data) => {\r\n this.followDrawingAlerts(event, (data as { id?: string } | null)?.id);\r\n this.eventBus.emit(event as ChartEventType, data);\r\n if (event === 'drawingCreate' || event === 'drawingUpdate' || event === 'drawingRemove') this.announceStateChange();\r\n });\r\n // Pasted drawings obey the same switches as drawing tools.\r\n this.drawingManager.setToolFilter((type) =>\r\n this.features.drawings && (this.features.drawingTools.length === 0 || this.features.drawingTools.includes(type)));\r\n // Undo/redo\r\n this.undoRedoManager = new UndoRedoManager();\r\n let history = '';\r\n this.undoRedoManager.setOnChange(() => {\r\n const { canUndo, canRedo } = this.undoRedoManager.getState();\r\n const now = `${canUndo}|${canRedo}`;\r\n if (now === history) return;\r\n history = now;\r\n this.eventBus.emit('historyChange', { canUndo, canRedo });\r\n });\r\n this.drawingManager.setUndoRedoManager(this.undoRedoManager);\r\n this.drawingManager.setSelectionListener((ids) => this.eventBus.emit('drawingSelect', { ids, primary: ids[0] ?? null }));\r\n this.drawingManager.setForeignHistory((action, direction) => {\r\n if (action.type === 'custom') action.custom?.[direction]();\r\n else this.applyIndicatorStep(action, direction);\r\n });\r\n this.drawingManager.setDataGetter(() => this.dataManager.getData());\r\n this.drawingManager.setDisplayDataGetter(() => this.getDisplayData());\r\n // Magnet mode\r\n if (options.crosshair?.mode === 'magnet' && this.features.drawingMagnet) {\r\n this.drawingManager.setMagnetMode('magnet');\r\n }\r\n\r\n // Trading\r\n this.tradingManager = new TradingManager({\r\n enabled: this.features.trading,\r\n contextMenu: { enabled: this.features.tradingContextMenu },\r\n });\r\n this.tradingRenderer = new TradingRenderer(this.tradingManager);\r\n this.tradingManager.setContainer(container);\r\n this.tradingManager.setRequestRender(() => this.engine.requestRender(LayerType.Overlay));\r\n this.tradingManager.setEventCallback((event, data) => {\r\n this.eventBus.emit(event as ChartEventType, data);\r\n });\r\n\r\n // Rendering\r\n this.engine = new RenderEngine(container);\r\n const size = this.engine.dprManager.getContainerSize();\r\n this.viewport = new Viewport(\r\n size.width,\r\n size.height,\r\n options.minBarSpacing ?? 2,\r\n options.maxBarSpacing ?? 30,\r\n options.rightMargin ?? 5,\r\n );\r\n this.viewport.setPanLimits({\r\n freePan: options.freePan ?? true,\r\n minVisibleBars: options.panLimits?.minVisibleBars ?? 3,\r\n });\r\n this.layoutManager.resize(size.width, size.height);\r\n\r\n // Sync viewport + layout when the container resizes. Without this hook\r\n // the chart's viewport keeps its construction-time chartRect forever\r\n // (which is wrong if the container started at 0 or got resized since),\r\n // and `scrollToEnd` / clampOffset compute against stale dimensions —\r\n // chart sticks at the wrong offset and the user can't scroll to the\r\n // latest bar.\r\n this.engine.onContainerResize = (newSize) => {\r\n if (newSize.width <= 0 || newSize.height <= 0) return;\r\n const wasAtEnd = this.viewport.isAtEnd();\r\n this.viewport.resize(newSize.width, newSize.height);\r\n this.layoutManager.resize(newSize.width, newSize.height);\r\n // wasAtEnd carries the user's intent: if they were tracking the live\r\n // edge before the resize, keep them there afterwards. Otherwise leave\r\n // their scroll position alone.\r\n this.updateViewportAndRender(wasAtEnd);\r\n };\r\n\r\n // Chart renderer\r\n this.chartRenderer = this.createChartRenderer(this.options.chartType);\r\n this.gridRenderer = new GridRenderer();\r\n if (options.grid?.visible === false) this.gridRenderer.setVisible(false);\r\n this.priceAxis = new PriceAxis();\r\n this.priceAxis.setLocale(this.numberLocale);\r\n this.leftPriceAxis.setLocale(this.numberLocale);\r\n this.timeAxis = new TimeAxis();\r\n\r\n // Crosshair\r\n this.crosshairHandler = new CrosshairHandler();\r\n this.crosshairHandler.setLocale(this.numberLocale);\r\n if (options.crosshair?.mode) {\r\n this.crosshairHandler.setMode(options.crosshair.mode);\r\n }\r\n // Crosshair callback — fired via microtask AFTER render, only when the bar\r\n // changes: no extra render requests, and the tooltip's one measurement\r\n // happens once per bar, not per mouse move.\r\n this.crosshairHandler.setCallback((barIndex, point) => {\r\n if (barIndex !== null && point) {\r\n const data = this.dataManager.getData();\r\n const bar = barIndex < data.length ? data[barIndex] : undefined;\r\n\r\n // Update legend (canvas-rendered, will show on next UI paint)\r\n this.chartLegend.setHoverBar(bar ?? null);\r\n\r\n // Update tooltip (DOM, lightweight update only when bar changes).\r\n // Over an indicator pane there is no price crosshair to sit beside.\r\n const vs = this.viewport.getState();\r\n const inPlot = point.y >= vs.chartRect.y && point.y <= vs.chartRect.y + vs.chartRect.height;\r\n if (bar && inPlot && this.features.crosshairTooltip) {\r\n this.crosshairTooltip.show(point, bar, this.themeManager.getTheme(), this.cachedContainerSize(), {\r\n prevClose: barIndex > 0 ? data[barIndex - 1]?.close : undefined,\r\n priceRange: vs.priceRange,\r\n plot: vs.chartRect,\r\n barStepMs: barTimeStepMs(data),\r\n });\r\n } else {\r\n this.crosshairTooltip.hide();\r\n }\r\n\r\n // Refresh the pinned tooltip's delta strip against the hovered bar.\r\n // Cheap when there's nothing pinned (early returns inside reposition).\r\n if (this.pinnedTooltip.isPinned()) {\r\n this.pinnedTooltip.reposition(\r\n this.viewport.getState(),\r\n bar ?? null,\r\n barIndex,\r\n this.themeManager.getTheme(),\r\n );\r\n }\r\n\r\n // Emit for external consumers\r\n this.eventBus.emit('crosshairMove', { point, bar, barIndex });\r\n } else {\r\n this.crosshairTooltip.hide();\r\n this.chartLegend.setHoverBar(null);\r\n this.eventBus.emit('crosshairLeave', {});\r\n }\r\n });\r\n\r\n // Chart legend (OHLCV overlay)\r\n this.chartLegend = new ChartLegend();\r\n this.chartLegend.setChartType(this.options.chartType);\r\n this.chartLegend.setLocale(this.numberLocale);\r\n\r\n // Watermark + Volume\r\n this.watermark = new Watermark();\r\n if (options.watermark) this.watermark.setConfig(options.watermark);\r\n this.volumeRenderer = new VolumeRenderer();\r\n this.volumeProfile = new VolumeProfileRenderer();\r\n this.marketProfile = new MarketProfileRenderer();\r\n this.depthHeatmap = new DepthHeatmapRenderer();\r\n this.periodLevels = new PeriodLevelsRenderer();\r\n this.pivotMarkers = new PivotMarkersRenderer();\r\n\r\n // Bar countdown timer\r\n this.barCountdown = new BarCountdown();\r\n this.barCountdown.setVisible(this.features.barCountdown);\r\n this.sessionBreaks = new SessionBreaks();\r\n this.sessionBreaks.setLocale(this.numberLocale);\r\n this.sessionShading = new SessionShading();\r\n this.compareRenderer = new CompareRenderer();\r\n\r\n // Apply session break config from options\r\n if (options.sessionBreaks) {\r\n this.sessionBreaks.setConfig({\r\n visible: options.sessionBreaks.visible ?? true,\r\n color: options.sessionBreaks.color,\r\n lineStyle: options.sessionBreaks.lineStyle,\r\n lineWidth: options.sessionBreaks.lineWidth,\r\n });\r\n } else if (options.features?.sessionBreaks !== false) {\r\n this.sessionBreaks.setVisible(true);\r\n }\r\n\r\n // Apply log scale from options\r\n if (options.logScale && this.features.logScale) {\r\n this.viewport.setLogScale(true);\r\n }\r\n\r\n // Animation\r\n this.animator = new Animator();\r\n\r\n // Crosshair tooltip (DOM)\r\n this.crosshairTooltip = new CrosshairTooltip();\r\n this.crosshairTooltip.create(container);\r\n this.crosshairTooltip.setLocale(this.numberLocale);\r\n this.pinnedTooltip = new PinnedTooltip();\r\n this.pinnedTooltip.create(container);\r\n if (options.priceFormat) this.applyPriceFormat(options.priceFormat);\r\n if (options.chartTypeOptions) this.chartTypeOptions = readChartTypeOptions(options.chartTypeOptions);\r\n if (options.highLowLines) this.priceLines.setHighLow(true);\r\n if (options.shapes) this.themeManager.setShape(readShapes(options.shapes));\r\n // Their tags in the chart's precision, locale and format.\r\n this.priceLines.setPriceText((p) => this.formatPrice(p));\r\n if (options.extendedHours === false) this.extendedHours = false;\r\n if (options.timeFormatter) this.applyTimeFormatter(options.timeFormatter);\r\n\r\n // Keyboard navigation\r\n this.keyboardHandler = new KeyboardHandler({\r\n scrollBars: (count) => {\r\n const barUnit = this.viewport.getState().barWidth + this.viewport.getState().barSpacing;\r\n this.viewport.scrollBy(count * barUnit);\r\n this.updateViewportAndRender();\r\n },\r\n zoom: (delta) => {\r\n const chartWidth = this.viewport.getState().chartRect.width;\r\n this.viewport.zoom(delta, chartWidth / 2);\r\n this.updateViewportAndRender();\r\n },\r\n goToStart: () => {\r\n this.viewport.scrollBy(-Infinity);\r\n this.updateViewportAndRender();\r\n },\r\n goToEnd: () => {\r\n this.viewport.scrollToEnd();\r\n this.updateViewportAndRender();\r\n },\r\n fitContent: () => this.fitContent(),\r\n });\r\n this.keyboardHandler.setEnabled(this.features.keyboard);\r\n\r\n // Screen readers: a summary, the view after a key moves it, the bars one by one.\r\n if (options.a11y !== false) {\r\n this.a11y = new ChartA11y(container, {\r\n symbol: () => this.currentSymbol || this.symbolInfo?.symbol || '',\r\n timeframe: () => this.streamTimeframe ?? '',\r\n chartType: () => this.options.chartType ?? 'candlestick',\r\n bars: () => this.getDisplayData(),\r\n visibleRange: () => this.viewport.getState().visibleRange,\r\n formatPrice: (price) => this.formatPrice(price),\r\n formatTime: (time) => zonedDateFormatter(this.numberLocale || 'en-US', { dateStyle: 'medium', timeStyle: 'short' }, this.displayTz)(\r\n time > SECONDS_TIME_LIMIT ? time : time * 1000,\r\n ),\r\n showBar: (time) => this.setCrosshairTime(time),\r\n }, options.a11y?.labels);\r\n for (const event of ['dataUpdate', 'chartTypeChange', 'symbolChange', 'timeframeChange'] as const) {\r\n this.eventBus.on(event, () => this.a11y?.refresh(event !== 'dataUpdate'));\r\n }\r\n }\r\n\r\n // Attach keyboard listener to window. Only consume the event when the\r\n // chart actually handles the key AND the container is the focused element\r\n // (or an ancestor) — this prevents the chart from swallowing keystrokes\r\n // intended for inputs elsewhere on the page.\r\n this.onWindowKeyDown = (e: KeyboardEvent) => {\r\n if (!this.keyboardHandler) return;\r\n const active = document.activeElement as HTMLElement | null;\r\n if (active && active !== this.container && !this.container.contains(active)) {\r\n return;\r\n }\r\n // Ignore when typing in a form field that happens to live inside the chart.\r\n if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.isContentEditable)) {\r\n return;\r\n }\r\n // A control inside the chart (a button over it) takes its own keys: Space presses it.\r\n if (active && active !== this.container && active.matches('button, a[href], select, [role=\"button\"], [role=\"menuitem\"]')) return;\r\n // Comma and period read the bars one at a time.\r\n if (this.a11y && this.features.keyboard && (e.key === ',' || e.key === '.') && !e.ctrlKey && !e.metaKey && !e.altKey) {\r\n e.preventDefault();\r\n this.a11y.readBar(e.key === '.' ? 1 : -1);\r\n return;\r\n }\r\n if (this.keyboardHandler.handleKey(e)) {\r\n e.preventDefault();\r\n this.a11y?.viewMoved();\r\n }\r\n };\r\n window.addEventListener('keydown', this.onWindowKeyDown);\r\n\r\n // Current price line (standalone, works without StreamManager)\r\n this.currentPriceLine = new CurrentPriceLine();\r\n this.currentPriceLine.setLocale(this.numberLocale);\r\n this.priceAxis.setReservedPriceProvider(() =>\r\n this.currentPriceLine.isVisible() ? this.currentPriceLine.getPrice() : null);\r\n\r\n // Alerts\r\n this.alertManager = new AlertManager();\r\n // Alerts on drawings check the drawing's lines where they are at the latest bar.\r\n this.alertManager.setDrawingLevels((id) => this.drawingLevelsNow(id));\r\n this.alertManager.setRequestRender(() => this.engine.requestRender(LayerType.Overlay));\r\n this.alertManager.on('triggered', (alert) => {\r\n this.eventBus.emit('alertTriggered', alertPayload(alert));\r\n // Back-compat: legacy listeners keyed off dataUpdate.\r\n this.eventBus.emit('dataUpdate', { alert: 'triggered', alertId: alert.id, price: alert.price, message: alert.message });\r\n });\r\n this.alertManager.on('added', (alert) => {\r\n this.eventBus.emit('alertAdd', alertPayload(alert));\r\n });\r\n this.alertManager.on('removed', (id) => {\r\n this.eventBus.emit('alertRemove', { id });\r\n });\r\n this.alertManager.on('expired', (alert) => {\r\n this.eventBus.emit('alertExpired', alertPayload(alert));\r\n });\r\n this.alertManager.on('updated', (alert) => {\r\n this.eventBus.emit('alertUpdate', alertPayload(alert));\r\n });\r\n\r\n // Signal markers\r\n this.signalMarkerManager = new SignalMarkerManager();\r\n this.signalMarkerManager.setRequestRender(() => this.engine.requestRender(LayerType.Overlay));\r\n this.signalMarkerManager.setDataGetter(() => this.dataManager.getData());\r\n this.signalMarkerManager.on('added', (m) => {\r\n this.eventBus.emit('signalMarkerAdd' as ChartEventType, { id: m.id, source: m.source, direction: m.direction });\r\n });\r\n this.signalMarkerManager.on('removed', (id) => {\r\n this.eventBus.emit('signalMarkerRemove' as ChartEventType, { id });\r\n });\r\n\r\n // Trade zones\r\n this.tradeZoneManager = new TradeZoneManager();\r\n this.tradeZoneManager.setRequestRender(() => this.engine.requestRender(LayerType.Overlay));\r\n this.tradeZoneManager.setDataGetter(() => this.dataManager.getData());\r\n this.tradeZoneManager.on('added', (z) => {\r\n this.eventBus.emit('tradeZoneAdd' as ChartEventType, { id: z.id, direction: z.direction });\r\n });\r\n this.tradeZoneManager.on('removed', (id) => {\r\n this.eventBus.emit('tradeZoneRemove' as ChartEventType, { id });\r\n });\r\n\r\n // Replay\r\n this.replayManager = new ReplayManager();\r\n this.replayManager.on('stateChange', (state) => {\r\n if (!this.replayRestarting) this.eventBus.emit('replayState', { state });\r\n });\r\n\r\n // Measure overlay (shift-drag ruler)\r\n this.measureOverlay = new MeasureOverlay();\r\n\r\n // Interaction\r\n this.interactionManager = new InteractionManager(container);\r\n if (this.features.panning) {\r\n // Decides per gesture when a drag also moves the price scale.\r\n const verticalGate = new VerticalPanGate();\r\n this.interactionManager.setPanHandler(\r\n new PanHandler(\r\n (deltaX, deltaY) => {\r\n if (deltaX) this.viewport.scrollBy(deltaX);\r\n\r\n // Grab-and-drag the price scale vertically. Auto-scale would just\r\n // recompute the range on the next frame, so engaging the drag\r\n // turns it off (double-click the price axis to restore), mirroring\r\n // the price-axis drag-scale gesture.\r\n if (verticalGate.step(deltaX, deltaY, this.options.autoScale !== false) && deltaY) {\r\n this.options.autoScale = false;\r\n this.viewport.panPriceRange(deltaY);\r\n }\r\n\r\n this.updateViewportAndRender();\r\n },\r\n () => verticalGate.reset(),\r\n ),\r\n );\r\n }\r\n // The time axis sits below the panes; the left scale's strip carries\r\n // overlays' labels only, or mirrors the price scale and scales it.\r\n this.interactionManager.setAxisStrips(() => ({\r\n plot: this.viewport.getState().chartRect,\r\n timeAxisTop: this.timeAxisTop(),\r\n left: {\r\n width: this.isLeftPriceScaleShown() ? this.leftAxisWidth : 0,\r\n scales: !this.indicatorEngine.hasLeftScaleOverlays(),\r\n },\r\n }));\r\n if (this.features.zooming) {\r\n this.interactionManager.setZoomHandler(\r\n new ZoomHandler((delta, centerX) => {\r\n // The pointer is in chart coordinates; zoom anchors in plot ones.\r\n this.viewport.zoom(delta, centerX - this.viewport.getState().chartRect.x);\r\n this.updateViewportAndRender();\r\n }),\r\n );\r\n\r\n // Axis drag-scaling:\r\n // drag price axis → scalePriceRange(factor), disables autoScale\r\n // drag time axis → zoom around chart center\r\n // dblclick axis → reset (price: re-enable autoScale; time: fitContent)\r\n const axisDrag = new AxisDragHandler(\r\n (factor) => {\r\n this.options.autoScale = false;\r\n this.viewport.scalePriceRange(factor);\r\n this.syncRenderContext();\r\n this.engine.requestRender();\r\n this.emitViewportEvents();\r\n },\r\n (factor) => {\r\n const cw = this.viewport.getState().chartRect.width;\r\n // factor > 1 means zoom OUT (compress time). Map to negative delta\r\n // because Viewport.zoom uses (1 + delta) on barWidth — positive\r\n // delta zooms in.\r\n this.viewport.zoom(1 / factor - 1, cw / 2);\r\n this.updateViewportAndRender();\r\n },\r\n );\r\n this.interactionManager.setAxisDragHandler(\r\n axisDrag,\r\n () => this.viewport.getState(),\r\n (axis) => {\r\n if (axis === 'price') {\r\n this.options.autoScale = true;\r\n this.updateViewportAndRender();\r\n } else {\r\n this.fitContent();\r\n }\r\n },\r\n );\r\n }\r\n if (this.features.crosshair) {\r\n this.interactionManager.setCrosshairHandler(this.crosshairHandler);\r\n }\r\n if (this.features.drawings) {\r\n this.interactionManager.setDrawingManager(\r\n this.drawingManager,\r\n // Inject current data so drawing anchors are stored/resolved as real\r\n // timestamps (survives timeframe/symbol switches).\r\n () => ({ ...this.viewport.getState(), data: this.getDisplayData() }),\r\n );\r\n this.interactionManager.setDrawingDoubleClick((id) => this.eventBus.emit('drawingDoubleClick', { id }));\r\n this.interactionManager.setDrawingContextMenu((id, pos) => {\r\n if (this.drawingManager.select(id)) this.eventBus.emit('drawingContextMenu', { id, x: pos.x, y: pos.y });\r\n });\r\n }\r\n // Off any drawing: the host's menu for where it was, when one listens.\r\n this.interactionManager.setChartContextMenu((area, pos) => {\r\n if (!this.eventBus.hasListeners('chartContextMenu')) return false;\r\n this.eventBus.emit('chartContextMenu', this.contextAt(area, pos));\r\n return true;\r\n });\r\n // The \"+\" rides on the crosshair: without one it is neither drawn nor pressed.\r\n this.priceAxisAddButton.setEnabled(this.features.priceAxisAddButton && this.features.crosshair);\r\n this.interactionManager.setPriceAxisAddButton(this.priceAxisAddButton, (pos) => {\r\n const price = yToPrice(pos.y, this.viewport.getState());\r\n if (Number.isFinite(price)) this.eventBus.emit('priceAxisAdd', { price, x: pos.x, y: pos.y });\r\n });\r\n if (this.features.trading) {\r\n this.interactionManager.setTradingManager(\r\n this.tradingManager,\r\n () => ({ ...this.viewport.getState(), data: this.getDisplayData() }),\r\n );\r\n }\r\n if (this.features.alerts) {\r\n const alertDrag = new AlertDragHandler(this.alertManager, () => this.viewport.getState());\r\n this.interactionManager.setAlertDragHandler(alertDrag);\r\n }\r\n\r\n // Drag pane dividers to resize indicator panels.\r\n const paneResize = new PaneResizeHandler();\r\n paneResize.configure(\r\n () => this.getResolvedLayout(),\r\n (panelId, size) => this.setPanelSize(panelId, size),\r\n );\r\n this.interactionManager.setPaneResizeHandler(paneResize);\r\n // Alt-click pins the OHLC tooltip at the bar under the cursor. Hitting\r\n // it a second time on the same bar unpins.\r\n this.interactionManager.setAltClickHandler((pos) => {\r\n const data = this.getDisplayData();\r\n if (data.length === 0) return;\r\n const vp = this.viewport.getState();\r\n const barUnit = vp.barWidth + vp.barSpacing;\r\n const idx = Math.round((vp.offset + pos.x) / barUnit);\r\n if (idx < 0 || idx >= data.length) return;\r\n if (this.pinnedTooltip.isPinned() && this.pinnedTooltip.getPinnedIndex() === idx) {\r\n this.pinnedTooltip.unpin();\r\n } else {\r\n this.pinnedTooltip.pin(data[idx], idx, this.themeManager.getTheme());\r\n }\r\n this.pinnedTooltip.reposition(\r\n this.viewport.getState(),\r\n this.dataManager.getData()[idx] ?? null,\r\n idx,\r\n this.themeManager.getTheme(),\r\n );\r\n this.engine.requestRender(LayerType.Overlay);\r\n });\r\n\r\n // Signal markers react to the pointer, when someone listens.\r\n this.interactionManager.setSignalMarkerHitTest(\r\n (pos) => (this.eventBus.hasListeners('signalMarkerHover') || this.eventBus.hasListeners('signalMarkerClick')\r\n ? this.signalMarkerManager.markerAt(pos, this.viewport.getState())\r\n : null),\r\n (marker, pos) => this.eventBus.emit('signalMarkerHover', { marker: marker as import('@tradecanvas/commons').SignalMarker | null, x: pos.x, y: pos.y }),\r\n // A hand only where a click does something.\r\n () => this.eventBus.hasListeners('signalMarkerClick'),\r\n );\r\n this.interactionManager.setClickHandler((pos) => {\r\n const data = this.getDisplayData();\r\n const marker = this.eventBus.hasListeners('signalMarkerClick') ? this.signalMarkerManager.markerAt(pos, this.viewport.getState()) : null;\r\n if (marker) {\r\n this.eventBus.emit('signalMarkerClick', { marker, x: pos.x, y: pos.y });\r\n return;\r\n }\r\n this.eventBus.emit('click', { x: pos.x, y: pos.y });\r\n if (data.length === 0) return;\r\n // Canonical bar mapping (accounts for chartRect.x + bar centering) so the\r\n // clicked bar matches the crosshair / drawings.\r\n const idx = xToBarIndex(pos.x, this.viewport.getState());\r\n if (idx < 0 || idx >= data.length) return;\r\n this.eventBus.emit('barClick', { bar: data[idx], barIndex: idx, point: pos });\r\n });\r\n\r\n this.interactionManager.setEscapeHandler(() => {\r\n if (this.zoomAreaMode) {\r\n this.setZoomAreaMode(false);\r\n return;\r\n }\r\n if (this.features.trading && this.tradingManager.isBracketActive()) {\r\n this.tradingManager.cancelBracket();\r\n this.eventBus.emit('dataUpdate', { bracket: 'cancelled' });\r\n return;\r\n }\r\n if (this.pinnedTooltip.isPinned()) {\r\n this.pinnedTooltip.unpin();\r\n this.engine.requestRender(LayerType.Overlay);\r\n }\r\n });\r\n\r\n this.interactionManager.setConfirmHandler(() => {\r\n if (this.features.trading && this.tradingManager.isBracketActive()) {\r\n this.tradingManager.confirmBracket();\r\n return true;\r\n }\r\n return false;\r\n });\r\n\r\n // Shift-drag measure tool — transient overlay. Coords resolved via\r\n // viewport + displayData so the ruler sticks to the data during zoom.\r\n this.interactionManager.setMeasureHandlers({\r\n begin: (pos) => {\r\n this.measureOverlay.begin(pos, this.viewport.getState(), this.getDisplayData());\r\n },\r\n move: (pos) => {\r\n this.measureOverlay.update(pos, this.viewport.getState(), this.getDisplayData());\r\n },\r\n end: () => {\r\n this.measureOverlay.end();\r\n },\r\n });\r\n\r\n // Ctrl/⌘-drag: select every drawing the box covers; Ctrl/⌘-click adds or\r\n // removes one. The box is kept inside the plot (moves are followed\r\n // off-chart), so the selection matches what the box showed.\r\n const inPlot = (pos: { x: number; y: number }) => {\r\n const r = this.viewport.getState().chartRect;\r\n return {\r\n x: Math.max(r.x, Math.min(r.x + r.width, pos.x)),\r\n y: Math.max(r.y, Math.min(r.y + r.height, pos.y)),\r\n };\r\n };\r\n this.interactionManager.setBoxSelectHandlers({\r\n begin: (pos) => this.selectionBoxOverlay.begin(inPlot(pos)),\r\n move: (pos) => this.selectionBoxOverlay.update(inPlot(pos)),\r\n end: () => {\r\n const box = this.selectionBoxOverlay.end();\r\n if (box && this.zoomAreaMode) {\r\n // A click, or a box too narrow to hold a bar or two, keeps the tool waiting.\r\n if (box.x1 - box.x0 < MIN_ZOOM_BOX_PX) return;\r\n this.zoomToBox(box);\r\n this.setZoomAreaMode(false);\r\n return;\r\n }\r\n if (!box || !this.features.drawings) return;\r\n // With the bar series, so anchors resolve as timestamps (as for drawing hit-tests).\r\n const vs = { ...this.viewport.getState(), data: this.getDisplayData() };\r\n if (box.isClick) this.drawingManager.toggleSelectionAt({ x: box.x1, y: box.y1 }, vs);\r\n else this.drawingManager.selectInRect(box, vs);\r\n this.engine.requestRender(LayerType.Overlay);\r\n },\r\n cancel: () => {\r\n this.selectionBoxOverlay.cancel();\r\n this.setZoomAreaMode(false);\r\n },\r\n });\r\n\r\n // A plain hover only moves pointer-tied visuals (crosshair, its axis\r\n // pills, measure ruler, selection box): repaint just the top canvas.\r\n this.interactionManager.setOverlayDirtyCallback((hoverOnly) => {\r\n this.engine.requestRender(hoverOnly ? LayerType.Hover : LayerType.Overlay);\r\n });\r\n this.interactionManager.attach();\r\n\r\n if (options.timeZone !== undefined && options.timeZone !== null) this.setTimezone(options.timeZone);\r\n if (options.leftPriceScale) this.leftScaleRequested = true;\r\n\r\n // Set render context\r\n this.syncRenderContext();\r\n this.engine.start();\r\n }\r\n\r\n // --- Data ---\r\n\r\n setData(data: DataSeries): void {\r\n this.loadSeries(data, false);\r\n }\r\n\r\n /**\r\n * `setData`; `restoring` puts the live series back after a replay, which\r\n * goes on as it was for alerts (their bars and last values stay).\r\n */\r\n private loadSeries(data: DataSeries, restoring: boolean): void {\r\n // A full replace is a new series: any replay of the old one ends, and a\r\n // stream's reconnect no longer merges into it.\r\n this.snapshotKey = null;\r\n this.endReplaySession();\r\n this.history.reset();\r\n this.dataManager.setData(this.regularOnly(data));\r\n this.crosshairHandler.setData(this.dataManager.getData());\r\n this.displayDataCache = null;\r\n this.sessionBreaks.invalidateCache();\r\n // A full data replace means a new series (symbol/timeframe switch, not\r\n // a live tick — those go through appendBar/updateLastBar instead), so\r\n // un-freeze the Y-axis the same way scrollToEnd below resets the X-axis.\r\n // Otherwise a price-axis drag or vertical pan on the old symbol silently\r\n // keeps the new symbol's chart stuck on the old price range.\r\n this.options.autoScale = this.defaultAutoScale;\r\n // New data context (symbol / timeframe) — drop stale alert prev-values so\r\n // the next tick seeds cleanly instead of crossing against the old series.\r\n if (!restoring) this.alertManager.clearLastValues();\r\n this.recalcIndicators(this.dataManager.getData());\r\n // Auto-set current price line from last bar's close\r\n if (data.length > 0) {\r\n this.currentPriceLine.setPrice(data[data.length - 1].close);\r\n }\r\n this.updateViewportAndRender(true);\r\n this.eventBus.emit('dataUpdate', { length: data.length });\r\n }\r\n\r\n /**\r\n * Add older bars in front of the data — e.g. a page of history loaded as\r\n * the user scrolls back. Bars at or after the first loaded bar are skipped.\r\n * The same bars stay on screen. Ignored during a replay. Returns how many\r\n * bars were added.\r\n */\r\n prependBars(bars: OHLCBar[]): number {\r\n if (this.replaySession) return 0;\r\n // Hidden extended hours: the older bars all go to the whole series, the\r\n // regular ones on the chart too; the page counts as loaded either way.\r\n let taken: number | null = null;\r\n if (this.fullSeries) {\r\n const first = this.fullSeries[0]?.time;\r\n const seen = new Set<number>();\r\n const older = bars\r\n .filter((b) => isWholeBar(b) && (first === undefined || b.time < first) && !seen.has(b.time) && seen.add(b.time))\r\n .sort((a, b) => a.time - b.time);\r\n this.fullSeries = [...older, ...this.fullSeries];\r\n const keep = this.sessionKeep(this.fullSeries);\r\n taken = older.length;\r\n bars = keep ? older.filter((b) => keep(b.time)) : older;\r\n }\r\n // The bar at the left edge, by time: Renko bricks and the like are rebuilt\r\n // from the whole series, so counting what was added would not find it.\r\n const shown = this.getDisplayData();\r\n const leftIndex = Math.max(0, Math.min(shown.length - 1, Math.floor(this.viewport.getState().visibleRange.from)));\r\n const leftTime = shown[leftIndex]?.time;\r\n const added = this.dataManager.prependBars(bars);\r\n if (added === 0) return taken ?? 0;\r\n const data = this.dataManager.getData();\r\n this.crosshairHandler.setData(data);\r\n this.displayDataCache = null;\r\n this.sessionBreaks.invalidateCache();\r\n // Both hold bar positions that just moved.\r\n if (this.pinnedTooltip.isPinned()) this.pinnedTooltip.unpin();\r\n this.measureOverlay.end();\r\n this.recalcIndicators(data);\r\n if (leftTime !== undefined) {\r\n const now = Math.round(timestampToBarIndex(leftTime, this.getDisplayData()));\r\n this.viewport.prependBars(now - leftIndex);\r\n }\r\n this.updateViewportAndRender();\r\n return taken ?? added;\r\n }\r\n\r\n /**\r\n * A stream snapshot for the series already on the chart (a reconnect): keep\r\n * the older bars paged in and the view, replace the rest.\r\n */\r\n private mergeSnapshot(bars: OHLCBar[]): void {\r\n const data = this.fullSeries ?? this.dataManager.getData();\r\n const first = bars[0]?.time;\r\n let keep = 0;\r\n while (first !== undefined && keep < data.length && data[keep].time < first) keep++;\r\n // Merge only when the snapshot reaches back over the old bars: after a\r\n // longer outage it starts later, and joining them would hide a gap.\r\n if (keep === 0 || keep === data.length) {\r\n this.setData(bars);\r\n return;\r\n }\r\n const follow = this.viewport.isAtEnd();\r\n this.dataManager.setData(this.regularOnly(data.slice(0, keep).concat(bars)));\r\n const merged = this.dataManager.getData();\r\n this.crosshairHandler.setData(merged);\r\n this.displayDataCache = null;\r\n this.sessionBreaks.invalidateCache();\r\n // The same series goes on (a reconnect): alerts keep their bars and last values.\r\n this.recalcIndicators(merged);\r\n if (merged.length > 0) this.currentPriceLine.setPrice(merged[merged.length - 1].close);\r\n this.updateViewportAndRender(follow);\r\n this.eventBus.emit('dataUpdate', { length: merged.length });\r\n }\r\n\r\n /**\r\n * Load older bars as the user scrolls back: `loader(before, limit)` gets the\r\n * oldest loaded bar's time and returns up to `limit` older bars (an empty\r\n * array once the history starts). A connected stream whose adapter has\r\n * `fetchHistoryBefore` sets this up by itself. Pass null to stop.\r\n */\r\n setHistoryLoader(loader: HistoryLoader | null, options?: { pageSize?: number }): void {\r\n this.historyFromStream = false;\r\n this.hostHistoryLoader = loader !== null;\r\n this.history.setLoader(loader, options?.pageSize);\r\n this.checkHistory();\r\n }\r\n\r\n /** Load one page of older bars now. Resolves to the number of bars added. */\r\n loadMoreHistory(): Promise<number> {\r\n if (this.replaySession) return Promise.resolve(0);\r\n return this.history.loadMore();\r\n }\r\n\r\n /** Whether older bars can still be loaded (a loader is set and the start wasn't reached). */\r\n hasMoreHistory(): boolean {\r\n return this.history.hasLoader() && this.history.hasMore();\r\n }\r\n\r\n isLoadingHistory(): boolean {\r\n return this.history.isLoading();\r\n }\r\n\r\n /**\r\n * Page older bars in when less than a screen of them is left of the view —\r\n * after the current update, so a page that just landed reports itself\r\n * before the next one starts. Not for chart types that reshape the bars\r\n * (Renko, Kagi…): a handful of bricks can stand for a whole page.\r\n */\r\n private checkHistory(): void {\r\n if (this.historyCheckQueued || !this.history.hasLoader()) return;\r\n this.historyCheckQueued = true;\r\n queueMicrotask(() => {\r\n this.historyCheckQueued = false;\r\n if (this.replaySession || !this.history.hasLoader() || isReshapedChartType(this.options.chartType)) return;\r\n const { from, to } = this.viewport.getState().visibleRange;\r\n this.history.maybeLoad(Math.max(0, Math.floor(from)), Math.max(HISTORY_AHEAD_MIN_BARS, to - from));\r\n });\r\n }\r\n\r\n /** Page the connected stream's history through its adapter, if it can. */\r\n private useStreamHistory(feed: DataAdapter, pageSize: number | undefined): void {\r\n if (this.hostHistoryLoader) return;\r\n // The same wrapper the stream uses: pages of a 7m chart are 7m bars.\r\n const adapter = withResampling(feed);\r\n const fetchBefore = adapter.fetchHistoryBefore;\r\n const timeframe = this.streamTimeframe;\r\n // Tick bars come from recent trades: a feed keeps no older ones to page in.\r\n if (!fetchBefore || timeframe === null || tickBarCount(timeframe) !== null) {\r\n if (this.historyFromStream) this.history.setLoader(null);\r\n this.historyFromStream = false;\r\n return;\r\n }\r\n const symbol = this.currentSymbol;\r\n this.history.setLoader((before, limit) => fetchBefore.call(adapter, symbol, timeframe, before, limit), pageSize);\r\n this.historyFromStream = true;\r\n this.checkHistory();\r\n }\r\n\r\n appendBar(bar: OHLCBar): void {\r\n if (this.replaySession) {\r\n this.replaySession.live.appendBar(bar);\r\n return;\r\n }\r\n if (!this.takeBar(bar, 'append')) return;\r\n // Follow the live edge only if the view is already there — browsing\r\n // history shouldn't be yanked back to the end on every new bar.\r\n const follow = this.autoScrollOnNewBar && this.viewport.isAtEnd();\r\n this.dataManager.appendBar(bar);\r\n const data = this.dataManager.getData();\r\n this.crosshairHandler.setData(data);\r\n this.displayDataCache = null;\r\n // Re-finalise the bar that just closed (its last tick may differ from the\r\n // final close) and compute the new one; everything older is untouched.\r\n this.recalcIndicatorsFrom(data, data.length - 2);\r\n this.updateViewportAndRender(follow);\r\n }\r\n\r\n /**\r\n * Append multiple bars at once (e.g., catch-up after reconnect).\r\n * More efficient than calling appendBar() in a loop — recalculates\r\n * indicators only once at the end.\r\n */\r\n appendBars(bars: OHLCBar[]): void {\r\n if (bars.length === 0) return;\r\n if (this.replaySession) {\r\n for (const bar of bars) this.replaySession.live.appendBar(bar);\r\n return;\r\n }\r\n bars = bars.filter((bar) => this.takeBar(bar, 'append'));\r\n if (bars.length === 0) return;\r\n const follow = this.viewport.isAtEnd();\r\n const firstChanged = this.dataManager.getLength() - 1;\r\n for (const bar of bars) {\r\n this.dataManager.appendBar(bar);\r\n }\r\n this.displayDataCache = null;\r\n this.recalcIndicatorsFrom(this.dataManager.getData(), firstChanged);\r\n this.crosshairHandler.setData(this.dataManager.getData());\r\n this.updateViewportAndRender(follow);\r\n }\r\n\r\n updateLastBar(bar: OHLCBar): void {\r\n if (this.replaySession) {\r\n this.replaySession.live.updateLastBar(bar);\r\n return;\r\n }\r\n if (!this.takeBar(bar, 'update')) {\r\n this.currentPriceLine.setPrice(bar.close);\r\n this.scheduleRender();\r\n return;\r\n }\r\n this.dataManager.updateLastBar(bar);\r\n this.currentPriceLine.setPrice(bar.close);\r\n // For non-transform chart types, the displayDataCache still points to the\r\n // same raw array (mutated in place), so no need to invalidate.\r\n // Only invalidate for transform types that produce derived arrays.\r\n if (this.options.chartType !== 'candlestick' && this.options.chartType !== 'line'\r\n && this.options.chartType !== 'area' && this.options.chartType !== 'bar'\r\n && this.options.chartType !== 'hollowCandle') {\r\n this.displayDataCache = null;\r\n }\r\n // Keep indicator lines in sync with the forming bar. Without this, panel\r\n // + overlay indicators freeze until bar close. Only the last bar changed.\r\n const data = this.dataManager.getData();\r\n this.recalcIndicatorsFrom(data, data.length - 1);\r\n this.scheduleRender();\r\n }\r\n\r\n /** Merge a price tick into the current last bar (convenience for live feeds) */\r\n updateLastBarFromTick(tick: { price: number; volume?: number; time: number }): void {\r\n if (this.replaySession) {\r\n this.replaySession.live.updateLastBarFromTick(tick);\r\n if (Number.isFinite(tick.price)) this.replaySession.price = { price: tick.price };\r\n return;\r\n }\r\n // A tick outside regular hours, while they are hidden, moves only the price line.\r\n const keep = this.fullSeries ? this.sessionKeep(this.fullSeries) : null;\r\n if (keep && !keep(tick.time)) {\r\n this.currentPriceLine.setPrice(tick.price);\r\n this.scheduleRender();\r\n return;\r\n }\r\n this.dataManager.updateLastBarFromTick(tick);\r\n this.currentPriceLine.setPrice(tick.price);\r\n // While extended hours are hidden, the whole series keeps the same bar.\r\n const full = this.fullSeries;\r\n const merged = this.dataManager.getData()[this.dataManager.getLength() - 1];\r\n if (full && merged && full.length > 0 && full[full.length - 1].time === merged.time) full[full.length - 1] = merged;\r\n if (this.options.chartType !== 'candlestick' && this.options.chartType !== 'line'\r\n && this.options.chartType !== 'area' && this.options.chartType !== 'bar'\r\n && this.options.chartType !== 'hollowCandle') {\r\n this.displayDataCache = null;\r\n }\r\n const data = this.dataManager.getData();\r\n this.recalcIndicatorsFrom(data, data.length - 1);\r\n this.scheduleRender();\r\n }\r\n\r\n // --- Chart type ---\r\n\r\n setChartType(type: ChartType | (string & {})): void {\r\n const previous = this.options.chartType ?? 'candlestick';\r\n this.options.chartType = type as ChartType;\r\n this.chartRenderer = this.createChartRenderer(type);\r\n this.chartLegend.setChartType(type as ChartType);\r\n this.displayDataCache = null;\r\n this.updateViewportAndRender(true);\r\n this.markStateChanged();\r\n if (type !== previous) this.eventBus.emit('chartTypeChange', { type, previous });\r\n }\r\n\r\n /**\r\n * Settings of Renko, Line Break, Kagi, Point & Figure and range bars, by\r\n * type: each type given replaces that type's settings (`{}` puts it back\r\n * to the defaults). Values it can't use are left out.\r\n */\r\n setChartTypeOptions(options: ChartTypeOptions): void {\r\n this.chartTypeOptions = { ...this.chartTypeOptions, ...readChartTypeOptions(options) };\r\n this.displayDataCache = null;\r\n this.updateViewportAndRender(true);\r\n this.markStateChanged();\r\n }\r\n\r\n getChartTypeOptions(): ChartTypeOptions {\r\n return readChartTypeOptions(this.chartTypeOptions);\r\n }\r\n\r\n /**\r\n * Show or hide the bars outside the symbol's regular hours\r\n * (`SymbolInfo.sessions`, in its `timezone`): pre- and post-market. Hidden,\r\n * they are kept aside and come back when shown again. Bars a day or longer,\r\n * and symbols without hours, are left as they are.\r\n */\r\n setExtendedHours(show: boolean): void {\r\n if (show === this.extendedHours) return;\r\n this.extendedHours = show;\r\n this.reapplyExtendedHours();\r\n this.markStateChanged();\r\n }\r\n\r\n isExtendedHoursVisible(): boolean {\r\n return this.extendedHours;\r\n }\r\n\r\n /** Which bar times to show, for bars like `data`'s; null: all of them. */\r\n private sessionKeep(data: readonly { time: number }[]): ((time: number) => boolean) | null {\r\n return this.extendedHours ? null : regularHoursFilter(this.symbolInfo, data);\r\n }\r\n\r\n /** The bars of `all` the chart shows (no state kept). */\r\n private shownBars(all: OHLCBar[]): OHLCBar[] {\r\n const keep = this.sessionKeep(all);\r\n return keep ? all.filter((b) => keep(b.time)) : all.slice();\r\n }\r\n\r\n /** The bars of `all` the chart shows; while some are hidden, `all` is kept as the whole series. */\r\n private regularOnly(all: OHLCBar[]): OHLCBar[] {\r\n const keep = this.sessionKeep(all);\r\n this.fullSeries = keep ? all.slice() : null;\r\n return keep ? all.filter((b) => keep(b.time)) : all;\r\n }\r\n\r\n /** A new or updated bar into the whole series (while one is kept): whether the chart shows it. */\r\n /** A stream bar onto the bars shown: the last one again or a new one after it; false for an older one. */\r\n private placeStreamBar(bar: OHLCBar): boolean {\r\n const how = barPlacement(this.dataManager.getData(), bar);\r\n if (how === 'stale') return false;\r\n if (how === 'append') this.dataManager.appendBar(bar);\r\n else this.dataManager.updateLastBar(bar);\r\n return true;\r\n }\r\n\r\n private takeBar(bar: OHLCBar, how: 'append' | 'update'): boolean {\r\n // Hidden hours with no whole series yet (too few bars to tell the interval): start one now if it can.\r\n if (!this.fullSeries && !this.extendedHours && !this.replaySession) {\r\n const data = this.dataManager.getData();\r\n // The interval is told by the last bars: no need to copy the rest.\r\n const tail = data.slice(-12);\r\n const all = how === 'append' || tail.length === 0 ? [...tail, bar] : [...tail.slice(0, -1), bar];\r\n if (this.sessionKeep(all)) {\r\n this.fullSeries = data.slice();\r\n const keep = this.sessionKeep(this.fullSeries.length > 1 ? this.fullSeries : all);\r\n if (keep && data.some((b) => !keep(b.time))) {\r\n this.dataManager.setData(data.filter((b) => keep(b.time)));\r\n this.displayDataCache = null;\r\n }\r\n }\r\n }\r\n const full = this.fullSeries;\r\n if (!full) return true;\r\n if (how === 'append' || full.length === 0) full.push(bar);\r\n else full[full.length - 1] = bar;\r\n const keep = this.sessionKeep(full);\r\n return !keep || keep(bar.time);\r\n }\r\n\r\n /** The bars shown again after the extended hours or the symbol's hours changed. */\r\n private reapplyExtendedHours(): void {\r\n if (this.replaySession) return; // the replay's end brings the series back through the filter\r\n const all = this.fullSeries ?? this.dataManager.getData().slice();\r\n if (!this.fullSeries && !this.sessionKeep(all)) return; // nothing hidden before or now\r\n this.dataManager.setData(this.regularOnly(all));\r\n const data = this.dataManager.getData();\r\n this.crosshairHandler.setData(data);\r\n this.displayDataCache = null;\r\n this.sessionBreaks.invalidateCache();\r\n this.recalcIndicators(data);\r\n this.updateViewportAndRender(true);\r\n this.eventBus.emit('dataUpdate', { length: data.length });\r\n }\r\n\r\n /** The price lines for this frame: during a replay without the live bid and ask. */\r\n private quoteLines(): PriceLines {\r\n this.priceLines.setQuoteShown(this.replaySession === null);\r\n return this.priceLines;\r\n }\r\n\r\n /**\r\n * The shapes the chart draws: `tagRadius` rounds its price tags, axis\r\n * pills and order badges (0: square, 999: pills). Kept through theme\r\n * changes.\r\n */\r\n setShapes(shapes: ShapeConfig): void {\r\n const read = readShapes(shapes);\r\n if (read.tagRadius === undefined) return;\r\n this.themeManager.setShape({ ...this.themeManager.getTheme().shape, ...read });\r\n this.syncRenderContext();\r\n this.engine.requestRender();\r\n }\r\n\r\n getShapes(): ShapeConfig {\r\n return { ...this.themeManager.getTheme().shape };\r\n }\r\n\r\n /** Show or hide the main series (its bars, candles or line); the rest of the chart stays. */\r\n setMainSeriesVisible(visible: boolean): void {\r\n this.mainSeriesVisible = visible;\r\n this.syncRenderContext();\r\n this.engine.requestRender();\r\n }\r\n\r\n isMainSeriesVisible(): boolean {\r\n return this.mainSeriesVisible;\r\n }\r\n\r\n /** Mark the highest high and lowest low on screen with a line and a price tag. */\r\n setHighLowLines(visible: boolean): void {\r\n this.priceLines.setHighLow(visible);\r\n this.engine.requestRender();\r\n }\r\n\r\n isHighLowLinesVisible(): boolean {\r\n return this.priceLines.isHighLowVisible();\r\n }\r\n\r\n /**\r\n * Mark the best bid and ask with lines and price tags (null takes them\r\n * off). A connected feed whose ticks carry `bid` / `ask` keeps them up to date.\r\n */\r\n setBidAsk(quote: BidAsk | null): void {\r\n this.priceLines.setBidAsk(quote);\r\n this.engine.requestRender(LayerType.Overlay);\r\n }\r\n\r\n getBidAsk(): BidAsk | null {\r\n return this.priceLines.getBidAsk();\r\n }\r\n\r\n // --- Indicators ---\r\n\r\n /**\r\n * Add an indicator. Panel indicators get a pane of their own at `position`.\r\n * `options.pane` draws it in another indicator's pane instead, on that\r\n * pane's scale; a price-pane indicator computed from a pane indicator's\r\n * line (`source: 'ind:<instanceId>:<key>'`) goes there by itself.\r\n */\r\n addIndicator(\r\n id: string,\r\n params: Record<string, number | string | boolean> = {},\r\n position: PanelPosition = 'bottom',\r\n options: { pane?: string; scale?: OverlayScale } = {},\r\n ): string | null {\r\n return this.recordIndicators(undefined, () => this.addIndicatorNow(id, params, position, options));\r\n }\r\n\r\n private addIndicatorNow(\r\n id: string,\r\n params: Record<string, number | string | boolean>,\r\n position: PanelPosition,\r\n options: { pane?: string; scale?: OverlayScale; instanceId?: string },\r\n ): string | null {\r\n if (!this.features.indicators) return null;\r\n if (this.features.indicatorIds.length > 0 && !this.features.indicatorIds.includes(id)) return null;\r\n const descriptor = this.indicatorEngine.getAvailableIndicators().find((d) => d.id === id);\r\n const pane = descriptor ? this.paneFor(descriptor, params, options.pane) : null;\r\n const instanceId = this.indicatorEngine.addIndicator(id, params, this.dataManager.getData(), {\r\n ...(pane ? { pane } : {}),\r\n ...(options.scale === 'left' ? { scale: 'left' as const } : {}),\r\n ...(options.instanceId ? { instanceId: options.instanceId } : {}),\r\n });\r\n if (descriptor?.placement === 'panel' && !pane) this.layoutManager.addPanel(instanceId, position);\r\n // The layout (a new pane) or the price scale (a new overlay) may change.\r\n this.updateViewportAndRender();\r\n this.eventBus.emit('indicatorAdd', { instanceId, id });\r\n this.markStateChanged();\r\n this.requestMissingSymbols(instanceId);\r\n return instanceId;\r\n }\r\n\r\n // --- Other symbols' bars ---\r\n\r\n /**\r\n * Another symbol's bars, for the indicators that read it ('compareSymbol',\r\n * 'spread'): they line them up with the chart's bars by time. Give them\r\n * again after a timeframe change; null forgets them.\r\n */\r\n setSymbolSeries(symbol: string, bars: DataSeries | null): void {\r\n symbol = symbol.trim();\r\n this.symbolSeries.set(symbol, bars);\r\n this.requestedSymbols.delete(symbol);\r\n const data = this.dataManager.getData();\r\n let changed = false;\r\n for (const ind of this.indicatorEngine.getActiveIndicators()) {\r\n if (!SYMBOL_INDICATORS.has(ind.id) || String(ind.params.symbol ?? '').trim() !== symbol.trim()) continue;\r\n this.indicatorEngine.updateIndicator(ind.instanceId, {}, data);\r\n changed = true;\r\n }\r\n if (!changed) return;\r\n this.announceIndicatorUpdate(0);\r\n this.updateViewportAndRender();\r\n }\r\n\r\n /** A symbol no indicator reads any more may be asked for again later. */\r\n private forgetUnreadSymbols(): void {\r\n const required = new Set(this.getRequiredSymbols());\r\n for (const symbol of this.requestedSymbols) if (!required.has(symbol)) this.requestedSymbols.delete(symbol);\r\n }\r\n\r\n /** The bars given for `symbol`, or null. */\r\n getSymbolSeries(symbol: string): DataSeries | null {\r\n return this.symbolSeries.get(symbol) ?? null;\r\n }\r\n\r\n /** The symbols the chart's indicators read, whether their bars are here or not. */\r\n getRequiredSymbols(): string[] {\r\n const out = new Set<string>();\r\n for (const ind of this.indicatorEngine.getActiveIndicators()) {\r\n const symbol = ind.params.symbol;\r\n if (SYMBOL_INDICATORS.has(ind.id) && typeof symbol === 'string' && symbol.trim()) out.add(symbol.trim());\r\n }\r\n return [...out];\r\n }\r\n\r\n /**\r\n * Ask once for the symbol an indicator just added or changed reads, when\r\n * the chart hasn't got it (not for symbols other indicators read: one\r\n * whose fetch failed is asked for again only by an indicator on it).\r\n */\r\n private requestMissingSymbols(instanceId: string): void {\r\n this.forgetUnreadSymbols();\r\n const config = this.indicatorEngine.getIndicatorConfig(instanceId);\r\n const symbol = config && SYMBOL_INDICATORS.has(config.id) && typeof config.params.symbol === 'string' ? config.params.symbol.trim() : '';\r\n if (!symbol || this.symbolSeries.has(symbol) || this.requestedSymbols.has(symbol)) return;\r\n this.requestedSymbols.add(symbol);\r\n this.eventBus.emit('symbolSeriesRequest', { symbol });\r\n }\r\n\r\n /**\r\n * Change an indicator's inputs. A source that would make it read from its\r\n * own lines (through other indicators) is ignored.\r\n */\r\n updateIndicator(instanceId: string, params: Record<string, number | string | boolean>): void {\r\n this.recordIndicators(instanceId, () => this.updateIndicatorNow(instanceId, params));\r\n }\r\n\r\n private updateIndicatorNow(instanceId: string, params: Record<string, number | string | boolean>): void {\r\n const descriptor = this.indicatorEngine.getIndicatorDescriptor(instanceId);\r\n const name = descriptor ? sourceParam(descriptor) : null;\r\n const before = name ? this.indicatorEngine.getIndicatorConfig(instanceId)?.params[name] : undefined;\r\n this.indicatorEngine.updateIndicator(instanceId, params, this.dataManager.getData());\r\n this.announceIndicatorUpdate(0);\r\n this.markStateChanged();\r\n this.eventBus.emit('indicatorChange', { instanceId, change: 'params' });\r\n this.requestMissingSymbols(instanceId);\r\n // A price-pane indicator follows a new source into its pane, and leaves\r\n // it when it no longer reads from it; otherwise it stays where it is.\r\n const config = this.indicatorEngine.getIndicatorConfig(instanceId);\r\n if (descriptor?.placement === 'overlay' && config && name && config.params[name] !== before) {\r\n const line = parseIndicatorSource(config.params[name]);\r\n const pane = line ? this.paneHostOf(line.instanceId, instanceId)\r\n : parseIndicatorSource(before) ? null : config.pane ?? null;\r\n if ((pane ?? undefined) !== config.pane) {\r\n this.indicatorEngine.setPane(instanceId, pane);\r\n this.eventBus.emit('indicatorChange', { instanceId, change: 'pane' });\r\n }\r\n }\r\n this.updateViewportAndRender();\r\n }\r\n\r\n /**\r\n * The pane an indicator is drawn in, other than its own: the pane of the\r\n * instance `requested`, or, for a price-pane indicator computed from a pane\r\n * indicator's line, that indicator's pane. Null = the price pane (overlays)\r\n * or a pane of its own (panels).\r\n */\r\n private paneFor(\r\n descriptor: IndicatorDescriptor,\r\n params: Readonly<Record<string, unknown>>,\r\n requested?: string,\r\n self?: string,\r\n ): string | null {\r\n if (requested) return this.paneHostOf(requested, self);\r\n if (descriptor.placement !== 'overlay') return null;\r\n const name = sourceParam(descriptor);\r\n const line = name ? parseIndicatorSource(params[name]) : null;\r\n return line ? this.paneHostOf(line.instanceId, self) : null;\r\n }\r\n\r\n /** The instance owning the pane `instanceId` is drawn in; null for the price pane. */\r\n private paneHostOf(instanceId: string, self?: string): string | null {\r\n if (instanceId === self) return null;\r\n if (this.layoutManager.getPanels().some((p) => p.id === instanceId)) return instanceId;\r\n const pane = this.indicatorEngine.getIndicatorConfig(instanceId)?.pane;\r\n return pane && pane !== self ? pane : null;\r\n }\r\n\r\n /** An indicator's reference levels (RSI 30 / 70): its own, else its indicator's defaults. */\r\n getIndicatorLevels(instanceId: string): number[] {\r\n return this.indicatorEngine.getLevels(instanceId);\r\n }\r\n\r\n /**\r\n * Put a price-pane overlay on the left price scale (fit to its own values,\r\n * the scale shows by itself) or back on the price scale. False when the\r\n * indicator isn't a price-pane overlay.\r\n */\r\n setIndicatorScale(instanceId: string, scale: OverlayScale): boolean {\r\n return this.recordIndicators(instanceId, () => this.setIndicatorScaleNow(instanceId, scale));\r\n }\r\n\r\n private setIndicatorScaleNow(instanceId: string, scale: OverlayScale): boolean {\r\n if (!this.indicatorEngine.setScale(instanceId, scale)) return false;\r\n this.updateViewportAndRender();\r\n this.markStateChanged();\r\n this.eventBus.emit('indicatorChange', { instanceId, change: 'scale' });\r\n return true;\r\n }\r\n\r\n getIndicatorScale(instanceId: string): OverlayScale {\r\n return this.indicatorEngine.getIndicatorConfig(instanceId)?.scale === 'left' ? 'left' : 'right';\r\n }\r\n\r\n /** Show the left price scale even without overlays on it (it then mirrors the price scale). */\r\n setLeftPriceScaleVisible(visible: boolean): void {\r\n this.leftScaleRequested = visible;\r\n this.updateViewportAndRender();\r\n }\r\n\r\n /** Whether the left price scale is on screen: asked for, or carrying an overlay. */\r\n isLeftPriceScaleShown(): boolean {\r\n return this.features.priceAxis !== false && (this.leftScaleRequested || this.indicatorEngine.hasLeftScaleOverlays());\r\n }\r\n\r\n /** The left price scale's range on screen, or null while it is hidden. */\r\n getLeftPriceRange(): { min: number; max: number } | null {\r\n return this.leftViewport ? { ...this.leftViewport.priceRange } : null;\r\n }\r\n\r\n /**\r\n * The left scale's viewport for this frame: fit to the overlays on it over\r\n * the visible bars (on a plain linear scale), or the price scale's when\r\n * none is.\r\n */\r\n private computeLeftViewport(): ViewportState | null {\r\n if (!this.isLeftPriceScaleShown()) return null;\r\n const vs = this.viewport.getState();\r\n if (!this.indicatorEngine.hasLeftScaleOverlays()) return vs;\r\n const range = this.indicatorEngine.getOverlayPriceRange(vs.visibleRange.from, vs.visibleRange.to, 'left');\r\n if (!range) return vs;\r\n const span = range.max - range.min || Math.abs(range.max) || 1;\r\n return {\r\n ...vs,\r\n priceRange: { min: range.min - span * LEFT_SCALE_PADDING, max: range.max + span * LEFT_SCALE_PADDING },\r\n logScale: false,\r\n scaleMode: 'regular',\r\n invertScale: false,\r\n // The overlays' own numbers, not the price's format.\r\n formatPrice: undefined,\r\n priceUnit: undefined,\r\n };\r\n }\r\n\r\n /** Set an indicator's reference levels; `null` restores its indicator's defaults. */\r\n setIndicatorLevels(instanceId: string, levels: readonly number[] | null): void {\r\n this.recordIndicators(instanceId, () => this.setIndicatorLevelsNow(instanceId, levels));\r\n }\r\n\r\n private setIndicatorLevelsNow(instanceId: string, levels: readonly number[] | null): void {\r\n if (!this.indicatorEngine.setLevels(instanceId, levels)) return;\r\n this.updateViewportAndRender();\r\n this.markStateChanged();\r\n this.eventBus.emit('indicatorChange', { instanceId, change: 'levels' });\r\n }\r\n\r\n /**\r\n * Remove an indicator, and the indicators computed from its lines with it.\r\n * Others drawn in its pane keep the pane (a pane indicator takes it over)\r\n * or go back to the price pane.\r\n */\r\n removeIndicator(instanceId: string): void {\r\n this.recordIndicators(undefined, () => this.removeIndicatorNow(instanceId));\r\n }\r\n\r\n private removeIndicatorNow(instanceId: string): void {\r\n if (!this.indicatorEngine.getIndicatorConfig(instanceId)) return;\r\n // It and everything computed from its lines, the last readers first.\r\n const doomed = [instanceId, ...this.indicatorEngine.getDependents(instanceId)];\r\n for (const id of doomed.reverse()) this.detachIndicator(id);\r\n this.markStateChanged();\r\n this.updateViewportAndRender();\r\n }\r\n\r\n /**\r\n * Remove one indicator. The first pane indicator drawn in its pane takes the\r\n * pane over and the others stay in it.\r\n */\r\n private detachIndicator(instanceId: string): void {\r\n if (!this.indicatorEngine.getIndicatorConfig(instanceId)) return;\r\n const panel = this.layoutManager.getPanels().find((p) => p.id === instanceId);\r\n const members = this.indicatorEngine.getPaneMembers(instanceId);\r\n const isPanel = (id: string) => this.indicatorEngine.getIndicatorDescriptor(id)?.placement === 'panel';\r\n const heir = panel ? members.find(isPanel) ?? null : null;\r\n this.indicatorEngine.removeIndicator(instanceId);\r\n if (heir) {\r\n this.indicatorEngine.setPane(heir, null);\r\n this.layoutManager.renamePanel(instanceId, heir);\r\n } else {\r\n this.layoutManager.removePanel(instanceId);\r\n }\r\n // With an heir the others stay in its pane; without one (only price-pane\r\n // indicators were drawn here) they go back to the price pane.\r\n for (const member of members) {\r\n if (member !== heir) this.indicatorEngine.setPane(member, heir);\r\n this.eventBus.emit('indicatorChange', { instanceId: member, change: 'pane' });\r\n }\r\n this.eventBus.emit('indicatorRemove', { instanceId });\r\n this.forgetUnreadSymbols();\r\n }\r\n\r\n /** The colours, line widths and opacity an indicator draws with (a copy), or null. */\r\n getIndicatorStyle(instanceId: string): ResolvedIndicatorStyle | null {\r\n return this.indicatorEngine.getIndicatorStyle(instanceId);\r\n }\r\n\r\n getIndicatorOutput(instanceId: string): IndicatorOutput | null {\r\n return this.indicatorEngine.getOutput(instanceId);\r\n }\r\n\r\n registerIndicator(plugin: IndicatorPlugin): void {\r\n this.pluginManager.registerIndicator(plugin);\r\n }\r\n\r\n /** The chart's plugin registry — register custom indicators / drawings / chart types / overlays. */\r\n get plugins(): PluginManager {\r\n return this.pluginManager;\r\n }\r\n\r\n static indicators(): IndicatorDescriptor[] {\r\n const engine = new IndicatorEngine();\r\n registerBuiltInIndicators(engine);\r\n return engine.getAvailableIndicators();\r\n }\r\n\r\n // --- Panel layout ---\r\n\r\n setPanelPosition(instanceId: string, position: PanelPosition): void {\r\n this.layoutManager.setPanelPosition(instanceId, position);\r\n this.updateViewportAndRender();\r\n this.eventBus.emit('indicatorChange', { instanceId, change: 'pane' });\r\n }\r\n\r\n setPanelSize(instanceId: string, size: number): void {\r\n this.layoutManager.setPanelSize(instanceId, size);\r\n this.updateViewportAndRender();\r\n this.eventBus.emit('paneResize', { instanceId, size });\r\n this.markStateChanged();\r\n }\r\n\r\n /**\r\n * Move an indicator to another pane: `target` is an indicator drawn there\r\n * (it joins that pane, on its scale), `'new'` a pane of its own (indicators\r\n * that draw in a pane), or `'price'` the price pane (overlays). Indicators\r\n * drawn in its own pane stay there (one of them takes the pane over) or\r\n * follow it when they read its lines. False when it can't go there.\r\n */\r\n moveIndicatorToPane(instanceId: string, target: string): boolean {\r\n return this.recordIndicators(instanceId, () => this.moveIndicatorToPaneNow(instanceId, target));\r\n }\r\n\r\n /** Whether `moveIndicatorToPane(instanceId, target)` would move it. */\r\n canMoveIndicatorToPane(instanceId: string, target: string): boolean {\r\n return this.paneMoveHost(instanceId, target) !== undefined;\r\n }\r\n\r\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. */\r\n private paneMoveHost(instanceId: string, target: string): string | null | undefined {\r\n const config = this.indicatorEngine.getIndicatorConfig(instanceId);\r\n const descriptor = this.indicatorEngine.getIndicatorDescriptor(instanceId);\r\n if (!config || !descriptor) return undefined;\r\n // An overlay computed from another indicator's line stays with that line.\r\n const name = sourceParam(descriptor);\r\n if (descriptor.placement === 'overlay' && name && parseIndicatorSource(config.params[name])) return undefined;\r\n if (target === 'price') return descriptor.placement === 'overlay' && config.pane ? null : undefined;\r\n if (target === 'new') {\r\n const ownsPane = this.layoutManager.getPanels().some((p) => p.id === instanceId);\r\n return descriptor.placement === 'panel' && !ownsPane ? null : undefined;\r\n }\r\n const host = this.paneHostOf(target, instanceId);\r\n return host && host !== config.pane && host !== instanceId ? host : undefined;\r\n }\r\n\r\n private moveIndicatorToPaneNow(instanceId: string, target: string): boolean {\r\n const host = this.paneMoveHost(instanceId, target);\r\n const config = this.indicatorEngine.getIndicatorConfig(instanceId);\r\n if (host === undefined || !config) return false;\r\n // Where it was, before the moves below change the config.\r\n const formerPane = config.pane;\r\n const ownsPane = this.layoutManager.getPanels().some((p) => p.id === instanceId);\r\n\r\n const changed = new Set<string>([instanceId]);\r\n if (ownsPane) {\r\n // Its pane's other indicators: those reading its lines go with it; of\r\n // the rest, the first that draws in a pane takes the pane over.\r\n const members = this.indicatorEngine.getPaneMembers(instanceId);\r\n const readers = new Set(this.indicatorEngine.getDependents(instanceId));\r\n const staying = members.filter((m) => !readers.has(m));\r\n const heir = staying.find((m) => this.indicatorEngine.getIndicatorDescriptor(m)?.placement === 'panel') ?? null;\r\n if (heir) {\r\n this.indicatorEngine.setPane(heir, null);\r\n this.layoutManager.renamePanel(instanceId, heir);\r\n for (const m of staying) if (m !== heir) this.indicatorEngine.setPane(m, heir);\r\n } else {\r\n this.layoutManager.removePanel(instanceId);\r\n for (const m of staying) this.indicatorEngine.setPane(m, null);\r\n }\r\n for (const m of members) {\r\n if (readers.has(m)) this.indicatorEngine.setPane(m, host ?? instanceId);\r\n changed.add(m);\r\n }\r\n }\r\n this.indicatorEngine.setPane(instanceId, host);\r\n if (target === 'new') {\r\n this.layoutManager.addPanel(instanceId, 'bottom');\r\n // Those reading its lines come along into its new pane.\r\n for (const m of this.indicatorEngine.getDependents(instanceId)) {\r\n if (this.indicatorEngine.getIndicatorConfig(m)?.pane === formerPane) {\r\n this.indicatorEngine.setPane(m, instanceId);\r\n changed.add(m);\r\n }\r\n }\r\n }\r\n for (const id of changed) this.eventBus.emit('indicatorChange', { instanceId: id, change: 'pane' });\r\n this.updateViewportAndRender();\r\n this.markStateChanged();\r\n return true;\r\n }\r\n\r\n /**\r\n * Fold a pane to its header, or open it again: the pane `instanceId` is\r\n * drawn in. While a pane is maximised, this puts the panes back first.\r\n */\r\n setPaneCollapsed(instanceId: string, collapsed: boolean): boolean {\r\n const pane = this.paneOf(instanceId);\r\n return this.recordIndicators(pane, () => this.paneChange(pane, 'collapsed', () => this.layoutManager.setPanelCollapsed(pane, collapsed)));\r\n }\r\n\r\n /** Whether the pane `instanceId` is drawn in shows folded (folded, or another pane is maximised). */\r\n isPaneCollapsed(instanceId: string): boolean {\r\n return this.layoutManager.isPanelShownCollapsed(this.paneOf(instanceId));\r\n }\r\n\r\n /** Let one pane take the room (the others fold, the price pane keeps a strip); `null` puts them back. */\r\n setMaximizedPane(instanceId: string | null): boolean {\r\n const pane = instanceId === null ? null : this.paneOf(instanceId);\r\n const subject = pane ?? this.layoutManager.getMaximizedPanel() ?? '';\r\n return this.recordIndicators(subject, () => this.paneChange(subject, 'maximized', () => this.layoutManager.setMaximizedPanel(pane)));\r\n }\r\n\r\n getMaximizedPane(): string | null {\r\n return this.layoutManager.getMaximizedPanel();\r\n }\r\n\r\n /**\r\n * A pane's value scale: logarithmic (used while all its values are above 0;\r\n * linear otherwise), upside down. The pane `instanceId` is drawn in.\r\n */\r\n setPaneScale(instanceId: string, scale: { log?: boolean; invert?: boolean; percent?: boolean }): boolean {\r\n const pane = this.paneOf(instanceId);\r\n return this.recordIndicators(pane, () => this.paneChange(pane, 'scale', () => this.layoutManager.setPanelScale(pane, scale)));\r\n }\r\n\r\n /** The pane's value scale as set (see `setPaneScale`). */\r\n getPaneScale(instanceId: string): { log: boolean; invert: boolean; percent: boolean } {\r\n const panel = this.layoutManager.getPanels().find((p) => p.id === this.paneOf(instanceId));\r\n return { log: !!panel?.logScale, invert: !!panel?.invertScale, percent: !!panel?.percentScale };\r\n }\r\n\r\n /** A pane's first value on screen (its own indicator's first line), the 0% of a percent scale. */\r\n private paneBaseline(instanceId: string, from: number, to: number): number | undefined {\r\n const series = this.indicatorEngine.getOutput(instanceId)?.series;\r\n const descriptor = this.indicatorEngine.getIndicatorDescriptor(instanceId);\r\n if (!series || !descriptor) return undefined;\r\n const key = descriptor.plots?.[0]?.key;\r\n for (let i = Math.max(0, from); i <= to && i < series.length; i++) {\r\n const point = series[i];\r\n if (!point) continue;\r\n const value = key !== undefined ? point[key] : Object.values(point).find((v) => typeof v === 'number' && Number.isFinite(v));\r\n if (typeof value === 'number' && Number.isFinite(value) && value !== 0) return value;\r\n }\r\n return undefined;\r\n }\r\n\r\n /** Move a pane one place up (`-1`) or down (`1`) among the panes on its side. */\r\n movePane(instanceId: string, delta: -1 | 1): boolean {\r\n const pane = this.paneOf(instanceId);\r\n return this.recordIndicators(pane, () => this.paneChange(pane, 'order', () => this.layoutManager.movePanel(pane, delta)));\r\n }\r\n\r\n /** Whether `movePane(instanceId, delta)` would move it. */\r\n canMovePane(instanceId: string, delta: -1 | 1): boolean {\r\n return this.layoutManager.canMovePanel(this.paneOf(instanceId), delta);\r\n }\r\n\r\n /** The pane an indicator is drawn in: its own, or the one it shares. */\r\n private paneOf(instanceId: string): string {\r\n return this.indicatorEngine.getIndicatorConfig(instanceId)?.pane ?? instanceId;\r\n }\r\n\r\n private paneChange(instanceId: string, change: 'collapsed' | 'maximized' | 'order' | 'scale', apply: () => boolean): boolean {\r\n if (!apply()) return false;\r\n this.updateViewportAndRender();\r\n this.eventBus.emit('paneChange', { instanceId, change });\r\n this.markStateChanged();\r\n return true;\r\n }\r\n\r\n /**\r\n * Indicator panes, in CSS pixels relative to the container: the whole pane,\r\n * its 20 px header (where the name is written) included.\r\n */\r\n getIndicatorPanes(): { instanceId: string; instanceIds: string[]; rect: { x: number; y: number; width: number; height: number } }[] {\r\n return this.buildPanelRenderInfos().map((p) => ({\r\n instanceId: p.instanceId,\r\n instanceIds: [p.instanceId, ...(p.members ?? [])],\r\n rect: { ...p.rect },\r\n }));\r\n }\r\n\r\n /** Tag each indicator line's latest value on its value axis (default on). */\r\n setIndicatorValueLabelsVisible(visible: boolean): void {\r\n this.features.indicatorValueLabels = visible;\r\n this.syncRenderContext();\r\n this.engine.requestRender();\r\n }\r\n\r\n /** Write each pane's indicator name and hovered values in its header (default); off when you label panes yourself. */\r\n setPaneTitlesVisible(visible: boolean): void {\r\n this.paneTitles = visible;\r\n this.engine.requestRender();\r\n }\r\n\r\n /**\r\n * The y (CSS px, container-relative) just below the OHLCV legend — where\r\n * content stacked under it starts. The plot's top edge when it is hidden.\r\n */\r\n getLegendBottom(): number {\r\n const top = this.viewport.getState().chartRect.y;\r\n return this.features.legend ? top + this.chartLegend.getHeight() : top;\r\n }\r\n\r\n /**\r\n * Whether each drawn bar is one data bar (candles, lines, Heikin-Ashi…).\r\n * Renko, Kagi, point & figure, line break and range bars redraw the series,\r\n * so a bar index on screen is not an index into the data.\r\n */\r\n isTimeAligned(): boolean {\r\n if (isReshapedChartType(this.options.chartType)) return false;\r\n return this.getDisplayData().length === this.dataManager.getLength();\r\n }\r\n\r\n /**\r\n * A price on the market's grid: a multiple of its smallest step (`minTick`),\r\n * else rounded to its precision, else to the decimals the axis shows.\r\n */\r\n roundPrice(price: number): number {\r\n if (!Number.isFinite(price)) return price;\r\n const tick = this.symbolInfo?.minTick ?? this.priceUnit ?? undefined;\r\n if (tick !== undefined && tick > 0) return Number((Math.round(price / tick) * tick).toFixed(stepDecimals(tick)));\r\n const { min, max } = this.viewport.getState().priceRange;\r\n return Number(price.toFixed(this.marketPricePrecision ?? autoPricePrecision(min, max)));\r\n }\r\n\r\n /**\r\n * How prices read on the price scale and everything that prints one: a\r\n * function of yours, fractions of a point (`{ denominator: 32 }` prints\r\n * `101'16`), or null for decimals. Indicator panes keep their own numbers.\r\n */\r\n setPriceFormat(format: PriceFormatter | PriceFraction | null): void {\r\n this.applyPriceFormat(format);\r\n // The axis may need room for the new labels, and the frame its new format.\r\n this.updateViewportAndRender();\r\n }\r\n\r\n /** The chart's price format as a function (`priceFormat`), or null for decimals. */\r\n getPriceFormatter(): PriceFormatter | null {\r\n return this.priceFormatter;\r\n }\r\n\r\n private applyPriceFormat(format: PriceFormatter | PriceFraction | null): void {\r\n this.priceFormatter = priceFormatterFor(format);\r\n this.priceUnit = format && typeof format !== 'function' ? fractionTick(format) : null;\r\n this.viewport.setPriceFormat(this.priceFormatter, this.priceUnit);\r\n this.crosshairTooltip.setPriceFormatter(this.priceFormatter);\r\n this.pinnedTooltip.setPriceFormatter(this.priceFormatter);\r\n }\r\n\r\n /** How times read on the time axis, the crosshair and the tooltip; null for the chart's own. */\r\n setTimeFormatter(formatter: TimeFormatter | null): void {\r\n this.applyTimeFormatter(formatter);\r\n this.engine.requestRender();\r\n }\r\n\r\n private applyTimeFormatter(formatter: TimeFormatter | null): void {\r\n this.timeAxis.setTimeFormatter(formatter);\r\n this.crosshairHandler.setTimeFormatter(formatter);\r\n this.crosshairTooltip.setTimeFormatter(formatter);\r\n }\r\n\r\n /**\r\n * A price as the price axis writes it: in the chart's price format when it\r\n * has one (`priceFormat`), else at the market's precision or the visible\r\n * range's, in the number locale.\r\n */\r\n formatPrice(price: number): string {\r\n if (this.priceFormatter) return this.priceFormatter(price);\r\n const { min, max } = this.viewport.getState().priceRange;\r\n return formatPrice(price, this.marketPricePrecision ?? autoPricePrecision(min, max), this.numberLocale);\r\n }\r\n\r\n // --- Drawing tools ---\r\n\r\n setDrawingTool(type: DrawingToolType | null): void {\r\n if (!this.features.drawings) return;\r\n // If whitelist is set, check it\r\n if (type && this.features.drawingTools.length > 0 && !this.features.drawingTools.includes(type)) return;\r\n // A tool picked puts the eraser and the zoom box away (the manager does the eraser).\r\n if (type) this.setZoomAreaMode(false);\r\n this.drawingManager.setActiveTool(type);\r\n }\r\n\r\n getDrawingTool(): DrawingToolType | null {\r\n return this.drawingManager.getActiveTool();\r\n }\r\n\r\n /** Keep the drawing tool after each drawing, to draw several in a row (Esc ends). */\r\n setStayInDrawingMode(enabled: boolean): void {\r\n this.drawingManager.setStayInDrawingMode(enabled);\r\n }\r\n\r\n isStayInDrawingMode(): boolean {\r\n return this.drawingManager.isStayInDrawingMode();\r\n }\r\n\r\n /** Copy the selected drawings (also Ctrl/⌘+C on the chart). Returns how many. */\r\n copyDrawings(): number {\r\n return this.drawingManager.copySelection();\r\n }\r\n\r\n /**\r\n * Paste copied drawings (also Ctrl/⌘+V) — from this chart or another one on\r\n * the page. Returns the new drawings' ids.\r\n */\r\n pasteDrawings(): string[] {\r\n if (!this.features.drawings) return [];\r\n return this.drawingManager.paste();\r\n }\r\n\r\n setDrawingStyle(style: Partial<DrawingStyle>): void {\r\n this.drawingManager.setStyle(style);\r\n }\r\n\r\n getDrawingStyle(): DrawingStyle {\r\n return this.drawingManager.getActiveStyle();\r\n }\r\n\r\n getSelectedDrawingId(): string | null {\r\n return this.drawingManager.getSelectedDrawingId();\r\n }\r\n\r\n /** Restyle the selected drawing (or one by id). Returns true if applied. */\r\n setSelectedDrawingStyle(style: Partial<DrawingStyle>, id?: string): boolean {\r\n return this.drawingManager.setSelectedDrawingStyle(style, id);\r\n }\r\n\r\n getDrawings(): DrawingState[] {\r\n return this.drawingManager.getDrawings();\r\n }\r\n\r\n setDrawings(drawings: DrawingState[]): void {\r\n this.drawingManager.setDrawings(drawings);\r\n this.dropOrphanDrawingAlerts();\r\n this.announceStateChange();\r\n }\r\n\r\n /** Append a drawing (id auto-assigned, active style applied). Returns the id. */\r\n addDrawing(state: {\r\n type: import('@tradecanvas/commons').DrawingToolType;\r\n anchors: import('@tradecanvas/commons').AnchorPoint[];\r\n style?: Partial<DrawingStyle>;\r\n /** The tool's own settings (see `getDrawingOptionDefs`); its defaults fill the rest. */\r\n options?: DrawingOptions;\r\n visible?: boolean;\r\n locked?: boolean;\r\n meta?: Record<string, unknown>;\r\n }): string | null {\r\n if (!this.features.drawings) return null;\r\n const id = this.drawingManager.addDrawing(state);\r\n this.markStateChanged();\r\n return id;\r\n }\r\n\r\n /**\r\n * Draw a Fibonacci retracement over the dominant swing (extreme high/low) in\r\n * the visible range. Returns the new drawing's id, or null if no swing.\r\n */\r\n autoFib(): string | null {\r\n if (!this.features.drawings) return null;\r\n const data = this.getDisplayData();\r\n const vs = this.viewport.getState();\r\n const swing = findDominantSwing(data, vs.visibleRange.from, vs.visibleRange.to);\r\n if (!swing) return null;\r\n return this.addDrawing({ type: 'fibRetracement', anchors: [swing[0], swing[1]] });\r\n }\r\n\r\n removeDrawing(id: string): void {\r\n this.drawingManager.removeDrawing(id);\r\n }\r\n\r\n /** Remove several drawings as one undo step; locked ones stay. Returns how many went. */\r\n removeDrawings(ids: readonly string[]): number {\r\n return this.drawingManager.removeDrawings(ids);\r\n }\r\n\r\n /** A drawing tool's name, anchors, options and style fields; null for an unknown tool. */\r\n getDrawingToolDescriptor(type: DrawingToolType): DrawingDescriptor | null {\r\n const descriptor = this.drawingManager.getDescriptor(type);\r\n return descriptor ? structuredClone(descriptor) : null;\r\n }\r\n\r\n /** The settings a drawing tool offers beyond the shared style (levels, extend…), with defaults. */\r\n getDrawingOptionDefs(type: DrawingToolType): DrawingOptionDefs {\r\n return this.drawingManager.getOptionDefs(type);\r\n }\r\n\r\n /** Every option of a drawing, its tool's defaults filled in. */\r\n getDrawingOptions(id: string): DrawingOptions {\r\n return this.drawingManager.getDrawingOptions(id);\r\n }\r\n\r\n /** Change some of a drawing's options; invalid ones are dropped. False for an unknown drawing. */\r\n setDrawingOptions(id: string, options: DrawingOptions): boolean {\r\n return this.updateDrawing(id, { options });\r\n }\r\n\r\n /**\r\n * Change a drawing's anchors (as many as it has), style or options, as one\r\n * undo step — or as part of the edit begun with `beginDrawingEdit`.\r\n * Applies to locked drawings too. False for an unknown drawing or wrong anchors.\r\n */\r\n updateDrawing(id: string, patch: DrawingPatch): boolean {\r\n const changed = this.drawingManager.updateDrawing(id, patch);\r\n if (changed) this.markStateChanged();\r\n return changed;\r\n }\r\n\r\n /** Start an edit (a settings dialog): the `updateDrawing` calls until `endDrawingEdit` are one undo step. */\r\n beginDrawingEdit(id: string): boolean {\r\n return this.drawingManager.beginEdit(id);\r\n }\r\n\r\n /** Keep the edit as one undo step, or with `cancel` put the drawing back as it was. */\r\n endDrawingEdit(id: string, options: { cancel?: boolean } = {}): void {\r\n this.drawingManager.endEdit(id, options);\r\n this.markStateChanged();\r\n }\r\n\r\n /** Options new drawings of `type` start with; null clears them. */\r\n setDrawingToolDefaults(type: DrawingToolType, options: DrawingOptions | null): void {\r\n this.drawingManager.setToolDefaults(type, options);\r\n }\r\n\r\n getDrawingToolDefaults(type: DrawingToolType): DrawingOptions {\r\n return this.drawingManager.getToolDefaults(type);\r\n }\r\n\r\n setDrawingVisible(id: string, visible: boolean): void {\r\n this.drawingManager.setDrawingVisible(id, visible);\r\n }\r\n\r\n setDrawingLocked(id: string, locked: boolean): void {\r\n this.drawingManager.setDrawingLocked(id, locked);\r\n }\r\n\r\n /** Show or hide several drawings (a selection, say) as one undo step. */\r\n setDrawingsVisible(ids: readonly string[], visible: boolean): void {\r\n this.drawingManager.setDrawingsVisible(ids, visible);\r\n }\r\n\r\n /** Lock or unlock several drawings as one undo step. */\r\n setDrawingsLocked(ids: readonly string[], locked: boolean): void {\r\n this.drawingManager.setDrawingsLocked(ids, locked);\r\n }\r\n\r\n clearDrawings(): void {\r\n this.drawingManager.clearDrawings();\r\n this.dropOrphanDrawingAlerts();\r\n }\r\n\r\n registerDrawingTool(plugin: DrawingPlugin): void {\r\n this.drawingManager.register(plugin);\r\n }\r\n\r\n // --- Undo/Redo ---\r\n\r\n undo(): boolean {\r\n if (!this.features.drawingUndoRedo) return false;\r\n return this.drawingManager.undo();\r\n }\r\n\r\n redo(): boolean {\r\n if (!this.features.drawingUndoRedo) return false;\r\n return this.drawingManager.redo();\r\n }\r\n\r\n /** Select a drawing (and the rest of its group); false for one it can't (unknown, hidden). */\r\n selectDrawing(id: string): boolean {\r\n const ok = this.drawingManager.select(id);\r\n if (ok) this.engine.requestRender();\r\n return ok;\r\n }\r\n\r\n getUndoRedoState(): { canUndo: boolean; canRedo: boolean } {\r\n return this.undoRedoManager.getState();\r\n }\r\n\r\n /**\r\n * Put a change of yours in the chart's undo history, with its drawings and\r\n * indicators: undo (Ctrl/Cmd+Z) calls `undo`, redo calls `redo`. Call it\r\n * after making the change. Changes about the same `subject` close together\r\n * merge into one step (a colour dragged across a picker). Ignored when\r\n * `features.drawingUndoRedo` is off.\r\n */\r\n recordUndo(step: { undo: () => void; redo: () => void; subject?: string }): void {\r\n if (!this.features.drawingUndoRedo || typeof step?.undo !== 'function' || typeof step.redo !== 'function') return;\r\n const at = performance.now();\r\n const last = this.undoRedoManager.last();\r\n if (step.subject !== undefined && last?.type === 'custom' && last.custom?.subject === step.subject && at - last.custom.at < INDICATOR_EDIT_MERGE_MS) {\r\n // Undo goes back to before the burst; redo to its end.\r\n this.undoRedoManager.replaceLast({ ...last, custom: { undo: last.custom.undo, redo: step.redo, subject: step.subject, at } });\r\n return;\r\n }\r\n this.undoRedoManager.push({ type: 'custom', before: null, after: null, custom: { undo: step.undo, redo: step.redo, subject: step.subject, at } });\r\n }\r\n\r\n // --- Drawing magnet ---\r\n\r\n /** The weak magnet on or off. Ignored (stays off) when `features.drawingMagnet` is false. */\r\n setDrawingMagnet(enabled: boolean): void {\r\n this.setDrawingMagnetMode(enabled ? 'weak' : 'off');\r\n }\r\n\r\n /** Whether a magnet (weak or strong) is on. */\r\n getDrawingMagnet(): boolean {\r\n return this.drawingManager.getMagnetMode() !== 'none';\r\n }\r\n\r\n /**\r\n * Snap new anchors to the bar's open, high, low or close: 'weak' when the\r\n * pointer is near one, 'strong' always. Stays off when\r\n * `features.drawingMagnet` is false.\r\n */\r\n setDrawingMagnetMode(mode: 'off' | 'weak' | 'strong'): void {\r\n const on = this.features.drawingMagnet && mode !== 'off';\r\n this.drawingManager.setMagnetMode(!on ? 'none' : mode === 'strong' ? 'strong' : 'magnet');\r\n }\r\n\r\n getDrawingMagnetMode(): 'off' | 'weak' | 'strong' {\r\n const mode = this.drawingManager.getMagnetMode();\r\n return mode === 'none' ? 'off' : mode === 'strong' ? 'strong' : 'weak';\r\n }\r\n\r\n // --- Right-click and the \"+\" by the price axis ---\r\n\r\n /** The price and bar time at a right-click, as far as its area has them. */\r\n private contextAt(area: import('@tradecanvas/commons').ChartContextArea, pos: { x: number; y: number }): import('@tradecanvas/commons').ChartContextMenuPayload {\r\n const vs = { ...this.viewport.getState(), data: this.getDisplayData() };\r\n const payload: import('@tradecanvas/commons').ChartContextMenuPayload = { area, x: pos.x, y: pos.y };\r\n if (area === 'plot' || area === 'priceAxis') payload.price = yToPrice(pos.y, vs);\r\n if ((area === 'plot' || area === 'timeAxis') && vs.data.length > 0) payload.time = xToTime(pos.x, vs);\r\n if (area === 'pane') {\r\n const pane = this.getIndicatorPanes().find((p) => pos.y >= p.rect.y && pos.y <= p.rect.y + p.rect.height);\r\n if (pane) payload.pane = pane.instanceId;\r\n }\r\n return payload;\r\n }\r\n\r\n /** Show or hide the \"+\" by the price axis (`features.priceAxisAddButton`). */\r\n setPriceAxisAddButton(visible: boolean): void {\r\n this.features.priceAxisAddButton = visible;\r\n this.priceAxisAddButton.setEnabled(visible && this.features.crosshair);\r\n this.engine.requestRender(LayerType.Overlay);\r\n }\r\n\r\n // --- Eraser and zoom area ---\r\n\r\n /**\r\n * The eraser: while on, clicking a drawing removes it (each one undoable).\r\n * Picking a drawing tool or pressing Escape turns it off. `toolModeChange`\r\n * reports `{ eraser }`.\r\n */\r\n setEraserMode(on: boolean): void {\r\n if (on) this.setZoomAreaMode(false);\r\n this.drawingManager.setEraser(on && this.features.drawings);\r\n }\r\n\r\n isEraserMode(): boolean {\r\n return this.drawingManager.isEraser();\r\n }\r\n\r\n /**\r\n * The zoom-area tool: the next drag draws a box and the chart zooms to its\r\n * bars; then it turns itself off. `toolModeChange` reports `{ zoomArea }`.\r\n */\r\n setZoomAreaMode(on: boolean): void {\r\n if (on === this.zoomAreaMode) return;\r\n this.zoomAreaMode = on;\r\n if (on) {\r\n this.drawingManager.setEraser(false);\r\n this.drawingManager.setActiveTool(null);\r\n }\r\n this.interactionManager?.setZoomAreaMode(on);\r\n this.eventBus.emit('toolModeChange', { zoomArea: on });\r\n }\r\n\r\n isZoomAreaMode(): boolean {\r\n return this.zoomAreaMode;\r\n }\r\n\r\n /** Zoom to the bars under a box dragged in zoom-area mode. */\r\n private zoomToBox(box: { x0: number; x1: number }): void {\r\n const vs = { ...this.viewport.getState(), data: this.getDisplayData() };\r\n this.setVisibleRange(xToTime(box.x0, vs), xToTime(box.x1, vs));\r\n }\r\n\r\n // --- Bulk drawing operations ---\r\n\r\n lockAllDrawings(): void {\r\n this.drawingManager.lockAllDrawings();\r\n }\r\n\r\n unlockAllDrawings(): void {\r\n this.drawingManager.unlockAllDrawings();\r\n }\r\n\r\n hideAllDrawings(): void {\r\n this.drawingManager.hideAllDrawings();\r\n }\r\n\r\n showAllDrawings(): void {\r\n this.drawingManager.showAllDrawings();\r\n }\r\n\r\n // --- Drawing duplication ---\r\n\r\n duplicateDrawing(id?: string): string | null {\r\n const targetId = id ?? this.drawingManager.getSelectedDrawingId();\r\n if (!targetId) return null;\r\n return this.drawingManager.duplicateDrawing(targetId);\r\n }\r\n\r\n // --- Data export ---\r\n\r\n /** Does nothing when `features.dataExport` is false. */\r\n /**\r\n * Download the bars on screen as CSV or JSON, each indicator line in a\r\n * column of its own (`indicators: false` leaves them out). Does nothing\r\n * when `features.dataExport` is false.\r\n */\r\n exportVisibleData(format: 'csv' | 'json' = 'csv', filename?: string, options: { indicators?: boolean } = {}): void {\r\n if (!this.features.dataExport) return;\r\n this.download(format, filename, { ...options, range: 'visible' });\r\n }\r\n\r\n /** `exportVisibleData` for every bar loaded. Does nothing when `features.dataExport` is false. */\r\n exportAllData(format: 'csv' | 'json' = 'csv', filename?: string, options: { indicators?: boolean } = {}): void {\r\n if (!this.features.dataExport) return;\r\n this.download(format, filename, { ...options, range: 'all' });\r\n }\r\n\r\n /**\r\n * The bars and their indicator lines, one column per line named as the\r\n * legend names it (\"SMA 20\", \"MACD 12 26 9 Signal\"): every bar loaded, or\r\n * those on screen (`range: 'visible'`).\r\n */\r\n getExportData(options: { range?: 'all' | 'visible'; indicators?: boolean } = {}): { bars: OHLCBar[]; columns: ExportColumn[] } {\r\n const data = this.dataManager.getData();\r\n let from = 0;\r\n let to = data.length - 1;\r\n if (options.range === 'visible') {\r\n const { visibleRange } = this.viewport.getState();\r\n from = Math.max(0, visibleRange.from);\r\n to = Math.min(data.length - 1, visibleRange.to);\r\n }\r\n const bars = data.slice(from, to + 1);\r\n if (options.indicators === false) return { bars, columns: [] };\r\n const columns: ExportColumn[] = [];\r\n for (const ind of this.indicatorEngine.getActiveIndicators()) {\r\n const series = this.indicatorEngine.getOutput(ind.instanceId)?.series;\r\n if (!series) continue;\r\n const name = indicatorChipLabel(ind.id, ind.params, ind.descriptor.defaultConfig, ind.descriptor.shortName);\r\n const plots = ind.descriptor.plots ?? Object.keys(series.find((p) => p) ?? {}).map((key) => ({ key, title: key }));\r\n for (const plot of plots) {\r\n const title = plots.length > 1 ? ` ${plot.title ?? plot.key}` : '';\r\n columns.push({ name: `${name}${title}`, values: bars.map((_, i) => series[from + i]?.[plot.key] ?? null) });\r\n }\r\n }\r\n // Two alike (an SMA 20 on the close and one on RSI) get a number each, so no column is lost.\r\n const seen = new Map<string, number>();\r\n for (const column of columns) {\r\n const count = (seen.get(column.name) ?? 0) + 1;\r\n seen.set(column.name, count);\r\n if (count > 1) column.name = `${column.name} (${count})`;\r\n }\r\n return { bars, columns };\r\n }\r\n\r\n /** `getExportData` as CSV or JSON text. */\r\n getExportText(format: 'csv' | 'json' = 'csv', options: { range?: 'all' | 'visible'; indicators?: boolean } = {}): string {\r\n const { bars, columns } = this.getExportData(options);\r\n return format === 'json' ? DataExporter.toJSON(bars, columns) : DataExporter.toCSV(bars, columns);\r\n }\r\n\r\n private download(format: 'csv' | 'json', filename: string | undefined, options: { range: 'all' | 'visible'; indicators?: boolean }): void {\r\n const text = this.getExportText(format, options);\r\n if (format === 'json') DataExporter.download(text, filename ?? 'chart-data.json', 'application/json');\r\n else DataExporter.download(text, filename ?? 'chart-data.csv', 'text/csv');\r\n }\r\n\r\n // --- Auto-save ---\r\n\r\n setAutoSave(key: string, delayMs = 5000): void {\r\n this.autoSaveScheduler.enable(key, delayMs);\r\n }\r\n\r\n disableAutoSave(): void {\r\n this.autoSaveScheduler.disable();\r\n }\r\n\r\n private scheduleAutoSave(): void {\r\n this.autoSaveScheduler.schedule();\r\n }\r\n\r\n /** Something a saved layout holds changed: auto-save it, and say so. */\r\n private markStateChanged(): void {\r\n this.scheduleAutoSave();\r\n this.announceStateChange();\r\n }\r\n\r\n private announceStateChange(): void {\r\n if (this.eventBus.hasListeners('stateChange')) this.eventBus.emit('stateChange', {});\r\n }\r\n\r\n // --- Indicator introspection ---\r\n\r\n getAvailableIndicators(): IndicatorDescriptor[] {\r\n return this.indicatorEngine.getAvailableIndicators();\r\n }\r\n\r\n getIndicatorInputs(id: string): { id: string; params: Record<string, unknown> } | null {\r\n const indicators = this.indicatorEngine.getAvailableIndicators();\r\n const desc = indicators.find(i => i.id === id);\r\n if (!desc) return null;\r\n return { id: desc.id, params: desc.defaultConfig };\r\n }\r\n\r\n /** Get all active indicator instances with their current params */\r\n getActiveIndicators(): import('@tradecanvas/core').ActiveIndicatorInfo[] {\r\n return this.indicatorEngine.getActiveIndicators();\r\n }\r\n\r\n /** Show or hide an indicator without removing it. */\r\n setIndicatorVisible(instanceId: string, visible: boolean): void {\r\n this.recordIndicators(instanceId, () => this.setIndicatorVisibleNow(instanceId, visible));\r\n }\r\n\r\n private setIndicatorVisibleNow(instanceId: string, visible: boolean): void {\r\n if (this.indicatorEngine.setVisible(instanceId, visible) !== null) {\r\n this.updateViewportAndRender();\r\n this.markStateChanged();\r\n this.eventBus.emit('indicatorChange', { instanceId, change: 'visible' });\r\n }\r\n }\r\n\r\n /** Get current config for a specific indicator instance */\r\n getIndicatorConfig(instanceId: string): { id: string; params: Record<string, unknown> } | null {\r\n const config = this.indicatorEngine.getIndicatorConfig(instanceId);\r\n if (!config) return null;\r\n return { id: config.id, params: { ...config.params } };\r\n }\r\n\r\n /** Update indicator colors/line widths at runtime */\r\n updateIndicatorStyle(instanceId: string, style: { colors?: string[]; lineWidths?: number[]; opacity?: number }): void {\r\n this.recordIndicators(instanceId, () => this.updateIndicatorStyleNow(instanceId, style));\r\n }\r\n\r\n private updateIndicatorStyleNow(instanceId: string, style: { colors?: string[]; lineWidths?: number[]; opacity?: number }): void {\r\n this.indicatorEngine.updateIndicatorStyle(instanceId, style);\r\n this.engine.requestRender();\r\n this.markStateChanged();\r\n this.eventBus.emit('indicatorChange', { instanceId, change: 'style' });\r\n }\r\n\r\n // --- Trading ---\r\n\r\n setOrders(orders: TradingOrder[]): void {\r\n if (!this.features.trading) return;\r\n this.tradingManager.setOrders(orders);\r\n this.eventBus.emit('ordersChange', { orders });\r\n }\r\n\r\n /**\r\n * Begin placing a draggable bracket order (entry + stop-loss + take-profit).\r\n * Defaults the entry to the latest close when `entry` is omitted. Drag the\r\n * three lines to adjust; confirm with Enter (emits `bracketPlace`) or cancel\r\n * with Esc. Returns false if trading is disabled or no price is available.\r\n */\r\n startBracket(side: import('@tradecanvas/commons').OrderSide, entry?: number): boolean {\r\n if (!this.features.trading) return false;\r\n const data = this.dataManager.getData();\r\n const price = entry ?? (data.length > 0 ? data[data.length - 1].close : null);\r\n if (price === null) return false;\r\n this.tradingManager.startBracket(side, price);\r\n return true;\r\n }\r\n\r\n cancelBracket(): void {\r\n if (this.features.trading) this.tradingManager.cancelBracket();\r\n }\r\n\r\n confirmBracket(): boolean {\r\n return this.features.trading ? this.tradingManager.confirmBracket() : false;\r\n }\r\n\r\n isBracketActive(): boolean {\r\n return this.features.trading && this.tradingManager.isBracketActive();\r\n }\r\n\r\n /**\r\n * Begin a drag-to-create single order at `price` (default: latest close) for\r\n * `side`. Drag the line to a level — the order type (limit/stop) is inferred\r\n * from the level vs the current price. Confirm with `confirmOrderDraft()`\r\n * (emits `orderPlace`) or cancel with `cancelOrderDraft()`. Returns false if\r\n * trading is disabled or no price is available.\r\n */\r\n startOrderDraft(side: import('@tradecanvas/commons').OrderSide, price?: number): boolean {\r\n if (!this.features.trading) return false;\r\n const data = this.dataManager.getData();\r\n const p = price ?? (data.length > 0 ? data[data.length - 1].close : null);\r\n if (p === null) return false;\r\n this.tradingManager.startOrderDraft(side, p);\r\n return true;\r\n }\r\n\r\n cancelOrderDraft(): void {\r\n if (this.features.trading) this.tradingManager.cancelOrderDraft();\r\n }\r\n\r\n confirmOrderDraft(): boolean {\r\n return this.features.trading ? this.tradingManager.confirmOrderDraft() : false;\r\n }\r\n\r\n isOrderDraftActive(): boolean {\r\n return this.features.trading && this.tradingManager.isOrderDraftActive();\r\n }\r\n\r\n /**\r\n * Emit an `orderPlace` intent (e.g. from a depth-ladder click). The chart\r\n * does not create the order itself — the host listens via\r\n * `chart.on('orderPlace', …)` and submits to its OMS.\r\n */\r\n placeOrderIntent(intent: import('@tradecanvas/commons').OrderPlaceIntent): void {\r\n if (!this.features.trading) return;\r\n this.eventBus.emit('orderPlace', intent);\r\n }\r\n\r\n setPositions(positions: TradingPosition[]): void {\r\n if (!this.features.trading) return;\r\n this.tradingManager.setPositions(positions);\r\n this.eventBus.emit('positionsChange', { positions });\r\n }\r\n\r\n /** The working orders on the chart (`setOrders`, or the execution adapter's). */\r\n getOrders(): TradingOrder[] {\r\n return this.tradingManager.getOrders();\r\n }\r\n\r\n /** The open positions on the chart (`setPositions`, or the execution adapter's). */\r\n getPositions(): TradingPosition[] {\r\n return this.tradingManager.getPositions();\r\n }\r\n\r\n /** Fills marked on the chart, oldest first: the execution adapter's, or `addFill`'s. */\r\n getFills(): import('@tradecanvas/commons').FillEvent[] {\r\n return this.tradingManager.getFills();\r\n }\r\n\r\n /** The P&L the fills realised since the last `clearFills`, all of them (the marks keep the latest 1000). */\r\n getRealisedPnl(): number {\r\n return this.tradingManager.getRealisedPnl();\r\n }\r\n\r\n /** Mark a fill on the chart (without an execution adapter, a host reports its own). */\r\n addFill(fill: import('@tradecanvas/commons').FillEvent): void {\r\n if (!this.features.trading) return;\r\n this.tradingManager.addFill(fill);\r\n this.eventBus.emit('executionFill', fill);\r\n }\r\n\r\n clearFills(): void {\r\n this.tradingManager.setFills([]);\r\n }\r\n\r\n /**\r\n * Ask for an order to be cancelled: emits `orderCancel`, which an execution\r\n * adapter carries out (as the × on the order's line does).\r\n */\r\n cancelOrderIntent(orderId: string): void {\r\n if (this.features.trading) this.eventBus.emit('orderCancel', { orderId });\r\n }\r\n\r\n /** Ask for a position to be closed: emits `positionClose`. */\r\n closePositionIntent(positionId: string): void {\r\n if (this.features.trading) this.eventBus.emit('positionClose', { positionId });\r\n }\r\n\r\n /** Ask for a position to be reversed (closed, then the same size the other way): emits `positionReverse`. */\r\n reversePositionIntent(positionId: string): void {\r\n if (this.features.trading) this.eventBus.emit('positionReverse', { positionId });\r\n }\r\n\r\n /** Ask for a position's stop-loss or take-profit to change (null removes it): emits `positionModify`. */\r\n modifyPositionIntent(intent: import('@tradecanvas/commons').PositionModifyIntent): void {\r\n if (this.features.trading) this.eventBus.emit('positionModify', intent);\r\n }\r\n\r\n setDepthData(depth: DepthData | null): void {\r\n if (!this.features.trading) return;\r\n this.tradingManager.setDepthData(depth);\r\n }\r\n\r\n setCurrentPrice(price: number, _pulseColor?: string): void {\r\n this.followLivePrice(price);\r\n if (this.replaySession) {\r\n // During a replay the price line shows the replayed close; the live\r\n // price comes back with replayStop(). Orders and alerts stay live.\r\n this.replaySession.price = { price };\r\n } else {\r\n // Also update standalone price line (visible even without trading feature)\r\n this.currentPriceLine.setPrice(price);\r\n this.scheduleRender();\r\n }\r\n }\r\n\r\n /** Whether a paper account trades on the replay (it takes a mark price) rather than on the live price. */\r\n private replayTrades(): boolean {\r\n return this.replaySession !== null && this.executionAdapter?.setMarkPrice !== undefined;\r\n }\r\n\r\n /** The orders and the alerts follow a live price (a paper account in a replay follows the replay). */\r\n private followLivePrice(price: number): void {\r\n if (!this.replayTrades()) this.tradingManager.setCurrentPrice(price);\r\n if (this.features.alerts) this.checkAlerts(price);\r\n }\r\n\r\n /**\r\n * Alerts against a live price, and against the indicator lines they watch,\r\n * all as of the same moment (a line crossing another compares like with\r\n * like). During a replay they keep watching the live market: the bars are\r\n * the live ones, and indicator lines — computed on the replayed bars — wait.\r\n */\r\n private checkAlerts(price: number): void {\r\n const live = this.replaySession ? this.replaySession.live.getData() : this.dataManager.getData();\r\n this.alertManager.setBarTime(live.length > 0 ? live[live.length - 1].time : null);\r\n const values = new Map<string, number>([['price', price]]);\r\n if (!this.replaySession) this.collectIndicatorValues(values);\r\n // A line against a line only with both of this moment (not during a replay).\r\n this.alertManager.checkChannels(values, { together: true });\r\n this.alertManager.checkDrawings(price);\r\n }\r\n\r\n /**\r\n * The latest values of the indicator lines alerts watch or compare with.\r\n * Channel format: `<instanceId>:<key>`; instanceIds are `tc_<id>_<n>` and\r\n * built-in keys are colon-free, so the first colon splits them. Values come\r\n * from the recalculated series, so indicator alerts evaluate at bar cadence\r\n * (the live intrabar tick doesn't repaint closed-bar indicator values).\r\n * Cheap — only walks alerts whose channel isn't `'price'`, usually none.\r\n */\r\n private collectIndicatorValues(into: Map<string, number>): void {\r\n const data = this.dataManager.getData();\r\n if (data.length === 0) return;\r\n const lastIdx = data.length - 1;\r\n const channels = this.alertManager.getAlerts().flatMap((a) => (a.target ? [a.channel, a.target] : [a.channel]));\r\n for (const channel of channels) {\r\n if (into.has(channel)) continue;\r\n const sep = channel.indexOf(':');\r\n if (sep < 0) continue;\r\n const point = this.indicatorEngine.getOutput(channel.slice(0, sep))?.series?.[lastIdx];\r\n const value = point?.[channel.slice(sep + 1)];\r\n if (typeof value === 'number' && Number.isFinite(value)) into.set(channel, value);\r\n }\r\n }\r\n\r\n setTradingConfig(config: Partial<TradingConfig>): void {\r\n if (!this.features.trading) return;\r\n this.tradingManager.setConfig(config);\r\n }\r\n\r\n // --- Real-time Streaming ---\r\n\r\n /**\r\n * Connect to a real-time data source.\r\n * Loads history, starts streaming, manages reconnection automatically.\r\n *\r\n * @example\r\n * import { BinanceAdapter } from '@tradecanvas/core';\r\n * chart.connect({\r\n * adapter: new BinanceAdapter(),\r\n * symbol: 'BTCUSDT',\r\n * timeframe: '1m',\r\n * });\r\n */\r\n async connect(config: StreamConfig): Promise<void> {\r\n if (!servesTimeframe(config.adapter, config.timeframe)) {\r\n throw new RangeError(`${config.adapter.name} cannot serve the ${config.timeframe} timeframe`);\r\n }\r\n this.snapshotKey = null;\r\n // Back to the live series first, so a failed history load can't leave\r\n // the replayed slice on screen.\r\n this.replayStop();\r\n this.disconnectStream();\r\n\r\n this.streamManager = new StreamManager();\r\n\r\n this.streamManager.on('snapshot', (bars) => {\r\n const key = `${this.currentSymbol}|${this.streamTimeframe}`;\r\n if (this.replaySession) {\r\n this.replaySession.live.setData(bars);\r\n } else if (key === this.snapshotKey && this.dataManager.getLength() > 0) {\r\n this.mergeSnapshot(bars); // a reconnect: keep the paged-in history and the view\r\n } else {\r\n this.setData(bars);\r\n }\r\n this.snapshotKey = key;\r\n this.useStreamHistory(config.adapter, config.historyPageSize);\r\n });\r\n\r\n // Each bar lands by its time: the last bar again (its latest values, a\r\n // close included), a new one after it, or none when it is older.\r\n this.streamManager.on('barClose', (bar) => {\r\n if (this.replaySession) {\r\n this.replaySession.live.appendBar(bar);\r\n return;\r\n }\r\n const how = barPlacement(this.fullSeries ?? this.dataManager.getData(), bar);\r\n if (how === 'stale' || !this.takeBar(bar, how)) return;\r\n const follow = this.autoScrollOnNewBar && this.viewport.isAtEnd();\r\n if (!this.placeStreamBar(bar)) return;\r\n const data = this.dataManager.getData();\r\n this.crosshairHandler.setData(data);\r\n this.displayDataCache = null;\r\n this.recalcIndicatorsFrom(data, data.length - 2);\r\n this.updateViewportAndRender(follow);\r\n });\r\n\r\n this.streamManager.on('barUpdate', (bar) => {\r\n if (this.replaySession) {\r\n this.replaySession.live.updateLastBar(bar);\r\n return;\r\n }\r\n this.currentPriceLine.setPrice(bar.close);\r\n const how = barPlacement(this.fullSeries ?? this.dataManager.getData(), bar);\r\n if (how === 'stale' || !this.takeBar(bar, how) || !this.placeStreamBar(bar)) {\r\n this.scheduleRender();\r\n return;\r\n }\r\n // Recalculate indicators so panel/overlay series track the forming bar\r\n // instead of freezing until bar close — incrementally, since only the\r\n // last bar changed.\r\n const data = this.dataManager.getData();\r\n this.recalcIndicatorsFrom(data, data.length - 1);\r\n this.scheduleRender();\r\n });\r\n\r\n this.setBidAsk(null); // the last symbol's quote\r\n // A tick may carry one side only: the other stays.\r\n this.streamManager.on('quote', (quote) => {\r\n const now = this.priceLines.getBidAsk();\r\n this.setBidAsk({ bid: quote.bid ?? now?.bid, ask: quote.ask ?? now?.ask });\r\n });\r\n\r\n this.streamManager.on('priceChange', ({ price, previousClose }) => {\r\n // Orders and alerts keep tracking the live market during a replay; the\r\n // price line shows the replayed close instead.\r\n this.followLivePrice(price);\r\n if (this.replaySession) {\r\n this.replaySession.price = { price, previousClose: previousClose ?? undefined };\r\n return;\r\n }\r\n this.currentPriceLine.setPrice(price, previousClose ?? undefined);\r\n this.engine.requestRender(LayerType.Overlay);\r\n });\r\n\r\n this.streamManager.on('connectionChange', (info) => {\r\n this.eventBus.emit('dataUpdate', { connection: info });\r\n });\r\n\r\n this.streamManager.on('error', (err) => {\r\n this.eventBus.emit('dataUpdate', { error: err.message });\r\n });\r\n\r\n this.autoScrollOnNewBar = config.autoScroll !== false;\r\n this.setStreamTarget(config.symbol, config.timeframe);\r\n this.streamAdapter = config.adapter;\r\n\r\n const manager = this.streamManager;\r\n this.resolveStreamSymbol(config.adapter, config.symbol);\r\n await manager.connect(config);\r\n // Superseded by a newer connect() (fast symbol/timeframe switching) or a\r\n // disconnect while history was loading — leave the countdown to it.\r\n if (this.streamManager !== manager) return;\r\n\r\n // Set up bar countdown timer based on timeframe\r\n const tfMs = timeframeToMs(config.timeframe);\r\n this.barCountdown.setTimeframeMs(tfMs);\r\n\r\n // Start countdown refresh interval\r\n if (this.countdownInterval) clearInterval(this.countdownInterval);\r\n this.countdownInterval = setInterval(() => {\r\n if (this.barCountdown.isVisible()) {\r\n this.engine.requestRender(LayerType.Hover);\r\n }\r\n }, 1000);\r\n }\r\n\r\n /** The stream's symbol and timeframe, told when they change. */\r\n private setStreamTarget(symbol: string, timeframe: TimeFrame): void {\r\n const previousSymbol = this.announcedSymbol;\r\n const previousTimeframe = this.announcedTimeframe;\r\n this.currentSymbol = symbol;\r\n this.streamTimeframe = timeframe;\r\n if (symbol !== previousSymbol) {\r\n this.announcedSymbol = symbol;\r\n this.eventBus.emit('symbolChange', { symbol, previous: previousSymbol });\r\n }\r\n if (timeframe !== previousTimeframe) {\r\n this.announcedTimeframe = timeframe;\r\n this.eventBus.emit('timeframeChange', { timeframe, previous: previousTimeframe });\r\n }\r\n }\r\n\r\n /**\r\n * Switch symbol or timeframe on an active stream without full reconnect.\r\n */\r\n async switchStream(symbol: string, timeframe: TimeFrame): Promise<void> {\r\n if (!this.streamManager) return;\r\n const adapter = this.streamAdapter;\r\n if (adapter && !servesTimeframe(adapter, timeframe)) {\r\n throw new RangeError(`${adapter.name} cannot serve the ${timeframe} timeframe`);\r\n }\r\n this.setStreamTarget(symbol, timeframe);\r\n // Until the new series is in, a page would be the new symbol's bars in\r\n // front of the old one's.\r\n if (this.historyFromStream) this.history.setLoader(null);\r\n this.barCountdown.setTimeframeMs(timeframeToMs(timeframe));\r\n if (adapter) this.resolveStreamSymbol(adapter, symbol);\r\n await this.streamManager.switchTo(symbol, timeframe);\r\n }\r\n\r\n /** False when `features.timeframes` is set and does not list `timeframe`. */\r\n isTimeframeAllowed(timeframe: TimeFrame): boolean {\r\n const allowed = this.features.timeframes;\r\n return allowed.length === 0 || allowed.includes(timeframe);\r\n }\r\n\r\n /**\r\n * Switch to a new timeframe. Requires an active stream connection.\r\n * Ignored when `features.timeframes` does not list it.\r\n * Internally calls switchStream with the current symbol.\r\n */\r\n async setTimeframe(timeframe: TimeFrame): Promise<void> {\r\n if (!this.streamManager) throw new Error('No active stream. Call connect() first.');\r\n if (!this.isTimeframeAllowed(timeframe)) return;\r\n await this.switchStream(this.currentSymbol, timeframe);\r\n }\r\n\r\n /**\r\n * Disconnect the real-time stream.\r\n */\r\n disconnectStream(): void {\r\n this.symbolInfoSeq++; // a lookup still on its way no longer applies\r\n this.priceLines.setBidAsk(null);\r\n if (this.streamManager) {\r\n this.streamManager.dispose();\r\n this.streamManager = null;\r\n }\r\n if (this.historyFromStream) {\r\n this.history.setLoader(null);\r\n this.historyFromStream = false;\r\n }\r\n this.streamAdapter = null;\r\n }\r\n\r\n /**\r\n * Connect an execution adapter, turning the trading overlay into a live\r\n * trading surface. The adapter is the source of truth: the chart routes its\r\n * emitted order/position intents into the adapter and renders the\r\n * `orders` / `positions` the adapter emits back. With no adapter connected,\r\n * those intents remain plain events (the default). Listen for failures via\r\n * `chart.on('executionError', …)`.\r\n */\r\n connectExecution(adapter: ExecutionAdapter, config: ExecutionConfig = {}): void {\r\n if (!this.features.trading) {\r\n throw new Error('connectExecution requires features.trading to be enabled');\r\n }\r\n this.disconnectExecution();\r\n this.executionAdapter = adapter;\r\n this.execTeardown = wireExecution(adapter, {\r\n onIntent: (type, handler) => {\r\n const wrapped = (e: ChartEvent) => handler(e.payload);\r\n this.eventBus.on(type, wrapped);\r\n return () => this.eventBus.off(type, wrapped);\r\n },\r\n setOrders: (orders) => this.setOrders(orders),\r\n setPositions: (positions) => this.setPositions(positions),\r\n onError: (error) => this.eventBus.emit('executionError', error),\r\n onFill: (fill) => {\r\n this.tradingManager.addFill(fill);\r\n this.eventBus.emit('executionFill', fill);\r\n },\r\n getPositions: () => this.tradingManager.getPositions(),\r\n });\r\n Promise.resolve(adapter.connect(config)).catch((cause) =>\r\n this.eventBus.emit('executionError', { message: 'Execution connect failed', cause }),\r\n );\r\n }\r\n\r\n /** Tear down the active execution adapter (if any) and stop routing intents. */\r\n disconnectExecution(): void {\r\n this.returnMarkToLive();\r\n if (this.execTeardown) {\r\n this.execTeardown();\r\n this.execTeardown = null;\r\n }\r\n if (this.executionAdapter) {\r\n this.executionAdapter.disconnect();\r\n this.executionAdapter = null;\r\n }\r\n }\r\n\r\n /** The connected execution adapter, or null (e.g. for adapter-specific calls). */\r\n getExecutionAdapter(): ExecutionAdapter | null {\r\n return this.executionAdapter;\r\n }\r\n\r\n /** Ignored (stays hidden) when `features.barCountdown` is false. */\r\n setBarCountdownVisible(visible: boolean): void {\r\n this.barCountdown.setVisible(visible && this.features.barCountdown);\r\n this.engine.requestRender(LayerType.Hover);\r\n }\r\n\r\n setSessionBreaksVisible(visible: boolean): void {\r\n this.sessionBreaks.setVisible(visible);\r\n this.engine.requestRender(LayerType.Background);\r\n }\r\n\r\n // --- Compare symbols ---\r\n\r\n /** Does nothing when `features.compareSymbols` is false. */\r\n addCompareSymbol(id: string, label: string, data: DataSeries, color: string): void {\r\n if (!this.features.compareSymbols) return;\r\n this.compareRenderer.addSymbol({ id, label, data, color, visible: true });\r\n // They count in the auto scale.\r\n this.updateViewportAndRender();\r\n }\r\n\r\n removeCompareSymbol(id: string): void {\r\n this.compareRenderer.removeSymbol(id);\r\n this.updateViewportAndRender();\r\n }\r\n\r\n updateCompareData(id: string, data: DataSeries): void {\r\n this.compareRenderer.setSymbolData(id, data);\r\n this.updateViewportAndRender();\r\n }\r\n\r\n setCompareMode(mode: 'percent' | 'absolute'): void {\r\n this.compareRenderer.setMode(mode);\r\n this.updateViewportAndRender();\r\n }\r\n\r\n clearCompareSymbols(): void {\r\n this.compareRenderer.clear();\r\n this.updateViewportAndRender();\r\n }\r\n\r\n // --- Price scale ---\r\n\r\n /** Turning it on is ignored when `features.logScale` is false. */\r\n setLogScale(enabled: boolean): void {\r\n if (enabled && !this.features.logScale) return;\r\n this.viewport.setLogScale(enabled);\r\n this.updateViewportAndRender();\r\n }\r\n\r\n isLogScale(): boolean {\r\n return this.viewport.isLogScale();\r\n }\r\n\r\n /** Turn the price scale upside down: higher prices lower on screen. */\r\n setInvertScale(inverted: boolean): void {\r\n this.viewport.setInvertScale(inverted);\r\n this.updateViewportAndRender();\r\n }\r\n\r\n isInvertScale(): boolean {\r\n return this.viewport.isInvertScale();\r\n }\r\n\r\n /**\r\n * Set the price-scale presentation: `regular`, `logarithmic`, `percentage`\r\n * (axis labels show % change from the first visible bar), or `indexedTo100`\r\n * (rebased so the first visible bar reads as 100).\r\n */\r\n setScaleMode(mode: import('@tradecanvas/commons').PriceScaleMode): void {\r\n if (mode === 'logarithmic' && !this.features.logScale) return;\r\n this.viewport.setScaleMode(mode);\r\n this.updateViewportAndRender();\r\n }\r\n\r\n getScaleMode(): import('@tradecanvas/commons').PriceScaleMode {\r\n return this.viewport.getScaleMode();\r\n }\r\n\r\n setSessionBreaksConfig(config: { color?: string; lineStyle?: 'solid' | 'dashed' | 'dotted'; lineWidth?: number }): void {\r\n this.sessionBreaks.setConfig(config);\r\n this.engine.requestRender(LayerType.Background);\r\n }\r\n\r\n getConnectionState(): ConnectionState {\r\n return this.streamManager?.getConnectionState() ?? 'disconnected';\r\n }\r\n\r\n getConnectionInfo(): ConnectionInfo {\r\n return this.streamManager?.getConnectionInfo() ?? { state: 'disconnected' };\r\n }\r\n\r\n setAutoScroll(enabled: boolean): void {\r\n this.autoScrollOnNewBar = enabled;\r\n }\r\n\r\n // --- Viewport ---\r\n\r\n scrollTo(timestamp: number): void {\r\n const data = this.dataManager.getData();\r\n for (let i = 0; i < data.length; i++) {\r\n if (data[i].time >= timestamp) {\r\n const barUnit = this.viewport.getState().barWidth + this.viewport.getState().barSpacing;\r\n this.viewport.scrollBy(i * barUnit - this.viewport.getState().offset);\r\n this.updateViewportAndRender();\r\n return;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Centre the bar that contains `time` — in the bars' own unit, ms or s —\r\n * (the first or last bar when `time` lies outside the data), keeping the zoom. Returns that bar's index, or -1\r\n * without data.\r\n */\r\n goToTime(time: number): number {\r\n const data = this.getDisplayData(); // the series on screen (renko bricks, kagi lines…)\r\n if (data.length === 0 || !Number.isFinite(time)) return -1;\r\n const index = Math.max(0, Math.min(data.length - 1, Math.floor(timestampToBarIndex(time, data))));\r\n const vs = this.viewport.getState();\r\n const centredOffset = index * (vs.barWidth + vs.barSpacing) + vs.barWidth / 2 - vs.chartRect.width / 2;\r\n this.viewport.scrollBy(centredOffset - vs.offset);\r\n this.updateViewportAndRender();\r\n return index;\r\n }\r\n\r\n /**\r\n * Show a span that ends at the last bar: 1D, 5D, 1M, 3M, 6M, YTD, 1Y, 5Y or\r\n * All. Months and YTD follow the calendar in the display timezone. Only\r\n * loaded bars can be shown, so a span longer than the data shows all of it.\r\n */\r\n setVisibleRangePreset(preset: RangePreset): void {\r\n const data = this.getDisplayData();\r\n if (data.length === 0) return;\r\n const last = data.length - 1;\r\n // Bars may carry seconds rather than milliseconds; the calendar works in ms.\r\n const unit = data[last].time > SECONDS_TIME_LIMIT ? 1 : 1000;\r\n const startMs = rangePresetStart(preset, data[last].time * unit, this.displayTz);\r\n const start = startMs === null ? null : startMs / unit;\r\n if (start === null || start < data[0].time) {\r\n this.fitContent();\r\n return;\r\n }\r\n // The first bar after `start`, so \"1D\" on 1-minute bars is 1440 bars, not\r\n // 1441; YTD starts on the year's first bar, the one at 1 January 00:00.\r\n const at = timestampToBarIndex(start, data);\r\n const first = Math.min(last, preset === 'YTD' ? Math.ceil(at) : Math.floor(at) + 1);\r\n this.viewport.zoomToBarRange(first, last + this.viewport.getRightMargin());\r\n this.viewport.scrollToEnd();\r\n this.updateViewportAndRender();\r\n }\r\n\r\n scrollToEnd(): void {\r\n this.viewport.scrollToEnd();\r\n this.updateViewportAndRender();\r\n }\r\n\r\n /** Scroll by `bars` bars: later for a positive count, earlier for a negative one. */\r\n scrollBars(bars: number): void {\r\n if (!Number.isFinite(bars) || bars === 0) return;\r\n const { barWidth, barSpacing } = this.viewport.getState();\r\n this.viewport.scrollBy(bars * (barWidth + barSpacing));\r\n this.updateViewportAndRender();\r\n }\r\n\r\n /**\r\n * Show the bars between two timestamps edge to edge. Times past either end\r\n * of the data are allowed (they map into the empty future / past).\r\n */\r\n setVisibleRange(fromTimestamp: number, toTimestamp: number): void {\r\n const data = this.dataManager.getData();\r\n if (data.length === 0) return;\r\n const a = timestampToBarIndex(Math.min(fromTimestamp, toTimestamp), data);\r\n const b = timestampToBarIndex(Math.max(fromTimestamp, toTimestamp), data);\r\n // Previously this only changed the bar width around the centre, so the\r\n // requested range was not actually what ended up on screen.\r\n this.viewport.zoomToBarRange(a, b);\r\n this.updateViewportAndRender();\r\n }\r\n\r\n /** Change free-panning bounds at runtime — see `ChartOptions.freePan` / `panLimits`. */\r\n setPanLimits(limits: { freePan?: boolean; minVisibleBars?: number }): void {\r\n this.viewport.setPanLimits(limits);\r\n this.updateViewportAndRender();\r\n }\r\n\r\n zoomIn(): void {\r\n const chartWidth = this.viewport.getState().chartRect.width;\r\n this.viewport.zoom(0.2, chartWidth / 2);\r\n this.updateViewportAndRender();\r\n }\r\n\r\n zoomOut(): void {\r\n const chartWidth = this.viewport.getState().chartRect.width;\r\n this.viewport.zoom(-0.2, chartWidth / 2);\r\n this.updateViewportAndRender();\r\n }\r\n\r\n /**\r\n * Fit all bars on screen with the usual right margin, resting at the live\r\n * edge — so new bars keep following afterwards. If they can't all fit at\r\n * the minimum bar width, the newest ones are shown.\r\n */\r\n fitContent(): void {\r\n const n = this.dataManager.getLength();\r\n if (n === 0) return;\r\n this.viewport.zoomToBarRange(0, n - 1 + this.viewport.getRightMargin());\r\n this.viewport.scrollToEnd();\r\n this.updateViewportAndRender();\r\n }\r\n\r\n // --- Events ---\r\n\r\n on<K extends ChartEventType>(type: K, handler: (event: ChartEvent<K extends keyof ChartEventMap ? ChartEventMap[K] : unknown>) => void): void {\r\n this.eventBus.on(type, handler as (event: ChartEvent) => void);\r\n }\r\n\r\n off<K extends ChartEventType>(type: K, handler: (event: ChartEvent<K extends keyof ChartEventMap ? ChartEventMap[K] : unknown>) => void): void {\r\n this.eventBus.off(type, handler as (event: ChartEvent) => void);\r\n }\r\n\r\n enableTauriBridge(options?: Partial<TauriBridgeOptions>): void {\r\n this.eventBus.enableTauriBridge({ enabled: true, ...options });\r\n }\r\n\r\n disableTauriBridge(): void {\r\n this.eventBus.disableTauriBridge();\r\n }\r\n\r\n // --- Theme ---\r\n\r\n setTheme(themeOrName: ThemeName | Theme): void {\r\n this.themeManager.setTheme(themeOrName);\r\n this.syncRenderContext();\r\n this.container.style.backgroundColor = this.themeManager.getTheme().background;\r\n this.engine.requestRender();\r\n this.eventBus.emit('themeChange', { theme: themeOrName });\r\n this.markStateChanged();\r\n }\r\n\r\n getTheme(): Theme {\r\n return this.themeManager.getTheme();\r\n }\r\n\r\n // --- Watermark ---\r\n\r\n setWatermark(text: string, opts?: { color?: string; fontSize?: number }): void {\r\n this.watermark.setConfig({ text, ...opts });\r\n this.engine.requestRender(LayerType.Background);\r\n }\r\n\r\n // --- Auto Scale ---\r\n\r\n setAutoScale(enabled: boolean): void {\r\n this.options.autoScale = enabled;\r\n // Unlike the drag-to-scale/vertical-pan gesture freeze, an explicit call\r\n // here is a deliberate preference — it should survive the next setData()\r\n // the same way a constructor-time `autoScale: false` would.\r\n this.defaultAutoScale = enabled;\r\n this.updateViewportAndRender();\r\n }\r\n\r\n isAutoScale(): boolean {\r\n return this.options.autoScale !== false;\r\n }\r\n\r\n // --- Crosshair ---\r\n\r\n setCrosshairMode(mode: 'normal' | 'magnet' | 'hidden'): void {\r\n this.crosshairHandler.setMode(mode);\r\n this.engine.requestRender(LayerType.Hover);\r\n }\r\n\r\n getCrosshairMode(): string {\r\n return this.crosshairHandler.getMode();\r\n }\r\n\r\n setCrosshairPosition(point: { x: number; y: number } | null): void {\r\n if (point) {\r\n this.crosshairHandler.onPointerMove(point);\r\n } else {\r\n this.crosshairHandler.onPointerLeave();\r\n }\r\n this.engine.requestRender(LayerType.Hover);\r\n }\r\n\r\n /**\r\n * Show a time-only crosshair — a vertical line and time label on the bar\r\n * that contains `time` — as when mirroring another chart; null clears it.\r\n * Fires no crosshair events, so linked charts don't echo each other.\r\n */\r\n setCrosshairTime(time: number | null): void {\r\n const data = this.getDisplayData();\r\n const slot = time === null || data.length === 0 ? null : Math.floor(timestampToBarIndex(time, data));\r\n if (slot === this.crosshairHandler.getSyncedSlot()) return;\r\n this.crosshairHandler.setSyncedSlot(slot);\r\n this.engine.requestRender(LayerType.Hover);\r\n }\r\n\r\n getData(): DataSeries {\r\n return this.dataManager.getData();\r\n }\r\n\r\n // --- Grid ---\r\n\r\n setGridVisible(visible: boolean): void {\r\n this.gridRenderer.setVisible(visible);\r\n this.engine.requestRender(LayerType.Background);\r\n }\r\n\r\n isGridVisible(): boolean {\r\n return this.gridRenderer.isVisible();\r\n }\r\n\r\n // --- Volume ---\r\n\r\n setVolumeVisible(visible: boolean): void {\r\n this.volumeRenderer.setVisible(visible);\r\n this.engine.requestRender(LayerType.Main);\r\n }\r\n\r\n // --- Volume Profile ---\r\n\r\n setVolumeProfileVisible(visible: boolean): void {\r\n this.volumeProfile.setVisible(visible);\r\n this.engine.requestRender(LayerType.Main);\r\n }\r\n\r\n isVolumeProfileVisible(): boolean {\r\n return this.volumeProfile.isVisible();\r\n }\r\n\r\n setVolumeProfileConfig(config: { buckets?: number; widthRatio?: number; opacity?: number; highlightPoC?: boolean }): void {\r\n if (config.buckets !== undefined) this.volumeProfile.setBuckets(config.buckets);\r\n if (config.widthRatio !== undefined) this.volumeProfile.setWidthRatio(config.widthRatio);\r\n if (config.opacity !== undefined) this.volumeProfile.setOpacity(config.opacity);\r\n if (config.highlightPoC !== undefined) this.volumeProfile.setHighlightPoC(config.highlightPoC);\r\n this.engine.requestRender(LayerType.Main);\r\n }\r\n\r\n // --- Market Profile (TPO) ---\r\n\r\n setMarketProfileVisible(visible: boolean): void {\r\n this.marketProfile.setVisible(visible);\r\n this.engine.requestRender(LayerType.Main);\r\n }\r\n\r\n isMarketProfileVisible(): boolean {\r\n return this.marketProfile.isVisible();\r\n }\r\n\r\n setMarketProfileConfig(config: { buckets?: number; widthRatio?: number; opacity?: number; valueAreaPct?: number; highlightPoC?: boolean; showStats?: boolean; splitBySession?: boolean; letters?: boolean }): void {\r\n if (config.buckets !== undefined) this.marketProfile.setBuckets(config.buckets);\r\n if (config.widthRatio !== undefined) this.marketProfile.setWidthRatio(config.widthRatio);\r\n if (config.opacity !== undefined) this.marketProfile.setOpacity(config.opacity);\r\n if (config.valueAreaPct !== undefined) this.marketProfile.setValueAreaPct(config.valueAreaPct);\r\n if (config.highlightPoC !== undefined) this.marketProfile.setHighlightPoC(config.highlightPoC);\r\n if (config.showStats !== undefined) this.marketProfile.setShowStats(config.showStats);\r\n if (config.splitBySession !== undefined) this.marketProfile.setSplitBySession(config.splitBySession);\r\n if (config.letters !== undefined) this.marketProfile.setLetters(config.letters);\r\n this.engine.requestRender(LayerType.Main);\r\n }\r\n\r\n /** POC / VAH / VAL of the currently rendered Market Profile, or null. */\r\n getMarketProfileStats(): import('@tradecanvas/core').MarketProfileStats | null {\r\n return this.marketProfile.getStats();\r\n }\r\n\r\n // --- Liquidity heatmap ---\r\n\r\n setDepthHeatmapVisible(visible: boolean): void {\r\n this.depthHeatmap.setVisible(visible);\r\n this.engine.requestRender(LayerType.Main);\r\n }\r\n\r\n isDepthHeatmapVisible(): boolean {\r\n return this.depthHeatmap.isVisible();\r\n }\r\n\r\n setDepthHeatmapConfig(config: { opacity?: number; capacity?: number }): void {\r\n if (config.opacity !== undefined) this.depthHeatmap.setOpacity(config.opacity);\r\n if (config.capacity !== undefined) this.depthHeatmap.setCapacity(config.capacity);\r\n this.engine.requestRender(LayerType.Main);\r\n }\r\n\r\n /**\r\n * Record an order-book snapshot for the liquidity heatmap, stamped at the\r\n * latest bar's time. Call alongside `setDepthData` on each book update.\r\n */\r\n pushDepthSnapshot(depth: import('@tradecanvas/commons').DepthData): void {\r\n const data = this.dataManager.getData();\r\n if (data.length === 0) return;\r\n this.depthHeatmap.push(data[data.length - 1].time, depth);\r\n if (this.depthHeatmap.isVisible()) this.engine.requestRender(LayerType.Main);\r\n }\r\n\r\n clearDepthHeatmap(): void {\r\n this.depthHeatmap.clear();\r\n this.engine.requestRender(LayerType.Main);\r\n }\r\n\r\n // --- Prior-period levels (PDH / PDL / PDC) ---\r\n\r\n setPeriodLevelsVisible(visible: boolean): void {\r\n this.periodLevels.setVisible(visible);\r\n this.engine.requestRender(LayerType.Main);\r\n }\r\n\r\n isPeriodLevelsVisible(): boolean {\r\n return this.periodLevels.isVisible();\r\n }\r\n\r\n setPeriodLevelsPeriod(period: import('@tradecanvas/core').LevelPeriod): void {\r\n this.periodLevels.setPeriod(period);\r\n this.engine.requestRender(LayerType.Main);\r\n }\r\n\r\n /**\r\n * The timezone for the time axis, crosshair, tooltip, day breaks and range\r\n * presets: an IANA zone (`'America/New_York'`, daylight saving included), a\r\n * fixed offset in minutes east of UTC (`-300`, `330`), or null for the\r\n * browser's. Throws a RangeError for a zone the browser doesn't know.\r\n */\r\n setTimezone(tz: TimeZoneSetting): void {\r\n assertTimezone(tz);\r\n this.timezoneSetting = tz;\r\n this.applyTimezone();\r\n }\r\n\r\n /** What `setTimezone` was given — `'exchange'` included. */\r\n getTimezone(): TimeZoneSetting {\r\n return this.timezoneSetting;\r\n }\r\n\r\n /** The zone the chart shows: `'exchange'` resolved to the symbol's zone (UTC when unknown). */\r\n getEffectiveTimezone(): TimeZoneSetting {\r\n return this.displayTz;\r\n }\r\n\r\n private applyTimezone(): void {\r\n const exchange = this.symbolInfo?.timezone;\r\n const tz = this.timezoneSetting === EXCHANGE_TIMEZONE\r\n ? (exchange && isValidTimeZone(exchange) ? exchange : 'UTC')\r\n : this.timezoneSetting;\r\n this.displayTz = tz;\r\n this.timeAxis.setTimezoneOffset(tz);\r\n this.crosshairHandler.setTimezoneOffset(tz);\r\n this.crosshairTooltip.setTimezoneOffset(tz);\r\n this.pinnedTooltip.setTimezone(tz);\r\n this.sessionBreaks.setTimezone(tz);\r\n this.engine.requestRender();\r\n }\r\n\r\n // --- Symbol info ---\r\n\r\n /**\r\n * What is known about the symbol on the chart — usually from the stream's\r\n * adapter (`resolveSymbol`), which a connected chart asks by itself. Its\r\n * price precision applies unless `setMarket` set one; its zone backs\r\n * `setTimezone('exchange')`; its hours feed the session shading.\r\n */\r\n setSymbolInfo(info: SymbolInfo | null): void {\r\n this.symbolInfo = info ? { ...info, sessions: info.sessions?.map((session) => ({ ...session })) } : null;\r\n if (this.marketConfig?.pricePrecision === undefined) {\r\n this.applyPricePrecision(this.symbolInfo?.pricePrecision ?? null);\r\n }\r\n const windows = (this.symbolInfo?.sessions ?? []).flatMap((session) => {\r\n const startMinute = sessionMinute(session.start);\r\n const endMinute = sessionMinute(session.end);\r\n return startMinute === null || endMinute === null ? [] : [{ startMinute, endMinute }];\r\n });\r\n const zone = this.symbolInfo?.timezone;\r\n // A symbol without hours of its own goes back to the host's.\r\n this.sessionShading.replaceConfig(windows.length > 0 && zone && isValidTimeZone(zone)\r\n ? { timeZone: zone, windows, startMinute: windows[0].startMinute, endMinute: windows[windows.length - 1].endMinute }\r\n : this.hostSessionHours);\r\n if (this.timezoneSetting === EXCHANGE_TIMEZONE) this.applyTimezone();\r\n this.reapplyExtendedHours();\r\n this.updateViewportAndRender();\r\n this.eventBus.emit('symbolInfoChange', { info: this.symbolInfo });\r\n }\r\n\r\n getSymbolInfo(): SymbolInfo | null {\r\n return this.symbolInfo;\r\n }\r\n\r\n /** Ask the stream's adapter about `symbol`, if it can tell; a late answer about another symbol is dropped. */\r\n private resolveStreamSymbol(adapter: DataAdapter, symbol: string): void {\r\n const resolve = adapter.resolveSymbol?.bind(adapter);\r\n if (!resolve) return;\r\n const seq = ++this.symbolInfoSeq;\r\n if (this.symbolInfo && this.symbolInfo.symbol !== symbol) this.setSymbolInfo(null);\r\n // Taken as a promise: an adapter may throw, or answer without one.\r\n Promise.resolve().then(() => resolve(symbol)).then(\r\n (info) => {\r\n // No answer leaves what the host set for the symbol, if anything.\r\n if (info && seq === this.symbolInfoSeq && this.currentSymbol === symbol) this.setSymbolInfo(info);\r\n },\r\n (err: unknown) => {\r\n // Only the symbol's details are missing; the feed itself is fine.\r\n if (seq === this.symbolInfoSeq) console.warn(`No symbol info for \"${symbol}\":`, err);\r\n },\r\n );\r\n }\r\n\r\n /** `setTimezone` with a fixed offset in minutes east of UTC, or null for the browser's zone. */\r\n setTimezoneOffset(minutes: number | null): void {\r\n this.setTimezone(minutes);\r\n }\r\n\r\n // --- Pivot / swing markers ---\r\n\r\n setPivotMarkersVisible(visible: boolean): void {\r\n this.pivotMarkers.setVisible(visible);\r\n this.engine.requestRender(LayerType.Overlay);\r\n }\r\n\r\n isPivotMarkersVisible(): boolean {\r\n return this.pivotMarkers.isVisible();\r\n }\r\n\r\n setPivotMarkersConfig(config: { left?: number; right?: number; showLabels?: boolean; structureLabels?: boolean }): void {\r\n if (config.left !== undefined || config.right !== undefined) {\r\n this.pivotMarkers.setStrength(config.left ?? 5, config.right ?? config.left ?? 5);\r\n }\r\n if (config.showLabels !== undefined) this.pivotMarkers.setShowLabels(config.showLabels);\r\n if (config.structureLabels !== undefined) this.pivotMarkers.setStructureLabels(config.structureLabels);\r\n this.engine.requestRender(LayerType.Overlay);\r\n }\r\n\r\n // --- Session (RTH) shading ---\r\n\r\n setSessionShadingVisible(visible: boolean): void {\r\n this.sessionShading.setVisible(visible);\r\n this.engine.requestRender(LayerType.Background);\r\n }\r\n\r\n isSessionShadingVisible(): boolean {\r\n return this.sessionShading.isVisible();\r\n }\r\n\r\n /**\r\n * Configure the regular session window (minutes-of-day + tz offset). Pass\r\n * `windows` for a split session — a market with a midday recess (e.g. SET's\r\n * 10:00–12:30 and 14:30–16:30) — so the lunch break dims like pre-/post-market.\r\n */\r\n setSessionShadingConfig(config: Partial<SessionHoursConfig>): void {\r\n // The host's hours win over the symbol's, until the next symbol brings its own.\r\n this.hostSessionHours = mergeSessionHours(this.hostSessionHours, config);\r\n this.sessionShading.replaceConfig(this.hostSessionHours);\r\n this.engine.requestRender(LayerType.Background);\r\n }\r\n\r\n // --- Tooltip ---\r\n\r\n setTooltipVisible(visible: boolean): void {\r\n if (!visible) this.crosshairTooltip.hide();\r\n }\r\n\r\n // --- Legend ---\r\n\r\n setLegend(config: Partial<import('@tradecanvas/core').LegendConfig>): void {\r\n this.chartLegend.setConfig(config);\r\n this.engine.requestRender(LayerType.Hover);\r\n }\r\n\r\n setSymbolName(symbol: string): void {\r\n this.chartLegend.setSymbol(symbol);\r\n this.engine.requestRender(LayerType.Hover);\r\n }\r\n\r\n /**\r\n * Set a status text displayed in the chart legend area (e.g., \"LIVE . 8ms\").\r\n * Pass null to clear.\r\n */\r\n setStatusText(text: string | null): void {\r\n this.chartLegend.setStatusText(text);\r\n this.engine.requestRender(LayerType.Hover);\r\n }\r\n\r\n // --- Screenshot ---\r\n\r\n screenshot(filename?: string): void {\r\n if (!this.features.screenshot) return;\r\n Screenshot.download(this.container, filename, this.themeManager.getTheme().background);\r\n }\r\n\r\n /** Copy the chart image to the clipboard. Returns false if unsupported/blocked. */\r\n async copyScreenshot(): Promise<boolean> {\r\n if (!this.features.screenshot) return false;\r\n try {\r\n const blob = await Screenshot.toBlob(this.container, this.themeManager.getTheme().background);\r\n const ClipboardItemCtor = (globalThis as { ClipboardItem?: typeof ClipboardItem }).ClipboardItem;\r\n if (!blob || !navigator.clipboard?.write || !ClipboardItemCtor) return false;\r\n await navigator.clipboard.write([new ClipboardItemCtor({ 'image/png': blob })]);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n screenshotDataURL(): string | null {\r\n if (!this.features.screenshot) return null;\r\n return Screenshot.toDataURL(this.container, this.themeManager.getTheme().background);\r\n }\r\n\r\n async screenshotBlob(): Promise<Blob | null> {\r\n if (!this.features.screenshot) return null;\r\n return Screenshot.toBlob(this.container, this.themeManager.getTheme().background);\r\n }\r\n\r\n // --- Alerts ---\r\n\r\n /**\r\n * Add an alert on `channel` (`'price'`, or an indicator line\r\n * `'<instanceId>:<key>'`) at `price`. `options` compare with another line\r\n * instead (`target`), measure a move (`movesUp` / `movesDown` with\r\n * `percent` and `bars`), look only at closed bars (`onBarClose`), or end it\r\n * (`expiresAt`). Throws a RangeError for options that don't fit together.\r\n */\r\n addAlert(\r\n price: number,\r\n condition: import('@tradecanvas/core').AlertCondition = 'crossing',\r\n message?: string,\r\n channel = 'price',\r\n label?: string,\r\n options: import('@tradecanvas/core').AlertOptions = {},\r\n ): string | null {\r\n if (!this.features.alerts) return null;\r\n const id = this.alertManager.addAlert(price, condition, message, false, channel, label, options);\r\n this.markStateChanged();\r\n return id;\r\n }\r\n\r\n removeAlert(id: string): void {\r\n this.alertManager.removeAlert(id);\r\n this.markStateChanged();\r\n }\r\n\r\n // --- Drawing order and groups ---\r\n\r\n /** Ids of the selected drawings, the one clicked last first. */\r\n getSelectedDrawingIds(): string[] {\r\n return this.drawingManager.getSelectedDrawingIds();\r\n }\r\n\r\n /** Draw a drawing on top of the others, under them, or one step up or down. False when already there. */\r\n moveDrawing(id: string, to: DrawingOrderMove): boolean {\r\n const moved = this.drawingManager.moveDrawing(id, to);\r\n if (moved) this.markStateChanged();\r\n return moved;\r\n }\r\n\r\n /** Group drawings (two or more): they are then selected, hidden and locked together. Returns the group id. */\r\n groupDrawings(ids: readonly string[], name?: string): string | null {\r\n const group = this.drawingManager.groupDrawings(ids, name);\r\n if (group) this.markStateChanged();\r\n return group;\r\n }\r\n\r\n ungroupDrawings(groupId: string): boolean {\r\n return this.afterDrawingChange(this.drawingManager.ungroup(groupId));\r\n }\r\n\r\n renameDrawingGroup(groupId: string, name: string): boolean {\r\n return this.afterDrawingChange(this.drawingManager.renameGroup(groupId, name));\r\n }\r\n\r\n setDrawingGroupVisible(groupId: string, visible: boolean): boolean {\r\n return this.afterDrawingChange(this.drawingManager.setGroupVisible(groupId, visible));\r\n }\r\n\r\n setDrawingGroupLocked(groupId: string, locked: boolean): boolean {\r\n return this.afterDrawingChange(this.drawingManager.setGroupLocked(groupId, locked));\r\n }\r\n\r\n /** Every drawing group, its drawings bottom to top. */\r\n getDrawingGroups(): { id: string; name: string; ids: string[] }[] {\r\n return this.drawingManager.getGroups();\r\n }\r\n\r\n private afterDrawingChange(changed: boolean): boolean {\r\n if (changed) this.markStateChanged();\r\n return changed;\r\n }\r\n\r\n /** Whether a drawing has lines an alert can cross (trend lines, rays, horizontals, channels). */\r\n canAddDrawingAlert(drawingId: string): boolean {\r\n return this.features.alerts && this.drawingLevelsNow(drawingId) !== null;\r\n }\r\n\r\n /**\r\n * Where a drawing's lines are at the latest bar, as drawn on the chart's\r\n * scale; null when it has none there (a trend line that ends before it).\r\n */\r\n private drawingLevelsNow(drawingId: string): number[] | null {\r\n // Alerts watch the live market, during a replay too.\r\n const data = this.replaySession ? this.replaySession.live.getData() : this.getDisplayData();\r\n if (data.length === 0) return null;\r\n const viewport = { ...this.viewport.getState(), data };\r\n return this.drawingManager.priceAt(drawingId, data[data.length - 1].time, viewport);\r\n }\r\n\r\n /**\r\n * A drawing's alerts go with it, and come back with it when an undo\r\n * brings the drawing back.\r\n */\r\n private followDrawingAlerts(event: string, drawingId: string | undefined): void {\r\n if (!drawingId || !this.alertManager) return;\r\n if (event === 'drawingRemove') {\r\n const taken = this.alertManager.takeDrawingAlerts(drawingId);\r\n if (taken.length > 0) this.removedDrawingAlerts.set(drawingId, taken);\r\n } else if (event === 'drawingCreate') {\r\n const back = this.removedDrawingAlerts.get(drawingId);\r\n if (!back) return;\r\n this.removedDrawingAlerts.delete(drawingId);\r\n this.alertManager.restoreAlerts(back);\r\n }\r\n }\r\n\r\n /** Remove alerts on drawings no longer on the chart (drawings replaced or cleared). */\r\n private dropOrphanDrawingAlerts(): void {\r\n this.removedDrawingAlerts.clear();\r\n if (!this.alertManager) return;\r\n const removed = this.alertManager.pruneDrawingAlerts(new Set(this.drawingManager.getDrawings().map((d) => d.id)));\r\n if (removed > 0) this.markStateChanged();\r\n }\r\n\r\n /**\r\n * Alert when the price crosses a drawing's line(s): a trend line where it\r\n * is at the latest bar, any line of a channel. Removed with the drawing.\r\n * Null when the drawing has no line to cross.\r\n */\r\n addDrawingAlert(\r\n drawingId: string,\r\n options: { condition?: 'crossing' | 'crossingUp' | 'crossingDown'; message?: string; repeating?: boolean; label?: string } = {},\r\n ): string | null {\r\n if (!this.canAddDrawingAlert(drawingId)) return null;\r\n const id = this.alertManager.addDrawingAlert(drawingId, options.condition ?? 'crossing', options.message, options.repeating ?? false, options.label);\r\n this.markStateChanged();\r\n return id;\r\n }\r\n\r\n getAlerts(): import('@tradecanvas/core').PriceAlert[] {\r\n return this.alertManager.getAlerts();\r\n }\r\n\r\n clearAlerts(): void {\r\n this.alertManager.clearAlerts();\r\n this.markStateChanged();\r\n }\r\n\r\n saveAlerts(key: string): void {\r\n this.alertManager.saveToStorage(key);\r\n }\r\n\r\n /** Load alerts saved with `saveAlerts`. Alerts on drawings that aren't on the chart are dropped. */\r\n loadAlerts(key: string): void {\r\n this.alertManager.loadFromStorage(key);\r\n this.alertManager.pruneDrawingAlerts(new Set(this.drawingManager.getDrawings().map((d) => d.id)));\r\n this.engine.requestRender(LayerType.Overlay);\r\n }\r\n\r\n // --- Signal Markers ---\r\n\r\n addSignalMarker(marker: Omit<import('@tradecanvas/commons').SignalMarker, 'id'> & { id?: string }): string {\r\n return this.signalMarkerManager.addMarker(marker);\r\n }\r\n\r\n removeSignalMarker(id: string): void {\r\n this.signalMarkerManager.removeMarker(id);\r\n }\r\n\r\n getSignalMarkers(): import('@tradecanvas/commons').SignalMarker[] {\r\n return this.signalMarkerManager.getMarkers();\r\n }\r\n\r\n setSignalMarkers(markers: import('@tradecanvas/commons').SignalMarker[]): void {\r\n this.signalMarkerManager.setMarkers(markers);\r\n }\r\n\r\n clearSignalMarkers(): void {\r\n this.signalMarkerManager.clearMarkers();\r\n }\r\n\r\n setSignalMarkerStyle(style: Partial<import('@tradecanvas/commons').SignalMarkerStyle>): void {\r\n this.signalMarkerManager.setStyle(style);\r\n }\r\n\r\n // --- Trade Zones ---\r\n\r\n addTradeZone(zone: Omit<import('@tradecanvas/commons').TradeZone, 'id'> & { id?: string }): string {\r\n return this.tradeZoneManager.addZone(zone);\r\n }\r\n\r\n updateTradeZone(id: string, updates: Partial<Omit<import('@tradecanvas/commons').TradeZone, 'id'>>): void {\r\n this.tradeZoneManager.updateZone(id, updates);\r\n }\r\n\r\n removeTradeZone(id: string): void {\r\n this.tradeZoneManager.removeZone(id);\r\n }\r\n\r\n getTradeZones(): import('@tradecanvas/commons').TradeZone[] {\r\n return this.tradeZoneManager.getZones();\r\n }\r\n\r\n setTradeZones(zones: import('@tradecanvas/commons').TradeZone[]): void {\r\n this.tradeZoneManager.setZones(zones);\r\n }\r\n\r\n clearTradeZones(): void {\r\n this.tradeZoneManager.clearZones();\r\n }\r\n\r\n setTradeZoneStyle(style: Partial<import('@tradecanvas/commons').TradeZoneStyle>): void {\r\n this.tradeZoneManager.setStyle(style);\r\n }\r\n\r\n // --- Replay ---\r\n\r\n /**\r\n * Replay the series bar by bar. While it runs the chart is detached from\r\n * live data: stream ticks and `appendBar`/`updateLastBar` calls are kept\r\n * aside (orders still track the live price), the price line shows the\r\n * replayed close, and auto-scale refits every step. `replayStop()` brings\r\n * back the live series, including everything that arrived meanwhile.\r\n *\r\n * With `steps` (finer bars), each step grows the forming bar from them.\r\n * A connected execution adapter that takes a mark price (a paper one)\r\n * trades on the replayed price and time.\r\n */\r\n replayStart(config?: Partial<import('@tradecanvas/core').ReplayConfig>): void {\r\n if (!this.features.replay) return;\r\n this.history.reset();\r\n if (!this.replaySession) {\r\n if (this.dataManager.getLength() === 0) return; // nothing to replay\r\n const live = new DataManager();\r\n live.setData(this.fullSeries ?? this.dataManager.getData());\r\n this.replaySession = { live, price: null, fedTime: null };\r\n }\r\n // Replay the live series as it stands now (a restart includes bars that\r\n // arrived during the previous run).\r\n const coarse = this.shownBars(this.replaySession.live.getData());\r\n const { steps: rawSteps, ...playConfig } = config ?? {};\r\n const steps = rawSteps ? replaySteps(rawSteps, coarse) : null;\r\n this.replayStepsSeries = steps;\r\n this.replayCoarse = coarse;\r\n // A replay fits each step; a price-axis drag from before shouldn't pin it.\r\n this.options.autoScale = true;\r\n // Loading stops the run before: that is a restart, not the replay ending.\r\n this.replayRestarting = true;\r\n try {\r\n this.replayManager.load(steps ?? coarse);\r\n } finally {\r\n this.replayRestarting = false;\r\n }\r\n if (steps && playConfig.startIndex !== undefined) playConfig.startIndex = lastStepOfBar(steps, coarse, playConfig.startIndex);\r\n // Each replayStart used to stack another 'bar' listener, so a restarted\r\n // replay ran every step once per previous start.\r\n this.replayBarUnsub?.();\r\n let loaded = -1; // bars of `coarse` shown, as of the last step (the forming one included)\r\n let lastStep = -1;\r\n let forming: OHLCBar | null = null;\r\n const series = steps ?? coarse;\r\n this.replayBarUnsub = this.replayManager.on('bar', ({ index }) => {\r\n // Which bar of the chart's series forms now, and how it looks.\r\n let barIndex = index;\r\n let shownBar: OHLCBar = coarse[index];\r\n if (steps) {\r\n const step = steps[index];\r\n barIndex = barAt(coarse, step.time);\r\n const sameBar = forming !== null && lastStep === index - 1 && barIndex === loaded - 1;\r\n forming = sameBar && forming ? mergeBar(forming, step) : formingBar(coarse[barIndex], steps, index);\r\n shownBar = forming;\r\n }\r\n lastStep = index;\r\n const nextLen = barIndex + 1;\r\n const forward = loaded > 0 && nextLen >= loaded && this.dataManager.getLength() === loaded;\r\n // Like live data: a replay step follows the newest bar while the view\r\n // rests at the end, and leaves it alone while the user looks at history.\r\n // The first step and any seek (back or forward) show the replay\r\n // position. (Read before the new bars land: `isAtEnd` compares against\r\n // the old length.)\r\n const stepForward = forward && !this.replaySeeking;\r\n const follow = !stepForward || (this.autoScrollOnNewBar && this.viewport.isAtEnd());\r\n if (forward) {\r\n // Forward: the bar that was forming closes as it is in the series, the\r\n // newly revealed bars are appended, and indicators update from there.\r\n if (steps && nextLen > loaded) this.dataManager.updateLastBar(coarse[loaded - 1]);\r\n for (let j = loaded; j < nextLen - 1; j++) this.dataManager.appendBar(coarse[j]);\r\n if (nextLen > loaded) this.dataManager.appendBar(shownBar);\r\n else this.dataManager.updateLastBar(shownBar);\r\n this.recalcIndicatorsFrom(this.dataManager.getData(), Math.max(0, loaded - 1));\r\n } else {\r\n // First step, or a seek: reload the prefix.\r\n this.dataManager.setData([...coarse.slice(0, barIndex), shownBar]);\r\n this.recalcIndicators(this.dataManager.getData());\r\n }\r\n loaded = nextLen;\r\n // The price line follows the replay, not the live market.\r\n this.currentPriceLine.setPrice(shownBar.close, barIndex > 0 ? coarse[barIndex - 1].close : undefined);\r\n this.crosshairHandler.setData(this.dataManager.getData());\r\n // The display cache isn't keyed to the data array — without this the\r\n // chart kept drawing the pre-replay series.\r\n this.displayDataCache = null;\r\n this.updateViewportAndRender(follow);\r\n this.feedReplaySteps(series, index);\r\n // Up to when it has shown: the next step's time (past the last one, its spacing on).\r\n const next = series[index + 1]?.time;\r\n const until = next ?? series[index].time + (index > 0 ? series[index].time - series[index - 1].time : 0);\r\n this.eventBus.emit('replayStep', { barIndex, time: coarse[barIndex].time, until });\r\n });\r\n this.replayManager.play(playConfig);\r\n }\r\n\r\n /** Jump the replay to show the bars (or finer steps) that opened before `time`. */\r\n replaySeekToTime(time: number): void {\r\n if (!this.replaySession || !this.replayCoarse) return;\r\n const series = this.replayStepsSeries ?? this.replayCoarse;\r\n let lo = 0;\r\n let hi = series.length - 1;\r\n while (lo < hi) {\r\n const mid = (lo + hi + 1) >> 1;\r\n if (series[mid].time < time) lo = mid;\r\n else hi = mid - 1;\r\n }\r\n this.replaySeek(lo);\r\n }\r\n\r\n /** A replay step's price to an adapter that takes a mark (a paper account), and to the orders on the chart. */\r\n private feedReplayPrice(price: number, time?: number): void {\r\n const adapter = this.executionAdapter;\r\n if (!adapter?.setMarkPrice) return;\r\n adapter.setMarkPrice(price, time);\r\n this.tradingManager.setCurrentPrice(price);\r\n }\r\n\r\n /** A paper account trading on the replay goes back to the live price (the orders too), and its fills to the clock. */\r\n private returnMarkToLive(): void {\r\n const session = this.replaySession;\r\n if (!session || !this.replayTrades()) return;\r\n const live = session.live.getData();\r\n const price = session.price?.price ?? live[live.length - 1]?.close;\r\n if (price !== undefined) this.feedReplayPrice(price);\r\n }\r\n\r\n /**\r\n * A paper account trades on the replayed prices and times, forward only:\r\n * it gets every step past the latest time it has seen (a jump ahead passes\r\n * them all, a long one in chunks), and nothing after a seek back or a\r\n * restart from an earlier bar until the replay is past that time again.\r\n */\r\n private feedReplaySteps(series: DataSeries, index: number): void {\r\n const session = this.replaySession;\r\n const adapter = this.executionAdapter;\r\n const bar = series[index];\r\n if (!session || !adapter?.setMarkPrice || !bar) return;\r\n if (session.fedTime === null) {\r\n // The start: the account's mark is where the replay begins.\r\n session.fedTime = bar.time;\r\n this.feedReplayPrice(bar.close, bar.time);\r\n return;\r\n }\r\n if (bar.time <= session.fedTime) return;\r\n const run = series.slice(firstAfter(series, session.fedTime), index + 1);\r\n session.fedTime = bar.time;\r\n const size = Math.ceil(run.length / MAX_REPLAY_CHUNKS);\r\n for (let i = 0; i < run.length; i += size) {\r\n for (const [price, time] of replayMarks(run.slice(i, i + size))) adapter.setMarkPrice(price, time);\r\n }\r\n this.tradingManager.setCurrentPrice(bar.close);\r\n }\r\n\r\n /**\r\n * The bar of the chart's series the replay is on (the forming one), or -1\r\n * without a replay.\r\n */\r\n getReplayBarIndex(): number {\r\n if (!this.replaySession) return -1;\r\n const { current } = this.replayManager.getProgress();\r\n const steps = this.replayStepsSeries;\r\n return steps && this.replayCoarse ? barAt(this.replayCoarse, steps[current]?.time ?? -Infinity) : current;\r\n }\r\n\r\n /** Jump the replay to the end of bar `barIndex` of the chart's series (a click on it). */\r\n replaySeekToBar(barIndex: number): void {\r\n const steps = this.replayStepsSeries;\r\n this.replaySeek(steps && this.replayCoarse ? lastStepOfBar(steps, this.replayCoarse, barIndex) : barIndex);\r\n }\r\n\r\n replayPause(): void { this.replayManager.pause(); }\r\n replayResume(): void { this.replayManager.resume(); }\r\n /** End the replay and return to the live series (with updates that arrived meanwhile). */\r\n replayStop(): void {\r\n const session = this.replaySession;\r\n this.endReplaySession();\r\n if (!session) return;\r\n this.loadSeries(session.live.getData(), true);\r\n if (session.price) this.currentPriceLine.setPrice(session.price.price, session.price.previousClose);\r\n }\r\n\r\n /** Whether a replay session is open (playing or paused). */\r\n isReplayActive(): boolean {\r\n return this.replaySession !== null;\r\n }\r\n\r\n /** The plot area in CSS pixels, relative to the chart container (no axes). */\r\n getPlotRect(): { x: number; y: number; width: number; height: number } {\r\n const { x, y, width, height } = this.viewport.getState().chartRect;\r\n return { x, y, width, height };\r\n }\r\n\r\n /** Stop the replay clock and drop the session without touching the data. */\r\n private endReplaySession(): void {\r\n if (!this.replaySession) return;\r\n this.returnMarkToLive();\r\n this.replaySession = null;\r\n this.replayStepsSeries = null;\r\n this.replayCoarse = null;\r\n this.replayBarUnsub?.();\r\n this.replayBarUnsub = null;\r\n this.replayManager.stop();\r\n }\r\n\r\n replaySeek(index: number): void {\r\n this.replaySeeking = true;\r\n try {\r\n this.replayManager.seekTo(index);\r\n } finally {\r\n this.replaySeeking = false;\r\n }\r\n }\r\n setReplaySpeed(speed: number): void { this.replayManager.setSpeed(speed); }\r\n getReplayState(): 'playing' | 'paused' | 'stopped' { return this.replayManager.getState(); }\r\n getReplayProgress(): { current: number; total: number; percent: number } { return this.replayManager.getProgress(); }\r\n\r\n // --- Save / Load ---\r\n\r\n /** Everything `loadState` restores: chart type, theme, drawings, indicators, alerts. */\r\n private captureSnapshot(): import('@tradecanvas/core').ChartSnapshot {\r\n return ChartStateManager.capture(\r\n {\r\n getDrawings: () => this.getDrawings(),\r\n getTheme: () => this.getTheme(),\r\n getAlerts: () => this.getAlerts(),\r\n getIndicators: () => this.getIndicatorSetup(),\r\n },\r\n { chartType: this.options.chartType, chartTypeOptions: this.getChartTypeOptions(), symbol: this.currentSymbol || undefined },\r\n );\r\n }\r\n\r\n /**\r\n * Put `list` in place of the chart's indicators (a layout, a template, an\r\n * undo), with their panes as they were: size, order, fold, maximise.\r\n * `keepIds` keeps each instance's id (an undo): the indicators still alike\r\n * are changed in place, not taken down and put back. Otherwise the ids are\r\n * new and the map says old → new.\r\n */\r\n private restoreIndicators(list: readonly SnapshotIndicator[], keepIds: boolean): Map<string, string> {\r\n // Hosts and sources first, so a pane member finds its pane and a reader its line.\r\n const ordered = dependencyOrder(list);\r\n const instanceIds = new Map<string, string>();\r\n this.unrestoredIndicators = [];\r\n const known = new Set(this.indicatorEngine.getAvailableIndicators().map((d) => d.id));\r\n const restored: [SnapshotIndicator, string][] = [];\r\n const kept = new Set<string>();\r\n\r\n if (keepIds) {\r\n // Take down only what differs in kind or place; the rest is edited in place.\r\n const wanted = new Map(ordered.map((ind) => [ind.instanceId, ind]));\r\n const current = this.getIndicatorSetup();\r\n for (const cur of current) {\r\n const want = wanted.get(cur.instanceId);\r\n if (want && sameKindAndPlace(cur, want)) kept.add(cur.instanceId);\r\n }\r\n for (const cur of current) {\r\n if (!kept.has(cur.instanceId)) this.removeIndicator(cur.instanceId);\r\n }\r\n // A reader taken down with its source comes back with it.\r\n for (const id of [...kept]) if (!this.indicatorEngine.getIndicatorConfig(id)) kept.delete(id);\r\n } else {\r\n for (const active of this.indicatorEngine.getActiveIndicators()) this.removeIndicator(active.instanceId);\r\n }\r\n\r\n // Lines read from other indicators and shared panes refer to old ids: follow them.\r\n const renamed = (params: Record<string, unknown>): Record<string, number | string | boolean> => {\r\n const out = { ...params } as Record<string, number | string | boolean>;\r\n for (const [name, value] of Object.entries(out)) {\r\n const line = parseIndicatorSource(value);\r\n const to = line && instanceIds.get(line.instanceId);\r\n if (to) out[name] = indicatorSource(to, line.key);\r\n }\r\n return out;\r\n };\r\n\r\n for (const ind of ordered) {\r\n let instanceId: string | null = null;\r\n if (kept.has(ind.instanceId)) {\r\n instanceId = ind.instanceId;\r\n const params = this.indicatorEngine.getIndicatorConfig(instanceId)?.params ?? {};\r\n const changed: Record<string, number | string | boolean> = {};\r\n for (const [name, value] of Object.entries(renamed(ind.params))) if (params[name] !== value) changed[name] = value;\r\n if (Object.keys(changed).length > 0) this.updateIndicator(instanceId, changed);\r\n } else {\r\n const position = (['top', 'bottom', 'left', 'right'] as const).find((p) => p === ind.position) ?? 'bottom';\r\n try {\r\n if (!known.has(ind.id)) throw new Error('no such indicator is registered');\r\n const pane = ind.pane ? instanceIds.get(ind.pane) : undefined;\r\n instanceId = this.addIndicatorNow(ind.id, renamed(ind.params), position, {\r\n ...(pane ? { pane } : {}),\r\n ...(ind.scale === 'left' ? { scale: 'left' as const } : {}),\r\n ...(keepIds ? { instanceId: ind.instanceId } : {}),\r\n });\r\n } catch (err) {\r\n console.warn(`Layout indicator \"${ind.id}\" kept but not shown:`, err);\r\n }\r\n }\r\n if (!instanceId) {\r\n this.unrestoredIndicators.push(ind);\r\n continue;\r\n }\r\n instanceIds.set(ind.instanceId, instanceId);\r\n restored.push([ind, instanceId]);\r\n this.restoreLooks(instanceId, ind);\r\n }\r\n this.restorePanes(restored);\r\n this.updateViewportAndRender();\r\n return instanceIds;\r\n }\r\n\r\n /** An indicator's style, visibility and levels as `ind` has them, changing only what differs. */\r\n private restoreLooks(instanceId: string, ind: SnapshotIndicator): void {\r\n const style = this.indicatorEngine.getIndicatorStyle(instanceId);\r\n if (ind.style && JSON.stringify({ ...style, ...ind.style }) !== JSON.stringify(style)) this.updateIndicatorStyle(instanceId, ind.style);\r\n const config = this.indicatorEngine.getIndicatorConfig(instanceId);\r\n if ((ind.visible ?? true) !== (config?.visible ?? true)) this.setIndicatorVisible(instanceId, ind.visible ?? true);\r\n const levels = ind.levels ?? null;\r\n if (JSON.stringify(levels) !== JSON.stringify(config?.levels ?? null)) this.setIndicatorLevels(instanceId, levels);\r\n }\r\n\r\n /** The panes as they were: size first (it opens a fold), then fold, order, maximise. */\r\n private restorePanes(restored: readonly [SnapshotIndicator, string][]): void {\r\n for (const [ind, instanceId] of restored) {\r\n if (ind.paneSize !== undefined) this.layoutManager.setPanelSize(instanceId, ind.paneSize);\r\n if (ind.paneSize !== undefined || ind.paneCollapsed) this.layoutManager.setPanelCollapsed(instanceId, !!ind.paneCollapsed);\r\n if (ind.paneSize !== undefined) this.layoutManager.setPanelScale(instanceId, { log: !!ind.paneLog, invert: !!ind.paneInvert, percent: !!ind.panePercent });\r\n }\r\n const ordered = restored\r\n .filter(([ind]) => ind.paneOrder !== undefined)\r\n .sort(([a], [b]) => (a.paneOrder ?? 0) - (b.paneOrder ?? 0))\r\n .map(([, instanceId]) => instanceId);\r\n if (ordered.length > 1) this.layoutManager.orderPanels(ordered);\r\n const maximized = restored.find(([ind]) => ind.paneMaximized);\r\n this.layoutManager.setMaximizedPanel(maximized ? maximized[1] : null);\r\n }\r\n\r\n /**\r\n * The chart's indicators as a layout or a template keeps them: inputs,\r\n * style, levels, scale and panes (shared, size, order, fold, maximise).\r\n */\r\n getIndicatorSetup(): import('@tradecanvas/core').SnapshotIndicator[] {\r\n const panels = this.layoutManager.getPanels();\r\n const maximized = this.layoutManager.getMaximizedPanel();\r\n return [\r\n ...this.indicatorEngine.getActiveIndicators().map((ind) => {\r\n const panel = panels.find((p) => p.id === ind.instanceId);\r\n const config = this.indicatorEngine.getIndicatorConfig(ind.instanceId);\r\n return {\r\n id: ind.id,\r\n instanceId: ind.instanceId,\r\n params: { ...ind.params },\r\n position: panel?.position,\r\n style: this.indicatorEngine.getIndicatorStyle(ind.instanceId) ?? undefined,\r\n visible: ind.visible,\r\n levels: config?.levels?.slice(),\r\n pane: ind.pane,\r\n scale: config?.scale,\r\n ...(panel\r\n ? {\r\n paneSize: panel.size,\r\n paneOrder: panels.indexOf(panel),\r\n ...(panel.collapsed ? { paneCollapsed: true } : {}),\r\n ...(maximized === ind.instanceId ? { paneMaximized: true } : {}),\r\n ...(panel.logScale ? { paneLog: true } : {}),\r\n ...(panel.invertScale ? { paneInvert: true } : {}),\r\n ...(panel.percentScale ? { panePercent: true } : {}),\r\n }\r\n : {}),\r\n };\r\n }),\r\n ...this.unrestoredIndicators,\r\n ];\r\n }\r\n\r\n /**\r\n * Put a set of indicators (from `getIndicatorSetup`, a template) in place\r\n * of the chart's, as one undo step.\r\n */\r\n applyIndicatorSetup(list: readonly SnapshotIndicator[]): void {\r\n if (!this.features.indicators) return;\r\n this.recordIndicators(undefined, () => {\r\n const before = new Map(this.indicatorEngine.getActiveIndicators().map((a) => [a.instanceId, a.id]));\r\n const instanceIds = this.withoutIndicatorHistory(() => this.restoreIndicators(list, false));\r\n this.followIndicatorAlerts(before, [...instanceIds.values()]);\r\n this.markStateChanged();\r\n });\r\n }\r\n\r\n /**\r\n * Alerts on the lines of indicators that went (`before`: id → kind) move to\r\n * the one new indicator of the same kind (`added`), when there is just one;\r\n * others stay as they are, as alerts on a removed indicator do.\r\n */\r\n private followIndicatorAlerts(before: ReadonlyMap<string, string>, added: readonly string[]): void {\r\n if (!this.features.alerts) return;\r\n // A channel on a replaced indicator → the one new indicator of its kind (or null: leave it).\r\n const heirOf = (channel: string): string | null => {\r\n const sep = channel.indexOf(':');\r\n if (sep <= 0) return null;\r\n const from = channel.slice(0, sep);\r\n const kind = before.get(from);\r\n if (!kind || this.indicatorEngine.getIndicatorConfig(from)) return null;\r\n const heirs = added.filter((id) => this.indicatorEngine.getIndicatorConfig(id)?.id === kind);\r\n return heirs.length === 1 ? heirs[0] + channel.slice(sep) : null;\r\n };\r\n for (const alert of this.alertManager.getAlerts()) {\r\n const channel = heirOf(alert.channel);\r\n const target = alert.target ? heirOf(alert.target) : null;\r\n if (!channel && !target) continue;\r\n const { target: _t, percent, bars, onBarClose, expiresAt } = alert;\r\n this.alertManager.removeAlert(alert.id);\r\n this.alertManager.addAlert(alert.price, alert.condition, alert.message, alert.repeating, channel ?? alert.channel, alert.label, {\r\n ...(alert.target ? { target: target ?? alert.target } : {}),\r\n ...(percent !== undefined ? { percent } : {}),\r\n ...(bars !== undefined ? { bars } : {}),\r\n ...(onBarClose ? { onBarClose } : {}),\r\n ...(expiresAt !== undefined ? { expiresAt } : {}),\r\n });\r\n }\r\n }\r\n\r\n /** Run `change` without recording undo steps for the indicators it changes. */\r\n private withoutIndicatorHistory<T>(change: () => T): T {\r\n this.indicatorHistoryDepth++;\r\n try {\r\n return change();\r\n } finally {\r\n this.indicatorHistoryDepth--;\r\n }\r\n }\r\n\r\n /**\r\n * Run an indicator change as one undo step (the indicators before and\r\n * after). Quick changes to the same indicator (a colour dragged, a period\r\n * typed) merge into one step.\r\n */\r\n private recordIndicators<T>(subject: string | undefined, change: () => T): T {\r\n if (this.indicatorHistoryDepth > 0 || !this.features.drawingUndoRedo) return change();\r\n const before = this.getIndicatorSetup();\r\n const result = this.withoutIndicatorHistory(change);\r\n const after = this.getIndicatorSetup();\r\n if (JSON.stringify(before) === JSON.stringify(after)) return result;\r\n const now = performance.now();\r\n const last = this.undoRedoManager.last();\r\n const merge = subject !== undefined && last?.type === 'indicators' && last.indicators?.subject === subject\r\n && now - (last.indicators.at ?? 0) < INDICATOR_EDIT_MERGE_MS\r\n && JSON.stringify(last.indicators.after) === JSON.stringify(before);\r\n const step = { type: 'indicators' as const, before: null, after: null };\r\n if (merge && last?.indicators) {\r\n // Back where the burst began (hidden, then shown again): no step left.\r\n if (JSON.stringify(last.indicators.before) === JSON.stringify(after)) this.undoRedoManager.dropLast();\r\n else this.undoRedoManager.replaceLast({ ...step, indicators: { ...last.indicators, after, at: now } });\r\n } else {\r\n this.undoRedoManager.push({ ...step, indicators: { before, after, subject, at: now } });\r\n }\r\n return result;\r\n }\r\n\r\n /** Undo or redo an indicator step: the indicators as they were, under their own ids. */\r\n private applyIndicatorStep(action: import('@tradecanvas/core').UndoableAction, direction: 'undo' | 'redo'): void {\r\n const step = action.indicators;\r\n if (!step) return;\r\n this.withoutIndicatorHistory(() => this.restoreIndicators(direction === 'undo' ? step.before : step.after, true));\r\n this.markStateChanged();\r\n }\r\n\r\n saveState(key?: string): string | null {\r\n if (!this.features.saveLoad) return null;\r\n const snapshot = this.captureSnapshot();\r\n const json = ChartStateManager.serialize(snapshot);\r\n if (key) ChartStateManager.saveToStorage(key, snapshot);\r\n return json;\r\n }\r\n\r\n loadState(json: string): void {\r\n if (!this.features.saveLoad) return;\r\n const snapshot = ChartStateManager.deserialize(json);\r\n // The saved type's settings (none saved: the defaults).\r\n this.chartTypeOptions = snapshot.chartTypeOptions ?? {};\r\n this.displayDataCache = null;\r\n if (snapshot.chartType) this.setChartType(snapshot.chartType);\r\n if (snapshot.drawings) this.setDrawings(snapshot.drawings);\r\n if (snapshot.theme) this.setTheme(snapshot.theme as any);\r\n\r\n // Indicators get new instance ids: remember old → new for alert channels.\r\n // Version-1 saves never captured indicators, so they leave them alone.\r\n const instanceIds = snapshot.version >= 2 && this.features.indicators\r\n ? this.withoutIndicatorHistory(() => this.restoreIndicators(snapshot.indicators, false))\r\n : new Map<string, string>();\r\n // Another layout: the old steps no longer apply to it.\r\n this.undoRedoManager.clear();\r\n\r\n if (snapshot.alerts && this.features.alerts) {\r\n this.alertManager.clearAlerts();\r\n for (const a of snapshot.alerts) {\r\n // A one-shot alert that already fired stays fired (as AlertManager's own storage does).\r\n if (a.triggered && !a.repeating) continue;\r\n // `<instanceId>:<key>` channels follow their indicator to its new id.\r\n const follow = (ch: string) => {\r\n const sep = ch.indexOf(':');\r\n const renamed = sep > 0 ? instanceIds.get(ch.slice(0, sep)) : undefined;\r\n return renamed ? renamed + ch.slice(sep) : ch;\r\n };\r\n const channel = follow(a.channel);\r\n const options = { ...readAlertOptions(a as unknown as Record<string, unknown>), ...(a.target ? { target: follow(a.target) } : {}) };\r\n if (a.expired) continue;\r\n if (a.drawingId) {\r\n if (this.drawingManager.hasPriceLevels(a.drawingId) && (a.condition === 'crossing' || a.condition === 'crossingUp' || a.condition === 'crossingDown')) {\r\n this.alertManager.addDrawingAlert(a.drawingId, a.condition, a.message, a.repeating, a.label);\r\n }\r\n continue;\r\n }\r\n try {\r\n this.alertManager.addAlert(a.price, a.condition, a.message, a.repeating, channel, a.label, options);\r\n } catch (err) {\r\n console.warn('Layout alert left out:', err);\r\n }\r\n }\r\n }\r\n this.markStateChanged();\r\n }\r\n\r\n loadStateFromStorage(key: string): boolean {\r\n if (!this.features.saveLoad) return false;\r\n const snapshot = ChartStateManager.loadFromStorage(key);\r\n if (!snapshot) return false;\r\n this.loadState(ChartStateManager.serialize(snapshot));\r\n return true;\r\n }\r\n\r\n downloadState(filename?: string): void {\r\n if (!this.features.saveLoad) return;\r\n ChartStateManager.downloadFile(this.captureSnapshot(), filename);\r\n }\r\n\r\n async loadStateFromFile(): Promise<void> {\r\n if (!this.features.saveLoad) return;\r\n const snapshot = await ChartStateManager.loadFromFile();\r\n this.loadState(ChartStateManager.serialize(snapshot));\r\n }\r\n\r\n // --- Locale ---\r\n\r\n setLocale(locale: Locale): void {\r\n setGlobalLocale(locale);\r\n this.engine.requestRender();\r\n }\r\n\r\n /**\r\n * Update the BCP 47 locale used for number formatting\r\n * (e.g. price axis labels, crosshair value badges).\r\n * Defaults to 'en-US'. Example values: 'de-DE', 'vi-VN', 'fr-FR'.\r\n */\r\n setNumberLocale(locale: string): void {\r\n this.numberLocale = locale;\r\n this.priceAxis.setLocale(locale);\r\n this.leftPriceAxis.setLocale(locale);\r\n this.crosshairHandler.setLocale(locale);\r\n this.chartLegend.setLocale(locale);\r\n this.crosshairTooltip.setLocale(locale);\r\n this.sessionBreaks.setLocale(locale);\r\n this.currentPriceLine.setLocale(locale);\r\n // Separators change label widths; the axis may need to refit.\r\n this.updateViewportAndRender();\r\n }\r\n\r\n getNumberLocale(): string {\r\n return this.numberLocale;\r\n }\r\n\r\n // --- Market ---\r\n\r\n setMarket(config: MarketConfig): void {\r\n this.marketConfig = config;\r\n\r\n // Apply market color scheme to theme\r\n if (config.colorScheme) {\r\n const base = this.themeManager.getTheme();\r\n const marketTheme: Theme = {\r\n ...base,\r\n candleUp: config.colorScheme.up,\r\n candleDown: config.colorScheme.down,\r\n candleUpWick: config.colorScheme.up,\r\n candleDownWick: config.colorScheme.down,\r\n volumeUp: config.colorScheme.up.replace(')', ', 0.3)').replace('rgb(', 'rgba(') || `${config.colorScheme.up}4D`,\r\n volumeDown: config.colorScheme.down.replace(')', ', 0.3)').replace('rgb(', 'rgba(') || `${config.colorScheme.down}4D`,\r\n };\r\n this.themeManager.setTheme(marketTheme);\r\n }\r\n\r\n // Apply price precision to trading, alerts, and price line\r\n if (config.pricePrecision !== undefined) this.applyPricePrecision(config.pricePrecision);\r\n\r\n // Longer price labels may need a wider axis.\r\n this.updateViewportAndRender();\r\n }\r\n\r\n /** Prices show `precision` decimals everywhere; null goes back to fitting the price range. */\r\n private applyPricePrecision(precision: number | null): void {\r\n this.marketPricePrecision = precision;\r\n this.tradingManager.setConfig({ pricePrecision: precision ?? undefined });\r\n this.alertManager.setPricePrecision(precision ?? DEFAULT_ORDER_PRECISION);\r\n this.streamManager?.priceLine.setPricePrecision(precision);\r\n this.currentPriceLine.setPricePrecision(precision);\r\n this.crosshairHandler.setPricePrecision(precision);\r\n this.chartLegend.setPricePrecision(precision);\r\n this.crosshairTooltip.setPricePrecision(precision);\r\n }\r\n\r\n getMarket(): MarketConfig | null {\r\n return this.marketConfig;\r\n }\r\n\r\n setPriceLimits(referencePrice: number): void {\r\n if (!this.marketConfig) return;\r\n const limits = computePriceLimits(referencePrice, this.marketConfig);\r\n if (limits) {\r\n this.marketConfig.priceLimits = {\r\n ...this.marketConfig.priceLimits!,\r\n referencePrice,\r\n };\r\n }\r\n this.engine.requestRender();\r\n }\r\n\r\n // --- Features ---\r\n\r\n /** Get the resolved feature configuration */\r\n getFeatures(): Readonly<Required<FeaturesConfig>> {\r\n return this.features;\r\n }\r\n\r\n /** Update feature flags at runtime. Only provided keys are changed. */\r\n setFeatures(patch: Partial<FeaturesConfig>): void {\r\n Object.assign(this.features, patch);\r\n\r\n // Apply immediate side-effects\r\n if (patch.crosshair === false || patch.crosshairTooltip === false) {\r\n this.crosshairTooltip.hide();\r\n }\r\n if (patch.grid !== undefined) {\r\n this.engine.requestRender(LayerType.Background);\r\n }\r\n if (patch.volume !== undefined) {\r\n this.volumeRenderer.setVisible(patch.volume);\r\n this.engine.requestRender(LayerType.Main);\r\n }\r\n if (patch.drawings === false) {\r\n this.drawingManager.setActiveTool(null);\r\n }\r\n if (patch.trading === false) {\r\n this.tradingManager.setOrders([]);\r\n this.tradingManager.setPositions([]);\r\n }\r\n if (patch.keyboard !== undefined) {\r\n this.keyboardHandler?.setEnabled(patch.keyboard);\r\n }\r\n\r\n this.engine.requestRender();\r\n }\r\n\r\n // --- Lifecycle ---\r\n\r\n resize(): void {\r\n const size = this.engine.dprManager.readContainerSize();\r\n if (size.width <= 0 || size.height <= 0) return;\r\n this.containerSizeCache = size;\r\n this.containerSizeCacheTime = Date.now();\r\n // Resize canvas layers immediately (don't wait for DPRManager's debounced callback)\r\n const dpr = this.engine.dprManager.getDpr();\r\n this.engine.layerManager.resize(size, dpr);\r\n // Check if viewport was at the end before resize\r\n const wasAtEnd = this.viewport.isAtEnd();\r\n this.viewport.resize(size.width, size.height);\r\n this.layoutManager.resize(size.width, size.height);\r\n this.updateViewportAndRender(wasAtEnd);\r\n this.eventBus.emit('resize', size);\r\n }\r\n\r\n destroy(): void {\r\n if (this.countdownInterval) clearInterval(this.countdownInterval);\r\n // A loader of the host's own: no page lands in, or is asked for by, a destroyed chart.\r\n this.history.setLoader(null);\r\n this.disableAutoSave();\r\n this.disconnectStream();\r\n this.disconnectExecution();\r\n if (this.onWindowKeyDown) {\r\n window.removeEventListener('keydown', this.onWindowKeyDown);\r\n this.onWindowKeyDown = null;\r\n }\r\n this.keyboardHandler = null;\r\n this.a11y?.destroy();\r\n this.a11y = null;\r\n this.interactionManager.detach();\r\n this.tradingManager.destroy();\r\n this.animator.dispose();\r\n this.crosshairTooltip.destroy();\r\n this.pinnedTooltip.destroy();\r\n this.replayRestarting = true; // going away: no word to the others\r\n this.replayManager.dispose();\r\n this.alertManager.dispose();\r\n this.undoRedoManager.clear();\r\n this.engine.destroy();\r\n this.eventBus.destroy();\r\n this.container.innerHTML = '';\r\n }\r\n\r\n // --- Internal ---\r\n\r\n private createChartRenderer(type: ChartType | string): ChartRendererInterface {\r\n const renderer = resolveRenderer(type, (t) => this.pluginManager.getChartType(t));\r\n if (renderer instanceof HiLoRenderer) renderer.setPriceText((p) => this.formatPrice(p));\r\n return renderer;\r\n }\r\n\r\n /** Cached display data. Invalidated when raw data or chart type changes. */\r\n private getDisplayData(): DataSeries {\r\n if (this.displayDataCache) return this.displayDataCache;\r\n const raw = this.dataManager.getData();\r\n if (raw.length === 0) return raw;\r\n const result = resolveDisplayData(this.options.chartType, raw, (t) => this.pluginManager.getChartType(t), this.chartTypeOptions);\r\n this.displayDataCache = result;\r\n return result;\r\n }\r\n\r\n /** Recompute indicators from bar `from` on, and say so (live ticks, closes, replay steps). */\r\n private recalcIndicatorsFrom(data: DataSeries, from: number): void {\r\n this.indicatorEngine.recalculateFrom(data, from);\r\n this.announceIndicatorUpdate(from);\r\n }\r\n\r\n private recalcIndicators(data: DataSeries): void {\r\n this.indicatorEngine.recalculateAll(data);\r\n this.announceIndicatorUpdate(0);\r\n }\r\n\r\n /**\r\n * Emit `indicatorUpdate` once per task, from the earliest changed bar, after\r\n * the update that caused it has finished and scheduled its redraw: a\r\n * listener can neither interrupt a live tick nor read the layout before\r\n * the chart has updated it.\r\n */\r\n private announceIndicatorUpdate(from: number): void {\r\n if (this.indicatorUpdateFrom !== null) {\r\n this.indicatorUpdateFrom = Math.min(this.indicatorUpdateFrom, from);\r\n return;\r\n }\r\n this.indicatorUpdateFrom = from;\r\n queueMicrotask(() => {\r\n const first = this.indicatorUpdateFrom;\r\n this.indicatorUpdateFrom = null;\r\n if (first !== null) this.eventBus.emit('indicatorUpdate', { from: first });\r\n });\r\n }\r\n\r\n /** Lightweight render for streaming updates. No layout resolve, no indicator recalc. */\r\n private scheduleRender(): void {\r\n if (this.renderScheduled) return;\r\n this.renderScheduled = true;\r\n requestAnimationFrame(() => {\r\n this.renderScheduled = false;\r\n // A tick moves indicator values: refit the panes' scales.\r\n this.panelInfoCache = null;\r\n this.fitDataAndAxes();\r\n this.syncRenderContext();\r\n this.engine.requestRender();\r\n this.emitViewportEvents();\r\n });\r\n }\r\n\r\n /**\r\n * Push the display data into the viewport and apply auto-scale. The single\r\n * place auto-scale is computed — both the live-tick path (`scheduleRender`)\r\n * and the pan/zoom/data path (`updateViewportAndRender`) go through it.\r\n * They used to diverge: ticks fit candles only while pan/zoom also fit\r\n * overlay indicators (Bollinger, Keltner, Ichimoku…), so the price scale\r\n * jumped on every tick and jumped back on every pan frame.\r\n */\r\n private applyDataToViewport(scrollToEnd = false): DataSeries {\r\n const displayData = this.getDisplayData();\r\n const autoScale = this.options.autoScale !== false;\r\n\r\n // updateData first so dataLength is current for scrollToEnd — but only\r\n // fit the price range once the FINAL visible window is known. Fitting\r\n // before scrollToEnd measured the old window (e.g. deep history) and left\r\n // every bar of a freshly loaded series off-screen.\r\n this.viewport.updateData(displayData, autoScale && !scrollToEnd);\r\n if (scrollToEnd) {\r\n this.viewport.scrollToEnd();\r\n if (autoScale) this.viewport.updateData(displayData, true);\r\n }\r\n\r\n const vs = this.viewport.getState();\r\n\r\n // Baseline for percentage / indexed-to-100 axis labels: the close of the\r\n // first visible bar. Ignored by other scale modes.\r\n if (displayData.length > 0) {\r\n const baseIdx = Math.max(0, Math.min(vs.visibleRange.from, displayData.length - 1));\r\n this.viewport.setScaleBaseline(displayData[baseIdx]?.close);\r\n }\r\n\r\n // Expand the fitted range to include overlay indicator values (BB, Ichimoku, etc.)\r\n // and the compare lines on the price scale.\r\n if (autoScale) {\r\n const overlayRange = joinRanges(\r\n this.indicatorEngine.getOverlayPriceRange(vs.visibleRange.from, Math.min(vs.visibleRange.to, displayData.length - 1)),\r\n this.features.compareSymbols ? this.compareRenderer.getPriceRange(displayData, vs) : null,\r\n );\r\n if (overlayRange) {\r\n const current = vs.priceRange;\r\n const expandedMin = Math.min(current.min, overlayRange.min);\r\n const expandedMax = Math.max(current.max, overlayRange.max);\r\n if (expandedMin < current.min || expandedMax > current.max) {\r\n const range = expandedMax - expandedMin || 1;\r\n this.viewport.setPriceRange(\r\n expandedMin - range * 0.02,\r\n expandedMax + range * 0.02,\r\n );\r\n }\r\n }\r\n }\r\n return displayData;\r\n }\r\n\r\n /** Full update: resolve layout, update viewport, sync context, request render. */\r\n private updateViewportAndRender(scrollToEnd = false): void {\r\n // Invalidate layout cache\r\n this.resolvedLayoutCache = null;\r\n this.panelInfoCache = null;\r\n\r\n this.layoutManager.setLeftAxisWidth(this.isLeftPriceScaleShown() ? this.leftAxisWidth : 0);\r\n const resolved = this.getResolvedLayout();\r\n this.viewport.setChartRect(resolved.mainChartRect);\r\n\r\n this.fitDataAndAxes(scrollToEnd);\r\n\r\n this.syncRenderContext();\r\n this.engine.requestRender();\r\n\r\n this.emitViewportEvents();\r\n }\r\n\r\n /**\r\n * Fit the data to the plot, then size both price scales to their labels. A\r\n * scale that changes width changes the plot, so the data is fit again —\r\n * before anything reads the layout for this frame.\r\n */\r\n private fitDataAndAxes(scrollToEnd = false): void {\r\n this.applyDataToViewport(scrollToEnd);\r\n const priceResized = this.fitPriceAxisWidth();\r\n this.leftViewport = this.computeLeftViewport();\r\n if (this.fitLeftAxisWidth() || priceResized) this.applyDataToViewport(scrollToEnd);\r\n }\r\n\r\n /** Size the left scale to its widest label; true when the layout changed. */\r\n private fitLeftAxisWidth(): boolean {\r\n const left = this.leftViewport;\r\n if (!left) return false;\r\n const theme = this.themeManager.getTheme();\r\n const required = requiredPriceAxisWidth({\r\n min: left.priceRange.min,\r\n max: left.priceRange.max,\r\n lastPrice: null,\r\n tagPrecision: null,\r\n locale: this.numberLocale,\r\n fontFamily: theme.font.family,\r\n fontSizeSmall: theme.font.sizeSmall,\r\n measure: (text, font) => this.measureText(text, font),\r\n });\r\n const next = nextPriceAxisWidth(this.leftAxisWidth, required);\r\n if (next === this.leftAxisWidth) return false;\r\n this.leftAxisWidth = next;\r\n this.layoutManager.setLeftAxisWidth(next);\r\n this.resolvedLayoutCache = null;\r\n this.panelInfoCache = null;\r\n this.viewport.setChartRect(this.getResolvedLayout().mainChartRect);\r\n return true;\r\n }\r\n\r\n /**\r\n * Size the price axis to its widest label (tick labels, last-price tag,\r\n * crosshair pill), as an auto-width scale — so a sub-cent\r\n * price isn't clipped. Returns true when the width, and so the layout,\r\n * changed.\r\n */\r\n private fitPriceAxisWidth(): boolean {\r\n if (!this.features.priceAxis) return false;\r\n const { priceRange } = this.viewport.getState();\r\n const theme = this.themeManager.getTheme();\r\n const required = requiredPriceAxisWidth({\r\n min: priceRange.min,\r\n max: priceRange.max,\r\n lastPrice: this.currentPriceLine.getPrice(),\r\n tagPrecision: this.marketPricePrecision,\r\n locale: this.numberLocale,\r\n format: this.priceFormatter,\r\n fontFamily: theme.font.family,\r\n fontSizeSmall: theme.font.sizeSmall,\r\n measure: (text, font) => this.measureText(text, font),\r\n });\r\n const next = nextPriceAxisWidth(this.priceAxisWidth, required);\r\n if (next === this.priceAxisWidth) return false;\r\n\r\n this.priceAxisWidth = next;\r\n this.layoutManager.setPriceAxisWidth(next);\r\n this.viewport.setPriceAxisWidth(next);\r\n this.resolvedLayoutCache = null;\r\n this.panelInfoCache = null;\r\n this.viewport.setChartRect(this.getResolvedLayout().mainChartRect);\r\n return true;\r\n }\r\n\r\n private measureText(text: string, font: string): number {\r\n if (this.measureCtx === undefined) {\r\n this.measureCtx = typeof document !== 'undefined'\r\n ? document.createElement('canvas').getContext('2d')\r\n : null;\r\n }\r\n const ctx = this.measureCtx;\r\n if (!ctx) return text.length * 7; // no canvas (SSR/tests): a generous estimate\r\n ctx.font = font;\r\n return ctx.measureText(text).width;\r\n }\r\n\r\n /**\r\n * Fire the public viewport events (`visibleRangeChange`, `priceRangeChange`,\r\n * `zoomChange`) whenever the corresponding piece of viewport state changed\r\n * since the last render. Called at the end of every updateViewportAndRender,\r\n * so panning, zooming, resizing, and data updates all surface to consumers.\r\n */\r\n private emitViewportEvents(): void {\r\n const vs = this.viewport.getState();\r\n\r\n const from = Math.max(0, Math.floor(vs.visibleRange.from));\r\n const to = Math.max(from, Math.ceil(vs.visibleRange.to));\r\n if (\r\n !this.lastEmittedRange ||\r\n this.lastEmittedRange.from !== from ||\r\n this.lastEmittedRange.to !== to\r\n ) {\r\n this.lastEmittedRange = { from, to };\r\n this.eventBus.emit('visibleRangeChange', { from, to });\r\n }\r\n\r\n const { min, max } = vs.priceRange;\r\n if (\r\n !this.lastEmittedPriceRange ||\r\n this.lastEmittedPriceRange.min !== min ||\r\n this.lastEmittedPriceRange.max !== max\r\n ) {\r\n this.lastEmittedPriceRange = { min, max };\r\n this.eventBus.emit('priceRangeChange', { min, max });\r\n }\r\n\r\n // `barWidth` here is pixels-per-bar — the value Viewport.zoom() mutates.\r\n if (this.lastEmittedBarWidth !== vs.barWidth) {\r\n this.lastEmittedBarWidth = vs.barWidth;\r\n this.eventBus.emit('zoomChange', { barWidth: vs.barWidth });\r\n }\r\n\r\n this.checkHistory();\r\n }\r\n\r\n private getResolvedLayout() {\r\n if (!this.resolvedLayoutCache) {\r\n this.resolvedLayoutCache = this.layoutManager.resolve();\r\n this.syncCrosshairBounds(this.resolvedLayoutCache);\r\n }\r\n return this.resolvedLayoutCache;\r\n }\r\n\r\n /** The hovered bar is reported over the price pane and the panes above and below it. */\r\n private syncCrosshairBounds(layout: import('@tradecanvas/commons').ResolvedLayout): void {\r\n const main = layout.mainChartRect;\r\n let top = main.y;\r\n let bottom = main.y + main.height;\r\n for (const panel of layout.panels) {\r\n if (panel.config.position !== 'top' && panel.config.position !== 'bottom') continue;\r\n top = Math.min(top, panel.rect.y);\r\n bottom = Math.max(bottom, panel.rect.y + panel.rect.height);\r\n }\r\n this.crosshairHandler.setReportBounds({ top, bottom });\r\n }\r\n\r\n /** Cache container size for 500ms to avoid layout thrashing on rapid calls */\r\n private cachedContainerSize(): { width: number; height: number } {\r\n const now = Date.now();\r\n if (!this.containerSizeCache || now - this.containerSizeCacheTime > 500) {\r\n this.containerSizeCache = this.engine.dprManager.getContainerSize();\r\n this.containerSizeCacheTime = now;\r\n }\r\n return this.containerSizeCache;\r\n }\r\n\r\n private buildPanelRenderInfos(): import('@tradecanvas/core').PanelRenderInfo[] {\r\n if (this.panelInfoCache) return this.panelInfoCache;\r\n\r\n const resolved = this.getResolvedLayout();\r\n const mainVP = this.viewport.getState();\r\n\r\n const { from, to } = mainVP.visibleRange;\r\n const panels = resolved.panels.map((panel) => {\r\n // The pane's one value scale: plots, axis, crosshair and levels all use it.\r\n // On a log scale only while all its values are above 0.\r\n const logRange = panel.config.logScale ? this.indicatorEngine.getPaneValueRange(panel.config.id, from, to, true) : null;\r\n const priceRange = logRange ?? this.indicatorEngine.getPaneValueRange(panel.config.id, from, to) ?? { ...DEFAULT_PANE_RANGE };\r\n\r\n // Inset the indicator drawing area below the panel header (title + divider)\r\n const PANEL_HEADER_HEIGHT = 20;\r\n const insetRect = {\r\n x: panel.rect.x,\r\n y: panel.rect.y + PANEL_HEADER_HEIGHT,\r\n width: panel.rect.width,\r\n height: Math.max(0, panel.rect.height - PANEL_HEADER_HEIGHT),\r\n };\r\n\r\n return {\r\n instanceId: panel.config.id,\r\n members: this.indicatorEngine.getPaneMembers(panel.config.id),\r\n rect: panel.rect,\r\n viewport: {\r\n ...mainVP,\r\n chartRect: insetRect,\r\n priceRange,\r\n // Each pane has its own value scale: linear unless set to log (and\r\n // all its values are above 0), upright unless set upside down.\r\n logScale: logRange !== null,\r\n // In percent: labels only, of its first value on screen.\r\n scaleMode: panel.config.percentScale ? 'percentage' as const : 'regular' as const,\r\n scaleBaseline: panel.config.percentScale ? this.paneBaseline(panel.config.id, from, to) : undefined,\r\n // A pane's values are its own, not prices.\r\n formatPrice: undefined,\r\n priceUnit: undefined,\r\n invertScale: !!panel.config.invertScale,\r\n },\r\n };\r\n });\r\n\r\n this.panelInfoCache = panels;\r\n return panels;\r\n }\r\n\r\n /** Where the time axis starts: below the main chart and every pane under it. */\r\n private timeAxisTop(): number {\r\n const resolved = this.getResolvedLayout();\r\n const bottomPanelHeight = resolved.panels\r\n .filter(p => p.config.position === 'bottom')\r\n .reduce((sum, p) => sum + p.rect.height, 0);\r\n return resolved.mainChartRect.y + resolved.mainChartRect.height + bottomPanelHeight;\r\n }\r\n\r\n private syncRenderContext(): void {\r\n // Keep the pinned tooltip anchored to its bar through pans / zooms.\r\n if (this.pinnedTooltip.isPinned()) {\r\n this.pinnedTooltip.reposition(\r\n this.viewport.getState(),\r\n null,\r\n null,\r\n this.themeManager.getTheme(),\r\n );\r\n }\r\n\r\n const panels = this.features.indicators ? this.buildPanelRenderInfos() : [];\r\n const timeAxisY = this.timeAxisTop();\r\n\r\n const displayData = this.getDisplayData();\r\n this.leftViewport = this.computeLeftViewport();\r\n this.engine.setRenderContext({\r\n leftViewport: this.leftViewport,\r\n leftPriceAxis: this.leftPriceAxis,\r\n leftAxisWidth: this.leftAxisWidth,\r\n chartRenderer: this.mainSeriesVisible ? this.chartRenderer : null,\r\n priceLines: this.quoteLines(),\r\n gridRenderer: this.features.grid ? this.gridRenderer : null,\r\n priceAxis: this.features.priceAxis ? this.priceAxis : null,\r\n timeAxis: this.features.timeAxis ? this.timeAxis : null,\r\n crosshairHandler: this.features.crosshair ? this.crosshairHandler : null,\r\n indicatorEngine: this.features.indicators ? this.indicatorEngine : null,\r\n drawingRenderer: this.features.drawings ? this.drawingRenderer : null,\r\n tradingRenderer: this.features.trading ? this.tradingRenderer : null,\r\n currentPriceLine: this.currentPriceLine,\r\n chartLegend: this.features.legend ? this.chartLegend : null,\r\n volumeRenderer: this.features.volume ? this.volumeRenderer : null,\r\n volumeProfile: this.volumeProfile,\r\n marketProfile: this.marketProfile,\r\n depthHeatmap: this.depthHeatmap,\r\n periodLevels: this.periodLevels,\r\n pivotMarkers: this.pivotMarkers,\r\n watermark: this.features.watermark ? this.watermark : null,\r\n barCountdown: this.barCountdown,\r\n sessionBreaks: this.sessionBreaks,\r\n sessionShading: this.sessionShading,\r\n compareRenderer: this.compareRenderer,\r\n alertManager: this.features.alerts ? this.alertManager : null,\r\n measureOverlay: this.measureOverlay,\r\n selectionBoxOverlay: this.selectionBoxOverlay,\r\n priceAxisAddButton: this.priceAxisAddButton,\r\n signalMarkerManager: this.signalMarkerManager,\r\n tradeZoneManager: this.tradeZoneManager,\r\n panels,\r\n priceLimits: this.buildPriceLimits(),\r\n timeAxisY,\r\n // Attach data to the viewport so drawings/indicators can treat\r\n // `anchor.time` as a real timestamp and convert via timestampToBarIndex.\r\n viewport: { ...this.viewport.getState(), data: displayData },\r\n theme: this.themeManager.getTheme(),\r\n data: displayData,\r\n numberLocale: this.numberLocale,\r\n paneTitles: this.paneTitles,\r\n indicatorValueLabels: this.features.indicatorValueLabels,\r\n formatPrice: (price) => this.formatPrice(price),\r\n renderOverlayPlugins: (c, layer) => this.drawOverlayPlugins(c, layer),\r\n });\r\n }\r\n\r\n /** Draw registered overlay plugins for a layer (called by the engine). */\r\n private drawOverlayPlugins(\r\n ctx: CanvasRenderingContext2D,\r\n layer: 'main' | 'overlay' | 'ui',\r\n ): void {\r\n const overlays = overlaysForLayer(this.pluginManager.getOverlays(), layer);\r\n if (overlays.length === 0) return;\r\n const context = {\r\n viewport: this.viewport.getState(),\r\n data: this.getDisplayData(),\r\n theme: this.themeManager.getTheme(),\r\n };\r\n for (const overlay of overlays) {\r\n overlay.render(ctx, context);\r\n }\r\n }\r\n\r\n private buildPriceLimits() {\r\n if (!this.marketConfig?.priceLimits?.enabled || !this.marketConfig.priceLimits.referencePrice) return null;\r\n const limits = computePriceLimits(this.marketConfig.priceLimits.referencePrice, this.marketConfig);\r\n if (!limits) return null;\r\n return {\r\n ...limits,\r\n colors: this.marketConfig.colorScheme ? {\r\n ceiling: this.marketConfig.colorScheme.ceiling,\r\n floor: this.marketConfig.colorScheme.floor,\r\n reference: this.marketConfig.colorScheme.reference,\r\n } : undefined,\r\n };\r\n }\r\n}\r\n\r\ntype SnapshotIndicator = import('@tradecanvas/core').SnapshotIndicator;\r\n\r\n/**\r\n * `list` with each indicator after the one whose pane it is drawn in and the\r\n * ones whose lines it reads (otherwise in the order given).\r\n */\r\nfunction dependencyOrder(list: readonly SnapshotIndicator[]): SnapshotIndicator[] {\r\n const byId = new Map(list.map((ind) => [ind.instanceId, ind]));\r\n const out: SnapshotIndicator[] = [];\r\n const done = new Set<string>();\r\n const visiting = new Set<string>();\r\n const visit = (ind: SnapshotIndicator) => {\r\n if (done.has(ind.instanceId) || visiting.has(ind.instanceId)) return;\r\n visiting.add(ind.instanceId);\r\n const needs = [ind.pane, ...Object.values(ind.params).map((v) => parseIndicatorSource(v)?.instanceId)];\r\n for (const id of needs) {\r\n const dep = id ? byId.get(id) : undefined;\r\n if (dep) visit(dep);\r\n }\r\n visiting.delete(ind.instanceId);\r\n done.add(ind.instanceId);\r\n out.push(ind);\r\n };\r\n for (const ind of list) visit(ind);\r\n return out;\r\n}\r\n\r\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. */\r\nfunction sameKindAndPlace(a: SnapshotIndicator, b: SnapshotIndicator): boolean {\r\n return a.id === b.id\r\n && (a.pane ?? null) === (b.pane ?? null)\r\n && (a.scale ?? null) === (b.scale ?? null)\r\n && (a.position === undefined) === (b.position === undefined);\r\n}\r\n\r\n/** Shapes the chart can draw: a corner radius from 0 to 999 px. */\r\nfunction readShapes(shapes: ShapeConfig): ShapeConfig {\r\n const r = shapes.tagRadius;\r\n return typeof r === 'number' && Number.isFinite(r) && r >= 0 ? { tagRadius: Math.min(r, 999) } : {};\r\n}\r\n\r\n/** The indicators that read another symbol's bars (`params.symbol`). */\r\nconst SYMBOL_INDICATORS: ReadonlySet<string> = new Set(['compareSymbol', 'spread']);\r\n\r\n/** Both ranges in one (either may be null). */\r\nfunction joinRanges(\r\n a: { min: number; max: number } | null,\r\n b: { min: number; max: number } | null,\r\n): { min: number; max: number } | null {\r\n if (!a) return b;\r\n if (!b) return a;\r\n return { min: Math.min(a.min, b.min), max: Math.max(a.max, b.max) };\r\n}\r\n\r\n/** An alert as its events carry it. */\r\nfunction alertPayload(alert: import('@tradecanvas/core').PriceAlert): import('@tradecanvas/commons').AlertPayload {\r\n return {\r\n id: alert.id,\r\n price: alert.price,\r\n condition: alert.condition,\r\n message: alert.message,\r\n triggered: alert.triggered,\r\n channel: alert.channel,\r\n label: alert.label,\r\n target: alert.target,\r\n drawingId: alert.drawingId,\r\n percent: alert.percent,\r\n bars: alert.bars,\r\n };\r\n}\r\n\r\n/** At most this many marks per jump go to a paper account (a long jump goes by in chunks). */\r\nconst MAX_REPLAY_CHUNKS = 500;\r\n\r\n/** The first bar of `series` after `time` (its length when none is). */\r\nfunction firstAfter(series: DataSeries, time: number): number {\r\n let lo = 0;\r\n let hi = series.length;\r\n while (lo < hi) {\r\n const mid = (lo + hi) >> 1;\r\n if (series[mid].time <= time) lo = mid + 1;\r\n else hi = mid;\r\n }\r\n return lo;\r\n}\r\n\r\n/**\r\n * The prices a run of bars went through, in order, as far as the bars tell:\r\n * each extreme with its time, the earlier one first (within one bar, a rising\r\n * bar dipped first), then the close.\r\n */\r\nfunction replayMarks(run: readonly OHLCBar[]): [number, number][] {\r\n let lowAt = 0;\r\n let highAt = 0;\r\n for (let i = 1; i < run.length; i++) {\r\n if (run[i].low < run[lowAt].low) lowAt = i;\r\n if (run[i].high > run[highAt].high) highAt = i;\r\n }\r\n const low: [number, number] = [run[lowAt].low, run[lowAt].time];\r\n const high: [number, number] = [run[highAt].high, run[highAt].time];\r\n const lowFirst = lowAt !== highAt ? lowAt < highAt : run[lowAt].close >= run[lowAt].open;\r\n const last = run[run.length - 1];\r\n return [...(lowFirst ? [low, high] : [high, low]), [last.close, last.time]];\r\n}\r\n\r\n/** How far the last of `coarse` reaches: its widest recent spacing (months differ, weekends skip). */\r\nfunction lastBarSpan(coarse: DataSeries): number {\r\n let span = 0;\r\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);\r\n return span > 0 ? span : Infinity;\r\n}\r\n\r\n/**\r\n * Finer bars for a replay of `coarse`: sorted, inside its span (from its\r\n * first bar to the end of its last one), with no repeated times. Where they\r\n * start later than `coarse` (a feed's history is shorter), its earlier bars\r\n * come first as whole steps, so a bar index means the same bar either way.\r\n */\r\nfunction replaySteps(steps: DataSeries, coarse: DataSeries): DataSeries | null {\r\n if (coarse.length === 0) return null;\r\n const end = coarse[coarse.length - 1].time + lastBarSpan(coarse);\r\n const sorted = steps\r\n .filter((b) => isWholeBar(b) && b.time >= coarse[0].time && b.time < end)\r\n .sort((a, b) => a.time - b.time);\r\n const fine: OHLCBar[] = [];\r\n for (const bar of sorted) if (fine.length === 0 || fine[fine.length - 1].time !== bar.time) fine.push(bar);\r\n if (fine.length === 0) return null;\r\n // The first coarse bar the finer ones cover from its start; the ones before are whole steps.\r\n const from = coarse.findIndex((b) => b.time >= fine[0].time);\r\n if (from < 0) return null; // they only reach into the last bar\r\n const head = coarse.slice(0, from);\r\n const tail = fine.filter((b) => b.time >= coarse[from].time);\r\n return [...head, ...tail];\r\n}\r\n\r\nfunction isWholeBar(b: OHLCBar): boolean {\r\n return [b.time, b.open, b.high, b.low, b.close].every(Number.isFinite);\r\n}\r\n\r\n/** The bar of `coarse` holding `time`: the last one starting at or before it (0 before the first). */\r\nfunction barAt(coarse: DataSeries, time: number): number {\r\n let lo = 0;\r\n let hi = coarse.length - 1;\r\n while (lo < hi) {\r\n const mid = (lo + hi + 1) >> 1;\r\n if (coarse[mid].time <= time) lo = mid;\r\n else hi = mid - 1;\r\n }\r\n return lo;\r\n}\r\n\r\n/** The last step inside bar `barIndex` of `coarse` (where that bar is whole), or the nearest before it. */\r\nfunction lastStepOfBar(steps: DataSeries, coarse: DataSeries, barIndex: number): number {\r\n const bar = Math.max(0, Math.min(Math.floor(Number.isFinite(barIndex) ? barIndex : 0), coarse.length - 1));\r\n const end = bar + 1 < coarse.length ? coarse[bar + 1].time : Infinity;\r\n let at = -1;\r\n for (let i = 0; i < steps.length && steps[i].time < end; i++) at = i;\r\n return Math.max(0, at);\r\n}\r\n\r\n/** Bar `bar` as it stood after step `index`: built from its steps so far. */\r\nfunction formingBar(bar: OHLCBar, steps: DataSeries, index: number): OHLCBar {\r\n let i = index;\r\n while (i > 0 && steps[i - 1].time >= bar.time) i--;\r\n let out: OHLCBar = { ...steps[i], time: bar.time };\r\n for (let j = i + 1; j <= index; j++) out = mergeBar(out, steps[j]);\r\n return out;\r\n}\r\n\r\n/** `bar` with one more step in it. */\r\nfunction mergeBar(bar: OHLCBar, step: OHLCBar): OHLCBar {\r\n return {\r\n time: bar.time,\r\n open: bar.open,\r\n high: Math.max(bar.high, step.high),\r\n low: Math.min(bar.low, step.low),\r\n close: step.close,\r\n volume: (bar.volume ?? 0) + (step.volume ?? 0),\r\n };\r\n}\r\n\r\n/** How a stream bar lands on `series`: after its last bar, as its last bar again, or before it (stale). */\r\nfunction barPlacement(series: readonly OHLCBar[], bar: OHLCBar): 'append' | 'update' | 'stale' {\r\n const last = series[series.length - 1];\r\n if (!last) return 'append';\r\n const at = normalizeBarTime(bar.time);\r\n const lastAt = normalizeBarTime(last.time);\r\n return at > lastAt ? 'append' : at === lastAt ? 'update' : 'stale';\r\n}\r\n","import type { OHLCBar, DataSeries } from '@tradecanvas/commons';\r\n\r\n/**\r\n * Best-effort parser for OHLCV CSV / JSON drops.\r\n *\r\n * Recognises:\r\n * - CSV (header-driven, comma / semicolon / tab delimiters)\r\n * - JSON array of objects (`[{ time, open, high, low, close, volume }]`)\r\n * - JSON array of arrays (`[[time, open, high, low, close, volume]]`)\r\n *\r\n * Time columns auto-detect ISO strings, unix-seconds, and unix-ms. Returned\r\n * bars are sorted ascending by time and de-duplicated on collision.\r\n */\r\nexport interface ParseResult {\r\n data: DataSeries;\r\n /** Total rows seen — useful for \"imported 4321 of 5000 rows\" style toasts. */\r\n rowCount: number;\r\n /** Rows skipped because of missing/invalid required fields. */\r\n skipped: number;\r\n}\r\n\r\nconst TIME_KEYS = ['time', 'timestamp', 'date', 'datetime', 't'];\r\nconst OPEN_KEYS = ['open', 'o'];\r\nconst HIGH_KEYS = ['high', 'h'];\r\nconst LOW_KEYS = ['low', 'l'];\r\nconst CLOSE_KEYS = ['close', 'c'];\r\nconst VOL_KEYS = ['volume', 'vol', 'v'];\r\n\r\nexport function parseOHLCV(text: string): ParseResult {\r\n const trimmed = text.trim();\r\n if (!trimmed) return { data: [], rowCount: 0, skipped: 0 };\r\n\r\n if (trimmed[0] === '[' || trimmed[0] === '{') {\r\n return parseJson(trimmed);\r\n }\r\n return parseCsv(trimmed);\r\n}\r\n\r\nfunction parseJson(text: string): ParseResult {\r\n let raw: unknown;\r\n try {\r\n raw = JSON.parse(text);\r\n } catch (e) {\r\n throw new Error(`Invalid JSON: ${(e as Error).message}`);\r\n }\r\n if (!Array.isArray(raw)) {\r\n throw new Error('Expected a JSON array of OHLCV rows');\r\n }\r\n const bars: OHLCBar[] = [];\r\n let skipped = 0;\r\n for (const row of raw) {\r\n const bar = Array.isArray(row) ? coerceArrayRow(row) : coerceObjectRow(row as Record<string, unknown>);\r\n if (bar) bars.push(bar); else skipped++;\r\n }\r\n return finalize(bars, raw.length, skipped);\r\n}\r\n\r\nfunction parseCsv(text: string): ParseResult {\r\n const lines = text.split(/\\r?\\n/).filter(l => l.trim().length > 0);\r\n if (lines.length === 0) return { data: [], rowCount: 0, skipped: 0 };\r\n\r\n const delimiter = detectDelimiter(lines[0]);\r\n const header = splitLine(lines[0], delimiter).map(h => h.trim().toLowerCase());\r\n\r\n // If the first row doesn't look like a header (all numeric), assume the\r\n // canonical order: time, open, high, low, close, volume.\r\n const looksLikeHeader = header.some(h => isNaN(Number(h)) && h.length > 0);\r\n const colIdx = looksLikeHeader\r\n ? buildColumnMap(header)\r\n : { time: 0, open: 1, high: 2, low: 3, close: 4, volume: 5 };\r\n\r\n if (colIdx.time < 0 || colIdx.open < 0 || colIdx.high < 0 || colIdx.low < 0 || colIdx.close < 0) {\r\n throw new Error('CSV is missing required column(s): time, open, high, low, close');\r\n }\r\n\r\n const bars: OHLCBar[] = [];\r\n let skipped = 0;\r\n for (let i = looksLikeHeader ? 1 : 0; i < lines.length; i++) {\r\n const cols = splitLine(lines[i], delimiter);\r\n const time = parseTimeCell(cols[colIdx.time]);\r\n // Empty cells parse to `Number('') === 0`, which would silently produce\r\n // bogus zero bars. Treat empty as invalid up-front so the row gets\r\n // skipped instead of polluting the series.\r\n const open = numCell(cols[colIdx.open]);\r\n const high = numCell(cols[colIdx.high]);\r\n const low = numCell(cols[colIdx.low]);\r\n const close = numCell(cols[colIdx.close]);\r\n const volume = colIdx.volume >= 0 ? Number(cols[colIdx.volume]) : 0;\r\n if (time === null || ![open, high, low, close].every(Number.isFinite)) {\r\n skipped++;\r\n continue;\r\n }\r\n bars.push({ time, open: open!, high: high!, low: low!, close: close!, volume: Number.isFinite(volume) ? volume : 0 });\r\n }\r\n\r\n return finalize(bars, lines.length - (looksLikeHeader ? 1 : 0), skipped);\r\n}\r\n\r\nfunction detectDelimiter(headerLine: string): string {\r\n const counts: Record<string, number> = { ',': 0, ';': 0, '\\t': 0, '|': 0 };\r\n for (const ch of headerLine) {\r\n if (ch in counts) counts[ch]++;\r\n }\r\n let best = ',';\r\n let bestCount = 0;\r\n for (const [d, n] of Object.entries(counts)) {\r\n if (n > bestCount) { best = d; bestCount = n; }\r\n }\r\n return best;\r\n}\r\n\r\n/** Minimal CSV splitter — handles double-quoted fields, doesn't try to be RFC-perfect. */\r\nfunction splitLine(line: string, delimiter: string): string[] {\r\n const out: string[] = [];\r\n let buf = '';\r\n let inQuote = false;\r\n for (let i = 0; i < line.length; i++) {\r\n const ch = line[i];\r\n if (ch === '\"') {\r\n if (inQuote && line[i + 1] === '\"') { buf += '\"'; i++; }\r\n else inQuote = !inQuote;\r\n } else if (ch === delimiter && !inQuote) {\r\n out.push(buf);\r\n buf = '';\r\n } else {\r\n buf += ch;\r\n }\r\n }\r\n out.push(buf);\r\n return out;\r\n}\r\n\r\nfunction buildColumnMap(header: string[]): { time: number; open: number; high: number; low: number; close: number; volume: number } {\r\n const find = (keys: string[]) => header.findIndex(h => keys.includes(h));\r\n return {\r\n time: find(TIME_KEYS),\r\n open: find(OPEN_KEYS),\r\n high: find(HIGH_KEYS),\r\n low: find(LOW_KEYS),\r\n close: find(CLOSE_KEYS),\r\n volume: find(VOL_KEYS),\r\n };\r\n}\r\n\r\nfunction coerceObjectRow(row: Record<string, unknown>): OHLCBar | null {\r\n const get = (keys: string[]) => {\r\n for (const k of keys) {\r\n if (row[k] !== undefined) return row[k];\r\n const upper = k.toUpperCase();\r\n if (row[upper] !== undefined) return row[upper];\r\n }\r\n return undefined;\r\n };\r\n const time = parseTimeCell(String(get(TIME_KEYS) ?? ''));\r\n const open = Number(get(OPEN_KEYS));\r\n const high = Number(get(HIGH_KEYS));\r\n const low = Number(get(LOW_KEYS));\r\n const close = Number(get(CLOSE_KEYS));\r\n const volume = Number(get(VOL_KEYS) ?? 0);\r\n if (time === null || ![open, high, low, close].every(Number.isFinite)) return null;\r\n return { time, open, high, low, close, volume: Number.isFinite(volume) ? volume : 0 };\r\n}\r\n\r\nfunction coerceArrayRow(row: unknown[]): OHLCBar | null {\r\n if (row.length < 5) return null;\r\n const time = parseTimeCell(String(row[0]));\r\n const open = Number(row[1]);\r\n const high = Number(row[2]);\r\n const low = Number(row[3]);\r\n const close = Number(row[4]);\r\n const volume = row.length >= 6 ? Number(row[5]) : 0;\r\n if (time === null || ![open, high, low, close].every(Number.isFinite)) return null;\r\n return { time, open, high, low, close, volume: Number.isFinite(volume) ? volume : 0 };\r\n}\r\n\r\n/**\r\n * Returns milliseconds since epoch, or null if unparseable. Heuristic:\r\n * - numeric < 1e12 → seconds, multiply by 1000\r\n * - numeric >= 1e12 → already ms\r\n * - non-numeric → Date.parse fallback (handles ISO, RFC 2822, etc.)\r\n */\r\nfunction numCell(raw: string | undefined): number {\r\n if (raw === undefined || raw.trim() === '') return NaN;\r\n return Number(raw);\r\n}\r\n\r\nfunction parseTimeCell(raw: string): number | null {\r\n const trimmed = raw.trim();\r\n if (!trimmed) return null;\r\n const asNum = Number(trimmed);\r\n if (Number.isFinite(asNum) && asNum > 0) {\r\n return asNum < 1e12 ? asNum * 1000 : asNum;\r\n }\r\n const parsed = Date.parse(trimmed);\r\n return Number.isFinite(parsed) ? parsed : null;\r\n}\r\n\r\nfunction finalize(bars: OHLCBar[], rowCount: number, skipped: number): ParseResult {\r\n bars.sort((a, b) => a.time - b.time);\r\n // De-dup on identical time — keep the last (later rows are typically more\r\n // authoritative if the source updated a bar after publishing it).\r\n const out: OHLCBar[] = [];\r\n for (const bar of bars) {\r\n if (out.length > 0 && out[out.length - 1].time === bar.time) {\r\n out[out.length - 1] = bar;\r\n } else {\r\n out.push(bar);\r\n }\r\n }\r\n return { data: out, rowCount, skipped };\r\n}\r\n","import type { DataSeries } from '@tradecanvas/commons';\r\nimport { parseOHLCV, type ParseResult } from './parseOHLCV.js';\r\n\r\nexport interface DragDropImporterCallbacks {\r\n /** Called with the parsed series. Hook into chart.setData() here. */\r\n onData: (data: DataSeries, result: ParseResult, file: File) => void;\r\n /** Called when parsing throws. Hook a toast/banner here. */\r\n onError?: (err: Error, file: File) => void;\r\n}\r\n\r\n/**\r\n * Hooks `dragover` / `drop` on a host element so users can drop a CSV / JSON\r\n * file onto the chart and get it loaded. A subtle dashed-outline overlay\r\n * shows up during the drag for visual confirmation.\r\n *\r\n * The host owns calling `chart.setData()` via the `onData` callback — the\r\n * importer stays decoupled from Chart so it can be reused in other surfaces\r\n * (backtester UI, comparison views, etc.).\r\n */\r\nexport class DragDropImporter {\r\n private host: HTMLElement;\r\n private overlay: HTMLDivElement | null = null;\r\n private callbacks: DragDropImporterCallbacks;\r\n private bound: { dragenter: (e: DragEvent) => void; dragover: (e: DragEvent) => void; dragleave: (e: DragEvent) => void; drop: (e: DragEvent) => void };\r\n private depth = 0;\r\n\r\n constructor(host: HTMLElement, callbacks: DragDropImporterCallbacks) {\r\n this.host = host;\r\n this.callbacks = callbacks;\r\n this.bound = {\r\n dragenter: (e) => this.onDragEnter(e),\r\n dragover: (e) => this.onDragOver(e),\r\n dragleave: (e) => this.onDragLeave(e),\r\n drop: (e) => this.onDrop(e),\r\n };\r\n }\r\n\r\n attach(): void {\r\n this.host.addEventListener('dragenter', this.bound.dragenter);\r\n this.host.addEventListener('dragover', this.bound.dragover);\r\n this.host.addEventListener('dragleave', this.bound.dragleave);\r\n this.host.addEventListener('drop', this.bound.drop);\r\n }\r\n\r\n detach(): void {\r\n this.host.removeEventListener('dragenter', this.bound.dragenter);\r\n this.host.removeEventListener('dragover', this.bound.dragover);\r\n this.host.removeEventListener('dragleave', this.bound.dragleave);\r\n this.host.removeEventListener('drop', this.bound.drop);\r\n this.hideOverlay();\r\n }\r\n\r\n private onDragEnter(e: DragEvent): void {\r\n if (!hasFiles(e)) return;\r\n e.preventDefault();\r\n this.depth++;\r\n this.showOverlay();\r\n }\r\n\r\n private onDragOver(e: DragEvent): void {\r\n if (!hasFiles(e)) return;\r\n e.preventDefault();\r\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';\r\n }\r\n\r\n private onDragLeave(e: DragEvent): void {\r\n if (!hasFiles(e)) return;\r\n this.depth = Math.max(0, this.depth - 1);\r\n if (this.depth === 0) this.hideOverlay();\r\n }\r\n\r\n private async onDrop(e: DragEvent): Promise<void> {\r\n e.preventDefault();\r\n this.depth = 0;\r\n this.hideOverlay();\r\n\r\n const file = e.dataTransfer?.files?.[0];\r\n if (!file) return;\r\n\r\n try {\r\n const text = await file.text();\r\n const result = parseOHLCV(text);\r\n if (result.data.length === 0) {\r\n throw new Error(`No valid OHLCV rows found in \"${file.name}\"`);\r\n }\r\n this.callbacks.onData(result.data, result, file);\r\n } catch (err) {\r\n this.callbacks.onError?.(err as Error, file);\r\n }\r\n }\r\n\r\n private showOverlay(): void {\r\n if (this.overlay) return;\r\n this.overlay = document.createElement('div');\r\n Object.assign(this.overlay.style, {\r\n position: 'absolute',\r\n inset: '8px',\r\n zIndex: '90',\r\n pointerEvents: 'none',\r\n borderRadius: '10px',\r\n border: '2px dashed var(--tcw-accent, #f2a93b)',\r\n background: 'rgba(79, 136, 255, 0.06)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n color: 'var(--tcw-accent, #f2a93b)',\r\n fontSize: '14px',\r\n fontWeight: '600',\r\n letterSpacing: '-0.005em',\r\n fontFamily: 'inherit',\r\n backdropFilter: 'blur(2px)',\r\n });\r\n this.overlay.textContent = 'Drop CSV or JSON to load chart data';\r\n // The host needs `position: relative` for absolute inset to work; Chart\r\n // already sets this, but guard for arbitrary hosts.\r\n const cs = getComputedStyle(this.host);\r\n if (cs.position === 'static') this.host.style.position = 'relative';\r\n this.host.appendChild(this.overlay);\r\n }\r\n\r\n private hideOverlay(): void {\r\n this.overlay?.remove();\r\n this.overlay = null;\r\n }\r\n}\r\n\r\nfunction hasFiles(e: DragEvent): boolean {\r\n return e.dataTransfer?.types?.includes('Files') ?? false;\r\n}\r\n","/** A saved layout as a list shows it. */\r\nexport interface SavedLayoutSummary {\r\n id: string;\r\n name: string;\r\n /** The symbol it opens on. */\r\n symbol?: string;\r\n /** The interval it opens on. */\r\n timeframe?: string;\r\n /** When it was last saved, in ms since the epoch. */\r\n updatedAt: number;\r\n /** What saved it: `'chart'` (a widget, the default) or `'grid'` (a grid of charts). */\r\n kind?: string;\r\n}\r\n\r\n/** A saved layout: its summary and what it holds, as the widget wrote it (JSON). */\r\nexport interface SavedLayout extends SavedLayoutSummary {\r\n content: string;\r\n}\r\n\r\ntype MaybePromise<T> = T | Promise<T>;\r\n\r\n/**\r\n * Where named layouts live. The widget ships one for `localStorage` and one in\r\n * memory; a host keeps layouts on its server by implementing these four calls\r\n * (each may return a promise).\r\n */\r\nexport interface LayoutStorage {\r\n /** Every layout, newest first. */\r\n list(): MaybePromise<SavedLayoutSummary[]>;\r\n /** One layout, or `null` when there is no such layout. */\r\n load(id: string): MaybePromise<SavedLayout | null>;\r\n /** Save a layout, replacing one with the same id. */\r\n save(layout: SavedLayout): MaybePromise<void>;\r\n remove(id: string): MaybePromise<void>;\r\n}\r\n\r\nfunction summary(layout: SavedLayoutSummary): SavedLayoutSummary {\r\n const out: SavedLayoutSummary = { id: layout.id, name: layout.name, updatedAt: layout.updatedAt };\r\n if (layout.symbol !== undefined) out.symbol = layout.symbol;\r\n if (layout.timeframe !== undefined) out.timeframe = layout.timeframe;\r\n if (layout.kind !== undefined) out.kind = layout.kind;\r\n return out;\r\n}\r\n\r\nconst newestFirst = (a: SavedLayoutSummary, b: SavedLayoutSummary): number => b.updatedAt - a.updatedAt;\r\n\r\n/** A summary read back from storage (or a host's server), or null when it is not one. */\r\nexport function readLayoutSummary(value: unknown): SavedLayoutSummary | null {\r\n if (typeof value !== 'object' || value === null) return null;\r\n const v = value as Record<string, unknown>;\r\n if (typeof v.id !== 'string' || !v.id || typeof v.name !== 'string' || typeof v.updatedAt !== 'number' || !Number.isFinite(v.updatedAt)) return null;\r\n return summary({\r\n id: v.id,\r\n name: v.name,\r\n updatedAt: v.updatedAt,\r\n symbol: typeof v.symbol === 'string' ? v.symbol : undefined,\r\n timeframe: typeof v.timeframe === 'string' ? v.timeframe : undefined,\r\n kind: typeof v.kind === 'string' ? v.kind : undefined,\r\n });\r\n}\r\n\r\n/** A whole layout read back from storage, or null when it is not one. */\r\nexport function readSavedLayout(value: unknown): SavedLayout | null {\r\n const found = readLayoutSummary(value);\r\n const content = found ? (value as Record<string, unknown>).content : undefined;\r\n return found && typeof content === 'string' ? { ...found, content } : null;\r\n}\r\n\r\n/** Layouts kept in memory: for tests, or a page that does not keep them. */\r\nexport function memoryLayouts(initial: readonly SavedLayout[] = []): LayoutStorage {\r\n const layouts = new Map(initial.map((l) => [l.id, { ...l }]));\r\n return {\r\n list: () => [...layouts.values()].map(summary).sort(newestFirst),\r\n load: (id) => {\r\n const found = layouts.get(id);\r\n return found ? { ...found } : null;\r\n },\r\n save: (layout) => {\r\n layouts.set(layout.id, { ...layout });\r\n },\r\n remove: (id) => {\r\n layouts.delete(id);\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Layouts in the browser's `localStorage`: an index of summaries under\r\n * `<prefix>index` and each layout's content under `<prefix>item:<id>`.\r\n * Entries it cannot read are skipped.\r\n */\r\nexport function localStorageLayouts(prefix = 'tcw:layouts:'): LayoutStorage {\r\n const indexKey = `${prefix}index`;\r\n const itemKey = (id: string) => `${prefix}item:${id}`;\r\n\r\n const readIndex = (): SavedLayoutSummary[] => {\r\n let raw: unknown;\r\n try {\r\n raw = JSON.parse(localStorage.getItem(indexKey) ?? '[]');\r\n } catch {\r\n return [];\r\n }\r\n if (!Array.isArray(raw)) return [];\r\n return raw.map(readLayoutSummary).filter((s): s is SavedLayoutSummary => s !== null);\r\n };\r\n const writeIndex = (index: readonly SavedLayoutSummary[]) => localStorage.setItem(indexKey, JSON.stringify(index));\r\n\r\n return {\r\n list: () => readIndex().sort(newestFirst),\r\n load: (id) => {\r\n const found = readIndex().find((s) => s.id === id);\r\n const content = found ? localStorage.getItem(itemKey(id)) : null;\r\n return found && content !== null ? { ...found, content } : null;\r\n },\r\n save: (layout) => {\r\n // The content first: an index entry never points at nothing.\r\n localStorage.setItem(itemKey(layout.id), layout.content);\r\n writeIndex([...readIndex().filter((s) => s.id !== layout.id), summary(layout)]);\r\n },\r\n remove: (id) => {\r\n writeIndex(readIndex().filter((s) => s.id !== id));\r\n localStorage.removeItem(itemKey(id));\r\n },\r\n };\r\n}\r\n","import { readLayoutSummary, readSavedLayout, type LayoutStorage, type SavedLayout, type SavedLayoutSummary } from './layoutStorage.js';\r\n\r\n/** The longest a layout's name may be. */\r\nexport const MAX_LAYOUT_NAME = 80;\r\n\r\n/** A layout name trimmed, with runs of spaces folded and capped in length; `null` when nothing is left. */\r\nexport function cleanLayoutName(name: string): string | null {\r\n const clean = name.replace(/\\s+/g, ' ').trim().slice(0, MAX_LAYOUT_NAME).trim();\r\n return clean || null;\r\n}\r\n\r\n/** What a session saves and opens: the widget, or a grid of widgets. */\r\nexport interface LayoutSessionHost {\r\n /** What to save now, with the symbol and interval a list shows. */\r\n capture(): { content: string; symbol?: string; timeframe?: string };\r\n /** Show a saved layout; throw when its content can't be shown. */\r\n apply(layout: SavedLayout): Promise<void> | void;\r\n}\r\n\r\nexport interface LayoutSessionOptions {\r\n /** Save the current layout by itself once changes settle. Default `true`. */\r\n autoSave?: boolean;\r\n /** How long changes must settle before an auto-save. Default 1500 ms. */\r\n debounceMs?: number;\r\n /**\r\n * What this session saves (`'chart'`, the default, or `'grid'`): it lists\r\n * and opens only layouts of its kind, so both can share one storage.\r\n */\r\n kind?: string;\r\n /** The current layout, its saved state or the auto-save switch changed. */\r\n onChange?: () => void;\r\n /** An auto-save failed (a full storage, a server down). */\r\n onError?: (error: unknown) => void;\r\n now?: () => number;\r\n newId?: () => string;\r\n}\r\n\r\nfunction randomId(): string {\r\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\r\n return c?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\r\n}\r\n\r\nfunction summaryOf(layout: SavedLayoutSummary): SavedLayoutSummary {\r\n const { id, name, symbol, timeframe, updatedAt, kind } = layout;\r\n return {\r\n id,\r\n name,\r\n ...(symbol !== undefined ? { symbol } : {}),\r\n ...(timeframe !== undefined ? { timeframe } : {}),\r\n updatedAt,\r\n ...(kind !== undefined ? { kind } : {}),\r\n };\r\n}\r\n\r\ntype Captured = ReturnType<LayoutSessionHost['capture']>;\r\n\r\n/**\r\n * Named layouts over a {@link LayoutStorage}: the one showing now, save it,\r\n * save it under a new name, open, rename and remove. Changes reported with\r\n * `changed()` are auto-saved into the current layout once they settle, and\r\n * only when what would be saved differs from what was.\r\n *\r\n * Every operation runs in turn, and reads the current layout and captures\r\n * the chart when its turn comes: a save never lands in a layout opened after\r\n * it was asked for, and never holds a half-opened chart. What storage hands\r\n * back is checked before it is used.\r\n */\r\nexport class LayoutSession {\r\n private currentLayout: SavedLayoutSummary | null = null;\r\n /** What the current layout holds in storage (as captured), to tell a real change. */\r\n private savedContent: string | null = null;\r\n private dirty = false;\r\n private autoSave: boolean;\r\n private readonly debounceMs: number;\r\n private readonly kind: string;\r\n private timer: ReturnType<typeof setTimeout> | null = null;\r\n private applying = 0;\r\n /** Operations waiting or running; a change while one runs is looked at after. */\r\n private busy = 0;\r\n private changedWhileBusy = false;\r\n private tail: Promise<unknown> = Promise.resolve();\r\n private destroyed = false;\r\n private readonly now: () => number;\r\n private readonly newId: () => string;\r\n\r\n constructor(\r\n private readonly storage: LayoutStorage,\r\n private readonly host: LayoutSessionHost,\r\n private readonly options: LayoutSessionOptions = {},\r\n ) {\r\n this.autoSave = options.autoSave ?? true;\r\n this.debounceMs = options.debounceMs ?? 1500;\r\n this.kind = options.kind ?? 'chart';\r\n this.now = options.now ?? Date.now;\r\n this.newId = options.newId ?? randomId;\r\n }\r\n\r\n /** The layout showing now, or `null` before one is saved or opened. */\r\n current(): SavedLayoutSummary | null {\r\n return this.currentLayout;\r\n }\r\n\r\n /** The chart differs from the current layout as saved (auto-save off, failed, or not yet run). */\r\n isDirty(): boolean {\r\n return this.dirty;\r\n }\r\n\r\n isAutoSave(): boolean {\r\n return this.autoSave;\r\n }\r\n\r\n setAutoSave(on: boolean): void {\r\n if (this.autoSave === on) return;\r\n this.autoSave = on;\r\n this.notify();\r\n if (on && this.dirty) this.changed();\r\n }\r\n\r\n /** Every saved layout of this session's kind, newest first; entries that aren't layouts are left out. */\r\n async list(): Promise<SavedLayoutSummary[]> {\r\n const all: unknown = await this.storage.list();\r\n if (!Array.isArray(all)) return [];\r\n return all\r\n .map(readLayoutSummary)\r\n .filter((s): s is SavedLayoutSummary => s !== null && (s.kind ?? 'chart') === this.kind)\r\n .sort((a, b) => b.updatedAt - a.updatedAt);\r\n }\r\n\r\n /** Save into the current layout; `null` when there is none yet (ask for a name, then `saveAs`). */\r\n save(): Promise<SavedLayoutSummary | null> {\r\n return this.queue(() => {\r\n const current = this.currentLayout;\r\n return current ? this.write(current.id, current.name) : null;\r\n });\r\n }\r\n\r\n /** Save as a new layout called `name`, and make it the current one. */\r\n saveAs(name: string): Promise<SavedLayoutSummary> {\r\n const clean = cleanLayoutName(name);\r\n if (!clean) return Promise.reject(new Error('A layout needs a name'));\r\n return this.queue(() => this.write(this.newId(), clean));\r\n }\r\n\r\n /**\r\n * Show a saved layout. `false` when there is no such layout (or it isn't\r\n * one of this kind); rejects when it could not be shown, and then no layout\r\n * is current (the chart may be part way).\r\n */\r\n open(id: string): Promise<boolean> {\r\n return this.queue(async () => {\r\n const layout = readSavedLayout(await this.storage.load(id));\r\n if (!layout || layout.id !== id || (layout.kind ?? 'chart') !== this.kind || this.destroyed) return false;\r\n this.cancelTimer();\r\n this.applying++;\r\n try {\r\n await this.host.apply(layout);\r\n } catch (err) {\r\n this.forget();\r\n throw err;\r\n } finally {\r\n this.applying--;\r\n }\r\n this.cancelTimer();\r\n this.currentLayout = summaryOf(layout);\r\n // Restored state can read differently (new ids): what shows now counts as saved.\r\n this.savedContent = this.host.capture().content;\r\n this.dirty = false;\r\n this.notify();\r\n return true;\r\n });\r\n }\r\n\r\n rename(id: string, name: string): Promise<void> {\r\n const clean = cleanLayoutName(name);\r\n if (!clean) return Promise.reject(new Error('A layout needs a name'));\r\n return this.queue(async () => {\r\n const layout = readSavedLayout(await this.storage.load(id));\r\n if (!layout || layout.id !== id) return;\r\n await this.storage.save({ ...layout, name: clean });\r\n if (this.currentLayout?.id === id) this.currentLayout = { ...this.currentLayout, name: clean };\r\n this.notify();\r\n });\r\n }\r\n\r\n remove(id: string): Promise<void> {\r\n return this.queue(async () => {\r\n await this.storage.remove(id);\r\n if (this.currentLayout?.id === id) this.forget();\r\n });\r\n }\r\n\r\n /** Something a layout holds may have changed: check once things settle, and auto-save. */\r\n changed(): void {\r\n if (this.destroyed || this.applying > 0) return;\r\n if (!this.currentLayout) {\r\n // A first save under way: look again once it has a layout to compare with.\r\n if (this.busy > 0) this.changedWhileBusy = true;\r\n return;\r\n }\r\n this.cancelTimer();\r\n this.timer = setTimeout(() => {\r\n this.timer = null;\r\n void this.queue(() => this.settle());\r\n }, this.debounceMs);\r\n }\r\n\r\n /** Stop. A change still waiting to be auto-saved is saved now. */\r\n destroy(): void {\r\n if (this.destroyed) return;\r\n const pending = this.timer !== null;\r\n this.cancelTimer();\r\n const current = this.currentLayout;\r\n if (pending && current && this.autoSave) {\r\n // Capture now: the chart is about to go.\r\n let captured: Captured | null = null;\r\n try {\r\n captured = this.host.capture();\r\n } catch (err) {\r\n this.options.onError?.(err);\r\n }\r\n if (captured && captured.content !== this.savedContent) {\r\n const last = captured;\r\n void this.queue(() => this.write(current.id, current.name, last)).catch((err: unknown) => this.options.onError?.(err));\r\n }\r\n }\r\n this.destroyed = true;\r\n }\r\n\r\n /** The changes have settled: save them into the current layout, or mark it changed. */\r\n private async settle(): Promise<void> {\r\n const current = this.currentLayout;\r\n if (this.destroyed || !current) return;\r\n let captured: Captured;\r\n try {\r\n captured = this.host.capture();\r\n } catch (err) {\r\n this.options.onError?.(err);\r\n return;\r\n }\r\n const differs = captured.content !== this.savedContent;\r\n if (differs && this.autoSave) {\r\n try {\r\n await this.write(current.id, current.name, captured);\r\n } catch (err) {\r\n this.dirty = true;\r\n this.notify();\r\n this.options.onError?.(err);\r\n }\r\n return;\r\n }\r\n if (differs !== this.dirty) {\r\n this.dirty = differs;\r\n this.notify();\r\n }\r\n }\r\n\r\n /** Store the chart as layout `id` and make it the current one (runs inside the queue). */\r\n private async write(id: string, name: string, captured: Captured = this.host.capture()): Promise<SavedLayoutSummary> {\r\n this.cancelTimer();\r\n const layout: SavedLayout = {\r\n id,\r\n name,\r\n ...(captured.symbol !== undefined ? { symbol: captured.symbol } : {}),\r\n ...(captured.timeframe !== undefined ? { timeframe: captured.timeframe } : {}),\r\n updatedAt: this.now(),\r\n kind: this.kind,\r\n content: captured.content,\r\n };\r\n await this.storage.save(layout);\r\n const summary = summaryOf(layout);\r\n this.currentLayout = summary;\r\n this.savedContent = captured.content;\r\n this.dirty = false;\r\n this.notify();\r\n return summary;\r\n }\r\n\r\n /** No layout is current any more. */\r\n private forget(): void {\r\n this.cancelTimer();\r\n this.currentLayout = null;\r\n this.savedContent = null;\r\n this.dirty = false;\r\n this.notify();\r\n }\r\n\r\n /** Run operations one after another; a failed one does not stop the next. */\r\n private queue<T>(task: () => T | Promise<T>): Promise<T> {\r\n this.busy++;\r\n const run = this.tail.then(task).finally(() => {\r\n this.busy--;\r\n if (this.busy === 0 && this.changedWhileBusy) {\r\n this.changedWhileBusy = false;\r\n this.changed();\r\n }\r\n });\r\n this.tail = run.catch(() => undefined);\r\n return run;\r\n }\r\n\r\n private cancelTimer(): void {\r\n if (this.timer) {\r\n clearTimeout(this.timer);\r\n this.timer = null;\r\n }\r\n }\r\n\r\n private notify(): void {\r\n if (!this.destroyed) this.options.onChange?.();\r\n }\r\n}\r\n"],"mappings":"qEAIA,IAAM,EAAS,MAET,EAAiB,GAOvB,SAAgB,EACd,EACA,EACoC,CACpC,IAAM,EAAO,GAAM,SACnB,GAAI,CAAC,GAAQ,EAAA,EAAA,EAAA,iBAAiB,EAAK,EAAI,EAAK,OAAS,EAAG,OAAO,KAC/D,IAAM,GAAW,GAAM,UAAY,EAAE,EAAE,QAAS,GAAY,CAC1D,IAAM,GAAA,EAAA,EAAA,eAA4B,EAAQ,MAAM,CAC1C,GAAA,EAAA,EAAA,eAA0B,EAAQ,IAAI,CAC5C,OAAO,IAAgB,MAAQ,IAAc,KAAO,EAAE,CAAG,CAAC,CAAE,cAAa,YAAW,CAAC,EACrF,CACF,GAAI,EAAQ,SAAW,EAAG,OAAO,KACjC,IAAI,EAAU,IACd,IAAK,IAAI,EAAI,KAAK,IAAI,EAAG,EAAK,OAAS,EAAe,CAAE,EAAI,EAAK,OAAQ,IAAK,CAC5E,IAAM,GAAA,EAAA,EAAA,kBAAuB,EAAK,GAAG,KAAK,EAAA,EAAA,EAAA,kBAAoB,EAAK,EAAI,GAAG,KAAK,CAC3E,EAAM,GAAK,EAAM,IAAS,EAAU,GAE1C,GAAI,EAAE,EAAU,GAAS,OAAO,KAChC,IAAM,EAAS,CAAE,SAAU,EAAM,UAAS,YAAa,EAAQ,GAAG,YAAa,UAAW,EAAQ,EAAQ,OAAS,GAAG,UAAW,CACjI,MAAQ,KAAA,EAAA,EAAA,mBAAA,EAAA,EAAA,kBAA2C,EAAK,CAAE,EAAO,CC5BnE,IAAa,EAAuC,CAClD,KAAM,QACN,QAAS,8BACT,MAAO,kBACP,KAAM,gEACN,IAAK,+EACL,KAAM,sHACP,CAeK,GAAQ,EAAkB,IAC9B,EAAS,QAAQ,cAAe,EAAG,IAAkB,KAAQ,EAAS,OAAO,EAAO,GAAM,CAAG,EAAG,CAE5F,EAAkB,CAAC,OAAQ,uBAAwB,aAAc,mBAAmB,CAGpF,EAAmB,IAEnB,EAAa,IAQN,EAAb,KAAuB,CACrB,KACA,KACA,OACA,OAAgC,KAChC,UAA0D,KAE1D,OAA0B,IAAI,IAC9B,UAAoB,EACpB,aAA6D,KAC7D,WAAgC,CAC9B,KAAK,OAAS,KACd,KAAK,OAAO,QAAQ,KAAK,EAG3B,YACE,EACA,EACA,EAAmC,EAAE,CACrC,CAHiB,KAAA,UAAA,EACA,KAAA,OAAA,EAGjB,KAAK,OAAS,CAAE,GAAG,EAAqB,GAAG,EAAQ,CACnD,IAAK,IAAM,KAAQ,EAAiB,KAAK,OAAO,IAAI,EAAM,EAAU,aAAa,EAAK,CAAC,CACvF,EAAU,aAAa,OAAQ,cAAc,CAE7C,KAAK,KAAO,EAAO,MAAO,aAAa,CACvC,KAAK,KAAK,GAAK,iBAAiB,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAG,GAAG,GACvE,EAAU,aAAa,mBAAoB,KAAK,KAAK,GAAG,CAExD,KAAK,KAAO,EAAO,MAAO,wBAAwB,CAClD,KAAK,KAAK,aAAa,YAAa,SAAS,CAC7C,KAAK,KAAK,aAAa,cAAe,OAAO,CAE7C,EAAU,OAAO,KAAK,KAAM,KAAK,KAAK,CACtC,EAAU,iBAAiB,OAAQ,KAAK,OAAO,CAC/C,KAAK,aAAa,CAGpB,UAAU,EAAwC,CAChD,KAAK,OAAS,CAAE,GAAG,EAAqB,GAAG,EAAQ,CACnD,KAAK,aAAa,CAIpB,QAAQ,EAAQ,GAAa,CAC3B,IAAM,EAAO,GAAc,KAAK,KAAK,CAAG,KAAK,WAE7C,GAAI,GAAS,GAAQ,GAAK,KAAK,UAAU,aAAa,aAAa,GAAK,KAAK,OAAO,MAAO,CACrF,KAAK,cAAc,aAAa,KAAK,aAAa,CACtD,KAAK,aAAe,KACpB,KAAK,UAAY,KAAK,KAAK,CAC3B,KAAK,UAAU,aAAa,aAAc,KAAK,SAAS,CAAC,CACzD,OAEF,KAAK,eAAiB,eAAiB,CACrC,KAAK,aAAe,KACpB,KAAK,QAAQ,GAAK,EACjB,EAAK,CAIV,WAAkB,CAChB,KAAK,OAAS,KACV,KAAK,WAAW,aAAa,KAAK,UAAU,CAChD,KAAK,UAAY,eAAiB,CAChC,KAAK,UAAY,KACjB,KAAK,IAAI,KAAK,UAAU,CAAC,EACxB,EAAiB,CAItB,QAAQ,EAAoB,CAC1B,IAAM,EAAO,KAAK,OAAO,MAAM,CAC/B,GAAI,EAAK,SAAW,EAAG,CACrB,KAAK,IAAI,KAAK,OAAO,MAAM,CAC3B,OAEF,GAAM,CAAE,MAAO,KAAK,OAAO,cAAc,CACnC,EAAQ,KAAK,IAAI,EAAG,KAAK,IAAI,EAAK,OAAS,EAAG,KAAK,MAAM,EAAG,CAAC,CAAC,CACpE,KAAK,OAAS,KAAK,SAAW,KAAO,EAAQ,KAAK,IAAI,EAAG,KAAK,IAAI,EAAK,OAAS,EAAG,KAAK,OAAS,EAAK,CAAC,CACvG,IAAM,EAAM,EAAK,KAAK,QACtB,KAAK,OAAO,QAAQ,EAAI,KAAK,CAC7B,IAAM,EAAI,KAAK,OAAO,YACtB,KAAK,IAAI,EAAK,KAAK,OAAO,IAAK,CAC7B,KAAM,KAAK,OAAO,WAAW,EAAI,KAAK,CACtC,KAAM,EAAE,EAAI,KAAK,CACjB,KAAM,EAAE,EAAI,KAAK,CACjB,IAAK,EAAE,EAAI,IAAI,CACf,MAAO,EAAE,EAAI,MAAM,CACnB,OAAQ,EAAa,EAAI,QAAU,EAAE,CACtC,CAAC,CAAC,CAGL,SAAgB,CACV,KAAK,WAAW,aAAa,KAAK,UAAU,CAC5C,KAAK,cAAc,aAAa,KAAK,aAAa,CACtD,KAAK,UAAU,oBAAoB,OAAQ,KAAK,OAAO,CACvD,IAAK,GAAM,CAAC,EAAM,KAAU,KAAK,OAC3B,IAAU,KAAM,KAAK,UAAU,gBAAgB,EAAK,CACnD,KAAK,UAAU,aAAa,EAAM,EAAM,CAE/C,KAAK,KAAK,QAAQ,CAClB,KAAK,KAAK,QAAQ,CAGpB,aAA4B,CAC1B,KAAK,UAAU,aAAa,uBAAwB,KAAK,OAAO,KAAK,CACrE,KAAK,KAAK,YAAc,KAAK,OAAO,KACpC,KAAK,QAAQ,GAAK,CAGpB,SAA0B,CACxB,IAAM,EAAO,KAAK,OAAO,MAAM,CAC/B,GAAI,EAAK,SAAW,EAAG,OAAO,KAAK,OAAO,MAC1C,IAAM,EAAO,KAAK,OAAO,WAAW,CAC9B,EAAO,CAAC,KAAK,OAAO,QAAQ,CAAE,KAAK,OAAO,SAAW,KAAK,OAAO,SAAS,EAAK,CAAG,EAAM,KAAK,OAAO,WAAW,CAAC,CACnH,OAAO,QAAQ,CACf,KAAK,KAAK,CACb,OAAO,EAAK,KAAK,OAAO,QAAS,CAAE,OAAM,MAAO,KAAK,OAAO,YAAY,EAAK,EAAK,OAAS,GAAG,MAAM,CAAE,CAAC,CAGzG,UAA2B,CACzB,IAAM,EAAO,KAAK,OAAO,MAAM,CAC/B,GAAI,EAAK,SAAW,EAAG,OAAO,KAAK,OAAO,MAC1C,IAAM,EAAQ,KAAK,OAAO,cAAc,CAClC,EAAO,KAAK,IAAI,EAAG,KAAK,IAAI,EAAK,OAAS,EAAG,KAAK,KAAK,EAAM,KAAK,CAAC,CAAC,CACpE,EAAK,KAAK,IAAI,EAAM,KAAK,IAAI,EAAK,OAAS,EAAG,KAAK,MAAM,EAAM,GAAG,CAAC,CAAC,CACtE,EAAO,KACP,EAAM,IACV,IAAK,IAAI,EAAI,EAAM,GAAK,EAAI,IAC1B,EAAO,KAAK,IAAI,EAAM,EAAK,GAAG,KAAK,CACnC,EAAM,KAAK,IAAI,EAAK,EAAK,GAAG,IAAI,CAElC,OAAO,EAAK,KAAK,OAAO,KAAM,CAC5B,MAAO,EAAK,EAAO,EACnB,KAAM,KAAK,OAAO,WAAW,EAAK,GAAM,KAAK,CAC7C,GAAI,KAAK,OAAO,WAAW,EAAK,GAAI,KAAK,CACzC,KAAM,KAAK,OAAO,YAAY,EAAK,CACnC,IAAK,KAAK,OAAO,YAAY,EAAI,CAClC,CAAC,CAGJ,IAAY,EAAoB,CAC9B,KAAK,KAAK,YAAc,IAK5B,SAAS,EAA8C,EAAQ,EAA6C,CAC1G,IAAM,EAAK,SAAS,cAAc,EAAI,CAatC,MAZA,GAAG,UAAY,EACf,OAAO,OAAO,EAAG,MAAO,CACtB,SAAU,WACV,MAAO,MACP,OAAQ,MACR,OAAQ,OACR,QAAS,IACT,SAAU,SACV,KAAM,gBACN,WAAY,SACZ,OAAQ,IACT,CAAC,CACK,EAGT,SAAS,EAAa,EAAmB,CACvC,OAAO,OAAO,UAAU,EAAE,CAAG,OAAO,EAAE,CAAG,EAAE,QAAQ,EAAE,CC1MvD,SAAgB,EACd,EACA,EACA,EACA,EACQ,CAER,IAAM,EADmB,OAAO,KAAlB,GAA+C,EAAO,CAEjE,IAAK,GAAM,EAAO,GAAG,CACrB,OAAQ,GAAmB,OAAO,GAAM,UAAY,OAAO,SAAS,EAAE,CAAC,CACvE,MAAM,EAAG,EAAE,CACX,IAAK,GAAM,OAAO,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAErC,EAAS,OAAO,EAAO,QAAW,UAAY,EAAO,OAAS,CAAC,EAAO,OAAO,CAAG,EAAE,CAExF,MAAO,CADM,IAAO,UAAY,EAAO,OAAS,QAAU,QAAU,GAAa,EAAG,aAAa,CACnF,GAAG,EAAQ,GAAG,EAAQ,CAAC,KAAK,IAAI,CCYhD,SAAgB,EAAkB,EAAyC,CACzE,OAAQ,EAAR,CACE,IAAK,cACL,IAAK,aACL,IAAK,YACL,IAAK,YACH,OAAO,IAAI,EAAA,oBACb,IAAK,OACH,OAAO,IAAI,EAAA,aACb,IAAK,OACH,OAAO,IAAI,EAAA,aACb,IAAK,MACH,OAAO,IAAI,EAAA,YACb,IAAK,eACH,OAAO,IAAI,EAAA,qBACb,IAAK,WACH,OAAO,IAAI,EAAA,iBACb,IAAK,QACH,OAAO,IAAI,EAAA,cACb,IAAK,OACH,OAAO,IAAI,EAAA,aACb,IAAK,iBACH,OAAO,IAAI,EAAA,uBACb,IAAK,gBACH,OAAO,IAAI,EAAA,qBACb,IAAK,aACH,OAAO,IAAI,EAAA,mBACb,IAAK,UACH,OAAO,IAAI,EAAA,gBACb,IAAK,WACH,OAAO,IAAI,EAAA,iBACb,IAAK,kBACH,OAAO,IAAI,EAAA,wBACb,IAAK,OACH,OAAO,IAAI,EAAA,aACb,QACE,OAAO,IAAI,EAAA,qBAWjB,SAAgB,EAAqB,EAAiB,EAAiB,EAA4B,EAAE,CAAc,CACjH,GAAI,EAAI,SAAW,EAAG,OAAO,EAE7B,OAAQ,EAAR,CACE,IAAK,aACH,OAAA,EAAA,EAAA,cAAoB,EAAI,CAC1B,IAAK,QAAS,CACZ,IAAM,EAAM,EAAQ,OAAO,QAC3B,OAAO,OAAO,GAAQ,UAAA,EAAA,EAAA,SACV,EAAK,CAAE,UAAW,EAAK,CAAC,EAAA,EAAA,EAAA,SACxB,EAAK,CAAE,UAAW,EAAG,OAAQ,GAAM,UAAW,EAAQ,OAAO,WAAa,GAAI,CAAC,CAE7F,IAAK,YACH,OAAA,EAAA,EAAA,aAAmB,EAAK,EAAQ,WAAW,OAAS,EAAE,CACxD,IAAK,OACH,OAAA,EAAA,EAAA,QAAc,EAAK,EAAQ,MAAM,UAAY,EAAG,EAAQ,MAAM,cAAgB,UAAU,CAC1F,IAAK,iBAAkB,CACrB,IAAM,EAAM,EAAQ,gBAAgB,QACpC,OAAA,EAAA,EAAA,kBAAwB,EAAK,OAAO,GAAQ,SAAW,EAAM,EAAa,EAAI,CAAG,IAAM,EAAQ,gBAAgB,UAAY,EAAE,CAE/H,IAAK,YAAa,CAChB,IAAM,EAAQ,EAAQ,WAAW,MACjC,OAAA,EAAA,EAAA,aAAmB,EAAK,CAAE,UAAW,OAAO,GAAU,SAAW,EAAQ,EAAa,EAAI,CAAG,KAAO,CAAC,CAEvG,QACE,OAAO,GAKb,SAAgB,EAAuB,EAA0B,CAC/D,OAAQ,EAAR,CACE,IAAK,aACL,IAAK,QACL,IAAK,YACL,IAAK,OACL,IAAK,iBACL,IAAK,YACH,MAAO,GACT,QACE,MAAO,IASb,SAAgB,EAAoB,EAA0B,CAC5D,OAAO,IAAS,cAAgB,EAAuB,EAAK,CAG9D,SAAS,EAAa,EAAyB,CAC7C,IAAI,EAAM,EACV,IAAK,IAAM,KAAK,EAAK,GAAO,EAAE,MAC9B,OAAO,EAAM,EAAI,OAUnB,SAAgB,GAAgB,EAAc,EAAkD,CAC9F,IAAM,EAAS,IAAS,EAAK,CAE7B,OADI,EAAe,EAAO,gBAAgB,CACnC,EAAkB,EAAkB,CAO7C,SAAgB,GACd,EACA,EACA,EACA,EACY,CACZ,GAAI,EAAI,SAAW,EAAG,OAAO,EAC7B,IAAM,EAAS,IAAS,EAAK,CAE7B,OADI,GAAQ,UAAkB,EAAO,UAAU,EAAI,CAC5C,EAAqB,EAAmB,EAAK,EAAQ,CC/J9D,IAAa,GAAb,KAA+B,CAC7B,MAAsD,KACtD,QAAkB,EAClB,IAA6B,KAE7B,YAAY,EAAqC,CAA7B,KAAA,KAAA,EAGpB,OAAO,EAAa,EAAuB,CACzC,KAAK,IAAM,EACX,KAAK,QAAU,EAIjB,SAAgB,CACd,KAAK,IAAM,KACX,KAAK,QAAU,EACf,KAAK,QAAQ,CAOf,UAAiB,CACX,KAAK,SAAW,GAAK,CAAC,KAAK,MAC3B,KAAK,OAAO,aAAa,KAAK,MAAM,CACxC,KAAK,MAAQ,eAAiB,CAC5B,KAAK,MAAQ,KACT,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI,EAChC,KAAK,QAAQ,EAIlB,QAAe,CACb,AAEE,KAAK,SADL,aAAa,KAAK,MAAM,CACX,MAIjB,WAAqB,CACnB,OAAO,KAAK,QAAU,GAAK,KAAK,MAAQ,KAG1C,YAAsB,CACpB,OAAO,KAAK,QAAU,OC/C1B,SAAS,EAAW,EAAuB,CACzC,GAAI,CAAC,GAAO,OAAO,EAAI,MAAS,UAAY,CAAC,OAAO,SAAS,EAAI,KAAK,CAAE,MAAO,GAC/E,GAAM,CAAE,OAAM,OAAM,MAAK,SAAU,EASnC,MADA,EANI,CAAC,SAAS,EAAK,EAAI,CAAC,SAAS,EAAK,EAAI,CAAC,SAAS,EAAI,EAAI,CAAC,SAAS,EAAM,EAExE,EAAO,GAAK,EAAO,GAAK,EAAM,GAAK,EAAQ,GAE3C,EAAO,GAEP,EAAI,SAAW,IAAA,KAAc,CAAC,SAAS,EAAI,OAAO,EAAI,EAAI,OAAS,IAKzE,SAAS,EAAY,EAAuB,CAC1C,OAAO,EAAI,SAAW,IAAA,IACjB,EAAI,MAAQ,EAAI,MAAQ,EAAI,MAAQ,EAAI,OACxC,EAAI,KAAO,EAAI,MAAQ,EAAI,KAAO,EAAI,MAI7C,SAAS,EAAY,EAAuB,CAC1C,MAAO,CACL,GAAG,EACH,KAAM,KAAK,IAAI,EAAI,KAAM,EAAI,KAAM,EAAI,IAAK,EAAI,MAAM,CACtD,IAAK,KAAK,IAAI,EAAI,KAAM,EAAI,KAAM,EAAI,IAAK,EAAI,MAAM,CACrD,OAAQ,EAAI,SAAW,IAAA,GAAsC,EAA1B,KAAK,IAAI,EAAG,EAAI,OAAO,CAC3D,CAGH,IAAa,EAAb,KAAyB,CACvB,KAA2B,EAAE,CAE7B,SAAsB,CACpB,OAAO,KAAK,KAGd,QAAQ,EAAwB,CAM9B,IAAM,EAAiB,EAAE,CACzB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAM,EAAK,GACZ,EAAW,EAAI,EACpB,EAAI,KAAK,EAAY,EAAI,CAAG,EAAM,EAAY,EAAI,CAAC,CAErD,KAAK,KAAO,EAOd,YAAY,EAAkC,CAC5C,IAAM,EAAQ,KAAK,KAAK,OAAS,EAAI,KAAK,KAAK,GAAG,KAAO,IACnD,EAAmB,EAAE,CAC3B,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAM,EAAK,GACb,CAAC,EAAW,EAAI,EAAI,EAAI,MAAQ,GACpC,EAAM,KAAK,EAAY,EAAI,CAAG,EAAM,EAAY,EAAI,CAAC,CAEvD,GAAI,EAAM,SAAW,EAAG,MAAO,GAC/B,EAAM,MAAM,EAAG,IAAM,EAAE,KAAO,EAAE,KAAK,CACrC,IAAI,EAAO,EACX,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAC5B,EAAO,GAAK,EAAM,EAAO,GAAG,OAAS,EAAM,GAAG,OAClD,EAAM,KAAU,EAAM,IAIxB,MAFA,GAAM,OAAS,EACf,KAAK,KAAO,EAAM,OAAO,KAAK,KAAK,CAC5B,EAGT,UAAU,EAAoB,CACvB,EAAW,EAAI,EACpB,KAAK,KAAK,KAAK,EAAY,EAAI,CAAC,CAGlC,cAAc,EAAoB,CAChC,GAAI,CAAC,EAAW,EAAI,CAAE,OACtB,IAAM,EAAY,EAAY,EAAI,CAClC,GAAI,KAAK,KAAK,SAAW,EAAG,CAC1B,KAAK,KAAK,KAAK,EAAU,CACzB,OAEF,KAAK,KAAK,KAAK,KAAK,OAAS,GAAK,EAGpC,sBAAsB,EAA8D,CAC9E,KAAK,KAAK,SAAW,IACrB,CAAC,SAAS,EAAK,MAAM,EAAI,EAAK,MAAQ,GACtC,EAAK,SAAW,IAAA,KAAc,CAAC,SAAS,EAAK,OAAO,EAAI,EAAK,OAAS,KAC1E,KAAK,KAAK,KAAK,KAAK,OAAS,IAAA,EAAA,EAAA,UAAc,KAAK,KAAK,KAAK,KAAK,OAAS,GAAI,EAAK,GAGnF,WAAoB,CAClB,OAAO,KAAK,KAAK,OAGnB,OAAc,CACZ,KAAK,KAAO,EAAE,GChGL,EAAmB,IAGnB,GAAuB,IAKvB,GAA4B,IAkB5B,GAAb,KAA0B,CACxB,OAAuC,KACvC,SAAA,IACA,QAAkB,GAClB,UAAoB,GACpB,SAAmB,EACnB,QAAkB,EAElB,IAAc,EAEd,YAAY,EAAyC,CAAxB,KAAA,KAAA,EAE7B,UAAU,EAA8B,EAAA,IAA4C,CAClF,KAAK,OAAS,EACd,KAAK,SAAW,KAAK,IAAI,EAAG,KAAK,MAAM,EAAS,CAAC,CACjD,KAAK,OAAO,CAGd,WAAqB,CACnB,OAAO,KAAK,SAAW,KAIzB,OAAc,CACZ,KAAK,MACL,KAAK,QAAU,GACf,KAAK,UAAY,GACjB,KAAK,SAAW,EAChB,KAAK,QAAU,EAGjB,WAAqB,CACnB,OAAO,KAAK,QAId,SAAmB,CACjB,MAAO,CAAC,KAAK,UAIf,UAAU,EAAsB,EAAqB,CAC/C,GAAgB,GAAS,CAAC,KAAK,SAAS,EACxC,KAAK,UAAA,GAAyC,KAAK,KAAK,CAAG,KAAK,SAC/D,KAAK,UAAU,CAItB,MAAM,UAA4B,CAChC,IAAM,EAAS,KAAK,OACd,EAAS,KAAK,KAAK,YAAY,CACrC,GAAI,CAAC,GAAU,CAAC,KAAK,SAAS,EAAI,IAAW,KAAM,MAAO,GAE1D,IAAM,EAAM,KAAK,IACjB,KAAK,QAAU,GACf,KAAK,KAAK,CAAE,MAAO,UAAW,MAAO,EAAG,CAAC,CACzC,IAAI,EACJ,GAAI,CACF,EAAO,MAAM,EAAO,EAAQ,KAAK,SAAS,OACnC,EAAc,CAMrB,OALI,IAAQ,KAAK,KACjB,KAAK,QAAU,GACf,KAAK,WACL,KAAK,QAAU,KAAK,KAAK,CAAG,KAAK,IAAI,GAAsB,EAAmB,IAAM,KAAK,SAAW,GAAG,CACvG,KAAK,KAAK,CAAE,MAAO,QAAS,MAAO,EAAG,MAAO,aAAe,MAAQ,EAAI,QAAU,OAAO,EAAI,CAAE,CAAC,CACzF,GALsB,EAO/B,GAAI,IAAQ,KAAK,IAAK,MAAO,GAC7B,KAAK,QAAU,GACf,KAAK,SAAW,EAChB,KAAK,QAAU,EACf,IAAM,EAAQ,KAAK,KAAK,QAAQ,EAAK,CAOrC,OANI,IAAU,GACZ,KAAK,UAAY,GACjB,KAAK,KAAK,CAAE,MAAO,MAAO,MAAO,EAAG,CAAC,EAErC,KAAK,KAAK,CAAE,MAAO,SAAU,MAAO,EAAO,CAAC,CAEvC,EAGT,SAA2B,CACzB,OAAO,KAAK,SAAW,MAAQ,CAAC,KAAK,SAAW,CAAC,KAAK,UAIxD,KAAa,EAAmC,CAC9C,GAAI,CACF,KAAK,KAAK,KAAK,EAAQ,OAChB,EAAc,CACrB,QAAQ,MAAM,8CAA+C,EAAI,EAIrE,KAAsB,CACpB,OAAO,KAAK,KAAK,IAAM,KAAK,KAAK,KAAK,CAAG,KAAK,KAAK,GClI1C,GAAb,KAA0B,CACxB,MAEA,MAAoC,KAEpC,YAAY,EAAiC,CACvC,CAAC,GAAe,IAAgB,OAClC,KAAK,MAAQ,CAAE,GAAG,EAAA,WAAY,CACrB,IAAgB,QACzB,KAAK,MAAQ,CAAE,GAAG,EAAA,YAAa,CAE/B,KAAK,MAAQ,CAAE,GAAG,EAAa,CAInC,UAAkB,CAChB,OAAO,KAAK,MAGd,SAAS,EAAsC,CACzC,IAAgB,OAClB,KAAK,MAAQ,CAAE,GAAG,EAAA,WAAY,CACrB,IAAgB,QACzB,KAAK,MAAQ,CAAE,GAAG,EAAA,YAAa,CAE/B,KAAK,MAAQ,CAAE,GAAG,EAAa,CAE7B,KAAK,QAAO,KAAK,MAAQ,CAAE,GAAG,KAAK,MAAO,MAAO,CAAE,GAAG,KAAK,MAAM,MAAO,GAAG,KAAK,MAAO,CAAE,EAI/F,SAAS,EAAiC,CACxC,KAAK,MAAQ,EAAQ,CAAE,GAAG,EAAO,CAAG,KACpC,KAAK,MAAQ,CAAE,GAAG,KAAK,MAAO,MAAO,EAAQ,CAAE,GAAG,KAAK,MAAM,MAAO,GAAG,EAAO,CAAG,KAAK,MAAM,MAAO,GCjCjG,GAAsB,IACtB,GAAkB,GAMlB,EAAgB,GAA4B,EAAE,WAAa,OAAS,EAAE,WAAa,SAE5E,GAAb,KAA2B,CACzB,OAAgC,EAAE,CAClC,eAAyB,EACzB,gBAA0B,EAC1B,eAAyB,EAAA,iBAEzB,cAAwB,EAExB,UAAmC,KAEnC,OAAO,EAAe,EAAsB,CAC1C,KAAK,eAAiB,EACtB,KAAK,gBAAkB,EAGzB,SAAS,EAAoB,EAA0B,SAAU,EAAqB,CAEpF,KAAK,UAAY,KACjB,IAAM,EAAa,IAAa,QAAU,IAAa,QACvD,KAAK,OAAO,KAAK,CACf,GAAI,EACJ,WACA,KAAM,IAAS,EAAa,GAAsB,EAAA,sBAClD,QAAS,EAAa,GAAkB,EAAA,iBACxC,QAAS,CAAE,KAAM,YAAa,oBAAqB,EAAY,CAChE,CAAC,CAIJ,YAAY,EAAgB,EAAoB,CAC9C,IAAM,EAAQ,KAAK,OAAO,KAAM,GAAM,EAAE,KAAO,EAAO,CACjD,IACL,EAAM,GAAK,EACX,EAAM,QAAU,CAAE,GAAG,EAAM,QAAS,oBAAqB,EAAM,CAC3D,KAAK,YAAc,IAAQ,KAAK,UAAY,IAGlD,YAAY,EAA0B,CACpC,KAAK,OAAS,KAAK,OAAO,OAAQ,GAAM,EAAE,KAAO,EAAW,CACxD,KAAK,YAAc,IAAY,KAAK,UAAY,MAQtD,kBAAkB,EAAoB,EAA6B,CACjE,IAAM,EAAQ,KAAK,OAAO,KAAM,GAAM,EAAE,KAAO,EAAW,CAK1D,MAJI,CAAC,GAAS,CAAC,EAAa,EAAM,EAC9B,KAAK,YAAc,MAAQ,CAAC,CAAC,EAAM,YAAc,EAAkB,IACvE,KAAK,UAAY,KACjB,EAAM,UAAY,GAAa,IAAA,GACxB,IAIT,iBAAiB,EAA6B,CAC5C,MAAO,CAAC,CAAC,KAAK,OAAO,KAAM,GAAM,EAAE,KAAO,EAAW,EAAE,UAIzD,sBAAsB,EAA6B,CACjD,IAAM,EAAQ,KAAK,OAAO,KAAM,GAAM,EAAE,KAAO,EAAW,CAE1D,MADI,CAAC,GAAS,CAAC,EAAa,EAAM,CAAS,GACpC,KAAK,YAAc,KAAuC,CAAC,CAAC,EAAM,UAAxC,KAAK,YAAc,EAItD,cAAc,EAAoB,EAAwE,CACxG,IAAM,EAAQ,KAAK,OAAO,KAAM,GAAM,EAAE,KAAO,EAAW,CAC1D,GAAI,CAAC,EAAO,MAAO,GACnB,IAAI,EAAU,GAad,OAZI,EAAM,MAAQ,IAAA,IAAa,CAAC,CAAC,EAAM,WAAa,EAAM,MACxD,EAAM,SAAW,EAAM,KAAO,IAAA,GAC9B,EAAU,IAER,EAAM,SAAW,IAAA,IAAa,CAAC,CAAC,EAAM,cAAgB,EAAM,SAC9D,EAAM,YAAc,EAAM,QAAU,IAAA,GACpC,EAAU,IAER,EAAM,UAAY,IAAA,IAAa,CAAC,CAAC,EAAM,eAAiB,EAAM,UAChE,EAAM,aAAe,EAAM,SAAW,IAAA,GACtC,EAAU,IAEL,EAIT,aAAa,EAAoB,EAAwB,CACvD,IAAM,EAAQ,KAAK,OAAO,KAAM,GAAM,EAAE,KAAO,EAAW,CAC1D,GAAI,CAAC,EAAO,MAAO,GACnB,IAAM,EAAQ,KAAK,OAAO,OAAQ,GAAM,EAAE,WAAa,EAAM,SAAS,CACtE,OAAO,EAAM,EAAM,QAAQ,EAAM,CAAG,KAAW,IAAA,GAOjD,kBAAkB,EAAoC,CACpD,IAAM,EAAQ,IAAe,KAAO,KAAO,KAAK,OAAO,KAAM,GAAM,EAAE,KAAO,EAAW,CAKvF,OAJI,IAAe,OAAS,CAAC,GAAS,CAAC,EAAa,EAAM,GACtD,KAAK,YAAc,EAAmB,IAC1C,KAAK,UAAY,EACb,IAAO,EAAM,UAAY,IAAA,IACtB,IAGT,mBAAmC,CACjC,OAAO,KAAK,UAOd,UAAU,EAAoB,EAAwB,CACpD,IAAM,EAAQ,KAAK,OAAO,KAAM,GAAM,EAAE,KAAO,EAAW,CAC1D,GAAI,CAAC,EAAO,MAAO,GACnB,IAAM,EAAQ,KAAK,OAAO,OAAQ,GAAM,EAAE,WAAa,EAAM,SAAS,CAEhE,EAAQ,EADH,EAAM,QAAQ,EACL,CAAK,GACzB,GAAI,CAAC,EAAO,MAAO,GACnB,IAAM,EAAI,KAAK,OAAO,QAAQ,EAAM,CAC9B,EAAI,KAAK,OAAO,QAAQ,EAAM,CAC9B,EAAO,CAAC,GAAG,KAAK,OAAO,CAI7B,MAHA,GAAK,GAAK,EACV,EAAK,GAAK,EACV,KAAK,OAAS,EACP,GAIT,YAAY,EAA8B,CACxC,IAAM,EAAQ,GAAmB,CAC/B,IAAM,EAAK,EAAI,QAAQ,EAAE,GAAG,CAC5B,OAAO,EAAK,EAAI,EAAI,OAAS,KAAK,OAAO,QAAQ,EAAE,CAAG,GAExD,KAAK,OAAS,CAAC,GAAG,KAAK,OAAO,CAAC,MAAM,EAAG,IAAM,EAAK,EAAE,CAAG,EAAK,EAAE,CAAC,CAIlE,UAAkB,EAAoB,EAA+B,CAGnE,OAFK,EAAa,EAAM,CACpB,KAAK,UAAkB,EAAM,KAAO,KAAK,UAAY,EAAA,GAClD,EAAM,UAAA,GAAoC,EAAM,KAFtB,EAAM,KAKzC,iBAAiB,EAAoB,EAA+B,CAClE,IAAM,EAAQ,KAAK,OAAO,KAAM,GAAM,EAAE,KAAO,EAAW,CAC1D,GAAI,EAAO,CACT,IAAM,EAAc,EAAM,WAAa,QAAU,EAAM,WAAa,QAC9D,EAAa,IAAa,QAAU,IAAa,QACvD,EAAM,SAAW,EACb,IAAgB,IAClB,EAAM,KAAO,EAAa,GAAsB,EAAA,qBAChD,EAAM,QAAU,EAAa,GAAkB,EAAA,kBAG7C,IACF,EAAM,UAAY,IAAA,GACd,KAAK,YAAc,IAAY,KAAK,UAAY,QAU1D,aAAa,EAAoB,EAAoB,CACnD,IAAM,EAAQ,KAAK,OAAO,KAAM,GAAM,EAAE,KAAO,EAAW,CAC1D,GAAI,EAAO,CACT,IAAM,EAAO,EAAa,EAAM,CAAG,KAAK,gBAAkB,EAAA,iBAAA,GAAoD,KAAK,eAAiB,EAC9H,EAAS,EAAO,EAAM,QAAU,KAAK,IAAI,EAAM,EAAK,CAAG,EAC7D,EAAM,KAAO,KAAK,IAAI,EAAM,QAAS,EAAO,CAC5C,EAAM,UAAY,IAAA,GAClB,KAAK,UAAY,MAKrB,kBAAkB,EAAqB,CACrC,KAAK,eAAiB,EAIxB,iBAAiB,EAAqB,CACpC,KAAK,cAAgB,KAAK,IAAI,EAAG,EAAM,CAGzC,WAA2B,CACzB,OAAO,KAAK,OAGd,SAA0B,CACxB,IAAM,EAAa,KAAK,OAAO,OAAQ,GAAM,EAAE,WAAa,OAAO,CAC7D,EAAc,KAAK,OAAO,OAAQ,GAAM,EAAE,WAAa,QAAQ,CAC/D,EAAY,KAAK,OAAO,OAAQ,GAAM,EAAE,WAAa,MAAM,CAC3D,EAAe,KAAK,OAAO,OAAQ,GAAM,EAAE,WAAa,SAAS,CAGjE,GAAU,EAAU,OAAS,EAAa,OAAS,GAAA,GACnD,EAAiB,KAAK,UAAY,KAAK,OAAO,KAAM,GAAM,EAAE,KAAO,KAAK,UAAU,CAAG,IAAA,GACrF,EAAgB,KAAK,IACzB,GAAgB,SAAW,EAC3B,KAAK,gBAAkB,EAAA,iBAAA,GAAoD,EAC5E,CACK,EAAQ,GAAmB,KAAK,UAAU,EAAG,EAAc,CAE3D,EAAY,EAAW,QAAQ,EAAG,IAAM,EAAI,EAAE,KAAM,EAAE,CACtD,EAAa,EAAY,QAAQ,EAAG,IAAM,EAAI,EAAE,KAAM,EAAE,CACxD,EAAY,EAAU,QAAQ,EAAG,IAAM,EAAI,EAAK,EAAE,CAAE,EAAE,CACtD,EAAe,EAAa,QAAQ,EAAG,IAAM,EAAI,EAAK,EAAE,CAAE,EAAE,CAE5D,EAAQ,EAAY,KAAK,cACzB,EAAsB,CAC1B,EAAG,EACH,EAAG,EACH,MAAO,KAAK,IAAI,EAAG,KAAK,eAAiB,EAAQ,EAAa,KAAK,eAAe,CAClF,OAAQ,KAAK,IAAI,EAAG,KAAK,gBAAkB,EAAY,EAAe,EAAA,iBAAiB,CACxF,CAEK,EAAkC,EAAE,CACpC,EAA0B,EAAE,CAG9B,EAAI,EACR,IAAK,IAAM,KAAS,EAClB,EAAe,KAAK,CAClB,OAAQ,EACR,KAAM,CAAE,IAAG,EAAG,EAAG,MAAO,EAAM,KAAM,OAAQ,KAAK,gBAAkB,EAAA,iBAAkB,CACtF,CAAC,CACF,EAAS,KAAK,CACZ,QAAS,EAAM,GACf,KAAM,CAAE,EAAG,EAAI,EAAM,KAAO,EAAG,EAAG,EAAG,MAAO,EAAG,OAAQ,KAAK,gBAAiB,CAC7E,YAAa,WACd,CAAC,CACF,GAAK,EAAM,KAIb,IAAI,EAAI,EACR,IAAK,IAAM,KAAS,EAAW,CAC7B,IAAM,EAAS,EAAK,EAAM,CAC1B,EAAe,KAAK,CAClB,OAAQ,EACR,KAAM,CAAE,EAAG,EAAO,IAAG,MAAO,EAAc,MAAO,SAAQ,CAC1D,CAAC,CACF,EAAS,KAAK,CACZ,QAAS,EAAM,GACf,KAAM,CAAE,EAAG,EAAO,EAAG,EAAI,EAAS,EAAG,MAAO,EAAc,MAAO,OAAQ,EAAG,CAC5E,YAAa,aACd,CAAC,CACF,GAAK,EAIP,EAAI,EAAc,EAAI,EAAc,OACpC,IAAK,IAAM,KAAS,EAAc,CAChC,IAAM,EAAS,EAAK,EAAM,CAC1B,EAAe,KAAK,CAClB,OAAQ,EACR,KAAM,CAAE,EAAG,EAAO,IAAG,MAAO,EAAc,MAAO,SAAQ,CAC1D,CAAC,CACF,EAAS,KAAK,CACZ,QAAS,EAAM,GACf,KAAM,CAAE,EAAG,EAAO,EAAG,EAAI,EAAG,MAAO,EAAc,MAAO,OAAQ,EAAG,CACnE,YAAa,aACd,CAAC,CACF,GAAK,EAIP,EAAI,EAAQ,EAAc,MAAQ,KAAK,eACvC,IAAK,IAAM,KAAS,EAClB,EAAe,KAAK,CAClB,OAAQ,EACR,KAAM,CAAE,IAAG,EAAG,EAAG,MAAO,EAAM,KAAM,OAAQ,KAAK,gBAAkB,EAAA,iBAAkB,CACtF,CAAC,CACF,EAAS,KAAK,CACZ,QAAS,EAAM,GACf,KAAM,CAAE,EAAG,EAAI,EAAG,EAAG,EAAG,MAAO,EAAG,OAAQ,KAAK,gBAAiB,CAChE,YAAa,WACd,CAAC,CACF,GAAK,EAAM,KAGb,MAAO,CAAE,gBAAe,OAAQ,EAAgB,WAAU,CAG5D,kBAAyB,CACvB,OAAO,KAAK,SAAS,CAAC,gBC/SpB,GAAe,GAEf,GAAmB,GAEnB,GAAwB,GAExB,EAAoB,EAuB1B,SAAgB,EAAuB,EAAoC,CACzE,GAAM,CAAE,MAAK,MAAK,YAAW,SAAQ,aAAY,gBAAe,WAAY,EACtE,GAAA,EAAA,EAAA,oBAAmC,EAAK,EAAI,CAC5C,EAAe,EAAM,cAAgB,EACrC,EAAS,IAAc,KAA+B,CAAC,EAAK,EAAI,CAAlC,CAAC,EAAK,EAAK,EAAU,CAErD,EAAO,EAAA,iBACX,IAAK,IAAM,KAAS,EAAQ,CAC1B,GAAI,CAAC,OAAO,SAAS,EAAM,CAAE,SAC7B,IAAM,EAAQ,EAAM,OAAS,EAAM,OAAO,EAAM,EAAA,EAAA,EAAA,aAAe,EAAO,EAAe,EAAO,CACtF,EAAM,EAAM,OAAS,GAAA,EAAA,EAAA,aAAoB,EAAO,EAAc,EAAO,CAC3E,EAAO,KAAK,IACV,EACA,EAAQ,EAAO,OAAO,EAAc,KAAK,IAAa,CAAG,GACzD,EAAQ,EAAK,aAAa,IAAa,CAAG,GAC1C,EAAQ,EAAK,OAAO,EAAc,KAAK,IAAa,CAAG,GACxD,CAGH,OAAO,GAAQ,EAAA,iBAAmB,EAAA,iBAAmB,KAAK,KAAK,EAAO,EAAkB,CAAG,EAI7F,SAAgB,EAAmB,EAAiB,EAA0B,CAG5E,OAFI,EAAW,GACX,GAAY,EAAA,GAAoC,EAC7C,EC7CT,IAAa,GAAb,KAA6B,CAC3B,KAAe,EACf,QAAkB,EAClB,QAAkB,GAGlB,OAAc,CACZ,KAAK,KAAO,EACZ,KAAK,QAAU,EACf,KAAK,QAAU,GAIjB,KAAK,EAAgB,EAAgB,EAA+B,CAClE,GAAI,KAAK,QAAS,MAAO,GAGzB,GAFA,KAAK,MAAQ,EACb,KAAK,SAAW,KAAK,IAAI,EAAO,CAC5B,CAAC,EACH,KAAK,QAAU,OACV,CACL,IAAM,EAAK,KAAK,IAAI,KAAK,KAAK,CAC9B,KAAK,QAAU,EAAA,IAA+B,GAAM,KAAK,QAAA,GAE3D,OAAO,KAAK,UCpBH,GAAqB,EAgClC,SAAgB,EACd,EACA,EACiB,CACjB,OAAO,EAAS,OAAQ,IAAO,EAAE,WAAW,OAAS,aAAe,EAAM,CAI5E,SAAgB,EAAU,EAAwB,CAChD,OAAQ,EAAE,KAAV,CACE,IAAK,YACH,MAAO,aAAa,EAAE,OAAO,WAAW,KAC1C,IAAK,UACH,MAAO,WAAW,EAAE,OAAO,WAAW,OACxC,IAAK,YACH,MAAO,aAAa,EAAE,OAAO,WAAW,OAC1C,IAAK,UACH,MAAO,WAAW,EAAE,OAAO,WAAW,MCzD5C,IAAM,EAAgB,IAAI,IAG1B,SAAgB,GAAe,EAA2B,CACxD,EAAc,IAAI,EAAU,EAAO,CAAE,EAAO,CAI9C,SAAgB,GAAiB,EAAsB,CACrD,OAAO,EAAc,OAAO,EAAI,CAIlC,SAAgB,GAAkC,CAChD,MAAO,CAAC,GAAG,EAAc,QAAQ,CAAC,CAIpC,SAAgB,IAA2B,CACzC,EAAc,OAAO,CCRvB,IAAa,EAAb,KAA2B,CACzB,QAAkB,IAAI,IAEtB,YACE,EACA,EAAgC,EAAE,CAClC,CACA,GAHQ,KAAA,gBAAA,EAGJ,EAAQ,gBAAkB,GAC5B,IAAK,IAAM,KAAK,GAAkB,CAAE,KAAK,IAAI,EAAE,CAEjD,IAAK,IAAM,KAAK,EAAQ,SAAW,EAAE,CAAE,KAAK,IAAI,EAAE,CAIpD,SAAS,EAA2B,CAClC,KAAK,IAAI,EAAO,CAIlB,WAAW,EAAsB,CAC/B,OAAO,KAAK,QAAQ,OAAO,EAAI,CAIjC,MAAsB,CACpB,MAAO,CAAC,GAAG,KAAK,QAAQ,QAAQ,CAAC,CAInC,kBAAkB,EAA+B,CAC/C,KAAK,IAAI,CAAE,KAAM,YAAa,SAAQ,CAAC,CAIzC,aAAa,EAA2C,CACtD,IAAM,EAAI,KAAK,QAAQ,IAAI,aAAa,IAAO,CAC/C,OAAO,GAAG,OAAS,YAAc,EAAE,OAAS,IAAA,GAI9C,aAA+B,CAC7B,IAAM,EAAuB,EAAE,CAC/B,IAAK,IAAM,KAAK,KAAK,QAAQ,QAAQ,CAC/B,EAAE,OAAS,WAAW,EAAI,KAAK,EAAE,OAAO,CAE9C,OAAO,EAGT,IAAY,EAA2B,CACrC,KAAK,QAAQ,IAAI,EAAU,EAAO,CAAE,EAAO,CAEvC,EAAO,OAAS,aAClB,KAAK,gBAAgB,SAAS,EAAO,OAAO,GCrB5C,EAGF,CACF,YAAa,EAAG,IAAM,EAAE,WAAW,EAAsB,CACzD,aAAc,EAAG,IAAM,EAAE,YAAY,EAAuB,CAC5D,aAAc,EAAG,IAAM,EAAE,YAAY,EAAuB,CAC5D,gBAAiB,EAAG,IAAM,EAAE,eAAe,EAA0B,CACrE,eAAgB,EAAG,IAAM,EAAE,cAAc,EAAyB,CAClE,iBAAkB,EAAG,EAAG,IAAY,GAAQ,EAAG,EAA4B,EAAQ,CACpF,CAOD,eAAe,GAAQ,EAA2B,EAA+B,EAAwC,CACvH,GAAM,CAAE,aAAc,EAClB,MAAU,IAAI,EAAO,WAAW,CACpC,GAAU,IAAI,EAAO,WAAW,CAChC,GAAI,CACF,GAAI,EAAQ,gBAAiB,CAC3B,MAAM,EAAQ,gBAAgB,EAAO,CACrC,OAEF,IAAM,EAAW,EAAQ,WAAW,CAAC,KAAM,GAAM,EAAE,KAAO,EAAO,WAAW,CAC5E,GAAI,CAAC,EAAU,MAAU,MAAM,eAAe,EAAO,WAAW,aAAa,CAC7E,IAAM,EAAW,EAAS,UAAY,EAAS,gBAAkB,GACjE,GAAI,EAAE,EAAW,GAAI,MAAU,MAAM,YAAY,EAAO,WAAW,8BAA8B,CACjG,MAAM,EAAQ,cAAc,CAAE,WAAY,EAAS,GAAI,CAAC,CACxD,GAAI,CACF,MAAM,EAAQ,WAAW,CACvB,KAAM,EAAS,OAAS,MAAQ,OAAS,MACzC,KAAM,SACN,MAAO,EAAS,WAChB,WACD,CAAC,OACK,EAAO,CACd,MAAU,MAAM,YAAY,EAAO,WAAW,iDAAkD,CAAE,QAAO,CAAC,SAEpG,CACR,EAAU,OAAO,EAAO,WAAW,GAUvC,SAAgB,GAAc,EAA2B,EAAiC,CACxF,IAAM,EAA+B,EAAE,CAInC,EAA+C,KAC7C,EAA0B,CAC9B,cAAiB,GAAa,EAAK,gBAAgB,EAAI,EAAE,CACzD,UAAW,IAAI,IAChB,CACK,EAAY,GAAsC,EAAK,UAAU,EAAE,MAAQ,EAAE,CAAC,CAC9E,EAAe,GAAyC,CAC5D,EAAY,EAAE,MAAQ,EAAE,CACxB,EAAK,aAAa,CAAC,GAAG,EAAU,CAAC,EAE7B,EAAW,GACf,EAAK,QAAQ,EAAE,MAAQ,CAAE,QAAS,kBAAmB,CAAC,CAClD,EAAU,GAAiC,CAC3C,EAAE,MAAM,EAAK,SAAS,EAAE,KAAK,EAEnC,EAAQ,GAAmB,SAAU,EAAS,CAC9C,EAAQ,GAAsB,YAAa,EAAY,CACvD,EAAQ,GAAmB,QAAS,EAAQ,CAC5C,EAAQ,GAAc,OAAQ,EAAO,CACrC,EAAU,SACF,EAAQ,IAAI,SAAU,EAAS,KAC/B,EAAQ,IAAI,YAAa,EAAY,KACrC,EAAQ,IAAI,QAAS,EAAQ,KAC7B,EAAQ,IAAI,OAAQ,EAAO,CAClC,CAGD,IAAK,IAAM,KAAQ,OAAO,KAAK,EAAS,CAA2B,CACjE,IAAM,EAAU,EAAS,GACnB,EAAQ,EAAK,SAAS,EAAO,GAAY,CAC7C,QAAQ,QAAQ,EAAQ,EAAS,EAAS,EAAQ,CAAC,CAAC,MAAO,GACzD,EAAK,QAAQ,CAAE,QAAS,6BAA6B,IAAQ,QAAO,CAAC,CACtE,EACD,CACF,EAAU,KAAK,EAAM,CAGvB,UAAa,CACX,IAAK,IAAM,KAAY,EAAW,GAAU,ECXhD,IAAM,GAAqB,CAAE,IAAK,EAAG,IAAK,IAAK,CAGzC,EAAqB,aAEd,GAAoB,WAEjC,SAAS,EAAe,EAA2B,CACjD,GAAI,OAAO,GAAO,UAAY,IAAA,YAA4B,EAAA,EAAA,EAAA,iBAAiB,EAAG,CAC5E,MAAU,WAAW,sBAAsB,IAAK,CAElD,GAAI,OAAO,GAAO,UAAY,CAAC,OAAO,SAAS,EAAG,CAAE,MAAU,WAAW,uBAAuB,IAAK,CAIvG,IAAM,GAA0B,EAE1B,EAAqB,IAErB,EAA0B,IAE1B,GAAyB,GAGzB,GAAkB,EAEX,GAAb,KAAmB,CACjB,OAAO,QAAA,QAEP,OACA,SACA,YACA,aACA,cACA,cACA,gBACA,eACA,gBACA,eACA,gBACA,SACA,cAA8C,KAC9C,iBAAoD,KACpD,aAA4C,KAC5C,mBAA6B,GAC7B,iBAA8C,KAE9C,oBAA6C,KAC7C,oBAAoF,KACpF,eAA+E,KAC/E,gBAA0B,GAE1B,eAAyB,EAAA,iBACzB,WACA,mBAAuE,KACvE,uBAAiC,EAGjC,iBAAgE,KAChE,sBAAqE,KACrE,oBAA6C,KAC7C,YACA,UACA,aAMA,qBAAgF,EAAE,CAElF,sBAAgC,EAEhC,qBAA+B,IAAI,IAEnC,aAAuB,GAEvB,mBAA6B,IAAI,EAAA,mBAGjC,UAAqC,KAErC,gBAA2C,KAE3C,WAAwC,KAExC,cAAwB,EACxB,cACA,eAEA,iBAA+C,CAAE,GAAG,EAAA,sBAAuB,CAC3E,gBACA,kBAAmE,KACnE,eACA,cACA,cACA,aACA,aACA,aACA,aACA,oBACA,iBACA,eACA,oBAA8B,IAAI,EAAA,oBAClC,cACA,eAA8C,KAE9C,cAAwB,GAMxB,cAKW,KAEX,kBAA+C,KAC/C,aAA0C,KAC1C,gBACA,kBAA4B,IAAI,GAAmB,GAAQ,KAAK,UAAU,EAAI,CAAC,CAC/E,SACA,iBACA,cACA,mBACA,iBACA,cAEA,iBAA6C,EAAE,CAC/C,kBAA4B,GAE5B,WAAqB,IAAI,EAAA,WAEzB,aAAuB,IAAI,EAAA,kBAE3B,cAAwB,GAExB,iBAA2B,GAE3B,WAAuC,KAEvC,iBAA2B,IAAI,IAC/B,aACA,UAEA,cAAwB,IAAI,EAAA,UAC5B,mBAA6B,GAC7B,cAAwB,EAAA,iBAExB,aAA6C,KAC7C,SACA,QACA,SACA,aAA4C,KAC5C,UACA,iBACA,aAEA,qBAA8C,KAE9C,eAAgD,KAEhD,UAAmC,KACnC,WAAqB,GAOrB,iBACA,gBAAkD,KAClD,KAAiC,KACjC,gBAA+D,KAC/D,cAAgC,GAEhC,gBAA4C,KAE5C,gBAAyC,KACzC,mBAA+C,KAC/C,cAA4C,KAE5C,QAEA,kBAA4B,GAE5B,kBAA4B,GAE5B,YAAqC,KACrC,mBAA6B,GAG7B,YAAY,EAAwB,EAAqD,CAEnF,EAAQ,UAAY,MAAM,EAAe,EAAQ,SAAS,CAC9D,KAAK,UAAY,EACjB,KAAK,QAAU,CAAE,GAAG,EAAS,UAAW,EAAQ,WAAa,cAAe,CAC5E,KAAK,aAAe,EAAQ,cAAgB,QAC5C,KAAK,iBAAmB,EAAQ,YAAc,GAG9C,IAAM,EAAI,EAAQ,UAAY,EAAE,CAChC,KAAK,SAAW,CACd,SAAU,EAAE,UAAY,GACxB,aAAc,EAAE,cAAgB,EAAE,CAClC,cAAe,EAAE,eAAiB,GAClC,gBAAiB,EAAE,iBAAmB,GACtC,QAAS,EAAE,SAAW,GAGtB,mBAAoB,EAAE,oBAAsB,GAC5C,mBAAoB,EAAE,oBAAsB,GAC5C,WAAY,EAAE,YAAc,GAC5B,aAAc,EAAE,cAAgB,EAAE,CAClC,QAAS,EAAE,SAAW,GACtB,QAAS,EAAE,SAAW,GACtB,UAAW,EAAE,WAAa,GAC1B,iBAAkB,EAAE,kBAAoB,GACxC,SAAU,EAAE,UAAY,GACxB,UAAW,EAAE,WAAa,GAC1B,SAAU,EAAE,UAAY,GACxB,KAAM,EAAE,MAAS,EAAQ,MAAM,SAAW,GAC1C,OAAQ,EAAE,QAAU,GACpB,qBAAsB,EAAE,sBAAwB,GAChD,OAAQ,EAAE,QAAU,GACpB,UAAW,EAAE,WAAa,GAC1B,SAAU,EAAE,UAAY,GACxB,WAAY,EAAE,YAAc,GAC5B,OAAQ,EAAE,QAAU,GACpB,OAAQ,EAAE,QAAU,GACpB,cAAe,EAAE,eAAiB,GAClC,aAAc,EAAE,cAAgB,GAChC,eAAgB,EAAE,gBAAkB,GACpC,WAAY,EAAE,YAAc,GAC5B,SAAU,EAAE,UAAY,GACxB,WAAY,EAAE,YAAc,EAAE,CAC9B,0BAA2B,EAAE,2BAA6B,EAAE,CAC7D,CAED,EAAU,MAAM,SAAW,WAC3B,EAAU,MAAM,SAAW,SAC3B,EAAU,SAAW,EACrB,EAAU,MAAM,QAAU,OAG1B,KAAK,YAAc,IAAI,EACvB,KAAK,QAAU,IAAI,GAAa,CAC9B,eAAkB,KAAK,YAAY,SAAS,CAAC,IAAI,MAAQ,KACzD,QAAU,GAAS,KAAK,YAAY,EAAK,CACzC,KAAO,GAAY,KAAK,SAAS,KAAK,cAAe,EAAQ,CAC9D,CAAC,CACF,KAAK,aAAe,IAAI,GAAa,EAAQ,MAAM,CACnD,KAAK,cAAgB,IAAI,GACzB,KAAK,gBAAkB,IAAI,EAAA,gBAC3B,KAAK,cAAgB,IAAI,EAAc,KAAK,gBAAiB,CAAE,QAAS,EAAQ,QAAS,CAAC,CAC1F,KAAK,SAAW,IAAI,EAAA,UAEpB,EAAA,EAAA,2BAA0B,KAAK,gBAAgB,CAE/C,KAAK,gBAAgB,SAAS,IAAI,EAAA,uBAAuB,KAAK,aAAa,CAAC,CAC5E,KAAK,gBAAgB,SAAS,IAAI,EAAA,gBAAgB,KAAK,aAAa,CAAC,CAGrE,KAAK,eAAiB,IAAI,EAAA,gBAC1B,EAAA,EAAA,6BAA4B,KAAK,eAAe,CAChD,KAAK,gBAAkB,IAAI,EAAA,gBAAgB,KAAK,eAAe,CAC/D,KAAK,eAAe,qBAAuB,CAEzC,KAAK,mBAAmB,CACxB,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,CAC5C,KAAK,kBAAkB,EACvB,CACF,KAAK,eAAe,kBAAkB,EAAO,IAAS,CACpD,KAAK,oBAAoB,EAAQ,GAAiC,GAAG,CACrE,KAAK,SAAS,KAAK,EAAyB,EAAK,EAC7C,IAAU,iBAAmB,IAAU,iBAAmB,IAAU,kBAAiB,KAAK,qBAAqB,EACnH,CAEF,KAAK,eAAe,cAAe,GACjC,KAAK,SAAS,WAAa,KAAK,SAAS,aAAa,SAAW,GAAK,KAAK,SAAS,aAAa,SAAS,EAAK,EAAE,CAEnH,KAAK,gBAAkB,IAAI,EAAA,gBAC3B,IAAI,EAAU,GACd,KAAK,gBAAgB,gBAAkB,CACrC,GAAM,CAAE,UAAS,WAAY,KAAK,gBAAgB,UAAU,CACtD,EAAM,GAAG,EAAQ,GAAG,IACtB,IAAQ,IACZ,EAAU,EACV,KAAK,SAAS,KAAK,gBAAiB,CAAE,UAAS,UAAS,CAAC,GACzD,CACF,KAAK,eAAe,mBAAmB,KAAK,gBAAgB,CAC5D,KAAK,eAAe,qBAAsB,GAAQ,KAAK,SAAS,KAAK,gBAAiB,CAAE,MAAK,QAAS,EAAI,IAAM,KAAM,CAAC,CAAC,CACxH,KAAK,eAAe,mBAAmB,EAAQ,IAAc,CACvD,EAAO,OAAS,SAAU,EAAO,SAAS,IAAY,CACrD,KAAK,mBAAmB,EAAQ,EAAU,EAC/C,CACF,KAAK,eAAe,kBAAoB,KAAK,YAAY,SAAS,CAAC,CACnE,KAAK,eAAe,yBAA2B,KAAK,gBAAgB,CAAC,CAEjE,EAAQ,WAAW,OAAS,UAAY,KAAK,SAAS,eACxD,KAAK,eAAe,cAAc,SAAS,CAI7C,KAAK,eAAiB,IAAI,EAAA,eAAe,CACvC,QAAS,KAAK,SAAS,QACvB,YAAa,CAAE,QAAS,KAAK,SAAS,mBAAoB,CAC3D,CAAC,CACF,KAAK,gBAAkB,IAAI,EAAA,gBAAgB,KAAK,eAAe,CAC/D,KAAK,eAAe,aAAa,EAAU,CAC3C,KAAK,eAAe,qBAAuB,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,CAAC,CACxF,KAAK,eAAe,kBAAkB,EAAO,IAAS,CACpD,KAAK,SAAS,KAAK,EAAyB,EAAK,EACjD,CAGF,KAAK,OAAS,IAAI,EAAA,aAAa,EAAU,CACzC,IAAM,EAAO,KAAK,OAAO,WAAW,kBAAkB,CA+KtD,GA9KA,KAAK,SAAW,IAAI,EAAA,SAClB,EAAK,MACL,EAAK,OACL,EAAQ,eAAiB,EACzB,EAAQ,eAAiB,GACzB,EAAQ,aAAe,EACxB,CACD,KAAK,SAAS,aAAa,CACzB,QAAS,EAAQ,SAAW,GAC5B,eAAgB,EAAQ,WAAW,gBAAkB,EACtD,CAAC,CACF,KAAK,cAAc,OAAO,EAAK,MAAO,EAAK,OAAO,CAQlD,KAAK,OAAO,kBAAqB,GAAY,CAC3C,GAAI,EAAQ,OAAS,GAAK,EAAQ,QAAU,EAAG,OAC/C,IAAM,EAAW,KAAK,SAAS,SAAS,CACxC,KAAK,SAAS,OAAO,EAAQ,MAAO,EAAQ,OAAO,CACnD,KAAK,cAAc,OAAO,EAAQ,MAAO,EAAQ,OAAO,CAIxD,KAAK,wBAAwB,EAAS,EAIxC,KAAK,cAAgB,KAAK,oBAAoB,KAAK,QAAQ,UAAU,CACrE,KAAK,aAAe,IAAI,EAAA,aACpB,EAAQ,MAAM,UAAY,IAAO,KAAK,aAAa,WAAW,GAAM,CACxE,KAAK,UAAY,IAAI,EAAA,UACrB,KAAK,UAAU,UAAU,KAAK,aAAa,CAC3C,KAAK,cAAc,UAAU,KAAK,aAAa,CAC/C,KAAK,SAAW,IAAI,EAAA,SAGpB,KAAK,iBAAmB,IAAI,EAAA,iBAC5B,KAAK,iBAAiB,UAAU,KAAK,aAAa,CAC9C,EAAQ,WAAW,MACrB,KAAK,iBAAiB,QAAQ,EAAQ,UAAU,KAAK,CAKvD,KAAK,iBAAiB,aAAa,EAAU,IAAU,CACrD,GAAI,IAAa,MAAQ,EAAO,CAC9B,IAAM,EAAO,KAAK,YAAY,SAAS,CACjC,EAAM,EAAW,EAAK,OAAS,EAAK,GAAY,IAAA,GAGtD,KAAK,YAAY,YAAY,GAAO,KAAK,CAIzC,IAAM,EAAK,KAAK,SAAS,UAAU,CAC7B,EAAS,EAAM,GAAK,EAAG,UAAU,GAAK,EAAM,GAAK,EAAG,UAAU,EAAI,EAAG,UAAU,OACjF,GAAO,GAAU,KAAK,SAAS,iBACjC,KAAK,iBAAiB,KAAK,EAAO,EAAK,KAAK,aAAa,UAAU,CAAE,KAAK,qBAAqB,CAAE,CAC/F,UAAW,EAAW,EAAI,EAAK,EAAW,IAAI,MAAQ,IAAA,GACtD,WAAY,EAAG,WACf,KAAM,EAAG,UACT,WAAA,EAAA,EAAA,eAAyB,EAAK,CAC/B,CAAC,CAEF,KAAK,iBAAiB,MAAM,CAK1B,KAAK,cAAc,UAAU,EAC/B,KAAK,cAAc,WACjB,KAAK,SAAS,UAAU,CACxB,GAAO,KACP,EACA,KAAK,aAAa,UAAU,CAC7B,CAIH,KAAK,SAAS,KAAK,gBAAiB,CAAE,QAAO,MAAK,WAAU,CAAC,MAE7D,KAAK,iBAAiB,MAAM,CAC5B,KAAK,YAAY,YAAY,KAAK,CAClC,KAAK,SAAS,KAAK,iBAAkB,EAAE,CAAC,EAE1C,CAGF,KAAK,YAAc,IAAI,EAAA,YACvB,KAAK,YAAY,aAAa,KAAK,QAAQ,UAAU,CACrD,KAAK,YAAY,UAAU,KAAK,aAAa,CAG7C,KAAK,UAAY,IAAI,EAAA,UACjB,EAAQ,WAAW,KAAK,UAAU,UAAU,EAAQ,UAAU,CAClE,KAAK,eAAiB,IAAI,EAAA,eAC1B,KAAK,cAAgB,IAAI,EAAA,sBACzB,KAAK,cAAgB,IAAI,EAAA,sBACzB,KAAK,aAAe,IAAI,EAAA,qBACxB,KAAK,aAAe,IAAI,EAAA,qBACxB,KAAK,aAAe,IAAI,EAAA,qBAGxB,KAAK,aAAe,IAAI,EAAA,aACxB,KAAK,aAAa,WAAW,KAAK,SAAS,aAAa,CACxD,KAAK,cAAgB,IAAI,EAAA,cACzB,KAAK,cAAc,UAAU,KAAK,aAAa,CAC/C,KAAK,eAAiB,IAAI,EAAA,eAC1B,KAAK,gBAAkB,IAAI,EAAA,gBAGvB,EAAQ,cACV,KAAK,cAAc,UAAU,CAC3B,QAAS,EAAQ,cAAc,SAAW,GAC1C,MAAO,EAAQ,cAAc,MAC7B,UAAW,EAAQ,cAAc,UACjC,UAAW,EAAQ,cAAc,UAClC,CAAC,CACO,EAAQ,UAAU,gBAAkB,IAC7C,KAAK,cAAc,WAAW,GAAK,CAIjC,EAAQ,UAAY,KAAK,SAAS,UACpC,KAAK,SAAS,YAAY,GAAK,CAIjC,KAAK,SAAW,IAAI,EAAA,SAGpB,KAAK,iBAAmB,IAAI,EAAA,iBAC5B,KAAK,iBAAiB,OAAO,EAAU,CACvC,KAAK,iBAAiB,UAAU,KAAK,aAAa,CAClD,KAAK,cAAgB,IAAI,EAAA,cACzB,KAAK,cAAc,OAAO,EAAU,CAChC,EAAQ,aAAa,KAAK,iBAAiB,EAAQ,YAAY,CAC/D,EAAQ,mBAAkB,KAAK,kBAAA,EAAA,EAAA,sBAAwC,EAAQ,iBAAiB,EAChG,EAAQ,cAAc,KAAK,WAAW,WAAW,GAAK,CACtD,EAAQ,QAAQ,KAAK,aAAa,SAAS,EAAW,EAAQ,OAAO,CAAC,CAE1E,KAAK,WAAW,aAAc,GAAM,KAAK,YAAY,EAAE,CAAC,CACpD,EAAQ,gBAAkB,KAAO,KAAK,cAAgB,IACtD,EAAQ,eAAe,KAAK,mBAAmB,EAAQ,cAAc,CAGzE,KAAK,gBAAkB,IAAI,EAAA,gBAAgB,CACzC,WAAa,GAAU,CACrB,IAAM,EAAU,KAAK,SAAS,UAAU,CAAC,SAAW,KAAK,SAAS,UAAU,CAAC,WAC7E,KAAK,SAAS,SAAS,EAAQ,EAAQ,CACvC,KAAK,yBAAyB,EAEhC,KAAO,GAAU,CACf,IAAM,EAAa,KAAK,SAAS,UAAU,CAAC,UAAU,MACtD,KAAK,SAAS,KAAK,EAAO,EAAa,EAAE,CACzC,KAAK,yBAAyB,EAEhC,cAAiB,CACf,KAAK,SAAS,SAAS,KAAU,CACjC,KAAK,yBAAyB,EAEhC,YAAe,CACb,KAAK,SAAS,aAAa,CAC3B,KAAK,yBAAyB,EAEhC,eAAkB,KAAK,YAAY,CACpC,CAAC,CACF,KAAK,gBAAgB,WAAW,KAAK,SAAS,SAAS,CAGnD,EAAQ,OAAS,GAAO,CAC1B,KAAK,KAAO,IAAI,EAAU,EAAW,CACnC,WAAc,KAAK,eAAiB,KAAK,YAAY,QAAU,GAC/D,cAAiB,KAAK,iBAAmB,GACzC,cAAiB,KAAK,QAAQ,WAAa,cAC3C,SAAY,KAAK,gBAAgB,CACjC,iBAAoB,KAAK,SAAS,UAAU,CAAC,aAC7C,YAAc,GAAU,KAAK,YAAY,EAAM,CAC/C,WAAa,IAAA,EAAA,EAAA,oBAA4B,KAAK,cAAgB,QAAS,CAAE,UAAW,SAAU,UAAW,QAAS,CAAE,KAAK,UAAU,CACjI,EAAO,EAAqB,EAAO,EAAO,IAC3C,CACD,QAAU,GAAS,KAAK,iBAAiB,EAAK,CAC/C,CAAE,EAAQ,MAAM,OAAO,CACxB,IAAK,IAAM,IAAS,CAAC,aAAc,kBAAmB,eAAgB,kBAAkB,CACtF,KAAK,SAAS,GAAG,MAAa,KAAK,MAAM,QAAQ,IAAU,aAAa,CAAC,CA+F7E,GAvFA,KAAK,gBAAmB,GAAqB,CAC3C,GAAI,CAAC,KAAK,gBAAiB,OAC3B,IAAM,EAAS,SAAS,cACpB,QAAU,IAAW,KAAK,WAAa,CAAC,KAAK,UAAU,SAAS,EAAO,GAIvE,MAAW,EAAO,UAAY,SAAW,EAAO,UAAY,YAAc,EAAO,qBAIjF,KAAU,IAAW,KAAK,WAAa,EAAO,QAAQ,8DAA8D,EAExH,IAAI,KAAK,MAAQ,KAAK,SAAS,WAAa,EAAE,MAAQ,KAAO,EAAE,MAAQ,MAAQ,CAAC,EAAE,SAAW,CAAC,EAAE,SAAW,CAAC,EAAE,OAAQ,CACpH,EAAE,gBAAgB,CAClB,KAAK,KAAK,QAAQ,EAAE,MAAQ,IAAM,EAAI,GAAG,CACzC,OAEE,KAAK,gBAAgB,UAAU,EAAE,GACnC,EAAE,gBAAgB,CAClB,KAAK,MAAM,WAAW,IAG1B,OAAO,iBAAiB,UAAW,KAAK,gBAAgB,CAGxD,KAAK,iBAAmB,IAAI,EAAA,iBAC5B,KAAK,iBAAiB,UAAU,KAAK,aAAa,CAClD,KAAK,UAAU,6BACb,KAAK,iBAAiB,WAAW,CAAG,KAAK,iBAAiB,UAAU,CAAG,KAAK,CAG9E,KAAK,aAAe,IAAI,EAAA,aAExB,KAAK,aAAa,iBAAkB,GAAO,KAAK,iBAAiB,EAAG,CAAC,CACrE,KAAK,aAAa,qBAAuB,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,CAAC,CACtF,KAAK,aAAa,GAAG,YAAc,GAAU,CAC3C,KAAK,SAAS,KAAK,iBAAkB,EAAa,EAAM,CAAC,CAEzD,KAAK,SAAS,KAAK,aAAc,CAAE,MAAO,YAAa,QAAS,EAAM,GAAI,MAAO,EAAM,MAAO,QAAS,EAAM,QAAS,CAAC,EACvH,CACF,KAAK,aAAa,GAAG,QAAU,GAAU,CACvC,KAAK,SAAS,KAAK,WAAY,EAAa,EAAM,CAAC,EACnD,CACF,KAAK,aAAa,GAAG,UAAY,GAAO,CACtC,KAAK,SAAS,KAAK,cAAe,CAAE,KAAI,CAAC,EACzC,CACF,KAAK,aAAa,GAAG,UAAY,GAAU,CACzC,KAAK,SAAS,KAAK,eAAgB,EAAa,EAAM,CAAC,EACvD,CACF,KAAK,aAAa,GAAG,UAAY,GAAU,CACzC,KAAK,SAAS,KAAK,cAAe,EAAa,EAAM,CAAC,EACtD,CAGF,KAAK,oBAAsB,IAAI,EAAA,oBAC/B,KAAK,oBAAoB,qBAAuB,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,CAAC,CAC7F,KAAK,oBAAoB,kBAAoB,KAAK,YAAY,SAAS,CAAC,CACxE,KAAK,oBAAoB,GAAG,QAAU,GAAM,CAC1C,KAAK,SAAS,KAAK,kBAAqC,CAAE,GAAI,EAAE,GAAI,OAAQ,EAAE,OAAQ,UAAW,EAAE,UAAW,CAAC,EAC/G,CACF,KAAK,oBAAoB,GAAG,UAAY,GAAO,CAC7C,KAAK,SAAS,KAAK,qBAAwC,CAAE,KAAI,CAAC,EAClE,CAGF,KAAK,iBAAmB,IAAI,EAAA,iBAC5B,KAAK,iBAAiB,qBAAuB,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,CAAC,CAC1F,KAAK,iBAAiB,kBAAoB,KAAK,YAAY,SAAS,CAAC,CACrE,KAAK,iBAAiB,GAAG,QAAU,GAAM,CACvC,KAAK,SAAS,KAAK,eAAkC,CAAE,GAAI,EAAE,GAAI,UAAW,EAAE,UAAW,CAAC,EAC1F,CACF,KAAK,iBAAiB,GAAG,UAAY,GAAO,CAC1C,KAAK,SAAS,KAAK,kBAAqC,CAAE,KAAI,CAAC,EAC/D,CAGF,KAAK,cAAgB,IAAI,EAAA,cACzB,KAAK,cAAc,GAAG,cAAgB,GAAU,CACzC,KAAK,kBAAkB,KAAK,SAAS,KAAK,cAAe,CAAE,QAAO,CAAC,EACxE,CAGF,KAAK,eAAiB,IAAI,EAAA,eAG1B,KAAK,mBAAqB,IAAI,EAAA,mBAAmB,EAAU,CACvD,KAAK,SAAS,QAAS,CAEzB,IAAM,EAAe,IAAI,GACzB,KAAK,mBAAmB,cACtB,IAAI,EAAA,YACD,EAAQ,IAAW,CACd,GAAQ,KAAK,SAAS,SAAS,EAAO,CAMtC,EAAa,KAAK,EAAQ,EAAQ,KAAK,QAAQ,YAAc,GAAM,EAAI,IACzE,KAAK,QAAQ,UAAY,GACzB,KAAK,SAAS,cAAc,EAAO,EAGrC,KAAK,yBAAyB,MAE1B,EAAa,OAAO,CAC3B,CACF,CAYH,GARA,KAAK,mBAAmB,mBAAqB,CAC3C,KAAM,KAAK,SAAS,UAAU,CAAC,UAC/B,YAAa,KAAK,aAAa,CAC/B,KAAM,CACJ,MAAO,KAAK,uBAAuB,CAAG,KAAK,cAAgB,EAC3D,OAAQ,CAAC,KAAK,gBAAgB,sBAAsB,CACrD,CACF,EAAE,CACC,KAAK,SAAS,QAAS,CACzB,KAAK,mBAAmB,eACtB,IAAI,EAAA,aAAa,EAAO,IAAY,CAElC,KAAK,SAAS,KAAK,EAAO,EAAU,KAAK,SAAS,UAAU,CAAC,UAAU,EAAE,CACzE,KAAK,yBAAyB,EAC9B,CACH,CAMD,IAAM,EAAW,IAAI,EAAA,gBAClB,GAAW,CACV,KAAK,QAAQ,UAAY,GACzB,KAAK,SAAS,gBAAgB,EAAO,CACrC,KAAK,mBAAmB,CACxB,KAAK,OAAO,eAAe,CAC3B,KAAK,oBAAoB,EAE1B,GAAW,CACV,IAAM,EAAK,KAAK,SAAS,UAAU,CAAC,UAAU,MAI9C,KAAK,SAAS,KAAK,EAAI,EAAS,EAAG,EAAK,EAAE,CAC1C,KAAK,yBAAyB,EAEjC,CACD,KAAK,mBAAmB,mBACtB,MACM,KAAK,SAAS,UAAU,CAC7B,GAAS,CACJ,IAAS,SACX,KAAK,QAAQ,UAAY,GACzB,KAAK,yBAAyB,EAE9B,KAAK,YAAY,EAGtB,CAmCH,GAjCI,KAAK,SAAS,WAChB,KAAK,mBAAmB,oBAAoB,KAAK,iBAAiB,CAEhE,KAAK,SAAS,WAChB,KAAK,mBAAmB,kBACtB,KAAK,oBAGE,CAAE,GAAG,KAAK,SAAS,UAAU,CAAE,KAAM,KAAK,gBAAgB,CAAE,EACpE,CACD,KAAK,mBAAmB,sBAAuB,GAAO,KAAK,SAAS,KAAK,qBAAsB,CAAE,KAAI,CAAC,CAAC,CACvG,KAAK,mBAAmB,uBAAuB,EAAI,IAAQ,CACrD,KAAK,eAAe,OAAO,EAAG,EAAE,KAAK,SAAS,KAAK,qBAAsB,CAAE,KAAI,EAAG,EAAI,EAAG,EAAG,EAAI,EAAG,CAAC,EACxG,EAGJ,KAAK,mBAAmB,qBAAqB,EAAM,IAC5C,KAAK,SAAS,aAAa,mBAAmB,EACnD,KAAK,SAAS,KAAK,mBAAoB,KAAK,UAAU,EAAM,EAAI,CAAC,CAC1D,IAFqD,GAG5D,CAEF,KAAK,mBAAmB,WAAW,KAAK,SAAS,oBAAsB,KAAK,SAAS,UAAU,CAC/F,KAAK,mBAAmB,sBAAsB,KAAK,mBAAqB,GAAQ,CAC9E,IAAM,GAAA,EAAA,EAAA,UAAiB,EAAI,EAAG,KAAK,SAAS,UAAU,CAAC,CACnD,OAAO,SAAS,EAAM,EAAE,KAAK,SAAS,KAAK,eAAgB,CAAE,QAAO,EAAG,EAAI,EAAG,EAAG,EAAI,EAAG,CAAC,EAC7F,CACE,KAAK,SAAS,SAChB,KAAK,mBAAmB,kBACtB,KAAK,oBACE,CAAE,GAAG,KAAK,SAAS,UAAU,CAAE,KAAM,KAAK,gBAAgB,CAAE,EACpE,CAEC,KAAK,SAAS,OAAQ,CACxB,IAAM,EAAY,IAAI,EAAA,iBAAiB,KAAK,iBAAoB,KAAK,SAAS,UAAU,CAAC,CACzF,KAAK,mBAAmB,oBAAoB,EAAU,CAIxD,IAAM,EAAa,IAAI,EAAA,kBACvB,EAAW,cACH,KAAK,mBAAmB,EAC7B,EAAS,IAAS,KAAK,aAAa,EAAS,EAAK,CACpD,CACD,KAAK,mBAAmB,qBAAqB,EAAW,CAGxD,KAAK,mBAAmB,mBAAoB,GAAQ,CAClD,IAAM,EAAO,KAAK,gBAAgB,CAClC,GAAI,EAAK,SAAW,EAAG,OACvB,IAAM,EAAK,KAAK,SAAS,UAAU,CAC7B,EAAU,EAAG,SAAW,EAAG,WAC3B,EAAM,KAAK,OAAO,EAAG,OAAS,EAAI,GAAK,EAAQ,CACjD,EAAM,GAAK,GAAO,EAAK,SACvB,KAAK,cAAc,UAAU,EAAI,KAAK,cAAc,gBAAgB,GAAK,EAC3E,KAAK,cAAc,OAAO,CAE1B,KAAK,cAAc,IAAI,EAAK,GAAM,EAAK,KAAK,aAAa,UAAU,CAAC,CAEtE,KAAK,cAAc,WACjB,KAAK,SAAS,UAAU,CACxB,KAAK,YAAY,SAAS,CAAC,IAAQ,KACnC,EACA,KAAK,aAAa,UAAU,CAC7B,CACD,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,GAC5C,CAGF,KAAK,mBAAmB,uBACrB,GAAS,KAAK,SAAS,aAAa,oBAAoB,EAAI,KAAK,SAAS,aAAa,oBAAoB,CACxG,KAAK,oBAAoB,SAAS,EAAK,KAAK,SAAS,UAAU,CAAC,CAChE,MACH,EAAQ,IAAQ,KAAK,SAAS,KAAK,oBAAqB,CAAU,SAA8D,EAAG,EAAI,EAAG,EAAG,EAAI,EAAG,CAAC,KAEhJ,KAAK,SAAS,aAAa,oBAAoB,CACtD,CACD,KAAK,mBAAmB,gBAAiB,GAAQ,CAC/C,IAAM,EAAO,KAAK,gBAAgB,CAC5B,EAAS,KAAK,SAAS,aAAa,oBAAoB,CAAG,KAAK,oBAAoB,SAAS,EAAK,KAAK,SAAS,UAAU,CAAC,CAAG,KACpI,GAAI,EAAQ,CACV,KAAK,SAAS,KAAK,oBAAqB,CAAE,SAAQ,EAAG,EAAI,EAAG,EAAG,EAAI,EAAG,CAAC,CACvE,OAGF,GADA,KAAK,SAAS,KAAK,QAAS,CAAE,EAAG,EAAI,EAAG,EAAG,EAAI,EAAG,CAAC,CAC/C,EAAK,SAAW,EAAG,OAGvB,IAAM,GAAA,EAAA,EAAA,aAAkB,EAAI,EAAG,KAAK,SAAS,UAAU,CAAC,CACpD,EAAM,GAAK,GAAO,EAAK,QAC3B,KAAK,SAAS,KAAK,WAAY,CAAE,IAAK,EAAK,GAAM,SAAU,EAAK,MAAO,EAAK,CAAC,EAC7E,CAEF,KAAK,mBAAmB,qBAAuB,CAC7C,GAAI,KAAK,aAAc,CACrB,KAAK,gBAAgB,GAAM,CAC3B,OAEF,GAAI,KAAK,SAAS,SAAW,KAAK,eAAe,iBAAiB,CAAE,CAClE,KAAK,eAAe,eAAe,CACnC,KAAK,SAAS,KAAK,aAAc,CAAE,QAAS,YAAa,CAAC,CAC1D,OAEE,KAAK,cAAc,UAAU,GAC/B,KAAK,cAAc,OAAO,CAC1B,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,GAE9C,CAEF,KAAK,mBAAmB,sBAClB,KAAK,SAAS,SAAW,KAAK,eAAe,iBAAiB,EAChE,KAAK,eAAe,gBAAgB,CAC7B,IAEF,GACP,CAIF,KAAK,mBAAmB,mBAAmB,CACzC,MAAQ,GAAQ,CACd,KAAK,eAAe,MAAM,EAAK,KAAK,SAAS,UAAU,CAAE,KAAK,gBAAgB,CAAC,EAEjF,KAAO,GAAQ,CACb,KAAK,eAAe,OAAO,EAAK,KAAK,SAAS,UAAU,CAAE,KAAK,gBAAgB,CAAC,EAElF,QAAW,CACT,KAAK,eAAe,KAAK,EAE5B,CAAC,CAKF,IAAM,EAAU,GAAkC,CAChD,IAAM,EAAI,KAAK,SAAS,UAAU,CAAC,UACnC,MAAO,CACL,EAAG,KAAK,IAAI,EAAE,EAAG,KAAK,IAAI,EAAE,EAAI,EAAE,MAAO,EAAI,EAAE,CAAC,CAChD,EAAG,KAAK,IAAI,EAAE,EAAG,KAAK,IAAI,EAAE,EAAI,EAAE,OAAQ,EAAI,EAAE,CAAC,CAClD,EAEH,KAAK,mBAAmB,qBAAqB,CAC3C,MAAQ,GAAQ,KAAK,oBAAoB,MAAM,EAAO,EAAI,CAAC,CAC3D,KAAO,GAAQ,KAAK,oBAAoB,OAAO,EAAO,EAAI,CAAC,CAC3D,QAAW,CACT,IAAM,EAAM,KAAK,oBAAoB,KAAK,CAC1C,GAAI,GAAO,KAAK,aAAc,CAE5B,GAAI,EAAI,GAAK,EAAI,GAAK,GAAiB,OACvC,KAAK,UAAU,EAAI,CACnB,KAAK,gBAAgB,GAAM,CAC3B,OAEF,GAAI,CAAC,GAAO,CAAC,KAAK,SAAS,SAAU,OAErC,IAAM,EAAK,CAAE,GAAG,KAAK,SAAS,UAAU,CAAE,KAAM,KAAK,gBAAgB,CAAE,CACnE,EAAI,QAAS,KAAK,eAAe,kBAAkB,CAAE,EAAG,EAAI,GAAI,EAAG,EAAI,GAAI,CAAE,EAAG,CAC/E,KAAK,eAAe,aAAa,EAAK,EAAG,CAC9C,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,EAE9C,WAAc,CACZ,KAAK,oBAAoB,QAAQ,CACjC,KAAK,gBAAgB,GAAM,EAE9B,CAAC,CAIF,KAAK,mBAAmB,wBAAyB,GAAc,CAC7D,KAAK,OAAO,cAAc,EAAY,EAAA,UAAU,MAAQ,EAAA,UAAU,QAAQ,EAC1E,CACF,KAAK,mBAAmB,QAAQ,CAE5B,EAAQ,WAAa,IAAA,IAAa,EAAQ,WAAa,MAAM,KAAK,YAAY,EAAQ,SAAS,CAC/F,EAAQ,iBAAgB,KAAK,mBAAqB,IAGtD,KAAK,mBAAmB,CACxB,KAAK,OAAO,OAAO,CAKrB,QAAQ,EAAwB,CAC9B,KAAK,WAAW,EAAM,GAAM,CAO9B,WAAmB,EAAkB,EAA0B,CAG7D,KAAK,YAAc,KACnB,KAAK,kBAAkB,CACvB,KAAK,QAAQ,OAAO,CACpB,KAAK,YAAY,QAAQ,KAAK,YAAY,EAAK,CAAC,CAChD,KAAK,iBAAiB,QAAQ,KAAK,YAAY,SAAS,CAAC,CACzD,KAAK,iBAAmB,KACxB,KAAK,cAAc,iBAAiB,CAMpC,KAAK,QAAQ,UAAY,KAAK,iBAGzB,GAAW,KAAK,aAAa,iBAAiB,CACnD,KAAK,iBAAiB,KAAK,YAAY,SAAS,CAAC,CAE7C,EAAK,OAAS,GAChB,KAAK,iBAAiB,SAAS,EAAK,EAAK,OAAS,GAAG,MAAM,CAE7D,KAAK,wBAAwB,GAAK,CAClC,KAAK,SAAS,KAAK,aAAc,CAAE,OAAQ,EAAK,OAAQ,CAAC,CAS3D,YAAY,EAAyB,CACnC,GAAI,KAAK,cAAe,MAAO,GAG/B,IAAI,EAAuB,KAC3B,GAAI,KAAK,WAAY,CACnB,IAAM,EAAQ,KAAK,WAAW,IAAI,KAC5B,EAAO,IAAI,IACX,EAAQ,EACX,OAAQ,GAAM,EAAW,EAAE,GAAK,IAAU,IAAA,IAAa,EAAE,KAAO,IAAU,CAAC,EAAK,IAAI,EAAE,KAAK,EAAI,EAAK,IAAI,EAAE,KAAK,CAAC,CAChH,MAAM,EAAG,IAAM,EAAE,KAAO,EAAE,KAAK,CAClC,KAAK,WAAa,CAAC,GAAG,EAAO,GAAG,KAAK,WAAW,CAChD,IAAM,EAAO,KAAK,YAAY,KAAK,WAAW,CAC9C,EAAQ,EAAM,OACd,EAAO,EAAO,EAAM,OAAQ,GAAM,EAAK,EAAE,KAAK,CAAC,CAAG,EAIpD,IAAM,EAAQ,KAAK,gBAAgB,CAC7B,EAAY,KAAK,IAAI,EAAG,KAAK,IAAI,EAAM,OAAS,EAAG,KAAK,MAAM,KAAK,SAAS,UAAU,CAAC,aAAa,KAAK,CAAC,CAAC,CAC3G,EAAW,EAAM,IAAY,KAC7B,EAAQ,KAAK,YAAY,YAAY,EAAK,CAChD,GAAI,IAAU,EAAG,OAAO,GAAS,EACjC,IAAM,EAAO,KAAK,YAAY,SAAS,CAQvC,GAPA,KAAK,iBAAiB,QAAQ,EAAK,CACnC,KAAK,iBAAmB,KACxB,KAAK,cAAc,iBAAiB,CAEhC,KAAK,cAAc,UAAU,EAAE,KAAK,cAAc,OAAO,CAC7D,KAAK,eAAe,KAAK,CACzB,KAAK,iBAAiB,EAAK,CACvB,IAAa,IAAA,GAAW,CAC1B,IAAM,EAAM,KAAK,OAAA,EAAA,EAAA,qBAA0B,EAAU,KAAK,gBAAgB,CAAC,CAAC,CAC5E,KAAK,SAAS,YAAY,EAAM,EAAU,CAG5C,OADA,KAAK,yBAAyB,CACvB,GAAS,EAOlB,cAAsB,EAAuB,CAC3C,IAAM,EAAO,KAAK,YAAc,KAAK,YAAY,SAAS,CACpD,EAAQ,EAAK,IAAI,KACnB,EAAO,EACX,KAAO,IAAU,IAAA,IAAa,EAAO,EAAK,QAAU,EAAK,GAAM,KAAO,GAAO,IAG7E,GAAI,IAAS,GAAK,IAAS,EAAK,OAAQ,CACtC,KAAK,QAAQ,EAAK,CAClB,OAEF,IAAM,EAAS,KAAK,SAAS,SAAS,CACtC,KAAK,YAAY,QAAQ,KAAK,YAAY,EAAK,MAAM,EAAG,EAAK,CAAC,OAAO,EAAK,CAAC,CAAC,CAC5E,IAAM,EAAS,KAAK,YAAY,SAAS,CACzC,KAAK,iBAAiB,QAAQ,EAAO,CACrC,KAAK,iBAAmB,KACxB,KAAK,cAAc,iBAAiB,CAEpC,KAAK,iBAAiB,EAAO,CACzB,EAAO,OAAS,GAAG,KAAK,iBAAiB,SAAS,EAAO,EAAO,OAAS,GAAG,MAAM,CACtF,KAAK,wBAAwB,EAAO,CACpC,KAAK,SAAS,KAAK,aAAc,CAAE,OAAQ,EAAO,OAAQ,CAAC,CAS7D,iBAAiB,EAA8B,EAAuC,CACpF,KAAK,kBAAoB,GACzB,KAAK,kBAAoB,IAAW,KACpC,KAAK,QAAQ,UAAU,EAAQ,GAAS,SAAS,CACjD,KAAK,cAAc,CAIrB,iBAAmC,CAEjC,OADI,KAAK,cAAsB,QAAQ,QAAQ,EAAE,CAC1C,KAAK,QAAQ,UAAU,CAIhC,gBAA0B,CACxB,OAAO,KAAK,QAAQ,WAAW,EAAI,KAAK,QAAQ,SAAS,CAG3D,kBAA4B,CAC1B,OAAO,KAAK,QAAQ,WAAW,CASjC,cAA6B,CACvB,KAAK,oBAAsB,CAAC,KAAK,QAAQ,WAAW,GACxD,KAAK,mBAAqB,GAC1B,mBAAqB,CAEnB,GADA,KAAK,mBAAqB,GACtB,KAAK,eAAiB,CAAC,KAAK,QAAQ,WAAW,EAAI,EAAoB,KAAK,QAAQ,UAAU,CAAE,OACpG,GAAM,CAAE,OAAM,MAAO,KAAK,SAAS,UAAU,CAAC,aAC9C,KAAK,QAAQ,UAAU,KAAK,IAAI,EAAG,KAAK,MAAM,EAAK,CAAC,CAAE,KAAK,IAAI,GAAwB,EAAK,EAAK,CAAC,EAClG,EAIJ,iBAAyB,EAAmB,EAAoC,CAC9E,GAAI,KAAK,kBAAmB,OAE5B,IAAM,GAAA,EAAA,EAAA,gBAAyB,EAAK,CAC9B,EAAc,EAAQ,mBACtB,EAAY,KAAK,gBAEvB,GAAI,CAAC,GAAe,IAAc,OAAA,EAAA,EAAA,cAAqB,EAAU,GAAK,KAAM,CACtE,KAAK,mBAAmB,KAAK,QAAQ,UAAU,KAAK,CACxD,KAAK,kBAAoB,GACzB,OAEF,IAAM,EAAS,KAAK,cACpB,KAAK,QAAQ,WAAW,EAAQ,IAAU,EAAY,KAAK,EAAS,EAAQ,EAAW,EAAQ,EAAM,CAAE,EAAS,CAChH,KAAK,kBAAoB,GACzB,KAAK,cAAc,CAGrB,UAAU,EAAoB,CAC5B,GAAI,KAAK,cAAe,CACtB,KAAK,cAAc,KAAK,UAAU,EAAI,CACtC,OAEF,GAAI,CAAC,KAAK,QAAQ,EAAK,SAAS,CAAE,OAGlC,IAAM,EAAS,KAAK,oBAAsB,KAAK,SAAS,SAAS,CACjE,KAAK,YAAY,UAAU,EAAI,CAC/B,IAAM,EAAO,KAAK,YAAY,SAAS,CACvC,KAAK,iBAAiB,QAAQ,EAAK,CACnC,KAAK,iBAAmB,KAGxB,KAAK,qBAAqB,EAAM,EAAK,OAAS,EAAE,CAChD,KAAK,wBAAwB,EAAO,CAQtC,WAAW,EAAuB,CAChC,GAAI,EAAK,SAAW,EAAG,OACvB,GAAI,KAAK,cAAe,CACtB,IAAK,IAAM,KAAO,EAAM,KAAK,cAAc,KAAK,UAAU,EAAI,CAC9D,OAGF,GADA,EAAO,EAAK,OAAQ,GAAQ,KAAK,QAAQ,EAAK,SAAS,CAAC,CACpD,EAAK,SAAW,EAAG,OACvB,IAAM,EAAS,KAAK,SAAS,SAAS,CAChC,EAAe,KAAK,YAAY,WAAW,CAAG,EACpD,IAAK,IAAM,KAAO,EAChB,KAAK,YAAY,UAAU,EAAI,CAEjC,KAAK,iBAAmB,KACxB,KAAK,qBAAqB,KAAK,YAAY,SAAS,CAAE,EAAa,CACnE,KAAK,iBAAiB,QAAQ,KAAK,YAAY,SAAS,CAAC,CACzD,KAAK,wBAAwB,EAAO,CAGtC,cAAc,EAAoB,CAChC,GAAI,KAAK,cAAe,CACtB,KAAK,cAAc,KAAK,cAAc,EAAI,CAC1C,OAEF,GAAI,CAAC,KAAK,QAAQ,EAAK,SAAS,CAAE,CAChC,KAAK,iBAAiB,SAAS,EAAI,MAAM,CACzC,KAAK,gBAAgB,CACrB,OAEF,KAAK,YAAY,cAAc,EAAI,CACnC,KAAK,iBAAiB,SAAS,EAAI,MAAM,CAIrC,KAAK,QAAQ,YAAc,eAAiB,KAAK,QAAQ,YAAc,QACpE,KAAK,QAAQ,YAAc,QAAU,KAAK,QAAQ,YAAc,OAChE,KAAK,QAAQ,YAAc,iBAChC,KAAK,iBAAmB,MAI1B,IAAM,EAAO,KAAK,YAAY,SAAS,CACvC,KAAK,qBAAqB,EAAM,EAAK,OAAS,EAAE,CAChD,KAAK,gBAAgB,CAIvB,sBAAsB,EAA8D,CAClF,GAAI,KAAK,cAAe,CACtB,KAAK,cAAc,KAAK,sBAAsB,EAAK,CAC/C,OAAO,SAAS,EAAK,MAAM,GAAE,KAAK,cAAc,MAAQ,CAAE,MAAO,EAAK,MAAO,EACjF,OAGF,IAAM,EAAO,KAAK,WAAa,KAAK,YAAY,KAAK,WAAW,CAAG,KACnE,GAAI,GAAQ,CAAC,EAAK,EAAK,KAAK,CAAE,CAC5B,KAAK,iBAAiB,SAAS,EAAK,MAAM,CAC1C,KAAK,gBAAgB,CACrB,OAEF,KAAK,YAAY,sBAAsB,EAAK,CAC5C,KAAK,iBAAiB,SAAS,EAAK,MAAM,CAE1C,IAAM,EAAO,KAAK,WACZ,EAAS,KAAK,YAAY,SAAS,CAAC,KAAK,YAAY,WAAW,CAAG,GACrE,GAAQ,GAAU,EAAK,OAAS,GAAK,EAAK,EAAK,OAAS,GAAG,OAAS,EAAO,OAAM,EAAK,EAAK,OAAS,GAAK,GACzG,KAAK,QAAQ,YAAc,eAAiB,KAAK,QAAQ,YAAc,QACpE,KAAK,QAAQ,YAAc,QAAU,KAAK,QAAQ,YAAc,OAChE,KAAK,QAAQ,YAAc,iBAChC,KAAK,iBAAmB,MAE1B,IAAM,EAAO,KAAK,YAAY,SAAS,CACvC,KAAK,qBAAqB,EAAM,EAAK,OAAS,EAAE,CAChD,KAAK,gBAAgB,CAKvB,aAAa,EAAuC,CAClD,IAAM,EAAW,KAAK,QAAQ,WAAa,cAC3C,KAAK,QAAQ,UAAY,EACzB,KAAK,cAAgB,KAAK,oBAAoB,EAAK,CACnD,KAAK,YAAY,aAAa,EAAkB,CAChD,KAAK,iBAAmB,KACxB,KAAK,wBAAwB,GAAK,CAClC,KAAK,kBAAkB,CACnB,IAAS,GAAU,KAAK,SAAS,KAAK,kBAAmB,CAAE,OAAM,WAAU,CAAC,CAQlF,oBAAoB,EAAiC,CACnD,KAAK,iBAAmB,CAAE,GAAG,KAAK,iBAAkB,IAAA,EAAA,EAAA,sBAAwB,EAAQ,CAAE,CACtF,KAAK,iBAAmB,KACxB,KAAK,wBAAwB,GAAK,CAClC,KAAK,kBAAkB,CAGzB,qBAAwC,CACtC,OAAA,EAAA,EAAA,sBAA4B,KAAK,iBAAiB,CASpD,iBAAiB,EAAqB,CAChC,IAAS,KAAK,gBAClB,KAAK,cAAgB,EACrB,KAAK,sBAAsB,CAC3B,KAAK,kBAAkB,EAGzB,wBAAkC,CAChC,OAAO,KAAK,cAId,YAAoB,EAAuE,CACzF,OAAO,KAAK,cAAgB,KAAO,EAAmB,KAAK,WAAY,EAAK,CAI9E,UAAkB,EAA2B,CAC3C,IAAM,EAAO,KAAK,YAAY,EAAI,CAClC,OAAO,EAAO,EAAI,OAAQ,GAAM,EAAK,EAAE,KAAK,CAAC,CAAG,EAAI,OAAO,CAI7D,YAAoB,EAA2B,CAC7C,IAAM,EAAO,KAAK,YAAY,EAAI,CAElC,MADA,MAAK,WAAa,EAAO,EAAI,OAAO,CAAG,KAChC,EAAO,EAAI,OAAQ,GAAM,EAAK,EAAE,KAAK,CAAC,CAAG,EAKlD,eAAuB,EAAuB,CAC5C,IAAM,EAAM,EAAa,KAAK,YAAY,SAAS,CAAE,EAAI,CAIzD,OAHI,IAAQ,QAAgB,IACxB,IAAQ,SAAU,KAAK,YAAY,UAAU,EAAI,CAChD,KAAK,YAAY,cAAc,EAAI,CACjC,IAGT,QAAgB,EAAc,EAAmC,CAE/D,GAAI,CAAC,KAAK,YAAc,CAAC,KAAK,eAAiB,CAAC,KAAK,cAAe,CAClE,IAAM,EAAO,KAAK,YAAY,SAAS,CAEjC,EAAO,EAAK,MAAM,IAAI,CACtB,EAAM,IAAQ,UAAY,EAAK,SAAW,EAAI,CAAC,GAAG,EAAM,EAAI,CAAG,CAAC,GAAG,EAAK,MAAM,EAAG,GAAG,CAAE,EAAI,CAChG,GAAI,KAAK,YAAY,EAAI,CAAE,CACzB,KAAK,WAAa,EAAK,OAAO,CAC9B,IAAM,EAAO,KAAK,YAAY,KAAK,WAAW,OAAS,EAAI,KAAK,WAAa,EAAI,CAC7E,GAAQ,EAAK,KAAM,GAAM,CAAC,EAAK,EAAE,KAAK,CAAC,GACzC,KAAK,YAAY,QAAQ,EAAK,OAAQ,GAAM,EAAK,EAAE,KAAK,CAAC,CAAC,CAC1D,KAAK,iBAAmB,OAI9B,IAAM,EAAO,KAAK,WAClB,GAAI,CAAC,EAAM,MAAO,GACd,IAAQ,UAAY,EAAK,SAAW,EAAG,EAAK,KAAK,EAAI,CACpD,EAAK,EAAK,OAAS,GAAK,EAC7B,IAAM,EAAO,KAAK,YAAY,EAAK,CACnC,MAAO,CAAC,GAAQ,EAAK,EAAI,KAAK,CAIhC,sBAAqC,CACnC,GAAI,KAAK,cAAe,OACxB,IAAM,EAAM,KAAK,YAAc,KAAK,YAAY,SAAS,CAAC,OAAO,CACjE,GAAI,CAAC,KAAK,YAAc,CAAC,KAAK,YAAY,EAAI,CAAE,OAChD,KAAK,YAAY,QAAQ,KAAK,YAAY,EAAI,CAAC,CAC/C,IAAM,EAAO,KAAK,YAAY,SAAS,CACvC,KAAK,iBAAiB,QAAQ,EAAK,CACnC,KAAK,iBAAmB,KACxB,KAAK,cAAc,iBAAiB,CACpC,KAAK,iBAAiB,EAAK,CAC3B,KAAK,wBAAwB,GAAK,CAClC,KAAK,SAAS,KAAK,aAAc,CAAE,OAAQ,EAAK,OAAQ,CAAC,CAI3D,YAAiC,CAE/B,OADA,KAAK,WAAW,cAAc,KAAK,gBAAkB,KAAK,CACnD,KAAK,WAQd,UAAU,EAA2B,CACnC,IAAM,EAAO,EAAW,EAAO,CAC3B,EAAK,YAAc,IAAA,KACvB,KAAK,aAAa,SAAS,CAAE,GAAG,KAAK,aAAa,UAAU,CAAC,MAAO,GAAG,EAAM,CAAC,CAC9E,KAAK,mBAAmB,CACxB,KAAK,OAAO,eAAe,EAG7B,WAAyB,CACvB,MAAO,CAAE,GAAG,KAAK,aAAa,UAAU,CAAC,MAAO,CAIlD,qBAAqB,EAAwB,CAC3C,KAAK,kBAAoB,EACzB,KAAK,mBAAmB,CACxB,KAAK,OAAO,eAAe,CAG7B,qBAA+B,CAC7B,OAAO,KAAK,kBAId,gBAAgB,EAAwB,CACtC,KAAK,WAAW,WAAW,EAAQ,CACnC,KAAK,OAAO,eAAe,CAG7B,uBAAiC,CAC/B,OAAO,KAAK,WAAW,kBAAkB,CAO3C,UAAU,EAA4B,CACpC,KAAK,WAAW,UAAU,EAAM,CAChC,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,CAG9C,WAA2B,CACzB,OAAO,KAAK,WAAW,WAAW,CAWpC,aACE,EACA,EAAoD,EAAE,CACtD,EAA0B,SAC1B,EAAmD,EAAE,CACtC,CACf,OAAO,KAAK,iBAAiB,IAAA,OAAiB,KAAK,gBAAgB,EAAI,EAAQ,EAAU,EAAQ,CAAC,CAGpG,gBACE,EACA,EACA,EACA,EACe,CAEf,GADI,CAAC,KAAK,SAAS,YACf,KAAK,SAAS,aAAa,OAAS,GAAK,CAAC,KAAK,SAAS,aAAa,SAAS,EAAG,CAAE,OAAO,KAC9F,IAAM,EAAa,KAAK,gBAAgB,wBAAwB,CAAC,KAAM,GAAM,EAAE,KAAO,EAAG,CACnF,EAAO,EAAa,KAAK,QAAQ,EAAY,EAAQ,EAAQ,KAAK,CAAG,KACrE,EAAa,KAAK,gBAAgB,aAAa,EAAI,EAAQ,KAAK,YAAY,SAAS,CAAE,CAC3F,GAAI,EAAO,CAAE,OAAM,CAAG,EAAE,CACxB,GAAI,EAAQ,QAAU,OAAS,CAAE,MAAO,OAAiB,CAAG,EAAE,CAC9D,GAAI,EAAQ,WAAa,CAAE,WAAY,EAAQ,WAAY,CAAG,EAAE,CACjE,CAAC,CAOF,OANI,GAAY,YAAc,SAAW,CAAC,GAAM,KAAK,cAAc,SAAS,EAAY,EAAS,CAEjG,KAAK,yBAAyB,CAC9B,KAAK,SAAS,KAAK,eAAgB,CAAE,aAAY,KAAI,CAAC,CACtD,KAAK,kBAAkB,CACvB,KAAK,sBAAsB,EAAW,CAC/B,EAUT,gBAAgB,EAAgB,EAA+B,CAC7D,EAAS,EAAO,MAAM,CACtB,KAAK,aAAa,IAAI,EAAQ,EAAK,CACnC,KAAK,iBAAiB,OAAO,EAAO,CACpC,IAAM,EAAO,KAAK,YAAY,SAAS,CACnC,EAAU,GACd,IAAK,IAAM,KAAO,KAAK,gBAAgB,qBAAqB,CACtD,CAAC,EAAkB,IAAI,EAAI,GAAG,EAAI,OAAO,EAAI,OAAO,QAAU,GAAG,CAAC,MAAM,GAAK,EAAO,MAAM,GAC9F,KAAK,gBAAgB,gBAAgB,EAAI,WAAY,EAAE,CAAE,EAAK,CAC9D,EAAU,IAEP,IACL,KAAK,wBAAwB,EAAE,CAC/B,KAAK,yBAAyB,EAIhC,qBAAoC,CAClC,IAAM,EAAW,IAAI,IAAI,KAAK,oBAAoB,CAAC,CACnD,IAAK,IAAM,KAAU,KAAK,iBAAuB,EAAS,IAAI,EAAO,EAAE,KAAK,iBAAiB,OAAO,EAAO,CAI7G,gBAAgB,EAAmC,CACjD,OAAO,KAAK,aAAa,IAAI,EAAO,EAAI,KAI1C,oBAA+B,CAC7B,IAAM,EAAM,IAAI,IAChB,IAAK,IAAM,KAAO,KAAK,gBAAgB,qBAAqB,CAAE,CAC5D,IAAM,EAAS,EAAI,OAAO,OACtB,EAAkB,IAAI,EAAI,GAAG,EAAI,OAAO,GAAW,UAAY,EAAO,MAAM,EAAE,EAAI,IAAI,EAAO,MAAM,CAAC,CAE1G,MAAO,CAAC,GAAG,EAAI,CAQjB,sBAA8B,EAA0B,CACtD,KAAK,qBAAqB,CAC1B,IAAM,EAAS,KAAK,gBAAgB,mBAAmB,EAAW,CAC5D,EAAS,GAAU,EAAkB,IAAI,EAAO,GAAG,EAAI,OAAO,EAAO,OAAO,QAAW,SAAW,EAAO,OAAO,OAAO,MAAM,CAAG,GAClI,CAAC,GAAU,KAAK,aAAa,IAAI,EAAO,EAAI,KAAK,iBAAiB,IAAI,EAAO,GACjF,KAAK,iBAAiB,IAAI,EAAO,CACjC,KAAK,SAAS,KAAK,sBAAuB,CAAE,SAAQ,CAAC,EAOvD,gBAAgB,EAAoB,EAAyD,CAC3F,KAAK,iBAAiB,MAAkB,KAAK,mBAAmB,EAAY,EAAO,CAAC,CAGtF,mBAA2B,EAAoB,EAAyD,CACtG,IAAM,EAAa,KAAK,gBAAgB,uBAAuB,EAAW,CACpE,EAAO,GAAA,EAAA,EAAA,aAAyB,EAAW,CAAG,KAC9C,EAAS,EAAO,KAAK,gBAAgB,mBAAmB,EAAW,EAAE,OAAO,GAAQ,IAAA,GAC1F,KAAK,gBAAgB,gBAAgB,EAAY,EAAQ,KAAK,YAAY,SAAS,CAAC,CACpF,KAAK,wBAAwB,EAAE,CAC/B,KAAK,kBAAkB,CACvB,KAAK,SAAS,KAAK,kBAAmB,CAAE,aAAY,OAAQ,SAAU,CAAC,CACvE,KAAK,sBAAsB,EAAW,CAGtC,IAAM,EAAS,KAAK,gBAAgB,mBAAmB,EAAW,CAClE,GAAI,GAAY,YAAc,WAAa,GAAU,GAAQ,EAAO,OAAO,KAAU,EAAQ,CAC3F,IAAM,GAAA,EAAA,EAAA,sBAA4B,EAAO,OAAO,GAAM,CAChD,EAAO,EAAO,KAAK,WAAW,EAAK,WAAY,EAAW,EAAA,EAAA,EAAA,sBACvC,EAAO,CAAG,KAAO,EAAO,MAAQ,MACpD,GAAQ,IAAA,MAAe,EAAO,OACjC,KAAK,gBAAgB,QAAQ,EAAY,EAAK,CAC9C,KAAK,SAAS,KAAK,kBAAmB,CAAE,aAAY,OAAQ,OAAQ,CAAC,EAGzE,KAAK,yBAAyB,CAShC,QACE,EACA,EACA,EACA,EACe,CACf,GAAI,EAAW,OAAO,KAAK,WAAW,EAAW,EAAK,CACtD,GAAI,EAAW,YAAc,UAAW,OAAO,KAC/C,IAAM,GAAA,EAAA,EAAA,aAAmB,EAAW,CAC9B,EAAO,GAAA,EAAA,EAAA,sBAA4B,EAAO,GAAM,CAAG,KACzD,OAAO,EAAO,KAAK,WAAW,EAAK,WAAY,EAAK,CAAG,KAIzD,WAAmB,EAAoB,EAA8B,CACnE,GAAI,IAAe,EAAM,OAAO,KAChC,GAAI,KAAK,cAAc,WAAW,CAAC,KAAM,GAAM,EAAE,KAAO,EAAW,CAAE,OAAO,EAC5E,IAAM,EAAO,KAAK,gBAAgB,mBAAmB,EAAW,EAAE,KAClE,OAAO,GAAQ,IAAS,EAAO,EAAO,KAIxC,mBAAmB,EAA8B,CAC/C,OAAO,KAAK,gBAAgB,UAAU,EAAW,CAQnD,kBAAkB,EAAoB,EAA8B,CAClE,OAAO,KAAK,iBAAiB,MAAkB,KAAK,qBAAqB,EAAY,EAAM,CAAC,CAG9F,qBAA6B,EAAoB,EAA8B,CAK7E,OAJK,KAAK,gBAAgB,SAAS,EAAY,EAAM,EACrD,KAAK,yBAAyB,CAC9B,KAAK,kBAAkB,CACvB,KAAK,SAAS,KAAK,kBAAmB,CAAE,aAAY,OAAQ,QAAS,CAAC,CAC/D,IAJuD,GAOhE,kBAAkB,EAAkC,CAClD,OAAO,KAAK,gBAAgB,mBAAmB,EAAW,EAAE,QAAU,OAAS,OAAS,QAI1F,yBAAyB,EAAwB,CAC/C,KAAK,mBAAqB,EAC1B,KAAK,yBAAyB,CAIhC,uBAAiC,CAC/B,OAAO,KAAK,SAAS,YAAc,KAAU,KAAK,oBAAsB,KAAK,gBAAgB,sBAAsB,EAIrH,mBAAyD,CACvD,OAAO,KAAK,aAAe,CAAE,GAAG,KAAK,aAAa,WAAY,CAAG,KAQnE,qBAAoD,CAClD,GAAI,CAAC,KAAK,uBAAuB,CAAE,OAAO,KAC1C,IAAM,EAAK,KAAK,SAAS,UAAU,CACnC,GAAI,CAAC,KAAK,gBAAgB,sBAAsB,CAAE,OAAO,EACzD,IAAM,EAAQ,KAAK,gBAAgB,qBAAqB,EAAG,aAAa,KAAM,EAAG,aAAa,GAAI,OAAO,CACzG,GAAI,CAAC,EAAO,OAAO,EACnB,IAAM,EAAO,EAAM,IAAM,EAAM,KAAO,KAAK,IAAI,EAAM,IAAI,EAAI,EAC7D,MAAO,CACL,GAAG,EACH,WAAY,CAAE,IAAK,EAAM,IAAM,EAAO,EAAoB,IAAK,EAAM,IAAM,EAAO,EAAoB,CACtG,SAAU,GACV,UAAW,UACX,YAAa,GAEb,YAAa,IAAA,GACb,UAAW,IAAA,GACZ,CAIH,mBAAmB,EAAoB,EAAwC,CAC7E,KAAK,iBAAiB,MAAkB,KAAK,sBAAsB,EAAY,EAAO,CAAC,CAGzF,sBAA8B,EAAoB,EAAwC,CACnF,KAAK,gBAAgB,UAAU,EAAY,EAAO,GACvD,KAAK,yBAAyB,CAC9B,KAAK,kBAAkB,CACvB,KAAK,SAAS,KAAK,kBAAmB,CAAE,aAAY,OAAQ,SAAU,CAAC,EAQzE,gBAAgB,EAA0B,CACxC,KAAK,iBAAiB,IAAA,OAAiB,KAAK,mBAAmB,EAAW,CAAC,CAG7E,mBAA2B,EAA0B,CACnD,GAAI,CAAC,KAAK,gBAAgB,mBAAmB,EAAW,CAAE,OAE1D,IAAM,EAAS,CAAC,EAAY,GAAG,KAAK,gBAAgB,cAAc,EAAW,CAAC,CAC9E,IAAK,IAAM,KAAM,EAAO,SAAS,CAAE,KAAK,gBAAgB,EAAG,CAC3D,KAAK,kBAAkB,CACvB,KAAK,yBAAyB,CAOhC,gBAAwB,EAA0B,CAChD,GAAI,CAAC,KAAK,gBAAgB,mBAAmB,EAAW,CAAE,OAC1D,IAAM,EAAQ,KAAK,cAAc,WAAW,CAAC,KAAM,GAAM,EAAE,KAAO,EAAW,CACvE,EAAU,KAAK,gBAAgB,eAAe,EAAW,CAEzD,EAAO,EAAQ,EAAQ,KADZ,GAAe,KAAK,gBAAgB,uBAAuB,EAAG,EAAE,YAAc,QACrD,EAAI,KAAO,KACrD,KAAK,gBAAgB,gBAAgB,EAAW,CAC5C,GACF,KAAK,gBAAgB,QAAQ,EAAM,KAAK,CACxC,KAAK,cAAc,YAAY,EAAY,EAAK,EAEhD,KAAK,cAAc,YAAY,EAAW,CAI5C,IAAK,IAAM,KAAU,EACf,IAAW,GAAM,KAAK,gBAAgB,QAAQ,EAAQ,EAAK,CAC/D,KAAK,SAAS,KAAK,kBAAmB,CAAE,WAAY,EAAQ,OAAQ,OAAQ,CAAC,CAE/E,KAAK,SAAS,KAAK,kBAAmB,CAAE,aAAY,CAAC,CACrD,KAAK,qBAAqB,CAI5B,kBAAkB,EAAmD,CACnE,OAAO,KAAK,gBAAgB,kBAAkB,EAAW,CAG3D,mBAAmB,EAA4C,CAC7D,OAAO,KAAK,gBAAgB,UAAU,EAAW,CAGnD,kBAAkB,EAA+B,CAC/C,KAAK,cAAc,kBAAkB,EAAO,CAI9C,IAAI,SAAyB,CAC3B,OAAO,KAAK,cAGd,OAAO,YAAoC,CACzC,IAAM,EAAS,IAAI,EAAA,gBAEnB,OADA,EAAA,EAAA,2BAA0B,EAAO,CAC1B,EAAO,wBAAwB,CAKxC,iBAAiB,EAAoB,EAA+B,CAClE,KAAK,cAAc,iBAAiB,EAAY,EAAS,CACzD,KAAK,yBAAyB,CAC9B,KAAK,SAAS,KAAK,kBAAmB,CAAE,aAAY,OAAQ,OAAQ,CAAC,CAGvE,aAAa,EAAoB,EAAoB,CACnD,KAAK,cAAc,aAAa,EAAY,EAAK,CACjD,KAAK,yBAAyB,CAC9B,KAAK,SAAS,KAAK,aAAc,CAAE,aAAY,OAAM,CAAC,CACtD,KAAK,kBAAkB,CAUzB,oBAAoB,EAAoB,EAAyB,CAC/D,OAAO,KAAK,iBAAiB,MAAkB,KAAK,uBAAuB,EAAY,EAAO,CAAC,CAIjG,uBAAuB,EAAoB,EAAyB,CAClE,OAAO,KAAK,aAAa,EAAY,EAAO,GAAK,IAAA,GAInD,aAAqB,EAAoB,EAA2C,CAClF,IAAM,EAAS,KAAK,gBAAgB,mBAAmB,EAAW,CAC5D,EAAa,KAAK,gBAAgB,uBAAuB,EAAW,CAC1E,GAAI,CAAC,GAAU,CAAC,EAAY,OAE5B,IAAM,GAAA,EAAA,EAAA,aAAmB,EAAW,CACpC,GAAI,EAAW,YAAc,WAAa,IAAA,EAAA,EAAA,sBAA6B,EAAO,OAAO,GAAM,CAAE,OAC7F,GAAI,IAAW,QAAS,OAAO,EAAW,YAAc,WAAa,EAAO,KAAO,KAAO,IAAA,GAC1F,GAAI,IAAW,MAAO,CACpB,IAAM,EAAW,KAAK,cAAc,WAAW,CAAC,KAAM,GAAM,EAAE,KAAO,EAAW,CAChF,OAAO,EAAW,YAAc,SAAW,CAAC,EAAW,KAAO,IAAA,GAEhE,IAAM,EAAO,KAAK,WAAW,EAAQ,EAAW,CAChD,OAAO,GAAQ,IAAS,EAAO,MAAQ,IAAS,EAAa,EAAO,IAAA,GAGtE,uBAA+B,EAAoB,EAAyB,CAC1E,IAAM,EAAO,KAAK,aAAa,EAAY,EAAO,CAC5C,EAAS,KAAK,gBAAgB,mBAAmB,EAAW,CAClE,GAAI,IAAS,IAAA,IAAa,CAAC,EAAQ,MAAO,GAE1C,IAAM,EAAa,EAAO,KACpB,EAAW,KAAK,cAAc,WAAW,CAAC,KAAM,GAAM,EAAE,KAAO,EAAW,CAE1E,EAAU,IAAI,IAAY,CAAC,EAAW,CAAC,CAC7C,GAAI,EAAU,CAGZ,IAAM,EAAU,KAAK,gBAAgB,eAAe,EAAW,CACzD,EAAU,IAAI,IAAI,KAAK,gBAAgB,cAAc,EAAW,CAAC,CACjE,EAAU,EAAQ,OAAQ,GAAM,CAAC,EAAQ,IAAI,EAAE,CAAC,CAChD,EAAO,EAAQ,KAAM,GAAM,KAAK,gBAAgB,uBAAuB,EAAE,EAAE,YAAc,QAAQ,EAAI,KAC3G,GAAI,EAAM,CACR,KAAK,gBAAgB,QAAQ,EAAM,KAAK,CACxC,KAAK,cAAc,YAAY,EAAY,EAAK,CAChD,IAAK,IAAM,KAAK,EAAa,IAAM,GAAM,KAAK,gBAAgB,QAAQ,EAAG,EAAK,KACzE,CACL,KAAK,cAAc,YAAY,EAAW,CAC1C,IAAK,IAAM,KAAK,EAAS,KAAK,gBAAgB,QAAQ,EAAG,KAAK,CAEhE,IAAK,IAAM,KAAK,EACV,EAAQ,IAAI,EAAE,EAAE,KAAK,gBAAgB,QAAQ,EAAG,GAAQ,EAAW,CACvE,EAAQ,IAAI,EAAE,CAIlB,GADA,KAAK,gBAAgB,QAAQ,EAAY,EAAK,CAC1C,IAAW,MAAO,CACpB,KAAK,cAAc,SAAS,EAAY,SAAS,CAEjD,IAAK,IAAM,KAAK,KAAK,gBAAgB,cAAc,EAAW,CACxD,KAAK,gBAAgB,mBAAmB,EAAE,EAAE,OAAS,IACvD,KAAK,gBAAgB,QAAQ,EAAG,EAAW,CAC3C,EAAQ,IAAI,EAAE,EAIpB,IAAK,IAAM,KAAM,EAAS,KAAK,SAAS,KAAK,kBAAmB,CAAE,WAAY,EAAI,OAAQ,OAAQ,CAAC,CAGnG,OAFA,KAAK,yBAAyB,CAC9B,KAAK,kBAAkB,CAChB,GAOT,iBAAiB,EAAoB,EAA6B,CAChE,IAAM,EAAO,KAAK,OAAO,EAAW,CACpC,OAAO,KAAK,iBAAiB,MAAY,KAAK,WAAW,EAAM,gBAAmB,KAAK,cAAc,kBAAkB,EAAM,EAAU,CAAC,CAAC,CAI3I,gBAAgB,EAA6B,CAC3C,OAAO,KAAK,cAAc,sBAAsB,KAAK,OAAO,EAAW,CAAC,CAI1E,iBAAiB,EAAoC,CACnD,IAAM,EAAO,IAAe,KAAO,KAAO,KAAK,OAAO,EAAW,CAC3D,EAAU,GAAQ,KAAK,cAAc,mBAAmB,EAAI,GAClE,OAAO,KAAK,iBAAiB,MAAe,KAAK,WAAW,EAAS,gBAAmB,KAAK,cAAc,kBAAkB,EAAK,CAAC,CAAC,CAGtI,kBAAkC,CAChC,OAAO,KAAK,cAAc,mBAAmB,CAO/C,aAAa,EAAoB,EAAwE,CACvG,IAAM,EAAO,KAAK,OAAO,EAAW,CACpC,OAAO,KAAK,iBAAiB,MAAY,KAAK,WAAW,EAAM,YAAe,KAAK,cAAc,cAAc,EAAM,EAAM,CAAC,CAAC,CAI/H,aAAa,EAAyE,CACpF,IAAM,EAAQ,KAAK,cAAc,WAAW,CAAC,KAAM,GAAM,EAAE,KAAO,KAAK,OAAO,EAAW,CAAC,CAC1F,MAAO,CAAE,IAAK,CAAC,CAAC,GAAO,SAAU,OAAQ,CAAC,CAAC,GAAO,YAAa,QAAS,CAAC,CAAC,GAAO,aAAc,CAIjG,aAAqB,EAAoB,EAAc,EAAgC,CACrF,IAAM,EAAS,KAAK,gBAAgB,UAAU,EAAW,EAAE,OACrD,EAAa,KAAK,gBAAgB,uBAAuB,EAAW,CAC1E,GAAI,CAAC,GAAU,CAAC,EAAY,OAC5B,IAAM,EAAM,EAAW,QAAQ,IAAI,IACnC,IAAK,IAAI,EAAI,KAAK,IAAI,EAAG,EAAK,CAAE,GAAK,GAAM,EAAI,EAAO,OAAQ,IAAK,CACjE,IAAM,EAAQ,EAAO,GACrB,GAAI,CAAC,EAAO,SACZ,IAAM,EAAQ,IAAQ,IAAA,GAAyB,OAAO,OAAO,EAAM,CAAC,KAAM,GAAM,OAAO,GAAM,UAAY,OAAO,SAAS,EAAE,CAAC,CAA1F,EAAM,GACxC,GAAI,OAAO,GAAU,UAAY,OAAO,SAAS,EAAM,EAAI,IAAU,EAAG,OAAO,GAMnF,SAAS,EAAoB,EAAwB,CACnD,IAAM,EAAO,KAAK,OAAO,EAAW,CACpC,OAAO,KAAK,iBAAiB,MAAY,KAAK,WAAW,EAAM,YAAe,KAAK,cAAc,UAAU,EAAM,EAAM,CAAC,CAAC,CAI3H,YAAY,EAAoB,EAAwB,CACtD,OAAO,KAAK,cAAc,aAAa,KAAK,OAAO,EAAW,CAAE,EAAM,CAIxE,OAAe,EAA4B,CACzC,OAAO,KAAK,gBAAgB,mBAAmB,EAAW,EAAE,MAAQ,EAGtE,WAAmB,EAAoB,EAAuD,EAA+B,CAK3H,OAJK,GAAO,EACZ,KAAK,yBAAyB,CAC9B,KAAK,SAAS,KAAK,aAAc,CAAE,aAAY,SAAQ,CAAC,CACxD,KAAK,kBAAkB,CAChB,IAJc,GAWvB,mBAAoI,CAClI,OAAO,KAAK,uBAAuB,CAAC,IAAK,IAAO,CAC9C,WAAY,EAAE,WACd,YAAa,CAAC,EAAE,WAAY,GAAI,EAAE,SAAW,EAAE,CAAE,CACjD,KAAM,CAAE,GAAG,EAAE,KAAM,CACpB,EAAE,CAIL,+BAA+B,EAAwB,CACrD,KAAK,SAAS,qBAAuB,EACrC,KAAK,mBAAmB,CACxB,KAAK,OAAO,eAAe,CAI7B,qBAAqB,EAAwB,CAC3C,KAAK,WAAa,EAClB,KAAK,OAAO,eAAe,CAO7B,iBAA0B,CACxB,IAAM,EAAM,KAAK,SAAS,UAAU,CAAC,UAAU,EAC/C,OAAO,KAAK,SAAS,OAAS,EAAM,KAAK,YAAY,WAAW,CAAG,EAQrE,eAAyB,CAEvB,OADI,EAAoB,KAAK,QAAQ,UAAU,CAAS,GACjD,KAAK,gBAAgB,CAAC,SAAW,KAAK,YAAY,WAAW,CAOtE,WAAW,EAAuB,CAChC,GAAI,CAAC,OAAO,SAAS,EAAM,CAAE,OAAO,EACpC,IAAM,EAAO,KAAK,YAAY,SAAW,KAAK,WAAa,IAAA,GAC3D,GAAI,IAAS,IAAA,IAAa,EAAO,EAAG,OAAO,QAAQ,KAAK,MAAM,EAAQ,EAAK,CAAG,GAAM,SAAA,EAAA,EAAA,cAAqB,EAAK,CAAC,CAAC,CAChH,GAAM,CAAE,MAAK,OAAQ,KAAK,SAAS,UAAU,CAAC,WAC9C,OAAO,OAAO,EAAM,QAAQ,KAAK,uBAAA,EAAA,EAAA,oBAA2C,EAAK,EAAI,CAAC,CAAC,CAQzF,eAAe,EAAqD,CAClE,KAAK,iBAAiB,EAAO,CAE7B,KAAK,yBAAyB,CAIhC,mBAA2C,CACzC,OAAO,KAAK,eAGd,iBAAyB,EAAqD,CAC5E,KAAK,gBAAA,EAAA,EAAA,mBAAmC,EAAO,CAC/C,KAAK,UAAY,GAAU,OAAO,GAAW,YAAA,EAAA,EAAA,cAA0B,EAAO,CAAG,KACjF,KAAK,SAAS,eAAe,KAAK,eAAgB,KAAK,UAAU,CACjE,KAAK,iBAAiB,kBAAkB,KAAK,eAAe,CAC5D,KAAK,cAAc,kBAAkB,KAAK,eAAe,CAI3D,iBAAiB,EAAuC,CACtD,KAAK,mBAAmB,EAAU,CAClC,KAAK,OAAO,eAAe,CAG7B,mBAA2B,EAAuC,CAChE,KAAK,SAAS,iBAAiB,EAAU,CACzC,KAAK,iBAAiB,iBAAiB,EAAU,CACjD,KAAK,iBAAiB,iBAAiB,EAAU,CAQnD,YAAY,EAAuB,CACjC,GAAI,KAAK,eAAgB,OAAO,KAAK,eAAe,EAAM,CAC1D,GAAM,CAAE,MAAK,OAAQ,KAAK,SAAS,UAAU,CAAC,WAC9C,OAAA,EAAA,EAAA,aAAmB,EAAO,KAAK,uBAAA,EAAA,EAAA,oBAA2C,EAAK,EAAI,CAAE,KAAK,aAAa,CAKzG,eAAe,EAAoC,CAC5C,KAAK,SAAS,WAEf,GAAQ,KAAK,SAAS,aAAa,OAAS,GAAK,CAAC,KAAK,SAAS,aAAa,SAAS,EAAK,GAE3F,GAAM,KAAK,gBAAgB,GAAM,CACrC,KAAK,eAAe,cAAc,EAAK,GAGzC,gBAAyC,CACvC,OAAO,KAAK,eAAe,eAAe,CAI5C,qBAAqB,EAAwB,CAC3C,KAAK,eAAe,qBAAqB,EAAQ,CAGnD,qBAA+B,CAC7B,OAAO,KAAK,eAAe,qBAAqB,CAIlD,cAAuB,CACrB,OAAO,KAAK,eAAe,eAAe,CAO5C,eAA0B,CAExB,OADK,KAAK,SAAS,SACZ,KAAK,eAAe,OAAO,CADE,EAAE,CAIxC,gBAAgB,EAAoC,CAClD,KAAK,eAAe,SAAS,EAAM,CAGrC,iBAAgC,CAC9B,OAAO,KAAK,eAAe,gBAAgB,CAG7C,sBAAsC,CACpC,OAAO,KAAK,eAAe,sBAAsB,CAInD,wBAAwB,EAA8B,EAAsB,CAC1E,OAAO,KAAK,eAAe,wBAAwB,EAAO,EAAG,CAG/D,aAA8B,CAC5B,OAAO,KAAK,eAAe,aAAa,CAG1C,YAAY,EAAgC,CAC1C,KAAK,eAAe,YAAY,EAAS,CACzC,KAAK,yBAAyB,CAC9B,KAAK,qBAAqB,CAI5B,WAAW,EASO,CAChB,GAAI,CAAC,KAAK,SAAS,SAAU,OAAO,KACpC,IAAM,EAAK,KAAK,eAAe,WAAW,EAAM,CAEhD,OADA,KAAK,kBAAkB,CAChB,EAOT,SAAyB,CACvB,GAAI,CAAC,KAAK,SAAS,SAAU,OAAO,KACpC,IAAM,EAAO,KAAK,gBAAgB,CAC5B,EAAK,KAAK,SAAS,UAAU,CAC7B,GAAA,EAAA,EAAA,mBAA0B,EAAM,EAAG,aAAa,KAAM,EAAG,aAAa,GAAG,CAE/E,OADK,EACE,KAAK,WAAW,CAAE,KAAM,iBAAkB,QAAS,CAAC,EAAM,GAAI,EAAM,GAAG,CAAE,CAAC,CAD9D,KAIrB,cAAc,EAAkB,CAC9B,KAAK,eAAe,cAAc,EAAG,CAIvC,eAAe,EAAgC,CAC7C,OAAO,KAAK,eAAe,eAAe,EAAI,CAIhD,yBAAyB,EAAiD,CACxE,IAAM,EAAa,KAAK,eAAe,cAAc,EAAK,CAC1D,OAAO,EAAa,gBAAgB,EAAW,CAAG,KAIpD,qBAAqB,EAA0C,CAC7D,OAAO,KAAK,eAAe,cAAc,EAAK,CAIhD,kBAAkB,EAA4B,CAC5C,OAAO,KAAK,eAAe,kBAAkB,EAAG,CAIlD,kBAAkB,EAAY,EAAkC,CAC9D,OAAO,KAAK,cAAc,EAAI,CAAE,UAAS,CAAC,CAQ5C,cAAc,EAAY,EAA8B,CACtD,IAAM,EAAU,KAAK,eAAe,cAAc,EAAI,EAAM,CAE5D,OADI,GAAS,KAAK,kBAAkB,CAC7B,EAIT,iBAAiB,EAAqB,CACpC,OAAO,KAAK,eAAe,UAAU,EAAG,CAI1C,eAAe,EAAY,EAAgC,EAAE,CAAQ,CACnE,KAAK,eAAe,QAAQ,EAAI,EAAQ,CACxC,KAAK,kBAAkB,CAIzB,uBAAuB,EAAuB,EAAsC,CAClF,KAAK,eAAe,gBAAgB,EAAM,EAAQ,CAGpD,uBAAuB,EAAuC,CAC5D,OAAO,KAAK,eAAe,gBAAgB,EAAK,CAGlD,kBAAkB,EAAY,EAAwB,CACpD,KAAK,eAAe,kBAAkB,EAAI,EAAQ,CAGpD,iBAAiB,EAAY,EAAuB,CAClD,KAAK,eAAe,iBAAiB,EAAI,EAAO,CAIlD,mBAAmB,EAAwB,EAAwB,CACjE,KAAK,eAAe,mBAAmB,EAAK,EAAQ,CAItD,kBAAkB,EAAwB,EAAuB,CAC/D,KAAK,eAAe,kBAAkB,EAAK,EAAO,CAGpD,eAAsB,CACpB,KAAK,eAAe,eAAe,CACnC,KAAK,yBAAyB,CAGhC,oBAAoB,EAA6B,CAC/C,KAAK,eAAe,SAAS,EAAO,CAKtC,MAAgB,CAEd,OADK,KAAK,SAAS,gBACZ,KAAK,eAAe,MAAM,CADU,GAI7C,MAAgB,CAEd,OADK,KAAK,SAAS,gBACZ,KAAK,eAAe,MAAM,CADU,GAK7C,cAAc,EAAqB,CACjC,IAAM,EAAK,KAAK,eAAe,OAAO,EAAG,CAEzC,OADI,GAAI,KAAK,OAAO,eAAe,CAC5B,EAGT,kBAA2D,CACzD,OAAO,KAAK,gBAAgB,UAAU,CAUxC,WAAW,EAAsE,CAC/E,GAAI,CAAC,KAAK,SAAS,iBAAmB,OAAO,GAAM,MAAS,YAAc,OAAO,EAAK,MAAS,WAAY,OAC3G,IAAM,EAAK,YAAY,KAAK,CACtB,EAAO,KAAK,gBAAgB,MAAM,CACxC,GAAI,EAAK,UAAY,IAAA,IAAa,GAAM,OAAS,UAAY,EAAK,QAAQ,UAAY,EAAK,SAAW,EAAK,EAAK,OAAO,GAAK,EAAyB,CAEnJ,KAAK,gBAAgB,YAAY,CAAE,GAAG,EAAM,OAAQ,CAAE,KAAM,EAAK,OAAO,KAAM,KAAM,EAAK,KAAM,QAAS,EAAK,QAAS,KAAI,CAAE,CAAC,CAC7H,OAEF,KAAK,gBAAgB,KAAK,CAAE,KAAM,SAAU,OAAQ,KAAM,MAAO,KAAM,OAAQ,CAAE,KAAM,EAAK,KAAM,KAAM,EAAK,KAAM,QAAS,EAAK,QAAS,KAAI,CAAE,CAAC,CAMnJ,iBAAiB,EAAwB,CACvC,KAAK,qBAAqB,EAAU,OAAS,MAAM,CAIrD,kBAA4B,CAC1B,OAAO,KAAK,eAAe,eAAe,GAAK,OAQjD,qBAAqB,EAAuC,CAC1D,IAAM,EAAK,KAAK,SAAS,eAAiB,IAAS,MACnD,KAAK,eAAe,cAAe,EAAc,IAAS,SAAW,SAAW,SAAxC,OAAiD,CAG3F,sBAAkD,CAChD,IAAM,EAAO,KAAK,eAAe,eAAe,CAChD,OAAO,IAAS,OAAS,MAAQ,IAAS,SAAW,SAAW,OAMlE,UAAkB,EAAuD,EAAuF,CAC9J,IAAM,EAAK,CAAE,GAAG,KAAK,SAAS,UAAU,CAAE,KAAM,KAAK,gBAAgB,CAAE,CACjE,EAAkE,CAAE,OAAM,EAAG,EAAI,EAAG,EAAG,EAAI,EAAG,CAGpG,IAFI,IAAS,QAAU,IAAS,eAAa,EAAQ,OAAA,EAAA,EAAA,UAAiB,EAAI,EAAG,EAAG,GAC3E,IAAS,QAAU,IAAS,aAAe,EAAG,KAAK,OAAS,IAAG,EAAQ,MAAA,EAAA,EAAA,SAAe,EAAI,EAAG,EAAG,EACjG,IAAS,OAAQ,CACnB,IAAM,EAAO,KAAK,mBAAmB,CAAC,KAAM,GAAM,EAAI,GAAK,EAAE,KAAK,GAAK,EAAI,GAAK,EAAE,KAAK,EAAI,EAAE,KAAK,OAAO,CACrG,IAAM,EAAQ,KAAO,EAAK,YAEhC,OAAO,EAIT,sBAAsB,EAAwB,CAC5C,KAAK,SAAS,mBAAqB,EACnC,KAAK,mBAAmB,WAAW,GAAW,KAAK,SAAS,UAAU,CACtE,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,CAU9C,cAAc,EAAmB,CAC3B,GAAI,KAAK,gBAAgB,GAAM,CACnC,KAAK,eAAe,UAAU,GAAM,KAAK,SAAS,SAAS,CAG7D,cAAwB,CACtB,OAAO,KAAK,eAAe,UAAU,CAOvC,gBAAgB,EAAmB,CAC7B,IAAO,KAAK,eAChB,KAAK,aAAe,EAChB,IACF,KAAK,eAAe,UAAU,GAAM,CACpC,KAAK,eAAe,cAAc,KAAK,EAEzC,KAAK,oBAAoB,gBAAgB,EAAG,CAC5C,KAAK,SAAS,KAAK,iBAAkB,CAAE,SAAU,EAAI,CAAC,EAGxD,gBAA0B,CACxB,OAAO,KAAK,aAId,UAAkB,EAAuC,CACvD,IAAM,EAAK,CAAE,GAAG,KAAK,SAAS,UAAU,CAAE,KAAM,KAAK,gBAAgB,CAAE,CACvE,KAAK,iBAAA,EAAA,EAAA,SAAwB,EAAI,GAAI,EAAG,EAAA,EAAA,EAAA,SAAU,EAAI,GAAI,EAAG,CAAC,CAKhE,iBAAwB,CACtB,KAAK,eAAe,iBAAiB,CAGvC,mBAA0B,CACxB,KAAK,eAAe,mBAAmB,CAGzC,iBAAwB,CACtB,KAAK,eAAe,iBAAiB,CAGvC,iBAAwB,CACtB,KAAK,eAAe,iBAAiB,CAKvC,iBAAiB,EAA4B,CAC3C,IAAM,EAAW,GAAM,KAAK,eAAe,sBAAsB,CAEjE,OADK,EACE,KAAK,eAAe,iBAAiB,EAAS,CAD/B,KAYxB,kBAAkB,EAAyB,MAAO,EAAmB,EAAoC,EAAE,CAAQ,CAC5G,KAAK,SAAS,YACnB,KAAK,SAAS,EAAQ,EAAU,CAAE,GAAG,EAAS,MAAO,UAAW,CAAC,CAInE,cAAc,EAAyB,MAAO,EAAmB,EAAoC,EAAE,CAAQ,CACxG,KAAK,SAAS,YACnB,KAAK,SAAS,EAAQ,EAAU,CAAE,GAAG,EAAS,MAAO,MAAO,CAAC,CAQ/D,cAAc,EAA+D,EAAE,CAAgD,CAC7H,IAAM,EAAO,KAAK,YAAY,SAAS,CACnC,EAAO,EACP,EAAK,EAAK,OAAS,EACvB,GAAI,EAAQ,QAAU,UAAW,CAC/B,GAAM,CAAE,gBAAiB,KAAK,SAAS,UAAU,CACjD,EAAO,KAAK,IAAI,EAAG,EAAa,KAAK,CACrC,EAAK,KAAK,IAAI,EAAK,OAAS,EAAG,EAAa,GAAG,CAEjD,IAAM,EAAO,EAAK,MAAM,EAAM,EAAK,EAAE,CACrC,GAAI,EAAQ,aAAe,GAAO,MAAO,CAAE,OAAM,QAAS,EAAE,CAAE,CAC9D,IAAM,EAA0B,EAAE,CAClC,IAAK,IAAM,KAAO,KAAK,gBAAgB,qBAAqB,CAAE,CAC5D,IAAM,EAAS,KAAK,gBAAgB,UAAU,EAAI,WAAW,EAAE,OAC/D,GAAI,CAAC,EAAQ,SACb,IAAM,EAAO,EAAmB,EAAI,GAAI,EAAI,OAAQ,EAAI,WAAW,cAAe,EAAI,WAAW,UAAU,CACrG,EAAQ,EAAI,WAAW,OAAS,OAAO,KAAK,EAAO,KAAM,GAAM,EAAE,EAAI,EAAE,CAAC,CAAC,IAAK,IAAS,CAAE,MAAK,MAAO,EAAK,EAAE,CAClH,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAQ,EAAM,OAAS,EAAI,IAAI,EAAK,OAAS,EAAK,MAAQ,GAChE,EAAQ,KAAK,CAAE,KAAM,GAAG,IAAO,IAAS,OAAQ,EAAK,KAAK,EAAG,IAAM,EAAO,EAAO,KAAK,EAAK,MAAQ,KAAK,CAAE,CAAC,EAI/G,IAAM,EAAO,IAAI,IACjB,IAAK,IAAM,KAAU,EAAS,CAC5B,IAAM,GAAS,EAAK,IAAI,EAAO,KAAK,EAAI,GAAK,EAC7C,EAAK,IAAI,EAAO,KAAM,EAAM,CACxB,EAAQ,IAAG,EAAO,KAAO,GAAG,EAAO,KAAK,IAAI,EAAM,IAExD,MAAO,CAAE,OAAM,UAAS,CAI1B,cAAc,EAAyB,MAAO,EAA+D,EAAE,CAAU,CACvH,GAAM,CAAE,OAAM,WAAY,KAAK,cAAc,EAAQ,CACrD,OAAO,IAAW,OAAS,EAAA,aAAa,OAAO,EAAM,EAAQ,CAAG,EAAA,aAAa,MAAM,EAAM,EAAQ,CAGnG,SAAiB,EAAwB,EAA8B,EAAmE,CACxI,IAAM,EAAO,KAAK,cAAc,EAAQ,EAAQ,CAC5C,IAAW,OAAQ,EAAA,aAAa,SAAS,EAAM,GAAY,kBAAmB,mBAAmB,CAChG,EAAA,aAAa,SAAS,EAAM,GAAY,iBAAkB,WAAW,CAK5E,YAAY,EAAa,EAAU,IAAY,CAC7C,KAAK,kBAAkB,OAAO,EAAK,EAAQ,CAG7C,iBAAwB,CACtB,KAAK,kBAAkB,SAAS,CAGlC,kBAAiC,CAC/B,KAAK,kBAAkB,UAAU,CAInC,kBAAiC,CAC/B,KAAK,kBAAkB,CACvB,KAAK,qBAAqB,CAG5B,qBAAoC,CAC9B,KAAK,SAAS,aAAa,cAAc,EAAE,KAAK,SAAS,KAAK,cAAe,EAAE,CAAC,CAKtF,wBAAgD,CAC9C,OAAO,KAAK,gBAAgB,wBAAwB,CAGtD,mBAAmB,EAAoE,CAErF,IAAM,EADa,KAAK,gBAAgB,wBAC3B,CAAW,KAAK,GAAK,EAAE,KAAO,EAAG,CAE9C,OADK,EACE,CAAE,GAAI,EAAK,GAAI,OAAQ,EAAK,cAAe,CADhC,KAKpB,qBAAyE,CACvE,OAAO,KAAK,gBAAgB,qBAAqB,CAInD,oBAAoB,EAAoB,EAAwB,CAC9D,KAAK,iBAAiB,MAAkB,KAAK,uBAAuB,EAAY,EAAQ,CAAC,CAG3F,uBAA+B,EAAoB,EAAwB,CACrE,KAAK,gBAAgB,WAAW,EAAY,EAAQ,GAAK,OAC3D,KAAK,yBAAyB,CAC9B,KAAK,kBAAkB,CACvB,KAAK,SAAS,KAAK,kBAAmB,CAAE,aAAY,OAAQ,UAAW,CAAC,EAK5E,mBAAmB,EAA4E,CAC7F,IAAM,EAAS,KAAK,gBAAgB,mBAAmB,EAAW,CAElE,OADK,EACE,CAAE,GAAI,EAAO,GAAI,OAAQ,CAAE,GAAG,EAAO,OAAQ,CAAE,CADlC,KAKtB,qBAAqB,EAAoB,EAA6E,CACpH,KAAK,iBAAiB,MAAkB,KAAK,wBAAwB,EAAY,EAAM,CAAC,CAG1F,wBAAgC,EAAoB,EAA6E,CAC/H,KAAK,gBAAgB,qBAAqB,EAAY,EAAM,CAC5D,KAAK,OAAO,eAAe,CAC3B,KAAK,kBAAkB,CACvB,KAAK,SAAS,KAAK,kBAAmB,CAAE,aAAY,OAAQ,QAAS,CAAC,CAKxE,UAAU,EAA8B,CACjC,KAAK,SAAS,UACnB,KAAK,eAAe,UAAU,EAAO,CACrC,KAAK,SAAS,KAAK,eAAgB,CAAE,SAAQ,CAAC,EAShD,aAAa,EAAgD,EAAyB,CACpF,GAAI,CAAC,KAAK,SAAS,QAAS,MAAO,GACnC,IAAM,EAAO,KAAK,YAAY,SAAS,CACjC,EAAQ,IAAU,EAAK,OAAS,EAAI,EAAK,EAAK,OAAS,GAAG,MAAQ,MAGxE,OAFI,IAAU,KAAa,IAC3B,KAAK,eAAe,aAAa,EAAM,EAAM,CACtC,IAGT,eAAsB,CAChB,KAAK,SAAS,SAAS,KAAK,eAAe,eAAe,CAGhE,gBAA0B,CACxB,OAAO,KAAK,SAAS,QAAU,KAAK,eAAe,gBAAgB,CAAG,GAGxE,iBAA2B,CACzB,OAAO,KAAK,SAAS,SAAW,KAAK,eAAe,iBAAiB,CAUvE,gBAAgB,EAAgD,EAAyB,CACvF,GAAI,CAAC,KAAK,SAAS,QAAS,MAAO,GACnC,IAAM,EAAO,KAAK,YAAY,SAAS,CACjC,EAAI,IAAU,EAAK,OAAS,EAAI,EAAK,EAAK,OAAS,GAAG,MAAQ,MAGpE,OAFI,IAAM,KAAa,IACvB,KAAK,eAAe,gBAAgB,EAAM,EAAE,CACrC,IAGT,kBAAyB,CACnB,KAAK,SAAS,SAAS,KAAK,eAAe,kBAAkB,CAGnE,mBAA6B,CAC3B,OAAO,KAAK,SAAS,QAAU,KAAK,eAAe,mBAAmB,CAAG,GAG3E,oBAA8B,CAC5B,OAAO,KAAK,SAAS,SAAW,KAAK,eAAe,oBAAoB,CAQ1E,iBAAiB,EAA+D,CACzE,KAAK,SAAS,SACnB,KAAK,SAAS,KAAK,aAAc,EAAO,CAG1C,aAAa,EAAoC,CAC1C,KAAK,SAAS,UACnB,KAAK,eAAe,aAAa,EAAU,CAC3C,KAAK,SAAS,KAAK,kBAAmB,CAAE,YAAW,CAAC,EAItD,WAA4B,CAC1B,OAAO,KAAK,eAAe,WAAW,CAIxC,cAAkC,CAChC,OAAO,KAAK,eAAe,cAAc,CAI3C,UAAuD,CACrD,OAAO,KAAK,eAAe,UAAU,CAIvC,gBAAyB,CACvB,OAAO,KAAK,eAAe,gBAAgB,CAI7C,QAAQ,EAAsD,CACvD,KAAK,SAAS,UACnB,KAAK,eAAe,QAAQ,EAAK,CACjC,KAAK,SAAS,KAAK,gBAAiB,EAAK,EAG3C,YAAmB,CACjB,KAAK,eAAe,SAAS,EAAE,CAAC,CAOlC,kBAAkB,EAAuB,CACnC,KAAK,SAAS,SAAS,KAAK,SAAS,KAAK,cAAe,CAAE,UAAS,CAAC,CAI3E,oBAAoB,EAA0B,CACxC,KAAK,SAAS,SAAS,KAAK,SAAS,KAAK,gBAAiB,CAAE,aAAY,CAAC,CAIhF,sBAAsB,EAA0B,CAC1C,KAAK,SAAS,SAAS,KAAK,SAAS,KAAK,kBAAmB,CAAE,aAAY,CAAC,CAIlF,qBAAqB,EAAmE,CAClF,KAAK,SAAS,SAAS,KAAK,SAAS,KAAK,iBAAkB,EAAO,CAGzE,aAAa,EAA+B,CACrC,KAAK,SAAS,SACnB,KAAK,eAAe,aAAa,EAAM,CAGzC,gBAAgB,EAAe,EAA4B,CACzD,KAAK,gBAAgB,EAAM,CACvB,KAAK,cAGP,KAAK,cAAc,MAAQ,CAAE,QAAO,EAGpC,KAAK,iBAAiB,SAAS,EAAM,CACrC,KAAK,gBAAgB,EAKzB,cAAgC,CAC9B,OAAO,KAAK,gBAAkB,MAAQ,KAAK,kBAAkB,eAAiB,IAAA,GAIhF,gBAAwB,EAAqB,CACtC,KAAK,cAAc,EAAE,KAAK,eAAe,gBAAgB,EAAM,CAChE,KAAK,SAAS,QAAQ,KAAK,YAAY,EAAM,CASnD,YAAoB,EAAqB,CACvC,IAAM,EAAO,KAAK,cAAgB,KAAK,cAAc,KAAK,SAAS,CAAG,KAAK,YAAY,SAAS,CAChG,KAAK,aAAa,WAAW,EAAK,OAAS,EAAI,EAAK,EAAK,OAAS,GAAG,KAAO,KAAK,CACjF,IAAM,EAAS,IAAI,IAAoB,CAAC,CAAC,QAAS,EAAM,CAAC,CAAC,CACrD,KAAK,eAAe,KAAK,uBAAuB,EAAO,CAE5D,KAAK,aAAa,cAAc,EAAQ,CAAE,SAAU,GAAM,CAAC,CAC3D,KAAK,aAAa,cAAc,EAAM,CAWxC,uBAA+B,EAAiC,CAC9D,IAAM,EAAO,KAAK,YAAY,SAAS,CACvC,GAAI,EAAK,SAAW,EAAG,OACvB,IAAM,EAAU,EAAK,OAAS,EACxB,EAAW,KAAK,aAAa,WAAW,CAAC,QAAS,GAAO,EAAE,OAAS,CAAC,EAAE,QAAS,EAAE,OAAO,CAAG,CAAC,EAAE,QAAQ,CAAE,CAC/G,IAAK,IAAM,KAAW,EAAU,CAC9B,GAAI,EAAK,IAAI,EAAQ,CAAE,SACvB,IAAM,EAAM,EAAQ,QAAQ,IAAI,CAChC,GAAI,EAAM,EAAG,SAEb,IAAM,EADQ,KAAK,gBAAgB,UAAU,EAAQ,MAAM,EAAG,EAAI,CAAC,EAAE,SAAS,KACxD,EAAQ,MAAM,EAAM,EAAE,EACxC,OAAO,GAAU,UAAY,OAAO,SAAS,EAAM,EAAE,EAAK,IAAI,EAAS,EAAM,EAIrF,iBAAiB,EAAsC,CAChD,KAAK,SAAS,SACnB,KAAK,eAAe,UAAU,EAAO,CAiBvC,MAAM,QAAQ,EAAqC,CACjD,GAAI,EAAA,EAAA,EAAA,iBAAiB,EAAO,QAAS,EAAO,UAAU,CACpD,MAAU,WAAW,GAAG,EAAO,QAAQ,KAAK,oBAAoB,EAAO,UAAU,YAAY,CAE/F,KAAK,YAAc,KAGnB,KAAK,YAAY,CACjB,KAAK,kBAAkB,CAEvB,KAAK,cAAgB,IAAI,EAAA,cAEzB,KAAK,cAAc,GAAG,WAAa,GAAS,CAC1C,IAAM,EAAM,GAAG,KAAK,cAAc,GAAG,KAAK,kBACtC,KAAK,cACP,KAAK,cAAc,KAAK,QAAQ,EAAK,CAC5B,IAAQ,KAAK,aAAe,KAAK,YAAY,WAAW,CAAG,EACpE,KAAK,cAAc,EAAK,CAExB,KAAK,QAAQ,EAAK,CAEpB,KAAK,YAAc,EACnB,KAAK,iBAAiB,EAAO,QAAS,EAAO,gBAAgB,EAC7D,CAIF,KAAK,cAAc,GAAG,WAAa,GAAQ,CACzC,GAAI,KAAK,cAAe,CACtB,KAAK,cAAc,KAAK,UAAU,EAAI,CACtC,OAEF,IAAM,EAAM,EAAa,KAAK,YAAc,KAAK,YAAY,SAAS,CAAE,EAAI,CAC5E,GAAI,IAAQ,SAAW,CAAC,KAAK,QAAQ,EAAK,EAAI,CAAE,OAChD,IAAM,EAAS,KAAK,oBAAsB,KAAK,SAAS,SAAS,CACjE,GAAI,CAAC,KAAK,eAAe,EAAI,CAAE,OAC/B,IAAM,EAAO,KAAK,YAAY,SAAS,CACvC,KAAK,iBAAiB,QAAQ,EAAK,CACnC,KAAK,iBAAmB,KACxB,KAAK,qBAAqB,EAAM,EAAK,OAAS,EAAE,CAChD,KAAK,wBAAwB,EAAO,EACpC,CAEF,KAAK,cAAc,GAAG,YAAc,GAAQ,CAC1C,GAAI,KAAK,cAAe,CACtB,KAAK,cAAc,KAAK,cAAc,EAAI,CAC1C,OAEF,KAAK,iBAAiB,SAAS,EAAI,MAAM,CACzC,IAAM,EAAM,EAAa,KAAK,YAAc,KAAK,YAAY,SAAS,CAAE,EAAI,CAC5E,GAAI,IAAQ,SAAW,CAAC,KAAK,QAAQ,EAAK,EAAI,EAAI,CAAC,KAAK,eAAe,EAAI,CAAE,CAC3E,KAAK,gBAAgB,CACrB,OAKF,IAAM,EAAO,KAAK,YAAY,SAAS,CACvC,KAAK,qBAAqB,EAAM,EAAK,OAAS,EAAE,CAChD,KAAK,gBAAgB,EACrB,CAEF,KAAK,UAAU,KAAK,CAEpB,KAAK,cAAc,GAAG,QAAU,GAAU,CACxC,IAAM,EAAM,KAAK,WAAW,WAAW,CACvC,KAAK,UAAU,CAAE,IAAK,EAAM,KAAO,GAAK,IAAK,IAAK,EAAM,KAAO,GAAK,IAAK,CAAC,EAC1E,CAEF,KAAK,cAAc,GAAG,eAAgB,CAAE,QAAO,mBAAoB,CAIjE,GADA,KAAK,gBAAgB,EAAM,CACvB,KAAK,cAAe,CACtB,KAAK,cAAc,MAAQ,CAAE,QAAO,cAAe,GAAiB,IAAA,GAAW,CAC/E,OAEF,KAAK,iBAAiB,SAAS,EAAO,GAAiB,IAAA,GAAU,CACjE,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,EAC5C,CAEF,KAAK,cAAc,GAAG,mBAAqB,GAAS,CAClD,KAAK,SAAS,KAAK,aAAc,CAAE,WAAY,EAAM,CAAC,EACtD,CAEF,KAAK,cAAc,GAAG,QAAU,GAAQ,CACtC,KAAK,SAAS,KAAK,aAAc,CAAE,MAAO,EAAI,QAAS,CAAC,EACxD,CAEF,KAAK,mBAAqB,EAAO,aAAe,GAChD,KAAK,gBAAgB,EAAO,OAAQ,EAAO,UAAU,CACrD,KAAK,cAAgB,EAAO,QAE5B,IAAM,EAAU,KAAK,cAKrB,GAJA,KAAK,oBAAoB,EAAO,QAAS,EAAO,OAAO,CACvD,MAAM,EAAQ,QAAQ,EAAO,CAGzB,KAAK,gBAAkB,EAAS,OAGpC,IAAM,GAAA,EAAA,EAAA,eAAqB,EAAO,UAAU,CAC5C,KAAK,aAAa,eAAe,EAAK,CAGlC,KAAK,mBAAmB,cAAc,KAAK,kBAAkB,CACjE,KAAK,kBAAoB,gBAAkB,CACrC,KAAK,aAAa,WAAW,EAC/B,KAAK,OAAO,cAAc,EAAA,UAAU,MAAM,EAE3C,IAAK,CAIV,gBAAwB,EAAgB,EAA4B,CAClE,IAAM,EAAiB,KAAK,gBACtB,EAAoB,KAAK,mBAC/B,KAAK,cAAgB,EACrB,KAAK,gBAAkB,EACnB,IAAW,IACb,KAAK,gBAAkB,EACvB,KAAK,SAAS,KAAK,eAAgB,CAAE,SAAQ,SAAU,EAAgB,CAAC,EAEtE,IAAc,IAChB,KAAK,mBAAqB,EAC1B,KAAK,SAAS,KAAK,kBAAmB,CAAE,YAAW,SAAU,EAAmB,CAAC,EAOrF,MAAM,aAAa,EAAgB,EAAqC,CACtE,GAAI,CAAC,KAAK,cAAe,OACzB,IAAM,EAAU,KAAK,cACrB,GAAI,GAAW,EAAA,EAAA,EAAA,iBAAiB,EAAS,EAAU,CACjD,MAAU,WAAW,GAAG,EAAQ,KAAK,oBAAoB,EAAU,YAAY,CAEjF,KAAK,gBAAgB,EAAQ,EAAU,CAGnC,KAAK,mBAAmB,KAAK,QAAQ,UAAU,KAAK,CACxD,KAAK,aAAa,gBAAA,EAAA,EAAA,eAA6B,EAAU,CAAC,CACtD,GAAS,KAAK,oBAAoB,EAAS,EAAO,CACtD,MAAM,KAAK,cAAc,SAAS,EAAQ,EAAU,CAItD,mBAAmB,EAA+B,CAChD,IAAM,EAAU,KAAK,SAAS,WAC9B,OAAO,EAAQ,SAAW,GAAK,EAAQ,SAAS,EAAU,CAQ5D,MAAM,aAAa,EAAqC,CACtD,GAAI,CAAC,KAAK,cAAe,MAAU,MAAM,0CAA0C,CAC9E,KAAK,mBAAmB,EAAU,EACvC,MAAM,KAAK,aAAa,KAAK,cAAe,EAAU,CAMxD,kBAAyB,CACvB,KAAK,gBACL,KAAK,WAAW,UAAU,KAAK,CAC/B,AAEE,KAAK,iBADL,KAAK,cAAc,SAAS,CACP,MAEvB,AAEE,KAAK,qBADL,KAAK,QAAQ,UAAU,KAAK,CACH,IAE3B,KAAK,cAAgB,KAWvB,iBAAiB,EAA2B,EAA0B,EAAE,CAAQ,CAC9E,GAAI,CAAC,KAAK,SAAS,QACjB,MAAU,MAAM,2DAA2D,CAE7E,KAAK,qBAAqB,CAC1B,KAAK,iBAAmB,EACxB,KAAK,aAAe,GAAc,EAAS,CACzC,UAAW,EAAM,IAAY,CAC3B,IAAM,EAAW,GAAkB,EAAQ,EAAE,QAAQ,CAErD,OADA,KAAK,SAAS,GAAG,EAAM,EAAQ,KAClB,KAAK,SAAS,IAAI,EAAM,EAAQ,EAE/C,UAAY,GAAW,KAAK,UAAU,EAAO,CAC7C,aAAe,GAAc,KAAK,aAAa,EAAU,CACzD,QAAU,GAAU,KAAK,SAAS,KAAK,iBAAkB,EAAM,CAC/D,OAAS,GAAS,CAChB,KAAK,eAAe,QAAQ,EAAK,CACjC,KAAK,SAAS,KAAK,gBAAiB,EAAK,EAE3C,iBAAoB,KAAK,eAAe,cAAc,CACvD,CAAC,CACF,QAAQ,QAAQ,EAAQ,QAAQ,EAAO,CAAC,CAAC,MAAO,GAC9C,KAAK,SAAS,KAAK,iBAAkB,CAAE,QAAS,2BAA4B,QAAO,CAAC,CACrF,CAIH,qBAA4B,CAC1B,KAAK,kBAAkB,CACvB,AAEE,KAAK,gBADL,KAAK,cAAc,CACC,MAEtB,AAEE,KAAK,oBADL,KAAK,iBAAiB,YAAY,CACV,MAK5B,qBAA+C,CAC7C,OAAO,KAAK,iBAId,uBAAuB,EAAwB,CAC7C,KAAK,aAAa,WAAW,GAAW,KAAK,SAAS,aAAa,CACnE,KAAK,OAAO,cAAc,EAAA,UAAU,MAAM,CAG5C,wBAAwB,EAAwB,CAC9C,KAAK,cAAc,WAAW,EAAQ,CACtC,KAAK,OAAO,cAAc,EAAA,UAAU,WAAW,CAMjD,iBAAiB,EAAY,EAAe,EAAkB,EAAqB,CAC5E,KAAK,SAAS,iBACnB,KAAK,gBAAgB,UAAU,CAAE,KAAI,QAAO,OAAM,QAAO,QAAS,GAAM,CAAC,CAEzE,KAAK,yBAAyB,EAGhC,oBAAoB,EAAkB,CACpC,KAAK,gBAAgB,aAAa,EAAG,CACrC,KAAK,yBAAyB,CAGhC,kBAAkB,EAAY,EAAwB,CACpD,KAAK,gBAAgB,cAAc,EAAI,EAAK,CAC5C,KAAK,yBAAyB,CAGhC,eAAe,EAAoC,CACjD,KAAK,gBAAgB,QAAQ,EAAK,CAClC,KAAK,yBAAyB,CAGhC,qBAA4B,CAC1B,KAAK,gBAAgB,OAAO,CAC5B,KAAK,yBAAyB,CAMhC,YAAY,EAAwB,CAC9B,GAAW,CAAC,KAAK,SAAS,WAC9B,KAAK,SAAS,YAAY,EAAQ,CAClC,KAAK,yBAAyB,EAGhC,YAAsB,CACpB,OAAO,KAAK,SAAS,YAAY,CAInC,eAAe,EAAyB,CACtC,KAAK,SAAS,eAAe,EAAS,CACtC,KAAK,yBAAyB,CAGhC,eAAyB,CACvB,OAAO,KAAK,SAAS,eAAe,CAQtC,aAAa,EAA2D,CAClE,IAAS,eAAiB,CAAC,KAAK,SAAS,WAC7C,KAAK,SAAS,aAAa,EAAK,CAChC,KAAK,yBAAyB,EAGhC,cAA8D,CAC5D,OAAO,KAAK,SAAS,cAAc,CAGrC,uBAAuB,EAAiG,CACtH,KAAK,cAAc,UAAU,EAAO,CACpC,KAAK,OAAO,cAAc,EAAA,UAAU,WAAW,CAGjD,oBAAsC,CACpC,OAAO,KAAK,eAAe,oBAAoB,EAAI,eAGrD,mBAAoC,CAClC,OAAO,KAAK,eAAe,mBAAmB,EAAI,CAAE,MAAO,eAAgB,CAG7E,cAAc,EAAwB,CACpC,KAAK,mBAAqB,EAK5B,SAAS,EAAyB,CAChC,IAAM,EAAO,KAAK,YAAY,SAAS,CACvC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAC/B,GAAI,EAAK,GAAG,MAAQ,EAAW,CAC7B,IAAM,EAAU,KAAK,SAAS,UAAU,CAAC,SAAW,KAAK,SAAS,UAAU,CAAC,WAC7E,KAAK,SAAS,SAAS,EAAI,EAAU,KAAK,SAAS,UAAU,CAAC,OAAO,CACrE,KAAK,yBAAyB,CAC9B,QAUN,SAAS,EAAsB,CAC7B,IAAM,EAAO,KAAK,gBAAgB,CAClC,GAAI,EAAK,SAAW,GAAK,CAAC,OAAO,SAAS,EAAK,CAAE,MAAO,GACxD,IAAM,EAAQ,KAAK,IAAI,EAAG,KAAK,IAAI,EAAK,OAAS,EAAG,KAAK,OAAA,EAAA,EAAA,qBAA0B,EAAM,EAAK,CAAC,CAAC,CAAC,CAC3F,EAAK,KAAK,SAAS,UAAU,CAC7B,EAAgB,GAAS,EAAG,SAAW,EAAG,YAAc,EAAG,SAAW,EAAI,EAAG,UAAU,MAAQ,EAGrG,OAFA,KAAK,SAAS,SAAS,EAAgB,EAAG,OAAO,CACjD,KAAK,yBAAyB,CACvB,EAQT,sBAAsB,EAA2B,CAC/C,IAAM,EAAO,KAAK,gBAAgB,CAClC,GAAI,EAAK,SAAW,EAAG,OACvB,IAAM,EAAO,EAAK,OAAS,EAErB,EAAO,EAAK,GAAM,KAAO,EAAqB,EAAI,IAClD,GAAA,EAAA,EAAA,kBAA2B,EAAQ,EAAK,GAAM,KAAO,EAAM,KAAK,UAAU,CAC1E,EAAQ,IAAY,KAAO,KAAO,EAAU,EAClD,GAAI,IAAU,MAAQ,EAAQ,EAAK,GAAG,KAAM,CAC1C,KAAK,YAAY,CACjB,OAIF,IAAM,GAAA,EAAA,EAAA,qBAAyB,EAAO,EAAK,CACrC,EAAQ,KAAK,IAAI,EAAM,IAAW,MAAQ,KAAK,KAAK,EAAG,CAAG,KAAK,MAAM,EAAG,CAAG,EAAE,CACnF,KAAK,SAAS,eAAe,EAAO,EAAO,KAAK,SAAS,gBAAgB,CAAC,CAC1E,KAAK,SAAS,aAAa,CAC3B,KAAK,yBAAyB,CAGhC,aAAoB,CAClB,KAAK,SAAS,aAAa,CAC3B,KAAK,yBAAyB,CAIhC,WAAW,EAAoB,CAC7B,GAAI,CAAC,OAAO,SAAS,EAAK,EAAI,IAAS,EAAG,OAC1C,GAAM,CAAE,WAAU,cAAe,KAAK,SAAS,UAAU,CACzD,KAAK,SAAS,SAAS,GAAQ,EAAW,GAAY,CACtD,KAAK,yBAAyB,CAOhC,gBAAgB,EAAuB,EAA2B,CAChE,IAAM,EAAO,KAAK,YAAY,SAAS,CACvC,GAAI,EAAK,SAAW,EAAG,OACvB,IAAM,GAAA,EAAA,EAAA,qBAAwB,KAAK,IAAI,EAAe,EAAY,CAAE,EAAK,CACnE,GAAA,EAAA,EAAA,qBAAwB,KAAK,IAAI,EAAe,EAAY,CAAE,EAAK,CAGzE,KAAK,SAAS,eAAe,EAAG,EAAE,CAClC,KAAK,yBAAyB,CAIhC,aAAa,EAA8D,CACzE,KAAK,SAAS,aAAa,EAAO,CAClC,KAAK,yBAAyB,CAGhC,QAAe,CACb,IAAM,EAAa,KAAK,SAAS,UAAU,CAAC,UAAU,MACtD,KAAK,SAAS,KAAK,GAAK,EAAa,EAAE,CACvC,KAAK,yBAAyB,CAGhC,SAAgB,CACd,IAAM,EAAa,KAAK,SAAS,UAAU,CAAC,UAAU,MACtD,KAAK,SAAS,KAAK,IAAM,EAAa,EAAE,CACxC,KAAK,yBAAyB,CAQhC,YAAmB,CACjB,IAAM,EAAI,KAAK,YAAY,WAAW,CAClC,IAAM,IACV,KAAK,SAAS,eAAe,EAAG,EAAI,EAAI,KAAK,SAAS,gBAAgB,CAAC,CACvE,KAAK,SAAS,aAAa,CAC3B,KAAK,yBAAyB,EAKhC,GAA6B,EAAS,EAAwG,CAC5I,KAAK,SAAS,GAAG,EAAM,EAAuC,CAGhE,IAA8B,EAAS,EAAwG,CAC7I,KAAK,SAAS,IAAI,EAAM,EAAuC,CAGjE,kBAAkB,EAA6C,CAC7D,KAAK,SAAS,kBAAkB,CAAE,QAAS,GAAM,GAAG,EAAS,CAAC,CAGhE,oBAA2B,CACzB,KAAK,SAAS,oBAAoB,CAKpC,SAAS,EAAsC,CAC7C,KAAK,aAAa,SAAS,EAAY,CACvC,KAAK,mBAAmB,CACxB,KAAK,UAAU,MAAM,gBAAkB,KAAK,aAAa,UAAU,CAAC,WACpE,KAAK,OAAO,eAAe,CAC3B,KAAK,SAAS,KAAK,cAAe,CAAE,MAAO,EAAa,CAAC,CACzD,KAAK,kBAAkB,CAGzB,UAAkB,CAChB,OAAO,KAAK,aAAa,UAAU,CAKrC,aAAa,EAAc,EAAoD,CAC7E,KAAK,UAAU,UAAU,CAAE,OAAM,GAAG,EAAM,CAAC,CAC3C,KAAK,OAAO,cAAc,EAAA,UAAU,WAAW,CAKjD,aAAa,EAAwB,CACnC,KAAK,QAAQ,UAAY,EAIzB,KAAK,iBAAmB,EACxB,KAAK,yBAAyB,CAGhC,aAAuB,CACrB,OAAO,KAAK,QAAQ,YAAc,GAKpC,iBAAiB,EAA4C,CAC3D,KAAK,iBAAiB,QAAQ,EAAK,CACnC,KAAK,OAAO,cAAc,EAAA,UAAU,MAAM,CAG5C,kBAA2B,CACzB,OAAO,KAAK,iBAAiB,SAAS,CAGxC,qBAAqB,EAA8C,CAC7D,EACF,KAAK,iBAAiB,cAAc,EAAM,CAE1C,KAAK,iBAAiB,gBAAgB,CAExC,KAAK,OAAO,cAAc,EAAA,UAAU,MAAM,CAQ5C,iBAAiB,EAA2B,CAC1C,IAAM,EAAO,KAAK,gBAAgB,CAC5B,EAAO,IAAS,MAAQ,EAAK,SAAW,EAAI,KAAO,KAAK,OAAA,EAAA,EAAA,qBAA0B,EAAM,EAAK,CAAC,CAChG,IAAS,KAAK,iBAAiB,eAAe,GAClD,KAAK,iBAAiB,cAAc,EAAK,CACzC,KAAK,OAAO,cAAc,EAAA,UAAU,MAAM,EAG5C,SAAsB,CACpB,OAAO,KAAK,YAAY,SAAS,CAKnC,eAAe,EAAwB,CACrC,KAAK,aAAa,WAAW,EAAQ,CACrC,KAAK,OAAO,cAAc,EAAA,UAAU,WAAW,CAGjD,eAAyB,CACvB,OAAO,KAAK,aAAa,WAAW,CAKtC,iBAAiB,EAAwB,CACvC,KAAK,eAAe,WAAW,EAAQ,CACvC,KAAK,OAAO,cAAc,EAAA,UAAU,KAAK,CAK3C,wBAAwB,EAAwB,CAC9C,KAAK,cAAc,WAAW,EAAQ,CACtC,KAAK,OAAO,cAAc,EAAA,UAAU,KAAK,CAG3C,wBAAkC,CAChC,OAAO,KAAK,cAAc,WAAW,CAGvC,uBAAuB,EAAmG,CACpH,EAAO,UAAY,IAAA,IAAW,KAAK,cAAc,WAAW,EAAO,QAAQ,CAC3E,EAAO,aAAe,IAAA,IAAW,KAAK,cAAc,cAAc,EAAO,WAAW,CACpF,EAAO,UAAY,IAAA,IAAW,KAAK,cAAc,WAAW,EAAO,QAAQ,CAC3E,EAAO,eAAiB,IAAA,IAAW,KAAK,cAAc,gBAAgB,EAAO,aAAa,CAC9F,KAAK,OAAO,cAAc,EAAA,UAAU,KAAK,CAK3C,wBAAwB,EAAwB,CAC9C,KAAK,cAAc,WAAW,EAAQ,CACtC,KAAK,OAAO,cAAc,EAAA,UAAU,KAAK,CAG3C,wBAAkC,CAChC,OAAO,KAAK,cAAc,WAAW,CAGvC,uBAAuB,EAA4L,CAC7M,EAAO,UAAY,IAAA,IAAW,KAAK,cAAc,WAAW,EAAO,QAAQ,CAC3E,EAAO,aAAe,IAAA,IAAW,KAAK,cAAc,cAAc,EAAO,WAAW,CACpF,EAAO,UAAY,IAAA,IAAW,KAAK,cAAc,WAAW,EAAO,QAAQ,CAC3E,EAAO,eAAiB,IAAA,IAAW,KAAK,cAAc,gBAAgB,EAAO,aAAa,CAC1F,EAAO,eAAiB,IAAA,IAAW,KAAK,cAAc,gBAAgB,EAAO,aAAa,CAC1F,EAAO,YAAc,IAAA,IAAW,KAAK,cAAc,aAAa,EAAO,UAAU,CACjF,EAAO,iBAAmB,IAAA,IAAW,KAAK,cAAc,kBAAkB,EAAO,eAAe,CAChG,EAAO,UAAY,IAAA,IAAW,KAAK,cAAc,WAAW,EAAO,QAAQ,CAC/E,KAAK,OAAO,cAAc,EAAA,UAAU,KAAK,CAI3C,uBAA+E,CAC7E,OAAO,KAAK,cAAc,UAAU,CAKtC,uBAAuB,EAAwB,CAC7C,KAAK,aAAa,WAAW,EAAQ,CACrC,KAAK,OAAO,cAAc,EAAA,UAAU,KAAK,CAG3C,uBAAiC,CAC/B,OAAO,KAAK,aAAa,WAAW,CAGtC,sBAAsB,EAAuD,CACvE,EAAO,UAAY,IAAA,IAAW,KAAK,aAAa,WAAW,EAAO,QAAQ,CAC1E,EAAO,WAAa,IAAA,IAAW,KAAK,aAAa,YAAY,EAAO,SAAS,CACjF,KAAK,OAAO,cAAc,EAAA,UAAU,KAAK,CAO3C,kBAAkB,EAAuD,CACvE,IAAM,EAAO,KAAK,YAAY,SAAS,CACnC,EAAK,SAAW,IACpB,KAAK,aAAa,KAAK,EAAK,EAAK,OAAS,GAAG,KAAM,EAAM,CACrD,KAAK,aAAa,WAAW,EAAE,KAAK,OAAO,cAAc,EAAA,UAAU,KAAK,EAG9E,mBAA0B,CACxB,KAAK,aAAa,OAAO,CACzB,KAAK,OAAO,cAAc,EAAA,UAAU,KAAK,CAK3C,uBAAuB,EAAwB,CAC7C,KAAK,aAAa,WAAW,EAAQ,CACrC,KAAK,OAAO,cAAc,EAAA,UAAU,KAAK,CAG3C,uBAAiC,CAC/B,OAAO,KAAK,aAAa,WAAW,CAGtC,sBAAsB,EAAuD,CAC3E,KAAK,aAAa,UAAU,EAAO,CACnC,KAAK,OAAO,cAAc,EAAA,UAAU,KAAK,CAS3C,YAAY,EAA2B,CACrC,EAAe,EAAG,CAClB,KAAK,gBAAkB,EACvB,KAAK,eAAe,CAItB,aAA+B,CAC7B,OAAO,KAAK,gBAId,sBAAwC,CACtC,OAAO,KAAK,UAGd,eAA8B,CAC5B,IAAM,EAAW,KAAK,YAAY,SAC5B,EAAK,KAAK,kBAAA,WACX,IAAA,EAAA,EAAA,iBAA4B,EAAS,CAAG,EAAW,MACpD,KAAK,gBACT,KAAK,UAAY,EACjB,KAAK,SAAS,kBAAkB,EAAG,CACnC,KAAK,iBAAiB,kBAAkB,EAAG,CAC3C,KAAK,iBAAiB,kBAAkB,EAAG,CAC3C,KAAK,cAAc,YAAY,EAAG,CAClC,KAAK,cAAc,YAAY,EAAG,CAClC,KAAK,OAAO,eAAe,CAW7B,cAAc,EAA+B,CAC3C,KAAK,WAAa,EAAO,CAAE,GAAG,EAAM,SAAU,EAAK,UAAU,IAAK,IAAa,CAAE,GAAG,EAAS,EAAE,CAAE,CAAG,KAChG,KAAK,cAAc,iBAAmB,IAAA,IACxC,KAAK,oBAAoB,KAAK,YAAY,gBAAkB,KAAK,CAEnE,IAAM,GAAW,KAAK,YAAY,UAAY,EAAE,EAAE,QAAS,GAAY,CACrE,IAAM,GAAA,EAAA,EAAA,eAA4B,EAAQ,MAAM,CAC1C,GAAA,EAAA,EAAA,eAA0B,EAAQ,IAAI,CAC5C,OAAO,IAAgB,MAAQ,IAAc,KAAO,EAAE,CAAG,CAAC,CAAE,cAAa,YAAW,CAAC,EACrF,CACI,EAAO,KAAK,YAAY,SAE9B,KAAK,eAAe,cAAc,EAAQ,OAAS,GAAK,IAAA,EAAA,EAAA,iBAAwB,EAAK,CACjF,CAAE,SAAU,EAAM,UAAS,YAAa,EAAQ,GAAG,YAAa,UAAW,EAAQ,EAAQ,OAAS,GAAG,UAAW,CAClH,KAAK,iBAAiB,CACtB,KAAK,kBAAA,YAAuC,KAAK,eAAe,CACpE,KAAK,sBAAsB,CAC3B,KAAK,yBAAyB,CAC9B,KAAK,SAAS,KAAK,mBAAoB,CAAE,KAAM,KAAK,WAAY,CAAC,CAGnE,eAAmC,CACjC,OAAO,KAAK,WAId,oBAA4B,EAAsB,EAAsB,CACtE,IAAM,EAAU,EAAQ,eAAe,KAAK,EAAQ,CACpD,GAAI,CAAC,EAAS,OACd,IAAM,EAAM,EAAE,KAAK,cACf,KAAK,YAAc,KAAK,WAAW,SAAW,GAAQ,KAAK,cAAc,KAAK,CAElF,QAAQ,SAAS,CAAC,SAAW,EAAQ,EAAO,CAAC,CAAC,KAC3C,GAAS,CAEJ,GAAQ,IAAQ,KAAK,eAAiB,KAAK,gBAAkB,GAAQ,KAAK,cAAc,EAAK,EAElG,GAAiB,CAEZ,IAAQ,KAAK,eAAe,QAAQ,KAAK,uBAAuB,EAAO,IAAK,EAAI,EAEvF,CAIH,kBAAkB,EAA8B,CAC9C,KAAK,YAAY,EAAQ,CAK3B,uBAAuB,EAAwB,CAC7C,KAAK,aAAa,WAAW,EAAQ,CACrC,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,CAG9C,uBAAiC,CAC/B,OAAO,KAAK,aAAa,WAAW,CAGtC,sBAAsB,EAAkG,EAClH,EAAO,OAAS,IAAA,IAAa,EAAO,QAAU,IAAA,KAChD,KAAK,aAAa,YAAY,EAAO,MAAQ,EAAG,EAAO,OAAS,EAAO,MAAQ,EAAE,CAE/E,EAAO,aAAe,IAAA,IAAW,KAAK,aAAa,cAAc,EAAO,WAAW,CACnF,EAAO,kBAAoB,IAAA,IAAW,KAAK,aAAa,mBAAmB,EAAO,gBAAgB,CACtG,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,CAK9C,yBAAyB,EAAwB,CAC/C,KAAK,eAAe,WAAW,EAAQ,CACvC,KAAK,OAAO,cAAc,EAAA,UAAU,WAAW,CAGjD,yBAAmC,CACjC,OAAO,KAAK,eAAe,WAAW,CAQxC,wBAAwB,EAA2C,CAEjE,KAAK,kBAAA,EAAA,EAAA,mBAAqC,KAAK,iBAAkB,EAAO,CACxE,KAAK,eAAe,cAAc,KAAK,iBAAiB,CACxD,KAAK,OAAO,cAAc,EAAA,UAAU,WAAW,CAKjD,kBAAkB,EAAwB,CACnC,GAAS,KAAK,iBAAiB,MAAM,CAK5C,UAAU,EAAiE,CACzE,KAAK,YAAY,UAAU,EAAO,CAClC,KAAK,OAAO,cAAc,EAAA,UAAU,MAAM,CAG5C,cAAc,EAAsB,CAClC,KAAK,YAAY,UAAU,EAAO,CAClC,KAAK,OAAO,cAAc,EAAA,UAAU,MAAM,CAO5C,cAAc,EAA2B,CACvC,KAAK,YAAY,cAAc,EAAK,CACpC,KAAK,OAAO,cAAc,EAAA,UAAU,MAAM,CAK5C,WAAW,EAAyB,CAC7B,KAAK,SAAS,YACnB,EAAA,WAAW,SAAS,KAAK,UAAW,EAAU,KAAK,aAAa,UAAU,CAAC,WAAW,CAIxF,MAAM,gBAAmC,CACvC,GAAI,CAAC,KAAK,SAAS,WAAY,MAAO,GACtC,GAAI,CACF,IAAM,EAAO,MAAM,EAAA,WAAW,OAAO,KAAK,UAAW,KAAK,aAAa,UAAU,CAAC,WAAW,CACvF,EAAqB,WAAwD,cAGnF,MAFI,CAAC,GAAQ,CAAC,UAAU,WAAW,OAAS,CAAC,EAA0B,IACvE,MAAM,UAAU,UAAU,MAAM,CAAC,IAAI,EAAkB,CAAE,YAAa,EAAM,CAAC,CAAC,CAAC,CACxE,SACD,CACN,MAAO,IAIX,mBAAmC,CAEjC,OADK,KAAK,SAAS,WACZ,EAAA,WAAW,UAAU,KAAK,UAAW,KAAK,aAAa,UAAU,CAAC,WAAW,CAD9C,KAIxC,MAAM,gBAAuC,CAE3C,OADK,KAAK,SAAS,WACZ,EAAA,WAAW,OAAO,KAAK,UAAW,KAAK,aAAa,UAAU,CAAC,WAAW,CAD3C,KAaxC,SACE,EACA,EAAwD,WACxD,EACA,EAAU,QACV,EACA,EAAoD,EAAE,CACvC,CACf,GAAI,CAAC,KAAK,SAAS,OAAQ,OAAO,KAClC,IAAM,EAAK,KAAK,aAAa,SAAS,EAAO,EAAW,EAAS,GAAO,EAAS,EAAO,EAAQ,CAEhG,OADA,KAAK,kBAAkB,CAChB,EAGT,YAAY,EAAkB,CAC5B,KAAK,aAAa,YAAY,EAAG,CACjC,KAAK,kBAAkB,CAMzB,uBAAkC,CAChC,OAAO,KAAK,eAAe,uBAAuB,CAIpD,YAAY,EAAY,EAA+B,CACrD,IAAM,EAAQ,KAAK,eAAe,YAAY,EAAI,EAAG,CAErD,OADI,GAAO,KAAK,kBAAkB,CAC3B,EAIT,cAAc,EAAwB,EAA8B,CAClE,IAAM,EAAQ,KAAK,eAAe,cAAc,EAAK,EAAK,CAE1D,OADI,GAAO,KAAK,kBAAkB,CAC3B,EAGT,gBAAgB,EAA0B,CACxC,OAAO,KAAK,mBAAmB,KAAK,eAAe,QAAQ,EAAQ,CAAC,CAGtE,mBAAmB,EAAiB,EAAuB,CACzD,OAAO,KAAK,mBAAmB,KAAK,eAAe,YAAY,EAAS,EAAK,CAAC,CAGhF,uBAAuB,EAAiB,EAA2B,CACjE,OAAO,KAAK,mBAAmB,KAAK,eAAe,gBAAgB,EAAS,EAAQ,CAAC,CAGvF,sBAAsB,EAAiB,EAA0B,CAC/D,OAAO,KAAK,mBAAmB,KAAK,eAAe,eAAe,EAAS,EAAO,CAAC,CAIrF,kBAAkE,CAChE,OAAO,KAAK,eAAe,WAAW,CAGxC,mBAA2B,EAA2B,CAEpD,OADI,GAAS,KAAK,kBAAkB,CAC7B,EAIT,mBAAmB,EAA4B,CAC7C,OAAO,KAAK,SAAS,QAAU,KAAK,iBAAiB,EAAU,GAAK,KAOtE,iBAAyB,EAAoC,CAE3D,IAAM,EAAO,KAAK,cAAgB,KAAK,cAAc,KAAK,SAAS,CAAG,KAAK,gBAAgB,CAC3F,GAAI,EAAK,SAAW,EAAG,OAAO,KAC9B,IAAM,EAAW,CAAE,GAAG,KAAK,SAAS,UAAU,CAAE,OAAM,CACtD,OAAO,KAAK,eAAe,QAAQ,EAAW,EAAK,EAAK,OAAS,GAAG,KAAM,EAAS,CAOrF,oBAA4B,EAAe,EAAqC,CAC1E,MAAC,GAAa,CAAC,KAAK,cACxB,IAAI,IAAU,gBAAiB,CAC7B,IAAM,EAAQ,KAAK,aAAa,kBAAkB,EAAU,CACxD,EAAM,OAAS,GAAG,KAAK,qBAAqB,IAAI,EAAW,EAAM,SAC5D,IAAU,gBAAiB,CACpC,IAAM,EAAO,KAAK,qBAAqB,IAAI,EAAU,CACrD,GAAI,CAAC,EAAM,OACX,KAAK,qBAAqB,OAAO,EAAU,CAC3C,KAAK,aAAa,cAAc,EAAK,GAKzC,yBAAwC,CACtC,KAAK,qBAAqB,OAAO,CAC5B,KAAK,cACM,KAAK,aAAa,mBAAmB,IAAI,IAAI,KAAK,eAAe,aAAa,CAAC,IAAK,GAAM,EAAE,GAAG,CAAC,CAC5G,CAAU,GAAG,KAAK,kBAAkB,CAQ1C,gBACE,EACA,EAA6H,EAAE,CAChH,CACf,GAAI,CAAC,KAAK,mBAAmB,EAAU,CAAE,OAAO,KAChD,IAAM,EAAK,KAAK,aAAa,gBAAgB,EAAW,EAAQ,WAAa,WAAY,EAAQ,QAAS,EAAQ,WAAa,GAAO,EAAQ,MAAM,CAEpJ,OADA,KAAK,kBAAkB,CAChB,EAGT,WAAsD,CACpD,OAAO,KAAK,aAAa,WAAW,CAGtC,aAAoB,CAClB,KAAK,aAAa,aAAa,CAC/B,KAAK,kBAAkB,CAGzB,WAAW,EAAmB,CAC5B,KAAK,aAAa,cAAc,EAAI,CAItC,WAAW,EAAmB,CAC5B,KAAK,aAAa,gBAAgB,EAAI,CACtC,KAAK,aAAa,mBAAmB,IAAI,IAAI,KAAK,eAAe,aAAa,CAAC,IAAK,GAAM,EAAE,GAAG,CAAC,CAAC,CACjG,KAAK,OAAO,cAAc,EAAA,UAAU,QAAQ,CAK9C,gBAAgB,EAA2F,CACzG,OAAO,KAAK,oBAAoB,UAAU,EAAO,CAGnD,mBAAmB,EAAkB,CACnC,KAAK,oBAAoB,aAAa,EAAG,CAG3C,kBAAkE,CAChE,OAAO,KAAK,oBAAoB,YAAY,CAG9C,iBAAiB,EAA8D,CAC7E,KAAK,oBAAoB,WAAW,EAAQ,CAG9C,oBAA2B,CACzB,KAAK,oBAAoB,cAAc,CAGzC,qBAAqB,EAAwE,CAC3F,KAAK,oBAAoB,SAAS,EAAM,CAK1C,aAAa,EAAsF,CACjG,OAAO,KAAK,iBAAiB,QAAQ,EAAK,CAG5C,gBAAgB,EAAY,EAA8E,CACxG,KAAK,iBAAiB,WAAW,EAAI,EAAQ,CAG/C,gBAAgB,EAAkB,CAChC,KAAK,iBAAiB,WAAW,EAAG,CAGtC,eAA4D,CAC1D,OAAO,KAAK,iBAAiB,UAAU,CAGzC,cAAc,EAAyD,CACrE,KAAK,iBAAiB,SAAS,EAAM,CAGvC,iBAAwB,CACtB,KAAK,iBAAiB,YAAY,CAGpC,kBAAkB,EAAqE,CACrF,KAAK,iBAAiB,SAAS,EAAM,CAgBvC,YAAY,EAAkE,CAC5E,GAAI,CAAC,KAAK,SAAS,OAAQ,OAE3B,GADA,KAAK,QAAQ,OAAO,CAChB,CAAC,KAAK,cAAe,CACvB,GAAI,KAAK,YAAY,WAAW,GAAK,EAAG,OACxC,IAAM,EAAO,IAAI,EACjB,EAAK,QAAQ,KAAK,YAAc,KAAK,YAAY,SAAS,CAAC,CAC3D,KAAK,cAAgB,CAAE,OAAM,MAAO,KAAM,QAAS,KAAM,CAI3D,IAAM,EAAS,KAAK,UAAU,KAAK,cAAc,KAAK,SAAS,CAAC,CAC1D,CAAE,MAAO,EAAU,GAAG,GAAe,GAAU,EAAE,CACjD,EAAQ,EAAW,GAAY,EAAU,EAAO,CAAG,KACzD,KAAK,kBAAoB,EACzB,KAAK,aAAe,EAEpB,KAAK,QAAQ,UAAY,GAEzB,KAAK,iBAAmB,GACxB,GAAI,CACF,KAAK,cAAc,KAAK,GAAS,EAAO,QAChC,CACR,KAAK,iBAAmB,GAEtB,GAAS,EAAW,aAAe,IAAA,KAAW,EAAW,WAAa,EAAc,EAAO,EAAQ,EAAW,WAAW,EAG7H,KAAK,kBAAkB,CACvB,IAAI,EAAS,GACT,EAAW,GACX,EAA0B,KACxB,EAAS,GAAS,EACxB,KAAK,eAAiB,KAAK,cAAc,GAAG,OAAQ,CAAE,WAAY,CAEhE,IAAI,EAAW,EACX,EAAoB,EAAO,GAC/B,GAAI,EAAO,CACT,IAAM,EAAO,EAAM,GACnB,EAAW,GAAM,EAAQ,EAAK,KAAK,CAEnC,EADgB,IAAY,MAAQ,IAAa,EAAQ,GAAK,IAAa,EAAS,GAC/D,EAAU,EAAS,EAAS,EAAK,CAAG,GAAW,EAAO,GAAW,EAAO,EAAM,CACnG,EAAW,EAEb,EAAW,EACX,IAAM,EAAU,EAAW,EACrB,EAAU,EAAS,GAAK,GAAW,GAAU,KAAK,YAAY,WAAW,GAAK,EAO9E,EAAS,EADK,GAAW,CAAC,KAAK,gBACL,KAAK,oBAAsB,KAAK,SAAS,SAAS,CAClF,GAAI,EAAS,CAGP,GAAS,EAAU,GAAQ,KAAK,YAAY,cAAc,EAAO,EAAS,GAAG,CACjF,IAAK,IAAI,EAAI,EAAQ,EAAI,EAAU,EAAG,IAAK,KAAK,YAAY,UAAU,EAAO,GAAG,CAC5E,EAAU,EAAQ,KAAK,YAAY,UAAU,EAAS,CACrD,KAAK,YAAY,cAAc,EAAS,CAC7C,KAAK,qBAAqB,KAAK,YAAY,SAAS,CAAE,KAAK,IAAI,EAAG,EAAS,EAAE,CAAC,MAG9E,KAAK,YAAY,QAAQ,CAAC,GAAG,EAAO,MAAM,EAAG,EAAS,CAAE,EAAS,CAAC,CAClE,KAAK,iBAAiB,KAAK,YAAY,SAAS,CAAC,CAEnD,EAAS,EAET,KAAK,iBAAiB,SAAS,EAAS,MAAO,EAAW,EAAI,EAAO,EAAW,GAAG,MAAQ,IAAA,GAAU,CACrG,KAAK,iBAAiB,QAAQ,KAAK,YAAY,SAAS,CAAC,CAGzD,KAAK,iBAAmB,KACxB,KAAK,wBAAwB,EAAO,CACpC,KAAK,gBAAgB,EAAQ,EAAM,CAGnC,IAAM,EADO,EAAO,EAAQ,IAAI,MACV,EAAO,GAAO,MAAQ,EAAQ,EAAI,EAAO,GAAO,KAAO,EAAO,EAAQ,GAAG,KAAO,GACtG,KAAK,SAAS,KAAK,aAAc,CAAE,WAAU,KAAM,EAAO,GAAU,KAAM,QAAO,CAAC,EAClF,CACF,KAAK,cAAc,KAAK,EAAW,CAIrC,iBAAiB,EAAoB,CACnC,GAAI,CAAC,KAAK,eAAiB,CAAC,KAAK,aAAc,OAC/C,IAAM,EAAS,KAAK,mBAAqB,KAAK,aAC1C,EAAK,EACL,EAAK,EAAO,OAAS,EACzB,KAAO,EAAK,GAAI,CACd,IAAM,EAAO,EAAK,EAAK,GAAM,EACzB,EAAO,GAAK,KAAO,EAAM,EAAK,EAC7B,EAAK,EAAM,EAElB,KAAK,WAAW,EAAG,CAIrB,gBAAwB,EAAe,EAAqB,CAC1D,IAAM,EAAU,KAAK,iBAChB,GAAS,eACd,EAAQ,aAAa,EAAO,EAAK,CACjC,KAAK,eAAe,gBAAgB,EAAM,EAI5C,kBAAiC,CAC/B,IAAM,EAAU,KAAK,cACrB,GAAI,CAAC,GAAW,CAAC,KAAK,cAAc,CAAE,OACtC,IAAM,EAAO,EAAQ,KAAK,SAAS,CAC7B,EAAQ,EAAQ,OAAO,OAAS,EAAK,EAAK,OAAS,IAAI,MACzD,IAAU,IAAA,IAAW,KAAK,gBAAgB,EAAM,CAStD,gBAAwB,EAAoB,EAAqB,CAC/D,IAAM,EAAU,KAAK,cACf,EAAU,KAAK,iBACf,EAAM,EAAO,GACnB,GAAI,CAAC,GAAW,CAAC,GAAS,cAAgB,CAAC,EAAK,OAChD,GAAI,EAAQ,UAAY,KAAM,CAE5B,EAAQ,QAAU,EAAI,KACtB,KAAK,gBAAgB,EAAI,MAAO,EAAI,KAAK,CACzC,OAEF,GAAI,EAAI,MAAQ,EAAQ,QAAS,OACjC,IAAM,EAAM,EAAO,MAAM,GAAW,EAAQ,EAAQ,QAAQ,CAAE,EAAQ,EAAE,CACxE,EAAQ,QAAU,EAAI,KACtB,IAAM,EAAO,KAAK,KAAK,EAAI,OAAS,GAAkB,CACtD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAI,OAAQ,GAAK,EACnC,IAAK,GAAM,CAAC,EAAO,KAAS,GAAY,EAAI,MAAM,EAAG,EAAI,EAAK,CAAC,CAAE,EAAQ,aAAa,EAAO,EAAK,CAEpG,KAAK,eAAe,gBAAgB,EAAI,MAAM,CAOhD,mBAA4B,CAC1B,GAAI,CAAC,KAAK,cAAe,MAAO,GAChC,GAAM,CAAE,WAAY,KAAK,cAAc,aAAa,CAC9C,EAAQ,KAAK,kBACnB,OAAO,GAAS,KAAK,aAAe,GAAM,KAAK,aAAc,EAAM,IAAU,MAAQ,KAAU,CAAG,EAIpG,gBAAgB,EAAwB,CACtC,IAAM,EAAQ,KAAK,kBACnB,KAAK,WAAW,GAAS,KAAK,aAAe,EAAc,EAAO,KAAK,aAAc,EAAS,CAAG,EAAS,CAG5G,aAAoB,CAAE,KAAK,cAAc,OAAO,CAChD,cAAqB,CAAE,KAAK,cAAc,QAAQ,CAElD,YAAmB,CACjB,IAAM,EAAU,KAAK,cACrB,KAAK,kBAAkB,CAClB,IACL,KAAK,WAAW,EAAQ,KAAK,SAAS,CAAE,GAAK,CACzC,EAAQ,OAAO,KAAK,iBAAiB,SAAS,EAAQ,MAAM,MAAO,EAAQ,MAAM,cAAc,EAIrG,gBAA0B,CACxB,OAAO,KAAK,gBAAkB,KAIhC,aAAuE,CACrE,GAAM,CAAE,IAAG,IAAG,QAAO,UAAW,KAAK,SAAS,UAAU,CAAC,UACzD,MAAO,CAAE,IAAG,IAAG,QAAO,SAAQ,CAIhC,kBAAiC,CAC1B,KAAK,gBACV,KAAK,kBAAkB,CACvB,KAAK,cAAgB,KACrB,KAAK,kBAAoB,KACzB,KAAK,aAAe,KACpB,KAAK,kBAAkB,CACvB,KAAK,eAAiB,KACtB,KAAK,cAAc,MAAM,EAG3B,WAAW,EAAqB,CAC9B,KAAK,cAAgB,GACrB,GAAI,CACF,KAAK,cAAc,OAAO,EAAM,QACxB,CACR,KAAK,cAAgB,IAGzB,eAAe,EAAqB,CAAE,KAAK,cAAc,SAAS,EAAM,CACxE,gBAAmD,CAAE,OAAO,KAAK,cAAc,UAAU,CACzF,mBAAyE,CAAE,OAAO,KAAK,cAAc,aAAa,CAKlH,iBAAqE,CACnE,OAAO,EAAA,kBAAkB,QACvB,CACE,gBAAmB,KAAK,aAAa,CACrC,aAAgB,KAAK,UAAU,CAC/B,cAAiB,KAAK,WAAW,CACjC,kBAAqB,KAAK,mBAAmB,CAC9C,CACD,CAAE,UAAW,KAAK,QAAQ,UAAW,iBAAkB,KAAK,qBAAqB,CAAE,OAAQ,KAAK,eAAiB,IAAA,GAAW,CAC7H,CAUH,kBAA0B,EAAoC,EAAuC,CAEnG,IAAM,EAAU,GAAgB,EAAK,CAC/B,EAAc,IAAI,IACxB,KAAK,qBAAuB,EAAE,CAC9B,IAAM,EAAQ,IAAI,IAAI,KAAK,gBAAgB,wBAAwB,CAAC,IAAK,GAAM,EAAE,GAAG,CAAC,CAC/E,EAA0C,EAAE,CAC5C,EAAO,IAAI,IAEjB,GAAI,EAAS,CAEX,IAAM,EAAS,IAAI,IAAI,EAAQ,IAAK,GAAQ,CAAC,EAAI,WAAY,EAAI,CAAC,CAAC,CAC7D,EAAU,KAAK,mBAAmB,CACxC,IAAK,IAAM,KAAO,EAAS,CACzB,IAAM,EAAO,EAAO,IAAI,EAAI,WAAW,CACnC,GAAQ,GAAiB,EAAK,EAAK,EAAE,EAAK,IAAI,EAAI,WAAW,CAEnE,IAAK,IAAM,KAAO,EACX,EAAK,IAAI,EAAI,WAAW,EAAE,KAAK,gBAAgB,EAAI,WAAW,CAGrE,IAAK,IAAM,IAAM,CAAC,GAAG,EAAK,CAAO,KAAK,gBAAgB,mBAAmB,EAAG,EAAE,EAAK,OAAO,EAAG,MAE7F,IAAK,IAAM,KAAU,KAAK,gBAAgB,qBAAqB,CAAE,KAAK,gBAAgB,EAAO,WAAW,CAI1G,IAAM,EAAW,GAA+E,CAC9F,IAAM,EAAM,CAAE,GAAG,EAAQ,CACzB,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,EAAI,CAAE,CAC/C,IAAM,GAAA,EAAA,EAAA,sBAA4B,EAAM,CAClC,EAAK,GAAQ,EAAY,IAAI,EAAK,WAAW,CAC/C,IAAI,EAAI,IAAA,EAAA,EAAA,iBAAwB,EAAI,EAAK,IAAI,EAEnD,OAAO,GAGT,IAAK,IAAM,KAAO,EAAS,CACzB,IAAI,EAA4B,KAChC,GAAI,EAAK,IAAI,EAAI,WAAW,CAAE,CAC5B,EAAa,EAAI,WACjB,IAAM,EAAS,KAAK,gBAAgB,mBAAmB,EAAW,EAAE,QAAU,EAAE,CAC1E,EAAqD,EAAE,CAC7D,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,EAAQ,EAAI,OAAO,CAAC,CAAM,EAAO,KAAU,IAAO,EAAQ,GAAQ,GACzG,OAAO,KAAK,EAAQ,CAAC,OAAS,GAAG,KAAK,gBAAgB,EAAY,EAAQ,KACzE,CACL,IAAM,EAAY,CAAC,MAAO,SAAU,OAAQ,QAAQ,CAAW,KAAM,GAAM,IAAM,EAAI,SAAS,EAAI,SAClG,GAAI,CACF,GAAI,CAAC,EAAM,IAAI,EAAI,GAAG,CAAE,MAAU,MAAM,kCAAkC,CAC1E,IAAM,EAAO,EAAI,KAAO,EAAY,IAAI,EAAI,KAAK,CAAG,IAAA,GACpD,EAAa,KAAK,gBAAgB,EAAI,GAAI,EAAQ,EAAI,OAAO,CAAE,EAAU,CACvE,GAAI,EAAO,CAAE,OAAM,CAAG,EAAE,CACxB,GAAI,EAAI,QAAU,OAAS,CAAE,MAAO,OAAiB,CAAG,EAAE,CAC1D,GAAI,EAAU,CAAE,WAAY,EAAI,WAAY,CAAG,EAAE,CAClD,CAAC,OACK,EAAK,CACZ,QAAQ,KAAK,qBAAqB,EAAI,GAAG,uBAAwB,EAAI,EAGzE,GAAI,CAAC,EAAY,CACf,KAAK,qBAAqB,KAAK,EAAI,CACnC,SAEF,EAAY,IAAI,EAAI,WAAY,EAAW,CAC3C,EAAS,KAAK,CAAC,EAAK,EAAW,CAAC,CAChC,KAAK,aAAa,EAAY,EAAI,CAIpC,OAFA,KAAK,aAAa,EAAS,CAC3B,KAAK,yBAAyB,CACvB,EAIT,aAAqB,EAAoB,EAA8B,CACrE,IAAM,EAAQ,KAAK,gBAAgB,kBAAkB,EAAW,CAC5D,EAAI,OAAS,KAAK,UAAU,CAAE,GAAG,EAAO,GAAG,EAAI,MAAO,CAAC,GAAK,KAAK,UAAU,EAAM,EAAE,KAAK,qBAAqB,EAAY,EAAI,MAAM,CACvI,IAAM,EAAS,KAAK,gBAAgB,mBAAmB,EAAW,EAC7D,EAAI,SAAW,OAAW,GAAQ,SAAW,KAAO,KAAK,oBAAoB,EAAY,EAAI,SAAW,GAAK,CAClH,IAAM,EAAS,EAAI,QAAU,KACzB,KAAK,UAAU,EAAO,GAAK,KAAK,UAAU,GAAQ,QAAU,KAAK,EAAE,KAAK,mBAAmB,EAAY,EAAO,CAIpH,aAAqB,EAAwD,CAC3E,IAAK,GAAM,CAAC,EAAK,KAAe,EAC1B,EAAI,WAAa,IAAA,IAAW,KAAK,cAAc,aAAa,EAAY,EAAI,SAAS,EACrF,EAAI,WAAa,IAAA,IAAa,EAAI,gBAAe,KAAK,cAAc,kBAAkB,EAAY,CAAC,CAAC,EAAI,cAAc,CACtH,EAAI,WAAa,IAAA,IAAW,KAAK,cAAc,cAAc,EAAY,CAAE,IAAK,CAAC,CAAC,EAAI,QAAS,OAAQ,CAAC,CAAC,EAAI,WAAY,QAAS,CAAC,CAAC,EAAI,YAAa,CAAC,CAE5J,IAAM,EAAU,EACb,QAAQ,CAAC,KAAS,EAAI,YAAc,IAAA,GAAU,CAC9C,MAAM,CAAC,GAAI,CAAC,MAAQ,EAAE,WAAa,IAAM,EAAE,WAAa,GAAG,CAC3D,KAAK,EAAG,KAAgB,EAAW,CAClC,EAAQ,OAAS,GAAG,KAAK,cAAc,YAAY,EAAQ,CAC/D,IAAM,EAAY,EAAS,MAAM,CAAC,KAAS,EAAI,cAAc,CAC7D,KAAK,cAAc,kBAAkB,EAAY,EAAU,GAAK,KAAK,CAOvE,mBAAqE,CACnE,IAAM,EAAS,KAAK,cAAc,WAAW,CACvC,EAAY,KAAK,cAAc,mBAAmB,CACxD,MAAO,CACL,GAAG,KAAK,gBAAgB,qBAAqB,CAAC,IAAK,GAAQ,CACzD,IAAM,EAAQ,EAAO,KAAM,GAAM,EAAE,KAAO,EAAI,WAAW,CACnD,EAAS,KAAK,gBAAgB,mBAAmB,EAAI,WAAW,CACtE,MAAO,CACL,GAAI,EAAI,GACR,WAAY,EAAI,WAChB,OAAQ,CAAE,GAAG,EAAI,OAAQ,CACzB,SAAU,GAAO,SACjB,MAAO,KAAK,gBAAgB,kBAAkB,EAAI,WAAW,EAAI,IAAA,GACjE,QAAS,EAAI,QACb,OAAQ,GAAQ,QAAQ,OAAO,CAC/B,KAAM,EAAI,KACV,MAAO,GAAQ,MACf,GAAI,EACA,CACE,SAAU,EAAM,KAChB,UAAW,EAAO,QAAQ,EAAM,CAChC,GAAI,EAAM,UAAY,CAAE,cAAe,GAAM,CAAG,EAAE,CAClD,GAAI,IAAc,EAAI,WAAa,CAAE,cAAe,GAAM,CAAG,EAAE,CAC/D,GAAI,EAAM,SAAW,CAAE,QAAS,GAAM,CAAG,EAAE,CAC3C,GAAI,EAAM,YAAc,CAAE,WAAY,GAAM,CAAG,EAAE,CACjD,GAAI,EAAM,aAAe,CAAE,YAAa,GAAM,CAAG,EAAE,CACpD,CACD,EAAE,CACP,EACD,CACF,GAAG,KAAK,qBACT,CAOH,oBAAoB,EAA0C,CACvD,KAAK,SAAS,YACnB,KAAK,iBAAiB,IAAA,OAAiB,CACrC,IAAM,EAAS,IAAI,IAAI,KAAK,gBAAgB,qBAAqB,CAAC,IAAK,GAAM,CAAC,EAAE,WAAY,EAAE,GAAG,CAAC,CAAC,CAC7F,EAAc,KAAK,4BAA8B,KAAK,kBAAkB,EAAM,GAAM,CAAC,CAC3F,KAAK,sBAAsB,EAAQ,CAAC,GAAG,EAAY,QAAQ,CAAC,CAAC,CAC7D,KAAK,kBAAkB,EACvB,CAQJ,sBAA8B,EAAqC,EAAgC,CACjG,GAAI,CAAC,KAAK,SAAS,OAAQ,OAE3B,IAAM,EAAU,GAAmC,CACjD,IAAM,EAAM,EAAQ,QAAQ,IAAI,CAChC,GAAI,GAAO,EAAG,OAAO,KACrB,IAAM,EAAO,EAAQ,MAAM,EAAG,EAAI,CAC5B,EAAO,EAAO,IAAI,EAAK,CAC7B,GAAI,CAAC,GAAQ,KAAK,gBAAgB,mBAAmB,EAAK,CAAE,OAAO,KACnE,IAAM,EAAQ,EAAM,OAAQ,GAAO,KAAK,gBAAgB,mBAAmB,EAAG,EAAE,KAAO,EAAK,CAC5F,OAAO,EAAM,SAAW,EAAI,EAAM,GAAK,EAAQ,MAAM,EAAI,CAAG,MAE9D,IAAK,IAAM,KAAS,KAAK,aAAa,WAAW,CAAE,CACjD,IAAM,EAAU,EAAO,EAAM,QAAQ,CAC/B,EAAS,EAAM,OAAS,EAAO,EAAM,OAAO,CAAG,KACrD,GAAI,CAAC,GAAW,CAAC,EAAQ,SACzB,GAAM,CAAE,OAAQ,EAAI,UAAS,OAAM,aAAY,aAAc,EAC7D,KAAK,aAAa,YAAY,EAAM,GAAG,CACvC,KAAK,aAAa,SAAS,EAAM,MAAO,EAAM,UAAW,EAAM,QAAS,EAAM,UAAW,GAAW,EAAM,QAAS,EAAM,MAAO,CAC9H,GAAI,EAAM,OAAS,CAAE,OAAQ,GAAU,EAAM,OAAQ,CAAG,EAAE,CAC1D,GAAI,IAAY,IAAA,GAA0B,EAAE,CAAhB,CAAE,UAAS,CACvC,GAAI,IAAS,IAAA,GAAuB,EAAE,CAAb,CAAE,OAAM,CACjC,GAAI,EAAa,CAAE,aAAY,CAAG,EAAE,CACpC,GAAI,IAAc,IAAA,GAA4B,EAAE,CAAlB,CAAE,YAAW,CAC5C,CAAC,EAKN,wBAAmC,EAAoB,CACrD,KAAK,wBACL,GAAI,CACF,OAAO,GAAQ,QACP,CACR,KAAK,yBAST,iBAA4B,EAA6B,EAAoB,CAC3E,GAAI,KAAK,sBAAwB,GAAK,CAAC,KAAK,SAAS,gBAAiB,OAAO,GAAQ,CACrF,IAAM,EAAS,KAAK,mBAAmB,CACjC,EAAS,KAAK,wBAAwB,EAAO,CAC7C,EAAQ,KAAK,mBAAmB,CACtC,GAAI,KAAK,UAAU,EAAO,GAAK,KAAK,UAAU,EAAM,CAAE,OAAO,EAC7D,IAAM,EAAM,YAAY,KAAK,CACvB,EAAO,KAAK,gBAAgB,MAAM,CAClC,EAAQ,IAAY,IAAA,IAAa,GAAM,OAAS,cAAgB,EAAK,YAAY,UAAY,GAC9F,GAAO,EAAK,WAAW,IAAM,GAAK,GAClC,KAAK,UAAU,EAAK,WAAW,MAAM,GAAK,KAAK,UAAU,EAAO,CAC/D,EAAO,CAAE,KAAM,aAAuB,OAAQ,KAAM,MAAO,KAAM,CAQvE,OAPI,GAAS,GAAM,WAEb,KAAK,UAAU,EAAK,WAAW,OAAO,GAAK,KAAK,UAAU,EAAM,CAAE,KAAK,gBAAgB,UAAU,CAChG,KAAK,gBAAgB,YAAY,CAAE,GAAG,EAAM,WAAY,CAAE,GAAG,EAAK,WAAY,QAAO,GAAI,EAAK,CAAE,CAAC,CAEtG,KAAK,gBAAgB,KAAK,CAAE,GAAG,EAAM,WAAY,CAAE,SAAQ,QAAO,UAAS,GAAI,EAAK,CAAE,CAAC,CAElF,EAIT,mBAA2B,EAAoD,EAAkC,CAC/G,IAAM,EAAO,EAAO,WACf,IACL,KAAK,4BAA8B,KAAK,kBAAkB,IAAc,OAAS,EAAK,OAAS,EAAK,MAAO,GAAK,CAAC,CACjH,KAAK,kBAAkB,EAGzB,UAAU,EAA6B,CACrC,GAAI,CAAC,KAAK,SAAS,SAAU,OAAO,KACpC,IAAM,EAAW,KAAK,iBAAiB,CACjC,EAAO,EAAA,kBAAkB,UAAU,EAAS,CAElD,OADI,GAAK,EAAA,kBAAkB,cAAc,EAAK,EAAS,CAChD,EAGT,UAAU,EAAoB,CAC5B,GAAI,CAAC,KAAK,SAAS,SAAU,OAC7B,IAAM,EAAW,EAAA,kBAAkB,YAAY,EAAK,CAEpD,KAAK,iBAAmB,EAAS,kBAAoB,EAAE,CACvD,KAAK,iBAAmB,KACpB,EAAS,WAAW,KAAK,aAAa,EAAS,UAAU,CACzD,EAAS,UAAU,KAAK,YAAY,EAAS,SAAS,CACtD,EAAS,OAAO,KAAK,SAAS,EAAS,MAAa,CAIxD,IAAM,EAAc,EAAS,SAAW,GAAK,KAAK,SAAS,WACvD,KAAK,4BAA8B,KAAK,kBAAkB,EAAS,WAAY,GAAM,CAAC,CACtF,IAAI,IAIR,GAFA,KAAK,gBAAgB,OAAO,CAExB,EAAS,QAAU,KAAK,SAAS,OAAQ,CAC3C,KAAK,aAAa,aAAa,CAC/B,IAAK,IAAM,KAAK,EAAS,OAAQ,CAE/B,GAAI,EAAE,WAAa,CAAC,EAAE,UAAW,SAEjC,IAAM,EAAU,GAAe,CAC7B,IAAM,EAAM,EAAG,QAAQ,IAAI,CACrB,EAAU,EAAM,EAAI,EAAY,IAAI,EAAG,MAAM,EAAG,EAAI,CAAC,CAAG,IAAA,GAC9D,OAAO,EAAU,EAAU,EAAG,MAAM,EAAI,CAAG,GAEvC,EAAU,EAAO,EAAE,QAAQ,CAC3B,EAAU,CAAE,IAAA,EAAA,EAAA,kBAAoB,EAAwC,CAAE,GAAI,EAAE,OAAS,CAAE,OAAQ,EAAO,EAAE,OAAO,CAAE,CAAG,EAAE,CAAG,CAC/H,MAAE,QACN,IAAI,EAAE,UAAW,CACX,KAAK,eAAe,eAAe,EAAE,UAAU,GAAK,EAAE,YAAc,YAAc,EAAE,YAAc,cAAgB,EAAE,YAAc,iBACpI,KAAK,aAAa,gBAAgB,EAAE,UAAW,EAAE,UAAW,EAAE,QAAS,EAAE,UAAW,EAAE,MAAM,CAE9F,SAEF,GAAI,CACF,KAAK,aAAa,SAAS,EAAE,MAAO,EAAE,UAAW,EAAE,QAAS,EAAE,UAAW,EAAS,EAAE,MAAO,EAAQ,OAC5F,EAAK,CACZ,QAAQ,KAAK,yBAA0B,EAAI,IAIjD,KAAK,kBAAkB,CAGzB,qBAAqB,EAAsB,CACzC,GAAI,CAAC,KAAK,SAAS,SAAU,MAAO,GACpC,IAAM,EAAW,EAAA,kBAAkB,gBAAgB,EAAI,CAGvD,OAFK,GACL,KAAK,UAAU,EAAA,kBAAkB,UAAU,EAAS,CAAC,CAC9C,IAFe,GAKxB,cAAc,EAAyB,CAChC,KAAK,SAAS,UACnB,EAAA,kBAAkB,aAAa,KAAK,iBAAiB,CAAE,EAAS,CAGlE,MAAM,mBAAmC,CACvC,GAAI,CAAC,KAAK,SAAS,SAAU,OAC7B,IAAM,EAAW,MAAM,EAAA,kBAAkB,cAAc,CACvD,KAAK,UAAU,EAAA,kBAAkB,UAAU,EAAS,CAAC,CAKvD,UAAU,EAAsB,EAC9B,EAAA,EAAA,WAAgB,EAAO,CACvB,KAAK,OAAO,eAAe,CAQ7B,gBAAgB,EAAsB,CACpC,KAAK,aAAe,EACpB,KAAK,UAAU,UAAU,EAAO,CAChC,KAAK,cAAc,UAAU,EAAO,CACpC,KAAK,iBAAiB,UAAU,EAAO,CACvC,KAAK,YAAY,UAAU,EAAO,CAClC,KAAK,iBAAiB,UAAU,EAAO,CACvC,KAAK,cAAc,UAAU,EAAO,CACpC,KAAK,iBAAiB,UAAU,EAAO,CAEvC,KAAK,yBAAyB,CAGhC,iBAA0B,CACxB,OAAO,KAAK,aAKd,UAAU,EAA4B,CAIpC,GAHA,KAAK,aAAe,EAGhB,EAAO,YAAa,CAEtB,IAAM,EAAqB,CACzB,GAFW,KAAK,aAAa,UAE1B,CACH,SAAU,EAAO,YAAY,GAC7B,WAAY,EAAO,YAAY,KAC/B,aAAc,EAAO,YAAY,GACjC,eAAgB,EAAO,YAAY,KACnC,SAAU,EAAO,YAAY,GAAG,QAAQ,IAAK,SAAS,CAAC,QAAQ,OAAQ,QAAQ,EAAI,GAAG,EAAO,YAAY,GAAG,IAC5G,WAAY,EAAO,YAAY,KAAK,QAAQ,IAAK,SAAS,CAAC,QAAQ,OAAQ,QAAQ,EAAI,GAAG,EAAO,YAAY,KAAK,IACnH,CACD,KAAK,aAAa,SAAS,EAAY,CAIrC,EAAO,iBAAmB,IAAA,IAAW,KAAK,oBAAoB,EAAO,eAAe,CAGxF,KAAK,yBAAyB,CAIhC,oBAA4B,EAAgC,CAC1D,KAAK,qBAAuB,EAC5B,KAAK,eAAe,UAAU,CAAE,eAAgB,GAAa,IAAA,GAAW,CAAC,CACzE,KAAK,aAAa,kBAAkB,GAAa,GAAwB,CACzE,KAAK,eAAe,UAAU,kBAAkB,EAAU,CAC1D,KAAK,iBAAiB,kBAAkB,EAAU,CAClD,KAAK,iBAAiB,kBAAkB,EAAU,CAClD,KAAK,YAAY,kBAAkB,EAAU,CAC7C,KAAK,iBAAiB,kBAAkB,EAAU,CAGpD,WAAiC,CAC/B,OAAO,KAAK,aAGd,eAAe,EAA8B,CACtC,KAAK,gBAEV,EAAA,EAAA,oBADkC,EAAgB,KAAK,aACnD,GACF,KAAK,aAAa,YAAc,CAC9B,GAAG,KAAK,aAAa,YACrB,iBACD,EAEH,KAAK,OAAO,eAAe,EAM7B,aAAkD,CAChD,OAAO,KAAK,SAId,YAAY,EAAsC,CAChD,OAAO,OAAO,KAAK,SAAU,EAAM,EAG/B,EAAM,YAAc,IAAS,EAAM,mBAAqB,KAC1D,KAAK,iBAAiB,MAAM,CAE1B,EAAM,OAAS,IAAA,IACjB,KAAK,OAAO,cAAc,EAAA,UAAU,WAAW,CAE7C,EAAM,SAAW,IAAA,KACnB,KAAK,eAAe,WAAW,EAAM,OAAO,CAC5C,KAAK,OAAO,cAAc,EAAA,UAAU,KAAK,EAEvC,EAAM,WAAa,IACrB,KAAK,eAAe,cAAc,KAAK,CAErC,EAAM,UAAY,KACpB,KAAK,eAAe,UAAU,EAAE,CAAC,CACjC,KAAK,eAAe,aAAa,EAAE,CAAC,EAElC,EAAM,WAAa,IAAA,IACrB,KAAK,iBAAiB,WAAW,EAAM,SAAS,CAGlD,KAAK,OAAO,eAAe,CAK7B,QAAe,CACb,IAAM,EAAO,KAAK,OAAO,WAAW,mBAAmB,CACvD,GAAI,EAAK,OAAS,GAAK,EAAK,QAAU,EAAG,OACzC,KAAK,mBAAqB,EAC1B,KAAK,uBAAyB,KAAK,KAAK,CAExC,IAAM,EAAM,KAAK,OAAO,WAAW,QAAQ,CAC3C,KAAK,OAAO,aAAa,OAAO,EAAM,EAAI,CAE1C,IAAM,EAAW,KAAK,SAAS,SAAS,CACxC,KAAK,SAAS,OAAO,EAAK,MAAO,EAAK,OAAO,CAC7C,KAAK,cAAc,OAAO,EAAK,MAAO,EAAK,OAAO,CAClD,KAAK,wBAAwB,EAAS,CACtC,KAAK,SAAS,KAAK,SAAU,EAAK,CAGpC,SAAgB,CACV,KAAK,mBAAmB,cAAc,KAAK,kBAAkB,CAEjE,KAAK,QAAQ,UAAU,KAAK,CAC5B,KAAK,iBAAiB,CACtB,KAAK,kBAAkB,CACvB,KAAK,qBAAqB,CAC1B,AAEE,KAAK,mBADL,OAAO,oBAAoB,UAAW,KAAK,gBAAgB,CACpC,MAEzB,KAAK,gBAAkB,KACvB,KAAK,MAAM,SAAS,CACpB,KAAK,KAAO,KACZ,KAAK,mBAAmB,QAAQ,CAChC,KAAK,eAAe,SAAS,CAC7B,KAAK,SAAS,SAAS,CACvB,KAAK,iBAAiB,SAAS,CAC/B,KAAK,cAAc,SAAS,CAC5B,KAAK,iBAAmB,GACxB,KAAK,cAAc,SAAS,CAC5B,KAAK,aAAa,SAAS,CAC3B,KAAK,gBAAgB,OAAO,CAC5B,KAAK,OAAO,SAAS,CACrB,KAAK,SAAS,SAAS,CACvB,KAAK,UAAU,UAAY,GAK7B,oBAA4B,EAAkD,CAC5E,IAAM,EAAW,GAAgB,EAAO,GAAM,KAAK,cAAc,aAAa,EAAE,CAAC,CAEjF,OADI,aAAoB,EAAA,cAAc,EAAS,aAAc,GAAM,KAAK,YAAY,EAAE,CAAC,CAChF,EAIT,gBAAqC,CACnC,GAAI,KAAK,iBAAkB,OAAO,KAAK,iBACvC,IAAM,EAAM,KAAK,YAAY,SAAS,CACtC,GAAI,EAAI,SAAW,EAAG,OAAO,EAC7B,IAAM,EAAS,GAAmB,KAAK,QAAQ,UAAW,EAAM,GAAM,KAAK,cAAc,aAAa,EAAE,CAAE,KAAK,iBAAiB,CAEhI,MADA,MAAK,iBAAmB,EACjB,EAIT,qBAA6B,EAAkB,EAAoB,CACjE,KAAK,gBAAgB,gBAAgB,EAAM,EAAK,CAChD,KAAK,wBAAwB,EAAK,CAGpC,iBAAyB,EAAwB,CAC/C,KAAK,gBAAgB,eAAe,EAAK,CACzC,KAAK,wBAAwB,EAAE,CASjC,wBAAgC,EAAoB,CAClD,GAAI,KAAK,sBAAwB,KAAM,CACrC,KAAK,oBAAsB,KAAK,IAAI,KAAK,oBAAqB,EAAK,CACnE,OAEF,KAAK,oBAAsB,EAC3B,mBAAqB,CACnB,IAAM,EAAQ,KAAK,oBACnB,KAAK,oBAAsB,KACvB,IAAU,MAAM,KAAK,SAAS,KAAK,kBAAmB,CAAE,KAAM,EAAO,CAAC,EAC1E,CAIJ,gBAA+B,CACzB,KAAK,kBACT,KAAK,gBAAkB,GACvB,0BAA4B,CAC1B,KAAK,gBAAkB,GAEvB,KAAK,eAAiB,KACtB,KAAK,gBAAgB,CACrB,KAAK,mBAAmB,CACxB,KAAK,OAAO,eAAe,CAC3B,KAAK,oBAAoB,EACzB,EAWJ,oBAA4B,EAAc,GAAmB,CAC3D,IAAM,EAAc,KAAK,gBAAgB,CACnC,EAAY,KAAK,QAAQ,YAAc,GAM7C,KAAK,SAAS,WAAW,EAAa,GAAa,CAAC,EAAY,CAC5D,IACF,KAAK,SAAS,aAAa,CACvB,GAAW,KAAK,SAAS,WAAW,EAAa,GAAK,EAG5D,IAAM,EAAK,KAAK,SAAS,UAAU,CAInC,GAAI,EAAY,OAAS,EAAG,CAC1B,IAAM,EAAU,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,aAAa,KAAM,EAAY,OAAS,EAAE,CAAC,CACnF,KAAK,SAAS,iBAAiB,EAAY,IAAU,MAAM,CAK7D,GAAI,EAAW,CACb,IAAM,EAAe,GACnB,KAAK,gBAAgB,qBAAqB,EAAG,aAAa,KAAM,KAAK,IAAI,EAAG,aAAa,GAAI,EAAY,OAAS,EAAE,CAAC,CACrH,KAAK,SAAS,eAAiB,KAAK,gBAAgB,cAAc,EAAa,EAAG,CAAG,KACtF,CACD,GAAI,EAAc,CAChB,IAAM,EAAU,EAAG,WACb,EAAc,KAAK,IAAI,EAAQ,IAAK,EAAa,IAAI,CACrD,EAAc,KAAK,IAAI,EAAQ,IAAK,EAAa,IAAI,CAC3D,GAAI,EAAc,EAAQ,KAAO,EAAc,EAAQ,IAAK,CAC1D,IAAM,EAAQ,EAAc,GAAe,EAC3C,KAAK,SAAS,cACZ,EAAc,EAAQ,IACtB,EAAc,EAAQ,IACvB,GAIP,OAAO,EAIT,wBAAgC,EAAc,GAAa,CAEzD,KAAK,oBAAsB,KAC3B,KAAK,eAAiB,KAEtB,KAAK,cAAc,iBAAiB,KAAK,uBAAuB,CAAG,KAAK,cAAgB,EAAE,CAC1F,IAAM,EAAW,KAAK,mBAAmB,CACzC,KAAK,SAAS,aAAa,EAAS,cAAc,CAElD,KAAK,eAAe,EAAY,CAEhC,KAAK,mBAAmB,CACxB,KAAK,OAAO,eAAe,CAE3B,KAAK,oBAAoB,CAQ3B,eAAuB,EAAc,GAAa,CAChD,KAAK,oBAAoB,EAAY,CACrC,IAAM,EAAe,KAAK,mBAAmB,CAC7C,KAAK,aAAe,KAAK,qBAAqB,EAC1C,KAAK,kBAAkB,EAAI,IAAc,KAAK,oBAAoB,EAAY,CAIpF,kBAAoC,CAClC,IAAM,EAAO,KAAK,aAClB,GAAI,CAAC,EAAM,MAAO,GAClB,IAAM,EAAQ,KAAK,aAAa,UAAU,CACpC,EAAW,EAAuB,CACtC,IAAK,EAAK,WAAW,IACrB,IAAK,EAAK,WAAW,IACrB,UAAW,KACX,aAAc,KACd,OAAQ,KAAK,aACb,WAAY,EAAM,KAAK,OACvB,cAAe,EAAM,KAAK,UAC1B,SAAU,EAAM,IAAS,KAAK,YAAY,EAAM,EAAK,CACtD,CAAC,CACI,EAAO,EAAmB,KAAK,cAAe,EAAS,CAO7D,OANI,IAAS,KAAK,cAAsB,IACxC,KAAK,cAAgB,EACrB,KAAK,cAAc,iBAAiB,EAAK,CACzC,KAAK,oBAAsB,KAC3B,KAAK,eAAiB,KACtB,KAAK,SAAS,aAAa,KAAK,mBAAmB,CAAC,cAAc,CAC3D,IAST,mBAAqC,CACnC,GAAI,CAAC,KAAK,SAAS,UAAW,MAAO,GACrC,GAAM,CAAE,cAAe,KAAK,SAAS,UAAU,CACzC,EAAQ,KAAK,aAAa,UAAU,CACpC,EAAW,EAAuB,CACtC,IAAK,EAAW,IAChB,IAAK,EAAW,IAChB,UAAW,KAAK,iBAAiB,UAAU,CAC3C,aAAc,KAAK,qBACnB,OAAQ,KAAK,aACb,OAAQ,KAAK,eACb,WAAY,EAAM,KAAK,OACvB,cAAe,EAAM,KAAK,UAC1B,SAAU,EAAM,IAAS,KAAK,YAAY,EAAM,EAAK,CACtD,CAAC,CACI,EAAO,EAAmB,KAAK,eAAgB,EAAS,CAS9D,OARI,IAAS,KAAK,eAAuB,IAEzC,KAAK,eAAiB,EACtB,KAAK,cAAc,kBAAkB,EAAK,CAC1C,KAAK,SAAS,kBAAkB,EAAK,CACrC,KAAK,oBAAsB,KAC3B,KAAK,eAAiB,KACtB,KAAK,SAAS,aAAa,KAAK,mBAAmB,CAAC,cAAc,CAC3D,IAGT,YAAoB,EAAc,EAAsB,CAClD,KAAK,aAAe,IAAA,KACtB,KAAK,WAAa,OAAO,SAAa,IAClC,SAAS,cAAc,SAAS,CAAC,WAAW,KAAK,CACjD,MAEN,IAAM,EAAM,KAAK,WAGjB,OAFK,GACL,EAAI,KAAO,EACJ,EAAI,YAAY,EAAK,CAAC,OAFZ,EAAK,OAAS,EAWjC,oBAAmC,CACjC,IAAM,EAAK,KAAK,SAAS,UAAU,CAE7B,EAAO,KAAK,IAAI,EAAG,KAAK,MAAM,EAAG,aAAa,KAAK,CAAC,CACpD,EAAK,KAAK,IAAI,EAAM,KAAK,KAAK,EAAG,aAAa,GAAG,CAAC,EAEtD,CAAC,KAAK,kBACN,KAAK,iBAAiB,OAAS,GAC/B,KAAK,iBAAiB,KAAO,KAE7B,KAAK,iBAAmB,CAAE,OAAM,KAAI,CACpC,KAAK,SAAS,KAAK,qBAAsB,CAAE,OAAM,KAAI,CAAC,EAGxD,GAAM,CAAE,MAAK,OAAQ,EAAG,YAEtB,CAAC,KAAK,uBACN,KAAK,sBAAsB,MAAQ,GACnC,KAAK,sBAAsB,MAAQ,KAEnC,KAAK,sBAAwB,CAAE,MAAK,MAAK,CACzC,KAAK,SAAS,KAAK,mBAAoB,CAAE,MAAK,MAAK,CAAC,EAIlD,KAAK,sBAAwB,EAAG,WAClC,KAAK,oBAAsB,EAAG,SAC9B,KAAK,SAAS,KAAK,aAAc,CAAE,SAAU,EAAG,SAAU,CAAC,EAG7D,KAAK,cAAc,CAGrB,mBAA4B,CAK1B,OAJK,KAAK,sBACR,KAAK,oBAAsB,KAAK,cAAc,SAAS,CACvD,KAAK,oBAAoB,KAAK,oBAAoB,EAE7C,KAAK,oBAId,oBAA4B,EAA6D,CACvF,IAAM,EAAO,EAAO,cAChB,EAAM,EAAK,EACX,EAAS,EAAK,EAAI,EAAK,OAC3B,IAAK,IAAM,KAAS,EAAO,OACrB,EAAM,OAAO,WAAa,OAAS,EAAM,OAAO,WAAa,WACjE,EAAM,KAAK,IAAI,EAAK,EAAM,KAAK,EAAE,CACjC,EAAS,KAAK,IAAI,EAAQ,EAAM,KAAK,EAAI,EAAM,KAAK,OAAO,EAE7D,KAAK,iBAAiB,gBAAgB,CAAE,MAAK,SAAQ,CAAC,CAIxD,qBAAiE,CAC/D,IAAM,EAAM,KAAK,KAAK,CAKtB,OAJI,CAAC,KAAK,oBAAsB,EAAM,KAAK,uBAAyB,OAClE,KAAK,mBAAqB,KAAK,OAAO,WAAW,kBAAkB,CACnE,KAAK,uBAAyB,GAEzB,KAAK,mBAGd,uBAA+E,CAC7E,GAAI,KAAK,eAAgB,OAAO,KAAK,eAErC,IAAM,EAAW,KAAK,mBAAmB,CACnC,EAAS,KAAK,SAAS,UAAU,CAEjC,CAAE,OAAM,MAAO,EAAO,aACtB,EAAS,EAAS,OAAO,IAAK,GAAU,CAG5C,IAAM,EAAW,EAAM,OAAO,SAAW,KAAK,gBAAgB,kBAAkB,EAAM,OAAO,GAAI,EAAM,EAAI,GAAK,CAAG,KAC7G,EAAa,GAAY,KAAK,gBAAgB,kBAAkB,EAAM,OAAO,GAAI,EAAM,EAAG,EAAI,CAAE,GAAG,GAAoB,CAIvH,EAAY,CAChB,EAAG,EAAM,KAAK,EACd,EAAG,EAAM,KAAK,EAAI,GAClB,MAAO,EAAM,KAAK,MAClB,OAAQ,KAAK,IAAI,EAAG,EAAM,KAAK,OAAS,GAAoB,CAC7D,CAED,MAAO,CACL,WAAY,EAAM,OAAO,GACzB,QAAS,KAAK,gBAAgB,eAAe,EAAM,OAAO,GAAG,CAC7D,KAAM,EAAM,KACZ,SAAU,CACR,GAAG,EACH,UAAW,EACX,aAGA,SAAU,IAAa,KAEvB,UAAW,EAAM,OAAO,aAAe,aAAwB,UAC/D,cAAe,EAAM,OAAO,aAAe,KAAK,aAAa,EAAM,OAAO,GAAI,EAAM,EAAG,CAAG,IAAA,GAE1F,YAAa,IAAA,GACb,UAAW,IAAA,GACX,YAAa,CAAC,CAAC,EAAM,OAAO,YAC7B,CACF,EACD,CAGF,MADA,MAAK,eAAiB,EACf,EAIT,aAA8B,CAC5B,IAAM,EAAW,KAAK,mBAAmB,CACnC,EAAoB,EAAS,OAChC,OAAO,GAAK,EAAE,OAAO,WAAa,SAAS,CAC3C,QAAQ,EAAK,IAAM,EAAM,EAAE,KAAK,OAAQ,EAAE,CAC7C,OAAO,EAAS,cAAc,EAAI,EAAS,cAAc,OAAS,EAGpE,mBAAkC,CAE5B,KAAK,cAAc,UAAU,EAC/B,KAAK,cAAc,WACjB,KAAK,SAAS,UAAU,CACxB,KACA,KACA,KAAK,aAAa,UAAU,CAC7B,CAGH,IAAM,EAAS,KAAK,SAAS,WAAa,KAAK,uBAAuB,CAAG,EAAE,CACrE,EAAY,KAAK,aAAa,CAE9B,EAAc,KAAK,gBAAgB,CACzC,KAAK,aAAe,KAAK,qBAAqB,CAC9C,KAAK,OAAO,iBAAiB,CAC3B,aAAc,KAAK,aACnB,cAAe,KAAK,cACpB,cAAe,KAAK,cACpB,cAAe,KAAK,kBAAoB,KAAK,cAAgB,KAC7D,WAAY,KAAK,YAAY,CAC7B,aAAc,KAAK,SAAS,KAAO,KAAK,aAAe,KACvD,UAAW,KAAK,SAAS,UAAY,KAAK,UAAY,KACtD,SAAU,KAAK,SAAS,SAAW,KAAK,SAAW,KACnD,iBAAkB,KAAK,SAAS,UAAY,KAAK,iBAAmB,KACpE,gBAAiB,KAAK,SAAS,WAAa,KAAK,gBAAkB,KACnE,gBAAiB,KAAK,SAAS,SAAW,KAAK,gBAAkB,KACjE,gBAAiB,KAAK,SAAS,QAAU,KAAK,gBAAkB,KAChE,iBAAkB,KAAK,iBACvB,YAAa,KAAK,SAAS,OAAS,KAAK,YAAc,KACvD,eAAgB,KAAK,SAAS,OAAS,KAAK,eAAiB,KAC7D,cAAe,KAAK,cACpB,cAAe,KAAK,cACpB,aAAc,KAAK,aACnB,aAAc,KAAK,aACnB,aAAc,KAAK,aACnB,UAAW,KAAK,SAAS,UAAY,KAAK,UAAY,KACtD,aAAc,KAAK,aACnB,cAAe,KAAK,cACpB,eAAgB,KAAK,eACrB,gBAAiB,KAAK,gBACtB,aAAc,KAAK,SAAS,OAAS,KAAK,aAAe,KACzD,eAAgB,KAAK,eACrB,oBAAqB,KAAK,oBAC1B,mBAAoB,KAAK,mBACzB,oBAAqB,KAAK,oBAC1B,iBAAkB,KAAK,iBACvB,SACA,YAAa,KAAK,kBAAkB,CACpC,YAGA,SAAU,CAAE,GAAG,KAAK,SAAS,UAAU,CAAE,KAAM,EAAa,CAC5D,MAAO,KAAK,aAAa,UAAU,CACnC,KAAM,EACN,aAAc,KAAK,aACnB,WAAY,KAAK,WACjB,qBAAsB,KAAK,SAAS,qBACpC,YAAc,GAAU,KAAK,YAAY,EAAM,CAC/C,sBAAuB,EAAG,IAAU,KAAK,mBAAmB,EAAG,EAAM,CACtE,CAAC,CAIJ,mBACE,EACA,EACM,CACN,IAAM,EAAW,EAAiB,KAAK,cAAc,aAAa,CAAE,EAAM,CAC1E,GAAI,EAAS,SAAW,EAAG,OAC3B,IAAM,EAAU,CACd,SAAU,KAAK,SAAS,UAAU,CAClC,KAAM,KAAK,gBAAgB,CAC3B,MAAO,KAAK,aAAa,UAAU,CACpC,CACD,IAAK,IAAM,KAAW,EACpB,EAAQ,OAAO,EAAK,EAAQ,CAIhC,kBAA2B,CACzB,GAAI,CAAC,KAAK,cAAc,aAAa,SAAW,CAAC,KAAK,aAAa,YAAY,eAAgB,OAAO,KACtG,IAAM,GAAA,EAAA,EAAA,oBAA4B,KAAK,aAAa,YAAY,eAAgB,KAAK,aAAa,CAElG,OADK,EACE,CACL,GAAG,EACH,OAAQ,KAAK,aAAa,YAAc,CACtC,QAAS,KAAK,aAAa,YAAY,QACvC,MAAO,KAAK,aAAa,YAAY,MACrC,UAAW,KAAK,aAAa,YAAY,UAC1C,CAAG,IAAA,GACL,CARmB,OAkBxB,SAAS,GAAgB,EAAyD,CAChF,IAAM,EAAO,IAAI,IAAI,EAAK,IAAK,GAAQ,CAAC,EAAI,WAAY,EAAI,CAAC,CAAC,CACxD,EAA2B,EAAE,CAC7B,EAAO,IAAI,IACX,EAAW,IAAI,IACf,EAAS,GAA2B,CACxC,GAAI,EAAK,IAAI,EAAI,WAAW,EAAI,EAAS,IAAI,EAAI,WAAW,CAAE,OAC9D,EAAS,IAAI,EAAI,WAAW,CAC5B,IAAM,EAAQ,CAAC,EAAI,KAAM,GAAG,OAAO,OAAO,EAAI,OAAO,CAAC,IAAK,IAAA,EAAA,EAAA,sBAA2B,EAAE,EAAE,WAAW,CAAC,CACtG,IAAK,IAAM,KAAM,EAAO,CACtB,IAAM,EAAM,EAAK,EAAK,IAAI,EAAG,CAAG,IAAA,GAC5B,GAAK,EAAM,EAAI,CAErB,EAAS,OAAO,EAAI,WAAW,CAC/B,EAAK,IAAI,EAAI,WAAW,CACxB,EAAI,KAAK,EAAI,EAEf,IAAK,IAAM,KAAO,EAAM,EAAM,EAAI,CAClC,OAAO,EAIT,SAAS,GAAiB,EAAsB,EAA+B,CAC7E,OAAO,EAAE,KAAO,EAAE,KACZ,EAAE,MAAQ,SAAW,EAAE,MAAQ,QAC/B,EAAE,OAAS,SAAW,EAAE,OAAS,OACjC,EAAE,WAAa,IAAA,KAAgB,EAAE,WAAa,IAAA,IAItD,SAAS,EAAW,EAAkC,CACpD,IAAM,EAAI,EAAO,UACjB,OAAO,OAAO,GAAM,UAAY,OAAO,SAAS,EAAE,EAAI,GAAK,EAAI,CAAE,UAAW,KAAK,IAAI,EAAG,IAAI,CAAE,CAAG,EAAE,CAIrG,IAAM,EAAyC,IAAI,IAAI,CAAC,gBAAiB,SAAS,CAAC,CAGnF,SAAS,GACP,EACA,EACqC,CAGrC,OAFK,EACA,EACE,CAAE,IAAK,KAAK,IAAI,EAAE,IAAK,EAAE,IAAI,CAAE,IAAK,KAAK,IAAI,EAAE,IAAK,EAAE,IAAI,CAAE,CADpD,EADA,EAMjB,SAAS,EAAa,EAA4F,CAChH,MAAO,CACL,GAAI,EAAM,GACV,MAAO,EAAM,MACb,UAAW,EAAM,UACjB,QAAS,EAAM,QACf,UAAW,EAAM,UACjB,QAAS,EAAM,QACf,MAAO,EAAM,MACb,OAAQ,EAAM,OACd,UAAW,EAAM,UACjB,QAAS,EAAM,QACf,KAAM,EAAM,KACb,CAIH,IAAM,GAAoB,IAG1B,SAAS,GAAW,EAAoB,EAAsB,CAC5D,IAAI,EAAK,EACL,EAAK,EAAO,OAChB,KAAO,EAAK,GAAI,CACd,IAAM,EAAO,EAAK,GAAO,EACrB,EAAO,GAAK,MAAQ,EAAM,EAAK,EAAM,EACpC,EAAK,EAEZ,OAAO,EAQT,SAAS,GAAY,EAA6C,CAChE,IAAI,EAAQ,EACR,EAAS,EACb,IAAK,IAAI,EAAI,EAAG,EAAI,EAAI,OAAQ,IAC1B,EAAI,GAAG,IAAM,EAAI,GAAO,MAAK,EAAQ,GACrC,EAAI,GAAG,KAAO,EAAI,GAAQ,OAAM,EAAS,GAE/C,IAAM,EAAwB,CAAC,EAAI,GAAO,IAAK,EAAI,GAAO,KAAK,CACzD,EAAyB,CAAC,EAAI,GAAQ,KAAM,EAAI,GAAQ,KAAK,CAC7D,EAAW,IAAU,EAA0B,EAAI,GAAO,OAAS,EAAI,GAAO,KAAhD,EAAQ,EACtC,EAAO,EAAI,EAAI,OAAS,GAC9B,MAAO,CAAC,GAAI,EAAW,CAAC,EAAK,EAAK,CAAG,CAAC,EAAM,EAAI,CAAG,CAAC,EAAK,MAAO,EAAK,KAAK,CAAC,CAI7E,SAAS,GAAY,EAA4B,CAC/C,IAAI,EAAO,EACX,IAAK,IAAI,EAAI,KAAK,IAAI,EAAG,EAAO,OAAS,GAAG,CAAE,EAAI,EAAO,OAAQ,IAAK,EAAO,KAAK,IAAI,EAAM,EAAO,GAAG,KAAO,EAAO,EAAI,GAAG,KAAK,CAChI,OAAO,EAAO,EAAI,EAAO,IAS3B,SAAS,GAAY,EAAmB,EAAuC,CAC7E,GAAI,EAAO,SAAW,EAAG,OAAO,KAChC,IAAM,EAAM,EAAO,EAAO,OAAS,GAAG,KAAO,GAAY,EAAO,CAC1D,EAAS,EACZ,OAAQ,GAAM,EAAW,EAAE,EAAI,EAAE,MAAQ,EAAO,GAAG,MAAQ,EAAE,KAAO,EAAI,CACxE,MAAM,EAAG,IAAM,EAAE,KAAO,EAAE,KAAK,CAC5B,EAAkB,EAAE,CAC1B,IAAK,IAAM,KAAO,GAAY,EAAK,SAAW,GAAK,EAAK,EAAK,OAAS,GAAG,OAAS,EAAI,OAAM,EAAK,KAAK,EAAI,CAC1G,GAAI,EAAK,SAAW,EAAG,OAAO,KAE9B,IAAM,EAAO,EAAO,UAAW,GAAM,EAAE,MAAQ,EAAK,GAAG,KAAK,CAC5D,GAAI,EAAO,EAAG,OAAO,KACrB,IAAM,EAAO,EAAO,MAAM,EAAG,EAAK,CAC5B,EAAO,EAAK,OAAQ,GAAM,EAAE,MAAQ,EAAO,GAAM,KAAK,CAC5D,MAAO,CAAC,GAAG,EAAM,GAAG,EAAK,CAG3B,SAAS,EAAW,EAAqB,CACvC,MAAO,CAAC,EAAE,KAAM,EAAE,KAAM,EAAE,KAAM,EAAE,IAAK,EAAE,MAAM,CAAC,MAAM,OAAO,SAAS,CAIxE,SAAS,GAAM,EAAoB,EAAsB,CACvD,IAAI,EAAK,EACL,EAAK,EAAO,OAAS,EACzB,KAAO,EAAK,GAAI,CACd,IAAM,EAAO,EAAK,EAAK,GAAM,EACzB,EAAO,GAAK,MAAQ,EAAM,EAAK,EAC9B,EAAK,EAAM,EAElB,OAAO,EAIT,SAAS,EAAc,EAAmB,EAAoB,EAA0B,CACtF,IAAM,EAAM,KAAK,IAAI,EAAG,KAAK,IAAI,KAAK,MAAM,OAAO,SAAS,EAAS,CAAG,EAAW,EAAE,CAAE,EAAO,OAAS,EAAE,CAAC,CACpG,EAAM,EAAM,EAAI,EAAO,OAAS,EAAO,EAAM,GAAG,KAAO,IACzD,EAAK,GACT,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,QAAU,EAAM,GAAG,KAAO,EAAK,IAAK,EAAK,EACnE,OAAO,KAAK,IAAI,EAAG,EAAG,CAIxB,SAAS,GAAW,EAAc,EAAmB,EAAwB,CAC3E,IAAI,EAAI,EACR,KAAO,EAAI,GAAK,EAAM,EAAI,GAAG,MAAQ,EAAI,MAAM,IAC/C,IAAI,EAAe,CAAE,GAAG,EAAM,GAAI,KAAM,EAAI,KAAM,CAClD,IAAK,IAAI,EAAI,EAAI,EAAG,GAAK,EAAO,IAAK,EAAM,EAAS,EAAK,EAAM,GAAG,CAClE,OAAO,EAIT,SAAS,EAAS,EAAc,EAAwB,CACtD,MAAO,CACL,KAAM,EAAI,KACV,KAAM,EAAI,KACV,KAAM,KAAK,IAAI,EAAI,KAAM,EAAK,KAAK,CACnC,IAAK,KAAK,IAAI,EAAI,IAAK,EAAK,IAAI,CAChC,MAAO,EAAK,MACZ,QAAS,EAAI,QAAU,IAAM,EAAK,QAAU,GAC7C,CAIH,SAAS,EAAa,EAA4B,EAA6C,CAC7F,IAAM,EAAO,EAAO,EAAO,OAAS,GACpC,GAAI,CAAC,EAAM,MAAO,SAClB,IAAM,GAAA,EAAA,EAAA,kBAAsB,EAAI,KAAK,CAC/B,GAAA,EAAA,EAAA,kBAA0B,EAAK,KAAK,CAC1C,OAAO,EAAK,EAAS,SAAW,IAAO,EAAS,SAAW,QCn+J7D,IAAM,EAAY,CAAC,OAAQ,YAAa,OAAQ,WAAY,IAAI,CAC1D,GAAY,CAAC,OAAQ,IAAI,CACzB,GAAY,CAAC,OAAQ,IAAI,CACzB,GAAW,CAAC,MAAO,IAAI,CACvB,GAAa,CAAC,QAAS,IAAI,CAC3B,GAAW,CAAC,SAAU,MAAO,IAAI,CAEvC,SAAgB,GAAW,EAA2B,CACpD,IAAM,EAAU,EAAK,MAAM,CAM3B,OALK,EAED,EAAQ,KAAO,KAAO,EAAQ,KAAO,IAChC,GAAU,EAAQ,CAEpB,GAAS,EAAQ,CALH,CAAE,KAAM,EAAE,CAAE,SAAU,EAAG,QAAS,EAAG,CAQ5D,SAAS,GAAU,EAA2B,CAC5C,IAAI,EACJ,GAAI,CACF,EAAM,KAAK,MAAM,EAAK,OACf,EAAG,CACV,MAAU,MAAM,iBAAkB,EAAY,UAAU,CAE1D,GAAI,CAAC,MAAM,QAAQ,EAAI,CACrB,MAAU,MAAM,sCAAsC,CAExD,IAAM,EAAkB,EAAE,CACtB,EAAU,EACd,IAAK,IAAM,KAAO,EAAK,CACrB,IAAM,EAAM,MAAM,QAAQ,EAAI,CAAG,GAAe,EAAI,CAAG,GAAgB,EAA+B,CAClG,EAAK,EAAK,KAAK,EAAI,CAAO,IAEhC,OAAO,GAAS,EAAM,EAAI,OAAQ,EAAQ,CAG5C,SAAS,GAAS,EAA2B,CAC3C,IAAM,EAAQ,EAAK,MAAM,QAAQ,CAAC,OAAO,GAAK,EAAE,MAAM,CAAC,OAAS,EAAE,CAClE,GAAI,EAAM,SAAW,EAAG,MAAO,CAAE,KAAM,EAAE,CAAE,SAAU,EAAG,QAAS,EAAG,CAEpE,IAAM,EAAY,GAAgB,EAAM,GAAG,CACrC,EAAS,EAAU,EAAM,GAAI,EAAU,CAAC,IAAI,GAAK,EAAE,MAAM,CAAC,aAAa,CAAC,CAIxE,EAAkB,EAAO,KAAK,GAAK,MAAM,OAAO,EAAE,CAAC,EAAI,EAAE,OAAS,EAAE,CACpE,EAAS,EACX,GAAe,EAAO,CACtB,CAAE,KAAM,EAAG,KAAM,EAAG,KAAM,EAAG,IAAK,EAAG,MAAO,EAAG,OAAQ,EAAG,CAE9D,GAAI,EAAO,KAAO,GAAK,EAAO,KAAO,GAAK,EAAO,KAAO,GAAK,EAAO,IAAM,GAAK,EAAO,MAAQ,EAC5F,MAAU,MAAM,kEAAkE,CAGpF,IAAM,EAAkB,EAAE,CACtB,EAAU,EACd,IAAK,IAAI,EAAI,KAAyB,EAAI,EAAM,OAAQ,IAAK,CAC3D,IAAM,EAAO,EAAU,EAAM,GAAI,EAAU,CACrC,EAAO,EAAc,EAAK,EAAO,MAAM,CAIvC,EAAO,EAAQ,EAAK,EAAO,MAAM,CACjC,EAAO,EAAQ,EAAK,EAAO,MAAM,CACjC,EAAM,EAAQ,EAAK,EAAO,KAAK,CAC/B,EAAQ,EAAQ,EAAK,EAAO,OAAO,CACnC,EAAS,EAAO,QAAU,EAAI,OAAO,EAAK,EAAO,QAAQ,CAAG,EAClE,GAAI,IAAS,MAAQ,CAAC,CAAC,EAAM,EAAM,EAAK,EAAM,CAAC,MAAM,OAAO,SAAS,CAAE,CACrE,IACA,SAEF,EAAK,KAAK,CAAE,OAAY,OAAa,OAAY,MAAa,QAAQ,OAAQ,OAAO,SAAS,EAAO,CAAG,EAAS,EAAG,CAAC,CAGvH,OAAO,GAAS,EAAM,EAAM,OAAU,KAA0B,EAAQ,CAG1E,SAAS,GAAgB,EAA4B,CACnD,IAAM,EAAiC,CAAE,IAAK,EAAG,IAAK,EAAG,IAAM,EAAG,IAAK,EAAG,CAC1E,IAAK,IAAM,KAAM,EACX,KAAM,GAAQ,EAAO,KAE3B,IAAI,EAAO,IACP,EAAY,EAChB,IAAK,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,EAAO,CACrC,EAAI,IAAa,EAAO,EAAG,EAAY,GAE7C,OAAO,EAIT,SAAS,EAAU,EAAc,EAA6B,CAC5D,IAAM,EAAgB,EAAE,CACpB,EAAM,GACN,EAAU,GACd,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAK,EAAK,GACZ,IAAO,IACL,GAAW,EAAK,EAAI,KAAO,KAAO,GAAO,IAAK,KAC7C,EAAU,CAAC,EACP,IAAO,GAAa,CAAC,GAC9B,EAAI,KAAK,EAAI,CACb,EAAM,IAEN,GAAO,EAIX,OADA,EAAI,KAAK,EAAI,CACN,EAGT,SAAS,GAAe,EAA4G,CAClI,IAAM,EAAQ,GAAmB,EAAO,UAAU,GAAK,EAAK,SAAS,EAAE,CAAC,CACxE,MAAO,CACL,KAAM,EAAK,EAAU,CACrB,KAAM,EAAK,GAAU,CACrB,KAAM,EAAK,GAAU,CACrB,IAAK,EAAK,GAAS,CACnB,MAAO,EAAK,GAAW,CACvB,OAAQ,EAAK,GAAS,CACvB,CAGH,SAAS,GAAgB,EAA8C,CACrE,IAAM,EAAO,GAAmB,CAC9B,IAAK,IAAM,KAAK,EAAM,CACpB,GAAI,EAAI,KAAO,IAAA,GAAW,OAAO,EAAI,GACrC,IAAM,EAAQ,EAAE,aAAa,CAC7B,GAAI,EAAI,KAAW,IAAA,GAAW,OAAO,EAAI,KAIvC,EAAO,EAAc,OAAO,EAAI,EAAU,EAAI,GAAG,CAAC,CAClD,EAAO,OAAO,EAAI,GAAU,CAAC,CAC7B,EAAO,OAAO,EAAI,GAAU,CAAC,CAC7B,EAAM,OAAO,EAAI,GAAS,CAAC,CAC3B,EAAQ,OAAO,EAAI,GAAW,CAAC,CAC/B,EAAS,OAAO,EAAI,GAAS,EAAI,EAAE,CAEzC,OADI,IAAS,MAAQ,CAAC,CAAC,EAAM,EAAM,EAAK,EAAM,CAAC,MAAM,OAAO,SAAS,CAAS,KACvE,CAAE,OAAM,OAAM,OAAM,MAAK,QAAO,OAAQ,OAAO,SAAS,EAAO,CAAG,EAAS,EAAG,CAGvF,SAAS,GAAe,EAAgC,CACtD,GAAI,EAAI,OAAS,EAAG,OAAO,KAC3B,IAAM,EAAO,EAAc,OAAO,EAAI,GAAG,CAAC,CACpC,EAAO,OAAO,EAAI,GAAG,CACrB,EAAO,OAAO,EAAI,GAAG,CACrB,EAAM,OAAO,EAAI,GAAG,CACpB,EAAQ,OAAO,EAAI,GAAG,CACtB,EAAS,EAAI,QAAU,EAAI,OAAO,EAAI,GAAG,CAAG,EAElD,OADI,IAAS,MAAQ,CAAC,CAAC,EAAM,EAAM,EAAK,EAAM,CAAC,MAAM,OAAO,SAAS,CAAS,KACvE,CAAE,OAAM,OAAM,OAAM,MAAK,QAAO,OAAQ,OAAO,SAAS,EAAO,CAAG,EAAS,EAAG,CASvF,SAAS,EAAQ,EAAiC,CAEhD,OADI,IAAQ,IAAA,IAAa,EAAI,MAAM,GAAK,GAAW,IAC5C,OAAO,EAAI,CAGpB,SAAS,EAAc,EAA4B,CACjD,IAAM,EAAU,EAAI,MAAM,CAC1B,GAAI,CAAC,EAAS,OAAO,KACrB,IAAM,EAAQ,OAAO,EAAQ,CAC7B,GAAI,OAAO,SAAS,EAAM,EAAI,EAAQ,EACpC,OAAO,EAAQ,aAAO,EAAQ,IAAO,EAEvC,IAAM,EAAS,KAAK,MAAM,EAAQ,CAClC,OAAO,OAAO,SAAS,EAAO,CAAG,EAAS,KAG5C,SAAS,GAAS,EAAiB,EAAkB,EAA8B,CACjF,EAAK,MAAM,EAAG,IAAM,EAAE,KAAO,EAAE,KAAK,CAGpC,IAAM,EAAiB,EAAE,CACzB,IAAK,IAAM,KAAO,EACZ,EAAI,OAAS,GAAK,EAAI,EAAI,OAAS,GAAG,OAAS,EAAI,KACrD,EAAI,EAAI,OAAS,GAAK,EAEtB,EAAI,KAAK,EAAI,CAGjB,MAAO,CAAE,KAAM,EAAK,WAAU,UAAS,CC9LzC,IAAa,GAAb,KAA8B,CAC5B,KACA,QAAyC,KACzC,UACA,MACA,MAAgB,EAEhB,YAAY,EAAmB,EAAsC,CACnE,KAAK,KAAO,EACZ,KAAK,UAAY,EACjB,KAAK,MAAQ,CACX,UAAY,GAAM,KAAK,YAAY,EAAE,CACrC,SAAW,GAAM,KAAK,WAAW,EAAE,CACnC,UAAY,GAAM,KAAK,YAAY,EAAE,CACrC,KAAO,GAAM,KAAK,OAAO,EAAE,CAC5B,CAGH,QAAe,CACb,KAAK,KAAK,iBAAiB,YAAa,KAAK,MAAM,UAAU,CAC7D,KAAK,KAAK,iBAAiB,WAAY,KAAK,MAAM,SAAS,CAC3D,KAAK,KAAK,iBAAiB,YAAa,KAAK,MAAM,UAAU,CAC7D,KAAK,KAAK,iBAAiB,OAAQ,KAAK,MAAM,KAAK,CAGrD,QAAe,CACb,KAAK,KAAK,oBAAoB,YAAa,KAAK,MAAM,UAAU,CAChE,KAAK,KAAK,oBAAoB,WAAY,KAAK,MAAM,SAAS,CAC9D,KAAK,KAAK,oBAAoB,YAAa,KAAK,MAAM,UAAU,CAChE,KAAK,KAAK,oBAAoB,OAAQ,KAAK,MAAM,KAAK,CACtD,KAAK,aAAa,CAGpB,YAAoB,EAAoB,CACjC,EAAS,EAAE,GAChB,EAAE,gBAAgB,CAClB,KAAK,QACL,KAAK,aAAa,EAGpB,WAAmB,EAAoB,CAChC,EAAS,EAAE,GAChB,EAAE,gBAAgB,CACd,EAAE,eAAc,EAAE,aAAa,WAAa,SAGlD,YAAoB,EAAoB,CACjC,EAAS,EAAE,GAChB,KAAK,MAAQ,KAAK,IAAI,EAAG,KAAK,MAAQ,EAAE,CACpC,KAAK,QAAU,GAAG,KAAK,aAAa,EAG1C,MAAc,OAAO,EAA6B,CAChD,EAAE,gBAAgB,CAClB,KAAK,MAAQ,EACb,KAAK,aAAa,CAElB,IAAM,EAAO,EAAE,cAAc,QAAQ,GAChC,KAEL,GAAI,CAEF,IAAM,EAAS,GAAW,MADP,EAAK,MAAM,CACC,CAC/B,GAAI,EAAO,KAAK,SAAW,EACzB,MAAU,MAAM,iCAAiC,EAAK,KAAK,GAAG,CAEhE,KAAK,UAAU,OAAO,EAAO,KAAM,EAAQ,EAAK,OACzC,EAAK,CACZ,KAAK,UAAU,UAAU,EAAc,EAAK,EAIhD,aAA4B,CACtB,KAAK,UACT,KAAK,QAAU,SAAS,cAAc,MAAM,CAC5C,OAAO,OAAO,KAAK,QAAQ,MAAO,CAChC,SAAU,WACV,MAAO,MACP,OAAQ,KACR,cAAe,OACf,aAAc,OACd,OAAQ,wCACR,WAAY,2BACZ,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,MAAO,6BACP,SAAU,OACV,WAAY,MACZ,cAAe,WACf,WAAY,UACZ,eAAgB,YACjB,CAAC,CACF,KAAK,QAAQ,YAAc,sCAGhB,iBAAiB,KAAK,KAC7B,CAAG,WAAa,WAAU,KAAK,KAAK,MAAM,SAAW,YACzD,KAAK,KAAK,YAAY,KAAK,QAAQ,EAGrC,aAA4B,CAC1B,KAAK,SAAS,QAAQ,CACtB,KAAK,QAAU,OAInB,SAAS,EAAS,EAAuB,CACvC,OAAO,EAAE,cAAc,OAAO,SAAS,QAAQ,EAAI,GC3FrD,SAAS,EAAQ,EAAgD,CAC/D,IAAM,EAA0B,CAAE,GAAI,EAAO,GAAI,KAAM,EAAO,KAAM,UAAW,EAAO,UAAW,CAIjG,OAHI,EAAO,SAAW,IAAA,KAAW,EAAI,OAAS,EAAO,QACjD,EAAO,YAAc,IAAA,KAAW,EAAI,UAAY,EAAO,WACvD,EAAO,OAAS,IAAA,KAAW,EAAI,KAAO,EAAO,MAC1C,EAGT,IAAM,IAAe,EAAuB,IAAkC,EAAE,UAAY,EAAE,UAG9F,SAAgB,EAAkB,EAA2C,CAC3E,GAAI,OAAO,GAAU,WAAY,EAAgB,OAAO,KACxD,IAAM,EAAI,EAEV,OADI,OAAO,EAAE,IAAO,UAAY,CAAC,EAAE,IAAM,OAAO,EAAE,MAAS,UAAY,OAAO,EAAE,WAAc,UAAY,CAAC,OAAO,SAAS,EAAE,UAAU,CAAS,KACzI,EAAQ,CACb,GAAI,EAAE,GACN,KAAM,EAAE,KACR,UAAW,EAAE,UACb,OAAQ,OAAO,EAAE,QAAW,SAAW,EAAE,OAAS,IAAA,GAClD,UAAW,OAAO,EAAE,WAAc,SAAW,EAAE,UAAY,IAAA,GAC3D,KAAM,OAAO,EAAE,MAAS,SAAW,EAAE,KAAO,IAAA,GAC7C,CAAC,CAIJ,SAAgB,GAAgB,EAAoC,CAClE,IAAM,EAAQ,EAAkB,EAAM,CAChC,EAAU,EAAS,EAAkC,QAAU,IAAA,GACrE,OAAO,GAAS,OAAO,GAAY,SAAW,CAAE,GAAG,EAAO,UAAS,CAAG,KAIxE,SAAgB,GAAc,EAAkC,EAAE,CAAiB,CACjF,IAAM,EAAU,IAAI,IAAI,EAAQ,IAAK,GAAM,CAAC,EAAE,GAAI,CAAE,GAAG,EAAG,CAAC,CAAC,CAAC,CAC7D,MAAO,CACL,SAAY,CAAC,GAAG,EAAQ,QAAQ,CAAC,CAAC,IAAI,EAAQ,CAAC,KAAK,GAAY,CAChE,KAAO,GAAO,CACZ,IAAM,EAAQ,EAAQ,IAAI,EAAG,CAC7B,OAAO,EAAQ,CAAE,GAAG,EAAO,CAAG,MAEhC,KAAO,GAAW,CAChB,EAAQ,IAAI,EAAO,GAAI,CAAE,GAAG,EAAQ,CAAC,EAEvC,OAAS,GAAO,CACd,EAAQ,OAAO,EAAG,EAErB,CAQH,SAAgB,GAAoB,EAAS,eAA+B,CAC1E,IAAM,EAAW,GAAG,EAAO,OACrB,EAAW,GAAe,GAAG,EAAO,OAAO,IAE3C,MAAwC,CAC5C,IAAI,EACJ,GAAI,CACF,EAAM,KAAK,MAAM,aAAa,QAAQ,EAAS,EAAI,KAAK,MAClD,CACN,MAAO,EAAE,CAGX,OADK,MAAM,QAAQ,EAAI,CAChB,EAAI,IAAI,EAAkB,CAAC,OAAQ,GAA+B,IAAM,KAAK,CADpD,EAAE,EAG9B,EAAc,GAAyC,aAAa,QAAQ,EAAU,KAAK,UAAU,EAAM,CAAC,CAElH,MAAO,CACL,SAAY,GAAW,CAAC,KAAK,GAAY,CACzC,KAAO,GAAO,CACZ,IAAM,EAAQ,GAAW,CAAC,KAAM,GAAM,EAAE,KAAO,EAAG,CAC5C,EAAU,EAAQ,aAAa,QAAQ,EAAQ,EAAG,CAAC,CAAG,KAC5D,OAAO,GAAS,IAAY,KAAO,CAAE,GAAG,EAAO,UAAS,CAAG,MAE7D,KAAO,GAAW,CAEhB,aAAa,QAAQ,EAAQ,EAAO,GAAG,CAAE,EAAO,QAAQ,CACxD,EAAW,CAAC,GAAG,GAAW,CAAC,OAAQ,GAAM,EAAE,KAAO,EAAO,GAAG,CAAE,EAAQ,EAAO,CAAC,CAAC,EAEjF,OAAS,GAAO,CACd,EAAW,GAAW,CAAC,OAAQ,GAAM,EAAE,KAAO,EAAG,CAAC,CAClD,aAAa,WAAW,EAAQ,EAAG,CAAC,EAEvC,CCxHH,IAAa,GAAkB,GAG/B,SAAgB,EAAgB,EAA6B,CAE3D,OADc,EAAK,QAAQ,OAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,EAAA,GAAmB,CAAC,MAClE,EAAS,KA6BlB,SAAS,IAAmB,CAE1B,OADW,WAA0D,QAC3D,cAAc,EAAI,GAAG,KAAK,KAAK,CAAC,SAAS,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAG,GAAG,GAGnG,SAAS,EAAU,EAAgD,CACjE,GAAM,CAAE,KAAI,OAAM,SAAQ,YAAW,YAAW,QAAS,EACzD,MAAO,CACL,KACA,OACA,GAAI,IAAW,IAAA,GAAyB,EAAE,CAAf,CAAE,SAAQ,CACrC,GAAI,IAAc,IAAA,GAA4B,EAAE,CAAlB,CAAE,YAAW,CAC3C,YACA,GAAI,IAAS,IAAA,GAAuB,EAAE,CAAb,CAAE,OAAM,CAClC,CAgBH,IAAa,GAAb,KAA2B,CACzB,cAAmD,KAEnD,aAAsC,KACtC,MAAgB,GAChB,SACA,WACA,KACA,MAAsD,KACtD,SAAmB,EAEnB,KAAe,EACf,iBAA2B,GAC3B,KAAiC,QAAQ,SAAS,CAClD,UAAoB,GACpB,IACA,MAEA,YACE,EACA,EACA,EAAiD,EAAE,CACnD,CAHiB,KAAA,QAAA,EACA,KAAA,KAAA,EACA,KAAA,QAAA,EAEjB,KAAK,SAAW,EAAQ,UAAY,GACpC,KAAK,WAAa,EAAQ,YAAc,KACxC,KAAK,KAAO,EAAQ,MAAQ,QAC5B,KAAK,IAAM,EAAQ,KAAO,KAAK,IAC/B,KAAK,MAAQ,EAAQ,OAAS,GAIhC,SAAqC,CACnC,OAAO,KAAK,cAId,SAAmB,CACjB,OAAO,KAAK,MAGd,YAAsB,CACpB,OAAO,KAAK,SAGd,YAAY,EAAmB,CACzB,KAAK,WAAa,IACtB,KAAK,SAAW,EAChB,KAAK,QAAQ,CACT,GAAM,KAAK,OAAO,KAAK,SAAS,EAItC,MAAM,MAAsC,CAC1C,IAAM,EAAe,MAAM,KAAK,QAAQ,MAAM,CAE9C,OADK,MAAM,QAAQ,EAAI,CAChB,EACJ,IAAI,EAAkB,CACtB,OAAQ,GAA+B,IAAM,OAAS,EAAE,MAAQ,WAAa,KAAK,KAAK,CACvF,MAAM,EAAG,IAAM,EAAE,UAAY,EAAE,UAAU,CAJZ,EAAE,CAQpC,MAA2C,CACzC,OAAO,KAAK,UAAY,CACtB,IAAM,EAAU,KAAK,cACrB,OAAO,EAAU,KAAK,MAAM,EAAQ,GAAI,EAAQ,KAAK,CAAG,MACxD,CAIJ,OAAO,EAA2C,CAChD,IAAM,EAAQ,EAAgB,EAAK,CAEnC,OADK,EACE,KAAK,UAAY,KAAK,MAAM,KAAK,OAAO,CAAE,EAAM,CAAC,CADrC,QAAQ,OAAW,MAAM,wBAAwB,CAAC,CASvE,KAAK,EAA8B,CACjC,OAAO,KAAK,MAAM,SAAY,CAC5B,IAAM,EAAS,GAAgB,MAAM,KAAK,QAAQ,KAAK,EAAG,CAAC,CAC3D,GAAI,CAAC,GAAU,EAAO,KAAO,IAAO,EAAO,MAAQ,WAAa,KAAK,MAAQ,KAAK,UAAW,MAAO,GACpG,KAAK,aAAa,CAClB,KAAK,WACL,GAAI,CACF,MAAM,KAAK,KAAK,MAAM,EAAO,OACtB,EAAK,CAEZ,MADA,KAAK,QAAQ,CACP,SACE,CACR,KAAK,WAQP,OANA,KAAK,aAAa,CAClB,KAAK,cAAgB,EAAU,EAAO,CAEtC,KAAK,aAAe,KAAK,KAAK,SAAS,CAAC,QACxC,KAAK,MAAQ,GACb,KAAK,QAAQ,CACN,IACP,CAGJ,OAAO,EAAY,EAA6B,CAC9C,IAAM,EAAQ,EAAgB,EAAK,CAEnC,OADK,EACE,KAAK,MAAM,SAAY,CAC5B,IAAM,EAAS,GAAgB,MAAM,KAAK,QAAQ,KAAK,EAAG,CAAC,CACvD,CAAC,GAAU,EAAO,KAAO,IAC7B,MAAM,KAAK,QAAQ,KAAK,CAAE,GAAG,EAAQ,KAAM,EAAO,CAAC,CAC/C,KAAK,eAAe,KAAO,IAAI,KAAK,cAAgB,CAAE,GAAG,KAAK,cAAe,KAAM,EAAO,EAC9F,KAAK,QAAQ,GACb,CAPiB,QAAQ,OAAW,MAAM,wBAAwB,CAAC,CAUvE,OAAO,EAA2B,CAChC,OAAO,KAAK,MAAM,SAAY,CAC5B,MAAM,KAAK,QAAQ,OAAO,EAAG,CACzB,KAAK,eAAe,KAAO,GAAI,KAAK,QAAQ,EAChD,CAIJ,SAAgB,CACV,UAAK,WAAa,KAAK,SAAW,GACtC,IAAI,CAAC,KAAK,cAAe,CAEnB,KAAK,KAAO,IAAG,KAAK,iBAAmB,IAC3C,OAEF,KAAK,aAAa,CAClB,KAAK,MAAQ,eAAiB,CAC5B,KAAK,MAAQ,KACR,KAAK,UAAY,KAAK,QAAQ,CAAC,EACnC,KAAK,WAAW,EAIrB,SAAgB,CACd,GAAI,KAAK,UAAW,OACpB,IAAM,EAAU,KAAK,QAAU,KAC/B,KAAK,aAAa,CAClB,IAAM,EAAU,KAAK,cACrB,GAAI,GAAW,GAAW,KAAK,SAAU,CAEvC,IAAI,EAA4B,KAChC,GAAI,CACF,EAAW,KAAK,KAAK,SAAS,OACvB,EAAK,CACZ,KAAK,QAAQ,UAAU,EAAI,CAE7B,GAAI,GAAY,EAAS,UAAY,KAAK,aAAc,CACtD,IAAM,EAAO,EACR,KAAK,UAAY,KAAK,MAAM,EAAQ,GAAI,EAAQ,KAAM,EAAK,CAAC,CAAC,MAAO,GAAiB,KAAK,QAAQ,UAAU,EAAI,CAAC,EAG1H,KAAK,UAAY,GAInB,MAAc,QAAwB,CACpC,IAAM,EAAU,KAAK,cACrB,GAAI,KAAK,WAAa,CAAC,EAAS,OAChC,IAAI,EACJ,GAAI,CACF,EAAW,KAAK,KAAK,SAAS,OACvB,EAAK,CACZ,KAAK,QAAQ,UAAU,EAAI,CAC3B,OAEF,IAAM,EAAU,EAAS,UAAY,KAAK,aAC1C,GAAI,GAAW,KAAK,SAAU,CAC5B,GAAI,CACF,MAAM,KAAK,MAAM,EAAQ,GAAI,EAAQ,KAAM,EAAS,OAC7C,EAAK,CACZ,KAAK,MAAQ,GACb,KAAK,QAAQ,CACb,KAAK,QAAQ,UAAU,EAAI,CAE7B,OAEE,IAAY,KAAK,QACnB,KAAK,MAAQ,EACb,KAAK,QAAQ,EAKjB,MAAc,MAAM,EAAY,EAAc,EAAqB,KAAK,KAAK,SAAS,CAA+B,CACnH,KAAK,aAAa,CAClB,IAAM,EAAsB,CAC1B,KACA,OACA,GAAI,EAAS,SAAW,IAAA,GAA0C,EAAE,CAAhC,CAAE,OAAQ,EAAS,OAAQ,CAC/D,GAAI,EAAS,YAAc,IAAA,GAAgD,EAAE,CAAtC,CAAE,UAAW,EAAS,UAAW,CACxE,UAAW,KAAK,KAAK,CACrB,KAAM,KAAK,KACX,QAAS,EAAS,QACnB,CACD,MAAM,KAAK,QAAQ,KAAK,EAAO,CAC/B,IAAM,EAAU,EAAU,EAAO,CAKjC,MAJA,MAAK,cAAgB,EACrB,KAAK,aAAe,EAAS,QAC7B,KAAK,MAAQ,GACb,KAAK,QAAQ,CACN,EAIT,QAAuB,CACrB,KAAK,aAAa,CAClB,KAAK,cAAgB,KACrB,KAAK,aAAe,KACpB,KAAK,MAAQ,GACb,KAAK,QAAQ,CAIf,MAAiB,EAAwC,CACvD,KAAK,OACL,IAAM,EAAM,KAAK,KAAK,KAAK,EAAK,CAAC,YAAc,CAC7C,KAAK,OACD,KAAK,OAAS,GAAK,KAAK,mBAC1B,KAAK,iBAAmB,GACxB,KAAK,SAAS,GAEhB,CAEF,MADA,MAAK,KAAO,EAAI,UAAY,IAAA,GAAU,CAC/B,EAGT,aAA4B,CAC1B,AAEE,KAAK,SADL,aAAa,KAAK,MAAM,CACX,MAIjB,QAAuB,CAChB,KAAK,WAAW,KAAK,QAAQ,YAAY"}
|