gloomberb 0.10.0 → 0.10.2
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/package.json +8 -1
- package/src/api-client/auth.ts +1 -0
- package/src/api-client/index.test.ts +359 -108
- package/src/api-client/index.ts +24 -13
- package/src/api-client/socket.ts +70 -35
- package/src/api-client/types.ts +1 -1
- package/src/app/language-observer.test.tsx +82 -0
- package/src/app/language-observer.tsx +13 -0
- package/src/app/pane-runtime/index.tsx +2 -2
- package/src/app/pane-runtime/pane-settings-runtime.tsx +2 -2
- package/src/app/pane-runtime/pane-template-runtime.ts +2 -2
- package/src/app/pane-runtime/plugin-bindings.ts +2 -2
- package/src/app/pane-runtime/ticker-open-runtime.ts +2 -2
- package/src/app/runtime/broker-import.ts +2 -2
- package/src/app/runtime/plugin-bindings.ts +2 -2
- package/src/app/runtime/startup.ts +2 -2
- package/src/app.tsx +16 -7
- package/src/brokers/account-cache.ts +6 -6
- package/src/brokers/broker-ticker-sync.ts +3 -3
- package/src/brokers/remote-broker-adapter.ts +2 -2
- package/src/brokers/sync-broker-instance.ts +5 -6
- package/src/cli/commands/api.ts +3 -6
- package/src/cli/commands/automation.ts +7 -15
- package/src/cli/commands/market.ts +26 -61
- package/src/cli/commands/overview.ts +34 -45
- package/src/cli/commands/search.ts +25 -27
- package/src/cli/commands/system.ts +27 -57
- package/src/cli/commands/ticker.ts +62 -63
- package/src/cli/context.test.ts +63 -0
- package/src/cli/context.ts +15 -1
- package/src/cli/index.ts +1 -1
- package/src/cli/pane-functions/data.ts +2 -19
- package/src/cli/pane-functions/index.ts +26 -25
- package/src/cli/pane-functions/report.ts +1 -1
- package/src/cli/pane-functions/screenshot.ts +3 -3
- package/src/cli/scoped-context.ts +41 -0
- package/src/components/chart/composite/composite-chart.test.tsx +615 -18
- package/src/components/chart/composite/composite-chart.tsx +179 -79
- package/src/components/chart/composite/format.test.ts +20 -7
- package/src/components/chart/composite/index.ts +0 -21
- package/src/components/chart/composite/interactions.test.ts +90 -3
- package/src/components/chart/composite/interactions.ts +189 -24
- package/src/components/chart/composite/price-series.ts +0 -2
- package/src/components/chart/composite/rasterizer.ts +10 -9
- package/src/components/chart/composite/renderers.test.ts +108 -2
- package/src/components/chart/composite/scene.test.ts +36 -0
- package/src/components/chart/composite/scene.ts +5 -2
- package/src/components/chart/composite/text-renderer.ts +37 -25
- package/src/components/chart/composite/time-axis.test.ts +243 -0
- package/src/components/chart/composite/time-axis.ts +608 -0
- package/src/components/chart/composite/types.ts +12 -1
- package/src/components/chart/core/data.ts +2 -159
- package/src/components/chart/core/date-window.ts +6 -225
- package/src/components/chart/core/palette.ts +1 -12
- package/src/components/chart/core/pointer.ts +0 -142
- package/src/components/chart/core/types.ts +1 -58
- package/src/components/chart/cursor-motion.ts +2 -4
- package/src/components/chart/indicators/types.ts +0 -9
- package/src/components/chart/market-session.ts +0 -11
- package/src/components/chart/native/kitty/protocol.ts +1 -1
- package/src/components/chart/native/raster/layout.ts +3 -10
- package/src/components/chart/static/chart/axis-overlays.test.tsx +64 -0
- package/src/components/chart/static/chart/axis-overlays.tsx +59 -14
- package/src/components/chart/static/chart/surface.tsx +1 -2
- package/src/components/command-bar/action-runtime.ts +2 -2
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/panel/body.tsx +2 -2
- package/src/components/command-bar/panel/index.tsx +11 -9
- package/src/components/command-bar/routes/ticker-search/actions.ts +2 -2
- package/src/components/command-bar/surface/index.tsx +2 -2
- package/src/components/command-bar/workflow/body.tsx +3 -3
- package/src/components/command-bar/workflow/collection-actions.ts +2 -2
- package/src/components/command-bar/workflow/coordinator.ts +2 -2
- package/src/components/command-bar/workflow/field-row.tsx +25 -11
- package/src/components/command-bar/workflow/tickers.ts +2 -2
- package/src/components/feed-data-table/stack-view.test.tsx +28 -36
- package/src/components/feed-data-table/stack-view.tsx +3 -1
- package/src/components/layout/detached-pane-shell.tsx +6 -6
- package/src/components/layout/header.tsx +13 -9
- package/src/components/layout/pane/content.test.tsx +42 -3
- package/src/components/layout/pane/content.tsx +3 -1
- package/src/components/layout/pane/footer/index.test.tsx +45 -2
- package/src/components/layout/pane/footer/registration.tsx +3 -1
- package/src/components/layout/shell/index.tsx +1 -2
- package/src/components/layout/shell/native/surfaces.ts +1 -1
- package/src/components/layout/shell/native/window-state.ts +1 -1
- package/src/components/layout/status-bar.tsx +12 -8
- package/src/components/layout/window-controls.tsx +8 -6
- package/src/components/onboarding/onboarding-wizard.tsx +3 -1
- package/src/components/pane-settings-dialog/desktop.tsx +0 -2
- package/src/components/pane-settings-dialog.tsx +0 -2
- package/src/components/pane-template-wizard.tsx +10 -9
- package/src/components/price-performance.tsx +2 -113
- package/src/components/price-sparkline/model.ts +15 -17
- package/src/components/price-sparkline/view.tsx +8 -7
- package/src/components/speedometer-gauge/desktop.tsx +2 -1
- package/src/components/speedometer-gauge/index.tsx +0 -2
- package/src/components/speedometer-gauge/terminal.tsx +24 -5
- package/src/components/ticker/badge/index.tsx +1 -18
- package/src/components/ui/data-table/opentui/index.tsx +3 -3
- package/src/components/ui/frame.tsx +1 -2
- package/src/components/ui/page-stack-view.test.tsx +126 -0
- package/src/components/ui/page-stack-view.tsx +24 -12
- package/src/components/ui/tabs.tsx +7 -3
- package/src/components/ui/ui.test.tsx +23 -2
- package/src/components/use-external-link-footer.ts +4 -2
- package/src/core/app-service-ports.ts +46 -0
- package/src/core/app-services.test.ts +66 -0
- package/src/core/app-services.ts +17 -15
- package/src/data/config/chart-settings.ts +3 -10
- package/src/data/config/store/index.test.ts +99 -2
- package/src/data/config/store/migrations.ts +278 -0
- package/src/data/config/store/normalize.ts +44 -186
- package/src/data/resource-store.ts +73 -12
- package/src/data/sqlite/cache.test.ts +104 -15
- package/src/i18n/index.test.ts +17 -0
- package/src/i18n/index.ts +23 -6
- package/src/i18n/ja.ts +13 -0
- package/src/i18n/ko.ts +13 -0
- package/src/i18n/react.ts +6 -0
- package/src/i18n/zh-cn.ts +13 -0
- package/src/i18n/zh-tw.ts +13 -0
- package/src/market-data/coordinator/auxiliary.ts +7 -3
- package/src/market-data/coordinator/chart.ts +1 -1
- package/src/market-data/coordinator/index.test.ts +26 -0
- package/src/market-data/coordinator/index.ts +11 -5
- package/src/market-data/coordinator/quotes.ts +4 -40
- package/src/market-data/coordinator/subscriptions.test.ts +89 -1
- package/src/market-data/hooks.tsx +47 -1
- package/src/market-data/market/status.ts +6 -6
- package/src/market-data/performance.ts +1 -1
- package/src/market-data/quote-subscription-target.test.ts +37 -0
- package/src/market-data/quote-subscription-target.ts +60 -0
- package/src/market-data/quotes/freshness.ts +1 -0
- package/src/market-data/quotes/resolution.ts +4 -0
- package/src/market-data/request-types.ts +3 -3
- package/src/news/aggregator.test.ts +97 -1
- package/src/news/aggregator.ts +100 -57
- package/src/news/hooks.test.tsx +64 -0
- package/src/news/hooks.ts +18 -25
- package/src/plugins/builtin/account-management/footer.ts +4 -2
- package/src/plugins/builtin/account-management/pane.tsx +9 -4
- package/src/plugins/builtin/ai/agent-history.ts +11 -7
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +0 -5
- package/src/plugins/builtin/ai/ask-ai.test.tsx +3 -4
- package/src/plugins/builtin/ai/index.test.ts +2 -2
- package/src/plugins/builtin/ai/index.tsx +10 -11
- package/src/plugins/builtin/ai/pane-settings.ts +1 -1
- package/src/plugins/builtin/ai/pi/runtime.ts +6 -5
- package/src/plugins/builtin/ai/providers.ts +1 -9
- package/src/plugins/builtin/ai/runner.ts +0 -6
- package/src/plugins/builtin/ai/screener/footer.ts +4 -1
- package/src/plugins/builtin/ai/screener/pane.test.tsx +9 -9
- package/src/plugins/builtin/ai/workspace/model.ts +2 -27
- package/src/plugins/builtin/ai/workspace/pane.tsx +2 -2
- package/src/plugins/builtin/analytics/metrics.ts +1 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +3 -2
- package/src/plugins/builtin/broker-manager/index.tsx +4 -2
- package/src/plugins/builtin/chart-composer/editor/controller.ts +643 -0
- package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +134 -0
- package/src/plugins/builtin/chart-composer/editor/model.test.ts +85 -0
- package/src/plugins/builtin/chart-composer/editor/model.ts +292 -0
- package/src/plugins/builtin/chart-composer/editor/opentui-view.tsx +108 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +65 -947
- package/src/plugins/builtin/chart-composer/index.tsx +0 -6
- package/src/plugins/builtin/chart-composer/pane.tsx +69 -36
- package/src/plugins/builtin/chart-composer/presets.ts +1 -19
- package/src/plugins/builtin/chart-composer/settings.ts +1 -1
- package/src/plugins/builtin/chat/content/composer-runtime.ts +1 -1
- package/src/plugins/builtin/chat/content/shortcuts.ts +0 -1
- package/src/plugins/builtin/chat/controller/fetch.ts +8 -2
- package/src/plugins/builtin/chat/controller/persistence.ts +2 -0
- package/src/plugins/builtin/chat/controller/session.test.ts +25 -0
- package/src/plugins/builtin/chat/controller/session.ts +2 -0
- package/src/plugins/builtin/chat/controller/state.ts +7 -0
- package/src/plugins/builtin/chat/controller.test.ts +73 -20
- package/src/plugins/builtin/chat/message/desktop.tsx +2 -0
- package/src/plugins/builtin/cloud-tweets/footer.ts +9 -28
- package/src/plugins/builtin/cloud-tweets/pane.tsx +0 -5
- package/src/plugins/builtin/cloud-tweets/table.tsx +12 -17
- package/src/plugins/builtin/correlation/compute.ts +4 -10
- package/src/plugins/builtin/econ/index.tsx +17 -19
- package/src/plugins/builtin/holders/types.ts +0 -1
- package/src/plugins/builtin/insider/index.tsx +6 -31
- package/src/plugins/builtin/news/index.tsx +5 -7
- package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +6 -8
- package/src/plugins/builtin/news/wire/news/footer.ts +10 -11
- package/src/plugins/builtin/news/wire/read-state.test.ts +7 -0
- package/src/plugins/builtin/news/wire/read-state.ts +18 -28
- package/src/plugins/builtin/news/wire/rss/parser.ts +1 -11
- package/src/plugins/builtin/options/footer.test.ts +115 -0
- package/src/plugins/builtin/options/footer.ts +110 -0
- package/src/plugins/builtin/options/live-quotes.test.ts +238 -0
- package/src/plugins/builtin/options/live-quotes.ts +147 -0
- package/src/plugins/builtin/options/view.test.tsx +71 -1
- package/src/plugins/builtin/options/view.tsx +39 -24
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +103 -117
- package/src/plugins/builtin/portfolio-list/cli/render.ts +11 -5
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +120 -135
- package/src/plugins/builtin/portfolio-list/header.tsx +9 -4
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +6 -1
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +4 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +24 -52
- package/src/plugins/builtin/sec/filing-content.test.ts +41 -0
- package/src/plugins/builtin/sec/filing-content.ts +94 -0
- package/src/plugins/builtin/sec/index.tsx +21 -104
- package/src/plugins/builtin/sectors/sector-model.ts +1 -7
- package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -0
- package/src/plugins/builtin/shared/pane-footer.ts +82 -0
- package/src/plugins/builtin/shared/read-state.ts +64 -3
- package/src/plugins/builtin/substack/read-state.ts +16 -41
- package/src/plugins/builtin/thirteenf/pane.tsx +24 -73
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +10 -10
- package/src/plugins/builtin/yield-curve/index.tsx +11 -9
- package/src/plugins/ibkr/dialogs.tsx +3 -3
- package/src/plugins/ibkr/gateway/fundamentals.ts +1 -1
- package/src/plugins/ibkr/gateway/history-requests.ts +2 -2
- package/src/plugins/ibkr/gateway/history.ts +2 -2
- package/src/plugins/ibkr/gateway/selection.ts +2 -2
- package/src/plugins/ibkr/gateway/service/index.ts +2 -2
- package/src/plugins/ibkr/gateway/service/native.ts +2 -2
- package/src/plugins/plugin-persistence.ts +3 -4
- package/src/plugins/prediction-markets/controller/keyboard.ts +1 -1
- package/src/plugins/prediction-markets/controller/live-updates.ts +6 -5
- package/src/plugins/prediction-markets/detail/book.tsx +2 -2
- package/src/plugins/prediction-markets/detail/outcomes.tsx +3 -1
- package/src/plugins/prediction-markets/detail/pane.tsx +3 -1
- package/src/plugins/prediction-markets/detail/trades.tsx +3 -3
- package/src/plugins/registry/context.ts +3 -4
- package/src/plugins/registry/index.ts +5 -6
- package/src/react/input.test.ts +126 -3
- package/src/react/input.ts +120 -1
- package/src/remote/app-host.tsx +2 -13
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +44 -60
- package/src/renderers/electrobun/bun/desktop/capability-bridge.ts +15 -13
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +26 -22
- package/src/renderers/electrobun/bun/desktop/http-fetch.ts +5 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +11 -6
- package/src/renderers/electrobun/bun/desktop/media.ts +5 -3
- package/src/renderers/electrobun/bun/desktop/plugin-state.ts +21 -22
- package/src/renderers/electrobun/bun/desktop/window-controls.ts +3 -1
- package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +18 -18
- package/src/renderers/electrobun/bun/index.ts +123 -83
- package/src/renderers/electrobun/shared/protocol.ts +146 -7
- package/src/renderers/electrobun/view/app-services.ts +5 -8
- package/src/renderers/electrobun/view/backend-rpc.ts +18 -8
- package/src/renderers/electrobun/view/broker-remote-client.ts +2 -2
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +5 -22
- package/src/renderers/electrobun/view/config-host.ts +1 -1
- package/src/renderers/electrobun/view/context-menu.ts +1 -17
- package/src/renderers/electrobun/view/data-table/index.tsx +0 -1
- package/src/renderers/electrobun/view/data-table/row.tsx +0 -4
- package/src/renderers/electrobun/view/desktop/control-styles.ts +37 -34
- package/src/renderers/electrobun/view/desktop/controls.tsx +36 -33
- package/src/renderers/electrobun/view/desktop/list-view.tsx +15 -12
- package/src/renderers/electrobun/view/desktop/popover.tsx +2 -3
- package/src/renderers/electrobun/view/dialog-host.tsx +2 -3
- package/src/renderers/electrobun/view/fatal-screen.tsx +0 -1
- package/src/renderers/electrobun/view/host/box.tsx +0 -1
- package/src/renderers/electrobun/view/host/chart-surface.tsx +0 -1
- package/src/renderers/electrobun/view/host/input.tsx +0 -1
- package/src/renderers/electrobun/view/host/media-surface.tsx +0 -1
- package/src/renderers/electrobun/view/host/native.ts +0 -1
- package/src/renderers/electrobun/view/host/scroll-box.tsx +0 -1
- package/src/renderers/electrobun/view/host/tabs.tsx +0 -1
- package/src/renderers/electrobun/view/host/text.tsx +0 -1
- package/src/renderers/electrobun/view/http-fetch.ts +3 -11
- package/src/renderers/electrobun/view/input-host.tsx +10 -53
- package/src/renderers/electrobun/view/key-event.ts +0 -1
- package/src/renderers/electrobun/view/main.tsx +3 -3
- package/src/renderers/electrobun/view/native-renderer.ts +0 -1
- package/src/renderers/electrobun/view/native-stubs/backend-rpc.ts +13 -1
- package/src/renderers/electrobun/view/native-stubs/chart/surface-manager.ts +11 -0
- package/src/renderers/electrobun/view/native-stubs/native-stubs.test.ts +47 -0
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +6 -44
- package/src/renderers/electrobun/view/remote/persistence.ts +6 -2
- package/src/renderers/electrobun/view/remote/quote-subscription-registry.test.ts +86 -0
- package/src/renderers/electrobun/view/remote/quote-subscription-registry.ts +72 -0
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +2 -2
- package/src/renderers/electrobun/view/toast-host.tsx +0 -1
- package/src/renderers/electrobun/view/ui-host.tsx +1 -2
- package/src/renderers/electrobun/view/update-host.ts +1 -1
- package/src/renderers/opentui/chart-surface.tsx +2 -1
- package/src/renderers/opentui/dialog-host.tsx +20 -4
- package/src/renderers/opentui/image/loader.ts +12 -2
- package/src/renderers/opentui/input-host.tsx +7 -49
- package/src/renderers/opentui/start.tsx +2 -0
- package/src/sources/gloomberb-cloud/history-quality.ts +44 -0
- package/src/sources/gloomberb-cloud/index.test.ts +173 -0
- package/src/sources/gloomberb-cloud/index.ts +42 -20
- package/src/sources/gloomberb-cloud/normalizers.ts +9 -3
- package/src/sources/provider-router/cache.ts +1 -1
- package/src/sources/provider-router/document-routes.ts +10 -5
- package/src/sources/provider-router/financials.test.ts +14 -0
- package/src/sources/provider-router/financials.ts +6 -1
- package/src/sources/provider-router/history.test.ts +163 -1
- package/src/sources/provider-router/history.ts +278 -347
- package/src/sources/provider-router/index.ts +2 -2
- package/src/sources/provider-router/routing.test.ts +39 -0
- package/src/sources/provider-router/routing.ts +24 -11
- package/src/sources/provider-router/supplemental.ts +37 -17
- package/src/sources/sec-edgar/content.ts +2 -12
- package/src/sources/sec-edgar.ts +2 -13
- package/src/sources/yahoo-finance/history.ts +2 -2
- package/src/sources/yahoo-finance/options.ts +5 -0
- package/src/sources/yahoo-finance.ts +2 -2
- package/src/state/app/bootstrap.ts +2 -2
- package/src/state/app/context/index.tsx +2 -2
- package/src/state/hooks/quote-streaming.ts +66 -1
- package/src/state/hooks/session-snapshot.ts +2 -2
- package/src/state/selectors.ts +0 -9
- package/src/sync/controller.test.ts +98 -0
- package/src/sync/controller.ts +29 -5
- package/src/sync/react.ts +2 -2
- package/src/sync/types.ts +2 -2
- package/src/theme/colors.test.ts +10 -0
- package/src/theme/colors.ts +75 -78
- package/src/theme/theme-context.test.tsx +67 -0
- package/src/theme/theme-context.tsx +9 -5
- package/src/tickers/open-target.ts +2 -2
- package/src/tickers/search/upsert.ts +2 -2
- package/src/time-series/alignment.ts +0 -7
- package/src/time-series/chart-data.ts +145 -0
- package/src/time-series/date-window.ts +44 -0
- package/src/time-series/history-window.ts +21 -0
- package/src/time-series/hooks.test.ts +59 -0
- package/src/time-series/hooks.ts +33 -12
- package/src/time-series/market-resolution.ts +2 -2
- package/src/time-series/market.ts +1 -1
- package/src/time-series/range.ts +9 -0
- package/src/{components/chart/core → time-series}/resolution.ts +3 -102
- package/src/time-series/resolve.test.ts +203 -1
- package/src/time-series/resolve.ts +96 -18
- package/src/time-series/spec-studies.test.ts +15 -0
- package/src/time-series/spec.ts +2 -2
- package/src/time-series/studies.ts +5 -2
- package/src/time-series/types.ts +3 -1
- package/src/time-series/use-chart-resolution.test.tsx +358 -0
- package/src/time-series/use-chart-resolution.ts +46 -4
- package/src/types/broker.ts +2 -2
- package/src/types/data-provider.ts +3 -3
- package/src/types/financials.ts +12 -0
- package/src/types/plugin.ts +2 -2
- package/src/ui/context-menu.tsx +3 -2
- package/src/ui/dialog.tsx +2 -2
- package/src/utils/html-entities.ts +3 -5
- package/src/utils/main-thread-monitor.ts +1 -1
- package/src/utils/price-history.ts +5 -5
- package/src/version.ts +3 -1
- package/src/components/chart/core/viewport.test.ts +0 -8
- package/src/components/chart/core/viewport.ts +0 -24
- package/src/time-series/index.ts +0 -14
- /package/src/{components/chart/core → time-series}/resolution.test.ts +0 -0
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { hasLikelyQuoteUnitMismatch } from "../../../utils/currency-units";
|
|
4
|
-
import {
|
|
5
|
-
CHART_RESOLUTION_STEP_MS,
|
|
6
|
-
type ManualChartResolution,
|
|
7
|
-
} from "./resolution";
|
|
8
|
-
|
|
1
|
+
export { appendLiveQuotePoint } from "../../../time-series/chart-data";
|
|
2
|
+
export type { AppendLiveQuotePointOptions } from "../../../time-series/chart-data";
|
|
9
3
|
export {
|
|
10
4
|
bucketOhlcSeries,
|
|
11
5
|
projectChartData,
|
|
@@ -17,154 +11,3 @@ export type {
|
|
|
17
11
|
ProjectChartDataOptions,
|
|
18
12
|
ProjectedChartPoint,
|
|
19
13
|
} from "./projection";
|
|
20
|
-
|
|
21
|
-
const MAX_LIVE_QUOTE_TAIL_AGE_MS = 7 * 24 * 60 * 60_000;
|
|
22
|
-
const MAX_LIVE_QUOTE_CLOCK_SKEW_MS = 5 * 60_000;
|
|
23
|
-
const MAX_INTRADAY_BAR_INTERVAL_MS = 6 * 60 * 60_000;
|
|
24
|
-
const MIN_LIVE_QUOTE_TAIL_GAP_MS = 5 * 60_000;
|
|
25
|
-
const MS_DAY = 86400_000;
|
|
26
|
-
|
|
27
|
-
export type AppendLiveQuotePointOptions =
|
|
28
|
-
| {
|
|
29
|
-
now?: number;
|
|
30
|
-
mode?: "scalar";
|
|
31
|
-
}
|
|
32
|
-
| {
|
|
33
|
-
now?: number;
|
|
34
|
-
mode: "ohlc";
|
|
35
|
-
resolution: ManualChartResolution;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
function coerceDate(value: Date | string | number): Date {
|
|
39
|
-
return value instanceof Date ? value : new Date(value);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function getPointTime(point: Pick<PricePoint, "date">): number {
|
|
43
|
-
return coerceDate(point.date as Date | string | number).getTime();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function getActiveQuotePrice(quote: Quote): number {
|
|
47
|
-
if ((quote.marketState === "PRE" || quote.marketState === "PREPRE") && quote.preMarketPrice != null) {
|
|
48
|
-
return quote.preMarketPrice;
|
|
49
|
-
}
|
|
50
|
-
if ((quote.marketState === "POST" || quote.marketState === "POSTPOST") && quote.postMarketPrice != null) {
|
|
51
|
-
return quote.postMarketPrice;
|
|
52
|
-
}
|
|
53
|
-
return quote.price;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function quoteBelongsToLatestBar(
|
|
57
|
-
latestTime: number,
|
|
58
|
-
quoteTime: number,
|
|
59
|
-
resolution: ManualChartResolution,
|
|
60
|
-
): boolean {
|
|
61
|
-
if (quoteTime < latestTime) return false;
|
|
62
|
-
if (resolution === "1mo") {
|
|
63
|
-
const latestDate = new Date(latestTime);
|
|
64
|
-
const quoteDate = new Date(quoteTime);
|
|
65
|
-
return latestDate.getUTCFullYear() === quoteDate.getUTCFullYear()
|
|
66
|
-
&& latestDate.getUTCMonth() === quoteDate.getUTCMonth();
|
|
67
|
-
}
|
|
68
|
-
return quoteTime - latestTime < CHART_RESOLUTION_STEP_MS[resolution];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function finiteOrFallback(value: number | undefined, fallback: number): number {
|
|
72
|
-
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function mergeQuoteIntoLatestBar(latest: PricePoint, quotePrice: number): PricePoint {
|
|
76
|
-
const open = finiteOrFallback(latest.open, latest.close);
|
|
77
|
-
const high = finiteOrFallback(latest.high, Math.max(open, latest.close));
|
|
78
|
-
const low = finiteOrFallback(latest.low, Math.min(open, latest.close));
|
|
79
|
-
return {
|
|
80
|
-
...latest,
|
|
81
|
-
open,
|
|
82
|
-
high: Math.max(high, open, latest.close, quotePrice),
|
|
83
|
-
low: Math.min(low, open, latest.close, quotePrice),
|
|
84
|
-
close: quotePrice,
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function appendLiveQuotePoint(
|
|
89
|
-
points: PricePoint[],
|
|
90
|
-
quote: Quote | null | undefined,
|
|
91
|
-
options: AppendLiveQuotePointOptions = {},
|
|
92
|
-
): PricePoint[] {
|
|
93
|
-
const now = options.now ?? Date.now();
|
|
94
|
-
if (!quote || isQuoteStaleForCurrentSession(quote, now)) return points;
|
|
95
|
-
|
|
96
|
-
const quoteTime = quote.lastUpdated;
|
|
97
|
-
const quotePrice = getActiveQuotePrice(quote);
|
|
98
|
-
if (
|
|
99
|
-
!Number.isFinite(quoteTime)
|
|
100
|
-
|| !Number.isFinite(quotePrice)
|
|
101
|
-
|| quotePrice <= 0
|
|
102
|
-
|| quoteTime > now + MAX_LIVE_QUOTE_CLOCK_SKEW_MS
|
|
103
|
-
|| now - quoteTime > MAX_LIVE_QUOTE_TAIL_AGE_MS
|
|
104
|
-
) {
|
|
105
|
-
return points;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const latest = points.at(-1);
|
|
109
|
-
if (!latest) return points;
|
|
110
|
-
|
|
111
|
-
const latestTime = getPointTime(latest);
|
|
112
|
-
if (!Number.isFinite(latestTime) || quoteTime < latestTime) return points;
|
|
113
|
-
|
|
114
|
-
const previous = points.at(-2);
|
|
115
|
-
const latestInterval = previous ? latestTime - getPointTime(previous) : Number.NaN;
|
|
116
|
-
if (
|
|
117
|
-
latestInterval > 0
|
|
118
|
-
&& latestInterval <= MAX_INTRADAY_BAR_INTERVAL_MS
|
|
119
|
-
&& quoteTime - latestTime > Math.max(MIN_LIVE_QUOTE_TAIL_GAP_MS, latestInterval * 3)
|
|
120
|
-
) {
|
|
121
|
-
return points;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const latestClose = latest.close;
|
|
125
|
-
if (hasLikelyQuoteUnitMismatch(
|
|
126
|
-
{ currency: quote.currency, price: latestClose },
|
|
127
|
-
{ currency: quote.currency, price: quotePrice },
|
|
128
|
-
)) {
|
|
129
|
-
return points;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (options.mode === "ohlc") {
|
|
133
|
-
if (quoteBelongsToLatestBar(latestTime, quoteTime, options.resolution)) {
|
|
134
|
-
const merged = mergeQuoteIntoLatestBar(latest, quotePrice);
|
|
135
|
-
return [...points.slice(0, -1), merged];
|
|
136
|
-
}
|
|
137
|
-
return [
|
|
138
|
-
...points,
|
|
139
|
-
{
|
|
140
|
-
date: new Date(quoteTime),
|
|
141
|
-
open: quotePrice,
|
|
142
|
-
high: quotePrice,
|
|
143
|
-
low: quotePrice,
|
|
144
|
-
close: quotePrice,
|
|
145
|
-
},
|
|
146
|
-
];
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (quoteTime === latestTime) return points;
|
|
150
|
-
|
|
151
|
-
return [
|
|
152
|
-
...points,
|
|
153
|
-
{
|
|
154
|
-
date: new Date(quoteTime),
|
|
155
|
-
close: quotePrice,
|
|
156
|
-
},
|
|
157
|
-
];
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Given the visible time span in milliseconds, return a generic bar size label
|
|
162
|
-
* for fetching higher-resolution data, or null if the base data is sufficient.
|
|
163
|
-
*/
|
|
164
|
-
export function resolveBarSize(visibleTimeSpanMs: number): string | null {
|
|
165
|
-
if (visibleTimeSpanMs < MS_DAY) return "5m";
|
|
166
|
-
if (visibleTimeSpanMs < 3 * MS_DAY) return "15m";
|
|
167
|
-
if (visibleTimeSpanMs < 28 * MS_DAY) return "1h";
|
|
168
|
-
if (visibleTimeSpanMs < 90 * MS_DAY) return "1d";
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { PricePoint } from "../../../types/financials";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import type { TimeRange, VisibleWindow } from "./types";
|
|
3
|
+
import type { DateWindowRange } from "../../../time-series/date-window";
|
|
4
|
+
import { subtractTimeRange } from "../../../time-series/date-window";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
interface VisibleDateWindow {
|
|
6
7
|
start: Date | null;
|
|
7
8
|
end: Date | null;
|
|
8
9
|
dates: Date[];
|
|
@@ -11,60 +12,6 @@ export interface VisibleDateWindow {
|
|
|
11
12
|
totalDates: number;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export interface DateWindowRange extends ChartDateWindow {}
|
|
15
|
-
|
|
16
|
-
export interface DateWindowViewport {
|
|
17
|
-
panOffset: number;
|
|
18
|
-
zoomLevel: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function subtractTimeRange(endDate: Date, range: TimeRange): Date {
|
|
22
|
-
const startDate = new Date(endDate);
|
|
23
|
-
switch (range) {
|
|
24
|
-
case "1D":
|
|
25
|
-
startDate.setDate(startDate.getDate() - 1);
|
|
26
|
-
break;
|
|
27
|
-
case "1W":
|
|
28
|
-
startDate.setDate(startDate.getDate() - 7);
|
|
29
|
-
break;
|
|
30
|
-
case "1M":
|
|
31
|
-
startDate.setMonth(startDate.getMonth() - 1);
|
|
32
|
-
break;
|
|
33
|
-
case "3M":
|
|
34
|
-
startDate.setMonth(startDate.getMonth() - 3);
|
|
35
|
-
break;
|
|
36
|
-
case "6M":
|
|
37
|
-
startDate.setMonth(startDate.getMonth() - 6);
|
|
38
|
-
break;
|
|
39
|
-
case "1Y":
|
|
40
|
-
startDate.setFullYear(startDate.getFullYear() - 1);
|
|
41
|
-
break;
|
|
42
|
-
case "5Y":
|
|
43
|
-
startDate.setFullYear(startDate.getFullYear() - 5);
|
|
44
|
-
break;
|
|
45
|
-
case "ALL":
|
|
46
|
-
startDate.setFullYear(startDate.getFullYear() - 50);
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
return startDate;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function isDateWindowWithinTimeRange(startDate: Date, endDate: Date, maxRange: TimeRange): boolean {
|
|
53
|
-
if (maxRange === "ALL") return true;
|
|
54
|
-
return startDate.getTime() >= subtractTimeRange(endDate, maxRange).getTime();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function getTimeRangeForDateWindow(
|
|
58
|
-
window: DateWindowRange | null,
|
|
59
|
-
): TimeRange {
|
|
60
|
-
if (!window?.start || !window.end) return "ALL";
|
|
61
|
-
return TIME_RANGES.find((candidate) => isDateWindowWithinTimeRange(window.start!, window.end!, candidate)) ?? "ALL";
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function clamp(value: number, min: number, max: number): number {
|
|
65
|
-
return Math.max(min, Math.min(max, value));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
15
|
function coerceDate(value: Date | string | number): Date {
|
|
69
16
|
return value instanceof Date ? value : new Date(value);
|
|
70
17
|
}
|
|
@@ -149,29 +96,10 @@ function resolveMinimumPointWindow(
|
|
|
149
96
|
return { startIdx, endIdx };
|
|
150
97
|
}
|
|
151
98
|
|
|
152
|
-
|
|
99
|
+
function getPointDates(points: readonly Pick<PricePoint, "date">[]): Date[] {
|
|
153
100
|
return points.map((point) => coerceDate(point.date as Date | string | number));
|
|
154
101
|
}
|
|
155
102
|
|
|
156
|
-
export function getDateWindowBounds(dates: readonly Date[]): DateWindowRange | null {
|
|
157
|
-
if (dates.length === 0) return null;
|
|
158
|
-
return {
|
|
159
|
-
start: dates[0] ?? null,
|
|
160
|
-
end: dates[dates.length - 1] ?? null,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export function getMinimumDateStepMs(dates: readonly Date[]): number {
|
|
165
|
-
let minimum = Number.POSITIVE_INFINITY;
|
|
166
|
-
for (let index = 1; index < dates.length; index += 1) {
|
|
167
|
-
const delta = dates[index]!.getTime() - dates[index - 1]!.getTime();
|
|
168
|
-
if (delta > 0 && delta < minimum) {
|
|
169
|
-
minimum = delta;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
return Number.isFinite(minimum) ? minimum : 1;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
103
|
export function buildPresetDateWindow(dates: readonly Date[], presetRange: TimeRange): DateWindowRange | null {
|
|
176
104
|
if (dates.length === 0) return null;
|
|
177
105
|
if (presetRange === "ALL") {
|
|
@@ -191,134 +119,7 @@ export function buildPresetDateWindow(dates: readonly Date[], presetRange: TimeR
|
|
|
191
119
|
};
|
|
192
120
|
}
|
|
193
121
|
|
|
194
|
-
|
|
195
|
-
left: DateWindowRange | null | undefined,
|
|
196
|
-
right: DateWindowRange | null | undefined,
|
|
197
|
-
toleranceMs = 1,
|
|
198
|
-
): boolean {
|
|
199
|
-
if (!left?.start || !left.end || !right?.start || !right.end) {
|
|
200
|
-
return left?.start === right?.start && left?.end === right?.end;
|
|
201
|
-
}
|
|
202
|
-
return Math.abs(left.start.getTime() - right.start.getTime()) <= toleranceMs
|
|
203
|
-
&& Math.abs(left.end.getTime() - right.end.getTime()) <= toleranceMs;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export function clampDateWindowToBounds(
|
|
207
|
-
window: DateWindowRange | null | undefined,
|
|
208
|
-
bounds: DateWindowRange | null | undefined,
|
|
209
|
-
minimumSpanMs = 1,
|
|
210
|
-
): DateWindowRange | null {
|
|
211
|
-
const normalizedWindow = normalizeDateWindowRange(window);
|
|
212
|
-
const normalizedBounds = normalizeDateWindowRange(bounds);
|
|
213
|
-
if (!normalizedWindow || !normalizedBounds) return null;
|
|
214
|
-
|
|
215
|
-
const availableSpanMs = Math.max(normalizedBounds.endMs - normalizedBounds.startMs, 0);
|
|
216
|
-
const effectiveMinimumSpanMs = availableSpanMs === 0
|
|
217
|
-
? 0
|
|
218
|
-
: Math.min(Math.max(minimumSpanMs, 1), availableSpanMs);
|
|
219
|
-
const requestedSpanMs = Math.max(normalizedWindow.endMs - normalizedWindow.startMs, 0);
|
|
220
|
-
const targetSpanMs = Math.min(
|
|
221
|
-
Math.max(requestedSpanMs, effectiveMinimumSpanMs),
|
|
222
|
-
availableSpanMs,
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
if (availableSpanMs === 0 || targetSpanMs === 0) {
|
|
226
|
-
return {
|
|
227
|
-
start: new Date(normalizedBounds.startMs),
|
|
228
|
-
end: new Date(normalizedBounds.endMs),
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (targetSpanMs >= availableSpanMs) {
|
|
233
|
-
return {
|
|
234
|
-
start: new Date(normalizedBounds.startMs),
|
|
235
|
-
end: new Date(normalizedBounds.endMs),
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
let startMs: number;
|
|
240
|
-
let endMs: number;
|
|
241
|
-
|
|
242
|
-
if (requestedSpanMs < targetSpanMs) {
|
|
243
|
-
const centerMs = normalizedWindow.startMs + (requestedSpanMs / 2);
|
|
244
|
-
startMs = centerMs - (targetSpanMs / 2);
|
|
245
|
-
endMs = centerMs + (targetSpanMs / 2);
|
|
246
|
-
} else {
|
|
247
|
-
startMs = normalizedWindow.startMs;
|
|
248
|
-
endMs = normalizedWindow.startMs + targetSpanMs;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (startMs < normalizedBounds.startMs) {
|
|
252
|
-
startMs = normalizedBounds.startMs;
|
|
253
|
-
endMs = startMs + targetSpanMs;
|
|
254
|
-
} else if (endMs > normalizedBounds.endMs) {
|
|
255
|
-
endMs = normalizedBounds.endMs;
|
|
256
|
-
startMs = endMs - targetSpanMs;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
return {
|
|
260
|
-
start: new Date(startMs),
|
|
261
|
-
end: new Date(endMs),
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
export function shiftDateWindow(
|
|
266
|
-
window: DateWindowRange | null | undefined,
|
|
267
|
-
shiftRatio: number,
|
|
268
|
-
): DateWindowRange | null {
|
|
269
|
-
const normalizedWindow = normalizeDateWindowRange(window);
|
|
270
|
-
if (!normalizedWindow) return null;
|
|
271
|
-
|
|
272
|
-
const spanMs = Math.max(normalizedWindow.endMs - normalizedWindow.startMs, 1);
|
|
273
|
-
const shiftMs = spanMs * shiftRatio;
|
|
274
|
-
|
|
275
|
-
return {
|
|
276
|
-
start: new Date(normalizedWindow.startMs - shiftMs),
|
|
277
|
-
end: new Date(normalizedWindow.endMs - shiftMs),
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function getCanonicalVisiblePointCount(dates: readonly Date[], presetRange: TimeRange): number {
|
|
282
|
-
if (dates.length === 0) return 0;
|
|
283
|
-
if (presetRange === "ALL") return dates.length;
|
|
284
|
-
const endDate = dates[dates.length - 1]!;
|
|
285
|
-
const threshold = subtractTimeRange(endDate, presetRange).getTime();
|
|
286
|
-
const firstVisibleIndex = dates.findIndex((date) => date.getTime() >= threshold);
|
|
287
|
-
if (firstVisibleIndex < 0) return dates.length;
|
|
288
|
-
return Math.max(dates.length - firstVisibleIndex, 1);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
export function getCanonicalZoomLevel(dates: readonly Date[], presetRange: TimeRange): number {
|
|
292
|
-
if (dates.length === 0) return 1;
|
|
293
|
-
const visibleCount = getCanonicalVisiblePointCount(dates, presetRange);
|
|
294
|
-
return clampChartZoom(dates.length, dates.length / Math.max(visibleCount, 1));
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
export function buildVisibleDateWindow(
|
|
298
|
-
dates: readonly Date[],
|
|
299
|
-
panOffset: number,
|
|
300
|
-
zoomLevel: number,
|
|
301
|
-
): VisibleDateWindow {
|
|
302
|
-
if (dates.length === 0) {
|
|
303
|
-
return { start: null, end: null, dates: [], startIdx: 0, endIdx: 0, totalDates: 0 };
|
|
304
|
-
}
|
|
305
|
-
const visibleCount = getVisiblePointCount(dates.length, zoomLevel);
|
|
306
|
-
const maxPan = Math.max(dates.length - visibleCount, 0);
|
|
307
|
-
const pan = clamp(panOffset, 0, maxPan);
|
|
308
|
-
const endIdx = dates.length - pan;
|
|
309
|
-
const startIdx = Math.max(endIdx - visibleCount, 0);
|
|
310
|
-
const visibleDates = dates.slice(startIdx, endIdx);
|
|
311
|
-
return {
|
|
312
|
-
start: visibleDates[0] ?? null,
|
|
313
|
-
end: visibleDates[visibleDates.length - 1] ?? null,
|
|
314
|
-
dates: visibleDates,
|
|
315
|
-
startIdx,
|
|
316
|
-
endIdx,
|
|
317
|
-
totalDates: dates.length,
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
export function buildVisibleDateWindowFromRange(
|
|
122
|
+
function buildVisibleDateWindowFromRange(
|
|
322
123
|
dates: readonly Date[],
|
|
323
124
|
window: DateWindowRange | null | undefined,
|
|
324
125
|
minimumPoints = 2,
|
|
@@ -352,26 +153,6 @@ export function buildVisibleDateWindowFromRange(
|
|
|
352
153
|
};
|
|
353
154
|
}
|
|
354
155
|
|
|
355
|
-
export function resolveViewportForDateWindow(
|
|
356
|
-
dates: readonly Date[],
|
|
357
|
-
window: DateWindowRange | null | undefined,
|
|
358
|
-
minimumPoints = 2,
|
|
359
|
-
): DateWindowViewport | null {
|
|
360
|
-
if (dates.length === 0) return null;
|
|
361
|
-
const visibleWindow = buildVisibleDateWindowFromRange(dates, window, minimumPoints);
|
|
362
|
-
if (visibleWindow.dates.length === 0) return null;
|
|
363
|
-
|
|
364
|
-
const visibleCount = Math.max(visibleWindow.dates.length, 1);
|
|
365
|
-
const zoomLevel = clampChartZoom(dates.length, dates.length / visibleCount);
|
|
366
|
-
const maxPanOffset = Math.max(dates.length - visibleCount, 0);
|
|
367
|
-
const panOffset = clamp(dates.length - visibleWindow.endIdx, 0, maxPanOffset);
|
|
368
|
-
|
|
369
|
-
return {
|
|
370
|
-
panOffset,
|
|
371
|
-
zoomLevel,
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
156
|
export function getVisibleWindowForDateRange(
|
|
376
157
|
points: readonly PricePoint[],
|
|
377
158
|
window: DateWindowRange | null | undefined,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ChartColors } from "./types";
|
|
2
|
+
import { blendHex } from "../../../theme/color-utils";
|
|
2
3
|
|
|
3
4
|
interface ChartPaletteInput {
|
|
4
5
|
bg: string;
|
|
@@ -17,18 +18,6 @@ export interface ResolvedChartPalette extends ChartColors {
|
|
|
17
18
|
wickDown: string;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
function blendHex(a: string, b: string, ratio: number): string {
|
|
21
|
-
const parse = (hex: string) => {
|
|
22
|
-
const h = hex.replace("#", "");
|
|
23
|
-
return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)] as const;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const [ar, ag, ab] = parse(a);
|
|
27
|
-
const [br, bg, bb] = parse(b);
|
|
28
|
-
const mix = (x: number, y: number) => Math.round(x + (y - x) * ratio).toString(16).padStart(2, "0");
|
|
29
|
-
return `#${mix(ar, br)}${mix(ag, bg)}${mix(ab, bb)}`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
21
|
export function resolveChartPalette(
|
|
33
22
|
baseColors: ChartPaletteInput,
|
|
34
23
|
trend: "positive" | "negative" | "neutral" = "positive",
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { NativeRendererHost as CliRenderer } from "../../../ui";
|
|
2
|
-
import { sameCursorPosition, type ChartCursorMotionKind } from "../cursor-motion";
|
|
3
2
|
|
|
4
3
|
type RendererMetricsHost = Pick<CliRenderer, "resolution" | "terminalWidth" | "terminalHeight">;
|
|
5
4
|
|
|
@@ -36,8 +35,6 @@ export interface ChartMouseEvent {
|
|
|
36
35
|
};
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
export type MouseInteractionEvent = Pick<ChartMouseEvent, "stopPropagation" | "preventDefault">;
|
|
40
|
-
|
|
41
38
|
export function consumeChartMouseEvent(event: Pick<ChartMouseEvent, "stopPropagation" | "preventDefault">): void {
|
|
42
39
|
event.stopPropagation?.();
|
|
43
40
|
event.preventDefault?.();
|
|
@@ -51,46 +48,10 @@ export interface LocalPlotPointer {
|
|
|
51
48
|
hasPixelPrecision: boolean;
|
|
52
49
|
}
|
|
53
50
|
|
|
54
|
-
export interface DisplayCursorState {
|
|
55
|
-
cellX: number | null;
|
|
56
|
-
cellY: number | null;
|
|
57
|
-
pixelX: number | null;
|
|
58
|
-
pixelY: number | null;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export const EMPTY_DISPLAY_CURSOR: DisplayCursorState = {
|
|
62
|
-
cellX: null,
|
|
63
|
-
cellY: null,
|
|
64
|
-
pixelX: null,
|
|
65
|
-
pixelY: null,
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export const PIXEL_CURSOR_SNAP_DISTANCE = 0.5;
|
|
69
|
-
|
|
70
|
-
const globalAnimationFrame = globalThis as typeof globalThis & {
|
|
71
|
-
requestAnimationFrame?: (callback: (timestamp: number) => void) => number;
|
|
72
|
-
cancelAnimationFrame?: (handle: number) => void;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
51
|
function clamp(value: number, min: number, max: number): number {
|
|
76
52
|
return Math.max(min, Math.min(max, value));
|
|
77
53
|
}
|
|
78
54
|
|
|
79
|
-
export function requestAnimationFrameSafe(callback: (timestamp: number) => void): number {
|
|
80
|
-
if (typeof globalAnimationFrame.requestAnimationFrame === "function") {
|
|
81
|
-
return globalAnimationFrame.requestAnimationFrame(callback);
|
|
82
|
-
}
|
|
83
|
-
return globalThis.setTimeout(() => callback(Date.now()), 16) as unknown as number;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function cancelAnimationFrameSafe(handle: number) {
|
|
87
|
-
if (typeof globalAnimationFrame.cancelAnimationFrame === "function") {
|
|
88
|
-
globalAnimationFrame.cancelAnimationFrame(handle);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
clearTimeout(handle);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
55
|
function getRendererCellMetrics(renderer: RendererMetricsHost) {
|
|
95
56
|
if (!renderer.resolution) return null;
|
|
96
57
|
const cellWidth = renderer.resolution.width / Math.max(renderer.terminalWidth, 1);
|
|
@@ -129,47 +90,6 @@ function getPlotBounds(renderable: PlotRenderableLike | null): { x: number; y: n
|
|
|
129
90
|
return { x, y, width: size.width, height: size.height };
|
|
130
91
|
}
|
|
131
92
|
|
|
132
|
-
export function getRenderablePixelSize(
|
|
133
|
-
renderable: RenderableSizeLike | null,
|
|
134
|
-
renderer: RendererMetricsHost,
|
|
135
|
-
) {
|
|
136
|
-
const metrics = getRendererCellMetrics(renderer);
|
|
137
|
-
const size = getRenderableSize(renderable);
|
|
138
|
-
if (!size || !metrics) return null;
|
|
139
|
-
return {
|
|
140
|
-
pixelWidth: Math.max(size.width * metrics.cellWidth, 1),
|
|
141
|
-
pixelHeight: Math.max(size.height * metrics.cellHeight, 1),
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function projectCellCursorToLocalPixels(
|
|
146
|
-
cellX: number,
|
|
147
|
-
cellY: number,
|
|
148
|
-
renderable: RenderableSizeLike | null,
|
|
149
|
-
renderer: RendererMetricsHost,
|
|
150
|
-
): { pixelX: number; pixelY: number } | null {
|
|
151
|
-
const pixelSize = getRenderablePixelSize(renderable, renderer);
|
|
152
|
-
const size = getRenderableSize(renderable);
|
|
153
|
-
if (!size || !pixelSize) return null;
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
pixelX: size.width <= 1
|
|
157
|
-
? 0
|
|
158
|
-
: clamp(
|
|
159
|
-
(cellX / Math.max(size.width - 1, 1)) * Math.max(pixelSize.pixelWidth - 1, 0),
|
|
160
|
-
0,
|
|
161
|
-
Math.max(pixelSize.pixelWidth - 1, 0),
|
|
162
|
-
),
|
|
163
|
-
pixelY: size.height <= 1
|
|
164
|
-
? 0
|
|
165
|
-
: clamp(
|
|
166
|
-
(cellY / Math.max(size.height - 1, 1)) * Math.max(pixelSize.pixelHeight - 1, 0),
|
|
167
|
-
0,
|
|
168
|
-
Math.max(pixelSize.pixelHeight - 1, 0),
|
|
169
|
-
),
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
93
|
export function scaleLocalPixelCoordinate(value: number | null, sourceExtent: number, targetExtent: number): number | null {
|
|
174
94
|
if (value === null) return null;
|
|
175
95
|
if (targetExtent <= 1) return 0;
|
|
@@ -183,55 +103,6 @@ export function scaleLocalPixelCoordinate(value: number | null, sourceExtent: nu
|
|
|
183
103
|
);
|
|
184
104
|
}
|
|
185
105
|
|
|
186
|
-
export function toCursorPosition(x: number | null, y: number | null) {
|
|
187
|
-
return { x, y };
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export function sameDisplayCursorState(
|
|
191
|
-
left: DisplayCursorState,
|
|
192
|
-
right: DisplayCursorState,
|
|
193
|
-
cellEpsilon = 0,
|
|
194
|
-
pixelEpsilon = 0,
|
|
195
|
-
): boolean {
|
|
196
|
-
return sameCursorPosition(toCursorPosition(left.cellX, left.cellY), toCursorPosition(right.cellX, right.cellY), cellEpsilon)
|
|
197
|
-
&& sameCursorPosition(toCursorPosition(left.pixelX, left.pixelY), toCursorPosition(right.pixelX, right.pixelY), pixelEpsilon);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export function buildDisplayCursorState(
|
|
201
|
-
cellX: number | null,
|
|
202
|
-
cellY: number | null,
|
|
203
|
-
renderable: RenderableSizeLike | null,
|
|
204
|
-
renderer: RendererMetricsHost,
|
|
205
|
-
pixelX: number | null = null,
|
|
206
|
-
pixelY: number | null = null,
|
|
207
|
-
): DisplayCursorState {
|
|
208
|
-
if (cellX === null || cellY === null) return EMPTY_DISPLAY_CURSOR;
|
|
209
|
-
if (pixelX !== null && pixelY !== null) {
|
|
210
|
-
return { cellX, cellY, pixelX, pixelY };
|
|
211
|
-
}
|
|
212
|
-
const derivedPixels = projectCellCursorToLocalPixels(cellX, cellY, renderable, renderer);
|
|
213
|
-
return {
|
|
214
|
-
cellX,
|
|
215
|
-
cellY,
|
|
216
|
-
pixelX: derivedPixels?.pixelX ?? null,
|
|
217
|
-
pixelY: derivedPixels?.pixelY ?? null,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export function resolveSelectionDisplayCursorState(
|
|
222
|
-
cursorX: number | null,
|
|
223
|
-
cursorY: number | null,
|
|
224
|
-
fallbackCursorX: number | null,
|
|
225
|
-
fallbackCursorY: number | null,
|
|
226
|
-
renderable: RenderableSizeLike | null,
|
|
227
|
-
renderer: RendererMetricsHost,
|
|
228
|
-
): DisplayCursorState {
|
|
229
|
-
const resolvedCursorX = cursorY === null ? (fallbackCursorX ?? cursorX) : cursorX;
|
|
230
|
-
const resolvedCursorY = cursorY ?? fallbackCursorY;
|
|
231
|
-
if (resolvedCursorX === null) return EMPTY_DISPLAY_CURSOR;
|
|
232
|
-
return buildDisplayCursorState(resolvedCursorX, resolvedCursorY, renderable, renderer);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
106
|
export function getLocalPlotPointer(
|
|
236
107
|
event: ChartMouseEvent,
|
|
237
108
|
renderable: PlotRenderableLike | null,
|
|
@@ -305,16 +176,3 @@ export function getGlobalMouseX(
|
|
|
305
176
|
if (event.pixelX === undefined || !metrics) return event.x;
|
|
306
177
|
return event.pixelX / metrics.cellWidth;
|
|
307
178
|
}
|
|
308
|
-
|
|
309
|
-
export function resolveCursorMotionKind(
|
|
310
|
-
event: ChartMouseEvent,
|
|
311
|
-
renderer: RendererMetricsHost,
|
|
312
|
-
): ChartCursorMotionKind {
|
|
313
|
-
return event.pixelX !== undefined && event.pixelY !== undefined && getRendererCellMetrics(renderer)
|
|
314
|
-
? "pixel"
|
|
315
|
-
: "cell";
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
export function buildBlankPlotLines(width: number, height: number): string[] {
|
|
319
|
-
return Array.from({ length: height }, () => " ".repeat(width));
|
|
320
|
-
}
|