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
@@ -13,6 +13,10 @@ import {
13
13
  normalizePaneLayout,
14
14
  normalizePaneId,
15
15
  } from "../../types/config";
16
+ import {
17
+ migrateChartPaneSettings,
18
+ type LegacyChartMigrationContext,
19
+ } from "./chart-settings";
16
20
 
17
21
  export function isLayoutConfig(value: unknown): value is LayoutConfig {
18
22
  return !!value
@@ -116,7 +120,11 @@ function sanitizePaneSettings(value: unknown): Record<string, unknown> | undefin
116
120
  return Object.keys(settings).length > 0 ? clonePaneSettings(settings) : undefined;
117
121
  }
118
122
 
119
- function sanitizePaneInstances(value: unknown, fallback: LayoutConfig): PaneInstanceConfig[] {
123
+ function sanitizePaneInstances(
124
+ value: unknown,
125
+ fallback: LayoutConfig,
126
+ chartMigration: LegacyChartMigrationContext,
127
+ ): PaneInstanceConfig[] {
120
128
  if (!Array.isArray(value)) return cloneLayout(fallback).instances;
121
129
  const seen = new Set<string>();
122
130
  const instances = value
@@ -127,20 +135,23 @@ function sanitizePaneInstances(value: unknown, fallback: LayoutConfig): PaneInst
127
135
  && typeof (entry as PaneInstanceConfig).paneId === "string",
128
136
  )
129
137
  .map((entry) => {
138
+ const originalPaneId = entry.paneId;
130
139
  const paneId = normalizePaneId(entry.paneId);
131
140
  const instanceId = seen.has(entry.instanceId) ? createPaneInstanceId(paneId) : entry.instanceId;
132
141
  seen.add(instanceId);
142
+ const binding = sanitizePaneBinding(entry.binding);
143
+ const settings = sanitizePaneSettings(entry.settings);
133
144
  return {
134
145
  instanceId,
135
146
  paneId,
136
147
  title: typeof entry.title === "string" ? entry.title : undefined,
137
- binding: sanitizePaneBinding(entry.binding),
148
+ binding,
138
149
  params: entry.params && typeof entry.params === "object"
139
150
  ? Object.fromEntries(
140
151
  Object.entries(entry.params).filter((param): param is [string, string] => typeof param[1] === "string"),
141
152
  )
142
153
  : undefined,
143
- settings: sanitizePaneSettings(entry.settings),
154
+ settings: migrateChartPaneSettings(originalPaneId, binding, settings, chartMigration),
144
155
  placementMemory: sanitizePlacementMemory(entry.placementMemory),
145
156
  };
146
157
  });
@@ -196,7 +207,11 @@ function sanitizeDetachedEntries(value: unknown, validInstanceIds: Set<string>):
196
207
  }));
197
208
  }
198
209
 
199
- export function sanitizeLayout(value: unknown, fallback: LayoutConfig): LayoutConfig {
210
+ export function sanitizeLayout(
211
+ value: unknown,
212
+ fallback: LayoutConfig,
213
+ chartMigration: LegacyChartMigrationContext = {},
214
+ ): LayoutConfig {
200
215
  if (!isLayoutConfig(value)) {
201
216
  return cloneLayout(fallback);
202
217
  }
@@ -208,7 +223,11 @@ export function sanitizeLayout(value: unknown, fallback: LayoutConfig): LayoutCo
208
223
  });
209
224
  }
210
225
 
211
- const instances = sanitizePaneInstances((value as LayoutConfig & { instances?: unknown }).instances, fallback);
226
+ const instances = sanitizePaneInstances(
227
+ (value as LayoutConfig & { instances?: unknown }).instances,
228
+ fallback,
229
+ chartMigration,
230
+ );
212
231
  const validInstanceIds = new Set(instances.map((entry) => entry.instanceId));
213
232
  const dockRoot = (value as { dockRoot?: LayoutConfig["dockRoot"] }).dockRoot ?? null;
