gloomberb 0.9.9 → 0.10.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 (519) hide show
  1. package/README.md +41 -3
  2. package/README.zh-CN.md +294 -0
  3. package/package.json +10 -4
  4. package/patches/@opentui%2Fcore@0.3.2.patch +26 -69
  5. package/src/api-client/auth.ts +2 -0
  6. package/src/api-client/index.test.ts +4 -0
  7. package/src/api-client/types.ts +3 -0
  8. package/src/app/pane-runtime/pane-settings-runtime.tsx +1 -0
  9. package/src/app/pane-template-dialog-wizard.test.tsx +36 -0
  10. package/src/app/pane-template-dialog-wizard.tsx +10 -3
  11. package/src/app/runtime/desktop-deeplink.test.ts +15 -0
  12. package/src/app/runtime/desktop-deeplink.ts +21 -6
  13. package/src/app.tsx +40 -33
  14. package/src/capabilities/service-capabilities.ts +7 -2
  15. package/src/cli/commands/automation.test.ts +202 -0
  16. package/src/cli/commands/automation.ts +153 -36
  17. package/src/cli/commands/market.ts +1 -14
  18. package/src/cli/commands/system.ts +4 -1
  19. package/src/cli/context.ts +1 -1
  20. package/src/cli/desktop-pane-shot.ts +65 -4
  21. package/src/cli/entry.ts +1 -1
  22. package/src/cli/pane-functions/capabilities.ts +532 -0
  23. package/src/cli/pane-functions/catalog.ts +39 -4
  24. package/src/cli/pane-functions/data.test.ts +17 -0
  25. package/src/cli/pane-functions/data.ts +55 -13
  26. package/src/cli/pane-functions/discovery.ts +36 -9
  27. package/src/cli/pane-functions/index.test.ts +104 -1
  28. package/src/cli/pane-functions/index.ts +64 -5
  29. package/src/cli/pane-functions/options.ts +21 -6
  30. package/src/cli/pane-functions/report.ts +735 -72
  31. package/src/cli/pane-functions/resolver.ts +70 -6
  32. package/src/cli/pane-functions/screenshot.test.ts +431 -0
  33. package/src/cli/pane-functions/screenshot.ts +952 -8
  34. package/src/components/chart/composite/column-layout.ts +215 -0
  35. package/src/components/chart/composite/composite-chart.test.tsx +630 -0
  36. package/src/components/chart/composite/composite-chart.tsx +1148 -0
  37. package/src/components/chart/composite/format.test.ts +137 -0
  38. package/src/components/chart/composite/format.ts +135 -0
  39. package/src/components/chart/composite/index.ts +38 -0
  40. package/src/components/chart/composite/interactions.test.ts +109 -0
  41. package/src/components/chart/composite/interactions.ts +247 -0
  42. package/src/components/chart/composite/price-series.test.ts +39 -0
  43. package/src/components/chart/composite/price-series.ts +78 -0
  44. package/src/components/chart/composite/rasterizer.ts +359 -0
  45. package/src/components/chart/composite/renderers.test.ts +610 -0
  46. package/src/components/chart/composite/scene.test.ts +464 -0
  47. package/src/components/chart/composite/scene.ts +553 -0
  48. package/src/components/chart/composite/text-renderer.ts +285 -0
  49. package/src/components/chart/composite/time-scale.test.ts +88 -0
  50. package/src/components/chart/composite/time-scale.ts +217 -0
  51. package/src/components/chart/composite/types.ts +141 -0
  52. package/src/components/chart/core/data.ts +81 -5
  53. package/src/components/chart/core/index.test.ts +128 -43
  54. package/src/components/chart/core/resolution.ts +0 -6
  55. package/src/components/chart/native/chart-rasterizer.ts +1 -3
  56. package/src/components/chart/native/kitty/index.test.ts +99 -120
  57. package/src/components/chart/native/kitty/manager.ts +3 -0
  58. package/src/components/chart/native/kitty/protocol.ts +4 -1
  59. package/src/components/chart/native/raster/crosshair.ts +79 -46
  60. package/src/components/chart/native/raster/types.ts +2 -11
  61. package/src/components/chart/native/surface/manager.ts +48 -3
  62. package/src/components/chart/price-axis-labels.tsx +18 -4
  63. package/src/components/command-bar/commands/direct/index.ts +25 -0
  64. package/src/components/command-bar/commands/registry.ts +9 -0
  65. package/src/components/command-bar/helpers.test.ts +4 -1
  66. package/src/components/command-bar/list/view.tsx +18 -6
  67. package/src/components/command-bar/multi-select-picker.tsx +6 -5
  68. package/src/components/command-bar/pane-settings.test.ts +141 -0
  69. package/src/components/command-bar/pane-settings.ts +61 -1
  70. package/src/components/command-bar/panel/body.tsx +7 -6
  71. package/src/components/command-bar/routes/root/command-items.ts +4 -4
  72. package/src/components/command-bar/routes/root/results.ts +10 -1
  73. package/src/components/command-bar/routes/root/selection.test.ts +54 -0
  74. package/src/components/command-bar/routes/root/selection.ts +13 -0
  75. package/src/components/command-bar/routing/actions.ts +18 -2
  76. package/src/components/command-bar/routing/list-state.ts +2 -1
  77. package/src/components/command-bar/surface/index.test.tsx +36 -4
  78. package/src/components/command-bar/surface/pane.test.tsx +93 -0
  79. package/src/components/command-bar/theme-picker.tsx +2 -1
  80. package/src/components/command-bar/view-model.ts +19 -20
  81. package/src/components/command-bar/workflow/body.tsx +2 -1
  82. package/src/components/command-bar/workflow/field-row.tsx +3 -2
  83. package/src/components/command-bar/workflow/fields.ts +4 -2
  84. package/src/components/command-bar/workflow/ops.test.ts +161 -0
  85. package/src/components/command-bar/workflow/ops.ts +20 -50
  86. package/src/components/command-bar/workflow/route-actions.test.ts +34 -0
  87. package/src/components/command-bar/workflow/route-actions.ts +4 -0
  88. package/src/components/command-bar/workflow/types.ts +1 -0
  89. package/src/components/feed-data-table/stack-view.tsx +6 -5
  90. package/src/components/index.ts +13 -1
  91. package/src/components/layout/detached-pane-shell.tsx +2 -1
  92. package/src/components/layout/floating-pane.tsx +3 -0
  93. package/src/components/layout/header.tsx +15 -8
  94. package/src/components/layout/pane/footer/index.test.tsx +22 -3
  95. package/src/components/layout/pane/footer/index.tsx +3 -1
  96. package/src/components/layout/pane/footer/model.ts +9 -2
  97. package/src/components/layout/pane/header.tsx +4 -6
  98. package/src/components/layout/pane/index.tsx +3 -0
  99. package/src/components/layout/pane/sidebar.test.tsx +100 -0
  100. package/src/components/layout/pane/sidebar.tsx +239 -0
  101. package/src/components/layout/pane/title.ts +4 -3
  102. package/src/components/layout/shell/action-menu-overlay.tsx +5 -3
  103. package/src/components/layout/shell/drag/runtime.ts +2 -0
  104. package/src/components/layout/shell/menu.test.ts +19 -0
  105. package/src/components/layout/shell/menu.ts +8 -5
  106. package/src/components/layout/shell/native/pointer-runtime.ts +4 -1
  107. package/src/components/layout/shell/pane/layers.tsx +2 -2
  108. package/src/components/layout/status-bar.tsx +8 -7
  109. package/src/components/layout/window-controls.tsx +4 -3
  110. package/src/components/metric-treemap/index.tsx +3 -2
  111. package/src/components/onboarding/onboarding-steps.tsx +30 -29
  112. package/src/components/onboarding/onboarding-wizard.tsx +5 -4
  113. package/src/components/onboarding/portfolio-step/broker-fields-panel.tsx +15 -12
  114. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +17 -16
  115. package/src/components/onboarding/portfolio-step/broker-sync-panel.tsx +6 -5
  116. package/src/components/onboarding/portfolio-step/choose-panel.tsx +5 -4
  117. package/src/components/pane-settings-dialog/desktop.tsx +25 -3
  118. package/src/components/pane-settings-dialog/field-dialogs.tsx +5 -4
  119. package/src/components/pane-settings-dialog/tui.tsx +11 -9
  120. package/src/components/pane-settings-dialog/value.ts +8 -5
  121. package/src/components/pane-settings-dialog.test.tsx +235 -0
  122. package/src/components/pane-settings-dialog.tsx +24 -0
  123. package/src/components/pane-template-wizard.tsx +6 -2
  124. package/src/components/ticker/list-table-view.tsx +3 -2
  125. package/src/components/ui/button.tsx +3 -1
  126. package/src/components/ui/choice-dialog.tsx +6 -1
  127. package/src/components/ui/confirm-dialog.tsx +3 -2
  128. package/src/components/ui/fields.tsx +10 -1
  129. package/src/components/ui/frame.tsx +4 -1
  130. package/src/components/ui/index.ts +5 -0
  131. package/src/components/ui/inline-quick-add.tsx +104 -0
  132. package/src/components/ui/list-view.tsx +5 -4
  133. package/src/components/ui/loading.tsx +2 -1
  134. package/src/components/ui/multi-select/dialog.tsx +188 -44
  135. package/src/components/ui/popover.tsx +10 -0
  136. package/src/components/ui/shortcut-hint.tsx +2 -0
  137. package/src/components/ui/status.tsx +4 -3
  138. package/src/components/ui/tabs.tsx +19 -8
  139. package/src/components/ui/toggle.tsx +44 -3
  140. package/src/components/ui/ui.test.tsx +67 -5
  141. package/src/core/app-services.ts +4 -6
  142. package/src/core/state/session-persistence.ts +3 -2
  143. package/src/data/config/chart-settings.ts +712 -0
  144. package/src/data/config/layout.ts +24 -5
  145. package/src/data/config/store/index.test.ts +377 -14
  146. package/src/data/config/store/normalize.ts +79 -65
  147. package/src/data/fred-series.ts +137 -0
  148. package/src/i18n/index.test.ts +114 -0
  149. package/src/i18n/index.ts +147 -0
  150. package/src/i18n/ja.ts +1013 -0
  151. package/src/i18n/ko.ts +1011 -0
  152. package/src/i18n/languages.test.ts +31 -0
  153. package/src/i18n/languages.ts +72 -0
  154. package/src/i18n/zh-cn.ts +1013 -0
  155. package/src/i18n/zh-tw.ts +1013 -0
  156. package/src/plugins/builtin/account-management/footer.ts +3 -2
  157. package/src/plugins/builtin/account-management/form-components.tsx +3 -2
  158. package/src/plugins/builtin/account-management/keyboard.ts +6 -0
  159. package/src/plugins/builtin/account-management/model.ts +25 -21
  160. package/src/plugins/builtin/account-management/navigation.test.ts +27 -0
  161. package/src/plugins/builtin/account-management/navigation.ts +24 -0
  162. package/src/plugins/builtin/account-management/pane.tsx +120 -85
  163. package/src/plugins/builtin/account-management/password-dialog.tsx +10 -9
  164. package/src/plugins/builtin/ai/agent-history.ts +139 -0
  165. package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +133 -51
  166. package/src/plugins/builtin/ai/ask-ai.test.tsx +83 -22
  167. package/src/plugins/builtin/ai/index.test.ts +302 -0
  168. package/src/plugins/builtin/ai/index.tsx +374 -40
  169. package/src/plugins/builtin/ai/pane-settings.test.ts +155 -0
  170. package/src/plugins/builtin/ai/pane-settings.ts +319 -0
  171. package/src/plugins/builtin/ai/pi/credential-store.test.ts +80 -0
  172. package/src/plugins/builtin/ai/pi/credential-store.ts +138 -0
  173. package/src/plugins/builtin/ai/pi/file-store.ts +138 -0
  174. package/src/plugins/builtin/ai/pi/host.test.ts +500 -0
  175. package/src/plugins/builtin/ai/pi/host.ts +564 -0
  176. package/src/plugins/builtin/ai/pi/index.ts +5 -0
  177. package/src/plugins/builtin/ai/pi/models-store.test.ts +44 -0
  178. package/src/plugins/builtin/ai/pi/models-store.ts +141 -0
  179. package/src/plugins/builtin/ai/pi/providers.ts +68 -0
  180. package/src/plugins/builtin/ai/pi/runtime.test.ts +240 -0
  181. package/src/plugins/builtin/ai/pi/runtime.ts +635 -0
  182. package/src/plugins/builtin/ai/providers.test.ts +68 -0
  183. package/src/plugins/builtin/ai/providers.ts +163 -38
  184. package/src/plugins/builtin/ai/runner-selection.test.ts +174 -0
  185. package/src/plugins/builtin/ai/runner-selection.ts +193 -0
  186. package/src/plugins/builtin/ai/runner-selector.tsx +153 -0
  187. package/src/plugins/builtin/ai/runner.test.ts +198 -0
  188. package/src/plugins/builtin/ai/runner.ts +276 -73
  189. package/src/plugins/builtin/ai/screener/contract.test.ts +26 -73
  190. package/src/plugins/builtin/ai/screener/contract.ts +14 -52
  191. package/src/plugins/builtin/ai/screener/editor-runtime.ts +55 -5
  192. package/src/plugins/builtin/ai/screener/editor.tsx +42 -36
  193. package/src/plugins/builtin/ai/screener/footer.ts +82 -67
  194. package/src/plugins/builtin/ai/screener/keyboard.ts +27 -11
  195. package/src/plugins/builtin/ai/screener/model.test.ts +24 -0
  196. package/src/plugins/builtin/ai/screener/model.ts +9 -3
  197. package/src/plugins/builtin/ai/screener/pane.test.tsx +142 -55
  198. package/src/plugins/builtin/ai/screener/pane.tsx +95 -97
  199. package/src/plugins/builtin/ai/screener/results-view.tsx +8 -41
  200. package/src/plugins/builtin/ai/screener/runner.ts +41 -25
  201. package/src/plugins/builtin/ai/screener/tabs-bar.tsx +2 -2
  202. package/src/plugins/builtin/ai/use-runtime-providers.ts +28 -0
  203. package/src/plugins/builtin/ai/workspace/model.test.ts +227 -0
  204. package/src/plugins/builtin/ai/workspace/model.ts +308 -0
  205. package/src/plugins/builtin/ai/workspace/pane.test.tsx +197 -0
  206. package/src/plugins/builtin/ai/workspace/pane.tsx +896 -0
  207. package/src/plugins/builtin/analytics/index.test.tsx +3 -3
  208. package/src/plugins/builtin/analytics/index.tsx +3 -8
  209. package/src/plugins/builtin/broker-manager/detail.tsx +25 -24
  210. package/src/plugins/builtin/broker-manager/index.test.tsx +1 -1
  211. package/src/plugins/builtin/broker-manager/index.tsx +9 -11
  212. package/src/plugins/builtin/broker-manager/model.ts +11 -10
  213. package/src/plugins/builtin/broker-manager/pane-actions.tsx +22 -21
  214. package/src/plugins/builtin/broker-manager/table.ts +9 -8
  215. package/src/plugins/builtin/changelog/index.tsx +3 -8
  216. package/src/plugins/builtin/chart-composer/chart-spec.ts +89 -0
  217. package/src/plugins/builtin/chart-composer/cli-options.ts +98 -0
  218. package/src/plugins/builtin/chart-composer/date-window-dialog.tsx +120 -0
  219. package/src/plugins/builtin/chart-composer/editor.test.tsx +332 -0
  220. package/src/plugins/builtin/chart-composer/editor.tsx +1107 -0
  221. package/src/plugins/builtin/chart-composer/index.tsx +228 -0
  222. package/src/plugins/builtin/chart-composer/pane.tsx +645 -0
  223. package/src/plugins/builtin/chart-composer/presets.test.ts +561 -0
  224. package/src/plugins/builtin/chart-composer/presets.ts +759 -0
  225. package/src/plugins/builtin/chart-composer/quick-add.test.tsx +245 -0
  226. package/src/plugins/builtin/chart-composer/quick-add.tsx +409 -0
  227. package/src/plugins/builtin/chart-composer/semantic.ts +86 -0
  228. package/src/plugins/builtin/chart-composer/series-catalog.test.ts +65 -0
  229. package/src/plugins/builtin/chart-composer/series-catalog.ts +295 -0
  230. package/src/plugins/builtin/chart-composer/settings.test.ts +163 -0
  231. package/src/plugins/builtin/chart-composer/settings.ts +397 -0
  232. package/src/plugins/builtin/chart-composer/shortcuts.test.ts +44 -0
  233. package/src/plugins/builtin/chart-composer/shortcuts.ts +35 -0
  234. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +103 -0
  235. package/src/plugins/builtin/chat/channel-labels.ts +5 -4
  236. package/src/plugins/builtin/chat/channels.ts +12 -11
  237. package/src/plugins/builtin/chat/chat.test.tsx +32 -0
  238. package/src/plugins/builtin/chat/content/composer-runtime.ts +6 -1
  239. package/src/plugins/builtin/chat/content/composer.tsx +9 -8
  240. package/src/plugins/builtin/chat/content/index.tsx +36 -1
  241. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +8 -7
  242. package/src/plugins/builtin/chat/content/transcript.tsx +9 -3
  243. package/src/plugins/builtin/chat/controller/utils.ts +10 -9
  244. package/src/plugins/builtin/chat/message/desktop.tsx +17 -10
  245. package/src/plugins/builtin/chat/message/profile-popover.test.tsx +122 -3
  246. package/src/plugins/builtin/chat/message/profile-popover.tsx +38 -1
  247. package/src/plugins/builtin/chat/message/terminal.tsx +19 -10
  248. package/src/plugins/builtin/chat/pane-state.test.ts +31 -0
  249. package/src/plugins/builtin/chat/pane-state.ts +30 -0
  250. package/src/plugins/builtin/chat/pane.tsx +27 -4
  251. package/src/plugins/builtin/chat/profile-popover.ts +62 -5
  252. package/src/plugins/builtin/chat/sidebar.test.tsx +4 -7
  253. package/src/plugins/builtin/chat/sidebar.tsx +107 -168
  254. package/src/plugins/builtin/chat/test-harness.tsx +6 -0
  255. package/src/plugins/builtin/cloud/auth-actions.tsx +5 -4
  256. package/src/plugins/builtin/cloud/plugin.tsx +150 -127
  257. package/src/plugins/builtin/composite-plugins.ts +71 -0
  258. package/src/plugins/builtin/correlation/index.test.tsx +4 -4
  259. package/src/plugins/builtin/correlation/index.tsx +3 -8
  260. package/src/plugins/builtin/earnings/index.tsx +37 -21
  261. package/src/plugins/builtin/earnings/model.test.ts +52 -0
  262. package/src/plugins/builtin/earnings/model.ts +32 -0
  263. package/src/plugins/builtin/econ/detail-view.tsx +17 -4
  264. package/src/plugins/builtin/econ/fred-cache.test.ts +57 -11
  265. package/src/plugins/builtin/econ/index.tsx +16 -20
  266. package/src/plugins/builtin/fear-greed/index.tsx +2 -8
  267. package/src/plugins/builtin/fx-matrix/index.tsx +3 -8
  268. package/src/plugins/builtin/help/components.tsx +5 -4
  269. package/src/plugins/builtin/help/index.tsx +14 -17
  270. package/src/plugins/builtin/holders/index.tsx +2 -8
  271. package/src/plugins/builtin/insider/index.tsx +2 -7
  272. package/src/plugins/builtin/kelly-sizer/index.test.tsx +2 -2
  273. package/src/plugins/builtin/kelly-sizer/index.tsx +3 -7
  274. package/src/plugins/builtin/kelly-sizer/test-support.tsx +3 -3
  275. package/src/plugins/builtin/layout-manager/index.tsx +3 -7
  276. package/src/plugins/builtin/market-heatmap/index.tsx +3 -8
  277. package/src/plugins/builtin/market-movers/index.tsx +3 -8
  278. package/src/plugins/builtin/news/index.tsx +12 -16
  279. package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +3 -3
  280. package/src/plugins/builtin/news/wire/index.tsx +83 -73
  281. package/src/plugins/builtin/options/index.tsx +2 -8
  282. package/src/plugins/builtin/plugin-module.test.ts +89 -0
  283. package/src/plugins/builtin/plugin-module.ts +116 -0
  284. package/src/plugins/builtin/portfolio-list/header.tsx +4 -3
  285. package/src/plugins/builtin/portfolio-list/index.test.ts +17 -0
  286. package/src/plugins/builtin/portfolio-list/index.tsx +3 -5
  287. package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +3 -3
  288. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +53 -53
  289. package/src/plugins/builtin/portfolio-list/settings.ts +5 -3
  290. package/src/plugins/builtin/portfolio-list/summary/index.tsx +4 -3
  291. package/src/plugins/builtin/research/index.tsx +2 -8
  292. package/src/plugins/builtin/sec/index.tsx +2 -8
  293. package/src/plugins/builtin/sectors/index.tsx +3 -8
  294. package/src/plugins/builtin/thirteenf/index.tsx +3 -8
  295. package/src/plugins/builtin/thirteenf/pane.test.tsx +1 -1
  296. package/src/plugins/builtin/ticker-detail/chart-tab-switch.test.tsx +49 -23
  297. package/src/plugins/builtin/ticker-detail/index.test.tsx +15 -136
  298. package/src/plugins/builtin/ticker-detail/index.tsx +2 -93
  299. package/src/plugins/builtin/ticker-detail/overview/components.tsx +6 -5
  300. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +41 -17
  301. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -3
  302. package/src/plugins/builtin/ticker-detail/quote-monitor/index.test.tsx +1 -1
  303. package/src/plugins/builtin/ticker-detail/settings.ts +3 -50
  304. package/src/plugins/builtin/ticker-research-backend-plugin.ts +17 -0
  305. package/src/plugins/builtin/ticker-research-plugin.ts +27 -0
  306. package/src/plugins/builtin/tv/channels.ts +27 -0
  307. package/src/plugins/builtin/tv/index.tsx +40 -0
  308. package/src/plugins/builtin/tv/pane.tsx +286 -0
  309. package/src/plugins/builtin/tv/youtube-stream.ts +100 -0
  310. package/src/plugins/builtin/world-indices/index.tsx +3 -8
  311. package/src/plugins/builtin/yield-curve/index.tsx +8 -8
  312. package/src/plugins/catalog-backend.test.ts +21 -0
  313. package/src/plugins/catalog-backend.ts +42 -0
  314. package/src/plugins/catalog-ui.ts +6 -14
  315. package/src/plugins/ibkr/instance-selection.test.ts +0 -1
  316. package/src/plugins/ownership.ts +143 -0
  317. package/src/plugins/prediction-markets/backend-plugin.ts +19 -0
  318. package/src/plugins/prediction-markets/chart.test.tsx +7 -5
  319. package/src/plugins/prediction-markets/chart.tsx +25 -7
  320. package/src/plugins/registry/index.test.ts +51 -0
  321. package/src/plugins/registry/index.ts +8 -0
  322. package/src/plugins/registry/pane-settings.ts +1 -1
  323. package/src/plugins/runtime/index.test.tsx +2 -2
  324. package/src/remote/app-host.tsx +16 -3
  325. package/src/remote/controller.test.ts +102 -1
  326. package/src/remote/controller.ts +122 -0
  327. package/src/remote/types.ts +11 -0
  328. package/src/renderers/electrobun/bun/core-capabilities.ts +123 -27
  329. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +5 -1
  330. package/src/renderers/electrobun/bun/desktop/initialization.ts +5 -1
  331. package/src/renderers/electrobun/bun/desktop/media.ts +15 -0
  332. package/src/renderers/electrobun/bun/desktop/window-style.ts +4 -1
  333. package/src/renderers/electrobun/bun/desktop/workspace/index.test.ts +32 -1
  334. package/src/renderers/electrobun/bun/desktop/workspace/index.ts +10 -0
  335. package/src/renderers/electrobun/bun/index.ts +5 -0
  336. package/src/renderers/electrobun/bun/tui-entry.ts +1 -6
  337. package/src/renderers/electrobun/view/ai-host.ts +124 -19
  338. package/src/renderers/electrobun/view/backend-rpc.ts +4 -0
  339. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +151 -39
  340. package/src/renderers/electrobun/view/desktop/popover.tsx +166 -0
  341. package/src/renderers/electrobun/view/dialog-host.test.ts +25 -0
  342. package/src/renderers/electrobun/view/dialog-host.tsx +75 -12
  343. package/src/renderers/electrobun/view/host/box.tsx +5 -0
  344. package/src/renderers/electrobun/view/host/chart-surface.tsx +10 -9
  345. package/src/renderers/electrobun/view/host/input.tsx +9 -19
  346. package/src/renderers/electrobun/view/host/media-surface.tsx +170 -0
  347. package/src/renderers/electrobun/view/host/mouse.ts +1 -0
  348. package/src/renderers/electrobun/view/host/style.ts +1 -1
  349. package/src/renderers/electrobun/view/host/tabs.tsx +4 -3
  350. package/src/renderers/electrobun/view/main.tsx +10 -3
  351. package/src/renderers/electrobun/view/styles.css +17 -0
  352. package/src/renderers/electrobun/view/ui-host.tsx +7 -0
  353. package/src/renderers/opentui/chart-surface.test.tsx +52 -6
  354. package/src/renderers/opentui/chart-surface.tsx +95 -11
  355. package/src/renderers/opentui/dialog-host.tsx +36 -5
  356. package/src/renderers/opentui/host.tsx +39 -0
  357. package/src/renderers/opentui/start.tsx +30 -0
  358. package/src/renderers/opentui/ui-host.tsx +2 -0
  359. package/src/sources/provider-router/index.test.ts +43 -0
  360. package/src/sources/provider-router/index.ts +4 -0
  361. package/src/sources/provider-router/supplemental.ts +46 -0
  362. package/src/sources/sec-edgar.test.ts +203 -2
  363. package/src/sources/sec-edgar.ts +70 -15
  364. package/src/state/app/context/index.tsx +13 -1
  365. package/src/state/app/context/selector.test.tsx +62 -1
  366. package/src/state/session-persistence.test.ts +13 -2
  367. package/src/sync/controller.test.ts +190 -3
  368. package/src/sync/controller.ts +76 -45
  369. package/src/sync/core-contributors.test.ts +121 -0
  370. package/src/sync/core-contributors.ts +104 -27
  371. package/src/sync/react.ts +6 -8
  372. package/src/sync/types.ts +3 -0
  373. package/src/time-series/alignment.ts +158 -0
  374. package/src/time-series/economic.ts +49 -0
  375. package/src/time-series/field-catalog.ts +306 -0
  376. package/src/time-series/fundamentals.test.ts +505 -0
  377. package/src/time-series/fundamentals.ts +842 -0
  378. package/src/time-series/hooks.ts +45 -0
  379. package/src/time-series/index.ts +14 -0
  380. package/src/time-series/live-quotes.test.ts +219 -0
  381. package/src/time-series/live-quotes.ts +203 -0
  382. package/src/time-series/market-resolution.ts +53 -0
  383. package/src/time-series/market.ts +125 -0
  384. package/src/time-series/reporting.ts +98 -0
  385. package/src/time-series/resolve.test.ts +1281 -0
  386. package/src/time-series/resolve.ts +783 -0
  387. package/src/time-series/spec-studies.test.ts +400 -0
  388. package/src/time-series/spec.ts +487 -0
  389. package/src/time-series/studies.ts +563 -0
  390. package/src/time-series/transforms.test.ts +105 -0
  391. package/src/time-series/transforms.ts +152 -0
  392. package/src/time-series/types.ts +162 -0
  393. package/src/time-series/use-chart-resolution.ts +166 -0
  394. package/src/types/config.ts +9 -11
  395. package/src/types/desktop-window.ts +1 -0
  396. package/src/types/financials.ts +4 -0
  397. package/src/types/media.ts +17 -0
  398. package/src/types/plugin.ts +32 -1
  399. package/src/ui/context-menu.tsx +15 -3
  400. package/src/ui/dialog.tsx +14 -2
  401. package/src/ui/host.tsx +40 -1
  402. package/src/ui/index.tsx +9 -0
  403. package/src/utils/exchanges.test.ts +13 -1
  404. package/src/utils/exchanges.ts +10 -0
  405. package/src/utils/financial-statements.test.ts +138 -0
  406. package/src/utils/financial-statements.ts +115 -8
  407. package/src/utils/format.test.ts +11 -1
  408. package/src/utils/format.ts +7 -0
  409. package/src/version.ts +1 -1
  410. package/src/components/chart/bar-chart-renderer.test.ts +0 -233
  411. package/src/components/chart/bar-chart-renderer.ts +0 -445
  412. package/src/components/chart/comparison/data.test.ts +0 -159
  413. package/src/components/chart/comparison/data.ts +0 -302
  414. package/src/components/chart/comparison/renderer.test.ts +0 -187
  415. package/src/components/chart/comparison/renderer.ts +0 -282
  416. package/src/components/chart/comparison/scene.ts +0 -178
  417. package/src/components/chart/comparison-stock-chart/axis-width.ts +0 -99
  418. package/src/components/chart/comparison-stock-chart/canvas.ts +0 -148
  419. package/src/components/chart/comparison-stock-chart/controls.ts +0 -252
  420. package/src/components/chart/comparison-stock-chart/helpers.ts +0 -101
  421. package/src/components/chart/comparison-stock-chart/interactions.ts +0 -208
  422. package/src/components/chart/comparison-stock-chart/layout-metrics.ts +0 -62
  423. package/src/components/chart/comparison-stock-chart/layout.tsx +0 -155
  424. package/src/components/chart/comparison-stock-chart/legend.tsx +0 -74
  425. package/src/components/chart/comparison-stock-chart/native-surfaces.ts +0 -89
  426. package/src/components/chart/comparison-stock-chart/render-data.ts +0 -227
  427. package/src/components/chart/comparison-stock-chart/render-output.ts +0 -140
  428. package/src/components/chart/comparison-stock-chart/selection-runtime.ts +0 -128
  429. package/src/components/chart/comparison-stock-chart/sources.ts +0 -51
  430. package/src/components/chart/comparison-stock-chart/surface-runtime.ts +0 -159
  431. package/src/components/chart/comparison-stock-chart/toolbar.tsx +0 -117
  432. package/src/components/chart/comparison-stock-chart/types.ts +0 -22
  433. package/src/components/chart/comparison-stock-chart/view.tsx +0 -572
  434. package/src/components/chart/comparison-stock-chart/viewport-runtime.ts +0 -239
  435. package/src/components/chart/comparison-stock-chart.tsx +0 -24
  436. package/src/components/chart/core/axis-measure.ts +0 -33
  437. package/src/components/chart/core/controller.test.ts +0 -157
  438. package/src/components/chart/core/controller.ts +0 -349
  439. package/src/components/chart/core/indicator-selector.tsx +0 -75
  440. package/src/components/chart/core/keyboard.ts +0 -24
  441. package/src/components/chart/core/pane-settings.test.ts +0 -52
  442. package/src/components/chart/core/pane-settings.ts +0 -141
  443. package/src/components/chart/core/scroll.test.ts +0 -64
  444. package/src/components/chart/core/scroll.ts +0 -82
  445. package/src/components/chart/core/window-viewport.test.ts +0 -69
  446. package/src/components/chart/core/window-viewport.ts +0 -162
  447. package/src/components/chart/display-cursor.ts +0 -153
  448. package/src/components/chart/indicators/bands.test.ts +0 -36
  449. package/src/components/chart/indicators/bands.ts +0 -29
  450. package/src/components/chart/indicators/moving-averages.test.ts +0 -39
  451. package/src/components/chart/indicators/moving-averages.ts +0 -42
  452. package/src/components/chart/indicators/options.test.ts +0 -32
  453. package/src/components/chart/indicators/options.ts +0 -107
  454. package/src/components/chart/indicators/oscillators.test.ts +0 -58
  455. package/src/components/chart/indicators/oscillators.ts +0 -92
  456. package/src/components/chart/native/canvas-bitmaps.ts +0 -140
  457. package/src/components/chart/native/raster/comparison-chart.ts +0 -162
  458. package/src/components/chart/native/surface/hooks.ts +0 -144
  459. package/src/components/chart/native/surface/rendering.ts +0 -211
  460. package/src/components/chart/static/bar-chart-surface.test.tsx +0 -101
  461. package/src/components/chart/static/bar-chart-surface.tsx +0 -292
  462. package/src/components/chart/stock-chart/auto.ts +0 -216
  463. package/src/components/chart/stock-chart/axis-width.ts +0 -146
  464. package/src/components/chart/stock-chart/bitmaps.ts +0 -22
  465. package/src/components/chart/stock-chart/canvas.ts +0 -202
  466. package/src/components/chart/stock-chart/controls.tsx +0 -149
  467. package/src/components/chart/stock-chart/cursor.ts +0 -60
  468. package/src/components/chart/stock-chart/data-runtime.ts +0 -128
  469. package/src/components/chart/stock-chart/footer.test.ts +0 -65
  470. package/src/components/chart/stock-chart/footer.ts +0 -303
  471. package/src/components/chart/stock-chart/geometry.ts +0 -160
  472. package/src/components/chart/stock-chart/index.test.ts +0 -532
  473. package/src/components/chart/stock-chart/index.tsx +0 -29
  474. package/src/components/chart/stock-chart/indicators.ts +0 -226
  475. package/src/components/chart/stock-chart/interaction-runtime.ts +0 -44
  476. package/src/components/chart/stock-chart/interactions.ts +0 -300
  477. package/src/components/chart/stock-chart/keyboard.ts +0 -373
  478. package/src/components/chart/stock-chart/layout-metrics.ts +0 -62
  479. package/src/components/chart/stock-chart/layout.tsx +0 -177
  480. package/src/components/chart/stock-chart/native-surfaces.ts +0 -121
  481. package/src/components/chart/stock-chart/presentation.ts +0 -67
  482. package/src/components/chart/stock-chart/renderer-switch.test.tsx +0 -263
  483. package/src/components/chart/stock-chart/rendering/bounds.ts +0 -116
  484. package/src/components/chart/stock-chart/rendering/candidates.ts +0 -145
  485. package/src/components/chart/stock-chart/rendering/data.ts +0 -370
  486. package/src/components/chart/stock-chart/rendering/invalidation.ts +0 -68
  487. package/src/components/chart/stock-chart/rendering/output.ts +0 -304
  488. package/src/components/chart/stock-chart/rendering/planning.ts +0 -122
  489. package/src/components/chart/stock-chart/rendering/projection.test.tsx +0 -98
  490. package/src/components/chart/stock-chart/rendering/projection.ts +0 -112
  491. package/src/components/chart/stock-chart/rendering/state.test.ts +0 -70
  492. package/src/components/chart/stock-chart/rendering/state.ts +0 -283
  493. package/src/components/chart/stock-chart/requests.ts +0 -337
  494. package/src/components/chart/stock-chart/resolution-support.ts +0 -96
  495. package/src/components/chart/stock-chart/resolved/runtime.ts +0 -289
  496. package/src/components/chart/stock-chart/resolved.tsx +0 -359
  497. package/src/components/chart/stock-chart/settings.ts +0 -81
  498. package/src/components/chart/stock-chart/surface-runtime.ts +0 -62
  499. package/src/components/chart/stock-chart/types.ts +0 -25
  500. package/src/components/chart/stock-chart/view.tsx +0 -155
  501. package/src/components/chart/stock-chart/viewport/effects.ts +0 -293
  502. package/src/components/chart/stock-chart/viewport/index.ts +0 -144
  503. package/src/components/chart/stock-chart/viewport/runtime.ts +0 -375
  504. package/src/components/chart/time-axis-label.tsx +0 -175
  505. package/src/plugins/builtin/ai/gloomberb-cli.ts +0 -71
  506. package/src/plugins/builtin/ai/screener/action-bar.tsx +0 -58
  507. package/src/plugins/builtin/comparison-chart/index.test.tsx +0 -848
  508. package/src/plugins/builtin/comparison-chart/index.tsx +0 -194
  509. package/src/plugins/builtin/econ/fred-cache.ts +0 -106
  510. package/src/plugins/builtin/plugin-groups.ts +0 -130
  511. package/src/plugins/builtin/ticker-detail/chart-pane.tsx +0 -91
  512. package/src/plugins/builtin/ticker-detail/chart-tab.tsx +0 -100
  513. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/content.tsx +0 -296
  514. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/data.ts +0 -62
  515. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/model.ts +0 -608
  516. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/settings.ts +0 -65
  517. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/types.ts +0 -58
  518. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph.tsx +0 -138
  519. package/src/plugins/builtin/ticker-detail/data-panes.test.ts +0 -220
