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,13 +1,19 @@
1
- import { useCallback, useEffect, useMemo, useState } from "react";
1
+ import { useCallback, useEffect, useMemo, useRef, useState, type RefObject } from "react";
2
2
 
3
3
  import {
4
4
  Button,
5
+ ChoiceDialog,
5
6
  confirmDialog,
7
+ DetailScrollBody,
6
8
  DataTableStackView,
7
9
  EmptyState,
8
10
  KeyValueRow,
11
+ openUrl,
9
12
  PaneStatusBody,
10
13
  QueryBar,
14
+ RemoteImage,
15
+ Section,
16
+ SectionHeading,
11
17
  useExternalLinkFooter,
12
18
  useQueryBarSearch,
13
19
  type DataTableCell,
@@ -18,20 +24,33 @@ import {
18
24
  import { loadingErrorFooterInfo } from "../../../components/data-table/table-pane";
19
25
  import { colors } from "../../../theme/colors";
20
26
  import type { PaneProps } from "../../../types/plugin";
21
- import { Box, ScrollBox, Text, TextAttributes } from "../../../ui";
22
- import { useDialog } from "../../../ui/dialog";
27
+ import { Box, ScrollBox, Text, TextAttributes, useUiCapabilities, type ScrollBoxRenderable } from "../../../ui";
28
+ import { useDialog, type PromptContext } from "../../../ui/dialog";
23
29
  import { isPlainKeyboardEvent } from "../../../utils/keyboard";
24
30
  import { formatRelativeAge } from "../../../utils/datetime-format";
25
31
  import { requiredGloomberb } from "../../../utils/semver";
26
32
  import { VERSION } from "../../../version";
27
33
  import { getCurrentPluginTarget, runsExternalPlugins } from "../../current-target";
28
34
  import { pluginSetupCommandId } from "../../registry/setup-command";
35
+ import { useAppSelector } from "../../../state/app/context";
36
+ import { BUILTIN_EDITORIAL } from "../../builtin-editorial";
29
37
  import { usePluginAppActions, usePluginPaneState } from "../../runtime";
30
38
  import { DEBUG_LOG_TEMPLATE_ID } from "../debug/template";
31
39
  import { loadRegistry, registryPluginUrl } from "./feed";
40
+ import { highlightedCodes, pluginFunctions, proFunctionCount, type PluginFunction } from "../../plugin-functions";
41
+ import {
42
+ matchingPack,
43
+ packChange,
44
+ packToggles,
45
+ restoreToggles,
46
+ STARTER_PACKS,
47
+ type PackChange,
48
+ type StarterPack,
49
+ } from "./packs";
32
50
  import {
33
51
  buildRows,
34
52
  collectCategories,
53
+ CORE_PLUGIN_ID,
35
54
  filterEntries,
36
55
  hasUpdate,
37
56
  installConsent,
@@ -54,7 +73,7 @@ import { getMarketplaceHost, getPluginManager, type MarketplaceHost } from "./st
54
73
 
55
74
  import { PLUGIN_MARKETPLACE_PANE_ID } from "./ids";
56
75
 
57
- type Column = DataTableColumn & { id: "name" | "tagline" | "version" | "status" };
76
+ type Column = DataTableColumn & { id: "name" | "tagline" | "panes" | "functions" | "pro" | "status" };
58
77
 
59
78
  const ALL_CATEGORIES = "all";
60
79
  /** Category ids are lowercase words; these read wrong title-cased. */
@@ -64,17 +83,61 @@ function categoryLabel(category: string): string {
64
83
  return CATEGORY_LABELS[category] ?? category.charAt(0).toUpperCase() + category.slice(1);
65
84
  }
66
85
 
67
- function buildColumns(width: number): Column[] {
68
- const nameWidth = Math.min(24, Math.max(14, Math.floor(width * 0.22)));
69
- // The description takes what the other columns leave.
86
+ /**
87
+ * A row reads as a name, a short line and three chips: how many panes, the
88
+ * codes that open them, and how many of its functions Pro unlocks. Narrow
89
+ * panes give up the Pro count, then the codes. Versions are in the detail;
90
+ * a waiting update shows in the status.
91
+ */
92
+ function buildColumns(width: number, fit: { functions: number; status: number }): Column[] {
93
+ const nameWidth = Math.min(23, Math.max(14, Math.floor(width * 0.2)));
94
+ // The description takes what the other columns leave; the chips take what they need.
70
95
  return [
71
96
  { id: "name", label: "PLUGIN", width: nameWidth, align: "left" },
72
97
  { id: "tagline", label: "DESCRIPTION", width: 16, align: "left", flexGrow: 1 },
73
- { id: "version", label: "VERSION", width: 16, align: "right" },
74
- { id: "status", label: "STATUS", width: 14, align: "left" },
98
+ { id: "panes", label: "PANES", width: 5, align: "right" },
99
+ ...(width >= 80 ? [{ id: "functions" as const, label: "FUNCTIONS", width: fit.functions, align: "left" as const }] : []),
100
+ ...(width >= 96 ? [{ id: "pro" as const, label: "PRO", width: 3, align: "right" as const }] : []),
101
+ { id: "status", label: "STATUS", width: fit.status, align: "left" },
75
102
  ];
76
103
  }
77
104
 
105
+ /** What a row's chips say about a plugin. */
106
+ interface EntryFacts {
107
+ panes: number;
108
+ paneNames: string[];
109
+ functions: PluginFunction[];
110
+ /** At most three codes, for the row. */
111
+ codes: string[];
112
+ /** Functions Free cannot fully open. */
113
+ pro: number;
114
+ }
115
+
116
+ /**
117
+ * Read from what the plugin registered when it is loaded here, so a built-in
118
+ * reads the way this build has it; from what the feed says otherwise.
119
+ */
120
+ function entryFacts(entry: MarketplaceEntry, host: MarketplaceHost | null): EntryFacts {
121
+ const live = entry.installed && host && !entry.loadError ? host.contributions(entry.id) : null;
122
+ const functions = live
123
+ ? pluginFunctions({ templates: live.templates, commands: live.commands })
124
+ : (entry.contributes?.shortcuts ?? []).map((shortcut) => ({
125
+ code: shortcut.code,
126
+ name: shortcut.name,
127
+ description: shortcut.description,
128
+ ...(shortcut.access ? { access: shortcut.access } : {}),
129
+ }));
130
+ return {
131
+ panes: live ? live.panes.length : entry.contributes?.panes.length ?? 0,
132
+ paneNames: live ? live.panes.map((pane) => pane.name) : [],
133
+ functions,
134
+ codes: entry.bundled ? highlightedCodes(entry.id, functions) : functions.slice(0, 3).map((fn) => fn.code),
135
+ pro: proFunctionCount(functions),
136
+ };
137
+ }
138
+
139
+ const ACCESS_LABELS = { pro: "Pro", preview: "Pro, free preview" } as const;
140
+
78
141
  const STATUS_COLORS: Record<MarketplaceStatusKind, string> = {
79
142
  failed: colors.negative,
80
143
  "needs-restart": colors.warning,
@@ -83,8 +146,9 @@ const STATUS_COLORS: Record<MarketplaceStatusKind, string> = {
83
146
  update: colors.textBright,
84
147
  "needs-gloomberb": colors.warning,
85
148
  errors: colors.warning,
86
- enabled: colors.positive,
87
- disabled: colors.textDim,
149
+ // On is the normal state, so it stays quiet and what is off stands out.
150
+ enabled: colors.textDim,
151
+ disabled: colors.textBright,
88
152
  none: colors.textDim,
89
153
  };
90
154
 
@@ -105,12 +169,23 @@ function renderCell(
105
169
  column: Column,
106
170
  rowState: { selected: boolean },
107
171
  busyId: string | null,
172
+ facts: (entry: MarketplaceEntry) => EntryFacts,
108
173
  ): DataTableCell {
109
174
  if (row.type === "header") return { text: "" };
110
175
  const { entry } = row;
111
176
  const selected = rowState.selected ? colors.selectedText : undefined;
112
177
 
113
178
  switch (column.id) {
179
+ case "panes": {
180
+ const panes = facts(entry).panes;
181
+ return { text: panes > 0 ? String(panes) : "", color: selected ?? colors.textDim };
182
+ }
183
+ case "functions":
184
+ return { text: facts(entry).codes.join(" "), color: selected ?? colors.text };
185
+ case "pro": {
186
+ const pro = facts(entry).pro;
187
+ return { text: pro > 0 ? String(pro) : "", color: selected ?? colors.warning };
188
+ }
114
189
  case "name":
115
190
  return {
116
191
  text: entry.name,
@@ -119,10 +194,6 @@ function renderCell(
119
194
  };
120
195
  case "tagline":
121
196
  return { text: entry.tagline, color: selected ?? colors.textDim };
122
- case "version": {
123
- const update = hasUpdate(entry);
124
- return { text: versionLabel(entry), color: selected ?? (update ? colors.textBright : colors.textDim) };
125
- }
126
197
  case "status": {
127
198
  if (busyId === entry.id) return { text: "working", color: selected ?? colors.textDim };
128
199
  const status = statusOf(entry);
@@ -206,6 +277,123 @@ function EntryDetail({ entry, width, host }: { entry: MarketplaceEntry; width: n
206
277
  );
207
278
  }
208
279
 
280
+ /** gloom.sh keeps a reviewed capture of these functions. */
281
+ function functionShotUrl(code: string): string {
282
+ return `https://gloom.sh/screenshots/fn/${encodeURIComponent(code)}.png`;
283
+ }
284
+
285
+ /**
286
+ * A built-in: what it adds and what Pro opens, function by function. The
287
+ * desktop shows a capture or two; the terminal leaves them to gloom.sh.
288
+ */
289
+ function BuiltinDetail({ entry, width, facts, scrollRef }: {
290
+ entry: MarketplaceEntry;
291
+ width: number;
292
+ facts: EntryFacts;
293
+ scrollRef: RefObject<ScrollBoxRenderable | null>;
294
+ }) {
295
+ const { nativePaneChrome } = useUiCapabilities();
296
+ // DetailScrollBody pads a cell on each side, and its scrollbar takes one.
297
+ const rowWidth = Math.max(1, width - 4);
298
+ const nameWidth = Math.max(12, Math.min(34, rowWidth - 8 - 20));
299
+ const status = statusOf(entry);
300
+ const shots = nativePaneChrome ? (BUILTIN_EDITORIAL[entry.id]?.screenshots ?? []).slice(0, 2) : [];
301
+ const shotWidth = Math.min(rowWidth, 72);
302
+ const summary = [
303
+ `${facts.panes} pane${facts.panes === 1 ? "" : "s"}`,
304
+ `${facts.functions.length} function${facts.functions.length === 1 ? "" : "s"}`,
305
+ ...(facts.pro > 0 ? [`${facts.pro} Pro`] : []),
306
+ ];
307
+
308
+ return (
309
+ <DetailScrollBody ref={scrollRef} resetScrollKey={entry.id}>
310
+ <Box flexDirection="row" gap={2} height={1}>
311
+ <Text fg={STATUS_COLORS[status.kind]}>{status.text}</Text>
312
+ <Text fg={colors.textDim}>{summary.join(" · ")}</Text>
313
+ </Box>
314
+
315
+ {entry.description ? (
316
+ <Box paddingTop={1} flexDirection="column">
317
+ <Text fg={colors.text}>{entry.description}</Text>
318
+ </Box>
319
+ ) : null}
320
+
321
+ {shots.length > 0 ? (
322
+ <Box paddingTop={1} flexDirection="column" gap={1}>
323
+ {shots.map((code) => (
324
+ <RemoteImage
325
+ key={code}
326
+ src={functionShotUrl(code)}
327
+ alt={`${code} in Gloomberb`}
328
+ label={code}
329
+ width={shotWidth}
330
+ height={Math.max(8, Math.floor(shotWidth * 0.3))}
331
+ />
332
+ ))}
333
+ </Box>
334
+ ) : null}
335
+
336
+ {facts.functions.length > 0 ? (
337
+ <Section title="Functions" width={rowWidth}>
338
+ {facts.functions.map((fn) => (
339
+ // The code is what people type, so it leads; Pro reads in the PRO column's colour.
340
+ <Box key={fn.code} flexDirection="row" height={1} width={rowWidth} overflow="hidden">
341
+ <Box width={8} flexShrink={0}><Text fg={colors.textBright}>{fn.code}</Text></Box>
342
+ <Box width={nameWidth} flexShrink={0} overflow="hidden"><Text fg={colors.text}>{fn.name}</Text></Box>
343
+ {fn.access ? <Box flexShrink={1} minWidth={0} overflow="hidden"><Text fg={colors.warning}>{ACCESS_LABELS[fn.access]}</Text></Box> : null}
344
+ </Box>
345
+ ))}
346
+ </Section>
347
+ ) : null}
348
+
349
+ {facts.paneNames.length > 0 ? (
350
+ <Box paddingTop={1} flexDirection="column">
351
+ <SectionHeading title="Panes" width={rowWidth} />
352
+ <Text fg={colors.textDim}>{facts.paneNames.join(", ")}</Text>
353
+ </Box>
354
+ ) : null}
355
+ </DetailScrollBody>
356
+ );
357
+ }
358
+
359
+ /** The strip's labels; the full names are in the menu, the confirmation and the toast. */
360
+ const PACK_SHORT_LABELS: Record<string, string> = {
361
+ everything: "Everything",
362
+ "equity-research": "Equity",
363
+ "options-desk": "Options",
364
+ "macro-rates": "Macro",
365
+ "credit-bonds": "Credit",
366
+ "alt-data-quant": "Alt data",
367
+ };
368
+ /** The strip's value when the plugins on match no pack. */
369
+ const CUSTOM_PACK = "custom";
370
+
371
+ /** One line per kind of change, with the plugins it names. */
372
+ function PackChangeBody({ change, name, width }: { change: PackChange; name: (id: string) => string; width: number }) {
373
+ const lines = [
374
+ { label: "Turn off", ids: change.turnOff },
375
+ { label: "Turn on", ids: change.turnOn },
376
+ { label: "Keep", ids: change.keep },
377
+ ].filter((line) => line.ids.length > 0);
378
+ return (
379
+ <Box flexDirection="column" width={width} gap={1}>
380
+ {lines.map((line) => (
381
+ <Box key={line.label} flexDirection="row" width={width}>
382
+ <Box width={12} flexShrink={0}>
383
+ <Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{`${line.label} ${line.ids.length}`}</Text>
384
+ </Box>
385
+ <Box flexGrow={1} flexShrink={1} minWidth={0}>
386
+ <Text fg={colors.text} wrapText>{line.ids.map(name).join(", ")}</Text>
387
+ </Box>
388
+ </Box>
389
+ ))}
390
+ {change.turnOff.length > 0 ? (
391
+ <Text fg={colors.textDim} wrapText>Panes are hidden, not closed. Undo brings them back.</Text>
392
+ ) : null}
393
+ </Box>
394
+ );
395
+ }
396
+
209
397
  type Busy = { id: string; verb: "installing" | "updating" | "removing" } | null;
210
398
 
211
399
  export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
@@ -213,10 +401,12 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
213
401
  const { createPaneFromTemplate, showPane, openPluginCommandWorkflow, notify } = usePluginAppActions();
214
402
  const [query, setQuery] = useState("");
215
403
  const [category, setCategory] = usePluginPaneState<string | null>("category", null);
216
- const [showBuiltin, setShowBuiltin] = useState(false);
404
+ // Built-ins are what Gloomberb is made of, so they show unless filtered out.
405
+ const [showBuiltin, setShowBuiltin] = usePluginPaneState<boolean>("showBuiltin", true);
217
406
  const [selectedId, setSelectedId] = usePluginPaneState<string | null>("selectedId", null);
218
407
  const [detailOpen, setDetailOpen] = usePluginPaneState<boolean>("detailOpen", false);
219
408
  const { active: searchFocused, focus: focusSearch, blur: blurSearch, searchProps } = useQueryBarSearch();
409
+ const detailScrollRef = useRef<ScrollBoxRenderable | null>(null);
220
410
 
221
411
  const [registry, setRegistry] = useState<RegistryPlugin[]>([]);
222
412
  const [status, setStatus] = useState<"loading" | "ready" | "error">("loading");
@@ -231,12 +421,6 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
231
421
  const [remoteHeads, setRemoteHeads] = useState<Record<string, string>>({});
232
422
  const [checkingRemotes, setCheckingRemotes] = useState(false);
233
423
  const busyId = busy?.id ?? null;
234
- const renderRow = useCallback((
235
- row: MarketplaceRow,
236
- column: Column,
237
- _index: number,
238
- rowState: { selected: boolean },
239
- ) => renderCell(row, column, rowState, busyId), [busyId]);
240
424
 
241
425
  const refresh = useCallback((force: boolean) => {
242
426
  setStatus((current) => (current === "ready" ? current : "loading"));
@@ -253,11 +437,32 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
253
437
  const host = getMarketplaceHost();
254
438
  const manager = getPluginManager();
255
439
  const target = getCurrentPluginTarget();
440
+ // A switch from anywhere (a pack, a toast, a sync) re-reads the list.
441
+ const disabledPlugins = useAppSelector((state) => state.config.disabledPlugins);
256
442
  const entries = useMemo(() => {
257
443
  void localRevision;
444
+ void disabledPlugins;
258
445
  const installed = host?.listInstalled() ?? [];
259
446
  return sortEntries(mergeCatalog({ registry, installed, target, remoteHeads }));
260
- }, [host, registry, target, localRevision, remoteHeads]);
447
+ }, [host, registry, target, localRevision, remoteHeads, disabledPlugins]);
448
+ const currentPack = useMemo(() => matchingPack(disabledPlugins), [disabledPlugins]);
449
+ // Rebuilt with the entries, which a switch or an install already refreshes.
450
+ const factsFor = useMemo(() => {
451
+ const cache = new Map<string, EntryFacts>();
452
+ return (entry: MarketplaceEntry): EntryFacts => {
453
+ const cached = cache.get(entry.id);
454
+ if (cached) return cached;
455
+ const facts = entryFacts(entry, host);
456
+ cache.set(entry.id, facts);
457
+ return facts;
458
+ };
459
+ }, [entries, host]);
460
+ const renderRow = useCallback((
461
+ row: MarketplaceRow,
462
+ column: Column,
463
+ _index: number,
464
+ rowState: { selected: boolean },
465
+ ) => renderCell(row, column, rowState, busyId, factsFor), [busyId, factsFor]);
261
466
 
262
467
  // Keyed by the folders themselves: the entries array is rebuilt whenever an
263
468
  // answer lands, and re-running the check on its own result would never stop.
@@ -442,11 +647,84 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
442
647
  }
443
648
  }, [bump, busy, confirmWidth, dialog, host, manager, notify, selected]);
444
649
 
445
- const toggleSelected = useCallback(() => {
650
+ const toggleSelected = useCallback(async () => {
446
651
  if (!host || !selected || !selected.installed || !selected.toggleable || selected.loadError) return;
447
- host.setPluginEnabled(selected.id, !selected.enabled);
652
+ const entry = selected;
653
+ if (entry.id === CORE_PLUGIN_ID && entry.bundled && entry.enabled) {
654
+ const confirmed = await confirmDialog(dialog, {
655
+ title: `Turn off ${entry.name}?`,
656
+ body: ["DES, G and the research pane stop working until you turn it back on."],
657
+ confirmLabel: "Turn off",
658
+ width: confirmWidth,
659
+ });
660
+ if (!confirmed) return;
661
+ }
662
+ host.setPluginEnabled(entry.id, !entry.enabled);
663
+ bump();
664
+ }, [bump, confirmWidth, dialog, host, selected]);
665
+
666
+ const pluginName = useCallback(
667
+ (id: string) => entries.find((entry) => entry.id === id)?.name ?? id,
668
+ [entries],
669
+ );
670
+
671
+ /**
672
+ * Says exactly what the pack turns off and on, applies it in one switch,
673
+ * and offers Undo: nothing is closed, so the old set comes back as it was.
674
+ */
675
+ const applyPack = useCallback(async (pack: StarterPack) => {
676
+ if (!host) return;
677
+ const previous = [...disabledPlugins];
678
+ const change = packChange(previous, pack);
679
+ const toggles = packToggles(change);
680
+ if (Object.keys(toggles).length === 0) {
681
+ notify({ body: `${pack.name} is already what you have on.`, type: "info" });
682
+ return;
683
+ }
684
+ const confirmed = await confirmDialog(dialog, {
685
+ title: `Switch to ${pack.name}?`,
686
+ body: <PackChangeBody change={change} name={pluginName} width={confirmWidth} />,
687
+ confirmLabel: "Switch",
688
+ confirmVariant: "primary",
689
+ width: confirmWidth,
690
+ });
691
+ if (!confirmed) return;
692
+ host.setPluginsEnabled(toggles);
448
693
  bump();
449
- }, [bump, host, selected]);
694
+ const parts = [
695
+ ...(change.turnOff.length > 0 ? [`${change.turnOff.length} off`] : []),
696
+ ...(change.turnOn.length > 0 ? [`${change.turnOn.length} back on`] : []),
697
+ ];
698
+ notify({
699
+ body: `${pack.name}: ${parts.join(", ")}.`,
700
+ type: "success",
701
+ // Long enough to reach for Undo after looking at the list.
702
+ duration: 12_000,
703
+ action: {
704
+ label: "Undo",
705
+ onClick: () => {
706
+ host.setPluginsEnabled(restoreToggles(previous));
707
+ bump();
708
+ },
709
+ },
710
+ });
711
+ }, [bump, confirmWidth, dialog, disabledPlugins, host, notify, pluginName]);
712
+
713
+ const choosePack = useCallback(async () => {
714
+ const id = await dialog.prompt<string>({
715
+ closeOnClickOutside: true,
716
+ content: (context: PromptContext<string>) => (
717
+ <ChoiceDialog
718
+ {...context}
719
+ title="Starter packs"
720
+ selectedChoiceId={currentPack?.id}
721
+ choices={STARTER_PACKS.map((pack) => ({ id: pack.id, label: pack.name, description: pack.tagline }))}
722
+ />
723
+ ),
724
+ }).catch(() => null);
725
+ const pack = STARTER_PACKS.find((candidate) => candidate.id === id);
726
+ if (pack) await applyPack(pack);
727
+ }, [applyPack, currentPack, dialog]);
450
728
 
451
729
  const setupSelected = useCallback(() => {
452
730
  if (!selected || !selected.installed || !selected.hasSetup || !selected.enabled) return;
@@ -484,7 +762,8 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
484
762
  // Not u: that installs an app update whenever one is waiting.
485
763
  case "g": void updateSelected(); return true;
486
764
  case "x": void removeSelected(); return true;
487
- case "e": toggleSelected(); return true;
765
+ case "e": void toggleSelected(); return true;
766
+ case "a": void choosePack(); return true;
488
767
  case "s": setupSelected(); return true;
489
768
  case "p": openSelected(); return true;
490
769
  case "d": openLog(); return true;
@@ -492,7 +771,7 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
492
771
  case "b": setShowBuiltin((value) => !value); return true;
493
772
  default: return false;
494
773
  }
495
- }, [installSelected, openLog, openSelected, refresh, removeSelected, setupSelected, toggleSelected, updateSelected]);
774
+ }, [choosePack, installSelected, openLog, openSelected, refresh, removeSelected, setShowBuiltin, setupSelected, toggleSelected, updateSelected]);
496
775
 
497
776
  /**
498
777
  * Pane keys go through the table's key handler, which runs while the pane
@@ -542,11 +821,15 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
542
821
  ? { id: "update", key: "g", label: " reload", title: "Reload", onPress: () => { void updateSelected(); } }
543
822
  : { id: "update", key: "g", label: "et update", onPress: () => { void updateSelected(); } });
544
823
  }
545
- if (canToggle) hints.push({ id: "toggle", key: "e", label: selected?.enabled ? "disable" : "nable", title: selected?.enabled ? "Disable" : "Enable", onPress: toggleSelected });
824
+ if (canToggle) hints.push({ id: "toggle", key: "e", label: selected?.enabled ? "disable" : "nable", title: selected?.enabled ? "Disable" : "Enable", onPress: () => { void toggleSelected(); } });
546
825
  if (canSetup) hints.push({ id: "setup", key: "s", label: "etup", onPress: setupSelected });
547
826
  if (canOpen) hints.push({ id: "open-pane", key: "p", label: "ane", onPress: openSelected });
548
827
  if (canLog) hints.push({ id: "log", key: "d", label: "ebug log", onPress: openLog });
549
828
  if (canRemove) hints.push({ id: "remove", key: "x", label: " remove", onPress: () => { void removeSelected(); } });
829
+ if (host) hints.push({ id: "packs", key: "a", label: " packs", title: "Starter packs", onPress: () => { void choosePack(); } });
830
+ // A built-in has no repository; its page on gloom.sh shows it at work.
831
+ const seeItUrl = selected?.bundled ? registryPluginUrl(selected.id) : null;
832
+ if (seeItUrl) hints.push({ id: "see-it", key: "o", label: " see it", title: "See it on gloom.sh", onPress: () => openUrl(seeItUrl) });
550
833
 
551
834
  useExternalLinkFooter({
552
835
  registrationId: PLUGIN_MARKETPLACE_PANE_ID,
@@ -557,7 +840,15 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
557
840
  hints,
558
841
  });
559
842
 
560
- const columns = useMemo(() => buildColumns(width), [width]);
843
+ const fit = useMemo(() => ({
844
+ functions: Math.min(16, Math.max(9, ...visible.map((entry) => factsFor(entry).codes.join(" ").length))),
845
+ status: Math.min(14, Math.max(7, ...visible.map((entry) => statusOf(entry).text.length))),
846
+ }), [factsFor, visible]);
847
+ const columns = useMemo(() => buildColumns(width, fit), [fit, width]);
848
+ const packOptions = useMemo(() => [
849
+ ...STARTER_PACKS.map((pack) => ({ value: pack.id, label: pack.name, short: PACK_SHORT_LABELS[pack.id], description: pack.tagline })),
850
+ ...(currentPack ? [] : [{ value: CUSTOM_PACK, label: "Custom", description: "Your own set of plugins.", disabled: true }]),
851
+ ], [currentPack]);
561
852
  // The pick is saved with the pane, so keep it listed (and resettable) even
562
853
  // when the catalog no longer has plugins in it.
563
854
  const categoryOptions = useMemo(
@@ -599,10 +890,30 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
599
890
  <DataTableStackView<MarketplaceRow, Column>
600
891
  focused={focused && !searchFocused}
601
892
  detailOpen={detailOpen && !!selected}
893
+ detailScrollRef={selected?.bundled ? detailScrollRef : undefined}
602
894
  onBack={() => setDetailOpen(false)}
603
- detailContent={selected ? <EntryDetail entry={selected} width={width} host={host} /> : null}
895
+ detailContent={selected
896
+ ? selected.bundled
897
+ ? <BuiltinDetail entry={selected} width={width} facts={factsFor(selected)} scrollRef={detailScrollRef} />
898
+ : <EntryDetail entry={selected} width={width} host={host} />
899
+ : null}
604
900
  detailTitle={selected?.name}
605
901
  rootBefore={(
902
+ <Box flexDirection="column" flexShrink={0}>
903
+ <QueryBar
904
+ width={width}
905
+ filters={[{
906
+ id: "pack",
907
+ label: "Starter pack",
908
+ inline: true,
909
+ value: currentPack?.id ?? CUSTOM_PACK,
910
+ options: packOptions,
911
+ onChange: (value: string) => {
912
+ const pack = STARTER_PACKS.find((candidate) => candidate.id === value);
913
+ if (pack) void applyPack(pack);
914
+ },
915
+ }]}
916
+ />
606
917
  <QueryBar
607
918
  width={width}
608
919
  search={{
@@ -621,9 +932,10 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
621
932
  options: categoryOptions,
622
933
  onChange: (value: string) => setCategory(value === ALL_CATEGORIES ? null : value),
623
934
  }] : []),
624
- { kind: "toggle" as const, id: "builtin", label: "Built in", value: showBuiltin, onChange: setShowBuiltin },
935
+ { kind: "toggle" as const, id: "builtin", label: "Built in", value: showBuiltin, defaultValue: true, onChange: setShowBuiltin },
625
936
  ]}
626
937
  />
938
+ </Box>
627
939
  )}
628
940
  selection={{
629
941
  kind: "id",
@@ -14,6 +14,12 @@ import type { InstalledPlugin } from "./model";
14
14
  export interface MarketplaceHost {
15
15
  listInstalled(): InstalledPlugin[];
16
16
  setPluginEnabled(pluginId: string, enabled: boolean): void;
17
+ /**
18
+ * Switches several plugins as one change, the way a starter pack does. A
19
+ * switched-off plugin's panes are hidden, not closed, so switching it back
20
+ * on shows them where they were.
21
+ */
22
+ setPluginsEnabled(changes: Readonly<Record<string, boolean>>): void;
17
23
  /**
18
24
  * Registers a plugin that was loaded after startup, so its panes and
19
25
  * commands exist in this session. Replaces a previous registration of the
@@ -37,7 +43,7 @@ export interface MarketplaceHost {
37
43
  interface PluginContributions {
38
44
  panes: Array<{ id: string; name: string }>;
39
45
  templates: Array<{ id: string; label: string; prefix?: string }>;
40
- commands: Array<{ id: string; label: string }>;
46
+ commands: Array<{ id: string; label: string; shortcut?: string }>;
41
47
  capabilities: number;
42
48
  broker: boolean;
43
49
  }