gloomberb 0.5.0 → 0.6.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 (500) hide show
  1. package/README.md +91 -11
  2. package/package.json +12 -5
  3. package/src/app.tsx +414 -191
  4. package/src/architecture/import-boundaries.test.ts +118 -0
  5. package/src/assets/gloomberb-logo.png +0 -0
  6. package/src/assets/gloomberb-logo.svg +73 -0
  7. package/src/brokers/account-cache.ts +157 -0
  8. package/src/brokers/profile-form.test.ts +73 -0
  9. package/src/brokers/profile-form.ts +109 -0
  10. package/src/brokers/remote-broker-adapter.ts +91 -0
  11. package/src/brokers/sync-broker-instance.test.ts +124 -1
  12. package/src/brokers/sync-broker-instance.ts +113 -17
  13. package/src/capabilities/factories.ts +100 -0
  14. package/src/capabilities/index.ts +4 -0
  15. package/src/capabilities/registry.test.ts +137 -0
  16. package/src/capabilities/registry.ts +131 -0
  17. package/src/capabilities/service-capabilities.ts +27 -0
  18. package/src/capabilities/types.ts +84 -0
  19. package/src/cli/commands/ticker.ts +11 -3
  20. package/src/cli/context.ts +19 -13
  21. package/src/cli/helpers.ts +1 -0
  22. package/src/cli/types.ts +2 -2
  23. package/src/components/chart/chart-controller.ts +4 -24
  24. package/src/components/chart/chart-data.ts +3 -14
  25. package/src/components/chart/chart-indicator-selector.tsx +28 -6
  26. package/src/components/chart/chart-pane-settings.ts +15 -28
  27. package/src/components/chart/chart-renderer.ts +74 -99
  28. package/src/components/chart/chart-resolution.ts +10 -10
  29. package/src/components/chart/chart-scroll.test.ts +6 -5
  30. package/src/components/chart/chart-scroll.ts +2 -2
  31. package/src/components/chart/chart-types.ts +0 -1
  32. package/src/components/chart/chart-viewport.ts +2 -1
  33. package/src/components/chart/chart.test.ts +37 -0
  34. package/src/components/chart/comparison-chart-data.ts +3 -17
  35. package/src/components/chart/comparison-chart-renderer.test.ts +27 -4
  36. package/src/components/chart/comparison-chart-renderer.ts +1 -1
  37. package/src/components/chart/comparison-stock-chart.tsx +304 -108
  38. package/src/components/chart/cursor-motion.ts +3 -2
  39. package/src/components/chart/indicators/bands.test.ts +0 -14
  40. package/src/components/chart/indicators/moving-averages.test.ts +0 -15
  41. package/src/components/chart/indicators/options.ts +3 -3
  42. package/src/components/chart/indicators/oscillators.test.ts +0 -21
  43. package/src/components/chart/indicators/types.ts +1 -2
  44. package/src/components/chart/native/chart-rasterizer.ts +37 -44
  45. package/src/components/chart/native/kitty-adapter.ts +2 -5
  46. package/src/components/chart/native/kitty-manager.ts +1 -1
  47. package/src/components/chart/native/kitty-protocol.ts +0 -1
  48. package/src/components/chart/native/kitty-support.ts +1 -1
  49. package/src/components/chart/native/kitty.test.ts +30 -13
  50. package/src/components/chart/native/renderer-selection.ts +1 -1
  51. package/src/components/chart/native/surface-manager.ts +1 -1
  52. package/src/components/chart/static-chart-surface.test.tsx +51 -0
  53. package/src/components/chart/static-chart-surface.tsx +191 -0
  54. package/src/components/chart/stock-chart-renderer-switch.test.tsx +242 -35
  55. package/src/components/chart/stock-chart.tsx +438 -141
  56. package/src/components/command-bar/__snapshots__/command-bar.test.tsx.snap +4 -4
  57. package/src/components/command-bar/command-bar.test.tsx +269 -20
  58. package/src/components/command-bar/command-bar.tsx +830 -353
  59. package/src/components/command-bar/command-registry.ts +10 -20
  60. package/src/components/command-bar/helpers.test.ts +1 -1
  61. package/src/components/command-bar/helpers.ts +9 -11
  62. package/src/components/command-bar/pane-template-display.ts +4 -8
  63. package/src/components/command-bar/root-shortcuts.ts +12 -11
  64. package/src/components/command-bar/view-model.test.ts +8 -42
  65. package/src/components/command-bar/view-model.ts +8 -36
  66. package/src/components/command-bar/workflow-ops.test.ts +76 -2
  67. package/src/components/command-bar/workflow-ops.ts +40 -9
  68. package/src/components/command-bar/workflow-types.ts +2 -1
  69. package/src/components/data-table-stack-view.test.tsx +206 -0
  70. package/src/components/data-table-stack-view.tsx +152 -0
  71. package/src/components/data-table-view.test.tsx +188 -0
  72. package/src/components/data-table-view.tsx +279 -0
  73. package/src/components/feed-data-table-stack-view.test.tsx +225 -0
  74. package/src/components/feed-data-table-stack-view.tsx +365 -0
  75. package/src/components/index.ts +26 -31
  76. package/src/components/layout/detached-pane-shell.tsx +185 -0
  77. package/src/components/layout/floating-pane.tsx +87 -13
  78. package/src/components/layout/header.tsx +139 -49
  79. package/src/components/layout/pane-content.tsx +40 -0
  80. package/src/components/layout/pane-footer.test.tsx +195 -0
  81. package/src/components/layout/pane-footer.tsx +410 -0
  82. package/src/components/layout/pane-header.test.tsx +39 -0
  83. package/src/components/layout/pane-header.tsx +192 -14
  84. package/src/components/layout/pane-sizing.ts +13 -4
  85. package/src/components/layout/pane-title.ts +35 -0
  86. package/src/components/layout/pane.tsx +56 -8
  87. package/src/components/layout/shell.test.tsx +322 -19
  88. package/src/components/layout/shell.tsx +1010 -366
  89. package/src/components/layout/status-bar.test.tsx +17 -10
  90. package/src/components/layout/status-bar.tsx +367 -100
  91. package/src/components/layout/titlebar-overlay.ts +2 -0
  92. package/src/components/markdown-editor.tsx +29 -14
  93. package/src/components/markdown-text.tsx +25 -24
  94. package/src/components/onboarding/onboarding-wizard.test.tsx +8 -3
  95. package/src/components/onboarding/onboarding-wizard.tsx +347 -473
  96. package/src/components/pane-settings-dialog.tsx +521 -89
  97. package/src/components/pane-template-wizard.test.tsx +1 -1
  98. package/src/components/pane-template-wizard.tsx +38 -37
  99. package/src/components/price-selector-dialog.tsx +22 -21
  100. package/src/components/ticker-badge-list.tsx +53 -0
  101. package/src/components/ticker-badge-text.test.tsx +44 -1
  102. package/src/components/ticker-badge-text.tsx +78 -38
  103. package/src/components/ticker-badge.tsx +28 -7
  104. package/src/components/ticker-list-table-view.tsx +248 -0
  105. package/src/components/ticker-list-table.test.tsx +47 -1
  106. package/src/components/ticker-list-table.tsx +212 -41
  107. package/src/components/toggle-list.tsx +114 -8
  108. package/src/components/ui/button.tsx +24 -16
  109. package/src/components/ui/choice-dialog.tsx +89 -0
  110. package/src/components/ui/data-table.tsx +313 -114
  111. package/src/components/ui/external-link.tsx +52 -13
  112. package/src/components/ui/fields.tsx +44 -30
  113. package/src/components/ui/frame.tsx +24 -57
  114. package/src/components/ui/index.ts +15 -50
  115. package/src/components/ui/list-view.tsx +65 -25
  116. package/src/components/ui/loading.tsx +5 -60
  117. package/src/components/ui/message-composer.test.tsx +81 -0
  118. package/src/components/ui/message-composer.tsx +139 -0
  119. package/src/components/ui/multi-select-dialog.tsx +100 -32
  120. package/src/components/ui/multi-select.ts +112 -0
  121. package/src/components/ui/native-select.tsx +120 -0
  122. package/src/components/ui/page-stack-view.tsx +49 -19
  123. package/src/components/ui/remote-image.tsx +75 -0
  124. package/src/components/ui/row-value-cache.test.ts +17 -0
  125. package/src/components/ui/row-value-cache.ts +41 -0
  126. package/src/components/ui/shortcut-hint.tsx +55 -0
  127. package/src/components/ui/status.tsx +12 -67
  128. package/src/components/ui/table-layout.ts +59 -0
  129. package/src/components/ui/tabs.tsx +249 -27
  130. package/src/components/ui/toggle.tsx +19 -127
  131. package/src/components/ui/ui.test.tsx +266 -53
  132. package/src/components/use-double-click-activation.ts +7 -2
  133. package/src/components/use-external-link-footer.ts +79 -0
  134. package/src/core/app-services.ts +95 -0
  135. package/src/core/state/app-state.ts +886 -0
  136. package/src/{state → core/state}/session-persistence.ts +7 -6
  137. package/src/data/config-layout.ts +225 -0
  138. package/src/data/config-store-node.ts +390 -0
  139. package/src/data/config-store.test.ts +72 -0
  140. package/src/data/config-store.ts +35 -478
  141. package/src/data/sqlite-json.ts +2 -5
  142. package/src/data/sqlite-retry.ts +1 -0
  143. package/src/index.tsx +2 -68
  144. package/src/market-data/coordinator-subscriptions.test.ts +125 -0
  145. package/src/market-data/coordinator.test.ts +73 -4
  146. package/src/market-data/coordinator.ts +190 -24
  147. package/src/market-data/hooks.test.tsx +1 -1
  148. package/src/market-data/hooks.tsx +109 -87
  149. package/src/market-data/query-store.ts +3 -3
  150. package/src/market-data/request-types.test.ts +0 -14
  151. package/src/market-data/result-types.ts +1 -0
  152. package/src/news/aggregator.test.ts +267 -0
  153. package/src/news/aggregator.ts +452 -0
  154. package/src/news/hooks.ts +39 -0
  155. package/src/news/ticker-symbols.ts +21 -0
  156. package/src/news/types.ts +77 -0
  157. package/src/notifications/app-notifier.ts +21 -10
  158. package/src/pane-settings.test.ts +0 -2
  159. package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +124 -134
  160. package/src/plugins/builtin/ai/gloomberb-cli.ts +14 -13
  161. package/src/plugins/builtin/ai/providers.ts +1 -9
  162. package/src/plugins/builtin/ai/runner.ts +41 -5
  163. package/src/plugins/builtin/ai/screener-pane.test.tsx +37 -55
  164. package/src/plugins/builtin/ai/screener-pane.tsx +231 -276
  165. package/src/plugins/builtin/ai/settings.ts +0 -1
  166. package/src/plugins/builtin/ai/utils.ts +1 -71
  167. package/src/plugins/builtin/alerts/alert-engine.test.ts +1 -38
  168. package/src/plugins/builtin/alerts/index.test.tsx +61 -82
  169. package/src/plugins/builtin/alerts/index.tsx +263 -155
  170. package/src/plugins/builtin/alerts/types.ts +7 -0
  171. package/src/plugins/builtin/analytics/index.test.tsx +13 -8
  172. package/src/plugins/builtin/analytics/index.tsx +78 -120
  173. package/src/plugins/builtin/analytics/metrics.test.ts +0 -4
  174. package/src/plugins/builtin/ask-ai.test.tsx +55 -54
  175. package/src/plugins/builtin/broker-manager/index.test.tsx +168 -0
  176. package/src/plugins/builtin/broker-manager/index.tsx +759 -0
  177. package/src/plugins/builtin/broker-manager/model.test.ts +107 -0
  178. package/src/plugins/builtin/broker-manager/model.ts +142 -0
  179. package/src/plugins/builtin/chat-controller.test.ts +150 -0
  180. package/src/plugins/builtin/chat-controller.ts +88 -16
  181. package/src/plugins/builtin/chat.test.tsx +283 -36
  182. package/src/plugins/builtin/chat.tsx +373 -172
  183. package/src/plugins/builtin/cloud-auth-actions.tsx +72 -0
  184. package/src/plugins/builtin/cloud-tweets.tsx +896 -0
  185. package/src/plugins/builtin/comparison-chart.test.tsx +46 -57
  186. package/src/plugins/builtin/comparison-chart.tsx +11 -11
  187. package/src/plugins/builtin/correlation/compute.test.ts +0 -17
  188. package/src/plugins/builtin/correlation/index.tsx +51 -46
  189. package/src/plugins/builtin/correlation/settings.ts +1 -2
  190. package/src/plugins/builtin/debug.tsx +99 -70
  191. package/src/plugins/builtin/earnings/earnings-cache.test.ts +2 -2
  192. package/src/plugins/builtin/earnings/earnings-cache.ts +12 -3
  193. package/src/plugins/builtin/earnings/earnings-data.test.ts +42 -11
  194. package/src/plugins/builtin/earnings/earnings-data.ts +70 -5
  195. package/src/plugins/builtin/earnings/index.tsx +187 -94
  196. package/src/plugins/builtin/econ/calendar-source.ts +9 -116
  197. package/src/plugins/builtin/econ/fred-client.test.ts +1 -14
  198. package/src/plugins/builtin/econ/fred-series-map.ts +1 -0
  199. package/src/plugins/builtin/econ/index.tsx +236 -421
  200. package/src/plugins/builtin/econ/types.ts +1 -0
  201. package/src/plugins/builtin/fx-matrix/index.tsx +43 -45
  202. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -0
  203. package/src/plugins/builtin/help.test.tsx +120 -60
  204. package/src/plugins/builtin/help.tsx +266 -82
  205. package/src/plugins/builtin/holders/index.test.ts +45 -0
  206. package/src/plugins/builtin/holders/index.tsx +949 -0
  207. package/src/plugins/builtin/insider/index.tsx +207 -253
  208. package/src/plugins/builtin/insider/insider-data.test.ts +1 -11
  209. package/src/plugins/builtin/insider/insider-data.ts +1 -0
  210. package/src/plugins/builtin/layout-manager.tsx +12 -40
  211. package/src/plugins/builtin/market-movers/index.tsx +70 -144
  212. package/src/plugins/builtin/market-movers/screener.test.ts +0 -18
  213. package/src/plugins/builtin/news-wire/breaking-notifications.test.ts +98 -0
  214. package/src/plugins/builtin/news-wire/breaking-notifications.ts +110 -0
  215. package/src/plugins/builtin/news-wire/breaking-pane.tsx +157 -0
  216. package/src/plugins/builtin/news-wire/categories.ts +114 -0
  217. package/src/plugins/builtin/news-wire/default-feeds.ts +3 -0
  218. package/src/plugins/builtin/news-wire/digest-store.ts +44 -0
  219. package/src/plugins/builtin/news-wire/feed-config.test.ts +90 -0
  220. package/src/plugins/builtin/news-wire/feed-config.ts +216 -0
  221. package/src/plugins/builtin/news-wire/feed-pane.tsx +21 -0
  222. package/src/plugins/builtin/news-wire/hash.ts +9 -0
  223. package/src/plugins/builtin/news-wire/index.tsx +92 -0
  224. package/src/plugins/builtin/news-wire/industry-pane.tsx +140 -0
  225. package/src/plugins/builtin/news-wire/news-detail-view.tsx +256 -0
  226. package/src/plugins/builtin/news-wire/news-footer.ts +28 -0
  227. package/src/plugins/builtin/news-wire/news-preset-pane.tsx +94 -0
  228. package/src/plugins/builtin/news-wire/news-query-presets.ts +40 -0
  229. package/src/plugins/builtin/news-wire/news-table.test.tsx +206 -0
  230. package/src/plugins/builtin/news-wire/news-table.tsx +296 -0
  231. package/src/plugins/builtin/news-wire/persisted-articles.ts +92 -0
  232. package/src/plugins/builtin/news-wire/read-state.test.ts +30 -0
  233. package/src/plugins/builtin/news-wire/read-state.ts +76 -0
  234. package/src/plugins/builtin/news-wire/rss-parser.test.ts +184 -0
  235. package/src/plugins/builtin/news-wire/rss-parser.ts +213 -0
  236. package/src/plugins/builtin/news-wire/rss-source.test.ts +159 -0
  237. package/src/plugins/builtin/news-wire/rss-source.ts +181 -0
  238. package/src/plugins/builtin/news-wire/top-pane.tsx +21 -0
  239. package/src/plugins/builtin/news.tsx +98 -25
  240. package/src/plugins/builtin/notes-files.ts +51 -10
  241. package/src/plugins/builtin/notes.tsx +332 -131
  242. package/src/plugins/builtin/options.test.tsx +185 -0
  243. package/src/plugins/builtin/options.tsx +346 -206
  244. package/src/plugins/builtin/plugin-groups.ts +92 -0
  245. package/src/plugins/builtin/portfolio-list/header.tsx +72 -37
  246. package/src/plugins/builtin/portfolio-list/index.tsx +25 -42
  247. package/src/plugins/builtin/portfolio-list/metrics.ts +1 -1
  248. package/src/plugins/builtin/portfolio-list/pane.tsx +246 -164
  249. package/src/plugins/builtin/portfolio-list/settings.ts +19 -51
  250. package/src/plugins/builtin/portfolio-list/summary.tsx +21 -16
  251. package/src/plugins/builtin/portfolio-list/table.tsx +66 -25
  252. package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.test.tsx +1 -1
  253. package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.ts +1 -1
  254. package/src/plugins/builtin/portfolio-list.test.ts +60 -1
  255. package/src/plugins/builtin/portfolio-list.test.tsx +122 -55
  256. package/src/plugins/builtin/quote-monitor.test.tsx +2 -27
  257. package/src/plugins/builtin/research/index.tsx +724 -0
  258. package/src/plugins/builtin/sec.tsx +67 -19
  259. package/src/plugins/builtin/sectors/index.tsx +42 -80
  260. package/src/plugins/builtin/sectors/sector-data.ts +1 -0
  261. package/src/plugins/builtin/ticker-detail/chart-tab.tsx +25 -39
  262. package/src/plugins/builtin/ticker-detail/financials-tab.tsx +108 -58
  263. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +383 -271
  264. package/src/plugins/builtin/ticker-detail/pane.tsx +135 -49
  265. package/src/plugins/builtin/ticker-detail/quote-monitor.tsx +43 -27
  266. package/src/plugins/builtin/ticker-detail/settings.ts +3 -6
  267. package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +16 -10
  268. package/src/plugins/builtin/ticker-detail.test.tsx +156 -33
  269. package/src/plugins/builtin/ticker-surface.ts +68 -0
  270. package/src/plugins/builtin/world-indices/index.tsx +44 -82
  271. package/src/plugins/builtin/world-indices/indices.ts +1 -0
  272. package/src/plugins/builtin/yahoo.ts +55 -1
  273. package/src/plugins/builtin/yield-curve/index.tsx +87 -145
  274. package/src/plugins/builtin/yield-curve/treasury-data.ts +6 -77
  275. package/src/plugins/catalog-ui.test.ts +24 -0
  276. package/src/plugins/catalog-ui.ts +44 -0
  277. package/src/plugins/catalog.ts +2 -50
  278. package/src/plugins/ibkr/__snapshots__/trade-tab.test.tsx.snap +5 -5
  279. package/src/plugins/ibkr/account-cache.test.ts +15 -14
  280. package/src/plugins/ibkr/account-cache.ts +15 -120
  281. package/src/plugins/ibkr/broker-adapter.ts +67 -0
  282. package/src/plugins/ibkr/config.test.ts +0 -18
  283. package/src/plugins/ibkr/dialogs.tsx +14 -56
  284. package/src/plugins/ibkr/flex.test.ts +57 -2
  285. package/src/plugins/ibkr/flex.ts +116 -12
  286. package/src/plugins/ibkr/gateway-helpers.ts +8 -3
  287. package/src/plugins/ibkr/gateway-service-native.ts +2381 -0
  288. package/src/plugins/ibkr/gateway-service.test.ts +47 -9
  289. package/src/plugins/ibkr/gateway-service.ts +264 -2235
  290. package/src/plugins/ibkr/gateway-types.ts +27 -0
  291. package/src/plugins/ibkr/index.test.tsx +3 -11
  292. package/src/plugins/ibkr/index.tsx +1 -0
  293. package/src/plugins/ibkr/price-normalization.test.ts +1 -6
  294. package/src/plugins/ibkr/price-normalization.ts +1 -0
  295. package/src/plugins/ibkr/trade-badge.tsx +6 -5
  296. package/src/plugins/ibkr/trade-tab.test.tsx +2 -2
  297. package/src/plugins/ibkr/trade-tab.tsx +164 -117
  298. package/src/plugins/ibkr/trading-pane.tsx +117 -83
  299. package/src/plugins/pane-manager.test.ts +3 -1
  300. package/src/plugins/pane-manager.ts +93 -19
  301. package/src/plugins/plugin-runtime.test.tsx +176 -74
  302. package/src/plugins/plugin-runtime.tsx +228 -10
  303. package/src/plugins/prediction-markets/cache.ts +49 -0
  304. package/src/plugins/prediction-markets/categories.ts +0 -10
  305. package/src/plugins/prediction-markets/chart.test.tsx +1 -1
  306. package/src/plugins/prediction-markets/chart.tsx +21 -20
  307. package/src/plugins/prediction-markets/cli.ts +1 -0
  308. package/src/plugins/prediction-markets/controller-data.ts +135 -89
  309. package/src/plugins/prediction-markets/controller.ts +37 -108
  310. package/src/plugins/prediction-markets/detail/book.tsx +23 -22
  311. package/src/plugins/prediction-markets/detail/outcomes.tsx +33 -32
  312. package/src/plugins/prediction-markets/detail/overview.tsx +17 -12
  313. package/src/plugins/prediction-markets/detail/pane.tsx +59 -45
  314. package/src/plugins/prediction-markets/detail/rules.tsx +22 -7
  315. package/src/plugins/prediction-markets/detail/shared.tsx +3 -2
  316. package/src/plugins/prediction-markets/detail/trades.tsx +12 -11
  317. package/src/plugins/prediction-markets/detail.test.tsx +1 -1
  318. package/src/plugins/prediction-markets/keyboard.ts +1 -0
  319. package/src/plugins/prediction-markets/launch.test.ts +1 -1
  320. package/src/plugins/prediction-markets/launch.ts +1 -1
  321. package/src/plugins/prediction-markets/metrics.ts +25 -12
  322. package/src/plugins/prediction-markets/pane.test.tsx +16 -7
  323. package/src/plugins/prediction-markets/pane.tsx +180 -82
  324. package/src/plugins/prediction-markets/plugin.test.tsx +37 -45
  325. package/src/plugins/prediction-markets/services/fetch.ts +27 -10
  326. package/src/plugins/prediction-markets/services/kalshi-adapter.ts +20 -3
  327. package/src/plugins/prediction-markets/services/polymarket-adapter.ts +29 -11
  328. package/src/plugins/prediction-markets/settings.ts +0 -5
  329. package/src/plugins/prediction-markets/test-helpers.tsx +19 -44
  330. package/src/plugins/registry.test.ts +307 -0
  331. package/src/plugins/registry.ts +293 -82
  332. package/src/react/input.ts +53 -0
  333. package/src/react/plugins/plugin-slot.tsx +21 -0
  334. package/src/react/use-raf-callback.ts +44 -0
  335. package/src/renderers/electrobun/bun/application-menu-click.test.ts +41 -0
  336. package/src/renderers/electrobun/bun/application-menu-click.ts +55 -0
  337. package/src/renderers/electrobun/bun/application-menu.ts +118 -0
  338. package/src/renderers/electrobun/bun/context-menu-click.test.ts +67 -0
  339. package/src/renderers/electrobun/bun/context-menu-click.ts +61 -0
  340. package/src/renderers/electrobun/bun/context-menu-normalize.test.ts +52 -0
  341. package/src/renderers/electrobun/bun/context-menu-normalize.ts +67 -0
  342. package/src/renderers/electrobun/bun/core-capabilities.ts +328 -0
  343. package/src/renderers/electrobun/bun/desktop-workspace.test.ts +63 -0
  344. package/src/renderers/electrobun/bun/desktop-workspace.ts +162 -0
  345. package/src/renderers/electrobun/bun/index.ts +1149 -0
  346. package/src/renderers/electrobun/bun/window-frame.test.ts +40 -0
  347. package/src/renderers/electrobun/bun/window-frame.ts +46 -0
  348. package/src/renderers/electrobun/shared/protocol.ts +72 -0
  349. package/src/renderers/electrobun/view/ai-host.ts +93 -0
  350. package/src/renderers/electrobun/view/app-services.ts +348 -0
  351. package/src/renderers/electrobun/view/application-menu-bridge.ts +12 -0
  352. package/src/renderers/electrobun/view/backend-rpc.ts +173 -0
  353. package/src/renderers/electrobun/view/broker-remote-client.ts +154 -0
  354. package/src/renderers/electrobun/view/config-host.ts +35 -0
  355. package/src/renderers/electrobun/view/context-menu.test.ts +140 -0
  356. package/src/renderers/electrobun/view/context-menu.ts +164 -0
  357. package/src/renderers/electrobun/view/data-table.tsx +773 -0
  358. package/src/renderers/electrobun/view/desktop-controls.tsx +651 -0
  359. package/src/renderers/electrobun/view/desktop-window-bridge.ts +59 -0
  360. package/src/renderers/electrobun/view/desktop-window-snapshot.test.ts +57 -0
  361. package/src/renderers/electrobun/view/desktop-window-snapshot.ts +41 -0
  362. package/src/renderers/electrobun/view/dialog-host.tsx +62 -0
  363. package/src/renderers/electrobun/view/http-fetch.ts +113 -0
  364. package/src/renderers/electrobun/view/input-host.tsx +65 -0
  365. package/src/renderers/electrobun/view/key-event.test.ts +34 -0
  366. package/src/renderers/electrobun/view/key-event.ts +134 -0
  367. package/src/renderers/electrobun/view/main.tsx +104 -0
  368. package/src/renderers/electrobun/view/native-renderer.ts +110 -0
  369. package/src/renderers/electrobun/view/native-stubs/chart-kitty-support.ts +9 -0
  370. package/src/renderers/electrobun/view/native-stubs/chart-surface-manager.ts +25 -0
  371. package/src/renderers/electrobun/view/native-stubs/chart-surface-sync.ts +1 -0
  372. package/src/renderers/electrobun/view/notes-files.ts +56 -0
  373. package/src/renderers/electrobun/view/resource-store.test.ts +44 -0
  374. package/src/renderers/electrobun/view/resource-store.ts +106 -0
  375. package/src/renderers/electrobun/view/rpc-codec.ts +42 -0
  376. package/src/renderers/electrobun/view/scrollbar-activity.ts +23 -0
  377. package/src/renderers/electrobun/view/toast-host.tsx +65 -0
  378. package/src/renderers/electrobun/view/ui-host.tsx +1490 -0
  379. package/src/renderers/electrobun/view/update-host.ts +38 -0
  380. package/src/renderers/opentui/dialog-host.test.tsx +44 -0
  381. package/src/renderers/opentui/dialog-host.tsx +56 -0
  382. package/src/renderers/opentui/host.tsx +134 -0
  383. package/src/renderers/opentui/input-host.tsx +24 -0
  384. package/src/renderers/opentui/start.tsx +71 -0
  385. package/src/renderers/opentui/test-utils.tsx +76 -0
  386. package/src/renderers/opentui/toast-host.tsx +10 -0
  387. package/src/renderers/opentui/ui-host.tsx +97 -0
  388. package/src/sources/gloomberb-cloud.test.ts +288 -17
  389. package/src/sources/gloomberb-cloud.ts +176 -9
  390. package/src/sources/provider-errors.ts +1 -0
  391. package/src/sources/provider-router.test.ts +220 -81
  392. package/src/sources/provider-router.ts +294 -111
  393. package/src/sources/sec-edgar.ts +19 -6
  394. package/src/sources/yahoo-finance.test.ts +0 -5
  395. package/src/sources/yahoo-finance.ts +420 -11
  396. package/src/state/app-activity.ts +7 -7
  397. package/src/state/app-bootstrap.ts +114 -40
  398. package/src/state/app-context-selector.test.tsx +70 -1
  399. package/src/state/app-context.test.ts +71 -3
  400. package/src/state/app-context.test.tsx +14 -7
  401. package/src/state/app-context.tsx +205 -869
  402. package/src/state/config-save-scheduler.ts +35 -0
  403. package/src/state/persist-scheduler.test.ts +67 -0
  404. package/src/state/persist-scheduler.ts +69 -0
  405. package/src/state/selectors-ui.ts +55 -0
  406. package/src/state/selectors.ts +15 -71
  407. package/src/state/session-persistence.test.ts +1 -1
  408. package/src/state/ticker-refresh-queue.ts +52 -3
  409. package/src/state/use-inline-tickers.ts +18 -16
  410. package/src/state/use-quote-streaming.test.ts +0 -6
  411. package/src/state/use-quote-streaming.test.tsx +1 -1
  412. package/src/state/use-quote-streaming.ts +3 -2
  413. package/src/state/use-session-snapshot.ts +32 -17
  414. package/src/test-support/plugin-runtime.ts +149 -0
  415. package/src/theme/color-utils.ts +1 -0
  416. package/src/theme/colors.ts +16 -0
  417. package/src/types/broker.ts +16 -1
  418. package/src/types/capability-route-source.ts +23 -0
  419. package/src/types/config.test.ts +23 -0
  420. package/src/types/config.ts +145 -10
  421. package/src/types/context-menu.ts +95 -0
  422. package/src/types/data-provider.ts +34 -3
  423. package/src/types/desktop-menu.ts +13 -0
  424. package/src/types/desktop-window.ts +29 -0
  425. package/src/types/financials.ts +123 -2
  426. package/src/types/instrument.ts +1 -0
  427. package/src/types/news-source.ts +9 -0
  428. package/src/types/persistence.ts +1 -0
  429. package/src/types/plugin.ts +32 -8
  430. package/src/ui/ascii-font.ts +85 -0
  431. package/src/ui/context-menu.test.ts +94 -0
  432. package/src/ui/context-menu.tsx +433 -0
  433. package/src/ui/dialog.tsx +55 -0
  434. package/src/ui/host.tsx +361 -0
  435. package/src/ui/index.tsx +126 -0
  436. package/src/ui/toast.tsx +40 -0
  437. package/src/updater.test.ts +69 -0
  438. package/src/updater.ts +61 -18
  439. package/src/utils/api-client.test.ts +186 -6
  440. package/src/utils/api-client.ts +433 -33
  441. package/src/utils/back-navigation.ts +6 -0
  442. package/src/utils/cli-output.ts +1 -0
  443. package/src/utils/currency-units.test.ts +1 -6
  444. package/src/utils/currency-units.ts +1 -0
  445. package/src/utils/debug-log.ts +68 -0
  446. package/src/utils/dom-screenshot.ts +326 -0
  447. package/src/utils/exchange-rate-map.ts +1 -0
  448. package/src/utils/exchanges.ts +214 -0
  449. package/src/utils/format.test.ts +7 -0
  450. package/src/utils/format.ts +1 -0
  451. package/src/utils/fuzzy-search.ts +1 -0
  452. package/src/utils/html-entities.test.ts +8 -0
  453. package/src/utils/html-entities.ts +32 -0
  454. package/src/utils/http-transport.ts +11 -0
  455. package/src/utils/main-thread-monitor.ts +127 -0
  456. package/src/utils/perf-marks.ts +54 -0
  457. package/src/utils/quote-freshness.ts +27 -50
  458. package/src/utils/quote-resolution.test.ts +5 -5
  459. package/src/utils/quote-resolution.ts +0 -1
  460. package/src/utils/quote-time.ts +1 -5
  461. package/src/utils/sec.test.ts +0 -12
  462. package/src/utils/selection-clipboard.ts +14 -6
  463. package/src/utils/shortcut-labels.test.ts +20 -0
  464. package/src/utils/shortcut-labels.ts +67 -0
  465. package/src/utils/slugify.ts +1 -0
  466. package/src/utils/terminal-input-reset.ts +1 -0
  467. package/src/utils/text-wrap.ts +82 -0
  468. package/src/utils/throttled-fetch.test.ts +19 -0
  469. package/src/utils/throttled-fetch.ts +11 -9
  470. package/src/utils/ticker-list.test.ts +1 -7
  471. package/src/utils/ticker-list.ts +1 -0
  472. package/src/utils/timestamp.ts +29 -0
  473. package/src/utils/tweet-text.test.ts +13 -0
  474. package/src/utils/tweet-text.ts +18 -0
  475. package/src/utils/url.ts +11 -0
  476. package/src/version.ts +1 -1
  477. package/src/components/chart/chart-control-hint.tsx +0 -39
  478. package/src/components/detail-feed-view.test.tsx +0 -180
  479. package/src/components/detail-feed-view.tsx +0 -531
  480. package/src/components/layout/pixel-mouse-mode.test.ts +0 -122
  481. package/src/components/layout/pixel-mouse.test.ts +0 -40
  482. package/src/components/spinner.tsx +0 -2
  483. package/src/components/tab-bar.tsx +0 -17
  484. package/src/components/ui/multi-select-chips.tsx +0 -69
  485. package/src/plugins/builtin/ai/index.test.tsx +0 -139
  486. package/src/plugins/builtin/ask-ai.tsx +0 -9
  487. package/src/plugins/builtin/correlation/plugin.test.ts +0 -44
  488. package/src/plugins/builtin/correlation/settings.test.ts +0 -34
  489. package/src/plugins/builtin/econ/calendar-source.test.ts +0 -219
  490. package/src/plugins/builtin/econ/fred-client.ts +0 -79
  491. package/src/plugins/builtin/fred-settings.ts +0 -10
  492. package/src/plugins/builtin/options-availability.test.tsx +0 -179
  493. package/src/plugins/builtin/options-availability.ts +0 -158
  494. package/src/plugins/builtin/portfolio-list/metrics.test.ts +0 -61
  495. package/src/plugins/builtin/yield-curve/treasury-data.test.ts +0 -212
  496. package/src/plugins/prediction-markets/status.ts +0 -24
  497. package/src/plugins/prediction-markets/table.tsx +0 -87
  498. package/src/utils/quote-time.test.ts +0 -22
  499. package/src/utils/selection-clipboard.test.ts +0 -43
  500. package/src/utils/terminal-input-reset.test.ts +0 -50
