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
@@ -1,4 +1,4 @@
1
- import { Box, Span, Text, TextAttributes, contextMenuDivider, useContextMenu, useUiCapabilities } from "../../ui";
1
+ import { Box, Text, TextAttributes, contextMenuDivider, useContextMenu, useUiCapabilities } from "../../ui";
2
2
  import { useDialog, type PromptContext } from "../../ui/dialog";
3
3
  import { useCallback, useMemo, useState } from "react";
4
4
  import { blendHex, hoverBg } from "../../theme/colors";
@@ -11,6 +11,7 @@ import {
11
11
  selectSavedLayouts,
12
12
  selectStatusBarVisible,
13
13
  } from "../../state/selectors-ui";
14
+ import { useViewport } from "../../react/input";
14
15
  import { getSharedRegistry } from "../../plugins/registry";
15
16
  import {
16
17
  gridlockAllPanes,
@@ -20,6 +21,7 @@ import { notifyGridlockComplete } from "../../plugins/gridlock-notification";
20
21
  import { PluginSlot } from "../../react/plugins/plugin-slot";
21
22
  import type { ContextMenuItem } from "../../types/context-menu";
22
23
  import type { LayoutConfig } from "../../types/config";
24
+ import { VERSION } from "../../version";
23
25
  import { ConfirmDialog } from "../ui/confirm-dialog";
24
26
  import { Tabs } from "../ui/tabs";
25
27
  import { useTransientLayout } from "./transient-layout";
@@ -28,6 +30,11 @@ type StatusBarEvent = { stopPropagation?: () => void; preventDefault?: () => voi
28
30
  type HoveredControl = string | null;
29
31
  type SetHoveredControl = (updater: (current: HoveredControl) => HoveredControl) => void;
30
32
 
33
+ /** Rendered width of the Tidy Windows control, including its leading gap. */
34
+ const TIDY_WINDOWS_COLUMNS = 15;
35
+ /** Space held back for the `status:widget` plugin slot, which sizes itself. */
36
+ const STATUS_WIDGET_COLUMNS = 20;
37
+
31
38
  type LayoutTabItem = {
32
39
  label: string;
33
40
  value: string;
@@ -45,8 +52,9 @@ type StatusBarViewProps = {
45
52
  hoveredControl: HoveredControl;
46
53
  layoutTabItems: LayoutTabItem[];
47
54
  layoutTabsWidth: number;
48
- openCommandBar: (event?: StatusBarEvent) => void;
55
+ openChangelog?: (event?: StatusBarEvent) => void;
49
56
  openLayoutContextMenu: (index: number, event: any) => void | Promise<unknown>;
57
+ rightAvailableWidth: number;
50
58
  setHoveredControl: SetHoveredControl;
51
59
  showTidyWindows: boolean;
52
60
  };
@@ -58,12 +66,13 @@ function truncate(text: string, width: number): string {
58
66
  return `${text.slice(0, width - 2)}..`;
59
67
  }
60
68
 
61
- export function StatusBar() {
69
+ export function StatusBar({ onOpenChangelog }: { onOpenChangelog?: (version: string) => void } = {}) {
62
70
  const { nativePaneChrome, nativeContextMenu } = useUiCapabilities();
63
71
  const { showContextMenu } = useContextMenu();
64
72
  const dialog = useDialog();
65
73
  const registry = getSharedRegistry();
66
74
  const dispatch = useAppDispatch();
75
+ const { width: termWidth } = useViewport();
67
76
  const layouts = useAppSelector(selectSavedLayouts);
68
77
  const activeLayoutIdx = useAppSelector(selectActiveLayoutIndex);
69
78
  const statusBarVisible = useAppSelector(selectStatusBarVisible);
@@ -143,10 +152,10 @@ export function StatusBar() {
143
152
  });
144
153
  };
145
154
 
146
- const openCommandBar = (event?: StatusBarEvent) => {
155
+ const openChangelog = (event?: StatusBarEvent) => {
147
156
  event?.preventDefault?.();
148
157
  event?.stopPropagation?.();
149
- dispatch({ type: "SET_COMMAND_BAR", open: true, query: "" });
158
+ onOpenChangelog?.(VERSION);
150
159
  };
151
160
 
152
161
  const requestDeleteLayout = useCallback(async (index: number) => {
@@ -263,6 +272,10 @@ export function StatusBar() {
263
272
 
264
273
  if (!statusBarVisible) return null;
265
274
 
275
+ const leftWidth = 1
276
+ + (hasMultipleLayouts ? layoutTabsWidth : 0)
277
+ + (showTidyWindows ? TIDY_WINDOWS_COLUMNS : 0);
278
+
266
279
  const viewProps: StatusBarViewProps = {
267
280
  activeLayoutIdx,
268
281
  activeLayoutValue,
@@ -273,8 +286,9 @@ export function StatusBar() {
273
286
  hoveredControl,
274
287
  layoutTabItems,
275
288
  layoutTabsWidth,
276
- openCommandBar,
289
+ openChangelog: onOpenChangelog ? openChangelog : undefined,
277
290
  openLayoutContextMenu,
291
+ rightAvailableWidth: Math.max(0, termWidth - leftWidth - STATUS_WIDGET_COLUMNS),
278
292
  setHoveredControl,
279
293
  showTidyWindows,
280
294
  };
@@ -311,7 +325,9 @@ function NativeStatusBar({
311
325
  >
312
326
  <StatusBarLayoutControl nativePaneChrome {...props} />
313
327
  {showTidyWindows && <NativeTidyWindows {...props} />}
314
- <StatusBarWidgets />
328
+ <Box flexGrow={1} minWidth={0} />
329
+ <StatusBarSummary nativePaneChrome {...props} />
330
+ <PluginSlot name="status:widget" />
315
331
  </Box>
316
332
  );
317
333
  }
@@ -336,7 +352,9 @@ function TerminalStatusBar({
336
352
  >
337
353
  <StatusBarLayoutControl nativePaneChrome={false} {...props} />
338
354
  {showTidyWindows && <TerminalTidyWindows {...props} />}
339
- <StatusBarWidgets />
355
+ <Box flexGrow={1} minWidth={0} />
356
+ <StatusBarSummary nativePaneChrome={false} {...props} />
357
+ <PluginSlot name="status:widget" />
340
358
  </Box>
341
359
  );
342
360
  }
@@ -346,24 +364,19 @@ function StatusBarLayoutControl({
346
364
  handleLayoutSelect,
347
365
  handleLayoutReorder,
348
366
  hasMultipleLayouts,
349
- hoveredControl,
350
367
  layoutTabItems,
351
368
  layoutTabsWidth,
352
369
  nativePaneChrome,
353
- openCommandBar,
354
- setHoveredControl,
355
370
  }: Pick<
356
371
  StatusBarViewProps,
357
372
  | "activeLayoutValue"
358
373
  | "handleLayoutSelect"
359
374
  | "handleLayoutReorder"
360
375
  | "hasMultipleLayouts"
361
- | "hoveredControl"
362
376
  | "layoutTabItems"
363
377
  | "layoutTabsWidth"
364
- | "openCommandBar"
365
- | "setHoveredControl"
366
378
  > & { nativePaneChrome: boolean }) {
379
+ if (!hasMultipleLayouts) return null;
367
380
  return (
368
381
  <Box
369
382
  paddingLeft={1}
@@ -371,49 +384,76 @@ function StatusBarLayoutControl({
371
384
  flexDirection="row"
372
385
  {...(nativePaneChrome ? { alignItems: "center", gap: 1 } : {})}
373
386
  >
374
- {hasMultipleLayouts ? (
375
- <Box width={layoutTabsWidth} height={1}>
376
- <Tabs
377
- tabs={layoutTabItems}
378
- activeValue={activeLayoutValue}
379
- onSelect={handleLayoutSelect}
380
- onReorder={handleLayoutReorder}
381
- compact
382
- variant="pill"
383
- />
384
- </Box>
385
- ) : (
386
- <CommandBarHint
387
- hoveredControl={hoveredControl}
388
- nativePaneChrome={nativePaneChrome}
389
- openCommandBar={openCommandBar}
390
- setHoveredControl={setHoveredControl}
387
+ <Box width={layoutTabsWidth} height={1}>
388
+ <Tabs
389
+ tabs={layoutTabItems}
390
+ activeValue={activeLayoutValue}
391
+ onSelect={handleLayoutSelect}
392
+ onReorder={handleLayoutReorder}
393
+ compact
394
+ variant="pill"
391
395
  />
392
- )}
396
+ </Box>
393
397
  </Box>
394
398
  );
395
399
  }
396
400
 
397
- function CommandBarHint({
401
+ /**
402
+ * The version chip, dropped when the row runs out of room. Live market status
403
+ * lives at the header's right edge, not here, so nothing in the status bar
404
+ * repeats it.
405
+ */
406
+ function StatusBarSummary({
407
+ hoveredControl,
408
+ nativePaneChrome,
409
+ openChangelog,
410
+ rightAvailableWidth,
411
+ setHoveredControl,
412
+ }: Pick<
413
+ StatusBarViewProps,
414
+ "hoveredControl" | "openChangelog" | "rightAvailableWidth" | "setHoveredControl"
415
+ > & { nativePaneChrome: boolean }) {
416
+ const versionLabel = `v${VERSION}`;
417
+ if (rightAvailableWidth < versionLabel.length + 1) return null;
418
+ return (
419
+ <VersionChip
420
+ hoveredControl={hoveredControl}
421
+ label={versionLabel}
422
+ nativePaneChrome={nativePaneChrome}
423
+ openChangelog={openChangelog}
424
+ setHoveredControl={setHoveredControl}
425
+ />
426
+ );
427
+ }
428
+
429
+ function VersionChip({
398
430
  hoveredControl,
431
+ label,
399
432
  nativePaneChrome,
400
- openCommandBar,
433
+ openChangelog,
401
434
  setHoveredControl,
402
- }: Pick<StatusBarViewProps, "hoveredControl" | "openCommandBar" | "setHoveredControl"> & {
435
+ }: Pick<StatusBarViewProps, "hoveredControl" | "openChangelog" | "setHoveredControl"> & {
436
+ label: string;
403
437
  nativePaneChrome: boolean;
404
438
  }) {
405
439
  const colors = useThemeColors();
406
- const hovered = hoveredControl === "command-bar";
440
+ const hovered = hoveredControl === "version";
407
441
  return (
408
- <Text
409
- fg={hovered ? colors.text : colors.textDim}
410
- {...(!nativePaneChrome ? { bg: hovered ? hoverBg(colors) : undefined } : {})}
411
- onMouseOver={() => setHoveredControl((current) => (current === "command-bar" ? current : "command-bar"))}
412
- onMouseDown={openCommandBar}
413
- {...(nativePaneChrome ? { "data-gloom-interactive": "true" } : {})}
414
- >
415
- <Span fg={colors.text}>Ctrl+P</Span> {t("command bar")}
416
- </Text>
442
+ <Box paddingRight={1} flexShrink={0}>
443
+ <Text
444
+ fg={hovered && openChangelog ? colors.text : colors.textDim}
445
+ {...(!nativePaneChrome ? { bg: hovered && openChangelog ? hoverBg(colors) : undefined } : {})}
446
+ title={openChangelog ? tf("Open changelog for {version}", { version: label }) : undefined}
447
+ aria-label={openChangelog ? tf("Open changelog for {version}", { version: label }) : undefined}
448
+ role={openChangelog ? "button" : undefined}
449
+ onMouseOver={() => setHoveredControl((current) => (current === "version" ? current : "version"))}
450
+ onMouseDown={openChangelog}
451
+ {...(nativePaneChrome && openChangelog ? { "data-gloom-interactive": "true" } : {})}
452
+ style={openChangelog ? { cursor: "pointer" } : undefined}
453
+ >
454
+ {label}
455
+ </Text>
456
+ </Box>
417
457
  );
418
458
  }
419
459
 
@@ -459,12 +499,3 @@ function TerminalTidyWindows({
459
499
  </Box>
460
500
  );
461
501
  }
462
-
463
- function StatusBarWidgets() {
464
- return (
465
- <>
466
- <Box flexGrow={1} />
467
- <PluginSlot name="status:widget" />
468
- </>
469
- );
470
- }
@@ -5,30 +5,11 @@ import { blendHex } from "../../theme/colors";
5
5
  import { useThemeColors } from "../../theme/theme-context";
6
6
  import { t } from "../../i18n";
7
7
  import { Button, ListView, type ListViewItem } from "../ui";
8
+ import { modalSurfaceStyle } from "../ui/frame";
8
9
  import type { ButtonProps } from "../ui/button";
9
10
  import { Tabs } from "../ui/tabs";
10
11
  import { TITLEBAR_OVERLAY_HEIGHT_PX } from "../layout/titlebar-overlay";
11
12
 
12
- function desktopSurfaceStyle(
13
- borderColor: string,
14
- backgroundColor: string,
15
- shadowColor: string,
16
- highlightColor: string,
17
- ) {
18
- return {
19
- width: "min(540px, 100%)",
20
- height: "auto",
21
- maxHeight: "calc(100vh - 88px)",
22
- padding: 14,
23
- backgroundColor,
24
- border: `1px solid ${borderColor}`,
25
- borderRadius: 6,
26
- boxShadow: `0 18px 48px color-mix(in srgb, ${shadowColor} 46%, transparent), inset 0 1px 0 color-mix(in srgb, ${highlightColor} 5%, transparent)`,
27
- boxSizing: "border-box" as const,
28
- overflowY: "auto" as const,
29
- };
30
- }
31
-
32
13
  export function OnboardingModal({
33
14
  children,
34
15
  width = 66,
@@ -61,12 +42,7 @@ export function OnboardingModal({
61
42
  >
62
43
  <Box
63
44
  flexDirection="column"
64
- style={desktopSurfaceStyle(
65
- blendHex(colors.border, colors.borderFocused, 0.18),
66
- blendHex(colors.panel, colors.bg, 0.12),
67
- colors.bg,
68
- colors.textBright,
69
- )}
45
+ style={modalSurfaceStyle(colors)}
70
46
  data-gloom-role="onboarding-modal"
71
47
  >
72
48
  {children}
@@ -1,46 +1,73 @@
1
1
  import type { PricePoint } from "../../types/financials";
2
- import { colors } from "../../theme/colors";
3
- import { renderChart, resolveChartPalette, type StyledContent } from "../chart/core/renderer";
4
- import type { ProjectedChartPoint } from "../chart/core/data";
5
2
 
6
- function coercePointDate(value: PricePoint["date"]): Date {
7
- return value instanceof Date ? value : new Date(value as Date | string | number);
3
+ // Braille cells pack a 2x4 dot grid; these are the Unicode bit offsets by dot.
4
+ const BRAILLE_DOT_BITS = [
5
+ [0x01, 0x02, 0x04, 0x40],
6
+ [0x08, 0x10, 0x20, 0x80],
7
+ ] as const;
8
+ const BRAILLE_BASE = 0x2800;
9
+
10
+ function plotLine(
11
+ dots: Uint8Array,
12
+ dotWidth: number,
13
+ dotHeight: number,
14
+ x0: number,
15
+ y0: number,
16
+ x1: number,
17
+ y1: number,
18
+ ): void {
19
+ const steps = Math.max(Math.abs(x1 - x0), Math.abs(y1 - y0), 1);
20
+ for (let step = 0; step <= steps; step += 1) {
21
+ const x = Math.round(x0 + ((x1 - x0) * step) / steps);
22
+ const y = Math.round(y0 + ((y1 - y0) * step) / steps);
23
+ if (x >= 0 && x < dotWidth && y >= 0 && y < dotHeight) dots[y * dotWidth + x] = 1;
24
+ }
25
+ }
26
+
27
+ /** Draws closes as a braille line, one text row per cell row. */
28
+ function renderBrailleSparkline(values: readonly number[], width: number, height: number): string[] {
29
+ const cellWidth = Math.max(1, Math.floor(width));
30
+ const cellHeight = Math.max(1, Math.floor(height));
31
+ const dotWidth = cellWidth * 2;
32
+ const dotHeight = cellHeight * 4;
33
+ const dots = new Uint8Array(dotWidth * dotHeight);
34
+ const min = Math.min(...values);
35
+ const max = Math.max(...values);
36
+ const range = max - min || 1;
37
+ const toX = (index: number) => (index / Math.max(values.length - 1, 1)) * (dotWidth - 1);
38
+ const toY = (value: number) => (1 - (value - min) / range) * (dotHeight - 1);
39
+ for (let index = 1; index < values.length; index += 1) {
40
+ plotLine(dots, dotWidth, dotHeight, toX(index - 1), toY(values[index - 1]!), toX(index), toY(values[index]!));
41
+ }
42
+ const rows: string[] = [];
43
+ for (let row = 0; row < cellHeight; row += 1) {
44
+ let text = "";
45
+ for (let column = 0; column < cellWidth; column += 1) {
46
+ let bits = 0;
47
+ for (let dx = 0; dx < 2; dx += 1) {
48
+ for (let dy = 0; dy < 4; dy += 1) {
49
+ if (dots[(row * 4 + dy) * dotWidth + column * 2 + dx]) bits |= BRAILLE_DOT_BITS[dx]![dy]!;
50
+ }
51
+ }
52
+ text += String.fromCharCode(BRAILLE_BASE + bits);
53
+ }
54
+ rows.push(text);
55
+ }
56
+ return rows;
8
57
  }
9
58
 
59
+ /** Text fallback for terminals without pixel graphics: the top row of a braille line. */
10
60
  export function renderPriceSparkline(
11
61
  priceHistory: PricePoint[],
12
62
  options: { width?: number; height?: number; maxPoints?: number } = {},
13
- ): StyledContent | null {
63
+ ): string | null {
14
64
  if (priceHistory.length < 2) return null;
15
-
16
65
  const width = Math.max(4, Math.floor(options.width ?? 10));
17
66
  const height = Math.max(1, Math.floor(options.height ?? 1));
18
67
  const maxPoints = Math.max(1, Math.floor(options.maxPoints ?? Math.max(20, width * 2)));
19
- const pointsWindow = priceHistory.slice(-maxPoints);
20
- const points: ProjectedChartPoint[] = pointsWindow.map((pt) => ({
21
- date: coercePointDate(pt.date),
22
- open: pt.open ?? pt.close,
23
- high: pt.high ?? pt.close,
24
- low: pt.low ?? pt.close,
25
- close: pt.close,
26
- volume: pt.volume ?? 0,
27
- }));
28
-
29
- const first = pointsWindow[0]?.close ?? 0;
30
- const last = pointsWindow[pointsWindow.length - 1]?.close ?? 0;
31
- const trend = last > first ? "positive" : last < first ? "negative" : "neutral";
32
- const palette = resolveChartPalette(colors, trend);
33
-
34
- const result = renderChart(points, {
35
- width,
36
- height,
37
- showVolume: false,
38
- volumeHeight: 0,
39
- cursorX: null,
40
- cursorY: null,
41
- mode: "line",
42
- colors: palette,
43
- });
44
-
45
- return result.lines[0] ?? null;
68
+ const values = priceHistory.slice(-maxPoints)
69
+ .map((point) => point.close)
70
+ .filter((value) => Number.isFinite(value));
71
+ if (values.length < 2) return null;
72
+ return renderBrailleSparkline(values, width, height)[0] ?? null;
46
73
  }
@@ -130,7 +130,7 @@ function TerminalPriceSparkline({
130
130
 
131
131
  return (
132
132
  <ChartSurface width={width} height={height} flexDirection="column" bitmaps={bitmap ? [bitmap] : null}>
133
- {fallback ? <Text content={fallback} /> : null}
133
+ {fallback ? <Text fg={color}>{fallback}</Text> : null}
134
134
  </ChartSurface>
135
135
  );
136
136
  }
@@ -0,0 +1,128 @@
1
+ /** @jsxImportSource react */
2
+ import { Window } from "happy-dom";
3
+
4
+ const testWindow = new Window({ url: "http://localhost" });
5
+ const domGlobals = {
6
+ IS_REACT_ACT_ENVIRONMENT: true,
7
+ window: testWindow,
8
+ document: testWindow.document,
9
+ navigator: testWindow.navigator,
10
+ KeyboardEvent: testWindow.KeyboardEvent,
11
+ MouseEvent: testWindow.MouseEvent,
12
+ HTMLElement: testWindow.HTMLElement,
13
+ Node: testWindow.Node,
14
+ };
15
+
16
+ /** Bun shares one process across test files, so the DOM globals must not leak. */
17
+ const priorGlobals = Object.fromEntries(
18
+ Object.keys(domGlobals).map((key) => [key, (globalThis as Record<string, unknown>)[key]]),
19
+ );
20
+ Object.assign(globalThis, domGlobals);
21
+
22
+ import { afterAll, afterEach, expect, test } from "bun:test";
23
+ import { act } from "react";
24
+ import { createRoot } from "react-dom/client";
25
+ import { UiHostProvider, type RendererHost, type UiHost } from "../ui";
26
+ import { WebBox } from "../renderers/electrobun/view/host/box";
27
+ import { WebText, WebSpan } from "../renderers/electrobun/view/host/text";
28
+ import { WebScrollBox } from "../renderers/electrobun/view/host/scroll-box";
29
+ import { WebInput } from "../renderers/electrobun/view/host/input";
30
+ import { WebButton, WebTextField } from "../renderers/electrobun/view/desktop/controls";
31
+ import { WebInputHostProvider } from "../renderers/electrobun/view/input-host";
32
+ import { useShortcut } from "../react/input";
33
+ import { SignInGate } from "./sign-in-gate";
34
+
35
+ const renderer: RendererHost = {
36
+ requestExit() {},
37
+ async openExternal() {},
38
+ async copyText() {},
39
+ async readText() { return ""; },
40
+ notify() {},
41
+ };
42
+
43
+ const ui = {
44
+ kind: "desktop-web",
45
+ capabilities: { cellWidthPx: 8, cellHeightPx: 18, fractionalViewport: true },
46
+ Box: WebBox,
47
+ Text: WebText,
48
+ Span: WebSpan,
49
+ ScrollBox: WebScrollBox,
50
+ Button: WebButton,
51
+ Input: WebInput,
52
+ TextField: WebTextField,
53
+ SpinnerMark: () => null,
54
+ } as unknown as UiHost;
55
+
56
+ afterAll(() => {
57
+ for (const [key, value] of Object.entries(priorGlobals)) {
58
+ if (value === undefined) delete (globalThis as Record<string, unknown>)[key];
59
+ else (globalThis as Record<string, unknown>)[key] = value;
60
+ }
61
+ });
62
+
63
+ let root: ReturnType<typeof createRoot> | undefined;
64
+
65
+ afterEach(async () => {
66
+ if (root) {
67
+ await act(async () => root!.unmount());
68
+ root = undefined;
69
+ }
70
+ });
71
+
72
+ /** Stands in for the app's own global shortcuts, which register unscoped. */
73
+ function AppShortcutSpy({ onKey }: { onKey: () => void }) {
74
+ useShortcut(onKey, { phase: "before", allowEditable: true });
75
+ return null;
76
+ }
77
+
78
+ async function renderGate(onAppKey: () => void): Promise<HTMLElement> {
79
+ const container = testWindow.document.createElement("div");
80
+ testWindow.document.body.appendChild(container);
81
+ root = createRoot(container as unknown as HTMLElement);
82
+ await act(async () => {
83
+ root!.render(
84
+ <UiHostProvider ui={ui} renderer={renderer}>
85
+ <WebInputHostProvider>
86
+ <AppShortcutSpy onKey={onAppKey} />
87
+ <SignInGate />
88
+ </WebInputHostProvider>
89
+ </UiHostProvider>,
90
+ );
91
+ });
92
+ return container as unknown as HTMLElement;
93
+ }
94
+
95
+ function pressKey(key: string, target?: Element): void {
96
+ const event = new testWindow.KeyboardEvent("keydown", { key, bubbles: true, cancelable: true });
97
+ (target ?? testWindow.document.body).dispatchEvent(event);
98
+ }
99
+
100
+ test("holds every app shortcut while the gate is up", async () => {
101
+ let appKeys = 0;
102
+ await renderGate(() => { appKeys += 1; });
103
+
104
+ await act(async () => {
105
+ for (const key of ["k", "Tab", "1", "?", "q", "`"]) pressKey(key);
106
+ });
107
+
108
+ expect(appKeys).toBe(0);
109
+ });
110
+
111
+ test("still delivers typing to the gate's own fields", async () => {
112
+ let appKeys = 0;
113
+ const container = await renderGate(() => { appKeys += 1; });
114
+
115
+ const email = container.querySelector('input[type="email"]') as HTMLInputElement | null;
116
+ expect(email).not.toBeNull();
117
+
118
+ await act(async () => {
119
+ email!.value = "trader@example.com";
120
+ email!.dispatchEvent(new testWindow.Event("input", { bubbles: true }) as unknown as Event);
121
+ pressKey("t", email!);
122
+ });
123
+
124
+ // The hold sits on the window listener, which fires after the field already
125
+ // took the key, so the value survives while the app still sees nothing.
126
+ expect(email!.value).toBe("trader@example.com");
127
+ expect(appKeys).toBe(0);
128
+ });