@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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { setStallAttentionProbe, type StallAttention } from "../../../../telemetry/stall-reports";
|
|
2
|
+
|
|
3
|
+
/** What this needs of a window: its id, its minimized state and its events. */
|
|
4
|
+
interface AttentionWindow {
|
|
5
|
+
id: number;
|
|
6
|
+
isMinimized(): boolean;
|
|
7
|
+
on?(name: string, handler: (event: unknown) => void): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const openWindows = new Map<number, AttentionWindow>();
|
|
11
|
+
let focusedWindowId: number | null = null;
|
|
12
|
+
let sawFocusChange = false;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Whether anyone was looking when the desktop app's Bun process froze. The
|
|
16
|
+
* system slows a hidden app's timers, which reads like a freeze, so a stall
|
|
17
|
+
* report from this process says whether one of the app's windows had the
|
|
18
|
+
* keyboard (the app was in front) and whether one was open and not
|
|
19
|
+
* minimized. Read only for a stall about to be reported.
|
|
20
|
+
*/
|
|
21
|
+
function desktopAttention(): StallAttention {
|
|
22
|
+
let visible: boolean | null = false;
|
|
23
|
+
try {
|
|
24
|
+
for (const window of openWindows.values()) {
|
|
25
|
+
if (!window.isMinimized()) {
|
|
26
|
+
visible = true;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
visible = null;
|
|
32
|
+
}
|
|
33
|
+
return { active: sawFocusChange ? focusedWindowId !== null : null, visible };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Follows a window the app opened until it closes; the first one installs the probe. */
|
|
37
|
+
export function trackWindowAttention(window: AttentionWindow): void {
|
|
38
|
+
if (openWindows.size === 0) setStallAttentionProbe(desktopAttention);
|
|
39
|
+
openWindows.set(window.id, window);
|
|
40
|
+
window.on?.("focus", () => {
|
|
41
|
+
focusedWindowId = window.id;
|
|
42
|
+
sawFocusChange = true;
|
|
43
|
+
});
|
|
44
|
+
window.on?.("blur", () => {
|
|
45
|
+
// Focus moving between two of the app's windows can report the new
|
|
46
|
+
// window's focus before the old one's blur.
|
|
47
|
+
if (focusedWindowId === window.id) focusedWindowId = null;
|
|
48
|
+
sawFocusChange = true;
|
|
49
|
+
});
|
|
50
|
+
window.on?.("close", () => {
|
|
51
|
+
openWindows.delete(window.id);
|
|
52
|
+
if (focusedWindowId === window.id) focusedWindowId = null;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { BrowserWindow } from "electrobun/bun";
|
|
2
|
+
import { measurePerf } from "../../../../utils/perf-marks";
|
|
3
|
+
import { trackWindowAttention } from "./attention";
|
|
2
4
|
import {
|
|
3
5
|
applyNativeWindowFrame,
|
|
4
6
|
applyWindowMoveEvent,
|
|
@@ -83,7 +85,8 @@ interface AppWindowOptions {
|
|
|
83
85
|
* its cached frame current as it moves, and holds it to `minSize` on resize.
|
|
84
86
|
*/
|
|
85
87
|
export function createAppWindow({ title, frame, rpc, minSize, onFrameChange, preload }: AppWindowOptions): BrowserWindow {
|
|
86
|
-
|
|
88
|
+
// Opening a window waits on the native layer, the webview's creation included.
|
|
89
|
+
const window = measurePerf("native.window.create", () => new BrowserWindow({
|
|
87
90
|
title,
|
|
88
91
|
frame,
|
|
89
92
|
url: APP_VIEW_URL,
|
|
@@ -94,7 +97,7 @@ export function createAppWindow({ title, frame, rpc, minSize, onFrameChange, pre
|
|
|
94
97
|
titleBarStyle: desktopTitleBarStyle(),
|
|
95
98
|
navigationRules: JSON.stringify(["views://*"]),
|
|
96
99
|
sandbox: false,
|
|
97
|
-
});
|
|
100
|
+
}));
|
|
98
101
|
applyWindowsWindowIcon(title);
|
|
99
102
|
applyWindowsCustomChrome(title);
|
|
100
103
|
if (process.platform === "win32") {
|
|
@@ -105,6 +108,7 @@ export function createAppWindow({ title, frame, rpc, minSize, onFrameChange, pre
|
|
|
105
108
|
});
|
|
106
109
|
}
|
|
107
110
|
updateWindowFrameCache(window, frame, minSize);
|
|
111
|
+
trackWindowAttention(window);
|
|
108
112
|
const readsNativeFrame = process.platform === "win32";
|
|
109
113
|
(window as any).on?.("move", (event: WindowMoveEvent) => {
|
|
110
114
|
if (readsNativeFrame) {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { BrowserView, BrowserWindow } from "electrobun/bun";
|
|
2
|
+
import { measurePerf } from "../../../../utils/perf-marks";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Electrobun's window and webview calls that go to the native layer. On
|
|
6
|
+
* Windows each one is answered on the thread that runs the windows, and this
|
|
7
|
+
* process waits for it: while that thread is busy, so is this one.
|
|
8
|
+
*/
|
|
9
|
+
const WINDOW_CALLS = [
|
|
10
|
+
"activate", "close", "show", "showInactive", "hide", "minimize", "unminimize", "maximize", "unmaximize",
|
|
11
|
+
"isMinimized", "isMaximized", "isFullScreen", "setFullScreen", "isAlwaysOnTop", "setAlwaysOnTop",
|
|
12
|
+
"setTitle", "setPosition", "setSize", "setFrame", "getFrame",
|
|
13
|
+
];
|
|
14
|
+
const VIEW_CALLS = [
|
|
15
|
+
"executeJavascript", "loadURL", "loadHTML", "setNavigationRules", "openDevTools", "closeDevTools", "toggleDevTools",
|
|
16
|
+
"setPageZoom",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
let measured = false;
|
|
20
|
+
|
|
21
|
+
/** `getFrame` as a stall report keeps it: `get-frame`. */
|
|
22
|
+
function sectionPart(name: string): string {
|
|
23
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function measureCalls(prototype: object, prefix: string, names: readonly string[]): void {
|
|
27
|
+
const methods = prototype as Record<string, unknown>;
|
|
28
|
+
for (const name of names) {
|
|
29
|
+
const original = methods[name];
|
|
30
|
+
if (typeof original !== "function") continue;
|
|
31
|
+
const section = `${prefix}.${sectionPart(name)}`;
|
|
32
|
+
methods[name] = function measuredNativeCall(this: unknown, ...args: unknown[]) {
|
|
33
|
+
return measurePerf(section, () => original.apply(this, args));
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Names each native window and webview call (`native.window.get-frame`,
|
|
40
|
+
* `native.view.execute-javascript`), so a stall inside or right after one
|
|
41
|
+
* says so instead of naming whatever was measured last. Once per process.
|
|
42
|
+
*/
|
|
43
|
+
export function measureNativeWindowCalls(): void {
|
|
44
|
+
if (measured) return;
|
|
45
|
+
measured = true;
|
|
46
|
+
measureCalls(BrowserWindow.prototype, "native.window", WINDOW_CALLS);
|
|
47
|
+
measureCalls(BrowserView.prototype, "native.view", VIEW_CALLS);
|
|
48
|
+
}
|
|
@@ -16,8 +16,12 @@ import { JsonTickerRepository } from "../../../data/json-ticker-repository";
|
|
|
16
16
|
import type { DesktopPaneShotPayload } from "../../../cli/desktop-pane-shot";
|
|
17
17
|
import {
|
|
18
18
|
SHOT_API_PROXY_PREFIX,
|
|
19
|
+
SHOT_API_SOCKET_PATH,
|
|
19
20
|
SHOT_HTTP_BRIDGE_PATH,
|
|
20
21
|
SHOT_MARKET_BRIDGE_PATH,
|
|
22
|
+
shotRouteUrl,
|
|
23
|
+
type ShotApiSocketCall,
|
|
24
|
+
type ShotApiSocketReply,
|
|
21
25
|
} from "../../../cli/desktop-pane-shot-routes";
|
|
22
26
|
import { instrumentFromTicker } from "../../../market-data/request-types";
|
|
23
27
|
import type { RendererHost } from "../../../ui/host";
|
|
@@ -95,6 +99,12 @@ const TRACKED_RESPONSE_METHODS = new Set<PropertyKey>([
|
|
|
95
99
|
"text",
|
|
96
100
|
]);
|
|
97
101
|
|
|
102
|
+
/**
|
|
103
|
+
* The page's own address, read once before anything can change it: the
|
|
104
|
+
* server answers only inside the secret folder the page opened at.
|
|
105
|
+
*/
|
|
106
|
+
const SHOT_PAGE_URL = window.location.href;
|
|
107
|
+
|
|
98
108
|
let pendingShotWork = 0;
|
|
99
109
|
let didInstallShotFetchTracker = false;
|
|
100
110
|
const rendererHost: RendererHost = {
|
|
@@ -142,22 +152,90 @@ function installShotFetchTracker(): void {
|
|
|
142
152
|
)) as typeof fetch;
|
|
143
153
|
}
|
|
144
154
|
|
|
155
|
+
/** Sends a read over the socket; null once the socket has closed. */
|
|
156
|
+
type ShotApiSocketSend = (call: Omit<ShotApiSocketCall, "id">, signal: AbortSignal | null | undefined) => Promise<Response> | null;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Cloud API reads over one socket to the Bun server, answered as each lands.
|
|
160
|
+
* A page's fetches share six connections to its origin, so a board's seventy
|
|
161
|
+
* contract histories and terms waited on each other for seconds. Null when
|
|
162
|
+
* the socket does not open; the page's fetch carries the reads then, and
|
|
163
|
+
* once the socket closes.
|
|
164
|
+
*/
|
|
165
|
+
function openShotApiSocket(): Promise<ShotApiSocketSend | null> {
|
|
166
|
+
return new Promise((resolve) => {
|
|
167
|
+
const pending = new Map<number, (reply: ShotApiSocketReply | null) => void>();
|
|
168
|
+
let nextId = 1;
|
|
169
|
+
let open = false;
|
|
170
|
+
const url = shotRouteUrl(SHOT_API_SOCKET_PATH, SHOT_PAGE_URL);
|
|
171
|
+
url.protocol = "ws:";
|
|
172
|
+
const socket = new WebSocket(url);
|
|
173
|
+
socket.addEventListener("message", (event) => {
|
|
174
|
+
let reply: ShotApiSocketReply;
|
|
175
|
+
try {
|
|
176
|
+
reply = JSON.parse(String(event.data)) as ShotApiSocketReply;
|
|
177
|
+
} catch {
|
|
178
|
+
// Intentional ignore: every reply the server sends is JSON; anything else answers no call.
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
pending.get(reply.id)?.(reply);
|
|
182
|
+
pending.delete(reply.id);
|
|
183
|
+
});
|
|
184
|
+
socket.addEventListener("close", () => {
|
|
185
|
+
open = false;
|
|
186
|
+
for (const settle of pending.values()) settle(null);
|
|
187
|
+
pending.clear();
|
|
188
|
+
resolve(null);
|
|
189
|
+
});
|
|
190
|
+
socket.addEventListener("open", () => {
|
|
191
|
+
open = true;
|
|
192
|
+
resolve((call, signal) => !open ? null : new Promise<Response>((answer, fail) => {
|
|
193
|
+
const aborted = () => fail(signal?.reason ?? new DOMException("The operation was aborted.", "AbortError"));
|
|
194
|
+
if (signal?.aborted) return aborted();
|
|
195
|
+
const id = nextId++;
|
|
196
|
+
const onAbort = () => {
|
|
197
|
+
pending.delete(id);
|
|
198
|
+
aborted();
|
|
199
|
+
};
|
|
200
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
201
|
+
pending.set(id, (reply) => {
|
|
202
|
+
signal?.removeEventListener("abort", onAbort);
|
|
203
|
+
// A socket that closed mid-read is a request that never got an answer, as a dropped connection is to fetch.
|
|
204
|
+
if (!reply) return fail(new TypeError("Failed to fetch"));
|
|
205
|
+
if ("error" in reply) return fail(new TypeError(reply.error));
|
|
206
|
+
const bytes = Uint8Array.from(atob(reply.body), (char) => char.charCodeAt(0));
|
|
207
|
+
const empty = bytes.length === 0 || reply.status === 204 || reply.status === 304;
|
|
208
|
+
answer(new Response(empty ? null : bytes, { status: reply.status, statusText: reply.statusText, headers: reply.headers }));
|
|
209
|
+
});
|
|
210
|
+
socket.send(JSON.stringify({ id, ...call } satisfies ShotApiSocketCall));
|
|
211
|
+
}));
|
|
212
|
+
}, { once: true });
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
145
216
|
function installShotCloudApiTransport(): void {
|
|
146
217
|
apiClient.setCookieSessionMode(true);
|
|
218
|
+
let socket: Promise<ShotApiSocketSend | null> | null = null;
|
|
147
219
|
setCloudApiFetchTransport((url, init = {}) => {
|
|
148
220
|
const upstream = new URL(url);
|
|
149
|
-
const proxyUrl =
|
|
221
|
+
const proxyUrl = shotRouteUrl(`${SHOT_API_PROXY_PREFIX}${upstream.pathname}`, SHOT_PAGE_URL);
|
|
150
222
|
proxyUrl.search = upstream.search;
|
|
151
223
|
const headers = new Headers(init.headers);
|
|
152
224
|
// Bun owns the real session cookie. The browser only talks to the local
|
|
153
225
|
// same-origin proxy and never receives or serializes the credential.
|
|
154
226
|
headers.delete("Cookie");
|
|
155
227
|
headers.delete("Origin");
|
|
156
|
-
|
|
228
|
+
const viaFetch = () => window.fetch(proxyUrl, {
|
|
157
229
|
...init,
|
|
158
230
|
headers,
|
|
159
231
|
credentials: "same-origin",
|
|
160
232
|
});
|
|
233
|
+
if ((init.method ?? "GET").toUpperCase() !== "GET" || init.body != null) return viaFetch();
|
|
234
|
+
return trackShotWork((async () => {
|
|
235
|
+
socket ??= openShotApiSocket();
|
|
236
|
+
const send = await socket;
|
|
237
|
+
return await send?.({ path: `${upstream.pathname}${upstream.search}`, headers: [...headers] }, init.signal) ?? await viaFetch();
|
|
238
|
+
})());
|
|
161
239
|
});
|
|
162
240
|
}
|
|
163
241
|
|
|
@@ -168,7 +246,7 @@ function installShotCloudApiTransport(): void {
|
|
|
168
246
|
*/
|
|
169
247
|
function installShotHttpFetchTransport(): void {
|
|
170
248
|
setHttpFetchTransport(async (url, init) => {
|
|
171
|
-
const response = await window.fetch(SHOT_HTTP_BRIDGE_PATH, {
|
|
249
|
+
const response = await window.fetch(shotRouteUrl(SHOT_HTTP_BRIDGE_PATH, SHOT_PAGE_URL), {
|
|
172
250
|
method: "POST",
|
|
173
251
|
headers: { "Content-Type": "application/json" },
|
|
174
252
|
body: JSON.stringify(await toRequestEnvelope(url, init)),
|
|
@@ -190,7 +268,7 @@ async function restoreShotCloudSession(): Promise<void> {
|
|
|
190
268
|
* of the corporate action history come from providers the router merges in.
|
|
191
269
|
*/
|
|
192
270
|
async function requestShotMarketData<T>(operation: string, args: unknown[], signal?: AbortSignal): Promise<T> {
|
|
193
|
-
const response = await window.fetch(SHOT_MARKET_BRIDGE_PATH, {
|
|
271
|
+
const response = await window.fetch(shotRouteUrl(SHOT_MARKET_BRIDGE_PATH, SHOT_PAGE_URL), {
|
|
194
272
|
method: "POST",
|
|
195
273
|
headers: { "Content-Type": "application/json" },
|
|
196
274
|
body: JSON.stringify({ operation, args }),
|
|
@@ -14,7 +14,9 @@ declare global {
|
|
|
14
14
|
* Set by a capture that carries a status line: shows the first of the
|
|
15
15
|
* variants (longest first) that fits the footer, cut in the middle when
|
|
16
16
|
* none does, and resolves to the text shown. `dates` says what the line
|
|
17
|
-
* dates, so the pane's own text that says the same goes.
|
|
17
|
+
* dates, so the pane's own text that says the same goes. With no variants
|
|
18
|
+
* the pane has nothing to date: the footer row is given back to the body
|
|
19
|
+
* and the text shown is empty.
|
|
18
20
|
*/
|
|
19
21
|
__GLOOM_CLI_SHOT_FIT_STATUS__?: (variants: string[], options?: { dates?: ShotLineDates }) => Promise<string>;
|
|
20
22
|
}
|
|
@@ -90,22 +92,28 @@ function footerOverflows(): boolean {
|
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
interface ShotStatusLineState {
|
|
93
|
-
|
|
95
|
+
/** Null once the capture has no line to show: the footer row is gone. */
|
|
96
|
+
text: string | null;
|
|
94
97
|
dates: ShotLineDates;
|
|
95
98
|
/** No variant fit beside the pane's own `as of`, so it gave way. */
|
|
96
99
|
crowded: boolean;
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
function useShotStatusLine(enabled: boolean, hasAsOf: () => boolean): ShotStatusLineState {
|
|
100
|
-
const [text, setText] = useState(PENDING_STATUS);
|
|
103
|
+
const [text, setText] = useState<string | null>(PENDING_STATUS);
|
|
101
104
|
const [dates, setDates] = useState<ShotLineDates>(null);
|
|
102
105
|
const [crowded, setCrowded] = useState(false);
|
|
103
106
|
useEffect(() => {
|
|
104
107
|
if (!enabled) return;
|
|
105
108
|
// What the line says goes before it is fitted, so the line gets its room.
|
|
106
|
-
window.__GLOOM_CLI_SHOT_FIT_STATUS__ = (variants, options) => {
|
|
109
|
+
window.__GLOOM_CLI_SHOT_FIT_STATUS__ = async (variants, options) => {
|
|
107
110
|
setDates(options?.dates ?? null);
|
|
108
111
|
setCrowded(false);
|
|
112
|
+
if (variants.length === 0) {
|
|
113
|
+
setText(null);
|
|
114
|
+
await nextFrames(2);
|
|
115
|
+
return "";
|
|
116
|
+
}
|
|
109
117
|
const show = async (next: string) => {
|
|
110
118
|
setText(next);
|
|
111
119
|
await nextFrames(2);
|
|
@@ -148,7 +156,7 @@ export function PaneShotFrame({ paneId, title, width, height, preserveStatus = f
|
|
|
148
156
|
const { text: statusText, dates, crowded } = useShotStatusLine(statusLine, hasAsOf);
|
|
149
157
|
const datesData = statusLine && dates === "data";
|
|
150
158
|
return <PaneFooterProvider>{(registeredFooter) => {
|
|
151
|
-
const status: PaneFooterSegment[] = statusLine ? [{ id: "shot-status", parts: [{ text: statusText, tone: "label" }] }] : [];
|
|
159
|
+
const status: PaneFooterSegment[] = statusLine && statusText !== null ? [{ id: "shot-status", parts: [{ text: statusText, tone: "label" }] }] : [];
|
|
152
160
|
const info = registeredFooter.info.filter((segment) => (preserveStatus || segment.icon === "warning" || segment.capture)
|
|
153
161
|
&& !(datesPaneData(segment) && (datesData || crowded))
|
|
154
162
|
&& !(segment.quoteStatus && statusLine && dates !== null));
|
|
@@ -36,6 +36,9 @@ export function detachedSnapshotKey(snapshot: DesktopSharedStateSnapshot, paneId
|
|
|
36
36
|
pluginConfig: snapshot.config.pluginConfig,
|
|
37
37
|
disabledPlugins: snapshot.config.disabledPlugins,
|
|
38
38
|
chartPreferences: snapshot.config.chartPreferences,
|
|
39
|
+
// A popped-out window collects Attention Counts on its own, so turning
|
|
40
|
+
// them off in the main window has to reach it.
|
|
41
|
+
telemetry: snapshot.config.telemetry ?? null,
|
|
39
42
|
instances: paneStateIds.map((id) => findPaneInstance(snapshot.config.layout, id) ?? null),
|
|
40
43
|
paneState: Object.fromEntries(paneStateIds.map((id) => [id, snapshot.paneState[id] ?? {}])),
|
|
41
44
|
});
|
|
@@ -53,7 +53,7 @@ import { crashReportsEnabled, installCrashReporter, reportCrash } from "../../..
|
|
|
53
53
|
import { browserDoNotTrack, installWindowCrashListeners } from "../../../telemetry/crash-reports-dom";
|
|
54
54
|
import { forwardAttendedStalls } from "../../../telemetry/stall-reports-dom";
|
|
55
55
|
import { currentTelemetryConfig } from "../../../telemetry/live-config";
|
|
56
|
-
import {
|
|
56
|
+
import { installAttentionCounter } from "../../../telemetry/attention-counts";
|
|
57
57
|
import { installUsageCounter, usageCountsEnabled } from "../../../telemetry/usage-counts";
|
|
58
58
|
import { installWindowUsageFlush } from "../../../telemetry/usage-counts-dom";
|
|
59
59
|
import { cloudSyncController } from "../../../sync/controller";
|
|
@@ -160,7 +160,12 @@ async function boot() {
|
|
|
160
160
|
getInstallId: () => init.telemetry.installId,
|
|
161
161
|
officialPluginIds: loadOfficialPluginIds,
|
|
162
162
|
});
|
|
163
|
-
installAttentionCounter(
|
|
163
|
+
installAttentionCounter({
|
|
164
|
+
allowed: () => !init.telemetry.optedOut && !browserDoNotTrack(),
|
|
165
|
+
launchConfig: () => init.config,
|
|
166
|
+
// A popped-out window does not sync: the main window does, and sends it the settings it pulled.
|
|
167
|
+
...(init.windowKind === "detached" ? { accountSettingsPulled: () => true } : {}),
|
|
168
|
+
});
|
|
164
169
|
// Desktop has no Debug Log: this window's freezes go to the Bun process, which keeps the stall log.
|
|
165
170
|
setStallSink(forwardAttendedStalls(sendElectrobunStall).sink);
|
|
166
171
|
installElectrobunCapabilityStreamClient();
|
|
@@ -31,6 +31,7 @@ type PayloadBuilder = (...args: unknown[]) => Record<string, unknown>;
|
|
|
31
31
|
const assetDataPayloads: Record<string, PayloadBuilder> = {
|
|
32
32
|
canProvide: (ticker, exchange, context) => ({ ticker, exchange, context }),
|
|
33
33
|
primaryMarketSourceName: () => ({}),
|
|
34
|
+
batchesReads: (method) => ({ method }),
|
|
34
35
|
getCachedFinancialsForTargets: (targets, options) => ({ targets, options }),
|
|
35
36
|
// An abort signal does not cross the bridge; the board stops waiting instead.
|
|
36
37
|
getQuotesBatch: (targets, options) => {
|
|
@@ -25,9 +25,16 @@ export const webRendererHost: RendererHost = {
|
|
|
25
25
|
void backendRequest("host.exit").catch(() => window.close());
|
|
26
26
|
});
|
|
27
27
|
},
|
|
28
|
-
startWindowDrag() {
|
|
29
|
-
// Windows moves the window itself, as it does from a native title bar
|
|
28
|
+
startWindowDrag(press) {
|
|
29
|
+
// Windows moves the window itself, as it does from a native title bar, and
|
|
30
|
+
// a double-click there maximizes or restores it.
|
|
30
31
|
if (detectPlatform() === "win32") {
|
|
32
|
+
if (press?.detail === 2) {
|
|
33
|
+
void backendRequest("host.windowControl", { action: "toggle-maximize" }).catch((error: unknown) => {
|
|
34
|
+
console.error("[desktop] could not maximize the window", error instanceof Error ? error.message : String(error));
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
31
38
|
void backendRequest("host.startWindowDrag").catch((error: unknown) => {
|
|
32
39
|
console.error("[desktop] could not start moving the window", error instanceof Error ? error.message : String(error));
|
|
33
40
|
});
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* nothing opened and can show the link instead.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { systemOpenCommand } from "../../utils/external-url";
|
|
10
|
+
|
|
9
11
|
type Env = Readonly<Record<string, string | undefined>>;
|
|
10
12
|
|
|
11
13
|
function present(value: string | undefined): boolean {
|
|
@@ -35,27 +37,31 @@ export function browserOpenCommands(url: string, env: Env, platform: NodeJS.Plat
|
|
|
35
37
|
const forwarded = present(env.BROWSER) ? envBrowserCommand(env.BROWSER!, url) : null;
|
|
36
38
|
|
|
37
39
|
if (platform === "darwin") {
|
|
38
|
-
|
|
40
|
+
const opener = systemOpenCommand(url, platform);
|
|
41
|
+
if (!remote) return opener ? [opener] : [];
|
|
39
42
|
return forwarded ? [forwarded] : [];
|
|
40
43
|
}
|
|
41
44
|
if (platform === "win32") {
|
|
42
|
-
|
|
45
|
+
const opener = systemOpenCommand(url, platform);
|
|
46
|
+
if (!remote) return opener ? [opener] : [];
|
|
43
47
|
return forwarded ? [forwarded] : [];
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
// Linux and the other unixes. WSL has no Linux browser, but Windows can
|
|
47
51
|
// open the link: `wslview` when wslu is installed, else Windows' own handler.
|
|
48
52
|
if (present(env.WSL_DISTRO_NAME) || present(env.WSL_INTEROP)) {
|
|
53
|
+
const windowsOpener = systemOpenCommand(url, "win32");
|
|
49
54
|
return [
|
|
50
55
|
...(forwarded ? [forwarded] : []),
|
|
51
56
|
["wslview", url],
|
|
52
|
-
[
|
|
57
|
+
...(windowsOpener ? [windowsOpener] : []),
|
|
53
58
|
];
|
|
54
59
|
}
|
|
55
60
|
const display = present(env.DISPLAY) || present(env.WAYLAND_DISPLAY);
|
|
56
61
|
// xdg-open reads $BROWSER itself, so a forwarding helper still applies.
|
|
57
62
|
if (remote && !display && !forwarded) return [];
|
|
58
|
-
|
|
63
|
+
const opener = systemOpenCommand(url, platform);
|
|
64
|
+
return opener ? [opener] : [];
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
/** How long an opener may run before it counts as having opened the browser. */
|
|
@@ -25,7 +25,7 @@ function resolveInsets(props: Record<string, unknown>): CellInsets {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export const OpenTuiImageSurface = forwardRef<unknown, ImageSurfaceProps>(function OpenTuiImageSurface(
|
|
28
|
-
{ children, src, alt: _alt, objectFit = "contain", ...props },
|
|
28
|
+
{ children, src, alt: _alt, objectFit = "contain", fallbackWhileLoading: _fallbackWhileLoading, imageBackground: _imageBackground, ...props },
|
|
29
29
|
forwardedRef,
|
|
30
30
|
) {
|
|
31
31
|
const renderer = useNativeRenderer();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { hostname } from "os";
|
|
2
2
|
import { getGloomberbDirs } from "../../data/config/home";
|
|
3
|
-
import { existsSync
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { makePrivateDirSync } from "../../utils/private-files";
|
|
4
5
|
import { App } from "../../app";
|
|
5
6
|
import { getDataDir, initDataDir, loadConfig, setConfigStoreHost } from "../../data/config/store";
|
|
6
7
|
import { applyLanguageFromConfig } from "../../i18n";
|
|
@@ -28,7 +29,7 @@ import { startRemoteControlServer, type RemoteControlServer } from "../../remote
|
|
|
28
29
|
import { createAppServices } from "../../core/app-services";
|
|
29
30
|
import { flushPendingPersistence } from "../../state/persist-scheduler";
|
|
30
31
|
import { loadOfficialPluginIds } from "../../plugins/marketplace/feed";
|
|
31
|
-
import { flushCrashReports, installCrashReporter } from "../../telemetry/crash-reports";
|
|
32
|
+
import { flushCrashReports, installCrashReporter, telemetryOptedOut } from "../../telemetry/crash-reports";
|
|
32
33
|
import {
|
|
33
34
|
CRASH_REPORT_EXIT_FLUSH_MS,
|
|
34
35
|
createNodeCrashReporterHost,
|
|
@@ -38,13 +39,14 @@ import {
|
|
|
38
39
|
} from "../../telemetry/crash-reports-node";
|
|
39
40
|
import { currentTelemetryConfig } from "../../telemetry/live-config";
|
|
40
41
|
import { installUpdateFailureReporter } from "../../telemetry/update-failures";
|
|
42
|
+
import { createStallClocks } from "../../telemetry/stall-clocks";
|
|
41
43
|
import { installStallReporter, recordStall } from "../../telemetry/stall-reports";
|
|
42
44
|
import { createNodeStallReporterHost } from "../../telemetry/stall-reports-node";
|
|
43
|
-
import { setStallSink } from "../../utils/main-thread-stall";
|
|
45
|
+
import { setStallClocks, setStallSink } from "../../utils/main-thread-stall";
|
|
44
46
|
import { createNodeUpdateFailureHost } from "../../telemetry/update-failures-node";
|
|
45
47
|
import { terminalInstallKind } from "../../updater";
|
|
46
48
|
import { flushUsageCounts, installUsageCounter, usageCountsEnabled } from "../../telemetry/usage-counts";
|
|
47
|
-
import {
|
|
49
|
+
import { installAttentionCounter } from "../../telemetry/attention-counts";
|
|
48
50
|
import { starPromptEnvironmentAllows } from "../../app/star-prompt/model";
|
|
49
51
|
import { allowStarPrompt } from "../../app/star-prompt/runtime";
|
|
50
52
|
|
|
@@ -114,7 +116,7 @@ export async function startOpenTuiApp({ externalPlugins, cliLaunchRequest }: Sta
|
|
|
114
116
|
const dataDir = await getDataDir() ?? getGloomberbDirs().data;
|
|
115
117
|
|
|
116
118
|
if (!existsSync(dataDir)) {
|
|
117
|
-
|
|
119
|
+
makePrivateDirSync(dataDir);
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
const config = await measurePerfAsync("startup.opentui.init-data-dir", () => initDataDir(dataDir));
|
|
@@ -129,6 +131,7 @@ export async function startOpenTuiApp({ externalPlugins, cliLaunchRequest }: Sta
|
|
|
129
131
|
}));
|
|
130
132
|
installStallReporter(createNodeStallReporterHost({ getDataDir: () => config.dataDir }));
|
|
131
133
|
setStallSink((event) => recordStall("terminal", event));
|
|
134
|
+
setStallClocks(createStallClocks());
|
|
132
135
|
installUsageCounter({
|
|
133
136
|
surface: "terminal",
|
|
134
137
|
os: describeNodeOs(),
|
|
@@ -136,7 +139,10 @@ export async function startOpenTuiApp({ externalPlugins, cliLaunchRequest }: Sta
|
|
|
136
139
|
getInstallId: () => readOrCreateInstallId(config.dataDir),
|
|
137
140
|
officialPluginIds: loadOfficialPluginIds,
|
|
138
141
|
});
|
|
139
|
-
const stopAttention = installAttentionCounter(
|
|
142
|
+
const stopAttention = installAttentionCounter({
|
|
143
|
+
allowed: () => !telemetryOptedOut(process.env),
|
|
144
|
+
launchConfig: () => config,
|
|
145
|
+
});
|
|
140
146
|
allowStarPrompt(starPromptEnvironmentAllows({
|
|
141
147
|
env: process.env,
|
|
142
148
|
stdinIsTTY: process.stdin.isTTY,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useTerminalDimensions } from "@opentui/react";
|
|
3
3
|
import { useSyncExternalStore } from "react";
|
|
4
4
|
import { colors } from "../../theme/colors";
|
|
5
|
-
import { createToastStore, type ToastHost, type ToastTone } from "../../ui/toast";
|
|
5
|
+
import { createToastStore, useToastsShown, type ToastHost, type ToastTone } from "../../ui/toast";
|
|
6
6
|
import { useActionShortcut } from "../../ui";
|
|
7
7
|
import { displayWidth } from "../../utils/format";
|
|
8
8
|
|
|
@@ -26,6 +26,7 @@ function toneIcon(tone: ToastTone): string {
|
|
|
26
26
|
function ToastViewport({ position = "bottom-right" }: { position?: string }) {
|
|
27
27
|
const dimensions = useTerminalDimensions();
|
|
28
28
|
const shownToasts = useSyncExternalStore(toastStore.subscribe, toastStore.getSnapshot, toastStore.getSnapshot);
|
|
29
|
+
useToastsShown(shownToasts);
|
|
29
30
|
const actionShortcut = useActionShortcut("notification-action");
|
|
30
31
|
const maxWidth = Math.max(1, Math.min(60, dimensions.width - 4));
|
|
31
32
|
const placement = position.startsWith("top") ? { top: 1 } : { bottom: 1 };
|
package/src/series/registry.ts
CHANGED
|
@@ -23,6 +23,24 @@ export function seriesExpression(definition: SeriesDefinition): string {
|
|
|
23
23
|
return definition.expression ?? loadedSeriesExpression(definition);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* The one catalog series a bare word is an alias of (`hashrate` is
|
|
28
|
+
* `bitcoin:hashrate`), for a chart typed with the word where a ticker goes.
|
|
29
|
+
* Only the words a series lists as its `aliases`, never any other id or
|
|
30
|
+
* name, and none that two series claim.
|
|
31
|
+
*/
|
|
32
|
+
export function seriesNamedBy(definitions: Iterable<SeriesDefinition>, word: string): SeriesDefinition | undefined {
|
|
33
|
+
const wanted = word.trim().toLowerCase();
|
|
34
|
+
if (!wanted) return undefined;
|
|
35
|
+
let found: SeriesDefinition | undefined;
|
|
36
|
+
for (const definition of definitions) {
|
|
37
|
+
if (definition.input || !definition.aliases?.includes(wanted)) continue;
|
|
38
|
+
if (found && found.id !== definition.id) return undefined;
|
|
39
|
+
found = definition;
|
|
40
|
+
}
|
|
41
|
+
return found;
|
|
42
|
+
}
|
|
43
|
+
|
|
26
44
|
/**
|
|
27
45
|
* The capability series id a loaded series charts under: its name, one of its
|
|
28
46
|
* lines after a tilde, and for a series with an input, the symbol after a
|
package/src/series/resolve.ts
CHANGED
|
@@ -195,7 +195,7 @@ export async function resolveLoadedSeries(
|
|
|
195
195
|
): Promise<ResolvedSeries> {
|
|
196
196
|
const ticker = request.ticker?.trim().toUpperCase() || undefined;
|
|
197
197
|
const line = definition.lines?.length ? request.line ?? definition.lines[0] : undefined;
|
|
198
|
-
const { points, edge, warning, attribution } = loadResult(await loadSeries(definition, {
|
|
198
|
+
const { points, edge, warning, savedAt, attribution } = loadResult(await loadSeries(definition, {
|
|
199
199
|
...seriesLoadWindow(request.viewport, now),
|
|
200
200
|
...(ticker ? { ticker } : {}),
|
|
201
201
|
...viewportBars(request.viewport, ticker),
|
|
@@ -221,6 +221,7 @@ export async function resolveLoadedSeries(
|
|
|
221
221
|
panelId: "main",
|
|
222
222
|
interpolation: style === "step" ? "step-after" : "none",
|
|
223
223
|
...(warning ? { warning } : {}),
|
|
224
|
+
...(savedAt != null && Number.isFinite(savedAt) ? { savedAt } : {}),
|
|
224
225
|
...(attribution ? { attribution: { ...attribution } } : {}),
|
|
225
226
|
points: normalizeSeriesPoints(points, edge),
|
|
226
227
|
};
|
package/src/series/standalone.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { CapabilityInvoker } from "../capabilities/types";
|
|
|
4
4
|
import type { GloomPlugin } from "../types/plugin";
|
|
5
5
|
import { resolveRegisteredSeries } from "./capability";
|
|
6
6
|
import { readSeriesFolder } from "./files";
|
|
7
|
-
import { SeriesRegistry, seriesExpression } from "./registry";
|
|
7
|
+
import { SeriesRegistry, seriesExpression, seriesNamedBy } from "./registry";
|
|
8
8
|
import type { SeriesDefinition } from "./types";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -75,3 +75,12 @@ export function standaloneSeriesCatalog(
|
|
|
75
75
|
return byExpression.get(expression);
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
|
+
|
|
79
|
+
/** The catalog series a bare word is an alias of (`hashrate`), where no plugin runtime runs; see `seriesNamedBy`. */
|
|
80
|
+
export function standaloneSeriesNamed(
|
|
81
|
+
plugins: readonly GloomPlugin[],
|
|
82
|
+
disabledPlugins: readonly string[] = [],
|
|
83
|
+
): (word: string) => SeriesDefinition | undefined {
|
|
84
|
+
const definitions = () => plugins.filter((plugin) => !disabledPlugins.includes(plugin.id)).flatMap((plugin) => plugin.series ?? []);
|
|
85
|
+
return (word) => seriesNamedBy(definitions(), word);
|
|
86
|
+
}
|
package/src/series/types.ts
CHANGED
|
@@ -92,6 +92,11 @@ export interface SeriesLoadResult {
|
|
|
92
92
|
points: readonly SeriesPoint[];
|
|
93
93
|
/** A note the chart shows above the plot, such as "cached data is stale". */
|
|
94
94
|
warning?: string;
|
|
95
|
+
/**
|
|
96
|
+
* When the copy the points came from was saved (ms), when its refresh failed
|
|
97
|
+
* and that copy answered: the chart's footer says "saved copy from …".
|
|
98
|
+
*/
|
|
99
|
+
savedAt?: number;
|
|
95
100
|
/** What a report of this series cites in place of Gloom Cloud. */
|
|
96
101
|
attribution?: SeriesAttribution;
|
|
97
102
|
}
|
|
@@ -123,6 +128,13 @@ interface SeriesDefinitionBase {
|
|
|
123
128
|
group?: string;
|
|
124
129
|
/** Extra words the catalog search matches, such as a ticker the series is often read against. */
|
|
125
130
|
tags?: readonly string[];
|
|
131
|
+
/**
|
|
132
|
+
* Words `fn G` charts the series by where a ticker goes (`hashrate`), each
|
|
133
|
+
* in lower case: only when no listing answers to the word, the quote source
|
|
134
|
+
* says it is not a ticker, and no other series has the word. A series with
|
|
135
|
+
* an `input` takes none.
|
|
136
|
+
*/
|
|
137
|
+
aliases?: readonly string[];
|
|
126
138
|
/** One sentence for the catalog search and for `gloomberb` automation. */
|
|
127
139
|
description?: string;
|
|
128
140
|
/**
|
package/src/series/validate.ts
CHANGED
|
@@ -23,11 +23,13 @@ const RATE_UNITS = ["%", "bp", "x", "ratio", "index", "score", "points"] as cons
|
|
|
23
23
|
const QUANTITY_UNITS = [
|
|
24
24
|
"number", "count", "units", "shares", "contracts", "days", "people", "jobs", "posts", "views", "hours",
|
|
25
25
|
"barrels", "barrels/day", "cubic feet", "MW", "MWh", "TWh", "GWh/day", "tons", "ounces",
|
|
26
|
+
// A hash rate: exahashes per second.
|
|
27
|
+
"EH/s",
|
|
26
28
|
// The instrument's own currency, for a field that applies to any ticker.
|
|
27
29
|
"currency", "currency/share",
|
|
28
30
|
] as const;
|
|
29
|
-
/** What a price is quoted per: `USD/barrel`, `USD/GPU-hour`. */
|
|
30
|
-
const PER_UNITS = ["share", "barrel", "gallon", "MMBtu", "MWh", "ounce", "ton", "tCO2", "hour", "GPU-hour"] as const;
|
|
31
|
+
/** What a price is quoted per: `USD/barrel`, `USD/GPU-hour`, a miner's `USD/PH/s/day`. */
|
|
32
|
+
const PER_UNITS = ["share", "barrel", "gallon", "MMBtu", "MWh", "ounce", "ton", "tCO2", "hour", "GPU-hour", "PH/s/day"] as const;
|
|
31
33
|
const SCALES = ["thousand", "million", "billion", "trillion"] as const;
|
|
32
34
|
|
|
33
35
|
const RATE_SET: ReadonlySet<string> = new Set(RATE_UNITS);
|
|
@@ -141,6 +143,13 @@ export function validateSeriesDefinition(definition: SeriesDefinition, context:
|
|
|
141
143
|
if (!Array.isArray(definition.tags) || definition.tags.length > 16) fail(id, "tags must be a list of at most 16 words.");
|
|
142
144
|
for (const tag of definition.tags) checkLabel(id, "tag", tag, 40, context, false);
|
|
143
145
|
}
|
|
146
|
+
if (definition.aliases !== undefined) {
|
|
147
|
+
if (!Array.isArray(definition.aliases) || definition.aliases.length > 4) fail(id, "aliases must be a list of at most 4 words.");
|
|
148
|
+
if (definition.input !== undefined) fail(id, "a series with an input takes no aliases: the word would stand for a ticker.");
|
|
149
|
+
for (const alias of definition.aliases) {
|
|
150
|
+
if (typeof alias !== "string" || !/^[a-z][a-z-]{3,31}$/.test(alias)) fail(id, `alias "${String(alias)}" must be one lower-case word of 4 to 32 letters.`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
144
153
|
if (definition.url !== undefined && (typeof definition.url !== "string" || !/^https?:\/\/\S+$/.test(definition.url))) {
|
|
145
154
|
fail(id, "url must be an http or https address.");
|
|
146
155
|
}
|