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
@@ -0,0 +1,53 @@
1
+ import { expect, test } from "bun:test";
2
+ import { registerPaneTableExporter } from "../../state/pane-table-export-registry";
3
+ import { createDefaultConfig } from "../../types/config";
4
+ import type { AppNotificationRequest, PaneDef } from "../../types/plugin";
5
+ import { resolveRegistryPaneSettings } from "./pane-settings";
6
+
7
+ test("adds a working CSV action to exportable table panes", async () => {
8
+ const pane = { instanceId: "prices:main", paneId: "prices", title: "Market Prices" };
9
+ const config = createDefaultConfig("/tmp/gloomberb-pane-export-test");
10
+ config.layout.instances = [pane];
11
+ const filenames: string[] = [];
12
+ const unregister = registerPaneTableExporter(pane.instanceId, async (filename) => {
13
+ filenames.push(filename);
14
+ return `~/Downloads/${filename}`;
15
+ });
16
+
17
+ try {
18
+ const paneDef: PaneDef = {
19
+ id: pane.paneId,
20
+ name: "Prices",
21
+ component: () => null,
22
+ defaultPosition: "right",
23
+ tableExport: true,
24
+ };
25
+ const resolved = resolveRegistryPaneSettings({
26
+ config,
27
+ getConfigState: () => null,
28
+ getPaneRuntimeState: () => null,
29
+ layout: config.layout,
30
+ paneDefs: new Map([[pane.paneId, paneDef]]),
31
+ paneOwners: new Map(),
32
+ resolvePaneTarget: () => pane.instanceId,
33
+ requestedPaneId: pane.instanceId,
34
+ });
35
+ const field = resolved?.settingsDef.fields[0];
36
+ expect(field).toMatchObject({ type: "action", label: "Export CSV", disabled: false });
37
+ if (!field || field.type !== "action" || !resolved) throw new Error("Missing export action");
38
+
39
+ const notifications: AppNotificationRequest[] = [];
40
+ await field.action({
41
+ ...resolved.context,
42
+ surface: "pane-dialog",
43
+ close: () => {},
44
+ openCommandBar: () => {},
45
+ notify: (notification) => notifications.push(notification),
46
+ });
47
+
48
+ expect(filenames[0]).toMatch(/^Market-Prices-.*\.csv$/);
49
+ expect(notifications[0]).toMatchObject({ type: "success" });
50
+ } finally {
51
+ unregister();
52
+ }
53
+ });
@@ -12,6 +12,10 @@ import type {
12
12
  PaneSettingsContext,
13
13
  PaneSettingsDef,
14
14
  } from "../../types/plugin";
15
+ import {
16
+ exportPaneTableCsv,
17
+ hasPaneTableExporter,
18
+ } from "../../state/pane-table-export-registry";
15
19
 
