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
package/src/plugins/ownership.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Built-in module ids and the
|
|
3
|
-
* does two jobs: config, sync and session state saved under it is
|
|
4
|
-
*
|
|
5
|
-
* have its state merged
|
|
6
|
-
* to an external plugin leaves this map so that plugin can claim its id.
|
|
2
|
+
* Built-in module ids and the state namespace each one's state lives in now.
|
|
3
|
+
* Every key does two jobs: config, sync and session state saved under it is
|
|
4
|
+
* rewritten to that namespace, and it is reserved, because an external plugin
|
|
5
|
+
* using it would have its state merged in the same way. A module that moves
|
|
6
|
+
* out to an external plugin leaves this map so that plugin can claim its id.
|
|
7
|
+
*
|
|
8
|
+
* A switched-off module id in `disabledPlugins` follows this map too, unless
|
|
9
|
+
* BUILTIN_DISABLED_PLUGIN_ALIASES sends it somewhere else.
|
|
7
10
|
*/
|
|
8
11
|
const BUILTIN_PLUGIN_OWNER_ALIASES: Record<string, string> = {
|
|
9
12
|
analytics: "portfolio",
|
|
@@ -42,6 +45,90 @@ const BUILTIN_PLUGIN_OWNER_ALIASES: Record<string, string> = {
|
|
|
42
45
|
"world-indices": "market-overview",
|
|
43
46
|
};
|
|
44
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Retired ids in `disabledPlugins` whose plugin is not the namespace their
|
|
50
|
+
* state moved to: once a plugin is split, a module's state stays where it was
|
|
51
|
+
* while its switch belongs to the successor that holds the module now.
|
|
52
|
+
*/
|
|
53
|
+
const BUILTIN_DISABLED_PLUGIN_ALIASES: Record<string, string> = {
|
|
54
|
+
"chart-composer": "ticker-core",
|
|
55
|
+
"company-research": "ticker-core",
|
|
56
|
+
"comparison-chart": "ticker-core",
|
|
57
|
+
correlation: "quant",
|
|
58
|
+
"crypto-board": "crypto",
|
|
59
|
+
"dividend-yield": "ticker-core",
|
|
60
|
+
"earnings-calendar": "earnings",
|
|
61
|
+
"earnings-calls": "earnings",
|
|
62
|
+
executives: "ticker-core",
|
|
63
|
+
"filing-events": "filings",
|
|
64
|
+
"fx-matrix": "global-markets",
|
|
65
|
+
holders: "ownership",
|
|
66
|
+
insider: "ownership",
|
|
67
|
+
jobs: "alt-data",
|
|
68
|
+
"market-movers": "screeners",
|
|
69
|
+
options: "options-volatility",
|
|
70
|
+
research: "ticker-core",
|
|
71
|
+
"risk-factors": "filings",
|
|
72
|
+
sec: "filings",
|
|
73
|
+
sectors: "global-markets",
|
|
74
|
+
"short-interest": "ownership",
|
|
75
|
+
"short-volume": "ownership",
|
|
76
|
+
"social-mentions": "alt-data",
|
|
77
|
+
thirteenf: "ownership",
|
|
78
|
+
"ticker-detail": "ticker-core",
|
|
79
|
+
"world-indices": "global-markets",
|
|
80
|
+
// `macro-tv` keeps meaning all of Macro: TV left for its own repository, so
|
|
81
|
+
// no successor holds it, and turning it off was turning Macro off.
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Retired built-in plugin ids that now stand for a group of built-ins in
|
|
86
|
+
* `disabledPlugins`, reserved for good. Turning the old plugin off, here or in
|
|
87
|
+
* an older app through sync, turns off every successor holding one of its
|
|
88
|
+
* modules; the old id is written back only while all of them are off, so an
|
|
89
|
+
* older app shows it off exactly then, and turning it back on there brings
|
|
90
|
+
* them all back.
|
|
91
|
+
*/
|
|
92
|
+
const BUILTIN_PLUGIN_GROUPS: Readonly<Record<string, readonly string[]>> = {
|
|
93
|
+
macro: ["rates-macro", "credit", "earnings"],
|
|
94
|
+
"market-overview": ["global-markets", "screeners", "futures-commodities", "crypto", "alt-data", "quant"],
|
|
95
|
+
// Shares Credit & Bonds and Earnings with Macro, and Alt Data and Quant with
|
|
96
|
+
// Market Overview: each holds modules from both.
|
|
97
|
+
"ticker-research": ["ticker-core", "options-volatility", "ownership", "filings", "alt-data", "quant", "credit", "earnings"],
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/** Every built-in that came out of a retired group: the plugins starter packs switch. */
|
|
101
|
+
export function regroupedBuiltinPluginIds(): string[] {
|
|
102
|
+
return [...new Set(Object.values(BUILTIN_PLUGIN_GROUPS).flat())];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The built-ins a retired group id stands for, or null when the id is not a group. */
|
|
106
|
+
export function builtinPluginGroupMembers(pluginId: string): readonly string[] | null {
|
|
107
|
+
return Object.prototype.hasOwnProperty.call(BUILTIN_PLUGIN_GROUPS, pluginId) ? BUILTIN_PLUGIN_GROUPS[pluginId]! : null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* `disabledPlugins` after switching plugins on or off in one go, or null when
|
|
112
|
+
* nothing changes. A retired group id switches every member, the way
|
|
113
|
+
* `gloomberb plugin enable|disable` does. Changes apply in order, so a later
|
|
114
|
+
* one wins for a plugin two groups share.
|
|
115
|
+
*/
|
|
116
|
+
export function applyPluginToggles(
|
|
117
|
+
disabledPlugins: readonly string[],
|
|
118
|
+
changes: Readonly<Record<string, boolean>>,
|
|
119
|
+
): string[] | null {
|
|
120
|
+
let next = [...disabledPlugins];
|
|
121
|
+
for (const [pluginId, enabled] of Object.entries(changes)) {
|
|
122
|
+
for (const memberId of builtinPluginGroupMembers(pluginId) ?? [pluginId]) {
|
|
123
|
+
if (enabled) next = next.filter((entry) => entry !== memberId);
|
|
124
|
+
else if (!next.includes(memberId)) next.push(memberId);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const unchanged = next.length === disabledPlugins.length
|
|
128
|
+
&& next.every((pluginId, index) => pluginId === disabledPlugins[index]);
|
|
129
|
+
return unchanged ? null : next;
|
|
130
|
+
}
|
|
131
|
+
|
|
45
132
|
/** The one built-in that cannot be disabled; its legacy module ids normalize to it. */
|
|
46
133
|
const NON_TOGGLEABLE_BUILTIN_PLUGIN_ID = "application";
|
|
47
134
|
|
|
@@ -54,18 +141,74 @@ function normalizeBuiltinPluginOwnerId(pluginId: string): string {
|
|
|
54
141
|
return BUILTIN_PLUGIN_OWNER_ALIASES[pluginId] ?? pluginId;
|
|
55
142
|
}
|
|
56
143
|
|
|
144
|
+
/** Every retired built-in module id, for checks that each still lands on the right plugin. */
|
|
145
|
+
export function retiredBuiltinModuleIds(): string[] {
|
|
146
|
+
return Object.keys(BUILTIN_PLUGIN_OWNER_ALIASES);
|
|
147
|
+
}
|
|
148
|
+
|
|
57
149
|
export function isReservedBuiltinPluginId(pluginId: string): boolean {
|
|
58
|
-
return Object.prototype.hasOwnProperty.call(BUILTIN_PLUGIN_OWNER_ALIASES, pluginId)
|
|
150
|
+
return Object.prototype.hasOwnProperty.call(BUILTIN_PLUGIN_OWNER_ALIASES, pluginId)
|
|
151
|
+
|| builtinPluginGroupMembers(pluginId) !== null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function normalizeBuiltinDisabledPluginId(pluginId: string): string {
|
|
155
|
+
return BUILTIN_DISABLED_PLUGIN_ALIASES[pluginId] ?? normalizeBuiltinPluginOwnerId(pluginId);
|
|
59
156
|
}
|
|
60
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Rewrites retired module ids to the plugin that holds the module now. Group
|
|
160
|
+
* ids are left as they are: the configuration migrations after this one read
|
|
161
|
+
* them before `expandBuiltinPluginGroups` runs.
|
|
162
|
+
*/
|
|
61
163
|
export function normalizeBuiltinDisabledPluginIds(pluginIds: readonly string[]): string[] {
|
|
62
164
|
return [...new Set(
|
|
63
165
|
pluginIds
|
|
64
|
-
.map(
|
|
166
|
+
.map(normalizeBuiltinDisabledPluginId)
|
|
65
167
|
.filter((pluginId) => pluginId !== NON_TOGGLEABLE_BUILTIN_PLUGIN_ID),
|
|
66
168
|
)];
|
|
67
169
|
}
|
|
68
170
|
|
|
171
|
+
/**
|
|
172
|
+
* `disabledPlugins` as saved, in the ids the registry knows: a retired group
|
|
173
|
+
* id becomes every member. Applied on every load, after the migrations; it
|
|
174
|
+
* only ever adds switched-off plugins, and running it twice changes nothing.
|
|
175
|
+
*/
|
|
176
|
+
export function expandBuiltinPluginGroups(pluginIds: readonly string[]): string[] {
|
|
177
|
+
return [...new Set(pluginIds.flatMap((pluginId) => builtinPluginGroupMembers(pluginId) ?? [pluginId]))];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* `disabledPlugins` as pulled. Another device may run an app from before
|
|
182
|
+
* modules were merged or plugins split, so retired module ids are rewritten
|
|
183
|
+
* as the migrations would, then groups expanded.
|
|
184
|
+
*/
|
|
185
|
+
export function decodeBuiltinDisabledPluginIds(pluginIds: readonly string[]): string[] {
|
|
186
|
+
return expandBuiltinPluginGroups(normalizeBuiltinDisabledPluginIds(pluginIds));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* `disabledPlugins` as written to disk and pushed: a group whose members are
|
|
191
|
+
* all off is written as its retired id alone, where its first member stood,
|
|
192
|
+
* and a group partly off as the members that are. Expanding the result gives
|
|
193
|
+
* the list back.
|
|
194
|
+
*/
|
|
195
|
+
export function encodeBuiltinDisabledPluginIds(pluginIds: readonly string[]): string[] {
|
|
196
|
+
const disabled = new Set(pluginIds);
|
|
197
|
+
const fullyOff = Object.entries(BUILTIN_PLUGIN_GROUPS)
|
|
198
|
+
.filter(([, members]) => members.length > 0 && members.every((member) => disabled.has(member)));
|
|
199
|
+
if (fullyOff.length === 0) return [...disabled];
|
|
200
|
+
const groupAt = new Map<string, string[]>();
|
|
201
|
+
for (const [groupId, members] of fullyOff) {
|
|
202
|
+
const first = pluginIds.find((pluginId) => members.includes(pluginId))!;
|
|
203
|
+
groupAt.set(first, [...(groupAt.get(first) ?? []), groupId]);
|
|
204
|
+
}
|
|
205
|
+
const covered = new Set(fullyOff.flatMap(([, members]) => members));
|
|
206
|
+
return [...new Set(pluginIds.flatMap((pluginId) => [
|
|
207
|
+
...(groupAt.get(pluginId) ?? []),
|
|
208
|
+
...(covered.has(pluginId) ? [] : [pluginId]),
|
|
209
|
+
]))];
|
|
210
|
+
}
|
|
211
|
+
|
|
69
212
|
export function normalizeBuiltinPluginStateMap(
|
|
70
213
|
value: Record<string, Record<string, unknown>>,
|
|
71
214
|
): Record<string, Record<string, unknown>> {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { FUNCTION_HELP, type FunctionAccess } from "../cli/pane-functions/function-help";
|
|
2
|
+
import { commands as coreCommands, getCommandPrefixes } from "../components/command-bar/commands/registry";
|
|
3
|
+
import { BUILTIN_EDITORIAL } from "./builtin-editorial";
|
|
4
|
+
|
|
5
|
+
/** A command-bar function a plugin adds, as the Plugins pane and gloom.sh list it. */
|
|
6
|
+
export interface PluginFunction {
|
|
7
|
+
code: string;
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
access?: FunctionAccess;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface FunctionSources {
|
|
14
|
+
templates: ReadonlyArray<{ label: string; prefix?: string; description?: string }>;
|
|
15
|
+
commands?: ReadonlyArray<{ label: string; shortcut?: string; description?: string }>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** The core command an alias belongs to: the research pane's T is DES. */
|
|
19
|
+
const CORE_PRIMARY = new Map(coreCommands.flatMap((command) => {
|
|
20
|
+
const [primary, ...aliases] = getCommandPrefixes(command);
|
|
21
|
+
return primary ? aliases.map((alias) => [alias, primary] as const) : [];
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The functions a plugin answers to, once each, in the order it registered
|
|
26
|
+
* them: its pane templates, then its commands. A code that is an alias of a
|
|
27
|
+
* core command is listed under the code people type.
|
|
28
|
+
*/
|
|
29
|
+
export function pluginFunctions({ templates, commands = [] }: FunctionSources): PluginFunction[] {
|
|
30
|
+
const byCode = new Map<string, PluginFunction>();
|
|
31
|
+
const add = (rawCode: string | undefined, name: string, description = "") => {
|
|
32
|
+
const typed = rawCode?.trim().toUpperCase();
|
|
33
|
+
if (!typed) return;
|
|
34
|
+
const code = CORE_PRIMARY.get(typed) ?? typed;
|
|
35
|
+
if (byCode.has(code)) return;
|
|
36
|
+
const access = FUNCTION_HELP[code]?.access;
|
|
37
|
+
byCode.set(code, { code, name, description, ...(access ? { access } : {}) });
|
|
38
|
+
};
|
|
39
|
+
for (const template of templates) add(template.prefix, template.label, template.description);
|
|
40
|
+
for (const command of commands) add(command.shortcut, command.label, command.description);
|
|
41
|
+
return [...byCode.values()];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Functions Free cannot fully open: Pro only, or a preview on Free. */
|
|
45
|
+
export function proFunctionCount(functions: readonly PluginFunction[]): number {
|
|
46
|
+
return functions.filter((fn) => fn.access).length;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The codes a row shows: the editorial pick when it names real codes, else the first three. */
|
|
50
|
+
export function highlightedCodes(pluginId: string, functions: readonly PluginFunction[]): string[] {
|
|
51
|
+
const codes = functions.map((fn) => fn.code);
|
|
52
|
+
const picked = (BUILTIN_EDITORIAL[pluginId]?.highlights ?? []).filter((code) => codes.includes(code));
|
|
53
|
+
return (picked.length > 0 ? picked : codes).slice(0, 3);
|
|
54
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AppNotificationRequest } from "../types/plugin";
|
|
2
|
+
import type { DisabledPluginOwner } from "./registry";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Opening something whose plugin is switched off. A pane added for it would
|
|
6
|
+
* stay hidden, so the open is refused and the user is offered the switch.
|
|
7
|
+
*/
|
|
8
|
+
export class PluginOffError extends Error {
|
|
9
|
+
constructor(readonly plugin: DisabledPluginOwner, what: string) {
|
|
10
|
+
super(`Turn on ${plugin.name} to open ${what}.`);
|
|
11
|
+
this.name = "PluginOffError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface PluginOffNotifier {
|
|
16
|
+
notify(notification: AppNotificationRequest): unknown;
|
|
17
|
+
setPluginEnabled(pluginId: string, enabled: boolean): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Says which plugin to turn on, with a button that turns it on and, when
|
|
22
|
+
* given, opens what was asked for.
|
|
23
|
+
*/
|
|
24
|
+
export function notifyPluginOff(host: PluginOffNotifier, error: PluginOffError, reopen?: () => void): void {
|
|
25
|
+
host.notify({
|
|
26
|
+
body: error.message,
|
|
27
|
+
type: "info",
|
|
28
|
+
action: {
|
|
29
|
+
label: "Turn on",
|
|
30
|
+
onClick: () => {
|
|
31
|
+
host.setPluginEnabled(error.plugin.id, true);
|
|
32
|
+
reopen?.();
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -45,6 +45,10 @@ export interface PluginHostActions {
|
|
|
45
45
|
openWindowMode(paneId?: string, mode?: WindowEditMode): void;
|
|
46
46
|
/** The shell's fullscreen toggle for a pane; false when there is nothing to fill the window with. */
|
|
47
47
|
togglePaneFullscreen(paneId: string): boolean;
|
|
48
|
+
/** The shell's Move to New Layout for a pane; false when it did nothing. Not on the plugin API. */
|
|
49
|
+
movePaneToNewLayout(paneId: string): boolean;
|
|
50
|
+
/** Returns a pane Move to New Layout moved to where it came from; false when it did nothing. Not on the plugin API. */
|
|
51
|
+
movePaneBack(paneId: string): boolean;
|
|
48
52
|
showPane(paneId: string): void;
|
|
49
53
|
createPaneFromTemplate(templateId: string, options?: PaneTemplateCreateOptions): void;
|
|
50
54
|
createPaneFromTemplateAsync(templateId: string, options?: PaneTemplateCreateOptions): Promise<void>;
|
|
@@ -62,6 +66,14 @@ export interface PluginHostActions {
|
|
|
62
66
|
watchNewsQuery(query: NewsQuery, listener: (state: NewsQueryState) => void): () => void;
|
|
63
67
|
|
|
64
68
|
notify(notification: AppNotificationRequest): AppNotificationDelivery | void;
|
|
69
|
+
/** Switches a plugin on or off the way the Plugins pane does. Not on the plugin API. */
|
|
70
|
+
setPluginEnabled(pluginId: string, enabled: boolean): void;
|
|
71
|
+
/**
|
|
72
|
+
* Switches several plugins on or off as one change: one config write and
|
|
73
|
+
* one save. A switched-off plugin's panes are hidden, never removed, so
|
|
74
|
+
* switching it back on shows them where they were. Not on the plugin API.
|
|
75
|
+
*/
|
|
76
|
+
setPluginsEnabled(changes: Readonly<Record<string, boolean>>): void;
|
|
65
77
|
getPaneRuntimeState(paneId: string): PaneRuntimeState | null;
|
|
66
78
|
updatePaneRuntimeState(paneId: string, patch: Partial<PaneRuntimeState>): void;
|
|
67
79
|
applyPaneSettingValue(paneId: string, field: PaneSettingField, value: unknown): Promise<void>;
|
|
@@ -95,6 +107,8 @@ export function createDefaultHostActions(current?: () => Pick<PluginHostActions,
|
|
|
95
107
|
sharePane: () => {},
|
|
96
108
|
openWindowMode: () => {},
|
|
97
109
|
togglePaneFullscreen: () => false,
|
|
110
|
+
movePaneToNewLayout: () => false,
|
|
111
|
+
movePaneBack: () => false,
|
|
98
112
|
showPane: () => {},
|
|
99
113
|
createPaneFromTemplate: () => {},
|
|
100
114
|
createPaneFromTemplateAsync: async () => {},
|
|
@@ -109,6 +123,8 @@ export function createDefaultHostActions(current?: () => Pick<PluginHostActions,
|
|
|
109
123
|
registerNewsCapability: () => () => {},
|
|
110
124
|
watchNewsQuery: () => () => {},
|
|
111
125
|
notify: () => {},
|
|
126
|
+
setPluginEnabled: () => {},
|
|
127
|
+
setPluginsEnabled: () => {},
|
|
112
128
|
getPaneRuntimeState: () => null,
|
|
113
129
|
updatePaneRuntimeState: () => {},
|
|
114
130
|
applyPaneSettingValue: async () => {},
|
|
@@ -54,7 +54,13 @@ import {
|
|
|
54
54
|
type ResolvedRegistryPaneQuickSetting,
|
|
55
55
|
type ResolvedRegistryPaneSettings,
|
|
56
56
|
} from "./pane-settings";
|
|
57
|
-
import {
|
|
57
|
+
import {
|
|
58
|
+
RegistryResumeStateListeners,
|
|
59
|
+
bindPluginContextNamespaces,
|
|
60
|
+
createPluginPaneSettingsState,
|
|
61
|
+
createPluginResumeState,
|
|
62
|
+
type NamespacedPluginContext,
|
|
63
|
+
} from "./plugin-state";
|
|
58
64
|
import { createPluginSetupCommand, isPluginConfigured } from "./setup-command";
|
|
59
65
|
import {
|
|
60
66
|
bindSharedRegistry,
|
|
@@ -76,6 +82,12 @@ interface PluginRegistryOptions {
|
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
export type { WindowEditMode } from "./host-actions";
|
|
85
|
+
|
|
86
|
+
/** A switched-off plugin, named for a "Turn on" offer. */
|
|
87
|
+
export interface DisabledPluginOwner {
|
|
88
|
+
id: string;
|
|
89
|
+
name: string;
|
|
90
|
+
}
|
|
79
91
|
export {
|
|
80
92
|
getSharedMarketData,
|
|
81
93
|
getSharedRegistry,
|
|
@@ -167,6 +179,7 @@ export class PluginRegistry implements PluginRuntimeAccess {
|
|
|
167
179
|
bindSharedRegistry(this, marketData);
|
|
168
180
|
this.capabilities = new CapabilityRegistry({
|
|
169
181
|
isPluginEnabled: (pluginId) => !this.getConfig().disabledPlugins.includes(pluginId),
|
|
182
|
+
pluginName: (pluginId) => this.plugins.get(pluginId)?.name,
|
|
170
183
|
isCapabilityEnabled: (capability, pluginId) => {
|
|
171
184
|
const disabledSources = this.getConfig().disabledSources ?? [];
|
|
172
185
|
return !disabledSources.includes(capability.sourceId ?? capability.id) && !this.getConfig().disabledPlugins.includes(pluginId);
|
|
@@ -407,6 +420,41 @@ export class PluginRegistry implements PluginRuntimeAccess {
|
|
|
407
420
|
return this.contributions.paneTemplatesMap.owners.get(templateId);
|
|
408
421
|
}
|
|
409
422
|
|
|
423
|
+
/**
|
|
424
|
+
* The plugin that owns this pane type when it is switched off. A pane of a
|
|
425
|
+
* switched-off plugin stays hidden, so callers offer to turn it on instead
|
|
426
|
+
* of adding one.
|
|
427
|
+
*/
|
|
428
|
+
getDisabledPaneOwner(
|
|
429
|
+
paneType: string,
|
|
430
|
+
disabledPlugins: readonly string[] = this.getConfig().disabledPlugins,
|
|
431
|
+
): DisabledPluginOwner | null {
|
|
432
|
+
return this.disabledOwner(this.getPanePluginId(paneType), disabledPlugins);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/** The same for a template: its own plugin, or the one owning the pane it opens. */
|
|
436
|
+
getDisabledPaneTemplateOwner(
|
|
437
|
+
templateId: string,
|
|
438
|
+
disabledPlugins: readonly string[] = this.getConfig().disabledPlugins,
|
|
439
|
+
): DisabledPluginOwner | null {
|
|
440
|
+
const paneType = this.paneTemplates.get(templateId)?.paneId;
|
|
441
|
+
return this.disabledOwner(this.getPaneTemplatePluginId(templateId), disabledPlugins)
|
|
442
|
+
?? (paneType ? this.getDisabledPaneOwner(paneType, disabledPlugins) : null);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/** The plugin behind a research tab when it is switched off, which hides the tab. */
|
|
446
|
+
getDisabledTickerResearchTabOwner(
|
|
447
|
+
tabId: string,
|
|
448
|
+
disabledPlugins: readonly string[] = this.getConfig().disabledPlugins,
|
|
449
|
+
): DisabledPluginOwner | null {
|
|
450
|
+
return this.disabledOwner(this.getTickerResearchTabPluginId(tabId), disabledPlugins);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private disabledOwner(pluginId: string | undefined, disabledPlugins: readonly string[]): DisabledPluginOwner | null {
|
|
454
|
+
if (!pluginId || !disabledPlugins.includes(pluginId)) return null;
|
|
455
|
+
return { id: pluginId, name: this.plugins.get(pluginId)?.name ?? pluginId };
|
|
456
|
+
}
|
|
457
|
+
|
|
410
458
|
getBrokerPluginId(brokerType: string): string | undefined {
|
|
411
459
|
return this.contributions.brokersMap.owners.get(brokerType);
|
|
412
460
|
}
|
|
@@ -429,10 +477,42 @@ export class PluginRegistry implements PluginRuntimeAccess {
|
|
|
429
477
|
}
|
|
430
478
|
}
|
|
431
479
|
|
|
480
|
+
/**
|
|
481
|
+
* The state parts of a plugin's context. `namespaceKey` is the plugin's id,
|
|
482
|
+
* or the namespace a composed module keeps; both resolve the way a render
|
|
483
|
+
* context's id does.
|
|
484
|
+
*/
|
|
485
|
+
private createNamespacedContext(namespaceKey: string): NamespacedPluginContext {
|
|
486
|
+
const stateId = this.stateNamespace(namespaceKey);
|
|
487
|
+
return {
|
|
488
|
+
persistence: createPluginPersistence(
|
|
489
|
+
this.persistence.pluginState,
|
|
490
|
+
this.persistence.resources,
|
|
491
|
+
`plugin:${stateId}`,
|
|
492
|
+
stateId,
|
|
493
|
+
),
|
|
494
|
+
resume: createPluginResumeState({
|
|
495
|
+
pluginId: stateId,
|
|
496
|
+
getResumeState: (key, version) => this.getResumeState(namespaceKey, key, version),
|
|
497
|
+
setResumeState: (key, value, version) => this.setResumeState(namespaceKey, key, value, version),
|
|
498
|
+
deleteResumeState: (key) => this.deleteResumeState(namespaceKey, key),
|
|
499
|
+
getPaneRuntimeState: (paneId) => this.getPaneRuntimeState(paneId),
|
|
500
|
+
updatePaneRuntimeState: (paneId, patch) => this.updatePaneRuntimeState(paneId, patch),
|
|
501
|
+
}),
|
|
502
|
+
teamState: createPluginTeamState(stateId),
|
|
503
|
+
configState: {
|
|
504
|
+
get: (key) => this.getConfigState(namespaceKey, key),
|
|
505
|
+
set: (key, value) => this.setConfigState(namespaceKey, key, value),
|
|
506
|
+
delete: (key) => this.deleteConfigState(namespaceKey, key),
|
|
507
|
+
keys: () => this.getConfigStateKeys(namespaceKey),
|
|
508
|
+
},
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
432
512
|
private createContext(pluginId: string): GloomPluginContext {
|
|
433
513
|
const contributions = this.contributions;
|
|
434
514
|
const items = contributions.getOrCreatePluginItems(pluginId);
|
|
435
|
-
return {
|
|
515
|
+
return bindPluginContextNamespaces({
|
|
436
516
|
registerPane: (pane) => contributions.registerPane(pluginId, pane),
|
|
437
517
|
registerPaneTemplate: (template) => contributions.registerPaneTemplate(pluginId, template, true),
|
|
438
518
|
registerCommand: (command) => contributions.registerCommand(pluginId, command),
|
|
@@ -468,33 +548,13 @@ export class PluginRegistry implements PluginRuntimeAccess {
|
|
|
468
548
|
marketData: this.marketData,
|
|
469
549
|
connectionHealth: this.connectionHealth,
|
|
470
550
|
tickerRepository: this.tickerRepository,
|
|
471
|
-
|
|
472
|
-
this.persistence.pluginState,
|
|
473
|
-
this.persistence.resources,
|
|
474
|
-
`plugin:${this.stateNamespace(pluginId)}`,
|
|
475
|
-
this.stateNamespace(pluginId),
|
|
476
|
-
),
|
|
551
|
+
...this.createNamespacedContext(pluginId),
|
|
477
552
|
log: debugLog.createLogger(pluginId),
|
|
478
|
-
resume: createPluginResumeState({
|
|
479
|
-
pluginId: this.stateNamespace(pluginId),
|
|
480
|
-
getResumeState: (key, version) => this.getResumeState(pluginId, key, version),
|
|
481
|
-
setResumeState: (key, value, version) => this.setResumeState(pluginId, key, value, version),
|
|
482
|
-
deleteResumeState: (key) => this.deleteResumeState(pluginId, key),
|
|
483
|
-
getPaneRuntimeState: (paneId) => this.getPaneRuntimeState(paneId),
|
|
484
|
-
updatePaneRuntimeState: (paneId, patch) => this.updatePaneRuntimeState(paneId, patch),
|
|
485
|
-
}),
|
|
486
|
-
teamState: createPluginTeamState(this.stateNamespace(pluginId)),
|
|
487
553
|
paneSettings: createPluginPaneSettingsState({
|
|
488
554
|
getLayout: () => this.getLayout(),
|
|
489
555
|
updateLayout: (layout) => this.updateLayout(layout),
|
|
490
556
|
resolvePaneTarget: (paneId) => this.resolvePaneTarget(paneId),
|
|
491
557
|
}),
|
|
492
|
-
configState: {
|
|
493
|
-
get: (key) => this.getConfigState(pluginId, key),
|
|
494
|
-
set: (key, value) => this.setConfigState(pluginId, key, value),
|
|
495
|
-
delete: (key) => this.deleteConfigState(pluginId, key),
|
|
496
|
-
keys: () => this.getConfigStateKeys(pluginId),
|
|
497
|
-
},
|
|
498
558
|
createBrokerInstance: (brokerType, label, values) => this.createBrokerInstance(brokerType, label, values),
|
|
499
559
|
updateBrokerInstance: this.updateBrokerInstance,
|
|
500
560
|
syncBrokerInstance: this.syncBrokerInstance,
|
|
@@ -519,7 +579,7 @@ export class PluginRegistry implements PluginRuntimeAccess {
|
|
|
519
579
|
// Every plugin event is also a host event; TypeScript cannot see that through the generic key.
|
|
520
580
|
emit: (event, payload) => this.events.emit(event, payload as HostEvents[typeof event]),
|
|
521
581
|
notify: (notification) => this.notify(notification),
|
|
522
|
-
};
|
|
582
|
+
}, (stateId) => this.createNamespacedContext(stateId));
|
|
523
583
|
}
|
|
524
584
|
|
|
525
585
|
private registryLog = debugLog.createLogger("registry");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PaneRuntimeState } from "../../core/state/app/state";
|
|
2
2
|
import { deletePaneSetting, setPaneSetting } from "../../pane-settings";
|
|
3
3
|
import type { LayoutConfig } from "../../types/config";
|
|
4
|
-
import type { PluginPaneSettingsState, PluginResumeState } from "../../types/plugin";
|
|
4
|
+
import type { GloomPluginContext, PluginPaneSettingsState, PluginResumeState } from "../../types/plugin";
|
|
5
5
|
import { deletePluginPaneStateValue, setPluginPaneStateValue } from "../../layout/pane-state";
|
|
6
6
|
|
|
7
7
|
export class RegistryResumeStateListeners {
|
|
@@ -35,6 +35,30 @@ export class RegistryResumeStateListeners {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/** The parts of a plugin context that read and write under its state namespace. */
|
|
39
|
+
export type NamespacedPluginContext = Pick<GloomPluginContext, "persistence" | "resume" | "configState" | "teamState">;
|
|
40
|
+
|
|
41
|
+
const namespacedContexts = new WeakMap<GloomPluginContext, (stateId: string) => NamespacedPluginContext>();
|
|
42
|
+
|
|
43
|
+
/** Lets `pluginContextInNamespace` rebuild this context's state parts for another namespace. */
|
|
44
|
+
export function bindPluginContextNamespaces(
|
|
45
|
+
context: GloomPluginContext,
|
|
46
|
+
createNamespaced: (stateId: string) => NamespacedPluginContext,
|
|
47
|
+
): GloomPluginContext {
|
|
48
|
+
namespacedContexts.set(context, createNamespaced);
|
|
49
|
+
return context;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The same plugin context with its persistence, resume, config and team state
|
|
54
|
+
* under another namespace. Contributions still register to the plugin that
|
|
55
|
+
* owns the context. A context the registry did not create comes back as is.
|
|
56
|
+
*/
|
|
57
|
+
export function pluginContextInNamespace(context: GloomPluginContext, stateId: string): GloomPluginContext {
|
|
58
|
+
const createNamespaced = namespacedContexts.get(context);
|
|
59
|
+
return createNamespaced ? { ...context, ...createNamespaced(stateId) } : context;
|
|
60
|
+
}
|
|
61
|
+
|
|
38
62
|
interface PluginResumeStateOptions {
|
|
39
63
|
pluginId: string;
|
|
40
64
|
getResumeState: <T = unknown>(key: string, schemaVersion?: number) => T | null;
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
createContext,
|
|
3
3
|
createElement,
|
|
4
4
|
useContext,
|
|
5
|
+
useMemo,
|
|
5
6
|
type ReactNode,
|
|
6
7
|
} from "react";
|
|
7
8
|
import type { BrokerAdapter } from "../../types/broker";
|
|
@@ -67,6 +68,34 @@ export function withPluginRender<P>(
|
|
|
67
68
|
);
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
function PluginStateNamespace({ stateId, children }: { stateId: string; children: ReactNode }) {
|
|
72
|
+
const outer = useContext(PluginRenderContext);
|
|
73
|
+
const runtime = outer?.runtime;
|
|
74
|
+
const value = useMemo(() => (runtime ? { pluginId: stateId, runtime } : null), [runtime, stateId]);
|
|
75
|
+
if (!value) return children;
|
|
76
|
+
return <PluginRenderContext value={value}>{children}</PluginRenderContext>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Renders a component under another state namespace of the plugin render
|
|
81
|
+
* context it is already in. A built-in module that moved to another plugin
|
|
82
|
+
* keeps reading and writing the pane and plugin state it already has.
|
|
83
|
+
*/
|
|
84
|
+
export function withPluginStateNamespace<P>(
|
|
85
|
+
stateId: string,
|
|
86
|
+
component: (props: P) => ReactNode,
|
|
87
|
+
): (props: P) => ReactNode {
|
|
88
|
+
const scoped = (props: P) => (
|
|
89
|
+
<PluginStateNamespace stateId={stateId}>
|
|
90
|
+
{createElement(component as (props: any) => ReactNode, props)}
|
|
91
|
+
</PluginStateNamespace>
|
|
92
|
+
);
|
|
93
|
+
// Names the namespace in React's tools, and to tests that check a moved
|
|
94
|
+
// module still reads the state it had.
|
|
95
|
+
scoped.displayName = `PluginStateNamespace(${stateId})`;
|
|
96
|
+
return scoped;
|
|
97
|
+
}
|
|
98
|
+
|
|
70
99
|
export function usePluginRenderContext(): PluginRenderContextValue {
|
|
71
100
|
const context = useContext(PluginRenderContext);
|
|
72
101
|
if (!context) {
|
package/src/react/input.ts
CHANGED
|
@@ -57,9 +57,20 @@ interface ShortcutEntry extends ShortcutRegistration {
|
|
|
57
57
|
|
|
58
58
|
export interface ShortcutRegistry {
|
|
59
59
|
register(registration: ShortcutRegistration): () => void;
|
|
60
|
+
/**
|
|
61
|
+
* Runs `listener` for every key, before any handler is picked for it. State
|
|
62
|
+
* it sets has rendered by the time the handlers run (when the host can
|
|
63
|
+
* flush), so they read it instead of what the last frame held.
|
|
64
|
+
*/
|
|
65
|
+
beforeDispatch(listener: (event: KeyEventLike) => void): () => void;
|
|
60
66
|
dispatch(event: KeyEventLike): void;
|
|
61
67
|
}
|
|
62
68
|
|
|
69
|
+
export interface ShortcutRegistryOptions {
|
|
70
|
+
/** The renderer's synchronous render flush, which `beforeDispatch` listeners run inside. */
|
|
71
|
+
flushSync?: (run: () => void) => void;
|
|
72
|
+
}
|
|
73
|
+
|
|
63
74
|
const SHORTCUT_PHASES: ReadonlyArray<ShortcutEntry["phase"]> = [
|
|
64
75
|
"capture",
|
|
65
76
|
"before",
|
|
@@ -88,9 +99,10 @@ function orderShortcutEntries(entries: ShortcutEntry[]): ShortcutEntry[] {
|
|
|
88
99
|
});
|
|
89
100
|
}
|
|
90
101
|
|
|
91
|
-
export function createShortcutRegistry(): ShortcutRegistry {
|
|
102
|
+
export function createShortcutRegistry({ flushSync }: ShortcutRegistryOptions = {}): ShortcutRegistry {
|
|
92
103
|
let nextOrder = 1;
|
|
93
104
|
const entries = new Map<number, ShortcutEntry>();
|
|
105
|
+
const beforeListeners = new Set<(event: KeyEventLike) => void>();
|
|
94
106
|
|
|
95
107
|
return {
|
|
96
108
|
register(registration) {
|
|
@@ -104,7 +116,20 @@ export function createShortcutRegistry(): ShortcutRegistry {
|
|
|
104
116
|
entries.delete(order);
|
|
105
117
|
};
|
|
106
118
|
},
|
|
119
|
+
beforeDispatch(listener) {
|
|
120
|
+
beforeListeners.add(listener);
|
|
121
|
+
return () => {
|
|
122
|
+
beforeListeners.delete(listener);
|
|
123
|
+
};
|
|
124
|
+
},
|
|
107
125
|
dispatch(event) {
|
|
126
|
+
if (beforeListeners.size > 0) {
|
|
127
|
+
const run = () => {
|
|
128
|
+
for (const listener of [...beforeListeners]) listener(event);
|
|
129
|
+
};
|
|
130
|
+
if (flushSync) flushSync(run);
|
|
131
|
+
else run();
|
|
132
|
+
}
|
|
108
133
|
const eligible = orderShortcutEntries(
|
|
109
134
|
[...entries.values()].filter((entry) => (
|
|
110
135
|
entry.isEnabled() && shouldDeliverShortcut(event, entry.allowsEditable())
|
|
@@ -151,11 +176,24 @@ export function useRegisteredShortcut(
|
|
|
151
176
|
}), [options?.phase, options?.scope, registry]);
|
|
152
177
|
}
|
|
153
178
|
|
|
179
|
+
export function useRegisteredBeforeShortcut(
|
|
180
|
+
registry: ShortcutRegistry,
|
|
181
|
+
listener: (event: KeyEventLike) => void,
|
|
182
|
+
): void {
|
|
183
|
+
const listenerRef = useRef(listener);
|
|
184
|
+
listenerRef.current = listener;
|
|
185
|
+
useLayoutEffect(
|
|
186
|
+
() => registry.beforeDispatch((event) => listenerRef.current(event)),
|
|
187
|
+
[registry],
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
154
191
|
export interface InputHost {
|
|
155
192
|
useShortcut(
|
|
156
193
|
handler: (event: KeyEventLike) => void,
|
|
157
194
|
options?: ShortcutOptions,
|
|
158
195
|
): void;
|
|
196
|
+
useBeforeShortcut(listener: (event: KeyEventLike) => void): void;
|
|
159
197
|
useViewport(): { width: number; height: number };
|
|
160
198
|
}
|
|
161
199
|
|
|
@@ -186,6 +224,15 @@ export function useShortcut(
|
|
|
186
224
|
useInputHost().useShortcut(handler, options);
|
|
187
225
|
}
|
|
188
226
|
|
|
227
|
+
/**
|
|
228
|
+
* Runs before every key reaches any shortcut handler, whatever its phase or
|
|
229
|
+
* scope, for state the handlers read: set it here and it has rendered by the
|
|
230
|
+
* time they run. Not for acting on the key.
|
|
231
|
+
*/
|
|
232
|
+
export function useBeforeShortcut(listener: (event: KeyEventLike) => void): void {
|
|
233
|
+
useInputHost().useBeforeShortcut(listener);
|
|
234
|
+
}
|
|
235
|
+
|
|
189
236
|
export function shouldDeliverShortcut(
|
|
190
237
|
event: KeyEventLike,
|
|
191
238
|
allowEditable: boolean,
|
package/src/remote/app-host.tsx
CHANGED
|
@@ -23,6 +23,11 @@ export function useRemoteControlHandler(): RemoteControlHandler | null {
|
|
|
23
23
|
export interface RemoteControlAdapter {
|
|
24
24
|
startServer?(options: { dataDir: string; handle: RemoteControlHandler }): void | (() => void | Promise<void>);
|
|
25
25
|
registerHandler?(handler: RemoteControlHandler | null): void | (() => void);
|
|
26
|
+
/**
|
|
27
|
+
* Set where a remote assistant may drive this app through Gloom Cloud: what
|
|
28
|
+
* kind of app it is and the machine name it shows under.
|
|
29
|
+
*/
|
|
30
|
+
terminalDevice?: { kind: "desktop" | "tui"; name: string };
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
interface RemoteControlHostProps {
|