gloomberb 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/api-client/types.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
} from "../types/financials";
|
|
12
12
|
import type { SyncSettings, SyncSnapshot } from "../sync/types";
|
|
13
13
|
import type { ChatPaneLink } from "./chat-pane-link";
|
|
14
|
+
import type { CreditLevelKind } from "./credit-boards";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* What stands behind a chat account. `discord` is a person on the Gloom
|
|
@@ -249,7 +250,7 @@ export interface AccountProfile {
|
|
|
249
250
|
showLocalTime?: boolean;
|
|
250
251
|
/** The desks chat shows beside your name; empty shows none. */
|
|
251
252
|
chatDesks?: string[];
|
|
252
|
-
/** Your answer to "What do you trade?" at the first run: private, it pre-fills `chatDesks`. */
|
|
253
|
+
/** Your answer to "What do you trade most?" at the first run: private, it pre-fills `chatDesks`. */
|
|
253
254
|
onboardingDesks?: string[];
|
|
254
255
|
syncEnabled: boolean;
|
|
255
256
|
weeklyRoundupEnabled: boolean;
|
|
@@ -486,12 +487,16 @@ export interface CloudPricePointPayload {
|
|
|
486
487
|
* alone), served in place of `volume`.
|
|
487
488
|
*/
|
|
488
489
|
venueVolume?: { venue: string; volume: number };
|
|
490
|
+
/** What a coin bar's volume counts: `aggregate` across venues, or `3 spot venues` summed. */
|
|
491
|
+
volumeBasis?: string;
|
|
489
492
|
/** A daily bar still forming when served: a coin's UTC day, or the trade date a future is trading. */
|
|
490
493
|
partial?: boolean;
|
|
491
494
|
/** A coin's daily bar whose prices were rebuilt from hourly bars; a day the source left empty has no volume. */
|
|
492
495
|
filled?: boolean;
|
|
493
496
|
/** A future's closed session whose settlement is not confirmed yet: it closes on the last trade. */
|
|
494
497
|
unsettled?: boolean;
|
|
498
|
+
/** A continuous future's calendar bar: the contract it closes on (NGZ26), where the server knows it. */
|
|
499
|
+
contract?: string;
|
|
495
500
|
}
|
|
496
501
|
|
|
497
502
|
type CloudEconImpact = "high" | "medium" | "low";
|
|
@@ -689,6 +694,8 @@ export interface CloudCdsHistoryPointPayload {
|
|
|
689
694
|
maturity: string;
|
|
690
695
|
/** For a line quoted upfront: that day's level in points on the line's coupon. */
|
|
691
696
|
upfrontPts?: number;
|
|
697
|
+
/** The day's trades, or the settlement price on a day without enough of them (`prints` 0). */
|
|
698
|
+
kind?: CreditLevelKind;
|
|
692
699
|
}
|
|
693
700
|
|
|
694
701
|
/**
|
|
@@ -735,6 +742,8 @@ export interface CloudCdsLevelPayload extends CloudCdsUpfrontQuote {
|
|
|
735
742
|
issuer: string;
|
|
736
743
|
spreadBp: number;
|
|
737
744
|
date: string;
|
|
745
|
+
/** As the history response's last point. */
|
|
746
|
+
kind?: CreditLevelKind;
|
|
738
747
|
/** As the history response's moves for the same name. */
|
|
739
748
|
change1W?: number | null;
|
|
740
749
|
change1M?: number | null;
|
|
@@ -810,6 +819,12 @@ export interface CloudCongressTradePayload {
|
|
|
810
819
|
sourceUrl: string;
|
|
811
820
|
returnSinceTx?: number | null;
|
|
812
821
|
returnSinceFiling?: number | null;
|
|
822
|
+
/** SPY's return over the same days as returnSinceTx, percent. */
|
|
823
|
+
spyReturnSinceTx?: number | null;
|
|
824
|
+
spyReturnSinceFiling?: number | null;
|
|
825
|
+
/** returnSinceTx less spyReturnSinceTx, percentage points. */
|
|
826
|
+
excessSinceTx?: number | null;
|
|
827
|
+
excessSinceFiling?: number | null;
|
|
813
828
|
returnAsOf?: string | null;
|
|
814
829
|
party?: string | null;
|
|
815
830
|
committees?: string[];
|
|
@@ -829,9 +844,12 @@ export interface CloudCongressMemberPayload {
|
|
|
829
844
|
lastFilingDate: string | null;
|
|
830
845
|
avgLagDays: number | null;
|
|
831
846
|
medianReturn?: number | null;
|
|
847
|
+
/** Median of the trades' excessSinceFiling. */
|
|
848
|
+
medianExcessSinceFiling?: number | null;
|
|
832
849
|
buyHitRate?: number | null;
|
|
833
850
|
pricedTradeCount?: number;
|
|
834
851
|
pricedBuyCount?: number;
|
|
852
|
+
excessTradeCount?: number;
|
|
835
853
|
party?: string | null;
|
|
836
854
|
committees?: string[];
|
|
837
855
|
}
|
|
@@ -1504,6 +1522,8 @@ interface CloudNewsTickerLinkPayload {
|
|
|
1504
1522
|
confidence: number;
|
|
1505
1523
|
relevanceScore: number;
|
|
1506
1524
|
impactScore?: number;
|
|
1525
|
+
/** The company's market cap, stated on its home listing and not on a cross-listing. */
|
|
1526
|
+
marketCap?: number | null;
|
|
1507
1527
|
sentiment?: "positive" | "neutral" | "negative" | null;
|
|
1508
1528
|
}
|
|
1509
1529
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { useEffect, useRef, useSyncExternalStore } from "react";
|
|
2
|
+
import { apiClient } from "../api-client";
|
|
3
|
+
import { t } from "../i18n";
|
|
4
|
+
import { useAppActive } from "../state/app/activity";
|
|
5
|
+
import { useAppDispatch, useAppGetState, useAppSelector } from "../state/app/context";
|
|
6
|
+
import { scheduleConfigSave } from "../state/config-save-scheduler";
|
|
7
|
+
import { cloudSyncController } from "../sync/controller";
|
|
8
|
+
import { attentionCollectionAvailable } from "../telemetry/attention-counts";
|
|
9
|
+
import { ATTENTION_OFF_MESSAGE, attentionNoticeDue, withAttentionNoticeShown } from "../telemetry/attention-setting";
|
|
10
|
+
import { reportTelemetryConfig } from "../telemetry/live-config";
|
|
11
|
+
import type { TelemetryConfig } from "../types/config";
|
|
12
|
+
import type { AppNotificationRequest } from "../types/plugin";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The whole notice, with no title: the terminal draws none, and it has to
|
|
16
|
+
* name the setting its Turn off acts on and say plainly what leaves the app.
|
|
17
|
+
*/
|
|
18
|
+
const ATTENTION_NOTICE_TEXT = "Attention Counts is now on. Tickers you open are sent to Gloom with your account; only hourly totals of 20+ people are published.";
|
|
19
|
+
|
|
20
|
+
/** The signed-in account with a verified email, or null; the same account the collector counts for. */
|
|
21
|
+
function verifiedAccount(): string | null {
|
|
22
|
+
return apiClient.getVerifiedSessionIdentity() ? apiClient.getCurrentUser()?.id ?? null : null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Where the account's synced settings come from; whether they were pulled this session. */
|
|
26
|
+
export interface AccountSettingsSource {
|
|
27
|
+
subscribe(listener: () => void): () => void;
|
|
28
|
+
getStatus(): { lastPullAt: string | null };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The one-time notice that Attention Counts is on by default: a toast that
|
|
33
|
+
* stays until dismissed, with Turn off. It shows once per account on this
|
|
34
|
+
* installation, while the app is in front, to a verified account whose
|
|
35
|
+
* setting is untouched, and only after this session has pulled the
|
|
36
|
+
* account's synced settings, so an off made on another device arrives
|
|
37
|
+
* before it. Nothing is counted under the default until the toast has been
|
|
38
|
+
* drawn for that account; an explicit on or off never sees it.
|
|
39
|
+
*/
|
|
40
|
+
export function AttentionCountsNotice({
|
|
41
|
+
notify,
|
|
42
|
+
accountSettings = cloudSyncController,
|
|
43
|
+
}: {
|
|
44
|
+
notify(request: AppNotificationRequest): void;
|
|
45
|
+
accountSettings?: AccountSettingsSource;
|
|
46
|
+
}) {
|
|
47
|
+
const dispatch = useAppDispatch();
|
|
48
|
+
const getState = useAppGetState();
|
|
49
|
+
const telemetry = useAppSelector((state) => state.config.telemetry);
|
|
50
|
+
const account = useSyncExternalStore((onChange) => apiClient.subscribeCurrentUser(onChange), verifiedAccount, verifiedAccount);
|
|
51
|
+
const pulled = useSyncExternalStore(
|
|
52
|
+
(onChange) => accountSettings.subscribe(onChange),
|
|
53
|
+
() => accountSettings.getStatus().lastPullAt !== null,
|
|
54
|
+
() => accountSettings.getStatus().lastPullAt !== null,
|
|
55
|
+
);
|
|
56
|
+
const onScreen = useAppActive();
|
|
57
|
+
/** Accounts this session already showed it to, so a re-render never stacks a second toast. */
|
|
58
|
+
const asked = useRef(new Set<string>());
|
|
59
|
+
const due = pulled && attentionNoticeDue(telemetry, account) && attentionCollectionAvailable();
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!due || !onScreen || !account || asked.current.has(account)) return;
|
|
63
|
+
asked.current.add(account);
|
|
64
|
+
const save = (change: (telemetry: TelemetryConfig | undefined) => TelemetryConfig | undefined) => {
|
|
65
|
+
const current = getState().config;
|
|
66
|
+
const next = change(current.telemetry);
|
|
67
|
+
if (next === current.telemetry) return;
|
|
68
|
+
// Reported first, so turning off drops unsent counts before React commits.
|
|
69
|
+
reportTelemetryConfig(next);
|
|
70
|
+
dispatch({ type: "SET_CONFIG", config: { ...current, telemetry: next } });
|
|
71
|
+
scheduleConfigSave(() => {
|
|
72
|
+
const state = getState();
|
|
73
|
+
return { ...state.config, recentTickers: state.recentTickers };
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
notify({
|
|
77
|
+
body: t(ATTENTION_NOTICE_TEXT),
|
|
78
|
+
type: "info",
|
|
79
|
+
persistent: true,
|
|
80
|
+
action: {
|
|
81
|
+
label: t("Turn off"),
|
|
82
|
+
onClick: () => {
|
|
83
|
+
save((current) => ({ ...current, attention: false }));
|
|
84
|
+
notify({ body: t(ATTENTION_OFF_MESSAGE), type: "success" });
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
onShown: () => {
|
|
88
|
+
if (verifiedAccount() === account) save((current) => withAttentionNoticeShown(current, account));
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}, [account, dispatch, due, getState, notify, onScreen]);
|
|
92
|
+
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
type UsageFunction,
|
|
33
33
|
} from "../../telemetry/usage-counts";
|
|
34
34
|
import { debugLog } from "../../utils/debug-log";
|
|
35
|
-
import { startMainThreadStallMonitor } from "../../utils/main-thread-stall";
|
|
35
|
+
import { markStartupComplete, startMainThreadStallMonitor } from "../../utils/main-thread-stall";
|
|
36
36
|
import { measurePerfAsync } from "../../utils/perf-marks";
|
|
37
37
|
|
|
38
38
|
const appLog = debugLog.createLogger("app");
|
|
@@ -161,6 +161,8 @@ export function useAppStartupRuntime({
|
|
|
161
161
|
} catch (err) {
|
|
162
162
|
logFailure("app.runtime.startup", "Workspace startup hydration failed")(err);
|
|
163
163
|
}
|
|
164
|
+
// The workspace has its data (or gave up on it): stalls from here on are not startup ones.
|
|
165
|
+
markStartupComplete();
|
|
164
166
|
})();
|
|
165
167
|
}, [
|
|
166
168
|
autoImportBrokerPositions,
|
package/src/app.tsx
CHANGED
|
@@ -20,6 +20,7 @@ import { DetachedPaneShell } from "./components/layout/detached-pane-shell";
|
|
|
20
20
|
import { TransientLayoutProvider } from "./components/layout/transient-layout";
|
|
21
21
|
import { CommandBar } from "./components/command-bar/surface";
|
|
22
22
|
import { OnboardingWizard } from "./components/onboarding/onboarding-wizard";
|
|
23
|
+
import { useRunSetupAgain } from "./components/onboarding/run-setup-again";
|
|
23
24
|
import { CompanyPickerHost } from "./plugins/builtin/cloud/company-picker";
|
|
24
25
|
import { TerminalRelayHost } from "./plugins/builtin/cloud/terminal-relay/host";
|
|
25
26
|
import { SignInGate } from "./components/sign-in-gate";
|
|
@@ -30,7 +31,7 @@ import type { AppServicesFactory, AppTickerRepositoryPort } from "./core/app-ser
|
|
|
30
31
|
import { useThemeColors } from "./theme/theme-context";
|
|
31
32
|
import type { AppConfig } from "./types/config";
|
|
32
33
|
import type { DesktopDeepLinkBridge } from "./types/desktop-deeplink";
|
|
33
|
-
import type { CliLaunchRequest, GloomPlugin } from "./types/plugin";
|
|
34
|
+
import type { AppNotificationRequest, CliLaunchRequest, GloomPlugin } from "./types/plugin";
|
|
34
35
|
import type { DataProvider } from "./types/data-provider";
|
|
35
36
|
import type { DesktopDockPreviewState, DesktopSharedStateSnapshot, DesktopThemePreviewState, DesktopWindowBridge } from "./types/desktop-window";
|
|
36
37
|
import type { DesktopApplicationMenuBridge } from "./types/desktop-menu";
|
|
@@ -69,6 +70,7 @@ import { AppLanguageConfigObserver } from "./app/language-observer";
|
|
|
69
70
|
import { isPaneShareHandoff } from "./shares/location";
|
|
70
71
|
import { apiClient } from "./api-client";
|
|
71
72
|
import { reportTelemetryConfig } from "./telemetry/live-config";
|
|
73
|
+
import { AttentionCountsNotice } from "./app/attention-notice";
|
|
72
74
|
import { recordFunctionOpen } from "./telemetry/usage-counts";
|
|
73
75
|
|
|
74
76
|
const EMPTY_EXTERNAL_PLUGINS: LoadedExternalPlugin[] = [];
|
|
@@ -87,6 +89,8 @@ interface AppInnerProps {
|
|
|
87
89
|
updatesEnabled?: boolean;
|
|
88
90
|
onboardingActive?: boolean;
|
|
89
91
|
onOnboardingComplete?: (config: AppConfig) => void | Promise<void>;
|
|
92
|
+
/** Shows setup again once Run Setup Again has saved its progress; absent where setup cannot run. */
|
|
93
|
+
onRunSetupAgain?: () => void;
|
|
90
94
|
/** Hosted browser terminal: nothing is reachable until a session exists. */
|
|
91
95
|
signInGateActive?: boolean;
|
|
92
96
|
/** Fires once the startup state is in place and the first real layout can paint. */
|
|
@@ -127,6 +131,7 @@ function AppInner({
|
|
|
127
131
|
updatesEnabled = true,
|
|
128
132
|
onboardingActive = false,
|
|
129
133
|
onOnboardingComplete,
|
|
134
|
+
onRunSetupAgain,
|
|
130
135
|
signInGateActive = false,
|
|
131
136
|
onInitialized,
|
|
132
137
|
pluginsReady,
|
|
@@ -186,6 +191,8 @@ function AppInner({
|
|
|
186
191
|
const dialog = useDialog();
|
|
187
192
|
const toast = useToastHost();
|
|
188
193
|
const isDetachedWindow = desktopWindowBridge?.kind === "detached";
|
|
194
|
+
// Not while setup is already showing.
|
|
195
|
+
const runSetupAgain = useRunSetupAgain(onboardingActive ? undefined : onRunSetupAgain);
|
|
189
196
|
const detachedPaneId = isDetachedWindow ? desktopWindowBridge.paneId ?? null : null;
|
|
190
197
|
const focusDetachedPane = useMemo(() => {
|
|
191
198
|
const focus = desktopWindowBridge?.kind === "main" ? desktopWindowBridge.focusDetachedPane : undefined;
|
|
@@ -222,6 +229,7 @@ function AppInner({
|
|
|
222
229
|
onClick: dismissAfter(() => notification.secondaryAction?.onClick()),
|
|
223
230
|
}
|
|
224
231
|
: undefined,
|
|
232
|
+
onShown: notification.onShown,
|
|
225
233
|
};
|
|
226
234
|
if (type === "success") toastId = toast.success(notification.body, options);
|
|
227
235
|
else if (type === "error") toastId = toast.error(notification.body, options);
|
|
@@ -232,6 +240,9 @@ function AppInner({
|
|
|
232
240
|
const notify = useCallback((body: string, options?: { type?: "info" | "success" | "error" }) => {
|
|
233
241
|
pluginRegistry.notify({ body, ...options });
|
|
234
242
|
}, [pluginRegistry]);
|
|
243
|
+
const notifyRequest = useCallback((request: AppNotificationRequest) => {
|
|
244
|
+
pluginRegistry.notify(request);
|
|
245
|
+
}, [pluginRegistry]);
|
|
235
246
|
|
|
236
247
|
useEffect(() => {
|
|
237
248
|
if (desktopWindowBridge?.kind !== "main" || !desktopWindowBridge.subscribeDockPreview) return;
|
|
@@ -489,6 +500,7 @@ function AppInner({
|
|
|
489
500
|
/>
|
|
490
501
|
</TransientLayoutProvider>
|
|
491
502
|
{!onboardingActive ? <CompanyPickerHost pluginRegistry={pluginRegistry} /> : null}
|
|
503
|
+
{initialized && !onboardingActive && !signInGateActive ? <AttentionCountsNotice notify={notifyRequest} /> : null}
|
|
492
504
|
<TerminalRelayHost device={remoteControlAdapter?.terminalDevice} />
|
|
493
505
|
{onboardingActive && onOnboardingComplete ? (
|
|
494
506
|
<OnboardingWizard
|
|
@@ -505,6 +517,7 @@ function AppInner({
|
|
|
505
517
|
pluginRegistry={pluginRegistry}
|
|
506
518
|
quitApp={() => rendererHost.requestExit()}
|
|
507
519
|
onCheckForUpdates={updatesEnabled ? runUpdateCommand : undefined}
|
|
520
|
+
onRunSetupAgain={runSetupAgain}
|
|
508
521
|
onNativeOccluderChange={setCommandBarNativeOccluder}
|
|
509
522
|
/>
|
|
510
523
|
)}
|
|
@@ -534,6 +547,11 @@ interface AppProps {
|
|
|
534
547
|
* terminal sets this; desktop and the TUI keep sign-in optional.
|
|
535
548
|
*/
|
|
536
549
|
requireSignIn?: boolean;
|
|
550
|
+
/**
|
|
551
|
+
* Settings can open setup again (Run Setup Again). The browser keeps no
|
|
552
|
+
* setup progress, so it turns this off.
|
|
553
|
+
*/
|
|
554
|
+
setupEnabled?: boolean;
|
|
537
555
|
/** Fires once the startup state is in place and the first real layout can paint. */
|
|
538
556
|
onInitialized?: () => void;
|
|
539
557
|
}
|
|
@@ -552,6 +570,7 @@ export function App({
|
|
|
552
570
|
remoteControlAdapter,
|
|
553
571
|
updatesEnabled = true,
|
|
554
572
|
requireSignIn = false,
|
|
573
|
+
setupEnabled = true,
|
|
555
574
|
onInitialized,
|
|
556
575
|
}: AppProps) {
|
|
557
576
|
useAppLanguage();
|
|
@@ -594,6 +613,8 @@ export function App({
|
|
|
594
613
|
&& (!effectiveInitialConfig.onboardingComplete || !!effectiveInitialConfig.onboardingProgress)
|
|
595
614
|
));
|
|
596
615
|
|
|
616
|
+
const showSetup = useCallback(() => setShowOnboarding(true), []);
|
|
617
|
+
|
|
597
618
|
useEffect(() => bindAppActivity(renderer), [renderer]);
|
|
598
619
|
|
|
599
620
|
const services = useMemo(() => {
|
|
@@ -656,6 +677,7 @@ export function App({
|
|
|
656
677
|
setConfig(updatedConfig);
|
|
657
678
|
setShowOnboarding(false);
|
|
658
679
|
}}
|
|
680
|
+
onRunSetupAgain={setupEnabled && !shareHandoff ? showSetup : undefined}
|
|
659
681
|
/>
|
|
660
682
|
</AppProvider>
|
|
661
683
|
</RemoteUiRegistryProvider>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"src/cli/commands/options-chain.ts": ["options","options-calculator"],
|
|
10
10
|
"src/cli/commands/options-leaps.ts": ["iv-history","options","vol-surface"],
|
|
11
11
|
"src/cli/commands/overview.ts": ["correlation","econ","market-movers","sectors","world-indices"],
|
|
12
|
-
"src/cli/commands/ticker.ts": ["ipo-calendar","notes"],
|
|
12
|
+
"src/cli/commands/ticker.ts": ["ipo-calendar","notes","sec"],
|
|
13
13
|
"src/cli/desktop-pane-shot.ts": ["market-valuation","time-sales"],
|
|
14
14
|
"src/cli/pane-functions/data.ts": ["chart-composer","correlation","seasonality","ticker-detail"],
|
|
15
15
|
"src/cli/pane-functions/financial-ratio-shot.ts": ["ticker-detail"],
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"src/components/onboarding/use-onboarding-upgrade.ts": ["chat"],
|
|
29
29
|
"src/layout/desks.ts": ["chart-composer"],
|
|
30
30
|
"src/plugins/catalog-browser.ts": ["alerts","analytics","attention","awards","backtest","bond-calculator","catalysts","cds","central-bank-rates","changelog","chart-composer","clinical-trials","cloud","comment-letters","commodities-energy","commodity-balances","company-attention","company-kpis","connections","correlation","cot","cpi","credit-boards","credit-conditions","credit-documents","crypto-board","crypto-etf-flows","debt-maturities","dividend-yield","docs","doe","earnings","earnings-calls","earnings-ripple","econ","econ-statistics","equity-screener","estimate-revisions","executives","exposure","fear-greed","filing-events","fixed-income","futures","futures-curve","fx-matrix","gpu","help","holders","insider","ipo-calendar","iv-history","jobs","jodi","kelly-sizer","layout-manager","macro-day","market-halts","market-heatmap","market-valuation","members","mna","money-markets","news","openfda","options","options-calculator","options-positioning","options-scenario","pe-band","perps","portfolio-list","power","primary-dealers","rate-path","realized-vol","relative-rotation","research","research-search","revenue-breakdown","reverse-dcf","risk-factors","roadmap","scanner","seasonality","sec","sectors","short-interest","short-volume","social-mentions","supply-chain","thirteenf","ticker-detail","time-sales","trace-bonds","treasury-auctions","treasury-daily","vol-surface","volatility","world-indices","world-venue-map","yield-curve"],
|
|
31
|
+
"src/plugins/catalog-cli.ts": ["alerts","fear-greed","news","notes","portfolio-list"],
|
|
31
32
|
"src/plugins/catalog-ui.ts": ["alerts","clinical-trials","cloud","comment-letters","custom-view","fear-greed","ipo-calendar","market-halts","market-heatmap","members","news","notes","openfda","research-search"],
|
|
32
33
|
"src/plugins/catalog.ts": ["debug"],
|
|
33
34
|
"src/remote/controller.ts": ["custom-view"],
|
|
@@ -203,7 +203,7 @@ function resolvedOutput(value: unknown): ResolvedSeries {
|
|
|
203
203
|
const input = object(value, "resolve output");
|
|
204
204
|
onlyKeys(input, "resolve output", [
|
|
205
205
|
"id", "label", "color", "unit", "unitGroup", "nativeFrequency", "timestampMode", "dataShape", "style", "transform",
|
|
206
|
-
"axis", "panelId", "interpolation", "timeBasis", "latestChangePercent", "points", "warning", "hidden", "attribution",
|
|
206
|
+
"axis", "panelId", "interpolation", "timeBasis", "latestChangePercent", "points", "warning", "savedAt", "hidden", "attribution",
|
|
207
207
|
"priceOf",
|
|
208
208
|
]);
|
|
209
209
|
if (!Array.isArray(input.points)) fail("resolve output points", "expected an array.");
|
|
@@ -247,6 +247,7 @@ function resolvedOutput(value: unknown): ResolvedSeries {
|
|
|
247
247
|
: {}),
|
|
248
248
|
points: input.points.map(point),
|
|
249
249
|
...(warning !== undefined ? { warning } : {}),
|
|
250
|
+
...(input.savedAt !== undefined ? { savedAt: finiteNumber(input.savedAt, "resolve output saved copy time") as number } : {}),
|
|
250
251
|
...(attribution ? { attribution: {
|
|
251
252
|
source: boundedString(attribution.source, "resolve output source", 160),
|
|
252
253
|
...(publisher !== undefined ? { publisher } : {}),
|
|
@@ -36,6 +36,7 @@ export function assetDataProvider(provider: AssetDataProvider): AssetDataCapabil
|
|
|
36
36
|
operations: {
|
|
37
37
|
canProvide: op((input: any) => provider.canProvide?.(input.ticker, input.exchange, input.context) ?? true, "query"),
|
|
38
38
|
primaryMarketSourceName: op(() => provider.primaryMarketSourceName?.() ?? provider.name, "query"),
|
|
39
|
+
batchesReads: op((input: any) => provider.batchesReads?.(input.method) ?? false, "query"),
|
|
39
40
|
getCachedFinancialsForTargets: op((input: any) => provider.getCachedFinancialsForTargets?.(input.targets ?? [], input.options) ?? new Map()),
|
|
40
41
|
getQuotesBatch: op((input: any) => provider.getQuotesBatch?.(input.targets ?? [], input.options) ?? Promise.resolve([])),
|
|
41
42
|
getTickerFinancialsBatch: op((input: any) => provider.getTickerFinancialsBatch?.(input.targets ?? [], input.options) ?? Promise.resolve([])),
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { format } from "util";
|
|
2
|
+
import { asciiText } from "../utils/ascii-text";
|
|
3
|
+
|
|
4
|
+
const NON_ASCII = /[^\x00-\x7F]/;
|
|
5
|
+
|
|
6
|
+
type ConsoleMethod = "log" | "info" | "warn" | "error";
|
|
7
|
+
const CONSOLE_STREAMS: ReadonlyArray<readonly [ConsoleMethod, "stdout" | "stderr"]> = [
|
|
8
|
+
["log", "stdout"], ["info", "stdout"], ["warn", "stderr"], ["error", "stderr"],
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Writes the command's text in ASCII while `enabled` says so (`cliAsciiEnabled`:
|
|
13
|
+
* plain output to a terminal that cannot show UTF-8), whichever way it is
|
|
14
|
+
* written: `process.stdout.write`, `process.stderr.write` or the console, which
|
|
15
|
+
* Bun writes without going through either stream. Tables are already ASCII
|
|
16
|
+
* when they are drawn, so their columns line up (`renderTable`); this catches
|
|
17
|
+
* the lines around them, such as a source line's `·`. A chunk that is not a
|
|
18
|
+
* string (a PNG on stdout) passes as it is, and so does all output while
|
|
19
|
+
* `enabled` is false.
|
|
20
|
+
*/
|
|
21
|
+
export function writeAsciiWhile(enabled: () => boolean): void {
|
|
22
|
+
const needsAscii = (text: string) => NON_ASCII.test(text) && enabled();
|
|
23
|
+
for (const name of ["stdout", "stderr"] as const) {
|
|
24
|
+
const stream = process[name];
|
|
25
|
+
const write = stream.write.bind(stream) as (chunk: unknown, ...rest: unknown[]) => boolean;
|
|
26
|
+
stream.write = ((chunk: unknown, ...rest: unknown[]) => write(
|
|
27
|
+
typeof chunk === "string" && needsAscii(chunk) ? asciiText(chunk) : chunk,
|
|
28
|
+
...rest,
|
|
29
|
+
)) as typeof stream.write;
|
|
30
|
+
}
|
|
31
|
+
for (const [method, name] of CONSOLE_STREAMS) {
|
|
32
|
+
const original = console[method].bind(console);
|
|
33
|
+
console[method] = (...args: unknown[]) => {
|
|
34
|
+
const text = format(...args);
|
|
35
|
+
if (!needsAscii(text)) {
|
|
36
|
+
original(...args);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
process[name].write(`${asciiText(text)}\n`);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -3,12 +3,14 @@ import { fixedUsdRate, heldCurrencyUnit } from "../utils/currency-units";
|
|
|
3
3
|
import { logFailure } from "../utils/log-failure";
|
|
4
4
|
|
|
5
5
|
/** One rate a converter used: units of `baseCurrency` per unit of `currency`, and when it was observed. */
|
|
6
|
-
interface BaseConversion {
|
|
6
|
+
export interface BaseConversion {
|
|
7
7
|
currency: string;
|
|
8
8
|
baseCurrency: string;
|
|
9
9
|
rate: number;
|
|
10
10
|
/** Epoch ms of the older leg's observation; null when the source did not date it. */
|
|
11
11
|
asOf: number | null;
|
|
12
|
+
/** A leg is past its fresh window, or its refresh failed and the last copy stands. */
|
|
13
|
+
stale: boolean;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
/** Converts an amount into the base currency, and lists the rates it used so a report can state them. */
|
|
@@ -21,6 +23,8 @@ export type BaseConverter = ((value: number, fromCurrency: string) => Promise<nu
|
|
|
21
23
|
interface UsdRate {
|
|
22
24
|
rate: number;
|
|
23
25
|
asOf: number | null;
|
|
26
|
+
/** Past its fresh window, or kept after a failed refresh. */
|
|
27
|
+
stale?: boolean;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export function createBaseConverter(
|
|
@@ -38,7 +42,10 @@ export function createBaseConverter(
|
|
|
38
42
|
if (dataProvider.getCachedQuery) {
|
|
39
43
|
const entry = await dataProvider.getCachedQuery("getExchangeRate", [currency])
|
|
40
44
|
.load({ background: dataProvider.revalidatesInBackground });
|
|
41
|
-
return {
|
|
45
|
+
return {
|
|
46
|
+
rate: Number.isFinite(entry.value) && entry.value > 0 ? entry.value : Number.NaN, asOf: entry.asOf ?? null,
|
|
47
|
+
stale: entry.refreshError != null || entry.staleAt <= Date.now(),
|
|
48
|
+
};
|
|
42
49
|
}
|
|
43
50
|
const rate = await dataProvider.getExchangeRate(currency);
|
|
44
51
|
return { rate: Number.isFinite(rate) && rate > 0 ? rate : Number.NaN, asOf: null };
|
|
@@ -73,6 +80,7 @@ export function createBaseConverter(
|
|
|
73
80
|
baseCurrency: base.currency,
|
|
74
81
|
rate: fromRate.rate / baseRate.rate,
|
|
75
82
|
asOf: dates.length > 0 ? Math.min(...dates) : null,
|
|
83
|
+
stale: fromRate.stale === true || baseRate.stale === true,
|
|
76
84
|
});
|
|
77
85
|
return (value / from.divisor * fromRate.rate) / baseRate.rate * base.divisor;
|
|
78
86
|
};
|
|
@@ -55,6 +55,21 @@ function declaredOptions(command: CliCommandDef): Map<string, DeclaredOption> |
|
|
|
55
55
|
const EXPLAIN_HINT = "--explain follows the reports of fn <function> and portfolio show. "
|
|
56
56
|
+ "gloom catalog explain <term> prints what one term means; gloom catalog glossary lists every term.";
|
|
57
57
|
|
|
58
|
+
/** Options people reach for to see a price in another currency, which no command takes: `fx` converts. */
|
|
59
|
+
const CURRENCY_OPTIONS = new Set(["--currency", "--ccy", "--cur", "--convert", "--fx", "--to-currency", "--in-currency"]);
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* `quote SBLK --currency EUR`: prices are in each listing's currency, and
|
|
63
|
+
* `fx` converts an amount, so the error names it, with the code given.
|
|
64
|
+
*/
|
|
65
|
+
function currencyOptionHint(flag: string, args: readonly string[]): string | null {
|
|
66
|
+
if (!CURRENCY_OPTIONS.has(flag)) return null;
|
|
67
|
+
const at = args.findIndex((arg) => arg === flag || arg.startsWith(`${flag}=`));
|
|
68
|
+
const typed = (args[at]?.includes("=") ? args[at]!.slice(flag.length + 1) : args[at + 1] ?? "").trim().toUpperCase();
|
|
69
|
+
const code = /^[A-Z]{3}$/.test(typed) ? typed : "EUR";
|
|
70
|
+
return `Prices are in each listing's own currency. To convert an amount, use fx: gloom fx 100 USD/${code}.`;
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
function failUnknownOption(
|
|
59
74
|
command: BuiltinCliCommandDef,
|
|
60
75
|
flag: string,
|
|
@@ -65,7 +80,7 @@ function failUnknownOption(
|
|
|
65
80
|
const suggestion = suggestCliOption(flag, flags);
|
|
66
81
|
const help = `See gloom help ${command.name}.`;
|
|
67
82
|
fail(`Unknown option ${flag} for ${command.name}.`, [
|
|
68
|
-
command.unknownOptionHint?.(flag, args) ?? (flag === "--explain" ? EXPLAIN_HINT : ""),
|
|
83
|
+
command.unknownOptionHint?.(flag, args) ?? currencyOptionHint(flag, args) ?? (flag === "--explain" ? EXPLAIN_HINT : ""),
|
|
69
84
|
suggestion ? `Did you mean ${suggestion}?` : "",
|
|
70
85
|
flags.length > 0 ? `Options: ${flags.join(", ")}. ${help}` : `${command.name} takes no options. ${help}`,
|
|
71
86
|
].filter(Boolean).join("\n"));
|
|
@@ -73,21 +73,32 @@ export function coinElsewhereNote(symbol: string, listings: ReadonlyArray<{ key:
|
|
|
73
73
|
return `${symbol} is ${named}, not ${coin}. For ${coin} use ${symbol}-USD.`;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/** A top coin quoted in dollars (BTC-USD, or on the crypto venue BTC-USD:CCC), which the CRYP board lists. */
|
|
77
|
+
function isBoardCoinPair(symbol: string): boolean {
|
|
78
|
+
const [pair = "", venue] = symbol.trim().toUpperCase().split(":");
|
|
79
|
+
const match = venue === undefined || venue === "CCC" ? /^([A-Z0-9]+)-USD$/.exec(pair) : null;
|
|
80
|
+
return !!match && MAJOR_COINS.has(match[1]!);
|
|
81
|
+
}
|
|
82
|
+
|
|
76
83
|
interface QuoteNoteResult {
|
|
77
84
|
target: { symbol: string };
|
|
78
|
-
quote: Pick<Quote, "name" | "instrumentType"> | null;
|
|
85
|
+
quote: Pick<Quote, "name" | "instrumentType"> & Partial<Pick<Quote, "dataSource">> | null;
|
|
79
86
|
error: string | null;
|
|
80
87
|
}
|
|
81
88
|
|
|
82
89
|
/**
|
|
83
|
-
* The router words a failure as "<reason> for <symbol>"
|
|
84
|
-
*
|
|
90
|
+
* The router words a failure as "<reason> for <symbol>", alone or closing its
|
|
91
|
+
* first sentence ("Gloom's data service did not answer for X. Try again in a
|
|
92
|
+
* moment."); drop the symbol so equal reasons group. Any other line, such as a
|
|
93
|
+
* sentence the data service wrote, is kept as it reads.
|
|
85
94
|
*/
|
|
86
95
|
function failureReason(message: string, symbol: string): string {
|
|
87
96
|
const line = (message.split("\n")[0] ?? "").trim();
|
|
88
97
|
const bare = line.replace(/\.+$/, "");
|
|
89
98
|
const suffix = ` for ${symbol}`;
|
|
90
|
-
|
|
99
|
+
if (bare.toLowerCase().endsWith(suffix.toLowerCase())) return bare.slice(0, -suffix.length);
|
|
100
|
+
const at = line.toLowerCase().indexOf(`${suffix.toLowerCase()}. `);
|
|
101
|
+
return at > 0 ? `${line.slice(0, at)}${line.slice(at + suffix.length)}` : line;
|
|
91
102
|
}
|
|
92
103
|
|
|
93
104
|
/**
|
|
@@ -112,6 +123,14 @@ export function quoteNotes(results: QuoteNoteResult[]): string[] {
|
|
|
112
123
|
const perpetuals = results
|
|
113
124
|
.filter((result) => result.quote && isCryptoVenuePerpetual(result.quote.instrumentType, result.quote.name))
|
|
114
125
|
.map((result) => result.target.symbol);
|
|
126
|
+
// A top coin's delayed quote is live on the crypto board, which needs no plan to stream it.
|
|
127
|
+
const boardCoins = results
|
|
128
|
+
.filter((result) => result.quote?.dataSource === "delayed" && isCryptoInstrumentType(result.quote.instrumentType ?? "")
|
|
129
|
+
&& isBoardCoinPair(result.target.symbol))
|
|
130
|
+
.map((result) => result.target.symbol);
|
|
131
|
+
if (boardCoins.length > 0) {
|
|
132
|
+
notes.push(`${boardCoins.join(", ")} ${boardCoins.length === 1 ? "streams" : "stream"} live on the CRYP board (gloom fn CRYP).`);
|
|
133
|
+
}
|
|
115
134
|
if (perpetuals.length > 0) {
|
|
116
135
|
notes.push(`${perpetuals.join(", ")}: perpetual futures ${perpetuals.length === 1 ? "price" : "prices"} on crypto venues, 24/7; not the listed future, not spot.`);
|
|
117
136
|
}
|