gloomberb 0.11.2 → 0.12.0

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 (531) hide show
  1. package/README.md +7 -4
  2. package/README.zh-CN.md +4 -2
  3. package/package.json +28 -1
  4. package/src/api-client/auth.ts +10 -0
  5. package/src/api-client/data.ts +108 -1
  6. package/src/api-client/index.test.ts +127 -0
  7. package/src/api-client/index.ts +134 -1
  8. package/src/api-client/normalizers.ts +39 -0
  9. package/src/api-client/paths.ts +85 -1
  10. package/src/api-client/types.ts +220 -0
  11. package/src/app/pane-runtime/index.tsx +15 -12
  12. package/src/app/pane-runtime/layout-placement.test.ts +27 -0
  13. package/src/app/pane-runtime/layout-placement.ts +13 -0
  14. package/src/app/pane-runtime/plugin-bindings.ts +62 -0
  15. package/src/app.tsx +60 -18
  16. package/src/architecture/browser-entry-graph.test.ts +96 -0
  17. package/src/brokers/profile-form.test.ts +25 -27
  18. package/src/brokers/test-broker.ts +68 -0
  19. package/src/capabilities/chart-series.ts +3 -2
  20. package/src/cli/commands/market.ts +2 -2
  21. package/src/cli/commands/plugins.ts +39 -11
  22. package/src/cli/desktop-pane-shot.ts +285 -16
  23. package/src/cli/index.ts +1 -1
  24. package/src/cli/pane-functions/capabilities.ts +61 -31
  25. package/src/cli/pane-functions/catalog.ts +16 -4
  26. package/src/cli/pane-functions/discovery.ts +1 -0
  27. package/src/cli/pane-functions/headless.test.ts +247 -0
  28. package/src/cli/pane-functions/headless.ts +423 -0
  29. package/src/cli/pane-functions/index.test.ts +9 -0
  30. package/src/cli/pane-functions/index.ts +6 -2
  31. package/src/cli/pane-functions/options.ts +27 -2
  32. package/src/cli/pane-functions/report.ts +4 -0
  33. package/src/cli/pane-functions/resolver.ts +26 -8
  34. package/src/cli/pane-functions/screenshot.test.ts +76 -0
  35. package/src/cli/pane-functions/screenshot.ts +236 -20
  36. package/src/cli/registry.ts +0 -22
  37. package/src/cli/restore-plugins.ts +34 -0
  38. package/src/cli/result.ts +7 -10
  39. package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
  40. package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
  41. package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
  42. package/src/components/chart/composite/composite-chart.test.tsx +68 -20
  43. package/src/components/chart/composite/composite-chart.tsx +301 -204
  44. package/src/components/chart/composite/format.ts +8 -2
  45. package/src/components/chart/composite/interactions.test.ts +68 -102
  46. package/src/components/chart/composite/interactions.ts +214 -264
  47. package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
  48. package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
  49. package/src/components/chart/composite/scene.ts +57 -3
  50. package/src/components/chart/composite/text-renderer.ts +3 -2
  51. package/src/components/chart/composite/time-scale.ts +138 -57
  52. package/src/components/chart/composite/types.ts +25 -1
  53. package/src/components/chart/core/data.ts +9 -13
  54. package/src/components/chart/core/date-window.ts +7 -1
  55. package/src/components/chart/core/pointer.ts +3 -0
  56. package/src/components/chart/core/types.ts +0 -45
  57. package/src/components/chart/native/chart-rasterizer.ts +0 -1
  58. package/src/components/chart/native/kitty/index.test.ts +0 -188
  59. package/src/components/chart/native/raster/crosshair.ts +6 -5
  60. package/src/components/chart/native/raster/primitives.ts +0 -23
  61. package/src/components/chart/native/renderer-selection.ts +81 -31
  62. package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
  63. package/src/components/chart/static/chart-surface.tsx +150 -0
  64. package/src/components/chart/static/index.ts +8 -0
  65. package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
  66. package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
  67. package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
  68. package/src/components/chart/static/series.ts +49 -0
  69. package/src/components/command-bar/action-runtime.ts +1 -3
  70. package/src/components/command-bar/assist/model.test.ts +17 -2
  71. package/src/components/command-bar/assist/model.ts +40 -9
  72. package/src/components/command-bar/commands/direct/index.ts +1 -1
  73. package/src/components/command-bar/commands/direct/runtime.ts +1 -1
  74. package/src/components/command-bar/commands/registry.ts +0 -11
  75. package/src/components/command-bar/helpers.test.ts +2 -2
  76. package/src/components/command-bar/helpers.ts +2 -6
  77. package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
  78. package/src/components/command-bar/list/badge.test.ts +54 -0
  79. package/src/components/command-bar/list/badge.ts +62 -0
  80. package/src/components/command-bar/list/model.test.ts +103 -0
  81. package/src/components/command-bar/list/model.ts +61 -4
  82. package/src/components/command-bar/list/view.test.tsx +113 -0
  83. package/src/components/command-bar/list/view.tsx +147 -131
  84. package/src/components/command-bar/pane-templates/workflow.ts +1 -1
  85. package/src/components/command-bar/panel/body.tsx +51 -65
  86. package/src/components/command-bar/panel/index.tsx +48 -13
  87. package/src/components/command-bar/panel/layout.test.ts +139 -0
  88. package/src/components/command-bar/panel/layout.ts +65 -28
  89. package/src/components/command-bar/panel/native-surface.ts +24 -0
  90. package/src/components/command-bar/panel/prompt-binding.ts +54 -0
  91. package/src/components/command-bar/panel/runtime.ts +6 -3
  92. package/src/components/command-bar/panel/state.ts +41 -7
  93. package/src/components/command-bar/panel/types.ts +7 -1
  94. package/src/components/command-bar/routes/root/provider-search.ts +34 -15
  95. package/src/components/command-bar/routes/root/results.test.ts +161 -0
  96. package/src/components/command-bar/routes/root/results.ts +30 -28
  97. package/src/components/command-bar/routes/root/runtime.ts +37 -20
  98. package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
  99. package/src/components/command-bar/routes/root/search-providers.ts +205 -0
  100. package/src/components/command-bar/routes/root/selection.ts +1 -12
  101. package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
  102. package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
  103. package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
  104. package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
  105. package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
  106. package/src/components/command-bar/routing/actions.ts +1 -81
  107. package/src/components/command-bar/routing/launch-request.ts +1 -1
  108. package/src/components/command-bar/routing/list-state.ts +6 -21
  109. package/src/components/command-bar/selection-runtime.ts +1 -1
  110. package/src/components/command-bar/surface/assist.test.tsx +14 -10
  111. package/src/components/command-bar/surface/environment.ts +8 -1
  112. package/src/components/command-bar/surface/index.test.tsx +84 -68
  113. package/src/components/command-bar/surface/index.tsx +33 -36
  114. package/src/components/command-bar/surface/test-harness.tsx +15 -3
  115. package/src/components/command-bar/theme-picker.tsx +43 -15
  116. package/src/components/command-bar/view-model.test.ts +53 -8
  117. package/src/components/command-bar/view-model.ts +43 -14
  118. package/src/components/command-bar/workflow/tickers.ts +1 -0
  119. package/src/components/command-bar/workflow/types.ts +1 -1
  120. package/src/components/data-table/export.test.ts +21 -0
  121. package/src/components/data-table/export.ts +21 -0
  122. package/src/components/data-table/view.test.tsx +45 -6
  123. package/src/components/data-table/view.tsx +55 -9
  124. package/src/components/index.ts +10 -2
  125. package/src/components/input-search-bar.test.tsx +36 -2
  126. package/src/components/input-search-bar.tsx +18 -1
  127. package/src/components/layout/header.test.tsx +57 -10
  128. package/src/components/layout/header.tsx +292 -149
  129. package/src/components/layout/market-summary.test.ts +81 -0
  130. package/src/components/layout/market-summary.ts +109 -0
  131. package/src/components/layout/shell/chrome.ts +96 -1
  132. package/src/components/layout/shell/index.tsx +11 -3
  133. package/src/components/layout/shell/menu.ts +9 -0
  134. package/src/components/layout/shell/pane/actions.ts +29 -0
  135. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  136. package/src/components/layout/shell/shortcuts.ts +3 -0
  137. package/src/components/layout/status-bar.test.tsx +9 -8
  138. package/src/components/layout/status-bar.tsx +85 -54
  139. package/src/components/onboarding/onboarding-frame.tsx +2 -26
  140. package/src/components/price-sparkline/index.ts +61 -34
  141. package/src/components/price-sparkline/view.tsx +1 -1
  142. package/src/components/sign-in-gate.test.tsx +128 -0
  143. package/src/components/sign-in-gate.tsx +149 -0
  144. package/src/components/speedometer-gauge/desktop.tsx +2 -12
  145. package/src/components/ticker/badge/list.tsx +17 -3
  146. package/src/components/ticker/list-table-view.tsx +6 -3
  147. package/src/components/ui/data-table/index.tsx +22 -4
  148. package/src/components/ui/data-table/opentui/index.tsx +165 -120
  149. package/src/components/ui/frame.tsx +28 -0
  150. package/src/components/ui/index.ts +3 -0
  151. package/src/components/ui/native-select.tsx +20 -10
  152. package/src/components/ui/select-button.tsx +138 -0
  153. package/src/components/ui/status.tsx +2 -2
  154. package/src/components/ui/table-layout.ts +1 -1
  155. package/src/components/use-double-click-activation.ts +7 -3
  156. package/src/core/app-services.ts +3 -3
  157. package/src/data/config/chart-settings.ts +4 -4
  158. package/src/data/config/layout.ts +12 -6
  159. package/src/data/config/store/index.test.ts +97 -0
  160. package/src/data/config/store/migrations.ts +41 -0
  161. package/src/data/config/store/normalize.ts +5 -3
  162. package/src/i18n/es.ts +6 -2
  163. package/src/i18n/ja.ts +6 -2
  164. package/src/i18n/ko.ts +6 -2
  165. package/src/i18n/zh-cn.ts +6 -2
  166. package/src/i18n/zh-tw.ts +6 -2
  167. package/src/layout-marketplace/gallery-terminal.tsx +126 -96
  168. package/src/layout-marketplace/gallery.test.tsx +65 -1
  169. package/src/layout-marketplace/pane-imagery.ts +1 -0
  170. package/src/layout-marketplace/payload.test.ts +17 -0
  171. package/src/layout-marketplace/payload.ts +40 -5
  172. package/src/market-data/coordinator/index.ts +3 -2
  173. package/src/market-data/coordinator/quotes.ts +173 -66
  174. package/src/market-data/coordinator/subscriptions.test.ts +215 -37
  175. package/src/pane-settings.ts +1 -1
  176. package/src/plugins/builtin/analytics/pane-model.ts +1 -1
  177. package/src/plugins/builtin/analytics/view.tsx +1 -1
  178. package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
  179. package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
  180. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
  181. package/src/plugins/builtin/chart-composer/cli-options.ts +12 -9
  182. package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
  183. package/src/plugins/builtin/chart-composer/settings.ts +2 -13
  184. package/src/plugins/builtin/chat/content/composer.tsx +8 -4
  185. package/src/plugins/builtin/cloud/auth-dialog.tsx +14 -228
  186. package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
  187. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  188. package/src/plugins/builtin/composite-plugins.ts +16 -1
  189. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  190. package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
  191. package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
  192. package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
  193. package/src/plugins/builtin/credit-conditions/index.tsx +2 -21
  194. package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
  195. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  196. package/src/plugins/builtin/earnings/index.tsx +1 -0
  197. package/src/plugins/builtin/earnings-calls/data.ts +223 -0
  198. package/src/plugins/builtin/earnings-calls/format.ts +49 -0
  199. package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
  200. package/src/plugins/builtin/earnings-calls/pane.tsx +756 -0
  201. package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
  202. package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
  203. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +334 -0
  204. package/src/plugins/builtin/econ/detail-view.tsx +1 -2
  205. package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
  206. package/src/plugins/builtin/econ/index.tsx +3 -1
  207. package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
  208. package/src/plugins/builtin/econ-statistics/client.ts +92 -0
  209. package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
  210. package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
  211. package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
  212. package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
  213. package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
  214. package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
  215. package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
  216. package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
  217. package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
  218. package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
  219. package/src/plugins/builtin/econ-statistics/view.ts +90 -0
  220. package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
  221. package/src/plugins/builtin/fear-greed/data.ts +6 -1
  222. package/src/plugins/builtin/futures/index.tsx +1 -0
  223. package/src/plugins/builtin/fx-matrix/index.tsx +1 -0
  224. package/src/plugins/builtin/help/index.tsx +4 -0
  225. package/src/plugins/builtin/holders/index.tsx +1 -0
  226. package/src/plugins/builtin/insider/index.tsx +1 -0
  227. package/src/plugins/builtin/ipo-calendar/index.tsx +1 -0
  228. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  229. package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
  230. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  231. package/src/plugins/builtin/market-movers/index.tsx +1 -0
  232. package/src/plugins/builtin/market-valuation/align.ts +122 -0
  233. package/src/plugins/builtin/market-valuation/cache.ts +32 -0
  234. package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
  235. package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
  236. package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
  237. package/src/plugins/builtin/market-valuation/client.ts +116 -0
  238. package/src/plugins/builtin/market-valuation/defs.ts +251 -0
  239. package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
  240. package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
  241. package/src/plugins/builtin/market-valuation/headless.ts +170 -0
  242. package/src/plugins/builtin/market-valuation/index.tsx +64 -0
  243. package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
  244. package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
  245. package/src/plugins/builtin/market-valuation/model.ts +8 -0
  246. package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
  247. package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
  248. package/src/plugins/builtin/market-valuation/series.ts +12 -0
  249. package/src/plugins/builtin/market-valuation/settings.ts +38 -0
  250. package/src/plugins/builtin/market-valuation/sources.ts +130 -0
  251. package/src/plugins/builtin/market-valuation/trend.ts +89 -0
  252. package/src/plugins/builtin/market-valuation/view.ts +137 -0
  253. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
  254. package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
  255. package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
  256. package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
  257. package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
  258. package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
  259. package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
  260. package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
  261. package/src/plugins/builtin/news/wire/index.tsx +31 -44
  262. package/src/plugins/builtin/options/analytics.test.ts +83 -0
  263. package/src/plugins/builtin/options/analytics.ts +110 -0
  264. package/src/plugins/builtin/options/index.tsx +18 -2
  265. package/src/plugins/builtin/options/table.ts +121 -51
  266. package/src/plugins/builtin/options/types.ts +22 -15
  267. package/src/plugins/builtin/options/view.test.tsx +53 -1
  268. package/src/plugins/builtin/options/view.tsx +92 -41
  269. package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
  270. package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
  271. package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
  272. package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
  273. package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
  274. package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
  275. package/src/plugins/builtin/plugin-module.test.ts +2 -4
  276. package/src/plugins/builtin/plugin-module.ts +9 -8
  277. package/src/plugins/builtin/portfolio-list/index.tsx +1 -0
  278. package/src/plugins/builtin/research/index.tsx +4 -0
  279. package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
  280. package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
  281. package/src/plugins/builtin/research-search/data.ts +109 -0
  282. package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
  283. package/src/plugins/builtin/research-search/document-view.tsx +254 -0
  284. package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
  285. package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
  286. package/src/plugins/builtin/research-search/index.tsx +85 -0
  287. package/src/plugins/builtin/research-search/model.test.ts +40 -0
  288. package/src/plugins/builtin/research-search/model.ts +291 -0
  289. package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
  290. package/src/plugins/builtin/research-search/pane.tsx +678 -0
  291. package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
  292. package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
  293. package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
  294. package/src/plugins/builtin/research-search/snippet.ts +134 -0
  295. package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
  296. package/src/plugins/builtin/scanner/flow-model.ts +29 -1
  297. package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
  298. package/src/plugins/builtin/scanner/index.ts +2 -0
  299. package/src/plugins/builtin/sec/index.tsx +1 -0
  300. package/src/plugins/builtin/sectors/index.tsx +1 -0
  301. package/src/plugins/builtin/shared/series-cache.ts +93 -0
  302. package/src/plugins/builtin/short-interest/index.tsx +1 -0
  303. package/src/plugins/builtin/short-interest/pane.tsx +1 -1
  304. package/src/plugins/builtin/thirteenf/index.tsx +1 -0
  305. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +3 -5
  306. package/src/plugins/builtin/ticker-detail/index.tsx +2 -14
  307. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
  308. package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
  309. package/src/plugins/builtin/volatility/index.tsx +1 -1
  310. package/src/plugins/builtin/world-indices/index.tsx +1 -0
  311. package/src/plugins/builtin/world-venue-map/index.ts +27 -0
  312. package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
  313. package/src/plugins/builtin/world-venue-map/map.tsx +481 -0
  314. package/src/plugins/builtin/world-venue-map/model.test.ts +85 -0
  315. package/src/plugins/builtin/world-venue-map/model.ts +268 -0
  316. package/src/plugins/builtin/world-venue-map/pane.tsx +300 -0
  317. package/src/plugins/builtin/world-venue-map/world-outlines.ts +2 -0
  318. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  319. package/src/plugins/bundle.test.ts +109 -0
  320. package/src/plugins/bundle.ts +139 -0
  321. package/src/plugins/catalog-backend.ts +6 -44
  322. package/src/plugins/catalog-browser.test.ts +1 -0
  323. package/src/plugins/catalog-browser.ts +12 -0
  324. package/src/plugins/catalog-manifest.test.ts +43 -0
  325. package/src/plugins/catalog-manifest.ts +92 -0
  326. package/src/plugins/catalog-ui.ts +20 -4
  327. package/src/plugins/catalog.ts +9 -2
  328. package/src/plugins/current-target.ts +28 -0
  329. package/src/plugins/host-contract.ts +37 -0
  330. package/src/plugins/host-link.ts +158 -0
  331. package/src/plugins/host-modules.ts +61 -0
  332. package/src/plugins/loader.ts +41 -22
  333. package/src/plugins/ownership.ts +1 -0
  334. package/src/plugins/prediction-markets/index.tsx +1 -0
  335. package/src/plugins/registry/context.ts +3 -0
  336. package/src/plugins/registry/contributions.ts +27 -0
  337. package/src/plugins/registry/index.test.ts +25 -0
  338. package/src/plugins/registry/index.ts +8 -0
  339. package/src/plugins/registry/pane-settings.test.ts +53 -0
  340. package/src/plugins/registry/pane-settings.ts +29 -3
  341. package/src/plugins/seed.test.ts +83 -0
  342. package/src/plugins/seed.ts +80 -0
  343. package/src/public/broker.ts +31 -0
  344. package/src/public/market-data.ts +9 -0
  345. package/src/public/pane-hooks.ts +57 -0
  346. package/src/public/public-api.test.ts +285 -0
  347. package/src/public/react.ts +62 -0
  348. package/src/public/test-support.ts +23 -0
  349. package/src/public/time-series.ts +8 -0
  350. package/src/public/utils.ts +77 -0
  351. package/src/remote/layout-helpers.ts +6 -8
  352. package/src/remote/resources.ts +2 -0
  353. package/src/remote/schema.ts +1 -0
  354. package/src/remote/semantic-tree.test.tsx +105 -0
  355. package/src/remote/semantic-tree.tsx +53 -35
  356. package/src/renderers/browser/main.tsx +6 -0
  357. package/src/renderers/browser/ui-host.tsx +11 -0
  358. package/src/renderers/cloudflare/worker.test.ts +19 -0
  359. package/src/renderers/cloudflare/worker.ts +1 -1
  360. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -2
  361. package/src/renderers/electrobun/bun/external-plugins.ts +140 -0
  362. package/src/renderers/electrobun/bun/index.ts +6 -2
  363. package/src/renderers/electrobun/shared/protocol.ts +25 -0
  364. package/src/renderers/electrobun/view/build-assets.ts +0 -1
  365. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +51 -1
  366. package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
  367. package/src/renderers/electrobun/view/external-plugins.ts +63 -0
  368. package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
  369. package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
  370. package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
  371. package/src/renderers/electrobun/view/host/mouse.ts +16 -2
  372. package/src/renderers/electrobun/view/main.tsx +27 -2
  373. package/src/renderers/electrobun/view/styles.css +5 -0
  374. package/src/renderers/electrobun/view/toast-host.tsx +18 -0
  375. package/src/renderers/electrobun/view/ui-host.tsx +3 -0
  376. package/src/renderers/opentui/chart-surface.tsx +5 -1
  377. package/src/renderers/opentui/host.tsx +20 -2
  378. package/src/renderers/opentui/interaction-performance.test.ts +79 -0
  379. package/src/renderers/opentui/interaction-performance.ts +188 -0
  380. package/src/renderers/opentui/start.tsx +26 -1
  381. package/src/renderers/opentui/test-utils.tsx +13 -0
  382. package/src/renderers/opentui/toast-host.tsx +26 -2
  383. package/src/renderers/opentui/ui-host.tsx +1 -0
  384. package/src/sources/provider-router/search.test.ts +191 -0
  385. package/src/sources/provider-router/search.ts +92 -39
  386. package/src/state/app/context/index.tsx +2 -0
  387. package/src/state/app/context/selector.test.tsx +33 -1
  388. package/src/state/hooks/quote-streaming.test.tsx +93 -0
  389. package/src/state/hooks/quote-streaming.ts +106 -30
  390. package/src/state/pane-table-export-registry.ts +50 -0
  391. package/src/theme/color-utils.ts +1 -1
  392. package/src/theme/colors.ts +50 -0
  393. package/src/theme/themes.test.ts +15 -2
  394. package/src/theme/themes.ts +19 -1
  395. package/src/tickers/search/index.test.ts +26 -0
  396. package/src/tickers/search/index.ts +42 -17
  397. package/src/tickers/search/ranking.ts +14 -0
  398. package/src/time-series/chart-data.test.ts +216 -0
  399. package/src/time-series/range.ts +12 -0
  400. package/src/time-series/resolution.test.ts +25 -26
  401. package/src/time-series/resolution.ts +44 -25
  402. package/src/time-series/resolve.ts +7 -0
  403. package/src/time-series/spec.ts +5 -5
  404. package/src/time-series/types.ts +3 -1
  405. package/src/time-series/use-chart-resolution.ts +5 -0
  406. package/src/types/config.ts +123 -3
  407. package/src/types/data-provider.ts +11 -0
  408. package/src/types/headless.ts +161 -0
  409. package/src/types/plugin.ts +104 -20
  410. package/src/ui/host.tsx +19 -1
  411. package/src/ui/index.tsx +1 -0
  412. package/src/ui/toast.tsx +8 -4
  413. package/src/utils/csv.test.ts +13 -0
  414. package/src/utils/csv.ts +37 -0
  415. package/src/utils/format.ts +35 -0
  416. package/src/utils/fuzzy-search.test.ts +94 -0
  417. package/src/utils/fuzzy-search.ts +81 -22
  418. package/src/utils/save-text-file.test.ts +24 -0
  419. package/src/utils/save-text-file.ts +36 -0
  420. package/src/utils/shortcut-labels.ts +14 -0
  421. package/src/components/chart/core/axis-format.ts +0 -157
  422. package/src/components/chart/core/index.test.ts +0 -1075
  423. package/src/components/chart/core/projection.ts +0 -357
  424. package/src/components/chart/core/render-utils.ts +0 -4
  425. package/src/components/chart/core/renderer.ts +0 -244
  426. package/src/components/chart/core/scene.ts +0 -180
  427. package/src/components/chart/core/time-axis.ts +0 -330
  428. package/src/components/chart/cursor-motion.ts +0 -47
  429. package/src/components/chart/indicators/types.ts +0 -26
  430. package/src/components/chart/market-session.ts +0 -176
  431. package/src/components/chart/multi-line/renderer.test.ts +0 -83
  432. package/src/components/chart/multi-line/renderer.ts +0 -161
  433. package/src/components/chart/multi-line/scene.ts +0 -188
  434. package/src/components/chart/native/raster/layout.ts +0 -129
  435. package/src/components/chart/native/raster/price-chart.ts +0 -244
  436. package/src/components/chart/rendering/chart-draw.ts +0 -270
  437. package/src/components/chart/rendering/geometry.ts +0 -95
  438. package/src/components/chart/rendering/pixel-buffer.ts +0 -267
  439. package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
  440. package/src/components/chart/scatter-chart-renderer.ts +0 -223
  441. package/src/components/chart/static/chart/surface.tsx +0 -358
  442. package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
  443. package/src/components/command-bar/routes/root/article-results.ts +0 -55
  444. package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
  445. package/src/plugins/builtin/news/wire/article-search.ts +0 -140
  446. package/src/plugins/builtin/substack/api/auth.ts +0 -185
  447. package/src/plugins/builtin/substack/api/cache.ts +0 -82
  448. package/src/plugins/builtin/substack/api/loaders.ts +0 -170
  449. package/src/plugins/builtin/substack/api/store.ts +0 -186
  450. package/src/plugins/builtin/substack/api/types.ts +0 -42
  451. package/src/plugins/builtin/substack/api.test.ts +0 -298
  452. package/src/plugins/builtin/substack/article-detail.tsx +0 -397
  453. package/src/plugins/builtin/substack/article-stack.tsx +0 -133
  454. package/src/plugins/builtin/substack/content.ts +0 -478
  455. package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
  456. package/src/plugins/builtin/substack/index.tsx +0 -49
  457. package/src/plugins/builtin/substack/login-view.tsx +0 -126
  458. package/src/plugins/builtin/substack/model.test.ts +0 -155
  459. package/src/plugins/builtin/substack/normalize.ts +0 -343
  460. package/src/plugins/builtin/substack/pane-footer.ts +0 -52
  461. package/src/plugins/builtin/substack/pane-state.ts +0 -170
  462. package/src/plugins/builtin/substack/pane.tsx +0 -547
  463. package/src/plugins/builtin/substack/read-state.ts +0 -30
  464. package/src/plugins/builtin/substack/table.ts +0 -111
  465. package/src/plugins/builtin/substack/types.ts +0 -71
  466. package/src/plugins/builtin/substack/utils.ts +0 -131
  467. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
  468. package/src/plugins/ibkr/account-cache.test.ts +0 -102
  469. package/src/plugins/ibkr/account-cache.ts +0 -24
  470. package/src/plugins/ibkr/broker-adapter.ts +0 -286
  471. package/src/plugins/ibkr/config.test.ts +0 -79
  472. package/src/plugins/ibkr/config.ts +0 -250
  473. package/src/plugins/ibkr/dialogs.tsx +0 -40
  474. package/src/plugins/ibkr/flex/index.test.ts +0 -264
  475. package/src/plugins/ibkr/flex/index.ts +0 -367
  476. package/src/plugins/ibkr/flex/statement-client.ts +0 -235
  477. package/src/plugins/ibkr/fundamental-parser.ts +0 -149
  478. package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
  479. package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
  480. package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
  481. package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
  482. package/src/plugins/ibkr/gateway/connection.ts +0 -152
  483. package/src/plugins/ibkr/gateway/contracts.ts +0 -132
  484. package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
  485. package/src/plugins/ibkr/gateway/helpers.ts +0 -33
  486. package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
  487. package/src/plugins/ibkr/gateway/history.ts +0 -120
  488. package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
  489. package/src/plugins/ibkr/gateway/market-data.ts +0 -345
  490. package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
  491. package/src/plugins/ibkr/gateway/orders.ts +0 -63
  492. package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
  493. package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
  494. package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
  495. package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
  496. package/src/plugins/ibkr/gateway/selection.ts +0 -51
  497. package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
  498. package/src/plugins/ibkr/gateway/service/index.ts +0 -429
  499. package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
  500. package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
  501. package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
  502. package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
  503. package/src/plugins/ibkr/gateway/service/native.ts +0 -543
  504. package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
  505. package/src/plugins/ibkr/gateway/types.ts +0 -27
  506. package/src/plugins/ibkr/index.test.tsx +0 -162
  507. package/src/plugins/ibkr/index.tsx +0 -159
  508. package/src/plugins/ibkr/instance-selection.test.ts +0 -120
  509. package/src/plugins/ibkr/instance-selection.ts +0 -36
  510. package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
  511. package/src/plugins/ibkr/portfolio-performance.ts +0 -129
  512. package/src/plugins/ibkr/trade/badge.tsx +0 -28
  513. package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
  514. package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
  515. package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
  516. package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
  517. package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
  518. package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
  519. package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
  520. package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
  521. package/src/plugins/ibkr/trade/tab/model.ts +0 -159
  522. package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
  523. package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
  524. package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
  525. package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
  526. package/src/plugins/ibkr/trade/utils.test.ts +0 -175
  527. package/src/plugins/ibkr/trade/utils.ts +0 -144
  528. package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
  529. package/src/plugins/ibkr/trading/pane.tsx +0 -332
  530. package/src/plugins/ibkr/trading/state.test.ts +0 -109
  531. package/src/plugins/ibkr/trading/state.ts +0 -335
