@gloom-sh/terminal 0.18.5 → 0.19.1
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +163 -8
- package/src/api-client/app-attention.ts +3 -0
- package/src/api-client/beneficial-owners.ts +3 -0
- package/src/api-client/company-kpis.ts +3 -0
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +19 -7
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/hiring.ts +3 -0
- package/src/api-client/index.ts +15 -0
- package/src/api-client/ipo.ts +24 -1
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +1 -1
- package/src/api-client/mna.ts +3 -0
- package/src/api-client/paths.ts +9 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/research-activity.ts +3 -2
- package/src/api-client/roadmap.ts +85 -0
- package/src/api-client/types.ts +82 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +0 -2
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +2 -2
- package/src/architecture/pane-tabs-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/brokers/sync-broker-instance.ts +2 -8
- package/src/capabilities/chart-series.ts +1 -1
- package/src/cli/commands/crypto-hints.ts +40 -24
- package/src/cli/commands/market.ts +107 -38
- package/src/cli/commands/news-search.ts +22 -0
- package/src/cli/commands/options-chain.ts +48 -7
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +147 -32
- package/src/cli/commands/search.ts +21 -1
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +87 -36
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +25 -2
- package/src/cli/glossary.ts +31 -2
- package/src/cli/history-rows.ts +57 -9
- package/src/cli/index-roots.ts +0 -7
- package/src/cli/index.ts +2 -0
- package/src/cli/listing-arg.ts +235 -60
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +4 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/discovery.ts +21 -1
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +93 -4
- package/src/cli/pane-functions/function-help.ts +20 -10
- package/src/cli/pane-functions/headless.ts +17 -7
- package/src/cli/pane-functions/index.ts +54 -16
- package/src/cli/pane-functions/market-session.ts +66 -4
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/resolver.ts +19 -8
- package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
- package/src/cli/pane-functions/screenshot-types.ts +5 -0
- package/src/cli/quote-answer.ts +22 -11
- package/src/cli/saved-copy-persistence.ts +88 -0
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +4 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-surface.tsx +52 -3
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +39 -4
- package/src/components/chart/composite/series-cues.ts +14 -0
- package/src/components/chart/composite/text-renderer.ts +9 -2
- package/src/components/chart/composite/tool-catalog.ts +36 -0
- package/src/components/chart/composite/tools.ts +203 -44
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
- package/src/components/command-bar/assist/inventory.ts +2 -1
- package/src/components/command-bar/commands/direct/index.ts +28 -13
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/routes/root/runtime.ts +6 -7
- package/src/components/command-bar/selection-runtime.ts +52 -3
- package/src/components/command-bar/surface/index.tsx +2 -0
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/pane/capture-stamp.ts +2 -2
- package/src/components/layout/pane/footer/index.tsx +22 -6
- package/src/components/layout/pane/footer/model.ts +83 -15
- package/src/components/layout/pane/status-footer.ts +35 -2
- package/src/components/layout/status-widget-space.ts +9 -0
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
- package/src/components/onboarding/wizard-broker-sync.ts +0 -1
- package/src/components/onboarding/wizard-positions.ts +3 -3
- package/src/components/ticker/list-table-view.tsx +8 -21
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/display.tsx +18 -3
- package/src/components/ui/icon.tsx +4 -1
- package/src/components/ui/list-view.tsx +8 -4
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +1 -0
- package/src/components/ui/select-button.tsx +5 -1
- package/src/components/ui/select-field.tsx +1 -0
- package/src/components/ui/table-layout.ts +8 -5
- package/src/core/app-runtime.ts +2 -0
- package/src/core/app-services.ts +2 -2
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/plugin-cache.ts +5 -3
- package/src/data/saved-copies.ts +8 -0
- package/src/data/ticker-repository.ts +16 -9
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/snapshot.json +103 -142
- package/src/i18n/es.ts +20 -6
- package/src/i18n/ja.ts +20 -6
- package/src/i18n/ko.ts +20 -6
- package/src/i18n/zh-cn.ts +20 -6
- package/src/i18n/zh-tw.ts +20 -6
- package/src/market-data/depositary-pair.ts +27 -3
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +16 -3
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/options-alternatives.ts +81 -4
- package/src/market-data/quotes/metadata.ts +7 -0
- package/src/news/aggregator.ts +119 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +17 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +362 -80
- package/src/plugins/builtin/alerts/command.ts +25 -1
- package/src/plugins/builtin/alerts/event-rows.ts +24 -3
- package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
- package/src/plugins/builtin/alerts/fired-log.ts +73 -9
- package/src/plugins/builtin/alerts/format.ts +75 -5
- package/src/plugins/builtin/alerts/headless.ts +34 -12
- package/src/plugins/builtin/alerts/history-rows.ts +3 -2
- package/src/plugins/builtin/alerts/history.ts +1 -1
- package/src/plugins/builtin/alerts/index.tsx +79 -16
- package/src/plugins/builtin/alerts/metric-command.ts +77 -4
- package/src/plugins/builtin/alerts/metric-data.ts +54 -13
- package/src/plugins/builtin/alerts/metric-model.ts +163 -14
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
- package/src/plugins/builtin/alerts/pane.tsx +61 -23
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +12 -8
- package/src/plugins/builtin/alerts/types.ts +4 -0
- package/src/plugins/builtin/analytics/index.tsx +2 -1
- package/src/plugins/builtin/analytics/risk-client.ts +16 -5
- package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
- package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
- package/src/plugins/builtin/analytics/risk-model.ts +55 -9
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
- package/src/plugins/builtin/attention/index.tsx +2 -1
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/broker-manager/index.tsx +7 -3
- package/src/plugins/builtin/buildout/cells.tsx +12 -4
- package/src/plugins/builtin/buildout/format.ts +38 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
- package/src/plugins/builtin/buildout/table-model.ts +23 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
- package/src/plugins/builtin/cds/client.ts +142 -30
- package/src/plugins/builtin/cds/headless.ts +35 -8
- package/src/plugins/builtin/cds/model.ts +145 -18
- package/src/plugins/builtin/cds/pane.tsx +16 -5
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
- package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
- package/src/plugins/builtin/chart-composer/headless.ts +6 -1
- package/src/plugins/builtin/chart-composer/index.tsx +5 -6
- package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
- package/src/plugins/builtin/chat/controller/index.ts +5 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
- package/src/plugins/builtin/chat/controller/state.ts +25 -2
- package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
- package/src/plugins/builtin/chat/controller/utils.ts +7 -2
- package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
- package/src/plugins/builtin/chat/status-preview.ts +79 -0
- package/src/plugins/builtin/chat/status-widget.tsx +147 -10
- package/src/plugins/builtin/chat/unread-badge.ts +51 -10
- package/src/plugins/builtin/clinical-trials/client.ts +49 -4
- package/src/plugins/builtin/clinical-trials/model.ts +86 -31
- package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
- package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
- package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/headless.ts +2 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +12 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/company-kpis/headless.ts +8 -3
- package/src/plugins/builtin/company-kpis/model.ts +73 -5
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +10 -10
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/doe/headless.ts +3 -2
- package/src/plugins/builtin/doe/pane.tsx +13 -3
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/data.ts +18 -6
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
- package/src/plugins/builtin/econ/calendar-model.ts +48 -1
- package/src/plugins/builtin/econ/calendar-source.ts +4 -2
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/figures.ts +48 -0
- package/src/plugins/builtin/econ/index.tsx +14 -5
- package/src/plugins/builtin/econ/print-watch.ts +3 -1
- package/src/plugins/builtin/econ/types.ts +2 -0
- package/src/plugins/builtin/econ-statistics/client.ts +11 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
- package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +4 -2
- package/src/plugins/builtin/equity-screener/headless.ts +144 -13
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/model.ts +99 -10
- package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
- package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
- package/src/plugins/builtin/fx-matrix/client.ts +6 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
- package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
- package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
- package/src/plugins/builtin/gpu/headless.ts +8 -2
- package/src/plugins/builtin/help/function-index.ts +4 -1
- package/src/plugins/builtin/help/index.tsx +62 -59
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
- package/src/plugins/builtin/help/table-cells.tsx +13 -2
- package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +22 -3
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/holders/report-rows.ts +4 -1
- package/src/plugins/builtin/insider/headless.ts +2 -2
- package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
- package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
- package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +33 -18
- package/src/plugins/builtin/iv-history/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/model.ts +163 -27
- package/src/plugins/builtin/iv-history/pane.tsx +10 -6
- package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
- package/src/plugins/builtin/iv-history/universe.ts +5 -1
- package/src/plugins/builtin/jobs/client.ts +25 -29
- package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
- package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
- package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
- package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
- package/src/plugins/builtin/macro-day/headless.ts +12 -5
- package/src/plugins/builtin/macro-day/index.tsx +1 -1
- package/src/plugins/builtin/macro-day/model.ts +39 -5
- package/src/plugins/builtin/macro-day/pane.tsx +27 -19
- package/src/plugins/builtin/macro-day/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +27 -7
- package/src/plugins/builtin/market-movers/headless.ts +34 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +22 -4
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
- package/src/plugins/builtin/mna/headless.ts +2 -0
- package/src/plugins/builtin/mna/pane.tsx +3 -1
- package/src/plugins/builtin/news/headless.ts +3 -1
- package/src/plugins/builtin/news/index.tsx +4 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +29 -5
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/table.ts +5 -2
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +17 -4
- package/src/plugins/builtin/options-positioning/client.ts +11 -0
- package/src/plugins/builtin/options-positioning/headless.ts +21 -8
- package/src/plugins/builtin/options-positioning/index.tsx +1 -1
- package/src/plugins/builtin/options-positioning/model.ts +78 -9
- package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
- package/src/plugins/builtin/options-scenario/client.ts +116 -47
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
- package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
- package/src/plugins/builtin/options-scenario/csp.ts +8 -2
- package/src/plugins/builtin/options-scenario/index.tsx +9 -7
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/model.ts +12 -49
- package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
- package/src/plugins/builtin/perps/cascade.ts +236 -0
- package/src/plugins/builtin/perps/client.ts +56 -2
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +19 -22
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
- package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/realized-vol/client.ts +13 -5
- package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
- package/src/plugins/builtin/realized-vol/headless.ts +8 -4
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/model.ts +37 -6
- package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +40 -12
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/index.tsx +7 -3
- package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
- package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
- package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/risk-factors/data.ts +4 -2
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/roadmap/index.tsx +28 -0
- package/src/plugins/builtin/roadmap/model.ts +67 -0
- package/src/plugins/builtin/roadmap/pane.tsx +244 -0
- package/src/plugins/builtin/scanner/flow-model.ts +50 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +204 -35
- package/src/plugins/builtin/scanner/index.ts +16 -5
- package/src/plugins/builtin/scanner/live-headless.ts +48 -5
- package/src/plugins/builtin/scanner/live-model.ts +11 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/headless.ts +10 -1
- package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
- package/src/plugins/builtin/sec/fund-facts.ts +116 -34
- package/src/plugins/builtin/sec/headless.ts +4 -2
- package/src/plugins/builtin/sec/index.tsx +3 -0
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +11 -2
- package/src/plugins/builtin/shared/research-cells.tsx +1 -1
- package/src/plugins/builtin/shared/series-cache.ts +4 -4
- package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/model.ts +16 -7
- package/src/plugins/builtin/themes/pane.tsx +5 -5
- package/src/plugins/builtin/thirteenf/api.ts +12 -1
- package/src/plugins/builtin/thirteenf/data.ts +7 -7
- package/src/plugins/builtin/thirteenf/headless.ts +31 -4
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +11 -3
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
- package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/client.ts +3 -13
- package/src/plugins/builtin/vol-surface/headless.ts +3 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -1
- package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +7 -4
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/builtin/yield-curve/client.ts +61 -1
- package/src/plugins/builtin/yield-curve/headless.ts +9 -6
- package/src/plugins/builtin/yield-curve/index.tsx +34 -10
- package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/host-link.ts +1 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +11 -7
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/remote/resources.ts +2 -1
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/icons.tsx +7 -0
- package/src/renderers/dom/desktop/list-view.tsx +13 -10
- package/src/renderers/dom/desktop/query-bar.tsx +4 -2
- package/src/renderers/dom/desktop/select-field.tsx +4 -2
- package/src/renderers/dom/host/chart-surface.tsx +21 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
- package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
- package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
- package/src/renderers/electrobun/bun/index.ts +26 -2
- package/src/renderers/electrobun/bun/window/create.ts +43 -4
- package/src/renderers/electrobun/bun/window/events.ts +20 -0
- package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
- package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
- package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
- package/src/renderers/electrobun/shared/app-name.ts +13 -0
- package/src/renderers/electrobun/shared/protocol.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +5 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/ui-host.tsx +8 -0
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +43 -8
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/index.ts +8 -5
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/controller.ts +4 -3
- package/src/sync/core-contributors.ts +90 -31
- package/src/telemetry/stall-reports-dom.ts +38 -0
- package/src/telemetry/stall-reports-node.ts +75 -0
- package/src/telemetry/stall-reports.ts +214 -0
- package/src/test-support/preload.ts +14 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name-listing.ts +53 -13
- package/src/tickers/company-name.ts +50 -4
- package/src/tickers/index-roots.ts +7 -0
- package/src/tickers/listing-fate.ts +2 -2
- package/src/tickers/listing-venues.ts +15 -3
- package/src/tickers/search/index.ts +6 -2
- package/src/tickers/search/ranking.ts +5 -1
- package/src/tickers/search/upsert.ts +13 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/tickers/ticker-changes.ts +8 -4
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +1 -1
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/fundamentals.ts +44 -1
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/resolve-context.ts +3 -1
- package/src/time-series/resolve.ts +56 -4
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +3 -2
- package/src/time-series/studies.ts +8 -2
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +2 -3
- package/src/time-series/types.ts +22 -1
- package/src/types/company-from.ts +40 -0
- package/src/types/data-provider.ts +7 -0
- package/src/types/financials.ts +59 -1
- package/src/types/headless.ts +27 -3
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +9 -0
- package/src/ui/host.tsx +3 -0
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +165 -7
- package/src/utils/declared-dividends.ts +1 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +60 -8
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/price-history.ts +7 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/version.ts +2 -2
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
|
@@ -7,7 +7,14 @@ const WS_SYSMENU = 0x00080000n;
|
|
|
7
7
|
const WS_THICKFRAME = 0x00040000n;
|
|
8
8
|
const WS_MINIMIZEBOX = 0x00020000n;
|
|
9
9
|
const WS_MAXIMIZEBOX = 0x00010000n;
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Electrobun's "hiddenInset" window is captioned and sizable, with the caption
|
|
12
|
+
* cut out of its frame: its edges and corners resize it, it maximizes to the
|
|
13
|
+
* work area and keeps its shadow, while the header draws the buttons. Windows
|
|
14
|
+
* still reads the box bits: they let a click on the taskbar button minimize
|
|
15
|
+
* the window, and Win+Up, snapping and the window menu maximize it.
|
|
16
|
+
*/
|
|
17
|
+
const WINDOWS_FRAME_STYLE = WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
|
|
11
18
|
const SWP_NOSIZE = 0x0001;
|
|
12
19
|
const SWP_NOMOVE = 0x0002;
|
|
13
20
|
const SWP_NOZORDER = 0x0004;
|
|
@@ -22,7 +29,7 @@ function styleValue(value: number | bigint): bigint {
|
|
|
22
29
|
|
|
23
30
|
function applyCustomChromeToWindow(win32: Win32, windowHandle: Pointer): boolean {
|
|
24
31
|
const currentStyle = styleValue(win32.symbols.GetWindowLongPtrW(windowHandle, GWL_STYLE));
|
|
25
|
-
const nextStyle = currentStyle
|
|
32
|
+
const nextStyle = currentStyle | WINDOWS_FRAME_STYLE;
|
|
26
33
|
if (nextStyle !== currentStyle) {
|
|
27
34
|
win32.symbols.SetWindowLongPtrW(windowHandle, GWL_STYLE, nextStyle);
|
|
28
35
|
}
|
|
@@ -41,6 +41,22 @@ function loadWin32() {
|
|
|
41
41
|
args: [FFIType.ptr, FFIType.ptr, FFIType.int, FFIType.int, FFIType.int, FFIType.int, FFIType.u32],
|
|
42
42
|
returns: FFIType.bool,
|
|
43
43
|
},
|
|
44
|
+
GetCursorPos: {
|
|
45
|
+
args: [FFIType.ptr],
|
|
46
|
+
returns: FFIType.bool,
|
|
47
|
+
},
|
|
48
|
+
GetClientRect: {
|
|
49
|
+
args: [FFIType.ptr, FFIType.ptr],
|
|
50
|
+
returns: FFIType.bool,
|
|
51
|
+
},
|
|
52
|
+
PostMessageW: {
|
|
53
|
+
args: [FFIType.ptr, FFIType.u32, FFIType.u64, FFIType.i64],
|
|
54
|
+
returns: FFIType.bool,
|
|
55
|
+
},
|
|
56
|
+
GetGUIThreadInfo: {
|
|
57
|
+
args: [FFIType.u32, FFIType.ptr],
|
|
58
|
+
returns: FFIType.bool,
|
|
59
|
+
},
|
|
44
60
|
});
|
|
45
61
|
}
|
|
46
62
|
|
|
@@ -56,6 +72,68 @@ function win32OrNull(): Win32 | null {
|
|
|
56
72
|
return cachedWin32;
|
|
57
73
|
}
|
|
58
74
|
|
|
75
|
+
const WM_SIZE = 0x0005;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Electrobun opens a window's page at the size of the whole window and fits it
|
|
79
|
+
* to the client area on each WM_SIZE. A framed window's client area is smaller
|
|
80
|
+
* than the window, so until the window first changed size its page ran past
|
|
81
|
+
* the right and bottom edges, the Close button with it. Sends the window the
|
|
82
|
+
* WM_SIZE that fits the page. False off Windows.
|
|
83
|
+
*/
|
|
84
|
+
export function fitPageToClientArea(windowHandle: Pointer): boolean {
|
|
85
|
+
const win32 = win32OrNull();
|
|
86
|
+
if (!win32) return false;
|
|
87
|
+
const client = new Int32Array(4);
|
|
88
|
+
if (!win32.symbols.GetClientRect(windowHandle, ptr(client))) return false;
|
|
89
|
+
const size = (((client[3]! - client[1]!) & 0xffff) << 16) | ((client[2]! - client[0]!) & 0xffff);
|
|
90
|
+
return win32.symbols.PostMessageW(windowHandle, WM_SIZE, 0n, BigInt(size >>> 0));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const WM_NCLBUTTONDOWN = 0x00a1;
|
|
94
|
+
const WM_CANCELMODE = 0x001f;
|
|
95
|
+
const HTCAPTION = 2;
|
|
96
|
+
/** GUITHREADINFO on 64-bit Windows: two DWORDs, six window handles, a RECT. */
|
|
97
|
+
const GUI_THREAD_INFO_SIZE = 72;
|
|
98
|
+
const GUI_THREAD_INFO_CAPTURE_OFFSET = 24;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The window holding the mouse on `windowHandle`'s thread, if any. Chromium
|
|
102
|
+
* takes the mouse on every press, and Windows will not start a move while
|
|
103
|
+
* another window holds it.
|
|
104
|
+
*/
|
|
105
|
+
function mouseCaptureOnThreadOf(win32: Win32, windowHandle: Pointer): Pointer | null {
|
|
106
|
+
const thread = win32.symbols.GetWindowThreadProcessId(windowHandle, null);
|
|
107
|
+
if (!thread) return null;
|
|
108
|
+
const info = new DataView(new ArrayBuffer(GUI_THREAD_INFO_SIZE));
|
|
109
|
+
info.setUint32(0, GUI_THREAD_INFO_SIZE, true);
|
|
110
|
+
if (!win32.symbols.GetGUIThreadInfo(thread, ptr(info))) return null;
|
|
111
|
+
const capture = Number(info.getBigUint64(GUI_THREAD_INFO_CAPTURE_OFFSET, true));
|
|
112
|
+
return capture ? capture as Pointer : null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Hands a press on the header to Windows as a press on a native title bar, from
|
|
117
|
+
* where the pointer is. Its move loop then moves the window until the button
|
|
118
|
+
* comes up: it snaps to the edges and corners of the screen and comes out of
|
|
119
|
+
* maximized, which a window moved by hand does not. Electrobun's own mover
|
|
120
|
+
* only moves. False off Windows.
|
|
121
|
+
*
|
|
122
|
+
* The page holds the mouse from the press, so it is asked to let go first, as
|
|
123
|
+
* a native title bar's window would have. Both messages go to the window's
|
|
124
|
+
* thread, which handles them in order.
|
|
125
|
+
*/
|
|
126
|
+
export function startSystemWindowMove(windowHandle: Pointer): boolean {
|
|
127
|
+
const win32 = win32OrNull();
|
|
128
|
+
if (!win32) return false;
|
|
129
|
+
const point = new Int32Array(2);
|
|
130
|
+
if (!win32.symbols.GetCursorPos(ptr(point))) return false;
|
|
131
|
+
const capture = mouseCaptureOnThreadOf(win32, windowHandle);
|
|
132
|
+
if (capture && capture !== windowHandle) win32.symbols.PostMessageW(capture, WM_CANCELMODE, 0n, 0n);
|
|
133
|
+
const position = ((point[1]! & 0xffff) << 16) | (point[0]! & 0xffff);
|
|
134
|
+
return win32.symbols.PostMessageW(windowHandle, WM_NCLBUTTONDOWN, BigInt(HTCAPTION), BigInt(position >>> 0));
|
|
135
|
+
}
|
|
136
|
+
|
|
59
137
|
export function wideString(value: string): Uint8Array {
|
|
60
138
|
return Uint8Array.from(Buffer.from(`${value}\0`, "utf16le"));
|
|
61
139
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { dlopen, FFIType, ptr, type Pointer } from "bun:ffi";
|
|
2
|
+
import Electrobun from "electrobun/bun";
|
|
3
|
+
import { wideString } from "./windows-native";
|
|
4
|
+
|
|
5
|
+
const WM_QUIT = 0x0012;
|
|
6
|
+
/** The class Electrobun registers for its windows. */
|
|
7
|
+
const WINDOW_CLASS = "BasicWindowClass";
|
|
8
|
+
/** How long a quit waits for this app's windows to be destroyed. */
|
|
9
|
+
const WINDOWS_CLOSED_TIMEOUT_MS = 1_000;
|
|
10
|
+
|
|
11
|
+
function loadUser32() {
|
|
12
|
+
return dlopen("user32.dll", {
|
|
13
|
+
GetWindowThreadProcessId: { args: [FFIType.ptr, FFIType.ptr], returns: FFIType.u32 },
|
|
14
|
+
FindWindowExW: { args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr], returns: FFIType.ptr },
|
|
15
|
+
PostThreadMessageW: { args: [FFIType.u32, FFIType.u32, FFIType.u64, FFIType.i64], returns: FFIType.bool },
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Electrobun ends its Windows event loop with PostQuitMessage, from the thread
|
|
21
|
+
* the app's code runs on rather than the one running the loop. The loop never
|
|
22
|
+
* ends: the app sits out a 5 s timeout with Chromium never shut down, and a
|
|
23
|
+
* Gloom opened meanwhile finds Chromium's profile still held, hands itself to
|
|
24
|
+
* the closing app and crashes. The quit message goes to the loop's own thread
|
|
25
|
+
* instead, once this app's windows are gone; with one still open, Electrobun's
|
|
26
|
+
* timeout ends the app as before. Does nothing off Windows.
|
|
27
|
+
*/
|
|
28
|
+
export function quitWindowsEventLoopOnItsThread(window: { ptr: Pointer | null }): void {
|
|
29
|
+
if (process.platform !== "win32" || !window.ptr) return;
|
|
30
|
+
let user32: ReturnType<typeof loadUser32>;
|
|
31
|
+
try {
|
|
32
|
+
user32 = loadUser32();
|
|
33
|
+
} catch {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const loopThread = user32.symbols.GetWindowThreadProcessId(window.ptr, null);
|
|
37
|
+
if (!loopThread) return;
|
|
38
|
+
const className = wideString(WINDOW_CLASS);
|
|
39
|
+
const processId = new Uint32Array(1);
|
|
40
|
+
const ownWindowOpen = (): boolean => {
|
|
41
|
+
let handle: Pointer | null = null;
|
|
42
|
+
while ((handle = user32.symbols.FindWindowExW(null, handle, ptr(className), null))) {
|
|
43
|
+
user32.symbols.GetWindowThreadProcessId(handle, ptr(processId));
|
|
44
|
+
if (processId[0] === process.pid) return true;
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
};
|
|
48
|
+
Electrobun.events.on("before-quit", () => {
|
|
49
|
+
// Quitting blocks this thread until the loop has ended, so the wait is here.
|
|
50
|
+
const deadline = Date.now() + WINDOWS_CLOSED_TIMEOUT_MS;
|
|
51
|
+
while (ownWindowOpen()) {
|
|
52
|
+
if (Date.now() > deadline) return;
|
|
53
|
+
Bun.sleepSync(16);
|
|
54
|
+
}
|
|
55
|
+
user32.symbols.PostThreadMessageW(loopThread, WM_QUIT, 0n, 0n);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -15,3 +15,16 @@ export const LEGACY_DESKTOP_APP_NAME = "Gloomberb";
|
|
|
15
15
|
* to it.
|
|
16
16
|
*/
|
|
17
17
|
export const DESKTOP_APP_IDENTIFIER = "com.vincelwt.gloomberb";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The identifiers a build of this app carries: the one above, and the one a
|
|
21
|
+
* later release moves to. Until every install has moved, either one can be
|
|
22
|
+
* on disk (an app bundle, an install the script or Homebrew replaces), so
|
|
23
|
+
* anything that asks whether a copy is this app accepts both.
|
|
24
|
+
*/
|
|
25
|
+
export const DESKTOP_APP_IDENTIFIERS: readonly string[] = [DESKTOP_APP_IDENTIFIER, "sh.gloom.desktop"];
|
|
26
|
+
|
|
27
|
+
/** Whether `identifier` is one a build of this app carries. */
|
|
28
|
+
export function isDesktopAppIdentifier(identifier: string | null | undefined): boolean {
|
|
29
|
+
return !!identifier && DESKTOP_APP_IDENTIFIERS.includes(identifier);
|
|
30
|
+
}
|
|
@@ -10,6 +10,7 @@ import type { CapabilityManifest } from "../../../capabilities";
|
|
|
10
10
|
import type { PluginOperationResult, PluginPin } from "../../../plugins/marketplace/store";
|
|
11
11
|
import type { RemoteControlRequest, RemoteControlResponse } from "../../../remote/types";
|
|
12
12
|
import type { HttpProxyRequestEnvelope, HttpProxyResponseEnvelope } from "../../../utils/http-proxy-response";
|
|
13
|
+
import type { StallEvent } from "../../../utils/main-thread-stall";
|
|
13
14
|
import type { BootFrame } from "../../dom/boot-frame-page";
|
|
14
15
|
|
|
15
16
|
export const ELECTROBUN_CONTEXT_MENU_ACTION = "gloom.context-menu.select";
|
|
@@ -32,8 +33,13 @@ export interface ElectrobunBackendInit {
|
|
|
32
33
|
pluginSeedNotice?: { body: string; type: "error" | "info" };
|
|
33
34
|
/** What the view needs to send crash reports and usage counts the same way the Bun process would. */
|
|
34
35
|
telemetry: ElectrobunTelemetryInit;
|
|
36
|
+
/** The copy on disk of the ids the view keeps in its own storage (view/view-ids.ts). */
|
|
37
|
+
viewIds: DesktopViewIds;
|
|
35
38
|
}
|
|
36
39
|
|
|
40
|
+
/** Ids the view keeps in its WebView storage, by storage key. */
|
|
41
|
+
export type DesktopViewIds = Record<string, string>;
|
|
42
|
+
|
|
37
43
|
interface ElectrobunTelemetryInit {
|
|
38
44
|
/** Random per-install id from `<dataDir>/install-id`; null when the file could not be written. */
|
|
39
45
|
installId: string | null;
|
|
@@ -206,6 +212,8 @@ interface DesktopBackendRequestMap {
|
|
|
206
212
|
"host.exit": { request: null; response: null };
|
|
207
213
|
"host.windowControl": { request: { action: DesktopWindowControlAction }; response: null };
|
|
208
214
|
"host.windowFullscreen": { request: null; response: boolean };
|
|
215
|
+
/** Windows: moves the window with the pointer through the system's own move loop. */
|
|
216
|
+
"host.startWindowDrag": { request: null; response: null };
|
|
209
217
|
"host.openExternal": { request: { url: string }; response: boolean };
|
|
210
218
|
"host.copyText": { request: { text: string }; response: null };
|
|
211
219
|
"host.focusWindow": { request: null; response: null };
|
|
@@ -222,6 +230,8 @@ interface DesktopBackendRequestMap {
|
|
|
222
230
|
"host.showContextMenu": { request: { menu: DesktopContextMenuItem[] }; response: boolean };
|
|
223
231
|
/** The layout the main window draws at the next launch before its script loads (dom/boot-frame.ts). Only the main window's is kept. */
|
|
224
232
|
"host.saveBootFrame": { request: { frame: BootFrame }; response: null };
|
|
233
|
+
/** The ids the view keeps in its storage, for the copy on disk. Only the main window's are kept. */
|
|
234
|
+
"host.saveViewIds": { request: { ids: DesktopViewIds }; response: null };
|
|
225
235
|
/** The unread count for the Dock icon badge: `""` (none), `"1"` to `"99"` or `"99+"`. Only the main window's is applied. */
|
|
226
236
|
"host.setAppBadge": { request: { label: string }; response: null };
|
|
227
237
|
/** `channel` absent (an older view) means stable. */
|
|
@@ -337,6 +347,8 @@ export interface ElectrobunDesktopRpcSchema {
|
|
|
337
347
|
};
|
|
338
348
|
messages: {
|
|
339
349
|
"host.restart": DesktopRestartMessage;
|
|
350
|
+
/** A window froze; the Bun process keeps the stall log and its report. */
|
|
351
|
+
"runtime.stall": StallEvent;
|
|
340
352
|
};
|
|
341
353
|
};
|
|
342
354
|
webview: {
|
|
@@ -17,6 +17,7 @@ import { subscribeCapability, type CapabilitySubscriptionOptions } from "./capab
|
|
|
17
17
|
import { RpcTimeoutError } from "../../../utils/rpc-timeout-error";
|
|
18
18
|
import { nameRpcTimeout, RPC_MAX_REQUEST_TIME_MS } from "./rpc-timeout";
|
|
19
19
|
import type { RemoteControlRequest, RemoteControlResponse } from "../../../remote/types";
|
|
20
|
+
import type { StallEvent } from "../../../utils/main-thread-stall";
|
|
20
21
|
|
|
21
22
|
type BackendMessages = ElectrobunDesktopRpcSchema["webview"]["messages"];
|
|
22
23
|
type BackendMessageName = keyof BackendMessages;
|
|
@@ -153,6 +154,11 @@ export function requestElectrobunRestart(message: DesktopRestartMessage = {}): v
|
|
|
153
154
|
rpc.send["host.restart"](message);
|
|
154
155
|
}
|
|
155
156
|
|
|
157
|
+
/** The Bun process keeps the stall log and sends its report. */
|
|
158
|
+
export function sendElectrobunStall(event: StallEvent): void {
|
|
159
|
+
rpc.send["runtime.stall"](event);
|
|
160
|
+
}
|
|
161
|
+
|
|
156
162
|
export function getElectrobunBackendInitSnapshot(): ElectrobunBackendInit | null {
|
|
157
163
|
return initSnapshot;
|
|
158
164
|
}
|
|
@@ -5,12 +5,14 @@ import { App } from "../../../app";
|
|
|
5
5
|
import { applyLanguageFromConfig } from "../../../i18n";
|
|
6
6
|
import { debugLog } from "../../../utils/debug-log";
|
|
7
7
|
import { capturePaneImage } from "../../../utils/dom-screenshot";
|
|
8
|
+
import { setStallSink } from "../../../utils/main-thread-stall";
|
|
8
9
|
import { measurePerfAsync } from "../../../utils/perf-marks";
|
|
9
10
|
import {
|
|
10
11
|
backendRequest,
|
|
11
12
|
initElectrobunBackend,
|
|
12
13
|
onBackendMessage,
|
|
13
14
|
replaceElectrobunCapabilityManifests,
|
|
15
|
+
sendElectrobunStall,
|
|
14
16
|
setElectrobunRemoteRequestHandler,
|
|
15
17
|
} from "./backend-rpc";
|
|
16
18
|
import { installElectrobunCapabilityStreamClient } from "./capability-stream-client";
|
|
@@ -49,10 +51,13 @@ import { loadOfficialPluginIds } from "../../../plugins/marketplace/feed";
|
|
|
49
51
|
import { setPluginSeedNotice } from "../../../plugins/seed-notice";
|
|
50
52
|
import { crashReportsEnabled, installCrashReporter, reportCrash } from "../../../telemetry/crash-reports";
|
|
51
53
|
import { browserDoNotTrack, installWindowCrashListeners } from "../../../telemetry/crash-reports-dom";
|
|
54
|
+
import { forwardAttendedStalls } from "../../../telemetry/stall-reports-dom";
|
|
52
55
|
import { currentTelemetryConfig } from "../../../telemetry/live-config";
|
|
53
56
|
import { attentionCountsEnabled, installAttentionCounter } from "../../../telemetry/attention-counts";
|
|
54
57
|
import { installUsageCounter, usageCountsEnabled } from "../../../telemetry/usage-counts";
|
|
55
58
|
import { installWindowUsageFlush } from "../../../telemetry/usage-counts-dom";
|
|
59
|
+
import { cloudSyncController } from "../../../sync/controller";
|
|
60
|
+
import { createViewIdMirror, restoreViewIds, viewIdStorage } from "./view-ids";
|
|
56
61
|
|
|
57
62
|
// Declared here rather than sniffed: the desktop view and the hosted browser
|
|
58
63
|
// app are both browser contexts but differ in what plugins may do.
|
|
@@ -156,6 +161,8 @@ async function boot() {
|
|
|
156
161
|
officialPluginIds: loadOfficialPluginIds,
|
|
157
162
|
});
|
|
158
163
|
installAttentionCounter(() => !init.telemetry.optedOut && !browserDoNotTrack() && attentionCountsEnabled(currentTelemetryConfig(init.config)));
|
|
164
|
+
// Desktop has no Debug Log: this window's freezes go to the Bun process, which keeps the stall log.
|
|
165
|
+
setStallSink(forwardAttendedStalls(sendElectrobunStall).sink);
|
|
159
166
|
installElectrobunCapabilityStreamClient();
|
|
160
167
|
installFocusScopeRelease();
|
|
161
168
|
installElectrobunWindowFullscreenTracking();
|
|
@@ -173,10 +180,26 @@ async function boot() {
|
|
|
173
180
|
const desktopApplicationMenuBridge: DesktopApplicationMenuBridge = {
|
|
174
181
|
subscribe: (listener) => onBackendMessage("application-menu.select", ({ command }) => listener(command)),
|
|
175
182
|
};
|
|
183
|
+
// Before anything reads them: ids this view's storage lacks come back from
|
|
184
|
+
// the copy on disk, and the main window keeps that copy up to date. The sync
|
|
185
|
+
// id is made at the first push, and a gloom.sh link can bring a visitor id.
|
|
186
|
+
const viewStorage = viewIdStorage();
|
|
187
|
+
if (viewStorage) restoreViewIds(viewStorage, init.viewIds);
|
|
188
|
+
const mirrorViewIds = viewStorage && init.windowKind === "main"
|
|
189
|
+
? createViewIdMirror(viewStorage, init.viewIds, (ids) => backendRequest("host.saveViewIds", { ids }))
|
|
190
|
+
: () => {};
|
|
191
|
+
mirrorViewIds();
|
|
192
|
+
cloudSyncController.subscribe(mirrorViewIds);
|
|
176
193
|
initializeDesktopResearchActivity();
|
|
177
|
-
const
|
|
194
|
+
const observedDeepLinks = observeDesktopDeepLinks({
|
|
178
195
|
subscribe: (listener) => onBackendMessage("desktop.deepLink", listener),
|
|
179
196
|
});
|
|
197
|
+
const desktopDeepLinkBridge: typeof observedDeepLinks = {
|
|
198
|
+
subscribe: (listener) => observedDeepLinks.subscribe((deeplink) => {
|
|
199
|
+
listener(deeplink);
|
|
200
|
+
mirrorViewIds();
|
|
201
|
+
}),
|
|
202
|
+
};
|
|
180
203
|
const webUiHost = createWebUiHost(init.desktopPlatform);
|
|
181
204
|
// Compiled by the Bun process, which owns the filesystem. A failure here must
|
|
182
205
|
// not stop the app from starting: the marketplace reports broken plugins, and
|
|
@@ -2,6 +2,7 @@ import { apiClient } from "../../../api-client";
|
|
|
2
2
|
import { newsProvider, type NewsCapability } from "../../../capabilities";
|
|
3
3
|
import { cloudNewsReadArticles, mapCloudNewsArticle, readCloudNews } from "../../../sources/gloomberb-cloud/news";
|
|
4
4
|
import type { NewsArticle, NewsPage, NewsQuery } from "../../../news/types";
|
|
5
|
+
import { readCompanyFrom } from "../../../types/company-from";
|
|
5
6
|
|
|
6
7
|
interface BackendNews {
|
|
7
8
|
id: string;
|
|
@@ -10,7 +11,8 @@ interface BackendNews {
|
|
|
10
11
|
getNewsPage(query: NewsQuery): Promise<NewsPage>;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
type CloudNewsClient = Pick<typeof apiClient, "getCloudNews" | "getCloudNewsStory" | "searchCloudNews"
|
|
14
|
+
type CloudNewsClient = Pick<typeof apiClient, "getCloudNews" | "getCloudNewsStory" | "searchCloudNews">
|
|
15
|
+
& Partial<Pick<typeof apiClient, "hasSessionCredential">>;
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
18
|
* The view's only news source: the desktop view runs without capability
|
|
@@ -32,6 +34,8 @@ export function createDesktopNewsSource(backend: BackendNews, client: CloudNewsC
|
|
|
32
34
|
return {
|
|
33
35
|
articles: cloudNewsReadArticles(read, query.ticker),
|
|
34
36
|
nextCursor: read.nextCursor ?? null,
|
|
37
|
+
access: read.access ?? null,
|
|
38
|
+
companyFrom: readCompanyFrom(read.companyFrom),
|
|
35
39
|
};
|
|
36
40
|
} catch {
|
|
37
41
|
// The aggregate page keeps RSS failures beside any usable stories.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TickerMetadata, TickerRecord } from "../../../../types/ticker";
|
|
2
2
|
import { backendRequest } from "../backend-rpc";
|
|
3
|
+
import { runTickerWrite } from "../../../../data/ticker-writes";
|
|
3
4
|
|
|
4
5
|
export class RemoteTickerRepository {
|
|
5
6
|
async loadAllTickers(): Promise<TickerRecord[]> {
|
|
@@ -10,17 +11,23 @@ export class RemoteTickerRepository {
|
|
|
10
11
|
return backendRequest("ticker.load", { symbol });
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
saveTicker(ticker: TickerRecord): Promise<void> {
|
|
15
|
+
return runTickerWrite(this, ticker.metadata.ticker, async () => {
|
|
16
|
+
await backendRequest("ticker.save", { ticker });
|
|
17
|
+
});
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
createTicker(metadata: TickerMetadata): Promise<TickerRecord> {
|
|
18
21
|
const ticker: TickerRecord = { metadata };
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
return runTickerWrite(this, metadata.ticker, async () => {
|
|
23
|
+
await backendRequest("ticker.save", { ticker });
|
|
24
|
+
return ticker;
|
|
25
|
+
});
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
deleteTicker(symbol: string): Promise<void> {
|
|
29
|
+
return runTickerWrite(this, symbol, async () => {
|
|
30
|
+
await backendRequest("ticker.delete", { symbol });
|
|
31
|
+
});
|
|
25
32
|
}
|
|
26
33
|
}
|
|
@@ -5,6 +5,7 @@ import { backendRequest } from "./backend-rpc";
|
|
|
5
5
|
import { NATIVE_CONTEXT_MENU_SUPPORTED, showDesktopContextMenu } from "./host/native";
|
|
6
6
|
import { startElectrobunWindowDrag } from "./host/window-move";
|
|
7
7
|
import { createDomUiHost } from "../../dom/dom-ui-host";
|
|
8
|
+
import { detectPlatform } from "../../../utils/platform";
|
|
8
9
|
|
|
9
10
|
export function createWebUiHost(desktopPlatform?: string): UiHost {
|
|
10
11
|
return createDomUiHost(desktopPlatform, {
|
|
@@ -25,6 +26,13 @@ export const webRendererHost: RendererHost = {
|
|
|
25
26
|
});
|
|
26
27
|
},
|
|
27
28
|
startWindowDrag() {
|
|
29
|
+
// Windows moves the window itself, as it does from a native title bar.
|
|
30
|
+
if (detectPlatform() === "win32") {
|
|
31
|
+
void backendRequest("host.startWindowDrag").catch((error: unknown) => {
|
|
32
|
+
console.error("[desktop] could not start moving the window", error instanceof Error ? error.message : String(error));
|
|
33
|
+
});
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
28
36
|
startElectrobunWindowDrag();
|
|
29
37
|
},
|
|
30
38
|
async controlWindow(action) {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ANONYMOUS_ID_STORAGE_KEY, ATTRIBUTION_STORAGE_KEY } from "../../../api-client/research-activity";
|
|
2
|
+
import { SYNC_CLIENT_ID_STORAGE_KEY } from "../../../sync/controller";
|
|
3
|
+
import { logFailure } from "../../../utils/log-failure";
|
|
4
|
+
import type { DesktopViewIds } from "../shared/protocol";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What the desktop view keeps only in its WebView storage: the id Cloud sync
|
|
8
|
+
* knows this device by, and the visitor id and first touch a gloom.sh link
|
|
9
|
+
* handed over. macOS and Windows keep that storage under the app's bundle
|
|
10
|
+
* identifier, so a build under another identifier would start without them.
|
|
11
|
+
* The Bun process keeps a copy in the data folder (bun/desktop/view-ids.ts),
|
|
12
|
+
* and a view whose storage lacks one takes it back from there.
|
|
13
|
+
*/
|
|
14
|
+
const VIEW_ID_KEYS = [SYNC_CLIENT_ID_STORAGE_KEY, ANONYMOUS_ID_STORAGE_KEY, ATTRIBUTION_STORAGE_KEY] as const;
|
|
15
|
+
|
|
16
|
+
interface ViewIdStorage {
|
|
17
|
+
getItem(key: string): string | null;
|
|
18
|
+
setItem(key: string, value: string): void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** The view's WebView storage, or null where it is refused. */
|
|
22
|
+
export function viewIdStorage(): ViewIdStorage | null {
|
|
23
|
+
try {
|
|
24
|
+
return globalThis.localStorage ?? null;
|
|
25
|
+
} catch {
|
|
26
|
+
// Intentional ignore: without storage there is nothing to restore or keep.
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readViewIds(storage: ViewIdStorage): DesktopViewIds {
|
|
32
|
+
const ids: DesktopViewIds = {};
|
|
33
|
+
for (const key of VIEW_ID_KEYS) {
|
|
34
|
+
const value = storage.getItem(key);
|
|
35
|
+
if (value) ids[key] = value;
|
|
36
|
+
}
|
|
37
|
+
return ids;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function sameIds(left: DesktopViewIds, right: DesktopViewIds): boolean {
|
|
41
|
+
const keys = Object.keys(left);
|
|
42
|
+
return keys.length === Object.keys(right).length && keys.every((key) => left[key] === right[key]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Puts the ids kept on disk into `storage` where it has none. One it has is never replaced. */
|
|
46
|
+
export function restoreViewIds(storage: ViewIdStorage, kept: DesktopViewIds | undefined): void {
|
|
47
|
+
try {
|
|
48
|
+
for (const key of VIEW_ID_KEYS) {
|
|
49
|
+
const value = kept?.[key];
|
|
50
|
+
if (value && !storage.getItem(key)) storage.setItem(key, value);
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logFailure("desktop-view-ids", "Could not restore the view's ids")(error);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A function that sends the ids in `storage` to be kept on disk when they
|
|
59
|
+
* differ from the copy there (`kept`, from `init`). It reads three keys, so it
|
|
60
|
+
* can run whenever one of them may have been written. A save that fails is
|
|
61
|
+
* tried again at the next call.
|
|
62
|
+
*/
|
|
63
|
+
export function createViewIdMirror(
|
|
64
|
+
storage: ViewIdStorage,
|
|
65
|
+
kept: DesktopViewIds | undefined,
|
|
66
|
+
save: (ids: DesktopViewIds) => Promise<unknown>,
|
|
67
|
+
): () => void {
|
|
68
|
+
let onDisk: DesktopViewIds = { ...kept };
|
|
69
|
+
return () => {
|
|
70
|
+
let ids: DesktopViewIds;
|
|
71
|
+
try {
|
|
72
|
+
ids = readViewIds(storage);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
logFailure("desktop-view-ids", "Could not read the view's ids")(error);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (sameIds(ids, onDisk)) return;
|
|
78
|
+
const previous = onDisk;
|
|
79
|
+
onDisk = ids;
|
|
80
|
+
save(ids).catch((error: unknown) => {
|
|
81
|
+
if (onDisk === ids) onDisk = previous;
|
|
82
|
+
logFailure("desktop-view-ids", "Could not send the view's ids to be kept")(error);
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -38,6 +38,9 @@ import {
|
|
|
38
38
|
} from "../../telemetry/crash-reports-node";
|
|
39
39
|
import { currentTelemetryConfig } from "../../telemetry/live-config";
|
|
40
40
|
import { installUpdateFailureReporter } from "../../telemetry/update-failures";
|
|
41
|
+
import { installStallReporter, recordStall } from "../../telemetry/stall-reports";
|
|
42
|
+
import { createNodeStallReporterHost } from "../../telemetry/stall-reports-node";
|
|
43
|
+
import { setStallSink } from "../../utils/main-thread-stall";
|
|
41
44
|
import { createNodeUpdateFailureHost } from "../../telemetry/update-failures-node";
|
|
42
45
|
import { terminalInstallKind } from "../../updater";
|
|
43
46
|
import { flushUsageCounts, installUsageCounter, usageCountsEnabled } from "../../telemetry/usage-counts";
|
|
@@ -124,6 +127,8 @@ export async function startOpenTuiApp({ externalPlugins, cliLaunchRequest }: Sta
|
|
|
124
127
|
installKind: terminalInstallKind(),
|
|
125
128
|
getDataDir: () => config.dataDir,
|
|
126
129
|
}));
|
|
130
|
+
installStallReporter(createNodeStallReporterHost({ getDataDir: () => config.dataDir }));
|
|
131
|
+
setStallSink((event) => recordStall("terminal", event));
|
|
127
132
|
installUsageCounter({
|
|
128
133
|
surface: "terminal",
|
|
129
134
|
os: describeNodeOs(),
|
package/src/series/registry.ts
CHANGED
|
@@ -63,6 +63,7 @@ export function seriesCatalogEntry(series: RegisteredSeries): SeriesCatalogEntry
|
|
|
63
63
|
...(definition.description ? { description: definition.description } : {}),
|
|
64
64
|
...(definition.publisher ? { publisher: definition.publisher } : {}),
|
|
65
65
|
...(definition.url ? { url: definition.url } : {}),
|
|
66
|
+
...(definition.limitation ? { limitation: definition.limitation } : {}),
|
|
66
67
|
...(definition.input ? { input: definition.input } : {}),
|
|
67
68
|
...(definition.placement ? { placement: definition.placement } : {}),
|
|
68
69
|
...(definition.lines?.length ? {
|
package/src/series/types.ts
CHANGED
|
@@ -133,6 +133,11 @@ interface SeriesDefinitionBase {
|
|
|
133
133
|
publisher?: string;
|
|
134
134
|
/** The publisher's page for the series. */
|
|
135
135
|
url?: string;
|
|
136
|
+
/**
|
|
137
|
+
* A limit on the data the publisher sets, such as a cap on how far back its
|
|
138
|
+
* history goes. One sentence: the catalog lists it beside the series.
|
|
139
|
+
*/
|
|
140
|
+
limitation?: string;
|
|
136
141
|
/** Ask for a ticker, an option or a crypto pair before charting. */
|
|
137
142
|
input?: SeriesInput;
|
|
138
143
|
/**
|
|
@@ -203,6 +208,7 @@ export interface SeriesCatalogEntry {
|
|
|
203
208
|
description?: string;
|
|
204
209
|
publisher?: string;
|
|
205
210
|
url?: string;
|
|
211
|
+
limitation?: string;
|
|
206
212
|
input?: SeriesInput;
|
|
207
213
|
placement?: SeriesPlacement;
|
|
208
214
|
/** The lines of a series that draws several, each with the expression that charts it alone. */
|
package/src/series/validate.ts
CHANGED
|
@@ -136,6 +136,7 @@ export function validateSeriesDefinition(definition: SeriesDefinition, context:
|
|
|
136
136
|
checkLabel(id, "group", definition.group, 32, context);
|
|
137
137
|
checkLabel(id, "description", definition.description, 300, context);
|
|
138
138
|
checkLabel(id, "publisher", definition.publisher, 60, context);
|
|
139
|
+
checkLabel(id, "limitation", definition.limitation, 160, context);
|
|
139
140
|
if (definition.tags !== undefined) {
|
|
140
141
|
if (!Array.isArray(definition.tags) || definition.tags.length > 16) fail(id, "tags must be a list of at most 16 words.");
|
|
141
142
|
for (const tag of definition.tags) checkLabel(id, "tag", tag, 40, context, false);
|
|
@@ -42,6 +42,8 @@ export interface FredSeriesCacheEntry {
|
|
|
42
42
|
export interface FredSeriesLoadResult extends FredSeriesCacheEntry {
|
|
43
43
|
source: "cache" | "network" | "stale-fallback";
|
|
44
44
|
refreshError?: string;
|
|
45
|
+
/** What the failed refresh threw, when a saved copy answered (`stale-fallback`). */
|
|
46
|
+
error?: unknown;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
const cache = createPluginCache<FredSeriesData>({
|
|
@@ -197,6 +199,6 @@ export async function loadCachedFredSeries(
|
|
|
197
199
|
if (!options?.force && !pending && cached && !cached.stale) return { ...cached, source: "cache" };
|
|
198
200
|
const result = await cache.load(cacheKey(request), loader, pending ? { ...options, force: true } : options);
|
|
199
201
|
// An early re-read is opportunistic: a failure keeps the still-fresh copy as it was.
|
|
200
|
-
if (pending && result.
|
|
202
|
+
if (pending && result.source === "stale-fallback" && cached && !cached.stale && !options?.force) return { ...cached, source: "cache" };
|
|
201
203
|
return withFredSourceFreshness(result);
|
|
202
204
|
}
|