16
20
  export interface ResolvedRegistryPaneSettings {
17
21
  paneId: string;
@@ -76,7 +80,7 @@ export function resolveRegistryPaneSettings({
76
80
  if (!pane) return null;
77
81
 
78
82
  const paneDef = paneDefs.get(pane.paneId);
79
- if (!paneDef?.settings) return null;
83
+ if (!paneDef || (!paneDef.settings && !paneDef.tableExport)) return null;
80
84
 
81
85
  const pluginId = paneOwners.get(pane.paneId);
82
86
  const paneSettings = getPaneSettings(pane);
@@ -99,10 +103,32 @@ export function resolveRegistryPaneSettings({
99
103
  activeTicker: resolveTickerForPane(stateView, targetPaneId),
100
104
  activeCollectionId: resolveCollectionForPane(stateView, targetPaneId),
101
105
  };
102
- const settingsDef = typeof paneDef.settings === "function"
106
+ const baseSettingsDef = typeof paneDef.settings === "function"
103
107
  ? paneDef.settings(context)
104
108
  : paneDef.settings;
105
- if (!settingsDef) return null;
109
+ if (!baseSettingsDef && !paneDef.tableExport) return null;
110
+ const settingsDef: PaneSettingsDef = paneDef.tableExport
111
+ ? {
112
+ ...(baseSettingsDef ?? {}),
113
+ fields: [
114
+ ...(baseSettingsDef?.fields ?? []),
115
+ {
116
+ key: "tableExport.csv",
117
+ label: "Export CSV",
118
+ description: "Export the current table as an Excel-compatible CSV file.",
119
+ type: "action",
120
+ actionId: "table.export-csv",
121
+ actionLabel: "Export",
122
+ disabled: !hasPaneTableExporter(targetPaneId),
123
+ action: (actionContext) => exportPaneTableCsv(
124
+ targetPaneId,
125
+ pane.title ?? paneDef.name,
126
+ actionContext.notify,
127
+ ),
128
+ },
129
+ ],
130
+ }
131
+ : baseSettingsDef!;
106
132
 
107
133
  const rawSettings = { ...paneSettings };
108
134
  const resolvedSettings = {
@@ -0,0 +1,83 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { mkdirSync, mkdtempSync, rmSync } from "fs";
3
+ import { tmpdir } from "os";
4
+ import { join } from "path";
5
+
6
+ import { EXTRACTED_PLUGINS, seedExtractedPlugins } from "./seed";
7
+ import type { AppConfig } from "../types/config";
8
+
9
+ /**
10
+ * Extracting a built-in plugin is the one change here that can quietly take a
11
+ * working feature away from an existing user, so the seeding rules are worth
12
+ * pinning: restore it once, never fight a deliberate removal, and never record
13
+ * a failure as done — being offline at startup is common, and marking it seeded
14
+ * would drop the plugin permanently.
15
+ */
16
+ function config(overrides: Partial<AppConfig> = {}): AppConfig {
17
+ return { disabledPlugins: [], seededPlugins: [], ...overrides } as AppConfig;
18
+ }
19
+
20
+ function withPluginsDir<T>(setup: (dir: string) => T): T {
21
+ const dir = mkdtempSync(join(tmpdir(), "gloom-seed-"));
22
+ try {
23
+ return setup(dir);
24
+ } finally {
25
+ rmSync(dir, { recursive: true, force: true });
26
+ }
27
+ }
28
+
29
+ describe("seedExtractedPlugins", () => {
30
+ test("installs an extracted plugin the user has not seen", async () => {
31
+ const installs: string[] = [];
32
+ const result = await withPluginsDir((dir) => seedExtractedPlugins(config(), async (ref) => {
33
+ installs.push(ref);
34
+ }, dir));
35
+
36
+ expect(installs).toEqual(EXTRACTED_PLUGINS.map((entry) => entry.repo));
37
+ expect(result.installed).toEqual(EXTRACTED_PLUGINS.map((entry) => entry.id));
38
+ expect(result.seeded).toEqual(EXTRACTED_PLUGINS.map((entry) => entry.id));
39
+ });
40
+
41
+ test("does nothing once a plugin has been seeded", async () => {
42
+ const installs: string[] = [];
43
+ const seeded = EXTRACTED_PLUGINS.map((entry) => entry.id);
44
+ await withPluginsDir((dir) => seedExtractedPlugins(config({ seededPlugins: seeded }), async (ref) => {
45
+ installs.push(ref);
46
+ }, dir));
47
+
48
+ expect(installs).toEqual([]);
49
+ });
50
+
51
+ test("respects a plugin the user disabled before the move", async () => {
52
+ const installs: string[] = [];
53
+ const result = await withPluginsDir((dir) => seedExtractedPlugins(
54
+ config({ disabledPlugins: ["substack"] }),
55
+ async (ref) => { installs.push(ref); },
56
+ dir,
57
+ ));
58
+
59
+ expect(installs).not.toContain("gloom-sh/gloomberb-substack");
60
+ // Recorded, so we stop asking rather than retrying every launch.
61
+ expect(result.seeded).toContain("substack");
62
+ });
63
+
64
+ test("records a plugin that is already installed without reinstalling it", async () => {
65
+ const installs: string[] = [];
66
+ const result = await withPluginsDir((pluginsDir) => {
67
+ mkdirSync(join(pluginsDir, "gloomberb-substack"), { recursive: true });
68
+ return seedExtractedPlugins(config(), async (ref) => { installs.push(ref); }, pluginsDir);
69
+ });
70
+
71
+ expect(installs).not.toContain("gloom-sh/gloomberb-substack");
72
+ expect(result.seeded).toContain("substack");
73
+ });
74
+
75
+ test("retries next launch instead of recording a failed install", async () => {
76
+ const result = await withPluginsDir((dir) => seedExtractedPlugins(config(), async () => {
77
+ throw new Error("offline");
78
+ }, dir));
79
+
80
+ expect(result.failed).toContain("substack");
81
+ expect(result.seeded).not.toContain("substack");
82
+ });
83
+ });
@@ -0,0 +1,80 @@
1
+ import { existsSync } from "fs";
2
+ import { join } from "path";
3
+
4
+ import type { AppConfig } from "../types/config";
5
+ import { debugLog } from "../utils/debug-log";
6
+ import { getPluginsDir } from "./loader";
7
+
8
+ const log = debugLog.createLogger("plugin-seed");
9
+
10
+ /**
11
+ * Plugins that used to ship inside Gloomberb and now live in their own
12
+ * repositories.
13
+ *
14
+ * Extracting one must not take a working feature away from someone who upgrades.
15
+ * On first launch after the move, each of these is installed once, then recorded
16
+ * so it is never reinstalled — including when the user removes it deliberately.
17
+ */
18
+ export const EXTRACTED_PLUGINS = [
19
+ { id: "substack", repo: "gloom-sh/gloomberb-substack", directory: "gloomberb-substack" },
20
+ { id: "ibkr", repo: "gloom-sh/gloomberb-ibkr", directory: "gloomberb-ibkr" },
21
+ { id: "ibkr-gateway", repo: "gloom-sh/gloomberb-ibkr-gateway", directory: "gloomberb-ibkr-gateway" },
22
+ ] as const;
23
+
24
+ export interface SeedResult {
25
+ installed: string[];
26
+ failed: string[];
27
+ /** Ids to record as seeded, whether or not this launch installed them. */
28
+ seeded: string[];
29
+ }
30
+
31
+ function isInstalled(directory: string, pluginsDir: string): boolean {
32
+ return existsSync(join(pluginsDir, directory));
33
+ }
34
+
35
+ /**
36
+ * Installs any extracted plugin the user has not seen yet.
37
+ *
38
+ * Best effort by design: a failure is logged and retried on the next launch
39
+ * rather than recorded, because the common cause is being offline at startup and
40
+ * marking it seeded would silently drop the plugin forever.
41
+ */
42
+ export async function seedExtractedPlugins(
43
+ config: AppConfig,
44
+ installPlugin: (ref: string) => Promise<void>,
45
+ pluginsDir: string = getPluginsDir(),
46
+ ): Promise<SeedResult> {
47
+ const alreadySeeded = new Set(config.seededPlugins ?? []);
48
+ const disabled = new Set(config.disabledPlugins ?? []);
49
+
50
+ const result: SeedResult = { installed: [], failed: [], seeded: [...alreadySeeded] };
51
+
52
+ for (const entry of EXTRACTED_PLUGINS) {
53
+ if (alreadySeeded.has(entry.id)) continue;
54
+
55
+ // Present already: either installed by hand or seeded before this record
56
+ // existed. Mark it so we stop looking.
57
+ if (isInstalled(entry.directory, pluginsDir)) {
58
+ result.seeded.push(entry.id);
59
+ continue;
60
+ }
61
+
62
+ // Turned off before the move: restoring it would override that choice.
63
+ if (disabled.has(entry.id)) {
64
+ result.seeded.push(entry.id);
65
+ continue;
66
+ }
67
+
68
+ try {
69
+ log.info(`Restoring ${entry.id} from ${entry.repo}`);
70
+ await installPlugin(entry.repo);
71
+ result.installed.push(entry.id);
72
+ result.seeded.push(entry.id);
73
+ } catch (error) {
74
+ log.error(`Could not restore ${entry.id}: ${error}`);
75
+ result.failed.push(entry.id);
76
+ }
77
+ }
78
+
79
+ return result;
80
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Broker plugin surface (`gloomberb/broker`).
3
+ *
4
+ * A broker plugin needs three things the generic plugin API does not cover: the
5
+ * account cache the app reads positions from, a resource store for cached
6
+ * fetches, and — on the desktop — a client for reaching its own Bun-side service
7
+ * from the view, since the renderer cannot open sockets itself.
8
+ */
9
+ export { getBrokerRemoteClient, setBrokerRemoteClient } from "../brokers/remote-broker-adapter";
10
+ export type { BrokerRemoteClient } from "../brokers/remote-broker-adapter";
11
+ export type { ResourceStore } from "../data/resource-store";
12
+ export { resolveTickerFinancialsForInstrument } from "../market-data/coordinator";
13
+
14
+ /**
15
+ * The app's cached-resource store, for plugins that persist fetched data across
16
+ * restarts. Set by the host at startup and cleared on teardown.
17
+ *
18
+ * A plugin should treat a null store as "no cache available" and still work,
19
+ * since it is absent before services are constructed.
20
+ */
21
+ let pluginResourceStore: import("../data/resource-store").ResourceStore | null = null;
22
+
23
+ export function setPluginResourceStore(
24
+ store: import("../data/resource-store").ResourceStore | null,
25
+ ): void {
26
+ pluginResourceStore = store;
27
+ }
28
+
29
+ export function getPluginResourceStore(): import("../data/resource-store").ResourceStore | null {
30
+ return pluginResourceStore;
31
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Market-data request shapes and price formatting (`gloomberb/market-data`).
3
+ *
4
+ * A plugin that provides quotes or history has to build the same request
5
+ * objects the host does, and format prices the same way, or its output looks
6
+ * foreign next to first-party panes.
7
+ */
8
+ export * from "../market-data/request-types";
9
+ export * from "../market-data/market/format";
@@ -0,0 +1,57 @@
1
+ /**
2
+ * App-state hooks a plugin pane legitimately needs.
3
+ *
4
+ * These exist so plugins do not import `state/app/context` directly. That module
5
+ * is the app's internal store: exposing it would let a plugin read and dispatch
6
+ * anything, and would make every refactor of app state a breaking change for
7
+ * plugins. Each hook here is a narrow, named read.
8
+ */
9
+ import { useEffect } from "react";
10
+
11
+ import {
12
+ useAppDispatch,
13
+ useAppSelector,
14
+ usePaneCollection as usePaneCollectionInternal,
15
+ usePaneInstanceId as usePaneInstanceIdInternal,
16
+ usePaneTicker as usePaneTickerInternal,
17
+ } from "../state/app/context";
18
+ import type { AppConfig } from "../types/config";
19
+ import type { AppState } from "../state/app/context";
20
+
21
+ export function useAppConfig(): AppConfig {
22
+ return useAppSelector((state) => state.config);
23
+ }
24
+
25
+ export function useBrokerAccounts(): AppState["brokerAccounts"] {
26
+ return useAppSelector((state) => state.brokerAccounts);
27
+ }
28
+
29
+ export function useTickers(): AppState["tickers"] {
30
+ return useAppSelector((state) => state.tickers);
31
+ }
32
+
33
+ /**
34
+ * Escape hatches for panes that need state this module has not named yet.
35
+ * Prefer a named hook above: these expose the app store directly, so anything
36
+ * built on them is coupled to its shape.
37
+ */
38
+ export { AppContext, PaneInstanceProvider, useAppDispatch, useAppSelector } from "../state/app/context";
39
+
40
+ export const usePaneInstanceId = usePaneInstanceIdInternal;
41
+ export const usePaneCollection = usePaneCollectionInternal;
42
+ export const usePaneTicker = usePaneTickerInternal;
43
+
44
+ /**
45
+ * Declares that this pane owns keyboard input while `captured` is true, so the
46
+ * app stops routing keys to global shortcuts. Released automatically on unmount.
47
+ */
48
+ export function useInputCapture(captured: boolean): void {
49
+ const dispatch = useAppDispatch();
50
+ useEffect(() => {
51
+ if (!captured) return;
52
+ dispatch({ type: "SET_INPUT_CAPTURED", captured: true });
53
+ return () => {
54
+ dispatch({ type: "SET_INPUT_CAPTURED", captured: false });
55
+ };
56
+ }, [captured, dispatch]);
57
+ }
@@ -0,0 +1,285 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import * as capabilities from "../capabilities";
4
+ import * as components from "../components";
5
+ import * as theme from "../theme/colors";
6
+ import * as ui from "../ui";
7
+ import * as pluginReact from "./react";
8
+ import * as testSupport from "./test-support";
9
+ import * as utils from "./utils";
10
+
11
+ /**
12
+ * The public plugin API is a compatibility boundary: plugins in other
13
+ * repositories (gloom-sh/gloomberb-substack, -ibkr, -hackernews, and any
14
+ * community plugin) import these subpaths, and a rename here breaks them
15
+ * silently at their next install rather than in this repo's CI.
16
+ *
17
+ * Adding an export is deliberate — update the list below in the same commit.
18
+ * Removing or renaming one is a breaking change for every installed plugin and
19
+ * needs a major version plus a migration note in PLUGINS.md.
20
+ */
21
+ const PUBLIC_API: Record<string, readonly string[]> = {
22
+ "ui": [
23
+ "AsciiText",
24
+ "Box",
25
+ "ChartSurface",
26
+ "ContextMenuProvider",
27
+ "ImageSurface",
28
+ "Input",
29
+ "MediaSurface",
30
+ "RGBA",
31
+ "ScrollBox",
32
+ "Span",
33
+ "SpinnerMark",
34
+ "Strong",
35
+ "StyledText",
36
+ "Text",
37
+ "TextAttributes",
38
+ "Textarea",
39
+ "UiHostProvider",
40
+ "Underline",
41
+ "compactContextMenuItems",
42
+ "contextMenuDivider",
43
+ "editableTextContextMenuItems",
44
+ "linkContextMenuItems",
45
+ "tickerContextMenuItems",
46
+ "useCommandBarShortcut",
47
+ "useContextMenu",
48
+ "useNativeRenderer",
49
+ "useRendererHost",
50
+ "useSyntaxStyleFactory",
51
+ "useTickerContextMenu",
52
+ "useUiCapabilities",
53
+ "useUiHost",
54
+ ],
55
+ "components": [
56
+ "Button",
57
+ "Checkbox",
58
+ "ChoiceDialog",
59
+ "ConfirmDialog",
60
+ "DataTableStackView",
61
+ "DataTableView",
62
+ "EmptyState",
63
+ "FeedDataTableStackView",
64
+ "InputSearchBar",
65
+ "MessageComposer",
66
+ "MetricTreemapSurface",
67
+ "NumberField",
68
+ "PaneFooterScope",
69
+ "PaneSidebar",
70
+ "PaneSidebarAction",
71
+ "PaneSidebarRow",
72
+ "PaneStatusBody",
73
+ "PriceSelectorDialog",
74
+ "RemoteImage",
75
+ "SegmentedControl",
76
+ "SelectButton",
77
+ "SpeedometerGauge",
78
+ "Spinner",
79
+ "StaticChartSurface",
80
+ "Tabs",
81
+ "TextField",
82
+ "TickerBadgeList",
83
+ "TickerBadgeText",
84
+ "TickerListTableView",
85
+ "activeStackIndex",
86
+ "buildMetricTreemapNavigationTiles",
87
+ "findMetricTreemapNeighbor",
88
+ "getMessageComposerBlockHeight",
89
+ "getPaneSidebarWidth",
90
+ "isTableScrollNearEnd",
91
+ "loadingText",
92
+ "shouldShowPaneSidebar",
93
+ "sortStackItems",
94
+ "unavailableText",
95
+ "useExternalLinkFooter",
96
+ "usePaneFooter",
97
+ "usePaneTicker",
98
+ "useTableLoadMore",
99
+ ],
100
+ "theme": [
101
+ "applyTheme",
102
+ "blendHex",
103
+ "clearTransientThemePreview",
104
+ "colors",
105
+ "commandBarAccentText",
106
+ "commandBarBadgeText",
107
+ "commandBarBg",
108
+ "commandBarHeadingText",
109
+ "commandBarHoverBg",
110
+ "commandBarInputBg",
111
+ "commandBarMatchText",
112
+ "commandBarPanelBg",
113
+ "commandBarSelectedBg",
114
+ "commandBarSelectedText",
115
+ "commandBarSubtleText",
116
+ "commandBarText",
117
+ "floatingPaneBg",
118
+ "floatingPaneTitleBg",
119
+ "getChartIndicatorColor",
120
+ "getCurrentThemeId",
121
+ "getThemeColors",
122
+ "hoverBg",
123
+ "paneBg",
124
+ "paneTitleBg",
125
+ "paneTitleText",
126
+ "previewTheme",
127
+ "priceColor",
128
+ "syncTheme",
129
+ ],
130
+ "capabilities": [
131
+ "AI_RUNNER_CAPABILITY_ID",
132
+ "BROKER_CAPABILITY_ID",
133
+ "CHART_SERIES_CAPABILITY_KIND",
134
+ "CapabilityRegistry",
135
+ "MAX_CHART_SERIES_CATALOG_ITEMS",
136
+ "MAX_CHART_SERIES_POINTS",
137
+ "NOTES_FILES_CAPABILITY_ID",
138
+ "assetDataProvider",
139
+ "chartSeriesCapabilityManifests",
140
+ "chartSeriesCatalogOutputSchema",
141
+ "chartSeriesCatalogRequestSchema",
142
+ "chartSeriesProvider",
143
+ "chartSeriesResolveOutputSchema",
144
+ "chartSeriesResolveRequestSchema",
145
+ "chartSeriesSourceKey",
146
+ "createChartSeriesResolver",
147
+ "isValidChartCapabilityId",
148
+ "isValidChartSeriesId",
149
+ "newsProvider",
150
+ "recordSchema",
151
+ "searchChartSeriesCapabilities",
152
+ ],
153
+ "utils": [
154
+ "buildBrokerPortfolioId",
155
+ "canonicalExchange",
156
+ "canonicalTickerKey",
157
+ "createBrokerInstanceId",
158
+ "createThrottledFetch",
159
+ "debugLog",
160
+ "decodeHtmlEntities",
161
+ "displayWidth",
162
+ "fnv1aHashString",
163
+ "formatCompact",
164
+ "formatCompactCurrency",
165
+ "formatCurrency",
166
+ "formatGrowthShort",
167
+ "formatNumber",
168
+ "formatPercent",
169
+ "formatPercentRaw",
170
+ "formatRelativeAge",
171
+ "formatTimeAgo",
172
+ "formatWithDivisor",
173
+ "getBrokerInstance",
174
+ "getBrokerInstancesByType",
175
+ "hasLikelyQuoteUnitMismatch",
176
+ "httpFetch",
177
+ "isBrokerPortfolioId",
178
+ "isPlainKey",
179
+ "isPlainKeyboardEvent",
180
+ "normalizePriceValueByDivisor",
181
+ "normalizeSymbol",
182
+ "normalizedHttpUrl",
183
+ "padTo",
184
+ "parsePublicTickerKey",
185
+ "pickUnit",
186
+ "publicExchange",
187
+ "publicTickerKey",
188
+ "resolveCurrencyUnit",
189
+ "resolveExchangeSubUnitCurrencyUnit",
190
+ "resolveExchangeTimeZone",
191
+ "resolvePriceHistoryCurrencyUnit",
192
+ "setHttpFetchTransport",
193
+ "splitLongTextSegmentByDisplayWidth",
194
+ "truncateToDisplayWidth",
195
+ "truncateWithEllipsis",
196
+ "wrapTextLines",
197
+ ],
198
+
199
+ "react": [
200
+ "AppContext",
201
+ "DEFAULT_MAX_READ_IDS",
202
+ "PaneInstanceProvider",
203
+ "deletePluginPaneStateValue",
204
+ "getPluginPaneStateValue",
205
+ "markPersistedReadId",
206
+ "normalizePersistedReadIdState",
207
+ "setPluginPaneStateValue",
208
+ "useAppConfig",
209
+ "useAppDispatch",
210
+ "useAppSelector",
211
+ "useAssetData",
212
+ "useBrokerAccounts",
213
+ "useCapabilityInvoker",
214
+ "useConnectionHealth",
215
+ "useDebouncedPluginPaneState",
216
+ "useInlineTickerOpener",
217
+ "useInlineTickers",
218
+ "useInputCapture",
219
+ "useMarketData",
220
+ "usePaneCollection",
221
+ "usePaneInstanceId",
222
+ "usePaneTicker",
223
+ "usePersistedReadIds",
224
+ "usePluginAppActions",
225
+ "usePluginBrokerActions",
226
+ "usePluginConfigState",
227
+ "usePluginPaneActions",
228
+ "usePluginPaneState",
229
+ "usePluginState",
230
+ "usePluginTickerActions",
231
+ "useSetPluginConfigStates",
232
+ "useShortcut",
233
+ "useTickers",
234
+ ],
235
+
236
+
237
+
238
+ "test-support": [
239
+ "AppContext",
240
+ "AppPersistence",
241
+ "MemoryPluginPersistence",
242
+ "PaneInstanceProvider",
243
+ "TestDialogProvider",
244
+ "clearPersistedBrokerAccounts",
245
+ "createInitialState",
246
+ "getBrokerAccountCacheSourceKey",
247
+ "loadPersistedBrokerAccountMap",
248
+ "loadPersistedBrokerAccounts",
249
+ "persistBrokerAccounts",
250
+ "testRender",
251
+ ],
252
+
253
+ };
254
+
255
+ const MODULES: Record<string, Record<string, unknown>> = {
256
+ ui,
257
+ components,
258
+ theme,
259
+ capabilities,
260
+ utils,
261
+ react: pluginReact,
262
+ "test-support": testSupport,
263
+ };
264
+
265
+ describe("public plugin API", () => {
266
+ for (const [name, expected] of Object.entries(PUBLIC_API)) {
267
+ test(`gloomberb/${name} exports exactly its documented surface`, () => {
268
+ const actual = Object.keys(MODULES[name]!)
269
+ .filter((key) => key !== "default")
270
+ .sort();
271
+ expect(actual).toEqual([...expected]);
272
+ });
273
+ }
274
+
275
+ test("every exports subpath in package.json resolves", async () => {
276
+ const pkg = JSON.parse(await Bun.file(new URL("../../package.json", import.meta.url)).text());
277
+ const subpaths = Object.entries(pkg.exports as Record<string, string>);
278
+ expect(subpaths.length).toBeGreaterThan(0);
279
+ for (const [subpath, target] of subpaths) {
280
+ if (subpath === "./package.json") continue;
281
+ const file = Bun.file(new URL(`../../${target.replace(/^\.\//, "")}`, import.meta.url));
282
+ expect(await file.exists(), `${subpath} -> ${target}`).toBe(true);
283
+ }
284
+ });
285
+ });