@@ -1,16 +1,16 @@
1
1
  import type { GloomPlugin } from "../types/plugin";
2
+ import type { LoadedExternalPlugin } from "./loader";
2
3
  import { newsPlugin } from "./builtin/news";
3
4
  import { notesPlugin } from "./builtin/notes";
4
- import { substackPlugin } from "./builtin/substack";
5
5
  import { aiPlugin } from "./builtin/ai";
6
6
  import { gloomberbCloudPlugin } from "./builtin/cloud";
7
- import { ibkrPlugin } from "./ibkr";
8
7
  import { publicPlugin } from "./broker-sync/public";
9
8
  import { robinhoodPlugin } from "./broker-sync/robinhood";
10
9
  import { simpleFinPlugin } from "./broker-sync/simplefin";
11
10
  import { predictionMarketsPlugin } from "./prediction-markets";
12
11
  import { pollsPlugin } from "./builtin/polls";
13
12
  import { alertsPlugin } from "./builtin/alerts";
13
+ import { researchSearchPlugin } from "./builtin/research-search";
14
14
  import {
15
15
  applicationPlugin,
16
16
  brokerPlugin,
@@ -25,13 +25,11 @@ export const uiBuiltinPlugins: GloomPlugin[] = [
25
25
  portfolioPlugin,
26
26
  tickerResearchPlugin,
27
27
  brokerPlugin,
28
- ibkrPlugin,
29
28
  publicPlugin,
30
29
  robinhoodPlugin,
31
30
  simpleFinPlugin,
32
31
  applicationPlugin,
33
32
  newsPlugin,
34
- substackPlugin,
35
33
  notesPlugin,
36
34
  aiPlugin,
37
35
  predictionMarketsPlugin,
@@ -39,8 +37,26 @@ export const uiBuiltinPlugins: GloomPlugin[] = [
39
37
  marketOverviewPlugin,
40
38
  macroPlugin,
41
39
  alertsPlugin,
40
+ researchSearchPlugin,
42
41
  ];
43
42
 
44
43
  export function getRendererBuiltinPlugins(): GloomPlugin[] {
45
44
  return uiBuiltinPlugins;
46
45
  }
46
+
47
+ /**
48
+ * The plugin list for a UI renderer: the built-ins it ships with, plus any
49
+ * external plugins that loaded and support this renderer.
50
+ *
51
+ * Deliberately not `getLoadablePlugins`, which is the CLI catalog and also
52
+ * carries the Yahoo fallback provider and the debug plugin. Routing the desktop
53
+ * through it would quietly change which plugins the app runs.
54
+ */
55
+ export function getRendererPlugins(externalPlugins: readonly LoadedExternalPlugin[] = []): GloomPlugin[] {
56
+ return [
57
+ ...uiBuiltinPlugins,
58
+ ...externalPlugins
59
+ .filter((entry) => !entry.error && !entry.unsupportedTarget)
60
+ .map((entry) => entry.plugin),
61
+ ];
62
+ }
@@ -1,4 +1,4 @@
1
- import type { GloomPlugin } from "../types/plugin";
1
+ import type { GloomPlugin, PluginTarget } from "../types/plugin";
2
2
  import type { LoadedExternalPlugin } from "./loader";
3
3
  import { debugPlugin } from "./builtin/debug";
4
4
  import { yahooPlugin } from "./builtin/yahoo";
@@ -9,6 +9,12 @@ export interface PluginCatalogEntry {
9
9
  source: "builtin" | "external";
10
10
  path?: string;
11
11
  error?: string;
12
+ /**
13
+ * Set when the plugin installed cleanly but cannot run here — IBKR Gateway on
14
+ * the web build, for example. The catalog still lists it so the marketplace
15
+ * can explain why it is inert instead of leaving the user wondering.
16
+ */
17
+ unsupportedTarget?: PluginTarget;
12
18
  }
13
19
 
14
20
  const builtinPlugins: GloomPlugin[] = [
@@ -28,12 +34,13 @@ export function getPluginCatalog(externalPlugins: LoadedExternalPlugin[] = []):
28
34
  source: "external" as const,
29
35
  path: entry.path,
30
36
  error: entry.error,
37
+ unsupportedTarget: entry.unsupportedTarget,
31
38
  })),