@@ -1,13 +1,17 @@
1
1
  import { afterEach, describe, expect, test } from "bun:test";
2
2
  import { act, useState } from "react";
3
- import { testRender } from "@opentui/react/test-utils";
3
+ import { PaneFooterBar, PaneFooterProvider } from "../../components/layout/pane-footer";
4
+ import { testRender } from "../../renderers/opentui/test-utils";
4
5
  import { AppContext, createInitialState } from "../../state/app-context";
6
+ import { createTestPluginRuntime } from "../../test-support/plugin-runtime";
5
7
  import { colors } from "../../theme/colors";
6
8
  import { createDefaultConfig } from "../../types/config";
7
9
  import type { PersistedResourceValue } from "../../types/persistence";
8
10
  import type { PluginPersistence } from "../../types/plugin";
11
+ import { Box } from "../../ui";
9
12
  import { apiClient, type ChatMessage } from "../../utils/api-client";
10
- import { setSharedDataProviderForTests, setSharedRegistryForTests } from "../registry";
13
+ import { PluginRenderProvider, type PluginRuntimeAccess } from "../plugin-runtime";
14
+ import { setSharedMarketDataForTests, setSharedRegistryForTests } from "../registry";
11
15
  import { ChatContent, ChatStatusWidget, getSelectedMessageScrollTop, gloomberbCloudPlugin } from "./chat";
