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,423 @@
1
+ import { apiClient } from "../../api-client";
2
+ import type { MarketContext } from "../types";
3
+ import type {
4
+ HeadlessBundleResult,
5
+ HeadlessBundleSection,
6
+ HeadlessPaneColumn,
7
+ HeadlessPaneContext,
8
+ HeadlessPaneDefinition,
9
+ HeadlessPaneEntry,
10
+ HeadlessPaneLoadArgs,
11
+ HeadlessPaneOptionValues,
12
+ HeadlessPaneResult,
13
+ HeadlessPaneRow,
14
+ HeadlessSeriesResult,
15
+ HeadlessSnapshotResult,
16
+ } from "../../types/plugin";
17
+ import { renderSection, renderTable } from "../../utils/cli-output";
18
+ import type { PaneFunctionReport } from "./report";
19
+ import type { ResolvedPaneFunction } from "./resolver";
20
+
21
+ interface SerializableHeadlessColumn {
22
+ key: string;
23
+ header: string;
24
+ align?: "left" | "right" | "center";
25
+ width?: number;
26
+ description?: string;
27
+ }
28
+
29
+ export interface LoadedHeadlessPaneModel {
30
+ definition: HeadlessPaneDefinition;
31
+ args: HeadlessPaneLoadArgs;
32
+ result: HeadlessPaneResult;
33
+ }
34
+
35
+ function argumentName(definition: HeadlessPaneDefinition): string {
36
+ return definition.argument.placeholder
37
+ ?? (definition.argument.kind === "free-text" ? "text" : "symbol");
38
+ }
39
+
40
+ function requiredArgumentError(definition: HeadlessPaneDefinition, token: string): Error {
41
+ return new Error(`Usage: gloomberb fn ${token} <${argumentName(definition)}>`);
42
+ }
43
+
44
+ function normalizeSymbol(value: string): string {
45
+ return value.trim().replace(/^\$+/, "").toUpperCase();
46
+ }
47
+
48
+ function normalizeSymbolList(value: string): string[] {
49
+ const symbols: string[] = [];
50
+ const seen = new Set<string>();
51
+ for (const part of value.split(/[,\n]/)) {
52
+ const symbol = normalizeSymbol(part);
53
+ if (!symbol || seen.has(symbol)) continue;
54
+ seen.add(symbol);
55
+ symbols.push(symbol);
56
+ }
57
+ return symbols;
58
+ }
59
+
60
+ function validateSymbolCount(
61
+ definition: HeadlessPaneDefinition,
62
+ token: string,
63
+ symbols: string[],
64
+ ): void {
65
+ const minimum = definition.argument.minimum
66
+ ?? (definition.argument.optional ? 0 : 1);
67
+ const maximum = definition.argument.maximum;
68
+ if (symbols.length < minimum) {
69
+ if (minimum <= 1) throw requiredArgumentError(definition, token);
70
+ throw new Error(`${token} requires at least ${minimum} symbols.`);
71
+ }
72
+ if (maximum != null && symbols.length > maximum) {
73
+ throw new Error(`${token} accepts at most ${maximum} symbols.`);
74
+ }
75
+ }
76
+
77
+ export function buildHeadlessPaneLoadArgs(
78
+ definition: HeadlessPaneDefinition,
79
+ token: string,
80
+ rawArgument: string,
81
+ options: HeadlessPaneOptionValues,
82
+ ): HeadlessPaneLoadArgs {
83
+ const raw = rawArgument.trim();
84
+ switch (definition.argument.kind) {
85
+ case "none":
86
+ if (raw) throw new Error(`${token} does not accept an argument.`);
87
+ return { rawArgument: raw, argument: null, symbols: [], options };
88
+ case "free-text":
89
+ if (!raw && !definition.argument.optional) throw requiredArgumentError(definition, token);
90
+ return { rawArgument: raw, argument: raw || null, symbols: [], options };
91
+ case "ticker": {
92
+ const symbol = normalizeSymbol(raw);
93
+ const symbols = symbol ? [symbol] : [];
94
+ validateSymbolCount(definition, token, symbols);
95
+ return {
96
+ rawArgument: raw,
97
+ argument: symbol || null,
98
+ symbols,
99
+ options,
100
+ };
101
+ }
102
+ case "tickers":
103
+ case "symbol-list": {
104
+ const symbols = normalizeSymbolList(raw);
105
+ validateSymbolCount(definition, token, symbols);
106
+ return {
107
+ rawArgument: raw,
108
+ argument: symbols.length > 0 ? symbols : null,
109
+ symbols,
110
+ options,
111
+ };
112
+ }
113
+ default: {
114
+ const _exhaustive: never = definition.argument.kind;
115
+ return _exhaustive;
116
+ }
117
+ }
118
+ }
119
+
120
+ function throwIfAborted(signal: AbortSignal): void {
121
+ if (!signal.aborted) return;
122
+ throw signal.reason instanceof Error
123
+ ? signal.reason
124
+ : new Error("Headless pane load was aborted.");
125
+ }
126
+
127
+ export async function loadHeadlessPaneModel(
128
+ definition: HeadlessPaneDefinition,
129
+ args: HeadlessPaneLoadArgs,
130
+ context: HeadlessPaneContext,
131
+ ): Promise<HeadlessPaneResult> {
132
+ throwIfAborted(context.signal);
133
+ const result = await definition.load(args, context);
134
+ throwIfAborted(context.signal);
135
+ validateHeadlessResult(definition.shape, result);
136
+ return result;
137
+ }
138
+
139
+ /**
140
+ * Loads the same renderer-neutral model used by `fn`.
141
+ * A future screenshot payload can call this function instead of adding another fetch path.
142
+ */
143
+ export async function loadResolvedHeadlessPaneModel(
144
+ resolved: ResolvedPaneFunction,
145
+ context: MarketContext,
146
+ rawArgument: string,
147
+ signal: AbortSignal = new AbortController().signal,
148
+ ): Promise<LoadedHeadlessPaneModel> {
149
+ const definition = resolved.headless;
150
+ if (!definition) throw new Error(`${resolved.token} has no headless pane model.`);
151
+ const args = buildHeadlessPaneLoadArgs(definition, resolved.token, rawArgument, resolved.options);
152
+ const headlessContext: HeadlessPaneContext = {
153
+ marketData: context.dataProvider,
154
+ apiClient,
155
+ config: context.config,
156
+ signal,
157
+ };
158
+ const result = await loadHeadlessPaneModel(definition, args, headlessContext);
159
+ return { definition, args, result };
160
+ }
161
+
162
+ function isRecord(value: unknown): value is Record<string, unknown> {
163
+ return value != null && typeof value === "object" && !Array.isArray(value);
164
+ }
165
+
166
+ function validateHeadlessResult(shape: HeadlessPaneDefinition["shape"], result: unknown): void {
167
+ if (!isRecord(result)) throw new Error(`Headless ${shape} loader returned an invalid result.`);
168
+ const valid = shape === "rows"
169
+ ? Array.isArray(result.rows)
170
+ : shape === "bundle"
171
+ ? Array.isArray(result.sections)
172
+ : shape === "series"
173
+ ? Array.isArray(result.series)
174
+ : Array.isArray(result.items) && result.asOf != null;
175
+ if (!valid) throw new Error(`Headless ${shape} loader returned an invalid result.`);
176
+ }
177
+
178
+ function serializableColumns(columns: HeadlessPaneColumn[]): SerializableHeadlessColumn[] {
179
+ return columns.map((column) => ({
180
+ key: column.key,
181
+ header: column.header,
182
+ ...(column.align ? { align: column.align } : {}),
183
+ ...(column.width != null ? { width: column.width } : {}),
184
+ ...(column.description ? { description: column.description } : {}),
185
+ }));
186
+ }
187
+
188
+ function inferColumns(rows: HeadlessPaneRow[]): HeadlessPaneColumn[] {
189
+ const keys = new Set<string>();
190
+ for (const row of rows) {
191
+ for (const key of Object.keys(row)) keys.add(key);
192
+ }
193
+ return [...keys].map((key) => ({ key, header: key }));
194
+ }
195
+
196
+ function columnsFor(
197
+ rows: HeadlessPaneRow[],
198
+ ownColumns: HeadlessPaneColumn[] | undefined,
199
+ fallbackColumns: HeadlessPaneColumn[] | undefined,
200
+ ): HeadlessPaneColumn[] {
201
+ return ownColumns ?? fallbackColumns ?? inferColumns(rows);
202
+ }
203
+
204
+ function serializeBundleSection(
205
+ section: HeadlessBundleSection,
206
+ fallbackColumns: HeadlessPaneColumn[] | undefined,
207
+ ): Record<string, unknown> {
208
+ if ("rows" in section && section.rows) {
209
+ const columns = columnsFor(section.rows, section.columns, fallbackColumns);
210
+ return {
211
+ title: section.title,
212
+ columns: serializableColumns(columns),
213
+ rows: section.rows,
214
+ };
215
+ }
216
+ return { title: section.title, entries: section.entries };
217
+ }
218
+
219
+ export function serializeHeadlessPaneResult(
220
+ definition: HeadlessPaneDefinition,
221
+ result: HeadlessPaneResult,
222
+ ): Record<string, unknown> {
223
+ const common = {
224
+ ...(result.errors ? { errors: result.errors } : {}),
225
+ ...(result.metadata ? { metadata: result.metadata } : {}),
226
+ };
227
+ switch (definition.shape) {
228
+ case "rows": {
229
+ const rowsResult = result as HeadlessPaneResult & { rows: HeadlessPaneRow[]; columns?: HeadlessPaneColumn[] };
230
+ const columns = columnsFor(rowsResult.rows, rowsResult.columns, definition.columns);
231
+ return { ...common, columns: serializableColumns(columns), rows: rowsResult.rows };
232
+ }
233
+ case "bundle": {
234
+ const bundle = result as HeadlessBundleResult;
235
+ return {
236
+ ...common,
237
+ sections: bundle.sections.map((section) => serializeBundleSection(section, definition.columns)),
238
+ };
239
+ }
240
+ case "series": {
241
+ const series = result as HeadlessSeriesResult;
242
+ return { ...common, series: series.series, ...(series.stats ? { stats: series.stats } : {}) };
243
+ }
244
+ case "snapshot": {
245
+ const snapshot = result as HeadlessSnapshotResult;
246
+ const columns = columnsFor(snapshot.items, undefined, definition.columns);
247
+ return {
248
+ ...common,
249
+ asOf: snapshot.asOf,
250
+ columns: serializableColumns(columns),
251
+ items: snapshot.items,
252
+ };
253
+ }
254
+ default: {
255
+ const _exhaustive: never = definition.shape;
256
+ return _exhaustive;
257
+ }
258
+ }
259
+ }
260
+
261
+ function displayValue(value: unknown): string {
262
+ if (value == null) return "-";
263
+ if (value instanceof Date) return value.toISOString();
264
+ if (typeof value === "object") return JSON.stringify(value);
265
+ return String(value);
266
+ }
267
+
268
+ function renderRows(
269
+ rows: HeadlessPaneRow[],
270
+ ownColumns: HeadlessPaneColumn[] | undefined,
271
+ fallbackColumns: HeadlessPaneColumn[] | undefined,
272
+ ): string {
273
+ if (rows.length === 0) return "No data.";
274
+ const columns = columnsFor(rows, ownColumns, fallbackColumns);
275
+ return renderTable(
276
+ columns.map((column) => ({
277
+ header: column.header.toUpperCase(),
278
+ align: column.align,
279
+ width: column.width,
280
+ })),
281
+ rows.map((row) => columns.map((column) => {
282
+ const value = row[column.key];
283
+ return column.format ? column.format(value, row) : displayValue(value);
284
+ })),
285
+ );
286
+ }
287
+
288
+ function renderEntries(entries: HeadlessPaneEntry[]): string {
289
+ if (entries.length === 0) return "No data.";
290
+ return renderTable(
291
+ [{ header: "METRIC" }, { header: "VALUE" }],
292
+ entries.map((entry) => [entry.label, entry.formatted ?? displayValue(entry.value)]),
293
+ );
294
+ }
295
+
296
+ function renderBundle(
297
+ definition: HeadlessPaneDefinition,
298
+ result: HeadlessBundleResult,
299
+ ): string[] {
300
+ return result.sections.flatMap((section, index) => [
301
+ ...(index > 0 ? [""] : []),
302
+ renderSection(section.title),
303
+ "rows" in section && section.rows
304
+ ? renderRows(section.rows, section.columns, definition.columns)
305
+ : renderEntries(section.entries),
306
+ ]);
307
+ }
308
+
309
+ function renderSeries(result: HeadlessSeriesResult): string[] {
310
+ const rows = result.series.map((series) => {
311
+ const latest = [...series.points].reverse().find((point) => (
312
+ point.value != null || point.close != null
313
+ ));
314
+ return {
315
+ series: series.label,
316
+ latest: latest ? displayValue(latest.date) : "-",
317
+ value: latest?.value ?? latest?.close ?? null,
318
+ points: series.points.length,
319
+ };
320
+ });
321
+ const columns: HeadlessPaneColumn[] = [
322
+ { key: "series", header: "Series" },
323
+ { key: "latest", header: "Latest" },
324
+ { key: "value", header: "Value", align: "right" },
325
+ { key: "points", header: "Points", align: "right" },
326
+ ];
327
+ const lines = [renderRows(rows, columns, undefined)];
328
+ if (result.stats) {
329
+ const entries = Array.isArray(result.stats)
330
+ ? result.stats
331
+ : Object.entries(result.stats).map(([label, value]) => ({ label, value }));
332
+ lines.push("", renderSection("Statistics"), renderEntries(entries));
333
+ }
334
+ return lines;
335
+ }
336
+
337
+ function reportTitle(definition: HeadlessPaneDefinition, args: HeadlessPaneLoadArgs, fallback: string): string {
338
+ if (typeof definition.describe === "function") return definition.describe(args);
339
+ return definition.describe ?? fallback;
340
+ }
341
+
342
+ export function renderHeadlessPaneText(
343
+ definition: HeadlessPaneDefinition,
344
+ result: HeadlessPaneResult,
345
+ args: HeadlessPaneLoadArgs,
346
+ fallbackTitle: string,
347
+ ): string {
348
+ const lines = [reportTitle(definition, args, fallbackTitle), ""];
349
+ switch (definition.shape) {
350
+ case "rows": {
351
+ const rowsResult = result as HeadlessPaneResult & { rows: HeadlessPaneRow[]; columns?: HeadlessPaneColumn[] };
352
+ lines.push(renderRows(rowsResult.rows, rowsResult.columns, definition.columns));
353
+ break;
354
+ }
355
+ case "bundle":
356
+ lines.push(...renderBundle(definition, result as HeadlessBundleResult));
357
+ break;
358
+ case "series":
359
+ lines.push(...renderSeries(result as HeadlessSeriesResult));
360
+ break;
361
+ case "snapshot": {
362
+ const snapshot = result as HeadlessSnapshotResult;
363
+ lines.push(`As of: ${displayValue(snapshot.asOf)}`, "", renderRows(snapshot.items, undefined, definition.columns));
364
+ break;
365
+ }
366
+ default: {
367
+ const _exhaustive: never = definition.shape;
368
+ return _exhaustive;
369
+ }
370
+ }
371
+ if (result.errors?.length) lines.push("", `Errors: ${result.errors.join(" ")}`);
372
+ return lines.join("\n").trimEnd();
373
+ }
374
+
375
+ function resultRowCount(definition: HeadlessPaneDefinition, result: HeadlessPaneResult): number {
376
+ switch (definition.shape) {
377
+ case "rows":
378
+ return (result as { rows: HeadlessPaneRow[] }).rows.length;
379
+ case "bundle":
380
+ return (result as HeadlessBundleResult).sections.reduce((count, section) => (
381
+ count + ("rows" in section && section.rows ? section.rows.length : section.entries.length)
382
+ ), 0);
383
+ case "series":
384
+ return (result as HeadlessSeriesResult).series.reduce((count, series) => count + series.points.length, 0);
385
+ case "snapshot":
386
+ return (result as HeadlessSnapshotResult).items.length;
387
+ default: {
388
+ const _exhaustive: never = definition.shape;
389
+ return _exhaustive;
390
+ }
391
+ }
392
+ }
393
+
394
+ export async function buildHeadlessFunctionReport(
395
+ resolved: ResolvedPaneFunction,
396
+ context: MarketContext,
397
+ rawArgument: string,
398
+ ): Promise<PaneFunctionReport> {
399
+ const loaded = await loadResolvedHeadlessPaneModel(resolved, context, rawArgument);
400
+ const rowCount = resultRowCount(loaded.definition, loaded.result);
401
+ const unavailableSymbols = rowCount === 0 ? loaded.args.symbols : [];
402
+ const serialized = serializeHeadlessPaneResult(loaded.definition, loaded.result);
403
+ return {
404
+ data: {
405
+ kind: loaded.definition.shape,
406
+ target: resolved.token,
407
+ capabilityId: resolved.capability.id,
408
+ symbols: loaded.args.symbols,
409
+ options: resolved.options,
410
+ rowCount,
411
+ empty: rowCount === 0,
412
+ complete: unavailableSymbols.length === 0 && !loaded.result.errors?.length,
413
+ unavailableSymbols,
414
+ ...serialized,
415
+ },
416
+ text: renderHeadlessPaneText(
417
+ loaded.definition,
418
+ loaded.result,
419
+ loaded.args,
420
+ resolved.label,
421
+ ),
422
+ };
423
+ }
@@ -208,6 +208,15 @@ describe("pane function CLI args", () => {
208
208
  });
