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,21 +1,86 @@
1
- import type { TickerFinancials } from "../../types/financials";
2
- import { formatCurrency, formatNumber, formatPercentRaw } from "../../utils/format";
3
- import { formatTimestamp } from "../helpers";
4
- import { buildTickerReport } from "../commands/ticker";
5
- import { createBaseConverter } from "../base-converter";
6
- import type { MarketContext } from "../types";
1
+ import type { TimeRange } from "../../components/chart/core/types";
2
+ import {
3
+ buildCorrelationMatrix,
4
+ pairKey,
5
+ type CorrelationSeries,
6
+ } from "../../plugins/builtin/correlation/matrix/model";
7
+ import { computeDatedReturns } from "../../plugins/builtin/correlation/compute";
8
+ import {
9
+ buildRelationshipAnalysis,
10
+ DEFAULT_RELATIONSHIP_SECOND_SYMBOL,
11
+ } from "../../plugins/builtin/correlation/relationship/model";
12
+ import {
13
+ graphRowsForFinancials,
14
+ limitGraphRowsBySymbol,
15
+ metricDef,
16
+ } from "../../time-series/reporting";
17
+ import type {
18
+ FundamentalPeriod,
19
+ GraphKind,
20
+ GraphMetricKey,
21
+ } from "../../time-series/reporting";
7
22
  import {
8
23
  buildFinancialTableModel,
9
24
  formatFinancialHeader,
10
- resolveFinancialPeriodOption,
11
25
  } from "../../plugins/builtin/ticker-detail/financials/model";
12
- import { optionString } from "./options";
13
- import type { ResolvedPaneFunction } from "./resolver";
26
+ import type { PricePoint, TickerFinancials } from "../../types/financials";
27
+ import { formatCurrency, formatNumber, formatPercent, formatPercentRaw } from "../../utils/format";
28
+ import { publicTickerKey } from "../../utils/exchanges";
29
+ import { apiClient } from "../../api-client";
30
+ import { parseChartSpec } from "../../plugins/builtin/chart-composer/chart-spec";
31
+ import { resolveChartSpecData } from "../../time-series/resolve";
32
+ import { formatTimestamp } from "../helpers";
33
+ import { buildTickerReport } from "../commands/ticker";
34
+ import { createBaseConverter } from "../base-converter";
35
+ import type { MarketContext } from "../types";
36
+ import type { NormalizedPaneFunctionOptions } from "./capabilities";
14
37
  import {
15
38
  fetchTickerFinancials,
16
39
  isFinancialAnalysisFunction,
40
+ clipPriceHistoryToRange,
17
41
  requireSymbol,
42
+ withShotPriceHistory,
18
43
  } from "./data";
44
+ import type { ResolvedPaneFunction } from "./resolver";
45
+
46
+ export interface PaneFunctionReportData {
47
+ kind: string;
48
+ target: string;
49
+ capabilityId: string;
50
+ symbols: string[];
51
+ options: NormalizedPaneFunctionOptions;
52
+ rowCount: number;
53
+ empty: boolean;
54
+ complete: boolean;
55
+ unavailableSymbols: string[];
56
+ [key: string]: unknown;
57
+ }
58
+
59
+ export interface PaneFunctionReport {
60
+ data: PaneFunctionReportData;
61
+ text: string;
62
+ }
63
+
64
+ function reportBase(
65
+ resolved: ResolvedPaneFunction,
66
+ symbols: string[],
67
+ rowCount: number,
68
+ unavailableSymbols: string[] = rowCount === 0 ? symbols : [],
69
+ ): Pick<
70
+ PaneFunctionReportData,
71
+ "target" | "capabilityId" | "symbols" | "options" | "rowCount" | "empty" | "complete" | "unavailableSymbols"
72
+ > {
73
+ return {
74
+ target: resolved.token,
75
+ capabilityId: resolved.capability.id,
76
+ symbols,
77
+ options: resolved.options,
78
+ rowCount,
79
+ empty: rowCount === 0,
80
+ complete: unavailableSymbols.length === 0,
81
+ unavailableSymbols,
82
+ };
83
+ }
19
84
 
