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.
Files changed (527) hide show
  1. package/package.json +4 -1
  2. package/src/api-client/askg.ts +43 -0
  3. package/src/api-client/auth.ts +15 -0
  4. package/src/api-client/beneficial-owners.ts +4 -0
  5. package/src/api-client/chat.ts +19 -0
  6. package/src/api-client/company-kpis.ts +14 -2
  7. package/src/api-client/data.ts +34 -16
  8. package/src/api-client/index.ts +12 -0
  9. package/src/api-client/normalizers.ts +27 -0
  10. package/src/api-client/paths.ts +88 -23
  11. package/src/api-client/request.ts +2 -1
  12. package/src/api-client/research-activity.ts +31 -0
  13. package/src/api-client/socket.ts +39 -8
  14. package/src/api-client/types.ts +57 -1
  15. package/src/api-client/upgrade-experiment.ts +90 -0
  16. package/src/app/global-shortcuts.ts +11 -1
  17. package/src/app/keybindings/actions.ts +10 -0
  18. package/src/app/pane-runtime/index.tsx +8 -0
  19. package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
  20. package/src/app/pane-runtime/plugin-bindings.ts +65 -25
  21. package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
  22. package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
  23. package/src/app/runtime/desktop-deeplink.ts +5 -0
  24. package/src/app/star-prompt/model.ts +98 -0
  25. package/src/app/star-prompt/runtime.ts +41 -0
  26. package/src/app/star-prompt/status-line.tsx +190 -0
  27. package/src/app.tsx +13 -7
  28. package/src/architecture/core-builtin-allowlist.json +70 -0
  29. package/src/brokers/account-cache.ts +45 -0
  30. package/src/brokers/signed-in/adapter.ts +33 -2
  31. package/src/brokers/signed-in/client.ts +10 -0
  32. package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
  33. package/src/brokers/signed-in/sign-in.ts +62 -5
  34. package/src/capabilities/registry.ts +8 -1
  35. package/src/capabilities/types.ts +2 -0
  36. package/src/cli/command-options.ts +74 -0
  37. package/src/cli/commands/command-utils.ts +29 -0
  38. package/src/cli/commands/crypto-hints.ts +12 -4
  39. package/src/cli/commands/market.ts +524 -148
  40. package/src/cli/commands/options-chain.ts +172 -0
  41. package/src/cli/commands/options-leaps.ts +422 -0
  42. package/src/cli/commands/overview.ts +223 -42
  43. package/src/cli/commands/search.ts +29 -8
  44. package/src/cli/commands/system.ts +99 -19
  45. package/src/cli/commands/themes.ts +64 -0
  46. package/src/cli/commands/ticker.ts +200 -73
  47. package/src/cli/desktop-pane-shot.ts +100 -16
  48. package/src/cli/errors.ts +25 -2
  49. package/src/cli/freshness.ts +7 -4
  50. package/src/cli/fx-pair.ts +57 -0
  51. package/src/cli/help.ts +40 -7
  52. package/src/cli/history-rows.ts +187 -15
  53. package/src/cli/index-roots.ts +51 -0
  54. package/src/cli/index.ts +21 -13
  55. package/src/cli/listing-arg.ts +211 -3
  56. package/src/cli/not-a-ticker.ts +37 -0
  57. package/src/cli/options.ts +55 -7
  58. package/src/cli/pane-functions/capabilities.ts +13 -2
  59. package/src/cli/pane-functions/catalog.ts +13 -2
  60. package/src/cli/pane-functions/data.ts +36 -5
  61. package/src/cli/pane-functions/dom.ts +89 -28
  62. package/src/cli/pane-functions/freshness.ts +205 -26
  63. package/src/cli/pane-functions/function-help.ts +71 -24
  64. package/src/cli/pane-functions/headless.ts +168 -35
  65. package/src/cli/pane-functions/index.ts +12 -2
  66. package/src/cli/pane-functions/market-session.ts +133 -0
  67. package/src/cli/pane-functions/options.ts +15 -4
  68. package/src/cli/pane-functions/report-notices.ts +63 -0
  69. package/src/cli/pane-functions/report.ts +12 -1
  70. package/src/cli/pane-functions/resolver.ts +32 -8
  71. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  72. package/src/cli/pane-functions/screenshot.ts +109 -23
  73. package/src/cli/registry.ts +8 -4
  74. package/src/cli/report-tables.ts +460 -0
  75. package/src/cli/result.ts +63 -16
  76. package/src/cli/row-window.ts +76 -0
  77. package/src/cli/scroll-truncation.ts +34 -0
  78. package/src/components/chart/composite/composite-chart.tsx +43 -4
  79. package/src/components/chart/composite/legend.tsx +39 -6
  80. package/src/components/chart/composite/rasterizer.ts +64 -4
  81. package/src/components/chart/composite/series-cues.ts +71 -0
  82. package/src/components/chart/composite/text-renderer.ts +16 -8
  83. package/src/components/chart/composite/types.ts +3 -0
  84. package/src/components/command-bar/commands/direct/index.ts +9 -1
  85. package/src/components/command-bar/commands/plugin/items.ts +3 -1
  86. package/src/components/command-bar/commands/registry.ts +7 -0
  87. package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
  88. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  89. package/src/components/command-bar/routes/root/command-items.ts +2 -0
  90. package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
  91. package/src/components/command-bar/routes/root/results.ts +40 -10
  92. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  93. package/src/components/command-bar/routes/root/search-providers.ts +97 -14
  94. package/src/components/command-bar/surface/index.tsx +18 -4
  95. package/src/components/command-bar/theme-picker.tsx +48 -13
  96. package/src/components/command-bar/workflow/ops.ts +5 -3
  97. package/src/components/data-table/export.ts +6 -51
  98. package/src/components/data-table/view.tsx +31 -10
  99. package/src/components/layout/floating-pane.tsx +6 -2
  100. package/src/components/layout/market-summary.ts +23 -9
  101. package/src/components/layout/pane/content.tsx +46 -29
  102. package/src/components/layout/pane/cycle-order.ts +2 -11
  103. package/src/components/layout/pane/header.tsx +2 -1
  104. package/src/components/layout/pane/title.ts +1 -1
  105. package/src/components/layout/presentation.ts +22 -0
  106. package/src/components/layout/shell/active-drag.ts +67 -35
  107. package/src/components/layout/shell/cursor-occlusion.ts +6 -5
  108. package/src/components/layout/shell/drag/index.ts +16 -7
  109. package/src/components/layout/shell/drag/live.ts +193 -0
  110. package/src/components/layout/shell/drag/overlays.tsx +179 -35
  111. package/src/components/layout/shell/drag/runtime.ts +62 -50
  112. package/src/components/layout/shell/drag/slide.ts +10 -0
  113. package/src/components/layout/shell/index.tsx +84 -57
  114. package/src/components/layout/shell/layout-state.ts +5 -11
  115. package/src/components/layout/shell/menu.ts +28 -1
  116. package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
  117. package/src/components/layout/shell/native/surfaces.ts +30 -14
  118. package/src/components/layout/shell/pane/actions.ts +2 -1
  119. package/src/components/layout/shell/pane/layers.tsx +461 -180
  120. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  121. package/src/components/layout/shell/pane/new-layout.ts +116 -0
  122. package/src/components/layout/shell/shortcuts.ts +5 -2
  123. package/src/components/layout/shell/terminal-pointer-runtime.ts +18 -18
  124. package/src/components/layout/shell/visible-layout.ts +44 -1
  125. package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
  126. package/src/components/layout/status-bar.tsx +4 -1
  127. package/src/components/markdown-text.tsx +9 -5
  128. package/src/components/metric-treemap/heat-surface.tsx +27 -8
  129. package/src/components/metric-treemap/heat-tiles.ts +48 -17
  130. package/src/components/onboarding/onboarding-frame.tsx +33 -10
  131. package/src/components/onboarding/onboarding-wizard.tsx +49 -40
  132. package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
  133. package/src/components/onboarding/pro-step-layout.ts +86 -0
  134. package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
  135. package/src/components/ui/button.tsx +10 -0
  136. package/src/components/ui/data-table/index.tsx +38 -0
  137. package/src/components/ui/data-table/types.ts +8 -1
  138. package/src/components/ui/data-table/visible-range.ts +2 -0
  139. package/src/components/ui/icon.tsx +11 -2
  140. package/src/components/ui/index.ts +1 -1
  141. package/src/core/connection-health.ts +1 -1
  142. package/src/core/state/app/layout-reducer.ts +89 -3
  143. package/src/core/state/app/state.ts +8 -5
  144. package/src/core/state/app/types.ts +13 -2
  145. package/src/data/config/layout.ts +55 -21
  146. package/src/data/config/store/fold-panes.ts +149 -0
  147. package/src/data/config/store/migrations.ts +7 -0
  148. package/src/data/config/store/node.ts +27 -6
  149. package/src/data/config/store/normalize.ts +37 -3
  150. package/src/data/json-persistence.ts +17 -3
  151. package/src/data/json-storage.ts +2 -0
  152. package/src/data/memory-resource-store.ts +46 -0
  153. package/src/i18n/es.ts +14 -1
  154. package/src/i18n/ja.ts +14 -1
  155. package/src/i18n/ko.ts +14 -1
  156. package/src/i18n/zh-cn.ts +14 -1
  157. package/src/i18n/zh-tw.ts +14 -1
  158. package/src/layout/desks.ts +1 -2
  159. package/src/layout/pane-layout-move.ts +264 -0
  160. package/src/layout/pane-manager/hidden-panes.ts +160 -0
  161. package/src/layout/pane-manager/index.ts +1 -0
  162. package/src/market-data/coordinator/entries.ts +7 -1
  163. package/src/market-data/coordinator/quotes.ts +2 -2
  164. package/src/market-data/market/freshness.ts +156 -11
  165. package/src/market-data/market/status.ts +40 -0
  166. package/src/market-data/options-alternatives.ts +69 -0
  167. package/src/market-data/quotes/contributions.ts +19 -2
  168. package/src/market-data/quotes/freshness.ts +5 -0
  169. package/src/market-data/quotes/metadata.ts +16 -0
  170. package/src/market-data/quotes/screener-live-quotes.ts +56 -5
  171. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  172. package/src/plugins/builtin/account-management/model.ts +1 -0
  173. package/src/plugins/builtin/account-management/navigation.ts +1 -1
  174. package/src/plugins/builtin/account-management/pane.tsx +31 -4
  175. package/src/plugins/builtin/alerts/quotes.ts +3 -0
  176. package/src/plugins/builtin/analytics/pane-model.ts +10 -6
  177. package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
  178. package/src/plugins/builtin/analytics/risk-model.ts +37 -8
  179. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
  180. package/src/plugins/builtin/attention/pane.tsx +1 -1
  181. package/src/plugins/builtin/backtest/pane.tsx +1 -1
  182. package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
  183. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  184. package/src/plugins/builtin/broker-manager/model.ts +8 -0
  185. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
  186. package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
  187. package/src/plugins/builtin/broker-manager/table.ts +5 -0
  188. package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
  189. package/src/plugins/builtin/catalysts/detail.tsx +1 -1
  190. package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
  191. package/src/plugins/builtin/chat/channels.ts +34 -36
  192. package/src/plugins/builtin/chat/command-bar.ts +138 -0
  193. package/src/plugins/builtin/chat/composer-commands.ts +21 -1
  194. package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
  195. package/src/plugins/builtin/chat/content/footer.tsx +5 -3
  196. package/src/plugins/builtin/chat/content/index.tsx +21 -1
  197. package/src/plugins/builtin/chat/content/mentions.ts +2 -1
  198. package/src/plugins/builtin/chat/content/user-map.ts +3 -1
  199. package/src/plugins/builtin/chat/controller/index.ts +48 -0
  200. package/src/plugins/builtin/chat/conversations.ts +256 -0
  201. package/src/plugins/builtin/chat/discord-command.ts +91 -0
  202. package/src/plugins/builtin/chat/edit-window.ts +12 -6
  203. package/src/plugins/builtin/chat/ghost-user.ts +14 -0
  204. package/src/plugins/builtin/chat/index.tsx +40 -2
  205. package/src/plugins/builtin/chat/layout.ts +2 -0
  206. package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
  207. package/src/plugins/builtin/chat/message/header.tsx +24 -15
  208. package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
  209. package/src/plugins/builtin/chat/message/render-state.ts +3 -0
  210. package/src/plugins/builtin/chat/pane-requests.ts +60 -0
  211. package/src/plugins/builtin/chat/pane-state.ts +32 -2
  212. package/src/plugins/builtin/chat/pane.tsx +8 -0
  213. package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
  214. package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
  215. package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
  216. package/src/plugins/builtin/cloud/askg/model.ts +129 -70
  217. package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
  218. package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
  219. package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
  220. package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
  221. package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
  222. package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
  223. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
  224. package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
  225. package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
  226. package/src/plugins/builtin/cloud/plugin.tsx +36 -1
  227. package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
  228. package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
  229. package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
  230. package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
  231. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
  232. package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
  233. package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
  234. package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
  235. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
  236. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
  237. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
  238. package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
  239. package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
  240. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
  241. package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
  242. package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
  243. package/src/plugins/builtin/comment-letters/client.ts +1 -3
  244. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  245. package/src/plugins/builtin/company-kpis/client.ts +23 -8
  246. package/src/plugins/builtin/company-kpis/headless.ts +3 -1
  247. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  248. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
  249. package/src/plugins/builtin/composite-plugins.ts +107 -64
  250. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
  251. package/src/plugins/builtin/credit-boards/index.tsx +21 -21
  252. package/src/plugins/builtin/credit-boards/model.ts +0 -1
  253. package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
  254. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
  255. package/src/plugins/builtin/debt-maturities/client.ts +37 -9
  256. package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
  257. package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
  258. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  259. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  260. package/src/plugins/builtin/earnings-calls/data.ts +28 -7
  261. package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
  262. package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
  263. package/src/plugins/builtin/econ/calendar-model.ts +42 -17
  264. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  265. package/src/plugins/builtin/econ/index.tsx +68 -22
  266. package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
  267. package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
  268. package/src/plugins/builtin/econ-statistics/view.ts +17 -0
  269. package/src/plugins/builtin/executives/data.ts +17 -6
  270. package/src/plugins/builtin/executives/pane.tsx +16 -7
  271. package/src/plugins/builtin/filing-events/pane.tsx +22 -5
  272. package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
  273. package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
  274. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
  275. package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
  276. package/src/plugins/builtin/gpu/headless.ts +9 -3
  277. package/src/plugins/builtin/gpu/model.ts +14 -5
  278. package/src/plugins/builtin/help/index.tsx +1 -0
  279. package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
  280. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  281. package/src/plugins/builtin/holders/client.ts +3 -6
  282. package/src/plugins/builtin/holders/format.ts +31 -1
  283. package/src/plugins/builtin/holders/headless.ts +32 -6
  284. package/src/plugins/builtin/holders/pane.tsx +25 -13
  285. package/src/plugins/builtin/holders/report-header.ts +46 -5
  286. package/src/plugins/builtin/insider/index.tsx +4 -4
  287. package/src/plugins/builtin/iv-history/charts.tsx +21 -7
  288. package/src/plugins/builtin/iv-history/client.ts +12 -2
  289. package/src/plugins/builtin/iv-history/format.ts +33 -2
  290. package/src/plugins/builtin/iv-history/headless.ts +22 -10
  291. package/src/plugins/builtin/iv-history/index.tsx +3 -3
  292. package/src/plugins/builtin/iv-history/model.ts +61 -13
  293. package/src/plugins/builtin/iv-history/pane.tsx +11 -3
  294. package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
  295. package/src/plugins/builtin/jobs/pane.tsx +1 -1
  296. package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
  297. package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
  298. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  299. package/src/plugins/builtin/kelly-sizer/pane.tsx +121 -45
  300. package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
  301. package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
  302. package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
  303. package/src/plugins/builtin/layout-manager/index.tsx +9 -1
  304. package/src/plugins/builtin/macro-day/client.ts +24 -0
  305. package/src/plugins/builtin/macro-day/headless.ts +11 -8
  306. package/src/plugins/builtin/macro-day/index.tsx +3 -0
  307. package/src/plugins/builtin/macro-day/model.ts +11 -6
  308. package/src/plugins/builtin/macro-day/pane.tsx +12 -8
  309. package/src/plugins/builtin/macro-day/release-table.ts +40 -0
  310. package/src/plugins/builtin/macro-day/releases.ts +62 -44
  311. package/src/plugins/builtin/market-heatmap/data.ts +25 -5
  312. package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
  313. package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
  314. package/src/plugins/builtin/market-heatmap/model.ts +94 -3
  315. package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
  316. package/src/plugins/builtin/market-movers/headless.ts +31 -4
  317. package/src/plugins/builtin/market-movers/index.tsx +8 -1
  318. package/src/plugins/builtin/market-movers/model.ts +71 -17
  319. package/src/plugins/builtin/market-movers/table.ts +50 -23
  320. package/src/plugins/builtin/members/pane.tsx +10 -0
  321. package/src/plugins/builtin/news/headless.ts +9 -2
  322. package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
  323. package/src/plugins/builtin/openfda/client.ts +1 -1
  324. package/src/plugins/builtin/options/carry.ts +95 -0
  325. package/src/plugins/builtin/options/index.tsx +58 -5
  326. package/src/plugins/builtin/options/strike-window.ts +183 -0
  327. package/src/plugins/builtin/options/table.ts +55 -3
  328. package/src/plugins/builtin/options/types.ts +8 -1
  329. package/src/plugins/builtin/options/view.tsx +161 -12
  330. package/src/plugins/builtin/options-positioning/headless.ts +4 -2
  331. package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
  332. package/src/plugins/builtin/options-scenario/client.ts +73 -17
  333. package/src/plugins/builtin/options-scenario/headless.ts +71 -24
  334. package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
  335. package/src/plugins/builtin/options-scenario/index.tsx +9 -2
  336. package/src/plugins/builtin/options-scenario/model.ts +33 -0
  337. package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
  338. package/src/plugins/builtin/pe-band/model.ts +2 -2
  339. package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
  340. package/src/plugins/builtin/perps/equity-row.tsx +8 -5
  341. package/src/plugins/builtin/perps/model.ts +16 -5
  342. package/src/plugins/builtin/perps/pane.tsx +1 -1
  343. package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
  344. package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
  345. package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
  346. package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
  347. package/src/plugins/builtin/plugin-module.ts +87 -4
  348. package/src/plugins/builtin/portfolio-list/account-metrics.ts +16 -1
  349. package/src/plugins/builtin/portfolio-list/allocation.ts +3 -0
  350. package/src/plugins/builtin/portfolio-list/cached-account.ts +18 -9
  351. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
  352. package/src/plugins/builtin/portfolio-list/cli/render.ts +89 -18
  353. package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
  354. package/src/plugins/builtin/portfolio-list/headless.ts +40 -7
  355. package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -4
  356. package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -4
  357. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
  358. package/src/plugins/builtin/realized-vol/headless.ts +3 -0
  359. package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
  360. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
  361. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
  362. package/src/plugins/builtin/research/relative-valuation-model.ts +7 -3
  363. package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
  364. package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
  365. package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
  366. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
  367. package/src/plugins/builtin/reverse-dcf/model.ts +2 -1
  368. package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
  369. package/src/plugins/builtin/risk-factors/data.ts +23 -9
  370. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  371. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  372. package/src/plugins/builtin/seasonality/pane.tsx +1 -1
  373. package/src/plugins/builtin/sec/index.tsx +5 -5
  374. package/src/plugins/builtin/sectors/client.ts +10 -2
  375. package/src/plugins/builtin/sectors/headless.ts +35 -6
  376. package/src/plugins/builtin/sectors/index.tsx +18 -1
  377. package/src/plugins/builtin/sectors/sector-model.ts +150 -41
  378. package/src/plugins/builtin/shared/command-tab.ts +51 -0
  379. package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
  380. package/src/plugins/builtin/shared/members-menu.tsx +260 -0
  381. package/src/plugins/builtin/shared/report-freshness.ts +50 -2
  382. package/src/plugins/builtin/shared/ticker-request.ts +25 -1
  383. package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
  384. package/src/plugins/builtin/shared/volatility/options.ts +1 -46
  385. package/src/plugins/builtin/short-interest/index.tsx +19 -4
  386. package/src/plugins/builtin/short-interest/surface.tsx +4 -4
  387. package/src/plugins/builtin/short-volume/index.tsx +1 -10
  388. package/src/plugins/builtin/short-volume/pane.tsx +1 -1
  389. package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
  390. package/src/plugins/builtin/supply-chain/model.ts +1 -1
  391. package/src/plugins/builtin/themes/pane.tsx +3 -0
  392. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
  393. package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
  394. package/src/plugins/builtin/ticker-detail/headless.ts +63 -25
  395. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -4
  396. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +3 -2
  397. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
  398. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
  399. package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
  400. package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
  401. package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
  402. package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
  403. package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
  404. package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
  405. package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
  406. package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
  407. package/src/plugins/builtin/vol-surface/client.ts +6 -2
  408. package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
  409. package/src/plugins/builtin/vol-surface/headless.ts +124 -20
  410. package/src/plugins/builtin/vol-surface/model.ts +23 -11
  411. package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
  412. package/src/plugins/builtin/vol-surface/tables.ts +124 -0
  413. package/src/plugins/builtin/world-indices/client.ts +22 -4
  414. package/src/plugins/builtin/world-indices/headless.ts +54 -27
  415. package/src/plugins/builtin/world-indices/model.ts +4 -2
  416. package/src/plugins/builtin/world-indices/table.tsx +13 -2
  417. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +2 -0
  418. package/src/plugins/builtin/world-venue-map/basemap.ts +18 -0
  419. package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
  420. package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
  421. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +2 -1
  422. package/src/plugins/builtin/world-venue-map/index.ts +6 -6
  423. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +27 -14
  424. package/src/plugins/builtin/world-venue-map/layers.ts +68 -10
  425. package/src/plugins/builtin/world-venue-map/map.tsx +198 -62
  426. package/src/plugins/builtin/world-venue-map/model.ts +16 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +3 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
  429. package/src/plugins/builtin/world-venue-map/pane.tsx +16 -10
  430. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  431. package/src/plugins/builtin-editorial.ts +126 -0
  432. package/src/plugins/catalog-backend.ts +16 -9
  433. package/src/plugins/catalog-browser.ts +127 -63
  434. package/src/plugins/catalog-manifest.ts +73 -40
  435. package/src/plugins/catalog-ui.ts +26 -6
  436. package/src/plugins/extracted-plugins.ts +7 -1
  437. package/src/plugins/ownership.ts +150 -7
  438. package/src/plugins/plugin-functions.ts +54 -0
  439. package/src/plugins/plugin-off.ts +36 -0
  440. package/src/plugins/registry/host-actions.ts +16 -0
  441. package/src/plugins/registry/index.ts +84 -24
  442. package/src/plugins/registry/plugin-state.ts +25 -1
  443. package/src/plugins/runtime/context.tsx +29 -0
  444. package/src/react/input.ts +48 -1
  445. package/src/remote/app-host.tsx +5 -0
  446. package/src/remote/controller.ts +3 -53
  447. package/src/remote/matching.ts +69 -0
  448. package/src/remote/schema.ts +1 -1
  449. package/src/remote/semantic-tree.tsx +14 -0
  450. package/src/remote/types.ts +2 -0
  451. package/src/renderers/browser/app-services.ts +3 -1
  452. package/src/renderers/dom/data-table/dom.ts +31 -0
  453. package/src/renderers/dom/data-table/index.tsx +5 -0
  454. package/src/renderers/dom/desktop/icons.tsx +14 -0
  455. package/src/renderers/dom/desktop/query-bar.tsx +59 -32
  456. package/src/renderers/dom/host/box.tsx +113 -17
  457. package/src/renderers/dom/host/drag-shield.ts +35 -0
  458. package/src/renderers/dom/host/live-frame.ts +62 -0
  459. package/src/renderers/dom/host/mouse.ts +8 -1
  460. package/src/renderers/dom/host/scroll-box.tsx +6 -5
  461. package/src/renderers/dom/host/tabs.tsx +3 -2
  462. package/src/renderers/dom/input-host.tsx +6 -1
  463. package/src/renderers/dom/styles.css +26 -5
  464. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
  465. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  466. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
  467. package/src/renderers/electrobun/bun/desktop/initialization.ts +15 -1
  468. package/src/renderers/electrobun/shared/protocol.ts +12 -1
  469. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -1
  470. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
  471. package/src/renderers/electrobun/view/host/native.ts +0 -10
  472. package/src/renderers/electrobun/view/host/window-move.ts +28 -0
  473. package/src/renderers/electrobun/view/main.tsx +4 -1
  474. package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
  475. package/src/renderers/electrobun/view/ui-host.tsx +2 -5
  476. package/src/renderers/opentui/input-host.tsx +6 -1
  477. package/src/renderers/opentui/start.tsx +9 -0
  478. package/src/renderers/opentui/ui-host.tsx +7 -1
  479. package/src/shares/portable-layout.ts +16 -6
  480. package/src/sources/gloomberb-cloud/index.ts +19 -10
  481. package/src/sources/gloomberb-cloud/normalizers.ts +20 -21
  482. package/src/sources/listing-symbols.ts +11 -5
  483. package/src/sources/provider-errors.ts +88 -3
  484. package/src/sources/provider-router/batches.ts +3 -3
  485. package/src/sources/provider-router/cached-routes.ts +8 -4
  486. package/src/sources/provider-router/financial-routes.ts +13 -7
  487. package/src/sources/provider-router/financials.ts +106 -16
  488. package/src/sources/provider-router/history.ts +27 -11
  489. package/src/sources/provider-router/index.ts +1 -1
  490. package/src/sources/provider-router/primary.ts +24 -6
  491. package/src/sources/sec-edgar.ts +68 -8
  492. package/src/state/pane-scroll-registry.tsx +67 -35
  493. package/src/sync/core-contributors.ts +4 -3
  494. package/src/test-support/cli-context.ts +2 -1
  495. package/src/test-support/data-provider.ts +34 -0
  496. package/src/test-support/margin-account.ts +108 -0
  497. package/src/test-support/shell-pane-keys.tsx +1 -0
  498. package/src/theme/series-colors.ts +46 -0
  499. package/src/theme/themes.ts +149 -0
  500. package/src/tickers/search/index.ts +22 -2
  501. package/src/tickers/search/ranking.ts +45 -1
  502. package/src/tickers/search/types.ts +3 -1
  503. package/src/time-series/resolution.ts +15 -0
  504. package/src/time-series/resolve.ts +1 -14
  505. package/src/types/config.ts +61 -1
  506. package/src/types/financials.ts +32 -3
  507. package/src/types/headless.ts +11 -0
  508. package/src/types/instrument.ts +8 -0
  509. package/src/types/plugin.ts +49 -3
  510. package/src/ui/host.tsx +16 -0
  511. package/src/ui/index.tsx +10 -3
  512. package/src/ui/scroll-box-axis.ts +26 -0
  513. package/src/utils/cli-output.ts +14 -1
  514. package/src/utils/currency-units.ts +8 -3
  515. package/src/utils/depositary-receipt.ts +54 -10
  516. package/src/utils/display-number.ts +63 -0
  517. package/src/utils/exchanges.ts +13 -0
  518. package/src/utils/format.ts +2 -1
  519. package/src/utils/fundamentals.ts +17 -1
  520. package/src/utils/fx-market-hours.ts +34 -0
  521. package/src/utils/option-expiry.ts +126 -0
  522. package/src/utils/price-history-integrity.ts +19 -6
  523. package/src/utils/price-history.ts +39 -6
  524. package/src/utils/rpc-timeout-error.ts +6 -2
  525. package/src/utils/sec-eps-basis.ts +159 -30
  526. package/src/utils/sec.ts +38 -34
  527. package/src/utils/throttled-fetch.ts +51 -22
@@ -1,9 +1,12 @@
1
1
  /**
2
- * Built-in module ids and the built-in plugin that owns each one. Every key
3
- * does two jobs: config, sync and session state saved under it is rewritten to
4
- * the owner, and it is reserved, because an external plugin using it would
5
- * have its state merged into the owner the same way. A module that moves out
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(normalizeBuiltinPluginOwnerId)
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 { RegistryResumeStateListeners, createPluginPaneSettingsState, createPluginResumeState } from "./plugin-state";
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
- persistence: createPluginPersistence(
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) {
@@ -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,
@@ -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 {