gloomberb 0.16.3 → 0.16.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (451) 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/signed-in/adapter.ts +33 -2
  30. package/src/brokers/signed-in/client.ts +10 -0
  31. package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
  32. package/src/brokers/signed-in/sign-in.ts +62 -5
  33. package/src/capabilities/registry.ts +8 -1
  34. package/src/capabilities/types.ts +2 -0
  35. package/src/cli/commands/crypto-hints.ts +12 -4
  36. package/src/cli/commands/market.ts +359 -99
  37. package/src/cli/commands/options-chain.ts +172 -0
  38. package/src/cli/commands/options-leaps.ts +422 -0
  39. package/src/cli/commands/overview.ts +164 -26
  40. package/src/cli/commands/search.ts +29 -8
  41. package/src/cli/commands/system.ts +99 -19
  42. package/src/cli/commands/themes.ts +64 -0
  43. package/src/cli/commands/ticker.ts +62 -30
  44. package/src/cli/desktop-pane-shot.ts +97 -16
  45. package/src/cli/errors.ts +25 -2
  46. package/src/cli/freshness.ts +7 -4
  47. package/src/cli/fx-pair.ts +57 -0
  48. package/src/cli/help.ts +14 -1
  49. package/src/cli/index-roots.ts +51 -0
  50. package/src/cli/index.ts +9 -6
  51. package/src/cli/listing-arg.ts +211 -3
  52. package/src/cli/not-a-ticker.ts +37 -0
  53. package/src/cli/options.ts +50 -6
  54. package/src/cli/pane-functions/capabilities.ts +8 -2
  55. package/src/cli/pane-functions/catalog.ts +13 -2
  56. package/src/cli/pane-functions/data.ts +36 -5
  57. package/src/cli/pane-functions/dom.ts +21 -13
  58. package/src/cli/pane-functions/freshness.ts +205 -26
  59. package/src/cli/pane-functions/function-help.ts +67 -22
  60. package/src/cli/pane-functions/headless.ts +5 -4
  61. package/src/cli/pane-functions/index.ts +3 -0
  62. package/src/cli/pane-functions/market-session.ts +133 -0
  63. package/src/cli/pane-functions/options.ts +15 -4
  64. package/src/cli/pane-functions/report-notices.ts +63 -0
  65. package/src/cli/pane-functions/report.ts +4 -0
  66. package/src/cli/pane-functions/resolver.ts +4 -6
  67. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  68. package/src/cli/pane-functions/screenshot.ts +105 -23
  69. package/src/cli/registry.ts +2 -1
  70. package/src/cli/result.ts +50 -13
  71. package/src/cli/row-window.ts +76 -0
  72. package/src/cli/scroll-truncation.ts +34 -0
  73. package/src/components/chart/composite/composite-chart.tsx +43 -4
  74. package/src/components/chart/composite/legend.tsx +39 -6
  75. package/src/components/chart/composite/rasterizer.ts +64 -4
  76. package/src/components/chart/composite/series-cues.ts +71 -0
  77. package/src/components/chart/composite/text-renderer.ts +16 -8
  78. package/src/components/chart/composite/types.ts +3 -0
  79. package/src/components/command-bar/commands/direct/index.ts +9 -1
  80. package/src/components/command-bar/commands/plugin/items.ts +3 -1
  81. package/src/components/command-bar/commands/registry.ts +7 -0
  82. package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
  83. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  84. package/src/components/command-bar/routes/root/command-items.ts +2 -0
  85. package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
  86. package/src/components/command-bar/routes/root/results.ts +40 -10
  87. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  88. package/src/components/command-bar/routes/root/search-providers.ts +97 -14
  89. package/src/components/command-bar/surface/index.tsx +18 -4
  90. package/src/components/command-bar/theme-picker.tsx +48 -13
  91. package/src/components/command-bar/workflow/ops.ts +5 -3
  92. package/src/components/data-table/view.tsx +28 -9
  93. package/src/components/layout/floating-pane.tsx +6 -2
  94. package/src/components/layout/market-summary.ts +23 -9
  95. package/src/components/layout/pane/content.tsx +46 -29
  96. package/src/components/layout/pane/cycle-order.ts +2 -11
  97. package/src/components/layout/pane/header.tsx +2 -1
  98. package/src/components/layout/pane/title.ts +1 -1
  99. package/src/components/layout/presentation.ts +22 -0
  100. package/src/components/layout/shell/active-drag.ts +1 -1
  101. package/src/components/layout/shell/drag/index.ts +4 -0
  102. package/src/components/layout/shell/drag/live.ts +76 -5
  103. package/src/components/layout/shell/drag/runtime.ts +8 -8
  104. package/src/components/layout/shell/index.tsx +47 -6
  105. package/src/components/layout/shell/layout-state.ts +5 -11
  106. package/src/components/layout/shell/menu.ts +28 -1
  107. package/src/components/layout/shell/pane/actions.ts +2 -1
  108. package/src/components/layout/shell/pane/layers.tsx +51 -10
  109. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  110. package/src/components/layout/shell/pane/new-layout.ts +116 -0
  111. package/src/components/layout/shell/shortcuts.ts +5 -2
  112. package/src/components/layout/shell/visible-layout.ts +44 -1
  113. package/src/components/layout/status-bar.tsx +4 -1
  114. package/src/components/markdown-text.tsx +9 -5
  115. package/src/components/metric-treemap/heat-surface.tsx +27 -8
  116. package/src/components/metric-treemap/heat-tiles.ts +48 -17
  117. package/src/components/onboarding/onboarding-frame.tsx +33 -10
  118. package/src/components/onboarding/onboarding-wizard.tsx +49 -40
  119. package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
  120. package/src/components/onboarding/pro-step-layout.ts +86 -0
  121. package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
  122. package/src/components/ui/button.tsx +10 -0
  123. package/src/components/ui/data-table/index.tsx +38 -0
  124. package/src/components/ui/data-table/types.ts +8 -1
  125. package/src/components/ui/data-table/visible-range.ts +2 -0
  126. package/src/components/ui/icon.tsx +11 -2
  127. package/src/components/ui/index.ts +1 -1
  128. package/src/core/connection-health.ts +1 -1
  129. package/src/core/state/app/layout-reducer.ts +89 -3
  130. package/src/core/state/app/state.ts +8 -5
  131. package/src/core/state/app/types.ts +13 -2
  132. package/src/data/config/layout.ts +55 -21
  133. package/src/data/config/store/fold-panes.ts +149 -0
  134. package/src/data/config/store/migrations.ts +7 -0
  135. package/src/data/config/store/node.ts +27 -6
  136. package/src/data/config/store/normalize.ts +37 -3
  137. package/src/i18n/es.ts +14 -1
  138. package/src/i18n/ja.ts +14 -1
  139. package/src/i18n/ko.ts +14 -1
  140. package/src/i18n/zh-cn.ts +14 -1
  141. package/src/i18n/zh-tw.ts +14 -1
  142. package/src/layout/desks.ts +1 -2
  143. package/src/layout/pane-layout-move.ts +264 -0
  144. package/src/layout/pane-manager/hidden-panes.ts +160 -0
  145. package/src/layout/pane-manager/index.ts +1 -0
  146. package/src/market-data/coordinator/entries.ts +7 -1
  147. package/src/market-data/coordinator/quotes.ts +2 -2
  148. package/src/market-data/market/freshness.ts +118 -18
  149. package/src/market-data/market/status.ts +40 -0
  150. package/src/market-data/options-alternatives.ts +69 -0
  151. package/src/market-data/quotes/contributions.ts +19 -2
  152. package/src/market-data/quotes/metadata.ts +16 -0
  153. package/src/market-data/quotes/screener-live-quotes.ts +56 -5
  154. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  155. package/src/plugins/builtin/account-management/model.ts +1 -0
  156. package/src/plugins/builtin/account-management/navigation.ts +1 -1
  157. package/src/plugins/builtin/account-management/pane.tsx +31 -4
  158. package/src/plugins/builtin/alerts/quotes.ts +3 -0
  159. package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
  160. package/src/plugins/builtin/analytics/risk-model.ts +37 -8
  161. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
  162. package/src/plugins/builtin/backtest/pane.tsx +1 -1
  163. package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
  164. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  165. package/src/plugins/builtin/broker-manager/model.ts +8 -0
  166. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
  167. package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
  168. package/src/plugins/builtin/broker-manager/table.ts +5 -0
  169. package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
  170. package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
  171. package/src/plugins/builtin/chat/channels.ts +34 -36
  172. package/src/plugins/builtin/chat/command-bar.ts +138 -0
  173. package/src/plugins/builtin/chat/composer-commands.ts +21 -1
  174. package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
  175. package/src/plugins/builtin/chat/content/footer.tsx +5 -3
  176. package/src/plugins/builtin/chat/content/index.tsx +21 -1
  177. package/src/plugins/builtin/chat/content/mentions.ts +2 -1
  178. package/src/plugins/builtin/chat/content/user-map.ts +3 -1
  179. package/src/plugins/builtin/chat/controller/index.ts +48 -0
  180. package/src/plugins/builtin/chat/conversations.ts +256 -0
  181. package/src/plugins/builtin/chat/discord-command.ts +91 -0
  182. package/src/plugins/builtin/chat/edit-window.ts +12 -6
  183. package/src/plugins/builtin/chat/ghost-user.ts +14 -0
  184. package/src/plugins/builtin/chat/index.tsx +40 -2
  185. package/src/plugins/builtin/chat/layout.ts +2 -0
  186. package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
  187. package/src/plugins/builtin/chat/message/header.tsx +24 -15
  188. package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
  189. package/src/plugins/builtin/chat/message/render-state.ts +3 -0
  190. package/src/plugins/builtin/chat/pane-requests.ts +60 -0
  191. package/src/plugins/builtin/chat/pane-state.ts +32 -2
  192. package/src/plugins/builtin/chat/pane.tsx +8 -0
  193. package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
  194. package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
  195. package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
  196. package/src/plugins/builtin/cloud/askg/model.ts +129 -70
  197. package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
  198. package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
  199. package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
  200. package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
  201. package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
  202. package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
  203. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
  204. package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
  205. package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
  206. package/src/plugins/builtin/cloud/plugin.tsx +36 -1
  207. package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
  208. package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
  209. package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
  210. package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
  211. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
  212. package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
  213. package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
  214. package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
  215. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
  216. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
  217. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
  218. package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
  219. package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
  220. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
  221. package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
  222. package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
  223. package/src/plugins/builtin/comment-letters/client.ts +1 -3
  224. package/src/plugins/builtin/company-kpis/client.ts +23 -8
  225. package/src/plugins/builtin/company-kpis/headless.ts +3 -1
  226. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  227. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
  228. package/src/plugins/builtin/composite-plugins.ts +107 -64
  229. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
  230. package/src/plugins/builtin/credit-boards/index.tsx +21 -21
  231. package/src/plugins/builtin/credit-boards/model.ts +0 -1
  232. package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
  233. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
  234. package/src/plugins/builtin/debt-maturities/client.ts +37 -9
  235. package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
  236. package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
  237. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  238. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  239. package/src/plugins/builtin/earnings-calls/data.ts +28 -7
  240. package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
  241. package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
  242. package/src/plugins/builtin/econ/calendar-model.ts +42 -17
  243. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  244. package/src/plugins/builtin/econ/index.tsx +68 -22
  245. package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
  246. package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
  247. package/src/plugins/builtin/econ-statistics/view.ts +17 -0
  248. package/src/plugins/builtin/executives/data.ts +17 -6
  249. package/src/plugins/builtin/executives/pane.tsx +16 -7
  250. package/src/plugins/builtin/filing-events/pane.tsx +22 -5
  251. package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
  252. package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
  253. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
  254. package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
  255. package/src/plugins/builtin/gpu/headless.ts +9 -3
  256. package/src/plugins/builtin/gpu/model.ts +14 -5
  257. package/src/plugins/builtin/help/index.tsx +1 -0
  258. package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
  259. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  260. package/src/plugins/builtin/holders/headless.ts +6 -1
  261. package/src/plugins/builtin/holders/pane.tsx +3 -0
  262. package/src/plugins/builtin/insider/index.tsx +4 -4
  263. package/src/plugins/builtin/iv-history/charts.tsx +21 -7
  264. package/src/plugins/builtin/iv-history/client.ts +12 -2
  265. package/src/plugins/builtin/iv-history/format.ts +33 -2
  266. package/src/plugins/builtin/iv-history/headless.ts +22 -10
  267. package/src/plugins/builtin/iv-history/index.tsx +3 -3
  268. package/src/plugins/builtin/iv-history/model.ts +61 -13
  269. package/src/plugins/builtin/iv-history/pane.tsx +11 -3
  270. package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
  271. package/src/plugins/builtin/jobs/pane.tsx +1 -1
  272. package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
  273. package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
  274. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  275. package/src/plugins/builtin/kelly-sizer/pane.tsx +109 -39
  276. package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
  277. package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
  278. package/src/plugins/builtin/layout-manager/index.tsx +9 -1
  279. package/src/plugins/builtin/macro-day/client.ts +24 -0
  280. package/src/plugins/builtin/macro-day/headless.ts +11 -8
  281. package/src/plugins/builtin/macro-day/index.tsx +3 -0
  282. package/src/plugins/builtin/macro-day/model.ts +11 -6
  283. package/src/plugins/builtin/macro-day/pane.tsx +12 -8
  284. package/src/plugins/builtin/macro-day/release-table.ts +40 -0
  285. package/src/plugins/builtin/macro-day/releases.ts +62 -44
  286. package/src/plugins/builtin/market-heatmap/data.ts +25 -5
  287. package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
  288. package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
  289. package/src/plugins/builtin/market-heatmap/model.ts +94 -3
  290. package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
  291. package/src/plugins/builtin/market-movers/headless.ts +31 -4
  292. package/src/plugins/builtin/market-movers/index.tsx +8 -1
  293. package/src/plugins/builtin/market-movers/model.ts +71 -17
  294. package/src/plugins/builtin/market-movers/table.ts +50 -23
  295. package/src/plugins/builtin/news/headless.ts +9 -2
  296. package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
  297. package/src/plugins/builtin/openfda/client.ts +1 -1
  298. package/src/plugins/builtin/options/carry.ts +95 -0
  299. package/src/plugins/builtin/options/index.tsx +58 -5
  300. package/src/plugins/builtin/options/strike-window.ts +183 -0
  301. package/src/plugins/builtin/options/table.ts +55 -3
  302. package/src/plugins/builtin/options/types.ts +8 -1
  303. package/src/plugins/builtin/options/view.tsx +161 -12
  304. package/src/plugins/builtin/options-positioning/headless.ts +4 -2
  305. package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
  306. package/src/plugins/builtin/options-scenario/client.ts +73 -17
  307. package/src/plugins/builtin/options-scenario/headless.ts +71 -24
  308. package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
  309. package/src/plugins/builtin/options-scenario/index.tsx +9 -2
  310. package/src/plugins/builtin/options-scenario/model.ts +33 -0
  311. package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
  312. package/src/plugins/builtin/pe-band/model.ts +2 -2
  313. package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
  314. package/src/plugins/builtin/perps/equity-row.tsx +8 -5
  315. package/src/plugins/builtin/perps/model.ts +16 -5
  316. package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
  317. package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
  318. package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
  319. package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
  320. package/src/plugins/builtin/plugin-module.ts +87 -4
  321. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
  322. package/src/plugins/builtin/portfolio-list/cli/render.ts +87 -18
  323. package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
  324. package/src/plugins/builtin/portfolio-list/headless.ts +38 -7
  325. package/src/plugins/builtin/realized-vol/headless.ts +3 -0
  326. package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
  327. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
  328. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
  329. package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
  330. package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
  331. package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
  332. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
  333. package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
  334. package/src/plugins/builtin/risk-factors/data.ts +23 -9
  335. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  336. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  337. package/src/plugins/builtin/seasonality/pane.tsx +1 -1
  338. package/src/plugins/builtin/sec/index.tsx +5 -5
  339. package/src/plugins/builtin/sectors/client.ts +10 -2
  340. package/src/plugins/builtin/sectors/headless.ts +35 -6
  341. package/src/plugins/builtin/sectors/index.tsx +18 -1
  342. package/src/plugins/builtin/sectors/sector-model.ts +150 -41
  343. package/src/plugins/builtin/shared/command-tab.ts +51 -0
  344. package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
  345. package/src/plugins/builtin/shared/members-menu.tsx +4 -4
  346. package/src/plugins/builtin/shared/report-freshness.ts +50 -2
  347. package/src/plugins/builtin/shared/ticker-request.ts +25 -1
  348. package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
  349. package/src/plugins/builtin/shared/volatility/options.ts +1 -46
  350. package/src/plugins/builtin/short-interest/index.tsx +19 -4
  351. package/src/plugins/builtin/short-interest/surface.tsx +4 -4
  352. package/src/plugins/builtin/short-volume/index.tsx +1 -10
  353. package/src/plugins/builtin/supply-chain/model.ts +1 -1
  354. package/src/plugins/builtin/ticker-detail/headless.ts +43 -21
  355. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
  356. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
  357. package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
  358. package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
  359. package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
  360. package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
  361. package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
  362. package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
  363. package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
  364. package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
  365. package/src/plugins/builtin/vol-surface/client.ts +6 -2
  366. package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
  367. package/src/plugins/builtin/vol-surface/headless.ts +124 -20
  368. package/src/plugins/builtin/vol-surface/model.ts +23 -11
  369. package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
  370. package/src/plugins/builtin/vol-surface/tables.ts +124 -0
  371. package/src/plugins/builtin/world-indices/client.ts +22 -4
  372. package/src/plugins/builtin/world-indices/headless.ts +54 -27
  373. package/src/plugins/builtin/world-indices/model.ts +4 -2
  374. package/src/plugins/builtin/world-indices/table.tsx +13 -2
  375. package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
  376. package/src/plugins/builtin/world-venue-map/index.ts +1 -1
  377. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  378. package/src/plugins/builtin-editorial.ts +126 -0
  379. package/src/plugins/catalog-backend.ts +16 -9
  380. package/src/plugins/catalog-browser.ts +127 -63
  381. package/src/plugins/catalog-manifest.ts +73 -40
  382. package/src/plugins/catalog-ui.ts +26 -6
  383. package/src/plugins/extracted-plugins.ts +7 -1
  384. package/src/plugins/ownership.ts +150 -7
  385. package/src/plugins/plugin-functions.ts +54 -0
  386. package/src/plugins/plugin-off.ts +36 -0
  387. package/src/plugins/registry/host-actions.ts +16 -0
  388. package/src/plugins/registry/index.ts +84 -24
  389. package/src/plugins/registry/plugin-state.ts +25 -1
  390. package/src/plugins/runtime/context.tsx +29 -0
  391. package/src/react/input.ts +48 -1
  392. package/src/remote/app-host.tsx +5 -0
  393. package/src/remote/controller.ts +3 -53
  394. package/src/remote/matching.ts +69 -0
  395. package/src/remote/schema.ts +1 -1
  396. package/src/remote/semantic-tree.tsx +14 -0
  397. package/src/remote/types.ts +2 -0
  398. package/src/renderers/dom/desktop/icons.tsx +14 -0
  399. package/src/renderers/dom/desktop/query-bar.tsx +59 -32
  400. package/src/renderers/dom/host/box.tsx +1 -1
  401. package/src/renderers/dom/host/drag-shield.ts +17 -6
  402. package/src/renderers/dom/host/live-frame.ts +10 -2
  403. package/src/renderers/dom/input-host.tsx +6 -1
  404. package/src/renderers/dom/styles.css +13 -0
  405. package/src/renderers/electrobun/bun/desktop/backend.ts +5 -1
  406. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
  407. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -1
  408. package/src/renderers/electrobun/shared/protocol.ts +2 -0
  409. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  410. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
  411. package/src/renderers/electrobun/view/main.tsx +4 -1
  412. package/src/renderers/opentui/input-host.tsx +6 -1
  413. package/src/renderers/opentui/start.tsx +9 -0
  414. package/src/shares/portable-layout.ts +16 -6
  415. package/src/sources/gloomberb-cloud/index.ts +19 -10
  416. package/src/sources/gloomberb-cloud/normalizers.ts +4 -1
  417. package/src/sources/listing-symbols.ts +11 -5
  418. package/src/sources/provider-errors.ts +88 -3
  419. package/src/sources/provider-router/cached-routes.ts +8 -4
  420. package/src/sources/provider-router/financial-routes.ts +11 -5
  421. package/src/sources/provider-router/financials.ts +42 -15
  422. package/src/sources/provider-router/history.ts +27 -11
  423. package/src/sources/provider-router/index.ts +1 -1
  424. package/src/sources/provider-router/primary.ts +23 -5
  425. package/src/sources/sec-edgar.ts +68 -8
  426. package/src/sync/core-contributors.ts +4 -3
  427. package/src/test-support/cli-context.ts +2 -1
  428. package/src/test-support/data-provider.ts +34 -0
  429. package/src/test-support/shell-pane-keys.tsx +1 -0
  430. package/src/theme/series-colors.ts +46 -0
  431. package/src/theme/themes.ts +149 -0
  432. package/src/tickers/search/index.ts +22 -2
  433. package/src/tickers/search/ranking.ts +45 -1
  434. package/src/tickers/search/types.ts +3 -1
  435. package/src/time-series/resolve.ts +1 -14
  436. package/src/types/config.ts +61 -1
  437. package/src/types/headless.ts +11 -0
  438. package/src/types/instrument.ts +8 -0
  439. package/src/types/plugin.ts +49 -3
  440. package/src/ui/host.tsx +8 -4
  441. package/src/ui/index.tsx +2 -0
  442. package/src/utils/cli-output.ts +14 -1
  443. package/src/utils/exchanges.ts +13 -0
  444. package/src/utils/format.ts +2 -1
  445. package/src/utils/fx-market-hours.ts +34 -0
  446. package/src/utils/option-expiry.ts +126 -0
  447. package/src/utils/price-history.ts +39 -6
  448. package/src/utils/rpc-timeout-error.ts +6 -2
  449. package/src/utils/sec-eps-basis.ts +159 -30
  450. package/src/utils/sec.ts +38 -34
  451. package/src/utils/throttled-fetch.ts +51 -22
