gloomberb 0.19.2 → 0.19.3

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 (472) hide show
  1. package/README.md +7 -1
  2. package/package.json +1 -1
  3. package/patches/electrobun@1.18.1.patch +108 -1
  4. package/src/account/upgrade-dialog.tsx +4 -23
  5. package/src/api-client/coalesce.ts +118 -0
  6. package/src/api-client/credit-boards.ts +13 -0
  7. package/src/api-client/crypto-etf-flows.ts +6 -0
  8. package/src/api-client/crypto-markets.ts +40 -0
  9. package/src/api-client/data.ts +201 -14
  10. package/src/api-client/equity-screener.ts +10 -0
  11. package/src/api-client/errors.ts +20 -0
  12. package/src/api-client/gpu.ts +29 -0
  13. package/src/api-client/index.ts +5 -2
  14. package/src/api-client/plan-access.ts +10 -1
  15. package/src/api-client/plan-rules.ts +35 -0
  16. package/src/api-client/research-activity.ts +1 -32
  17. package/src/api-client/types.ts +21 -1
  18. package/src/app/attention-notice.tsx +94 -0
  19. package/src/app.tsx +23 -1
  20. package/src/architecture/core-builtin-allowlist.json +2 -1
  21. package/src/capabilities/factories.ts +1 -0
  22. package/src/cli/ascii-output.ts +42 -0
  23. package/src/cli/base-converter.ts +10 -2
  24. package/src/cli/command-options.ts +16 -1
  25. package/src/cli/commands/crypto-hints.ts +23 -4
  26. package/src/cli/commands/market.ts +92 -23
  27. package/src/cli/commands/news-search.ts +24 -5
  28. package/src/cli/commands/overview.ts +9 -4
  29. package/src/cli/commands/search.ts +71 -12
  30. package/src/cli/commands/system.ts +22 -30
  31. package/src/cli/commands/ticker.ts +49 -9
  32. package/src/cli/commands/version.ts +33 -0
  33. package/src/cli/context.ts +5 -9
  34. package/src/cli/data-dir.ts +7 -0
  35. package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
  36. package/src/cli/desktop-pane-shot-browser.ts +320 -0
  37. package/src/cli/desktop-pane-shot-routes.ts +26 -1
  38. package/src/cli/desktop-pane-shot.ts +196 -256
  39. package/src/cli/early-command.ts +42 -0
  40. package/src/cli/earnings-calendar.ts +9 -0
  41. package/src/cli/entry.ts +19 -13
  42. package/src/cli/errors.ts +12 -2
  43. package/src/cli/help.ts +1 -1
  44. package/src/cli/history-export.ts +22 -2
  45. package/src/cli/history-rows.ts +154 -4
  46. package/src/cli/index.ts +18 -1
  47. package/src/cli/known-gaps.ts +40 -5
  48. package/src/cli/listing-arg.ts +46 -9
  49. package/src/cli/options.ts +1 -1
  50. package/src/cli/output-file.ts +60 -13
  51. package/src/cli/pane-functions/catalog.ts +14 -3
  52. package/src/cli/pane-functions/data.ts +1 -1
  53. package/src/cli/pane-functions/dom.ts +45 -0
  54. package/src/cli/pane-functions/freshness.ts +46 -9
  55. package/src/cli/pane-functions/function-help.ts +17 -9
  56. package/src/cli/pane-functions/headless.ts +22 -14
  57. package/src/cli/pane-functions/index.ts +6 -2
  58. package/src/cli/pane-functions/options.ts +6 -2
  59. package/src/cli/pane-functions/resolver.ts +27 -5
  60. package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
  61. package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
  62. package/src/cli/pane-functions/screenshot-http.ts +70 -0
  63. package/src/cli/pane-functions/screenshot-render.ts +19 -5
  64. package/src/cli/quote-answer.ts +20 -0
  65. package/src/cli/registry.ts +41 -13
  66. package/src/cli/result.ts +12 -6
  67. package/src/cli/ticker-resolution.ts +2 -1
  68. package/src/cli/types.ts +2 -0
  69. package/src/components/chart/composite/format.ts +3 -0
  70. package/src/components/chart-table/header.tsx +1 -1
  71. package/src/components/chart-table/layout.ts +28 -11
  72. package/src/components/command-bar/action-runtime.ts +4 -0
  73. package/src/components/command-bar/commands/direct/index.ts +18 -33
  74. package/src/components/command-bar/commands/direct/runtime.ts +5 -0
  75. package/src/components/command-bar/commands/registry.ts +10 -1
  76. package/src/components/command-bar/routes/root/command-items.ts +9 -4
  77. package/src/components/command-bar/routes/root/runtime.ts +24 -1
  78. package/src/components/command-bar/surface/index.tsx +9 -3
  79. package/src/components/company-logo.tsx +2 -1
  80. package/src/components/layout/header.tsx +8 -3
  81. package/src/components/layout/market-summary.ts +25 -7
  82. package/src/components/layout/pane/footer/index.tsx +7 -2
  83. package/src/components/layout/pane/footer/model.ts +40 -5
  84. package/src/components/market-board/index.tsx +3 -2
  85. package/src/components/onboarding/desks-step.tsx +12 -4
  86. package/src/components/onboarding/first-run-workspace.ts +81 -4
  87. package/src/components/onboarding/onboarding-frame.tsx +2 -1
  88. package/src/components/onboarding/onboarding-keyboard.ts +8 -5
  89. package/src/components/onboarding/onboarding-wizard.tsx +54 -23
  90. package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
  91. package/src/components/onboarding/run-setup-again.ts +24 -0
  92. package/src/components/onboarding/use-onboarding-progress.ts +33 -15
  93. package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
  94. package/src/components/onboarding/wizard-model.ts +57 -3
  95. package/src/components/onboarding/wizard-positions.ts +1 -1
  96. package/src/components/ui/delayed-mark.tsx +56 -0
  97. package/src/components/ui/external-link.tsx +3 -6
  98. package/src/components/ui/icon.tsx +8 -1
  99. package/src/components/ui/logo-mark.tsx +8 -5
  100. package/src/components/ui/stat-grid.tsx +263 -19
  101. package/src/data/config/store/node.ts +11 -5
  102. package/src/data/config/store/normalize.ts +16 -3
  103. package/src/data/profile-permissions.ts +47 -0
  104. package/src/data/resource-store.ts +34 -31
  105. package/src/data/sqlite/access.ts +19 -8
  106. package/src/data/sqlite/checkpointer.ts +95 -8
  107. package/src/data/sqlite/database.ts +40 -7
  108. package/src/docs/snapshot.json +191 -78
  109. package/src/i18n/es.ts +18 -2
  110. package/src/i18n/ja.ts +18 -2
  111. package/src/i18n/ko.ts +18 -2
  112. package/src/i18n/zh-cn.ts +18 -2
  113. package/src/i18n/zh-tw.ts +18 -2
  114. package/src/layout/desk-keys.ts +4 -1
  115. package/src/layout/desks.ts +38 -5
  116. package/src/layout-marketplace/gallery-desktop.tsx +45 -16
  117. package/src/market-data/batched-reads.ts +16 -0
  118. package/src/market-data/coordinator/auxiliary.ts +4 -2
  119. package/src/market-data/coordinator/index.ts +11 -2
  120. package/src/market-data/futures-board.ts +1 -1
  121. package/src/market-data/futures-contract.ts +3 -1
  122. package/src/market-data/market/index-level.ts +6 -4
  123. package/src/market-data/published-apac-sessions.ts +6 -0
  124. package/src/news/aggregator.ts +7 -4
  125. package/src/news/news-model.ts +11 -0
  126. package/src/news/story-id.ts +22 -0
  127. package/src/news/ticker-symbols.ts +17 -1
  128. package/src/notifications/app-notifier.ts +35 -14
  129. package/src/plugins/builtin/account-management/form-components.tsx +1 -1
  130. package/src/plugins/builtin/account-management/index.tsx +2 -3
  131. package/src/plugins/builtin/account-management/model.ts +1 -1
  132. package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
  133. package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
  134. package/src/plugins/builtin/alerts/events.ts +10 -2
  135. package/src/plugins/builtin/alerts/index.tsx +5 -5
  136. package/src/plugins/builtin/alerts/research-builder.ts +5 -1
  137. package/src/plugins/builtin/alerts/research-rules.ts +18 -0
  138. package/src/plugins/builtin/alerts/targets.ts +4 -1
  139. package/src/plugins/builtin/attention/headless.ts +1 -1
  140. package/src/plugins/builtin/attention/index.tsx +2 -4
  141. package/src/plugins/builtin/attention/listing.ts +1 -1
  142. package/src/plugins/builtin/attention/series.ts +1 -1
  143. package/src/plugins/builtin/awards/index.tsx +1 -2
  144. package/src/plugins/builtin/backtest/client.ts +1 -0
  145. package/src/plugins/builtin/backtest/index.tsx +1 -2
  146. package/src/plugins/builtin/backtest/rules.ts +3 -1
  147. package/src/plugins/builtin/backtest/window.ts +8 -7
  148. package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
  149. package/src/plugins/builtin/broker-manager/index.tsx +1 -1
  150. package/src/plugins/builtin/buildout/cells.tsx +5 -0
  151. package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
  152. package/src/plugins/builtin/buildout/company/id.ts +2 -0
  153. package/src/plugins/builtin/buildout/company/model.ts +247 -0
  154. package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
  155. package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
  156. package/src/plugins/builtin/buildout/facilities.ts +309 -0
  157. package/src/plugins/builtin/buildout/format.ts +9 -0
  158. package/src/plugins/builtin/buildout/index.tsx +51 -5
  159. package/src/plugins/builtin/buildout/model/index.ts +62 -10
  160. package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
  161. package/src/plugins/builtin/buildout/model/types.ts +11 -2
  162. package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
  163. package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
  164. package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
  165. package/src/plugins/builtin/buildout/table-model.ts +26 -2
  166. package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
  167. package/src/plugins/builtin/cds/headless.ts +10 -2
  168. package/src/plugins/builtin/cds/model.ts +7 -3
  169. package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
  170. package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
  171. package/src/plugins/builtin/changelog/index.tsx +3 -2
  172. package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -2
  173. package/src/plugins/builtin/chart-composer/headless.ts +88 -21
  174. package/src/plugins/builtin/chart-composer/index.tsx +2 -3
  175. package/src/plugins/builtin/chart-composer/pane.tsx +26 -2
  176. package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
  177. package/src/plugins/builtin/chart-composer/studies.ts +1 -1
  178. package/src/plugins/builtin/chat/controller/index.ts +1 -1
  179. package/src/plugins/builtin/chat/controller/messages.ts +15 -4
  180. package/src/plugins/builtin/chat/ghost-user.ts +8 -0
  181. package/src/plugins/builtin/chat/index.tsx +5 -5
  182. package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
  183. package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
  184. package/src/plugins/builtin/cloud/index.tsx +1 -2
  185. package/src/plugins/builtin/cloud/plugin.tsx +6 -4
  186. package/src/plugins/builtin/cloud/team/module.tsx +1 -1
  187. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
  188. package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
  189. package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
  190. package/src/plugins/builtin/comment-letters/index.tsx +1 -2
  191. package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
  192. package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
  193. package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
  194. package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
  195. package/src/plugins/builtin/congress-trades/headless.ts +2 -0
  196. package/src/plugins/builtin/congress-trades/model.ts +92 -64
  197. package/src/plugins/builtin/congress-trades/table.tsx +14 -2
  198. package/src/plugins/builtin/connections/index.ts +2 -3
  199. package/src/plugins/builtin/cot/index.tsx +1 -2
  200. package/src/plugins/builtin/cpi/index.tsx +1 -2
  201. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
  202. package/src/plugins/builtin/credit-boards/client.ts +7 -2
  203. package/src/plugins/builtin/credit-boards/headless.ts +25 -12
  204. package/src/plugins/builtin/credit-boards/model.ts +29 -0
  205. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
  206. package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
  207. package/src/plugins/builtin/crypto-board/headless.ts +4 -3
  208. package/src/plugins/builtin/crypto-board/index.tsx +4 -2
  209. package/src/plugins/builtin/crypto-board/series.ts +2 -0
  210. package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
  211. package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
  212. package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
  213. package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
  214. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
  215. package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
  216. package/src/plugins/builtin/debt-maturities/model.ts +17 -1
  217. package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
  218. package/src/plugins/builtin/debug/index.tsx +1 -1
  219. package/src/plugins/builtin/dividend-yield/client.ts +32 -2
  220. package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
  221. package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
  222. package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
  223. package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
  224. package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
  225. package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
  226. package/src/plugins/builtin/dividend-yield/view.ts +21 -0
  227. package/src/plugins/builtin/docs/index.tsx +2 -3
  228. package/src/plugins/builtin/doe/headless.ts +16 -3
  229. package/src/plugins/builtin/doe/index.tsx +5 -4
  230. package/src/plugins/builtin/earnings/board.tsx +2 -1
  231. package/src/plugins/builtin/earnings/history.tsx +2 -1
  232. package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
  233. package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
  234. package/src/plugins/builtin/econ/calendar-model.ts +29 -6
  235. package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
  236. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  237. package/src/plugins/builtin/equity-screener/headless.ts +1 -0
  238. package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
  239. package/src/plugins/builtin/equity-screener/index.tsx +1 -2
  240. package/src/plugins/builtin/equity-screener/model.ts +28 -4
  241. package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
  242. package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
  243. package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
  244. package/src/plugins/builtin/exposure/holdings.ts +35 -13
  245. package/src/plugins/builtin/exposure/index.tsx +1 -2
  246. package/src/plugins/builtin/fear-greed/cli.ts +44 -0
  247. package/src/plugins/builtin/fear-greed/index.tsx +5 -53
  248. package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
  249. package/src/plugins/builtin/fixed-income/index.tsx +1 -2
  250. package/src/plugins/builtin/futures/board-reads.ts +25 -0
  251. package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
  252. package/src/plugins/builtin/futures/index.tsx +15 -0
  253. package/src/plugins/builtin/futures/model.ts +14 -0
  254. package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
  255. package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
  256. package/src/plugins/builtin/futures-curve/headless.ts +6 -1
  257. package/src/plugins/builtin/futures-curve/index.tsx +1 -2
  258. package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
  259. package/src/plugins/builtin/gpu/board.tsx +51 -12
  260. package/src/plugins/builtin/gpu/client.ts +15 -2
  261. package/src/plugins/builtin/gpu/equities.tsx +5 -2
  262. package/src/plugins/builtin/gpu/headless.ts +14 -2
  263. package/src/plugins/builtin/gpu/index.tsx +1 -2
  264. package/src/plugins/builtin/gpu/model.ts +20 -2
  265. package/src/plugins/builtin/gpu/pane.tsx +1 -1
  266. package/src/plugins/builtin/help/index.tsx +1 -1
  267. package/src/plugins/builtin/holders/index-holders.ts +24 -12
  268. package/src/plugins/builtin/holders/index.tsx +2 -3
  269. package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
  270. package/src/plugins/builtin/iv-history/index.tsx +1 -2
  271. package/src/plugins/builtin/jodi/index.tsx +1 -2
  272. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
  273. package/src/plugins/builtin/layout-manager/index.tsx +9 -8
  274. package/src/plugins/builtin/macro-day/pane.tsx +4 -2
  275. package/src/plugins/builtin/market-halts/index.tsx +1 -2
  276. package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
  277. package/src/plugins/builtin/market-movers/index.tsx +7 -1
  278. package/src/plugins/builtin/market-movers/session-body.tsx +4 -1
  279. package/src/plugins/builtin/market-valuation/index.tsx +1 -2
  280. package/src/plugins/builtin/members/index.tsx +1 -2
  281. package/src/plugins/builtin/money-markets/index.tsx +1 -2
  282. package/src/plugins/builtin/money-markets/pane.tsx +2 -1
  283. package/src/plugins/builtin/news/headless.ts +7 -0
  284. package/src/plugins/builtin/news/wire/index.tsx +1 -1
  285. package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
  286. package/src/plugins/builtin/notes/index.tsx +3 -6
  287. package/src/plugins/builtin/openfda/index.tsx +1 -2
  288. package/src/plugins/builtin/options/view.tsx +6 -5
  289. package/src/plugins/builtin/options-calculator/index.tsx +1 -2
  290. package/src/plugins/builtin/options-positioning/index.tsx +1 -2
  291. package/src/plugins/builtin/options-scenario/index.tsx +2 -4
  292. package/src/plugins/builtin/perps/index.tsx +1 -2
  293. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
  294. package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
  295. package/src/plugins/builtin/plugin-module.ts +16 -2
  296. package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
  297. package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
  298. package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
  299. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
  300. package/src/plugins/builtin/power/index.tsx +1 -2
  301. package/src/plugins/builtin/rate-path/index.tsx +1 -2
  302. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  303. package/src/plugins/builtin/realized-vol/index.tsx +1 -2
  304. package/src/plugins/builtin/realized-vol/model.ts +16 -2
  305. package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
  306. package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
  307. package/src/plugins/builtin/research/analyst-headless.ts +33 -6
  308. package/src/plugins/builtin/research/analyst-model.ts +37 -2
  309. package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
  310. package/src/plugins/builtin/research/index.tsx +5 -8
  311. package/src/plugins/builtin/research-search/index.tsx +1 -2
  312. package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
  313. package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
  314. package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
  315. package/src/plugins/builtin/roadmap/index.tsx +2 -3
  316. package/src/plugins/builtin/roadmap/pane.tsx +49 -2
  317. package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
  318. package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
  319. package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
  320. package/src/plugins/builtin/sec/filing-content.ts +38 -6
  321. package/src/plugins/builtin/sec/headless.ts +5 -1
  322. package/src/plugins/builtin/sec/index.tsx +4 -2
  323. package/src/plugins/builtin/sec/model.ts +2 -0
  324. package/src/plugins/builtin/sectors/index.tsx +11 -5
  325. package/src/plugins/builtin/series-inventory.ts +2 -0
  326. package/src/plugins/builtin/shared/cloud-resource.ts +60 -0
  327. package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
  328. package/src/plugins/builtin/shared/forming-bars.ts +29 -0
  329. package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
  330. package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
  331. package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
  332. package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
  333. package/src/plugins/builtin/short-volume/pane.tsx +2 -1
  334. package/src/plugins/builtin/social-mentions/index.tsx +1 -2
  335. package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
  336. package/src/plugins/builtin/supply-chain/client.ts +22 -1
  337. package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
  338. package/src/plugins/builtin/supply-chain/index.tsx +1 -2
  339. package/src/plugins/builtin/thirteenf/api.ts +116 -13
  340. package/src/plugins/builtin/thirteenf/data.ts +132 -30
  341. package/src/plugins/builtin/thirteenf/index.tsx +2 -4
  342. package/src/plugins/builtin/thirteenf/model.ts +2 -0
  343. package/src/plugins/builtin/thirteenf/pane.tsx +20 -11
  344. package/src/plugins/builtin/thirteenf/types.ts +12 -0
  345. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +17 -4
  346. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
  347. package/src/plugins/builtin/ticker-detail/headless.ts +11 -3
  348. package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
  349. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
  350. package/src/plugins/builtin/time-sales/index.tsx +1 -2
  351. package/src/plugins/builtin/time-sales/pane.tsx +3 -1
  352. package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
  353. package/src/plugins/builtin/trace-bonds/model.ts +44 -10
  354. package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
  355. package/src/plugins/builtin/treasury-auctions/pane.tsx +2 -2
  356. package/src/plugins/builtin/vol-surface/client.ts +18 -3
  357. package/src/plugins/builtin/vol-surface/index.tsx +1 -2
  358. package/src/plugins/builtin/volatility/index.tsx +1 -2
  359. package/src/plugins/builtin/world-indices/footer.ts +6 -1
  360. package/src/plugins/builtin/world-venue-map/client.ts +10 -1
  361. package/src/plugins/builtin/world-venue-map/index.ts +6 -2
  362. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
  363. package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
  364. package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
  365. package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
  366. package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
  367. package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
  368. package/src/plugins/builtin/yield-curve/curves.ts +16 -2
  369. package/src/plugins/builtin/yield-curve/headless.ts +3 -3
  370. package/src/plugins/bundle.ts +52 -2
  371. package/src/plugins/catalog-browser.ts +2 -2
  372. package/src/plugins/catalog-cli.ts +20 -0
  373. package/src/plugins/host-link.ts +13 -1
  374. package/src/plugins/installer.ts +15 -4
  375. package/src/plugins/loader.ts +43 -11
  376. package/src/plugins/peer-imports.ts +201 -0
  377. package/src/plugins/peer-retry.ts +26 -29
  378. package/src/plugins/plugin-process.ts +128 -0
  379. package/src/plugins/redirection-guard.ts +45 -0
  380. package/src/remote/endpoint.ts +16 -3
  381. package/src/renderers/browser/main.tsx +3 -2
  382. package/src/renderers/cloudflare/http-proxy.ts +7 -123
  383. package/src/renderers/dom/desktop/icons.tsx +2 -0
  384. package/src/renderers/dom/host/image-surface.tsx +46 -21
  385. package/src/renderers/dom/styles.css +7 -0
  386. package/src/renderers/dom/toast-host.tsx +2 -0
  387. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  388. package/src/renderers/electrobun/bun/desktop/initialization.ts +3 -2
  389. package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
  390. package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
  391. package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
  392. package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
  393. package/src/renderers/electrobun/bun/window/create.ts +4 -2
  394. package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
  395. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
  396. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
  397. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
  398. package/src/renderers/electrobun/view/main.tsx +7 -2
  399. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
  400. package/src/renderers/opentui/browser-open.ts +10 -4
  401. package/src/renderers/opentui/image/surface.tsx +1 -1
  402. package/src/renderers/opentui/start.tsx +12 -6
  403. package/src/renderers/opentui/toast-host.tsx +2 -1
  404. package/src/series/registry.ts +18 -0
  405. package/src/series/standalone.ts +10 -1
  406. package/src/series/types.ts +7 -0
  407. package/src/series/validate.ts +11 -2
  408. package/src/sources/gloomberb-cloud/index.ts +28 -5
  409. package/src/sources/gloomberb-cloud/news.ts +24 -3
  410. package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
  411. package/src/sources/provider-errors.ts +26 -3
  412. package/src/sources/provider-router/history.ts +5 -0
  413. package/src/sources/provider-router/index.ts +13 -0
  414. package/src/sync/core-contributors.ts +32 -0
  415. package/src/telemetry/attention-counts.ts +64 -6
  416. package/src/telemetry/attention-setting.ts +33 -0
  417. package/src/telemetry/crash-reports-node.ts +3 -1
  418. package/src/telemetry/stall-clocks.ts +93 -0
  419. package/src/telemetry/stall-reports-node.ts +12 -7
  420. package/src/telemetry/stall-reports.ts +34 -5
  421. package/src/test-support/preload.ts +9 -0
  422. package/src/tickers/company-name.ts +19 -0
  423. package/src/tickers/listing-fate.ts +14 -3
  424. package/src/tickers/saved-instrument.ts +17 -0
  425. package/src/tickers/search/home-listing.ts +13 -2
  426. package/src/tickers/search/index.ts +4 -1
  427. package/src/tickers/search/types.ts +6 -1
  428. package/src/time-series/bar-aggregation.ts +7 -1
  429. package/src/time-series/base-series.ts +3 -3
  430. package/src/time-series/chart-bounds.ts +1 -1
  431. package/src/time-series/chart-data.ts +5 -2
  432. package/src/time-series/chart-seed.ts +1 -1
  433. package/src/time-series/coin-session-close.ts +211 -0
  434. package/src/time-series/live-bars.ts +13 -2
  435. package/src/time-series/live-quotes.ts +1 -1
  436. package/src/time-series/official-close.ts +159 -0
  437. package/src/time-series/price-comparison.ts +55 -6
  438. package/src/time-series/resolve.ts +12 -3
  439. package/src/time-series/series-presentation.ts +1 -1
  440. package/src/time-series/studies/formula.ts +205 -0
  441. package/src/time-series/studies/indicators.ts +299 -0
  442. package/src/time-series/studies/leverage.ts +75 -0
  443. package/src/time-series/studies/pair.ts +138 -0
  444. package/src/time-series/studies/resolve.ts +333 -0
  445. package/src/time-series/studies/shared.ts +141 -0
  446. package/src/time-series/studies/units.ts +243 -0
  447. package/src/time-series/studies/vwap.ts +274 -0
  448. package/src/types/config.ts +18 -2
  449. package/src/types/data-provider.ts +23 -0
  450. package/src/types/financials.ts +22 -0
  451. package/src/types/headless.ts +42 -3
  452. package/src/types/plugin.ts +32 -3
  453. package/src/ui/host.tsx +11 -0
  454. package/src/ui/toast.tsx +26 -1
  455. package/src/utils/ascii-text.ts +140 -0
  456. package/src/utils/cli-output.ts +91 -18
  457. package/src/utils/concurrency.ts +24 -0
  458. package/src/utils/exchanges.ts +6 -0
  459. package/src/utils/external-url.ts +20 -7
  460. package/src/utils/futures-generic.ts +4 -4
  461. package/src/utils/http-proxy-upstream.ts +124 -0
  462. package/src/utils/main-thread-stall.ts +75 -3
  463. package/src/utils/perf-marks.ts +36 -2
  464. package/src/utils/private-files.ts +141 -0
  465. package/src/utils/public-address.ts +99 -0
  466. package/src/account/upgrade-personalization.ts +0 -57
  467. package/src/account/upgrade-tickers.ts +0 -124
  468. package/src/api-client/upgrade-experiment.ts +0 -91
  469. package/src/plugins/builtin/cloud/browser.tsx +0 -3
  470. package/src/renderers/electrobun/launcher/preload.ts +0 -13
  471. package/src/renderers/electrobun/launcher/windows-emulation.ts +0 -50
  472. package/src/time-series/studies.ts +0 -1628
