gloomberb 0.5.1 → 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 -14
  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 +9 -10
  85. package/src/components/layout/pane-title.ts +35 -0
  86. package/src/components/layout/pane.tsx +52 -11
  87. package/src/components/layout/shell.test.tsx +315 -107
  88. package/src/components/layout/shell.tsx +1010 -368
  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 +129 -0
  180. package/src/plugins/builtin/chat-controller.ts +85 -14
  181. package/src/plugins/builtin/chat.test.tsx +283 -36
  182. package/src/plugins/builtin/chat.tsx +363 -170
  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 +99 -9
  440. package/src/utils/api-client.ts +378 -18
  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
@@ -16,7 +16,7 @@ exports[`CommandBar renders the default layout with opencode-style chrome 1`] =
16
16
  Quote Monitor QQ
17
17
 
18
18
  Search
19
- Search Ticker T
19
+ Description DES
20
20
 
21
21
  Navigation
22
22
  Help HELP
@@ -100,7 +100,7 @@ exports[`CommandBar collapses the trailing column on narrow terminals 1`] = `
100
100
  Quote Monitor QQ
101
101
 
102
102
  Search
103
- Search Ticker T
103
+ Description DES
104
104
 
105
105
 
106
106
 
@@ -112,8 +112,8 @@ exports[`CommandBar groups ticker search sections and keeps saved matches above
112
112
  "
113
113
 
114
114
  Commands
115
- T appl
116
- Shortcut: Open APPL
115
+ DES appl
116
+ Shortcut: DES APPL
117
117
  Saved
118
118
  AAPL NASDAQ
119
119
 
@@ -1,9 +1,9 @@
1
1
  import { afterEach, describe, expect, test } from "bun:test";
2
2
  import { act, useReducer } from "react";
3
- import { testRender } from "@opentui/react/test-utils";
3
+ import { testRender } from "../../renderers/opentui/test-utils";
4
4
  import { DialogProvider } from "@opentui-ui/dialog/react";
5
5
  import { CommandBar } from "./command-bar";
6
- import { AppContext, type AppState, appReducer, createInitialState } from "../../state/app-context";
6
+ import { AppContext, type AppState, appReducer, createInitialState, getEffectiveThemeId } from "../../state/app-context";
7
7
  import { createTestDataProvider } from "../../test-support/data-provider";
8
8
  import { cloneLayout, createDefaultConfig, type AppConfig } from "../../types/config";
9
9
  import type { DataProvider } from "../../types/data-provider";
