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
@@ -3,12 +3,15 @@ import type { RemoteUiNodeSnapshot } from "../../remote/types";
3
3
  import {
4
4
  chartSeriesEvidenceWithinRange,
5
5
  chartEvidenceMismatchesFor,
6
+ isPaneScreenshotUsable,
6
7
  missingActiveTabSelections,
8
+ resolveDesktopShotApiProxy,
7
9
  shotDataEvidenceFor,
8
10
  shotExpectedText,
9
11
  shotPriceHistoryRange,
10
12
  shotSemanticRowCount,
11
13
  shotUnavailableSymbols,
14
+ stripDesktopShotCredentials,
12
15
  type PaneScreenshotExpectedChartEvidence,
13
16
  type PaneScreenshotExpectedSelection,
14
17
  } from "./screenshot";
@@ -18,6 +21,79 @@ import type { ResolvedPaneFunction } from "./resolver";
18
21
  import { buildCustomChartPreset } from "../../plugins/builtin/chart-composer/presets";
19
22
  import { CHART_COMPOSER_PANE_ID } from "../../types/config";
20
23
 
24
+ describe("pane screenshot rendered readiness", () => {
25
+ const rendered = {
26
+ rowCount: 2,
27
+ loadingStateDetected: false,
28
+ errorStateDetected: false,
29
+ emptyStateDetected: false,
30
+ complete: true,
31
+ semanticMismatch: false,
32
+ requiresStructuredDataEvidence: false,
33
+ hasStructuredDataEvidence: false,
34
+ };
35
+
36
+ test("requires rows without loading, error, or empty states", () => {
37
+ expect(isPaneScreenshotUsable(rendered)).toBe(true);
38
+ expect(isPaneScreenshotUsable({ ...rendered, rowCount: 0 })).toBe(false);
39
+ expect(isPaneScreenshotUsable({ ...rendered, loadingStateDetected: true })).toBe(false);
40
+ expect(isPaneScreenshotUsable({ ...rendered, errorStateDetected: true })).toBe(false);
41
+ expect(isPaneScreenshotUsable({ ...rendered, emptyStateDetected: true })).toBe(false);
42
+ });
43
+
44
+ test("preserves mapped capability completeness and evidence checks", () => {
45
+ expect(isPaneScreenshotUsable({ ...rendered, complete: false })).toBe(false);
46
+ expect(isPaneScreenshotUsable({ ...rendered, semanticMismatch: true })).toBe(false);
47
+ expect(isPaneScreenshotUsable({
48
+ ...rendered,
49
+ requiresStructuredDataEvidence: true,
50
+ })).toBe(false);
51
+ expect(isPaneScreenshotUsable({
52
+ ...rendered,
53
+ requiresStructuredDataEvidence: true,
54
+ hasStructuredDataEvidence: true,
55
+ })).toBe(true);
56
+ });
57
+ });
58
+
59
+ describe("pane screenshot payload credentials", () => {
60
+ test("keeps the restored session in the proxy and strips credential fields from page data", () => {
61
+ const sessionToken = "private-shot-session";
62
+ const proxy = resolveDesktopShotApiProxy({
63
+ persistence: {
64
+ pluginState: {
65
+ get: (_pluginId: string, key: string) => key === "resume:session"
66
+ ? { value: { sessionToken }, schemaVersion: 1, updatedAt: 1 }
67
+ : null,
68
+ },
69
+ },
70
+ } as any);
71
+ const payload = stripDesktopShotCredentials({
72
+ config: {
73
+ theme: "tokyo",
74
+ pluginConfig: {
75
+ service: { apiKey: "private-api-key", display: "compact" },
76
+ },
77
+ brokerInstances: [{ config: { password: "private-password", region: "US" } }],
78
+ },
79
+ paneState: {
80
+ pane: { pluginState: { service: { accessToken: "private-access", tab: "latest" } } },
81
+ },
82
+ });
83
+
84
+ expect(proxy.sessionToken).toBe(sessionToken);
85
+ expect(payload).toEqual({
86
+ config: {
87
+ theme: "tokyo",
88
+ pluginConfig: { service: { display: "compact" } },
89
+ brokerInstances: [{ config: { region: "US" } }],
90
+ },
91
+ paneState: { pane: { pluginState: { service: { tab: "latest" } } } },
92
+ });
93
+ expect(JSON.stringify(payload)).not.toContain("private-");
94
+ });
95
+ });
96
+
21
97
  describe("pane screenshot active-state verification", () => {
22
98
  const expected: PaneScreenshotExpectedSelection[] = [
23
99
  { control: "statement", label: "Cash Flow" },
@@ -1,12 +1,16 @@
1
1
  import { dirname, resolve } from "path";
2
2
  import { mkdir } from "fs/promises";
3
3
  import type { PaneRuntimeState } from "../../core/state/app/state";
4
- import { CHART_COMPOSER_PANE_ID } from "../../types/config";
4
+ import { CHART_COMPOSER_PANE_ID, type AppConfig } from "../../types/config";
5
5
  import type { OptionsChain, PricePoint, TickerFinancials } from "../../types/financials";
6
6
  import type { TickerRecord } from "../../types/ticker";
7
7
  import { slugifyName } from "../../utils/slugify";
8
+ import { getTheme, getThemeIds } from "../../theme/themes";
9
+
10
+ const DEFAULT_SHOT_DEVICE_SCALE_FACTOR = 2;
8
11
  import {
9
12
  renderDesktopPaneScreenshot,
13
+ type DesktopPaneShotApiProxy,
10
14
  type DesktopPaneShotPayload,
11
15
  type DesktopPaneShotRenderResult,
12
16
  } from "../desktop-pane-shot";
@@ -39,8 +43,16 @@ import {
39
43
  getVisibleWindowForDateRange,
40
44
  } from "../../components/chart/core/date-window";
41
45
  import { parseChartSpec } from "../../plugins/builtin/chart-composer/chart-spec";
46
+ import {
47
+ defaultValuationSeriesLoader,
48
+ requiredSeries as valuationRequiredSeries,
49
+ } from "../../plugins/builtin/market-valuation/client";
50
+ import type { DatedObservation } from "../../plugins/builtin/market-valuation/series";
51
+ import { defaultStatLoader } from "../../plugins/builtin/econ-statistics/client";
52
+ import { STATS } from "../../plugins/builtin/econ-statistics/stats";
42
53
  import { publicTickerKey } from "../../utils/exchanges";
43
54
  import { apiClient } from "../../api-client";
55
+ import { getCloudApiBaseUrl } from "../../api-client/request";
44
56
  import type { FredSeriesCacheEntry } from "../../data/fred-series";
45
57
  import type { ResolvedSeries } from "../../time-series/types";
46
58
  import { chartSeriesSourceKey, createChartSeriesResolver } from "../../capabilities";
@@ -57,6 +69,102 @@ import {
57
69
  const DESKTOP_CELL_WIDTH_PX = 8;
58
70
  const DESKTOP_CELL_HEIGHT_PX = 18;
59
71
  const OPTIONS_PANE_ID = "options";
72
+ const MARKET_VALUATION_PANE_ID = "market-valuation";
73
+ const ECON_STATISTICS_PANE_ID = "econ-statistics";
74
+ const CLOUD_PLUGIN_ID = "gloomberb-cloud";
75
+ const CLOUD_SESSION_KEYS = ["resume:session", "session"] as const;
76
+ const CREDENTIAL_FIELD_NAMES = new Set([
77
+ "accesstoken",
78
+ "accessurl",
79
+ "apikey",
80
+ "apisecret",
81
+ "authorization",
82
+ "cookie",
83
+ "credentials",
84
+ "oauth",
85
+ "passphrase",
86
+ "password",
87
+ "privatekey",
88
+ "refreshtoken",
89
+ "secret",
90
+ "sessiontoken",
91
+ "token",
92
+ ]);
93
+
94
+ interface PersistedShotCloudSession {
95
+ sessionToken?: unknown;
96
+ }
97
+
98
+ /**
99
+ * The proxy receives the desktop session out of band. Keeping this separate
100
+ * from DesktopPaneShotPayload prevents the token from entering the page,
101
+ * semantic evidence, or JSON CLI output.
102
+ */
103
+ function isCredentialField(key: string): boolean {
104
+ const normalized = key.replace(/[^a-z0-9]/gi, "").toLowerCase();
105
+ return CREDENTIAL_FIELD_NAMES.has(normalized)
106
+ || normalized.endsWith("password")
107
+ || normalized.endsWith("passphrase")
108
+ || normalized.endsWith("secret")
109
+ || normalized.endsWith("token")
110
+ || normalized.endsWith("credential")
111
+ || normalized.endsWith("cookie")
112
+ || normalized.endsWith("apikey")
113
+ || normalized.endsWith("accesskey")
114
+ || normalized.endsWith("privatekey")
115
+ || normalized.endsWith("authorization")
116
+ || normalized.endsWith("accessurl");
117
+ }
118
+
119
+ /** Removes credential-shaped object fields before data enters the page or JSON output. */
120
+ export function stripDesktopShotCredentials<T>(value: T): T {
121
+ if (Array.isArray(value)) {
122
+ return value.map((entry) => stripDesktopShotCredentials(entry)) as T;
123
+ }
124
+ if (!value || typeof value !== "object" || value instanceof Date) return value;
125
+ const clean: Record<string, unknown> = {};
126
+ for (const [key, entry] of Object.entries(value as Record<string, unknown>)) {
127
+ if (isCredentialField(key)) continue;
128
+ clean[key] = stripDesktopShotCredentials(entry);
129
+ }
130
+ return clean as T;
131
+ }
132
+
133
+ export function resolveDesktopShotApiProxy(
134
+ context: Pick<MarketContext, "persistence">,
135
+ ): DesktopPaneShotApiProxy {
136
+ let sessionToken: string | null = null;
137
+ for (const key of CLOUD_SESSION_KEYS) {
138
+ const value = context.persistence.pluginState.get<PersistedShotCloudSession>(
139
+ CLOUD_PLUGIN_ID,
140
+ key,
141
+ 1,
142
+ )?.value;
143
+ if (typeof value?.sessionToken === "string" && value.sessionToken.length > 0) {
144
+ sessionToken = value.sessionToken;
145
+ break;
146
+ }
147
+ }
148
+ return {
149
+ baseUrl: getCloudApiBaseUrl(),
150
+ sessionToken,
151
+ };
152
+ }
153
+
154
+ /** Same reason as the valuation legs: the renderer cannot fill its own cache. */
155
+ async function collectShotStatSeries(
156
+ resolved: ResolvedPaneFunction,
157
+ ): Promise<Array<[string, DatedObservation[]]>> {
158
+ if (resolved.pane.id !== ECON_STATISTICS_PANE_ID) return [];
159
+ const loaded = await Promise.all(STATS.map(async (def) => {
160
+ try {
161
+ return [def.seriesId, await defaultStatLoader(def)] as [string, DatedObservation[]];
162
+ } catch {
163
+ return null;
164
+ }
165
+ }));
166
+ return loaded.filter((entry): entry is [string, DatedObservation[]] => !!entry);
167
+ }
60
168
 
61
169
  async function collectShotFredSeries(
62
170
  resolved: ResolvedPaneFunction,
@@ -81,6 +189,25 @@ async function collectShotFredSeries(
81
189
  return loaded.filter((entry): entry is [string, FredSeriesCacheEntry] => !!entry);
82
190
  }
83
191
 
192
+ /**
193
+ * The pane reads its legs from a client cache the shot renderer cannot fill itself,
194
+ * so fetch them here on the Bun side and hand them over with the payload.
195
+ */
196
+ async function collectShotValuationSeries(
197
+ resolved: ResolvedPaneFunction,
198
+ ): Promise<Array<[string, DatedObservation[]]>> {
199
+ if (resolved.pane.id !== MARKET_VALUATION_PANE_ID) return [];
200
+ const loaded = await Promise.all(valuationRequiredSeries().map(async (def) => {
201
+ try {
202
+ const series = await defaultValuationSeriesLoader(def);
203
+ return [def.key, series.observations] as [string, DatedObservation[]];
204
+ } catch {
205
+ return null;
206
+ }
207
+ }));
208
+ return loaded.filter((entry): entry is [string, DatedObservation[]] => !!entry);
209
+ }
210
+
84
211
  async function collectShotCapabilitySeries(
85
212
  resolved: ResolvedPaneFunction,
86
213
  ): Promise<Array<[string, ResolvedSeries]>> {
@@ -196,6 +323,27 @@ export type PaneScreenshotDataEvidence =
196
323
  | PaneScreenshotFundamentalSeriesEvidence
197
324
  | PaneScreenshotFinancialStatementEvidence;
198
325
 
326
+ export interface PaneScreenshotReadinessSignals {
327
+ rowCount: number;
328
+ loadingStateDetected: boolean;
329
+ errorStateDetected: boolean;
330
+ emptyStateDetected: boolean;
331
+ complete: boolean;
332
+ semanticMismatch: boolean;
333
+ requiresStructuredDataEvidence: boolean;
334
+ hasStructuredDataEvidence: boolean;
335
+ }
336
+
337
+ export function isPaneScreenshotUsable(signals: PaneScreenshotReadinessSignals): boolean {
338
+ return signals.rowCount > 0
339
+ && !signals.loadingStateDetected
340
+ && !signals.errorStateDetected
341
+ && !signals.emptyStateDetected
342
+ && signals.complete
343
+ && !signals.semanticMismatch
344
+ && (!signals.requiresStructuredDataEvidence || signals.hasStructuredDataEvidence);
345
+ }
346
+
199
347
  export interface PaneScreenshotResult {
200
348
  kind: "pane-screenshot";
201
349
  target: string;
@@ -238,9 +386,20 @@ async function buildDesktopShotPayload(
238
386
  options: Record<string, string | true>,
239
387
  widthPx: number,
240
388
  heightPx: number,
389
+ theme: string | null,
390
+ scale: number,
391
+ watermark: string | null,
241
392
  ): Promise<DesktopPaneShotPayload> {
242
- const widthCells = Math.max(1, Math.round(widthPx / DESKTOP_CELL_WIDTH_PX));
243
- const heightCells = Math.max(1, Math.round(heightPx / DESKTOP_CELL_HEIGHT_PX));
393
+ // The requested size is the output size. Scale shrinks the CSS viewport and
394
+ // raises the device scale factor by the same factor, so a 1200px wide shot at
395
+ // scale 1.5 lays out 100 cells instead of 150 and every glyph is 1.5x larger.
396
+ // Snap to whole cells: rounding up used to make the pane a few pixels taller
397
+ // than the capture, which clipped the bottom axis of charts.
398
+ const widthCells = Math.max(1, Math.floor(widthPx / scale / DESKTOP_CELL_WIDTH_PX));
399
+ const heightCells = Math.max(1, Math.floor(heightPx / scale / DESKTOP_CELL_HEIGHT_PX));
400
+ widthPx = widthCells * DESKTOP_CELL_WIDTH_PX;
401
+ heightPx = heightCells * DESKTOP_CELL_HEIGHT_PX;
402
+ const deviceScaleFactor = DEFAULT_SHOT_DEVICE_SCALE_FACTOR * scale;
244
403
  const initialPaneState = optionPaneState(resolved.options);
245
404
  const pluginState = capabilityPluginState(resolved.capability, resolved.options);
246
405
  if (Object.keys(pluginState).length > 0) {
@@ -252,9 +411,9 @@ async function buildDesktopShotPayload(
252
411
  if (isFinancialAnalysisFunction(resolved) && !initialPaneState.activeTabId) {
253
412
  initialPaneState.activeTabId = "financials";
254
413
  }
255
- const paneState: Record<string, PaneRuntimeState> = {
414
+ const paneState = stripDesktopShotCredentials<Record<string, PaneRuntimeState>>({
256
415
  [resolved.instance.instanceId]: initialPaneState,
257
- };
416
+ });
258
417
  const layout = {
259
418
  dockRoot: null,
260
419
  instances: [resolved.instance],
@@ -268,8 +427,9 @@ async function buildDesktopShotPayload(
268
427
  }],
269
428
  detached: [],
270
429
  };
271
- const config = {
430
+ const config = stripDesktopShotCredentials<AppConfig>({
272
431
  ...context.config,
432
+ ...(theme ? { theme: resolveShotTheme(theme) } : {}),
273
433
  layout,
274
434
  layouts: [{
275
435
  name: "CLI Shot",
@@ -280,14 +440,16 @@ async function buildDesktopShotPayload(
280
440
  }],
281
441
  activeLayoutIndex: 0,
282
442
  onboardingComplete: true,
283
- };
443
+ });
284
444
 
285
445
  const tickers: TickerRecord[] = [];
286
446
  const financials: Array<[string, TickerFinancials]> = [];
287
447
  const optionsChains: Array<[string, OptionsChain]> = [];
288
- const [fredSeries, capabilitySeries] = await Promise.all([
448
+ const [fredSeries, capabilitySeries, valuationSeries, statSeries] = await Promise.all([
289
449
  collectShotFredSeries(resolved),
290
450
  collectShotCapabilitySeries(resolved),
451
+ collectShotValuationSeries(resolved),
452
+ collectShotStatSeries(resolved),
291
453
  ]);
292
454
  const includeOptionsChains = resolved.pane.id === OPTIONS_PANE_ID || resolved.template?.paneId === OPTIONS_PANE_ID;
293
455
  for (const symbol of collectShotSymbols(resolved, rawArg)) {
@@ -327,15 +489,30 @@ async function buildDesktopShotPayload(
327
489
  heightCells,
328
490
  widthPx,
329
491
  heightPx,
492
+ deviceScaleFactor,
493
+ watermark,
330
494
  tickers,
331
495
  financials,
332
496
  optionsChains,
333
497
  fredSeries,
498
+ valuationSeries,
499
+ statSeries,
334
500
  capabilitySeries,
335
501
  paneState,
336
502
  };
337
503
  }
338
504
 
505
+ function resolveShotTheme(requested: string): string {
506
+ const normalized = requested.trim().toLowerCase().replace(/[\s_]+/g, "-");
507
+ const ids = getThemeIds();
508
+ const match = ids.find((id) => id.toLowerCase() === normalized)
509
+ ?? ids.find((id) => getTheme(id).name.toLowerCase().replace(/[\s_]+/g, "-") === normalized);
510
+ if (!match) {
511
+ throw new Error(`Unknown theme "${requested}". Available themes: ${ids.join(", ")}`);
512
+ }
513
+ return match;
514
+ }
515
+
339
516
  export function shotPriceHistoryRange(resolved: ResolvedPaneFunction): TimeRange | null {
340
517
  switch (resolved.capability.id) {
341
518
  case "chart-composer":
@@ -364,6 +541,9 @@ export async function renderDesktopShot({
364
541
  outputPath,
365
542
  width,
366
543
  height,
544
+ theme,
545
+ scale,
546
+ watermark,
367
547
  options,
368
548
  }: {
369
549
  resolved: ResolvedPaneFunction;
@@ -372,14 +552,41 @@ export async function renderDesktopShot({
372
552
  outputPath: string;
373
553
  width: number;
374
554
  height: number;
555
+ theme?: string | null;
556
+ scale?: number;
557
+ watermark?: string | null;
375
558
  options: Record<string, string | true>;
376
559
  }): Promise<PaneScreenshotResult> {
377
560
  await mkdir(dirname(outputPath), { recursive: true });
378
- const payload = await buildDesktopShotPayload(resolved, context, rawArg, options, width, height);
379
- const render = await renderDesktopPaneScreenshot(payload, outputPath);
561
+ const apiProxy = resolveDesktopShotApiProxy(context);
562
+ const previousSessionToken = apiClient.getSessionToken();
563
+ let payload: DesktopPaneShotPayload;
564
+ let render: DesktopPaneShotRenderResult;
565
+ apiClient.setSessionToken(apiProxy.sessionToken);
566
+ try {
567
+ payload = await buildDesktopShotPayload(
568
+ resolved,
569
+ context,
570
+ rawArg,
571
+ options,
572
+ width,
573
+ height,
574
+ theme ?? null,
575
+ scale ?? 1,
576
+ watermark ?? null,
577
+ );
578
+ render = await renderDesktopPaneScreenshot(payload, outputPath, apiProxy);
579
+ } finally {
580
+ apiClient.setSessionToken(previousSessionToken);
581
+ }
380
582
  const symbols = payload.financials.map(([symbol]) => symbol);
381
- const rowCount = shotSemanticRowCount(resolved, payload, render.semanticUi);
382
- const unavailableSymbols = shotUnavailableSymbols(resolved, payload, render.semanticUi);
583
+ const usesLiveDomEvidence = resolved.capability.screenshotReadiness === "live-dom";
584
+ const rowCount = usesLiveDomEvidence
585
+ ? render.rows.length
586
+ : shotSemanticRowCount(resolved, payload, render.semanticUi);
587
+ const unavailableSymbols = usesLiveDomEvidence
588
+ ? []
589
+ : shotUnavailableSymbols(resolved, payload, render.semanticUi);
383
590
  const complete = unavailableSymbols.length === 0;
384
591
  const expectedText = shotExpectedText(resolved, symbols, payload);
385
592
  const normalizedVisibleText = render.visibleText.toLowerCase();
@@ -397,12 +604,21 @@ export async function renderDesktopShot({
397
604
  const semanticMismatch = missingExpectedText.length > 0
398
605
  || missingExpectedSelections.length > 0
399
606
  || chartEvidenceMismatches.length > 0;
400
- const empty = render.emptyStateDetected || rowCount === 0 || semanticMismatch;
401
- const usable = resolved.capability.botSafe
402
- && resolved.capability.screenshotReadiness === "ready"
403
- && !empty
404
- && complete
405
- && (!requiresStructuredDataEvidence(resolved) || dataEvidence !== null);
607
+ const empty = render.loadingStateDetected
608
+ || render.errorStateDetected
609
+ || render.emptyStateDetected
610
+ || rowCount === 0
611
+ || semanticMismatch;
612
+ const usable = isPaneScreenshotUsable({
613
+ rowCount,
614
+ loadingStateDetected: render.loadingStateDetected,
615
+ errorStateDetected: render.errorStateDetected,
616
+ emptyStateDetected: render.emptyStateDetected,
617
+ complete,
618
+ semanticMismatch,
619
+ requiresStructuredDataEvidence: requiresStructuredDataEvidence(resolved),
620
+ hasStructuredDataEvidence: dataEvidence !== null,
621
+ });
406
622
  return {
407
623
  kind: "pane-screenshot",
408
624
  target: resolved.token,
@@ -414,7 +630,7 @@ export async function renderDesktopShot({
414
630
  screenshotReadiness: resolved.capability.screenshotReadiness,
415
631
  },
416
632
  symbols,
417
- options: resolved.options,
633
+ options: stripDesktopShotCredentials(resolved.options),
418
634
  rowCount,
419
635
  empty,
420
636
  complete,
@@ -424,7 +640,7 @@ export async function renderDesktopShot({
424
640
  dataEvidence,
425
641
  outputPath,
426
642
  render: {
427
- ...render,
643
+ ...stripDesktopShotCredentials(render),
428
644
  expectedText,
429
645
  missingExpectedText,
430
646
  expectedSelections,
@@ -4,7 +4,6 @@ import type {
4
4
  CliCommandDef,
5
5
  CliDispatchResult,
6
6
  GloomPlugin,
7
- PluginCliCommandDescriptor,
8
7
  } from "../types/plugin";
9
8
  import type { LoadedExternalPlugin } from "../plugins/loader";
10
9
  import { getPluginCatalog } from "../plugins/catalog";
@@ -35,7 +34,6 @@ export interface CliCommandRegistry {
35
34
  config: AppConfig | null;
36
35
  plugins: GloomPlugin[];
37
36
  externalPlugins: LoadedExternalPlugin[];
38
- descriptors: Array<{ pluginId: string; descriptor: PluginCliCommandDescriptor }>;
39
37
  }
40
38
 
41
39
  interface BuildCliCommandRegistryOptions {
@@ -71,19 +69,6 @@ function getCommandUsageLabel(command: CliCommandDef): string {
71
69
  return command.help?.usage?.[0] ?? command.name;
72
70
  }
73
71
 
74
- function descriptorToCommand(descriptor: PluginCliCommandDescriptor): CliCommandDef | null {
75
- if (!descriptor.execute) return null;
76
- return {
77
- name: descriptor.name,
78
- aliases: descriptor.aliases,
79
- description: descriptor.summary,
80
- help: descriptor.examples?.length
81
- ? { usage: descriptor.examples }
82
- : undefined,
83
- execute: descriptor.execute,
84
- };
85
- }
86
-
87
72
  function renderHelpSections(registry: CliCommandRegistry): string[] {
88
73
  const lines: string[] = [];
89
74
 
@@ -172,7 +157,6 @@ export function buildCliCommandRegistry({
172
157
  }
173
158
 
174
159
  const loadablePlugins: GloomPlugin[] = [];
175
- const descriptors: Array<{ pluginId: string; descriptor: PluginCliCommandDescriptor }> = [];
176
160
  for (const entry of catalog) {
177
161
  if (entry.error) {
178
162
  registryLog.warn(`Skipping external plugin "${entry.plugin.id}" for CLI registration.`, {
@@ -185,11 +169,6 @@ export function buildCliCommandRegistry({
185
169
  for (const command of entry.plugin.cliCommands ?? []) {
186
170
  registerCommand(command, entry.plugin.id, "plugin");
187
171
  }
188
- for (const descriptor of entry.plugin.cli?.commands ?? []) {
189
- descriptors.push({ pluginId: entry.plugin.id, descriptor });
190
- const command = descriptorToCommand(descriptor);
191
- if (command) registerCommand(command, entry.plugin.id, "plugin");
192
- }
193
172
  }
194
173
 
195
174
  const commands = allCommands.filter((entry) => (
@@ -210,7 +189,6 @@ export function buildCliCommandRegistry({
210
189
  config,
211
190
  plugins: loadablePlugins.filter((plugin) => !disabledPlugins.has(plugin.id)),
212
191
  externalPlugins,
213
- descriptors,
214
192
  };
215
193
  }
216
194
 
@@ -0,0 +1,34 @@
1
+ import { saveConfig } from "../data/config/store";
2
+ import { loadCliConfigIfAvailable } from "./context";
3
+ import { seedExtractedPlugins } from "../plugins/seed";
4
+ import { debugLog } from "../utils/debug-log";
5
+
6
+ const log = debugLog.createLogger("plugin-seed");
7
+
8
+ /**
9
+ * Reinstalls plugins that moved out of this repository into their own.
10
+ *
11
+ * Runs before the plugin catalog is read so a restored plugin is available in
12
+ * the same session, and stays quiet on failure: an offline launch should start
13
+ * normally and try again next time, not block on a network request.
14
+ */
15
+ export async function restoreExtractedPlugins(): Promise<void> {
16
+ try {
17
+ const config = await loadCliConfigIfAvailable();
18
+ // No data directory yet means a first run: there is nothing to restore.
19
+ if (!config) return;
20
+ const { installPlugin } = await import("./commands/plugins");
21
+ const result = await seedExtractedPlugins(config, installPlugin);
22
+
23
+ const seeded = [...new Set(result.seeded)].sort();
24
+ const existing = [...new Set(config.seededPlugins ?? [])].sort();
25
+ if (seeded.join() === existing.join()) return;
26
+
27
+ await saveConfig({ ...config, seededPlugins: seeded });
28
+ if (result.installed.length > 0) {
29
+ log.info(`Restored ${result.installed.join(", ")} into ~/.gloomberb/plugins`);
30
+ }
31
+ } catch (error) {
32
+ log.error(`Plugin restore skipped: ${error}`);
33
+ }
34
+ }
package/src/cli/result.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { CliGlobalOptions } from "./options";
2
2
  import { renderTable, type CliTableColumn } from "../utils/cli-output";
3
+ import { serializeCsv } from "../utils/csv";
3
4
 
4
5
  export interface CliResult<T = unknown> {
5
6
  data: T;
@@ -57,11 +58,6 @@ function inferColumns(rows: Record<string, unknown>[]): CliResultColumn<Record<s
57
58
  return [...keys].map((key) => ({ key, header: key }));
58
59
  }
59
60
 
60
- function csvEscape(value: string): string {
61
- if (!/[",\n\r]/.test(value)) return value;
62
- return `"${value.replace(/"/g, '""')}"`;
63
- }
64
-
65
61
  function serializeColumns<Row>(columns?: CliResultColumn<Row>[]) {
66
62
  return columns?.map((column) => ({
67
63
  key: column.key,
@@ -78,11 +74,12 @@ function renderCsv<Row extends Record<string, unknown>>(
78
74
  const resolvedColumns = columns && columns.length > 0
79
75
  ? columns
80
76
  : inferColumns(rows as Record<string, unknown>[]) as CliResultColumn<Row>[];
81
- const header = resolvedColumns.map((column) => csvEscape(column.header)).join(",");
82
- const body = rows.map((row) => resolvedColumns
83
- .map((column) => csvEscape(normalizeCell(column.value ? column.value(row) : row[column.key])))
84
- .join(","));
85
- return [header, ...body].join("\n");
77
+ return serializeCsv([
78
+ resolvedColumns.map((column) => column.header),
79
+ ...rows.map((row) => resolvedColumns.map((column) => (
80
+ column.value ? column.value(row) : row[column.key]
81
+ ))),
82
+ ]);
86
83
  }
87
84
 
88
85
  function renderTextTable<Row extends Record<string, unknown>>(
@@ -4,9 +4,9 @@ import {
4
4
  UiHostProvider,
5
5
  type RendererHost,
6
6
  type UiHost,
7
- } from "../../../../ui";
8
- import { WebBox } from "../../../../renderers/electrobun/view/host/box";
9
- import { WebText } from "../../../../renderers/electrobun/view/host/text";
7
+ } from "../../../ui";
8
+ import { WebBox } from "../../../renderers/electrobun/view/host/box";
9
+ import { WebText } from "../../../renderers/electrobun/view/host/text";
10
10
  import { StaticXAxisLabels } from "./axis-overlays";
11
11
 
12
12
  const renderer: RendererHost = {
@@ -1,19 +1,14 @@
1
- import { Box, Text, useUiCapabilities, useUiHost } from "../../../../ui";
1
+ import { Box, Text, useUiCapabilities, useUiHost } from "../../../ui";
2
+ import type { CompositeChartXMarker } from "./types";
2
3
 
3
- export interface StaticChartXMarker {
4
- id: string;
5
- xRatio: number;
6
- label?: string;
7
- color?: string;
8
- lineChar?: string;
9
- }
4
+ export type StaticChartXMarker = CompositeChartXMarker;
10
5
 
11
6
  export interface StaticChartXAxisLabel {
12
7
  label: string;
13
8
  ratio: number;
14
9
  }
15
10
 
16
- export function clampRatio(value: number): number {
11
+ function clampRatio(value: number): number {
17
12
  if (!Number.isFinite(value)) return 0;
18
13
  return Math.max(0, Math.min(1, value));
19
14
  }
@@ -222,7 +217,7 @@ export function StaticXMarkerOverlay({
222
217
  height,
223
218
  fallbackColor,
224
219
  }: {
225
- markers: StaticChartXMarker[];
220
+ markers: readonly StaticChartXMarker[];
226
221
  width: number;
227
222
  height: number;
228
223
  fallbackColor?: string;
@@ -288,7 +283,7 @@ export function StaticXMarkerLabels({
288
283
  width,
289
284
  fallbackColor,
290
285
  }: {
291
- markers: StaticChartXMarker[];
286
+ markers: readonly StaticChartXMarker[];
292
287
  width: number;
293
288
  fallbackColor?: string;
294
289
  }) {