@@ -1,9 +1,13 @@
1
- import type { TickerFinancials } from "../../types/financials";
1
+ import type { PricePoint, TickerFinancials } from "../../types/financials";
2
2
  import type { TickerRecord } from "../../types/ticker";
3
- import { LEGACY_TICKER_DETAIL_PANE_ID } from "../../types/config";
3
+ import type { TimeRange } from "../../components/chart/core/types";
4
+ import { subtractTimeRange } from "../../components/chart/core/date-window";
5
+ import { CHART_COMPOSER_PANE_ID, LEGACY_TICKER_DETAIL_PANE_ID } from "../../types/config";
6
+ import { parseChartSpec } from "../../plugins/builtin/chart-composer/chart-spec";
4
7
  import { normalizeTickerInput } from "../../tickers/search";
5
8
  import type { MarketContext } from "../types";
6
9
  import { cleanTickerInput } from "./options";
10
+ import { parsePublicTickerKey, publicTickerKey } from "../../utils/exchanges";
7
11
  import type { ResolvedPaneFunction } from "./resolver";
8
12
 
9
13
  const SHOT_PRICE_HISTORY_RANGE = "5Y" as const;
@@ -13,12 +17,18 @@ const FINANCIAL_ANALYSIS_TEMPLATE_ID = "financial-analysis-pane";
13
17
  export async function fetchTickerFinancials(
14
18
  context: MarketContext,
15
19
  symbol: string,
16
- ): Promise<{ tickerFile: TickerRecord | null; financials: TickerFinancials }> {
20
+ ): Promise<{
21
+ tickerFile: TickerRecord | null;
22
+ financials: TickerFinancials;
23
+ instrument: { symbol: string; exchange?: string };
24
+ }> {
17
25
  const normalized = cleanTickerInput(symbol);
18
- const tickerFile = await context.store.loadTicker(normalized);
19
- const exchange = tickerFile?.metadata.exchange ?? "";
20
- const financials = await context.dataProvider.getTickerFinancials(normalized, exchange);
21
- return { tickerFile, financials };
26
+ const instrument = parsePublicTickerKey(normalized);
27
+ const tickerFile = await context.store.loadTicker(normalized)
28
+ ?? (instrument.symbol !== normalized ? await context.store.loadTicker(instrument.symbol) : null);
29
+ const exchange = instrument.exchange ?? tickerFile?.metadata.exchange ?? "";
30
+ const financials = await context.dataProvider.getTickerFinancials(instrument.symbol, exchange);
31
+ return { tickerFile, financials, instrument: { symbol: instrument.symbol, ...(exchange ? { exchange } : {}) } };
22
32
  }
