@itssumitrai/fin-charter 0.2.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/README.md +96 -0
- package/dist/api/chart-api.d.ts +173 -0
- package/dist/api/export.d.ts +46 -0
- package/dist/api/indicator-api.d.ts +38 -0
- package/dist/api/options.d.ts +226 -0
- package/dist/api/pane-api.d.ts +28 -0
- package/dist/api/series-api.d.ts +115 -0
- package/dist/core/accessibility.d.ts +90 -0
- package/dist/core/alert-line.d.ts +49 -0
- package/dist/core/chart-state.d.ts +39 -0
- package/dist/core/chart-sync.d.ts +83 -0
- package/dist/core/crosshair.d.ts +28 -0
- package/dist/core/css-theme.d.ts +41 -0
- package/dist/core/custom-indicator.d.ts +70 -0
- package/dist/core/data-feed.d.ts +59 -0
- package/dist/core/data-layer.d.ts +38 -0
- package/dist/core/invalidation.d.ts +24 -0
- package/dist/core/market-session.d.ts +11 -0
- package/dist/core/order-line.d.ts +134 -0
- package/dist/core/pane-divider.d.ts +22 -0
- package/dist/core/pane.d.ts +32 -0
- package/dist/core/periodicity.d.ts +6 -0
- package/dist/core/plugin.d.ts +90 -0
- package/dist/core/price-line.d.ts +17 -0
- package/dist/core/price-scale.d.ts +74 -0
- package/dist/core/replay.d.ts +73 -0
- package/dist/core/rtl.d.ts +22 -0
- package/dist/core/segment-tree.d.ts +38 -0
- package/dist/core/series-markers.d.ts +18 -0
- package/dist/core/storage-adapter.d.ts +50 -0
- package/dist/core/streaming-adapter.d.ts +102 -0
- package/dist/core/symbol-resolver.d.ts +32 -0
- package/dist/core/text-label.d.ts +72 -0
- package/dist/core/time-scale.d.ts +109 -0
- package/dist/core/types.d.ts +144 -0
- package/dist/core/undo-redo.d.ts +46 -0
- package/dist/currency/currency.d.ts +8 -0
- package/dist/currency/index.d.ts +2 -0
- package/dist/drawings/arrow.d.ts +10 -0
- package/dist/drawings/base.d.ts +79 -0
- package/dist/drawings/channel.d.ts +10 -0
- package/dist/drawings/crossline.d.ts +10 -0
- package/dist/drawings/ellipse.d.ts +10 -0
- package/dist/drawings/fib-arc.d.ts +10 -0
- package/dist/drawings/fib-fan.d.ts +12 -0
- package/dist/drawings/fib-projection.d.ts +11 -0
- package/dist/drawings/fibonacci.d.ts +11 -0
- package/dist/drawings/horizontal-line.d.ts +10 -0
- package/dist/drawings/index.d.ts +7 -0
- package/dist/drawings/measurement.d.ts +11 -0
- package/dist/drawings/pitchfork.d.ts +12 -0
- package/dist/drawings/ray.d.ts +12 -0
- package/dist/drawings/rectangle.d.ts +10 -0
- package/dist/drawings/text-annotation.d.ts +11 -0
- package/dist/drawings/trendline.d.ts +10 -0
- package/dist/drawings/vertical-line.d.ts +10 -0
- package/dist/formatting/index.d.ts +5 -0
- package/dist/formatting/price-formatter.d.ts +6 -0
- package/dist/formatting/time-formatter.d.ts +7 -0
- package/dist/formatting/volume-formatter.d.ts +1 -0
- package/dist/i18n/i18n.d.ts +6 -0
- package/dist/i18n/index.d.ts +3 -0
- package/dist/i18n/locales/en.d.ts +3 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index10.js +2 -0
- package/dist/index10.js.map +1 -0
- package/dist/index100.js +2 -0
- package/dist/index100.js.map +1 -0
- package/dist/index101.js +2 -0
- package/dist/index101.js.map +1 -0
- package/dist/index102.js +2 -0
- package/dist/index102.js.map +1 -0
- package/dist/index103.js +2 -0
- package/dist/index103.js.map +1 -0
- package/dist/index104.js +2 -0
- package/dist/index104.js.map +1 -0
- package/dist/index105.js +2 -0
- package/dist/index105.js.map +1 -0
- package/dist/index106.js +2 -0
- package/dist/index106.js.map +1 -0
- package/dist/index107.js +2 -0
- package/dist/index107.js.map +1 -0
- package/dist/index108.js +2 -0
- package/dist/index108.js.map +1 -0
- package/dist/index109.js +2 -0
- package/dist/index109.js.map +1 -0
- package/dist/index11.js +2 -0
- package/dist/index11.js.map +1 -0
- package/dist/index110.js +2 -0
- package/dist/index110.js.map +1 -0
- package/dist/index111.js +2 -0
- package/dist/index111.js.map +1 -0
- package/dist/index112.js +2 -0
- package/dist/index112.js.map +1 -0
- package/dist/index113.js +2 -0
- package/dist/index113.js.map +1 -0
- package/dist/index114.js +2 -0
- package/dist/index114.js.map +1 -0
- package/dist/index115.js +2 -0
- package/dist/index115.js.map +1 -0
- package/dist/index116.js +2 -0
- package/dist/index116.js.map +1 -0
- package/dist/index117.js +2 -0
- package/dist/index117.js.map +1 -0
- package/dist/index118.js +2 -0
- package/dist/index118.js.map +1 -0
- package/dist/index119.js +2 -0
- package/dist/index119.js.map +1 -0
- package/dist/index12.js +2 -0
- package/dist/index12.js.map +1 -0
- package/dist/index120.js +2 -0
- package/dist/index120.js.map +1 -0
- package/dist/index121.js +2 -0
- package/dist/index121.js.map +1 -0
- package/dist/index122.js +2 -0
- package/dist/index122.js.map +1 -0
- package/dist/index123.js +2 -0
- package/dist/index123.js.map +1 -0
- package/dist/index124.js +2 -0
- package/dist/index124.js.map +1 -0
- package/dist/index13.js +2 -0
- package/dist/index13.js.map +1 -0
- package/dist/index14.js +2 -0
- package/dist/index14.js.map +1 -0
- package/dist/index15.js +2 -0
- package/dist/index15.js.map +1 -0
- package/dist/index16.js +2 -0
- package/dist/index16.js.map +1 -0
- package/dist/index17.js +2 -0
- package/dist/index17.js.map +1 -0
- package/dist/index18.js +2 -0
- package/dist/index18.js.map +1 -0
- package/dist/index19.js +2 -0
- package/dist/index19.js.map +1 -0
- package/dist/index2.js +2 -0
- package/dist/index2.js.map +1 -0
- package/dist/index20.js +2 -0
- package/dist/index20.js.map +1 -0
- package/dist/index21.js +2 -0
- package/dist/index21.js.map +1 -0
- package/dist/index22.js +2 -0
- package/dist/index22.js.map +1 -0
- package/dist/index23.js +2 -0
- package/dist/index23.js.map +1 -0
- package/dist/index24.js +2 -0
- package/dist/index24.js.map +1 -0
- package/dist/index25.js +2 -0
- package/dist/index25.js.map +1 -0
- package/dist/index26.js +2 -0
- package/dist/index26.js.map +1 -0
- package/dist/index27.js +2 -0
- package/dist/index27.js.map +1 -0
- package/dist/index28.js +2 -0
- package/dist/index28.js.map +1 -0
- package/dist/index29.js +2 -0
- package/dist/index29.js.map +1 -0
- package/dist/index3.js +2 -0
- package/dist/index3.js.map +1 -0
- package/dist/index30.js +2 -0
- package/dist/index30.js.map +1 -0
- package/dist/index31.js +2 -0
- package/dist/index31.js.map +1 -0
- package/dist/index32.js +2 -0
- package/dist/index32.js.map +1 -0
- package/dist/index33.js +2 -0
- package/dist/index33.js.map +1 -0
- package/dist/index34.js +2 -0
- package/dist/index34.js.map +1 -0
- package/dist/index35.js +2 -0
- package/dist/index35.js.map +1 -0
- package/dist/index36.js +2 -0
- package/dist/index36.js.map +1 -0
- package/dist/index37.js +2 -0
- package/dist/index37.js.map +1 -0
- package/dist/index38.js +2 -0
- package/dist/index38.js.map +1 -0
- package/dist/index39.js +2 -0
- package/dist/index39.js.map +1 -0
- package/dist/index4.js +2 -0
- package/dist/index4.js.map +1 -0
- package/dist/index40.js +2 -0
- package/dist/index40.js.map +1 -0
- package/dist/index41.js +2 -0
- package/dist/index41.js.map +1 -0
- package/dist/index42.js +2 -0
- package/dist/index42.js.map +1 -0
- package/dist/index43.js +2 -0
- package/dist/index43.js.map +1 -0
- package/dist/index44.js +2 -0
- package/dist/index44.js.map +1 -0
- package/dist/index45.js +2 -0
- package/dist/index45.js.map +1 -0
- package/dist/index46.js +2 -0
- package/dist/index46.js.map +1 -0
- package/dist/index47.js +2 -0
- package/dist/index47.js.map +1 -0
- package/dist/index48.js +2 -0
- package/dist/index48.js.map +1 -0
- package/dist/index49.js +2 -0
- package/dist/index49.js.map +1 -0
- package/dist/index5.js +2 -0
- package/dist/index5.js.map +1 -0
- package/dist/index50.js +2 -0
- package/dist/index50.js.map +1 -0
- package/dist/index51.js +2 -0
- package/dist/index51.js.map +1 -0
- package/dist/index52.js +2 -0
- package/dist/index52.js.map +1 -0
- package/dist/index53.js +2 -0
- package/dist/index53.js.map +1 -0
- package/dist/index54.js +2 -0
- package/dist/index54.js.map +1 -0
- package/dist/index55.js +2 -0
- package/dist/index55.js.map +1 -0
- package/dist/index56.js +2 -0
- package/dist/index56.js.map +1 -0
- package/dist/index57.js +2 -0
- package/dist/index57.js.map +1 -0
- package/dist/index58.js +2 -0
- package/dist/index58.js.map +1 -0
- package/dist/index59.js +2 -0
- package/dist/index59.js.map +1 -0
- package/dist/index6.js +2 -0
- package/dist/index6.js.map +1 -0
- package/dist/index60.js +2 -0
- package/dist/index60.js.map +1 -0
- package/dist/index61.js +2 -0
- package/dist/index61.js.map +1 -0
- package/dist/index62.js +2 -0
- package/dist/index62.js.map +1 -0
- package/dist/index63.js +2 -0
- package/dist/index63.js.map +1 -0
- package/dist/index64.js +2 -0
- package/dist/index64.js.map +1 -0
- package/dist/index65.js +3 -0
- package/dist/index65.js.map +1 -0
- package/dist/index66.js +2 -0
- package/dist/index66.js.map +1 -0
- package/dist/index67.js +2 -0
- package/dist/index67.js.map +1 -0
- package/dist/index68.js +2 -0
- package/dist/index68.js.map +1 -0
- package/dist/index69.js +2 -0
- package/dist/index69.js.map +1 -0
- package/dist/index7.js +2 -0
- package/dist/index7.js.map +1 -0
- package/dist/index70.js +2 -0
- package/dist/index70.js.map +1 -0
- package/dist/index71.js +2 -0
- package/dist/index71.js.map +1 -0
- package/dist/index72.js +2 -0
- package/dist/index72.js.map +1 -0
- package/dist/index73.js +2 -0
- package/dist/index73.js.map +1 -0
- package/dist/index74.js +2 -0
- package/dist/index74.js.map +1 -0
- package/dist/index75.js +2 -0
- package/dist/index75.js.map +1 -0
- package/dist/index76.js +2 -0
- package/dist/index76.js.map +1 -0
- package/dist/index77.js +2 -0
- package/dist/index77.js.map +1 -0
- package/dist/index78.js +2 -0
- package/dist/index78.js.map +1 -0
- package/dist/index79.js +2 -0
- package/dist/index79.js.map +1 -0
- package/dist/index8.js +2 -0
- package/dist/index8.js.map +1 -0
- package/dist/index80.js +2 -0
- package/dist/index80.js.map +1 -0
- package/dist/index81.js +2 -0
- package/dist/index81.js.map +1 -0
- package/dist/index82.js +2 -0
- package/dist/index82.js.map +1 -0
- package/dist/index83.js +2 -0
- package/dist/index83.js.map +1 -0
- package/dist/index84.js +2 -0
- package/dist/index84.js.map +1 -0
- package/dist/index85.js +2 -0
- package/dist/index85.js.map +1 -0
- package/dist/index86.js +2 -0
- package/dist/index86.js.map +1 -0
- package/dist/index87.js +2 -0
- package/dist/index87.js.map +1 -0
- package/dist/index88.js +2 -0
- package/dist/index88.js.map +1 -0
- package/dist/index89.js +2 -0
- package/dist/index89.js.map +1 -0
- package/dist/index9.js +2 -0
- package/dist/index9.js.map +1 -0
- package/dist/index90.js +2 -0
- package/dist/index90.js.map +1 -0
- package/dist/index91.js +2 -0
- package/dist/index91.js.map +1 -0
- package/dist/index92.js +2 -0
- package/dist/index92.js.map +1 -0
- package/dist/index93.js +2 -0
- package/dist/index93.js.map +1 -0
- package/dist/index94.js +2 -0
- package/dist/index94.js.map +1 -0
- package/dist/index95.js +2 -0
- package/dist/index95.js.map +1 -0
- package/dist/index96.js +2 -0
- package/dist/index96.js.map +1 -0
- package/dist/index97.js +2 -0
- package/dist/index97.js.map +1 -0
- package/dist/index98.js +2 -0
- package/dist/index98.js.map +1 -0
- package/dist/index99.js +2 -0
- package/dist/index99.js.map +1 -0
- package/dist/indicators/adx.d.ts +6 -0
- package/dist/indicators/aroon.d.ts +5 -0
- package/dist/indicators/atr.d.ts +1 -0
- package/dist/indicators/awesome-oscillator.d.ts +1 -0
- package/dist/indicators/bollinger.d.ts +6 -0
- package/dist/indicators/cci.d.ts +1 -0
- package/dist/indicators/chaikin-mf.d.ts +1 -0
- package/dist/indicators/choppiness.d.ts +1 -0
- package/dist/indicators/coppock.d.ts +1 -0
- package/dist/indicators/donchian.d.ts +6 -0
- package/dist/indicators/elder-force.d.ts +1 -0
- package/dist/indicators/ema.d.ts +1 -0
- package/dist/indicators/ichimoku.d.ts +8 -0
- package/dist/indicators/index.d.ts +31 -0
- package/dist/indicators/keltner.d.ts +6 -0
- package/dist/indicators/linear-regression.d.ts +1 -0
- package/dist/indicators/macd.d.ts +6 -0
- package/dist/indicators/mfi.d.ts +1 -0
- package/dist/indicators/obv.d.ts +1 -0
- package/dist/indicators/parabolic-sar.d.ts +1 -0
- package/dist/indicators/pivot-points.d.ts +10 -0
- package/dist/indicators/roc.d.ts +1 -0
- package/dist/indicators/rsi.d.ts +1 -0
- package/dist/indicators/sma.d.ts +1 -0
- package/dist/indicators/stochastic.d.ts +5 -0
- package/dist/indicators/supertrend.d.ts +5 -0
- package/dist/indicators/trix.d.ts +5 -0
- package/dist/indicators/utils.d.ts +20 -0
- package/dist/indicators/volume-profile.d.ts +37 -0
- package/dist/indicators/volume.d.ts +1 -0
- package/dist/indicators/vwap.d.ts +1 -0
- package/dist/indicators/vwma.d.ts +1 -0
- package/dist/indicators/williams-r.d.ts +1 -0
- package/dist/interactions/axis-drag.d.ts +24 -0
- package/dist/interactions/context-menu-handler.d.ts +42 -0
- package/dist/interactions/crosshair.d.ts +20 -0
- package/dist/interactions/drawing-handler.d.ts +39 -0
- package/dist/interactions/event-router.d.ts +37 -0
- package/dist/interactions/keyboard-nav.d.ts +11 -0
- package/dist/interactions/pan-zoom.d.ts +39 -0
- package/dist/interactions/range-selection.d.ts +103 -0
- package/dist/interactions/touch-gestures.d.ts +36 -0
- package/dist/logo.svg +40 -0
- package/dist/market/exchange-map.d.ts +3 -0
- package/dist/market/index.d.ts +4 -0
- package/dist/market/market-calendar.d.ts +4 -0
- package/dist/market/market-definition.d.ts +22 -0
- package/dist/mockServiceWorker.js +349 -0
- package/dist/renderers/area.d.ts +17 -0
- package/dist/renderers/bar-ohlc.d.ts +17 -0
- package/dist/renderers/baseline-delta-mountain.d.ts +22 -0
- package/dist/renderers/baseline.d.ts +19 -0
- package/dist/renderers/candlestick.d.ts +21 -0
- package/dist/renderers/canvas-renderer.d.ts +28 -0
- package/dist/renderers/colored-line.d.ts +15 -0
- package/dist/renderers/colored-mountain.d.ts +18 -0
- package/dist/renderers/column.d.ts +14 -0
- package/dist/renderers/high-low.d.ts +14 -0
- package/dist/renderers/histogram.d.ts +16 -0
- package/dist/renderers/hlc-area.d.ts +16 -0
- package/dist/renderers/hollow-candle.d.ts +18 -0
- package/dist/renderers/kagi.d.ts +22 -0
- package/dist/renderers/line-break.d.ts +20 -0
- package/dist/renderers/line.d.ts +17 -0
- package/dist/renderers/point-figure.d.ts +20 -0
- package/dist/renderers/renderer.d.ts +59 -0
- package/dist/renderers/renko.d.ts +24 -0
- package/dist/renderers/step-line.d.ts +14 -0
- package/dist/renderers/text-cache.d.ts +23 -0
- package/dist/renderers/volume-candle.d.ts +20 -0
- package/dist/renderers/webgl/area-webgl.d.ts +28 -0
- package/dist/renderers/webgl/candlestick-webgl.d.ts +30 -0
- package/dist/renderers/webgl/index.d.ts +7 -0
- package/dist/renderers/webgl/line-webgl.d.ts +23 -0
- package/dist/renderers/webgl/webgl-utils.d.ts +23 -0
- package/dist/timezone/index.d.ts +2 -0
- package/dist/timezone/timezone.d.ts +12 -0
- package/dist/transforms/aggregate.d.ts +2 -0
- package/dist/transforms/heikin-ashi.d.ts +2 -0
- package/dist/transforms/index.d.ts +2 -0
- package/dist/ui/context-menu.d.ts +14 -0
- package/dist/ui/hud.d.ts +43 -0
- package/dist/ui/settings-popup.d.ts +15 -0
- package/package.json +129 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index109.js","names":[],"sources":["../src/core/streaming-adapter.ts"],"sourcesContent":["import type { Bar } from './types';\n\n/**\n * IStreamingAdapter — interface for real-time bar data streaming.\n *\n * Implementations connect to WebSocket, polling, or SSE endpoints\n * and push bar updates to the chart via callbacks.\n */\nexport interface IStreamingAdapter {\n /** Connect to the data source. */\n connect(): void;\n /** Disconnect and clean up. */\n disconnect(): void;\n /** Subscribe to real-time bar updates for a symbol/resolution. */\n subscribeBars(\n symbol: string,\n resolution: string,\n onBar: (bar: Bar) => void,\n ): void;\n /** Unsubscribe from bar updates. */\n unsubscribeBars(symbol: string, resolution: string): void;\n}\n\n// ─── WebSocket Adapter ──────────────────────────────────────────────────────\n\nexport interface WebSocketAdapterOptions {\n /** WebSocket URL (e.g., 'wss://stream.example.com/bars'). */\n url: string;\n /** Auto-reconnect on disconnect (default: true). */\n reconnect?: boolean;\n /** Max reconnect attempts (default: 10, 0 = unlimited). */\n maxReconnectAttempts?: number;\n /** Initial reconnect delay in ms (default: 1000). Doubles each attempt. */\n reconnectDelay?: number;\n /** Max reconnect delay in ms (default: 30000). */\n maxReconnectDelay?: number;\n /**\n * Parse an incoming WebSocket message into a Bar.\n * Return null to skip the message.\n */\n parseMessage?: (data: unknown) => { symbol: string; resolution: string; bar: Bar } | null;\n}\n\ntype BarCallback = (bar: Bar) => void;\n\n/**\n * WebSocketAdapter — streams bar data over a WebSocket connection\n * with automatic reconnection and exponential backoff.\n */\nexport class WebSocketAdapter implements IStreamingAdapter {\n private _options: Required<WebSocketAdapterOptions>;\n private _ws: WebSocket | null = null;\n private _subscriptions: Map<string, BarCallback> = new Map();\n private _reconnectAttempts = 0;\n private _reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private _connected = false;\n private _intentionalClose = false;\n\n constructor(options: WebSocketAdapterOptions) {\n this._options = {\n url: options.url,\n reconnect: options.reconnect ?? true,\n maxReconnectAttempts: options.maxReconnectAttempts ?? 10,\n reconnectDelay: options.reconnectDelay ?? 1000,\n maxReconnectDelay: options.maxReconnectDelay ?? 30000,\n parseMessage: options.parseMessage ?? defaultParseMessage,\n };\n }\n\n get connected(): boolean {\n return this._connected;\n }\n\n get reconnectAttempts(): number {\n return this._reconnectAttempts;\n }\n\n connect(): void {\n this._clearReconnectTimer();\n this._reconnectAttempts = 0;\n this._intentionalClose = false;\n this._createConnection();\n }\n\n disconnect(): void {\n this._intentionalClose = true;\n this._clearReconnectTimer();\n if (this._ws) {\n this._ws.close();\n this._ws = null;\n }\n this._connected = false;\n }\n\n subscribeBars(symbol: string, resolution: string, onBar: BarCallback): void {\n const key = `${symbol}:${resolution}`;\n this._subscriptions.set(key, onBar);\n }\n\n unsubscribeBars(symbol: string, resolution: string): void {\n const key = `${symbol}:${resolution}`;\n this._subscriptions.delete(key);\n }\n\n private _createConnection(): void {\n if (this._ws) {\n this._intentionalClose = true;\n this._ws.close();\n this._ws = null;\n this._intentionalClose = false;\n }\n\n const ws = new WebSocket(this._options.url);\n\n ws.onopen = () => {\n this._connected = true;\n this._reconnectAttempts = 0;\n };\n\n ws.onmessage = (event) => {\n let data: unknown;\n try {\n data = JSON.parse(event.data as string);\n } catch {\n return;\n }\n\n const parsed = this._options.parseMessage(data);\n if (!parsed) return;\n\n const key = `${parsed.symbol}:${parsed.resolution}`;\n const callback = this._subscriptions.get(key);\n if (callback) callback(parsed.bar);\n };\n\n ws.onclose = () => {\n this._connected = false;\n this._ws = null;\n if (!this._intentionalClose && this._options.reconnect) {\n this._scheduleReconnect();\n }\n };\n\n ws.onerror = () => {\n // Error is followed by close event; reconnection handled there.\n };\n\n this._ws = ws;\n }\n\n private _scheduleReconnect(): void {\n this._clearReconnectTimer();\n const { maxReconnectAttempts, reconnectDelay, maxReconnectDelay } = this._options;\n if (maxReconnectAttempts > 0 && this._reconnectAttempts >= maxReconnectAttempts) {\n return;\n }\n\n const delay = Math.min(\n reconnectDelay * Math.pow(2, this._reconnectAttempts),\n maxReconnectDelay,\n );\n this._reconnectAttempts++;\n\n this._reconnectTimer = setTimeout(() => {\n this._reconnectTimer = null;\n this._createConnection();\n }, delay);\n }\n\n private _clearReconnectTimer(): void {\n if (this._reconnectTimer !== null) {\n clearTimeout(this._reconnectTimer);\n this._reconnectTimer = null;\n }\n }\n}\n\n// ─── Polling Adapter ────────────────────────────────────────────────────────\n\nexport interface PollingAdapterOptions {\n /** Fetch function that returns the latest bar for a symbol/resolution. */\n fetchBar: (symbol: string, resolution: string) => Promise<Bar | null>;\n /** Polling interval in ms (default: 5000). */\n interval?: number;\n}\n\n/**\n * PollingAdapter — polls a REST endpoint at regular intervals\n * for the latest bar data.\n */\nexport class PollingAdapter implements IStreamingAdapter {\n private _options: PollingAdapterOptions;\n private _interval: number;\n private _timers: Map<string, ReturnType<typeof setInterval>> = new Map();\n private _subscriptions: Map<string, BarCallback> = new Map();\n private _fetching: Set<string> = new Set();\n\n constructor(options: PollingAdapterOptions) {\n this._options = options;\n this._interval = options.interval ?? 5000;\n }\n\n connect(): void {\n // Polling starts per-subscription, nothing to do globally.\n }\n\n disconnect(): void {\n for (const timer of this._timers.values()) {\n clearInterval(timer);\n }\n this._timers.clear();\n this._subscriptions.clear();\n }\n\n subscribeBars(symbol: string, resolution: string, onBar: BarCallback): void {\n const key = `${symbol}:${resolution}`;\n this._subscriptions.set(key, onBar);\n\n // Clear existing timer for this key to prevent leaking intervals\n const existingTimer = this._timers.get(key);\n if (existingTimer) {\n clearInterval(existingTimer);\n }\n\n const timer = setInterval(async () => {\n if (this._fetching.has(key)) return;\n this._fetching.add(key);\n try {\n const bar = await this._options.fetchBar(symbol, resolution);\n const cb = this._subscriptions.get(key);\n if (bar && cb) cb(bar);\n } finally {\n this._fetching.delete(key);\n }\n }, this._interval);\n\n this._timers.set(key, timer);\n }\n\n unsubscribeBars(symbol: string, resolution: string): void {\n const key = `${symbol}:${resolution}`;\n const timer = this._timers.get(key);\n if (timer) {\n clearInterval(timer);\n this._timers.delete(key);\n }\n this._subscriptions.delete(key);\n }\n}\n\n// ─── Tick Buffer ────────────────────────────────────────────────────────────\n\n/**\n * TickBuffer — collects incoming ticks and flushes them at 60fps\n * to avoid overwhelming the chart with per-tick repaints.\n */\nexport class TickBuffer {\n private _buffer: Map<string, Bar> = new Map();\n private _flushCallbacks: Map<string, BarCallback> = new Map();\n private _rafId: number | null = null;\n\n /** Register a flush callback for a symbol:resolution key. */\n register(key: string, onFlush: BarCallback): void {\n this._flushCallbacks.set(key, onFlush);\n }\n\n /** Unregister and stop flushing for a key. */\n unregister(key: string): void {\n this._flushCallbacks.delete(key);\n this._buffer.delete(key);\n if (this._flushCallbacks.size === 0) this._stopLoop();\n }\n\n /** Push a tick into the buffer (latest tick wins per key). */\n push(key: string, bar: Bar): void {\n this._buffer.set(key, bar);\n // Schedule flush if not already running\n if (this._rafId === null && this._flushCallbacks.size > 0) {\n this._startLoop();\n }\n }\n\n private _startLoop(): void {\n const flush = () => {\n if (this._buffer.size === 0) {\n this._rafId = null;\n return;\n }\n const buffer = this._buffer;\n this._buffer = new Map();\n\n for (const [key, bar] of buffer) {\n const cb = this._flushCallbacks.get(key);\n if (cb) cb(bar);\n }\n this._rafId = requestAnimationFrame(flush);\n };\n this._rafId = requestAnimationFrame(flush);\n }\n\n private _stopLoop(): void {\n if (this._rafId !== null) {\n cancelAnimationFrame(this._rafId);\n this._rafId = null;\n }\n this._buffer.clear();\n }\n}\n\n// ─── Default message parser ─────────────────────────────────────────────────\n\nfunction defaultParseMessage(\n data: unknown,\n): { symbol: string; resolution: string; bar: Bar } | null {\n if (typeof data !== 'object' || data === null) return null;\n const msg = data as Record<string, unknown>;\n if (typeof msg.symbol !== 'string' || typeof msg.resolution !== 'string') return null;\n const bar = msg.bar ?? msg;\n if (typeof (bar as Record<string, unknown>).time !== 'number') return null;\n const b = bar as Record<string, number>;\n return {\n symbol: msg.symbol as string,\n resolution: msg.resolution as string,\n bar: {\n time: b.time,\n open: b.open ?? 0,\n high: b.high ?? 0,\n low: b.low ?? 0,\n close: b.close ?? 0,\n volume: b.volume ?? 0,\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/index11.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=50,e=class{constructor(e,i,o,s,n){this._dragging=!1,this._startY=0,this._startAboveH=0,this._startBelowH=0,this._onPointerDown=t=>{t.preventDefault(),this._dragging=!0,this._startY=t.clientY,this._startAboveH=this._getAboveHeight(),this._startBelowH=this._getBelowHeight(),document.addEventListener("pointermove",this._onPointerMove),document.addEventListener("pointerup",this._onPointerUp)},this._onPointerMove=e=>{if(!this._dragging)return;const i=e.clientY-this._startY,o=this._startAboveH+this._startBelowH;let s=this._startAboveH+i,n=this._startBelowH-i;s<t&&(s=t,n=o-t),n<t&&(n=t,s=o-t),this._setAboveHeight(s),this._setBelowHeight(n),this._onResize()},this._onPointerUp=()=>{this._dragging=!1,document.removeEventListener("pointermove",this._onPointerMove),document.removeEventListener("pointerup",this._onPointerUp)},this._getAboveHeight=e,this._setAboveHeight=i,this._getBelowHeight=o,this._setBelowHeight=s,this._onResize=n,this.el=document.createElement("div"),this.el.style.width="100%",this.el.style.height="4px",this.el.style.cursor="row-resize",this.el.style.backgroundColor="rgba(255,255,255,0.06)",this.el.style.flexShrink="0",this.el.addEventListener("pointerdown",this._onPointerDown)}destroy(){this.el.removeEventListener("pointerdown",this._onPointerDown),document.removeEventListener("pointermove",this._onPointerMove),document.removeEventListener("pointerup",this._onPointerUp)}};export{e as PaneDivider};
|
|
2
|
+
//# sourceMappingURL=index11.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index11.js","names":[],"sources":["../src/core/pane-divider.ts"],"sourcesContent":["const MIN_PANE_HEIGHT = 50;\nexport const DIVIDER_HEIGHT = 4;\n\n/**\n * A draggable divider between two adjacent panes.\n * On drag, redistributes height between the panes above and below.\n */\nexport class PaneDivider {\n public readonly el: HTMLDivElement;\n\n private _getAboveHeight: () => number;\n private _setAboveHeight: (h: number) => void;\n private _getBelowHeight: () => number;\n private _setBelowHeight: (h: number) => void;\n private _onResize: () => void;\n\n private _dragging = false;\n private _startY = 0;\n private _startAboveH = 0;\n private _startBelowH = 0;\n\n constructor(\n getAboveHeight: () => number,\n setAboveHeight: (h: number) => void,\n getBelowHeight: () => number,\n setBelowHeight: (h: number) => void,\n onResize: () => void,\n ) {\n this._getAboveHeight = getAboveHeight;\n this._setAboveHeight = setAboveHeight;\n this._getBelowHeight = getBelowHeight;\n this._setBelowHeight = setBelowHeight;\n this._onResize = onResize;\n\n this.el = document.createElement('div');\n this.el.style.width = '100%';\n this.el.style.height = `${DIVIDER_HEIGHT}px`;\n this.el.style.cursor = 'row-resize';\n this.el.style.backgroundColor = 'rgba(255,255,255,0.06)';\n this.el.style.flexShrink = '0';\n\n this.el.addEventListener('pointerdown', this._onPointerDown);\n }\n\n destroy(): void {\n this.el.removeEventListener('pointerdown', this._onPointerDown);\n document.removeEventListener('pointermove', this._onPointerMove);\n document.removeEventListener('pointerup', this._onPointerUp);\n }\n\n private _onPointerDown = (e: PointerEvent): void => {\n e.preventDefault();\n this._dragging = true;\n this._startY = e.clientY;\n this._startAboveH = this._getAboveHeight();\n this._startBelowH = this._getBelowHeight();\n\n document.addEventListener('pointermove', this._onPointerMove);\n document.addEventListener('pointerup', this._onPointerUp);\n };\n\n private _onPointerMove = (e: PointerEvent): void => {\n if (!this._dragging) return;\n\n const delta = e.clientY - this._startY;\n const total = this._startAboveH + this._startBelowH;\n\n let newAbove = this._startAboveH + delta;\n let newBelow = this._startBelowH - delta;\n\n // Enforce minimums\n if (newAbove < MIN_PANE_HEIGHT) {\n newAbove = MIN_PANE_HEIGHT;\n newBelow = total - MIN_PANE_HEIGHT;\n }\n if (newBelow < MIN_PANE_HEIGHT) {\n newBelow = MIN_PANE_HEIGHT;\n newAbove = total - MIN_PANE_HEIGHT;\n }\n\n this._setAboveHeight(newAbove);\n this._setBelowHeight(newBelow);\n this._onResize();\n };\n\n private _onPointerUp = (): void => {\n this._dragging = false;\n document.removeEventListener('pointermove', this._onPointerMove);\n document.removeEventListener('pointerup', this._onPointerUp);\n };\n}\n"],"mappings":"AAAA,IAAM,EAAkB,GAOX,EAAb,MAcE,WAAA,CACE,EACA,EACA,EACA,EACA,mBAVkB,eACF,oBACK,oBACA,sBA+BG,IACxB,EAAE,iBACF,KAAK,WAAY,EACjB,KAAK,QAAU,EAAE,QACjB,KAAK,aAAe,KAAK,kBACzB,KAAK,aAAe,KAAK,kBAEzB,SAAS,iBAAiB,cAAe,KAAK,gBAC9C,SAAS,iBAAiB,YAAa,KAAK,mCAGpB,IACxB,IAAK,KAAK,UAAW,OAErB,MAAM,EAAQ,EAAE,QAAU,KAAK,QACzB,EAAQ,KAAK,aAAe,KAAK,aAEvC,IAAI,EAAW,KAAK,aAAe,EAC/B,EAAW,KAAK,aAAe,EAG/B,EAAW,IACb,EAAW,EACX,EAAW,EAAQ,GAEjB,EAAW,IACb,EAAW,EACX,EAAW,EAAQ,GAGrB,KAAK,gBAAgB,GACrB,KAAK,gBAAgB,GACrB,KAAK,oCAIL,KAAK,WAAY,EACjB,SAAS,oBAAoB,cAAe,KAAK,gBACjD,SAAS,oBAAoB,YAAa,KAAK,eA5D/C,KAAK,gBAAkB,EACvB,KAAK,gBAAkB,EACvB,KAAK,gBAAkB,EACvB,KAAK,gBAAkB,EACvB,KAAK,UAAY,EAEjB,KAAK,GAAK,SAAS,cAAc,OACjC,KAAK,GAAG,MAAM,MAAQ,OACtB,KAAK,GAAG,MAAM,OAAS,MACvB,KAAK,GAAG,MAAM,OAAS,aACvB,KAAK,GAAG,MAAM,gBAAkB,yBAChC,KAAK,GAAG,MAAM,WAAa,IAE3B,KAAK,GAAG,iBAAiB,cAAe,KAAK,gBAG/C,OAAA,GACE,KAAK,GAAG,oBAAoB,cAAe,KAAK,gBAChD,SAAS,oBAAoB,cAAe,KAAK,gBACjD,SAAS,oBAAoB,YAAa,KAAK"}
|
package/dist/index110.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t={pinchZoom:!0,twoFingerPan:!0,momentum:!0,momentumFriction:.95},i=class{constructor(i,s,n){this._activeTouches=/* @__PURE__ */new Map,this._initialPinchDistance=null,this._initialBarSpacing=null,this._lastPanX=null,this._velocityX=0,this._momentumRafId=null,this._timeScale=i,this._requestRepaint=s,this._options={...t,...n},this._options.momentumFriction=Math.max(0,Math.min(1,this._options.momentumFriction))}get activeTouchCount(){return this._activeTouches.size}onPointerDown(t,i,s){if(this._activeTouches.set(s,{id:s,x:t,y:i}),this._stopMomentum(),2===this._activeTouches.size){const t=[...this._activeTouches.values()];return this._initialPinchDistance=this._distance(t[0],t[1]),this._initialBarSpacing=this._timeScale.barSpacing,this._lastPanX=(t[0].x+t[1].x)/2,!0}}onPointerMove(t,i,s){const n=this._activeTouches.get(s);if(!n)return;if(n.x,n.x=t,n.y=i,2!==this._activeTouches.size)return;const e=[...this._activeTouches.values()];if(this._options.pinchZoom&&null!==this._initialPinchDistance&&null!==this._initialBarSpacing){const t=this._distance(e[0],e[1])/this._initialPinchDistance,i=this._initialBarSpacing*t,s=Math.max(this._timeScale.minBarSpacing,Math.min(this._timeScale.maxBarSpacing,i));this._timeScale.setBarSpacing(s)}if(this._options.twoFingerPan&&null!==this._lastPanX){const t=(e[0].x+e[1].x)/2,i=t-this._lastPanX;this._velocityX=i,this._timeScale.scrollBy(i),this._lastPanX=t}return this._requestRepaint(),!0}onPointerUp(t){this._activeTouches.delete(t),this._activeTouches.size<2&&(this._options.momentum&&Math.abs(this._velocityX)>1&&this._startMomentum(),this._initialPinchDistance=null,this._initialBarSpacing=null,this._lastPanX=null)}dispose(){this._stopMomentum(),this._activeTouches.clear()}_distance(t,i){const s=i.x-t.x,n=i.y-t.y;return Math.sqrt(s*s+n*n)}_startMomentum(){const t=this._velocityX;this._stopMomentum(),this._velocityX=t;const i=this._options.momentumFriction,s=()=>{if(this._velocityX*=i,Math.abs(this._velocityX)<.5)return this._velocityX=0,void(this._momentumRafId=null);this._timeScale.scrollBy(this._velocityX),this._requestRepaint(),this._momentumRafId=requestAnimationFrame(s)};this._momentumRafId=requestAnimationFrame(s)}_stopMomentum(){null!==this._momentumRafId&&(cancelAnimationFrame(this._momentumRafId),this._momentumRafId=null),this._velocityX=0}};export{i as TouchGestureHandler};
|
|
2
|
+
//# sourceMappingURL=index110.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index110.js","names":[],"sources":["../src/interactions/touch-gestures.ts"],"sourcesContent":["/**\n * Touch gesture handlers for mobile: pinch-to-zoom and two-finger pan.\n *\n * Works alongside the existing PanZoomHandler by detecting multi-touch\n * gestures and translating them into timeScale operations.\n */\nimport type { EventHandler } from './event-router';\nimport type { TimeScale } from '../core/time-scale';\n\nexport interface TouchGestureOptions {\n /** Enable pinch-to-zoom (default: true). */\n pinchZoom?: boolean;\n /** Enable two-finger pan (default: true). */\n twoFingerPan?: boolean;\n /** Enable momentum scrolling after pan release (default: true). */\n momentum?: boolean;\n /** Momentum friction factor 0–1 (default: 0.95). */\n momentumFriction?: number;\n}\n\nconst DEFAULT_OPTIONS: Required<TouchGestureOptions> = {\n pinchZoom: true,\n twoFingerPan: true,\n momentum: true,\n momentumFriction: 0.95,\n};\n\ninterface TouchPoint {\n id: number;\n x: number;\n y: number;\n}\n\n/**\n * TouchGestureHandler — detects pinch-to-zoom and two-finger pan\n * on touch devices and applies them to the chart's TimeScale.\n */\nexport class TouchGestureHandler implements EventHandler {\n private _options: Required<TouchGestureOptions>;\n private _timeScale: TimeScale;\n private _requestRepaint: () => void;\n private _activeTouches: Map<number, TouchPoint> = new Map();\n private _initialPinchDistance: number | null = null;\n private _initialBarSpacing: number | null = null;\n private _lastPanX: number | null = null;\n private _velocityX = 0;\n private _momentumRafId: number | null = null;\n\n constructor(\n timeScale: TimeScale,\n requestRepaint: () => void,\n options?: TouchGestureOptions,\n ) {\n this._timeScale = timeScale;\n this._requestRepaint = requestRepaint;\n this._options = { ...DEFAULT_OPTIONS, ...options };\n // Clamp momentumFriction to valid (0, 1) range\n this._options.momentumFriction = Math.max(0, Math.min(1, this._options.momentumFriction));\n }\n\n get activeTouchCount(): number {\n return this._activeTouches.size;\n }\n\n onPointerDown(x: number, y: number, pointerId: number): boolean | void {\n this._activeTouches.set(pointerId, { id: pointerId, x, y });\n this._stopMomentum();\n\n if (this._activeTouches.size === 2) {\n // Start pinch/pan tracking\n const points = [...this._activeTouches.values()];\n this._initialPinchDistance = this._distance(points[0], points[1]);\n this._initialBarSpacing = this._timeScale.barSpacing;\n this._lastPanX = (points[0].x + points[1].x) / 2;\n return true; // consume to prevent single-finger pan\n }\n }\n\n onPointerMove(x: number, y: number, pointerId: number): boolean | void {\n const touch = this._activeTouches.get(pointerId);\n if (!touch) return;\n\n const prevX = touch.x;\n touch.x = x;\n touch.y = y;\n\n if (this._activeTouches.size !== 2) return;\n\n const points = [...this._activeTouches.values()];\n\n // Pinch-to-zoom\n if (this._options.pinchZoom && this._initialPinchDistance !== null && this._initialBarSpacing !== null) {\n const currentDist = this._distance(points[0], points[1]);\n const scale = currentDist / this._initialPinchDistance;\n const newSpacing = this._initialBarSpacing * scale;\n const clamped = Math.max(\n this._timeScale.minBarSpacing,\n Math.min(this._timeScale.maxBarSpacing, newSpacing),\n );\n this._timeScale.setBarSpacing(clamped);\n }\n\n // Two-finger pan\n if (this._options.twoFingerPan && this._lastPanX !== null) {\n const currentMidX = (points[0].x + points[1].x) / 2;\n const deltaX = currentMidX - this._lastPanX;\n this._velocityX = deltaX;\n this._timeScale.scrollBy(deltaX);\n this._lastPanX = currentMidX;\n }\n\n this._requestRepaint();\n return true;\n }\n\n onPointerUp(pointerId: number): boolean | void {\n this._activeTouches.delete(pointerId);\n\n if (this._activeTouches.size < 2) {\n // Gesture ended\n if (this._options.momentum && Math.abs(this._velocityX) > 1) {\n this._startMomentum();\n }\n this._initialPinchDistance = null;\n this._initialBarSpacing = null;\n this._lastPanX = null;\n }\n }\n\n dispose(): void {\n this._stopMomentum();\n this._activeTouches.clear();\n }\n\n private _distance(a: TouchPoint, b: TouchPoint): number {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n return Math.sqrt(dx * dx + dy * dy);\n }\n\n private _startMomentum(): void {\n const savedVelocity = this._velocityX;\n this._stopMomentum();\n this._velocityX = savedVelocity;\n const friction = this._options.momentumFriction;\n const tick = () => {\n this._velocityX *= friction;\n if (Math.abs(this._velocityX) < 0.5) {\n this._velocityX = 0;\n this._momentumRafId = null;\n return;\n }\n this._timeScale.scrollBy(this._velocityX);\n this._requestRepaint();\n this._momentumRafId = requestAnimationFrame(tick);\n };\n this._momentumRafId = requestAnimationFrame(tick);\n }\n\n private _stopMomentum(): void {\n if (this._momentumRafId !== null) {\n cancelAnimationFrame(this._momentumRafId);\n this._momentumRafId = null;\n }\n this._velocityX = 0;\n }\n}\n"],"mappings":"AAoBA,IAAM,EAAiD,CACrD,WAAW,EACX,cAAc,EACd,UAAU,EACV,iBAAkB,KAaP,EAAb,MAWE,WAAA,CACE,EACA,EACA,sCAVgD,IAAI,+BACP,6BACH,oBACT,qBACd,sBACmB,KAOtC,KAAK,WAAa,EAClB,KAAK,gBAAkB,EACvB,KAAK,SAAW,IAAK,KAAoB,GAEzC,KAAK,SAAS,iBAAmB,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,KAAK,SAAS,mBAGzE,oBAAI,GACF,OAAO,KAAK,eAAe,KAG7B,aAAA,CAAc,EAAW,EAAW,GAIlC,GAHA,KAAK,eAAe,IAAI,EAAW,CAAE,GAAI,EAAW,IAAG,MACvD,KAAK,gBAE4B,IAA7B,KAAK,eAAe,KAAY,CAElC,MAAM,EAAS,IAAI,KAAK,eAAe,UAIvC,OAHA,KAAK,sBAAwB,KAAK,UAAU,EAAO,GAAI,EAAO,IAC9D,KAAK,mBAAqB,KAAK,WAAW,WAC1C,KAAK,WAAa,EAAO,GAAG,EAAI,EAAO,GAAG,GAAK,GACxC,GAIX,aAAA,CAAc,EAAW,EAAW,GAClC,MAAM,EAAQ,KAAK,eAAe,IAAI,GACtC,IAAK,EAAO,OAMZ,GAJc,EAAM,EACpB,EAAM,EAAI,EACV,EAAM,EAAI,EAEuB,IAA7B,KAAK,eAAe,KAAY,OAEpC,MAAM,EAAS,IAAI,KAAK,eAAe,UAGvC,GAAI,KAAK,SAAS,WAA4C,OAA/B,KAAK,uBAA8D,OAA5B,KAAK,mBAA6B,CAEtG,MAAM,EADc,KAAK,UAAU,EAAO,GAAI,EAAO,IACzB,KAAK,sBAC3B,EAAa,KAAK,mBAAqB,EACvC,EAAU,KAAK,IACnB,KAAK,WAAW,cAChB,KAAK,IAAI,KAAK,WAAW,cAAe,IAE1C,KAAK,WAAW,cAAc,GAIhC,GAAI,KAAK,SAAS,cAAmC,OAAnB,KAAK,UAAoB,CACzD,MAAM,GAAe,EAAO,GAAG,EAAI,EAAO,GAAG,GAAK,EAC5C,EAAS,EAAc,KAAK,UAClC,KAAK,WAAa,EAClB,KAAK,WAAW,SAAS,GACzB,KAAK,UAAY,EAInB,OADA,KAAK,mBACE,EAGT,WAAA,CAAY,GACV,KAAK,eAAe,OAAO,GAEvB,KAAK,eAAe,KAAO,IAEzB,KAAK,SAAS,UAAY,KAAK,IAAI,KAAK,YAAc,GACxD,KAAK,iBAEP,KAAK,sBAAwB,KAC7B,KAAK,mBAAqB,KAC1B,KAAK,UAAY,MAIrB,OAAA,GACE,KAAK,gBACL,KAAK,eAAe,QAGtB,SAAA,CAAkB,EAAe,GAC/B,MAAM,EAAK,EAAE,EAAI,EAAE,EACb,EAAK,EAAE,EAAI,EAAE,EACnB,OAAO,KAAK,KAAK,EAAK,EAAK,EAAK,GAGlC,cAAA,GACE,MAAM,EAAgB,KAAK,WAC3B,KAAK,gBACL,KAAK,WAAa,EAClB,MAAM,EAAW,KAAK,SAAS,iBACzB,EAAA,KAEJ,GADA,KAAK,YAAc,EACf,KAAK,IAAI,KAAK,YAAc,GAG9B,OAFA,KAAK,WAAa,OAClB,KAAK,eAAiB,MAGxB,KAAK,WAAW,SAAS,KAAK,YAC9B,KAAK,kBACL,KAAK,eAAiB,sBAAsB,IAE9C,KAAK,eAAiB,sBAAsB,GAG9C,aAAA,GAC8B,OAAxB,KAAK,iBACP,qBAAqB,KAAK,gBAC1B,KAAK,eAAiB,MAExB,KAAK,WAAa"}
|
package/dist/index111.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=new Set(["ar","he","fa","ur","ps","sd","yi","dv","ckb","ug"]);function r(r){const e=r.split("-")[0].toLowerCase();return t.has(e)?"rtl":"ltr"}function e(t,r,e){return e?r-t:t}function n(t,r){return"start"===t?r?"right":"left":r?"left":"right"}export{r as detectDirection,e as mirrorX,n as resolveTextAlign};
|
|
2
|
+
//# sourceMappingURL=index111.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index111.js","names":[],"sources":["../src/core/rtl.ts"],"sourcesContent":["/**\n * RTL (Right-to-Left) layout building blocks.\n *\n * These utilities provide the foundation for RTL support. Chart-level\n * integration (price axis flip, time axis mirroring, tooltip/menu layout)\n * uses these helpers when the `direction` option is set.\n */\n\nexport type TextDirection = 'ltr' | 'rtl';\n\n/**\n * Languages that use right-to-left script by default.\n * Kurdish (ku) is excluded because it depends on script variant\n * (ku-Arab/Sorani is RTL, ku-Latn is LTR). Use 'ckb' for Sorani.\n */\nconst RTL_LANGUAGES = new Set([\n 'ar', // Arabic\n 'he', // Hebrew\n 'fa', // Persian\n 'ur', // Urdu\n 'ps', // Pashto\n 'sd', // Sindhi\n 'yi', // Yiddish\n 'dv', // Divehi\n 'ckb', // Central Kurdish (Sorani)\n 'ug', // Uyghur\n]);\n\n/**\n * Detect text direction from a locale string.\n * Returns 'rtl' for Arabic, Hebrew, Persian, Urdu, and other RTL locales.\n */\nexport function detectDirection(locale: string): TextDirection {\n const lang = locale.split('-')[0].toLowerCase();\n return RTL_LANGUAGES.has(lang) ? 'rtl' : 'ltr';\n}\n\n/**\n * Mirror an X coordinate for RTL layout.\n * In RTL mode, x=0 becomes x=width and vice versa.\n */\nexport function mirrorX(x: number, width: number, isRTL: boolean): number {\n return isRTL ? width - x : x;\n}\n\n/**\n * Get the CSS text-align value for the current direction.\n */\nexport function resolveTextAlign(align: 'start' | 'end', isRTL: boolean): 'left' | 'right' {\n if (align === 'start') return isRTL ? 'right' : 'left';\n return isRTL ? 'left' : 'right';\n}\n"],"mappings":"AAeA,IAAM,EAAgB,IAAI,IAAI,CAC5B,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,MACA,OAOF,SAAgB,EAAgB,GAC9B,MAAM,EAAO,EAAO,MAAM,KAAK,GAAG,cAClC,OAAO,EAAc,IAAI,GAAQ,MAAQ,MAO3C,SAAgB,EAAQ,EAAW,EAAe,GAChD,OAAO,EAAQ,EAAQ,EAAI,EAM7B,SAAgB,EAAiB,EAAwB,GACvD,MAAc,UAAV,EAA0B,EAAQ,QAAU,OACzC,EAAQ,OAAS"}
|
package/dist/index112.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=class{constructor(){this._store=null,this._currentIndex=0,this._endIndex=0,this._speed=1,this._playing=!1,this._timerId=null,this._callbacks=[],this._baseIntervalMs=500}get active(){return null!==this._store}get playing(){return this._playing}get currentIndex(){return this._currentIndex}get speed(){return this._speed}onEvent(t){this._callbacks.push(t)}offEvent(t){const e=this._callbacks.indexOf(t);e>=0&&this._callbacks.splice(e,1)}start(t,e,s){if(this.stop(),0!==t.length){if(this._store=t,this._endIndex=t.length-1,this._currentIndex=Math.max(0,Math.min(e,this._endIndex)),this._speed=s?.speed??1,this._currentIndex>=this._endIndex)return this._playing=!1,void this._emit({type:"end",barIndex:this._currentIndex});this._playing=!0,this._startTimer()}}pause(){this._playing&&(this._playing=!1,this._stopTimer(),this._emit({type:"pause",barIndex:this._currentIndex}))}resume(){!this._playing&&this._store&&(this._currentIndex>=this._endIndex||(this._playing=!0,this._startTimer(),this._emit({type:"resume",barIndex:this._currentIndex})))}stepForward(){return!(!this._store||this._currentIndex>=this._endIndex)&&(this._currentIndex++,this._emitBar(),this._currentIndex>=this._endIndex&&(this._playing=!1,this._stopTimer(),this._emit({type:"end",barIndex:this._currentIndex})),!0)}stepBackward(){return!(!this._store||this._currentIndex<=0)&&(this._currentIndex--,this._emitBar(),!0)}setSpeed(t){this._speed=t,this._playing&&(this._stopTimer(),this._startTimer())}stop(){this._stopTimer(),this._store=null,this._currentIndex=0,this._playing=!1}_startTimer(){this._stopTimer();const t=this._baseIntervalMs/this._speed;this._timerId=setInterval(()=>{this.stepForward()||(this._playing=!1,this._stopTimer())},t)}_stopTimer(){null!==this._timerId&&(clearInterval(this._timerId),this._timerId=null)}_emitBar(){if(!this._store)return;const t=this._currentIndex,e={time:this._store.time[t],open:this._store.open[t],high:this._store.high[t],low:this._store.low[t],close:this._store.close[t],volume:this._store.volume[t]};this._emit({type:"bar",barIndex:t,bar:e})}_emit(t){for(const e of this._callbacks)e(t)}};export{t as ReplayManager};
|
|
2
|
+
//# sourceMappingURL=index112.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index112.js","names":[],"sources":["../src/core/replay.ts"],"sourcesContent":["import type { Bar, ColumnStore } from './types';\n\nexport type ReplaySpeed = 1 | 2 | 5 | 10;\n\nexport type ReplayBarEvent = { type: 'bar'; barIndex: number; bar: Bar };\nexport type ReplayControlEvent = { type: 'pause' | 'resume' | 'end'; barIndex: number };\nexport type ReplayEvent = ReplayBarEvent | ReplayControlEvent;\n\nexport type ReplayEventCallback = (event: ReplayEvent) => void;\n\nexport interface ReplayOptions {\n /** Playback speed multiplier (default: 1). */\n speed?: ReplaySpeed;\n}\n\n/**\n * ReplayManager — steps through historical data bar-by-bar to simulate\n * live data streaming. Bars appear progressively as if in real-time.\n *\n * Usage:\n * replay.start(store, fromIndex, { speed: 2 });\n * replay.onEvent(callback); // fired for each revealed bar or control event\n * replay.pause();\n * replay.stepForward();\n * replay.resume();\n * replay.stop();\n */\nexport class ReplayManager {\n private _store: ColumnStore | null = null;\n private _currentIndex = 0;\n private _endIndex = 0;\n private _speed: ReplaySpeed = 1;\n private _playing = false;\n private _timerId: ReturnType<typeof setInterval> | null = null;\n private _callbacks: ReplayEventCallback[] = [];\n private _baseIntervalMs = 500; // 500ms per bar at 1x speed\n\n /** Whether replay is currently active (playing or paused). */\n get active(): boolean {\n return this._store !== null;\n }\n\n /** Whether replay is currently playing (not paused). */\n get playing(): boolean {\n return this._playing;\n }\n\n /** Current bar index in the replay. */\n get currentIndex(): number {\n return this._currentIndex;\n }\n\n /** Current playback speed. */\n get speed(): ReplaySpeed {\n return this._speed;\n }\n\n /** Subscribe to replay events. */\n onEvent(callback: ReplayEventCallback): void {\n this._callbacks.push(callback);\n }\n\n /** Unsubscribe from replay events. */\n offEvent(callback: ReplayEventCallback): void {\n const idx = this._callbacks.indexOf(callback);\n if (idx >= 0) this._callbacks.splice(idx, 1);\n }\n\n /**\n * Start replay from the given bar index.\n * Bars from 0 to fromIndex are initially visible; subsequent bars\n * are revealed progressively.\n */\n start(store: ColumnStore, fromIndex: number, options?: ReplayOptions): void {\n this.stop();\n if (store.length === 0) return;\n\n this._store = store;\n this._endIndex = store.length - 1;\n this._currentIndex = Math.max(0, Math.min(fromIndex, this._endIndex));\n this._speed = options?.speed ?? 1;\n\n // Don't start playing if already at the end\n if (this._currentIndex >= this._endIndex) {\n this._playing = false;\n this._emit({ type: 'end', barIndex: this._currentIndex });\n return;\n }\n\n this._playing = true;\n this._startTimer();\n }\n\n /** Pause playback. */\n pause(): void {\n if (!this._playing) return;\n this._playing = false;\n this._stopTimer();\n this._emit({ type: 'pause', barIndex: this._currentIndex });\n }\n\n /** Resume playback after pause. */\n resume(): void {\n if (this._playing || !this._store) return;\n // Don't resume if already at end\n if (this._currentIndex >= this._endIndex) return;\n this._playing = true;\n this._startTimer();\n this._emit({ type: 'resume', barIndex: this._currentIndex });\n }\n\n /** Step forward one bar (works while paused or playing). */\n stepForward(): boolean {\n if (!this._store || this._currentIndex >= this._endIndex) return false;\n this._currentIndex++;\n this._emitBar();\n if (this._currentIndex >= this._endIndex) {\n this._playing = false;\n this._stopTimer();\n this._emit({ type: 'end', barIndex: this._currentIndex });\n }\n return true;\n }\n\n /** Step backward one bar (works while paused or playing). */\n stepBackward(): boolean {\n if (!this._store || this._currentIndex <= 0) return false;\n this._currentIndex--;\n this._emitBar();\n return true;\n }\n\n /** Set playback speed. */\n setSpeed(speed: ReplaySpeed): void {\n this._speed = speed;\n if (this._playing) {\n this._stopTimer();\n this._startTimer();\n }\n }\n\n /** Stop replay and reset state. */\n stop(): void {\n this._stopTimer();\n this._store = null;\n this._currentIndex = 0;\n this._playing = false;\n }\n\n private _startTimer(): void {\n this._stopTimer();\n const interval = this._baseIntervalMs / this._speed;\n this._timerId = setInterval(() => {\n if (!this.stepForward()) {\n this._playing = false;\n this._stopTimer();\n }\n }, interval);\n }\n\n private _stopTimer(): void {\n if (this._timerId !== null) {\n clearInterval(this._timerId);\n this._timerId = null;\n }\n }\n\n private _emitBar(): void {\n if (!this._store) return;\n const i = this._currentIndex;\n const bar: Bar = {\n time: this._store.time[i],\n open: this._store.open[i],\n high: this._store.high[i],\n low: this._store.low[i],\n close: this._store.close[i],\n volume: this._store.volume[i],\n };\n this._emit({ type: 'bar', barIndex: i, bar });\n }\n\n private _emit(event: ReplayEvent): void {\n for (const cb of this._callbacks) cb(event);\n }\n}\n"],"mappings":"AA2BA,IAAa,EAAb,gCACuC,wBACb,iBACJ,cACU,iBACX,gBACuC,qBACd,wBAClB,IAG1B,UAAI,GACF,OAAuB,OAAhB,KAAK,OAId,WAAI,GACF,OAAO,KAAK,SAId,gBAAI,GACF,OAAO,KAAK,cAId,SAAI,GACF,OAAO,KAAK,OAId,OAAA,CAAQ,GACN,KAAK,WAAW,KAAK,GAIvB,QAAA,CAAS,GACP,MAAM,EAAM,KAAK,WAAW,QAAQ,GAChC,GAAO,GAAG,KAAK,WAAW,OAAO,EAAK,GAQ5C,KAAA,CAAM,EAAoB,EAAmB,GAE3C,GADA,KAAK,OACgB,IAAjB,EAAM,OAAV,CAQA,GANA,KAAK,OAAS,EACd,KAAK,UAAY,EAAM,OAAS,EAChC,KAAK,cAAgB,KAAK,IAAI,EAAG,KAAK,IAAI,EAAW,KAAK,YAC1D,KAAK,OAAS,GAAS,OAAS,EAG5B,KAAK,eAAiB,KAAK,UAG7B,OAFA,KAAK,UAAW,OAChB,KAAK,MAAM,CAAE,KAAM,MAAO,SAAU,KAAK,gBAI3C,KAAK,UAAW,EAChB,KAAK,aAfmB,EAmB1B,KAAA,GACO,KAAK,WACV,KAAK,UAAW,EAChB,KAAK,aACL,KAAK,MAAM,CAAE,KAAM,QAAS,SAAU,KAAK,iBAI7C,MAAA,IACM,KAAK,UAAa,KAAK,SAEvB,KAAK,eAAiB,KAAK,YAC/B,KAAK,UAAW,EAChB,KAAK,cACL,KAAK,MAAM,CAAE,KAAM,SAAU,SAAU,KAAK,kBAI9C,WAAA,GACE,SAAK,KAAK,QAAU,KAAK,eAAiB,KAAK,aAC/C,KAAK,gBACL,KAAK,WACD,KAAK,eAAiB,KAAK,YAC7B,KAAK,UAAW,EAChB,KAAK,aACL,KAAK,MAAM,CAAE,KAAM,MAAO,SAAU,KAAK,kBAEpC,GAIT,YAAA,GACE,SAAK,KAAK,QAAU,KAAK,eAAiB,KAC1C,KAAK,gBACL,KAAK,YACE,GAIT,QAAA,CAAS,GACP,KAAK,OAAS,EACV,KAAK,WACP,KAAK,aACL,KAAK,eAKT,IAAA,GACE,KAAK,aACL,KAAK,OAAS,KACd,KAAK,cAAgB,EACrB,KAAK,UAAW,EAGlB,WAAA,GACE,KAAK,aACL,MAAM,EAAW,KAAK,gBAAkB,KAAK,OAC7C,KAAK,SAAW,YAAA,KACT,KAAK,gBACR,KAAK,UAAW,EAChB,KAAK,eAEN,GAGL,UAAA,GACwB,OAAlB,KAAK,WACP,cAAc,KAAK,UACnB,KAAK,SAAW,MAIpB,QAAA,GACE,IAAK,KAAK,OAAQ,OAClB,MAAM,EAAI,KAAK,cACT,EAAW,CACf,KAAM,KAAK,OAAO,KAAK,GACvB,KAAM,KAAK,OAAO,KAAK,GACvB,KAAM,KAAK,OAAO,KAAK,GACvB,IAAK,KAAK,OAAO,IAAI,GACrB,MAAO,KAAK,OAAO,MAAM,GACzB,OAAQ,KAAK,OAAO,OAAO,IAE7B,KAAK,MAAM,CAAE,KAAM,MAAO,SAAU,EAAG,QAGzC,KAAA,CAAc,GACZ,IAAK,MAAM,KAAM,KAAK,WAAY,EAAG"}
|
package/dist/index113.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=class{constructor(){this._plugins=/* @__PURE__ */new Map,this._installOrder=[],this._chartApi=null}setChartApi(t){this._chartApi=t}use(t){if(this._plugins.has(t.name))throw new Error(`Plugin '${t.name}' is already installed`);if(t.dependencies)for(const i of t.dependencies)if(!this._plugins.has(i))throw new Error(`Plugin '${t.name}' depends on '${i}' which is not installed`);this._plugins.set(t.name,t),this._installOrder.push(t.name),this._chartApi&&t.install(this._chartApi)}remove(t){const i=this._plugins.get(t);if(!i)return!1;for(const[s,n]of this._plugins)if(s!==t&&n.dependencies?.includes(t))throw new Error(`Cannot remove plugin '${t}': plugin '${s}' depends on it`);return i.uninstall?.(),this._plugins.delete(t),this._installOrder=this._installOrder.filter(i=>i!==t),!0}get(t){return this._plugins.get(t)}list(){return[...this._installOrder]}has(t){return this._plugins.has(t)}notifyPaint(t){for(const i of this._installOrder)this._plugins.get(i)?.onPaint?.(t)}notifyOptionsChange(t){for(const i of this._installOrder)this._plugins.get(i)?.onOptionsChange?.(t)}notifyDataUpdate(){for(const t of this._installOrder)this._plugins.get(t)?.onDataUpdate?.()}removeAll(){for(let t=this._installOrder.length-1;t>=0;t--){const i=this._installOrder[t];this._plugins.get(i)?.uninstall?.()}this._plugins.clear(),this._installOrder=[]}};export{t as PluginManager};
|
|
2
|
+
//# sourceMappingURL=index113.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index113.js","names":[],"sources":["../src/core/plugin.ts"],"sourcesContent":["/**\n * IPlugin — formal plugin interface with lifecycle hooks.\n *\n * Plugins can add custom series types, indicators, drawing tools,\n * UI panels, and intercept chart events.\n */\nexport interface IPlugin {\n /** Unique plugin identifier. */\n readonly name: string;\n /** Plugin version (semver). */\n readonly version?: string;\n /** Plugins this plugin depends on (by name). */\n readonly dependencies?: string[];\n\n /**\n * Called when the plugin is installed on a chart via chart.use(plugin).\n * Receives the chart API for registering extensions.\n */\n install(chart: PluginChartApi): void;\n\n /**\n * Called when the plugin is uninstalled or the chart is destroyed.\n */\n uninstall?(): void;\n\n /**\n * Called on each paint cycle, after all series have been rendered.\n * Allows plugins to render custom overlays.\n */\n onPaint?(context: PluginPaintContext): void;\n\n /**\n * Called when chart options change.\n */\n onOptionsChange?(options: Record<string, unknown>): void;\n\n /**\n * Called when data is updated on any series.\n */\n onDataUpdate?(): void;\n}\n\n/**\n * Subset of the chart API exposed to plugins.\n */\nexport interface PluginChartApi {\n /** Get the DOM container element. */\n getContainer(): HTMLElement;\n /** Request a chart repaint. */\n requestRepaint(): void;\n /** Get chart dimensions. */\n getSize(): { width: number; height: number };\n}\n\n/**\n * Context passed to plugin paint callbacks.\n */\nexport interface PluginPaintContext {\n ctx: CanvasRenderingContext2D;\n width: number;\n height: number;\n pixelRatio: number;\n}\n\n/**\n * PluginManager — manages plugin lifecycle, dependency resolution,\n * and event dispatch.\n */\nexport class PluginManager {\n private _plugins: Map<string, IPlugin> = new Map();\n private _installOrder: string[] = [];\n private _chartApi: PluginChartApi | null = null;\n\n /** Set the chart API reference. Called once during chart initialization. */\n setChartApi(api: PluginChartApi): void {\n this._chartApi = api;\n }\n\n /**\n * Install a plugin. Resolves dependencies (must already be installed).\n */\n use(plugin: IPlugin): void {\n if (this._plugins.has(plugin.name)) {\n throw new Error(`Plugin '${plugin.name}' is already installed`);\n }\n\n // Check dependencies\n if (plugin.dependencies) {\n for (const dep of plugin.dependencies) {\n if (!this._plugins.has(dep)) {\n throw new Error(\n `Plugin '${plugin.name}' depends on '${dep}' which is not installed`,\n );\n }\n }\n }\n\n this._plugins.set(plugin.name, plugin);\n this._installOrder.push(plugin.name);\n\n if (this._chartApi) {\n plugin.install(this._chartApi);\n }\n }\n\n /** Uninstall a plugin by name. */\n remove(name: string): boolean {\n const plugin = this._plugins.get(name);\n if (!plugin) return false;\n\n // Check if other plugins depend on this one\n for (const [otherName, other] of this._plugins) {\n if (otherName !== name && other.dependencies?.includes(name)) {\n throw new Error(\n `Cannot remove plugin '${name}': plugin '${otherName}' depends on it`,\n );\n }\n }\n\n plugin.uninstall?.();\n this._plugins.delete(name);\n this._installOrder = this._installOrder.filter((n) => n !== name);\n return true;\n }\n\n /** Get an installed plugin by name. */\n get(name: string): IPlugin | undefined {\n return this._plugins.get(name);\n }\n\n /** List installed plugin names in install order. */\n list(): string[] {\n return [...this._installOrder];\n }\n\n /** Whether a plugin is installed. */\n has(name: string): boolean {\n return this._plugins.has(name);\n }\n\n /** Notify all plugins of a paint cycle. */\n notifyPaint(context: PluginPaintContext): void {\n for (const name of this._installOrder) {\n this._plugins.get(name)?.onPaint?.(context);\n }\n }\n\n /** Notify all plugins of options change. */\n notifyOptionsChange(options: Record<string, unknown>): void {\n for (const name of this._installOrder) {\n this._plugins.get(name)?.onOptionsChange?.(options);\n }\n }\n\n /** Notify all plugins of data update. */\n notifyDataUpdate(): void {\n for (const name of this._installOrder) {\n this._plugins.get(name)?.onDataUpdate?.();\n }\n }\n\n /** Uninstall all plugins in reverse order. */\n removeAll(): void {\n for (let i = this._installOrder.length - 1; i >= 0; i--) {\n const name = this._installOrder[i];\n this._plugins.get(name)?.uninstall?.();\n }\n this._plugins.clear();\n this._installOrder = [];\n }\n}\n"],"mappings":"AAoEA,IAAa,EAAb,iDAC2C,IAAI,uBACX,kBACS,KAG3C,WAAA,CAAY,GACV,KAAK,UAAY,EAMnB,GAAA,CAAI,GACF,GAAI,KAAK,SAAS,IAAI,EAAO,MAC3B,MAAM,IAAI,MAAM,WAAW,EAAO,8BAIpC,GAAI,EAAO,iBACJ,MAAM,KAAO,EAAO,aACvB,IAAK,KAAK,SAAS,IAAI,GACrB,MAAM,IAAI,MACR,WAAW,EAAO,qBAAqB,6BAM/C,KAAK,SAAS,IAAI,EAAO,KAAM,GAC/B,KAAK,cAAc,KAAK,EAAO,MAE3B,KAAK,WACP,EAAO,QAAQ,KAAK,WAKxB,MAAA,CAAO,GACL,MAAM,EAAS,KAAK,SAAS,IAAI,GACjC,IAAK,EAAQ,OAAO,EAGpB,IAAK,MAAO,EAAW,KAAU,KAAK,SACpC,GAAI,IAAc,GAAQ,EAAM,cAAc,SAAS,GACrD,MAAM,IAAI,MACR,yBAAyB,eAAkB,oBAQjD,OAHA,EAAO,cACP,KAAK,SAAS,OAAO,GACrB,KAAK,cAAgB,KAAK,cAAc,OAAQ,GAAM,IAAM,IACrD,EAIT,GAAA,CAAI,GACF,OAAO,KAAK,SAAS,IAAI,GAI3B,IAAA,GACE,MAAO,IAAI,KAAK,eAIlB,GAAA,CAAI,GACF,OAAO,KAAK,SAAS,IAAI,GAI3B,WAAA,CAAY,GACV,IAAK,MAAM,KAAQ,KAAK,cACtB,KAAK,SAAS,IAAI,IAAO,UAAU,GAKvC,mBAAA,CAAoB,GAClB,IAAK,MAAM,KAAQ,KAAK,cACtB,KAAK,SAAS,IAAI,IAAO,kBAAkB,GAK/C,gBAAA,GACE,IAAK,MAAM,KAAQ,KAAK,cACtB,KAAK,SAAS,IAAI,IAAO,iBAK7B,SAAA,GACE,IAAK,IAAI,EAAI,KAAK,cAAc,OAAS,EAAG,GAAK,EAAG,IAAK,CACvD,MAAM,EAAO,KAAK,cAAc,GAChC,KAAK,SAAS,IAAI,IAAO,cAE3B,KAAK,SAAS,QACd,KAAK,cAAgB"}
|
package/dist/index114.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=class{constructor(e="fc-"){this._prefix=e}async save(e,t){localStorage.setItem(this._prefix+e,JSON.stringify(t))}async load(e){const t=localStorage.getItem(this._prefix+e);return null===t?null:JSON.parse(t)}async delete(e){localStorage.removeItem(this._prefix+e)}},t=class{constructor(e="fin-charter",t="chart-state"){this._dbPromise=null,this._dbName=e,this._storeName=t}_getDB(){return this._dbPromise||(this._dbPromise=new Promise((e,t)=>{const r=indexedDB.open(this._dbName,1);r.onupgradeneeded=()=>{const e=r.result;e.objectStoreNames.contains(this._storeName)||e.createObjectStore(this._storeName)},r.onsuccess=()=>e(r.result),r.onerror=()=>t(r.error)})),this._dbPromise}async save(e,t){const r=await this._getDB();return new Promise((s,o)=>{const a=r.transaction(this._storeName,"readwrite");a.objectStore(this._storeName).put(t,e),a.oncomplete=()=>s(),a.onerror=()=>o(a.error)})}async load(e){const t=await this._getDB();return new Promise((r,s)=>{const o=t.transaction(this._storeName,"readonly").objectStore(this._storeName).get(e);o.onsuccess=()=>r(o.result??null),o.onerror=()=>s(o.error)})}async delete(e){const t=await this._getDB();return new Promise((r,s)=>{const o=t.transaction(this._storeName,"readwrite");o.objectStore(this._storeName).delete(e),o.oncomplete=()=>r(),o.onerror=()=>s(o.error)})}},r=class{constructor(e,t=500){this._debounceTimer=null,this._adapter=e,this._debounceMs=t}saveDrawings(e,t){this._debounceTimer&&clearTimeout(this._debounceTimer),this._debounceTimer=setTimeout(()=>{this._adapter.save(`drawings:${e}`,t)},this._debounceMs)}async loadDrawings(e){return this._adapter.load(`drawings:${e}`)}async deleteDrawings(e){return this._adapter.delete(`drawings:${e}`)}flush(){this._debounceTimer&&(clearTimeout(this._debounceTimer),this._debounceTimer=null)}};export{r as DrawingPersistence,t as IndexedDBAdapter,e as LocalStorageAdapter};
|
|
2
|
+
//# sourceMappingURL=index114.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index114.js","names":[],"sources":["../src/core/storage-adapter.ts"],"sourcesContent":["/**\n * IStorageAdapter — interface for persisting chart drawings and state.\n *\n * Implementations can use localStorage, IndexedDB, REST API, etc.\n */\nexport interface IStorageAdapter {\n save(key: string, state: unknown): Promise<void>;\n load(key: string): Promise<unknown | null>;\n delete(key: string): Promise<void>;\n}\n\n/**\n * LocalStorageAdapter — persists data to browser localStorage.\n */\nexport class LocalStorageAdapter implements IStorageAdapter {\n private _prefix: string;\n\n constructor(prefix = 'fc-') {\n this._prefix = prefix;\n }\n\n async save(key: string, state: unknown): Promise<void> {\n localStorage.setItem(this._prefix + key, JSON.stringify(state));\n }\n\n async load(key: string): Promise<unknown | null> {\n const data = localStorage.getItem(this._prefix + key);\n if (data === null) return null;\n return JSON.parse(data);\n }\n\n async delete(key: string): Promise<void> {\n localStorage.removeItem(this._prefix + key);\n }\n}\n\n/**\n * IndexedDBAdapter — persists data to browser IndexedDB for larger state.\n */\nexport class IndexedDBAdapter implements IStorageAdapter {\n private _dbName: string;\n private _storeName: string;\n private _dbPromise: Promise<IDBDatabase> | null = null;\n\n constructor(dbName = 'fin-charter', storeName = 'chart-state') {\n this._dbName = dbName;\n this._storeName = storeName;\n }\n\n private _getDB(): Promise<IDBDatabase> {\n if (this._dbPromise) return this._dbPromise;\n this._dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(this._dbName, 1);\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(this._storeName)) {\n db.createObjectStore(this._storeName);\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n return this._dbPromise;\n }\n\n async save(key: string, state: unknown): Promise<void> {\n const db = await this._getDB();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(this._storeName, 'readwrite');\n tx.objectStore(this._storeName).put(state, key);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n async load(key: string): Promise<unknown | null> {\n const db = await this._getDB();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(this._storeName, 'readonly');\n const request = tx.objectStore(this._storeName).get(key);\n request.onsuccess = () => resolve(request.result ?? null);\n request.onerror = () => reject(request.error);\n });\n }\n\n async delete(key: string): Promise<void> {\n const db = await this._getDB();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(this._storeName, 'readwrite');\n tx.objectStore(this._storeName).delete(key);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n}\n\n/**\n * DrawingPersistence — manages auto-save/load of drawings per symbol.\n */\nexport class DrawingPersistence {\n private _adapter: IStorageAdapter;\n private _debounceMs: number;\n private _debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(adapter: IStorageAdapter, debounceMs = 500) {\n this._adapter = adapter;\n this._debounceMs = debounceMs;\n }\n\n /** Auto-save drawings for a symbol (debounced). */\n saveDrawings(symbol: string, drawings: unknown): void {\n if (this._debounceTimer) clearTimeout(this._debounceTimer);\n this._debounceTimer = setTimeout(() => {\n this._adapter.save(`drawings:${symbol}`, drawings);\n }, this._debounceMs);\n }\n\n /** Load drawings for a symbol. */\n async loadDrawings(symbol: string): Promise<unknown | null> {\n return this._adapter.load(`drawings:${symbol}`);\n }\n\n /** Delete drawings for a symbol. */\n async deleteDrawings(symbol: string): Promise<void> {\n return this._adapter.delete(`drawings:${symbol}`);\n }\n\n /** Flush any pending debounced save immediately. */\n flush(): void {\n if (this._debounceTimer) {\n clearTimeout(this._debounceTimer);\n this._debounceTimer = null;\n }\n }\n}\n"],"mappings":"AAcA,IAAa,EAAb,MAGE,WAAA,CAAY,EAAS,OACnB,KAAK,QAAU,EAGjB,UAAM,CAAK,EAAa,GACtB,aAAa,QAAQ,KAAK,QAAU,EAAK,KAAK,UAAU,IAG1D,UAAM,CAAK,GACT,MAAM,EAAO,aAAa,QAAQ,KAAK,QAAU,GACjD,OAAa,OAAT,EAAsB,KACnB,KAAK,MAAM,GAGpB,YAAM,CAAO,GACX,aAAa,WAAW,KAAK,QAAU,KAO9B,EAAb,MAKE,WAAA,CAAY,EAAS,cAAe,EAAY,+BAFE,KAGhD,KAAK,QAAU,EACf,KAAK,WAAa,EAGpB,MAAA,GACE,OAAI,KAAK,aACT,KAAK,WAAa,IAAI,QAAA,CAAS,EAAS,KACtC,MAAM,EAAU,UAAU,KAAK,KAAK,QAAS,GAC7C,EAAQ,gBAAA,KACN,MAAM,EAAK,EAAQ,OACd,EAAG,iBAAiB,SAAS,KAAK,aACrC,EAAG,kBAAkB,KAAK,aAG9B,EAAQ,UAAA,IAAkB,EAAQ,EAAQ,QAC1C,EAAQ,QAAA,IAAgB,EAAO,EAAQ,UAVb,KAAK,WAenC,UAAM,CAAK,EAAa,GACtB,MAAM,QAAW,KAAK,SACtB,OAAO,IAAI,QAAA,CAAS,EAAS,KAC3B,MAAM,EAAK,EAAG,YAAY,KAAK,WAAY,aAC3C,EAAG,YAAY,KAAK,YAAY,IAAI,EAAO,GAC3C,EAAG,WAAA,IAAmB,IACtB,EAAG,QAAA,IAAgB,EAAO,EAAG,SAIjC,UAAM,CAAK,GACT,MAAM,QAAW,KAAK,SACtB,OAAO,IAAI,QAAA,CAAS,EAAS,KAE3B,MAAM,EADK,EAAG,YAAY,KAAK,WAAY,YACxB,YAAY,KAAK,YAAY,IAAI,GACpD,EAAQ,UAAA,IAAkB,EAAQ,EAAQ,QAAU,MACpD,EAAQ,QAAA,IAAgB,EAAO,EAAQ,SAI3C,YAAM,CAAO,GACX,MAAM,QAAW,KAAK,SACtB,OAAO,IAAI,QAAA,CAAS,EAAS,KAC3B,MAAM,EAAK,EAAG,YAAY,KAAK,WAAY,aAC3C,EAAG,YAAY,KAAK,YAAY,OAAO,GACvC,EAAG,WAAA,IAAmB,IACtB,EAAG,QAAA,IAAgB,EAAO,EAAG,WAQtB,EAAb,MAKE,WAAA,CAAY,EAA0B,EAAa,yBAFY,KAG7D,KAAK,SAAW,EAChB,KAAK,YAAc,EAIrB,YAAA,CAAa,EAAgB,GACvB,KAAK,gBAAgB,aAAa,KAAK,gBAC3C,KAAK,eAAiB,WAAA,KACpB,KAAK,SAAS,KAAK,YAAY,IAAU,IACxC,KAAK,aAIV,kBAAM,CAAa,GACjB,OAAO,KAAK,SAAS,KAAK,YAAY,KAIxC,oBAAM,CAAe,GACnB,OAAO,KAAK,SAAS,OAAO,YAAY,KAI1C,KAAA,GACM,KAAK,iBACP,aAAa,KAAK,gBAClB,KAAK,eAAiB"}
|
package/dist/index115.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=class{constructor(){this._descriptors=/* @__PURE__ */new Map}register(t){if(this._descriptors.has(t.name))throw new Error(`Custom indicator '${t.name}' is already registered`);if(!t.compute)throw new Error(`Custom indicator '${t.name}' must have a compute function`);if(0===t.outputs.length)throw new Error(`Custom indicator '${t.name}' must have at least one output`);this._descriptors.set(t.name,t)}unregister(t){return this._descriptors.delete(t)}get(t){return this._descriptors.get(t)}list(){return[...this._descriptors.keys()]}has(t){return this._descriptors.has(t)}getAll(){return[...this._descriptors.values()]}compute(t,r,e){const s=this._descriptors.get(t);if(!s)throw new Error(`Custom indicator '${t}' is not registered`);const o={};for(const i of s.params)o[i.name]=e[i.name]??i.defaultValue;return s.compute(r,o)}};export{t as CustomIndicatorRegistry};
|
|
2
|
+
//# sourceMappingURL=index115.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index115.js","names":[],"sources":["../src/core/custom-indicator.ts"],"sourcesContent":["import type { ColumnStore } from './types';\n\n/**\n * Output descriptor for a custom indicator output line.\n */\nexport interface IndicatorOutput {\n /** Name of this output (e.g., 'upper', 'middle', 'lower'). */\n name: string;\n /** Default color for rendering. */\n color?: string;\n /** Rendering style: 'line' (default), 'histogram', 'dots', 'area'. */\n style?: 'line' | 'histogram' | 'dots' | 'area';\n}\n\n/**\n * Parameter descriptor for a custom indicator parameter.\n */\nexport interface IndicatorParam {\n /** Parameter name. */\n name: string;\n /** Default value. */\n defaultValue: number;\n /** Minimum value (for UI validation). */\n min?: number;\n /** Maximum value (for UI validation). */\n max?: number;\n /** Step for UI controls. */\n step?: number;\n}\n\n/**\n * Compute function signature for custom indicators.\n * Receives the ColumnStore and parameter values, returns a map of output name → Float64Array.\n */\nexport type IndicatorComputeFn = (\n store: ColumnStore,\n params: Record<string, number>,\n) => Map<string, Float64Array>;\n\n/**\n * Descriptor for registering a custom indicator.\n */\nexport interface CustomIndicatorDescriptor {\n /** Unique name for this indicator type. */\n name: string;\n /** Human-readable label for UI. */\n label: string;\n /** Whether this indicator overlays on the main price chart or gets its own pane. */\n overlay: boolean;\n /** Parameter definitions with defaults. */\n params: IndicatorParam[];\n /** Output definitions (multiple outputs supported, e.g., MACD has signal + histogram). */\n outputs: IndicatorOutput[];\n /** Compute function that produces output values from bar data and parameters. */\n compute: IndicatorComputeFn;\n}\n\n/**\n * Registry for custom indicator types.\n * Indicators registered here appear alongside built-in indicators.\n */\nexport class CustomIndicatorRegistry {\n private _descriptors: Map<string, CustomIndicatorDescriptor> = new Map();\n\n /** Register a custom indicator type. */\n register(descriptor: CustomIndicatorDescriptor): void {\n if (this._descriptors.has(descriptor.name)) {\n throw new Error(`Custom indicator '${descriptor.name}' is already registered`);\n }\n if (!descriptor.compute) {\n throw new Error(`Custom indicator '${descriptor.name}' must have a compute function`);\n }\n if (descriptor.outputs.length === 0) {\n throw new Error(`Custom indicator '${descriptor.name}' must have at least one output`);\n }\n this._descriptors.set(descriptor.name, descriptor);\n }\n\n /** Unregister a custom indicator type. */\n unregister(name: string): boolean {\n return this._descriptors.delete(name);\n }\n\n /** Get a registered descriptor by name. */\n get(name: string): CustomIndicatorDescriptor | undefined {\n return this._descriptors.get(name);\n }\n\n /** List all registered custom indicator names. */\n list(): string[] {\n return [...this._descriptors.keys()];\n }\n\n /** Check if a custom indicator type is registered. */\n has(name: string): boolean {\n return this._descriptors.has(name);\n }\n\n /** Get all registered descriptors. */\n getAll(): CustomIndicatorDescriptor[] {\n return [...this._descriptors.values()];\n }\n\n /** Compute a custom indicator's outputs for the given data and params. */\n compute(\n name: string,\n store: ColumnStore,\n params: Record<string, number>,\n ): Map<string, Float64Array> {\n const descriptor = this._descriptors.get(name);\n if (!descriptor) {\n throw new Error(`Custom indicator '${name}' is not registered`);\n }\n // Merge default params with provided params\n const mergedParams: Record<string, number> = {};\n for (const p of descriptor.params) {\n mergedParams[p.name] = params[p.name] ?? p.defaultValue;\n }\n return descriptor.compute(store, mergedParams);\n }\n}\n"],"mappings":"AA6DA,IAAa,EAAb,qDACiE,IAAI,IAGnE,QAAA,CAAS,GACP,GAAI,KAAK,aAAa,IAAI,EAAW,MACnC,MAAM,IAAI,MAAM,qBAAqB,EAAW,+BAElD,IAAK,EAAW,QACd,MAAM,IAAI,MAAM,qBAAqB,EAAW,sCAElD,GAAkC,IAA9B,EAAW,QAAQ,OACrB,MAAM,IAAI,MAAM,qBAAqB,EAAW,uCAElD,KAAK,aAAa,IAAI,EAAW,KAAM,GAIzC,UAAA,CAAW,GACT,OAAO,KAAK,aAAa,OAAO,GAIlC,GAAA,CAAI,GACF,OAAO,KAAK,aAAa,IAAI,GAI/B,IAAA,GACE,MAAO,IAAI,KAAK,aAAa,QAI/B,GAAA,CAAI,GACF,OAAO,KAAK,aAAa,IAAI,GAI/B,MAAA,GACE,MAAO,IAAI,KAAK,aAAa,UAI/B,OAAA,CACE,EACA,EACA,GAEA,MAAM,EAAa,KAAK,aAAa,IAAI,GACzC,IAAK,EACH,MAAM,IAAI,MAAM,qBAAqB,wBAGvC,MAAM,EAAuC,CAAA,EAC7C,IAAK,MAAM,KAAK,EAAW,OACzB,EAAa,EAAE,MAAQ,EAAO,EAAE,OAAS,EAAE,aAE7C,OAAO,EAAW,QAAQ,EAAO"}
|
package/dist/index116.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var o={bg:"--fc-bg",text:"--fc-text",fontSize:"--fc-font-size",fontFamily:"--fc-font-family",gridHorzColor:"--fc-grid-horz-color",gridVertColor:"--fc-grid-vert-color",crosshairVertColor:"--fc-crosshair-vert-color",crosshairHorzColor:"--fc-crosshair-horz-color",candleUpColor:"--fc-candle-up",candleDownColor:"--fc-candle-down",lineColor:"--fc-line-color",areaTopColor:"--fc-area-top",areaBottomColor:"--fc-area-bottom",areaLineColor:"--fc-area-line",volumeUpColor:"--fc-volume-up",volumeDownColor:"--fc-volume-down",watermarkColor:"--fc-watermark-color"};function r(r){const l=getComputedStyle(r),e=o=>l.getPropertyValue(o).trim()||void 0,t=e(o.bg),a=e(o.text),s=e(o.fontSize),i=e(o.fontFamily),n={};(t||a||s||i)&&(n.layout={},t&&(n.layout.backgroundColor=t),a&&(n.layout.textColor=a),s&&(n.layout.fontSize=parseInt(s,10)),i&&(n.layout.fontFamily=i));const C=e(o.gridHorzColor),u=e(o.gridVertColor);(C||u)&&(n.grid={},C&&(n.grid.horzLinesColor=C),u&&(n.grid.vertLinesColor=u));const c=e(o.crosshairVertColor),y=e(o.crosshairHorzColor);(c||y)&&(n.crosshair={},c&&(n.crosshair.vertLineColor=c),y&&(n.crosshair.horzLineColor=y));const m=e(o.volumeUpColor),h=e(o.volumeDownColor);(m||h)&&(n.volume={},m&&(n.volume.upColor=m),h&&(n.volume.downColor=h));const p=e(o.watermarkColor);return p&&(n.watermark={color:p}),n}function l(r){const l=[];return r.layout?.backgroundColor&&l.push(`${o.bg}: ${r.layout.backgroundColor}`),r.layout?.textColor&&l.push(`${o.text}: ${r.layout.textColor}`),r.layout?.fontSize&&l.push(`${o.fontSize}: ${r.layout.fontSize}px`),r.layout?.fontFamily&&l.push(`${o.fontFamily}: ${r.layout.fontFamily}`),r.grid?.horzLinesColor&&l.push(`${o.gridHorzColor}: ${r.grid.horzLinesColor}`),r.grid?.vertLinesColor&&l.push(`${o.gridVertColor}: ${r.grid.vertLinesColor}`),r.crosshair?.vertLineColor&&l.push(`${o.crosshairVertColor}: ${r.crosshair.vertLineColor}`),r.crosshair?.horzLineColor&&l.push(`${o.crosshairHorzColor}: ${r.crosshair.horzLineColor}`),r.volume?.upColor&&l.push(`${o.volumeUpColor}: ${r.volume.upColor}`),r.volume?.downColor&&l.push(`${o.volumeDownColor}: ${r.volume.downColor}`),r.watermark?.color&&l.push(`${o.watermarkColor}: ${r.watermark.color}`),l.join(";\n")}function e(r,l){l.layout?.backgroundColor&&r.style.setProperty(o.bg,l.layout.backgroundColor),l.layout?.textColor&&r.style.setProperty(o.text,l.layout.textColor),l.layout?.fontSize&&r.style.setProperty(o.fontSize,`${l.layout.fontSize}px`),l.layout?.fontFamily&&r.style.setProperty(o.fontFamily,l.layout.fontFamily),l.grid?.horzLinesColor&&r.style.setProperty(o.gridHorzColor,l.grid.horzLinesColor),l.grid?.vertLinesColor&&r.style.setProperty(o.gridVertColor,l.grid.vertLinesColor),l.crosshair?.vertLineColor&&r.style.setProperty(o.crosshairVertColor,l.crosshair.vertLineColor),l.crosshair?.horzLineColor&&r.style.setProperty(o.crosshairHorzColor,l.crosshair.horzLineColor),l.volume?.upColor&&r.style.setProperty(o.volumeUpColor,l.volume.upColor),l.volume?.downColor&&r.style.setProperty(o.volumeDownColor,l.volume.downColor),l.watermark?.color&&r.style.setProperty(o.watermarkColor,l.watermark.color)}export{o as CSS_VARS,e as applyCSSTheme,l as generateCSSTheme,r as readCSSTheme};
|
|
2
|
+
//# sourceMappingURL=index116.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index116.js","names":[],"sources":["../src/core/css-theme.ts"],"sourcesContent":["import type { DeepPartial } from './types';\nimport type { ChartOptions } from '../api/options';\n\n/**\n * CSS custom property names used by fin-charter.\n * All prefixed with --fc- to avoid collisions.\n */\nexport const CSS_VARS = {\n // Layout\n bg: '--fc-bg',\n text: '--fc-text',\n fontSize: '--fc-font-size',\n fontFamily: '--fc-font-family',\n\n // Grid\n gridHorzColor: '--fc-grid-horz-color',\n gridVertColor: '--fc-grid-vert-color',\n\n // Crosshair\n crosshairVertColor: '--fc-crosshair-vert-color',\n crosshairHorzColor: '--fc-crosshair-horz-color',\n\n // Series\n candleUpColor: '--fc-candle-up',\n candleDownColor: '--fc-candle-down',\n lineColor: '--fc-line-color',\n areaTopColor: '--fc-area-top',\n areaBottomColor: '--fc-area-bottom',\n areaLineColor: '--fc-area-line',\n\n // Volume\n volumeUpColor: '--fc-volume-up',\n volumeDownColor: '--fc-volume-down',\n\n // Watermark\n watermarkColor: '--fc-watermark-color',\n} as const;\n\n/**\n * Read CSS custom properties from a container element and return\n * a partial ChartOptions object. Undefined properties (not set in CSS)\n * are omitted so mergeOptions will skip them.\n */\nexport function readCSSTheme(container: HTMLElement): DeepPartial<ChartOptions> {\n const style = getComputedStyle(container);\n const get = (name: string): string | undefined => {\n const val = style.getPropertyValue(name).trim();\n return val || undefined;\n };\n\n const bg = get(CSS_VARS.bg);\n const text = get(CSS_VARS.text);\n const fontSizeStr = get(CSS_VARS.fontSize);\n const fontFamily = get(CSS_VARS.fontFamily);\n\n const opts: DeepPartial<ChartOptions> = {};\n\n if (bg || text || fontSizeStr || fontFamily) {\n opts.layout = {};\n if (bg) opts.layout.backgroundColor = bg;\n if (text) opts.layout.textColor = text;\n if (fontSizeStr) opts.layout.fontSize = parseInt(fontSizeStr, 10);\n if (fontFamily) opts.layout.fontFamily = fontFamily;\n }\n\n const gridH = get(CSS_VARS.gridHorzColor);\n const gridV = get(CSS_VARS.gridVertColor);\n if (gridH || gridV) {\n opts.grid = {};\n if (gridH) opts.grid.horzLinesColor = gridH;\n if (gridV) opts.grid.vertLinesColor = gridV;\n }\n\n const chV = get(CSS_VARS.crosshairVertColor);\n const chH = get(CSS_VARS.crosshairHorzColor);\n if (chV || chH) {\n opts.crosshair = {};\n if (chV) opts.crosshair.vertLineColor = chV;\n if (chH) opts.crosshair.horzLineColor = chH;\n }\n\n const volUp = get(CSS_VARS.volumeUpColor);\n const volDown = get(CSS_VARS.volumeDownColor);\n if (volUp || volDown) {\n opts.volume = {};\n if (volUp) opts.volume.upColor = volUp;\n if (volDown) opts.volume.downColor = volDown;\n }\n\n const wmColor = get(CSS_VARS.watermarkColor);\n if (wmColor) {\n opts.watermark = { color: wmColor };\n }\n\n return opts;\n}\n\n/**\n * Generate a CSS string that sets all custom properties for a theme.\n * Can be applied to a container element via `element.style.cssText` or\n * inserted into a <style> block.\n */\nexport function generateCSSTheme(options: DeepPartial<ChartOptions>): string {\n const vars: string[] = [];\n\n if (options.layout?.backgroundColor) vars.push(`${CSS_VARS.bg}: ${options.layout.backgroundColor}`);\n if (options.layout?.textColor) vars.push(`${CSS_VARS.text}: ${options.layout.textColor}`);\n if (options.layout?.fontSize) vars.push(`${CSS_VARS.fontSize}: ${options.layout.fontSize}px`);\n if (options.layout?.fontFamily) vars.push(`${CSS_VARS.fontFamily}: ${options.layout.fontFamily}`);\n\n if (options.grid?.horzLinesColor) vars.push(`${CSS_VARS.gridHorzColor}: ${options.grid.horzLinesColor}`);\n if (options.grid?.vertLinesColor) vars.push(`${CSS_VARS.gridVertColor}: ${options.grid.vertLinesColor}`);\n\n if (options.crosshair?.vertLineColor) vars.push(`${CSS_VARS.crosshairVertColor}: ${options.crosshair.vertLineColor}`);\n if (options.crosshair?.horzLineColor) vars.push(`${CSS_VARS.crosshairHorzColor}: ${options.crosshair.horzLineColor}`);\n\n if (options.volume?.upColor) vars.push(`${CSS_VARS.volumeUpColor}: ${options.volume.upColor}`);\n if (options.volume?.downColor) vars.push(`${CSS_VARS.volumeDownColor}: ${options.volume.downColor}`);\n\n if (options.watermark?.color) vars.push(`${CSS_VARS.watermarkColor}: ${options.watermark.color}`);\n\n return vars.join(';\\n');\n}\n\n/**\n * Apply CSS custom properties for a theme directly to an element.\n */\nexport function applyCSSTheme(element: HTMLElement, options: DeepPartial<ChartOptions>): void {\n if (options.layout?.backgroundColor) element.style.setProperty(CSS_VARS.bg, options.layout.backgroundColor);\n if (options.layout?.textColor) element.style.setProperty(CSS_VARS.text, options.layout.textColor);\n if (options.layout?.fontSize) element.style.setProperty(CSS_VARS.fontSize, `${options.layout.fontSize}px`);\n if (options.layout?.fontFamily) element.style.setProperty(CSS_VARS.fontFamily, options.layout.fontFamily);\n\n if (options.grid?.horzLinesColor) element.style.setProperty(CSS_VARS.gridHorzColor, options.grid.horzLinesColor);\n if (options.grid?.vertLinesColor) element.style.setProperty(CSS_VARS.gridVertColor, options.grid.vertLinesColor);\n\n if (options.crosshair?.vertLineColor) element.style.setProperty(CSS_VARS.crosshairVertColor, options.crosshair.vertLineColor);\n if (options.crosshair?.horzLineColor) element.style.setProperty(CSS_VARS.crosshairHorzColor, options.crosshair.horzLineColor);\n\n if (options.volume?.upColor) element.style.setProperty(CSS_VARS.volumeUpColor, options.volume.upColor);\n if (options.volume?.downColor) element.style.setProperty(CSS_VARS.volumeDownColor, options.volume.downColor);\n\n if (options.watermark?.color) element.style.setProperty(CSS_VARS.watermarkColor, options.watermark.color);\n}\n"],"mappings":"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"}
|
package/dist/index117.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=class{constructor(t,e={}){this._cache=/* @__PURE__ */new Map,this._pendingRequests=/* @__PURE__ */new Map,this._loading=!1,this._feed=t,this._maxCacheEntries=e.maxCacheEntries??100}get loading(){return this._loading}async getBars(t,e,s,i){const n=`${t}:${e}`,h=this._findCachedBars(n,s,i);if(null!==h)return h;const r=`${n}:${s}:${i}`,a=this._pendingRequests.get(r);if(a)return a;this._loading=!0;const c=this._feed.getBars(t,e,s,i).then(t=>(this._addToCache(n,s,i,t),t)).finally(()=>{this._pendingRequests.delete(r),this._loading=this._pendingRequests.size>0});return this._pendingRequests.set(r,c),c}findGaps(t,e,s,i){const n=`${t}:${e}`,h=this._cache.get(n)??[];if(0===h.length)return[{from:s,to:i}];const r=[...h].sort((t,e)=>t.from-e.from),a=[];let c=s;for(const o of r)if(o.from>c&&a.push({from:c,to:Math.min(o.from,i)}),c=Math.max(c,o.to),c>=i)break;return c<i&&a.push({from:c,to:i}),a}clearCache(t,e){t&&e?this._cache.delete(`${t}:${e}`):this._cache.clear()}cacheSize(t,e){return this._cache.get(`${t}:${e}`)?.length??0}_findCachedBars(t,e,s){const i=this._cache.get(t);if(!i)return null;for(const n of i)if(n.from<=e&&n.to>=s)return n.bars.filter(t=>t.time>=e&&t.time<=s);return null}_addToCache(t,e,s,i){let n=this._cache.get(t);n||(n=[],this._cache.set(t,n)),n.push({from:e,to:s,bars:[...i]}),n.length>this._maxCacheEntries&&n.shift()}};export{t as DataFeedManager};
|
|
2
|
+
//# sourceMappingURL=index117.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index117.js","names":[],"sources":["../src/core/data-feed.ts"],"sourcesContent":["import type { Bar } from './types';\n\n/**\n * IDataFeed — abstraction for fetching OHLCV bar data with pagination.\n *\n * Implementations provide a `getBars()` method that the chart calls when\n * the user scrolls to the edge and more historical data is needed.\n */\nexport interface IDataFeed {\n /**\n * Fetch bars for the given symbol and resolution within the time range.\n * @param symbol - Symbol identifier (e.g., 'AAPL', 'BTCUSD')\n * @param resolution - Bar interval (e.g., '1', '5', '15', '60', 'D', 'W')\n * @param from - Start of range (Unix timestamp, seconds)\n * @param to - End of range (Unix timestamp, seconds)\n * @returns Promise resolving to an array of bars, sorted by time ascending.\n * Empty array means no more data available for the requested range.\n */\n getBars(symbol: string, resolution: string, from: number, to: number): Promise<Bar[]>;\n}\n\n/**\n * CacheEntry tracks fetched ranges to avoid duplicate requests.\n */\ninterface CacheEntry {\n from: number;\n to: number;\n bars: Bar[];\n}\n\nexport interface DataFeedManagerOptions {\n /** Maximum number of cached range entries per symbol+resolution (default: 100). */\n maxCacheEntries?: number;\n}\n\n/**\n * DataFeedManager wraps an IDataFeed with:\n * - Request deduplication (won't re-fetch the same range)\n * - Local cache with configurable size limit\n * - Gap detection for missing ranges\n * - Loading state tracking\n */\nexport class DataFeedManager {\n private _feed: IDataFeed;\n private _cache: Map<string, CacheEntry[]> = new Map();\n private _pendingRequests: Map<string, Promise<Bar[]>> = new Map();\n private _loading = false;\n private _maxCacheEntries: number;\n\n constructor(feed: IDataFeed, options: DataFeedManagerOptions = {}) {\n this._feed = feed;\n this._maxCacheEntries = options.maxCacheEntries ?? 100;\n }\n\n /** Whether a fetch is currently in progress. */\n get loading(): boolean {\n return this._loading;\n }\n\n /**\n * Fetch bars with deduplication and caching.\n * If the requested range (or a superset) is already cached, returns cached data.\n * Otherwise fetches from the underlying data feed and caches the result.\n */\n async getBars(\n symbol: string,\n resolution: string,\n from: number,\n to: number,\n ): Promise<Bar[]> {\n const key = `${symbol}:${resolution}`;\n\n // Check cache first\n const cached = this._findCachedBars(key, from, to);\n if (cached !== null) return cached;\n\n // Deduplicate: if an identical request is in-flight, reuse it\n const requestKey = `${key}:${from}:${to}`;\n const pending = this._pendingRequests.get(requestKey);\n if (pending) return pending;\n\n // Fetch from feed\n this._loading = true;\n const promise = this._feed.getBars(symbol, resolution, from, to)\n .then((bars) => {\n this._addToCache(key, from, to, bars);\n return bars;\n })\n .finally(() => {\n this._pendingRequests.delete(requestKey);\n this._loading = this._pendingRequests.size > 0;\n });\n\n this._pendingRequests.set(requestKey, promise);\n return promise;\n }\n\n /**\n * Detect gaps in cached data and return the missing ranges that need fetching.\n */\n findGaps(symbol: string, resolution: string, from: number, to: number): Array<{ from: number; to: number }> {\n const key = `${symbol}:${resolution}`;\n const entries = this._cache.get(key) ?? [];\n if (entries.length === 0) return [{ from, to }];\n\n // Sort entries by from time\n const sorted = [...entries].sort((a, b) => a.from - b.from);\n const gaps: Array<{ from: number; to: number }> = [];\n\n let cursor = from;\n for (const entry of sorted) {\n if (entry.from > cursor) {\n gaps.push({ from: cursor, to: Math.min(entry.from, to) });\n }\n cursor = Math.max(cursor, entry.to);\n if (cursor >= to) break;\n }\n if (cursor < to) {\n gaps.push({ from: cursor, to });\n }\n\n return gaps;\n }\n\n /** Clear the cache for a specific symbol+resolution, or all if not specified. */\n clearCache(symbol?: string, resolution?: string): void {\n if (symbol && resolution) {\n this._cache.delete(`${symbol}:${resolution}`);\n } else {\n this._cache.clear();\n }\n }\n\n /** Get the number of cached entries for a symbol+resolution. */\n cacheSize(symbol: string, resolution: string): number {\n return this._cache.get(`${symbol}:${resolution}`)?.length ?? 0;\n }\n\n private _findCachedBars(key: string, from: number, to: number): Bar[] | null {\n const entries = this._cache.get(key);\n if (!entries) return null;\n\n // Look for a single entry that covers the full range\n for (const entry of entries) {\n if (entry.from <= from && entry.to >= to) {\n return entry.bars.filter((b) => b.time >= from && b.time <= to);\n }\n }\n return null;\n }\n\n private _addToCache(key: string, from: number, to: number, bars: Bar[]): void {\n let entries = this._cache.get(key);\n if (!entries) {\n entries = [];\n this._cache.set(key, entries);\n }\n\n entries.push({ from, to, bars: [...bars] });\n\n // Enforce max cache size\n if (entries.length > this._maxCacheEntries) {\n entries.shift(); // Remove oldest\n }\n }\n}\n"],"mappings":"AA0CA,IAAa,EAAb,MAOE,WAAA,CAAY,EAAiB,EAAkC,CAAA,8BALnB,IAAI,yCACQ,IAAI,mBACzC,EAIjB,KAAK,MAAQ,EACb,KAAK,iBAAmB,EAAQ,iBAAmB,IAIrD,WAAI,GACF,OAAO,KAAK,SAQd,aAAM,CACJ,EACA,EACA,EACA,GAEA,MAAM,EAAM,GAAG,KAAU,IAGnB,EAAS,KAAK,gBAAgB,EAAK,EAAM,GAC/C,GAAe,OAAX,EAAiB,OAAO,EAG5B,MAAM,EAAa,GAAG,KAAO,KAAQ,IAC/B,EAAU,KAAK,iBAAiB,IAAI,GAC1C,GAAI,EAAS,OAAO,EAGpB,KAAK,UAAW,EAChB,MAAM,EAAU,KAAK,MAAM,QAAQ,EAAQ,EAAY,EAAM,GAC1D,KAAM,IACL,KAAK,YAAY,EAAK,EAAM,EAAI,GACzB,IAER,QAAA,KACC,KAAK,iBAAiB,OAAO,GAC7B,KAAK,SAAW,KAAK,iBAAiB,KAAO,IAIjD,OADA,KAAK,iBAAiB,IAAI,EAAY,GAC/B,EAMT,QAAA,CAAS,EAAgB,EAAoB,EAAc,GACzD,MAAM,EAAM,GAAG,KAAU,IACnB,EAAU,KAAK,OAAO,IAAI,IAAQ,GACxC,GAAuB,IAAnB,EAAQ,OAAc,MAAO,CAAC,CAAE,OAAM,OAG1C,MAAM,EAAS,IAAI,GAAS,KAAA,CAAM,EAAG,IAAM,EAAE,KAAO,EAAE,MAChD,EAA4C,GAElD,IAAI,EAAS,EACb,IAAK,MAAM,KAAS,EAKlB,GAJI,EAAM,KAAO,GACf,EAAK,KAAK,CAAE,KAAM,EAAQ,GAAI,KAAK,IAAI,EAAM,KAAM,KAErD,EAAS,KAAK,IAAI,EAAQ,EAAM,IAC5B,GAAU,EAAI,MAMpB,OAJI,EAAS,GACX,EAAK,KAAK,CAAE,KAAM,EAAQ,OAGrB,EAIT,UAAA,CAAW,EAAiB,GACtB,GAAU,EACZ,KAAK,OAAO,OAAO,GAAG,KAAU,KAEhC,KAAK,OAAO,QAKhB,SAAA,CAAU,EAAgB,GACxB,OAAO,KAAK,OAAO,IAAI,GAAG,KAAU,MAAe,QAAU,EAG/D,eAAA,CAAwB,EAAa,EAAc,GACjD,MAAM,EAAU,KAAK,OAAO,IAAI,GAChC,IAAK,EAAS,OAAO,KAGrB,IAAK,MAAM,KAAS,EAClB,GAAI,EAAM,MAAQ,GAAQ,EAAM,IAAM,EACpC,OAAO,EAAM,KAAK,OAAQ,GAAM,EAAE,MAAQ,GAAQ,EAAE,MAAQ,GAGhE,OAAO,KAGT,WAAA,CAAoB,EAAa,EAAc,EAAY,GACzD,IAAI,EAAU,KAAK,OAAO,IAAI,GACzB,IACH,EAAU,GACV,KAAK,OAAO,IAAI,EAAK,IAGvB,EAAQ,KAAK,CAAE,OAAM,KAAI,KAAM,IAAI,KAG/B,EAAQ,OAAS,KAAK,kBACxB,EAAQ"}
|
package/dist/index118.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var i="#2196F3",e=1,t="dashed",s="",l=!0,r=!0,n=1,h=class{constructor(h){this._onModified=[],this._onCancelled=[],this._cancelled=!1,this.id="order-"+n++,this._price=h.price,this._quantity=h.quantity,this._side=h.side,this._orderType=h.orderType,this._color=h.color??i,this._lineWidth=h.lineWidth??e,this._lineStyle=h.lineStyle??t,this._title=h.title??s,this._draggable=h.draggable??l,this._axisLabelVisible=h.axisLabelVisible??r}get price(){return this._price}get quantity(){return this._quantity}get side(){return this._side}get orderType(){return this._orderType}get color(){return this._color}get lineWidth(){return this._lineWidth}get lineStyle(){return this._lineStyle}get title(){return this._title}get draggable(){return this._draggable}get axisLabelVisible(){return this._axisLabelVisible}get isCancelled(){return this._cancelled}setPrice(i){this._cancelled||(this._price=i,this._fireModified())}setQuantity(i){this._cancelled||(this._quantity=i,this._fireModified())}applyOptions(i){this._cancelled||(void 0!==i.price&&(this._price=i.price),void 0!==i.quantity&&(this._quantity=i.quantity),void 0!==i.side&&(this._side=i.side),void 0!==i.orderType&&(this._orderType=i.orderType),void 0!==i.color&&(this._color=i.color),void 0!==i.lineWidth&&(this._lineWidth=i.lineWidth),void 0!==i.lineStyle&&(this._lineStyle=i.lineStyle),void 0!==i.title&&(this._title=i.title),void 0!==i.draggable&&(this._draggable=i.draggable),void 0!==i.axisLabelVisible&&(this._axisLabelVisible=i.axisLabelVisible),this._fireModified())}cancel(){if(!this._cancelled){this._cancelled=!0;for(const i of this._onCancelled)i(this)}}onModified(i){this._onModified.push(i)}offModified(i){const e=this._onModified.indexOf(i);e>=0&&this._onModified.splice(e,1)}onCancelled(i){this._onCancelled.push(i)}offCancelled(i){const e=this._onCancelled.indexOf(i);e>=0&&this._onCancelled.splice(e,1)}serialize(){return{id:this.id,price:this._price,quantity:this._quantity,side:this._side,orderType:this._orderType,color:this._color,lineWidth:this._lineWidth,lineStyle:this._lineStyle,title:this._title,draggable:this._draggable,axisLabelVisible:this._axisLabelVisible}}_fireModified(){for(const i of this._onModified)i(this)}},d=1,o=class{constructor(i){this.id="position-"+d++,this._entryPrice=i.entryPrice,this._quantity=i.quantity,this._side=i.side,this._color=i.color??("buy"===i.side?"#26a69a":"#ef5350"),this._lineWidth=i.lineWidth??1,this._lineStyle=i.lineStyle??"solid",this._title=i.title??"",this._axisLabelVisible=i.axisLabelVisible??!0}get entryPrice(){return this._entryPrice}get quantity(){return this._quantity}get side(){return this._side}get color(){return this._color}get lineWidth(){return this._lineWidth}get lineStyle(){return this._lineStyle}get title(){return this._title}get axisLabelVisible(){return this._axisLabelVisible}unrealizedPnL(i){const e=i-this._entryPrice;return 0===e?0:"buy"===this._side?e*this._quantity:-e*this._quantity}unrealizedPnLPercent(i){return 0===this._entryPrice?0:this.unrealizedPnL(i)/(this._entryPrice*this._quantity)*100}applyOptions(i){void 0!==i.entryPrice&&(this._entryPrice=i.entryPrice),void 0!==i.quantity&&(this._quantity=i.quantity),void 0!==i.side&&(this._side=i.side),void 0!==i.color&&(this._color=i.color),void 0!==i.lineWidth&&(this._lineWidth=i.lineWidth),void 0!==i.lineStyle&&(this._lineStyle=i.lineStyle),void 0!==i.title&&(this._title=i.title),void 0!==i.axisLabelVisible&&(this._axisLabelVisible=i.axisLabelVisible)}serialize(){return{id:this.id,entryPrice:this._entryPrice,quantity:this._quantity,side:this._side,color:this._color,lineWidth:this._lineWidth,lineStyle:this._lineStyle,title:this._title,axisLabelVisible:this._axisLabelVisible}}};export{h as OrderLine,o as PositionLine};
|
|
2
|
+
//# sourceMappingURL=index118.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index118.js","names":[],"sources":["../src/core/order-line.ts"],"sourcesContent":["// ─── Order Lines & Position Lines ───────────────────────────────────────────\n\nexport type OrderSide = 'buy' | 'sell';\nexport type OrderType = 'limit' | 'stop' | 'market';\n\nexport type OrderModifiedCallback = (order: OrderLine) => void;\nexport type OrderCancelledCallback = (order: OrderLine) => void;\n\nexport interface OrderLineOptions {\n price: number;\n quantity: number;\n side: OrderSide;\n orderType: OrderType;\n color?: string;\n lineWidth?: number;\n lineStyle?: 'solid' | 'dashed' | 'dotted';\n title?: string;\n draggable?: boolean;\n axisLabelVisible?: boolean;\n}\n\nconst DEFAULT_ORDER_LINE_OPTIONS: Required<\n Pick<OrderLineOptions, 'color' | 'lineWidth' | 'lineStyle' | 'title' | 'draggable' | 'axisLabelVisible'>\n> = {\n color: '#2196F3',\n lineWidth: 1,\n lineStyle: 'dashed',\n title: '',\n draggable: true,\n axisLabelVisible: true,\n};\n\nlet nextOrderId = 1;\n\n/**\n * OrderLine represents a pending order on the chart (limit, stop, or market).\n * It extends the concept of PriceLine with trading-specific properties such as\n * quantity, side, order type, and drag-to-modify support.\n */\nexport class OrderLine {\n readonly id: string;\n private _price: number;\n private _quantity: number;\n private _side: OrderSide;\n private _orderType: OrderType;\n private _color: string;\n private _lineWidth: number;\n private _lineStyle: 'solid' | 'dashed' | 'dotted';\n private _title: string;\n private _draggable: boolean;\n private _axisLabelVisible: boolean;\n private _onModified: OrderModifiedCallback[] = [];\n private _onCancelled: OrderCancelledCallback[] = [];\n private _cancelled = false;\n\n constructor(options: OrderLineOptions) {\n this.id = `order-${nextOrderId++}`;\n this._price = options.price;\n this._quantity = options.quantity;\n this._side = options.side;\n this._orderType = options.orderType;\n this._color = options.color ?? DEFAULT_ORDER_LINE_OPTIONS.color;\n this._lineWidth = options.lineWidth ?? DEFAULT_ORDER_LINE_OPTIONS.lineWidth;\n this._lineStyle = options.lineStyle ?? DEFAULT_ORDER_LINE_OPTIONS.lineStyle;\n this._title = options.title ?? DEFAULT_ORDER_LINE_OPTIONS.title;\n this._draggable = options.draggable ?? DEFAULT_ORDER_LINE_OPTIONS.draggable;\n this._axisLabelVisible = options.axisLabelVisible ?? DEFAULT_ORDER_LINE_OPTIONS.axisLabelVisible;\n }\n\n get price(): number { return this._price; }\n get quantity(): number { return this._quantity; }\n get side(): OrderSide { return this._side; }\n get orderType(): OrderType { return this._orderType; }\n get color(): string { return this._color; }\n get lineWidth(): number { return this._lineWidth; }\n get lineStyle(): 'solid' | 'dashed' | 'dotted' { return this._lineStyle; }\n get title(): string { return this._title; }\n get draggable(): boolean { return this._draggable; }\n get axisLabelVisible(): boolean { return this._axisLabelVisible; }\n get isCancelled(): boolean { return this._cancelled; }\n\n /** Update the order price (e.g. after a drag). Fires onModified callbacks. */\n setPrice(price: number): void {\n if (this._cancelled) return;\n this._price = price;\n this._fireModified();\n }\n\n /** Update the order quantity. Fires onModified callbacks. */\n setQuantity(quantity: number): void {\n if (this._cancelled) return;\n this._quantity = quantity;\n this._fireModified();\n }\n\n /** Apply partial option updates. Fires onModified callbacks. */\n applyOptions(opts: Partial<OrderLineOptions>): void {\n if (this._cancelled) return;\n if (opts.price !== undefined) this._price = opts.price;\n if (opts.quantity !== undefined) this._quantity = opts.quantity;\n if (opts.side !== undefined) this._side = opts.side;\n if (opts.orderType !== undefined) this._orderType = opts.orderType;\n if (opts.color !== undefined) this._color = opts.color;\n if (opts.lineWidth !== undefined) this._lineWidth = opts.lineWidth;\n if (opts.lineStyle !== undefined) this._lineStyle = opts.lineStyle;\n if (opts.title !== undefined) this._title = opts.title;\n if (opts.draggable !== undefined) this._draggable = opts.draggable;\n if (opts.axisLabelVisible !== undefined) this._axisLabelVisible = opts.axisLabelVisible;\n this._fireModified();\n }\n\n /** Cancel this order. Fires onCancelled callbacks. */\n cancel(): void {\n if (this._cancelled) return;\n this._cancelled = true;\n for (const cb of this._onCancelled) cb(this);\n }\n\n onModified(callback: OrderModifiedCallback): void {\n this._onModified.push(callback);\n }\n\n offModified(callback: OrderModifiedCallback): void {\n const idx = this._onModified.indexOf(callback);\n if (idx >= 0) this._onModified.splice(idx, 1);\n }\n\n onCancelled(callback: OrderCancelledCallback): void {\n this._onCancelled.push(callback);\n }\n\n offCancelled(callback: OrderCancelledCallback): void {\n const idx = this._onCancelled.indexOf(callback);\n if (idx >= 0) this._onCancelled.splice(idx, 1);\n }\n\n /** Serialize to a plain object for persistence. */\n serialize(): {\n id: string;\n price: number;\n quantity: number;\n side: OrderSide;\n orderType: OrderType;\n color: string;\n lineWidth: number;\n lineStyle: string;\n title: string;\n draggable: boolean;\n axisLabelVisible: boolean;\n } {\n return {\n id: this.id,\n price: this._price,\n quantity: this._quantity,\n side: this._side,\n orderType: this._orderType,\n color: this._color,\n lineWidth: this._lineWidth,\n lineStyle: this._lineStyle,\n title: this._title,\n draggable: this._draggable,\n axisLabelVisible: this._axisLabelVisible,\n };\n }\n\n private _fireModified(): void {\n for (const cb of this._onModified) cb(this);\n }\n}\n\n// ─── Position Line ──────────────────────────────────────────────────────────\n\nexport interface PositionLineOptions {\n entryPrice: number;\n quantity: number;\n side: OrderSide;\n color?: string;\n lineWidth?: number;\n lineStyle?: 'solid' | 'dashed' | 'dotted';\n title?: string;\n axisLabelVisible?: boolean;\n}\n\nlet nextPositionId = 1;\n\n/**\n * PositionLine represents an open trading position on the chart.\n * It shows the entry price, quantity, and calculates unrealized P&L\n * given a current market price.\n */\nexport class PositionLine {\n readonly id: string;\n private _entryPrice: number;\n private _quantity: number;\n private _side: OrderSide;\n private _color: string;\n private _lineWidth: number;\n private _lineStyle: 'solid' | 'dashed' | 'dotted';\n private _title: string;\n private _axisLabelVisible: boolean;\n\n constructor(options: PositionLineOptions) {\n this.id = `position-${nextPositionId++}`;\n this._entryPrice = options.entryPrice;\n this._quantity = options.quantity;\n this._side = options.side;\n this._color = options.color ?? (options.side === 'buy' ? '#26a69a' : '#ef5350');\n this._lineWidth = options.lineWidth ?? 1;\n this._lineStyle = options.lineStyle ?? 'solid';\n this._title = options.title ?? '';\n this._axisLabelVisible = options.axisLabelVisible ?? true;\n }\n\n get entryPrice(): number { return this._entryPrice; }\n get quantity(): number { return this._quantity; }\n get side(): OrderSide { return this._side; }\n get color(): string { return this._color; }\n get lineWidth(): number { return this._lineWidth; }\n get lineStyle(): 'solid' | 'dashed' | 'dotted' { return this._lineStyle; }\n get title(): string { return this._title; }\n get axisLabelVisible(): boolean { return this._axisLabelVisible; }\n\n /**\n * Calculate unrealized profit/loss given the current market price.\n * Positive means profit, negative means loss.\n */\n unrealizedPnL(currentPrice: number): number {\n const diff = currentPrice - this._entryPrice;\n if (diff === 0) return 0;\n return this._side === 'buy'\n ? diff * this._quantity\n : -diff * this._quantity;\n }\n\n /**\n * Calculate unrealized P&L as a percentage of the entry value.\n */\n unrealizedPnLPercent(currentPrice: number): number {\n if (this._entryPrice === 0) return 0;\n const pnl = this.unrealizedPnL(currentPrice);\n return (pnl / (this._entryPrice * this._quantity)) * 100;\n }\n\n /** Apply partial option updates. */\n applyOptions(opts: Partial<PositionLineOptions>): void {\n if (opts.entryPrice !== undefined) this._entryPrice = opts.entryPrice;\n if (opts.quantity !== undefined) this._quantity = opts.quantity;\n if (opts.side !== undefined) this._side = opts.side;\n if (opts.color !== undefined) this._color = opts.color;\n if (opts.lineWidth !== undefined) this._lineWidth = opts.lineWidth;\n if (opts.lineStyle !== undefined) this._lineStyle = opts.lineStyle;\n if (opts.title !== undefined) this._title = opts.title;\n if (opts.axisLabelVisible !== undefined) this._axisLabelVisible = opts.axisLabelVisible;\n }\n\n /** Serialize to a plain object for persistence. */\n serialize(): {\n id: string;\n entryPrice: number;\n quantity: number;\n side: OrderSide;\n color: string;\n lineWidth: number;\n lineStyle: string;\n title: string;\n axisLabelVisible: boolean;\n } {\n return {\n id: this.id,\n entryPrice: this._entryPrice,\n quantity: this._quantity,\n side: this._side,\n color: this._color,\n lineWidth: this._lineWidth,\n lineStyle: this._lineStyle,\n title: this._title,\n axisLabelVisible: this._axisLabelVisible,\n };\n }\n}\n"],"mappings":"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"}
|
package/dist/index119.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var n="en",t={};function r(t){n=t}function c(){return n}function o(n,r){t[n]={...t[n],...r}}function e(r,c){const o=t[n]?.[r]??t.en?.[r]??r;return c?o.replace(/\{\{(\w+)\}\}/g,(n,t)=>{const r=c[t];return void 0!==r?String(r):n}):o}async function i(n,t){o(n,await t())}export{c as getLocale,i as loadLocale,o as registerLocale,r as setLocale,e as t};
|
|
2
|
+
//# sourceMappingURL=index119.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index119.js","names":[],"sources":["../src/i18n/i18n.ts"],"sourcesContent":["export type Translations = Record<string, string>;\n\nlet _locale = 'en';\nconst _registry: Record<string, Translations> = {};\n\nexport function setLocale(lang: string): void {\n _locale = lang;\n}\n\nexport function getLocale(): string {\n return _locale;\n}\n\nexport function registerLocale(lang: string, translations: Translations): void {\n _registry[lang] = { ..._registry[lang], ...translations };\n}\n\nexport function t(key: string, params?: Record<string, string | number>): string {\n const val = _registry[_locale]?.[key] ?? _registry['en']?.[key] ?? key;\n if (!params) return val;\n return val.replace(/\\{\\{(\\w+)\\}\\}/g, (match, k) => {\n const v = params[k];\n return v !== undefined ? String(v) : match;\n });\n}\n\nexport async function loadLocale(\n lang: string,\n loader: () => Promise<Translations>,\n): Promise<void> {\n registerLocale(lang, await loader());\n}\n"],"mappings":"AAEA,IAAI,EAAU,KACR,EAA0C,CAAA,EAEhD,SAAgB,EAAU,GACxB,EAAU,EAGZ,SAAgB,IACd,OAAO,EAGT,SAAgB,EAAe,EAAc,GAC3C,EAAU,GAAQ,IAAK,EAAU,MAAU,GAG7C,SAAgB,EAAE,EAAa,GAC7B,MAAM,EAAM,EAAU,KAAW,IAAQ,EAAU,KAAQ,IAAQ,EACnE,OAAK,EACE,EAAI,QAAQ,iBAAA,CAAmB,EAAO,KAC3C,MAAM,EAAI,EAAO,GACjB,YAAa,IAAN,EAAkB,OAAO,GAAK,IAHnB,EAOtB,eAAsB,EACpB,EACA,GAEA,EAAe,QAAY"}
|
package/dist/index12.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var i={price:0,color:"#FF9800",lineWidth:1,lineStyle:"dashed",title:"",triggerMode:"crossing-either",armed:!0,axisLabelVisible:!0},s=class{constructor(s,t,e){this._callbacks=[],this._lastPrice=null,this.id=s,this._options={...i,...t},this._requestRepaint=e??null}get options(){return{...this._options}}applyOptions(i){Object.assign(this._options,i),this._requestRepaint?.()}onTriggered(i){this._callbacks.push(i)}offTriggered(i){const s=this._callbacks.indexOf(i);s>=0&&this._callbacks.splice(s,1)}setArmed(i){this._options.armed=i,this._requestRepaint?.()}isArmed(){return this._options.armed}checkCrossing(i){if(!this._options.armed)return;const s=this._options.price,t=this._lastPrice;if(this._lastPrice=i,null===t)return;const e=t<=s&&i>s,r=t>=s&&i<s,{triggerMode:o}=this._options;let n=null;if(!e||"crossing-up"!==o&&"crossing-either"!==o?!r||"crossing-down"!==o&&"crossing-either"!==o||(n="down"):n="up",n)for(const l of this._callbacks)l(this,n)}serialize(){return{id:this.id,options:{...this._options}}}};export{s as AlertLine};
|
|
2
|
+
//# sourceMappingURL=index12.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index12.js","names":[],"sources":["../src/core/alert-line.ts"],"sourcesContent":["// ─── Alert Lines ────────────────────────────────────────────────────────────\n\nexport type AlertTriggerMode = 'crossing-up' | 'crossing-down' | 'crossing-either';\n\nexport type AlertLineCallback = (alert: AlertLine, direction: 'up' | 'down') => void;\n\nexport interface AlertLineOptions {\n price: number;\n color: string;\n lineWidth: number;\n lineStyle: 'solid' | 'dashed' | 'dotted';\n title: string;\n triggerMode: AlertTriggerMode;\n /** Whether the alert is armed (will fire callbacks on crossing). */\n armed: boolean;\n /** Show a bell icon on the price axis. */\n axisLabelVisible: boolean;\n axisLabelColor?: string;\n axisLabelTextColor?: string;\n}\n\nexport const DEFAULT_ALERT_LINE_OPTIONS: AlertLineOptions = {\n price: 0,\n color: '#FF9800',\n lineWidth: 1,\n lineStyle: 'dashed',\n title: '',\n triggerMode: 'crossing-either',\n armed: true,\n axisLabelVisible: true,\n};\n\n/**\n * AlertLine — a price level that fires callbacks when the current price\n * crosses it. Distinct from PriceLine: alerts have armed/disarmed state,\n * trigger modes, and can be dragged to adjust the price.\n */\nexport class AlertLine {\n readonly id: string;\n private _options: AlertLineOptions;\n private _callbacks: AlertLineCallback[] = [];\n private _lastPrice: number | null = null;\n private _requestRepaint: (() => void) | null;\n\n constructor(id: string, options: AlertLineOptions, requestRepaint?: () => void) {\n this.id = id;\n this._options = { ...DEFAULT_ALERT_LINE_OPTIONS, ...options };\n this._requestRepaint = requestRepaint ?? null;\n }\n\n get options(): Readonly<AlertLineOptions> {\n return { ...this._options };\n }\n\n applyOptions(opts: Partial<AlertLineOptions>): void {\n Object.assign(this._options, opts);\n this._requestRepaint?.();\n }\n\n /** Subscribe to alert trigger events. */\n onTriggered(callback: AlertLineCallback): void {\n this._callbacks.push(callback);\n }\n\n /** Unsubscribe from alert trigger events. */\n offTriggered(callback: AlertLineCallback): void {\n const idx = this._callbacks.indexOf(callback);\n if (idx >= 0) this._callbacks.splice(idx, 1);\n }\n\n /** Arm or disarm the alert. */\n setArmed(armed: boolean): void {\n this._options.armed = armed;\n this._requestRepaint?.();\n }\n\n isArmed(): boolean {\n return this._options.armed;\n }\n\n /**\n * Check if the current price has crossed the alert level since the last\n * check, and fire callbacks if so. Called by the chart on each data update.\n */\n checkCrossing(currentPrice: number): void {\n if (!this._options.armed) return;\n\n const alertPrice = this._options.price;\n const prevPrice = this._lastPrice;\n this._lastPrice = currentPrice;\n\n if (prevPrice === null) return;\n\n const crossedUp = prevPrice <= alertPrice && currentPrice > alertPrice;\n const crossedDown = prevPrice >= alertPrice && currentPrice < alertPrice;\n\n const { triggerMode } = this._options;\n let direction: 'up' | 'down' | null = null;\n\n if (crossedUp && (triggerMode === 'crossing-up' || triggerMode === 'crossing-either')) {\n direction = 'up';\n } else if (crossedDown && (triggerMode === 'crossing-down' || triggerMode === 'crossing-either')) {\n direction = 'down';\n }\n\n if (direction) {\n for (const cb of this._callbacks) cb(this, direction);\n }\n }\n\n /** Serialize to a plain object for chart state save/restore. */\n serialize(): { id: string; options: AlertLineOptions } {\n return { id: this.id, options: { ...this._options } };\n }\n}\n"],"mappings":"AAqBA,IAAa,EAA+C,CAC1D,MAAO,EACP,MAAO,UACP,UAAW,EACX,UAAW,SACX,MAAO,GACP,YAAa,kBACb,OAAO,EACP,kBAAkB,GAQP,EAAb,MAOE,WAAA,CAAY,EAAY,EAA2B,mBAJT,mBACN,KAIlC,KAAK,GAAK,EACV,KAAK,SAAW,IAAK,KAA+B,GACpD,KAAK,gBAAkB,GAAkB,KAG3C,WAAI,GACF,MAAO,IAAK,KAAK,UAGnB,YAAA,CAAa,GACX,OAAO,OAAO,KAAK,SAAU,GAC7B,KAAK,oBAIP,WAAA,CAAY,GACV,KAAK,WAAW,KAAK,GAIvB,YAAA,CAAa,GACX,MAAM,EAAM,KAAK,WAAW,QAAQ,GAChC,GAAO,GAAG,KAAK,WAAW,OAAO,EAAK,GAI5C,QAAA,CAAS,GACP,KAAK,SAAS,MAAQ,EACtB,KAAK,oBAGP,OAAA,GACE,OAAO,KAAK,SAAS,MAOvB,aAAA,CAAc,GACZ,IAAK,KAAK,SAAS,MAAO,OAE1B,MAAM,EAAa,KAAK,SAAS,MAC3B,EAAY,KAAK,WAGvB,GAFA,KAAK,WAAa,EAEA,OAAd,EAAoB,OAExB,MAAM,EAAY,GAAa,GAAc,EAAe,EACtD,EAAc,GAAa,GAAc,EAAe,GAExD,YAAE,GAAgB,KAAK,SAC7B,IAAI,EAAkC,KAQtC,IANI,GAA8B,gBAAhB,GAAiD,oBAAhB,GAExC,GAAgC,kBAAhB,GAAmD,oBAAhB,IAC5D,EAAY,QAFZ,EAAY,KAKV,EACF,IAAK,MAAM,KAAM,KAAK,WAAY,EAAG,KAAM,GAK/C,SAAA,GACE,MAAO,CAAE,GAAI,KAAK,GAAI,QAAS,IAAK,KAAK"}
|
package/dist/index120.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e={"month.jan":"Jan","month.feb":"Feb","month.mar":"Mar","month.apr":"Apr","month.may":"May","month.jun":"Jun","month.jul":"Jul","month.aug":"Aug","month.sep":"Sep","month.oct":"Oct","month.nov":"Nov","month.dec":"Dec","day.sun":"Sun","day.mon":"Mon","day.tue":"Tue","day.wed":"Wed","day.thu":"Thu","day.fri":"Fri","day.sat":"Sat","session.pre":"Pre-Market","session.regular":"Regular","session.post":"Post-Market","session.closed":"Closed","label.open":"O","label.high":"H","label.low":"L","label.close":"C","label.volume":"V","volume.K":"K","volume.M":"M","volume.B":"B","volume.T":"T"};export{e as default};
|
|
2
|
+
//# sourceMappingURL=index120.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index120.js","names":[],"sources":["../src/i18n/locales/en.ts"],"sourcesContent":["import type { Translations } from '../i18n';\n\nconst en: Translations = {\n 'month.jan': 'Jan', 'month.feb': 'Feb', 'month.mar': 'Mar',\n 'month.apr': 'Apr', 'month.may': 'May', 'month.jun': 'Jun',\n 'month.jul': 'Jul', 'month.aug': 'Aug', 'month.sep': 'Sep',\n 'month.oct': 'Oct', 'month.nov': 'Nov', 'month.dec': 'Dec',\n 'day.sun': 'Sun', 'day.mon': 'Mon', 'day.tue': 'Tue',\n 'day.wed': 'Wed', 'day.thu': 'Thu', 'day.fri': 'Fri', 'day.sat': 'Sat',\n 'session.pre': 'Pre-Market', 'session.regular': 'Regular',\n 'session.post': 'Post-Market', 'session.closed': 'Closed',\n 'label.open': 'O', 'label.high': 'H', 'label.low': 'L',\n 'label.close': 'C', 'label.volume': 'V',\n 'volume.K': 'K', 'volume.M': 'M', 'volume.B': 'B', 'volume.T': 'T',\n};\n\nexport default en;\n"],"mappings":"AAEA,IAAM,EAAmB,CACvB,YAAa,MAAO,YAAa,MAAO,YAAa,MACrD,YAAa,MAAO,YAAa,MAAO,YAAa,MACrD,YAAa,MAAO,YAAa,MAAO,YAAa,MACrD,YAAa,MAAO,YAAa,MAAO,YAAa,MACrD,UAAW,MAAO,UAAW,MAAO,UAAW,MAC/C,UAAW,MAAO,UAAW,MAAO,UAAW,MAAO,UAAW,MACjE,cAAe,aAAc,kBAAmB,UAChD,eAAgB,cAAe,iBAAkB,SACjD,aAAc,IAAK,aAAc,IAAK,YAAa,IACnD,cAAe,IAAK,eAAgB,IACpC,WAAY,IAAK,WAAY,IAAK,WAAY,IAAK,WAAY"}
|
package/dist/index121.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var m={USD:{code:"USD",symbol:"$",decimals:2},EUR:{code:"EUR",symbol:"€",decimals:2},GBP:{code:"GBP",symbol:"£",decimals:2},JPY:{code:"JPY",symbol:"¥",decimals:0},CHF:{code:"CHF",symbol:"CHF",decimals:2},CAD:{code:"CAD",symbol:"CA$",decimals:2},AUD:{code:"AUD",symbol:"A$",decimals:2},CNY:{code:"CNY",symbol:"¥",decimals:2},HKD:{code:"HKD",symbol:"HK$",decimals:2},INR:{code:"INR",symbol:"₹",decimals:2},KRW:{code:"KRW",symbol:"₩",decimals:0},SGD:{code:"SGD",symbol:"S$",decimals:2},BRL:{code:"BRL",symbol:"R$",decimals:2},BTC:{code:"BTC",symbol:"₿",decimals:8},ETH:{code:"ETH",symbol:"Ξ",decimals:8}};function e(e){return m[e]??{code:e,symbol:e,decimals:2}}var c=/* @__PURE__ */new Map;function o(m,o,s="en-US"){if(!isFinite(m))return"-";const i=e(o),l=`${s}|${o}`;let d=c.get(l);if(!d){try{const m=new Intl.NumberFormat(s,{style:"currency",currency:o,minimumFractionDigits:i.decimals,maximumFractionDigits:i.decimals});d=e=>m.format(e)}catch{const m=new Intl.NumberFormat(s,{minimumFractionDigits:i.decimals,maximumFractionDigits:i.decimals});d=e=>`${i.symbol}${m.format(e)}`}c.set(l,d)}return d(m)}export{m as CURRENCIES,o as formatCurrency,e as getCurrencyInfo};
|
|
2
|
+
//# sourceMappingURL=index121.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index121.js","names":[],"sources":["../src/currency/currency.ts"],"sourcesContent":["export interface CurrencyInfo {\n code: string;\n symbol: string;\n decimals: number;\n}\n\nexport const CURRENCIES: Record<string, CurrencyInfo> = {\n USD: { code: 'USD', symbol: '$', decimals: 2 },\n EUR: { code: 'EUR', symbol: '€', decimals: 2 },\n GBP: { code: 'GBP', symbol: '£', decimals: 2 },\n JPY: { code: 'JPY', symbol: '¥', decimals: 0 },\n CHF: { code: 'CHF', symbol: 'CHF', decimals: 2 },\n CAD: { code: 'CAD', symbol: 'CA$', decimals: 2 },\n AUD: { code: 'AUD', symbol: 'A$', decimals: 2 },\n CNY: { code: 'CNY', symbol: '¥', decimals: 2 },\n HKD: { code: 'HKD', symbol: 'HK$', decimals: 2 },\n INR: { code: 'INR', symbol: '₹', decimals: 2 },\n KRW: { code: 'KRW', symbol: '₩', decimals: 0 },\n SGD: { code: 'SGD', symbol: 'S$', decimals: 2 },\n BRL: { code: 'BRL', symbol: 'R$', decimals: 2 },\n BTC: { code: 'BTC', symbol: '₿', decimals: 8 },\n ETH: { code: 'ETH', symbol: 'Ξ', decimals: 8 },\n};\n\nexport function getCurrencyInfo(code: string): CurrencyInfo {\n return CURRENCIES[code] ?? { code, symbol: code, decimals: 2 };\n}\n\nconst _fmtCache = new Map<string, (v: number) => string>();\n\nexport function formatCurrency(\n value: number,\n currencyCode: string,\n locale: string = 'en-US',\n): string {\n if (!isFinite(value)) return '-';\n const info = getCurrencyInfo(currencyCode);\n const key = `${locale}|${currencyCode}`;\n let formatter = _fmtCache.get(key);\n if (!formatter) {\n try {\n const fmt = new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: currencyCode,\n minimumFractionDigits: info.decimals,\n maximumFractionDigits: info.decimals,\n });\n formatter = (v: number) => fmt.format(v);\n } catch {\n // Fallback for non-ISO currencies like BTC\n const fmt = new Intl.NumberFormat(locale, {\n minimumFractionDigits: info.decimals,\n maximumFractionDigits: info.decimals,\n });\n formatter = (v: number) => `${info.symbol}${fmt.format(v)}`;\n }\n _fmtCache.set(key, formatter);\n }\n return formatter(value);\n}\n"],"mappings":"AAMA,IAAa,EAA2C,CACtD,IAAK,CAAE,KAAM,MAAO,OAAQ,IAAK,SAAU,GAC3C,IAAK,CAAE,KAAM,MAAO,OAAQ,IAAK,SAAU,GAC3C,IAAK,CAAE,KAAM,MAAO,OAAQ,IAAK,SAAU,GAC3C,IAAK,CAAE,KAAM,MAAO,OAAQ,IAAK,SAAU,GAC3C,IAAK,CAAE,KAAM,MAAO,OAAQ,MAAO,SAAU,GAC7C,IAAK,CAAE,KAAM,MAAO,OAAQ,MAAO,SAAU,GAC7C,IAAK,CAAE,KAAM,MAAO,OAAQ,KAAM,SAAU,GAC5C,IAAK,CAAE,KAAM,MAAO,OAAQ,IAAK,SAAU,GAC3C,IAAK,CAAE,KAAM,MAAO,OAAQ,MAAO,SAAU,GAC7C,IAAK,CAAE,KAAM,MAAO,OAAQ,IAAK,SAAU,GAC3C,IAAK,CAAE,KAAM,MAAO,OAAQ,IAAK,SAAU,GAC3C,IAAK,CAAE,KAAM,MAAO,OAAQ,KAAM,SAAU,GAC5C,IAAK,CAAE,KAAM,MAAO,OAAQ,KAAM,SAAU,GAC5C,IAAK,CAAE,KAAM,MAAO,OAAQ,IAAK,SAAU,GAC3C,IAAK,CAAE,KAAM,MAAO,OAAQ,IAAK,SAAU,IAG7C,SAAgB,EAAgB,GAC9B,OAAO,EAAW,IAAS,CAAE,OAAM,OAAQ,EAAM,SAAU,GAG7D,IAAM,iBAAY,IAAI,IAEtB,SAAgB,EACd,EACA,EACA,EAAiB,SAEjB,IAAK,SAAS,GAAQ,MAAO,IAC7B,MAAM,EAAO,EAAgB,GACvB,EAAM,GAAG,KAAU,IACzB,IAAI,EAAY,EAAU,IAAI,GAC9B,IAAK,EAAW,CACd,IACE,MAAM,EAAM,IAAI,KAAK,aAAa,EAAQ,CACxC,MAAO,WACP,SAAU,EACV,sBAAuB,EAAK,SAC5B,sBAAuB,EAAK,WAE9B,EAAa,GAAc,EAAI,OAAO,SAGtC,MAAM,EAAM,IAAI,KAAK,aAAa,EAAQ,CACxC,sBAAuB,EAAK,SAC5B,sBAAuB,EAAK,WAE9B,EAAa,GAAc,GAAG,EAAK,SAAS,EAAI,OAAO,KAEzD,EAAU,IAAI,EAAK,GAErB,OAAO,EAAU"}
|
package/dist/index122.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=/* @__PURE__ */new Map,a={id:"us",name:"US Equities",timezone:"America/New_York",currency:"USD",sessions:[{id:"premarket",label:"PRE",startMinute:240,endMinute:570,bgColor:"rgba(255,235,59,0.05)"},{id:"regular",label:"",startMinute:570,endMinute:960,bgColor:"transparent"},{id:"postmarket",label:"POST",startMinute:960,endMinute:1200,bgColor:"rgba(255,235,59,0.05)"}],holidays:[{date:"2025-01-01",name:"New Year's Day"},{date:"2025-01-20",name:"MLK Day"},{date:"2025-02-17",name:"Presidents' Day"},{date:"2025-04-18",name:"Good Friday"},{date:"2025-05-26",name:"Memorial Day"},{date:"2025-06-19",name:"Juneteenth"},{date:"2025-07-04",name:"Independence Day"},{date:"2025-09-01",name:"Labor Day"},{date:"2025-11-27",name:"Thanksgiving"},{date:"2025-12-25",name:"Christmas"}]},n={id:"uk",name:"London Stock Exchange",timezone:"Europe/London",currency:"GBP",sessions:[{id:"regular",label:"",startMinute:480,endMinute:990,bgColor:"transparent"}],holidays:[{date:"2025-01-01",name:"New Year's Day"},{date:"2025-04-18",name:"Good Friday"},{date:"2025-04-21",name:"Easter Monday"},{date:"2025-05-05",name:"Early May Bank Holiday"},{date:"2025-05-26",name:"Spring Bank Holiday"},{date:"2025-08-25",name:"Summer Bank Holiday"},{date:"2025-12-25",name:"Christmas"},{date:"2025-12-26",name:"Boxing Day"}]},t={id:"jp",name:"Japan Exchange",timezone:"Asia/Tokyo",currency:"JPY",sessions:[{id:"morning",label:"AM",startMinute:540,endMinute:690,bgColor:"transparent"},{id:"afternoon",label:"PM",startMinute:750,endMinute:900,bgColor:"transparent"}],holidays:[{date:"2025-01-01",name:"New Year's Day"},{date:"2025-01-02",name:"Bank Holiday"},{date:"2025-01-03",name:"Bank Holiday"},{date:"2025-01-13",name:"Coming of Age Day"},{date:"2025-02-11",name:"National Foundation Day"},{date:"2025-02-23",name:"Emperor's Birthday"},{date:"2025-03-20",name:"Vernal Equinox"},{date:"2025-04-29",name:"Showa Day"},{date:"2025-05-03",name:"Constitution Memorial Day"},{date:"2025-05-05",name:"Children's Day"},{date:"2025-05-06",name:"Substitute Holiday"}]},r={id:"de",name:"Deutsche Boerse",timezone:"Europe/Berlin",currency:"EUR",sessions:[{id:"regular",label:"",startMinute:540,endMinute:1050,bgColor:"transparent"}],holidays:[{date:"2025-01-01",name:"New Year's Day"},{date:"2025-04-18",name:"Good Friday"},{date:"2025-04-21",name:"Easter Monday"},{date:"2025-05-01",name:"Labour Day"},{date:"2025-12-24",name:"Christmas Eve"},{date:"2025-12-25",name:"Christmas"},{date:"2025-12-26",name:"St Stephen's Day"},{date:"2025-12-31",name:"New Year's Eve"}]},d={id:"au",name:"Australian Securities Exchange",timezone:"Australia/Sydney",currency:"AUD",sessions:[{id:"preopen",label:"PRE",startMinute:420,endMinute:600,bgColor:"rgba(255,235,59,0.05)"},{id:"regular",label:"",startMinute:600,endMinute:960,bgColor:"transparent"}],holidays:[{date:"2025-01-01",name:"New Year's Day"},{date:"2025-01-27",name:"Australia Day"},{date:"2025-04-18",name:"Good Friday"},{date:"2025-04-21",name:"Easter Monday"},{date:"2025-04-25",name:"ANZAC Day"},{date:"2025-06-09",name:"Queen's Birthday"},{date:"2025-12-25",name:"Christmas"},{date:"2025-12-26",name:"Boxing Day"}]},i={id:"crypto",name:"Cryptocurrency",timezone:"UTC",currency:"USD",sessions:[{id:"regular",label:"",startMinute:0,endMinute:1440,bgColor:"transparent"}],holidays:[]};for(const m of[a,n,t,r,d,i])e.set(m.id,m);function o(a){return e.get(a)}function s(a){e.set(a.id,a)}export{d as AU_MARKET,i as CRYPTO_MARKET,r as DE_MARKET,t as JP_MARKET,n as UK_MARKET,a as US_MARKET,o as getMarket,s as registerMarket};
|
|
2
|
+
//# sourceMappingURL=index122.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index122.js","names":[],"sources":["../src/market/market-definition.ts"],"sourcesContent":["import type { MarketSession } from '../core/market-session';\n\nexport interface MarketHoliday {\n date: string; // 'YYYY-MM-DD'\n name?: string;\n earlyClose?: number; // minute of day for early close, or omit for full day\n}\n\nexport interface MarketDefinition {\n id: string;\n name: string;\n timezone: string; // IANA timezone\n currency: string; // ISO 4217\n sessions: MarketSession[];\n holidays: MarketHoliday[];\n}\n\nconst _markets = new Map<string, MarketDefinition>();\n\nexport const US_MARKET: MarketDefinition = {\n id: 'us',\n name: 'US Equities',\n timezone: 'America/New_York',\n currency: 'USD',\n sessions: [\n { id: 'premarket', label: 'PRE', startMinute: 240, endMinute: 570, bgColor: 'rgba(255,235,59,0.05)' },\n { id: 'regular', label: '', startMinute: 570, endMinute: 960, bgColor: 'transparent' },\n { id: 'postmarket', label: 'POST', startMinute: 960, endMinute: 1200, bgColor: 'rgba(255,235,59,0.05)' },\n ],\n holidays: [\n { date: '2025-01-01', name: \"New Year's Day\" },\n { date: '2025-01-20', name: 'MLK Day' },\n { date: '2025-02-17', name: \"Presidents' Day\" },\n { date: '2025-04-18', name: 'Good Friday' },\n { date: '2025-05-26', name: 'Memorial Day' },\n { date: '2025-06-19', name: 'Juneteenth' },\n { date: '2025-07-04', name: 'Independence Day' },\n { date: '2025-09-01', name: 'Labor Day' },\n { date: '2025-11-27', name: 'Thanksgiving' },\n { date: '2025-12-25', name: 'Christmas' },\n ],\n};\n\nexport const UK_MARKET: MarketDefinition = {\n id: 'uk', name: 'London Stock Exchange', timezone: 'Europe/London', currency: 'GBP',\n sessions: [{ id: 'regular', label: '', startMinute: 480, endMinute: 990, bgColor: 'transparent' }],\n holidays: [\n { date: '2025-01-01', name: \"New Year's Day\" },\n { date: '2025-04-18', name: 'Good Friday' },\n { date: '2025-04-21', name: 'Easter Monday' },\n { date: '2025-05-05', name: 'Early May Bank Holiday' },\n { date: '2025-05-26', name: 'Spring Bank Holiday' },\n { date: '2025-08-25', name: 'Summer Bank Holiday' },\n { date: '2025-12-25', name: 'Christmas' },\n { date: '2025-12-26', name: 'Boxing Day' },\n ],\n};\n\nexport const JP_MARKET: MarketDefinition = {\n id: 'jp', name: 'Japan Exchange', timezone: 'Asia/Tokyo', currency: 'JPY',\n sessions: [\n { id: 'morning', label: 'AM', startMinute: 540, endMinute: 690, bgColor: 'transparent' },\n { id: 'afternoon', label: 'PM', startMinute: 750, endMinute: 900, bgColor: 'transparent' },\n ],\n holidays: [\n { date: '2025-01-01', name: \"New Year's Day\" },\n { date: '2025-01-02', name: 'Bank Holiday' },\n { date: '2025-01-03', name: 'Bank Holiday' },\n { date: '2025-01-13', name: 'Coming of Age Day' },\n { date: '2025-02-11', name: 'National Foundation Day' },\n { date: '2025-02-23', name: \"Emperor's Birthday\" },\n { date: '2025-03-20', name: 'Vernal Equinox' },\n { date: '2025-04-29', name: 'Showa Day' },\n { date: '2025-05-03', name: 'Constitution Memorial Day' },\n { date: '2025-05-05', name: \"Children's Day\" },\n { date: '2025-05-06', name: 'Substitute Holiday' },\n ],\n};\n\nexport const DE_MARKET: MarketDefinition = {\n id: 'de', name: 'Deutsche Boerse', timezone: 'Europe/Berlin', currency: 'EUR',\n sessions: [{ id: 'regular', label: '', startMinute: 540, endMinute: 1050, bgColor: 'transparent' }],\n holidays: [\n { date: '2025-01-01', name: \"New Year's Day\" },\n { date: '2025-04-18', name: 'Good Friday' },\n { date: '2025-04-21', name: 'Easter Monday' },\n { date: '2025-05-01', name: 'Labour Day' },\n { date: '2025-12-24', name: 'Christmas Eve' },\n { date: '2025-12-25', name: 'Christmas' },\n { date: '2025-12-26', name: \"St Stephen's Day\" },\n { date: '2025-12-31', name: \"New Year's Eve\" },\n ],\n};\n\nexport const AU_MARKET: MarketDefinition = {\n id: 'au', name: 'Australian Securities Exchange', timezone: 'Australia/Sydney', currency: 'AUD',\n sessions: [\n { id: 'preopen', label: 'PRE', startMinute: 420, endMinute: 600, bgColor: 'rgba(255,235,59,0.05)' },\n { id: 'regular', label: '', startMinute: 600, endMinute: 960, bgColor: 'transparent' },\n ],\n holidays: [\n { date: '2025-01-01', name: \"New Year's Day\" },\n { date: '2025-01-27', name: 'Australia Day' },\n { date: '2025-04-18', name: 'Good Friday' },\n { date: '2025-04-21', name: 'Easter Monday' },\n { date: '2025-04-25', name: 'ANZAC Day' },\n { date: '2025-06-09', name: \"Queen's Birthday\" },\n { date: '2025-12-25', name: 'Christmas' },\n { date: '2025-12-26', name: 'Boxing Day' },\n ],\n};\n\nexport const CRYPTO_MARKET: MarketDefinition = {\n id: 'crypto', name: 'Cryptocurrency', timezone: 'UTC', currency: 'USD',\n sessions: [{ id: 'regular', label: '', startMinute: 0, endMinute: 1440, bgColor: 'transparent' }],\n holidays: [],\n};\n\nfor (const m of [US_MARKET, UK_MARKET, JP_MARKET, DE_MARKET, AU_MARKET, CRYPTO_MARKET]) {\n _markets.set(m.id, m);\n}\n\nexport function getMarket(id: string): MarketDefinition | undefined {\n return _markets.get(id);\n}\n\nexport function registerMarket(market: MarketDefinition): void {\n _markets.set(market.id, market);\n}\n"],"mappings":"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"}
|
package/dist/index123.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{getTimezoneOffsetMinutes as e,timestampToDateParts as t}from"./index103.js";function n(e,t){if(t.sessions.some(e=>0===e.startMinute&&1440===e.endMinute)&&0===t.holidays.length)return!0;const[n,i,s]=e.split("-").map(Number),o=new Date(n,i-1,s).getDay();return 0!==o&&6!==o&&!t.holidays.some(t=>t.date===e&&void 0===t.earlyClose)}function i(e,t){const n=t.holidays.find(t=>t.date===e);return n&&void 0!==n.earlyClose?n.earlyClose:null}function s(i,s){if(s.sessions.some(e=>0===e.startMinute&&1440===e.endMinute)&&0===s.holidays.length)return i;const o=(s.sessions.find(e=>"regular"===e.id||"morning"===e.id)??s.sessions[0]).startMinute,r=t(i,s.timezone),a=60*r.hour+r.minute;if(a<o&&n(`${r.year}-${String(r.month).padStart(2,"0")}-${String(r.day).padStart(2,"0")}`,s)){const t=e(i,s.timezone);return Date.UTC(r.year,r.month-1,r.day)/1e3+60*(o-t)}let l=new Date(r.year,r.month-1,r.day);a>=o&&l.setDate(l.getDate()+1);for(let t=0;t<10;t++){if(n(l.toISOString().slice(0,10),s)){const t=e(l.getTime()/1e3,s.timezone);return Date.UTC(l.getFullYear(),l.getMonth(),l.getDate())/1e3+60*(o-t)}l.setDate(l.getDate()+1)}return i}export{s as getNextOpen,i as isEarlyClose,n as isMarketDate};
|
|
2
|
+
//# sourceMappingURL=index123.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index123.js","names":[],"sources":["../src/market/market-calendar.ts"],"sourcesContent":["import type { MarketDefinition } from './market-definition';\nimport { timestampToDateParts, getTimezoneOffsetMinutes } from '../timezone/timezone';\n\nexport function isMarketDate(dateStr: string, market: MarketDefinition): boolean {\n if (market.sessions.some(s => s.startMinute === 0 && s.endMinute === 1440) && market.holidays.length === 0) {\n return true;\n }\n const [y, m, d] = dateStr.split('-').map(Number);\n const dow = new Date(y, m - 1, d).getDay();\n if (dow === 0 || dow === 6) return false;\n if (market.holidays.some(h => h.date === dateStr && h.earlyClose === undefined)) return false;\n return true;\n}\n\nexport function isEarlyClose(dateStr: string, market: MarketDefinition): number | null {\n const holiday = market.holidays.find(h => h.date === dateStr);\n if (holiday && holiday.earlyClose !== undefined) return holiday.earlyClose;\n return null;\n}\n\nexport function getNextOpen(timestampSec: number, market: MarketDefinition): number {\n if (market.sessions.some(s => s.startMinute === 0 && s.endMinute === 1440) && market.holidays.length === 0) {\n return timestampSec;\n }\n\n const regularSession = market.sessions.find(s => s.id === 'regular' || s.id === 'morning') ?? market.sessions[0];\n const openMinute = regularSession.startMinute;\n const parts = timestampToDateParts(timestampSec, market.timezone);\n const currentMinute = parts.hour * 60 + parts.minute;\n\n if (currentMinute < openMinute) {\n const dateStr = `${parts.year}-${String(parts.month).padStart(2, '0')}-${String(parts.day).padStart(2, '0')}`;\n if (isMarketDate(dateStr, market)) {\n const offset = getTimezoneOffsetMinutes(timestampSec, market.timezone);\n const midnightUtc = Date.UTC(parts.year, parts.month - 1, parts.day) / 1000;\n return midnightUtc + (openMinute - offset) * 60;\n }\n }\n\n let searchDate = new Date(parts.year, parts.month - 1, parts.day);\n if (currentMinute >= openMinute) {\n searchDate.setDate(searchDate.getDate() + 1);\n }\n\n for (let i = 0; i < 10; i++) {\n const dateStr = searchDate.toISOString().slice(0, 10);\n if (isMarketDate(dateStr, market)) {\n const offset = getTimezoneOffsetMinutes(searchDate.getTime() / 1000, market.timezone);\n const midnightUtc = Date.UTC(searchDate.getFullYear(), searchDate.getMonth(), searchDate.getDate()) / 1000;\n return midnightUtc + (openMinute - offset) * 60;\n }\n searchDate.setDate(searchDate.getDate() + 1);\n }\n\n return timestampSec;\n}\n"],"mappings":"mFAGA,SAAgB,EAAa,EAAiB,GAC5C,GAAI,EAAO,SAAS,KAAK,GAAuB,IAAlB,EAAE,aAAqC,OAAhB,EAAE,YAAkD,IAA3B,EAAO,SAAS,OAC5F,OAAO,EAET,MAAO,EAAG,EAAG,GAAK,EAAQ,MAAM,KAAK,IAAI,QACnC,EAAM,IAAI,KAAK,EAAG,EAAI,EAAG,GAAG,SAClC,OAAY,IAAR,GAAqB,IAAR,IACb,EAAO,SAAS,KAAK,GAAK,EAAE,OAAS,QAA4B,IAAjB,EAAE,YAIxD,SAAgB,EAAa,EAAiB,GAC5C,MAAM,EAAU,EAAO,SAAS,KAAK,GAAK,EAAE,OAAS,GACrD,OAAI,QAAkC,IAAvB,EAAQ,WAAiC,EAAQ,WACzD,KAGT,SAAgB,EAAY,EAAsB,GAChD,GAAI,EAAO,SAAS,KAAK,GAAuB,IAAlB,EAAE,aAAqC,OAAhB,EAAE,YAAkD,IAA3B,EAAO,SAAS,OAC5F,OAAO,EAIT,MAAM,GADiB,EAAO,SAAS,KAAK,GAAc,YAAT,EAAE,IAA6B,YAAT,EAAE,KAAqB,EAAO,SAAS,IAC5E,YAC5B,EAAQ,EAAqB,EAAc,EAAO,UAClD,EAA6B,GAAb,EAAM,KAAY,EAAM,OAE9C,GAAI,EAAgB,GAEd,EADY,GAAG,EAAM,QAAQ,OAAO,EAAM,OAAO,SAAS,EAAG,QAAQ,OAAO,EAAM,KAAK,SAAS,EAAG,OAC7E,GAAS,CACjC,MAAM,EAAS,EAAyB,EAAc,EAAO,UAE7D,OADoB,KAAK,IAAI,EAAM,KAAM,EAAM,MAAQ,EAAG,EAAM,KAAO,IAC1B,IAAvB,EAAa,GAIvC,IAAI,EAAa,IAAI,KAAK,EAAM,KAAM,EAAM,MAAQ,EAAG,EAAM,KACzD,GAAiB,GACnB,EAAW,QAAQ,EAAW,UAAY,GAG5C,IAAK,IAAI,EAAI,EAAG,EAAI,GAAI,IAAK,CAE3B,GAAI,EADY,EAAW,cAAc,MAAM,EAAG,IACxB,GAAS,CACjC,MAAM,EAAS,EAAyB,EAAW,UAAY,IAAM,EAAO,UAE5E,OADoB,KAAK,IAAI,EAAW,cAAe,EAAW,WAAY,EAAW,WAAa,IACzD,IAAvB,EAAa,GAErC,EAAW,QAAQ,EAAW,UAAY,GAG5C,OAAO"}
|
package/dist/index124.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{getMarket as o}from"./index122.js";var t=/* @__PURE__ */new Map;for(const A of["NYSE","NAS","NASDAQ","AMEX","ARCA","BATS","IEX","OTC","PNK","NYSEARCA"])t.set(A,"us");for(const A of["LSE","LON","AIM"])t.set(A,"uk");for(const A of["JPX","TSE","TYO"])t.set(A,"jp");for(const A of["FRA","ETR","BER","STU","XETRA"])t.set(A,"de");for(const A of["ASX","AX"])t.set(A,"au");for(const A of["CRYPTO","BINANCE","COINBASE","KRAKEN"])t.set(A,"crypto");function s(s){const e=t.get(s.toUpperCase());if(e)return o(e)}function e(o,s){t.set(o.toUpperCase(),s)}export{s as getMarketForExchange,e as registerExchange};
|
|
2
|
+
//# sourceMappingURL=index124.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index124.js","names":[],"sources":["../src/market/exchange-map.ts"],"sourcesContent":["import type { MarketDefinition } from './market-definition';\nimport { getMarket } from './market-definition';\n\nconst _exchangeToMarket = new Map<string, string>();\n\nfor (const code of ['NYSE', 'NAS', 'NASDAQ', 'AMEX', 'ARCA', 'BATS', 'IEX', 'OTC', 'PNK', 'NYSEARCA']) {\n _exchangeToMarket.set(code, 'us');\n}\nfor (const code of ['LSE', 'LON', 'AIM']) {\n _exchangeToMarket.set(code, 'uk');\n}\nfor (const code of ['JPX', 'TSE', 'TYO']) {\n _exchangeToMarket.set(code, 'jp');\n}\nfor (const code of ['FRA', 'ETR', 'BER', 'STU', 'XETRA']) {\n _exchangeToMarket.set(code, 'de');\n}\nfor (const code of ['ASX', 'AX']) {\n _exchangeToMarket.set(code, 'au');\n}\nfor (const code of ['CRYPTO', 'BINANCE', 'COINBASE', 'KRAKEN']) {\n _exchangeToMarket.set(code, 'crypto');\n}\n\nexport function getMarketForExchange(exchangeCode: string): MarketDefinition | undefined {\n const marketId = _exchangeToMarket.get(exchangeCode.toUpperCase());\n if (!marketId) return undefined;\n return getMarket(marketId);\n}\n\nexport function registerExchange(exchangeCode: string, marketId: string): void {\n _exchangeToMarket.set(exchangeCode.toUpperCase(), marketId);\n}\n"],"mappings":"0CAGA,IAAM,iBAAoB,IAAI,IAE9B,IAAK,MAAM,IAAQ,CAAC,OAAQ,MAAO,SAAU,OAAQ,OAAQ,OAAQ,MAAO,MAAO,MAAO,YACxF,EAAkB,IAAI,EAAM,MAE9B,IAAK,MAAM,IAAQ,CAAC,MAAO,MAAO,OAChC,EAAkB,IAAI,EAAM,MAE9B,IAAK,MAAM,IAAQ,CAAC,MAAO,MAAO,OAChC,EAAkB,IAAI,EAAM,MAE9B,IAAK,MAAM,IAAQ,CAAC,MAAO,MAAO,MAAO,MAAO,SAC9C,EAAkB,IAAI,EAAM,MAE9B,IAAK,MAAM,IAAQ,CAAC,MAAO,MACzB,EAAkB,IAAI,EAAM,MAE9B,IAAK,MAAM,IAAQ,CAAC,SAAU,UAAW,WAAY,UACnD,EAAkB,IAAI,EAAM,UAG9B,SAAgB,EAAqB,GACnC,MAAM,EAAW,EAAkB,IAAI,EAAa,eACpD,GAAK,EACL,OAAO,EAAU,GAGnB,SAAgB,EAAiB,EAAsB,GACrD,EAAkB,IAAI,EAAa,cAAe"}
|
package/dist/index13.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t={text:"",backgroundColor:"rgba(33, 150, 243, 0.85)",textColor:"#ffffff",borderColor:"transparent",borderWidth:1,borderRadius:4,fontSize:11,fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',boxAnchor:"center",padding:6,showConnector:!0,connectorColor:"rgba(33, 150, 243, 0.6)",yOffset:-30},e=class{constructor(e,o,r,i,n){this.id=e,this._time=o,this._price=r,this._options={...t,...i},this._requestRepaint=n??null}get time(){return this._time}get price(){return this._price}get options(){return{...this._options}}applyOptions(t){Object.assign(this._options,t),this._requestRepaint?.()}setPosition(t,e){this._time=t,this._price=e,this._requestRepaint?.()}createPaneView(t,e){const o=this;return{renderer:()=>({draw(r){const{context:i,pixelRatio:n}=r,s=o._options,a=t(o._time)*n,l=e(o._price)*n,c=l+s.yOffset*n;i.save();const h=s.fontSize*n;i.font=`${h}px ${s.fontFamily}`;const p=i.measureText(s.text).width,d=s.padding*n,f=p+2*d,b=h+2*d,u=Math.min(s.borderRadius*n,f/2,b/2);let _;switch(s.boxAnchor){case"left":_=a;break;case"right":_=a-f;break;default:_=a-f/2}const m=c-b/2;s.showConnector&&"transparent"!==s.connectorColor&&(i.strokeStyle=s.connectorColor,i.lineWidth=n,i.setLineDash([3*n,3*n]),i.beginPath(),i.moveTo(a,l),i.lineTo(a,m+b/2),i.stroke(),i.setLineDash([])),i.beginPath(),i.moveTo(_+u,m),i.lineTo(_+f-u,m),i.arcTo(_+f,m,_+f,m+u,u),i.lineTo(_+f,m+b-u),i.arcTo(_+f,m+b,_+f-u,m+b,u),i.lineTo(_+u,m+b),i.arcTo(_,m+b,_,m+b-u,u),i.lineTo(_,m+u),i.arcTo(_,m,_+u,m,u),i.closePath(),"transparent"!==s.backgroundColor&&(i.fillStyle=s.backgroundColor,i.fill()),"transparent"!==s.borderColor&&s.borderWidth>0&&(i.strokeStyle=s.borderColor,i.lineWidth=s.borderWidth*n,i.stroke()),i.fillStyle=s.textColor,i.textBaseline="middle",i.textAlign="left",i.fillText(s.text,_+d,m+b/2),i.restore()}})}}serialize(){return{id:this.id,time:this._time,price:this._price,options:{...this._options}}}};function o(t,o){return t.map((t,r)=>new e(`label_${r}`,t.time,t.price,{text:t.text,...t.options},o))}export{e as TextLabel,o as createTextLabels};
|
|
2
|
+
//# sourceMappingURL=index13.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index13.js","names":[],"sources":["../src/core/text-label.ts"],"sourcesContent":["import type { IPaneView, IPaneRenderer, IRenderTarget } from './types';\n\nexport interface TextLabelOptions {\n /** Text content. */\n text: string;\n /** Background color (set to 'transparent' for no background). */\n backgroundColor: string;\n /** Text color. */\n textColor: string;\n /** Border color (set to 'transparent' for no border). */\n borderColor: string;\n /** Border width in pixels. */\n borderWidth: number;\n /** Border radius in pixels. */\n borderRadius: number;\n /** Font size in pixels. */\n fontSize: number;\n /** Font family. */\n fontFamily: string;\n /** Box anchor relative to the time/price coordinate: 'left' = box starts at anchor, 'center' = centered, 'right' = box ends at anchor. */\n boxAnchor: 'left' | 'center' | 'right';\n /** Padding inside the box (pixels). */\n padding: number;\n /** Whether to draw an arrow/connector line from the box to the anchor point. */\n showConnector: boolean;\n /** Connector line color. */\n connectorColor: string;\n /** Y offset of the label box from the anchor point (pixels). */\n yOffset: number;\n}\n\nexport const DEFAULT_TEXT_LABEL_OPTIONS: TextLabelOptions = {\n text: '',\n backgroundColor: 'rgba(33, 150, 243, 0.85)',\n textColor: '#ffffff',\n borderColor: 'transparent',\n borderWidth: 1,\n borderRadius: 4,\n fontSize: 11,\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n boxAnchor: 'center',\n padding: 6,\n showConnector: true,\n connectorColor: 'rgba(33, 150, 243, 0.6)',\n yOffset: -30,\n};\n\n/**\n * TextLabel — a lightweight text annotation anchored to a time/price coordinate.\n *\n * Unlike the drawing tool's text annotation, TextLabels are programmatic\n * (e.g., for annotating earnings dates, dividends) and don't require\n * user interaction to place. They survive zoom/pan and are included in\n * screenshot exports.\n */\nexport class TextLabel {\n readonly id: string;\n /** Bar index (not timestamp). Use DataLayer.findIndex(timestamp) to convert. */\n private _time: number;\n private _price: number;\n private _options: TextLabelOptions;\n private _requestRepaint: (() => void) | null;\n\n constructor(\n id: string,\n time: number,\n price: number,\n options: Partial<TextLabelOptions>,\n requestRepaint?: () => void,\n ) {\n this.id = id;\n this._time = time;\n this._price = price;\n this._options = { ...DEFAULT_TEXT_LABEL_OPTIONS, ...options };\n this._requestRepaint = requestRepaint ?? null;\n }\n\n get time(): number { return this._time; }\n get price(): number { return this._price; }\n get options(): Readonly<TextLabelOptions> { return { ...this._options }; }\n\n applyOptions(opts: Partial<TextLabelOptions>): void {\n Object.assign(this._options, opts);\n this._requestRepaint?.();\n }\n\n setPosition(time: number, price: number): void {\n this._time = time;\n this._price = price;\n this._requestRepaint?.();\n }\n\n /**\n * Create an IPaneView for rendering this label on the chart canvas.\n */\n createPaneView(\n indexToX: (i: number) => number,\n priceToY: (p: number) => number,\n ): IPaneView {\n const self = this;\n return {\n renderer(): IPaneRenderer | null {\n return {\n draw(target: IRenderTarget): void {\n const { context: ctx, pixelRatio: pr } = target;\n const opts = self._options;\n\n const anchorX = indexToX(self._time) * pr;\n const anchorY = priceToY(self._price) * pr;\n const yOff = opts.yOffset * pr;\n const labelY = anchorY + yOff;\n\n ctx.save();\n\n // Measure text\n const fontSize = opts.fontSize * pr;\n ctx.font = `${fontSize}px ${opts.fontFamily}`;\n const textW = ctx.measureText(opts.text).width;\n const pad = opts.padding * pr;\n const boxW = textW + pad * 2;\n const boxH = fontSize + pad * 2;\n // Clamp borderRadius to prevent self-intersecting paths\n const borderR = Math.min(opts.borderRadius * pr, boxW / 2, boxH / 2);\n\n // Position box relative to anchor\n let boxX: number;\n switch (opts.boxAnchor) {\n case 'left': boxX = anchorX; break;\n case 'right': boxX = anchorX - boxW; break;\n default: boxX = anchorX - boxW / 2; break;\n }\n const boxY = labelY - boxH / 2;\n\n // Connector line\n if (opts.showConnector && opts.connectorColor !== 'transparent') {\n ctx.strokeStyle = opts.connectorColor;\n ctx.lineWidth = pr;\n ctx.setLineDash([3 * pr, 3 * pr]);\n ctx.beginPath();\n ctx.moveTo(anchorX, anchorY);\n ctx.lineTo(anchorX, boxY + boxH / 2);\n ctx.stroke();\n ctx.setLineDash([]);\n }\n\n // Build rounded rect path (shared between fill and stroke)\n ctx.beginPath();\n ctx.moveTo(boxX + borderR, boxY);\n ctx.lineTo(boxX + boxW - borderR, boxY);\n ctx.arcTo(boxX + boxW, boxY, boxX + boxW, boxY + borderR, borderR);\n ctx.lineTo(boxX + boxW, boxY + boxH - borderR);\n ctx.arcTo(boxX + boxW, boxY + boxH, boxX + boxW - borderR, boxY + boxH, borderR);\n ctx.lineTo(boxX + borderR, boxY + boxH);\n ctx.arcTo(boxX, boxY + boxH, boxX, boxY + boxH - borderR, borderR);\n ctx.lineTo(boxX, boxY + borderR);\n ctx.arcTo(boxX, boxY, boxX + borderR, boxY, borderR);\n ctx.closePath();\n\n // Fill background\n if (opts.backgroundColor !== 'transparent') {\n ctx.fillStyle = opts.backgroundColor;\n ctx.fill();\n }\n\n // Stroke border (uses the same path)\n if (opts.borderColor !== 'transparent' && opts.borderWidth > 0) {\n ctx.strokeStyle = opts.borderColor;\n ctx.lineWidth = opts.borderWidth * pr;\n ctx.stroke();\n }\n\n // Text\n ctx.fillStyle = opts.textColor;\n ctx.textBaseline = 'middle';\n ctx.textAlign = 'left';\n ctx.fillText(opts.text, boxX + pad, boxY + boxH / 2);\n\n ctx.restore();\n },\n };\n },\n };\n }\n\n /** Serialize for persistence. */\n serialize(): { id: string; time: number; price: number; options: TextLabelOptions } {\n return {\n id: this.id,\n time: this._time,\n price: this._price,\n options: { ...this._options },\n };\n }\n}\n\n/**\n * Batch-create text labels (e.g., for annotating earnings dates).\n */\nexport function createTextLabels(\n entries: Array<{ time: number; price: number; text: string; options?: Partial<TextLabelOptions> }>,\n requestRepaint?: () => void,\n): TextLabel[] {\n return entries.map((e, i) =>\n new TextLabel(`label_${i}`, e.time, e.price, { text: e.text, ...e.options }, requestRepaint),\n );\n}\n"],"mappings":"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"}
|
package/dist/index14.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=class{constructor(t=50){this._undoStack=[],this._redoStack=[],this._changeCallbacks=[],this._maxDepth=t}execute(t){t.execute(),this._undoStack.push(t),this._redoStack.length=0,this._undoStack.length>this._maxDepth&&this._undoStack.shift(),this._notifyChange()}undo(){const t=this._undoStack.pop();return!!t&&(t.undo(),this._redoStack.push(t),this._notifyChange(),!0)}redo(){const t=this._redoStack.pop();return!!t&&(t.execute(),this._undoStack.push(t),this._notifyChange(),!0)}canUndo(){return this._undoStack.length>0}canRedo(){return this._redoStack.length>0}clear(){this._undoStack.length=0,this._redoStack.length=0,this._notifyChange()}get undoSize(){return this._undoStack.length}get redoSize(){return this._redoStack.length}get maxDepth(){return this._maxDepth}onChange(t){this._changeCallbacks.push(t)}offChange(t){const e=this._changeCallbacks.indexOf(t);e>=0&&this._changeCallbacks.splice(e,1)}_notifyChange(){for(const t of this._changeCallbacks)t()}};export{t as UndoRedoManager};
|
|
2
|
+
//# sourceMappingURL=index14.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index14.js","names":[],"sources":["../src/core/undo-redo.ts"],"sourcesContent":["/**\n * Command pattern undo/redo stack.\n *\n * Each command has an `execute()` and `undo()` method. Commands are pushed\n * onto the undo stack when executed, and moved between undo/redo stacks\n * as the user navigates history.\n */\n\nexport interface Command {\n /** Human-readable description for UI. */\n readonly label: string;\n /** Execute (or re-execute) the command. */\n execute(): void;\n /** Reverse the command. */\n undo(): void;\n}\n\nexport type UndoRedoChangeCallback = () => void;\n\nexport class UndoRedoManager {\n private _undoStack: Command[] = [];\n private _redoStack: Command[] = [];\n private _maxDepth: number;\n private _changeCallbacks: UndoRedoChangeCallback[] = [];\n\n constructor(maxDepth = 50) {\n this._maxDepth = maxDepth;\n }\n\n /** Push and execute a command. Clears the redo stack. */\n execute(command: Command): void {\n command.execute();\n this._undoStack.push(command);\n this._redoStack.length = 0;\n\n // Enforce max depth\n if (this._undoStack.length > this._maxDepth) {\n this._undoStack.shift();\n }\n this._notifyChange();\n }\n\n /** Undo the last command. */\n undo(): boolean {\n const cmd = this._undoStack.pop();\n if (!cmd) return false;\n cmd.undo();\n this._redoStack.push(cmd);\n this._notifyChange();\n return true;\n }\n\n /** Redo the last undone command. */\n redo(): boolean {\n const cmd = this._redoStack.pop();\n if (!cmd) return false;\n cmd.execute();\n this._undoStack.push(cmd);\n this._notifyChange();\n return true;\n }\n\n /** Whether undo is available. */\n canUndo(): boolean {\n return this._undoStack.length > 0;\n }\n\n /** Whether redo is available. */\n canRedo(): boolean {\n return this._redoStack.length > 0;\n }\n\n /** Clear both stacks (e.g., on symbol change). */\n clear(): void {\n this._undoStack.length = 0;\n this._redoStack.length = 0;\n this._notifyChange();\n }\n\n /** Number of undo-able commands. */\n get undoSize(): number {\n return this._undoStack.length;\n }\n\n /** Number of redo-able commands. */\n get redoSize(): number {\n return this._redoStack.length;\n }\n\n /** Get the configured max depth. */\n get maxDepth(): number {\n return this._maxDepth;\n }\n\n /** Subscribe to changes in undo/redo availability. */\n onChange(callback: UndoRedoChangeCallback): void {\n this._changeCallbacks.push(callback);\n }\n\n /** Unsubscribe from changes. */\n offChange(callback: UndoRedoChangeCallback): void {\n const idx = this._changeCallbacks.indexOf(callback);\n if (idx >= 0) this._changeCallbacks.splice(idx, 1);\n }\n\n private _notifyChange(): void {\n for (const cb of this._changeCallbacks) cb();\n }\n}\n"],"mappings":"AAmBA,IAAa,EAAb,MAME,WAAA,CAAY,EAAW,oBALS,mBACA,yBAEqB,GAGnD,KAAK,UAAY,EAInB,OAAA,CAAQ,GACN,EAAQ,UACR,KAAK,WAAW,KAAK,GACrB,KAAK,WAAW,OAAS,EAGrB,KAAK,WAAW,OAAS,KAAK,WAChC,KAAK,WAAW,QAElB,KAAK,gBAIP,IAAA,GACE,MAAM,EAAM,KAAK,WAAW,MAC5B,QAAK,IACL,EAAI,OACJ,KAAK,WAAW,KAAK,GACrB,KAAK,iBACE,GAIT,IAAA,GACE,MAAM,EAAM,KAAK,WAAW,MAC5B,QAAK,IACL,EAAI,UACJ,KAAK,WAAW,KAAK,GACrB,KAAK,iBACE,GAIT,OAAA,GACE,OAAO,KAAK,WAAW,OAAS,EAIlC,OAAA,GACE,OAAO,KAAK,WAAW,OAAS,EAIlC,KAAA,GACE,KAAK,WAAW,OAAS,EACzB,KAAK,WAAW,OAAS,EACzB,KAAK,gBAIP,YAAI,GACF,OAAO,KAAK,WAAW,OAIzB,YAAI,GACF,OAAO,KAAK,WAAW,OAIzB,YAAI,GACF,OAAO,KAAK,UAId,QAAA,CAAS,GACP,KAAK,iBAAiB,KAAK,GAI7B,SAAA,CAAU,GACR,MAAM,EAAM,KAAK,iBAAiB,QAAQ,GACtC,GAAO,GAAG,KAAK,iBAAiB,OAAO,EAAK,GAGlD,aAAA,GACE,IAAK,MAAM,KAAM,KAAK,iBAAkB"}
|
package/dist/index15.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var t=class{constructor(t,e,i,s){this._active=!1,this._selecting=!1,this._startX=0,this._startY=0,this._endX=0,this._endY=0,this._callbacks=[],this._timeScale=t,this._priceScale=e,this._getStore=i,this._requestRepaint=s}get active(){return this._active}set active(t){this._active=t,t||this.clear()}get selecting(){return this._selecting}get startX(){return this._startX}get startY(){return this._startY}get endX(){return this._endX}get endY(){return this._endY}onRangeSelected(t){this._callbacks.push(t)}offRangeSelected(t){const e=this._callbacks.indexOf(t);e>=0&&this._callbacks.splice(e,1)}clear(){this._selecting=!1,this._requestRepaint();for(const t of this._callbacks)t(null)}onPointerDown(t,e){if(this._active)return this._selecting=!0,this._startX=t,this._startY=e,this._endX=t,this._endY=e,this._requestRepaint(),!0}onPointerMove(t,e){if(this._selecting)return this._endX=t,this._endY=e,this._requestRepaint(),!0}onPointerUp(){if(!this._selecting)return;this._selecting=!1;const t=this._computeStats();if(t)for(const e of this._callbacks)e(t)}onKeyDown(t){if("Escape"===t&&this._selecting)return this.clear(),!0}_computeStats(){const t=this._getStore();if(!t||0===t.length)return null;let e=Math.round(this._timeScale.xToIndex(Math.min(this._startX,this._endX))),i=Math.round(this._timeScale.xToIndex(Math.max(this._startX,this._endX)));if(e=Math.max(0,Math.min(t.length-1,e)),i=Math.max(0,Math.min(t.length-1,i)),e>i&&([e,i]=[i,e]),e===i)return null;let s=-1/0,n=1/0,h=0;for(let _=e;_<=i;_++)t.high[_]>s&&(s=t.high[_]),t.low[_]<n&&(n=t.low[_]),h+=t.volume[_];const r=t.close[e],o=t.close[i],c=o-r,a=0!==r?c/r*100:0;return{fromTime:t.time[e],toTime:t.time[i],fromPrice:r,toPrice:o,high:s,low:n,priceChange:c,percentChange:a,barCount:i-e+1,totalVolume:h}}},e=class{constructor(t,e,i,s){this._active=!1,this._firstPoint=null,this._secondPoint=null,this._hovering=!1,this._hoverX=0,this._hoverY=0,this._callbacks=[],this._timeScale=t,this._priceScale=e,this._getStore=i,this._requestRepaint=s}get active(){return this._active}set active(t){this._active=t,t||this.clear()}get firstPoint(){return this._firstPoint}get secondPoint(){return this._secondPoint}get hovering(){return this._hovering}get hoverX(){return this._hoverX}get hoverY(){return this._hoverY}onMeasure(t){this._callbacks.push(t)}offMeasure(t){const e=this._callbacks.indexOf(t);e>=0&&this._callbacks.splice(e,1)}clear(){this._firstPoint=null,this._secondPoint=null,this._hovering=!1,this._requestRepaint();for(const t of this._callbacks)t(null)}onPointerDown(t,e){if(!this._active)return;if(!this._firstPoint)return this._firstPoint={x:t,y:e},this._secondPoint=null,this._requestRepaint(),!0;this._secondPoint={x:t,y:e},this._hovering=!1,this._requestRepaint();const i=this._computeResult();if(i)for(const s of this._callbacks)s(i);return!0}onPointerMove(t,e){if(this._active&&this._firstPoint&&!this._secondPoint)return this._hovering=!0,this._hoverX=t,this._hoverY=e,this._requestRepaint(),!0}onKeyDown(t){if("Escape"===t&&this._active)return this.clear(),!0}_computeResult(){if(!this._firstPoint||!this._secondPoint)return null;const t=this._getStore();if(!t||0===t.length)return null;const e=Math.max(0,Math.min(t.length-1,Math.round(this._timeScale.xToIndex(this._firstPoint.x)))),i=Math.max(0,Math.min(t.length-1,Math.round(this._timeScale.xToIndex(this._secondPoint.x)))),s=Math.min(e,i),n=Math.max(e,i),h=this._priceScale.yToPrice(this._firstPoint.y),r=this._priceScale.yToPrice(this._secondPoint.y),o=r-h,c=0!==h?o/h*100:0;return{fromTime:t.time[s],toTime:t.time[n],fromPrice:h,toPrice:r,priceChange:o,percentChange:c,barCount:n-s+1,timeElapsed:t.time[n]-t.time[s]}}};export{e as MeasureHandler,t as RangeSelectionHandler};
|
|
2
|
+
//# sourceMappingURL=index15.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index15.js","names":[],"sources":["../src/interactions/range-selection.ts"],"sourcesContent":["import type { EventHandler } from './event-router';\nimport type { TimeScale } from '../core/time-scale';\nimport type { PriceScale } from '../core/price-scale';\nimport type { ColumnStore } from '../core/types';\n\nexport interface RangeSelectionStats {\n fromTime: number;\n toTime: number;\n fromPrice: number;\n toPrice: number;\n high: number;\n low: number;\n priceChange: number;\n percentChange: number;\n barCount: number;\n totalVolume: number;\n}\n\nexport type RangeSelectionCallback = (stats: RangeSelectionStats | null) => void;\n\n/**\n * RangeSelectionHandler — allows users to drag-select a time range on the\n * chart and see summary statistics (high, low, change, volume).\n *\n * Activated by setting `active = true`. When active, pointer-down starts a\n * selection; pointer-move extends it; pointer-up finalizes and fires the\n * callback.\n */\nexport class RangeSelectionHandler implements EventHandler {\n private _active = false;\n private _selecting = false;\n private _startX = 0;\n private _startY = 0;\n private _endX = 0;\n private _endY = 0;\n private _timeScale: TimeScale;\n private _priceScale: PriceScale;\n private _getStore: () => ColumnStore | null;\n private _requestRepaint: () => void;\n private _callbacks: RangeSelectionCallback[] = [];\n\n constructor(\n timeScale: TimeScale,\n priceScale: PriceScale,\n getStore: () => ColumnStore | null,\n requestRepaint: () => void,\n ) {\n this._timeScale = timeScale;\n this._priceScale = priceScale;\n this._getStore = getStore;\n this._requestRepaint = requestRepaint;\n }\n\n get active(): boolean { return this._active; }\n set active(value: boolean) {\n this._active = value;\n if (!value) this.clear();\n }\n\n get selecting(): boolean { return this._selecting; }\n get startX(): number { return this._startX; }\n get startY(): number { return this._startY; }\n get endX(): number { return this._endX; }\n get endY(): number { return this._endY; }\n\n onRangeSelected(callback: RangeSelectionCallback): void {\n this._callbacks.push(callback);\n }\n\n offRangeSelected(callback: RangeSelectionCallback): void {\n const idx = this._callbacks.indexOf(callback);\n if (idx >= 0) this._callbacks.splice(idx, 1);\n }\n\n clear(): void {\n this._selecting = false;\n this._requestRepaint();\n for (const cb of this._callbacks) cb(null);\n }\n\n onPointerDown(x: number, y: number): boolean | void {\n if (!this._active) return;\n this._selecting = true;\n this._startX = x;\n this._startY = y;\n this._endX = x;\n this._endY = y;\n this._requestRepaint();\n return true;\n }\n\n onPointerMove(x: number, y: number): boolean | void {\n if (!this._selecting) return;\n this._endX = x;\n this._endY = y;\n this._requestRepaint();\n return true;\n }\n\n onPointerUp(): boolean | void {\n if (!this._selecting) return;\n this._selecting = false;\n\n const stats = this._computeStats();\n if (stats) {\n for (const cb of this._callbacks) cb(stats);\n }\n }\n\n onKeyDown(key: string): boolean | void {\n if (key === 'Escape' && this._selecting) {\n this.clear();\n return true;\n }\n }\n\n private _computeStats(): RangeSelectionStats | null {\n const store = this._getStore();\n if (!store || store.length === 0) return null;\n\n // Convert pixel X to bar index\n let fromIdx = Math.round(this._timeScale.xToIndex(Math.min(this._startX, this._endX)));\n let toIdx = Math.round(this._timeScale.xToIndex(Math.max(this._startX, this._endX)));\n fromIdx = Math.max(0, Math.min(store.length - 1, fromIdx));\n toIdx = Math.max(0, Math.min(store.length - 1, toIdx));\n\n if (fromIdx > toIdx) [fromIdx, toIdx] = [toIdx, fromIdx];\n if (fromIdx === toIdx) return null;\n\n let high = -Infinity;\n let low = Infinity;\n let totalVolume = 0;\n\n for (let i = fromIdx; i <= toIdx; i++) {\n if (store.high[i] > high) high = store.high[i];\n if (store.low[i] < low) low = store.low[i];\n totalVolume += store.volume[i];\n }\n\n const fromPrice = store.close[fromIdx];\n const toPrice = store.close[toIdx];\n const priceChange = toPrice - fromPrice;\n const percentChange = fromPrice !== 0 ? (priceChange / fromPrice) * 100 : 0;\n\n return {\n fromTime: store.time[fromIdx],\n toTime: store.time[toIdx],\n fromPrice,\n toPrice,\n high,\n low,\n priceChange,\n percentChange,\n barCount: toIdx - fromIdx + 1,\n totalVolume,\n };\n }\n}\n\n// ─── Measure Tool ───────────────────────────────────────────────────────────\n\nexport interface MeasureResult {\n fromTime: number;\n toTime: number;\n fromPrice: number;\n toPrice: number;\n priceChange: number;\n percentChange: number;\n barCount: number;\n timeElapsed: number;\n}\n\nexport type MeasureCallback = (result: MeasureResult | null) => void;\n\n/**\n * MeasureHandler — click two points to see price change, % change, bar count,\n * and time elapsed between them.\n */\nexport class MeasureHandler implements EventHandler {\n private _active = false;\n private _firstPoint: { x: number; y: number } | null = null;\n private _secondPoint: { x: number; y: number } | null = null;\n private _hovering = false;\n private _hoverX = 0;\n private _hoverY = 0;\n private _timeScale: TimeScale;\n private _priceScale: PriceScale;\n private _getStore: () => ColumnStore | null;\n private _requestRepaint: () => void;\n private _callbacks: MeasureCallback[] = [];\n\n constructor(\n timeScale: TimeScale,\n priceScale: PriceScale,\n getStore: () => ColumnStore | null,\n requestRepaint: () => void,\n ) {\n this._timeScale = timeScale;\n this._priceScale = priceScale;\n this._getStore = getStore;\n this._requestRepaint = requestRepaint;\n }\n\n get active(): boolean { return this._active; }\n set active(value: boolean) {\n this._active = value;\n if (!value) this.clear();\n }\n\n get firstPoint(): { x: number; y: number } | null { return this._firstPoint; }\n get secondPoint(): { x: number; y: number } | null { return this._secondPoint; }\n get hovering(): boolean { return this._hovering; }\n get hoverX(): number { return this._hoverX; }\n get hoverY(): number { return this._hoverY; }\n\n onMeasure(callback: MeasureCallback): void {\n this._callbacks.push(callback);\n }\n\n offMeasure(callback: MeasureCallback): void {\n const idx = this._callbacks.indexOf(callback);\n if (idx >= 0) this._callbacks.splice(idx, 1);\n }\n\n clear(): void {\n this._firstPoint = null;\n this._secondPoint = null;\n this._hovering = false;\n this._requestRepaint();\n for (const cb of this._callbacks) cb(null);\n }\n\n onPointerDown(x: number, y: number): boolean | void {\n if (!this._active) return;\n\n if (!this._firstPoint) {\n this._firstPoint = { x, y };\n this._secondPoint = null;\n this._requestRepaint();\n return true;\n }\n\n // Second click — compute result\n this._secondPoint = { x, y };\n this._hovering = false;\n this._requestRepaint();\n\n const result = this._computeResult();\n if (result) {\n for (const cb of this._callbacks) cb(result);\n }\n return true;\n }\n\n onPointerMove(x: number, y: number): boolean | void {\n if (!this._active || !this._firstPoint || this._secondPoint) return;\n this._hovering = true;\n this._hoverX = x;\n this._hoverY = y;\n this._requestRepaint();\n return true;\n }\n\n onKeyDown(key: string): boolean | void {\n if (key === 'Escape' && this._active) {\n this.clear();\n return true;\n }\n }\n\n private _computeResult(): MeasureResult | null {\n if (!this._firstPoint || !this._secondPoint) return null;\n const store = this._getStore();\n if (!store || store.length === 0) return null;\n\n const idx1 = Math.max(0, Math.min(store.length - 1,\n Math.round(this._timeScale.xToIndex(this._firstPoint.x))));\n const idx2 = Math.max(0, Math.min(store.length - 1,\n Math.round(this._timeScale.xToIndex(this._secondPoint.x))));\n\n const fromIdx = Math.min(idx1, idx2);\n const toIdx = Math.max(idx1, idx2);\n\n const fromPrice = this._priceScale.yToPrice(this._firstPoint.y);\n const toPrice = this._priceScale.yToPrice(this._secondPoint.y);\n const priceChange = toPrice - fromPrice;\n const percentChange = fromPrice !== 0 ? (priceChange / fromPrice) * 100 : 0;\n\n return {\n fromTime: store.time[fromIdx],\n toTime: store.time[toIdx],\n fromPrice,\n toPrice,\n priceChange,\n percentChange,\n barCount: toIdx - fromIdx + 1,\n timeElapsed: store.time[toIdx] - store.time[fromIdx],\n };\n }\n}\n"],"mappings":"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"}
|
package/dist/index16.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=class{constructor(){this._element=null,this._handlers=[],this._boundPointerDown=this._handlePointerDown.bind(this),this._boundPointerMove=this._handlePointerMove.bind(this),this._boundPointerUp=this._handlePointerUp.bind(this),this._boundPointerLeave=this._handlePointerLeave.bind(this),this._boundWheel=this._handleWheel.bind(this),this._boundKeyDown=this._handleKeyDown.bind(this),this._boundContextMenu=this._handleContextMenu.bind(this)}attach(e){this._element=e,e.style.touchAction="none",e.addEventListener("pointerdown",this._boundPointerDown),e.addEventListener("pointermove",this._boundPointerMove),e.addEventListener("pointerup",this._boundPointerUp),e.addEventListener("pointerleave",this._boundPointerLeave),e.addEventListener("wheel",this._boundWheel,{passive:!1}),e.hasAttribute("tabindex")||e.setAttribute("tabindex","0"),e.addEventListener("keydown",this._boundKeyDown),e.addEventListener("contextmenu",this._boundContextMenu)}detach(){const e=this._element;e&&(e.removeEventListener("pointerdown",this._boundPointerDown),e.removeEventListener("pointermove",this._boundPointerMove),e.removeEventListener("pointerup",this._boundPointerUp),e.removeEventListener("pointerleave",this._boundPointerLeave),e.removeEventListener("wheel",this._boundWheel),e.removeEventListener("keydown",this._boundKeyDown),e.removeEventListener("contextmenu",this._boundContextMenu),this._element=null)}addHandler(e){this._handlers.push(e)}removeHandler(e){const t=this._handlers.indexOf(e);-1!==t&&this._handlers.splice(t,1)}_getLocalCoords(e){const t=this._element;if(!t)return{x:e.clientX,y:e.clientY};const n=t.getBoundingClientRect();return{x:e.clientX-n.left,y:e.clientY-n.top}}_handlePointerDown(e){const{x:t,y:n}=this._getLocalCoords(e);for(const o of this._handlers)if(!0===o.onPointerDown?.(t,n,e.pointerId))break}_handlePointerMove(e){const{x:t,y:n}=this._getLocalCoords(e);for(const o of this._handlers)if(!0===o.onPointerMove?.(t,n,e.pointerId))break}_handlePointerUp(e){for(const t of this._handlers)if(!0===t.onPointerUp?.(e.pointerId))break}_handlePointerLeave(e){for(const t of this._handlers)if(!0===t.onPointerUp?.(e.pointerId))break}_handleWheel(e){e.preventDefault();const t=this._element;let n=e.clientX,o=e.clientY;if(t){const i=t.getBoundingClientRect();n=e.clientX-i.left,o=e.clientY-i.top}for(const i of this._handlers)i.onWheel?.(n,o,e.deltaY)}_handleKeyDown(e){for(const t of this._handlers)if(!0===t.onKeyDown?.(e.key,e.shiftKey))break}_handleContextMenu(e){e.preventDefault();const t=this._element;let n=e.clientX,o=e.clientY;if(t){const i=t.getBoundingClientRect();n=e.clientX-i.left,o=e.clientY-i.top}for(const i of this._handlers)if(!0===i.onContextMenu?.(n,o))break}};export{e as EventRouter};
|
|
2
|
+
//# sourceMappingURL=index16.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index16.js","names":[],"sources":["../src/interactions/event-router.ts"],"sourcesContent":["export interface EventHandler {\n /**\n * Called on pointer down. Return `true` to consume the event and prevent\n * subsequent handlers from receiving it; return `false` or `undefined` to\n * let the event propagate.\n */\n onPointerDown?(x: number, y: number, pointerId: number): boolean | void;\n onPointerMove?(x: number, y: number, pointerId: number): boolean | void;\n onPointerUp?(pointerId: number): boolean | void;\n onWheel?(x: number, y: number, deltaY: number): void;\n onKeyDown?(key: string, shiftKey: boolean): boolean | void;\n onContextMenu?(x: number, y: number): boolean | void;\n}\n\nexport class EventRouter {\n private _element: HTMLElement | null = null;\n private _handlers: EventHandler[] = [];\n\n // Bound listener references for proper cleanup\n private _boundPointerDown: (e: PointerEvent) => void;\n private _boundPointerMove: (e: PointerEvent) => void;\n private _boundPointerUp: (e: PointerEvent) => void;\n private _boundPointerLeave: (e: PointerEvent) => void;\n private _boundWheel: (e: WheelEvent) => void;\n private _boundKeyDown: (e: KeyboardEvent) => void;\n private _boundContextMenu: (e: MouseEvent) => void;\n\n constructor() {\n this._boundPointerDown = this._handlePointerDown.bind(this);\n this._boundPointerMove = this._handlePointerMove.bind(this);\n this._boundPointerUp = this._handlePointerUp.bind(this);\n this._boundPointerLeave = this._handlePointerLeave.bind(this);\n this._boundWheel = this._handleWheel.bind(this);\n this._boundKeyDown = this._handleKeyDown.bind(this);\n this._boundContextMenu = this._handleContextMenu.bind(this);\n }\n\n attach(element: HTMLElement): void {\n this._element = element;\n element.style.touchAction = 'none';\n\n element.addEventListener('pointerdown', this._boundPointerDown);\n element.addEventListener('pointermove', this._boundPointerMove);\n element.addEventListener('pointerup', this._boundPointerUp);\n element.addEventListener('pointerleave', this._boundPointerLeave);\n element.addEventListener('wheel', this._boundWheel, { passive: false });\n\n // Key events require tabIndex and focus\n if (!element.hasAttribute('tabindex')) {\n element.setAttribute('tabindex', '0');\n }\n element.addEventListener('keydown', this._boundKeyDown);\n element.addEventListener('contextmenu', this._boundContextMenu);\n }\n\n detach(): void {\n const el = this._element;\n if (!el) return;\n\n el.removeEventListener('pointerdown', this._boundPointerDown);\n el.removeEventListener('pointermove', this._boundPointerMove);\n el.removeEventListener('pointerup', this._boundPointerUp);\n el.removeEventListener('pointerleave', this._boundPointerLeave);\n el.removeEventListener('wheel', this._boundWheel);\n el.removeEventListener('keydown', this._boundKeyDown);\n el.removeEventListener('contextmenu', this._boundContextMenu);\n\n this._element = null;\n }\n\n addHandler(handler: EventHandler): void {\n this._handlers.push(handler);\n }\n\n removeHandler(handler: EventHandler): void {\n const idx = this._handlers.indexOf(handler);\n if (idx !== -1) {\n this._handlers.splice(idx, 1);\n }\n }\n\n // ── Private helpers ────────────────────────────────────────────────────────\n\n private _getLocalCoords(e: PointerEvent): { x: number; y: number } {\n const el = this._element;\n if (!el) return { x: e.clientX, y: e.clientY };\n const rect = el.getBoundingClientRect();\n return { x: e.clientX - rect.left, y: e.clientY - rect.top };\n }\n\n private _handlePointerDown(e: PointerEvent): void {\n const { x, y } = this._getLocalCoords(e);\n for (const h of this._handlers) {\n if (h.onPointerDown?.(x, y, e.pointerId) === true) break;\n }\n }\n\n private _handlePointerMove(e: PointerEvent): void {\n const { x, y } = this._getLocalCoords(e);\n for (const h of this._handlers) {\n if (h.onPointerMove?.(x, y, e.pointerId) === true) break;\n }\n }\n\n private _handlePointerUp(e: PointerEvent): void {\n for (const h of this._handlers) {\n if (h.onPointerUp?.(e.pointerId) === true) break;\n }\n }\n\n private _handlePointerLeave(e: PointerEvent): void {\n for (const h of this._handlers) {\n if (h.onPointerUp?.(e.pointerId) === true) break;\n }\n }\n\n private _handleWheel(e: WheelEvent): void {\n e.preventDefault();\n const el = this._element;\n let x = e.clientX;\n let y = e.clientY;\n if (el) {\n const rect = el.getBoundingClientRect();\n x = e.clientX - rect.left;\n y = e.clientY - rect.top;\n }\n for (const h of this._handlers) {\n h.onWheel?.(x, y, e.deltaY);\n }\n }\n\n private _handleKeyDown(e: KeyboardEvent): void {\n for (const h of this._handlers) {\n if (h.onKeyDown?.(e.key, e.shiftKey) === true) break;\n }\n }\n\n private _handleContextMenu(e: MouseEvent): void {\n e.preventDefault();\n const el = this._element;\n let x = e.clientX, y = e.clientY;\n if (el) { const rect = el.getBoundingClientRect(); x = e.clientX - rect.left; y = e.clientY - rect.top; }\n for (const h of this._handlers) {\n if (h.onContextMenu?.(x, y) === true) break;\n }\n }\n}\n"],"mappings":"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"}
|