@gloom-sh/terminal 0.19.2 → 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 +1 -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 +20 -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.tsx +23 -1
- package/src/architecture/core-builtin-allowlist.json +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 +92 -23
- package/src/cli/commands/news-search.ts +24 -5
- package/src/cli/commands/overview.ts +9 -4
- package/src/cli/commands/search.ts +71 -12
- 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 +5 -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/help.ts +1 -1
- package/src/cli/history-export.ts +22 -2
- package/src/cli/history-rows.ts +154 -4
- package/src/cli/index.ts +18 -1
- package/src/cli/known-gaps.ts +40 -5
- package/src/cli/listing-arg.ts +46 -9
- 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 +20 -0
- 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 +24 -1
- package/src/components/command-bar/surface/index.tsx +9 -3
- package/src/components/company-logo.tsx +2 -1
- package/src/components/layout/header.tsx +8 -3
- package/src/components/layout/market-summary.ts +25 -7
- package/src/components/layout/pane/footer/index.tsx +7 -2
- package/src/components/layout/pane/footer/model.ts +40 -5
- 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 +2 -1
- package/src/components/onboarding/onboarding-keyboard.ts +8 -5
- package/src/components/onboarding/onboarding-wizard.tsx +54 -23
- 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/external-link.tsx +3 -6
- package/src/components/ui/icon.tsx +8 -1
- package/src/components/ui/logo-mark.tsx +8 -5
- package/src/components/ui/stat-grid.tsx +263 -19
- package/src/data/config/store/node.ts +11 -5
- package/src/data/config/store/normalize.ts +16 -3
- package/src/data/profile-permissions.ts +47 -0
- package/src/data/resource-store.ts +34 -31
- package/src/data/sqlite/access.ts +19 -8
- package/src/data/sqlite/checkpointer.ts +95 -8
- package/src/data/sqlite/database.ts +40 -7
- 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/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/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/changelog/index.tsx +3 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -2
- 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 +26 -2
- package/src/plugins/builtin/chart-composer/series-expression.ts +3 -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/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/index.tsx +1 -2
- 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/sovr-pane.tsx +9 -4
- package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
- 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 +2 -0
- 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/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/headless.ts +16 -3
- package/src/plugins/builtin/doe/index.tsx +5 -4
- package/src/plugins/builtin/earnings/board.tsx +2 -1
- package/src/plugins/builtin/earnings/history.tsx +2 -1
- 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-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/exposure/holdings.ts +35 -13
- package/src/plugins/builtin/exposure/index.tsx +1 -2
- 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/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 +15 -2
- 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/help/index.tsx +1 -1
- 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/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/index.tsx +7 -1
- package/src/plugins/builtin/market-movers/session-body.tsx +4 -1
- 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/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/index.tsx +1 -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/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/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 +60 -0
- 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/fx-rate-freshness.ts +17 -0
- package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
- 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/social-mentions/index.tsx +1 -2
- package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
- 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 +116 -13
- package/src/plugins/builtin/thirteenf/data.ts +132 -30
- package/src/plugins/builtin/thirteenf/index.tsx +2 -4
- package/src/plugins/builtin/thirteenf/model.ts +2 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +20 -11
- package/src/plugins/builtin/thirteenf/types.ts +12 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +17 -4
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
- package/src/plugins/builtin/ticker-detail/headless.ts +11 -3
- 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/pane.tsx +2 -2
- 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/index.tsx +1 -2
- 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/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 +15 -4
- 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 +2 -0
- 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/initialization.ts +3 -2
- 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/create.ts +4 -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/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/standalone.ts +10 -1
- package/src/series/types.ts +7 -0
- package/src/series/validate.ts +11 -2
- 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 +12 -7
- package/src/telemetry/stall-reports.ts +34 -5
- package/src/test-support/preload.ts +9 -0
- package/src/tickers/company-name.ts +19 -0
- package/src/tickers/listing-fate.ts +14 -3
- package/src/tickers/saved-instrument.ts +17 -0
- package/src/tickers/search/home-listing.ts +13 -2
- package/src/tickers/search/index.ts +4 -1
- 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/official-close.ts +159 -0
- package/src/time-series/price-comparison.ts +55 -6
- package/src/time-series/resolve.ts +12 -3
- 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/types/config.ts +18 -2
- package/src/types/data-provider.ts +23 -0
- package/src/types/financials.ts +22 -0
- package/src/types/headless.ts +42 -3
- package/src/types/plugin.ts +32 -3
- package/src/ui/host.tsx +11 -0
- 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/futures-generic.ts +4 -4
- package/src/utils/http-proxy-upstream.ts +124 -0
- package/src/utils/main-thread-stall.ts +75 -3
- package/src/utils/perf-marks.ts +36 -2
- 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/renderers/electrobun/launcher/preload.ts +0 -13
- package/src/renderers/electrobun/launcher/windows-emulation.ts +0 -50
- package/src/time-series/studies.ts +0 -1628
|
@@ -4,14 +4,9 @@ import { holdsActiveTicker } from "../../../shares/pane-image";
|
|
|
4
4
|
import { TICKER_RESEARCH_PANE_ID } from "../../../types/config";
|
|
5
5
|
import { normalizeTickerInput } from "../../../tickers/search";
|
|
6
6
|
import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
|
|
7
|
-
import { FinancialAnalysisPane } from "./financials/pane";
|
|
8
|
-
import { HistoricalPricesPane } from "./data-panes/historical-prices";
|
|
9
|
-
import { ReturnsPane } from "./data-panes/returns-pane";
|
|
10
|
-
import { TickerResearchPane } from "./pane";
|
|
11
7
|
import { futuresContractCache } from "./overview/futures-contract";
|
|
12
8
|
import { offeringFilingsCache } from "./financials/offerings";
|
|
13
9
|
import { TICKER_RESEARCH_BUILTIN_TABS } from "./research-tabs";
|
|
14
|
-
import { QuoteMonitorPane } from "./quote-monitor";
|
|
15
10
|
import { financialStatementsHeadless, historicalPricesHeadless, quoteComparisonHeadless, returnsHeadless } from "./headless";
|
|
16
11
|
import {
|
|
17
12
|
buildQuoteMonitorSettingsDef,
|
|
@@ -47,7 +42,7 @@ export const tickerDetailModule: PluginModule = {
|
|
|
47
42
|
id: TICKER_RESEARCH_PANE_ID,
|
|
48
43
|
name: "Research",
|
|
49
44
|
icon: "D",
|
|
50
|
-
component: TickerResearchPane,
|
|
45
|
+
get component(): typeof import("./pane").TickerResearchPane { const { TickerResearchPane } = require("./pane"); return TickerResearchPane; },
|
|
51
46
|
defaultPosition: "right",
|
|
52
47
|
tickerFollower: true,
|
|
53
48
|
// DES and ADR open this pane; ADR's window is named by its params (the tab it asked for), so settings never say whose a window is.
|
|
@@ -70,7 +65,7 @@ export const tickerDetailModule: PluginModule = {
|
|
|
70
65
|
id: "financial-analysis",
|
|
71
66
|
name: "Financials",
|
|
72
67
|
icon: "F",
|
|
73
|
-
component: FinancialAnalysisPane,
|
|
68
|
+
get component(): typeof import("./financials/pane").FinancialAnalysisPane { const { FinancialAnalysisPane } = require("./financials/pane"); return FinancialAnalysisPane; },
|
|
74
69
|
headless: financialStatementsHeadless,
|
|
75
70
|
defaultPosition: "right",
|
|
76
71
|
tickerFollower: true,
|
|
@@ -82,7 +77,7 @@ export const tickerDetailModule: PluginModule = {
|
|
|
82
77
|
id: "quote-monitor",
|
|
83
78
|
name: "Quote Monitor",
|
|
84
79
|
icon: "Q",
|
|
85
|
-
component: QuoteMonitorPane,
|
|
80
|
+
get component(): typeof import("./quote-monitor").QuoteMonitorPane { const { QuoteMonitorPane } = require("./quote-monitor"); return QuoteMonitorPane; },
|
|
86
81
|
headless: quoteComparisonHeadless,
|
|
87
82
|
defaultPosition: "right",
|
|
88
83
|
defaultMode: "floating",
|
|
@@ -97,7 +92,7 @@ export const tickerDetailModule: PluginModule = {
|
|
|
97
92
|
id: "historical-prices",
|
|
98
93
|
name: "Historical Prices",
|
|
99
94
|
icon: "H",
|
|
100
|
-
component: HistoricalPricesPane,
|
|
95
|
+
get component(): typeof import("./data-panes/historical-prices").HistoricalPricesPane { const { HistoricalPricesPane } = require("./data-panes/historical-prices"); return HistoricalPricesPane; },
|
|
101
96
|
headless: historicalPricesHeadless,
|
|
102
97
|
defaultPosition: "right",
|
|
103
98
|
tickerFollower: true,
|
|
@@ -110,7 +105,7 @@ export const tickerDetailModule: PluginModule = {
|
|
|
110
105
|
reportFreshness: { status: "not-a-feed", basis: "price history" },
|
|
111
106
|
name: "Returns",
|
|
112
107
|
icon: "R",
|
|
113
|
-
component: ReturnsPane,
|
|
108
|
+
get component(): typeof import("./data-panes/returns-pane").ReturnsPane { const { ReturnsPane } = require("./data-panes/returns-pane"); return ReturnsPane; },
|
|
114
109
|
headless: returnsHeadless,
|
|
115
110
|
defaultPosition: "right",
|
|
116
111
|
tickerFollower: true,
|
|
@@ -62,6 +62,8 @@ import { describeFundamentalMarketCap } from "../../../utils/market-capitalizati
|
|
|
62
62
|
import { liveFiftyTwoWeekRange, liveMarketCapitalization } from "../../../market-data/quotes/live-valuation";
|
|
63
63
|
import { instrumentTypeLabel } from "../../../tickers/instrument-kind";
|
|
64
64
|
import { ClippedText } from "../../../components/ui/display";
|
|
65
|
+
import { DelayedMark, delayedMarkWidth } from "../../../components/ui/delayed-mark";
|
|
66
|
+
import { useFreeQuoteDelay } from "../../../api-client/plan-access";
|
|
65
67
|
import { safeExternalUrl } from "../../../utils/external-url";
|
|
66
68
|
import { cashRunway, SHORT_RUNWAY_HISTORY } from "../../../utils/cash-runway";
|
|
67
69
|
import { useOfferingNote } from "./financials/offerings";
|
|
@@ -126,6 +128,7 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
126
128
|
const figureCells = useFigureCells();
|
|
127
129
|
|
|
128
130
|
const quote = financials?.quote;
|
|
131
|
+
const quoteDelayMinutes = useFreeQuoteDelay(quote);
|
|
129
132
|
const fundamentals = financials?.fundamentals;
|
|
130
133
|
const capitalization = liveMarketCapitalization(quote, fundamentals);
|
|
131
134
|
const profile = financials?.profile;
|
|
@@ -195,8 +198,7 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
195
198
|
id: `${ticker.metadata.ticker}:price`,
|
|
196
199
|
label: `${ticker.metadata.ticker} Price`,
|
|
197
200
|
color: priceColor(chartDelta),
|
|
198
|
-
|
|
199
|
-
unit: unknownHistoryBasis ? "unknown" : priceSeriesUnit(quoteCurrency, instrumentType),
|
|
201
|
+
unit: unknownHistoryBasis ? "unknown" : priceSeriesUnit(quoteCurrency, instrumentType, quote?.priceBasis),
|
|
200
202
|
style: "area",
|
|
201
203
|
axis: "right",
|
|
202
204
|
panelId: "price",
|
|
@@ -251,7 +253,10 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
251
253
|
const venueText = listingVenue ? (companyName ? ` (${listingVenue})` : listingVenue) : "";
|
|
252
254
|
const sessionLabel = quoteSessionLabel(quote);
|
|
253
255
|
const marketStateText = sessionLabel ? t(sessionLabel) : "";
|
|
254
|
-
|
|
256
|
+
// Signed out or free, the headline price is the delayed one: the mark says so after the session.
|
|
257
|
+
const delayMinutes = sessionQuote ? quoteDelayMinutes : null;
|
|
258
|
+
const delayCells = delayMinutes != null ? delayedMarkWidth(delayMinutes, nativePaneChrome === true) + 1 : 0;
|
|
259
|
+
const hasIdentityLine = Boolean(companyName || venueText || marketStateText || delayMinutes != null);
|
|
255
260
|
// The desktop logo is a square from the top of the company name's capitals down to the
|
|
256
261
|
// price's baseline, so its edges meet the text's and the three read as one block. The
|
|
257
262
|
// extended-hours line hangs below, as it comes and goes with the session. Without a
|
|
@@ -290,7 +295,8 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
290
295
|
+ quoteChangeCells + 1 + displayWidth(changeBasisText) <= quoteTextWidth;
|
|
291
296
|
const companyNameWidth = Math.max(8, quoteTextWidth
|
|
292
297
|
- displayWidth(venueText)
|
|
293
|
-
- (marketStateText ? displayWidth(marketStateText) + 1 : 0)
|
|
298
|
+
- (marketStateText ? displayWidth(marketStateText) + 1 : 0)
|
|
299
|
+
- delayCells);
|
|
294
300
|
// A listing that no longer trades keeps its delisting date whole on a narrow pane: the venue gives way, then the name.
|
|
295
301
|
const delistedRoom = quote?.delisting && marketStateText ? quoteTextWidth - displayWidth(marketStateText) - 1 : null;
|
|
296
302
|
const identityVenue = delistedRoom !== null && delistedRoom - displayWidth(venueText) < 8 ? "" : venueText;
|
|
@@ -401,7 +407,7 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
401
407
|
/>
|
|
402
408
|
<Box flexDirection="column" flexGrow={1} flexShrink={1} minWidth={0}>
|
|
403
409
|
{hasIdentityLine && (
|
|
404
|
-
<Box flexDirection="row" minWidth={0} overflow="hidden">
|
|
410
|
+
<Box flexDirection="row" alignItems="center" minWidth={0} overflow="hidden">
|
|
405
411
|
{identityName && (
|
|
406
412
|
<ClippedText fg={colors.textDim} text={identityName} width={identityNameWidth} />
|
|
407
413
|
)}
|
|
@@ -411,6 +417,11 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
411
417
|
{identityName || identityVenue ? " " : ""}{marketStateText}
|
|
412
418
|
</Text>
|
|
413
419
|
)}
|
|
420
|
+
{delayMinutes != null && (
|
|
421
|
+
<Box flexShrink={0} paddingLeft={identityName || identityVenue || marketStateText ? 1 : 0}>
|
|
422
|
+
<DelayedMark minutes={delayMinutes} />
|
|
423
|
+
</Box>
|
|
424
|
+
)}
|
|
414
425
|
</Box>
|
|
415
426
|
)}
|
|
416
427
|
|
|
@@ -546,7 +557,6 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
|
|
|
546
557
|
series={[priceSeries]}
|
|
547
558
|
panels={[{ id: "price" }]}
|
|
548
559
|
levels={trade && researchTradeMatches(trade, instrumentFromTicker(ticker)) ? { seriesId: priceSeries.id, items: trade.levels } : undefined}
|
|
549
|
-
axisWidth={8}
|
|
550
560
|
showLegend={false}
|
|
551
561
|
/>
|
|
552
562
|
</Box>
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { PluginModule } from "../plugin-module";
|
|
2
2
|
import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
|
|
3
3
|
import { timeSalesHeadless } from "./headless";
|
|
4
|
-
import { TimeSalesPane } from "./pane";
|
|
5
4
|
export const timeSalesModule: PluginModule = {
|
|
6
|
-
panes: [{ id: "time-sales", name: "Time and Sales", icon: "T", component: TimeSalesPane,
|
|
5
|
+
panes: [{ id: "time-sales", name: "Time and Sales", icon: "T", get component(): typeof import("./pane").TimeSalesPane { const { TimeSalesPane } = require("./pane"); return TimeSalesPane; },
|
|
7
6
|
defaultPosition: "right", tickerFollower: true, defaultMode: "floating", defaultFloatingSize: { width: 104, height: 28 },
|
|
8
7
|
tableExport: true, headless: timeSalesHeadless, settings: { title: "Time and Sales", fields: [{ key: "tab", label: "View", type: "select", options: [{ value: "trades", label: "Trades" }, { value: "quotes", label: "NBBO" }] }] } }],
|
|
9
8
|
paneTemplates: [
|
|
@@ -100,8 +100,10 @@ function TimeSalesView({ width, height, focused, symbol, exchange }: PaneProps &
|
|
|
100
100
|
}, [data]);
|
|
101
101
|
const tradeDecision = useRef<TapeDigits | null>(null);
|
|
102
102
|
const quoteDecision = useRef<TapeDigits | null>(null);
|
|
103
|
+
// Decimals come from the prints alone: a session extreme can sit a fraction of a cent off a tick (NVDA's 200.9199 low)
|
|
104
|
+
// and must not turn every trade into four decimals. The session range reads at the prints' decimals instead.
|
|
103
105
|
const tradeDigits = useMemo(() => (tradeDecision.current = stickyTapePriceDigits(tradeDecision.current, symbol,
|
|
104
|
-
|
|
106
|
+
data?.trades.map((trade) => trade.price) ?? []))?.digits ?? 2, [data, symbol]);
|
|
105
107
|
const quoteDigits = useMemo(() => (quoteDecision.current = stickyTapePriceDigits(quoteDecision.current, symbol,
|
|
106
108
|
data?.quotes.flatMap((quote) => [quote.bid, quote.ask]) ?? []))?.digits ?? 2, [data, symbol]);
|
|
107
109
|
// The session range stands beside the tab's own prices, so it reads at their decimals: the quote tab's bid and ask, not the trades'.
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
quotedCompanyName,
|
|
9
9
|
type CorporateBond,
|
|
10
10
|
} from "./client";
|
|
11
|
-
import { bondStackPriority, formatBondChange, formatBondDecimal, type BondColumnId } from "./model";
|
|
11
|
+
import { bondStackPriority, formatBondChange, formatBondDecimal, sharesOneIssuer, type BondColumnId } from "./model";
|
|
12
12
|
|
|
13
13
|
const decimal = (digits: number) => (value: unknown) => (
|
|
14
14
|
typeof value === "number" ? formatBondDecimal(value, digits) : "—"
|
|
@@ -33,6 +33,15 @@ const COLUMNS: HeadlessPaneColumn[] = [
|
|
|
33
33
|
column("grade", { key: "traceGradeCode", header: "Grade" }),
|
|
34
34
|
];
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* One issuer's tape (`sharesOneIssuer`) leaves the issuer column out of the
|
|
38
|
+
* text table, as the pane does; exports and piped output keep it.
|
|
39
|
+
*/
|
|
40
|
+
function reportColumns(bonds: readonly CorporateBond[]): HeadlessPaneColumn[] {
|
|
41
|
+
if (!sharesOneIssuer(bonds)) return COLUMNS;
|
|
42
|
+
return COLUMNS.map((spec) => spec.key === "issuerName" ? { ...spec, minTableWidth: Number.MAX_SAFE_INTEGER } : spec);
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
function reportRow(bond: CorporateBond) {
|
|
37
46
|
return {
|
|
38
47
|
id: bond.id,
|
|
@@ -85,7 +94,7 @@ export const traceBondsHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
85
94
|
sections: [{
|
|
86
95
|
// An empty answer is its message alone, dated in its own words.
|
|
87
96
|
title: bonds.length > 0 ? (tradeDate ? `Last sales ${tradeDate}` : "Last sales") : query ? "" : "Last sales",
|
|
88
|
-
columns:
|
|
97
|
+
columns: reportColumns(bonds),
|
|
89
98
|
rows: bonds.map(reportRow),
|
|
90
99
|
...(query ? { empty: noPrintsText(query, tradeDate, issuer) } : {}),
|
|
91
100
|
}],
|
|
@@ -85,26 +85,32 @@ export function sortBonds(bonds: readonly CorporateBond[], sort: BondSort): Corp
|
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* What a sale says leads: its price, yield and CUSIP, after the issuer when
|
|
90
|
+
* the rows are several issuers', before what describes the bond (coupon,
|
|
91
|
+
* maturity) and the rest of the sale.
|
|
92
|
+
*/
|
|
88
93
|
const BOND_COLUMNS: readonly BondColumn[] = [
|
|
89
94
|
{ id: "issuer", label: "Issuer", width: 18, align: "left", flexGrow: 1 },
|
|
95
|
+
{ id: "price", label: "Price", width: 9, align: "right" },
|
|
96
|
+
{ id: "yield", label: "Yield", width: 8, align: "right" },
|
|
90
97
|
{ id: "cusip", label: "CUSIP", width: 10, align: "left" },
|
|
91
98
|
{ id: "coupon", label: "Coupon", width: 7, align: "right" },
|
|
92
99
|
{ id: "maturity", label: "Maturity", width: 11, align: "left" },
|
|
93
|
-
{ id: "price", label: "Price", width: 9, align: "right" },
|
|
94
|
-
{ id: "yield", label: "Yield", width: 8, align: "right" },
|
|
95
100
|
{ id: "change", label: "Chg", width: 8, align: "right" },
|
|
96
101
|
{ id: "traded", label: "Traded", width: 11, align: "left" },
|
|
97
102
|
{ id: "grade", label: "Grade", width: 6, align: "left" },
|
|
98
103
|
];
|
|
99
104
|
|
|
100
105
|
/**
|
|
101
|
-
* What a narrow tape gives up first. The
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* moves its columns under the rows in the same order
|
|
106
|
+
* What a narrow tape gives up first. The price and yield are what was printed
|
|
107
|
+
* and the CUSIP tells two issues with one coupon and maturity apart, so they
|
|
108
|
+
* never go, nor does the issuer while it names the row; the descriptive
|
|
109
|
+
* columns go before them, the coupon last as it is the narrowest. The
|
|
110
|
+
* headless report moves its columns under the rows in the same order
|
|
111
|
+
* (`bondStackPriority`).
|
|
106
112
|
*/
|
|
107
|
-
const BOND_NARROW_DROP_ORDER: readonly BondColumnId[] = ["grade", "change", "
|
|
113
|
+
const BOND_NARROW_DROP_ORDER: readonly BondColumnId[] = ["grade", "change", "traded", "maturity", "coupon"];
|
|
108
114
|
|
|
109
115
|
/** `stackPriority` of each column in the text report: the higher, the sooner it moves under its row. */
|
|
110
116
|
export function bondStackPriority(columnId: BondColumnId): number {
|
|
@@ -112,6 +118,34 @@ export function bondStackPriority(columnId: BondColumnId): number {
|
|
|
112
118
|
return index < 0 ? 0 : BOND_NARROW_DROP_ORDER.length - index;
|
|
113
119
|
}
|
|
114
120
|
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
/** The tape's symbol root of an issue (`WHR` of `WHR4800338` and `WHR.GM`), or null without one. */
|
|
122
|
+
function symbolRoot(symbol: string | null): string | null {
|
|
123
|
+
return symbol?.match(/^[A-Z]+/)?.[0] ?? null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Whether every row is the same issuer's, so an issuer column would only
|
|
128
|
+
* repeat it: more than one row, all under one symbol root, or all one name
|
|
129
|
+
* where a row has no symbol. One row keeps the name, the only thing naming it.
|
|
130
|
+
*/
|
|
131
|
+
export function sharesOneIssuer(bonds: readonly Pick<CorporateBond, "issuerName" | "symbol">[]): boolean {
|
|
132
|
+
if (bonds.length < 2) return false;
|
|
133
|
+
const roots = bonds.map((bond) => symbolRoot(bond.symbol));
|
|
134
|
+
if (roots.every((root) => root != null)) return new Set(roots).size === 1;
|
|
135
|
+
return new Set(bonds.map((bond) => bond.issuerName.trim().toUpperCase())).size === 1;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The sort the tape shows: a saved issuer sort, while one issuer's tape leaves
|
|
140
|
+
* the issuer column out, sorts as the default, which the header can show; the
|
|
141
|
+
* saved sort comes back with the column.
|
|
142
|
+
*/
|
|
143
|
+
export function shownBondSort(sort: BondSort, sharedIssuer: boolean): BondSort {
|
|
144
|
+
return sharedIssuer && sort.columnId === "issuer" ? DEFAULT_BOND_SORT : sort;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** The columns at `width`, without the issuer's when every row shares it (`sharesOneIssuer`). */
|
|
148
|
+
export function buildBondColumns(width: number, { sharedIssuer = false }: { sharedIssuer?: boolean } = {}): BondColumn[] {
|
|
149
|
+
const columns = sharedIssuer ? BOND_COLUMNS.filter((column) => column.id !== "issuer") : BOND_COLUMNS;
|
|
150
|
+
return fitChartTableColumns(columns, width, BOND_NARROW_DROP_ORDER);
|
|
117
151
|
}
|
|
@@ -40,6 +40,8 @@ import {
|
|
|
40
40
|
formatBondChange,
|
|
41
41
|
formatBondDecimal,
|
|
42
42
|
isBondColumnId,
|
|
43
|
+
sharesOneIssuer,
|
|
44
|
+
shownBondSort,
|
|
43
45
|
sortBonds,
|
|
44
46
|
type BondColumn,
|
|
45
47
|
type BondSort,
|
|
@@ -73,8 +75,11 @@ export function TraceBondsPane({ focused, width, height }: PaneProps) {
|
|
|
73
75
|
useAutoRefresh(updatedAt, load);
|
|
74
76
|
usePaneRefreshKey(() => void reload(), { focused, enabled: !searchActive });
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
+
// One issuer's tape says its name once, in what was asked, not on every row.
|
|
79
|
+
const sharedIssuer = useMemo(() => sharesOneIssuer(bonds), [bonds]);
|
|
80
|
+
const columns = useMemo(() => buildBondColumns(width, { sharedIssuer }), [sharedIssuer, width]);
|
|
81
|
+
const shownSort = shownBondSort(sort, sharedIssuer);
|
|
82
|
+
const rows = useMemo(() => sortBonds(bonds, shownSort), [bonds, shownSort]);
|
|
78
83
|
const tradeDate = answered ? data.tradeDate : null;
|
|
79
84
|
const footerInfo = useMemo(
|
|
80
85
|
() => (tradeDate ? [{ id: "trade-date", parts: [{ text: tradeDate }] }] : []),
|
|
@@ -96,11 +101,11 @@ export function TraceBondsPane({ focused, width, height }: PaneProps) {
|
|
|
96
101
|
|
|
97
102
|
const onHeaderClick = useCallback((columnId: string) => {
|
|
98
103
|
if (!isBondColumnId(columnId)) return;
|
|
99
|
-
setSort((current) => nextHeaderSort(current, columnId, {
|
|
104
|
+
setSort((current) => nextHeaderSort(shownBondSort(current, sharedIssuer), columnId, {
|
|
100
105
|
firstDirection: firstBondSortDirection,
|
|
101
106
|
resetTo: DEFAULT_BOND_SORT,
|
|
102
107
|
}));
|
|
103
|
-
}, [setSort]);
|
|
108
|
+
}, [setSort, sharedIssuer]);
|
|
104
109
|
|
|
105
110
|
const handleRootKeyDown = useCallback((
|
|
106
111
|
event: DataTableKeyEvent,
|
|
@@ -190,13 +195,13 @@ export function TraceBondsPane({ focused, width, height }: PaneProps) {
|
|
|
190
195
|
}}
|
|
191
196
|
columns={columns}
|
|
192
197
|
items={rows}
|
|
193
|
-
sortColumnId={
|
|
194
|
-
sortDirection={
|
|
198
|
+
sortColumnId={shownSort.columnId}
|
|
199
|
+
sortDirection={shownSort.direction}
|
|
195
200
|
onHeaderClick={onHeaderClick}
|
|
196
201
|
onSortChange={(columnId, direction) => {
|
|
197
202
|
if (isBondColumnId(columnId)) setSort({ columnId, direction });
|
|
198
203
|
}}
|
|
199
|
-
resetScrollKey={`${needle}:${
|
|
204
|
+
resetScrollKey={`${needle}:${shownSort.columnId ?? ""}:${shownSort.direction}`}
|
|
200
205
|
getItemKey={(row) => row.id}
|
|
201
206
|
renderCell={renderCell}
|
|
202
207
|
selectedTextOverridesCellColor
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
type DataTableRootKeyContext,
|
|
14
14
|
} from "../../../components";
|
|
15
15
|
import { loadingErrorFooterInfo, usePaneRefreshKey } from "../../../components/data-table/table-pane";
|
|
16
|
-
import { savedCopyFooterInfo } from "../../../components/layout/pane/footer";
|
|
16
|
+
import { ageFooterSegment, savedCopyFooterInfo } from "../../../components/layout/pane/footer";
|
|
17
17
|
import { STALE_SEGMENT } from "../../../components/layout/pane/status-footer";
|
|
18
18
|
import { usePaneInstance } from "../../../state/app/context";
|
|
19
19
|
import { colors } from "../../../theme/colors";
|
|
@@ -309,7 +309,7 @@ export function TreasuryAuctionsPane({ focused, width, height }: PaneProps) {
|
|
|
309
309
|
if (savedAt != null) info.push(...savedCopyFooterInfo([savedAt]));
|
|
310
310
|
else if (stale) info.push(STALE_SEGMENT);
|
|
311
311
|
if (fetchedAt && savedAt == null) {
|
|
312
|
-
info.push(
|
|
312
|
+
info.push(ageFooterSegment(formatRelativeAge(fetchedAt)));
|
|
313
313
|
}
|
|
314
314
|
return {
|
|
315
315
|
info,
|
|
@@ -17,7 +17,14 @@ import { expiredExpiryText, findListedExpiry, isLiveOptionExpiry, liveOptionExpi
|
|
|
17
17
|
import { optionModelSpot } from "../options/model-spot";
|
|
18
18
|
|
|
19
19
|
const DEFAULT_SURFACE_EXPIRY_LIMIT = 18;
|
|
20
|
+
/** Expiries read at a time from a source that takes them one by one. */
|
|
20
21
|
const SURFACE_LOAD_CONCURRENCY = 4;
|
|
22
|
+
/**
|
|
23
|
+
* Expiries asked at once from a source that batches them (`batchesExpiries`):
|
|
24
|
+
* the API client sends one listing's expiries asked in one moment in batch
|
|
25
|
+
* requests of six, side by side.
|
|
26
|
+
*/
|
|
27
|
+
const SURFACE_BATCHED_CONCURRENCY = 48;
|
|
21
28
|
/** Each kept expiry must be at least this much further out than the previous one. */
|
|
22
29
|
const SURFACE_TENOR_RATIO = 1.35;
|
|
23
30
|
|
|
@@ -43,8 +50,10 @@ export function selectSurfaceExpiries(catalogue: readonly number[], limit: numbe
|
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
export interface SurfaceLoaderDependencies {
|
|
46
|
-
loadOptions(request: OptionsRequest, options?: { forceRefresh?: boolean }): Promise<QueryEntry<OptionsChain>>;
|
|
53
|
+
loadOptions(request: OptionsRequest, options?: { forceRefresh?: boolean; batch?: boolean }): Promise<QueryEntry<OptionsChain>>;
|
|
47
54
|
loadYieldCurve(): Promise<YieldPoint[]>;
|
|
55
|
+
/** Whether `loadOptions` reaches a source that batches expiries asked together; absent, four are read at a time. */
|
|
56
|
+
batchesExpiries?(): boolean | Promise<boolean>;
|
|
48
57
|
now?: () => number;
|
|
49
58
|
}
|
|
50
59
|
|
|
@@ -58,6 +67,7 @@ export function createSurfaceDependencies(
|
|
|
58
67
|
loadOptions: (request, options) => coordinator
|
|
59
68
|
? coordinator.loadOptions(request, options)
|
|
60
69
|
: Promise.reject(new Error("Market data coordinator unavailable")),
|
|
70
|
+
batchesExpiries: () => coordinator?.batchesOptions() ?? false,
|
|
61
71
|
loadYieldCurve: () => cloudApi.getCloudYieldCurve(),
|
|
62
72
|
};
|
|
63
73
|
}
|
|
@@ -179,6 +189,8 @@ export async function loadVolatilitySurface(
|
|
|
179
189
|
};
|
|
180
190
|
checkAbort();
|
|
181
191
|
publish();
|
|
192
|
+
// Asked while the catalogue loads, so it costs the expiries no wait.
|
|
193
|
+
const batched = Promise.resolve().then(() => dependencies.batchesExpiries?.()).then((answer) => answer === true, () => false);
|
|
182
194
|
const treasury = Promise.resolve().then(() => dependencies.loadYieldCurve()).then((value) => {
|
|
183
195
|
if (request.signal?.aborted) return;
|
|
184
196
|
curve = value;
|
|
@@ -233,6 +245,9 @@ export async function loadVolatilitySurface(
|
|
|
233
245
|
publish();
|
|
234
246
|
}
|
|
235
247
|
|
|
248
|
+
// A source that takes expiries one by one is asked four at a time, and a cancelled load asks for no more.
|
|
249
|
+
const batch = await abortable(batched, request.signal, CANCELLED);
|
|
250
|
+
const concurrency = batch ? SURFACE_BATCHED_CONCURRENCY : SURFACE_LOAD_CONCURRENCY;
|
|
236
251
|
let next = 0;
|
|
237
252
|
const worker = async () => {
|
|
238
253
|
while (!request.signal?.aborted && next < selected.length) {
|
|
@@ -240,7 +255,7 @@ export async function loadVolatilitySurface(
|
|
|
240
255
|
try {
|
|
241
256
|
// The catalogue response already refreshed its own slice, unless it came from cache.
|
|
242
257
|
const entry = await dependencies.loadOptions({ instrument: request.instrument, expirationDate: expiration },
|
|
243
|
-
{ forceRefresh: !!request.forceRefresh && (!!request.expiries || expiration !== catalogueExpiration) });
|
|
258
|
+
{ forceRefresh: !!request.forceRefresh && (!!request.expiries || expiration !== catalogueExpiration), ...(batch ? { batch } : {}) });
|
|
244
259
|
if (request.signal?.aborted) return;
|
|
245
260
|
entries.set(expiration, entry);
|
|
246
261
|
if (!resolveEntryValue(entry) && !entry.error) errors.set(expiration, "Options chain unavailable");
|
|
@@ -251,7 +266,7 @@ export async function loadVolatilitySurface(
|
|
|
251
266
|
publish();
|
|
252
267
|
}
|
|
253
268
|
};
|
|
254
|
-
await abortable(Promise.all([treasury, ...Array.from({ length: Math.min(
|
|
269
|
+
await abortable(Promise.all([treasury, ...Array.from({ length: Math.min(concurrency, selected.length) }, worker)]), request.signal, CANCELLED);
|
|
255
270
|
checkAbort();
|
|
256
271
|
finished = true;
|
|
257
272
|
const result = snapshot();
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { PluginModule } from "../plugin-module";
|
|
2
2
|
import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
|
|
3
|
-
import { VolSurfacePane } from "./pane";
|
|
4
3
|
import { volSurfaceHeadless } from "./headless";
|
|
5
4
|
|
|
6
5
|
export const volSurfaceModule: PluginModule = {
|
|
7
6
|
panes: [{
|
|
8
|
-
id: "vol-surface", name: "Volatility Surface", icon: "V", component: VolSurfacePane,
|
|
7
|
+
id: "vol-surface", name: "Volatility Surface", icon: "V", get component(): typeof import("./pane").VolSurfacePane { const { VolSurfacePane } = require("./pane"); return VolSurfacePane; },
|
|
9
8
|
defaultPosition: "right", tickerFollower: true, defaultMode: "floating", defaultFloatingSize: { width: 126, height: 36 },
|
|
10
9
|
tableExport: true, headless: volSurfaceHeadless,
|
|
11
10
|
settings: {
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import type { PluginModule } from "../plugin-module";
|
|
2
2
|
import { volatilityHeadless } from "./headless";
|
|
3
|
-
import { VolatilityPane } from "./pane";
|
|
4
3
|
import { VOLATILITY_SERIES } from "./series";
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
export const volatilityModule: PluginModule = {
|
|
8
7
|
series: VOLATILITY_SERIES,
|
|
9
8
|
panes: [{
|
|
10
|
-
id: "volatility-term-structure", name: "Volatility", icon: "V", component: VolatilityPane,
|
|
9
|
+
id: "volatility-term-structure", name: "Volatility", icon: "V", get component(): typeof import("./pane").VolatilityPane { const { VolatilityPane } = require("./pane"); return VolatilityPane; },
|
|
11
10
|
defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 110, height: 34 },
|
|
12
11
|
tableExport: true, headless: volatilityHeadless,
|
|
13
12
|
}],
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { usePaneFooter, type PaneFooterSegment } from "../../../components";
|
|
2
2
|
import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
|
|
3
|
+
import { useQuoteDelayFooter } from "../shared/cloud-upgrade";
|
|
3
4
|
import {
|
|
4
5
|
boardErrorMessage,
|
|
6
|
+
boardQuotes,
|
|
5
7
|
quoteBoardFooterInfo,
|
|
6
8
|
quoteBoardStatus,
|
|
7
9
|
type BoardQuoteMap,
|
|
@@ -12,14 +14,17 @@ export function useWorldIndicesFooter(quotes: BoardQuoteMap, onRefresh: () => vo
|
|
|
12
14
|
const errorMessage = boardErrorMessage(quotes);
|
|
13
15
|
|
|
14
16
|
usePaneRefreshKey(onRefresh, { focused });
|
|
17
|
+
// Signed out or free, the indices' quotes are delayed ones.
|
|
18
|
+
const quoteDelay = useQuoteDelayFooter(boardQuotes(quotes), "indices-delay");
|
|
15
19
|
|
|
16
20
|
usePaneFooter(
|
|
17
21
|
"world-indices",
|
|
18
22
|
() => {
|
|
19
23
|
const info: PaneFooterSegment[] = quoteBoardFooterInfo(status);
|
|
20
24
|
if (errorMessage) info.push({ id: "reason", parts: [{ text: errorMessage, tone: "warning" }] });
|
|
25
|
+
if (quoteDelay) info.push(quoteDelay);
|
|
21
26
|
return { info };
|
|
22
27
|
},
|
|
23
|
-
[errorMessage, focused, onRefresh, status.latestTs, status.loading, status.savedAt, status.stale, status.unavailable],
|
|
28
|
+
[errorMessage, focused, onRefresh, quoteDelay, status.latestTs, status.loading, status.savedAt, status.stale, status.unavailable],
|
|
24
29
|
);
|
|
25
30
|
}
|
|
@@ -10,11 +10,20 @@ import {
|
|
|
10
10
|
type GeoLayersPayload,
|
|
11
11
|
type GeoRequest,
|
|
12
12
|
} from "../../../api-client/geo";
|
|
13
|
+
import { withLocalGeoLayers } from "./local-layers";
|
|
13
14
|
|
|
14
15
|
export const ENTITY_PAGE_SIZE = 100;
|
|
15
16
|
|
|
16
17
|
/** Requests under /cloud/geo/ through the shared client, resolved late so tests can stub it. */
|
|
17
|
-
|
|
18
|
+
const serverGeoRequest: GeoRequest = (path, init) => apiClient.geo(path, init);
|
|
19
|
+
|
|
20
|
+
/** The server's layers and the app's own (`local-layers.ts`). */
|
|
21
|
+
export const cloudGeoRequest: GeoRequest = withLocalGeoLayers(serverGeoRequest);
|
|
22
|
+
|
|
23
|
+
/** The same, with the app's own layers narrowed to what a linked pane selects. */
|
|
24
|
+
export function scopedGeoRequest(scope: string | null): GeoRequest {
|
|
25
|
+
return scope ? withLocalGeoLayers(serverGeoRequest, scope) : cloudGeoRequest;
|
|
26
|
+
}
|
|
18
27
|
|
|
19
28
|
export async function loadWorldVenues(): Promise<CloudWorldVenueMapPayload> {
|
|
20
29
|
const response = await apiClient.getCloudWorldVenues();
|
|
@@ -9,6 +9,7 @@ import { CHOKEPOINT_SERIES, createGeoChartSeriesCapability } from "./geo-series"
|
|
|
9
9
|
import { mapHeadless } from "./headless";
|
|
10
10
|
import { buildMapSettingsDef, groupTitle, MAP_PRESET_OPTIONS, parseMapPreset } from "./layers";
|
|
11
11
|
import { WORLD_VENUE_MAP_PANE_ID, WorldVenueMapPane } from "./pane";
|
|
12
|
+
import { mapFollowsTicker } from "./scope";
|
|
12
13
|
|
|
13
14
|
const chartComposerHeadless = chartHeadless("chart-composer-pane");
|
|
14
15
|
|
|
@@ -33,6 +34,7 @@ const chokepointTemplate: PaneTemplateDef = {
|
|
|
33
34
|
keywords: ["chokepoint", "chokepoints", "transits", "suez", "panama", "hormuz", "malacca", "shipping", "strait", "canal"],
|
|
34
35
|
shortcut: { prefix: "CHOKE", argPlaceholder: "chokepoint", argKind: "text", argOptional: true },
|
|
35
36
|
canCreate: () => true,
|
|
37
|
+
catalogSettings: {},
|
|
36
38
|
createInstance: (_context, options) => createChokepointInstance(cloudGeoRequest, options?.arg),
|
|
37
39
|
};
|
|
38
40
|
|
|
@@ -46,6 +48,8 @@ export const worldVenueMapModule: PluginModule = {
|
|
|
46
48
|
defaultPosition: "right",
|
|
47
49
|
defaultMode: "floating",
|
|
48
50
|
defaultFloatingSize: { width: 124, height: 36 },
|
|
51
|
+
// Linked to a list, a map with the app's own layers (facilities) shows the selected company's.
|
|
52
|
+
tickerFollower: mapFollowsTicker,
|
|
49
53
|
// The layer picker comes from the server's catalog; without one the pane keeps its old settings.
|
|
50
54
|
settings: (context) => buildMapSettingsDef(context.settings, peekGeoCatalog()?.layers) ?? { fields: [] },
|
|
51
55
|
},
|
|
@@ -55,8 +59,8 @@ export const worldVenueMapModule: PluginModule = {
|
|
|
55
59
|
id: "world-venue-map-pane",
|
|
56
60
|
paneId: WORLD_VENUE_MAP_PANE_ID,
|
|
57
61
|
label: "World Map",
|
|
58
|
-
description: "A world map with trading venues and their hours; ships, ports, energy and
|
|
59
|
-
keywords: ["world", "map", "globe", "countries", "venue", "venues", "exchange", "mic", "market hours", "open markets", "ships", "ship", "vessels", "vessel", "shipping", "ais", "ports", "port", "chokepoints", "pipelines", "airports", "energy", "layers"],
|
|
62
|
+
description: "A world map with trading venues and their hours; ships, ports, energy, airports and AI buildout facilities as layers.",
|
|
63
|
+
keywords: ["world", "map", "globe", "countries", "venue", "venues", "exchange", "mic", "market hours", "open markets", "ships", "ship", "vessels", "vessel", "shipping", "ais", "ports", "port", "chokepoints", "pipelines", "airports", "energy", "facilities", "data centers", "fabs", "layers"],
|
|
60
64
|
shortcut: {
|
|
61
65
|
prefix: "MAP",
|
|
62
66
|
argPlaceholder: "layers",
|