12
16
  import { ChatController } from "./chat-controller";
13
17
 
@@ -141,6 +145,8 @@ function createHarness(
141
145
  height?: number;
142
146
  configureState?: (state: ReturnType<typeof createInitialState>) => void;
143
147
  close?: () => void;
148
+ withFooter?: boolean;
149
+ runtime?: PluginRuntimeAccess;
144
150
  },
145
151
  ) {
146
152
  const width = options?.width ?? 60;
@@ -148,15 +154,36 @@ function createHarness(
148
154
  const state = createInitialState(createDefaultConfig("/tmp/gloomberb-chat"));
149
155
  options?.configureState?.(state);
150
156
 
157
+ const content = options?.withFooter ? (
158
+ <PaneFooterProvider>
159
+ {(footer) => (
160
+ <Box flexDirection="column" width={width} height={height}>
161
+ <ChatContent
162
+ controller={controller}
163
+ width={width}
164
+ height={Math.max(1, height - 1)}
165
+ focused
166
+ close={options?.close}
167
+ />
168
+ <PaneFooterBar footer={footer} focused width={width} />
169
+ </Box>
170
+ )}
171
+ </PaneFooterProvider>
172
+ ) : (
173
+ <ChatContent
174
+ controller={controller}
175
+ width={width}
176
+ height={height}
177
+ focused
178
+ close={options?.close}
179
+ />
180
+ );
181
+
151
182
  return (
152
183
  <AppContext value={{ state, dispatch: () => {} }}>
153
- <ChatContent
154
- controller={controller}
155
- width={width}
156
- height={height}
157
- focused
158
- close={options?.close}
159
- />
184
+ <PluginRenderProvider pluginId="gloomberb-cloud" runtime={options?.runtime ?? createTestPluginRuntime()}>
185
+ {content}
186
+ </PluginRenderProvider>
160
187
  </AppContext>
161
188
  );
162
189
  }
@@ -208,7 +235,7 @@ function hexToRgbaInts(hex: string) {
208
235
 
209
236
  afterEach(async () => {
210
237
  setSharedRegistryForTests(undefined);
211
- setSharedDataProviderForTests(undefined);
238
+ setSharedMarketDataForTests(undefined);
212
239
  apiClient.connectChannel = originalConnectChannel;
213
240
  apiClient.setSessionToken(null);
214
241
 
@@ -329,6 +356,29 @@ describe("ChatContent", () => {
329
356
  expect(frame).not.toContain("click a message");
330
357
  });
331
358
 
359
+ test("uses the full message row width when the reply action is hidden", async () => {
360
+ const controller = createController({
361
+ messages: [{
362
+ ...makeMessage(1),
363
+ content: "small panes should use full width",
364
+ }],
365
+ });
366
+
367
+ await act(async () => {
368
+ testSetup = await testRender(createHarness(controller, {
369
+ width: 44,
370
+ height: 8,
371
+ }), {
372
+ width: 44,
373
+ height: 8,
374
+ });
375
+ });
376
+
377
+ await flushFrame();
378
+
379
+ expect(testSetup.captureCharFrame()).toContain("small panes should use full width");
380
+ });
381
+
332
382
  test("does not select a message when the row is clicked", async () => {
333
383
  const controller = createController({
334
384
  messages: [makeMessage(1)],
@@ -417,6 +467,34 @@ describe("ChatContent", () => {
417
467
  expect(frame).toContain("Reply to @user2...");
418
468
  });
419
469
 
470
+ test("shows the reply action next to the selected message timestamp", async () => {
471
+ const controller = createController({
472
+ messages: [makeMessage(1), makeMessage(2)],
473
+ });
474
+
475
+ await act(async () => {
476
+ testSetup = await testRender(createHarness(controller, {
477
+ width: 72,
478
+ height: 12,
479
+ }), {
480
+ width: 72,
481
+ height: 12,
482
+ });
483
+ });
484
+
485
+ await flushFrame();
486
+
487
+ await emitKeypress({ name: "up", sequence: "\u001b[A" });
488
+ await flushFrame();
489
+
490
+ const lines = testSetup.captureCharFrame().split("\n");
491
+ const headerLine = lines.find((line) => line.includes("user2"));
492
+ const bodyLine = lines.find((line) => line.includes("message 2"));
493
+
494
+ expect(headerLine).toContain("Reply");
495
+ expect(bodyLine).not.toContain("Reply");
496
+ });
497
+
420
498
  test("down arrow from the newest selected message returns focus to the composer", async () => {
421
499
  const controller = createController({
422
500
  messages: [makeMessage(1), makeMessage(2)],
@@ -839,6 +917,45 @@ describe("ChatContent", () => {
839
917
  expect(sentMessages).toEqual(["first line\nsecond line"]);
840
918
  });
841
919
 
920
+ test("keeps typed shortcut letters in the composer instead of moving message selection", async () => {
921
+ const controller = createController({
922
+ messages: Array.from({ length: 18 }, (_, index) => makeMessage(index + 1)),
923
+ });
924
+
925
+ await act(async () => {
926
+ testSetup = await testRender(createHarness(controller), {
927
+ width: 60,
928
+ height: 12,
929
+ });
930
+ });
931
+
932
+ await flushFrame();
933
+
934
+ const frameBeforeClick = testSetup.captureCharFrame().split("\n");
935
+ const inputRow = frameBeforeClick.findIndex((line) => line.includes("Type a message..."));
936
+ const inputCol = frameBeforeClick[inputRow]?.indexOf("Type a message...") ?? -1;
937
+
938
+ expect(inputRow).toBeGreaterThanOrEqual(0);
939
+ expect(inputCol).toBeGreaterThanOrEqual(0);
940
+
941
+ await act(async () => {
942
+ await testSetup!.mockMouse.click(inputCol + 1, inputRow);
943
+ await testSetup!.renderOnce();
944
+ await testSetup!.renderOnce();
945
+ });
946
+
947
+ await act(async () => {
948
+ await testSetup!.mockInput.typeText("g");
949
+ await testSetup!.renderOnce();
950
+ await testSetup!.renderOnce();
951
+ });
952
+
953
+ const frameAfterType = testSetup.captureCharFrame();
954
+ expect(frameAfterType).toContain("> g");
955
+ expect(frameAfterType).not.toContain("user1 3/30/26");
956
+ expect(frameAfterType).not.toContain("message 1 ");
957
+ });
958
+
842
959
  test("renders optimistic sends with a sending status", async () => {
843
960
  const controller = createController({
844
961
  messages: [{
@@ -880,9 +997,9 @@ describe("ChatContent", () => {
880
997
  });
881
998
 
882
999
  await act(async () => {
883
- testSetup = await testRender(createHarness(controller, { width: 60, height: 12 }), {
1000
+ testSetup = await testRender(createHarness(controller, { width: 60, height: 13, withFooter: true }), {
884
1001
  width: 60,
885
- height: 12,
1002
+ height: 13,
886
1003
  });
887
1004
  });
888
1005
 
@@ -899,11 +1016,131 @@ describe("ChatContent", () => {
899
1016
  await flushFrame();
900
1017
 
901
1018
  const frameAfterUpdate = testSetup.captureCharFrame();
902
- expect(frameAfterUpdate).toContain("7 messages");
1019
+ expect(frameAfterUpdate).not.toContain("7 messages");
903
1020
  expect(frameAfterUpdate).toContain("message 7");
904
1021
  expect(frameAfterUpdate).not.toContain("message 1");
905
1022
  });
906
1023
 
1024
+ test("loads older messages at the top without jumping away from the current transcript", async () => {
1025
+ const controller = createController({
1026
+ messages: [
1027
+ makeMessage(4),
1028
+ makeMessage(5),
1029
+ makeMessage(6),
1030
+ makeMessage(7),
1031
+ makeMessage(8),
1032
+ makeMessage(9),
1033
+ makeMessage(10),
1034
+ ],
1035
+ });
1036
+ let loadCount = 0;
1037
+ controller.loadOlderMessages = async () => {
1038
+ loadCount += 1;
1039
+ (controller as any).mergeMessages([
1040
+ makeMessage(1),
1041
+ makeMessage(2),
1042
+ makeMessage(3),
1043
+ ], { notifyMentions: false });
1044
+ };
1045
+
1046
+ await act(async () => {
1047
+ testSetup = await testRender(createHarness(controller, { width: 60, height: 13 }), {
1048
+ width: 60,
1049
+ height: 13,
1050
+ });
1051
+ });
1052
+
1053
+ await flushFrame();
1054
+ expect(testSetup.captureCharFrame()).toContain("message 10");
1055
+
1056
+ await emitKeypress({ name: "g", sequence: "g" });
1057
+ await flushFrame();
1058
+
1059
+ expect(loadCount).toBe(1);
1060
+ expect(controller.getSnapshot().messages.map((message) => message.id)).toEqual([
1061
+ "m1",
1062
+ "m2",
1063
+ "m3",
1064
+ "m4",
1065
+ "m5",
1066
+ "m6",
1067
+ "m7",
1068
+ "m8",
1069
+ "m9",
1070
+ "m10",
1071
+ ]);
1072
+ const frame = testSetup.captureCharFrame();
1073
+ expect(frame).toContain("message 4");
1074
+ expect(frame).not.toContain("message 1");
1075
+ });
1076
+
1077
+ test("repins the transcript to the latest message when a following chat pane regains focus", async () => {
1078
+ const controller = createController({
1079
+ messages: [
1080
+ makeMessage(1),
1081
+ makeMessage(2),
1082
+ makeMessage(3),
1083
+ makeMessage(4),
1084
+ makeMessage(5),
1085
+ makeMessage(6),
1086
+ makeMessage(7),
1087
+ makeMessage(8),
1088
+ ],
1089
+ });
1090
+ const state = createInitialState(createDefaultConfig("/tmp/gloomberb-chat-refocus"));
1091
+ let setFocused: ((focused: boolean) => void) | undefined;
1092
+
1093
+ function FocusHarness() {
1094
+ const [isFocused, updateFocused] = useState(false);
1095
+ setFocused = updateFocused;
1096
+
1097
+ return (
1098
+ <AppContext value={{ state, dispatch: () => {} }}>
1099
+ <PluginRenderProvider pluginId="gloomberb-cloud" runtime={createTestPluginRuntime()}>
1100
+ <ChatContent
1101
+ controller={controller}
1102
+ width={60}
1103
+ height={13}
1104
+ focused={isFocused}
1105
+ />
1106
+ </PluginRenderProvider>
1107
+ </AppContext>
1108
+ );
1109
+ }
1110
+
1111
+ await act(async () => {
1112
+ testSetup = await testRender(<FocusHarness />, {
1113
+ width: 60,
1114
+ height: 13,
1115
+ });
1116
+ });
1117
+
1118
+ await flushFrame();
1119
+ expect(testSetup.captureCharFrame()).toContain("message 8");
1120
+
1121
+ await act(async () => {
1122
+ await testSetup!.mockMouse.scroll(3, 4, "up");
1123
+ await testSetup!.mockMouse.scroll(3, 4, "up");
1124
+ await testSetup!.mockMouse.scroll(3, 4, "up");
1125
+ await testSetup!.renderOnce();
1126
+ await testSetup!.renderOnce();
1127
+ });
1128
+
1129
+ const scrolledFrame = testSetup.captureCharFrame();
1130
+ expect(scrolledFrame).toContain("message 2");
1131
+ expect(scrolledFrame).not.toContain("message 8");
1132
+
1133
+ await act(async () => {
1134
+ setFocused?.(true);
1135
+ await testSetup!.renderOnce();
1136
+ });
1137
+ await flushFrame();
1138
+
1139
+ const refocusedFrame = testSetup.captureCharFrame();
1140
+ expect(refocusedFrame).toContain("message 8");
1141
+ expect(refocusedFrame).not.toContain("message 2");
1142
+ });
1143
+
907
1144
  test("keeps the bottom reachable after narrowing the chat pane", async () => {
908
1145
  const controller = createController({
909
1146
  messages: [
@@ -932,12 +1169,14 @@ describe("ChatContent", () => {
932
1169
 
933
1170
  return (
934
1171
  <AppContext value={{ state, dispatch: () => {} }}>
935
- <ChatContent
936
- controller={controller}
937
- width={size.width}
938
- height={size.height}
939
- focused
940
- />
1172
+ <PluginRenderProvider pluginId="gloomberb-cloud" runtime={createTestPluginRuntime()}>
1173
+ <ChatContent
1174
+ controller={controller}
1175
+ width={size.width}
1176
+ height={size.height}
1177
+ focused
1178
+ />
1179
+ </PluginRenderProvider>
941
1180
  </AppContext>
942
1181
  );
943
1182
  }
@@ -960,7 +1199,7 @@ describe("ChatContent", () => {
960
1199
  const frame = testSetup!.captureCharFrame();
961
1200
  expect(frame).toContain("vince");
962
1201
  expect(frame).toContain("newest message stays");
963
- expect(frame).toContain("reachable after shrink");
1202
+ expect(frame).toContain("shrink");
964
1203
  });
965
1204
 
966
1205
  test("renders ticker badges and opens a floating detail pane on click", async () => {
@@ -977,7 +1216,7 @@ describe("ChatContent", () => {
977
1216
  const opened: string[] = [];
978
1217
 
979
1218
  setSharedRegistryForTests({
980
- pinTickerFn(symbol: string) {
1219
+ pinTicker(symbol: string) {
981
1220
  opened.push(symbol);
982
1221
  },
983
1222
  } as any);
@@ -1107,7 +1346,7 @@ describe("ChatContent", () => {
1107
1346
  const frame = testSetup.captureCharFrame();
1108
1347
  expect(frame).toContain("message 1");
1109
1348
  expect(frame).toContain("Read-only chat.");
1110
- expect(frame).toContain("Login");
1349
+ expect(frame).toContain("Log In");
1111
1350
  expect(frame).toContain("Sign Up");
1112
1351
  expect(frame).not.toContain("Type a message...");
1113
1352
  });
@@ -1123,7 +1362,9 @@ describe("ChatContent", () => {
1123
1362
  await act(async () => {
1124
1363
  testSetup = await testRender(
1125
1364
  <AppContext value={{ state, dispatch: () => {} }}>
1126
- <ChatStatusWidget controller={controller} />
1365
+ <PluginRenderProvider pluginId="gloomberb-cloud" runtime={createTestPluginRuntime()}>
1366
+ <ChatStatusWidget controller={controller} />
1367
+ </PluginRenderProvider>
1127
1368
  </AppContext>,
1128
1369
  { width: 40, height: 1 },
1129
1370
  );
@@ -1146,16 +1387,18 @@ describe("ChatContent", () => {
1146
1387
  const state = createInitialState(createDefaultConfig("/tmp/gloomberb-chat"));
1147
1388
  state.config.disabledPlugins = [];
1148
1389
 
1149
- setSharedRegistryForTests({
1150
- openCommandBarFn(query?: string) {
1390
+ const runtime = createTestPluginRuntime({
1391
+ openCommandBar(query?: string) {
1151
1392
  openedQueries.push(query ?? "");
1152
1393
  },
1153
- } as any);
1394
+ });
1154
1395
 
1155
1396
  await act(async () => {
1156
1397
  testSetup = await testRender(
1157
1398
  <AppContext value={{ state, dispatch: () => {} }}>
1158
- <ChatStatusWidget controller={controller} />
1399
+ <PluginRenderProvider pluginId="gloomberb-cloud" runtime={runtime}>
1400
+ <ChatStatusWidget controller={controller} />
1401
+ </PluginRenderProvider>
1159
1402
  </AppContext>,
1160
1403
  { width: 40, height: 1 },
1161
1404
  );
@@ -1165,22 +1408,22 @@ describe("ChatContent", () => {
1165
1408
 
1166
1409
  const frame = testSetup.captureCharFrame();
1167
1410
  expect(frame).toContain("☁");
1168
- expect(frame).toContain("Login");
1169
- expect(frame).toContain("Sign Up");
1411
+ expect(frame).toContain("Log In");
1412
+ expect(frame).not.toContain("Sign Up");
1170
1413
  expect(frame).not.toContain("Shift+C");
1171
1414
 
1172
1415
  const line = frame.split("\n")[0] ?? "";
1173
- const signUpCol = line.indexOf("Sign Up");
1416
+ const loginCol = line.indexOf("Log In");
1174
1417
 
1175
- expect(signUpCol).toBeGreaterThanOrEqual(0);
1418
+ expect(loginCol).toBeGreaterThanOrEqual(0);
1176
1419
 
1177
1420
  await act(async () => {
1178
- await testSetup!.mockMouse.click(signUpCol + 1, 0);
1421
+ await testSetup!.mockMouse.click(loginCol + 1, 0);
1179
1422
  await testSetup!.renderOnce();
1180
1423
  await testSetup!.renderOnce();
1181
1424
  });
1182
1425
 
1183
- expect(openedQueries).toEqual(["Sign Up"]);
1426
+ expect(openedQueries).toEqual(["Log In"]);
1184
1427
  });
1185
1428
 
1186
1429
  test("shows an unread mention badge and opens chat from the status widget", async () => {
@@ -1192,11 +1435,11 @@ describe("ChatContent", () => {
1192
1435
  const state = createInitialState(createDefaultConfig("/tmp/gloomberb-chat"));
1193
1436
  state.config.disabledPlugins = [];
1194
1437
 
1195
- setSharedRegistryForTests({
1438
+ const runtime = createTestPluginRuntime({
1196
1439
  showWidget(paneId: string) {
1197
1440
  openedWidgets.push(paneId);
1198
1441
  },
1199
- } as any);
1442
+ });
1200
1443
 
1201
1444
  await act(async () => {
1202
1445
  (controller as any).mergeMessages([{
@@ -1212,7 +1455,9 @@ describe("ChatContent", () => {
1212
1455
  await act(async () => {
1213
1456
  testSetup = await testRender(
1214
1457
  <AppContext value={{ state, dispatch: () => {} }}>
1215
- <ChatStatusWidget controller={controller} />
1458
+ <PluginRenderProvider pluginId="gloomberb-cloud" runtime={runtime}>
1459
+ <ChatStatusWidget controller={controller} />
1460
+ </PluginRenderProvider>
1216
1461
  </AppContext>,
1217
1462
  { width: 40, height: 1 },
1218
1463
  );
@@ -1249,6 +1494,8 @@ describe("ChatContent", () => {
1249
1494
  deleteState: () => {},
1250
1495
  },
1251
1496
  registerPane: () => {},
1497
+ registerPaneTemplate: () => {},
1498
+ registerDetailTab: () => {},
1252
1499
  registerShortcut: () => {},
1253
1500
  registerCommand: (command: { id: string; wizardLayout?: string; wizard?: Array<{ key: string }> }) => {
1254
1501
  registeredCommands.push(command);