32
39
  ];
33
40
  }
34
41
 
35
42
  export function getLoadablePlugins(externalPlugins: LoadedExternalPlugin[] = []): GloomPlugin[] {
36
43
  return getPluginCatalog(externalPlugins)
37
- .filter((entry) => !entry.error)
44
+ .filter((entry) => !entry.error && !entry.unsupportedTarget)
38
45
  .map((entry) => entry.plugin);
39
46
  }
@@ -0,0 +1,28 @@
1
+ import type { PluginTarget } from "../types/plugin";
2
+
3
+ /**
4
+ * Which renderer is running.
5
+ *
6
+ * Renderers declare this at startup rather than having it sniffed from globals:
7
+ * the Electrobun view and the hosted browser app are both browser contexts and
8
+ * are otherwise hard to tell apart, yet they differ in what a plugin can do
9
+ * (the desktop app can shell out to git; term.gloom.sh cannot).
10
+ */
11
+ let currentTarget: PluginTarget = "cli";
12
+
13
+ export function setCurrentPluginTarget(target: PluginTarget): void {
14
+ currentTarget = target;
15
+ }
16
+
17
+ export function getCurrentPluginTarget(): PluginTarget {
18
+ return currentTarget;
19
+ }
20
+
21
+ /**
22
+ * Whether this renderer can install plugins itself. Installing means cloning a
23
+ * repository and running `bun install`, so only the Bun-hosted renderers can;
24
+ * elsewhere the marketplace shows the command to run instead.
25
+ */
26
+ export function canInstallPlugins(): boolean {
27
+ return currentTarget === "cli" || currentTarget === "tui";
28
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The contract shared between the plugin bundler and the renderers that load
3
+ * its output.
4
+ *
5
+ * Kept in its own module with no imports on purpose. The bundler runs in Bun
6
+ * and reaches the filesystem; the renderers that consume it are browser
7
+ * contexts. Pulling these two constants from `bundle.ts` dragged the whole
8
+ * bundler — and through it `plugins/loader.ts`, which reads `process.env.HOME`
9
+ * at module scope — into the desktop view, where it threw on load.
10
+ */
11
+
12
+ export const PLUGIN_HOST_GLOBAL = "__GLOOM_PLUGIN_HOST__";
13
+
14
+ /**
15
+ * Specifiers a plugin may import that must resolve to the host's copy. Anything
16
+ * else is a plugin's own dependency and gets bundled normally.
17
+ *
18
+ * Deliberately no `react-dom`: it is a renderer package, and plugins are
19
+ * required to be renderer-neutral so the same code runs in the terminal. A
20
+ * plugin reaching for it should fail to bundle rather than quietly work on the
21
+ * desktop and break in the TUI.
22
+ */
23
+ export const SHARED_SPECIFIERS = [
24
+ "react",
25
+ "react/jsx-runtime",
26
+ "react/jsx-dev-runtime",
27
+ "gloomberb/types/plugin",
28
+ "gloomberb/types/persistence",
29
+ "gloomberb/ui",
30
+ "gloomberb/components",
31
+ "gloomberb/theme",
32
+ "gloomberb/capabilities",
33
+ "gloomberb/utils",
34
+ "gloomberb/react",
35
+ ] as const;
36
+
37
+ export type SharedSpecifier = (typeof SHARED_SPECIFIERS)[number];
@@ -0,0 +1,158 @@
1
+ import { existsSync, lstatSync, mkdirSync, readlinkSync, rmSync, symlinkSync } from "fs";
2
+ import { dirname, join, resolve } from "path";
3
+
4
+ /**
5
+ * External plugins live in `~/.gloomberb/plugins/<name>/`, outside any
6
+ * `node_modules` chain that could reach the running Gloomberb install. Left
7
+ * alone, `import { Box } from "gloomberb/ui"` does not resolve, and a plugin
8
+ * that lists `react` as a real dependency gets its *own* copy — two React
9
+ * instances in one process, which throws on the first hook.
10
+ *
11
+ * So the host links itself and its React into each plugin's `node_modules`.
12
+ * Plugins declare both as peer dependencies and never install them. This keeps
13
+ * one module instance per process and makes plugin imports resolve exactly the
14
+ * way they do for first-party code.
15
+ *
16
+ * Links are rebuilt after every install and update, because `bun install`
17
+ * prunes entries it does not know about, and repaired at load time so a plugin
18
+ * copied in by hand still works.
19
+ */
20
+
21
+ const LINKED_PACKAGES = ["gloomberb", "react", "react-dom"] as const;
22
+
23
+ let cachedHostRoot: string | null | undefined;
24
+
25
+ /** Walks up from this module to the directory holding the `gloomberb` package.json. */
26
+ export function findHostPackageRoot(startDir: string = import.meta.dir): string | null {
27
+ if (cachedHostRoot !== undefined && startDir === import.meta.dir) return cachedHostRoot;
28
+ let dir = resolve(startDir);
29
+ for (let depth = 0; depth < 12; depth += 1) {
30
+ const pkgPath = join(dir, "package.json");
31
+ if (existsSync(pkgPath)) {
32
+ try {
33
+ const pkg = JSON.parse(require("fs").readFileSync(pkgPath, "utf-8")) as { name?: string };
34
+ if (pkg.name === "gloomberb") {
35
+ if (startDir === import.meta.dir) cachedHostRoot = dir;
36
+ return dir;
37
+ }
38
+ } catch {
39
+ // Unreadable package.json — keep walking rather than giving up.
40
+ }
41
+ }
42
+ const parent = dirname(dir);
43
+ if (parent === dir) break;
44
+ dir = parent;
45
+ }
46
+ if (startDir === import.meta.dir) cachedHostRoot = null;
47
+ return null;
48
+ }
49
+
50
+ function linkTarget(hostRoot: string, pkg: string): string | null {
51
+ if (pkg === "gloomberb") return hostRoot;
52
+ const candidate = join(hostRoot, "node_modules", pkg);
53
+ return existsSync(candidate) ? candidate : null;
54
+ }
55
+
56
+ /** True when `path` is already a symlink pointing at `target`. */
57
+ function alreadyLinked(path: string, target: string): boolean {
58
+ try {
59
+ return lstatSync(path).isSymbolicLink() && resolve(dirname(path), readlinkSync(path)) === resolve(target);
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Links sibling plugins a plugin declares as peer dependencies.
67
+ *
68
+ * A plugin can legitimately extend another — IBKR Gateway builds on the Flex
69
+ * plugin, which owns the broker id and the shared bridge. Those live in separate
70
+ * install directories with no path between them, so the peer is linked the same
71
+ * way the host is. A missing sibling is not an error here: the plugin reports it
72
+ * at load, which is more useful than failing the install.
73
+ */
74
+ function linkPeerPlugins(pluginDir: string, pluginsDir: string): string[] {
75
+ const linked: string[] = [];
76
+ let peers: string[] = [];
77
+ try {
78
+ const pkg = JSON.parse(require("fs").readFileSync(join(pluginDir, "package.json"), "utf-8"));
79
+ peers = Object.keys(pkg.peerDependencies ?? {}).filter((name) => name.startsWith("gloomberb-"));
80
+ } catch {
81
+ return linked;
82
+ }
83
+
84
+ for (const peer of peers) {
85
+ // Installed directories are named after the repository, which is the package
86
+ // name for every plugin in the registry.
87
+ const target = join(pluginsDir, peer);
88
+ if (!existsSync(target)) continue;
89
+ const linkPath = join(pluginDir, "node_modules", peer);
90
+ if (alreadyLinked(linkPath, target)) {
91
+ linked.push(peer);
92
+ continue;
93
+ }
94
+ try {
95
+ mkdirSync(join(pluginDir, "node_modules"), { recursive: true });
96
+ if (existsSync(linkPath) || lstatSync(linkPath, { throwIfNoEntry: false })) {
97
+ rmSync(linkPath, { recursive: true, force: true });
98
+ }
99
+ symlinkSync(target, linkPath, "dir");
100
+ linked.push(peer);
101
+ } catch {
102
+ // Reported by the plugin's own load failure if it actually needed it.
103
+ }
104
+ }
105
+ return linked;
106
+ }
107
+
108
+ export interface HostLinkResult {
109
+ linked: string[];
110
+ skipped: string[];
111
+ error?: string;
112
+ }
113
+
114
+ /**
115
+ * Points `<pluginDir>/node_modules/{gloomberb,react,react-dom}` at the running
116
+ * install. Safe to call repeatedly.
117
+ */
118
+ export function linkHostPackages(
119
+ pluginDir: string,
120
+ hostRoot = findHostPackageRoot(),
121
+ pluginsDir = dirname(pluginDir),
122
+ ): HostLinkResult {
123
+ if (!hostRoot) return { linked: [], skipped: [...LINKED_PACKAGES], error: "Could not locate the Gloomberb install." };
124
+
125
+ const modulesDir = join(pluginDir, "node_modules");
126
+ const linked: string[] = [];
127
+ const skipped: string[] = [];
128
+
129
+ for (const pkg of LINKED_PACKAGES) {
130
+ const target = linkTarget(hostRoot, pkg);
131
+ if (!target) {
132
+ skipped.push(pkg);
133
+ continue;
134
+ }
135
+ const linkPath = join(modulesDir, pkg);
136
+ if (alreadyLinked(linkPath, target)) {
137
+ linked.push(pkg);
138
+ continue;
139
+ }
140
+ try {
141
+ mkdirSync(modulesDir, { recursive: true });
142
+ // A real directory here means `bun install` fetched a second copy; replacing
143
+ // it is the whole point, otherwise the plugin runs against a duplicate React.
144
+ if (existsSync(linkPath) || lstatSync(linkPath, { throwIfNoEntry: false })) {
145
+ rmSync(linkPath, { recursive: true, force: true });
146
+ }
147
+ symlinkSync(target, linkPath, "dir");
148
+ linked.push(pkg);
149
+ } catch (err) {
150
+ skipped.push(pkg);
151
+ if (pkg === "gloomberb") return { linked, skipped, error: String(err) };
152
+ }
153
+ }
154
+
155
+ linked.push(...linkPeerPlugins(pluginDir, pluginsDir));
156
+
157
+ return { linked, skipped };
158
+ }
@@ -0,0 +1,61 @@
1
+ import { PLUGIN_HOST_GLOBAL, SHARED_SPECIFIERS } from "./host-contract";
2
+
3
+ /**
4
+ * Publishes the host's shared modules for compiled plugin bundles to read.
5
+ *
6
+ * Browser-context renderers load plugins as separate ES modules, so the bundler
7
+ * rewrites their `react` and `gloomberb/*` imports to read from this registry
8
+ * (see `bundle.ts`). This must run before any plugin bundle is imported, and
9
+ * the modules here must be the same instances the app itself uses — importing
10
+ * them normally is what guarantees that.
11
+ */
12
+ export async function installPluginHostModules(): Promise<void> {
13
+ const globals = globalThis as Record<string, unknown>;
14
+ if (globals[PLUGIN_HOST_GLOBAL]) return;
15
+
16
+ const [
17
+ react,
18
+ jsxRuntime,
19
+ typesPlugin,
20
+ typesPersistence,
21
+ ui,
22
+ components,
23
+ theme,
24
+ capabilities,
25
+ utils,
26
+ pluginReact,
27
+ ] = await Promise.all([
28
+ import("react"),
29
+ import("react/jsx-runtime"),
30
+ // Type-only modules still need an entry: a plugin may import a runtime
31
+ // value from them, and a missing key throws a clearer error than undefined.
32
+ import("../types/plugin"),
33
+ import("../types/persistence"),
34
+ import("../ui"),
35
+ import("../components"),
36
+ import("../theme/colors"),
37
+ import("../capabilities"),
38
+ import("../public/utils"),
39
+ import("../public/react"),
40
+ ]);
41
+
42
+ const registry: Record<string, unknown> = {
43
+ "react": react,
44
+ "react/jsx-runtime": jsxRuntime,
45
+ "react/jsx-dev-runtime": jsxRuntime,
46
+ "gloomberb/types/plugin": typesPlugin,
47
+ "gloomberb/types/persistence": typesPersistence,
48
+ "gloomberb/ui": ui,
49
+ "gloomberb/components": components,
50
+ "gloomberb/theme": theme,
51
+ "gloomberb/capabilities": capabilities,
52
+ "gloomberb/utils": utils,
53
+ "gloomberb/react": pluginReact,
54
+ };
55
+
56
+ for (const specifier of SHARED_SPECIFIERS) {
57
+ if (!registry[specifier]) throw new Error(`Plugin host registry is missing "${specifier}"`);
58
+ }
59
+
60
+ globals[PLUGIN_HOST_GLOBAL] = registry;
61
+ }
@@ -2,8 +2,9 @@ import { readdir } from "fs/promises";
2
2
  import { join } from "path";
3
3
  import { existsSync } from "fs";
4
4
  import { homedir } from "os";
5
- import type { GloomPlugin } from "../types/plugin";
5
+ import type { GloomPlugin, PluginTarget } from "../types/plugin";
6
6
  import { debugLog } from "../utils/debug-log";
7
+ import { linkHostPackages } from "./host-link";
7
8
 
8
9
  const loaderLog = debugLog.createLogger("plugin-loader");
9
10
 
@@ -13,13 +14,41 @@ export interface LoadedExternalPlugin {
13
14
  plugin: GloomPlugin;
14
15
  path: string;
15
16
  error?: string;
17
+ /** Set when the plugin loaded but does not support the running renderer. */
18
+ unsupportedTarget?: PluginTarget;
16
19
  }
17
20
 
18
21
  export function getPluginsDir(): string {
19
22
  return PLUGINS_DIR;
20
23
  }
21
24
 
22
- export async function loadExternalPlugins(): Promise<LoadedExternalPlugin[]> {
25
+ /** Resolves a plugin directory's entry file the way `bun install` would. */
26
+ export async function resolvePluginEntry(pluginDir: string): Promise<string | null> {
27
+ const pkgPath = join(pluginDir, "package.json");
28
+ if (existsSync(pkgPath)) {
29
+ try {
30
+ const pkg = JSON.parse(await Bun.file(pkgPath).text());
31
+ if (pkg.main) {
32
+ const main = join(pluginDir, pkg.main);
33
+ if (existsSync(main)) return main;
34
+ }
35
+ } catch {
36
+ // Malformed package.json falls through to the index candidates.
37
+ }
38
+ }
39
+ for (const candidate of ["index.ts", "index.tsx", "index.js"]) {
40
+ const path = join(pluginDir, candidate);
41
+ if (existsSync(path)) return path;
42
+ }
43
+ return null;
44
+ }
45
+
46
+ export function pluginSupportsTarget(plugin: GloomPlugin, target: PluginTarget): boolean {
47
+ // No declaration means "everywhere"; the registry fills this in for listed plugins.
48
+ return !plugin.targets || plugin.targets.length === 0 || plugin.targets.includes(target);
49
+ }
50
+
51
+ export async function loadExternalPlugins(target: PluginTarget = "cli"): Promise<LoadedExternalPlugin[]> {
23
52
  if (!existsSync(PLUGINS_DIR)) return [];
24
53
 
25
54
  const results: LoadedExternalPlugin[] = [];
@@ -29,32 +58,22 @@ export async function loadExternalPlugins(): Promise<LoadedExternalPlugin[]> {
29
58
  if (!entry.isDirectory()) continue;
30
59
  const pluginDir = join(PLUGINS_DIR, entry.name);
31
60
 
32
- // Look for entry file
33
- let entryFile: string | null = null;
34
-
35
- // Check package.json first
36
- const pkgPath = join(pluginDir, "package.json");
37
- if (existsSync(pkgPath)) {
38
- try {
39
- const pkg = JSON.parse(await Bun.file(pkgPath).text());
40
- if (pkg.main) entryFile = join(pluginDir, pkg.main);
41
- } catch { /* ignore malformed package.json */ }
42
- }
43
-
44
- // Fall back to index files
45
- if (!entryFile) {
46
- for (const candidate of ["index.ts", "index.tsx", "index.js"]) {
47
- const p = join(pluginDir, candidate);
48
- if (existsSync(p)) { entryFile = p; break; }
49
- }
50
- }
51
-
61
+ const entryFile = await resolvePluginEntry(pluginDir);
52
62
  if (!entryFile) continue;
53
63
 
64
+ // Repairs `gloomberb`/`react` links for plugins copied in by hand or left
65
+ // behind by a `bun install` that pruned them.
66
+ linkHostPackages(pluginDir);
67
+
54
68
  try {
55
69
  const mod = await import(entryFile);
56
70
  const plugin: GloomPlugin = mod.default ?? mod.plugin;
57
71
  if (plugin && plugin.id && plugin.name) {
72
+ if (!pluginSupportsTarget(plugin, target)) {
73
+ loaderLog.info(`Skipped ${plugin.id}: does not support "${target}"`);
74
+ results.push({ plugin, path: pluginDir, unsupportedTarget: target });
75
+ continue;
76
+ }
58
77
  loaderLog.info(`Loaded external plugin: ${plugin.id} v${plugin.version ?? "0.0.0"}`);
59
78
  results.push({ plugin, path: pluginDir });
60
79
  }
@@ -7,6 +7,7 @@ const BUILTIN_PLUGIN_OWNER_ALIASES: Record<string, string> = {
7
7
  "comparison-chart": "ticker-research",
8
8
  correlation: "market-overview",
9
9
  "earnings-calendar": "macro",
10
+ "earnings-calls": "macro",
10
11
  "ipo-calendar": "macro",
11
12
  "fear-greed": "market-overview",
12
13
  "fx-matrix": "market-overview",
@@ -43,6 +43,7 @@ export const predictionMarketsPlugin: GloomPlugin = {
43
43
  defaultPosition: "left",
44
44
  defaultMode: "floating",
45
45
  defaultFloatingSize: { width: 132, height: 36 },
46
+ tableExport: true,
46
47
  settings: (context) =>
47
48
  buildPredictionMarketsPaneSettingsDef(
48
49
  context.config,
@@ -142,6 +142,9 @@ export function createRegistryPluginContext({
142
142
  registerPane: (pane) => contributions.registerPane(pluginId, pane, items),
143
143
  registerPaneTemplate: (template) => contributions.registerPaneTemplate(pluginId, template, items),
144
144
  registerCommand: (command) => contributions.registerCommand(pluginId, command, items),
145
+ registerCommandBarSearchProvider: (provider) => (
146
+ contributions.registerCommandBarSearchProvider(pluginId, provider, items)
147
+ ),
145
148
  registerColumn: (column) => contributions.registerColumn(pluginId, column, items),
146
149
  registerBroker: (broker) => contributions.registerBroker(pluginId, broker, items),
147
150
  registerCapability: (capability) => {
@@ -1,5 +1,6 @@
1
1
  import type { BrokerAdapter } from "../../types/broker";
2
2
  import type {
3
+ CommandBarSearchProvider,
3
4
  CommandDef,
4
5
  ContextMenuProviderDef,
5
6
  CustomColumnDef,
@@ -19,6 +20,7 @@ export interface PluginItems {
19
20
  panes: string[];
20
21
  paneTemplates: string[];
21
22
  commands: string[];
23
+ commandBarSearchProviders: string[];
22
24
  columns: string[];
23
25
  brokers: string[];
24
26
  capabilities: string[];
@@ -45,6 +47,7 @@ function setUnique<T>(map: Map<string, T>, id: string, value: T): void {
45
47
  export class RegistryContributions {
46
48
  readonly pluginItems = new Map<string, PluginItems>();
47
49
  readonly commandOwners = new Map<string, string>();
50
+ readonly commandBarSearchProviderOwners = new Map<string, string>();
48
51
  readonly paneOwners = new Map<string, string>();
49
52
  readonly paneTemplateOwners = new Map<string, string>();
50
53
  readonly shortcutOwners = new Map<string, string>();
@@ -54,6 +57,7 @@ export class RegistryContributions {
54
57
  readonly panesMap = new Map<string, PaneDef>();
55
58
  readonly paneTemplatesMap = new Map<string, PaneTemplateDef>();
56
59
  readonly commandsMap = new Map<string, CommandDef>();
60
+ readonly commandBarSearchProvidersMap = new Map<string, CommandBarSearchProvider>();
57
61
  readonly columnsMap = new Map<string, CustomColumnDef>();
58
62
  readonly brokersMap = new Map<string, BrokerAdapter>();
59
63
  readonly tickerResearchTabsMap = new Map<string, TickerResearchTabDef>();
@@ -71,6 +75,7 @@ export class RegistryContributions {
71
75
  panes: [],
72
76
  paneTemplates: [],
73
77
  commands: [],
78
+ commandBarSearchProviders: [],
74
79
  columns: [],
75
80
  brokers: [],
76
81
  capabilities: [],
@@ -104,6 +109,24 @@ export class RegistryContributions {
104
109
  items.commands.push(command.id);
105
110
  }
106
111
 
112
+ registerCommandBarSearchProvider(
113
+ pluginId: string,
114
+ provider: CommandBarSearchProvider,
115
+ items = this.getOrCreatePluginItems(pluginId),
116
+ ): () => void {
117
+ setUnique(this.commandBarSearchProvidersMap, provider.id, provider);
118
+ this.commandBarSearchProviderOwners.set(provider.id, pluginId);
119
+ items.commandBarSearchProviders.push(provider.id);
120
+ return () => {
121
+ // Only the registration still in place may be withdrawn: a re-registered
122
+ // provider under the same id belongs to whoever registered it last.
123
+ if (this.commandBarSearchProvidersMap.get(provider.id) !== provider) return;
124
+ this.commandBarSearchProvidersMap.delete(provider.id);
125
+ this.commandBarSearchProviderOwners.delete(provider.id);
126
+ items.commandBarSearchProviders = items.commandBarSearchProviders.filter((id) => id !== provider.id);
127
+ };
128
+ }
129
+
107
130
  registerColumn(_pluginId: string, column: CustomColumnDef, items: PluginItems): void {
108
131
  setUnique(this.columnsMap, column.id, column);
109
132
  items.columns.push(column.id);
@@ -158,6 +181,10 @@ export class RegistryContributions {
158
181
  this.commandsMap.delete(commandId);
159
182
  this.commandOwners.delete(commandId);
160
183
  }
184
+ for (const providerId of items.commandBarSearchProviders) {
185
+ this.commandBarSearchProvidersMap.delete(providerId);
186
+ this.commandBarSearchProviderOwners.delete(providerId);
187
+ }
161
188
  for (const columnId of items.columns) this.columnsMap.delete(columnId);
162
189
  for (const brokerId of items.brokers) this.brokersMap.delete(brokerId);
163
190
  for (const capabilityId of items.capabilities) this.capabilityOwners.delete(capabilityId);
@@ -317,6 +317,31 @@ describe("PluginRegistry ticker research tabs", () => {
317
317
  });
318
318
  });
319
319
 
320
+ describe("PluginRegistry command bar search providers", () => {
321
+ test("withdraws providers on dispose and on plugin unregister", async () => {
322
+ const registry = createRegistry();
323
+ const provider = (id: string) => ({
324
+ id,
325
+ category: "Documents",
326
+ provide: async () => [],
327
+ });
328
+ let disposeSelfRegistered: (() => void) | null = null;
329
+
330
+ await registry.register(plugin("research-search", (ctx) => {
331
+ ctx.registerCommandBarSearchProvider(provider("documents"));
332
+ disposeSelfRegistered = ctx.registerCommandBarSearchProvider(provider("self-withdrawn"));
333
+ }));
334
+
335
+ expect(registry.getCommandBarSearchProviderPluginId("documents")).toBe("research-search");
336
+ disposeSelfRegistered!();
337
+ expect(registry.commandBarSearchProviders.has("self-withdrawn")).toBe(false);
338
+
339
+ registry.unregister("research-search");
340
+ expect(registry.commandBarSearchProviders.size).toBe(0);
341
+ expect(registry.getCommandBarSearchProviderPluginId("documents")).toBeUndefined();
342
+ });
343
+ });
344
+
320
345
  describe("PluginRegistry pane settings", () => {
321
346
  test("resolves effective pane setting values from settings definitions", async () => {
322
347
  const registry = createRegistry();
@@ -19,6 +19,7 @@ import type {
19
19
  AppNotificationDelivery,
20
20
  AppNotificationRequest,
21
21
  BrokerInstanceUpdateOptions,
22
+ CommandBarSearchProvider,
22
23
  CommandDef,
23
24
  CustomColumnDef,
24
25
  GloomPlugin,
@@ -258,6 +259,9 @@ export class PluginRegistry implements PluginRuntimeAccess {
258
259
  get panes(): ReadonlyMap<string, PaneDef> { return this.contributions.panesMap; }
259
260
  get paneTemplates(): ReadonlyMap<string, PaneTemplateDef> { return this.contributions.paneTemplatesMap; }
260
261
  get commands(): ReadonlyMap<string, CommandDef> { return this.contributions.commandsMap; }
262
+ get commandBarSearchProviders(): ReadonlyMap<string, CommandBarSearchProvider> {
263
+ return this.contributions.commandBarSearchProvidersMap;
264
+ }
261
265
  get columns(): ReadonlyMap<string, CustomColumnDef> { return this.contributions.columnsMap; }
262
266
  get brokers(): ReadonlyMap<string, BrokerAdapter> { return this.contributions.brokersMap; }
263
267
  get tickerResearchTabs(): ReadonlyMap<string, TickerResearchTabDef> { return this.contributions.tickerResearchTabsMap; }
@@ -430,6 +434,10 @@ export class PluginRegistry implements PluginRuntimeAccess {
430
434
  return this.contributions.commandOwners.get(commandId);
431
435
  }
432
436
 
437
+ getCommandBarSearchProviderPluginId(providerId: string): string | undefined {
438
+ return this.contributions.commandBarSearchProviderOwners.get(providerId);
439
+ }
440
+
433
441
  getPanePluginId(paneId: string): string | undefined {
434
442
  return this.contributions.paneOwners.get(paneId);
435
443
  }