gloomberb 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -11
- package/package.json +12 -5
- package/src/app.tsx +414 -191
- package/src/architecture/import-boundaries.test.ts +118 -0
- package/src/assets/gloomberb-logo.png +0 -0
- package/src/assets/gloomberb-logo.svg +73 -0
- package/src/brokers/account-cache.ts +157 -0
- package/src/brokers/profile-form.test.ts +73 -0
- package/src/brokers/profile-form.ts +109 -0
- package/src/brokers/remote-broker-adapter.ts +91 -0
- package/src/brokers/sync-broker-instance.test.ts +124 -1
- package/src/brokers/sync-broker-instance.ts +113 -17
- package/src/capabilities/factories.ts +100 -0
- package/src/capabilities/index.ts +4 -0
- package/src/capabilities/registry.test.ts +137 -0
- package/src/capabilities/registry.ts +131 -0
- package/src/capabilities/service-capabilities.ts +27 -0
- package/src/capabilities/types.ts +84 -0
- package/src/cli/commands/ticker.ts +11 -3
- package/src/cli/context.ts +19 -13
- package/src/cli/helpers.ts +1 -0
- package/src/cli/types.ts +2 -2
- package/src/components/chart/chart-controller.ts +4 -24
- package/src/components/chart/chart-data.ts +3 -14
- package/src/components/chart/chart-indicator-selector.tsx +28 -6
- package/src/components/chart/chart-pane-settings.ts +15 -28
- package/src/components/chart/chart-renderer.ts +74 -99
- package/src/components/chart/chart-resolution.ts +10 -10
- package/src/components/chart/chart-scroll.test.ts +6 -5
- package/src/components/chart/chart-scroll.ts +2 -2
- package/src/components/chart/chart-types.ts +0 -1
- package/src/components/chart/chart-viewport.ts +2 -1
- package/src/components/chart/chart.test.ts +37 -0
- package/src/components/chart/comparison-chart-data.ts +3 -17
- package/src/components/chart/comparison-chart-renderer.test.ts +27 -4
- package/src/components/chart/comparison-chart-renderer.ts +1 -1
- package/src/components/chart/comparison-stock-chart.tsx +304 -108
- package/src/components/chart/cursor-motion.ts +3 -2
- package/src/components/chart/indicators/bands.test.ts +0 -14
- package/src/components/chart/indicators/moving-averages.test.ts +0 -15
- package/src/components/chart/indicators/options.ts +3 -3
- package/src/components/chart/indicators/oscillators.test.ts +0 -21
- package/src/components/chart/indicators/types.ts +1 -2
- package/src/components/chart/native/chart-rasterizer.ts +37 -44
- package/src/components/chart/native/kitty-adapter.ts +2 -5
- package/src/components/chart/native/kitty-manager.ts +1 -1
- package/src/components/chart/native/kitty-protocol.ts +0 -1
- package/src/components/chart/native/kitty-support.ts +1 -1
- package/src/components/chart/native/kitty.test.ts +30 -13
- package/src/components/chart/native/renderer-selection.ts +1 -1
- package/src/components/chart/native/surface-manager.ts +1 -1
- package/src/components/chart/static-chart-surface.test.tsx +51 -0
- package/src/components/chart/static-chart-surface.tsx +191 -0
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +242 -35
- package/src/components/chart/stock-chart.tsx +438 -141
- package/src/components/command-bar/__snapshots__/command-bar.test.tsx.snap +4 -4
- package/src/components/command-bar/command-bar.test.tsx +269 -20
- package/src/components/command-bar/command-bar.tsx +830 -353
- package/src/components/command-bar/command-registry.ts +10 -20
- package/src/components/command-bar/helpers.test.ts +1 -1
- package/src/components/command-bar/helpers.ts +9 -11
- package/src/components/command-bar/pane-template-display.ts +4 -8
- package/src/components/command-bar/root-shortcuts.ts +12 -11
- package/src/components/command-bar/view-model.test.ts +8 -42
- package/src/components/command-bar/view-model.ts +8 -36
- package/src/components/command-bar/workflow-ops.test.ts +76 -2
- package/src/components/command-bar/workflow-ops.ts +40 -9
- package/src/components/command-bar/workflow-types.ts +2 -1
- package/src/components/data-table-stack-view.test.tsx +206 -0
- package/src/components/data-table-stack-view.tsx +152 -0
- package/src/components/data-table-view.test.tsx +188 -0
- package/src/components/data-table-view.tsx +279 -0
- package/src/components/feed-data-table-stack-view.test.tsx +225 -0
- package/src/components/feed-data-table-stack-view.tsx +365 -0
- package/src/components/index.ts +26 -31
- package/src/components/layout/detached-pane-shell.tsx +185 -0
- package/src/components/layout/floating-pane.tsx +87 -13
- package/src/components/layout/header.tsx +139 -49
- package/src/components/layout/pane-content.tsx +40 -0
- package/src/components/layout/pane-footer.test.tsx +195 -0
- package/src/components/layout/pane-footer.tsx +410 -0
- package/src/components/layout/pane-header.test.tsx +39 -0
- package/src/components/layout/pane-header.tsx +192 -14
- package/src/components/layout/pane-sizing.ts +13 -4
- package/src/components/layout/pane-title.ts +35 -0
- package/src/components/layout/pane.tsx +56 -8
- package/src/components/layout/shell.test.tsx +322 -19
- package/src/components/layout/shell.tsx +1010 -366
- package/src/components/layout/status-bar.test.tsx +17 -10
- package/src/components/layout/status-bar.tsx +367 -100
- package/src/components/layout/titlebar-overlay.ts +2 -0
- package/src/components/markdown-editor.tsx +29 -14
- package/src/components/markdown-text.tsx +25 -24
- package/src/components/onboarding/onboarding-wizard.test.tsx +8 -3
- package/src/components/onboarding/onboarding-wizard.tsx +347 -473
- package/src/components/pane-settings-dialog.tsx +521 -89
- package/src/components/pane-template-wizard.test.tsx +1 -1
- package/src/components/pane-template-wizard.tsx +38 -37
- package/src/components/price-selector-dialog.tsx +22 -21
- package/src/components/ticker-badge-list.tsx +53 -0
- package/src/components/ticker-badge-text.test.tsx +44 -1
- package/src/components/ticker-badge-text.tsx +78 -38
- package/src/components/ticker-badge.tsx +28 -7
- package/src/components/ticker-list-table-view.tsx +248 -0
- package/src/components/ticker-list-table.test.tsx +47 -1
- package/src/components/ticker-list-table.tsx +212 -41
- package/src/components/toggle-list.tsx +114 -8
- package/src/components/ui/button.tsx +24 -16
- package/src/components/ui/choice-dialog.tsx +89 -0
- package/src/components/ui/data-table.tsx +313 -114
- package/src/components/ui/external-link.tsx +52 -13
- package/src/components/ui/fields.tsx +44 -30
- package/src/components/ui/frame.tsx +24 -57
- package/src/components/ui/index.ts +15 -50
- package/src/components/ui/list-view.tsx +65 -25
- package/src/components/ui/loading.tsx +5 -60
- package/src/components/ui/message-composer.test.tsx +81 -0
- package/src/components/ui/message-composer.tsx +139 -0
- package/src/components/ui/multi-select-dialog.tsx +100 -32
- package/src/components/ui/multi-select.ts +112 -0
- package/src/components/ui/native-select.tsx +120 -0
- package/src/components/ui/page-stack-view.tsx +49 -19
- package/src/components/ui/remote-image.tsx +75 -0
- package/src/components/ui/row-value-cache.test.ts +17 -0
- package/src/components/ui/row-value-cache.ts +41 -0
- package/src/components/ui/shortcut-hint.tsx +55 -0
- package/src/components/ui/status.tsx +12 -67
- package/src/components/ui/table-layout.ts +59 -0
- package/src/components/ui/tabs.tsx +249 -27
- package/src/components/ui/toggle.tsx +19 -127
- package/src/components/ui/ui.test.tsx +266 -53
- package/src/components/use-double-click-activation.ts +7 -2
- package/src/components/use-external-link-footer.ts +79 -0
- package/src/core/app-services.ts +95 -0
- package/src/core/state/app-state.ts +886 -0
- package/src/{state → core/state}/session-persistence.ts +7 -6
- package/src/data/config-layout.ts +225 -0
- package/src/data/config-store-node.ts +390 -0
- package/src/data/config-store.test.ts +72 -0
- package/src/data/config-store.ts +35 -478
- package/src/data/sqlite-json.ts +2 -5
- package/src/data/sqlite-retry.ts +1 -0
- package/src/index.tsx +2 -68
- package/src/market-data/coordinator-subscriptions.test.ts +125 -0
- package/src/market-data/coordinator.test.ts +73 -4
- package/src/market-data/coordinator.ts +190 -24
- package/src/market-data/hooks.test.tsx +1 -1
- package/src/market-data/hooks.tsx +109 -87
- package/src/market-data/query-store.ts +3 -3
- package/src/market-data/request-types.test.ts +0 -14
- package/src/market-data/result-types.ts +1 -0
- package/src/news/aggregator.test.ts +267 -0
- package/src/news/aggregator.ts +452 -0
- package/src/news/hooks.ts +39 -0
- package/src/news/ticker-symbols.ts +21 -0
- package/src/news/types.ts +77 -0
- package/src/notifications/app-notifier.ts +21 -10
- package/src/pane-settings.test.ts +0 -2
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +124 -134
- package/src/plugins/builtin/ai/gloomberb-cli.ts +14 -13
- package/src/plugins/builtin/ai/providers.ts +1 -9
- package/src/plugins/builtin/ai/runner.ts +41 -5
- package/src/plugins/builtin/ai/screener-pane.test.tsx +37 -55
- package/src/plugins/builtin/ai/screener-pane.tsx +231 -276
- package/src/plugins/builtin/ai/settings.ts +0 -1
- package/src/plugins/builtin/ai/utils.ts +1 -71
- package/src/plugins/builtin/alerts/alert-engine.test.ts +1 -38
- package/src/plugins/builtin/alerts/index.test.tsx +61 -82
- package/src/plugins/builtin/alerts/index.tsx +263 -155
- package/src/plugins/builtin/alerts/types.ts +7 -0
- package/src/plugins/builtin/analytics/index.test.tsx +13 -8
- package/src/plugins/builtin/analytics/index.tsx +78 -120
- package/src/plugins/builtin/analytics/metrics.test.ts +0 -4
- package/src/plugins/builtin/ask-ai.test.tsx +55 -54
- package/src/plugins/builtin/broker-manager/index.test.tsx +168 -0
- package/src/plugins/builtin/broker-manager/index.tsx +759 -0
- package/src/plugins/builtin/broker-manager/model.test.ts +107 -0
- package/src/plugins/builtin/broker-manager/model.ts +142 -0
- package/src/plugins/builtin/chat-controller.test.ts +150 -0
- package/src/plugins/builtin/chat-controller.ts +88 -16
- package/src/plugins/builtin/chat.test.tsx +283 -36
- package/src/plugins/builtin/chat.tsx +373 -172
- package/src/plugins/builtin/cloud-auth-actions.tsx +72 -0
- package/src/plugins/builtin/cloud-tweets.tsx +896 -0
- package/src/plugins/builtin/comparison-chart.test.tsx +46 -57
- package/src/plugins/builtin/comparison-chart.tsx +11 -11
- package/src/plugins/builtin/correlation/compute.test.ts +0 -17
- package/src/plugins/builtin/correlation/index.tsx +51 -46
- package/src/plugins/builtin/correlation/settings.ts +1 -2
- package/src/plugins/builtin/debug.tsx +99 -70
- package/src/plugins/builtin/earnings/earnings-cache.test.ts +2 -2
- package/src/plugins/builtin/earnings/earnings-cache.ts +12 -3
- package/src/plugins/builtin/earnings/earnings-data.test.ts +42 -11
- package/src/plugins/builtin/earnings/earnings-data.ts +70 -5
- package/src/plugins/builtin/earnings/index.tsx +187 -94
- package/src/plugins/builtin/econ/calendar-source.ts +9 -116
- package/src/plugins/builtin/econ/fred-client.test.ts +1 -14
- package/src/plugins/builtin/econ/fred-series-map.ts +1 -0
- package/src/plugins/builtin/econ/index.tsx +236 -421
- package/src/plugins/builtin/econ/types.ts +1 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +43 -45
- package/src/plugins/builtin/fx-matrix/pairs.ts +1 -0
- package/src/plugins/builtin/help.test.tsx +120 -60
- package/src/plugins/builtin/help.tsx +266 -82
- package/src/plugins/builtin/holders/index.test.ts +45 -0
- package/src/plugins/builtin/holders/index.tsx +949 -0
- package/src/plugins/builtin/insider/index.tsx +207 -253
- package/src/plugins/builtin/insider/insider-data.test.ts +1 -11
- package/src/plugins/builtin/insider/insider-data.ts +1 -0
- package/src/plugins/builtin/layout-manager.tsx +12 -40
- package/src/plugins/builtin/market-movers/index.tsx +70 -144
- package/src/plugins/builtin/market-movers/screener.test.ts +0 -18
- package/src/plugins/builtin/news-wire/breaking-notifications.test.ts +98 -0
- package/src/plugins/builtin/news-wire/breaking-notifications.ts +110 -0
- package/src/plugins/builtin/news-wire/breaking-pane.tsx +157 -0
- package/src/plugins/builtin/news-wire/categories.ts +114 -0
- package/src/plugins/builtin/news-wire/default-feeds.ts +3 -0
- package/src/plugins/builtin/news-wire/digest-store.ts +44 -0
- package/src/plugins/builtin/news-wire/feed-config.test.ts +90 -0
- package/src/plugins/builtin/news-wire/feed-config.ts +216 -0
- package/src/plugins/builtin/news-wire/feed-pane.tsx +21 -0
- package/src/plugins/builtin/news-wire/hash.ts +9 -0
- package/src/plugins/builtin/news-wire/index.tsx +92 -0
- package/src/plugins/builtin/news-wire/industry-pane.tsx +140 -0
- package/src/plugins/builtin/news-wire/news-detail-view.tsx +256 -0
- package/src/plugins/builtin/news-wire/news-footer.ts +28 -0
- package/src/plugins/builtin/news-wire/news-preset-pane.tsx +94 -0
- package/src/plugins/builtin/news-wire/news-query-presets.ts +40 -0
- package/src/plugins/builtin/news-wire/news-table.test.tsx +206 -0
- package/src/plugins/builtin/news-wire/news-table.tsx +296 -0
- package/src/plugins/builtin/news-wire/persisted-articles.ts +92 -0
- package/src/plugins/builtin/news-wire/read-state.test.ts +30 -0
- package/src/plugins/builtin/news-wire/read-state.ts +76 -0
- package/src/plugins/builtin/news-wire/rss-parser.test.ts +184 -0
- package/src/plugins/builtin/news-wire/rss-parser.ts +213 -0
- package/src/plugins/builtin/news-wire/rss-source.test.ts +159 -0
- package/src/plugins/builtin/news-wire/rss-source.ts +181 -0
- package/src/plugins/builtin/news-wire/top-pane.tsx +21 -0
- package/src/plugins/builtin/news.tsx +98 -25
- package/src/plugins/builtin/notes-files.ts +51 -10
- package/src/plugins/builtin/notes.tsx +332 -131
- package/src/plugins/builtin/options.test.tsx +185 -0
- package/src/plugins/builtin/options.tsx +346 -206
- package/src/plugins/builtin/plugin-groups.ts +92 -0
- package/src/plugins/builtin/portfolio-list/header.tsx +72 -37
- package/src/plugins/builtin/portfolio-list/index.tsx +25 -42
- package/src/plugins/builtin/portfolio-list/metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane.tsx +246 -164
- package/src/plugins/builtin/portfolio-list/settings.ts +19 -51
- package/src/plugins/builtin/portfolio-list/summary.tsx +21 -16
- package/src/plugins/builtin/portfolio-list/table.tsx +66 -25
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.test.tsx +1 -1
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.ts +1 -1
- package/src/plugins/builtin/portfolio-list.test.ts +60 -1
- package/src/plugins/builtin/portfolio-list.test.tsx +122 -55
- package/src/plugins/builtin/quote-monitor.test.tsx +2 -27
- package/src/plugins/builtin/research/index.tsx +724 -0
- package/src/plugins/builtin/sec.tsx +67 -19
- package/src/plugins/builtin/sectors/index.tsx +42 -80
- package/src/plugins/builtin/sectors/sector-data.ts +1 -0
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +25 -39
- package/src/plugins/builtin/ticker-detail/financials-tab.tsx +108 -58
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +383 -271
- package/src/plugins/builtin/ticker-detail/pane.tsx +135 -49
- package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +43 -27
- package/src/plugins/builtin/ticker-detail/settings.ts +3 -6
- package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +16 -10
- package/src/plugins/builtin/ticker-detail.test.tsx +156 -33
- package/src/plugins/builtin/ticker-surface.ts +68 -0
- package/src/plugins/builtin/world-indices/index.tsx +44 -82
- package/src/plugins/builtin/world-indices/indices.ts +1 -0
- package/src/plugins/builtin/yahoo.ts +55 -1
- package/src/plugins/builtin/yield-curve/index.tsx +87 -145
- package/src/plugins/builtin/yield-curve/treasury-data.ts +6 -77
- package/src/plugins/catalog-ui.test.ts +24 -0
- package/src/plugins/catalog-ui.ts +44 -0
- package/src/plugins/catalog.ts +2 -50
- package/src/plugins/ibkr/__snapshots__/trade-tab.test.tsx.snap +5 -5
- package/src/plugins/ibkr/account-cache.test.ts +15 -14
- package/src/plugins/ibkr/account-cache.ts +15 -120
- package/src/plugins/ibkr/broker-adapter.ts +67 -0
- package/src/plugins/ibkr/config.test.ts +0 -18
- package/src/plugins/ibkr/dialogs.tsx +14 -56
- package/src/plugins/ibkr/flex.test.ts +57 -2
- package/src/plugins/ibkr/flex.ts +116 -12
- package/src/plugins/ibkr/gateway-helpers.ts +8 -3
- package/src/plugins/ibkr/gateway-service-native.ts +2381 -0
- package/src/plugins/ibkr/gateway-service.test.ts +47 -9
- package/src/plugins/ibkr/gateway-service.ts +264 -2235
- package/src/plugins/ibkr/gateway-types.ts +27 -0
- package/src/plugins/ibkr/index.test.tsx +3 -11
- package/src/plugins/ibkr/index.tsx +1 -0
- package/src/plugins/ibkr/price-normalization.test.ts +1 -6
- package/src/plugins/ibkr/price-normalization.ts +1 -0
- package/src/plugins/ibkr/trade-badge.tsx +6 -5
- package/src/plugins/ibkr/trade-tab.test.tsx +2 -2
- package/src/plugins/ibkr/trade-tab.tsx +164 -117
- package/src/plugins/ibkr/trading-pane.tsx +117 -83
- package/src/plugins/pane-manager.test.ts +3 -1
- package/src/plugins/pane-manager.ts +93 -19
- package/src/plugins/plugin-runtime.test.tsx +176 -74
- package/src/plugins/plugin-runtime.tsx +228 -10
- package/src/plugins/prediction-markets/cache.ts +49 -0
- package/src/plugins/prediction-markets/categories.ts +0 -10
- package/src/plugins/prediction-markets/chart.test.tsx +1 -1
- package/src/plugins/prediction-markets/chart.tsx +21 -20
- package/src/plugins/prediction-markets/cli.ts +1 -0
- package/src/plugins/prediction-markets/controller-data.ts +135 -89
- package/src/plugins/prediction-markets/controller.ts +37 -108
- package/src/plugins/prediction-markets/detail/book.tsx +23 -22
- package/src/plugins/prediction-markets/detail/outcomes.tsx +33 -32
- package/src/plugins/prediction-markets/detail/overview.tsx +17 -12
- package/src/plugins/prediction-markets/detail/pane.tsx +59 -45
- package/src/plugins/prediction-markets/detail/rules.tsx +22 -7
- package/src/plugins/prediction-markets/detail/shared.tsx +3 -2
- package/src/plugins/prediction-markets/detail/trades.tsx +12 -11
- package/src/plugins/prediction-markets/detail.test.tsx +1 -1
- package/src/plugins/prediction-markets/keyboard.ts +1 -0
- package/src/plugins/prediction-markets/launch.test.ts +1 -1
- package/src/plugins/prediction-markets/launch.ts +1 -1
- package/src/plugins/prediction-markets/metrics.ts +25 -12
- package/src/plugins/prediction-markets/pane.test.tsx +16 -7
- package/src/plugins/prediction-markets/pane.tsx +180 -82
- package/src/plugins/prediction-markets/plugin.test.tsx +37 -45
- package/src/plugins/prediction-markets/services/fetch.ts +27 -10
- package/src/plugins/prediction-markets/services/kalshi-adapter.ts +20 -3
- package/src/plugins/prediction-markets/services/polymarket-adapter.ts +29 -11
- package/src/plugins/prediction-markets/settings.ts +0 -5
- package/src/plugins/prediction-markets/test-helpers.tsx +19 -44
- package/src/plugins/registry.test.ts +307 -0
- package/src/plugins/registry.ts +293 -82
- package/src/react/input.ts +53 -0
- package/src/react/plugins/plugin-slot.tsx +21 -0
- package/src/react/use-raf-callback.ts +44 -0
- package/src/renderers/electrobun/bun/application-menu-click.test.ts +41 -0
- package/src/renderers/electrobun/bun/application-menu-click.ts +55 -0
- package/src/renderers/electrobun/bun/application-menu.ts +118 -0
- package/src/renderers/electrobun/bun/context-menu-click.test.ts +67 -0
- package/src/renderers/electrobun/bun/context-menu-click.ts +61 -0
- package/src/renderers/electrobun/bun/context-menu-normalize.test.ts +52 -0
- package/src/renderers/electrobun/bun/context-menu-normalize.ts +67 -0
- package/src/renderers/electrobun/bun/core-capabilities.ts +328 -0
- package/src/renderers/electrobun/bun/desktop-workspace.test.ts +63 -0
- package/src/renderers/electrobun/bun/desktop-workspace.ts +162 -0
- package/src/renderers/electrobun/bun/index.ts +1149 -0
- package/src/renderers/electrobun/bun/window-frame.test.ts +40 -0
- package/src/renderers/electrobun/bun/window-frame.ts +46 -0
- package/src/renderers/electrobun/shared/protocol.ts +72 -0
- package/src/renderers/electrobun/view/ai-host.ts +93 -0
- package/src/renderers/electrobun/view/app-services.ts +348 -0
- package/src/renderers/electrobun/view/application-menu-bridge.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +173 -0
- package/src/renderers/electrobun/view/broker-remote-client.ts +154 -0
- package/src/renderers/electrobun/view/config-host.ts +35 -0
- package/src/renderers/electrobun/view/context-menu.test.ts +140 -0
- package/src/renderers/electrobun/view/context-menu.ts +164 -0
- package/src/renderers/electrobun/view/data-table.tsx +773 -0
- package/src/renderers/electrobun/view/desktop-controls.tsx +651 -0
- package/src/renderers/electrobun/view/desktop-window-bridge.ts +59 -0
- package/src/renderers/electrobun/view/desktop-window-snapshot.test.ts +57 -0
- package/src/renderers/electrobun/view/desktop-window-snapshot.ts +41 -0
- package/src/renderers/electrobun/view/dialog-host.tsx +62 -0
- package/src/renderers/electrobun/view/http-fetch.ts +113 -0
- package/src/renderers/electrobun/view/input-host.tsx +65 -0
- package/src/renderers/electrobun/view/key-event.test.ts +34 -0
- package/src/renderers/electrobun/view/key-event.ts +134 -0
- package/src/renderers/electrobun/view/main.tsx +104 -0
- package/src/renderers/electrobun/view/native-renderer.ts +110 -0
- package/src/renderers/electrobun/view/native-stubs/chart-kitty-support.ts +9 -0
- package/src/renderers/electrobun/view/native-stubs/chart-surface-manager.ts +25 -0
- package/src/renderers/electrobun/view/native-stubs/chart-surface-sync.ts +1 -0
- package/src/renderers/electrobun/view/notes-files.ts +56 -0
- package/src/renderers/electrobun/view/resource-store.test.ts +44 -0
- package/src/renderers/electrobun/view/resource-store.ts +106 -0
- package/src/renderers/electrobun/view/rpc-codec.ts +42 -0
- package/src/renderers/electrobun/view/scrollbar-activity.ts +23 -0
- package/src/renderers/electrobun/view/toast-host.tsx +65 -0
- package/src/renderers/electrobun/view/ui-host.tsx +1490 -0
- package/src/renderers/electrobun/view/update-host.ts +38 -0
- package/src/renderers/opentui/dialog-host.test.tsx +44 -0
- package/src/renderers/opentui/dialog-host.tsx +56 -0
- package/src/renderers/opentui/host.tsx +134 -0
- package/src/renderers/opentui/input-host.tsx +24 -0
- package/src/renderers/opentui/start.tsx +71 -0
- package/src/renderers/opentui/test-utils.tsx +76 -0
- package/src/renderers/opentui/toast-host.tsx +10 -0
- package/src/renderers/opentui/ui-host.tsx +97 -0
- package/src/sources/gloomberb-cloud.test.ts +288 -17
- package/src/sources/gloomberb-cloud.ts +176 -9
- package/src/sources/provider-errors.ts +1 -0
- package/src/sources/provider-router.test.ts +220 -81
- package/src/sources/provider-router.ts +294 -111
- package/src/sources/sec-edgar.ts +19 -6
- package/src/sources/yahoo-finance.test.ts +0 -5
- package/src/sources/yahoo-finance.ts +420 -11
- package/src/state/app-activity.ts +7 -7
- package/src/state/app-bootstrap.ts +114 -40
- package/src/state/app-context-selector.test.tsx +70 -1
- package/src/state/app-context.test.ts +71 -3
- package/src/state/app-context.test.tsx +14 -7
- package/src/state/app-context.tsx +205 -869
- package/src/state/config-save-scheduler.ts +35 -0
- package/src/state/persist-scheduler.test.ts +67 -0
- package/src/state/persist-scheduler.ts +69 -0
- package/src/state/selectors-ui.ts +55 -0
- package/src/state/selectors.ts +15 -71
- package/src/state/session-persistence.test.ts +1 -1
- package/src/state/ticker-refresh-queue.ts +52 -3
- package/src/state/use-inline-tickers.ts +18 -16
- package/src/state/use-quote-streaming.test.ts +0 -6
- package/src/state/use-quote-streaming.test.tsx +1 -1
- package/src/state/use-quote-streaming.ts +3 -2
- package/src/state/use-session-snapshot.ts +32 -17
- package/src/test-support/plugin-runtime.ts +149 -0
- package/src/theme/color-utils.ts +1 -0
- package/src/theme/colors.ts +16 -0
- package/src/types/broker.ts +16 -1
- package/src/types/capability-route-source.ts +23 -0
- package/src/types/config.test.ts +23 -0
- package/src/types/config.ts +145 -10
- package/src/types/context-menu.ts +95 -0
- package/src/types/data-provider.ts +34 -3
- package/src/types/desktop-menu.ts +13 -0
- package/src/types/desktop-window.ts +29 -0
- package/src/types/financials.ts +123 -2
- package/src/types/instrument.ts +1 -0
- package/src/types/news-source.ts +9 -0
- package/src/types/persistence.ts +1 -0
- package/src/types/plugin.ts +32 -8
- package/src/ui/ascii-font.ts +85 -0
- package/src/ui/context-menu.test.ts +94 -0
- package/src/ui/context-menu.tsx +433 -0
- package/src/ui/dialog.tsx +55 -0
- package/src/ui/host.tsx +361 -0
- package/src/ui/index.tsx +126 -0
- package/src/ui/toast.tsx +40 -0
- package/src/updater.test.ts +69 -0
- package/src/updater.ts +61 -18
- package/src/utils/api-client.test.ts +186 -6
- package/src/utils/api-client.ts +433 -33
- package/src/utils/back-navigation.ts +6 -0
- package/src/utils/cli-output.ts +1 -0
- package/src/utils/currency-units.test.ts +1 -6
- package/src/utils/currency-units.ts +1 -0
- package/src/utils/debug-log.ts +68 -0
- package/src/utils/dom-screenshot.ts +326 -0
- package/src/utils/exchange-rate-map.ts +1 -0
- package/src/utils/exchanges.ts +214 -0
- package/src/utils/format.test.ts +7 -0
- package/src/utils/format.ts +1 -0
- package/src/utils/fuzzy-search.ts +1 -0
- package/src/utils/html-entities.test.ts +8 -0
- package/src/utils/html-entities.ts +32 -0
- package/src/utils/http-transport.ts +11 -0
- package/src/utils/main-thread-monitor.ts +127 -0
- package/src/utils/perf-marks.ts +54 -0
- package/src/utils/quote-freshness.ts +27 -50
- package/src/utils/quote-resolution.test.ts +5 -5
- package/src/utils/quote-resolution.ts +0 -1
- package/src/utils/quote-time.ts +1 -5
- package/src/utils/sec.test.ts +0 -12
- package/src/utils/selection-clipboard.ts +14 -6
- package/src/utils/shortcut-labels.test.ts +20 -0
- package/src/utils/shortcut-labels.ts +67 -0
- package/src/utils/slugify.ts +1 -0
- package/src/utils/terminal-input-reset.ts +1 -0
- package/src/utils/text-wrap.ts +82 -0
- package/src/utils/throttled-fetch.test.ts +19 -0
- package/src/utils/throttled-fetch.ts +11 -9
- package/src/utils/ticker-list.test.ts +1 -7
- package/src/utils/ticker-list.ts +1 -0
- package/src/utils/timestamp.ts +29 -0
- package/src/utils/tweet-text.test.ts +13 -0
- package/src/utils/tweet-text.ts +18 -0
- package/src/utils/url.ts +11 -0
- package/src/version.ts +1 -1
- package/src/components/chart/chart-control-hint.tsx +0 -39
- package/src/components/detail-feed-view.test.tsx +0 -180
- package/src/components/detail-feed-view.tsx +0 -531
- package/src/components/layout/pixel-mouse-mode.test.ts +0 -122
- package/src/components/layout/pixel-mouse.test.ts +0 -40
- package/src/components/spinner.tsx +0 -2
- package/src/components/tab-bar.tsx +0 -17
- package/src/components/ui/multi-select-chips.tsx +0 -69
- package/src/plugins/builtin/ai/index.test.tsx +0 -139
- package/src/plugins/builtin/ask-ai.tsx +0 -9
- package/src/plugins/builtin/correlation/plugin.test.ts +0 -44
- package/src/plugins/builtin/correlation/settings.test.ts +0 -34
- package/src/plugins/builtin/econ/calendar-source.test.ts +0 -219
- package/src/plugins/builtin/econ/fred-client.ts +0 -79
- package/src/plugins/builtin/fred-settings.ts +0 -10
- package/src/plugins/builtin/options-availability.test.tsx +0 -179
- package/src/plugins/builtin/options-availability.ts +0 -158
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +0 -61
- package/src/plugins/builtin/yield-curve/treasury-data.test.ts +0 -212
- package/src/plugins/prediction-markets/status.ts +0 -24
- package/src/plugins/prediction-markets/table.tsx +0 -87
- package/src/utils/quote-time.test.ts +0 -22
- package/src/utils/selection-clipboard.test.ts +0 -43
- package/src/utils/terminal-input-reset.test.ts +0 -50
|
@@ -1,29 +1,41 @@
|
|
|
1
|
+
import { Box, Input, ScrollBox, Text, Textarea, useUiCapabilities } from "../../ui";
|
|
1
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
3
|
import type { RefObject } from "react";
|
|
3
|
-
import { TextAttributes, type InputRenderable, type TextareaRenderable } from "
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { Button, NumberField, TextField } from "../ui";
|
|
4
|
+
import { TextAttributes, type InputRenderable, type ScrollBoxRenderable, type TextareaRenderable } from "../../ui";
|
|
5
|
+
import { useNativeRenderer } from "../../ui";
|
|
6
|
+
import { useViewport } from "../../react/input";
|
|
7
|
+
import { Button, NumberField, Spinner, TextField } from "../ui";
|
|
8
|
+
import { NativeSelect, openNativeSelect, type NativeSelectElement } from "../ui/native-select";
|
|
7
9
|
import { ToggleList } from "../toggle-list";
|
|
8
10
|
import {
|
|
9
11
|
colors,
|
|
10
12
|
commandBarBg,
|
|
11
13
|
commandBarHeadingText,
|
|
12
14
|
commandBarHoverBg,
|
|
15
|
+
commandBarInputBg,
|
|
16
|
+
commandBarPanelBg,
|
|
13
17
|
commandBarSelectedBg,
|
|
14
18
|
commandBarSelectedText,
|
|
15
19
|
commandBarSubtleText,
|
|
16
20
|
commandBarText,
|
|
17
21
|
} from "../../theme/colors";
|
|
18
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
getEffectiveThemeId,
|
|
24
|
+
getFocusedCollectionId,
|
|
25
|
+
useAppDispatch,
|
|
26
|
+
useAppSelector,
|
|
27
|
+
useFocusedTicker,
|
|
28
|
+
type AppState,
|
|
29
|
+
} from "../../state/app-context";
|
|
19
30
|
import { fuzzyFilter } from "../../utils/fuzzy-search";
|
|
20
31
|
import { commands, getThemeOptions, matchPrefix, type Command } from "./command-registry";
|
|
21
|
-
import { applyTheme } from "../../theme/colors";
|
|
32
|
+
import { applyTheme, getCurrentThemeId } from "../../theme/colors";
|
|
22
33
|
import { exportConfig, importConfig, resetAllData, saveConfig } from "../../data/config-store";
|
|
23
34
|
import type { DataProvider } from "../../types/data-provider";
|
|
24
35
|
import type { TickerRepository } from "../../data/ticker-repository";
|
|
25
36
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
26
37
|
import type { TickerRecord } from "../../types/ticker";
|
|
38
|
+
import type { Quote } from "../../types/financials";
|
|
27
39
|
import type {
|
|
28
40
|
CommandDef,
|
|
29
41
|
PaneSettingField,
|
|
@@ -39,8 +51,10 @@ import {
|
|
|
39
51
|
resolveFollowBindingInstance,
|
|
40
52
|
type LayoutConfig,
|
|
41
53
|
} from "../../types/config";
|
|
42
|
-
import {
|
|
43
|
-
|
|
54
|
+
import {
|
|
55
|
+
buildBrokerProfileConfig,
|
|
56
|
+
validateBrokerProfileValues,
|
|
57
|
+
} from "../../brokers/profile-form";
|
|
44
58
|
import {
|
|
45
59
|
addPaneFloating,
|
|
46
60
|
addPaneToLayout,
|
|
@@ -178,7 +192,24 @@ interface ListScreenState {
|
|
|
178
192
|
|
|
179
193
|
type WorkflowStringValues = Record<string, string>;
|
|
180
194
|
|
|
195
|
+
type CommandBarListRow =
|
|
196
|
+
| { kind: "spacer"; id: string }
|
|
197
|
+
| { kind: "heading"; id: string; label: string }
|
|
198
|
+
| { kind: "item"; item: ResultItem; globalIdx: number }
|
|
199
|
+
| { kind: "message"; id: string; label: string; dim?: boolean }
|
|
200
|
+
| { kind: "spinner"; id: string; label: string }
|
|
201
|
+
| { kind: "filler"; id: string };
|
|
202
|
+
|
|
181
203
|
const commandBarLog = debugLog.createLogger("command-bar");
|
|
204
|
+
const COMMAND_BAR_OVERLAY_Z_INDEX = 2_147_483_646;
|
|
205
|
+
const COMMAND_BAR_PANEL_Z_INDEX = 2_147_483_647;
|
|
206
|
+
const NATIVE_COMMAND_BAR_PADDING_X_PX = 14;
|
|
207
|
+
const NATIVE_COMMAND_BAR_PADDING_Y_PX = 14;
|
|
208
|
+
|
|
209
|
+
function getDefaultConfigBackupPath(): string {
|
|
210
|
+
const home = typeof process !== "undefined" ? process.env.HOME : undefined;
|
|
211
|
+
return `${home || "~"}/gloomberb-config-backup.json`;
|
|
212
|
+
}
|
|
182
213
|
|
|
183
214
|
function getInputRef(
|
|
184
215
|
store: Record<string, RefObject<InputRenderable | TextareaRenderable | null>>,
|
|
@@ -190,10 +221,109 @@ function getInputRef(
|
|
|
190
221
|
return store[fieldId]!;
|
|
191
222
|
}
|
|
192
223
|
|
|
224
|
+
function normalizeWorkflowCopy(value?: string): string {
|
|
225
|
+
return (value ?? "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function getWorkflowFieldDescription(field: CommandBarWorkflowField, active: boolean): string | null {
|
|
229
|
+
const description = field.description?.trim();
|
|
230
|
+
if (!description) return null;
|
|
231
|
+
if (normalizeWorkflowCopy(description) === normalizeWorkflowCopy(field.placeholder)) return null;
|
|
232
|
+
return field.type === "textarea" && active ? `${description} Ctrl+S submits.` : description;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function isSetAlertWorkflow(route: CommandBarRoute | null): route is CommandBarWorkflowRoute {
|
|
236
|
+
return route?.kind === "workflow"
|
|
237
|
+
&& route.payload.kind === "plugin-command"
|
|
238
|
+
&& route.payload.actionId === "set-alert";
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function normalizeAlertWorkflowSymbol(value: CommandBarFieldValue | undefined): string {
|
|
242
|
+
return coerceFieldString(value).trim().toUpperCase();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function formatAlertWorkflowPrice(value: number): string {
|
|
246
|
+
return Number.isFinite(value) ? String(value) : "";
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function formatAlertWorkflowQuote(quote: Quote): string {
|
|
250
|
+
const price = formatAlertWorkflowPrice(quote.price);
|
|
251
|
+
const exchange = quote.fullExchangeName || quote.exchangeName || quote.listingExchangeName || "";
|
|
252
|
+
const name = quote.name || quote.symbol;
|
|
253
|
+
const source = quote.dataSource === "delayed" ? "delayed" : quote.dataSource || "";
|
|
254
|
+
return [quote.symbol, name, exchange, price, source].filter(Boolean).join(" ");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function summarizeAlertWorkflowQuoteError(symbol: string, error: unknown): string {
|
|
258
|
+
if (error instanceof Error && error.message.trim()) return error.message;
|
|
259
|
+
return `No quote found for "${symbol}".`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function updateAlertWorkflowFieldDescriptions(
|
|
263
|
+
fields: CommandBarWorkflowField[],
|
|
264
|
+
descriptions: Partial<Record<"symbol" | "price", string | undefined>>,
|
|
265
|
+
): CommandBarWorkflowField[] {
|
|
266
|
+
return fields.map((field) => {
|
|
267
|
+
if (field.id !== "symbol" && field.id !== "price") return field;
|
|
268
|
+
const description = descriptions[field.id];
|
|
269
|
+
return field.description === description ? field : { ...field, description };
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function estimateWorkflowBodyRows(route: CommandBarWorkflowRoute): number {
|
|
274
|
+
const visibleFields = getVisibleWorkflowFields(route.fields, route.values);
|
|
275
|
+
const introRows = (route.subtitle ? 1 : 0)
|
|
276
|
+
+ (route.description?.length ?? 0)
|
|
277
|
+
+ (route.subtitle || (route.description?.length ?? 0) > 0 ? 1 : 0);
|
|
278
|
+
const fieldRows = visibleFields.reduce((total, field, index) => {
|
|
279
|
+
const active = field.id === route.activeFieldId;
|
|
280
|
+
const controlRows = field.type === "textarea" ? 6 : 1;
|
|
281
|
+
const descriptionRows = getWorkflowFieldDescription(field, active) ? 1 : 0;
|
|
282
|
+
const gapRows = index === visibleFields.length - 1 ? 0 : 1;
|
|
283
|
+
return total + 1 + controlRows + descriptionRows + gapRows;
|
|
284
|
+
}, 0);
|
|
285
|
+
const statusRows = (route.error ? 1 : 0) + (route.pending && route.pendingLabel ? 1 : 0);
|
|
286
|
+
return introRows + fieldRows + statusRows + 1;
|
|
287
|
+
}
|
|
288
|
+
|
|
193
289
|
function orderListResults(results: ResultItem[]): ResultItem[] {
|
|
194
290
|
return buildSections(results).flatMap((section) => section.items);
|
|
195
291
|
}
|
|
196
292
|
|
|
293
|
+
function buildListRows(listState: ListScreenState): CommandBarListRow[] {
|
|
294
|
+
const rows: CommandBarListRow[] = [];
|
|
295
|
+
const sections = buildSections(listState.results);
|
|
296
|
+
let globalIdx = 0;
|
|
297
|
+
sections.forEach((section, sectionIndex) => {
|
|
298
|
+
if (sectionIndex > 0) {
|
|
299
|
+
rows.push({ kind: "spacer", id: `spacer:${sectionIndex}:${section.category}` });
|
|
300
|
+
}
|
|
301
|
+
rows.push({ kind: "heading", id: `heading:${sectionIndex}:${section.category}`, label: section.category });
|
|
302
|
+
for (const item of section.items) {
|
|
303
|
+
rows.push({ kind: "item", item, globalIdx });
|
|
304
|
+
globalIdx += 1;
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
return rows;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function buildNativeListRows(listState: ListScreenState, rows: CommandBarListRow[]): CommandBarListRow[] {
|
|
311
|
+
if (listState.searching && rows.length === 0) {
|
|
312
|
+
return [{ kind: "spinner", id: "searching", label: "Searching…" }];
|
|
313
|
+
}
|
|
314
|
+
if (rows.length === 0) {
|
|
315
|
+
return [{ kind: "message", id: "empty", label: listState.emptyLabel }];
|
|
316
|
+
}
|
|
317
|
+
if (listState.searching) {
|
|
318
|
+
return [...rows, { kind: "spinner", id: "searching", label: "Searching…" }];
|
|
319
|
+
}
|
|
320
|
+
return rows;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function clampListIndex(index: number, length: number): number {
|
|
324
|
+
return Math.max(0, Math.min(index, Math.max(0, length - 1)));
|
|
325
|
+
}
|
|
326
|
+
|
|
197
327
|
function getVisibleMultiSelectPickerOptions(
|
|
198
328
|
route: Extract<CommandBarRoute, { kind: "picker" }>,
|
|
199
329
|
): CommandBarPickerOption[] {
|
|
@@ -233,16 +363,68 @@ export function CommandBar({
|
|
|
233
363
|
quitApp,
|
|
234
364
|
onCheckForUpdates,
|
|
235
365
|
}: CommandBarProps) {
|
|
236
|
-
const
|
|
366
|
+
const dispatch = useAppDispatch();
|
|
367
|
+
const config = useAppSelector((state) => state.config);
|
|
368
|
+
const paneState = useAppSelector((state) => state.paneState);
|
|
369
|
+
const tickers = useAppSelector((state) => state.tickers);
|
|
370
|
+
const financials = useAppSelector((state) => state.financials);
|
|
371
|
+
const focusedPaneId = useAppSelector((state) => state.focusedPaneId);
|
|
372
|
+
const layoutHistory = useAppSelector((state) => state.layoutHistory);
|
|
373
|
+
const recentTickers = useAppSelector((state) => state.recentTickers);
|
|
374
|
+
const commandBarOpen = useAppSelector((state) => state.commandBarOpen);
|
|
375
|
+
const commandBarQuery = useAppSelector((state) => state.commandBarQuery);
|
|
376
|
+
const commandBarLaunchRequest = useAppSelector((state) => state.commandBarLaunchRequest);
|
|
377
|
+
const updateAvailable = useAppSelector((state) => state.updateAvailable);
|
|
378
|
+
const updateProgress = useAppSelector((state) => state.updateProgress);
|
|
379
|
+
const updateCheckInProgress = useAppSelector((state) => state.updateCheckInProgress);
|
|
380
|
+
const updateNotice = useAppSelector((state) => state.updateNotice);
|
|
381
|
+
const state = useMemo(() => ({
|
|
382
|
+
config,
|
|
383
|
+
paneState,
|
|
384
|
+
tickers,
|
|
385
|
+
financials,
|
|
386
|
+
focusedPaneId,
|
|
387
|
+
layoutHistory,
|
|
388
|
+
recentTickers,
|
|
389
|
+
commandBarOpen,
|
|
390
|
+
commandBarQuery,
|
|
391
|
+
commandBarLaunchRequest,
|
|
392
|
+
updateAvailable,
|
|
393
|
+
updateProgress,
|
|
394
|
+
updateCheckInProgress,
|
|
395
|
+
updateNotice,
|
|
396
|
+
}) as AppState, [
|
|
397
|
+
commandBarLaunchRequest,
|
|
398
|
+
commandBarOpen,
|
|
399
|
+
commandBarQuery,
|
|
400
|
+
config,
|
|
401
|
+
financials,
|
|
402
|
+
focusedPaneId,
|
|
403
|
+
layoutHistory,
|
|
404
|
+
paneState,
|
|
405
|
+
recentTickers,
|
|
406
|
+
tickers,
|
|
407
|
+
updateAvailable,
|
|
408
|
+
updateCheckInProgress,
|
|
409
|
+
updateNotice,
|
|
410
|
+
updateProgress,
|
|
411
|
+
]);
|
|
237
412
|
const stateRef = useRef(state);
|
|
238
413
|
stateRef.current = state;
|
|
239
414
|
const { symbol: activeTickerSymbol, ticker: activeTickerData, financials: activeFinancials } = useFocusedTicker();
|
|
240
|
-
const renderer =
|
|
241
|
-
const { width: termWidth, height: termHeight } =
|
|
415
|
+
const renderer = useNativeRenderer();
|
|
416
|
+
const { width: termWidth, height: termHeight } = useViewport();
|
|
417
|
+
const { nativePaneChrome, cellWidthPx = 8, cellHeightPx = 18 } = useUiCapabilities();
|
|
418
|
+
const availableCommands = useMemo(
|
|
419
|
+
() => nativePaneChrome
|
|
420
|
+
? commands.filter((command) => command.id !== "cycle-chart-renderer")
|
|
421
|
+
: commands,
|
|
422
|
+
[nativePaneChrome],
|
|
423
|
+
);
|
|
242
424
|
const [rootQuery, setRootQueryValue] = useState(state.commandBarQuery);
|
|
243
425
|
const rootQueryRef = useRef(rootQuery);
|
|
244
426
|
rootQueryRef.current = rootQuery;
|
|
245
|
-
const rootModeInfo = resolveCommandBarMode(rootQuery);
|
|
427
|
+
const rootModeInfo = resolveCommandBarMode(rootQuery, availableCommands);
|
|
246
428
|
const [rootSelectedIdx, setRootSelectedIdx] = useState(0);
|
|
247
429
|
const [rootHoveredIdx, setRootHoveredIdx] = useState<number | null>(null);
|
|
248
430
|
const [rootSearching, setRootSearching] = useState(false);
|
|
@@ -257,12 +439,16 @@ export function CommandBar({
|
|
|
257
439
|
const rootSearchRequestIdRef = useRef(0);
|
|
258
440
|
const rootLastSearchedQueryRef = useRef<string | null>(null);
|
|
259
441
|
const tickerSearchCacheRef = useRef<Map<string, TickerSearchCandidate[]>>(new Map());
|
|
442
|
+
const alertWorkflowQuoteRequestRef = useRef(0);
|
|
260
443
|
const skipTickerSearchDebounceRef = useRef(false);
|
|
261
444
|
const lastMainBrowseRef = useRef<CommandBarMainSnapshot>({ query: "", selectedIdx: 0 });
|
|
262
445
|
const previousRootSelectionContextRef = useRef<{ query: string; mode: string } | null>(null);
|
|
263
446
|
const previousRootModeRef = useRef(rootModeInfo.kind);
|
|
264
447
|
const rootThemeBaseIdRef = useRef<string | null>(null);
|
|
265
448
|
const workflowInputRefs = useRef<Record<string, RefObject<InputRenderable | TextareaRenderable | null>>>({});
|
|
449
|
+
const workflowNativeSelectRefs = useRef(new Map<string, NativeSelectElement>());
|
|
450
|
+
const workflowScrollRef = useRef<ScrollBoxRenderable | null>(null);
|
|
451
|
+
const nativeListScrollRef = useRef<ScrollBoxRenderable | null>(null);
|
|
266
452
|
const visibleListStateRef = useRef<ListScreenState | null>(null);
|
|
267
453
|
const processedLaunchSequenceRef = useRef<number | null>(null);
|
|
268
454
|
const currentRoute = routeStack[routeStack.length - 1] ?? null;
|
|
@@ -271,21 +457,24 @@ export function CommandBar({
|
|
|
271
457
|
const activeCollectionId = getFocusedCollectionId(state);
|
|
272
458
|
const activePortfolio = state.config.portfolios.find((portfolio) => portfolio.id === activeCollectionId);
|
|
273
459
|
|
|
460
|
+
const clearThemePreview = useCallback((themeId: string | null | undefined) => {
|
|
461
|
+
if (!themeId) return;
|
|
462
|
+
if (getCurrentThemeId() !== themeId) {
|
|
463
|
+
applyTheme(themeId);
|
|
464
|
+
}
|
|
465
|
+
dispatch({ type: "PREVIEW_THEME", theme: null });
|
|
466
|
+
}, [dispatch]);
|
|
467
|
+
|
|
274
468
|
const restoreThemePreview = useCallback(() => {
|
|
275
469
|
const themeRoute = [...routeStack].reverse().find((route) => (
|
|
276
470
|
route.kind === "mode" && route.screen === "themes"
|
|
277
471
|
));
|
|
278
472
|
if (themeRoute?.kind === "mode" && themeRoute.themeBaseId) {
|
|
279
|
-
|
|
280
|
-
dispatch({ type: "SET_THEME", theme: themeRoute.themeBaseId });
|
|
473
|
+
clearThemePreview(themeRoute.themeBaseId);
|
|
281
474
|
return;
|
|
282
475
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
applyTheme(rootThemeBaseId);
|
|
286
|
-
dispatch({ type: "SET_THEME", theme: rootThemeBaseId });
|
|
287
|
-
}
|
|
288
|
-
}, [dispatch, routeStack]);
|
|
476
|
+
clearThemePreview(rootThemeBaseIdRef.current);
|
|
477
|
+
}, [clearThemePreview, routeStack]);
|
|
289
478
|
|
|
290
479
|
const closeAll = useCallback((options?: { revertThemePreview?: boolean }) => {
|
|
291
480
|
if (options?.revertThemePreview !== false) {
|
|
@@ -318,7 +507,10 @@ export function CommandBar({
|
|
|
318
507
|
setRouteStack((current) => {
|
|
319
508
|
if (current.length === 0) return current;
|
|
320
509
|
const next = [...current];
|
|
321
|
-
|
|
510
|
+
const top = next[next.length - 1]!;
|
|
511
|
+
const updated = updater(top);
|
|
512
|
+
if (updated === top) return current;
|
|
513
|
+
next[next.length - 1] = updated;
|
|
322
514
|
return next;
|
|
323
515
|
});
|
|
324
516
|
}, []);
|
|
@@ -330,8 +522,7 @@ export function CommandBar({
|
|
|
330
522
|
}
|
|
331
523
|
|
|
332
524
|
if (currentRoute.kind === "mode" && currentRoute.screen === "themes" && currentRoute.themeBaseId) {
|
|
333
|
-
|
|
334
|
-
dispatch({ type: "SET_THEME", theme: currentRoute.themeBaseId });
|
|
525
|
+
clearThemePreview(currentRoute.themeBaseId);
|
|
335
526
|
}
|
|
336
527
|
|
|
337
528
|
if (routeStack.length === 1 && currentRoute.restoreMain) {
|
|
@@ -341,7 +532,7 @@ export function CommandBar({
|
|
|
341
532
|
}
|
|
342
533
|
|
|
343
534
|
setRouteStack((current) => current.slice(0, -1));
|
|
344
|
-
}, [closeAll, currentRoute,
|
|
535
|
+
}, [clearThemePreview, closeAll, currentRoute, routeStack.length, setRootQuery]);
|
|
345
536
|
|
|
346
537
|
const dismissCommandBar = useCallback(() => {
|
|
347
538
|
if (currentRoute) {
|
|
@@ -390,9 +581,24 @@ export function CommandBar({
|
|
|
390
581
|
}, [dispatch]);
|
|
391
582
|
|
|
392
583
|
const openFixedTickerPane = useCallback((symbol: string) => {
|
|
393
|
-
pluginRegistry.
|
|
584
|
+
pluginRegistry.pinTicker(symbol, { floating: true, paneType: "ticker-detail" });
|
|
394
585
|
}, [pluginRegistry]);
|
|
395
586
|
|
|
587
|
+
const previewThemeId = useCallback((themeId: string | undefined) => {
|
|
588
|
+
if (!themeId) return;
|
|
589
|
+
if (getCurrentThemeId() !== themeId) {
|
|
590
|
+
applyTheme(themeId);
|
|
591
|
+
}
|
|
592
|
+
const previewTheme = themeId === stateRef.current.config.theme ? null : themeId;
|
|
593
|
+
if (stateRef.current.themePreview !== previewTheme) {
|
|
594
|
+
dispatch({ type: "PREVIEW_THEME", theme: previewTheme });
|
|
595
|
+
}
|
|
596
|
+
}, [dispatch]);
|
|
597
|
+
|
|
598
|
+
const previewThemeSelection = useCallback((listState: ListScreenState, selectedIdx: number) => {
|
|
599
|
+
previewThemeId(listState.results[selectedIdx]?.themeId);
|
|
600
|
+
}, [previewThemeId]);
|
|
601
|
+
|
|
396
602
|
const focusTicker = useCallback((symbol: string, options?: { forceNewPane?: boolean }) => {
|
|
397
603
|
const currentState = stateRef.current;
|
|
398
604
|
const focusedPane = currentState.focusedPaneId
|
|
@@ -444,7 +650,7 @@ export function CommandBar({
|
|
|
444
650
|
}, [dispatch, persistLayoutChange, pluginRegistry]);
|
|
445
651
|
|
|
446
652
|
const openModeRoute = useCallback((
|
|
447
|
-
screen: "ticker-search" | "themes" | "plugins" | "layout"
|
|
653
|
+
screen: "ticker-search" | "themes" | "plugins" | "layout",
|
|
448
654
|
initialQuery = "",
|
|
449
655
|
payload?: Record<string, unknown>,
|
|
450
656
|
) => {
|
|
@@ -470,6 +676,14 @@ export function CommandBar({
|
|
|
470
676
|
inputRef.current?.focus?.();
|
|
471
677
|
}, []);
|
|
472
678
|
|
|
679
|
+
const setWorkflowNativeSelectRef = useCallback((fieldId: string, element: NativeSelectElement | null) => {
|
|
680
|
+
if (element) {
|
|
681
|
+
workflowNativeSelectRefs.current.set(fieldId, element);
|
|
682
|
+
} else {
|
|
683
|
+
workflowNativeSelectRefs.current.delete(fieldId);
|
|
684
|
+
}
|
|
685
|
+
}, []);
|
|
686
|
+
|
|
473
687
|
const openWorkflowRoute = useCallback((route: CommandBarWorkflowRoute) => {
|
|
474
688
|
pushRoute({
|
|
475
689
|
...route,
|
|
@@ -659,17 +873,10 @@ export function CommandBar({
|
|
|
659
873
|
throw new Error(`Unknown broker "${brokerId}".`);
|
|
660
874
|
}
|
|
661
875
|
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
const nextValue = String(values[field.key] ?? "").trim();
|
|
665
|
-
if (!nextValue) {
|
|
666
|
-
throw new Error(`${field.label} is required.`);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
876
|
+
const validationError = validateBrokerProfileValues(adapter, values);
|
|
877
|
+
if (validationError) throw new Error(validationError);
|
|
669
878
|
|
|
670
|
-
const brokerValues =
|
|
671
|
-
? buildIbkrConfigFromValues(values)
|
|
672
|
-
: values;
|
|
879
|
+
const brokerValues = buildBrokerProfileConfig(adapter, values);
|
|
673
880
|
const instance = await pluginRegistry.createBrokerInstanceFn(
|
|
674
881
|
brokerId,
|
|
675
882
|
adapter.name.trim(),
|
|
@@ -1242,7 +1449,7 @@ export function CommandBar({
|
|
|
1242
1449
|
pluginRegistry,
|
|
1243
1450
|
]);
|
|
1244
1451
|
|
|
1245
|
-
const
|
|
1452
|
+
const runSecurityDescriptionShortcut = useCallback(async (query?: string) => {
|
|
1246
1453
|
const trimmed = query?.trim() || "";
|
|
1247
1454
|
if (!trimmed) {
|
|
1248
1455
|
const inferred = normalizeTickerInput(activeTickerSymbol, query);
|
|
@@ -1647,9 +1854,18 @@ export function CommandBar({
|
|
|
1647
1854
|
if (launch.kind === "plugin-command") {
|
|
1648
1855
|
const command = pluginRegistry.commands.get(launch.commandId);
|
|
1649
1856
|
if (!command?.wizard || command.wizard.length === 0) return;
|
|
1650
|
-
|
|
1857
|
+
let values: Record<string, string> | undefined;
|
|
1858
|
+
try {
|
|
1859
|
+
values = command.shortcutArg?.parse?.("", {
|
|
1860
|
+
activeTicker: activeTickerSymbol,
|
|
1861
|
+
});
|
|
1862
|
+
} catch {
|
|
1863
|
+
values = undefined;
|
|
1864
|
+
}
|
|
1865
|
+
openPluginCommandWorkflow(command, values ? { values } : undefined);
|
|
1651
1866
|
}
|
|
1652
1867
|
}, [
|
|
1868
|
+
activeTickerSymbol,
|
|
1653
1869
|
openPluginCommandWorkflow,
|
|
1654
1870
|
pluginRegistry,
|
|
1655
1871
|
state.commandBarLaunchRequest,
|
|
@@ -1959,9 +2175,9 @@ export function CommandBar({
|
|
|
1959
2175
|
if (pluginId && disabledPlugins.has(pluginId)) return false;
|
|
1960
2176
|
const prefix = template.shortcut?.prefix?.toUpperCase();
|
|
1961
2177
|
if (!prefix) return false;
|
|
1962
|
-
if (upper !== prefix && !upper.startsWith(`${prefix} `)) return false;
|
|
1963
2178
|
const arg = trimmed.slice(prefix.length).trim();
|
|
1964
2179
|
const argKind = template.shortcut?.argKind ?? template.shortcut?.argPlaceholder;
|
|
2180
|
+
if (upper !== prefix && (!argKind || !upper.startsWith(`${prefix} `))) return false;
|
|
1965
2181
|
if (!template.canCreate) return true;
|
|
1966
2182
|
try {
|
|
1967
2183
|
const canCreate = template.canCreate(context, arg ? { arg } : undefined);
|
|
@@ -1994,11 +2210,11 @@ export function CommandBar({
|
|
|
1994
2210
|
|
|
1995
2211
|
const rootShortcutIntent = useMemo(() => parseRootShortcutIntent({
|
|
1996
2212
|
query: rootQuery,
|
|
1997
|
-
commands,
|
|
2213
|
+
commands: availableCommands,
|
|
1998
2214
|
pluginCommands: getAvailablePluginCommands(),
|
|
1999
2215
|
paneTemplates: getAvailablePaneShortcutTemplates(rootQuery),
|
|
2000
2216
|
activeTicker: activeTickerSymbol,
|
|
2001
|
-
}), [activeTickerSymbol, getAvailablePaneShortcutTemplates, getAvailablePluginCommands, rootQuery]);
|
|
2217
|
+
}), [activeTickerSymbol, availableCommands, getAvailablePaneShortcutTemplates, getAvailablePluginCommands, rootQuery]);
|
|
2002
2218
|
|
|
2003
2219
|
const createPaneTemplateItem = useCallback((
|
|
2004
2220
|
template: PaneTemplateDef,
|
|
@@ -2035,7 +2251,7 @@ export function CommandBar({
|
|
|
2035
2251
|
id: `pane-template:${template.id}:${arg || ""}`,
|
|
2036
2252
|
label: displayLabel,
|
|
2037
2253
|
detail: shortcutLabel ? `${template.description} · ${shortcutLabel}` : template.description,
|
|
2038
|
-
category: options?.category ?? (pluginName ? `${pluginName} Panes` : "
|
|
2254
|
+
category: options?.category ?? (pluginName ? `${pluginName} Panes` : "Panes"),
|
|
2039
2255
|
kind: "action",
|
|
2040
2256
|
right: options?.showShortcut ? template.shortcut?.prefix : undefined,
|
|
2041
2257
|
searchText: `${displayLabel} ${template.label} ${template.paneId} ${template.keywords?.join(" ") || ""} ${shortcutLabel || ""} ${pluginName || ""}`,
|
|
@@ -2043,13 +2259,6 @@ export function CommandBar({
|
|
|
2043
2259
|
};
|
|
2044
2260
|
}, [openPaneTemplateDirect, openPaneTemplateWorkflow, pluginRegistry, runPaneTemplateShortcut, shouldOpenTemplateConfig]);
|
|
2045
2261
|
|
|
2046
|
-
const paneTemplateItems = useCallback((filterQuery?: string): ResultItem[] => {
|
|
2047
|
-
const items = getAvailablePaneTemplates().map((template) => createPaneTemplateItem(template));
|
|
2048
|
-
return filterQuery
|
|
2049
|
-
? fuzzyFilter(items, filterQuery, (item) => `${item.label} ${item.detail} ${item.searchText || ""} ${item.right || ""}`)
|
|
2050
|
-
: items;
|
|
2051
|
-
}, [createPaneTemplateItem, getAvailablePaneTemplates]);
|
|
2052
|
-
|
|
2053
2262
|
const paneShortcutItems = useCallback((options?: {
|
|
2054
2263
|
filterQuery?: string;
|
|
2055
2264
|
createOptions?: PaneTemplateCreateOptions;
|
|
@@ -2096,10 +2305,9 @@ export function CommandBar({
|
|
|
2096
2305
|
command: CommandDef,
|
|
2097
2306
|
shortcutArg: string,
|
|
2098
2307
|
): Record<string, string> => {
|
|
2099
|
-
if (!shortcutArg.trim()) return {};
|
|
2100
2308
|
return command.shortcutArg?.parse?.(shortcutArg, {
|
|
2101
2309
|
activeTicker: activeTickerSymbol,
|
|
2102
|
-
}) ?? { shortcut: shortcutArg };
|
|
2310
|
+
}) ?? (shortcutArg.trim() ? { shortcut: shortcutArg } : {});
|
|
2103
2311
|
}, [activeTickerSymbol]);
|
|
2104
2312
|
|
|
2105
2313
|
const createPluginCommandItem = useCallback((
|
|
@@ -2144,7 +2352,8 @@ export function CommandBar({
|
|
|
2144
2352
|
return;
|
|
2145
2353
|
}
|
|
2146
2354
|
if (command.wizard && command.wizard.length > 0) {
|
|
2147
|
-
|
|
2355
|
+
const values = parsePluginCommandShortcutArg(command, "");
|
|
2356
|
+
openPluginCommandWorkflow(command, { values });
|
|
2148
2357
|
return;
|
|
2149
2358
|
}
|
|
2150
2359
|
const confirm = resolvePluginCommandConfirm(command);
|
|
@@ -2304,7 +2513,7 @@ export function CommandBar({
|
|
|
2304
2513
|
});
|
|
2305
2514
|
return;
|
|
2306
2515
|
case "export-config": {
|
|
2307
|
-
const exportPath =
|
|
2516
|
+
const exportPath = getDefaultConfigBackupPath();
|
|
2308
2517
|
void exportConfig(state.config, exportPath)
|
|
2309
2518
|
.then(() => {
|
|
2310
2519
|
notify(`Config exported to ${exportPath}`, { type: "success" });
|
|
@@ -2316,7 +2525,7 @@ export function CommandBar({
|
|
|
2316
2525
|
return;
|
|
2317
2526
|
}
|
|
2318
2527
|
case "import-config": {
|
|
2319
|
-
const importPath =
|
|
2528
|
+
const importPath = getDefaultConfigBackupPath();
|
|
2320
2529
|
void importConfig(state.config.dataDir, importPath)
|
|
2321
2530
|
.then((imported) => {
|
|
2322
2531
|
dispatch({ type: "SET_CONFIG", config: imported });
|
|
@@ -2350,8 +2559,8 @@ export function CommandBar({
|
|
|
2350
2559
|
void onCheckForUpdates?.();
|
|
2351
2560
|
closeAll({ revertThemePreview: false });
|
|
2352
2561
|
return;
|
|
2353
|
-
case "
|
|
2354
|
-
void
|
|
2562
|
+
case "security-description":
|
|
2563
|
+
void runSecurityDescriptionShortcut(arg);
|
|
2355
2564
|
return;
|
|
2356
2565
|
case "remove-watchlist":
|
|
2357
2566
|
case "remove-portfolio": {
|
|
@@ -2384,12 +2593,12 @@ export function CommandBar({
|
|
|
2384
2593
|
onCheckForUpdates,
|
|
2385
2594
|
pluginRegistry,
|
|
2386
2595
|
quitApp,
|
|
2387
|
-
|
|
2596
|
+
runSecurityDescriptionShortcut,
|
|
2388
2597
|
executeCollectionCommand,
|
|
2389
2598
|
]);
|
|
2390
2599
|
|
|
2391
|
-
const activeMatch = matchPrefix(rootQuery);
|
|
2392
|
-
const
|
|
2600
|
+
const activeMatch = matchPrefix(rootQuery, availableCommands);
|
|
2601
|
+
const rootSecurityDescriptionArg = activeMatch?.command.id === "security-description" && activeMatch.arg.length >= 1
|
|
2393
2602
|
? activeMatch.arg
|
|
2394
2603
|
: null;
|
|
2395
2604
|
|
|
@@ -2406,18 +2615,17 @@ export function CommandBar({
|
|
|
2406
2615
|
if (currentRoute) return;
|
|
2407
2616
|
|
|
2408
2617
|
const previousMode = previousRootModeRef.current;
|
|
2409
|
-
if (rootModeInfo.kind === "themes" && previousMode !== "themes") {
|
|
2618
|
+
if (rootModeInfo.kind === "themes" && (previousMode !== "themes" || !rootThemeBaseIdRef.current)) {
|
|
2410
2619
|
rootThemeBaseIdRef.current = state.config.theme;
|
|
2411
2620
|
} else if (rootModeInfo.kind !== "themes" && previousMode === "themes") {
|
|
2412
2621
|
const rootThemeBaseId = rootThemeBaseIdRef.current;
|
|
2413
|
-
if (rootThemeBaseId && state
|
|
2414
|
-
|
|
2415
|
-
dispatch({ type: "SET_THEME", theme: rootThemeBaseId });
|
|
2622
|
+
if (rootThemeBaseId && getEffectiveThemeId(state) !== rootThemeBaseId) {
|
|
2623
|
+
clearThemePreview(rootThemeBaseId);
|
|
2416
2624
|
}
|
|
2417
2625
|
rootThemeBaseIdRef.current = null;
|
|
2418
2626
|
}
|
|
2419
2627
|
previousRootModeRef.current = rootModeInfo.kind;
|
|
2420
|
-
}, [
|
|
2628
|
+
}, [clearThemePreview, currentRoute, rootModeInfo.kind, state.config.theme, state.themePreview]);
|
|
2421
2629
|
|
|
2422
2630
|
const tickerSearchRouteQuery = currentRoute?.kind === "mode" && currentRoute.screen === "ticker-search"
|
|
2423
2631
|
? currentRoute.query
|
|
@@ -2597,9 +2805,9 @@ export function CommandBar({
|
|
|
2597
2805
|
case "check-for-updates":
|
|
2598
2806
|
if (state.updateProgress?.phase === "downloading") return `Downloading v${state.updateAvailable?.version}: ${state.updateProgress.percent ?? 0}%`;
|
|
2599
2807
|
if (state.updateProgress?.phase === "replacing") return "Installing update";
|
|
2600
|
-
if (state.updateProgress?.phase === "done") return "Update installed - restart to apply";
|
|
2808
|
+
if (state.updateProgress?.phase === "done") return state.updateProgress.message ?? "Update installed - restart to apply";
|
|
2601
2809
|
if (state.updateProgress?.phase === "error") return `Update failed: ${state.updateProgress.error}`;
|
|
2602
|
-
if (state.updateCheckInProgress) return "Checking
|
|
2810
|
+
if (state.updateCheckInProgress) return "Checking releases now";
|
|
2603
2811
|
if (state.updateAvailable) return `Latest available: v${state.updateAvailable.version}`;
|
|
2604
2812
|
if (state.updateNotice) return state.updateNotice;
|
|
2605
2813
|
return command.description;
|
|
@@ -2651,17 +2859,17 @@ export function CommandBar({
|
|
|
2651
2859
|
}
|
|
2652
2860
|
|
|
2653
2861
|
const { command } = rootShortcutIntent;
|
|
2654
|
-
if (command.id === "
|
|
2862
|
+
if (command.id === "security-description") {
|
|
2655
2863
|
const inferredSymbol = normalizeTickerInput(activeTickerSymbol, undefined);
|
|
2656
2864
|
if (!rootShortcutIntent.argText && inferredSymbol) {
|
|
2657
2865
|
return {
|
|
2658
|
-
id: "
|
|
2866
|
+
id: "security-description:inferred",
|
|
2659
2867
|
label: inferredSymbol,
|
|
2660
|
-
detail: `Open ${inferredSymbol}`,
|
|
2868
|
+
detail: `Open security details for ${inferredSymbol}`,
|
|
2661
2869
|
category: "Search",
|
|
2662
2870
|
kind: "action",
|
|
2663
2871
|
right: command.prefix,
|
|
2664
|
-
action: () => { void
|
|
2872
|
+
action: () => { void runSecurityDescriptionShortcut(inferredSymbol); },
|
|
2665
2873
|
};
|
|
2666
2874
|
}
|
|
2667
2875
|
return null;
|
|
@@ -2724,13 +2932,29 @@ export function CommandBar({
|
|
|
2724
2932
|
}
|
|
2725
2933
|
|
|
2726
2934
|
const items: ResultItem[] = [];
|
|
2727
|
-
const match = matchPrefix(rootQuery);
|
|
2935
|
+
const match = matchPrefix(rootQuery, availableCommands);
|
|
2728
2936
|
let initialIdx = 0;
|
|
2729
2937
|
const shortcutItem = buildRootShortcutItem();
|
|
2730
2938
|
|
|
2731
|
-
if (
|
|
2939
|
+
if (rootShortcutIntent.kind !== "none" && rootShortcutIntent.source === "pane-template" && shortcutItem) {
|
|
2940
|
+
const matchingTemplates = getAvailablePaneShortcutTemplates(rootQuery);
|
|
2941
|
+
const templateItems = matchingTemplates.length > 0
|
|
2942
|
+
? matchingTemplates.map((template) => createPaneTemplateItem(template, {
|
|
2943
|
+
category: "Panes",
|
|
2944
|
+
createOptions: rootShortcutIntent.argText ? { arg: rootShortcutIntent.argText } : undefined,
|
|
2945
|
+
showShortcut: true,
|
|
2946
|
+
shortcutExecution: true,
|
|
2947
|
+
}))
|
|
2948
|
+
: [shortcutItem];
|
|
2949
|
+
const seenItemIds = new Set<string>();
|
|
2950
|
+
for (const item of templateItems) {
|
|
2951
|
+
if (seenItemIds.has(item.id)) continue;
|
|
2952
|
+
seenItemIds.add(item.id);
|
|
2953
|
+
items.push(item);
|
|
2954
|
+
}
|
|
2955
|
+
} else if (
|
|
2732
2956
|
rootShortcutIntent.kind !== "none"
|
|
2733
|
-
&&
|
|
2957
|
+
&& rootShortcutIntent.source === "plugin-command"
|
|
2734
2958
|
&& shortcutItem
|
|
2735
2959
|
) {
|
|
2736
2960
|
items.push(shortcutItem);
|
|
@@ -2768,8 +2992,6 @@ export function CommandBar({
|
|
|
2768
2992
|
action: toggleAction,
|
|
2769
2993
|
});
|
|
2770
2994
|
}
|
|
2771
|
-
} else if (match && match.command.id === "new-pane") {
|
|
2772
|
-
items.push(...paneTemplateItems(match.arg));
|
|
2773
2995
|
} else if (match && match.command.id === "layout") {
|
|
2774
2996
|
const currentLayout = state.config.layout;
|
|
2775
2997
|
const focusedPane = state.focusedPaneId ? findPaneInstance(currentLayout, state.focusedPaneId) : null;
|
|
@@ -3052,7 +3274,7 @@ export function CommandBar({
|
|
|
3052
3274
|
},
|
|
3053
3275
|
});
|
|
3054
3276
|
});
|
|
3055
|
-
} else if (match && match.command.id === "
|
|
3277
|
+
} else if (match && match.command.id === "security-description") {
|
|
3056
3278
|
if (shortcutItem) {
|
|
3057
3279
|
items.push(shortcutItem);
|
|
3058
3280
|
}
|
|
@@ -3060,7 +3282,7 @@ export function CommandBar({
|
|
|
3060
3282
|
items.push({
|
|
3061
3283
|
id: "search-hint",
|
|
3062
3284
|
label: "Type a ticker symbol",
|
|
3063
|
-
detail: "
|
|
3285
|
+
detail: "Open security details after resolving a ticker",
|
|
3064
3286
|
category: "Search",
|
|
3065
3287
|
kind: "info",
|
|
3066
3288
|
action: () => {},
|
|
@@ -3091,7 +3313,7 @@ export function CommandBar({
|
|
|
3091
3313
|
category: "Tickers",
|
|
3092
3314
|
})));
|
|
3093
3315
|
items.push(...paneShortcutItems());
|
|
3094
|
-
for (const command of
|
|
3316
|
+
for (const command of availableCommands) {
|
|
3095
3317
|
const item = commandToItem(command);
|
|
3096
3318
|
if (item) items.push(item);
|
|
3097
3319
|
}
|
|
@@ -3099,7 +3321,7 @@ export function CommandBar({
|
|
|
3099
3321
|
items.push(...pluginCommandItems());
|
|
3100
3322
|
} else {
|
|
3101
3323
|
const tickerItems = localTickerSearchResultItems(undefined, { category: "Tickers" });
|
|
3102
|
-
const commandItems =
|
|
3324
|
+
const commandItems = availableCommands
|
|
3103
3325
|
.map((command) => commandToItem(command))
|
|
3104
3326
|
.filter((item): item is ResultItem => item !== null);
|
|
3105
3327
|
const allItems = [
|
|
@@ -3118,6 +3340,7 @@ export function CommandBar({
|
|
|
3118
3340
|
activeCollectionId,
|
|
3119
3341
|
activeTickerData,
|
|
3120
3342
|
activeTickerSymbol,
|
|
3343
|
+
availableCommands,
|
|
3121
3344
|
closeAll,
|
|
3122
3345
|
createPluginCommandItem,
|
|
3123
3346
|
currentRoute,
|
|
@@ -3130,7 +3353,6 @@ export function CommandBar({
|
|
|
3130
3353
|
openBuiltInWorkflow,
|
|
3131
3354
|
openPickerRoute,
|
|
3132
3355
|
paneShortcutItems,
|
|
3133
|
-
paneTemplateItems,
|
|
3134
3356
|
persistLayoutChange,
|
|
3135
3357
|
pluginCommandItems,
|
|
3136
3358
|
pluginRegistry,
|
|
@@ -3138,7 +3360,7 @@ export function CommandBar({
|
|
|
3138
3360
|
rootQuery,
|
|
3139
3361
|
rootShortcutIntent,
|
|
3140
3362
|
runDirectCommand,
|
|
3141
|
-
|
|
3363
|
+
runSecurityDescriptionShortcut,
|
|
3142
3364
|
state,
|
|
3143
3365
|
tickerActionItems,
|
|
3144
3366
|
]);
|
|
@@ -3175,7 +3397,7 @@ export function CommandBar({
|
|
|
3175
3397
|
return;
|
|
3176
3398
|
}
|
|
3177
3399
|
|
|
3178
|
-
if (!
|
|
3400
|
+
if (!rootSecurityDescriptionArg) {
|
|
3179
3401
|
setRootSearching(false);
|
|
3180
3402
|
setRootProviderResults(null);
|
|
3181
3403
|
setRootProviderResultsQuery(null);
|
|
@@ -3184,7 +3406,7 @@ export function CommandBar({
|
|
|
3184
3406
|
return;
|
|
3185
3407
|
}
|
|
3186
3408
|
|
|
3187
|
-
const searchQuery =
|
|
3409
|
+
const searchQuery = rootSecurityDescriptionArg;
|
|
3188
3410
|
if (rootSearchTimerRef.current) clearTimeout(rootSearchTimerRef.current);
|
|
3189
3411
|
if (rootLastSearchedQueryRef.current === searchQuery) {
|
|
3190
3412
|
return;
|
|
@@ -3250,18 +3472,18 @@ export function CommandBar({
|
|
|
3250
3472
|
currentRoute,
|
|
3251
3473
|
dataProvider,
|
|
3252
3474
|
readTickerSearchCache,
|
|
3253
|
-
|
|
3475
|
+
rootSecurityDescriptionArg,
|
|
3254
3476
|
state.config.portfolios,
|
|
3255
3477
|
state.tickers,
|
|
3256
3478
|
writeTickerSearchCache,
|
|
3257
3479
|
]);
|
|
3258
3480
|
|
|
3259
3481
|
const rootResults = useMemo(() => {
|
|
3260
|
-
if (
|
|
3482
|
+
if (rootSecurityDescriptionArg && rootProviderResultsQuery === rootSecurityDescriptionArg && rootProviderResults) {
|
|
3261
3483
|
return rootProviderResults;
|
|
3262
3484
|
}
|
|
3263
3485
|
return rootResultModel.items;
|
|
3264
|
-
}, [rootProviderResults, rootProviderResultsQuery, rootResultModel.items,
|
|
3486
|
+
}, [rootProviderResults, rootProviderResultsQuery, rootResultModel.items, rootSecurityDescriptionArg]);
|
|
3265
3487
|
|
|
3266
3488
|
const rootGhostCompletion = !currentRoute && rootShortcutIntent.kind === "inferred-complete"
|
|
3267
3489
|
? rootShortcutIntent.completionQuery
|
|
@@ -3302,14 +3524,14 @@ export function CommandBar({
|
|
|
3302
3524
|
: `Shortcut: ${rootShortcutIntent.label}`;
|
|
3303
3525
|
}
|
|
3304
3526
|
|
|
3305
|
-
if (rootShortcutIntent.command.id === "
|
|
3527
|
+
if (rootShortcutIntent.command.id === "security-description") {
|
|
3306
3528
|
const symbol = normalizeTickerInput(activeTickerSymbol, rootShortcutIntent.argText);
|
|
3307
3529
|
if (symbol) {
|
|
3308
3530
|
return rootShortcutIntent.kind === "inferred-complete"
|
|
3309
|
-
? `Shortcut:
|
|
3310
|
-
: `Shortcut:
|
|
3531
|
+
? `Shortcut: DES ${symbol} · Tab to accept`
|
|
3532
|
+
: `Shortcut: DES ${symbol}`;
|
|
3311
3533
|
}
|
|
3312
|
-
return "Shortcut:
|
|
3534
|
+
return "Shortcut: Description";
|
|
3313
3535
|
}
|
|
3314
3536
|
|
|
3315
3537
|
if (isCollectionCommand(rootShortcutIntent.command.id)) {
|
|
@@ -3351,7 +3573,7 @@ export function CommandBar({
|
|
|
3351
3573
|
const acceptRootShortcutTab = useCallback((): boolean => {
|
|
3352
3574
|
const intent = parseRootShortcutIntent({
|
|
3353
3575
|
query: rootQueryRef.current,
|
|
3354
|
-
commands,
|
|
3576
|
+
commands: availableCommands,
|
|
3355
3577
|
pluginCommands: getAvailablePluginCommands(),
|
|
3356
3578
|
paneTemplates: getAvailablePaneShortcutTemplates(rootQueryRef.current),
|
|
3357
3579
|
activeTicker: activeTickerSymbol,
|
|
@@ -3381,7 +3603,7 @@ export function CommandBar({
|
|
|
3381
3603
|
return false;
|
|
3382
3604
|
}
|
|
3383
3605
|
|
|
3384
|
-
if (intent.command.id === "
|
|
3606
|
+
if (intent.command.id === "security-description") {
|
|
3385
3607
|
openModeRoute("ticker-search", intent.argText);
|
|
3386
3608
|
return true;
|
|
3387
3609
|
}
|
|
@@ -3395,6 +3617,7 @@ export function CommandBar({
|
|
|
3395
3617
|
return false;
|
|
3396
3618
|
}, [
|
|
3397
3619
|
activeTickerSymbol,
|
|
3620
|
+
availableCommands,
|
|
3398
3621
|
getAvailablePluginCommands,
|
|
3399
3622
|
getAvailablePaneShortcutTemplates,
|
|
3400
3623
|
openModeRoute,
|
|
@@ -3405,7 +3628,7 @@ export function CommandBar({
|
|
|
3405
3628
|
const resolveImmediateRootSelection = useCallback((query: string): ResultItem | null => {
|
|
3406
3629
|
const intent = parseRootShortcutIntent({
|
|
3407
3630
|
query,
|
|
3408
|
-
commands,
|
|
3631
|
+
commands: availableCommands,
|
|
3409
3632
|
pluginCommands: getAvailablePluginCommands(),
|
|
3410
3633
|
paneTemplates: getAvailablePaneShortcutTemplates(query),
|
|
3411
3634
|
activeTicker: activeTickerSymbol,
|
|
@@ -3425,49 +3648,45 @@ export function CommandBar({
|
|
|
3425
3648
|
});
|
|
3426
3649
|
}
|
|
3427
3650
|
|
|
3428
|
-
const match = matchPrefix(query);
|
|
3651
|
+
const match = matchPrefix(query, availableCommands);
|
|
3429
3652
|
if (!match) {
|
|
3430
3653
|
return null;
|
|
3431
3654
|
}
|
|
3432
3655
|
|
|
3433
|
-
if (match.command.id === "
|
|
3656
|
+
if (match.command.id === "security-description") {
|
|
3434
3657
|
if (!match.arg) {
|
|
3435
3658
|
const inferredTicker = normalizeTickerInput(activeTickerSymbol, undefined);
|
|
3436
3659
|
if (inferredTicker) {
|
|
3437
3660
|
return {
|
|
3438
|
-
id: "
|
|
3661
|
+
id: "security-description:inferred",
|
|
3439
3662
|
label: inferredTicker,
|
|
3440
|
-
detail: `Open ${inferredTicker}`,
|
|
3663
|
+
detail: `Open security details for ${inferredTicker}`,
|
|
3441
3664
|
category: "Search",
|
|
3442
3665
|
kind: "action",
|
|
3443
3666
|
right: match.command.prefix,
|
|
3444
|
-
action: () => { void
|
|
3667
|
+
action: () => { void runSecurityDescriptionShortcut(inferredTicker); },
|
|
3445
3668
|
};
|
|
3446
3669
|
}
|
|
3447
3670
|
return {
|
|
3448
|
-
id: "
|
|
3449
|
-
label: "
|
|
3450
|
-
detail: "
|
|
3671
|
+
id: "security-description-route",
|
|
3672
|
+
label: "Description",
|
|
3673
|
+
detail: "Open security details for a ticker",
|
|
3451
3674
|
category: "Search",
|
|
3452
3675
|
kind: "command",
|
|
3453
3676
|
action: () => openModeRoute("ticker-search", ""),
|
|
3454
3677
|
};
|
|
3455
3678
|
}
|
|
3456
3679
|
return {
|
|
3457
|
-
id: `
|
|
3458
|
-
label: `
|
|
3459
|
-
detail: "
|
|
3680
|
+
id: `security-description:${match.arg}`,
|
|
3681
|
+
label: `DES ${match.arg.toUpperCase()}`,
|
|
3682
|
+
detail: "Open security details or resolve the ticker",
|
|
3460
3683
|
category: "Search",
|
|
3461
3684
|
kind: "command",
|
|
3462
3685
|
right: match.command.prefix,
|
|
3463
|
-
action: () => { void
|
|
3686
|
+
action: () => { void runSecurityDescriptionShortcut(match.arg); },
|
|
3464
3687
|
};
|
|
3465
3688
|
}
|
|
3466
3689
|
|
|
3467
|
-
if (match.command.id === "new-pane") {
|
|
3468
|
-
return paneTemplateItems(match.arg)[0] ?? null;
|
|
3469
|
-
}
|
|
3470
|
-
|
|
3471
3690
|
if (match.command.id === "theme") {
|
|
3472
3691
|
return {
|
|
3473
3692
|
id: "theme-route",
|
|
@@ -3534,13 +3753,13 @@ export function CommandBar({
|
|
|
3534
3753
|
createPaneTemplateItem,
|
|
3535
3754
|
createPluginCommandItem,
|
|
3536
3755
|
activeTickerSymbol,
|
|
3756
|
+
availableCommands,
|
|
3537
3757
|
executeCollectionCommand,
|
|
3538
3758
|
getAvailablePluginCommands,
|
|
3539
3759
|
getAvailablePaneShortcutTemplates,
|
|
3540
3760
|
openModeRoute,
|
|
3541
|
-
paneTemplateItems,
|
|
3542
3761
|
runDirectCommand,
|
|
3543
|
-
|
|
3762
|
+
runSecurityDescriptionShortcut,
|
|
3544
3763
|
]);
|
|
3545
3764
|
|
|
3546
3765
|
const routeListState = useMemo<ListScreenState | null>(() => {
|
|
@@ -3548,7 +3767,7 @@ export function CommandBar({
|
|
|
3548
3767
|
const emptyState = getEmptyState(
|
|
3549
3768
|
rootModeInfo.kind,
|
|
3550
3769
|
rootQuery,
|
|
3551
|
-
activeMatch?.command.id === "
|
|
3770
|
+
activeMatch?.command.id === "security-description" ? activeMatch.arg : undefined,
|
|
3552
3771
|
);
|
|
3553
3772
|
return {
|
|
3554
3773
|
kind: "root",
|
|
@@ -3904,23 +4123,6 @@ export function CommandBar({
|
|
|
3904
4123
|
footerRight: getScreenFooterRight(currentRoute),
|
|
3905
4124
|
};
|
|
3906
4125
|
}
|
|
3907
|
-
case "new-pane": {
|
|
3908
|
-
const results = paneTemplateItems(currentRoute.query);
|
|
3909
|
-
return {
|
|
3910
|
-
kind: "mode",
|
|
3911
|
-
title: "New Pane",
|
|
3912
|
-
subtitle: "Create panes from plugin-defined templates.",
|
|
3913
|
-
query: currentRoute.query,
|
|
3914
|
-
selectedIdx: currentRoute.selectedIdx,
|
|
3915
|
-
hoveredIdx: currentRoute.hoveredIdx,
|
|
3916
|
-
results: orderListResults(results),
|
|
3917
|
-
searching: false,
|
|
3918
|
-
emptyLabel: getEmptyState("new-pane", currentRoute.query).label,
|
|
3919
|
-
emptyDetail: getEmptyState("new-pane", currentRoute.query).detail,
|
|
3920
|
-
footerLeft: getScreenFooterLeft(currentRoute),
|
|
3921
|
-
footerRight: getScreenFooterRight(currentRoute),
|
|
3922
|
-
};
|
|
3923
|
-
}
|
|
3924
4126
|
case "ticker-search": {
|
|
3925
4127
|
const results = currentRoute.query.trim()
|
|
3926
4128
|
? tickerSearchResults.map((item) => adaptTickerSearchRouteResult(item, currentRoute.payload))
|
|
@@ -3928,8 +4130,8 @@ export function CommandBar({
|
|
|
3928
4130
|
const emptyState = getEmptyState("search", currentRoute.query, currentRoute.query);
|
|
3929
4131
|
return {
|
|
3930
4132
|
kind: "mode",
|
|
3931
|
-
title: "
|
|
3932
|
-
subtitle: "
|
|
4133
|
+
title: "Security Description",
|
|
4134
|
+
subtitle: "Resolve a ticker, then open its detail pane.",
|
|
3933
4135
|
query: currentRoute.query,
|
|
3934
4136
|
selectedIdx: currentRoute.selectedIdx,
|
|
3935
4137
|
hoveredIdx: currentRoute.hoveredIdx,
|
|
@@ -4022,6 +4224,7 @@ export function CommandBar({
|
|
|
4022
4224
|
dispatch,
|
|
4023
4225
|
duplicatePane,
|
|
4024
4226
|
executeCollectionCommand,
|
|
4227
|
+
getAvailablePaneShortcutTemplates,
|
|
4025
4228
|
getAvailablePaneTemplates,
|
|
4026
4229
|
localTickerSearchResultItems,
|
|
4027
4230
|
mapTickerSearchCandidateToResultItem,
|
|
@@ -4032,7 +4235,6 @@ export function CommandBar({
|
|
|
4032
4235
|
openPaneSettingsRoute,
|
|
4033
4236
|
openPickerRoute,
|
|
4034
4237
|
paneShortcutItems,
|
|
4035
|
-
paneTemplateItems,
|
|
4036
4238
|
persistLayoutChange,
|
|
4037
4239
|
pluginCommandItems,
|
|
4038
4240
|
pluginRegistry,
|
|
@@ -4052,21 +4254,6 @@ export function CommandBar({
|
|
|
4052
4254
|
]);
|
|
4053
4255
|
visibleListStateRef.current = routeListState;
|
|
4054
4256
|
|
|
4055
|
-
useEffect(() => {
|
|
4056
|
-
if (!state.commandBarOpen) return;
|
|
4057
|
-
if (!routeListState) return;
|
|
4058
|
-
const selected = currentRoute?.kind === "mode" && currentRoute.screen === "themes"
|
|
4059
|
-
? routeListState.results[currentRoute.selectedIdx]
|
|
4060
|
-
: !currentRoute && rootModeInfo.kind === "themes"
|
|
4061
|
-
? routeListState.results[rootSelectedIdx]
|
|
4062
|
-
: null;
|
|
4063
|
-
if (!selected?.themeId) return;
|
|
4064
|
-
if (selected?.themeId && state.config.theme !== selected.themeId) {
|
|
4065
|
-
applyTheme(selected.themeId);
|
|
4066
|
-
dispatch({ type: "SET_THEME", theme: selected.themeId });
|
|
4067
|
-
}
|
|
4068
|
-
}, [currentRoute, dispatch, rootModeInfo.kind, rootSelectedIdx, routeListState, state.commandBarOpen, state.config.theme]);
|
|
4069
|
-
|
|
4070
4257
|
useEffect(() => {
|
|
4071
4258
|
if (currentRoute?.kind !== "workflow") return;
|
|
4072
4259
|
ensureRouteFieldFocus(currentRoute);
|
|
@@ -4092,6 +4279,10 @@ export function CommandBar({
|
|
|
4092
4279
|
|
|
4093
4280
|
const setActiveListQuery = useCallback((nextQuery: string) => {
|
|
4094
4281
|
if (!currentRoute) {
|
|
4282
|
+
if (rootModeInfo.kind === "themes" && resolveCommandBarMode(nextQuery, availableCommands).kind !== "themes") {
|
|
4283
|
+
clearThemePreview(rootThemeBaseIdRef.current ?? stateRef.current.config.theme);
|
|
4284
|
+
rootThemeBaseIdRef.current = null;
|
|
4285
|
+
}
|
|
4095
4286
|
setRootQuery(nextQuery);
|
|
4096
4287
|
return;
|
|
4097
4288
|
}
|
|
@@ -4104,14 +4295,18 @@ export function CommandBar({
|
|
|
4104
4295
|
return route;
|
|
4105
4296
|
});
|
|
4106
4297
|
}
|
|
4107
|
-
}, [currentRoute, setRootQuery, updateTopRoute]);
|
|
4298
|
+
}, [availableCommands, clearThemePreview, currentRoute, rootModeInfo.kind, setRootQuery, updateTopRoute]);
|
|
4108
4299
|
|
|
4109
4300
|
const moveListSelection = useCallback((delta: number) => {
|
|
4110
4301
|
const listState = visibleListStateRef.current;
|
|
4111
4302
|
if (!listState || listState.results.length === 0 || delta === 0) return;
|
|
4112
|
-
const
|
|
4303
|
+
const nextIndex = clampListIndex(listState.selectedIdx + delta, listState.results.length);
|
|
4304
|
+
const nextListState = { ...listState, selectedIdx: nextIndex, hoveredIdx: null };
|
|
4305
|
+
visibleListStateRef.current = nextListState;
|
|
4306
|
+
previewThemeSelection(nextListState, nextIndex);
|
|
4307
|
+
|
|
4113
4308
|
if (!currentRouteRef.current) {
|
|
4114
|
-
setRootSelectedIdx(
|
|
4309
|
+
setRootSelectedIdx(nextIndex);
|
|
4115
4310
|
setRootHoveredIdx(null);
|
|
4116
4311
|
return;
|
|
4117
4312
|
}
|
|
@@ -4122,19 +4317,19 @@ export function CommandBar({
|
|
|
4122
4317
|
if (!top || (top.kind !== "mode" && top.kind !== "picker" && top.kind !== "pane-settings")) {
|
|
4123
4318
|
return current;
|
|
4124
4319
|
}
|
|
4125
|
-
const nextIndex = Math.max(0, Math.min(top.selectedIdx + delta, maxIndex));
|
|
4126
4320
|
next[next.length - 1] = { ...top, selectedIdx: nextIndex, hoveredIdx: null };
|
|
4127
4321
|
return next;
|
|
4128
4322
|
});
|
|
4129
|
-
}, []);
|
|
4323
|
+
}, [previewThemeSelection]);
|
|
4130
4324
|
|
|
4131
4325
|
const setHoveredIndex = useCallback((index: number | null) => {
|
|
4132
4326
|
if (!currentRoute) {
|
|
4133
|
-
setRootHoveredIdx(index);
|
|
4327
|
+
setRootHoveredIdx((current) => (current === index ? current : index));
|
|
4134
4328
|
return;
|
|
4135
4329
|
}
|
|
4136
4330
|
updateTopRoute((route) => {
|
|
4137
4331
|
if (route.kind === "mode" || route.kind === "picker" || route.kind === "pane-settings") {
|
|
4332
|
+
if (route.hoveredIdx === index) return route;
|
|
4138
4333
|
return { ...route, hoveredIdx: index };
|
|
4139
4334
|
}
|
|
4140
4335
|
return route;
|
|
@@ -4185,6 +4380,111 @@ export function CommandBar({
|
|
|
4185
4380
|
});
|
|
4186
4381
|
}, []);
|
|
4187
4382
|
|
|
4383
|
+
const alertWorkflowActive = isSetAlertWorkflow(currentRoute);
|
|
4384
|
+
const alertWorkflowSymbol = alertWorkflowActive
|
|
4385
|
+
? normalizeAlertWorkflowSymbol(currentRoute.values.symbol)
|
|
4386
|
+
: "";
|
|
4387
|
+
|
|
4388
|
+
useEffect(() => {
|
|
4389
|
+
if (!alertWorkflowActive) return;
|
|
4390
|
+
|
|
4391
|
+
const requestId = alertWorkflowQuoteRequestRef.current + 1;
|
|
4392
|
+
alertWorkflowQuoteRequestRef.current = requestId;
|
|
4393
|
+
|
|
4394
|
+
if (!alertWorkflowSymbol) {
|
|
4395
|
+
updateTopRoute((route) => {
|
|
4396
|
+
if (!isSetAlertWorkflow(route)) return route;
|
|
4397
|
+
return {
|
|
4398
|
+
...route,
|
|
4399
|
+
fields: updateAlertWorkflowFieldDescriptions(route.fields, {
|
|
4400
|
+
symbol: "Enter a symbol to validate it.",
|
|
4401
|
+
price: "Target fills from the current price after the symbol resolves.",
|
|
4402
|
+
}),
|
|
4403
|
+
payloadMeta: {
|
|
4404
|
+
...(route.payloadMeta ?? {}),
|
|
4405
|
+
alertQuoteSymbol: "",
|
|
4406
|
+
alertQuoteStatus: "idle",
|
|
4407
|
+
},
|
|
4408
|
+
};
|
|
4409
|
+
});
|
|
4410
|
+
return;
|
|
4411
|
+
}
|
|
4412
|
+
|
|
4413
|
+
updateTopRoute((route) => {
|
|
4414
|
+
if (!isSetAlertWorkflow(route)) return route;
|
|
4415
|
+
if (normalizeAlertWorkflowSymbol(route.values.symbol) !== alertWorkflowSymbol) return route;
|
|
4416
|
+
return {
|
|
4417
|
+
...route,
|
|
4418
|
+
fields: updateAlertWorkflowFieldDescriptions(route.fields, {
|
|
4419
|
+
symbol: `Checking ${alertWorkflowSymbol}...`,
|
|
4420
|
+
price: "Target fills from the current price after the symbol resolves.",
|
|
4421
|
+
}),
|
|
4422
|
+
payloadMeta: {
|
|
4423
|
+
...(route.payloadMeta ?? {}),
|
|
4424
|
+
alertQuoteSymbol: alertWorkflowSymbol,
|
|
4425
|
+
alertQuoteStatus: "checking",
|
|
4426
|
+
},
|
|
4427
|
+
};
|
|
4428
|
+
});
|
|
4429
|
+
|
|
4430
|
+
void dataProvider.getQuote(alertWorkflowSymbol, "")
|
|
4431
|
+
.then((quote) => {
|
|
4432
|
+
if (alertWorkflowQuoteRequestRef.current !== requestId) return;
|
|
4433
|
+
if (!quote || typeof quote.price !== "number" || !Number.isFinite(quote.price)) {
|
|
4434
|
+
throw new Error(`No quote found for "${alertWorkflowSymbol}".`);
|
|
4435
|
+
}
|
|
4436
|
+
|
|
4437
|
+
const quotePrice = formatAlertWorkflowPrice(quote.price);
|
|
4438
|
+
updateTopRoute((route) => {
|
|
4439
|
+
if (!isSetAlertWorkflow(route)) return route;
|
|
4440
|
+
if (normalizeAlertWorkflowSymbol(route.values.symbol) !== alertWorkflowSymbol) return route;
|
|
4441
|
+
|
|
4442
|
+
const currentPrice = coerceFieldString(route.values.price).trim();
|
|
4443
|
+
const previousAutoPrice = String(route.payloadMeta?.alertAutoPrice ?? "");
|
|
4444
|
+
const shouldPrefill = currentPrice.length === 0 || currentPrice === previousAutoPrice;
|
|
4445
|
+
const nextValues = shouldPrefill
|
|
4446
|
+
? { ...route.values, price: quotePrice }
|
|
4447
|
+
: route.values;
|
|
4448
|
+
|
|
4449
|
+
return {
|
|
4450
|
+
...route,
|
|
4451
|
+
values: nextValues,
|
|
4452
|
+
fields: updateAlertWorkflowFieldDescriptions(route.fields, {
|
|
4453
|
+
symbol: formatAlertWorkflowQuote(quote),
|
|
4454
|
+
price: `Current price ${quotePrice}; edit to set the target.`,
|
|
4455
|
+
}),
|
|
4456
|
+
payloadMeta: {
|
|
4457
|
+
...(route.payloadMeta ?? {}),
|
|
4458
|
+
alertQuoteSymbol: alertWorkflowSymbol,
|
|
4459
|
+
alertQuoteStatus: "valid",
|
|
4460
|
+
alertAutoPrice: quotePrice,
|
|
4461
|
+
},
|
|
4462
|
+
};
|
|
4463
|
+
});
|
|
4464
|
+
})
|
|
4465
|
+
.catch((error) => {
|
|
4466
|
+
if (alertWorkflowQuoteRequestRef.current !== requestId) return;
|
|
4467
|
+
const message = summarizeAlertWorkflowQuoteError(alertWorkflowSymbol, error);
|
|
4468
|
+
updateTopRoute((route) => {
|
|
4469
|
+
if (!isSetAlertWorkflow(route)) return route;
|
|
4470
|
+
if (normalizeAlertWorkflowSymbol(route.values.symbol) !== alertWorkflowSymbol) return route;
|
|
4471
|
+
return {
|
|
4472
|
+
...route,
|
|
4473
|
+
fields: updateAlertWorkflowFieldDescriptions(route.fields, {
|
|
4474
|
+
symbol: message,
|
|
4475
|
+
price: undefined,
|
|
4476
|
+
}),
|
|
4477
|
+
payloadMeta: {
|
|
4478
|
+
...(route.payloadMeta ?? {}),
|
|
4479
|
+
alertQuoteSymbol: alertWorkflowSymbol,
|
|
4480
|
+
alertQuoteStatus: "invalid",
|
|
4481
|
+
alertQuoteError: message,
|
|
4482
|
+
},
|
|
4483
|
+
};
|
|
4484
|
+
});
|
|
4485
|
+
});
|
|
4486
|
+
}, [alertWorkflowActive, alertWorkflowSymbol, dataProvider, updateTopRoute]);
|
|
4487
|
+
|
|
4188
4488
|
const moveWorkflowFocus = useCallback((delta: number) => {
|
|
4189
4489
|
if (currentRoute?.kind !== "workflow") return;
|
|
4190
4490
|
syncActiveWorkflowTextarea(currentRoute);
|
|
@@ -4602,6 +4902,18 @@ export function CommandBar({
|
|
|
4602
4902
|
const activeField = visibleFields.find((field) => field.id === currentRoute.activeFieldId) ?? visibleFields[0];
|
|
4603
4903
|
const activeTextarea = activeField?.type === "textarea";
|
|
4604
4904
|
|
|
4905
|
+
if (isPlainBackspace(event)) {
|
|
4906
|
+
const activeValue = activeField
|
|
4907
|
+
? getWorkflowFieldStringValue(activeField, currentRoute.values[activeField.id])
|
|
4908
|
+
: "";
|
|
4909
|
+
if (!activeField || !isWorkflowTextField(activeField) || activeValue.length === 0) {
|
|
4910
|
+
event.stopPropagation();
|
|
4911
|
+
event.preventDefault();
|
|
4912
|
+
popRoute();
|
|
4913
|
+
return;
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
|
|
4605
4917
|
if (event.name === "tab") {
|
|
4606
4918
|
event.stopPropagation();
|
|
4607
4919
|
event.preventDefault();
|
|
@@ -4641,11 +4953,19 @@ export function CommandBar({
|
|
|
4641
4953
|
return;
|
|
4642
4954
|
}
|
|
4643
4955
|
|
|
4644
|
-
if (
|
|
4956
|
+
if (
|
|
4957
|
+
event.name === "return"
|
|
4958
|
+
|| event.name === "enter"
|
|
4959
|
+
|| (nativePaneChrome && event.name === "space" && activeField?.type === "select")
|
|
4960
|
+
) {
|
|
4645
4961
|
if (!activeField) return;
|
|
4646
4962
|
if (activeField.type === "select" || activeField.type === "multi-select" || activeField.type === "ordered-multi-select" || activeField.type === "toggle") {
|
|
4647
4963
|
event.stopPropagation();
|
|
4648
4964
|
event.preventDefault();
|
|
4965
|
+
if (nativePaneChrome && activeField.type === "select") {
|
|
4966
|
+
openNativeSelect(workflowNativeSelectRefs.current.get(activeField.id));
|
|
4967
|
+
return;
|
|
4968
|
+
}
|
|
4649
4969
|
openWorkflowFieldPicker(currentRoute, activeField);
|
|
4650
4970
|
return;
|
|
4651
4971
|
}
|
|
@@ -4670,7 +4990,8 @@ export function CommandBar({
|
|
|
4670
4990
|
if (currentRoute.pickerId === "field-multi-select" && (event.name === "space" || event.sequence === " ")) {
|
|
4671
4991
|
event.stopPropagation();
|
|
4672
4992
|
event.preventDefault();
|
|
4673
|
-
const
|
|
4993
|
+
const listState = visibleListStateRef.current;
|
|
4994
|
+
const selected = listState?.results[listState.selectedIdx];
|
|
4674
4995
|
if (!selected) return;
|
|
4675
4996
|
handleMultiSelectToggle(selected.id);
|
|
4676
4997
|
return;
|
|
@@ -4720,7 +5041,8 @@ export function CommandBar({
|
|
|
4720
5041
|
return;
|
|
4721
5042
|
}
|
|
4722
5043
|
|
|
4723
|
-
|
|
5044
|
+
const activeListState = visibleListStateRef.current;
|
|
5045
|
+
if (!activeListState) return;
|
|
4724
5046
|
|
|
4725
5047
|
if (!currentRoute && event.name === "tab") {
|
|
4726
5048
|
if (acceptRootShortcutTab()) {
|
|
@@ -4754,7 +5076,7 @@ export function CommandBar({
|
|
|
4754
5076
|
if ((event.ctrl && event.name === "w") || (event.meta && (event.name === "h" || event.name === "u"))) {
|
|
4755
5077
|
event.stopPropagation();
|
|
4756
5078
|
event.preventDefault();
|
|
4757
|
-
const trimmed =
|
|
5079
|
+
const trimmed = activeListState.query.replace(/\s+$/, "");
|
|
4758
5080
|
const nextQuery = trimmed.replace(/[^\s]+$/, "").replace(/\s+$/, "");
|
|
4759
5081
|
setActiveListQuery(nextQuery);
|
|
4760
5082
|
return;
|
|
@@ -4765,7 +5087,7 @@ export function CommandBar({
|
|
|
4765
5087
|
if (pluginToggleMode && event.name === "space") {
|
|
4766
5088
|
event.stopPropagation();
|
|
4767
5089
|
event.preventDefault();
|
|
4768
|
-
const selected =
|
|
5090
|
+
const selected = activeListState.results[activeListState.selectedIdx];
|
|
4769
5091
|
if (selected?.pluginToggle) {
|
|
4770
5092
|
void selected.pluginToggle();
|
|
4771
5093
|
}
|
|
@@ -4806,21 +5128,20 @@ export function CommandBar({
|
|
|
4806
5128
|
handleMultiSelectToggle,
|
|
4807
5129
|
moveListSelection,
|
|
4808
5130
|
moveWorkflowFocus,
|
|
5131
|
+
nativePaneChrome,
|
|
4809
5132
|
openWorkflowFieldPicker,
|
|
4810
5133
|
popRoute,
|
|
4811
5134
|
renderer,
|
|
4812
5135
|
rootModeInfo.kind,
|
|
4813
|
-
routeListState,
|
|
4814
5136
|
setActiveListQuery,
|
|
4815
5137
|
updateWorkflowValue,
|
|
4816
5138
|
]);
|
|
4817
5139
|
|
|
4818
|
-
const barWidth =
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
const
|
|
4822
|
-
const
|
|
4823
|
-
const contentPadding = 3;
|
|
5140
|
+
const barWidth = nativePaneChrome
|
|
5141
|
+
? Math.max(46, Math.min(78, termWidth - 10, Math.floor(termWidth * 0.64)))
|
|
5142
|
+
: Math.max(42, Math.min(72, termWidth - 8, Math.floor(termWidth * 0.68)));
|
|
5143
|
+
const baseBodyHeight = Math.min(16, Math.max(9, termHeight - 9));
|
|
5144
|
+
const contentPadding = nativePaneChrome ? 1 : 3;
|
|
4824
5145
|
const paletteBg = commandBarBg();
|
|
4825
5146
|
const paletteHeadingText = commandBarHeadingText();
|
|
4826
5147
|
const paletteHoverBg = commandBarHoverBg();
|
|
@@ -4828,98 +5149,138 @@ export function CommandBar({
|
|
|
4828
5149
|
const paletteSelectedText = commandBarSelectedText();
|
|
4829
5150
|
const paletteText = commandBarText();
|
|
4830
5151
|
const paletteSubtleText = commandBarSubtleText();
|
|
4831
|
-
const
|
|
4832
|
-
const
|
|
4833
|
-
const labelWidth = Math.max(10, resultsInnerWidth - trailingWidth);
|
|
4834
|
-
const queryDisplayWidth = Math.max(8, barWidth - contentPadding * 2);
|
|
5152
|
+
const panelBg = nativePaneChrome ? commandBarPanelBg() : paletteBg;
|
|
5153
|
+
const inputBg = nativePaneChrome ? commandBarInputBg() : paletteBg;
|
|
4835
5154
|
const visibleListState = routeListState && (routeListState.kind === "root" || routeListState.kind === "mode" || routeListState.kind === "picker" || routeListState.kind === "pane-settings")
|
|
4836
5155
|
? routeListState
|
|
4837
5156
|
: null;
|
|
4838
5157
|
const showCustomMultiSelectPicker = currentRoute?.kind === "picker" && currentRoute.pickerId === "field-multi-select";
|
|
5158
|
+
const listRows = visibleListState ? buildListRows(visibleListState) : [];
|
|
5159
|
+
const nativeListRows = visibleListState ? buildNativeListRows(visibleListState, listRows) : [];
|
|
5160
|
+
const workflowBodyHeight = currentRoute?.kind === "workflow"
|
|
5161
|
+
? Math.min(
|
|
5162
|
+
Math.max(9, termHeight - (nativePaneChrome ? 7 : 9)),
|
|
5163
|
+
Math.max(7, estimateWorkflowBodyRows(currentRoute)),
|
|
5164
|
+
)
|
|
5165
|
+
: baseBodyHeight;
|
|
5166
|
+
const bodyHeight = currentRoute?.kind === "workflow" ? workflowBodyHeight : baseBodyHeight;
|
|
5167
|
+
const listBodyHeight = baseBodyHeight;
|
|
5168
|
+
const nativePanelPaddingColumns = nativePaneChrome
|
|
5169
|
+
? Math.ceil((NATIVE_COMMAND_BAR_PADDING_X_PX * 2) / Math.max(1, cellWidthPx))
|
|
5170
|
+
: 0;
|
|
5171
|
+
const nativePanelPaddingRows = nativePaneChrome
|
|
5172
|
+
? Math.ceil((NATIVE_COMMAND_BAR_PADDING_Y_PX * 2) / Math.max(1, cellHeightPx))
|
|
5173
|
+
: 0;
|
|
5174
|
+
const nativeBodyChromeRows = (currentRoute?.kind === "workflow"
|
|
5175
|
+
|| currentRoute?.kind === "confirm"
|
|
5176
|
+
|| showCustomMultiSelectPicker)
|
|
5177
|
+
? 1
|
|
5178
|
+
: 3;
|
|
5179
|
+
const barHeight = nativePaneChrome
|
|
5180
|
+
? bodyHeight + nativeBodyChromeRows + nativePanelPaddingRows
|
|
5181
|
+
: bodyHeight + 7;
|
|
5182
|
+
const barLeft = Math.max(4, Math.floor((termWidth - barWidth) / 2));
|
|
5183
|
+
const barTop = Math.max(1, Math.floor((termHeight - barHeight) / 2));
|
|
5184
|
+
const resultsInnerWidth = Math.max(12, barWidth - nativePanelPaddingColumns - contentPadding * 2);
|
|
5185
|
+
const trailingWidth = Math.max(8, Math.min(12, Math.floor(resultsInnerWidth * 0.18)));
|
|
5186
|
+
const labelWidth = Math.max(10, resultsInnerWidth - trailingWidth);
|
|
5187
|
+
const queryDisplayWidth = Math.max(8, resultsInnerWidth);
|
|
5188
|
+
const nativeSelectedRowIndex = nativePaneChrome && visibleListState
|
|
5189
|
+
? nativeListRows.findIndex((row) => row.kind === "item" && row.globalIdx === visibleListState.selectedIdx)
|
|
5190
|
+
: -1;
|
|
4839
5191
|
const bodySlotKey = showCustomMultiSelectPicker
|
|
4840
5192
|
? "picker:field-multi-select"
|
|
4841
5193
|
: currentRoute?.kind === "picker"
|
|
4842
5194
|
? `picker:${currentRoute.pickerId}`
|
|
4843
5195
|
: currentRoute?.kind ?? "root";
|
|
4844
5196
|
|
|
5197
|
+
useEffect(() => {
|
|
5198
|
+
if (!nativePaneChrome || nativeSelectedRowIndex < 0) return;
|
|
5199
|
+
const scrollBox = nativeListScrollRef.current;
|
|
5200
|
+
if (!scrollBox) return;
|
|
5201
|
+
const viewportHeight = Math.max(1, scrollBox.viewport?.height ?? listBodyHeight);
|
|
5202
|
+
if (nativeSelectedRowIndex < scrollBox.scrollTop) {
|
|
5203
|
+
scrollBox.scrollTo(nativeSelectedRowIndex);
|
|
5204
|
+
} else if (nativeSelectedRowIndex >= scrollBox.scrollTop + viewportHeight) {
|
|
5205
|
+
scrollBox.scrollTo(nativeSelectedRowIndex - viewportHeight + 1);
|
|
5206
|
+
}
|
|
5207
|
+
}, [listBodyHeight, nativePaneChrome, nativeSelectedRowIndex, visibleListState?.kind, visibleListState?.query]);
|
|
5208
|
+
|
|
5209
|
+
useEffect(() => {
|
|
5210
|
+
if (!nativePaneChrome || currentRoute?.kind !== "workflow") return;
|
|
5211
|
+
const scrollBox = workflowScrollRef.current;
|
|
5212
|
+
if (!scrollBox) return;
|
|
5213
|
+
const visibleFields = getVisibleWorkflowFields(currentRoute.fields, currentRoute.values);
|
|
5214
|
+
const activeIndex = visibleFields.findIndex((field) => field.id === currentRoute.activeFieldId);
|
|
5215
|
+
if (activeIndex < 0) return;
|
|
5216
|
+
|
|
5217
|
+
const estimatedFieldHeight = 4;
|
|
5218
|
+
const fieldTop = activeIndex * estimatedFieldHeight;
|
|
5219
|
+
const fieldBottom = fieldTop + estimatedFieldHeight;
|
|
5220
|
+
const viewportHeight = Math.max(1, scrollBox.viewport?.height ?? bodyHeight);
|
|
5221
|
+
if (fieldTop < scrollBox.scrollTop) {
|
|
5222
|
+
scrollBox.scrollTo(fieldTop);
|
|
5223
|
+
} else if (fieldBottom > scrollBox.scrollTop + viewportHeight) {
|
|
5224
|
+
scrollBox.scrollTo(fieldBottom - viewportHeight);
|
|
5225
|
+
}
|
|
5226
|
+
}, [bodyHeight, currentRoute, nativePaneChrome]);
|
|
5227
|
+
|
|
4845
5228
|
const renderListBody = () => {
|
|
4846
5229
|
if (!visibleListState) return null;
|
|
4847
5230
|
|
|
4848
|
-
const allRows
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
| { kind: "spinner"; id: string; label: string }
|
|
4854
|
-
| { kind: "filler"; id: string }
|
|
4855
|
-
> = [];
|
|
4856
|
-
const sections = buildSections(visibleListState.results);
|
|
4857
|
-
let globalIdx = 0;
|
|
4858
|
-
sections.forEach((section, sectionIndex) => {
|
|
4859
|
-
if (sectionIndex > 0) {
|
|
4860
|
-
allRows.push({ kind: "spacer", id: `spacer:${sectionIndex}:${section.category}` });
|
|
4861
|
-
}
|
|
4862
|
-
allRows.push({ kind: "heading", id: `heading:${sectionIndex}:${section.category}`, label: section.category });
|
|
4863
|
-
for (const item of section.items) {
|
|
4864
|
-
allRows.push({ kind: "item", item, globalIdx });
|
|
4865
|
-
globalIdx += 1;
|
|
4866
|
-
}
|
|
4867
|
-
});
|
|
4868
|
-
|
|
4869
|
-
let visibleRows: typeof allRows;
|
|
4870
|
-
if (visibleListState.searching && allRows.length === 0) {
|
|
5231
|
+
const allRows = listRows;
|
|
5232
|
+
let visibleRows: CommandBarListRow[];
|
|
5233
|
+
if (nativePaneChrome) {
|
|
5234
|
+
visibleRows = nativeListRows;
|
|
5235
|
+
} else if (visibleListState.searching && allRows.length === 0) {
|
|
4871
5236
|
visibleRows = [{ kind: "spinner", id: "searching", label: "Searching…" }];
|
|
4872
5237
|
} else if (allRows.length === 0) {
|
|
4873
5238
|
visibleRows = [{ kind: "message", id: "empty", label: visibleListState.emptyLabel }];
|
|
4874
5239
|
} else {
|
|
4875
5240
|
const selectedRowIdx = allRows.findIndex((row) => row.kind === "item" && row.globalIdx === visibleListState.selectedIdx);
|
|
4876
|
-
const halfWindow = Math.floor(
|
|
4877
|
-
let windowStart = Math.max(0, Math.min(selectedRowIdx - halfWindow, allRows.length -
|
|
5241
|
+
const halfWindow = Math.floor(listBodyHeight / 2);
|
|
5242
|
+
let windowStart = Math.max(0, Math.min(selectedRowIdx - halfWindow, allRows.length - listBodyHeight));
|
|
4878
5243
|
if (windowStart < 0) windowStart = 0;
|
|
4879
|
-
visibleRows = allRows.slice(windowStart, windowStart +
|
|
5244
|
+
visibleRows = allRows.slice(windowStart, windowStart + listBodyHeight);
|
|
4880
5245
|
if (visibleListState.searching) {
|
|
4881
|
-
if (visibleRows.length >=
|
|
4882
|
-
visibleRows = visibleRows.slice(0,
|
|
5246
|
+
if (visibleRows.length >= listBodyHeight) {
|
|
5247
|
+
visibleRows = visibleRows.slice(0, listBodyHeight - 1);
|
|
4883
5248
|
}
|
|
4884
5249
|
visibleRows.push({ kind: "spinner", id: "searching", label: "Searching…" });
|
|
4885
5250
|
}
|
|
4886
5251
|
}
|
|
4887
5252
|
|
|
4888
|
-
while (visibleRows.length <
|
|
5253
|
+
while (!nativePaneChrome && visibleRows.length < listBodyHeight) {
|
|
4889
5254
|
visibleRows.push({ kind: "filler", id: `filler:${visibleRows.length}` });
|
|
4890
5255
|
}
|
|
4891
5256
|
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
flexDirection="column"
|
|
4895
|
-
height={bodyHeight}
|
|
4896
|
-
onMouseScroll={handleListScroll}
|
|
4897
|
-
>
|
|
5257
|
+
const renderedRows = (
|
|
5258
|
+
<>
|
|
4898
5259
|
{visibleRows.map((row) => {
|
|
4899
5260
|
if (row.kind === "filler" || row.kind === "spacer") {
|
|
4900
|
-
return <
|
|
5261
|
+
return <Box key={row.id} height={1} />;
|
|
4901
5262
|
}
|
|
4902
5263
|
if (row.kind === "spinner") {
|
|
4903
5264
|
return (
|
|
4904
|
-
<
|
|
5265
|
+
<Box key={row.id} height={1} paddingX={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: handleListScroll } : {})}>
|
|
4905
5266
|
<Spinner label={row.label} />
|
|
4906
|
-
</
|
|
5267
|
+
</Box>
|
|
4907
5268
|
);
|
|
4908
5269
|
}
|
|
4909
5270
|
if (row.kind === "message") {
|
|
4910
5271
|
return (
|
|
4911
|
-
<
|
|
4912
|
-
<
|
|
4913
|
-
</
|
|
5272
|
+
<Box key={row.id} height={1} paddingX={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: handleListScroll } : {})}>
|
|
5273
|
+
<Text fg={paletteText}>{truncateText(row.label, queryDisplayWidth)}</Text>
|
|
5274
|
+
</Box>
|
|
4914
5275
|
);
|
|
4915
5276
|
}
|
|
4916
5277
|
if (row.kind === "heading") {
|
|
4917
5278
|
return (
|
|
4918
|
-
<
|
|
4919
|
-
<
|
|
4920
|
-
{truncateText(row.label,
|
|
4921
|
-
</
|
|
4922
|
-
</
|
|
5279
|
+
<Box key={row.id} height={1} paddingX={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: handleListScroll } : {})}>
|
|
5280
|
+
<Text attributes={TextAttributes.BOLD} fg={paletteHeadingText}>
|
|
5281
|
+
{truncateText(row.label, queryDisplayWidth)}
|
|
5282
|
+
</Text>
|
|
5283
|
+
</Box>
|
|
4923
5284
|
);
|
|
4924
5285
|
}
|
|
4925
5286
|
|
|
@@ -4929,14 +5290,19 @@ export function CommandBar({
|
|
|
4929
5290
|
const label = truncateText(presentation.label, labelWidth);
|
|
4930
5291
|
const trailing = truncateText(presentation.trailing, trailingWidth);
|
|
4931
5292
|
return (
|
|
4932
|
-
<
|
|
5293
|
+
<Box
|
|
4933
5294
|
key={row.item.id}
|
|
4934
5295
|
flexDirection="row"
|
|
4935
5296
|
height={1}
|
|
4936
5297
|
paddingX={contentPadding}
|
|
4937
|
-
backgroundColor={isSelected
|
|
5298
|
+
backgroundColor={isSelected
|
|
5299
|
+
? paletteSelectedBg
|
|
5300
|
+
: isHovered
|
|
5301
|
+
? paletteHoverBg
|
|
5302
|
+
: (nativePaneChrome ? panelBg : paletteBg)}
|
|
4938
5303
|
onMouseMove={() => setHoveredIndex(row.globalIdx)}
|
|
4939
|
-
|
|
5304
|
+
onMouseOut={() => setHoveredIndex(null)}
|
|
5305
|
+
{...(!nativePaneChrome ? { onMouseScroll: handleListScroll } : {})}
|
|
4940
5306
|
onMouseDown={(event: any) => {
|
|
4941
5307
|
event.stopPropagation?.();
|
|
4942
5308
|
event.preventDefault?.();
|
|
@@ -4952,64 +5318,97 @@ export function CommandBar({
|
|
|
4952
5318
|
}
|
|
4953
5319
|
activateListSelection({ item: row.item });
|
|
4954
5320
|
}}
|
|
5321
|
+
data-command-bar-row-selected={nativePaneChrome && isSelected ? "true" : undefined}
|
|
5322
|
+
style={nativePaneChrome ? { borderRadius: 6 } : undefined}
|
|
4955
5323
|
>
|
|
4956
|
-
<
|
|
4957
|
-
<
|
|
5324
|
+
<Box width={labelWidth}>
|
|
5325
|
+
<Text fg={isSelected ? paletteSelectedText : presentation.primaryMuted ? paletteSubtleText : paletteText}>
|
|
4958
5326
|
{label}
|
|
4959
|
-
</
|
|
4960
|
-
</
|
|
4961
|
-
<
|
|
4962
|
-
<
|
|
4963
|
-
</
|
|
4964
|
-
</
|
|
5327
|
+
</Text>
|
|
5328
|
+
</Box>
|
|
5329
|
+
<Box width={trailingWidth}>
|
|
5330
|
+
<Text fg={isSelected ? paletteSelectedText : paletteSubtleText}>{trailing}</Text>
|
|
5331
|
+
</Box>
|
|
5332
|
+
</Box>
|
|
4965
5333
|
);
|
|
4966
5334
|
})}
|
|
4967
|
-
|
|
5335
|
+
</>
|
|
5336
|
+
);
|
|
5337
|
+
|
|
5338
|
+
if (nativePaneChrome) {
|
|
5339
|
+
return (
|
|
5340
|
+
<ScrollBox
|
|
5341
|
+
ref={nativeListScrollRef}
|
|
5342
|
+
flexDirection="column"
|
|
5343
|
+
height={listBodyHeight}
|
|
5344
|
+
scrollY
|
|
5345
|
+
>
|
|
5346
|
+
{renderedRows}
|
|
5347
|
+
</ScrollBox>
|
|
5348
|
+
);
|
|
5349
|
+
}
|
|
5350
|
+
|
|
5351
|
+
return (
|
|
5352
|
+
<Box
|
|
5353
|
+
flexDirection="column"
|
|
5354
|
+
height={listBodyHeight}
|
|
5355
|
+
onMouseScroll={handleListScroll}
|
|
5356
|
+
>
|
|
5357
|
+
{renderedRows}
|
|
5358
|
+
</Box>
|
|
4968
5359
|
);
|
|
4969
5360
|
};
|
|
4970
5361
|
|
|
4971
5362
|
const renderWorkflowBody = () => {
|
|
4972
5363
|
if (currentRoute?.kind !== "workflow") return null;
|
|
4973
5364
|
const visibleFields = getVisibleWorkflowFields(currentRoute.fields, currentRoute.values);
|
|
4974
|
-
|
|
4975
|
-
|
|
5365
|
+
const workflowContent = (
|
|
5366
|
+
<>
|
|
4976
5367
|
{currentRoute.subtitle && (
|
|
4977
|
-
<
|
|
4978
|
-
<
|
|
4979
|
-
</
|
|
5368
|
+
<Box height={1}>
|
|
5369
|
+
<Text fg={paletteSubtleText}>{truncateText(currentRoute.subtitle, queryDisplayWidth)}</Text>
|
|
5370
|
+
</Box>
|
|
4980
5371
|
)}
|
|
4981
5372
|
{currentRoute.description?.map((line, index) => (
|
|
4982
|
-
<
|
|
4983
|
-
<
|
|
4984
|
-
</
|
|
5373
|
+
<Box key={`workflow-desc:${index}`} height={1}>
|
|
5374
|
+
<Text fg={paletteSubtleText}>{truncateText(line, queryDisplayWidth)}</Text>
|
|
5375
|
+
</Box>
|
|
4985
5376
|
))}
|
|
4986
|
-
{currentRoute.subtitle || (currentRoute.description?.length ?? 0) > 0 ? <
|
|
4987
|
-
{visibleFields.map((field) => {
|
|
5377
|
+
{currentRoute.subtitle || (currentRoute.description?.length ?? 0) > 0 ? <Box height={1} /> : null}
|
|
5378
|
+
{visibleFields.map((field, fieldIndex) => {
|
|
4988
5379
|
const active = field.id === currentRoute.activeFieldId;
|
|
5380
|
+
const isLastField = fieldIndex === visibleFields.length - 1;
|
|
4989
5381
|
const value = currentRoute.values[field.id];
|
|
4990
5382
|
const borderColor = active ? paletteSelectedBg : paletteBg;
|
|
5383
|
+
const fieldBg = nativePaneChrome ? "transparent" : active ? inputBg : panelBg;
|
|
5384
|
+
const useNativeSelect = nativePaneChrome && field.type === "select";
|
|
5385
|
+
const fieldDescription = getWorkflowFieldDescription(field, active);
|
|
4991
5386
|
return (
|
|
4992
|
-
<
|
|
5387
|
+
<Box
|
|
4993
5388
|
key={field.id}
|
|
4994
5389
|
flexDirection="column"
|
|
4995
|
-
marginBottom
|
|
4996
|
-
backgroundColor={
|
|
5390
|
+
{...(!nativePaneChrome ? { marginBottom: isLastField ? 0 : 1 } : {})}
|
|
5391
|
+
backgroundColor={fieldBg}
|
|
4997
5392
|
onMouseDown={(event: any) => {
|
|
4998
5393
|
event.stopPropagation?.();
|
|
4999
5394
|
syncActiveWorkflowTextarea(currentRoute);
|
|
5000
5395
|
updateTopRoute((route) => route.kind === "workflow"
|
|
5001
5396
|
? { ...route, activeFieldId: field.id, error: null }
|
|
5002
5397
|
: route);
|
|
5003
|
-
if (!isWorkflowTextField(field)) {
|
|
5398
|
+
if (!isWorkflowTextField(field) && !useNativeSelect) {
|
|
5004
5399
|
openWorkflowFieldPicker(currentRoute, field);
|
|
5005
5400
|
}
|
|
5006
5401
|
}}
|
|
5402
|
+
style={nativePaneChrome ? {
|
|
5403
|
+
marginBottom: isLastField ? 8 : 10,
|
|
5404
|
+
paddingBlock: 3,
|
|
5405
|
+
} : undefined}
|
|
5007
5406
|
>
|
|
5008
|
-
<
|
|
5009
|
-
<
|
|
5407
|
+
<Box height={1}>
|
|
5408
|
+
<Text fg={active ? paletteText : paletteSubtleText} attributes={active ? TextAttributes.BOLD : 0}>
|
|
5010
5409
|
{field.label}
|
|
5011
|
-
</
|
|
5012
|
-
</
|
|
5410
|
+
</Text>
|
|
5411
|
+
</Box>
|
|
5013
5412
|
{isWorkflowTextField(field) ? (
|
|
5014
5413
|
field.type === "number" ? (
|
|
5015
5414
|
<NumberField
|
|
@@ -5017,6 +5416,8 @@ export function CommandBar({
|
|
|
5017
5416
|
value={coerceFieldString(value)}
|
|
5018
5417
|
placeholder={field.placeholder}
|
|
5019
5418
|
focused={active && !currentRoute.pending}
|
|
5419
|
+
variant="default"
|
|
5420
|
+
backgroundColor={nativePaneChrome ? inputBg : fieldBg}
|
|
5020
5421
|
onChange={(nextValue) => updateWorkflowValue(field.id, nextValue)}
|
|
5021
5422
|
onSubmit={() => {
|
|
5022
5423
|
const index = visibleFields.findIndex((entry) => entry.id === field.id);
|
|
@@ -5028,15 +5429,20 @@ export function CommandBar({
|
|
|
5028
5429
|
}}
|
|
5029
5430
|
/>
|
|
5030
5431
|
) : field.type === "textarea" ? (
|
|
5031
|
-
<
|
|
5432
|
+
<Box
|
|
5032
5433
|
minHeight={6}
|
|
5033
5434
|
height={6}
|
|
5034
|
-
border
|
|
5435
|
+
border={!nativePaneChrome}
|
|
5035
5436
|
borderColor={active ? paletteSelectedBg : paletteBg}
|
|
5036
|
-
backgroundColor={
|
|
5437
|
+
backgroundColor={nativePaneChrome ? inputBg : fieldBg}
|
|
5438
|
+
style={nativePaneChrome ? {
|
|
5439
|
+
border: `1px solid ${active ? colors.borderFocused : colors.border}`,
|
|
5440
|
+
borderRadius: 6,
|
|
5441
|
+
overflow: "hidden",
|
|
5442
|
+
} : undefined}
|
|
5037
5443
|
>
|
|
5038
5444
|
{active ? (
|
|
5039
|
-
<
|
|
5445
|
+
<Textarea
|
|
5040
5446
|
key={field.id}
|
|
5041
5447
|
ref={getInputRef(workflowInputRefs.current, field.id) as RefObject<TextareaRenderable | null>}
|
|
5042
5448
|
initialValue={coerceFieldString(value)}
|
|
@@ -5044,27 +5450,27 @@ export function CommandBar({
|
|
|
5044
5450
|
focused={!currentRoute.pending}
|
|
5045
5451
|
textColor={paletteText}
|
|
5046
5452
|
placeholderColor={paletteSubtleText}
|
|
5047
|
-
backgroundColor={colors.panel}
|
|
5453
|
+
backgroundColor={nativePaneChrome ? inputBg : colors.panel}
|
|
5048
5454
|
flexGrow={1}
|
|
5049
5455
|
wrapText
|
|
5050
5456
|
/>
|
|
5051
5457
|
) : (
|
|
5052
|
-
<
|
|
5458
|
+
<Box flexDirection="column" paddingX={1} paddingY={0}>
|
|
5053
5459
|
{(() => {
|
|
5054
5460
|
const preview = coerceFieldString(value).trim();
|
|
5055
5461
|
const lines = (preview || field.placeholder || "Unset")
|
|
5056
5462
|
.split("\n")
|
|
5057
|
-
.flatMap((line) => line.match(new RegExp(`.{1,${Math.max(1,
|
|
5463
|
+
.flatMap((line) => line.match(new RegExp(`.{1,${Math.max(1, queryDisplayWidth - 8)}}`, "g")) ?? [""])
|
|
5058
5464
|
.slice(0, 4);
|
|
5059
5465
|
return lines.map((line, index) => (
|
|
5060
|
-
<
|
|
5061
|
-
<
|
|
5062
|
-
</
|
|
5466
|
+
<Box key={`${field.id}:preview:${index}`} height={1}>
|
|
5467
|
+
<Text fg={preview ? paletteText : paletteSubtleText}>{line || " "}</Text>
|
|
5468
|
+
</Box>
|
|
5063
5469
|
));
|
|
5064
5470
|
})()}
|
|
5065
|
-
</
|
|
5471
|
+
</Box>
|
|
5066
5472
|
)}
|
|
5067
|
-
</
|
|
5473
|
+
</Box>
|
|
5068
5474
|
) : (
|
|
5069
5475
|
<TextField
|
|
5070
5476
|
inputRef={getInputRef(workflowInputRefs.current, field.id) as RefObject<InputRenderable | null>}
|
|
@@ -5072,6 +5478,8 @@ export function CommandBar({
|
|
|
5072
5478
|
value={coerceFieldString(value)}
|
|
5073
5479
|
placeholder={field.placeholder}
|
|
5074
5480
|
focused={active && !currentRoute.pending}
|
|
5481
|
+
variant="default"
|
|
5482
|
+
backgroundColor={nativePaneChrome ? inputBg : fieldBg}
|
|
5075
5483
|
onChange={(nextValue) => updateWorkflowValue(field.id, nextValue)}
|
|
5076
5484
|
onSubmit={() => {
|
|
5077
5485
|
const index = visibleFields.findIndex((entry) => entry.id === field.id);
|
|
@@ -5083,83 +5491,116 @@ export function CommandBar({
|
|
|
5083
5491
|
}}
|
|
5084
5492
|
/>
|
|
5085
5493
|
)
|
|
5494
|
+
) : useNativeSelect ? (
|
|
5495
|
+
<NativeSelect
|
|
5496
|
+
value={coerceFieldString(value)}
|
|
5497
|
+
options={field.options}
|
|
5498
|
+
width="100%"
|
|
5499
|
+
selectRef={(element) => setWorkflowNativeSelectRef(field.id, element)}
|
|
5500
|
+
onFocus={() => {
|
|
5501
|
+
updateTopRoute((route) => route.kind === "workflow"
|
|
5502
|
+
? { ...route, activeFieldId: field.id, error: null }
|
|
5503
|
+
: route);
|
|
5504
|
+
}}
|
|
5505
|
+
onChange={(nextValue) => updateWorkflowValue(field.id, nextValue)}
|
|
5506
|
+
/>
|
|
5086
5507
|
) : (
|
|
5087
|
-
<
|
|
5508
|
+
<Box
|
|
5088
5509
|
height={1}
|
|
5089
|
-
backgroundColor={borderColor}
|
|
5510
|
+
backgroundColor={nativePaneChrome ? "transparent" : borderColor}
|
|
5090
5511
|
onMouseDown={(event: any) => {
|
|
5091
5512
|
event.stopPropagation?.();
|
|
5092
5513
|
openWorkflowFieldPicker(currentRoute, field);
|
|
5093
5514
|
}}
|
|
5515
|
+
style={nativePaneChrome ? { borderRadius: 4 } : undefined}
|
|
5094
5516
|
>
|
|
5095
|
-
<
|
|
5096
|
-
{truncateText(summarizeWorkflowFieldValue(field, value),
|
|
5097
|
-
</
|
|
5098
|
-
</
|
|
5517
|
+
<Text fg={active ? paletteText : paletteSubtleText}>
|
|
5518
|
+
{truncateText(summarizeWorkflowFieldValue(field, value), queryDisplayWidth)}
|
|
5519
|
+
</Text>
|
|
5520
|
+
</Box>
|
|
5099
5521
|
)}
|
|
5100
|
-
{
|
|
5101
|
-
<
|
|
5102
|
-
<
|
|
5103
|
-
{truncateText(
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
: field.description,
|
|
5107
|
-
barWidth - contentPadding * 2,
|
|
5108
|
-
)}
|
|
5109
|
-
</text>
|
|
5110
|
-
</box>
|
|
5522
|
+
{fieldDescription && (
|
|
5523
|
+
<Box height={1}>
|
|
5524
|
+
<Text fg={paletteSubtleText}>
|
|
5525
|
+
{truncateText(fieldDescription, queryDisplayWidth)}
|
|
5526
|
+
</Text>
|
|
5527
|
+
</Box>
|
|
5111
5528
|
)}
|
|
5112
|
-
</
|
|
5529
|
+
</Box>
|
|
5113
5530
|
);
|
|
5114
5531
|
})}
|
|
5115
5532
|
{currentRoute.error && (
|
|
5116
|
-
<
|
|
5117
|
-
<
|
|
5118
|
-
</
|
|
5533
|
+
<Box height={1}>
|
|
5534
|
+
<Text fg={colors.negative}>{truncateText(currentRoute.error, queryDisplayWidth)}</Text>
|
|
5535
|
+
</Box>
|
|
5119
5536
|
)}
|
|
5120
5537
|
{currentRoute.pendingLabel && currentRoute.pending && (
|
|
5121
|
-
<
|
|
5538
|
+
<Box height={1}>
|
|
5122
5539
|
<Spinner label={currentRoute.pendingLabel} />
|
|
5123
|
-
</
|
|
5540
|
+
</Box>
|
|
5124
5541
|
)}
|
|
5125
|
-
<
|
|
5126
|
-
<
|
|
5542
|
+
{!nativePaneChrome && <Box flexGrow={1} />}
|
|
5543
|
+
<Box flexDirection="row" gap={1} justifyContent={visibleFields.some((field) => field.type === "textarea") ? "flex-end" : "flex-start"}>
|
|
5127
5544
|
<Button label={currentRoute.submitLabel} variant="primary" onPress={() => { void submitWorkflowRoute(currentRoute); }} disabled={currentRoute.pending} />
|
|
5128
|
-
</
|
|
5129
|
-
|
|
5545
|
+
</Box>
|
|
5546
|
+
</>
|
|
5547
|
+
);
|
|
5548
|
+
|
|
5549
|
+
if (nativePaneChrome) {
|
|
5550
|
+
return (
|
|
5551
|
+
<ScrollBox
|
|
5552
|
+
ref={workflowScrollRef}
|
|
5553
|
+
height={bodyHeight}
|
|
5554
|
+
scrollY
|
|
5555
|
+
style={{ overflowX: "hidden", paddingRight: 4 }}
|
|
5556
|
+
>
|
|
5557
|
+
<Box
|
|
5558
|
+
flexDirection="column"
|
|
5559
|
+
paddingX={contentPadding}
|
|
5560
|
+
>
|
|
5561
|
+
{workflowContent}
|
|
5562
|
+
</Box>
|
|
5563
|
+
</ScrollBox>
|
|
5564
|
+
);
|
|
5565
|
+
}
|
|
5566
|
+
|
|
5567
|
+
return (
|
|
5568
|
+
<Box flexDirection="column" height={bodyHeight} paddingX={contentPadding}>
|
|
5569
|
+
{workflowContent}
|
|
5570
|
+
</Box>
|
|
5130
5571
|
);
|
|
5131
5572
|
};
|
|
5132
5573
|
|
|
5133
5574
|
const renderConfirmBody = () => {
|
|
5134
5575
|
if (currentRoute?.kind !== "confirm") return null;
|
|
5135
5576
|
return (
|
|
5136
|
-
<
|
|
5577
|
+
<Box flexDirection="column" height={bodyHeight} paddingX={contentPadding}>
|
|
5137
5578
|
{currentRoute.body.map((line, index) => (
|
|
5138
|
-
<
|
|
5139
|
-
<
|
|
5140
|
-
</
|
|
5579
|
+
<Box key={`confirm:${index}`} height={1}>
|
|
5580
|
+
<Text fg={paletteText}>{truncateText(line, queryDisplayWidth)}</Text>
|
|
5581
|
+
</Box>
|
|
5141
5582
|
))}
|
|
5142
|
-
<
|
|
5583
|
+
<Box height={1} />
|
|
5143
5584
|
{currentRoute.error && (
|
|
5144
|
-
<
|
|
5145
|
-
<
|
|
5146
|
-
</
|
|
5585
|
+
<Box height={1}>
|
|
5586
|
+
<Text fg={colors.negative}>{truncateText(currentRoute.error, queryDisplayWidth)}</Text>
|
|
5587
|
+
</Box>
|
|
5147
5588
|
)}
|
|
5148
5589
|
{currentRoute.pending && (
|
|
5149
|
-
<
|
|
5590
|
+
<Box height={1}>
|
|
5150
5591
|
<Spinner label="Working…" />
|
|
5151
|
-
</
|
|
5592
|
+
</Box>
|
|
5152
5593
|
)}
|
|
5153
|
-
<
|
|
5154
|
-
<
|
|
5594
|
+
<Box flexGrow={1} />
|
|
5595
|
+
<Box flexDirection="row" gap={1}>
|
|
5155
5596
|
<Button
|
|
5156
5597
|
label={currentRoute.confirmLabel}
|
|
5157
5598
|
variant={currentRoute.tone === "danger" ? "danger" : "primary"}
|
|
5158
5599
|
onPress={() => { void confirmCurrentRoute(); }}
|
|
5159
5600
|
disabled={currentRoute.pending}
|
|
5160
5601
|
/>
|
|
5161
|
-
</
|
|
5162
|
-
</
|
|
5602
|
+
</Box>
|
|
5603
|
+
</Box>
|
|
5163
5604
|
);
|
|
5164
5605
|
};
|
|
5165
5606
|
|
|
@@ -5177,7 +5618,7 @@ export function CommandBar({
|
|
|
5177
5618
|
? 0
|
|
5178
5619
|
: Math.max(0, Math.min(currentRoute.selectedIdx, items.length - 1));
|
|
5179
5620
|
return (
|
|
5180
|
-
<
|
|
5621
|
+
<Box flexDirection="column" height={bodyHeight} paddingX={contentPadding}>
|
|
5181
5622
|
<ToggleList
|
|
5182
5623
|
items={items}
|
|
5183
5624
|
selectedIdx={selectedIdx}
|
|
@@ -5190,113 +5631,149 @@ export function CommandBar({
|
|
|
5190
5631
|
: route);
|
|
5191
5632
|
}}
|
|
5192
5633
|
onToggle={(id) => handleMultiSelectToggle(id)}
|
|
5193
|
-
bgColor={paletteBg}
|
|
5634
|
+
bgColor={nativePaneChrome ? panelBg : paletteBg}
|
|
5194
5635
|
/>
|
|
5195
|
-
<
|
|
5636
|
+
<Box flexDirection="row" gap={1}>
|
|
5196
5637
|
<Button label="Done" variant="primary" onPress={commitMultiSelectPicker} />
|
|
5197
|
-
</
|
|
5198
|
-
</
|
|
5638
|
+
</Box>
|
|
5639
|
+
</Box>
|
|
5199
5640
|
);
|
|
5200
5641
|
};
|
|
5201
5642
|
|
|
5202
5643
|
return (
|
|
5203
|
-
<
|
|
5644
|
+
<Box
|
|
5204
5645
|
position="absolute"
|
|
5205
5646
|
top={0}
|
|
5206
5647
|
left={0}
|
|
5207
5648
|
width={termWidth}
|
|
5208
5649
|
height={termHeight}
|
|
5209
|
-
zIndex={100}
|
|
5650
|
+
zIndex={nativePaneChrome ? COMMAND_BAR_OVERLAY_Z_INDEX : 100}
|
|
5210
5651
|
onMouseDown={(event: any) => {
|
|
5211
5652
|
event.stopPropagation?.();
|
|
5212
5653
|
event.preventDefault?.();
|
|
5213
5654
|
closeAll();
|
|
5214
5655
|
}}
|
|
5215
5656
|
>
|
|
5216
|
-
<
|
|
5657
|
+
<Box
|
|
5217
5658
|
position="absolute"
|
|
5218
5659
|
top={barTop}
|
|
5219
5660
|
left={barLeft}
|
|
5220
5661
|
width={barWidth}
|
|
5221
5662
|
height={barHeight}
|
|
5222
5663
|
flexDirection="column"
|
|
5223
|
-
backgroundColor={
|
|
5224
|
-
zIndex={101}
|
|
5664
|
+
backgroundColor={panelBg}
|
|
5665
|
+
zIndex={nativePaneChrome ? COMMAND_BAR_PANEL_Z_INDEX : 101}
|
|
5225
5666
|
onMouseDown={(event: any) => {
|
|
5226
5667
|
event.stopPropagation?.();
|
|
5227
5668
|
}}
|
|
5669
|
+
data-gloom-role="command-bar-panel"
|
|
5670
|
+
style={nativePaneChrome ? {
|
|
5671
|
+
borderRadius: 8,
|
|
5672
|
+
boxShadow: "0 14px 32px rgba(0,0,0,0.18)",
|
|
5673
|
+
overflow: "hidden",
|
|
5674
|
+
padding: `${NATIVE_COMMAND_BAR_PADDING_Y_PX}px ${NATIVE_COMMAND_BAR_PADDING_X_PX}px`,
|
|
5675
|
+
} : undefined}
|
|
5228
5676
|
>
|
|
5229
|
-
<
|
|
5677
|
+
{!nativePaneChrome && <Box height={1} backgroundColor={paletteBg} />}
|
|
5678
|
+
|
|
5679
|
+
{!nativePaneChrome && (
|
|
5680
|
+
<Box
|
|
5681
|
+
height={1}
|
|
5682
|
+
paddingX={contentPadding}
|
|
5683
|
+
flexDirection="row"
|
|
5684
|
+
alignItems="center"
|
|
5685
|
+
>
|
|
5686
|
+
{currentRoute && (
|
|
5687
|
+
<Box marginRight={1}>
|
|
5688
|
+
<Button label="Back" variant="ghost" onPress={popRoute} />
|
|
5689
|
+
</Box>
|
|
5690
|
+
)}
|
|
5691
|
+
<Box flexGrow={1}>
|
|
5692
|
+
<Text fg={paletteText} attributes={TextAttributes.BOLD}>
|
|
5693
|
+
{currentRoute?.kind === "mode"
|
|
5694
|
+
? currentRoute.screen === "themes" ? "Change Theme"
|
|
5695
|
+
: currentRoute.screen === "plugins" ? "Manage Plugins"
|
|
5696
|
+
: currentRoute.screen === "layout" ? "Layout Actions"
|
|
5697
|
+
: "Security Description"
|
|
5698
|
+
: currentRoute?.kind === "picker" ? currentRoute.title
|
|
5699
|
+
: currentRoute?.kind === "pane-settings" ? "Pane Settings"
|
|
5700
|
+
: currentRoute?.kind === "workflow" ? currentRoute.title
|
|
5701
|
+
: currentRoute?.kind === "confirm" ? currentRoute.title
|
|
5702
|
+
: "Commands"}
|
|
5703
|
+
</Text>
|
|
5704
|
+
</Box>
|
|
5705
|
+
</Box>
|
|
5706
|
+
)}
|
|
5230
5707
|
|
|
5231
|
-
<
|
|
5232
|
-
{currentRoute && (
|
|
5233
|
-
<
|
|
5234
|
-
<
|
|
5235
|
-
|
|
5708
|
+
<Box key={bodySlotKey} flexDirection="column" flexGrow={1} width="100%" backgroundColor={panelBg}>
|
|
5709
|
+
{nativePaneChrome && currentRoute && (
|
|
5710
|
+
<Box height={1} paddingX={contentPadding}>
|
|
5711
|
+
<Text
|
|
5712
|
+
fg={paletteSubtleText}
|
|
5713
|
+
onMouseDown={(event: any) => {
|
|
5714
|
+
event.stopPropagation?.();
|
|
5715
|
+
event.preventDefault?.();
|
|
5716
|
+
popRoute();
|
|
5717
|
+
}}
|
|
5718
|
+
data-gloom-interactive="true"
|
|
5719
|
+
>
|
|
5720
|
+
← Back
|
|
5721
|
+
</Text>
|
|
5722
|
+
</Box>
|
|
5236
5723
|
)}
|
|
5237
|
-
<box flexGrow={1}>
|
|
5238
|
-
<text fg={paletteText} attributes={TextAttributes.BOLD}>
|
|
5239
|
-
{currentRoute?.kind === "mode"
|
|
5240
|
-
? currentRoute.screen === "themes" ? "Change Theme"
|
|
5241
|
-
: currentRoute.screen === "plugins" ? "Manage Plugins"
|
|
5242
|
-
: currentRoute.screen === "layout" ? "Layout Actions"
|
|
5243
|
-
: currentRoute.screen === "new-pane" ? "New Pane"
|
|
5244
|
-
: "Search Ticker"
|
|
5245
|
-
: currentRoute?.kind === "picker" ? currentRoute.title
|
|
5246
|
-
: currentRoute?.kind === "pane-settings" ? "Pane Settings"
|
|
5247
|
-
: currentRoute?.kind === "workflow" ? currentRoute.title
|
|
5248
|
-
: currentRoute?.kind === "confirm" ? currentRoute.title
|
|
5249
|
-
: "Commands"}
|
|
5250
|
-
</text>
|
|
5251
|
-
</box>
|
|
5252
|
-
</box>
|
|
5253
|
-
|
|
5254
|
-
<box key={bodySlotKey} flexDirection="column" flexGrow={1} width="100%" backgroundColor={paletteBg}>
|
|
5255
5724
|
{(visibleListState || currentRoute?.kind === "picker") && visibleListState && (
|
|
5256
5725
|
<>
|
|
5257
|
-
<
|
|
5258
|
-
<
|
|
5259
|
-
|
|
5726
|
+
<Box height={1} paddingX={contentPadding}>
|
|
5727
|
+
<Box
|
|
5728
|
+
width={queryDisplayWidth}
|
|
5729
|
+
height={1}
|
|
5730
|
+
position="relative"
|
|
5731
|
+
backgroundColor={nativePaneChrome ? undefined : inputBg}
|
|
5732
|
+
style={nativePaneChrome ? undefined : {
|
|
5733
|
+
overflow: "hidden",
|
|
5734
|
+
}}
|
|
5735
|
+
>
|
|
5736
|
+
<Input
|
|
5260
5737
|
value={visibleListState.query}
|
|
5261
5738
|
onInput={setActiveListQuery}
|
|
5262
5739
|
onChange={setActiveListQuery}
|
|
5263
5740
|
placeholder={visibleListState.kind === "root" ? "Search" : "Filter"}
|
|
5264
5741
|
focused
|
|
5265
|
-
width={queryDisplayWidth}
|
|
5266
|
-
backgroundColor={paletteBg}
|
|
5267
|
-
focusedBackgroundColor={paletteBg}
|
|
5742
|
+
width={nativePaneChrome ? "100%" : queryDisplayWidth}
|
|
5743
|
+
backgroundColor={nativePaneChrome ? "transparent" : paletteBg}
|
|
5744
|
+
focusedBackgroundColor={nativePaneChrome ? "transparent" : paletteBg}
|
|
5268
5745
|
textColor={paletteText}
|
|
5269
5746
|
focusedTextColor={paletteText}
|
|
5270
5747
|
placeholderColor={paletteSubtleText}
|
|
5271
5748
|
cursorColor={colors.textBright}
|
|
5272
5749
|
/>
|
|
5273
5750
|
{visibleListState.kind === "root" && rootGhostSuffix && (
|
|
5274
|
-
<
|
|
5751
|
+
<Box
|
|
5275
5752
|
position="absolute"
|
|
5276
5753
|
top={0}
|
|
5277
5754
|
left={Math.max(0, Math.min(rootQuery.length, queryDisplayWidth - 1))}
|
|
5278
5755
|
width={Math.max(0, queryDisplayWidth - Math.min(rootQuery.length, queryDisplayWidth - 1))}
|
|
5279
5756
|
height={1}
|
|
5280
5757
|
>
|
|
5281
|
-
<
|
|
5758
|
+
<Text fg={paletteSubtleText}>
|
|
5282
5759
|
{truncateText(
|
|
5283
5760
|
rootGhostSuffix,
|
|
5284
5761
|
Math.max(0, queryDisplayWidth - Math.min(rootQuery.length, queryDisplayWidth - 1)),
|
|
5285
5762
|
)}
|
|
5286
|
-
</
|
|
5287
|
-
</
|
|
5763
|
+
</Text>
|
|
5764
|
+
</Box>
|
|
5288
5765
|
)}
|
|
5289
|
-
</
|
|
5290
|
-
</
|
|
5291
|
-
<
|
|
5766
|
+
</Box>
|
|
5767
|
+
</Box>
|
|
5768
|
+
<Box height={1} paddingX={contentPadding}>
|
|
5292
5769
|
{visibleListState.kind === "root" && rootShortcutFeedback
|
|
5293
5770
|
? (
|
|
5294
|
-
<
|
|
5295
|
-
{truncateText(rootShortcutFeedback,
|
|
5296
|
-
</
|
|
5771
|
+
<Text fg={paletteSubtleText}>
|
|
5772
|
+
{truncateText(rootShortcutFeedback, queryDisplayWidth)}
|
|
5773
|
+
</Text>
|
|
5297
5774
|
)
|
|
5298
5775
|
: null}
|
|
5299
|
-
</
|
|
5776
|
+
</Box>
|
|
5300
5777
|
</>
|
|
5301
5778
|
)}
|
|
5302
5779
|
|
|
@@ -5304,10 +5781,10 @@ export function CommandBar({
|
|
|
5304
5781
|
{currentRoute?.kind === "workflow" && renderWorkflowBody()}
|
|
5305
5782
|
{currentRoute?.kind === "confirm" && renderConfirmBody()}
|
|
5306
5783
|
{showCustomMultiSelectPicker && renderMultiSelectBody()}
|
|
5307
|
-
</
|
|
5784
|
+
</Box>
|
|
5308
5785
|
|
|
5309
|
-
<
|
|
5310
|
-
</
|
|
5311
|
-
</
|
|
5786
|
+
{!nativePaneChrome && <Box flexGrow={1} />}
|
|
5787
|
+
</Box>
|
|
5788
|
+
</Box>
|
|
5312
5789
|
);
|
|
5313
5790
|
}
|