23
33
 
24
34
  export async function withShotPriceHistory(
@@ -28,12 +38,14 @@ export async function withShotPriceHistory(
28
38
  financials: TickerFinancials,
29
39
  ): Promise<TickerFinancials> {
30
40
  if (financials.priceHistory?.length) return financials;
31
- const exchange = tickerFile?.metadata.exchange
41
+ const instrument = parsePublicTickerKey(symbol);
42
+ const exchange = instrument.exchange
43
+ ?? tickerFile?.metadata.exchange
32
44
  ?? financials.quote?.listingExchangeName
33
45
  ?? financials.quote?.exchangeName
34
46
  ?? "";
35
47
  try {
36
- const priceHistory = await context.dataProvider.getPriceHistory(symbol, exchange, SHOT_PRICE_HISTORY_RANGE);
48
+ const priceHistory = await context.dataProvider.getPriceHistory(instrument.symbol, exchange, SHOT_PRICE_HISTORY_RANGE);
37
49
  return priceHistory.length > 0 ? { ...financials, priceHistory } : financials;
38
50
  } catch {
39
51
  return financials;
@@ -54,13 +66,14 @@ export function isFinancialAnalysisFunction(resolved: ResolvedPaneFunction): boo
54
66
  }
55
67
 
56
68
  export function createFallbackTicker(symbol: string, financials: TickerFinancials | null, context: MarketContext): TickerRecord {
69
+ const instrument = parsePublicTickerKey(symbol);
57
70
  const quote = financials?.quote;
58
71
  return {
59
72
  metadata: {
60
- ticker: symbol,
61
- exchange: quote?.listingExchangeName ?? quote?.exchangeName ?? "",
73
+ ticker: instrument.symbol,
74
+ exchange: instrument.exchange ?? quote?.listingExchangeName ?? quote?.exchangeName ?? "",
62
75
  currency: quote?.currency ?? context.config.baseCurrency,
63
- name: quote?.name ?? symbol,
76
+ name: quote?.name ?? instrument.symbol,
64
77
  portfolios: [],
65
78
  watchlists: [],
66
79
  positions: [],
@@ -71,8 +84,37 @@ export function createFallbackTicker(symbol: string, financials: TickerFinancial
71
84
  }
72
85
 
73
86
  export function collectShotSymbols(resolved: ResolvedPaneFunction, rawArg: string): string[] {
74
- const symbols = resolved.createOptions?.symbols?.length
87
+ if (resolved.pane.id === CHART_COMPOSER_PANE_ID) {
88
+ const spec = parseChartSpec(resolved.instance.settings?.chartSpec);
89
+ if (spec) {
90
+ return [...new Set(spec.series.flatMap((series) => (
91
+ series.source.kind === "security"
92
+ ? [publicTickerKey(series.source.instrument.symbol, series.source.instrument.exchange)]
93
+ : []
94
+ )).filter(Boolean))];
95
+ }
96
+ }
97
+ let symbols = resolved.createOptions?.symbols?.length
75
98
  ? resolved.createOptions.symbols
76
99
  : [resolved.createOptions?.symbol ?? normalizeTickerInput(null, cleanTickerInput(rawArg))].filter((symbol): symbol is string => !!symbol);
100
+ if (resolved.capability.id === "security-relationship" && symbols.length === 1) {
101
+ symbols = [...symbols, "SPY"];
102
+ }
77
103
  return [...new Set(symbols.map(cleanTickerInput).filter(Boolean))];
78
104
  }
105
+
106
+ export function clipPriceHistoryToRange(points: PricePoint[], range: TimeRange): PricePoint[] {
107
+ if (range === "ALL" || points.length === 0) return points;
108
+ const dated = points.flatMap((point) => {
109
+ const date = point.date instanceof Date ? point.date : new Date(point.date);
110
+ return Number.isFinite(date.getTime()) ? [{ point, date }] : [];
111
+ });
112
+ const end = dated.reduce<Date | null>((latest, entry) => (
113
+ !latest || entry.date.getTime() > latest.getTime() ? entry.date : latest
114
+ ), null);
115
+ if (!end) return [];
116
+ const start = subtractTimeRange(end, range);
117
+ return dated
118
+ .filter(({ date }) => date.getTime() >= start.getTime() && date.getTime() <= end.getTime())
119
+ .map(({ point }) => point);
120
+ }
@@ -1,4 +1,11 @@
1
- import type { GloomPlugin, PaneDef, PaneTemplateDef } from "../../types/plugin";
1
+ import type {
2
+ GloomPlugin,
3
+ GloomPluginContext,
4
+ PaneDef,
5
+ PaneTemplateDef,
6
+ PluginPersistence,
7
+ } from "../../types/plugin";
8
+ import type { PersistedResourceValue } from "../../types/persistence";
2
9
  import type { MarketContext } from "../types";
3
10
  import type { PaneFunctionCatalog } from "./catalog";
4
11
 
@@ -6,9 +13,11 @@ export async function createPaneCatalog(context: MarketContext, plugins: GloomPl
6
13
  const panes = new Map<string, PaneDef>();
7
14
  const paneTemplates = new Map<string, PaneTemplateDef>();
8
15
  const setupPlugins: GloomPlugin[] = [];
9
- const fakePersistence = createFakePluginPersistence();
16
+ const fakePersistence = createDiscoveryPluginPersistence();
10
17
  // Collect setup-registered panes without booting the real plugin runtime or polling engines.
11
- const fakeContext = {
18
+ // Keep this object exhaustively typed so additions to GloomPluginContext fail at
19
+ // compile time instead of crashing `gloomberb catalog` at runtime.
20
+ const discoveryContext: GloomPluginContext = {
12
21
  registerPane: (pane: PaneDef) => panes.set(pane.id, pane),
13
22
  registerPaneTemplate: (template: PaneTemplateDef) => paneTemplates.set(template.id, template),
14
23
  registerCommand: () => {},
@@ -19,6 +28,8 @@ export async function createPaneCatalog(context: MarketContext, plugins: GloomPl
19
28
  registerShortcut: () => {},
20
29
  registerTickerAction: () => {},
21
30
  registerContextMenuProvider: () => {},
31
+ registerSyncContributor: () => () => {},
32
+ registerSyncTransport: () => () => {},
22
33
  watchNewsQuery: () => () => {},
23
34
  getData: () => null,
24
35
  getTicker: () => null,
@@ -79,7 +90,7 @@ export async function createPaneCatalog(context: MarketContext, plugins: GloomPl
79
90
  for (const template of plugin.paneTemplates ?? []) paneTemplates.set(template.id, template);
80
91
  if (plugin.setup) {
81
92
  setupPlugins.push(plugin);
82
- await plugin.setup(fakeContext as never);
93
+ await plugin.setup(discoveryContext);
83
94
  }
84
95
  }
85
96
 
@@ -94,15 +105,31 @@ export async function createPaneCatalog(context: MarketContext, plugins: GloomPl
94
105
  };
95
106
  }
96
107
 
97
- function createFakePluginPersistence() {
98
- const resources = new Map<string, unknown>();
108
+ function createDiscoveryPluginPersistence(): PluginPersistence {
109
+ const resources = new Map<string, PersistedResourceValue<unknown>>();
99
110
  return {
100
111
  getState: () => null,
101
112
  setState: () => {},
102
113
  deleteState: () => {},
103
- getResource: (kind: string, key: string) => resources.get(`${kind}:${key}`) ?? null,
104
- setResource: (kind: string, key: string, value: unknown) => {
105
- const entry = { value, updatedAt: Date.now(), expiresAt: null, provenance: null };
114
+ getResource: <T = unknown>(kind: string, key: string) => (
115
+ resources.get(`${kind}:${key}`) as PersistedResourceValue<T> | undefined
116
+ ) ?? null,
117
+ setResource: <T = unknown>(
118
+ kind: string,
119
+ key: string,
120
+ value: T,
121
+ options: Parameters<PluginPersistence["setResource"]>[3],
122
+ ) => {
123
+ const fetchedAt = Date.now();
124
+ const entry: PersistedResourceValue<T> = {
125
+ value,
126
+ fetchedAt,
127
+ staleAt: fetchedAt + options.cachePolicy.staleMs,
128
+ expiresAt: fetchedAt + options.cachePolicy.expireMs,
129
+ sourceKey: options.sourceKey ?? "",
130
+ schemaVersion: options.schemaVersion ?? 1,
131
+ provenance: options.provenance ?? null,
132
+ };
106
133
  resources.set(`${kind}:${key}`, entry);
107
134
  return entry;
108
135
  },
@@ -9,8 +9,27 @@ const {
9
9
  optionPaneState,
10
10
  filterPaneCatalogEntries,
11
11
  renderPaneCatalogReport,
12
+ getPaneFunctionCapability,
13
+ normalizeCapabilityOptions,
14
+ capabilityPluginState,
12
15
  } = paneFunctionTestInternals;
13
16
 
17
+ const dummyPane = {
18
+ id: "test-pane",
19
+ name: "Test",
20
+ component: () => null,
21
+ defaultPosition: "right" as const,
22
+ };
23
+
24
+ function capabilityFor(templateId: string) {
25
+ return getPaneFunctionCapability({
26
+ id: templateId,
27
+ paneId: dummyPane.id,
28
+ label: "Test",
29
+ description: "Test",
30
+ }, dummyPane);
31
+ }
32
+
14
33
  describe("pane function CLI args", () => {
15
34
  test("parses target, argument, output, size, and pane options", () => {
16
35
  const parsed = parsePaneFunctionArgs([
@@ -31,6 +50,7 @@ describe("pane function CLI args", () => {
31
50
  expect(parsed.outputPath).toBe("/tmp/fa-nvda.png");
32
51
  expect(parsed.width).toBe(900);
33
52
  expect(parsed.height).toBe(700);
53
+ expect(parsed.requireBotSafe).toBe(false);
34
54
  expect(parsed.options).toEqual({
35
55
  period: "quarterly",
36
56
  statement: "balance",
@@ -75,6 +95,12 @@ describe("pane function CLI args", () => {
75
95
  expect(parsePaneCatalogArgs(["chart", "price", "--limit", "3"])).toEqual({
76
96
  query: "chart price",
77
97
  limit: 3,
98
+ botSafeOnly: false,
99
+ });
100
+ expect(parsePaneCatalogArgs(["cash", "flow", "--bot-safe"])).toEqual({
101
+ query: "cash flow",
102
+ limit: 25,
103
+ botSafeOnly: true,
78
104
  });
79
105
  });
80
106
 
@@ -92,6 +118,7 @@ describe("pane function CLI args", () => {
92
118
  argPlaceholder: "ticker",
93
119
  keywords: ["gp", "graph", "price", "chart"],
94
120
  defaultSettings: { lockedTabId: "chart", chartRangePreset: "5Y" },
121
+ capability: capabilityFor("graph-price-pane"),
95
122
  },
96
123
  {
97
124
  token: "FA",
@@ -105,10 +132,86 @@ describe("pane function CLI args", () => {
105
132
  argPlaceholder: "ticker",
106
133
  keywords: ["fa", "financial", "analysis", "statements"],
107
134
  defaultSettings: { lockedTabId: "financials" },
135
+ capability: capabilityFor("financial-analysis-pane"),
108
136
  },
109
137
  ], "price chart");
110
138
 
111
139
  expect(matches.map((entry) => entry.token)).toEqual(["GP"]);
112
- expect(renderPaneCatalogReport(matches, { query: "price chart", limit: 10 })).toContain("gloomberb shot GP <ticker>");
140
+ expect(renderPaneCatalogReport(matches, { query: "price chart", limit: 10, botSafeOnly: false })).toContain("gloomberb shot GP <ticker>");
141
+ });
142
+
143
+ test("finds the semantic financial comparison capability from natural wording", () => {
144
+ const matches = filterPaneCatalogEntries([
145
+ {
146
+ token: "GF",
147
+ label: "Fundamental Graph",
148
+ description: "Graph statement metrics for one or more tickers.",
149
+ paneId: "fundamental-graph",
150
+ paneName: "Fundamental Graph",
151
+ templateId: "fundamental-graph-pane",
152
+ shortcut: "GF",
153
+ argKind: "ticker-list",
154
+ argPlaceholder: "tickers",
155
+ keywords: ["fundamental", "graph", "financials", "statements"],
156
+ defaultSettings: {},
157
+ capability: capabilityFor("fundamental-graph-pane"),
158
+ },
159
+ {
160
+ token: "CMP",
161
+ label: "Comparison Chart",
162
+ description: "Compare stock prices.",
163
+ paneId: "comparison-chart",
164
+ paneName: "Compare",
165
+ templateId: "comparison-chart-pane",
166
+ shortcut: "CMP",
167
+ argKind: "ticker-list",
168
+ argPlaceholder: "tickers",
169
+ keywords: ["compare", "price"],
170
+ defaultSettings: {},
171
+ capability: capabilityFor("comparison-chart-pane"),
172
+ },
173
+ ], "cash flow comparison");
174
+
175
+ expect(matches.map(({ token }) => token)).toEqual(["GF", "CMP"]);
176
+ });
177
+
178
+ test("normalizes GF options without creating retired plugin state", () => {
179
+ const capability = capabilityFor("fundamental-graph-pane");
180
+ const options = normalizeCapabilityOptions(capability, {
181
+ metric: "operating cash flow",
182
+ period: "yearly",
183
+ });
184
+
185
+ expect(options).toEqual({
186
+ metric: "operatingCashFlow",
187
+ period: "annual",
188
+ });
189
+ expect(capabilityPluginState(capability, options)).toEqual({});
190
+ });
191
+
192
+ test("exposes custom G as a bot-safe mixed-series capability", () => {
193
+ const capability = capabilityFor("chart-composer-pane");
194
+
195
+ expect(capability).toMatchObject({
196
+ id: "chart-composer",
197
+ botSafe: true,
198
+ tickerCardinality: "none",
199
+ reportReadiness: "ready",
200
+ screenshotReadiness: "ready",
201
+ });
202
+ expect(normalizeCapabilityOptions(capability, {
203
+ range: "five years",
204
+ resolution: "1d",
205
+ })).toEqual({
206
+ rangePreset: "5Y",
207
+ chartResolution: "1d",
208
+ });
209
+ });
210
+
211
+ test("rejects financial statement options on a price comparison", () => {
212
+ const capability = capabilityFor("comparison-chart-pane");
213
+ expect(() => normalizeCapabilityOptions(capability, {
214
+ tab: "cashflow",
215
+ })).toThrow("price-comparison does not support --tab");
113
216
  });
114
217
  });
@@ -21,6 +21,11 @@ import { defaultScreenshotPath, renderDesktopShot } from "./screenshot";
21
21
  import {
22
22
  buildPaneCatalogEntries,
23
23
  } from "./catalog";
24
+ import {
25
+ capabilityPluginState,
26
+ getPaneFunctionCapability,
27
+ normalizeCapabilityOptions,
28
+ } from "./capabilities";
24
29
 
25
30
  async function withPaneRuntime<T>(
26
31
  ctx: CliCommandContext,
@@ -47,8 +52,26 @@ async function withPaneRuntime<T>(
47
52
  export async function runPaneFunction(args: string[], ctx: CliCommandContext) {
48
53
  await runPaneCliCommand(ctx, async () => {
49
54
  await withPaneRuntime(ctx, args, async ({ parsed, context, resolved }) => {
50
- const report = await buildFunctionReport(resolved, context, parsed.arg, parsed.options);
51
- console.log(report);
55
+ if (parsed.requireBotSafe && (
56
+ !resolved.capability.botSafe || resolved.capability.reportReadiness !== "ready"
57
+ )) {
58
+ throw new Error(
59
+ `${resolved.token} is not a verified bot-safe report capability. `
60
+ + `Use "gloomberb catalog ${resolved.token}" to inspect readiness.`,
61
+ );
62
+ }
63
+ const report = await buildFunctionReport(resolved, context, parsed.arg);
64
+ if (parsed.requireBotSafe && (report.data.empty || !report.data.complete)) {
65
+ const unavailable = report.data.unavailableSymbols.length > 0
66
+ ? ` Missing data for ${report.data.unavailableSymbols.join(", ")}.`
67
+ : "";
68
+ throw new Error(
69
+ `${resolved.token} did not produce a complete bot-safe report.${unavailable}`,
70
+ );
71
+ }
72
+ ctx.printResult({ data: report.data }, {
73
+ text: () => report.text,
74
+ });
52
75
  });
53
76
  });
54
77
  }
@@ -56,10 +79,18 @@ export async function runPaneFunction(args: string[], ctx: CliCommandContext) {
56
79
  export async function runPaneScreenshot(args: string[], ctx: CliCommandContext) {
57
80
  await runPaneCliCommand(ctx, async () => {
58
81
  await withPaneRuntime(ctx, args, async ({ parsed, context, resolved }) => {
82
+ if (parsed.requireBotSafe && (
83
+ !resolved.capability.botSafe || resolved.capability.screenshotReadiness !== "ready"
84
+ )) {
85
+ throw new Error(
86
+ `${resolved.token} is not a verified bot-safe screenshot capability. `
87
+ + `Use "gloomberb catalog ${resolved.token}" to inspect readiness.`,
88
+ );
89
+ }
59
90
  const outputPath = parsed.outputPath
60
91
  ? resolve(process.cwd(), ensurePngExtension(parsed.outputPath))
61
92
  : defaultScreenshotPath(resolved, parsed.arg);
62
- await renderDesktopShot({
93
+ const result = await renderDesktopShot({
63
94
  resolved,
64
95
  context,
65
96
  rawArg: parsed.arg,
@@ -68,7 +99,29 @@ export async function runPaneScreenshot(args: string[], ctx: CliCommandContext)
68
99
  height: parsed.height,
69
100
  options: parsed.options,
70
101
  });
71
- console.log(`Saved screenshot to ${outputPath}`);
102
+ if (parsed.requireBotSafe && !result.usable) {
103
+ throw new Error(
104
+ `${resolved.token} did not produce a usable bot-safe screenshot: `
105
+ + `${result.empty ? "empty render" : ""}${result.empty && !result.complete ? ", " : ""}`
106
+ + `${!result.complete ? `missing data for ${result.unavailableSymbols.join(", ")}` : ""}`
107
+ + `${result.semanticMismatch ? `${(result.empty || !result.complete) ? ", " : ""}rendered content did not match the requested capability` : ""}`,
108
+ );
109
+ }
110
+ ctx.printResult({ data: result }, {
111
+ text: (data) => [
112
+ `Saved screenshot to ${data.outputPath}`,
113
+ `Result: ${data.rowCount} semantic rows; empty=${data.empty}; complete=${data.complete}; mismatch=${data.semanticMismatch}; usable=${data.usable}`,
114
+ ...(data.unavailableSymbols.length > 0
115
+ ? [`Unavailable symbols: ${data.unavailableSymbols.join(", ")}`]
116
+ : []),
117
+ ...(data.render.emptyStateMarkers.length > 0
118
+ ? [`Empty markers: ${data.render.emptyStateMarkers.join(", ")}`]
119
+ : []),
120
+ ...(data.render.missingExpectedText.length > 0
121
+ ? [`Missing expected text: ${data.render.missingExpectedText.join(", ")}`]
122
+ : []),
123
+ ].join("\n"),
124
+ });
72
125
  });
73
126
  });
74
127
  }
@@ -84,7 +137,10 @@ export async function runPaneCatalog(args: string[], ctx: CliCommandContext) {
84
137
  const registry = await createPaneCatalog(context, ctx.plugins);
85
138
  try {
86
139
  const entries = await buildPaneCatalogEntries(registry, context);
87
- const filtered = filterPaneCatalogEntries(entries, parsed.query);
140
+ const botSafeEntries = effectiveParsed.botSafeOnly
141
+ ? entries.filter((entry) => entry.capability.botSafe)
142
+ : entries;
143
+ const filtered = filterPaneCatalogEntries(botSafeEntries, parsed.query);
88
144
  if (ctx.cliOptions.format === "text") {
89
145
  console.log(renderPaneCatalogReport(filtered, effectiveParsed));
90
146
  } else {
@@ -118,4 +174,7 @@ export const paneFunctionTestInternals = {
118
174
  optionPaneState,
119
175
  filterPaneCatalogEntries,
120
176
  renderPaneCatalogReport,
177
+ getPaneFunctionCapability,
178
+ normalizeCapabilityOptions,
179
+ capabilityPluginState,
121
180
  };
@@ -6,6 +6,7 @@ import {
6
6
  resolveFinancialPeriodOption,
7
7
  } from "../../plugins/builtin/ticker-detail/financials/model";
8
8
  import type { PaneRuntimeState } from "../../core/state/app/state";
9
+ import type { NormalizedPaneFunctionOptions } from "./capabilities";
9
10
 
10
11
  const DEFAULT_SHOT_WIDTH = 1280;
11
12
  const DEFAULT_SHOT_HEIGHT = 720;
@@ -22,11 +23,13 @@ export interface ParsedPaneFunctionArgs {
22
23
  outputPath: string | null;
23
24
  width: number;
24
25
  height: number;
26
+ requireBotSafe: boolean;
25
27
  }
26
28
 
27
29
  export interface ParsedPaneCatalogArgs {
28
30
  query: string;
29
31
  limit: number;
32
+ botSafeOnly: boolean;
30
33
  }
31
34
 
32
35
  function normalizeOptionKey(value: string): string {
@@ -59,6 +62,7 @@ export function parsePaneFunctionArgs(args: string[]): ParsedPaneFunctionArgs {
59
62
  let outputPath: string | null = null;
60
63
  let width = DEFAULT_SHOT_WIDTH;
61
64
  let height = DEFAULT_SHOT_HEIGHT;
65
+ let requireBotSafe = false;
62
66
 
63
67
  for (let index = 0; index < args.length; index += 1) {
64
68
  const token = args[index]!;
@@ -80,7 +84,9 @@ export function parsePaneFunctionArgs(args: string[]): ParsedPaneFunctionArgs {
80
84
  const nextValue = next && !next.startsWith("--") ? args[++index] : true;
81
85
  const value: string | true = inlineValue ?? nextValue ?? true;
82
86
  const normalizedKey = normalizeOptionKey(key);
83
- if (normalizedKey === "output" || normalizedKey === "out" || normalizedKey === "o") {
87
+ if (normalizedKey === "requireBotSafe" || normalizedKey === "botSafe") {
88
+ requireBotSafe = value === true || String(value).toLowerCase() === "true";
89
+ } else if (normalizedKey === "output" || normalizedKey === "out" || normalizedKey === "o") {
84
90
  outputPath = value === true ? null : value;
85
91
  } else if (normalizedKey === "width" && value !== true) {
86
92
  const parsedWidth = Number(value);
@@ -101,12 +107,13 @@ export function parsePaneFunctionArgs(args: string[]): ParsedPaneFunctionArgs {
101
107
 
102
108
  const target = positionals[0]?.trim() ?? "";
103
109
  const arg = positionals.slice(1).join(" ").trim();
104
- return { target, arg, options, outputPath, width, height };
110
+ return { target, arg, options, outputPath, width, height, requireBotSafe };
105
111
  }
106
112
 
107
113
  export function parsePaneCatalogArgs(args: string[]): ParsedPaneCatalogArgs {
108
114
  const queryParts: string[] = [];
109
115
  let limit = DEFAULT_CATALOG_LIMIT;
116
+ let botSafeOnly = false;
110
117
 
111
118
  for (let index = 0; index < args.length; index += 1) {
112
119
  const token = args[index]!;
@@ -120,6 +127,11 @@ export function parsePaneCatalogArgs(args: string[]): ParsedPaneCatalogArgs {
120
127
  continue;
121
128
  }
122
129
 
130
+ if (token === "--bot-safe" || token === "--botsafe") {
131
+ botSafeOnly = true;
132
+ continue;
133
+ }
134
+
123
135
  if (token === "--limit") {
124
136
  const parsed = Number(args[++index]);
125
137
  if (Number.isFinite(parsed) && parsed > 0) {
@@ -142,12 +154,15 @@ export function parsePaneCatalogArgs(args: string[]): ParsedPaneCatalogArgs {
142
154
  return {
143
155
  query: queryParts.join(" ").trim(),
144
156
  limit,
157
+ botSafeOnly,
145
158
  };
146
159
  }
147
160
 
148
- export function optionString(options: Record<string, string | true>, key: string): string | undefined {
161
+ type PaneOptionValues = Record<string, string | number | boolean>;
162
+
163
+ export function optionString(options: PaneOptionValues, key: string): string | undefined {
149
164
  const value = options[normalizeOptionKey(key)];
150
- return value === true ? undefined : value;
165
+ return value === true || value === undefined ? undefined : String(value);
151
166
  }
152
167
 
153
168
  const RESERVED_OPTION_KEYS = new Set(["output", "out", "o", "width", "height", "arguments", "state"]);
@@ -211,7 +226,7 @@ function normalizeFinancialSubTabOption(value: string | undefined): string | und
211
226
  ))?.key;
212
227
  }
213
228
 
214
- export function optionPaneState(options: Record<string, string | true>): PaneRuntimeState {
229
+ export function optionPaneState(options: PaneOptionValues | NormalizedPaneFunctionOptions): PaneRuntimeState {
215
230
  const state: PaneRuntimeState = {};
216
231
  const rawState = optionString(options, "state");
217
232
  if (rawState) {
@@ -222,7 +237,7 @@ export function optionPaneState(options: Record<string, string | true>): PaneRun
222
237
 
223
238
  for (const [key, value] of Object.entries(options)) {
224
239
  if (!key.startsWith("state.") || value === true) continue;
225
- state[key.slice("state.".length)] = coerceSettingValue(value);
240
+ state[key.slice("state.".length)] = typeof value === "string" ? coerceSettingValue(value) : value;
226
241
  }
227
242
 
228
243
  const activeTab = optionString(options, "activeTabId")