@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
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
type SavedLayout,
|
|
9
9
|
} from "../../types/config";
|
|
10
10
|
import type { TickerMetadata, TickerRecord } from "../../types/ticker";
|
|
11
|
+
import { MAX_DESK_PICKS, type DeskKey } from "../../layout/desk-keys";
|
|
12
|
+
import { DESKS, isDeskKey } from "../../layout/desks";
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* The watchlist a first run lands with: the two index ETFs and the names most
|
|
@@ -211,13 +213,88 @@ export function buildFirstRunLayout({
|
|
|
211
213
|
};
|
|
212
214
|
}
|
|
213
215
|
|
|
214
|
-
/**
|
|
215
|
-
|
|
216
|
-
|
|
216
|
+
/**
|
|
217
|
+
* The ids of the layouts onboarding makes. Running it again finds its own
|
|
218
|
+
* layouts by these and replaces them, so it never adds a copy; a layout
|
|
219
|
+
* without one is the user's.
|
|
220
|
+
*/
|
|
221
|
+
const ONBOARDING_HOME_LAYOUT_ID = "onboarding:home";
|
|
222
|
+
const ONBOARDING_LAYOUT_ID_PREFIX = "onboarding:";
|
|
223
|
+
const ONBOARDING_DESK_LAYOUT_ID_PREFIX = `${ONBOARDING_LAYOUT_ID_PREFIX}desk:`;
|
|
224
|
+
|
|
225
|
+
export function onboardingDeskLayoutId(desk: DeskKey): string {
|
|
226
|
+
return `${ONBOARDING_DESK_LAYOUT_ID_PREFIX}${desk}`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function isOnboardingLayout(layout: SavedLayout): boolean {
|
|
230
|
+
return layout.id?.startsWith(ONBOARDING_LAYOUT_ID_PREFIX) === true;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Takes back the layouts setup made before it tagged them, so a run from
|
|
235
|
+
* Settings rebuilds them in place instead of copying them. Such a profile
|
|
236
|
+
* has setup's Home first and its desks right after it, under the desks'
|
|
237
|
+
* names. A first layout not called Home is the user's, and so is
|
|
238
|
+
* everything after the desks.
|
|
239
|
+
*/
|
|
240
|
+
function adoptUntaggedSetupLayouts(config: AppConfig): AppConfig {
|
|
241
|
+
const [first, ...rest] = config.layouts;
|
|
242
|
+
if (!first || first.name !== "Home" || config.layouts.some(isOnboardingLayout)) return config;
|
|
243
|
+
const desks: SavedLayout[] = [];
|
|
244
|
+
for (const layout of rest) {
|
|
245
|
+
const key = DESKS.find((desk) => desk.name === layout.name)?.key;
|
|
246
|
+
if (!key || desks.length >= MAX_DESK_PICKS || desks.some((desk) => desk.id === onboardingDeskLayoutId(key))) break;
|
|
247
|
+
desks.push({ ...layout, id: onboardingDeskLayoutId(key) });
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
...config,
|
|
251
|
+
layouts: [{ ...first, id: ONBOARDING_HOME_LAYOUT_ID }, ...desks, ...rest.slice(desks.length)],
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** The desks setup made that are still tabs, in tab order: what a run from Settings starts with ticked. */
|
|
256
|
+
export function setupDeskPicks(config: AppConfig): DeskKey[] {
|
|
257
|
+
const picks: DeskKey[] = [];
|
|
258
|
+
for (const layout of adoptUntaggedSetupLayouts(config).layouts) {
|
|
259
|
+
const key = layout.id?.startsWith(ONBOARDING_DESK_LAYOUT_ID_PREFIX)
|
|
260
|
+
? layout.id.slice(ONBOARDING_DESK_LAYOUT_ID_PREFIX.length)
|
|
261
|
+
: null;
|
|
262
|
+
if (isDeskKey(key) && !picks.includes(key)) picks.push(key);
|
|
263
|
+
}
|
|
264
|
+
return picks.slice(0, MAX_DESK_PICKS);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Puts the first-run workspace in place and makes Home active: Home, then a
|
|
269
|
+
* tab per picked desk. The layouts an earlier run made are replaced where
|
|
270
|
+
* they stood, and a desk not picked this time goes with them; a first run
|
|
271
|
+
* replaces the stock Home it starts with. Every other layout is the user's
|
|
272
|
+
* and keeps its place and content. A run from Settings (`rerun`) also
|
|
273
|
+
* rebuilds what setup made before it tagged its layouts, and with none of
|
|
274
|
+
* setup's left it puts Home first, replacing nothing.
|
|
275
|
+
*/
|
|
276
|
+
export function applyFirstRunLayout(
|
|
277
|
+
config: AppConfig,
|
|
278
|
+
home: SavedLayout,
|
|
279
|
+
desks: ReadonlyArray<SavedLayout & { id: string }> = [],
|
|
280
|
+
{ rerun = false }: { rerun?: boolean } = {},
|
|
281
|
+
): AppConfig {
|
|
282
|
+
const { layouts: current } = rerun ? adoptUntaggedSetupLayouts(config) : config;
|
|
283
|
+
const earlier = current.findIndex(isOnboardingLayout);
|
|
284
|
+
const users = current.filter((layout) => !isOnboardingLayout(layout));
|
|
285
|
+
// Everything before the first layout onboarding made is the user's, so its index holds.
|
|
286
|
+
const at = earlier >= 0 ? earlier : 0;
|
|
287
|
+
const kept = earlier >= 0 || rerun ? users : users.slice(1);
|
|
288
|
+
const layouts = [
|
|
289
|
+
...kept.slice(0, at),
|
|
290
|
+
{ ...home, id: ONBOARDING_HOME_LAYOUT_ID },
|
|
291
|
+
...desks,
|
|
292
|
+
...kept.slice(at),
|
|
293
|
+
];
|
|
217
294
|
return {
|
|
218
295
|
...config,
|
|
219
296
|
layouts,
|
|
220
297
|
layout: home.layout,
|
|
221
|
-
activeLayoutIndex:
|
|
298
|
+
activeLayoutIndex: at,
|
|
222
299
|
};
|
|
223
300
|
}
|
|
@@ -219,7 +219,8 @@ export function OnboardingCoach({
|
|
|
219
219
|
</Box>
|
|
220
220
|
<Box height={1} />
|
|
221
221
|
<Box flexGrow={1} minHeight={0}>{children}</Box>
|
|
222
|
-
|
|
222
|
+
{/* As OnboardingActions: a button that does not fit beside the one before it wraps under it. */}
|
|
223
|
+
<Box flexDirection="row" flexWrap="wrap" alignItems="center" columnGap={1} flexShrink={0}>{actions}</Box>
|
|
223
224
|
</Box>
|
|
224
225
|
);
|
|
225
226
|
}
|
|
@@ -358,7 +359,8 @@ export function OnboardingHeader({
|
|
|
358
359
|
}
|
|
359
360
|
|
|
360
361
|
// Progress, not navigation: three segments, the current one lit, the ones
|
|
361
|
-
// behind it filled. A filled segment is
|
|
362
|
+
// behind it filled. A filled segment is a way back unless the wizard marks
|
|
363
|
+
// it unavailable: a step done for good stays filled and is not offered again.
|
|
362
364
|
const activeIndex = sections.findIndex((section) => section.id === active);
|
|
363
365
|
return (
|
|
364
366
|
<Box flexDirection="row" alignItems="flex-start" justifyContent="space-between" minWidth={0}>
|
|
@@ -58,7 +58,7 @@ export function useOnboardingKeyboard({
|
|
|
58
58
|
submitAccountField, backPortfolio, continuePortfolio, setPortfolioOptionIdx, activeBrokerFields,
|
|
59
59
|
brokerFieldIdx, setBrokerSelectIdx, brokerChoices, openBrokerGuide, chosenDesks, finishDesks,
|
|
60
60
|
setDeskCursor, deskCursor, setChosenDesks, continueAccount, skipSetup, primaryUpgradeAction,
|
|
61
|
-
setBillingInterval, keybindings, dialogOpen, commandBarOpen, saveProgressInBackground,
|
|
61
|
+
setBillingInterval, keybindings, dialogOpen, commandBarOpen, saveProgressInBackground, goBack,
|
|
62
62
|
}: {
|
|
63
63
|
stage: OnboardingStage;
|
|
64
64
|
progress: OnboardingProgress;
|
|
@@ -102,6 +102,8 @@ export function useOnboardingKeyboard({
|
|
|
102
102
|
dialogOpen: boolean;
|
|
103
103
|
commandBarOpen: boolean;
|
|
104
104
|
saveProgressInBackground: (patch: Partial<OnboardingProgress> & Pick<OnboardingProgress, "stage">, baseConfig?: AppConfig | undefined) => void;
|
|
105
|
+
/** Back on the card: to the step the user came from. */
|
|
106
|
+
goBack: () => void;
|
|
105
107
|
}) {
|
|
106
108
|
const activeSection: OnboardingSectionId = stage === "portfolio" || stage === "desks"
|
|
107
109
|
? "portfolio"
|
|
@@ -128,7 +130,8 @@ export function useOnboardingKeyboard({
|
|
|
128
130
|
|
|
129
131
|
const handled = ((): boolean => {
|
|
130
132
|
if (isPlainKey(event, "f10")) {
|
|
131
|
-
|
|
133
|
+
// The first run needs a holding; a run from Settings can be left as it was.
|
|
134
|
+
if (isBrokerCommitting || ((stage === "portfolio" || stage === "desks") && !progress.rerun)) return true;
|
|
132
135
|
if (stage === "upgrade" && !planAccess.hasProAccess) continueFree();
|
|
133
136
|
else void finish(true);
|
|
134
137
|
return true;
|
|
@@ -234,7 +237,7 @@ export function useOnboardingKeyboard({
|
|
|
234
237
|
continueAccount();
|
|
235
238
|
} else if (escape) {
|
|
236
239
|
if (account.accountSub === "qr" || account.accountSub === "login") account.returnToAccountForm();
|
|
237
|
-
else
|
|
240
|
+
else goBack();
|
|
238
241
|
} else if (letter(BROWSER_SIGN_IN_KEY) && accountForm) {
|
|
239
242
|
account.beginQrSignIn();
|
|
240
243
|
} else if (letter(SKIP_SETUP_KEY)) {
|
|
@@ -251,7 +254,7 @@ export function useOnboardingKeyboard({
|
|
|
251
254
|
if (enter) {
|
|
252
255
|
primaryUpgradeAction();
|
|
253
256
|
} else if (escape) {
|
|
254
|
-
|
|
257
|
+
goBack();
|
|
255
258
|
} else if (!planAccess.hasProAccess && letter(KEEP_FREE_KEY)) {
|
|
256
259
|
continueFree();
|
|
257
260
|
} else if (!planAccess.hasProAccess && (isPlainKey(event, "left") || letter("h"))) {
|
|
@@ -266,7 +269,7 @@ export function useOnboardingKeyboard({
|
|
|
266
269
|
|
|
267
270
|
if (stage === "ready") {
|
|
268
271
|
if (enter) void finish();
|
|
269
|
-
else if (escape)
|
|
272
|
+
else if (escape) goBack();
|
|
270
273
|
else return false;
|
|
271
274
|
return true;
|
|
272
275
|
}
|
|
@@ -20,7 +20,7 @@ import { t, tf } from "../../i18n";
|
|
|
20
20
|
import { useAppLanguage } from "../../i18n/react";
|
|
21
21
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
22
22
|
import { formatCloudPrice, monthsFreeYearly } from "../../account/billing";
|
|
23
|
-
import { proStepCopy
|
|
23
|
+
import { proStepCopy } from "../../account/upgrade-dialog";
|
|
24
24
|
import { Button, SegmentedControl, terminalButtonColumns } from "../ui";
|
|
25
25
|
import { AccountStep, PortfolioStep } from "./onboarding-steps";
|
|
26
26
|
import {
|
|
@@ -41,7 +41,7 @@ import { useOnboardingAccount } from "./wizard-account";
|
|
|
41
41
|
import { useOnboardingBrokerSync } from "./wizard-broker-sync";
|
|
42
42
|
import { useOnboardingPositions } from "./wizard-positions";
|
|
43
43
|
import { DesksStep, toggleDeskChoice } from "./desks-step";
|
|
44
|
-
import { getOnboardingProgress } from "./wizard-model";
|
|
44
|
+
import { getOnboardingProgress, isOnboardingWorkspaceBuilt, previousOnboardingStage } from "./wizard-model";
|
|
45
45
|
import {
|
|
46
46
|
useOnboardingKeyboard,
|
|
47
47
|
SKIP_SETUP_KEY,
|
|
@@ -86,7 +86,22 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
|
|
|
86
86
|
const config = useAppSelector((state) => state.config);
|
|
87
87
|
const progress = getOnboardingProgress(config);
|
|
88
88
|
const stage = progress.stage;
|
|
89
|
+
const workspaceBuilt = isOnboardingWorkspaceBuilt(progress);
|
|
90
|
+
/** Opened again from Settings: any step can be left, and the first-run funnel is not told. */
|
|
91
|
+
const rerun = progress.rerun === true;
|
|
89
92
|
const researchOpenedRef = useRef<string | null>(null);
|
|
93
|
+
/** The stages the user came through, oldest first, for Back to retrace. */
|
|
94
|
+
const visitedRef = useRef<OnboardingStage[]>([]);
|
|
95
|
+
const shownStageRef = useRef(stage);
|
|
96
|
+
const backTargetRef = useRef<OnboardingStage | null>(null);
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
const left = shownStageRef.current;
|
|
99
|
+
if (left === stage) return;
|
|
100
|
+
shownStageRef.current = stage;
|
|
101
|
+
const back = backTargetRef.current === stage;
|
|
102
|
+
backTargetRef.current = null;
|
|
103
|
+
if (!back) visitedRef.current = [...visitedRef.current, left];
|
|
104
|
+
}, [stage]);
|
|
90
105
|
const [editingField, setEditingField] = useState(false);
|
|
91
106
|
/** The added position the keyboard acts on once no field is being typed in. */
|
|
92
107
|
const [positionCursorSymbol, setPositionCursorSymbol] = useState<string | null>(null);
|
|
@@ -109,7 +124,7 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
|
|
|
109
124
|
// bar, so an unread inbox never stalls the first session.
|
|
110
125
|
const account = useOnboardingAccount({
|
|
111
126
|
nextStep: () => {
|
|
112
|
-
recordResearchActivity("onboarding_signed_in");
|
|
127
|
+
if (!rerun) recordResearchActivity("onboarding_signed_in");
|
|
113
128
|
saveProgressInBackground({ stage: "upgrade", accountStatus: "signed-in" });
|
|
114
129
|
},
|
|
115
130
|
setEditingField,
|
|
@@ -127,12 +142,12 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
|
|
|
127
142
|
|
|
128
143
|
useEffect(() => {
|
|
129
144
|
const activity = STAGE_ACTIVITY[stage];
|
|
130
|
-
if (activity) recordResearchActivity(activity);
|
|
131
|
-
}, [stage]);
|
|
145
|
+
if (activity && !rerun) recordResearchActivity(activity);
|
|
146
|
+
}, [rerun, stage]);
|
|
132
147
|
|
|
133
148
|
useEffect(() => {
|
|
134
|
-
if (stage === "portfolio" && positionCount > 0) recordResearchActivity("onboarding_position_added");
|
|
135
|
-
}, [positionCount, stage]);
|
|
149
|
+
if (stage === "portfolio" && positionCount > 0 && !rerun) recordResearchActivity("onboarding_position_added");
|
|
150
|
+
}, [positionCount, rerun, stage]);
|
|
136
151
|
|
|
137
152
|
// The portfolio step opens straight into the ticker field.
|
|
138
153
|
useEffect(() => {
|
|
@@ -197,8 +212,8 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
|
|
|
197
212
|
useEffect(() => {
|
|
198
213
|
if (stage !== "research" || !progress.tickerSymbol || researchOpenedRef.current === progress.tickerSymbol) return;
|
|
199
214
|
researchOpenedRef.current = progress.tickerSymbol;
|
|
200
|
-
recordResearchActivity("ticker_saved");
|
|
201
|
-
}, [stage, progress.tickerSymbol]);
|
|
215
|
+
if (!rerun) recordResearchActivity("ticker_saved");
|
|
216
|
+
}, [rerun, stage, progress.tickerSymbol]);
|
|
202
217
|
const movePositionCursor = useCallback((delta: number) => {
|
|
203
218
|
if (positionCursorIndex < 0) return;
|
|
204
219
|
const next = Math.max(0, Math.min(positionCount - 1, positionCursorIndex + delta));
|
|
@@ -239,16 +254,17 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
|
|
|
239
254
|
: null;
|
|
240
255
|
const helpFocused = focusedInstance?.paneId === "help";
|
|
241
256
|
const {
|
|
242
|
-
offer, pricing, billingInterval, setBillingInterval, planAccess, primaryUpgradeAction, continueFree,
|
|
257
|
+
offer, pricing, billingInterval, setBillingInterval, planAccess, primaryUpgradeAction, continueFree,
|
|
243
258
|
} = useOnboardingUpgrade({
|
|
244
259
|
stage, progress, saveProgressInBackground, persistProgress,
|
|
245
|
-
shown: !helpFocused && !dialogOpen && !commandBarOpen,
|
|
246
260
|
});
|
|
247
261
|
|
|
248
262
|
const goToSection = useCallback((section: OnboardingSectionId) => {
|
|
249
263
|
if (finishingRef.current || isBrokerCommitting) return;
|
|
250
264
|
setEditingField(false);
|
|
251
265
|
if (section === "portfolio") {
|
|
266
|
+
// Done for good once the workspace is built: picking it again would build it twice.
|
|
267
|
+
if (workspaceBuilt) return;
|
|
252
268
|
setPortfolioSub("positions");
|
|
253
269
|
saveProgressInBackground({ stage: "portfolio" });
|
|
254
270
|
return;
|
|
@@ -260,10 +276,22 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
|
|
|
260
276
|
if (planAccess.signedIn) {
|
|
261
277
|
saveProgressInBackground({ stage: "upgrade", accountStatus: "signed-in" });
|
|
262
278
|
}
|
|
263
|
-
}, [isBrokerCommitting, planAccess.signedIn, saveProgressInBackground]);
|
|
279
|
+
}, [isBrokerCommitting, planAccess.signedIn, saveProgressInBackground, workspaceBuilt]);
|
|
280
|
+
|
|
281
|
+
/** Back and Esc: to the step the user came from, never one that is done for good. */
|
|
282
|
+
const goBack = useCallback(() => {
|
|
283
|
+
if (finishingRef.current || isBrokerCommitting) return;
|
|
284
|
+
const back = previousOnboardingStage(progress, visitedRef.current, { pro: planAccess.hasProAccess });
|
|
285
|
+
if (!back) return;
|
|
286
|
+
visitedRef.current = back.visited;
|
|
287
|
+
backTargetRef.current = back.stage;
|
|
288
|
+
setEditingField(false);
|
|
289
|
+
if (back.stage === "portfolio") setPortfolioSub("positions");
|
|
290
|
+
saveProgressInBackground({ stage: back.stage });
|
|
291
|
+
}, [isBrokerCommitting, planAccess.hasProAccess, progress, saveProgressInBackground]);
|
|
264
292
|
|
|
265
293
|
const sectionAvailability: Partial<Record<OnboardingSectionId, boolean>> = {
|
|
266
|
-
portfolio: !isBrokerCommitting,
|
|
294
|
+
portfolio: !isBrokerCommitting && !workspaceBuilt,
|
|
267
295
|
// The workspace is built when the desks are picked, so that step comes first.
|
|
268
296
|
cloud: !isBrokerCommitting && stage !== "desks" && (stage === "account" || stage === "upgrade" || stage === "ready" || !!progress.tickerSymbol),
|
|
269
297
|
pro: !isBrokerCommitting && planAccess.signedIn && (
|
|
@@ -282,7 +310,7 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
|
|
|
282
310
|
submitAccountField, backPortfolio, continuePortfolio, setPortfolioOptionIdx, activeBrokerFields,
|
|
283
311
|
brokerFieldIdx, setBrokerSelectIdx, brokerChoices, openBrokerGuide, chosenDesks, finishDesks,
|
|
284
312
|
setDeskCursor, deskCursor, setChosenDesks, continueAccount, skipSetup, primaryUpgradeAction,
|
|
285
|
-
setBillingInterval, keybindings, dialogOpen, commandBarOpen, saveProgressInBackground,
|
|
313
|
+
setBillingInterval, keybindings, dialogOpen, commandBarOpen, saveProgressInBackground, goBack,
|
|
286
314
|
});
|
|
287
315
|
|
|
288
316
|
return {
|
|
@@ -295,7 +323,7 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
|
|
|
295
323
|
openBrokerConnect, continueFromPositions, backPortfolio, continuePortfolio, chosenDesks, deskCursor,
|
|
296
324
|
setDeskCursor, setChosenDesks, buildingDesks, finishDesks, account, viewportWidth, viewportHeight, continueAccount,
|
|
297
325
|
offer, planAccess, pricing, billingInterval, setBillingInterval, continueFree, primaryUpgradeAction,
|
|
298
|
-
|
|
326
|
+
goBack, rerun,
|
|
299
327
|
};
|
|
300
328
|
}
|
|
301
329
|
|
|
@@ -310,7 +338,7 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
|
|
|
310
338
|
openBrokerConnect, continueFromPositions, backPortfolio, continuePortfolio, chosenDesks, deskCursor,
|
|
311
339
|
setDeskCursor, setChosenDesks, buildingDesks, finishDesks, account, viewportWidth, viewportHeight, continueAccount,
|
|
312
340
|
offer, planAccess, pricing, billingInterval, setBillingInterval, continueFree, primaryUpgradeAction,
|
|
313
|
-
|
|
341
|
+
goBack, rerun,
|
|
314
342
|
} = useOnboardingWizard(props);
|
|
315
343
|
|
|
316
344
|
if (helpFocused) {
|
|
@@ -322,12 +350,15 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
|
|
|
322
350
|
return <OnboardingCoach step={t("YOUR WORKSPACE")}
|
|
323
351
|
title={tf("Built around {ticker}", { ticker })}
|
|
324
352
|
actions={<>
|
|
325
|
-
{/*
|
|
326
|
-
|
|
353
|
+
{/* Each button shows its key, as Connect shows Alt+Enter; in the
|
|
354
|
+
terminal the second wraps under the first. The desktop card has no
|
|
355
|
+
room for both: like a toast, its dismiss key is in the tooltip.
|
|
356
|
+
F10 also dismisses it. */}
|
|
327
357
|
<OnboardingButton
|
|
328
358
|
label="Keep exploring"
|
|
329
359
|
variant="ghost"
|
|
330
|
-
|
|
360
|
+
shortcut={!desktop && notificationDismissShortcut ? notificationDismissShortcut : undefined}
|
|
361
|
+
title={desktop && notificationDismissShortcut ? `${t("Keep exploring")} (${notificationDismissShortcut})` : undefined}
|
|
331
362
|
onPress={() => { void finish(); }}
|
|
332
363
|
/>
|
|
333
364
|
<OnboardingButton
|
|
@@ -384,7 +415,8 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
|
|
|
384
415
|
onDismiss={skipSetup}
|
|
385
416
|
dismissing={isFinishing}
|
|
386
417
|
dismissDisabled={isBrokerCommitting}
|
|
387
|
-
|
|
418
|
+
// The first run needs a holding; a run from Settings can be left as it was.
|
|
419
|
+
showDismiss={rerun}
|
|
388
420
|
/>
|
|
389
421
|
<OnboardingTitle
|
|
390
422
|
step={desktop ? undefined : t("PORTFOLIO")}
|
|
@@ -464,12 +496,14 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
|
|
|
464
496
|
onNavigate={goToSection}
|
|
465
497
|
onDismiss={skipSetup}
|
|
466
498
|
dismissing={isFinishing}
|
|
467
|
-
|
|
499
|
+
dismissDisabled={buildingDesks}
|
|
500
|
+
showDismiss={rerun}
|
|
468
501
|
/>
|
|
469
502
|
<OnboardingTitle
|
|
470
503
|
step={desktop ? undefined : t("DESKS")}
|
|
471
|
-
title={t("What do you trade?")}
|
|
472
|
-
|
|
504
|
+
title={t("What do you trade most?")}
|
|
505
|
+
// A line of its own: the picks guide the workspace, they do not limit it.
|
|
506
|
+
description={`${t("Pick up to 3; each adds a ready-made tab.")}\n${t("Everything else is one search away.")}`}
|
|
473
507
|
/>
|
|
474
508
|
{/* Not allowed to shrink: on a short window the card scrolls instead of the list running under the buttons. */}
|
|
475
509
|
<DesksStep
|
|
@@ -591,7 +625,7 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
|
|
|
591
625
|
<OnboardingButton
|
|
592
626
|
label="Back"
|
|
593
627
|
variant="ghost"
|
|
594
|
-
onPress={account.accountSub === "qr" || account.accountSub === "login" ? account.returnToAccountForm :
|
|
628
|
+
onPress={account.accountSub === "qr" || account.accountSub === "login" ? account.returnToAccountForm : goBack}
|
|
595
629
|
/>
|
|
596
630
|
{account.accountSub !== "qr" ? (
|
|
597
631
|
<OnboardingButton
|
|
@@ -618,7 +652,7 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
|
|
|
618
652
|
: proCopy.note;
|
|
619
653
|
// Ranked: the data itself first, then what reads it.
|
|
620
654
|
const features = [
|
|
621
|
-
{ title:
|
|
655
|
+
{ title: t("Real-time market data"), description: t("Free is 15 minutes behind on quotes and 12 hours on news.") },
|
|
622
656
|
{ title: t("MCP server"), description: t("Claude Code, Codex or Cursor call Gloom's research tools.") },
|
|
623
657
|
{ title: t("Ask Gloom"), description: t("Answers cite filings, calls and news.") },
|
|
624
658
|
{ title: t("Earnings calls"), description: t("Transcripts, summaries, guidance and scores.") },
|
|
@@ -731,7 +765,7 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
|
|
|
731
765
|
<OnboardingButton
|
|
732
766
|
label="Back"
|
|
733
767
|
variant="ghost"
|
|
734
|
-
onPress={
|
|
768
|
+
onPress={goBack}
|
|
735
769
|
/>
|
|
736
770
|
<OnboardingButton
|
|
737
771
|
label={isFinishing ? "Opening workspace..." : "Start exploring"}
|
|
@@ -7,6 +7,8 @@ import { formatMarketPrice } from "../../../market-data/market/format";
|
|
|
7
7
|
import { getRegularSessionDisplay } from "../../../market-data/market/status";
|
|
8
8
|
import { formatPercentRaw } from "../../../utils/format";
|
|
9
9
|
import { Button, NumberField, TextField } from "../../ui";
|
|
10
|
+
import { DelayedMark } from "../../ui/delayed-mark";
|
|
11
|
+
import { useFreeQuoteDelay } from "../../../api-client/plan-access";
|
|
10
12
|
import { ONBOARDING_DESKTOP } from "../onboarding-frame";
|
|
11
13
|
import {
|
|
12
14
|
POSITION_FIELDS,
|
|
@@ -31,7 +33,7 @@ const FIELD_LABELS: Record<PositionFieldId, string> = {
|
|
|
31
33
|
const FIELD_PLACEHOLDERS: Record<Exclude<PositionFieldId, "currency">, string> = {
|
|
32
34
|
ticker: "e.g. AAPL",
|
|
33
35
|
shares: "optional",
|
|
34
|
-
avgCost: "
|
|
36
|
+
avgCost: "last price",
|
|
35
37
|
};
|
|
36
38
|
|
|
37
39
|
/** A field's label; the size counts contracts once the symbol is a future. */
|
|
@@ -47,6 +49,8 @@ function fieldPlaceholder(field: PositionFieldId, state: OnboardingPositionsStat
|
|
|
47
49
|
/** Company name and last price for the symbol being typed, or why it will not resolve. */
|
|
48
50
|
function PreviewLine({ preview, error }: Pick<OnboardingPositionsState, "preview" | "error">) {
|
|
49
51
|
const colors = useThemeColors();
|
|
52
|
+
// Signed out or free, the price is the delayed one, and says so.
|
|
53
|
+
const delay = useFreeQuoteDelay(preview.status === "ready" ? preview.quote : null);
|
|
50
54
|
if (error) return <Text fg={colors.negative} wrapText>{error}</Text>;
|
|
51
55
|
if (preview.status === "idle") return null;
|
|
52
56
|
if (preview.status === "checking") {
|
|
@@ -58,11 +62,12 @@ function PreviewLine({ preview, error }: Pick<OnboardingPositionsState, "preview
|
|
|
58
62
|
const price = headline?.price;
|
|
59
63
|
const change = headline?.changePercent;
|
|
60
64
|
return (
|
|
61
|
-
<Box flexDirection="row" minWidth={0} overflow="hidden">
|
|
65
|
+
<Box flexDirection="row" alignItems="center" minWidth={0} overflow="hidden">
|
|
62
66
|
<Text fg={colors.text} attributes={TextAttributes.BOLD}>{preview.symbol}</Text>
|
|
63
67
|
{preview.name ? <Text fg={colors.textDim}>{` ${preview.name}`}</Text> : null}
|
|
64
68
|
{price != null ? <Text fg={colors.text}>{` ${formatMarketPrice(price, { maxWidth: 12 })}`}</Text> : null}
|
|
65
69
|
{change != null ? <Text fg={shownPriceColor(change, 2, colors)}>{` ${formatPercentRaw(change)}`}</Text> : null}
|
|
70
|
+
{price != null && delay != null ? <Box flexShrink={0} paddingLeft={1}><DelayedMark minutes={delay} /></Box> : null}
|
|
66
71
|
{preview.duplicate ? <Text fg={colors.textMuted}>{t(" already added")}</Text> : null}
|
|
67
72
|
</Box>
|
|
68
73
|
);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAppDispatch, useAppStateRef } from "../../state/app/context";
|
|
3
|
+
import { saveConfigImmediately } from "../../state/config-save-scheduler";
|
|
4
|
+
import type { OnboardingProgress } from "../../types/config";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Settings' Run Setup Again: setup from its first step, on what the user has.
|
|
8
|
+
* The holdings are the portfolio's own, and the desks setup made start
|
|
9
|
+
* ticked; finishing rebuilds setup's layouts in place and leaves the user's
|
|
10
|
+
* alone. `open` shows the wizard once the progress is saved; without it
|
|
11
|
+
* (a host that cannot run setup) there is nothing to run.
|
|
12
|
+
*/
|
|
13
|
+
export function useRunSetupAgain(open: (() => void) | undefined): (() => Promise<void>) | undefined {
|
|
14
|
+
const dispatch = useAppDispatch();
|
|
15
|
+
const stateRef = useAppStateRef();
|
|
16
|
+
const run = useCallback(async () => {
|
|
17
|
+
if (!open) return;
|
|
18
|
+
const progress: OnboardingProgress = { version: 1, stage: "portfolio", rerun: true };
|
|
19
|
+
await saveConfigImmediately({ ...stateRef.current.config, onboardingComplete: false, onboardingProgress: progress });
|
|
20
|
+
dispatch({ type: "SET_ONBOARDING_STATE", complete: false, progress });
|
|
21
|
+
open();
|
|
22
|
+
}, [dispatch, open, stateRef]);
|
|
23
|
+
return open ? run : undefined;
|
|
24
|
+
}
|
|
@@ -10,7 +10,13 @@ import type { PluginRegistry } from "../../plugins/registry";
|
|
|
10
10
|
import { resolvePlanAccess } from "../../api-client/plan-access";
|
|
11
11
|
import type { PortfolioSub } from "./onboarding-steps";
|
|
12
12
|
import type { useOnboardingPositions } from "./wizard-positions";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
applyFirstRunLayout,
|
|
15
|
+
buildFirstRunLayout,
|
|
16
|
+
onboardingDeskLayoutId,
|
|
17
|
+
planFirstRunWatchlist,
|
|
18
|
+
setupDeskPicks,
|
|
19
|
+
} from "./first-run-workspace";
|
|
14
20
|
import { buildDesk, getDesk, isDeskKey, isDeskStock, pickDeskCompany, type DeskKey } from "../../layout/desks";
|
|
15
21
|
import { debugLog } from "../../utils/debug-log";
|
|
16
22
|
import {
|
|
@@ -92,15 +98,18 @@ export function useOnboardingWorkspace({
|
|
|
92
98
|
selectedBrokerId: string | null;
|
|
93
99
|
setPortfolioSub: Dispatch<SetStateAction<PortfolioSub>>;
|
|
94
100
|
}) {
|
|
95
|
-
|
|
101
|
+
// Run again, it starts with the desks it made ticked.
|
|
102
|
+
const [chosenDesks, setChosenDesks] = useState<DeskKey[]>(() => setupDeskPicks(stateRef.current.config));
|
|
96
103
|
const [deskCursor, setDeskCursor] = useState(0);
|
|
97
104
|
const [buildingDesks, setBuildingDesks] = useState(false);
|
|
98
105
|
const buildingDesksRef = useRef(false);
|
|
99
106
|
|
|
100
107
|
/**
|
|
101
108
|
* Seeds the watchlist and swaps Home for the first-run workspace built
|
|
102
|
-
* around `symbol`, with the picked desks as tabs right after it.
|
|
103
|
-
*
|
|
109
|
+
* around `symbol`, with the picked desks as tabs right after it. Run again,
|
|
110
|
+
* it replaces the layouts it made before instead of adding copies, and
|
|
111
|
+
* from Settings it leaves an existing watchlist alone. The config comes
|
|
112
|
+
* back for the caller to persist with its own progress patch.
|
|
104
113
|
*/
|
|
105
114
|
const buildFirstRunWorkspace = useCallback(async (
|
|
106
115
|
baseConfig: AppConfig,
|
|
@@ -109,12 +118,17 @@ export function useOnboardingWorkspace({
|
|
|
109
118
|
desks: readonly DeskKey[],
|
|
110
119
|
): Promise<AppConfig> => {
|
|
111
120
|
let config = baseConfig;
|
|
121
|
+
const rerun = getOnboardingProgress(baseConfig).rerun === true;
|
|
112
122
|
let watchlistId = config.watchlists[0]?.id;
|
|
123
|
+
// A run from Settings leaves a watchlist the user has as it is.
|
|
124
|
+
const seedWatchlist = !rerun || !watchlistId;
|
|
113
125
|
if (!watchlistId) {
|
|
114
126
|
watchlistId = "watchlist";
|
|
115
127
|
config = { ...config, watchlists: [{ id: watchlistId, name: "Watchlist" }] };
|
|
116
128
|
}
|
|
117
|
-
const plan =
|
|
129
|
+
const plan = seedWatchlist
|
|
130
|
+
? planFirstRunWatchlist(stateRef.current.tickers, watchlistId, portfolioId)
|
|
131
|
+
: { create: [], update: [] };
|
|
118
132
|
for (const metadata of plan.create) {
|
|
119
133
|
try {
|
|
120
134
|
const ticker = await pluginRegistry.tickerRepository.createTicker(metadata);
|
|
@@ -138,7 +152,7 @@ export function useOnboardingWorkspace({
|
|
|
138
152
|
watchlistId,
|
|
139
153
|
hasPane: (paneId) => pluginRegistry.panes?.has(paneId) ?? false,
|
|
140
154
|
});
|
|
141
|
-
const workspace = applyFirstRunLayout(config, home);
|
|
155
|
+
const workspace = applyFirstRunLayout(config, home, [], { rerun });
|
|
142
156
|
if (desks.length === 0) return workspace;
|
|
143
157
|
const { tickers, financials } = stateRef.current;
|
|
144
158
|
const held = [...tickers.values()]
|
|
@@ -150,13 +164,15 @@ export function useOnboardingWorkspace({
|
|
|
150
164
|
const pro = resolvePlanAccess(apiClient.getCurrentUser()).hasProAccess;
|
|
151
165
|
// A desk that fails to build is left out; it never holds up the first run.
|
|
152
166
|
const deskTabs = await Promise.all(desks.map((key) => (
|
|
153
|
-
buildDesk(getDesk(key), { catalog: pluginRegistry, config: workspace, company, pro }).
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
167
|
+
buildDesk(getDesk(key), { catalog: pluginRegistry, config: workspace, company, pro }).then(
|
|
168
|
+
(tab) => (tab ? { ...tab, id: onboardingDeskLayoutId(key) } : null),
|
|
169
|
+
(error) => {
|
|
170
|
+
onboardingLog.error("Desk build failed", { desk: key, error: String(error) });
|
|
171
|
+
return null;
|
|
172
|
+
},
|
|
173
|
+
)
|
|
157
174
|
)));
|
|
158
|
-
|
|
159
|
-
return { ...workspace, layouts: [workspace.layouts[0]!, ...tabs, ...workspace.layouts.slice(1)] };
|
|
175
|
+
return applyFirstRunLayout(config, home, deskTabs.filter((tab) => tab !== null), { rerun });
|
|
160
176
|
}, [dispatch, pluginRegistry, stateRef]);
|
|
161
177
|
|
|
162
178
|
const commitWorkspaceProgress = useCallback(async (
|
|
@@ -187,7 +203,7 @@ export function useOnboardingWorkspace({
|
|
|
187
203
|
}, [positions, saveProgressInBackground]);
|
|
188
204
|
|
|
189
205
|
/**
|
|
190
|
-
* Leaves "What do you trade?": builds the workspace with the picked desks as
|
|
206
|
+
* Leaves "What do you trade most?": builds the workspace with the picked desks as
|
|
191
207
|
* tabs (none when skipped, which is today's workspace) and moves on.
|
|
192
208
|
*/
|
|
193
209
|
const finishDesks = useCallback((desks: readonly DeskKey[]) => {
|
|
@@ -270,7 +286,8 @@ export function useOnboardingCompletion({
|
|
|
270
286
|
setPersistenceError(null);
|
|
271
287
|
try {
|
|
272
288
|
await progressSaveQueueRef.current.catch(() => {});
|
|
273
|
-
const desks = getOnboardingProgress(stateRef.current.config)
|
|
289
|
+
const { desks: picked, rerun } = getOnboardingProgress(stateRef.current.config);
|
|
290
|
+
const desks = picked?.filter(isDeskKey);
|
|
274
291
|
const nextConfig: AppConfig = {
|
|
275
292
|
...stateRef.current.config,
|
|
276
293
|
onboardingComplete: true,
|
|
@@ -282,7 +299,8 @@ export function useOnboardingCompletion({
|
|
|
282
299
|
complete: true,
|
|
283
300
|
progress: undefined,
|
|
284
301
|
});
|
|
285
|
-
|
|
302
|
+
// The funnel counts first runs; a run from Settings is not one.
|
|
303
|
+
if (!rerun) recordResearchActivity(skipped ? "onboarding_skipped" : "onboarding_completed", undefined, undefined, { desks });
|
|
286
304
|
await Promise.resolve(onComplete(nextConfig));
|
|
287
305
|
} catch (error) {
|
|
288
306
|
setPersistenceError(error instanceof Error ? error.message : String(error));
|
|
@@ -6,14 +6,12 @@ import { chatController } from "../../plugins/builtin/chat/controller";
|
|
|
6
6
|
import type { CloudBillingInterval } from "../../account/billing";
|
|
7
7
|
import { loadUpgradeOffer, type UpgradeOffer } from "../../account/upgrade-dialog";
|
|
8
8
|
import { useCloudUpgradeAction } from "../../plugins/builtin/shared/cloud-upgrade";
|
|
9
|
-
import { useUpgradePersonalization } from "../../account/upgrade-personalization";
|
|
10
9
|
import { usePlanAccess } from "../../api-client/plan-access";
|
|
11
10
|
|
|
12
11
|
export function useOnboardingUpgrade({
|
|
13
|
-
stage, progress, saveProgressInBackground, persistProgress,
|
|
12
|
+
stage, progress, saveProgressInBackground, persistProgress,
|
|
14
13
|
}: {
|
|
15
14
|
stage: OnboardingStage;
|
|
16
|
-
shown: boolean;
|
|
17
15
|
progress: OnboardingProgress;
|
|
18
16
|
saveProgressInBackground: (patch: Partial<OnboardingProgress> & Pick<OnboardingProgress, "stage">, baseConfig?: AppConfig | undefined) => void;
|
|
19
17
|
persistProgress: (patch: Partial<OnboardingProgress> & Pick<OnboardingProgress, "stage">, baseConfig?: AppConfig | undefined) => Promise<AppConfig>;
|
|
@@ -25,8 +23,6 @@ export function useOnboardingUpgrade({
|
|
|
25
23
|
const appActive = useAppActive();
|
|
26
24
|
const planAccess = usePlanAccess();
|
|
27
25
|
const openUpgrade = useCloudUpgradeAction("onboarding-pro");
|
|
28
|
-
// The step lists the features from its first frame, so that is when it is shown.
|
|
29
|
-
const personalTickers = useUpgradePersonalization("onboarding-pro", shown && stage === "upgrade");
|
|
30
26
|
// The price and whether this account still has its trial, asked again for
|
|
31
27
|
// whichever account reaches the step.
|
|
32
28
|
const accountId = planAccess.signedIn ? apiClient.getCurrentUser()?.id ?? null : null;
|
|
@@ -82,6 +78,6 @@ export function useOnboardingUpgrade({
|
|
|
82
78
|
}, [progress.accountStatus, saveProgressInBackground]);
|
|
83
79
|
|
|
84
80
|
return {
|
|
85
|
-
offer, pricing, billingInterval, setBillingInterval, planAccess, primaryUpgradeAction, continueFree,
|
|
81
|
+
offer, pricing, billingInterval, setBillingInterval, planAccess, primaryUpgradeAction, continueFree,
|
|
86
82
|
};
|
|
87
83
|
}
|