gloomberb 0.10.4 → 0.11.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/LICENSE +1 -1
- package/README.md +30 -3
- package/README.zh-CN.md +1 -1
- package/package.json +5 -5
- package/src/api-client/auth.ts +34 -0
- package/src/api-client/data.ts +25 -0
- package/src/api-client/index.test.ts +81 -0
- package/src/api-client/index.ts +37 -0
- package/src/api-client/request.test.ts +30 -0
- package/src/api-client/request.ts +44 -20
- package/src/api-client/socket-health.test.ts +79 -0
- package/src/api-client/socket.ts +108 -5
- package/src/api-client/types.ts +139 -0
- package/src/app/global-shortcuts.test.tsx +144 -9
- package/src/app/global-shortcuts.ts +33 -11
- package/src/app/runtime/broker-import.ts +39 -5
- package/src/app/runtime/startup.ts +7 -1
- package/src/app/runtime/update.ts +26 -1
- package/src/app.tsx +36 -19
- package/src/brokers/broker-ticker-sync.ts +3 -8
- package/src/brokers/sync-broker-instance.test.ts +59 -0
- package/src/brokers/sync-broker-instance.ts +37 -13
- package/src/capabilities/chart-series.ts +318 -0
- package/src/capabilities/factories.ts +46 -0
- package/src/capabilities/index.ts +1 -0
- package/src/capabilities/registry.test.ts +135 -0
- package/src/capabilities/registry.ts +31 -4
- package/src/capabilities/types.ts +52 -0
- package/src/cli/commands/overview.ts +2 -2
- package/src/cli/desktop-pane-shot.ts +2 -0
- package/src/cli/entry.ts +11 -1
- package/src/cli/native-smoke.ts +5 -0
- package/src/cli/pane-functions/catalog.ts +4 -0
- package/src/cli/pane-functions/data.ts +6 -1
- package/src/cli/pane-functions/discovery.ts +76 -10
- package/src/cli/pane-functions/report.test.ts +76 -0
- package/src/cli/pane-functions/report.ts +7 -1
- package/src/cli/pane-functions/resolver.ts +4 -1
- package/src/cli/pane-functions/screenshot.test.ts +56 -3
- package/src/cli/pane-functions/screenshot.ts +68 -5
- package/src/components/chart/composite/composite-chart.test.tsx +54 -0
- package/src/components/chart/composite/composite-chart.tsx +92 -15
- package/src/components/chart/composite/downsample.test.ts +136 -0
- package/src/components/chart/composite/downsample.ts +208 -0
- package/src/components/chart/composite/panel-series.test.ts +66 -0
- package/src/components/chart/composite/panel-series.ts +101 -0
- package/src/components/chart/multi-line/renderer.ts +1 -0
- package/src/components/chart/native/kitty/index.test.ts +25 -3
- package/src/components/chart/native/kitty/support.ts +37 -3
- package/src/components/chart/native/use-chart-text-fallback.ts +19 -0
- package/src/components/chart/static/chart/surface.tsx +43 -12
- package/src/components/chart/static/multi-line-chart-surface.tsx +41 -5
- package/src/components/command-bar/commands/direct/index.ts +18 -0
- package/src/components/command-bar/commands/registry.ts +14 -0
- package/src/components/command-bar/routes/root/article-results.test.ts +83 -0
- package/src/components/command-bar/routes/root/article-results.ts +55 -0
- package/src/components/command-bar/routes/root/command-items.ts +3 -0
- package/src/components/command-bar/routes/root/provider-search.ts +7 -3
- package/src/components/command-bar/routes/root/results.ts +38 -11
- package/src/components/command-bar/routes/root/runtime.ts +6 -2
- package/src/components/command-bar/routes/ticker-search/actions.ts +1 -1
- package/src/components/command-bar/routes/ticker-search/results.test.ts +34 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +4 -6
- package/src/components/command-bar/routes/ticker-search/route.ts +2 -2
- package/src/components/command-bar/routing/list-state.ts +2 -1
- package/src/components/command-bar/surface/assist.test.tsx +13 -6
- package/src/components/command-bar/surface/index.test.tsx +65 -0
- package/src/components/command-bar/surface/index.tsx +35 -0
- package/src/components/command-bar/view-model.test.ts +43 -0
- package/src/components/command-bar/view-model.ts +41 -4
- package/src/components/command-bar/workflow/collection-actions.test.ts +94 -0
- package/src/components/command-bar/workflow/collection-actions.ts +14 -3
- package/src/components/command-bar/workflow/ops.test.ts +136 -0
- package/src/components/command-bar/workflow/ops.ts +71 -1
- package/src/components/index.ts +1 -1
- package/src/components/layout/header.test.tsx +35 -0
- package/src/components/layout/header.tsx +101 -9
- package/src/components/layout/pane/cycle-order.test.ts +0 -1
- package/src/components/layout/pane/footer/index.test.tsx +19 -19
- package/src/components/layout/pane/header.tsx +2 -13
- package/src/components/layout/shell/index.tsx +3 -0
- package/src/components/layout/status-bar.test.tsx +36 -1
- package/src/components/layout/status-bar.tsx +48 -2
- package/src/components/onboarding/account-step/chooser-panel.tsx +27 -50
- package/src/components/onboarding/account-step/form-panel.tsx +85 -29
- package/src/components/onboarding/account-step/index.tsx +10 -1
- package/src/components/onboarding/account-step/model.ts +3 -3
- package/src/components/onboarding/account-step/qr-panel.tsx +68 -0
- package/src/components/onboarding/account-step/signed-in-panel.tsx +4 -7
- package/src/components/onboarding/onboarding-frame.tsx +517 -0
- package/src/components/onboarding/onboarding-steps.tsx +0 -264
- package/src/components/onboarding/onboarding-wizard.test.tsx +626 -200
- package/src/components/onboarding/onboarding-wizard.tsx +848 -266
- package/src/components/onboarding/portfolio-step/broker-fields-panel.tsx +81 -51
- package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +14 -20
- package/src/components/onboarding/portfolio-step/broker-sync-panel.tsx +8 -11
- package/src/components/onboarding/portfolio-step/choose-panel.tsx +18 -22
- package/src/components/onboarding/portfolio-step/index.tsx +3 -0
- package/src/components/onboarding/portfolio-step/types.ts +3 -0
- package/src/components/onboarding/wizard-account.ts +38 -3
- package/src/components/onboarding/wizard-broker-sync.ts +45 -67
- package/src/components/onboarding/wizard-model.ts +18 -2
- package/src/components/ui/data-table/opentui/index.tsx +29 -9
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/external-link.tsx +4 -2
- package/src/components/ui/loading.tsx +1 -1
- package/src/components/ui/status.tsx +76 -4
- package/src/components/ui/table-layout.test.ts +24 -0
- package/src/components/ui/table-layout.ts +90 -23
- package/src/components/ui/tabs.tsx +86 -4
- package/src/core/app-services.ts +14 -3
- package/src/core/connection-health.test.ts +143 -0
- package/src/core/connection-health.ts +295 -0
- package/src/core/state/app/layout-reducer.ts +34 -0
- package/src/core/state/app/layout.ts +21 -0
- package/src/core/state/app/state.ts +10 -0
- package/src/core/state/app/types.ts +7 -1
- package/src/data/config/chart-settings.ts +11 -2
- package/src/data/config/store/index.test.ts +83 -9
- package/src/data/config/store/migrations.ts +16 -0
- package/src/data/config/store/normalize.ts +59 -1
- package/src/data/fred-series.ts +8 -2
- package/src/i18n/es.ts +126 -0
- package/src/i18n/ja.ts +126 -0
- package/src/i18n/ko.ts +126 -0
- package/src/i18n/zh-cn.ts +126 -0
- package/src/i18n/zh-tw.ts +126 -0
- package/src/market-data/coordinator/chart.ts +3 -2
- package/src/market-data/hooks.tsx +3 -2
- package/src/market-data/market/status.test.ts +12 -0
- package/src/market-data/market/status.ts +34 -17
- package/src/news/aggregator.test.ts +39 -0
- package/src/news/aggregator.ts +78 -22
- package/src/news/hooks.ts +4 -0
- package/src/notifications/app-notifier.test.ts +28 -6
- package/src/notifications/app-notifier.ts +24 -5
- package/src/plugins/broker-sync/broker-sync.test.ts +130 -0
- package/src/plugins/broker-sync/native-loader.ts +10 -0
- package/src/plugins/broker-sync/normalize.ts +271 -0
- package/src/plugins/broker-sync/public.ts +122 -0
- package/src/plugins/broker-sync/robinhood-native.ts +367 -0
- package/src/plugins/broker-sync/robinhood.ts +114 -0
- package/src/plugins/broker-sync/simplefin-native.ts +201 -0
- package/src/plugins/broker-sync/simplefin.ts +70 -0
- package/src/plugins/builtin/account-management/footer.ts +8 -19
- package/src/plugins/builtin/account-management/pane.tsx +13 -8
- package/src/plugins/builtin/ai/pi/host.ts +6 -5
- package/src/plugins/builtin/ai/providers.ts +1 -1
- package/src/plugins/builtin/ai/screener/footer.ts +0 -7
- package/src/plugins/builtin/ai/screener/pane.test.tsx +2 -1
- package/src/plugins/builtin/ai/screener/pane.tsx +3 -1
- package/src/plugins/builtin/ai/screener/results-view.tsx +8 -0
- package/src/plugins/builtin/ai/screener/runner.ts +7 -3
- package/src/plugins/builtin/ai/workspace/pane.tsx +13 -2
- package/src/plugins/builtin/alerts/alert-engine.ts +13 -0
- package/src/plugins/builtin/alerts/constants.ts +2 -1
- package/src/plugins/builtin/alerts/index.tsx +67 -36
- package/src/plugins/builtin/alerts/pane.tsx +17 -65
- package/src/plugins/builtin/analytics/index.tsx +24 -11
- package/src/plugins/builtin/analytics/pane-model.ts +48 -14
- package/src/plugins/builtin/analytics/portfolio-selection.ts +19 -0
- package/src/plugins/builtin/analytics/view.tsx +5 -3
- package/src/plugins/builtin/broker-manager/detail.tsx +7 -2
- package/src/plugins/builtin/broker-manager/footer.ts +4 -9
- package/src/plugins/builtin/broker-manager/index.tsx +1 -6
- package/src/plugins/builtin/buildout/detail/ui.tsx +15 -4
- package/src/plugins/builtin/buildout/pane/header.tsx +1 -0
- package/src/plugins/builtin/changelog/index.tsx +60 -45
- package/src/plugins/builtin/chart-composer/catalog-inventory.test.ts +97 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +366 -0
- package/src/plugins/builtin/chart-composer/chart-spec.ts +21 -1
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +307 -0
- package/src/plugins/builtin/chart-composer/editor/controller.ts +1 -1
- package/src/plugins/builtin/chart-composer/index.tsx +45 -5
- package/src/plugins/builtin/chart-composer/pane.tsx +7 -88
- package/src/plugins/builtin/chart-composer/presets.test.ts +48 -0
- package/src/plugins/builtin/chart-composer/presets.ts +69 -3
- package/src/plugins/builtin/chart-composer/quick-add.tsx +8 -5
- package/src/plugins/builtin/chart-composer/semantic.ts +18 -14
- package/src/plugins/builtin/chart-composer/series-catalog.test.ts +39 -0
- package/src/plugins/builtin/chart-composer/series-catalog.ts +104 -3
- package/src/plugins/builtin/chart-composer/shortcuts.test.ts +10 -10
- package/src/plugins/builtin/chart-composer/shortcuts.ts +4 -12
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +230 -23
- package/src/plugins/builtin/chat/chat.test.tsx +22 -0
- package/src/plugins/builtin/chat/content/composer.tsx +6 -4
- package/src/plugins/builtin/chat/content/index.tsx +7 -0
- package/src/plugins/builtin/chat/content/snapshot.ts +9 -2
- package/src/plugins/builtin/chat/content/transcript.tsx +10 -0
- package/src/plugins/builtin/chat/controller/channel-actions.ts +11 -1
- package/src/plugins/builtin/chat/controller/channels.ts +13 -3
- package/src/plugins/builtin/chat/controller/index.ts +61 -7
- package/src/plugins/builtin/chat/controller/lifecycle.ts +3 -0
- package/src/plugins/builtin/chat/controller/message-loading.ts +5 -1
- package/src/plugins/builtin/chat/controller/messages.ts +3 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +32 -11
- package/src/plugins/builtin/chat/controller/state.ts +7 -0
- package/src/plugins/builtin/chat/controller/view.ts +1 -0
- package/src/plugins/builtin/chat/controller.test.ts +117 -28
- package/src/plugins/builtin/cloud/auth-commands.ts +18 -0
- package/src/plugins/builtin/cloud/device-signin-dialog.tsx +243 -0
- package/src/plugins/builtin/cloud/device-signin.test.ts +217 -0
- package/src/plugins/builtin/cloud/device-signin.ts +240 -0
- package/src/plugins/builtin/cloud/plugin.tsx +7 -1
- package/src/plugins/builtin/cloud-tweets/table.tsx +57 -10
- package/src/plugins/builtin/composite-plugins.ts +39 -5
- package/src/plugins/builtin/congress-trades/detail.tsx +2 -4
- package/src/plugins/builtin/congress-trades/footer.ts +7 -16
- package/src/plugins/builtin/congress-trades/pane.tsx +12 -20
- package/src/plugins/builtin/connections/index.ts +53 -0
- package/src/plugins/builtin/connections/pane.test.tsx +79 -0
- package/src/plugins/builtin/connections/pane.tsx +258 -0
- package/src/plugins/builtin/correlation/index.test.tsx +2 -1
- package/src/plugins/builtin/correlation/index.tsx +58 -19
- package/src/plugins/builtin/correlation/matrix/model.ts +18 -7
- package/src/plugins/builtin/correlation/relationship/pane.tsx +43 -19
- package/src/plugins/builtin/correlation/settings.ts +1 -1
- package/src/plugins/builtin/credit-conditions/client.test.ts +80 -0
- package/src/plugins/builtin/credit-conditions/client.ts +105 -0
- package/src/plugins/builtin/credit-conditions/index.test.tsx +111 -0
- package/src/plugins/builtin/credit-conditions/index.tsx +227 -0
- package/src/plugins/builtin/credit-conditions/model.test.ts +43 -0
- package/src/plugins/builtin/credit-conditions/model.ts +64 -0
- package/src/plugins/builtin/debug/index.tsx +11 -6
- package/src/plugins/builtin/dividend-yield/client.test.ts +58 -0
- package/src/plugins/builtin/dividend-yield/client.ts +252 -0
- package/src/plugins/builtin/dividend-yield/index.tsx +61 -0
- package/src/plugins/builtin/dividend-yield/model.ts +83 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +305 -0
- package/src/plugins/builtin/dividend-yield/types.ts +22 -0
- package/src/plugins/builtin/earnings/data/cache.test.ts +10 -7
- package/src/plugins/builtin/earnings/data/cache.ts +23 -9
- package/src/plugins/builtin/earnings/index.tsx +57 -6
- package/src/plugins/builtin/econ/calendar-model.test.ts +37 -3
- package/src/plugins/builtin/econ/calendar-model.ts +32 -26
- package/src/plugins/builtin/econ/fred-cache.test.ts +8 -1
- package/src/plugins/builtin/econ/fred-series-map.ts +18 -0
- package/src/plugins/builtin/econ/index.test.tsx +105 -0
- package/src/plugins/builtin/econ/index.tsx +95 -52
- package/src/plugins/builtin/fear-greed/cache.test.ts +18 -3
- package/src/plugins/builtin/fear-greed/cache.ts +18 -6
- package/src/plugins/builtin/fear-greed/charts.tsx +9 -2
- package/src/plugins/builtin/fear-greed/format.ts +0 -9
- package/src/plugins/builtin/fear-greed/pane.tsx +21 -21
- package/src/plugins/builtin/futures/contracts.ts +100 -0
- package/src/plugins/builtin/futures/index.tsx +298 -0
- package/src/plugins/builtin/futures/model.test.ts +129 -0
- package/src/plugins/builtin/futures/model.ts +136 -0
- package/src/plugins/builtin/futures/pane.test.tsx +239 -0
- package/src/plugins/builtin/futures/table.test.ts +155 -0
- package/src/plugins/builtin/futures/table.tsx +241 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +160 -134
- package/src/plugins/builtin/fx-matrix/pairs.ts +12 -1
- package/src/plugins/builtin/fx-matrix/pane.test.tsx +109 -0
- package/src/plugins/builtin/help/components.tsx +1 -30
- package/src/plugins/builtin/help/index.tsx +5 -25
- package/src/plugins/builtin/holders/pane.tsx +17 -12
- package/src/plugins/builtin/holders/treemap.tsx +4 -2
- package/src/plugins/builtin/insider/index.tsx +20 -10
- package/src/plugins/builtin/ipo-calendar/cache.ts +101 -0
- package/src/plugins/builtin/ipo-calendar/client.test.ts +31 -0
- package/src/plugins/builtin/ipo-calendar/client.ts +296 -0
- package/src/plugins/builtin/ipo-calendar/index.tsx +65 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +145 -0
- package/src/plugins/builtin/ipo-calendar/pane.tsx +328 -0
- package/src/plugins/builtin/ipo-calendar/types.ts +19 -0
- package/src/plugins/builtin/kelly-sizer/index.test.tsx +3 -3
- package/src/plugins/builtin/kelly-sizer/model.ts +8 -1
- package/src/plugins/builtin/kelly-sizer/pane.tsx +33 -15
- package/src/plugins/builtin/kelly-sizer/view.tsx +12 -5
- package/src/plugins/builtin/market-halts/client.ts +114 -0
- package/src/plugins/builtin/market-halts/index.tsx +29 -0
- package/src/plugins/builtin/market-halts/model.test.ts +187 -0
- package/src/plugins/builtin/market-halts/model.ts +291 -0
- package/src/plugins/builtin/market-halts/pane.tsx +256 -0
- package/src/plugins/builtin/market-heatmap/data.test.ts +1 -0
- package/src/plugins/builtin/market-heatmap/data.ts +12 -3
- package/src/plugins/builtin/market-heatmap/index.tsx +131 -46
- package/src/plugins/builtin/market-movers/index.tsx +145 -63
- package/src/plugins/builtin/market-movers/model.ts +19 -3
- package/src/plugins/builtin/market-movers/screener.test.ts +108 -0
- package/src/plugins/builtin/market-movers/screener.ts +168 -10
- package/src/plugins/builtin/news/index.tsx +78 -99
- package/src/plugins/builtin/news/wire/article-search.test.ts +72 -0
- package/src/plugins/builtin/news/wire/article-search.ts +140 -0
- package/src/plugins/builtin/news/wire/breaking/pane.tsx +18 -8
- package/src/plugins/builtin/news/wire/categories.ts +35 -0
- package/src/plugins/builtin/news/wire/default-feeds.ts +47 -1
- package/src/plugins/builtin/news/wire/feed-config.test.ts +10 -5
- package/src/plugins/builtin/news/wire/index.tsx +41 -4
- package/src/plugins/builtin/news/wire/industry-pane.tsx +38 -19
- package/src/plugins/builtin/news/wire/news/detail-view.tsx +10 -5
- package/src/plugins/builtin/news/wire/news/footer.ts +1 -0
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +15 -6
- package/src/plugins/builtin/news/wire/news/query-presets.ts +16 -2
- package/src/plugins/builtin/news/wire/news/table.test.tsx +54 -0
- package/src/plugins/builtin/news/wire/news/table.tsx +111 -17
- package/src/plugins/builtin/notes/files.test.ts +18 -0
- package/src/plugins/builtin/notes/files.ts +10 -4
- package/src/plugins/builtin/notes/index.tsx +4 -1
- package/src/plugins/builtin/notes/quick-notes-pane.test.tsx +182 -0
- package/src/plugins/builtin/notes/quick-notes-pane.tsx +74 -31
- package/src/plugins/builtin/notes/ticker-notes-tab.test.tsx +380 -0
- package/src/plugins/builtin/notes/ticker-notes-tab.tsx +97 -40
- package/src/plugins/builtin/options/calc-seed.test.ts +118 -0
- package/src/plugins/builtin/options/calc-seed.ts +52 -0
- package/src/plugins/builtin/options/footer.ts +4 -1
- package/src/plugins/builtin/options/index.tsx +22 -1
- package/src/plugins/builtin/options/live-quotes.ts +8 -0
- package/src/plugins/builtin/options/table.ts +3 -1
- package/src/plugins/builtin/options/view.test.tsx +5 -1
- package/src/plugins/builtin/options/view.tsx +112 -17
- package/src/plugins/builtin/options-calculator/index.tsx +35 -0
- package/src/plugins/builtin/options-calculator/model.test.ts +172 -0
- package/src/plugins/builtin/options-calculator/model.ts +273 -0
- package/src/plugins/builtin/options-calculator/pane.test.tsx +138 -0
- package/src/plugins/builtin/options-calculator/pane.tsx +218 -0
- package/src/plugins/builtin/plugin-module.test.ts +16 -2
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/polls/client.ts +57 -0
- package/src/plugins/builtin/polls/hooks.ts +20 -0
- package/src/plugins/builtin/polls/index.tsx +35 -0
- package/src/plugins/builtin/polls/normalize.test.ts +230 -0
- package/src/plugins/builtin/polls/normalize.ts +319 -0
- package/src/plugins/builtin/polls/pane.tsx +722 -0
- package/src/plugins/builtin/polls/types.ts +77 -0
- package/src/plugins/builtin/portfolio-list/header.tsx +15 -4
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -3
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +1 -1
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -0
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +2 -1
- package/src/plugins/builtin/research/analyst-pane.tsx +40 -67
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +25 -19
- package/src/plugins/builtin/research/index.test.ts +6 -25
- package/src/plugins/builtin/research/index.tsx +11 -5
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +86 -26
- package/src/plugins/builtin/scanner/denied.tsx +31 -0
- package/src/plugins/builtin/scanner/feed.ts +94 -0
- package/src/plugins/builtin/scanner/flow-model.test.ts +85 -0
- package/src/plugins/builtin/scanner/flow-model.ts +133 -0
- package/src/plugins/builtin/scanner/flow-pane.tsx +197 -0
- package/src/plugins/builtin/scanner/hilo-bars.tsx +121 -0
- package/src/plugins/builtin/scanner/hilo-model.test.ts +44 -0
- package/src/plugins/builtin/scanner/hilo-model.ts +75 -0
- package/src/plugins/builtin/scanner/hilo-pane.tsx +165 -0
- package/src/plugins/builtin/scanner/index.ts +109 -0
- package/src/plugins/builtin/scanner/waiting.tsx +11 -0
- package/src/plugins/builtin/sec/index.tsx +11 -7
- package/src/plugins/builtin/sectors/index.tsx +105 -81
- package/src/plugins/builtin/sectors/move-bar.tsx +43 -0
- package/src/plugins/builtin/sectors/sector-data.ts +18 -0
- package/src/plugins/builtin/sectors/sector-model.ts +13 -15
- package/src/plugins/builtin/shared/auto-refresh.ts +51 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +29 -5
- package/src/plugins/builtin/shared/pane-footer.ts +16 -3
- package/src/plugins/builtin/shared/screener-live-quotes.test.ts +91 -0
- package/src/plugins/builtin/shared/screener-live-quotes.ts +134 -0
- package/src/plugins/builtin/shared/table-pane.ts +1 -5
- package/src/plugins/builtin/shared/use-quote-board.test.tsx +211 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +195 -0
- package/src/plugins/builtin/short-interest/client.ts +124 -0
- package/src/plugins/builtin/short-interest/index.tsx +62 -0
- package/src/plugins/builtin/short-interest/model.ts +113 -0
- package/src/plugins/builtin/short-interest/pane.tsx +252 -0
- package/src/plugins/builtin/short-interest/types.ts +9 -0
- package/src/plugins/builtin/substack/article-detail.tsx +15 -1
- package/src/plugins/builtin/substack/pane-footer.ts +3 -22
- package/src/plugins/builtin/substack/pane.tsx +1 -2
- package/src/plugins/builtin/thirteenf/api.ts +4 -1
- package/src/plugins/builtin/thirteenf/model.ts +2 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +6 -2
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +10 -4
- package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +67 -11
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +15 -8
- package/src/plugins/builtin/ticker-detail/index.tsx +5 -21
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +72 -32
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +0 -10
- package/src/plugins/builtin/ticker-detail/pane.tsx +4 -1
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.test.tsx +70 -0
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +34 -26
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +96 -49
- package/src/plugins/builtin/ticker-detail/research-tabs.ts +32 -0
- package/src/plugins/builtin/ticker-research-backend-plugin.ts +2 -9
- package/src/plugins/builtin/ticker-research-plugin.ts +6 -0
- package/src/plugins/builtin/treasury-auctions/cache.test.ts +173 -0
- package/src/plugins/builtin/treasury-auctions/cache.ts +104 -0
- package/src/plugins/builtin/treasury-auctions/client.test.ts +181 -0
- package/src/plugins/builtin/treasury-auctions/client.ts +151 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +90 -0
- package/src/plugins/builtin/treasury-auctions/model.test.ts +147 -0
- package/src/plugins/builtin/treasury-auctions/model.ts +188 -0
- package/src/plugins/builtin/treasury-auctions/pane.test.tsx +170 -0
- package/src/plugins/builtin/treasury-auctions/pane.tsx +417 -0
- package/src/plugins/builtin/treasury-auctions/types.ts +59 -0
- package/src/plugins/builtin/tv/pane.tsx +67 -7
- package/src/plugins/builtin/tv/youtube-embed.test.ts +84 -0
- package/src/plugins/builtin/tv/youtube-embed.ts +173 -0
- package/src/plugins/builtin/tv/youtube-stream.ts +27 -2
- package/src/plugins/builtin/volatility/client.ts +87 -0
- package/src/plugins/builtin/volatility/index.test.tsx +107 -0
- package/src/plugins/builtin/volatility/index.tsx +193 -0
- package/src/plugins/builtin/volatility/model.test.ts +66 -0
- package/src/plugins/builtin/volatility/model.ts +113 -0
- package/src/plugins/builtin/world-indices/footer.ts +43 -23
- package/src/plugins/builtin/world-indices/index.tsx +44 -93
- package/src/plugins/builtin/world-indices/indices.ts +11 -2
- package/src/plugins/builtin/world-indices/model.ts +17 -24
- package/src/plugins/builtin/world-indices/pane.test.tsx +116 -0
- package/src/plugins/builtin/world-indices/table.tsx +82 -37
- package/src/plugins/builtin/yield-curve/index.tsx +16 -7
- package/src/plugins/builtin/yield-curve/treasury-data.test.ts +25 -0
- package/src/plugins/builtin/yield-curve/treasury-data.ts +16 -0
- package/src/plugins/catalog-backend.ts +8 -0
- package/src/plugins/catalog-ui.ts +8 -0
- package/src/plugins/event-bus.ts +1 -0
- package/src/plugins/ibkr/gateway/account-loaders.ts +1 -1
- package/src/plugins/ibkr/trade/tab/footer.ts +9 -23
- package/src/plugins/ibkr/trade/tab/index.tsx +1 -5
- package/src/plugins/ibkr/trading/pane-view.tsx +149 -87
- package/src/plugins/ibkr/trading/pane.tsx +2 -2
- package/src/plugins/ownership.ts +3 -0
- package/src/plugins/pane-manager/floating.ts +8 -2
- package/src/plugins/pane-manager.test.ts +13 -0
- package/src/plugins/prediction-markets/backend-plugin.ts +2 -0
- package/src/plugins/prediction-markets/capability.test.ts +281 -0
- package/src/plugins/prediction-markets/capability.ts +171 -0
- package/src/plugins/prediction-markets/chart.tsx +18 -26
- package/src/plugins/prediction-markets/columns.ts +3 -2
- package/src/plugins/prediction-markets/controller/catalog.ts +15 -12
- package/src/plugins/prediction-markets/controller/detail.ts +4 -0
- package/src/plugins/prediction-markets/controller/index.ts +0 -15
- package/src/plugins/prediction-markets/detail/book.tsx +3 -23
- package/src/plugins/prediction-markets/detail/overview.tsx +0 -18
- package/src/plugins/prediction-markets/detail/pane.tsx +36 -12
- package/src/plugins/prediction-markets/detail.test.tsx +1 -1
- package/src/plugins/prediction-markets/index.tsx +2 -0
- package/src/plugins/prediction-markets/metrics.ts +28 -18
- package/src/plugins/prediction-markets/navigation.ts +1 -0
- package/src/plugins/prediction-markets/pane.test.tsx +1 -1
- package/src/plugins/prediction-markets/pane.tsx +0 -1
- package/src/plugins/prediction-markets/services/fetch.ts +3 -2
- package/src/plugins/prediction-markets/services/kalshi/adapter.ts +82 -23
- package/src/plugins/prediction-markets/services/kalshi/normalize.ts +3 -1
- package/src/plugins/prediction-markets/services/kalshi/types.ts +14 -0
- package/src/plugins/prediction-markets/services/polymarket/adapter.ts +26 -14
- package/src/plugins/prediction-markets/services/polymarket/detail.ts +67 -18
- package/src/plugins/prediction-markets/test-helpers.tsx +0 -1
- package/src/plugins/prediction-markets/types.ts +3 -9
- package/src/plugins/registry/context.ts +6 -1
- package/src/plugins/registry/index.test.ts +2 -0
- package/src/plugins/registry/index.ts +53 -3
- package/src/plugins/runtime/context.tsx +4 -2
- package/src/plugins/runtime/index.tsx +9 -0
- package/src/remote/controller.test.ts +17 -4
- package/src/renderers/electrobun/bun/application-menu/index.ts +1 -1
- package/src/renderers/electrobun/bun/core-capabilities.ts +2 -2
- package/src/renderers/electrobun/bun/desktop/capability-bridge.test.ts +64 -0
- package/src/renderers/electrobun/bun/desktop/capability-bridge.ts +41 -7
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +5 -2
- package/src/renderers/electrobun/bun/desktop/http-fetch.test.ts +33 -0
- package/src/renderers/electrobun/bun/desktop/http-fetch.ts +53 -6
- package/src/renderers/electrobun/bun/desktop/initialization.test.ts +27 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +6 -1
- package/src/renderers/electrobun/bun/index.ts +1 -0
- package/src/renderers/electrobun/shared/protocol.ts +2 -0
- package/src/renderers/electrobun/view/app-services.ts +25 -2
- package/src/renderers/electrobun/view/build-assets.ts +5 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +186 -65
- package/src/renderers/electrobun/view/cli-pane-shot-health.test.ts +25 -0
- package/src/renderers/electrobun/view/cli-pane-shot-health.ts +31 -0
- package/src/renderers/electrobun/view/data-table/index.tsx +10 -11
- package/src/renderers/electrobun/view/data-table/row.tsx +2 -0
- package/src/renderers/electrobun/view/desktop/controls.test.tsx +68 -0
- package/src/renderers/electrobun/view/desktop/controls.tsx +25 -1
- package/src/renderers/electrobun/view/host/box.test.tsx +80 -0
- package/src/renderers/electrobun/view/host/media-surface.tsx +70 -3
- package/src/renderers/electrobun/view/host/tabs.tsx +97 -5
- package/src/renderers/electrobun/view/host/text.tsx +5 -2
- package/src/renderers/electrobun/view/input-host.test.tsx +140 -0
- package/src/renderers/electrobun/view/input-host.tsx +4 -2
- package/src/renderers/electrobun/view/native-stubs/backend-rpc.ts +19 -1
- package/src/renderers/electrobun/view/native-stubs/broker-sync-native-loader.ts +7 -0
- package/src/renderers/electrobun/view/native-stubs/chart/kitty-support.ts +10 -0
- package/src/renderers/electrobun/view/remote/capability-invoker.test.ts +46 -0
- package/src/renderers/electrobun/view/remote/capability-invoker.ts +34 -13
- package/src/renderers/electrobun/view/remote/connection-health-backend.ts +19 -0
- package/src/renderers/electrobun/view/remote/connection-health.test.ts +44 -0
- package/src/renderers/electrobun/view/remote/connection-health.ts +49 -0
- package/src/renderers/electrobun/view/styles.css +5 -0
- package/src/renderers/opentui/dialog-host.tsx +279 -80
- package/src/renderers/opentui/host.tsx +15 -1
- package/src/renderers/opentui/terminal-media.test.ts +102 -0
- package/src/renderers/opentui/terminal-media.ts +142 -0
- package/src/renderers/opentui/test-utils.tsx +9 -6
- package/src/renderers/opentui/toast-host.tsx +136 -7
- package/src/renderers/opentui/ui-host.tsx +19 -3
- package/src/sources/provider-router/connection-health.test.ts +28 -0
- package/src/sources/provider-router/connection-health.ts +47 -0
- package/src/sources/provider-router/history.test.ts +53 -4
- package/src/sources/provider-router/history.ts +119 -14
- package/src/sources/provider-router/index.ts +12 -1
- package/src/sources/yahoo-finance/history.ts +2 -9
- package/src/sources/yahoo-finance/http.ts +1 -1
- package/src/sources/yahoo-finance/symbols.ts +1 -1
- package/src/sources/yahoo-finance/types.ts +19 -0
- package/src/state/app/context/index.test.tsx +101 -0
- package/src/state/app/context/index.tsx +10 -0
- package/src/state/config-save-scheduler.ts +1 -6
- package/src/state/persist-scheduler.test.ts +31 -0
- package/src/state/persist-scheduler.ts +21 -8
- package/src/sync/auth-transition.test.ts +36 -0
- package/src/sync/auth-transition.ts +22 -0
- package/src/sync/core-contributors.test.ts +58 -1
- package/src/sync/core-contributors.ts +39 -2
- package/src/sync/react.ts +9 -0
- package/src/test-support/plugin-runtime.ts +3 -0
- package/src/theme/font-scale.test.ts +14 -0
- package/src/theme/font-scale.ts +69 -0
- package/src/tickers/search/index.test.ts +284 -2
- package/src/tickers/search/index.ts +55 -18
- package/src/tickers/search/ranking.ts +143 -18
- package/src/tickers/search/types.ts +1 -0
- package/src/time-series/alignment.ts +15 -11
- package/src/time-series/field-catalog.ts +10 -0
- package/src/time-series/fundamentals.ts +19 -11
- package/src/time-series/hooks.ts +8 -2
- package/src/time-series/parsed-history-cache.ts +35 -0
- package/src/time-series/resolution.ts +10 -0
- package/src/time-series/resolve.test.ts +332 -3
- package/src/time-series/resolve.ts +225 -25
- package/src/time-series/spec-studies.test.ts +23 -0
- package/src/time-series/spec.ts +21 -2
- package/src/time-series/transforms.test.ts +76 -1
- package/src/time-series/transforms.ts +39 -8
- package/src/time-series/types.ts +9 -2
- package/src/time-series/use-chart-resolution.test.tsx +105 -1
- package/src/time-series/use-chart-resolution.ts +67 -13
- package/src/types/config.ts +30 -27
- package/src/types/media.ts +2 -0
- package/src/types/plugin.ts +8 -1
- package/src/ui/host.tsx +4 -0
- package/src/ui/pointer-drag.ts +12 -0
- package/src/ui/qr.test.ts +79 -0
- package/src/ui/qr.ts +82 -0
- package/src/updater/github-releases.test.ts +3 -3
- package/src/updater/github-releases.ts +1 -1
- package/src/updater.test.ts +117 -16
- package/src/updater.ts +32 -5
- package/src/utils/exchanges.ts +1 -0
- package/src/utils/external-url.test.ts +32 -0
- package/src/utils/external-url.ts +23 -0
- package/src/utils/sort-values.ts +31 -0
- package/src/utils/zoned-date-time.ts +67 -0
- package/src/components/onboarding/wizard-keyboard.ts +0 -306
- package/src/plugins/builtin/chart-composer/date-window-dialog.tsx +0 -120
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "../../assist/model";
|
|
14
14
|
import { matchPrefix, type Command } from "../../commands/registry";
|
|
15
15
|
import { isCollectionCommand } from "../../helpers";
|
|
16
|
+
import { dedupeById } from "../../view-model";
|
|
16
17
|
import type { ResultItem } from "../../list/model";
|
|
17
18
|
import type { parseRootShortcutIntent } from "./shortcuts";
|
|
18
19
|
import type { CommandBarRoute } from "../../workflow/types";
|
|
@@ -21,6 +22,13 @@ import { buildRootShortcutItem } from "./shortcut-items";
|
|
|
21
22
|
|
|
22
23
|
type RootShortcutIntent = ReturnType<typeof parseRootShortcutIntent>;
|
|
23
24
|
|
|
25
|
+
/** The ART plugin command claims the prefix, so article rows must still be shown. */
|
|
26
|
+
export function isArticleLookupShortcut(intent: RootShortcutIntent): boolean {
|
|
27
|
+
return intent.kind !== "none"
|
|
28
|
+
&& intent.source === "plugin-command"
|
|
29
|
+
&& intent.command.id === "open-news-article";
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
interface PaneTemplateItemOptions {
|
|
25
33
|
category?: string;
|
|
26
34
|
createOptions?: PaneTemplateCreateOptions;
|
|
@@ -67,6 +75,7 @@ export interface RootResultModelOptions {
|
|
|
67
75
|
pluginCommandResultItems: (command: CommandDef, shortcutArg: string) => ResultItem[];
|
|
68
76
|
rootQuery: string;
|
|
69
77
|
rootShortcutIntent: RootShortcutIntent;
|
|
78
|
+
articleResultItems?: ResultItem[];
|
|
70
79
|
runDirectCommand: (command: Command, arg: string) => void;
|
|
71
80
|
runSecurityDescriptionShortcut: (query?: string) => void | Promise<void>;
|
|
72
81
|
state: AppState;
|
|
@@ -74,9 +83,17 @@ export interface RootResultModelOptions {
|
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
/** An in-flight or answered request keeps its rows even if the heuristic lapses. */
|
|
77
|
-
function isAssistSectionVisible(
|
|
86
|
+
function isAssistSectionVisible(
|
|
87
|
+
assist: AssistRowHandlers,
|
|
88
|
+
query: string,
|
|
89
|
+
resultCount: number,
|
|
90
|
+
hasShortcutIntent: boolean,
|
|
91
|
+
): boolean {
|
|
78
92
|
if (!query.trim()) return false;
|
|
79
93
|
if (assist.state.status !== "idle" && assist.state.query === query.trim()) return true;
|
|
94
|
+
// A resolved shortcut is the user speaking the command language, so nothing is
|
|
95
|
+
// asked of the AI, and a sign-up offer must not outrank that exact match.
|
|
96
|
+
if (hasShortcutIntent) return false;
|
|
80
97
|
// Signed out there is nothing to wait for, so the older heuristic still picks
|
|
81
98
|
// the queries worth offering a sign-up row for.
|
|
82
99
|
if (!assist.enabled) return shouldShowAssistRow({ query, resultCount });
|
|
@@ -108,6 +125,7 @@ export function buildRootResultModel(options: RootResultModelOptions): RootResul
|
|
|
108
125
|
pluginCommandResultItems,
|
|
109
126
|
rootQuery,
|
|
110
127
|
rootShortcutIntent,
|
|
128
|
+
articleResultItems = [],
|
|
111
129
|
runDirectCommand,
|
|
112
130
|
runSecurityDescriptionShortcut,
|
|
113
131
|
state,
|
|
@@ -157,19 +175,18 @@ export function buildRootResultModel(options: RootResultModelOptions): RootResul
|
|
|
157
175
|
includePromptableTickerTemplates: true,
|
|
158
176
|
})
|
|
159
177
|
: [];
|
|
160
|
-
|
|
161
|
-
for (const item of [...templateItems, ...relatedTemplateItems]) {
|
|
162
|
-
if (seenItemIds.has(item.id)) continue;
|
|
163
|
-
seenItemIds.add(item.id);
|
|
164
|
-
items.push(item);
|
|
165
|
-
}
|
|
178
|
+
items.push(...templateItems, ...relatedTemplateItems);
|
|
166
179
|
} else if (
|
|
167
180
|
rootShortcutIntent.kind !== "none"
|
|
168
181
|
&& rootShortcutIntent.source === "plugin-command"
|
|
169
182
|
&& shortcutItem
|
|
170
183
|
) {
|
|
171
|
-
|
|
172
|
-
|
|
184
|
+
if (isArticleLookupShortcut(rootShortcutIntent)) {
|
|
185
|
+
items.push(shortcutItem);
|
|
186
|
+
} else {
|
|
187
|
+
const dynamicItems = pluginCommandResultItems(rootShortcutIntent.command, rootShortcutIntent.argText);
|
|
188
|
+
items.push(...(dynamicItems.length > 0 ? dynamicItems : [shortcutItem]));
|
|
189
|
+
}
|
|
173
190
|
} else if (match && match.command.id === "plugins") {
|
|
174
191
|
items.push(...buildPluginItems(match.arg));
|
|
175
192
|
} else if (match && match.command.id === "layout") {
|
|
@@ -227,12 +244,22 @@ export function buildRootResultModel(options: RootResultModelOptions): RootResul
|
|
|
227
244
|
items.push(...matchedItems);
|
|
228
245
|
}
|
|
229
246
|
|
|
247
|
+
if (rootShortcutIntent.kind === "none" || isArticleLookupShortcut(rootShortcutIntent)) {
|
|
248
|
+
items.push(...articleResultItems);
|
|
249
|
+
}
|
|
250
|
+
|
|
230
251
|
// Built from the local matches, then moved above them: the AI answers the
|
|
231
252
|
// question the user typed, so it leads the list. Rows landing here renumber
|
|
232
253
|
// everything below, which the root selection effect absorbs by identity.
|
|
233
|
-
const assistItems = assist
|
|
254
|
+
const assistItems = assist
|
|
255
|
+
&& isAssistSectionVisible(
|
|
256
|
+
assist,
|
|
257
|
+
rootQuery,
|
|
258
|
+
items.length,
|
|
259
|
+
rootShortcutIntent.kind !== "none" && !isArticleLookupShortcut(rootShortcutIntent),
|
|
260
|
+
)
|
|
234
261
|
? buildAssistResultItems({ ...assist, query: rootQuery })
|
|
235
262
|
: [];
|
|
236
263
|
|
|
237
|
-
return { items: [...assistItems, ...items], initialIdx };
|
|
264
|
+
return { items: dedupeById([...assistItems, ...items]), initialIdx };
|
|
238
265
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef, type Dispatch, type RefObject, type SetStateAction } from "react";
|
|
1
|
+
import { useEffect, useLayoutEffect, useMemo, useRef, type Dispatch, type RefObject, type SetStateAction } from "react";
|
|
2
2
|
import type { ScrollBoxRenderable } from "../../../../ui";
|
|
3
3
|
import type { AppState } from "../../../../state/app/context";
|
|
4
4
|
import type { DataProvider } from "../../../../types/data-provider";
|
|
@@ -63,6 +63,7 @@ interface UseCommandBarRootRuntimeOptions {
|
|
|
63
63
|
}): ResultItem[];
|
|
64
64
|
pluginCommandItems(): ResultItem[];
|
|
65
65
|
pluginCommandResultItems(command: CommandDef, shortcutArg: string): ResultItem[];
|
|
66
|
+
articleResultItems?: ResultItem[];
|
|
66
67
|
readTickerSearchCache(
|
|
67
68
|
query: string,
|
|
68
69
|
brokerId?: string | null,
|
|
@@ -114,6 +115,7 @@ export function useCommandBarRootRuntime({
|
|
|
114
115
|
paneShortcutItems,
|
|
115
116
|
pluginCommandItems,
|
|
116
117
|
pluginCommandResultItems,
|
|
118
|
+
articleResultItems = [],
|
|
117
119
|
readTickerSearchCache,
|
|
118
120
|
rootModeKind,
|
|
119
121
|
rootQuery,
|
|
@@ -187,6 +189,7 @@ export function useCommandBarRootRuntime({
|
|
|
187
189
|
pluginCommandResultItems,
|
|
188
190
|
rootQuery,
|
|
189
191
|
rootShortcutIntent,
|
|
192
|
+
articleResultItems,
|
|
190
193
|
runDirectCommand,
|
|
191
194
|
runSecurityDescriptionShortcut,
|
|
192
195
|
state,
|
|
@@ -215,6 +218,7 @@ export function useCommandBarRootRuntime({
|
|
|
215
218
|
pluginCommandResultItems,
|
|
216
219
|
rootQuery,
|
|
217
220
|
rootShortcutIntent,
|
|
221
|
+
articleResultItems,
|
|
218
222
|
runDirectCommand,
|
|
219
223
|
runSecurityDescriptionShortcut,
|
|
220
224
|
state,
|
|
@@ -246,7 +250,7 @@ export function useCommandBarRootRuntime({
|
|
|
246
250
|
writeTickerSearchCache,
|
|
247
251
|
});
|
|
248
252
|
|
|
249
|
-
|
|
253
|
+
useLayoutEffect(() => {
|
|
250
254
|
if (currentRoute) return;
|
|
251
255
|
|
|
252
256
|
const resultIds = orderedRootResults.map((item) => item.id);
|
|
@@ -43,7 +43,7 @@ export function useCommandBarTickerSearchActions({
|
|
|
43
43
|
focusTicker(ticker.metadata.ticker, options);
|
|
44
44
|
closeAll({ revertThemePreview: false });
|
|
45
45
|
})();
|
|
46
|
-
}, [
|
|
46
|
+
}, [closeAll, dispatch, focusTicker, pluginRegistry.events, tickerRepository]);
|
|
47
47
|
|
|
48
48
|
const mapTickerSearchCandidateToResultItem = useCallback((candidate: TickerSearchCandidate): ResultItem => {
|
|
49
49
|
const detail = formatTickerSearchDetail(candidate);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import type { ResultItem } from "../../list/model";
|
|
3
|
+
import { mergeTickerSearchResultItems } from "./results";
|
|
4
|
+
|
|
5
|
+
function resultItem(id: string, label: string, right: string, kind: ResultItem["kind"] = "ticker"): ResultItem {
|
|
6
|
+
return {
|
|
7
|
+
id,
|
|
8
|
+
label,
|
|
9
|
+
detail: "Apple Inc.",
|
|
10
|
+
category: kind === "info" ? "Search" : "Saved",
|
|
11
|
+
kind,
|
|
12
|
+
right,
|
|
13
|
+
action: () => {},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test("keeps completed ticker-search results authoritative over provisional rows", () => {
|
|
18
|
+
const authoritative = [
|
|
19
|
+
resultItem("ranked:AAPL", "AAPL", "Equity NASDAQ"),
|
|
20
|
+
resultItem("ranked:APC", "APC", "Equity XETRA"),
|
|
21
|
+
];
|
|
22
|
+
const provisional = [
|
|
23
|
+
resultItem("local:APC", "APC", "Equity XETRA"),
|
|
24
|
+
resultItem("local:AAPL", "AAPL", "Equity NASDAQ"),
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
expect(mergeTickerSearchResultItems("Apple", authoritative, provisional).map((item) => item.id)).toEqual([
|
|
28
|
+
"ranked:AAPL",
|
|
29
|
+
"ranked:APC",
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
const noResults = resultItem("no-results", "No matches for Apple", "", "info");
|
|
33
|
+
expect(mergeTickerSearchResultItems("Apple", [noResults], [])).toEqual([noResults]);
|
|
34
|
+
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { TickerRecord } from "../../../../types/ticker";
|
|
2
2
|
import {
|
|
3
3
|
createLocalTickerSearchCandidates,
|
|
4
|
-
rankTickerSearchItems,
|
|
5
4
|
type TickerSearchCandidate,
|
|
6
5
|
} from "../../../../tickers/search";
|
|
7
6
|
import type { ResultItem } from "../../list/model";
|
|
@@ -33,7 +32,7 @@ function isExactTickerResultMatch(item: ResultItem, query: string): boolean {
|
|
|
33
32
|
|
|
34
33
|
export function mergeTickerSearchResultItems(
|
|
35
34
|
query: string,
|
|
36
|
-
|
|
35
|
+
rankedItems: ResultItem[],
|
|
37
36
|
fallbackItems: ResultItem[],
|
|
38
37
|
): ResultItem[] {
|
|
39
38
|
const merged: ResultItem[] = [];
|
|
@@ -45,12 +44,11 @@ export function mergeTickerSearchResultItems(
|
|
|
45
44
|
seen.add(key);
|
|
46
45
|
merged.push(item);
|
|
47
46
|
};
|
|
48
|
-
|
|
47
|
+
rankedItems.forEach(addItem);
|
|
49
48
|
fallbackItems.forEach(addItem);
|
|
50
|
-
if (merged.length === 0) return fallbackItems;
|
|
49
|
+
if (merged.length === 0) return rankedItems.length > 0 ? rankedItems : fallbackItems;
|
|
51
50
|
|
|
52
|
-
return
|
|
53
|
-
.map((item) => isExactTickerResultMatch(item, query) && item.category !== "Saved"
|
|
51
|
+
return merged.map((item) => isExactTickerResultMatch(item, query) && item.category !== "Saved"
|
|
54
52
|
? { ...item, category: "Exact Match" }
|
|
55
53
|
: item);
|
|
56
54
|
}
|
|
@@ -80,7 +80,7 @@ export function useTickerSearchRouteResults(options: {
|
|
|
80
80
|
brokerInstanceId,
|
|
81
81
|
);
|
|
82
82
|
setTickerSearchResults(cachedCandidates
|
|
83
|
-
? mergeTickerSearchResultItems(searchQuery,
|
|
83
|
+
? mergeTickerSearchResultItems(searchQuery, buildTickerSearchResultItems(cachedCandidates, searchQuery), localItems)
|
|
84
84
|
: localItems);
|
|
85
85
|
const requestId = ++searchRequestIdRef.current;
|
|
86
86
|
const searchDelay = skipTickerSearchDebounceRef.current ? 0 : 200;
|
|
@@ -109,8 +109,8 @@ export function useTickerSearchRouteResults(options: {
|
|
|
109
109
|
);
|
|
110
110
|
setTickerSearchResults(mergeTickerSearchResultItems(
|
|
111
111
|
searchQuery,
|
|
112
|
-
localTickerSearchResultItems(searchQuery, { limit: 6 }),
|
|
113
112
|
buildTickerSearchResultItems(combined, searchQuery),
|
|
113
|
+
localTickerSearchResultItems(searchQuery, { limit: 6 }),
|
|
114
114
|
));
|
|
115
115
|
} catch {
|
|
116
116
|
if (requestId !== searchRequestIdRef.current) return;
|
|
@@ -135,12 +135,13 @@ function buildRouteListState(options: BuildRouteListStateOptions): ListScreenSta
|
|
|
135
135
|
query: currentRoute.query,
|
|
136
136
|
selectedIdx: currentRoute.selectedIdx,
|
|
137
137
|
hoveredIdx: currentRoute.hoveredIdx,
|
|
138
|
-
results: orderListResults(results),
|
|
138
|
+
results: orderListResults(results, { sectionOrder: "ranked" }),
|
|
139
139
|
searching: tickerSearchPending,
|
|
140
140
|
emptyLabel: emptyState.label,
|
|
141
141
|
emptyDetail: emptyState.detail,
|
|
142
142
|
footerLeft: getScreenFooterLeft(currentRoute),
|
|
143
143
|
footerRight: getScreenFooterRight(currentRoute),
|
|
144
|
+
sectionOrder: "ranked",
|
|
144
145
|
};
|
|
145
146
|
}
|
|
146
147
|
default:
|
|
@@ -144,12 +144,17 @@ describe("CommandBar AI assist", () => {
|
|
|
144
144
|
|
|
145
145
|
test("keeps the row the user picked when an answer lands above it", async () => {
|
|
146
146
|
signInVerified();
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
147
|
+
let releaseResponse = () => {};
|
|
148
|
+
const held = new Promise<void>((resolve) => { releaseResponse = resolve; });
|
|
149
|
+
const requests = mockAssistTransport(async () => {
|
|
150
|
+
await held;
|
|
151
|
+
return jsonResponse({
|
|
152
|
+
candidates: [
|
|
153
|
+
{ input: "CHAT #general", title: "Open the general channel", prefix: "CHAT", confidence: 0.9 },
|
|
154
|
+
{ input: "CHAT #random", title: "Open the random channel", prefix: "CHAT", confidence: 0.4 },
|
|
155
|
+
],
|
|
156
|
+
});
|
|
157
|
+
});
|
|
153
158
|
const created: Array<{ templateId: string; options?: PaneTemplateCreateOptions }> = [];
|
|
154
159
|
|
|
155
160
|
testSetup = await testRender(
|
|
@@ -161,8 +166,10 @@ describe("CommandBar AI assist", () => {
|
|
|
161
166
|
);
|
|
162
167
|
|
|
163
168
|
await testSetup.renderOnce();
|
|
169
|
+
await waitForRequest(requests);
|
|
164
170
|
// Down lands on the local match while a single "Thinking…" row sits above.
|
|
165
171
|
await emitKeypress(testSetup, { name: "down" });
|
|
172
|
+
releaseResponse();
|
|
166
173
|
await waitForFrameToContain("CHAT #random — Open the random channel", ASSIST_WAIT_ATTEMPTS);
|
|
167
174
|
|
|
168
175
|
// Two answers replaced that one row, so the chosen row moved down by one —
|
|
@@ -926,6 +926,71 @@ describe("CommandBar", () => {
|
|
|
926
926
|
expect(aaplRow).toBeLessThan(appRow);
|
|
927
927
|
});
|
|
928
928
|
|
|
929
|
+
test("keeps the provider-ranked canonical listing ahead of provisional saved order", async () => {
|
|
930
|
+
testSetup = await testRender(
|
|
931
|
+
<CommandBarHarness
|
|
932
|
+
query=""
|
|
933
|
+
extraTickers={[makeTicker("APC", "Apple Inc.", {
|
|
934
|
+
exchange: "XETRA",
|
|
935
|
+
currency: "EUR",
|
|
936
|
+
assetCategory: "STK",
|
|
937
|
+
})]}
|
|
938
|
+
configureState={(state) => ({
|
|
939
|
+
...state,
|
|
940
|
+
commandBarLaunchRequest: {
|
|
941
|
+
kind: "ticker-search",
|
|
942
|
+
query: "Apple",
|
|
943
|
+
sequence: 1,
|
|
944
|
+
},
|
|
945
|
+
tickers: new Map([
|
|
946
|
+
["APC", state.tickers.get("APC")!],
|
|
947
|
+
["AAPL", state.tickers.get("AAPL")!],
|
|
948
|
+
["MSFT", state.tickers.get("MSFT")!],
|
|
949
|
+
]),
|
|
950
|
+
})}
|
|
951
|
+
dataProvider={makeDataProvider(async () => [
|
|
952
|
+
{
|
|
953
|
+
providerId: "yahoo",
|
|
954
|
+
symbol: "AAPL",
|
|
955
|
+
name: "Apple Inc.",
|
|
956
|
+
exchange: "NASDAQ",
|
|
957
|
+
primaryExchange: "NASDAQ",
|
|
958
|
+
type: "EQUITY",
|
|
959
|
+
currency: "USD",
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
providerId: "broker",
|
|
963
|
+
symbol: "APC",
|
|
964
|
+
name: "Apple Inc.",
|
|
965
|
+
exchange: "XETRA",
|
|
966
|
+
primaryExchange: "XETRA",
|
|
967
|
+
type: "EQUITY",
|
|
968
|
+
currency: "EUR",
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
providerId: "yahoo",
|
|
972
|
+
symbol: "APLY",
|
|
973
|
+
name: "Apple Yield Shares ETF",
|
|
974
|
+
exchange: "NYSE Arca",
|
|
975
|
+
type: "ETF",
|
|
976
|
+
currency: "USD",
|
|
977
|
+
},
|
|
978
|
+
])}
|
|
979
|
+
/>,
|
|
980
|
+
{ width: 100, height: 24 },
|
|
981
|
+
);
|
|
982
|
+
|
|
983
|
+
await testSetup.renderOnce();
|
|
984
|
+
const frame = await waitForFrameToContain("APLY");
|
|
985
|
+
const rows = frame.split("\n");
|
|
986
|
+
const aaplRow = rows.findIndex((line) => line.trimStart().startsWith("AAPL"));
|
|
987
|
+
const apcRow = rows.findIndex((line) => line.trimStart().startsWith("APC"));
|
|
988
|
+
|
|
989
|
+
expect(aaplRow).toBeGreaterThanOrEqual(0);
|
|
990
|
+
expect(apcRow).toBeGreaterThanOrEqual(0);
|
|
991
|
+
expect(aaplRow).toBeLessThan(apcRow);
|
|
992
|
+
});
|
|
993
|
+
|
|
929
994
|
test("renders form-layout wizard fields together on one screen", async () => {
|
|
930
995
|
testSetup = await testRender(
|
|
931
996
|
<CommandBarHarness
|
|
@@ -7,6 +7,14 @@ import { usePlanAccess } from "../../../plugins/builtin/shared/plan-access";
|
|
|
7
7
|
import { buildAssistCommandInventory } from "../assist/inventory";
|
|
8
8
|
import { useCommandBarAssist } from "../assist/runtime";
|
|
9
9
|
import { shouldAutoAskAssist, type AssistRowHandlers } from "../assist/model";
|
|
10
|
+
import { useNewsArticles } from "../../../news/hooks";
|
|
11
|
+
import {
|
|
12
|
+
ARTICLE_SEARCH_QUERY,
|
|
13
|
+
cachedNewsArticles,
|
|
14
|
+
looksLikeArticleQuery,
|
|
15
|
+
} from "../../../plugins/builtin/news/wire/article-search";
|
|
16
|
+
import { buildArticleSearchResultItems } from "../routes/root/article-results";
|
|
17
|
+
import { openUrl } from "../../ui/external-link";
|
|
10
18
|
import { useRouteListState } from "../routing/list-state";
|
|
11
19
|
import { useCommandBarRootRuntime } from "../routes/root/runtime";
|
|
12
20
|
import { parseRootShortcutIntent } from "../routes/root/shortcuts";
|
|
@@ -247,6 +255,32 @@ export function CommandBar({
|
|
|
247
255
|
),
|
|
248
256
|
}), [askAssistNow, assistActive, assistAutoAsk, assistState, planAccess.emailVerified, startAssistSignUp]);
|
|
249
257
|
|
|
258
|
+
const newsState = useNewsArticles(looksLikeArticleQuery(rootQuery) ? ARTICLE_SEARCH_QUERY : null);
|
|
259
|
+
const articleResultItems = useMemo(() => {
|
|
260
|
+
const cached = cachedNewsArticles();
|
|
261
|
+
const seen = new Set<string>();
|
|
262
|
+
const articles = [];
|
|
263
|
+
for (const article of [...cached, ...newsState.articles]) {
|
|
264
|
+
if (seen.has(article.id)) continue;
|
|
265
|
+
seen.add(article.id);
|
|
266
|
+
articles.push(article);
|
|
267
|
+
}
|
|
268
|
+
const localReady = cached.length > 0
|
|
269
|
+
|| newsState.phase === "ready"
|
|
270
|
+
|| newsState.phase === "refreshing"
|
|
271
|
+
|| newsState.phase === "error";
|
|
272
|
+
const stillLoading = !localReady && (newsState.phase === "idle" || newsState.phase === "loading");
|
|
273
|
+
return buildArticleSearchResultItems({
|
|
274
|
+
articles,
|
|
275
|
+
query: rootQuery,
|
|
276
|
+
phase: stillLoading ? "loading" : "ready",
|
|
277
|
+
onOpen: (article) => {
|
|
278
|
+
openUrl(article.url);
|
|
279
|
+
closeAll({ revertThemePreview: false });
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
}, [closeAll, newsState.articles, newsState.phase, rootQuery]);
|
|
283
|
+
|
|
250
284
|
const {
|
|
251
285
|
activeMatch,
|
|
252
286
|
orderedRootResults,
|
|
@@ -283,6 +317,7 @@ export function CommandBar({
|
|
|
283
317
|
paneShortcutItems,
|
|
284
318
|
pluginCommandItems,
|
|
285
319
|
pluginCommandResultItems,
|
|
320
|
+
articleResultItems,
|
|
286
321
|
readTickerSearchCache,
|
|
287
322
|
rootModeKind: rootModeInfo.kind,
|
|
288
323
|
rootQuery,
|
|
@@ -47,6 +47,25 @@ describe("command bar view model helpers", () => {
|
|
|
47
47
|
expect(sections.map((section) => section.category)).toEqual(["Tickers", "Config", "Danger", "Debug"]);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
+
test("drops an offer-only section below real matches even when its category leads", () => {
|
|
51
|
+
const sections = buildSections([
|
|
52
|
+
{ id: "assist:sign-up", category: "Ask AI", disabled: true, defaultSelectable: false },
|
|
53
|
+
{ id: "holders", category: "Panes" },
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
expect(sections.map((section) => section.category)).toEqual(["Panes", "Ask AI"]);
|
|
57
|
+
expect(sections[0]?.items[0]?.id).toBe("holders");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("keeps an answered AI section leading the list", () => {
|
|
61
|
+
const sections = buildSections([
|
|
62
|
+
{ id: "assist:candidate:0", category: "Ask AI" },
|
|
63
|
+
{ id: "holders", category: "Panes" },
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
expect(sections.map((section) => section.category)).toEqual(["Ask AI", "Panes"]);
|
|
67
|
+
});
|
|
68
|
+
|
|
50
69
|
test("keeps non-exact ticker suggestions behind app sections in app-first order", () => {
|
|
51
70
|
const sections = buildSections([
|
|
52
71
|
{ id: "pane", category: "Panes" },
|
|
@@ -67,6 +86,30 @@ describe("command bar view model helpers", () => {
|
|
|
67
86
|
]);
|
|
68
87
|
});
|
|
69
88
|
|
|
89
|
+
test("orders ticker sections by their best-ranked candidate", () => {
|
|
90
|
+
const sections = buildSections([
|
|
91
|
+
{ id: "primary", category: "Primary Listing" },
|
|
92
|
+
{ id: "saved-alternate", category: "Saved" },
|
|
93
|
+
{ id: "other", category: "Other Listings" },
|
|
94
|
+
{ id: "saved-fallback", category: "Saved" },
|
|
95
|
+
{ id: "fund", category: "Funds & Derivatives" },
|
|
96
|
+
], { sectionOrder: "ranked" });
|
|
97
|
+
|
|
98
|
+
expect(sections.map((section) => section.category)).toEqual([
|
|
99
|
+
"Primary Listing",
|
|
100
|
+
"Saved",
|
|
101
|
+
"Other Listings",
|
|
102
|
+
"Funds & Derivatives",
|
|
103
|
+
]);
|
|
104
|
+
expect(sections.flatMap((section) => section.items.map((item) => item.id))).toEqual([
|
|
105
|
+
"primary",
|
|
106
|
+
"saved-alternate",
|
|
107
|
+
"saved-fallback",
|
|
108
|
+
"other",
|
|
109
|
+
"fund",
|
|
110
|
+
]);
|
|
111
|
+
});
|
|
112
|
+
|
|
70
113
|
test("derives row presentation for toggles and current rows", () => {
|
|
71
114
|
expect(getRowPresentation({
|
|
72
115
|
id: "plugin:news",
|
|
@@ -4,6 +4,23 @@ import { truncateToDisplayWidth } from "../../utils/format";
|
|
|
4
4
|
|
|
5
5
|
export { rankTickerSearchItems } from "../../tickers/search";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Drop repeats of an id, keeping the first. The root list is assembled from
|
|
9
|
+
* overlapping sources (pane shortcuts, commands, plugin commands, ticker
|
|
10
|
+
* actions, layout items) in priority order, so the first hit is the one whose
|
|
11
|
+
* category and detail fit best.
|
|
12
|
+
*/
|
|
13
|
+
export function dedupeById<T extends { id: string }>(items: T[]): T[] {
|
|
14
|
+
const seen = new Set<string>();
|
|
15
|
+
const result: T[] = [];
|
|
16
|
+
for (const item of items) {
|
|
17
|
+
if (seen.has(item.id)) continue;
|
|
18
|
+
seen.add(item.id);
|
|
19
|
+
result.push(item);
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
|
|
7
24
|
export type CommandBarMode = "default" | "search" | "themes" | "plugins" | "layout" | "direct-command";
|
|
8
25
|
|
|
9
26
|
export interface CommandBarModeInfo {
|
|
@@ -17,7 +34,7 @@ export interface CommandBarSection<T> {
|
|
|
17
34
|
items: T[];
|
|
18
35
|
}
|
|
19
36
|
|
|
20
|
-
export type CommandBarSectionOrder = "default" | "app-first";
|
|
37
|
+
export type CommandBarSectionOrder = "default" | "app-first" | "ranked";
|
|
21
38
|
|
|
22
39
|
export interface CommandBarItemView {
|
|
23
40
|
id: string;
|
|
@@ -66,7 +83,24 @@ export function resolveCommandBarMode(query: string, commandList?: Command[]): C
|
|
|
66
83
|
}
|
|
67
84
|
}
|
|
68
85
|
|
|
69
|
-
|
|
86
|
+
/**
|
|
87
|
+
* A section whose every row is disabled or unselectable is an offer, not an
|
|
88
|
+
* answer, so it sorts below real matches however its category is prioritized.
|
|
89
|
+
*/
|
|
90
|
+
const OFFER_SECTION_DEMOTION = 200;
|
|
91
|
+
|
|
92
|
+
interface SectionSortableItem {
|
|
93
|
+
category: string;
|
|
94
|
+
disabled?: boolean;
|
|
95
|
+
defaultSelectable?: boolean;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function isOfferOnlySection<T extends SectionSortableItem>(items: T[]): boolean {
|
|
99
|
+
return items.length > 0
|
|
100
|
+
&& items.every((item) => item.disabled === true || item.defaultSelectable === false);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function buildSections<T extends SectionSortableItem>(
|
|
70
104
|
items: T[],
|
|
71
105
|
options?: { sectionOrder?: CommandBarSectionOrder },
|
|
72
106
|
): Array<CommandBarSection<T>> {
|
|
@@ -82,8 +116,10 @@ export function buildSections<T extends { category: string }>(
|
|
|
82
116
|
return sections
|
|
83
117
|
.map((section, index) => ({ section, index }))
|
|
84
118
|
.sort((a, b) => {
|
|
85
|
-
const leftPriority = getCategoryPriority(a.section.category, options?.sectionOrder)
|
|
86
|
-
|
|
119
|
+
const leftPriority = getCategoryPriority(a.section.category, options?.sectionOrder)
|
|
120
|
+
+ (isOfferOnlySection(a.section.items) ? OFFER_SECTION_DEMOTION : 0);
|
|
121
|
+
const rightPriority = getCategoryPriority(b.section.category, options?.sectionOrder)
|
|
122
|
+
+ (isOfferOnlySection(b.section.items) ? OFFER_SECTION_DEMOTION : 0);
|
|
87
123
|
const priorityDiff = leftPriority - rightPriority;
|
|
88
124
|
return priorityDiff !== 0 ? priorityDiff : a.index - b.index;
|
|
89
125
|
})
|
|
@@ -138,6 +174,7 @@ export function truncateText(text: string, width: number): string {
|
|
|
138
174
|
|
|
139
175
|
function getCategoryPriority(category: string, sectionOrder: CommandBarSectionOrder = "default"): number {
|
|
140
176
|
const normalized = category.trim().toLowerCase();
|
|
177
|
+
if (sectionOrder === "ranked") return 0;
|
|
141
178
|
// The AI answers the question the user actually typed, so it leads the list.
|
|
142
179
|
if (normalized === "ask ai") return -100;
|
|
143
180
|
if (normalized === "exact match") return -50;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type { Dispatch } from "react";
|
|
3
|
+
import { createInitialState, type AppAction, type AppState } from "../../../state/app/context";
|
|
4
|
+
import { createDefaultConfig } from "../../../types/config";
|
|
5
|
+
import type { DataProvider } from "../../../types/data-provider";
|
|
6
|
+
import type { TickerRecord } from "../../../types/ticker";
|
|
7
|
+
import { EventBus } from "../../../plugins/event-bus";
|
|
8
|
+
import type { PluginRegistry } from "../../../plugins/registry";
|
|
9
|
+
import { createCommandBarCollectionWorkflowActions } from "./collection-actions";
|
|
10
|
+
|
|
11
|
+
function ticker(symbol: string, portfolios: string[] = []): TickerRecord {
|
|
12
|
+
return {
|
|
13
|
+
metadata: {
|
|
14
|
+
ticker: symbol,
|
|
15
|
+
exchange: "NASDAQ",
|
|
16
|
+
currency: "USD",
|
|
17
|
+
name: symbol,
|
|
18
|
+
portfolios,
|
|
19
|
+
watchlists: [],
|
|
20
|
+
positions: [],
|
|
21
|
+
custom: {},
|
|
22
|
+
tags: [],
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function createHarness(initialTicker: TickerRecord) {
|
|
28
|
+
const state: AppState = {
|
|
29
|
+
...createInitialState(createDefaultConfig(":memory:")),
|
|
30
|
+
tickers: new Map([[initialTicker.metadata.ticker, initialTicker]]),
|
|
31
|
+
};
|
|
32
|
+
const order: string[] = [];
|
|
33
|
+
const payloads: Array<{ symbol: string; portfolioId: string }> = [];
|
|
34
|
+
const events = new EventBus();
|
|
35
|
+
const pluginRegistry = { events } as PluginRegistry;
|
|
36
|
+
events.on("command-bar:portfolio-membership-persisted", (payload) => {
|
|
37
|
+
order.push("event");
|
|
38
|
+
payloads.push(payload);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const actions = createCommandBarCollectionWorkflowActions({
|
|
42
|
+
activeCollectionId: "main",
|
|
43
|
+
activeTickerSymbol: initialTicker.metadata.ticker,
|
|
44
|
+
dataProvider: {} as DataProvider,
|
|
45
|
+
dispatch: ((action: AppAction) => {
|
|
46
|
+
expect(action.type).toBe("UPDATE_TICKER");
|
|
47
|
+
order.push("dispatch");
|
|
48
|
+
}) as Dispatch<AppAction>,
|
|
49
|
+
getState: () => state,
|
|
50
|
+
notify: () => {},
|
|
51
|
+
persistConfig: () => {},
|
|
52
|
+
pluginRegistry,
|
|
53
|
+
setActiveCollection: () => {},
|
|
54
|
+
tickerRepository: {
|
|
55
|
+
loadAllTickers: async () => [],
|
|
56
|
+
loadTicker: async () => null,
|
|
57
|
+
saveTicker: async () => {
|
|
58
|
+
order.push("save:start");
|
|
59
|
+
await Promise.resolve();
|
|
60
|
+
order.push("save:end");
|
|
61
|
+
},
|
|
62
|
+
createTicker: async () => initialTicker,
|
|
63
|
+
deleteTicker: async () => {},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return { actions, order, payloads };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe("command-bar collection workflow actions", () => {
|
|
71
|
+
test("emits portfolio membership after the ticker is saved and dispatched", async () => {
|
|
72
|
+
const { actions, order, payloads } = createHarness(ticker("AAPL"));
|
|
73
|
+
|
|
74
|
+
await actions.addTickerMembershipFromWorkflow({
|
|
75
|
+
portfolioId: "main",
|
|
76
|
+
ticker: "AAPL",
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect(order).toEqual(["save:start", "save:end", "dispatch", "event"]);
|
|
80
|
+
expect(payloads).toEqual([{ symbol: "AAPL", portfolioId: "main" }]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("emits portfolio membership when a resumed AP finds it already durable", async () => {
|
|
84
|
+
const { actions, order, payloads } = createHarness(ticker("AAPL", ["main"]));
|
|
85
|
+
|
|
86
|
+
await actions.addTickerMembershipFromWorkflow({
|
|
87
|
+
portfolioId: "main",
|
|
88
|
+
ticker: "AAPL",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
expect(order).toEqual(["event"]);
|
|
92
|
+
expect(payloads).toEqual([{ symbol: "AAPL", portfolioId: "main" }]);
|
|
93
|
+
});
|
|
94
|
+
});
|