@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
package/src/cli/registry.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
GloomPlugin,
|
|
7
7
|
} from "../types/plugin";
|
|
8
8
|
import type { LoadedExternalPlugin } from "../plugins/loader";
|
|
9
|
-
import {
|
|
9
|
+
import { getBuiltinCliCommands } from "../plugins/catalog-cli";
|
|
10
10
|
import { initCliServices, initConfigData, initMarketData } from "./context";
|
|
11
11
|
import { closeAndFail, fail } from "./errors";
|
|
12
12
|
import { DEFAULT_CLI_OPTIONS, type CliGlobalOptions } from "./options";
|
|
@@ -38,11 +38,14 @@ export interface CliCommandRegistry {
|
|
|
38
38
|
config: AppConfig | null;
|
|
39
39
|
plugins: GloomPlugin[];
|
|
40
40
|
externalPlugins: LoadedExternalPlugin[];
|
|
41
|
+
/** Loads installed contributions when dispatch or application services need them. */
|
|
42
|
+
ensureExternalPlugins?: () => Promise<void>;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
interface BuildCliCommandRegistryOptions {
|
|
44
46
|
coreCommands: CliCommandDef[];
|
|
45
47
|
externalPlugins?: LoadedExternalPlugin[];
|
|
48
|
+
loadExternalPlugins?: () => Promise<LoadedExternalPlugin[]>;
|
|
46
49
|
config?: AppConfig | null;
|
|
47
50
|
}
|
|
48
51
|
|
|
@@ -72,11 +75,11 @@ export function normalizeCliDispatchResult(result: void | CliDispatchResult): Cl
|
|
|
72
75
|
export function buildCliCommandRegistry({
|
|
73
76
|
coreCommands,
|
|
74
77
|
externalPlugins = [],
|
|
78
|
+
loadExternalPlugins,
|
|
75
79
|
config = null,
|
|
76
80
|
}: BuildCliCommandRegistryOptions): CliCommandRegistry {
|
|
77
81
|
const allCommands: RegisteredCliCommand[] = [];
|
|
78
82
|
const allTokens = new Map<string, RegisteredCliCommand>();
|
|
79
|
-
const catalog = getPluginCatalog(externalPlugins);
|
|
80
83
|
const disabledPlugins = new Set(config?.disabledPlugins ?? []);
|
|
81
84
|
|
|
82
85
|
const registerCommand = (
|
|
@@ -110,8 +113,10 @@ export function buildCliCommandRegistry({
|
|
|
110
113
|
registerCommand(command, "core", "core", true);
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
const
|
|
114
|
-
|
|
116
|
+
for (const { pluginId, commands } of getBuiltinCliCommands()) {
|
|
117
|
+
for (const command of commands) registerCommand(command, pluginId, "plugin", true);
|
|
118
|
+
}
|
|
119
|
+
for (const entry of externalPlugins) {
|
|
115
120
|
if (entry.error) {
|
|
116
121
|
registryLog.warn(`Skipping external plugin "${entry.plugin.id}" for CLI registration.`, {
|
|
117
122
|
path: entry.path,
|
|
@@ -119,9 +124,8 @@ export function buildCliCommandRegistry({
|
|
|
119
124
|
});
|
|
120
125
|
continue;
|
|
121
126
|
}
|
|
122
|
-
loadablePlugins.push(entry.plugin);
|
|
123
127
|
for (const command of entry.plugin.cliCommands ?? []) {
|
|
124
|
-
registerCommand(command, entry.plugin.id, "plugin",
|
|
128
|
+
registerCommand(command, entry.plugin.id, "plugin", false);
|
|
125
129
|
}
|
|
126
130
|
}
|
|
127
131
|
|
|
@@ -137,30 +141,54 @@ export function buildCliCommandRegistry({
|
|
|
137
141
|
}
|
|
138
142
|
}
|
|
139
143
|
|
|
140
|
-
|
|
144
|
+
let plugins: GloomPlugin[] | undefined;
|
|
145
|
+
let loading: Promise<void> | undefined;
|
|
146
|
+
const registry: CliCommandRegistry = {
|
|
141
147
|
commands,
|
|
142
148
|
lookup,
|
|
143
149
|
config,
|
|
144
|
-
plugins
|
|
150
|
+
get plugins() {
|
|
151
|
+
const { getPluginCatalog } = require("../plugins/catalog") as typeof import("../plugins/catalog");
|
|
152
|
+
return plugins ??= getPluginCatalog(registry.externalPlugins)
|
|
153
|
+
.filter((entry) => !entry.error && !disabledPlugins.has(entry.plugin.id))
|
|
154
|
+
.map((entry) => entry.plugin);
|
|
155
|
+
},
|
|
145
156
|
externalPlugins,
|
|
157
|
+
...(loadExternalPlugins ? { ensureExternalPlugins: () => loading ??= (async () => {
|
|
158
|
+
const loaded = await loadExternalPlugins();
|
|
159
|
+
const expanded = buildCliCommandRegistry({ coreCommands, externalPlugins: loaded, config });
|
|
160
|
+
registry.commands = expanded.commands;
|
|
161
|
+
registry.lookup = expanded.lookup;
|
|
162
|
+
registry.externalPlugins = loaded;
|
|
163
|
+
// A command can retain ctx.plugins before opening market data (search does).
|
|
164
|
+
// Keep that reference current without materializing panes for command-only work.
|
|
165
|
+
if (plugins) plugins.splice(0, plugins.length, ...expanded.plugins);
|
|
166
|
+
})() } : {}),
|
|
146
167
|
};
|
|
168
|
+
return registry;
|
|
147
169
|
}
|
|
148
170
|
|
|
149
171
|
export function createCliCommandContext(
|
|
150
172
|
ownerId: string,
|
|
151
|
-
registryOrPlugins: Pick<CliCommandRegistry, "plugins" | "externalPlugins"> | GloomPlugin[],
|
|
173
|
+
registryOrPlugins: Pick<CliCommandRegistry, "plugins" | "externalPlugins" | "ensureExternalPlugins"> | GloomPlugin[],
|
|
152
174
|
cliOptions: CliGlobalOptions = DEFAULT_CLI_OPTIONS,
|
|
153
175
|
args: readonly string[] = [],
|
|
154
176
|
): CliCommandContext {
|
|
155
|
-
const registry = Array.isArray(registryOrPlugins)
|
|
177
|
+
const registry: Pick<CliCommandRegistry, "plugins" | "externalPlugins" | "ensureExternalPlugins"> = Array.isArray(registryOrPlugins)
|
|
156
178
|
? { plugins: registryOrPlugins, externalPlugins: [] }
|
|
157
179
|
: registryOrPlugins;
|
|
158
180
|
return {
|
|
159
181
|
initConfigData,
|
|
160
|
-
initMarketData: () =>
|
|
161
|
-
|
|
182
|
+
initMarketData: async () => {
|
|
183
|
+
await registry.ensureExternalPlugins?.();
|
|
184
|
+
return initMarketData({ plugins: registry.plugins });
|
|
185
|
+
},
|
|
186
|
+
initServices: async () => {
|
|
187
|
+
await registry.ensureExternalPlugins?.();
|
|
188
|
+
return initCliServices({ externalPlugins: registry.externalPlugins });
|
|
189
|
+
},
|
|
162
190
|
cliOptions,
|
|
163
|
-
plugins
|
|
191
|
+
get plugins() { return registry.plugins; },
|
|
164
192
|
fail,
|
|
165
193
|
closeAndFail,
|
|
166
194
|
output: {
|
package/src/cli/result.ts
CHANGED
|
@@ -93,6 +93,8 @@ export interface CliResultRenderOptions<T = unknown, Row = Record<string, unknow
|
|
|
93
93
|
heading?: string;
|
|
94
94
|
/** Text-mode line printed under the rows, such as where a listing stops. Not printed with no rows: say it in `empty`. */
|
|
95
95
|
footnote?: string;
|
|
96
|
+
/** Text-mode line under each table row, indented and muted, cut to the width: a story's summary under its headline. */
|
|
97
|
+
rowNote?: (row: Row) => string | null | undefined;
|
|
96
98
|
/** Text-mode block printed under the rows and footnote, for figures about a row that are not columns. */
|
|
97
99
|
details?: (data: T) => string;
|
|
98
100
|
/**
|
|
@@ -446,6 +448,7 @@ function formatTextCell<Row extends Record<string, unknown>>(
|
|
|
446
448
|
function renderTextTable<Row extends Record<string, unknown>>(
|
|
447
449
|
rows: Row[],
|
|
448
450
|
columns?: CliResultColumn<Row>[],
|
|
451
|
+
rowNote?: (row: Row) => string | null | undefined,
|
|
449
452
|
): string {
|
|
450
453
|
const resolvedColumns = textColumns(rows, columns);
|
|
451
454
|
const cells = rows.map((row) => resolvedColumns.map((column) => formatTextCell(column, row, "table")));
|
|
@@ -469,6 +472,7 @@ function renderTextTable<Row extends Record<string, unknown>>(
|
|
|
469
472
|
stackRows: column.stackRows,
|
|
470
473
|
})),
|
|
471
474
|
cells.map((row) => shown.map(({ index }) => row[index]!)),
|
|
475
|
+
rowNote ? { below: rows.map((row) => { const note = rowNote(row); return note ? [note] : []; }), cutBelow: true } : {},
|
|
472
476
|
);
|
|
473
477
|
}
|
|
474
478
|
|
|
@@ -581,15 +585,16 @@ function serializeCliRows<T, Row extends Record<string, unknown>>(
|
|
|
581
585
|
if (renderOptions.text) {
|
|
582
586
|
return renderOptions.text(result.data);
|
|
583
587
|
}
|
|
584
|
-
|
|
585
|
-
const
|
|
588
|
+
// The heading's lines, the summary and the notes are prose: each wraps to a narrow --width, as the source line does.
|
|
589
|
+
const summary = wrapToTerminal(renderOptions.summary?.(result.data) ?? "");
|
|
590
|
+
const withHeading = (text: string) => renderOptions.heading ? `${wrapToTerminal(renderOptions.heading)}\n${text}` : text;
|
|
586
591
|
if (rows.length === 0) {
|
|
587
|
-
return withHeading(summary || cliStyles.muted(renderOptions.empty ?? "No results."));
|
|
592
|
+
return withHeading(summary || cliStyles.muted(wrapToTerminal(renderOptions.empty ?? "No results.")));
|
|
588
593
|
}
|
|
589
594
|
const table = renderTextRows(rows as Row[], result.data, renderOptions);
|
|
590
595
|
const notes = [window.note, renderOptions.footnote].filter((note): note is string => !!note);
|
|
591
596
|
const details = renderOptions.details?.(result.data) ?? "";
|
|
592
|
-
const body = [table, ...notes.map((note) => cliStyles.muted(note)), ...details ? ["", details] : []].join("\n");
|
|
597
|
+
const body = [table, ...notes.map((note) => cliStyles.muted(wrapToTerminal(note))), ...details ? ["", details] : []].join("\n");
|
|
593
598
|
return withHeading(summary ? `${summary}\n\n${body}` : body);
|
|
594
599
|
}
|
|
595
600
|
|
|
@@ -607,7 +612,7 @@ function renderTextRows<T, Row extends Record<string, unknown>>(
|
|
|
607
612
|
}
|
|
608
613
|
return layout === "record"
|
|
609
614
|
? renderTextRecords(rows as Row[], columns)
|
|
610
|
-
: renderTextTable(rows as Row[], columns);
|
|
615
|
+
: renderTextTable(rows as Row[], columns, renderOptions.rowNote);
|
|
611
616
|
}
|
|
612
617
|
|
|
613
618
|
/** Settles once the result `printCliResult` last wrote has left the process: a pipe can hold it back. */
|
|
@@ -640,8 +645,9 @@ export function printCliResult<T, Row extends Record<string, unknown> = Record<s
|
|
|
640
645
|
// JSON carries warnings in its envelope. Text, CSV and NDJSON are only the
|
|
641
646
|
// rows, so a warning goes to stderr, out of a file the rows are piped into.
|
|
642
647
|
if (options.format !== "json" && !options.quiet) {
|
|
648
|
+
// Each wraps to a narrow --width, as a note does.
|
|
643
649
|
for (const warning of result.warnings ?? []) {
|
|
644
|
-
console.error(`${cliStyles.warning("warning:")} ${hinted(warning)}`);
|
|
650
|
+
console.error(wrapToTerminal(`${cliStyles.warning("warning:")} ${hinted(warning)}`));
|
|
645
651
|
}
|
|
646
652
|
}
|
|
647
653
|
// CSV says what its rows do not in closing `# note:` lines; NDJSON has no place for one among its rows,
|
|
@@ -139,7 +139,8 @@ export interface CliTickerOptions {
|
|
|
139
139
|
*/
|
|
140
140
|
export class CoinListingError extends ListingArgError {
|
|
141
141
|
constructor(message: string, details: string, readonly hint: string) {
|
|
142
|
-
|
|
142
|
+
// A listing message that already ends in the coin note (`describeOtherListings`) keeps it once.
|
|
143
|
+
super(message, details.endsWith(hint) ? details : `${details}\n${hint}`);
|
|
143
144
|
this.name = "CoinListingError";
|
|
144
145
|
}
|
|
145
146
|
}
|
package/src/cli/types.ts
CHANGED
|
@@ -21,6 +21,8 @@ export type MarketContext = ConfigContext & {
|
|
|
21
21
|
seriesCapabilities?: CapabilityInvoker;
|
|
22
22
|
/** The catalog series charted as an expression (`FRED:DFII10`), in a report run without the plugin runtime. */
|
|
23
23
|
findCatalogSeries?: (expression: string) => import("../series/types").SeriesDefinition | undefined;
|
|
24
|
+
/** The catalog series a bare word is an alias of (`hashrate`), in a report run without the plugin runtime. */
|
|
25
|
+
findCatalogSeriesNamed?: (word: string) => import("../series/types").SeriesDefinition | undefined;
|
|
24
26
|
/** `--refresh`: prefetched data bypasses the local cache. */
|
|
25
27
|
refresh?: boolean;
|
|
26
28
|
/** Filings behind the statements and a bank's figures, read from Gloom Cloud; absent where the data service is a stand-in. */
|
|
@@ -49,6 +49,7 @@ function formatFullCurrencyValue(
|
|
|
49
49
|
options: MarketFormatOptions = {},
|
|
50
50
|
): string | null {
|
|
51
51
|
if (unit.trim() === INDEX_POINTS_UNIT) return formatMarketPrice(value, { ...options, assetCategory, priceBasis: "points" });
|
|
52
|
+
if (unit.trim() === "32nds") return formatMarketPrice(value, { ...options, assetCategory, priceBasis: "thirty-seconds" });
|
|
52
53
|
const currency = unitCurrencyCode(unit);
|
|
53
54
|
return currency ? formatMarketPriceWithCurrency(value, currency, { ...options, assetCategory }) : null;
|
|
54
55
|
}
|
|
@@ -362,6 +363,8 @@ export function formatCompositeAxisValue(value: number, domain: CompositeAxisDom
|
|
|
362
363
|
// Derived dimensions are included in the legend value. Axis labels stay
|
|
363
364
|
// numeric so a narrow gutter cannot truncate USD/JPY into a false USD label.
|
|
364
365
|
if (group.startsWith("derived-unit:")) return compact;
|
|
366
|
+
// Treasury prices keep their whole points and 32nds at every zoom level.
|
|
367
|
+
if (domain.unit.trim() === "32nds") return formatAxisPriceValue(value, domain);
|
|
365
368
|
// Compact labels suit a wide view, but a zoomed one can hold several ticks
|
|
366
369
|
// inside a single rounding step and print one repeated price down the gutter.
|
|
367
370
|
// Where that happens the axis spends the digits needed to tell them apart,
|
|
@@ -257,7 +257,7 @@ export function ChartTableHeader(props: ChartTableHeaderProps) {
|
|
|
257
257
|
return (
|
|
258
258
|
<Box flexDirection="column" flexShrink={0}>
|
|
259
259
|
{query}
|
|
260
|
-
{layout.figureRows ? <StatGrid items={layout.figures} width={width} /> : null}
|
|
260
|
+
{layout.figureRows ? <StatGrid items={layout.figures} width={width} maxLines={layout.figureMaxLines} /> : null}
|
|
261
261
|
{band}
|
|
262
262
|
</Box>
|
|
263
263
|
);
|
|
@@ -31,6 +31,8 @@ const CHART_SHARE = 0.4;
|
|
|
31
31
|
const SHORT_BODY_ROWS = 12;
|
|
32
32
|
/** The figures never take more than this share of the body. */
|
|
33
33
|
const FIGURE_SHARE = 0.25;
|
|
34
|
+
/** The share of the body figures that wrap rather than cut a date may take, beyond FIGURE_SHARE. */
|
|
35
|
+
const WRAPPED_FIGURE_SHARE = 1 / 3;
|
|
34
36
|
|
|
35
37
|
/**
|
|
36
38
|
* The rows a kit table spends outside its body: the header, plus a horizontal
|
|
@@ -70,15 +72,28 @@ export interface ChartTableLayoutInput {
|
|
|
70
72
|
export interface ChartTableLayout {
|
|
71
73
|
figures: StatItem[];
|
|
72
74
|
figureRows: number;
|
|
75
|
+
/** The lines the figures may take, so a whole-row cell too narrow for one line wraps within them (StatGrid's `maxLines`). */
|
|
76
|
+
figureMaxLines: number;
|
|
73
77
|
mode: ChartBandMode;
|
|
74
78
|
chartRows: number;
|
|
75
79
|
}
|
|
76
80
|
|
|
77
|
-
/**
|
|
78
|
-
function
|
|
81
|
+
/** Rows `figures` take within `maxLines` (StatGrid's own count). */
|
|
82
|
+
function figureRows(figures: readonly StatItem[], width: number, maxLines: number): number {
|
|
83
|
+
return statGridRows([...figures], width, undefined, { maxLines });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The figures that fit the row budget a line each, dropped from the end, and
|
|
88
|
+
* the lines they may take: a whole-row figure too narrow for one line wraps
|
|
89
|
+
* onto a second rather than cut a date, into rows up to WRAPPED_FIGURE_SHARE
|
|
90
|
+
* of the body, the leading ones first; the rest keep one line, cut. Wrapping
|
|
91
|
+
* never costs a figure that fits a line. A budget of one row wraps none.
|
|
92
|
+
*/
|
|
93
|
+
function fitFigures(figures: readonly StatItem[], width: number, maxRows: number, body: number): { figures: StatItem[]; maxLines: number } {
|
|
79
94
|
const kept = [...figures];
|
|
80
|
-
while (kept.length > 0 &&
|
|
81
|
-
return kept;
|
|
95
|
+
while (kept.length > 0 && figureRows(kept, width, maxRows) > maxRows) kept.pop();
|
|
96
|
+
return { figures: kept, maxLines: maxRows > 1 ? Math.max(maxRows, Math.floor(body * WRAPPED_FIGURE_SHARE)) : maxRows };
|
|
82
97
|
}
|
|
83
98
|
|
|
84
99
|
/**
|
|
@@ -89,7 +104,9 @@ function fitFigures(figures: readonly StatItem[], width: number, maxRows: number
|
|
|
89
104
|
* fewer), so the rows the pane is about never vanish.
|
|
90
105
|
* - The figures take at most a quarter of the body, one row when it is short
|
|
91
106
|
* or when a second row would cost the chart its place, and lose items from
|
|
92
|
-
* the end rather than
|
|
107
|
+
* the end rather than spill; a whole-row figure wraps onto a second line
|
|
108
|
+
* rather than cut its detail, in rows up to a third of the body, never
|
|
109
|
+
* at the cost of a figure that fits a line.
|
|
93
110
|
* - The chart takes 40% of what is left, or everything the table does not
|
|
94
111
|
* need when all its rows fit, so no blank band opens under a short table.
|
|
95
112
|
* - Below a readable chart the band becomes a one-row strip, then nothing.
|
|
@@ -125,11 +142,11 @@ function layoutWithFigureRows(input: ChartTableLayoutInput, body: number, figure
|
|
|
125
142
|
|
|
126
143
|
// A table with no room for a single row after the figures gets the body back.
|
|
127
144
|
const figureBudget = Math.min(figureCap, Math.max(0, body - chrome - Math.min(tableRows, 1)));
|
|
128
|
-
const figures = figureBudget > 0 ? fitFigures(input.figures ?? [], width, figureBudget) : [];
|
|
129
|
-
const
|
|
145
|
+
const { figures, maxLines: figureMaxLines } = figureBudget > 0 ? fitFigures(input.figures ?? [], width, figureBudget, body) : { figures: [], maxLines: 0 };
|
|
146
|
+
const shownRows = figures.length ? figureRows(figures, width, figureMaxLines) : 0;
|
|
130
147
|
|
|
131
|
-
const rest = body -
|
|
132
|
-
const none: ChartTableLayout = { figures, figureRows, mode: "none", chartRows: 0 };
|
|
148
|
+
const rest = body - shownRows;
|
|
149
|
+
const none: ChartTableLayout = { figures, figureRows: shownRows, figureMaxLines, mode: "none", chartRows: 0 };
|
|
133
150
|
if (!input.chart || width < CHART_MIN_WIDTH) return none;
|
|
134
151
|
|
|
135
152
|
const minRows = input.chart.minRows ?? CHART_MIN_ROWS;
|
|
@@ -137,7 +154,7 @@ function layoutWithFigureRows(input: ChartTableLayoutInput, body: number, figure
|
|
|
137
154
|
const maxRows = input.chart.maxRows ?? Number.POSITIVE_INFINITY;
|
|
138
155
|
const room = rest - tableMin;
|
|
139
156
|
const spare = rest - tableFit;
|
|
140
|
-
const full = (rows: number): ChartTableLayout => ({ figures, figureRows, mode: "full", chartRows: Math.min(rows, maxRows) });
|
|
157
|
+
const full = (rows: number): ChartTableLayout => ({ figures, figureRows: shownRows, figureMaxLines, mode: "full", chartRows: Math.min(rows, maxRows) });
|
|
141
158
|
if (room >= minRows) {
|
|
142
159
|
// With every table row in view beside a readable chart, the chart takes
|
|
143
160
|
// exactly the rest; otherwise its 40% share, never below what it draws in.
|
|
@@ -148,7 +165,7 @@ function layoutWithFigureRows(input: ChartTableLayoutInput, body: number, figure
|
|
|
148
165
|
// instead of a strip over a blank band.
|
|
149
166
|
if (spare >= compactRows) return full(spare);
|
|
150
167
|
if (input.chart.strip !== false && room >= 1) {
|
|
151
|
-
return { figures, figureRows, mode: "strip", chartRows: 1 };
|
|
168
|
+
return { figures, figureRows: shownRows, figureMaxLines, mode: "strip", chartRows: 1 };
|
|
152
169
|
}
|
|
153
170
|
return none;
|
|
154
171
|
}
|
|
@@ -32,6 +32,8 @@ interface UseCommandBarActionRuntimeOptions {
|
|
|
32
32
|
dispatch: Dispatch<AppAction>;
|
|
33
33
|
focusedPaneId: string | null;
|
|
34
34
|
onCheckForUpdates?: () => void | Promise<void>;
|
|
35
|
+
/** Settings' Run Setup Again; absent where setup cannot run. */
|
|
36
|
+
onRunSetupAgain?: () => Promise<void>;
|
|
35
37
|
persistConfig: (nextConfig: AppState["config"]) => void;
|
|
36
38
|
pluginRegistry: PluginRegistry;
|
|
37
39
|
pushRoute: (route: CommandBarRoute) => void;
|
|
@@ -57,6 +59,7 @@ export function useCommandBarActionRuntime({
|
|
|
57
59
|
dispatch,
|
|
58
60
|
focusedPaneId,
|
|
59
61
|
onCheckForUpdates,
|
|
62
|
+
onRunSetupAgain,
|
|
60
63
|
persistConfig,
|
|
61
64
|
pluginRegistry,
|
|
62
65
|
pushRoute,
|
|
@@ -222,6 +225,7 @@ export function useCommandBarActionRuntime({
|
|
|
222
225
|
focusTicker,
|
|
223
226
|
notify,
|
|
224
227
|
onCheckForUpdates,
|
|
228
|
+
onRunSetupAgain,
|
|
225
229
|
openBuiltInWorkflow,
|
|
226
230
|
openInlineConfirm,
|
|
227
231
|
openModeRoute,
|
|
@@ -29,6 +29,7 @@ import { parseWindowModeCommandArg } from "../../layout-items";
|
|
|
29
29
|
import type { CommandBarRoute } from "../../workflow/types";
|
|
30
30
|
import { recordFunctionOpen } from "../../../../telemetry/usage-counts";
|
|
31
31
|
import { reportTelemetryConfig } from "../../../../telemetry/live-config";
|
|
32
|
+
import { ATTENTION_OFF_MESSAGE } from "../../../../telemetry/attention-setting";
|
|
32
33
|
import { NIGHTLY_CHANNEL_SUMMARY, withUpdateChannel } from "../../../../updater/channel";
|
|
33
34
|
import { isPrereleaseVersion } from "../../../../utils/semver";
|
|
34
35
|
import { VERSION } from "../../../../version";
|
|
@@ -53,6 +54,8 @@ export function runDirectCommandAction(options: {
|
|
|
53
54
|
getState: () => AppState;
|
|
54
55
|
notify: NotifyFn;
|
|
55
56
|
onCheckForUpdates?: () => void | Promise<void>;
|
|
57
|
+
/** Settings' Run Setup Again; absent where setup cannot run. */
|
|
58
|
+
onRunSetupAgain?: () => Promise<void>;
|
|
56
59
|
openBuiltInWorkflow: (actionId: string) => void;
|
|
57
60
|
openInlineConfirm: OpenInlineConfirm;
|
|
58
61
|
openModeRoute: (screen: "ticker-search" | "layout", initialQuery?: string) => void;
|
|
@@ -78,6 +81,7 @@ export function runDirectCommandAction(options: {
|
|
|
78
81
|
getState,
|
|
79
82
|
notify,
|
|
80
83
|
onCheckForUpdates,
|
|
84
|
+
onRunSetupAgain,
|
|
81
85
|
openBuiltInWorkflow,
|
|
82
86
|
openInlineConfirm,
|
|
83
87
|
openModeRoute,
|
|
@@ -329,47 +333,22 @@ export function runDirectCommandAction(options: {
|
|
|
329
333
|
return;
|
|
330
334
|
}
|
|
331
335
|
case "toggle-crash-reports":
|
|
332
|
-
case "toggle-usage-counts":
|
|
333
|
-
|
|
336
|
+
case "toggle-usage-counts":
|
|
337
|
+
case "toggle-attention-counts": {
|
|
338
|
+
const key = command.id === "toggle-crash-reports" ? "crashReports" : command.id === "toggle-usage-counts" ? "usage" : "attention";
|
|
339
|
+
// All three are on by default: only an explicit false is off.
|
|
334
340
|
const enabled = state.config.telemetry?.[key] === false;
|
|
335
341
|
const nextConfig = { ...state.config, telemetry: { ...state.config.telemetry, [key]: enabled } };
|
|
342
|
+
// Reported before React commits, so turning Attention Counts off drops unsent counts at once.
|
|
336
343
|
reportTelemetryConfig(nextConfig.telemetry);
|
|
337
344
|
dispatch({ type: "SET_CONFIG", config: nextConfig });
|
|
338
345
|
persistConfig(nextConfig);
|
|
339
|
-
notify(
|
|
346
|
+
notify(key === "attention" && !enabled
|
|
347
|
+
? t(ATTENTION_OFF_MESSAGE)
|
|
348
|
+
: `${t(command.label)}: ${t(enabled ? "on" : "off")}`, { type: "success" });
|
|
340
349
|
closeAll({ revertThemePreview: false });
|
|
341
350
|
return;
|
|
342
351
|
}
|
|
343
|
-
case "toggle-attention-counts": {
|
|
344
|
-
const setEnabled = (enabled: boolean) => {
|
|
345
|
-
const current = getState().config;
|
|
346
|
-
const nextConfig = { ...current, telemetry: { ...current.telemetry, attention: enabled } };
|
|
347
|
-
// Clear unsent counts before React commits the settings update.
|
|
348
|
-
reportTelemetryConfig(nextConfig.telemetry);
|
|
349
|
-
dispatch({ type: "SET_CONFIG", config: nextConfig });
|
|
350
|
-
persistConfig(nextConfig);
|
|
351
|
-
notify(`${t(command.label)}: ${t(enabled ? "on" : "off")}`, { type: "success" });
|
|
352
|
-
};
|
|
353
|
-
if (state.config.telemetry?.attention === true) {
|
|
354
|
-
setEnabled(false);
|
|
355
|
-
closeAll({ revertThemePreview: false });
|
|
356
|
-
} else {
|
|
357
|
-
openInlineConfirm({
|
|
358
|
-
confirmId: "enable-attention-counts",
|
|
359
|
-
title: "Share Attention Counts?",
|
|
360
|
-
body: [
|
|
361
|
-
"While signed in with a verified account, share the tickers you open in DES, charts, quotes and options, and add to watchlists.",
|
|
362
|
-
"Gloom uses your account to limit contributions. Published counts are delayed and rounded, and require at least 20 contributors. Your holdings, watchlist names and searches are not included.",
|
|
363
|
-
"This is separate from Usage Counts and is off by default. Turn it off at any time to stop collection and discard unsent counts. Already published anonymous totals cannot be removed.",
|
|
364
|
-
],
|
|
365
|
-
confirmLabel: "Share Counts",
|
|
366
|
-
cancelLabel: "Keep Off",
|
|
367
|
-
tone: "default",
|
|
368
|
-
onConfirm: async () => { setEnabled(true); },
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
352
|
case "font-size-increase":
|
|
374
353
|
case "font-size-decrease": {
|
|
375
354
|
const delta = command.id === "font-size-increase" ? 1 : -1;
|
|
@@ -391,6 +370,12 @@ export function runDirectCommandAction(options: {
|
|
|
391
370
|
void onCheckForUpdates?.();
|
|
392
371
|
closeAll({ revertThemePreview: false });
|
|
393
372
|
return;
|
|
373
|
+
case "run-setup-again":
|
|
374
|
+
closeAll({ revertThemePreview: false });
|
|
375
|
+
void onRunSetupAgain?.().catch((error: unknown) => {
|
|
376
|
+
notify(error instanceof Error ? error.message : String(error), { type: "error" });
|
|
377
|
+
});
|
|
378
|
+
return;
|
|
394
379
|
case "release-channel": {
|
|
395
380
|
// The runtime checks again as soon as the channel changes.
|
|
396
381
|
const setChannel = (channel: "stable" | "nightly") => {
|
|
@@ -39,6 +39,8 @@ interface UseCommandBarDirectCommandRuntimeOptions {
|
|
|
39
39
|
focusTicker: (symbol: string, options?: PinTickerOptions) => void;
|
|
40
40
|
notify: (body: string, options?: { type?: "info" | "success" | "error" }) => void;
|
|
41
41
|
onCheckForUpdates?: () => void | Promise<void>;
|
|
42
|
+
/** Settings' Run Setup Again; absent where setup cannot run. */
|
|
43
|
+
onRunSetupAgain?: () => Promise<void>;
|
|
42
44
|
openBuiltInWorkflow: (actionId: string) => void;
|
|
43
45
|
openInlineConfirm: OpenInlineConfirm;
|
|
44
46
|
openModeRoute: (
|
|
@@ -66,6 +68,7 @@ export function useCommandBarDirectCommandRuntime({
|
|
|
66
68
|
focusTicker,
|
|
67
69
|
notify,
|
|
68
70
|
onCheckForUpdates,
|
|
71
|
+
onRunSetupAgain,
|
|
69
72
|
openBuiltInWorkflow,
|
|
70
73
|
openInlineConfirm,
|
|
71
74
|
openModeRoute,
|
|
@@ -136,6 +139,7 @@ export function useCommandBarDirectCommandRuntime({
|
|
|
136
139
|
getState,
|
|
137
140
|
notify,
|
|
138
141
|
onCheckForUpdates,
|
|
142
|
+
onRunSetupAgain,
|
|
139
143
|
openBuiltInWorkflow,
|
|
140
144
|
openInlineConfirm,
|
|
141
145
|
openModeRoute,
|
|
@@ -159,6 +163,7 @@ export function useCommandBarDirectCommandRuntime({
|
|
|
159
163
|
getState,
|
|
160
164
|
notify,
|
|
161
165
|
onCheckForUpdates,
|
|
166
|
+
onRunSetupAgain,
|
|
162
167
|
openBuiltInWorkflow,
|
|
163
168
|
openInlineConfirm,
|
|
164
169
|
openModeRoute,
|
|
@@ -276,7 +276,7 @@ export const commands: Command[] = [
|
|
|
276
276
|
id: "toggle-attention-counts",
|
|
277
277
|
prefix: "",
|
|
278
278
|
label: "Attention Counts",
|
|
279
|
-
description: "
|
|
279
|
+
description: "Turn ticker research counts on or off (on by default)",
|
|
280
280
|
category: "Config",
|
|
281
281
|
},
|
|
282
282
|
|
|
@@ -309,6 +309,15 @@ export const commands: Command[] = [
|
|
|
309
309
|
category: "Config",
|
|
310
310
|
},
|
|
311
311
|
|
|
312
|
+
// Setup, from its first step on what the user has (components/onboarding/run-setup-again.ts)
|
|
313
|
+
{
|
|
314
|
+
id: "run-setup-again",
|
|
315
|
+
prefix: "",
|
|
316
|
+
label: "Run Setup Again",
|
|
317
|
+
description: "Rebuild Home and setup's desk tabs; your other layouts stay",
|
|
318
|
+
category: "Config",
|
|
319
|
+
},
|
|
320
|
+
|
|
312
321
|
// Import/Export
|
|
313
322
|
{
|
|
314
323
|
id: "export-config",
|
|
@@ -109,6 +109,9 @@ export function createRootCommandItemBuilder({
|
|
|
109
109
|
if (state.updateProgress?.phase === "ready") return "Update ready, restart to apply";
|
|
110
110
|
if (state.updateProgress?.phase === "error") return failedUpdateLabel(state.updateProgress);
|
|
111
111
|
return command.label;
|
|
112
|
+
// Unlike its neighbours it started out off, so the row says what the default is now.
|
|
113
|
+
case "toggle-attention-counts":
|
|
114
|
+
return `${command.label} (on by default)`;
|
|
112
115
|
default:
|
|
113
116
|
return command.label;
|
|
114
117
|
}
|
|
@@ -175,9 +178,9 @@ export function createRootCommandItemBuilder({
|
|
|
175
178
|
case "toggle-usage-counts":
|
|
176
179
|
return state.config.telemetry?.usage === false ? "Currently off" : "Currently on";
|
|
177
180
|
case "toggle-attention-counts":
|
|
178
|
-
return state.config.telemetry?.attention ===
|
|
179
|
-
? "Currently on
|
|
180
|
-
: "Currently
|
|
181
|
+
return state.config.telemetry?.attention === false
|
|
182
|
+
? "Currently off. Signed in, this turns it off on your other devices too"
|
|
183
|
+
: "Currently on: tickers you open go to Gloom, published only as hourly totals of 20+ people";
|
|
181
184
|
case "release-channel":
|
|
182
185
|
if (resolveUpdateChannel(state.config) === "nightly") return `Currently nightly: ${NIGHTLY_CHANNEL_SUMMARY}`;
|
|
183
186
|
return isPrereleaseVersion(VERSION)
|
|
@@ -201,7 +204,7 @@ export function createRootCommandItemBuilder({
|
|
|
201
204
|
case "toggle-usage-counts":
|
|
202
205
|
return state.config.telemetry?.usage === false ? "off" : "on";
|
|
203
206
|
case "toggle-attention-counts":
|
|
204
|
-
return state.config.telemetry?.attention ===
|
|
207
|
+
return state.config.telemetry?.attention === false ? "off" : "on";
|
|
205
208
|
case "release-channel":
|
|
206
209
|
return resolveUpdateChannel(state.config);
|
|
207
210
|
default:
|
|
@@ -223,6 +226,8 @@ export function createRootCommandItemBuilder({
|
|
|
223
226
|
return "update";
|
|
224
227
|
case "release-channel":
|
|
225
228
|
return "nightly stable beta prerelease update updates channel";
|
|
229
|
+
case "run-setup-again":
|
|
230
|
+
return "settings onboarding wizard guide first run start over desks holdings";
|
|
226
231
|
default:
|
|
227
232
|
return "";
|
|
228
233
|
}
|
|
@@ -20,6 +20,21 @@ import { buildRootShortcutFeedback } from "./shortcut-feedback";
|
|
|
20
20
|
import type { ShortcutIntent } from "./shortcuts";
|
|
21
21
|
import { clampIndex } from "../../../../utils/math";
|
|
22
22
|
|
|
23
|
+
/** Written the way a symbol is: up to five letters and a share class (AAPL, nvda, BRK.B). */
|
|
24
|
+
const SYMBOL_SHAPED_QUERY = /^[A-Za-z]{1,5}(?:[.\-/][A-Za-z]{1,2})?$/;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Text that asks for a symbol rather than a local row: symbol-shaped, and in
|
|
28
|
+
* capitals or in no row's name. `nvda` matches Exposure only through its
|
|
29
|
+
* example input; `upd` is in Check for Updates.
|
|
30
|
+
*/
|
|
31
|
+
function readsAsSymbol(text: string, rows: readonly ResultItem[]): boolean {
|
|
32
|
+
if (!SYMBOL_SHAPED_QUERY.test(text)) return false;
|
|
33
|
+
if (text === text.toUpperCase()) return true;
|
|
34
|
+
const wanted = text.toLowerCase();
|
|
35
|
+
return !rows.some((row) => isDefaultSelectable(row) && row.label.toLowerCase().includes(wanted));
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
interface UseCommandBarRootRuntimeOptions {
|
|
24
39
|
activeCollectionId: string | null;
|
|
25
40
|
activePortfolio?: AppState["config"]["portfolios"][number];
|
|
@@ -154,6 +169,13 @@ export function useCommandBarRootRuntime({
|
|
|
154
169
|
} {
|
|
155
170
|
const previousRootSelectionContextRef = useRef<{ query: string; mode: string } | null>(null);
|
|
156
171
|
const previousRootResultIdsRef = useRef<string[]>([]);
|
|
172
|
+
/**
|
|
173
|
+
* The row the untouched highlight rests on once the query's answer is in,
|
|
174
|
+
* and the answer it was on. A later answer that reorders the rows (a
|
|
175
|
+
* fresher search over a cached one, a broker's) moves the highlight with
|
|
176
|
+
* its row, so Enter opens the row that was highlighted.
|
|
177
|
+
*/
|
|
178
|
+
const pinnedRootRowRef = useRef<{ id: string; answer: string | null } | null>(null);
|
|
157
179
|
const activeMatch = matchPrefix(rootQuery, availableCommands);
|
|
158
180
|
|
|
159
181
|
const tickerSearchRouteQuery = currentRoute?.kind === "mode" && currentRoute.screen === "ticker-search"
|
|
@@ -310,6 +332,13 @@ export function useCommandBarRootRuntime({
|
|
|
310
332
|
writeTickerSearchCache,
|
|
311
333
|
});
|
|
312
334
|
const rootSearching = tickerSearching || providerSearching;
|
|
335
|
+
// A symbol whose search has not answered: the rows on screen are what the
|
|
336
|
+
// letters match locally (a pane whose example names AAPL), not the symbol,
|
|
337
|
+
// so none of them is highlighted and Enter waits for the answer, as it would
|
|
338
|
+
// get had the symbol been typed slowly. Lower case found in a row's name
|
|
339
|
+
// (`upd`, `list`) is that row and runs at once.
|
|
340
|
+
const rootSymbolAnswerPending = !rootResultsCurrent && rootPlainTickerSearchArg !== null
|
|
341
|
+
&& (rootTickerReading !== null || rootListingChoice !== null || readsAsSymbol(rootPlainTickerSearchArg, orderedRootResults));
|
|
313
342
|
|
|
314
343
|
useLayoutEffect(() => {
|
|
315
344
|
if (currentRoute) return;
|
|
@@ -326,6 +355,7 @@ export function useCommandBarRootRuntime({
|
|
|
326
355
|
previousRootSelectionContextRef.current = { query: rootQuery, mode: rootModeKind };
|
|
327
356
|
if (selectionContextChanged) {
|
|
328
357
|
rootSelectionNavigatedRef.current = false;
|
|
358
|
+
pinnedRootRowRef.current = null;
|
|
329
359
|
}
|
|
330
360
|
|
|
331
361
|
setRootSelectedIdx((current) => {
|
|
@@ -338,15 +368,23 @@ export function useCommandBarRootRuntime({
|
|
|
338
368
|
if (shiftedIdx >= 0) return shiftedIdx;
|
|
339
369
|
return clampIndex(current, resultIds.length);
|
|
340
370
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
//
|
|
344
|
-
//
|
|
371
|
+
const pinnedIdx = pinnedRootRowRef.current ? resultIds.indexOf(pinnedRootRowRef.current.id) : -1;
|
|
372
|
+
if (pinnedIdx >= 0) return pinnedIdx;
|
|
373
|
+
// Untouched, the selection follows the best row on offer until the
|
|
374
|
+
// answer is in. When every row is an offer or a placeholder, none of
|
|
375
|
+
// them is the answer and none is selected: Enter must not take one of
|
|
376
|
+
// them for the ticker still being searched.
|
|
345
377
|
const defaultIdx = orderedRootResults.findIndex(isDefaultSelectable);
|
|
346
|
-
if (defaultIdx < 0) return -1;
|
|
347
|
-
|
|
378
|
+
if (defaultIdx < 0 || rootSymbolAnswerPending) return -1;
|
|
379
|
+
const selected = clampIndex(Math.max(rootResultModel.initialIdx, defaultIdx), resultIds.length);
|
|
380
|
+
pinnedRootRowRef.current = rootResultsCurrent && resultIds[selected]
|
|
381
|
+
? { id: resultIds[selected]!, answer: activeRootProviderResultsKey }
|
|
382
|
+
: null;
|
|
383
|
+
return selected;
|
|
348
384
|
});
|
|
349
385
|
}, [
|
|
386
|
+
activeRootProviderResultsKey,
|
|
387
|
+
rootResultsCurrent,
|
|
350
388
|
activeMatch?.command.id,
|
|
351
389
|
currentRoute,
|
|
352
390
|
orderedRootResults,
|
|
@@ -354,13 +392,20 @@ export function useCommandBarRootRuntime({
|
|
|
354
392
|
rootQuery,
|
|
355
393
|
rootResultModel.initialIdx,
|
|
356
394
|
rootSelectionNavigatedRef,
|
|
395
|
+
rootSymbolAnswerPending,
|
|
357
396
|
setRootHoveredIdx,
|
|
358
397
|
setRootSelectedIdx,
|
|
359
398
|
]);
|
|
360
399
|
|
|
361
400
|
useEffect(() => {
|
|
362
401
|
if (!activeRootProviderResultsKey) return;
|
|
402
|
+
// A later answer that only reorders the rows leaves the highlight on its row.
|
|
403
|
+
const pinned = pinnedRootRowRef.current;
|
|
404
|
+
if (pinned && pinned.answer !== activeRootProviderResultsKey
|
|
405
|
+
&& orderedRootResults.some((item) => item.id === pinned.id)) return;
|
|
363
406
|
setRootSelectedIdx(0);
|
|
407
|
+
const top = orderedRootResults[0];
|
|
408
|
+
pinnedRootRowRef.current = top ? { id: top.id, answer: activeRootProviderResultsKey } : null;
|
|
364
409
|
setRootHoveredIdx(null);
|
|
365
410
|
nativeListScrollRef.current?.scrollTo(0);
|
|
366
411
|
}, [activeRootProviderResultsKey, nativeListScrollRef, setRootHoveredIdx, setRootSelectedIdx]);
|