@gloom-sh/terminal 0.19.1 → 0.19.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/package.json +2 -1
- package/patches/electrobun@1.18.1.patch +108 -1
- package/src/account/upgrade-dialog.tsx +4 -23
- package/src/api-client/coalesce.ts +118 -0
- package/src/api-client/credit-boards.ts +13 -0
- package/src/api-client/crypto-etf-flows.ts +6 -0
- package/src/api-client/crypto-markets.ts +40 -0
- package/src/api-client/data.ts +201 -14
- package/src/api-client/equity-screener.ts +10 -0
- package/src/api-client/errors.ts +58 -0
- package/src/api-client/gpu.ts +29 -0
- package/src/api-client/index.ts +5 -2
- package/src/api-client/plan-access.ts +10 -1
- package/src/api-client/plan-rules.ts +35 -0
- package/src/api-client/research-activity.ts +1 -32
- package/src/api-client/types.ts +21 -1
- package/src/app/attention-notice.tsx +94 -0
- package/src/app/runtime/startup.ts +3 -1
- package/src/app.tsx +23 -1
- package/src/architecture/core-builtin-allowlist.json +2 -1
- package/src/capabilities/chart-series.ts +2 -1
- package/src/capabilities/factories.ts +1 -0
- package/src/cli/ascii-output.ts +42 -0
- package/src/cli/base-converter.ts +10 -2
- package/src/cli/command-options.ts +16 -1
- package/src/cli/commands/crypto-hints.ts +23 -4
- package/src/cli/commands/market.ts +101 -27
- package/src/cli/commands/news-search.ts +24 -5
- package/src/cli/commands/overview.ts +14 -10
- package/src/cli/commands/search.ts +84 -25
- package/src/cli/commands/system.ts +22 -30
- package/src/cli/commands/ticker.ts +49 -9
- package/src/cli/commands/version.ts +33 -0
- package/src/cli/context.ts +6 -9
- package/src/cli/data-dir.ts +7 -0
- package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
- package/src/cli/desktop-pane-shot-browser.ts +320 -0
- package/src/cli/desktop-pane-shot-routes.ts +26 -1
- package/src/cli/desktop-pane-shot.ts +196 -256
- package/src/cli/early-command.ts +42 -0
- package/src/cli/earnings-calendar.ts +9 -0
- package/src/cli/entry.ts +19 -13
- package/src/cli/errors.ts +12 -2
- package/src/cli/glossary.ts +2 -1
- package/src/cli/help.ts +1 -1
- package/src/cli/history-export.ts +22 -2
- package/src/cli/history-rows.ts +189 -11
- package/src/cli/index.ts +18 -1
- package/src/cli/known-gaps.ts +51 -5
- package/src/cli/listing-arg.ts +63 -10
- package/src/cli/options.ts +1 -1
- package/src/cli/output-file.ts +60 -13
- package/src/cli/pane-functions/catalog.ts +14 -3
- package/src/cli/pane-functions/data.ts +1 -1
- package/src/cli/pane-functions/dom.ts +45 -0
- package/src/cli/pane-functions/freshness.ts +46 -9
- package/src/cli/pane-functions/function-help.ts +17 -9
- package/src/cli/pane-functions/headless.ts +22 -14
- package/src/cli/pane-functions/index.ts +6 -2
- package/src/cli/pane-functions/options.ts +6 -2
- package/src/cli/pane-functions/resolver.ts +27 -5
- package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
- package/src/cli/pane-functions/screenshot-http.ts +70 -0
- package/src/cli/pane-functions/screenshot-render.ts +19 -5
- package/src/cli/quote-answer.ts +49 -6
- package/src/cli/registry.ts +41 -13
- package/src/cli/result.ts +12 -6
- package/src/cli/ticker-resolution.ts +2 -1
- package/src/cli/types.ts +2 -0
- package/src/components/chart/composite/format.ts +3 -0
- package/src/components/chart-table/header.tsx +1 -1
- package/src/components/chart-table/layout.ts +28 -11
- package/src/components/command-bar/action-runtime.ts +4 -0
- package/src/components/command-bar/commands/direct/index.ts +18 -33
- package/src/components/command-bar/commands/direct/runtime.ts +5 -0
- package/src/components/command-bar/commands/registry.ts +10 -1
- package/src/components/command-bar/routes/root/command-items.ts +9 -4
- package/src/components/command-bar/routes/root/runtime.ts +51 -6
- package/src/components/command-bar/selection-runtime.ts +12 -29
- package/src/components/command-bar/surface/index.tsx +9 -3
- package/src/components/company-logo.tsx +2 -1
- package/src/components/layout/detached-pane-shell.tsx +2 -2
- package/src/components/layout/header.tsx +10 -5
- package/src/components/layout/market-summary.ts +25 -7
- package/src/components/layout/pane/footer/index.tsx +8 -2
- package/src/components/layout/pane/footer/model.ts +73 -6
- package/src/components/layout/pane/header.tsx +2 -2
- package/src/components/layout/pane/status-footer.ts +2 -1
- package/src/components/market-board/index.tsx +3 -2
- package/src/components/onboarding/desks-step.tsx +12 -4
- package/src/components/onboarding/first-run-workspace.ts +81 -4
- package/src/components/onboarding/onboarding-frame.tsx +4 -2
- package/src/components/onboarding/onboarding-keyboard.ts +8 -5
- package/src/components/onboarding/onboarding-wizard.tsx +60 -26
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
- package/src/components/onboarding/run-setup-again.ts +24 -0
- package/src/components/onboarding/use-onboarding-progress.ts +33 -15
- package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
- package/src/components/onboarding/wizard-model.ts +57 -3
- package/src/components/onboarding/wizard-positions.ts +1 -1
- package/src/components/ui/delayed-mark.tsx +56 -0
- package/src/components/ui/display.tsx +16 -5
- package/src/components/ui/external-link.tsx +3 -6
- package/src/components/ui/icon.tsx +16 -3
- package/src/components/ui/logo-mark.tsx +8 -5
- package/src/components/ui/stat-grid.tsx +263 -19
- package/src/core/app-services.ts +11 -2
- package/src/data/app-persistence.ts +9 -7
- package/src/data/config/store/node.ts +11 -5
- package/src/data/config/store/normalize.ts +16 -3
- package/src/data/plugin-cache.ts +15 -0
- package/src/data/plugin-state-store.ts +81 -24
- package/src/data/profile-permissions.ts +47 -0
- package/src/data/resource-store.ts +165 -90
- package/src/data/session-store.ts +31 -8
- package/src/data/sqlite/access.ts +324 -0
- package/src/data/sqlite/checkpointer.ts +285 -0
- package/src/data/sqlite/database.ts +80 -4
- package/src/data/sqlite/retry.ts +50 -9
- package/src/data/ticker-store.ts +57 -23
- package/src/docs/snapshot.json +191 -78
- package/src/i18n/es.ts +18 -2
- package/src/i18n/ja.ts +18 -2
- package/src/i18n/ko.ts +18 -2
- package/src/i18n/zh-cn.ts +18 -2
- package/src/i18n/zh-tw.ts +18 -2
- package/src/layout/desk-keys.ts +4 -1
- package/src/layout/desks.ts +38 -5
- package/src/layout-marketplace/gallery-desktop.tsx +45 -16
- package/src/market-data/batched-reads.ts +16 -0
- package/src/market-data/coordinator/auxiliary.ts +4 -2
- package/src/market-data/coordinator/index.ts +11 -2
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +3 -1
- package/src/market-data/market/index-level.ts +6 -4
- package/src/market-data/published-apac-sessions.ts +6 -0
- package/src/market-data/realized-volatility.ts +4 -2
- package/src/news/aggregator.ts +7 -4
- package/src/news/news-model.ts +11 -0
- package/src/news/story-id.ts +22 -0
- package/src/news/ticker-symbols.ts +17 -1
- package/src/notifications/app-notifier.ts +35 -14
- package/src/plugins/auto-update-node.ts +10 -6
- package/src/plugins/builtin/account-management/form-components.tsx +1 -1
- package/src/plugins/builtin/account-management/index.tsx +2 -3
- package/src/plugins/builtin/account-management/model.ts +1 -1
- package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
- package/src/plugins/builtin/alerts/events.ts +10 -2
- package/src/plugins/builtin/alerts/index.tsx +5 -5
- package/src/plugins/builtin/alerts/research-builder.ts +5 -1
- package/src/plugins/builtin/alerts/research-rules.ts +18 -0
- package/src/plugins/builtin/alerts/targets.ts +4 -1
- package/src/plugins/builtin/attention/headless.ts +1 -1
- package/src/plugins/builtin/attention/index.tsx +2 -4
- package/src/plugins/builtin/attention/listing.ts +1 -1
- package/src/plugins/builtin/attention/series.ts +1 -1
- package/src/plugins/builtin/awards/index.tsx +1 -2
- package/src/plugins/builtin/backtest/client.ts +1 -0
- package/src/plugins/builtin/backtest/index.tsx +1 -2
- package/src/plugins/builtin/backtest/rules.ts +3 -1
- package/src/plugins/builtin/backtest/window.ts +8 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
- package/src/plugins/builtin/broker-manager/index.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +5 -0
- package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
- package/src/plugins/builtin/buildout/company/id.ts +2 -0
- package/src/plugins/builtin/buildout/company/model.ts +247 -0
- package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
- package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
- package/src/plugins/builtin/buildout/facilities.ts +309 -0
- package/src/plugins/builtin/buildout/format.ts +9 -0
- package/src/plugins/builtin/buildout/index.tsx +51 -5
- package/src/plugins/builtin/buildout/model/index.ts +62 -10
- package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
- package/src/plugins/builtin/buildout/model/types.ts +11 -2
- package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
- package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
- package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
- package/src/plugins/builtin/buildout/table-model.ts +26 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
- package/src/plugins/builtin/cds/headless.ts +10 -2
- package/src/plugins/builtin/cds/model.ts +7 -3
- package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
- package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
- package/src/plugins/builtin/central-bank-rates/series.ts +2 -3
- package/src/plugins/builtin/changelog/index.tsx +3 -2
- package/src/plugins/builtin/chart-composer/cli-options.ts +16 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -0
- package/src/plugins/builtin/chart-composer/headless.ts +88 -21
- package/src/plugins/builtin/chart-composer/index.tsx +2 -3
- package/src/plugins/builtin/chart-composer/pane.tsx +30 -2
- package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
- package/src/plugins/builtin/chart-composer/session-range.ts +5 -2
- package/src/plugins/builtin/chart-composer/studies.ts +1 -1
- package/src/plugins/builtin/chat/controller/index.ts +1 -1
- package/src/plugins/builtin/chat/controller/messages.ts +15 -4
- package/src/plugins/builtin/chat/ghost-user.ts +8 -0
- package/src/plugins/builtin/chat/index.tsx +5 -5
- package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/index.tsx +1 -2
- package/src/plugins/builtin/cloud/plugin.tsx +6 -4
- package/src/plugins/builtin/cloud/team/module.tsx +1 -1
- package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
- package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
- package/src/plugins/builtin/comment-letters/index.tsx +1 -2
- package/src/plugins/builtin/commodity-balances/client.ts +4 -1
- package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
- package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
- package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
- package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
- package/src/plugins/builtin/congress-trades/headless.ts +2 -0
- package/src/plugins/builtin/congress-trades/model.ts +92 -64
- package/src/plugins/builtin/congress-trades/table.tsx +14 -2
- package/src/plugins/builtin/connections/index.ts +2 -3
- package/src/plugins/builtin/cot/client.ts +28 -2
- package/src/plugins/builtin/cot/headless.ts +4 -1
- package/src/plugins/builtin/cot/index.tsx +5 -4
- package/src/plugins/builtin/cot/model.ts +58 -7
- package/src/plugins/builtin/cot/pane.tsx +6 -2
- package/src/plugins/builtin/cpi/client.ts +6 -5
- package/src/plugins/builtin/cpi/index.tsx +1 -2
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
- package/src/plugins/builtin/credit-boards/client.ts +7 -2
- package/src/plugins/builtin/credit-boards/headless.ts +25 -12
- package/src/plugins/builtin/credit-boards/model.ts +29 -0
- package/src/plugins/builtin/credit-boards/series.ts +3 -4
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
- package/src/plugins/builtin/credit-conditions/client.ts +22 -14
- package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
- package/src/plugins/builtin/crypto-board/client.ts +5 -5
- package/src/plugins/builtin/crypto-board/headless.ts +4 -3
- package/src/plugins/builtin/crypto-board/index.tsx +4 -2
- package/src/plugins/builtin/crypto-board/series.ts +6 -4
- package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
- package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
- package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
- package/src/plugins/builtin/debt-maturities/model.ts +17 -1
- package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
- package/src/plugins/builtin/debt-maturities/series.ts +3 -3
- package/src/plugins/builtin/debug/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/client.ts +32 -2
- package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
- package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
- package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
- package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
- package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
- package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
- package/src/plugins/builtin/dividend-yield/view.ts +21 -0
- package/src/plugins/builtin/docs/index.tsx +2 -3
- package/src/plugins/builtin/doe/client.ts +6 -5
- package/src/plugins/builtin/doe/headless.ts +16 -3
- package/src/plugins/builtin/doe/index.tsx +5 -4
- package/src/plugins/builtin/doe/prices.ts +2 -3
- package/src/plugins/builtin/doe/series.ts +4 -4
- package/src/plugins/builtin/earnings/board.tsx +2 -1
- package/src/plugins/builtin/earnings/history.tsx +2 -1
- package/src/plugins/builtin/earnings-calls/calendar.tsx +10 -2
- package/src/plugins/builtin/earnings-calls/data.ts +19 -11
- package/src/plugins/builtin/earnings-calls/pane.tsx +27 -14
- package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
- package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
- package/src/plugins/builtin/econ/calendar-model.ts +29 -6
- package/src/plugins/builtin/econ/detail-view.tsx +16 -2
- package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/headless.ts +1 -0
- package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
- package/src/plugins/builtin/equity-screener/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/model.ts +28 -4
- package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
- package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
- package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
- package/src/plugins/builtin/executives/data.ts +6 -1
- package/src/plugins/builtin/executives/pane.tsx +9 -21
- package/src/plugins/builtin/exposure/holdings.ts +35 -13
- package/src/plugins/builtin/exposure/index.tsx +1 -2
- package/src/plugins/builtin/fear-greed/cache.ts +4 -0
- package/src/plugins/builtin/fear-greed/cli.ts +44 -0
- package/src/plugins/builtin/fear-greed/index.tsx +5 -53
- package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
- package/src/plugins/builtin/fear-greed/series.ts +6 -4
- package/src/plugins/builtin/fixed-income/index.tsx +1 -2
- package/src/plugins/builtin/futures/board-reads.ts +25 -0
- package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
- package/src/plugins/builtin/futures/index.tsx +15 -0
- package/src/plugins/builtin/futures/model.ts +14 -0
- package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
- package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
- package/src/plugins/builtin/futures-curve/headless.ts +6 -1
- package/src/plugins/builtin/futures-curve/index.tsx +1 -2
- package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
- package/src/plugins/builtin/gpu/board.tsx +51 -12
- package/src/plugins/builtin/gpu/client.ts +19 -3
- package/src/plugins/builtin/gpu/equities.tsx +5 -2
- package/src/plugins/builtin/gpu/headless.ts +14 -2
- package/src/plugins/builtin/gpu/index.tsx +1 -2
- package/src/plugins/builtin/gpu/model.ts +20 -2
- package/src/plugins/builtin/gpu/pane.tsx +1 -1
- package/src/plugins/builtin/gpu/series.ts +2 -3
- package/src/plugins/builtin/help/index.tsx +1 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +1 -2
- package/src/plugins/builtin/holders/index-holders.ts +24 -12
- package/src/plugins/builtin/holders/index.tsx +2 -3
- package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
- package/src/plugins/builtin/iv-history/index.tsx +1 -2
- package/src/plugins/builtin/jodi/client.ts +4 -1
- package/src/plugins/builtin/jodi/index.tsx +1 -2
- package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
- package/src/plugins/builtin/layout-manager/index.tsx +9 -8
- package/src/plugins/builtin/macro-day/pane.tsx +4 -2
- package/src/plugins/builtin/market-halts/index.tsx +1 -2
- package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
- package/src/plugins/builtin/market-movers/client.ts +1 -0
- package/src/plugins/builtin/market-movers/index.tsx +16 -5
- package/src/plugins/builtin/market-movers/screener.ts +23 -6
- package/src/plugins/builtin/market-movers/session-body.tsx +6 -2
- package/src/plugins/builtin/market-valuation/index.tsx +1 -2
- package/src/plugins/builtin/members/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/pane.tsx +2 -1
- package/src/plugins/builtin/money-markets/series.ts +3 -5
- package/src/plugins/builtin/news/headless.ts +7 -0
- package/src/plugins/builtin/news/wire/index.tsx +1 -1
- package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
- package/src/plugins/builtin/notes/index.tsx +3 -6
- package/src/plugins/builtin/openfda/index.tsx +1 -2
- package/src/plugins/builtin/options/view.tsx +6 -5
- package/src/plugins/builtin/options-calculator/index.tsx +1 -2
- package/src/plugins/builtin/options-positioning/index.tsx +1 -2
- package/src/plugins/builtin/options-scenario/index.tsx +2 -4
- package/src/plugins/builtin/perps/index.tsx +1 -2
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
- package/src/plugins/builtin/plugin-module.ts +16 -2
- package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
- package/src/plugins/builtin/power/gas-client.ts +2 -1
- package/src/plugins/builtin/power/index.tsx +1 -2
- package/src/plugins/builtin/power/prices-client.ts +3 -2
- package/src/plugins/builtin/rate-path/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/headless.ts +2 -0
- package/src/plugins/builtin/realized-vol/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/model.ts +16 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
- package/src/plugins/builtin/research/analyst-headless.ts +33 -6
- package/src/plugins/builtin/research/analyst-model.ts +37 -2
- package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
- package/src/plugins/builtin/research/index.tsx +5 -8
- package/src/plugins/builtin/research-search/index.tsx +1 -2
- package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
- package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
- package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
- package/src/plugins/builtin/risk-factors/data.ts +8 -4
- package/src/plugins/builtin/risk-factors/index.tsx +3 -1
- package/src/plugins/builtin/risk-factors/pane.tsx +5 -8
- package/src/plugins/builtin/roadmap/index.tsx +2 -3
- package/src/plugins/builtin/roadmap/pane.tsx +49 -2
- package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
- package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
- package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
- package/src/plugins/builtin/scanner/lock-note.ts +26 -3
- package/src/plugins/builtin/sec/filing-content.ts +38 -6
- package/src/plugins/builtin/sec/headless.ts +5 -1
- package/src/plugins/builtin/sec/index.tsx +4 -2
- package/src/plugins/builtin/sec/model.ts +2 -0
- package/src/plugins/builtin/sectors/index.tsx +11 -5
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +73 -8
- package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
- package/src/plugins/builtin/shared/forming-bars.ts +29 -0
- package/src/plugins/builtin/shared/fred-points.ts +6 -1
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +23 -8
- package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
- package/src/plugins/builtin/shared/series-warning.ts +13 -4
- package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/series.ts +2 -3
- package/src/plugins/builtin/social-mentions/client.ts +15 -10
- package/src/plugins/builtin/social-mentions/index.tsx +1 -2
- package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
- package/src/plugins/builtin/social-mentions/series.ts +2 -3
- package/src/plugins/builtin/supply-chain/client.ts +22 -1
- package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
- package/src/plugins/builtin/supply-chain/index.tsx +1 -2
- package/src/plugins/builtin/thirteenf/api.ts +127 -29
- package/src/plugins/builtin/thirteenf/data.ts +165 -35
- package/src/plugins/builtin/thirteenf/index.tsx +2 -4
- package/src/plugins/builtin/thirteenf/model.ts +2 -0
- package/src/plugins/builtin/thirteenf/overlap-pane.tsx +5 -1
- package/src/plugins/builtin/thirteenf/pane.tsx +41 -16
- package/src/plugins/builtin/thirteenf/types.ts +14 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -6
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
- package/src/plugins/builtin/ticker-detail/financials/model.ts +35 -2
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +53 -18
- package/src/plugins/builtin/ticker-detail/headless.ts +70 -11
- package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
- package/src/plugins/builtin/time-sales/index.tsx +1 -2
- package/src/plugins/builtin/time-sales/pane.tsx +3 -1
- package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
- package/src/plugins/builtin/trace-bonds/model.ts +44 -10
- package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
- package/src/plugins/builtin/treasury-auctions/cache.ts +20 -8
- package/src/plugins/builtin/treasury-auctions/client.ts +3 -3
- package/src/plugins/builtin/treasury-auctions/headless.ts +3 -1
- package/src/plugins/builtin/treasury-auctions/pane.tsx +10 -4
- package/src/plugins/builtin/vol-surface/client.ts +18 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -2
- package/src/plugins/builtin/volatility/client.ts +43 -16
- package/src/plugins/builtin/volatility/headless.ts +5 -2
- package/src/plugins/builtin/volatility/index.tsx +1 -2
- package/src/plugins/builtin/volatility/model.ts +17 -6
- package/src/plugins/builtin/volatility/pane.tsx +6 -2
- package/src/plugins/builtin/volatility/series.ts +2 -1
- package/src/plugins/builtin/world-indices/footer.ts +6 -1
- package/src/plugins/builtin/world-venue-map/client.ts +10 -1
- package/src/plugins/builtin/world-venue-map/index.ts +6 -2
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
- package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
- package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
- package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
- package/src/plugins/builtin/yield-curve/client.ts +4 -6
- package/src/plugins/builtin/yield-curve/curves.ts +16 -2
- package/src/plugins/builtin/yield-curve/headless.ts +3 -3
- package/src/plugins/bundle.ts +52 -2
- package/src/plugins/catalog-browser.ts +2 -2
- package/src/plugins/catalog-cli.ts +20 -0
- package/src/plugins/host-link.ts +13 -1
- package/src/plugins/installer.ts +64 -42
- package/src/plugins/loader.ts +43 -11
- package/src/plugins/peer-imports.ts +201 -0
- package/src/plugins/peer-retry.ts +26 -29
- package/src/plugins/plugin-process.ts +128 -0
- package/src/plugins/redirection-guard.ts +45 -0
- package/src/remote/endpoint.ts +16 -3
- package/src/renderers/browser/main.tsx +3 -2
- package/src/renderers/cloudflare/http-proxy.ts +7 -123
- package/src/renderers/dom/desktop/icons.tsx +5 -2
- package/src/renderers/dom/host/image-surface.tsx +46 -21
- package/src/renderers/dom/styles.css +7 -0
- package/src/renderers/dom/toast-host.tsx +2 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +23 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +12 -3
- package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
- package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
- package/src/renderers/electrobun/bun/window/attention.ts +54 -0
- package/src/renderers/electrobun/bun/window/create.ts +6 -2
- package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
- package/src/renderers/electrobun/view/main.tsx +7 -2
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
- package/src/renderers/electrobun/view/ui-host.tsx +9 -2
- package/src/renderers/opentui/browser-open.ts +10 -4
- package/src/renderers/opentui/image/surface.tsx +1 -1
- package/src/renderers/opentui/start.tsx +12 -6
- package/src/renderers/opentui/toast-host.tsx +2 -1
- package/src/series/registry.ts +18 -0
- package/src/series/resolve.ts +2 -1
- package/src/series/standalone.ts +10 -1
- package/src/series/types.ts +12 -0
- package/src/series/validate.ts +11 -2
- package/src/sources/gloomberb-cloud/fred-series.ts +5 -3
- package/src/sources/gloomberb-cloud/index.ts +28 -5
- package/src/sources/gloomberb-cloud/news.ts +24 -3
- package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
- package/src/sources/provider-errors.ts +26 -3
- package/src/sources/provider-router/history.ts +5 -0
- package/src/sources/provider-router/index.ts +13 -0
- package/src/sync/core-contributors.ts +32 -0
- package/src/telemetry/attention-counts.ts +64 -6
- package/src/telemetry/attention-setting.ts +33 -0
- package/src/telemetry/crash-reports-node.ts +3 -1
- package/src/telemetry/stall-clocks.ts +93 -0
- package/src/telemetry/stall-reports-node.ts +25 -9
- package/src/telemetry/stall-reports.ts +218 -13
- package/src/test-support/outage.ts +8 -0
- package/src/test-support/preload.ts +9 -0
- package/src/tickers/company-name-listing.ts +59 -29
- package/src/tickers/company-name.ts +84 -1
- package/src/tickers/listing-fate.ts +14 -3
- package/src/tickers/saved-instrument.ts +17 -0
- package/src/tickers/search/home-listing.ts +104 -8
- package/src/tickers/search/index.ts +5 -2
- package/src/tickers/search/types.ts +6 -1
- package/src/time-series/bar-aggregation.ts +7 -1
- package/src/time-series/base-series.ts +3 -3
- package/src/time-series/chart-bounds.ts +1 -1
- package/src/time-series/chart-data.ts +5 -2
- package/src/time-series/chart-seed.ts +1 -1
- package/src/time-series/coin-session-close.ts +211 -0
- package/src/time-series/live-bars.ts +13 -2
- package/src/time-series/live-quotes.ts +1 -1
- package/src/time-series/market.ts +5 -3
- package/src/time-series/official-close.ts +159 -0
- package/src/time-series/price-comparison.ts +75 -8
- package/src/time-series/resolve.ts +32 -4
- package/src/time-series/series-presentation.ts +1 -1
- package/src/time-series/studies/formula.ts +205 -0
- package/src/time-series/studies/indicators.ts +299 -0
- package/src/time-series/studies/leverage.ts +75 -0
- package/src/time-series/studies/pair.ts +138 -0
- package/src/time-series/studies/resolve.ts +333 -0
- package/src/time-series/studies/shared.ts +141 -0
- package/src/time-series/studies/units.ts +243 -0
- package/src/time-series/studies/vwap.ts +274 -0
- package/src/time-series/types.ts +7 -0
- package/src/types/config.ts +18 -2
- package/src/types/data-provider.ts +23 -0
- package/src/types/financials.ts +40 -3
- package/src/types/headless.ts +42 -3
- package/src/types/plugin.ts +32 -3
- package/src/ui/host.tsx +17 -1
- package/src/ui/toast.tsx +26 -1
- package/src/utils/ascii-text.ts +140 -0
- package/src/utils/cli-output.ts +91 -18
- package/src/utils/concurrency.ts +24 -0
- package/src/utils/exchanges.ts +6 -0
- package/src/utils/external-url.ts +20 -7
- package/src/utils/financial-statements.ts +73 -16
- package/src/utils/fundamentals.ts +17 -5
- package/src/utils/futures-generic.ts +4 -4
- package/src/utils/http-proxy-upstream.ts +124 -0
- package/src/utils/lease-liabilities.ts +54 -0
- package/src/utils/main-thread-stall.ts +122 -5
- package/src/utils/perf-marks.ts +58 -12
- package/src/utils/price-history-integrity.ts +47 -1
- package/src/utils/private-files.ts +141 -0
- package/src/utils/public-address.ts +99 -0
- package/src/account/upgrade-personalization.ts +0 -57
- package/src/account/upgrade-tickers.ts +0 -124
- package/src/api-client/upgrade-experiment.ts +0 -91
- package/src/plugins/builtin/cloud/browser.tsx +0 -3
- package/src/time-series/studies.ts +0 -1628
|
@@ -61,8 +61,23 @@ import {
|
|
|
61
61
|
updateBuildoutFooterInfo,
|
|
62
62
|
} from "./status";
|
|
63
63
|
import { useBuildoutDataRuntime } from "../data-runtime";
|
|
64
|
+
import { GEO_SCOPE_STATE_KEY } from "../../world-venue-map/scope";
|
|
65
|
+
import type { GeoScope } from "../../world-venue-map/local-layers";
|
|
66
|
+
import { boardRows, boardThemes, resolveBoardTheme, ThemeBoard, ThemeStats, themeStatRows, useThemeStepKeys } from "./themes";
|
|
64
67
|
|
|
65
68
|
const BUILDOUT_UPGRADE_URL = "https://thebuildout.ai/pricing";
|
|
69
|
+
/** How long the cursor rests on a company before linked panes follow it. */
|
|
70
|
+
const CURSOR_PUBLISH_MS = 150;
|
|
71
|
+
|
|
72
|
+
/** A symbol the rest of the app can open: the dataset writes some Asian listings by number alone. */
|
|
73
|
+
function linkableTicker(value: string | null | undefined): string | null {
|
|
74
|
+
const symbol = tickerSymbol(value);
|
|
75
|
+
return symbol && /^[A-Z][A-Z0-9.-]*$/.test(symbol) ? symbol : null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function sameScope(left: GeoScope | null, right: GeoScope | null): boolean {
|
|
79
|
+
return left?.key === right?.key && left?.label === right?.label;
|
|
80
|
+
}
|
|
66
81
|
|
|
67
82
|
/** A star a row's company or site did not take: said by its star cell, in full at the top of its detail. */
|
|
68
83
|
const favoriteNoteKey = (row: BuildoutRow) => favoriteKey(row) ?? rowKey(row);
|
|
@@ -97,8 +112,11 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
|
|
|
97
112
|
const tableScrollRef = useRef<ScrollBoxRenderable | null>(null);
|
|
98
113
|
const favoriteBusyKeysRef = useRef<Set<string>>(new Set());
|
|
99
114
|
const clearFavoriteNotesRef = useRef<() => void>(() => {});
|
|
100
|
-
const [activeTab, setActiveTab] = usePaneStateValue<BuildoutTabId>("activeTab", "
|
|
115
|
+
const [activeTab, setActiveTab] = usePaneStateValue<BuildoutTabId>("activeTab", "themes");
|
|
101
116
|
const [selectedList, setSelectedList] = useState<BuildoutList | null>(null);
|
|
117
|
+
const [themeSlug, setThemeSlug] = usePaneStateValue<string | null>("themeSlug", null);
|
|
118
|
+
const [, setCursorSymbol] = usePaneStateValue<string | null>("cursorSymbol", null);
|
|
119
|
+
const [, setGeoScope] = usePaneStateValue<GeoScope | null>(GEO_SCOPE_STATE_KEY, null);
|
|
102
120
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
103
121
|
const [detailRow, setDetailRow] = useState<BuildoutRow | null>(null);
|
|
104
122
|
const [sort, setSort] = useState<SortPreference<BuildoutColumnId>>({ columnId: null, direction: "asc" });
|
|
@@ -126,7 +144,12 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
|
|
|
126
144
|
activeTab,
|
|
127
145
|
onBeforeLoad: handleBeforeLoad,
|
|
128
146
|
selectedList,
|
|
147
|
+
themeSlug,
|
|
129
148
|
});
|
|
149
|
+
const themes = useMemo(() => (state.status === "ready" ? boardThemes(state.lists) : []), [state]);
|
|
150
|
+
const themeList = useMemo(() => resolveBoardTheme(themes, themeSlug), [themeSlug, themes]);
|
|
151
|
+
// The list whose companies the table shows: the board's theme, or the one opened from Companies.
|
|
152
|
+
const currentList = activeTab === "themes" ? themeList : selectedList;
|
|
130
153
|
const favoriteToken = state.status === "ready" ? state.token : null;
|
|
131
154
|
const canFavorite = favoriteToken != null;
|
|
132
155
|
|
|
@@ -146,25 +169,43 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
|
|
|
146
169
|
useEffect(() => {
|
|
147
170
|
setSelectedIndex(0);
|
|
148
171
|
setDetailRow(null);
|
|
149
|
-
const nextColumn: BuildoutColumnId | null = activeTab === "companies"
|
|
150
|
-
?
|
|
172
|
+
const nextColumn: BuildoutColumnId | null = activeTab === "companies" || activeTab === "themes"
|
|
173
|
+
? currentList ? "marketCap" : null
|
|
151
174
|
: activeTab === "sites" ? "capture" : "time";
|
|
152
175
|
setSort({ columnId: nextColumn, direction: defaultSortDirection(nextColumn) });
|
|
153
|
-
}, [activeTab,
|
|
176
|
+
}, [activeTab, currentList?.slug]);
|
|
154
177
|
|
|
155
178
|
const rows = useMemo(
|
|
156
|
-
() => sortRows(activeRows(state, activeTab,
|
|
157
|
-
[activeTab,
|
|
179
|
+
() => sortRows(activeRows(state, activeTab, currentList), sort.columnId, sort.direction),
|
|
180
|
+
[activeTab, currentList, sort, state],
|
|
158
181
|
);
|
|
159
182
|
// Until the next star on that row, or a reload.
|
|
160
183
|
const favoriteNotes = useRowNotes<BuildoutRow, string>(rows, { key: favoriteNoteKey });
|
|
161
184
|
const { setNote: setFavoriteNote, noteFor: favoriteNoteFor } = favoriteNotes;
|
|
162
185
|
clearFavoriteNotesRef.current = favoriteNotes.clearNotes;
|
|
163
186
|
const columns = useMemo(
|
|
164
|
-
() => columnsForTab(activeTab,
|
|
165
|
-
[activeTab, canFavorite,
|
|
187
|
+
() => columnsForTab(activeTab, currentList, canFavorite, width),
|
|
188
|
+
[activeTab, canFavorite, currentList, width],
|
|
166
189
|
);
|
|
167
190
|
const selectedRow = rows[selectedIndex] ?? rows[0] ?? null;
|
|
191
|
+
|
|
192
|
+
// Panes linked to this one follow the company under the cursor, and a linked map can widen to the
|
|
193
|
+
// theme it belongs to. Off the company tabs both stay where they were.
|
|
194
|
+
const cursorCompany = detailRow?.kind === "company" ? detailRow.item : selectedRow?.kind === "company" ? selectedRow.item : null;
|
|
195
|
+
const cursorTicker = linkableTicker(cursorCompany?.ticker);
|
|
196
|
+
// Published once the cursor rests: arrowing down a theme would otherwise load every company it passes.
|
|
197
|
+
useEffect(() => {
|
|
198
|
+
if (!cursorTicker) return;
|
|
199
|
+
const timer = setTimeout(() => setCursorSymbol(cursorTicker), CURSOR_PUBLISH_MS);
|
|
200
|
+
return () => clearTimeout(timer);
|
|
201
|
+
}, [cursorTicker, setCursorSymbol]);
|
|
202
|
+
const companyTab = activeTab === "themes" || activeTab === "companies";
|
|
203
|
+
const themeScope = companyTab && currentList && currentList.slug !== "all" ? currentList : null;
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (!companyTab) return;
|
|
206
|
+
const next: GeoScope | null = themeScope ? { key: `theme:${themeScope.slug}`, label: themeScope.name } : null;
|
|
207
|
+
setGeoScope((current) => (sameScope(current, next) ? current : next));
|
|
208
|
+
}, [companyTab, setGeoScope, themeScope?.name, themeScope?.slug]);
|
|
168
209
|
// The table's cells carry their own badges; this catalog only serves the open
|
|
169
210
|
// detail: its row's tickers, plus the loaded rows' tickers its text mentions.
|
|
170
211
|
// Streaming every loaded row here would subscribe pages of symbols nobody sees.
|
|
@@ -226,8 +267,8 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
|
|
|
226
267
|
// The free tier sees the head of a list; the footer says so, the `$` hint
|
|
227
268
|
// is the way out, and no count of what is hidden is drawn.
|
|
228
269
|
const partialList = state.status === "ready"
|
|
229
|
-
&&
|
|
230
|
-
&& !!
|
|
270
|
+
&& companyTab
|
|
271
|
+
&& !!currentList
|
|
231
272
|
&& !state.companies.loadingMore
|
|
232
273
|
&& !state.companies.hasMore
|
|
233
274
|
&& !state.companies.error
|
|
@@ -262,6 +303,10 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
|
|
|
262
303
|
setDetailRow(row);
|
|
263
304
|
}, [openCompanyList]);
|
|
264
305
|
|
|
306
|
+
const selectTheme = useCallback((list: BuildoutList) => {
|
|
307
|
+
setThemeSlug(list.slug);
|
|
308
|
+
}, [setThemeSlug]);
|
|
309
|
+
|
|
265
310
|
const updateFavorite = useCallback((key: string, starred: boolean) => {
|
|
266
311
|
setState((current) => applyFavoriteToState(current, key, starred));
|
|
267
312
|
setDetailRow((current) => (
|
|
@@ -328,23 +373,23 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
|
|
|
328
373
|
}, [chooseOpenTarget, favoriteBusyKey, favoriteTarget, favoriteTargetKey, openTargets.length, startUpgrade, state, toggleFavoriteRow]);
|
|
329
374
|
|
|
330
375
|
usePaneFooter("buildout", () => ({
|
|
331
|
-
info: updateBuildoutFooterInfo(state, activeTab,
|
|
376
|
+
info: updateBuildoutFooterInfo(state, activeTab, currentList, {
|
|
332
377
|
upgradeMessage,
|
|
333
378
|
partialList,
|
|
334
379
|
onUpgrade: startUpgrade,
|
|
335
380
|
quoteFreshness: detailQuoteFreshness,
|
|
336
381
|
}),
|
|
337
382
|
hints: footerHints,
|
|
338
|
-
}), [activeTab, detailQuoteFreshness, footerHints, partialList,
|
|
383
|
+
}), [activeTab, currentList, detailQuoteFreshness, footerHints, partialList, startUpgrade, state, upgradeMessage]);
|
|
339
384
|
|
|
340
|
-
const activePage = state.status === "ready" ? activeBuildoutPage(state, activeTab,
|
|
385
|
+
const activePage = state.status === "ready" ? activeBuildoutPage(state, activeTab, currentList) : null;
|
|
341
386
|
const loadMoreActiveRows = useTableLoadMore(
|
|
342
387
|
tableScrollRef,
|
|
343
388
|
!!activePage && !activePage.loadingMore && !!activePage.hasMore && !activePage.error,
|
|
344
389
|
() => {
|
|
345
390
|
if (!activePage) return;
|
|
346
|
-
if (
|
|
347
|
-
void loadCompanies(
|
|
391
|
+
if (companyTab && currentList) {
|
|
392
|
+
void loadCompanies(currentList, activePage.offset, true);
|
|
348
393
|
} else if (activeTab === "sites") {
|
|
349
394
|
void loadSites(activePage.offset, true);
|
|
350
395
|
} else if (activeTab === "intel") {
|
|
@@ -385,6 +430,8 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
|
|
|
385
430
|
toggleFavorite,
|
|
386
431
|
}), [favoriteBusyKey, favoriteNoteFor, toggleFavorite]);
|
|
387
432
|
|
|
433
|
+
useThemeStepKeys({ themes, selectedSlug: themeList?.slug ?? null, enabled: focused && activeTab === "themes" && !detailRow, onSelect: selectTheme });
|
|
434
|
+
|
|
388
435
|
const { strip: tabStrip, rows: tabRows } = usePaneTabs({
|
|
389
436
|
tabs,
|
|
390
437
|
activeValue: activeTab,
|
|
@@ -406,7 +453,12 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
|
|
|
406
453
|
}
|
|
407
454
|
|
|
408
455
|
const listOpen = activeTab === "companies" && !!selectedList;
|
|
409
|
-
|
|
456
|
+
// Themes: the board, the theme in figures, then its companies; an open company takes the whole body.
|
|
457
|
+
const themesBody = activeTab === "themes" && !detailRow;
|
|
458
|
+
const bodyHeight = Math.max(1, height - tabRows);
|
|
459
|
+
const boardHeight = themesBody ? boardRows(bodyHeight) : 0;
|
|
460
|
+
const statHeight = themesBody ? themeStatRows(themeList, width) : 0;
|
|
461
|
+
const tableHeight = Math.max(1, bodyHeight - (listOpen ? 1 : 0) - boardHeight - statHeight);
|
|
410
462
|
const table = (
|
|
411
463
|
<DataTableStackView<BuildoutRow, BuildoutColumn>
|
|
412
464
|
focused={focused}
|
|
@@ -430,7 +482,7 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
|
|
|
430
482
|
selection={{
|
|
431
483
|
kind: "index",
|
|
432
484
|
selectedIndex,
|
|
433
|
-
onChange:
|
|
485
|
+
onChange: setSelectedIndex,
|
|
434
486
|
}}
|
|
435
487
|
onActivate={activateRow}
|
|
436
488
|
// The lists start unsorted, and every table sorts by its headers.
|
|
@@ -441,16 +493,35 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
|
|
|
441
493
|
getItemKey={rowKey}
|
|
442
494
|
renderCell={renderCell}
|
|
443
495
|
selectedTextOverridesCellColor
|
|
444
|
-
emptyContent={renderBuildoutPageStatus(state, activeTab,
|
|
445
|
-
emptyStateTitle={
|
|
496
|
+
emptyContent={renderBuildoutPageStatus(state, activeTab, currentList)}
|
|
497
|
+
emptyStateTitle={currentList ? "No companies" : "No rows"}
|
|
446
498
|
onRootKeyDown={handleRootKeyDown}
|
|
447
499
|
onDetailKeyDown={handleDetailKeyDown}
|
|
448
500
|
onBodyScrollActivity={loadMoreActiveRows}
|
|
449
501
|
scrollRef={tableScrollRef}
|
|
450
|
-
resetScrollKey={`${activeTab}:${
|
|
502
|
+
resetScrollKey={`${activeTab}:${currentList?.slug ?? "lists"}`}
|
|
451
503
|
/>
|
|
452
504
|
);
|
|
453
505
|
|
|
506
|
+
if (activeTab === "themes") {
|
|
507
|
+
return (
|
|
508
|
+
<Box flexDirection="column" width={width} height={height} overflow="hidden">
|
|
509
|
+
{tabStrip && <Box height={1}>{tabStrip}</Box>}
|
|
510
|
+
{boardHeight > 0 ? (
|
|
511
|
+
<ThemeBoard
|
|
512
|
+
themes={themes}
|
|
513
|
+
selectedSlug={themeList?.slug ?? null}
|
|
514
|
+
width={width}
|
|
515
|
+
height={boardHeight}
|
|
516
|
+
onSelect={selectTheme}
|
|
517
|
+
/>
|
|
518
|
+
) : null}
|
|
519
|
+
{themesBody ? <ThemeStats list={themeList} width={width} /> : null}
|
|
520
|
+
{table}
|
|
521
|
+
</Box>
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
454
525
|
// Tabs on top; below them a list opened from Companies is a stack level with
|
|
455
526
|
// the shared Back row, and a company opened from that list is the table's
|
|
456
527
|
// own detail. The outer stack only listens while the inner detail is closed.
|
|
@@ -11,7 +11,7 @@ export function activeBuildoutPage(
|
|
|
11
11
|
selectedList: BuildoutList | null,
|
|
12
12
|
) {
|
|
13
13
|
if (state.status !== "ready") return null;
|
|
14
|
-
if (activeTab === "companies") return selectedList ? state.companies : null;
|
|
14
|
+
if (activeTab === "companies" || activeTab === "themes") return selectedList ? state.companies : null;
|
|
15
15
|
if (activeTab === "sites") return state.sites;
|
|
16
16
|
return state.intel;
|
|
17
17
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useId, useMemo } from "react";
|
|
2
|
+
import { MetricTreemapSurface, StatGrid, type MetricTreemapItem, type StatItem } from "../../../../components";
|
|
3
|
+
import { statGridWithin } from "../../../../components/ui/stat-grid";
|
|
4
|
+
import { useShortcut } from "../../../../react/input";
|
|
5
|
+
import { priceColor } from "../../../../theme/colors";
|
|
6
|
+
import { isPlainKey } from "../../../../utils/keyboard";
|
|
7
|
+
import { metricNumber, moneyAmount, text } from "../format";
|
|
8
|
+
import type { BuildoutList } from "../model/types";
|
|
9
|
+
|
|
10
|
+
/** The board leaves the table at least this many rows, and draws only when it has this many itself. */
|
|
11
|
+
const MIN_TABLE_ROWS = 8;
|
|
12
|
+
const MIN_BOARD_ROWS = 6;
|
|
13
|
+
|
|
14
|
+
/** The themes the board draws, largest first: the order the arrows step through. */
|
|
15
|
+
export function boardThemes(lists: readonly BuildoutList[]): BuildoutList[] {
|
|
16
|
+
return lists
|
|
17
|
+
.filter((list) => list.slug !== "all")
|
|
18
|
+
.map((list, index) => ({ list, index, cap: moneyAmount(list.totalMarketCap) ?? 0 }))
|
|
19
|
+
.sort((left, right) => right.cap - left.cap || left.index - right.index)
|
|
20
|
+
.map(({ list }) => list);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The theme the board opens on: the one saved with the pane, else the largest. */
|
|
24
|
+
export function resolveBoardTheme(themes: readonly BuildoutList[], slug: string | null): BuildoutList | null {
|
|
25
|
+
return themes.find((list) => list.slug === slug) ?? themes[0] ?? null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Rows for the board in a Themes body of `height` rows; zero when the table needs them all. */
|
|
29
|
+
export function boardRows(height: number): number {
|
|
30
|
+
const rows = Math.min(Math.floor(height * 0.46), height - MIN_TABLE_ROWS);
|
|
31
|
+
return rows >= MIN_BOARD_ROWS ? rows : 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Left and right step to the next theme by size, board or not; up and down
|
|
36
|
+
* stay with the companies table. Ahead of the pane's tabs, which take left and
|
|
37
|
+
* right as well; h and l still switch tabs.
|
|
38
|
+
*/
|
|
39
|
+
export function useThemeStepKeys({ themes, selectedSlug, enabled, onSelect }: {
|
|
40
|
+
themes: readonly BuildoutList[];
|
|
41
|
+
selectedSlug: string | null;
|
|
42
|
+
enabled: boolean;
|
|
43
|
+
onSelect: (list: BuildoutList) => void;
|
|
44
|
+
}) {
|
|
45
|
+
const scope = useId();
|
|
46
|
+
const index = Math.max(0, themes.findIndex((list) => list.slug === selectedSlug));
|
|
47
|
+
useShortcut((event) => {
|
|
48
|
+
if (!enabled || themes.length === 0) return;
|
|
49
|
+
const step = isPlainKey(event, "left") ? -1 : isPlainKey(event, "right") ? 1 : 0;
|
|
50
|
+
if (!step) return;
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
event.stopPropagation();
|
|
53
|
+
onSelect(themes[(index + step + themes.length) % themes.length]!);
|
|
54
|
+
}, { enabled, scope: `buildout-themes:${scope}` });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The selected theme in figures, between the board and its companies. */
|
|
58
|
+
function themeStatItems(list: BuildoutList | null): StatItem[] {
|
|
59
|
+
if (!list) return [];
|
|
60
|
+
const returnColor = metricNumber(list.avgReturn1y);
|
|
61
|
+
const growthColor = metricNumber(list.avgSectorGrowth);
|
|
62
|
+
return [
|
|
63
|
+
{ id: "theme", label: "Theme", value: list.name, detail: list.shortDescription ?? undefined, detailVariants: [""], wide: true },
|
|
64
|
+
{ id: "companies", label: "Companies", value: text(list.companyCount) },
|
|
65
|
+
{ id: "return", label: "1Y median", value: text(list.avgReturn1y), ...(returnColor == null ? {} : { color: priceColor(returnColor) }) },
|
|
66
|
+
{ id: "growth", label: "Growth", value: text(list.avgSectorGrowth), ...(growthColor == null ? {} : { color: priceColor(growthColor) }) },
|
|
67
|
+
{ id: "leaders", label: "Leaders", value: text(list.leaders) },
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function themeStatRows(list: BuildoutList | null, width: number): number {
|
|
72
|
+
return statGridWithin(themeStatItems(list), width, 2).lines;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function ThemeStats({ list, width }: { list: BuildoutList | null; width: number }) {
|
|
76
|
+
const { items } = statGridWithin(themeStatItems(list), width, 2);
|
|
77
|
+
return <StatGrid items={items} width={width} maxLines={2} />;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The themes as a board: each tile sized by the theme's market value and
|
|
82
|
+
* coloured by its companies' median one-year return.
|
|
83
|
+
*/
|
|
84
|
+
export function ThemeBoard({ themes, selectedSlug, width, height, onSelect }: {
|
|
85
|
+
themes: readonly BuildoutList[];
|
|
86
|
+
selectedSlug: string | null;
|
|
87
|
+
width: number;
|
|
88
|
+
height: number;
|
|
89
|
+
onSelect: (list: BuildoutList) => void;
|
|
90
|
+
}) {
|
|
91
|
+
const items = useMemo<MetricTreemapItem<BuildoutList>[]>(() => themes.map((list) => ({
|
|
92
|
+
id: list.slug,
|
|
93
|
+
label: list.name,
|
|
94
|
+
weight: moneyAmount(list.totalMarketCap),
|
|
95
|
+
colorValue: metricNumber(list.avgReturn1y),
|
|
96
|
+
primaryText: list.avgReturn1y,
|
|
97
|
+
tooltip: [list.name, `${text(list.avgReturn1y)} 1Y median · ${text(list.totalMarketCap)} · ${text(list.companyCount)} companies`],
|
|
98
|
+
data: list,
|
|
99
|
+
})), [themes]);
|
|
100
|
+
return (
|
|
101
|
+
<MetricTreemapSurface
|
|
102
|
+
items={items}
|
|
103
|
+
width={width}
|
|
104
|
+
height={height}
|
|
105
|
+
selectedId={selectedSlug}
|
|
106
|
+
onSelect={(item) => onSelect(item.data)}
|
|
107
|
+
emptyStateTitle="No themes"
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
@@ -10,9 +10,10 @@ import type {
|
|
|
10
10
|
} from "./model/types";
|
|
11
11
|
import { dropColumnsToFit } from "../../../components/ui/table-layout";
|
|
12
12
|
import type { SortDirection } from "../../../utils/sort-values";
|
|
13
|
-
import { metricNumber, sourceDetailEntries, textOrNull, tickerSymbol } from "./format";
|
|
13
|
+
import { LIST_TIER_ORDER, metricNumber, sourceDetailEntries, textOrNull, tickerSymbol } from "./format";
|
|
14
14
|
|
|
15
15
|
export const tabs: Array<{ label: string; value: BuildoutTabId }> = [
|
|
16
|
+
{ label: "Themes", value: "themes" },
|
|
16
17
|
{ label: "Companies", value: "companies" },
|
|
17
18
|
{ label: "Sites", value: "sites" },
|
|
18
19
|
{ label: "Intel", value: "intel" },
|
|
@@ -46,6 +47,17 @@ const companyColumns: BuildoutColumn[] = [
|
|
|
46
47
|
{ id: "employees", label: "Employees", width: 9, align: "right" },
|
|
47
48
|
];
|
|
48
49
|
|
|
50
|
+
/** A theme's companies under its board: what each does in the theme, then the figures. */
|
|
51
|
+
const themeCompanyColumns: BuildoutColumn[] = [
|
|
52
|
+
{ id: "company", label: "Company", width: 26, align: "left", flexGrow: 2 },
|
|
53
|
+
{ id: "listRole", label: "Role", width: 15, align: "left" },
|
|
54
|
+
{ id: "listTier", label: "Tier", width: 9, align: "left" },
|
|
55
|
+
{ id: "marketCap", label: "Mkt Cap", width: 10, align: "right" },
|
|
56
|
+
{ id: "revenueGrowth", label: "Growth", width: 9, align: "right" },
|
|
57
|
+
{ id: "return1y", label: "1Y", width: 8, align: "right" },
|
|
58
|
+
];
|
|
59
|
+
const THEME_COMPANY_DROP_ORDER: readonly BuildoutColumnId[] = ["listTier", "revenueGrowth", "listRole"];
|
|
60
|
+
|
|
49
61
|
const siteColumns: BuildoutColumn[] = [
|
|
50
62
|
{ id: "site", label: "Site Name", width: 28, align: "left", flexGrow: 2 },
|
|
51
63
|
// The default sort, so it sits where it is always on screen.
|
|
@@ -160,11 +172,16 @@ export function activeRows(
|
|
|
160
172
|
selectedList: BuildoutList | null,
|
|
161
173
|
): BuildoutRow[] {
|
|
162
174
|
if (state.status !== "ready") return [];
|
|
175
|
+
if (activeTab === "themes") {
|
|
176
|
+
return selectedList && state.companies.listSlug === selectedList.slug
|
|
177
|
+
? state.companies.items.map((item) => ({ kind: "company", item }))
|
|
178
|
+
: [];
|
|
179
|
+
}
|
|
163
180
|
if (activeTab === "companies") {
|
|
164
181
|
if (!selectedList) {
|
|
165
182
|
return state.lists.map((item) => ({ kind: "list", item }));
|
|
166
183
|
}
|
|
167
|
-
return state.companies.items.map((item) => ({ kind: "company", item }));
|
|
184
|
+
return state.companies.listSlug === selectedList.slug ? state.companies.items.map((item) => ({ kind: "company", item })) : [];
|
|
168
185
|
}
|
|
169
186
|
if (activeTab === "sites") {
|
|
170
187
|
return state.sites.items.map((item) => ({ kind: "site", item }));
|
|
@@ -190,6 +207,9 @@ export function columnsForTab(
|
|
|
190
207
|
canFavorite: boolean,
|
|
191
208
|
width: number,
|
|
192
209
|
): BuildoutColumn[] {
|
|
210
|
+
if (activeTab === "themes") {
|
|
211
|
+
return dropColumnsToFit(canFavorite ? [favoriteColumn, ...themeCompanyColumns] : themeCompanyColumns, width - 1, THEME_COMPANY_DROP_ORDER);
|
|
212
|
+
}
|
|
193
213
|
if (activeTab === "companies") {
|
|
194
214
|
const columns = selectedList
|
|
195
215
|
? canFavorite ? [favoriteColumn, ...companyColumns] : companyColumns
|
|
@@ -286,6 +306,10 @@ function sortValue(row: BuildoutRow, columnId: BuildoutColumnId): SortComparable
|
|
|
286
306
|
return numberSort(company.return1y);
|
|
287
307
|
case "employees":
|
|
288
308
|
return numberSort(company.employeeCount);
|
|
309
|
+
case "listRole":
|
|
310
|
+
return stringSort(company.listRole);
|
|
311
|
+
case "listTier":
|
|
312
|
+
return numberSort(company.listTier && LIST_TIER_ORDER.includes(company.listTier) ? LIST_TIER_ORDER.indexOf(company.listTier) : null);
|
|
289
313
|
default:
|
|
290
314
|
return stringSort(rowTitle(row));
|
|
291
315
|
}
|
|
@@ -163,3 +163,19 @@ export const earningsPluginMeta = {
|
|
|
163
163
|
version: "1.0.0",
|
|
164
164
|
toggleable: true,
|
|
165
165
|
};
|
|
166
|
+
|
|
167
|
+
export const fearGreedPluginMeta = {
|
|
168
|
+
id: "fear-greed",
|
|
169
|
+
name: "Fear & Greed",
|
|
170
|
+
version: "1.0.0",
|
|
171
|
+
description: "CNN Fear & Greed index with the seven indicator charts",
|
|
172
|
+
toggleable: true,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export const notesPluginMeta = {
|
|
176
|
+
id: "notes",
|
|
177
|
+
name: "Notes",
|
|
178
|
+
version: "2.0.0",
|
|
179
|
+
description: "Markdown notes on tickers and on their own, personal or shared with a team.",
|
|
180
|
+
toggleable: true,
|
|
181
|
+
};
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
type CoveredIssuers,
|
|
22
22
|
type InstrumentSearch,
|
|
23
23
|
} from "./client";
|
|
24
|
-
import { formatContract } from "../credit-boards/model";
|
|
24
|
+
import { formatContract, levelKinds } from "../credit-boards/model";
|
|
25
25
|
import {
|
|
26
26
|
DEFAULT_TRADE_SORT,
|
|
27
27
|
ISSUER_LIST_SORTS,
|
|
@@ -122,6 +122,8 @@ function dailySection(history: CdsSpreadHistory, days: number, name: string): He
|
|
|
122
122
|
const from = latest ? new Date(Date.parse(`${latest}T00:00:00Z`) - days * 86_400_000).toISOString().slice(0, 10) : "";
|
|
123
123
|
// A name quoted upfront lists each day's points beside its spread.
|
|
124
124
|
const upfront = history.quote === "upfront";
|
|
125
|
+
const shown = history.points.filter((point) => point.date > from).reverse();
|
|
126
|
+
const kinds = levelKinds(shown, (point) => point.kind);
|
|
125
127
|
return {
|
|
126
128
|
title: "Daily",
|
|
127
129
|
columns: [
|
|
@@ -136,8 +138,9 @@ function dailySection(history: CdsSpreadHistory, days: number, name: string): He
|
|
|
136
138
|
}] : []),
|
|
137
139
|
{ key: "prints", header: "Prints", align: "right" as const },
|
|
138
140
|
{ key: "maturity", header: "Contract", format: (value: unknown) => formatContract(typeof value === "string" ? value : null) },
|
|
141
|
+
...kinds.columns,
|
|
139
142
|
],
|
|
140
|
-
rows:
|
|
143
|
+
rows: shown
|
|
141
144
|
.map((point) => ({
|
|
142
145
|
day: point.date,
|
|
143
146
|
spreadBp: point.spreadBp,
|
|
@@ -145,6 +148,7 @@ function dailySection(history: CdsSpreadHistory, days: number, name: string): He
|
|
|
145
148
|
unit: upfront ? "pts" : "bp",
|
|
146
149
|
prints: point.prints,
|
|
147
150
|
maturity: point.maturity,
|
|
151
|
+
...kinds.row(point),
|
|
148
152
|
})),
|
|
149
153
|
empty: "No daily 5Y levels.",
|
|
150
154
|
symbol: name,
|
|
@@ -212,11 +216,14 @@ function spreadEntries(history: CdsSpreadHistory): HeadlessPaneEntry[] {
|
|
|
212
216
|
: { lowBp: summary.range.low, highBp: summary.range.high, since: summary.range.since },
|
|
213
217
|
formatted: `${formatSpreadRange(unit, summary.range)} since ${summary.range.since}`,
|
|
214
218
|
} : null;
|
|
219
|
+
// A level that is the day's settlement price says so beside its date; one from trades says nothing.
|
|
220
|
+
const kind = summary.settlement ? [{ key: "kind", label: "Level", value: "settlement" }] : [];
|
|
215
221
|
if (unit === "pts") {
|
|
216
222
|
return [
|
|
217
223
|
{ key: "upfrontPts", label: "5Y upfront", value: summary.level, formatted: formatPoints(summary.level), unit: "pts" },
|
|
218
224
|
{ key: "couponBp", label: "Coupon", value: summary.couponBp, formatted: formatBp(summary.couponBp) },
|
|
219
225
|
{ key: "date", label: "Date", value: summary.date },
|
|
226
|
+
...kind,
|
|
220
227
|
{ key: "change1wPts", label: "1W change", value: summary.change1w, formatted: formatPointsMove(summary.change1w), unit: "pts" },
|
|
221
228
|
{ key: "change1mPts", label: "1M change", value: summary.change1m, formatted: formatPointsMove(summary.change1m), unit: "pts" },
|
|
222
229
|
...(range ? [range] : []),
|
|
@@ -225,6 +232,7 @@ function spreadEntries(history: CdsSpreadHistory): HeadlessPaneEntry[] {
|
|
|
225
232
|
return [
|
|
226
233
|
{ key: "spreadBp", label: "5Y spread", value: summary.level, formatted: formatBp(summary.level) },
|
|
227
234
|
{ key: "date", label: "Date", value: summary.date },
|
|
235
|
+
...kind,
|
|
228
236
|
{ key: "change1wBp", label: "1W change", value: summary.change1w, formatted: formatSpreadMove(summary.change1w) },
|
|
229
237
|
{ key: "change1mBp", label: "1M change", value: summary.change1m, formatted: formatSpreadMove(summary.change1m) },
|
|
230
238
|
...(range ? [range] : []),
|
|
@@ -6,7 +6,7 @@ import type { TickerRecord } from "../../../types/ticker";
|
|
|
6
6
|
import { formatCompact } from "../../../utils/format";
|
|
7
7
|
import { compareSortValues, type SortPreference } from "../../../utils/sort-values";
|
|
8
8
|
import { typoDistance } from "../../../utils/typo-distance";
|
|
9
|
-
import { formatMove } from "../credit-boards/model";
|
|
9
|
+
import { formatMove, SETTLEMENT_TEXT } from "../credit-boards/model";
|
|
10
10
|
|
|
11
11
|
export const CDS_PANE_ID = "cds";
|
|
12
12
|
|
|
@@ -858,6 +858,8 @@ export interface CdsSpreadSummary {
|
|
|
858
858
|
/** The latest level in that unit, and the New York trade date it is from. */
|
|
859
859
|
level: number;
|
|
860
860
|
date: string;
|
|
861
|
+
/** The level is that day's settlement price, not its trades. */
|
|
862
|
+
settlement: boolean;
|
|
861
863
|
/** The same level in bp. */
|
|
862
864
|
spreadBp: number;
|
|
863
865
|
/** The running coupon of a name quoted upfront, bp. */
|
|
@@ -888,6 +890,7 @@ export function spreadSummary(line: CdsSpreadLine): CdsSpreadSummary | null {
|
|
|
888
890
|
unit: upfront ? "pts" : "bp",
|
|
889
891
|
level: upfront ? line.upfrontPts! : latest.spreadBp,
|
|
890
892
|
date: latest.date,
|
|
893
|
+
settlement: latest.kind === "settlement",
|
|
891
894
|
spreadBp: latest.spreadBp,
|
|
892
895
|
couponBp: upfront ? line.couponBp : null,
|
|
893
896
|
change1w: upfront ? line.change1WPts : line.change1W,
|
|
@@ -921,15 +924,16 @@ export function spreadFigures(line: CdsSpreadLine): StatItem[] {
|
|
|
921
924
|
const summary = spreadSummary(line);
|
|
922
925
|
if (!summary) return [];
|
|
923
926
|
const { unit } = summary;
|
|
927
|
+
const dated = summary.settlement ? `${summary.date} · ${SETTLEMENT_TEXT}` : summary.date;
|
|
924
928
|
return [
|
|
925
929
|
unit === "pts"
|
|
926
930
|
? {
|
|
927
931
|
id: "spread",
|
|
928
932
|
label: "5Y upfront",
|
|
929
933
|
value: formatPoints(summary.level),
|
|
930
|
-
detail: summary.couponBp == null ?
|
|
934
|
+
detail: summary.couponBp == null ? dated : `${dated} · ${formatBp(summary.couponBp)} coupon`,
|
|
931
935
|
}
|
|
932
|
-
: { id: "spread", label: "5Y spread", value: formatBp(summary.level), detail:
|
|
936
|
+
: { id: "spread", label: "5Y spread", value: formatBp(summary.level), detail: dated },
|
|
933
937
|
...(summary.change1w != null ? [{ id: "1w", label: "1W", value: formatQuotedMove(unit, summary.change1w) }] : []),
|
|
934
938
|
...(summary.change1m != null ? [{ id: "1m", label: "1M", value: formatQuotedMove(unit, summary.change1m) }] : []),
|
|
935
939
|
...(summary.range ? [{
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type { PluginModule } from "../plugin-module";
|
|
2
2
|
import { centralBankRatesCache } from "./client";
|
|
3
3
|
import { centralBankRatesHeadless } from "./headless";
|
|
4
|
-
import { CentralBankRatesPane } from "./pane";
|
|
5
4
|
import { CENTRAL_BANK_SERIES } from "./series";
|
|
6
5
|
|
|
7
6
|
export const centralBankRatesModule: PluginModule = {
|
|
8
7
|
series: CENTRAL_BANK_SERIES,
|
|
9
|
-
panes: [{ id: "central-bank-rates", name: "Central Bank Rates", icon: "%", component: CentralBankRatesPane,
|
|
8
|
+
panes: [{ id: "central-bank-rates", name: "Central Bank Rates", icon: "%", get component(): typeof import("./pane").CentralBankRatesPane { const { CentralBankRatesPane } = require("./pane"); return CentralBankRatesPane; },
|
|
10
9
|
defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 108, height: 25 },
|
|
11
10
|
tableExport: true, headless: centralBankRatesHeadless }],
|
|
12
11
|
paneTemplates: [{
|
|
@@ -2,6 +2,7 @@ import { useCallback, useMemo } from "react";
|
|
|
2
2
|
import { Box } from "../../../ui";
|
|
3
3
|
import { useAsyncResource, useAutoRefresh, usePluginPaneState, useUpdatedAgo } from "../../../public/react";
|
|
4
4
|
import { CompositeChart, MarketBoardStack, PaneStatusBody, usePaneNoticeFooter, usePaneStatusLinkFooter, type StatItem } from "../../../components";
|
|
5
|
+
import { ageFooterSegment } from "../../../components/layout/pane/footer";
|
|
5
6
|
import { colors } from "../../../theme/colors";
|
|
6
7
|
import { isAccessDenied } from "../../../api-client/errors";
|
|
7
8
|
import type { CentralBankRow } from "../../../api-client/central-bank-rates";
|
|
@@ -59,7 +60,7 @@ export function CentralBankRatesPane({ width, height, focused }: PaneProps) {
|
|
|
59
60
|
notices: [...(data ? policyNotices(data) : []), ...(resource.data?.refreshError ? [resource.data.refreshError] : [])] });
|
|
60
61
|
usePaneStatusLinkFooter({ registrationId: "central-bank-rates", focused, loading: resource.loading, error: resource.error,
|
|
61
62
|
url: selected?.observation.sourceUrl ?? null, showOpenHint: true,
|
|
62
|
-
info: data && updatedAgo ? [
|
|
63
|
+
info: data && updatedAgo ? [ageFooterSegment(updatedAgo)] : [],
|
|
63
64
|
stale: !!data && resource.data?.stale,
|
|
64
65
|
});
|
|
65
66
|
return <Box width={width} height={height} flexDirection="column">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SeriesDefinition } from "../../../series/types";
|
|
2
2
|
import { CENTRAL_BANKS, centralBankNames } from "../../../utils/central-banks";
|
|
3
|
-
import {
|
|
3
|
+
import { cachedSeriesNote } from "../shared/series-warning";
|
|
4
4
|
import { loadCentralBankRates } from "./client";
|
|
5
5
|
import { policySeriesAttribution } from "./model";
|
|
6
6
|
|
|
@@ -24,12 +24,11 @@ export const CENTRAL_BANK_SERIES: readonly SeriesDefinition[] = CENTRAL_BANKS.ma
|
|
|
24
24
|
const resource = await loadCentralBankRates();
|
|
25
25
|
const row = resource.payload.rows.find((entry) => entry.id === bank.id);
|
|
26
26
|
if (!row) throw new Error(`${bank.place} is not on the board right now.`);
|
|
27
|
-
const warning = cachedSeriesWarning(resource);
|
|
28
27
|
// Cited as CBR cites the row: the BIS or FRED series and the bank that sets the rate.
|
|
29
28
|
const attribution = policySeriesAttribution(row);
|
|
30
29
|
return {
|
|
31
30
|
points: row.history.map((point) => ({ date: point.date, value: point.value })),
|
|
32
|
-
...(
|
|
31
|
+
...cachedSeriesNote(resource),
|
|
33
32
|
...(attribution ? { attribution } : {}),
|
|
34
33
|
};
|
|
35
34
|
},
|
|
@@ -82,7 +82,8 @@ function ChangelogDetail({
|
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
/** The Changelog, as its own pane (CHG) and as ROADMAP's Changelog view. */
|
|
86
|
+
export function ChangelogPane({ focused, width, height }: PaneProps) {
|
|
86
87
|
const paneInstance = usePaneInstance();
|
|
87
88
|
const requestedVersion = paneInstance?.params?.version ?? null;
|
|
88
89
|
const [releases, setReleases] = useState<ChangelogRelease[]>(() => releaseCache.peek(RELEASES, { stale: true }) ?? []);
|
|
@@ -289,7 +290,7 @@ export const changelogModule: PluginModule = {
|
|
|
289
290
|
name: "Changelog",
|
|
290
291
|
icon: "L",
|
|
291
292
|
component: ChangelogPane,
|
|
292
|
-
reportFreshness: { status: "not-a-feed", basis: "release notes" },
|
|
293
|
+
reportFreshness: { status: "not-a-feed", basis: "release notes", local: true },
|
|
293
294
|
defaultPosition: "right",
|
|
294
295
|
defaultMode: "floating",
|
|
295
296
|
defaultFloatingSize: { width: 100, height: 32 },
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "../../../time-series/spec";
|
|
12
12
|
import type { ChartSeriesSpec, ChartSpec, SeriesPeriod } from "../../../time-series/types";
|
|
13
13
|
import { yearToDateBaseDay } from "../../../market-data/market/year-to-date";
|
|
14
|
+
import { readPriceWindow } from "../../../utils/price-window";
|
|
14
15
|
|
|
15
16
|
const PRICE_CAPABILITIES = new Set(["price-chart", "intraday-price-chart", "price-comparison"]);
|
|
16
17
|
|
|
@@ -35,6 +36,13 @@ export function yearToDateWindow(spec: ChartSpec, now = Date.now()): { start: st
|
|
|
35
36
|
return { start: starts.sort()[0] ?? `${Number(today.slice(0, 4)) - 1}-12-31`, end: today };
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
/** The shortest range whose history reaches back to `from`: 5Y for a window from 2022, ALL for one from 2019. */
|
|
40
|
+
function rangeCovering(from: string, now = Date.now()): TimeRange {
|
|
41
|
+
const days = (now - Date.parse(`${from}T00:00:00Z`)) / 86_400_000;
|
|
42
|
+
const covering: Array<[TimeRange, number]> = [["1M", 31], ["3M", 92], ["6M", 183], ["1Y", 366], ["5Y", 5 * 366]];
|
|
43
|
+
return covering.find(([, span]) => days <= span)?.[0] ?? "ALL";
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
function chartResolution(options: HeadlessPaneOptionValues): ChartResolution | null {
|
|
39
47
|
return parseChartResolution(options.chartResolution ?? options.resolution);
|
|
40
48
|
}
|
|
@@ -77,6 +85,14 @@ export function applyChartComposerCapabilityOptions(
|
|
|
77
85
|
};
|
|
78
86
|
}
|
|
79
87
|
|
|
88
|
+
// --from and --to: those dates, each leg rebased at its first close in them, as YTD is from the prior year's last.
|
|
89
|
+
const window = capabilityId === "price-comparison"
|
|
90
|
+
? readPriceWindow(typeof options.from === "string" ? options.from : undefined, typeof options.to === "string" ? options.to : undefined)
|
|
91
|
+
: null;
|
|
92
|
+
if (window) {
|
|
93
|
+
next = { ...next, viewport: { ...next.viewport, range: rangeCovering(window.from), dateWindow: { start: window.from, end: window.to } } };
|
|
94
|
+
}
|
|
95
|
+
|
|
80
96
|
if (capabilityId === "price-chart" && options.extendedHours === true) {
|
|
81
97
|
next = { ...next, viewport: { ...next.viewport, extendedHours: true } };
|
|
82
98
|
}
|