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,15 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Box, ChartSurface, ScrollBox, Text } from "../../ui";
|
|
2
|
+
import { TextAttributes, type BoxRenderable, type NativeRendererHost as CliRenderer } from "../../ui";
|
|
3
|
+
import { useNativeRenderer, useUiCapabilities } from "../../ui";
|
|
4
|
+
import { useShortcut } from "../../react/input";
|
|
3
5
|
import { memo, useEffect, useMemo, useRef, useState } from "react";
|
|
4
6
|
import { useChartQueries } from "../../market-data/hooks";
|
|
5
7
|
import { buildChartKey } from "../../market-data/selectors";
|
|
6
|
-
import {
|
|
8
|
+
import { useAppSelector, usePaneSettingValue } from "../../state/app-context";
|
|
9
|
+
import { usePaneFooter, type PaneHint } from "../layout/pane-footer";
|
|
7
10
|
import { blendHex, colors, getComparisonSeriesColor, priceColor } from "../../theme/colors";
|
|
8
11
|
import type { BrokerContractRef } from "../../types/instrument";
|
|
9
12
|
import type { PricePoint } from "../../types/financials";
|
|
10
13
|
import { formatPercentRaw } from "../../utils/format";
|
|
11
14
|
import { formatMarketPriceWithCurrency, formatSignedMarketPrice } from "../../utils/market-format";
|
|
12
|
-
import {
|
|
15
|
+
import { getSharedMarketData } from "../../plugins/registry";
|
|
13
16
|
import {
|
|
14
17
|
applyComparisonZoomAroundAnchor,
|
|
15
18
|
getMaxComparisonPanOffset,
|
|
@@ -91,7 +94,6 @@ import { resolveChartRendererState } from "./native/renderer-selection";
|
|
|
91
94
|
import { getNativeSurfaceManager } from "./native/surface-manager";
|
|
92
95
|
import { syncCachedNativeSurface } from "./native/surface-sync";
|
|
93
96
|
import { formatAxisCell, formatDateShort, resolveChartAxisWidth, type StyledContent } from "./chart-renderer";
|
|
94
|
-
import { ChartControlHint } from "./chart-control-hint";
|
|
95
97
|
|
|
96
98
|
const MODE_CHIPS: Record<ComparisonChartRenderMode, string> = {
|
|
97
99
|
area: "A",
|
|
@@ -552,7 +554,8 @@ function ComparisonStockChartView({
|
|
|
552
554
|
symbolSources,
|
|
553
555
|
onOpenSymbol,
|
|
554
556
|
}: ComparisonStockChartViewProps) {
|
|
555
|
-
const renderer =
|
|
557
|
+
const renderer = useNativeRenderer();
|
|
558
|
+
const { canvasCharts, cellWidthPx = 8, cellHeightPx = 18, pixelRatio = 1 } = useUiCapabilities();
|
|
556
559
|
const nativeSurfaceManager = useMemo(() => getNativeSurfaceManager(renderer), [renderer]);
|
|
557
560
|
const [storedRangePreset] = usePaneSettingValue("rangePreset", DEFAULT_COMPARISON_CHART_RANGE_PRESET);
|
|
558
561
|
const [storedResolution] = usePaneSettingValue<ChartResolution>("chartResolution", DEFAULT_COMPARISON_CHART_RESOLUTION);
|
|
@@ -573,6 +576,7 @@ function ComparisonStockChartView({
|
|
|
573
576
|
const supportMap = useMemo(() => buildChartResolutionSupportMap(resolutionSupport ?? []), [resolutionSupport]);
|
|
574
577
|
const [kittySupport, setKittySupport] = useState<boolean | null>(() => getCachedKittySupport(renderer));
|
|
575
578
|
const [displayCursor, setDisplayCursor] = useState<DisplayCursorState>(EMPTY_DISPLAY_CURSOR);
|
|
579
|
+
const [canvasBaseBitmapState, setCanvasBaseBitmapState] = useState<{ key: string; bitmap: NativeChartBitmap } | null>(null);
|
|
576
580
|
const plotRef = useRef<BoxRenderable | null>(null);
|
|
577
581
|
const nativeBaseSurfaceIdRef = useRef(`comparison-chart-surface:${paneId}:base`);
|
|
578
582
|
const nativeCrosshairSurfaceIdRef = useRef(`comparison-chart-surface:${paneId}:crosshair`);
|
|
@@ -580,6 +584,7 @@ function ComparisonStockChartView({
|
|
|
580
584
|
const lastNativeGeometryRef = useRef<{ rect: CellRect; visibleRect: CellRect | null } | null>(null);
|
|
581
585
|
const lastNativeBaseBitmapRef = useRef<{ key: string; bitmap: NativeChartBitmap } | null>(null);
|
|
582
586
|
const lastNativeCrosshairBitmapRef = useRef<{ key: string; bitmap: NativeChartBitmap } | null>(null);
|
|
587
|
+
const lastCanvasBaseBitmapRef = useRef<{ key: string; bitmap: NativeChartBitmap } | null>(null);
|
|
583
588
|
const displayCursorRef = useRef<DisplayCursorState>(EMPTY_DISPLAY_CURSOR);
|
|
584
589
|
const targetCursorRef = useRef<DisplayCursorState>(EMPTY_DISPLAY_CURSOR);
|
|
585
590
|
const cursorMotionKindRef = useRef<ChartCursorMotionKind>("discrete");
|
|
@@ -596,7 +601,7 @@ function ComparisonStockChartView({
|
|
|
596
601
|
const headerRows = 1;
|
|
597
602
|
const controlRows = 2;
|
|
598
603
|
const timeAxisRows = 1;
|
|
599
|
-
const helpRows =
|
|
604
|
+
const helpRows = 0;
|
|
600
605
|
const legendColumns = getLegendColumns(width);
|
|
601
606
|
const legendNeededRows = symbols.length > 0 ? Math.ceil(symbols.length / legendColumns) : 0;
|
|
602
607
|
const legendRows = legendNeededRows > 0
|
|
@@ -734,7 +739,7 @@ function ComparisonStockChartView({
|
|
|
734
739
|
), [nativeSurfaceManager]);
|
|
735
740
|
|
|
736
741
|
useEffect(() => {
|
|
737
|
-
const provider =
|
|
742
|
+
const provider = getSharedMarketData();
|
|
738
743
|
if ((!provider?.getChartResolutionSupport && !provider?.getChartResolutionCapabilities) || symbolSources.length === 0) {
|
|
739
744
|
setResolutionSupport(null);
|
|
740
745
|
return;
|
|
@@ -1009,13 +1014,19 @@ function ComparisonStockChartView({
|
|
|
1009
1014
|
setViewState(nextState);
|
|
1010
1015
|
};
|
|
1011
1016
|
|
|
1017
|
+
const projectionViewState = useMemo(() => ({
|
|
1018
|
+
panOffset: viewState.panOffset,
|
|
1019
|
+
zoomLevel: viewState.zoomLevel,
|
|
1020
|
+
renderMode: viewState.renderMode,
|
|
1021
|
+
}), [viewState.panOffset, viewState.renderMode, viewState.zoomLevel]);
|
|
1022
|
+
|
|
1012
1023
|
const visibleWindow = useMemo(() => (
|
|
1013
|
-
getVisibleComparisonWindow(series,
|
|
1014
|
-
), [chartWidth,
|
|
1024
|
+
getVisibleComparisonWindow(series, projectionViewState, chartWidth)
|
|
1025
|
+
), [chartWidth, projectionViewState, series]);
|
|
1015
1026
|
|
|
1016
1027
|
const projection = useMemo(() => (
|
|
1017
|
-
projectComparisonChartData(series, chartWidth,
|
|
1018
|
-
), [axisMode, chartWidth,
|
|
1028
|
+
projectComparisonChartData(series, chartWidth, projectionViewState, axisMode)
|
|
1029
|
+
), [axisMode, chartWidth, projectionViewState, series]);
|
|
1019
1030
|
|
|
1020
1031
|
const cursorX = viewState.cursorX !== null ? clamp(viewState.cursorX, 0, chartWidth - 1) : null;
|
|
1021
1032
|
const cursorY = viewState.cursorY !== null ? clamp(viewState.cursorY, 0, chartHeight - 1) : null;
|
|
@@ -1054,6 +1065,8 @@ function ComparisonStockChartView({
|
|
|
1054
1065
|
|
|
1055
1066
|
const rendererState = resolveChartRendererState(preferredRenderer, kittySupport, renderer.resolution);
|
|
1056
1067
|
const effectiveRenderer: ResolvedChartRenderer = rendererState.renderer;
|
|
1068
|
+
const useCanvasChart = canvasCharts && effectiveRenderer !== "kitty";
|
|
1069
|
+
const showNativeUnavailable = rendererState.nativeUnavailable && !useCanvasChart;
|
|
1057
1070
|
|
|
1058
1071
|
const staticScene = useMemo(() => buildComparisonChartScene(projection, {
|
|
1059
1072
|
width: chartWidth,
|
|
@@ -1074,7 +1087,7 @@ function ComparisonStockChartView({
|
|
|
1074
1087
|
}), [chartColors, chartHeight, chartWidth, projection, viewState.selectedSymbol]);
|
|
1075
1088
|
|
|
1076
1089
|
const interactiveResult = useMemo(() => (
|
|
1077
|
-
effectiveRenderer === "kitty"
|
|
1090
|
+
effectiveRenderer === "kitty" || useCanvasChart
|
|
1078
1091
|
? null
|
|
1079
1092
|
: renderComparisonChart(projection, {
|
|
1080
1093
|
width: chartWidth,
|
|
@@ -1084,9 +1097,66 @@ function ComparisonStockChartView({
|
|
|
1084
1097
|
selectedSymbol: viewState.selectedSymbol,
|
|
1085
1098
|
colors: chartColors,
|
|
1086
1099
|
})
|
|
1087
|
-
), [chartColors, chartHeight, chartWidth, displayCursorX, displayCursorY, effectiveRenderer, projection, viewState.selectedSymbol]);
|
|
1100
|
+
), [chartColors, chartHeight, chartWidth, displayCursorX, displayCursorY, effectiveRenderer, projection, useCanvasChart, viewState.selectedSymbol]);
|
|
1101
|
+
|
|
1102
|
+
const result = effectiveRenderer === "kitty" || useCanvasChart ? staticResult : interactiveResult!;
|
|
1103
|
+
const timeAxisLabel = result.timeLabels || staticResult.timeLabels;
|
|
1104
|
+
const footerHints = useMemo<PaneHint[]>(() => {
|
|
1105
|
+
const zoomIn = () => {
|
|
1106
|
+
setViewState((current) => applyComparisonZoomAroundAnchor(
|
|
1107
|
+
clearActivePreset(current),
|
|
1108
|
+
current.zoomLevel * 1.5,
|
|
1109
|
+
RIGHT_EDGE_ANCHOR_RATIO,
|
|
1110
|
+
series,
|
|
1111
|
+
));
|
|
1112
|
+
};
|
|
1113
|
+
const resetView = () => {
|
|
1114
|
+
pendingCanonicalResetRef.current += 1;
|
|
1115
|
+
updateDisplayCursorTarget(EMPTY_DISPLAY_CURSOR, "discrete");
|
|
1116
|
+
setViewState((current) => ({
|
|
1117
|
+
...(resolveResolutionSelection(current, effectiveResolution, supportMap, visibleDateWindow) ?? current),
|
|
1118
|
+
panOffset: 0,
|
|
1119
|
+
zoomLevel: 1,
|
|
1120
|
+
cursorX: null,
|
|
1121
|
+
cursorY: null,
|
|
1122
|
+
}));
|
|
1123
|
+
};
|
|
1124
|
+
const cycleRange = () => {
|
|
1125
|
+
const currentIndex = TIME_RANGES.indexOf(viewState.presetRange);
|
|
1126
|
+
setRangePreset(TIME_RANGES[(currentIndex + 1) % TIME_RANGES.length] ?? TIME_RANGES[0]!);
|
|
1127
|
+
};
|
|
1128
|
+
|
|
1129
|
+
return [
|
|
1130
|
+
{
|
|
1131
|
+
id: "mode",
|
|
1132
|
+
key: "m",
|
|
1133
|
+
label: "ode",
|
|
1134
|
+
onPress: () => setViewState((current) => ({
|
|
1135
|
+
...current,
|
|
1136
|
+
renderMode: current.renderMode === "line" ? "area" : "line",
|
|
1137
|
+
})),
|
|
1138
|
+
},
|
|
1139
|
+
{
|
|
1140
|
+
id: "resolution",
|
|
1141
|
+
key: "r",
|
|
1142
|
+
label: "es",
|
|
1143
|
+
onPress: () => {
|
|
1144
|
+
const currentIndex = resolutionChips.indexOf(effectiveResolution);
|
|
1145
|
+
const nextResolution = resolutionChips[(currentIndex + 1) % resolutionChips.length] ?? "auto";
|
|
1146
|
+
setResolution(nextResolution);
|
|
1147
|
+
},
|
|
1148
|
+
},
|
|
1149
|
+
{ id: "zoom", key: "+/-", label: "zoom", onPress: zoomIn },
|
|
1150
|
+
{ id: "reset", key: "0", label: "reset", onPress: resetView },
|
|
1151
|
+
...(width >= 72 ? [{ id: "range", key: "1-7", label: "range", onPress: cycleRange }] : []),
|
|
1152
|
+
];
|
|
1153
|
+
}, [effectiveResolution, resolutionChips, series, supportMap, viewState.presetRange, visibleDateWindow, width]);
|
|
1154
|
+
|
|
1155
|
+
usePaneFooter("comparison-chart", () => ({
|
|
1156
|
+
order: 10,
|
|
1157
|
+
hints: footerHints,
|
|
1158
|
+
}), [footerHints]);
|
|
1088
1159
|
|
|
1089
|
-
const result = effectiveRenderer === "kitty" ? staticResult : interactiveResult!;
|
|
1090
1160
|
const liveScene = useMemo(() => (
|
|
1091
1161
|
effectiveRenderer === "kitty"
|
|
1092
1162
|
? staticScene
|
|
@@ -1309,7 +1379,7 @@ function ComparisonStockChartView({
|
|
|
1309
1379
|
rendererState.nativeReady,
|
|
1310
1380
|
]);
|
|
1311
1381
|
|
|
1312
|
-
|
|
1382
|
+
useShortcut((event) => {
|
|
1313
1383
|
if (!focused || symbols.length === 0) return;
|
|
1314
1384
|
|
|
1315
1385
|
switch (event.name) {
|
|
@@ -1575,12 +1645,13 @@ function ComparisonStockChartView({
|
|
|
1575
1645
|
};
|
|
1576
1646
|
|
|
1577
1647
|
if (symbols.length === 0) {
|
|
1578
|
-
return <
|
|
1648
|
+
return <Text fg={colors.textDim}>No comparison tickers configured.</Text>;
|
|
1579
1649
|
}
|
|
1580
1650
|
|
|
1581
1651
|
const hasChartData = series.some((entry) => entry.points.length > 0);
|
|
1582
|
-
const
|
|
1583
|
-
const
|
|
1652
|
+
const cursorScene = effectiveRenderer === "kitty" ? staticScene : liveScene;
|
|
1653
|
+
const selectedSeries = hasChartData ? (cursorScene?.selectedSeries ?? staticResult.selectedSeries) : null;
|
|
1654
|
+
const selectedPoint = hasChartData ? (cursorScene?.selectedPoint ?? staticResult.selectedPoint) : null;
|
|
1584
1655
|
const selectedRawValue = selectedPoint?.rawValue ?? selectedSeries?.latestRawValue ?? null;
|
|
1585
1656
|
const selectedBaseValue = selectedSeries?.baseValue ?? null;
|
|
1586
1657
|
const selectedChange = selectedRawValue !== null && selectedBaseValue !== null
|
|
@@ -1590,12 +1661,14 @@ function ComparisonStockChartView({
|
|
|
1590
1661
|
? (selectedChange / selectedBaseValue) * 100
|
|
1591
1662
|
: null;
|
|
1592
1663
|
const selectedCurrency = selectedSeries?.currency ?? "USD";
|
|
1593
|
-
const displayDate =
|
|
1664
|
+
const displayDate = cursorScene?.activeDate ?? staticResult.activeDate;
|
|
1594
1665
|
const visiblePriceRange = result.priceRange ?? staticResult.priceRange ?? undefined;
|
|
1595
1666
|
const axisLabels = new Map(staticResult.axisLabels.map((entry) => [entry.row, entry.label]));
|
|
1596
|
-
const
|
|
1667
|
+
const cursorRow = useCanvasChart ? cursorScene?.cursorRow ?? null : result.cursorRow;
|
|
1668
|
+
const crosshairValue = useCanvasChart ? cursorScene?.crosshairValue ?? null : result.crosshairValue;
|
|
1669
|
+
const cursorAxisLabel = cursorRow !== null && crosshairValue !== null
|
|
1597
1670
|
? formatComparisonCursorAxisValue(
|
|
1598
|
-
|
|
1671
|
+
crosshairValue,
|
|
1599
1672
|
projection.effectiveAxisMode,
|
|
1600
1673
|
visiblePriceRange,
|
|
1601
1674
|
)
|
|
@@ -1605,57 +1678,188 @@ function ComparisonStockChartView({
|
|
|
1605
1678
|
end: visibleWindow.dates[visibleWindow.dates.length - 1] ?? null,
|
|
1606
1679
|
});
|
|
1607
1680
|
|
|
1681
|
+
const canvasBitmapSize = useMemo(() => {
|
|
1682
|
+
if (!canvasCharts) return null;
|
|
1683
|
+
const resolutionScale = Math.max(1, pixelRatio);
|
|
1684
|
+
return {
|
|
1685
|
+
pixelWidth: Math.max(1, Math.round(chartWidth * cellWidthPx * resolutionScale)),
|
|
1686
|
+
pixelHeight: Math.max(1, Math.round(chartHeight * cellHeightPx * resolutionScale)),
|
|
1687
|
+
};
|
|
1688
|
+
}, [canvasCharts, cellHeightPx, cellWidthPx, chartHeight, chartWidth, pixelRatio]);
|
|
1689
|
+
|
|
1690
|
+
const canvasProjection = useMemo(() => (
|
|
1691
|
+
canvasBitmapSize
|
|
1692
|
+
? projectComparisonChartData(
|
|
1693
|
+
series,
|
|
1694
|
+
Math.max(chartWidth, canvasBitmapSize.pixelWidth),
|
|
1695
|
+
projectionViewState,
|
|
1696
|
+
axisMode,
|
|
1697
|
+
)
|
|
1698
|
+
: null
|
|
1699
|
+
), [axisMode, canvasBitmapSize, chartWidth, projectionViewState, series]);
|
|
1700
|
+
|
|
1701
|
+
const canvasBaseScene = useMemo(() => (
|
|
1702
|
+
canvasProjection
|
|
1703
|
+
? buildComparisonChartScene(canvasProjection, {
|
|
1704
|
+
width: Math.max(chartWidth, canvasBitmapSize?.pixelWidth ?? chartWidth),
|
|
1705
|
+
height: chartHeight,
|
|
1706
|
+
cursorX: null,
|
|
1707
|
+
cursorY: null,
|
|
1708
|
+
selectedSymbol: viewState.selectedSymbol,
|
|
1709
|
+
colors: chartColors,
|
|
1710
|
+
})
|
|
1711
|
+
: null
|
|
1712
|
+
), [canvasBitmapSize?.pixelWidth, canvasProjection, chartColors, chartHeight, chartWidth, viewState.selectedSymbol]);
|
|
1713
|
+
|
|
1714
|
+
const canvasBaseBitmapKey = useMemo(() => {
|
|
1715
|
+
if (!canvasBitmapSize || !canvasProjection || !hasChartData || isBlockingBody || bodyMessage) return null;
|
|
1716
|
+
return buildComparisonNativeBitmapKey(
|
|
1717
|
+
symbols.length,
|
|
1718
|
+
canvasProjection,
|
|
1719
|
+
viewState.selectedSymbol,
|
|
1720
|
+
canvasBitmapSize.pixelWidth,
|
|
1721
|
+
canvasBitmapSize.pixelHeight,
|
|
1722
|
+
[chartColors.bgColor, chartColors.gridColor, chartColors.crosshairColor].join(","),
|
|
1723
|
+
);
|
|
1724
|
+
}, [
|
|
1725
|
+
bodyMessage,
|
|
1726
|
+
canvasBitmapSize,
|
|
1727
|
+
canvasProjection,
|
|
1728
|
+
chartColors.bgColor,
|
|
1729
|
+
chartColors.crosshairColor,
|
|
1730
|
+
chartColors.gridColor,
|
|
1731
|
+
hasChartData,
|
|
1732
|
+
isBlockingBody,
|
|
1733
|
+
symbols.length,
|
|
1734
|
+
viewState.selectedSymbol,
|
|
1735
|
+
]);
|
|
1736
|
+
|
|
1737
|
+
const canvasBaseBitmap = useMemo<NativeChartBitmap | null>(() => {
|
|
1738
|
+
return canvasBaseBitmapKey && canvasBaseBitmapState?.key === canvasBaseBitmapKey
|
|
1739
|
+
? canvasBaseBitmapState.bitmap
|
|
1740
|
+
: null;
|
|
1741
|
+
}, [canvasBaseBitmapKey, canvasBaseBitmapState]);
|
|
1742
|
+
|
|
1743
|
+
const visibleCanvasBaseBitmap = useMemo<NativeChartBitmap | null>(() => {
|
|
1744
|
+
if (canvasBaseBitmap) return canvasBaseBitmap;
|
|
1745
|
+
if (!canvasBaseBitmapKey || !canvasBitmapSize || !canvasBaseBitmapState) return null;
|
|
1746
|
+
return canvasBaseBitmapState.bitmap.width === canvasBitmapSize.pixelWidth
|
|
1747
|
+
&& canvasBaseBitmapState.bitmap.height === canvasBitmapSize.pixelHeight
|
|
1748
|
+
? canvasBaseBitmapState.bitmap
|
|
1749
|
+
: null;
|
|
1750
|
+
}, [canvasBaseBitmap, canvasBaseBitmapKey, canvasBaseBitmapState, canvasBitmapSize]);
|
|
1751
|
+
|
|
1752
|
+
useEffect(() => {
|
|
1753
|
+
if (!canvasBitmapSize || !canvasBaseBitmapKey || !canvasBaseScene) {
|
|
1754
|
+
lastCanvasBaseBitmapRef.current = null;
|
|
1755
|
+
setCanvasBaseBitmapState((current) => (current === null ? current : null));
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
const cachedBitmap = lastCanvasBaseBitmapRef.current?.key === canvasBaseBitmapKey
|
|
1760
|
+
? lastCanvasBaseBitmapRef.current.bitmap
|
|
1761
|
+
: null;
|
|
1762
|
+
if (cachedBitmap) {
|
|
1763
|
+
setCanvasBaseBitmapState((current) => (
|
|
1764
|
+
current?.key === canvasBaseBitmapKey ? current : { key: canvasBaseBitmapKey, bitmap: cachedBitmap }
|
|
1765
|
+
));
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
let cancelled = false;
|
|
1770
|
+
const frame = requestAnimationFrameSafe(() => {
|
|
1771
|
+
const bitmap = renderNativeComparisonChartBase(canvasBaseScene, canvasBitmapSize.pixelWidth, canvasBitmapSize.pixelHeight);
|
|
1772
|
+
if (cancelled) return;
|
|
1773
|
+
lastCanvasBaseBitmapRef.current = { key: canvasBaseBitmapKey, bitmap };
|
|
1774
|
+
setCanvasBaseBitmapState((current) => (
|
|
1775
|
+
current?.key === canvasBaseBitmapKey ? current : { key: canvasBaseBitmapKey, bitmap }
|
|
1776
|
+
));
|
|
1777
|
+
});
|
|
1778
|
+
|
|
1779
|
+
return () => {
|
|
1780
|
+
cancelled = true;
|
|
1781
|
+
cancelAnimationFrameSafe(frame);
|
|
1782
|
+
};
|
|
1783
|
+
}, [canvasBaseBitmapKey, canvasBaseScene, canvasBitmapSize]);
|
|
1784
|
+
|
|
1785
|
+
const canvasCrosshair = useMemo(() => {
|
|
1786
|
+
if (!canvasBitmapSize || !visibleCanvasBaseBitmap || !nativeCrosshair) return null;
|
|
1787
|
+
const renderablePixelSize = getRenderablePixelSize(plotRef.current, renderer);
|
|
1788
|
+
const overlayPixelX = scaleLocalPixelCoordinate(
|
|
1789
|
+
nativeCrosshair.pixelX,
|
|
1790
|
+
renderablePixelSize?.pixelWidth ?? canvasBitmapSize.pixelWidth,
|
|
1791
|
+
canvasBitmapSize.pixelWidth,
|
|
1792
|
+
);
|
|
1793
|
+
const overlayPixelY = scaleLocalPixelCoordinate(
|
|
1794
|
+
nativeCrosshair.pixelY,
|
|
1795
|
+
renderablePixelSize?.pixelHeight ?? canvasBitmapSize.pixelHeight,
|
|
1796
|
+
canvasBitmapSize.pixelHeight,
|
|
1797
|
+
);
|
|
1798
|
+
if (overlayPixelX === null || overlayPixelY === null) return null;
|
|
1799
|
+
return {
|
|
1800
|
+
pixelX: overlayPixelX,
|
|
1801
|
+
pixelY: overlayPixelY,
|
|
1802
|
+
color: nativeCrosshair.colors.crosshairColor,
|
|
1803
|
+
};
|
|
1804
|
+
}, [canvasBitmapSize, nativeCrosshair, renderer, visibleCanvasBaseBitmap]);
|
|
1805
|
+
|
|
1806
|
+
const plotBitmaps = useMemo(() => {
|
|
1807
|
+
if (!visibleCanvasBaseBitmap) return null;
|
|
1808
|
+
return [visibleCanvasBaseBitmap];
|
|
1809
|
+
}, [visibleCanvasBaseBitmap]);
|
|
1810
|
+
|
|
1608
1811
|
const plotLines: Array<string | StyledContent> = effectiveRenderer === "kitty"
|
|
1609
1812
|
? blankPlotLines
|
|
1610
1813
|
: result.lines;
|
|
1611
|
-
const plotContent =
|
|
1612
|
-
|
|
1613
|
-
|
|
1814
|
+
const plotContent = plotBitmaps || (useCanvasChart && canvasBaseBitmapKey)
|
|
1815
|
+
? null
|
|
1816
|
+
: plotLines.map((line, index) => (
|
|
1817
|
+
<Text key={index} content={line as unknown as string} />
|
|
1818
|
+
));
|
|
1614
1819
|
|
|
1615
1820
|
const plotBox = (
|
|
1616
|
-
<
|
|
1821
|
+
<ChartSurface
|
|
1617
1822
|
ref={plotRef}
|
|
1618
1823
|
width={chartWidth}
|
|
1619
1824
|
height={chartHeight}
|
|
1620
1825
|
flexDirection="column"
|
|
1621
|
-
|
|
1826
|
+
bitmaps={plotBitmaps}
|
|
1827
|
+
crosshair={canvasCrosshair}
|
|
1622
1828
|
onMouseMove={hasChartData && !isBlockingBody && !bodyMessage ? handlePlotMove : undefined}
|
|
1623
1829
|
onMouseDown={hasChartData && !isBlockingBody && !bodyMessage ? handlePlotDown : undefined}
|
|
1624
1830
|
onMouseUp={hasChartData && !isBlockingBody && !bodyMessage ? () => { dragRef.current = null; } : undefined}
|
|
1625
1831
|
onMouseDrag={hasChartData && !isBlockingBody && !bodyMessage ? handlePlotDrag : undefined}
|
|
1626
1832
|
onMouseDragEnd={hasChartData && !isBlockingBody && !bodyMessage ? () => { dragRef.current = null; } : undefined}
|
|
1627
|
-
|
|
1628
|
-
dragRef.current = null;
|
|
1629
|
-
} : undefined}
|
|
1833
|
+
onMouseScroll={hasChartData && !isBlockingBody && !bodyMessage ? handlePlotScroll : undefined}
|
|
1630
1834
|
>
|
|
1631
1835
|
{isBlockingBody
|
|
1632
1836
|
? (
|
|
1633
|
-
<
|
|
1634
|
-
<
|
|
1635
|
-
</
|
|
1837
|
+
<Box flexGrow={1} alignItems="center" justifyContent="center">
|
|
1838
|
+
<Text fg={colors.textDim}>Loading chart...</Text>
|
|
1839
|
+
</Box>
|
|
1636
1840
|
)
|
|
1637
1841
|
: bodyMessage
|
|
1638
1842
|
? (
|
|
1639
|
-
<
|
|
1640
|
-
<
|
|
1641
|
-
</
|
|
1843
|
+
<Box flexGrow={1} alignItems="center" justifyContent="center">
|
|
1844
|
+
<Text fg={colors.textDim}>{bodyMessage}</Text>
|
|
1845
|
+
</Box>
|
|
1642
1846
|
)
|
|
1643
1847
|
: plotContent}
|
|
1644
|
-
</
|
|
1848
|
+
</ChartSurface>
|
|
1645
1849
|
);
|
|
1646
1850
|
|
|
1647
1851
|
const axisBox = (
|
|
1648
|
-
<
|
|
1852
|
+
<Box width={axisSectionWidth} height={chartHeight} flexDirection="column">
|
|
1649
1853
|
{Array.from({ length: chartHeight }, (_, row) => {
|
|
1650
|
-
const isCursorRow = cursorAxisLabel !== null &&
|
|
1854
|
+
const isCursorRow = cursorAxisLabel !== null && cursorRow === row;
|
|
1651
1855
|
const label = isCursorRow ? cursorAxisLabel : (axisLabels.get(row) ?? null);
|
|
1652
1856
|
return (
|
|
1653
|
-
<
|
|
1857
|
+
<Text key={row} fg={isCursorRow ? chartColors.crosshairColor : colors.textDim}>
|
|
1654
1858
|
{formatAxisCell(label, axisWidth).padEnd(axisSectionWidth)}
|
|
1655
|
-
</
|
|
1859
|
+
</Text>
|
|
1656
1860
|
);
|
|
1657
1861
|
})}
|
|
1658
|
-
</
|
|
1862
|
+
</Box>
|
|
1659
1863
|
);
|
|
1660
1864
|
|
|
1661
1865
|
const legendRowsData = Array.from({ length: Math.ceil(symbols.length / legendColumns) }, (_, rowIndex) => (
|
|
@@ -1663,17 +1867,17 @@ function ComparisonStockChartView({
|
|
|
1663
1867
|
));
|
|
1664
1868
|
|
|
1665
1869
|
return (
|
|
1666
|
-
<
|
|
1870
|
+
<Box
|
|
1667
1871
|
flexDirection="column"
|
|
1668
1872
|
flexGrow={1}
|
|
1669
1873
|
onMouseScroll={hasChartData && !isBlockingBody && !bodyMessage ? handlePlotScroll : undefined}
|
|
1670
1874
|
>
|
|
1671
|
-
<
|
|
1672
|
-
<
|
|
1875
|
+
<Box flexDirection="row" gap={2} height={1}>
|
|
1876
|
+
<Text attributes={TextAttributes.BOLD} fg={colors.textBright}>
|
|
1673
1877
|
{viewState.selectedSymbol ?? "Compare"} - {getChartResolutionLabel(effectiveResolution)}
|
|
1674
|
-
</
|
|
1675
|
-
<
|
|
1676
|
-
<
|
|
1878
|
+
</Text>
|
|
1879
|
+
<Text fg={colors.textDim}>{visibleLabel}</Text>
|
|
1880
|
+
<Text fg={selectedChange !== null ? priceColor(selectedChange) : colors.textDim}>
|
|
1677
1881
|
{selectedRawValue !== null
|
|
1678
1882
|
? formatMarketPriceWithCurrency(selectedRawValue, selectedCurrency, {
|
|
1679
1883
|
minimumFractionDigits: 2,
|
|
@@ -1681,8 +1885,8 @@ function ComparisonStockChartView({
|
|
|
1681
1885
|
priceRange: visiblePriceRange,
|
|
1682
1886
|
})
|
|
1683
1887
|
: "—"}
|
|
1684
|
-
</
|
|
1685
|
-
<
|
|
1888
|
+
</Text>
|
|
1889
|
+
<Text fg={selectedChange !== null ? priceColor(selectedChange) : colors.textDim}>
|
|
1686
1890
|
{selectedChange !== null && selectedChangePct !== null
|
|
1687
1891
|
? `${formatSignedMarketPrice(selectedChange, {
|
|
1688
1892
|
minimumFractionDigits: 2,
|
|
@@ -1690,14 +1894,14 @@ function ComparisonStockChartView({
|
|
|
1690
1894
|
priceRange: visiblePriceRange,
|
|
1691
1895
|
})} (${formatPercentRaw(selectedChangePct)})`
|
|
1692
1896
|
: "Waiting for data"}
|
|
1693
|
-
</
|
|
1694
|
-
{displayDate && <
|
|
1695
|
-
</
|
|
1897
|
+
</Text>
|
|
1898
|
+
{displayDate && <Text fg={colors.textDim}>{formatDateShort(displayDate)}</Text>}
|
|
1899
|
+
</Box>
|
|
1696
1900
|
|
|
1697
|
-
<
|
|
1698
|
-
<
|
|
1901
|
+
<Box flexDirection="row" height={1}>
|
|
1902
|
+
<Box flexDirection="row" gap={1}>
|
|
1699
1903
|
{TIME_RANGES.map((range, index) => (
|
|
1700
|
-
<
|
|
1904
|
+
<Text
|
|
1701
1905
|
key={range}
|
|
1702
1906
|
fg={activePreset === range ? colors.textBright : (isRangePresetSupported(range, availableManualResolutions) ? colors.textDim : colors.textMuted)}
|
|
1703
1907
|
attributes={activePreset === range ? TextAttributes.BOLD : 0}
|
|
@@ -1708,49 +1912,49 @@ function ComparisonStockChartView({
|
|
|
1708
1912
|
}}
|
|
1709
1913
|
>
|
|
1710
1914
|
{`${index + 1}:${range}`}
|
|
1711
|
-
</
|
|
1915
|
+
</Text>
|
|
1712
1916
|
))}
|
|
1713
|
-
</
|
|
1714
|
-
</
|
|
1917
|
+
</Box>
|
|
1918
|
+
</Box>
|
|
1715
1919
|
|
|
1716
|
-
<
|
|
1717
|
-
<
|
|
1920
|
+
<Box flexDirection="row" height={1}>
|
|
1921
|
+
<Box flexDirection="row" gap={1}>
|
|
1718
1922
|
{resolutionChips.map((resolution) => (
|
|
1719
|
-
<
|
|
1923
|
+
<Text
|
|
1720
1924
|
key={resolution}
|
|
1721
1925
|
fg={effectiveResolution === resolution ? colors.textBright : colors.textDim}
|
|
1722
1926
|
attributes={effectiveResolution === resolution ? TextAttributes.BOLD : 0}
|
|
1723
1927
|
onMouseDown={() => setResolution(resolution)}
|
|
1724
1928
|
>
|
|
1725
1929
|
{getChartResolutionLabel(resolution)}
|
|
1726
|
-
</
|
|
1930
|
+
</Text>
|
|
1727
1931
|
))}
|
|
1728
1932
|
{isUpdating && (
|
|
1729
|
-
<
|
|
1933
|
+
<Text fg={colors.textDim}>updating</Text>
|
|
1730
1934
|
)}
|
|
1731
|
-
</
|
|
1732
|
-
<
|
|
1733
|
-
<
|
|
1935
|
+
</Box>
|
|
1936
|
+
<Box flexGrow={1} />
|
|
1937
|
+
<Box flexDirection="row" gap={1}>
|
|
1734
1938
|
{COMPARISON_RENDER_MODES.map((mode) => (
|
|
1735
|
-
<
|
|
1939
|
+
<Text
|
|
1736
1940
|
key={mode}
|
|
1737
1941
|
fg={viewState.renderMode === mode ? colors.textBright : colors.textDim}
|
|
1738
1942
|
attributes={viewState.renderMode === mode ? TextAttributes.BOLD : 0}
|
|
1739
1943
|
onMouseDown={() => setViewState((current) => ({ ...current, renderMode: mode }))}
|
|
1740
1944
|
>
|
|
1741
1945
|
{MODE_CHIPS[mode]}
|
|
1742
|
-
</
|
|
1946
|
+
</Text>
|
|
1743
1947
|
))}
|
|
1744
1948
|
{projection.warning && (
|
|
1745
|
-
<
|
|
1949
|
+
<Text fg={colors.textDim}>{projection.warning}</Text>
|
|
1746
1950
|
)}
|
|
1747
|
-
{
|
|
1748
|
-
<
|
|
1951
|
+
{showNativeUnavailable && (
|
|
1952
|
+
<Text fg={colors.textDim}>native unavailable</Text>
|
|
1749
1953
|
)}
|
|
1750
|
-
</
|
|
1751
|
-
</
|
|
1954
|
+
</Box>
|
|
1955
|
+
</Box>
|
|
1752
1956
|
|
|
1753
|
-
<
|
|
1957
|
+
<Box
|
|
1754
1958
|
flexDirection="row"
|
|
1755
1959
|
height={chartHeight}
|
|
1756
1960
|
gap={axisGap}
|
|
@@ -1758,17 +1962,17 @@ function ComparisonStockChartView({
|
|
|
1758
1962
|
>
|
|
1759
1963
|
{plotBox}
|
|
1760
1964
|
{axisBox}
|
|
1761
|
-
</
|
|
1965
|
+
</Box>
|
|
1762
1966
|
|
|
1763
|
-
<
|
|
1764
|
-
<
|
|
1765
|
-
</
|
|
1967
|
+
<Box height={timeAxisRows}>
|
|
1968
|
+
<Text fg={colors.textDim}>{timeAxisLabel}</Text>
|
|
1969
|
+
</Box>
|
|
1766
1970
|
|
|
1767
1971
|
{legendRows > 0 && (
|
|
1768
|
-
<
|
|
1769
|
-
<
|
|
1972
|
+
<ScrollBox height={legendRows} scrollY>
|
|
1973
|
+
<Box flexDirection="column">
|
|
1770
1974
|
{legendRowsData.map((legendRow, rowIndex) => (
|
|
1771
|
-
<
|
|
1975
|
+
<Box key={`legend-row:${rowIndex}`} flexDirection="row" gap={1}>
|
|
1772
1976
|
{legendRow.map((symbol) => {
|
|
1773
1977
|
const item = projection.series.find((entry) => entry.symbol === symbol) ?? null;
|
|
1774
1978
|
const isSelected = viewState.selectedSymbol === symbol;
|
|
@@ -1787,7 +1991,7 @@ function ComparisonStockChartView({
|
|
|
1787
1991
|
: `${symbol} waiting`;
|
|
1788
1992
|
|
|
1789
1993
|
return (
|
|
1790
|
-
<
|
|
1994
|
+
<Box
|
|
1791
1995
|
key={symbol}
|
|
1792
1996
|
width={legendItemWidth}
|
|
1793
1997
|
backgroundColor={isSelected ? blendHex(colors.panel, colors.borderFocused, 0.18) : colors.panel}
|
|
@@ -1797,59 +2001,51 @@ function ComparisonStockChartView({
|
|
|
1797
2001
|
onOpenSymbol(symbol);
|
|
1798
2002
|
}}
|
|
1799
2003
|
>
|
|
1800
|
-
<
|
|
2004
|
+
<Text fg={item?.color ?? colors.textDim} attributes={isSelected ? TextAttributes.BOLD : 0}>
|
|
1801
2005
|
{clipText(`${isSelected ? ">" : " "} ${summary}`, legendItemWidth)}
|
|
1802
|
-
</
|
|
1803
|
-
</
|
|
2006
|
+
</Text>
|
|
2007
|
+
</Box>
|
|
1804
2008
|
);
|
|
1805
2009
|
})}
|
|
1806
|
-
</
|
|
2010
|
+
</Box>
|
|
1807
2011
|
))}
|
|
1808
|
-
</
|
|
1809
|
-
</
|
|
2012
|
+
</Box>
|
|
2013
|
+
</ScrollBox>
|
|
1810
2014
|
)}
|
|
1811
2015
|
|
|
1812
|
-
|
|
1813
|
-
<box flexDirection="row" gap={1}>
|
|
1814
|
-
<ChartControlHint hotkey="m" label="ode" />
|
|
1815
|
-
<ChartControlHint hotkey="r" label="es" />
|
|
1816
|
-
<ChartControlHint hotkey="+/-" label="zoom" />
|
|
1817
|
-
<ChartControlHint hotkey="0" label="reset" />
|
|
1818
|
-
{width >= 72 && <ChartControlHint hotkey="1-7" label="range" />}
|
|
1819
|
-
{width >= 88 && <ChartControlHint hotkey="up/down" label="legend" />}
|
|
1820
|
-
</box>
|
|
1821
|
-
</box>
|
|
1822
|
-
</box>
|
|
2016
|
+
</Box>
|
|
1823
2017
|
);
|
|
1824
2018
|
}
|
|
1825
2019
|
|
|
1826
2020
|
const MemoizedComparisonStockChartView = memo(ComparisonStockChartView);
|
|
1827
2021
|
|
|
1828
2022
|
export function ComparisonStockChart(props: ComparisonStockChartProps) {
|
|
1829
|
-
const
|
|
2023
|
+
const tickers = useAppSelector((state) => state.tickers);
|
|
2024
|
+
const financials = useAppSelector((state) => state.financials);
|
|
2025
|
+
const chartPreferences = useAppSelector((state) => state.config.chartPreferences);
|
|
1830
2026
|
const symbolsKey = props.symbols.join("|");
|
|
1831
2027
|
const stableSymbols = useMemo(() => props.symbols, [symbolsKey]);
|
|
1832
2028
|
const symbolSources = useMemo<ComparisonChartSymbolSource[]>(() => stableSymbols.map((symbol) => {
|
|
1833
|
-
const ticker =
|
|
1834
|
-
const
|
|
2029
|
+
const ticker = tickers.get(symbol) ?? null;
|
|
2030
|
+
const financial = financials.get(symbol) ?? null;
|
|
1835
2031
|
const instrument = ticker?.metadata.broker_contracts?.[0] ?? null;
|
|
1836
2032
|
return {
|
|
1837
2033
|
symbol,
|
|
1838
|
-
currency:
|
|
2034
|
+
currency: financial?.quote?.currency ?? ticker?.metadata.currency,
|
|
1839
2035
|
exchange: ticker?.metadata.exchange ?? "",
|
|
1840
2036
|
brokerId: instrument?.brokerId,
|
|
1841
2037
|
brokerInstanceId: instrument?.brokerInstanceId,
|
|
1842
2038
|
instrument,
|
|
1843
|
-
priceHistory:
|
|
2039
|
+
priceHistory: financial?.priceHistory ?? [],
|
|
1844
2040
|
};
|
|
1845
|
-
}), [
|
|
2041
|
+
}), [financials, stableSymbols, tickers]);
|
|
1846
2042
|
|
|
1847
2043
|
return (
|
|
1848
2044
|
<MemoizedComparisonStockChartView
|
|
1849
2045
|
{...props}
|
|
1850
2046
|
symbols={stableSymbols}
|
|
1851
|
-
defaultRenderMode={
|
|
1852
|
-
preferredRenderer={
|
|
2047
|
+
defaultRenderMode={chartPreferences.defaultRenderMode}
|
|
2048
|
+
preferredRenderer={chartPreferences.renderer}
|
|
1853
2049
|
symbolSources={symbolSources}
|
|
1854
2050
|
/>
|
|
1855
2051
|
);
|