gloomberb 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -11
- package/package.json +12 -5
- package/src/app.tsx +414 -191
- package/src/architecture/import-boundaries.test.ts +118 -0
- package/src/assets/gloomberb-logo.png +0 -0
- package/src/assets/gloomberb-logo.svg +73 -0
- package/src/brokers/account-cache.ts +157 -0
- package/src/brokers/profile-form.test.ts +73 -0
- package/src/brokers/profile-form.ts +109 -0
- package/src/brokers/remote-broker-adapter.ts +91 -0
- package/src/brokers/sync-broker-instance.test.ts +124 -1
- package/src/brokers/sync-broker-instance.ts +113 -17
- package/src/capabilities/factories.ts +100 -0
- package/src/capabilities/index.ts +4 -0
- package/src/capabilities/registry.test.ts +137 -0
- package/src/capabilities/registry.ts +131 -0
- package/src/capabilities/service-capabilities.ts +27 -0
- package/src/capabilities/types.ts +84 -0
- package/src/cli/commands/ticker.ts +11 -3
- package/src/cli/context.ts +19 -13
- package/src/cli/helpers.ts +1 -0
- package/src/cli/types.ts +2 -2
- package/src/components/chart/chart-controller.ts +4 -24
- package/src/components/chart/chart-data.ts +3 -14
- package/src/components/chart/chart-indicator-selector.tsx +28 -6
- package/src/components/chart/chart-pane-settings.ts +15 -28
- package/src/components/chart/chart-renderer.ts +74 -99
- package/src/components/chart/chart-resolution.ts +10 -10
- package/src/components/chart/chart-scroll.test.ts +6 -5
- package/src/components/chart/chart-scroll.ts +2 -2
- package/src/components/chart/chart-types.ts +0 -1
- package/src/components/chart/chart-viewport.ts +2 -1
- package/src/components/chart/chart.test.ts +37 -0
- package/src/components/chart/comparison-chart-data.ts +3 -17
- package/src/components/chart/comparison-chart-renderer.test.ts +27 -4
- package/src/components/chart/comparison-chart-renderer.ts +1 -1
- package/src/components/chart/comparison-stock-chart.tsx +304 -108
- package/src/components/chart/cursor-motion.ts +3 -2
- package/src/components/chart/indicators/bands.test.ts +0 -14
- package/src/components/chart/indicators/moving-averages.test.ts +0 -15
- package/src/components/chart/indicators/options.ts +3 -3
- package/src/components/chart/indicators/oscillators.test.ts +0 -21
- package/src/components/chart/indicators/types.ts +1 -2
- package/src/components/chart/native/chart-rasterizer.ts +37 -44
- package/src/components/chart/native/kitty-adapter.ts +2 -5
- package/src/components/chart/native/kitty-manager.ts +1 -1
- package/src/components/chart/native/kitty-protocol.ts +0 -1
- package/src/components/chart/native/kitty-support.ts +1 -1
- package/src/components/chart/native/kitty.test.ts +30 -13
- package/src/components/chart/native/renderer-selection.ts +1 -1
- package/src/components/chart/native/surface-manager.ts +1 -1
- package/src/components/chart/static-chart-surface.test.tsx +51 -0
- package/src/components/chart/static-chart-surface.tsx +191 -0
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +242 -35
- package/src/components/chart/stock-chart.tsx +438 -141
- package/src/components/command-bar/__snapshots__/command-bar.test.tsx.snap +4 -4
- package/src/components/command-bar/command-bar.test.tsx +269 -20
- package/src/components/command-bar/command-bar.tsx +830 -353
- package/src/components/command-bar/command-registry.ts +10 -20
- package/src/components/command-bar/helpers.test.ts +1 -1
- package/src/components/command-bar/helpers.ts +9 -11
- package/src/components/command-bar/pane-template-display.ts +4 -8
- package/src/components/command-bar/root-shortcuts.ts +12 -11
- package/src/components/command-bar/view-model.test.ts +8 -42
- package/src/components/command-bar/view-model.ts +8 -36
- package/src/components/command-bar/workflow-ops.test.ts +76 -2
- package/src/components/command-bar/workflow-ops.ts +40 -9
- package/src/components/command-bar/workflow-types.ts +2 -1
- package/src/components/data-table-stack-view.test.tsx +206 -0
- package/src/components/data-table-stack-view.tsx +152 -0
- package/src/components/data-table-view.test.tsx +188 -0
- package/src/components/data-table-view.tsx +279 -0
- package/src/components/feed-data-table-stack-view.test.tsx +225 -0
- package/src/components/feed-data-table-stack-view.tsx +365 -0
- package/src/components/index.ts +26 -31
- package/src/components/layout/detached-pane-shell.tsx +185 -0
- package/src/components/layout/floating-pane.tsx +87 -14
- package/src/components/layout/header.tsx +139 -49
- package/src/components/layout/pane-content.tsx +40 -0
- package/src/components/layout/pane-footer.test.tsx +195 -0
- package/src/components/layout/pane-footer.tsx +410 -0
- package/src/components/layout/pane-header.test.tsx +39 -0
- package/src/components/layout/pane-header.tsx +192 -14
- package/src/components/layout/pane-sizing.ts +9 -10
- package/src/components/layout/pane-title.ts +35 -0
- package/src/components/layout/pane.tsx +52 -11
- package/src/components/layout/shell.test.tsx +315 -107
- package/src/components/layout/shell.tsx +1010 -368
- package/src/components/layout/status-bar.test.tsx +17 -10
- package/src/components/layout/status-bar.tsx +367 -100
- package/src/components/layout/titlebar-overlay.ts +2 -0
- package/src/components/markdown-editor.tsx +29 -14
- package/src/components/markdown-text.tsx +25 -24
- package/src/components/onboarding/onboarding-wizard.test.tsx +8 -3
- package/src/components/onboarding/onboarding-wizard.tsx +347 -473
- package/src/components/pane-settings-dialog.tsx +521 -89
- package/src/components/pane-template-wizard.test.tsx +1 -1
- package/src/components/pane-template-wizard.tsx +38 -37
- package/src/components/price-selector-dialog.tsx +22 -21
- package/src/components/ticker-badge-list.tsx +53 -0
- package/src/components/ticker-badge-text.test.tsx +44 -1
- package/src/components/ticker-badge-text.tsx +78 -38
- package/src/components/ticker-badge.tsx +28 -7
- package/src/components/ticker-list-table-view.tsx +248 -0
- package/src/components/ticker-list-table.test.tsx +47 -1
- package/src/components/ticker-list-table.tsx +212 -41
- package/src/components/toggle-list.tsx +114 -8
- package/src/components/ui/button.tsx +24 -16
- package/src/components/ui/choice-dialog.tsx +89 -0
- package/src/components/ui/data-table.tsx +313 -114
- package/src/components/ui/external-link.tsx +52 -13
- package/src/components/ui/fields.tsx +44 -30
- package/src/components/ui/frame.tsx +24 -57
- package/src/components/ui/index.ts +15 -50
- package/src/components/ui/list-view.tsx +65 -25
- package/src/components/ui/loading.tsx +5 -60
- package/src/components/ui/message-composer.test.tsx +81 -0
- package/src/components/ui/message-composer.tsx +139 -0
- package/src/components/ui/multi-select-dialog.tsx +100 -32
- package/src/components/ui/multi-select.ts +112 -0
- package/src/components/ui/native-select.tsx +120 -0
- package/src/components/ui/page-stack-view.tsx +49 -19
- package/src/components/ui/remote-image.tsx +75 -0
- package/src/components/ui/row-value-cache.test.ts +17 -0
- package/src/components/ui/row-value-cache.ts +41 -0
- package/src/components/ui/shortcut-hint.tsx +55 -0
- package/src/components/ui/status.tsx +12 -67
- package/src/components/ui/table-layout.ts +59 -0
- package/src/components/ui/tabs.tsx +249 -27
- package/src/components/ui/toggle.tsx +19 -127
- package/src/components/ui/ui.test.tsx +266 -53
- package/src/components/use-double-click-activation.ts +7 -2
- package/src/components/use-external-link-footer.ts +79 -0
- package/src/core/app-services.ts +95 -0
- package/src/core/state/app-state.ts +886 -0
- package/src/{state → core/state}/session-persistence.ts +7 -6
- package/src/data/config-layout.ts +225 -0
- package/src/data/config-store-node.ts +390 -0
- package/src/data/config-store.test.ts +72 -0
- package/src/data/config-store.ts +35 -478
- package/src/data/sqlite-json.ts +2 -5
- package/src/data/sqlite-retry.ts +1 -0
- package/src/index.tsx +2 -68
- package/src/market-data/coordinator-subscriptions.test.ts +125 -0
- package/src/market-data/coordinator.test.ts +73 -4
- package/src/market-data/coordinator.ts +190 -24
- package/src/market-data/hooks.test.tsx +1 -1
- package/src/market-data/hooks.tsx +109 -87
- package/src/market-data/query-store.ts +3 -3
- package/src/market-data/request-types.test.ts +0 -14
- package/src/market-data/result-types.ts +1 -0
- package/src/news/aggregator.test.ts +267 -0
- package/src/news/aggregator.ts +452 -0
- package/src/news/hooks.ts +39 -0
- package/src/news/ticker-symbols.ts +21 -0
- package/src/news/types.ts +77 -0
- package/src/notifications/app-notifier.ts +21 -10
- package/src/pane-settings.test.ts +0 -2
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +124 -134
- package/src/plugins/builtin/ai/gloomberb-cli.ts +14 -13
- package/src/plugins/builtin/ai/providers.ts +1 -9
- package/src/plugins/builtin/ai/runner.ts +41 -5
- package/src/plugins/builtin/ai/screener-pane.test.tsx +37 -55
- package/src/plugins/builtin/ai/screener-pane.tsx +231 -276
- package/src/plugins/builtin/ai/settings.ts +0 -1
- package/src/plugins/builtin/ai/utils.ts +1 -71
- package/src/plugins/builtin/alerts/alert-engine.test.ts +1 -38
- package/src/plugins/builtin/alerts/index.test.tsx +61 -82
- package/src/plugins/builtin/alerts/index.tsx +263 -155
- package/src/plugins/builtin/alerts/types.ts +7 -0
- package/src/plugins/builtin/analytics/index.test.tsx +13 -8
- package/src/plugins/builtin/analytics/index.tsx +78 -120
- package/src/plugins/builtin/analytics/metrics.test.ts +0 -4
- package/src/plugins/builtin/ask-ai.test.tsx +55 -54
- package/src/plugins/builtin/broker-manager/index.test.tsx +168 -0
- package/src/plugins/builtin/broker-manager/index.tsx +759 -0
- package/src/plugins/builtin/broker-manager/model.test.ts +107 -0
- package/src/plugins/builtin/broker-manager/model.ts +142 -0
- package/src/plugins/builtin/chat-controller.test.ts +129 -0
- package/src/plugins/builtin/chat-controller.ts +85 -14
- package/src/plugins/builtin/chat.test.tsx +283 -36
- package/src/plugins/builtin/chat.tsx +363 -170
- package/src/plugins/builtin/cloud-auth-actions.tsx +72 -0
- package/src/plugins/builtin/cloud-tweets.tsx +896 -0
- package/src/plugins/builtin/comparison-chart.test.tsx +46 -57
- package/src/plugins/builtin/comparison-chart.tsx +11 -11
- package/src/plugins/builtin/correlation/compute.test.ts +0 -17
- package/src/plugins/builtin/correlation/index.tsx +51 -46
- package/src/plugins/builtin/correlation/settings.ts +1 -2
- package/src/plugins/builtin/debug.tsx +99 -70
- package/src/plugins/builtin/earnings/earnings-cache.test.ts +2 -2
- package/src/plugins/builtin/earnings/earnings-cache.ts +12 -3
- package/src/plugins/builtin/earnings/earnings-data.test.ts +42 -11
- package/src/plugins/builtin/earnings/earnings-data.ts +70 -5
- package/src/plugins/builtin/earnings/index.tsx +187 -94
- package/src/plugins/builtin/econ/calendar-source.ts +9 -116
- package/src/plugins/builtin/econ/fred-client.test.ts +1 -14
- package/src/plugins/builtin/econ/fred-series-map.ts +1 -0
- package/src/plugins/builtin/econ/index.tsx +236 -421
- package/src/plugins/builtin/econ/types.ts +1 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +43 -45
- package/src/plugins/builtin/fx-matrix/pairs.ts +1 -0
- package/src/plugins/builtin/help.test.tsx +120 -60
- package/src/plugins/builtin/help.tsx +266 -82
- package/src/plugins/builtin/holders/index.test.ts +45 -0
- package/src/plugins/builtin/holders/index.tsx +949 -0
- package/src/plugins/builtin/insider/index.tsx +207 -253
- package/src/plugins/builtin/insider/insider-data.test.ts +1 -11
- package/src/plugins/builtin/insider/insider-data.ts +1 -0
- package/src/plugins/builtin/layout-manager.tsx +12 -40
- package/src/plugins/builtin/market-movers/index.tsx +70 -144
- package/src/plugins/builtin/market-movers/screener.test.ts +0 -18
- package/src/plugins/builtin/news-wire/breaking-notifications.test.ts +98 -0
- package/src/plugins/builtin/news-wire/breaking-notifications.ts +110 -0
- package/src/plugins/builtin/news-wire/breaking-pane.tsx +157 -0
- package/src/plugins/builtin/news-wire/categories.ts +114 -0
- package/src/plugins/builtin/news-wire/default-feeds.ts +3 -0
- package/src/plugins/builtin/news-wire/digest-store.ts +44 -0
- package/src/plugins/builtin/news-wire/feed-config.test.ts +90 -0
- package/src/plugins/builtin/news-wire/feed-config.ts +216 -0
- package/src/plugins/builtin/news-wire/feed-pane.tsx +21 -0
- package/src/plugins/builtin/news-wire/hash.ts +9 -0
- package/src/plugins/builtin/news-wire/index.tsx +92 -0
- package/src/plugins/builtin/news-wire/industry-pane.tsx +140 -0
- package/src/plugins/builtin/news-wire/news-detail-view.tsx +256 -0
- package/src/plugins/builtin/news-wire/news-footer.ts +28 -0
- package/src/plugins/builtin/news-wire/news-preset-pane.tsx +94 -0
- package/src/plugins/builtin/news-wire/news-query-presets.ts +40 -0
- package/src/plugins/builtin/news-wire/news-table.test.tsx +206 -0
- package/src/plugins/builtin/news-wire/news-table.tsx +296 -0
- package/src/plugins/builtin/news-wire/persisted-articles.ts +92 -0
- package/src/plugins/builtin/news-wire/read-state.test.ts +30 -0
- package/src/plugins/builtin/news-wire/read-state.ts +76 -0
- package/src/plugins/builtin/news-wire/rss-parser.test.ts +184 -0
- package/src/plugins/builtin/news-wire/rss-parser.ts +213 -0
- package/src/plugins/builtin/news-wire/rss-source.test.ts +159 -0
- package/src/plugins/builtin/news-wire/rss-source.ts +181 -0
- package/src/plugins/builtin/news-wire/top-pane.tsx +21 -0
- package/src/plugins/builtin/news.tsx +98 -25
- package/src/plugins/builtin/notes-files.ts +51 -10
- package/src/plugins/builtin/notes.tsx +332 -131
- package/src/plugins/builtin/options.test.tsx +185 -0
- package/src/plugins/builtin/options.tsx +346 -206
- package/src/plugins/builtin/plugin-groups.ts +92 -0
- package/src/plugins/builtin/portfolio-list/header.tsx +72 -37
- package/src/plugins/builtin/portfolio-list/index.tsx +25 -42
- package/src/plugins/builtin/portfolio-list/metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane.tsx +246 -164
- package/src/plugins/builtin/portfolio-list/settings.ts +19 -51
- package/src/plugins/builtin/portfolio-list/summary.tsx +21 -16
- package/src/plugins/builtin/portfolio-list/table.tsx +66 -25
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.test.tsx +1 -1
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.ts +1 -1
- package/src/plugins/builtin/portfolio-list.test.ts +60 -1
- package/src/plugins/builtin/portfolio-list.test.tsx +122 -55
- package/src/plugins/builtin/quote-monitor.test.tsx +2 -27
- package/src/plugins/builtin/research/index.tsx +724 -0
- package/src/plugins/builtin/sec.tsx +67 -19
- package/src/plugins/builtin/sectors/index.tsx +42 -80
- package/src/plugins/builtin/sectors/sector-data.ts +1 -0
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +25 -39
- package/src/plugins/builtin/ticker-detail/financials-tab.tsx +108 -58
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +383 -271
- package/src/plugins/builtin/ticker-detail/pane.tsx +135 -49
- package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +43 -27
- package/src/plugins/builtin/ticker-detail/settings.ts +3 -6
- package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +16 -10
- package/src/plugins/builtin/ticker-detail.test.tsx +156 -33
- package/src/plugins/builtin/ticker-surface.ts +68 -0
- package/src/plugins/builtin/world-indices/index.tsx +44 -82
- package/src/plugins/builtin/world-indices/indices.ts +1 -0
- package/src/plugins/builtin/yahoo.ts +55 -1
- package/src/plugins/builtin/yield-curve/index.tsx +87 -145
- package/src/plugins/builtin/yield-curve/treasury-data.ts +6 -77
- package/src/plugins/catalog-ui.test.ts +24 -0
- package/src/plugins/catalog-ui.ts +44 -0
- package/src/plugins/catalog.ts +2 -50
- package/src/plugins/ibkr/__snapshots__/trade-tab.test.tsx.snap +5 -5
- package/src/plugins/ibkr/account-cache.test.ts +15 -14
- package/src/plugins/ibkr/account-cache.ts +15 -120
- package/src/plugins/ibkr/broker-adapter.ts +67 -0
- package/src/plugins/ibkr/config.test.ts +0 -18
- package/src/plugins/ibkr/dialogs.tsx +14 -56
- package/src/plugins/ibkr/flex.test.ts +57 -2
- package/src/plugins/ibkr/flex.ts +116 -12
- package/src/plugins/ibkr/gateway-helpers.ts +8 -3
- package/src/plugins/ibkr/gateway-service-native.ts +2381 -0
- package/src/plugins/ibkr/gateway-service.test.ts +47 -9
- package/src/plugins/ibkr/gateway-service.ts +264 -2235
- package/src/plugins/ibkr/gateway-types.ts +27 -0
- package/src/plugins/ibkr/index.test.tsx +3 -11
- package/src/plugins/ibkr/index.tsx +1 -0
- package/src/plugins/ibkr/price-normalization.test.ts +1 -6
- package/src/plugins/ibkr/price-normalization.ts +1 -0
- package/src/plugins/ibkr/trade-badge.tsx +6 -5
- package/src/plugins/ibkr/trade-tab.test.tsx +2 -2
- package/src/plugins/ibkr/trade-tab.tsx +164 -117
- package/src/plugins/ibkr/trading-pane.tsx +117 -83
- package/src/plugins/pane-manager.test.ts +3 -1
- package/src/plugins/pane-manager.ts +93 -19
- package/src/plugins/plugin-runtime.test.tsx +176 -74
- package/src/plugins/plugin-runtime.tsx +228 -10
- package/src/plugins/prediction-markets/cache.ts +49 -0
- package/src/plugins/prediction-markets/categories.ts +0 -10
- package/src/plugins/prediction-markets/chart.test.tsx +1 -1
- package/src/plugins/prediction-markets/chart.tsx +21 -20
- package/src/plugins/prediction-markets/cli.ts +1 -0
- package/src/plugins/prediction-markets/controller-data.ts +135 -89
- package/src/plugins/prediction-markets/controller.ts +37 -108
- package/src/plugins/prediction-markets/detail/book.tsx +23 -22
- package/src/plugins/prediction-markets/detail/outcomes.tsx +33 -32
- package/src/plugins/prediction-markets/detail/overview.tsx +17 -12
- package/src/plugins/prediction-markets/detail/pane.tsx +59 -45
- package/src/plugins/prediction-markets/detail/rules.tsx +22 -7
- package/src/plugins/prediction-markets/detail/shared.tsx +3 -2
- package/src/plugins/prediction-markets/detail/trades.tsx +12 -11
- package/src/plugins/prediction-markets/detail.test.tsx +1 -1
- package/src/plugins/prediction-markets/keyboard.ts +1 -0
- package/src/plugins/prediction-markets/launch.test.ts +1 -1
- package/src/plugins/prediction-markets/launch.ts +1 -1
- package/src/plugins/prediction-markets/metrics.ts +25 -12
- package/src/plugins/prediction-markets/pane.test.tsx +16 -7
- package/src/plugins/prediction-markets/pane.tsx +180 -82
- package/src/plugins/prediction-markets/plugin.test.tsx +37 -45
- package/src/plugins/prediction-markets/services/fetch.ts +27 -10
- package/src/plugins/prediction-markets/services/kalshi-adapter.ts +20 -3
- package/src/plugins/prediction-markets/services/polymarket-adapter.ts +29 -11
- package/src/plugins/prediction-markets/settings.ts +0 -5
- package/src/plugins/prediction-markets/test-helpers.tsx +19 -44
- package/src/plugins/registry.test.ts +307 -0
- package/src/plugins/registry.ts +293 -82
- package/src/react/input.ts +53 -0
- package/src/react/plugins/plugin-slot.tsx +21 -0
- package/src/react/use-raf-callback.ts +44 -0
- package/src/renderers/electrobun/bun/application-menu-click.test.ts +41 -0
- package/src/renderers/electrobun/bun/application-menu-click.ts +55 -0
- package/src/renderers/electrobun/bun/application-menu.ts +118 -0
- package/src/renderers/electrobun/bun/context-menu-click.test.ts +67 -0
- package/src/renderers/electrobun/bun/context-menu-click.ts +61 -0
- package/src/renderers/electrobun/bun/context-menu-normalize.test.ts +52 -0
- package/src/renderers/electrobun/bun/context-menu-normalize.ts +67 -0
- package/src/renderers/electrobun/bun/core-capabilities.ts +328 -0
- package/src/renderers/electrobun/bun/desktop-workspace.test.ts +63 -0
- package/src/renderers/electrobun/bun/desktop-workspace.ts +162 -0
- package/src/renderers/electrobun/bun/index.ts +1149 -0
- package/src/renderers/electrobun/bun/window-frame.test.ts +40 -0
- package/src/renderers/electrobun/bun/window-frame.ts +46 -0
- package/src/renderers/electrobun/shared/protocol.ts +72 -0
- package/src/renderers/electrobun/view/ai-host.ts +93 -0
- package/src/renderers/electrobun/view/app-services.ts +348 -0
- package/src/renderers/electrobun/view/application-menu-bridge.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +173 -0
- package/src/renderers/electrobun/view/broker-remote-client.ts +154 -0
- package/src/renderers/electrobun/view/config-host.ts +35 -0
- package/src/renderers/electrobun/view/context-menu.test.ts +140 -0
- package/src/renderers/electrobun/view/context-menu.ts +164 -0
- package/src/renderers/electrobun/view/data-table.tsx +773 -0
- package/src/renderers/electrobun/view/desktop-controls.tsx +651 -0
- package/src/renderers/electrobun/view/desktop-window-bridge.ts +59 -0
- package/src/renderers/electrobun/view/desktop-window-snapshot.test.ts +57 -0
- package/src/renderers/electrobun/view/desktop-window-snapshot.ts +41 -0
- package/src/renderers/electrobun/view/dialog-host.tsx +62 -0
- package/src/renderers/electrobun/view/http-fetch.ts +113 -0
- package/src/renderers/electrobun/view/input-host.tsx +65 -0
- package/src/renderers/electrobun/view/key-event.test.ts +34 -0
- package/src/renderers/electrobun/view/key-event.ts +134 -0
- package/src/renderers/electrobun/view/main.tsx +104 -0
- package/src/renderers/electrobun/view/native-renderer.ts +110 -0
- package/src/renderers/electrobun/view/native-stubs/chart-kitty-support.ts +9 -0
- package/src/renderers/electrobun/view/native-stubs/chart-surface-manager.ts +25 -0
- package/src/renderers/electrobun/view/native-stubs/chart-surface-sync.ts +1 -0
- package/src/renderers/electrobun/view/notes-files.ts +56 -0
- package/src/renderers/electrobun/view/resource-store.test.ts +44 -0
- package/src/renderers/electrobun/view/resource-store.ts +106 -0
- package/src/renderers/electrobun/view/rpc-codec.ts +42 -0
- package/src/renderers/electrobun/view/scrollbar-activity.ts +23 -0
- package/src/renderers/electrobun/view/toast-host.tsx +65 -0
- package/src/renderers/electrobun/view/ui-host.tsx +1490 -0
- package/src/renderers/electrobun/view/update-host.ts +38 -0
- package/src/renderers/opentui/dialog-host.test.tsx +44 -0
- package/src/renderers/opentui/dialog-host.tsx +56 -0
- package/src/renderers/opentui/host.tsx +134 -0
- package/src/renderers/opentui/input-host.tsx +24 -0
- package/src/renderers/opentui/start.tsx +71 -0
- package/src/renderers/opentui/test-utils.tsx +76 -0
- package/src/renderers/opentui/toast-host.tsx +10 -0
- package/src/renderers/opentui/ui-host.tsx +97 -0
- package/src/sources/gloomberb-cloud.test.ts +288 -17
- package/src/sources/gloomberb-cloud.ts +176 -9
- package/src/sources/provider-errors.ts +1 -0
- package/src/sources/provider-router.test.ts +220 -81
- package/src/sources/provider-router.ts +294 -111
- package/src/sources/sec-edgar.ts +19 -6
- package/src/sources/yahoo-finance.test.ts +0 -5
- package/src/sources/yahoo-finance.ts +420 -11
- package/src/state/app-activity.ts +7 -7
- package/src/state/app-bootstrap.ts +114 -40
- package/src/state/app-context-selector.test.tsx +70 -1
- package/src/state/app-context.test.ts +71 -3
- package/src/state/app-context.test.tsx +14 -7
- package/src/state/app-context.tsx +205 -869
- package/src/state/config-save-scheduler.ts +35 -0
- package/src/state/persist-scheduler.test.ts +67 -0
- package/src/state/persist-scheduler.ts +69 -0
- package/src/state/selectors-ui.ts +55 -0
- package/src/state/selectors.ts +15 -71
- package/src/state/session-persistence.test.ts +1 -1
- package/src/state/ticker-refresh-queue.ts +52 -3
- package/src/state/use-inline-tickers.ts +18 -16
- package/src/state/use-quote-streaming.test.ts +0 -6
- package/src/state/use-quote-streaming.test.tsx +1 -1
- package/src/state/use-quote-streaming.ts +3 -2
- package/src/state/use-session-snapshot.ts +32 -17
- package/src/test-support/plugin-runtime.ts +149 -0
- package/src/theme/color-utils.ts +1 -0
- package/src/theme/colors.ts +16 -0
- package/src/types/broker.ts +16 -1
- package/src/types/capability-route-source.ts +23 -0
- package/src/types/config.test.ts +23 -0
- package/src/types/config.ts +145 -10
- package/src/types/context-menu.ts +95 -0
- package/src/types/data-provider.ts +34 -3
- package/src/types/desktop-menu.ts +13 -0
- package/src/types/desktop-window.ts +29 -0
- package/src/types/financials.ts +123 -2
- package/src/types/instrument.ts +1 -0
- package/src/types/news-source.ts +9 -0
- package/src/types/persistence.ts +1 -0
- package/src/types/plugin.ts +32 -8
- package/src/ui/ascii-font.ts +85 -0
- package/src/ui/context-menu.test.ts +94 -0
- package/src/ui/context-menu.tsx +433 -0
- package/src/ui/dialog.tsx +55 -0
- package/src/ui/host.tsx +361 -0
- package/src/ui/index.tsx +126 -0
- package/src/ui/toast.tsx +40 -0
- package/src/updater.test.ts +69 -0
- package/src/updater.ts +61 -18
- package/src/utils/api-client.test.ts +99 -9
- package/src/utils/api-client.ts +378 -18
- package/src/utils/back-navigation.ts +6 -0
- package/src/utils/cli-output.ts +1 -0
- package/src/utils/currency-units.test.ts +1 -6
- package/src/utils/currency-units.ts +1 -0
- package/src/utils/debug-log.ts +68 -0
- package/src/utils/dom-screenshot.ts +326 -0
- package/src/utils/exchange-rate-map.ts +1 -0
- package/src/utils/exchanges.ts +214 -0
- package/src/utils/format.test.ts +7 -0
- package/src/utils/format.ts +1 -0
- package/src/utils/fuzzy-search.ts +1 -0
- package/src/utils/html-entities.test.ts +8 -0
- package/src/utils/html-entities.ts +32 -0
- package/src/utils/http-transport.ts +11 -0
- package/src/utils/main-thread-monitor.ts +127 -0
- package/src/utils/perf-marks.ts +54 -0
- package/src/utils/quote-freshness.ts +27 -50
- package/src/utils/quote-resolution.test.ts +5 -5
- package/src/utils/quote-resolution.ts +0 -1
- package/src/utils/quote-time.ts +1 -5
- package/src/utils/sec.test.ts +0 -12
- package/src/utils/selection-clipboard.ts +14 -6
- package/src/utils/shortcut-labels.test.ts +20 -0
- package/src/utils/shortcut-labels.ts +67 -0
- package/src/utils/slugify.ts +1 -0
- package/src/utils/terminal-input-reset.ts +1 -0
- package/src/utils/text-wrap.ts +82 -0
- package/src/utils/throttled-fetch.test.ts +19 -0
- package/src/utils/throttled-fetch.ts +11 -9
- package/src/utils/ticker-list.test.ts +1 -7
- package/src/utils/ticker-list.ts +1 -0
- package/src/utils/timestamp.ts +29 -0
- package/src/utils/tweet-text.test.ts +13 -0
- package/src/utils/tweet-text.ts +18 -0
- package/src/utils/url.ts +11 -0
- package/src/version.ts +1 -1
- package/src/components/chart/chart-control-hint.tsx +0 -39
- package/src/components/detail-feed-view.test.tsx +0 -180
- package/src/components/detail-feed-view.tsx +0 -531
- package/src/components/layout/pixel-mouse-mode.test.ts +0 -122
- package/src/components/layout/pixel-mouse.test.ts +0 -40
- package/src/components/spinner.tsx +0 -2
- package/src/components/tab-bar.tsx +0 -17
- package/src/components/ui/multi-select-chips.tsx +0 -69
- package/src/plugins/builtin/ai/index.test.tsx +0 -139
- package/src/plugins/builtin/ask-ai.tsx +0 -9
- package/src/plugins/builtin/correlation/plugin.test.ts +0 -44
- package/src/plugins/builtin/correlation/settings.test.ts +0 -34
- package/src/plugins/builtin/econ/calendar-source.test.ts +0 -219
- package/src/plugins/builtin/econ/fred-client.ts +0 -79
- package/src/plugins/builtin/fred-settings.ts +0 -10
- package/src/plugins/builtin/options-availability.test.tsx +0 -179
- package/src/plugins/builtin/options-availability.ts +0 -158
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +0 -61
- package/src/plugins/builtin/yield-curve/treasury-data.test.ts +0 -212
- package/src/plugins/prediction-markets/status.ts +0 -24
- package/src/plugins/prediction-markets/table.tsx +0 -87
- package/src/utils/quote-time.test.ts +0 -22
- package/src/utils/selection-clipboard.test.ts +0 -43
- package/src/utils/terminal-input-reset.test.ts +0 -50
|
@@ -10,10 +10,10 @@ export interface CommandContext {
|
|
|
10
10
|
|
|
11
11
|
export interface Command {
|
|
12
12
|
id: string;
|
|
13
|
-
prefix: string; // e.g., "
|
|
13
|
+
prefix: string; // e.g., "DES", "AW", "RP"
|
|
14
14
|
label: string;
|
|
15
15
|
description: string;
|
|
16
|
-
hasArg?: boolean; // true if prefix takes an argument (e.g., "
|
|
16
|
+
hasArg?: boolean; // true if prefix takes an argument (e.g., "DES AMD")
|
|
17
17
|
argPlaceholder?: string;
|
|
18
18
|
shortcut?: string;
|
|
19
19
|
category: string;
|
|
@@ -21,14 +21,14 @@ export interface Command {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export const commands: Command[] = [
|
|
24
|
-
//
|
|
24
|
+
// Security description
|
|
25
25
|
{
|
|
26
|
-
id: "
|
|
27
|
-
prefix: "
|
|
28
|
-
label: "
|
|
29
|
-
description: "
|
|
26
|
+
id: "security-description",
|
|
27
|
+
prefix: "DES",
|
|
28
|
+
label: "Description",
|
|
29
|
+
description: "Open security details for a ticker",
|
|
30
30
|
hasArg: true,
|
|
31
|
-
argPlaceholder: "
|
|
31
|
+
argPlaceholder: "ticker",
|
|
32
32
|
category: "Search",
|
|
33
33
|
execute: () => {}, // handled specially by command bar
|
|
34
34
|
},
|
|
@@ -159,16 +159,6 @@ export const commands: Command[] = [
|
|
|
159
159
|
category: "Config",
|
|
160
160
|
execute: () => {}, // handled by command bar
|
|
161
161
|
},
|
|
162
|
-
{
|
|
163
|
-
id: "new-pane",
|
|
164
|
-
prefix: "NP",
|
|
165
|
-
label: "New Pane",
|
|
166
|
-
description: "Add or create a new pane from plugin templates",
|
|
167
|
-
hasArg: true,
|
|
168
|
-
argPlaceholder: "pane",
|
|
169
|
-
category: "Config",
|
|
170
|
-
execute: () => {}, // handled by command bar
|
|
171
|
-
},
|
|
172
162
|
{
|
|
173
163
|
id: "toggle-status-bar",
|
|
174
164
|
prefix: "SB",
|
|
@@ -259,12 +249,12 @@ export function getThemeOptions(): Array<{ id: string; name: string; description
|
|
|
259
249
|
}
|
|
260
250
|
|
|
261
251
|
/** Find a command whose prefix matches the start of the input */
|
|
262
|
-
export function matchPrefix(input: string): { command: Command; arg: string } | null {
|
|
252
|
+
export function matchPrefix(input: string, commandList: Command[] = commands): { command: Command; arg: string } | null {
|
|
263
253
|
const upper = input.toUpperCase().trim();
|
|
264
254
|
if (!upper) return null;
|
|
265
255
|
|
|
266
256
|
// Sort by prefix length descending so longer prefixes match first (e.g., "AW" before "A")
|
|
267
|
-
const sorted = [...
|
|
257
|
+
const sorted = [...commandList].sort((a, b) => b.prefix.length - a.prefix.length);
|
|
268
258
|
|
|
269
259
|
for (const cmd of sorted) {
|
|
270
260
|
if (upper.startsWith(cmd.prefix + " ") || upper === cmd.prefix) {
|
|
@@ -212,7 +212,7 @@ describe("command-bar helpers", () => {
|
|
|
212
212
|
expect(getScreenFooterLeft(orderedPickerRoute)).toBe("up/down move space toggle [ ] reorder enter done");
|
|
213
213
|
expect(getScreenFooterRight(orderedPickerRoute)).toBe("backspace/esc back");
|
|
214
214
|
expect(getScreenFooterRight(filteredPickerRoute)).toBe("backspace delete esc back");
|
|
215
|
-
expect(getScreenFooterRight(workflowRoute)).toBe("esc back");
|
|
215
|
+
expect(getScreenFooterRight(workflowRoute)).toBe("backspace/esc back");
|
|
216
216
|
expect(getScreenFooterRight(confirmRoute)).toBe("backspace/esc back");
|
|
217
217
|
});
|
|
218
218
|
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
PaneTemplateDef,
|
|
5
5
|
WizardStep,
|
|
6
6
|
} from "../../types/plugin";
|
|
7
|
+
|
|
7
8
|
export { slugifyName } from "../../utils/slugify";
|
|
8
9
|
import type {
|
|
9
10
|
CommandBarFieldOption,
|
|
@@ -13,7 +14,7 @@ import type {
|
|
|
13
14
|
} from "./workflow-types";
|
|
14
15
|
import type { CollectionKind, CollectionMembershipAction } from "./workflow-ops";
|
|
15
16
|
|
|
16
|
-
export type RouteCommandId = "
|
|
17
|
+
export type RouteCommandId = "security-description" | "theme" | "plugins" | "layout";
|
|
17
18
|
export type CollectionCommandId = "add-watchlist" | "add-portfolio" | "remove-watchlist" | "remove-portfolio";
|
|
18
19
|
|
|
19
20
|
export function summarizeError(error: unknown): Record<string, string> {
|
|
@@ -28,11 +29,10 @@ export function summarizeError(error: unknown): Record<string, string> {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export function isRouteCommandId(commandId: string): commandId is RouteCommandId {
|
|
31
|
-
return commandId === "
|
|
32
|
+
return commandId === "security-description"
|
|
32
33
|
|| commandId === "theme"
|
|
33
34
|
|| commandId === "plugins"
|
|
34
|
-
|| commandId === "layout"
|
|
35
|
-
|| commandId === "new-pane";
|
|
35
|
+
|| commandId === "layout";
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export function normalizeFieldOptions(
|
|
@@ -69,7 +69,7 @@ export function coerceFieldValues(value: CommandBarFieldValue | undefined): stri
|
|
|
69
69
|
: [];
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
function fieldOptionLabel(field: CommandBarWorkflowField, value: string): string {
|
|
73
73
|
if (field.type !== "select" && field.type !== "multi-select" && field.type !== "ordered-multi-select") {
|
|
74
74
|
return value;
|
|
75
75
|
}
|
|
@@ -237,9 +237,9 @@ export function normalizeWizardFields(steps: WizardStep[]): {
|
|
|
237
237
|
return { fields, description, initialValues, pendingLabel, successLabel };
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
export function routeCommandIdToScreen(commandId: RouteCommandId): "ticker-search" | "themes" | "plugins" | "layout"
|
|
240
|
+
export function routeCommandIdToScreen(commandId: RouteCommandId): "ticker-search" | "themes" | "plugins" | "layout" {
|
|
241
241
|
switch (commandId) {
|
|
242
|
-
case "
|
|
242
|
+
case "security-description":
|
|
243
243
|
return "ticker-search";
|
|
244
244
|
case "theme":
|
|
245
245
|
return "themes";
|
|
@@ -247,8 +247,6 @@ export function routeCommandIdToScreen(commandId: RouteCommandId): "ticker-searc
|
|
|
247
247
|
return "plugins";
|
|
248
248
|
case "layout":
|
|
249
249
|
return "layout";
|
|
250
|
-
case "new-pane":
|
|
251
|
-
return "new-pane";
|
|
252
250
|
}
|
|
253
251
|
}
|
|
254
252
|
|
|
@@ -327,7 +325,7 @@ export function getScreenFooterLeft(route: CommandBarRoute | null): string {
|
|
|
327
325
|
export function getScreenFooterRight(route: CommandBarRoute | null): string {
|
|
328
326
|
if (!route) return "esc cancel";
|
|
329
327
|
|
|
330
|
-
if (route.kind === "workflow") return "esc back";
|
|
328
|
+
if (route.kind === "workflow") return "backspace/esc back";
|
|
331
329
|
if (route.kind === "confirm") return "backspace/esc back";
|
|
332
330
|
|
|
333
331
|
if (
|
|
@@ -341,7 +339,7 @@ export function getScreenFooterRight(route: CommandBarRoute | null): string {
|
|
|
341
339
|
}
|
|
342
340
|
|
|
343
341
|
export function isRootParsedCommand(commandId: string): boolean {
|
|
344
|
-
return commandId === "
|
|
342
|
+
return commandId === "security-description"
|
|
345
343
|
|| commandId === "add-watchlist"
|
|
346
344
|
|| commandId === "add-portfolio"
|
|
347
345
|
|| commandId === "remove-watchlist"
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import type { PaneTemplateDef } from "../../types/plugin";
|
|
2
2
|
|
|
3
|
-
export function formatPaneTemplateLabel(label: string): string {
|
|
4
|
-
let normalized = label.trim();
|
|
5
|
-
if (normalized.startsWith("New ")) normalized = normalized.slice(4);
|
|
6
|
-
if (normalized.endsWith(" Pane")) normalized = normalized.slice(0, -5);
|
|
7
|
-
return normalized;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
3
|
export function getPaneTemplateDisplayLabel(template: Pick<PaneTemplateDef, "label">): string {
|
|
11
|
-
|
|
4
|
+
let label = template.label.trim();
|
|
5
|
+
if (label.startsWith("New ")) label = label.slice(4);
|
|
6
|
+
if (label.endsWith(" Pane")) label = label.slice(0, -5);
|
|
7
|
+
return label;
|
|
12
8
|
}
|
|
@@ -2,8 +2,8 @@ import type { CommandDef, PaneTemplateDef } from "../../types/plugin";
|
|
|
2
2
|
import type { Command } from "./command-registry";
|
|
3
3
|
import { getPaneTemplateDisplayLabel } from "./pane-template-display";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
type RootShortcutArgKind = "text" | "ticker" | "ticker-list";
|
|
6
|
+
type ShortcutIntentKind = "none" | "complete" | "inferred-complete" | "partial" | "ambiguous";
|
|
7
7
|
|
|
8
8
|
interface ShortcutIntentBase {
|
|
9
9
|
kind: Exclude<ShortcutIntentKind, "none">;
|
|
@@ -16,22 +16,22 @@ interface ShortcutIntentBase {
|
|
|
16
16
|
completionQuery: string | null;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
interface CommandShortcutIntent extends ShortcutIntentBase {
|
|
20
20
|
source: "command";
|
|
21
21
|
command: Command;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
interface PaneTemplateShortcutIntent extends ShortcutIntentBase {
|
|
25
25
|
source: "pane-template";
|
|
26
26
|
template: PaneTemplateDef;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
interface PluginCommandShortcutIntent extends ShortcutIntentBase {
|
|
30
30
|
source: "plugin-command";
|
|
31
31
|
command: CommandDef;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
type ShortcutIntent =
|
|
35
35
|
| { kind: "none" }
|
|
36
36
|
| CommandShortcutIntent
|
|
37
37
|
| PaneTemplateShortcutIntent
|
|
@@ -60,11 +60,11 @@ function mapPlaceholderToArgKind(value: string | undefined): RootShortcutArgKind
|
|
|
60
60
|
return null;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
function getPaneShortcutArgKind(template: PaneTemplateDef): RootShortcutArgKind | null {
|
|
64
64
|
return template.shortcut?.argKind ?? mapPlaceholderToArgKind(template.shortcut?.argPlaceholder);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
function getCommandShortcutArgKind(command: Command): RootShortcutArgKind | null {
|
|
68
68
|
return mapPlaceholderToArgKind(command.argPlaceholder) ?? (command.hasArg ? "text" : null);
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -135,9 +135,10 @@ export function parseRootShortcutIntent({
|
|
|
135
135
|
if (!trimmed) return { kind: "none" };
|
|
136
136
|
|
|
137
137
|
const upper = trimmed.toUpperCase();
|
|
138
|
-
const match = buildShortcutCandidates(commands, pluginCommands, paneTemplates).find((candidate) =>
|
|
139
|
-
upper === candidate.prefix
|
|
140
|
-
|
|
138
|
+
const match = buildShortcutCandidates(commands, pluginCommands, paneTemplates).find((candidate) => {
|
|
139
|
+
if (upper === candidate.prefix) return true;
|
|
140
|
+
return !!candidate.argKind && upper.startsWith(`${candidate.prefix} `);
|
|
141
|
+
});
|
|
141
142
|
if (!match) return { kind: "none" };
|
|
142
143
|
|
|
143
144
|
const argText = trimmed.slice(match.prefix.length).trim();
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import {
|
|
3
3
|
buildSections,
|
|
4
|
-
getEmptyState,
|
|
5
|
-
getFooterHints,
|
|
6
4
|
getRowPresentation,
|
|
7
5
|
rankTickerSearchItems,
|
|
8
6
|
resolveCommandBarMode,
|
|
9
7
|
} from "./view-model";
|
|
8
|
+
import { commands } from "./command-registry";
|
|
10
9
|
|
|
11
10
|
describe("command bar view model helpers", () => {
|
|
12
11
|
test("resolves prefix-driven modes", () => {
|
|
13
12
|
expect(resolveCommandBarMode("")).toMatchObject({ kind: "default", badge: "BROWSE" });
|
|
14
|
-
expect(resolveCommandBarMode("
|
|
13
|
+
expect(resolveCommandBarMode("DES NVDA")).toMatchObject({ kind: "search", badge: "DES" });
|
|
15
14
|
expect(resolveCommandBarMode("TH ")).toMatchObject({ kind: "themes", badge: "THEMES" });
|
|
16
15
|
expect(resolveCommandBarMode("PL notes")).toMatchObject({ kind: "plugins", badge: "PLUGINS" });
|
|
17
16
|
expect(resolveCommandBarMode("LAY ")).toMatchObject({ kind: "layout", badge: "LAYOUT" });
|
|
18
|
-
expect(resolveCommandBarMode("NP ")).toMatchObject({ kind: "
|
|
17
|
+
expect(resolveCommandBarMode("NP ")).toMatchObject({ kind: "default", badge: "FILTER" });
|
|
19
18
|
expect(resolveCommandBarMode("PS")).toMatchObject({ kind: "direct-command", badge: "COMMAND" });
|
|
20
19
|
expect(resolveCommandBarMode("AW")).toMatchObject({ kind: "direct-command", badge: "COMMAND" });
|
|
21
20
|
});
|
|
22
21
|
|
|
22
|
+
test("can resolve modes against a renderer-specific command list", () => {
|
|
23
|
+
const desktopCommands = commands.filter((command) => command.id !== "cycle-chart-renderer");
|
|
24
|
+
expect(resolveCommandBarMode("CR", desktopCommands)).toMatchObject({ kind: "default", badge: "FILTER" });
|
|
25
|
+
});
|
|
26
|
+
|
|
23
27
|
test("builds sections while preserving order", () => {
|
|
24
28
|
const sections = buildSections([
|
|
25
29
|
{ id: "a", category: "Tickers" },
|
|
@@ -42,44 +46,6 @@ describe("command bar view model helpers", () => {
|
|
|
42
46
|
expect(sections.map((section) => section.category)).toEqual(["Tickers", "Config", "Danger", "Debug"]);
|
|
43
47
|
});
|
|
44
48
|
|
|
45
|
-
test("returns footer hints for plugin toggles", () => {
|
|
46
|
-
expect(getFooterHints("plugins", false)).toEqual({
|
|
47
|
-
left: "up/down move enter select space toggle",
|
|
48
|
-
right: "esc cancel",
|
|
49
|
-
});
|
|
50
|
-
expect(getFooterHints("layout", false)).toEqual({
|
|
51
|
-
left: "up/down move enter select",
|
|
52
|
-
right: "esc cancel",
|
|
53
|
-
});
|
|
54
|
-
expect(getFooterHints("new-pane", false)).toEqual({
|
|
55
|
-
left: "up/down move enter select",
|
|
56
|
-
right: "esc cancel",
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test("returns specific empty states", () => {
|
|
61
|
-
expect(getEmptyState("search", "T ", "")).toEqual({
|
|
62
|
-
label: "Type a ticker symbol",
|
|
63
|
-
detail: "Search Yahoo Finance and connected brokers",
|
|
64
|
-
});
|
|
65
|
-
expect(getEmptyState("search", "T zom", "zom")).toEqual({
|
|
66
|
-
label: 'No matches for "zom"',
|
|
67
|
-
detail: "Try a symbol, company name, or exchange variant",
|
|
68
|
-
});
|
|
69
|
-
expect(getEmptyState("default", "abc")).toEqual({
|
|
70
|
-
label: 'No matches for "abc"',
|
|
71
|
-
detail: "Try a ticker, command name, or prefix",
|
|
72
|
-
});
|
|
73
|
-
expect(getEmptyState("layout", "LAY ")).toEqual({
|
|
74
|
-
label: "No layout actions match",
|
|
75
|
-
detail: "LAY",
|
|
76
|
-
});
|
|
77
|
-
expect(getEmptyState("new-pane", "NP ")).toEqual({
|
|
78
|
-
label: "No pane templates match",
|
|
79
|
-
detail: "NP",
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
49
|
test("derives row presentation for toggles and current theme rows", () => {
|
|
84
50
|
expect(getRowPresentation({
|
|
85
51
|
id: "plugin:news",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { matchPrefix } from "./command-registry";
|
|
1
|
+
import { matchPrefix, type Command } from "./command-registry";
|
|
2
|
+
|
|
2
3
|
export { rankTickerSearchItems } from "../../utils/ticker-search";
|
|
3
4
|
|
|
4
|
-
export type CommandBarMode = "default" | "search" | "themes" | "plugins" | "layout" | "
|
|
5
|
+
export type CommandBarMode = "default" | "search" | "themes" | "plugins" | "layout" | "direct-command";
|
|
5
6
|
|
|
6
7
|
export interface CommandBarModeInfo {
|
|
7
8
|
kind: CommandBarMode;
|
|
@@ -9,12 +10,6 @@ export interface CommandBarModeInfo {
|
|
|
9
10
|
hint: string;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
export interface CommandBarModeStripEntry {
|
|
13
|
-
prefix: string;
|
|
14
|
-
label: string;
|
|
15
|
-
active: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
13
|
export interface CommandBarFooterHints {
|
|
19
14
|
left: string;
|
|
20
15
|
right: string;
|
|
@@ -44,16 +39,8 @@ export interface CommandBarRowPresentation {
|
|
|
44
39
|
primaryMuted: boolean;
|
|
45
40
|
}
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
{ prefix: "TH", label: "Change theme", kind: "themes" },
|
|
50
|
-
{ prefix: "PL", label: "Toggle plugins", kind: "plugins" },
|
|
51
|
-
{ prefix: "LAY", label: "Layout actions", kind: "layout" },
|
|
52
|
-
{ prefix: "NP", label: "New pane", kind: "new-pane" },
|
|
53
|
-
] as const;
|
|
54
|
-
|
|
55
|
-
export function resolveCommandBarMode(query: string): CommandBarModeInfo {
|
|
56
|
-
const match = matchPrefix(query);
|
|
42
|
+
export function resolveCommandBarMode(query: string, commandList?: Command[]): CommandBarModeInfo {
|
|
43
|
+
const match = matchPrefix(query, commandList);
|
|
57
44
|
|
|
58
45
|
if (!query.trim()) {
|
|
59
46
|
return { kind: "default", badge: "BROWSE", hint: "Type a command, ticker, or prefix" };
|
|
@@ -64,29 +51,19 @@ export function resolveCommandBarMode(query: string): CommandBarModeInfo {
|
|
|
64
51
|
}
|
|
65
52
|
|
|
66
53
|
switch (match.command.id) {
|
|
67
|
-
case "
|
|
68
|
-
return { kind: "search", badge: "
|
|
54
|
+
case "security-description":
|
|
55
|
+
return { kind: "search", badge: "DES", hint: "Open security details for a ticker" };
|
|
69
56
|
case "theme":
|
|
70
57
|
return { kind: "themes", badge: "THEMES", hint: "Preview with arrows, Enter to save, Esc to revert" };
|
|
71
58
|
case "plugins":
|
|
72
59
|
return { kind: "plugins", badge: "PLUGINS", hint: "Toggle plugins without leaving the list" };
|
|
73
60
|
case "layout":
|
|
74
61
|
return { kind: "layout", badge: "LAYOUT", hint: "Organize panes, history, and saved layouts" };
|
|
75
|
-
case "new-pane":
|
|
76
|
-
return { kind: "new-pane", badge: "NEW PANE", hint: "Create plugin-defined panes for the current workspace" };
|
|
77
62
|
default:
|
|
78
63
|
return { kind: "direct-command", badge: "COMMAND", hint: `Run ${match.command.label}` };
|
|
79
64
|
}
|
|
80
65
|
}
|
|
81
66
|
|
|
82
|
-
export function getModeStrip(mode: CommandBarMode): CommandBarModeStripEntry[] {
|
|
83
|
-
return MODE_STRIP_ENTRIES.map((entry) => ({
|
|
84
|
-
prefix: entry.prefix,
|
|
85
|
-
label: entry.label,
|
|
86
|
-
active: entry.kind === mode,
|
|
87
|
-
}));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
67
|
export function buildSections<T extends { category: string }>(items: T[]): Array<CommandBarSection<T>> {
|
|
91
68
|
const sections: Array<CommandBarSection<T>> = [];
|
|
92
69
|
for (const item of items) {
|
|
@@ -117,9 +94,6 @@ export function getFooterHints(mode: CommandBarMode, isNarrow: boolean): Command
|
|
|
117
94
|
if (mode === "layout") {
|
|
118
95
|
return { left: moveAndSelect, right: "esc cancel" };
|
|
119
96
|
}
|
|
120
|
-
if (mode === "new-pane") {
|
|
121
|
-
return { left: moveAndSelect, right: "esc cancel" };
|
|
122
|
-
}
|
|
123
97
|
return { left: moveAndSelect, right: "esc cancel" };
|
|
124
98
|
}
|
|
125
99
|
|
|
@@ -127,7 +101,7 @@ export function getEmptyState(mode: CommandBarMode, query: string, searchQuery?:
|
|
|
127
101
|
switch (mode) {
|
|
128
102
|
case "search":
|
|
129
103
|
if (!searchQuery) {
|
|
130
|
-
return { label: "Type a ticker symbol", detail: "
|
|
104
|
+
return { label: "Type a ticker symbol", detail: "Open security details after resolving a ticker" };
|
|
131
105
|
}
|
|
132
106
|
return { label: `No matches for "${searchQuery}"`, detail: "Try a symbol, company name, or exchange variant" };
|
|
133
107
|
case "plugins":
|
|
@@ -136,8 +110,6 @@ export function getEmptyState(mode: CommandBarMode, query: string, searchQuery?:
|
|
|
136
110
|
return { label: "No themes match", detail: query.trim() || "Installed themes will appear here" };
|
|
137
111
|
case "layout":
|
|
138
112
|
return { label: "No layout actions match", detail: query.trim() || "Focused-pane and layout actions will appear here" };
|
|
139
|
-
case "new-pane":
|
|
140
|
-
return { label: "No pane templates match", detail: query.trim() || "Plugin-defined pane templates will appear here" };
|
|
141
113
|
default:
|
|
142
114
|
if (query.trim()) {
|
|
143
115
|
return { label: `No matches for "${query.trim()}"`, detail: "Try a ticker, command name, or prefix" };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { createDefaultConfig } from "../../types/config";
|
|
2
|
+
import { cloneLayout, createDefaultConfig, findPaneInstance, type LayoutConfig } from "../../types/config";
|
|
3
3
|
import { createInitialState } from "../../state/app-context";
|
|
4
4
|
import { createTestDataProvider } from "../../test-support/data-provider";
|
|
5
|
-
import { createPaneTemplateOrThrow } from "./workflow-ops";
|
|
5
|
+
import { applyPaneSettingFieldValue, createPaneTemplateOrThrow } from "./workflow-ops";
|
|
6
6
|
|
|
7
7
|
function makeDataProvider() {
|
|
8
8
|
return createTestDataProvider({ id: "test" });
|
|
@@ -68,3 +68,77 @@ describe("createPaneTemplateOrThrow", () => {
|
|
|
68
68
|
expect(placeCalls).toHaveLength(0);
|
|
69
69
|
});
|
|
70
70
|
});
|
|
71
|
+
|
|
72
|
+
describe("applyPaneSettingFieldValue", () => {
|
|
73
|
+
test("keeps portfolio panes on their displayed collection when switching back to all collections", async () => {
|
|
74
|
+
const config = createDefaultConfig("/tmp/gloomberb-workflow-ops-test");
|
|
75
|
+
const layout = cloneLayout(config.layout);
|
|
76
|
+
const portfolioPane = findPaneInstance(layout, "portfolio-list:main");
|
|
77
|
+
if (!portfolioPane) throw new Error("missing portfolio pane");
|
|
78
|
+
portfolioPane.settings = {
|
|
79
|
+
...(portfolioPane.settings ?? {}),
|
|
80
|
+
collectionScope: "watchlists",
|
|
81
|
+
visibleCollectionIds: ["watchlist"],
|
|
82
|
+
hideTabs: true,
|
|
83
|
+
lockedCollectionId: "watchlist",
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const state = createInitialState({ ...config, layout });
|
|
87
|
+
state.paneState["portfolio-list:main"] = {
|
|
88
|
+
collectionId: "main",
|
|
89
|
+
cursorSymbol: null,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const persisted: LayoutConfig[] = [];
|
|
93
|
+
const actions: unknown[] = [];
|
|
94
|
+
|
|
95
|
+
await applyPaneSettingFieldValue("portfolio-list:main", {
|
|
96
|
+
key: "collectionScope",
|
|
97
|
+
label: "Collections",
|
|
98
|
+
type: "select",
|
|
99
|
+
options: [],
|
|
100
|
+
}, "all", {
|
|
101
|
+
dataProvider: makeDataProvider() as any,
|
|
102
|
+
tickerRepository: makeTickerRepository() as any,
|
|
103
|
+
dispatch: (action) => { actions.push(action); },
|
|
104
|
+
getState: () => state,
|
|
105
|
+
persistLayout: (nextLayout) => { persisted.push(nextLayout); },
|
|
106
|
+
pluginRegistry: {
|
|
107
|
+
resolvePaneSettings: () => ({
|
|
108
|
+
paneId: "portfolio-list:main",
|
|
109
|
+
pane: portfolioPane,
|
|
110
|
+
paneDef: {
|
|
111
|
+
id: "portfolio-list",
|
|
112
|
+
name: "Portfolio",
|
|
113
|
+
component: () => null,
|
|
114
|
+
defaultPosition: "left",
|
|
115
|
+
},
|
|
116
|
+
settingsDef: { title: "Portfolio Pane Settings", fields: [] },
|
|
117
|
+
context: {
|
|
118
|
+
config: state.config,
|
|
119
|
+
layout: state.config.layout,
|
|
120
|
+
paneId: "portfolio-list:main",
|
|
121
|
+
paneType: "portfolio-list",
|
|
122
|
+
pane: portfolioPane,
|
|
123
|
+
settings: portfolioPane.settings ?? {},
|
|
124
|
+
paneState: state.paneState["portfolio-list:main"] ?? {},
|
|
125
|
+
activeTicker: null,
|
|
126
|
+
activeCollectionId: "main",
|
|
127
|
+
},
|
|
128
|
+
}),
|
|
129
|
+
} as any,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const nextPane = findPaneInstance(persisted[0]!, "portfolio-list:main");
|
|
133
|
+
expect(nextPane?.settings).toMatchObject({ collectionScope: "all" });
|
|
134
|
+
expect("visibleCollectionIds" in (nextPane?.settings ?? {})).toBe(false);
|
|
135
|
+
expect("hideTabs" in (nextPane?.settings ?? {})).toBe(false);
|
|
136
|
+
expect("lockedCollectionId" in (nextPane?.settings ?? {})).toBe(false);
|
|
137
|
+
expect(nextPane?.params?.collectionId).toBe("watchlist");
|
|
138
|
+
expect(actions).toContainEqual({
|
|
139
|
+
type: "UPDATE_PANE_STATE",
|
|
140
|
+
paneId: "portfolio-list:main",
|
|
141
|
+
patch: { collectionId: "watchlist" },
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -10,6 +10,7 @@ import { resolveTickerSearch, upsertTickerFromSearchResult } from "../../utils/t
|
|
|
10
10
|
import { formatTickerListInput, parseTickerListInput } from "../../utils/ticker-list";
|
|
11
11
|
import { updatePaneInstance, setPaneSettings } from "../../pane-settings";
|
|
12
12
|
import { isManualPortfolio } from "../../plugins/builtin/portfolio-list/mutations";
|
|
13
|
+
import { cleanPortfolioPaneSettings, resolvePortfolioPaneCollectionId } from "../../plugins/builtin/portfolio-list/settings";
|
|
13
14
|
import {
|
|
14
15
|
buildComparisonChartPaneTitle,
|
|
15
16
|
COMPARISON_CHART_PANE_ID,
|
|
@@ -356,6 +357,14 @@ export async function applyPaneSettingFieldValue(
|
|
|
356
357
|
const state = deps.getState();
|
|
357
358
|
const shouldPushHistory = options?.pushHistory !== false;
|
|
358
359
|
|
|
360
|
+
if (field.storage === "plugin") {
|
|
361
|
+
if (!descriptor.pluginId) {
|
|
362
|
+
throw new Error("This pane setting is not owned by a plugin.");
|
|
363
|
+
}
|
|
364
|
+
await deps.pluginRegistry.setConfigState(descriptor.pluginId, field.key, value);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
359
368
|
if (descriptor.pane.paneId === "quote-monitor" && field.key === "symbol") {
|
|
360
369
|
const rawQuery = typeof value === "string" ? value.trim() : "";
|
|
361
370
|
const resolvedTicker = await resolveTickerSearch({
|
|
@@ -419,18 +428,13 @@ export async function applyPaneSettingFieldValue(
|
|
|
419
428
|
return;
|
|
420
429
|
}
|
|
421
430
|
|
|
422
|
-
|
|
431
|
+
let nextSettings: Record<string, unknown> = {
|
|
423
432
|
...descriptor.context.settings,
|
|
424
433
|
[field.key]: value,
|
|
425
434
|
};
|
|
426
435
|
|
|
427
|
-
if (descriptor.pane.paneId === "portfolio-list"
|
|
428
|
-
|
|
429
|
-
? descriptor.context.paneState.collectionId
|
|
430
|
-
: descriptor.context.activeCollectionId;
|
|
431
|
-
if (lockedCollectionId) {
|
|
432
|
-
nextSettings.lockedCollectionId = lockedCollectionId;
|
|
433
|
-
}
|
|
436
|
+
if (descriptor.pane.paneId === "portfolio-list") {
|
|
437
|
+
nextSettings = cleanPortfolioPaneSettings(nextSettings);
|
|
434
438
|
}
|
|
435
439
|
|
|
436
440
|
if (descriptor.pane.paneId === "ticker-detail" && field.key === "hideTabs" && value === true) {
|
|
@@ -440,6 +444,33 @@ export async function applyPaneSettingFieldValue(
|
|
|
440
444
|
nextSettings.lockedTabId = lockedTabId;
|
|
441
445
|
}
|
|
442
446
|
|
|
443
|
-
|
|
447
|
+
let nextLayout = setPaneSettings(state.config.layout, targetId, nextSettings);
|
|
448
|
+
|
|
449
|
+
if (descriptor.pane.paneId === "portfolio-list") {
|
|
450
|
+
const currentCollectionId = typeof descriptor.context.paneState.collectionId === "string"
|
|
451
|
+
? descriptor.context.paneState.collectionId
|
|
452
|
+
: (descriptor.context.activeCollectionId ?? "");
|
|
453
|
+
const displayedCollectionId = resolvePortfolioPaneCollectionId(
|
|
454
|
+
state.config,
|
|
455
|
+
descriptor.context.settings,
|
|
456
|
+
currentCollectionId,
|
|
457
|
+
);
|
|
458
|
+
const nextCollectionId = resolvePortfolioPaneCollectionId(
|
|
459
|
+
state.config,
|
|
460
|
+
nextSettings,
|
|
461
|
+
displayedCollectionId || currentCollectionId,
|
|
462
|
+
);
|
|
463
|
+
if (nextCollectionId) {
|
|
464
|
+
nextLayout = updatePaneInstance(nextLayout, targetId, (instance) => ({
|
|
465
|
+
...instance,
|
|
466
|
+
params: {
|
|
467
|
+
...(instance.params ?? {}),
|
|
468
|
+
collectionId: nextCollectionId,
|
|
469
|
+
},
|
|
470
|
+
}));
|
|
471
|
+
deps.dispatch({ type: "UPDATE_PANE_STATE", paneId: targetId, patch: { collectionId: nextCollectionId } });
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
444
475
|
deps.persistLayout(nextLayout, { pushHistory: shouldPushHistory });
|
|
445
476
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
export interface CommandBarMainSnapshot {
|
|
2
3
|
query: string;
|
|
3
4
|
selectedIdx: number;
|
|
@@ -37,7 +38,7 @@ export interface CommandBarRouteBase {
|
|
|
37
38
|
|
|
38
39
|
export interface CommandBarModeRoute extends CommandBarRouteBase {
|
|
39
40
|
kind: "mode";
|
|
40
|
-
screen: "ticker-search" | "themes" | "plugins" | "layout"
|
|
41
|
+
screen: "ticker-search" | "themes" | "plugins" | "layout";
|
|
41
42
|
query: string;
|
|
42
43
|
selectedIdx: number;
|
|
43
44
|
hoveredIdx: number | null;
|