20
85
  function formatQuoteLine(financials: TickerFinancials): string {
21
86
  const quote = financials.quote;
@@ -35,14 +100,13 @@ async function buildFinancialStatementReport(
35
100
  resolved: ResolvedPaneFunction,
36
101
  context: MarketContext,
37
102
  rawArg: string,
38
- options: Record<string, string | true>,
39
- ): Promise<string> {
103
+ ): Promise<PaneFunctionReport> {
40
104
  const symbol = requireSymbol(resolved, rawArg);
41
105
  const { tickerFile, financials } = await fetchTickerFinancials(context, symbol);
42
106
  if (!financials.quote) throw new Error(`No quote data available for ${symbol}.`);
43
107
  const table = buildFinancialTableModel(financials, {
44
- period: resolveFinancialPeriodOption(optionString(options, "period")),
45
- statement: optionString(options, "statement") ?? optionString(options, "tab"),
108
+ period: resolved.options.period as FundamentalPeriod | undefined,
109
+ statement: resolved.options.statement as string | undefined,
46
110
  });
47
111
  const name = financials.quote.name ?? tickerFile?.metadata.name ?? symbol;
48
112
  const lines = [
@@ -57,19 +121,63 @@ async function buildFinancialStatementReport(
57
121
  lines.push(formatQuoteLine(financials));
58
122
  lines.push("");
59
123
  lines.push(`No financial statement rows are available for ${symbol} from the configured data providers.`);
60
- return lines.join("\n");
124
+ return {
125
+ data: {
126
+ kind: "financial-statement",
127
+ ...reportBase(resolved, [symbol], 0),
128
+ symbol,
129
+ name,
130
+ currency: financials.quote.currency,
131
+ statement: resolved.options.statement,
132
+ period: resolved.options.period,
133
+ columns: [],
134
+ rows: [],
135
+ },
136
+ text: lines.join("\n"),
137
+ };
61
138
  }
62
- const columns = [
63
- { header: "Metric" },
64
- ...table.statements.map((statement) => ({ header: formatFinancialHeader(statement.date).trim(), align: "right" as const })),
65
- ];
66
- const rows = table.rows.map((row) => [
67
- row.unitLabel,
68
- ...row.cells.map((cell) => `${cell.valueText.trim()}${cell.growthText.trim() ? ` ${cell.growthText.trim()}` : ""}`),
69
- ]);
70
- lines.push(contextOutputTable(columns, rows));
71
139
 
72
- return lines.join("\n");
140
+ const columns = table.statements.map((statement) => ({
141
+ date: statement.date,
142
+ label: formatFinancialHeader(statement.date).trim(),
143
+ }));
144
+ const structuredRows = table.rows.map((row) => ({
145
+ id: row.id,
146
+ kind: row.kind,
147
+ metric: row.unitLabel,
148
+ cells: row.cells.map((cell, index) => ({
149
+ date: table.statements[index]?.date ?? "",
150
+ value: cell.value ?? null,
151
+ growth: cell.growth ?? null,
152
+ formatted: cell.valueText.trim(),
153
+ })),
154
+ }));
155
+ lines.push(contextOutputTable(
156
+ [
157
+ { header: "Metric" },
158
+ ...columns.map(({ label }) => ({ header: label, align: "right" as const })),
159
+ ],
160
+ table.rows.map((row) => [
161
+ row.unitLabel,
162
+ ...row.cells.map((cell) => `${cell.valueText.trim()}${cell.growthText.trim() ? ` ${cell.growthText.trim()}` : ""}`),
163
+ ]),
164
+ ));
165
+
166
+ return {
167
+ data: {
168
+ kind: "financial-statement",
169
+ ...reportBase(resolved, [symbol], structuredRows.length),
170
+ symbol,
171
+ name,
172
+ currency: financials.quote.currency,
173
+ statement: table.subTab.key,
174
+ statementLabel: table.subTab.name,
175
+ period: table.period,
176
+ columns,
177
+ rows: structuredRows,
178
+ },
179
+ text: lines.join("\n"),
180
+ };
73
181
  }
74
182
 
75
183
  function contextOutputTable(
@@ -96,85 +204,640 @@ function contextOutputTable(
96
204
  ].join("\n");
97
205
  }
98
206
 
99
- async function buildTickerListReport(
207
+ async function buildChartComposerReport(
100
208
  resolved: ResolvedPaneFunction,
101
209
  context: MarketContext,
102
- ): Promise<string | null> {
103
- const symbols = resolved.createOptions?.symbols;
104
- if (!symbols?.length) return null;
105
- const quotes = await Promise.all(symbols.map(async (symbol) => {
106
- const quote = await context.dataProvider.getQuote(symbol);
107
- return quote;
210
+ ): Promise<PaneFunctionReport> {
211
+ const parsed = parseChartSpec(resolved.instance.settings?.chartSpec);
212
+ if (!parsed) throw new Error("The chart composer specification is invalid.");
213
+ const spec = {
214
+ ...parsed,
215
+ series: await Promise.all(parsed.series.map(async (series) => {
216
+ if (series.source.kind !== "security" || series.source.instrument.exchange) return series;
217
+ const ticker = await context.store.loadTicker(series.source.instrument.symbol);
218
+ return ticker?.metadata.exchange
219
+ ? {
220
+ ...series,
221
+ source: {
222
+ ...series.source,
223
+ instrument: { ...series.source.instrument, exchange: ticker.metadata.exchange },
224
+ },
225
+ }
226
+ : series;
227
+ })),
228
+ };
229
+ const result = await resolveChartSpecData(spec, {
230
+ dataProvider: context.dataProvider,
231
+ loadFredSeries: async (request) => ({
232
+ data: await apiClient.getCloudFredSeries(request.seriesId, {
233
+ startDate: request.startDate,
234
+ sortOrder: request.sortOrder,
235
+ }),
236
+ fetchedAt: Date.now(),
237
+ stale: false,
238
+ source: "network",
239
+ }),
240
+ });
241
+ const baseIds = new Set(spec.series.map((series) => series.id));
242
+ const series = result.series.map((entry) => ({
243
+ id: entry.id,
244
+ label: entry.label,
245
+ derived: !baseIds.has(entry.id),
246
+ unit: entry.unit,
247
+ panelId: entry.panelId,
248
+ axis: entry.axis,
249
+ style: entry.style,
250
+ transform: entry.transform,
251
+ observations: entry.points.map((point) => ({
252
+ date: point.date.toISOString(),
253
+ value: point.value ?? point.close ?? null,
254
+ })),
108
255
  }));
109
- const rows = quotes.map((quote) => [
110
- quote.symbol,
111
- quote.name ?? "",
112
- formatCurrency(quote.price, quote.currency),
113
- `${quote.change >= 0 ? "+" : ""}${formatCurrency(quote.change, quote.currency)}`,
114
- formatPercentRaw(quote.changePercent),
115
- ]);
116
- return [
117
- `${resolved.label} | ${symbols.join(", ")}`,
256
+ const symbols = [...new Set(spec.series.flatMap((entry) => entry.source.kind === "security"
257
+ ? [publicTickerKey(entry.source.instrument.symbol, entry.source.instrument.exchange)]
258
+ : []))];
259
+ const unavailable = spec.series.flatMap((entry) => {
260
+ const output = result.series.find((candidate) => candidate.id === entry.id);
261
+ if (output?.points.length) return [];
262
+ return [entry.source.kind === "security"
263
+ ? publicTickerKey(entry.source.instrument.symbol, entry.source.instrument.exchange)
264
+ : `FRED:${entry.source.seriesId}`];
265
+ });
266
+ const rowCount = series.reduce((count, entry) => count + entry.observations.length, 0);
267
+ const tableRows = series.map((entry) => {
268
+ const latest = entry.observations.at(-1);
269
+ return [
270
+ entry.label,
271
+ latest?.date.slice(0, 10) ?? "-",
272
+ latest?.value == null ? "-" : formatNumber(latest.value, 4),
273
+ entry.unit,
274
+ String(entry.observations.length),
275
+ ];
276
+ });
277
+ const text = [
278
+ `${resolved.label} | ${spec.viewport.range} | ${spec.viewport.resolution}`,
118
279
  "",
119
280
  contextOutputTable([
120
- { header: "Ticker" },
121
- { header: "Name" },
122
- { header: "Last", align: "right" },
123
- { header: "Change", align: "right" },
124
- { header: "Change %", align: "right" },
125
- ], rows),
281
+ { header: "Series" },
282
+ { header: "Latest" },
283
+ { header: "Value", align: "right" },
284
+ { header: "Unit" },
285
+ { header: "Points", align: "right" },
286
+ ], tableRows),
287
+ ...(result.warnings.length > 0 ? ["", `Warnings: ${result.warnings.join(" ")}`] : []),
288
+ ...(result.errors.length > 0 ? ["", `Errors: ${result.errors.join(" ")}`] : []),
126
289
  ].join("\n");
290
+ return {
291
+ data: {
292
+ kind: "chart-composer",
293
+ ...reportBase(resolved, symbols, rowCount, unavailable),
294
+ viewport: spec.viewport,
295
+ panels: spec.panels,
296
+ warnings: result.warnings,
297
+ errors: result.errors,
298
+ series,
299
+ },
300
+ text,
301
+ };
302
+ }
303
+
304
+ function resolvedSymbols(resolved: ResolvedPaneFunction): string[] {
305
+ return resolved.createOptions?.symbols?.length
306
+ ? resolved.createOptions.symbols
307
+ : resolved.createOptions?.symbol
308
+ ? [resolved.createOptions.symbol]
309
+ : [];
310
+ }
311
+
312
+ async function loadFinancials(
313
+ context: MarketContext,
314
+ symbols: string[],
315
+ includePriceHistory = false,
316
+ ): Promise<Array<{ symbol: string; financials: TickerFinancials }>> {
317
+ return Promise.all(symbols.map(async (symbol) => {
318
+ const entry = await fetchTickerFinancials(context, symbol);
319
+ return {
320
+ symbol,
321
+ financials: includePriceHistory
322
+ ? await withShotPriceHistory(context, symbol, entry.tickerFile, entry.financials)
323
+ : entry.financials,
324
+ };
325
+ }));
326
+ }
327
+
328
+ async function buildGraphSeriesReport(
329
+ resolved: ResolvedPaneFunction,
330
+ context: MarketContext,
331
+ kind: GraphKind,
332
+ ): Promise<PaneFunctionReport> {
333
+ const symbols = resolvedSymbols(resolved);
334
+ const metric = resolved.options.metric as GraphMetricKey;
335
+ const period = resolved.options.period as FundamentalPeriod;
336
+ const definition = metricDef(kind, metric);
337
+ const periodCount = resolved.options.periods == null ? null : Number(resolved.options.periods);
338
+ const entries = await loadFinancials(context, symbols, kind === "valuation");
339
+ const rows = entries.flatMap(({ symbol, financials }) => (
340
+ limitGraphRowsBySymbol(
341
+ graphRowsForFinancials(financials, kind, metric, period, symbol),
342
+ periodCount,
343
+ ).map((row) => ({
344
+ symbol: row.symbol,
345
+ date: row.date,
346
+ category: row.category,
347
+ value: row.value,
348
+ formattedValue: definition.format(row.value),
349
+ growth: row.growth,
350
+ currency: financials.quote?.currency ?? null,
351
+ }))
352
+ ));
353
+ const availability = entries.map(({ symbol, financials }) => ({
354
+ symbol,
355
+ currency: financials.quote?.currency ?? null,
356
+ annualStatements: financials.annualStatements.length,
357
+ quarterlyStatements: financials.quarterlyStatements.length,
358
+ rowCount: rows.filter((row) => row.symbol === symbol).length,
359
+ }));
360
+ const unavailableSymbols = availability.filter(({ rowCount }) => rowCount === 0).map(({ symbol }) => symbol);
361
+ const textRows = rows.map((row) => [
362
+ row.symbol,
363
+ row.category,
364
+ row.formattedValue,
365
+ row.growth == null ? "-" : formatPercent(row.growth),
366
+ ]);
367
+ const text = [
368
+ `${resolved.label} | ${definition.label} | ${period === "annual" ? "Annual" : "Quarterly"} | ${symbols.join(", ")}`,
369
+ "",
370
+ rows.length > 0
371
+ ? contextOutputTable([
372
+ { header: "Ticker" },
373
+ { header: "Period" },
374
+ { header: "Value", align: "right" },
375
+ { header: "Growth", align: "right" },
376
+ ], textRows)
377
+ : `No ${definition.label.toLowerCase()} rows are available for ${symbols.join(", ")}.`,
378
+ ].join("\n");
379
+ return {
380
+ data: {
381
+ kind: kind === "valuation" ? "valuation-series" : "fundamental-series",
382
+ ...reportBase(resolved, symbols, rows.length, unavailableSymbols),
383
+ metric,
384
+ metricLabel: definition.label,
385
+ period,
386
+ rows,
387
+ availability,
388
+ },
389
+ text,
390
+ };
391
+ }
392
+
393
+ async function buildQuoteComparisonReport(
394
+ resolved: ResolvedPaneFunction,
395
+ context: MarketContext,
396
+ ): Promise<PaneFunctionReport> {
397
+ const symbols = resolvedSymbols(resolved);
398
+ const quotes = await Promise.all(symbols.map((symbol) => context.dataProvider.getQuote(symbol)));
399
+ const rows = quotes.map((quote) => ({
400
+ symbol: quote.symbol,
401
+ name: quote.name ?? "",
402
+ price: quote.price,
403
+ currency: quote.currency,
404
+ change: quote.change,
405
+ changePercent: quote.changePercent,
406
+ marketCap: quote.marketCap ?? null,
407
+ updatedAt: quote.lastUpdated,
408
+ }));
409
+ return {
410
+ data: {
411
+ kind: "quote-comparison",
412
+ ...reportBase(resolved, symbols, rows.length),
413
+ rows,
414
+ },
415
+ text: [
416
+ `${resolved.label} | ${symbols.join(", ")}`,
417
+ "",
418
+ contextOutputTable([
419
+ { header: "Ticker" },
420
+ { header: "Name" },
421
+ { header: "Last", align: "right" },
422
+ { header: "Change", align: "right" },
423
+ { header: "Change %", align: "right" },
424
+ ], quotes.map((quote) => [
425
+ quote.symbol,
426
+ quote.name ?? "",
427
+ formatCurrency(quote.price, quote.currency),
428
+ `${quote.change >= 0 ? "+" : ""}${formatCurrency(quote.change, quote.currency)}`,
429
+ formatPercentRaw(quote.changePercent),
430
+ ])),
431
+ ].join("\n"),
432
+ };
433
+ }
434
+
435
+ function relativeValuationValues(financials: TickerFinancials) {
436
+ const { quote, fundamentals } = financials;
437
+ const evSales = fundamentals?.enterpriseValue != null && fundamentals.revenue
438
+ ? fundamentals.enterpriseValue / fundamentals.revenue
439
+ : null;
440
+ const fcfYield = fundamentals?.freeCashFlow != null && quote?.marketCap
441
+ ? fundamentals.freeCashFlow / quote.marketCap
442
+ : null;
443
+ return {
444
+ price: quote?.price ?? null,
445
+ currency: quote?.currency ?? null,
446
+ changePercent: quote?.changePercent ?? null,
447
+ marketCap: quote?.marketCap ?? null,
448
+ trailingPE: fundamentals?.trailingPE ?? null,
449
+ forwardPE: fundamentals?.forwardPE ?? null,
450
+ evSales,
451
+ fcfYield,
452
+ revenueGrowth: fundamentals?.revenueGrowth ?? fundamentals?.lastQuarterGrowth ?? null,
453
+ operatingMargin: fundamentals?.operatingMargin ?? null,
454
+ };
455
+ }
456
+
457
+ async function buildRelativeValuationReport(
458
+ resolved: ResolvedPaneFunction,
459
+ context: MarketContext,
460
+ ): Promise<PaneFunctionReport> {
461
+ const symbols = resolvedSymbols(resolved);
462
+ const entries = await loadFinancials(context, symbols);
463
+ const rows = entries.map(({ symbol, financials }) => ({ symbol, ...relativeValuationValues(financials) }));
464
+ const usableSymbols = new Set(rows
465
+ .filter((row) => [
466
+ row.marketCap,
467
+ row.trailingPE,
468
+ row.forwardPE,
469
+ row.evSales,
470
+ row.fcfYield,
471
+ row.revenueGrowth,
472
+ row.operatingMargin,
473
+ ].some((value) => value != null))
474
+ .map(({ symbol }) => symbol));
475
+ const unavailableSymbols = symbols.filter((symbol) => !usableSymbols.has(symbol));
476
+ const usableRowCount = usableSymbols.size;
477
+ return {
478
+ data: {
479
+ kind: "relative-valuation",
480
+ ...reportBase(resolved, symbols, usableRowCount, unavailableSymbols),
481
+ rows,
482
+ },
483
+ text: [
484
+ `${resolved.label} | ${symbols.join(", ")}`,
485
+ "",
486
+ contextOutputTable([
487
+ { header: "Ticker" },
488
+ { header: "Last", align: "right" },
489
+ { header: "P/E", align: "right" },
490
+ { header: "Fwd P/E", align: "right" },
491
+ { header: "EV/S", align: "right" },
492
+ { header: "FCF Yield", align: "right" },
493
+ { header: "Revenue Growth", align: "right" },
494
+ { header: "Op Margin", align: "right" },
495
+ ], rows.map((row) => [
496
+ row.symbol,
497
+ row.price == null || !row.currency ? "-" : formatCurrency(row.price, row.currency),
498
+ row.trailingPE == null ? "-" : formatNumber(row.trailingPE, 1),
499
+ row.forwardPE == null ? "-" : formatNumber(row.forwardPE, 1),
500
+ row.evSales == null ? "-" : formatNumber(row.evSales, 1),
501
+ row.fcfYield == null ? "-" : formatPercent(row.fcfYield),
502
+ row.revenueGrowth == null ? "-" : formatPercent(row.revenueGrowth),
503
+ row.operatingMargin == null ? "-" : formatPercent(row.operatingMargin),
504
+ ])),
505
+ ].join("\n"),
506
+ };
507
+ }
508
+
509
+ function pricePointDate(point: PricePoint): string {
510
+ const value = point.date instanceof Date ? point.date : new Date(point.date);
511
+ return Number.isFinite(value.getTime()) ? value.toISOString() : String(point.date);
512
+ }
513
+
514
+ async function loadPriceHistory(
515
+ context: MarketContext,
516
+ symbol: string,
517
+ range: TimeRange,
518
+ ): Promise<PricePoint[]> {
519
+ const ticker = await context.store.loadTicker(symbol);
520
+ const points = await context.dataProvider.getPriceHistory(symbol, ticker?.metadata.exchange ?? "", range);
521
+ return clipPriceHistoryToRange(points, range);
522
+ }
523
+
524
+ function resolvedPriceRange(resolved: ResolvedPaneFunction): TimeRange {
525
+ if (resolved.capability.id === "intraday-price-chart") return "1D";
526
+ return (resolved.options.rangePreset ?? resolved.options.range ?? "1Y") as TimeRange;
527
+ }
528
+
529
+ async function buildPriceHistoryReport(
530
+ resolved: ResolvedPaneFunction,
531
+ context: MarketContext,
532
+ rawArg: string,
533
+ ): Promise<PaneFunctionReport> {
534
+ const symbol = requireSymbol(resolved, rawArg);
535
+ const range = resolvedPriceRange(resolved);
536
+ const points = await loadPriceHistory(context, symbol, range);
537
+ const rows = [...points]
538
+ .sort((left, right) => pricePointDate(left).localeCompare(pricePointDate(right)))
539
+ .map((point) => ({
540
+ date: pricePointDate(point),
541
+ open: point.open ?? null,
542
+ high: point.high ?? null,
543
+ low: point.low ?? null,
544
+ close: point.close,
545
+ volume: point.volume ?? null,
546
+ }));
547
+ const shown = rows.slice(-30).reverse();
548
+ return {
549
+ data: {
550
+ kind: "price-history",
551
+ ...reportBase(resolved, [symbol], rows.length),
552
+ symbol,
553
+ range,
554
+ rows,
555
+ },
556
+ text: [
557
+ `${resolved.label} | ${symbol} | ${range} | ${rows.length} points`,
558
+ "",
559
+ rows.length > 0
560
+ ? contextOutputTable([
561
+ { header: "Date" },
562
+ { header: "Open", align: "right" },
563
+ { header: "High", align: "right" },
564
+ { header: "Low", align: "right" },
565
+ { header: "Close", align: "right" },
566
+ { header: "Volume", align: "right" },
567
+ ], shown.map((row) => [
568
+ row.date.slice(0, 10),
569
+ row.open == null ? "-" : formatNumber(row.open, 2),
570
+ row.high == null ? "-" : formatNumber(row.high, 2),
571
+ row.low == null ? "-" : formatNumber(row.low, 2),
572
+ formatNumber(row.close, 2),
573
+ row.volume == null ? "-" : formatNumber(row.volume, 0),
574
+ ]))
575
+ : `No price history is available for ${symbol}.`,
576
+ ].join("\n"),
577
+ };
578
+ }
579
+
580
+ async function buildPriceComparisonReport(
581
+ resolved: ResolvedPaneFunction,
582
+ context: MarketContext,
583
+ ): Promise<PaneFunctionReport> {
584
+ const symbols = resolvedSymbols(resolved);
585
+ const range = resolvedPriceRange(resolved);
586
+ const rows = await Promise.all(symbols.map(async (symbol) => {
587
+ const points = (await loadPriceHistory(context, symbol, range))
588
+ .filter((point) => Number.isFinite(point.close))
589
+ .sort((left, right) => pricePointDate(left).localeCompare(pricePointDate(right)));
590
+ const first = points[0];
591
+ const last = points.at(-1);
592
+ return {
593
+ symbol,
594
+ startDate: first ? pricePointDate(first) : null,
595
+ endDate: last ? pricePointDate(last) : null,
596
+ startPrice: first?.close ?? null,
597
+ endPrice: last?.close ?? null,
598
+ return: first && last && first.close !== 0 ? (last.close - first.close) / first.close : null,
599
+ pointCount: points.length,
600
+ };
601
+ }));
602
+ const usableRows = rows.filter((row) => row.return != null);
603
+ const unavailableSymbols = rows.filter((row) => row.return == null).map((row) => row.symbol);
604
+ return {
605
+ data: {
606
+ kind: "price-performance",
607
+ ...reportBase(resolved, symbols, usableRows.length, unavailableSymbols),
608
+ range,
609
+ axisMode: resolved.options.axisMode,
610
+ rows,
611
+ },
612
+ text: [
613
+ `${resolved.label} | ${range} price performance | ${symbols.join(", ")}`,
614
+ "",
615
+ contextOutputTable([
616
+ { header: "Ticker" },
617
+ { header: "Start" },
618
+ { header: "End" },
619
+ { header: "Start Price", align: "right" },
620
+ { header: "End Price", align: "right" },
621
+ { header: "Return", align: "right" },
622
+ { header: "Points", align: "right" },
623
+ ], rows.map((row) => [
624
+ row.symbol,
625
+ row.startDate?.slice(0, 10) ?? "-",
626
+ row.endDate?.slice(0, 10) ?? "-",
627
+ row.startPrice == null ? "-" : formatNumber(row.startPrice, 2),
628
+ row.endPrice == null ? "-" : formatNumber(row.endPrice, 2),
629
+ row.return == null ? "-" : formatPercent(row.return),
630
+ String(row.pointCount),
631
+ ])),
632
+ ].join("\n"),
633
+ };
634
+ }
635
+
636
+ async function buildCorrelationReport(
637
+ resolved: ResolvedPaneFunction,
638
+ context: MarketContext,
639
+ ): Promise<PaneFunctionReport> {
640
+ const symbols = resolvedSymbols(resolved);
641
+ const range = resolvedPriceRange(resolved);
642
+ const series = await Promise.all(symbols.map(async (symbol): Promise<CorrelationSeries> => {
643
+ const returns = computeDatedReturns(await loadPriceHistory(context, symbol, range));
644
+ return {
645
+ symbol,
646
+ returns,
647
+ status: returns.length >= 5 ? "ready" : "insufficient",
648
+ observationCount: returns.length,
649
+ };
650
+ }));
651
+ const bySymbol = new Map(series.map((entry) => [entry.symbol, entry]));
652
+ const matrix = buildCorrelationMatrix(symbols, bySymbol);
653
+ const rows = symbols.flatMap((left, leftIndex) => symbols.slice(leftIndex + 1).map((right) => {
654
+ const result = matrix.results.get(pairKey(left, right));
655
+ return {
656
+ left,
657
+ right,
658
+ correlation: result?.correlation ?? null,
659
+ sampleSize: result?.sampleSize ?? 0,
660
+ };
661
+ }));
662
+ const usableRows = rows.filter((row) => row.correlation != null);
663
+ const unavailableSymbols = series
664
+ .filter(({ status }) => status !== "ready")
665
+ .map(({ symbol }) => symbol);
666
+ return {
667
+ data: {
668
+ kind: "correlation-matrix",
669
+ ...reportBase(resolved, symbols, usableRows.length, unavailableSymbols),
670
+ range,
671
+ rows,
672
+ availability: series.map(({ symbol, status, observationCount }) => ({ symbol, status, observationCount })),
673
+ },
674
+ text: [
675
+ `${resolved.label} | Daily returns | ${range} | ${symbols.join(", ")}`,
676
+ "",
677
+ contextOutputTable([
678
+ { header: "Pair" },
679
+ { header: "Correlation", align: "right" },
680
+ { header: "Shared obs", align: "right" },
681
+ ], rows.map((row) => [
682
+ `${row.left}/${row.right}`,
683
+ row.correlation == null ? "-" : formatNumber(row.correlation, 3),
684
+ String(row.sampleSize),
685
+ ])),
686
+ ].join("\n"),
687
+ };
688
+ }
689
+
690
+ async function buildRelationshipReport(
691
+ resolved: ResolvedPaneFunction,
692
+ context: MarketContext,
693
+ ): Promise<PaneFunctionReport> {
694
+ const requested = resolvedSymbols(resolved);
695
+ const symbols = [requested[0]!, requested[1] ?? DEFAULT_RELATIONSHIP_SECOND_SYMBOL];
696
+ const range = resolvedPriceRange(resolved);
697
+ const correlationWindow = Number(resolved.options.correlationWindow ?? 120);
698
+ const histories = await Promise.all(symbols.map((symbol) => loadPriceHistory(context, symbol, range)));
699
+ const leftPoints = histories[0] ?? [];
700
+ const rightPoints = histories[1] ?? [];
701
+ const analysis = buildRelationshipAnalysis(leftPoints, rightPoints, correlationWindow);
702
+ const rowCount = analysis.stats?.sampleSize ?? analysis.returns.length;
703
+ const unavailableSymbols = [
704
+ ...(leftPoints.length < 2 ? [symbols[0]!] : []),
705
+ ...(rightPoints.length < 2 ? [symbols[1]!] : []),
706
+ ];
707
+ const row = {
708
+ left: symbols[0],
709
+ right: symbols[1],
710
+ range,
711
+ correlationWindow,
712
+ latestRatio: analysis.latestRatio,
713
+ latestCorrelation: analysis.latestCorrelation,
714
+ regression: analysis.stats,
715
+ alignedPriceCount: analysis.aligned.length,
716
+ returnCount: analysis.returns.length,
717
+ };
718
+ return {
719
+ data: {
720
+ kind: "relationship-analysis",
721
+ ...reportBase(resolved, symbols, rowCount, unavailableSymbols),
722
+ ...row,
723
+ },
724
+ text: [
725
+ `${resolved.label} | ${symbols[0]}/${symbols[1]} | ${range}`,
726
+ "",
727
+ contextOutputTable([
728
+ { header: "Metric" },
729
+ { header: "Value", align: "right" },
730
+ ], [
731
+ ["Latest ratio", analysis.latestRatio == null ? "-" : formatNumber(analysis.latestRatio, 4)],
732
+ [`Rolling correlation (${correlationWindow})`, analysis.latestCorrelation == null ? "-" : formatNumber(analysis.latestCorrelation, 3)],
733
+ ["Beta", analysis.stats ? formatNumber(analysis.stats.beta, 3) : "-"],
734
+ ["Alpha", analysis.stats ? formatNumber(analysis.stats.alpha, 3) : "-"],
735
+ ["R squared", analysis.stats ? formatNumber(analysis.stats.rSquared, 3) : "-"],
736
+ ["Shared returns", String(analysis.stats?.sampleSize ?? analysis.returns.length)],
737
+ ]),
738
+ ].join("\n"),
739
+ };
127
740
  }
128
741
 
129
742
  export async function buildFunctionReport(
130
743
  resolved: ResolvedPaneFunction,
131
744
  context: MarketContext,
132
745
  rawArg: string,
133
- options: Record<string, string | true>,
134
- ): Promise<string> {
746
+ ): Promise<PaneFunctionReport> {
135
747
  if (isFinancialAnalysisFunction(resolved)) {
136
- return buildFinancialStatementReport(resolved, context, rawArg, options);
748
+ return buildFinancialStatementReport(resolved, context, rawArg);
749
+ }
750
+ if (
751
+ resolved.template?.shortcut?.argKind === "ticker-list"
752
+ && resolvedSymbols(resolved).length === 0
753
+ ) {
754
+ return buildLegacyPaneReport(resolved, context, rawArg);
137
755
  }
138
756
 
139
- if (resolved.template?.shortcut?.argKind === "ticker-list") {
140
- const report = await buildTickerListReport(resolved, context);
141
- if (report) return report;
757
+ switch (resolved.capability.id) {
758
+ case "chart-composer":
759
+ return buildChartComposerReport(resolved, context);
760
+ case "fundamental-series":
761
+ return buildGraphSeriesReport(resolved, context, "fundamental");
762
+ case "valuation-series":
763
+ return buildGraphSeriesReport(resolved, context, "valuation");
764
+ case "quote-comparison":
765
+ return buildQuoteComparisonReport(resolved, context);
766
+ case "relative-valuation":
767
+ return buildRelativeValuationReport(resolved, context);
768
+ case "historical-prices":
769
+ case "price-chart":
770
+ case "intraday-price-chart":
771
+ return buildPriceHistoryReport(resolved, context, rawArg);
772
+ case "price-comparison":
773
+ return buildPriceComparisonReport(resolved, context);
774
+ case "return-correlation":
775
+ return buildCorrelationReport(resolved, context);
776
+ case "security-relationship":
777
+ return buildRelationshipReport(resolved, context);
142
778
  }
143
779
 
780
+ if (resolved.capability.reportReadiness === "unsupported") {
781
+ return buildLegacyPaneReport(resolved, context, rawArg);
782
+ }
783
+ throw new Error(`No structured report builder is registered for ${resolved.token}.`);
784
+ }
785
+
786
+ async function buildLegacyPaneReport(
787
+ resolved: ResolvedPaneFunction,
788
+ context: MarketContext,
789
+ rawArg: string,
790
+ ): Promise<PaneFunctionReport> {
791
+ const symbols = resolvedSymbols(resolved);
792
+ if (resolved.template?.shortcut?.argKind === "ticker-list" && symbols.length > 0) {
793
+ return buildQuoteComparisonReport(resolved, context);
794
+ }
144
795
  if (resolved.template?.shortcut?.argKind === "ticker") {
145
796
  const symbol = requireSymbol(resolved, rawArg);
146
797
  const { tickerFile, financials } = await fetchTickerFinancials(context, symbol);
147
798
  if (!financials.quote) throw new Error(`No quote data available for ${symbol}.`);
148
799
  const toBase = createBaseConverter(context.dataProvider, context.config.baseCurrency);
149
- return buildTickerReport({
800
+ const text = await buildTickerReport({
150
801
  symbol,
151
802
  tickerFile,
152
803
  financials,
153
804
  config: context.config,
154
805
  toBase,
155
806
  });
807
+ return {
808
+ data: {
809
+ kind: "legacy-ticker-summary",
810
+ ...reportBase(resolved, [symbol], 1),
811
+ text,
812
+ },
813
+ text,
814
+ };
156
815
  }
157
816
 
158
- return buildPaneDescriptionReport(resolved, rawArg);
159
- }
160
-
161
- function buildPaneDescriptionReport(resolved: ResolvedPaneFunction, rawArg: string): string {
162
- const lines = [
163
- `${resolved.label}`,
817
+ const settings = Object.entries(resolved.instance.settings ?? {}).map(([key, value]) => ({
818
+ key,
819
+ value,
820
+ }));
821
+ const text = [
822
+ resolved.label,
164
823
  resolved.description,
165
824
  "",
166
825
  `Pane: ${resolved.pane.id} (${resolved.pane.name})`,
167
- ];
168
- if (resolved.template) lines.push(`Template: ${resolved.template.id}`);
169
- if (resolved.shortcut) lines.push(`Shortcut: ${resolved.shortcut}`);
170
- if (rawArg) lines.push(`Argument: ${rawArg}`);
171
- const settingsEntries = Object.entries(resolved.instance.settings ?? {});
172
- if (settingsEntries.length > 0) {
173
- lines.push("");
174
- lines.push("Settings:");
175
- for (const [key, value] of settingsEntries) {
176
- lines.push(` ${key}: ${String(value)}`);
177
- }
178
- }
179
- return lines.join("\n");
826
+ ...(resolved.template ? [`Template: ${resolved.template.id}`] : []),
827
+ ...(resolved.shortcut ? [`Shortcut: ${resolved.shortcut}`] : []),
828
+ ...(rawArg ? [`Argument: ${rawArg}`] : []),
829
+ ...(settings.length > 0
830
+ ? ["", "Settings:", ...settings.map(({ key, value }) => ` ${key}: ${String(value)}`)]
831
+ : []),
832
+ ].join("\n");
833
+ return {
834
+ data: {
835
+ kind: "pane-description",
836
+ ...reportBase(resolved, symbols, settings.length),
837
+ paneId: resolved.pane.id,
838
+ templateId: resolved.template?.id ?? null,
839
+ settings,
840
+ },
841
+ text,
842
+ };
180
843
  }