@@ -1,5 +1,10 @@
1
+ import type { FunctionAccess } from "../cli/pane-functions/function-help";
2
+ import type { GloomPlugin, PluginTarget } from "../types/plugin";
1
3
  import { ABSORBED_PLUGINS } from "./absorbed";
4
+ import { highlightedCodes, pluginFunctions } from "./plugin-functions";
5
+ import { BUILTIN_EDITORIAL } from "./builtin-editorial";
2
6
  import { getPluginCatalog } from "./catalog";
7
+ import { browserBuiltinPlugins } from "./catalog-browser";
3
8
 
4
9
  /**
5
10
  * The public description of the plugins that ship inside Gloomberb.
@@ -8,71 +13,80 @@ import { getPluginCatalog } from "./catalog";
8
13
  * built-in has no repository to read metadata from. Hand-maintaining that half
9
14
  * elsewhere meant it silently drifted every time a built-in changed its panes
10
15
  * or description, so it is derived from the live catalog here instead and
11
- * checked against a committed snapshot in CI.
16
+ * checked against a committed snapshot in CI. Editorial copy the catalog does
17
+ * not carry lives in `builtin-editorial.ts`.
18
+ *
19
+ * Fields added since the first version are optional for the directory, which
20
+ * keeps reading a manifest without them.
12
21
  */