@@ -10,7 +10,13 @@ import type { PluginRegistry } from "../../plugins/registry";
10
10
  import { resolvePlanAccess } from "../../api-client/plan-access";
11
11
  import type { PortfolioSub } from "./onboarding-steps";
12
12
  import type { useOnboardingPositions } from "./wizard-positions";
13
- import { applyFirstRunLayout, buildFirstRunLayout, planFirstRunWatchlist } from "./first-run-workspace";
13
+ import {
14
+ applyFirstRunLayout,
15
+ buildFirstRunLayout,
16
+ onboardingDeskLayoutId,
17
+ planFirstRunWatchlist,
18
+ setupDeskPicks,
19
+ } from "./first-run-workspace";
14
20
  import { buildDesk, getDesk, isDeskKey, isDeskStock, pickDeskCompany, type DeskKey } from "../../layout/desks";
15
21
  import { debugLog } from "../../utils/debug-log";
16
22
  import {
@@ -92,15 +98,18 @@ export function useOnboardingWorkspace({
92
98
  selectedBrokerId: string | null;
93
99
  setPortfolioSub: Dispatch<SetStateAction<PortfolioSub>>;
94
100
  }) {
95
- const [chosenDesks, setChosenDesks] = useState<DeskKey[]>([]);
101
+ // Run again, it starts with the desks it made ticked.
102
+ const [chosenDesks, setChosenDesks] = useState<DeskKey[]>(() => setupDeskPicks(stateRef.current.config));
96
103
  const [deskCursor, setDeskCursor] = useState(0);
97
104
  const [buildingDesks, setBuildingDesks] = useState(false);
98
105
  const buildingDesksRef = useRef(false);
99
106
 
100
107
  /**
101
108
  * Seeds the watchlist and swaps Home for the first-run workspace built
102
- * around `symbol`, with the picked desks as tabs right after it. The config
103
- * comes back for the caller to persist with its own progress patch.
109
+ * around `symbol`, with the picked desks as tabs right after it. Run again,
110
+ * it replaces the layouts it made before instead of adding copies, and
111
+ * from Settings it leaves an existing watchlist alone. The config comes
112
+ * back for the caller to persist with its own progress patch.
104
113
  */
105
114
  const buildFirstRunWorkspace = useCallback(async (
106
115
  baseConfig: AppConfig,
@@ -109,12 +118,17 @@ export function useOnboardingWorkspace({
109
118
  desks: readonly DeskKey[],
110
119
  ): Promise<AppConfig> => {
111
120
  let config = baseConfig;
121
+ const rerun = getOnboardingProgress(baseConfig).rerun === true;
112
122
  let watchlistId = config.watchlists[0]?.id;
123
+ // A run from Settings leaves a watchlist the user has as it is.
124
+ const seedWatchlist = !rerun || !watchlistId;
113
125
  if (!watchlistId) {
114
126
  watchlistId = "watchlist";
115
127
  config = { ...config, watchlists: [{ id: watchlistId, name: "Watchlist" }] };
116
128
  }
117
- const plan = planFirstRunWatchlist(stateRef.current.tickers, watchlistId, portfolioId);
129
+ const plan = seedWatchlist
130
+ ? planFirstRunWatchlist(stateRef.current.tickers, watchlistId, portfolioId)
131
+ : { create: [], update: [] };
118
132
  for (const metadata of plan.create) {
119
133
  try {
120
134
  const ticker = await pluginRegistry.tickerRepository.createTicker(metadata);
@@ -138,7 +152,7 @@ export function useOnboardingWorkspace({
138
152
  watchlistId,
139
153
  hasPane: (paneId) => pluginRegistry.panes?.has(paneId) ?? false,
140
154
  });
141
- const workspace = applyFirstRunLayout(config, home);
155
+ const workspace = applyFirstRunLayout(config, home, [], { rerun });
142
156
  if (desks.length === 0) return workspace;
143
157
  const { tickers, financials } = stateRef.current;
144
158
  const held = [...tickers.values()]
@@ -150,13 +164,15 @@ export function useOnboardingWorkspace({
150
164
  const pro = resolvePlanAccess(apiClient.getCurrentUser()).hasProAccess;
151
165
  // A desk that fails to build is left out; it never holds up the first run.
152
166
  const deskTabs = await Promise.all(desks.map((key) => (
153
- buildDesk(getDesk(key), { catalog: pluginRegistry, config: workspace, company, pro }).catch((error) => {
154
- onboardingLog.error("Desk build failed", { desk: key, error: String(error) });
155
- return null;
156
- })
167
+ buildDesk(getDesk(key), { catalog: pluginRegistry, config: workspace, company, pro }).then(
168
+ (tab) => (tab ? { ...tab, id: onboardingDeskLayoutId(key) } : null),
169
+ (error) => {
170
+ onboardingLog.error("Desk build failed", { desk: key, error: String(error) });
171
+ return null;
172
+ },
173
+ )
157
174
  )));
158
- const tabs = deskTabs.filter((tab) => tab !== null);
159
- return { ...workspace, layouts: [workspace.layouts[0]!, ...tabs, ...workspace.layouts.slice(1)] };
175
+ return applyFirstRunLayout(config, home, deskTabs.filter((tab) => tab !== null), { rerun });
160
176
  }, [dispatch, pluginRegistry, stateRef]);
161
177
 
162
178
  const commitWorkspaceProgress = useCallback(async (
@@ -187,7 +203,7 @@ export function useOnboardingWorkspace({
187
203
  }, [positions, saveProgressInBackground]);
188
204
 
189
205
  /**
190
- * Leaves "What do you trade?": builds the workspace with the picked desks as
206
+ * Leaves "What do you trade most?": builds the workspace with the picked desks as
191
207
  * tabs (none when skipped, which is today's workspace) and moves on.
192
208
  */
193
209
  const finishDesks = useCallback((desks: readonly DeskKey[]) => {
@@ -270,7 +286,8 @@ export function useOnboardingCompletion({
270
286
  setPersistenceError(null);
271
287
  try {
272
288
  await progressSaveQueueRef.current.catch(() => {});
273
- const desks = getOnboardingProgress(stateRef.current.config).desks?.filter(isDeskKey);
289
+ const { desks: picked, rerun } = getOnboardingProgress(stateRef.current.config);
290
+ const desks = picked?.filter(isDeskKey);
274
291
  const nextConfig: AppConfig = {
275
292
  ...stateRef.current.config,
276
293
  onboardingComplete: true,
@@ -282,7 +299,8 @@ export function useOnboardingCompletion({
282
299
  complete: true,
283
300
  progress: undefined,
284
301
  });
285
- recordResearchActivity(skipped ? "onboarding_skipped" : "onboarding_completed", undefined, undefined, { desks });
302
+ // The funnel counts first runs; a run from Settings is not one.
303
+ if (!rerun) recordResearchActivity(skipped ? "onboarding_skipped" : "onboarding_completed", undefined, undefined, { desks });
286
304
  await Promise.resolve(onComplete(nextConfig));
287
305
  } catch (error) {
288
306
  setPersistenceError(error instanceof Error ? error.message : String(error));
@@ -6,14 +6,12 @@ import { chatController } from "../../plugins/builtin/chat/controller";
6
6
  import type { CloudBillingInterval } from "../../account/billing";
7
7
  import { loadUpgradeOffer, type UpgradeOffer } from "../../account/upgrade-dialog";
8
8
  import { useCloudUpgradeAction } from "../../plugins/builtin/shared/cloud-upgrade";
9
- import { useUpgradePersonalization } from "../../account/upgrade-personalization";
10
9
  import { usePlanAccess } from "../../api-client/plan-access";
11
10
 
12
11
  export function useOnboardingUpgrade({
13
- stage, progress, saveProgressInBackground, persistProgress, shown,
12
+ stage, progress, saveProgressInBackground, persistProgress,
14
13
  }: {
15
14
  stage: OnboardingStage;
16
- shown: boolean;
17
15
  progress: OnboardingProgress;
18
16
  saveProgressInBackground: (patch: Partial<OnboardingProgress> & Pick<OnboardingProgress, "stage">, baseConfig?: AppConfig | undefined) => void;
19
17
  persistProgress: (patch: Partial<OnboardingProgress> & Pick<OnboardingProgress, "stage">, baseConfig?: AppConfig | undefined) => Promise<AppConfig>;
@@ -25,8 +23,6 @@ export function useOnboardingUpgrade({
25
23
  const appActive = useAppActive();
26
24
  const planAccess = usePlanAccess();
27
25
  const openUpgrade = useCloudUpgradeAction("onboarding-pro");
28
- // The step lists the features from its first frame, so that is when it is shown.
29
- const personalTickers = useUpgradePersonalization("onboarding-pro", shown && stage === "upgrade");
30
26
  // The price and whether this account still has its trial, asked again for
31
27
  // whichever account reaches the step.
32
28
  const accountId = planAccess.signedIn ? apiClient.getCurrentUser()?.id ?? null : null;
@@ -82,6 +78,6 @@ export function useOnboardingUpgrade({
82
78
  }, [progress.accountStatus, saveProgressInBackground]);
83
79
 
84
80
  return {
85
- offer, pricing, billingInterval, setBillingInterval, planAccess, primaryUpgradeAction, continueFree, personalTickers,
81
+ offer, pricing, billingInterval, setBillingInterval, planAccess, primaryUpgradeAction, continueFree,
86
82
  };
87
83
  }
@@ -19,9 +19,63 @@ const LEGACY_STAGES: Partial<Record<OnboardingStage, OnboardingStage>> = {
19
19
  };
20
20
 
21
21
  export function getOnboardingProgress(config: AppConfig): OnboardingProgress {
22
- const progress = config.onboardingProgress ?? { version: 1, stage: "portfolio" };
23
- const stage = LEGACY_STAGES[progress.stage];
24
- return stage ? { ...progress, stage } : progress;
22
+ const saved = config.onboardingProgress ?? { version: 1, stage: "portfolio" };
23
+ const legacy = LEGACY_STAGES[saved.stage];
24
+ const progress = legacy ? { ...saved, stage: legacy } : saved;
25
+ // Older builds sent Back from Gloom Cloud to the portfolio after the
26
+ // workspace was built; such a profile resumes on the workspace.
27
+ return isOnboardingStageFinished(progress, progress.stage) ? { ...progress, stage: "research" } : progress;
28
+ }
29
+
30
+ /** Stages the wizard reaches only once the workspace is built. */
31
+ const WORKSPACE_STAGES: ReadonlySet<OnboardingStage> = new Set(["research", "account", "upgrade", "ready"]);
32
+
33
+ /**
34
+ * Whether the workspace is built around a holding: the portfolio and desk
35
+ * steps are finished. Older builds saved no desks, so a later stage with a
36
+ * holding counts too.
37
+ */
38
+ export function isOnboardingWorkspaceBuilt(progress: OnboardingProgress): boolean {
39
+ return !!progress.tickerSymbol && (Array.isArray(progress.desks) || WORKSPACE_STAGES.has(progress.stage));
40
+ }
41
+
42
+ /**
43
+ * A step that is done for good: the portfolio and desk steps once the
44
+ * workspace is built (building twice would add every desk twice), and Pro
45
+ * for an account that has it. The wizard shows them as done and never asks
46
+ * them again.
47
+ */
48
+ function isOnboardingStageFinished(progress: OnboardingProgress, stage: OnboardingStage, pro = false): boolean {
49
+ if (stage === "upgrade") return pro;
50
+ return (stage === "portfolio" || stage === "desks") && isOnboardingWorkspaceBuilt(progress);
51
+ }
52
+
53
+ /**
54
+ * Where Back (and Esc) goes: the step the user came from, passing over any
55
+ * that is finished for good (`pro`: the account has Pro). With no way back
56
+ * recorded (the app restarted on this step), the step before it in the
57
+ * flow. `visited` is the stages the user came through, oldest first; what
58
+ * comes back is what is left of it. Null when the step has no way back.
59
+ */
60
+ export function previousOnboardingStage(
61
+ progress: OnboardingProgress,
62
+ visited: readonly OnboardingStage[],
63
+ { pro = false }: { pro?: boolean } = {},
64
+ ): { stage: OnboardingStage; visited: OnboardingStage[] } | null {
65
+ for (let index = visited.length - 1; index >= 0; index -= 1) {
66
+ const stage = visited[index]!;
67
+ if (stage !== progress.stage && !isOnboardingStageFinished(progress, stage, pro)) {
68
+ return { stage, visited: visited.slice(0, index) };
69
+ }
70
+ }
71
+ const stage: OnboardingStage | null = progress.stage === "account"
72
+ ? isOnboardingWorkspaceBuilt(progress) ? "research" : "portfolio"
73
+ : progress.stage === "upgrade"
74
+ ? "account"
75
+ : progress.stage === "ready"
76
+ ? progress.accountStatus === "signed-in" && !pro ? "upgrade" : "account"
77
+ : null;
78
+ return stage ? { stage, visited: [] } : null;
25
79
  }
26
80
 
27
81
  export function withOnboardingProgress(
@@ -302,7 +302,7 @@ export function useOnboardingPositions({
302
302
  const costBasis = avgCost ?? result.quote?.price;
303
303
  if (costBasis === undefined || costBasis === null || !Number.isFinite(costBasis)) {
304
304
  setFieldIdx(2);
305
- setError(t("No live price yet. Enter the average cost."));
305
+ setError(t("No price yet. Enter the average cost."));
306
306
  return false;
307
307
  }
308
308
  // A future is entered in contracts, sized by its terms; a cost typed as corn is quoted counts cents.
@@ -0,0 +1,56 @@
1
+ import { Box, Text, useUiCapabilities } from "../../ui";
2
+ import { useThemeColors } from "../../theme/theme-context";
3
+ import { tf } from "../../i18n";
4
+ import { displayWidth } from "../../utils/format";
5
+ import { ICON_GLYPHS, Icon } from "./icon";
6
+
7
+ /** The terminal's form of the mark: the clock and the delay, as the pane footer draws it (`◷15m`). */
8
+ function delayedMarkText(minutes: number): string {
9
+ return `${ICON_GLYPHS.clock}${minutes}m`;
10
+ }
11
+
12
+ /** The delay the mark spells out after its clock: `15m`. */
13
+ function delayedMarkDelay(minutes: number): string {
14
+ return `${minutes}m`;
15
+ }
16
+
17
+ /**
18
+ * Cells the mark takes: `◷15m` in the terminal; on the desktop and the web the
19
+ * clock icon and its gap (12px and 4px, three cells at the narrowest cell) and `15m`.
20
+ */
21
+ export function delayedMarkWidth(minutes: number, nativePaneChrome: boolean): number {
22
+ return nativePaneChrome ? 3 + displayWidth(delayedMarkDelay(minutes)) : displayWidth(delayedMarkText(minutes));
23
+ }
24
+
25
+ /**
26
+ * The one mark for a price shown late, beside the price: what the pane
27
+ * footer's clock is for a pane's data (`delayedFooterSegment`), for a price
28
+ * outside a footer (the header's SPY, the status bar, a quote's headline).
29
+ * It reads without hovering: a yellow clock and the delay (`15m`) in the dim
30
+ * tone of the meta text around it on the desktop and the web, `◷15m` in the
31
+ * terminal, with `15m delayed` in its tooltip. Callers decide when a price is
32
+ * late with `freeQuoteDelayMinutes` (or `useFreeQuoteDelay`); it draws nothing else.
33
+ */
34
+ export function DelayedMark({ minutes }: { minutes: number }) {
35
+ const colors = useThemeColors();
36
+ const { nativePaneChrome } = useUiCapabilities();
37
+ const detail = tf("{delay} delayed", { delay: `${minutes}m` });
38
+ if (!nativePaneChrome) {
39
+ return <Text fg={colors.warning} flexShrink={0} selectable={false} aria-label={detail}>{delayedMarkText(minutes)}</Text>;
40
+ }
41
+ return (
42
+ <Box
43
+ role="img"
44
+ aria-label={detail}
45
+ title={detail}
46
+ flexShrink={0}
47
+ flexDirection="row"
48
+ alignItems="center"
49
+ data-gloom-role="delayed-mark"
50
+ style={{ gap: "var(--gloom-space-xs)" }}
51
+ >
52
+ <Icon name="clock" color={colors.warning} />
53
+ <Text fg={colors.textDim} selectable={false}>{delayedMarkDelay(minutes)}</Text>
54
+ </Box>
55
+ );
56
+ }
@@ -3,7 +3,7 @@ import { Box, Text } from "../../ui";
3
3
  import { TextAttributes } from "../../ui";
4
4
  import { tf } from "../../i18n";
5
5
  import { useThemeColors } from "../../theme/theme-context";
6
- import { safeExternalUrl } from "../../utils/external-url";
6
+ import { safeExternalUrl, systemOpenCommand } from "../../utils/external-url";
7
7
  import { linkContextMenuItems, useContextMenu, useRendererHost, useUiCapabilities } from "../../ui";
8
8
  import type { ContextMenuItem } from "../../types/context-menu";
9
9
  import { usePaneMenuItems } from "../layout/pane/footer/registration";
@@ -20,11 +20,8 @@ export function openUrl(rawUrl: string) {
20
20
 
21
21
  if (typeof Bun !== "undefined" && typeof Bun.spawn === "function") {
22
22
  const platform = typeof process !== "undefined" ? process.platform : "linux";
23
- const command = platform === "darwin"
24
- ? ["open", url]
25
- : platform === "win32"
26
- ? ["cmd", "/c", "start", "", url]
27
- : ["xdg-open", url];
23
+ const command = systemOpenCommand(url, platform);
24
+ if (!command) return;
28
25
  // Spawning throws when the opener is not installed (no xdg-open on
29
26
  // minimal Linux and WSL); a link that does nothing beats a crash.
30
27
  try {
@@ -138,6 +138,11 @@ export interface IconButtonProps {
138
138
  size?: number;
139
139
  /** Keep the press from also reaching a row or header behind the button. */
140
140
  stopPropagation?: boolean;
141
+ /**
142
+ * A few characters after the icon, in a muted tone, for a status that must
143
+ * read without hovering (a delay's `15m`). The label stays the accessible name.
144
+ */
145
+ text?: string;
141
146
  }
142
147
 
143
148
  /**
@@ -157,6 +162,7 @@ export function IconButton({
157
162
  disabled = false,
158
163
  size = 12,
159
164
  stopPropagation = true,
165
+ text,
160
166
  }: IconButtonProps) {
161
167
  const colors = useThemeColors();
162
168
  const label = t(rawLabel);
@@ -182,6 +188,7 @@ export function IconButton({
182
188
  disabled={disabled}
183
189
  size={size}
184
190
  stopPropagation={stopPropagation}
191
+ text={text}
185
192
  />
186
193
  );
187
194
  }
@@ -201,7 +208,7 @@ export function IconButton({
201
208
  >
202
209
  {/* Pressed takes the colour the desktop and the terminal pane header give an active toggle. */}
203
210
  <Text fg={disabled ? colors.textMuted : color ?? (pressed ? colors.warning : colors.textDim)} selectable={false}>
204
- {` ${ICON_GLYPHS[icon]} `}
211
+ {` ${ICON_GLYPHS[icon]}${text ?? ""} `}
205
212
  </Text>
206
213
  </Box>
207
214
  );
@@ -23,15 +23,16 @@ const LETTER_STYLE: Record<LogoMarkSize, CSSProperties> = {
23
23
  };
24
24
 
25
25
  export interface LogoMarkProps {
26
- /** The logo, an image URL; without one, or when it fails to load, the tile shows `name`'s initial. */
26
+ /** The logo, an image URL; without one, while it loads and when it fails to load, the tile shows `name`'s initial. */
27
27
  src?: string | null;
28
28
  /** What the logo stands for, such as a broker. Only its initial is drawn; the label beside it names it. */
29
29
  name: string;
30
30
  size?: LogoMarkSize;
31
31
  /**
32
- * Draws the logo on a white plate. Company and coin logos are made for a
33
- * light page: a black mark on a clear field (XRP's) would vanish on a dark
34
- * theme, while a logo that fills its square covers the plate.
32
+ * Draws the logo on a white plate once it has loaded, never behind the
33
+ * initial. Company and coin logos are made for a light page: a black mark
34
+ * on a clear field (XRP's) would vanish on a dark theme, while a logo that
35
+ * fills its square covers the plate.
35
36
  */
36
37
  plate?: boolean;
37
38
  }
@@ -59,9 +60,11 @@ export function LogoMark({ src, name, size = "row", plate = false }: LogoMarkPro
59
60
  src={src ?? undefined}
60
61
  alt=""
61
62
  objectFit="cover"
63
+ fallbackWhileLoading
64
+ imageBackground={plate ? LOGO_PLATE : undefined}
62
65
  aria-hidden="true"
63
66
  data-gloom-role="logo-mark"
64
- style={{ width: tile.side, height: tile.side, flexShrink: 0, borderRadius: tile.radius, ...(plate ? { background: LOGO_PLATE } : {}) }}
67
+ style={{ width: tile.side, height: tile.side, flexShrink: 0, borderRadius: tile.radius }}
65
68
  >
66
69
  <Box width="100%" height="100%" alignItems="center" justifyContent="center" backgroundColor={colors.border}>
67
70
  <Text fg={colors.textBright} attributes={TextAttributes.BOLD} style={LETTER_STYLE[size]}>