gloomberb 0.11.3 → 0.13.1

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 (574) hide show
  1. package/README.md +8 -4
  2. package/README.zh-CN.md +4 -2
  3. package/package.json +28 -2
  4. package/src/api-client/auth.ts +12 -1
  5. package/src/api-client/data.ts +103 -1
  6. package/src/api-client/index.test.ts +127 -0
  7. package/src/api-client/index.ts +139 -3
  8. package/src/api-client/normalizers.ts +39 -0
  9. package/src/api-client/paths.ts +85 -1
  10. package/src/api-client/research-activity.ts +61 -0
  11. package/src/api-client/types.ts +197 -0
  12. package/src/app/pane-runtime/index.tsx +15 -12
  13. package/src/app/pane-runtime/layout-placement.test.ts +27 -0
  14. package/src/app/pane-runtime/layout-placement.ts +13 -0
  15. package/src/app/pane-runtime/plugin-bindings.ts +62 -0
  16. package/src/app.tsx +60 -18
  17. package/src/architecture/browser-entry-graph.test.ts +96 -0
  18. package/src/brokers/profile-form.test.ts +25 -27
  19. package/src/brokers/test-broker.ts +68 -0
  20. package/src/cli/commands/plugins.ts +43 -14
  21. package/src/cli/desktop-pane-shot.ts +362 -24
  22. package/src/cli/index.ts +1 -1
  23. package/src/cli/pane-functions/capabilities.ts +126 -43
  24. package/src/cli/pane-functions/catalog.ts +16 -4
  25. package/src/cli/pane-functions/discovery.ts +1 -0
  26. package/src/cli/pane-functions/dom.test.ts +41 -0
  27. package/src/cli/pane-functions/dom.ts +199 -0
  28. package/src/cli/pane-functions/headless.test.ts +247 -0
  29. package/src/cli/pane-functions/headless.ts +423 -0
  30. package/src/cli/pane-functions/index.test.ts +20 -0
  31. package/src/cli/pane-functions/index.ts +10 -6
  32. package/src/cli/pane-functions/intraday-shot.test.ts +307 -0
  33. package/src/cli/pane-functions/intraday-shot.ts +334 -0
  34. package/src/cli/pane-functions/options.ts +27 -2
  35. package/src/cli/pane-functions/report.test.ts +29 -1
  36. package/src/cli/pane-functions/report.ts +69 -4
  37. package/src/cli/pane-functions/resolver.ts +26 -8
  38. package/src/cli/pane-functions/screenshot.test.ts +76 -0
  39. package/src/cli/pane-functions/screenshot.ts +428 -42
  40. package/src/cli/restore-plugins.ts +41 -0
  41. package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
  42. package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
  43. package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
  44. package/src/components/chart/composite/composite-chart.test.tsx +136 -21
  45. package/src/components/chart/composite/composite-chart.tsx +378 -215
  46. package/src/components/chart/composite/format.ts +8 -2
  47. package/src/components/chart/composite/interactions.test.ts +68 -102
  48. package/src/components/chart/composite/interactions.ts +214 -264
  49. package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
  50. package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
  51. package/src/components/chart/composite/rasterizer.ts +33 -0
  52. package/src/components/chart/composite/renderers.test.ts +51 -4
  53. package/src/components/chart/composite/scene.test.ts +128 -2
  54. package/src/components/chart/composite/scene.ts +149 -19
  55. package/src/components/chart/composite/text-renderer.ts +16 -2
  56. package/src/components/chart/composite/time-scale.ts +163 -62
  57. package/src/components/chart/composite/types.ts +51 -1
  58. package/src/components/chart/core/data.ts +9 -13
  59. package/src/components/chart/core/date-window.ts +7 -1
  60. package/src/components/chart/core/pointer.ts +3 -0
  61. package/src/components/chart/core/types.ts +0 -45
  62. package/src/components/chart/native/chart-rasterizer.ts +0 -1
  63. package/src/components/chart/native/kitty/index.test.ts +0 -188
  64. package/src/components/chart/native/raster/crosshair.ts +6 -5
  65. package/src/components/chart/native/raster/primitives.ts +0 -23
  66. package/src/components/chart/native/renderer-selection.ts +81 -31
  67. package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
  68. package/src/components/chart/static/chart-surface.tsx +150 -0
  69. package/src/components/chart/static/index.ts +8 -0
  70. package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
  71. package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
  72. package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
  73. package/src/components/chart/static/series.ts +49 -0
  74. package/src/components/command-bar/action-runtime.ts +1 -3
  75. package/src/components/command-bar/assist/model.test.ts +17 -2
  76. package/src/components/command-bar/assist/model.ts +40 -9
  77. package/src/components/command-bar/commands/direct/index.ts +1 -1
  78. package/src/components/command-bar/commands/direct/runtime.ts +1 -1
  79. package/src/components/command-bar/commands/registry.ts +0 -11
  80. package/src/components/command-bar/helpers.test.ts +2 -2
  81. package/src/components/command-bar/helpers.ts +2 -6
  82. package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
  83. package/src/components/command-bar/list/badge.test.ts +54 -0
  84. package/src/components/command-bar/list/badge.ts +62 -0
  85. package/src/components/command-bar/list/model.test.ts +103 -0
  86. package/src/components/command-bar/list/model.ts +61 -4
  87. package/src/components/command-bar/list/view.test.tsx +113 -0
  88. package/src/components/command-bar/list/view.tsx +147 -131
  89. package/src/components/command-bar/pane-templates/workflow.ts +1 -1
  90. package/src/components/command-bar/panel/body.tsx +51 -65
  91. package/src/components/command-bar/panel/index.tsx +48 -13
  92. package/src/components/command-bar/panel/layout.test.ts +139 -0
  93. package/src/components/command-bar/panel/layout.ts +65 -28
  94. package/src/components/command-bar/panel/native-surface.ts +24 -0
  95. package/src/components/command-bar/panel/prompt-binding.ts +54 -0
  96. package/src/components/command-bar/panel/runtime.ts +6 -3
  97. package/src/components/command-bar/panel/state.ts +41 -7
  98. package/src/components/command-bar/panel/types.ts +7 -1
  99. package/src/components/command-bar/routes/root/provider-search.ts +34 -15
  100. package/src/components/command-bar/routes/root/results.test.ts +161 -0
  101. package/src/components/command-bar/routes/root/results.ts +30 -28
  102. package/src/components/command-bar/routes/root/runtime.ts +37 -20
  103. package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
  104. package/src/components/command-bar/routes/root/search-providers.ts +205 -0
  105. package/src/components/command-bar/routes/root/selection.ts +1 -12
  106. package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
  107. package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
  108. package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
  109. package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
  110. package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
  111. package/src/components/command-bar/routing/actions.ts +1 -81
  112. package/src/components/command-bar/routing/launch-request.ts +1 -1
  113. package/src/components/command-bar/routing/list-state.ts +6 -21
  114. package/src/components/command-bar/selection-runtime.ts +1 -1
  115. package/src/components/command-bar/surface/assist.test.tsx +34 -19
  116. package/src/components/command-bar/surface/environment.ts +8 -1
  117. package/src/components/command-bar/surface/index.test.tsx +84 -68
  118. package/src/components/command-bar/surface/index.tsx +33 -36
  119. package/src/components/command-bar/surface/test-harness.tsx +38 -7
  120. package/src/components/command-bar/theme-picker.tsx +43 -15
  121. package/src/components/command-bar/view-model.test.ts +53 -8
  122. package/src/components/command-bar/view-model.ts +43 -14
  123. package/src/components/command-bar/workflow/tickers.ts +1 -0
  124. package/src/components/command-bar/workflow/types.ts +1 -1
  125. package/src/components/data-table/view.test.tsx +45 -6
  126. package/src/components/data-table/view.tsx +55 -9
  127. package/src/components/index.ts +10 -2
  128. package/src/components/input-search-bar.test.tsx +36 -2
  129. package/src/components/input-search-bar.tsx +18 -1
  130. package/src/components/layout/header.test.tsx +57 -10
  131. package/src/components/layout/header.tsx +292 -149
  132. package/src/components/layout/market-summary.test.ts +81 -0
  133. package/src/components/layout/market-summary.ts +109 -0
  134. package/src/components/layout/shell/chrome.ts +96 -1
  135. package/src/components/layout/shell/index.tsx +11 -3
  136. package/src/components/layout/shell/menu.ts +9 -0
  137. package/src/components/layout/shell/pane/actions.ts +29 -0
  138. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  139. package/src/components/layout/shell/shortcuts.ts +3 -0
  140. package/src/components/layout/status-bar.test.tsx +9 -8
  141. package/src/components/layout/status-bar.tsx +85 -54
  142. package/src/components/onboarding/account-step/chooser-panel.tsx +2 -2
  143. package/src/components/onboarding/account-step/qr-panel.tsx +2 -10
  144. package/src/components/onboarding/onboarding-frame.tsx +2 -26
  145. package/src/components/onboarding/onboarding-wizard.test.tsx +38 -12
  146. package/src/components/onboarding/onboarding-wizard.tsx +49 -14
  147. package/src/components/price-sparkline/index.ts +61 -34
  148. package/src/components/price-sparkline/view.tsx +1 -1
  149. package/src/components/sign-in-gate.test.tsx +128 -0
  150. package/src/components/sign-in-gate.tsx +149 -0
  151. package/src/components/speedometer-gauge/desktop.tsx +2 -12
  152. package/src/components/ticker/badge/list.tsx +17 -3
  153. package/src/components/ticker/list-table-view.tsx +6 -3
  154. package/src/components/ui/data-table/index.tsx +3 -2
  155. package/src/components/ui/data-table/opentui/index.tsx +165 -120
  156. package/src/components/ui/frame.tsx +28 -0
  157. package/src/components/ui/index.ts +3 -0
  158. package/src/components/ui/native-select.tsx +20 -10
  159. package/src/components/ui/select-button.tsx +138 -0
  160. package/src/components/ui/status.tsx +2 -2
  161. package/src/components/ui/table-layout.ts +1 -1
  162. package/src/components/use-double-click-activation.ts +7 -3
  163. package/src/core/app-services.ts +3 -3
  164. package/src/data/config/layout.ts +12 -6
  165. package/src/data/config/store/index.test.ts +120 -0
  166. package/src/data/config/store/migrations.ts +41 -0
  167. package/src/data/config/store/node.ts +7 -1
  168. package/src/data/config/store/normalize.ts +9 -3
  169. package/src/i18n/es.ts +6 -2
  170. package/src/i18n/ja.ts +6 -2
  171. package/src/i18n/ko.ts +6 -2
  172. package/src/i18n/zh-cn.ts +6 -2
  173. package/src/i18n/zh-tw.ts +6 -2
  174. package/src/layout-marketplace/pane-imagery.ts +1 -0
  175. package/src/layout-marketplace/payload.test.ts +17 -0
  176. package/src/layout-marketplace/payload.ts +40 -5
  177. package/src/market-data/coordinator/index.ts +3 -2
  178. package/src/market-data/coordinator/quotes.ts +173 -66
  179. package/src/market-data/coordinator/subscriptions.test.ts +215 -37
  180. package/src/pane-settings.ts +1 -1
  181. package/src/plugins/builtin/analytics/pane-model.ts +1 -1
  182. package/src/plugins/builtin/analytics/view.tsx +1 -1
  183. package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
  184. package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
  185. package/src/plugins/builtin/cds/client.test.ts +20 -0
  186. package/src/plugins/builtin/cds/client.ts +13 -3
  187. package/src/plugins/builtin/cds/headless.test.ts +30 -0
  188. package/src/plugins/builtin/cds/headless.ts +113 -0
  189. package/src/plugins/builtin/cds/index.tsx +4 -0
  190. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
  191. package/src/plugins/builtin/chart-composer/cli-options.ts +4 -1
  192. package/src/plugins/builtin/chart-composer/index.tsx +1 -1
  193. package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
  194. package/src/plugins/builtin/chat/content/composer.tsx +8 -4
  195. package/src/plugins/builtin/cloud/askg/protocol.test.ts +189 -0
  196. package/src/plugins/builtin/cloud/askg/protocol.ts +274 -0
  197. package/src/plugins/builtin/cloud/auth-dialog.tsx +19 -227
  198. package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
  199. package/src/plugins/builtin/cloud/device-signin-dialog.tsx +21 -8
  200. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  201. package/src/plugins/builtin/cloud/verification-panel.tsx +56 -0
  202. package/src/plugins/builtin/composite-plugins.ts +14 -1
  203. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  204. package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
  205. package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
  206. package/src/plugins/builtin/credit-conditions/client.ts +1 -1
  207. package/src/plugins/builtin/credit-conditions/headless.test.ts +31 -0
  208. package/src/plugins/builtin/credit-conditions/headless.ts +55 -0
  209. package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
  210. package/src/plugins/builtin/credit-conditions/index.tsx +6 -21
  211. package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
  212. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  213. package/src/plugins/builtin/earnings-calls/data.ts +223 -0
  214. package/src/plugins/builtin/earnings-calls/format.ts +49 -0
  215. package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
  216. package/src/plugins/builtin/earnings-calls/pane.tsx +760 -0
  217. package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
  218. package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
  219. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +337 -0
  220. package/src/plugins/builtin/econ/detail-view.tsx +1 -2
  221. package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
  222. package/src/plugins/builtin/econ/index.tsx +2 -1
  223. package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
  224. package/src/plugins/builtin/econ-statistics/client.ts +92 -0
  225. package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
  226. package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
  227. package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
  228. package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
  229. package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
  230. package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
  231. package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
  232. package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
  233. package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
  234. package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
  235. package/src/plugins/builtin/econ-statistics/view.ts +90 -0
  236. package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
  237. package/src/plugins/builtin/fear-greed/data.ts +6 -1
  238. package/src/plugins/builtin/fear-greed/headless.test.ts +53 -0
  239. package/src/plugins/builtin/fear-greed/headless.ts +84 -0
  240. package/src/plugins/builtin/fear-greed/index.tsx +4 -0
  241. package/src/plugins/builtin/help/index.tsx +4 -0
  242. package/src/plugins/builtin/holders/index.tsx +1 -0
  243. package/src/plugins/builtin/insider/index.tsx +1 -0
  244. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  245. package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
  246. package/src/plugins/builtin/market-movers/client.ts +76 -0
  247. package/src/plugins/builtin/market-movers/headless.test.ts +34 -0
  248. package/src/plugins/builtin/market-movers/headless.ts +73 -0
  249. package/src/plugins/builtin/market-movers/index.tsx +11 -51
  250. package/src/plugins/builtin/market-valuation/align.ts +122 -0
  251. package/src/plugins/builtin/market-valuation/cache.ts +32 -0
  252. package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
  253. package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
  254. package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
  255. package/src/plugins/builtin/market-valuation/client.ts +116 -0
  256. package/src/plugins/builtin/market-valuation/defs.ts +251 -0
  257. package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
  258. package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
  259. package/src/plugins/builtin/market-valuation/headless.ts +170 -0
  260. package/src/plugins/builtin/market-valuation/index.tsx +64 -0
  261. package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
  262. package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
  263. package/src/plugins/builtin/market-valuation/model.ts +8 -0
  264. package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
  265. package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
  266. package/src/plugins/builtin/market-valuation/series.ts +12 -0
  267. package/src/plugins/builtin/market-valuation/settings.ts +38 -0
  268. package/src/plugins/builtin/market-valuation/sources.ts +130 -0
  269. package/src/plugins/builtin/market-valuation/trend.ts +89 -0
  270. package/src/plugins/builtin/market-valuation/view.ts +137 -0
  271. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
  272. package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
  273. package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
  274. package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
  275. package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
  276. package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
  277. package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
  278. package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
  279. package/src/plugins/builtin/news/wire/index.tsx +31 -44
  280. package/src/plugins/builtin/options/index.tsx +1 -0
  281. package/src/plugins/builtin/options/view.test.tsx +29 -1
  282. package/src/plugins/builtin/options/view.tsx +1 -22
  283. package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
  284. package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
  285. package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
  286. package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
  287. package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
  288. package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
  289. package/src/plugins/builtin/plugin-module.ts +9 -1
  290. package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -237
  291. package/src/plugins/builtin/research/event-model.ts +245 -0
  292. package/src/plugins/builtin/research/events-headless.test.ts +32 -0
  293. package/src/plugins/builtin/research/events-headless.ts +87 -0
  294. package/src/plugins/builtin/research/index.tsx +8 -0
  295. package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
  296. package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
  297. package/src/plugins/builtin/research-search/data.ts +109 -0
  298. package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
  299. package/src/plugins/builtin/research-search/document-view.tsx +254 -0
  300. package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
  301. package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
  302. package/src/plugins/builtin/research-search/index.tsx +85 -0
  303. package/src/plugins/builtin/research-search/model.test.ts +40 -0
  304. package/src/plugins/builtin/research-search/model.ts +291 -0
  305. package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
  306. package/src/plugins/builtin/research-search/pane.tsx +682 -0
  307. package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
  308. package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
  309. package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
  310. package/src/plugins/builtin/research-search/snippet.ts +134 -0
  311. package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
  312. package/src/plugins/builtin/scanner/flow-model.ts +29 -1
  313. package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
  314. package/src/plugins/builtin/scanner/headless.test.ts +59 -0
  315. package/src/plugins/builtin/scanner/headless.ts +125 -0
  316. package/src/plugins/builtin/scanner/index.ts +4 -0
  317. package/src/plugins/builtin/sec/index.tsx +1 -0
  318. package/src/plugins/builtin/sectors/client.ts +54 -0
  319. package/src/plugins/builtin/sectors/headless.test.ts +28 -0
  320. package/src/plugins/builtin/sectors/headless.ts +80 -0
  321. package/src/plugins/builtin/sectors/index.tsx +6 -51
  322. package/src/plugins/builtin/shared/cloud-upgrade.ts +8 -4
  323. package/src/plugins/builtin/shared/series-cache.ts +93 -0
  324. package/src/plugins/builtin/short-interest/index.tsx +1 -0
  325. package/src/plugins/builtin/short-interest/pane.tsx +1 -1
  326. package/src/plugins/builtin/thirteenf/index.tsx +1 -0
  327. package/src/plugins/builtin/ticker-detail/index.tsx +0 -14
  328. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
  329. package/src/plugins/builtin/ticker-detail/pane.tsx +14 -0
  330. package/src/plugins/builtin/treasury-auctions/headless.test.ts +34 -0
  331. package/src/plugins/builtin/treasury-auctions/headless.ts +95 -0
  332. package/src/plugins/builtin/treasury-auctions/index.tsx +4 -0
  333. package/src/plugins/builtin/volatility/client.ts +16 -3
  334. package/src/plugins/builtin/volatility/headless.test.ts +35 -0
  335. package/src/plugins/builtin/volatility/headless.ts +75 -0
  336. package/src/plugins/builtin/volatility/index.tsx +5 -1
  337. package/src/plugins/builtin/world-indices/client.ts +43 -0
  338. package/src/plugins/builtin/world-indices/headless.test.ts +32 -0
  339. package/src/plugins/builtin/world-indices/headless.ts +109 -0
  340. package/src/plugins/builtin/world-indices/index.tsx +4 -0
  341. package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
  342. package/src/plugins/builtin/world-venue-map/map.tsx +213 -69
  343. package/src/plugins/builtin/world-venue-map/model.test.ts +32 -0
  344. package/src/plugins/builtin/world-venue-map/model.ts +134 -13
  345. package/src/plugins/builtin/yield-curve/headless.test.ts +24 -0
  346. package/src/plugins/builtin/yield-curve/headless.ts +67 -0
  347. package/src/plugins/builtin/yield-curve/index.tsx +7 -9
  348. package/src/plugins/builtin/yield-curve/treasury-data.ts +16 -6
  349. package/src/plugins/bundle.test.ts +166 -0
  350. package/src/plugins/bundle.ts +141 -0
  351. package/src/plugins/catalog-backend.test.ts +26 -0
  352. package/src/plugins/catalog-backend.ts +9 -2
  353. package/src/plugins/catalog-browser.test.ts +1 -13
  354. package/src/plugins/catalog-browser.ts +14 -0
  355. package/src/plugins/catalog-manifest.test.ts +58 -0
  356. package/src/plugins/catalog-manifest.ts +99 -0
  357. package/src/plugins/catalog-ui.ts +20 -10
  358. package/src/plugins/catalog.ts +9 -2
  359. package/src/plugins/current-target.ts +28 -0
  360. package/src/plugins/host-contract.ts +46 -0
  361. package/src/plugins/host-link.ts +158 -0
  362. package/src/plugins/host-modules.ts +73 -0
  363. package/src/plugins/loader.ts +90 -23
  364. package/src/plugins/ownership.ts +1 -0
  365. package/src/plugins/prediction-markets/index.tsx +1 -0
  366. package/src/plugins/registry/context.ts +3 -0
  367. package/src/plugins/registry/contributions.ts +27 -0
  368. package/src/plugins/registry/index.test.ts +25 -0
  369. package/src/plugins/registry/index.ts +8 -0
  370. package/src/plugins/registry/pane-settings.ts +6 -13
  371. package/src/plugins/seed.test.ts +83 -0
  372. package/src/plugins/seed.ts +83 -0
  373. package/src/public/broker.ts +37 -0
  374. package/src/public/market-data.ts +9 -0
  375. package/src/public/pane-hooks.ts +57 -0
  376. package/src/public/public-api.test.ts +285 -0
  377. package/src/public/react.ts +62 -0
  378. package/src/public/test-support.ts +23 -0
  379. package/src/public/time-series.ts +8 -0
  380. package/src/public/utils.ts +77 -0
  381. package/src/remote/layout-helpers.ts +6 -8
  382. package/src/remote/resources.ts +2 -0
  383. package/src/remote/schema.test.ts +65 -0
  384. package/src/remote/schema.ts +323 -39
  385. package/src/remote/semantic-tree.test.tsx +105 -0
  386. package/src/remote/semantic-tree.tsx +53 -35
  387. package/src/remote/types.ts +49 -2
  388. package/src/renderers/browser/app-services.ts +3 -0
  389. package/src/renderers/browser/config-host.ts +24 -8
  390. package/src/renderers/browser/deeplink-bridge.test.ts +11 -0
  391. package/src/renderers/browser/deeplink-bridge.ts +15 -3
  392. package/src/renderers/browser/main.tsx +8 -0
  393. package/src/renderers/browser/research-entry.test.ts +8 -0
  394. package/src/renderers/browser/research-entry.ts +7 -0
  395. package/src/renderers/cloudflare/worker.test.ts +19 -0
  396. package/src/renderers/cloudflare/worker.ts +1 -1
  397. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +2 -2
  398. package/src/renderers/electrobun/bun/desktop/initialization.ts +10 -3
  399. package/src/renderers/electrobun/bun/external-plugins.ts +156 -0
  400. package/src/renderers/electrobun/bun/index.ts +5 -2
  401. package/src/renderers/electrobun/shared/protocol.ts +21 -0
  402. package/src/renderers/electrobun/view/build-assets.ts +0 -3
  403. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +114 -11
  404. package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
  405. package/src/renderers/electrobun/view/external-plugins.ts +63 -0
  406. package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
  407. package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
  408. package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
  409. package/src/renderers/electrobun/view/host/mouse.ts +16 -2
  410. package/src/renderers/electrobun/view/main.tsx +27 -2
  411. package/src/renderers/electrobun/view/styles.css +5 -0
  412. package/src/renderers/electrobun/view/toast-host.tsx +18 -0
  413. package/src/renderers/opentui/chart-surface.tsx +5 -1
  414. package/src/renderers/opentui/host.tsx +16 -2
  415. package/src/renderers/opentui/interaction-performance.test.ts +79 -0
  416. package/src/renderers/opentui/interaction-performance.ts +188 -0
  417. package/src/renderers/opentui/start.tsx +26 -1
  418. package/src/renderers/opentui/test-utils.tsx +13 -0
  419. package/src/renderers/opentui/toast-host.tsx +26 -2
  420. package/src/renderers/opentui/ui-host.tsx +1 -0
  421. package/src/sources/provider-router/search.test.ts +191 -0
  422. package/src/sources/provider-router/search.ts +92 -39
  423. package/src/state/app/context/index.tsx +2 -0
  424. package/src/state/app/context/selector.test.tsx +33 -1
  425. package/src/state/hooks/quote-streaming.test.tsx +93 -0
  426. package/src/state/hooks/quote-streaming.ts +106 -30
  427. package/src/state/pane-table-export-registry.ts +19 -0
  428. package/src/sync/core-contributors.test.ts +111 -0
  429. package/src/sync/core-contributors.ts +155 -1
  430. package/src/theme/color-utils.ts +1 -1
  431. package/src/theme/colors.ts +50 -0
  432. package/src/theme/themes.test.ts +15 -2
  433. package/src/theme/themes.ts +19 -1
  434. package/src/tickers/search/index.test.ts +26 -0
  435. package/src/tickers/search/index.ts +42 -17
  436. package/src/tickers/search/ranking.ts +14 -0
  437. package/src/time-series/chart-data.test.ts +216 -0
  438. package/src/time-series/resolution.test.ts +25 -26
  439. package/src/time-series/resolution.ts +41 -10
  440. package/src/time-series/resolve.test.ts +118 -1
  441. package/src/time-series/resolve.ts +21 -16
  442. package/src/time-series/types.ts +3 -1
  443. package/src/time-series/use-chart-resolution.ts +5 -0
  444. package/src/types/config.ts +125 -3
  445. package/src/types/data-provider.ts +11 -0
  446. package/src/types/headless.ts +161 -0
  447. package/src/types/plugin.ts +102 -0
  448. package/src/ui/host.tsx +12 -1
  449. package/src/ui/index.tsx +1 -0
  450. package/src/ui/toast.tsx +8 -4
  451. package/src/utils/format.ts +35 -0
  452. package/src/utils/fuzzy-search.test.ts +94 -0
  453. package/src/utils/fuzzy-search.ts +81 -22
  454. package/src/utils/shortcut-labels.ts +14 -0
  455. package/src/components/chart/core/axis-format.ts +0 -157
  456. package/src/components/chart/core/index.test.ts +0 -1075
  457. package/src/components/chart/core/projection.ts +0 -357
  458. package/src/components/chart/core/render-utils.ts +0 -4
  459. package/src/components/chart/core/renderer.ts +0 -244
  460. package/src/components/chart/core/scene.ts +0 -180
  461. package/src/components/chart/core/time-axis.ts +0 -330
  462. package/src/components/chart/cursor-motion.ts +0 -47
  463. package/src/components/chart/indicators/types.ts +0 -26
  464. package/src/components/chart/market-session.ts +0 -176
  465. package/src/components/chart/multi-line/renderer.test.ts +0 -83
  466. package/src/components/chart/multi-line/renderer.ts +0 -161
  467. package/src/components/chart/multi-line/scene.ts +0 -188
  468. package/src/components/chart/native/raster/layout.ts +0 -129
  469. package/src/components/chart/native/raster/price-chart.ts +0 -244
  470. package/src/components/chart/rendering/chart-draw.ts +0 -270
  471. package/src/components/chart/rendering/geometry.ts +0 -95
  472. package/src/components/chart/rendering/pixel-buffer.ts +0 -267
  473. package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
  474. package/src/components/chart/scatter-chart-renderer.ts +0 -223
  475. package/src/components/chart/static/chart/surface.tsx +0 -358
  476. package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
  477. package/src/components/command-bar/routes/root/article-results.ts +0 -55
  478. package/src/plugins/broker-sync/broker-sync.test.ts +0 -188
  479. package/src/plugins/broker-sync/native-loader.ts +0 -10
  480. package/src/plugins/broker-sync/normalize.ts +0 -308
  481. package/src/plugins/broker-sync/public.ts +0 -122
  482. package/src/plugins/broker-sync/robinhood-native.ts +0 -367
  483. package/src/plugins/broker-sync/robinhood.ts +0 -114
  484. package/src/plugins/broker-sync/simplefin-native.ts +0 -201
  485. package/src/plugins/broker-sync/simplefin.ts +0 -70
  486. package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
  487. package/src/plugins/builtin/news/wire/article-search.ts +0 -140
  488. package/src/plugins/builtin/substack/api/auth.ts +0 -185
  489. package/src/plugins/builtin/substack/api/cache.ts +0 -82
  490. package/src/plugins/builtin/substack/api/loaders.ts +0 -170
  491. package/src/plugins/builtin/substack/api/store.ts +0 -186
  492. package/src/plugins/builtin/substack/api/types.ts +0 -42
  493. package/src/plugins/builtin/substack/api.test.ts +0 -298
  494. package/src/plugins/builtin/substack/article-detail.tsx +0 -397
  495. package/src/plugins/builtin/substack/article-stack.tsx +0 -133
  496. package/src/plugins/builtin/substack/content.ts +0 -478
  497. package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
  498. package/src/plugins/builtin/substack/index.tsx +0 -49
  499. package/src/plugins/builtin/substack/login-view.tsx +0 -126
  500. package/src/plugins/builtin/substack/model.test.ts +0 -155
  501. package/src/plugins/builtin/substack/normalize.ts +0 -343
  502. package/src/plugins/builtin/substack/pane-footer.ts +0 -52
  503. package/src/plugins/builtin/substack/pane-state.ts +0 -170
  504. package/src/plugins/builtin/substack/pane.tsx +0 -547
  505. package/src/plugins/builtin/substack/read-state.ts +0 -30
  506. package/src/plugins/builtin/substack/table.ts +0 -111
  507. package/src/plugins/builtin/substack/types.ts +0 -71
  508. package/src/plugins/builtin/substack/utils.ts +0 -131
  509. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
  510. package/src/plugins/ibkr/account-cache.test.ts +0 -102
  511. package/src/plugins/ibkr/account-cache.ts +0 -24
  512. package/src/plugins/ibkr/broker-adapter.ts +0 -286
  513. package/src/plugins/ibkr/config.test.ts +0 -79
  514. package/src/plugins/ibkr/config.ts +0 -250
  515. package/src/plugins/ibkr/dialogs.tsx +0 -40
  516. package/src/plugins/ibkr/flex/index.test.ts +0 -264
  517. package/src/plugins/ibkr/flex/index.ts +0 -367
  518. package/src/plugins/ibkr/flex/statement-client.ts +0 -235
  519. package/src/plugins/ibkr/fundamental-parser.ts +0 -149
  520. package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
  521. package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
  522. package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
  523. package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
  524. package/src/plugins/ibkr/gateway/connection.ts +0 -152
  525. package/src/plugins/ibkr/gateway/contracts.ts +0 -132
  526. package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
  527. package/src/plugins/ibkr/gateway/helpers.ts +0 -33
  528. package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
  529. package/src/plugins/ibkr/gateway/history.ts +0 -120
  530. package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
  531. package/src/plugins/ibkr/gateway/market-data.ts +0 -345
  532. package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
  533. package/src/plugins/ibkr/gateway/orders.ts +0 -63
  534. package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
  535. package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
  536. package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
  537. package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
  538. package/src/plugins/ibkr/gateway/selection.ts +0 -51
  539. package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
  540. package/src/plugins/ibkr/gateway/service/index.ts +0 -429
  541. package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
  542. package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
  543. package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
  544. package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
  545. package/src/plugins/ibkr/gateway/service/native.ts +0 -543
  546. package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
  547. package/src/plugins/ibkr/gateway/types.ts +0 -27
  548. package/src/plugins/ibkr/index.test.tsx +0 -162
  549. package/src/plugins/ibkr/index.tsx +0 -159
  550. package/src/plugins/ibkr/instance-selection.test.ts +0 -120
  551. package/src/plugins/ibkr/instance-selection.ts +0 -36
  552. package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
  553. package/src/plugins/ibkr/portfolio-performance.ts +0 -129
  554. package/src/plugins/ibkr/trade/badge.tsx +0 -28
  555. package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
  556. package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
  557. package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
  558. package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
  559. package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
  560. package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
  561. package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
  562. package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
  563. package/src/plugins/ibkr/trade/tab/model.ts +0 -159
  564. package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
  565. package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
  566. package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
  567. package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
  568. package/src/plugins/ibkr/trade/utils.test.ts +0 -175
  569. package/src/plugins/ibkr/trade/utils.ts +0 -144
  570. package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
  571. package/src/plugins/ibkr/trading/pane.tsx +0 -332
  572. package/src/plugins/ibkr/trading/state.test.ts +0 -109
  573. package/src/plugins/ibkr/trading/state.ts +0 -335
  574. package/src/renderers/electrobun/view/native-stubs/broker-sync-native-loader.ts +0 -7