214
233
  const floating = sanitizeFloatingEntries((value as { floating?: unknown }).floating, validInstanceIds);
@@ -37,7 +37,7 @@ function createSavedConfig(overrides: Record<string, unknown> = {}): Record<stri
37
37
  brokerInstances: [],
38
38
  disabledPlugins: [],
39
39
  theme: "amber",
40
- chartPreferences: { defaultRenderMode: "area", renderer: "auto" },
40
+ chartPreferences: { renderer: "auto" },
41
41
  valueFlashingEnabled: true,
42
42
  recentTickers: [],
43
43
  ...overrides,
@@ -49,6 +49,17 @@ async function writeConfigJson(dataDir: string, config: Record<string, unknown>)
49
49
  }
50
50
 
51
51
  describe("sanitizeLayout", () => {
52
+ test("keeps the default research layout free of retired chart settings", () => {
53
+ const researchPanes = DEFAULT_LAYOUT.instances.filter((instance) => instance.paneId === "ticker-research");
54
+ expect(researchPanes.length).toBeGreaterThan(0);
55
+ for (const pane of researchPanes) {
56
+ expect(pane.settings).not.toHaveProperty("chartRangePreset");
57
+ expect(pane.settings).not.toHaveProperty("chartResolution");
58
+ expect(pane.settings).not.toHaveProperty("chartAxisMode");
59
+ expect(pane.settings).not.toHaveProperty("chartRenderMode");
60
+ }
61
+ });
62
+
52
63
  test("rewrites unbound ticker-detail panes to follow the first portfolio pane", () => {
53
64
  const layout = sanitizeLayout({
54
65
  dockRoot: {
@@ -159,6 +170,88 @@ describe("sanitizeLayout", () => {
159
170
  });
160
171
  });
161
172
 
173
+ test("converts retired chart panes into composer specs", () => {
174
+ const layout = sanitizeLayout({
175
+ dockRoot: { kind: "pane", instanceId: "comparison-chart:main" },
176
+ instances: [{
177
+ instanceId: "comparison-chart:main",
178
+ paneId: "comparison-chart",
179
+ binding: { kind: "none" },
180
+ settings: {
181
+ symbols: ["AAPL", "MSFT"],
182
+ axisMode: "percent",
183
+ rangePreset: "1Y",
184
+ chartResolution: "1d",
185
+ },
186
+ }],
187
+ floating: [],
188
+ detached: [],
189
+ }, DEFAULT_LAYOUT);
190
+
191
+ const pane = findPaneInstance(layout, "comparison-chart:main");
192
+ expect(pane?.paneId).toBe("chart-composer");
193
+ expect(pane?.settings).toEqual({
194
+ chartSpec: expect.objectContaining({
195
+ version: 1,
196
+ viewport: { range: "1Y", resolution: "1d" },
197
+ series: [
198
+ expect.objectContaining({
199
+ transform: "percent",
200
+ interpolation: "none",
201
+ source: expect.objectContaining({ fieldId: "market.close" }),
202
+ }),
203
+ expect.objectContaining({
204
+ transform: "percent",
205
+ interpolation: "none",
206
+ source: expect.objectContaining({ fieldId: "market.close" }),
207
+ }),
208
+ ],
209
+ }),
210
+ });
211
+ });
212
+
213
+ test("migrates ticker research chart settings into one composer spec", () => {
214
+ const layout = sanitizeLayout({
215
+ dockRoot: { kind: "pane", instanceId: "ticker-detail:aapl" },
216
+ instances: [{
217
+ instanceId: "ticker-detail:aapl",
218
+ paneId: "ticker-research",
219
+ binding: { kind: "fixed", symbol: "AAPL" },
220
+ settings: {
221
+ hideTabs: true,
222
+ lockedTabId: "fundamental-graphs",
223
+ chartAxisMode: "percent",
224
+ chartRangePreset: "1Y",
225
+ chartResolution: "1wk",
226
+ },
227
+ }],
228
+ floating: [],
229
+ detached: [],
230
+ }, DEFAULT_LAYOUT);
231
+
232
+ const settings = findPaneInstance(layout, "ticker-detail:aapl")?.settings;
233
+ expect(settings).toEqual({
234
+ hideTabs: true,
235
+ lockedTabId: "chart",
236
+ chartSpec: expect.objectContaining({
237
+ version: 1,
238
+ viewport: { range: "1Y", resolution: "1wk" },
239
+ series: [expect.objectContaining({
240
+ transform: "percent",
241
+ interpolation: "none",
242
+ source: expect.objectContaining({
243
+ kind: "security",
244
+ instrument: { symbol: "AAPL" },
245
+ fieldId: "market.ohlcv",
246
+ }),
247
+ })],
248
+ }),
249
+ });
250
+ expect(settings).not.toHaveProperty("chartAxisMode");
251
+ expect(settings).not.toHaveProperty("chartRangePreset");
252
+ expect(settings).not.toHaveProperty("chartResolution");
253
+ });
254
+
162
255
  test("falls back to the default layout when given an obsolete column layout", () => {
163
256
  const layout = sanitizeLayout({
164
257
  columns: [{ width: "100%" }],
@@ -179,6 +272,175 @@ describe("sanitizeLayout", () => {
179
272
  });
180
273
 
181
274
  describe("loadConfig", () => {
275
+ test("folds saved graph plugin state into composer specs and removes only chart-owned state", async () => {
276
+ const dataDir = await createTempConfigDir();
277
+ const legacyLayout = {
278
+ dockRoot: {
279
+ kind: "split" as const,
280
+ axis: "horizontal" as const,
281
+ ratio: 0.5,
282
+ first: { kind: "pane" as const, instanceId: "fundamental-graph:pair" },
283
+ second: { kind: "pane" as const, instanceId: "ticker-detail:nvda" },
284
+ },
285
+ instances: [
286
+ {
287
+ instanceId: "fundamental-graph:pair",
288
+ paneId: "fundamental-graph",
289
+ binding: { kind: "fixed" as const, symbol: "AAPL" },
290
+ settings: {
291
+ chartKind: "fundamental",
292
+ metric: "totalRevenue",
293
+ period: "quarterly",
294
+ periods: 8,
295
+ symbols: ["AAPL", "MSFT"],
296
+ symbolsText: "AAPL, MSFT",
297
+ },
298
+ },
299
+ {
300
+ instanceId: "ticker-detail:nvda",
301
+ paneId: "ticker-research",
302
+ binding: { kind: "fixed" as const, symbol: "NVDA" },
303
+ settings: {
304
+ hideTabs: true,
305
+ lockedTabId: "fundamental-graphs",
306
+ chartRangePreset: "1Y",
307
+ chartResolution: "1wk",
308
+ },
309
+ },
310
+ ],
311
+ floating: [],
312
+ detached: [],
313
+ };
314
+ await writeConfigJson(dataDir, createSavedConfig({
315
+ configVersion: CURRENT_CONFIG_VERSION - 1,
316
+ layout: legacyLayout,
317
+ layouts: [{
318
+ name: "Graphs",
319
+ layout: legacyLayout,
320
+ paneState: {
321
+ "fundamental-graph:pair": {
322
+ cursorSymbol: "AAPL",
323
+ pluginState: {
324
+ "ticker-detail": {
325
+ period: "annual",
326
+ chartKind: "valuation",
327
+ metric: "evSales",
328
+ periods: 3,
329
+ selectedIdx: 4,
330
+ hiddenSeriesIds: ["MSFT"],
331
+ retainedPreference: "keep",
332
+ },
333
+ },
334
+ },
335
+ "ticker-detail:nvda": {
336
+ activeTabId: "fundamental-graphs",
337
+ financialSubTab: "cashflow",
338
+ pluginState: {
339
+ "ticker-detail": {
340
+ detailPeriod: "annual",
341
+ detailChartKind: "fundamental",
342
+ detailMetric: "grossProfit",
343
+ selectedIdx: 2,
344
+ hiddenSeriesIds: [],
345
+ retainedPreference: "keep-too",
346
+ },
347
+ },
348
+ },
349
+ },
350
+ }],
351
+ activeLayoutIndex: 0,
352
+ }));
353
+
354
+ const config = await loadConfig(dataDir);
355
+ const standalone = findPaneInstance(config.layout, "fundamental-graph:pair");
356
+ const standaloneSpec = standalone?.settings?.chartSpec as any;
357
+ expect(standalone?.paneId).toBe("chart-composer");
358
+ expect(standalone?.settings).toEqual({ chartSpec: expect.any(Object) });
359
+ expect(standaloneSpec.viewport).toEqual({ range: "ALL", resolution: "auto", maxPoints: 3 });
360
+ expect(standaloneSpec.series.map((series: any) => ({
361
+ symbol: series.source.instrument.symbol,
362
+ fieldId: series.source.fieldId,
363
+ period: series.source.period,
364
+ visible: series.visible,
365
+ }))).toEqual([
366
+ { symbol: "AAPL", fieldId: "valuation.evSales", period: "annual", visible: true },
367
+ { symbol: "MSFT", fieldId: "valuation.evSales", period: "annual", visible: false },
368
+ ]);
369
+
370
+ const research = findPaneInstance(config.layout, "ticker-detail:nvda");
371
+ const researchSpec = research?.settings?.chartSpec as any;
372
+ expect(research?.settings?.lockedTabId).toBe("chart");
373
+ expect(researchSpec.viewport).toEqual({ range: "ALL", resolution: "auto", maxPoints: undefined });
374
+ expect(researchSpec.series[0]).toEqual(expect.objectContaining({
375
+ style: "columns",
376
+ source: expect.objectContaining({
377
+ fieldId: "fundamental.grossProfit",
378
+ period: "annual",
379
+ }),
380
+ }));
381
+
382
+ expect(config.layouts[0]?.paneState).toEqual({
383
+ "fundamental-graph:pair": {
384
+ cursorSymbol: "AAPL",
385
+ pluginState: { "ticker-research": { retainedPreference: "keep" } },
386
+ },
387
+ "ticker-detail:nvda": {
388
+ activeTabId: "chart",
389
+ financialSubTab: "cashflow",
390
+ pluginState: { "ticker-research": { retainedPreference: "keep-too" } },
391
+ },
392
+ });
393
+ });
394
+
395
+ test("migrates global indicator selection and render mode without retaining plugin keys", async () => {
396
+ const dataDir = await createTempConfigDir();
397
+ const legacyLayout = {
398
+ dockRoot: { kind: "pane" as const, instanceId: "ticker-chart:aapl" },
399
+ instances: [{
400
+ instanceId: "ticker-chart:aapl",
401
+ paneId: "ticker-chart",
402
+ binding: { kind: "fixed" as const, symbol: "AAPL" },
403
+ settings: {
404
+ chartAxisMode: "percent",
405
+ chartRangePreset: "6M",
406
+ chartResolution: "1d",
407
+ chartRenderMode: "candles",
408
+ },
409
+ }],
410
+ floating: [],
411
+ detached: [],
412
+ };
413
+ await writeConfigJson(dataDir, createSavedConfig({
414
+ configVersion: CURRENT_CONFIG_VERSION - 1,
415
+ layout: legacyLayout,
416
+ layouts: [{ name: "Price", layout: legacyLayout }],
417
+ activeLayoutIndex: 0,
418
+ chartPreferences: { renderer: "kitty", defaultRenderMode: "line" },
419
+ pluginConfig: {
420
+ "ticker-detail": {
421
+ chartIndicators: ["sma50", "bollinger20"],
422
+ chartIndicatorsVersion: 2,
423
+ retainedPreference: "keep",
424
+ },
425
+ },
426
+ }));
427
+
428
+ const config = await loadConfig(dataDir);
429
+ const pane = findPaneInstance(config.layout, "ticker-chart:aapl");
430
+ const spec = pane?.settings?.chartSpec as any;
431
+ expect(pane?.paneId).toBe("chart-composer");
432
+ expect(spec.viewport).toEqual({ range: "6M", resolution: "1d" });
433
+ expect(spec.series[0]).toEqual(expect.objectContaining({ style: "candles", transform: "raw" }));
434
+ expect(spec.studies.map((study: any) => ({ kind: study.kind, parameters: study.parameters }))).toEqual([
435
+ { kind: "sma", parameters: { period: 50 } },
436
+ { kind: "bollinger", parameters: { period: 20, stdDev: 2 } },
437
+ ]);
438
+ expect(config.pluginConfig).toEqual({
439
+ "ticker-research": { retainedPreference: "keep" },
440
+ });
441
+ expect(config.chartPreferences).toEqual({ renderer: "kitty" });
442
+ });
443
+
182
444
  test("defaults detached layouts to an empty list for older configs", async () => {
183
445
  const dataDir = await createTempConfigDir();
184
446
  const layoutWithoutDetached = {
@@ -209,7 +471,6 @@ describe("loadConfig", () => {
209
471
  const config = await loadConfig(dataDir);
210
472
 
211
473
  expect(config.chartPreferences).toEqual({
212
- defaultRenderMode: "area",
213
474
  renderer: "auto",
214
475
  });
215
476
  });
@@ -219,7 +480,6 @@ describe("loadConfig", () => {
219
480
  await writeConfigJson(dataDir, createSavedConfig({
220
481
  configVersion: 7,
221
482
  chartPreferences: {
222
- defaultRenderMode: "hlc",
223
483
  renderer: "nope",
224
484
  },
225
485
  }));
@@ -227,7 +487,6 @@ describe("loadConfig", () => {
227
487
  const config = await loadConfig(dataDir);
228
488
 
229
489
  expect(config.chartPreferences).toEqual({
230
- defaultRenderMode: "hlc",
231
490
  renderer: "auto",
232
491
  });
233
492
  expect(config.pluginConfig).toEqual({});
@@ -261,7 +520,7 @@ describe("loadConfig", () => {
261
520
  displayMode: "expanded",
262
521
  },
263
522
  },
264
- chartPreferences: { defaultRenderMode: "line" },
523
+ chartPreferences: { renderer: "auto" },
265
524
  }));
266
525
 
267
526
  const config = await loadConfig(dataDir);
@@ -285,16 +544,43 @@ describe("loadConfig", () => {
285
544
  expect(config.disabledPlugins).toEqual(["chat", "news"]);
286
545
  });
287
546
 
288
- test("migrates disabled built-in feature plugins to grouped plugin ids", async () => {
547
+ test("migrates disabled built-in modules to their owning plugin ids", async () => {
289
548
  const dataDir = await createTempConfigDir();
290
549
  await writeConfigJson(dataDir, createSavedConfig({
291
550
  configVersion: CURRENT_CONFIG_VERSION - 1,
292
- disabledPlugins: ["options", "sec", "world-indices", "earnings-calendar", "ibkr", "broker-manager", "options"],
551
+ disabledPlugins: [
552
+ "options",
553
+ "sec",
554
+ "thirteenf",
555
+ "world-indices",
556
+ "market-heatmap",
557
+ "fear-greed",
558
+ "chart-composer",
559
+ "comparison-chart",
560
+ "earnings-calendar",
561
+ "macro-tv",
562
+ "ibkr",
563
+ "broker-manager",
564
+ "analytics",
565
+ "kelly-sizer",
566
+ "portfolio-list",
567
+ "changelog",
568
+ "help",
569
+ "layout-manager",
570
+ "application",
571
+ ],
293
572
  }));
294
573
 
295
574
  const config = await loadConfig(dataDir);
296
575
 
297
- expect(config.disabledPlugins).toEqual(["ticker-research", "market-overview", "macro", "ibkr", "broker"]);
576
+ expect(config.disabledPlugins).toEqual([
577
+ "ticker-research",
578
+ "market-overview",
579
+ "macro",
580
+ "ibkr",
581
+ "broker",
582
+ "portfolio",
583
+ ]);
298
584
  });
299
585
 
300
586
  test("migrates grouped built-in plugin config keys", async () => {
@@ -308,6 +594,16 @@ describe("loadConfig", () => {
308
594
  "company-research": {
309
595
  preferredTab: "analyst-research",
310
596
  },
597
+ "kelly-sizer": {
598
+ inherited: true,
599
+ shared: "legacy",
600
+ },
601
+ portfolio: {
602
+ shared: "canonical",
603
+ },
604
+ changelog: {
605
+ dismissedVersion: "1.2.3",
606
+ },
311
607
  },
312
608
  }));
313
609
 
@@ -318,9 +614,28 @@ describe("loadConfig", () => {
318
614
  selectedExpiration: "2026-06-19",
319
615
  preferredTab: "analyst-research",
320
616
  },
617
+ portfolio: {
618
+ inherited: true,
619
+ shared: "canonical",
620
+ },
621
+ application: {
622
+ dismissedVersion: "1.2.3",
623
+ },
321
624
  });
322
625
  });
323
626
 
627
+ test("does not repeat the legacy Cloud-to-Macro disable migration", async () => {
628
+ const dataDir = await createTempConfigDir();
629
+ await writeConfigJson(dataDir, createSavedConfig({
630
+ configVersion: CURRENT_CONFIG_VERSION - 1,
631
+ disabledPlugins: ["gloomberb-cloud"],
632
+ }));
633
+
634
+ const config = await loadConfig(dataDir);
635
+
636
+ expect(config.disabledPlugins).toEqual(["gloomberb-cloud"]);
637
+ });
638
+
324
639
  test("enables Gloom Cloud when migrating older default configs", async () => {
325
640
  const dataDir = await createTempConfigDir();
326
641
  await writeConfigJson(dataDir, createSavedConfig({
@@ -361,14 +676,20 @@ describe("loadConfig", () => {
361
676
  });
362
677
  });
363
678
 
364
- test("preserves saved layout pane state and focus metadata", async () => {
679
+ test("migrates legacy saved pane tab IDs and preserves focus metadata", async () => {
365
680
  const dataDir = await createTempConfigDir();
366
681
  await writeConfigJson(dataDir, createSavedConfig({
367
682
  layouts: [{
368
683
  name: "Chart",
369
684
  layout: DEFAULT_LAYOUT,
370
685
  paneState: {
371
- "ticker-detail:main": { activeTabId: "chart" },
686
+ "ticker-detail:main": {
687
+ activeTabId: "fundamental-graphs",
688
+ pluginState: {
689
+ "ticker-detail": { detailMetric: "revenue", shared: "legacy" },
690
+ "ticker-research": { shared: "canonical" },
691
+ },
692
+ },
372
693
  "missing:pane": { activeTabId: "overview" },
373
694
  },
374
695
  focusedPaneId: "ticker-detail:main",
@@ -379,7 +700,12 @@ describe("loadConfig", () => {
379
700
  const config = await loadConfig(dataDir);
380
701
 
381
702
  expect(config.layouts[0]?.paneState).toEqual({
382
- "ticker-detail:main": { activeTabId: "chart" },
703
+ "ticker-detail:main": {
704
+ activeTabId: "chart",
705
+ pluginState: {
706
+ "ticker-research": { shared: "canonical" },
707
+ },
708
+ },
383
709
  });
384
710
  expect(config.layouts[0]?.focusedPaneId).toBe("ticker-detail:main");
385
711
  expect(config.layouts[0]?.activePanel).toBe("right");
@@ -389,7 +715,12 @@ describe("loadConfig", () => {
389
715
  layouts: Array<{ paneState?: Record<string, unknown>; focusedPaneId?: string | null; activePanel?: string }>;
390
716
  };
391
717
  expect(persisted.layouts[0]?.paneState).toEqual({
392
- "ticker-detail:main": { activeTabId: "chart" },
718
+ "ticker-detail:main": {
719
+ activeTabId: "chart",
720
+ pluginState: {
721
+ "ticker-research": { shared: "canonical" },
722
+ },
723
+ },
393
724
  });
394
725
  expect(persisted.layouts[0]?.focusedPaneId).toBe("ticker-detail:main");
395
726
  expect(persisted.layouts[0]?.activePanel).toBe("right");
@@ -426,6 +757,37 @@ describe("loadConfig", () => {
426
757
  .toEqual(DEFAULT_PORTFOLIO_COLUMN_IDS);
427
758
  });
428
759
 
760
+ test("does not replay the portfolio column migration for version 19 configs", async () => {
761
+ const dataDir = await createTempConfigDir();
762
+ const selectedColumnIds = DEFAULT_COLUMNS.map((column) => column.id);
763
+ const layout = {
764
+ ...DEFAULT_LAYOUT,
765
+ instances: DEFAULT_LAYOUT.instances.map((instance) => (
766
+ instance.instanceId === "portfolio-list:main"
767
+ ? {
768
+ ...instance,
769
+ settings: {
770
+ ...(instance.settings ?? {}),
771
+ columnIds: selectedColumnIds,
772
+ },
773
+ }
774
+ : instance
775
+ )),
776
+ };
777
+
778
+ await writeConfigJson(dataDir, createSavedConfig({
779
+ configVersion: 19,
780
+ layout,
781
+ layouts: [{ name: "Default", layout }],
782
+ }));
783
+
784
+ const config = await loadConfig(dataDir);
785
+
786
+ expect(findPaneInstance(config.layout, "portfolio-list:main")?.settings?.columnIds).toEqual(selectedColumnIds);
787
+ expect(findPaneInstance(config.layouts[0]?.layout ?? DEFAULT_LAYOUT, "portfolio-list:main")?.settings?.columnIds)
788
+ .toEqual(selectedColumnIds);
789
+ });
790
+
429
791
  test("falls back to the default layout when persisted layouts use the obsolete column shape", async () => {
430
792
  const dataDir = await createTempConfigDir();
431
793
  const obsoleteLayout = {
@@ -456,7 +818,8 @@ describe("loadConfig", () => {
456
818
  expect(config.configVersion).toBe(CURRENT_CONFIG_VERSION);
457
819
  expect(config.activeLayoutIndex).toBe(1);
458
820
  expect(config.layouts.map((layout) => layout.name)).toEqual(["Default", "Research"]);
459
- expect(config.layout).toEqual(DEFAULT_LAYOUT);
821
+ const expectedLayout = sanitizeLayout(DEFAULT_LAYOUT, DEFAULT_LAYOUT);
822
+ expect(config.layout).toEqual(expectedLayout);
460
823
 
461
824
  await saveConfig(config);
462
825
  const persisted = JSON.parse(await readFile(join(dataDir, "config.json"), "utf-8")) as {
@@ -467,7 +830,7 @@ describe("loadConfig", () => {
467
830
 
468
831
  expect(persisted.configVersion).toBe(CURRENT_CONFIG_VERSION);
469
832
  expect(persisted.activeLayoutIndex).toBe(1);
470
- expect(persisted.layouts[1]?.layout).toEqual(DEFAULT_LAYOUT as unknown as Record<string, unknown>);
833
+ expect(persisted.layouts[1]?.layout).toEqual(JSON.parse(JSON.stringify(expectedLayout)));
471
834
  });
472
835
  });
473
836