@@ -214,7 +214,7 @@ function makePluginRegistry(hasPaneSettings: (paneId: string) => boolean = () =>
214
214
  hasPaneSettings,
215
215
  events: { emit: () => {} },
216
216
  hideWidget: () => {},
217
- pinTickerFn: () => {},
217
+ pinTicker: () => {},
218
218
  showWidget: () => {},
219
219
  updateLayoutFn: () => {},
220
220
  getTermSizeFn: () => ({ width: 80, height: 24 }),
@@ -305,7 +305,7 @@ function CommandBarHarness({
305
305
  return (
306
306
  <AppContext value={{ state: currentState, dispatch: currentDispatch }}>
307
307
  <DialogProvider dialogOptions={{ style: { backgroundColor: "#000000", borderColor: "#ffffff", borderStyle: "single" } }}>
308
- {live && <text>{`theme:${currentState.config.theme}`}</text>}
308
+ {live && <text>{`theme:${getEffectiveThemeId(currentState)}`}</text>}
309
309
  {showQueryState && <text>{`query:${currentState.commandBarQuery}`}</text>}
310
310
  {currentState.commandBarOpen && (
311
311
  <CommandBar
@@ -537,6 +537,70 @@ describe("CommandBar", () => {
537
537
  expect(workflowFrame).toContain("AMD");
538
538
  });
539
539
 
540
+ test("prefills alert command targets from the resolved quote", async () => {
541
+ const quoteProvider = createTestDataProvider({
542
+ id: "test",
543
+ search: async () => [],
544
+ getQuote: async (symbol: string) => ({
545
+ symbol,
546
+ price: 201.5,
547
+ currency: "USD",
548
+ change: 1,
549
+ changePercent: 0.5,
550
+ name: "Advanced Micro Devices",
551
+ exchangeName: "NASDAQ",
552
+ lastUpdated: Date.now(),
553
+ dataSource: "live",
554
+ }),
555
+ });
556
+
557
+ testSetup = await testRender(<CommandBarHarness
558
+ query="SA AMD"
559
+ dataProvider={quoteProvider}
560
+ configurePluginRegistry={(pluginRegistry) => {
561
+ (pluginRegistry.commands as Map<string, any>).set("set-alert", {
562
+ id: "set-alert",
563
+ label: "Add Alert",
564
+ description: "Create a price alert from a symbol, condition, and target price",
565
+ keywords: ["add", "set", "alert", "price", "trigger"],
566
+ shortcut: "SA",
567
+ shortcutArg: {
568
+ placeholder: "symbol condition price",
569
+ kind: "ticker",
570
+ parse: (arg: string) => ({ symbol: arg.trim().toUpperCase() }),
571
+ },
572
+ category: "data",
573
+ wizardLayout: "form",
574
+ wizard: [
575
+ { key: "symbol", label: "Symbol", type: "text" },
576
+ {
577
+ key: "condition",
578
+ label: "Condition",
579
+ type: "select",
580
+ options: [{ label: "Above", value: "above" }],
581
+ },
582
+ { key: "price", label: "Target Price", type: "number" },
583
+ ],
584
+ execute: async () => {},
585
+ });
586
+ }}
587
+ />, {
588
+ width: 90,
589
+ height: 24,
590
+ });
591
+
592
+ await testSetup.renderOnce();
593
+
594
+ await act(async () => {
595
+ testSetup!.mockInput.pressEnter();
596
+ await Bun.sleep(0);
597
+ await testSetup!.renderOnce();
598
+ });
599
+
600
+ const workflowFrame = await waitForFrameToContain("Advanced Micro Devices");
601
+ expect(workflowFrame).toContain("201.5");
602
+ });
603
+
540
604
  test("updates workflow select fields from the option picker", async () => {
541
605
  testSetup = await testRender(<CommandBarHarness
542
606
  query="SA AMD"
@@ -611,6 +675,8 @@ describe("CommandBar", () => {
611
675
  test("opens plugin command workflows from a launch request", async () => {
612
676
  testSetup = await testRender(<CommandBarHarness
613
677
  query=""
678
+ selectedTicker="AMD"
679
+ extraTickers={[makeTicker("AMD", "Advanced Micro Devices")]}
614
680
  configureState={(state) => ({
615
681
  ...state,
616
682
  commandBarLaunchRequest: {
@@ -625,6 +691,13 @@ describe("CommandBar", () => {
625
691
  label: "Set Alert",
626
692
  keywords: ["alert", "price", "trigger"],
627
693
  shortcut: "SA",
694
+ shortcutArg: {
695
+ placeholder: "symbol condition price",
696
+ kind: "ticker",
697
+ parse: (_arg: string, context?: { activeTicker: string | null }) => (
698
+ context?.activeTicker ? { symbol: context.activeTicker } : {}
699
+ ),
700
+ },
628
701
  category: "data",
629
702
  wizardLayout: "form",
630
703
  wizard: [
@@ -652,6 +725,7 @@ describe("CommandBar", () => {
652
725
  expect(frame).toContain("Set Alert");
653
726
  expect(frame).toContain("Symbol");
654
727
  expect(frame).toContain("Condition");
728
+ expect(frame).toContain("AMD");
655
729
  });
656
730
 
657
731
  test("renders theme prefix results in the root query", async () => {
@@ -727,6 +801,32 @@ describe("CommandBar", () => {
727
801
  expect(frame).toContain("theme:green");
728
802
  });
729
803
 
804
+ test("does not preview a theme from mouse hover alone", async () => {
805
+ testSetup = await testRender(<CommandBarHarness query="TH " live />, {
806
+ width: 80,
807
+ height: 24,
808
+ });
809
+
810
+ await testSetup.renderOnce();
811
+ const frame = testSetup.captureCharFrame();
812
+ const rows = frame.split("\n");
813
+ const hoveredRow = rows.findIndex((line) => line.includes("Green"));
814
+ const hoveredCol = rows[hoveredRow]?.indexOf("Green") ?? -1;
815
+
816
+ expect(hoveredRow).toBeGreaterThanOrEqual(0);
817
+ expect(hoveredCol).toBeGreaterThanOrEqual(0);
818
+
819
+ await act(async () => {
820
+ await testSetup!.mockMouse.moveTo(hoveredCol + 1, hoveredRow);
821
+ await testSetup!.renderOnce();
822
+ });
823
+ await testSetup.renderOnce();
824
+
825
+ const hoveredFrame = testSetup.captureCharFrame();
826
+ expect(hoveredFrame).toContain("theme:amber");
827
+ expect(hoveredFrame).not.toContain("theme:green");
828
+ });
829
+
730
830
  test("keeps the selected theme after pressing enter in root theme mode", async () => {
731
831
  testSetup = await testRender(<CommandBarHarness query="TH " live />, {
732
832
  width: 80,
@@ -753,8 +853,51 @@ describe("CommandBar", () => {
753
853
  expect(frame).not.toContain("Commands");
754
854
  });
755
855
 
856
+ test("uses pending keyboard theme selection when enter follows before a repaint", async () => {
857
+ testSetup = await testRender(<CommandBarHarness query="TH " live />, {
858
+ width: 80,
859
+ height: 24,
860
+ });
861
+
862
+ await testSetup.renderOnce();
863
+ await act(async () => {
864
+ testSetup!.mockInput.pressArrow("down");
865
+ testSetup!.mockInput.pressEnter();
866
+ await Bun.sleep(0);
867
+ await testSetup!.renderOnce();
868
+ });
869
+ await testSetup.renderOnce();
870
+
871
+ const frame = testSetup.captureCharFrame();
872
+ expect(frame).toContain("theme:green");
873
+ expect(frame).not.toContain("theme:amber");
874
+ expect(frame).not.toContain("Commands");
875
+ });
876
+
877
+ test("clears theme preview when leaving root theme mode", async () => {
878
+ testSetup = await testRender(<CommandBarHarness query="TH " live />, {
879
+ width: 80,
880
+ height: 24,
881
+ });
882
+
883
+ await testSetup.renderOnce();
884
+ await act(async () => {
885
+ testSetup!.mockInput.pressArrow("down");
886
+ await testSetup!.renderOnce();
887
+ });
888
+ expect(testSetup.captureCharFrame()).toContain("theme:green");
889
+
890
+ await emitKeypress(testSetup, { name: "u", ctrl: true });
891
+ await testSetup.renderOnce();
892
+
893
+ const frame = testSetup.captureCharFrame();
894
+ expect(frame).toContain("theme:amber");
895
+ expect(frame).not.toContain("theme:green");
896
+ expect(frame).toContain("Commands");
897
+ });
898
+
756
899
  test("keeps typed prefixes in the root query until a result is activated", async () => {
757
- testSetup = await testRender(<CommandBarHarness query="T " />, {
900
+ testSetup = await testRender(<CommandBarHarness query="DES " />, {
758
901
  width: 80,
759
902
  height: 24,
760
903
  });
@@ -763,7 +906,7 @@ describe("CommandBar", () => {
763
906
 
764
907
  let frame = testSetup.captureCharFrame();
765
908
  expect(frame).toContain("Commands");
766
- expect(frame).toContain("T");
909
+ expect(frame).toContain("DES");
767
910
  expect(frame).toContain("Type a ticker symbol");
768
911
  expect(frame).not.toContain("Back");
769
912
  });
@@ -785,7 +928,7 @@ describe("CommandBar", () => {
785
928
 
786
929
  const frame = testSetup.captureCharFrame();
787
930
  expect(frame).toContain("Back");
788
- expect(frame).toContain("Search Ticker");
931
+ expect(frame).toContain("Security Description");
789
932
  });
790
933
 
791
934
  test("QQ with an active ticker shows ghost completion and tab inserts the symbol", async () => {
@@ -845,13 +988,13 @@ describe("CommandBar", () => {
845
988
  });
846
989
 
847
990
  test("clears the root query with cmd-backspace", async () => {
848
- testSetup = await testRender(<CommandBarHarness query="T AMD" />, {
991
+ testSetup = await testRender(<CommandBarHarness query="DES AMD" />, {
849
992
  width: 80,
850
993
  height: 24,
851
994
  });
852
995
 
853
996
  await testSetup.renderOnce();
854
- expect(testSetup.captureCharFrame()).toContain("T AMD");
997
+ expect(testSetup.captureCharFrame()).toContain("DES AMD");
855
998
 
856
999
  await act(async () => {
857
1000
  testSetup!.mockInput.pressKey("backspace", { meta: true });
@@ -861,7 +1004,7 @@ describe("CommandBar", () => {
861
1004
  const frame = testSetup.captureCharFrame();
862
1005
  expect(frame).toContain("Commands");
863
1006
  expect(frame).toContain("Search");
864
- expect(frame).not.toContain("T AMD");
1007
+ expect(frame).not.toContain("DES AMD");
865
1008
  });
866
1009
 
867
1010
  test("pressing the close shortcut at the root closes the command bar", async () => {
@@ -883,7 +1026,7 @@ describe("CommandBar", () => {
883
1026
  test("loads provider-backed ticker search results in the root command results", async () => {
884
1027
  testSetup = await testRender(
885
1028
  <CommandBarHarness
886
- query="T appl"
1029
+ query="DES appl"
887
1030
  dataProvider={makeDataProvider(async () => [
888
1031
  { providerId: "yahoo", symbol: "IVSX", name: "Invsivx Holdings", exchange: "NYSE", type: "ETF" },
889
1032
  { providerId: "yahoo", symbol: "AAPL", name: "Apple Inc", exchange: "NASDAQ", type: "EQUITY" },
@@ -903,14 +1046,14 @@ describe("CommandBar", () => {
903
1046
  expect(frame).toContain("AAOI");
904
1047
  });
905
1048
 
906
- test("T MSFT opens an exact ticker directly", async () => {
1049
+ test("DES MSFT opens an exact ticker directly", async () => {
907
1050
  const pinned: string[] = [];
908
1051
 
909
1052
  testSetup = await testRender(
910
1053
  <CommandBarHarness
911
- query="T MSFT"
1054
+ query="DES MSFT"
912
1055
  configurePluginRegistry={(pluginRegistry) => {
913
- pluginRegistry.pinTickerFn = (symbol) => {
1056
+ pluginRegistry.pinTicker = (symbol) => {
914
1057
  pinned.push(symbol);
915
1058
  };
916
1059
  }}
@@ -1445,7 +1588,7 @@ describe("CommandBar", () => {
1445
1588
  <CommandBarHarness
1446
1589
  query=""
1447
1590
  configurePluginRegistry={(pluginRegistry) => {
1448
- pluginRegistry.pinTickerFn = (symbol) => {
1591
+ pluginRegistry.pinTicker = (symbol) => {
1449
1592
  pinned.push(symbol);
1450
1593
  };
1451
1594
  }}
@@ -1566,7 +1709,7 @@ describe("CommandBar", () => {
1566
1709
  test("moves through long result lists with the mouse wheel", async () => {
1567
1710
  testSetup = await testRender(
1568
1711
  <CommandBarHarness
1569
- query="NP scratch"
1712
+ query="scratch"
1570
1713
  configurePluginRegistry={(pluginRegistry) => {
1571
1714
  const paneTemplates = pluginRegistry.paneTemplates as Map<string, any>;
1572
1715
  for (let index = 0; index < 20; index++) {
@@ -1684,8 +1827,8 @@ describe("CommandBar", () => {
1684
1827
  expect(frame).toContain("1c / 1d");
1685
1828
  });
1686
1829
 
1687
- test("renders new pane mode with plugin-defined pane templates", async () => {
1688
- testSetup = await testRender(<CommandBarHarness query="NP chat" />, {
1830
+ test("filters plugin-defined pane templates directly from the root query", async () => {
1831
+ testSetup = await testRender(<CommandBarHarness query="chat" />, {
1689
1832
  width: 100,
1690
1833
  height: 18,
1691
1834
  });
@@ -1724,6 +1867,80 @@ describe("CommandBar", () => {
1724
1867
  expect(frame).toContain("QQ");
1725
1868
  });
1726
1869
 
1870
+ test("shows pane templates that share the same shortcut", async () => {
1871
+ testSetup = await testRender(<CommandBarHarness
1872
+ query="DUP"
1873
+ configurePluginRegistry={(pluginRegistry) => {
1874
+ const paneTemplates = pluginRegistry.paneTemplates as Map<string, any>;
1875
+ paneTemplates.set("first-duplicate-pane", {
1876
+ id: "first-duplicate-pane",
1877
+ paneId: "first-duplicate",
1878
+ label: "First Duplicate",
1879
+ description: "First pane using a shared shortcut",
1880
+ shortcut: { prefix: "DUP" },
1881
+ });
1882
+ paneTemplates.set("second-duplicate-pane", {
1883
+ id: "second-duplicate-pane",
1884
+ paneId: "second-duplicate",
1885
+ label: "Second Duplicate",
1886
+ description: "Second pane using a shared shortcut",
1887
+ shortcut: { prefix: "DUP" },
1888
+ });
1889
+ }}
1890
+ />, {
1891
+ width: 100,
1892
+ height: 18,
1893
+ });
1894
+
1895
+ await testSetup.renderOnce();
1896
+
1897
+ const frame = testSetup.captureCharFrame();
1898
+ expect(frame).toContain("First Duplicate");
1899
+ expect(frame).toContain("Second Duplicate");
1900
+ });
1901
+
1902
+ test("does not treat no-argument shortcut prefixes as typed pane names", async () => {
1903
+ const created: Array<{ templateId: string; options?: Record<string, unknown> }> = [];
1904
+
1905
+ testSetup = await testRender(<CommandBarHarness
1906
+ query="Top News"
1907
+ configurePluginRegistry={(pluginRegistry) => {
1908
+ pluginRegistry.createPaneFromTemplateAsyncFn = async (templateId, options) => {
1909
+ created.push({ templateId, options });
1910
+ };
1911
+ const paneTemplates = pluginRegistry.paneTemplates as Map<string, any>;
1912
+ paneTemplates.set("market-movers-pane", {
1913
+ id: "market-movers-pane",
1914
+ paneId: "market-movers",
1915
+ label: "Market Movers",
1916
+ description: "Track market gainers and losers",
1917
+ shortcut: { prefix: "MOST" },
1918
+ });
1919
+ paneTemplates.set("news-top-pane", {
1920
+ id: "news-top-pane",
1921
+ paneId: "news-top",
1922
+ label: "Top News",
1923
+ description: "Curated top market stories ranked by importance",
1924
+ shortcut: { prefix: "TOP" },
1925
+ });
1926
+ }}
1927
+ />, {
1928
+ width: 100,
1929
+ height: 18,
1930
+ });
1931
+
1932
+ await testSetup.renderOnce();
1933
+ expect(testSetup.captureCharFrame()).toContain("Top News");
1934
+
1935
+ await act(async () => {
1936
+ testSetup!.mockInput.pressEnter();
1937
+ await Bun.sleep(0);
1938
+ await testSetup!.renderOnce();
1939
+ });
1940
+
1941
+ expect(created).toEqual([{ templateId: "news-top-pane", options: undefined }]);
1942
+ });
1943
+
1727
1944
  test("opens plugin command wizards inline inside the command bar", async () => {
1728
1945
  let submittedValues: Record<string, string> | undefined;
1729
1946
 
@@ -1794,6 +2011,38 @@ describe("CommandBar", () => {
1794
2011
  });
1795
2012
  });
1796
2013
 
2014
+ test("does not duplicate placeholder text as workflow field help", async () => {
2015
+ testSetup = await testRender(<CommandBarHarness
2016
+ query="Duplicate Form"
2017
+ configurePluginRegistry={(pluginRegistry) => {
2018
+ (pluginRegistry.commands as Map<string, any>).set("plugin:duplicate-form", {
2019
+ id: "plugin:duplicate-form",
2020
+ label: "Duplicate Form",
2021
+ description: "Check workflow field copy",
2022
+ category: "config",
2023
+ wizard: [
2024
+ { key: "name", label: "Name", type: "text", placeholder: "Profile name" },
2025
+ { key: "token", label: "Token", type: "text", placeholder: "repeat marker", body: ["repeat marker"] },
2026
+ ],
2027
+ execute: async () => {},
2028
+ } as any);
2029
+ pluginRegistry.getCommandPluginId = () => "notes";
2030
+ }}
2031
+ />, {
2032
+ width: 100,
2033
+ height: 24,
2034
+ });
2035
+
2036
+ await testSetup.renderOnce();
2037
+ await act(async () => {
2038
+ testSetup!.mockInput.pressEnter();
2039
+ await testSetup!.renderOnce();
2040
+ });
2041
+
2042
+ const frame = testSetup.captureCharFrame();
2043
+ expect(frame.match(/repeat marker/g)?.length ?? 0).toBe(1);
2044
+ });
2045
+
1797
2046
  test("does not treat backspace as Back inside workflow text fields", async () => {
1798
2047
  testSetup = await testRender(<CommandBarHarness
1799
2048
  query="Plugin Login"
@@ -1804,7 +2053,7 @@ describe("CommandBar", () => {
1804
2053
  description: "Authenticate without leaving the command bar",
1805
2054
  category: "config",
1806
2055
  wizard: [
1807
- { key: "username", label: "Username", type: "text", placeholder: "vince" },
2056
+ { key: "username", label: "Username", type: "text" },
1808
2057
  ],
1809
2058
  execute: async () => {},
1810
2059
  } as any);
@@ -2402,7 +2651,7 @@ describe("CommandBar", () => {
2402
2651
  test("groups ticker search sections and keeps saved matches above looser provider results", async () => {
2403
2652
  testSetup = await testRender(
2404
2653
  <CommandBarHarness
2405
- query="T appl"
2654
+ query="DES appl"
2406
2655
  dataProvider={makeDataProvider(async () => [
2407
2656
  { providerId: "yahoo", symbol: "IVSX", name: "Invsivx Holdings", exchange: "NYSE", type: "ETF" },
2408
2657
  { providerId: "yahoo", symbol: "AAPL", name: "Apple Inc", exchange: "NASDAQ", type: "EQUITY" },