13
22
 
23
+ /** A function the plugin adds, as the directory lists it. */
24
+ interface BuiltinShortcutManifestEntry {
25
+ code: string;
26
+ name: string;
27
+ description: string;
28
+ /** Free gets only the upgrade (`pro`) or a preview (`preview`); absent when Free gets it all. */
29
+ access?: FunctionAccess;
30
+ /** Where it runs, when that is narrower than the plugin: a code the web app lacks. */
31
+ targets?: PluginTarget[];
32
+ }
33
+
14
34
  interface BuiltinPluginManifestEntry {
15
35
  id: string;
16
36
  name: string;
17
37
  description: string;
38
+ /** Ten words at most, for a card. */
39
+ tagline?: string;
18
40
  /** Curated, since the catalog has no notion of categories. */
19
41
  categories: string[];
20
42
  toggleable: boolean;
21
43
  featured?: true;
22
44
  /** Path to artwork in this repo, resolved by the directory against HEAD. */
23
45
  icon?: string;
46
+ /** Everywhere the app runs, and the web when the browser catalog carries it. */
47
+ targets: PluginTarget[];
24
48
  contributes: {
25
49
  panes: string[];
26
50
  capabilities: string[];
27
51
  broker: boolean;
52
+ shortcuts?: BuiltinShortcutManifestEntry[];
28
53
  };
54
+ /** The codes a card shows. */
55
+ highlights?: string[];
56
+ /** Codes whose reviewed capture the directory shows, best first. */
57
+ screenshots?: string[];
29
58
  }
