@tradecanvas/commons 1.25.0 → 1.27.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/dist/icons/iconSet.d.ts +28 -0
- package/dist/icons/iconSet.d.ts.map +1 -0
- package/dist/icons/index.d.ts +2 -0
- package/dist/icons/index.d.ts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +790 -237
- package/dist/index.js.map +1 -1
- package/dist/types/chart.d.ts +15 -4
- package/dist/types/chart.d.ts.map +1 -1
- package/dist/utils/data.d.ts +12 -1
- package/dist/utils/data.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/types/rendering.ts","../src/types/style.ts","../src/types/drawing.ts","../src/types/trading.ts","../src/types/signal.ts","../src/types/realtime.ts","../src/types/quote.ts","../src/types/news.ts","../src/utils/math.ts","../src/utils/data.ts","../src/utils/color.ts","../src/utils/timezone.ts","../src/utils/time.ts","../src/utils/precision.ts","../src/utils/priceSource.ts","../src/utils/resample.ts","../src/utils/symbols.ts","../src/utils/marketStatus.ts","../src/utils/drawingOptions.ts","../src/utils/drawingStyle.ts","../src/utils/chartTypeOptions.ts","../src/constants/themes.ts","../src/utils/chartStyle.ts","../src/constants/defaults.ts","../src/i18n/en.ts","../src/i18n/vi.ts","../src/i18n/index.ts","../src/market/presets.ts"],"sourcesContent":["export interface Point {\n x: number;\n y: number;\n}\n\nexport interface Size {\n width: number;\n height: number;\n}\n\nexport interface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport type PriceScaleMode = 'regular' | 'logarithmic' | 'percentage' | 'indexedTo100';\n\nexport interface ViewportState {\n visibleRange: { from: number; to: number };\n priceRange: { min: number; max: number };\n barWidth: number;\n barSpacing: number;\n offset: number;\n chartRect: Rect;\n /**\n * Logarithmic price geometry. Kept for back-compat; mirrors\n * `scaleMode === 'logarithmic'`. Prefer reading `scaleMode`.\n */\n logScale?: boolean;\n /**\n * Price-scale presentation. `regular`, `percentage`, and `indexedTo100` share\n * the same linear geometry — only the axis labels differ (rebased to\n * `scaleBaseline`). `logarithmic` changes the geometry. Defaults to\n * `regular` when unset.\n */\n scaleMode?: PriceScaleMode;\n /**\n * Reference price for `percentage` / `indexedTo100` axis labels — usually the\n * close of the first visible bar. Set by the chart each frame.\n */\n scaleBaseline?: number;\n /** Upside down: higher prices lower on screen. */\n invertScale?: boolean;\n /**\n * How prices on this scale read, when the chart has a price format of its\n * own (`ChartOptions.priceFormat`). Unset: decimals. Indicator panes leave\n * it unset.\n */\n formatPrice?: (price: number) => string;\n /**\n * The step prices move in under that format (a 32nd): the axis puts its\n * ticks on whole numbers of it.\n */\n priceUnit?: number;\n /**\n * Optional reference to the current bar series. When set, drawings/indicators\n * treat `anchor.time` as a real timestamp and convert to bar index via\n * `timestampToBarIndex(time, data)` at render/hit-test time. This lets\n * anchors survive timeframe / symbol switches. When unset,\n * `anchor.time` is treated as a raw bar index (legacy behavior).\n */\n data?: ReadonlyArray<{ time: number }>;\n /**\n * Width of the price axis strip right of `chartRect`, in CSS pixels. The\n * chart widens it to fit long labels (sub-cent prices); renderers drawing\n * badges on the axis size them to it. Defaults to `PRICE_AXIS_WIDTH`.\n */\n priceAxisWidth?: number;\n}\n\n/**\n * What changed, for `requestRender()`. The chart paints two canvases: a scene\n * (everything that follows data, viewport and chart objects) and a thin top\n * canvas for pointer-tied visuals. Any type except `Hover` redraws both;\n * `Hover` redraws only the top canvas (crosshair, its axis pills, legend,\n * bar countdown, measure ruler, selection box).\n */\nexport enum LayerType {\n Background = 0,\n Main = 1,\n Panel = 2,\n Overlay = 3,\n UI = 4,\n Hover = 5,\n}\n","import type { LineStyle } from './chart.js';\n\n/** The kind of value a style key takes. */\nexport type StyleValueKind = 'color' | 'lineStyle' | 'width' | 'boolean';\n\n/**\n * Every key `chart.applyOverrides()` takes, and the kind of its value.\n *\n * - `series.<chartType>.*` style the main series while it is drawn as that\n * type. Up and down colours fall back to `series.candlestick.*`, line\n * colours and widths to `series.line.*`, area fills to `series.area.*`,\n * then to the theme.\n * - The rest style one part of the chart each, falling back to the theme's\n * tokens: with no overrides the chart looks as its theme makes it.\n */\nexport const CHART_STYLE_KEYS = {\n 'background.color': 'color',\n\n 'panes.background': 'color',\n 'panes.separatorColor': 'color',\n 'panes.titleColor': 'color',\n\n 'grid.horizontal.visible': 'boolean',\n 'grid.horizontal.color': 'color',\n 'grid.horizontal.style': 'lineStyle',\n 'grid.horizontal.width': 'width',\n 'grid.vertical.visible': 'boolean',\n 'grid.vertical.color': 'color',\n 'grid.vertical.style': 'lineStyle',\n 'grid.vertical.width': 'width',\n\n 'crosshair.horizontal.visible': 'boolean',\n 'crosshair.horizontal.color': 'color',\n 'crosshair.horizontal.style': 'lineStyle',\n 'crosshair.horizontal.width': 'width',\n 'crosshair.vertical.visible': 'boolean',\n 'crosshair.vertical.color': 'color',\n 'crosshair.vertical.style': 'lineStyle',\n 'crosshair.vertical.width': 'width',\n 'crosshair.labelBackground': 'color',\n 'crosshair.labelTextColor': 'color',\n\n 'axis.price.lineColor': 'color',\n 'axis.price.textColor': 'color',\n 'axis.time.lineColor': 'color',\n 'axis.time.textColor': 'color',\n\n 'legend.textColor': 'color',\n 'legend.labelColor': 'color',\n\n 'watermark.color': 'color',\n\n 'lastPrice.visible': 'boolean',\n 'lastPrice.upColor': 'color',\n 'lastPrice.downColor': 'color',\n 'lastPrice.style': 'lineStyle',\n 'lastPrice.width': 'width',\n\n 'volume.upColor': 'color',\n 'volume.downColor': 'color',\n\n 'sessionBreaks.color': 'color',\n 'sessionBreaks.style': 'lineStyle',\n 'sessionBreaks.width': 'width',\n\n 'highLow.color': 'color',\n\n 'trading.buyColor': 'color',\n 'trading.sellColor': 'color',\n 'trading.profitColor': 'color',\n 'trading.lossColor': 'color',\n 'trading.entryColor': 'color',\n\n 'markers.longColor': 'color',\n 'markers.shortColor': 'color',\n 'markers.neutralColor': 'color',\n\n 'tradeZones.profitColor': 'color',\n 'tradeZones.lossColor': 'color',\n 'tradeZones.activeColor': 'color',\n\n 'drawings.handleColor': 'color',\n\n 'series.candlestick.upColor': 'color',\n 'series.candlestick.downColor': 'color',\n 'series.candlestick.wickUpColor': 'color',\n 'series.candlestick.wickDownColor': 'color',\n 'series.heikinAshi.upColor': 'color',\n 'series.heikinAshi.downColor': 'color',\n 'series.heikinAshi.wickUpColor': 'color',\n 'series.heikinAshi.wickDownColor': 'color',\n 'series.hollowCandle.upColor': 'color',\n 'series.hollowCandle.downColor': 'color',\n 'series.volumeCandles.upColor': 'color',\n 'series.volumeCandles.downColor': 'color',\n 'series.volumeCandles.wickUpColor': 'color',\n 'series.volumeCandles.wickDownColor': 'color',\n 'series.equivolume.upColor': 'color',\n 'series.equivolume.downColor': 'color',\n 'series.equivolume.wickUpColor': 'color',\n 'series.equivolume.wickDownColor': 'color',\n 'series.bar.upColor': 'color',\n 'series.bar.downColor': 'color',\n 'series.hiLo.upColor': 'color',\n 'series.hiLo.downColor': 'color',\n 'series.renko.upColor': 'color',\n 'series.renko.downColor': 'color',\n 'series.lineBreak.upColor': 'color',\n 'series.lineBreak.downColor': 'color',\n 'series.rangeBars.upColor': 'color',\n 'series.rangeBars.downColor': 'color',\n 'series.kagi.upColor': 'color',\n 'series.kagi.downColor': 'color',\n 'series.pointAndFigure.upColor': 'color',\n 'series.pointAndFigure.downColor': 'color',\n 'series.baseline.upColor': 'color',\n 'series.baseline.downColor': 'color',\n 'series.baseline.lineWidth': 'width',\n 'series.line.color': 'color',\n 'series.line.lineWidth': 'width',\n 'series.stepLine.color': 'color',\n 'series.stepLine.lineWidth': 'width',\n 'series.lineWithMarkers.color': 'color',\n 'series.lineWithMarkers.lineWidth': 'width',\n 'series.area.lineColor': 'color',\n 'series.area.lineWidth': 'width',\n 'series.area.topColor': 'color',\n 'series.area.bottomColor': 'color',\n 'series.hlcArea.lineColor': 'color',\n 'series.hlcArea.lineWidth': 'width',\n 'series.hlcArea.topColor': 'color',\n 'series.hlcArea.bottomColor': 'color',\n} as const satisfies Record<string, StyleValueKind>;\n\nexport type ChartStyleKey = keyof typeof CHART_STYLE_KEYS;\n\n/** The value a key of `kind` takes. */\nexport type StyleValue<K extends StyleValueKind> = K extends 'color'\n ? string\n : K extends 'lineStyle'\n ? LineStyle\n : K extends 'width'\n ? number\n : boolean;\n\n/** Style overrides, by key. */\nexport type ChartStyleOverrides = {\n [K in ChartStyleKey]?: StyleValue<(typeof CHART_STYLE_KEYS)[K]>;\n};\n\n/** A change to overrides: a value sets a key, `null` takes it away. */\nexport type ChartStyleOverridesPatch = {\n [K in ChartStyleKey]?: StyleValue<(typeof CHART_STYLE_KEYS)[K]> | null;\n};\n\n/**\n * Where an override goes. `'host'`: your app's, kept through theme changes\n * and never saved. `'user'`: what the person using the chart chose (the\n * widget's Settings write here), kept per theme and saved with `saveState`.\n */\nexport type StyleLayer = 'host' | 'user';\n\n/** One indicator pane's own look, over the panes' (`chart.setPaneStyle`). */\nexport interface PaneStyle {\n background?: string;\n separator?: string;\n}\n\n/** A line's look, as resolved. */\nexport interface ResolvedLineLook {\n visible: boolean;\n color: string;\n style: LineStyle;\n width: number;\n}\n\n/**\n * The chart's finer looks, worked out from the theme and its overrides once\n * per change. Renderers read it from `theme.style`; a theme without one (a\n * plugin's own) draws as before.\n */\nexport interface ResolvedChartStyle {\n panes: { background: string; separator: string; title: string };\n grid: { horizontal: ResolvedLineLook; vertical: ResolvedLineLook };\n crosshair: {\n horizontal: ResolvedLineLook;\n vertical: ResolvedLineLook;\n labelBackground: string;\n labelText: string;\n };\n axis: { price: { line: string; text: string }; time: { line: string; text: string } };\n legend: { text: string; label: string };\n /** Null: the watermark's own colour, or the theme's. */\n watermark: { color: string | null };\n lastPrice: { visible: boolean; up: string; down: string; style: LineStyle; width: number };\n /** Null: the session breaks' own settings, or the theme's. */\n sessionBreaks: { color: string | null; style: LineStyle | null; width: number | null };\n /** Null: the high and low lines' own colour. */\n highLow: { color: string | null };\n /** Orders (buy, sell) and positions (profit, loss, entry). Null: the trading config's colours. */\n trading: { buy: string | null; sell: string | null; profit: string | null; loss: string | null; entry: string | null };\n /** Signal markers. Null: their own style's colours. */\n markers: { long: string | null; short: string | null; neutral: string | null };\n /** Trade zones. Null: their own style's colours. */\n tradeZones: { profit: string | null; loss: string | null; active: string | null };\n /** A selected drawing's handles. Null: white. */\n drawings: { handle: string | null };\n /** The main series' line width, for the chart types drawn as a line. */\n series: { lineWidth: number };\n}\n","import type { Point, ViewportState } from './rendering.js';\nimport type { OHLCBar } from './ohlc.js';\n\nexport type DrawingToolType =\n | 'trendLine' | 'horizontalLine' | 'horizontalRay' | 'verticalLine' | 'ray' | 'extendedLine'\n | 'crossLine' | 'trendAngle' | 'infoLine'\n | 'parallelChannel' | 'regressionChannel'\n | 'fibRetracement' | 'fibExtension' | 'fibTimeZones' | 'fibChannel' | 'fibSpeedResistanceFan'\n | 'rectangle' | 'ellipse' | 'triangle' | 'circle'\n | 'pitchfork' | 'schiffPitchfork' | 'modifiedSchiffPitchfork' | 'elliottWave'\n | 'xabcdPattern' | 'abcdPattern' | 'headAndShoulders'\n | 'priceRange' | 'dateRange' | 'dateAndPriceRange' | 'measure'\n | 'text' | 'arrow' | 'priceLabel'\n | 'gannFan' | 'gannBox' | 'cyclicLines'\n | 'anchoredVWAP'\n | 'volumeProfileRange'\n | 'riskReward'\n | 'note' | 'callout' | 'flag' | 'arrowMark' | 'icon'\n | 'brush' | 'highlighter' | 'path' | 'polyline' | 'curve' | 'arc'\n | 'fibCircles' | 'fibSpiral' | 'fibArcs' | 'fibWedge' | 'pitchfan' | 'gannSquare'\n | 'elliottImpulse' | 'elliottCorrection' | 'elliottTriangle' | 'elliottDoubleCombo' | 'elliottTripleCombo'\n | 'threeDrives' | 'cypherPattern'\n | 'timeCycles' | 'sineLine'\n | 'forecast' | 'projection' | 'barsPattern';\n\nexport interface AnchorPoint {\n time: number;\n price: number;\n}\n\nexport interface DrawingStyle {\n color: string;\n lineWidth: number;\n lineStyle: 'solid' | 'dashed' | 'dotted';\n fillColor?: string;\n fillOpacity?: number;\n fontSize?: number;\n text?: string;\n}\n\nexport interface DrawingState {\n id: string;\n type: DrawingToolType;\n anchors: AnchorPoint[];\n style: DrawingStyle;\n visible: boolean;\n locked: boolean;\n /** The tool's own settings (levels, extend left/right…); see `DrawingDescriptor.options`. */\n options?: DrawingOptions;\n /** The group it belongs to: drawings selected, hidden and locked together. */\n group?: { id: string; name: string };\n meta?: Record<string, unknown>;\n}\n\n/** One level of a Fibonacci or Gann tool: a ratio, shown or not, in its own colour. */\nexport interface DrawingLevel {\n value: number;\n visible: boolean;\n /** CSS colour; the drawing's colour when left out. */\n color?: string;\n}\n\nexport type DrawingOptionValue = boolean | number | string | DrawingLevel[];\n\n/** A drawing's settings beyond the shared style, by key. Missing keys take the tool's defaults. */\nexport type DrawingOptions = Record<string, DrawingOptionValue>;\n\n/** A setting a drawing tool offers beyond the shared style, with its default. */\nexport type DrawingOptionDef =\n | { kind: 'boolean'; label: string; default: boolean }\n | { kind: 'number'; label: string; default: number; min?: number; max?: number; step?: number }\n | { kind: 'choice'; label: string; default: string; choices: readonly { value: string; label: string }[] }\n | { kind: 'levels'; label: string; default: readonly DrawingLevel[] }\n | { kind: 'text'; label: string; default: string };\n\nexport type DrawingOptionDefs = Readonly<Record<string, DrawingOptionDef>>;\n\nexport interface DrawingDescriptor {\n type: DrawingToolType;\n name: string;\n requiredAnchors: number;\n singleClick?: boolean;\n /** Settings the tool offers beyond the shared style; a settings dialog is built from them. */\n options?: DrawingOptionDefs;\n /** It fills an area with `style.fillColor` (a settings dialog offers the fill). */\n fill?: boolean;\n /** It draws `style.text` (a settings dialog offers the text and its size). */\n text?: boolean;\n /**\n * How it is drawn with the pointer. 'clicks' (the default): a click per\n * anchor up to `requiredAnchors`. 'freehand': press and drag, a point every\n * few pixels, release to finish. 'path': a click per point, at least\n * `requiredAnchors` and at most `maxAnchors`; a double-click, Enter or a\n * click on the last point finishes it.\n */\n creation?: 'clicks' | 'freehand' | 'path';\n /** Most points a 'path' takes (100 when left out). */\n maxAnchors?: number;\n}\n\nexport interface DrawingPlugin {\n descriptor: DrawingDescriptor;\n render(\n ctx: CanvasRenderingContext2D,\n state: DrawingState,\n viewport: ViewportState,\n selected: boolean,\n ): void;\n hitTest(\n point: Point,\n state: DrawingState,\n viewport: ViewportState,\n tolerance: number,\n ): boolean;\n hitTestAnchor(\n point: Point,\n state: DrawingState,\n viewport: ViewportState,\n tolerance: number,\n ): number;\n /**\n * The drawing after handle `index` (from `hitTestAnchor`) is dragged to\n * `anchor`. Without it, the handle is anchor `index` and moves there. A\n * tool with handles that are not anchors (a position's target, say)\n * changes its options instead.\n */\n moveHandle?(state: DrawingState, index: number, anchor: AnchorPoint): DrawingState;\n /**\n * The prices of the drawing's lines at `time` (what an alert on it\n * crosses), or null where it doesn't reach. Tools without it take no alerts.\n * With `viewport` the lines follow the chart as drawn: straight across bars\n * (not clock time, so a weekend gap doesn't bend them) and on a log scale\n * straight in log price.\n */\n priceAt?(state: DrawingState, time: number, viewport?: ViewportState): number[] | null;\n /**\n * For a tool drawn from the chart's bars (an anchored VWAP, a volume\n * profile): the chart hands it a getter for them when it is registered.\n */\n setDataGetter?(getter: () => readonly OHLCBar[]): void;\n}\n\nexport const DEFAULT_DRAWING_STYLE: DrawingStyle = {\n color: '#4c8dff',\n lineWidth: 1,\n lineStyle: 'solid',\n fillColor: 'rgba(76, 141, 255, 0.1)',\n fillOpacity: 0.1,\n fontSize: 12,\n};\n","export type OrderSide = 'buy' | 'sell';\nexport type OrderType = 'market' | 'limit' | 'stop' | 'stopLimit';\nexport type OrderStatus = 'pending' | 'filled' | 'cancelled' | 'rejected';\nexport type OrderLabel = 'LIMIT' | 'STOP' | 'SL' | 'TP' | 'STOP LIMIT';\n\n/** How long an order works: good till cancelled, or for the day. */\nexport type TimeInForce = 'gtc' | 'day';\n\nexport interface TradingOrder {\n id: string;\n side: OrderSide;\n type: OrderType;\n price: number;\n stopPrice?: number;\n quantity: number;\n label?: OrderLabel;\n draggable?: boolean;\n /** Stop-loss and take-profit the position gets when the order fills. */\n stopLoss?: number;\n takeProfit?: number;\n timeInForce?: TimeInForce;\n meta?: Record<string, unknown>;\n}\n\nexport interface TradingPosition {\n id: string;\n side: OrderSide;\n entryPrice: number;\n quantity: number;\n /** Quantity already closed (for partial-close visualization). 0 ≤ closedQuantity ≤ quantity. */\n closedQuantity?: number;\n stopLoss?: number;\n takeProfit?: number;\n meta?: Record<string, unknown>;\n}\n\n/** Threshold-based P&L color stop. Sorted ascending by `pnl` is recommended. */\nexport interface PnLThreshold {\n /** Inclusive lower bound. Use -Infinity for the bottom-most stop. */\n pnl: number;\n color: string;\n}\n\n/** Tokens passed to position label templates. */\nexport interface PositionLabelContext {\n side: OrderSide;\n quantity: number;\n closedQuantity: number;\n openQuantity: number;\n entryPrice: number;\n currentPrice: number;\n pnl: number;\n pnlPct: number;\n precision: number;\n /** The chart's price format, for `{entry}` and `{price}` (decimals at `precision` without one). */\n formatPrice?: (price: number) => string;\n}\n\nexport interface DepthLevel {\n price: number;\n volume: number;\n}\n\nexport interface DepthData {\n bids: DepthLevel[];\n asks: DepthLevel[];\n}\n\nexport interface TradingConfig {\n enabled: boolean;\n orderColors?: { buy?: string; sell?: string };\n positionColors?: { profit?: string; loss?: string; entry?: string };\n /**\n * Optional gradient of colors keyed to P&L value. When provided, the rendered\n * position zone uses the color of the highest threshold whose `pnl` ≤ live P&L.\n * Falls back to `positionColors.profit`/`.loss` when unset.\n */\n pnlThresholds?: PnLThreshold[];\n /**\n * Position P&L label template. Supports tokens: {side} {qty} {closedQty}\n * {openQty} {entry} {price} {pnl} {pnlPct} {pnlSign}. Pass a function for\n * full control. Default: `{side} {qty} | P&L: {pnlSign}{pnl}`.\n */\n positionLabel?: string | ((ctx: PositionLabelContext) => string);\n depthOverlay?: {\n enabled?: boolean;\n bidColor?: string;\n askColor?: string;\n maxWidth?: number;\n };\n contextMenu?: { enabled?: boolean };\n /**\n * Buttons on the lines: × on an order cancels it, × on a position closes it\n * and ⇅ reverses it, × on a stop-loss or take-profit removes it. All on by\n * default.\n */\n lineButtons?: { cancel?: boolean; close?: boolean; reverse?: boolean; removeStops?: boolean };\n /** Marks where orders filled (from the execution adapter's fills). On by default. */\n fillMarks?: boolean;\n pricePrecision?: number;\n dragThreshold?: number;\n}\n\nexport interface OrderPlaceIntent {\n side: OrderSide;\n type: OrderType;\n price: number;\n stopPrice?: number;\n quantity?: number;\n /** Stop-loss and take-profit for the position the order opens. */\n stopLoss?: number;\n takeProfit?: number;\n timeInForce?: TimeInForce;\n}\n\nexport interface OrderModifyIntent {\n orderId: string;\n newPrice: number;\n /** The price before the change, when known (e.g. from a drag). Optional so\n * the chart's `orderModify` event payload routes straight to an adapter. */\n previousPrice?: number;\n}\n\nexport interface OrderCancelIntent {\n orderId: string;\n}\n\nexport interface PositionModifyIntent {\n positionId: string;\n /** A new stop-loss; null removes it, left out keeps it. */\n stopLoss?: number | null;\n /** A new take-profit; null removes it, left out keeps it. */\n takeProfit?: number | null;\n}\n\nexport interface PositionCloseIntent {\n positionId: string;\n}\n\n/** Close a position and open the same size the other way. */\nexport interface PositionReverseIntent {\n positionId: string;\n}\n\nexport const DEFAULT_TRADING_CONFIG: TradingConfig = {\n enabled: true,\n orderColors: { buy: '#1fa874', sell: '#e8505b' },\n positionColors: { profit: '#1fa874', loss: '#e8505b', entry: '#4c8dff' },\n depthOverlay: { enabled: false, bidColor: 'rgba(31, 168, 116,0.15)', askColor: 'rgba(232, 80, 91,0.15)', maxWidth: 100 },\n contextMenu: { enabled: true },\n lineButtons: { cancel: true, close: true, reverse: true, removeStops: true },\n fillMarks: true,\n pricePrecision: 2,\n dragThreshold: 3,\n};\n","export type SignalDirection = 'long' | 'short' | 'neutral';\n\nexport interface SignalMarker {\n id: string;\n time: number;\n price: number;\n direction: SignalDirection;\n confidence: number;\n source: string;\n label?: string;\n color?: string;\n meta?: Record<string, unknown>;\n}\n\nexport interface SignalMarkerStyle {\n longColor?: string;\n shortColor?: string;\n neutralColor?: string;\n arrowSize?: number;\n showLabel?: boolean;\n showConfidence?: boolean;\n sourceColors?: Record<string, string>;\n}\n\nexport const DEFAULT_SIGNAL_STYLE: SignalMarkerStyle = {\n longColor: '#1fa874',\n shortColor: '#e8505b',\n neutralColor: '#9E9E9E',\n arrowSize: 12,\n showLabel: true,\n showConfidence: true,\n};\n\nexport type TradeZoneDirection = 'long' | 'short';\n\nexport interface TradeZone {\n id: string;\n entryTime: number;\n entryPrice: number;\n exitTime?: number;\n exitPrice?: number;\n direction: TradeZoneDirection;\n pnl?: number;\n pnlPercent?: number;\n label?: string;\n meta?: Record<string, unknown>;\n}\n\nexport interface TradeZoneStyle {\n profitColor?: string;\n lossColor?: string;\n activeColor?: string;\n fillOpacity?: number;\n borderWidth?: number;\n showLabel?: boolean;\n showPnl?: boolean;\n}\n\nexport const DEFAULT_TRADE_ZONE_STYLE: TradeZoneStyle = {\n profitColor: '#1fa874',\n lossColor: '#e8505b',\n activeColor: '#4c8dff',\n fillOpacity: 0.12,\n borderWidth: 1,\n showLabel: true,\n showPnl: true,\n};\n","import type { OHLCBar, TimeFrame } from './ohlc.js';\nimport type { SymbolInfo, SymbolSearchOptions } from './symbol.js';\nimport type { Quote } from './quote.js';\nimport type { NewsItem } from './news.js';\n\n/** One trade: when (ms), at what price, how much. */\nexport interface Trade {\n time: number;\n price: number;\n volume: number;\n}\n\n// --- Connection ---\n\nexport type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';\n\nexport interface ConnectionInfo {\n state: ConnectionState;\n latency?: number;\n reconnectAttempt?: number;\n lastMessageTime?: number;\n error?: string;\n}\n\n// --- Ticks & Trades ---\n\nexport interface RawTick {\n time: number;\n price: number;\n volume: number;\n side?: 'buy' | 'sell';\n /** The best bid and ask with it, when the feed has them (the chart marks them). */\n bid?: number;\n ask?: number;\n}\n\nexport interface AggregatedBar extends OHLCBar {\n closed: boolean; // true when bar is finalized\n tickCount: number; // number of ticks in this bar\n}\n\n// --- Data Adapter (Strategy Pattern) ---\n\nexport interface DataAdapterConfig {\n symbol: string;\n timeframe: TimeFrame;\n reconnect?: boolean; // default: true\n reconnectMaxRetries?: number; // default: Infinity\n reconnectBaseDelay?: number; // ms, default: 1000\n reconnectMaxDelay?: number; // ms, default: 30000\n heartbeatInterval?: number; // ms, default: 30000\n bufferSize?: number; // max ticks to buffer, default: 1000\n}\n\nexport type DataAdapterEventType =\n | 'tick'\n | 'bar'\n | 'barClose'\n | 'snapshot' // initial historical data loaded\n | 'connectionChange'\n | 'error'\n /**\n * The feed learned about the connected symbol after the chart asked\n * (`resolveSymbol`), for instance on its first message: `data` is the\n * `SymbolInfo`. The chart applies it (precision, price step, hours).\n */\n | 'symbolInfo';\n\nexport interface DataAdapterEvent<T = unknown> {\n type: DataAdapterEventType;\n data: T;\n timestamp: number;\n}\n\nexport type DataAdapterListener<T = unknown> = (event: DataAdapterEvent<T>) => void;\n\n/**\n * Data adapter interface. Implements the observer pattern:\n * - connect() to start receiving data\n * - on('bar'|'tick'|'connectionChange', handler) to receive events\n * - disconnect() to stop, then connect() again to switch symbols/timeframes\n * - No separate subscribe/unsubscribe — reconnect is the intended pattern\n *\n * Strategy pattern for pluggable data sources.\n * Implementations handle the specifics of each data source (WebSocket, REST,\n * SSE, etc.) while the StreamManager orchestrates lifecycle and aggregation.\n *\n * Built-in: BinanceAdapter\n * Implement this for: custom exchange APIs, broker feeds, mock data\n */\nexport interface DataAdapter {\n readonly name: string;\n\n /**\n * Optional: the timeframes the feed serves. Charts build any other\n * timeframe (7m, 90m, 2d…) from the coarsest of these that divides it.\n * Leave it out when the feed serves every timeframe.\n */\n readonly supportedTimeframes?: readonly TimeFrame[];\n\n connect(config: DataAdapterConfig): void;\n disconnect(): void;\n getConnectionState(): ConnectionState;\n\n /**\n * Load historical bars. Called once on connect, before streaming starts.\n * Returns bars sorted by time ascending.\n */\n fetchHistory(symbol: string, timeframe: TimeFrame, limit?: number): Promise<OHLCBar[]>;\n\n /**\n * Optional: load up to `limit` bars older than `before` (the oldest loaded\n * bar's time, in the bars' own unit), sorted by time ascending. Return an\n * empty array when no older data exists. A chart connected to an adapter\n * with this method loads older bars as the user scrolls back in time.\n */\n fetchHistoryBefore?(symbol: string, timeframe: TimeFrame, before: number, limit: number): Promise<OHLCBar[]>;\n\n /** Optional: symbols matching `query`, best first — for a symbol search box. */\n searchSymbols?(query: string, options?: SymbolSearchOptions): Promise<SymbolInfo[]>;\n\n /**\n * Optional: what the feed knows about `symbol` (name, price step, exchange\n * timezone and hours), or null when it doesn't know it. A connected chart\n * asks for it and applies it.\n */\n resolveSymbol?(symbol: string): Promise<SymbolInfo | null>;\n\n /**\n * Optional: live quotes (last price, the day's change, high, low, volume)\n * for many symbols at once, until the function it returns is called. A\n * widget's watchlist fills its rows from it.\n */\n subscribeQuotes?(symbols: readonly string[], onQuotes: (quotes: Quote[]) => void): () => void;\n\n /** Optional: recent headlines about `symbol`, newest first. */\n fetchNews?(symbol: string, limit?: number): Promise<NewsItem[]>;\n\n /**\n * Optional, with `subscribeTrades`: up to `limit` recent trades, oldest\n * first. A chart builds tick timeframes (`'100T'`) from a feed's trades.\n */\n fetchTrades?(symbol: string, limit?: number): Promise<Trade[]>;\n\n /** Optional, with `fetchTrades`: live trades of `symbol` until the function it returns is called. */\n subscribeTrades?(symbol: string, onTrades: (trades: Trade[]) => void): () => void;\n\n on<T = unknown>(event: DataAdapterEventType, listener: DataAdapterListener<T>): void;\n off<T = unknown>(event: DataAdapterEventType, listener: DataAdapterListener<T>): void;\n\n dispose(): void;\n}\n\n// --- Stream Manager Config ---\n\nexport interface StreamConfig {\n adapter: DataAdapter;\n symbol: string;\n timeframe: TimeFrame;\n historyLimit?: number; // bars to load initially, default: 500\n /** Bars per request when scrolling back for older bars (needs `adapter.fetchHistoryBefore`), default: 500. */\n historyPageSize?: number;\n autoScroll?: boolean; // scroll to end on new bar, default: true\n showCurrentPriceLine?: boolean; // default: true\n aggregateTicks?: boolean; // build bars from ticks, default: false\n reconnect?: ReconnectConfig;\n}\n\nexport interface ReconnectConfig {\n enabled: boolean; // default: true\n maxRetries: number; // default: Infinity\n baseDelay: number; // ms, default: 1000\n maxDelay: number; // ms, default: 30000\n backoffMultiplier: number; // default: 2\n}\n\nexport const DEFAULT_RECONNECT: ReconnectConfig = {\n enabled: true,\n maxRetries: Infinity,\n baseDelay: 1000,\n maxDelay: 30000,\n backoffMultiplier: 2,\n};\n\nexport const DEFAULT_STREAM_CONFIG: Partial<StreamConfig> = {\n historyLimit: 500,\n autoScroll: true,\n showCurrentPriceLine: true,\n aggregateTicks: false,\n};\n","/**\n * A symbol's latest quote: its price and the day's move, for a watchlist row\n * or a symbol's details. Prices are in the symbol's quote currency.\n */\nexport interface Quote {\n symbol: string;\n /** The last traded price. */\n last: number;\n /** The move from the reference price (the prior close; 24 h ago for crypto). */\n change?: number;\n /** The same move, in percent. */\n changePercent?: number;\n open?: number;\n high?: number;\n low?: number;\n /** The prior session's close. */\n prevClose?: number;\n /** Volume over the same period, in the base unit. */\n volume?: number;\n bid?: number;\n ask?: number;\n /** When the quote was made, in ms since the epoch. */\n time?: number;\n}\n\n/** Live quotes for many symbols at once. */\nexport interface QuoteSource {\n /**\n * Send quotes for `symbols` to `onQuotes` as they change (any number per\n * call) until the function it returns is called.\n */\n subscribeQuotes(symbols: readonly string[], onQuotes: (quotes: Quote[]) => void): () => void;\n}\n\nconst QUOTE_NUMBERS = ['change', 'changePercent', 'open', 'high', 'low', 'prevClose', 'volume', 'bid', 'ask', 'time'] as const;\n\n/**\n * A quote from untrusted input (a host's push, a feed's frame): its symbol\n * and last price must be usable; any other field that isn't a finite number\n * is left out. The change is worked out from the prior close or the open when\n * the input has none. Null when it isn't a quote.\n */\nexport function readQuote(raw: unknown): Quote | null {\n if (!raw || typeof raw !== 'object') return null;\n const r = raw as Record<string, unknown>;\n const symbol = typeof r.symbol === 'string' ? r.symbol.trim() : '';\n if (!symbol || symbol.length > 64 || !isFiniteNumber(r.last)) return null;\n const quote: Quote = { symbol, last: r.last };\n for (const key of QUOTE_NUMBERS) {\n const v = r[key];\n if (isFiniteNumber(v)) quote[key] = v;\n }\n const ref = quote.prevClose ?? quote.open;\n if (quote.change === undefined && ref !== undefined) quote.change = quote.last - ref;\n if (quote.changePercent === undefined && quote.change !== undefined) {\n const base = quote.last - quote.change;\n if (base !== 0) quote.changePercent = (quote.change / base) * 100;\n }\n return quote;\n}\n\nfunction isFiniteNumber(v: unknown): v is number {\n return typeof v === 'number' && Number.isFinite(v);\n}\n","/** A news headline about a symbol. */\nexport interface NewsItem {\n title: string;\n /** When it was published, in ms since the epoch. */\n time: number;\n /** The article; only http(s) links are kept. */\n url?: string;\n source?: string;\n summary?: string;\n id?: string;\n}\n\nconst MAX_TITLE = 300;\nconst MAX_TEXT = 1000;\n\nconst text = (v: unknown, max: number): string | undefined => {\n if (typeof v !== 'string') return undefined;\n const t = v.trim();\n return t ? t.slice(0, max) : undefined;\n};\n\nconst webUrl = (v: unknown): string | undefined => {\n if (typeof v !== 'string') return undefined;\n try {\n const url = new URL(v);\n return url.protocol === 'https:' || url.protocol === 'http:' ? url.href : undefined;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Headlines from untrusted input (a feed, a host): each needs a title and a\n * time; links that aren't web pages are dropped. Newest first, at most\n * `limit`.\n */\nexport function readNews(raw: unknown, limit = 20): NewsItem[] {\n if (!Array.isArray(raw)) return [];\n const items: NewsItem[] = [];\n for (const entry of raw) {\n if (!entry || typeof entry !== 'object') continue;\n const r = entry as Record<string, unknown>;\n const title = text(r.title, MAX_TITLE);\n if (!title || typeof r.time !== 'number' || !Number.isFinite(r.time)) continue;\n const item: NewsItem = { title, time: r.time };\n const url = webUrl(r.url);\n if (url) item.url = url;\n const source = text(r.source, MAX_TITLE);\n if (source) item.source = source;\n const summary = text(r.summary, MAX_TEXT);\n if (summary) item.summary = summary;\n const id = text(r.id, MAX_TITLE);\n if (id) item.id = id;\n items.push(item);\n }\n return items.sort((a, b) => b.time - a.time).slice(0, Math.max(0, limit));\n}\n","export function clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n}\n\nexport function lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\nexport function inverseLerp(a: number, b: number, value: number): number {\n if (a === b) return 0;\n return (value - a) / (b - a);\n}\n\nexport function roundToStep(value: number, step: number): number {\n return Math.round(value / step) * step;\n}\n\nexport function niceNumber(value: number, round: boolean): number {\n const exp = Math.floor(Math.log10(value));\n const frac = value / Math.pow(10, exp);\n let nice: number;\n if (round) {\n if (frac < 1.5) nice = 1;\n else if (frac < 3) nice = 2;\n else if (frac < 7) nice = 5;\n else nice = 10;\n } else {\n if (frac <= 1) nice = 1;\n else if (frac <= 2) nice = 2;\n else if (frac <= 5) nice = 5;\n else nice = 10;\n }\n return nice * Math.pow(10, exp);\n}\n\nexport function computeTickStep(min: number, max: number, maxTicks: number): number {\n const range = niceNumber(max - min, false);\n return niceNumber(range / (maxTicks - 1), true);\n}\n","import type { OHLCBar, DataSeries } from '../types/ohlc.js';\n\n/**\n * Bar times at or above this are milliseconds (from March 1973 on), below it\n * seconds (up to the year 5138).\n */\nexport const MS_TIME_THRESHOLD = 1e11;\n\n/** Milliseconds in one unit of a series' bar times: 1 for milliseconds, 1000 for seconds. */\nexport type BarTimeUnit = 1 | 1000;\n\n/**\n * The unit of a series' bar times, decided once from its newest bar, so its\n * old bars keep the series' unit: a millisecond bar from 2000 is below 1e12\n * and would pass for seconds on its own. Milliseconds when there are no bars.\n */\nexport function barTimeUnit(data: ArrayLike<{ time: number }>): BarTimeUnit {\n const last = data.length > 0 ? data[data.length - 1].time : NaN;\n if (!Number.isFinite(last)) return 1;\n return Math.abs(last) >= MS_TIME_THRESHOLD ? 1 : 1000;\n}\n\n/**\n * Normalize bar timestamp to milliseconds. Pass the series' unit\n * (`barTimeUnit(data)`) when there is one; on its own a time is taken as\n * milliseconds from `MS_TIME_THRESHOLD` (1973) up, seconds below.\n */\nexport function normalizeBarTime(time: number, unit?: BarTimeUnit): number {\n if (unit !== undefined) return time * unit;\n return Math.abs(time) >= MS_TIME_THRESHOLD ? time : time * 1000;\n}\n\n/**\n * Normalize a bar's timestamp field to milliseconds.\n * Accepts either { time } (ms or s) or { t, o, h, l, c, v } wire format.\n */\nexport function normalizeBar(raw: Record<string, number>): OHLCBar {\n const time = normalizeBarTime(raw.time ?? raw.t ?? 0);\n return {\n time,\n open: raw.open ?? raw.o ?? 0,\n high: raw.high ?? raw.h ?? 0,\n low: raw.low ?? raw.l ?? 0,\n close: raw.close ?? raw.c ?? 0,\n volume: raw.volume ?? raw.v ?? 0,\n };\n}\n\nexport function sliceVisibleData(\n data: DataSeries,\n from: number,\n to: number,\n): DataSeries {\n const startIdx = Math.max(0, from);\n const endIdx = Math.min(data.length, to + 1);\n return data.slice(startIdx, endIdx);\n}\n\nexport function findBarIndex(data: DataSeries, timestamp: number): number {\n let lo = 0;\n let hi = data.length - 1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n if (data[mid].time < timestamp) lo = mid + 1;\n else if (data[mid].time > timestamp) hi = mid - 1;\n else return mid;\n }\n return lo;\n}\n\nexport function computePriceRange(\n data: DataSeries,\n from: number,\n to: number,\n padding = 0.05,\n): { min: number; max: number } {\n if (data.length === 0) return { min: 0, max: 1 };\n const startIdx = Math.max(0, from);\n const endIdx = Math.min(data.length - 1, to);\n let min = Infinity;\n let max = -Infinity;\n for (let i = startIdx; i <= endIdx; i++) {\n if (data[i].low < min) min = data[i].low;\n if (data[i].high > max) max = data[i].high;\n }\n if (min === Infinity) return { min: 0, max: 1 };\n const range = max - min || 1;\n return {\n min: min - range * padding,\n max: max + range * padding,\n };\n}\n\nexport function mergeBar(existing: OHLCBar, tick: { price: number; volume?: number; time: number }): OHLCBar {\n return {\n ...existing,\n high: Math.max(existing.high, tick.price),\n low: Math.min(existing.low, tick.price),\n close: tick.price,\n volume: existing.volume + (tick.volume ?? 0),\n time: tick.time,\n };\n}\n","export function hexToRgba(hex: string, alpha = 1): string {\n // #rgb and #rgba are short for #rrggbb and #rrggbbaa; a hex alpha gives way to `alpha`.\n let h = hex.slice(1);\n if (h.length === 3 || h.length === 4) h = [...h.slice(0, 3)].map((c) => c + c).join('');\n const r = parseInt(h.slice(0, 2), 16);\n const g = parseInt(h.slice(2, 4), 16);\n const b = parseInt(h.slice(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n\n/**\n * `color` at `alpha`: hex (#rgb, #rgba, #rrggbb, #rrggbbaa) and rgb()/rgba()\n * with commas or spaces. Any other colour (a name, hsl()) comes back as it is.\n */\nexport function withAlpha(color: string, alpha: number): string {\n const c = color.trim();\n if (/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(c)) {\n return hexToRgba(c, alpha);\n }\n const rgb = c.match(/^rgba?\\(\\s*(\\d+(?:\\.\\d+)?)[\\s,]+(\\d+(?:\\.\\d+)?)[\\s,]+(\\d+(?:\\.\\d+)?)/i);\n if (rgb) {\n return `rgba(${rgb[1]}, ${rgb[2]}, ${rgb[3]}, ${alpha})`;\n }\n return color;\n}\n\nexport function lerpColor(colorA: string, colorB: string, t: number): string {\n const parseHex = (hex: string) => {\n hex = hex.replace('#', '');\n if (hex.length === 3) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n return {\n r: parseInt(hex.slice(0, 2), 16),\n g: parseInt(hex.slice(2, 4), 16),\n b: parseInt(hex.slice(4, 6), 16),\n };\n };\n const a = parseHex(colorA);\n const b = parseHex(colorB);\n const r = Math.round(a.r + (b.r - a.r) * t);\n const g = Math.round(a.g + (b.g - a.g) * t);\n const bl = Math.round(a.b + (b.b - a.b) * t);\n return `rgb(${r},${g},${bl})`;\n}\n","/**\n * The display timezone: an IANA name (`'America/New_York'`, `'UTC'`), a fixed\n * offset in minutes east of UTC (`-300`), or null for the browser's own zone.\n */\nexport type TimeZoneSetting = string | number | null;\n\nconst MINUTE_MS = 60_000;\nconst DAY_MS = 24 * 60 * MINUTE_MS;\n/** Zones change their offset on quarter hours, so it holds for each 15 minutes. */\nconst OFFSET_BUCKET_MS = 15 * MINUTE_MS;\nconst OFFSET_CACHE_MAX = 8192;\n/**\n * Clocks change months apart, so a week whose offset is the same at both\n * ends holds it throughout: one lookup serves a week of bars.\n */\nconst WEEK_MS = 7 * DAY_MS;\nconst WEEK_CACHE_MAX = 4096;\n/** Names of UTC itself: no lookup needed. */\nconst UTC_ZONES = new Set(['UTC', 'Etc/UTC', 'GMT', 'Etc/GMT', 'UCT', 'Etc/UCT', 'Universal', 'Etc/Universal', 'Zulu', 'Etc/Zulu']);\n\nconst zoneFormats = new Map<string, Intl.DateTimeFormat>();\nconst offsetCache = new Map<string, number>();\n/** A week's offsets: `before` until the clocks change at `change`, then `after`. */\ninterface WeekOffsets {\n before: number;\n change: number;\n after: number;\n}\n/** Per zone, the offsets of each week looked at. */\nconst weekOffsets = new Map<string, Map<number, WeekOffsets>>();\n\nfunction zoneFormat(zone: string): Intl.DateTimeFormat {\n let format = zoneFormats.get(zone);\n if (!format) {\n format = new Intl.DateTimeFormat('en-US', {\n timeZone: zone,\n hourCycle: 'h23',\n year: 'numeric',\n month: 'numeric',\n day: 'numeric',\n hour: 'numeric',\n minute: 'numeric',\n second: 'numeric',\n });\n zoneFormats.set(zone, format);\n }\n return format;\n}\n\n/** Whether `zone` is a time zone this browser knows (an IANA name such as `'Europe/London'`). */\nexport function isValidTimeZone(zone: string): boolean {\n if (!zone) return false;\n try {\n zoneFormat(zone);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * The zone's offset from UTC, in minutes east, at `timeMs` — daylight saving\n * time included. Throws a RangeError for a zone the browser doesn't know.\n */\nexport function zoneOffsetMinutes(zone: string, timeMs: number): number {\n if (!Number.isFinite(timeMs) || UTC_ZONES.has(zone)) return 0;\n let weeks = weekOffsets.get(zone);\n if (!weeks) {\n weeks = new Map();\n weekOffsets.set(zone, weeks);\n }\n const week = Math.floor(timeMs / WEEK_MS);\n let offsets = weeks.get(week);\n if (!offsets) {\n offsets = readWeek(zone, week * WEEK_MS);\n if (weeks.size >= WEEK_CACHE_MAX) weeks.clear();\n weeks.set(week, offsets);\n }\n return timeMs < offsets.change ? offsets.before : offsets.after;\n}\n\n/** The offsets of the week from `start`, and the quarter hour the clocks change in it, if they do. */\nfunction readWeek(zone: string, start: number): WeekOffsets {\n const before = bucketOffset(zone, start);\n let last = start + WEEK_MS - OFFSET_BUCKET_MS;\n const after = bucketOffset(zone, last);\n if (after === before) return { before, change: Infinity, after };\n // Halve the week down to the first quarter hour on the new offset.\n let first = start;\n while (last - first > OFFSET_BUCKET_MS) {\n const mid = first + Math.floor((last - first) / OFFSET_BUCKET_MS / 2) * OFFSET_BUCKET_MS;\n if (bucketOffset(zone, mid) === before) first = mid;\n else last = mid;\n }\n return { before, change: last, after };\n}\n\n/** The zone's offset over the quarter hour holding `timeMs`. */\nfunction bucketOffset(zone: string, timeMs: number): number {\n const bucket = Math.floor(timeMs / OFFSET_BUCKET_MS);\n const key = `${zone}|${bucket}`;\n const cached = offsetCache.get(key);\n if (cached !== undefined) return cached;\n\n const at = bucket * OFFSET_BUCKET_MS;\n let year = 1970, month = 1, day = 1, hour = 0, minute = 0, second = 0;\n for (const part of zoneFormat(zone).formatToParts(new Date(at))) {\n const value = Number(part.value);\n switch (part.type) {\n case 'year': year = value; break;\n case 'month': month = value; break;\n case 'day': day = value; break;\n case 'hour': hour = value % 24; break;\n case 'minute': minute = value; break;\n case 'second': second = value; break;\n }\n }\n const offset = Math.round((Date.UTC(year, month - 1, day, hour, minute, second) - at) / MINUTE_MS);\n if (offsetCache.size >= OFFSET_CACHE_MAX) offsetCache.clear();\n offsetCache.set(key, offset);\n return offset;\n}\n\n/** Minutes east of UTC at `timeMs` for any display timezone. */\nexport function offsetAt(tz: TimeZoneSetting, timeMs: number): number {\n if (tz === null) return -new Date(timeMs).getTimezoneOffset();\n if (typeof tz === 'number') return tz;\n return zoneOffsetMinutes(tz, timeMs);\n}\n\n/**\n * The instant whose wall clock in `tz` reads `wallMs` (a wall-clock time\n * written as if it were UTC, e.g. `Date.UTC(2026, 0, 15, 9, 30)`). A time\n * that occurs twice (clocks going back) is its first occurrence; a time a\n * spring-forward skips moves forward by the jump (02:30 → 03:30).\n */\nexport function wallToUtc(wallMs: number, tz: TimeZoneSetting): number {\n // The instant is within 14 hours of `wallMs`, so the offsets a day either\n // side bracket any clock change near it.\n const early = wallMs - offsetAt(tz, wallMs - DAY_MS) * MINUTE_MS;\n if (early + offsetAt(tz, early) * MINUTE_MS === wallMs) return early;\n const late = wallMs - offsetAt(tz, wallMs + DAY_MS) * MINUTE_MS;\n if (late + offsetAt(tz, late) * MINUTE_MS === wallMs) return late;\n return early; // skipped: read with the offset from before the jump\n}\n\nconst dateFormatters = new Map<string, (ms: number) => string>();\n\n/**\n * `Intl.DateTimeFormat#format` for dates in a display timezone, cached per\n * locale, options and zone (building a format costs ~40µs). A fixed offset\n * is applied by shifting the instant and formatting it as UTC.\n */\nexport function zonedDateFormatter(\n locale: string,\n options: Intl.DateTimeFormatOptions,\n tz: TimeZoneSetting,\n): (ms: number) => string {\n const key = `${locale}|${JSON.stringify(options)}|${tz === null ? 'local' : typeof tz === 'number' ? `fixed:${tz}` : tz}`;\n let formatter = dateFormatters.get(key);\n if (formatter) return formatter;\n\n const timeZone = tz === null ? undefined : typeof tz === 'number' ? 'UTC' : tz;\n let format: Intl.DateTimeFormat;\n try {\n format = new Intl.DateTimeFormat(locale, { ...options, timeZone });\n } catch {\n format = new Intl.DateTimeFormat('en-US', { ...options, timeZone }); // unknown locale tag\n }\n const shift = typeof tz === 'number' ? tz * MINUTE_MS : 0;\n formatter = (ms) => format.format(new Date(ms + shift));\n dateFormatters.set(key, formatter);\n return formatter;\n}\n","import type { KnownTimeFrame, TimeFrame, TimeFrameUnit } from '../types/ohlc.js';\nimport { offsetAt, type TimeZoneSetting } from './timezone.js';\nimport { barTimeUnit } from './data.js';\n\nconst TIMEFRAME_MS: Record<KnownTimeFrame, number> = {\n '1s': 1_000,\n '5s': 5_000,\n '15s': 15_000,\n '30s': 30_000,\n '1m': 60_000,\n '3m': 180_000,\n '5m': 300_000,\n '15m': 900_000,\n '30m': 1_800_000,\n '45m': 2_700_000,\n '1h': 3_600_000,\n '2h': 7_200_000,\n '3h': 10_800_000,\n '4h': 14_400_000,\n '6h': 21_600_000,\n '8h': 28_800_000,\n '12h': 43_200_000,\n '1d': 86_400_000,\n '2d': 172_800_000,\n '3d': 259_200_000,\n '1w': 604_800_000,\n '2w': 1_209_600_000,\n '1M': 2_592_000_000,\n '3M': 7_776_000_000,\n '6M': 15_552_000_000,\n '12M': 31_536_000_000,\n};\n\n/** Milliseconds per unit; a month counts 30 days (a 12-month frame, 365). */\nconst UNIT_MS: Record<TimeFrameUnit, number> = {\n s: 1_000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n M: 2_592_000_000,\n};\n\nconst TIMEFRAME_PATTERN = /^([1-9]\\d*)([smhdwM])$/;\n\n/** A timeframe's count and unit, or null when `value` isn't one (`'7m'` → 7 minutes). */\nexport function parseTimeframe(value: string): { count: number; unit: TimeFrameUnit } | null {\n const match = TIMEFRAME_PATTERN.exec(value);\n if (!match) return null;\n const count = Number(match[1]);\n return Number.isSafeInteger(count) ? { count, unit: match[2] as TimeFrameUnit } : null;\n}\n\n/** Most trades a tick bar may hold. */\nconst MAX_TICKS_PER_BAR = 1_000_000;\n\n/** The trades per bar of a tick timeframe (`'100T'` → 100), or null for any other. */\nexport function tickBarCount(timeframe: string): number | null {\n const match = /^([1-9]\\d*)T$/.exec(timeframe);\n if (!match) return null;\n const count = Number(match[1]);\n return Number.isSafeInteger(count) && count <= MAX_TICKS_PER_BAR ? count : null;\n}\n\nexport function isTimeFrame(value: string): value is TimeFrame {\n return parseTimeframe(value) !== null;\n}\n\n/** Length of a timeframe in milliseconds (approximate for months); NaN for a non-timeframe. */\nexport function timeframeToMs(tf: TimeFrame): number {\n const known = TIMEFRAME_MS[tf as KnownTimeFrame];\n if (known !== undefined) return known;\n const parsed = parseTimeframe(tf);\n return parsed ? parsed.count * UNIT_MS[parsed.unit] : Number.NaN;\n}\n\nexport function formatTimestamp(timestamp: number, tf: TimeFrame): string {\n const d = new Date(timestamp);\n const ms = timeframeToMs(tf);\n if (ms >= 86_400_000) {\n return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n }\n if (ms >= 3_600_000) {\n return d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });\n }\n return d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' });\n}\n\nexport function alignToTimeframe(timestamp: number, tf: TimeFrame): number {\n const ms = timeframeToMs(tf);\n return Math.floor(timestamp / ms) * ms;\n}\n\nexport interface TimeParts {\n year: number;\n month: number; // 1-12\n day: number;\n hours: number;\n minutes: number;\n}\n\n/**\n * Calendar parts of a timestamp in a display timezone: an IANA zone\n * (`'America/New_York'`, daylight saving time included), a fixed UTC offset in\n * minutes (e.g. -300 for EST) or the browser's zone (`null`).\n */\nexport function timeParts(timeMs: number, tz: TimeZoneSetting): TimeParts {\n if (tz === null) {\n const d = new Date(timeMs);\n return {\n year: d.getFullYear(),\n month: d.getMonth() + 1,\n day: d.getDate(),\n hours: d.getHours(),\n minutes: d.getMinutes(),\n };\n }\n const d = new Date(timeMs + offsetAt(tz, timeMs) * 60_000);\n return {\n year: d.getUTCFullYear(),\n month: d.getUTCMonth() + 1,\n day: d.getUTCDate(),\n hours: d.getUTCHours(),\n minutes: d.getUTCMinutes(),\n };\n}\n\n/**\n * A bar with no time-of-day component (a daily, weekly, monthly or yearly candle — always anchored\n * to a calendar-day boundary, so hours/minutes read 00:00) needs the YEAR in its label instead: a\n * bare `month/day` is ambiguous across years, and `00:00` tells a trader nothing. Detected from the\n * parts themselves rather than a separate timeframe parameter, so every caller that formats a bar's\n * time gets the same rule for free. It can't tell an hourly bar at midnight from a daily one:\n * callers that have the series ask `barsAreDaily` first and fall back to this.\n */\nexport function isDateOnly(parts: Pick<TimeParts, 'hours' | 'minutes'>): boolean {\n return parts.hours === 0 && parts.minutes === 0;\n}\n\n/** Gaps this long or longer (a day with a daylight-saving change is 23 h) mean daily-or-larger bars. */\nconst DAILY_GAP_MS = 20 * 3_600_000;\n\n/**\n * Whether bars are a day apart or more (daily, weekly, monthly): their labels\n * then carry a date and no time of day, while an hourly bar at midnight keeps\n * its time. Read from the smallest gap among the first bars, so a weekend or\n * a missing session doesn't make an intraday series look daily. Undefined for\n * fewer than two bars.\n */\nexport function barsAreDaily(data: ArrayLike<{ time: number }>): boolean | undefined {\n const n = Math.min(data.length, 64);\n if (n < 2) return undefined;\n const unit = barTimeUnit(data);\n let gap = Infinity;\n for (let i = 1; i < n; i++) {\n const d = Math.abs(data[i].time - data[i - 1].time) * unit;\n if (d > 0 && d < gap) gap = d;\n }\n return gap === Infinity ? undefined : gap >= DAILY_GAP_MS;\n}\n\n/** A short label like `UTC-5` or `UTC+5:30` for the offset in force at `atMs` (default: now). */\nexport function tzLabel(tz: TimeZoneSetting, atMs: number = Date.now()): string {\n const min = offsetAt(tz, atMs);\n const sign = min >= 0 ? '+' : '-';\n const abs = Math.abs(min);\n const h = Math.floor(abs / 60);\n const m = abs % 60;\n return m === 0 ? `UTC${sign}${h}` : `UTC${sign}${h}:${String(m).padStart(2, '0')}`;\n}\n\n/** Day-of-week for a Monday-anchored reference week (1970-01-05 was a Monday, UTC). */\nconst WEEK_ANCHOR_MS = Date.UTC(1970, 0, 5);\n\n/**\n * Start of the bucket a timestamp falls into for a given timeframe.\n *\n * Intraday and daily frames are anchored to the Unix epoch (UTC) via fixed-ms\n * flooring. Weekly frames are anchored to Monday (or `weekStartsOn`), monthly\n * and yearly frames use calendar boundaries (1st of month, Jan for years) so\n * 3M aligns to quarters and 12M to calendar years.\n */\nexport function timeframeBucketStart(\n timestamp: number,\n tf: TimeFrame,\n weekStartsOn: 0 | 1 = 1,\n): number {\n const { count, unit } = parseTimeframe(tf) ?? { count: 1, unit: 'm' as const };\n\n if (unit === 's' || unit === 'm' || unit === 'h' || unit === 'd') {\n const ms = count * UNIT_MS[unit];\n return Math.floor(timestamp / ms) * ms;\n }\n\n const d = new Date(timestamp);\n\n if (unit === 'w') {\n const dayMs = UNIT_MS.d;\n const dow = d.getUTCDay();\n const shift = (dow - weekStartsOn + 7) % 7;\n const weekStart = Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() - shift);\n if (count <= 1) return weekStart;\n const weekIndex = Math.floor((weekStart - WEEK_ANCHOR_MS) / (7 * dayMs));\n const grouped = Math.floor(weekIndex / count) * count;\n return WEEK_ANCHOR_MS + grouped * 7 * dayMs;\n }\n\n // months / quarters / years\n const totalMonths = d.getUTCFullYear() * 12 + d.getUTCMonth();\n const grouped = Math.floor(totalMonths / count) * count;\n return Date.UTC(Math.floor(grouped / 12), grouped % 12, 1);\n}\n","const NUMBER_FORMAT_CACHE_LIMIT = 64;\nconst numberFormatCache = new Map<string, Intl.NumberFormat>();\n\n/**\n * `Number#toLocaleString(locale, options)` constructs a fresh\n * `Intl.NumberFormat` on every call — ~20µs in V8 versus ~0.4µs to reuse one.\n * Axis labels, the legend, crosshair pills and panel axes format dozens of\n * numbers per frame, so formatters are cached per (locale, precision). The\n * output is identical: per spec, `toLocaleString` is defined as exactly this.\n */\nfunction numberFormat(locale: string, precision: number): Intl.NumberFormat {\n const key = `${locale}|${precision}`;\n let nf = numberFormatCache.get(key);\n if (!nf) {\n if (numberFormatCache.size >= NUMBER_FORMAT_CACHE_LIMIT) numberFormatCache.clear();\n nf = new Intl.NumberFormat(locale, {\n minimumFractionDigits: precision,\n maximumFractionDigits: precision,\n });\n numberFormatCache.set(key, nf);\n }\n return nf;\n}\n\nexport function formatPrice(value: number, precision = 2, locale = 'en-US'): string {\n return numberFormat(locale, precision).format(value);\n}\n\n/**\n * Decimals the price axis uses for a visible price range: one more than its\n * tick step needs, 2 at minimum. Everything that prints a price next to the\n * axis (legend, crosshair pill, last-price tag) shares it, so a sub-cent\n * asset reads \"0.000004349\" everywhere instead of \"0.00\" off the axis.\n */\nexport function autoPricePrecision(min: number, max: number): number {\n if (!(max > min) || !Number.isFinite(min) || !Number.isFinite(max)) return 2;\n const step = computeTickStep(min, max, 8);\n return step > 0 && step < 1 ? Math.ceil(-Math.log10(step)) + 1 : 2;\n}\n\nimport type { PriceScaleMode } from '../types/rendering.js';\nimport type { PriceFormatter, PriceFraction } from '../types/chart.js';\nimport { computeTickStep } from './math.js';\n\n/**\n * Format a price-axis label for a given scale mode.\n *\n * - `regular` / `logarithmic`: the raw price.\n * - `percentage`: change from `baseline`, e.g. `+12.34%`.\n * - `indexedTo100`: the price rebased so `baseline` reads as 100.\n *\n * Falls back to a plain price when a baseline is required but missing/zero.\n */\nexport function formatPriceScaleLabel(\n price: number,\n mode: PriceScaleMode,\n baseline: number | undefined,\n precision = 2,\n locale = 'en-US',\n): string {\n if ((mode === 'percentage' || mode === 'indexedTo100') && baseline && baseline !== 0) {\n if (mode === 'percentage') {\n const pct = (price / baseline - 1) * 100;\n const sign = pct > 0 ? '+' : '';\n return `${sign}${pct.toFixed(2)}%`;\n }\n const indexed = (price / baseline) * 100;\n return formatPrice(indexed, 2, locale);\n }\n return formatPrice(price, precision, locale);\n}\n\n/**\n * `value` in fractions of a point: whole points, an apostrophe, then the\n * fraction's numerator padded to the denominator's digits (`101'16` for\n * 101.5 in 32nds). A `subDenominator` of 2 or 4 adds the half or quarter of\n * that fraction as one more digit, as futures quote it (`101'165`, `110'162`,\n * `110'167`). Rounds to the nearest step. A denominator that isn't a whole\n * number above 1 gives plain digits.\n */\nexport function formatFraction(value: number, denominator: number, subDenominator = 1): string {\n const sub = Number.isInteger(subDenominator) && subDenominator > 1 ? subDenominator : 1;\n if (!Number.isFinite(value) || !Number.isInteger(denominator) || denominator < 2) return String(value);\n const steps = denominator * sub;\n const total = Math.round(Math.abs(value) * steps);\n const whole = Math.floor(total / steps);\n const rest = total - whole * steps;\n const numerator = Math.floor(rest / sub);\n const width = String(denominator - 1).length;\n const tail = sub > 1 ? String(Math.floor(((rest - numerator * sub) / sub) * 10)) : '';\n const sign = value < 0 && total > 0 ? '-' : '';\n return `${sign}${whole}'${String(numerator).padStart(width, '0')}${tail}`;\n}\n\n/**\n * The smallest step a fraction format prints (a 32nd; a quarter of one with\n * `subDenominator` 4), or null for a denominator it can't print (it falls\n * back to plain digits then).\n */\nexport function fractionTick(fraction: PriceFraction): number | null {\n if (!Number.isInteger(fraction.denominator) || fraction.denominator < 2) return null;\n const sub = Number.isInteger(fraction.subDenominator) && (fraction.subDenominator ?? 0) > 1 ? fraction.subDenominator as number : 1;\n return 1 / (fraction.denominator * sub);\n}\n\n/** The formatter a `priceFormat` option stands for: its function, its fraction's, or null for decimals. */\nexport function priceFormatterFor(format: PriceFormatter | PriceFraction | null | undefined): PriceFormatter | null {\n if (!format) return null;\n if (typeof format === 'function') return format;\n // A denominator it can't print leaves the chart's decimals.\n if (fractionTick(format) === null) return null;\n return (price) => formatFraction(price, format.denominator, format.subDenominator);\n}\n\nexport function formatVolume(value: number): string {\n if (value >= 1_000_000_000) return (value / 1_000_000_000).toFixed(2) + 'B';\n if (value >= 1_000_000) return (value / 1_000_000).toFixed(2) + 'M';\n if (value >= 1_000) return (value / 1_000).toFixed(2) + 'K';\n return value.toFixed(0);\n}\n\nexport function detectPrecision(values: number[]): number {\n let maxDecimals = 0;\n for (const v of values) {\n const str = v.toString();\n const dot = str.indexOf('.');\n if (dot >= 0) {\n maxDecimals = Math.max(maxDecimals, str.length - dot - 1);\n }\n }\n return Math.min(maxDecimals, 8);\n}\n","import type { OHLCBar } from '../types/ohlc.js';\n\n/** The price an indicator can be computed from. */\nexport const PRICE_SOURCES = ['close', 'open', 'high', 'low', 'hl2', 'hlc3', 'ohlc4', 'hlcc4'] as const;\n\nexport type PriceSource = (typeof PRICE_SOURCES)[number];\n\nexport function isPriceSource(value: unknown): value is PriceSource {\n return typeof value === 'string' && (PRICE_SOURCES as readonly string[]).includes(value);\n}\n\n/** A bar's price by source: `hl2` = (high + low) / 2, `hlc3`, `ohlc4`, `hlcc4` = (h + l + 2c) / 4. */\nexport function sourcePrice(bar: OHLCBar, source: PriceSource): number {\n switch (source) {\n case 'open': return bar.open;\n case 'high': return bar.high;\n case 'low': return bar.low;\n case 'hl2': return (bar.high + bar.low) / 2;\n case 'hlc3': return (bar.high + bar.low + bar.close) / 3;\n case 'ohlc4': return (bar.open + bar.high + bar.low + bar.close) / 4;\n case 'hlcc4': return (bar.high + bar.low + 2 * bar.close) / 4;\n default: return bar.close;\n }\n}\n\nconst INDICATOR_SOURCE = /^ind:([^:]+):(.+)$/;\n\n/** A source that is another indicator's line: `ind:<instanceId>:<key>`. */\nexport function indicatorSource(instanceId: string, key: string): string {\n return `ind:${instanceId}:${key}`;\n}\n\n/** The indicator line a source refers to, or null for a price source or anything else. */\nexport function parseIndicatorSource(value: unknown): { instanceId: string; key: string } | null {\n if (typeof value !== 'string') return null;\n const m = INDICATOR_SOURCE.exec(value);\n return m ? { instanceId: m[1], key: m[2] } : null;\n}\n","import type { OHLCBar, TimeFrame, TimeFrameUnit } from '../types/ohlc.js';\nimport { parseTimeframe, timeframeBucketStart, timeframeToMs } from './time.js';\n\nconst DAY_MS = 86_400_000;\n\n/**\n * Aggregate bars (ascending, times in ms) into a coarser timeframe: per\n * bucket (see `timeframeBucketStart`) the first open, the extremes, the last\n * close and the summed volume. The input bars are not changed.\n */\nexport function resampleBars(bars: readonly OHLCBar[], target: TimeFrame, weekStartsOn: 0 | 1 = 1): OHLCBar[] {\n const out: OHLCBar[] = [];\n let current: OHLCBar | null = null;\n for (const bar of bars) {\n const key = timeframeBucketStart(bar.time, target, weekStartsOn);\n if (current === null || key !== current.time) {\n current = { time: key, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume };\n out.push(current);\n } else {\n if (bar.high > current.high) current.high = bar.high;\n if (bar.low < current.low) current.low = bar.low;\n current.close = bar.close;\n current.volume += bar.volume;\n }\n }\n return out;\n}\n\nconst isFixedLength = (unit: TimeFrameUnit): boolean => unit === 's' || unit === 'm' || unit === 'h' || unit === 'd';\n\n/** Whether whole bars of `base` fill every bucket of `target` exactly. */\nfunction buildsInto(base: TimeFrame, target: TimeFrame): boolean {\n const b = parseTimeframe(base);\n const t = parseTimeframe(target);\n if (!b || !t) return false;\n const baseMs = timeframeToMs(base);\n // Feeds disagree on where a 3-day bar starts: only build from whole days.\n if (b.unit === 'd' && b.count > 1) return false;\n if (isFixedLength(t.unit)) {\n return isFixedLength(b.unit) && timeframeToMs(target) % baseMs === 0;\n }\n if (b.unit === t.unit) return t.count % b.count === 0;\n // Weeks and months start at midnight: build them from bars that never\n // cross one.\n return isFixedLength(b.unit) && DAY_MS % baseMs === 0;\n}\n\n/**\n * The coarsest of `supported` that builds `target` from whole bars (e.g. 7m\n * from 1m, 90m from 30m, a quarter from months), or null when none does.\n */\nexport function pickBaseTimeframe(target: TimeFrame, supported: readonly TimeFrame[]): TimeFrame | null {\n let best: TimeFrame | null = null;\n let bestMs = 0;\n for (const tf of supported) {\n if (!buildsInto(tf, target)) continue;\n const ms = timeframeToMs(tf);\n if (ms > bestMs) {\n best = tf;\n bestMs = ms;\n }\n }\n return best;\n}\n","import type { SymbolInfo } from '../types/symbol.js';\n\n/** How well `query` (upper case) matches `text` (upper case): 0 when it doesn't. */\nfunction score(text: string, query: string): number {\n if (!text) return 0;\n if (text === query) return 1000;\n if (text.startsWith(query)) return 600 - Math.min(100, text.length - query.length);\n const at = text.indexOf(query);\n return at >= 0 ? 300 - Math.min(100, at) : 0;\n}\n\n/**\n * Symbols matching `query`, best first: an exact ticker, then tickers that\n * start with it, then names that contain it. `boost` adds to a match's score\n * (say, for the quote currencies most traded). An empty query keeps the order\n * given. At most `limit` results.\n */\nexport function rankSymbols(\n symbols: readonly SymbolInfo[],\n query: string,\n limit = 50,\n boost?: (info: SymbolInfo) => number,\n): SymbolInfo[] {\n const q = query.trim().toUpperCase();\n if (!q) return symbols.slice(0, limit);\n const scored: { info: SymbolInfo; score: number }[] = [];\n for (const info of symbols) {\n const s = Math.max(\n score(info.symbol.toUpperCase(), q),\n score((info.description ?? '').toUpperCase(), q) * 0.8,\n );\n if (s > 0) scored.push({ info, score: s + (boost?.(info) ?? 0) });\n }\n scored.sort((a, b) => b.score - a.score || a.info.symbol.localeCompare(b.info.symbol));\n return scored.slice(0, limit).map((s) => s.info);\n}\n\n/** Decimals in a price step: `'0.01000000'` → 2, `'1'` → 0, `1e-8` → 8. */\nexport function stepDecimals(step: number | string): number {\n const value = typeof step === 'number' ? step : Number(step);\n if (!Number.isFinite(value) || value <= 0) return 0;\n for (let decimals = 0; decimals <= 12; decimals++) {\n const scaled = value * 10 ** decimals;\n if (Math.abs(scaled - Math.round(scaled)) < 1e-9 * Math.max(1, scaled)) return decimals;\n }\n return 12;\n}\n\n/** `'09:30'` → 570 (`'24:00'`, a day's end, → 1440), or null for anything that isn't a time of day. */\nexport function sessionMinute(time: string): number | null {\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(time.trim());\n if (!match) return null;\n const hours = Number(match[1]);\n const minutes = Number(match[2]);\n if (hours === 24 && minutes === 0) return 1440;\n return hours < 24 && minutes < 60 ? hours * 60 + minutes : null;\n}\n","import type { SymbolInfo } from '../types/symbol.js';\nimport { sessionMinute } from './symbols.js';\nimport { isValidTimeZone, zoneOffsetMinutes } from './timezone.js';\n\n/** Whether a symbol's market is trading now, and when that changes. */\nexport interface MarketStatus {\n /** `'always'`: no hours known (24/7, such as crypto). */\n state: 'open' | 'closed' | 'always';\n /** When it next closes (open) or opens (closed), in ms; null when unknown. */\n next: number | null;\n}\n\nconst DAY_MS = 86_400_000;\nconst MINUTE_MS = 60_000;\n/** Days looked ahead for the next open (a long weekend, a week of holidays). */\nconst LOOK_AHEAD_DAYS = 8;\n\n/**\n * The market status of `info` at `now`: in a session of its hours (in its\n * exchange's time zone, clocks changes included) or not, and when that\n * changes. Sessions that meet count as one; a session past midnight belongs\n * to the day it starts. Without hours or a zone it knows: `'always'`.\n */\nexport function marketStatus(info: SymbolInfo | null | undefined, now: number): MarketStatus {\n const zone = info?.timezone;\n const sessions = (info?.sessions ?? []).flatMap((s) => {\n const start = sessionMinute(s.start);\n const end = sessionMinute(s.end);\n if (start === null || end === null || start === end) return [];\n return [{ start, end, days: Array.isArray(s.days) ? s.days : null }];\n });\n if (!zone || !isValidTimeZone(zone) || sessions.length === 0 || !Number.isFinite(now)) {\n return { state: 'always', next: null };\n }\n\n const offset = zoneOffsetMinutes(zone, now) * MINUTE_MS;\n const today = Math.floor((now + offset) / DAY_MS) * DAY_MS;\n // A wall-clock time in the zone, back to UTC with the offset that holds then\n // (asked twice, so a time beside a clock change settles on its side).\n const toUtc = (local: number) => {\n const guess = local - zoneOffsetMinutes(zone, local - offset) * MINUTE_MS;\n return local - zoneOffsetMinutes(zone, guess) * MINUTE_MS;\n };\n\n const spans: [number, number][] = [];\n for (let d = -1; d <= LOOK_AHEAD_DAYS; d++) {\n const dayStart = today + d * DAY_MS;\n const weekday = (Math.floor(dayStart / DAY_MS) + 4) % 7; // 1 Jan 1970 was a Thursday\n for (const s of sessions) {\n if (s.days && !s.days.includes(weekday)) continue;\n const start = dayStart + s.start * MINUTE_MS;\n const end = (s.end > s.start ? dayStart : dayStart + DAY_MS) + s.end * MINUTE_MS;\n spans.push([toUtc(start), toUtc(end)]);\n }\n }\n spans.sort((a, b) => a[0] - b[0]);\n const merged: [number, number][] = [];\n for (const [start, end] of spans) {\n const last = merged[merged.length - 1];\n if (last && start <= last[1]) merged[merged.length - 1] = [last[0], Math.max(last[1], end)];\n else merged.push([start, end]);\n }\n\n const current = merged.find(([start, end]) => start <= now && now < end);\n if (current) return { state: 'open', next: current[1] };\n const upcoming = merged.find(([start]) => start > now);\n return { state: 'closed', next: upcoming ? upcoming[0] : null };\n}\n","import type { DrawingLevel, DrawingOptionDef, DrawingOptionDefs, DrawingOptionValue, DrawingOptions } from '../types/drawing.js';\n\n/** Most levels a drawing keeps; more is a pasted or hand-edited state, not a chart. */\nconst MAX_LEVELS = 48;\nconst MAX_TEXT = 2000;\n/** Largest level ratio kept (a Fibonacci level of 4.236, a time zone of 89 bars). */\nconst MAX_LEVEL_VALUE = 10_000;\n\nfunction sanitizeLevels(raw: unknown): DrawingLevel[] | undefined {\n if (!Array.isArray(raw)) return undefined;\n const levels: DrawingLevel[] = [];\n for (const item of raw) {\n if (levels.length >= MAX_LEVELS) break;\n if (typeof item !== 'object' || item === null) continue;\n const { value, visible, color } = item as Record<string, unknown>;\n if (typeof value !== 'number' || !Number.isFinite(value) || Math.abs(value) > MAX_LEVEL_VALUE) continue;\n if (visible !== undefined && typeof visible !== 'boolean') continue;\n const level: DrawingLevel = { value, visible: visible ?? true };\n if (typeof color === 'string' && color) level.color = color;\n levels.push(level);\n }\n return levels;\n}\n\n/** `raw` as a value for `def`, or undefined when it isn't one. */\nfunction sanitizeValue(def: DrawingOptionDef, raw: unknown): DrawingOptionValue | undefined {\n switch (def.kind) {\n case 'boolean':\n return typeof raw === 'boolean' ? raw : undefined;\n case 'number': {\n if (typeof raw !== 'number' || !Number.isFinite(raw)) return undefined;\n return Math.min(def.max ?? Infinity, Math.max(def.min ?? -Infinity, raw));\n }\n case 'choice':\n return typeof raw === 'string' && def.choices.some((c) => c.value === raw) ? raw : undefined;\n case 'levels':\n return sanitizeLevels(raw);\n case 'text':\n return typeof raw === 'string' ? raw.slice(0, MAX_TEXT) : undefined;\n }\n}\n\n/** The valid entries of `raw` for a tool's options: unknown keys and wrong values are dropped, numbers clamped. */\nexport function sanitizeDrawingOptions(defs: DrawingOptionDefs | undefined, raw: unknown): DrawingOptions {\n if (!defs || typeof raw !== 'object' || raw === null) return {};\n const out: DrawingOptions = {};\n for (const [key, value] of Object.entries(raw)) {\n const def = defs[key];\n if (!def) continue;\n const clean = sanitizeValue(def, value);\n if (clean !== undefined) out[key] = clean;\n }\n return out;\n}\n\nfunction copyValue(value: DrawingOptionValue | readonly DrawingLevel[]): DrawingOptionValue {\n return Array.isArray(value) ? value.map((level) => ({ ...level })) : (value as DrawingOptionValue);\n}\n\n/** One option of a drawing: its own value when valid, else the tool's default. */\nexport function drawingOption<T extends DrawingOptionValue = DrawingOptionValue>(\n defs: DrawingOptionDefs | undefined,\n options: DrawingOptions | undefined,\n key: string,\n): T {\n const def = defs?.[key];\n if (!def) return options?.[key] as T;\n const own = options && key in options ? sanitizeValue(def, options[key]) : undefined;\n return copyValue(own ?? def.default) as T;\n}\n\n/** Every option of a tool for a drawing, defaults filled in. */\nexport function resolveDrawingOptions(defs: DrawingOptionDefs | undefined, options: DrawingOptions | undefined): DrawingOptions {\n const out: DrawingOptions = {};\n for (const key of Object.keys(defs ?? {})) out[key] = drawingOption(defs, options, key);\n return out;\n}\n","import type { DrawingStyle } from '../types/drawing.js';\n\nconst LINE_STYLES: readonly DrawingStyle['lineStyle'][] = ['solid', 'dashed', 'dotted'];\nconst MAX_COLOR = 64;\nconst MAX_TEXT = 2000;\nconst MAX_LINE_WIDTH = 20;\nconst MAX_FONT_SIZE = 200;\n\nfunction finiteIn(value: unknown, min: number, max: number): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : undefined;\n}\n\nfunction colorValue(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 && value.length <= MAX_COLOR ? value : undefined;\n}\n\n/**\n * The valid fields of a drawing style from untrusted data (a saved layout, a\n * template): colours as short strings, a known line style, widths, opacity\n * and font size as finite numbers in range, text cut to length. Anything\n * else is dropped.\n */\nexport function sanitizeDrawingStyle(raw: unknown): Partial<DrawingStyle> {\n if (typeof raw !== 'object' || raw === null) return {};\n const obj = raw as Record<string, unknown>;\n const out: Partial<DrawingStyle> = {};\n const color = colorValue(obj.color);\n if (color !== undefined) out.color = color;\n const lineWidth = finiteIn(obj.lineWidth, 0.5, MAX_LINE_WIDTH);\n if (lineWidth !== undefined) out.lineWidth = lineWidth;\n if (LINE_STYLES.includes(obj.lineStyle as DrawingStyle['lineStyle'])) out.lineStyle = obj.lineStyle as DrawingStyle['lineStyle'];\n const fillColor = colorValue(obj.fillColor);\n if (fillColor !== undefined) out.fillColor = fillColor;\n const fillOpacity = finiteIn(obj.fillOpacity, 0, 1);\n if (fillOpacity !== undefined) out.fillOpacity = fillOpacity;\n const fontSize = finiteIn(obj.fontSize, 6, MAX_FONT_SIZE);\n if (fontSize !== undefined) out.fontSize = fontSize;\n if (typeof obj.text === 'string') out.text = obj.text.slice(0, MAX_TEXT);\n return out;\n}\n","import type { ChartTypeOptions } from '../types/chart.js';\n\nconst positive = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v) && v > 0;\nconst wholeIn = (v: unknown, lo: number, hi: number): v is number =>\n typeof v === 'number' && Number.isInteger(v) && v >= lo && v <= hi;\nconst isObject = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);\n\n/**\n * The chart type settings in `raw` the chart can use: boxes, ranges and\n * reversals above 0, whole line and box counts in range. A type given at\n * all is in the result (empty when none of its settings were usable), so\n * a type's settings can be put back to their defaults.\n */\nexport function readChartTypeOptions(raw: unknown): ChartTypeOptions {\n if (!isObject(raw)) return {};\n const out: ChartTypeOptions = {};\n if (isObject(raw.renko)) {\n const { boxSize, atrPeriod } = raw.renko;\n out.renko = {\n ...(positive(boxSize) || boxSize === 'atr' ? { boxSize } : {}),\n ...(wholeIn(atrPeriod, 1, 500) ? { atrPeriod } : {}),\n };\n }\n if (isObject(raw.lineBreak)) {\n const { lines } = raw.lineBreak;\n out.lineBreak = wholeIn(lines, 1, 10) ? { lines } : {};\n }\n if (isObject(raw.kagi)) {\n const { reversal, reversalType } = raw.kagi;\n out.kagi = {\n ...(positive(reversal) ? { reversal } : {}),\n ...(reversalType === 'percent' || reversalType === 'price' ? { reversalType } : {}),\n };\n }\n if (isObject(raw.pointAndFigure)) {\n const { boxSize, reversal } = raw.pointAndFigure;\n out.pointAndFigure = {\n ...(positive(boxSize) || boxSize === 'auto' ? { boxSize } : {}),\n ...(wholeIn(reversal, 1, 10) ? { reversal } : {}),\n };\n }\n if (isObject(raw.rangeBars)) {\n const { range } = raw.rangeBars;\n out.rangeBars = positive(range) || range === 'auto' ? { range } : {};\n }\n return out;\n}\n","import type { Theme } from '../types/theme.js';\nimport { withAlpha } from '../utils/color.js';\n\n/** The chart's own type: the system UI face, so text matches the page around it. */\nexport const DEFAULT_FONT_FAMILY = '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif';\n\nconst DEFAULT_FONT = {\n family: DEFAULT_FONT_FAMILY,\n sizeSmall: 10,\n sizeMedium: 12,\n sizeLarge: 14,\n};\n\n/**\n * TradeCanvas palette. Themes pick their own candle colours for their\n * background; everything drawn on top of either theme (indicators, orders,\n * signals, drawings) uses the theme-neutral values below, which keep at\n * least 3:1 contrast on both the dark ink and a white background. `amber` is\n * the brand accent for dark surfaces (2:1 on white); light surfaces use\n * `LIGHT_THEME.lineColor` / `ochre` instead.\n */\nexport const TC_PALETTE = {\n /** Brand amber — the default series line and the accent. */\n amber: '#f2a93b',\n /** Rising / bullish, legible on dark and light. */\n up: '#1fa874',\n /** Falling / bearish, legible on dark and light. */\n down: '#e8505b',\n /** Amber deep enough for white backgrounds; auto-coloured series use it. */\n ochre: '#c98316',\n /** Secondary series, signal lines. */\n blue: '#4c8dff',\n violet: '#a57cff',\n cyan: '#1398a8',\n rose: '#e25592',\n /** Zero lines, neutral guides. */\n neutral: '#7d8696',\n /** Ink ground of the dark theme. */\n ink: '#0c1016',\n} as const;\n\n/**\n * How strongly volume bars show: the candle colour at this opacity, so they\n * read as a backdrop under the candles rather than a second chart.\n */\nexport const VOLUME_ALPHA = 0.22;\n\n/** The volume bar colour for a candle colour. */\nexport function volumeColor(candleColor: string): string {\n return withAlpha(candleColor, VOLUME_ALPHA);\n}\n\nconst DARK_UP = '#2eb88a';\nconst DARK_DOWN = '#e85a67';\n\n/** Order in which overlay indicators are coloured when no colour is given. */\nexport const TC_SERIES_COLORS: readonly string[] = [\n TC_PALETTE.blue,\n TC_PALETTE.ochre,\n TC_PALETTE.violet,\n TC_PALETTE.cyan,\n TC_PALETTE.rose,\n];\n\nexport const DARK_THEME: Theme = {\n name: 'dark',\n background: TC_PALETTE.ink,\n text: '#d9dde4',\n textSecondary: '#7d8696',\n grid: '#161b23',\n crosshair: '#8a93a3',\n candleUp: DARK_UP,\n candleDown: DARK_DOWN,\n candleUpWick: DARK_UP,\n candleDownWick: DARK_DOWN,\n lineColor: TC_PALETTE.amber,\n areaTopColor: 'rgba(242, 169, 59, 0.32)',\n areaBottomColor: 'rgba(242, 169, 59, 0.0)',\n volumeUp: volumeColor(DARK_UP),\n volumeDown: volumeColor(DARK_DOWN),\n axisLine: '#1f2630',\n axisLabel: '#9aa3b2',\n axisLabelBackground: '#1f2630',\n font: DEFAULT_FONT,\n};\n\nexport const LIGHT_THEME: Theme = {\n name: 'light',\n background: '#ffffff',\n text: '#0f131a',\n textSecondary: '#6b7380',\n grid: '#eef0f3',\n crosshair: '#8a93a3',\n candleUp: '#16a36a',\n candleDown: '#d9414f',\n candleUpWick: '#16a36a',\n candleDownWick: '#d9414f',\n lineColor: '#c77a0a',\n areaTopColor: 'rgba(199, 122, 10, 0.24)',\n areaBottomColor: 'rgba(199, 122, 10, 0.0)',\n volumeUp: volumeColor('#16a36a'),\n volumeDown: volumeColor('#d9414f'),\n axisLine: '#dde1e7',\n axisLabel: '#5b6472',\n axisLabelBackground: '#eef0f3',\n font: DEFAULT_FONT,\n};\n\nexport const DARK_TERMINAL: Theme = {\n name: 'terminal',\n background: '#0E0E0E',\n text: '#C0C0C0',\n textSecondary: '#8A8A8A',\n grid: '#1A1A1A',\n crosshair: '#666666',\n candleUp: '#00FF87',\n candleDown: '#FF3B4D',\n candleUpWick: '#00FF87',\n candleDownWick: '#FF3B4D',\n lineColor: '#3D8BFD',\n areaTopColor: 'rgba(61, 139, 253, 0.3)',\n areaBottomColor: 'rgba(61, 139, 253, 0.0)',\n volumeUp: 'rgba(0, 255, 135, 0.2)',\n volumeDown: 'rgba(255, 59, 77, 0.2)',\n axisLine: '#1A1A1A',\n axisLabel: '#8A8A8A',\n axisLabelBackground: '#1A1A1A',\n font: {\n family: \"'Roboto Mono', 'JetBrains Mono', 'SF Mono', Consolas, monospace\",\n sizeSmall: 10,\n sizeMedium: 12,\n sizeLarge: 14,\n },\n};\n\n/** The built-in themes' volume colours, to tell a preset's from a theme's own. */\nconst PRESET_VOLUME = new Set([DARK_THEME.volumeUp, DARK_THEME.volumeDown, LIGHT_THEME.volumeUp, LIGHT_THEME.volumeDown, DARK_TERMINAL.volumeUp, DARK_TERMINAL.volumeDown]);\n\n/**\n * The volume bars' colours for `theme`: its own `volumeUp` / `volumeDown`,\n * except that a theme spread from a preset with new candle colours (and the\n * preset's volume left in place) gets volume in its candles' colours, as the\n * bars have always followed the candles.\n */\nexport function resolveVolumeColors(theme: Pick<Theme, 'candleUp' | 'candleDown' | 'volumeUp' | 'volumeDown'>): { up: string; down: string } {\n const pick = (volume: string, candle: string): string => {\n if (!PRESET_VOLUME.has(volume)) return volume;\n const derived = volumeColor(candle);\n return derived === candle ? volume : derived;\n };\n return { up: pick(theme.volumeUp, theme.candleUp), down: pick(theme.volumeDown, theme.candleDown) };\n}\n","import type { ChartType, LineStyle } from '../types/chart.js';\nimport type { Theme } from '../types/theme.js';\nimport type { IndicatorPlotStyle } from '../types/indicator.js';\nimport {\n CHART_STYLE_KEYS,\n type ChartStyleKey,\n type ChartStyleOverrides,\n type ChartStyleOverridesPatch,\n type PaneStyle,\n type ResolvedChartStyle,\n type ResolvedLineLook,\n type StyleValueKind,\n} from '../types/style.js';\nimport { resolveVolumeColors } from '../constants/themes.js';\n\nconst LINE_STYLES: readonly LineStyle[] = ['solid', 'dashed', 'dotted'];\n/** Widths past this are a mistake, not a look. */\nconst MAX_WIDTH = 20;\n/** Long enough for any colour CSS writes, short enough to keep anything else out. */\nconst MAX_COLOR_LENGTH = 64;\n/** The main series' line width when nothing sets it. */\nconst DEFAULT_LINE_WIDTH = 2;\n\n/**\n * A colour as CSS writes one, and only that: hex, a name, or a colour\n * function (`rgb()`, `hsl()`, `oklch()`, `color()`…) of numbers, units and\n * words. Anything else is refused, so a value read from a saved layout can't\n * carry markup or script into a style or an attribute.\n */\nconst COLOR_PATTERNS = [\n /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i,\n /^[a-z]{3,30}$/i,\n /^(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color)\\([0-9a-z.,%/\\s-]*\\)$/i,\n];\n\nfunction isColor(value: unknown): value is string {\n return typeof value === 'string' && value.length <= MAX_COLOR_LENGTH && COLOR_PATTERNS.some((pattern) => pattern.test(value.trim()));\n}\n\nfunction isValue(kind: StyleValueKind, value: unknown): boolean {\n switch (kind) {\n case 'color': return isColor(value);\n case 'lineStyle': return LINE_STYLES.includes(value as LineStyle);\n case 'width': return typeof value === 'number' && Number.isFinite(value) && value > 0 && value <= MAX_WIDTH;\n case 'boolean': return typeof value === 'boolean';\n }\n}\n\nfunction isStyleKey(key: string): key is ChartStyleKey {\n return Object.prototype.hasOwnProperty.call(CHART_STYLE_KEYS, key);\n}\n\n/**\n * Overrides read from anywhere (your options, a saved layout): the keys it\n * knows with values of their kind, `null` taking a key away. `rejected`\n * names what was left out.\n */\nexport function readChartStyleOverrides(raw: unknown): { overrides: ChartStyleOverridesPatch; rejected: string[] } {\n const overrides: Record<string, unknown> = {};\n const rejected: string[] = [];\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return { overrides: {}, rejected };\n for (const [key, value] of Object.entries(raw)) {\n if (!isStyleKey(key)) rejected.push(key);\n else if (value === null) overrides[key] = null;\n else if (isValue(CHART_STYLE_KEYS[key], value)) overrides[key] = value;\n else rejected.push(key);\n }\n return { overrides: overrides as ChartStyleOverridesPatch, rejected };\n}\n\n/** A pane's own style: the colours it can use, or null when it has none. */\nexport function readPaneStyle(raw: unknown): PaneStyle | null {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return null;\n const { background, separator } = raw as Record<string, unknown>;\n const style: PaneStyle = {\n ...(isColor(background) ? { background } : {}),\n ...(isColor(separator) ? { separator } : {}),\n };\n return Object.keys(style).length > 0 ? style : null;\n}\n\n/** A plot key as indicators name them: short, nothing odd in it. */\nconst PLOT_KEY = /^[A-Za-z0-9_]{1,32}$/;\n\n/** Plot styles read from anywhere: by plot key, a dash style and whether it shows; undefined when none. */\nexport function readIndicatorPlotStyles(raw: unknown): Record<string, IndicatorPlotStyle> | undefined {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return undefined;\n const out: Record<string, IndicatorPlotStyle> = {};\n for (const [key, value] of Object.entries(raw)) {\n if (!PLOT_KEY.test(key) || typeof value !== 'object' || value === null) continue;\n const { lineStyle, visible } = value as Record<string, unknown>;\n const plot: IndicatorPlotStyle = {\n ...(LINE_STYLES.includes(lineStyle as LineStyle) ? { lineStyle: lineStyle as LineStyle } : {}),\n ...(typeof visible === 'boolean' ? { visible } : {}),\n };\n if (Object.keys(plot).length > 0) out[key] = plot;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\n/** A dash pattern for a line style: `dashed` its own, `dotted` dots as wide as the line. */\nexport function lineDash(style: LineStyle, dashed: readonly number[], width = 1): number[] {\n if (style === 'solid') return [];\n if (style === 'dotted') return [width, width * 2];\n return [...dashed];\n}\n\ntype Lookup = (key: string) => unknown;\n\n/** The main series' colours and widths, as the chart type `type` takes them. */\nfunction seriesLooks(base: Theme, o: Lookup, type: ChartType) {\n const own = (prop: string) => o(`series.${type}.${prop}`);\n const upSet = own('upColor') ?? o('series.candlestick.upColor');\n const downSet = own('downColor') ?? o('series.candlestick.downColor');\n const up = (upSet as string | undefined) ?? base.candleUp;\n const down = (downSet as string | undefined) ?? base.candleDown;\n return {\n up,\n down,\n // A wick takes its body's colour once that is set, as the theme's own do.\n wickUp: ((own('wickUpColor') ?? o('series.candlestick.wickUpColor')) as string | undefined) ?? (upSet !== undefined ? up : base.candleUpWick),\n wickDown: ((own('wickDownColor') ?? o('series.candlestick.wickDownColor')) as string | undefined) ?? (downSet !== undefined ? down : base.candleDownWick),\n line: ((own('color') ?? own('lineColor') ?? o('series.line.color')) as string | undefined) ?? base.lineColor,\n lineWidth: ((own('lineWidth') ?? o('series.line.lineWidth')) as number | undefined) ?? DEFAULT_LINE_WIDTH,\n top: ((own('topColor') ?? o('series.area.topColor')) as string | undefined) ?? base.areaTopColor,\n bottom: ((own('bottomColor') ?? o('series.area.bottomColor')) as string | undefined) ?? base.areaBottomColor,\n };\n}\n\nfunction lineLook(o: Lookup, prefix: string, fallback: ResolvedLineLook): ResolvedLineLook {\n return {\n visible: (o(`${prefix}.visible`) as boolean | undefined) ?? fallback.visible,\n color: (o(`${prefix}.color`) as string | undefined) ?? fallback.color,\n style: (o(`${prefix}.style`) as LineStyle | undefined) ?? fallback.style,\n width: (o(`${prefix}.width`) as number | undefined) ?? fallback.width,\n };\n}\n\n/**\n * The theme the chart draws with: `base` with `overrides` on its tokens (the\n * main series' as `chartType` takes them) and the finer looks in `style`.\n * `base` is left as it was.\n */\nexport function resolveChartTheme(base: Theme, chartType: ChartType, overrides: ChartStyleOverrides): Theme {\n const { style: _ignored, ...plain } = base;\n const map = overrides as Record<string, unknown>;\n const o: Lookup = (key) => map[key];\n const series = seriesLooks(plain, o, chartType);\n const background = (o('background.color') as string | undefined) ?? plain.background;\n const str = (key: ChartStyleKey, fallback: string) => (o(key) as string | undefined) ?? fallback;\n /** A colour of a part with its own settings: the override, or null to keep to them. */\n const ownColor = (key: ChartStyleKey): string | null => (o(key) as string | undefined) ?? null;\n\n const style: ResolvedChartStyle = {\n panes: {\n background: str('panes.background', background),\n separator: str('panes.separatorColor', plain.axisLine),\n title: str('panes.titleColor', plain.textSecondary),\n },\n grid: {\n horizontal: lineLook(o, 'grid.horizontal', { visible: true, color: plain.grid, style: 'solid', width: 1 }),\n vertical: lineLook(o, 'grid.vertical', { visible: true, color: plain.grid, style: 'solid', width: 1 }),\n },\n crosshair: {\n horizontal: lineLook(o, 'crosshair.horizontal', { visible: true, color: plain.crosshair, style: 'dashed', width: 1 }),\n vertical: lineLook(o, 'crosshair.vertical', { visible: true, color: plain.crosshair, style: 'dashed', width: 1 }),\n labelBackground: str('crosshair.labelBackground', plain.text),\n labelText: str('crosshair.labelTextColor', background),\n },\n axis: {\n price: { line: str('axis.price.lineColor', plain.axisLine), text: str('axis.price.textColor', plain.axisLabel) },\n time: { line: str('axis.time.lineColor', plain.axisLine), text: str('axis.time.textColor', plain.axisLabel) },\n },\n legend: { text: str('legend.textColor', plain.text), label: str('legend.labelColor', plain.textSecondary) },\n watermark: { color: (o('watermark.color') as string | undefined) ?? null },\n lastPrice: {\n visible: (o('lastPrice.visible') as boolean | undefined) ?? true,\n up: str('lastPrice.upColor', series.up),\n down: str('lastPrice.downColor', series.down),\n style: (o('lastPrice.style') as LineStyle | undefined) ?? 'dashed',\n width: (o('lastPrice.width') as number | undefined) ?? 1,\n },\n sessionBreaks: {\n color: (o('sessionBreaks.color') as string | undefined) ?? null,\n style: (o('sessionBreaks.style') as LineStyle | undefined) ?? null,\n width: (o('sessionBreaks.width') as number | undefined) ?? null,\n },\n highLow: { color: (o('highLow.color') as string | undefined) ?? null },\n series: { lineWidth: series.lineWidth },\n trading: {\n buy: ownColor('trading.buyColor'),\n sell: ownColor('trading.sellColor'),\n profit: ownColor('trading.profitColor'),\n loss: ownColor('trading.lossColor'),\n entry: ownColor('trading.entryColor'),\n },\n markers: { long: ownColor('markers.longColor'), short: ownColor('markers.shortColor'), neutral: ownColor('markers.neutralColor') },\n tradeZones: { profit: ownColor('tradeZones.profitColor'), loss: ownColor('tradeZones.lossColor'), active: ownColor('tradeZones.activeColor') },\n drawings: { handle: ownColor('drawings.handleColor') },\n };\n\n return {\n ...plain,\n background,\n candleUp: series.up,\n candleDown: series.down,\n candleUpWick: series.wickUp,\n candleDownWick: series.wickDown,\n lineColor: series.line,\n areaTopColor: series.top,\n areaBottomColor: series.bottom,\n volumeUp: str('volume.upColor', plain.volumeUp),\n volumeDown: str('volume.downColor', plain.volumeDown),\n style,\n };\n}\n\n/** How each key that isn't the series' reads back from the resolved theme. */\nconst READ_BACK: { [K in ChartStyleKey]?: (t: Theme, s: ResolvedChartStyle) => unknown } = {\n 'background.color': (t) => t.background,\n 'panes.background': (_t, s) => s.panes.background,\n 'panes.separatorColor': (_t, s) => s.panes.separator,\n 'panes.titleColor': (_t, s) => s.panes.title,\n 'crosshair.labelBackground': (_t, s) => s.crosshair.labelBackground,\n 'crosshair.labelTextColor': (_t, s) => s.crosshair.labelText,\n 'axis.price.lineColor': (_t, s) => s.axis.price.line,\n 'axis.price.textColor': (_t, s) => s.axis.price.text,\n 'axis.time.lineColor': (_t, s) => s.axis.time.line,\n 'axis.time.textColor': (_t, s) => s.axis.time.text,\n 'legend.textColor': (_t, s) => s.legend.text,\n 'legend.labelColor': (_t, s) => s.legend.label,\n 'watermark.color': (_t, s) => s.watermark.color,\n 'lastPrice.visible': (_t, s) => s.lastPrice.visible,\n 'lastPrice.upColor': (_t, s) => s.lastPrice.up,\n 'lastPrice.downColor': (_t, s) => s.lastPrice.down,\n 'lastPrice.style': (_t, s) => s.lastPrice.style,\n 'lastPrice.width': (_t, s) => s.lastPrice.width,\n // As drawn: a theme that sets only candle colours gets volume in those colours.\n 'volume.upColor': (t) => resolveVolumeColors(t).up,\n 'volume.downColor': (t) => resolveVolumeColors(t).down,\n 'sessionBreaks.color': (_t, s) => s.sessionBreaks.color,\n 'sessionBreaks.style': (_t, s) => s.sessionBreaks.style,\n 'sessionBreaks.width': (_t, s) => s.sessionBreaks.width,\n 'highLow.color': (_t, s) => s.highLow.color,\n 'trading.buyColor': (_t, s) => s.trading.buy,\n 'trading.sellColor': (_t, s) => s.trading.sell,\n 'trading.profitColor': (_t, s) => s.trading.profit,\n 'trading.lossColor': (_t, s) => s.trading.loss,\n 'trading.entryColor': (_t, s) => s.trading.entry,\n 'markers.longColor': (_t, s) => s.markers.long,\n 'markers.shortColor': (_t, s) => s.markers.short,\n 'markers.neutralColor': (_t, s) => s.markers.neutral,\n 'tradeZones.profitColor': (_t, s) => s.tradeZones.profit,\n 'tradeZones.lossColor': (_t, s) => s.tradeZones.loss,\n 'tradeZones.activeColor': (_t, s) => s.tradeZones.active,\n 'drawings.handleColor': (_t, s) => s.drawings.handle,\n};\n\nconst LINE_PROPS: Record<string, keyof ResolvedLineLook> = { visible: 'visible', color: 'color', style: 'style', width: 'width' };\n\n/**\n * What `key` resolves to: its override, or what it falls back to. A\n * `series.<type>.*` key resolves as that type would draw; the last price\n * follows `chartType`'s colours. Null for the parts that keep to their own\n * settings when not overridden (the watermark, session breaks, high/low).\n */\nexport function chartStyleValue(\n base: Theme,\n overrides: ChartStyleOverrides,\n key: ChartStyleKey,\n chartType: ChartType = 'candlestick',\n): string | number | boolean | null {\n const parts = key.split('.');\n if (parts[0] === 'series') {\n const looks = seriesLooks(base, (k) => (overrides as Record<string, unknown>)[k], parts[1] as ChartType);\n switch (parts[2]) {\n case 'upColor': return looks.up;\n case 'downColor': return looks.down;\n case 'wickUpColor': return looks.wickUp;\n case 'wickDownColor': return looks.wickDown;\n case 'color':\n case 'lineColor': return looks.line;\n case 'lineWidth': return looks.lineWidth;\n case 'topColor': return looks.top;\n case 'bottomColor': return looks.bottom;\n }\n }\n const theme = resolveChartTheme(base, chartType, overrides);\n const style = theme.style!;\n if (parts[0] === 'grid' || (parts[0] === 'crosshair' && (parts[1] === 'horizontal' || parts[1] === 'vertical'))) {\n const group = parts[0] === 'grid' ? style.grid : style.crosshair;\n return group[parts[1] as 'horizontal' | 'vertical'][LINE_PROPS[parts[2]]];\n }\n return (READ_BACK[key]?.(theme, style) ?? null) as string | number | boolean | null;\n}\n","import type { ChartOptions } from '../types/chart.js';\n\nexport const DEFAULT_CHART_OPTIONS: Required<Pick<ChartOptions, 'autoScale' | 'rightMargin' | 'minBarSpacing' | 'maxBarSpacing'>> & Pick<ChartOptions, 'grid' | 'crosshair'> = {\n autoScale: true,\n rightMargin: 5,\n minBarSpacing: 2,\n maxBarSpacing: 30,\n grid: {\n visible: true,\n hLineStyle: 'solid',\n vLineStyle: 'solid',\n },\n crosshair: {\n mode: 'magnet',\n },\n};\n\n// Standard timeframe presets for different market types\nimport type { TimeFrame } from '../types/ohlc.js';\n\n/** Crypto: all timeframes including seconds */\nexport const TIMEFRAMES_CRYPTO: TimeFrame[] = [\n '1s', '1m', '3m', '5m', '15m', '30m',\n '1h', '2h', '4h', '6h', '8h', '12h',\n '1d', '3d', '1w', '1M',\n];\n\n/** Stocks: minute-level and above (no seconds) */\nexport const TIMEFRAMES_STOCK: TimeFrame[] = [\n '1m', '5m', '15m', '30m',\n '1h', '2h', '4h',\n '1d', '1w', '1M', '3M', '6M', '12M',\n];\n\n/** Forex: common forex timeframes */\nexport const TIMEFRAMES_FOREX: TimeFrame[] = [\n '1m', '5m', '15m', '30m',\n '1h', '4h',\n '1d', '1w', '1M',\n];\n\n/** Default favorites shown in quick-access bar */\nexport const DEFAULT_TIMEFRAME_FAVORITES: TimeFrame[] = [\n '1m', '5m', '15m', '1h', '4h', '1d', '1w',\n];\n\nexport const DEFAULT_BAR_WIDTH = 8;\nexport const DEFAULT_BAR_SPACING = 2;\nexport const PRICE_AXIS_WIDTH = 70;\nexport const TIME_AXIS_HEIGHT = 30;\nexport const MIN_PANEL_HEIGHT = 60;\nexport const DEFAULT_PANEL_HEIGHT = 120;\n","import type { LocaleStrings } from './types.js';\n\nexport const en: LocaleStrings = {\n // Chart types\n candlestick: 'Candlestick',\n line: 'Line',\n area: 'Area',\n bar: 'OHLC Bar',\n\n // Axes\n price: 'Price',\n volume: 'Volume',\n time: 'Time',\n open: 'Open',\n high: 'High',\n low: 'Low',\n close: 'Close',\n\n // Indicators - overlays\n sma: 'SMA',\n ema: 'EMA',\n bollingerBands: 'Bollinger Bands',\n vwap: 'VWAP',\n ichimoku: 'Ichimoku Cloud',\n parabolicSAR: 'Parabolic SAR',\n supertrend: 'Supertrend',\n keltnerChannel: 'Keltner Channel',\n donchianChannel: 'Donchian Channel',\n\n // Indicators - panels\n rsi: 'RSI',\n macd: 'MACD',\n stochastic: 'Stochastic',\n atr: 'ATR',\n adx: 'ADX',\n obv: 'OBV',\n williamsR: 'Williams %R',\n cci: 'CCI',\n mfi: 'MFI',\n aroon: 'Aroon',\n roc: 'ROC',\n tsi: 'TSI',\n cmf: 'CMF',\n stddev: 'Std Dev',\n volumeProfile: 'Volume Profile',\n accumulationDistribution: 'A/D Line',\n vroc: 'VROC',\n\n // Drawing tools\n trendLine: 'Trend Line',\n horizontalLine: 'Horizontal Line',\n verticalLine: 'Vertical Line',\n ray: 'Ray',\n extendedLine: 'Extended Line',\n parallelChannel: 'Parallel Channel',\n regressionChannel: 'Regression Channel',\n fibRetracement: 'Fibonacci Retracement',\n fibExtension: 'Fibonacci Extension',\n rectangle: 'Rectangle',\n ellipse: 'Ellipse',\n triangle: 'Triangle',\n pitchfork: \"Andrews' Pitchfork\",\n elliottWave: 'Elliott Wave',\n priceRange: 'Price Range',\n dateRange: 'Date Range',\n measure: 'Measure',\n textTool: 'Text',\n arrow: 'Arrow',\n clearAll: 'Clear All',\n\n // Trading\n buy: 'Buy',\n sell: 'Sell',\n buyLimit: 'Buy Limit',\n sellLimit: 'Sell Limit',\n buyStop: 'Buy Stop',\n sellStop: 'Sell Stop',\n stopLoss: 'Stop Loss',\n takeProfit: 'Take Profit',\n market: 'Market',\n limit: 'Limit',\n stop: 'Stop',\n cancel: 'Cancel',\n modify: 'Modify',\n quantity: 'Qty',\n pnl: 'P&L',\n activeOrders: 'Active Orders',\n positions: 'Positions',\n noOrders: 'No active orders',\n noPositions: 'No open positions',\n placeOrder: 'Place Order',\n rightClickToTrade: 'Right-click chart to place orders',\n\n // Market\n ceiling: 'Ceiling',\n floor: 'Floor',\n reference: 'Reference',\n session: 'Session',\n preOpen: 'Pre-Open',\n continuous: 'Continuous',\n preClose: 'Pre-Close',\n closed: 'Closed',\n\n // UI\n settings: 'Settings',\n theme: 'Theme',\n darkTheme: 'Dark',\n lightTheme: 'Light',\n tools: 'Tools',\n indicators: 'Indicators',\n overlays: 'Overlays',\n panels: 'Panels',\n orders: 'Orders',\n autoScale: 'Auto Scale',\n crosshair: 'Crosshair',\n grid: 'Grid',\n loading: 'Loading...',\n error: 'Error',\n\n numberDecimalSeparator: '.',\n numberGroupSeparator: ',',\n};\n","import type { LocaleStrings } from './types.js';\n\nexport const vi: LocaleStrings = {\n // Chart types\n candlestick: 'Nến',\n line: 'Đường',\n area: 'Vùng',\n bar: 'Thanh OHLC',\n\n // Axes\n price: 'Giá',\n volume: 'Khối lượng',\n time: 'Thời gian',\n open: 'Mở',\n high: 'Cao',\n low: 'Thấp',\n close: 'Đóng',\n\n // Indicators - overlays\n sma: 'SMA',\n ema: 'EMA',\n bollingerBands: 'Dải Bollinger',\n vwap: 'VWAP',\n ichimoku: 'Mây Ichimoku',\n parabolicSAR: 'Parabolic SAR',\n supertrend: 'Supertrend',\n keltnerChannel: 'Kênh Keltner',\n donchianChannel: 'Kênh Donchian',\n\n // Indicators - panels\n rsi: 'RSI',\n macd: 'MACD',\n stochastic: 'Stochastic',\n atr: 'ATR',\n adx: 'ADX',\n obv: 'OBV',\n williamsR: 'Williams %R',\n cci: 'CCI',\n mfi: 'MFI',\n aroon: 'Aroon',\n roc: 'ROC',\n tsi: 'TSI',\n cmf: 'CMF',\n stddev: 'Độ lệch chuẩn',\n volumeProfile: 'Phân bổ KL',\n accumulationDistribution: 'Tích lũy/Phân phối',\n vroc: 'VROC',\n\n // Drawing tools\n trendLine: 'Đường xu hướng',\n horizontalLine: 'Đường ngang',\n verticalLine: 'Đường dọc',\n ray: 'Tia',\n extendedLine: 'Đường kéo dài',\n parallelChannel: 'Kênh song song',\n regressionChannel: 'Kênh hồi quy',\n fibRetracement: 'Fibonacci thoái lui',\n fibExtension: 'Fibonacci mở rộng',\n rectangle: 'Hình chữ nhật',\n ellipse: 'Hình elip',\n triangle: 'Tam giác',\n pitchfork: 'Chĩa ba Andrews',\n elliottWave: 'Sóng Elliott',\n priceRange: 'Khoảng giá',\n dateRange: 'Khoảng thời gian',\n measure: 'Đo lường',\n textTool: 'Chữ',\n arrow: 'Mũi tên',\n clearAll: 'Xóa tất cả',\n\n // Trading\n buy: 'Mua',\n sell: 'Bán',\n buyLimit: 'Mua giới hạn',\n sellLimit: 'Bán giới hạn',\n buyStop: 'Mua chặn',\n sellStop: 'Bán chặn',\n stopLoss: 'Cắt lỗ',\n takeProfit: 'Chốt lời',\n market: 'Thị trường',\n limit: 'Giới hạn',\n stop: 'Dừng',\n cancel: 'Hủy',\n modify: 'Sửa',\n quantity: 'KL',\n pnl: 'Lãi/Lỗ',\n activeOrders: 'Lệnh chờ',\n positions: 'Vị thế',\n noOrders: 'Không có lệnh chờ',\n noPositions: 'Không có vị thế mở',\n placeOrder: 'Đặt lệnh',\n rightClickToTrade: 'Nhấp chuột phải để đặt lệnh',\n\n // Market\n ceiling: 'Trần',\n floor: 'Sàn',\n reference: 'Tham chiếu',\n session: 'Phiên',\n preOpen: 'Trước giờ mở',\n continuous: 'Liên tục',\n preClose: 'Trước giờ đóng',\n closed: 'Đóng cửa',\n\n // UI\n settings: 'Cài đặt',\n theme: 'Giao diện',\n darkTheme: 'Tối',\n lightTheme: 'Sáng',\n tools: 'Công cụ',\n indicators: 'Chỉ báo',\n overlays: 'Phủ lên',\n panels: 'Bảng',\n orders: 'Lệnh',\n autoScale: 'Tự co giãn',\n crosshair: 'Chữ thập',\n grid: 'Lưới',\n loading: 'Đang tải...',\n error: 'Lỗi',\n\n numberDecimalSeparator: ',',\n numberGroupSeparator: '.',\n};\n","export type { Locale, LocaleStrings, NumberFormatConfig, DateFormatConfig } from './types.js';\nexport { en } from './en.js';\nexport { vi } from './vi.js';\n\nimport type { Locale, LocaleStrings } from './types.js';\nimport { en } from './en.js';\nimport { vi } from './vi.js';\n\nconst locales = new Map<string, LocaleStrings>([\n ['en', en],\n ['vi', vi],\n]);\n\nlet currentLocale: Locale = 'en';\nlet currentStrings: LocaleStrings = en;\n\nexport function setLocale(locale: Locale): void {\n currentLocale = locale;\n currentStrings = locales.get(locale) ?? en;\n}\n\nexport function getLocale(): Locale {\n return currentLocale;\n}\n\nexport function t(key: keyof LocaleStrings): string {\n return currentStrings[key] ?? (en as any)[key] ?? key;\n}\n\nexport function registerLocale(locale: string, strings: LocaleStrings): void {\n locales.set(locale, strings);\n}\n\nexport function getLocaleStrings(locale?: string): LocaleStrings {\n return locales.get(locale ?? currentLocale) ?? en;\n}\n\n// Number formatting\nexport function formatNumber(value: number, precision = 2, locale?: string): string {\n const strings = locales.get(locale ?? currentLocale) ?? en;\n const dec = strings.numberDecimalSeparator;\n const grp = strings.numberGroupSeparator;\n\n const fixed = value.toFixed(precision);\n const [intPart, decPart] = fixed.split('.');\n\n // Group integer part\n const negative = intPart.startsWith('-');\n const digits = negative ? intPart.slice(1) : intPart;\n let grouped = '';\n for (let i = digits.length - 1, count = 0; i >= 0; i--, count++) {\n if (count > 0 && count % 3 === 0) grouped = grp + grouped;\n grouped = digits[i] + grouped;\n }\n if (negative) grouped = '-' + grouped;\n\n return decPart ? grouped + dec + decPart : grouped;\n}\n\nexport function formatVND(value: number): string {\n return formatNumber(value, 0, 'vi');\n}\n\nexport function formatVolumeLoc(value: number, locale?: string): string {\n if (value >= 1e9) return formatNumber(value / 1e9, 2, locale ?? currentLocale) + 'B';\n if (value >= 1e6) return formatNumber(value / 1e6, 2, locale ?? currentLocale) + 'M';\n if (value >= 1e3) return formatNumber(value / 1e3, 2, locale ?? currentLocale) + 'K';\n return formatNumber(value, 0, locale ?? currentLocale);\n}\n","import type { MarketConfig, MarketColorScheme, PriceStepBand, TradingSession, VN30FContract } from './types.js';\nimport type { Theme } from '../types/theme.js';\nimport { volumeColor } from '../constants/themes.js';\nimport { stepDecimals } from '../utils/symbols.js';\nimport { isValidTimeZone, zoneOffsetMinutes } from '../utils/timezone.js';\n\n/**\n * Vietnamese price boards: green up, red down, purple at the ceiling (trần),\n * cyan at the floor (sàn), yellow at the reference price (tham chiếu).\n */\nexport const VN_COLORS: MarketColorScheme = {\n up: '#22C55E',\n down: '#EF4444',\n unchanged: '#EAB308',\n ceiling: '#D946EF',\n floor: '#06B6D4',\n reference: '#EAB308',\n};\n\nconst VN_TIME_ZONE = 'Asia/Ho_Chi_Minh';\n\n// Session times and rules: the exchanges' trading rules as of May 2025 (the\n// HOSE KRX system went live on 5 May 2025).\nexport const HOSE_SESSIONS: TradingSession[] = [\n { name: 'ATO', startTime: '09:00', endTime: '09:15', type: 'preOpen' },\n { name: 'Phiên 1', startTime: '09:15', endTime: '11:30', type: 'continuous' },\n { name: 'Nghỉ trưa', startTime: '11:30', endTime: '13:00', type: 'closed' },\n { name: 'Phiên 2', startTime: '13:00', endTime: '14:30', type: 'continuous' },\n { name: 'ATC', startTime: '14:30', endTime: '14:45', type: 'preClose' },\n];\n\nexport const HNX_SESSIONS: TradingSession[] = [\n { name: 'Phiên 1', startTime: '09:15', endTime: '11:30', type: 'continuous' },\n { name: 'Nghỉ trưa', startTime: '11:30', endTime: '13:00', type: 'closed' },\n { name: 'Phiên 2', startTime: '13:00', endTime: '14:30', type: 'continuous' },\n { name: 'ATC', startTime: '14:30', endTime: '14:45', type: 'preClose' },\n { name: 'PLO', startTime: '14:45', endTime: '15:00', type: 'postClose' },\n];\n\nexport const UPCOM_SESSIONS: TradingSession[] = [\n { name: 'Phiên 1', startTime: '09:15', endTime: '11:30', type: 'continuous' },\n { name: 'Nghỉ trưa', startTime: '11:30', endTime: '13:00', type: 'closed' },\n { name: 'Phiên 2', startTime: '13:00', endTime: '14:30', type: 'continuous' },\n];\n\nexport const VN30F_SESSIONS: TradingSession[] = [\n { name: 'ATO', startTime: '08:45', endTime: '09:00', type: 'preOpen' },\n { name: 'Phiên 1', startTime: '09:00', endTime: '11:30', type: 'continuous' },\n { name: 'Nghỉ trưa', startTime: '11:30', endTime: '13:00', type: 'closed' },\n { name: 'Phiên 2', startTime: '13:00', endTime: '14:30', type: 'continuous' },\n { name: 'ATC', startTime: '14:30', endTime: '14:45', type: 'preClose' },\n];\n\n/** HOSE shares and closed funds, in thousands of dong: 10, 50 and 100 dong steps. */\nconst HOSE_STEPS: readonly PriceStepBand[] = [\n { from: 0, step: 0.01 },\n { from: 10, step: 0.05 },\n { from: 50, step: 0.1 },\n];\n\n// Market presets. Vietnamese prices are in thousands of dong (25.35 is 25,350\n// dong), as price boards show them; scaleMarketPrices(preset, 1000) for dong.\nexport const MARKET_HOSE: MarketConfig = {\n type: 'stock',\n exchange: 'HOSE',\n currency: 'VND',\n pricePrecision: 2,\n volumeUnit: 100,\n priceStep: 0.01,\n priceSteps: HOSE_STEPS,\n timeZone: VN_TIME_ZONE,\n priceLimits: { enabled: true, ceilingPercent: 7, floorPercent: 7 },\n sessions: HOSE_SESSIONS,\n colorScheme: VN_COLORS,\n};\n\n/** HOSE ETFs and covered warrants: 10 dong at every price. */\nexport const MARKET_HOSE_ETF: MarketConfig = {\n ...MARKET_HOSE,\n priceSteps: undefined,\n};\n\nexport const MARKET_HNX: MarketConfig = {\n type: 'stock',\n exchange: 'HNX',\n currency: 'VND',\n pricePrecision: 1,\n volumeUnit: 100,\n priceStep: 0.1,\n timeZone: VN_TIME_ZONE,\n priceLimits: { enabled: true, ceilingPercent: 10, floorPercent: 10 },\n sessions: HNX_SESSIONS,\n colorScheme: VN_COLORS,\n};\n\nexport const MARKET_UPCOM: MarketConfig = {\n type: 'stock',\n exchange: 'UPCOM',\n currency: 'VND',\n pricePrecision: 1,\n volumeUnit: 100,\n priceStep: 0.1,\n timeZone: VN_TIME_ZONE,\n priceLimits: { enabled: true, ceilingPercent: 15, floorPercent: 15 },\n sessions: UPCOM_SESSIONS,\n colorScheme: VN_COLORS,\n};\n\n/** VN30 index futures on HNX: index points, 0.1 a step, 100,000 dong a point. */\nexport const MARKET_VN30F: MarketConfig = {\n type: 'futures',\n exchange: 'HNX',\n currency: 'VND',\n pricePrecision: 1,\n volumeUnit: 1,\n priceStep: 0.1,\n contractMultiplier: 100_000,\n timeZone: VN_TIME_ZONE,\n priceLimits: { enabled: true, ceilingPercent: 7, floorPercent: 7 },\n sessions: VN30F_SESSIONS,\n colorScheme: VN_COLORS,\n};\n\nexport const MARKET_CRYPTO: MarketConfig = {\n type: 'crypto',\n currency: 'USDT',\n pricePrecision: 2,\n priceLimits: { enabled: false },\n};\n\nexport const MARKET_NYSE: MarketConfig = {\n type: 'stock',\n exchange: 'NYSE',\n currency: 'USD',\n pricePrecision: 2,\n priceStep: 0.01,\n timeZone: 'America/New_York',\n priceLimits: { enabled: false },\n sessions: [\n { name: 'Pre-Market', startTime: '04:00', endTime: '09:30', type: 'preOpen' },\n { name: 'Regular', startTime: '09:30', endTime: '16:00', type: 'continuous' },\n { name: 'After-Hours', startTime: '16:00', endTime: '20:00', type: 'postClose' },\n ],\n};\n\n// Build a theme variant for VN stock market\nexport function createVNTheme(base: Theme): Theme {\n return {\n ...base,\n candleUp: VN_COLORS.up,\n candleDown: VN_COLORS.down,\n candleUpWick: VN_COLORS.up,\n candleDownWick: VN_COLORS.down,\n volumeUp: volumeColor(VN_COLORS.up),\n volumeDown: volumeColor(VN_COLORS.down),\n };\n}\n\n/** Rounding slack, so 27.1 / 0.05 (541.999…) still counts as 542 steps. */\nconst STEP_EPSILON = 1e-9;\n\n/**\n * The price step at `price`: the band it falls in, else the market's\n * `priceStep`; undefined when the market names no step.\n */\nexport function priceStepAt(price: number, config: MarketConfig): number | undefined {\n let step = config.priceStep;\n for (const band of config.priceSteps ?? []) {\n if (price + STEP_EPSILON >= band.from) step = band.step;\n }\n return step !== undefined && step > 0 ? step : undefined;\n}\n\n/** The step of the band just below `price`, for one step down from it. */\nfunction stepBelow(price: number, config: MarketConfig): number | undefined {\n let step = config.priceStep;\n for (const band of config.priceSteps ?? []) {\n if (price - STEP_EPSILON > band.from) step = band.step;\n }\n return step !== undefined && step > 0 ? step : undefined;\n}\n\n/**\n * `price` on the step of its band: to the nearest step, or `'down'` / `'up'`.\n * A market without a step leaves it as it is.\n */\nexport function roundToPriceStep(price: number, config: MarketConfig, mode: 'nearest' | 'down' | 'up' = 'nearest'): number {\n const step = priceStepAt(price, config);\n if (step === undefined || !Number.isFinite(price)) return price;\n const steps = price / step;\n const count = mode === 'down'\n ? Math.floor(steps + STEP_EPSILON)\n : mode === 'up'\n ? Math.ceil(steps - STEP_EPSILON)\n : Math.round(steps);\n return Number((count * step).toFixed(stepDecimals(step)));\n}\n\n/**\n * The day's ceiling, floor and reference prices. With a price step, the\n * ceiling rounds down and the floor up to a valid price; when one would land\n * back on the reference it moves a step off it, and a reference of one step\n * keeps the floor at the reference (the exchanges' rules).\n */\nexport function computePriceLimits(referencePrice: number, config: MarketConfig): { ceiling: number; floor: number; reference: number } | null {\n if (!config.priceLimits?.enabled || !config.priceLimits.ceilingPercent) return null;\n const ceilPct = config.priceLimits.ceilingPercent / 100;\n const floorPct = (config.priceLimits.floorPercent ?? config.priceLimits.ceilingPercent) / 100;\n const reference = referencePrice;\n const rawCeiling = reference * (1 + ceilPct);\n const rawFloor = reference * (1 - floorPct);\n const refStep = priceStepAt(reference, config);\n if (refStep === undefined) return { ceiling: rawCeiling, floor: rawFloor, reference };\n\n const oneUp = roundToPriceStep(reference + refStep, config);\n if (Math.abs(reference - refStep) < STEP_EPSILON) return { ceiling: oneUp, floor: reference, reference };\n\n let ceiling = roundToPriceStep(rawCeiling, config, 'down');\n let floor = roundToPriceStep(rawFloor, config, 'up');\n if (ceiling <= reference) ceiling = oneUp;\n if (floor >= reference) floor = roundToPriceStep(reference - (stepBelow(reference, config) ?? refStep), config);\n if (floor <= 0) floor = reference;\n return { ceiling, floor, reference };\n}\n\n/** `value * factor` without float dust (0.05 * 1000 is 50, not 50.00000000000001). */\nconst scaled = (value: number, factor: number) => Math.round(value * factor * 1e9) / 1e9;\n\n/**\n * A copy of `config` for prices `factor` times larger (a power of ten):\n * steps, bands, the reference price and the decimals follow; a contract's\n * multiplier stays. `scaleMarketPrices(MARKET_HOSE, 1000)` reads prices in\n * dong (25350) instead of thousands (25.35).\n */\nexport function scaleMarketPrices(config: MarketConfig, factor: number): MarketConfig {\n const decimalsLess = Math.round(Math.log10(factor));\n return {\n ...config,\n pricePrecision: config.pricePrecision === undefined ? undefined : Math.max(0, config.pricePrecision - decimalsLess),\n priceStep: config.priceStep === undefined ? undefined : scaled(config.priceStep, factor),\n priceSteps: config.priceSteps?.map((band) => ({ from: scaled(band.from, factor), step: scaled(band.step, factor) })),\n priceLimits: config.priceLimits && {\n ...config.priceLimits,\n referencePrice: config.priceLimits.referencePrice === undefined ? undefined : scaled(config.priceLimits.referencePrice, factor),\n },\n };\n}\n\nconst MINUTE_MS = 60_000;\nconst DAY_MS = 86_400_000;\n\n/** `HH:mm` of `now` in `timeZone`, or on this computer's clock without one. */\nfunction wallClock(now: number, timeZone?: string): string {\n let hours: number;\n let minutes: number;\n if (timeZone) {\n const local = new Date(now + zoneOffsetMinutes(timeZone, now) * MINUTE_MS);\n hours = local.getUTCHours();\n minutes = local.getUTCMinutes();\n } else {\n const local = new Date(now);\n hours = local.getHours();\n minutes = local.getMinutes();\n }\n return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;\n}\n\n/**\n * The session `now` falls in: by the clock of `timeZone` (a market's\n * `timeZone`), or of this computer without one. Null outside them all, and\n * for a time zone it doesn't know. By the time of day only: weekends and\n * holidays are yours to tell.\n */\nexport function getCurrentSession(sessions: TradingSession[], now: number = Date.now(), timeZone?: string): TradingSession | null {\n if (timeZone !== undefined && !isValidTimeZone(timeZone)) return null;\n const hhmm = wallClock(now, timeZone);\n for (const session of sessions) {\n if (hhmm >= session.startTime && hhmm < session.endTime) return session;\n }\n return null;\n}\n\nconst pad2 = (n: number) => String(n).padStart(2, '0');\nconst isoDay = (ms: number) => {\n const d = new Date(ms);\n return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;\n};\nconst THURSDAY = 4;\n/** Vietnam is UTC+7 all year. */\nconst VN_OFFSET_MS = 7 * 3_600_000;\n\n/**\n * The last trading day of the VN30 futures contract of `month` (1–12): the\n * third Thursday, or the trading day before it when that is a holiday\n * (`isHoliday` gets `YYYY-MM-DD`). As `YYYY-MM-DD`.\n */\nexport function vn30fLastTradingDay(year: number, month: number, isHoliday?: (day: string) => boolean): string {\n const first = Date.UTC(year, month - 1, 1);\n const firstThursday = 1 + ((THURSDAY - new Date(first).getUTCDay() + 7) % 7);\n let day = Date.UTC(year, month - 1, firstThursday + 14);\n const closed = (ms: number) => {\n const weekday = new Date(ms).getUTCDay();\n return weekday === 0 || weekday === 6 || (isHoliday?.(isoDay(ms)) ?? false);\n };\n while (closed(day)) day -= DAY_MS;\n return isoDay(day);\n}\n\n/**\n * The VN30 futures contracts listed at `now`: this month's (until its last\n * trading day), the next month's, and the last months of the two quarters\n * after that.\n */\nexport function vn30fContracts(now: number, isHoliday?: (day: string) => boolean): VN30FContract[] {\n const today = isoDay(now + VN_OFFSET_MS);\n const contract = (year: number, month: number): VN30FContract => ({\n symbol: `VN30F${pad2(year % 100)}${pad2(month)}`,\n year,\n month,\n lastTradingDay: vn30fLastTradingDay(year, month, isHoliday),\n });\n const next = (c: VN30FContract) => (c.month === 12 ? contract(c.year + 1, 1) : contract(c.year, c.month + 1));\n\n const vnNow = new Date(now + VN_OFFSET_MS);\n let front = contract(vnNow.getUTCFullYear(), vnNow.getUTCMonth() + 1);\n if (today > front.lastTradingDay) front = next(front);\n const listed = [front, next(front)];\n let candidate = next(listed[1]);\n while (listed.length < 4) {\n if (candidate.month % 3 === 0) listed.push(candidate);\n candidate = next(candidate);\n }\n return listed;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/types/rendering.ts","../src/types/style.ts","../src/types/drawing.ts","../src/types/trading.ts","../src/types/signal.ts","../src/types/realtime.ts","../src/types/quote.ts","../src/types/news.ts","../src/utils/math.ts","../src/utils/data.ts","../src/utils/color.ts","../src/utils/timezone.ts","../src/utils/time.ts","../src/utils/precision.ts","../src/utils/priceSource.ts","../src/utils/resample.ts","../src/utils/symbols.ts","../src/utils/marketStatus.ts","../src/utils/drawingOptions.ts","../src/utils/drawingStyle.ts","../src/utils/chartTypeOptions.ts","../src/constants/themes.ts","../src/utils/chartStyle.ts","../src/constants/defaults.ts","../src/i18n/en.ts","../src/i18n/vi.ts","../src/i18n/index.ts","../src/market/presets.ts","../src/icons/iconSet.ts"],"sourcesContent":["export interface Point {\n x: number;\n y: number;\n}\n\nexport interface Size {\n width: number;\n height: number;\n}\n\nexport interface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport type PriceScaleMode = 'regular' | 'logarithmic' | 'percentage' | 'indexedTo100';\n\nexport interface ViewportState {\n visibleRange: { from: number; to: number };\n priceRange: { min: number; max: number };\n barWidth: number;\n barSpacing: number;\n offset: number;\n chartRect: Rect;\n /**\n * Logarithmic price geometry. Kept for back-compat; mirrors\n * `scaleMode === 'logarithmic'`. Prefer reading `scaleMode`.\n */\n logScale?: boolean;\n /**\n * Price-scale presentation. `regular`, `percentage`, and `indexedTo100` share\n * the same linear geometry — only the axis labels differ (rebased to\n * `scaleBaseline`). `logarithmic` changes the geometry. Defaults to\n * `regular` when unset.\n */\n scaleMode?: PriceScaleMode;\n /**\n * Reference price for `percentage` / `indexedTo100` axis labels — usually the\n * close of the first visible bar. Set by the chart each frame.\n */\n scaleBaseline?: number;\n /** Upside down: higher prices lower on screen. */\n invertScale?: boolean;\n /**\n * How prices on this scale read, when the chart has a price format of its\n * own (`ChartOptions.priceFormat`). Unset: decimals. Indicator panes leave\n * it unset.\n */\n formatPrice?: (price: number) => string;\n /**\n * The step prices move in under that format (a 32nd): the axis puts its\n * ticks on whole numbers of it.\n */\n priceUnit?: number;\n /**\n * Optional reference to the current bar series. When set, drawings/indicators\n * treat `anchor.time` as a real timestamp and convert to bar index via\n * `timestampToBarIndex(time, data)` at render/hit-test time. This lets\n * anchors survive timeframe / symbol switches. When unset,\n * `anchor.time` is treated as a raw bar index (legacy behavior).\n */\n data?: ReadonlyArray<{ time: number }>;\n /**\n * Width of the price axis strip right of `chartRect`, in CSS pixels. The\n * chart widens it to fit long labels (sub-cent prices); renderers drawing\n * badges on the axis size them to it. Defaults to `PRICE_AXIS_WIDTH`.\n */\n priceAxisWidth?: number;\n}\n\n/**\n * What changed, for `requestRender()`. The chart paints two canvases: a scene\n * (everything that follows data, viewport and chart objects) and a thin top\n * canvas for pointer-tied visuals. Any type except `Hover` redraws both;\n * `Hover` redraws only the top canvas (crosshair, its axis pills, legend,\n * bar countdown, measure ruler, selection box).\n */\nexport enum LayerType {\n Background = 0,\n Main = 1,\n Panel = 2,\n Overlay = 3,\n UI = 4,\n Hover = 5,\n}\n","import type { LineStyle } from './chart.js';\n\n/** The kind of value a style key takes. */\nexport type StyleValueKind = 'color' | 'lineStyle' | 'width' | 'boolean';\n\n/**\n * Every key `chart.applyOverrides()` takes, and the kind of its value.\n *\n * - `series.<chartType>.*` style the main series while it is drawn as that\n * type. Up and down colours fall back to `series.candlestick.*`, line\n * colours and widths to `series.line.*`, area fills to `series.area.*`,\n * then to the theme.\n * - The rest style one part of the chart each, falling back to the theme's\n * tokens: with no overrides the chart looks as its theme makes it.\n */\nexport const CHART_STYLE_KEYS = {\n 'background.color': 'color',\n\n 'panes.background': 'color',\n 'panes.separatorColor': 'color',\n 'panes.titleColor': 'color',\n\n 'grid.horizontal.visible': 'boolean',\n 'grid.horizontal.color': 'color',\n 'grid.horizontal.style': 'lineStyle',\n 'grid.horizontal.width': 'width',\n 'grid.vertical.visible': 'boolean',\n 'grid.vertical.color': 'color',\n 'grid.vertical.style': 'lineStyle',\n 'grid.vertical.width': 'width',\n\n 'crosshair.horizontal.visible': 'boolean',\n 'crosshair.horizontal.color': 'color',\n 'crosshair.horizontal.style': 'lineStyle',\n 'crosshair.horizontal.width': 'width',\n 'crosshair.vertical.visible': 'boolean',\n 'crosshair.vertical.color': 'color',\n 'crosshair.vertical.style': 'lineStyle',\n 'crosshair.vertical.width': 'width',\n 'crosshair.labelBackground': 'color',\n 'crosshair.labelTextColor': 'color',\n\n 'axis.price.lineColor': 'color',\n 'axis.price.textColor': 'color',\n 'axis.time.lineColor': 'color',\n 'axis.time.textColor': 'color',\n\n 'legend.textColor': 'color',\n 'legend.labelColor': 'color',\n\n 'watermark.color': 'color',\n\n 'lastPrice.visible': 'boolean',\n 'lastPrice.upColor': 'color',\n 'lastPrice.downColor': 'color',\n 'lastPrice.style': 'lineStyle',\n 'lastPrice.width': 'width',\n\n 'volume.upColor': 'color',\n 'volume.downColor': 'color',\n\n 'sessionBreaks.color': 'color',\n 'sessionBreaks.style': 'lineStyle',\n 'sessionBreaks.width': 'width',\n\n 'highLow.color': 'color',\n\n 'trading.buyColor': 'color',\n 'trading.sellColor': 'color',\n 'trading.profitColor': 'color',\n 'trading.lossColor': 'color',\n 'trading.entryColor': 'color',\n\n 'markers.longColor': 'color',\n 'markers.shortColor': 'color',\n 'markers.neutralColor': 'color',\n\n 'tradeZones.profitColor': 'color',\n 'tradeZones.lossColor': 'color',\n 'tradeZones.activeColor': 'color',\n\n 'drawings.handleColor': 'color',\n\n 'series.candlestick.upColor': 'color',\n 'series.candlestick.downColor': 'color',\n 'series.candlestick.wickUpColor': 'color',\n 'series.candlestick.wickDownColor': 'color',\n 'series.heikinAshi.upColor': 'color',\n 'series.heikinAshi.downColor': 'color',\n 'series.heikinAshi.wickUpColor': 'color',\n 'series.heikinAshi.wickDownColor': 'color',\n 'series.hollowCandle.upColor': 'color',\n 'series.hollowCandle.downColor': 'color',\n 'series.volumeCandles.upColor': 'color',\n 'series.volumeCandles.downColor': 'color',\n 'series.volumeCandles.wickUpColor': 'color',\n 'series.volumeCandles.wickDownColor': 'color',\n 'series.equivolume.upColor': 'color',\n 'series.equivolume.downColor': 'color',\n 'series.equivolume.wickUpColor': 'color',\n 'series.equivolume.wickDownColor': 'color',\n 'series.bar.upColor': 'color',\n 'series.bar.downColor': 'color',\n 'series.hiLo.upColor': 'color',\n 'series.hiLo.downColor': 'color',\n 'series.renko.upColor': 'color',\n 'series.renko.downColor': 'color',\n 'series.lineBreak.upColor': 'color',\n 'series.lineBreak.downColor': 'color',\n 'series.rangeBars.upColor': 'color',\n 'series.rangeBars.downColor': 'color',\n 'series.kagi.upColor': 'color',\n 'series.kagi.downColor': 'color',\n 'series.pointAndFigure.upColor': 'color',\n 'series.pointAndFigure.downColor': 'color',\n 'series.baseline.upColor': 'color',\n 'series.baseline.downColor': 'color',\n 'series.baseline.lineWidth': 'width',\n 'series.line.color': 'color',\n 'series.line.lineWidth': 'width',\n 'series.stepLine.color': 'color',\n 'series.stepLine.lineWidth': 'width',\n 'series.lineWithMarkers.color': 'color',\n 'series.lineWithMarkers.lineWidth': 'width',\n 'series.area.lineColor': 'color',\n 'series.area.lineWidth': 'width',\n 'series.area.topColor': 'color',\n 'series.area.bottomColor': 'color',\n 'series.hlcArea.lineColor': 'color',\n 'series.hlcArea.lineWidth': 'width',\n 'series.hlcArea.topColor': 'color',\n 'series.hlcArea.bottomColor': 'color',\n} as const satisfies Record<string, StyleValueKind>;\n\nexport type ChartStyleKey = keyof typeof CHART_STYLE_KEYS;\n\n/** The value a key of `kind` takes. */\nexport type StyleValue<K extends StyleValueKind> = K extends 'color'\n ? string\n : K extends 'lineStyle'\n ? LineStyle\n : K extends 'width'\n ? number\n : boolean;\n\n/** Style overrides, by key. */\nexport type ChartStyleOverrides = {\n [K in ChartStyleKey]?: StyleValue<(typeof CHART_STYLE_KEYS)[K]>;\n};\n\n/** A change to overrides: a value sets a key, `null` takes it away. */\nexport type ChartStyleOverridesPatch = {\n [K in ChartStyleKey]?: StyleValue<(typeof CHART_STYLE_KEYS)[K]> | null;\n};\n\n/**\n * Where an override goes. `'host'`: your app's, kept through theme changes\n * and never saved. `'user'`: what the person using the chart chose (the\n * widget's Settings write here), kept per theme and saved with `saveState`.\n */\nexport type StyleLayer = 'host' | 'user';\n\n/** One indicator pane's own look, over the panes' (`chart.setPaneStyle`). */\nexport interface PaneStyle {\n background?: string;\n separator?: string;\n}\n\n/** A line's look, as resolved. */\nexport interface ResolvedLineLook {\n visible: boolean;\n color: string;\n style: LineStyle;\n width: number;\n}\n\n/**\n * The chart's finer looks, worked out from the theme and its overrides once\n * per change. Renderers read it from `theme.style`; a theme without one (a\n * plugin's own) draws as before.\n */\nexport interface ResolvedChartStyle {\n panes: { background: string; separator: string; title: string };\n grid: { horizontal: ResolvedLineLook; vertical: ResolvedLineLook };\n crosshair: {\n horizontal: ResolvedLineLook;\n vertical: ResolvedLineLook;\n labelBackground: string;\n labelText: string;\n };\n axis: { price: { line: string; text: string }; time: { line: string; text: string } };\n legend: { text: string; label: string };\n /** Null: the watermark's own colour, or the theme's. */\n watermark: { color: string | null };\n lastPrice: { visible: boolean; up: string; down: string; style: LineStyle; width: number };\n /** Null: the session breaks' own settings, or the theme's. */\n sessionBreaks: { color: string | null; style: LineStyle | null; width: number | null };\n /** Null: the high and low lines' own colour. */\n highLow: { color: string | null };\n /** Orders (buy, sell) and positions (profit, loss, entry). Null: the trading config's colours. */\n trading: { buy: string | null; sell: string | null; profit: string | null; loss: string | null; entry: string | null };\n /** Signal markers. Null: their own style's colours. */\n markers: { long: string | null; short: string | null; neutral: string | null };\n /** Trade zones. Null: their own style's colours. */\n tradeZones: { profit: string | null; loss: string | null; active: string | null };\n /** A selected drawing's handles. Null: white. */\n drawings: { handle: string | null };\n /** The main series' line width, for the chart types drawn as a line. */\n series: { lineWidth: number };\n}\n","import type { Point, ViewportState } from './rendering.js';\nimport type { OHLCBar } from './ohlc.js';\n\nexport type DrawingToolType =\n | 'trendLine' | 'horizontalLine' | 'horizontalRay' | 'verticalLine' | 'ray' | 'extendedLine'\n | 'crossLine' | 'trendAngle' | 'infoLine'\n | 'parallelChannel' | 'regressionChannel'\n | 'fibRetracement' | 'fibExtension' | 'fibTimeZones' | 'fibChannel' | 'fibSpeedResistanceFan'\n | 'rectangle' | 'ellipse' | 'triangle' | 'circle'\n | 'pitchfork' | 'schiffPitchfork' | 'modifiedSchiffPitchfork' | 'elliottWave'\n | 'xabcdPattern' | 'abcdPattern' | 'headAndShoulders'\n | 'priceRange' | 'dateRange' | 'dateAndPriceRange' | 'measure'\n | 'text' | 'arrow' | 'priceLabel'\n | 'gannFan' | 'gannBox' | 'cyclicLines'\n | 'anchoredVWAP'\n | 'volumeProfileRange'\n | 'riskReward'\n | 'note' | 'callout' | 'flag' | 'arrowMark' | 'icon'\n | 'brush' | 'highlighter' | 'path' | 'polyline' | 'curve' | 'arc'\n | 'fibCircles' | 'fibSpiral' | 'fibArcs' | 'fibWedge' | 'pitchfan' | 'gannSquare'\n | 'elliottImpulse' | 'elliottCorrection' | 'elliottTriangle' | 'elliottDoubleCombo' | 'elliottTripleCombo'\n | 'threeDrives' | 'cypherPattern'\n | 'timeCycles' | 'sineLine'\n | 'forecast' | 'projection' | 'barsPattern';\n\nexport interface AnchorPoint {\n time: number;\n price: number;\n}\n\nexport interface DrawingStyle {\n color: string;\n lineWidth: number;\n lineStyle: 'solid' | 'dashed' | 'dotted';\n fillColor?: string;\n fillOpacity?: number;\n fontSize?: number;\n text?: string;\n}\n\nexport interface DrawingState {\n id: string;\n type: DrawingToolType;\n anchors: AnchorPoint[];\n style: DrawingStyle;\n visible: boolean;\n locked: boolean;\n /** The tool's own settings (levels, extend left/right…); see `DrawingDescriptor.options`. */\n options?: DrawingOptions;\n /** The group it belongs to: drawings selected, hidden and locked together. */\n group?: { id: string; name: string };\n meta?: Record<string, unknown>;\n}\n\n/** One level of a Fibonacci or Gann tool: a ratio, shown or not, in its own colour. */\nexport interface DrawingLevel {\n value: number;\n visible: boolean;\n /** CSS colour; the drawing's colour when left out. */\n color?: string;\n}\n\nexport type DrawingOptionValue = boolean | number | string | DrawingLevel[];\n\n/** A drawing's settings beyond the shared style, by key. Missing keys take the tool's defaults. */\nexport type DrawingOptions = Record<string, DrawingOptionValue>;\n\n/** A setting a drawing tool offers beyond the shared style, with its default. */\nexport type DrawingOptionDef =\n | { kind: 'boolean'; label: string; default: boolean }\n | { kind: 'number'; label: string; default: number; min?: number; max?: number; step?: number }\n | { kind: 'choice'; label: string; default: string; choices: readonly { value: string; label: string }[] }\n | { kind: 'levels'; label: string; default: readonly DrawingLevel[] }\n | { kind: 'text'; label: string; default: string };\n\nexport type DrawingOptionDefs = Readonly<Record<string, DrawingOptionDef>>;\n\nexport interface DrawingDescriptor {\n type: DrawingToolType;\n name: string;\n requiredAnchors: number;\n singleClick?: boolean;\n /** Settings the tool offers beyond the shared style; a settings dialog is built from them. */\n options?: DrawingOptionDefs;\n /** It fills an area with `style.fillColor` (a settings dialog offers the fill). */\n fill?: boolean;\n /** It draws `style.text` (a settings dialog offers the text and its size). */\n text?: boolean;\n /**\n * How it is drawn with the pointer. 'clicks' (the default): a click per\n * anchor up to `requiredAnchors`. 'freehand': press and drag, a point every\n * few pixels, release to finish. 'path': a click per point, at least\n * `requiredAnchors` and at most `maxAnchors`; a double-click, Enter or a\n * click on the last point finishes it.\n */\n creation?: 'clicks' | 'freehand' | 'path';\n /** Most points a 'path' takes (100 when left out). */\n maxAnchors?: number;\n}\n\nexport interface DrawingPlugin {\n descriptor: DrawingDescriptor;\n render(\n ctx: CanvasRenderingContext2D,\n state: DrawingState,\n viewport: ViewportState,\n selected: boolean,\n ): void;\n hitTest(\n point: Point,\n state: DrawingState,\n viewport: ViewportState,\n tolerance: number,\n ): boolean;\n hitTestAnchor(\n point: Point,\n state: DrawingState,\n viewport: ViewportState,\n tolerance: number,\n ): number;\n /**\n * The drawing after handle `index` (from `hitTestAnchor`) is dragged to\n * `anchor`. Without it, the handle is anchor `index` and moves there. A\n * tool with handles that are not anchors (a position's target, say)\n * changes its options instead.\n */\n moveHandle?(state: DrawingState, index: number, anchor: AnchorPoint): DrawingState;\n /**\n * The prices of the drawing's lines at `time` (what an alert on it\n * crosses), or null where it doesn't reach. Tools without it take no alerts.\n * With `viewport` the lines follow the chart as drawn: straight across bars\n * (not clock time, so a weekend gap doesn't bend them) and on a log scale\n * straight in log price.\n */\n priceAt?(state: DrawingState, time: number, viewport?: ViewportState): number[] | null;\n /**\n * For a tool drawn from the chart's bars (an anchored VWAP, a volume\n * profile): the chart hands it a getter for them when it is registered.\n */\n setDataGetter?(getter: () => readonly OHLCBar[]): void;\n}\n\nexport const DEFAULT_DRAWING_STYLE: DrawingStyle = {\n color: '#4c8dff',\n lineWidth: 1,\n lineStyle: 'solid',\n fillColor: 'rgba(76, 141, 255, 0.1)',\n fillOpacity: 0.1,\n fontSize: 12,\n};\n","export type OrderSide = 'buy' | 'sell';\nexport type OrderType = 'market' | 'limit' | 'stop' | 'stopLimit';\nexport type OrderStatus = 'pending' | 'filled' | 'cancelled' | 'rejected';\nexport type OrderLabel = 'LIMIT' | 'STOP' | 'SL' | 'TP' | 'STOP LIMIT';\n\n/** How long an order works: good till cancelled, or for the day. */\nexport type TimeInForce = 'gtc' | 'day';\n\nexport interface TradingOrder {\n id: string;\n side: OrderSide;\n type: OrderType;\n price: number;\n stopPrice?: number;\n quantity: number;\n label?: OrderLabel;\n draggable?: boolean;\n /** Stop-loss and take-profit the position gets when the order fills. */\n stopLoss?: number;\n takeProfit?: number;\n timeInForce?: TimeInForce;\n meta?: Record<string, unknown>;\n}\n\nexport interface TradingPosition {\n id: string;\n side: OrderSide;\n entryPrice: number;\n quantity: number;\n /** Quantity already closed (for partial-close visualization). 0 ≤ closedQuantity ≤ quantity. */\n closedQuantity?: number;\n stopLoss?: number;\n takeProfit?: number;\n meta?: Record<string, unknown>;\n}\n\n/** Threshold-based P&L color stop. Sorted ascending by `pnl` is recommended. */\nexport interface PnLThreshold {\n /** Inclusive lower bound. Use -Infinity for the bottom-most stop. */\n pnl: number;\n color: string;\n}\n\n/** Tokens passed to position label templates. */\nexport interface PositionLabelContext {\n side: OrderSide;\n quantity: number;\n closedQuantity: number;\n openQuantity: number;\n entryPrice: number;\n currentPrice: number;\n pnl: number;\n pnlPct: number;\n precision: number;\n /** The chart's price format, for `{entry}` and `{price}` (decimals at `precision` without one). */\n formatPrice?: (price: number) => string;\n}\n\nexport interface DepthLevel {\n price: number;\n volume: number;\n}\n\nexport interface DepthData {\n bids: DepthLevel[];\n asks: DepthLevel[];\n}\n\nexport interface TradingConfig {\n enabled: boolean;\n orderColors?: { buy?: string; sell?: string };\n positionColors?: { profit?: string; loss?: string; entry?: string };\n /**\n * Optional gradient of colors keyed to P&L value. When provided, the rendered\n * position zone uses the color of the highest threshold whose `pnl` ≤ live P&L.\n * Falls back to `positionColors.profit`/`.loss` when unset.\n */\n pnlThresholds?: PnLThreshold[];\n /**\n * Position P&L label template. Supports tokens: {side} {qty} {closedQty}\n * {openQty} {entry} {price} {pnl} {pnlPct} {pnlSign}. Pass a function for\n * full control. Default: `{side} {qty} | P&L: {pnlSign}{pnl}`.\n */\n positionLabel?: string | ((ctx: PositionLabelContext) => string);\n depthOverlay?: {\n enabled?: boolean;\n bidColor?: string;\n askColor?: string;\n maxWidth?: number;\n };\n contextMenu?: { enabled?: boolean };\n /**\n * Buttons on the lines: × on an order cancels it, × on a position closes it\n * and ⇅ reverses it, × on a stop-loss or take-profit removes it. All on by\n * default.\n */\n lineButtons?: { cancel?: boolean; close?: boolean; reverse?: boolean; removeStops?: boolean };\n /** Marks where orders filled (from the execution adapter's fills). On by default. */\n fillMarks?: boolean;\n pricePrecision?: number;\n dragThreshold?: number;\n}\n\nexport interface OrderPlaceIntent {\n side: OrderSide;\n type: OrderType;\n price: number;\n stopPrice?: number;\n quantity?: number;\n /** Stop-loss and take-profit for the position the order opens. */\n stopLoss?: number;\n takeProfit?: number;\n timeInForce?: TimeInForce;\n}\n\nexport interface OrderModifyIntent {\n orderId: string;\n newPrice: number;\n /** The price before the change, when known (e.g. from a drag). Optional so\n * the chart's `orderModify` event payload routes straight to an adapter. */\n previousPrice?: number;\n}\n\nexport interface OrderCancelIntent {\n orderId: string;\n}\n\nexport interface PositionModifyIntent {\n positionId: string;\n /** A new stop-loss; null removes it, left out keeps it. */\n stopLoss?: number | null;\n /** A new take-profit; null removes it, left out keeps it. */\n takeProfit?: number | null;\n}\n\nexport interface PositionCloseIntent {\n positionId: string;\n}\n\n/** Close a position and open the same size the other way. */\nexport interface PositionReverseIntent {\n positionId: string;\n}\n\nexport const DEFAULT_TRADING_CONFIG: TradingConfig = {\n enabled: true,\n orderColors: { buy: '#1fa874', sell: '#e8505b' },\n positionColors: { profit: '#1fa874', loss: '#e8505b', entry: '#4c8dff' },\n depthOverlay: { enabled: false, bidColor: 'rgba(31, 168, 116,0.15)', askColor: 'rgba(232, 80, 91,0.15)', maxWidth: 100 },\n contextMenu: { enabled: true },\n lineButtons: { cancel: true, close: true, reverse: true, removeStops: true },\n fillMarks: true,\n pricePrecision: 2,\n dragThreshold: 3,\n};\n","export type SignalDirection = 'long' | 'short' | 'neutral';\n\nexport interface SignalMarker {\n id: string;\n time: number;\n price: number;\n direction: SignalDirection;\n confidence: number;\n source: string;\n label?: string;\n color?: string;\n meta?: Record<string, unknown>;\n}\n\nexport interface SignalMarkerStyle {\n longColor?: string;\n shortColor?: string;\n neutralColor?: string;\n arrowSize?: number;\n showLabel?: boolean;\n showConfidence?: boolean;\n sourceColors?: Record<string, string>;\n}\n\nexport const DEFAULT_SIGNAL_STYLE: SignalMarkerStyle = {\n longColor: '#1fa874',\n shortColor: '#e8505b',\n neutralColor: '#9E9E9E',\n arrowSize: 12,\n showLabel: true,\n showConfidence: true,\n};\n\nexport type TradeZoneDirection = 'long' | 'short';\n\nexport interface TradeZone {\n id: string;\n entryTime: number;\n entryPrice: number;\n exitTime?: number;\n exitPrice?: number;\n direction: TradeZoneDirection;\n pnl?: number;\n pnlPercent?: number;\n label?: string;\n meta?: Record<string, unknown>;\n}\n\nexport interface TradeZoneStyle {\n profitColor?: string;\n lossColor?: string;\n activeColor?: string;\n fillOpacity?: number;\n borderWidth?: number;\n showLabel?: boolean;\n showPnl?: boolean;\n}\n\nexport const DEFAULT_TRADE_ZONE_STYLE: TradeZoneStyle = {\n profitColor: '#1fa874',\n lossColor: '#e8505b',\n activeColor: '#4c8dff',\n fillOpacity: 0.12,\n borderWidth: 1,\n showLabel: true,\n showPnl: true,\n};\n","import type { OHLCBar, TimeFrame } from './ohlc.js';\nimport type { SymbolInfo, SymbolSearchOptions } from './symbol.js';\nimport type { Quote } from './quote.js';\nimport type { NewsItem } from './news.js';\n\n/** One trade: when (ms), at what price, how much. */\nexport interface Trade {\n time: number;\n price: number;\n volume: number;\n}\n\n// --- Connection ---\n\nexport type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';\n\nexport interface ConnectionInfo {\n state: ConnectionState;\n latency?: number;\n reconnectAttempt?: number;\n lastMessageTime?: number;\n error?: string;\n}\n\n// --- Ticks & Trades ---\n\nexport interface RawTick {\n time: number;\n price: number;\n volume: number;\n side?: 'buy' | 'sell';\n /** The best bid and ask with it, when the feed has them (the chart marks them). */\n bid?: number;\n ask?: number;\n}\n\nexport interface AggregatedBar extends OHLCBar {\n closed: boolean; // true when bar is finalized\n tickCount: number; // number of ticks in this bar\n}\n\n// --- Data Adapter (Strategy Pattern) ---\n\nexport interface DataAdapterConfig {\n symbol: string;\n timeframe: TimeFrame;\n reconnect?: boolean; // default: true\n reconnectMaxRetries?: number; // default: Infinity\n reconnectBaseDelay?: number; // ms, default: 1000\n reconnectMaxDelay?: number; // ms, default: 30000\n heartbeatInterval?: number; // ms, default: 30000\n bufferSize?: number; // max ticks to buffer, default: 1000\n}\n\nexport type DataAdapterEventType =\n | 'tick'\n | 'bar'\n | 'barClose'\n | 'snapshot' // initial historical data loaded\n | 'connectionChange'\n | 'error'\n /**\n * The feed learned about the connected symbol after the chart asked\n * (`resolveSymbol`), for instance on its first message: `data` is the\n * `SymbolInfo`. The chart applies it (precision, price step, hours).\n */\n | 'symbolInfo';\n\nexport interface DataAdapterEvent<T = unknown> {\n type: DataAdapterEventType;\n data: T;\n timestamp: number;\n}\n\nexport type DataAdapterListener<T = unknown> = (event: DataAdapterEvent<T>) => void;\n\n/**\n * Data adapter interface. Implements the observer pattern:\n * - connect() to start receiving data\n * - on('bar'|'tick'|'connectionChange', handler) to receive events\n * - disconnect() to stop, then connect() again to switch symbols/timeframes\n * - No separate subscribe/unsubscribe — reconnect is the intended pattern\n *\n * Strategy pattern for pluggable data sources.\n * Implementations handle the specifics of each data source (WebSocket, REST,\n * SSE, etc.) while the StreamManager orchestrates lifecycle and aggregation.\n *\n * Built-in: BinanceAdapter\n * Implement this for: custom exchange APIs, broker feeds, mock data\n */\nexport interface DataAdapter {\n readonly name: string;\n\n /**\n * Optional: the timeframes the feed serves. Charts build any other\n * timeframe (7m, 90m, 2d…) from the coarsest of these that divides it.\n * Leave it out when the feed serves every timeframe.\n */\n readonly supportedTimeframes?: readonly TimeFrame[];\n\n connect(config: DataAdapterConfig): void;\n disconnect(): void;\n getConnectionState(): ConnectionState;\n\n /**\n * Load historical bars. Called once on connect, before streaming starts.\n * Returns bars sorted by time ascending.\n */\n fetchHistory(symbol: string, timeframe: TimeFrame, limit?: number): Promise<OHLCBar[]>;\n\n /**\n * Optional: load up to `limit` bars older than `before` (the oldest loaded\n * bar's time, in the bars' own unit), sorted by time ascending. Return an\n * empty array when no older data exists. A chart connected to an adapter\n * with this method loads older bars as the user scrolls back in time.\n */\n fetchHistoryBefore?(symbol: string, timeframe: TimeFrame, before: number, limit: number): Promise<OHLCBar[]>;\n\n /** Optional: symbols matching `query`, best first — for a symbol search box. */\n searchSymbols?(query: string, options?: SymbolSearchOptions): Promise<SymbolInfo[]>;\n\n /**\n * Optional: what the feed knows about `symbol` (name, price step, exchange\n * timezone and hours), or null when it doesn't know it. A connected chart\n * asks for it and applies it.\n */\n resolveSymbol?(symbol: string): Promise<SymbolInfo | null>;\n\n /**\n * Optional: live quotes (last price, the day's change, high, low, volume)\n * for many symbols at once, until the function it returns is called. A\n * widget's watchlist fills its rows from it.\n */\n subscribeQuotes?(symbols: readonly string[], onQuotes: (quotes: Quote[]) => void): () => void;\n\n /** Optional: recent headlines about `symbol`, newest first. */\n fetchNews?(symbol: string, limit?: number): Promise<NewsItem[]>;\n\n /**\n * Optional, with `subscribeTrades`: up to `limit` recent trades, oldest\n * first. A chart builds tick timeframes (`'100T'`) from a feed's trades.\n */\n fetchTrades?(symbol: string, limit?: number): Promise<Trade[]>;\n\n /** Optional, with `fetchTrades`: live trades of `symbol` until the function it returns is called. */\n subscribeTrades?(symbol: string, onTrades: (trades: Trade[]) => void): () => void;\n\n on<T = unknown>(event: DataAdapterEventType, listener: DataAdapterListener<T>): void;\n off<T = unknown>(event: DataAdapterEventType, listener: DataAdapterListener<T>): void;\n\n dispose(): void;\n}\n\n// --- Stream Manager Config ---\n\nexport interface StreamConfig {\n adapter: DataAdapter;\n symbol: string;\n timeframe: TimeFrame;\n historyLimit?: number; // bars to load initially, default: 500\n /** Bars per request when scrolling back for older bars (needs `adapter.fetchHistoryBefore`), default: 500. */\n historyPageSize?: number;\n autoScroll?: boolean; // scroll to end on new bar, default: true\n showCurrentPriceLine?: boolean; // default: true\n aggregateTicks?: boolean; // build bars from ticks, default: false\n reconnect?: ReconnectConfig;\n}\n\nexport interface ReconnectConfig {\n enabled: boolean; // default: true\n maxRetries: number; // default: Infinity\n baseDelay: number; // ms, default: 1000\n maxDelay: number; // ms, default: 30000\n backoffMultiplier: number; // default: 2\n}\n\nexport const DEFAULT_RECONNECT: ReconnectConfig = {\n enabled: true,\n maxRetries: Infinity,\n baseDelay: 1000,\n maxDelay: 30000,\n backoffMultiplier: 2,\n};\n\nexport const DEFAULT_STREAM_CONFIG: Partial<StreamConfig> = {\n historyLimit: 500,\n autoScroll: true,\n showCurrentPriceLine: true,\n aggregateTicks: false,\n};\n","/**\n * A symbol's latest quote: its price and the day's move, for a watchlist row\n * or a symbol's details. Prices are in the symbol's quote currency.\n */\nexport interface Quote {\n symbol: string;\n /** The last traded price. */\n last: number;\n /** The move from the reference price (the prior close; 24 h ago for crypto). */\n change?: number;\n /** The same move, in percent. */\n changePercent?: number;\n open?: number;\n high?: number;\n low?: number;\n /** The prior session's close. */\n prevClose?: number;\n /** Volume over the same period, in the base unit. */\n volume?: number;\n bid?: number;\n ask?: number;\n /** When the quote was made, in ms since the epoch. */\n time?: number;\n}\n\n/** Live quotes for many symbols at once. */\nexport interface QuoteSource {\n /**\n * Send quotes for `symbols` to `onQuotes` as they change (any number per\n * call) until the function it returns is called.\n */\n subscribeQuotes(symbols: readonly string[], onQuotes: (quotes: Quote[]) => void): () => void;\n}\n\nconst QUOTE_NUMBERS = ['change', 'changePercent', 'open', 'high', 'low', 'prevClose', 'volume', 'bid', 'ask', 'time'] as const;\n\n/**\n * A quote from untrusted input (a host's push, a feed's frame): its symbol\n * and last price must be usable; any other field that isn't a finite number\n * is left out. The change is worked out from the prior close or the open when\n * the input has none. Null when it isn't a quote.\n */\nexport function readQuote(raw: unknown): Quote | null {\n if (!raw || typeof raw !== 'object') return null;\n const r = raw as Record<string, unknown>;\n const symbol = typeof r.symbol === 'string' ? r.symbol.trim() : '';\n if (!symbol || symbol.length > 64 || !isFiniteNumber(r.last)) return null;\n const quote: Quote = { symbol, last: r.last };\n for (const key of QUOTE_NUMBERS) {\n const v = r[key];\n if (isFiniteNumber(v)) quote[key] = v;\n }\n const ref = quote.prevClose ?? quote.open;\n if (quote.change === undefined && ref !== undefined) quote.change = quote.last - ref;\n if (quote.changePercent === undefined && quote.change !== undefined) {\n const base = quote.last - quote.change;\n if (base !== 0) quote.changePercent = (quote.change / base) * 100;\n }\n return quote;\n}\n\nfunction isFiniteNumber(v: unknown): v is number {\n return typeof v === 'number' && Number.isFinite(v);\n}\n","/** A news headline about a symbol. */\nexport interface NewsItem {\n title: string;\n /** When it was published, in ms since the epoch. */\n time: number;\n /** The article; only http(s) links are kept. */\n url?: string;\n source?: string;\n summary?: string;\n id?: string;\n}\n\nconst MAX_TITLE = 300;\nconst MAX_TEXT = 1000;\n\nconst text = (v: unknown, max: number): string | undefined => {\n if (typeof v !== 'string') return undefined;\n const t = v.trim();\n return t ? t.slice(0, max) : undefined;\n};\n\nconst webUrl = (v: unknown): string | undefined => {\n if (typeof v !== 'string') return undefined;\n try {\n const url = new URL(v);\n return url.protocol === 'https:' || url.protocol === 'http:' ? url.href : undefined;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Headlines from untrusted input (a feed, a host): each needs a title and a\n * time; links that aren't web pages are dropped. Newest first, at most\n * `limit`.\n */\nexport function readNews(raw: unknown, limit = 20): NewsItem[] {\n if (!Array.isArray(raw)) return [];\n const items: NewsItem[] = [];\n for (const entry of raw) {\n if (!entry || typeof entry !== 'object') continue;\n const r = entry as Record<string, unknown>;\n const title = text(r.title, MAX_TITLE);\n if (!title || typeof r.time !== 'number' || !Number.isFinite(r.time)) continue;\n const item: NewsItem = { title, time: r.time };\n const url = webUrl(r.url);\n if (url) item.url = url;\n const source = text(r.source, MAX_TITLE);\n if (source) item.source = source;\n const summary = text(r.summary, MAX_TEXT);\n if (summary) item.summary = summary;\n const id = text(r.id, MAX_TITLE);\n if (id) item.id = id;\n items.push(item);\n }\n return items.sort((a, b) => b.time - a.time).slice(0, Math.max(0, limit));\n}\n","export function clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n}\n\nexport function lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\nexport function inverseLerp(a: number, b: number, value: number): number {\n if (a === b) return 0;\n return (value - a) / (b - a);\n}\n\nexport function roundToStep(value: number, step: number): number {\n return Math.round(value / step) * step;\n}\n\nexport function niceNumber(value: number, round: boolean): number {\n const exp = Math.floor(Math.log10(value));\n const frac = value / Math.pow(10, exp);\n let nice: number;\n if (round) {\n if (frac < 1.5) nice = 1;\n else if (frac < 3) nice = 2;\n else if (frac < 7) nice = 5;\n else nice = 10;\n } else {\n if (frac <= 1) nice = 1;\n else if (frac <= 2) nice = 2;\n else if (frac <= 5) nice = 5;\n else nice = 10;\n }\n return nice * Math.pow(10, exp);\n}\n\nexport function computeTickStep(min: number, max: number, maxTicks: number): number {\n const range = niceNumber(max - min, false);\n return niceNumber(range / (maxTicks - 1), true);\n}\n","import type { OHLCBar, DataSeries } from '../types/ohlc.js';\n\n/**\n * Bar times at or above this are milliseconds (from March 1973 on), below it\n * seconds (up to the year 5138).\n */\nexport const MS_TIME_THRESHOLD = 1e11;\n\n/** Milliseconds in one unit of a series' bar times: 1 for milliseconds, 1000 for seconds. */\nexport type BarTimeUnit = 1 | 1000;\n\n/**\n * The unit of a series' bar times, decided once from its newest bar, so its\n * old bars keep the series' unit: a millisecond bar from 2000 is below 1e12\n * and would pass for seconds on its own. Milliseconds when there are no bars.\n */\nexport function barTimeUnit(data: ArrayLike<{ time: number }>): BarTimeUnit {\n const last = data.length > 0 ? data[data.length - 1].time : NaN;\n if (!Number.isFinite(last)) return 1;\n return Math.abs(last) >= MS_TIME_THRESHOLD ? 1 : 1000;\n}\n\n/**\n * Normalize bar timestamp to milliseconds. Pass the series' unit\n * (`barTimeUnit(data)`) when there is one; on its own a time is taken as\n * milliseconds from `MS_TIME_THRESHOLD` (1973) up, seconds below.\n */\nexport function normalizeBarTime(time: number, unit?: BarTimeUnit): number {\n if (unit !== undefined) return time * unit;\n return Math.abs(time) >= MS_TIME_THRESHOLD ? time : time * 1000;\n}\n\n/**\n * Normalize a bar's timestamp field to milliseconds.\n * Accepts either { time } (ms or s) or { t, o, h, l, c, v } wire format.\n */\nexport function normalizeBar(raw: Record<string, number>): OHLCBar {\n const time = normalizeBarTime(raw.time ?? raw.t ?? 0);\n return {\n time,\n open: raw.open ?? raw.o ?? 0,\n high: raw.high ?? raw.h ?? 0,\n low: raw.low ?? raw.l ?? 0,\n close: raw.close ?? raw.c ?? 0,\n volume: raw.volume ?? raw.v ?? 0,\n };\n}\n\nexport function sliceVisibleData(\n data: DataSeries,\n from: number,\n to: number,\n): DataSeries {\n const startIdx = Math.max(0, from);\n const endIdx = Math.min(data.length, to + 1);\n return data.slice(startIdx, endIdx);\n}\n\nexport function findBarIndex(data: DataSeries, timestamp: number): number {\n let lo = 0;\n let hi = data.length - 1;\n while (lo <= hi) {\n const mid = (lo + hi) >>> 1;\n if (data[mid].time < timestamp) lo = mid + 1;\n else if (data[mid].time > timestamp) hi = mid - 1;\n else return mid;\n }\n return lo;\n}\n\n/**\n * `min`..`max` with room above and below: `padding` of the span on each side,\n * or on a log scale the same share of its log span, so the range stays above\n * zero and the room looks the same top and bottom.\n */\nexport function padPriceRange(min: number, max: number, padding: number, log = false): { min: number; max: number } {\n const logSpan = log && min > 0 && max > 0 ? Math.log(max / min) : Number.NaN;\n if (Number.isFinite(logSpan)) {\n const span = logSpan || Math.log(1 + 2 * padding);\n const factor = Math.exp(span * padding);\n return { min: min / factor, max: max * factor };\n }\n const range = max - min || 1;\n return { min: min - range * padding, max: max + range * padding };\n}\n\nexport function computePriceRange(\n data: DataSeries,\n from: number,\n to: number,\n padding = 0.05,\n /** Pad as a log scale shows it (`padPriceRange`). */\n log = false,\n): { min: number; max: number } {\n if (data.length === 0) return { min: 0, max: 1 };\n const startIdx = Math.max(0, from);\n const endIdx = Math.min(data.length - 1, to);\n let min = Infinity;\n let max = -Infinity;\n for (let i = startIdx; i <= endIdx; i++) {\n if (data[i].low < min) min = data[i].low;\n if (data[i].high > max) max = data[i].high;\n }\n if (min === Infinity) return { min: 0, max: 1 };\n return padPriceRange(min, max, padding, log);\n}\n\nexport function mergeBar(existing: OHLCBar, tick: { price: number; volume?: number; time: number }): OHLCBar {\n return {\n ...existing,\n high: Math.max(existing.high, tick.price),\n low: Math.min(existing.low, tick.price),\n close: tick.price,\n volume: existing.volume + (tick.volume ?? 0),\n time: tick.time,\n };\n}\n","export function hexToRgba(hex: string, alpha = 1): string {\n // #rgb and #rgba are short for #rrggbb and #rrggbbaa; a hex alpha gives way to `alpha`.\n let h = hex.slice(1);\n if (h.length === 3 || h.length === 4) h = [...h.slice(0, 3)].map((c) => c + c).join('');\n const r = parseInt(h.slice(0, 2), 16);\n const g = parseInt(h.slice(2, 4), 16);\n const b = parseInt(h.slice(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n\n/**\n * `color` at `alpha`: hex (#rgb, #rgba, #rrggbb, #rrggbbaa) and rgb()/rgba()\n * with commas or spaces. Any other colour (a name, hsl()) comes back as it is.\n */\nexport function withAlpha(color: string, alpha: number): string {\n const c = color.trim();\n if (/^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(c)) {\n return hexToRgba(c, alpha);\n }\n const rgb = c.match(/^rgba?\\(\\s*(\\d+(?:\\.\\d+)?)[\\s,]+(\\d+(?:\\.\\d+)?)[\\s,]+(\\d+(?:\\.\\d+)?)/i);\n if (rgb) {\n return `rgba(${rgb[1]}, ${rgb[2]}, ${rgb[3]}, ${alpha})`;\n }\n return color;\n}\n\nexport function lerpColor(colorA: string, colorB: string, t: number): string {\n const parseHex = (hex: string) => {\n hex = hex.replace('#', '');\n if (hex.length === 3) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n return {\n r: parseInt(hex.slice(0, 2), 16),\n g: parseInt(hex.slice(2, 4), 16),\n b: parseInt(hex.slice(4, 6), 16),\n };\n };\n const a = parseHex(colorA);\n const b = parseHex(colorB);\n const r = Math.round(a.r + (b.r - a.r) * t);\n const g = Math.round(a.g + (b.g - a.g) * t);\n const bl = Math.round(a.b + (b.b - a.b) * t);\n return `rgb(${r},${g},${bl})`;\n}\n","/**\n * The display timezone: an IANA name (`'America/New_York'`, `'UTC'`), a fixed\n * offset in minutes east of UTC (`-300`), or null for the browser's own zone.\n */\nexport type TimeZoneSetting = string | number | null;\n\nconst MINUTE_MS = 60_000;\nconst DAY_MS = 24 * 60 * MINUTE_MS;\n/** Zones change their offset on quarter hours, so it holds for each 15 minutes. */\nconst OFFSET_BUCKET_MS = 15 * MINUTE_MS;\nconst OFFSET_CACHE_MAX = 8192;\n/**\n * Clocks change months apart, so a week whose offset is the same at both\n * ends holds it throughout: one lookup serves a week of bars.\n */\nconst WEEK_MS = 7 * DAY_MS;\nconst WEEK_CACHE_MAX = 4096;\n/** Names of UTC itself: no lookup needed. */\nconst UTC_ZONES = new Set(['UTC', 'Etc/UTC', 'GMT', 'Etc/GMT', 'UCT', 'Etc/UCT', 'Universal', 'Etc/Universal', 'Zulu', 'Etc/Zulu']);\n\nconst zoneFormats = new Map<string, Intl.DateTimeFormat>();\nconst offsetCache = new Map<string, number>();\n/** A week's offsets: `before` until the clocks change at `change`, then `after`. */\ninterface WeekOffsets {\n before: number;\n change: number;\n after: number;\n}\n/** Per zone, the offsets of each week looked at. */\nconst weekOffsets = new Map<string, Map<number, WeekOffsets>>();\n\nfunction zoneFormat(zone: string): Intl.DateTimeFormat {\n let format = zoneFormats.get(zone);\n if (!format) {\n format = new Intl.DateTimeFormat('en-US', {\n timeZone: zone,\n hourCycle: 'h23',\n year: 'numeric',\n month: 'numeric',\n day: 'numeric',\n hour: 'numeric',\n minute: 'numeric',\n second: 'numeric',\n });\n zoneFormats.set(zone, format);\n }\n return format;\n}\n\n/** Whether `zone` is a time zone this browser knows (an IANA name such as `'Europe/London'`). */\nexport function isValidTimeZone(zone: string): boolean {\n if (!zone) return false;\n try {\n zoneFormat(zone);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * The zone's offset from UTC, in minutes east, at `timeMs` — daylight saving\n * time included. Throws a RangeError for a zone the browser doesn't know.\n */\nexport function zoneOffsetMinutes(zone: string, timeMs: number): number {\n if (!Number.isFinite(timeMs) || UTC_ZONES.has(zone)) return 0;\n let weeks = weekOffsets.get(zone);\n if (!weeks) {\n weeks = new Map();\n weekOffsets.set(zone, weeks);\n }\n const week = Math.floor(timeMs / WEEK_MS);\n let offsets = weeks.get(week);\n if (!offsets) {\n offsets = readWeek(zone, week * WEEK_MS);\n if (weeks.size >= WEEK_CACHE_MAX) weeks.clear();\n weeks.set(week, offsets);\n }\n return timeMs < offsets.change ? offsets.before : offsets.after;\n}\n\n/** The offsets of the week from `start`, and the quarter hour the clocks change in it, if they do. */\nfunction readWeek(zone: string, start: number): WeekOffsets {\n const before = bucketOffset(zone, start);\n let last = start + WEEK_MS - OFFSET_BUCKET_MS;\n const after = bucketOffset(zone, last);\n if (after === before) return { before, change: Infinity, after };\n // Halve the week down to the first quarter hour on the new offset.\n let first = start;\n while (last - first > OFFSET_BUCKET_MS) {\n const mid = first + Math.floor((last - first) / OFFSET_BUCKET_MS / 2) * OFFSET_BUCKET_MS;\n if (bucketOffset(zone, mid) === before) first = mid;\n else last = mid;\n }\n return { before, change: last, after };\n}\n\n/** The zone's offset over the quarter hour holding `timeMs`. */\nfunction bucketOffset(zone: string, timeMs: number): number {\n const bucket = Math.floor(timeMs / OFFSET_BUCKET_MS);\n const key = `${zone}|${bucket}`;\n const cached = offsetCache.get(key);\n if (cached !== undefined) return cached;\n\n const at = bucket * OFFSET_BUCKET_MS;\n let year = 1970, month = 1, day = 1, hour = 0, minute = 0, second = 0;\n for (const part of zoneFormat(zone).formatToParts(new Date(at))) {\n const value = Number(part.value);\n switch (part.type) {\n case 'year': year = value; break;\n case 'month': month = value; break;\n case 'day': day = value; break;\n case 'hour': hour = value % 24; break;\n case 'minute': minute = value; break;\n case 'second': second = value; break;\n }\n }\n const offset = Math.round((Date.UTC(year, month - 1, day, hour, minute, second) - at) / MINUTE_MS);\n if (offsetCache.size >= OFFSET_CACHE_MAX) offsetCache.clear();\n offsetCache.set(key, offset);\n return offset;\n}\n\n/** Minutes east of UTC at `timeMs` for any display timezone. */\nexport function offsetAt(tz: TimeZoneSetting, timeMs: number): number {\n if (tz === null) return -new Date(timeMs).getTimezoneOffset();\n if (typeof tz === 'number') return tz;\n return zoneOffsetMinutes(tz, timeMs);\n}\n\n/**\n * The instant whose wall clock in `tz` reads `wallMs` (a wall-clock time\n * written as if it were UTC, e.g. `Date.UTC(2026, 0, 15, 9, 30)`). A time\n * that occurs twice (clocks going back) is its first occurrence; a time a\n * spring-forward skips moves forward by the jump (02:30 → 03:30).\n */\nexport function wallToUtc(wallMs: number, tz: TimeZoneSetting): number {\n // The instant is within 14 hours of `wallMs`, so the offsets a day either\n // side bracket any clock change near it.\n const early = wallMs - offsetAt(tz, wallMs - DAY_MS) * MINUTE_MS;\n if (early + offsetAt(tz, early) * MINUTE_MS === wallMs) return early;\n const late = wallMs - offsetAt(tz, wallMs + DAY_MS) * MINUTE_MS;\n if (late + offsetAt(tz, late) * MINUTE_MS === wallMs) return late;\n return early; // skipped: read with the offset from before the jump\n}\n\nconst dateFormatters = new Map<string, (ms: number) => string>();\n\n/**\n * `Intl.DateTimeFormat#format` for dates in a display timezone, cached per\n * locale, options and zone (building a format costs ~40µs). A fixed offset\n * is applied by shifting the instant and formatting it as UTC.\n */\nexport function zonedDateFormatter(\n locale: string,\n options: Intl.DateTimeFormatOptions,\n tz: TimeZoneSetting,\n): (ms: number) => string {\n const key = `${locale}|${JSON.stringify(options)}|${tz === null ? 'local' : typeof tz === 'number' ? `fixed:${tz}` : tz}`;\n let formatter = dateFormatters.get(key);\n if (formatter) return formatter;\n\n const timeZone = tz === null ? undefined : typeof tz === 'number' ? 'UTC' : tz;\n let format: Intl.DateTimeFormat;\n try {\n format = new Intl.DateTimeFormat(locale, { ...options, timeZone });\n } catch {\n format = new Intl.DateTimeFormat('en-US', { ...options, timeZone }); // unknown locale tag\n }\n const shift = typeof tz === 'number' ? tz * MINUTE_MS : 0;\n formatter = (ms) => format.format(new Date(ms + shift));\n dateFormatters.set(key, formatter);\n return formatter;\n}\n","import type { KnownTimeFrame, TimeFrame, TimeFrameUnit } from '../types/ohlc.js';\nimport { offsetAt, type TimeZoneSetting } from './timezone.js';\nimport { barTimeUnit } from './data.js';\n\nconst TIMEFRAME_MS: Record<KnownTimeFrame, number> = {\n '1s': 1_000,\n '5s': 5_000,\n '15s': 15_000,\n '30s': 30_000,\n '1m': 60_000,\n '3m': 180_000,\n '5m': 300_000,\n '15m': 900_000,\n '30m': 1_800_000,\n '45m': 2_700_000,\n '1h': 3_600_000,\n '2h': 7_200_000,\n '3h': 10_800_000,\n '4h': 14_400_000,\n '6h': 21_600_000,\n '8h': 28_800_000,\n '12h': 43_200_000,\n '1d': 86_400_000,\n '2d': 172_800_000,\n '3d': 259_200_000,\n '1w': 604_800_000,\n '2w': 1_209_600_000,\n '1M': 2_592_000_000,\n '3M': 7_776_000_000,\n '6M': 15_552_000_000,\n '12M': 31_536_000_000,\n};\n\n/** Milliseconds per unit; a month counts 30 days (a 12-month frame, 365). */\nconst UNIT_MS: Record<TimeFrameUnit, number> = {\n s: 1_000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n M: 2_592_000_000,\n};\n\nconst TIMEFRAME_PATTERN = /^([1-9]\\d*)([smhdwM])$/;\n\n/** A timeframe's count and unit, or null when `value` isn't one (`'7m'` → 7 minutes). */\nexport function parseTimeframe(value: string): { count: number; unit: TimeFrameUnit } | null {\n const match = TIMEFRAME_PATTERN.exec(value);\n if (!match) return null;\n const count = Number(match[1]);\n return Number.isSafeInteger(count) ? { count, unit: match[2] as TimeFrameUnit } : null;\n}\n\n/** Most trades a tick bar may hold. */\nconst MAX_TICKS_PER_BAR = 1_000_000;\n\n/** The trades per bar of a tick timeframe (`'100T'` → 100), or null for any other. */\nexport function tickBarCount(timeframe: string): number | null {\n const match = /^([1-9]\\d*)T$/.exec(timeframe);\n if (!match) return null;\n const count = Number(match[1]);\n return Number.isSafeInteger(count) && count <= MAX_TICKS_PER_BAR ? count : null;\n}\n\nexport function isTimeFrame(value: string): value is TimeFrame {\n return parseTimeframe(value) !== null;\n}\n\n/** Length of a timeframe in milliseconds (approximate for months); NaN for a non-timeframe. */\nexport function timeframeToMs(tf: TimeFrame): number {\n const known = TIMEFRAME_MS[tf as KnownTimeFrame];\n if (known !== undefined) return known;\n const parsed = parseTimeframe(tf);\n return parsed ? parsed.count * UNIT_MS[parsed.unit] : Number.NaN;\n}\n\nexport function formatTimestamp(timestamp: number, tf: TimeFrame): string {\n const d = new Date(timestamp);\n const ms = timeframeToMs(tf);\n if (ms >= 86_400_000) {\n return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n }\n if (ms >= 3_600_000) {\n return d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });\n }\n return d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' });\n}\n\nexport function alignToTimeframe(timestamp: number, tf: TimeFrame): number {\n const ms = timeframeToMs(tf);\n return Math.floor(timestamp / ms) * ms;\n}\n\nexport interface TimeParts {\n year: number;\n month: number; // 1-12\n day: number;\n hours: number;\n minutes: number;\n}\n\n/**\n * Calendar parts of a timestamp in a display timezone: an IANA zone\n * (`'America/New_York'`, daylight saving time included), a fixed UTC offset in\n * minutes (e.g. -300 for EST) or the browser's zone (`null`).\n */\nexport function timeParts(timeMs: number, tz: TimeZoneSetting): TimeParts {\n if (tz === null) {\n const d = new Date(timeMs);\n return {\n year: d.getFullYear(),\n month: d.getMonth() + 1,\n day: d.getDate(),\n hours: d.getHours(),\n minutes: d.getMinutes(),\n };\n }\n const d = new Date(timeMs + offsetAt(tz, timeMs) * 60_000);\n return {\n year: d.getUTCFullYear(),\n month: d.getUTCMonth() + 1,\n day: d.getUTCDate(),\n hours: d.getUTCHours(),\n minutes: d.getUTCMinutes(),\n };\n}\n\n/**\n * A bar with no time-of-day component (a daily, weekly, monthly or yearly candle — always anchored\n * to a calendar-day boundary, so hours/minutes read 00:00) needs the YEAR in its label instead: a\n * bare `month/day` is ambiguous across years, and `00:00` tells a trader nothing. Detected from the\n * parts themselves rather than a separate timeframe parameter, so every caller that formats a bar's\n * time gets the same rule for free. It can't tell an hourly bar at midnight from a daily one:\n * callers that have the series ask `barsAreDaily` first and fall back to this.\n */\nexport function isDateOnly(parts: Pick<TimeParts, 'hours' | 'minutes'>): boolean {\n return parts.hours === 0 && parts.minutes === 0;\n}\n\n/** Gaps this long or longer (a day with a daylight-saving change is 23 h) mean daily-or-larger bars. */\nconst DAILY_GAP_MS = 20 * 3_600_000;\n\n/**\n * Whether bars are a day apart or more (daily, weekly, monthly): their labels\n * then carry a date and no time of day, while an hourly bar at midnight keeps\n * its time. Read from the smallest gap among the first bars, so a weekend or\n * a missing session doesn't make an intraday series look daily. Undefined for\n * fewer than two bars.\n */\nexport function barsAreDaily(data: ArrayLike<{ time: number }>): boolean | undefined {\n const n = Math.min(data.length, 64);\n if (n < 2) return undefined;\n const unit = barTimeUnit(data);\n let gap = Infinity;\n for (let i = 1; i < n; i++) {\n const d = Math.abs(data[i].time - data[i - 1].time) * unit;\n if (d > 0 && d < gap) gap = d;\n }\n return gap === Infinity ? undefined : gap >= DAILY_GAP_MS;\n}\n\n/** A short label like `UTC-5` or `UTC+5:30` for the offset in force at `atMs` (default: now). */\nexport function tzLabel(tz: TimeZoneSetting, atMs: number = Date.now()): string {\n const min = offsetAt(tz, atMs);\n const sign = min >= 0 ? '+' : '-';\n const abs = Math.abs(min);\n const h = Math.floor(abs / 60);\n const m = abs % 60;\n return m === 0 ? `UTC${sign}${h}` : `UTC${sign}${h}:${String(m).padStart(2, '0')}`;\n}\n\n/** Day-of-week for a Monday-anchored reference week (1970-01-05 was a Monday, UTC). */\nconst WEEK_ANCHOR_MS = Date.UTC(1970, 0, 5);\n\n/**\n * Start of the bucket a timestamp falls into for a given timeframe.\n *\n * Intraday and daily frames are anchored to the Unix epoch (UTC) via fixed-ms\n * flooring. Weekly frames are anchored to Monday (or `weekStartsOn`), monthly\n * and yearly frames use calendar boundaries (1st of month, Jan for years) so\n * 3M aligns to quarters and 12M to calendar years.\n */\nexport function timeframeBucketStart(\n timestamp: number,\n tf: TimeFrame,\n weekStartsOn: 0 | 1 = 1,\n): number {\n const { count, unit } = parseTimeframe(tf) ?? { count: 1, unit: 'm' as const };\n\n if (unit === 's' || unit === 'm' || unit === 'h' || unit === 'd') {\n const ms = count * UNIT_MS[unit];\n return Math.floor(timestamp / ms) * ms;\n }\n\n const d = new Date(timestamp);\n\n if (unit === 'w') {\n const dayMs = UNIT_MS.d;\n const dow = d.getUTCDay();\n const shift = (dow - weekStartsOn + 7) % 7;\n const weekStart = Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() - shift);\n if (count <= 1) return weekStart;\n const weekIndex = Math.floor((weekStart - WEEK_ANCHOR_MS) / (7 * dayMs));\n const grouped = Math.floor(weekIndex / count) * count;\n return WEEK_ANCHOR_MS + grouped * 7 * dayMs;\n }\n\n // months / quarters / years\n const totalMonths = d.getUTCFullYear() * 12 + d.getUTCMonth();\n const grouped = Math.floor(totalMonths / count) * count;\n return Date.UTC(Math.floor(grouped / 12), grouped % 12, 1);\n}\n","const NUMBER_FORMAT_CACHE_LIMIT = 64;\nconst numberFormatCache = new Map<string, Intl.NumberFormat>();\n\n/**\n * `Number#toLocaleString(locale, options)` constructs a fresh\n * `Intl.NumberFormat` on every call — ~20µs in V8 versus ~0.4µs to reuse one.\n * Axis labels, the legend, crosshair pills and panel axes format dozens of\n * numbers per frame, so formatters are cached per (locale, precision). The\n * output is identical: per spec, `toLocaleString` is defined as exactly this.\n */\nfunction numberFormat(locale: string, precision: number): Intl.NumberFormat {\n const key = `${locale}|${precision}`;\n let nf = numberFormatCache.get(key);\n if (!nf) {\n if (numberFormatCache.size >= NUMBER_FORMAT_CACHE_LIMIT) numberFormatCache.clear();\n nf = new Intl.NumberFormat(locale, {\n minimumFractionDigits: precision,\n maximumFractionDigits: precision,\n });\n numberFormatCache.set(key, nf);\n }\n return nf;\n}\n\nexport function formatPrice(value: number, precision = 2, locale = 'en-US'): string {\n return numberFormat(locale, precision).format(value);\n}\n\n/**\n * Decimals the price axis uses for a visible price range: one more than its\n * tick step needs, 2 at minimum. Everything that prints a price next to the\n * axis (legend, crosshair pill, last-price tag) shares it, so a sub-cent\n * asset reads \"0.000004349\" everywhere instead of \"0.00\" off the axis.\n */\nexport function autoPricePrecision(min: number, max: number): number {\n if (!(max > min) || !Number.isFinite(min) || !Number.isFinite(max)) return 2;\n const step = computeTickStep(min, max, 8);\n return step > 0 && step < 1 ? Math.ceil(-Math.log10(step)) + 1 : 2;\n}\n\nimport type { PriceScaleMode } from '../types/rendering.js';\nimport type { PriceFormatter, PriceFraction } from '../types/chart.js';\nimport { computeTickStep } from './math.js';\n\n/**\n * Format a price-axis label for a given scale mode.\n *\n * - `regular` / `logarithmic`: the raw price.\n * - `percentage`: change from `baseline`, e.g. `+12.34%`.\n * - `indexedTo100`: the price rebased so `baseline` reads as 100.\n *\n * Falls back to a plain price when a baseline is required but missing/zero.\n */\nexport function formatPriceScaleLabel(\n price: number,\n mode: PriceScaleMode,\n baseline: number | undefined,\n precision = 2,\n locale = 'en-US',\n): string {\n if ((mode === 'percentage' || mode === 'indexedTo100') && baseline && baseline !== 0) {\n if (mode === 'percentage') {\n const pct = (price / baseline - 1) * 100;\n const sign = pct > 0 ? '+' : '';\n return `${sign}${pct.toFixed(2)}%`;\n }\n const indexed = (price / baseline) * 100;\n return formatPrice(indexed, 2, locale);\n }\n return formatPrice(price, precision, locale);\n}\n\n/**\n * `value` in fractions of a point: whole points, an apostrophe, then the\n * fraction's numerator padded to the denominator's digits (`101'16` for\n * 101.5 in 32nds). A `subDenominator` of 2 or 4 adds the half or quarter of\n * that fraction as one more digit, as futures quote it (`101'165`, `110'162`,\n * `110'167`). Rounds to the nearest step. A denominator that isn't a whole\n * number above 1 gives plain digits.\n */\nexport function formatFraction(value: number, denominator: number, subDenominator = 1): string {\n const sub = Number.isInteger(subDenominator) && subDenominator > 1 ? subDenominator : 1;\n if (!Number.isFinite(value) || !Number.isInteger(denominator) || denominator < 2) return String(value);\n const steps = denominator * sub;\n const total = Math.round(Math.abs(value) * steps);\n const whole = Math.floor(total / steps);\n const rest = total - whole * steps;\n const numerator = Math.floor(rest / sub);\n const width = String(denominator - 1).length;\n const tail = sub > 1 ? String(Math.floor(((rest - numerator * sub) / sub) * 10)) : '';\n const sign = value < 0 && total > 0 ? '-' : '';\n return `${sign}${whole}'${String(numerator).padStart(width, '0')}${tail}`;\n}\n\n/**\n * The smallest step a fraction format prints (a 32nd; a quarter of one with\n * `subDenominator` 4), or null for a denominator it can't print (it falls\n * back to plain digits then).\n */\nexport function fractionTick(fraction: PriceFraction): number | null {\n if (!Number.isInteger(fraction.denominator) || fraction.denominator < 2) return null;\n const sub = Number.isInteger(fraction.subDenominator) && (fraction.subDenominator ?? 0) > 1 ? fraction.subDenominator as number : 1;\n return 1 / (fraction.denominator * sub);\n}\n\n/** The formatter a `priceFormat` option stands for: its function, its fraction's, or null for decimals. */\nexport function priceFormatterFor(format: PriceFormatter | PriceFraction | null | undefined): PriceFormatter | null {\n if (!format) return null;\n if (typeof format === 'function') return format;\n // A denominator it can't print leaves the chart's decimals.\n if (fractionTick(format) === null) return null;\n return (price) => formatFraction(price, format.denominator, format.subDenominator);\n}\n\nexport function formatVolume(value: number): string {\n if (value >= 1_000_000_000) return (value / 1_000_000_000).toFixed(2) + 'B';\n if (value >= 1_000_000) return (value / 1_000_000).toFixed(2) + 'M';\n if (value >= 1_000) return (value / 1_000).toFixed(2) + 'K';\n return value.toFixed(0);\n}\n\nexport function detectPrecision(values: number[]): number {\n let maxDecimals = 0;\n for (const v of values) {\n const str = v.toString();\n const dot = str.indexOf('.');\n if (dot >= 0) {\n maxDecimals = Math.max(maxDecimals, str.length - dot - 1);\n }\n }\n return Math.min(maxDecimals, 8);\n}\n","import type { OHLCBar } from '../types/ohlc.js';\n\n/** The price an indicator can be computed from. */\nexport const PRICE_SOURCES = ['close', 'open', 'high', 'low', 'hl2', 'hlc3', 'ohlc4', 'hlcc4'] as const;\n\nexport type PriceSource = (typeof PRICE_SOURCES)[number];\n\nexport function isPriceSource(value: unknown): value is PriceSource {\n return typeof value === 'string' && (PRICE_SOURCES as readonly string[]).includes(value);\n}\n\n/** A bar's price by source: `hl2` = (high + low) / 2, `hlc3`, `ohlc4`, `hlcc4` = (h + l + 2c) / 4. */\nexport function sourcePrice(bar: OHLCBar, source: PriceSource): number {\n switch (source) {\n case 'open': return bar.open;\n case 'high': return bar.high;\n case 'low': return bar.low;\n case 'hl2': return (bar.high + bar.low) / 2;\n case 'hlc3': return (bar.high + bar.low + bar.close) / 3;\n case 'ohlc4': return (bar.open + bar.high + bar.low + bar.close) / 4;\n case 'hlcc4': return (bar.high + bar.low + 2 * bar.close) / 4;\n default: return bar.close;\n }\n}\n\nconst INDICATOR_SOURCE = /^ind:([^:]+):(.+)$/;\n\n/** A source that is another indicator's line: `ind:<instanceId>:<key>`. */\nexport function indicatorSource(instanceId: string, key: string): string {\n return `ind:${instanceId}:${key}`;\n}\n\n/** The indicator line a source refers to, or null for a price source or anything else. */\nexport function parseIndicatorSource(value: unknown): { instanceId: string; key: string } | null {\n if (typeof value !== 'string') return null;\n const m = INDICATOR_SOURCE.exec(value);\n return m ? { instanceId: m[1], key: m[2] } : null;\n}\n","import type { OHLCBar, TimeFrame, TimeFrameUnit } from '../types/ohlc.js';\nimport { parseTimeframe, timeframeBucketStart, timeframeToMs } from './time.js';\n\nconst DAY_MS = 86_400_000;\n\n/**\n * Aggregate bars (ascending, times in ms) into a coarser timeframe: per\n * bucket (see `timeframeBucketStart`) the first open, the extremes, the last\n * close and the summed volume. The input bars are not changed.\n */\nexport function resampleBars(bars: readonly OHLCBar[], target: TimeFrame, weekStartsOn: 0 | 1 = 1): OHLCBar[] {\n const out: OHLCBar[] = [];\n let current: OHLCBar | null = null;\n for (const bar of bars) {\n const key = timeframeBucketStart(bar.time, target, weekStartsOn);\n if (current === null || key !== current.time) {\n current = { time: key, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume };\n out.push(current);\n } else {\n if (bar.high > current.high) current.high = bar.high;\n if (bar.low < current.low) current.low = bar.low;\n current.close = bar.close;\n current.volume += bar.volume;\n }\n }\n return out;\n}\n\nconst isFixedLength = (unit: TimeFrameUnit): boolean => unit === 's' || unit === 'm' || unit === 'h' || unit === 'd';\n\n/** Whether whole bars of `base` fill every bucket of `target` exactly. */\nfunction buildsInto(base: TimeFrame, target: TimeFrame): boolean {\n const b = parseTimeframe(base);\n const t = parseTimeframe(target);\n if (!b || !t) return false;\n const baseMs = timeframeToMs(base);\n // Feeds disagree on where a 3-day bar starts: only build from whole days.\n if (b.unit === 'd' && b.count > 1) return false;\n if (isFixedLength(t.unit)) {\n return isFixedLength(b.unit) && timeframeToMs(target) % baseMs === 0;\n }\n if (b.unit === t.unit) return t.count % b.count === 0;\n // Weeks and months start at midnight: build them from bars that never\n // cross one.\n return isFixedLength(b.unit) && DAY_MS % baseMs === 0;\n}\n\n/**\n * The coarsest of `supported` that builds `target` from whole bars (e.g. 7m\n * from 1m, 90m from 30m, a quarter from months), or null when none does.\n */\nexport function pickBaseTimeframe(target: TimeFrame, supported: readonly TimeFrame[]): TimeFrame | null {\n let best: TimeFrame | null = null;\n let bestMs = 0;\n for (const tf of supported) {\n if (!buildsInto(tf, target)) continue;\n const ms = timeframeToMs(tf);\n if (ms > bestMs) {\n best = tf;\n bestMs = ms;\n }\n }\n return best;\n}\n","import type { SymbolInfo } from '../types/symbol.js';\n\n/** How well `query` (upper case) matches `text` (upper case): 0 when it doesn't. */\nfunction score(text: string, query: string): number {\n if (!text) return 0;\n if (text === query) return 1000;\n if (text.startsWith(query)) return 600 - Math.min(100, text.length - query.length);\n const at = text.indexOf(query);\n return at >= 0 ? 300 - Math.min(100, at) : 0;\n}\n\n/**\n * Symbols matching `query`, best first: an exact ticker, then tickers that\n * start with it, then names that contain it. `boost` adds to a match's score\n * (say, for the quote currencies most traded). An empty query keeps the order\n * given. At most `limit` results.\n */\nexport function rankSymbols(\n symbols: readonly SymbolInfo[],\n query: string,\n limit = 50,\n boost?: (info: SymbolInfo) => number,\n): SymbolInfo[] {\n const q = query.trim().toUpperCase();\n if (!q) return symbols.slice(0, limit);\n const scored: { info: SymbolInfo; score: number }[] = [];\n for (const info of symbols) {\n const s = Math.max(\n score(info.symbol.toUpperCase(), q),\n score((info.description ?? '').toUpperCase(), q) * 0.8,\n );\n if (s > 0) scored.push({ info, score: s + (boost?.(info) ?? 0) });\n }\n scored.sort((a, b) => b.score - a.score || a.info.symbol.localeCompare(b.info.symbol));\n return scored.slice(0, limit).map((s) => s.info);\n}\n\n/** Decimals in a price step: `'0.01000000'` → 2, `'1'` → 0, `1e-8` → 8. */\nexport function stepDecimals(step: number | string): number {\n const value = typeof step === 'number' ? step : Number(step);\n if (!Number.isFinite(value) || value <= 0) return 0;\n for (let decimals = 0; decimals <= 12; decimals++) {\n const scaled = value * 10 ** decimals;\n if (Math.abs(scaled - Math.round(scaled)) < 1e-9 * Math.max(1, scaled)) return decimals;\n }\n return 12;\n}\n\n/** `'09:30'` → 570 (`'24:00'`, a day's end, → 1440), or null for anything that isn't a time of day. */\nexport function sessionMinute(time: string): number | null {\n const match = /^(\\d{1,2}):(\\d{2})$/.exec(time.trim());\n if (!match) return null;\n const hours = Number(match[1]);\n const minutes = Number(match[2]);\n if (hours === 24 && minutes === 0) return 1440;\n return hours < 24 && minutes < 60 ? hours * 60 + minutes : null;\n}\n","import type { SymbolInfo } from '../types/symbol.js';\nimport { sessionMinute } from './symbols.js';\nimport { isValidTimeZone, zoneOffsetMinutes } from './timezone.js';\n\n/** Whether a symbol's market is trading now, and when that changes. */\nexport interface MarketStatus {\n /** `'always'`: no hours known (24/7, such as crypto). */\n state: 'open' | 'closed' | 'always';\n /** When it next closes (open) or opens (closed), in ms; null when unknown. */\n next: number | null;\n}\n\nconst DAY_MS = 86_400_000;\nconst MINUTE_MS = 60_000;\n/** Days looked ahead for the next open (a long weekend, a week of holidays). */\nconst LOOK_AHEAD_DAYS = 8;\n\n/**\n * The market status of `info` at `now`: in a session of its hours (in its\n * exchange's time zone, clocks changes included) or not, and when that\n * changes. Sessions that meet count as one; a session past midnight belongs\n * to the day it starts. Without hours or a zone it knows: `'always'`.\n */\nexport function marketStatus(info: SymbolInfo | null | undefined, now: number): MarketStatus {\n const zone = info?.timezone;\n const sessions = (info?.sessions ?? []).flatMap((s) => {\n const start = sessionMinute(s.start);\n const end = sessionMinute(s.end);\n if (start === null || end === null || start === end) return [];\n return [{ start, end, days: Array.isArray(s.days) ? s.days : null }];\n });\n if (!zone || !isValidTimeZone(zone) || sessions.length === 0 || !Number.isFinite(now)) {\n return { state: 'always', next: null };\n }\n\n const offset = zoneOffsetMinutes(zone, now) * MINUTE_MS;\n const today = Math.floor((now + offset) / DAY_MS) * DAY_MS;\n // A wall-clock time in the zone, back to UTC with the offset that holds then\n // (asked twice, so a time beside a clock change settles on its side).\n const toUtc = (local: number) => {\n const guess = local - zoneOffsetMinutes(zone, local - offset) * MINUTE_MS;\n return local - zoneOffsetMinutes(zone, guess) * MINUTE_MS;\n };\n\n const spans: [number, number][] = [];\n for (let d = -1; d <= LOOK_AHEAD_DAYS; d++) {\n const dayStart = today + d * DAY_MS;\n const weekday = (Math.floor(dayStart / DAY_MS) + 4) % 7; // 1 Jan 1970 was a Thursday\n for (const s of sessions) {\n if (s.days && !s.days.includes(weekday)) continue;\n const start = dayStart + s.start * MINUTE_MS;\n const end = (s.end > s.start ? dayStart : dayStart + DAY_MS) + s.end * MINUTE_MS;\n spans.push([toUtc(start), toUtc(end)]);\n }\n }\n spans.sort((a, b) => a[0] - b[0]);\n const merged: [number, number][] = [];\n for (const [start, end] of spans) {\n const last = merged[merged.length - 1];\n if (last && start <= last[1]) merged[merged.length - 1] = [last[0], Math.max(last[1], end)];\n else merged.push([start, end]);\n }\n\n const current = merged.find(([start, end]) => start <= now && now < end);\n if (current) return { state: 'open', next: current[1] };\n const upcoming = merged.find(([start]) => start > now);\n return { state: 'closed', next: upcoming ? upcoming[0] : null };\n}\n","import type { DrawingLevel, DrawingOptionDef, DrawingOptionDefs, DrawingOptionValue, DrawingOptions } from '../types/drawing.js';\n\n/** Most levels a drawing keeps; more is a pasted or hand-edited state, not a chart. */\nconst MAX_LEVELS = 48;\nconst MAX_TEXT = 2000;\n/** Largest level ratio kept (a Fibonacci level of 4.236, a time zone of 89 bars). */\nconst MAX_LEVEL_VALUE = 10_000;\n\nfunction sanitizeLevels(raw: unknown): DrawingLevel[] | undefined {\n if (!Array.isArray(raw)) return undefined;\n const levels: DrawingLevel[] = [];\n for (const item of raw) {\n if (levels.length >= MAX_LEVELS) break;\n if (typeof item !== 'object' || item === null) continue;\n const { value, visible, color } = item as Record<string, unknown>;\n if (typeof value !== 'number' || !Number.isFinite(value) || Math.abs(value) > MAX_LEVEL_VALUE) continue;\n if (visible !== undefined && typeof visible !== 'boolean') continue;\n const level: DrawingLevel = { value, visible: visible ?? true };\n if (typeof color === 'string' && color) level.color = color;\n levels.push(level);\n }\n return levels;\n}\n\n/** `raw` as a value for `def`, or undefined when it isn't one. */\nfunction sanitizeValue(def: DrawingOptionDef, raw: unknown): DrawingOptionValue | undefined {\n switch (def.kind) {\n case 'boolean':\n return typeof raw === 'boolean' ? raw : undefined;\n case 'number': {\n if (typeof raw !== 'number' || !Number.isFinite(raw)) return undefined;\n return Math.min(def.max ?? Infinity, Math.max(def.min ?? -Infinity, raw));\n }\n case 'choice':\n return typeof raw === 'string' && def.choices.some((c) => c.value === raw) ? raw : undefined;\n case 'levels':\n return sanitizeLevels(raw);\n case 'text':\n return typeof raw === 'string' ? raw.slice(0, MAX_TEXT) : undefined;\n }\n}\n\n/** The valid entries of `raw` for a tool's options: unknown keys and wrong values are dropped, numbers clamped. */\nexport function sanitizeDrawingOptions(defs: DrawingOptionDefs | undefined, raw: unknown): DrawingOptions {\n if (!defs || typeof raw !== 'object' || raw === null) return {};\n const out: DrawingOptions = {};\n for (const [key, value] of Object.entries(raw)) {\n const def = defs[key];\n if (!def) continue;\n const clean = sanitizeValue(def, value);\n if (clean !== undefined) out[key] = clean;\n }\n return out;\n}\n\nfunction copyValue(value: DrawingOptionValue | readonly DrawingLevel[]): DrawingOptionValue {\n return Array.isArray(value) ? value.map((level) => ({ ...level })) : (value as DrawingOptionValue);\n}\n\n/** One option of a drawing: its own value when valid, else the tool's default. */\nexport function drawingOption<T extends DrawingOptionValue = DrawingOptionValue>(\n defs: DrawingOptionDefs | undefined,\n options: DrawingOptions | undefined,\n key: string,\n): T {\n const def = defs?.[key];\n if (!def) return options?.[key] as T;\n const own = options && key in options ? sanitizeValue(def, options[key]) : undefined;\n return copyValue(own ?? def.default) as T;\n}\n\n/** Every option of a tool for a drawing, defaults filled in. */\nexport function resolveDrawingOptions(defs: DrawingOptionDefs | undefined, options: DrawingOptions | undefined): DrawingOptions {\n const out: DrawingOptions = {};\n for (const key of Object.keys(defs ?? {})) out[key] = drawingOption(defs, options, key);\n return out;\n}\n","import type { DrawingStyle } from '../types/drawing.js';\n\nconst LINE_STYLES: readonly DrawingStyle['lineStyle'][] = ['solid', 'dashed', 'dotted'];\nconst MAX_COLOR = 64;\nconst MAX_TEXT = 2000;\nconst MAX_LINE_WIDTH = 20;\nconst MAX_FONT_SIZE = 200;\n\nfunction finiteIn(value: unknown, min: number, max: number): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? Math.min(max, Math.max(min, value)) : undefined;\n}\n\nfunction colorValue(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 && value.length <= MAX_COLOR ? value : undefined;\n}\n\n/**\n * The valid fields of a drawing style from untrusted data (a saved layout, a\n * template): colours as short strings, a known line style, widths, opacity\n * and font size as finite numbers in range, text cut to length. Anything\n * else is dropped.\n */\nexport function sanitizeDrawingStyle(raw: unknown): Partial<DrawingStyle> {\n if (typeof raw !== 'object' || raw === null) return {};\n const obj = raw as Record<string, unknown>;\n const out: Partial<DrawingStyle> = {};\n const color = colorValue(obj.color);\n if (color !== undefined) out.color = color;\n const lineWidth = finiteIn(obj.lineWidth, 0.5, MAX_LINE_WIDTH);\n if (lineWidth !== undefined) out.lineWidth = lineWidth;\n if (LINE_STYLES.includes(obj.lineStyle as DrawingStyle['lineStyle'])) out.lineStyle = obj.lineStyle as DrawingStyle['lineStyle'];\n const fillColor = colorValue(obj.fillColor);\n if (fillColor !== undefined) out.fillColor = fillColor;\n const fillOpacity = finiteIn(obj.fillOpacity, 0, 1);\n if (fillOpacity !== undefined) out.fillOpacity = fillOpacity;\n const fontSize = finiteIn(obj.fontSize, 6, MAX_FONT_SIZE);\n if (fontSize !== undefined) out.fontSize = fontSize;\n if (typeof obj.text === 'string') out.text = obj.text.slice(0, MAX_TEXT);\n return out;\n}\n","import type { ChartTypeOptions } from '../types/chart.js';\n\nconst positive = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v) && v > 0;\nconst wholeIn = (v: unknown, lo: number, hi: number): v is number =>\n typeof v === 'number' && Number.isInteger(v) && v >= lo && v <= hi;\nconst isObject = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);\n\n/**\n * The chart type settings in `raw` the chart can use: boxes, ranges and\n * reversals above 0, whole line and box counts in range. A type given at\n * all is in the result (empty when none of its settings were usable), so\n * a type's settings can be put back to their defaults.\n */\nexport function readChartTypeOptions(raw: unknown): ChartTypeOptions {\n if (!isObject(raw)) return {};\n const out: ChartTypeOptions = {};\n if (isObject(raw.renko)) {\n const { boxSize, atrPeriod } = raw.renko;\n out.renko = {\n ...(positive(boxSize) || boxSize === 'atr' ? { boxSize } : {}),\n ...(wholeIn(atrPeriod, 1, 500) ? { atrPeriod } : {}),\n };\n }\n if (isObject(raw.lineBreak)) {\n const { lines } = raw.lineBreak;\n out.lineBreak = wholeIn(lines, 1, 10) ? { lines } : {};\n }\n if (isObject(raw.kagi)) {\n const { reversal, reversalType } = raw.kagi;\n out.kagi = {\n ...(positive(reversal) ? { reversal } : {}),\n ...(reversalType === 'percent' || reversalType === 'price' ? { reversalType } : {}),\n };\n }\n if (isObject(raw.pointAndFigure)) {\n const { boxSize, reversal } = raw.pointAndFigure;\n out.pointAndFigure = {\n ...(positive(boxSize) || boxSize === 'auto' ? { boxSize } : {}),\n ...(wholeIn(reversal, 1, 10) ? { reversal } : {}),\n };\n }\n if (isObject(raw.rangeBars)) {\n const { range } = raw.rangeBars;\n out.rangeBars = positive(range) || range === 'auto' ? { range } : {};\n }\n return out;\n}\n","import type { Theme } from '../types/theme.js';\nimport { withAlpha } from '../utils/color.js';\n\n/** The chart's own type: the system UI face, so text matches the page around it. */\nexport const DEFAULT_FONT_FAMILY = '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif';\n\nconst DEFAULT_FONT = {\n family: DEFAULT_FONT_FAMILY,\n sizeSmall: 10,\n sizeMedium: 12,\n sizeLarge: 14,\n};\n\n/**\n * TradeCanvas palette. Themes pick their own candle colours for their\n * background; everything drawn on top of either theme (indicators, orders,\n * signals, drawings) uses the theme-neutral values below, which keep at\n * least 3:1 contrast on both the dark ink and a white background. `amber` is\n * the brand accent for dark surfaces (2:1 on white); light surfaces use\n * `LIGHT_THEME.lineColor` / `ochre` instead.\n */\nexport const TC_PALETTE = {\n /** Brand amber — the default series line and the accent. */\n amber: '#f2a93b',\n /** Rising / bullish, legible on dark and light. */\n up: '#1fa874',\n /** Falling / bearish, legible on dark and light. */\n down: '#e8505b',\n /** Amber deep enough for white backgrounds; auto-coloured series use it. */\n ochre: '#c98316',\n /** Secondary series, signal lines. */\n blue: '#4c8dff',\n violet: '#a57cff',\n cyan: '#1398a8',\n rose: '#e25592',\n /** Zero lines, neutral guides. */\n neutral: '#7d8696',\n /** Ink ground of the dark theme. */\n ink: '#0c1016',\n} as const;\n\n/**\n * How strongly volume bars show: the candle colour at this opacity, so they\n * read as a backdrop under the candles rather than a second chart.\n */\nexport const VOLUME_ALPHA = 0.22;\n\n/** The volume bar colour for a candle colour. */\nexport function volumeColor(candleColor: string): string {\n return withAlpha(candleColor, VOLUME_ALPHA);\n}\n\nconst DARK_UP = '#2eb88a';\nconst DARK_DOWN = '#e85a67';\n\n/** Order in which overlay indicators are coloured when no colour is given. */\nexport const TC_SERIES_COLORS: readonly string[] = [\n TC_PALETTE.blue,\n TC_PALETTE.ochre,\n TC_PALETTE.violet,\n TC_PALETTE.cyan,\n TC_PALETTE.rose,\n];\n\nexport const DARK_THEME: Theme = {\n name: 'dark',\n background: TC_PALETTE.ink,\n text: '#d9dde4',\n textSecondary: '#7d8696',\n grid: '#161b23',\n crosshair: '#8a93a3',\n candleUp: DARK_UP,\n candleDown: DARK_DOWN,\n candleUpWick: DARK_UP,\n candleDownWick: DARK_DOWN,\n lineColor: TC_PALETTE.amber,\n areaTopColor: 'rgba(242, 169, 59, 0.32)',\n areaBottomColor: 'rgba(242, 169, 59, 0.0)',\n volumeUp: volumeColor(DARK_UP),\n volumeDown: volumeColor(DARK_DOWN),\n axisLine: '#1f2630',\n axisLabel: '#9aa3b2',\n axisLabelBackground: '#1f2630',\n font: DEFAULT_FONT,\n};\n\nexport const LIGHT_THEME: Theme = {\n name: 'light',\n background: '#ffffff',\n text: '#0f131a',\n textSecondary: '#6b7380',\n grid: '#eef0f3',\n crosshair: '#8a93a3',\n candleUp: '#16a36a',\n candleDown: '#d9414f',\n candleUpWick: '#16a36a',\n candleDownWick: '#d9414f',\n lineColor: '#c77a0a',\n areaTopColor: 'rgba(199, 122, 10, 0.24)',\n areaBottomColor: 'rgba(199, 122, 10, 0.0)',\n volumeUp: volumeColor('#16a36a'),\n volumeDown: volumeColor('#d9414f'),\n axisLine: '#dde1e7',\n axisLabel: '#5b6472',\n axisLabelBackground: '#eef0f3',\n font: DEFAULT_FONT,\n};\n\nexport const DARK_TERMINAL: Theme = {\n name: 'terminal',\n background: '#0E0E0E',\n text: '#C0C0C0',\n textSecondary: '#8A8A8A',\n grid: '#1A1A1A',\n crosshair: '#666666',\n candleUp: '#00FF87',\n candleDown: '#FF3B4D',\n candleUpWick: '#00FF87',\n candleDownWick: '#FF3B4D',\n lineColor: '#3D8BFD',\n areaTopColor: 'rgba(61, 139, 253, 0.3)',\n areaBottomColor: 'rgba(61, 139, 253, 0.0)',\n volumeUp: 'rgba(0, 255, 135, 0.2)',\n volumeDown: 'rgba(255, 59, 77, 0.2)',\n axisLine: '#1A1A1A',\n axisLabel: '#8A8A8A',\n axisLabelBackground: '#1A1A1A',\n font: {\n family: \"'Roboto Mono', 'JetBrains Mono', 'SF Mono', Consolas, monospace\",\n sizeSmall: 10,\n sizeMedium: 12,\n sizeLarge: 14,\n },\n};\n\n/** The built-in themes' volume colours, to tell a preset's from a theme's own. */\nconst PRESET_VOLUME = new Set([DARK_THEME.volumeUp, DARK_THEME.volumeDown, LIGHT_THEME.volumeUp, LIGHT_THEME.volumeDown, DARK_TERMINAL.volumeUp, DARK_TERMINAL.volumeDown]);\n\n/**\n * The volume bars' colours for `theme`: its own `volumeUp` / `volumeDown`,\n * except that a theme spread from a preset with new candle colours (and the\n * preset's volume left in place) gets volume in its candles' colours, as the\n * bars have always followed the candles.\n */\nexport function resolveVolumeColors(theme: Pick<Theme, 'candleUp' | 'candleDown' | 'volumeUp' | 'volumeDown'>): { up: string; down: string } {\n const pick = (volume: string, candle: string): string => {\n if (!PRESET_VOLUME.has(volume)) return volume;\n const derived = volumeColor(candle);\n return derived === candle ? volume : derived;\n };\n return { up: pick(theme.volumeUp, theme.candleUp), down: pick(theme.volumeDown, theme.candleDown) };\n}\n","import type { ChartType, LineStyle } from '../types/chart.js';\nimport type { Theme } from '../types/theme.js';\nimport type { IndicatorPlotStyle } from '../types/indicator.js';\nimport {\n CHART_STYLE_KEYS,\n type ChartStyleKey,\n type ChartStyleOverrides,\n type ChartStyleOverridesPatch,\n type PaneStyle,\n type ResolvedChartStyle,\n type ResolvedLineLook,\n type StyleValueKind,\n} from '../types/style.js';\nimport { resolveVolumeColors } from '../constants/themes.js';\n\nconst LINE_STYLES: readonly LineStyle[] = ['solid', 'dashed', 'dotted'];\n/** Widths past this are a mistake, not a look. */\nconst MAX_WIDTH = 20;\n/** Long enough for any colour CSS writes, short enough to keep anything else out. */\nconst MAX_COLOR_LENGTH = 64;\n/** The main series' line width when nothing sets it. */\nconst DEFAULT_LINE_WIDTH = 2;\n\n/**\n * A colour as CSS writes one, and only that: hex, a name, or a colour\n * function (`rgb()`, `hsl()`, `oklch()`, `color()`…) of numbers, units and\n * words. Anything else is refused, so a value read from a saved layout can't\n * carry markup or script into a style or an attribute.\n */\nconst COLOR_PATTERNS = [\n /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i,\n /^[a-z]{3,30}$/i,\n /^(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color)\\([0-9a-z.,%/\\s-]*\\)$/i,\n];\n\nfunction isColor(value: unknown): value is string {\n return typeof value === 'string' && value.length <= MAX_COLOR_LENGTH && COLOR_PATTERNS.some((pattern) => pattern.test(value.trim()));\n}\n\nfunction isValue(kind: StyleValueKind, value: unknown): boolean {\n switch (kind) {\n case 'color': return isColor(value);\n case 'lineStyle': return LINE_STYLES.includes(value as LineStyle);\n case 'width': return typeof value === 'number' && Number.isFinite(value) && value > 0 && value <= MAX_WIDTH;\n case 'boolean': return typeof value === 'boolean';\n }\n}\n\nfunction isStyleKey(key: string): key is ChartStyleKey {\n return Object.prototype.hasOwnProperty.call(CHART_STYLE_KEYS, key);\n}\n\n/**\n * Overrides read from anywhere (your options, a saved layout): the keys it\n * knows with values of their kind, `null` taking a key away. `rejected`\n * names what was left out.\n */\nexport function readChartStyleOverrides(raw: unknown): { overrides: ChartStyleOverridesPatch; rejected: string[] } {\n const overrides: Record<string, unknown> = {};\n const rejected: string[] = [];\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return { overrides: {}, rejected };\n for (const [key, value] of Object.entries(raw)) {\n if (!isStyleKey(key)) rejected.push(key);\n else if (value === null) overrides[key] = null;\n else if (isValue(CHART_STYLE_KEYS[key], value)) overrides[key] = value;\n else rejected.push(key);\n }\n return { overrides: overrides as ChartStyleOverridesPatch, rejected };\n}\n\n/** A pane's own style: the colours it can use, or null when it has none. */\nexport function readPaneStyle(raw: unknown): PaneStyle | null {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return null;\n const { background, separator } = raw as Record<string, unknown>;\n const style: PaneStyle = {\n ...(isColor(background) ? { background } : {}),\n ...(isColor(separator) ? { separator } : {}),\n };\n return Object.keys(style).length > 0 ? style : null;\n}\n\n/** A plot key as indicators name them: short, nothing odd in it. */\nconst PLOT_KEY = /^[A-Za-z0-9_]{1,32}$/;\n\n/** Plot styles read from anywhere: by plot key, a dash style and whether it shows; undefined when none. */\nexport function readIndicatorPlotStyles(raw: unknown): Record<string, IndicatorPlotStyle> | undefined {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return undefined;\n const out: Record<string, IndicatorPlotStyle> = {};\n for (const [key, value] of Object.entries(raw)) {\n if (!PLOT_KEY.test(key) || typeof value !== 'object' || value === null) continue;\n const { lineStyle, visible } = value as Record<string, unknown>;\n const plot: IndicatorPlotStyle = {\n ...(LINE_STYLES.includes(lineStyle as LineStyle) ? { lineStyle: lineStyle as LineStyle } : {}),\n ...(typeof visible === 'boolean' ? { visible } : {}),\n };\n if (Object.keys(plot).length > 0) out[key] = plot;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\n/** A dash pattern for a line style: `dashed` its own, `dotted` dots as wide as the line. */\nexport function lineDash(style: LineStyle, dashed: readonly number[], width = 1): number[] {\n if (style === 'solid') return [];\n if (style === 'dotted') return [width, width * 2];\n return [...dashed];\n}\n\ntype Lookup = (key: string) => unknown;\n\n/** The main series' colours and widths, as the chart type `type` takes them. */\nfunction seriesLooks(base: Theme, o: Lookup, type: ChartType) {\n const own = (prop: string) => o(`series.${type}.${prop}`);\n const upSet = own('upColor') ?? o('series.candlestick.upColor');\n const downSet = own('downColor') ?? o('series.candlestick.downColor');\n const up = (upSet as string | undefined) ?? base.candleUp;\n const down = (downSet as string | undefined) ?? base.candleDown;\n return {\n up,\n down,\n // A wick takes its body's colour once that is set, as the theme's own do.\n wickUp: ((own('wickUpColor') ?? o('series.candlestick.wickUpColor')) as string | undefined) ?? (upSet !== undefined ? up : base.candleUpWick),\n wickDown: ((own('wickDownColor') ?? o('series.candlestick.wickDownColor')) as string | undefined) ?? (downSet !== undefined ? down : base.candleDownWick),\n line: ((own('color') ?? own('lineColor') ?? o('series.line.color')) as string | undefined) ?? base.lineColor,\n lineWidth: ((own('lineWidth') ?? o('series.line.lineWidth')) as number | undefined) ?? DEFAULT_LINE_WIDTH,\n top: ((own('topColor') ?? o('series.area.topColor')) as string | undefined) ?? base.areaTopColor,\n bottom: ((own('bottomColor') ?? o('series.area.bottomColor')) as string | undefined) ?? base.areaBottomColor,\n };\n}\n\nfunction lineLook(o: Lookup, prefix: string, fallback: ResolvedLineLook): ResolvedLineLook {\n return {\n visible: (o(`${prefix}.visible`) as boolean | undefined) ?? fallback.visible,\n color: (o(`${prefix}.color`) as string | undefined) ?? fallback.color,\n style: (o(`${prefix}.style`) as LineStyle | undefined) ?? fallback.style,\n width: (o(`${prefix}.width`) as number | undefined) ?? fallback.width,\n };\n}\n\n/**\n * The theme the chart draws with: `base` with `overrides` on its tokens (the\n * main series' as `chartType` takes them) and the finer looks in `style`.\n * `base` is left as it was.\n */\nexport function resolveChartTheme(base: Theme, chartType: ChartType, overrides: ChartStyleOverrides): Theme {\n const { style: _ignored, ...plain } = base;\n const map = overrides as Record<string, unknown>;\n const o: Lookup = (key) => map[key];\n const series = seriesLooks(plain, o, chartType);\n const background = (o('background.color') as string | undefined) ?? plain.background;\n const str = (key: ChartStyleKey, fallback: string) => (o(key) as string | undefined) ?? fallback;\n /** A colour of a part with its own settings: the override, or null to keep to them. */\n const ownColor = (key: ChartStyleKey): string | null => (o(key) as string | undefined) ?? null;\n\n const style: ResolvedChartStyle = {\n panes: {\n background: str('panes.background', background),\n separator: str('panes.separatorColor', plain.axisLine),\n title: str('panes.titleColor', plain.textSecondary),\n },\n grid: {\n horizontal: lineLook(o, 'grid.horizontal', { visible: true, color: plain.grid, style: 'solid', width: 1 }),\n vertical: lineLook(o, 'grid.vertical', { visible: true, color: plain.grid, style: 'solid', width: 1 }),\n },\n crosshair: {\n horizontal: lineLook(o, 'crosshair.horizontal', { visible: true, color: plain.crosshair, style: 'dashed', width: 1 }),\n vertical: lineLook(o, 'crosshair.vertical', { visible: true, color: plain.crosshair, style: 'dashed', width: 1 }),\n labelBackground: str('crosshair.labelBackground', plain.text),\n labelText: str('crosshair.labelTextColor', background),\n },\n axis: {\n price: { line: str('axis.price.lineColor', plain.axisLine), text: str('axis.price.textColor', plain.axisLabel) },\n time: { line: str('axis.time.lineColor', plain.axisLine), text: str('axis.time.textColor', plain.axisLabel) },\n },\n legend: { text: str('legend.textColor', plain.text), label: str('legend.labelColor', plain.textSecondary) },\n watermark: { color: (o('watermark.color') as string | undefined) ?? null },\n lastPrice: {\n visible: (o('lastPrice.visible') as boolean | undefined) ?? true,\n up: str('lastPrice.upColor', series.up),\n down: str('lastPrice.downColor', series.down),\n style: (o('lastPrice.style') as LineStyle | undefined) ?? 'dashed',\n width: (o('lastPrice.width') as number | undefined) ?? 1,\n },\n sessionBreaks: {\n color: (o('sessionBreaks.color') as string | undefined) ?? null,\n style: (o('sessionBreaks.style') as LineStyle | undefined) ?? null,\n width: (o('sessionBreaks.width') as number | undefined) ?? null,\n },\n highLow: { color: (o('highLow.color') as string | undefined) ?? null },\n series: { lineWidth: series.lineWidth },\n trading: {\n buy: ownColor('trading.buyColor'),\n sell: ownColor('trading.sellColor'),\n profit: ownColor('trading.profitColor'),\n loss: ownColor('trading.lossColor'),\n entry: ownColor('trading.entryColor'),\n },\n markers: { long: ownColor('markers.longColor'), short: ownColor('markers.shortColor'), neutral: ownColor('markers.neutralColor') },\n tradeZones: { profit: ownColor('tradeZones.profitColor'), loss: ownColor('tradeZones.lossColor'), active: ownColor('tradeZones.activeColor') },\n drawings: { handle: ownColor('drawings.handleColor') },\n };\n\n return {\n ...plain,\n background,\n candleUp: series.up,\n candleDown: series.down,\n candleUpWick: series.wickUp,\n candleDownWick: series.wickDown,\n lineColor: series.line,\n areaTopColor: series.top,\n areaBottomColor: series.bottom,\n volumeUp: str('volume.upColor', plain.volumeUp),\n volumeDown: str('volume.downColor', plain.volumeDown),\n style,\n };\n}\n\n/** How each key that isn't the series' reads back from the resolved theme. */\nconst READ_BACK: { [K in ChartStyleKey]?: (t: Theme, s: ResolvedChartStyle) => unknown } = {\n 'background.color': (t) => t.background,\n 'panes.background': (_t, s) => s.panes.background,\n 'panes.separatorColor': (_t, s) => s.panes.separator,\n 'panes.titleColor': (_t, s) => s.panes.title,\n 'crosshair.labelBackground': (_t, s) => s.crosshair.labelBackground,\n 'crosshair.labelTextColor': (_t, s) => s.crosshair.labelText,\n 'axis.price.lineColor': (_t, s) => s.axis.price.line,\n 'axis.price.textColor': (_t, s) => s.axis.price.text,\n 'axis.time.lineColor': (_t, s) => s.axis.time.line,\n 'axis.time.textColor': (_t, s) => s.axis.time.text,\n 'legend.textColor': (_t, s) => s.legend.text,\n 'legend.labelColor': (_t, s) => s.legend.label,\n 'watermark.color': (_t, s) => s.watermark.color,\n 'lastPrice.visible': (_t, s) => s.lastPrice.visible,\n 'lastPrice.upColor': (_t, s) => s.lastPrice.up,\n 'lastPrice.downColor': (_t, s) => s.lastPrice.down,\n 'lastPrice.style': (_t, s) => s.lastPrice.style,\n 'lastPrice.width': (_t, s) => s.lastPrice.width,\n // As drawn: a theme that sets only candle colours gets volume in those colours.\n 'volume.upColor': (t) => resolveVolumeColors(t).up,\n 'volume.downColor': (t) => resolveVolumeColors(t).down,\n 'sessionBreaks.color': (_t, s) => s.sessionBreaks.color,\n 'sessionBreaks.style': (_t, s) => s.sessionBreaks.style,\n 'sessionBreaks.width': (_t, s) => s.sessionBreaks.width,\n 'highLow.color': (_t, s) => s.highLow.color,\n 'trading.buyColor': (_t, s) => s.trading.buy,\n 'trading.sellColor': (_t, s) => s.trading.sell,\n 'trading.profitColor': (_t, s) => s.trading.profit,\n 'trading.lossColor': (_t, s) => s.trading.loss,\n 'trading.entryColor': (_t, s) => s.trading.entry,\n 'markers.longColor': (_t, s) => s.markers.long,\n 'markers.shortColor': (_t, s) => s.markers.short,\n 'markers.neutralColor': (_t, s) => s.markers.neutral,\n 'tradeZones.profitColor': (_t, s) => s.tradeZones.profit,\n 'tradeZones.lossColor': (_t, s) => s.tradeZones.loss,\n 'tradeZones.activeColor': (_t, s) => s.tradeZones.active,\n 'drawings.handleColor': (_t, s) => s.drawings.handle,\n};\n\nconst LINE_PROPS: Record<string, keyof ResolvedLineLook> = { visible: 'visible', color: 'color', style: 'style', width: 'width' };\n\n/**\n * What `key` resolves to: its override, or what it falls back to. A\n * `series.<type>.*` key resolves as that type would draw; the last price\n * follows `chartType`'s colours. Null for the parts that keep to their own\n * settings when not overridden (the watermark, session breaks, high/low).\n */\nexport function chartStyleValue(\n base: Theme,\n overrides: ChartStyleOverrides,\n key: ChartStyleKey,\n chartType: ChartType = 'candlestick',\n): string | number | boolean | null {\n const parts = key.split('.');\n if (parts[0] === 'series') {\n const looks = seriesLooks(base, (k) => (overrides as Record<string, unknown>)[k], parts[1] as ChartType);\n switch (parts[2]) {\n case 'upColor': return looks.up;\n case 'downColor': return looks.down;\n case 'wickUpColor': return looks.wickUp;\n case 'wickDownColor': return looks.wickDown;\n case 'color':\n case 'lineColor': return looks.line;\n case 'lineWidth': return looks.lineWidth;\n case 'topColor': return looks.top;\n case 'bottomColor': return looks.bottom;\n }\n }\n const theme = resolveChartTheme(base, chartType, overrides);\n const style = theme.style!;\n if (parts[0] === 'grid' || (parts[0] === 'crosshair' && (parts[1] === 'horizontal' || parts[1] === 'vertical'))) {\n const group = parts[0] === 'grid' ? style.grid : style.crosshair;\n return group[parts[1] as 'horizontal' | 'vertical'][LINE_PROPS[parts[2]]];\n }\n return (READ_BACK[key]?.(theme, style) ?? null) as string | number | boolean | null;\n}\n","import type { ChartOptions } from '../types/chart.js';\n\nexport const DEFAULT_CHART_OPTIONS: Required<Pick<ChartOptions, 'autoScale' | 'rightMargin' | 'minBarSpacing' | 'maxBarSpacing'>> & Pick<ChartOptions, 'grid' | 'crosshair'> = {\n autoScale: true,\n rightMargin: 5,\n minBarSpacing: 2,\n maxBarSpacing: 30,\n grid: {\n visible: true,\n hLineStyle: 'solid',\n vLineStyle: 'solid',\n },\n crosshair: {\n mode: 'magnet',\n },\n};\n\n// Standard timeframe presets for different market types\nimport type { TimeFrame } from '../types/ohlc.js';\n\n/** Crypto: all timeframes including seconds */\nexport const TIMEFRAMES_CRYPTO: TimeFrame[] = [\n '1s', '1m', '3m', '5m', '15m', '30m',\n '1h', '2h', '4h', '6h', '8h', '12h',\n '1d', '3d', '1w', '1M',\n];\n\n/** Stocks: minute-level and above (no seconds) */\nexport const TIMEFRAMES_STOCK: TimeFrame[] = [\n '1m', '5m', '15m', '30m',\n '1h', '2h', '4h',\n '1d', '1w', '1M', '3M', '6M', '12M',\n];\n\n/** Forex: common forex timeframes */\nexport const TIMEFRAMES_FOREX: TimeFrame[] = [\n '1m', '5m', '15m', '30m',\n '1h', '4h',\n '1d', '1w', '1M',\n];\n\n/** Default favorites shown in quick-access bar */\nexport const DEFAULT_TIMEFRAME_FAVORITES: TimeFrame[] = [\n '1m', '5m', '15m', '1h', '4h', '1d', '1w',\n];\n\nexport const DEFAULT_BAR_WIDTH = 8;\nexport const DEFAULT_BAR_SPACING = 2;\nexport const PRICE_AXIS_WIDTH = 70;\nexport const TIME_AXIS_HEIGHT = 30;\nexport const MIN_PANEL_HEIGHT = 60;\nexport const DEFAULT_PANEL_HEIGHT = 120;\n","import type { LocaleStrings } from './types.js';\n\nexport const en: LocaleStrings = {\n // Chart types\n candlestick: 'Candlestick',\n line: 'Line',\n area: 'Area',\n bar: 'OHLC Bar',\n\n // Axes\n price: 'Price',\n volume: 'Volume',\n time: 'Time',\n open: 'Open',\n high: 'High',\n low: 'Low',\n close: 'Close',\n\n // Indicators - overlays\n sma: 'SMA',\n ema: 'EMA',\n bollingerBands: 'Bollinger Bands',\n vwap: 'VWAP',\n ichimoku: 'Ichimoku Cloud',\n parabolicSAR: 'Parabolic SAR',\n supertrend: 'Supertrend',\n keltnerChannel: 'Keltner Channel',\n donchianChannel: 'Donchian Channel',\n\n // Indicators - panels\n rsi: 'RSI',\n macd: 'MACD',\n stochastic: 'Stochastic',\n atr: 'ATR',\n adx: 'ADX',\n obv: 'OBV',\n williamsR: 'Williams %R',\n cci: 'CCI',\n mfi: 'MFI',\n aroon: 'Aroon',\n roc: 'ROC',\n tsi: 'TSI',\n cmf: 'CMF',\n stddev: 'Std Dev',\n volumeProfile: 'Volume Profile',\n accumulationDistribution: 'A/D Line',\n vroc: 'VROC',\n\n // Drawing tools\n trendLine: 'Trend Line',\n horizontalLine: 'Horizontal Line',\n verticalLine: 'Vertical Line',\n ray: 'Ray',\n extendedLine: 'Extended Line',\n parallelChannel: 'Parallel Channel',\n regressionChannel: 'Regression Channel',\n fibRetracement: 'Fibonacci Retracement',\n fibExtension: 'Fibonacci Extension',\n rectangle: 'Rectangle',\n ellipse: 'Ellipse',\n triangle: 'Triangle',\n pitchfork: \"Andrews' Pitchfork\",\n elliottWave: 'Elliott Wave',\n priceRange: 'Price Range',\n dateRange: 'Date Range',\n measure: 'Measure',\n textTool: 'Text',\n arrow: 'Arrow',\n clearAll: 'Clear All',\n\n // Trading\n buy: 'Buy',\n sell: 'Sell',\n buyLimit: 'Buy Limit',\n sellLimit: 'Sell Limit',\n buyStop: 'Buy Stop',\n sellStop: 'Sell Stop',\n stopLoss: 'Stop Loss',\n takeProfit: 'Take Profit',\n market: 'Market',\n limit: 'Limit',\n stop: 'Stop',\n cancel: 'Cancel',\n modify: 'Modify',\n quantity: 'Qty',\n pnl: 'P&L',\n activeOrders: 'Active Orders',\n positions: 'Positions',\n noOrders: 'No active orders',\n noPositions: 'No open positions',\n placeOrder: 'Place Order',\n rightClickToTrade: 'Right-click chart to place orders',\n\n // Market\n ceiling: 'Ceiling',\n floor: 'Floor',\n reference: 'Reference',\n session: 'Session',\n preOpen: 'Pre-Open',\n continuous: 'Continuous',\n preClose: 'Pre-Close',\n closed: 'Closed',\n\n // UI\n settings: 'Settings',\n theme: 'Theme',\n darkTheme: 'Dark',\n lightTheme: 'Light',\n tools: 'Tools',\n indicators: 'Indicators',\n overlays: 'Overlays',\n panels: 'Panels',\n orders: 'Orders',\n autoScale: 'Auto Scale',\n crosshair: 'Crosshair',\n grid: 'Grid',\n loading: 'Loading...',\n error: 'Error',\n\n numberDecimalSeparator: '.',\n numberGroupSeparator: ',',\n};\n","import type { LocaleStrings } from './types.js';\n\nexport const vi: LocaleStrings = {\n // Chart types\n candlestick: 'Nến',\n line: 'Đường',\n area: 'Vùng',\n bar: 'Thanh OHLC',\n\n // Axes\n price: 'Giá',\n volume: 'Khối lượng',\n time: 'Thời gian',\n open: 'Mở',\n high: 'Cao',\n low: 'Thấp',\n close: 'Đóng',\n\n // Indicators - overlays\n sma: 'SMA',\n ema: 'EMA',\n bollingerBands: 'Dải Bollinger',\n vwap: 'VWAP',\n ichimoku: 'Mây Ichimoku',\n parabolicSAR: 'Parabolic SAR',\n supertrend: 'Supertrend',\n keltnerChannel: 'Kênh Keltner',\n donchianChannel: 'Kênh Donchian',\n\n // Indicators - panels\n rsi: 'RSI',\n macd: 'MACD',\n stochastic: 'Stochastic',\n atr: 'ATR',\n adx: 'ADX',\n obv: 'OBV',\n williamsR: 'Williams %R',\n cci: 'CCI',\n mfi: 'MFI',\n aroon: 'Aroon',\n roc: 'ROC',\n tsi: 'TSI',\n cmf: 'CMF',\n stddev: 'Độ lệch chuẩn',\n volumeProfile: 'Phân bổ KL',\n accumulationDistribution: 'Tích lũy/Phân phối',\n vroc: 'VROC',\n\n // Drawing tools\n trendLine: 'Đường xu hướng',\n horizontalLine: 'Đường ngang',\n verticalLine: 'Đường dọc',\n ray: 'Tia',\n extendedLine: 'Đường kéo dài',\n parallelChannel: 'Kênh song song',\n regressionChannel: 'Kênh hồi quy',\n fibRetracement: 'Fibonacci thoái lui',\n fibExtension: 'Fibonacci mở rộng',\n rectangle: 'Hình chữ nhật',\n ellipse: 'Hình elip',\n triangle: 'Tam giác',\n pitchfork: 'Chĩa ba Andrews',\n elliottWave: 'Sóng Elliott',\n priceRange: 'Khoảng giá',\n dateRange: 'Khoảng thời gian',\n measure: 'Đo lường',\n textTool: 'Chữ',\n arrow: 'Mũi tên',\n clearAll: 'Xóa tất cả',\n\n // Trading\n buy: 'Mua',\n sell: 'Bán',\n buyLimit: 'Mua giới hạn',\n sellLimit: 'Bán giới hạn',\n buyStop: 'Mua chặn',\n sellStop: 'Bán chặn',\n stopLoss: 'Cắt lỗ',\n takeProfit: 'Chốt lời',\n market: 'Thị trường',\n limit: 'Giới hạn',\n stop: 'Dừng',\n cancel: 'Hủy',\n modify: 'Sửa',\n quantity: 'KL',\n pnl: 'Lãi/Lỗ',\n activeOrders: 'Lệnh chờ',\n positions: 'Vị thế',\n noOrders: 'Không có lệnh chờ',\n noPositions: 'Không có vị thế mở',\n placeOrder: 'Đặt lệnh',\n rightClickToTrade: 'Nhấp chuột phải để đặt lệnh',\n\n // Market\n ceiling: 'Trần',\n floor: 'Sàn',\n reference: 'Tham chiếu',\n session: 'Phiên',\n preOpen: 'Trước giờ mở',\n continuous: 'Liên tục',\n preClose: 'Trước giờ đóng',\n closed: 'Đóng cửa',\n\n // UI\n settings: 'Cài đặt',\n theme: 'Giao diện',\n darkTheme: 'Tối',\n lightTheme: 'Sáng',\n tools: 'Công cụ',\n indicators: 'Chỉ báo',\n overlays: 'Phủ lên',\n panels: 'Bảng',\n orders: 'Lệnh',\n autoScale: 'Tự co giãn',\n crosshair: 'Chữ thập',\n grid: 'Lưới',\n loading: 'Đang tải...',\n error: 'Lỗi',\n\n numberDecimalSeparator: ',',\n numberGroupSeparator: '.',\n};\n","export type { Locale, LocaleStrings, NumberFormatConfig, DateFormatConfig } from './types.js';\nexport { en } from './en.js';\nexport { vi } from './vi.js';\n\nimport type { Locale, LocaleStrings } from './types.js';\nimport { en } from './en.js';\nimport { vi } from './vi.js';\n\nconst locales = new Map<string, LocaleStrings>([\n ['en', en],\n ['vi', vi],\n]);\n\nlet currentLocale: Locale = 'en';\nlet currentStrings: LocaleStrings = en;\n\nexport function setLocale(locale: Locale): void {\n currentLocale = locale;\n currentStrings = locales.get(locale) ?? en;\n}\n\nexport function getLocale(): Locale {\n return currentLocale;\n}\n\nexport function t(key: keyof LocaleStrings): string {\n return currentStrings[key] ?? (en as any)[key] ?? key;\n}\n\nexport function registerLocale(locale: string, strings: LocaleStrings): void {\n locales.set(locale, strings);\n}\n\nexport function getLocaleStrings(locale?: string): LocaleStrings {\n return locales.get(locale ?? currentLocale) ?? en;\n}\n\n// Number formatting\nexport function formatNumber(value: number, precision = 2, locale?: string): string {\n const strings = locales.get(locale ?? currentLocale) ?? en;\n const dec = strings.numberDecimalSeparator;\n const grp = strings.numberGroupSeparator;\n\n const fixed = value.toFixed(precision);\n const [intPart, decPart] = fixed.split('.');\n\n // Group integer part\n const negative = intPart.startsWith('-');\n const digits = negative ? intPart.slice(1) : intPart;\n let grouped = '';\n for (let i = digits.length - 1, count = 0; i >= 0; i--, count++) {\n if (count > 0 && count % 3 === 0) grouped = grp + grouped;\n grouped = digits[i] + grouped;\n }\n if (negative) grouped = '-' + grouped;\n\n return decPart ? grouped + dec + decPart : grouped;\n}\n\nexport function formatVND(value: number): string {\n return formatNumber(value, 0, 'vi');\n}\n\nexport function formatVolumeLoc(value: number, locale?: string): string {\n if (value >= 1e9) return formatNumber(value / 1e9, 2, locale ?? currentLocale) + 'B';\n if (value >= 1e6) return formatNumber(value / 1e6, 2, locale ?? currentLocale) + 'M';\n if (value >= 1e3) return formatNumber(value / 1e3, 2, locale ?? currentLocale) + 'K';\n return formatNumber(value, 0, locale ?? currentLocale);\n}\n","import type { MarketConfig, MarketColorScheme, PriceStepBand, TradingSession, VN30FContract } from './types.js';\nimport type { Theme } from '../types/theme.js';\nimport { volumeColor } from '../constants/themes.js';\nimport { stepDecimals } from '../utils/symbols.js';\nimport { isValidTimeZone, zoneOffsetMinutes } from '../utils/timezone.js';\n\n/**\n * Vietnamese price boards: green up, red down, purple at the ceiling (trần),\n * cyan at the floor (sàn), yellow at the reference price (tham chiếu).\n */\nexport const VN_COLORS: MarketColorScheme = {\n up: '#22C55E',\n down: '#EF4444',\n unchanged: '#EAB308',\n ceiling: '#D946EF',\n floor: '#06B6D4',\n reference: '#EAB308',\n};\n\nconst VN_TIME_ZONE = 'Asia/Ho_Chi_Minh';\n\n// Session times and rules: the exchanges' trading rules as of May 2025 (the\n// HOSE KRX system went live on 5 May 2025).\nexport const HOSE_SESSIONS: TradingSession[] = [\n { name: 'ATO', startTime: '09:00', endTime: '09:15', type: 'preOpen' },\n { name: 'Phiên 1', startTime: '09:15', endTime: '11:30', type: 'continuous' },\n { name: 'Nghỉ trưa', startTime: '11:30', endTime: '13:00', type: 'closed' },\n { name: 'Phiên 2', startTime: '13:00', endTime: '14:30', type: 'continuous' },\n { name: 'ATC', startTime: '14:30', endTime: '14:45', type: 'preClose' },\n];\n\nexport const HNX_SESSIONS: TradingSession[] = [\n { name: 'Phiên 1', startTime: '09:15', endTime: '11:30', type: 'continuous' },\n { name: 'Nghỉ trưa', startTime: '11:30', endTime: '13:00', type: 'closed' },\n { name: 'Phiên 2', startTime: '13:00', endTime: '14:30', type: 'continuous' },\n { name: 'ATC', startTime: '14:30', endTime: '14:45', type: 'preClose' },\n { name: 'PLO', startTime: '14:45', endTime: '15:00', type: 'postClose' },\n];\n\nexport const UPCOM_SESSIONS: TradingSession[] = [\n { name: 'Phiên 1', startTime: '09:15', endTime: '11:30', type: 'continuous' },\n { name: 'Nghỉ trưa', startTime: '11:30', endTime: '13:00', type: 'closed' },\n { name: 'Phiên 2', startTime: '13:00', endTime: '14:30', type: 'continuous' },\n];\n\nexport const VN30F_SESSIONS: TradingSession[] = [\n { name: 'ATO', startTime: '08:45', endTime: '09:00', type: 'preOpen' },\n { name: 'Phiên 1', startTime: '09:00', endTime: '11:30', type: 'continuous' },\n { name: 'Nghỉ trưa', startTime: '11:30', endTime: '13:00', type: 'closed' },\n { name: 'Phiên 2', startTime: '13:00', endTime: '14:30', type: 'continuous' },\n { name: 'ATC', startTime: '14:30', endTime: '14:45', type: 'preClose' },\n];\n\n/** HOSE shares and closed funds, in thousands of dong: 10, 50 and 100 dong steps. */\nconst HOSE_STEPS: readonly PriceStepBand[] = [\n { from: 0, step: 0.01 },\n { from: 10, step: 0.05 },\n { from: 50, step: 0.1 },\n];\n\n// Market presets. Vietnamese prices are in thousands of dong (25.35 is 25,350\n// dong), as price boards show them; scaleMarketPrices(preset, 1000) for dong.\nexport const MARKET_HOSE: MarketConfig = {\n type: 'stock',\n exchange: 'HOSE',\n currency: 'VND',\n pricePrecision: 2,\n volumeUnit: 100,\n priceStep: 0.01,\n priceSteps: HOSE_STEPS,\n timeZone: VN_TIME_ZONE,\n priceLimits: { enabled: true, ceilingPercent: 7, floorPercent: 7 },\n sessions: HOSE_SESSIONS,\n colorScheme: VN_COLORS,\n};\n\n/** HOSE ETFs and covered warrants: 10 dong at every price. */\nexport const MARKET_HOSE_ETF: MarketConfig = {\n ...MARKET_HOSE,\n priceSteps: undefined,\n};\n\nexport const MARKET_HNX: MarketConfig = {\n type: 'stock',\n exchange: 'HNX',\n currency: 'VND',\n pricePrecision: 1,\n volumeUnit: 100,\n priceStep: 0.1,\n timeZone: VN_TIME_ZONE,\n priceLimits: { enabled: true, ceilingPercent: 10, floorPercent: 10 },\n sessions: HNX_SESSIONS,\n colorScheme: VN_COLORS,\n};\n\nexport const MARKET_UPCOM: MarketConfig = {\n type: 'stock',\n exchange: 'UPCOM',\n currency: 'VND',\n pricePrecision: 1,\n volumeUnit: 100,\n priceStep: 0.1,\n timeZone: VN_TIME_ZONE,\n priceLimits: { enabled: true, ceilingPercent: 15, floorPercent: 15 },\n sessions: UPCOM_SESSIONS,\n colorScheme: VN_COLORS,\n};\n\n/** VN30 index futures on HNX: index points, 0.1 a step, 100,000 dong a point. */\nexport const MARKET_VN30F: MarketConfig = {\n type: 'futures',\n exchange: 'HNX',\n currency: 'VND',\n pricePrecision: 1,\n volumeUnit: 1,\n priceStep: 0.1,\n contractMultiplier: 100_000,\n timeZone: VN_TIME_ZONE,\n priceLimits: { enabled: true, ceilingPercent: 7, floorPercent: 7 },\n sessions: VN30F_SESSIONS,\n colorScheme: VN_COLORS,\n};\n\nexport const MARKET_CRYPTO: MarketConfig = {\n type: 'crypto',\n currency: 'USDT',\n pricePrecision: 2,\n priceLimits: { enabled: false },\n};\n\nexport const MARKET_NYSE: MarketConfig = {\n type: 'stock',\n exchange: 'NYSE',\n currency: 'USD',\n pricePrecision: 2,\n priceStep: 0.01,\n timeZone: 'America/New_York',\n priceLimits: { enabled: false },\n sessions: [\n { name: 'Pre-Market', startTime: '04:00', endTime: '09:30', type: 'preOpen' },\n { name: 'Regular', startTime: '09:30', endTime: '16:00', type: 'continuous' },\n { name: 'After-Hours', startTime: '16:00', endTime: '20:00', type: 'postClose' },\n ],\n};\n\n// Build a theme variant for VN stock market\nexport function createVNTheme(base: Theme): Theme {\n return {\n ...base,\n candleUp: VN_COLORS.up,\n candleDown: VN_COLORS.down,\n candleUpWick: VN_COLORS.up,\n candleDownWick: VN_COLORS.down,\n volumeUp: volumeColor(VN_COLORS.up),\n volumeDown: volumeColor(VN_COLORS.down),\n };\n}\n\n/** Rounding slack, so 27.1 / 0.05 (541.999…) still counts as 542 steps. */\nconst STEP_EPSILON = 1e-9;\n\n/**\n * The price step at `price`: the band it falls in, else the market's\n * `priceStep`; undefined when the market names no step.\n */\nexport function priceStepAt(price: number, config: MarketConfig): number | undefined {\n let step = config.priceStep;\n for (const band of config.priceSteps ?? []) {\n if (price + STEP_EPSILON >= band.from) step = band.step;\n }\n return step !== undefined && step > 0 ? step : undefined;\n}\n\n/** The step of the band just below `price`, for one step down from it. */\nfunction stepBelow(price: number, config: MarketConfig): number | undefined {\n let step = config.priceStep;\n for (const band of config.priceSteps ?? []) {\n if (price - STEP_EPSILON > band.from) step = band.step;\n }\n return step !== undefined && step > 0 ? step : undefined;\n}\n\n/**\n * `price` on the step of its band: to the nearest step, or `'down'` / `'up'`.\n * A market without a step leaves it as it is.\n */\nexport function roundToPriceStep(price: number, config: MarketConfig, mode: 'nearest' | 'down' | 'up' = 'nearest'): number {\n const step = priceStepAt(price, config);\n if (step === undefined || !Number.isFinite(price)) return price;\n const steps = price / step;\n const count = mode === 'down'\n ? Math.floor(steps + STEP_EPSILON)\n : mode === 'up'\n ? Math.ceil(steps - STEP_EPSILON)\n : Math.round(steps);\n return Number((count * step).toFixed(stepDecimals(step)));\n}\n\n/**\n * The day's ceiling, floor and reference prices. With a price step, the\n * ceiling rounds down and the floor up to a valid price; when one would land\n * back on the reference it moves a step off it, and a reference of one step\n * keeps the floor at the reference (the exchanges' rules).\n */\nexport function computePriceLimits(referencePrice: number, config: MarketConfig): { ceiling: number; floor: number; reference: number } | null {\n if (!config.priceLimits?.enabled || !config.priceLimits.ceilingPercent) return null;\n const ceilPct = config.priceLimits.ceilingPercent / 100;\n const floorPct = (config.priceLimits.floorPercent ?? config.priceLimits.ceilingPercent) / 100;\n const reference = referencePrice;\n const rawCeiling = reference * (1 + ceilPct);\n const rawFloor = reference * (1 - floorPct);\n const refStep = priceStepAt(reference, config);\n if (refStep === undefined) return { ceiling: rawCeiling, floor: rawFloor, reference };\n\n const oneUp = roundToPriceStep(reference + refStep, config);\n if (Math.abs(reference - refStep) < STEP_EPSILON) return { ceiling: oneUp, floor: reference, reference };\n\n let ceiling = roundToPriceStep(rawCeiling, config, 'down');\n let floor = roundToPriceStep(rawFloor, config, 'up');\n if (ceiling <= reference) ceiling = oneUp;\n if (floor >= reference) floor = roundToPriceStep(reference - (stepBelow(reference, config) ?? refStep), config);\n if (floor <= 0) floor = reference;\n return { ceiling, floor, reference };\n}\n\n/** `value * factor` without float dust (0.05 * 1000 is 50, not 50.00000000000001). */\nconst scaled = (value: number, factor: number) => Math.round(value * factor * 1e9) / 1e9;\n\n/**\n * A copy of `config` for prices `factor` times larger (a power of ten):\n * steps, bands, the reference price and the decimals follow; a contract's\n * multiplier stays. `scaleMarketPrices(MARKET_HOSE, 1000)` reads prices in\n * dong (25350) instead of thousands (25.35).\n */\nexport function scaleMarketPrices(config: MarketConfig, factor: number): MarketConfig {\n const decimalsLess = Math.round(Math.log10(factor));\n return {\n ...config,\n pricePrecision: config.pricePrecision === undefined ? undefined : Math.max(0, config.pricePrecision - decimalsLess),\n priceStep: config.priceStep === undefined ? undefined : scaled(config.priceStep, factor),\n priceSteps: config.priceSteps?.map((band) => ({ from: scaled(band.from, factor), step: scaled(band.step, factor) })),\n priceLimits: config.priceLimits && {\n ...config.priceLimits,\n referencePrice: config.priceLimits.referencePrice === undefined ? undefined : scaled(config.priceLimits.referencePrice, factor),\n },\n };\n}\n\nconst MINUTE_MS = 60_000;\nconst DAY_MS = 86_400_000;\n\n/** `HH:mm` of `now` in `timeZone`, or on this computer's clock without one. */\nfunction wallClock(now: number, timeZone?: string): string {\n let hours: number;\n let minutes: number;\n if (timeZone) {\n const local = new Date(now + zoneOffsetMinutes(timeZone, now) * MINUTE_MS);\n hours = local.getUTCHours();\n minutes = local.getUTCMinutes();\n } else {\n const local = new Date(now);\n hours = local.getHours();\n minutes = local.getMinutes();\n }\n return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;\n}\n\n/**\n * The session `now` falls in: by the clock of `timeZone` (a market's\n * `timeZone`), or of this computer without one. Null outside them all, and\n * for a time zone it doesn't know. By the time of day only: weekends and\n * holidays are yours to tell.\n */\nexport function getCurrentSession(sessions: TradingSession[], now: number = Date.now(), timeZone?: string): TradingSession | null {\n if (timeZone !== undefined && !isValidTimeZone(timeZone)) return null;\n const hhmm = wallClock(now, timeZone);\n for (const session of sessions) {\n if (hhmm >= session.startTime && hhmm < session.endTime) return session;\n }\n return null;\n}\n\nconst pad2 = (n: number) => String(n).padStart(2, '0');\nconst isoDay = (ms: number) => {\n const d = new Date(ms);\n return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;\n};\nconst THURSDAY = 4;\n/** Vietnam is UTC+7 all year. */\nconst VN_OFFSET_MS = 7 * 3_600_000;\n\n/**\n * The last trading day of the VN30 futures contract of `month` (1–12): the\n * third Thursday, or the trading day before it when that is a holiday\n * (`isHoliday` gets `YYYY-MM-DD`). As `YYYY-MM-DD`.\n */\nexport function vn30fLastTradingDay(year: number, month: number, isHoliday?: (day: string) => boolean): string {\n const first = Date.UTC(year, month - 1, 1);\n const firstThursday = 1 + ((THURSDAY - new Date(first).getUTCDay() + 7) % 7);\n let day = Date.UTC(year, month - 1, firstThursday + 14);\n const closed = (ms: number) => {\n const weekday = new Date(ms).getUTCDay();\n return weekday === 0 || weekday === 6 || (isHoliday?.(isoDay(ms)) ?? false);\n };\n while (closed(day)) day -= DAY_MS;\n return isoDay(day);\n}\n\n/**\n * The VN30 futures contracts listed at `now`: this month's (until its last\n * trading day), the next month's, and the last months of the two quarters\n * after that.\n */\nexport function vn30fContracts(now: number, isHoliday?: (day: string) => boolean): VN30FContract[] {\n const today = isoDay(now + VN_OFFSET_MS);\n const contract = (year: number, month: number): VN30FContract => ({\n symbol: `VN30F${pad2(year % 100)}${pad2(month)}`,\n year,\n month,\n lastTradingDay: vn30fLastTradingDay(year, month, isHoliday),\n });\n const next = (c: VN30FContract) => (c.month === 12 ? contract(c.year + 1, 1) : contract(c.year, c.month + 1));\n\n const vnNow = new Date(now + VN_OFFSET_MS);\n let front = contract(vnNow.getUTCFullYear(), vnNow.getUTCMonth() + 1);\n if (today > front.lastTradingDay) front = next(front);\n const listed = [front, next(front)];\n let candidate = next(listed[1]);\n while (listed.length < 4) {\n if (candidate.month % 3 === 0) listed.push(candidate);\n candidate = next(candidate);\n }\n return listed;\n}\n","/**\n * The TradeCanvas icon set, as in the \"TradeCanvas Icons\" Figma file: a 24 px\n * grid with 2 px padding, 1.75 px strokes with round caps and joins. Drawing\n * tools mark their anchor points (where you click to draw) with filled dots.\n */\nexport interface IconDef {\n /** Stroked path data. */\n d?: string;\n /** Dashed stroke, for guides. */\n dash?: string;\n /** Solid shapes, such as filled candle bodies. */\n fills?: readonly string[];\n /** An area filled at 22%. */\n soft?: string;\n /** Filled dots: [cx, cy, r = 1.75]. */\n dots?: readonly (readonly number[])[];\n}\n\n/** Toolbar, sidebar and panel glyphs. */\nexport const UI_ICONS: Readonly<Record<string, IconDef>> = {\n cursor: { d: 'M6 3.5L18.5 12.5L12.6 13.6L9.7 19.4z' }, // Cursor\n trendingUp: { d: 'M3 17L9 11L13 14.5L21 6.5M15 6.5H21V12.5' }, // Indicators / Long\n trendingDown: { d: 'M3 7L9 13L13 9.5L21 17.5M15 17.5H21V11.5' }, // Short\n minus: { d: 'M5 12H19' }, // Minus\n plus: { d: 'M12 5V19M5 12H19' }, // Plus\n penLine: { d: 'M14.5 5.5L18.5 9.5M4 20L5 15.5L15.8 4.7a2 2 0 0 1 2.83 0l.67.67a2 2 0 0 1 0 2.83L8.5 19z' }, // Draw\n zigzag: { d: 'M3 17L8 9L13 15L21 6' }, // Patterns\n hash: { d: 'M5 9H19M5 15H19M10 4L8.5 20M15.5 4L14 20' }, // Grid\n square: { d: 'M6.5 4.5h11a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2h-11a2 2 0 0 1-2-2v-11a2 2 0 0 1 2-2z' }, // Shape\n gitBranch: { d: 'M6 3V15M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM18 9a9 9 0 0 1-9 9' }, // Branch\n ruler: { d: 'M3.5 16.5L16.5 3.5L20.5 7.5L7.5 20.5zM6.5 13.5L8 15M9.5 10.5L11.5 12.5M12.5 7.5L14 9' }, // Measure\n type: { d: 'M6 6V4.5H18V6M12 4.5V19.5M9.5 19.5H14.5' }, // Text\n magnet: { d: 'M6 4V11a6 6 0 0 0 12 0V4M6 4h3.5v7a2.5 2.5 0 0 0 5 0V4H18M6 8h3.5M14.5 8H18' }, // Magnet\n magnetStrong: { d: 'M6 4V11a6 6 0 0 0 12 0V4M6 4h3.5v7a2.5 2.5 0 0 0 5 0V4H18', fills: ['M6 4h3.5v4H6z', 'M14.5 4H18v4h-3.5z'] }, // Strong magnet\n eraser: { d: 'M8.5 19.5L3.9 14.9a1.5 1.5 0 0 1 0-2.1l8.5-8.5a1.5 1.5 0 0 1 2.1 0l5.2 5.2a1.5 1.5 0 0 1 0 2.1L12 19.5zM8 9l7 7M12 19.5H20' }, // Eraser\n zoomIn: { d: 'M16.5 10.5a6 6 0 1 1-12 0 6 6 0 0 1 12 0zM15 15L20.5 20.5M10.5 7.5V13.5M7.5 10.5H13.5' }, // Zoom area\n undo: { d: 'M9 14L4 9L9 4M4 9H14.5a5.5 5.5 0 0 1 0 11H11' }, // Undo\n redo: { d: 'M15 14L20 9L15 4M20 9H9.5a5.5 5.5 0 0 0 0 11H13' }, // Redo\n trash: { d: 'M4 7H20M9.5 7V4.5h5V7M6.5 7l.8 12.2A1.5 1.5 0 0 0 8.8 20.6h6.4a1.5 1.5 0 0 0 1.5-1.4L17.5 7M10 11v6M14 11v6' }, // Delete\n camera: { d: 'M4.5 7.5h3L9 5h6l1.5 2.5h3A1.5 1.5 0 0 1 21 9v9.5a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 3 18.5V9a1.5 1.5 0 0 1 1.5-1.5zM15.5 13.5a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0z' }, // Screenshot\n settings: { d: 'M4 7H14M18 7H20M4 17H8M12 17H20M18 7a2 2 0 1 1-4 0 2 2 0 0 1 4 0zM12 17a2 2 0 1 1-4 0 2 2 0 0 1 4 0z' }, // Settings\n moon: { d: 'M20 14.5A8 8 0 1 1 9.5 4a6.5 6.5 0 0 0 10.5 10.5z' }, // Dark theme\n sun: { d: 'M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0zM12 2.5v2M12 19.5v2M4.6 4.6l1.4 1.4M18 18l1.4 1.4M2.5 12h2M19.5 12h2M4.6 19.4L6 18M18 6l1.4-1.4' }, // Light theme\n chevronDown: { d: 'M6 9L12 15L18 9' }, // Expand\n chevronUp: { d: 'M6 15L12 9L18 15' }, // Collapse\n barChart: { d: 'M5 20V13M12 20V5M19 20V10' }, // Chart type\n x: { d: 'M6 6L18 18M18 6L6 18' }, // Close\n play: { d: 'M7 4.5v15L19.5 12z' }, // Play\n pause: { d: 'M8 5V19M16 5V19' }, // Pause\n stop: { d: 'M7.5 6h9A1.5 1.5 0 0 1 18 7.5v9a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 6 16.5v-9A1.5 1.5 0 0 1 7.5 6z' }, // Stop\n receipt: { d: 'M6 3.5h12v17l-2-1.25-2 1.25-2-1.25-2 1.25-2-1.25-2 1.25zM9 8h6M9 12h6M9 16h3' }, // Orders\n bell: { d: 'M6 16.5V11a6 6 0 0 1 12 0v5.5l1.5 2H4.5zM10 20.5a2.2 2.2 0 0 0 4 0' }, // Alerts\n layers: { d: 'M12 3.5L21 8.5L12 13.5L3 8.5zM3 12.5L12 17.5L21 12.5M3 16.5L12 21.5L21 16.5' }, // Objects\n eye: { d: 'M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12zM15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0z' }, // Show\n eyeOff: { d: 'M4 4L20 20M9.9 5.8A9.6 9.6 0 0 1 12 5.5c6 0 9.5 6.5 9.5 6.5a16 16 0 0 1-2.3 3.1M6.6 7.1C3.9 8.9 2.5 12 2.5 12S6 18.5 12 18.5a9.3 9.3 0 0 0 4.7-1.3M10 10.1a3 3 0 0 0 4 4' }, // Hide\n lock: { d: 'M7 10.5h10a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2zM8 10.5V7.5a4 4 0 0 1 8 0v3' }, // Lock\n unlock: { d: 'M7 10.5h10a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2zM8 10.5V7.5a4 4 0 0 1 7.7-1.5' }, // Unlock\n palette: { d: 'M12 3.5a8.5 8.5 0 0 0 0 17c1.1 0 1.6-.8 1.6-1.6 0-.5-.2-.9-.5-1.2a1.6 1.6 0 0 1 1.2-2.7h1.9a4.3 4.3 0 0 0 4.3-4.3C20.5 6.9 16.7 3.5 12 3.5z', dots: [[7.5, 11.5, 1.25], [9.5, 7.75, 1.25], [14, 7.25, 1.25]] }, // Style\n check: { d: 'M5 12.5L10 17.5L19 7' }, // Done\n maximize: { d: 'M8 4H5a1 1 0 0 0-1 1v3M16 4h3a1 1 0 0 1 1 1v3M20 16v3a1 1 0 0 1-1 1h-3M8 20H5a1 1 0 0 1-1-1v-3' }, // Fullscreen\n minimize: { d: 'M4 8h3a1 1 0 0 0 1-1V4M16 4v3a1 1 0 0 0 1 1h3M20 16h-3a1 1 0 0 0-1 1v3M8 20v-3a1 1 0 0 0-1-1H4' }, // Exit fullscreen\n repeat: { d: 'M17 3.5L20.5 7L17 10.5M3.5 11.5V10a3 3 0 0 1 3-3h14M7 20.5L3.5 17L7 13.5M20.5 12.5V14a3 3 0 0 1-3 3h-14' }, // Stay in drawing mode\n info: { d: 'M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0zM12 11V16.5', dots: [[12, 7.75, 1.25]] }, // Symbol info\n chevronLeft: { d: 'M15 5L8 12L15 19' }, // Scroll left\n chevronRight: { d: 'M9 5L16 12L9 19' }, // Scroll right\n reset: { d: 'M4.5 12a7.5 7.5 0 1 0 2.2-5.3M4.5 4v4.5H9' }, // Reset the view\n calendar: { d: 'M5.5 5h13a2 2 0 0 1 2 2v11.5a2 2 0 0 1-2 2h-13a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2zM3.5 10H20.5M8 3V7M16 3V7' }, // Go to date\n star: { d: 'M12 3.5L14.6 9L20.5 9.6L16 13.6L17.3 19.5L12 16.5L6.7 19.5L8 13.6L3.5 9.6L9.4 9z' }, // Favourite\n pin: { d: 'M8 3.5h8M9.5 3.5v5.5L5.5 14h13l-4-5v-5.5M12 14v6.5' }, // Pinned\n search: { d: 'M16.5 10.5a6 6 0 1 1-12 0 6 6 0 0 1 12 0zM15 15L20.5 20.5' }, // Search\n enter: { d: 'M19 5v6.5a3 3 0 0 1-3 3H5.5M9.5 10L5 14.5L9.5 19' }, // Enter (select)\n stepBack: { d: 'M6 5v14M18.5 5.5v13L8.5 12z' }, // Replay: one bar back\n stepForward: { d: 'M18 5v14M5.5 5.5v13l10-6.5z' }, // Replay: one bar on\n ladder: { d: 'M4 6H11M13 6H17M6 10H11M13 10H20M8 14H11M13 14H15M5 18H11M13 18H19' }, // Depth ladder\n save: { d: 'M5.5 3.5h10.5l4.5 4.5v10.5a2 2 0 0 1-2 2h-13a2 2 0 0 1-2-2v-13a2 2 0 0 1 2-2zM8 3.5v5h7v-5M7.5 20.5v-6h9v6' }, // Save layout\n folder: { d: 'M3.5 7a2 2 0 0 1 2-2h4l2 2.5h7a2 2 0 0 1 2 2V17a2 2 0 0 1-2 2h-13a2 2 0 0 1-2-2z' }, // Open layout\n link: { d: 'M10 14a4 4 0 0 0 5.66 0l3-3a4 4 0 0 0-5.66-5.66l-1.2 1.2M14 10a4 4 0 0 0-5.66 0l-3 3a4 4 0 0 0 5.66 5.66l1.2-1.2' }, // Sync\n arrowUp: { d: 'M12 19V5M6 11L12 5L18 11' }, // Move pane up\n arrowDown: { d: 'M12 5V19M6 13L12 19L18 13' }, // Move pane down\n more: { dots: [[6, 12, 1.6], [12, 12, 1.6], [18, 12, 1.6]] }, // More\n layoutGrid: { d: 'M5 4.5h4.5a.5.5 0 0 1 .5.5v4.5a.5.5 0 0 1-.5.5H5a.5.5 0 0 1-.5-.5V5a.5.5 0 0 1 .5-.5zM14.5 4.5H19a.5.5 0 0 1 .5.5v4.5a.5.5 0 0 1-.5.5h-4.5a.5.5 0 0 1-.5-.5V5a.5.5 0 0 1 .5-.5zM5 14h4.5a.5.5 0 0 1 .5.5V19a.5.5 0 0 1-.5.5H5a.5.5 0 0 1-.5-.5v-4.5A.5.5 0 0 1 5 14zM14.5 14H19a.5.5 0 0 1 .5.5V19a.5.5 0 0 1-.5.5h-4.5a.5.5 0 0 1-.5-.5v-4.5a.5.5 0 0 1 .5-.5z' }, // Chart layout\n};\n\n/** One per drawing tool, keyed by tool type. */\nexport const DRAWING_TOOL_ICONS: Readonly<Record<string, IconDef>> = {\n trendLine: { d: 'M5 18L19 6', dots: [[5, 18], [19, 6]] }, // Trend Line\n ray: { d: 'M5 17L21 5M17.5 4.81L21 5L20.2 8.41', dots: [[5, 17], [11, 12.5]] }, // Ray\n extendedLine: { d: 'M3 18.5L21 5M17.5 4.81L21 5L20.2 8.41M6.5 18.69L3 18.5L3.8 15.09', dots: [[9, 14], [15, 9.5]] }, // Extended Line\n infoLine: { d: 'M5 20L19 10M4.5 3.5h9a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-9a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1zM6.5 6.5h5', dots: [[5, 20], [19, 10]] }, // Info Line\n trendAngle: { d: 'M5 18L19 7M5 18H20M11 18A6 6 0 0 0 9.71 14.29', dots: [[5, 18], [19, 7]] }, // Trend Angle\n horizontalLine: { d: 'M3 12H21', dots: [[12, 12]] }, // Horizontal Line\n horizontalRay: { d: 'M7 12H21M18.5 9.5L21 12L18.5 14.5', dots: [[7, 12]] }, // Horizontal Ray\n verticalLine: { d: 'M12 3V21', dots: [[12, 12]] }, // Vertical Line\n crossLine: { d: 'M3 12H21M12 3V21', dots: [[12, 12]] }, // Cross Line\n parallelChannel: { d: 'M3 15L15 5M9 19L21 9', dots: [[3, 15], [15, 5], [9, 19]] }, // Parallel Channel\n regressionChannel: { d: 'M3 12L21 4M3 20L21 12', dash: 'M3 16L21 8', dots: [[3, 16], [21, 8]] }, // Regression Channel\n fibRetracement: { d: 'M3 4H21M3 9H21M3 13H21M3 20H21', dots: [[5, 20], [19, 4]] }, // Fib Retracement\n fibExtension: { d: 'M3 20L8 10L12 16M14 4H21M14 8H21M14 12H21', dots: [[3, 20], [8, 10], [12, 16]] }, // Fib Extension\n fibChannel: { d: 'M3 9L21 3M3 13L21 7M3 16L21 10M3 21L21 15', dots: [[3, 21], [21, 15], [3, 9]] }, // Fib Channel\n fibTimeZones: { d: 'M3 4V20M6 4V20M9.5 4V20M14 4V20M20.5 4V20', dots: [[3, 20], [6, 20]] }, // Fib Time Zones\n fibSpeedResistanceFan: { d: 'M4 20L20 4M4 20L20 11M4 20L20 16M4 20L11 4', dash: 'M4 4H20V20', dots: [[4, 20], [20, 4]] }, // Fib Speed Resistance Fan\n rectangle: { d: 'M5.5 6h13A1.5 1.5 0 0 1 20 7.5v9a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 4 16.5v-9A1.5 1.5 0 0 1 5.5 6z', dots: [[4, 6], [20, 18]] }, // Rectangle\n circle: { d: 'M19.5 12a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0z', dots: [[12, 12], [19.5, 12]] }, // Circle\n ellipse: { d: 'M20.5 12c0 3-3.8 5.5-8.5 5.5S3.5 15 3.5 12 7.3 6.5 12 6.5s8.5 2.5 8.5 5.5z', dots: [[3.5, 12], [20.5, 12]] }, // Ellipse\n triangle: { d: 'M12 4.5L20 19H4z', dots: [[12, 4.5], [20, 19], [4, 19]] }, // Triangle\n pitchfork: { d: 'M4 20L16.75 3M7 9L11.5 3M13 15L21 4.33M7 9L13 15', dots: [[4, 20], [7, 9], [13, 15]] }, // Pitchfork\n schiffPitchfork: { d: 'M4 14.5L21 7.42M7 9L21 3.17M13 15L21 11.67M7 9L13 15', dash: 'M4 20V14.5', dots: [[4, 14.5], [7, 9], [13, 15]] }, // Schiff Pitchfork\n modifiedSchiffPitchfork: { d: 'M5.5 14.5L21 5.89M7 9L17.8 3M13 15L21 10.56M7 9L13 15', dash: 'M4 20L5.5 14.5', dots: [[5.5, 14.5], [7, 9], [13, 15]] }, // Modified Schiff Pitchfork\n gannFan: { d: 'M4 20L20 4M4 20L20 12M4 20L12 4M4 20L20 16.5M4 20L7.5 4', dots: [[4, 20], [20, 4]] }, // Gann Fan\n gannBox: { d: 'M4 4H20V20H4zM4 20L20 4M4 4L20 20M12 4V20', dots: [[4, 20], [20, 4]] }, // Gann Box\n cyclicLines: { d: 'M4 4V20M9 4V20M14 4V20M19 4V20', dots: [[4, 20], [9, 20]] }, // Cyclic Lines\n xabcdPattern: { d: 'M3 18L7 6L11 13L15 8L21 19', dash: 'M3 18L11 13L21 19', dots: [[3, 18], [7, 6], [11, 13], [15, 8], [21, 19]] }, // XABCD Pattern\n abcdPattern: { d: 'M4 19L9 8L13 14L20 4', dots: [[4, 19], [9, 8], [13, 14], [20, 4]] }, // ABCD Pattern\n headAndShoulders: { d: 'M3 19L6 11L8.5 15L12 5L15.5 15L18 11L21 19', dash: 'M4.5 15H19.5', dots: [[6, 11], [12, 5], [18, 11]] }, // Head and Shoulders\n elliottWave: { d: 'M3 19L6 13L8.5 16L14 6L16 10L21 4', dots: [[3, 19], [21, 4]] }, // Elliott Wave\n priceRange: { d: 'M5 4H19M5 20H19M12 6.5V17.5M9 9.5L12 6.5L15 9.5M9 14.5L12 17.5L15 14.5' }, // Price Range\n dateRange: { d: 'M4 5V19M20 5V19M6.5 12H17.5M9.5 9L6.5 12L9.5 15M14.5 9L17.5 12L14.5 15' }, // Date Range\n dateAndPriceRange: { d: 'M6 18V4M3 7L6 4L9 7M6 18H20M17 15L20 18L17 21', dash: 'M6 4H20V18' }, // Date and Price Range\n measure: { d: 'M3.5 16.5L16.5 3.5L20.5 7.5L7.5 20.5zM6.5 13.5L8 15M9.5 10.5L11.5 12.5M12.5 7.5L14 9' }, // Measure\n text: { d: 'M6 6V4.5H18V6M12 4.5V19.5M9.5 19.5H14.5' }, // Text\n priceLabel: { d: 'M9 5H19a1.5 1.5 0 0 1 1.5 1.5v11A1.5 1.5 0 0 1 19 19H9L3.5 12zM11 12H17' }, // Price Label\n arrow: { d: 'M5 19L19 5M10 5H19V14' }, // Arrow\n riskReward: { d: 'M5 12V4H19V12M3 12H21', dash: 'M5 12V18H19V12', dots: [[5, 12]] }, // Long/Short Position\n anchoredVWAP: { d: 'M4 18C9 17 12 10 20 7M4 20.5V18', dots: [[4, 18]] }, // Anchored VWAP\n volumeProfileRange: { d: 'M6 7H12M6 10.5H17M6 14H15M6 17.5H10', dash: 'M3.5 4V20M20.5 4V20', dots: [[3.5, 20], [20.5, 20]] }, // Fixed Range Volume Profile\n note: { d: 'M5.5 4.5h13v9l-5 5h-8zM13.5 18.5v-5h5M8.5 8.5h7M8.5 11.5h4' }, // Note\n callout: { d: 'M10.5 4h9a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-9a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1zM12 12L5 19M12.5 8h5', dots: [[5, 19]] }, // Callout\n flag: { d: 'M6 21V4M6 4.5h11l-2.5 3.75L17 12H6', soft: 'M6 4.5h11l-2.5 3.75L17 12H6z', dots: [[6, 21]] }, // Flag Mark\n arrowMark: { d: 'M12 4L18 11H14.5V20H9.5V11H6z' }, // Arrow Mark\n icon: { d: 'M12 3.5l2.6 5.3 5.9.85-4.25 4.15 1 5.85L12 16.9l-5.25 2.75 1-5.85L3.5 9.65l5.9-.85z' }, // Icon\n brush: { d: 'M3.5 17c2.5 0 3-5 6-5s2.5 5 5.5 5 3.5-6 5.5-9' }, // Brush\n highlighter: { d: 'M14.5 4.5l5 5L11 18H6v-5zM12 7l5 5M4 21h8' }, // Highlighter\n path: { d: 'M4 18L9 10L14 14L20 6M16.2 6.6L20 6L19.6 9.8', dots: [[4, 18], [9, 10], [14, 14]] }, // Path\n polyline: { d: 'M5 17L8 6L16 4L20 14L12 20z', soft: 'M5 17L8 6L16 4L20 14L12 20z', dots: [[5, 17], [8, 6], [16, 4], [20, 14], [12, 20]] }, // Polyline\n curve: { d: 'M4 18C8 4 16 4 20 18', dots: [[4, 18], [20, 18], [12, 7.5]] }, // Curve\n arc: { d: 'M4 17a8 8 0 0 1 16 0', dots: [[4, 17], [20, 17], [12, 9]] }, // Arc\n fibCircles: { d: 'M14 12a2 2 0 1 1-4 0 2 2 0 0 1 4 0zM16.5 12a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0zM20 12a8 8 0 1 1-16 0 8 8 0 0 1 16 0z', dots: [[12, 12], [20, 12]] }, // Fib Circles\n fibSpiral: { d: 'M12 12a1.5 1.5 0 0 1 1.5 1.5 3 3 0 0 1-3 3A4.5 4.5 0 0 1 6 12a6 6 0 0 1 6-6 8 8 0 0 1 8 8', dots: [[12, 12]] }, // Fib Spiral\n fibArcs: { d: 'M10 20a6 6 0 0 0-6-6M14 20a10 10 0 0 0-10-10M18 20a14 14 0 0 0-14-14', dash: 'M4 20L18 6', dots: [[4, 20], [18, 6]] }, // Fib Speed Resistance Arcs\n fibWedge: { d: 'M4 19L20 7M4 19L20 16M10.4 14.2A8 8 0 0 1 11.86 17.53M14.4 11.2A13 13 0 0 1 16.78 16.6', dots: [[4, 19], [20, 7], [20, 16]] }, // Fib Wedge\n pitchfan: { d: 'M4 20L14 4M4 20L20 14M4 20L17 9', dash: 'M14 4L20 14', dots: [[4, 20], [14, 4], [20, 14]] }, // Pitchfan\n gannSquare: { d: 'M4 4H20V20H4zM4 20L20 4M4 12H20M12 4V20M4 12a8 8 0 0 1 8 8', dots: [[4, 20], [20, 4]] }, // Gann Square\n elliottImpulse: { d: 'M3 19L7 12L10 15L15 5L17.5 9L21 3', dots: [[3, 19], [21, 3]] }, // Elliott Impulse Wave\n elliottCorrection: { d: 'M4 5L10 17L14 11L20 20', dots: [[4, 5], [20, 20]] }, // Elliott Correction Wave\n elliottTriangle: { d: 'M3 6L7 17L11 9L15 15L18 11L21 13', dots: [[3, 6], [21, 13]] }, // Elliott Triangle Wave\n elliottDoubleCombo: { d: 'M4 5L9 14L13 9L20 19', dots: [[4, 5], [9, 14], [13, 9], [20, 19]] }, // Elliott Double Combo Wave\n elliottTripleCombo: { d: 'M3 5L7 13L10 9L14 16L17 12L21 20', dots: [[3, 5], [21, 20]] }, // Elliott Triple Combo Wave\n threeDrives: { d: 'M3 20L7 12L9.5 15L14 7L16.5 10L21 3', dash: 'M7 12L14 7L21 3', dots: [[7, 12], [14, 7], [21, 3]] }, // Three Drives Pattern\n cypherPattern: { d: 'M3 19L7 9L10 14L15 5L20 16', dash: 'M3 19L10 14L20 16', dots: [[3, 19], [7, 9], [10, 14], [15, 5], [20, 16]] }, // Cypher Pattern\n timeCycles: { d: 'M3 18a4.5 4.5 0 0 1 9 0a4.5 4.5 0 0 1 9 0', dots: [[3, 18], [12, 18]] }, // Time Cycles\n sineLine: { d: 'M3 12C5 5 7.5 5 9 12S13 19 15 12 19.5 5 21 12', dots: [[6, 6.75], [12, 17.25]] }, // Sine Line\n forecast: { d: 'M4 18L15.2 7.8M19.5 6a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0z', dots: [[4, 18]] }, // Forecast\n projection: { d: 'M12 19L18 11M15.2 11.3L18 11L17.8 13.8', dash: 'M3 17L9 9', dots: [[3, 17], [9, 9], [12, 19]] }, // Projection\n barsPattern: { d: 'M5 6V14M9 9V18M15 4V12M19 7V16', fills: ['M4 8h2v4H4z', 'M8 11h2v5H8z', 'M14 6h2v4h-2z', 'M18 9h2v5h-2z'], dash: 'M12 3V21' }, // Bars Pattern\n};\n\n/** One per chart type, keyed by chart type. */\nexport const CHART_TYPE_ICONS: Readonly<Record<string, IconDef>> = {\n candlestick: { d: 'M8 3.5V7M8 16V20.5M16 5V8.5M16 15.5V19', fills: ['M6.5 7h3a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1z', 'M14.5 8.5h3a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-5a1 1 0 0 1 1-1z'] }, // Candlestick\n hollowCandle: { d: 'M8 3.5V7M8 16V20.5M16 5V8.5M16 15.5V19M6.5 7h3a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1zM14.5 8.5h3a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-5a1 1 0 0 1 1-1z' }, // Hollow Candle\n heikinAshi: { d: 'M5.5 9.5V12M5.5 18V20M12 5.5V8M12 14V16M18.5 3V4.5M18.5 10.5V12.5', fills: ['M4 12h3a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5H4a.5.5 0 0 1-.5-.5v-5A.5.5 0 0 1 4 12z', 'M10.5 8h3a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-5a.5.5 0 0 1 .5-.5z', 'M17 4.5h3a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5V5a.5.5 0 0 1 .5-.5z'] }, // Heikin-Ashi\n bar: { d: 'M8 4V20M5 8H8M8 16H11M16 6V18M13 15H16M16 9H19' }, // Bar (OHLC)\n volumeCandles: { d: 'M6.5 5V8M6.5 16V19M15.5 3.5V6M15.5 16V20', fills: ['M5.5 8h2a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-7a.5.5 0 0 1 .5-.5z', 'M11.5 6h8a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5z'] }, // Volume Candles\n equivolume: { d: 'M4.5 10h2a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1zM10 6h5a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zM18.5 4h1a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1z' }, // Equivolume\n line: { d: 'M3 17L8 11L12 14L21 5' }, // Line\n area: { d: 'M3 17L8 11L12 14L21 5', soft: 'M3 17L8 11L12 14L21 5V20H3z' }, // Area\n baseline: { d: 'M3 16L7 9L11 14L15 7L21 15', dash: 'M3 12H21' }, // Baseline\n hlcArea: { d: 'M3 9L8 6L13 9L21 4M3 15L8 13L13 16L21 11', dash: 'M3 12L8 9.5L13 12.5L21 7.5', soft: 'M3 9L8 6L13 9L21 4V11L13 16L8 13L3 15z' }, // HLC Area\n stepLine: { d: 'M3 18H8V12H12V15H16V7H21' }, // Step Line\n lineWithMarkers: { d: 'M4 17L9 10L14 14L20 6', dots: [[4, 17], [9, 10], [14, 14], [20, 6]] }, // Line with Markers\n hiLo: { d: 'M6 7V17M12 4V14M18 9V20', fills: ['M5 7h2v10H5z', 'M11 4h2v10h-2z', 'M17 9h2v11h-2z'] }, // High-Low\n renko: { d: 'M3.5 15.5h4v4h-4zM7.5 11.5h4v4h-4zM11.5 7.5h4v4h-4zM15.5 11.5h4v4h-4z' }, // Renko\n lineBreak: { d: 'M4 14h4v5H4zM8 10h4v4H8zM16 9h4v8h-4z', fills: ['M12 5h4v5h-4z'] }, // Line Break\n kagi: { d: 'M4 19V11H9V16H14V5H20V13' }, // Kagi\n pointAndFigure: { d: 'M5.5 5.5l3 3M8.5 5.5l-3 3M5.5 10.5l3 3M8.5 10.5l-3 3M5.5 15.5l3 3M8.5 15.5l-3 3M14.5 9a1.5 1.5 0 1 0 3 0a1.5 1.5 0 1 0-3 0M14.5 14a1.5 1.5 0 1 0 3 0a1.5 1.5 0 1 0-3 0' }, // Point & Figure\n rangeBars: { d: 'M6.5 11V12M6.5 18V19M12 7V8M12 14V15M17.5 3.5V4.5M17.5 10.5V11.5', fills: ['M5 12h3v6H5z', 'M10.5 8h3v6h-3z', 'M16 4.5h3v6h-3z'] }, // Range Bars\n};\n\nexport const ICON_DOT_RADIUS = 1.75;\nexport const ICON_SOFT_OPACITY = 0.22;\nconst markupCache = new WeakMap<IconDef, string>();\n\n/** The icon's SVG elements, drawn in `currentColor`. */\nfunction markup(icon: IconDef): string {\n const cached = markupCache.get(icon);\n if (cached) return cached;\n let s = '';\n if (icon.soft) s += `<path d=\"${icon.soft}\" fill=\"currentColor\" fill-opacity=\"${ICON_SOFT_OPACITY}\" stroke=\"none\"/>`;\n for (const f of icon.fills ?? []) s += `<path d=\"${f}\" fill=\"currentColor\" stroke=\"none\"/>`;\n if (icon.d) s += `<path d=\"${icon.d}\"/>`;\n if (icon.dash) s += `<path d=\"${icon.dash}\" stroke-dasharray=\"2 2.5\"/>`;\n for (const [cx, cy, r = ICON_DOT_RADIUS] of icon.dots ?? []) s += `<circle cx=\"${cx}\" cy=\"${cy}\" r=\"${r}\" fill=\"currentColor\" stroke=\"none\"/>`;\n markupCache.set(icon, s);\n return s;\n}\n\n/** The icon as an inline SVG string at `size` px, coloured by `currentColor`. */\nexport function iconSvg(icon: IconDef, size: number): string {\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size}\" height=\"${size}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\">${markup(icon)}</svg>`;\n}\n"],"mappings":"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"}
|