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
@@ -0,0 +1,152 @@
1
+ import type { ResolvedSeries, SeriesTransform, TimeSeriesPoint } from "./types";
2
+
3
+ const DAY_MS = 24 * 60 * 60 * 1_000;
4
+ const NUMERIC_POINT_FIELDS = ["value", "open", "high", "low", "close", "volume"] as const;
5
+
6
+ function finiteNumber(value: unknown): value is number {
7
+ return typeof value === "number" && Number.isFinite(value);
8
+ }
9
+
10
+ function clonePoint(point: TimeSeriesPoint): TimeSeriesPoint {
11
+ return {
12
+ ...point,
13
+ date: new Date(point.date),
14
+ observedAt: new Date(point.observedAt),
15
+ availableAt: point.availableAt ? new Date(point.availableAt) : undefined,
16
+ provenance: point.provenance ? { ...point.provenance } : undefined,
17
+ };
18
+ }
19
+
20
+ function primaryValue(point: TimeSeriesPoint): number | null {
21
+ return finiteNumber(point.value)
22
+ ? point.value
23
+ : finiteNumber(point.close)
24
+ ? point.close
25
+ : null;
26
+ }
27
+
28
+ function relativeValue(value: number | null | undefined, baseline: number, index100: boolean): number | null {
29
+ if (!finiteNumber(value) || baseline === 0) return null;
30
+ return index100 ? (value / baseline) * 100 : ((value - baseline) / Math.abs(baseline)) * 100;
31
+ }
32
+
33
+ function growthValue(value: number | null | undefined, previous: number | null | undefined): number | null {
34
+ if (!finiteNumber(value) || !finiteNumber(previous) || previous === 0) return null;
35
+ return ((value - previous) / Math.abs(previous)) * 100;
36
+ }
37
+
38
+ function shiftUtcMonths(date: Date, months: number): Date {
39
+ const shifted = new Date(date);
40
+ const originalDay = shifted.getUTCDate();
41
+ shifted.setUTCDate(1);
42
+ shifted.setUTCMonth(shifted.getUTCMonth() - months);
43
+ const lastDay = new Date(Date.UTC(
44
+ shifted.getUTCFullYear(),
45
+ shifted.getUTCMonth() + 1,
46
+ 0,
47
+ )).getUTCDate();
48
+ shifted.setUTCDate(Math.min(originalDay, lastDay));
49
+ return shifted;
50
+ }
51
+
52
+ function referencePoint(
53
+ points: readonly TimeSeriesPoint[],
54
+ currentIndex: number,
55
+ months: number,
56
+ toleranceDays: number,
57
+ ): TimeSeriesPoint | null {
58
+ const current = points[currentIndex];
59
+ if (!current) return null;
60
+ const currentTime = current.observedAt.getTime();
61
+ const target = shiftUtcMonths(current.observedAt, months).getTime();
62
+ let best: { point: TimeSeriesPoint; distance: number; date: number } | null = null;
63
+ for (let index = 0; index < currentIndex; index += 1) {
64
+ const candidate = points[index]!;
65
+ const candidateTime = candidate.observedAt.getTime();
66
+ if (!Number.isFinite(candidateTime) || candidateTime >= currentTime) continue;
67
+ const distance = Math.abs(candidateTime - target);
68
+ if (distance > toleranceDays * DAY_MS) continue;
69
+ if (!best || distance < best.distance || (distance === best.distance && candidateTime > best.date)) {
70
+ best = { point: candidate, distance, date: candidateTime };
71
+ }
72
+ }
73
+ return best?.point ?? null;
74
+ }
75
+
76
+ function mapNumericFields(
77
+ point: TimeSeriesPoint,
78
+ mapper: (value: number | null | undefined, field: typeof NUMERIC_POINT_FIELDS[number]) => number | null,
79
+ ): TimeSeriesPoint {
80
+ const transformed = clonePoint(point);
81
+ for (const field of NUMERIC_POINT_FIELDS) {
82
+ const value = point[field];
83
+ if (field !== "value" && value === undefined) continue;
84
+ (transformed as unknown as Record<string, unknown>)[field] = mapper(value, field);
85
+ }
86
+ return transformed;
87
+ }
88
+
89
+ /** Applies a display transform without mutating the source observations. */
90
+ export function applySeriesTransform(
91
+ sourcePoints: readonly TimeSeriesPoint[],
92
+ transform: SeriesTransform,
93
+ options: { baseline?: number | null } = {},
94
+ ): TimeSeriesPoint[] {
95
+ const points = sourcePoints
96
+ .map(clonePoint)
97
+ .filter((point) => Number.isFinite(point.date.getTime()) && Number.isFinite(point.observedAt.getTime()))
98
+ .sort((left, right) => left.date.getTime() - right.date.getTime());
99
+ if (transform === "raw") return points;
100
+
101
+ if (transform === "percent" || transform === "index100") {
102
+ const baselinePoint = points.find((point) => {
103
+ const value = primaryValue(point);
104
+ return value !== null && value !== 0;
105
+ });
106
+ const baseline = options.baseline !== undefined
107
+ ? options.baseline
108
+ : baselinePoint ? primaryValue(baselinePoint) : null;
109
+ if (baseline === null || baseline === 0) {
110
+ return points.map((point) => mapNumericFields(point, () => null));
111
+ }
112
+ return points.map((point) => mapNumericFields(
113
+ point,
114
+ (value) => relativeValue(value, baseline, transform === "index100"),
115
+ ));
116
+ }
117
+
118
+ if (transform === "log") {
119
+ return points.map((point) => mapNumericFields(
120
+ point,
121
+ (value) => finiteNumber(value) && value > 0 ? Math.log(value) : null,
122
+ ));
123
+ }
124
+
125
+ const months = transform === "yoy" ? 12 : 3;
126
+ const toleranceDays = transform === "yoy" ? 62 : 46;
127
+ return points.map((point, index) => {
128
+ const reference = referencePoint(points, index, months, toleranceDays);
129
+ if (!reference) return mapNumericFields(point, () => null);
130
+ return mapNumericFields(point, (value, field) => growthValue(value, reference[field]));
131
+ });
132
+ }
133
+
134
+ export function applyResolvedSeriesTransform(
135
+ series: ResolvedSeries,
136
+ transform: SeriesTransform = series.transform,
137
+ options: { baseline?: number | null } = {},
138
+ ): ResolvedSeries {
139
+ if (transform === "raw") return { ...series, transform, points: applySeriesTransform(series.points, "raw") };
140
+ const percent = transform === "percent" || transform === "yoy" || transform === "qoq";
141
+ return {
142
+ ...series,
143
+ transform,
144
+ unit: percent ? "%" : transform === "index100" ? "index" : "log",
145
+ unitGroup: percent ? "percent" : transform === "index100" ? "index" : "log",
146
+ dataShape: "scalar",
147
+ style: series.style === "candles" || series.style === "ohlc" || series.style === "hlc"
148
+ ? "line"
149
+ : series.style,
150
+ points: applySeriesTransform(series.points, transform, options),
151
+ };
152
+ }
@@ -0,0 +1,162 @@
1
+ import type { ChartResolution, TimeRange } from "../components/chart/core/types";
2
+ import type { InstrumentRef } from "../market-data/request-types";
3
+
4
+ export const CHART_SPEC_VERSION = 1 as const;
5
+
6
+ export type SeriesPeriod = "auto" | "daily" | "weekly" | "monthly" | "quarterly" | "annual" | "ttm";
7
+ export type SeriesStyle = "line" | "area" | "step" | "columns" | "points" | "candles" | "ohlc" | "hlc";
8
+ export type SeriesTransform = "raw" | "percent" | "index100" | "yoy" | "qoq" | "log";
9
+ export type SeriesAxis = "auto" | "left" | "right";
10
+ export type SeriesInterpolation = "none" | "step-after";
11
+ export type SeriesTimestampMode = "available-at" | "period-end";
12
+ export type PanelScale = "linear" | "log";
13
+
14
+ export interface SecuritySeriesSource {
15
+ kind: "security";
16
+ instrument: InstrumentRef;
17
+ fieldId: string;
18
+ period?: SeriesPeriod;
19
+ timestampMode?: SeriesTimestampMode;
20
+ }
21
+
22
+ export interface EconomicSeriesSource {
23
+ kind: "economic";
24
+ provider: "fred";
25
+ seriesId: string;
26
+ }
27
+
28
+ export type ChartSeriesSource = SecuritySeriesSource | EconomicSeriesSource;
29
+
30
+ export interface ChartSeriesSpec {
31
+ id: string;
32
+ source: ChartSeriesSource;
33
+ label?: string;
34
+ style: SeriesStyle;
35
+ transform: SeriesTransform;
36
+ axis: SeriesAxis;
37
+ panelId: string;
38
+ interpolation: SeriesInterpolation;
39
+ color?: string;
40
+ visible?: boolean;
41
+ }
42
+
43
+ export type ChartStudyKind =
44
+ | "volume"
45
+ | "sma"
46
+ | "ema"
47
+ | "bollinger"
48
+ | "rsi"
49
+ | "macd"
50
+ | "ratio"
51
+ | "spread"
52
+ | "correlation";
53
+
54
+ export interface ChartStudySpec {
55
+ id: string;
56
+ kind: ChartStudyKind;
57
+ inputSeriesIds: string[];
58
+ parameters: Record<string, number>;
59
+ panelId: string;
60
+ axis: SeriesAxis;
61
+ color?: string;
62
+ visible?: boolean;
63
+ }
64
+
65
+ export interface ChartPanelSpec {
66
+ id: string;
67
+ label?: string;
68
+ height?: number;
69
+ scale?: PanelScale;
70
+ }
71
+
72
+ export interface ChartViewportSpec {
73
+ range: TimeRange;
74
+ resolution: ChartResolution;
75
+ dateWindow?: { start: string; end: string };
76
+ /** Optional latest-observation cap, useful for period-based financial views. */
77
+ maxPoints?: number;
78
+ }
79
+
80
+ export interface ChartSpec {
81
+ version: typeof CHART_SPEC_VERSION;
82
+ viewport: ChartViewportSpec;
83
+ panels: ChartPanelSpec[];
84
+ series: ChartSeriesSpec[];
85
+ studies: ChartStudySpec[];
86
+ }
87
+
88
+ export type SeriesDataShape = "scalar" | "ohlcv" | "event" | "band";
89
+
90
+ export interface TimeSeriesPoint {
91
+ date: Date;
92
+ observedAt: Date;
93
+ availableAt?: Date;
94
+ value: number | null;
95
+ open?: number | null;
96
+ high?: number | null;
97
+ low?: number | null;
98
+ close?: number | null;
99
+ volume?: number | null;
100
+ periodLabel?: string;
101
+ provenance?: {
102
+ providerId?: string;
103
+ quality?: "reported" | "derived" | "estimated";
104
+ };
105
+ }
106
+
107
+ export interface ResolvedSeriesMarketTimeBasis {
108
+ kind: "market";
109
+ /** IANA timezone used to recognize one exchange-local trading day. */
110
+ timeZone: string;
111
+ /** Requested bar cadence when known; otherwise the chart derives it. */
112
+ cadenceMs?: number;
113
+ }
114
+
115
+ export interface ResolvedSeries {
116
+ id: string;
117
+ label: string;
118
+ color: string;
119
+ unit: string;
120
+ unitGroup: string;
121
+ nativeFrequency: SeriesPeriod;
122
+ /** Authored time basis retained for layout and cursor semantics. */
123
+ timestampMode?: SeriesTimestampMode;
124
+ dataShape: SeriesDataShape;
125
+ style: SeriesStyle;
126
+ transform: SeriesTransform;
127
+ axis: Exclude<SeriesAxis, "auto">;
128
+ panelId: string;
129
+ interpolation: SeriesInterpolation;
130
+ /** Present only for exchange-traded market observations. */
131
+ timeBasis?: ResolvedSeriesMarketTimeBasis;
132
+ points: TimeSeriesPoint[];
133
+ warning?: string;
134
+ }
135
+
136
+ export interface TimeSeriesFieldDefinition {
137
+ id: string;
138
+ label: string;
139
+ shortLabel: string;
140
+ sourceKind: ChartSeriesSource["kind"];
141
+ dataShape: SeriesDataShape;
142
+ unit: string;
143
+ unitGroup: string;
144
+ nativeFrequency: SeriesPeriod;
145
+ styles: SeriesStyle[];
146
+ defaultStyle: SeriesStyle;
147
+ transforms: SeriesTransform[];
148
+ defaultInterpolation: SeriesInterpolation;
149
+ }
150
+
151
+ export interface ChartResolutionResult {
152
+ series: ResolvedSeries[];
153
+ /** Series available to the legend, including hidden base series that can be restored. */
154
+ legendSeries?: ResolvedSeries[];
155
+ /** Loaded observations retained outside the visible window for interactive navigation. */
156
+ bufferedSeries?: ResolvedSeries[];
157
+ loading: boolean;
158
+ errors: string[];
159
+ warnings: string[];
160
+ /** Effective inclusive bounds used to clip the resolved chart data. */
161
+ viewport?: { start: Date; end: Date };
162
+ }
@@ -0,0 +1,166 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import {
3
+ ChartResolveCache,
4
+ resolveChartSpecData,
5
+ type ChartResolveOptions,
6
+ type ChartResolveSources,
7
+ } from "./resolve";
8
+ import type { ChartResolutionResult, ChartSpec } from "./types";
9
+ import {
10
+ LIVE_CHART_REFRESH_INTERVAL_MS,
11
+ liveChartQuoteTargetSignature,
12
+ subscribeToLiveChartQuotes,
13
+ } from "./live-quotes";
14
+ import type { Quote } from "../types/financials";
15
+
16
+ export interface UseChartResolutionResult extends ChartResolutionResult {
17
+ reload: () => void;
18
+ }
19
+
20
+ export interface UseChartResolutionOptions {
21
+ liveRefreshIntervalMs?: number;
22
+ autoViewport?: ChartResolveOptions["autoViewport"];
23
+ targetPointCount?: number;
24
+ }
25
+
26
+ const EMPTY_RESULT: ChartResolutionResult = {
27
+ series: [],
28
+ loading: false,
29
+ errors: [],
30
+ warnings: [],
31
+ };
32
+
33
+ function withQuoteOverrides(
34
+ sources: ChartResolveSources,
35
+ liveOverrides: ReadonlyMap<string, Quote>,
36
+ ): ChartResolveSources {
37
+ if (liveOverrides.size === 0) return sources;
38
+ if (!sources.quoteOverrides || sources.quoteOverrides.size === 0) {
39
+ return { ...sources, quoteOverrides: liveOverrides };
40
+ }
41
+ const combined = new Map(sources.quoteOverrides);
42
+ for (const [key, quote] of liveOverrides) combined.set(key, quote);
43
+ return { ...sources, quoteOverrides: combined };
44
+ }
45
+
46
+ export function useChartResolution(
47
+ spec: ChartSpec,
48
+ sources: ChartResolveSources,
49
+ options: UseChartResolutionOptions = {},
50
+ ): UseChartResolutionResult {
51
+ const [result, setResult] = useState<ChartResolutionResult>(EMPTY_RESULT);
52
+ const [revision, setRevision] = useState(0);
53
+ const generationRef = useRef(0);
54
+ const liveSubscriptionGenerationRef = useRef(0);
55
+ const liveQuoteOverridesRef = useRef<ReadonlyMap<string, Quote>>(new Map());
56
+ const resolveCacheRef = useRef(new ChartResolveCache());
57
+ const cacheIdentityRef = useRef<{
58
+ spec: ChartSpec | null;
59
+ sources: ChartResolveSources | null;
60
+ revision: number;
61
+ }>({ spec: null, sources: null, revision: -1 });
62
+ const autoViewportStart = options.autoViewport?.start.getTime();
63
+ const autoViewportEnd = options.autoViewport?.end.getTime();
64
+ const validAutoViewport = typeof autoViewportStart === "number"
65
+ && Number.isFinite(autoViewportStart)
66
+ && typeof autoViewportEnd === "number"
67
+ && Number.isFinite(autoViewportEnd)
68
+ && autoViewportStart <= autoViewportEnd
69
+ ? { start: new Date(autoViewportStart), end: new Date(autoViewportEnd) }
70
+ : null;
71
+ const adaptiveTargetPointCount = validAutoViewport ? options.targetPointCount : undefined;
72
+ const resolveOptions: ChartResolveOptions = {
73
+ autoViewport: validAutoViewport,
74
+ targetPointCount: adaptiveTargetPointCount,
75
+ };
76
+ const latestRequestRef = useRef({ spec, sources, options: resolveOptions });
77
+ latestRequestRef.current = { spec, sources, options: resolveOptions };
78
+ const reload = useCallback(() => setRevision((current) => current + 1), []);
79
+
80
+ useEffect(() => {
81
+ generationRef.current += 1;
82
+ const generation = generationRef.current;
83
+ const cacheIdentity = cacheIdentityRef.current;
84
+ const resetCache = cacheIdentity.spec !== spec
85
+ || cacheIdentity.sources !== sources
86
+ || cacheIdentity.revision !== revision;
87
+ if (resetCache) {
88
+ resolveCacheRef.current = new ChartResolveCache();
89
+ cacheIdentityRef.current = { spec, sources, revision };
90
+ }
91
+ const cache = resolveCacheRef.current;
92
+ setResult((current) => ({ ...current, loading: true, errors: [] }));
93
+ resolveChartSpecData(
94
+ spec,
95
+ withQuoteOverrides(sources, liveQuoteOverridesRef.current),
96
+ cache,
97
+ resolveOptions,
98
+ )
99
+ .then((next) => {
100
+ if (generationRef.current === generation) setResult(next);
101
+ })
102
+ .catch((error) => {
103
+ if (generationRef.current !== generation) return;
104
+ setResult({
105
+ series: [],
106
+ loading: false,
107
+ errors: [error instanceof Error ? error.message : String(error)],
108
+ warnings: [],
109
+ });
110
+ });
111
+ return () => {
112
+ if (generationRef.current === generation) generationRef.current += 1;
113
+ };
114
+ }, [adaptiveTargetPointCount, autoViewportEnd, autoViewportStart, revision, sources, spec]);
115
+
116
+ const liveTargetSignature = liveChartQuoteTargetSignature(spec);
117
+ const liveRefreshIntervalMs = options.liveRefreshIntervalMs ?? LIVE_CHART_REFRESH_INTERVAL_MS;
118
+ useEffect(() => {
119
+ const subscriptionGeneration = ++liveSubscriptionGenerationRef.current;
120
+ liveQuoteOverridesRef.current = new Map();
121
+ const dispose = subscribeToLiveChartQuotes({
122
+ spec,
123
+ dataProvider: sources.dataProvider,
124
+ refreshIntervalMs: liveRefreshIntervalMs,
125
+ onRefresh: async (quoteOverrides) => {
126
+ if (liveSubscriptionGenerationRef.current !== subscriptionGeneration) return;
127
+ liveQuoteOverridesRef.current = quoteOverrides;
128
+ const request = latestRequestRef.current;
129
+ const generation = ++generationRef.current;
130
+ try {
131
+ const next = await resolveChartSpecData(
132
+ request.spec,
133
+ withQuoteOverrides(request.sources, quoteOverrides),
134
+ resolveCacheRef.current,
135
+ request.options,
136
+ );
137
+ if (
138
+ liveSubscriptionGenerationRef.current === subscriptionGeneration
139
+ && generationRef.current === generation
140
+ ) {
141
+ setResult(next);
142
+ }
143
+ } catch (error) {
144
+ if (
145
+ liveSubscriptionGenerationRef.current !== subscriptionGeneration
146
+ || generationRef.current !== generation
147
+ ) return;
148
+ setResult((current) => ({
149
+ ...current,
150
+ loading: false,
151
+ errors: [error instanceof Error ? error.message : String(error)],
152
+ }));
153
+ }
154
+ },
155
+ });
156
+ return () => {
157
+ dispose();
158
+ if (liveSubscriptionGenerationRef.current === subscriptionGeneration) {
159
+ liveSubscriptionGenerationRef.current += 1;
160
+ liveQuoteOverridesRef.current = new Map();
161
+ }
162
+ };
163
+ }, [liveRefreshIntervalMs, liveTargetSignature, sources.dataProvider]);
164
+
165
+ return { ...result, reload };
166
+ }
@@ -1,12 +1,11 @@
1
1
  import type { Portfolio, Watchlist } from "./ticker";