30
59
 
31
- /**
32
- * Editorial metadata the runtime catalog does not carry. Anything missing here
33
- * is reported by `generate-plugin-manifest.ts` rather than silently defaulted,
34
- * so a new built-in cannot slip into the directory uncategorised.
35
- */
36
- const EDITORIAL: Record<
37
- string,
38
- { categories: string[]; featured?: true; icon?: string }
39
- > = {
40
- "gloomberb-cloud": {
41
- categories: ["data", "cloud"],
42
- featured: true,
43
- icon: "plugin-icons/gloomberb-cloud.webp",
44
- },
45
- alerts: { categories: ["alerts"] },
46
- application: { categories: ["core"] },
47
- broker: { categories: ["broker"] },
48
- "clinical-trials": { categories: ["research"] },
49
- "comment-letters": { categories: ["research"] },
50
- "custom-view": { categories: ["data", "productivity"] },
51
- debug: { categories: ["developer"] },
52
- "fear-greed": { categories: ["markets", "sentiment"], icon: "plugin-icons/fear-greed.webp" },
53
- members: { categories: ["markets", "research"] },
54
- "ipo-calendar": { categories: ["macro", "markets"], icon: "plugin-icons/ipo-calendar.webp" },
55
- macro: { categories: ["macro"] },
56
- "market-halts": { categories: ["markets"], icon: "plugin-icons/market-halts.webp" },
57
- "market-heatmap": { categories: ["markets"], icon: "plugin-icons/market-heatmap.webp" },
58
- "market-overview": { categories: ["markets"] },
59
- news: { categories: ["news"] },
60
- notes: { categories: ["productivity"] },
61
- openfda: { categories: ["research"] },
62
- portfolio: { categories: ["portfolio"] },
63
- "research-search": { categories: ["research", "news"] },
64
- "ticker-research": { categories: ["research"] },
65
- };
66
-
67
60
  export interface BuiltinManifest {
68
61
  version: 1;
69
62
  plugins: BuiltinPluginManifestEntry[];
70
63
  /** Built-in ids with no editorial entry; a non-empty list fails generation. */
71
64
  uncategorised: string[];
65
+ /** Editorial highlights or screenshots naming a code the plugin lacks, as `id:CODE`; these fail generation too. */
66
+ unknownCodes: string[];
67
+ }
68
+
69
+ const NATIVE_TARGETS: PluginTarget[] = ["cli", "tui", "desktop"];
70
+
71
+ /**
72
+ * The codes a plugin's pane templates answer to. Commands a plugin registers
73
+ * while it sets up (ALRT, NOTE) are not known until it runs, so they are not
74
+ * listed; the Plugins pane reads them from the running app.
75
+ */
76
+ function functionsOf(plugin: GloomPlugin) {
77
+ return pluginFunctions({
78
+ templates: (plugin.paneTemplates ?? []).map((template) => ({
79
+ label: template.label,
80
+ prefix: template.shortcut?.prefix,
81
+ description: template.description,
82
+ })),
83
+ });
72
84
  }
