gloomberb 0.5.0 → 0.6.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 +91 -11
- package/package.json +12 -5
- package/src/app.tsx +414 -191
- package/src/architecture/import-boundaries.test.ts +118 -0
- package/src/assets/gloomberb-logo.png +0 -0
- package/src/assets/gloomberb-logo.svg +73 -0
- package/src/brokers/account-cache.ts +157 -0
- package/src/brokers/profile-form.test.ts +73 -0
- package/src/brokers/profile-form.ts +109 -0
- package/src/brokers/remote-broker-adapter.ts +91 -0
- package/src/brokers/sync-broker-instance.test.ts +124 -1
- package/src/brokers/sync-broker-instance.ts +113 -17
- package/src/capabilities/factories.ts +100 -0
- package/src/capabilities/index.ts +4 -0
- package/src/capabilities/registry.test.ts +137 -0
- package/src/capabilities/registry.ts +131 -0
- package/src/capabilities/service-capabilities.ts +27 -0
- package/src/capabilities/types.ts +84 -0
- package/src/cli/commands/ticker.ts +11 -3
- package/src/cli/context.ts +19 -13
- package/src/cli/helpers.ts +1 -0
- package/src/cli/types.ts +2 -2
- package/src/components/chart/chart-controller.ts +4 -24
- package/src/components/chart/chart-data.ts +3 -14
- package/src/components/chart/chart-indicator-selector.tsx +28 -6
- package/src/components/chart/chart-pane-settings.ts +15 -28
- package/src/components/chart/chart-renderer.ts +74 -99
- package/src/components/chart/chart-resolution.ts +10 -10
- package/src/components/chart/chart-scroll.test.ts +6 -5
- package/src/components/chart/chart-scroll.ts +2 -2
- package/src/components/chart/chart-types.ts +0 -1
- package/src/components/chart/chart-viewport.ts +2 -1
- package/src/components/chart/chart.test.ts +37 -0
- package/src/components/chart/comparison-chart-data.ts +3 -17
- package/src/components/chart/comparison-chart-renderer.test.ts +27 -4
- package/src/components/chart/comparison-chart-renderer.ts +1 -1
- package/src/components/chart/comparison-stock-chart.tsx +304 -108
- package/src/components/chart/cursor-motion.ts +3 -2
- package/src/components/chart/indicators/bands.test.ts +0 -14
- package/src/components/chart/indicators/moving-averages.test.ts +0 -15
- package/src/components/chart/indicators/options.ts +3 -3
- package/src/components/chart/indicators/oscillators.test.ts +0 -21
- package/src/components/chart/indicators/types.ts +1 -2
- package/src/components/chart/native/chart-rasterizer.ts +37 -44
- package/src/components/chart/native/kitty-adapter.ts +2 -5
- package/src/components/chart/native/kitty-manager.ts +1 -1
- package/src/components/chart/native/kitty-protocol.ts +0 -1
- package/src/components/chart/native/kitty-support.ts +1 -1
- package/src/components/chart/native/kitty.test.ts +30 -13
- package/src/components/chart/native/renderer-selection.ts +1 -1
- package/src/components/chart/native/surface-manager.ts +1 -1
- package/src/components/chart/static-chart-surface.test.tsx +51 -0
- package/src/components/chart/static-chart-surface.tsx +191 -0
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +242 -35
- package/src/components/chart/stock-chart.tsx +438 -141
- package/src/components/command-bar/__snapshots__/command-bar.test.tsx.snap +4 -4
- package/src/components/command-bar/command-bar.test.tsx +269 -20
- package/src/components/command-bar/command-bar.tsx +830 -353
- package/src/components/command-bar/command-registry.ts +10 -20
- package/src/components/command-bar/helpers.test.ts +1 -1
- package/src/components/command-bar/helpers.ts +9 -11
- package/src/components/command-bar/pane-template-display.ts +4 -8
- package/src/components/command-bar/root-shortcuts.ts +12 -11
- package/src/components/command-bar/view-model.test.ts +8 -42
- package/src/components/command-bar/view-model.ts +8 -36
- package/src/components/command-bar/workflow-ops.test.ts +76 -2
- package/src/components/command-bar/workflow-ops.ts +40 -9
- package/src/components/command-bar/workflow-types.ts +2 -1
- package/src/components/data-table-stack-view.test.tsx +206 -0
- package/src/components/data-table-stack-view.tsx +152 -0
- package/src/components/data-table-view.test.tsx +188 -0
- package/src/components/data-table-view.tsx +279 -0
- package/src/components/feed-data-table-stack-view.test.tsx +225 -0
- package/src/components/feed-data-table-stack-view.tsx +365 -0
- package/src/components/index.ts +26 -31
- package/src/components/layout/detached-pane-shell.tsx +185 -0
- package/src/components/layout/floating-pane.tsx +87 -13
- package/src/components/layout/header.tsx +139 -49
- package/src/components/layout/pane-content.tsx +40 -0
- package/src/components/layout/pane-footer.test.tsx +195 -0
- package/src/components/layout/pane-footer.tsx +410 -0
- package/src/components/layout/pane-header.test.tsx +39 -0
- package/src/components/layout/pane-header.tsx +192 -14
- package/src/components/layout/pane-sizing.ts +13 -4
- package/src/components/layout/pane-title.ts +35 -0
- package/src/components/layout/pane.tsx +56 -8
- package/src/components/layout/shell.test.tsx +322 -19
- package/src/components/layout/shell.tsx +1010 -366
- package/src/components/layout/status-bar.test.tsx +17 -10
- package/src/components/layout/status-bar.tsx +367 -100
- package/src/components/layout/titlebar-overlay.ts +2 -0
- package/src/components/markdown-editor.tsx +29 -14
- package/src/components/markdown-text.tsx +25 -24
- package/src/components/onboarding/onboarding-wizard.test.tsx +8 -3
- package/src/components/onboarding/onboarding-wizard.tsx +347 -473
- package/src/components/pane-settings-dialog.tsx +521 -89
- package/src/components/pane-template-wizard.test.tsx +1 -1
- package/src/components/pane-template-wizard.tsx +38 -37
- package/src/components/price-selector-dialog.tsx +22 -21
- package/src/components/ticker-badge-list.tsx +53 -0
- package/src/components/ticker-badge-text.test.tsx +44 -1
- package/src/components/ticker-badge-text.tsx +78 -38
- package/src/components/ticker-badge.tsx +28 -7
- package/src/components/ticker-list-table-view.tsx +248 -0
- package/src/components/ticker-list-table.test.tsx +47 -1
- package/src/components/ticker-list-table.tsx +212 -41
- package/src/components/toggle-list.tsx +114 -8
- package/src/components/ui/button.tsx +24 -16
- package/src/components/ui/choice-dialog.tsx +89 -0
- package/src/components/ui/data-table.tsx +313 -114
- package/src/components/ui/external-link.tsx +52 -13
- package/src/components/ui/fields.tsx +44 -30
- package/src/components/ui/frame.tsx +24 -57
- package/src/components/ui/index.ts +15 -50
- package/src/components/ui/list-view.tsx +65 -25
- package/src/components/ui/loading.tsx +5 -60
- package/src/components/ui/message-composer.test.tsx +81 -0
- package/src/components/ui/message-composer.tsx +139 -0
- package/src/components/ui/multi-select-dialog.tsx +100 -32
- package/src/components/ui/multi-select.ts +112 -0
- package/src/components/ui/native-select.tsx +120 -0
- package/src/components/ui/page-stack-view.tsx +49 -19
- package/src/components/ui/remote-image.tsx +75 -0
- package/src/components/ui/row-value-cache.test.ts +17 -0
- package/src/components/ui/row-value-cache.ts +41 -0
- package/src/components/ui/shortcut-hint.tsx +55 -0
- package/src/components/ui/status.tsx +12 -67
- package/src/components/ui/table-layout.ts +59 -0
- package/src/components/ui/tabs.tsx +249 -27
- package/src/components/ui/toggle.tsx +19 -127
- package/src/components/ui/ui.test.tsx +266 -53
- package/src/components/use-double-click-activation.ts +7 -2
- package/src/components/use-external-link-footer.ts +79 -0
- package/src/core/app-services.ts +95 -0
- package/src/core/state/app-state.ts +886 -0
- package/src/{state → core/state}/session-persistence.ts +7 -6
- package/src/data/config-layout.ts +225 -0
- package/src/data/config-store-node.ts +390 -0
- package/src/data/config-store.test.ts +72 -0
- package/src/data/config-store.ts +35 -478
- package/src/data/sqlite-json.ts +2 -5
- package/src/data/sqlite-retry.ts +1 -0
- package/src/index.tsx +2 -68
- package/src/market-data/coordinator-subscriptions.test.ts +125 -0
- package/src/market-data/coordinator.test.ts +73 -4
- package/src/market-data/coordinator.ts +190 -24
- package/src/market-data/hooks.test.tsx +1 -1
- package/src/market-data/hooks.tsx +109 -87
- package/src/market-data/query-store.ts +3 -3
- package/src/market-data/request-types.test.ts +0 -14
- package/src/market-data/result-types.ts +1 -0
- package/src/news/aggregator.test.ts +267 -0
- package/src/news/aggregator.ts +452 -0
- package/src/news/hooks.ts +39 -0
- package/src/news/ticker-symbols.ts +21 -0
- package/src/news/types.ts +77 -0
- package/src/notifications/app-notifier.ts +21 -10
- package/src/pane-settings.test.ts +0 -2
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +124 -134
- package/src/plugins/builtin/ai/gloomberb-cli.ts +14 -13
- package/src/plugins/builtin/ai/providers.ts +1 -9
- package/src/plugins/builtin/ai/runner.ts +41 -5
- package/src/plugins/builtin/ai/screener-pane.test.tsx +37 -55
- package/src/plugins/builtin/ai/screener-pane.tsx +231 -276
- package/src/plugins/builtin/ai/settings.ts +0 -1
- package/src/plugins/builtin/ai/utils.ts +1 -71
- package/src/plugins/builtin/alerts/alert-engine.test.ts +1 -38
- package/src/plugins/builtin/alerts/index.test.tsx +61 -82
- package/src/plugins/builtin/alerts/index.tsx +263 -155
- package/src/plugins/builtin/alerts/types.ts +7 -0
- package/src/plugins/builtin/analytics/index.test.tsx +13 -8
- package/src/plugins/builtin/analytics/index.tsx +78 -120
- package/src/plugins/builtin/analytics/metrics.test.ts +0 -4
- package/src/plugins/builtin/ask-ai.test.tsx +55 -54
- package/src/plugins/builtin/broker-manager/index.test.tsx +168 -0
- package/src/plugins/builtin/broker-manager/index.tsx +759 -0
- package/src/plugins/builtin/broker-manager/model.test.ts +107 -0
- package/src/plugins/builtin/broker-manager/model.ts +142 -0
- package/src/plugins/builtin/chat-controller.test.ts +150 -0
- package/src/plugins/builtin/chat-controller.ts +88 -16
- package/src/plugins/builtin/chat.test.tsx +283 -36
- package/src/plugins/builtin/chat.tsx +373 -172
- package/src/plugins/builtin/cloud-auth-actions.tsx +72 -0
- package/src/plugins/builtin/cloud-tweets.tsx +896 -0
- package/src/plugins/builtin/comparison-chart.test.tsx +46 -57
- package/src/plugins/builtin/comparison-chart.tsx +11 -11
- package/src/plugins/builtin/correlation/compute.test.ts +0 -17
- package/src/plugins/builtin/correlation/index.tsx +51 -46
- package/src/plugins/builtin/correlation/settings.ts +1 -2
- package/src/plugins/builtin/debug.tsx +99 -70
- package/src/plugins/builtin/earnings/earnings-cache.test.ts +2 -2
- package/src/plugins/builtin/earnings/earnings-cache.ts +12 -3
- package/src/plugins/builtin/earnings/earnings-data.test.ts +42 -11
- package/src/plugins/builtin/earnings/earnings-data.ts +70 -5
- package/src/plugins/builtin/earnings/index.tsx +187 -94
- package/src/plugins/builtin/econ/calendar-source.ts +9 -116
- package/src/plugins/builtin/econ/fred-client.test.ts +1 -14
- package/src/plugins/builtin/econ/fred-series-map.ts +1 -0
- package/src/plugins/builtin/econ/index.tsx +236 -421
- package/src/plugins/builtin/econ/types.ts +1 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +43 -45
- package/src/plugins/builtin/fx-matrix/pairs.ts +1 -0
- package/src/plugins/builtin/help.test.tsx +120 -60
- package/src/plugins/builtin/help.tsx +266 -82
- package/src/plugins/builtin/holders/index.test.ts +45 -0
- package/src/plugins/builtin/holders/index.tsx +949 -0
- package/src/plugins/builtin/insider/index.tsx +207 -253
- package/src/plugins/builtin/insider/insider-data.test.ts +1 -11
- package/src/plugins/builtin/insider/insider-data.ts +1 -0
- package/src/plugins/builtin/layout-manager.tsx +12 -40
- package/src/plugins/builtin/market-movers/index.tsx +70 -144
- package/src/plugins/builtin/market-movers/screener.test.ts +0 -18
- package/src/plugins/builtin/news-wire/breaking-notifications.test.ts +98 -0
- package/src/plugins/builtin/news-wire/breaking-notifications.ts +110 -0
- package/src/plugins/builtin/news-wire/breaking-pane.tsx +157 -0
- package/src/plugins/builtin/news-wire/categories.ts +114 -0
- package/src/plugins/builtin/news-wire/default-feeds.ts +3 -0
- package/src/plugins/builtin/news-wire/digest-store.ts +44 -0
- package/src/plugins/builtin/news-wire/feed-config.test.ts +90 -0
- package/src/plugins/builtin/news-wire/feed-config.ts +216 -0
- package/src/plugins/builtin/news-wire/feed-pane.tsx +21 -0
- package/src/plugins/builtin/news-wire/hash.ts +9 -0
- package/src/plugins/builtin/news-wire/index.tsx +92 -0
- package/src/plugins/builtin/news-wire/industry-pane.tsx +140 -0
- package/src/plugins/builtin/news-wire/news-detail-view.tsx +256 -0
- package/src/plugins/builtin/news-wire/news-footer.ts +28 -0
- package/src/plugins/builtin/news-wire/news-preset-pane.tsx +94 -0
- package/src/plugins/builtin/news-wire/news-query-presets.ts +40 -0
- package/src/plugins/builtin/news-wire/news-table.test.tsx +206 -0
- package/src/plugins/builtin/news-wire/news-table.tsx +296 -0
- package/src/plugins/builtin/news-wire/persisted-articles.ts +92 -0
- package/src/plugins/builtin/news-wire/read-state.test.ts +30 -0
- package/src/plugins/builtin/news-wire/read-state.ts +76 -0
- package/src/plugins/builtin/news-wire/rss-parser.test.ts +184 -0
- package/src/plugins/builtin/news-wire/rss-parser.ts +213 -0
- package/src/plugins/builtin/news-wire/rss-source.test.ts +159 -0
- package/src/plugins/builtin/news-wire/rss-source.ts +181 -0
- package/src/plugins/builtin/news-wire/top-pane.tsx +21 -0
- package/src/plugins/builtin/news.tsx +98 -25
- package/src/plugins/builtin/notes-files.ts +51 -10
- package/src/plugins/builtin/notes.tsx +332 -131
- package/src/plugins/builtin/options.test.tsx +185 -0
- package/src/plugins/builtin/options.tsx +346 -206
- package/src/plugins/builtin/plugin-groups.ts +92 -0
- package/src/plugins/builtin/portfolio-list/header.tsx +72 -37
- package/src/plugins/builtin/portfolio-list/index.tsx +25 -42
- package/src/plugins/builtin/portfolio-list/metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane.tsx +246 -164
- package/src/plugins/builtin/portfolio-list/settings.ts +19 -51
- package/src/plugins/builtin/portfolio-list/summary.tsx +21 -16
- package/src/plugins/builtin/portfolio-list/table.tsx +66 -25
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.test.tsx +1 -1
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.ts +1 -1
- package/src/plugins/builtin/portfolio-list.test.ts +60 -1
- package/src/plugins/builtin/portfolio-list.test.tsx +122 -55
- package/src/plugins/builtin/quote-monitor.test.tsx +2 -27
- package/src/plugins/builtin/research/index.tsx +724 -0
- package/src/plugins/builtin/sec.tsx +67 -19
- package/src/plugins/builtin/sectors/index.tsx +42 -80
- package/src/plugins/builtin/sectors/sector-data.ts +1 -0
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +25 -39
- package/src/plugins/builtin/ticker-detail/financials-tab.tsx +108 -58
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +383 -271
- package/src/plugins/builtin/ticker-detail/pane.tsx +135 -49
- package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +43 -27
- package/src/plugins/builtin/ticker-detail/settings.ts +3 -6
- package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +16 -10
- package/src/plugins/builtin/ticker-detail.test.tsx +156 -33
- package/src/plugins/builtin/ticker-surface.ts +68 -0
- package/src/plugins/builtin/world-indices/index.tsx +44 -82
- package/src/plugins/builtin/world-indices/indices.ts +1 -0
- package/src/plugins/builtin/yahoo.ts +55 -1
- package/src/plugins/builtin/yield-curve/index.tsx +87 -145
- package/src/plugins/builtin/yield-curve/treasury-data.ts +6 -77
- package/src/plugins/catalog-ui.test.ts +24 -0
- package/src/plugins/catalog-ui.ts +44 -0
- package/src/plugins/catalog.ts +2 -50
- package/src/plugins/ibkr/__snapshots__/trade-tab.test.tsx.snap +5 -5
- package/src/plugins/ibkr/account-cache.test.ts +15 -14
- package/src/plugins/ibkr/account-cache.ts +15 -120
- package/src/plugins/ibkr/broker-adapter.ts +67 -0
- package/src/plugins/ibkr/config.test.ts +0 -18
- package/src/plugins/ibkr/dialogs.tsx +14 -56
- package/src/plugins/ibkr/flex.test.ts +57 -2
- package/src/plugins/ibkr/flex.ts +116 -12
- package/src/plugins/ibkr/gateway-helpers.ts +8 -3
- package/src/plugins/ibkr/gateway-service-native.ts +2381 -0
- package/src/plugins/ibkr/gateway-service.test.ts +47 -9
- package/src/plugins/ibkr/gateway-service.ts +264 -2235
- package/src/plugins/ibkr/gateway-types.ts +27 -0
- package/src/plugins/ibkr/index.test.tsx +3 -11
- package/src/plugins/ibkr/index.tsx +1 -0
- package/src/plugins/ibkr/price-normalization.test.ts +1 -6
- package/src/plugins/ibkr/price-normalization.ts +1 -0
- package/src/plugins/ibkr/trade-badge.tsx +6 -5
- package/src/plugins/ibkr/trade-tab.test.tsx +2 -2
- package/src/plugins/ibkr/trade-tab.tsx +164 -117
- package/src/plugins/ibkr/trading-pane.tsx +117 -83
- package/src/plugins/pane-manager.test.ts +3 -1
- package/src/plugins/pane-manager.ts +93 -19
- package/src/plugins/plugin-runtime.test.tsx +176 -74
- package/src/plugins/plugin-runtime.tsx +228 -10
- package/src/plugins/prediction-markets/cache.ts +49 -0
- package/src/plugins/prediction-markets/categories.ts +0 -10
- package/src/plugins/prediction-markets/chart.test.tsx +1 -1
- package/src/plugins/prediction-markets/chart.tsx +21 -20
- package/src/plugins/prediction-markets/cli.ts +1 -0
- package/src/plugins/prediction-markets/controller-data.ts +135 -89
- package/src/plugins/prediction-markets/controller.ts +37 -108
- package/src/plugins/prediction-markets/detail/book.tsx +23 -22
- package/src/plugins/prediction-markets/detail/outcomes.tsx +33 -32
- package/src/plugins/prediction-markets/detail/overview.tsx +17 -12
- package/src/plugins/prediction-markets/detail/pane.tsx +59 -45
- package/src/plugins/prediction-markets/detail/rules.tsx +22 -7
- package/src/plugins/prediction-markets/detail/shared.tsx +3 -2
- package/src/plugins/prediction-markets/detail/trades.tsx +12 -11
- package/src/plugins/prediction-markets/detail.test.tsx +1 -1
- package/src/plugins/prediction-markets/keyboard.ts +1 -0
- package/src/plugins/prediction-markets/launch.test.ts +1 -1
- package/src/plugins/prediction-markets/launch.ts +1 -1
- package/src/plugins/prediction-markets/metrics.ts +25 -12
- package/src/plugins/prediction-markets/pane.test.tsx +16 -7
- package/src/plugins/prediction-markets/pane.tsx +180 -82
- package/src/plugins/prediction-markets/plugin.test.tsx +37 -45
- package/src/plugins/prediction-markets/services/fetch.ts +27 -10
- package/src/plugins/prediction-markets/services/kalshi-adapter.ts +20 -3
- package/src/plugins/prediction-markets/services/polymarket-adapter.ts +29 -11
- package/src/plugins/prediction-markets/settings.ts +0 -5
- package/src/plugins/prediction-markets/test-helpers.tsx +19 -44
- package/src/plugins/registry.test.ts +307 -0
- package/src/plugins/registry.ts +293 -82
- package/src/react/input.ts +53 -0
- package/src/react/plugins/plugin-slot.tsx +21 -0
- package/src/react/use-raf-callback.ts +44 -0
- package/src/renderers/electrobun/bun/application-menu-click.test.ts +41 -0
- package/src/renderers/electrobun/bun/application-menu-click.ts +55 -0
- package/src/renderers/electrobun/bun/application-menu.ts +118 -0
- package/src/renderers/electrobun/bun/context-menu-click.test.ts +67 -0
- package/src/renderers/electrobun/bun/context-menu-click.ts +61 -0
- package/src/renderers/electrobun/bun/context-menu-normalize.test.ts +52 -0
- package/src/renderers/electrobun/bun/context-menu-normalize.ts +67 -0
- package/src/renderers/electrobun/bun/core-capabilities.ts +328 -0
- package/src/renderers/electrobun/bun/desktop-workspace.test.ts +63 -0
- package/src/renderers/electrobun/bun/desktop-workspace.ts +162 -0
- package/src/renderers/electrobun/bun/index.ts +1149 -0
- package/src/renderers/electrobun/bun/window-frame.test.ts +40 -0
- package/src/renderers/electrobun/bun/window-frame.ts +46 -0
- package/src/renderers/electrobun/shared/protocol.ts +72 -0
- package/src/renderers/electrobun/view/ai-host.ts +93 -0
- package/src/renderers/electrobun/view/app-services.ts +348 -0
- package/src/renderers/electrobun/view/application-menu-bridge.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +173 -0
- package/src/renderers/electrobun/view/broker-remote-client.ts +154 -0
- package/src/renderers/electrobun/view/config-host.ts +35 -0
- package/src/renderers/electrobun/view/context-menu.test.ts +140 -0
- package/src/renderers/electrobun/view/context-menu.ts +164 -0
- package/src/renderers/electrobun/view/data-table.tsx +773 -0
- package/src/renderers/electrobun/view/desktop-controls.tsx +651 -0
- package/src/renderers/electrobun/view/desktop-window-bridge.ts +59 -0
- package/src/renderers/electrobun/view/desktop-window-snapshot.test.ts +57 -0
- package/src/renderers/electrobun/view/desktop-window-snapshot.ts +41 -0
- package/src/renderers/electrobun/view/dialog-host.tsx +62 -0
- package/src/renderers/electrobun/view/http-fetch.ts +113 -0
- package/src/renderers/electrobun/view/input-host.tsx +65 -0
- package/src/renderers/electrobun/view/key-event.test.ts +34 -0
- package/src/renderers/electrobun/view/key-event.ts +134 -0
- package/src/renderers/electrobun/view/main.tsx +104 -0
- package/src/renderers/electrobun/view/native-renderer.ts +110 -0
- package/src/renderers/electrobun/view/native-stubs/chart-kitty-support.ts +9 -0
- package/src/renderers/electrobun/view/native-stubs/chart-surface-manager.ts +25 -0
- package/src/renderers/electrobun/view/native-stubs/chart-surface-sync.ts +1 -0
- package/src/renderers/electrobun/view/notes-files.ts +56 -0
- package/src/renderers/electrobun/view/resource-store.test.ts +44 -0
- package/src/renderers/electrobun/view/resource-store.ts +106 -0
- package/src/renderers/electrobun/view/rpc-codec.ts +42 -0
- package/src/renderers/electrobun/view/scrollbar-activity.ts +23 -0
- package/src/renderers/electrobun/view/toast-host.tsx +65 -0
- package/src/renderers/electrobun/view/ui-host.tsx +1490 -0
- package/src/renderers/electrobun/view/update-host.ts +38 -0
- package/src/renderers/opentui/dialog-host.test.tsx +44 -0
- package/src/renderers/opentui/dialog-host.tsx +56 -0
- package/src/renderers/opentui/host.tsx +134 -0
- package/src/renderers/opentui/input-host.tsx +24 -0
- package/src/renderers/opentui/start.tsx +71 -0
- package/src/renderers/opentui/test-utils.tsx +76 -0
- package/src/renderers/opentui/toast-host.tsx +10 -0
- package/src/renderers/opentui/ui-host.tsx +97 -0
- package/src/sources/gloomberb-cloud.test.ts +288 -17
- package/src/sources/gloomberb-cloud.ts +176 -9
- package/src/sources/provider-errors.ts +1 -0
- package/src/sources/provider-router.test.ts +220 -81
- package/src/sources/provider-router.ts +294 -111
- package/src/sources/sec-edgar.ts +19 -6
- package/src/sources/yahoo-finance.test.ts +0 -5
- package/src/sources/yahoo-finance.ts +420 -11
- package/src/state/app-activity.ts +7 -7
- package/src/state/app-bootstrap.ts +114 -40
- package/src/state/app-context-selector.test.tsx +70 -1
- package/src/state/app-context.test.ts +71 -3
- package/src/state/app-context.test.tsx +14 -7
- package/src/state/app-context.tsx +205 -869
- package/src/state/config-save-scheduler.ts +35 -0
- package/src/state/persist-scheduler.test.ts +67 -0
- package/src/state/persist-scheduler.ts +69 -0
- package/src/state/selectors-ui.ts +55 -0
- package/src/state/selectors.ts +15 -71
- package/src/state/session-persistence.test.ts +1 -1
- package/src/state/ticker-refresh-queue.ts +52 -3
- package/src/state/use-inline-tickers.ts +18 -16
- package/src/state/use-quote-streaming.test.ts +0 -6
- package/src/state/use-quote-streaming.test.tsx +1 -1
- package/src/state/use-quote-streaming.ts +3 -2
- package/src/state/use-session-snapshot.ts +32 -17
- package/src/test-support/plugin-runtime.ts +149 -0
- package/src/theme/color-utils.ts +1 -0
- package/src/theme/colors.ts +16 -0
- package/src/types/broker.ts +16 -1
- package/src/types/capability-route-source.ts +23 -0
- package/src/types/config.test.ts +23 -0
- package/src/types/config.ts +145 -10
- package/src/types/context-menu.ts +95 -0
- package/src/types/data-provider.ts +34 -3
- package/src/types/desktop-menu.ts +13 -0
- package/src/types/desktop-window.ts +29 -0
- package/src/types/financials.ts +123 -2
- package/src/types/instrument.ts +1 -0
- package/src/types/news-source.ts +9 -0
- package/src/types/persistence.ts +1 -0
- package/src/types/plugin.ts +32 -8
- package/src/ui/ascii-font.ts +85 -0
- package/src/ui/context-menu.test.ts +94 -0
- package/src/ui/context-menu.tsx +433 -0
- package/src/ui/dialog.tsx +55 -0
- package/src/ui/host.tsx +361 -0
- package/src/ui/index.tsx +126 -0
- package/src/ui/toast.tsx +40 -0
- package/src/updater.test.ts +69 -0
- package/src/updater.ts +61 -18
- package/src/utils/api-client.test.ts +186 -6
- package/src/utils/api-client.ts +433 -33
- package/src/utils/back-navigation.ts +6 -0
- package/src/utils/cli-output.ts +1 -0
- package/src/utils/currency-units.test.ts +1 -6
- package/src/utils/currency-units.ts +1 -0
- package/src/utils/debug-log.ts +68 -0
- package/src/utils/dom-screenshot.ts +326 -0
- package/src/utils/exchange-rate-map.ts +1 -0
- package/src/utils/exchanges.ts +214 -0
- package/src/utils/format.test.ts +7 -0
- package/src/utils/format.ts +1 -0
- package/src/utils/fuzzy-search.ts +1 -0
- package/src/utils/html-entities.test.ts +8 -0
- package/src/utils/html-entities.ts +32 -0
- package/src/utils/http-transport.ts +11 -0
- package/src/utils/main-thread-monitor.ts +127 -0
- package/src/utils/perf-marks.ts +54 -0
- package/src/utils/quote-freshness.ts +27 -50
- package/src/utils/quote-resolution.test.ts +5 -5
- package/src/utils/quote-resolution.ts +0 -1
- package/src/utils/quote-time.ts +1 -5
- package/src/utils/sec.test.ts +0 -12
- package/src/utils/selection-clipboard.ts +14 -6
- package/src/utils/shortcut-labels.test.ts +20 -0
- package/src/utils/shortcut-labels.ts +67 -0
- package/src/utils/slugify.ts +1 -0
- package/src/utils/terminal-input-reset.ts +1 -0
- package/src/utils/text-wrap.ts +82 -0
- package/src/utils/throttled-fetch.test.ts +19 -0
- package/src/utils/throttled-fetch.ts +11 -9
- package/src/utils/ticker-list.test.ts +1 -7
- package/src/utils/ticker-list.ts +1 -0
- package/src/utils/timestamp.ts +29 -0
- package/src/utils/tweet-text.test.ts +13 -0
- package/src/utils/tweet-text.ts +18 -0
- package/src/utils/url.ts +11 -0
- package/src/version.ts +1 -1
- package/src/components/chart/chart-control-hint.tsx +0 -39
- package/src/components/detail-feed-view.test.tsx +0 -180
- package/src/components/detail-feed-view.tsx +0 -531
- package/src/components/layout/pixel-mouse-mode.test.ts +0 -122
- package/src/components/layout/pixel-mouse.test.ts +0 -40
- package/src/components/spinner.tsx +0 -2
- package/src/components/tab-bar.tsx +0 -17
- package/src/components/ui/multi-select-chips.tsx +0 -69
- package/src/plugins/builtin/ai/index.test.tsx +0 -139
- package/src/plugins/builtin/ask-ai.tsx +0 -9
- package/src/plugins/builtin/correlation/plugin.test.ts +0 -44
- package/src/plugins/builtin/correlation/settings.test.ts +0 -34
- package/src/plugins/builtin/econ/calendar-source.test.ts +0 -219
- package/src/plugins/builtin/econ/fred-client.ts +0 -79
- package/src/plugins/builtin/fred-settings.ts +0 -10
- package/src/plugins/builtin/options-availability.test.tsx +0 -179
- package/src/plugins/builtin/options-availability.ts +0 -158
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +0 -61
- package/src/plugins/builtin/yield-curve/treasury-data.test.ts +0 -212
- package/src/plugins/prediction-markets/status.ts +0 -24
- package/src/plugins/prediction-markets/table.tsx +0 -87
- package/src/utils/quote-time.test.ts +0 -22
- package/src/utils/selection-clipboard.test.ts +0 -43
- package/src/utils/terminal-input-reset.test.ts +0 -50
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { Box, ChartSurface, Text } from "../../ui";
|
|
1
2
|
import { memo, type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { TextAttributes, type BoxRenderable, type CliRenderer } from "
|
|
3
|
-
import {
|
|
3
|
+
import { TextAttributes, type BoxRenderable, type NativeRendererHost as CliRenderer } from "../../ui";
|
|
4
|
+
import { useNativeRenderer, useUiCapabilities } from "../../ui";
|
|
5
|
+
import { useShortcut } from "../../react/input";
|
|
4
6
|
import { useAppDispatch, useAppSelector, usePaneInstance, usePaneInstanceId, usePaneSettingValue, usePaneTicker } from "../../state/app-context";
|
|
7
|
+
import { usePaneFooter, type PaneFooterSegment, type PaneHint } from "../layout/pane-footer";
|
|
8
|
+
import { ShortcutHint } from "../ui";
|
|
5
9
|
import { saveConfig } from "../../data/config-store";
|
|
6
|
-
import {
|
|
10
|
+
import { getSharedMarketData } from "../../plugins/registry";
|
|
7
11
|
import { colors } from "../../theme/colors";
|
|
8
12
|
import { formatCompact } from "../../utils/format";
|
|
9
13
|
import { formatMarketPriceWithCurrency } from "../../utils/market-format";
|
|
@@ -79,7 +83,6 @@ import {
|
|
|
79
83
|
import {
|
|
80
84
|
buildChartScene,
|
|
81
85
|
formatAxisCell,
|
|
82
|
-
formatDateShort,
|
|
83
86
|
formatCursorAxisValue,
|
|
84
87
|
getActivePointIndex,
|
|
85
88
|
getPointTerminalColumn,
|
|
@@ -105,7 +108,6 @@ import {
|
|
|
105
108
|
type ChartViewState,
|
|
106
109
|
type ResolvedChartRenderer,
|
|
107
110
|
} from "./chart-types";
|
|
108
|
-
import { ChartControlHint } from "./chart-control-hint";
|
|
109
111
|
import {
|
|
110
112
|
computeBitmapSize,
|
|
111
113
|
intersectCellRects,
|
|
@@ -155,6 +157,7 @@ interface StockChartProps {
|
|
|
155
157
|
height: number;
|
|
156
158
|
focused: boolean;
|
|
157
159
|
interactive?: boolean;
|
|
160
|
+
onActivate?: () => void;
|
|
158
161
|
compact?: boolean;
|
|
159
162
|
axisMode?: ChartAxisMode;
|
|
160
163
|
historyOverride?: PricePoint[] | null;
|
|
@@ -162,6 +165,7 @@ interface StockChartProps {
|
|
|
162
165
|
indicatorConfig?: IndicatorConfig;
|
|
163
166
|
showVolume?: boolean;
|
|
164
167
|
footerControls?: ReactNode;
|
|
168
|
+
footerHints?: PaneHint[];
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
interface ResolvedStockChartProps extends StockChartProps {
|
|
@@ -1204,7 +1208,7 @@ const INDICATOR_COLORS = [
|
|
|
1204
1208
|
"#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEAA7", "#DDA0DD", "#98D8C8",
|
|
1205
1209
|
];
|
|
1206
1210
|
|
|
1207
|
-
|
|
1211
|
+
function getIndicatorWarmupPeriod(config: IndicatorConfig): number {
|
|
1208
1212
|
const periods = [
|
|
1209
1213
|
...(config.sma ?? []),
|
|
1210
1214
|
...(config.ema ?? []),
|
|
@@ -1354,7 +1358,7 @@ export function computeProjectedIndicatorOverlays(
|
|
|
1354
1358
|
return reindexIndicatorOverlaysForProjection(overlays, sourcePoints, projectedPoints);
|
|
1355
1359
|
}
|
|
1356
1360
|
|
|
1357
|
-
|
|
1361
|
+
function reindexIndicatorOverlaysForProjection(
|
|
1358
1362
|
overlays: ChartIndicatorOverlays,
|
|
1359
1363
|
sourcePoints: readonly PricePoint[],
|
|
1360
1364
|
projectedPoints: readonly ProjectedChartPoint[],
|
|
@@ -1474,6 +1478,7 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
1474
1478
|
height,
|
|
1475
1479
|
focused,
|
|
1476
1480
|
interactive,
|
|
1481
|
+
onActivate,
|
|
1477
1482
|
compact,
|
|
1478
1483
|
axisMode = "price",
|
|
1479
1484
|
historyOverride = null,
|
|
@@ -1481,11 +1486,13 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
1481
1486
|
indicatorConfig: indicatorConfigOverride,
|
|
1482
1487
|
showVolume: showVolumeOverride,
|
|
1483
1488
|
footerControls,
|
|
1489
|
+
footerHints,
|
|
1484
1490
|
symbol,
|
|
1485
1491
|
ticker,
|
|
1486
1492
|
financials,
|
|
1487
1493
|
}: ResolvedStockChartProps) {
|
|
1488
|
-
const renderer =
|
|
1494
|
+
const renderer = useNativeRenderer();
|
|
1495
|
+
const { canvasCharts, cellWidthPx = 8, cellHeightPx = 18, pixelRatio = 1 } = useUiCapabilities();
|
|
1489
1496
|
const dispatch = useAppDispatch();
|
|
1490
1497
|
const config = useAppSelector((state) => state.config);
|
|
1491
1498
|
const paneId = usePaneInstanceId();
|
|
@@ -1523,7 +1530,12 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
1523
1530
|
const [lastReadyRenderView, setLastReadyRenderView] = useState<CachedRenderedView | null>(null);
|
|
1524
1531
|
const showVolume = showVolumeOverride ?? !compact;
|
|
1525
1532
|
const [kittySupport, setKittySupport] = useState<boolean | null>(() => getCachedKittySupport(renderer));
|
|
1533
|
+
const rendererState = resolveChartRendererState(preferredRenderer, kittySupport, renderer.resolution);
|
|
1534
|
+
const effectiveRenderer: ResolvedChartRenderer = rendererState.renderer;
|
|
1535
|
+
const useCanvasChart = canvasCharts && effectiveRenderer !== "kitty";
|
|
1536
|
+
const showNativeUnavailable = rendererState.nativeUnavailable && !useCanvasChart;
|
|
1526
1537
|
const [displayCursor, setDisplayCursor] = useState<DisplayCursorState>(EMPTY_DISPLAY_CURSOR);
|
|
1538
|
+
const [canvasBaseBitmapState, setCanvasBaseBitmapState] = useState<{ key: string; bitmap: NativeChartBitmap } | null>(null);
|
|
1527
1539
|
const plotRef = useRef<BoxRenderable | null>(null);
|
|
1528
1540
|
const nativeSurfaceScope = compact ? "compact" : "full";
|
|
1529
1541
|
const nativeBaseSurfaceId = useMemo(
|
|
@@ -1538,6 +1550,7 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
1538
1550
|
const lastNativeGeometryRef = useRef<{ rect: CellRect; visibleRect: CellRect | null } | null>(null);
|
|
1539
1551
|
const lastNativeBaseBitmapRef = useRef<{ key: string; bitmap: NativeChartBitmap } | null>(null);
|
|
1540
1552
|
const lastNativeCrosshairBitmapRef = useRef<{ key: string; bitmap: NativeChartBitmap } | null>(null);
|
|
1553
|
+
const lastCanvasBaseBitmapRef = useRef<{ key: string; bitmap: NativeChartBitmap } | null>(null);
|
|
1541
1554
|
const displayCursorRef = useRef<DisplayCursorState>(EMPTY_DISPLAY_CURSOR);
|
|
1542
1555
|
const targetCursorRef = useRef<DisplayCursorState>(EMPTY_DISPLAY_CURSOR);
|
|
1543
1556
|
const cursorMotionKindRef = useRef<ChartCursorMotionKind>("discrete");
|
|
@@ -1633,7 +1646,7 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
1633
1646
|
() => instrumentFromTicker(ticker, ticker?.metadata.ticker ?? null),
|
|
1634
1647
|
[ticker],
|
|
1635
1648
|
);
|
|
1636
|
-
const dataProvider =
|
|
1649
|
+
const dataProvider = getSharedMarketData();
|
|
1637
1650
|
const capabilityKey = instrumentRef ? [
|
|
1638
1651
|
instrumentRef.symbol,
|
|
1639
1652
|
instrumentRef.exchange ?? "",
|
|
@@ -1748,11 +1761,14 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
1748
1761
|
(value) => Array.isArray(value) && value.length > 0,
|
|
1749
1762
|
"No price history available.",
|
|
1750
1763
|
);
|
|
1751
|
-
const
|
|
1764
|
+
const shouldUseBoundsFallback = !compact
|
|
1765
|
+
&& fallbackPriceHistory.length > 0
|
|
1766
|
+
&& (!boundsChartEntry || (rawBoundsBodyState.blocking && !rawBoundsBodyState.data?.length));
|
|
1767
|
+
const boundsBodyState = shouldUseBoundsFallback
|
|
1752
1768
|
? {
|
|
1753
1769
|
data: fallbackPriceHistory,
|
|
1754
1770
|
blocking: false,
|
|
1755
|
-
updating:
|
|
1771
|
+
updating: !!boundsChartEntry && rawBoundsBodyState.blocking,
|
|
1756
1772
|
emptyMessage: null,
|
|
1757
1773
|
errorMessage: null,
|
|
1758
1774
|
}
|
|
@@ -1765,11 +1781,14 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
1765
1781
|
const axisGap = axisSectionWidthBudget > 0 ? 1 : 0;
|
|
1766
1782
|
const minChartWidth = compact ? 12 : 20;
|
|
1767
1783
|
const measurementChartWidth = Math.max(width - axisSectionWidthBudget - axisGap, minChartWidth);
|
|
1768
|
-
const headerRows = compact ? 0 :
|
|
1769
|
-
const helpRow = compact ?
|
|
1784
|
+
const headerRows = compact ? 0 : 2;
|
|
1785
|
+
const helpRow = compact ? 1 : 0;
|
|
1770
1786
|
const timeAxisRow = 1;
|
|
1787
|
+
// Native/canvas surfaces can cover the last physical row after pixel rounding;
|
|
1788
|
+
// keep the text x-axis one row clear of the pane footer.
|
|
1789
|
+
const nativeSurfaceGuardRow = !compact && (useCanvasChart || effectiveRenderer === "kitty") ? 1 : 0;
|
|
1771
1790
|
const volumeHeight = showVolume && !compact ? 3 : 0;
|
|
1772
|
-
const chartHeight = Math.max(height - headerRows - helpRow - timeAxisRow, 4);
|
|
1791
|
+
const chartHeight = Math.max(height - headerRows - helpRow - timeAxisRow - nativeSurfaceGuardRow, 4);
|
|
1773
1792
|
const chartCurrency = currencyOverride ?? financials?.quote?.currency ?? ticker?.metadata.currency ?? "USD";
|
|
1774
1793
|
const chartAssetCategory = ticker?.metadata.assetCategory;
|
|
1775
1794
|
const boundsHistory = compact
|
|
@@ -2803,7 +2822,7 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
2803
2822
|
return buildIndicatorRenderKey(indicators);
|
|
2804
2823
|
}, [indicators]);
|
|
2805
2824
|
|
|
2806
|
-
|
|
2825
|
+
useShortcut((event) => {
|
|
2807
2826
|
if (!focused || compact) return;
|
|
2808
2827
|
const key = resolveChartKeyboardKey(event);
|
|
2809
2828
|
|
|
@@ -3125,9 +3144,6 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3125
3144
|
indicators,
|
|
3126
3145
|
}), [axisMode, chartColors, chartHeight, chartWidth, compact, indicators, projection.effectiveMode, projection.points, showVolume, timeAxisDates, volumeHeight]);
|
|
3127
3146
|
|
|
3128
|
-
const rendererState = resolveChartRendererState(preferredRenderer, kittySupport, renderer.resolution);
|
|
3129
|
-
const effectiveRenderer: ResolvedChartRenderer = rendererState.renderer;
|
|
3130
|
-
|
|
3131
3147
|
const staticResult = useMemo(() => renderChart(projection.points, {
|
|
3132
3148
|
width: chartWidth,
|
|
3133
3149
|
height: chartHeight,
|
|
@@ -3145,7 +3161,7 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3145
3161
|
}), [axisMode, chartAssetCategory, chartColors, chartCurrency, chartHeight, chartWidth, compact, indicators, projection.effectiveMode, projection.points, showVolume, timeAxisDates, volumeHeight]);
|
|
3146
3162
|
|
|
3147
3163
|
const interactiveResult = useMemo(() => (
|
|
3148
|
-
effectiveRenderer === "kitty"
|
|
3164
|
+
effectiveRenderer === "kitty" || useCanvasChart
|
|
3149
3165
|
? null
|
|
3150
3166
|
: renderChart(projection.points, {
|
|
3151
3167
|
width: chartWidth,
|
|
@@ -3162,20 +3178,20 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3162
3178
|
timeAxisDates,
|
|
3163
3179
|
indicators,
|
|
3164
3180
|
})
|
|
3165
|
-
), [axisMode, chartAssetCategory, chartColors, chartCurrency, chartHeight, chartWidth, compact, displayCursorX, displayCursorY, effectiveRenderer, indicators, projection.effectiveMode, projection.points, showVolume, timeAxisDates, volumeHeight]);
|
|
3181
|
+
), [axisMode, chartAssetCategory, chartColors, chartCurrency, chartHeight, chartWidth, compact, displayCursorX, displayCursorY, effectiveRenderer, indicators, projection.effectiveMode, projection.points, showVolume, timeAxisDates, useCanvasChart, volumeHeight]);
|
|
3166
3182
|
|
|
3167
|
-
const result = effectiveRenderer === "kitty" ? staticResult : interactiveResult!;
|
|
3183
|
+
const result = effectiveRenderer === "kitty" || useCanvasChart ? staticResult : interactiveResult!;
|
|
3168
3184
|
|
|
3169
|
-
const
|
|
3185
|
+
const rasterCursorRow = (effectiveRenderer === "kitty" || useCanvasChart) && displayCursorY !== null && nativeBaseScene
|
|
3170
3186
|
? Math.round(clamp(displayCursorY, 0, Math.max(nativeBaseScene.chartRows - 1, 0)))
|
|
3171
3187
|
: null;
|
|
3172
|
-
const
|
|
3188
|
+
const rasterCrosshairPrice = (effectiveRenderer === "kitty" || useCanvasChart) && displayCursorY !== null && nativeBaseScene
|
|
3173
3189
|
? nativeBaseScene.max
|
|
3174
3190
|
- (clamp(displayCursorY, 0, Math.max(nativeBaseScene.chartRows - 1, 0)) / Math.max(nativeBaseScene.chartRows - 1, 1))
|
|
3175
3191
|
* (nativeBaseScene.max - nativeBaseScene.min)
|
|
3176
3192
|
: null;
|
|
3177
|
-
const cursorRow = effectiveRenderer === "kitty" ?
|
|
3178
|
-
const crosshairPrice = effectiveRenderer === "kitty" ?
|
|
3193
|
+
const cursorRow = effectiveRenderer === "kitty" || useCanvasChart ? rasterCursorRow : result.cursorRow;
|
|
3194
|
+
const crosshairPrice = effectiveRenderer === "kitty" || useCanvasChart ? rasterCrosshairPrice : result.crosshairPrice;
|
|
3179
3195
|
|
|
3180
3196
|
const nativeCrosshair = useMemo<NativeCrosshairOverlay | null>(() => {
|
|
3181
3197
|
if (!interactive || displayCursor.cellX === null || displayCursor.cellY === null) return null;
|
|
@@ -3417,11 +3433,7 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3417
3433
|
const hasHistory = chartWindow.points.length > 0;
|
|
3418
3434
|
const requestedMode = projection.requestedMode;
|
|
3419
3435
|
const showOhlcSummary = projection.effectiveMode === "candles" || projection.effectiveMode === "ohlc";
|
|
3420
|
-
const hasSelectionCursor = selectionCursorX !== null;
|
|
3421
3436
|
const hasDisplayCursor = displayCursorX !== null && displayCursorY !== null;
|
|
3422
|
-
const displayDate = hasSelectionCursor || showOhlcSummary
|
|
3423
|
-
? (selectionScene?.dateAtCursor ? formatDateShort(selectionScene.dateAtCursor) : null)
|
|
3424
|
-
: null;
|
|
3425
3437
|
const activePoint = showOhlcSummary ? (selectionScene?.activePoint ?? null) : null;
|
|
3426
3438
|
const visiblePriceRange = selectionScene
|
|
3427
3439
|
? Math.max(selectionScene.max - selectionScene.min, 0)
|
|
@@ -3442,6 +3454,147 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3442
3454
|
? (renderBodyState.errorMessage ?? renderBodyState.emptyMessage)
|
|
3443
3455
|
: null;
|
|
3444
3456
|
const isUpdating = !compact && (renderBodyState.updating || boundsBodyState.updating);
|
|
3457
|
+
const timeAxisLabel = selectionScene?.timeLabels ?? staticResult.timeLabels;
|
|
3458
|
+
const timeAxisBox = (
|
|
3459
|
+
<Box
|
|
3460
|
+
height={1}
|
|
3461
|
+
width={chartWidth}
|
|
3462
|
+
overflow="hidden"
|
|
3463
|
+
data-gloom-role="chart-time-axis"
|
|
3464
|
+
>
|
|
3465
|
+
<Text fg={colors.textDim} style={{ whiteSpace: "pre" }}>{timeAxisLabel}</Text>
|
|
3466
|
+
</Box>
|
|
3467
|
+
);
|
|
3468
|
+
const chartFooterHints = useMemo<PaneHint[]>(() => {
|
|
3469
|
+
if (compact) return [];
|
|
3470
|
+
|
|
3471
|
+
const cycleMode = () => {
|
|
3472
|
+
setViewState((current) => {
|
|
3473
|
+
const activeMode = current.renderMode ?? "area";
|
|
3474
|
+
const index = CHART_RENDER_MODES.indexOf(activeMode);
|
|
3475
|
+
const nextMode = CHART_RENDER_MODES[(index + 1) % CHART_RENDER_MODES.length]!;
|
|
3476
|
+
persistDefaultRenderMode(nextMode);
|
|
3477
|
+
return { ...current, renderMode: nextMode };
|
|
3478
|
+
});
|
|
3479
|
+
};
|
|
3480
|
+
const cycleResolution = () => {
|
|
3481
|
+
const currentIndex = resolutionChips.indexOf(selectedResolution);
|
|
3482
|
+
const nextResolution = resolutionChips[(currentIndex + 1) % resolutionChips.length] ?? "auto";
|
|
3483
|
+
setResolution(nextResolution);
|
|
3484
|
+
};
|
|
3485
|
+
const zoomIn = () => {
|
|
3486
|
+
if (effectiveResolution === "auto") {
|
|
3487
|
+
requestAutoWindow(resolveAutoZoomWindow({
|
|
3488
|
+
historyPoints: history,
|
|
3489
|
+
boundsDates: boundsHistoryDates,
|
|
3490
|
+
currentWindow: navigableDateWindow,
|
|
3491
|
+
direction: "in",
|
|
3492
|
+
anchorRatio: RIGHT_EDGE_ANCHOR_RATIO,
|
|
3493
|
+
}));
|
|
3494
|
+
return;
|
|
3495
|
+
}
|
|
3496
|
+
setViewState((current) => applyZoomAroundAnchor(current, current.zoomLevel * 1.5, RIGHT_EDGE_ANCHOR_RATIO, boundsHistory));
|
|
3497
|
+
};
|
|
3498
|
+
const resetView = () => {
|
|
3499
|
+
if (effectiveResolution === "auto") {
|
|
3500
|
+
pendingAutoWindowRef.current = null;
|
|
3501
|
+
setPendingAutoWindowOverride(null);
|
|
3502
|
+
setRenderedAutoView(null);
|
|
3503
|
+
updateDisplayCursorTarget(EMPTY_DISPLAY_CURSOR, "discrete");
|
|
3504
|
+
setViewState((current) => clearAutoViewportState(current));
|
|
3505
|
+
return;
|
|
3506
|
+
}
|
|
3507
|
+
pendingCanonicalResetRef.current += 1;
|
|
3508
|
+
updateDisplayCursorTarget(EMPTY_DISPLAY_CURSOR, "discrete");
|
|
3509
|
+
setViewState((current) => {
|
|
3510
|
+
const nextState = resolveViewportResolutionSelection(
|
|
3511
|
+
current,
|
|
3512
|
+
effectiveResolution,
|
|
3513
|
+
selectionSupportMap,
|
|
3514
|
+
visibleDateWindow,
|
|
3515
|
+
) ?? current;
|
|
3516
|
+
return {
|
|
3517
|
+
...nextState,
|
|
3518
|
+
panOffset: 0,
|
|
3519
|
+
zoomLevel: 1,
|
|
3520
|
+
cursorX: null,
|
|
3521
|
+
cursorY: null,
|
|
3522
|
+
};
|
|
3523
|
+
});
|
|
3524
|
+
};
|
|
3525
|
+
const cycleRange = () => {
|
|
3526
|
+
const currentIndex = activePreset ? TIME_RANGES.indexOf(activePreset) : -1;
|
|
3527
|
+
const nextRange = TIME_RANGES[(currentIndex + 1) % TIME_RANGES.length] ?? TIME_RANGES[0]!;
|
|
3528
|
+
setRange(nextRange);
|
|
3529
|
+
};
|
|
3530
|
+
|
|
3531
|
+
return [
|
|
3532
|
+
{ id: "mode", key: "m", label: "ode", onPress: cycleMode },
|
|
3533
|
+
...(footerHints ?? []),
|
|
3534
|
+
{ id: "resolution", key: "r", label: "es", onPress: cycleResolution },
|
|
3535
|
+
{ id: "zoom", key: "+/-", label: "zoom", onPress: zoomIn },
|
|
3536
|
+
{ id: "reset", key: "0", label: "reset", onPress: resetView },
|
|
3537
|
+
...(width >= 72 ? [{ id: "range", key: "1-7", label: "range", onPress: cycleRange }] : []),
|
|
3538
|
+
];
|
|
3539
|
+
}, [
|
|
3540
|
+
activePreset,
|
|
3541
|
+
boundsHistory,
|
|
3542
|
+
boundsHistoryDates,
|
|
3543
|
+
compact,
|
|
3544
|
+
effectiveResolution,
|
|
3545
|
+
footerHints,
|
|
3546
|
+
history,
|
|
3547
|
+
navigableDateWindow,
|
|
3548
|
+
resolutionChips,
|
|
3549
|
+
selectedResolution,
|
|
3550
|
+
selectionSupportMap,
|
|
3551
|
+
visibleDateWindow,
|
|
3552
|
+
width,
|
|
3553
|
+
]);
|
|
3554
|
+
|
|
3555
|
+
const chartFooterInfo = useMemo<PaneFooterSegment[]>(() => {
|
|
3556
|
+
if (compact || !showOhlcSummary || !activePoint) return [];
|
|
3557
|
+
|
|
3558
|
+
const formatPrice = (value: number) => formatMarketPriceWithCurrency(value, chartCurrency, {
|
|
3559
|
+
assetCategory: chartAssetCategory,
|
|
3560
|
+
minimumFractionDigits: 2,
|
|
3561
|
+
precisionOffset: 1,
|
|
3562
|
+
priceRange: visiblePriceRange,
|
|
3563
|
+
});
|
|
3564
|
+
|
|
3565
|
+
return [{
|
|
3566
|
+
id: "ohlc",
|
|
3567
|
+
parts: [
|
|
3568
|
+
{ text: "O", tone: "label" },
|
|
3569
|
+
{ text: formatPrice(activePoint.open), tone: "value" },
|
|
3570
|
+
{ text: "H", tone: "label" },
|
|
3571
|
+
{ text: formatPrice(activePoint.high), tone: "value" },
|
|
3572
|
+
{ text: "L", tone: "label" },
|
|
3573
|
+
{ text: formatPrice(activePoint.low), tone: "value" },
|
|
3574
|
+
{ text: "C", tone: "label" },
|
|
3575
|
+
{ text: formatPrice(activePoint.close), tone: "value" },
|
|
3576
|
+
{ text: "V", tone: "label" },
|
|
3577
|
+
{ text: formatCompact(activePoint.volume), tone: "value" },
|
|
3578
|
+
],
|
|
3579
|
+
}];
|
|
3580
|
+
}, [
|
|
3581
|
+
activePoint,
|
|
3582
|
+
chartAssetCategory,
|
|
3583
|
+
chartCurrency,
|
|
3584
|
+
compact,
|
|
3585
|
+
showOhlcSummary,
|
|
3586
|
+
visiblePriceRange,
|
|
3587
|
+
]);
|
|
3588
|
+
|
|
3589
|
+
usePaneFooter("stock-chart", () => (
|
|
3590
|
+
compact
|
|
3591
|
+
? null
|
|
3592
|
+
: {
|
|
3593
|
+
order: 10,
|
|
3594
|
+
info: chartFooterInfo,
|
|
3595
|
+
hints: chartFooterHints,
|
|
3596
|
+
}
|
|
3597
|
+
), [chartFooterHints, chartFooterInfo, compact]);
|
|
3445
3598
|
|
|
3446
3599
|
useEffect(() => {
|
|
3447
3600
|
queueMicrotask(() => renderer.requestRender());
|
|
@@ -3474,7 +3627,8 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3474
3627
|
};
|
|
3475
3628
|
|
|
3476
3629
|
const handlePlotDown = (event: ChartMouseEvent) => {
|
|
3477
|
-
if (
|
|
3630
|
+
if (compact) return;
|
|
3631
|
+
if (!interactive) onActivate?.();
|
|
3478
3632
|
focusPaneForMouseInteraction(event);
|
|
3479
3633
|
const localPointer = getLocalPlotPointer(event, plotRef.current, renderer);
|
|
3480
3634
|
if (!localPointer) return;
|
|
@@ -3499,7 +3653,7 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3499
3653
|
};
|
|
3500
3654
|
|
|
3501
3655
|
const handlePlotDrag = (event: ChartMouseEvent) => {
|
|
3502
|
-
if (!interactive
|
|
3656
|
+
if (compact || (!interactive && !dragRef.current)) return;
|
|
3503
3657
|
const localPointer = getLocalPlotPointer(event, plotRef.current, renderer);
|
|
3504
3658
|
if (localPointer) {
|
|
3505
3659
|
const selectionCursor = resolveSelectionCursor(localPointer, projection.points.length, chartWidth, projection.effectiveMode);
|
|
@@ -3522,7 +3676,7 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3522
3676
|
const deltaCells = getGlobalMouseX(event, renderer) - dragRef.current.startGlobalX;
|
|
3523
3677
|
requestAutoWindow(shiftDateWindow(
|
|
3524
3678
|
dragRef.current.startWindow,
|
|
3525
|
-
|
|
3679
|
+
getDragPanWindowRatio(deltaCells, chartWidth),
|
|
3526
3680
|
));
|
|
3527
3681
|
return;
|
|
3528
3682
|
}
|
|
@@ -3601,122 +3755,260 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3601
3755
|
}));
|
|
3602
3756
|
};
|
|
3603
3757
|
|
|
3758
|
+
const canvasBitmapSize = useMemo(() => {
|
|
3759
|
+
if (!canvasCharts) return null;
|
|
3760
|
+
const resolutionScale = Math.max(1, pixelRatio);
|
|
3761
|
+
return {
|
|
3762
|
+
pixelWidth: Math.max(1, Math.round(chartWidth * cellWidthPx * resolutionScale)),
|
|
3763
|
+
pixelHeight: Math.max(1, Math.round(chartHeight * cellHeightPx * resolutionScale)),
|
|
3764
|
+
};
|
|
3765
|
+
}, [canvasCharts, cellHeightPx, cellWidthPx, chartHeight, chartWidth, pixelRatio]);
|
|
3766
|
+
|
|
3767
|
+
const canvasProjection = useMemo(() => {
|
|
3768
|
+
if (!canvasBitmapSize) return null;
|
|
3769
|
+
const canvasWidth = Math.max(chartWidth, canvasBitmapSize.pixelWidth);
|
|
3770
|
+
const canvasOhlcOptions = resolveStableOhlcProjectionOptions({
|
|
3771
|
+
pointCount: chartWindow.points.length,
|
|
3772
|
+
sourceIndexOffset: chartWindow.startIdx,
|
|
3773
|
+
bucketWidth: canvasWidth,
|
|
3774
|
+
navigationPointCount: navigationOhlcPointCount,
|
|
3775
|
+
});
|
|
3776
|
+
return projectChartData(
|
|
3777
|
+
chartWindow.points,
|
|
3778
|
+
canvasWidth,
|
|
3779
|
+
viewState.renderMode,
|
|
3780
|
+
!!compact,
|
|
3781
|
+
canvasOhlcOptions,
|
|
3782
|
+
);
|
|
3783
|
+
}, [
|
|
3784
|
+
canvasBitmapSize,
|
|
3785
|
+
chartWidth,
|
|
3786
|
+
chartWindow.points,
|
|
3787
|
+
chartWindow.startIdx,
|
|
3788
|
+
compact,
|
|
3789
|
+
navigationOhlcPointCount,
|
|
3790
|
+
viewState.renderMode,
|
|
3791
|
+
]);
|
|
3792
|
+
|
|
3793
|
+
const canvasIndicators = useMemo(() => (
|
|
3794
|
+
sourceIndicatorOverlays && chartWindow.points.length && canvasProjection?.points.length
|
|
3795
|
+
? reindexIndicatorOverlaysForProjection(
|
|
3796
|
+
sourceIndicatorOverlays,
|
|
3797
|
+
chartWindow.points,
|
|
3798
|
+
canvasProjection.points,
|
|
3799
|
+
chartWindow.startIdx,
|
|
3800
|
+
)
|
|
3801
|
+
: null
|
|
3802
|
+
), [canvasProjection?.points, chartWindow.points, chartWindow.startIdx, sourceIndicatorOverlays]);
|
|
3803
|
+
|
|
3804
|
+
const canvasIndicatorRenderKey = useMemo(() => buildIndicatorRenderKey(canvasIndicators), [canvasIndicators]);
|
|
3805
|
+
|
|
3806
|
+
const canvasBaseScene = useMemo(() => (
|
|
3807
|
+
canvasProjection
|
|
3808
|
+
? buildChartScene(canvasProjection.points, {
|
|
3809
|
+
width: canvasProjection.points.length,
|
|
3810
|
+
height: chartHeight,
|
|
3811
|
+
showVolume: showVolume && !compact,
|
|
3812
|
+
volumeHeight,
|
|
3813
|
+
cursorX: null,
|
|
3814
|
+
cursorY: null,
|
|
3815
|
+
mode: canvasProjection.effectiveMode,
|
|
3816
|
+
axisMode,
|
|
3817
|
+
colors: chartColors,
|
|
3818
|
+
timeAxisDates,
|
|
3819
|
+
indicators: canvasIndicators,
|
|
3820
|
+
})
|
|
3821
|
+
: null
|
|
3822
|
+
), [axisMode, canvasIndicators, canvasProjection, chartColors, chartHeight, compact, showVolume, timeAxisDates, volumeHeight]);
|
|
3823
|
+
|
|
3824
|
+
const canvasBaseBitmapKey = useMemo(() => {
|
|
3825
|
+
if (!canvasBitmapSize || !canvasProjection || !hasHistory || isBlockingBody || bodyMessage) return null;
|
|
3826
|
+
return buildNativeBitmapKey(
|
|
3827
|
+
canvasProjection.points.length,
|
|
3828
|
+
canvasProjection.points,
|
|
3829
|
+
canvasBitmapSize.pixelWidth,
|
|
3830
|
+
canvasBitmapSize.pixelHeight,
|
|
3831
|
+
canvasProjection.effectiveMode,
|
|
3832
|
+
showVolume && !compact,
|
|
3833
|
+
[
|
|
3834
|
+
chartColors.lineColor,
|
|
3835
|
+
chartColors.fillColor,
|
|
3836
|
+
chartColors.gridColor,
|
|
3837
|
+
chartColors.volumeUp,
|
|
3838
|
+
chartColors.volumeDown,
|
|
3839
|
+
chartColors.candleUp,
|
|
3840
|
+
chartColors.candleDown,
|
|
3841
|
+
].join(","),
|
|
3842
|
+
canvasIndicatorRenderKey,
|
|
3843
|
+
);
|
|
3844
|
+
}, [
|
|
3845
|
+
bodyMessage,
|
|
3846
|
+
canvasBitmapSize,
|
|
3847
|
+
canvasIndicatorRenderKey,
|
|
3848
|
+
canvasProjection,
|
|
3849
|
+
chartColors.candleDown,
|
|
3850
|
+
chartColors.candleUp,
|
|
3851
|
+
chartColors.fillColor,
|
|
3852
|
+
chartColors.gridColor,
|
|
3853
|
+
chartColors.lineColor,
|
|
3854
|
+
chartColors.volumeDown,
|
|
3855
|
+
chartColors.volumeUp,
|
|
3856
|
+
compact,
|
|
3857
|
+
hasHistory,
|
|
3858
|
+
isBlockingBody,
|
|
3859
|
+
showVolume,
|
|
3860
|
+
]);
|
|
3861
|
+
|
|
3862
|
+
const canvasBaseBitmap = useMemo<NativeChartBitmap | null>(() => {
|
|
3863
|
+
return canvasBaseBitmapKey && canvasBaseBitmapState?.key === canvasBaseBitmapKey
|
|
3864
|
+
? canvasBaseBitmapState.bitmap
|
|
3865
|
+
: null;
|
|
3866
|
+
}, [canvasBaseBitmapKey, canvasBaseBitmapState]);
|
|
3867
|
+
|
|
3868
|
+
const visibleCanvasBaseBitmap = useMemo<NativeChartBitmap | null>(() => {
|
|
3869
|
+
if (canvasBaseBitmap) return canvasBaseBitmap;
|
|
3870
|
+
if (!canvasBaseBitmapKey || !canvasBitmapSize || !canvasBaseBitmapState) return null;
|
|
3871
|
+
return canvasBaseBitmapState.bitmap.width === canvasBitmapSize.pixelWidth
|
|
3872
|
+
&& canvasBaseBitmapState.bitmap.height === canvasBitmapSize.pixelHeight
|
|
3873
|
+
? canvasBaseBitmapState.bitmap
|
|
3874
|
+
: null;
|
|
3875
|
+
}, [canvasBaseBitmap, canvasBaseBitmapKey, canvasBaseBitmapState, canvasBitmapSize]);
|
|
3876
|
+
|
|
3877
|
+
useEffect(() => {
|
|
3878
|
+
if (!canvasBitmapSize || !canvasBaseBitmapKey || !canvasBaseScene) {
|
|
3879
|
+
lastCanvasBaseBitmapRef.current = null;
|
|
3880
|
+
setCanvasBaseBitmapState((current) => (current === null ? current : null));
|
|
3881
|
+
return;
|
|
3882
|
+
}
|
|
3883
|
+
|
|
3884
|
+
const cachedBitmap = lastCanvasBaseBitmapRef.current?.key === canvasBaseBitmapKey
|
|
3885
|
+
? lastCanvasBaseBitmapRef.current.bitmap
|
|
3886
|
+
: null;
|
|
3887
|
+
if (cachedBitmap) {
|
|
3888
|
+
setCanvasBaseBitmapState((current) => (
|
|
3889
|
+
current?.key === canvasBaseBitmapKey ? current : { key: canvasBaseBitmapKey, bitmap: cachedBitmap }
|
|
3890
|
+
));
|
|
3891
|
+
return;
|
|
3892
|
+
}
|
|
3893
|
+
|
|
3894
|
+
let cancelled = false;
|
|
3895
|
+
const frame = requestAnimationFrameSafe(() => {
|
|
3896
|
+
const bitmap = renderNativeChartBase(canvasBaseScene, canvasBitmapSize.pixelWidth, canvasBitmapSize.pixelHeight);
|
|
3897
|
+
if (cancelled) return;
|
|
3898
|
+
lastCanvasBaseBitmapRef.current = { key: canvasBaseBitmapKey, bitmap };
|
|
3899
|
+
setCanvasBaseBitmapState((current) => (
|
|
3900
|
+
current?.key === canvasBaseBitmapKey ? current : { key: canvasBaseBitmapKey, bitmap }
|
|
3901
|
+
));
|
|
3902
|
+
});
|
|
3903
|
+
|
|
3904
|
+
return () => {
|
|
3905
|
+
cancelled = true;
|
|
3906
|
+
cancelAnimationFrameSafe(frame);
|
|
3907
|
+
};
|
|
3908
|
+
}, [canvasBaseBitmapKey, canvasBaseScene, canvasBitmapSize]);
|
|
3909
|
+
|
|
3910
|
+
const canvasCrosshair = useMemo(() => {
|
|
3911
|
+
if (!canvasBitmapSize || !visibleCanvasBaseBitmap || !nativeCrosshair) return null;
|
|
3912
|
+
const renderablePixelSize = getRenderablePixelSize(plotRef.current, renderer);
|
|
3913
|
+
const overlayPixelX = scaleLocalPixelCoordinate(
|
|
3914
|
+
nativeCrosshair.pixelX,
|
|
3915
|
+
renderablePixelSize?.pixelWidth ?? canvasBitmapSize.pixelWidth,
|
|
3916
|
+
canvasBitmapSize.pixelWidth,
|
|
3917
|
+
);
|
|
3918
|
+
const overlayPixelY = scaleLocalPixelCoordinate(
|
|
3919
|
+
nativeCrosshair.pixelY,
|
|
3920
|
+
renderablePixelSize?.pixelHeight ?? canvasBitmapSize.pixelHeight,
|
|
3921
|
+
canvasBitmapSize.pixelHeight,
|
|
3922
|
+
);
|
|
3923
|
+
if (overlayPixelX === null || overlayPixelY === null) return null;
|
|
3924
|
+
return {
|
|
3925
|
+
pixelX: overlayPixelX,
|
|
3926
|
+
pixelY: overlayPixelY,
|
|
3927
|
+
color: nativeCrosshair.colors.crosshairColor,
|
|
3928
|
+
};
|
|
3929
|
+
}, [canvasBitmapSize, nativeCrosshair, renderer, visibleCanvasBaseBitmap]);
|
|
3930
|
+
|
|
3931
|
+
const plotBitmaps = useMemo(() => {
|
|
3932
|
+
if (!visibleCanvasBaseBitmap) return null;
|
|
3933
|
+
return [visibleCanvasBaseBitmap];
|
|
3934
|
+
}, [visibleCanvasBaseBitmap]);
|
|
3935
|
+
|
|
3604
3936
|
const plotLines: Array<string | StyledContent> = effectiveRenderer === "kitty"
|
|
3605
3937
|
? blankPlotLines
|
|
3606
3938
|
: result.lines;
|
|
3607
|
-
const plotContent =
|
|
3608
|
-
|
|
3609
|
-
|
|
3939
|
+
const plotContent = plotBitmaps || (useCanvasChart && canvasBaseBitmapKey)
|
|
3940
|
+
? null
|
|
3941
|
+
: plotLines.map((line, index) => (
|
|
3942
|
+
<Text key={index} content={line as unknown as string} />
|
|
3943
|
+
));
|
|
3610
3944
|
|
|
3611
3945
|
const plotBox = (
|
|
3612
|
-
<
|
|
3946
|
+
<ChartSurface
|
|
3613
3947
|
ref={plotRef}
|
|
3614
3948
|
width={chartWidth}
|
|
3615
3949
|
height={chartHeight}
|
|
3616
3950
|
flexDirection="column"
|
|
3617
|
-
|
|
3951
|
+
bitmaps={plotBitmaps}
|
|
3952
|
+
crosshair={canvasCrosshair}
|
|
3618
3953
|
onMouseMove={compact || !hasHistory || isBlockingBody || !!bodyMessage ? undefined : handlePlotMove}
|
|
3619
3954
|
onMouseDown={compact || !hasHistory || isBlockingBody || !!bodyMessage ? undefined : handlePlotDown}
|
|
3620
3955
|
onMouseUp={compact || !hasHistory || isBlockingBody || !!bodyMessage ? undefined : () => { dragRef.current = null; }}
|
|
3621
3956
|
onMouseDrag={compact || !hasHistory || isBlockingBody || !!bodyMessage ? undefined : handlePlotDrag}
|
|
3622
3957
|
onMouseDragEnd={compact || !hasHistory || isBlockingBody || !!bodyMessage ? undefined : () => { dragRef.current = null; }}
|
|
3623
|
-
|
|
3624
|
-
dragRef.current = null;
|
|
3625
|
-
}}
|
|
3958
|
+
onMouseScroll={compact || !hasHistory || isBlockingBody || !!bodyMessage ? undefined : handlePlotScroll}
|
|
3626
3959
|
>
|
|
3627
3960
|
{isBlockingBody
|
|
3628
3961
|
? (
|
|
3629
|
-
<
|
|
3630
|
-
<
|
|
3631
|
-
</
|
|
3962
|
+
<Box flexGrow={1} alignItems="center" justifyContent="center">
|
|
3963
|
+
<Text fg={colors.textDim}>Loading chart...</Text>
|
|
3964
|
+
</Box>
|
|
3632
3965
|
)
|
|
3633
3966
|
: bodyMessage
|
|
3634
3967
|
? (
|
|
3635
|
-
<
|
|
3636
|
-
<
|
|
3637
|
-
</
|
|
3968
|
+
<Box flexGrow={1} alignItems="center" justifyContent="center">
|
|
3969
|
+
<Text fg={colors.textDim}>{bodyMessage}</Text>
|
|
3970
|
+
</Box>
|
|
3638
3971
|
)
|
|
3639
3972
|
: plotContent}
|
|
3640
|
-
</
|
|
3973
|
+
</ChartSurface>
|
|
3641
3974
|
);
|
|
3642
3975
|
|
|
3643
3976
|
const axisBox = (
|
|
3644
|
-
<
|
|
3977
|
+
<Box width={axisSectionWidth} height={chartHeight} flexDirection="column">
|
|
3645
3978
|
{Array.from({ length: chartHeight }, (_, row) => {
|
|
3646
3979
|
const isCursorRow = cursorAxisLabel !== null && cursorRow === row;
|
|
3647
3980
|
const label = isCursorRow ? cursorAxisLabel : (axisLabels.get(row) ?? null);
|
|
3648
3981
|
return (
|
|
3649
|
-
<
|
|
3982
|
+
<Text key={row} fg={isCursorRow ? chartColors.crosshairColor : colors.textDim}>
|
|
3650
3983
|
{formatAxisCell(label, axisWidth).padEnd(axisSectionWidth)}
|
|
3651
|
-
</
|
|
3984
|
+
</Text>
|
|
3652
3985
|
);
|
|
3653
3986
|
})}
|
|
3654
|
-
</
|
|
3987
|
+
</Box>
|
|
3655
3988
|
);
|
|
3656
3989
|
|
|
3657
3990
|
if (compact) {
|
|
3658
3991
|
return (
|
|
3659
|
-
<
|
|
3660
|
-
<
|
|
3992
|
+
<Box flexDirection="column">
|
|
3993
|
+
<Box flexDirection="row" height={chartHeight} gap={axisGap}>
|
|
3661
3994
|
{plotBox}
|
|
3662
3995
|
{axisBox}
|
|
3663
|
-
</
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
</box>
|
|
3667
|
-
</box>
|
|
3996
|
+
</Box>
|
|
3997
|
+
{timeAxisBox}
|
|
3998
|
+
</Box>
|
|
3668
3999
|
);
|
|
3669
4000
|
}
|
|
3670
4001
|
|
|
3671
4002
|
return (
|
|
3672
|
-
<
|
|
4003
|
+
<Box
|
|
3673
4004
|
flexDirection="column"
|
|
3674
4005
|
flexGrow={1}
|
|
3675
4006
|
onMouseScroll={compact || !hasHistory || isBlockingBody || !!bodyMessage ? undefined : handlePlotScroll}
|
|
3676
4007
|
>
|
|
3677
|
-
<
|
|
3678
|
-
<
|
|
3679
|
-
{ticker?.metadata.ticker ?? ""} - {getChartResolutionLabel(selectedResolution)}
|
|
3680
|
-
</text>
|
|
3681
|
-
{fallbackResolutionLabel && (
|
|
3682
|
-
<text fg={colors.textDim}>{fallbackResolutionLabel}</text>
|
|
3683
|
-
)}
|
|
3684
|
-
{displayDate && <text fg={colors.textDim}>{displayDate}</text>}
|
|
3685
|
-
{showOhlcSummary && activePoint && (
|
|
3686
|
-
<>
|
|
3687
|
-
<text fg={colors.textDim}>O {formatMarketPriceWithCurrency(activePoint.open, chartCurrency, {
|
|
3688
|
-
assetCategory: chartAssetCategory,
|
|
3689
|
-
minimumFractionDigits: 2,
|
|
3690
|
-
precisionOffset: 1,
|
|
3691
|
-
priceRange: visiblePriceRange,
|
|
3692
|
-
})}</text>
|
|
3693
|
-
<text fg={colors.textDim}>H {formatMarketPriceWithCurrency(activePoint.high, chartCurrency, {
|
|
3694
|
-
assetCategory: chartAssetCategory,
|
|
3695
|
-
minimumFractionDigits: 2,
|
|
3696
|
-
precisionOffset: 1,
|
|
3697
|
-
priceRange: visiblePriceRange,
|
|
3698
|
-
})}</text>
|
|
3699
|
-
<text fg={colors.textDim}>L {formatMarketPriceWithCurrency(activePoint.low, chartCurrency, {
|
|
3700
|
-
assetCategory: chartAssetCategory,
|
|
3701
|
-
minimumFractionDigits: 2,
|
|
3702
|
-
precisionOffset: 1,
|
|
3703
|
-
priceRange: visiblePriceRange,
|
|
3704
|
-
})}</text>
|
|
3705
|
-
<text fg={colors.textDim}>C {formatMarketPriceWithCurrency(activePoint.close, chartCurrency, {
|
|
3706
|
-
assetCategory: chartAssetCategory,
|
|
3707
|
-
minimumFractionDigits: 2,
|
|
3708
|
-
precisionOffset: 1,
|
|
3709
|
-
priceRange: visiblePriceRange,
|
|
3710
|
-
})}</text>
|
|
3711
|
-
<text fg={colors.textDim}>V {formatCompact(activePoint.volume)}</text>
|
|
3712
|
-
</>
|
|
3713
|
-
)}
|
|
3714
|
-
</box>
|
|
3715
|
-
|
|
3716
|
-
<box flexDirection="row" height={1}>
|
|
3717
|
-
<box flexDirection="row" gap={1}>
|
|
4008
|
+
<Box flexDirection="row" height={1}>
|
|
4009
|
+
<Box flexDirection="row" gap={1}>
|
|
3718
4010
|
{TIME_RANGES.map((range, index) => (
|
|
3719
|
-
<
|
|
4011
|
+
<Text
|
|
3720
4012
|
key={range}
|
|
3721
4013
|
fg={activePreset === range ? chartColors.activeRangeColor : (isRangePresetSupported(range, availableManualResolutions) ? chartColors.inactiveRangeColor : colors.textMuted)}
|
|
3722
4014
|
attributes={activePreset === range ? TextAttributes.BOLD : 0}
|
|
@@ -3726,15 +4018,15 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3726
4018
|
}}
|
|
3727
4019
|
>
|
|
3728
4020
|
{`${index + 1}:${range}`}
|
|
3729
|
-
</
|
|
4021
|
+
</Text>
|
|
3730
4022
|
))}
|
|
3731
|
-
</
|
|
3732
|
-
</
|
|
4023
|
+
</Box>
|
|
4024
|
+
</Box>
|
|
3733
4025
|
|
|
3734
|
-
<
|
|
3735
|
-
<
|
|
4026
|
+
<Box flexDirection="row" height={1}>
|
|
4027
|
+
<Box flexDirection="row" gap={1}>
|
|
3736
4028
|
{resolutionChips.map((resolution) => (
|
|
3737
|
-
<
|
|
4029
|
+
<Text
|
|
3738
4030
|
key={resolution}
|
|
3739
4031
|
fg={selectedResolution === resolution ? chartColors.activeRangeColor : chartColors.inactiveRangeColor}
|
|
3740
4032
|
attributes={selectedResolution === resolution ? TextAttributes.BOLD : 0}
|
|
@@ -3744,17 +4036,20 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3744
4036
|
}}
|
|
3745
4037
|
>
|
|
3746
4038
|
{getChartResolutionLabel(resolution)}
|
|
3747
|
-
</
|
|
4039
|
+
</Text>
|
|
3748
4040
|
))}
|
|
3749
4041
|
{isUpdating && (
|
|
3750
|
-
<
|
|
4042
|
+
<Text fg={colors.textDim}>updating</Text>
|
|
4043
|
+
)}
|
|
4044
|
+
{fallbackResolutionLabel && (
|
|
4045
|
+
<Text fg={colors.textDim}>{fallbackResolutionLabel}</Text>
|
|
3751
4046
|
)}
|
|
3752
|
-
</
|
|
3753
|
-
<
|
|
4047
|
+
</Box>
|
|
4048
|
+
<Box flexGrow={1} />
|
|
3754
4049
|
{chartWidth >= 72 ? (
|
|
3755
|
-
<
|
|
4050
|
+
<Box flexDirection="row" gap={1}>
|
|
3756
4051
|
{CHART_RENDER_MODES.map((mode) => (
|
|
3757
|
-
<
|
|
4052
|
+
<Text
|
|
3758
4053
|
key={mode}
|
|
3759
4054
|
fg={requestedMode === mode ? chartColors.activeRangeColor : chartColors.inactiveRangeColor}
|
|
3760
4055
|
attributes={requestedMode === mode ? TextAttributes.BOLD : 0}
|
|
@@ -3764,29 +4059,29 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3764
4059
|
}}
|
|
3765
4060
|
>
|
|
3766
4061
|
{MODE_CHIPS[mode]}
|
|
3767
|
-
</
|
|
4062
|
+
</Text>
|
|
3768
4063
|
))}
|
|
3769
4064
|
{projection.fallbackMode && (
|
|
3770
|
-
<
|
|
4065
|
+
<Text fg={colors.textDim}>auto:{MODE_LABELS[projection.fallbackMode]}</Text>
|
|
3771
4066
|
)}
|
|
3772
|
-
{
|
|
3773
|
-
<
|
|
4067
|
+
{showNativeUnavailable && (
|
|
4068
|
+
<Text fg={colors.textDim}>native unavailable</Text>
|
|
3774
4069
|
)}
|
|
3775
|
-
</
|
|
4070
|
+
</Box>
|
|
3776
4071
|
) : (
|
|
3777
|
-
<
|
|
3778
|
-
<
|
|
4072
|
+
<Box flexDirection="row" gap={1}>
|
|
4073
|
+
<Text fg={colors.textDim}>mode:{MODE_LABELS[requestedMode]}</Text>
|
|
3779
4074
|
{projection.fallbackMode && (
|
|
3780
|
-
<
|
|
4075
|
+
<Text fg={colors.textDim}>auto:{MODE_LABELS[projection.fallbackMode]}</Text>
|
|
3781
4076
|
)}
|
|
3782
|
-
{
|
|
3783
|
-
<
|
|
4077
|
+
{showNativeUnavailable && (
|
|
4078
|
+
<Text fg={colors.textDim}>native unavailable</Text>
|
|
3784
4079
|
)}
|
|
3785
|
-
</
|
|
4080
|
+
</Box>
|
|
3786
4081
|
)}
|
|
3787
|
-
</
|
|
4082
|
+
</Box>
|
|
3788
4083
|
|
|
3789
|
-
<
|
|
4084
|
+
<Box
|
|
3790
4085
|
flexDirection="row"
|
|
3791
4086
|
height={chartHeight}
|
|
3792
4087
|
gap={axisGap}
|
|
@@ -3794,20 +4089,22 @@ export const ResolvedStockChart = memo(function ResolvedStockChart({
|
|
|
3794
4089
|
>
|
|
3795
4090
|
{plotBox}
|
|
3796
4091
|
{axisBox}
|
|
3797
|
-
</
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
4092
|
+
</Box>
|
|
4093
|
+
|
|
4094
|
+
{timeAxisBox}
|
|
4095
|
+
|
|
4096
|
+
{compact && (
|
|
4097
|
+
<>
|
|
4098
|
+
<Box height={1} flexDirection="row" gap={1}>
|
|
4099
|
+
<ShortcutHint hotkey="m" label="ode" />
|
|
4100
|
+
{footerControls}
|
|
4101
|
+
<ShortcutHint hotkey="r" label="es" />
|
|
4102
|
+
<ShortcutHint hotkey="+/-" label="zoom" />
|
|
4103
|
+
<ShortcutHint hotkey="0" label="reset" />
|
|
4104
|
+
{width >= 72 && <ShortcutHint hotkey="1-7" label="range" />}
|
|
4105
|
+
</Box>
|
|
4106
|
+
</>
|
|
4107
|
+
)}
|
|
4108
|
+
</Box>
|
|
3812
4109
|
);
|
|
3813
4110
|
});
|