2
+ import type { LanguagePreference } from "../i18n/languages";
2
3
 
3
- export const CURRENT_CONFIG_VERSION = 19;
4
+ export const CURRENT_CONFIG_VERSION = 20;
4
5
 
5
- type DefaultChartRenderMode = "area" | "line" | "candles" | "ohlc" | "hlc";
6
6
  type ChartRendererPreference = "auto" | "kitty" | "braille";
7
7
 
8
8
  export interface ChartPreferences {
9
- defaultRenderMode: DefaultChartRenderMode;
10
9
  renderer: ChartRendererPreference;
11
10
  }
12
11
 
@@ -135,21 +134,26 @@ export interface AppConfig {
135
134
  chartPreferences: ChartPreferences;
136
135
  valueFlashingEnabled: boolean;
137
136
  recentTickers: string[];
137
+ language?: LanguagePreference;
138
138
  onboardingComplete?: boolean;
139
139
  }
140
140
 
141
141
  export const TICKER_RESEARCH_PANE_ID = "ticker-research";
142
142
  export const LEGACY_TICKER_DETAIL_PANE_ID = "ticker-detail";
143
+ export const CHART_COMPOSER_PANE_ID = "chart-composer";
143
144
 
144
145
  export function normalizePaneId(paneId: string): string {
145
- return paneId === LEGACY_TICKER_DETAIL_PANE_ID ? TICKER_RESEARCH_PANE_ID : paneId;
146
+ if (paneId === LEGACY_TICKER_DETAIL_PANE_ID) return TICKER_RESEARCH_PANE_ID;
147
+ if (paneId === "comparison-chart" || paneId === "ticker-chart" || paneId === "fundamental-graph") {
148
+ return CHART_COMPOSER_PANE_ID;
149
+ }
150
+ return paneId;
146
151
  }