73
85
 
74
86
  export function buildBuiltinManifest(): BuiltinManifest {
75
87
  const uncategorised: string[] = [];
88
+ const unknownCodes: string[] = [];
89
+ const web = new Map(browserBuiltinPlugins.map((plugin) => [plugin.id, plugin]));
76
90
 
77
91
  const plugins = getPluginCatalog()
78
92
  // Built in again, but released apps from before that still install these
@@ -80,18 +94,34 @@ export function buildBuiltinManifest(): BuiltinManifest {
80
94
  // id a built-in claims. This build shows that row as its built-in, so they
81
95
  // join the manifest once those releases have updated.
82
96
  .filter(({ plugin }) => !ABSORBED_PLUGINS.some((absorbed) => absorbed.id === plugin.id))
83
- .map(({ plugin }) => {
84
- const editorial = EDITORIAL[plugin.id];
97
+ .map(({ plugin }): BuiltinPluginManifestEntry => {
98
+ const editorial = BUILTIN_EDITORIAL[plugin.id];
85
99
  if (!editorial) uncategorised.push(plugin.id);
86
100
 
101
+ // Some built-ins carry fewer functions in the web app.
102
+ const webPlugin = web.get(plugin.id);
103
+ const webCodes = new Set(webPlugin ? functionsOf(webPlugin).map((fn) => fn.code) : []);
104
+ const functions = functionsOf(plugin);
105
+ const shortcuts = functions.map((fn): BuiltinShortcutManifestEntry => ({
106
+ ...fn,
107
+ ...(webPlugin && !webCodes.has(fn.code) ? { targets: [...NATIVE_TARGETS] } : {}),
108
+ }));
109
+ const codes = new Set(functions.map((fn) => fn.code));
110
+ for (const code of [...editorial?.highlights ?? [], ...editorial?.screenshots ?? []]) {
111
+ if (!codes.has(code)) unknownCodes.push(`${plugin.id}:${code}`);
112
+ }
113
+ const screenshots = (editorial?.screenshots ?? []).filter((code) => codes.has(code));
114
+
87
115
  return {
88
116
  id: plugin.id,
89
117
  name: plugin.name,
90
118
  description: plugin.description ?? "",
119
+ ...(editorial?.tagline ? { tagline: editorial.tagline } : {}),
91
120
  categories: editorial?.categories ?? [],
92
121
  toggleable: plugin.toggleable === true,
93
122
  ...(editorial?.featured ? { featured: true as const } : {}),
94
123
  ...(editorial?.icon ? { icon: editorial.icon } : {}),
124
+ targets: webPlugin ? [...NATIVE_TARGETS, "web"] : [...NATIVE_TARGETS],
95
125
  contributes: {
96
126
  panes: (plugin.panes ?? []).map((pane) => pane.id).sort(),
97
127
  capabilities: (plugin.capabilities ?? [])
@@ -101,10 +131,13 @@ export function buildBuiltinManifest(): BuiltinManifest {
101
131
  })
102
132
  .sort(),
103
133
  broker: !!plugin.broker,
134
+ ...(shortcuts.length > 0 ? { shortcuts } : {}),
104
135
  },
136
+ ...(functions.length > 0 ? { highlights: highlightedCodes(plugin.id, functions) } : {}),
137
+ ...(screenshots.length > 0 ? { screenshots } : {}),
105
138
  };
106
139
  })
107
140
  .sort((left, right) => left.id.localeCompare(right.id));
108
141
 
109
- return { version: 1, plugins, uncategorised: uncategorised.sort() };
142
+ return { version: 1, plugins, uncategorised: uncategorised.sort(), unknownCodes };
110
143
  }
@@ -15,24 +15,42 @@ import { clinicalTrialsPlugin } from "./builtin/clinical-trials";
15
15
  import { commentLettersPlugin } from "./builtin/comment-letters";
16
16
  import { openFdaPlugin } from "./builtin/openfda";
17
17
  import {
18
+ altDataPlugin,
18
19
  applicationPlugin,
19
20
  brokerPlugin,
20
- macroPlugin,
21
- marketOverviewPlugin,
21
+ creditPlugin,
22
+ cryptoPlugin,
23
+ earningsPlugin,
24
+ filingsPlugin,
25
+ futuresCommoditiesPlugin,
26
+ globalMarketsPlugin,
27
+ optionsVolatilityPlugin,
28
+ ownershipPlugin,
22
29
  portfolioPlugin,
23
- tickerResearchPlugin,
30
+ quantPlugin,
31
+ ratesMacroPlugin,
32
+ screenersPlugin,
33
+ tickerCorePlugin,
24
34
  } from "./builtin/composite-plugins";
25
35
 
26
36
  export const uiBuiltinPlugins: GloomPlugin[] = [
27
37
  gloomberbCloudPlugin,
28
38
  portfolioPlugin,
29
- tickerResearchPlugin,
39
+ tickerCorePlugin,
40
+ optionsVolatilityPlugin,
41
+ ownershipPlugin,
42
+ filingsPlugin,
30
43
  brokerPlugin,
31
44
  applicationPlugin,
32
45
  newsPlugin,
33
46
  notesPlugin,
34
47
  customViewPlugin,
35
- marketOverviewPlugin,
48
+ globalMarketsPlugin,
49
+ screenersPlugin,
50
+ futuresCommoditiesPlugin,
51
+ cryptoPlugin,
52
+ altDataPlugin,
53
+ quantPlugin,
36
54
  marketHeatmapPlugin,
37
55
  marketHaltsPlugin,
38
56
  fearGreedPlugin,
@@ -41,7 +59,9 @@ export const uiBuiltinPlugins: GloomPlugin[] = [
41
59
  clinicalTrialsPlugin,
42
60
  commentLettersPlugin,
43
61
  openFdaPlugin,
44
- macroPlugin,
62
+ ratesMacroPlugin,
63
+ creditPlugin,
64
+ earningsPlugin,
45
65
  alertsPlugin,
46
66
  researchSearchPlugin,
47
67
  ];
@@ -4,6 +4,7 @@ import { join } from "path";
4
4
  import type { AppConfig } from "../types/config";
5
5
  import { debugLog } from "../utils/debug-log";
6
6
  import { getPluginsDir } from "./loader";
7
+ import { encodeBuiltinDisabledPluginIds } from "./ownership";
7
8
  import { pluginDirectoryNames } from "./plugin-names";
8
9
 
9
10
  const log = debugLog.createLogger("plugin-seed");
@@ -63,7 +64,12 @@ export async function seedExtractedPlugins(
63
64
  pluginsDir: string = getPluginsDir(),
64
65
  ): Promise<SeedResult> {
65
66
  const alreadySeeded = new Set(config.seededPlugins ?? []);
66
- const disabled = new Set(config.disabledPlugins ?? []);
67
+ // A previous owner that is now a group of built-ins counts as off only while
68
+ // all of them are, which is when its id is saved.
69
+ const disabled = new Set([
70
+ ...config.disabledPlugins ?? [],
71
+ ...encodeBuiltinDisabledPluginIds(config.disabledPlugins ?? []),
72
+ ]);
67
73
 
68
74
  const result: SeedResult = { installed: [], failed: [], seeded: [...alreadySeeded] };
69
75
 
@@ -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 () => {},