@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
|
@@ -40,7 +40,6 @@ import {
|
|
|
40
40
|
DEFAULT_ISSUER_SORT,
|
|
41
41
|
DEFAULT_TRADE_SORT,
|
|
42
42
|
ISSUER_SORT_COLUMN_IDS,
|
|
43
|
-
TRADE_SORT_COLUMN_IDS,
|
|
44
43
|
buildIssuerColumns,
|
|
45
44
|
buildTradeColumns,
|
|
46
45
|
cdsFreshness,
|
|
@@ -48,9 +47,11 @@ import {
|
|
|
48
47
|
formatEventTime,
|
|
49
48
|
formatMaturity,
|
|
50
49
|
formatNotional,
|
|
50
|
+
formatPoints,
|
|
51
51
|
formatQuoted,
|
|
52
52
|
formatQuotedMove,
|
|
53
53
|
formatUpfront,
|
|
54
|
+
hasPointsQuotes,
|
|
54
55
|
issuerListSort,
|
|
55
56
|
lastTradeOf,
|
|
56
57
|
noLevelMessage,
|
|
@@ -63,6 +64,7 @@ import {
|
|
|
63
64
|
spreadChartPoints,
|
|
64
65
|
spreadFigures,
|
|
65
66
|
summarizeIssuers,
|
|
67
|
+
tradeSortColumnIds,
|
|
66
68
|
tradesForIssuer,
|
|
67
69
|
withSpreadLevels,
|
|
68
70
|
type CdsIssuerSummary,
|
|
@@ -138,6 +140,11 @@ function renderTradeCell(row: CdsTrade, column: TradeColumn, lineMaturity: strin
|
|
|
138
140
|
text: formatBp(row.spreadBp),
|
|
139
141
|
color: dimmed ?? (row.spreadBp == null ? colors.textDim : colors.textBright),
|
|
140
142
|
};
|
|
143
|
+
case "points":
|
|
144
|
+
return {
|
|
145
|
+
text: formatPoints(row.pointsUpfront),
|
|
146
|
+
color: dimmed ?? (row.pointsUpfront == null ? colors.textDim : colors.textBright),
|
|
147
|
+
};
|
|
141
148
|
case "upfront":
|
|
142
149
|
return {
|
|
143
150
|
text: formatUpfront(row),
|
|
@@ -175,7 +182,8 @@ function CdsTradeTable({
|
|
|
175
182
|
/** What an empty table says: the window and the name searched. */
|
|
176
183
|
emptyMessage: string;
|
|
177
184
|
}) {
|
|
178
|
-
const
|
|
185
|
+
const points = hasPointsQuotes(trades);
|
|
186
|
+
const columns = useMemo(() => buildTradeColumns(width, points), [points, width]);
|
|
179
187
|
const renderTradeRow = useCallback((trade: CdsTrade, column: TradeColumn) => (
|
|
180
188
|
renderTradeCell(trade, column, lineMaturity)
|
|
181
189
|
), [lineMaturity]);
|
|
@@ -276,7 +284,7 @@ function IssuerHeader({ issuer, line, trades, width, height, focused, loading, s
|
|
|
276
284
|
});
|
|
277
285
|
return (
|
|
278
286
|
<ChartTableHeader width={width} height={height} tableRows={trades.length} figures={figures}
|
|
279
|
-
tableColumns={buildTradeColumns(width)}
|
|
287
|
+
tableColumns={buildTradeColumns(width, hasPointsQuotes(trades))}
|
|
280
288
|
chart={points.length >= 2 || loading || why ? {
|
|
281
289
|
series,
|
|
282
290
|
formatValue: (value: number) => formatQuoted(unit, value),
|
|
@@ -388,12 +396,13 @@ export function CdsPane({
|
|
|
388
396
|
setDetailOpen(false);
|
|
389
397
|
}, [issuerQuery, issuers, selectedIssuerKey]);
|
|
390
398
|
|
|
399
|
+
const tradePoints = hasPointsQuotes(visibleTrades);
|
|
391
400
|
const cycleTradeSort = useCallback((step: 1 | -1) => {
|
|
392
401
|
setTradeSort((current) => {
|
|
393
|
-
const next = cycleSortPreference<TradeColumnId>(
|
|
402
|
+
const next = cycleSortPreference<TradeColumnId>(tradeSortColumnIds(tradePoints), current, step);
|
|
394
403
|
return { columnId: next.columnId ?? DEFAULT_TRADE_SORT.columnId, direction: next.direction };
|
|
395
404
|
});
|
|
396
|
-
}, []);
|
|
405
|
+
}, [tradePoints]);
|
|
397
406
|
const cycleIssuerSort = useCallback((step: 1 | -1) => {
|
|
398
407
|
setIssuerSort((current) => {
|
|
399
408
|
const next = cycleSortPreference<IssuerColumnId>(ISSUER_SORT_COLUMN_IDS, current, step);
|
|
@@ -451,6 +460,8 @@ export function CdsPane({
|
|
|
451
460
|
query: issuerQuery ? activity?.query ?? issuerQuery : selectedSummary?.issuer ?? null,
|
|
452
461
|
issuer: issuerQuery ? activity?.issuer ?? null : selectedSummary?.issuer ?? null,
|
|
453
462
|
days: activity?.days ?? CDS_TRADE_DAYS,
|
|
463
|
+
// A name the covered issuers do not have, once the tape has neither trades nor a line under it.
|
|
464
|
+
unmatched: issuerQuery && activity?.unmatched && !trades.length && history.data && !history.data.issuer ? activity.unmatched : null,
|
|
454
465
|
});
|
|
455
466
|
|
|
456
467
|
if (status === "loading" && !activity) {
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "../../../components";
|
|
12
12
|
import { DetailReader } from "../../../components/reader/detail-reader";
|
|
13
13
|
import { loadingErrorFooterInfo, usePaneRefreshKey } from "../../../components/data-table/table-pane";
|
|
14
|
+
import { createTtlCache } from "../../../data/ttl-cache";
|
|
14
15
|
import { fetchChangelogReleases, type ChangelogRelease } from "../../../updater/github-releases";
|
|
15
16
|
import { colors } from "../../../theme/colors";
|
|
16
17
|
import type { PaneProps } from "../../../types/plugin";
|
|
@@ -32,7 +33,8 @@ const CHANGELOG_CACHE_TTL_MS = 10 * 60 * 1000;
|
|
|
32
33
|
|
|
33
34
|
// Process-lifetime cache. Move it into the updater module if other
|
|
34
35
|
// surfaces start reading releases too.
|
|
35
|
-
|
|
36
|
+
const releaseCache = createTtlCache<ChangelogRelease[]>({ ttlMs: CHANGELOG_CACHE_TTL_MS });
|
|
37
|
+
const RELEASES = "releases";
|
|
36
38
|
|
|
37
39
|
type ChangelogColumn = DataTableColumn & { id: ChangelogColumnId };
|
|
38
40
|
type LoadStatus = "loading" | "loaded" | "error";
|
|
@@ -83,10 +85,10 @@ function ChangelogDetail({
|
|
|
83
85
|
function ChangelogPane({ focused, width, height }: PaneProps) {
|
|
84
86
|
const paneInstance = usePaneInstance();
|
|
85
87
|
const requestedVersion = paneInstance?.params?.version ?? null;
|
|
86
|
-
const [releases, setReleases] = useState<ChangelogRelease[]>(() =>
|
|
88
|
+
const [releases, setReleases] = useState<ChangelogRelease[]>(() => releaseCache.peek(RELEASES, { stale: true }) ?? []);
|
|
87
89
|
// The mount effect starts the request immediately, so the first paint is a
|
|
88
90
|
// spinner and never "No changelog entries found".
|
|
89
|
-
const [status, setStatus] = useState<LoadStatus>(() => (
|
|
91
|
+
const [status, setStatus] = useState<LoadStatus>(() => (releaseCache.peek(RELEASES, { stale: true }) ? "loaded" : "loading"));
|
|
90
92
|
const [error, setError] = useState<string | null>(null);
|
|
91
93
|
// The selected row and the open release are pane state, so a reload or a
|
|
92
94
|
// shared layout comes back to the same release.
|
|
@@ -98,8 +100,9 @@ function ChangelogPane({ focused, width, height }: PaneProps) {
|
|
|
98
100
|
const requestedVersionOpenedRef = useRef(false);
|
|
99
101
|
|
|
100
102
|
const loadReleases = useCallback(async (force = false) => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
const fresh = force ? undefined : releaseCache.peek(RELEASES);
|
|
104
|
+
if (fresh) {
|
|
105
|
+
setReleases(fresh);
|
|
103
106
|
setError(null);
|
|
104
107
|
setStatus("loaded");
|
|
105
108
|
return;
|
|
@@ -118,7 +121,7 @@ function ChangelogPane({ focused, width, height }: PaneProps) {
|
|
|
118
121
|
|
|
119
122
|
try {
|
|
120
123
|
const nextReleases = await fetchChangelogReleases(CHANGELOG_LIMIT, controller.signal);
|
|
121
|
-
|
|
124
|
+
releaseCache.set(RELEASES, nextReleases);
|
|
122
125
|
setReleases(nextReleases);
|
|
123
126
|
setStatus("loaded");
|
|
124
127
|
} catch (loadError) {
|
|
@@ -31,6 +31,8 @@ export interface CatalogSeriesRow {
|
|
|
31
31
|
/** The registered expression, `{ticker}` where a symbol goes. */
|
|
32
32
|
template: string;
|
|
33
33
|
url?: string;
|
|
34
|
+
/** A limit the publisher sets on the series' data, such as how far back its history goes. */
|
|
35
|
+
limitation?: string;
|
|
34
36
|
/** Asks for a symbol before charting. */
|
|
35
37
|
input?: SeriesInput;
|
|
36
38
|
/** The symbol a row filled in for a typed ticker charts. */
|
|
@@ -120,6 +122,7 @@ function rowFromEntry(entry: SeriesCatalogEntry): Omit<CatalogSeriesRow, "order"
|
|
|
120
122
|
expression,
|
|
121
123
|
template: entry.expression,
|
|
122
124
|
...(entry.url ? { url: entry.url } : {}),
|
|
125
|
+
...(entry.limitation ? { limitation: entry.limitation } : {}),
|
|
123
126
|
...(entry.input ? { input: entry.input } : {}),
|
|
124
127
|
...(entry.placement || entry.lines?.length ? {
|
|
125
128
|
seriesName: entry.name,
|
|
@@ -81,6 +81,10 @@ export function applyChartComposerCapabilityOptions(
|
|
|
81
81
|
next = { ...next, viewport: { ...next.viewport, extendedHours: true } };
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
if (capabilityId === "price-comparison" && options.totalReturn === true) {
|
|
85
|
+
next = { ...next, viewport: { ...next.viewport, totalReturn: true } };
|
|
86
|
+
}
|
|
87
|
+
|
|
84
88
|
if (PRICE_CAPABILITIES.has(capabilityId) && options.axisMode) {
|
|
85
89
|
const transform = options.axisMode === "percent" ? "percent" : "raw";
|
|
86
90
|
next = {
|
|
@@ -4,6 +4,7 @@ import { Box } from "../../../ui";
|
|
|
4
4
|
import {
|
|
5
5
|
DataTableView,
|
|
6
6
|
QueryBar,
|
|
7
|
+
usePaneNoticeFooter,
|
|
7
8
|
usePaneTabs,
|
|
8
9
|
useQueryBarSearch,
|
|
9
10
|
type DataTableCell,
|
|
@@ -242,6 +243,12 @@ export function DataCatalogPane({ focused, width, height }: PaneProps) {
|
|
|
242
243
|
}
|
|
243
244
|
}, []);
|
|
244
245
|
|
|
246
|
+
usePaneNoticeFooter({
|
|
247
|
+
registrationId: `${DATA_CATALOG_PANE_ID}:notices`,
|
|
248
|
+
focused,
|
|
249
|
+
notices: selectedRow?.limitation ? [`${selectedRow.label}: ${selectedRow.limitation}`] : [],
|
|
250
|
+
});
|
|
251
|
+
|
|
245
252
|
usePaneStatusLinkFooter({
|
|
246
253
|
registrationId: DATA_CATALOG_PANE_ID,
|
|
247
254
|
focused,
|
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
SeriesTimestampMode,
|
|
11
11
|
SeriesTransform,
|
|
12
12
|
} from "../../../../time-series/types";
|
|
13
|
-
import { hasStudyInputCount, validateChartSpec } from "../../../../time-series/spec";
|
|
13
|
+
import { hasStudyInputCount, isOhlcSeriesStyle, validateChartSpec } from "../../../../time-series/spec";
|
|
14
14
|
import { isPlainKey } from "../../../../utils/keyboard";
|
|
15
15
|
import { getSharedRegistry } from "../../../registry";
|
|
16
16
|
import {
|
|
@@ -415,7 +415,7 @@ export function useSeriesEditorController({
|
|
|
415
415
|
: current.panels,
|
|
416
416
|
series: replaceAt(current.series, selectedIndex, (() => {
|
|
417
417
|
const currentSeries = current.series[selectedIndex]!;
|
|
418
|
-
const ohlcStyle = currentSeries.style
|
|
418
|
+
const ohlcStyle = isOhlcSeriesStyle(currentSeries.style);
|
|
419
419
|
return {
|
|
420
420
|
...currentSeries,
|
|
421
421
|
style: transform !== "raw" && ohlcStyle
|
|
@@ -21,6 +21,16 @@ function normalized(instrument: InstrumentRef): InstrumentRef {
|
|
|
21
21
|
const key = (instrument: InstrumentRef) => instrumentIdentityKey(normalized(instrument));
|
|
22
22
|
const publicKey = (instrument: InstrumentRef) => publicTickerKey(instrument.symbol, instrument.exchange);
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* One listing however a series names it: the same symbol and broker contract, on the same venue
|
|
26
|
+
* or with the venue left out on either side (`AAPL` added beside a revenue line on `AAPL:NASDAQ`).
|
|
27
|
+
*/
|
|
28
|
+
function sameListing(left: InstrumentRef, right: InstrumentRef): boolean {
|
|
29
|
+
const a = normalized(left), b = normalized(right);
|
|
30
|
+
return key({ ...a, exchange: "" }) === key({ ...b, exchange: "" })
|
|
31
|
+
&& (!a.exchange || !b.exchange || canonicalExchange(a.exchange) === canonicalExchange(b.exchange));
|
|
32
|
+
}
|
|
33
|
+
|
|
24
34
|
/** Bootstrap old charts once; persisted IDs keep ownership through comparison collisions. */
|
|
25
35
|
export function resolveFollowSeriesIds(
|
|
26
36
|
spec: ChartSpec,
|
|
@@ -44,8 +54,10 @@ export function resolveFollowSeriesIds(
|
|
|
44
54
|
|| (next && securities.find(series => series.source.kind === "security" && publicKey(normalized(series.source.instrument)) === publicKey(next)))
|
|
45
55
|
|| securities[0];
|
|
46
56
|
if (!owner || owner.source.kind !== "security") return [];
|
|
47
|
-
const
|
|
48
|
-
|
|
57
|
+
const ownerInstrument = owner.source.instrument;
|
|
58
|
+
// Every field of the followed ticker moves with it (revenue and total debt); other tickers stay.
|
|
59
|
+
return securities.filter(series => series.source.kind === "security" && sameListing(series.source.instrument, ownerInstrument))
|
|
60
|
+
.map(series => series.id);
|
|
49
61
|
}
|
|
50
62
|
|
|
51
63
|
/** Only the followed series move; other contracts and comparisons stay authored. */
|
|
@@ -138,6 +138,12 @@ export const paneSchemas = {
|
|
|
138
138
|
],
|
|
139
139
|
defaultValue: "percent",
|
|
140
140
|
},
|
|
141
|
+
{
|
|
142
|
+
key: "totalReturn",
|
|
143
|
+
description: "Reinvest each cash distribution at its ex-date close, so the returns are total returns, not price returns.",
|
|
144
|
+
type: "boolean",
|
|
145
|
+
defaultValue: false,
|
|
146
|
+
},
|
|
141
147
|
{
|
|
142
148
|
key: "chartResolution",
|
|
143
149
|
description: BAR_SIZE_DESCRIPTION,
|
|
@@ -151,7 +157,7 @@ export const paneSchemas = {
|
|
|
151
157
|
discovery: {
|
|
152
158
|
id: "price-comparison",
|
|
153
159
|
aliases: ["stock comparison", "price performance comparison", "returns comparison", "compare stock prices"],
|
|
154
|
-
limitations: ["Compares closing-price changes in each listing's currency over shared observation dates. Cash distributions and FX conversion are excluded;
|
|
160
|
+
limitations: ["Compares closing-price changes in each listing's currency over shared observation dates. Cash distributions and FX conversion are excluded unless totalReturn is set, which reinvests each distribution at its ex-date close; FX conversion is never applied."],
|
|
155
161
|
screenshotReadiness: "ready",
|
|
156
162
|
},
|
|
157
163
|
},
|
|
@@ -228,6 +228,8 @@ export async function loadChartPaneModel(
|
|
|
228
228
|
...reported,
|
|
229
229
|
...name ? { name } : {},
|
|
230
230
|
...valueDecimals !== undefined ? { textDecimals: valueDecimals } : {},
|
|
231
|
+
// A company's reported total (revenue, market cap) reads as 63.33B in the report, as in the legend and the tables.
|
|
232
|
+
...series.unitGroup.split(":")[0] === "currency-total" ? { textCompact: true } : {},
|
|
231
233
|
derived: !ids.has(series.id),
|
|
232
234
|
points: series.points.map((point) => ({
|
|
233
235
|
...point,
|
|
@@ -251,7 +253,7 @@ export async function loadChartPaneModel(
|
|
|
251
253
|
...(chart.priceHistoryIntegrity?.length ? { priceHistoryIntegrity: chart.priceHistoryIntegrity } : {}),
|
|
252
254
|
...(valuationPriceIssues.length ? { valuationPriceIssues } : {}),
|
|
253
255
|
...(periodCoverage.length ? { periodCoverage } : {}),
|
|
254
|
-
notices: [...chart.warnings.filter((warning) => warning === FINANCIAL_VINTAGE_NOTICE || warning === SEC_EPS_BASIS_NOTICE || warning === chart.priceComparison?.notice
|
|
256
|
+
notices: [...chart.coverageNotices ?? [], ...chart.returnBasisNotice ? [chart.returnBasisNotice] : [], ...chart.warnings.filter((warning) => warning === FINANCIAL_VINTAGE_NOTICE || warning === SEC_EPS_BASIS_NOTICE || warning === chart.priceComparison?.notice
|
|
255
257
|
|| chart.delistings?.some((delisting) => delisting.notice === warning)), ...integrityNotices],
|
|
256
258
|
priceComparison: chart.priceComparison ?? null,
|
|
257
259
|
summaries: chart.series.filter((series) => !series.profile).map((series) => ({ id: series.id, ...summarizeResolvedSeries(series) })),
|
|
@@ -723,6 +725,9 @@ export function chartHeadless(template: keyof typeof paneSchemas): HeadlessPaneD
|
|
|
723
725
|
}, context.marketData) };
|
|
724
726
|
}
|
|
725
727
|
const model = await loadChartPaneModel(spec, context);
|
|
728
|
+
// A bare coin ticker charted as another listing (ETH, the Grayscale fund) says where the coin is, as `quote` does.
|
|
729
|
+
const coinNotes = (await Promise.all(listings.values())).flatMap((read) => read?.coinNote ?? []);
|
|
730
|
+
if (coinNotes.length) model.warnings = [...model.warnings ?? [], ...coinNotes];
|
|
726
731
|
if (template === "graph-intraday-price-pane" && !intradayHistories.length && model.chart.viewport) {
|
|
727
732
|
const { start, end } = model.chart.viewport;
|
|
728
733
|
for (const { target, data } of [
|
|
@@ -10,7 +10,6 @@ import { parseTickerListInput } from "../../../tickers/list";
|
|
|
10
10
|
import { canonicalExchange, parsePublicTickerKey, publicTickerKey } from "../../../utils/exchanges";
|
|
11
11
|
import { tickerInstrumentLabel } from "../../../tickers/instrument-label";
|
|
12
12
|
import type { ChartSpec } from "../../../time-series/types";
|
|
13
|
-
import { isPriceOnlyMarketFieldId } from "../../../time-series/field-catalog";
|
|
14
13
|
import { ChartComposerPane, ChartComposerResearchTab } from "./pane";
|
|
15
14
|
import { DataCatalogPane } from "./data-catalog-pane";
|
|
16
15
|
import { listFieldSeries } from "./field-series";
|
|
@@ -453,7 +452,7 @@ const chartComposerTemplates: PaneTemplateDef[] = [
|
|
|
453
452
|
prefix: "CMP",
|
|
454
453
|
aliases: ["COMP", "TRA"],
|
|
455
454
|
label: "Comparison Chart",
|
|
456
|
-
description: "Compare price returns over shared observation dates for two or more tickers. A leveraged or inverse fund gets its daily objective drawn from its index fund (3× daily QQQ beside TQQQ) and its decay against it.",
|
|
455
|
+
description: "Compare price returns over shared observation dates for two or more tickers, or total returns with distributions reinvested. A leveraged or inverse fund gets its daily objective drawn from its index fund (3× daily QQQ beside TQQQ) and its decay against it.",
|
|
457
456
|
// Leveraged and inverse funds: the daily line and the decay come with the comparison.
|
|
458
457
|
keywords: ["leveraged", "inverse", "decay", "daily-reset", "daily reset", "volatility decay"],
|
|
459
458
|
argKind: "ticker-list",
|
|
@@ -484,9 +483,10 @@ const chartComposerTemplates: PaneTemplateDef[] = [
|
|
|
484
483
|
];
|
|
485
484
|
|
|
486
485
|
/**
|
|
487
|
-
* A
|
|
488
|
-
*
|
|
489
|
-
*
|
|
486
|
+
* A chart whose securities are all one ticker can follow a list: GP, GIP, and GF or GE of one
|
|
487
|
+
* ticker, whatever fields it shows (revenue beside total debt). Every series of that ticker moves
|
|
488
|
+
* to the selected one and keeps its field; FRED and other series stay as they are. A comparison
|
|
489
|
+
* of several tickers would move only one of them, and a FRED-only chart has no ticker to follow.
|
|
490
490
|
*/
|
|
491
491
|
function chartShowsOneSecurity(pane: PaneInstanceConfig): boolean {
|
|
492
492
|
const spec = parseChartSpec(pane.settings?.[CHART_SPEC_SETTING_KEY]);
|
|
@@ -495,7 +495,6 @@ function chartShowsOneSecurity(pane: PaneInstanceConfig): boolean {
|
|
|
495
495
|
const symbols = new Set<string>();
|
|
496
496
|
for (const series of spec.series) {
|
|
497
497
|
if (series.source.kind !== "security") continue;
|
|
498
|
-
if (!isPriceOnlyMarketFieldId(series.source.fieldId)) return false;
|
|
499
498
|
symbols.add(parsePublicTickerKey(series.source.instrument.symbol).symbol.toUpperCase());
|
|
500
499
|
}
|
|
501
500
|
return symbols.size === 1;
|
|
@@ -55,7 +55,9 @@ import {
|
|
|
55
55
|
import { colors } from "../../../theme/colors";
|
|
56
56
|
import { isUsListingExchange, parsePublicTickerKey, publicTickerKey } from "../../../utils/exchanges";
|
|
57
57
|
import { getTimeSeriesField, isMarketFieldId } from "../../../time-series/field-catalog";
|
|
58
|
-
import { isDatasetChart } from "../../../time-series/spec";
|
|
58
|
+
import { isDatasetChart, isOhlcSeriesStyle } from "../../../time-series/spec";
|
|
59
|
+
import { useThemeColors } from "../../../theme/theme-context";
|
|
60
|
+
import { priceComparisonSeriesIds } from "../../../time-series/price-comparison";
|
|
59
61
|
import { seriesMessageName, studyMessageName } from "../../../time-series/series-name";
|
|
60
62
|
import { CHART_COMPOSER_PANE_ID } from "../../../types/config";
|
|
61
63
|
import { useRemoteUiNode } from "../../../remote/semantic-tree";
|
|
@@ -117,6 +119,8 @@ import {
|
|
|
117
119
|
CHART_BAR_SIZE_PRESETS,
|
|
118
120
|
CHART_RANGES as RANGES,
|
|
119
121
|
CHART_RESOLUTIONS,
|
|
122
|
+
CHART_CANDLE_COLOURS_SETTING_KEY,
|
|
123
|
+
chartCandleColours,
|
|
120
124
|
chartStudyOptionsFor,
|
|
121
125
|
chartStudyPeriodTitle,
|
|
122
126
|
chartStudySettingLabel,
|
|
@@ -342,6 +346,14 @@ function ChartComposerSurface({
|
|
|
342
346
|
// plausible time is dropped without pinning. This handle only sets the
|
|
343
347
|
// pin; the chart it opens on is what shows and keeps it.
|
|
344
348
|
const [pinnedMoment, setPinnedMoment] = usePaneSettingValue<unknown>(CHART_PINNED_MOMENT_SETTING_KEY, null);
|
|
349
|
+
const [candleColoursSetting] = usePaneSettingValue<unknown>(CHART_CANDLE_COLOURS_SETTING_KEY, null);
|
|
350
|
+
const gainColour = useThemeColors().positive;
|
|
351
|
+
// Rising candles in the gain colour; falling ones already take the loss colour.
|
|
352
|
+
const candleColour = useCallback((list: ResolvedSeries[]) => (
|
|
353
|
+
chartCandleColours(candleColoursSetting) === "change"
|
|
354
|
+
? list.map((entry) => isOhlcSeriesStyle(entry.style) ? { ...entry, color: gainColour } : entry)
|
|
355
|
+
: list
|
|
356
|
+
), [candleColoursSetting, gainColour]);
|
|
345
357
|
const momentLink = useTabCursorLink(paneId ? `${paneId}:moment` : null, { viewer: false });
|
|
346
358
|
useEffect(() => {
|
|
347
359
|
if (pinnedMoment == null || !momentLink?.pin) return;
|
|
@@ -475,9 +487,9 @@ function ChartComposerSurface({
|
|
|
475
487
|
});
|
|
476
488
|
const resolvedIds = new Set(resolved.map((entry) => entry.id));
|
|
477
489
|
const missing = spec.series.filter((entry) => !resolvedIds.has(entry.id));
|
|
478
|
-
if (missing.length === 0) return resolved;
|
|
490
|
+
if (missing.length === 0) return candleColour(resolved);
|
|
479
491
|
const bySpecOrder = new Map(spec.series.map((entry, index) => [entry.id, index] as const));
|
|
480
|
-
return [
|
|
492
|
+
return candleColour([
|
|
481
493
|
...resolved,
|
|
482
494
|
...missing.map((entry): ResolvedSeries => ({
|
|
483
495
|
id: entry.id,
|
|
@@ -495,15 +507,15 @@ function ChartComposerSurface({
|
|
|
495
507
|
hidden: true,
|
|
496
508
|
points: [],
|
|
497
509
|
})),
|
|
498
|
-
].sort((a, b) => (bySpecOrder.get(a.id) ?? Number.MAX_SAFE_INTEGER) - (bySpecOrder.get(b.id) ?? Number.MAX_SAFE_INTEGER));
|
|
499
|
-
}, [listings.names, nameSeries, resolution.legendSeries, spec.series]);
|
|
510
|
+
].sort((a, b) => (bySpecOrder.get(a.id) ?? Number.MAX_SAFE_INTEGER) - (bySpecOrder.get(b.id) ?? Number.MAX_SAFE_INTEGER)));
|
|
511
|
+
}, [candleColour, listings.names, nameSeries, resolution.legendSeries, spec.series]);
|
|
500
512
|
const plottedSeries = useMemo(
|
|
501
|
-
() => projectVisibleChartSeries(
|
|
513
|
+
() => candleColour(projectVisibleChartSeries(
|
|
502
514
|
spec,
|
|
503
515
|
resolution.bufferedSeries ?? resolution.series,
|
|
504
516
|
resolution.legendSeries,
|
|
505
|
-
),
|
|
506
|
-
[resolution.bufferedSeries, resolution.legendSeries, resolution.series, spec],
|
|
517
|
+
)),
|
|
518
|
+
[candleColour, resolution.bufferedSeries, resolution.legendSeries, resolution.series, spec],
|
|
507
519
|
);
|
|
508
520
|
const { notify } = usePluginAppActions();
|
|
509
521
|
const { runtime } = usePluginRenderContext();
|
|
@@ -1190,6 +1202,22 @@ function ChartComposerSurface({
|
|
|
1190
1202
|
}]
|
|
1191
1203
|
: [];
|
|
1192
1204
|
|
|
1205
|
+
// Returns of two or more prices compare as price returns; the toggle reinvests their cash distributions.
|
|
1206
|
+
const totalReturnOn = spec.viewport.totalReturn === true;
|
|
1207
|
+
const totalReturnFilters = totalReturnOn || priceComparisonSeriesIds(spec) !== null
|
|
1208
|
+
? [{
|
|
1209
|
+
id: "total-return",
|
|
1210
|
+
kind: "toggle" as const,
|
|
1211
|
+
label: "Total return",
|
|
1212
|
+
short: "Total ret",
|
|
1213
|
+
value: totalReturnOn,
|
|
1214
|
+
onChange: (value: boolean) => {
|
|
1215
|
+
const { totalReturn: _previous, ...viewport } = specRef.current.viewport;
|
|
1216
|
+
updateSpec({ ...specRef.current, viewport: value ? { ...viewport, totalReturn: true } : viewport });
|
|
1217
|
+
},
|
|
1218
|
+
}]
|
|
1219
|
+
: [];
|
|
1220
|
+
|
|
1193
1221
|
// A company that reports no quarterly figures says so, and where its annual ones are.
|
|
1194
1222
|
const unreported = resolution.legendSeries?.find((entry) => entry.unreportedPeriod && entry.points.length === 0);
|
|
1195
1223
|
const emptyMessage = spec.series.length === 0
|
|
@@ -1216,6 +1244,7 @@ function ChartComposerSurface({
|
|
|
1216
1244
|
onChange: (value: string) => setRange(value as TimeRange) },
|
|
1217
1245
|
...genericFilters,
|
|
1218
1246
|
...extendedHoursFilters,
|
|
1247
|
+
...totalReturnFilters,
|
|
1219
1248
|
]}
|
|
1220
1249
|
view={resolutionOptions.length > 1 ? {
|
|
1221
1250
|
value: spec.viewport.resolution,
|
|
@@ -129,6 +129,20 @@ export const CHART_SETTING_KEYS = {
|
|
|
129
129
|
realizedVolEstimator: "chartRealizedVolEstimator",
|
|
130
130
|
} as const;
|
|
131
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Whether rising candles take their series' colour or the theme's gain colour.
|
|
134
|
+
* Kept beside the spec in the pane's settings, so a build without it opens the
|
|
135
|
+
* chart unchanged; it is not one of CHART_SETTING_KEYS, which a spec edit
|
|
136
|
+
* clears as derived from the spec.
|
|
137
|
+
*/
|
|
138
|
+
export const CHART_CANDLE_COLOURS_SETTING_KEY = "chartCandleColours";
|
|
139
|
+
|
|
140
|
+
export type ChartCandleColours = "series" | "change";
|
|
141
|
+
|
|
142
|
+
export function chartCandleColours(value: unknown): ChartCandleColours {
|
|
143
|
+
return value === "change" ? "change" : "series";
|
|
144
|
+
}
|
|
145
|
+
|
|
132
146
|
function fallbackSpec(symbol: string | null | undefined): ChartSpec {
|
|
133
147
|
return symbol ? buildPriceChartPreset(symbol) : buildEmptyChartPreset();
|
|
134
148
|
}
|
|
@@ -417,6 +431,7 @@ export function buildChartComposerPaneSettingsDef(
|
|
|
417
431
|
[CHART_SETTING_KEYS.range]: spec.viewport.range,
|
|
418
432
|
[CHART_SETTING_KEYS.resolution]: spec.viewport.resolution,
|
|
419
433
|
[CHART_SETTING_KEYS.mode]: inlineStyleTarget?.style ?? "",
|
|
434
|
+
[CHART_CANDLE_COLOURS_SETTING_KEY]: chartCandleColours(settings[CHART_CANDLE_COLOURS_SETTING_KEY]),
|
|
420
435
|
...(realizedVol ? {
|
|
421
436
|
[CHART_SETTING_KEYS.realizedVolWindow]: String(realizedVol.parameters.window ?? 30),
|
|
422
437
|
[CHART_SETTING_KEYS.realizedVolEstimator]: realizedVol.parameters.estimator ?? "close-to-close",
|
|
@@ -494,6 +509,18 @@ export function buildChartComposerPaneSettingsDef(
|
|
|
494
509
|
options: modes.map((mode) => ({ value: mode, label: mode.toUpperCase() })),
|
|
495
510
|
}]
|
|
496
511
|
: []),
|
|
512
|
+
...(spec.series.some((series) => isOhlcSeriesStyle(series.style))
|
|
513
|
+
? [{
|
|
514
|
+
key: CHART_CANDLE_COLOURS_SETTING_KEY,
|
|
515
|
+
label: "Candle Colours",
|
|
516
|
+
description: "Draw rising candles in their series' colour, or in the theme's gain colour against its loss colour.",
|
|
517
|
+
type: "select" as const,
|
|
518
|
+
options: [
|
|
519
|
+
{ value: "series", label: "Series colour" },
|
|
520
|
+
{ value: "change", label: "Gains & losses" },
|
|
521
|
+
],
|
|
522
|
+
}]
|
|
523
|
+
: []),
|
|
497
524
|
],
|
|
498
525
|
applyValue: applyChartComposerPaneSetting,
|
|
499
526
|
};
|
|
@@ -55,6 +55,7 @@ export function filterCommandSuggestions(
|
|
|
55
55
|
const byCode: ChatFunction[] = [];
|
|
56
56
|
const byName: ChatFunction[] = [];
|
|
57
57
|
for (const fn of new Set(functions.values())) {
|
|
58
|
+
if (fn.unlisted) continue;
|
|
58
59
|
if (fn.code.startsWith(upper) || fn.aliases.includes(upper)) byCode.push(fn);
|
|
59
60
|
else if (fn.name.toLowerCase().split(/[\s/&()-]+/).some((word) => word.startsWith(lower))) byName.push(fn);
|
|
60
61
|
}
|
|
@@ -74,6 +74,7 @@ import { ChatControllerMessageLoading } from "./message-loading";
|
|
|
74
74
|
import { ChatMentionCandidates } from "./mention-candidates";
|
|
75
75
|
import { ChatControllerStorage } from "./storage";
|
|
76
76
|
import { listUnreadInboxItems } from "../unread-inbox";
|
|
77
|
+
import { ChatStatusPreview } from "../status-preview";
|
|
77
78
|
import type { ChatConversationList } from "../conversations";
|
|
78
79
|
import {
|
|
79
80
|
conversationActivityAt,
|
|
@@ -118,6 +119,8 @@ export class ChatController {
|
|
|
118
119
|
);
|
|
119
120
|
private notifyFn: (notification: AppNotificationRequest) => AppNotificationDelivery | void = () => {};
|
|
120
121
|
private openMessageFn: ((channelId: string, messageId: string) => void) | undefined;
|
|
122
|
+
/** A DM, mention or reply that shows in the status bar on arrival; the status bar widget draws it. */
|
|
123
|
+
readonly statusPreview = new ChatStatusPreview();
|
|
121
124
|
private readonly alerts = new ChatNotificationAlerts({
|
|
122
125
|
getAppActive: () => this.appActive,
|
|
123
126
|
ensureChannelState: (channelId) => this.ensureChannelState(channelId),
|
|
@@ -125,6 +128,7 @@ export class ChatController {
|
|
|
125
128
|
getChannel: (channelId) => this.channelCatalog.getChannels().find((channel) => channel.id === channelId),
|
|
126
129
|
getNotify: () => this.notifyFn,
|
|
127
130
|
getOpenMessage: () => this.openMessageFn,
|
|
131
|
+
statusPreview: this.statusPreview,
|
|
128
132
|
});
|
|
129
133
|
|
|
130
134
|
private readonly storage = new ChatControllerStorage({
|
|
@@ -271,7 +275,7 @@ export class ChatController {
|
|
|
271
275
|
* Keeps who sent each channel's unread messages (`unreadPeople` in the
|
|
272
276
|
* snapshot) current until the returned stop is called, reading what the
|
|
273
277
|
* device does not hold. Nothing is read, and the snapshot leaves it empty,
|
|
274
|
-
* unless something tracks it:
|
|
278
|
+
* unless something tracks it: the app badge and the status bar widget.
|
|
275
279
|
*/
|
|
276
280
|
trackUnreadPeople(): () => void {
|
|
277
281
|
return this.unreadPeople.track();
|
|
@@ -5,7 +5,9 @@ import type {
|
|
|
5
5
|
import { apiClient, type ChatChannel, type ChatMessage, type ChatNotification } from "../../../../api-client";
|
|
6
6
|
import type { ChannelRuntimeState, MergeMessagesOptions } from "./state";
|
|
7
7
|
import { formatChatPaneTitle } from "../channel-labels";
|
|
8
|
+
import type { ChatStatusPreview } from "../status-preview";
|
|
8
9
|
import {
|
|
10
|
+
chatMessageLine,
|
|
9
11
|
formatChannelToast,
|
|
10
12
|
formatMentionToast,
|
|
11
13
|
formatReplyToast,
|
|
@@ -28,6 +30,8 @@ interface ChatNotificationAlertsOptions {
|
|
|
28
30
|
getChannel: (channelId: string) => ChatChannel | undefined;
|
|
29
31
|
getNotify: () => Notify;
|
|
30
32
|
getOpenMessage: () => ((channelId: string, messageId: string) => void) | undefined;
|
|
33
|
+
/** Where a message for you shows on arrival instead of a toast, while the status bar has room. */
|
|
34
|
+
statusPreview: ChatStatusPreview;
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
/** Whether this window holds the message as read: its read marker is at or after it. */
|
|
@@ -53,9 +57,9 @@ function acknowledge(notification: ChatNotification, seen: boolean): void {
|
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
/**
|
|
56
|
-
* Decides, once per message, whether a chat notification
|
|
57
|
-
* a desktop banner. A message already read here or on
|
|
58
|
-
* quiet, whether it arrives live or again on reconnect.
|
|
60
|
+
* Decides, once per message, whether a chat notification shows in the status
|
|
61
|
+
* bar, as a toast or as a desktop banner. A message already read here or on
|
|
62
|
+
* another device stays quiet, whether it arrives live or again on reconnect.
|
|
59
63
|
*/
|
|
60
64
|
export class ChatNotificationAlerts {
|
|
61
65
|
private readonly notifiedMessageIds = new Set<string>();
|
|
@@ -94,6 +98,7 @@ export class ChatNotificationAlerts {
|
|
|
94
98
|
for (const timer of this.waiting.values()) clearTimeout(timer);
|
|
95
99
|
this.waiting.clear();
|
|
96
100
|
this.notifiedMessageIds.clear();
|
|
101
|
+
this.options.statusPreview.dismiss();
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
/** Read here, or open in front of the reader: nothing to raise, and the server hears it was seen. */
|
|
@@ -124,8 +129,33 @@ export class ChatNotificationAlerts {
|
|
|
124
129
|
this.present(notification);
|
|
125
130
|
}
|
|
126
131
|
|
|
132
|
+
/**
|
|
133
|
+
* A DM or group message, a mention or a reply, while the app is in front:
|
|
134
|
+
* the status bar shows it in place of the toast when it has room, and the
|
|
135
|
+
* server hears it was seen, as for a toast. Channel chatter never shows there.
|
|
136
|
+
*/
|
|
137
|
+
private presentInStatusBar(notification: ChatNotification, channel: ChatChannel | undefined): boolean {
|
|
138
|
+
const direct = channel?.kind === "direct" || channel?.kind === "group";
|
|
139
|
+
if (!this.options.getAppActive() || (notification.type === "channel" && !direct)) return false;
|
|
140
|
+
const { message } = notification;
|
|
141
|
+
const sentAt = Date.parse(message.createdAt);
|
|
142
|
+
const shown = this.options.statusPreview.offer({
|
|
143
|
+
channelId: notification.channelId,
|
|
144
|
+
messageId: notification.messageId,
|
|
145
|
+
senderName: message.user.username?.trim() || message.user.displayName,
|
|
146
|
+
direct,
|
|
147
|
+
text: chatMessageLine(message),
|
|
148
|
+
at: Number.isFinite(sentAt) ? sentAt : Date.now(),
|
|
149
|
+
});
|
|
150
|
+
if (!shown) return false;
|
|
151
|
+
this.notifiedMessageIds.add(notification.messageId);
|
|
152
|
+
acknowledge(notification, true);
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
127
156
|
private present(notification: ChatNotification): void {
|
|
128
157
|
const channel = this.options.getChannel(notification.channelId);
|
|
158
|
+
if (this.presentInStatusBar(notification, channel)) return;
|
|
129
159
|
const body = notification.type === "reply"
|
|
130
160
|
? formatReplyToast(notification.message)
|
|
131
161
|
: notification.type === "mention"
|
|
@@ -46,12 +46,34 @@ export interface PersistedTranscript {
|
|
|
46
46
|
messages: ChatMessage[];
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/** An unread message for you: who sent it, and when. */
|
|
50
|
+
export interface UnreadMessageForYou {
|
|
51
|
+
/** Null for a DM whose unread messages are not on this device: only the sender is known. */
|
|
52
|
+
messageId: string | null;
|
|
53
|
+
senderId: string;
|
|
54
|
+
/** The sender's username, or their name without one. */
|
|
55
|
+
senderName: string;
|
|
56
|
+
/** When it was sent, in ms; zero when not known. */
|
|
57
|
+
at: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
49
60
|
/** Who the unread messages of one channel are from, by account id. */
|
|
50
61
|
export interface ChannelUnreadPeople {
|
|
51
62
|
/** Everyone who sent an unread message there. */
|
|
52
63
|
senders: string[];
|
|
53
|
-
/** The ones among them with an unread message that mentions you. */
|
|
64
|
+
/** The ones among them with an unread message that mentions you or replies to you. */
|
|
54
65
|
mentioners: string[];
|
|
66
|
+
/**
|
|
67
|
+
* The newest unread message for you: in a DM or group chat the newest
|
|
68
|
+
* unread message, elsewhere the newest that mentions or replies to you.
|
|
69
|
+
* Null when none is known on this device.
|
|
70
|
+
*/
|
|
71
|
+
latestForYou: UnreadMessageForYou | null;
|
|
72
|
+
/**
|
|
73
|
+
* How many unread messages are for you: every one in a DM or group chat,
|
|
74
|
+
* the ones known to mention or reply to you elsewhere.
|
|
75
|
+
*/
|
|
76
|
+
forYouCount: number;
|
|
55
77
|
}
|
|
56
78
|
|
|
57
79
|
export interface ChatControllerSnapshot {
|
|
@@ -83,7 +105,8 @@ export interface ChatControllerSnapshot {
|
|
|
83
105
|
unreadMentionCount: number;
|
|
84
106
|
/**
|
|
85
107
|
* Who sent the unread messages of each channel with any, by channel id: what
|
|
86
|
-
* the app badge counts people from
|
|
108
|
+
* the app badge counts people from and the status bar names them from.
|
|
109
|
+
* Empty unless one of them asked for it
|
|
87
110
|
* ({@link ChatController.trackUnreadPeople}). A channel whose unread messages
|
|
88
111
|
* are not on this device yet lists only the ones that are, until they load.
|
|
89
112
|
*/
|