147
152
 
148
153
  const TICKER_PANE_IDS = new Set([
149
154
  TICKER_RESEARCH_PANE_ID,
150
155
  LEGACY_TICKER_DETAIL_PANE_ID,
151
156
  "financial-analysis",
152
- "ticker-chart",
153
157
  "quote-monitor",
154
158
  "ticker-news",
155
159
  "notes",
@@ -161,7 +165,6 @@ const TICKER_PANE_IDS = new Set([
161
165
  "corporate-actions",
162
166
  "earnings-estimates",
163
167
  "historical-prices",
164
- "fundamental-graph",
165
168
  "ibkr-trading",
166
169
  ]);
167
170
 
@@ -226,8 +229,6 @@ const DEFAULT_HOME_LAYOUT: LayoutConfig = {
226
229
  settings: {
227
230
  hideTabs: false,
228
231
  lockedTabId: "overview",
229
- chartRangePreset: "5Y",
230
- chartResolution: "auto",
231
232
  },
232
233
  binding: { kind: "follow", sourceInstanceId: "portfolio-list:main" },
233
234
  },
@@ -238,8 +239,6 @@ const DEFAULT_HOME_LAYOUT: LayoutConfig = {
238
239
  settings: {
239
240
  hideTabs: false,
240
241
  lockedTabId: "overview",
241
- chartRangePreset: "5Y",
242
- chartResolution: "auto",
243
242
  },
244
243
  binding: { kind: "fixed", symbol: "NVDA" },
245
244
  },
@@ -630,7 +629,6 @@ export function createDefaultConfig(dataDir: string): AppConfig {
630
629
  pluginConfig: {},
631
630
  theme: "amber",
632
631
  chartPreferences: {
633
- defaultRenderMode: "area",
634
632
  renderer: "auto",
635
633
  },
636
634
  valueFlashingEnabled: true,
@@ -7,6 +7,7 @@ export interface DesktopSharedStateSnapshot {
7
7
  focusedPaneId: string | null;
8
8
  activePanel: "left" | "right";
9
9
  statusBarVisible: boolean;
10
+ mainStateRevision?: number;
10
11
  layoutChanged?: boolean;
11
12
  }
12
13
 
@@ -216,6 +216,10 @@ export interface CompanyProfile {
216
216
 
217
217
  export interface FinancialStatement {
218
218
  date: string;
219
+ /** Earliest date on which the complete row was publicly available, when known. */
220
+ availableAt?: string;
221
+ /** Per-field publication dates used by point-in-time charts and calculations. */
222
+ fieldAvailability?: Record<string, string>;
219
223
  // Income Statement
220
224
  totalRevenue?: number;
221
225
  costOfRevenue?: number;
@@ -0,0 +1,17 @@
1
+ export interface LiveStreamResolveRequest {
2
+ provider: "youtube";
3
+ sourceId: string;
4
+ force?: boolean;
5
+ }
6
+
7
+ export interface ResolvedLiveStream {
8
+ provider: "youtube";
9
+ sourceId: string;
10
+ videoId: string;
11
+ title: string;
12
+ manifestUrl: string;
13
+ watchUrl: string;
14
+ posterUrl?: string;
15
+ resolvedAt: number;
16
+ expiresAt: number;
17
+ }
@@ -78,6 +78,8 @@ interface PaneSettingFieldBase {
78
78
  label: string;
79
79
  description?: string;
80
80
  storage?: "pane" | "plugin";
81
+ /** Keys in the same storage scope that should be reset when this value changes. */
82
+ clearOnChange?: string[];
81
83
  }
82
84
 
83
85
  interface PaneSettingToggleField extends PaneSettingFieldBase {
@@ -104,17 +106,43 @@ interface PaneSettingOrderedMultiSelectField extends PaneSettingFieldBase {
104
106
  options: PaneSettingOption[];
105
107
  }
106
108
 
109
+ export interface PaneSettingActionContext extends PaneSettingsContext {
110
+ surface: "pane-dialog" | "command-bar";
111
+ close(): void;
112
+ openCommandBar(query?: string): void;
113
+ notify(notification: AppNotificationRequest): void;
114
+ }
115
+
116
+ export type PaneSettingActionHandler = (
117
+ context: PaneSettingActionContext,
118
+ ) => void | Promise<void>;
119
+
120
+ export type PaneSettingActionField = Omit<PaneSettingFieldBase, "storage" | "clearOnChange"> & {
121
+ type: "action";
122
+ actionId: string;
123
+ actionLabel?: string;
124
+ disabled?: boolean;
125
+ action: PaneSettingActionHandler;
126
+ };
127
+
107
128
  export type PaneSettingField =
108
129
  | PaneSettingToggleField
109
130
  | PaneSettingTextField
110
131
  | PaneSettingSelectField
111
132
  | PaneSettingMultiSelectField
112
- | PaneSettingOrderedMultiSelectField;
133
+ | PaneSettingOrderedMultiSelectField
134
+ | PaneSettingActionField;
113
135
 
114
136
  export interface PaneSettingsDef {
115
137
  title?: string;
116
138
  values?: Record<string, unknown>;
117
139
  fields: PaneSettingField[];
140
+ applyValue?: (
141
+ settings: Record<string, unknown>,
142
+ field: PaneSettingField,
143
+ value: unknown,
144
+ context: PaneSettingsContext,
145
+ ) => Record<string, unknown> | Promise<Record<string, unknown>>;
118
146
  }
119
147
 
120
148
  export interface PaneTemplateContext {
@@ -172,9 +200,12 @@ export interface WizardStep {
172
200
  label: string;
173
201
  placeholder?: string;
174
202
  defaultValue?: string;
203
+ required?: boolean;
175
204
  type?: "text" | "password" | "info" | "select" | "number" | "textarea";
176
205
  options?: Array<{ label: string; value: string }>;
177
206
  dependsOn?: { key: string; value: string };
207
+ /** Later wizard values to clear when this selection changes from its default. */
208
+ clearOnChange?: string[];
178
209
  body?: string[];
179
210
  }
180
211