209
209
  });
210
210
 
211
+ test("enables live DOM screenshots without claiming report support for unmapped panes", () => {
212
+ expect(capabilityFor("new-api-pane")).toMatchObject({
213
+ id: "unverified-pane",
214
+ botSafe: false,
215
+ reportReadiness: "unsupported",
216
+ screenshotReadiness: "live-dom",
217
+ });
218
+ });
219
+
211
220
  test("rejects financial statement options on a price comparison", () => {
212
221
  const capability = capabilityFor("comparison-chart-pane");
213
222
  expect(() => normalizeCapabilityOptions(capability, {
@@ -37,12 +37,13 @@ async function withPaneRuntime<T>(
37
37
  registry: PaneFunctionCatalog;
38
38
  resolved: ResolvedPaneFunction;
39
39
  }) => Promise<T>,
40
+ settings: { strictHeadlessOptions?: boolean } = {},
40
41
  ): Promise<T> {
41
42
  const parsed = parsePaneFunctionArgs(args);
42
43
  return withMarketData(ctx, async (context) => {
43
44
  const registry = await createPaneCatalog(context, ctx.plugins);
44
45
  try {
45
- const resolved = await resolvePaneFunction(registry, context, parsed);
46
+ const resolved = await resolvePaneFunction(registry, context, parsed, settings);
46
47
  return await run({ parsed, context, registry, resolved });
47
48
  } finally {
48
49
  registry.destroy();
@@ -73,7 +74,7 @@ export async function runPaneFunction(args: string[], ctx: CliCommandContext) {
73
74
  ctx.printResult({ data: report.data }, {
74
75
  text: () => report.text,
75
76
  });
76
- });
77
+ }, { strictHeadlessOptions: true });
77
78
  });
78
79
  }
79
80
 
@@ -98,6 +99,9 @@ export async function runPaneScreenshot(args: string[], ctx: CliCommandContext)
98
99
  outputPath,
99
100
  width: parsed.width,
100
101
  height: parsed.height,
102
+ theme: parsed.theme,
103
+ scale: parsed.scale,
104
+ watermark: parsed.watermark,
101
105
  options: parsed.options,
102
106
  });
