gloomberb 0.19.1 → 0.19.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/package.json +2 -1
- package/patches/electrobun@1.18.1.patch +108 -1
- package/src/account/upgrade-dialog.tsx +4 -23
- package/src/api-client/coalesce.ts +118 -0
- package/src/api-client/credit-boards.ts +13 -0
- package/src/api-client/crypto-etf-flows.ts +6 -0
- package/src/api-client/crypto-markets.ts +40 -0
- package/src/api-client/data.ts +201 -14
- package/src/api-client/equity-screener.ts +10 -0
- package/src/api-client/errors.ts +58 -0
- package/src/api-client/gpu.ts +29 -0
- package/src/api-client/index.ts +5 -2
- package/src/api-client/plan-access.ts +10 -1
- package/src/api-client/plan-rules.ts +35 -0
- package/src/api-client/research-activity.ts +1 -32
- package/src/api-client/types.ts +21 -1
- package/src/app/attention-notice.tsx +94 -0
- package/src/app/runtime/startup.ts +3 -1
- package/src/app.tsx +23 -1
- package/src/architecture/core-builtin-allowlist.json +2 -1
- package/src/capabilities/chart-series.ts +2 -1
- package/src/capabilities/factories.ts +1 -0
- package/src/cli/ascii-output.ts +42 -0
- package/src/cli/base-converter.ts +10 -2
- package/src/cli/command-options.ts +16 -1
- package/src/cli/commands/crypto-hints.ts +23 -4
- package/src/cli/commands/market.ts +101 -27
- package/src/cli/commands/news-search.ts +24 -5
- package/src/cli/commands/overview.ts +14 -10
- package/src/cli/commands/search.ts +84 -25
- package/src/cli/commands/system.ts +22 -30
- package/src/cli/commands/ticker.ts +49 -9
- package/src/cli/commands/version.ts +33 -0
- package/src/cli/context.ts +6 -9
- package/src/cli/data-dir.ts +7 -0
- package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
- package/src/cli/desktop-pane-shot-browser.ts +320 -0
- package/src/cli/desktop-pane-shot-routes.ts +26 -1
- package/src/cli/desktop-pane-shot.ts +196 -256
- package/src/cli/early-command.ts +42 -0
- package/src/cli/earnings-calendar.ts +9 -0
- package/src/cli/entry.ts +19 -13
- package/src/cli/errors.ts +12 -2
- package/src/cli/glossary.ts +2 -1
- package/src/cli/help.ts +1 -1
- package/src/cli/history-export.ts +22 -2
- package/src/cli/history-rows.ts +189 -11
- package/src/cli/index.ts +18 -1
- package/src/cli/known-gaps.ts +51 -5
- package/src/cli/listing-arg.ts +63 -10
- package/src/cli/options.ts +1 -1
- package/src/cli/output-file.ts +60 -13
- package/src/cli/pane-functions/catalog.ts +14 -3
- package/src/cli/pane-functions/data.ts +1 -1
- package/src/cli/pane-functions/dom.ts +45 -0
- package/src/cli/pane-functions/freshness.ts +46 -9
- package/src/cli/pane-functions/function-help.ts +17 -9
- package/src/cli/pane-functions/headless.ts +22 -14
- package/src/cli/pane-functions/index.ts +6 -2
- package/src/cli/pane-functions/options.ts +6 -2
- package/src/cli/pane-functions/resolver.ts +27 -5
- package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
- package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
- package/src/cli/pane-functions/screenshot-http.ts +70 -0
- package/src/cli/pane-functions/screenshot-render.ts +19 -5
- package/src/cli/quote-answer.ts +49 -6
- package/src/cli/registry.ts +41 -13
- package/src/cli/result.ts +12 -6
- package/src/cli/ticker-resolution.ts +2 -1
- package/src/cli/types.ts +2 -0
- package/src/components/chart/composite/format.ts +3 -0
- package/src/components/chart-table/header.tsx +1 -1
- package/src/components/chart-table/layout.ts +28 -11
- package/src/components/command-bar/action-runtime.ts +4 -0
- package/src/components/command-bar/commands/direct/index.ts +18 -33
- package/src/components/command-bar/commands/direct/runtime.ts +5 -0
- package/src/components/command-bar/commands/registry.ts +10 -1
- package/src/components/command-bar/routes/root/command-items.ts +9 -4
- package/src/components/command-bar/routes/root/runtime.ts +51 -6
- package/src/components/command-bar/selection-runtime.ts +12 -29
- package/src/components/command-bar/surface/index.tsx +9 -3
- package/src/components/company-logo.tsx +2 -1
- package/src/components/layout/detached-pane-shell.tsx +2 -2
- package/src/components/layout/header.tsx +10 -5
- package/src/components/layout/market-summary.ts +25 -7
- package/src/components/layout/pane/footer/index.tsx +8 -2
- package/src/components/layout/pane/footer/model.ts +73 -6
- package/src/components/layout/pane/header.tsx +2 -2
- package/src/components/layout/pane/status-footer.ts +2 -1
- package/src/components/market-board/index.tsx +3 -2
- package/src/components/onboarding/desks-step.tsx +12 -4
- package/src/components/onboarding/first-run-workspace.ts +81 -4
- package/src/components/onboarding/onboarding-frame.tsx +4 -2
- package/src/components/onboarding/onboarding-keyboard.ts +8 -5
- package/src/components/onboarding/onboarding-wizard.tsx +60 -26
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
- package/src/components/onboarding/run-setup-again.ts +24 -0
- package/src/components/onboarding/use-onboarding-progress.ts +33 -15
- package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
- package/src/components/onboarding/wizard-model.ts +57 -3
- package/src/components/onboarding/wizard-positions.ts +1 -1
- package/src/components/ui/delayed-mark.tsx +56 -0
- package/src/components/ui/display.tsx +16 -5
- package/src/components/ui/external-link.tsx +3 -6
- package/src/components/ui/icon.tsx +16 -3
- package/src/components/ui/logo-mark.tsx +8 -5
- package/src/components/ui/stat-grid.tsx +263 -19
- package/src/core/app-services.ts +11 -2
- package/src/data/app-persistence.ts +9 -7
- package/src/data/config/store/node.ts +11 -5
- package/src/data/config/store/normalize.ts +16 -3
- package/src/data/plugin-cache.ts +15 -0
- package/src/data/plugin-state-store.ts +81 -24
- package/src/data/profile-permissions.ts +47 -0
- package/src/data/resource-store.ts +165 -90
- package/src/data/session-store.ts +31 -8
- package/src/data/sqlite/access.ts +324 -0
- package/src/data/sqlite/checkpointer.ts +285 -0
- package/src/data/sqlite/database.ts +80 -4
- package/src/data/sqlite/retry.ts +50 -9
- package/src/data/ticker-store.ts +57 -23
- package/src/docs/snapshot.json +191 -78
- package/src/i18n/es.ts +18 -2
- package/src/i18n/ja.ts +18 -2
- package/src/i18n/ko.ts +18 -2
- package/src/i18n/zh-cn.ts +18 -2
- package/src/i18n/zh-tw.ts +18 -2
- package/src/layout/desk-keys.ts +4 -1
- package/src/layout/desks.ts +38 -5
- package/src/layout-marketplace/gallery-desktop.tsx +45 -16
- package/src/market-data/batched-reads.ts +16 -0
- package/src/market-data/coordinator/auxiliary.ts +4 -2
- package/src/market-data/coordinator/index.ts +11 -2
- package/src/market-data/futures-board.ts +1 -1
- package/src/market-data/futures-contract.ts +3 -1
- package/src/market-data/market/index-level.ts +6 -4
- package/src/market-data/published-apac-sessions.ts +6 -0
- package/src/market-data/realized-volatility.ts +4 -2
- package/src/news/aggregator.ts +7 -4
- package/src/news/news-model.ts +11 -0
- package/src/news/story-id.ts +22 -0
- package/src/news/ticker-symbols.ts +17 -1
- package/src/notifications/app-notifier.ts +35 -14
- package/src/plugins/auto-update-node.ts +10 -6
- package/src/plugins/builtin/account-management/form-components.tsx +1 -1
- package/src/plugins/builtin/account-management/index.tsx +2 -3
- package/src/plugins/builtin/account-management/model.ts +1 -1
- package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
- package/src/plugins/builtin/alerts/events.ts +10 -2
- package/src/plugins/builtin/alerts/index.tsx +5 -5
- package/src/plugins/builtin/alerts/research-builder.ts +5 -1
- package/src/plugins/builtin/alerts/research-rules.ts +18 -0
- package/src/plugins/builtin/alerts/targets.ts +4 -1
- package/src/plugins/builtin/attention/headless.ts +1 -1
- package/src/plugins/builtin/attention/index.tsx +2 -4
- package/src/plugins/builtin/attention/listing.ts +1 -1
- package/src/plugins/builtin/attention/series.ts +1 -1
- package/src/plugins/builtin/awards/index.tsx +1 -2
- package/src/plugins/builtin/backtest/client.ts +1 -0
- package/src/plugins/builtin/backtest/index.tsx +1 -2
- package/src/plugins/builtin/backtest/rules.ts +3 -1
- package/src/plugins/builtin/backtest/window.ts +8 -7
- package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
- package/src/plugins/builtin/broker-manager/index.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +5 -0
- package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
- package/src/plugins/builtin/buildout/company/id.ts +2 -0
- package/src/plugins/builtin/buildout/company/model.ts +247 -0
- package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
- package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
- package/src/plugins/builtin/buildout/facilities.ts +309 -0
- package/src/plugins/builtin/buildout/format.ts +9 -0
- package/src/plugins/builtin/buildout/index.tsx +51 -5
- package/src/plugins/builtin/buildout/model/index.ts +62 -10
- package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
- package/src/plugins/builtin/buildout/model/types.ts +11 -2
- package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
- package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
- package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
- package/src/plugins/builtin/buildout/table-model.ts +26 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
- package/src/plugins/builtin/cds/headless.ts +10 -2
- package/src/plugins/builtin/cds/model.ts +7 -3
- package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
- package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
- package/src/plugins/builtin/central-bank-rates/series.ts +2 -3
- package/src/plugins/builtin/changelog/index.tsx +3 -2
- package/src/plugins/builtin/chart-composer/cli-options.ts +16 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -0
- package/src/plugins/builtin/chart-composer/headless.ts +88 -21
- package/src/plugins/builtin/chart-composer/index.tsx +2 -3
- package/src/plugins/builtin/chart-composer/pane.tsx +30 -2
- package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
- package/src/plugins/builtin/chart-composer/session-range.ts +5 -2
- package/src/plugins/builtin/chart-composer/studies.ts +1 -1
- package/src/plugins/builtin/chat/controller/index.ts +1 -1
- package/src/plugins/builtin/chat/controller/messages.ts +15 -4
- package/src/plugins/builtin/chat/ghost-user.ts +8 -0
- package/src/plugins/builtin/chat/index.tsx +5 -5
- package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/index.tsx +1 -2
- package/src/plugins/builtin/cloud/plugin.tsx +6 -4
- package/src/plugins/builtin/cloud/team/module.tsx +1 -1
- package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
- package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
- package/src/plugins/builtin/comment-letters/index.tsx +1 -2
- package/src/plugins/builtin/commodity-balances/client.ts +4 -1
- package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
- package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
- package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
- package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
- package/src/plugins/builtin/congress-trades/headless.ts +2 -0
- package/src/plugins/builtin/congress-trades/model.ts +92 -64
- package/src/plugins/builtin/congress-trades/table.tsx +14 -2
- package/src/plugins/builtin/connections/index.ts +2 -3
- package/src/plugins/builtin/cot/client.ts +28 -2
- package/src/plugins/builtin/cot/headless.ts +4 -1
- package/src/plugins/builtin/cot/index.tsx +5 -4
- package/src/plugins/builtin/cot/model.ts +58 -7
- package/src/plugins/builtin/cot/pane.tsx +6 -2
- package/src/plugins/builtin/cpi/client.ts +6 -5
- package/src/plugins/builtin/cpi/index.tsx +1 -2
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
- package/src/plugins/builtin/credit-boards/client.ts +7 -2
- package/src/plugins/builtin/credit-boards/headless.ts +25 -12
- package/src/plugins/builtin/credit-boards/model.ts +29 -0
- package/src/plugins/builtin/credit-boards/series.ts +3 -4
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
- package/src/plugins/builtin/credit-conditions/client.ts +22 -14
- package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
- package/src/plugins/builtin/crypto-board/client.ts +5 -5
- package/src/plugins/builtin/crypto-board/headless.ts +4 -3
- package/src/plugins/builtin/crypto-board/index.tsx +4 -2
- package/src/plugins/builtin/crypto-board/series.ts +6 -4
- package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
- package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
- package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
- package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
- package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
- package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
- package/src/plugins/builtin/debt-maturities/model.ts +17 -1
- package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
- package/src/plugins/builtin/debt-maturities/series.ts +3 -3
- package/src/plugins/builtin/debug/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/client.ts +32 -2
- package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
- package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
- package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
- package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
- package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
- package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
- package/src/plugins/builtin/dividend-yield/view.ts +21 -0
- package/src/plugins/builtin/docs/index.tsx +2 -3
- package/src/plugins/builtin/doe/client.ts +6 -5
- package/src/plugins/builtin/doe/headless.ts +16 -3
- package/src/plugins/builtin/doe/index.tsx +5 -4
- package/src/plugins/builtin/doe/prices.ts +2 -3
- package/src/plugins/builtin/doe/series.ts +4 -4
- package/src/plugins/builtin/earnings/board.tsx +2 -1
- package/src/plugins/builtin/earnings/history.tsx +2 -1
- package/src/plugins/builtin/earnings-calls/calendar.tsx +10 -2
- package/src/plugins/builtin/earnings-calls/data.ts +19 -11
- package/src/plugins/builtin/earnings-calls/pane.tsx +27 -14
- package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
- package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
- package/src/plugins/builtin/econ/calendar-model.ts +29 -6
- package/src/plugins/builtin/econ/detail-view.tsx +16 -2
- package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/headless.ts +1 -0
- package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
- package/src/plugins/builtin/equity-screener/index.tsx +1 -2
- package/src/plugins/builtin/equity-screener/model.ts +28 -4
- package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
- package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
- package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
- package/src/plugins/builtin/executives/data.ts +6 -1
- package/src/plugins/builtin/executives/pane.tsx +9 -21
- package/src/plugins/builtin/exposure/holdings.ts +35 -13
- package/src/plugins/builtin/exposure/index.tsx +1 -2
- package/src/plugins/builtin/fear-greed/cache.ts +4 -0
- package/src/plugins/builtin/fear-greed/cli.ts +44 -0
- package/src/plugins/builtin/fear-greed/index.tsx +5 -53
- package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
- package/src/plugins/builtin/fear-greed/series.ts +6 -4
- package/src/plugins/builtin/fixed-income/index.tsx +1 -2
- package/src/plugins/builtin/futures/board-reads.ts +25 -0
- package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
- package/src/plugins/builtin/futures/index.tsx +15 -0
- package/src/plugins/builtin/futures/model.ts +14 -0
- package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
- package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
- package/src/plugins/builtin/futures-curve/headless.ts +6 -1
- package/src/plugins/builtin/futures-curve/index.tsx +1 -2
- package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
- package/src/plugins/builtin/gpu/board.tsx +51 -12
- package/src/plugins/builtin/gpu/client.ts +19 -3
- package/src/plugins/builtin/gpu/equities.tsx +5 -2
- package/src/plugins/builtin/gpu/headless.ts +14 -2
- package/src/plugins/builtin/gpu/index.tsx +1 -2
- package/src/plugins/builtin/gpu/model.ts +20 -2
- package/src/plugins/builtin/gpu/pane.tsx +1 -1
- package/src/plugins/builtin/gpu/series.ts +2 -3
- package/src/plugins/builtin/help/index.tsx +1 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +1 -2
- package/src/plugins/builtin/holders/index-holders.ts +24 -12
- package/src/plugins/builtin/holders/index.tsx +2 -3
- package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
- package/src/plugins/builtin/iv-history/index.tsx +1 -2
- package/src/plugins/builtin/jodi/client.ts +4 -1
- package/src/plugins/builtin/jodi/index.tsx +1 -2
- package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
- package/src/plugins/builtin/layout-manager/index.tsx +9 -8
- package/src/plugins/builtin/macro-day/pane.tsx +4 -2
- package/src/plugins/builtin/market-halts/index.tsx +1 -2
- package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
- package/src/plugins/builtin/market-movers/client.ts +1 -0
- package/src/plugins/builtin/market-movers/index.tsx +16 -5
- package/src/plugins/builtin/market-movers/screener.ts +23 -6
- package/src/plugins/builtin/market-movers/session-body.tsx +6 -2
- package/src/plugins/builtin/market-valuation/index.tsx +1 -2
- package/src/plugins/builtin/members/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/index.tsx +1 -2
- package/src/plugins/builtin/money-markets/pane.tsx +2 -1
- package/src/plugins/builtin/money-markets/series.ts +3 -5
- package/src/plugins/builtin/news/headless.ts +7 -0
- package/src/plugins/builtin/news/wire/index.tsx +1 -1
- package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
- package/src/plugins/builtin/notes/index.tsx +3 -6
- package/src/plugins/builtin/openfda/index.tsx +1 -2
- package/src/plugins/builtin/options/view.tsx +6 -5
- package/src/plugins/builtin/options-calculator/index.tsx +1 -2
- package/src/plugins/builtin/options-positioning/index.tsx +1 -2
- package/src/plugins/builtin/options-scenario/index.tsx +2 -4
- package/src/plugins/builtin/perps/index.tsx +1 -2
- package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
- package/src/plugins/builtin/plugin-module.ts +16 -2
- package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
- package/src/plugins/builtin/power/gas-client.ts +2 -1
- package/src/plugins/builtin/power/index.tsx +1 -2
- package/src/plugins/builtin/power/prices-client.ts +3 -2
- package/src/plugins/builtin/rate-path/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/headless.ts +2 -0
- package/src/plugins/builtin/realized-vol/index.tsx +1 -2
- package/src/plugins/builtin/realized-vol/model.ts +16 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
- package/src/plugins/builtin/research/analyst-headless.ts +33 -6
- package/src/plugins/builtin/research/analyst-model.ts +37 -2
- package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
- package/src/plugins/builtin/research/index.tsx +5 -8
- package/src/plugins/builtin/research-search/index.tsx +1 -2
- package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
- package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
- package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
- package/src/plugins/builtin/risk-factors/data.ts +8 -4
- package/src/plugins/builtin/risk-factors/index.tsx +3 -1
- package/src/plugins/builtin/risk-factors/pane.tsx +5 -8
- package/src/plugins/builtin/roadmap/index.tsx +2 -3
- package/src/plugins/builtin/roadmap/pane.tsx +49 -2
- package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
- package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
- package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
- package/src/plugins/builtin/scanner/lock-note.ts +26 -3
- package/src/plugins/builtin/sec/filing-content.ts +38 -6
- package/src/plugins/builtin/sec/headless.ts +5 -1
- package/src/plugins/builtin/sec/index.tsx +4 -2
- package/src/plugins/builtin/sec/model.ts +2 -0
- package/src/plugins/builtin/sectors/index.tsx +11 -5
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +73 -8
- package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
- package/src/plugins/builtin/shared/forming-bars.ts +29 -0
- package/src/plugins/builtin/shared/fred-points.ts +6 -1
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
- package/src/plugins/builtin/shared/pro-lock-note.ts +23 -8
- package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
- package/src/plugins/builtin/shared/series-warning.ts +13 -4
- package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
- package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/pane.tsx +2 -1
- package/src/plugins/builtin/short-volume/series.ts +2 -3
- package/src/plugins/builtin/social-mentions/client.ts +15 -10
- package/src/plugins/builtin/social-mentions/index.tsx +1 -2
- package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
- package/src/plugins/builtin/social-mentions/series.ts +2 -3
- package/src/plugins/builtin/supply-chain/client.ts +22 -1
- package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
- package/src/plugins/builtin/supply-chain/index.tsx +1 -2
- package/src/plugins/builtin/thirteenf/api.ts +127 -29
- package/src/plugins/builtin/thirteenf/data.ts +165 -35
- package/src/plugins/builtin/thirteenf/index.tsx +2 -4
- package/src/plugins/builtin/thirteenf/model.ts +2 -0
- package/src/plugins/builtin/thirteenf/overlap-pane.tsx +5 -1
- package/src/plugins/builtin/thirteenf/pane.tsx +41 -16
- package/src/plugins/builtin/thirteenf/types.ts +14 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -6
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
- package/src/plugins/builtin/ticker-detail/financials/model.ts +35 -2
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +53 -18
- package/src/plugins/builtin/ticker-detail/headless.ts +70 -11
- package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
- package/src/plugins/builtin/time-sales/index.tsx +1 -2
- package/src/plugins/builtin/time-sales/pane.tsx +3 -1
- package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
- package/src/plugins/builtin/trace-bonds/model.ts +44 -10
- package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
- package/src/plugins/builtin/treasury-auctions/cache.ts +20 -8
- package/src/plugins/builtin/treasury-auctions/client.ts +3 -3
- package/src/plugins/builtin/treasury-auctions/headless.ts +3 -1
- package/src/plugins/builtin/treasury-auctions/pane.tsx +10 -4
- package/src/plugins/builtin/vol-surface/client.ts +18 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -2
- package/src/plugins/builtin/volatility/client.ts +43 -16
- package/src/plugins/builtin/volatility/headless.ts +5 -2
- package/src/plugins/builtin/volatility/index.tsx +1 -2
- package/src/plugins/builtin/volatility/model.ts +17 -6
- package/src/plugins/builtin/volatility/pane.tsx +6 -2
- package/src/plugins/builtin/volatility/series.ts +2 -1
- package/src/plugins/builtin/world-indices/footer.ts +6 -1
- package/src/plugins/builtin/world-venue-map/client.ts +10 -1
- package/src/plugins/builtin/world-venue-map/index.ts +6 -2
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
- package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
- package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
- package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
- package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
- package/src/plugins/builtin/yield-curve/client.ts +4 -6
- package/src/plugins/builtin/yield-curve/curves.ts +16 -2
- package/src/plugins/builtin/yield-curve/headless.ts +3 -3
- package/src/plugins/bundle.ts +52 -2
- package/src/plugins/catalog-browser.ts +2 -2
- package/src/plugins/catalog-cli.ts +20 -0
- package/src/plugins/host-link.ts +13 -1
- package/src/plugins/installer.ts +64 -42
- package/src/plugins/loader.ts +43 -11
- package/src/plugins/peer-imports.ts +201 -0
- package/src/plugins/peer-retry.ts +26 -29
- package/src/plugins/plugin-process.ts +128 -0
- package/src/plugins/redirection-guard.ts +45 -0
- package/src/remote/endpoint.ts +16 -3
- package/src/renderers/browser/main.tsx +3 -2
- package/src/renderers/cloudflare/http-proxy.ts +7 -123
- package/src/renderers/dom/desktop/icons.tsx +5 -2
- package/src/renderers/dom/host/image-surface.tsx +46 -21
- package/src/renderers/dom/styles.css +7 -0
- package/src/renderers/dom/toast-host.tsx +2 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +23 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +12 -3
- package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
- package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
- package/src/renderers/electrobun/bun/window/attention.ts +54 -0
- package/src/renderers/electrobun/bun/window/create.ts +6 -2
- package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
- package/src/renderers/electrobun/view/main.tsx +7 -2
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
- package/src/renderers/electrobun/view/ui-host.tsx +9 -2
- package/src/renderers/opentui/browser-open.ts +10 -4
- package/src/renderers/opentui/image/surface.tsx +1 -1
- package/src/renderers/opentui/start.tsx +12 -6
- package/src/renderers/opentui/toast-host.tsx +2 -1
- package/src/series/registry.ts +18 -0
- package/src/series/resolve.ts +2 -1
- package/src/series/standalone.ts +10 -1
- package/src/series/types.ts +12 -0
- package/src/series/validate.ts +11 -2
- package/src/sources/gloomberb-cloud/fred-series.ts +5 -3
- package/src/sources/gloomberb-cloud/index.ts +28 -5
- package/src/sources/gloomberb-cloud/news.ts +24 -3
- package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
- package/src/sources/provider-errors.ts +26 -3
- package/src/sources/provider-router/history.ts +5 -0
- package/src/sources/provider-router/index.ts +13 -0
- package/src/sync/core-contributors.ts +32 -0
- package/src/telemetry/attention-counts.ts +64 -6
- package/src/telemetry/attention-setting.ts +33 -0
- package/src/telemetry/crash-reports-node.ts +3 -1
- package/src/telemetry/stall-clocks.ts +93 -0
- package/src/telemetry/stall-reports-node.ts +25 -9
- package/src/telemetry/stall-reports.ts +218 -13
- package/src/test-support/outage.ts +8 -0
- package/src/test-support/preload.ts +9 -0
- package/src/tickers/company-name-listing.ts +59 -29
- package/src/tickers/company-name.ts +84 -1
- package/src/tickers/listing-fate.ts +14 -3
- package/src/tickers/saved-instrument.ts +17 -0
- package/src/tickers/search/home-listing.ts +104 -8
- package/src/tickers/search/index.ts +5 -2
- package/src/tickers/search/types.ts +6 -1
- package/src/time-series/bar-aggregation.ts +7 -1
- package/src/time-series/base-series.ts +3 -3
- package/src/time-series/chart-bounds.ts +1 -1
- package/src/time-series/chart-data.ts +5 -2
- package/src/time-series/chart-seed.ts +1 -1
- package/src/time-series/coin-session-close.ts +211 -0
- package/src/time-series/live-bars.ts +13 -2
- package/src/time-series/live-quotes.ts +1 -1
- package/src/time-series/market.ts +5 -3
- package/src/time-series/official-close.ts +159 -0
- package/src/time-series/price-comparison.ts +75 -8
- package/src/time-series/resolve.ts +32 -4
- package/src/time-series/series-presentation.ts +1 -1
- package/src/time-series/studies/formula.ts +205 -0
- package/src/time-series/studies/indicators.ts +299 -0
- package/src/time-series/studies/leverage.ts +75 -0
- package/src/time-series/studies/pair.ts +138 -0
- package/src/time-series/studies/resolve.ts +333 -0
- package/src/time-series/studies/shared.ts +141 -0
- package/src/time-series/studies/units.ts +243 -0
- package/src/time-series/studies/vwap.ts +274 -0
- package/src/time-series/types.ts +7 -0
- package/src/types/config.ts +18 -2
- package/src/types/data-provider.ts +23 -0
- package/src/types/financials.ts +40 -3
- package/src/types/headless.ts +42 -3
- package/src/types/plugin.ts +32 -3
- package/src/ui/host.tsx +17 -1
- package/src/ui/toast.tsx +26 -1
- package/src/utils/ascii-text.ts +140 -0
- package/src/utils/cli-output.ts +91 -18
- package/src/utils/concurrency.ts +24 -0
- package/src/utils/exchanges.ts +6 -0
- package/src/utils/external-url.ts +20 -7
- package/src/utils/financial-statements.ts +73 -16
- package/src/utils/fundamentals.ts +17 -5
- package/src/utils/futures-generic.ts +4 -4
- package/src/utils/http-proxy-upstream.ts +124 -0
- package/src/utils/lease-liabilities.ts +54 -0
- package/src/utils/main-thread-stall.ts +122 -5
- package/src/utils/perf-marks.ts +58 -12
- package/src/utils/price-history-integrity.ts +47 -1
- package/src/utils/private-files.ts +141 -0
- package/src/utils/public-address.ts +99 -0
- package/src/account/upgrade-personalization.ts +0 -57
- package/src/account/upgrade-tickers.ts +0 -124
- package/src/api-client/upgrade-experiment.ts +0 -91
- package/src/plugins/builtin/cloud/browser.tsx +0 -3
- package/src/time-series/studies.ts +0 -1628
|
@@ -19,30 +19,12 @@ import {
|
|
|
19
19
|
readRequestInit,
|
|
20
20
|
toResponseHead,
|
|
21
21
|
type HttpProxyResponseEnvelope,
|
|
22
|
-
type ProxiedRequestInit,
|
|
23
22
|
} from "../../utils/http-proxy-response";
|
|
24
23
|
import { isProxiedHost, PROXY_ALLOWED_HOSTS } from "../../utils/plugin-proxy-hosts";
|
|
24
|
+
import { fetchCheckingRedirects, readBytesWithin, type ProxyUpstreamResult } from "../../utils/http-proxy-upstream";
|
|
25
25
|
import type { ProxySessionGate } from "./proxy-session";
|
|
26
26
|
|
|
27
27
|
const PROXY_METHODS = new Set(["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"]);
|
|
28
|
-
/** Set by the caller's browser or meaningful only to the hop it came from. */
|
|
29
|
-
const STRIPPED_REQUEST_HEADERS = new Set([
|
|
30
|
-
"connection",
|
|
31
|
-
"content-length",
|
|
32
|
-
"host",
|
|
33
|
-
"keep-alive",
|
|
34
|
-
"proxy-authorization",
|
|
35
|
-
"te",
|
|
36
|
-
"trailer",
|
|
37
|
-
"transfer-encoding",
|
|
38
|
-
"upgrade",
|
|
39
|
-
]);
|
|
40
|
-
/** Dropped when a redirect leaves the origin the plugin set them for. */
|
|
41
|
-
const CROSS_ORIGIN_STRIPPED_HEADERS = ["authorization", "cookie", "proxy-authorization"];
|
|
42
|
-
/** Dropped when a redirect turns the request into a bodiless GET. */
|
|
43
|
-
const REQUEST_BODY_HEADERS = ["content-encoding", "content-language", "content-location", "content-type"];
|
|
44
|
-
const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
|
|
45
|
-
const MAX_REDIRECTS = 5;
|
|
46
28
|
const MAX_BODY_BYTES = 5 * 1024 * 1024;
|
|
47
29
|
/** Plugin request bodies are small JSON; anything near this is not one. */
|
|
48
30
|
const MAX_REQUEST_BYTES = 1024 * 1024;
|
|
@@ -88,112 +70,10 @@ export function validateProxyTarget(
|
|
|
88
70
|
return { url };
|
|
89
71
|
}
|
|
90
72
|
|
|
91
|
-
function upstreamHeaders(raw: Record<string, string>): Headers {
|
|
92
|
-
const headers = new Headers();
|
|
93
|
-
for (const [name, value] of Object.entries(raw)) {
|
|
94
|
-
if (STRIPPED_REQUEST_HEADERS.has(name.toLowerCase())) continue;
|
|
95
|
-
try {
|
|
96
|
-
headers.set(name, value);
|
|
97
|
-
} catch {
|
|
98
|
-
// A header name the runtime refuses is dropped rather than failing the
|
|
99
|
-
// whole request, matching how a browser treats an unsettable header.
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return headers;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
73
|
function proxyError(message: string, status: number, headers: Record<string, string> = {}): Response {
|
|
106
74
|
return Response.json({ error: message }, { status, headers: { "cache-control": "no-store", ...headers } });
|
|
107
75
|
}
|
|
108
76
|
|
|
109
|
-
/**
|
|
110
|
-
* Reads a body, giving up once it passes `maxBytes`. Returns null when it was
|
|
111
|
-
* too large, after cancelling the stream so the rest is never pulled.
|
|
112
|
-
*/
|
|
113
|
-
async function readBytesWithin(body: ReadableStream<Uint8Array> | null, maxBytes: number): Promise<Uint8Array | null> {
|
|
114
|
-
if (!body) return new Uint8Array(0);
|
|
115
|
-
const reader = body.getReader();
|
|
116
|
-
const chunks: Uint8Array[] = [];
|
|
117
|
-
let size = 0;
|
|
118
|
-
for (;;) {
|
|
119
|
-
const { done, value } = await reader.read();
|
|
120
|
-
if (done) break;
|
|
121
|
-
size += value.byteLength;
|
|
122
|
-
if (size > maxBytes) {
|
|
123
|
-
await reader.cancel().catch(() => {});
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
chunks.push(value);
|
|
127
|
-
}
|
|
128
|
-
const bytes = new Uint8Array(size);
|
|
129
|
-
let offset = 0;
|
|
130
|
-
for (const chunk of chunks) {
|
|
131
|
-
bytes.set(chunk, offset);
|
|
132
|
-
offset += chunk.byteLength;
|
|
133
|
-
}
|
|
134
|
-
return bytes;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
type UpstreamResult = { response: Response } | { error: string; status: number };
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Follows redirects one hop at a time instead of letting `fetch` do it, so
|
|
141
|
-
* every `Location` passes the same allowlist check as the first URL. An
|
|
142
|
-
* allowlisted host that redirects elsewhere (an open redirect, a moved API)
|
|
143
|
-
* would otherwise carry the request to any host on the internet.
|
|
144
|
-
*
|
|
145
|
-
* Method and header handling mirror the Fetch spec: 303, and 301/302 after a
|
|
146
|
-
* POST, become a bodiless GET; credentials the plugin set are dropped when the
|
|
147
|
-
* redirect leaves the origin they were meant for.
|
|
148
|
-
*/
|
|
149
|
-
async function fetchWithinAllowlist(
|
|
150
|
-
first: URL,
|
|
151
|
-
init: ProxiedRequestInit,
|
|
152
|
-
fetchUpstream: typeof fetch,
|
|
153
|
-
hosts: readonly string[],
|
|
154
|
-
signal: AbortSignal,
|
|
155
|
-
): Promise<UpstreamResult> {
|
|
156
|
-
const headers = upstreamHeaders(init.headers);
|
|
157
|
-
const mode = init.redirect ?? "follow";
|
|
158
|
-
let url = first;
|
|
159
|
-
let method = init.method;
|
|
160
|
-
let body = init.body;
|
|
161
|
-
|
|
162
|
-
for (let followed = 0; ; followed += 1) {
|
|
163
|
-
const response = await fetchUpstream(url, { method, headers, body, redirect: "manual", signal });
|
|
164
|
-
const location = REDIRECT_STATUSES.has(response.status) ? response.headers.get("location") : null;
|
|
165
|
-
// "manual" hands the redirect back to the plugin. If it follows it, that
|
|
166
|
-
// comes back through here as a new request and is checked like one.
|
|
167
|
-
if (location === null || mode === "manual") return { response };
|
|
168
|
-
|
|
169
|
-
await response.body?.cancel().catch(() => {});
|
|
170
|
-
if (mode === "error") return { error: "The upstream response was a redirect.", status: 502 };
|
|
171
|
-
if (followed >= MAX_REDIRECTS) return { error: "The upstream redirected too many times.", status: 502 };
|
|
172
|
-
|
|
173
|
-
let next: string;
|
|
174
|
-
try {
|
|
175
|
-
next = new URL(location, url).href;
|
|
176
|
-
} catch {
|
|
177
|
-
return { error: "The upstream sent an invalid redirect.", status: 502 };
|
|
178
|
-
}
|
|
179
|
-
const target = validateProxyTarget(next, hosts);
|
|
180
|
-
if ("error" in target) return { error: `The upstream redirect was refused. ${target.error}`, status: 502 };
|
|
181
|
-
|
|
182
|
-
if (target.url.origin !== url.origin) {
|
|
183
|
-
for (const name of CROSS_ORIGIN_STRIPPED_HEADERS) headers.delete(name);
|
|
184
|
-
}
|
|
185
|
-
if (
|
|
186
|
-
(response.status === 303 && method !== "GET" && method !== "HEAD")
|
|
187
|
-
|| ((response.status === 301 || response.status === 302) && method === "POST")
|
|
188
|
-
) {
|
|
189
|
-
method = "GET";
|
|
190
|
-
body = undefined;
|
|
191
|
-
for (const name of REQUEST_BODY_HEADERS) headers.delete(name);
|
|
192
|
-
}
|
|
193
|
-
url = target.url;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
77
|
function upstreamFailure(error: unknown): Response {
|
|
198
78
|
const timedOut = error instanceof Error && error.name === "TimeoutError";
|
|
199
79
|
return proxyError(timedOut ? "The upstream request timed out." : "The upstream request failed.", 504);
|
|
@@ -256,9 +136,13 @@ export async function handleHttpProxy(request: Request, options: HttpProxyOption
|
|
|
256
136
|
}
|
|
257
137
|
|
|
258
138
|
const signal = AbortSignal.timeout(Math.min(init.timeoutMs ?? DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS));
|
|
259
|
-
let result:
|
|
139
|
+
let result: ProxyUpstreamResult;
|
|
260
140
|
try {
|
|
261
|
-
result = await
|
|
141
|
+
result = await fetchCheckingRedirects(target.url, init, {
|
|
142
|
+
fetch: fetchUpstream,
|
|
143
|
+
check: (url) => validateProxyTarget(url, hosts),
|
|
144
|
+
signal,
|
|
145
|
+
});
|
|
262
146
|
} catch (error) {
|
|
263
147
|
return upstreamFailure(error);
|
|
264
148
|
}
|
|
@@ -180,7 +180,7 @@ const ICONS: Record<IconName, ReactNode> = {
|
|
|
180
180
|
),
|
|
181
181
|
};
|
|
182
182
|
|
|
183
|
-
export function WebIcon({ name, size = 12, color }: IconProps) {
|
|
183
|
+
export function WebIcon({ name, size = 12, color, fullHeight }: IconProps) {
|
|
184
184
|
return (
|
|
185
185
|
<svg
|
|
186
186
|
viewBox="0 0 12 12"
|
|
@@ -189,7 +189,8 @@ export function WebIcon({ name, size = 12, color }: IconProps) {
|
|
|
189
189
|
fill="none"
|
|
190
190
|
aria-hidden="true"
|
|
191
191
|
data-gloom-icon={name}
|
|
192
|
-
|
|
192
|
+
// The viewBox keeps its size and sits in the middle of the taller box (preserveAspectRatio's default).
|
|
193
|
+
style={{ flex: "none", display: "block", color, ...(fullHeight ? { height: "100%" } : {}) }}
|
|
193
194
|
>
|
|
194
195
|
{ICONS[name]}
|
|
195
196
|
</svg>
|
|
@@ -224,6 +225,7 @@ export function WebIconButton({
|
|
|
224
225
|
disabled = false,
|
|
225
226
|
size = 12,
|
|
226
227
|
stopPropagation = true,
|
|
228
|
+
text,
|
|
227
229
|
}: IconButtonProps) {
|
|
228
230
|
return (
|
|
229
231
|
<button
|
|
@@ -260,6 +262,7 @@ export function WebIconButton({
|
|
|
260
262
|
}}
|
|
261
263
|
>
|
|
262
264
|
<WebIcon name={icon} size={size} />
|
|
265
|
+
{text ? <span className="gloom-icon-button-text">{text}</span> : null}
|
|
263
266
|
</button>
|
|
264
267
|
);
|
|
265
268
|
}
|
|
@@ -1,48 +1,73 @@
|
|
|
1
1
|
/** @jsxImportSource react */
|
|
2
|
-
import {
|
|
2
|
+
import { useState, type CSSProperties, type ReactNode } from "react";
|
|
3
3
|
import type { ImageSurfaceProps } from "../../../ui/host";
|
|
4
4
|
import { cleanDomProps, commonStyle } from "./style";
|
|
5
5
|
|
|
6
|
+
type ImageStatus = "loading" | "loaded" | "failed";
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Images that failed to load this session, such as a company with no logo:
|
|
8
10
|
* a row scrolled back into view draws its fallback at once rather than an
|
|
9
11
|
* empty box while it asks again.
|
|
10
12
|
*/
|
|
11
13
|
const failedImages = new Set<string>();
|
|
12
|
-
|
|
14
|
+
/** Images that loaded this session: a row scrolled back into view draws its picture on its first frame, not its stand-in. */
|
|
15
|
+
const loadedImages = new Set<string>();
|
|
16
|
+
const IMAGES_KEPT = 2_000;
|
|
17
|
+
|
|
18
|
+
function remember(images: Set<string>, src: string): void {
|
|
19
|
+
if (images.size >= IMAGES_KEPT) images.delete(images.values().next().value!);
|
|
20
|
+
images.add(src);
|
|
21
|
+
}
|
|
13
22
|
|
|
14
|
-
function
|
|
15
|
-
if (failedImages.
|
|
16
|
-
|
|
23
|
+
function initialStatus(src: string): ImageStatus {
|
|
24
|
+
if (failedImages.has(src)) return "failed";
|
|
25
|
+
return loadedImages.has(src) ? "loaded" : "loading";
|
|
17
26
|
}
|
|
18
27
|
|
|
19
|
-
/**
|
|
20
|
-
|
|
28
|
+
/**
|
|
29
|
+
* An image still being fetched, kept out of the layout and the paint so the
|
|
30
|
+
* box shows its `children` until the picture can replace them whole.
|
|
31
|
+
*/
|
|
32
|
+
const PENDING_IMAGE_STYLE: CSSProperties = { position: "absolute", width: 0, height: 0, opacity: 0, pointerEvents: "none" };
|
|
33
|
+
|
|
34
|
+
/** An image filling its box; `children` stand in without a `src`, or once it fails to load (and while it loads with `fallbackWhileLoading`). */
|
|
35
|
+
export function WebImageSurface({ children, src, alt = "", objectFit = "contain", fallbackWhileLoading = false, imageBackground, ...props }: ImageSurfaceProps) {
|
|
21
36
|
const imageSrc = typeof src === "string" ? src.trim() : "";
|
|
22
|
-
|
|
23
|
-
|
|
37
|
+
// Keyed by the address it describes, so a row that takes another logo starts that one's own load.
|
|
38
|
+
const [known, setKnown] = useState<{ src: string; status: ImageStatus }>(() => ({ src: imageSrc, status: initialStatus(imageSrc) }));
|
|
39
|
+
const status = known.src === imageSrc ? known.status : initialStatus(imageSrc);
|
|
24
40
|
const baseStyle = commonStyle(props);
|
|
41
|
+
const waiting = fallbackWhileLoading && status === "loading";
|
|
25
42
|
return (
|
|
26
43
|
<div
|
|
27
44
|
{...cleanDomProps(props)}
|
|
28
45
|
style={{
|
|
29
46
|
...baseStyle,
|
|
30
47
|
overflow: baseStyle.overflow ?? "hidden",
|
|
48
|
+
...(imageBackground && status === "loaded" ? { background: imageBackground } : {}),
|
|
31
49
|
...(props.style as CSSProperties | undefined),
|
|
32
50
|
}}
|
|
33
51
|
>
|
|
34
|
-
{imageSrc &&
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
{imageSrc && status !== "failed" ? (
|
|
53
|
+
<>
|
|
54
|
+
<img
|
|
55
|
+
key={imageSrc}
|
|
56
|
+
src={imageSrc}
|
|
57
|
+
alt={alt}
|
|
58
|
+
draggable={false}
|
|
59
|
+
onLoad={() => {
|
|
60
|
+
remember(loadedImages, imageSrc);
|
|
61
|
+
setKnown({ src: imageSrc, status: "loaded" });
|
|
62
|
+
}}
|
|
63
|
+
onError={() => {
|
|
64
|
+
remember(failedImages, imageSrc);
|
|
65
|
+
setKnown({ src: imageSrc, status: "failed" });
|
|
66
|
+
}}
|
|
67
|
+
style={waiting ? PENDING_IMAGE_STYLE : { width: "100%", height: "100%", display: "block", objectFit }}
|
|
68
|
+
/>
|
|
69
|
+
{waiting ? children as ReactNode : null}
|
|
70
|
+
</>
|
|
46
71
|
) : children as ReactNode}
|
|
47
72
|
</div>
|
|
48
73
|
);
|
|
@@ -1441,6 +1441,10 @@ html[data-gloom-screenshot="true"] [data-gloom-role="chart-watermark"] {
|
|
|
1441
1441
|
box-shadow: inset -1px 0 0 var(--sg-hairline);
|
|
1442
1442
|
}
|
|
1443
1443
|
[data-gloom-role="stat-grid-cell"][data-wide="true"] { grid-column: 1 / -1; box-shadow: none; }
|
|
1444
|
+
/* A whole-row cell too narrow for its value and detail on one line: the label
|
|
1445
|
+
stays on the first line, the value and the detail run down beside it. */
|
|
1446
|
+
[data-gloom-role="stat-grid-cell"][data-wrapped="true"] { align-items: flex-start; }
|
|
1447
|
+
[data-gloom-role="stat-grid-cell"][data-wrapped="true"] [data-gloom-role="stat-grid-detail"] { flex: none; }
|
|
1444
1448
|
/* Column grid text whose track can be narrower than it, and the grid itself: clipped
|
|
1445
1449
|
without a scroll container where the engine has `overflow: clip`. A text box sets its
|
|
1446
1450
|
own overflow inline, hence !important. The margin keeps the antialiased edge of a glyph
|
|
@@ -1497,6 +1501,9 @@ html[data-gloom-screenshot="true"] [data-gloom-role="chart-watermark"] {
|
|
|
1497
1501
|
.gloom-icon-button:disabled, [data-gloom-role="pane-footer-icon"] { cursor: default; }
|
|
1498
1502
|
.gloom-icon-button:not(:disabled):hover { background: var(--gloom-hover-fill); color: var(--gloom-text-bright); }
|
|
1499
1503
|
.gloom-icon-button[aria-pressed="true"] { color: var(--gloom-warning); }
|
|
1504
|
+
/* A status's few characters after its icon (a delay's 15m): read without hovering, in the muted tone of the meta text around it. */
|
|
1505
|
+
.gloom-icon-button-text, [data-gloom-role="pane-footer-icon-text"] { margin-left: var(--gloom-space-xs); color: var(--gloom-text-muted); }
|
|
1506
|
+
.gloom-icon-button:not(:disabled):hover .gloom-icon-button-text { color: inherit; }
|
|
1500
1507
|
.gloom-qb-view button:disabled { color: var(--gloom-text-muted); opacity: 0.55; cursor: default; }
|
|
1501
1508
|
[data-gloom-role="field-grid-value"] button,
|
|
1502
1509
|
[data-gloom-role="field-grid-value"] button > span { justify-content: flex-end !important; text-align: right; padding-inline: 0 !important; }
|
|
@@ -6,12 +6,14 @@ import {
|
|
|
6
6
|
ToastHostProvider,
|
|
7
7
|
type ToastHost,
|
|
8
8
|
type ToastStore,
|
|
9
|
+
useToastsShown,
|
|
9
10
|
} from "../../ui/toast";
|
|
10
11
|
import { WebButton } from "./desktop/controls";
|
|
11
12
|
import { WebIconButton } from "./desktop/icons";
|
|
12
13
|
|
|
13
14
|
function ToastViewport({ store }: { store: ToastStore }) {
|
|
14
15
|
const toasts = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
16
|
+
useToastsShown(toasts);
|
|
15
17
|
const { dismiss } = store;
|
|
16
18
|
const actionShortcut = useActionShortcut("notification-action");
|
|
17
19
|
const dismissShortcut = useActionShortcut("notification-dismiss");
|
|
@@ -16,6 +16,7 @@ import { startRemoteControlServer, type RemoteControlServer } from "../../../../
|
|
|
16
16
|
import type { RemoteControlRequest, RemoteControlResponse } from "../../../../remote/types";
|
|
17
17
|
import { installCrashReporter } from "../../../../telemetry/crash-reports";
|
|
18
18
|
import { createNodeCrashReporterHost } from "../../../../telemetry/crash-reports-node";
|
|
19
|
+
import { createStallClocks } from "../../../../telemetry/stall-clocks";
|
|
19
20
|
import { installStallReporter, parseStallEvent, recordStall } from "../../../../telemetry/stall-reports";
|
|
20
21
|
import { createNodeStallReporterHost } from "../../../../telemetry/stall-reports-node";
|
|
21
22
|
import { installUpdateFailureReporter, reviewLastUpdateAttempt } from "../../../../telemetry/update-failures";
|
|
@@ -23,7 +24,7 @@ import { createNodeUpdateFailureHost } from "../../../../telemetry/update-failur
|
|
|
23
24
|
import type { AppConfig } from "../../../../types/config";
|
|
24
25
|
import { linkDesktopCommands } from "../../../../updater/command-names";
|
|
25
26
|
import { debugLog } from "../../../../utils/debug-log";
|
|
26
|
-
import { setStallSink, startMainThreadStallMonitor } from "../../../../utils/main-thread-stall";
|
|
27
|
+
import { setStallClocks, setStallSink, startMainThreadStallMonitor } from "../../../../utils/main-thread-stall";
|
|
27
28
|
import type { DesktopSharedStateSnapshot } from "../../../../types/desktop-window";
|
|
28
29
|
import type {
|
|
29
30
|
ContextMenuSelectMessage,
|
|
@@ -45,6 +46,7 @@ import {
|
|
|
45
46
|
takeExternalPluginBundles,
|
|
46
47
|
} from "../external-plugins";
|
|
47
48
|
import { MAIN_WINDOW_RPC_KEY } from "../window/focus";
|
|
49
|
+
import { measureNativeWindowCalls } from "../window/native-timing";
|
|
48
50
|
import { handleDesktopBackendRequest } from "./backend-requests";
|
|
49
51
|
import { detectDesktopInstallKind, removeUpdateAsides } from "./update";
|
|
50
52
|
import { removeLegacyMacApp } from "./app-name";
|
|
@@ -58,6 +60,7 @@ import { applyMacosDockIcon } from "./macos-dock-icon";
|
|
|
58
60
|
import { handleDesktopPluginStateRequest } from "./plugin-state";
|
|
59
61
|
import { scheduleDesktopRelaunch } from "./relaunch";
|
|
60
62
|
import { createDesktopRpcRegistry } from "./rpc-registry";
|
|
63
|
+
import { measureRpcTraffic } from "./rpc-timing";
|
|
61
64
|
import { DesktopStateBroadcaster } from "./state-broadcaster";
|
|
62
65
|
import { createDesktopWorkspace, type DesktopWorkspace } from "./workspace";
|
|
63
66
|
import { handleDesktopWorkspaceRequest } from "./workspace/requests";
|
|
@@ -162,6 +165,8 @@ export class DesktopBackend {
|
|
|
162
165
|
// Desktop has no Debug Log: freezes of this process and of its windows go to the stall log in the data folder.
|
|
163
166
|
installStallReporter(createNodeStallReporterHost({ getDataDir: () => this.config?.dataDir ?? null }));
|
|
164
167
|
setStallSink((event) => recordStall("app", event));
|
|
168
|
+
setStallClocks(createStallClocks());
|
|
169
|
+
measureNativeWindowCalls();
|
|
165
170
|
startMainThreadStallMonitor();
|
|
166
171
|
}
|
|
167
172
|
// The native menu shows the same accelerators the keys use, so a rebind
|
|
@@ -273,6 +278,7 @@ export class DesktopBackend {
|
|
|
273
278
|
},
|
|
274
279
|
},
|
|
275
280
|
});
|
|
281
|
+
measureRpcTraffic(rpc);
|
|
276
282
|
this.rpcs.registerWindowRpc(key, rpc);
|
|
277
283
|
return rpc;
|
|
278
284
|
}
|
|
@@ -18,6 +18,8 @@ import type { DesktopBackend } from "./backend";
|
|
|
18
18
|
|
|
19
19
|
const INITIAL_DOCK_SUPPRESSION_MS = 800;
|
|
20
20
|
const WINDOW_CONTROL_DOCK_SUPPRESSION_MS = 5_000;
|
|
21
|
+
/** How long after the main window has its data the popped-out windows of the last session open. */
|
|
22
|
+
const LAUNCH_WINDOWS_DELAY_MS = 300;
|
|
21
23
|
|
|
22
24
|
export class DesktopDetachedWindowManager {
|
|
23
25
|
private readonly windows = new Map<string, BrowserWindow>();
|
|
@@ -27,9 +29,29 @@ export class DesktopDetachedWindowManager {
|
|
|
27
29
|
private readonly pendingMoveFlush = new Set<string>();
|
|
28
30
|
private readonly suppressDockUntil = new Map<string, number>();
|
|
29
31
|
private readonly windowControlDockSuppressed = new Set<string>();
|
|
32
|
+
/**
|
|
33
|
+
* At launch, popped-out windows wait for the main window. Opening a window
|
|
34
|
+
* holds this process until the system has made it (on Windows, its
|
|
35
|
+
* browser too), and the main window's `init` used to wait behind each one.
|
|
36
|
+
*/
|
|
37
|
+
private windowsAllowed = false;
|
|
38
|
+
private windowsTimer: ReturnType<typeof setTimeout> | null = null;
|
|
30
39
|
|
|
31
40
|
constructor(private readonly backend: DesktopBackend) {}
|
|
32
41
|
|
|
42
|
+
/**
|
|
43
|
+
* The main window has its data: the popped-out windows open shortly
|
|
44
|
+
* after, once it has had a moment to draw and ask for what it shows.
|
|
45
|
+
*/
|
|
46
|
+
openAfterMainWindow(): void {
|
|
47
|
+
if (this.windowsAllowed || this.windowsTimer) return;
|
|
48
|
+
this.windowsTimer = setTimeout(() => {
|
|
49
|
+
this.windowsTimer = null;
|
|
50
|
+
this.windowsAllowed = true;
|
|
51
|
+
this.reconcile();
|
|
52
|
+
}, LAUNCH_WINDOWS_DELAY_MS);
|
|
53
|
+
}
|
|
54
|
+
|
|
33
55
|
focusDetachedPane(instanceId: string): boolean {
|
|
34
56
|
return this.focusWindowForRpcKey(detachedRpcKey(instanceId));
|
|
35
57
|
}
|
|
@@ -88,7 +110,7 @@ export class DesktopDetachedWindowManager {
|
|
|
88
110
|
for (const [instanceId, entry] of desiredEntries) {
|
|
89
111
|
const existingWindow = this.windows.get(instanceId);
|
|
90
112
|
if (!existingWindow) {
|
|
91
|
-
this.createWindow(instanceId, entry);
|
|
113
|
+
if (this.windowsAllowed) this.createWindow(instanceId, entry);
|
|
92
114
|
continue;
|
|
93
115
|
}
|
|
94
116
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { existsSync
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import { makePrivateDirSync } from "../../../../utils/private-files";
|
|
2
3
|
import { homedir, hostname } from "os";
|
|
3
4
|
import { getGloomberbDirs } from "../../../../data/config/home";
|
|
4
5
|
import type { AppServices } from "../../../../core/app-services";
|
|
@@ -25,6 +26,7 @@ import { telemetryOptedOut } from "../../../../telemetry/crash-reports";
|
|
|
25
26
|
import { describeNodeOs, readOrCreateInstallId } from "../../../../telemetry/crash-reports-node";
|
|
26
27
|
import type { DesktopBackend, DesktopRpc } from "./backend";
|
|
27
28
|
import { readDesktopViewIds } from "./view-ids";
|
|
29
|
+
import { markStartupComplete } from "../../../../utils/main-thread-stall";
|
|
28
30
|
|
|
29
31
|
interface DesktopWindowTarget {
|
|
30
32
|
kind: "main" | "detached";
|
|
@@ -102,7 +104,7 @@ function buildInitializationPayload(
|
|
|
102
104
|
async function resolveDesktopDataDir(): Promise<string> {
|
|
103
105
|
const dataDir = await getDataDir() ?? getGloomberbDirs().data;
|
|
104
106
|
if (!existsSync(dataDir)) {
|
|
105
|
-
|
|
107
|
+
makePrivateDirSync(dataDir);
|
|
106
108
|
}
|
|
107
109
|
return dataDir;
|
|
108
110
|
}
|
|
@@ -194,5 +196,12 @@ export async function initializeDesktopBackend(
|
|
|
194
196
|
backend.workspace = createDesktopWorkspace(config, backend.getSessionSnapshot());
|
|
195
197
|
backend.detachedWindows.reconcile();
|
|
196
198
|
}
|
|
197
|
-
|
|
199
|
+
const init = buildInitializationPayload(backend, config, services, windowTarget);
|
|
200
|
+
// The main window has what it needs to draw: this process is up, and the
|
|
201
|
+
// popped-out windows open next.
|
|
202
|
+
if (windowTarget.kind === "main") {
|
|
203
|
+
markStartupComplete();
|
|
204
|
+
backend.detachedWindows.openAfterMainWindow();
|
|
205
|
+
}
|
|
206
|
+
return init;
|
|
198
207
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { measurePerf } from "../../../../utils/perf-marks";
|
|
2
|
+
|
|
3
|
+
interface RpcPacket {
|
|
4
|
+
type?: unknown;
|
|
5
|
+
id?: unknown;
|
|
6
|
+
method?: unknown;
|
|
7
|
+
params?: unknown;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface RpcTransport {
|
|
11
|
+
send?(packet: RpcPacket): void;
|
|
12
|
+
registerHandler?(handler: (packet: RpcPacket) => unknown): void;
|
|
13
|
+
unregisterHandler?(): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Answers waiting for their request's name; past this the oldest is forgotten. */
|
|
17
|
+
const MAX_OPEN_REQUESTS = 500;
|
|
18
|
+
|
|
19
|
+
/** A request or message name as a stall report keeps it: lower-case words, dotted. */
|
|
20
|
+
function sectionPart(name: unknown): string {
|
|
21
|
+
if (typeof name !== "string") return "unknown";
|
|
22
|
+
const part = name
|
|
23
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
24
|
+
.toLowerCase()
|
|
25
|
+
.replace(/[^a-z0-9.]+/g, "-")
|
|
26
|
+
.replace(/^[-.]+|[-.]+$/g, "");
|
|
27
|
+
return part || "unknown";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** What a window asked for: the backend's own method inside the one Electrobun request. */
|
|
31
|
+
function requestName(packet: RpcPacket): string {
|
|
32
|
+
const params = packet.params as { method?: unknown } | null | undefined;
|
|
33
|
+
return sectionPart(packet.method === "backend.request" ? params?.method : packet.method);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Names every turn a window's channel takes on this process: the part of a
|
|
38
|
+
* request answered before its first wait (`rpc.request.<method>`), a message
|
|
39
|
+
* from the window (`rpc.message.<name>`), and the encoding, encryption and
|
|
40
|
+
* sending of an answer (`rpc.respond.<method>`) or a message to the window
|
|
41
|
+
* (`rpc.send.<name>`). A stall that follows one then says which, where the
|
|
42
|
+
* report would otherwise name whatever was last measured minutes before.
|
|
43
|
+
* Call before the channel's window is created: Electrobun hands it the
|
|
44
|
+
* transport then.
|
|
45
|
+
*/
|
|
46
|
+
export function measureRpcTraffic(rpc: { setTransport(transport: RpcTransport): void }): void {
|
|
47
|
+
const setTransport = rpc.setTransport;
|
|
48
|
+
const openRequests = new Map<unknown, string>();
|
|
49
|
+
|
|
50
|
+
const incoming = (packet: RpcPacket): string => {
|
|
51
|
+
if (!packet || typeof packet !== "object") return "rpc.unknown";
|
|
52
|
+
if (packet.type === "request") {
|
|
53
|
+
const name = requestName(packet);
|
|
54
|
+
openRequests.set(packet.id, name);
|
|
55
|
+
if (openRequests.size > MAX_OPEN_REQUESTS) openRequests.delete(openRequests.keys().next().value);
|
|
56
|
+
return `rpc.request.${name}`;
|
|
57
|
+
}
|
|
58
|
+
if (packet.type === "message") return `rpc.message.${sectionPart(packet.id)}`;
|
|
59
|
+
return "rpc.response";
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const outgoing = (packet: RpcPacket): string => {
|
|
63
|
+
if (!packet || typeof packet !== "object") return "rpc.unknown";
|
|
64
|
+
if (packet.type === "response") {
|
|
65
|
+
const name = openRequests.get(packet.id) ?? "unknown";
|
|
66
|
+
openRequests.delete(packet.id);
|
|
67
|
+
return `rpc.respond.${name}`;
|
|
68
|
+
}
|
|
69
|
+
if (packet.type === "message") return `rpc.send.${sectionPart(packet.id)}`;
|
|
70
|
+
return `rpc.ask.${sectionPart(packet.method)}`;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
rpc.setTransport = (transport) => {
|
|
74
|
+
const { send, registerHandler } = transport;
|
|
75
|
+
setTransport.call(rpc, {
|
|
76
|
+
...transport,
|
|
77
|
+
...(send ? { send: (packet: RpcPacket) => measurePerf(outgoing(packet), () => send.call(transport, packet)) } : {}),
|
|
78
|
+
...(registerHandler
|
|
79
|
+
? {
|
|
80
|
+
registerHandler: (handler: (packet: RpcPacket) => unknown) => registerHandler.call(
|
|
81
|
+
transport,
|
|
82
|
+
(packet: RpcPacket) => measurePerf(incoming(packet), () => handler(packet)),
|
|
83
|
+
),
|
|
84
|
+
}
|
|
85
|
+
: {}),
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -111,10 +111,20 @@ export async function removeUpdateAsides(
|
|
|
111
111
|
|
|
112
112
|
const TAR_BLOCK = 512;
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* A macOS extended-attributes sidecar (`._name`), which an archive lists just
|
|
116
|
+
* before the file it describes.
|
|
117
|
+
*/
|
|
118
|
+
function isAppleDoublePath(name: string): boolean {
|
|
119
|
+
return name.split("/").some((segment) => segment.startsWith("._"));
|
|
120
|
+
}
|
|
121
|
+
|
|
114
122
|
/**
|
|
115
123
|
* The first path in a tar, read from its leading headers: a ustar name and
|
|
116
|
-
* prefix, or the long name a pax (`x`) or GNU (`L`) header gives it.
|
|
117
|
-
* `
|
|
124
|
+
* prefix, or the long name a pax (`x`) or GNU (`L`) header gives it. A macOS
|
|
125
|
+
* extended-attributes sidecar (`._Gloomberb`) is skipped, so the path is the
|
|
126
|
+
* entry the archive carries, not the metadata beside it. Null when `bytes`
|
|
127
|
+
* ends before one.
|
|
118
128
|
*/
|
|
119
129
|
export function firstTarPath(bytes: Uint8Array): string | null {
|
|
120
130
|
const decoder = new TextDecoder();
|
|
@@ -136,8 +146,14 @@ export function firstTarPath(bytes: Uint8Array): string | null {
|
|
|
136
146
|
header = data + Math.ceil(size / TAR_BLOCK) * TAR_BLOCK;
|
|
137
147
|
continue;
|
|
138
148
|
}
|
|
139
|
-
|
|
140
|
-
const name = field(header, 100);
|
|
149
|
+
const fromHeader = longName == null;
|
|
150
|
+
const name = longName ?? field(header, 100);
|
|
151
|
+
longName = null;
|
|
152
|
+
if (isAppleDoublePath(name)) {
|
|
153
|
+
header = data + Math.ceil(size / TAR_BLOCK) * TAR_BLOCK;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (!fromHeader) return name;
|
|
141
157
|
// POSIX ustar ("ustar\0") only: GNU tar keeps other fields where the prefix would be.
|
|
142
158
|
const prefix = field(header + 257, 6) === "ustar" ? field(header + 345, 155) : "";
|
|
143
159
|
return prefix ? `${prefix}/${name}` : name;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync, renameSync, rmSync } from "fs";
|
|
2
2
|
import { join } from "path";
|
|
3
|
+
import { makePrivateDirSync, writePrivateFileSync } from "../../../../utils/private-files";
|
|
3
4
|
import type { DesktopViewIds } from "../../shared/protocol";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -37,13 +38,16 @@ export function readDesktopViewIds(dataDir: string): DesktopViewIds {
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Replaces the copy with what the view sent, written whole or not at all and
|
|
43
|
+
* readable by the user alone. Throws when it cannot, so the view tries again.
|
|
44
|
+
*/
|
|
41
45
|
export function saveDesktopViewIds(dataDir: string, ids: unknown): void {
|
|
42
46
|
const path = viewIdsPath(dataDir);
|
|
43
47
|
const temporary = `${path}.${process.pid}.tmp`;
|
|
44
48
|
try {
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
makePrivateDirSync(dataDir);
|
|
50
|
+
writePrivateFileSync(temporary, JSON.stringify(parseViewIds(ids)));
|
|
47
51
|
renameSync(temporary, path);
|
|
48
52
|
} catch (error) {
|
|
49
53
|
rmSync(temporary, { force: true });
|
|
@@ -110,9 +110,13 @@ async function bundlePluginDirectory(
|
|
|
110
110
|
// production JSX runtime. A bundle built from a process without
|
|
111
111
|
// NODE_ENV set targets jsx-dev-runtime instead and fails on first
|
|
112
112
|
// render with "jsxDEV is not a function".
|
|
113
|
+
//
|
|
114
|
+
// The view has no `process`, so the bundler also gives a plugin that reads
|
|
115
|
+
// one a stand-in (see plugins/plugin-process.ts), named after the plugin.
|
|
113
116
|
const outDir = pluginBundleCacheDir(getPluginCacheDir());
|
|
114
117
|
const result = await bundleExternalPlugin(pluginDir, join(outDir, directory), {
|
|
115
118
|
define: { "process.env.NODE_ENV": "\"production\"" },
|
|
119
|
+
pluginName: plugin.name || directory,
|
|
116
120
|
});
|
|
117
121
|
const code = await Bun.file(result.outputPath).text();
|
|
118
122
|
bundleCache.set(pluginDir, { mtimeMs, code });
|