gloomberb 0.9.9 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (519) hide show
  1. package/README.md +41 -3
  2. package/README.zh-CN.md +294 -0
  3. package/package.json +10 -4
  4. package/patches/@opentui%2Fcore@0.3.2.patch +26 -69
  5. package/src/api-client/auth.ts +2 -0
  6. package/src/api-client/index.test.ts +4 -0
  7. package/src/api-client/types.ts +3 -0
  8. package/src/app/pane-runtime/pane-settings-runtime.tsx +1 -0
  9. package/src/app/pane-template-dialog-wizard.test.tsx +36 -0
  10. package/src/app/pane-template-dialog-wizard.tsx +10 -3
  11. package/src/app/runtime/desktop-deeplink.test.ts +15 -0
  12. package/src/app/runtime/desktop-deeplink.ts +21 -6
  13. package/src/app.tsx +40 -33
  14. package/src/capabilities/service-capabilities.ts +7 -2
  15. package/src/cli/commands/automation.test.ts +202 -0
  16. package/src/cli/commands/automation.ts +153 -36
  17. package/src/cli/commands/market.ts +1 -14
  18. package/src/cli/commands/system.ts +4 -1
  19. package/src/cli/context.ts +1 -1
  20. package/src/cli/desktop-pane-shot.ts +65 -4
  21. package/src/cli/entry.ts +1 -1
  22. package/src/cli/pane-functions/capabilities.ts +532 -0
  23. package/src/cli/pane-functions/catalog.ts +39 -4
  24. package/src/cli/pane-functions/data.test.ts +17 -0
  25. package/src/cli/pane-functions/data.ts +55 -13
  26. package/src/cli/pane-functions/discovery.ts +36 -9
  27. package/src/cli/pane-functions/index.test.ts +104 -1
  28. package/src/cli/pane-functions/index.ts +64 -5
  29. package/src/cli/pane-functions/options.ts +21 -6
  30. package/src/cli/pane-functions/report.ts +735 -72
  31. package/src/cli/pane-functions/resolver.ts +70 -6
  32. package/src/cli/pane-functions/screenshot.test.ts +431 -0
  33. package/src/cli/pane-functions/screenshot.ts +952 -8
  34. package/src/components/chart/composite/column-layout.ts +215 -0
  35. package/src/components/chart/composite/composite-chart.test.tsx +630 -0
  36. package/src/components/chart/composite/composite-chart.tsx +1148 -0
  37. package/src/components/chart/composite/format.test.ts +137 -0
  38. package/src/components/chart/composite/format.ts +135 -0
  39. package/src/components/chart/composite/index.ts +38 -0
  40. package/src/components/chart/composite/interactions.test.ts +109 -0
  41. package/src/components/chart/composite/interactions.ts +247 -0
  42. package/src/components/chart/composite/price-series.test.ts +39 -0
  43. package/src/components/chart/composite/price-series.ts +78 -0
  44. package/src/components/chart/composite/rasterizer.ts +359 -0
  45. package/src/components/chart/composite/renderers.test.ts +610 -0
  46. package/src/components/chart/composite/scene.test.ts +464 -0
  47. package/src/components/chart/composite/scene.ts +553 -0
  48. package/src/components/chart/composite/text-renderer.ts +285 -0
  49. package/src/components/chart/composite/time-scale.test.ts +88 -0
  50. package/src/components/chart/composite/time-scale.ts +217 -0
  51. package/src/components/chart/composite/types.ts +141 -0
  52. package/src/components/chart/core/data.ts +81 -5
  53. package/src/components/chart/core/index.test.ts +128 -43
  54. package/src/components/chart/core/resolution.ts +0 -6
  55. package/src/components/chart/native/chart-rasterizer.ts +1 -3
  56. package/src/components/chart/native/kitty/index.test.ts +99 -120
  57. package/src/components/chart/native/kitty/manager.ts +3 -0
  58. package/src/components/chart/native/kitty/protocol.ts +4 -1
  59. package/src/components/chart/native/raster/crosshair.ts +79 -46
  60. package/src/components/chart/native/raster/types.ts +2 -11
  61. package/src/components/chart/native/surface/manager.ts +48 -3
  62. package/src/components/chart/price-axis-labels.tsx +18 -4
  63. package/src/components/command-bar/commands/direct/index.ts +25 -0
  64. package/src/components/command-bar/commands/registry.ts +9 -0
  65. package/src/components/command-bar/helpers.test.ts +4 -1
  66. package/src/components/command-bar/list/view.tsx +18 -6
  67. package/src/components/command-bar/multi-select-picker.tsx +6 -5
  68. package/src/components/command-bar/pane-settings.test.ts +141 -0
  69. package/src/components/command-bar/pane-settings.ts +61 -1
  70. package/src/components/command-bar/panel/body.tsx +7 -6
  71. package/src/components/command-bar/routes/root/command-items.ts +4 -4
  72. package/src/components/command-bar/routes/root/results.ts +10 -1
  73. package/src/components/command-bar/routes/root/selection.test.ts +54 -0
  74. package/src/components/command-bar/routes/root/selection.ts +13 -0
  75. package/src/components/command-bar/routing/actions.ts +18 -2
  76. package/src/components/command-bar/routing/list-state.ts +2 -1
  77. package/src/components/command-bar/surface/index.test.tsx +36 -4
  78. package/src/components/command-bar/surface/pane.test.tsx +93 -0
  79. package/src/components/command-bar/theme-picker.tsx +2 -1
  80. package/src/components/command-bar/view-model.ts +19 -20
  81. package/src/components/command-bar/workflow/body.tsx +2 -1
  82. package/src/components/command-bar/workflow/field-row.tsx +3 -2
  83. package/src/components/command-bar/workflow/fields.ts +4 -2
  84. package/src/components/command-bar/workflow/ops.test.ts +161 -0
  85. package/src/components/command-bar/workflow/ops.ts +20 -50
  86. package/src/components/command-bar/workflow/route-actions.test.ts +34 -0
  87. package/src/components/command-bar/workflow/route-actions.ts +4 -0
  88. package/src/components/command-bar/workflow/types.ts +1 -0
  89. package/src/components/feed-data-table/stack-view.tsx +6 -5
  90. package/src/components/index.ts +13 -1
  91. package/src/components/layout/detached-pane-shell.tsx +2 -1
  92. package/src/components/layout/floating-pane.tsx +3 -0
  93. package/src/components/layout/header.tsx +15 -8
  94. package/src/components/layout/pane/footer/index.test.tsx +22 -3
  95. package/src/components/layout/pane/footer/index.tsx +3 -1
  96. package/src/components/layout/pane/footer/model.ts +9 -2
  97. package/src/components/layout/pane/header.tsx +4 -6
  98. package/src/components/layout/pane/index.tsx +3 -0
  99. package/src/components/layout/pane/sidebar.test.tsx +100 -0
  100. package/src/components/layout/pane/sidebar.tsx +239 -0
  101. package/src/components/layout/pane/title.ts +4 -3
  102. package/src/components/layout/shell/action-menu-overlay.tsx +5 -3
  103. package/src/components/layout/shell/drag/runtime.ts +2 -0
  104. package/src/components/layout/shell/menu.test.ts +19 -0
  105. package/src/components/layout/shell/menu.ts +8 -5
  106. package/src/components/layout/shell/native/pointer-runtime.ts +4 -1
  107. package/src/components/layout/shell/pane/layers.tsx +2 -2
  108. package/src/components/layout/status-bar.tsx +8 -7
  109. package/src/components/layout/window-controls.tsx +4 -3
  110. package/src/components/metric-treemap/index.tsx +3 -2
  111. package/src/components/onboarding/onboarding-steps.tsx +30 -29
  112. package/src/components/onboarding/onboarding-wizard.tsx +5 -4
  113. package/src/components/onboarding/portfolio-step/broker-fields-panel.tsx +15 -12
  114. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +17 -16
  115. package/src/components/onboarding/portfolio-step/broker-sync-panel.tsx +6 -5
  116. package/src/components/onboarding/portfolio-step/choose-panel.tsx +5 -4
  117. package/src/components/pane-settings-dialog/desktop.tsx +25 -3
  118. package/src/components/pane-settings-dialog/field-dialogs.tsx +5 -4
  119. package/src/components/pane-settings-dialog/tui.tsx +11 -9
  120. package/src/components/pane-settings-dialog/value.ts +8 -5
  121. package/src/components/pane-settings-dialog.test.tsx +235 -0
  122. package/src/components/pane-settings-dialog.tsx +24 -0
  123. package/src/components/pane-template-wizard.tsx +6 -2
  124. package/src/components/ticker/list-table-view.tsx +3 -2
  125. package/src/components/ui/button.tsx +3 -1
  126. package/src/components/ui/choice-dialog.tsx +6 -1
  127. package/src/components/ui/confirm-dialog.tsx +3 -2
  128. package/src/components/ui/fields.tsx +10 -1
  129. package/src/components/ui/frame.tsx +4 -1
  130. package/src/components/ui/index.ts +5 -0
  131. package/src/components/ui/inline-quick-add.tsx +104 -0
  132. package/src/components/ui/list-view.tsx +5 -4
  133. package/src/components/ui/loading.tsx +2 -1
  134. package/src/components/ui/multi-select/dialog.tsx +188 -44
  135. package/src/components/ui/popover.tsx +10 -0
  136. package/src/components/ui/shortcut-hint.tsx +2 -0
  137. package/src/components/ui/status.tsx +4 -3
  138. package/src/components/ui/tabs.tsx +19 -8
  139. package/src/components/ui/toggle.tsx +44 -3
  140. package/src/components/ui/ui.test.tsx +67 -5
  141. package/src/core/app-services.ts +4 -6
  142. package/src/core/state/session-persistence.ts +3 -2
  143. package/src/data/config/chart-settings.ts +712 -0
  144. package/src/data/config/layout.ts +24 -5
  145. package/src/data/config/store/index.test.ts +377 -14
  146. package/src/data/config/store/normalize.ts +79 -65
  147. package/src/data/fred-series.ts +137 -0
  148. package/src/i18n/index.test.ts +114 -0
  149. package/src/i18n/index.ts +147 -0
  150. package/src/i18n/ja.ts +1013 -0
  151. package/src/i18n/ko.ts +1011 -0
  152. package/src/i18n/languages.test.ts +31 -0
  153. package/src/i18n/languages.ts +72 -0
  154. package/src/i18n/zh-cn.ts +1013 -0
  155. package/src/i18n/zh-tw.ts +1013 -0
  156. package/src/plugins/builtin/account-management/footer.ts +3 -2
  157. package/src/plugins/builtin/account-management/form-components.tsx +3 -2
  158. package/src/plugins/builtin/account-management/keyboard.ts +6 -0
  159. package/src/plugins/builtin/account-management/model.ts +25 -21
  160. package/src/plugins/builtin/account-management/navigation.test.ts +27 -0
  161. package/src/plugins/builtin/account-management/navigation.ts +24 -0
  162. package/src/plugins/builtin/account-management/pane.tsx +120 -85
  163. package/src/plugins/builtin/account-management/password-dialog.tsx +10 -9
  164. package/src/plugins/builtin/ai/agent-history.ts +139 -0
  165. package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +133 -51
  166. package/src/plugins/builtin/ai/ask-ai.test.tsx +83 -22
  167. package/src/plugins/builtin/ai/index.test.ts +302 -0
  168. package/src/plugins/builtin/ai/index.tsx +374 -40
  169. package/src/plugins/builtin/ai/pane-settings.test.ts +155 -0
  170. package/src/plugins/builtin/ai/pane-settings.ts +319 -0
  171. package/src/plugins/builtin/ai/pi/credential-store.test.ts +80 -0
  172. package/src/plugins/builtin/ai/pi/credential-store.ts +138 -0
  173. package/src/plugins/builtin/ai/pi/file-store.ts +138 -0
  174. package/src/plugins/builtin/ai/pi/host.test.ts +500 -0
  175. package/src/plugins/builtin/ai/pi/host.ts +564 -0
  176. package/src/plugins/builtin/ai/pi/index.ts +5 -0
  177. package/src/plugins/builtin/ai/pi/models-store.test.ts +44 -0
  178. package/src/plugins/builtin/ai/pi/models-store.ts +141 -0
  179. package/src/plugins/builtin/ai/pi/providers.ts +68 -0
  180. package/src/plugins/builtin/ai/pi/runtime.test.ts +240 -0
  181. package/src/plugins/builtin/ai/pi/runtime.ts +635 -0
  182. package/src/plugins/builtin/ai/providers.test.ts +68 -0
  183. package/src/plugins/builtin/ai/providers.ts +163 -38
  184. package/src/plugins/builtin/ai/runner-selection.test.ts +174 -0
  185. package/src/plugins/builtin/ai/runner-selection.ts +193 -0
  186. package/src/plugins/builtin/ai/runner-selector.tsx +153 -0
  187. package/src/plugins/builtin/ai/runner.test.ts +198 -0
  188. package/src/plugins/builtin/ai/runner.ts +276 -73
  189. package/src/plugins/builtin/ai/screener/contract.test.ts +26 -73
  190. package/src/plugins/builtin/ai/screener/contract.ts +14 -52
  191. package/src/plugins/builtin/ai/screener/editor-runtime.ts +55 -5
  192. package/src/plugins/builtin/ai/screener/editor.tsx +42 -36
  193. package/src/plugins/builtin/ai/screener/footer.ts +82 -67
  194. package/src/plugins/builtin/ai/screener/keyboard.ts +27 -11
  195. package/src/plugins/builtin/ai/screener/model.test.ts +24 -0
  196. package/src/plugins/builtin/ai/screener/model.ts +9 -3
  197. package/src/plugins/builtin/ai/screener/pane.test.tsx +142 -55
  198. package/src/plugins/builtin/ai/screener/pane.tsx +95 -97
  199. package/src/plugins/builtin/ai/screener/results-view.tsx +8 -41
  200. package/src/plugins/builtin/ai/screener/runner.ts +41 -25
  201. package/src/plugins/builtin/ai/screener/tabs-bar.tsx +2 -2
  202. package/src/plugins/builtin/ai/use-runtime-providers.ts +28 -0
  203. package/src/plugins/builtin/ai/workspace/model.test.ts +227 -0
  204. package/src/plugins/builtin/ai/workspace/model.ts +308 -0
  205. package/src/plugins/builtin/ai/workspace/pane.test.tsx +197 -0
  206. package/src/plugins/builtin/ai/workspace/pane.tsx +896 -0
  207. package/src/plugins/builtin/analytics/index.test.tsx +3 -3
  208. package/src/plugins/builtin/analytics/index.tsx +3 -8
  209. package/src/plugins/builtin/broker-manager/detail.tsx +25 -24
  210. package/src/plugins/builtin/broker-manager/index.test.tsx +1 -1
  211. package/src/plugins/builtin/broker-manager/index.tsx +9 -11
  212. package/src/plugins/builtin/broker-manager/model.ts +11 -10
  213. package/src/plugins/builtin/broker-manager/pane-actions.tsx +22 -21
  214. package/src/plugins/builtin/broker-manager/table.ts +9 -8
  215. package/src/plugins/builtin/changelog/index.tsx +3 -8
  216. package/src/plugins/builtin/chart-composer/chart-spec.ts +89 -0
  217. package/src/plugins/builtin/chart-composer/cli-options.ts +98 -0
  218. package/src/plugins/builtin/chart-composer/date-window-dialog.tsx +120 -0
  219. package/src/plugins/builtin/chart-composer/editor.test.tsx +332 -0
  220. package/src/plugins/builtin/chart-composer/editor.tsx +1107 -0
  221. package/src/plugins/builtin/chart-composer/index.tsx +228 -0
  222. package/src/plugins/builtin/chart-composer/pane.tsx +645 -0
  223. package/src/plugins/builtin/chart-composer/presets.test.ts +561 -0
  224. package/src/plugins/builtin/chart-composer/presets.ts +759 -0
  225. package/src/plugins/builtin/chart-composer/quick-add.test.tsx +245 -0
  226. package/src/plugins/builtin/chart-composer/quick-add.tsx +409 -0
  227. package/src/plugins/builtin/chart-composer/semantic.ts +86 -0
  228. package/src/plugins/builtin/chart-composer/series-catalog.test.ts +65 -0
  229. package/src/plugins/builtin/chart-composer/series-catalog.ts +295 -0
  230. package/src/plugins/builtin/chart-composer/settings.test.ts +163 -0
  231. package/src/plugins/builtin/chart-composer/settings.ts +397 -0
  232. package/src/plugins/builtin/chart-composer/shortcuts.test.ts +44 -0
  233. package/src/plugins/builtin/chart-composer/shortcuts.ts +35 -0
  234. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +103 -0
  235. package/src/plugins/builtin/chat/channel-labels.ts +5 -4
  236. package/src/plugins/builtin/chat/channels.ts +12 -11
  237. package/src/plugins/builtin/chat/chat.test.tsx +32 -0
  238. package/src/plugins/builtin/chat/content/composer-runtime.ts +6 -1
  239. package/src/plugins/builtin/chat/content/composer.tsx +9 -8
  240. package/src/plugins/builtin/chat/content/index.tsx +36 -1
  241. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +8 -7
  242. package/src/plugins/builtin/chat/content/transcript.tsx +9 -3
  243. package/src/plugins/builtin/chat/controller/utils.ts +10 -9
  244. package/src/plugins/builtin/chat/message/desktop.tsx +17 -10
  245. package/src/plugins/builtin/chat/message/profile-popover.test.tsx +122 -3
  246. package/src/plugins/builtin/chat/message/profile-popover.tsx +38 -1
  247. package/src/plugins/builtin/chat/message/terminal.tsx +19 -10
  248. package/src/plugins/builtin/chat/pane-state.test.ts +31 -0
  249. package/src/plugins/builtin/chat/pane-state.ts +30 -0
  250. package/src/plugins/builtin/chat/pane.tsx +27 -4
  251. package/src/plugins/builtin/chat/profile-popover.ts +62 -5
  252. package/src/plugins/builtin/chat/sidebar.test.tsx +4 -7
  253. package/src/plugins/builtin/chat/sidebar.tsx +107 -168
  254. package/src/plugins/builtin/chat/test-harness.tsx +6 -0
  255. package/src/plugins/builtin/cloud/auth-actions.tsx +5 -4
  256. package/src/plugins/builtin/cloud/plugin.tsx +150 -127
  257. package/src/plugins/builtin/composite-plugins.ts +71 -0
  258. package/src/plugins/builtin/correlation/index.test.tsx +4 -4
  259. package/src/plugins/builtin/correlation/index.tsx +3 -8
  260. package/src/plugins/builtin/earnings/index.tsx +37 -21
  261. package/src/plugins/builtin/earnings/model.test.ts +52 -0
  262. package/src/plugins/builtin/earnings/model.ts +32 -0
  263. package/src/plugins/builtin/econ/detail-view.tsx +17 -4
  264. package/src/plugins/builtin/econ/fred-cache.test.ts +57 -11
  265. package/src/plugins/builtin/econ/index.tsx +16 -20
  266. package/src/plugins/builtin/fear-greed/index.tsx +2 -8
  267. package/src/plugins/builtin/fx-matrix/index.tsx +3 -8
  268. package/src/plugins/builtin/help/components.tsx +5 -4
  269. package/src/plugins/builtin/help/index.tsx +14 -17
  270. package/src/plugins/builtin/holders/index.tsx +2 -8
  271. package/src/plugins/builtin/insider/index.tsx +2 -7
  272. package/src/plugins/builtin/kelly-sizer/index.test.tsx +2 -2
  273. package/src/plugins/builtin/kelly-sizer/index.tsx +3 -7
  274. package/src/plugins/builtin/kelly-sizer/test-support.tsx +3 -3
  275. package/src/plugins/builtin/layout-manager/index.tsx +3 -7
  276. package/src/plugins/builtin/market-heatmap/index.tsx +3 -8
  277. package/src/plugins/builtin/market-movers/index.tsx +3 -8
  278. package/src/plugins/builtin/news/index.tsx +12 -16
  279. package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +3 -3
  280. package/src/plugins/builtin/news/wire/index.tsx +83 -73
  281. package/src/plugins/builtin/options/index.tsx +2 -8
  282. package/src/plugins/builtin/plugin-module.test.ts +89 -0
  283. package/src/plugins/builtin/plugin-module.ts +116 -0
  284. package/src/plugins/builtin/portfolio-list/header.tsx +4 -3
  285. package/src/plugins/builtin/portfolio-list/index.test.ts +17 -0
  286. package/src/plugins/builtin/portfolio-list/index.tsx +3 -5
  287. package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +3 -3
  288. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +53 -53
  289. package/src/plugins/builtin/portfolio-list/settings.ts +5 -3
  290. package/src/plugins/builtin/portfolio-list/summary/index.tsx +4 -3
  291. package/src/plugins/builtin/research/index.tsx +2 -8
  292. package/src/plugins/builtin/sec/index.tsx +2 -8
  293. package/src/plugins/builtin/sectors/index.tsx +3 -8
  294. package/src/plugins/builtin/thirteenf/index.tsx +3 -8
  295. package/src/plugins/builtin/thirteenf/pane.test.tsx +1 -1
  296. package/src/plugins/builtin/ticker-detail/chart-tab-switch.test.tsx +49 -23
  297. package/src/plugins/builtin/ticker-detail/index.test.tsx +15 -136
  298. package/src/plugins/builtin/ticker-detail/index.tsx +2 -93
  299. package/src/plugins/builtin/ticker-detail/overview/components.tsx +6 -5
  300. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +41 -17
  301. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -3
  302. package/src/plugins/builtin/ticker-detail/quote-monitor/index.test.tsx +1 -1
  303. package/src/plugins/builtin/ticker-detail/settings.ts +3 -50
  304. package/src/plugins/builtin/ticker-research-backend-plugin.ts +17 -0
  305. package/src/plugins/builtin/ticker-research-plugin.ts +27 -0
  306. package/src/plugins/builtin/tv/channels.ts +27 -0
  307. package/src/plugins/builtin/tv/index.tsx +40 -0
  308. package/src/plugins/builtin/tv/pane.tsx +286 -0
  309. package/src/plugins/builtin/tv/youtube-stream.ts +100 -0
  310. package/src/plugins/builtin/world-indices/index.tsx +3 -8
  311. package/src/plugins/builtin/yield-curve/index.tsx +8 -8
  312. package/src/plugins/catalog-backend.test.ts +21 -0
  313. package/src/plugins/catalog-backend.ts +42 -0
  314. package/src/plugins/catalog-ui.ts +6 -14
  315. package/src/plugins/ibkr/instance-selection.test.ts +0 -1
  316. package/src/plugins/ownership.ts +143 -0
  317. package/src/plugins/prediction-markets/backend-plugin.ts +19 -0
  318. package/src/plugins/prediction-markets/chart.test.tsx +7 -5
  319. package/src/plugins/prediction-markets/chart.tsx +25 -7
  320. package/src/plugins/registry/index.test.ts +51 -0
  321. package/src/plugins/registry/index.ts +8 -0
  322. package/src/plugins/registry/pane-settings.ts +1 -1
  323. package/src/plugins/runtime/index.test.tsx +2 -2
  324. package/src/remote/app-host.tsx +16 -3
  325. package/src/remote/controller.test.ts +102 -1
  326. package/src/remote/controller.ts +122 -0
  327. package/src/remote/types.ts +11 -0
  328. package/src/renderers/electrobun/bun/core-capabilities.ts +123 -27
  329. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +5 -1
  330. package/src/renderers/electrobun/bun/desktop/initialization.ts +5 -1
  331. package/src/renderers/electrobun/bun/desktop/media.ts +15 -0
  332. package/src/renderers/electrobun/bun/desktop/window-style.ts +4 -1
  333. package/src/renderers/electrobun/bun/desktop/workspace/index.test.ts +32 -1
  334. package/src/renderers/electrobun/bun/desktop/workspace/index.ts +10 -0
  335. package/src/renderers/electrobun/bun/index.ts +5 -0
  336. package/src/renderers/electrobun/bun/tui-entry.ts +1 -6
  337. package/src/renderers/electrobun/view/ai-host.ts +124 -19
  338. package/src/renderers/electrobun/view/backend-rpc.ts +4 -0
  339. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +151 -39
  340. package/src/renderers/electrobun/view/desktop/popover.tsx +166 -0
  341. package/src/renderers/electrobun/view/dialog-host.test.ts +25 -0
  342. package/src/renderers/electrobun/view/dialog-host.tsx +75 -12
  343. package/src/renderers/electrobun/view/host/box.tsx +5 -0
  344. package/src/renderers/electrobun/view/host/chart-surface.tsx +10 -9
  345. package/src/renderers/electrobun/view/host/input.tsx +9 -19
  346. package/src/renderers/electrobun/view/host/media-surface.tsx +170 -0
  347. package/src/renderers/electrobun/view/host/mouse.ts +1 -0
  348. package/src/renderers/electrobun/view/host/style.ts +1 -1
  349. package/src/renderers/electrobun/view/host/tabs.tsx +4 -3
  350. package/src/renderers/electrobun/view/main.tsx +10 -3
  351. package/src/renderers/electrobun/view/styles.css +17 -0
  352. package/src/renderers/electrobun/view/ui-host.tsx +7 -0
  353. package/src/renderers/opentui/chart-surface.test.tsx +52 -6
  354. package/src/renderers/opentui/chart-surface.tsx +95 -11
  355. package/src/renderers/opentui/dialog-host.tsx +36 -5
  356. package/src/renderers/opentui/host.tsx +39 -0
  357. package/src/renderers/opentui/start.tsx +30 -0
  358. package/src/renderers/opentui/ui-host.tsx +2 -0
  359. package/src/sources/provider-router/index.test.ts +43 -0
  360. package/src/sources/provider-router/index.ts +4 -0
  361. package/src/sources/provider-router/supplemental.ts +46 -0
  362. package/src/sources/sec-edgar.test.ts +203 -2
  363. package/src/sources/sec-edgar.ts +70 -15
  364. package/src/state/app/context/index.tsx +13 -1
  365. package/src/state/app/context/selector.test.tsx +62 -1
  366. package/src/state/session-persistence.test.ts +13 -2
  367. package/src/sync/controller.test.ts +190 -3
  368. package/src/sync/controller.ts +76 -45
  369. package/src/sync/core-contributors.test.ts +121 -0
  370. package/src/sync/core-contributors.ts +104 -27
  371. package/src/sync/react.ts +6 -8
  372. package/src/sync/types.ts +3 -0
  373. package/src/time-series/alignment.ts +158 -0
  374. package/src/time-series/economic.ts +49 -0
  375. package/src/time-series/field-catalog.ts +306 -0
  376. package/src/time-series/fundamentals.test.ts +505 -0
  377. package/src/time-series/fundamentals.ts +842 -0
  378. package/src/time-series/hooks.ts +45 -0
  379. package/src/time-series/index.ts +14 -0
  380. package/src/time-series/live-quotes.test.ts +219 -0
  381. package/src/time-series/live-quotes.ts +203 -0
  382. package/src/time-series/market-resolution.ts +53 -0
  383. package/src/time-series/market.ts +125 -0
  384. package/src/time-series/reporting.ts +98 -0
  385. package/src/time-series/resolve.test.ts +1281 -0
  386. package/src/time-series/resolve.ts +783 -0
  387. package/src/time-series/spec-studies.test.ts +400 -0
  388. package/src/time-series/spec.ts +487 -0
  389. package/src/time-series/studies.ts +563 -0
  390. package/src/time-series/transforms.test.ts +105 -0
  391. package/src/time-series/transforms.ts +152 -0
  392. package/src/time-series/types.ts +162 -0
  393. package/src/time-series/use-chart-resolution.ts +166 -0
  394. package/src/types/config.ts +9 -11
  395. package/src/types/desktop-window.ts +1 -0
  396. package/src/types/financials.ts +4 -0
  397. package/src/types/media.ts +17 -0
  398. package/src/types/plugin.ts +32 -1
  399. package/src/ui/context-menu.tsx +15 -3
  400. package/src/ui/dialog.tsx +14 -2
  401. package/src/ui/host.tsx +40 -1
  402. package/src/ui/index.tsx +9 -0
  403. package/src/utils/exchanges.test.ts +13 -1
  404. package/src/utils/exchanges.ts +10 -0
  405. package/src/utils/financial-statements.test.ts +138 -0
  406. package/src/utils/financial-statements.ts +115 -8
  407. package/src/utils/format.test.ts +11 -1
  408. package/src/utils/format.ts +7 -0
  409. package/src/version.ts +1 -1
  410. package/src/components/chart/bar-chart-renderer.test.ts +0 -233
  411. package/src/components/chart/bar-chart-renderer.ts +0 -445
  412. package/src/components/chart/comparison/data.test.ts +0 -159
  413. package/src/components/chart/comparison/data.ts +0 -302
  414. package/src/components/chart/comparison/renderer.test.ts +0 -187
  415. package/src/components/chart/comparison/renderer.ts +0 -282
  416. package/src/components/chart/comparison/scene.ts +0 -178
  417. package/src/components/chart/comparison-stock-chart/axis-width.ts +0 -99
  418. package/src/components/chart/comparison-stock-chart/canvas.ts +0 -148
  419. package/src/components/chart/comparison-stock-chart/controls.ts +0 -252
  420. package/src/components/chart/comparison-stock-chart/helpers.ts +0 -101
  421. package/src/components/chart/comparison-stock-chart/interactions.ts +0 -208
  422. package/src/components/chart/comparison-stock-chart/layout-metrics.ts +0 -62
  423. package/src/components/chart/comparison-stock-chart/layout.tsx +0 -155
  424. package/src/components/chart/comparison-stock-chart/legend.tsx +0 -74
  425. package/src/components/chart/comparison-stock-chart/native-surfaces.ts +0 -89
  426. package/src/components/chart/comparison-stock-chart/render-data.ts +0 -227
  427. package/src/components/chart/comparison-stock-chart/render-output.ts +0 -140
  428. package/src/components/chart/comparison-stock-chart/selection-runtime.ts +0 -128
  429. package/src/components/chart/comparison-stock-chart/sources.ts +0 -51
  430. package/src/components/chart/comparison-stock-chart/surface-runtime.ts +0 -159
  431. package/src/components/chart/comparison-stock-chart/toolbar.tsx +0 -117
  432. package/src/components/chart/comparison-stock-chart/types.ts +0 -22
  433. package/src/components/chart/comparison-stock-chart/view.tsx +0 -572
  434. package/src/components/chart/comparison-stock-chart/viewport-runtime.ts +0 -239
  435. package/src/components/chart/comparison-stock-chart.tsx +0 -24
  436. package/src/components/chart/core/axis-measure.ts +0 -33
  437. package/src/components/chart/core/controller.test.ts +0 -157
  438. package/src/components/chart/core/controller.ts +0 -349
  439. package/src/components/chart/core/indicator-selector.tsx +0 -75
  440. package/src/components/chart/core/keyboard.ts +0 -24
  441. package/src/components/chart/core/pane-settings.test.ts +0 -52
  442. package/src/components/chart/core/pane-settings.ts +0 -141
  443. package/src/components/chart/core/scroll.test.ts +0 -64
  444. package/src/components/chart/core/scroll.ts +0 -82
  445. package/src/components/chart/core/window-viewport.test.ts +0 -69
  446. package/src/components/chart/core/window-viewport.ts +0 -162
  447. package/src/components/chart/display-cursor.ts +0 -153
  448. package/src/components/chart/indicators/bands.test.ts +0 -36
  449. package/src/components/chart/indicators/bands.ts +0 -29
  450. package/src/components/chart/indicators/moving-averages.test.ts +0 -39
  451. package/src/components/chart/indicators/moving-averages.ts +0 -42
  452. package/src/components/chart/indicators/options.test.ts +0 -32
  453. package/src/components/chart/indicators/options.ts +0 -107
  454. package/src/components/chart/indicators/oscillators.test.ts +0 -58
  455. package/src/components/chart/indicators/oscillators.ts +0 -92
  456. package/src/components/chart/native/canvas-bitmaps.ts +0 -140
  457. package/src/components/chart/native/raster/comparison-chart.ts +0 -162
  458. package/src/components/chart/native/surface/hooks.ts +0 -144
  459. package/src/components/chart/native/surface/rendering.ts +0 -211
  460. package/src/components/chart/static/bar-chart-surface.test.tsx +0 -101
  461. package/src/components/chart/static/bar-chart-surface.tsx +0 -292
  462. package/src/components/chart/stock-chart/auto.ts +0 -216
  463. package/src/components/chart/stock-chart/axis-width.ts +0 -146
  464. package/src/components/chart/stock-chart/bitmaps.ts +0 -22
  465. package/src/components/chart/stock-chart/canvas.ts +0 -202
  466. package/src/components/chart/stock-chart/controls.tsx +0 -149
  467. package/src/components/chart/stock-chart/cursor.ts +0 -60
  468. package/src/components/chart/stock-chart/data-runtime.ts +0 -128
  469. package/src/components/chart/stock-chart/footer.test.ts +0 -65
  470. package/src/components/chart/stock-chart/footer.ts +0 -303
  471. package/src/components/chart/stock-chart/geometry.ts +0 -160
  472. package/src/components/chart/stock-chart/index.test.ts +0 -532
  473. package/src/components/chart/stock-chart/index.tsx +0 -29
  474. package/src/components/chart/stock-chart/indicators.ts +0 -226
  475. package/src/components/chart/stock-chart/interaction-runtime.ts +0 -44
  476. package/src/components/chart/stock-chart/interactions.ts +0 -300
  477. package/src/components/chart/stock-chart/keyboard.ts +0 -373
  478. package/src/components/chart/stock-chart/layout-metrics.ts +0 -62
  479. package/src/components/chart/stock-chart/layout.tsx +0 -177
  480. package/src/components/chart/stock-chart/native-surfaces.ts +0 -121
  481. package/src/components/chart/stock-chart/presentation.ts +0 -67
  482. package/src/components/chart/stock-chart/renderer-switch.test.tsx +0 -263
  483. package/src/components/chart/stock-chart/rendering/bounds.ts +0 -116
  484. package/src/components/chart/stock-chart/rendering/candidates.ts +0 -145
  485. package/src/components/chart/stock-chart/rendering/data.ts +0 -370
  486. package/src/components/chart/stock-chart/rendering/invalidation.ts +0 -68
  487. package/src/components/chart/stock-chart/rendering/output.ts +0 -304
  488. package/src/components/chart/stock-chart/rendering/planning.ts +0 -122
  489. package/src/components/chart/stock-chart/rendering/projection.test.tsx +0 -98
  490. package/src/components/chart/stock-chart/rendering/projection.ts +0 -112
  491. package/src/components/chart/stock-chart/rendering/state.test.ts +0 -70
  492. package/src/components/chart/stock-chart/rendering/state.ts +0 -283
  493. package/src/components/chart/stock-chart/requests.ts +0 -337
  494. package/src/components/chart/stock-chart/resolution-support.ts +0 -96
  495. package/src/components/chart/stock-chart/resolved/runtime.ts +0 -289
  496. package/src/components/chart/stock-chart/resolved.tsx +0 -359
  497. package/src/components/chart/stock-chart/settings.ts +0 -81
  498. package/src/components/chart/stock-chart/surface-runtime.ts +0 -62
  499. package/src/components/chart/stock-chart/types.ts +0 -25
  500. package/src/components/chart/stock-chart/view.tsx +0 -155
  501. package/src/components/chart/stock-chart/viewport/effects.ts +0 -293
  502. package/src/components/chart/stock-chart/viewport/index.ts +0 -144
  503. package/src/components/chart/stock-chart/viewport/runtime.ts +0 -375
  504. package/src/components/chart/time-axis-label.tsx +0 -175
  505. package/src/plugins/builtin/ai/gloomberb-cli.ts +0 -71
  506. package/src/plugins/builtin/ai/screener/action-bar.tsx +0 -58
  507. package/src/plugins/builtin/comparison-chart/index.test.tsx +0 -848
  508. package/src/plugins/builtin/comparison-chart/index.tsx +0 -194
  509. package/src/plugins/builtin/econ/fred-cache.ts +0 -106
  510. package/src/plugins/builtin/plugin-groups.ts +0 -130
  511. package/src/plugins/builtin/ticker-detail/chart-pane.tsx +0 -91
  512. package/src/plugins/builtin/ticker-detail/chart-tab.tsx +0 -100
  513. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/content.tsx +0 -296
  514. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/data.ts +0 -62
  515. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/model.ts +0 -608
  516. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/settings.ts +0 -65
  517. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/types.ts +0 -58
  518. package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph.tsx +0 -138
  519. package/src/plugins/builtin/ticker-detail/data-panes.test.ts +0 -220
