gloomberb 0.16.2 → 0.16.4
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/package.json +4 -1
- package/src/api-client/askg.ts +43 -0
- package/src/api-client/auth.ts +15 -0
- package/src/api-client/beneficial-owners.ts +4 -0
- package/src/api-client/chat.ts +19 -0
- package/src/api-client/company-kpis.ts +14 -2
- package/src/api-client/data.ts +34 -16
- package/src/api-client/index.ts +12 -0
- package/src/api-client/normalizers.ts +27 -0
- package/src/api-client/paths.ts +88 -23
- package/src/api-client/request.ts +2 -1
- package/src/api-client/research-activity.ts +31 -0
- package/src/api-client/socket.ts +39 -8
- package/src/api-client/types.ts +57 -1
- package/src/api-client/upgrade-experiment.ts +90 -0
- package/src/app/global-shortcuts.ts +11 -1
- package/src/app/keybindings/actions.ts +10 -0
- package/src/app/pane-runtime/index.tsx +8 -0
- package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
- package/src/app/pane-runtime/plugin-bindings.ts +65 -25
- package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
- package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
- package/src/app/runtime/desktop-deeplink.ts +5 -0
- package/src/app/star-prompt/model.ts +98 -0
- package/src/app/star-prompt/runtime.ts +41 -0
- package/src/app/star-prompt/status-line.tsx +190 -0
- package/src/app.tsx +13 -7
- package/src/architecture/core-builtin-allowlist.json +70 -0
- package/src/brokers/account-cache.ts +45 -0
- package/src/brokers/signed-in/adapter.ts +33 -2
- package/src/brokers/signed-in/client.ts +10 -0
- package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
- package/src/brokers/signed-in/sign-in.ts +62 -5
- package/src/capabilities/registry.ts +8 -1
- package/src/capabilities/types.ts +2 -0
- package/src/cli/command-options.ts +74 -0
- package/src/cli/commands/command-utils.ts +29 -0
- package/src/cli/commands/crypto-hints.ts +12 -4
- package/src/cli/commands/market.ts +524 -148
- package/src/cli/commands/options-chain.ts +172 -0
- package/src/cli/commands/options-leaps.ts +422 -0
- package/src/cli/commands/overview.ts +223 -42
- package/src/cli/commands/search.ts +29 -8
- package/src/cli/commands/system.ts +99 -19
- package/src/cli/commands/themes.ts +64 -0
- package/src/cli/commands/ticker.ts +200 -73
- package/src/cli/desktop-pane-shot.ts +100 -16
- package/src/cli/errors.ts +25 -2
- package/src/cli/freshness.ts +7 -4
- package/src/cli/fx-pair.ts +57 -0
- package/src/cli/help.ts +40 -7
- package/src/cli/history-rows.ts +187 -15
- package/src/cli/index-roots.ts +51 -0
- package/src/cli/index.ts +21 -13
- package/src/cli/listing-arg.ts +211 -3
- package/src/cli/not-a-ticker.ts +37 -0
- package/src/cli/options.ts +55 -7
- package/src/cli/pane-functions/capabilities.ts +13 -2
- package/src/cli/pane-functions/catalog.ts +13 -2
- package/src/cli/pane-functions/data.ts +36 -5
- package/src/cli/pane-functions/dom.ts +89 -28
- package/src/cli/pane-functions/freshness.ts +205 -26
- package/src/cli/pane-functions/function-help.ts +71 -24
- package/src/cli/pane-functions/headless.ts +168 -35
- package/src/cli/pane-functions/index.ts +12 -2
- package/src/cli/pane-functions/market-session.ts +133 -0
- package/src/cli/pane-functions/options.ts +15 -4
- package/src/cli/pane-functions/report-notices.ts +63 -0
- package/src/cli/pane-functions/report.ts +12 -1
- package/src/cli/pane-functions/resolver.ts +32 -8
- package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
- package/src/cli/pane-functions/screenshot.ts +109 -23
- package/src/cli/registry.ts +8 -4
- package/src/cli/report-tables.ts +460 -0
- package/src/cli/result.ts +63 -16
- package/src/cli/row-window.ts +76 -0
- package/src/cli/scroll-truncation.ts +34 -0
- package/src/components/chart/composite/composite-chart.tsx +43 -4
- package/src/components/chart/composite/legend.tsx +39 -6
- package/src/components/chart/composite/rasterizer.ts +64 -4
- package/src/components/chart/composite/series-cues.ts +71 -0
- package/src/components/chart/composite/text-renderer.ts +16 -8
- package/src/components/chart/composite/types.ts +3 -0
- package/src/components/command-bar/commands/direct/index.ts +9 -1
- package/src/components/command-bar/commands/plugin/items.ts +3 -1
- package/src/components/command-bar/commands/registry.ts +7 -0
- package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
- package/src/components/command-bar/pane-templates/workflow.ts +5 -0
- package/src/components/command-bar/routes/root/command-items.ts +2 -0
- package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
- package/src/components/command-bar/routes/root/results.ts +40 -10
- package/src/components/command-bar/routes/root/runtime.ts +8 -0
- package/src/components/command-bar/routes/root/search-providers.ts +97 -14
- package/src/components/command-bar/surface/index.tsx +18 -4
- package/src/components/command-bar/theme-picker.tsx +48 -13
- package/src/components/command-bar/workflow/ops.ts +5 -3
- package/src/components/data-table/export.ts +6 -51
- package/src/components/data-table/view.tsx +31 -10
- package/src/components/layout/floating-pane.tsx +6 -2
- package/src/components/layout/market-summary.ts +23 -9
- package/src/components/layout/pane/content.tsx +46 -29
- package/src/components/layout/pane/cycle-order.ts +2 -11
- package/src/components/layout/pane/header.tsx +2 -1
- package/src/components/layout/pane/title.ts +1 -1
- package/src/components/layout/presentation.ts +22 -0
- package/src/components/layout/shell/active-drag.ts +67 -35
- package/src/components/layout/shell/cursor-occlusion.ts +6 -5
- package/src/components/layout/shell/drag/index.ts +16 -7
- package/src/components/layout/shell/drag/live.ts +193 -0
- package/src/components/layout/shell/drag/overlays.tsx +179 -35
- package/src/components/layout/shell/drag/runtime.ts +62 -50
- package/src/components/layout/shell/drag/slide.ts +10 -0
- package/src/components/layout/shell/index.tsx +84 -57
- package/src/components/layout/shell/layout-state.ts +5 -11
- package/src/components/layout/shell/menu.ts +28 -1
- package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
- package/src/components/layout/shell/native/surfaces.ts +30 -14
- package/src/components/layout/shell/pane/actions.ts +2 -1
- package/src/components/layout/shell/pane/layers.tsx +461 -180
- package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
- package/src/components/layout/shell/pane/new-layout.ts +116 -0
- package/src/components/layout/shell/shortcuts.ts +5 -2
- package/src/components/layout/shell/terminal-pointer-runtime.ts +18 -18
- package/src/components/layout/shell/visible-layout.ts +44 -1
- package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
- package/src/components/layout/status-bar.tsx +4 -1
- package/src/components/markdown-text.tsx +9 -5
- package/src/components/metric-treemap/heat-surface.tsx +27 -8
- package/src/components/metric-treemap/heat-tiles.ts +48 -17
- package/src/components/onboarding/onboarding-frame.tsx +33 -10
- package/src/components/onboarding/onboarding-wizard.tsx +49 -40
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
- package/src/components/onboarding/pro-step-layout.ts +86 -0
- package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
- package/src/components/ui/button.tsx +10 -0
- package/src/components/ui/data-table/index.tsx +38 -0
- package/src/components/ui/data-table/types.ts +8 -1
- package/src/components/ui/data-table/visible-range.ts +2 -0
- package/src/components/ui/icon.tsx +11 -2
- package/src/components/ui/index.ts +1 -1
- package/src/core/connection-health.ts +1 -1
- package/src/core/state/app/layout-reducer.ts +89 -3
- package/src/core/state/app/state.ts +8 -5
- package/src/core/state/app/types.ts +13 -2
- package/src/data/config/layout.ts +55 -21
- package/src/data/config/store/fold-panes.ts +149 -0
- package/src/data/config/store/migrations.ts +7 -0
- package/src/data/config/store/node.ts +27 -6
- package/src/data/config/store/normalize.ts +37 -3
- package/src/data/json-persistence.ts +17 -3
- package/src/data/json-storage.ts +2 -0
- package/src/data/memory-resource-store.ts +46 -0
- package/src/i18n/es.ts +14 -1
- package/src/i18n/ja.ts +14 -1
- package/src/i18n/ko.ts +14 -1
- package/src/i18n/zh-cn.ts +14 -1
- package/src/i18n/zh-tw.ts +14 -1
- package/src/layout/desks.ts +1 -2
- package/src/layout/pane-layout-move.ts +264 -0
- package/src/layout/pane-manager/hidden-panes.ts +160 -0
- package/src/layout/pane-manager/index.ts +1 -0
- package/src/market-data/coordinator/entries.ts +7 -1
- package/src/market-data/coordinator/quotes.ts +2 -2
- package/src/market-data/market/freshness.ts +156 -11
- package/src/market-data/market/status.ts +40 -0
- package/src/market-data/options-alternatives.ts +69 -0
- package/src/market-data/quotes/contributions.ts +19 -2
- package/src/market-data/quotes/freshness.ts +5 -0
- package/src/market-data/quotes/metadata.ts +16 -0
- package/src/market-data/quotes/screener-live-quotes.ts +56 -5
- package/src/plugins/builtin/account-management/keyboard.ts +3 -0
- package/src/plugins/builtin/account-management/model.ts +1 -0
- package/src/plugins/builtin/account-management/navigation.ts +1 -1
- package/src/plugins/builtin/account-management/pane.tsx +31 -4
- package/src/plugins/builtin/alerts/quotes.ts +3 -0
- package/src/plugins/builtin/analytics/pane-model.ts +10 -6
- package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
- package/src/plugins/builtin/analytics/risk-model.ts +37 -8
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
- package/src/plugins/builtin/attention/pane.tsx +1 -1
- package/src/plugins/builtin/backtest/pane.tsx +1 -1
- package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
- package/src/plugins/builtin/broker-manager/index.tsx +7 -3
- package/src/plugins/builtin/broker-manager/model.ts +8 -0
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
- package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
- package/src/plugins/builtin/broker-manager/table.ts +5 -0
- package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
- package/src/plugins/builtin/catalysts/detail.tsx +1 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
- package/src/plugins/builtin/chat/channels.ts +34 -36
- package/src/plugins/builtin/chat/command-bar.ts +138 -0
- package/src/plugins/builtin/chat/composer-commands.ts +21 -1
- package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
- package/src/plugins/builtin/chat/content/footer.tsx +5 -3
- package/src/plugins/builtin/chat/content/index.tsx +21 -1
- package/src/plugins/builtin/chat/content/mentions.ts +2 -1
- package/src/plugins/builtin/chat/content/user-map.ts +3 -1
- package/src/plugins/builtin/chat/controller/index.ts +48 -0
- package/src/plugins/builtin/chat/conversations.ts +256 -0
- package/src/plugins/builtin/chat/discord-command.ts +91 -0
- package/src/plugins/builtin/chat/edit-window.ts +12 -6
- package/src/plugins/builtin/chat/ghost-user.ts +14 -0
- package/src/plugins/builtin/chat/index.tsx +40 -2
- package/src/plugins/builtin/chat/layout.ts +2 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
- package/src/plugins/builtin/chat/message/header.tsx +24 -15
- package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
- package/src/plugins/builtin/chat/message/render-state.ts +3 -0
- package/src/plugins/builtin/chat/pane-requests.ts +60 -0
- package/src/plugins/builtin/chat/pane-state.ts +32 -2
- package/src/plugins/builtin/chat/pane.tsx +8 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
- package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
- package/src/plugins/builtin/cloud/askg/model.ts +129 -70
- package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
- package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
- package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
- package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
- package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
- package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
- package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
- package/src/plugins/builtin/cloud/plugin.tsx +36 -1
- package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
- package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
- package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
- package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
- package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
- package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
- package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
- package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
- package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
- package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
- package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
- package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
- package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
- package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
- package/src/plugins/builtin/comment-letters/client.ts +1 -3
- package/src/plugins/builtin/company-attention/pane.tsx +1 -1
- package/src/plugins/builtin/company-kpis/client.ts +23 -8
- package/src/plugins/builtin/company-kpis/headless.ts +3 -1
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
- package/src/plugins/builtin/composite-plugins.ts +107 -64
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
- package/src/plugins/builtin/credit-boards/index.tsx +21 -21
- package/src/plugins/builtin/credit-boards/model.ts +0 -1
- package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
- package/src/plugins/builtin/debt-maturities/client.ts +37 -9
- package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
- package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
- package/src/plugins/builtin/earnings-calls/data.ts +28 -7
- package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
- package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
- package/src/plugins/builtin/econ/calendar-model.ts +42 -17
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/index.tsx +68 -22
- package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
- package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
- package/src/plugins/builtin/econ-statistics/view.ts +17 -0
- package/src/plugins/builtin/executives/data.ts +17 -6
- package/src/plugins/builtin/executives/pane.tsx +16 -7
- package/src/plugins/builtin/filing-events/pane.tsx +22 -5
- package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
- package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
- package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
- package/src/plugins/builtin/gpu/headless.ts +9 -3
- package/src/plugins/builtin/gpu/model.ts +14 -5
- package/src/plugins/builtin/help/index.tsx +1 -0
- package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
- package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
- package/src/plugins/builtin/holders/client.ts +3 -6
- package/src/plugins/builtin/holders/format.ts +31 -1
- package/src/plugins/builtin/holders/headless.ts +32 -6
- package/src/plugins/builtin/holders/pane.tsx +25 -13
- package/src/plugins/builtin/holders/report-header.ts +46 -5
- package/src/plugins/builtin/insider/index.tsx +4 -4
- package/src/plugins/builtin/iv-history/charts.tsx +21 -7
- package/src/plugins/builtin/iv-history/client.ts +12 -2
- package/src/plugins/builtin/iv-history/format.ts +33 -2
- package/src/plugins/builtin/iv-history/headless.ts +22 -10
- package/src/plugins/builtin/iv-history/index.tsx +3 -3
- package/src/plugins/builtin/iv-history/model.ts +61 -13
- package/src/plugins/builtin/iv-history/pane.tsx +11 -3
- package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
- package/src/plugins/builtin/jobs/pane.tsx +1 -1
- package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
- package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +121 -45
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
- package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
- package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
- package/src/plugins/builtin/layout-manager/index.tsx +9 -1
- package/src/plugins/builtin/macro-day/client.ts +24 -0
- package/src/plugins/builtin/macro-day/headless.ts +11 -8
- package/src/plugins/builtin/macro-day/index.tsx +3 -0
- package/src/plugins/builtin/macro-day/model.ts +11 -6
- package/src/plugins/builtin/macro-day/pane.tsx +12 -8
- package/src/plugins/builtin/macro-day/release-table.ts +40 -0
- package/src/plugins/builtin/macro-day/releases.ts +62 -44
- package/src/plugins/builtin/market-heatmap/data.ts +25 -5
- package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
- package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
- package/src/plugins/builtin/market-heatmap/model.ts +94 -3
- package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
- package/src/plugins/builtin/market-movers/headless.ts +31 -4
- package/src/plugins/builtin/market-movers/index.tsx +8 -1
- package/src/plugins/builtin/market-movers/model.ts +71 -17
- package/src/plugins/builtin/market-movers/table.ts +50 -23
- package/src/plugins/builtin/members/pane.tsx +10 -0
- package/src/plugins/builtin/news/headless.ts +9 -2
- package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
- package/src/plugins/builtin/openfda/client.ts +1 -1
- package/src/plugins/builtin/options/carry.ts +95 -0
- package/src/plugins/builtin/options/index.tsx +58 -5
- package/src/plugins/builtin/options/strike-window.ts +183 -0
- package/src/plugins/builtin/options/table.ts +55 -3
- package/src/plugins/builtin/options/types.ts +8 -1
- package/src/plugins/builtin/options/view.tsx +161 -12
- package/src/plugins/builtin/options-positioning/headless.ts +4 -2
- package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
- package/src/plugins/builtin/options-scenario/client.ts +73 -17
- package/src/plugins/builtin/options-scenario/headless.ts +71 -24
- package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
- package/src/plugins/builtin/options-scenario/index.tsx +9 -2
- package/src/plugins/builtin/options-scenario/model.ts +33 -0
- package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
- package/src/plugins/builtin/pe-band/model.ts +2 -2
- package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
- package/src/plugins/builtin/perps/equity-row.tsx +8 -5
- package/src/plugins/builtin/perps/model.ts +16 -5
- package/src/plugins/builtin/perps/pane.tsx +1 -1
- package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
- package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
- package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
- package/src/plugins/builtin/plugin-module.ts +87 -4
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +16 -1
- package/src/plugins/builtin/portfolio-list/allocation.ts +3 -0
- package/src/plugins/builtin/portfolio-list/cached-account.ts +18 -9
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
- package/src/plugins/builtin/portfolio-list/cli/render.ts +89 -18
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
- package/src/plugins/builtin/portfolio-list/headless.ts +40 -7
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -4
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -4
- package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
- package/src/plugins/builtin/realized-vol/headless.ts +3 -0
- package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
- package/src/plugins/builtin/research/relative-valuation-model.ts +7 -3
- package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
- package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
- package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
- package/src/plugins/builtin/reverse-dcf/model.ts +2 -1
- package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
- package/src/plugins/builtin/risk-factors/data.ts +23 -9
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
- package/src/plugins/builtin/seasonality/pane.tsx +1 -1
- package/src/plugins/builtin/sec/index.tsx +5 -5
- package/src/plugins/builtin/sectors/client.ts +10 -2
- package/src/plugins/builtin/sectors/headless.ts +35 -6
- package/src/plugins/builtin/sectors/index.tsx +18 -1
- package/src/plugins/builtin/sectors/sector-model.ts +150 -41
- package/src/plugins/builtin/shared/command-tab.ts +51 -0
- package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
- package/src/plugins/builtin/shared/members-menu.tsx +260 -0
- package/src/plugins/builtin/shared/report-freshness.ts +50 -2
- package/src/plugins/builtin/shared/ticker-request.ts +25 -1
- package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
- package/src/plugins/builtin/shared/volatility/options.ts +1 -46
- package/src/plugins/builtin/short-interest/index.tsx +19 -4
- package/src/plugins/builtin/short-interest/surface.tsx +4 -4
- package/src/plugins/builtin/short-volume/index.tsx +1 -10
- package/src/plugins/builtin/short-volume/pane.tsx +1 -1
- package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
- package/src/plugins/builtin/supply-chain/model.ts +1 -1
- package/src/plugins/builtin/themes/pane.tsx +3 -0
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
- package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
- package/src/plugins/builtin/ticker-detail/headless.ts +63 -25
- package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -4
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +3 -2
- package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
- package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
- package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
- package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
- package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
- package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
- package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
- package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
- package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
- package/src/plugins/builtin/vol-surface/client.ts +6 -2
- package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
- package/src/plugins/builtin/vol-surface/headless.ts +124 -20
- package/src/plugins/builtin/vol-surface/model.ts +23 -11
- package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
- package/src/plugins/builtin/vol-surface/tables.ts +124 -0
- package/src/plugins/builtin/world-indices/client.ts +22 -4
- package/src/plugins/builtin/world-indices/headless.ts +54 -27
- package/src/plugins/builtin/world-indices/model.ts +4 -2
- package/src/plugins/builtin/world-indices/table.tsx +13 -2
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +2 -0
- package/src/plugins/builtin/world-venue-map/basemap.ts +18 -0
- package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
- package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
- package/src/plugins/builtin/world-venue-map/geo-svg.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/index.ts +6 -6
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +27 -14
- package/src/plugins/builtin/world-venue-map/layers.ts +68 -10
- package/src/plugins/builtin/world-venue-map/map.tsx +198 -62
- package/src/plugins/builtin/world-venue-map/model.ts +16 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +3 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
- package/src/plugins/builtin/world-venue-map/pane.tsx +16 -10
- package/src/plugins/builtin/yield-curve/index.tsx +1 -1
- package/src/plugins/builtin-editorial.ts +126 -0
- package/src/plugins/catalog-backend.ts +16 -9
- package/src/plugins/catalog-browser.ts +127 -63
- package/src/plugins/catalog-manifest.ts +73 -40
- package/src/plugins/catalog-ui.ts +26 -6
- package/src/plugins/extracted-plugins.ts +7 -1
- package/src/plugins/ownership.ts +150 -7
- package/src/plugins/plugin-functions.ts +54 -0
- package/src/plugins/plugin-off.ts +36 -0
- package/src/plugins/registry/host-actions.ts +16 -0
- package/src/plugins/registry/index.ts +84 -24
- package/src/plugins/registry/plugin-state.ts +25 -1
- package/src/plugins/runtime/context.tsx +29 -0
- package/src/react/input.ts +48 -1
- package/src/remote/app-host.tsx +5 -0
- package/src/remote/controller.ts +3 -53
- package/src/remote/matching.ts +69 -0
- package/src/remote/schema.ts +1 -1
- package/src/remote/semantic-tree.tsx +14 -0
- package/src/remote/types.ts +2 -0
- package/src/renderers/browser/app-services.ts +3 -1
- package/src/renderers/dom/data-table/dom.ts +31 -0
- package/src/renderers/dom/data-table/index.tsx +5 -0
- package/src/renderers/dom/desktop/icons.tsx +14 -0
- package/src/renderers/dom/desktop/query-bar.tsx +59 -32
- package/src/renderers/dom/host/box.tsx +113 -17
- package/src/renderers/dom/host/drag-shield.ts +35 -0
- package/src/renderers/dom/host/live-frame.ts +62 -0
- package/src/renderers/dom/host/mouse.ts +8 -1
- package/src/renderers/dom/host/scroll-box.tsx +6 -5
- package/src/renderers/dom/host/tabs.tsx +3 -2
- package/src/renderers/dom/input-host.tsx +6 -1
- package/src/renderers/dom/styles.css +26 -5
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +15 -1
- package/src/renderers/electrobun/shared/protocol.ts +12 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -1
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
- package/src/renderers/electrobun/view/host/native.ts +0 -10
- package/src/renderers/electrobun/view/host/window-move.ts +28 -0
- package/src/renderers/electrobun/view/main.tsx +4 -1
- package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
- package/src/renderers/electrobun/view/ui-host.tsx +2 -5
- package/src/renderers/opentui/input-host.tsx +6 -1
- package/src/renderers/opentui/start.tsx +9 -0
- package/src/renderers/opentui/ui-host.tsx +7 -1
- package/src/shares/portable-layout.ts +16 -6
- package/src/sources/gloomberb-cloud/index.ts +19 -10
- package/src/sources/gloomberb-cloud/normalizers.ts +20 -21
- package/src/sources/listing-symbols.ts +11 -5
- package/src/sources/provider-errors.ts +88 -3
- package/src/sources/provider-router/batches.ts +3 -3
- package/src/sources/provider-router/cached-routes.ts +8 -4
- package/src/sources/provider-router/financial-routes.ts +13 -7
- package/src/sources/provider-router/financials.ts +106 -16
- package/src/sources/provider-router/history.ts +27 -11
- package/src/sources/provider-router/index.ts +1 -1
- package/src/sources/provider-router/primary.ts +24 -6
- package/src/sources/sec-edgar.ts +68 -8
- package/src/state/pane-scroll-registry.tsx +67 -35
- package/src/sync/core-contributors.ts +4 -3
- package/src/test-support/cli-context.ts +2 -1
- package/src/test-support/data-provider.ts +34 -0
- package/src/test-support/margin-account.ts +108 -0
- package/src/test-support/shell-pane-keys.tsx +1 -0
- package/src/theme/series-colors.ts +46 -0
- package/src/theme/themes.ts +149 -0
- package/src/tickers/search/index.ts +22 -2
- package/src/tickers/search/ranking.ts +45 -1
- package/src/tickers/search/types.ts +3 -1
- package/src/time-series/resolution.ts +15 -0
- package/src/time-series/resolve.ts +1 -14
- package/src/types/config.ts +61 -1
- package/src/types/financials.ts +32 -3
- package/src/types/headless.ts +11 -0
- package/src/types/instrument.ts +8 -0
- package/src/types/plugin.ts +49 -3
- package/src/ui/host.tsx +16 -0
- package/src/ui/index.tsx +10 -3
- package/src/ui/scroll-box-axis.ts +26 -0
- package/src/utils/cli-output.ts +14 -1
- package/src/utils/currency-units.ts +8 -3
- package/src/utils/depositary-receipt.ts +54 -10
- package/src/utils/display-number.ts +63 -0
- package/src/utils/exchanges.ts +13 -0
- package/src/utils/format.ts +2 -1
- package/src/utils/fundamentals.ts +17 -1
- package/src/utils/fx-market-hours.ts +34 -0
- package/src/utils/option-expiry.ts +126 -0
- package/src/utils/price-history-integrity.ts +19 -6
- package/src/utils/price-history.ts +39 -6
- package/src/utils/rpc-timeout-error.ts +6 -2
- package/src/utils/sec-eps-basis.ts +159 -30
- package/src/utils/sec.ts +38 -34
- package/src/utils/throttled-fetch.ts +51 -22
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useCallback, useMemo, useSyncExternalStore } from "react";
|
|
2
|
+
import { apiClient } from "../../../api-client";
|
|
3
|
+
import { fetchSignedInBrokerConnection, type SignedInBrokerConnection } from "../../../brokers/signed-in/client";
|
|
4
|
+
import { isSignedInBrokerProfile, signedInBrokerId } from "../../../brokers/signed-in/profile";
|
|
5
|
+
import { tf } from "../../../i18n";
|
|
6
|
+
import { useAsyncResource } from "../../../react/async-resource";
|
|
7
|
+
import type { BrokerInstanceConfig } from "../../../types/config";
|
|
8
|
+
import { formatDetailDate, formatShortDate } from "../../../utils/datetime-format";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Some brokers end a sign-in on their own clock, however often it was
|
|
12
|
+
* refreshed, and Gloom flags it a day before. Only that case shows anything: a
|
|
13
|
+
* server that predates the flag, or a sign-in with no end in sight, adds nothing.
|
|
14
|
+
*/
|
|
15
|
+
export type SignInEnds = ReadonlyMap<string, number>;
|
|
16
|
+
|
|
17
|
+
/** When the broker ends the sign-in, if Gloom says it is time to connect again. */
|
|
18
|
+
export function signInEndOf(connection: Pick<SignedInBrokerConnection, "expiresAt" | "expiresSoon">): number | null {
|
|
19
|
+
if (connection.expiresSoon !== true || !connection.expiresAt) return null;
|
|
20
|
+
const endsAt = Date.parse(connection.expiresAt);
|
|
21
|
+
return Number.isFinite(endsAt) ? endsAt : null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The profile row's status, in the place of "Connected": "Ends Oct 10". */
|
|
25
|
+
export function signInEndStatus(endsAt: number, now = Date.now()): string {
|
|
26
|
+
const date = formatShortDate(endsAt, { year: false });
|
|
27
|
+
return endsAt > now ? tf("Ends {date}", { date }) : tf("Ended {date}", { date });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** The profile detail's note: "Sign-in ends Oct 10, 2026 at 3:00 PM". */
|
|
31
|
+
export function signInEndNote(endsAt: number, now = Date.now()): string {
|
|
32
|
+
const when = formatDetailDate(endsAt);
|
|
33
|
+
return endsAt > now ? tf("Sign-in ends {when}", { when }) : tf("Sign-in ended {when}", { when });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* When each signed-in profile's sign-in ends, by instance id, for the profiles
|
|
38
|
+
* whose broker is about to end it. Read from Gloom when the pane opens and
|
|
39
|
+
* after a connect or sync; a failed read is the same as nothing to say.
|
|
40
|
+
*/
|
|
41
|
+
export function useSignInEnds(
|
|
42
|
+
instances: readonly BrokerInstanceConfig[],
|
|
43
|
+
): { ends: SignInEnds; reload: () => void } {
|
|
44
|
+
const profiles = useMemo(
|
|
45
|
+
() => instances.flatMap((instance) => isSignedInBrokerProfile(instance) && instance.enabled !== false
|
|
46
|
+
? [{ id: instance.id, broker: signedInBrokerId(instance) }]
|
|
47
|
+
: []),
|
|
48
|
+
[instances],
|
|
49
|
+
);
|
|
50
|
+
// One read per broker, however many profiles name it.
|
|
51
|
+
const brokers = useMemo(() => [...new Set(profiles.map((profile) => profile.broker).filter(Boolean))].sort(), [profiles]);
|
|
52
|
+
const brokerKey = brokers.join(",");
|
|
53
|
+
const signedIn = useSyncExternalStore(
|
|
54
|
+
(listener) => apiClient.subscribeCurrentUser(listener),
|
|
55
|
+
() => apiClient.isSignedIn(),
|
|
56
|
+
() => apiClient.isSignedIn(),
|
|
57
|
+
);
|
|
58
|
+
const loader = useMemo(() => brokerKey && signedIn
|
|
59
|
+
? async () => {
|
|
60
|
+
const reads = await Promise.all(brokerKey.split(",").map(async (broker) => {
|
|
61
|
+
try {
|
|
62
|
+
return [broker, signInEndOf(await fetchSignedInBrokerConnection(broker))] as const;
|
|
63
|
+
} catch {
|
|
64
|
+
return [broker, null] as const;
|
|
65
|
+
}
|
|
66
|
+
}));
|
|
67
|
+
return new Map(reads);
|
|
68
|
+
}
|
|
69
|
+
: null, [brokerKey, signedIn]);
|
|
70
|
+
const { data, reload } = useAsyncResource(loader, { keepPreviousData: true });
|
|
71
|
+
|
|
72
|
+
const ends = useMemo<SignInEnds>(() => {
|
|
73
|
+
const byInstance = new Map<string, number>();
|
|
74
|
+
for (const profile of profiles) {
|
|
75
|
+
const endsAt = data?.get(profile.broker);
|
|
76
|
+
if (endsAt != null) byInstance.set(profile.id, endsAt);
|
|
77
|
+
}
|
|
78
|
+
return byInstance;
|
|
79
|
+
}, [data, profiles]);
|
|
80
|
+
const refresh = useCallback(() => { void reload(); }, [reload]);
|
|
81
|
+
return { ends, reload: refresh };
|
|
82
|
+
}
|
|
@@ -4,6 +4,7 @@ import { colors } from "../../../theme/colors";
|
|
|
4
4
|
import { formatRelativeAge } from "../../../utils/datetime-format";
|
|
5
5
|
import type { BrokerDisplayState, BrokerProfileRow } from "./model";
|
|
6
6
|
import { t } from "../../../i18n";
|
|
7
|
+
import { signInEndStatus } from "./sign-in-end";
|
|
7
8
|
|
|
8
9
|
type BrokerColumnId = "profile" | "status" | "broker" | "mode" | "accounts" | "updated";
|
|
9
10
|
export type BrokerColumn = DataTableColumn & { id: BrokerColumnId };
|
|
@@ -45,6 +46,10 @@ export function renderBrokerCell(row: BrokerProfileRow, column: BrokerColumn): D
|
|
|
45
46
|
attributes: TextAttributes.BOLD,
|
|
46
47
|
};
|
|
47
48
|
case "status":
|
|
49
|
+
// A sign-in the broker is about to end replaces "Connected" until it is renewed.
|
|
50
|
+
if (row.signInEndsAt !== null) {
|
|
51
|
+
return { text: signInEndStatus(row.signInEndsAt), color: colors.warning };
|
|
52
|
+
}
|
|
48
53
|
return {
|
|
49
54
|
text: row.stateLabel,
|
|
50
55
|
color: stateColor(row.state),
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
// the browser catalog (catalog-browser.ts). Keep this file import-free so the
|
|
3
3
|
// browser catalog can read it without pulling in native-only modules.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
// them
|
|
5
|
+
// Screeners & Movers, Ownership & Insiders and Earnings ship fewer panes on
|
|
6
|
+
// the web, so each catalog gives them their own description rather than
|
|
7
|
+
// advertising panes they lack.
|
|
7
8
|
|
|
8
9
|
export const applicationPluginMeta = {
|
|
9
10
|
id: "application",
|
|
@@ -20,11 +21,44 @@ export const portfolioPluginMeta = {
|
|
|
20
21
|
toggleable: true,
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
// Ticker Research's successors. Every module keeps the `ticker-research`
|
|
25
|
+
// state namespace: these four declare it, Alt Data and Quant default to it,
|
|
26
|
+
// and Credit & Bonds and Earnings compose theirs with an override.
|
|
27
|
+
// `ticker-research` itself now stands for all eight in disabledPlugins (see
|
|
28
|
+
// ownership.ts). The core keeps the name people know.
|
|
29
|
+
|
|
30
|
+
export const tickerCorePluginMeta = {
|
|
31
|
+
id: "ticker-core",
|
|
32
|
+
stateId: "ticker-research",
|
|
25
33
|
name: "Ticker Research",
|
|
26
34
|
version: "1.0.0",
|
|
27
|
-
description: "Company
|
|
35
|
+
description: "Company overview, charts, financials, quotes and returns, analyst research and estimates, KPIs and segments, valuation, dividends, executives, and time and sales.",
|
|
36
|
+
toggleable: true,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const optionsVolatilityPluginMeta = {
|
|
40
|
+
id: "options-volatility",
|
|
41
|
+
stateId: "ticker-research",
|
|
42
|
+
name: "Options & Volatility",
|
|
43
|
+
version: "1.0.0",
|
|
44
|
+
description: "Option chains and positioning, a pricing calculator, scenarios, the volatility surface, and realized and implied volatility history.",
|
|
45
|
+
toggleable: true,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const ownershipPluginMeta = {
|
|
49
|
+
id: "ownership",
|
|
50
|
+
stateId: "ticker-research",
|
|
51
|
+
name: "Ownership & Insiders",
|
|
52
|
+
version: "1.0.0",
|
|
53
|
+
toggleable: true,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const filingsPluginMeta = {
|
|
57
|
+
id: "filings",
|
|
58
|
+
stateId: "ticker-research",
|
|
59
|
+
name: "Filings & Events",
|
|
60
|
+
version: "1.0.0",
|
|
61
|
+
description: "SEC filings, risk factors, 8-K events, catalysts and litigation, and M&A and distress.",
|
|
28
62
|
toggleable: true,
|
|
29
63
|
};
|
|
30
64
|
|
|
@@ -36,16 +70,92 @@ export const newsPluginMeta = {
|
|
|
36
70
|
toggleable: true,
|
|
37
71
|
};
|
|
38
72
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
73
|
+
// Market Overview's successors. Its modules keep the `market-overview` state
|
|
74
|
+
// namespace; Alt Data and Quant default to `ticker-research`, where their
|
|
75
|
+
// Ticker Research modules keep theirs, and compose their Market Overview
|
|
76
|
+
// modules with a `market-overview` override. `market-overview` itself
|
|
77
|
+
// now stands for all six in disabledPlugins (see ownership.ts).
|
|
78
|
+
|
|
79
|
+
export const globalMarketsPluginMeta = {
|
|
80
|
+
id: "global-markets",
|
|
81
|
+
stateId: "market-overview",
|
|
82
|
+
name: "Global Markets",
|
|
83
|
+
version: "1.0.0",
|
|
84
|
+
description: "World indices, the world map with chokepoint transits, sector and thematic performance, the FX matrix, and relative rotation.",
|
|
85
|
+
toggleable: true,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const screenersPluginMeta = {
|
|
89
|
+
id: "screeners",
|
|
90
|
+
stateId: "market-overview",
|
|
91
|
+
name: "Screeners & Movers",
|
|
92
|
+
version: "1.0.0",
|
|
93
|
+
toggleable: true,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const futuresCommoditiesPluginMeta = {
|
|
97
|
+
id: "futures-commodities",
|
|
98
|
+
stateId: "market-overview",
|
|
99
|
+
name: "Futures & Commodities",
|
|
100
|
+
version: "1.0.0",
|
|
101
|
+
description: "Futures board and curves, CFTC positioning, and EIA petroleum and gas storage.",
|
|
102
|
+
toggleable: true,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const cryptoPluginMeta = {
|
|
106
|
+
id: "crypto",
|
|
107
|
+
stateId: "market-overview",
|
|
108
|
+
name: "Crypto & Perps",
|
|
109
|
+
version: "1.0.0",
|
|
110
|
+
description: "Top crypto assets by market cap, and perpetual funding, open interest and premiums.",
|
|
111
|
+
toggleable: true,
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const altDataPluginMeta = {
|
|
115
|
+
id: "alt-data",
|
|
116
|
+
stateId: "ticker-research",
|
|
117
|
+
name: "Supply Chain & Alt Data",
|
|
118
|
+
version: "1.0.0",
|
|
119
|
+
description: "Supply chains and exposure, government awards, hiring, apps and job postings, social mentions, research attention, GPU prices, and power queues.",
|
|
120
|
+
toggleable: true,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const quantPluginMeta = {
|
|
124
|
+
id: "quant",
|
|
125
|
+
stateId: "ticker-research",
|
|
126
|
+
name: "Quant",
|
|
127
|
+
version: "1.0.0",
|
|
128
|
+
description: "Correlation matrix and relationship graphs, backtests, seasonality, and moves on macro release days.",
|
|
129
|
+
toggleable: true,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Macro's successors keep its state namespace, so the pane state, settings and
|
|
133
|
+
// caches saved while they were one plugin stay theirs; the Ticker Research
|
|
134
|
+
// modules Credit & Bonds and Earnings hold keep `ticker-research`. `macro` itself now
|
|
135
|
+
// stands for all three in disabledPlugins (see ownership.ts).
|
|
136
|
+
|
|
137
|
+
export const ratesMacroPluginMeta = {
|
|
138
|
+
id: "rates-macro",
|
|
139
|
+
stateId: "macro",
|
|
140
|
+
name: "Rates & Macro",
|
|
141
|
+
version: "1.0.0",
|
|
142
|
+
description: "Economic calendar and statistics, consumer prices, yield curve, rate path, money markets, central bank rates, volatility, and market valuation.",
|
|
143
|
+
toggleable: true,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const creditPluginMeta = {
|
|
147
|
+
id: "credit",
|
|
148
|
+
stateId: "macro",
|
|
149
|
+
name: "Credit & Bonds",
|
|
42
150
|
version: "1.0.0",
|
|
151
|
+
description: "Single-name, index and sovereign CDS, credit spreads, Treasury auctions, a bond calculator, credit documents and covenants, and debt maturities.",
|
|
43
152
|
toggleable: true,
|
|
44
153
|
};
|
|
45
154
|
|
|
46
|
-
export const
|
|
47
|
-
id: "
|
|
48
|
-
|
|
155
|
+
export const earningsPluginMeta = {
|
|
156
|
+
id: "earnings",
|
|
157
|
+
stateId: "macro",
|
|
158
|
+
name: "Earnings",
|
|
49
159
|
version: "1.0.0",
|
|
50
160
|
toggleable: true,
|
|
51
161
|
};
|
|
@@ -18,7 +18,7 @@ function Evidence({ event, width, height }: { event: CatalystEvent; width: numbe
|
|
|
18
18
|
const facts = catalystFacts(event);
|
|
19
19
|
const procedures = Array.isArray(event.metadata?.proceduralStatements) ? event.metadata.proceduralStatements.filter((value): value is string => typeof value === "string") : [];
|
|
20
20
|
const documents = Array.isArray(event.metadata?.documents) ? event.metadata.documents.filter((value): value is { url: string; title?: string; label?: string } => !!value && typeof value === "object" && "url" in value && typeof value.url === "string" && /^https?:\/\//.test(value.url)) : [];
|
|
21
|
-
return <ScrollBox width={width} height={desktop ? undefined : height} flexGrow={1} flexBasis={0} contentOptions={{ paddingX: 1 }}>
|
|
21
|
+
return <ScrollBox scrollY width={width} height={desktop ? undefined : height} flexGrow={1} flexBasis={0} contentOptions={{ paddingX: 1 }}>
|
|
22
22
|
<KeyValueRow label="Classification" value={`${catalystLabel(event.type)} · ${catalystAgency(event.agency)} · ${event.country ?? event.jurisdiction}`} labelWidth={18} />
|
|
23
23
|
<KeyValueRow label="Status" value={humanLabel(event.status)} detail={`Revision ${event.revision}`} labelWidth={18} />
|
|
24
24
|
<KeyValueRow label="Announced" value={catalystDate(event, "announced")} labelWidth={18} />
|
|
@@ -1145,7 +1145,7 @@ export function ChartComposerPane({ paneId, focused, width, height }: PaneProps)
|
|
|
1145
1145
|
}
|
|
1146
1146
|
}, [follows, ownedIds, savedIds, setSpec, spec, stored, target, unlinkedFrom, updateSettings]);
|
|
1147
1147
|
if (follows && !target && ownedIds.length > 0) {
|
|
1148
|
-
return <EmptyState title={error ?? "
|
|
1148
|
+
return <EmptyState title={error ?? "Select a ticker."} />;
|
|
1149
1149
|
}
|
|
1150
1150
|
return (
|
|
1151
1151
|
<ChartComposerSurface
|
|
@@ -3,9 +3,10 @@ import type { ChatChannel, ChatChannelState } from "../../../api-client";
|
|
|
3
3
|
import type { AppConfig } from "../../../types/config";
|
|
4
4
|
import { t } from "../../../i18n";
|
|
5
5
|
import { chatController } from "./controller";
|
|
6
|
-
import { formatChannelLabel } from "./channel-labels";
|
|
7
6
|
import { describeConversationStartError, knownConversationRefusal } from "./direct-messages";
|
|
8
7
|
import { buildChatUserByUsername } from "./content/user-map";
|
|
8
|
+
import { chatConversationRow, chatNewDmRow, openChatConversation, openChatNewDm } from "./command-bar";
|
|
9
|
+
import { buildChatConversations, selectChatConversations } from "./conversations";
|
|
9
10
|
|
|
10
11
|
export const DEFAULT_CHAT_CHANNEL_ID = "everyone";
|
|
11
12
|
export const LAST_VISITED_CHAT_CHANNEL_KEY = "lastChatChannelId";
|
|
@@ -42,20 +43,6 @@ export function getPreferredChatOpenChannelId(
|
|
|
42
43
|
return normalizeChannelId(unreadConversation?.channelId ?? unreadStates[0]?.channelId ?? getLastVisitedChatChannelId(config));
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
function conversationDetail(channel: ChatChannel): string {
|
|
46
|
-
if (channel.kind === "direct") {
|
|
47
|
-
const displayName = channel.dmUser?.displayName?.trim();
|
|
48
|
-
const username = channel.dmUser?.username?.trim();
|
|
49
|
-
return [displayName && username ? displayName : null, t("Direct message")].filter(Boolean).join(" - ");
|
|
50
|
-
}
|
|
51
|
-
const members = (channel.members ?? [])
|
|
52
|
-
.map((member) => member.username ? `@${member.username}` : member.displayName)
|
|
53
|
-
.filter(Boolean)
|
|
54
|
-
.slice(0, 4);
|
|
55
|
-
const suffix = (channel.members?.length ?? 0) > members.length ? ` +${(channel.members?.length ?? 0) - members.length}` : "";
|
|
56
|
-
return members.length > 0 ? `${t("Group chat")} - ${members.join(", ")}${suffix}` : t("Group chat");
|
|
57
|
-
}
|
|
58
|
-
|
|
59
46
|
export function parseDmUsernames(value: string): string[] {
|
|
60
47
|
const usernames = new Set<string>();
|
|
61
48
|
for (const rawPart of value.split(/[\s,]+/)) {
|
|
@@ -106,18 +93,40 @@ export async function openDmTargetFromCommand(ctx: GloomPluginContext, usernames
|
|
|
106
93
|
} catch (error) {
|
|
107
94
|
throw new Error(describeConversationStartError(error, usernames, userByUsername));
|
|
108
95
|
}
|
|
109
|
-
|
|
96
|
+
openChatConversation(ctx, channel.id);
|
|
110
97
|
}
|
|
111
98
|
|
|
99
|
+
/**
|
|
100
|
+
* `DM` in the command bar. Bare, it lists your DMs and groups, unread first,
|
|
101
|
+
* then the latest, and ends with New DM, which opens the Chat pane's dialog.
|
|
102
|
+
* With names after it, the conversations those names find come first, then
|
|
103
|
+
* the row that starts or opens a DM with exactly what was typed.
|
|
104
|
+
*/
|
|
112
105
|
export function buildDmCommandResults(ctx: GloomPluginContext, arg: string): CommandResultDef[] {
|
|
106
|
+
const list = chatController.listConversations();
|
|
107
|
+
const conversations = list
|
|
108
|
+
? buildChatConversations(list.states, list.userId)
|
|
109
|
+
.filter((conversation) => conversation.kind === "direct" || conversation.kind === "group")
|
|
110
|
+
: [];
|
|
111
|
+
const open = (channelId: string) => openChatConversation(ctx, channelId);
|
|
112
|
+
const category = "Chat";
|
|
113
113
|
const trimmed = arg.trim();
|
|
114
114
|
if (trimmed) {
|
|
115
115
|
const usernames = parseDmUsernames(trimmed);
|
|
116
116
|
const valid = hasOnlyDmUsernameArgs(trimmed) && usernames.length > 0;
|
|
117
|
+
const found = selectChatConversations(conversations, { kind: "search", text: trimmed });
|
|
118
|
+
const rows: CommandResultDef[] = found.map((conversation) => ({
|
|
119
|
+
...chatConversationRow(conversation, open),
|
|
120
|
+
category,
|
|
121
|
+
}));
|
|
122
|
+
// The DM you already share is the row above; a second one would open it too.
|
|
123
|
+
const alreadyShared = usernames.length === 1
|
|
124
|
+
&& found.some((conversation) => conversation.kind === "direct" && conversation.username === usernames[0]);
|
|
125
|
+
if (alreadyShared) return rows;
|
|
117
126
|
const label = usernames.length <= 1
|
|
118
127
|
? `DM ${usernames[0] ? `@${usernames[0]}` : trimmed}`
|
|
119
128
|
: `Group ${usernames.map((username) => `@${username}`).join(", ")}`;
|
|
120
|
-
return [{
|
|
129
|
+
return [...rows, {
|
|
121
130
|
id: `start:${valid ? usernames.join(",") : trimmed}`,
|
|
122
131
|
label,
|
|
123
132
|
detail: valid
|
|
@@ -125,40 +134,29 @@ export function buildDmCommandResults(ctx: GloomPluginContext, arg: string): Com
|
|
|
125
134
|
? t("Start or open direct message")
|
|
126
135
|
: t("Start group chat")
|
|
127
136
|
: t("Use @username, or multiple usernames for a group chat"),
|
|
128
|
-
category
|
|
129
|
-
|
|
137
|
+
category,
|
|
138
|
+
// No tag: it starts a conversation, so it must not read as one.
|
|
139
|
+
right: "",
|
|
130
140
|
disabled: !valid,
|
|
131
141
|
execute: () => openDmTargetFromCommand(ctx, usernames),
|
|
132
142
|
}];
|
|
133
143
|
}
|
|
134
144
|
|
|
135
|
-
|
|
136
|
-
.filter((channel) => channel.kind === "direct" || channel.kind === "group");
|
|
137
|
-
if (conversations.length === 0) {
|
|
145
|
+
if (!list) {
|
|
138
146
|
return [{
|
|
139
147
|
id: "empty",
|
|
140
148
|
label: t("No DMs yet"),
|
|
141
149
|
detail: t("Type DM @username to start one"),
|
|
142
|
-
category
|
|
150
|
+
category,
|
|
143
151
|
right: "DM",
|
|
144
152
|
disabled: true,
|
|
145
153
|
execute: () => {},
|
|
146
154
|
}];
|
|
147
155
|
}
|
|
148
156
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
detail: conversationDetail(channel),
|
|
153
|
-
category: t("Conversations"),
|
|
154
|
-
right: channel.kind === "group" ? t("Group") : t("DM"),
|
|
155
|
-
keywords: [
|
|
156
|
-
channel.name,
|
|
157
|
-
channel.dmUser?.username ?? "",
|
|
158
|
-
...(channel.members ?? []).map((member) => member.username ?? member.displayName),
|
|
159
|
-
],
|
|
160
|
-
execute: () => openChatChannelFromCommand(ctx, channel.id),
|
|
161
|
-
}));
|
|
157
|
+
const listed = selectChatConversations(conversations, { kind: "listing" })
|
|
158
|
+
.map((conversation) => ({ ...chatConversationRow(conversation, open), category }));
|
|
159
|
+
return [...listed, { ...chatNewDmRow(() => openChatNewDm(ctx)), category }];
|
|
162
160
|
}
|
|
163
161
|
|
|
164
162
|
/**
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { t, tf } from "../../../i18n";
|
|
2
|
+
import type {
|
|
3
|
+
CommandBarResultDef,
|
|
4
|
+
CommandBarSearchProvider,
|
|
5
|
+
GloomPluginContext,
|
|
6
|
+
} from "../../../types/plugin";
|
|
7
|
+
import { chatController } from "./controller";
|
|
8
|
+
import {
|
|
9
|
+
buildChatConversations,
|
|
10
|
+
parseChatConversationQuery,
|
|
11
|
+
selectChatConversations,
|
|
12
|
+
type ChatConversation,
|
|
13
|
+
type ChatConversationKind,
|
|
14
|
+
type ChatConversationList,
|
|
15
|
+
} from "./conversations";
|
|
16
|
+
import { requestChatPaneAction } from "./pane-requests";
|
|
17
|
+
|
|
18
|
+
const CHAT_PANE_TEMPLATE_ID = "new-chat-pane";
|
|
19
|
+
/**
|
|
20
|
+
* Pane template value: show the conversation in a Chat pane that is already
|
|
21
|
+
* open (switching it over) rather than in a pane of its own.
|
|
22
|
+
*/
|
|
23
|
+
export const CHAT_REUSE_PANE_VALUE = "reusePane";
|
|
24
|
+
const CHAT_SEARCH_PROVIDER_ID = "chat:conversations";
|
|
25
|
+
|
|
26
|
+
type OpenChatPane = Pick<GloomPluginContext, "createPaneFromTemplate">;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Shows a conversation with its composer ready: in the open Chat pane, which
|
|
30
|
+
* switches to it, or in a new one when none is open.
|
|
31
|
+
*/
|
|
32
|
+
export function openChatConversation(ctx: OpenChatPane, channelId: string): void {
|
|
33
|
+
requestChatPaneAction({ action: "compose", channelId });
|
|
34
|
+
ctx.createPaneFromTemplate(CHAT_PANE_TEMPLATE_ID, {
|
|
35
|
+
arg: channelId,
|
|
36
|
+
values: { [CHAT_REUSE_PANE_VALUE]: "1" },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The Chat pane's own New DM dialog, in the open Chat pane or a new one. */
|
|
41
|
+
export function openChatNewDm(ctx: OpenChatPane): void {
|
|
42
|
+
requestChatPaneAction({ action: "new-dm" });
|
|
43
|
+
ctx.createPaneFromTemplate(CHAT_PANE_TEMPLATE_ID, { values: { [CHAT_REUSE_PANE_VALUE]: "1" } });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const BADGES: Record<ChatConversationKind, string> = {
|
|
47
|
+
channel: "CHAT",
|
|
48
|
+
team: "TEAM",
|
|
49
|
+
direct: "DM",
|
|
50
|
+
group: "GROUP",
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const DETAILS: Record<ChatConversationKind, string> = {
|
|
54
|
+
channel: "Channel",
|
|
55
|
+
team: "Team channel",
|
|
56
|
+
direct: "Direct message",
|
|
57
|
+
group: "Group chat",
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function unreadLabel(count: number): string {
|
|
61
|
+
return tf("{count} unread", { count: count > 99 ? "99+" : String(count) });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* One conversation as a command-bar row: a tag that tells channels from DMs,
|
|
66
|
+
* the name the Chat pane shows, and the unread count on the right. `right` is
|
|
67
|
+
* empty rather than unset so a command's shortcut never stands in for it.
|
|
68
|
+
*/
|
|
69
|
+
export function chatConversationRow(
|
|
70
|
+
conversation: ChatConversation,
|
|
71
|
+
open: (channelId: string) => void,
|
|
72
|
+
): CommandBarResultDef {
|
|
73
|
+
return {
|
|
74
|
+
id: `conversation:${conversation.channelId}`,
|
|
75
|
+
label: conversation.title,
|
|
76
|
+
...(conversation.name ? { name: conversation.name } : {}),
|
|
77
|
+
badge: BADGES[conversation.kind],
|
|
78
|
+
detail: t(DETAILS[conversation.kind]),
|
|
79
|
+
right: conversation.unreadCount > 0 ? unreadLabel(conversation.unreadCount) : "",
|
|
80
|
+
keywords: [...conversation.words, "chat", "messages"],
|
|
81
|
+
execute: () => open(conversation.channelId),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function chatNewDmRow(open: () => void): CommandBarResultDef {
|
|
86
|
+
return {
|
|
87
|
+
id: "new-dm",
|
|
88
|
+
label: t("New DM"),
|
|
89
|
+
detail: t("Start a direct or group chat"),
|
|
90
|
+
right: "",
|
|
91
|
+
keywords: ["new", "dm", "direct", "message", "group", "chat"],
|
|
92
|
+
execute: open,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface ChatSearchSource {
|
|
97
|
+
listConversations(): ChatConversationList | null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The conversations that answer the command bar's text, from the chat state
|
|
102
|
+
* the Chat pane already keeps: nothing is asked of the server. Signed out or
|
|
103
|
+
* unverified there is nothing, and the Chat pane command is all there is.
|
|
104
|
+
*/
|
|
105
|
+
export function buildChatSearchRows(
|
|
106
|
+
ctx: OpenChatPane,
|
|
107
|
+
query: string,
|
|
108
|
+
source: ChatSearchSource = chatController,
|
|
109
|
+
): CommandBarResultDef[] {
|
|
110
|
+
const list = source.listConversations();
|
|
111
|
+
if (!list) return [];
|
|
112
|
+
const parsed = parseChatConversationQuery(query);
|
|
113
|
+
const conversations = selectChatConversations(buildChatConversations(list.states, list.userId), parsed);
|
|
114
|
+
const rows = conversations.map((conversation) => chatConversationRow(conversation, (channelId) => {
|
|
115
|
+
openChatConversation(ctx, channelId);
|
|
116
|
+
}));
|
|
117
|
+
return parsed.kind === "listing" ? [...rows, chatNewDmRow(() => openChatNewDm(ctx))] : rows;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Channels and DMs in the command bar: typing part of a channel or a person
|
|
122
|
+
* finds the conversation, "chat" and "messages" list them, and the empty bar
|
|
123
|
+
* shows the unread and latest ones. Answered from memory on every keystroke.
|
|
124
|
+
*/
|
|
125
|
+
export function createChatSearchProvider(
|
|
126
|
+
ctx: OpenChatPane,
|
|
127
|
+
source: ChatSearchSource = chatController,
|
|
128
|
+
): CommandBarSearchProvider {
|
|
129
|
+
const match = (query: string) => buildChatSearchRows(ctx, query, source);
|
|
130
|
+
return {
|
|
131
|
+
id: CHAT_SEARCH_PROVIDER_ID,
|
|
132
|
+
category: "Chat",
|
|
133
|
+
minQueryLength: 0,
|
|
134
|
+
shortcuts: ["CHAT"],
|
|
135
|
+
match,
|
|
136
|
+
provide: async (query) => match(query),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
export type ChatComposerCommand =
|
|
2
2
|
| { kind: "direct"; username: string; draft: string }
|
|
3
|
-
| { kind: "group"; usernames: string[]; name: string | undefined }
|
|
3
|
+
| { kind: "group"; usernames: string[]; name: string | undefined }
|
|
4
|
+
| DiscordComposerCommand;
|
|
5
|
+
|
|
6
|
+
/** `usage` is anything after /discord that is not a subcommand: it answers with the usage instead of going to the channel as text. */
|
|
7
|
+
export type DiscordComposerCommand =
|
|
8
|
+
| { kind: "discord"; action: "connect" | "unlink" | "usage" }
|
|
9
|
+
| { kind: "discord"; action: "mirror"; enabled: boolean };
|
|
4
10
|
|
|
5
11
|
const USERNAME_PATTERN = "[A-Za-z][A-Za-z0-9_]{2,29}";
|
|
6
12
|
const DIRECT_COMMAND_RE = new RegExp(`^/dm\\s+@?(${USERNAME_PATTERN})(?:\\s+([\\s\\S]+))?$`, "i");
|
|
7
13
|
const GROUP_COMMAND_RE = /^\/group\s+(.+)$/i;
|
|
14
|
+
const DISCORD_COMMAND_RE = /^\/discord(?:\s+([\s\S]*))?$/i;
|
|
8
15
|
const GROUP_USERNAME_RE = new RegExp(`@(${USERNAME_PATTERN})`, "g");
|
|
9
16
|
|
|
17
|
+
function parseDiscordArguments(rest: string): DiscordComposerCommand {
|
|
18
|
+
const [subcommand, value, ...extra] = rest.toLowerCase().split(/\s+/).filter(Boolean);
|
|
19
|
+
if (!subcommand) return { kind: "discord", action: "connect" };
|
|
20
|
+
if (subcommand === "unlink" && value === undefined) return { kind: "discord", action: "unlink" };
|
|
21
|
+
if (subcommand === "mirror" && extra.length === 0 && (value === "on" || value === "off")) {
|
|
22
|
+
return { kind: "discord", action: "mirror", enabled: value === "on" };
|
|
23
|
+
}
|
|
24
|
+
return { kind: "discord", action: "usage" };
|
|
25
|
+
}
|
|
26
|
+
|
|
10
27
|
export function parseChatComposerCommand(content: string): ChatComposerCommand | null {
|
|
28
|
+
const discordCommand = content.match(DISCORD_COMMAND_RE);
|
|
29
|
+
if (discordCommand) return parseDiscordArguments(discordCommand[1] ?? "");
|
|
30
|
+
|
|
11
31
|
const directCommand = content.match(DIRECT_COMMAND_RE);
|
|
12
32
|
if (directCommand) {
|
|
13
33
|
return {
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
COMPOSER_ACTION_WIDTH,
|
|
6
6
|
formatInlinePreview,
|
|
7
7
|
} from "../layout";
|
|
8
|
-
import { parseChatComposerCommand } from "../composer-commands";
|
|
8
|
+
import { parseChatComposerCommand, type DiscordComposerCommand } from "../composer-commands";
|
|
9
9
|
import { t, tf } from "../../../../i18n";
|
|
10
10
|
import type { ChatContentController } from "./types";
|
|
11
11
|
import { getComposerCursorOffset, moveComposerCursorToOffset } from "./composer-cursor";
|
|
@@ -45,6 +45,7 @@ export function useChatComposerRuntime({
|
|
|
45
45
|
draftAttachmentCount = 0,
|
|
46
46
|
onPastedImagePaths,
|
|
47
47
|
onConversationStartError,
|
|
48
|
+
onDiscordCommand,
|
|
48
49
|
}: {
|
|
49
50
|
applyingExternalDraftRef: MutableRef<boolean>;
|
|
50
51
|
blurInput: () => void;
|
|
@@ -77,6 +78,8 @@ export function useChatComposerRuntime({
|
|
|
77
78
|
onPastedImagePaths?: (paths: string[], pasted: string) => void;
|
|
78
79
|
/** A `/dm` or `/group` command the server refused, with the names it asked for. */
|
|
79
80
|
onConversationStartError?: (error: unknown, usernames: string[]) => void;
|
|
81
|
+
/** Runs a /discord command. Without it the command is sent as text. */
|
|
82
|
+
onDiscordCommand?: (command: DiscordComposerCommand) => void;
|
|
80
83
|
}) {
|
|
81
84
|
const replyToRef = useRef(replyTo);
|
|
82
85
|
const draftAttachmentCountRef = useRef(draftAttachmentCount);
|
|
@@ -262,6 +265,11 @@ export function useChatComposerRuntime({
|
|
|
262
265
|
}).catch((error: unknown) => onConversationStartError?.(error, [composerCommand.username]));
|
|
263
266
|
return;
|
|
264
267
|
}
|
|
268
|
+
if (composerCommand?.kind === "discord" && onDiscordCommand) {
|
|
269
|
+
replaceComposerDraft("");
|
|
270
|
+
onDiscordCommand(composerCommand);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
265
273
|
if (composerCommand?.kind === "group") {
|
|
266
274
|
void controller.openGroupChannel({
|
|
267
275
|
usernames: composerCommand.usernames,
|
|
@@ -293,8 +301,10 @@ export function useChatComposerRuntime({
|
|
|
293
301
|
inputValueRef,
|
|
294
302
|
onChannelChange,
|
|
295
303
|
onConversationStartError,
|
|
304
|
+
onDiscordCommand,
|
|
296
305
|
expandDirectSection,
|
|
297
306
|
persistDraft,
|
|
307
|
+
replaceComposerDraft,
|
|
298
308
|
setEditingMessage,
|
|
299
309
|
setFollowMessages,
|
|
300
310
|
setSelectedIdx,
|
|
@@ -6,6 +6,7 @@ import type { InlineTickerCatalogEntry } from "../../../../state/hooks/inline-ti
|
|
|
6
6
|
import type { ContextMenuItem } from "../../../../types/context-menu";
|
|
7
7
|
import { useRendererHost, useUiCapabilities } from "../../../../ui";
|
|
8
8
|
import { useOptionalDialog, type PromptContext } from "../../../../ui/dialog";
|
|
9
|
+
import { chatAuthorName, isDiscordGhost } from "../ghost-user";
|
|
9
10
|
import { chatMessageOpenTargets, type ChatOpenTarget } from "./open-targets";
|
|
10
11
|
import { openChatImageViewer } from "../attachments/desktop";
|
|
11
12
|
|
|
@@ -105,7 +106,7 @@ export function useChatFooter({
|
|
|
105
106
|
attachments: selectedMessage.attachments,
|
|
106
107
|
index: target.index,
|
|
107
108
|
caption: selectedMessage.content,
|
|
108
|
-
author: selectedMessage.user
|
|
109
|
+
author: chatAuthorName(selectedMessage.user),
|
|
109
110
|
});
|
|
110
111
|
return;
|
|
111
112
|
}
|
|
@@ -135,7 +136,8 @@ export function useChatFooter({
|
|
|
135
136
|
if (target) openTarget(target);
|
|
136
137
|
}, [dialog, openTarget, openTargets]);
|
|
137
138
|
|
|
138
|
-
|
|
139
|
+
// A Discord ghost has no card to show.
|
|
140
|
+
const author = selectedMessage && !isDiscordGhost(selectedMessage.user) ? selectedMessage.user : null;
|
|
139
141
|
// A card opened from the keyboard follows the selection: moving on closes it.
|
|
140
142
|
const keyboardProfileRef = useRef(false);
|
|
141
143
|
const toggleAuthorProfile = useCallback(() => {
|
|
@@ -215,7 +217,7 @@ export function useChatFooter({
|
|
|
215
217
|
else if (canSend) hints.push({ id: "reply", key: "Enter", label: "reply", title: "Reply", onPress: () => latest.current.beginReplyTo(latest.current.selectedIdx, { deferFocus: true }) });
|
|
216
218
|
if (canEdit) hints.push({ id: "edit", key: "e", label: "dit", title: "Edit Message", onPress: () => { latest.current.beginEditMessage(latest.current.selectedIdx, { deferFocus: true }); } });
|
|
217
219
|
if (openTargets.length > 0) hints.push({ id: "open", key: "o", label: "pen", title: openTargetTitle, onPress: () => { void latest.current.openSelectedTargets(); } });
|
|
218
|
-
// Every author has a card, if only their name and a way to write to them.
|
|
220
|
+
// Every author but a Discord ghost has a card, if only their name and a way to write to them.
|
|
219
221
|
if (author) hints.push({ id: "profile", key: "p", label: "rofile", title: "Show Profile", onPress: () => latest.current.toggleAuthorProfile() });
|
|
220
222
|
} else if (!composing && canSend) {
|
|
221
223
|
hints.push({ id: "compose", key: "i", label: " compose", title: "Compose", onPress: () => queueMicrotask(() => latest.current.focusComposer()) });
|