gloomberb 0.11.2 → 0.12.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 (531) hide show
  1. package/README.md +7 -4
  2. package/README.zh-CN.md +4 -2
  3. package/package.json +28 -1
  4. package/src/api-client/auth.ts +10 -0
  5. package/src/api-client/data.ts +108 -1
  6. package/src/api-client/index.test.ts +127 -0
  7. package/src/api-client/index.ts +134 -1
  8. package/src/api-client/normalizers.ts +39 -0
  9. package/src/api-client/paths.ts +85 -1
  10. package/src/api-client/types.ts +220 -0
  11. package/src/app/pane-runtime/index.tsx +15 -12
  12. package/src/app/pane-runtime/layout-placement.test.ts +27 -0
  13. package/src/app/pane-runtime/layout-placement.ts +13 -0
  14. package/src/app/pane-runtime/plugin-bindings.ts +62 -0
  15. package/src/app.tsx +60 -18
  16. package/src/architecture/browser-entry-graph.test.ts +96 -0
  17. package/src/brokers/profile-form.test.ts +25 -27
  18. package/src/brokers/test-broker.ts +68 -0
  19. package/src/capabilities/chart-series.ts +3 -2
  20. package/src/cli/commands/market.ts +2 -2
  21. package/src/cli/commands/plugins.ts +39 -11
  22. package/src/cli/desktop-pane-shot.ts +285 -16
  23. package/src/cli/index.ts +1 -1
  24. package/src/cli/pane-functions/capabilities.ts +61 -31
  25. package/src/cli/pane-functions/catalog.ts +16 -4
  26. package/src/cli/pane-functions/discovery.ts +1 -0
  27. package/src/cli/pane-functions/headless.test.ts +247 -0
  28. package/src/cli/pane-functions/headless.ts +423 -0
  29. package/src/cli/pane-functions/index.test.ts +9 -0
  30. package/src/cli/pane-functions/index.ts +6 -2
  31. package/src/cli/pane-functions/options.ts +27 -2
  32. package/src/cli/pane-functions/report.ts +4 -0
  33. package/src/cli/pane-functions/resolver.ts +26 -8
  34. package/src/cli/pane-functions/screenshot.test.ts +76 -0
  35. package/src/cli/pane-functions/screenshot.ts +236 -20
  36. package/src/cli/registry.ts +0 -22
  37. package/src/cli/restore-plugins.ts +34 -0
  38. package/src/cli/result.ts +7 -10
  39. package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
  40. package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
  41. package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
  42. package/src/components/chart/composite/composite-chart.test.tsx +68 -20
  43. package/src/components/chart/composite/composite-chart.tsx +301 -204
  44. package/src/components/chart/composite/format.ts +8 -2
  45. package/src/components/chart/composite/interactions.test.ts +68 -102
  46. package/src/components/chart/composite/interactions.ts +214 -264
  47. package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
  48. package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
  49. package/src/components/chart/composite/scene.ts +57 -3
  50. package/src/components/chart/composite/text-renderer.ts +3 -2
  51. package/src/components/chart/composite/time-scale.ts +138 -57
  52. package/src/components/chart/composite/types.ts +25 -1
  53. package/src/components/chart/core/data.ts +9 -13
  54. package/src/components/chart/core/date-window.ts +7 -1
  55. package/src/components/chart/core/pointer.ts +3 -0
  56. package/src/components/chart/core/types.ts +0 -45
  57. package/src/components/chart/native/chart-rasterizer.ts +0 -1
  58. package/src/components/chart/native/kitty/index.test.ts +0 -188
  59. package/src/components/chart/native/raster/crosshair.ts +6 -5
  60. package/src/components/chart/native/raster/primitives.ts +0 -23
  61. package/src/components/chart/native/renderer-selection.ts +81 -31
  62. package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
  63. package/src/components/chart/static/chart-surface.tsx +150 -0
  64. package/src/components/chart/static/index.ts +8 -0
  65. package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
  66. package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
  67. package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
  68. package/src/components/chart/static/series.ts +49 -0
  69. package/src/components/command-bar/action-runtime.ts +1 -3
  70. package/src/components/command-bar/assist/model.test.ts +17 -2
  71. package/src/components/command-bar/assist/model.ts +40 -9
  72. package/src/components/command-bar/commands/direct/index.ts +1 -1
  73. package/src/components/command-bar/commands/direct/runtime.ts +1 -1
  74. package/src/components/command-bar/commands/registry.ts +0 -11
  75. package/src/components/command-bar/helpers.test.ts +2 -2
  76. package/src/components/command-bar/helpers.ts +2 -6
  77. package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
  78. package/src/components/command-bar/list/badge.test.ts +54 -0
  79. package/src/components/command-bar/list/badge.ts +62 -0
  80. package/src/components/command-bar/list/model.test.ts +103 -0
  81. package/src/components/command-bar/list/model.ts +61 -4
  82. package/src/components/command-bar/list/view.test.tsx +113 -0
  83. package/src/components/command-bar/list/view.tsx +147 -131
  84. package/src/components/command-bar/pane-templates/workflow.ts +1 -1
  85. package/src/components/command-bar/panel/body.tsx +51 -65
  86. package/src/components/command-bar/panel/index.tsx +48 -13
  87. package/src/components/command-bar/panel/layout.test.ts +139 -0
  88. package/src/components/command-bar/panel/layout.ts +65 -28
  89. package/src/components/command-bar/panel/native-surface.ts +24 -0
  90. package/src/components/command-bar/panel/prompt-binding.ts +54 -0
  91. package/src/components/command-bar/panel/runtime.ts +6 -3
  92. package/src/components/command-bar/panel/state.ts +41 -7
  93. package/src/components/command-bar/panel/types.ts +7 -1
  94. package/src/components/command-bar/routes/root/provider-search.ts +34 -15
  95. package/src/components/command-bar/routes/root/results.test.ts +161 -0
  96. package/src/components/command-bar/routes/root/results.ts +30 -28
  97. package/src/components/command-bar/routes/root/runtime.ts +37 -20
  98. package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
  99. package/src/components/command-bar/routes/root/search-providers.ts +205 -0
  100. package/src/components/command-bar/routes/root/selection.ts +1 -12
  101. package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
  102. package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
  103. package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
  104. package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
  105. package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
  106. package/src/components/command-bar/routing/actions.ts +1 -81
  107. package/src/components/command-bar/routing/launch-request.ts +1 -1
  108. package/src/components/command-bar/routing/list-state.ts +6 -21
  109. package/src/components/command-bar/selection-runtime.ts +1 -1
  110. package/src/components/command-bar/surface/assist.test.tsx +14 -10
  111. package/src/components/command-bar/surface/environment.ts +8 -1
  112. package/src/components/command-bar/surface/index.test.tsx +84 -68
  113. package/src/components/command-bar/surface/index.tsx +33 -36
  114. package/src/components/command-bar/surface/test-harness.tsx +15 -3
  115. package/src/components/command-bar/theme-picker.tsx +43 -15
  116. package/src/components/command-bar/view-model.test.ts +53 -8
  117. package/src/components/command-bar/view-model.ts +43 -14
  118. package/src/components/command-bar/workflow/tickers.ts +1 -0
  119. package/src/components/command-bar/workflow/types.ts +1 -1
  120. package/src/components/data-table/export.test.ts +21 -0
  121. package/src/components/data-table/export.ts +21 -0
  122. package/src/components/data-table/view.test.tsx +45 -6
  123. package/src/components/data-table/view.tsx +55 -9
  124. package/src/components/index.ts +10 -2
  125. package/src/components/input-search-bar.test.tsx +36 -2
  126. package/src/components/input-search-bar.tsx +18 -1
  127. package/src/components/layout/header.test.tsx +57 -10
  128. package/src/components/layout/header.tsx +292 -149
  129. package/src/components/layout/market-summary.test.ts +81 -0
  130. package/src/components/layout/market-summary.ts +109 -0
  131. package/src/components/layout/shell/chrome.ts +96 -1
  132. package/src/components/layout/shell/index.tsx +11 -3
  133. package/src/components/layout/shell/menu.ts +9 -0
  134. package/src/components/layout/shell/pane/actions.ts +29 -0
  135. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  136. package/src/components/layout/shell/shortcuts.ts +3 -0
  137. package/src/components/layout/status-bar.test.tsx +9 -8
  138. package/src/components/layout/status-bar.tsx +85 -54
  139. package/src/components/onboarding/onboarding-frame.tsx +2 -26
  140. package/src/components/price-sparkline/index.ts +61 -34
  141. package/src/components/price-sparkline/view.tsx +1 -1
  142. package/src/components/sign-in-gate.test.tsx +128 -0
  143. package/src/components/sign-in-gate.tsx +149 -0
  144. package/src/components/speedometer-gauge/desktop.tsx +2 -12
  145. package/src/components/ticker/badge/list.tsx +17 -3
  146. package/src/components/ticker/list-table-view.tsx +6 -3
  147. package/src/components/ui/data-table/index.tsx +22 -4
  148. package/src/components/ui/data-table/opentui/index.tsx +165 -120
  149. package/src/components/ui/frame.tsx +28 -0
  150. package/src/components/ui/index.ts +3 -0
  151. package/src/components/ui/native-select.tsx +20 -10
  152. package/src/components/ui/select-button.tsx +138 -0
  153. package/src/components/ui/status.tsx +2 -2
  154. package/src/components/ui/table-layout.ts +1 -1
  155. package/src/components/use-double-click-activation.ts +7 -3
  156. package/src/core/app-services.ts +3 -3
  157. package/src/data/config/chart-settings.ts +4 -4
  158. package/src/data/config/layout.ts +12 -6
  159. package/src/data/config/store/index.test.ts +97 -0
  160. package/src/data/config/store/migrations.ts +41 -0
  161. package/src/data/config/store/normalize.ts +5 -3
  162. package/src/i18n/es.ts +6 -2
  163. package/src/i18n/ja.ts +6 -2
  164. package/src/i18n/ko.ts +6 -2
  165. package/src/i18n/zh-cn.ts +6 -2
  166. package/src/i18n/zh-tw.ts +6 -2
  167. package/src/layout-marketplace/gallery-terminal.tsx +126 -96
  168. package/src/layout-marketplace/gallery.test.tsx +65 -1
  169. package/src/layout-marketplace/pane-imagery.ts +1 -0
  170. package/src/layout-marketplace/payload.test.ts +17 -0
  171. package/src/layout-marketplace/payload.ts +40 -5
  172. package/src/market-data/coordinator/index.ts +3 -2
  173. package/src/market-data/coordinator/quotes.ts +173 -66
  174. package/src/market-data/coordinator/subscriptions.test.ts +215 -37
  175. package/src/pane-settings.ts +1 -1
  176. package/src/plugins/builtin/analytics/pane-model.ts +1 -1
  177. package/src/plugins/builtin/analytics/view.tsx +1 -1
  178. package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
  179. package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
  180. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
  181. package/src/plugins/builtin/chart-composer/cli-options.ts +12 -9
  182. package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
  183. package/src/plugins/builtin/chart-composer/settings.ts +2 -13
  184. package/src/plugins/builtin/chat/content/composer.tsx +8 -4
  185. package/src/plugins/builtin/cloud/auth-dialog.tsx +14 -228
  186. package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
  187. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  188. package/src/plugins/builtin/composite-plugins.ts +16 -1
  189. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  190. package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
  191. package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
  192. package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
  193. package/src/plugins/builtin/credit-conditions/index.tsx +2 -21
  194. package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
  195. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  196. package/src/plugins/builtin/earnings/index.tsx +1 -0
  197. package/src/plugins/builtin/earnings-calls/data.ts +223 -0
  198. package/src/plugins/builtin/earnings-calls/format.ts +49 -0
  199. package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
  200. package/src/plugins/builtin/earnings-calls/pane.tsx +756 -0
  201. package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
  202. package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
  203. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +334 -0
  204. package/src/plugins/builtin/econ/detail-view.tsx +1 -2
  205. package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
  206. package/src/plugins/builtin/econ/index.tsx +3 -1
  207. package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
  208. package/src/plugins/builtin/econ-statistics/client.ts +92 -0
  209. package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
  210. package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
  211. package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
  212. package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
  213. package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
  214. package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
  215. package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
  216. package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
  217. package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
  218. package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
  219. package/src/plugins/builtin/econ-statistics/view.ts +90 -0
  220. package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
  221. package/src/plugins/builtin/fear-greed/data.ts +6 -1
  222. package/src/plugins/builtin/futures/index.tsx +1 -0
  223. package/src/plugins/builtin/fx-matrix/index.tsx +1 -0
  224. package/src/plugins/builtin/help/index.tsx +4 -0
  225. package/src/plugins/builtin/holders/index.tsx +1 -0
  226. package/src/plugins/builtin/insider/index.tsx +1 -0
  227. package/src/plugins/builtin/ipo-calendar/index.tsx +1 -0
  228. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  229. package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
  230. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  231. package/src/plugins/builtin/market-movers/index.tsx +1 -0
  232. package/src/plugins/builtin/market-valuation/align.ts +122 -0
  233. package/src/plugins/builtin/market-valuation/cache.ts +32 -0
  234. package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
  235. package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
  236. package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
  237. package/src/plugins/builtin/market-valuation/client.ts +116 -0
  238. package/src/plugins/builtin/market-valuation/defs.ts +251 -0
  239. package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
  240. package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
  241. package/src/plugins/builtin/market-valuation/headless.ts +170 -0
  242. package/src/plugins/builtin/market-valuation/index.tsx +64 -0
  243. package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
  244. package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
  245. package/src/plugins/builtin/market-valuation/model.ts +8 -0
  246. package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
  247. package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
  248. package/src/plugins/builtin/market-valuation/series.ts +12 -0
  249. package/src/plugins/builtin/market-valuation/settings.ts +38 -0
  250. package/src/plugins/builtin/market-valuation/sources.ts +130 -0
  251. package/src/plugins/builtin/market-valuation/trend.ts +89 -0
  252. package/src/plugins/builtin/market-valuation/view.ts +137 -0
  253. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
  254. package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
  255. package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
  256. package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
  257. package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
  258. package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
  259. package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
  260. package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
  261. package/src/plugins/builtin/news/wire/index.tsx +31 -44
  262. package/src/plugins/builtin/options/analytics.test.ts +83 -0
  263. package/src/plugins/builtin/options/analytics.ts +110 -0
  264. package/src/plugins/builtin/options/index.tsx +18 -2
  265. package/src/plugins/builtin/options/table.ts +121 -51
  266. package/src/plugins/builtin/options/types.ts +22 -15
  267. package/src/plugins/builtin/options/view.test.tsx +53 -1
  268. package/src/plugins/builtin/options/view.tsx +92 -41
  269. package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
  270. package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
  271. package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
  272. package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
  273. package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
  274. package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
  275. package/src/plugins/builtin/plugin-module.test.ts +2 -4
  276. package/src/plugins/builtin/plugin-module.ts +9 -8
  277. package/src/plugins/builtin/portfolio-list/index.tsx +1 -0
  278. package/src/plugins/builtin/research/index.tsx +4 -0
  279. package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
  280. package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
  281. package/src/plugins/builtin/research-search/data.ts +109 -0
  282. package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
  283. package/src/plugins/builtin/research-search/document-view.tsx +254 -0
  284. package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
  285. package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
  286. package/src/plugins/builtin/research-search/index.tsx +85 -0
  287. package/src/plugins/builtin/research-search/model.test.ts +40 -0
  288. package/src/plugins/builtin/research-search/model.ts +291 -0
  289. package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
  290. package/src/plugins/builtin/research-search/pane.tsx +678 -0
  291. package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
  292. package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
  293. package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
  294. package/src/plugins/builtin/research-search/snippet.ts +134 -0
  295. package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
  296. package/src/plugins/builtin/scanner/flow-model.ts +29 -1
  297. package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
  298. package/src/plugins/builtin/scanner/index.ts +2 -0
  299. package/src/plugins/builtin/sec/index.tsx +1 -0
  300. package/src/plugins/builtin/sectors/index.tsx +1 -0
  301. package/src/plugins/builtin/shared/series-cache.ts +93 -0
  302. package/src/plugins/builtin/short-interest/index.tsx +1 -0
  303. package/src/plugins/builtin/short-interest/pane.tsx +1 -1
  304. package/src/plugins/builtin/thirteenf/index.tsx +1 -0
  305. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +3 -5
  306. package/src/plugins/builtin/ticker-detail/index.tsx +2 -14
  307. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
  308. package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
  309. package/src/plugins/builtin/volatility/index.tsx +1 -1
  310. package/src/plugins/builtin/world-indices/index.tsx +1 -0
  311. package/src/plugins/builtin/world-venue-map/index.ts +27 -0
  312. package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
  313. package/src/plugins/builtin/world-venue-map/map.tsx +481 -0
  314. package/src/plugins/builtin/world-venue-map/model.test.ts +85 -0
  315. package/src/plugins/builtin/world-venue-map/model.ts +268 -0
  316. package/src/plugins/builtin/world-venue-map/pane.tsx +300 -0
  317. package/src/plugins/builtin/world-venue-map/world-outlines.ts +2 -0
  318. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  319. package/src/plugins/bundle.test.ts +109 -0
  320. package/src/plugins/bundle.ts +139 -0
  321. package/src/plugins/catalog-backend.ts +6 -44
  322. package/src/plugins/catalog-browser.test.ts +1 -0
  323. package/src/plugins/catalog-browser.ts +12 -0
  324. package/src/plugins/catalog-manifest.test.ts +43 -0
  325. package/src/plugins/catalog-manifest.ts +92 -0
  326. package/src/plugins/catalog-ui.ts +20 -4
  327. package/src/plugins/catalog.ts +9 -2
  328. package/src/plugins/current-target.ts +28 -0
  329. package/src/plugins/host-contract.ts +37 -0
  330. package/src/plugins/host-link.ts +158 -0
  331. package/src/plugins/host-modules.ts +61 -0
  332. package/src/plugins/loader.ts +41 -22
  333. package/src/plugins/ownership.ts +1 -0
  334. package/src/plugins/prediction-markets/index.tsx +1 -0
  335. package/src/plugins/registry/context.ts +3 -0
  336. package/src/plugins/registry/contributions.ts +27 -0
  337. package/src/plugins/registry/index.test.ts +25 -0
  338. package/src/plugins/registry/index.ts +8 -0
  339. package/src/plugins/registry/pane-settings.test.ts +53 -0
  340. package/src/plugins/registry/pane-settings.ts +29 -3
  341. package/src/plugins/seed.test.ts +83 -0
  342. package/src/plugins/seed.ts +80 -0
  343. package/src/public/broker.ts +31 -0
  344. package/src/public/market-data.ts +9 -0
  345. package/src/public/pane-hooks.ts +57 -0
  346. package/src/public/public-api.test.ts +285 -0
  347. package/src/public/react.ts +62 -0
  348. package/src/public/test-support.ts +23 -0
  349. package/src/public/time-series.ts +8 -0
  350. package/src/public/utils.ts +77 -0
  351. package/src/remote/layout-helpers.ts +6 -8
  352. package/src/remote/resources.ts +2 -0
  353. package/src/remote/schema.ts +1 -0
  354. package/src/remote/semantic-tree.test.tsx +105 -0
  355. package/src/remote/semantic-tree.tsx +53 -35
  356. package/src/renderers/browser/main.tsx +6 -0
  357. package/src/renderers/browser/ui-host.tsx +11 -0
  358. package/src/renderers/cloudflare/worker.test.ts +19 -0
  359. package/src/renderers/cloudflare/worker.ts +1 -1
  360. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -2
  361. package/src/renderers/electrobun/bun/external-plugins.ts +140 -0
  362. package/src/renderers/electrobun/bun/index.ts +6 -2
  363. package/src/renderers/electrobun/shared/protocol.ts +25 -0
  364. package/src/renderers/electrobun/view/build-assets.ts +0 -1
  365. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +51 -1
  366. package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
  367. package/src/renderers/electrobun/view/external-plugins.ts +63 -0
  368. package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
  369. package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
  370. package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
  371. package/src/renderers/electrobun/view/host/mouse.ts +16 -2
  372. package/src/renderers/electrobun/view/main.tsx +27 -2
  373. package/src/renderers/electrobun/view/styles.css +5 -0
  374. package/src/renderers/electrobun/view/toast-host.tsx +18 -0
  375. package/src/renderers/electrobun/view/ui-host.tsx +3 -0
  376. package/src/renderers/opentui/chart-surface.tsx +5 -1
  377. package/src/renderers/opentui/host.tsx +20 -2
  378. package/src/renderers/opentui/interaction-performance.test.ts +79 -0
  379. package/src/renderers/opentui/interaction-performance.ts +188 -0
  380. package/src/renderers/opentui/start.tsx +26 -1
  381. package/src/renderers/opentui/test-utils.tsx +13 -0
  382. package/src/renderers/opentui/toast-host.tsx +26 -2
  383. package/src/renderers/opentui/ui-host.tsx +1 -0
  384. package/src/sources/provider-router/search.test.ts +191 -0
  385. package/src/sources/provider-router/search.ts +92 -39
  386. package/src/state/app/context/index.tsx +2 -0
  387. package/src/state/app/context/selector.test.tsx +33 -1
  388. package/src/state/hooks/quote-streaming.test.tsx +93 -0
  389. package/src/state/hooks/quote-streaming.ts +106 -30
  390. package/src/state/pane-table-export-registry.ts +50 -0
  391. package/src/theme/color-utils.ts +1 -1
  392. package/src/theme/colors.ts +50 -0
  393. package/src/theme/themes.test.ts +15 -2
  394. package/src/theme/themes.ts +19 -1
  395. package/src/tickers/search/index.test.ts +26 -0
  396. package/src/tickers/search/index.ts +42 -17
  397. package/src/tickers/search/ranking.ts +14 -0
  398. package/src/time-series/chart-data.test.ts +216 -0
  399. package/src/time-series/range.ts +12 -0
  400. package/src/time-series/resolution.test.ts +25 -26
  401. package/src/time-series/resolution.ts +44 -25
  402. package/src/time-series/resolve.ts +7 -0
  403. package/src/time-series/spec.ts +5 -5
  404. package/src/time-series/types.ts +3 -1
  405. package/src/time-series/use-chart-resolution.ts +5 -0
  406. package/src/types/config.ts +123 -3
  407. package/src/types/data-provider.ts +11 -0
  408. package/src/types/headless.ts +161 -0
  409. package/src/types/plugin.ts +104 -20
  410. package/src/ui/host.tsx +19 -1
  411. package/src/ui/index.tsx +1 -0
  412. package/src/ui/toast.tsx +8 -4
  413. package/src/utils/csv.test.ts +13 -0
  414. package/src/utils/csv.ts +37 -0
  415. package/src/utils/format.ts +35 -0
  416. package/src/utils/fuzzy-search.test.ts +94 -0
  417. package/src/utils/fuzzy-search.ts +81 -22
  418. package/src/utils/save-text-file.test.ts +24 -0
  419. package/src/utils/save-text-file.ts +36 -0
  420. package/src/utils/shortcut-labels.ts +14 -0
  421. package/src/components/chart/core/axis-format.ts +0 -157
  422. package/src/components/chart/core/index.test.ts +0 -1075
  423. package/src/components/chart/core/projection.ts +0 -357
  424. package/src/components/chart/core/render-utils.ts +0 -4
  425. package/src/components/chart/core/renderer.ts +0 -244
  426. package/src/components/chart/core/scene.ts +0 -180
  427. package/src/components/chart/core/time-axis.ts +0 -330
  428. package/src/components/chart/cursor-motion.ts +0 -47
  429. package/src/components/chart/indicators/types.ts +0 -26
  430. package/src/components/chart/market-session.ts +0 -176
  431. package/src/components/chart/multi-line/renderer.test.ts +0 -83
  432. package/src/components/chart/multi-line/renderer.ts +0 -161
  433. package/src/components/chart/multi-line/scene.ts +0 -188
  434. package/src/components/chart/native/raster/layout.ts +0 -129
  435. package/src/components/chart/native/raster/price-chart.ts +0 -244
  436. package/src/components/chart/rendering/chart-draw.ts +0 -270
  437. package/src/components/chart/rendering/geometry.ts +0 -95
  438. package/src/components/chart/rendering/pixel-buffer.ts +0 -267
  439. package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
  440. package/src/components/chart/scatter-chart-renderer.ts +0 -223
  441. package/src/components/chart/static/chart/surface.tsx +0 -358
  442. package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
  443. package/src/components/command-bar/routes/root/article-results.ts +0 -55
  444. package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
  445. package/src/plugins/builtin/news/wire/article-search.ts +0 -140
  446. package/src/plugins/builtin/substack/api/auth.ts +0 -185
  447. package/src/plugins/builtin/substack/api/cache.ts +0 -82
  448. package/src/plugins/builtin/substack/api/loaders.ts +0 -170
  449. package/src/plugins/builtin/substack/api/store.ts +0 -186
  450. package/src/plugins/builtin/substack/api/types.ts +0 -42
  451. package/src/plugins/builtin/substack/api.test.ts +0 -298
  452. package/src/plugins/builtin/substack/article-detail.tsx +0 -397
  453. package/src/plugins/builtin/substack/article-stack.tsx +0 -133
  454. package/src/plugins/builtin/substack/content.ts +0 -478
  455. package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
  456. package/src/plugins/builtin/substack/index.tsx +0 -49
  457. package/src/plugins/builtin/substack/login-view.tsx +0 -126
  458. package/src/plugins/builtin/substack/model.test.ts +0 -155
  459. package/src/plugins/builtin/substack/normalize.ts +0 -343
  460. package/src/plugins/builtin/substack/pane-footer.ts +0 -52
  461. package/src/plugins/builtin/substack/pane-state.ts +0 -170
  462. package/src/plugins/builtin/substack/pane.tsx +0 -547
  463. package/src/plugins/builtin/substack/read-state.ts +0 -30
  464. package/src/plugins/builtin/substack/table.ts +0 -111
  465. package/src/plugins/builtin/substack/types.ts +0 -71
  466. package/src/plugins/builtin/substack/utils.ts +0 -131
  467. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
  468. package/src/plugins/ibkr/account-cache.test.ts +0 -102
  469. package/src/plugins/ibkr/account-cache.ts +0 -24
  470. package/src/plugins/ibkr/broker-adapter.ts +0 -286
  471. package/src/plugins/ibkr/config.test.ts +0 -79
  472. package/src/plugins/ibkr/config.ts +0 -250
  473. package/src/plugins/ibkr/dialogs.tsx +0 -40
  474. package/src/plugins/ibkr/flex/index.test.ts +0 -264
  475. package/src/plugins/ibkr/flex/index.ts +0 -367
  476. package/src/plugins/ibkr/flex/statement-client.ts +0 -235
  477. package/src/plugins/ibkr/fundamental-parser.ts +0 -149
  478. package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
  479. package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
  480. package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
  481. package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
  482. package/src/plugins/ibkr/gateway/connection.ts +0 -152
  483. package/src/plugins/ibkr/gateway/contracts.ts +0 -132
  484. package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
  485. package/src/plugins/ibkr/gateway/helpers.ts +0 -33
  486. package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
  487. package/src/plugins/ibkr/gateway/history.ts +0 -120
  488. package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
  489. package/src/plugins/ibkr/gateway/market-data.ts +0 -345
  490. package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
  491. package/src/plugins/ibkr/gateway/orders.ts +0 -63
  492. package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
  493. package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
  494. package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
  495. package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
  496. package/src/plugins/ibkr/gateway/selection.ts +0 -51
  497. package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
  498. package/src/plugins/ibkr/gateway/service/index.ts +0 -429
  499. package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
  500. package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
  501. package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
  502. package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
  503. package/src/plugins/ibkr/gateway/service/native.ts +0 -543
  504. package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
  505. package/src/plugins/ibkr/gateway/types.ts +0 -27
  506. package/src/plugins/ibkr/index.test.tsx +0 -162
  507. package/src/plugins/ibkr/index.tsx +0 -159
  508. package/src/plugins/ibkr/instance-selection.test.ts +0 -120
  509. package/src/plugins/ibkr/instance-selection.ts +0 -36
  510. package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
  511. package/src/plugins/ibkr/portfolio-performance.ts +0 -129
  512. package/src/plugins/ibkr/trade/badge.tsx +0 -28
  513. package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
  514. package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
  515. package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
  516. package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
  517. package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
  518. package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
  519. package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
  520. package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
  521. package/src/plugins/ibkr/trade/tab/model.ts +0 -159
  522. package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
  523. package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
  524. package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
  525. package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
  526. package/src/plugins/ibkr/trade/utils.test.ts +0 -175
  527. package/src/plugins/ibkr/trade/utils.ts +0 -144
  528. package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
  529. package/src/plugins/ibkr/trading/pane.tsx +0 -332
  530. package/src/plugins/ibkr/trading/state.test.ts +0 -109
  531. package/src/plugins/ibkr/trading/state.ts +0 -335
