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
@@ -0,0 +1,113 @@
1
+ import { afterEach, expect, test } from "bun:test";
2
+ import { act } from "react";
3
+ import { testRender } from "../../../renderers/opentui/test-utils";
4
+ import { CommandBarListBody } from "./view";
5
+ import type { CommandBarListRow, ListScreenState, ResultItem } from "./model";
6
+
7
+ let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
8
+
9
+ afterEach(() => {
10
+ testSetup?.renderer.destroy();
11
+ testSetup = undefined;
12
+ });
13
+
14
+ function item(overrides: Partial<ResultItem> & { id: string; label: string }): ResultItem {
15
+ return {
16
+ detail: "",
17
+ category: "Commands",
18
+ kind: "command",
19
+ action: () => {},
20
+ ...overrides,
21
+ };
22
+ }
23
+
24
+ function rows(items: ResultItem[]): CommandBarListRow[] {
25
+ return [
26
+ { kind: "heading", id: "heading", label: "Commands" },
27
+ ...items.map((entry, index) => ({ kind: "item" as const, item: entry, globalIdx: index })),
28
+ ];
29
+ }
30
+
31
+ const LIST_STATE: ListScreenState = {
32
+ kind: "root",
33
+ title: "Commands",
34
+ query: "port",
35
+ selectedIdx: -1,
36
+ hoveredIdx: null,
37
+ results: [],
38
+ searching: false,
39
+ emptyLabel: "",
40
+ emptyDetail: "",
41
+ footerLeft: "",
42
+ footerRight: "",
43
+ };
44
+
45
+ function ListHarness({ nativeListRows }: { nativeListRows: CommandBarListRow[] }) {
46
+ return (
47
+ <CommandBarListBody
48
+ visibleListState={LIST_STATE}
49
+ nativeListRows={nativeListRows}
50
+ listBodyHeight={16}
51
+ contentPadding={3}
52
+ labelWidth={40}
53
+ nativePaneChrome={false}
54
+ nativeListScrollRef={{ current: null }}
55
+ paletteAccentText="#ffffff"
56
+ paletteBg="#000000"
57
+ paletteHeadingText="#ffffff"
58
+ paletteHoverBg="#000000"
59
+ paletteMatchText="#ffffff"
60
+ paletteSelectedBg="#000000"
61
+ paletteSelectedText="#ffffff"
62
+ paletteSubtleText="#ffffff"
63
+ paletteText="#ffffff"
64
+ panelBg="#000000"
65
+ queryDisplayWidth={50}
66
+ trailingWidth={10}
67
+ onHoverIndex={() => {}}
68
+ onListScroll={() => {}}
69
+ onRowMouseDown={() => {}}
70
+ />
71
+ );
72
+ }
73
+
74
+ function columnOf(frame: string, text: string): number {
75
+ const line = frame.split("\n").find((row) => row.includes(text));
76
+ return line ? line.indexOf(text) : -1;
77
+ }
78
+
79
+ /**
80
+ * Instruments and documents arrive after the local rows and carry wider badges
81
+ * than they do, so a column measured from what is on screen would widen
82
+ * mid-query and drag every label already rendered to the right.
83
+ */
84
+ test("keeps labels in place when a later section brings wider badges", async () => {
85
+ const local = [
86
+ item({ id: "portfolio", label: "Open Portfolio" }),
87
+ item({ id: "quote", label: "Quote", right: "Q" }),
88
+ ];
89
+
90
+ testSetup = await testRender(<ListHarness nativeListRows={rows(local)} />, { width: 60, height: 20 });
91
+ await testSetup.renderOnce();
92
+ const before = testSetup.captureCharFrame();
93
+ testSetup.renderer.destroy();
94
+ testSetup = await testRender(
95
+ <ListHarness
96
+ nativeListRows={rows([
97
+ ...local,
98
+ item({ id: "spy", label: "SPDR S&P 500", kind: "search", badge: "ETF" }),
99
+ item({ id: "filing", label: "Annual report", kind: "action", badge: "10-K" }),
100
+ item({ id: "derivative", label: "Call spread", kind: "search", badge: "DERIV" }),
101
+ ])}
102
+ />,
103
+ { width: 60, height: 20 },
104
+ );
105
+ await testSetup.renderOnce();
106
+ const after = testSetup.captureCharFrame();
107
+
108
+ expect(columnOf(before, "Open Portfolio")).toBeGreaterThan(0);
109
+ expect(columnOf(after, "Open Portfolio")).toBe(columnOf(before, "Open Portfolio"));
110
+ expect(columnOf(after, "Commands")).toBe(columnOf(before, "Commands"));
111
+ // The widest badge ends one gap short of the label edge it shares.
112
+ expect(columnOf(after, "DERIV") + "DERIV".length).toBe(columnOf(after, "Call spread") - 1);
113
+ });
@@ -1,16 +1,30 @@
1
- import { memo, useLayoutEffect, useMemo, useRef, type RefObject } from "react";
1
+ import { memo, useMemo, type RefObject } from "react";
2
2
  import {
3
3
  Box,
4
- Input,
5
4
  ScrollBox,
6
5
  Text,
7
6
  TextAttributes,
8
- type InputRenderable,
9
7
  type ScrollBoxRenderable,
10
8
  } from "../../../ui";