103
107
  if (parsed.requireBotSafe && !result.usable) {
@@ -23,6 +23,12 @@ export interface ParsedPaneFunctionArgs {
23
23
  outputPath: string | null;
24
24
  width: number;
25
25
  height: number;
26
+ /** Theme id override for screenshots; null keeps the configured theme. */
27
+ theme: string | null;
28
+ /** Render scale: 1.5 renders two thirds as many cells at 1.5x size, so text reads larger at the same pixel size. */
29
+ scale: number;
30
+ /** Small label drawn in the pane title bar, e.g. a domain; null draws nothing. */
31
+ watermark: string | null;
26
32
  requireBotSafe: boolean;
27
33
  }
28
34
 
@@ -62,6 +68,9 @@ export function parsePaneFunctionArgs(args: string[]): ParsedPaneFunctionArgs {
62
68
  let outputPath: string | null = null;
63
69
  let width = DEFAULT_SHOT_WIDTH;
64
70
  let height = DEFAULT_SHOT_HEIGHT;
71
+ let theme: string | null = null;
72
+ let scale = 1;
73
+ let watermark: string | null = null;
65
74
  let requireBotSafe = false;
66
75
 
67
76
  for (let index = 0; index < args.length; index += 1) {
@@ -98,6 +107,12 @@ export function parsePaneFunctionArgs(args: string[]): ParsedPaneFunctionArgs {
98
107
  if (Number.isFinite(parsedHeight)) {
99
108
  height = Math.max(MIN_SHOT_HEIGHT, Math.min(MAX_SHOT_HEIGHT, Math.round(parsedHeight)));
100
109
  }
110
+ } else if (normalizedKey === "theme" && value !== true) {
111
+ theme = value.trim() || null;
112
+ } else if (normalizedKey === "scale" && value !== true) {
113
+ scale = parseScale(value);
114
+ } else if (normalizedKey === "watermark" && value !== true) {
115
+ watermark = value.trim() || null;
101
116
  } else if (normalizedKey === "arguments" && value !== true) {
102
117
  Object.assign(options, parseArgumentsOption(value));
103
118
  } else {
@@ -107,7 +122,7 @@ export function parsePaneFunctionArgs(args: string[]): ParsedPaneFunctionArgs {
107
122
 
108
123
  const target = positionals[0]?.trim() ?? "";
109
124
  const arg = positionals.slice(1).join(" ").trim();
110
- return { target, arg, options, outputPath, width, height, requireBotSafe };
125
+ return { target, arg, options, outputPath, width, height, theme, scale, watermark, requireBotSafe };
111
126
  }
112
127
 
113
128
  export function parsePaneCatalogArgs(args: string[]): ParsedPaneCatalogArgs {
@@ -165,7 +180,17 @@ export function optionString(options: PaneOptionValues, key: string): string | u
165
180
  return value === true || value === undefined ? undefined : String(value);
166
181
  }
167
182
 
168
- const RESERVED_OPTION_KEYS = new Set(["output", "out", "o", "width", "height", "arguments", "state"]);
183
+ const RESERVED_OPTION_KEYS = new Set([
184
+ "output", "out", "o", "width", "height", "theme", "scale", "watermark", "arguments", "state",
185
+ ]);
186
+
187
+ function parseScale(value: string): number {
188
+ const parsed = Number.parseFloat(value);
189
+ if (!Number.isFinite(parsed) || parsed < 0.5 || parsed > 4) {
190
+ throw new Error(`--scale must be a number between 0.5 and 4, got "${value}".`);
191
+ }
192
+ return parsed;
193
+ }
169
194
 
170
195
  export function optionSettings(options: Record<string, string | true>): Record<string, unknown> {
171
196
  const settings: Record<string, unknown> = {};
@@ -44,6 +44,7 @@ import {
44
44
  withShotPriceHistory,
45
45
  } from "./data";
46
46
  import type { ResolvedPaneFunction } from "./resolver";
47
+ import { buildHeadlessFunctionReport } from "./headless";
47
48
 
48
49
  export interface PaneFunctionReportData {
49
50
  kind: string;
@@ -750,6 +751,9 @@ export async function buildFunctionReport(
750
751
  context: MarketContext,
751
752
  rawArg: string,
752
753
  ): Promise<PaneFunctionReport> {
754
+ if (resolved.headless) {
755
+ return buildHeadlessFunctionReport(resolved, context, rawArg);
756
+ }
753
757
  if (isFinancialAnalysisFunction(resolved)) {
754
758
  return buildFinancialStatementReport(resolved, context, rawArg);
755
759
  }
@@ -1,6 +1,11 @@
1
1
  import type { PaneInstanceConfig } from "../../types/config";
2
2
  import { CHART_COMPOSER_PANE_ID } from "../../types/config";
3
- import type { PaneDef, PaneTemplateCreateOptions, PaneTemplateDef } from "../../types/plugin";
3
+ import type {
4
+ HeadlessPaneDefinition,
5
+ PaneDef,
6
+ PaneTemplateCreateOptions,
7
+ PaneTemplateDef,
8
+ } from "../../types/plugin";
4
9
  import { applyChartComposerCapabilityOptions } from "../../plugins/builtin/chart-composer/cli-options";
5
10
  import { parseChartSpec } from "../../plugins/builtin/chart-composer/chart-spec";
6
11
  import { normalizeTickerInput } from "../../tickers/search";
@@ -20,6 +25,7 @@ import {
20
25
  } from "./options";
21
26
  import {
22
27
  capabilityPaneSettings,
28
+ getHeadlessPaneDefinition,
23
29
  getPaneFunctionCapability,
24
30
  normalizeCapabilityOptions,
25
31
  type NormalizedPaneFunctionOptions,
@@ -33,6 +39,7 @@ export interface ResolvedPaneFunction {
33
39
  shortcut?: string;
34
40
  pane: PaneDef;
35
41
  template?: PaneTemplateDef;
42
+ headless?: HeadlessPaneDefinition;
36
43
  instance: PaneInstanceConfig;
37
44
  createOptions: PaneTemplateCreateOptions | undefined;
38
45
  optionSettings: Record<string, unknown>;
@@ -41,16 +48,24 @@ export interface ResolvedPaneFunction {
41
48
  }
42
49
 
43
50
  async function buildPaneInstance(
44
- resolved: Pick<ResolvedPaneFunction, "pane" | "template" | "createOptions" | "optionSettings" | "capability" | "options">,
51
+ resolved: Pick<ResolvedPaneFunction, "pane" | "template" | "headless" | "createOptions" | "optionSettings" | "capability" | "options">,
45
52
  context: MarketContext,
46
53
  target: string,
47
54
  ): Promise<PaneInstanceConfig> {
48
- // A text argument is a phrase, not a symbol: uppercasing it and binding it as
49
- // a ticker makes the pane look for a provider for "HIGH GROWTH SOFTWARE".
50
- const argKind = resolved.template?.shortcut?.argKind;
55
+ // Free text is a phrase, not a symbol. Treating it as a ticker makes the
56
+ // pane look for a provider for values such as "HIGH GROWTH SOFTWARE".
57
+ const shortcutArgKind = resolved.template?.shortcut?.argKind;
58
+ const headlessArgKind = resolved.headless?.argument.kind;
59
+ const fallbackSymbol = headlessArgKind === "free-text" || headlessArgKind === "none"
60
+ ? null
61
+ : headlessArgKind === "tickers" || headlessArgKind === "symbol-list"
62
+ ? normalizeTickerInput(null, cleanTickerInput(target.split(/[,\n]/)[0] ?? ""))
63
+ : shortcutArgKind === "text"
64
+ ? null
65
+ : normalizeTickerInput(null, cleanTickerInput(target));
51
66
  const primarySymbol = resolved.createOptions?.symbol
52
67
  ?? resolved.createOptions?.symbols?.[0]
53
- ?? (argKind === "text" ? null : normalizeTickerInput(null, cleanTickerInput(target)));
68
+ ?? fallbackSymbol;
54
69
  const templateContext = buildTemplateContext(context, primarySymbol ?? null);
55
70
  const spec = await resolved.template?.createInstance?.(templateContext, resolved.createOptions) ?? {};
56
71
  const instanceId = `${resolved.pane.id}:cli-${slugifyName(target || resolved.pane.id, "pane")}`;
@@ -83,6 +98,7 @@ export async function resolvePaneFunction(
83
98
  registry: PaneFunctionCatalog,
84
99
  context: MarketContext,
85
100
  args: ParsedPaneFunctionArgs,
101
+ resolutionSettings: { strictHeadlessOptions?: boolean } = {},
86
102
  ): Promise<ResolvedPaneFunction> {
87
103
  if (!args.target) {
88
104
  throw new Error("Usage: gloomberb fn <function-or-pane> [argument] [--key value]");
@@ -106,9 +122,10 @@ export async function resolvePaneFunction(
106
122
  throw new Error(`Template "${template?.id}" points at missing pane "${template?.paneId}".`);
107
123
  }
108
124
  const createOptions = buildCreateOptions(template, args.arg);
125
+ const headless = getHeadlessPaneDefinition(template, pane);
109
126
  const capability = getPaneFunctionCapability(template, pane);
110
127
  const normalizedOptions = normalizeCapabilityOptions(capability, args.options, {
111
- strict: args.requireBotSafe,
128
+ strict: args.requireBotSafe || (!!headless && resolutionSettings.strictHeadlessOptions === true),
112
129
  });
113
130
  const settings = capability.botSafe
114
131
  ? {
@@ -126,13 +143,14 @@ export async function resolvePaneFunction(
126
143
  shortcut: template?.shortcut?.prefix,
127
144
  pane,
128
145
  template,
146
+ ...(headless ? { headless } : {}),
129
147
  instance: {} as PaneInstanceConfig,
130
148
  createOptions,
131
149
  optionSettings: settings,
132
150
  capability,
133
151
  options: normalizedOptions,
134
152
  };
135
- resolved.instance = await buildPaneInstance(resolved, context, args.arg || pane.id);
153
+ resolved.instance = await buildPaneInstance(resolved, context, args.arg);
136
154
  return resolved;
137
155
  }
138
156