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
@@ -13,14 +13,27 @@ import {
13
13
  commandBarText,
14
14
  } from "../../../theme/colors";
15
15
  import { useThemeColors } from "../../../theme/theme-context";
16
+ import { t } from "../../../i18n";
17
+ import type { ListScreenState } from "../list/model";
16
18
  import { CommandBarPanelBody } from "./body";
19
+ import { NATIVE_COMMAND_SURFACE, nativeCommandSurfaceBorder } from "./native-surface";
20
+ import { publishCommandBarPrompt } from "./prompt-binding";
17
21
  import type { CommandBarPanelProps } from "./types";
18
22
 
19
23
  const COMMAND_BAR_OVERLAY_Z_INDEX = 2_147_483_646;
20
24
  const COMMAND_BAR_PANEL_Z_INDEX = 2_147_483_647;
21
- const NATIVE_COMMAND_BAR_PADDING_X_PX = 14;
22
- const NATIVE_COMMAND_BAR_PADDING_Y_PX = 14;
23
- const NATIVE_COMMAND_BAR_SHADOW = "0 10px 18px color-mix(in srgb, var(--gloom-bg) 34%, transparent)";
25
+
26
+ /**
27
+ * The root screen keeps the closed prompt's own words. Opening the bar puts the
28
+ * caret in the same input the header was already showing, so changing the
29
+ * placeholder underneath it read as the control being swapped out. Nested
30
+ * screens are a different screen and say so.
31
+ */
32
+ function resolvePromptPlaceholder(listState: ListScreenState): string {
33
+ if (listState.kind === "root") return t("Search or run a command");
34
+ if (listState.title === "Security Description") return t("Search tickers");
35
+ return t("Filter");
36
+ }
24
37
 
