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,20 +1,21 @@
|
|
|
1
|
+
import { AsciiText, Box, Input, Span, Strong, Text, Underline, useUiHost } from "../../ui";
|
|
1
2
|
import { useState, useCallback, useEffect, useRef, useMemo, type RefObject } from "react";
|
|
2
|
-
import {
|
|
3
|
-
import { TextAttributes } from "
|
|
4
|
-
import type
|
|
3
|
+
import { useShortcut, useViewport } from "../../react/input";
|
|
4
|
+
import { TextAttributes } from "../../ui";
|
|
5
|
+
import { type InputRenderable } from "../../ui";
|
|
5
6
|
import { colors, applyTheme } from "../../theme/colors";
|
|
6
7
|
import { themes, getThemeIds } from "../../theme/themes";
|
|
7
8
|
import { saveConfig } from "../../data/config-store";
|
|
8
9
|
import type { AppConfig } from "../../types/config";
|
|
9
10
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
10
11
|
import { resolveBrokerConfigFields, type BrokerAdapter, type BrokerConfigField } from "../../types/broker";
|
|
11
|
-
import {
|
|
12
|
+
import { buildBrokerProfileConfig, validateBrokerProfileValues } from "../../brokers/profile-form";
|
|
12
13
|
import { createBrokerInstanceId } from "../../utils/broker-instances";
|
|
13
14
|
import { isBackNavigationKey, isPlainEscape } from "../../utils/back-navigation";
|
|
15
|
+
import { detectShortcutPlatform, formatPrimaryShortcut, getShortcutDisplayMode } from "../../utils/shortcut-labels";
|
|
14
16
|
import { syncBrokerInstance } from "../../brokers/sync-broker-instance";
|
|
15
17
|
import { debugLog } from "../../utils/debug-log";
|
|
16
|
-
import {
|
|
17
|
-
import { TextField, ExternalLink } from "../ui";
|
|
18
|
+
import { TextField, ExternalLink, ListView, type ListViewItem } from "../ui";
|
|
18
19
|
|
|
19
20
|
interface OnboardingWizardProps {
|
|
20
21
|
config: AppConfig;
|
|
@@ -22,8 +23,8 @@ interface OnboardingWizardProps {
|
|
|
22
23
|
onComplete: (config: AppConfig) => void | Promise<void>;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
type Step = "welcome" | "theme" | "portfolio" | "
|
|
26
|
-
const STEPS: Step[] = ["welcome", "theme", "portfolio", "
|
|
26
|
+
type Step = "welcome" | "theme" | "portfolio" | "shortcuts" | "ready";
|
|
27
|
+
const STEPS: Step[] = ["welcome", "theme", "portfolio", "shortcuts", "ready"];
|
|
27
28
|
|
|
28
29
|
// Sub-steps within the portfolio step
|
|
29
30
|
type PortfolioSub = "choose" | "manual-name" | "broker-setup" | "broker-fields" | "broker-sync";
|
|
@@ -34,12 +35,7 @@ interface BrokerOption {
|
|
|
34
35
|
adapter: BrokerAdapter;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
const
|
|
38
|
-
"\u259E\u2580\u2596\u259C \u258C \u258C ",
|
|
39
|
-
"\u258C\u2584\u2596\u2590 \u259E\u2580\u2596\u259E\u2580\u2596\u259B\u259A\u2580\u2596\u259B\u2580\u2596\u259E\u2580\u2596\u2599\u2580\u2596\u259B\u2580\u2596",
|
|
40
|
-
"\u258C \u258C\u2590 \u258C \u258C\u258C \u258C\u258C\u2590 \u258C\u258C \u258C\u259B\u2580 \u258C \u258C \u258C",
|
|
41
|
-
"\u259D\u2580 \u2598\u259D\u2580 \u259D\u2580 \u2598\u259D \u2598\u2580\u2580 \u259D\u2580\u2598\u2598 \u2580\u2580 ",
|
|
42
|
-
];
|
|
38
|
+
const LOGO_TEXT = "Gloomberb";
|
|
43
39
|
|
|
44
40
|
const PASSWORD_MASK_CHAR = "*";
|
|
45
41
|
const onboardingLog = debugLog.createLogger("onboarding");
|
|
@@ -49,23 +45,6 @@ interface BrokerSyncSummary {
|
|
|
49
45
|
positionsImported: number;
|
|
50
46
|
}
|
|
51
47
|
|
|
52
|
-
function getToggleablePlugins(pluginRegistry: PluginRegistry) {
|
|
53
|
-
const result: Array<{ id: string; name: string; description: string; order: number }> = [];
|
|
54
|
-
for (const [, plugin] of pluginRegistry.allPlugins) {
|
|
55
|
-
if (plugin.toggleable) {
|
|
56
|
-
result.push({
|
|
57
|
-
id: plugin.id,
|
|
58
|
-
name: plugin.name,
|
|
59
|
-
description: plugin.description ?? "",
|
|
60
|
-
order: plugin.order ?? Number.MAX_SAFE_INTEGER,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return result
|
|
65
|
-
.sort((left, right) => left.order - right.order || left.name.localeCompare(right.name))
|
|
66
|
-
.map(({ order: _order, ...plugin }) => plugin);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
48
|
function summarizeError(error: unknown): string {
|
|
70
49
|
if (error instanceof Error && error.message.trim()) {
|
|
71
50
|
return error.message.trim();
|
|
@@ -91,7 +70,6 @@ function focusPortfolioListCollection(config: AppConfig, collectionId: string):
|
|
|
91
70
|
},
|
|
92
71
|
settings: {
|
|
93
72
|
...instance.settings,
|
|
94
|
-
lockedCollectionId: collectionId,
|
|
95
73
|
visibleCollectionIds: [collectionId, ...visibleCollectionIds.filter((value) => value !== collectionId)],
|
|
96
74
|
},
|
|
97
75
|
};
|
|
@@ -107,7 +85,7 @@ function focusPortfolioListCollection(config: AppConfig, collectionId: string):
|
|
|
107
85
|
}
|
|
108
86
|
|
|
109
87
|
export function OnboardingWizard({ config, pluginRegistry, onComplete }: OnboardingWizardProps) {
|
|
110
|
-
const { width: termWidth, height: termHeight } =
|
|
88
|
+
const { width: termWidth, height: termHeight } = useViewport();
|
|
111
89
|
const [step, setStep] = useState<Step>("welcome");
|
|
112
90
|
const [themeIdx, setThemeIdx] = useState(0);
|
|
113
91
|
|
|
@@ -128,13 +106,6 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
128
106
|
const [isFinishing, setIsFinishing] = useState(false);
|
|
129
107
|
const [finishError, setFinishError] = useState<string | null>(null);
|
|
130
108
|
|
|
131
|
-
// Plugin state
|
|
132
|
-
const toggleablePlugins = useMemo(() => getToggleablePlugins(pluginRegistry), [pluginRegistry]);
|
|
133
|
-
const [disabledPlugins, setDisabledPlugins] = useState<string[]>(() => (
|
|
134
|
-
config.disabledPlugins.filter((pluginId) => pluginId !== "gloomberb-cloud")
|
|
135
|
-
));
|
|
136
|
-
const [pluginIdx, setPluginIdx] = useState(0);
|
|
137
|
-
|
|
138
109
|
const inputRef = useRef<InputRenderable>(null);
|
|
139
110
|
const brokerSyncAttemptRef = useRef(0);
|
|
140
111
|
const themeIds = getThemeIds();
|
|
@@ -152,15 +123,15 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
152
123
|
}, [pluginRegistry.brokers]);
|
|
153
124
|
|
|
154
125
|
// Build the list of choices for the portfolio step: manual + all connectable brokers
|
|
155
|
-
const portfolioChoices = useMemo(() => {
|
|
156
|
-
const choices:
|
|
157
|
-
{ id: "manual", label: "Create Manual Portfolio",
|
|
126
|
+
const portfolioChoices = useMemo<ListViewItem[]>(() => {
|
|
127
|
+
const choices: ListViewItem[] = [
|
|
128
|
+
{ id: "manual", label: "Create Manual Portfolio", description: "Add tickers and positions by hand" },
|
|
158
129
|
];
|
|
159
130
|
for (const broker of brokerOptions) {
|
|
160
131
|
choices.push({
|
|
161
132
|
id: broker.id,
|
|
162
133
|
label: `Connect ${broker.name}`,
|
|
163
|
-
|
|
134
|
+
description: `Auto-import positions via ${broker.name}`,
|
|
164
135
|
});
|
|
165
136
|
}
|
|
166
137
|
return choices;
|
|
@@ -232,10 +203,13 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
232
203
|
}
|
|
233
204
|
|
|
234
205
|
const brokerOption = brokerOptions.find((option) => option.id === selectedBrokerId);
|
|
206
|
+
const adapter = brokerOption?.adapter;
|
|
207
|
+
if (!adapter) throw new Error(`Unknown broker "${selectedBrokerId}".`);
|
|
208
|
+
const validationError = validateBrokerProfileValues(adapter, selectedValues);
|
|
209
|
+
if (validationError) throw new Error(validationError);
|
|
210
|
+
|
|
235
211
|
const label = brokerOption?.name || selectedBrokerId;
|
|
236
|
-
const brokerConfig = (
|
|
237
|
-
? buildIbkrConfigFromValues(selectedValues)
|
|
238
|
-
: { ...selectedValues }) as unknown as Record<string, unknown>;
|
|
212
|
+
const brokerConfig = buildBrokerProfileConfig(adapter, selectedValues);
|
|
239
213
|
const instanceId = createBrokerInstanceId(
|
|
240
214
|
selectedBrokerId,
|
|
241
215
|
label,
|
|
@@ -280,7 +254,7 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
280
254
|
brokers: pluginRegistry.brokers,
|
|
281
255
|
tickerRepository: pluginRegistry.tickerRepository,
|
|
282
256
|
resources: pluginRegistry.persistence.resources,
|
|
283
|
-
|
|
257
|
+
persistResolvedBrokerConfig: true,
|
|
284
258
|
});
|
|
285
259
|
if (brokerSyncAttemptRef.current !== attemptId) {
|
|
286
260
|
return;
|
|
@@ -347,7 +321,7 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
347
321
|
portfolios: isBroker
|
|
348
322
|
? (baseConfig ?? config).portfolios
|
|
349
323
|
: [{ id: "main", name: portfolioName || "Main Portfolio", currency: "USD" }],
|
|
350
|
-
disabledPlugins,
|
|
324
|
+
disabledPlugins: [],
|
|
351
325
|
onboardingComplete: true,
|
|
352
326
|
};
|
|
353
327
|
|
|
@@ -360,7 +334,6 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
360
334
|
}, [
|
|
361
335
|
brokerSyncedConfig,
|
|
362
336
|
config,
|
|
363
|
-
disabledPlugins,
|
|
364
337
|
isFinishing,
|
|
365
338
|
onComplete,
|
|
366
339
|
portfolioName,
|
|
@@ -378,7 +351,7 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
378
351
|
}));
|
|
379
352
|
}, [resetBrokerSync]);
|
|
380
353
|
|
|
381
|
-
|
|
354
|
+
useShortcut((event) => {
|
|
382
355
|
if (isFinishing) {
|
|
383
356
|
return;
|
|
384
357
|
}
|
|
@@ -576,22 +549,6 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
576
549
|
setBrokerSelectIdx((index) => Math.min(optionCount - 1, index + 1));
|
|
577
550
|
}
|
|
578
551
|
}
|
|
579
|
-
} else if (step === "plugins") {
|
|
580
|
-
if (event.name === "up" || event.name === "k") {
|
|
581
|
-
setPluginIdx((i) => Math.max(0, i - 1));
|
|
582
|
-
} else if (event.name === "down" || event.name === "j") {
|
|
583
|
-
setPluginIdx((i) => Math.min(toggleablePlugins.length - 1, i + 1));
|
|
584
|
-
} else if (event.name === "space" || event.name === " ") {
|
|
585
|
-
event.stopPropagation?.();
|
|
586
|
-
const plugin = toggleablePlugins[pluginIdx];
|
|
587
|
-
if (plugin) {
|
|
588
|
-
setDisabledPlugins((prev) =>
|
|
589
|
-
prev.includes(plugin.id)
|
|
590
|
-
? prev.filter((id) => id !== plugin.id)
|
|
591
|
-
: [...prev, plugin.id]
|
|
592
|
-
);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
552
|
}
|
|
596
553
|
});
|
|
597
554
|
|
|
@@ -623,7 +580,7 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
623
580
|
: null;
|
|
624
581
|
|
|
625
582
|
return (
|
|
626
|
-
<
|
|
583
|
+
<Box
|
|
627
584
|
position="absolute"
|
|
628
585
|
top={0}
|
|
629
586
|
left={0}
|
|
@@ -632,7 +589,7 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
632
589
|
backgroundColor={colors.bg}
|
|
633
590
|
zIndex={200}
|
|
634
591
|
>
|
|
635
|
-
<
|
|
592
|
+
<Box
|
|
636
593
|
position="absolute"
|
|
637
594
|
top={contentTop}
|
|
638
595
|
left={contentLeft}
|
|
@@ -646,6 +603,7 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
646
603
|
sub={portfolioSub}
|
|
647
604
|
choices={portfolioChoices}
|
|
648
605
|
optionIdx={portfolioOptionIdx}
|
|
606
|
+
onOptionSelect={setPortfolioOptionIdx}
|
|
649
607
|
portfolioName={portfolioName}
|
|
650
608
|
onNameChange={setPortfolioName}
|
|
651
609
|
selectedBrokerId={selectedBrokerId}
|
|
@@ -660,21 +618,8 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
660
618
|
brokerSyncError={brokerSyncError}
|
|
661
619
|
/>
|
|
662
620
|
)}
|
|
663
|
-
{step === "plugins" && (
|
|
664
|
-
<PluginsStep
|
|
665
|
-
plugins={toggleablePlugins}
|
|
666
|
-
disabledPlugins={disabledPlugins}
|
|
667
|
-
selectedIdx={pluginIdx}
|
|
668
|
-
onToggle={(id) => {
|
|
669
|
-
setDisabledPlugins((prev) =>
|
|
670
|
-
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
|
|
671
|
-
);
|
|
672
|
-
}}
|
|
673
|
-
onSelect={setPluginIdx}
|
|
674
|
-
/>
|
|
675
|
-
)}
|
|
676
621
|
{step === "shortcuts" && (
|
|
677
|
-
<ShortcutsStep pluginRegistry={pluginRegistry}
|
|
622
|
+
<ShortcutsStep pluginRegistry={pluginRegistry} />
|
|
678
623
|
)}
|
|
679
624
|
{step === "ready" && (
|
|
680
625
|
<ReadyStep
|
|
@@ -687,20 +632,20 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
687
632
|
)}
|
|
688
633
|
|
|
689
634
|
{/* Bottom: progress + nav hints */}
|
|
690
|
-
<
|
|
691
|
-
<
|
|
692
|
-
<
|
|
693
|
-
<
|
|
694
|
-
</
|
|
695
|
-
</
|
|
696
|
-
<
|
|
697
|
-
<
|
|
698
|
-
<
|
|
699
|
-
<
|
|
700
|
-
</
|
|
701
|
-
</
|
|
702
|
-
</
|
|
703
|
-
</
|
|
635
|
+
<Box height={1} />
|
|
636
|
+
<Box height={1} flexDirection="row" width={contentWidth}>
|
|
637
|
+
<Box flexGrow={1}>
|
|
638
|
+
<Text fg={colors.textMuted}>{progressDots}</Text>
|
|
639
|
+
</Box>
|
|
640
|
+
</Box>
|
|
641
|
+
<Box height={1} flexDirection="row" width={contentWidth}>
|
|
642
|
+
<Box flexGrow={1} />
|
|
643
|
+
<Box>
|
|
644
|
+
<Text fg={colors.textMuted}>{hintText}</Text>
|
|
645
|
+
</Box>
|
|
646
|
+
</Box>
|
|
647
|
+
</Box>
|
|
648
|
+
</Box>
|
|
704
649
|
);
|
|
705
650
|
}
|
|
706
651
|
|
|
@@ -708,24 +653,20 @@ export function OnboardingWizard({ config, pluginRegistry, onComplete }: Onboard
|
|
|
708
653
|
|
|
709
654
|
function WelcomeStep() {
|
|
710
655
|
return (
|
|
711
|
-
<
|
|
712
|
-
<
|
|
713
|
-
{
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
</
|
|
717
|
-
|
|
718
|
-
<
|
|
719
|
-
<
|
|
720
|
-
<
|
|
721
|
-
</
|
|
722
|
-
<
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
</box>
|
|
726
|
-
<box height={2} />
|
|
727
|
-
|
|
728
|
-
</box>
|
|
656
|
+
<Box flexDirection="column" paddingX={2}>
|
|
657
|
+
<Box height={1} />
|
|
658
|
+
<AsciiText text={LOGO_TEXT} font="wordmark" color={colors.textBright} />
|
|
659
|
+
<Box height={2} />
|
|
660
|
+
<Box height={1}>
|
|
661
|
+
<Text fg={colors.textDim}>{"The open terminal for modern finance."}</Text>
|
|
662
|
+
</Box>
|
|
663
|
+
<Box height={2} />
|
|
664
|
+
<Box height={1}>
|
|
665
|
+
<Text fg={colors.textMuted}>{"Let's set things up (~30s)."}</Text>
|
|
666
|
+
</Box>
|
|
667
|
+
<Box height={2} />
|
|
668
|
+
|
|
669
|
+
</Box>
|
|
729
670
|
);
|
|
730
671
|
}
|
|
731
672
|
|
|
@@ -737,30 +678,30 @@ function ThemeStep({ themeIds, selectedIdx, height }: { themeIds: string[]; sele
|
|
|
737
678
|
const windowEnd = Math.min(themeIds.length, windowStart + maxVisible);
|
|
738
679
|
|
|
739
680
|
return (
|
|
740
|
-
<
|
|
741
|
-
<
|
|
742
|
-
<
|
|
743
|
-
</
|
|
744
|
-
<
|
|
745
|
-
<
|
|
746
|
-
<
|
|
747
|
-
<
|
|
748
|
-
</
|
|
749
|
-
<
|
|
750
|
-
<
|
|
751
|
-
<
|
|
752
|
-
<
|
|
753
|
-
<
|
|
754
|
-
<
|
|
755
|
-
<
|
|
756
|
-
<
|
|
757
|
-
</
|
|
758
|
-
<
|
|
681
|
+
<Box flexDirection="column" paddingX={2}>
|
|
682
|
+
<Box height={1}>
|
|
683
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{"Theme"}</Text>
|
|
684
|
+
</Box>
|
|
685
|
+
<Box height={1} />
|
|
686
|
+
<Box height={1} flexDirection="row">
|
|
687
|
+
<Text fg={colors.textDim}>{"Change it later from the command bar with "}</Text>
|
|
688
|
+
<Text fg={colors.text} attributes={TextAttributes.BOLD}>{"TH"}</Text>
|
|
689
|
+
</Box>
|
|
690
|
+
<Box height={1} />
|
|
691
|
+
<Box height={1}>
|
|
692
|
+
<Text fg={colors.positive}>{" \u2588\u2588 "}</Text>
|
|
693
|
+
<Text fg={colors.negative}>{" \u2588\u2588 "}</Text>
|
|
694
|
+
<Text fg={colors.text}>{" \u2588\u2588 "}</Text>
|
|
695
|
+
<Text fg={colors.textBright}>{" \u2588\u2588 "}</Text>
|
|
696
|
+
<Text fg={colors.borderFocused}>{" \u2588\u2588 "}</Text>
|
|
697
|
+
<Text fg={colors.textDim}>{" \u2588\u2588 "}</Text>
|
|
698
|
+
</Box>
|
|
699
|
+
<Box height={1} />
|
|
759
700
|
|
|
760
701
|
{windowStart > 0 && (
|
|
761
|
-
<
|
|
762
|
-
<
|
|
763
|
-
</
|
|
702
|
+
<Box height={1}>
|
|
703
|
+
<Text fg={colors.textMuted}>{"\u2191 more"}</Text>
|
|
704
|
+
</Box>
|
|
764
705
|
)}
|
|
765
706
|
|
|
766
707
|
{themeIds.slice(windowStart, windowEnd).map((id, i) => {
|
|
@@ -768,28 +709,28 @@ function ThemeStep({ themeIds, selectedIdx, height }: { themeIds: string[]; sele
|
|
|
768
709
|
const globalIdx = windowStart + i;
|
|
769
710
|
const isSel = globalIdx === selectedIdx;
|
|
770
711
|
return (
|
|
771
|
-
<
|
|
772
|
-
<
|
|
712
|
+
<Box key={id} height={1} backgroundColor={isSel ? colors.selected : colors.bg}>
|
|
713
|
+
<Text fg={isSel ? colors.selectedText : colors.textDim}>
|
|
773
714
|
{isSel ? "\u25b8 " : " "}
|
|
774
|
-
</
|
|
775
|
-
<
|
|
715
|
+
</Text>
|
|
716
|
+
<Text fg={isSel ? colors.text : colors.textDim} attributes={isSel ? TextAttributes.BOLD : 0}>
|
|
776
717
|
{theme.name}
|
|
777
|
-
</
|
|
778
|
-
</
|
|
718
|
+
</Text>
|
|
719
|
+
</Box>
|
|
779
720
|
);
|
|
780
721
|
})}
|
|
781
722
|
|
|
782
723
|
{windowEnd < themeIds.length && (
|
|
783
|
-
<
|
|
784
|
-
<
|
|
785
|
-
</
|
|
724
|
+
<Box height={1}>
|
|
725
|
+
<Text fg={colors.textMuted}>{"\u2193 more"}</Text>
|
|
726
|
+
</Box>
|
|
786
727
|
)}
|
|
787
728
|
|
|
788
|
-
<
|
|
789
|
-
<
|
|
790
|
-
<
|
|
791
|
-
</
|
|
792
|
-
</
|
|
729
|
+
<Box height={1} />
|
|
730
|
+
<Box height={1}>
|
|
731
|
+
<Text fg={colors.textMuted}>{"Use \u2191\u2193 to browse"}</Text>
|
|
732
|
+
</Box>
|
|
733
|
+
</Box>
|
|
793
734
|
);
|
|
794
735
|
}
|
|
795
736
|
|
|
@@ -797,6 +738,7 @@ function PortfolioStep({
|
|
|
797
738
|
sub,
|
|
798
739
|
choices,
|
|
799
740
|
optionIdx,
|
|
741
|
+
onOptionSelect,
|
|
800
742
|
portfolioName,
|
|
801
743
|
onNameChange,
|
|
802
744
|
selectedBrokerId,
|
|
@@ -811,8 +753,9 @@ function PortfolioStep({
|
|
|
811
753
|
brokerSyncError,
|
|
812
754
|
}: {
|
|
813
755
|
sub: PortfolioSub;
|
|
814
|
-
choices:
|
|
756
|
+
choices: ListViewItem[];
|
|
815
757
|
optionIdx: number;
|
|
758
|
+
onOptionSelect: (idx: number) => void;
|
|
816
759
|
portfolioName: string;
|
|
817
760
|
onNameChange: (n: string) => void;
|
|
818
761
|
selectedBrokerId: string | null;
|
|
@@ -828,62 +771,50 @@ function PortfolioStep({
|
|
|
828
771
|
}) {
|
|
829
772
|
if (sub === "choose") {
|
|
830
773
|
return (
|
|
831
|
-
<
|
|
832
|
-
<
|
|
833
|
-
<
|
|
834
|
-
</
|
|
835
|
-
<
|
|
836
|
-
<
|
|
837
|
-
<
|
|
838
|
-
</
|
|
839
|
-
<
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
<text fg={colors.textDim}>{choices[optionIdx]?.desc}</text>
|
|
858
|
-
</box>
|
|
859
|
-
|
|
860
|
-
<box height={1} />
|
|
861
|
-
<box height={1}>
|
|
862
|
-
<text fg={colors.textDim}>{"Edits later from the command bar."}</text>
|
|
863
|
-
</box>
|
|
864
|
-
<box height={1} />
|
|
865
|
-
<box height={1}>
|
|
866
|
-
<text fg={colors.textMuted}>{"Use \u2191\u2193 to choose"}</text>
|
|
867
|
-
</box>
|
|
868
|
-
</box>
|
|
774
|
+
<Box flexDirection="column" paddingX={2}>
|
|
775
|
+
<Box height={1}>
|
|
776
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{"Set up a portfolio"}</Text>
|
|
777
|
+
</Box>
|
|
778
|
+
<Box height={1} />
|
|
779
|
+
<Box height={1}>
|
|
780
|
+
<Text fg={colors.textDim}>{"How would you like to get started?"}</Text>
|
|
781
|
+
</Box>
|
|
782
|
+
<Box height={2} />
|
|
783
|
+
|
|
784
|
+
<ListView
|
|
785
|
+
items={choices}
|
|
786
|
+
selectedIndex={optionIdx}
|
|
787
|
+
onSelect={onOptionSelect}
|
|
788
|
+
showSelectedDescription
|
|
789
|
+
/>
|
|
790
|
+
|
|
791
|
+
<Box height={1} />
|
|
792
|
+
<Box height={1}>
|
|
793
|
+
<Text fg={colors.textDim}>{"Edits later from the command bar."}</Text>
|
|
794
|
+
</Box>
|
|
795
|
+
<Box height={1} />
|
|
796
|
+
<Box height={1}>
|
|
797
|
+
<Text fg={colors.textMuted}>{"Use \u2191\u2193 to choose"}</Text>
|
|
798
|
+
</Box>
|
|
799
|
+
</Box>
|
|
869
800
|
);
|
|
870
801
|
}
|
|
871
802
|
|
|
872
803
|
if (sub === "manual-name") {
|
|
873
804
|
return (
|
|
874
|
-
<
|
|
875
|
-
<
|
|
876
|
-
<
|
|
877
|
-
</
|
|
878
|
-
<
|
|
879
|
-
<
|
|
880
|
-
<
|
|
881
|
-
</
|
|
882
|
-
<
|
|
883
|
-
<
|
|
884
|
-
<
|
|
885
|
-
</
|
|
886
|
-
<
|
|
805
|
+
<Box flexDirection="column" paddingX={2}>
|
|
806
|
+
<Box height={1}>
|
|
807
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{"Name your portfolio"}</Text>
|
|
808
|
+
</Box>
|
|
809
|
+
<Box height={1} />
|
|
810
|
+
<Box height={1}>
|
|
811
|
+
<Text fg={colors.textDim}>{"Create watchlists later."}</Text>
|
|
812
|
+
</Box>
|
|
813
|
+
<Box height={2} />
|
|
814
|
+
<Box height={1}>
|
|
815
|
+
<Text fg={colors.text}>{"Portfolio name:"}</Text>
|
|
816
|
+
</Box>
|
|
817
|
+
<Box height={1}>
|
|
887
818
|
{editing ? (
|
|
888
819
|
<TextField
|
|
889
820
|
inputRef={inputRef}
|
|
@@ -897,23 +828,23 @@ function PortfolioStep({
|
|
|
897
828
|
onSubmit={() => {}}
|
|
898
829
|
/>
|
|
899
830
|
) : (
|
|
900
|
-
<
|
|
831
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>
|
|
901
832
|
{`> ${portfolioName}`}
|
|
902
|
-
</
|
|
833
|
+
</Text>
|
|
903
834
|
)}
|
|
904
|
-
</
|
|
905
|
-
<
|
|
906
|
-
<
|
|
907
|
-
<
|
|
908
|
-
<
|
|
909
|
-
<
|
|
910
|
-
</
|
|
911
|
-
<
|
|
912
|
-
<
|
|
913
|
-
<
|
|
914
|
-
<
|
|
915
|
-
</
|
|
916
|
-
</
|
|
835
|
+
</Box>
|
|
836
|
+
<Box height={2} />
|
|
837
|
+
<Box height={1} flexDirection="row">
|
|
838
|
+
<Text fg={colors.textDim}>{"After setup, use the command bar ("}</Text>
|
|
839
|
+
<Text fg={colors.text}>{"Ctrl+P"}</Text>
|
|
840
|
+
<Text fg={colors.textDim}>{") and"}</Text>
|
|
841
|
+
</Box>
|
|
842
|
+
<Box height={1} flexDirection="row">
|
|
843
|
+
<Text fg={colors.textDim}>{"type "}</Text>
|
|
844
|
+
<Text fg={colors.text} attributes={TextAttributes.BOLD}>{"DES AAPL"}</Text>
|
|
845
|
+
<Text fg={colors.textDim}>{" to open security details for a stock or ETF."}</Text>
|
|
846
|
+
</Box>
|
|
847
|
+
</Box>
|
|
917
848
|
);
|
|
918
849
|
}
|
|
919
850
|
|
|
@@ -923,110 +854,110 @@ function PortfolioStep({
|
|
|
923
854
|
const isGateway = connectionMode === "gateway";
|
|
924
855
|
|
|
925
856
|
return (
|
|
926
|
-
<
|
|
927
|
-
<
|
|
928
|
-
<
|
|
857
|
+
<Box flexDirection="column" paddingX={2}>
|
|
858
|
+
<Box height={1}>
|
|
859
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>
|
|
929
860
|
{`Setup Guide — ${brokerLabel}`}
|
|
930
|
-
</
|
|
931
|
-
</
|
|
932
|
-
<
|
|
861
|
+
</Text>
|
|
862
|
+
</Box>
|
|
863
|
+
<Box height={1} />
|
|
933
864
|
|
|
934
865
|
{selectedBrokerId === "ibkr" && !isGateway && (
|
|
935
866
|
<>
|
|
936
|
-
<
|
|
937
|
-
<
|
|
938
|
-
</
|
|
939
|
-
<
|
|
940
|
-
<
|
|
941
|
-
<
|
|
942
|
-
</
|
|
943
|
-
<
|
|
944
|
-
<
|
|
945
|
-
</
|
|
946
|
-
<
|
|
947
|
-
<
|
|
948
|
-
</
|
|
949
|
-
<
|
|
950
|
-
<
|
|
951
|
-
</
|
|
952
|
-
<
|
|
867
|
+
<Box height={1}>
|
|
868
|
+
<Text fg={colors.textDim}>{"You'll need 2 things from IBKR Account Management:"}</Text>
|
|
869
|
+
</Box>
|
|
870
|
+
<Box height={2} />
|
|
871
|
+
<Box height={1}>
|
|
872
|
+
<Text fg={colors.textDim}>{"1. Go to "}<Underline><Span fg={colors.text}>{"Reports > Flex Queries"}</Span></Underline></Text>
|
|
873
|
+
</Box>
|
|
874
|
+
<Box height={1}>
|
|
875
|
+
<Text fg={colors.textDim}>{"2. Create a Flex Query that includes positions data"}</Text>
|
|
876
|
+
</Box>
|
|
877
|
+
<Box height={1}>
|
|
878
|
+
<Text fg={colors.textDim}>{"3. Note the "}<Strong><Span fg={colors.text}>{"Query ID"}</Span></Strong>{" (numeric)"}</Text>
|
|
879
|
+
</Box>
|
|
880
|
+
<Box height={1}>
|
|
881
|
+
<Text fg={colors.textDim}>{"4. Under "}<Underline><Span fg={colors.text}>{"Reports > Settings"}</Span></Underline>{", generate a "}<Strong><Span fg={colors.text}>{"Flex Web Service Token"}</Span></Strong></Text>
|
|
882
|
+
</Box>
|
|
883
|
+
<Box height={2} />
|
|
953
884
|
<ExternalLink url="https://www.ibkrguides.com/orgportal/performanceandstatements/flex.htm" />
|
|
954
885
|
</>
|
|
955
886
|
)}
|
|
956
887
|
|
|
957
888
|
{selectedBrokerId === "ibkr" && isGateway && (
|
|
958
889
|
<>
|
|
959
|
-
<
|
|
960
|
-
<
|
|
961
|
-
</
|
|
962
|
-
<
|
|
963
|
-
<
|
|
964
|
-
<
|
|
965
|
-
</
|
|
966
|
-
<
|
|
967
|
-
<
|
|
968
|
-
</
|
|
969
|
-
<
|
|
970
|
-
<
|
|
971
|
-
</
|
|
972
|
-
<
|
|
973
|
-
<
|
|
974
|
-
</
|
|
975
|
-
<
|
|
976
|
-
<
|
|
977
|
-
</
|
|
978
|
-
<
|
|
979
|
-
<
|
|
980
|
-
</
|
|
981
|
-
<
|
|
982
|
-
<
|
|
983
|
-
</
|
|
984
|
-
<
|
|
890
|
+
<Box height={1}>
|
|
891
|
+
<Text fg={colors.textDim}>{"You'll need IB Gateway or TWS running locally:"}</Text>
|
|
892
|
+
</Box>
|
|
893
|
+
<Box height={2} />
|
|
894
|
+
<Box height={1}>
|
|
895
|
+
<Text fg={colors.textDim}>{"1. Download and install "}<Strong><Span fg={colors.text}>{"IB Gateway"}</Span></Strong>{" (or use TWS)"}</Text>
|
|
896
|
+
</Box>
|
|
897
|
+
<Box height={1}>
|
|
898
|
+
<Text fg={colors.textDim}>{"2. Log in with your IBKR credentials"}</Text>
|
|
899
|
+
</Box>
|
|
900
|
+
<Box height={1}>
|
|
901
|
+
<Text fg={colors.textDim}>{"3. In "}<Underline><Span fg={colors.text}>{"Configuration > API > Settings"}</Span></Underline>{":"}</Text>
|
|
902
|
+
</Box>
|
|
903
|
+
<Box height={1}>
|
|
904
|
+
<Text fg={colors.textDim}>{" Enable \"ActiveX and Socket Clients\""}</Text>
|
|
905
|
+
</Box>
|
|
906
|
+
<Box height={1}>
|
|
907
|
+
<Text fg={colors.textDim}>{" Gloomberb can auto-detect local API ports (4001, 4002, 7496, 7497)"}</Text>
|
|
908
|
+
</Box>
|
|
909
|
+
<Box height={1}>
|
|
910
|
+
<Text fg={colors.textDim}>{" Use Manual setup only if you need a custom host or exact socket port"}</Text>
|
|
911
|
+
</Box>
|
|
912
|
+
<Box height={1}>
|
|
913
|
+
<Text fg={colors.textDim}>{"4. Keep it running while using Gloomberb"}</Text>
|
|
914
|
+
</Box>
|
|
915
|
+
<Box height={2} />
|
|
985
916
|
<ExternalLink url="https://www.interactivebrokers.com/en/trading/ibgateway-stable.php" />
|
|
986
917
|
</>
|
|
987
918
|
)}
|
|
988
919
|
|
|
989
920
|
{selectedBrokerId !== "ibkr" && (
|
|
990
921
|
<>
|
|
991
|
-
<
|
|
992
|
-
<
|
|
993
|
-
</
|
|
994
|
-
<
|
|
995
|
-
<
|
|
996
|
-
</
|
|
922
|
+
<Box height={1}>
|
|
923
|
+
<Text fg={colors.textDim}>{`You'll need your ${brokerLabel} API credentials.`}</Text>
|
|
924
|
+
</Box>
|
|
925
|
+
<Box height={1}>
|
|
926
|
+
<Text fg={colors.textDim}>{"Check your broker's documentation for setup instructions."}</Text>
|
|
927
|
+
</Box>
|
|
997
928
|
</>
|
|
998
929
|
)}
|
|
999
930
|
|
|
1000
|
-
<
|
|
931
|
+
<Box height={2} />
|
|
1001
932
|
|
|
1002
|
-
</
|
|
933
|
+
</Box>
|
|
1003
934
|
);
|
|
1004
935
|
}
|
|
1005
936
|
|
|
1006
937
|
if (sub === "broker-sync" && selectedBrokerId) {
|
|
1007
938
|
const brokerLabel = choices.find((choice) => choice.id === selectedBrokerId)?.label.replace("Connect ", "") ?? selectedBrokerId;
|
|
1008
939
|
return (
|
|
1009
|
-
<
|
|
1010
|
-
<
|
|
1011
|
-
<
|
|
1012
|
-
</
|
|
1013
|
-
<
|
|
1014
|
-
<
|
|
1015
|
-
<
|
|
940
|
+
<Box flexDirection="column" paddingX={2}>
|
|
941
|
+
<Box height={1}>
|
|
942
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{`Connect ${brokerLabel}`}</Text>
|
|
943
|
+
</Box>
|
|
944
|
+
<Box height={1} />
|
|
945
|
+
<Box height={1}>
|
|
946
|
+
<Text fg={brokerSyncing ? colors.text : colors.negative}>
|
|
1016
947
|
{brokerSyncing
|
|
1017
948
|
? `Connecting to ${brokerLabel} and importing accounts and positions...`
|
|
1018
949
|
: brokerSyncError || `Unable to sync ${brokerLabel}.`}
|
|
1019
|
-
</
|
|
1020
|
-
</
|
|
1021
|
-
<
|
|
1022
|
-
<
|
|
1023
|
-
<
|
|
950
|
+
</Text>
|
|
951
|
+
</Box>
|
|
952
|
+
<Box height={2} />
|
|
953
|
+
<Box height={1}>
|
|
954
|
+
<Text fg={colors.textDim}>
|
|
1024
955
|
{brokerSyncing
|
|
1025
956
|
? "This happens now so your portfolio is ready before onboarding finishes."
|
|
1026
957
|
: "Press Enter to retry, or Backspace to edit the broker settings."}
|
|
1027
|
-
</
|
|
1028
|
-
</
|
|
1029
|
-
</
|
|
958
|
+
</Text>
|
|
959
|
+
</Box>
|
|
960
|
+
</Box>
|
|
1030
961
|
);
|
|
1031
962
|
}
|
|
1032
963
|
|
|
@@ -1036,13 +967,13 @@ function PortfolioStep({
|
|
|
1036
967
|
const values = brokerValues[selectedBrokerId] ?? {};
|
|
1037
968
|
|
|
1038
969
|
return (
|
|
1039
|
-
<
|
|
1040
|
-
<
|
|
1041
|
-
<
|
|
970
|
+
<Box flexDirection="column" paddingX={2}>
|
|
971
|
+
<Box height={1}>
|
|
972
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>
|
|
1042
973
|
{"Connect "}{choices.find((c) => c.id === selectedBrokerId)?.label.replace("Connect ", "") ?? selectedBrokerId}
|
|
1043
|
-
</
|
|
1044
|
-
</
|
|
1045
|
-
<
|
|
974
|
+
</Text>
|
|
975
|
+
</Box>
|
|
976
|
+
<Box height={1} />
|
|
1046
977
|
|
|
1047
978
|
{/* Show completed fields */}
|
|
1048
979
|
{brokerFields.map((field, i) => {
|
|
@@ -1055,10 +986,10 @@ function PortfolioStep({
|
|
|
1055
986
|
? field.options?.find((option) => option.value === val)?.label ?? val
|
|
1056
987
|
: field.type === "password" ? PASSWORD_MASK_CHAR.repeat(val.length) : val;
|
|
1057
988
|
return (
|
|
1058
|
-
<
|
|
1059
|
-
<
|
|
1060
|
-
<
|
|
1061
|
-
</
|
|
989
|
+
<Box key={field.key} height={1}>
|
|
990
|
+
<Text fg={colors.positive}>{"\u2713 "}</Text>
|
|
991
|
+
<Text fg={colors.text}>{`${field.label}: ${selectedLabel}`}</Text>
|
|
992
|
+
</Box>
|
|
1062
993
|
);
|
|
1063
994
|
}
|
|
1064
995
|
|
|
@@ -1066,15 +997,15 @@ function PortfolioStep({
|
|
|
1066
997
|
const activeSelectValue = field.options?.[brokerSelectIdx]?.value ?? values[field.key] ?? "";
|
|
1067
998
|
const effectiveValue = val || field.defaultValue || "";
|
|
1068
999
|
return (
|
|
1069
|
-
<
|
|
1070
|
-
{i > 0 && <
|
|
1071
|
-
<
|
|
1072
|
-
<
|
|
1000
|
+
<Box key={field.key} flexDirection="column">
|
|
1001
|
+
{i > 0 && <Box height={1} />}
|
|
1002
|
+
<Box height={1}>
|
|
1003
|
+
<Text fg={colors.text} attributes={TextAttributes.BOLD}>
|
|
1073
1004
|
{`Step ${i + 1}: `}
|
|
1074
|
-
</
|
|
1075
|
-
<
|
|
1076
|
-
</
|
|
1077
|
-
<
|
|
1005
|
+
</Text>
|
|
1006
|
+
<Text fg={colors.text}>{field.label}</Text>
|
|
1007
|
+
</Box>
|
|
1008
|
+
<Box height={1}>
|
|
1078
1009
|
{field.type !== "select" && (editing ? (
|
|
1079
1010
|
<TextField
|
|
1080
1011
|
inputRef={inputRef}
|
|
@@ -1089,142 +1020,87 @@ function PortfolioStep({
|
|
|
1089
1020
|
onSubmit={() => {}}
|
|
1090
1021
|
/>
|
|
1091
1022
|
) : (
|
|
1092
|
-
<
|
|
1023
|
+
<Text fg={effectiveValue ? colors.positive : colors.textMuted}>
|
|
1093
1024
|
{effectiveValue
|
|
1094
1025
|
? `\u2713 ${field.label}: ${field.type === "password" ? PASSWORD_MASK_CHAR.repeat(effectiveValue.length) : effectiveValue}`
|
|
1095
1026
|
: "Press enter to type..."}
|
|
1096
|
-
</
|
|
1027
|
+
</Text>
|
|
1097
1028
|
))}
|
|
1098
|
-
</
|
|
1029
|
+
</Box>
|
|
1099
1030
|
{field.type !== "select" && !val && field.defaultValue && (
|
|
1100
|
-
<
|
|
1101
|
-
<
|
|
1102
|
-
</
|
|
1031
|
+
<Box height={1}>
|
|
1032
|
+
<Text fg={colors.textMuted}>{`Press enter to use ${field.defaultValue}`}</Text>
|
|
1033
|
+
</Box>
|
|
1103
1034
|
)}
|
|
1104
1035
|
{field.type === "select" && (
|
|
1105
|
-
<
|
|
1036
|
+
<Box flexDirection="column">
|
|
1106
1037
|
{(field.options ?? []).map((option, optionIdx) => {
|
|
1107
1038
|
const selected = optionIdx === brokerSelectIdx;
|
|
1108
1039
|
return (
|
|
1109
|
-
<
|
|
1110
|
-
<
|
|
1111
|
-
<
|
|
1112
|
-
<
|
|
1040
|
+
<Box key={option.value} flexDirection="column" backgroundColor={selected ? colors.selected : colors.bg}>
|
|
1041
|
+
<Box height={1}>
|
|
1042
|
+
<Text fg={selected ? colors.selectedText : colors.textDim}>{selected ? "\u25b8 " : " "}</Text>
|
|
1043
|
+
<Text
|
|
1113
1044
|
fg={selected ? colors.text : colors.textDim}
|
|
1114
1045
|
attributes={selected ? TextAttributes.BOLD : 0}
|
|
1115
1046
|
>
|
|
1116
1047
|
{option.label}
|
|
1117
|
-
</
|
|
1118
|
-
</
|
|
1048
|
+
</Text>
|
|
1049
|
+
</Box>
|
|
1119
1050
|
{option.description && (
|
|
1120
|
-
<
|
|
1121
|
-
<
|
|
1122
|
-
</
|
|
1051
|
+
<Box height={1}>
|
|
1052
|
+
<Text fg={colors.textMuted}>{` ${option.description}`}</Text>
|
|
1053
|
+
</Box>
|
|
1123
1054
|
)}
|
|
1124
|
-
</
|
|
1055
|
+
</Box>
|
|
1125
1056
|
);
|
|
1126
1057
|
})}
|
|
1127
|
-
</
|
|
1058
|
+
</Box>
|
|
1128
1059
|
)}
|
|
1129
1060
|
{field.type === "select" && (
|
|
1130
|
-
<
|
|
1131
|
-
<
|
|
1061
|
+
<Box height={1}>
|
|
1062
|
+
<Text fg={colors.textMuted}>
|
|
1132
1063
|
{activeSelectValue ? `Selected: ${field.options?.find((option) => option.value === activeSelectValue)?.label ?? activeSelectValue}` : "Use \u2191\u2193 to choose"}
|
|
1133
|
-
</
|
|
1134
|
-
</
|
|
1064
|
+
</Text>
|
|
1065
|
+
</Box>
|
|
1135
1066
|
)}
|
|
1136
|
-
</
|
|
1067
|
+
</Box>
|
|
1137
1068
|
);
|
|
1138
1069
|
}
|
|
1139
1070
|
|
|
1140
1071
|
return null;
|
|
1141
1072
|
})}
|
|
1142
1073
|
|
|
1143
|
-
<
|
|
1144
|
-
<
|
|
1145
|
-
<
|
|
1146
|
-
</
|
|
1147
|
-
<
|
|
1148
|
-
<
|
|
1149
|
-
<
|
|
1074
|
+
<Box height={2} />
|
|
1075
|
+
<Box height={1}>
|
|
1076
|
+
<Text fg={colors.textDim}>{"Credentials are saved locally."}</Text>
|
|
1077
|
+
</Box>
|
|
1078
|
+
<Box height={1} />
|
|
1079
|
+
<Box height={1}>
|
|
1080
|
+
<Text fg={colors.textMuted}>
|
|
1150
1081
|
{`Field ${brokerFieldIdx + 1} of ${brokerFields.length}`}
|
|
1151
|
-
</
|
|
1152
|
-
</
|
|
1153
|
-
</
|
|
1154
|
-
);
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
function PluginsStep({
|
|
1158
|
-
plugins,
|
|
1159
|
-
disabledPlugins,
|
|
1160
|
-
selectedIdx,
|
|
1161
|
-
onToggle,
|
|
1162
|
-
onSelect,
|
|
1163
|
-
}: {
|
|
1164
|
-
plugins: { id: string; name: string; description: string }[];
|
|
1165
|
-
disabledPlugins: string[];
|
|
1166
|
-
selectedIdx: number;
|
|
1167
|
-
onToggle: (id: string) => void;
|
|
1168
|
-
onSelect: (idx: number) => void;
|
|
1169
|
-
}) {
|
|
1170
|
-
const items: ToggleListItem[] = plugins.map((p) => ({
|
|
1171
|
-
id: p.id,
|
|
1172
|
-
label: p.name,
|
|
1173
|
-
enabled: !disabledPlugins.includes(p.id),
|
|
1174
|
-
description: p.description,
|
|
1175
|
-
}));
|
|
1176
|
-
|
|
1177
|
-
return (
|
|
1178
|
-
<box flexDirection="column" paddingX={2}>
|
|
1179
|
-
<box height={1}>
|
|
1180
|
-
<text fg={colors.textBright} attributes={TextAttributes.BOLD}>{"Select plugins to enable"}</text>
|
|
1181
|
-
</box>
|
|
1182
|
-
|
|
1183
|
-
<box height={2} />
|
|
1184
|
-
|
|
1185
|
-
<ToggleList
|
|
1186
|
-
items={items}
|
|
1187
|
-
selectedIdx={selectedIdx}
|
|
1188
|
-
onToggle={onToggle}
|
|
1189
|
-
onSelect={onSelect}
|
|
1190
|
-
/>
|
|
1191
|
-
|
|
1192
|
-
<box height={1} />
|
|
1193
|
-
<box height={1}>
|
|
1194
|
-
<text fg={colors.textMuted}>{"Use \u2191\u2193 to navigate \u00b7 space to toggle"}</text>
|
|
1195
|
-
</box>
|
|
1196
|
-
<box height={1} />
|
|
1197
|
-
<box height={1}>
|
|
1198
|
-
<text fg={colors.textDim}>{"Toggle plugins anytime from the command bar"}</text>
|
|
1199
|
-
</box>
|
|
1200
|
-
<box height={1} flexDirection="row">
|
|
1201
|
-
<text fg={colors.textDim}>{"with the "}</text>
|
|
1202
|
-
<text fg={colors.text} attributes={TextAttributes.BOLD}>{"PL"}</text>
|
|
1203
|
-
<text fg={colors.textDim}>{" prefix."}</text>
|
|
1204
|
-
</box>
|
|
1205
|
-
</box>
|
|
1082
|
+
</Text>
|
|
1083
|
+
</Box>
|
|
1084
|
+
</Box>
|
|
1206
1085
|
);
|
|
1207
1086
|
}
|
|
1208
1087
|
|
|
1209
|
-
function ShortcutsStep({
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
disabledPlugins: string[];
|
|
1215
|
-
}) {
|
|
1088
|
+
function ShortcutsStep({ pluginRegistry }: { pluginRegistry: PluginRegistry }) {
|
|
1089
|
+
const uiHost = useUiHost();
|
|
1090
|
+
const shortcutPlatform = detectShortcutPlatform();
|
|
1091
|
+
const shortcutDisplayMode = getShortcutDisplayMode(uiHost.kind);
|
|
1092
|
+
const platformShortcut = (keys: string | readonly string[]) => formatPrimaryShortcut(keys, shortcutPlatform, shortcutDisplayMode);
|
|
1216
1093
|
const keyboardShortcuts = [
|
|
1217
1094
|
{ key: "Ctrl+P / `", desc: "Open the command bar" },
|
|
1218
1095
|
{ key: "Tab", desc: "Switch between panels" },
|
|
1219
|
-
{ key: "
|
|
1096
|
+
{ key: platformShortcut("W"), desc: "Close the focused pane" },
|
|
1097
|
+
{ key: platformShortcut(["Shift", "D"]), desc: "Dock or float the focused pane" },
|
|
1220
1098
|
{ key: "q", desc: "Quit" },
|
|
1221
1099
|
];
|
|
1222
1100
|
|
|
1223
|
-
const disabledSet = useMemo(() => new Set(disabledPlugins), [disabledPlugins]);
|
|
1224
|
-
|
|
1225
1101
|
const commandPrefixes = useMemo(() => {
|
|
1226
1102
|
const builtIn = [
|
|
1227
|
-
{ key: "
|
|
1103
|
+
{ key: "DES AAPL", desc: "Open security details" },
|
|
1228
1104
|
{ key: "TH", desc: "Switch theme" },
|
|
1229
1105
|
{ key: "PL", desc: "Toggle plugins" },
|
|
1230
1106
|
{ key: "PS", desc: "Edit the current window settings" },
|
|
@@ -1236,8 +1112,6 @@ function ShortcutsStep({
|
|
|
1236
1112
|
|
|
1237
1113
|
for (const [, template] of pluginRegistry.paneTemplates) {
|
|
1238
1114
|
if (!template.shortcut) continue;
|
|
1239
|
-
const pluginId = pluginRegistry.getPaneTemplatePluginId(template.id);
|
|
1240
|
-
if (pluginId && disabledSet.has(pluginId)) continue;
|
|
1241
1115
|
if (builtInKeys.has(template.shortcut.prefix)) continue;
|
|
1242
1116
|
const label = template.shortcut.argPlaceholder
|
|
1243
1117
|
? `${template.shortcut.prefix} <${template.shortcut.argPlaceholder}>`
|
|
@@ -1247,46 +1121,46 @@ function ShortcutsStep({
|
|
|
1247
1121
|
|
|
1248
1122
|
pluginPrefixes.sort((a, b) => a.key.localeCompare(b.key));
|
|
1249
1123
|
return [...builtIn, ...pluginPrefixes];
|
|
1250
|
-
}, [pluginRegistry
|
|
1124
|
+
}, [pluginRegistry]);
|
|
1251
1125
|
|
|
1252
|
-
const COL =
|
|
1126
|
+
const COL = 20;
|
|
1253
1127
|
|
|
1254
1128
|
return (
|
|
1255
|
-
<
|
|
1256
|
-
<
|
|
1257
|
-
<
|
|
1258
|
-
</
|
|
1259
|
-
<
|
|
1129
|
+
<Box flexDirection="column" paddingX={2}>
|
|
1130
|
+
<Box height={1}>
|
|
1131
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{"After launch shortcuts"}</Text>
|
|
1132
|
+
</Box>
|
|
1133
|
+
<Box height={1} />
|
|
1260
1134
|
|
|
1261
1135
|
{keyboardShortcuts.map((s) => (
|
|
1262
|
-
<
|
|
1263
|
-
<
|
|
1264
|
-
<
|
|
1265
|
-
</
|
|
1136
|
+
<Box key={s.key} height={1} flexDirection="row">
|
|
1137
|
+
<Text fg={colors.text} attributes={TextAttributes.BOLD}>{s.key.padEnd(COL)}</Text>
|
|
1138
|
+
<Text fg={colors.textDim}>{s.desc}</Text>
|
|
1139
|
+
</Box>
|
|
1266
1140
|
))}
|
|
1267
1141
|
|
|
1268
|
-
<
|
|
1269
|
-
<
|
|
1270
|
-
<
|
|
1271
|
-
</
|
|
1272
|
-
<
|
|
1273
|
-
<
|
|
1274
|
-
<
|
|
1275
|
-
</
|
|
1276
|
-
<
|
|
1142
|
+
<Box height={2} />
|
|
1143
|
+
<Box height={1}>
|
|
1144
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{"Command-bar prefixes"}</Text>
|
|
1145
|
+
</Box>
|
|
1146
|
+
<Box height={1} />
|
|
1147
|
+
<Box height={1}>
|
|
1148
|
+
<Text fg={colors.textDim}>{"Type these in the command bar ("}<Span fg={colors.text}><Strong>{"Ctrl+P"}</Strong></Span>{" or "}<Span fg={colors.text}><Strong>{"`"}</Strong></Span>{"):"}</Text>
|
|
1149
|
+
</Box>
|
|
1150
|
+
<Box height={1} />
|
|
1277
1151
|
|
|
1278
1152
|
{commandPrefixes.map((s) => (
|
|
1279
|
-
<
|
|
1280
|
-
<
|
|
1281
|
-
<
|
|
1282
|
-
</
|
|
1153
|
+
<Box key={s.key} height={1} flexDirection="row">
|
|
1154
|
+
<Text fg={colors.text} attributes={TextAttributes.BOLD}>{s.key.padEnd(COL)}</Text>
|
|
1155
|
+
<Text fg={colors.textDim}>{s.desc}</Text>
|
|
1156
|
+
</Box>
|
|
1283
1157
|
))}
|
|
1284
1158
|
|
|
1285
|
-
<
|
|
1286
|
-
<
|
|
1287
|
-
<
|
|
1288
|
-
</
|
|
1289
|
-
</
|
|
1159
|
+
<Box height={2} />
|
|
1160
|
+
<Box height={1}>
|
|
1161
|
+
<Text fg={colors.textDim}>{"Everything is searchable, just type what you want."}</Text>
|
|
1162
|
+
</Box>
|
|
1163
|
+
</Box>
|
|
1290
1164
|
);
|
|
1291
1165
|
}
|
|
1292
1166
|
|
|
@@ -1307,58 +1181,58 @@ function ReadyStep({
|
|
|
1307
1181
|
const positionLabel = positionsImported === 1 ? "position" : "positions";
|
|
1308
1182
|
|
|
1309
1183
|
return (
|
|
1310
|
-
<
|
|
1311
|
-
<
|
|
1312
|
-
<
|
|
1313
|
-
<
|
|
1314
|
-
</
|
|
1315
|
-
<
|
|
1316
|
-
<
|
|
1317
|
-
<
|
|
1318
|
-
<
|
|
1319
|
-
</
|
|
1320
|
-
<
|
|
1321
|
-
<
|
|
1322
|
-
<
|
|
1184
|
+
<Box flexDirection="column" paddingX={2}>
|
|
1185
|
+
<Box height={2} />
|
|
1186
|
+
<Box height={1}>
|
|
1187
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{"You're all set"}</Text>
|
|
1188
|
+
</Box>
|
|
1189
|
+
<Box height={2} />
|
|
1190
|
+
<Box height={1}>
|
|
1191
|
+
<Text fg={colors.positive} attributes={TextAttributes.BOLD}>{"\u2713"}</Text>
|
|
1192
|
+
<Text fg={colors.text}>{" Theme configured"}</Text>
|
|
1193
|
+
</Box>
|
|
1194
|
+
<Box height={1}>
|
|
1195
|
+
<Text fg={colors.positive} attributes={TextAttributes.BOLD}>{"\u2713"}</Text>
|
|
1196
|
+
<Text fg={colors.text}>
|
|
1323
1197
|
{brokerName
|
|
1324
1198
|
? ` ${brokerName} connected. Imported ${positionsImported} ${positionLabel}`
|
|
1325
1199
|
: ` Portfolio "${portfolioName}" created`}
|
|
1326
|
-
</
|
|
1327
|
-
</
|
|
1328
|
-
<
|
|
1329
|
-
<
|
|
1330
|
-
<
|
|
1331
|
-
</
|
|
1332
|
-
<
|
|
1200
|
+
</Text>
|
|
1201
|
+
</Box>
|
|
1202
|
+
<Box height={1}>
|
|
1203
|
+
<Text fg={colors.positive} attributes={TextAttributes.BOLD}>{"\u2713"}</Text>
|
|
1204
|
+
<Text fg={colors.text}>{" Plugins enabled"}</Text>
|
|
1205
|
+
</Box>
|
|
1206
|
+
<Box height={2} />
|
|
1333
1207
|
{brokerName ? (
|
|
1334
|
-
<
|
|
1335
|
-
<
|
|
1208
|
+
<Box height={1}>
|
|
1209
|
+
<Text fg={isFinishing ? colors.text : colors.textDim}>
|
|
1336
1210
|
{isFinishing
|
|
1337
1211
|
? "Launching Gloomberb..."
|
|
1338
1212
|
: positionsImported > 0
|
|
1339
1213
|
? "Your broker portfolio is ready and will open directly after launch."
|
|
1340
1214
|
: "Broker sync finished. If you expected holdings, check the selected account or connection mode."}
|
|
1341
|
-
</
|
|
1342
|
-
</
|
|
1215
|
+
</Text>
|
|
1216
|
+
</Box>
|
|
1343
1217
|
) : (
|
|
1344
|
-
<
|
|
1345
|
-
<
|
|
1346
|
-
</
|
|
1218
|
+
<Box height={1}>
|
|
1219
|
+
<Text fg={colors.textDim}>{"Search for broker names in the command bar to connect."}</Text>
|
|
1220
|
+
</Box>
|
|
1347
1221
|
)}
|
|
1348
1222
|
{error && (
|
|
1349
1223
|
<>
|
|
1350
|
-
<
|
|
1351
|
-
<
|
|
1352
|
-
<
|
|
1353
|
-
</
|
|
1224
|
+
<Box height={1} />
|
|
1225
|
+
<Box height={2}>
|
|
1226
|
+
<Text fg={colors.negative}>{error}</Text>
|
|
1227
|
+
</Box>
|
|
1354
1228
|
</>
|
|
1355
1229
|
)}
|
|
1356
|
-
<
|
|
1357
|
-
<
|
|
1358
|
-
<
|
|
1359
|
-
<
|
|
1360
|
-
</
|
|
1361
|
-
<
|
|
1362
|
-
</
|
|
1230
|
+
<Box height={2} />
|
|
1231
|
+
<Box height={1} flexDirection="row">
|
|
1232
|
+
<Text fg={colors.textDim}>{"Data stored in "}</Text>
|
|
1233
|
+
<Text fg={colors.text}>{"~/gloomberb/"}</Text>
|
|
1234
|
+
</Box>
|
|
1235
|
+
<Box height={1} />
|
|
1236
|
+
</Box>
|
|
1363
1237
|
);
|
|
1364
1238
|
}
|