@gloom-sh/terminal 0.19.1 → 0.19.3
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 +7 -1
- package/package.json +2 -1
- package/patches/electrobun@1.18.1.patch +108 -1
- package/src/account/upgrade-dialog.tsx +4 -23
- package/src/api-client/coalesce.ts +118 -0
- package/src/api-client/credit-boards.ts +13 -0
- package/src/api-client/crypto-etf-flows.ts +6 -0
- package/src/api-client/crypto-markets.ts +40 -0
- package/src/api-client/data.ts +201 -14
- package/src/api-client/equity-screener.ts +10 -0
- package/src/api-client/errors.ts +58 -0
- package/src/api-client/gpu.ts +29 -0
- package/src/api-client/index.ts +5 -2
- package/src/api-client/plan-access.ts +10 -1
- package/src/api-client/plan-rules.ts +35 -0
- package/src/api-client/research-activity.ts +1 -32
- package/src/api-client/types.ts +21 -1
- package/src/app/attention-notice.tsx +94 -0
- package/src/app/runtime/startup.ts +3 -1
- package/src/app.tsx +23 -1
- package/src/architecture/core-builtin-allowlist.json +2 -1
- package/src/capabilities/chart-series.ts +2 -1
- package/src/capabilities/factories.ts +1 -0
- package/src/cli/ascii-output.ts +42 -0
- package/src/cli/base-converter.ts +10 -2
- package/src/cli/command-options.ts +16 -1
- package/src/cli/commands/crypto-hints.ts +23 -4
- package/src/cli/commands/market.ts +101 -27
- package/src/cli/commands/news-search.ts +24 -5
- package/src/cli/commands/overview.ts +14 -10
- package/src/cli/commands/search.ts +84 -25
- package/src/cli/commands/system.ts +22 -30
- package/src/cli/commands/ticker.ts +49 -9
- package/src/cli/commands/version.ts +33 -0
- package/src/cli/context.ts +6 -9
- package/src/cli/data-dir.ts +7 -0
- package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
- package/src/cli/desktop-pane-shot-browser.ts +320 -0
- package/src/cli/desktop-pane-shot-routes.ts +26 -1
- package/src/cli/desktop-pane-shot.ts +196 -256
- package/src/cli/early-command.ts +42 -0
- package/src/cli/earnings-calendar.ts +9 -0
- package/src/cli/entry.ts +19 -13
- package/src/cli/errors.ts +12 -2
- package/src/cli/glossary.ts +2 -1
- package/src/cli/help.ts +1 -1
- package/src/cli/history-export.ts +22 -2
- package/src/cli/history-rows.ts +189 -11
- package/src/cli/index.ts +18 -1
- package/src/cli/known-gaps.ts +51 -5
- package/src/cli/listing-arg.ts +63 -10
- package/src/cli/options.ts +1 -1
- package/src/cli/output-file.ts +60 -13
- package/src/cli/pane-functions/catalog.ts +14 -3
- package/src/cli/pane-functions/data.ts +1 -1
- package/src/cli/pane-functions/dom.ts +45 -0
- package/src/cli/pane-functions/freshness.ts +46 -9
- package/src/cli/pane-functions/function-help.ts +17 -9
- package/src/cli/pane-functions/headless.ts +22 -14
- package/src/cli/pane-functions/index.ts +6 -2
- package/src/cli/pane-functions/options.ts +6 -2
- package/src/cli/pane-functions/resolver.ts +27 -5
- package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
- package/src/cli/pane-functions/screenshot-http.ts +70 -0
- package/src/cli/pane-functions/screenshot-render.ts +19 -5
- package/src/cli/quote-answer.ts +49 -6
- package/src/cli/registry.ts +41 -13
- package/src/cli/result.ts +12 -6
- package/src/cli/ticker-resolution.ts +2 -1
- package/src/cli/types.ts +2 -0
- package/src/components/chart/composite/format.ts +3 -0
- package/src/components/chart-table/header.tsx +1 -1
- package/src/components/chart-table/layout.ts +28 -11
- package/src/components/command-bar/action-runtime.ts +4 -0
- package/src/components/command-bar/commands/direct/index.ts +18 -33
- package/src/components/command-bar/commands/direct/runtime.ts +5 -0
- package/src/components/command-bar/commands/registry.ts +10 -1
- package/src/components/command-bar/routes/root/command-items.ts +9 -4
- package/src/components/command-bar/routes/root/runtime.ts +51 -6
- package/src/components/command-bar/selection-runtime.ts +12 -29
- package/src/components/command-bar/surface/index.tsx +9 -3
- package/src/components/company-logo.tsx +2 -1
- package/src/components/layout/detached-pane-shell.tsx +2 -2
- package/src/components/layout/header.tsx +10 -5
- package/src/components/layout/market-summary.ts +25 -7
- package/src/components/layout/pane/footer/index.tsx +8 -2
- package/src/components/layout/pane/footer/model.ts +73 -6
- package/src/components/layout/pane/header.tsx +2 -2
- package/src/components/layout/pane/status-footer.ts +2 -1
- package/src/components/market-board/index.tsx +3 -2
- package/src/components/onboarding/desks-step.tsx +12 -4
- package/src/components/onboarding/first-run-workspace.ts +81 -4
- package/src/components/onboarding/onboarding-frame.tsx +4 -2
- package/src/components/onboarding/onboarding-keyboard.ts +8 -5
- package/src/components/onboarding/onboarding-wizard.tsx +60 -26
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
- package/src/components/onboarding/run-setup-again.ts +24 -0
- package/src/components/onboarding/use-onboarding-progress.ts +33 -15
- package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
- package/src/components/onboarding/wizard-model.ts +57 -3
- package/src/components/onboarding/wizard-positions.ts +1 -1
- package/src/components/ui/delayed-mark.tsx +56 -0
- package/src/components/ui/display.tsx +16 -5
- package/src/components/ui/external-link.tsx +3 -6
- package/src/components/ui/icon.tsx +16 -3
- package/src/components/ui/logo-mark.tsx +8 -5
- package/src/components/ui/stat-grid.tsx +263 -19
- package/src/core/app-services.ts +11 -2
- package/src/data/app-persistence.ts +9 -7
- package/src/data/config/store/node.ts +11 -5
- package/src/data/config/store/normalize.ts +16 -3
- package/src/data/plugin-cache.ts +15 -0
- package/src/data/plugin-state-store.ts +81 -24
- package/src/data/profile-permissions.ts +47 -0
- package/src/data/resource-store.ts +165 -90
- package/src/data/session-store.ts +31 -8
- package/src/data/sqlite/access.ts +324 -0
- package/src/data/sqlite/checkpointer.ts +285 -0
- package/src/data/sqlite/database.ts +80 -4
- package/src/data/sqlite/retry.ts +50 -9
- package/src/data/ticker-store.ts +57 -23
- package/src/docs/snapshot.json +191 -78
- package/src/i18n/es.ts +18 -2
- package/src/i18n/ja.ts +18 -2
- package/src/i18n/ko.ts +18 -2
- package/src/i18n/zh-cn.ts +18 -2
- package/src/i18n/zh-tw.ts +18 -2
- package/src/layout/desk-keys.ts +4 -1
- package/src/layout/desks.ts +38 -5
- package/src/layout-marketplace/gallery-desktop.tsx +45 -16
- package/src/market-data/batched-reads.ts +16 -0
- package/src/market-data/coordinator/auxiliary.ts +4 -2
- package/src/market-data/coordinator/index.ts +11 -2
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +3 -1
- package/src/market-data/market/index-level.ts +6 -4
- package/src/market-data/published-apac-sessions.ts +6 -0
- package/src/market-data/realized-volatility.ts +4 -2
- package/src/news/aggregator.ts +7 -4
- package/src/news/news-model.ts +11 -0
- package/src/news/story-id.ts +22 -0
- package/src/news/ticker-symbols.ts +17 -1
- package/src/notifications/app-notifier.ts +35 -14
- package/src/plugins/auto-update-node.ts +10 -6
- package/src/plugins/builtin/account-management/form-components.tsx +1 -1
- package/src/plugins/builtin/account-management/index.tsx +2 -3
- package/src/plugins/builtin/account-management/model.ts +1 -1
- package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
- package/src/plugins/builtin/alerts/events.ts +10 -2
- package/src/plugins/builtin/alerts/index.tsx +5 -5
- package/src/plugins/builtin/alerts/research-builder.ts +5 -1
- package/src/plugins/builtin/alerts/research-rules.ts +18 -0
- package/src/plugins/builtin/alerts/targets.ts +4 -1
- package/src/plugins/builtin/attention/headless.ts +1 -1
- package/src/plugins/builtin/attention/index.tsx +2 -4
- package/src/plugins/builtin/attention/listing.ts +1 -1
- package/src/plugins/builtin/attention/series.ts +1 -1
- package/src/plugins/builtin/awards/index.tsx +1 -2
- package/src/plugins/builtin/backtest/client.ts +1 -0
- package/src/plugins/builtin/backtest/index.tsx +1 -2
- package/src/plugins/builtin/backtest/rules.ts +3 -1
- package/src/plugins/builtin/backtest/window.ts +8 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
- package/src/plugins/builtin/broker-manager/index.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +5 -0
- package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
- package/src/plugins/builtin/buildout/company/id.ts +2 -0
- package/src/plugins/builtin/buildout/company/model.ts +247 -0
- package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
- package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
- package/src/plugins/builtin/buildout/facilities.ts +309 -0
- package/src/plugins/builtin/buildout/format.ts +9 -0
- package/src/plugins/builtin/buildout/index.tsx +51 -5
- package/src/plugins/builtin/buildout/model/index.ts +62 -10
- package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
- package/src/plugins/builtin/buildout/model/types.ts +11 -2
- package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
- package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
- package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
- package/src/plugins/builtin/buildout/table-model.ts +26 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
- package/src/plugins/builtin/cds/headless.ts +10 -2
- package/src/plugins/builtin/cds/model.ts +7 -3
- package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
- package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
- package/src/plugins/builtin/central-bank-rates/series.ts +2 -3
- package/src/plugins/builtin/changelog/index.tsx +3 -2
- package/src/plugins/builtin/chart-composer/cli-options.ts +16 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -0
- package/src/plugins/builtin/chart-composer/headless.ts +88 -21
- package/src/plugins/builtin/chart-composer/index.tsx +2 -3
- package/src/plugins/builtin/chart-composer/pane.tsx +30 -2
- package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
- package/src/plugins/builtin/chart-composer/session-range.ts +5 -2
- package/src/plugins/builtin/chart-composer/studies.ts +1 -1
- package/src/plugins/builtin/chat/controller/index.ts +1 -1
- package/src/plugins/builtin/chat/controller/messages.ts +15 -4
- package/src/plugins/builtin/chat/ghost-user.ts +8 -0
- package/src/plugins/builtin/chat/index.tsx +5 -5
- package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/index.tsx +1 -2
- package/src/plugins/builtin/cloud/plugin.tsx +6 -4
- package/src/plugins/builtin/cloud/team/module.tsx +1 -1
- package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
- package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
- package/src/plugins/builtin/comment-letters/index.tsx +1 -2
- package/src/plugins/builtin/commodity-balances/client.ts +4 -1
- package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
- package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
- package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
- package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
- package/src/plugins/builtin/congress-trades/headless.ts +2 -0
- package/src/plugins/builtin/congress-trades/model.ts +92 -64
- package/src/plugins/builtin/congress-trades/table.tsx +14 -2
- package/src/plugins/builtin/connections/index.ts +2 -3
- package/src/plugins/builtin/cot/client.ts +28 -2
- package/src/plugins/builtin/cot/headless.ts +4 -1
- package/src/plugins/builtin/cot/index.tsx +5 -4
- package/src/plugins/builtin/cot/model.ts +58 -7
- package/src/plugins/builtin/cot/pane.tsx +6 -2
- package/src/plugins/builtin/cpi/client.ts +6 -5
- package/src/plugins/builtin/cpi/index.tsx +1 -2
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
- package/src/plugins/builtin/credit-boards/client.ts +7 -2
- package/src/plugins/builtin/credit-boards/headless.ts +25 -12
- package/src/plugins/builtin/credit-boards/model.ts +29 -0
- package/src/plugins/builtin/credit-boards/series.ts +3 -4
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
- package/src/plugins/builtin/credit-conditions/client.ts +22 -14
- package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
- package/src/plugins/builtin/crypto-board/client.ts +5 -5
- package/src/plugins/builtin/crypto-board/headless.ts +4 -3
- package/src/plugins/builtin/crypto-board/index.tsx +4 -2
- package/src/plugins/builtin/crypto-board/series.ts +6 -4
- package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
- package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
- package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
- package/src/plugins/builtin/debt-maturities/model.ts +17 -1
- package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
- package/src/plugins/builtin/debt-maturities/series.ts +3 -3
- package/src/plugins/builtin/debug/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/client.ts +32 -2
- package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
- package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
- package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
- package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
- package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
- package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
- package/src/plugins/builtin/dividend-yield/view.ts +21 -0
- package/src/plugins/builtin/docs/index.tsx +2 -3
- package/src/plugins/builtin/doe/client.ts +6 -5
- package/src/plugins/builtin/doe/headless.ts +16 -3
- package/src/plugins/builtin/doe/index.tsx +5 -4
- package/src/plugins/builtin/doe/prices.ts +2 -3
- package/src/plugins/builtin/doe/series.ts +4 -4
- package/src/plugins/builtin/earnings/board.tsx +2 -1
- package/src/plugins/builtin/earnings/history.tsx +2 -1
- package/src/plugins/builtin/earnings-calls/calendar.tsx +10 -2
- package/src/plugins/builtin/earnings-calls/data.ts +19 -11
- package/src/plugins/builtin/earnings-calls/pane.tsx +27 -14
- package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
- package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
- package/src/plugins/builtin/econ/calendar-model.ts +29 -6
- package/src/plugins/builtin/econ/detail-view.tsx +16 -2
- package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/headless.ts +1 -0
- package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
- package/src/plugins/builtin/equity-screener/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/model.ts +28 -4
- package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
- package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
- package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
- package/src/plugins/builtin/executives/data.ts +6 -1
- package/src/plugins/builtin/executives/pane.tsx +9 -21
- package/src/plugins/builtin/exposure/holdings.ts +35 -13
- package/src/plugins/builtin/exposure/index.tsx +1 -2
- package/src/plugins/builtin/fear-greed/cache.ts +4 -0
- package/src/plugins/builtin/fear-greed/cli.ts +44 -0
- package/src/plugins/builtin/fear-greed/index.tsx +5 -53
- package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
- package/src/plugins/builtin/fear-greed/series.ts +6 -4
- package/src/plugins/builtin/fixed-income/index.tsx +1 -2
- package/src/plugins/builtin/futures/board-reads.ts +25 -0
- package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
- package/src/plugins/builtin/futures/index.tsx +15 -0
- package/src/plugins/builtin/futures/model.ts +14 -0
- package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
- package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
- package/src/plugins/builtin/futures-curve/headless.ts +6 -1
- package/src/plugins/builtin/futures-curve/index.tsx +1 -2
- package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
- package/src/plugins/builtin/gpu/board.tsx +51 -12
- package/src/plugins/builtin/gpu/client.ts +19 -3
- package/src/plugins/builtin/gpu/equities.tsx +5 -2
- package/src/plugins/builtin/gpu/headless.ts +14 -2
- package/src/plugins/builtin/gpu/index.tsx +1 -2
- package/src/plugins/builtin/gpu/model.ts +20 -2
- package/src/plugins/builtin/gpu/pane.tsx +1 -1
- package/src/plugins/builtin/gpu/series.ts +2 -3
- package/src/plugins/builtin/help/index.tsx +1 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +1 -2
- package/src/plugins/builtin/holders/index-holders.ts +24 -12
- package/src/plugins/builtin/holders/index.tsx +2 -3
- package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
- package/src/plugins/builtin/iv-history/index.tsx +1 -2
- package/src/plugins/builtin/jodi/client.ts +4 -1
- package/src/plugins/builtin/jodi/index.tsx +1 -2
- package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
- package/src/plugins/builtin/layout-manager/index.tsx +9 -8
- package/src/plugins/builtin/macro-day/pane.tsx +4 -2
- package/src/plugins/builtin/market-halts/index.tsx +1 -2
- package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
- package/src/plugins/builtin/market-movers/client.ts +1 -0
- package/src/plugins/builtin/market-movers/index.tsx +16 -5
- package/src/plugins/builtin/market-movers/screener.ts +23 -6
- package/src/plugins/builtin/market-movers/session-body.tsx +6 -2
- package/src/plugins/builtin/market-valuation/index.tsx +1 -2
- package/src/plugins/builtin/members/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/pane.tsx +2 -1
- package/src/plugins/builtin/money-markets/series.ts +3 -5
- package/src/plugins/builtin/news/headless.ts +7 -0
- package/src/plugins/builtin/news/wire/index.tsx +1 -1
- package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
- package/src/plugins/builtin/notes/index.tsx +3 -6
- package/src/plugins/builtin/openfda/index.tsx +1 -2
- package/src/plugins/builtin/options/view.tsx +6 -5
- package/src/plugins/builtin/options-calculator/index.tsx +1 -2
- package/src/plugins/builtin/options-positioning/index.tsx +1 -2
- package/src/plugins/builtin/options-scenario/index.tsx +2 -4
- package/src/plugins/builtin/perps/index.tsx +1 -2
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
- package/src/plugins/builtin/plugin-module.ts +16 -2
- package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
- package/src/plugins/builtin/power/gas-client.ts +2 -1
- package/src/plugins/builtin/power/index.tsx +1 -2
- package/src/plugins/builtin/power/prices-client.ts +3 -2
- package/src/plugins/builtin/rate-path/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/headless.ts +2 -0
- package/src/plugins/builtin/realized-vol/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/model.ts +16 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
- package/src/plugins/builtin/research/analyst-headless.ts +33 -6
- package/src/plugins/builtin/research/analyst-model.ts +37 -2
- package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
- package/src/plugins/builtin/research/index.tsx +5 -8
- package/src/plugins/builtin/research-search/index.tsx +1 -2
- package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
- package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
- package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
- package/src/plugins/builtin/risk-factors/data.ts +8 -4
- package/src/plugins/builtin/risk-factors/index.tsx +3 -1
- package/src/plugins/builtin/risk-factors/pane.tsx +5 -8
- package/src/plugins/builtin/roadmap/index.tsx +2 -3
- package/src/plugins/builtin/roadmap/pane.tsx +49 -2
- package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
- package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
- package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
- package/src/plugins/builtin/scanner/lock-note.ts +26 -3
- package/src/plugins/builtin/sec/filing-content.ts +38 -6
- package/src/plugins/builtin/sec/headless.ts +5 -1
- package/src/plugins/builtin/sec/index.tsx +4 -2
- package/src/plugins/builtin/sec/model.ts +2 -0
- package/src/plugins/builtin/sectors/index.tsx +11 -5
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +73 -8
- package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
- package/src/plugins/builtin/shared/forming-bars.ts +29 -0
- package/src/plugins/builtin/shared/fred-points.ts +6 -1
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +23 -8
- package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
- package/src/plugins/builtin/shared/series-warning.ts +13 -4
- package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/series.ts +2 -3
- package/src/plugins/builtin/social-mentions/client.ts +15 -10
- package/src/plugins/builtin/social-mentions/index.tsx +1 -2
- package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
- package/src/plugins/builtin/social-mentions/series.ts +2 -3
- package/src/plugins/builtin/supply-chain/client.ts +22 -1
- package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
- package/src/plugins/builtin/supply-chain/index.tsx +1 -2
- package/src/plugins/builtin/thirteenf/api.ts +127 -29
- package/src/plugins/builtin/thirteenf/data.ts +165 -35
- package/src/plugins/builtin/thirteenf/index.tsx +2 -4
- package/src/plugins/builtin/thirteenf/model.ts +2 -0
- package/src/plugins/builtin/thirteenf/overlap-pane.tsx +5 -1
- package/src/plugins/builtin/thirteenf/pane.tsx +41 -16
- package/src/plugins/builtin/thirteenf/types.ts +14 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -6
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
- package/src/plugins/builtin/ticker-detail/financials/model.ts +35 -2
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +53 -18
- package/src/plugins/builtin/ticker-detail/headless.ts +70 -11
- package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
- package/src/plugins/builtin/time-sales/index.tsx +1 -2
- package/src/plugins/builtin/time-sales/pane.tsx +3 -1
- package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
- package/src/plugins/builtin/trace-bonds/model.ts +44 -10
- package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
- package/src/plugins/builtin/treasury-auctions/cache.ts +20 -8
- package/src/plugins/builtin/treasury-auctions/client.ts +3 -3
- package/src/plugins/builtin/treasury-auctions/headless.ts +3 -1
- package/src/plugins/builtin/treasury-auctions/pane.tsx +10 -4
- package/src/plugins/builtin/vol-surface/client.ts +18 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -2
- package/src/plugins/builtin/volatility/client.ts +43 -16
- package/src/plugins/builtin/volatility/headless.ts +5 -2
- package/src/plugins/builtin/volatility/index.tsx +1 -2
- package/src/plugins/builtin/volatility/model.ts +17 -6
- package/src/plugins/builtin/volatility/pane.tsx +6 -2
- package/src/plugins/builtin/volatility/series.ts +2 -1
- package/src/plugins/builtin/world-indices/footer.ts +6 -1
- package/src/plugins/builtin/world-venue-map/client.ts +10 -1
- package/src/plugins/builtin/world-venue-map/index.ts +6 -2
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
- package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
- package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
- package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
- package/src/plugins/builtin/yield-curve/client.ts +4 -6
- package/src/plugins/builtin/yield-curve/curves.ts +16 -2
- package/src/plugins/builtin/yield-curve/headless.ts +3 -3
- package/src/plugins/bundle.ts +52 -2
- package/src/plugins/catalog-browser.ts +2 -2
- package/src/plugins/catalog-cli.ts +20 -0
- package/src/plugins/host-link.ts +13 -1
- package/src/plugins/installer.ts +64 -42
- package/src/plugins/loader.ts +43 -11
- package/src/plugins/peer-imports.ts +201 -0
- package/src/plugins/peer-retry.ts +26 -29
- package/src/plugins/plugin-process.ts +128 -0
- package/src/plugins/redirection-guard.ts +45 -0
- package/src/remote/endpoint.ts +16 -3
- package/src/renderers/browser/main.tsx +3 -2
- package/src/renderers/cloudflare/http-proxy.ts +7 -123
- package/src/renderers/dom/desktop/icons.tsx +5 -2
- package/src/renderers/dom/host/image-surface.tsx +46 -21
- package/src/renderers/dom/styles.css +7 -0
- package/src/renderers/dom/toast-host.tsx +2 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +23 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +12 -3
- package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
- package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
- package/src/renderers/electrobun/bun/window/attention.ts +54 -0
- package/src/renderers/electrobun/bun/window/create.ts +6 -2
- package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
- package/src/renderers/electrobun/view/main.tsx +7 -2
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
- package/src/renderers/electrobun/view/ui-host.tsx +9 -2
- package/src/renderers/opentui/browser-open.ts +10 -4
- package/src/renderers/opentui/image/surface.tsx +1 -1
- package/src/renderers/opentui/start.tsx +12 -6
- package/src/renderers/opentui/toast-host.tsx +2 -1
- package/src/series/registry.ts +18 -0
- package/src/series/resolve.ts +2 -1
- package/src/series/standalone.ts +10 -1
- package/src/series/types.ts +12 -0
- package/src/series/validate.ts +11 -2
- package/src/sources/gloomberb-cloud/fred-series.ts +5 -3
- package/src/sources/gloomberb-cloud/index.ts +28 -5
- package/src/sources/gloomberb-cloud/news.ts +24 -3
- package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
- package/src/sources/provider-errors.ts +26 -3
- package/src/sources/provider-router/history.ts +5 -0
- package/src/sources/provider-router/index.ts +13 -0
- package/src/sync/core-contributors.ts +32 -0
- package/src/telemetry/attention-counts.ts +64 -6
- package/src/telemetry/attention-setting.ts +33 -0
- package/src/telemetry/crash-reports-node.ts +3 -1
- package/src/telemetry/stall-clocks.ts +93 -0
- package/src/telemetry/stall-reports-node.ts +25 -9
- package/src/telemetry/stall-reports.ts +218 -13
- package/src/test-support/outage.ts +8 -0
- package/src/test-support/preload.ts +9 -0
- package/src/tickers/company-name-listing.ts +59 -29
- package/src/tickers/company-name.ts +84 -1
- package/src/tickers/listing-fate.ts +14 -3
- package/src/tickers/saved-instrument.ts +17 -0
- package/src/tickers/search/home-listing.ts +104 -8
- package/src/tickers/search/index.ts +5 -2
- package/src/tickers/search/types.ts +6 -1
- package/src/time-series/bar-aggregation.ts +7 -1
- package/src/time-series/base-series.ts +3 -3
- package/src/time-series/chart-bounds.ts +1 -1
- package/src/time-series/chart-data.ts +5 -2
- package/src/time-series/chart-seed.ts +1 -1
- package/src/time-series/coin-session-close.ts +211 -0
- package/src/time-series/live-bars.ts +13 -2
- package/src/time-series/live-quotes.ts +1 -1
- package/src/time-series/market.ts +5 -3
- package/src/time-series/official-close.ts +159 -0
- package/src/time-series/price-comparison.ts +75 -8
- package/src/time-series/resolve.ts +32 -4
- package/src/time-series/series-presentation.ts +1 -1
- package/src/time-series/studies/formula.ts +205 -0
- package/src/time-series/studies/indicators.ts +299 -0
- package/src/time-series/studies/leverage.ts +75 -0
- package/src/time-series/studies/pair.ts +138 -0
- package/src/time-series/studies/resolve.ts +333 -0
- package/src/time-series/studies/shared.ts +141 -0
- package/src/time-series/studies/units.ts +243 -0
- package/src/time-series/studies/vwap.ts +274 -0
- package/src/time-series/types.ts +7 -0
- package/src/types/config.ts +18 -2
- package/src/types/data-provider.ts +23 -0
- package/src/types/financials.ts +40 -3
- package/src/types/headless.ts +42 -3
- package/src/types/plugin.ts +32 -3
- package/src/ui/host.tsx +17 -1
- package/src/ui/toast.tsx +26 -1
- package/src/utils/ascii-text.ts +140 -0
- package/src/utils/cli-output.ts +91 -18
- package/src/utils/concurrency.ts +24 -0
- package/src/utils/exchanges.ts +6 -0
- package/src/utils/external-url.ts +20 -7
- package/src/utils/financial-statements.ts +73 -16
- package/src/utils/fundamentals.ts +17 -5
- package/src/utils/futures-generic.ts +4 -4
- package/src/utils/http-proxy-upstream.ts +124 -0
- package/src/utils/lease-liabilities.ts +54 -0
- package/src/utils/main-thread-stall.ts +122 -5
- package/src/utils/perf-marks.ts +58 -12
- package/src/utils/price-history-integrity.ts +47 -1
- package/src/utils/private-files.ts +141 -0
- package/src/utils/public-address.ts +99 -0
- package/src/account/upgrade-personalization.ts +0 -57
- package/src/account/upgrade-tickers.ts +0 -124
- package/src/api-client/upgrade-experiment.ts +0 -91
- package/src/plugins/builtin/cloud/browser.tsx +0 -3
- package/src/time-series/studies.ts +0 -1628
|
@@ -2,8 +2,8 @@ import { useCallback, useEffect, useRef, type MutableRefObject } from "react";
|
|
|
2
2
|
import type { AppState } from "../../state/app/context";
|
|
3
3
|
import type { TickerRecord } from "../../types/ticker";
|
|
4
4
|
import { t } from "../../i18n";
|
|
5
|
-
import {
|
|
6
|
-
import { isKnownExchangeCode
|
|
5
|
+
import { listingChoiceQuery } from "../../tickers/search";
|
|
6
|
+
import { isKnownExchangeCode } from "../../utils/exchanges";
|
|
7
7
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
8
8
|
import type { Command } from "./commands/registry";
|
|
9
9
|
import type {
|
|
@@ -294,37 +294,21 @@ export function useCommandBarSelectionRuntime({
|
|
|
294
294
|
const activateListSelection = useCallback((options?: { secondary?: boolean; item?: ResultItem }) => {
|
|
295
295
|
const listState = visibleListStateRef.current;
|
|
296
296
|
if (!listState) return;
|
|
297
|
-
// Enter
|
|
298
|
-
//
|
|
299
|
-
//
|
|
297
|
+
// Enter opens exactly the highlighted row. The typed command runs as typed
|
|
298
|
+
// only while no listing for it is highlighted: before its search answers
|
|
299
|
+
// (`DES HFC` then opens the listing `quote HFC` reads), or while the rows
|
|
300
|
+
// on screen are an earlier query's.
|
|
300
301
|
const queryChanged = rootQueryRef.current !== rootQuery;
|
|
301
302
|
const tickerArgument = rootShortcutIntent.kind !== "none" && rootShortcutIntent.argKind === "ticker"
|
|
302
303
|
? rootShortcutIntent.argText : null;
|
|
303
304
|
const listingQuery = listingChoiceQuery(tickerArgument ?? rootQueryRef.current);
|
|
304
305
|
const choosingVenue = listingQuery && !isKnownExchangeCode(listingQuery.suffix);
|
|
305
|
-
const
|
|
306
|
+
const highlighted = listState.results[listState.selectedIdx];
|
|
307
|
+
const listingHighlighted = !queryChanged && (highlighted?.kind === "ticker" || highlighted?.kind === "search");
|
|
308
|
+
const submitTyped = !options?.item && !currentRoute && !listingHighlighted
|
|
306
309
|
&& (queryChanged || (!rootSelectionNavigatedRef.current && !choosingVenue));
|
|
307
|
-
|
|
310
|
+
const typed = submitTyped && (queryChanged || tickerArgument !== null)
|
|
308
311
|
? resolveImmediateRootSelection(rootQueryRef.current) : null;
|
|
309
|
-
const first = listState.results[listState.selectedIdx];
|
|
310
|
-
if (typed && !queryChanged && rootShortcutIntent.kind !== "none" && rootShortcutIntent.argKind === "ticker"
|
|
311
|
-
&& first && (first.kind === "ticker" || first.kind === "search")) {
|
|
312
|
-
const argument = rootShortcutIntent.argText;
|
|
313
|
-
// Company names and partial symbols still accept the suggested match.
|
|
314
|
-
// An exact symbol or named venue goes through listing resolution.
|
|
315
|
-
if (!parsePublicTickerKey(argument).exchange && !findExactTickerSearchMatch(
|
|
316
|
-
listState.results.filter((item) => item.kind === "ticker" || item.kind === "search"), argument,
|
|
317
|
-
)) typed = null;
|
|
318
|
-
}
|
|
319
|
-
// A plain symbol follows the same path once it is the default ticker row.
|
|
320
|
-
// Commands and company-name searches keep their own default actions.
|
|
321
|
-
const plainSymbol = submitTyped && !typed && first && (first.kind === "ticker" || first.kind === "search")
|
|
322
|
-
&& findExactTickerSearchMatch([first], rootQueryRef.current);
|
|
323
|
-
if (plainSymbol && first) {
|
|
324
|
-
const symbol = rootQueryRef.current;
|
|
325
|
-
typed = { ...first, action: () => runSecurityDescriptionShortcut(symbol),
|
|
326
|
-
secondaryAction: () => runSecurityDescriptionShortcut(symbol, { forceNewPane: true }) };
|
|
327
|
-
}
|
|
328
312
|
const selected = options?.item ?? typed ?? listState.results[listState.selectedIdx];
|
|
329
313
|
if (!selected) {
|
|
330
314
|
if (searchPending && !currentRoute && !options?.item && !rootSelectionNavigatedRef.current) {
|
|
@@ -399,8 +383,8 @@ export function useCommandBarSelectionRuntime({
|
|
|
399
383
|
item: selected,
|
|
400
384
|
query,
|
|
401
385
|
rank: selected === typed ? 0 : Math.max(0, listState.results.indexOf(selected)),
|
|
402
|
-
// A
|
|
403
|
-
isShortcut: () => !
|
|
386
|
+
// A listing row stays a ticker choice; a submitted command is the shortcut itself.
|
|
387
|
+
isShortcut: () => !listingHighlighted && (selected === typed || resolveImmediateRootSelection(query)?.id === selected.id),
|
|
404
388
|
}, run);
|
|
405
389
|
}, [
|
|
406
390
|
closeAll,
|
|
@@ -416,7 +400,6 @@ export function useCommandBarSelectionRuntime({
|
|
|
416
400
|
rootSelectionNavigatedRef,
|
|
417
401
|
rootShortcutIntent,
|
|
418
402
|
runRootRow,
|
|
419
|
-
runSecurityDescriptionShortcut,
|
|
420
403
|
searchPending,
|
|
421
404
|
stateConfigLayout,
|
|
422
405
|
stateRef,
|
|
@@ -44,6 +44,8 @@ interface CommandBarProps {
|
|
|
44
44
|
pluginRegistry: PluginRegistry;
|
|
45
45
|
quitApp: () => void;
|
|
46
46
|
onCheckForUpdates?: () => void | Promise<void>;
|
|
47
|
+
/** Settings' Run Setup Again; without it (the browser, or setup already showing) the command is left out. */
|
|
48
|
+
onRunSetupAgain?: () => Promise<void>;
|
|
47
49
|
onNativeOccluderChange?: (rect: LayoutBounds | null) => void;
|
|
48
50
|
}
|
|
49
51
|
|
|
@@ -53,6 +55,7 @@ export function CommandBar({
|
|
|
53
55
|
pluginRegistry,
|
|
54
56
|
quitApp,
|
|
55
57
|
onCheckForUpdates,
|
|
58
|
+
onRunSetupAgain,
|
|
56
59
|
onNativeOccluderChange,
|
|
57
60
|
}: CommandBarProps) {
|
|
58
61
|
const {
|
|
@@ -80,9 +83,11 @@ export function CommandBar({
|
|
|
80
83
|
visibleListStateRef,
|
|
81
84
|
} = useCommandBarEnvironment();
|
|
82
85
|
// Without an updater (the web terminal) neither checking nor a channel means anything.
|
|
83
|
-
const availableCommands = useMemo(() =>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
const availableCommands = useMemo(() => allAvailableCommands.filter((command) => {
|
|
87
|
+
if (command.id === "check-for-updates" || command.id === "release-channel") return !!onCheckForUpdates;
|
|
88
|
+
if (command.id === "run-setup-again") return !!onRunSetupAgain;
|
|
89
|
+
return true;
|
|
90
|
+
}), [allAvailableCommands, onCheckForUpdates, onRunSetupAgain]);
|
|
86
91
|
const {
|
|
87
92
|
applyThemePreview,
|
|
88
93
|
clearThemePreview,
|
|
@@ -200,6 +205,7 @@ export function CommandBar({
|
|
|
200
205
|
dispatch,
|
|
201
206
|
focusedPaneId: state.focusedPaneId,
|
|
202
207
|
onCheckForUpdates,
|
|
208
|
+
onRunSetupAgain,
|
|
203
209
|
persistConfig,
|
|
204
210
|
pluginRegistry,
|
|
205
211
|
pushRoute,
|
|
@@ -84,6 +84,7 @@ export function CompanyLogo({
|
|
|
84
84
|
alignItems="center"
|
|
85
85
|
justifyContent="center"
|
|
86
86
|
objectFit="contain"
|
|
87
|
+
fallbackWhileLoading
|
|
87
88
|
style={{
|
|
88
89
|
width: cellWidthPx * width,
|
|
89
90
|
height: cellHeightPx * height,
|
|
@@ -91,7 +92,7 @@ export function CompanyLogo({
|
|
|
91
92
|
...style,
|
|
92
93
|
}}
|
|
93
94
|
>
|
|
94
|
-
{/* Without a logo the initial fills the same tile, so the header keeps its shape. */}
|
|
95
|
+
{/* Without a logo, or until it loads, the initial fills the same tile, so the header keeps its shape. */}
|
|
95
96
|
<Box width="100%" height="100%" bg={colors.border} alignItems="center" justifyContent="center">
|
|
96
97
|
<Text
|
|
97
98
|
fg={colors.text}
|
|
@@ -170,9 +170,9 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
170
170
|
fireAndForget(desktopWindowBridge.closeDetachedPane?.(desktopWindowBridge.paneId), "desktop.closeDetachedPane");
|
|
171
171
|
}, [desktopWindowBridge, locked, pluginRegistry]);
|
|
172
172
|
|
|
173
|
-
const startWindowDrag = useCallback(() => {
|
|
173
|
+
const startWindowDrag = useCallback((press?: { detail?: number }) => {
|
|
174
174
|
focusPane();
|
|
175
|
-
if (nativeWindowChrome) void rendererHost.startWindowDrag?.();
|
|
175
|
+
if (nativeWindowChrome) void rendererHost.startWindowDrag?.(press);
|
|
176
176
|
}, [focusPane, nativeWindowChrome, rendererHost]);
|
|
177
177
|
|
|
178
178
|
const sharePane = useCallback(() => {
|
|
@@ -36,6 +36,7 @@ import { resolveHeaderPromptGeometry } from "./shell/chrome";
|
|
|
36
36
|
import { useWindowFullscreen } from "./window-fullscreen";
|
|
37
37
|
import { WindowControls, WINDOWS_CONTROL_GROUP_WIDTH_PX } from "./window-controls";
|
|
38
38
|
import { Button, terminalButtonColumns } from "../ui/button";
|
|
39
|
+
import { DelayedMark, delayedMarkWidth } from "../ui/delayed-mark";
|
|
39
40
|
|
|
40
41
|
const UPDATE_NOTICE_DURATION_MS = 5_000;
|
|
41
42
|
/** Kept clear after the terminal update status, so its last character never touches the Help pill. */
|
|
@@ -768,7 +769,10 @@ function HeaderMarketSummary({ nativePaneChrome, width }: { nativePaneChrome: bo
|
|
|
768
769
|
available: width,
|
|
769
770
|
baseCurrencyWidth: displayWidth(summary.baseCurrencyLabel) + 1,
|
|
770
771
|
countdownWidth: summary.marketLabel.length - summary.marketLabelShort.length,
|
|
771
|
-
|
|
772
|
+
// A delayed SPY keeps its mark: the two fit, or give way, together. Once the
|
|
773
|
+
// rest of the cluster is gone, it gives up its move rather than give way.
|
|
774
|
+
spyWidth: summary.spyText.length + 1 + (summary.spyDelayMinutes != null ? delayedMarkWidth(summary.spyDelayMinutes, nativePaneChrome) + 1 : 0),
|
|
775
|
+
spyMoveWidth: summary.spyDelayMinutes != null ? summary.spyText.length - summary.spyPriceText.length : 0,
|
|
772
776
|
stateWidth: summary.marketLabelShort ? summary.marketLabelShort.length + 1 : 0,
|
|
773
777
|
});
|
|
774
778
|
const marketLabel = fit.showCountdown ? summary.marketLabel : summary.marketLabelShort;
|
|
@@ -783,8 +787,9 @@ function HeaderMarketSummary({ nativePaneChrome, width }: { nativePaneChrome: bo
|
|
|
783
787
|
</Box>
|
|
784
788
|
) : null}
|
|
785
789
|
{fit.showSpy ? (
|
|
786
|
-
<Box paddingRight={1} flexShrink={0} data-gloom-role="header-market">
|
|
787
|
-
<Text fg={summary.spyColor}>{summary.spyText}</Text>
|
|
790
|
+
<Box paddingRight={1} flexShrink={0} flexDirection="row" alignItems="center" gap={1} data-gloom-role="header-market">
|
|
791
|
+
<Text fg={summary.spyColor}>{fit.showSpyMove ? summary.spyText : summary.spyPriceText}</Text>
|
|
792
|
+
{summary.spyDelayMinutes != null ? <DelayedMark minutes={summary.spyDelayMinutes} /> : null}
|
|
788
793
|
</Box>
|
|
789
794
|
) : null}
|
|
790
795
|
{fit.showBaseCurrency ? (
|
|
@@ -870,9 +875,9 @@ export function Header({
|
|
|
870
875
|
// bar itself wears the prompt's fill and reads as a single strip.
|
|
871
876
|
const headerBg = nativePaneChrome ? colors.header : headerSurface(colors);
|
|
872
877
|
|
|
873
|
-
const startWindowDrag = useCallback(() => {
|
|
878
|
+
const startWindowDrag = useCallback((press?: { detail?: number }) => {
|
|
874
879
|
if (!titleBarOverlay || !nativeWindowChrome) return;
|
|
875
|
-
void rendererHost.startWindowDrag?.();
|
|
880
|
+
void rendererHost.startWindowDrag?.(press);
|
|
876
881
|
}, [nativeWindowChrome, rendererHost, titleBarOverlay]);
|
|
877
882
|
|
|
878
883
|
const openCommandBar = useCallback((event?: HeaderActionEvent) => {
|
|
@@ -12,6 +12,7 @@ import { formatPercentRaw } from "../../utils/format";
|
|
|
12
12
|
import { formatMarketPriceWithCurrency, liveQuoteFormatOptions } from "../../market-data/market/format";
|
|
13
13
|
import { getSessionMoveDisplay, marketStateColor, marketStateCountdown, marketStateLabel } from "../../market-data/market/status";
|
|
14
14
|
import type { Quote } from "../../types/financials";
|
|
15
|
+
import { useFreeQuoteDelay } from "../../api-client/plan-access";
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* SPY rides the shared feed. The snapshot poll only runs while the feed has
|
|
@@ -42,6 +43,10 @@ export interface MarketSummary {
|
|
|
42
43
|
marketLabelShort: string;
|
|
43
44
|
spyColor: string;
|
|
44
45
|
spyText: string;
|
|
46
|
+
/** `spyText` without its move (`SPY $762.43`), for a delayed SPY in a header too narrow for both and the mark. */
|
|
47
|
+
spyPriceText: string;
|
|
48
|
+
/** Minutes SPY's price is late for this account (signed out or free), drawn as the delayed mark beside it; null when live. */
|
|
49
|
+
spyDelayMinutes: number | null;
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
export interface MarketSummaryFit {
|
|
@@ -49,6 +54,8 @@ export interface MarketSummaryFit {
|
|
|
49
54
|
showCountdown: boolean;
|
|
50
55
|
showState: boolean;
|
|
51
56
|
showSpy: boolean;
|
|
57
|
+
/** SPY keeps its move; false when it gave the move up to stay (`spyMoveWidth`). */
|
|
58
|
+
showSpyMove: boolean;
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
/**
|
|
@@ -57,12 +64,16 @@ export interface MarketSummaryFit {
|
|
|
57
64
|
* base currency, then the countdown suffix that widens the label. A narrowing
|
|
58
65
|
* header therefore sheds the countdown, then the currency, then the state, and
|
|
59
66
|
* keeps SPY longest. `countdownWidth` is what the suffix adds to the label.
|
|
67
|
+
* `spyMoveWidth` is what SPY's move adds to `spyWidth`, for a SPY that gives
|
|
68
|
+
* its move up before it gives way: a delayed SPY, whose mark makes it wider,
|
|
69
|
+
* so a narrow header still shows it and says it is late.
|
|
60
70
|
*/
|
|
61
71
|
export function resolveMarketSummaryFit(options: {
|
|
62
72
|
available: number;
|
|
63
73
|
baseCurrencyWidth: number;
|
|
64
74
|
countdownWidth: number;
|
|
65
75
|
spyWidth: number;
|
|
76
|
+
spyMoveWidth?: number;
|
|
66
77
|
stateWidth: number;
|
|
67
78
|
}): MarketSummaryFit {
|
|
68
79
|
let remaining = options.available;
|
|
@@ -71,11 +82,13 @@ export function resolveMarketSummaryFit(options: {
|
|
|
71
82
|
remaining -= width;
|
|
72
83
|
return true;
|
|
73
84
|
};
|
|
74
|
-
const
|
|
85
|
+
const spyMoveWidth = options.spyMoveWidth ?? 0;
|
|
86
|
+
const showSpyMove = take(options.spyWidth);
|
|
87
|
+
const showSpy = showSpyMove || (spyMoveWidth > 0 && take(options.spyWidth - spyMoveWidth));
|
|
75
88
|
const showState = take(options.stateWidth);
|
|
76
89
|
const showBaseCurrency = take(options.baseCurrencyWidth);
|
|
77
90
|
const showCountdown = showState && take(options.countdownWidth);
|
|
78
|
-
return { showBaseCurrency, showCountdown, showState, showSpy };
|
|
91
|
+
return { showBaseCurrency, showCountdown, showState, showSpy, showSpyMove: showSpy && showSpyMove };
|
|
79
92
|
}
|
|
80
93
|
|
|
81
94
|
/**
|
|
@@ -87,15 +100,17 @@ export function resolveMarketSummaryFit(options: {
|
|
|
87
100
|
* The price carries its currency's sign ($), so the base currency label
|
|
88
101
|
* after it is never read as its unit.
|
|
89
102
|
*/
|
|
90
|
-
export function spySummary(quote: Quote | null | undefined, colors: ThemeColors): Pick<MarketSummary, "spyColor" | "spyText"> {
|
|
103
|
+
export function spySummary(quote: Quote | null | undefined, colors: ThemeColors): Pick<MarketSummary, "spyColor" | "spyPriceText" | "spyText"> {
|
|
91
104
|
const display = getSessionMoveDisplay(quote);
|
|
92
105
|
// Fixed decimals and a percent slot as wide as "+0.53%", so a streamed tick
|
|
93
106
|
// never slides the market-state label or re-fits the cluster.
|
|
107
|
+
const spyPriceText = display
|
|
108
|
+
? `SPY ${formatMarketPriceWithCurrency(display.price, quote?.currency || "USD", liveQuoteFormatOptions(quote, quote?.currency, "ETF"))}`
|
|
109
|
+
: "SPY —";
|
|
94
110
|
return {
|
|
95
111
|
spyColor: display?.change != null ? priceColor(display.change, colors) : colors.textDim,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
: "SPY —",
|
|
112
|
+
spyPriceText,
|
|
113
|
+
spyText: display ? `${spyPriceText} ${formatPercentRaw(display.changePercent).padStart(6)}` : spyPriceText,
|
|
99
114
|
};
|
|
100
115
|
}
|
|
101
116
|
|
|
@@ -135,7 +150,8 @@ export function useMarketSummary(): MarketSummary {
|
|
|
135
150
|
return () => { clearInterval(id); };
|
|
136
151
|
}, [appActive]);
|
|
137
152
|
|
|
138
|
-
const { spyColor, spyText } = spySummary(spyQuote, colors);
|
|
153
|
+
const { spyColor, spyPriceText, spyText } = spySummary(spyQuote, colors);
|
|
154
|
+
const spyDelayMinutes = useFreeQuoteDelay(spyQuote);
|
|
139
155
|
|
|
140
156
|
const mktCountdown = mktState ? marketStateCountdown(mktState, now) : null;
|
|
141
157
|
const marketLabelShort = mktState ? t(marketStateLabel(mktState)) : "";
|
|
@@ -149,6 +165,8 @@ export function useMarketSummary(): MarketSummary {
|
|
|
149
165
|
marketLabel,
|
|
150
166
|
marketLabelShort,
|
|
151
167
|
spyColor,
|
|
168
|
+
spyPriceText,
|
|
152
169
|
spyText,
|
|
170
|
+
spyDelayMinutes,
|
|
153
171
|
};
|
|
154
172
|
}
|
|
@@ -24,11 +24,13 @@ import {
|
|
|
24
24
|
} from "./model";
|
|
25
25
|
|
|
26
26
|
export {
|
|
27
|
+
ageFooterSegment,
|
|
27
28
|
companyDataFooterSegment,
|
|
28
29
|
datesPaneData,
|
|
29
30
|
DELAYED_SEGMENT,
|
|
30
31
|
delayedFooterSegment,
|
|
31
32
|
hasPaneFooterContent,
|
|
33
|
+
savedCopyFooterInfo,
|
|
32
34
|
savedCopyFooterSegment,
|
|
33
35
|
splitDelayedStatus,
|
|
34
36
|
type CombinedPaneFooter,
|
|
@@ -79,10 +81,12 @@ function terminalSegmentKey(segment: PaneFooterSegment, focused: boolean): strin
|
|
|
79
81
|
: "";
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
/** Columns an icon segment keeps when hints crowd the footer. */
|
|
84
|
+
/** Columns an icon segment keeps when hints crowd the footer: the icon, and on the desktop its `iconText` after it. */
|
|
83
85
|
function iconSegmentReserve(segment: PaneFooterSegment, focused: boolean, nativePaneChrome: boolean): number {
|
|
84
86
|
if (!segment.icon) return 0;
|
|
85
|
-
return nativePaneChrome
|
|
87
|
+
return nativePaneChrome
|
|
88
|
+
? 3 + (segment.iconText ? segment.iconText.length + 1 : 0)
|
|
89
|
+
: Math.max(3, 1 + terminalSegmentKey(segment, focused).length);
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
function SegmentView({ segment, focused }: { segment: PaneFooterSegment; focused: boolean }) {
|
|
@@ -123,6 +127,7 @@ function SegmentView({ segment, focused }: { segment: PaneFooterSegment; focused
|
|
|
123
127
|
data-icon={segment.icon}
|
|
124
128
|
>
|
|
125
129
|
<Icon name={segment.icon} size={14} color={segment.disabled ? colors.textMuted : footerToneColor(segment.parts[0] ?? { text: "" })} />
|
|
130
|
+
{segment.iconText ? <Text fg={colors.textMuted} selectable={false} data-gloom-role="pane-footer-icon-text">{segment.iconText}</Text> : null}
|
|
126
131
|
</Box>
|
|
127
132
|
);
|
|
128
133
|
}
|
|
@@ -139,6 +144,7 @@ function SegmentView({ segment, focused }: { segment: PaneFooterSegment; focused
|
|
|
139
144
|
disabled={!interactive}
|
|
140
145
|
color={segment.disabled ? colors.textMuted : footerToneColor(segment.parts[0] ?? { text: "" })}
|
|
141
146
|
onPress={() => segment.onPress?.()}
|
|
147
|
+
text={segment.iconText}
|
|
142
148
|
/>
|
|
143
149
|
);
|
|
144
150
|
}
|
|
@@ -30,6 +30,12 @@ export interface PaneFooterSegment {
|
|
|
30
30
|
* data a source delivers late: build it with {@link delayedFooterSegment}.
|
|
31
31
|
*/
|
|
32
32
|
icon?: "warning" | "clock";
|
|
33
|
+
/**
|
|
34
|
+
* A few characters the desktop draws after `icon`, in the footer's muted
|
|
35
|
+
* tone, so the segment reads without hovering: a delay's `15m`. The
|
|
36
|
+
* terminal's `parts` already carry it (`◷15m`).
|
|
37
|
+
*/
|
|
38
|
+
iconText?: string;
|
|
33
39
|
/**
|
|
34
40
|
* The segment only dates the pane's data (`as of 2026-10-05`). A `gloomberb
|
|
35
41
|
* shot` whose status line already dates that data leaves it out, so the
|
|
@@ -51,6 +57,21 @@ export interface PaneFooterSegment {
|
|
|
51
57
|
* warning.
|
|
52
58
|
*/
|
|
53
59
|
capture?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* The segment only says how long ago the pane's data loaded (`5m ago`,
|
|
62
|
+
* `fetched just now`), relative to the moment it is read: build it with
|
|
63
|
+
* {@link ageFooterSegment}. A row too narrow for every segment leaves it out
|
|
64
|
+
* first, whole, before anything else shortens or is cut, so the source and
|
|
65
|
+
* the date stay.
|
|
66
|
+
*/
|
|
67
|
+
age?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* When the copy the pane shows was saved, on the saved-copy segment
|
|
70
|
+
* ({@link savedCopyFooterSegment}). The footer draws that segment first,
|
|
71
|
+
* wherever a pane registers it, and once: the oldest when several parts of
|
|
72
|
+
* the pane show a saved copy.
|
|
73
|
+
*/
|
|
74
|
+
savedCopyAt?: number;
|
|
54
75
|
/**
|
|
55
76
|
* Shorter forms of `parts`, longest first. When the row is too narrow for
|
|
56
77
|
* `parts` beside the hints, it shows the first that fits, or the last, and
|
|
@@ -68,10 +89,11 @@ export interface PaneFooterSegment {
|
|
|
68
89
|
const DELAYED_FOOTER_GLYPH = "◷";
|
|
69
90
|
|
|
70
91
|
/**
|
|
71
|
-
* The footer's one form for data a source delivers late: a yellow clock
|
|
72
|
-
* `
|
|
73
|
-
* (`◷15m`) in the terminal, which has no tooltip, so it
|
|
74
|
-
* rather than a phrase.
|
|
92
|
+
* The footer's one form for data a source delivers late: a yellow clock and
|
|
93
|
+
* the delay (`15m`) on the desktop, with `detail` in its tooltip, and the
|
|
94
|
+
* clock with the delay (`◷15m`) in the terminal, which has no tooltip, so it
|
|
95
|
+
* takes a few cells rather than a phrase. Without a known delay the desktop
|
|
96
|
+
* draws the clock alone. `onPress` makes it a button, such as an upgrade.
|
|
75
97
|
*/
|
|
76
98
|
export function delayedFooterSegment({
|
|
77
99
|
id,
|
|
@@ -94,6 +116,7 @@ export function delayedFooterSegment({
|
|
|
94
116
|
label: detail,
|
|
95
117
|
title: detail,
|
|
96
118
|
parts: [{ text: `${DELAYED_FOOTER_GLYPH}${delay ?? ""}`, tone: "warning" }],
|
|
119
|
+
...(delay ? { iconText: delay } : {}),
|
|
97
120
|
...(onPress ? { onPress } : {}),
|
|
98
121
|
...(quoteStatus ? { quoteStatus } : {}),
|
|
99
122
|
};
|
|
@@ -113,12 +136,36 @@ export function savedCopyFooterSegment(id: string, savedAt: number, now = Date.n
|
|
|
113
136
|
return {
|
|
114
137
|
id,
|
|
115
138
|
quoteStatus: true,
|
|
139
|
+
savedCopyAt: savedAt,
|
|
116
140
|
title: `${t("Couldn't refresh")}: ${text}`,
|
|
117
141
|
parts: [{ text, tone: "warning" }],
|
|
118
142
|
variants: [[{ text: t("saved copy"), tone: "warning" }]],
|
|
119
143
|
};
|
|
120
144
|
}
|
|
121
145
|
|
|
146
|
+
/**
|
|
147
|
+
* The footer's one form for how long ago the pane's data loaded: `5m ago`,
|
|
148
|
+
* or `fetched 5m ago` with a `verb`. A narrow row leaves it out first (`age`).
|
|
149
|
+
*/
|
|
150
|
+
export function ageFooterSegment(ago: string, { id = "updated", verb, tone = "muted" }: {
|
|
151
|
+
id?: string;
|
|
152
|
+
/** Said before the age: `fetched`, `checked`, `updated`. */
|
|
153
|
+
verb?: string;
|
|
154
|
+
tone?: PaneFooterPart["tone"];
|
|
155
|
+
} = {}): PaneFooterSegment {
|
|
156
|
+
return { id, age: true, parts: [{ text: verb ? `${verb} ${ago}` : ago, tone }] };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The saved-copy segment of a pane showing several answers (a list and the
|
|
161
|
+
* report it opens): the oldest of the copies among `savedAts` (ms), or no
|
|
162
|
+
* segment when none of them is one.
|
|
163
|
+
*/
|
|
164
|
+
export function savedCopyFooterInfo(savedAts: readonly (number | null | undefined)[], id = "saved-copy"): PaneFooterSegment[] {
|
|
165
|
+
const times = savedAts.filter((at): at is number => typeof at === "number" && Number.isFinite(at));
|
|
166
|
+
return times.length > 0 ? [savedCopyFooterSegment(id, Math.min(...times))] : [];
|
|
167
|
+
}
|
|
168
|
+
|
|
122
169
|
/**
|
|
123
170
|
* The footer's one form for company data a pane shows from another listing
|
|
124
171
|
* of the company: a receipt or a foreign line (SKHY, HY9H in Frankfurt) reads
|
|
@@ -299,7 +346,8 @@ function trimTrailing(info: PaneFooterSegment[], width: number, keep: number): P
|
|
|
299
346
|
}
|
|
300
347
|
|
|
301
348
|
/**
|
|
302
|
-
* The info segments as they fit `width`:
|
|
349
|
+
* The info segments as they fit `width`: an `age` (`5m ago`) is left out
|
|
350
|
+
* first, so the source and the date outlast it; then segments with `variants` step down
|
|
303
351
|
* together, one variant at a time, until the row fits or none is left; beside
|
|
304
352
|
* the hints, the trailing status that still does not fit is then cut or left
|
|
305
353
|
* out (trimTrailing). A status after every segment with shorter forms gives
|
|
@@ -310,6 +358,10 @@ function trimTrailing(info: PaneFooterSegment[], width: number, keep: number): P
|
|
|
310
358
|
* so a frame that fits its own status line sees it overflow.
|
|
311
359
|
*/
|
|
312
360
|
export function fitPaneFooterInfo(info: PaneFooterSegment[], width: number, { besideHints = true }: { besideHints?: boolean } = {}): PaneFooterSegment[] {
|
|
361
|
+
// How long ago the data loaded goes first, whole: a cut age (`fetched just`) says nothing.
|
|
362
|
+
if (infoTextWidth(info) > width && info.some((segment) => segment.age)) {
|
|
363
|
+
return fitPaneFooterInfo(info.filter((segment) => !segment.age), width, { besideHints });
|
|
364
|
+
}
|
|
313
365
|
if (!besideHints) return stepDownTogether(info, width);
|
|
314
366
|
const lastWithVariants = info.findLastIndex((segment) => (segment.variants?.length ?? 0) > 0);
|
|
315
367
|
if (lastWithVariants >= 0 && lastWithVariants < info.length - 1 && infoTextWidth(info) > width) {
|
|
@@ -450,7 +502,20 @@ export function combinePaneFooterRegistrations(registrations: Map<string, PaneFo
|
|
|
450
502
|
const shown = yielded.length > 0 ? hints.filter((hint) => !yielded.includes(hint)) : hints;
|
|
451
503
|
|
|
452
504
|
if (info.length === 0 && shown.length === 0 && menu.length === 0 && keys.length === 0) return EMPTY_FOOTER;
|
|
453
|
-
return { info, hints: shown, menu, keys };
|
|
505
|
+
return { info: savedCopyFirst(info), hints: shown, menu, keys };
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* The saved-copy segment in its one place: first, so it reads the same in
|
|
510
|
+
* every pane and is the last a narrow row cuts, and once, the oldest copy,
|
|
511
|
+
* when a list and the report it opens each register one.
|
|
512
|
+
*/
|
|
513
|
+
function savedCopyFirst(info: PaneFooterSegment[]): PaneFooterSegment[] {
|
|
514
|
+
let oldest: PaneFooterSegment | undefined;
|
|
515
|
+
for (const segment of info) {
|
|
516
|
+
if (segment.savedCopyAt != null && (oldest?.savedCopyAt == null || segment.savedCopyAt < oldest.savedCopyAt)) oldest = segment;
|
|
517
|
+
}
|
|
518
|
+
return oldest ? [oldest, ...info.filter((segment) => segment.savedCopyAt == null)] : info;
|
|
454
519
|
}
|
|
455
520
|
|
|
456
521
|
function sameFooterParts(left: PaneFooterPart[], right: PaneFooterPart[]): boolean {
|
|
@@ -507,6 +572,8 @@ export function samePaneFooterRegistration(
|
|
|
507
572
|
&& segment.shortcut === other.shortcut
|
|
508
573
|
&& !!segment.onPress === !!other.onPress
|
|
509
574
|
&& segment.disabled === other.disabled
|
|
575
|
+
&& segment.savedCopyAt === other.savedCopyAt
|
|
576
|
+
&& segment.age === other.age
|
|
510
577
|
&& sameFooterParts(segment.parts, other.parts)
|
|
511
578
|
&& (segment.variants?.length ?? 0) === (other.variants?.length ?? 0)
|
|
512
579
|
&& (segment.variants ?? []).every((variant, variantIndex) => sameFooterParts(variant, other.variants![variantIndex]!));
|
|
@@ -178,13 +178,13 @@ export function PaneHeader({
|
|
|
178
178
|
event.stopPropagation?.();
|
|
179
179
|
onHeaderMouseDown?.(event);
|
|
180
180
|
};
|
|
181
|
-
const beginWindowDrag = (event?: { stopPropagation?: () => void }) => {
|
|
181
|
+
const beginWindowDrag = (event?: { stopPropagation?: () => void; detail?: number }) => {
|
|
182
182
|
event?.stopPropagation?.();
|
|
183
183
|
if (titleBar) {
|
|
184
184
|
onHeaderMouseDown?.(event);
|
|
185
185
|
return;
|
|
186
186
|
}
|
|
187
|
-
void rendererHost.startWindowDrag?.();
|
|
187
|
+
void rendererHost.startWindowDrag?.(event);
|
|
188
188
|
};
|
|
189
189
|
const barHandlers = windowDrag
|
|
190
190
|
? { onMouseDown: beginWindowDrag }
|
|
@@ -11,7 +11,8 @@ const EMPTY_STATUS_INFO: PaneFooterSegment[] = [];
|
|
|
11
11
|
// has no per-pane footer hint. Do not reintroduce one. See PR #589. Panes bind
|
|
12
12
|
// it with `usePaneRefreshKey`.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/** The shared warning for data on screen older than it should be, for a pane composing its own footer. */
|
|
15
|
+
export const STALE_SEGMENT: PaneFooterSegment = { id: "stale", parts: [{ text: "stale", tone: "warning" }] };
|
|
15
16
|
|
|
16
17
|
interface PaneStatusInfoOptions {
|
|
17
18
|
loading?: boolean;
|
|
@@ -215,9 +215,10 @@ export interface StatChartDetailProps {
|
|
|
215
215
|
export function StatChartDetail({ items, width, height, empty, emptySubject, emptyTitle, renderChart }: StatChartDetailProps) {
|
|
216
216
|
return (
|
|
217
217
|
<Box flexDirection="column" width={width} height={height}>
|
|
218
|
-
|
|
218
|
+
{/* A figure wraps only into rows the chart's least leaves, so the detail never spills. */}
|
|
219
|
+
<StatGrid items={items} width={width} maxLines={height - 3} />
|
|
219
220
|
<PaneStatusBody empty={empty} subject={emptySubject} emptyTitle={emptyTitle}>
|
|
220
|
-
{renderChart(Math.max(3, height - statGridRows(items, width)))}
|
|
221
|
+
{renderChart(Math.max(3, height - statGridRows(items, width, undefined, { maxLines: height - 3 })))}
|
|
221
222
|
</PaneStatusBody>
|
|
222
223
|
</Box>
|
|
223
224
|
);
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { Box, useUiCapabilities } from "../../ui";
|
|
2
2
|
import { t } from "../../i18n";
|
|
3
3
|
import { DESKS, deskFunctions, type DeskKey } from "../../layout/desks";
|
|
4
|
+
import { MAX_DESK_PICKS } from "../../layout/desk-keys";
|
|
4
5
|
import { ToggleList } from "../toggle-list";
|
|
5
6
|
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Ticks or unticks a desk; the ticked ones keep the order they were ticked
|
|
9
|
+
* in. A fourth tick is ignored: untick one first.
|
|
10
|
+
*/
|
|
11
|
+
export function toggleDeskChoice(chosen: DeskKey[], key: DeskKey): DeskKey[] {
|
|
12
|
+
if (chosen.includes(key)) return chosen.filter((entry) => entry !== key);
|
|
13
|
+
return chosen.length < MAX_DESK_PICKS ? [...chosen, key] : chosen;
|
|
9
14
|
}
|
|
10
15
|
|
|
11
|
-
/** "What do you trade?": one checkbox per desk, with the functions it opens. */
|
|
16
|
+
/** "What do you trade most?": one checkbox per desk, with the functions it opens. */
|
|
12
17
|
export function DesksStep({
|
|
13
18
|
chosen,
|
|
14
19
|
cursor,
|
|
@@ -21,6 +26,7 @@ export function DesksStep({
|
|
|
21
26
|
onToggle: (key: DeskKey) => void;
|
|
22
27
|
}) {
|
|
23
28
|
const desktop = useUiCapabilities().nativePaneChrome === true;
|
|
29
|
+
const full = chosen.length >= MAX_DESK_PICKS;
|
|
24
30
|
return (
|
|
25
31
|
<Box flexDirection="column" flexShrink={0} paddingX={desktop ? 0 : 2} style={desktop ? { marginTop: 14 } : undefined}>
|
|
26
32
|
<ToggleList
|
|
@@ -28,6 +34,8 @@ export function DesksStep({
|
|
|
28
34
|
id: desk.key,
|
|
29
35
|
label: t(desk.label),
|
|
30
36
|
enabled: chosen.includes(desk.key),
|
|
37
|
+
// Once three are ticked, the rest wait until one is unticked.
|
|
38
|
+
disabled: full && !chosen.includes(desk.key),
|
|
31
39
|
detail: deskFunctions(desk).join(" "),
|
|
32
40
|
}))}
|
|
33
41
|
selectedIdx={cursor}
|