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
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
ConnectionHealthRegistry,
|
|
4
|
+
GLOOM_CLOUD_SOCKET_CONNECTION_ID,
|
|
5
|
+
type ConnectionHealthStatus,
|
|
6
|
+
} from "../core/connection-health";
|
|
7
|
+
import { CloudApiSocket } from "./socket";
|
|
8
|
+
|
|
9
|
+
function waitForStatus(
|
|
10
|
+
health: ConnectionHealthRegistry,
|
|
11
|
+
status: ConnectionHealthStatus,
|
|
12
|
+
): Promise<void> {
|
|
13
|
+
if (health.getSnapshot().sources[0]?.status === status) return Promise.resolve();
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const timer = setTimeout(() => {
|
|
16
|
+
unsubscribe();
|
|
17
|
+
reject(new Error(`Timed out waiting for ${status}`));
|
|
18
|
+
}, 2_000);
|
|
19
|
+
const unsubscribe = health.subscribe(() => {
|
|
20
|
+
if (health.getSnapshot().sources[0]?.status !== status) return;
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
unsubscribe();
|
|
23
|
+
resolve();
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("CloudApiSocket connection health", () => {
|
|
29
|
+
test("reports transitions from a real loopback websocket", async () => {
|
|
30
|
+
let peer: ServerWebSocket<unknown> | null = null;
|
|
31
|
+
const server = Bun.serve({
|
|
32
|
+
hostname: "127.0.0.1",
|
|
33
|
+
port: 0,
|
|
34
|
+
fetch(request, server) {
|
|
35
|
+
return server.upgrade(request) ? undefined : new Response("Upgrade required", { status: 426 });
|
|
36
|
+
},
|
|
37
|
+
websocket: {
|
|
38
|
+
open(socket) {
|
|
39
|
+
peer = socket;
|
|
40
|
+
},
|
|
41
|
+
message() {},
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
const health = new ConnectionHealthRegistry();
|
|
45
|
+
health.registerSource({
|
|
46
|
+
id: GLOOM_CLOUD_SOCKET_CONNECTION_ID,
|
|
47
|
+
name: "Gloom Cloud Stream",
|
|
48
|
+
kind: "websocket",
|
|
49
|
+
});
|
|
50
|
+
const socket = new CloudApiSocket({
|
|
51
|
+
getBaseUrl: () => `http://127.0.0.1:${server.port}`,
|
|
52
|
+
getSocketAuthToken: () => null,
|
|
53
|
+
hasVerifiedUser: () => false,
|
|
54
|
+
isUsingWebSocketToken: () => false,
|
|
55
|
+
clearWebSocketTokenForFallback: () => false,
|
|
56
|
+
markCurrentUserUnverified: () => {},
|
|
57
|
+
updateCurrentUserFromSocket: () => {},
|
|
58
|
+
}, health);
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
socket.subscribeQuotes([{ symbol: "AAPL" }], () => {});
|
|
62
|
+
expect(health.getSnapshot().sources[0]).toMatchObject({
|
|
63
|
+
status: "connecting",
|
|
64
|
+
socketState: "connecting",
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await waitForStatus(health, "connected");
|
|
68
|
+
peer!.close(1000, "network lost");
|
|
69
|
+
await waitForStatus(health, "disconnected");
|
|
70
|
+
expect(health.getSnapshot().sources[0]).toMatchObject({
|
|
71
|
+
socketState: "closed",
|
|
72
|
+
currentDetail: "network lost",
|
|
73
|
+
});
|
|
74
|
+
} finally {
|
|
75
|
+
socket.dispose();
|
|
76
|
+
void server.stop(true);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
package/src/api-client/socket.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type {
|
|
|
4
4
|
ChatNotification,
|
|
5
5
|
CloudQuotePayload,
|
|
6
6
|
QuoteStreamTarget,
|
|
7
|
+
ScannerFeedEvent,
|
|
8
|
+
ScannerKind,
|
|
7
9
|
} from "./types";
|
|
8
10
|
import {
|
|
9
11
|
normalizeChatMessage,
|
|
@@ -12,6 +14,11 @@ import {
|
|
|
12
14
|
import { debugLog } from "../utils/debug-log";
|
|
13
15
|
import { canonicalExchange, normalizeSymbol } from "../utils/exchanges";
|
|
14
16
|
import { mergeQuoteSubscriptionTargets } from "../market-data/quote-subscription-target";
|
|
17
|
+
import {
|
|
18
|
+
connectionHealth,
|
|
19
|
+
GLOOM_CLOUD_SOCKET_CONNECTION_ID,
|
|
20
|
+
type ConnectionHealthRegistry,
|
|
21
|
+
} from "../core/connection-health";
|
|
15
22
|
|
|
16
23
|
const QUOTE_SUBSCRIPTION_FLUSH_MS = 25;
|
|
17
24
|
const cloudApiLog = debugLog.createLogger("cloud-api");
|
|
@@ -24,6 +31,12 @@ type QuoteSubscription = {
|
|
|
24
31
|
target: QuoteStreamTarget;
|
|
25
32
|
listener: QuoteListener;
|
|
26
33
|
};
|
|
34
|
+
type ScannerListener = (event: ScannerFeedEvent) => void;
|
|
35
|
+
|
|
36
|
+
const SCANNER_MESSAGE_KINDS: Record<string, ScannerKind> = {
|
|
37
|
+
"scanner.hilo": "hilo",
|
|
38
|
+
"scanner.flow": "flow",
|
|
39
|
+
};
|
|
27
40
|
|
|
28
41
|
function mergeQuoteStreamSubscriptions(
|
|
29
42
|
subscriptions: Iterable<QuoteSubscription>,
|
|
@@ -68,8 +81,14 @@ export class CloudApiSocket {
|
|
|
68
81
|
private readonly pendingQuoteSubscribes = new Map<string, QuoteStreamTarget>();
|
|
69
82
|
private readonly pendingQuoteUnsubscribes = new Map<string, QuoteStreamTarget>();
|
|
70
83
|
private quoteSubscriptionFlushTimer: ReturnType<typeof setTimeout> | null = null;
|
|
84
|
+
private readonly scannerListeners = new Map<ScannerKind, Set<ScannerListener>>();
|
|
85
|
+
/** Latest fan-out payload, so a pane opened mid-stream does not wait for the next tick. */
|
|
86
|
+
private readonly scannerSnapshots = new Map<ScannerKind, ScannerFeedEvent>();
|
|
71
87
|
|
|
72
|
-
constructor(
|
|
88
|
+
constructor(
|
|
89
|
+
private readonly delegate: CloudApiSocketDelegate,
|
|
90
|
+
private readonly health: ConnectionHealthRegistry = connectionHealth,
|
|
91
|
+
) {}
|
|
73
92
|
|
|
74
93
|
syncAuthState(options: { reconnect?: boolean } = {}): void {
|
|
75
94
|
if (!this.shouldKeepSocketOpen()) {
|
|
@@ -91,6 +110,7 @@ export class CloudApiSocket {
|
|
|
91
110
|
this.ws = null;
|
|
92
111
|
if (ws) {
|
|
93
112
|
cloudApiLog.info("teardown websocket");
|
|
113
|
+
this.health.reportSocketState(GLOOM_CLOUD_SOCKET_CONNECTION_ID, "idle", "Socket closed locally");
|
|
94
114
|
}
|
|
95
115
|
try {
|
|
96
116
|
ws?.close();
|
|
@@ -163,6 +183,37 @@ export class CloudApiSocket {
|
|
|
163
183
|
};
|
|
164
184
|
}
|
|
165
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Subscribes to a shared server-computed scanner. Every open pane of the same
|
|
188
|
+
* kind shares one upstream subscription; the socket fans the payload out.
|
|
189
|
+
*/
|
|
190
|
+
subscribeScanner(scanner: ScannerKind, listener: ScannerListener): () => void {
|
|
191
|
+
const listeners = this.scannerListeners.get(scanner) ?? new Set<ScannerListener>();
|
|
192
|
+
const firstListener = listeners.size === 0;
|
|
193
|
+
listeners.add(listener);
|
|
194
|
+
this.scannerListeners.set(scanner, listeners);
|
|
195
|
+
this.ensureSocket();
|
|
196
|
+
if (firstListener) {
|
|
197
|
+
this.sendSocketMessage({ type: "scanner.subscribe", scanner });
|
|
198
|
+
} else {
|
|
199
|
+
const snapshot = this.scannerSnapshots.get(scanner);
|
|
200
|
+
if (snapshot) listener(snapshot);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return () => {
|
|
204
|
+
const current = this.scannerListeners.get(scanner);
|
|
205
|
+
if (!current || !current.delete(listener)) return;
|
|
206
|
+
if (current.size === 0) {
|
|
207
|
+
this.scannerListeners.delete(scanner);
|
|
208
|
+
this.scannerSnapshots.delete(scanner);
|
|
209
|
+
this.sendSocketMessage({ type: "scanner.unsubscribe", scanner });
|
|
210
|
+
}
|
|
211
|
+
if (!this.shouldKeepSocketOpen()) {
|
|
212
|
+
this.teardown();
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
166
217
|
subscribeQuotes(
|
|
167
218
|
targets: QuoteStreamTarget[],
|
|
168
219
|
onQuote: (target: QuoteStreamTarget, quote: CloudQuotePayload) => void,
|
|
@@ -264,6 +315,8 @@ export class CloudApiSocket {
|
|
|
264
315
|
this.quoteTargets.clear();
|
|
265
316
|
this.pendingQuoteSubscribes.clear();
|
|
266
317
|
this.pendingQuoteUnsubscribes.clear();
|
|
318
|
+
this.scannerListeners.clear();
|
|
319
|
+
this.scannerSnapshots.clear();
|
|
267
320
|
if (this.quoteSubscriptionFlushTimer) {
|
|
268
321
|
clearTimeout(this.quoteSubscriptionFlushTimer);
|
|
269
322
|
this.quoteSubscriptionFlushTimer = null;
|
|
@@ -296,7 +349,7 @@ export class CloudApiSocket {
|
|
|
296
349
|
return;
|
|
297
350
|
}
|
|
298
351
|
this.delegate.markCurrentUserUnverified();
|
|
299
|
-
if (this.quoteTargets.size > 0) {
|
|
352
|
+
if (this.quoteTargets.size > 0 || this.scannerListeners.size > 0) {
|
|
300
353
|
return;
|
|
301
354
|
}
|
|
302
355
|
this.teardown();
|
|
@@ -326,6 +379,24 @@ export class CloudApiSocket {
|
|
|
326
379
|
return;
|
|
327
380
|
}
|
|
328
381
|
|
|
382
|
+
const scannerKind = typeof parsed?.type === "string" ? SCANNER_MESSAGE_KINDS[parsed.type] : undefined;
|
|
383
|
+
if (scannerKind) {
|
|
384
|
+
const { type: _type, ...payload } = parsed;
|
|
385
|
+
this.emitScannerEvent(scannerKind, { type: "data", payload });
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (parsed?.type === "scanner.denied") {
|
|
390
|
+
const denied = SCANNER_MESSAGE_KINDS[`scanner.${parsed.scanner}`];
|
|
391
|
+
if (denied) {
|
|
392
|
+
this.emitScannerEvent(denied, {
|
|
393
|
+
type: "denied",
|
|
394
|
+
reason: typeof parsed.reason === "string" ? parsed.reason : "pro_required",
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
329
400
|
if (parsed?.type === "market.quote" && parsed.quote && typeof parsed.symbol === "string") {
|
|
330
401
|
const key = marketKey(parsed.symbol, parsed.exchange);
|
|
331
402
|
const quote: CloudQuotePayload = {
|
|
@@ -347,8 +418,15 @@ export class CloudApiSocket {
|
|
|
347
418
|
return baseUrl.replace(/^https?/, wsProtocol);
|
|
348
419
|
}
|
|
349
420
|
|
|
421
|
+
private emitScannerEvent(scanner: ScannerKind, event: ScannerFeedEvent): void {
|
|
422
|
+
this.scannerSnapshots.set(scanner, event);
|
|
423
|
+
for (const listener of this.scannerListeners.get(scanner) ?? []) {
|
|
424
|
+
listener(event);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
350
428
|
private shouldKeepSocketOpen(): boolean {
|
|
351
|
-
if (this.quoteTargets.size > 0) return true;
|
|
429
|
+
if (this.quoteTargets.size > 0 || this.scannerListeners.size > 0) return true;
|
|
352
430
|
return !!this.delegate.getSocketAuthToken()
|
|
353
431
|
&& this.delegate.hasVerifiedUser()
|
|
354
432
|
&& this.channelListeners.size > 0;
|
|
@@ -368,12 +446,24 @@ export class CloudApiSocket {
|
|
|
368
446
|
quoteTargets: this.quoteTargets.size,
|
|
369
447
|
channelTargets: this.channelListeners.size,
|
|
370
448
|
});
|
|
371
|
-
|
|
449
|
+
this.health.reportSocketState(GLOOM_CLOUD_SOCKET_CONNECTION_ID, "connecting", this.getWebSocketBaseUrl());
|
|
450
|
+
let ws: WebSocket;
|
|
451
|
+
try {
|
|
452
|
+
ws = new WebSocket(url);
|
|
453
|
+
} catch (error) {
|
|
454
|
+
this.health.reportSocketState(
|
|
455
|
+
GLOOM_CLOUD_SOCKET_CONNECTION_ID,
|
|
456
|
+
"error",
|
|
457
|
+
error instanceof Error ? error.message : String(error),
|
|
458
|
+
);
|
|
459
|
+
throw error;
|
|
460
|
+
}
|
|
372
461
|
this.ws = ws;
|
|
373
462
|
|
|
374
463
|
ws.onopen = () => {
|
|
375
464
|
if (this.ws !== ws) return;
|
|
376
465
|
cloudApiLog.info("websocket open");
|
|
466
|
+
this.health.reportSocketState(GLOOM_CLOUD_SOCKET_CONNECTION_ID, "open", this.getWebSocketBaseUrl());
|
|
377
467
|
this.reconnectDelayMs = 1000;
|
|
378
468
|
this.flushSubscriptions();
|
|
379
469
|
};
|
|
@@ -396,6 +486,11 @@ export class CloudApiSocket {
|
|
|
396
486
|
tokenSource: usingWebSocketToken ? "websocket" : "session",
|
|
397
487
|
});
|
|
398
488
|
if (!activeSocket) return;
|
|
489
|
+
this.health.reportSocketState(
|
|
490
|
+
GLOOM_CLOUD_SOCKET_CONNECTION_ID,
|
|
491
|
+
"closed",
|
|
492
|
+
closeEvent?.reason || (closeEvent?.code ? `Closed (${closeEvent.code})` : "Socket closed"),
|
|
493
|
+
);
|
|
399
494
|
if (usingWebSocketToken && this.delegate.clearWebSocketTokenForFallback()) {
|
|
400
495
|
this.reconnectDelayMs = 1000;
|
|
401
496
|
cloudApiLog.warn("cleared websocket token after socket close; falling back to session token");
|
|
@@ -405,7 +500,9 @@ export class CloudApiSocket {
|
|
|
405
500
|
};
|
|
406
501
|
|
|
407
502
|
ws.onerror = () => {
|
|
408
|
-
|
|
503
|
+
if (this.ws !== ws) return;
|
|
504
|
+
this.health.reportSocketState(GLOOM_CLOUD_SOCKET_CONNECTION_ID, "error", "WebSocket error");
|
|
505
|
+
// Reconnect is handled by onclose.
|
|
409
506
|
};
|
|
410
507
|
}
|
|
411
508
|
|
|
@@ -428,6 +525,8 @@ export class CloudApiSocket {
|
|
|
428
525
|
|| type === "market.unsubscribe"
|
|
429
526
|
|| type === "chat.subscribe"
|
|
430
527
|
|| type === "chat.unsubscribe"
|
|
528
|
+
|| type === "scanner.subscribe"
|
|
529
|
+
|| type === "scanner.unsubscribe"
|
|
431
530
|
) {
|
|
432
531
|
cloudApiLog.info("send websocket message", payload);
|
|
433
532
|
}
|
|
@@ -442,6 +541,10 @@ export class CloudApiSocket {
|
|
|
442
541
|
this.sendSocketMessage({ type: "chat.subscribe", channelId });
|
|
443
542
|
}
|
|
444
543
|
|
|
544
|
+
for (const scanner of this.scannerListeners.keys()) {
|
|
545
|
+
this.sendSocketMessage({ type: "scanner.subscribe", scanner });
|
|
546
|
+
}
|
|
547
|
+
|
|
445
548
|
if (this.quoteTargets.size > 0) {
|
|
446
549
|
this.sendSocketMessage({
|
|
447
550
|
type: "market.subscribe",
|
package/src/api-client/types.ts
CHANGED
|
@@ -322,10 +322,28 @@ export interface CloudFredSeriesPayload {
|
|
|
322
322
|
info: CloudFredSeriesInfoPayload | null;
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
+
export interface CloudShortInterestPointPayload {
|
|
326
|
+
settlementDate: string;
|
|
327
|
+
sharesShort: number;
|
|
328
|
+
previousSharesShort: number | null;
|
|
329
|
+
averageDailyVolume: number | null;
|
|
330
|
+
daysToCover: number | null;
|
|
331
|
+
changePercent: number | null;
|
|
332
|
+
revised: boolean;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export interface CloudShortInterestPayload {
|
|
336
|
+
symbol: string;
|
|
337
|
+
issueName: string | null;
|
|
338
|
+
points: CloudShortInterestPointPayload[];
|
|
339
|
+
}
|
|
340
|
+
|
|
325
341
|
export interface CloudYieldPointPayload {
|
|
326
342
|
maturity: string;
|
|
327
343
|
maturityYears: number;
|
|
328
344
|
yield: number | null;
|
|
345
|
+
/** FRED observation date. Absent on servers older than the field. */
|
|
346
|
+
asOf?: string | null;
|
|
329
347
|
}
|
|
330
348
|
|
|
331
349
|
type CloudCongressTradeSide = "BUY" | "SELL" | "EXCHANGE" | "OTHER";
|
|
@@ -570,12 +588,133 @@ export interface CloudMarketBatchPayload<T> {
|
|
|
570
588
|
items: Array<CloudMarketBatchItem<T>>;
|
|
571
589
|
}
|
|
572
590
|
|
|
591
|
+
export type CloudMarketScreenerCategory = "gainers" | "losers" | "most-active";
|
|
592
|
+
|
|
593
|
+
export interface CloudMarketScreenerItem {
|
|
594
|
+
rank: number;
|
|
595
|
+
symbol: string;
|
|
596
|
+
name: string;
|
|
597
|
+
price: number;
|
|
598
|
+
change: number;
|
|
599
|
+
changePercent: number;
|
|
600
|
+
volume: number;
|
|
601
|
+
tradeCount?: number;
|
|
602
|
+
currency: string;
|
|
603
|
+
high52w?: number;
|
|
604
|
+
low52w?: number;
|
|
605
|
+
dayHigh?: number;
|
|
606
|
+
dayLow?: number;
|
|
607
|
+
exchange: string;
|
|
608
|
+
lastUpdated: number;
|
|
609
|
+
dataSource: "live";
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export interface CloudMarketScreenerPayload {
|
|
613
|
+
providerId: "gloomberb-cloud";
|
|
614
|
+
category: CloudMarketScreenerCategory;
|
|
615
|
+
asOf: string;
|
|
616
|
+
stale?: boolean;
|
|
617
|
+
items: CloudMarketScreenerItem[];
|
|
618
|
+
}
|
|
619
|
+
|
|
573
620
|
export interface CloudVerificationResponse {
|
|
574
621
|
sent: boolean;
|
|
575
622
|
email?: string;
|
|
576
623
|
alreadyVerified?: boolean;
|
|
577
624
|
}
|
|
578
625
|
|
|
626
|
+
/** A short-lived, single-use browser URL that establishes the existing desktop session. */
|
|
627
|
+
export interface CloudBrowserHandoffResponse {
|
|
628
|
+
url: string;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
export interface DeviceAuthStartResponse {
|
|
632
|
+
deviceCode: string;
|
|
633
|
+
/** 8 characters formatted "XXXX-XXXX", shown so a user without the app can type it. */
|
|
634
|
+
userCode: string;
|
|
635
|
+
expiresAt: string;
|
|
636
|
+
/** Like https://gloom.sh/link/<userCode>; this is what the QR code encodes. */
|
|
637
|
+
verificationUri: string;
|
|
638
|
+
pollIntervalMs: number;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/** The approved response is single-use; polling again returns a non-approved status. */
|
|
642
|
+
export type DeviceAuthTokenResponse =
|
|
643
|
+
| { status: "pending" }
|
|
644
|
+
| { status: "denied" }
|
|
645
|
+
| { status: "expired" }
|
|
646
|
+
| { status: "approved"; sessionToken: string; user: AuthUser };
|
|
647
|
+
|
|
648
|
+
/** Shared server-computed scanners fanned out to every subscriber. */
|
|
649
|
+
export type ScannerKind = "hilo" | "flow";
|
|
650
|
+
|
|
651
|
+
export type ScannerStatus = "live" | "starting" | "closed" | "degraded";
|
|
652
|
+
|
|
653
|
+
export interface ScannerWindowCounts {
|
|
654
|
+
highs: number;
|
|
655
|
+
lows: number;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
export interface ScannerHiloExtreme {
|
|
659
|
+
symbol: string;
|
|
660
|
+
price: number;
|
|
661
|
+
volume?: number | null;
|
|
662
|
+
/** Times this symbol printed a new session extreme today. */
|
|
663
|
+
count: number;
|
|
664
|
+
at: number;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/** Which entitlement tier produced the payload: free accounts get a delayed instance. */
|
|
668
|
+
export interface ScannerAccessInfo {
|
|
669
|
+
access: "realtime" | "delayed";
|
|
670
|
+
/** 0 on the realtime instance, 15 on the delayed one. */
|
|
671
|
+
delayMinutes: number;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export interface ScannerHiloPayload extends ScannerAccessInfo {
|
|
675
|
+
status: ScannerStatus;
|
|
676
|
+
asOf: number;
|
|
677
|
+
windows: {
|
|
678
|
+
s30: ScannerWindowCounts;
|
|
679
|
+
m1: ScannerWindowCounts;
|
|
680
|
+
m5: ScannerWindowCounts;
|
|
681
|
+
};
|
|
682
|
+
highs: ScannerHiloExtreme[];
|
|
683
|
+
lows: ScannerHiloExtreme[];
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
export interface ScannerFlowEvent {
|
|
687
|
+
id: string;
|
|
688
|
+
at: number;
|
|
689
|
+
underlying: string;
|
|
690
|
+
contract: string;
|
|
691
|
+
right: "C" | "P";
|
|
692
|
+
strike: number;
|
|
693
|
+
expiry: string;
|
|
694
|
+
side: "ask" | "bid" | "mid" | "unknown";
|
|
695
|
+
kind: "sweep" | "block" | "split" | "trade";
|
|
696
|
+
size: number;
|
|
697
|
+
price: number;
|
|
698
|
+
premium: number;
|
|
699
|
+
volume?: number | null;
|
|
700
|
+
openInterest?: number | null;
|
|
701
|
+
volOi?: number | null;
|
|
702
|
+
iv?: number | null;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
export interface ScannerFlowPayload extends ScannerAccessInfo {
|
|
706
|
+
status: ScannerStatus;
|
|
707
|
+
asOf: number;
|
|
708
|
+
events: ScannerFlowEvent[];
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
export type ScannerPayload = ScannerHiloPayload | ScannerFlowPayload;
|
|
712
|
+
|
|
713
|
+
/** What a scanner subscriber receives: data, or the entitlement refusal. */
|
|
714
|
+
export type ScannerFeedEvent<T extends ScannerPayload = ScannerPayload> =
|
|
715
|
+
| { type: "data"; payload: T }
|
|
716
|
+
| { type: "denied"; reason: string };
|
|
717
|
+
|
|
579
718
|
export interface QuoteStreamTarget {
|
|
580
719
|
symbol: string;
|
|
581
720
|
exchange?: string;
|
|
@@ -26,7 +26,10 @@ afterEach(async () => {
|
|
|
26
26
|
}
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
function createRegistry(
|
|
29
|
+
function createRegistry(
|
|
30
|
+
shortcutExecute?: () => void,
|
|
31
|
+
onShowPane?: (paneId: string) => void,
|
|
32
|
+
): PluginRegistry {
|
|
30
33
|
return {
|
|
31
34
|
shortcuts: new Map(shortcutExecute
|
|
32
35
|
? [["test-shortcut", { id: "test-shortcut", key: "x", execute: shortcutExecute }]]
|
|
@@ -39,6 +42,7 @@ function createRegistry(shortcutExecute?: () => void): PluginRegistry {
|
|
|
39
42
|
]),
|
|
40
43
|
getPluginPaneIds: () => [],
|
|
41
44
|
getShortcutPluginId: () => null,
|
|
45
|
+
showPane: onShowPane ?? (() => {}),
|
|
42
46
|
} as unknown as PluginRegistry;
|
|
43
47
|
}
|
|
44
48
|
|
|
@@ -96,12 +100,28 @@ async function renderHarness(
|
|
|
96
100
|
});
|
|
97
101
|
}
|
|
98
102
|
|
|
99
|
-
|
|
103
|
+
/** The OpenTUI input host derives `targetEditable` from the focused editor. */
|
|
104
|
+
function focusEditor() {
|
|
105
|
+
Object.defineProperty(testSetup!.renderer, "currentFocusedEditor", {
|
|
106
|
+
configurable: true,
|
|
107
|
+
get: () => ({}),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function emitKeypress(event: {
|
|
112
|
+
name?: string;
|
|
113
|
+
ctrl?: boolean;
|
|
114
|
+
meta?: boolean;
|
|
115
|
+
super?: boolean;
|
|
116
|
+
shift?: boolean;
|
|
117
|
+
alt?: boolean;
|
|
118
|
+
}) {
|
|
100
119
|
const keyEvent = {
|
|
101
120
|
ctrl: false,
|
|
102
121
|
meta: false,
|
|
103
122
|
super: false,
|
|
104
123
|
option: false,
|
|
124
|
+
alt: false,
|
|
105
125
|
shift: false,
|
|
106
126
|
eventType: "press",
|
|
107
127
|
repeated: false,
|
|
@@ -164,17 +184,20 @@ describe("useAppGlobalShortcuts", () => {
|
|
|
164
184
|
expect(event.propagationStopped).toBe(true);
|
|
165
185
|
});
|
|
166
186
|
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
const config = createDefaultConfig("/tmp/gloomberb-global-shortcuts-layouts");
|
|
187
|
+
function layoutState(suffix: string, options: { commandBarOpen?: boolean } = {}) {
|
|
188
|
+
const config = createDefaultConfig(`/tmp/gloomberb-global-shortcuts-${suffix}`);
|
|
170
189
|
config.layouts = [
|
|
171
190
|
{ name: "One", layout: cloneLayout(config.layout) },
|
|
172
191
|
{ name: "Two", layout: cloneLayout(config.layout) },
|
|
173
192
|
{ name: "Three", layout: cloneLayout(config.layout) },
|
|
174
193
|
];
|
|
175
194
|
config.activeLayoutIndex = 0;
|
|
176
|
-
|
|
177
|
-
|
|
195
|
+
return { ...createInitialState(config), ...options };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
test("switches saved layouts with Ctrl-number and consumes the shortcut", async () => {
|
|
199
|
+
const actions: AppAction[] = [];
|
|
200
|
+
await renderHarness(layoutState("layouts"), createRegistry(), (action) => actions.push(action));
|
|
178
201
|
|
|
179
202
|
const event = await emitKeypress({ name: "2", ctrl: true });
|
|
180
203
|
|
|
@@ -183,6 +206,72 @@ describe("useAppGlobalShortcuts", () => {
|
|
|
183
206
|
expect(event.propagationStopped).toBe(true);
|
|
184
207
|
});
|
|
185
208
|
|
|
209
|
+
// Browsers and the desktop webview report Cmd as meta; the OpenTUI host maps
|
|
210
|
+
// the kitty `super` modifier onto the same field.
|
|
211
|
+
test("switches saved layouts with the Cmd-number reported by web and kitty hosts", async () => {
|
|
212
|
+
const actions: AppAction[] = [];
|
|
213
|
+
await renderHarness(layoutState("layouts-meta"), createRegistry(), (action) => actions.push(action));
|
|
214
|
+
|
|
215
|
+
const event = await emitKeypress({ name: "3", super: true });
|
|
216
|
+
|
|
217
|
+
expect(actions).toEqual([{ type: "SWITCH_LAYOUT", index: 2 }]);
|
|
218
|
+
expect(event.defaultPrevented).toBe(true);
|
|
219
|
+
expect(event.propagationStopped).toBe(true);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Alt-digit keeps its terminal meaning; only the primary modifier switches.
|
|
223
|
+
test("ignores Alt-number", async () => {
|
|
224
|
+
const actions: AppAction[] = [];
|
|
225
|
+
await renderHarness(layoutState("layouts-alt"), createRegistry(), (action) => actions.push(action));
|
|
226
|
+
|
|
227
|
+
const event = await emitKeypress({ name: "2", alt: true });
|
|
228
|
+
|
|
229
|
+
expect(actions).toEqual([]);
|
|
230
|
+
expect(event.defaultPrevented).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Leaving the digit unclaimed lets the desktop webview treat Cmd-digit as its
|
|
234
|
+
// own browser tab shortcut, which navigates the app away.
|
|
235
|
+
test("consumes layout numbers while the command bar is open without switching", async () => {
|
|
236
|
+
const actions: AppAction[] = [];
|
|
237
|
+
await renderHarness(
|
|
238
|
+
layoutState("layouts-command-bar", { commandBarOpen: true }),
|
|
239
|
+
createRegistry(),
|
|
240
|
+
(action) => actions.push(action),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const event = await emitKeypress({ name: "2", ctrl: true });
|
|
244
|
+
|
|
245
|
+
expect(actions).toEqual([]);
|
|
246
|
+
expect(event.defaultPrevented).toBe(true);
|
|
247
|
+
expect(event.propagationStopped).toBe(true);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("consumes primary-modifier numbers with only one layout", async () => {
|
|
251
|
+
const actions: AppAction[] = [];
|
|
252
|
+
const config = createDefaultConfig("/tmp/gloomberb-global-shortcuts-one-layout");
|
|
253
|
+
const state = { ...createInitialState(config), commandBarOpen: true };
|
|
254
|
+
await renderHarness(state, createRegistry(), (action) => actions.push(action));
|
|
255
|
+
|
|
256
|
+
const event = await emitKeypress({ name: "1", super: true });
|
|
257
|
+
|
|
258
|
+
expect(actions).toEqual([]);
|
|
259
|
+
expect(event.defaultPrevented).toBe(true);
|
|
260
|
+
expect(event.propagationStopped).toBe(true);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("consumes layout numbers while an editable field owns the keyboard", async () => {
|
|
264
|
+
const actions: AppAction[] = [];
|
|
265
|
+
await renderHarness(layoutState("layouts-editable"), createRegistry(), (action) => actions.push(action));
|
|
266
|
+
focusEditor();
|
|
267
|
+
|
|
268
|
+
const event = await emitKeypress({ name: "2", super: true });
|
|
269
|
+
|
|
270
|
+
expect(actions).toEqual([]);
|
|
271
|
+
expect(event.defaultPrevented).toBe(true);
|
|
272
|
+
expect(event.propagationStopped).toBe(true);
|
|
273
|
+
});
|
|
274
|
+
|
|
186
275
|
test("does not run plain plugin shortcuts while input is captured", async () => {
|
|
187
276
|
let executed = 0;
|
|
188
277
|
const actions: AppAction[] = [];
|
|
@@ -202,6 +291,54 @@ describe("useAppGlobalShortcuts", () => {
|
|
|
202
291
|
expect(event.propagationStopped).toBe(false);
|
|
203
292
|
});
|
|
204
293
|
|
|
294
|
+
test("opens Help with question mark after the command bar is closed", async () => {
|
|
295
|
+
const openedPanes: string[] = [];
|
|
296
|
+
const actions: AppAction[] = [];
|
|
297
|
+
const state = createInitialState(createDefaultConfig("/tmp/gloomberb-global-shortcuts-help"));
|
|
298
|
+
await renderHarness(state, createRegistry(undefined, (paneId) => openedPanes.push(paneId)), (action) => actions.push(action));
|
|
299
|
+
|
|
300
|
+
const event = await emitKeypress({ name: "?", shift: true });
|
|
301
|
+
|
|
302
|
+
expect(openedPanes).toEqual(["help"]);
|
|
303
|
+
expect(actions).toEqual([]);
|
|
304
|
+
expect(event.defaultPrevented).toBe(true);
|
|
305
|
+
expect(event.propagationStopped).toBe(true);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test("does not open Help with question mark while using the command bar", async () => {
|
|
309
|
+
const openedPanes: string[] = [];
|
|
310
|
+
const actions: AppAction[] = [];
|
|
311
|
+
const config = createDefaultConfig("/tmp/gloomberb-global-shortcuts-help-guard");
|
|
312
|
+
const commandBarState = {
|
|
313
|
+
...createInitialState(config),
|
|
314
|
+
commandBarOpen: true,
|
|
315
|
+
};
|
|
316
|
+
await renderHarness(commandBarState, createRegistry(undefined, (paneId) => openedPanes.push(paneId)), (action) => actions.push(action));
|
|
317
|
+
|
|
318
|
+
let event = await emitKeypress({ name: "?", shift: true });
|
|
319
|
+
expect(openedPanes).toEqual([]);
|
|
320
|
+
expect(actions).toEqual([]);
|
|
321
|
+
expect(event.defaultPrevented).toBe(false);
|
|
322
|
+
expect(event.propagationStopped).toBe(false);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("does not open Help with question mark while typing", async () => {
|
|
326
|
+
const openedPanes: string[] = [];
|
|
327
|
+
const actions: AppAction[] = [];
|
|
328
|
+
const config = createDefaultConfig("/tmp/gloomberb-global-shortcuts-help-input");
|
|
329
|
+
const inputCapturedState = {
|
|
330
|
+
...createInitialState(config),
|
|
331
|
+
inputCaptured: true,
|
|
332
|
+
};
|
|
333
|
+
await renderHarness(inputCapturedState, createRegistry(undefined, (paneId) => openedPanes.push(paneId)), (action) => actions.push(action));
|
|
334
|
+
|
|
335
|
+
const event = await emitKeypress({ name: "?", shift: true });
|
|
336
|
+
expect(openedPanes).toEqual([]);
|
|
337
|
+
expect(actions).toEqual([]);
|
|
338
|
+
expect(event.defaultPrevented).toBe(false);
|
|
339
|
+
expect(event.propagationStopped).toBe(false);
|
|
340
|
+
});
|
|
341
|
+
|
|
205
342
|
test("cycles panes with Tab while input is captured", async () => {
|
|
206
343
|
const actions: AppAction[] = [];
|
|
207
344
|
const state = {
|
|
@@ -218,8 +355,6 @@ describe("useAppGlobalShortcuts", () => {
|
|
|
218
355
|
"portfolio-list:main",
|
|
219
356
|
"chat:main",
|
|
220
357
|
"ticker-detail:main",
|
|
221
|
-
"ticker-detail:nvda",
|
|
222
|
-
"help:main",
|
|
223
358
|
],
|
|
224
359
|
}]);
|
|
225
360
|
expect(event.defaultPrevented).toBe(true);
|