25
38
  export function CommandBarPanel({
26
39
  bodyHeight,
@@ -29,6 +42,7 @@ export function CommandBarPanel({
29
42
  contentPadding,
30
43
  currentRoute,
31
44
  getWorkflowInputRef,
45
+ hasChromeRow,
32
46
  labelWidth,
33
47
  listBodyHeight,
34
48
  nativeListRows,
@@ -58,7 +72,6 @@ export function CommandBarPanel({
58
72
  panelBounds,
59
73
  queryDisplayWidth,
60
74
  rootGhostSuffix,
61
- rootQueryLength,
62
75
  rootShortcutFeedback,
63
76
  selectedScrollRowIndex,
64
77
  termHeight,
@@ -95,6 +108,24 @@ export function CommandBarPanel({
95
108
  }
96
109
  }, [listBodyHeight, nativeListScrollRef, selectedScrollRowIndex, visibleListState?.kind, visibleListState?.query]);
97
110
 
111
+ // The header prompt is the bar's input while a list screen is showing. A
112
+ // workflow owns its own fields, so it publishes nothing and the prompt goes
113
+ // quiet rather than taking focus from them.
114
+ useLayoutEffect(() => {
115
+ if (!visibleListState) {
116
+ publishCommandBarPrompt(null);
117
+ return;
118
+ }
119
+ publishCommandBarPrompt({
120
+ screenKey: `${visibleListState.kind}:${visibleListState.title}`,
121
+ query: visibleListState.query,
122
+ placeholder: resolvePromptPlaceholder(visibleListState),
123
+ ghostSuffix: visibleListState.kind === "root" ? rootGhostSuffix : null,
124
+ onQueryChange,
125
+ });
126
+ }, [onQueryChange, rootGhostSuffix, visibleListState]);
127
+ useLayoutEffect(() => () => publishCommandBarPrompt(null), []);
128
+
98
129
  useLayoutEffect(() => {
99
130
  onNativeOccluderChange?.(nativeOccluderRect);
100
131
  return () => {
@@ -109,12 +140,14 @@ export function CommandBarPanel({
109
140
  ]);
110
141
 
111
142
  return (
143
+ // The click-away overlay starts where the sheet does: the header above it
144
+ // holds the bar's input, and a click there must reach it, not close the bar.
112
145
  <Box
113
146
  position="absolute"
114
- top={0}
147
+ top={panelBounds.y}
115
148
  left={0}
116
149
  width={termWidth}
117
- height={termHeight}
150
+ height={Math.max(0, termHeight - panelBounds.y)}
118
151
  zIndex={nativePaneChrome ? COMMAND_BAR_OVERLAY_Z_INDEX : 100}
119
152
  onMouseDown={(event: any) => {
120
153
  event.stopPropagation?.();
@@ -124,7 +157,7 @@ export function CommandBarPanel({
124
157
  >
125
158
  <Box
126
159
  position="absolute"
127
- top={panelBounds.y}
160
+ top={0}
128
161
  left={panelBounds.x}
129
162
  width={panelBounds.width}
130
163
  height={panelBounds.height}
@@ -136,10 +169,14 @@ export function CommandBarPanel({
136
169
  }}
137
170
  data-gloom-role="command-bar-panel"
138
171
  style={nativePaneChrome ? {
139
- borderRadius: 8,
140
- boxShadow: NATIVE_COMMAND_BAR_SHADOW,
172
+ // The lower half of the control the header input opens: rounded and
173
+ // bordered along its three free edges, open where the input sits.
174
+ border: `1px solid ${nativeCommandSurfaceBorder(colors)}`,
175
+ borderTopWidth: 0,
176
+ borderRadius: `0 0 ${NATIVE_COMMAND_SURFACE.radiusPx}px ${NATIVE_COMMAND_SURFACE.radiusPx}px`,
177
+ boxShadow: NATIVE_COMMAND_SURFACE.shadow,
141
178
  overflow: "hidden",
142
- padding: `${NATIVE_COMMAND_BAR_PADDING_Y_PX}px ${NATIVE_COMMAND_BAR_PADDING_X_PX}px`,
179
+ padding: `${NATIVE_COMMAND_SURFACE.paddingYPx}px ${NATIVE_COMMAND_SURFACE.paddingXPx}px`,
143
180
  } : undefined}
144
181
  >
145
182
  <CommandBarPanelBody
@@ -149,6 +186,7 @@ export function CommandBarPanel({
149
186
  contentPadding={contentPadding}
150
187
  currentRoute={currentRoute}
151
188
  getWorkflowInputRef={getWorkflowInputRef}
189
+ hasChromeRow={hasChromeRow}
152
190
  labelWidth={labelWidth}
153
191
  listBodyHeight={listBodyHeight}
154
192
  nativeListRows={nativeListRows}
@@ -167,7 +205,6 @@ export function CommandBarPanel({
167
205
  onMultiSelectSelect={onMultiSelectSelect}
168
206
  onMultiSelectToggle={onMultiSelectToggle}
169
207
  onNativeSelectRef={onNativeSelectRef}
170
- onQueryChange={onQueryChange}
171
208
  onThemeCommit={onThemeCommit}
172
209
  onThemePreview={onThemePreview}
173
210
  onWorkflowActiveTextareaSync={onWorkflowActiveTextareaSync}
@@ -185,8 +222,6 @@ export function CommandBarPanel({
185
222
  panelBg,
186
223
  }}
187
224
  queryDisplayWidth={queryDisplayWidth}
188
- rootGhostSuffix={rootGhostSuffix}
189
- rootQueryLength={rootQueryLength}
190
225
  rootShortcutFeedback={rootShortcutFeedback}
191
226
  themePickerActive={themePickerActive}
192
227
  themePickerFilter={themePickerFilter}
@@ -0,0 +1,139 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { resolveAppHeaderHeightCells, resolveHeaderPromptGeometry } from "../../layout/shell/chrome";
3
+ import { resolveCommandBarPanelLayout } from "./layout";
4
+
5
+ const BASE = {
6
+ cellHeightPx: 0,
7
+ cellWidthPx: 0,
8
+ currentRoute: null,
9
+ hasRootFeedback: false,
10
+ hasVisibleListState: true,
11
+ nativeListRowCount: 8,
12
+ nativePaneChrome: false,
13
+ showCustomMultiSelectPicker: false,
14
+ themePickerActive: false,
15
+ themePickerRowCount: 0,
16
+ titleBarOverlay: undefined as boolean | undefined,
17
+ };
18
+
19
+ const DESKTOP = {
20
+ ...BASE,
21
+ cellHeightPx: 18,
22
+ cellWidthPx: 8,
23
+ nativePaneChrome: true,
24
+ nativeWindowChrome: true,
25
+ titleBarOverlay: true,
26
+ };
27
+
28
+ describe("command bar sheet geometry", () => {
29
+ /**
30
+ * The sheet is the header prompt expanding downward: same left edge, same
31
+ * width, top edge on the header's bottom edge. Any drift between the two
32
+ * reads as a banner, or a floating window, hanging near the prompt.
33
+ *
34
+ * The widths are pinned because the header now spends columns on the market
35
+ * cluster as well as the update status, and the sheet pays for them: a
36
+ * change there silently narrows every result row.
37
+ */
38
+ test("shares the prompt's left edge and width on every window size", () => {
39
+ const expectedWidths: Record<number, number> = { 46: 40, 80: 49, 120: 63, 200: 96 };
40
+ for (const termWidth of [46, 80, 120, 200]) {
41
+ const layout = resolveCommandBarPanelLayout({ ...BASE, termHeight: 40, termWidth });
42
+ const prompt = resolveHeaderPromptGeometry({ termWidth });
43
+ expect(prompt.width).toBe(expectedWidths[termWidth]!);
44
+ expect(prompt.left + prompt.width + prompt.marketColumns).toBeLessThanOrEqual(termWidth);
45
+ expect(layout.panelBounds).toEqual({
46
+ x: prompt.left,
47
+ y: resolveAppHeaderHeightCells({}),
48
+ width: prompt.width,
49
+ height: layout.panelBounds.height,
50
+ });
51
+ expect(layout.nativeOccluderRect).toEqual({
52
+ x: prompt.left,
53
+ y: 0,
54
+ width: prompt.width,
55
+ height: layout.panelBounds.height,
56
+ });
57
+ }
58
+
59
+ // A titlebar-overlay header is 28px tall, so its height in cells is fractional.
60
+ const desktop = resolveCommandBarPanelLayout({ ...DESKTOP, termHeight: 40, termWidth: 200 });
61
+ const desktopPrompt = resolveHeaderPromptGeometry({
62
+ nativePaneChrome: true,
63
+ nativeWindowChrome: true,
64
+ termWidth: 200,
65
+ titleBarOverlay: true,
66
+ });
67
+ expect(desktop.panelBounds.x).toBe(desktopPrompt.left);
68
+ expect(desktop.panelBounds.width).toBe(desktopPrompt.width);
69
+ expect(desktop.panelBounds.y).toBeCloseTo(28 / 18, 6);
70
+ });
71
+
72
+ /**
73
+ * The selection bar is the row box, padding included, so the columns have
74
+ * to add back up to the sheet: nothing capped short of it, nothing past it.
75
+ */
76
+ test("fills the sheet with the results columns", () => {
77
+ const terminal = resolveCommandBarPanelLayout({ ...BASE, termHeight: 40, termWidth: 200 });
78
+ expect(terminal.queryDisplayWidth + terminal.contentPadding * 2).toBe(terminal.panelBounds.width);
79
+ expect(terminal.labelWidth + terminal.trailingWidth).toBe(terminal.resultsInnerWidth);
80
+ expect(terminal.trailingWidth).toBe(12);
81
+
82
+ const desktop = resolveCommandBarPanelLayout({ ...DESKTOP, termHeight: 40, termWidth: 200 });
83
+ expect(desktop.queryDisplayWidth + desktop.contentPadding * 2 + desktop.nativePanelPaddingColumns)
84
+ .toBe(desktop.panelBounds.width);
85
+ });
86
+
87
+ /**
88
+ * A 24-row terminal keeps its 16 rows; from there the list takes about
89
+ * 55% of the window, up to 26 rows, so a tall window shows more of a
90
+ * multi-line document list. The chrome row and its blank line come out of
91
+ * the same budget, so the sheet's footprint does not grow when they show.
92
+ */
93
+ test("grows with the window past 24 rows and caps at 26", () => {
94
+ const expected: Array<[number, number]> = [[24, 16], [40, 22], [60, 26]];
95
+ for (const [termHeight, bodyHeight] of expected) {
96
+ const plain = resolveCommandBarPanelLayout({ ...BASE, termHeight, termWidth: 120 });
97
+ expect(plain.bodyHeight).toBe(bodyHeight);
98
+ expect(plain.hasChromeRow).toBe(false);
99
+ expect(plain.panelBounds.height).toBe(bodyHeight + 2);
100
+
101
+ const withChrome = resolveCommandBarPanelLayout({ ...BASE, hasRootFeedback: true, termHeight, termWidth: 120 });
102
+ expect(withChrome.hasChromeRow).toBe(true);
103
+ expect(withChrome.panelBounds.height).toBe(withChrome.bodyHeight + 4);
104
+ expect(withChrome.panelBounds.height).toBeLessThanOrEqual(plain.panelBounds.height);
105
+ }
106
+ });
107
+
108
+ /**
109
+ * The theme picker keeps its own rows rather than a list state, so it has to
110
+ * hand the layout a count of its own. Without one it was excluded from the
111
+ * compact path entirely and the sheet opened at full height over a dozen
112
+ * themes.
113
+ */
114
+ test("sizes the desktop sheet to the theme picker's own rows", () => {
115
+ const picker = (themePickerRowCount: number) => resolveCommandBarPanelLayout({
116
+ ...DESKTOP,
117
+ hasVisibleListState: false,
118
+ termHeight: 60,
119
+ termWidth: 200,
120
+ themePickerActive: true,
121
+ themePickerRowCount,
122
+ });
123
+
124
+ expect(picker(4).bodyHeight).toBe(4);
125
+ expect(picker(1).bodyHeight).toBe(1);
126
+ // More themes than the sheet can hold falls back to the full budget and scrolls.
127
+ expect(picker(200).bodyHeight).toBe(picker(200).baseBodyHeight);
128
+ });
129
+
130
+ /** The bottom rows of a short terminal stay clear at every sheet height. */
131
+ test("never reaches the last terminal row", () => {
132
+ for (const termHeight of [18, 24, 40]) {
133
+ for (const hasRootFeedback of [false, true]) {
134
+ const layout = resolveCommandBarPanelLayout({ ...BASE, hasRootFeedback, termHeight, termWidth: 80 });
135
+ expect(layout.panelBounds.y + layout.panelBounds.height).toBeLessThan(termHeight - 1);
136
+ }
137
+ }
138
+ });
139
+ });
@@ -1,13 +1,30 @@
1
1
  import type { LayoutBounds } from "../../../plugins/pane-manager";
2
- import { resolveAppHeaderHeightCells } from "../../layout/shell/chrome";
2
+ import { resolveAppHeaderHeightCells, resolveHeaderPromptGeometry } from "../../layout/shell/chrome";
3
3
  import { estimateWorkflowBodyRows } from "../workflow/fields";
4
+ import { NATIVE_COMMAND_SURFACE } from "./native-surface";
4
5
  import type { CommandBarRoute } from "../workflow/types";
5
6
 
7
+ const BODY_MIN_ROWS = 9;
8
+ const BODY_MAX_ROWS = 26;
9
+ /** Rows a short window keeps regardless of its share, so 24-row terminals lose nothing. */
10
+ const BODY_SHORT_WINDOW_ROWS = 16;
11
+ /** Share of the window a tall one gives the list; multi-line document rows need the room. */
12
+ const BODY_HEIGHT_SHARE = 0.55;
13
+ /** Rows of the app that stay visible under the sheet on the terminal. */
14
+ const TERMINAL_BOTTOM_CLEARANCE = 6;
15
+ const DESKTOP_BOTTOM_CLEARANCE = 4;
16
+ /** One padding row above the list and one below. */
17
+ const SHEET_PADDING_ROWS = 2;
18
+ /** The chrome row plus the blank line that separates it from the first heading. */
19
+ const CHROME_ROWS = 2;
20
+
6
21
  export interface CommandBarPanelLayout {
7
22
  barWidth: number;
8
23
  baseBodyHeight: number;
9
24
  bodyHeight: number;
10
25
  contentPadding: number;
26
+ /** True when the sheet opens with a chrome row (back link, feedback) above the list. */
27
+ hasChromeRow: boolean;
11
28
  listBodyHeight: number;
12
29
  nativeOccluderRect: LayoutBounds;
13
30
  nativePanelPaddingColumns: number;
@@ -23,67 +40,84 @@ export function resolveCommandBarPanelLayout({
23
40
  cellHeightPx,
24
41
  cellWidthPx,
25
42
  currentRoute,
43
+ hasRootFeedback,
26
44
  hasVisibleListState,
27
45
  nativeListRowCount,
28
46
  nativePaneChrome,
47
+ nativeWindowChrome,
29
48
  showCustomMultiSelectPicker,
30
49
  termHeight,
31
50
  termWidth,
32
51
  themePickerActive,
52
+ themePickerRowCount,
33
53
  titleBarOverlay,
34
54
  }: {
35
55
  cellHeightPx: number;
36
56
  cellWidthPx: number;
37
57
  currentRoute: CommandBarRoute | null;
58
+ hasRootFeedback: boolean;
38
59
  hasVisibleListState: boolean;
39
60
  nativeListRowCount: number;
40
61
  nativePaneChrome: boolean;
62
+ nativeWindowChrome?: boolean;
41
63
  showCustomMultiSelectPicker: boolean;
42
64
  termHeight: number;
43
65
  termWidth: number;
44
66
  themePickerActive: boolean;
67
+ themePickerRowCount: number;
45
68
  titleBarOverlay: boolean | undefined;
46
69
  }): CommandBarPanelLayout {
47
- const barWidth = nativePaneChrome
48
- ? Math.max(46, Math.min(78, termWidth - 10, Math.floor(termWidth * 0.64)))
49
- : Math.max(42, Math.min(72, termWidth - 8, Math.floor(termWidth * 0.68)));
50
- const baseBodyHeight = Math.min(16, Math.max(9, termHeight - 9));
70
+ const prompt = resolveHeaderPromptGeometry({ nativePaneChrome, nativeWindowChrome, termWidth, titleBarOverlay });
71
+ const barWidth = prompt.width;
51
72
  const contentPadding = nativePaneChrome ? 1 : 3;
73
+ const nativePanelPaddingColumns = nativePaneChrome
74
+ ? Math.ceil((NATIVE_COMMAND_SURFACE.paddingXPx * 2) / Math.max(1, cellWidthPx))
75
+ : 0;
76
+ const nativePanelPaddingRows = nativePaneChrome
77
+ ? Math.ceil((NATIVE_COMMAND_SURFACE.paddingYPx * 2) / Math.max(1, cellHeightPx))
78
+ : 0;
79
+ // A nested screen shows its back link; the root shows shortcut feedback when
80
+ // there is any. Otherwise the list starts right under the padding row.
81
+ const hasChromeRow = currentRoute !== null || hasRootFeedback;
82
+ const paddingRows = nativePaneChrome ? nativePanelPaddingRows : SHEET_PADDING_ROWS;
83
+ const chromeRows = hasChromeRow ? CHROME_ROWS : 0;
84
+ const bottomClearance = nativePaneChrome ? DESKTOP_BOTTOM_CLEARANCE : TERMINAL_BOTTOM_CLEARANCE;
85
+ // Budgeted for a sheet without a chrome row; the row and its blank line are
86
+ // paid out of the list, so the sheet's footprint does not jump when a typed
87
+ // prefix brings feedback or a nested screen brings its back link.
88
+ const listBudget = Math.max(
89
+ BODY_MIN_ROWS,
90
+ Math.min(
91
+ BODY_MAX_ROWS,
92
+ termHeight - bottomClearance - paddingRows,
93
+ Math.max(BODY_SHORT_WINDOW_ROWS, Math.floor(termHeight * BODY_HEIGHT_SHARE)),
94
+ ),
95
+ );
96
+ const baseBodyHeight = Math.max(BODY_MIN_ROWS, listBudget - chromeRows);
52
97
  const workflowBodyHeight = currentRoute?.kind === "workflow"
53
98
  ? Math.min(
54
- Math.max(9, termHeight - (nativePaneChrome ? 7 : 9)),
99
+ Math.max(BODY_MIN_ROWS, termHeight - bottomClearance - paddingRows - chromeRows),
55
100
  Math.max(7, estimateWorkflowBodyRows(currentRoute)),
56
101
  )
57
102
  : baseBodyHeight;
103
+ // The theme picker keeps its own rows rather than a list state, so it reports
104
+ // a count of its own; without one the sheet used to open at full height over
105
+ // a dozen themes.
106
+ const compactRowCount = themePickerActive ? themePickerRowCount : nativeListRowCount;
58
107
  const shouldUseCompactListHeight = nativePaneChrome
59
- && hasVisibleListState
60
- && !themePickerActive
108
+ && (hasVisibleListState || themePickerActive)
61
109
  && !showCustomMultiSelectPicker;
62
110
  const listBodyHeight = shouldUseCompactListHeight
63
- ? Math.min(baseBodyHeight, Math.max(1, nativeListRowCount))
111
+ ? Math.min(baseBodyHeight, Math.max(1, compactRowCount))
64
112
  : baseBodyHeight;
65
113
  const bodyHeight = currentRoute?.kind === "workflow"
66
114
  ? workflowBodyHeight
67
115
  : shouldUseCompactListHeight
68
116
  ? listBodyHeight
69
117
  : baseBodyHeight;
70
- const nativePanelPaddingColumns = nativePaneChrome
71
- ? Math.ceil((14 * 2) / Math.max(1, cellWidthPx))
72
- : 0;
73
- const nativePanelPaddingRows = nativePaneChrome
74
- ? Math.ceil((14 * 2) / Math.max(1, cellHeightPx))
75
- : 0;
76
- const nativeBodyChromeRows = (currentRoute?.kind === "workflow"
77
- || currentRoute?.kind === "confirm"
78
- || showCustomMultiSelectPicker)
79
- ? 1
80
- : currentRoute ? 3 : 2;
81
- const barHeight = nativePaneChrome
82
- ? bodyHeight + nativeBodyChromeRows + nativePanelPaddingRows
83
- : bodyHeight + 7;
84
- const barLeft = Math.max(4, Math.floor((termWidth - barWidth) / 2));
85
- const barTop = Math.max(1, Math.floor((termHeight - barHeight) / 2));
118
+ const barHeight = bodyHeight + paddingRows + chromeRows;
86
119
  const appHeaderHeight = resolveAppHeaderHeightCells({ titleBarOverlay, cellHeightPx });
120
+ const barTop = appHeaderHeight;
87
121
  const resultsInnerWidth = Math.max(12, barWidth - nativePanelPaddingColumns - contentPadding * 2);
88
122
  const trailingWidth = Math.max(8, Math.min(12, Math.floor(resultsInnerWidth * 0.18)));
89
123
  const labelWidth = Math.max(10, resultsInnerWidth - trailingWidth);
@@ -94,16 +128,19 @@ export function resolveCommandBarPanelLayout({
94
128
  baseBodyHeight,
95
129
  bodyHeight,
96
130
  contentPadding,
131
+ hasChromeRow,
97
132
  listBodyHeight,
133
+ // Occluder coordinates are relative to the content area, which starts where
134
+ // the sheet does, so the sheet's own top is the content origin.
98
135
  nativeOccluderRect: {
99
- x: barLeft,
100
- y: barTop - appHeaderHeight,
136
+ x: prompt.left,
137
+ y: 0,
101
138
  width: barWidth,
102
139
  height: barHeight,
103
140
  },
104
141
  nativePanelPaddingColumns,
105
142
  panelBounds: {
106
- x: barLeft,
143
+ x: prompt.left,
107
144
  y: barTop,
108
145
  width: barWidth,
109
146
  height: barHeight,
@@ -0,0 +1,24 @@
1
+ import { blendHex, commandBarPanelBg, type ThemeColors } from "../../../theme/colors";
2
+
3
+ /**
4
+ * Desktop metrics for the command surface, which is one control in two boxes:
5
+ * the header input on top, the sheet hanging under it. They share a radius, a
6
+ * shadow and a border so the pair reads as one rounded rectangle that grew
7
+ * downward, rather than an input with a panel parked below it. The input keeps
8
+ * the top corners, the sheet the bottom ones, and neither draws the edge they
9
+ * meet on.
10
+ */
11
+ export const NATIVE_COMMAND_SURFACE = {
12
+ paddingXPx: 14,
13
+ paddingYPx: 14,
14
+ radiusPx: 8,
15
+ shadow: "0 10px 18px color-mix(in srgb, var(--gloom-bg) 34%, transparent)",
16
+ } as const;
17
+
18
+ /**
19
+ * Hairline around the surface. Lifted off the panel fill rather than the
20
+ * header, which is the one thing the two halves do not have in common.
21
+ */
22
+ export function nativeCommandSurfaceBorder(palette: ThemeColors): string {
23
+ return blendHex(commandBarPanelBg(palette), palette.textBright, 0.16);
24
+ }
@@ -0,0 +1,54 @@
1
+ import { useSyncExternalStore } from "react";
2
+
3
+ /**
4
+ * What the header prompt needs to act as the command bar's input while the bar
5
+ * is open. Published by the panel, read by the header.
6
+ *
7
+ * A store rather than app state or context: the header and the command bar are
8
+ * siblings, and the query of a nested screen (a picker, pane settings) lives in
9
+ * the bar's own route stack, not in app state, so `state.commandBarQuery` alone
10
+ * cannot drive the input.
11
+ */
12
+ export interface CommandBarPromptBinding {
13
+ /** Changes per screen so the input remounts with a fresh buffer. */
14
+ screenKey: string;
15
+ query: string;
16
+ placeholder: string;
17
+ /** Completion drawn after the typed text on the root screen, e.g. the ticker after "QQ". */
18
+ ghostSuffix: string | null;
19
+ onQueryChange: (query: string) => void;
20
+ }
21
+
22
+ let binding: CommandBarPromptBinding | null = null;
23
+ const listeners = new Set<() => void>();
24
+
25
+ function sameBinding(a: CommandBarPromptBinding | null, b: CommandBarPromptBinding | null): boolean {
26
+ if (a === b) return true;
27
+ if (!a || !b) return false;
28
+ return a.screenKey === b.screenKey
29
+ && a.query === b.query
30
+ && a.placeholder === b.placeholder
31
+ && a.ghostSuffix === b.ghostSuffix
32
+ && a.onQueryChange === b.onQueryChange;
33
+ }
34
+
35
+ export function publishCommandBarPrompt(next: CommandBarPromptBinding | null): void {
36
+ if (sameBinding(binding, next)) return;
37
+ binding = next;
38
+ for (const listener of listeners) listener();
39
+ }
40
+
41
+ function subscribe(listener: () => void): () => void {
42
+ listeners.add(listener);
43
+ return () => {
44
+ listeners.delete(listener);
45
+ };
46
+ }
47
+
48
+ function getSnapshot(): CommandBarPromptBinding | null {
49
+ return binding;
50
+ }
51
+
52
+ export function useCommandBarPromptBinding(): CommandBarPromptBinding | null {
53
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
54
+ }
@@ -54,6 +54,7 @@ interface CommandBarPanelRuntimeOptions {
54
54
  moveWorkflowFocus: (delta: number) => void;
55
55
  nativeListScrollRef: RefObject<ScrollBoxRenderable | null>;
56
56
  nativePaneChrome: boolean;
57
+ nativeWindowChrome?: boolean;
57
58
  onNativeOccluderChange?: (rect: LayoutBounds | null) => void;
58
59
  openWorkflowFieldPicker: OpenWorkflowFieldPicker;
59
60
  persistConfig: (nextConfig: AppState["config"]) => void;
@@ -62,7 +63,6 @@ interface CommandBarPanelRuntimeOptions {
62
63
  resetAssist: () => boolean;
63
64
  rootGhostSuffix: string | null;
64
65
  rootModeKind: string;
65
- rootQueryLength: number;
66
66
  rootShortcutFeedback: string | null;
67
67
  routeListState: ListScreenState | null;
68
68
  setActiveListQuery: (query: string) => void;
@@ -107,6 +107,7 @@ export function useCommandBarPanelRuntime({
107
107
  moveWorkflowFocus,
108
108
  nativeListScrollRef,
109
109
  nativePaneChrome,
110
+ nativeWindowChrome,
110
111
  onNativeOccluderChange,
111
112
  openWorkflowFieldPicker,
112
113
  persistConfig,
@@ -115,7 +116,6 @@ export function useCommandBarPanelRuntime({
115
116
  resetAssist,
116
117
  rootGhostSuffix,
117
118
  rootModeKind,
118
- rootQueryLength,
119
119
  rootShortcutFeedback,
120
120
  routeListState,
121
121
  setActiveListQuery,
@@ -212,12 +212,15 @@ export function useCommandBarPanelRuntime({
212
212
  cellWidthPx,
213
213
  currentRoute,
214
214
  nativePaneChrome,
215
+ nativeWindowChrome,
216
+ rootShortcutFeedback,
215
217
  routeListState,
216
218
  setRootSelectedIdx,
217
219
  showCustomMultiSelectPicker,
218
220
  termHeight,
219
221
  termWidth,
220
222
  themePickerActive,
223
+ themePickerFilter,
221
224
  titleBarOverlay,
222
225
  updateTopRoute,
223
226
  visibleListStateRef,
@@ -240,6 +243,7 @@ export function useCommandBarPanelRuntime({
240
243
  contentPadding: panelLayout.contentPadding,
241
244
  currentRoute,
242
245
  getWorkflowInputRef,
246
+ hasChromeRow: panelLayout.hasChromeRow,
243
247
  labelWidth: panelLayout.labelWidth,
244
248
  listBodyHeight: panelLayout.listBodyHeight,
245
249
  nativeListRows,
@@ -269,7 +273,6 @@ export function useCommandBarPanelRuntime({
269
273
  panelBounds: panelLayout.panelBounds,
270
274
  queryDisplayWidth: panelLayout.queryDisplayWidth,
271
275
  rootGhostSuffix,
272
- rootQueryLength,
273
276
  rootShortcutFeedback,
274
277
  selectedScrollRowIndex,
275
278
  termHeight,