@@ -1,19 +1,33 @@
1
1
  import { mkdir, mkdtemp, rm, writeFile } from "fs/promises";
2
2
  import { tmpdir } from "os";
3
- import { join } from "path";
4
- import { pathToFileURL } from "url";
3
+ import { join, resolve, sep } from "path";
5
4
  import type { AppConfig } from "../types/config";
6
5
  import type { ResolvedSeries } from "../time-series/types";
7
- import type { OptionsChain, TickerFinancials } from "../types/financials";
6
+ import type { OptionsChain, PricePoint, TickerFinancials } from "../types/financials";
7
+ import type { ManualChartResolution } from "../time-series/resolution";
8
8
  import type { TickerRecord } from "../types/ticker";
9
9
  import type { PaneRuntimeState } from "../core/state/app/state";
10
10
  import type { RemoteUiNodeSnapshot } from "../remote/types";
11
11
  import type { FredSeriesCacheEntry } from "../data/fred-series";
12
+ import type { DatedObservation } from "../plugins/builtin/market-valuation/series";
12
13
  import {
13
14
  electrobunViewPath,
14
15
  writeElectrobunViewPage,
15
16
  } from "../renderers/electrobun/view/build-assets";
16
17
 
18
+ export interface DesktopPaneShotIntradayHistory {
19
+ symbol: string;
20
+ exchange: string;
21
+ rangePreset: "1D" | "1W";
22
+ resolution: ManualChartResolution;
23
+ requestedSession: string | null;
24
+ sessionDates: string[];
25
+ points: PricePoint[];
26
+ start: string | null;
27
+ end: string | null;
28
+ unavailableReason: string | null;
29
+ }
30
+
17
31
  export interface DesktopPaneShotPayload {
18
32
  config: AppConfig;
19
33
  paneId: string;
@@ -21,16 +35,49 @@ export interface DesktopPaneShotPayload {
21
35
  heightCells: number;
22
36
  widthPx: number;
23
37
  heightPx: number;
38
+ /** Chrome device scale factor; 2 keeps text crisp, higher values zoom the layout. */
39
+ deviceScaleFactor?: number;
40
+ /** Label drawn at the right edge of the pane title bar; null draws nothing. */
41
+ watermark?: string | null;
24
42
  tickers: TickerRecord[];
25
43
  financials: Array<[string, TickerFinancials]>;
44
+ intradayHistories: DesktopPaneShotIntradayHistory[];
26
45
  optionsChains: Array<[string, OptionsChain]>;
27
46
  fredSeries: Array<[string, FredSeriesCacheEntry]>;
47
+ valuationSeries: Array<[string, DatedObservation[]]>;
48
+ statSeries: Array<[string, DatedObservation[]]>;
28
49
  capabilitySeries: Array<[string, ResolvedSeries]>;
29
50
  paneState: Record<string, PaneRuntimeState>;
30
51
  }
31
52
 
53
+ /** Kept in Bun memory and never serialized into the browser page or CLI result. */
54
+ export interface DesktopPaneShotApiProxy {
55
+ baseUrl: string;
56
+ sessionToken: string | null;
57
+ }
58
+
59
+ export interface DesktopPaneShotRenderedCell {
60
+ columnId?: string;
61
+ columnLabel: string;
62
+ text: string;
63
+ }
64
+
65
+ export interface DesktopPaneShotRenderedRow {
66
+ tableIndex: number;
67
+ rowIndex: number;
68
+ key?: string;
69
+ selected: boolean;
70
+ cells: DesktopPaneShotRenderedCell[];
71
+ }
72
+
32
73
  export interface DesktopPaneShotRenderResult {
33
74
  visibleText: string;
75
+ rows: DesktopPaneShotRenderedRow[];
76
+ truncated: boolean;
77
+ truncationReasons: string[];
78
+ loadingStateDetected: boolean;
79
+ errorStateDetected: boolean;
80
+ errorStateMarkers: string[];
34
81
  emptyStateDetected: boolean;
35
82
  emptyStateMarkers: string[];
36
83
  semanticUi: RemoteUiNodeSnapshot[];
@@ -47,30 +94,49 @@ type PendingCdpCall = {
47
94
  reject: (error: Error) => void;
48
95
  };
49
96
 
97
+ const SHOT_MODE_CSS = [
98
+ "[data-gloom-role='composite-chart-toolbar']",
99
+ "[data-gloom-role='chart-series-quick-add']",
100
+ "[data-gloom-role='pane-close']",
101
+ ].join(", ") + " { display: none !important; }\n"
102
+ // Sits where the hidden close button was: one cell high, right-aligned in the title bar.
103
+ + "[data-gloom-role='shot-watermark'] { position: fixed; top: 1px; right: 10px; height: var(--cell-h);"
104
+ + " line-height: var(--cell-h); font-size: 12px; letter-spacing: 0.02em; color: var(--gloom-text-dim, #888);"
105
+ + " pointer-events: none; z-index: 1000; }";
106
+
50
107
  const CHROME_POLL_ATTEMPTS = 80;
51
- const SHOT_READY_TIMEOUT_MS = 10_000;
108
+ const SHOT_READY_TIMEOUT_MS = 45_000;
52
109
  const CDP_CALL_TIMEOUT_MS = 10_000;
53
- const SHOT_DEVICE_SCALE_FACTOR = 2;
110
+ const DEFAULT_DEVICE_SCALE_FACTOR = 2;
111
+ const SHOT_API_PROXY_PREFIX = "/__gloom_cli_api__";
112
+ const SESSION_COOKIE_NAMES = ["__Secure-gloomberb.session_token", "gloomberb.session_token"] as const;
54
113
 
55
114
  export async function renderDesktopPaneScreenshot(
56
115
  payload: DesktopPaneShotPayload,
57
116
  outputPath: string,
117
+ apiProxy: DesktopPaneShotApiProxy,
118
+ options: { captureImage?: boolean } = {},
58
119
  ): Promise<DesktopPaneShotRenderResult> {
59
120
  const tempDir = await mkdtemp(join(tmpdir(), "gloom-pane-shot-"));
121
+ let server: ReturnType<typeof Bun.serve> | null = null;
60
122
  try {
61
123
  const outdir = join(tempDir, "assets");
62
124
  await mkdir(outdir, { recursive: true });
63
- const htmlPath = await buildShotPage(outdir, payload);
125
+ await buildShotPage(outdir, payload);
126
+ server = serveShotPage(outdir, apiProxy);
64
127
  const chrome = await findChromeExecutable();
65
128
  return await capturePageScreenshot({
66
129
  chrome,
67
- url: pathToFileURL(htmlPath).href,
130
+ url: new URL("/", server.url).href,
68
131
  outputPath,
69
132
  widthPx: payload.widthPx,
70
133
  heightPx: payload.heightPx,
134
+ deviceScaleFactor: payload.deviceScaleFactor ?? DEFAULT_DEVICE_SCALE_FACTOR,
71
135
  userDataDir: join(tempDir, "chrome-profile"),
136
+ captureImage: options.captureImage !== false,
72
137
  });
73
138
  } finally {
139
+ server?.stop(true);
74
140
  await rm(tempDir, { recursive: true, force: true });
75
141
  }
76
142
  }
@@ -90,6 +156,20 @@ async function buildShotPage(outdir: string, payload: DesktopPaneShotPayload): P
90
156
  loadingText: "Rendering pane...",
91
157
  bootstrapScript: `
92
158
  window.__GLOOM_CLI_SHOT_PAYLOAD__ = ${payloadJson};
159
+ (() => {
160
+ // A screenshot cannot be interacted with, so drawing tools, the
161
+ // quick-add input and the close button only add noise to the image.
162
+ const style = document.createElement("style");
163
+ style.textContent = ${JSON.stringify(SHOT_MODE_CSS)};
164
+ document.head.appendChild(style);
165
+ const watermark = ${JSON.stringify(payload.watermark ?? null)};
166
+ if (watermark) {
167
+ const mark = document.createElement("div");
168
+ mark.setAttribute("data-gloom-role", "shot-watermark");
169
+ mark.textContent = watermark;
170
+ document.addEventListener("DOMContentLoaded", () => document.body.appendChild(mark));
171
+ }
172
+ })();
93
173
  window.addEventListener("error", (event) => {
94
174
  window.__GLOOM_CLI_SHOT_ERROR__ = event.error && event.error.stack ? event.error.stack : String(event.error || event.message);
95
175
  });
@@ -100,6 +180,116 @@ async function buildShotPage(outdir: string, payload: DesktopPaneShotPayload): P
100
180
  });
101
181
  }
102
182
 
183
+ function serveShotPage(
184
+ outdir: string,
185
+ apiProxy: DesktopPaneShotApiProxy,
186
+ ): ReturnType<typeof Bun.serve> {
187
+ const staticRoot = resolve(outdir);
188
+ return Bun.serve({
189
+ hostname: "127.0.0.1",
190
+ port: 0,
191
+ idleTimeout: 60,
192
+ fetch(request) {
193
+ const url = new URL(request.url);
194
+ if (url.pathname.startsWith(`${SHOT_API_PROXY_PREFIX}/`)) {
195
+ return proxyShotApiRequest(request, url, apiProxy);
196
+ }
197
+ return serveShotAsset(url, staticRoot);
198
+ },
199
+ });
200
+ }
201
+
202
+ async function serveShotAsset(url: URL, staticRoot: string): Promise<Response> {
203
+ let pathname: string;
204
+ try {
205
+ pathname = decodeURIComponent(url.pathname);
206
+ } catch {
207
+ return new Response("Bad request", { status: 400 });
208
+ }
209
+ const relativePath = pathname === "/" ? "index.html" : pathname.replace(/^\/+/, "");
210
+ const filePath = resolve(staticRoot, relativePath);
211
+ if (filePath !== staticRoot && !filePath.startsWith(`${staticRoot}${sep}`)) {
212
+ return new Response("Not found", { status: 404 });
213
+ }
214
+ const file = Bun.file(filePath);
215
+ if (!(await file.exists())) return new Response("Not found", { status: 404 });
216
+ return new Response(file);
217
+ }
218
+
219
+ async function proxyShotApiRequest(
220
+ request: Request,
221
+ requestUrl: URL,
222
+ apiProxy: DesktopPaneShotApiProxy,
223
+ ): Promise<Response> {
224
+ const baseUrl = new URL(apiProxy.baseUrl);
225
+ const proxiedPath = requestUrl.pathname.slice(SHOT_API_PROXY_PREFIX.length) || "/";
226
+ const target = new URL(baseUrl);
227
+ const basePath = baseUrl.pathname.replace(/\/+$/, "");
228
+ target.pathname = `${basePath}${proxiedPath.startsWith("/") ? proxiedPath : `/${proxiedPath}`}`;
229
+ target.search = requestUrl.search;
230
+ target.hash = "";
231
+ const headers = new Headers(request.headers);
232
+ for (const name of [
233
+ "authorization",
234
+ "connection",
235
+ "content-length",
236
+ "cookie",
237
+ "host",
238
+ "origin",
239
+ "proxy-authorization",
240
+ "referer",
241
+ "te",
242
+ "trailer",
243
+ "transfer-encoding",
244
+ "upgrade",
245
+ "x-forwarded-for",
246
+ "x-forwarded-host",
247
+ "x-forwarded-proto",
248
+ ]) {
249
+ headers.delete(name);
250
+ }
251
+ const browserOwnedHeaders: string[] = [];
252
+ headers.forEach((_value, name) => {
253
+ if (name.startsWith("sec-") || name === "accept-encoding") browserOwnedHeaders.push(name);
254
+ });
255
+ for (const name of browserOwnedHeaders) headers.delete(name);
256
+ headers.set("Origin", baseUrl.origin);
257
+ if (apiProxy.sessionToken) {
258
+ headers.set(
259
+ "Cookie",
260
+ SESSION_COOKIE_NAMES.map((name) => `${name}=${apiProxy.sessionToken}`).join("; "),
261
+ );
262
+ }
263
+
264
+ try {
265
+ const response = await fetch(target, {
266
+ method: request.method,
267
+ headers,
268
+ body: request.method === "GET" || request.method === "HEAD" ? undefined : request.body,
269
+ redirect: "follow",
270
+ });
271
+ const responseHeaders = new Headers(response.headers);
272
+ for (const name of [
273
+ "access-control-allow-credentials",
274
+ "access-control-allow-origin",
275
+ "content-encoding",
276
+ "content-length",
277
+ "set-cookie",
278
+ "transfer-encoding",
279
+ ]) {
280
+ responseHeaders.delete(name);
281
+ }
282
+ responseHeaders.set("Cache-Control", "no-store");
283
+ return new Response(response.body, {
284
+ status: response.status,
285
+ statusText: response.statusText,
286
+ headers: responseHeaders,
287
+ });
288
+ } catch {
289
+ return Response.json({ error: "Cloud API request failed." }, { status: 502 });
290
+ }
291
+ }
292
+
103
293
  async function findChromeExecutable(): Promise<string> {
104
294
  const candidates = [
105
295
  process.env.CHROME_PATH,
@@ -123,14 +313,18 @@ async function capturePageScreenshot({
123
313
  outputPath,
124
314
  widthPx,
125
315
  heightPx,
316
+ deviceScaleFactor,
126
317
  userDataDir,
318
+ captureImage,
127
319
  }: {
128
320
  chrome: string;
129
321
  url: string;
130
322
  outputPath: string;
131
323
  widthPx: number;
132
324
  heightPx: number;
325
+ deviceScaleFactor: number;
133
326
  userDataDir: string;
327
+ captureImage: boolean;
134
328
  }): Promise<DesktopPaneShotRenderResult> {
135
329
  await mkdir(userDataDir, { recursive: true });
136
330
  const port = 43000 + Math.floor(Math.random() * 10000);
@@ -166,18 +360,20 @@ async function capturePageScreenshot({
166
360
  await session.send("Emulation.setDeviceMetricsOverride", {
167
361
  width: widthPx,
168
362
  height: heightPx,
169
- deviceScaleFactor: SHOT_DEVICE_SCALE_FACTOR,
363
+ deviceScaleFactor,
170
364
  mobile: false,
171
365
  });
172
366
  await waitForShotReady(session);
173
367
  const rendered = await readRenderedPaneState(session);
174
- const screenshot = await session.send("Page.captureScreenshot", {
175
- format: "png",
176
- fromSurface: true,
177
- captureBeyondViewport: false,
178
- }) as { data?: string };
179
- if (!screenshot.data) throw new Error("Chrome did not return screenshot data.");
180
- await writeFile(outputPath, Uint8Array.from(Buffer.from(screenshot.data, "base64")));
368
+ if (captureImage) {
369
+ const screenshot = await session.send("Page.captureScreenshot", {
370
+ format: "png",
371
+ fromSurface: true,
372
+ captureBeyondViewport: false,
373
+ }) as { data?: string };
374
+ if (!screenshot.data) throw new Error("Chrome did not return screenshot data.");
375
+ await writeFile(outputPath, Uint8Array.from(Buffer.from(screenshot.data, "base64")));
376
+ }
181
377
  return rendered;
182
378
  } catch (error) {
183
379
  const message = error instanceof Error ? error.message : String(error);
@@ -189,6 +385,22 @@ async function capturePageScreenshot({
189
385
  }
190
386
  }
191
387
 
388
+ const LOADING_STATE_PATTERNS = [
389
+ /\bLoading(?: [^.]{0,80})?\.{3}/gi,
390
+ /\bRendering pane\.{3}/gi,
391
+ ];
392
+
393
+ const ERROR_STATE_PATTERNS = [
394
+ /\b[^.]{1,100} unavailable\./gi,
395
+ /\bFailed to fetch\b/gi,
396
+ /\bCould not load\b/gi,
397
+ /\bSign in to\b/gi,
398
+ /\bVerify your email\b/gi,
399
+ /\brequires signup and email verification\b/gi,
400
+ /\bpart of Gloom Cloud Pro\b/gi,
401
+ /\bCloud API request failed\b/gi,
402
+ ];
403
+
192
404
  const EMPTY_STATE_PATTERNS = [
193
405
  /\bNo chart data\b/gi,
194
406
  /\bNo graph data\b/gi,
@@ -197,21 +409,127 @@ const EMPTY_STATE_PATTERNS = [
197
409
  /\bNo comparison tickers configured\b/gi,
198
410
  /\bNo relationship tickers configured\b/gi,
199
411
  /\bNo overlapping price history\b/gi,
200
- /\bLoading chart\.{0,3}\b/gi,
201
- /\bRendering pane\.{0,3}\b/gi,
202
- /\bNo tickers selected\b/gi,
203
- /\bMarket data unavailable\b/gi,
412
+ /\bNo tickers? selected\b/gi,
204
413
  /\bNo data(?: available)?\b/gi,
414
+ /\bNothing to show yet\b/gi,
415
+ /\bNo transcribed calls(?: for [^.]+)? yet\b/gi,
416
+ /\bNo short interest (?:data|found)\b/gi,
417
+ /\bNo (?:House PTR |insider )?(?:trades|members|transactions|filings)\b/gi,
205
418
  ];
206
419
 
420
+ function stateMarkers(text: string, patterns: RegExp[]): string[] {
421
+ return [...new Set(patterns.flatMap((pattern) => (
422
+ [...text.matchAll(pattern)].map((match) => match[0])
423
+ )))];
424
+ }
425
+
207
426
  async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneShotRenderResult> {
208
427
  const result = await session.send("Runtime.evaluate", {
209
428
  expression: `(() => {
210
429
  const root = document.getElementById("root") || document.body;
430
+ const semanticUi = window.__GLOOM_CLI_SHOT_SEMANTIC_UI__ || [];
431
+ const normalize = (value) => String(value || "").replace(/\\s+/g, " ").trim();
432
+ const isVisible = (element) => {
433
+ if (!(element instanceof HTMLElement)) return false;
434
+ const style = getComputedStyle(element);
435
+ if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") return false;
436
+ const rect = element.getBoundingClientRect();
437
+ return rect.width > 0 && rect.height > 0
438
+ && rect.right > 0 && rect.bottom > 0
439
+ && rect.left < window.innerWidth && rect.top < window.innerHeight;
440
+ };
441
+ const semanticTables = semanticUi.filter((node) => node && node.role === "table");
442
+ const rows = [];
443
+ const truncationReasons = new Set();
444
+ const markScrollTruncation = (element) => {
445
+ if (!(element instanceof HTMLElement) || !isVisible(element)) return;
446
+ if (
447
+ element.getAttribute("data-gloom-scrollbar-y") === "visible"
448
+ || element.scrollHeight > element.clientHeight + 1
449
+ ) {
450
+ truncationReasons.add("rows extend below the rendered viewport");
451
+ }
452
+ if (
453
+ element.getAttribute("data-gloom-scrollbar-x") === "visible"
454
+ || element.scrollWidth > element.clientWidth + 1
455
+ ) {
456
+ truncationReasons.add("columns extend beyond the rendered viewport");
457
+ }
458
+ };
459
+ [...root.querySelectorAll(
460
+ '[data-gloom-scrollbar-x="visible"], [data-gloom-scrollbar-y="visible"], [data-gloom-role="data-table-body-scroll"]',
461
+ )].forEach(markScrollTruncation);
462
+ [...root.querySelectorAll('[data-gloom-role="data-table"]')]
463
+ .filter(isVisible)
464
+ .forEach((table, tableIndex) => {
465
+ const tableMetadata = semanticTables[tableIndex] && semanticTables[tableIndex].metadata || {};
466
+ const semanticColumns = Array.isArray(tableMetadata.columns) ? tableMetadata.columns : [];
467
+ const semanticRows = Array.isArray(tableMetadata.rows) ? tableMetadata.rows : [];
468
+ const headers = [...table.querySelectorAll('[data-gloom-role="data-table-header-cell"]')]
469
+ .map((cell) => normalize(cell.innerText || cell.textContent));
470
+ const rowElements = [...table.querySelectorAll(
471
+ '[data-gloom-role="data-table-row"], [data-gloom-role="data-table-section-header"]',
472
+ )].filter(isVisible);
473
+ rowElements.forEach((row, rowIndex) => {
474
+ const cellElements = [...row.querySelectorAll('[data-gloom-role="data-table-cell"]')];
475
+ const values = cellElements.length > 0 ? cellElements : [row];
476
+ const semanticRow = semanticRows[rowIndex] || {};
477
+ const cells = values.map((cell, cellIndex) => {
478
+ const semanticColumn = semanticColumns[cellIndex] || {};
479
+ const text = normalize(cell.innerText || cell.textContent);
480
+ const textNodes = [cell, ...cell.querySelectorAll('*')];
481
+ if (
482
+ /\\u2026|\\.\\.\\./.test(text)
483
+ || textNodes.some((node) => (
484
+ node instanceof HTMLElement
485
+ && isVisible(node)
486
+ && node.scrollWidth > node.clientWidth + 1
487
+ ))
488
+ ) {
489
+ truncationReasons.add("one or more cells are visibly clipped");
490
+ }
491
+ return {
492
+ ...(typeof semanticColumn.id === "string" ? { columnId: semanticColumn.id } : {}),
493
+ columnLabel: typeof semanticColumn.label === "string"
494
+ ? semanticColumn.label
495
+ : headers[cellIndex] || (values.length === 1 ? "Row" : String(cellIndex + 1)),
496
+ text,
497
+ };
498
+ }).filter((cell) => cell.text.length > 0);
499
+ if (cells.length === 0) return;
500
+ rows.push({
501
+ tableIndex,
502
+ rowIndex,
503
+ ...(typeof semanticRow.key === "string" ? { key: semanticRow.key } : {}),
504
+ selected: row.getAttribute("data-selected") === "true" || semanticRow.selected === true,
505
+ cells,
506
+ });
507
+ });
508
+ });
509
+ if (rows.length === 0) {
510
+ [...root.querySelectorAll('[data-gloom-role="desktop-list-row"]')]
511
+ .filter(isVisible)
512
+ .forEach((row, rowIndex) => {
513
+ const text = normalize(row.innerText || row.textContent);
514
+ if (!text) return;
515
+ rows.push({
516
+ tableIndex: 0,
517
+ rowIndex,
518
+ selected: row.getAttribute("data-selected") === "true",
519
+ cells: [{ columnLabel: "Row", text }],
520
+ });
521
+ });
522
+ }
211
523
  return {
212
524
  visibleText: root.innerText || root.textContent || "",
213
525
  error: window.__GLOOM_CLI_SHOT_ERROR__ || "",
214
- semanticUi: window.__GLOOM_CLI_SHOT_SEMANTIC_UI__ || [],
526
+ loadingStateDetected: root.querySelector('[data-gloom-status="loading"]') !== null,
527
+ errorStateDetected: root.querySelector('[data-gloom-status="error"]') !== null,
528
+ emptyStateDetected: root.querySelector('[data-gloom-status="empty"]') !== null,
529
+ rows,
530
+ truncated: truncationReasons.size > 0,
531
+ truncationReasons: [...truncationReasons],
532
+ semanticUi,
215
533
  };
216
534
  })()`,
217
535
  returnByValue: true,
@@ -220,6 +538,12 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
220
538
  value?: {
221
539
  visibleText?: string;
222
540
  error?: string;
541
+ loadingStateDetected?: boolean;
542
+ errorStateDetected?: boolean;
543
+ emptyStateDetected?: boolean;
544
+ rows?: DesktopPaneShotRenderedRow[];
545
+ truncated?: boolean;
546
+ truncationReasons?: string[];
223
547
  semanticUi?: RemoteUiNodeSnapshot[];
224
548
  };
225
549
  };
@@ -227,12 +551,26 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
227
551
  const value = result.result?.value;
228
552
  if (value?.error) throw new Error(value.error);
229
553
  const visibleText = (value?.visibleText ?? "").replace(/\s+/g, " ").trim();
230
- const emptyStateMarkers = [...new Set(EMPTY_STATE_PATTERNS.flatMap((pattern) => (
231
- [...visibleText.matchAll(pattern)].map((match) => match[0])
232
- )))];
554
+ const loadingStateMarkers = stateMarkers(visibleText, LOADING_STATE_PATTERNS);
555
+ const errorStateMarkers = stateMarkers(visibleText, ERROR_STATE_PATTERNS);
556
+ const emptyStateMarkers = stateMarkers(visibleText, EMPTY_STATE_PATTERNS);
557
+ const rows = Array.isArray(value?.rows) ? value.rows : [];
558
+ const textShowsEllipsis = rows.some((row) => row.cells.some((cell) => /\u2026|\.\.\./.test(cell.text)));
559
+ const truncationReasons = Array.isArray(value?.truncationReasons)
560
+ ? value.truncationReasons.filter((reason): reason is string => typeof reason === "string")
561
+ : [];
562
+ if (textShowsEllipsis && !truncationReasons.includes("one or more cells are visibly clipped")) {
563
+ truncationReasons.push("one or more cells are visibly clipped");
564
+ }
233
565
  return {
234
566
  visibleText,
235
- emptyStateDetected: emptyStateMarkers.length > 0,
567
+ rows,
568
+ truncated: value?.truncated === true || textShowsEllipsis,
569
+ truncationReasons,
570
+ loadingStateDetected: value?.loadingStateDetected === true || loadingStateMarkers.length > 0,
571
+ errorStateDetected: value?.errorStateDetected === true || errorStateMarkers.length > 0,
572
+ errorStateMarkers,
573
+ emptyStateDetected: value?.emptyStateDetected === true || emptyStateMarkers.length > 0,
236
574
  emptyStateMarkers,
237
575
  semanticUi: Array.isArray(value?.semanticUi) ? value.semanticUi : [],
238
576
  };
package/src/cli/index.ts CHANGED
@@ -121,7 +121,7 @@ function createCoreCliCommands(
121
121
  aliases: ["screenshot"],
122
122
  description: "Render a desktop-style screenshot for a pane-backed market function",
123
123
  help: {
124
- usage: ["shot <function-or-pane> [argument] [--output path] [--width px] [--height px] [--key value]"],
124
+ usage: ["shot <function-or-pane> [argument] [--output path] [--width px] [--height px] [--theme id] [--scale n] [--watermark text] [--key value]"],
125
125
  },
126
126
  execute: async (args, ctx) => {
127
127
  await runPaneScreenshot(args, ctx);