@@ -12,6 +12,7 @@ import type { ResultItem } from "../../list/model";
12
12
  import {
13
13
  buildTickerSearchCacheKey,
14
14
  createQuickLookTickerCandidates,
15
+ formatInstrumentBadge,
15
16
  } from "./results";
16
17
 
17
18
  interface UseCommandBarTickerSearchActionsOptions {
@@ -47,13 +48,20 @@ export function useCommandBarTickerSearchActions({
47
48
 
48
49
  const mapTickerSearchCandidateToResultItem = useCallback((candidate: TickerSearchCandidate): ResultItem => {
49
50
  const detail = formatTickerSearchDetail(candidate);
50
- const right = formatTickerSearchRight(candidate);
51
+ // The class lives in the badge, so the trailing text carries only the
52
+ // venue rather than saying "Equity" twice on one row. An unclassified
53
+ // instrument has no class to show, so the badge column lifts its venue
54
+ // instead (a short exchange code qualifies on its own) and the trailing
55
+ // text goes quiet rather than repeating it.
56
+ const badge = formatInstrumentBadge(candidate);
57
+ const right = candidate.exchangeLabel || candidate.primaryExchangeLabel || candidate.right || undefined;
51
58
 
52
59
  if (candidate.kind === "ticker" && candidate.ticker) {
53
60
  return {
54
61
  id: candidate.id,
55
62
  label: candidate.label,
56
63
  detail,
64
+ badge,
57
65
  right,
58
66
  category: candidate.category,
59
67
  kind: "ticker",
@@ -72,6 +80,7 @@ export function useCommandBarTickerSearchActions({
72
80
  id: candidate.id,
73
81
  label: candidate.label,
74
82
  detail,
83
+ badge,
75
84
  right,
76
85
  category: candidate.category,
77
86
  kind: "search",
@@ -145,14 +154,6 @@ export function useCommandBarTickerSearchActions({
145
154
  };
146
155
  }
147
156
 
148
- function formatTickerSearchRight(candidate: TickerSearchCandidate): string | undefined {
149
- const assetClass = formatInstrumentClass(candidate.instrumentClass);
150
- const exchange = candidate.exchangeLabel || candidate.primaryExchangeLabel || candidate.right || "";
151
- if (!exchange) return assetClass;
152
- if (assetClass === "Other") return exchange;
153
- return `${assetClass} ${exchange}`;
154
- }
155
-
156
157
  function formatTickerSearchDetail(candidate: TickerSearchCandidate): string {
157
158
  const metadata = [
158
159
  formatInstrumentClass(candidate.instrumentClass),
@@ -1,6 +1,6 @@
1
1
  import { expect, test } from "bun:test";
2
2
  import type { ResultItem } from "../../list/model";
3
- import { mergeTickerSearchResultItems } from "./results";
3
+ import { formatInstrumentBadge, mergePlainRootTickerResults, mergeTickerSearchResultItems } from "./results";
4
4
 
5
5
  function resultItem(id: string, label: string, right: string, kind: ResultItem["kind"] = "ticker"): ResultItem {
6
6
  return {
@@ -32,3 +32,56 @@ test("keeps completed ticker-search results authoritative over provisional rows"
32
32
  const noResults = resultItem("no-results", "No matches for Apple", "", "info");
33
33
  expect(mergeTickerSearchResultItems("Apple", [noResults], [])).toEqual([noResults]);
34
34
  });
35
+
36
+ test("folds a plain query's symbol hits into one capped Instruments section behind the local rows", () => {
37
+ const pane: ResultItem = {
38
+ id: "pane:news",
39
+ label: "News",
40
+ detail: "",
41
+ category: "Panes",
42
+ kind: "action",
43
+ action: () => {},
44
+ };
45
+ const providerItems = [
46
+ resultItem("search:NVDA", "NVDA", "Equity NASDAQ", "search"),
47
+ // The saved row for the same symbol carries a different badge; one row per symbol.
48
+ resultItem("goto:NVDA", "NVDA", "NASDAQ"),
49
+ resultItem("search:NVDA.MX", "NVDA.MX", "Equity BMV", "search"),
50
+ resultItem("search:NVD.DE", "NVD.DE", "Equity XETRA", "search"),
51
+ resultItem("search:NVDL", "NVDL", "Fund NASDAQ", "search"),
52
+ resultItem("search:NVDS", "NVDS", "Fund NASDAQ", "search"),
53
+ resultItem("search:NVDX", "NVDX", "Fund NASDAQ", "search"),
54
+ resultItem("search-error", "Search failed", "", "info"),
55
+ ];
56
+
57
+ const merged = mergePlainRootTickerResults("nvda", providerItems, [pane]);
58
+
59
+ expect(merged.map((item) => [item.id, item.category])).toEqual([
60
+ ["search:NVDA", "Exact Match"],
61
+ ["pane:news", "Panes"],
62
+ ["search:NVDA.MX", "Instruments"],
63
+ ["search:NVD.DE", "Instruments"],
64
+ ["search:NVDL", "Instruments"],
65
+ ["search:NVDS", "Instruments"],
66
+ ]);
67
+ expect(merged.find((item) => item.id === "search:NVDA.MX")?.right).toBe("Equity BMV");
68
+ });
69
+
70
+ /**
71
+ * The class tag is what the eye sorts instruments by, so the mapping from a
72
+ * provider's type strings has to stay put: an exchange-traded fund is not a
73
+ * mutual fund, and an unclassified instrument gets no tag rather than a stand-in.
74
+ */
75
+ test("names the instrument class for the badge column", () => {
76
+ const search = (type: string) => ({ providerId: "yahoo", symbol: "X", name: "X", exchange: "NYQ", type });
77
+ expect(formatInstrumentBadge({ instrumentClass: "equity" })).toBe("EQ");
78
+ expect(formatInstrumentBadge({ instrumentClass: "fund", result: search("ETF") })).toBe("ETF");
79
+ expect(formatInstrumentBadge({ instrumentClass: "fund", result: search("ETN") })).toBe("ETF");
80
+ expect(formatInstrumentBadge({ instrumentClass: "fund", result: search("MUTUALFUND") })).toBe("FUND");
81
+ expect(formatInstrumentBadge({
82
+ instrumentClass: "fund",
83
+ ticker: { metadata: { ticker: "VTI", assetCategory: "ETF" } } as never,
84
+ })).toBe("ETF");
85
+ expect(formatInstrumentBadge({ instrumentClass: "derivative" })).toBe("DERIV");
86
+ expect(formatInstrumentBadge({ instrumentClass: "other", result: search("INDEX") })).toBeUndefined();
87
+ });
@@ -19,6 +19,33 @@ export function createQuickLookTickerCandidates(tickers: Iterable<TickerRecord>)
19
19
  return createLocalTickerSearchCandidates(tickers, new Map(), QUICK_LOOK_TICKER_SEARCH_OPTIONS);
20
20
  }
21
21
 
22
+ /** Provider type or saved asset category, whichever classified the candidate. */
23
+ function rawInstrumentType(candidate: Pick<TickerSearchCandidate, "result" | "ticker">): string {
24
+ return candidate.result?.brokerContract?.secType
25
+ || candidate.result?.type
26
+ || candidate.ticker?.metadata.assetCategory
27
+ || "";
28
+ }
29
+
30
+ /**
31
+ * Class tag for the badge column. An unclassified instrument gets none: the
32
+ * row lifts its exchange code there instead when the code is short enough.
33
+ */
34
+ export function formatInstrumentBadge(
35
+ candidate: Pick<TickerSearchCandidate, "instrumentClass" | "result" | "ticker">,
36
+ ): string | undefined {
37
+ switch (candidate.instrumentClass) {
38
+ case "equity":
39
+ return "EQ";
40
+ case "fund":
41
+ return /\bET[FNP]\b/i.test(rawInstrumentType(candidate)) ? "ETF" : "FUND";
42
+ case "derivative":
43
+ return "DERIV";
44
+ case "other":
45
+ return undefined;
46
+ }
47
+ }
48
+
22
49
  export function normalizeCommandTickerSearchText(value: string): string {
23
50
  return value.trim().toUpperCase().replace(/[^A-Z0-9]+/g, "");
24
51
  }
@@ -53,29 +80,43 @@ export function mergeTickerSearchResultItems(
53
80
  : item);
54
81
  }
55
82
 
83
+ export const ROOT_INSTRUMENTS_CATEGORY = "Instruments";
84
+
85
+ /** Enough to surface the listing the user means without burying the sections below. */
86
+ const ROOT_INSTRUMENTS_LIMIT = 5;
87
+
88
+ function isInstrumentItem(item: ResultItem): boolean {
89
+ return item.kind === "ticker" || item.kind === "search";
90
+ }
91
+
92
+ /**
93
+ * Fold symbol-search rows into a plain root query's list. An exact symbol hit
94
+ * is promoted ahead of everything; the rest collapse into one Instruments
95
+ * section with one row per symbol, since the listing split of the DES route
96
+ * is noise next to panes and commands. Info rows ("no matches", "search
97
+ * failed") are dropped: the instruments are an extra here, never the answer.
98
+ */
56
99
  export function mergePlainRootTickerResults(
57
100
  query: string,
58
101
  providerItems: ResultItem[],
59
102
  rootItems: ResultItem[],
60
103
  ): ResultItem[] {
61
- const merged: ResultItem[] = [];
62
- const seen = new Set<string>();
63
- const addItem = (item: ResultItem, options?: { skipInfo?: boolean }) => {
64
- if (options?.skipInfo && item.kind === "info") return;
65
- const key = (item.kind === "ticker" || item.kind === "search")
66
- ? `${item.kind}:${item.label.trim().toUpperCase()}:${(item.right || "").trim().toUpperCase()}`
67
- : `${item.kind}:${item.id}`;
68
- if (seen.has(key)) return;
69
- seen.add(key);
70
- merged.push(item);
71
- };
104
+ const seenSymbols = new Set<string>();
105
+ const instruments: ResultItem[] = [];
106
+ for (const item of providerItems) {
107
+ if (!isInstrumentItem(item)) continue;
108
+ const symbol = item.label.trim().toUpperCase();
109
+ if (seenSymbols.has(symbol)) continue;
110
+ seenSymbols.add(symbol);
111
+ instruments.push(item);
112
+ if (instruments.length >= ROOT_INSTRUMENTS_LIMIT) break;
113
+ }
114
+ if (instruments.length === 0) return rootItems;
72
115
 
73
- providerItems
74
- .filter((item) => item.category === "Exact Match" || isExactTickerResultMatch(item, query))
75
- .forEach((item) => addItem(item, { skipInfo: true }));
76
- rootItems.forEach((item) => addItem(item));
77
- providerItems
78
- .filter((item) => item.category !== "Exact Match" && !isExactTickerResultMatch(item, query))
79
- .forEach((item) => addItem(item, { skipInfo: true }));
80
- return merged.length > 0 ? merged : rootItems;
116
+ const isExact = (item: ResultItem) => item.category === "Exact Match" || isExactTickerResultMatch(item, query);
117
+ return [
118
+ ...instruments.filter(isExact).map((item) => ({ ...item, category: "Exact Match" })),
119
+ ...rootItems,
120
+ ...instruments.filter((item) => !isExact(item)).map((item) => ({ ...item, category: ROOT_INSTRUMENTS_CATEGORY })),
121
+ ];
81
122
  }
@@ -95,6 +95,7 @@ export function useTickerSearchRouteResults(options: {
95
95
  dataProvider,
96
96
  searchContext: {
97
97
  preferBroker: true,
98
+ interactive: true,
98
99
  brokerId: brokerId ?? undefined,
99
100
  brokerInstanceId: brokerInstanceId ?? undefined,
100
101
  },
@@ -26,7 +26,7 @@ import type {
26
26
  type CloseAll = (options?: { revertThemePreview?: boolean }) => void;
27
27
  type Notify = (body: string, options?: { type?: "info" | "success" | "error" }) => void;
28
28
  type OpenModeRoute = (
29
- screen: "ticker-search" | "plugins" | "layout",
29
+ screen: "ticker-search" | "layout",
30
30
  initialQuery?: string,
31
31
  payload?: Record<string, unknown>,
32
32
  ) => void;
@@ -80,15 +80,6 @@ export function useCommandBarRouteActions({
80
80
  stateRef,
81
81
  updateTopRoute,
82
82
  }: UseCommandBarRouteActionsOptions) {
83
- const buildPluginItems = useCallback((query: string): ResultItem[] => buildPluginToggleItems({
84
- disabledPlugins: state.config.disabledPlugins || [],
85
- dispatch,
86
- getConfig: () => stateRef.current.config,
87
- persistConfig,
88
- pluginRegistry,
89
- query,
90
- }), [dispatch, persistConfig, pluginRegistry, state.config.disabledPlugins, stateRef]);
91
-
92
83
  const buildWindowModeItems = useCallback((arg: string): ResultItem[] => buildWindowModeResultItems({
93
84
  arg,
94
85
  closeAll,
@@ -235,7 +226,6 @@ export function useCommandBarRouteActions({
235
226
  activatePaneSettingField,
236
227
  buildLayoutItems,
237
228
  buildPaneSettingItems,
238
- buildPluginItems,
239
229
  buildWindowModeItems,
240
230
  executeCollectionCommand,
241
231
  openPaneSettingsRoute,
@@ -243,73 +233,3 @@ export function useCommandBarRouteActions({
243
233
  };
244
234
  }
245
235
 
246
- function buildPluginToggleItems({
247
- disabledPlugins,
248
- dispatch,
249
- getConfig,
250
- persistConfig,
251
- pluginRegistry,
252
- query,
253
- }: {
254
- disabledPlugins: readonly string[];
255
- dispatch: Dispatch<AppAction>;
256
- getConfig: () => AppState["config"];
257
- persistConfig: (nextConfig: AppState["config"]) => void;
258
- pluginRegistry: PluginRegistry;
259
- query: string;
260
- }): ResultItem[] {
261
- const normalizedQuery = query.trim().toLowerCase();
262
- const pluginQuery = normalizedQuery === "plugin" || normalizedQuery === "plugins"
263
- ? ""
264
- : normalizedQuery;
265
- const toggleable = [...pluginRegistry.allPlugins.values()].filter((plugin) => plugin.toggleable);
266
- const filtered = pluginQuery
267
- ? toggleable.filter((plugin) => (
268
- [
269
- plugin.name,
270
- plugin.id,
271
- plugin.description,
272
- ...pluginRegistry.getPluginPaneIds(plugin.id).flatMap((paneId) => [
273
- paneId,
274
- pluginRegistry.panes.get(paneId)?.name,
275
- ]),
276
- ...pluginRegistry.getPluginPaneTemplateIds(plugin.id).flatMap((templateId) => {
277
- const template = pluginRegistry.paneTemplates.get(templateId);
278
- return [
279
- templateId,
280
- template?.label,
281
- template?.description,
282
- ...(template?.keywords ?? []),
283
- ];
284
- }),
285
- ].some((term) => typeof term === "string" && term.toLowerCase().includes(pluginQuery))
286
- ))
287
- : toggleable;
288
-
289
- return filtered.map((plugin): ResultItem => {
290
- const enabled = !disabledPlugins.includes(plugin.id);
291
- const toggleAction = () => {
292
- dispatch({ type: "TOGGLE_PLUGIN", pluginId: plugin.id });
293
- const currentConfig = getConfig();
294
- const nextDisabled = enabled
295
- ? [...disabledPlugins, plugin.id]
296
- : disabledPlugins.filter((entry) => entry !== plugin.id);
297
- if (enabled) {
298
- for (const paneId of pluginRegistry.getPluginPaneIds(plugin.id)) {
299
- pluginRegistry.hidePane(paneId);
300
- }
301
- }
302
- persistConfig({ ...currentConfig, disabledPlugins: nextDisabled });
303
- };
304
- return {
305
- id: `plugin:${plugin.id}`,
306
- label: plugin.name,
307
- detail: plugin.description || "",
308
- category: "Plugins",
309
- kind: "plugin",
310
- checked: enabled,
311
- pluginToggle: toggleAction,
312
- action: toggleAction,
313
- };
314
- });
315
- }
@@ -8,7 +8,7 @@ interface UseCommandBarLaunchRequestOptions {
8
8
  commandBarLaunchRequest: AppState["commandBarLaunchRequest"];
9
9
  commandBarOpen: boolean;
10
10
  openModeRoute: (
11
- screen: "ticker-search" | "plugins" | "layout",
11
+ screen: "ticker-search" | "layout",
12
12
  initialQuery?: string,
13
13
  payload?: Record<string, unknown>,
14
14
  ) => void;
@@ -2,6 +2,7 @@ import { useMemo } from "react";
2
2
  import type { PluginRegistry } from "../../../plugins/registry";
3
3
  import {
4
4
  getEmptyState,
5
+ type CommandBarCategoryPriorities,
5
6
  type CommandBarMode,
6
7
  type CommandBarSectionOrder,
7
8
  } from "../view-model";
@@ -33,13 +34,13 @@ interface BuildRouteListStateOptions {
33
34
  query: string,
34
35
  options?: { keepRouteOpen?: boolean },
35
36
  ) => ResultItem[];
36
- buildPluginItems: (query: string) => ResultItem[];
37
37
  currentRoute: CommandBarRoute | null;
38
38
  orderedRootResults: ResultItem[];
39
39
  pluginRegistry: Pick<PluginRegistry, "resolvePaneSettings">;
40
40
  rootHoveredIdx: number | null;
41
41
  rootModeKind: CommandBarMode;
42
42
  rootQuery: string;
43
+ rootCategoryPriorities?: CommandBarCategoryPriorities;
43
44
  rootSearching: boolean;
44
45
  rootSectionOrder: CommandBarSectionOrder;
45
46
  rootSelectedIdx: number;
@@ -53,12 +54,12 @@ function buildRouteListState(options: BuildRouteListStateOptions): ListScreenSta
53
54
  adaptTickerSearchRouteResult,
54
55
  buildLayoutItems,
55
56
  buildPaneSettingItems,
56
- buildPluginItems,
57
57
  currentRoute,
58
58
  orderedRootResults,
59
59
  pluginRegistry,
60
60
  rootHoveredIdx,
61
61
  rootModeKind,
62
+ rootCategoryPriorities,
62
63
  rootQuery,
63
64
  rootSearching,
64
65
  rootSectionOrder,
@@ -86,28 +87,12 @@ function buildRouteListState(options: BuildRouteListStateOptions): ListScreenSta
86
87
  footerLeft: getScreenFooterLeft(null),
87
88
  footerRight: getScreenFooterRight(null),
88
89
  sectionOrder: rootSectionOrder,
90
+ categoryPriorities: rootCategoryPriorities,
89
91
  };
90
92
  }
91
93
 
92
94
  if (currentRoute.kind === "mode") {
93
95
  switch (currentRoute.screen) {
94
- case "plugins": {
95
- const results = buildPluginItems(currentRoute.query);
96
- return {
97
- kind: "mode",
98
- title: "Manage Plugins",
99
- subtitle: "Toggle optional plugins without leaving the command bar.",
100
- query: currentRoute.query,
101
- selectedIdx: currentRoute.selectedIdx,
102
- hoveredIdx: currentRoute.hoveredIdx,
103
- results: orderListResults(results),
104
- searching: false,
105
- emptyLabel: getEmptyState("plugins", currentRoute.query).label,
106
- emptyDetail: getEmptyState("plugins", currentRoute.query).detail,
107
- footerLeft: getScreenFooterLeft(currentRoute),
108
- footerRight: getScreenFooterRight(currentRoute),
109
- };
110
- }
111
96
  case "layout": {
112
97
  const results = buildLayoutItems(currentRoute.query);
113
98
  return {
@@ -207,10 +192,10 @@ export function useRouteListState(options: BuildRouteListStateOptions): ListScre
207
192
  adaptTickerSearchRouteResult,
208
193
  buildLayoutItems,
209
194
  buildPaneSettingItems,
210
- buildPluginItems,
211
195
  currentRoute,
212
196
  orderedRootResults,
213
197
  pluginRegistry,
198
+ rootCategoryPriorities,
214
199
  rootHoveredIdx,
215
200
  rootModeKind,
216
201
  rootQuery,
@@ -226,10 +211,10 @@ export function useRouteListState(options: BuildRouteListStateOptions): ListScre
226
211
  adaptTickerSearchRouteResult,
227
212
  buildLayoutItems,
228
213
  buildPaneSettingItems,
229
- buildPluginItems,
230
214
  currentRoute,
231
215
  orderedRootResults,
232
216
  pluginRegistry,
217
+ rootCategoryPriorities,
233
218
  rootHoveredIdx,
234
219
  rootModeKind,
235
220
  rootQuery,
@@ -28,7 +28,7 @@ import type { OpenInlineConfirm } from "./routing/confirm";
28
28
  import { activatePickerSelectionAction } from "./picker-activation";
29
29
 
30
30
  type OpenModeRouteFn = (
31
- screen: "ticker-search" | "plugins" | "layout",
31
+ screen: "ticker-search" | "layout",
32
32
  initialQuery?: string,
33
33
  payload?: Record<string, unknown>,
34
34
  ) => void;
@@ -120,7 +120,8 @@ describe("CommandBar AI assist", () => {
120
120
  query="new chat pane"
121
121
  configurePluginRegistry={configureEarningsRegistry(created)}
122
122
  />,
123
- { width: 100, height: 20 },
123
+ // Wide enough for the answer's label and its title to share one row.
124
+ { width: 120, height: 20 },
124
125
  );
125
126
 
126
127
  await testSetup.renderOnce();
@@ -131,12 +132,14 @@ describe("CommandBar AI assist", () => {
131
132
  expect(sentCommandCount).toBeGreaterThan(0);
132
133
 
133
134
  releaseResponse();
134
- const answered = await waitForFrameToContain("CHAT #general — Open the general channel", ASSIST_WAIT_ATTEMPTS);
135
+ const answered = await waitForFrameToContain("#general · Open the general channel", ASSIST_WAIT_ATTEMPTS);
135
136
  expect(answered).not.toContain("Thinking…");
136
137
  expect(requests).toHaveLength(1);
137
- // Above the local matches, and holding the selection an untouched query
138
- // never moved: plain Enter runs the AI's best guess.
138
+ // Above the local matches, laid out like any other row with the prefix in
139
+ // the badge column, and holding the selection an untouched query never
140
+ // moved: plain Enter runs the AI's best guess.
139
141
  expect(answered.indexOf("Ask AI")).toBeLessThan(answered.indexOf("Panes"));
142
+ expect(answered).toMatch(/CHAT\s+#general · Open the general channel/);
140
143
 
141
144
  await emitKeypress(testSetup, { name: "return", sequence: "\r" });
142
145
  expect(created).toEqual([{ templateId: "new-chat-pane", options: { arg: "#general" } }]);
@@ -162,7 +165,8 @@ describe("CommandBar AI assist", () => {
162
165
  query="new chat pane"
163
166
  configurePluginRegistry={configureEarningsRegistry(created)}
164
167
  />,
165
- { width: 100, height: 20 },
168
+ // Wide enough for the answers' labels and titles to share one row.
169
+ { width: 120, height: 20 },
166
170
  );
167
171
 
168
172
  await testSetup.renderOnce();
@@ -170,9 +174,9 @@ describe("CommandBar AI assist", () => {
170
174
  // Down lands on the local match while a single "Thinking…" row sits above.
171
175
  await emitKeypress(testSetup, { name: "down" });
172
176
  releaseResponse();
173
- await waitForFrameToContain("CHAT #random — Open the random channel", ASSIST_WAIT_ATTEMPTS);
177
+ await waitForFrameToContain("#random · Open the random channel", ASSIST_WAIT_ATTEMPTS);
174
178
 
175
- // Two answers replaced that one row, so the chosen row moved down by one —
179
+ // Two answers replaced that one row, so the chosen row moved down by one;
176
180
  // Enter still runs it rather than whatever now sits at its old index.
177
181
  await emitKeypress(testSetup, { name: "return", sequence: "\r" });
178
182
  expect(created).toEqual([{ templateId: "new-chat-pane", options: undefined }]);
@@ -196,7 +200,7 @@ describe("CommandBar AI assist", () => {
196
200
  );
197
201
 
198
202
  await testSetup.renderOnce();
199
- await waitForFrameToContain("ERN NVDA — Earnings Calendar for NVDA", ASSIST_WAIT_ATTEMPTS);
203
+ await waitForFrameToContain("NVDA · Earnings Calendar", ASSIST_WAIT_ATTEMPTS);
200
204
 
201
205
  await emitKeypress(testSetup, { name: "return", sequence: "\r" });
202
206
  expect(created).toEqual([{ templateId: "earnings-calendar-pane", options: undefined }]);
@@ -275,8 +279,8 @@ describe("CommandBar AI assist", () => {
275
279
  await testSetup.renderOnce();
276
280
  expect(requests).toEqual([]);
277
281
 
278
- // The offer leads the list but never takes the Enter that belongs to the
279
- // local match the user was already looking at.
282
+ // The offer sits under the list and never takes the Enter that belongs to
283
+ // the local match the user was already looking at.
280
284
  await emitKeypress(testSetup, { name: "return", sequence: "\r" });
281
285
  expect(created).toEqual([{ templateId: "new-chat-pane", options: undefined }]);
282
286
  });
@@ -82,7 +82,13 @@ export function useCommandBarEnvironment() {
82
82
  }, []);
83
83
  const { symbol: activeTickerSymbol, ticker: activeTickerData, financials: activeFinancials } = useFocusedTicker();
84
84
  const { width: termWidth, height: termHeight } = useViewport();
85
- const { nativePaneChrome: nativePaneChromeCapability, cellWidthPx = 8, cellHeightPx = 18, titleBarOverlay } = useUiCapabilities();
85
+ const {
86
+ nativePaneChrome: nativePaneChromeCapability,
87
+ cellWidthPx = 8,
88
+ cellHeightPx = 18,
89
+ nativeWindowChrome,
90
+ titleBarOverlay,
91
+ } = useUiCapabilities();
86
92
  const nativePaneChrome = nativePaneChromeCapability === true;
87
93
  const availableCommands = useMemo(
88
94
  () => nativePaneChrome
@@ -111,6 +117,7 @@ export function useCommandBarEnvironment() {
111
117
  getCommittedThemeId,
112
118
  nativeListScrollRef,
113
119
  nativePaneChrome,
120
+ nativeWindowChrome,
114
121
  persistConfig,
115
122
  skipTickerSearchDebounceRef,
116
123
  state,