@@ -1,8 +1,9 @@
1
1
  import { Box, ScrollBox, Text } from "../../ui";
2
2
  import { TextAttributes, type ScrollBoxRenderable } from "../../ui";
3
3
  import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
4
+ import { t } from "../../i18n";
5
+ import { displayWidth, formatTimeAgo } from "../../utils/format";
4
6
  import { colors } from "../../theme/colors";
5
- import { formatTimeAgo } from "../../utils/format";
6
7
  import { isPlainKey } from "../../utils/keyboard";
7
8
  import { toTimestampMillis } from "../../utils/timestamp";
8
9
  import { DataTableStackView } from "../data-table/stack-view";
@@ -111,7 +112,7 @@ function buildColumns(
111
112
  const timeWidth = 8;
112
113
  const sourceWidth = Math.min(
113
114
  Math.max(
114
- sourceLabel.length,
115
+ displayWidth(sourceLabel),
115
116
  ...items.map((item) => item.eyebrow?.length ?? 0),
116
117
  6,
117
118
  ),
@@ -123,7 +124,7 @@ function buildColumns(
123
124
  );
124
125
 
125
126
  return [
126
- { id: "time", label: "Time", width: timeWidth, align: "left" },
127
+ { id: "time", label: t("Time"), width: timeWidth, align: "left" },
127
128
  { id: "source", label: sourceLabel, width: sourceWidth, align: "left" },
128
129
  { id: "title", label: titleLabel, width: titleWidth, align: "left" },
129
130
  ];
@@ -155,7 +156,7 @@ export function FeedDataTableStackView({
155
156
  const detailScrollRef = useRef<ScrollBoxRenderable>(null);
156
157
  const detailTextWidth = Math.max(width - 2, 12);
157
158
  const columns = useMemo(
158
- () => buildColumns(width, sourceLabel, titleLabel, items),
159
+ () => buildColumns(width, t(sourceLabel), t(titleLabel), items),
159
160
  [items, sourceLabel, titleLabel, width],
160
161
  );
161
162
  const sortedRows = useMemo(() => {
@@ -356,7 +357,7 @@ export function FeedDataTableStackView({
356
357
  )}
357
358
  getItemKey={(row) => row.item.id}
358
359
  renderCell={renderCell}
359
- emptyStateTitle={emptyStateTitle}
360
+ emptyStateTitle={t(emptyStateTitle)}
360
361
  emptyStateHint={emptyStateHint}
361
362
  showHorizontalScrollbar={false}
362
363
  />
@@ -22,7 +22,19 @@ export type { FeedDataTableItem } from "./feed-data-table/stack-view";
22
22
  export { activeStackIndex, sortStackItems } from "./feed-stack-controller";
23
23
  export type { StackSortPreference } from "./feed-stack-controller";
24
24
  export { PaneFooterScope, usePaneFooter } from "./layout/pane/footer";
25
- export type { PaneFooterSegment, PaneHint } from "./layout/pane/footer";
25
+ export type { PaneFooterPressEvent, PaneFooterSegment, PaneHint } from "./layout/pane/footer";
26
+ export {
27
+ getPaneSidebarWidth,
28
+ PaneSidebar,
29
+ PaneSidebarAction,
30
+ PaneSidebarRow,
31
+ shouldShowPaneSidebar,
32
+ } from "./layout/pane/sidebar";
33
+ export type {
34
+ PaneSidebarActionRenderState,
35
+ PaneSidebarRenderState,
36
+ PaneSidebarRowRenderState,
37
+ } from "./layout/pane/sidebar";
26
38
  export { useExternalLinkFooter } from "./use-external-link-footer";
27
39
  export { Button } from "./ui/button";
28
40
  export { Checkbox } from "./ui/checkbox";
@@ -1,5 +1,6 @@
1
1
  import { Box, Text, useRendererHost, useUiCapabilities } from "../../ui";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { t } from "../../i18n";
3
4
  import { useShortcut, useViewport } from "../../react/input";
4
5
  import { useAppDispatch, useAppSelector } from "../../state/app/context";
5
6
  import type { DesktopWindowBridge } from "../../types/desktop-window";
@@ -121,7 +122,7 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
121
122
  if (!instance || !paneDef) {
122
123
  return (
123
124
  <Box flexGrow={1} alignItems="center" justifyContent="center" backgroundColor={colors.bg}>
124
- <Text fg={colors.textDim}>Pane unavailable.</Text>
125
+ <Text fg={colors.textDim}>{t("Pane unavailable.")}</Text>
125
126
  </Box>
126
127
  );
127
128
  }
@@ -18,6 +18,7 @@ interface FloatingPaneWrapperProps {
18
18
  windowModeSelected?: boolean;
19
19
  showActions?: boolean;
20
20
  onMouseDown?: (event: any) => void;
21
+ onMouseDownCapture?: (event: any) => void;
21
22
  onHeaderMouseMove?: (event: any) => void;
22
23
  onHeaderMouseDown?: (event: any) => void;
23
24
  onHeaderMouseDrag?: (event: any) => void;
@@ -63,6 +64,7 @@ export function FloatingPaneWrapper({
63
64
  windowModeSelected = false,
64
65
  showActions = false,
65
66
  onMouseDown,
67
+ onMouseDownCapture,
66
68
  onHeaderMouseMove,
67
69
  onHeaderMouseDown,
68
70
  onHeaderMouseDrag,
@@ -104,6 +106,7 @@ export function FloatingPaneWrapper({
104
106
  showBorderColor: true,
105
107
  })}
106
108
  onMouseDown={onMouseDown}
109
+ onMouseDownCapture={onMouseDownCapture}
107
110
  >
108
111
  <PaneHeader
109
112
  title={title}
@@ -12,6 +12,7 @@ import {
12
12
  selectUpdateProgress,
13
13
  } from "../../state/selectors-ui";
14
14
  import { getSharedMarketDataCoordinator } from "../../market-data/coordinator";
15
+ import { t, tf } from "../../i18n";
15
16
  import { useQuoteEntry, useResolvedEntryValue } from "../../market-data/hooks";
16
17
  import { formatPercentRaw } from "../../utils/format";
17
18
  import { formatMarketPrice } from "../../market-data/market/format";
@@ -70,7 +71,10 @@ function UpdateStatus() {
70
71
  <Box flexDirection="row" gap={1}>
71
72
  <SpinnerMark name="dots" color={colors.headerText} />
72
73
  <Text fg={colors.headerText}>
73
- Downloading v{updateAvailable?.version}: {updateProgress.percent ?? 0}%
74
+ {tf("Downloading v{version}: {percent}%", {
75
+ version: updateAvailable?.version ?? "",
76
+ percent: updateProgress.percent ?? 0,
77
+ })}
74
78
  </Text>
75
79
  </Box>
76
80
  );
@@ -79,15 +83,15 @@ function UpdateStatus() {
79
83
  return (
80
84
  <Box flexDirection="row" gap={1}>
81
85
  <SpinnerMark name="dots" color={colors.headerText} />
82
- <Text fg={colors.headerText}>Installing update...</Text>
86
+ <Text fg={colors.headerText}>{t("Installing update...")}</Text>
83
87
  </Box>
84
88
  );
85
89
  }
86
90
  if (updateProgress.phase === "done") {
87
- return <Text fg={colors.headerText}>{updateProgress.message ?? "Update installed, restart to apply"}</Text>;
91
+ return <Text fg={colors.headerText}>{t(updateProgress.message ?? "Update installed, restart to apply")}</Text>;
88
92
  }
89
93
  if (updateProgress.phase === "error") {
90
- return <Text fg={colors.headerText}>Update failed: {updateProgress.error}</Text>;
94
+ return <Text fg={colors.headerText}>{tf("Update failed: {error}", { error: updateProgress.error })}</Text>;
91
95
  }
92
96
  }
93
97
 
@@ -95,7 +99,7 @@ function UpdateStatus() {
95
99
  return (
96
100
  <Box flexDirection="row" gap={1}>
97
101
  <SpinnerMark name="dots" color={colors.headerText} />
98
- <Text fg={colors.headerText}>Checking for updates...</Text>
102
+ <Text fg={colors.headerText}>{t("Checking for updates...")}</Text>
99
103
  </Box>
100
104
  );
101
105
  }
@@ -104,13 +108,16 @@ function UpdateStatus() {
104
108
  if (updateAvailable.updateAction.kind === "manual") {
105
109
  return (
106
110
  <Text fg={colors.headerText}>
107
- v{updateAvailable.version} available — run {updateAvailable.updateAction.command}
111
+ {tf("v{version} available — run {command}", {
112
+ version: updateAvailable.version,
113
+ command: updateAvailable.updateAction.command,
114
+ })}
108
115
  </Text>
109
116
  );
110
117
  }
111
118
  return (
112
119
  <Text fg={colors.headerText}>
113
- v{updateAvailable.version} available — starting download...
120
+ {tf("v{version} available — starting download...", { version: updateAvailable.version })}
114
121
  </Text>
115
122
  );
116
123
  }
@@ -158,7 +165,7 @@ export function Header() {
158
165
 
159
166
  // Market status
160
167
  const mktState = spyQuote?.marketState;
161
- const mktLabel = mktState ? marketStateLabel(mktState) : "";
168
+ const mktLabel = mktState ? t(marketStateLabel(mktState)) : "";
162
169
  const mktColor = mktState ? marketStateColor(mktState) : colors.headerText;
163
170
 
164
171
  if (titleBarOverlay) {
@@ -20,8 +20,10 @@ afterEach(() => {
20
20
 
21
21
  function Registration({
22
22
  onRefresh,
23
+ refreshDisabled = false,
23
24
  }: {
24
25
  onRefresh?: () => void;
26
+ refreshDisabled?: boolean;
25
27
  }) {
26
28
  usePaneFooter("test", () => {
27
29
  return {
@@ -35,10 +37,10 @@ function Registration({
35
37
  },
36
38
  ],
37
39
  hints: [
38
- { id: "refresh", key: "r", label: "efresh", onPress: onRefresh },
40
+ { id: "refresh", key: "r", label: "efresh", onPress: onRefresh, disabled: refreshDisabled },
39
41
  ],
40
42
  };
41
- }, [onRefresh]);
43
+ }, [onRefresh, refreshDisabled]);
42
44
  return null;
43
45
  }
44
46
 
@@ -55,15 +57,17 @@ function ExternalLinkRegistration() {
55
57
  function FooterHarness({
56
58
  focused = false,
57
59
  onRefresh,
60
+ refreshDisabled = false,
58
61
  }: {
59
62
  focused?: boolean;
60
63
  onRefresh?: () => void;
64
+ refreshDisabled?: boolean;
61
65
  }) {
62
66
  return (
63
67
  <PaneFooterProvider>
64
68
  {(footer) => (
65
69
  <Box width={64} height={1}>
66
- <Registration onRefresh={onRefresh} />
70
+ <Registration onRefresh={onRefresh} refreshDisabled={refreshDisabled} />
67
71
  <PaneFooterBar footer={footer} focused={focused} width={64} />
68
72
  </Box>
69
73
  )}
@@ -110,6 +114,21 @@ describe("PaneFooterBar", () => {
110
114
  expect(frame).not.toContain("https://example.com");
111
115
  });
112
116
 
117
+ test("omits disabled controls instead of rendering muted hints", async () => {
118
+ testSetup = await testRender(
119
+ <FooterHarness focused refreshDisabled onRefresh={() => {}} />,
120
+ { width: 64, height: 1 },
121
+ );
122
+ await act(async () => {
123
+ await testSetup!.renderOnce();
124
+ await testSetup!.renderOnce();
125
+ });
126
+
127
+ const frame = testSetup.captureCharFrame();
128
+ expect(frame).toContain("Rows 12");
129
+ expect(frame).not.toContain("[r]efresh");
130
+ });
131
+
113
132
  test("calls hint onPress from mouse interaction", async () => {
114
133
  let refreshCount = 0;
115
134
  testSetup = await testRender(<FooterHarness focused onRefresh={() => { refreshCount += 1; }} />, { width: 64, height: 1 });
@@ -7,6 +7,7 @@ import {
7
7
  EMPTY_FOOTER,
8
8
  hasPaneFooterContent,
9
9
  type CombinedPaneFooter,
10
+ type PaneFooterPressEvent,
10
11
  type PaneFooterPart,
11
12
  type PaneFooterSegment,
12
13
  type PaneHint,
@@ -15,6 +16,7 @@ import {
15
16
  export {
16
17
  hasPaneFooterContent,
17
18
  type CombinedPaneFooter,
19
+ type PaneFooterPressEvent,
18
20
  type PaneFooterSegment,
19
21
  type PaneHint,
20
22
  } from "./model";
@@ -140,7 +142,7 @@ function FooterContent({
140
142
  showBackground?: boolean;
141
143
  }) {
142
144
  const hasInfo = footer.info.length > 0;
143
- const visibleHints = focused ? footer.hints : [];
145
+ const visibleHints = focused ? footer.hints.filter((hint) => !hint.disabled) : [];
144
146
  const hasHints = visibleHints.length > 0;
145
147
  const dividerColor = focused ? colors.borderFocused : colors.border;
146
148
  const backgroundColor = showBackground ? blendHex(colors.bg, dividerColor, focused ? 0.12 : 0.06) : undefined;
@@ -11,6 +11,13 @@ export interface PaneFooterSegment {
11
11
  disabled?: boolean;
12
12
  }
13
13
 
14
+ export interface PaneFooterPressEvent {
15
+ pixelX?: number;
16
+ pixelY?: number;
17
+ stopPropagation?: () => void;
18
+ preventDefault?: () => void;
19
+ }
20
+
14
21
  export interface PaneFooterPart {
15
22
  text: string;
16
23
  tone?: "label" | "value" | "muted" | "positive" | "negative" | "warning";
@@ -22,7 +29,7 @@ export interface PaneHint {
22
29
  id: string;
23
30
  key: string;
24
31
  label: string;
25
- onPress?: () => void;
32
+ onPress?: (event?: PaneFooterPressEvent) => void;
26
33
  disabled?: boolean;
27
34
  }
28
35
 
@@ -35,7 +42,7 @@ export const EMPTY_FOOTER: CombinedPaneFooter = { info: [], hints: [] };
35
42
 
36
43
  export function hasPaneFooterContent(footer?: CombinedPaneFooter | null): boolean {
37
44
  if (!footer) return false;
38
- return footer.info.length > 0 || footer.hints.length > 0;
45
+ return footer.info.length > 0 || footer.hints.some((hint) => !hint.disabled);
39
46
  }
40
47
 
41
48
  export function combinePaneFooterRegistrations(registrations: Map<string, PaneFooterRegistration>): CombinedPaneFooter {
@@ -1,6 +1,7 @@
1
1
  import { Box, Span, Text, useNativeRenderer, useUiCapabilities } from "../../../ui";
2
2
  import { useCallback, useRef, type ReactNode } from "react";
3
3
  import { blendHex, colors, floatingPaneTitleBg, paneTitleBg, paneTitleText } from "../../../theme/colors";
4
+ import { displayWidth, truncateToDisplayWidth } from "../../../utils/format";
4
5
 
5
6
  const PANE_HEADER_HEIGHT = 1;
6
7
  const PANE_HEADER_GRIP = ":: ";
@@ -24,10 +25,7 @@ interface PaneHeaderProps {
24
25
  }
25
26
 
26
27
  function truncateTitle(title: string, maxWidth: number): string {
27
- if (maxWidth <= 0) return "";
28
- if (title.length <= maxWidth) return title;
29
- if (maxWidth <= 2) return ".".repeat(maxWidth);
30
- return `${title.slice(0, maxWidth - 2)}..`;
28
+ return truncateToDisplayWidth(title, maxWidth);
31
29
  }
32
30
 
33
31
  function captureTerminalPointerDrag(renderer: unknown, renderable: unknown): void {
@@ -218,7 +216,7 @@ export function PaneHeader({
218
216
  const contentWidth = PANE_HEADER_GRIP.length + closeText.length + actionText.length;
219
217
  const titleWidth = Math.max(0, innerWidth - contentWidth);
220
218
  const clippedTitle = truncateTitle(title, titleWidth);
221
- const fillLen = Math.max(0, innerWidth - PANE_HEADER_GRIP.length - clippedTitle.length - actionText.length - closeText.length);
219
+ const fillLen = Math.max(0, innerWidth - PANE_HEADER_GRIP.length - displayWidth(clippedTitle) - actionText.length - closeText.length);
222
220
  const fill = "─".repeat(fillLen);
223
221
 
224
222
  return (
@@ -257,7 +255,7 @@ export function PaneHeader({
257
255
 
258
256
  const titleWidth = Math.max(0, width - PANE_HEADER_GRIP.length - actionText.length - closeText.length);
259
257
  const clippedTitle = truncateTitle(title, titleWidth);
260
- const padding = " ".repeat(Math.max(0, titleWidth - clippedTitle.length));
258
+ const padding = " ".repeat(Math.max(0, titleWidth - displayWidth(clippedTitle)));
261
259
 
262
260
  return (
263
261
  <Box
@@ -16,6 +16,7 @@ interface PaneWrapperProps {
16
16
  flexGrow?: number;
17
17
  showActions?: boolean;
18
18
  onMouseDown?: (event: any) => void;
19
+ onMouseDownCapture?: (event: any) => void;
19
20
  onHeaderMouseMove?: (event: any) => void;
20
21
  onHeaderMouseDown?: (event: any) => void;
21
22
  onHeaderMouseDrag?: (event: any) => void;
@@ -36,6 +37,7 @@ export function PaneWrapper({
36
37
  flexGrow,
37
38
  showActions = false,
38
39
  onMouseDown,
40
+ onMouseDownCapture,
39
41
  onHeaderMouseMove,
40
42
  onHeaderMouseDown,
41
43
  onHeaderMouseDrag,
@@ -76,6 +78,7 @@ export function PaneWrapper({
76
78
  showBorderColor: true,
77
79
  })}
78
80
  onMouseDown={onMouseDown}
81
+ onMouseDownCapture={onMouseDownCapture}
79
82
  >
80
83
  {title && (
81
84
  <PaneHeader
@@ -0,0 +1,100 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+ import { act } from "react";
3
+ import { testRender } from "../../../renderers/opentui/test-utils";
4
+ import { Text } from "../../../ui";
5
+ import {
6
+ getPaneSidebarWidth,
7
+ PaneSidebar,
8
+ PaneSidebarAction,
9
+ PaneSidebarRow,
10
+ shouldShowPaneSidebar,
11
+ } from "./sidebar";
12
+
13
+ let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
14
+
15
+ afterEach(async () => {
16
+ await act(async () => {
17
+ testSetup?.renderer.destroy();
18
+ testSetup = undefined;
19
+ });
20
+ });
21
+
22
+ describe("pane sidebar metrics", () => {
23
+ test("uses the shared responsive breakpoint and host-specific widths", () => {
24
+ expect(shouldShowPaneSidebar(2, 72, 8)).toBe(true);
25
+ expect(shouldShowPaneSidebar(1, 72, 8)).toBe(false);
26
+ expect(shouldShowPaneSidebar(1, 72, 8, 1)).toBe(true);
27
+ expect(shouldShowPaneSidebar(2, 71, 8)).toBe(false);
28
+ expect(shouldShowPaneSidebar(2, 72, 7)).toBe(false);
29
+
30
+ expect(getPaneSidebarWidth(72, false)).toBe(18);
31
+ expect(getPaneSidebarWidth(100, false)).toBe(24);
32
+ expect(getPaneSidebarWidth(72, true)).toBe(14);
33
+ expect(getPaneSidebarWidth(90, true)).toBe(17);
34
+ expect(getPaneSidebarWidth(200, true)).toBe(19);
35
+ });
36
+ });
37
+
38
+ test("renders a terminal divider and keeps nested actions from selecting their row", async () => {
39
+ let selections = 0;
40
+ let actions = 0;
41
+
42
+ await act(async () => {
43
+ testSetup = await testRender(
44
+ <PaneSidebar width={20} height={4} focused keyboardFocused>
45
+ <PaneSidebarRow
46
+ active={false}
47
+ ariaLabel="Alpha conversation"
48
+ onSelect={() => {
49
+ selections += 1;
50
+ }}
51
+ >
52
+ {({ foregroundColor, onMouseDown }) => (
53
+ <>
54
+ <Text fg={foregroundColor} onMouseDown={onMouseDown}> Alpha</Text>
55
+ <PaneSidebarAction
56
+ width={3}
57
+ ariaLabel="New conversation"
58
+ onPress={() => {
59
+ actions += 1;
60
+ }}
61
+ >
62
+ {({ foregroundColor: actionColor, onMouseDown: onActionMouseDown }) => (
63
+ <Text fg={actionColor} onMouseDown={onActionMouseDown}>+</Text>
64
+ )}
65
+ </PaneSidebarAction>
66
+ </>
67
+ )}
68
+ </PaneSidebarRow>
69
+ </PaneSidebar>,
70
+ { width: 24, height: 4 },
71
+ );
72
+ });
73
+ await act(async () => {
74
+ await testSetup!.renderOnce();
75
+ });
76
+
77
+ const frame = testSetup!.captureCharFrame();
78
+ const lines = frame.split("\n");
79
+ const row = lines.findIndex((line) => line.includes("Alpha"));
80
+ const labelColumn = lines[row]?.indexOf("Alpha") ?? -1;
81
+ const actionColumn = lines[row]?.indexOf("+") ?? -1;
82
+ expect(row).toBeGreaterThanOrEqual(0);
83
+ expect(labelColumn).toBeGreaterThanOrEqual(0);
84
+ expect(actionColumn).toBeGreaterThanOrEqual(0);
85
+ expect(lines.slice(0, 4).every((line) => line[19] === "│")).toBe(true);
86
+
87
+ await act(async () => {
88
+ await testSetup!.mockMouse.click(labelColumn, row);
89
+ await testSetup!.renderOnce();
90
+ });
91
+ expect(selections).toBe(1);
92
+ expect(actions).toBe(0);
93
+
94
+ await act(async () => {
95
+ await testSetup!.mockMouse.click(actionColumn, row);
96
+ await testSetup!.renderOnce();
97
+ });
98
+ expect(actions).toBe(1);
99
+ expect(selections).toBe(1);
100
+ });