gloomberb 0.16.3 → 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/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/commands/crypto-hints.ts +12 -4
- package/src/cli/commands/market.ts +359 -99
- package/src/cli/commands/options-chain.ts +172 -0
- package/src/cli/commands/options-leaps.ts +422 -0
- package/src/cli/commands/overview.ts +164 -26
- 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 +62 -30
- package/src/cli/desktop-pane-shot.ts +97 -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 +14 -1
- package/src/cli/index-roots.ts +51 -0
- package/src/cli/index.ts +9 -6
- package/src/cli/listing-arg.ts +211 -3
- package/src/cli/not-a-ticker.ts +37 -0
- package/src/cli/options.ts +50 -6
- package/src/cli/pane-functions/capabilities.ts +8 -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 +21 -13
- package/src/cli/pane-functions/freshness.ts +205 -26
- package/src/cli/pane-functions/function-help.ts +67 -22
- package/src/cli/pane-functions/headless.ts +5 -4
- package/src/cli/pane-functions/index.ts +3 -0
- 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 +4 -0
- package/src/cli/pane-functions/resolver.ts +4 -6
- package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
- package/src/cli/pane-functions/screenshot.ts +105 -23
- package/src/cli/registry.ts +2 -1
- package/src/cli/result.ts +50 -13
- 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/view.tsx +28 -9
- 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 +1 -1
- package/src/components/layout/shell/drag/index.ts +4 -0
- package/src/components/layout/shell/drag/live.ts +76 -5
- package/src/components/layout/shell/drag/runtime.ts +8 -8
- package/src/components/layout/shell/index.tsx +47 -6
- package/src/components/layout/shell/layout-state.ts +5 -11
- package/src/components/layout/shell/menu.ts +28 -1
- package/src/components/layout/shell/pane/actions.ts +2 -1
- package/src/components/layout/shell/pane/layers.tsx +51 -10
- 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/visible-layout.ts +44 -1
- 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/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 +118 -18
- 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/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/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/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/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-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/headless.ts +6 -1
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- 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 +109 -39
- 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/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/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/cli/portfolio-command.ts +71 -36
- package/src/plugins/builtin/portfolio-list/cli/render.ts +87 -18
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
- package/src/plugins/builtin/portfolio-list/headless.ts +38 -7
- 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/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/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 +4 -4
- 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/supply-chain/model.ts +1 -1
- package/src/plugins/builtin/ticker-detail/headless.ts +43 -21
- 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/geo-series.ts +1 -1
- package/src/plugins/builtin/world-venue-map/index.ts +1 -1
- 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/dom/desktop/icons.tsx +14 -0
- package/src/renderers/dom/desktop/query-bar.tsx +59 -32
- package/src/renderers/dom/host/box.tsx +1 -1
- package/src/renderers/dom/host/drag-shield.ts +17 -6
- package/src/renderers/dom/host/live-frame.ts +10 -2
- package/src/renderers/dom/input-host.tsx +6 -1
- package/src/renderers/dom/styles.css +13 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +5 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -1
- package/src/renderers/electrobun/shared/protocol.ts +2 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
- package/src/renderers/electrobun/view/main.tsx +4 -1
- package/src/renderers/opentui/input-host.tsx +6 -1
- package/src/renderers/opentui/start.tsx +9 -0
- package/src/shares/portable-layout.ts +16 -6
- package/src/sources/gloomberb-cloud/index.ts +19 -10
- package/src/sources/gloomberb-cloud/normalizers.ts +4 -1
- package/src/sources/listing-symbols.ts +11 -5
- package/src/sources/provider-errors.ts +88 -3
- package/src/sources/provider-router/cached-routes.ts +8 -4
- package/src/sources/provider-router/financial-routes.ts +11 -5
- package/src/sources/provider-router/financials.ts +42 -15
- 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 +23 -5
- package/src/sources/sec-edgar.ts +68 -8
- 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/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/resolve.ts +1 -14
- package/src/types/config.ts +61 -1
- 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 +8 -4
- package/src/ui/index.tsx +2 -0
- package/src/utils/cli-output.ts +14 -1
- package/src/utils/exchanges.ts +13 -0
- package/src/utils/format.ts +2 -1
- package/src/utils/fx-market-hours.ts +34 -0
- package/src/utils/option-expiry.ts +126 -0
- 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
|
@@ -3,6 +3,7 @@ import { getCurrentPluginTarget } from "../plugins/current-target";
|
|
|
3
3
|
import type { DesktopDeepLinkBridge } from "../types/desktop-deeplink";
|
|
4
4
|
import { exposeExperiment, storedExperimentAssignments } from "./web-experiments";
|
|
5
5
|
import { createWallExperimentSession, type WallExperiment, type WallExperimentContext, type WallTeaserVariant } from "./wall-experiments";
|
|
6
|
+
import { createUpgradeExperimentSession, UPGRADE_PERSONALIZED, type UpgradeExperimentContext, type UpgradePersonalizedVariant } from "./upgrade-experiment";
|
|
6
7
|
import { hasProAccess } from "./plan-rules";
|
|
7
8
|
import { withDeadline } from "../utils/async-deadline";
|
|
8
9
|
|
|
@@ -38,6 +39,7 @@ let anonymousId: string | undefined;
|
|
|
38
39
|
let attribution: Record<string, string> = {};
|
|
39
40
|
const wallExperiment = createWallExperimentSession();
|
|
40
41
|
const signinWallExperiment = createWallExperimentSession("wall_teaser_signin");
|
|
42
|
+
const upgradeExperiment = createUpgradeExperimentSession();
|
|
41
43
|
const WALL_VIEWS_SESSION_KEY = "gloomberb.wall-teaser.views";
|
|
42
44
|
|
|
43
45
|
const ATTRIBUTION_STORAGE_KEY = "gloomberb.web.attribution";
|
|
@@ -524,6 +526,35 @@ export function exposeWallTeaser(experiment: WallExperiment = "wall_teaser"): Pr
|
|
|
524
526
|
});
|
|
525
527
|
}
|
|
526
528
|
|
|
529
|
+
function upgradeExperimentContext(): UpgradeExperimentContext {
|
|
530
|
+
const { accountId, pro, optedOut, automated, session } = wallExperimentContext();
|
|
531
|
+
return { accountId, pro, optedOut, automated, session };
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/** The `upgrade_personalized` arm this session already has, without asking; see upgrade-experiment.ts. */
|
|
535
|
+
export function knownUpgradePersonalized(): UpgradePersonalizedVariant | null | undefined {
|
|
536
|
+
return upgradeExperiment.known(upgradeExperimentContext());
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Asks for the `upgrade_personalized` arm when an upgrade surface is shown to
|
|
541
|
+
* a free account that has tickers to name, once per account and session. The
|
|
542
|
+
* request says where it showed and nothing about the holdings.
|
|
543
|
+
*/
|
|
544
|
+
export function exposeUpgradePersonalized(placement: string): Promise<UpgradePersonalizedVariant | null> {
|
|
545
|
+
const context = upgradeExperimentContext();
|
|
546
|
+
const surface = getCurrentPluginTarget();
|
|
547
|
+
return upgradeExperiment.expose(context, () => {
|
|
548
|
+
const controller = new AbortController();
|
|
549
|
+
return withDeadline(apiClient.recordExperimentExposure({
|
|
550
|
+
eventId: crypto.randomUUID(),
|
|
551
|
+
surface,
|
|
552
|
+
experiment: UPGRADE_PERSONALIZED,
|
|
553
|
+
...(ACTIVITY_ID.test(placement) ? { placement } : {}),
|
|
554
|
+
}, controller.signal), 3_000, "Upgrade experiment request timed out", (error) => controller.abort(error));
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
|
|
527
558
|
/** Records the visible wall once per placement, including visitors outside the experiment. */
|
|
528
559
|
export function recordWallViewed(placement: string, teaserKind?: "summary" | "sample" | "none"): void {
|
|
529
560
|
recordResearchActivity("wall_viewed", undefined, undefined, { placement, teaser_kind: teaserKind });
|
package/src/api-client/socket.ts
CHANGED
|
@@ -33,7 +33,9 @@ const QUOTE_SUBSCRIPTION_FLUSH_MS = 25;
|
|
|
33
33
|
const MARKET_BATCH_FEATURE = "market.batch";
|
|
34
34
|
/** Chat frames carry `attachments`; without it the server writes images into the text. */
|
|
35
35
|
export const CHAT_ATTACHMENTS_FEATURE = "chat.attachments";
|
|
36
|
-
|
|
36
|
+
/** The app can run terminal tools relayed from a remote assistant; it still announces itself first. */
|
|
37
|
+
export const TERMINAL_RELAY_FEATURE = "terminal.relay";
|
|
38
|
+
const CLIENT_SOCKET_FEATURES = [MARKET_BATCH_FEATURE, CHAT_ATTACHMENTS_FEATURE, TERMINAL_RELAY_FEATURE] as const;
|
|
37
39
|
const cloudApiLog = debugLog.createLogger("cloud-api");
|
|
38
40
|
|
|
39
41
|
type ChannelListener = (message: ChatMessage) => void;
|
|
@@ -131,6 +133,7 @@ export class CloudApiSocket {
|
|
|
131
133
|
/** What the last "ready" frame offered; kept across reconnects so a blip does not hide features. */
|
|
132
134
|
private offeredFeatures: ReadonlySet<string> = new Set();
|
|
133
135
|
private readonly offeredFeatureListeners = new Set<() => void>();
|
|
136
|
+
private readonly connectionListeners = new Set<(event: "ready" | "closed") => void>();
|
|
134
137
|
private readonly tapeListeners = new Map<string, { symbol: string; exchange: string; listeners: Set<(event: TapeFeedEvent) => void> }>();
|
|
135
138
|
private readonly scannerListeners = new Map<
|
|
136
139
|
ScannerKind,
|
|
@@ -164,6 +167,7 @@ export class CloudApiSocket {
|
|
|
164
167
|
const ws = this.ws;
|
|
165
168
|
this.ws = null;
|
|
166
169
|
if (ws) {
|
|
170
|
+
this.emitConnection("closed");
|
|
167
171
|
cloudApiLog.info("teardown websocket");
|
|
168
172
|
this.health.reportSocketState(
|
|
169
173
|
GLOOM_CLOUD_SOCKET_CONNECTION_ID,
|
|
@@ -260,6 +264,29 @@ export class CloudApiSocket {
|
|
|
260
264
|
};
|
|
261
265
|
}
|
|
262
266
|
|
|
267
|
+
/** "ready" after each server hello (features negotiated), "closed" when that connection ends. */
|
|
268
|
+
subscribeConnection(listener: (event: "ready" | "closed") => void): () => void {
|
|
269
|
+
this.connectionListeners.add(listener);
|
|
270
|
+
return () => {
|
|
271
|
+
this.connectionListeners.delete(listener);
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Sends one frame on the open connection; false when there is none. */
|
|
276
|
+
sendFrame(payload: Record<string, unknown>): boolean {
|
|
277
|
+
if (this.ws?.readyState !== WebSocket.OPEN) return false;
|
|
278
|
+
try {
|
|
279
|
+
this.ws.send(JSON.stringify(payload));
|
|
280
|
+
return true;
|
|
281
|
+
} catch {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private emitConnection(event: "ready" | "closed"): void {
|
|
287
|
+
for (const listener of this.connectionListeners) listener(event);
|
|
288
|
+
}
|
|
289
|
+
|
|
263
290
|
subscribeTeamNotifications(listener: TeamNotificationListener): () => void {
|
|
264
291
|
this.teamNotificationListeners.add(listener);
|
|
265
292
|
this.syncAuthState();
|
|
@@ -470,6 +497,7 @@ export class CloudApiSocket {
|
|
|
470
497
|
});
|
|
471
498
|
this.channelListeners.clear();
|
|
472
499
|
this.offeredFeatureListeners.clear();
|
|
500
|
+
this.connectionListeners.clear();
|
|
473
501
|
this.chatNotificationListeners.clear();
|
|
474
502
|
this.chatPresenceListeners.clear();
|
|
475
503
|
this.teamNotificationListeners.clear();
|
|
@@ -500,13 +528,15 @@ export class CloudApiSocket {
|
|
|
500
528
|
if (parsed?.type === "ready") {
|
|
501
529
|
recordServerClockSample(parsed.serverTime);
|
|
502
530
|
this.negotiateSocketFeatures(parsed.features);
|
|
503
|
-
if (
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
531
|
+
if (parsed.user) {
|
|
532
|
+
cloudApiLog.info("websocket ready", {
|
|
533
|
+
emailVerified: parsed.user.emailVerified === true,
|
|
534
|
+
});
|
|
535
|
+
this.delegate.updateCurrentUserFromSocket(
|
|
536
|
+
parsed.user as Partial<AuthUser>,
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
this.emitConnection("ready");
|
|
510
540
|
return;
|
|
511
541
|
}
|
|
512
542
|
|
|
@@ -769,6 +799,7 @@ export class CloudApiSocket {
|
|
|
769
799
|
tokenSource: usingWebSocketToken ? "websocket" : "session",
|
|
770
800
|
});
|
|
771
801
|
if (!activeSocket) return;
|
|
802
|
+
this.emitConnection("closed");
|
|
772
803
|
this.health.reportSocketState(
|
|
773
804
|
GLOOM_CLOUD_SOCKET_CONNECTION_ID,
|
|
774
805
|
"closed",
|
package/src/api-client/types.ts
CHANGED
|
@@ -10,10 +10,20 @@ import type {
|
|
|
10
10
|
} from "../types/financials";
|
|
11
11
|
import type { SyncSettings, SyncSnapshot } from "../sync/types";
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* What stands behind a chat account. `discord` is a person on the Gloom
|
|
15
|
+
* Discord who has not linked a Gloom account: a ghost with no profile, no DMs.
|
|
16
|
+
* A server that predates the field leaves it out, which reads as a human.
|
|
17
|
+
*/
|
|
18
|
+
export type ChatAccountType = "human" | "managed_llm" | "discord";
|
|
19
|
+
|
|
20
|
+
/** Where a message was written. A message without one came from the app. */
|
|
21
|
+
export type ChatMessageOrigin = "app" | "discord";
|
|
22
|
+
|
|
13
23
|
export interface ChatUserSummary {
|
|
14
24
|
id: string;
|
|
15
25
|
/** A person, a managed assistant (Gloombot), or a Discord user bridged into public channels. */
|
|
16
|
-
accountType?:
|
|
26
|
+
accountType?: ChatAccountType;
|
|
17
27
|
username: string | null;
|
|
18
28
|
displayName: string;
|
|
19
29
|
bio?: string | null;
|
|
@@ -47,6 +57,7 @@ export interface ChatMessage {
|
|
|
47
57
|
createdAt: string;
|
|
48
58
|
editedAt?: string | null;
|
|
49
59
|
user: ChatUserSummary;
|
|
60
|
+
origin?: ChatMessageOrigin;
|
|
50
61
|
replyTo?: {
|
|
51
62
|
content: string;
|
|
52
63
|
user: { id?: string; username: string };
|
|
@@ -67,6 +78,15 @@ export interface ChatMessage {
|
|
|
67
78
|
clientMessageId?: string;
|
|
68
79
|
}
|
|
69
80
|
|
|
81
|
+
/** Whether this account is tied to a person on the Gloom Discord, and whether its public messages go there. */
|
|
82
|
+
export interface ChatDiscordLink {
|
|
83
|
+
linked: boolean;
|
|
84
|
+
discordUsername?: string;
|
|
85
|
+
mirror: boolean;
|
|
86
|
+
/** False when the server has linking switched off. A server that predates the field is available. */
|
|
87
|
+
available: boolean;
|
|
88
|
+
}
|
|
89
|
+
|
|
70
90
|
export interface ChatChannel {
|
|
71
91
|
id: string;
|
|
72
92
|
name: string;
|
|
@@ -225,6 +245,22 @@ export interface CloudPricing {
|
|
|
225
245
|
yearly: CloudPricingTier;
|
|
226
246
|
}
|
|
227
247
|
|
|
248
|
+
/**
|
|
249
|
+
* What an MCP key reaches beyond market data: `data` is market and filing
|
|
250
|
+
* tools only, `read` adds notes, teams and collections, `write` lets the agent
|
|
251
|
+
* save notes and change collection items.
|
|
252
|
+
*/
|
|
253
|
+
export type McpKeyScope = "data" | "read" | "write";
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* A key just created with `POST /account/mcp/keys`. `token` is the secret,
|
|
257
|
+
* which the server never returns again: show it once, never store it.
|
|
258
|
+
*/
|
|
259
|
+
export interface CreatedMcpKey {
|
|
260
|
+
token: string;
|
|
261
|
+
apiKey: { id: string; name: string; scope: McpKeyScope };
|
|
262
|
+
}
|
|
263
|
+
|
|
228
264
|
/** The account's private calendar feed link (`/account/calendar-feed`). */
|
|
229
265
|
export interface CalendarFeed {
|
|
230
266
|
url: string;
|
|
@@ -382,6 +418,17 @@ export interface CloudEconEventPayload {
|
|
|
382
418
|
impact: CloudEconImpact;
|
|
383
419
|
}
|
|
384
420
|
|
|
421
|
+
/** CPI, payrolls and FOMC statement days as published, as GET /cloud/econ/release-days serves them. */
|
|
422
|
+
export interface CloudMacroReleaseDaysPayload {
|
|
423
|
+
/** When the publishers' pages were read, ISO. */
|
|
424
|
+
checkedAt: string;
|
|
425
|
+
/** The last New York day the lists are complete for. */
|
|
426
|
+
coveredThrough: string;
|
|
427
|
+
sources: string[];
|
|
428
|
+
/** New York dates, oldest first. */
|
|
429
|
+
releases: Record<"cpi" | "jobs" | "fomc", string[]>;
|
|
430
|
+
}
|
|
431
|
+
|
|
385
432
|
export interface CloudFredObservationPayload {
|
|
386
433
|
date: string;
|
|
387
434
|
value: number | null;
|
|
@@ -408,6 +455,11 @@ export interface CloudFredSeriesPayload {
|
|
|
408
455
|
coverage?: { observations: "available"; info: "available" | "unavailable" };
|
|
409
456
|
}
|
|
410
457
|
|
|
458
|
+
/** The FRED series Gloom Cloud serves, from `GET /cloud/econ/series`; any other id is refused. */
|
|
459
|
+
export interface CloudFredSeriesCatalogPayload {
|
|
460
|
+
series: Array<{ id: string; title?: string | null; group?: string | null }>;
|
|
461
|
+
}
|
|
462
|
+
|
|
411
463
|
/**
|
|
412
464
|
* One month of Robert Shiller's dataset. FRED carries no long-run S&P earnings,
|
|
413
465
|
* so this is what every earnings-based valuation ratio is built from.
|
|
@@ -1319,6 +1371,8 @@ export interface CloudMarketResponse<T> {
|
|
|
1319
1371
|
status: CloudMarketStatus;
|
|
1320
1372
|
data: T | null;
|
|
1321
1373
|
reasonCode?: string;
|
|
1374
|
+
/** Untrusted, human-readable reason the answer is empty. Cleaned before it is shown. */
|
|
1375
|
+
message?: string;
|
|
1322
1376
|
asOf?: string;
|
|
1323
1377
|
staleAt?: string;
|
|
1324
1378
|
stale?: boolean;
|
|
@@ -1362,6 +1416,8 @@ export interface CloudMarketBatchItem<T> {
|
|
|
1362
1416
|
status: CloudMarketStatus;
|
|
1363
1417
|
data: T | null;
|
|
1364
1418
|
reasonCode?: string;
|
|
1419
|
+
/** Untrusted, human-readable reason this item is empty. Cleaned before it is shown. */
|
|
1420
|
+
message?: string;
|
|
1365
1421
|
stale?: boolean;
|
|
1366
1422
|
}
|
|
1367
1423
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { ExperimentAnswer, StorageLike } from "./web-experiments";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `upgrade_personalized`: the upgrade sheet and onboarding's Pro step name
|
|
5
|
+
* the account's own holdings, or the tickers it watches, instead of the
|
|
6
|
+
* generic line. The registry, the arm and the stop
|
|
7
|
+
* switch live with the API (gloomberb-platform `shared/experiments.ts`); the
|
|
8
|
+
* unit is the signed-in free account, so the same person gets the same arm in
|
|
9
|
+
* the terminal, the desktop app, the web terminal and on gloom.sh/cloud.
|
|
10
|
+
*
|
|
11
|
+
* The tickers never leave the device: the exposure carries the experiment and
|
|
12
|
+
* where it was shown, nothing about the holdings.
|
|
13
|
+
*/
|
|
14
|
+
export const UPGRADE_PERSONALIZED = "upgrade_personalized";
|
|
15
|
+
|
|
16
|
+
export type UpgradePersonalizedVariant = "control" | "personalized";
|
|
17
|
+
|
|
18
|
+
export interface UpgradeExperimentContext {
|
|
19
|
+
accountId?: string;
|
|
20
|
+
pro: boolean;
|
|
21
|
+
optedOut: boolean;
|
|
22
|
+
automated: boolean;
|
|
23
|
+
/** Keeps this session's answer across reloads where there is one (web, desktop). */
|
|
24
|
+
session?: StorageLike;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const SESSION_PREFIX = "gloomberb.upgrade-personalized.exposed:";
|
|
28
|
+
|
|
29
|
+
function unit(context: UpgradeExperimentContext): string | undefined {
|
|
30
|
+
if (!context.accountId || context.pro || context.optedOut || context.automated) return undefined;
|
|
31
|
+
return `account:${context.accountId}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isVariant(value: unknown): value is UpgradePersonalizedVariant {
|
|
35
|
+
return value === "control" || value === "personalized";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function read(storage: StorageLike | undefined, key: string): string | null {
|
|
39
|
+
try { return storage?.getItem(key) ?? null; } catch { return null; }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function write(storage: StorageLike | undefined, key: string, value: string): void {
|
|
43
|
+
try { storage?.setItem(key, value); } catch { /* Storage is optional in private browsing. */ }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* One exposure per account and session. An accepted arm, or the API's "not in
|
|
48
|
+
* the experiment", is kept for the session; a failed request (offline, an API
|
|
49
|
+
* that does not know the experiment yet, a timeout) is not an exposure and is
|
|
50
|
+
* asked again by the next surface.
|
|
51
|
+
*/
|
|
52
|
+
export function createUpgradeExperimentSession() {
|
|
53
|
+
const answers = new Map<string, UpgradePersonalizedVariant | null>();
|
|
54
|
+
const pending = new Map<string, Promise<UpgradePersonalizedVariant | null>>();
|
|
55
|
+
|
|
56
|
+
function known(context: UpgradeExperimentContext): UpgradePersonalizedVariant | null | undefined {
|
|
57
|
+
const identity = unit(context);
|
|
58
|
+
if (!identity) return null;
|
|
59
|
+
if (answers.has(identity)) return answers.get(identity);
|
|
60
|
+
const saved = read(context.session, `${SESSION_PREFIX}${identity}`);
|
|
61
|
+
if (saved === "excluded") return null;
|
|
62
|
+
return isVariant(saved) ? saved : undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
/** The arm already answered in this session, null outside it, undefined when not asked yet. */
|
|
67
|
+
known,
|
|
68
|
+
expose(
|
|
69
|
+
context: UpgradeExperimentContext,
|
|
70
|
+
ask: () => Promise<ExperimentAnswer>,
|
|
71
|
+
): Promise<UpgradePersonalizedVariant | null> {
|
|
72
|
+
const identity = unit(context);
|
|
73
|
+
if (!identity) return Promise.resolve(null);
|
|
74
|
+
const previous = known(context);
|
|
75
|
+
if (previous !== undefined) return Promise.resolve(previous);
|
|
76
|
+
const inFlight = pending.get(identity);
|
|
77
|
+
if (inFlight) return inFlight;
|
|
78
|
+
const request = (async () => {
|
|
79
|
+
let answer: ExperimentAnswer;
|
|
80
|
+
try { answer = await ask(); } catch { return null; }
|
|
81
|
+
const variant = answer.accepted && isVariant(answer.variant) ? answer.variant : null;
|
|
82
|
+
answers.set(identity, variant);
|
|
83
|
+
write(context.session, `${SESSION_PREFIX}${identity}`, variant ?? "excluded");
|
|
84
|
+
return variant;
|
|
85
|
+
})().finally(() => pending.delete(identity));
|
|
86
|
+
pending.set(identity, request);
|
|
87
|
+
return request;
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -14,6 +14,7 @@ import { requestFunctionHelp, useFunctionHelpHost } from "../plugins/builtin/hel
|
|
|
14
14
|
import { buildRegistryHelpIndex, helpFunctionForPane } from "../plugins/builtin/help/function-index";
|
|
15
15
|
import { recordFunctionOpen } from "../telemetry/usage-counts";
|
|
16
16
|
import { isMoveDownShortcut, isMoveUpShortcut } from "../components/command-bar/keyboard-handlers";
|
|
17
|
+
import { pressStarPromptKey } from "./star-prompt/runtime";
|
|
17
18
|
import {
|
|
18
19
|
copyActiveSelection,
|
|
19
20
|
isCopyShortcut,
|
|
@@ -154,10 +155,19 @@ export function useAppGlobalShortcuts({
|
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
// Toasts float above everything, so their keys work over a dialog too.
|
|
158
|
+
// The status bar's star line answers the same keys once no toast does,
|
|
159
|
+
// but only over the workspace itself.
|
|
157
160
|
if (action === "notification-action" || action === "notification-dismiss") {
|
|
158
|
-
const
|
|
161
|
+
const toastHandled = action === "notification-action"
|
|
159
162
|
? toastHost.activateNewest?.() === true
|
|
160
163
|
: toastHost.dismissNewest?.() === true;
|
|
164
|
+
const handled = toastHandled || (
|
|
165
|
+
!dialogOpen
|
|
166
|
+
&& !state.commandBarOpen
|
|
167
|
+
&& !state.inputCaptured
|
|
168
|
+
&& !isDetachedWindow
|
|
169
|
+
&& pressStarPromptKey(action === "notification-action" ? "open" : "dismiss")
|
|
170
|
+
);
|
|
161
171
|
if (handled) {
|
|
162
172
|
event.preventDefault();
|
|
163
173
|
event.stopPropagation();
|
|
@@ -22,6 +22,7 @@ export type CoreKeybindingActionId =
|
|
|
22
22
|
| "close-floating-panes"
|
|
23
23
|
| "pane-settings"
|
|
24
24
|
| "pane-fullscreen"
|
|
25
|
+
| "pane-new-layout"
|
|
25
26
|
| "pane-float"
|
|
26
27
|
| "pane-pop-out"
|
|
27
28
|
| "pane-screenshot"
|
|
@@ -52,6 +53,7 @@ export const PANE_ACTION_IDS = new Set<CoreKeybindingActionId>([
|
|
|
52
53
|
"close-floating-panes",
|
|
53
54
|
"pane-settings",
|
|
54
55
|
"pane-fullscreen",
|
|
56
|
+
"pane-new-layout",
|
|
55
57
|
"pane-float",
|
|
56
58
|
"pane-pop-out",
|
|
57
59
|
"pane-screenshot",
|
|
@@ -186,6 +188,14 @@ export const KEYBINDING_ACTIONS: readonly KeybindingActionDef[] = [
|
|
|
186
188
|
description: "Fill the window with the focused pane, or restore it.",
|
|
187
189
|
defaults: ["CmdOrCtrl+Shift+F"],
|
|
188
190
|
},
|
|
191
|
+
{
|
|
192
|
+
id: "pane-new-layout",
|
|
193
|
+
category: "Pane Management",
|
|
194
|
+
description: "Move the focused pane to a new layout of its own, or back to the layout it came from.",
|
|
195
|
+
// N for new, and no app chord had it. Browsers keep Ctrl/Cmd+Shift+N for a
|
|
196
|
+
// private window, so on the web the pane menu and command bar carry it.
|
|
197
|
+
defaults: ["CmdOrCtrl+Shift+N"],
|
|
198
|
+
},
|
|
189
199
|
{
|
|
190
200
|
id: "pane-float",
|
|
191
201
|
category: "Pane Management",
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
isPaneInLayout,
|
|
11
11
|
} from "../../layout/pane-manager";
|
|
12
12
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
13
|
+
import { PluginOffError, notifyPluginOff } from "../../plugins/plugin-off";
|
|
13
14
|
import type { LoadedExternalPlugin } from "../../plugins/loader";
|
|
14
15
|
import {
|
|
15
16
|
getFocusedCollectionId,
|
|
@@ -215,6 +216,12 @@ export function useAppPaneRuntime({
|
|
|
215
216
|
const target = resolvePaneShowTarget(state.config.layout, paneId);
|
|
216
217
|
const paneDef = pluginRegistry.panes.get(target.paneType);
|
|
217
218
|
if (!paneDef) return;
|
|
219
|
+
// Its pane would be added, or focused, hidden.
|
|
220
|
+
const owner = pluginRegistry.getDisabledPaneOwner(target.paneType, stateRef.current.config.disabledPlugins);
|
|
221
|
+
if (owner) {
|
|
222
|
+
notifyPluginOff(pluginRegistry, new PluginOffError(owner, paneDef.name), () => pluginRegistry.showPane(paneId));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
218
225
|
|
|
219
226
|
if (target.paneType === TICKER_RESEARCH_PANE_ID) {
|
|
220
227
|
showTickerResearchPane();
|
|
@@ -241,6 +248,7 @@ export function useAppPaneRuntime({
|
|
|
241
248
|
pluginRegistry,
|
|
242
249
|
showTickerResearchPane,
|
|
243
250
|
state.config.layout,
|
|
251
|
+
stateRef,
|
|
244
252
|
]);
|
|
245
253
|
|
|
246
254
|
const { createPaneFromTemplate } = useAppPaneTemplateRuntime({
|
|
@@ -4,6 +4,7 @@ import { createPaneTemplateOrThrow } from "../../components/command-bar/workflow
|
|
|
4
4
|
import { openFormModal } from "../../components/form-modal";
|
|
5
5
|
import type { AppTickerRepositoryPort } from "../../core/app-service-ports";
|
|
6
6
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
7
|
+
import { PluginOffError, notifyPluginOff } from "../../plugins/plugin-off";
|
|
7
8
|
import type { AppAction, AppState } from "../../state/app/context";
|
|
8
9
|
import type { PaneBinding, PaneInstanceConfig } from "../../types/config";
|
|
9
10
|
import type { DataProvider } from "../../types/data-provider";
|
|
@@ -70,6 +71,10 @@ export function useAppPaneTemplateRuntime({
|
|
|
70
71
|
placePaneInstance,
|
|
71
72
|
});
|
|
72
73
|
} catch (error) {
|
|
74
|
+
if (error instanceof PluginOffError) {
|
|
75
|
+
notifyPluginOff(pluginRegistry, error, () => { void createPaneFromTemplate(templateId, options); });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
73
78
|
notify(
|
|
74
79
|
error instanceof Error ? error.message : `Could not create ${getPaneTemplateDisplayLabel(template).toLowerCase()}.`,
|
|
75
80
|
{ type: "info" },
|
|
@@ -8,6 +8,13 @@ import { openFormModal } from "../../components/form-modal";
|
|
|
8
8
|
import { t } from "../../i18n";
|
|
9
9
|
import { openBrokerAddFlow } from "../../plugins/builtin/broker-manager/add-request";
|
|
10
10
|
import { getPanelFocusTarget } from "../../core/state/app/layout";
|
|
11
|
+
import { getVisiblePaneCycleOrder } from "../../components/layout/pane/cycle-order";
|
|
12
|
+
import {
|
|
13
|
+
collectDisabledPaneIds,
|
|
14
|
+
resolveShellVisibleLayout,
|
|
15
|
+
restoreShellHiddenPanes,
|
|
16
|
+
} from "../../components/layout/shell/visible-layout";
|
|
17
|
+
import { applyPluginToggles } from "../../plugins/ownership";
|
|
11
18
|
import { setLayoutManagerDispatch } from "../../plugins/builtin/layout-manager";
|
|
12
19
|
import { setMarketplaceHost } from "../../plugins/builtin/plugin-marketplace/store";
|
|
13
20
|
import type { InstalledPlugin } from "../../plugins/builtin/plugin-marketplace/model";
|
|
@@ -25,6 +32,7 @@ import {
|
|
|
25
32
|
removePane,
|
|
26
33
|
} from "../../layout/pane-manager";
|
|
27
34
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
35
|
+
import { PluginOffError, notifyPluginOff } from "../../plugins/plugin-off";
|
|
28
36
|
import { reportCrash } from "../../telemetry/crash-reports";
|
|
29
37
|
import { recordFunctionOpen, usageFunctionForPane } from "../../telemetry/usage-counts";
|
|
30
38
|
import { captureAttentionAction } from "../../telemetry/attention-counts";
|
|
@@ -33,15 +41,16 @@ import {
|
|
|
33
41
|
resolveTickerNavigationReplacementPane,
|
|
34
42
|
shouldFocusTickerNavigationTarget,
|
|
35
43
|
} from "../../layout/ticker-navigation";
|
|
36
|
-
import
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
import {
|
|
45
|
+
resolveTickerForPane,
|
|
46
|
+
type AppAction,
|
|
47
|
+
type AppState,
|
|
39
48
|
} from "../../state/app/context";
|
|
40
49
|
import type {
|
|
41
50
|
LayoutConfig,
|
|
42
51
|
PaneInstanceConfig,
|
|
43
52
|
} from "../../types/config";
|
|
44
|
-
import { TICKER_RESEARCH_PANE_ID } from "../../types/config";
|
|
53
|
+
import { findPaneInstance, TICKER_RESEARCH_PANE_ID } from "../../types/config";
|
|
45
54
|
import type { DataProvider } from "../../types/data-provider";
|
|
46
55
|
import type {
|
|
47
56
|
PaneDef,
|
|
@@ -129,7 +138,33 @@ export function bindAppPanePluginRegistry({
|
|
|
129
138
|
switchTickerResearchTab,
|
|
130
139
|
tickerRepository,
|
|
131
140
|
}: BindAppPanePluginRegistryOptions): void {
|
|
141
|
+
// Switching plugins never edits the layout: the shell hides a switched-off
|
|
142
|
+
// plugin's panes where they are and shows them again when it comes back.
|
|
143
|
+
// Only focus moves, off a pane that just went hidden.
|
|
144
|
+
const setPluginsEnabled = (changes: Readonly<Record<string, boolean>>) => {
|
|
145
|
+
const current = stateRef.current;
|
|
146
|
+
// Setting a state, not flipping it, so a second "Turn on" from an older
|
|
147
|
+
// toast cannot switch the plugin back off.
|
|
148
|
+
const disabledPlugins = applyPluginToggles(current.config.disabledPlugins ?? [], changes);
|
|
149
|
+
if (!disabledPlugins) return;
|
|
150
|
+
const focused = current.focusedPaneId ? findPaneInstance(current.config.layout, current.focusedPaneId) : undefined;
|
|
151
|
+
const focusHidden = !!focused && collectDisabledPaneIds(pluginRegistry, disabledPlugins).has(focused.paneId);
|
|
152
|
+
dispatch(focusHidden
|
|
153
|
+
? {
|
|
154
|
+
type: "SET_DISABLED_PLUGINS",
|
|
155
|
+
disabledPlugins,
|
|
156
|
+
focusedPaneId: getVisiblePaneCycleOrder(current.config.layout, pluginRegistry, disabledPlugins)[0] ?? null,
|
|
157
|
+
}
|
|
158
|
+
: { type: "SET_DISABLED_PLUGINS", disabledPlugins });
|
|
159
|
+
const nextConfig = { ...current.config, disabledPlugins };
|
|
160
|
+
persistConfig(nextConfig);
|
|
161
|
+
pluginRegistry.events.emit("config:changed", { config: nextConfig });
|
|
162
|
+
};
|
|
163
|
+
const setPluginEnabled = (pluginId: string, enabled: boolean) => setPluginsEnabled({ [pluginId]: enabled });
|
|
164
|
+
|
|
132
165
|
pluginRegistry.bindHost({
|
|
166
|
+
setPluginEnabled,
|
|
167
|
+
setPluginsEnabled,
|
|
133
168
|
selectTicker: (symbol, paneId) => selectTickerInPane(symbol, paneId),
|
|
134
169
|
switchPanel: (panel) => {
|
|
135
170
|
if (isDetachedWindow) return;
|
|
@@ -196,10 +231,9 @@ export function bindAppPanePluginRegistry({
|
|
|
196
231
|
const sharedPane = materialized.layout.instances[0];
|
|
197
232
|
if (!sharedPane) throw new Error("This shared pane is invalid.");
|
|
198
233
|
const paneDef = pluginRegistry.panes.get(sharedPane.paneId);
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
234
|
+
if (!paneDef) throw new Error("This shared pane is unavailable in this version of Gloomberb.");
|
|
235
|
+
const owner = pluginRegistry.getDisabledPaneOwner(sharedPane.paneId, stateRef.current.config.disabledPlugins);
|
|
236
|
+
if (owner) throw new PluginOffError(owner, "this shared pane");
|
|
203
237
|
const instance = buildPaneInstance(sharedPane.paneId, sharedPane);
|
|
204
238
|
if (!instance) throw new Error("This shared pane could not be created.");
|
|
205
239
|
dispatch({
|
|
@@ -242,6 +276,15 @@ export function bindAppPanePluginRegistry({
|
|
|
242
276
|
showPane(paneId);
|
|
243
277
|
return;
|
|
244
278
|
}
|
|
279
|
+
// In the layout but hidden with its plugin, as the research pane that
|
|
280
|
+
// follows a list is while Ticker Research is off.
|
|
281
|
+
const paneType = findPaneInstance(stateRef.current.config.layout, instanceId)?.paneId;
|
|
282
|
+
const owner = paneType ? pluginRegistry.getDisabledPaneOwner(paneType, stateRef.current.config.disabledPlugins) : null;
|
|
283
|
+
if (owner) {
|
|
284
|
+
const what = resolveTickerForPane(stateRef.current, instanceId) ?? pluginRegistry.panes.get(paneType!)?.name ?? paneType!;
|
|
285
|
+
notifyPluginOff(pluginRegistry, new PluginOffError(owner, what), () => pluginRegistry.focusPane(paneId, layout));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
245
288
|
|
|
246
289
|
focusVisiblePane(instanceId, layout);
|
|
247
290
|
},
|
|
@@ -254,6 +297,12 @@ export function bindAppPanePluginRegistry({
|
|
|
254
297
|
},
|
|
255
298
|
navigateTicker: (rawSymbol, options) => {
|
|
256
299
|
if (isDetachedWindow) return;
|
|
300
|
+
// Navigation lands in a research pane, which stays hidden while its plugin is off.
|
|
301
|
+
const researchOwner = pluginRegistry.getDisabledPaneOwner(TICKER_RESEARCH_PANE_ID, stateRef.current.config.disabledPlugins);
|
|
302
|
+
if (researchOwner) {
|
|
303
|
+
notifyPluginOff(pluginRegistry, new PluginOffError(researchOwner, rawSymbol), () => pluginRegistry.navigateTicker(rawSymbol, options));
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
257
306
|
const recordAttention = captureAttentionAction();
|
|
258
307
|
recordFunctionOpen(usageFunctionForPane(pluginRegistry, TICKER_RESEARCH_PANE_ID));
|
|
259
308
|
const sourcePaneId = options?.sourcePaneId ?? stateRef.current.focusedPaneId;
|
|
@@ -334,12 +383,15 @@ export function bindAppPanePluginRegistry({
|
|
|
334
383
|
},
|
|
335
384
|
});
|
|
336
385
|
|
|
386
|
+
// Layout commands (Tidy, Swap, Float, Close) act on what is on screen, as
|
|
387
|
+
// the shell's own menus do, and leave a switched-off plugin's panes alone.
|
|
388
|
+
const disabledPaneIds = () => collectDisabledPaneIds(pluginRegistry, stateRef.current.config.disabledPlugins);
|
|
337
389
|
setLayoutManagerDispatch(dispatch, () => ({
|
|
338
|
-
layout: state.config.layout,
|
|
390
|
+
layout: resolveShellVisibleLayout(state.config.layout, disabledPaneIds(), pluginRegistry.panes),
|
|
339
391
|
termWidth: pluginRegistry.getTermSize().width,
|
|
340
392
|
termHeight: pluginRegistry.getTermSize().height,
|
|
341
393
|
focusedPaneId: state.focusedPaneId,
|
|
342
|
-
}));
|
|
394
|
+
}), (edited) => restoreShellHiddenPanes(stateRef.current.config.layout, edited, disabledPaneIds(), pluginRegistry.panes));
|
|
343
395
|
|
|
344
396
|
setExternalPlugins(externalPlugins);
|
|
345
397
|
|
|
@@ -457,7 +509,7 @@ export function bindAppPanePluginRegistry({
|
|
|
457
509
|
});
|
|
458
510
|
const commands = [...pluginRegistry.commands.entries()]
|
|
459
511
|
.filter(([id]) => pluginRegistry.getCommandPluginId(id) === pluginId && !id.endsWith(":setup"))
|
|
460
|
-
.map(([id, command]) => ({ id, label: command.label }));
|
|
512
|
+
.map(([id, command]) => ({ id, label: command.label, ...(command.shortcut ? { shortcut: command.shortcut } : {}) }));
|
|
461
513
|
const capabilities = pluginRegistry.capabilities.manifests()
|
|
462
514
|
.filter((manifest) => pluginRegistry.getCapabilityPluginId(manifest.id) === pluginId).length;
|
|
463
515
|
const broker = [...pluginRegistry.brokers.keys()].some((type) => pluginRegistry.getBrokerPluginId(type) === pluginId);
|
|
@@ -466,19 +518,7 @@ export function bindAppPanePluginRegistry({
|
|
|
466
518
|
notify: (notification) => {
|
|
467
519
|
pluginRegistry.notify(notification);
|
|
468
520
|
},
|
|
469
|
-
setPluginEnabled
|
|
470
|
-
|
|
471
|
-
if (!enabled) {
|
|
472
|
-
for (const paneId of pluginRegistry.getPluginPaneIds(pluginId)) pluginRegistry.hidePane(paneId);
|
|
473
|
-
}
|
|
474
|
-
dispatch({ type: "TOGGLE_PLUGIN", pluginId });
|
|
475
|
-
const disabled = current.disabledPlugins ?? [];
|
|
476
|
-
persistConfig({
|
|
477
|
-
...current,
|
|
478
|
-
disabledPlugins: enabled
|
|
479
|
-
? disabled.filter((entry) => entry !== pluginId)
|
|
480
|
-
: [...disabled, pluginId],
|
|
481
|
-
});
|
|
482
|
-
},
|
|
521
|
+
setPluginEnabled,
|
|
522
|
+
setPluginsEnabled,
|
|
483
523
|
});
|
|
484
524
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
findDockLeaf,
|
|
6
6
|
} from "../../layout/pane-manager";
|
|
7
7
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
8
|
+
import { PluginOffError, notifyPluginOff } from "../../plugins/plugin-off";
|
|
8
9
|
import {
|
|
9
10
|
findTickerResearchFollower,
|
|
10
11
|
listVisibleTickerSourcePanes,
|
|
@@ -110,6 +111,15 @@ export function useAppTickerInspectorRuntime({
|
|
|
110
111
|
}, [pluginRegistry, resolveInspectorPane, state.config.layout]);
|
|
111
112
|
|
|
112
113
|
const switchTickerResearchTab = useCallback((tabId: string, preferredPaneId?: string | null) => {
|
|
114
|
+
// The tab lives in a research pane, which stays hidden while its plugin is
|
|
115
|
+
// off, and the tab itself is hidden while its own plugin is.
|
|
116
|
+
const owner = pluginRegistry.getDisabledPaneOwner(TICKER_RESEARCH_PANE_ID, state.config.disabledPlugins)
|
|
117
|
+
?? pluginRegistry.getDisabledTickerResearchTabOwner(tabId, state.config.disabledPlugins);
|
|
118
|
+
if (owner) {
|
|
119
|
+
const tabName = pluginRegistry.tickerResearchTabs.get(tabId)?.name ?? "this tab";
|
|
120
|
+
notifyPluginOff(pluginRegistry, new PluginOffError(owner, tabName), () => pluginRegistry.switchTab(tabId, preferredPaneId ?? undefined));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
113
123
|
const targetPaneId = (() => {
|
|
114
124
|
const target = preferredPaneId ? resolvePaneInstance(state.config.layout, preferredPaneId) : null;
|
|
115
125
|
if (target?.paneId === TICKER_RESEARCH_PANE_ID) return target.instanceId;
|
|
@@ -131,7 +141,9 @@ export function useAppTickerInspectorRuntime({
|
|
|
131
141
|
dispatch,
|
|
132
142
|
ensureInspectorPane,
|
|
133
143
|
persistLayout,
|
|
144
|
+
pluginRegistry,
|
|
134
145
|
resolveTickerSourcePaneId,
|
|
146
|
+
state.config.disabledPlugins,
|
|
135
147
|
state.config.layout,
|
|
136
148
|
state.focusedPaneId,
|
|
137
149
|
]);
|