11
9
  import { Spinner } from "../../ui";
12
10
  import { t } from "../../../i18n";
13
- import type { CommandBarListRow, ListScreenState, ResultItem } from "./model";
11
+ import { commandBarBadgeText, type CommandBarBadgeTone } from "../../../theme/colors";
12
+ import { useThemeColors } from "../../../theme/theme-context";
13
+ import type { CommandBarResultLineSegment } from "../../../types/plugin";
14
+ import { truncateTextSegments } from "../../../utils/format";
15
+ import {
16
+ BADGE_COLUMN_WIDTH,
17
+ BADGE_GAP,
18
+ badgeConsumesRight,
19
+ resolveRowBadge,
20
+ } from "./badge";
21
+ import {
22
+ getListRowsHeight,
23
+ getResultItemLines,
24
+ type CommandBarListRow,
25
+ type ListScreenState,
26
+ type ResultItem,
27
+ } from "./model";
14
28
  import { getRowPresentation, truncateText } from "../view-model";
15
29
  import { useRemoteUiNode } from "../../../remote/semantic-tree";
16
30
 
@@ -20,109 +34,8 @@ export type CommandBarListScrollEvent = {
20
34
  scroll?: { direction?: string; delta?: number };
21
35
  };
22
36
 
23
- interface CommandBarListHeaderProps {
24
- kind: ListScreenState["kind"];
25
- title: string;
26
- query: string;
27
- queryDisplayWidth: number;
28
- nativePaneChrome: boolean;
29
- inputBg: string;
30
- paletteBg: string;
31
- paletteText: string;
32
- paletteSubtleText: string;
33
- cursorColor: string;
34
- contentPadding: number;
35
- rootGhostSuffix: string | null;
36
- rootQueryLength: number;
37
- rootShortcutFeedback: string | null;
38
- onQueryChange: (query: string) => void;
39
- }
40
-
41
- export const CommandBarListHeader = memo(function CommandBarListHeader({
42
- kind,
43
- title,
44
- query,
45
- queryDisplayWidth,
46
- nativePaneChrome,
47
- inputBg,
48
- paletteBg,
49
- paletteText,
50
- paletteSubtleText,
51
- cursorColor,
52
- contentPadding,
53
- rootGhostSuffix,
54
- rootQueryLength,
55
- rootShortcutFeedback,
56
- onQueryChange,
57
- }: CommandBarListHeaderProps) {
58
- const inputRef = useRef<InputRenderable | null>(null);
59
-
60
- useLayoutEffect(() => {
61
- const input = inputRef.current;
62
- if (!input || input.editBuffer.getText() === query) return;
63
- input.editBuffer.setText?.(query);
64
- input.setCursorOffset?.(query.length);
65
- }, [kind, query, title]);
66
-
67
- return (
68
- <>
69
- <Box height={1} paddingX={contentPadding}>
70
- <Box
71
- width={queryDisplayWidth}
72
- height={1}
73
- position="relative"
74
- backgroundColor={nativePaneChrome ? undefined : inputBg}
75
- style={nativePaneChrome ? undefined : {
76
- overflow: "hidden",
77
- }}
78
- >
79
- <Input
80
- key={`${kind}:${title}`}
81
- ref={inputRef}
82
- value={query}
83
- onInput={onQueryChange}
84
- placeholder={kind === "root" ? t("Command or plain English…") : title === "Security Description" ? t("Search tickers") : t("Filter")}
85
- focused
86
- data-gloom-remote-scope="command-bar"
87
- data-gloom-remote-surface="command-bar"
88
- width={nativePaneChrome ? "100%" : queryDisplayWidth}
89
- backgroundColor={nativePaneChrome ? "transparent" : paletteBg}
90
- focusedBackgroundColor={nativePaneChrome ? "transparent" : paletteBg}
91
- textColor={paletteText}
92
- focusedTextColor={paletteText}
93
- placeholderColor={paletteSubtleText}
94
- cursorColor={cursorColor}
95
- />
96
- {kind === "root" && rootGhostSuffix && (
97
- <Box
98
- position="absolute"
99
- top={0}
100
- left={Math.max(0, Math.min(rootQueryLength, queryDisplayWidth - 1))}
101
- width={Math.max(0, queryDisplayWidth - Math.min(rootQueryLength, queryDisplayWidth - 1))}
102
- height={1}
103
- >
104
- <Text fg={paletteSubtleText}>
105
- {truncateText(
106
- rootGhostSuffix,
107
- Math.max(0, queryDisplayWidth - Math.min(rootQueryLength, queryDisplayWidth - 1)),
108
- )}
109
- </Text>
110
- </Box>
111
- )}
112
- </Box>
113
- </Box>
114
- <Box height={1} paddingX={contentPadding}>
115
- {kind === "root" && rootShortcutFeedback
116
- ? (
117
- <Text fg={paletteSubtleText}>
118
- {truncateText(rootShortcutFeedback, queryDisplayWidth)}
119
- </Text>
120
- )
121
- : null}
122
- </Box>
123
- </>
124
- );
125
- });
37
+ /** Columns every row gives up to the badge column, badge or not. */
38
+ const BADGE_INDENT = BADGE_COLUMN_WIDTH + BADGE_GAP;
126
39
 
127
40
  interface CommandBarListItemRowProps {
128
41
  item: ResultItem;
@@ -138,7 +51,9 @@ interface CommandBarListItemRowProps {
138
51
  nativePaneChrome: boolean;
139
52
  paletteAccentText: string;
140
53
  paletteBg: string;
54
+ paletteHeadingText: string;
141
55
  paletteHoverBg: string;
56
+ paletteMatchText: string;
142
57
  paletteSelectedBg: string;
143
58
  paletteSelectedText: string;
144
59
  paletteSubtleText: string;
@@ -163,7 +78,9 @@ const CommandBarListItemRow = memo(function CommandBarListItemRow({
163
78
  nativePaneChrome,
164
79
  paletteAccentText,
165
80
  paletteBg,
81
+ paletteHeadingText,
166
82
  paletteHoverBg,
83
+ paletteMatchText,
167
84
  paletteSelectedBg,
168
85
  paletteSelectedText,
169
86
  paletteSubtleText,
@@ -174,8 +91,27 @@ const CommandBarListItemRow = memo(function CommandBarListItemRow({
174
91
  onRowMouseDown,
175
92
  }: CommandBarListItemRowProps) {
176
93
  const presentation = getRowPresentation(item, isSelected, trailingWidth > 0);
177
- const label = truncateText(presentation.label, labelWidth);
178
- const trailing = truncateText(presentation.trailing, trailingWidth);
94
+ const badge = resolveRowBadge(item);
95
+ // The badge column and its gap come out of the label, so the right column
96
+ // stays where it is for rows with and without a badge alike.
97
+ const labelColumnWidth = Math.max(1, labelWidth - BADGE_INDENT);
98
+ // A long title stops one cell short of the right column, so its ellipsis
99
+ // never runs into the date or shortcut sitting there.
100
+ const label = truncateText(presentation.label, Math.max(1, labelColumnWidth - (trailingWidth > 0 ? 1 : 0)));
101
+ // "current" outranks the shortcut on the right; otherwise a badge lifted from
102
+ // `right` must not be repeated there.
103
+ const trailing = badgeConsumesRight(item) && !item.current
104
+ ? ""
105
+ : truncateText(presentation.trailing, trailingWidth);
106
+ const lineWidth = labelColumnWidth + trailingWidth;
107
+ const lines = useMemo(
108
+ () => getResultItemLines(item).map((line) => truncateTextSegments(
109
+ line.segments,
110
+ lineWidth,
111
+ (ellipsis) => ({ text: ellipsis, emphasis: "muted" as const }),
112
+ )),
113
+ [item, lineWidth],
114
+ );
179
115
  const activate = () => onRowMouseDown({
180
116
  preventDefault() {},
181
117
  stopPropagation() {},
@@ -215,8 +151,8 @@ const CommandBarListItemRow = memo(function CommandBarListItemRow({
215
151
  return (
216
152
  <Box
217
153
  key={item.id}
218
- flexDirection="row"
219
- height={1}
154
+ flexDirection="column"
155
+ height={1 + lines.length}
220
156
  paddingX={contentPadding}
221
157
  backgroundColor={isSelected
222
158
  ? paletteSelectedBg
@@ -230,26 +166,95 @@ const CommandBarListItemRow = memo(function CommandBarListItemRow({
230
166
  data-command-bar-row-selected={nativePaneChrome && isSelected ? "true" : undefined}
231
167
  style={nativePaneChrome ? { borderRadius: 6 } : undefined}
232
168
  >
233
- <Box width={labelWidth}>
234
- <Text fg={isSelected ? paletteSelectedText : presentation.primaryMuted ? paletteSubtleText : paletteText}>
235
- {label}
236
- </Text>
169
+ <Box flexDirection="row" height={1}>
170
+ <Box width={BADGE_INDENT} flexDirection="row">
171
+ {badge && <CommandBarRowBadge text={badge.text} tone={badge.tone} width={BADGE_COLUMN_WIDTH} />}
172
+ </Box>
173
+ <Box width={labelColumnWidth}>
174
+ <Text fg={isSelected ? paletteSelectedText : presentation.primaryMuted ? paletteSubtleText : paletteText}>
175
+ {label}
176
+ </Text>
177
+ </Box>
178
+ <Box width={trailingWidth}>
179
+ <Text
180
+ fg={isSelected
181
+ ? paletteSelectedText
182
+ : presentation.trailingAccent
183
+ ? paletteAccentText
184
+ : paletteSubtleText}
185
+ >
186
+ {trailing}
187
+ </Text>
188
+ </Box>
237
189
  </Box>
238
- <Box width={trailingWidth}>
239
- <Text
240
- fg={isSelected
241
- ? paletteSelectedText
242
- : presentation.trailingAccent
243
- ? paletteAccentText
244
- : paletteSubtleText}
190
+ {lines.map((segments, index) => (
191
+ <Box
192
+ key={`line:${index}`}
193
+ flexDirection="row"
194
+ height={1}
195
+ width={lineWidth}
196
+ marginLeft={BADGE_INDENT}
197
+ overflow="hidden"
245
198
  >
246
- {trailing}
247
- </Text>
248
- </Box>
199
+ {segments.map((segment, segmentIndex) => (
200
+ <Text
201
+ key={segmentIndex}
202
+ fg={resolveLineSegmentColor(segment, {
203
+ isSelected,
204
+ paletteHeadingText,
205
+ paletteMatchText,
206
+ paletteSelectedText,
207
+ paletteSubtleText,
208
+ })}
209
+ attributes={segment.emphasis === "match" ? TextAttributes.BOLD : TextAttributes.NONE}
210
+ >
211
+ {segment.text}
212
+ </Text>
213
+ ))}
214
+ </Box>
215
+ ))}
249
216
  </Box>
250
217
  );
251
218
  });
252
219
 
220
+ /**
221
+ * A tag set flush against the label edge in a column padded to the widest tag
222
+ * in the list, so the labels line up and the tags share one right edge. Text
223
+ * only: a filled box would outweigh the label it introduces.
224
+ */
225
+ function CommandBarRowBadge({ text, tone, width }: { text: string; tone: CommandBarBadgeTone; width: number }) {
226
+ const themeColors = useThemeColors();
227
+ return (
228
+ <Box width={width} height={1}>
229
+ <Text fg={commandBarBadgeText(tone, themeColors)}>
230
+ {truncateText(text, width).padStart(width, " ")}
231
+ </Text>
232
+ </Box>
233
+ );
234
+ }
235
+
236
+ /**
237
+ * Matched runs keep their highlight on the selected row: the selection colour is
238
+ * what tells you where you are, the match colour is what you were looking for.
239
+ * A muted run (a source lead-in, an ellipsis) steps down one more level than
240
+ * the snippet so it reads as metadata, except on the selected row where the
241
+ * whole line takes the selection text so nothing there goes dim.
242
+ */
243
+ function resolveLineSegmentColor(
244
+ segment: CommandBarResultLineSegment,
245
+ palette: {
246
+ isSelected: boolean;
247
+ paletteHeadingText: string;
248
+ paletteMatchText: string;
249
+ paletteSelectedText: string;
250
+ paletteSubtleText: string;
251
+ },
252
+ ): string {
253
+ if (segment.emphasis === "match") return palette.paletteMatchText;
254
+ if (palette.isSelected) return palette.paletteSelectedText;
255
+ return segment.emphasis === "muted" ? palette.paletteHeadingText : palette.paletteSubtleText;
256
+ }
257
+
253
258
  interface CommandBarListBodyProps {
254
259
  visibleListState: ListScreenState;
255
260
  nativeListRows: CommandBarListRow[];
@@ -262,6 +267,7 @@ interface CommandBarListBodyProps {
262
267
  paletteBg: string;
263
268
  paletteHeadingText: string;
264
269
  paletteHoverBg: string;
270
+ paletteMatchText: string;
265
271
  paletteSelectedBg: string;
266
272
  paletteSelectedText: string;
267
273
  paletteSubtleText: string;
@@ -286,6 +292,7 @@ export const CommandBarListBody = memo(function CommandBarListBody({
286
292
  paletteBg,
287
293
  paletteHeadingText,
288
294
  paletteHoverBg,
295
+ paletteMatchText,
289
296
  paletteSelectedBg,
290
297
  paletteSelectedText,
291
298
  paletteSubtleText,
@@ -297,12 +304,19 @@ export const CommandBarListBody = memo(function CommandBarListBody({
297
304
  onListScroll,
298
305
  onRowMouseDown,
299
306
  }: CommandBarListBodyProps) {
307
+ // Headings, messages and the spinner sit on the label edge: the badge column
308
+ // is a gutter for the rows, not an indent for everything else.
309
+ const labelEdgePadding = contentPadding + BADGE_INDENT;
310
+ const labelEdgeWidth = Math.max(1, queryDisplayWidth - BADGE_INDENT);
300
311
  const visibleRows = useMemo(() => {
301
312
  const rows = nativeListRows;
302
313
  if (nativePaneChrome) return rows;
303
314
  const paddedRows = [...rows];
304
- while (paddedRows.length < listBodyHeight) {
315
+ // Padded in lines, not rows: a multi-line result already fills several.
316
+ let filledLines = getListRowsHeight(rows);
317
+ while (filledLines < listBodyHeight) {
305
318
  paddedRows.push({ kind: "filler", id: `filler:${paddedRows.length}` });
319
+ filledLines += 1;
306
320
  }
307
321
  return paddedRows;
308
322
  }, [
@@ -319,23 +333,23 @@ export const CommandBarListBody = memo(function CommandBarListBody({
319
333
  }
320
334
  if (row.kind === "spinner") {
321
335
  return (
322
- <Box key={row.id} height={1} paddingX={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: onListScroll } : {})}>
336
+ <Box key={row.id} height={1} paddingLeft={labelEdgePadding} paddingRight={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: onListScroll } : {})}>
323
337
  <Spinner label={t(row.label)} />
324
338
  </Box>
325
339
  );
326
340
  }
327
341
  if (row.kind === "message") {
328
342
  return (
329
- <Box key={row.id} height={1} paddingX={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: onListScroll } : {})}>
330
- <Text fg={paletteText}>{truncateText(t(row.label), queryDisplayWidth)}</Text>
343
+ <Box key={row.id} height={1} paddingLeft={labelEdgePadding} paddingRight={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: onListScroll } : {})}>
344
+ <Text fg={paletteText}>{truncateText(t(row.label), labelEdgeWidth)}</Text>
331
345
  </Box>
332
346
  );
333
347
  }
334
348
  if (row.kind === "heading") {
335
349
  return (
336
- <Box key={row.id} height={1} paddingX={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: onListScroll } : {})}>
350
+ <Box key={row.id} height={1} paddingLeft={labelEdgePadding} paddingRight={contentPadding} {...(!nativePaneChrome ? { onMouseScroll: onListScroll } : {})}>
337
351
  <Text attributes={TextAttributes.BOLD} fg={row.accent ? paletteAccentText : paletteHeadingText}>
338
- {truncateText(t(row.label), queryDisplayWidth)}
352
+ {truncateText(t(row.label), labelEdgeWidth)}
339
353
  </Text>
340
354
  </Box>
341
355
  );
@@ -360,7 +374,9 @@ export const CommandBarListBody = memo(function CommandBarListBody({
360
374
  nativePaneChrome={nativePaneChrome}
361
375
  paletteAccentText={paletteAccentText}
362
376
  paletteBg={paletteBg}
377
+ paletteHeadingText={paletteHeadingText}
363
378
  paletteHoverBg={paletteHoverBg}
379
+ paletteMatchText={paletteMatchText}
364
380
  paletteSelectedBg={paletteSelectedBg}
365
381
  paletteSelectedText={paletteSelectedText}
366
382
  paletteSubtleText={paletteSubtleText}
@@ -35,7 +35,7 @@ import {
35
35
  type CloseAllFn = (options?: { revertThemePreview?: boolean }) => void;
36
36
  type NotifyFn = (body: string, options?: { type?: "info" | "success" | "error" }) => void;
37
37
  type OpenModeRouteFn = (
38
- screen: "ticker-search" | "plugins" | "layout",
38
+ screen: "ticker-search" | "layout",
39
39
  initialQuery?: string,
40
40
  payload?: Record<string, unknown>,
41
41
  ) => void;
@@ -1,10 +1,8 @@
1
1
  import { Box, Text, TextAttributes } from "../../../ui";
2
+ import { commandBarMatchText } from "../../../theme/colors";
2
3
  import { useThemeColors } from "../../../theme/theme-context";
3
4
  import { Button, Spinner } from "../../ui";
4
- import {
5
- CommandBarListBody,
6
- CommandBarListHeader,
7
- } from "../list/view";
5
+ import { CommandBarListBody } from "../list/view";
8
6
  import {
9
7
  CommandBarMultiSelectBody,
10
8
  isMultiSelectPickerRoute,
@@ -24,7 +22,9 @@ type CommandBarPanelBodyProps = Omit<
24
22
  | "nativeOccluderRect"
25
23
  | "onNativeOccluderChange"
26
24
  | "onOverlayClose"
25
+ | "onQueryChange"
27
26
  | "panelBounds"
27
+ | "rootGhostSuffix"
28
28
  | "selectedScrollRowIndex"
29
29
  | "termHeight"
30
30
  | "termWidth"
@@ -39,6 +39,7 @@ export function CommandBarPanelBody({
39
39
  contentPadding,
40
40
  currentRoute,
41
41
  getWorkflowInputRef,
42
+ hasChromeRow,
42
43
  labelWidth,
43
44
  listBodyHeight,
44
45
  nativeListRows,
@@ -57,15 +58,12 @@ export function CommandBarPanelBody({
57
58
  onMultiSelectSelect,
58
59
  onMultiSelectToggle,
59
60
  onNativeSelectRef,
60
- onQueryChange,
61
61
  onThemeCommit,
62
62
  onThemePreview,
63
63
  onWorkflowActiveTextareaSync,
64
64
  onWorkflowSubmit,
65
65
  palette,
66
66
  queryDisplayWidth,
67
- rootGhostSuffix,
68
- rootQueryLength,
69
67
  rootShortcutFeedback,
70
68
  themePickerActive,
71
69
  themePickerFilter,
@@ -90,62 +88,51 @@ export function CommandBarPanelBody({
90
88
 
91
89
  return (
92
90
  <>
93
- {!nativePaneChrome && <Box height={1} backgroundColor={paletteBg} />}
91
+ {/* The desktop sheet pads itself in CSS; the terminal spends a row. */}
92
+ {!nativePaneChrome && <Box height={1} />}
94
93
 
95
- {!nativePaneChrome && (
96
- <Box
97
- height={1}
98
- paddingX={contentPadding}
99
- flexDirection="row"
100
- alignItems="center"
101
- >
102
- {currentRoute && (
103
- <Box marginRight={1}>
104
- <Button label={t("Back")} variant="ghost" onPress={onBack} />
94
+ {/* Rows stop at the results column, so a selection bar on a wide window
95
+ does not run on past the text into empty sheet. */}
96
+ <Box
97
+ key={bodySlotKey}
98
+ flexDirection="column"
99
+ flexGrow={1}
100
+ width={queryDisplayWidth + contentPadding * 2}
101
+ backgroundColor={panelBg}
102
+ >
103
+ {/* The query itself is typed in the header prompt. This row only exists
104
+ when it has something to say: the way back from a nested screen, or
105
+ what a typed prefix resolved to. Its height is reserved in
106
+ panel/layout.ts, which is why the render is keyed on the same flag. */}
107
+ {hasChromeRow && (
108
+ <>
109
+ <Box height={1} paddingX={contentPadding} flexDirection="row">
110
+ {currentRoute ? (
111
+ <>
112
+ <Text
113
+ fg={paletteSubtleText}
114
+ onMouseDown={(event: any) => {
115
+ event.stopPropagation?.();
116
+ event.preventDefault?.();
117
+ onBack();
118
+ }}
119
+ data-gloom-interactive="true"
120
+ >
121
+ {`\u2190 ${t("Back")}`}
122
+ </Text>
123
+ <Box width={2} />
124
+ <Text fg={paletteText} attributes={TextAttributes.BOLD}>
125
+ {truncateText(t(getCommandBarPanelTitle(currentRoute)), Math.max(1, queryDisplayWidth - 8))}
126
+ </Text>
127
+ </>
128
+ ) : rootShortcutFeedback ? (
129
+ <Text fg={paletteSubtleText}>
130
+ {truncateText(rootShortcutFeedback, queryDisplayWidth)}
131
+ </Text>
132
+ ) : null}
105
133
  </Box>
106
- )}
107
- <Box flexGrow={1}>
108
- <Text fg={paletteText} attributes={TextAttributes.BOLD}>
109
- {t(getCommandBarPanelTitle(currentRoute))}
110
- </Text>
111
- </Box>
112
- </Box>
113
- )}
114
-
115
- <Box key={bodySlotKey} flexDirection="column" flexGrow={1} width="100%" backgroundColor={panelBg}>
116
- {nativePaneChrome && currentRoute && (
117
- <Box height={1} paddingX={contentPadding}>
118
- <Text
119
- fg={paletteSubtleText}
120
- onMouseDown={(event: any) => {
121
- event.stopPropagation?.();
122
- event.preventDefault?.();
123
- onBack();
124
- }}
125
- data-gloom-interactive="true"
126
- >
127
- {`← ${t("Back")}`}
128
- </Text>
129
- </Box>
130
- )}
131
- {(visibleListState || currentRoute?.kind === "picker") && visibleListState && (
132
- <CommandBarListHeader
133
- kind={visibleListState.kind}
134
- title={visibleListState.title}
135
- query={visibleListState.query}
136
- queryDisplayWidth={queryDisplayWidth}
137
- nativePaneChrome={nativePaneChrome}
138
- inputBg={inputBg}
139
- paletteBg={paletteBg}
140
- paletteText={paletteText}
141
- paletteSubtleText={paletteSubtleText}
142
- cursorColor={themeColors.textBright}
143
- contentPadding={contentPadding}
144
- rootGhostSuffix={rootGhostSuffix}
145
- rootQueryLength={rootQueryLength}
146
- rootShortcutFeedback={rootShortcutFeedback}
147
- onQueryChange={onQueryChange}
148
- />
134
+ <Box height={1} />
135
+ </>
149
136
  )}
150
137
 
151
138
  {themePickerActive && (
@@ -184,6 +171,7 @@ export function CommandBarPanelBody({
184
171
  paletteBg={paletteBg}
185
172
  paletteHeadingText={paletteHeadingText}
186
173
  paletteHoverBg={paletteHoverBg}
174
+ paletteMatchText={commandBarMatchText(themeColors)}
187
175
  paletteSelectedBg={paletteSelectedBg}
188
176
  paletteSelectedText={paletteSelectedText}
189
177
  paletteSubtleText={paletteSubtleText}
@@ -249,15 +237,13 @@ export function CommandBarPanelBody({
249
237
  )}
250
238
  </Box>
251
239
 
252
- {!nativePaneChrome && <Box flexGrow={1} />}
240
+ {!nativePaneChrome && <Box height={1} />}
253
241
  </>
254
242
  );
255
243
  }
256
244
 
257
- function getCommandBarPanelTitle(route: CommandBarRoute | null): string {
258
- if (!route) return "Commands";
245
+ function getCommandBarPanelTitle(route: CommandBarRoute): string {
259
246
  if (route.kind === "mode") {
260
- if (route.screen === "plugins") return "Manage Plugins";
261
247
  if (route.screen === "layout") return "Layout Actions";
262
248
  return "Security Description";
263
249
  }