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
@@ -257,7 +257,7 @@ export function ChartTableHeader(props: ChartTableHeaderProps) {
257
257
  return (
258
258
  <Box flexDirection="column" flexShrink={0}>
259
259
  {query}
260
- {layout.figureRows ? <StatGrid items={layout.figures} width={width} /> : null}
260
+ {layout.figureRows ? <StatGrid items={layout.figures} width={width} maxLines={layout.figureMaxLines} /> : null}
261
261
  {band}
262
262
  </Box>
263
263
  );
@@ -31,6 +31,8 @@ const CHART_SHARE = 0.4;
31
31
  const SHORT_BODY_ROWS = 12;
32
32
  /** The figures never take more than this share of the body. */
33
33
  const FIGURE_SHARE = 0.25;
34
+ /** The share of the body figures that wrap rather than cut a date may take, beyond FIGURE_SHARE. */
35
+ const WRAPPED_FIGURE_SHARE = 1 / 3;
34
36
 
35
37
  /**
36
38
  * The rows a kit table spends outside its body: the header, plus a horizontal
@@ -70,15 +72,28 @@ export interface ChartTableLayoutInput {
70
72
  export interface ChartTableLayout {
71
73
  figures: StatItem[];
72
74
  figureRows: number;
75
+ /** The lines the figures may take, so a whole-row cell too narrow for one line wraps within them (StatGrid's `maxLines`). */
76
+ figureMaxLines: number;
73
77
  mode: ChartBandMode;
74
78
  chartRows: number;
75
79
  }
76
80
 
77
- /** Drops figures from the end until they fit the row budget. */
78
- function fitFigures(figures: readonly StatItem[], width: number, maxRows: number): StatItem[] {
81
+ /** Rows `figures` take within `maxLines` (StatGrid's own count). */
82
+ function figureRows(figures: readonly StatItem[], width: number, maxLines: number): number {
83
+ return statGridRows([...figures], width, undefined, { maxLines });
84
+ }
85
+
86
+ /**
87
+ * The figures that fit the row budget a line each, dropped from the end, and
88
+ * the lines they may take: a whole-row figure too narrow for one line wraps
89
+ * onto a second rather than cut a date, into rows up to WRAPPED_FIGURE_SHARE
90
+ * of the body, the leading ones first; the rest keep one line, cut. Wrapping
91
+ * never costs a figure that fits a line. A budget of one row wraps none.
92
+ */
93
+ function fitFigures(figures: readonly StatItem[], width: number, maxRows: number, body: number): { figures: StatItem[]; maxLines: number } {
79
94
  const kept = [...figures];
80
- while (kept.length > 0 && statGridRows(kept, width) > maxRows) kept.pop();
81
- return kept;
95
+ while (kept.length > 0 && figureRows(kept, width, maxRows) > maxRows) kept.pop();
96
+ return { figures: kept, maxLines: maxRows > 1 ? Math.max(maxRows, Math.floor(body * WRAPPED_FIGURE_SHARE)) : maxRows };
82
97
  }
83
98
 
84
99
  /**
@@ -89,7 +104,9 @@ function fitFigures(figures: readonly StatItem[], width: number, maxRows: number
89
104
  * fewer), so the rows the pane is about never vanish.
90
105
  * - The figures take at most a quarter of the body, one row when it is short
91
106
  * or when a second row would cost the chart its place, and lose items from
92
- * the end rather than wrap.
107
+ * the end rather than spill; a whole-row figure wraps onto a second line
108
+ * rather than cut its detail, in rows up to a third of the body, never
109
+ * at the cost of a figure that fits a line.
93
110
  * - The chart takes 40% of what is left, or everything the table does not
94
111
  * need when all its rows fit, so no blank band opens under a short table.
95
112
  * - Below a readable chart the band becomes a one-row strip, then nothing.
@@ -125,11 +142,11 @@ function layoutWithFigureRows(input: ChartTableLayoutInput, body: number, figure
125
142
 
126
143
  // A table with no room for a single row after the figures gets the body back.
127
144
  const figureBudget = Math.min(figureCap, Math.max(0, body - chrome - Math.min(tableRows, 1)));
128
- const figures = figureBudget > 0 ? fitFigures(input.figures ?? [], width, figureBudget) : [];
129
- const figureRows = figures.length ? statGridRows(figures, width) : 0;
145
+ const { figures, maxLines: figureMaxLines } = figureBudget > 0 ? fitFigures(input.figures ?? [], width, figureBudget, body) : { figures: [], maxLines: 0 };
146
+ const shownRows = figures.length ? figureRows(figures, width, figureMaxLines) : 0;
130
147
 
131
- const rest = body - figureRows;
132
- const none: ChartTableLayout = { figures, figureRows, mode: "none", chartRows: 0 };
148
+ const rest = body - shownRows;
149
+ const none: ChartTableLayout = { figures, figureRows: shownRows, figureMaxLines, mode: "none", chartRows: 0 };
133
150
  if (!input.chart || width < CHART_MIN_WIDTH) return none;
134
151
 
135
152
  const minRows = input.chart.minRows ?? CHART_MIN_ROWS;
@@ -137,7 +154,7 @@ function layoutWithFigureRows(input: ChartTableLayoutInput, body: number, figure
137
154
  const maxRows = input.chart.maxRows ?? Number.POSITIVE_INFINITY;
138
155
  const room = rest - tableMin;
139
156
  const spare = rest - tableFit;
140
- const full = (rows: number): ChartTableLayout => ({ figures, figureRows, mode: "full", chartRows: Math.min(rows, maxRows) });
157
+ const full = (rows: number): ChartTableLayout => ({ figures, figureRows: shownRows, figureMaxLines, mode: "full", chartRows: Math.min(rows, maxRows) });
141
158
  if (room >= minRows) {
142
159
  // With every table row in view beside a readable chart, the chart takes
143
160
  // exactly the rest; otherwise its 40% share, never below what it draws in.
@@ -148,7 +165,7 @@ function layoutWithFigureRows(input: ChartTableLayoutInput, body: number, figure
148
165
  // instead of a strip over a blank band.
149
166
  if (spare >= compactRows) return full(spare);
150
167
  if (input.chart.strip !== false && room >= 1) {
151
- return { figures, figureRows, mode: "strip", chartRows: 1 };
168
+ return { figures, figureRows: shownRows, figureMaxLines, mode: "strip", chartRows: 1 };
152
169
  }
153
170
  return none;
154
171
  }
@@ -32,6 +32,8 @@ interface UseCommandBarActionRuntimeOptions {
32
32
  dispatch: Dispatch<AppAction>;
33
33
  focusedPaneId: string | null;
34
34
  onCheckForUpdates?: () => void | Promise<void>;
35
+ /** Settings' Run Setup Again; absent where setup cannot run. */
36
+ onRunSetupAgain?: () => Promise<void>;
35
37
  persistConfig: (nextConfig: AppState["config"]) => void;
36
38
  pluginRegistry: PluginRegistry;
37
39
  pushRoute: (route: CommandBarRoute) => void;
@@ -57,6 +59,7 @@ export function useCommandBarActionRuntime({
57
59
  dispatch,
58
60
  focusedPaneId,
59
61
  onCheckForUpdates,
62
+ onRunSetupAgain,
60
63
  persistConfig,
61
64
  pluginRegistry,
62
65
  pushRoute,
@@ -222,6 +225,7 @@ export function useCommandBarActionRuntime({
222
225
  focusTicker,
223
226
  notify,
224
227
  onCheckForUpdates,
228
+ onRunSetupAgain,
225
229
  openBuiltInWorkflow,
226
230
  openInlineConfirm,
227
231
  openModeRoute,
@@ -29,6 +29,7 @@ import { parseWindowModeCommandArg } from "../../layout-items";
29
29
  import type { CommandBarRoute } from "../../workflow/types";
30
30
  import { recordFunctionOpen } from "../../../../telemetry/usage-counts";
31
31
  import { reportTelemetryConfig } from "../../../../telemetry/live-config";
32
+ import { ATTENTION_OFF_MESSAGE } from "../../../../telemetry/attention-setting";
32
33
  import { NIGHTLY_CHANNEL_SUMMARY, withUpdateChannel } from "../../../../updater/channel";
33
34
  import { isPrereleaseVersion } from "../../../../utils/semver";
34
35
  import { VERSION } from "../../../../version";
@@ -53,6 +54,8 @@ export function runDirectCommandAction(options: {
53
54
  getState: () => AppState;
54
55
  notify: NotifyFn;
55
56
  onCheckForUpdates?: () => void | Promise<void>;
57
+ /** Settings' Run Setup Again; absent where setup cannot run. */
58
+ onRunSetupAgain?: () => Promise<void>;
56
59
  openBuiltInWorkflow: (actionId: string) => void;
57
60
  openInlineConfirm: OpenInlineConfirm;
58
61
  openModeRoute: (screen: "ticker-search" | "layout", initialQuery?: string) => void;
@@ -78,6 +81,7 @@ export function runDirectCommandAction(options: {
78
81
  getState,
79
82
  notify,
80
83
  onCheckForUpdates,
84
+ onRunSetupAgain,
81
85
  openBuiltInWorkflow,
82
86
  openInlineConfirm,
83
87
  openModeRoute,
@@ -329,47 +333,22 @@ export function runDirectCommandAction(options: {
329
333
  return;
330
334
  }
331
335
  case "toggle-crash-reports":
332
- case "toggle-usage-counts": {
333
- const key = command.id === "toggle-crash-reports" ? "crashReports" : "usage";
336
+ case "toggle-usage-counts":
337
+ case "toggle-attention-counts": {
338
+ const key = command.id === "toggle-crash-reports" ? "crashReports" : command.id === "toggle-usage-counts" ? "usage" : "attention";
339
+ // All three are on by default: only an explicit false is off.
334
340
  const enabled = state.config.telemetry?.[key] === false;
335
341
  const nextConfig = { ...state.config, telemetry: { ...state.config.telemetry, [key]: enabled } };
342
+ // Reported before React commits, so turning Attention Counts off drops unsent counts at once.
336
343
  reportTelemetryConfig(nextConfig.telemetry);
337
344
  dispatch({ type: "SET_CONFIG", config: nextConfig });
338
345
  persistConfig(nextConfig);
339
- notify(`${t(command.label)}: ${t(enabled ? "on" : "off")}`, { type: "success" });
346
+ notify(key === "attention" && !enabled
347
+ ? t(ATTENTION_OFF_MESSAGE)
348
+ : `${t(command.label)}: ${t(enabled ? "on" : "off")}`, { type: "success" });
340
349
  closeAll({ revertThemePreview: false });
341
350
  return;
342
351
  }
343
- case "toggle-attention-counts": {
344
- const setEnabled = (enabled: boolean) => {
345
- const current = getState().config;
346
- const nextConfig = { ...current, telemetry: { ...current.telemetry, attention: enabled } };
347
- // Clear unsent counts before React commits the settings update.
348
- reportTelemetryConfig(nextConfig.telemetry);
349
- dispatch({ type: "SET_CONFIG", config: nextConfig });
350
- persistConfig(nextConfig);
351
- notify(`${t(command.label)}: ${t(enabled ? "on" : "off")}`, { type: "success" });
352
- };
353
- if (state.config.telemetry?.attention === true) {
354
- setEnabled(false);
355
- closeAll({ revertThemePreview: false });
356
- } else {
357
- openInlineConfirm({
358
- confirmId: "enable-attention-counts",
359
- title: "Share Attention Counts?",
360
- body: [
361
- "While signed in with a verified account, share the tickers you open in DES, charts, quotes and options, and add to watchlists.",
362
- "Gloom uses your account to limit contributions. Published counts are delayed and rounded, and require at least 20 contributors. Your holdings, watchlist names and searches are not included.",
363
- "This is separate from Usage Counts and is off by default. Turn it off at any time to stop collection and discard unsent counts. Already published anonymous totals cannot be removed.",
364
- ],
365
- confirmLabel: "Share Counts",
366
- cancelLabel: "Keep Off",
367
- tone: "default",
368
- onConfirm: async () => { setEnabled(true); },
369
- });
370
- }
371
- return;
372
- }
373
352
  case "font-size-increase":
374
353
  case "font-size-decrease": {
375
354
  const delta = command.id === "font-size-increase" ? 1 : -1;
@@ -391,6 +370,12 @@ export function runDirectCommandAction(options: {
391
370
  void onCheckForUpdates?.();
392
371
  closeAll({ revertThemePreview: false });
393
372
  return;
373
+ case "run-setup-again":
374
+ closeAll({ revertThemePreview: false });
375
+ void onRunSetupAgain?.().catch((error: unknown) => {
376
+ notify(error instanceof Error ? error.message : String(error), { type: "error" });
377
+ });
378
+ return;
394
379
  case "release-channel": {
395
380
  // The runtime checks again as soon as the channel changes.
396
381
  const setChannel = (channel: "stable" | "nightly") => {
@@ -39,6 +39,8 @@ interface UseCommandBarDirectCommandRuntimeOptions {
39
39
  focusTicker: (symbol: string, options?: PinTickerOptions) => void;
40
40
  notify: (body: string, options?: { type?: "info" | "success" | "error" }) => void;
41
41
  onCheckForUpdates?: () => void | Promise<void>;
42
+ /** Settings' Run Setup Again; absent where setup cannot run. */
43
+ onRunSetupAgain?: () => Promise<void>;
42
44
  openBuiltInWorkflow: (actionId: string) => void;
43
45
  openInlineConfirm: OpenInlineConfirm;
44
46
  openModeRoute: (
@@ -66,6 +68,7 @@ export function useCommandBarDirectCommandRuntime({
66
68
  focusTicker,
67
69
  notify,
68
70
  onCheckForUpdates,
71
+ onRunSetupAgain,
69
72
  openBuiltInWorkflow,
70
73
  openInlineConfirm,
71
74
  openModeRoute,
@@ -136,6 +139,7 @@ export function useCommandBarDirectCommandRuntime({
136
139
  getState,
137
140
  notify,
138
141
  onCheckForUpdates,
142
+ onRunSetupAgain,
139
143
  openBuiltInWorkflow,
140
144
  openInlineConfirm,
141
145
  openModeRoute,
@@ -159,6 +163,7 @@ export function useCommandBarDirectCommandRuntime({
159
163
  getState,
160
164
  notify,
161
165
  onCheckForUpdates,
166
+ onRunSetupAgain,
162
167
  openBuiltInWorkflow,
163
168
  openInlineConfirm,
164
169
  openModeRoute,
@@ -276,7 +276,7 @@ export const commands: Command[] = [
276
276
  id: "toggle-attention-counts",
277
277
  prefix: "",
278
278
  label: "Attention Counts",
279
- description: "Opt in to sharing ticker research counts for Gloom Trending",
279
+ description: "Turn ticker research counts on or off (on by default)",
280
280
  category: "Config",
281
281
  },
282
282
 
@@ -309,6 +309,15 @@ export const commands: Command[] = [
309
309
  category: "Config",
310
310
  },
311
311
 
312
+ // Setup, from its first step on what the user has (components/onboarding/run-setup-again.ts)
313
+ {
314
+ id: "run-setup-again",
315
+ prefix: "",
316
+ label: "Run Setup Again",
317
+ description: "Rebuild Home and setup's desk tabs; your other layouts stay",
318
+ category: "Config",
319
+ },
320
+
312
321
  // Import/Export
313
322
  {
314
323
  id: "export-config",
@@ -109,6 +109,9 @@ export function createRootCommandItemBuilder({
109
109
  if (state.updateProgress?.phase === "ready") return "Update ready, restart to apply";
110
110
  if (state.updateProgress?.phase === "error") return failedUpdateLabel(state.updateProgress);
111
111
  return command.label;
112
+ // Unlike its neighbours it started out off, so the row says what the default is now.
113
+ case "toggle-attention-counts":
114
+ return `${command.label} (on by default)`;
112
115
  default:
113
116
  return command.label;
114
117
  }
@@ -175,9 +178,9 @@ export function createRootCommandItemBuilder({
175
178
  case "toggle-usage-counts":
176
179
  return state.config.telemetry?.usage === false ? "Currently off" : "Currently on";
177
180
  case "toggle-attention-counts":
178
- return state.config.telemetry?.attention === true
179
- ? "Currently on: share ticker research counts"
180
- : "Currently off: opt in to sharing ticker research counts";
181
+ return state.config.telemetry?.attention === false
182
+ ? "Currently off. Signed in, this turns it off on your other devices too"
183
+ : "Currently on: tickers you open go to Gloom, published only as hourly totals of 20+ people";
181
184
  case "release-channel":
182
185
  if (resolveUpdateChannel(state.config) === "nightly") return `Currently nightly: ${NIGHTLY_CHANNEL_SUMMARY}`;
183
186
  return isPrereleaseVersion(VERSION)
@@ -201,7 +204,7 @@ export function createRootCommandItemBuilder({
201
204
  case "toggle-usage-counts":
202
205
  return state.config.telemetry?.usage === false ? "off" : "on";
203
206
  case "toggle-attention-counts":
204
- return state.config.telemetry?.attention === true ? "on" : "off";
207
+ return state.config.telemetry?.attention === false ? "off" : "on";
205
208
  case "release-channel":
206
209
  return resolveUpdateChannel(state.config);
207
210
  default:
@@ -223,6 +226,8 @@ export function createRootCommandItemBuilder({
223
226
  return "update";
224
227
  case "release-channel":
225
228
  return "nightly stable beta prerelease update updates channel";
229
+ case "run-setup-again":
230
+ return "settings onboarding wizard guide first run start over desks holdings";
226
231
  default:
227
232
  return "";
228
233
  }
@@ -20,6 +20,21 @@ import { buildRootShortcutFeedback } from "./shortcut-feedback";
20
20
  import type { ShortcutIntent } from "./shortcuts";
21
21
  import { clampIndex } from "../../../../utils/math";
22
22
 
23
+ /** Written the way a symbol is: up to five letters and a share class (AAPL, nvda, BRK.B). */
24
+ const SYMBOL_SHAPED_QUERY = /^[A-Za-z]{1,5}(?:[.\-/][A-Za-z]{1,2})?$/;
25
+
26
+ /**
27
+ * Text that asks for a symbol rather than a local row: symbol-shaped, and in
28
+ * capitals or in no row's name. `nvda` matches Exposure only through its
29
+ * example input; `upd` is in Check for Updates.
30
+ */
31
+ function readsAsSymbol(text: string, rows: readonly ResultItem[]): boolean {
32
+ if (!SYMBOL_SHAPED_QUERY.test(text)) return false;
33
+ if (text === text.toUpperCase()) return true;
34
+ const wanted = text.toLowerCase();
35
+ return !rows.some((row) => isDefaultSelectable(row) && row.label.toLowerCase().includes(wanted));
36
+ }
37
+
23
38
  interface UseCommandBarRootRuntimeOptions {
24
39
  activeCollectionId: string | null;
25
40
  activePortfolio?: AppState["config"]["portfolios"][number];
@@ -317,6 +332,13 @@ export function useCommandBarRootRuntime({
317
332
  writeTickerSearchCache,
318
333
  });
319
334
  const rootSearching = tickerSearching || providerSearching;
335
+ // A symbol whose search has not answered: the rows on screen are what the
336
+ // letters match locally (a pane whose example names AAPL), not the symbol,
337
+ // so none of them is highlighted and Enter waits for the answer, as it would
338
+ // get had the symbol been typed slowly. Lower case found in a row's name
339
+ // (`upd`, `list`) is that row and runs at once.
340
+ const rootSymbolAnswerPending = !rootResultsCurrent && rootPlainTickerSearchArg !== null
341
+ && (rootTickerReading !== null || rootListingChoice !== null || readsAsSymbol(rootPlainTickerSearchArg, orderedRootResults));
320
342
 
321
343
  useLayoutEffect(() => {
322
344
  if (currentRoute) return;
@@ -353,7 +375,7 @@ export function useCommandBarRootRuntime({
353
375
  // them is the answer and none is selected: Enter must not take one of
354
376
  // them for the ticker still being searched.
355
377
  const defaultIdx = orderedRootResults.findIndex(isDefaultSelectable);
356
- if (defaultIdx < 0) return -1;
378
+ if (defaultIdx < 0 || rootSymbolAnswerPending) return -1;
357
379
  const selected = clampIndex(Math.max(rootResultModel.initialIdx, defaultIdx), resultIds.length);
358
380
  pinnedRootRowRef.current = rootResultsCurrent && resultIds[selected]
359
381
  ? { id: resultIds[selected]!, answer: activeRootProviderResultsKey }
@@ -370,6 +392,7 @@ export function useCommandBarRootRuntime({
370
392
  rootQuery,
371
393
  rootResultModel.initialIdx,
372
394
  rootSelectionNavigatedRef,
395
+ rootSymbolAnswerPending,
373
396
  setRootHoveredIdx,
374
397
  setRootSelectedIdx,
375
398
  ]);
@@ -44,6 +44,8 @@ interface CommandBarProps {
44
44
  pluginRegistry: PluginRegistry;
45
45
  quitApp: () => void;
46
46
  onCheckForUpdates?: () => void | Promise<void>;
47
+ /** Settings' Run Setup Again; without it (the browser, or setup already showing) the command is left out. */
48
+ onRunSetupAgain?: () => Promise<void>;
47
49
  onNativeOccluderChange?: (rect: LayoutBounds | null) => void;
48
50
  }
49
51
 
@@ -53,6 +55,7 @@ export function CommandBar({
53
55
  pluginRegistry,
54
56
  quitApp,
55
57
  onCheckForUpdates,
58
+ onRunSetupAgain,
56
59
  onNativeOccluderChange,
57
60
  }: CommandBarProps) {
58
61
  const {
@@ -80,9 +83,11 @@ export function CommandBar({
80
83
  visibleListStateRef,
81
84
  } = useCommandBarEnvironment();
82
85
  // Without an updater (the web terminal) neither checking nor a channel means anything.
83
- const availableCommands = useMemo(() => onCheckForUpdates
84
- ? allAvailableCommands
85
- : allAvailableCommands.filter((command) => command.id !== "check-for-updates" && command.id !== "release-channel"), [allAvailableCommands, onCheckForUpdates]);
86
+ const availableCommands = useMemo(() => allAvailableCommands.filter((command) => {
87
+ if (command.id === "check-for-updates" || command.id === "release-channel") return !!onCheckForUpdates;
88
+ if (command.id === "run-setup-again") return !!onRunSetupAgain;
89
+ return true;
90
+ }), [allAvailableCommands, onCheckForUpdates, onRunSetupAgain]);
86
91
  const {
87
92
  applyThemePreview,
88
93
  clearThemePreview,
@@ -200,6 +205,7 @@ export function CommandBar({
200
205
  dispatch,
201
206
  focusedPaneId: state.focusedPaneId,
202
207
  onCheckForUpdates,
208
+ onRunSetupAgain,
203
209
  persistConfig,
204
210
  pluginRegistry,
205
211
  pushRoute,
@@ -84,6 +84,7 @@ export function CompanyLogo({
84
84
  alignItems="center"
85
85
  justifyContent="center"
86
86
  objectFit="contain"
87
+ fallbackWhileLoading
87
88
  style={{
88
89
  width: cellWidthPx * width,
89
90
  height: cellHeightPx * height,
@@ -91,7 +92,7 @@ export function CompanyLogo({
91
92
  ...style,
92
93
  }}
93
94
  >
94
- {/* Without a logo the initial fills the same tile, so the header keeps its shape. */}
95
+ {/* Without a logo, or until it loads, the initial fills the same tile, so the header keeps its shape. */}
95
96
  <Box width="100%" height="100%" bg={colors.border} alignItems="center" justifyContent="center">
96
97
  <Text
97
98
  fg={colors.text}
@@ -36,6 +36,7 @@ import { resolveHeaderPromptGeometry } from "./shell/chrome";
36
36
  import { useWindowFullscreen } from "./window-fullscreen";
37
37
  import { WindowControls, WINDOWS_CONTROL_GROUP_WIDTH_PX } from "./window-controls";
38
38
  import { Button, terminalButtonColumns } from "../ui/button";
39
+ import { DelayedMark, delayedMarkWidth } from "../ui/delayed-mark";
39
40
 
40
41
  const UPDATE_NOTICE_DURATION_MS = 5_000;
41
42
  /** Kept clear after the terminal update status, so its last character never touches the Help pill. */
@@ -768,7 +769,10 @@ function HeaderMarketSummary({ nativePaneChrome, width }: { nativePaneChrome: bo
768
769
  available: width,
769
770
  baseCurrencyWidth: displayWidth(summary.baseCurrencyLabel) + 1,
770
771
  countdownWidth: summary.marketLabel.length - summary.marketLabelShort.length,
771
- spyWidth: summary.spyText.length + 1,
772
+ // A delayed SPY keeps its mark: the two fit, or give way, together. Once the
773
+ // rest of the cluster is gone, it gives up its move rather than give way.
774
+ spyWidth: summary.spyText.length + 1 + (summary.spyDelayMinutes != null ? delayedMarkWidth(summary.spyDelayMinutes, nativePaneChrome) + 1 : 0),
775
+ spyMoveWidth: summary.spyDelayMinutes != null ? summary.spyText.length - summary.spyPriceText.length : 0,
772
776
  stateWidth: summary.marketLabelShort ? summary.marketLabelShort.length + 1 : 0,
773
777
  });
774
778
  const marketLabel = fit.showCountdown ? summary.marketLabel : summary.marketLabelShort;
@@ -783,8 +787,9 @@ function HeaderMarketSummary({ nativePaneChrome, width }: { nativePaneChrome: bo
783
787
  </Box>
784
788
  ) : null}
785
789
  {fit.showSpy ? (
786
- <Box paddingRight={1} flexShrink={0} data-gloom-role="header-market">
787
- <Text fg={summary.spyColor}>{summary.spyText}</Text>
790
+ <Box paddingRight={1} flexShrink={0} flexDirection="row" alignItems="center" gap={1} data-gloom-role="header-market">
791
+ <Text fg={summary.spyColor}>{fit.showSpyMove ? summary.spyText : summary.spyPriceText}</Text>
792
+ {summary.spyDelayMinutes != null ? <DelayedMark minutes={summary.spyDelayMinutes} /> : null}
788
793
  </Box>
789
794
  ) : null}
790
795
  {fit.showBaseCurrency ? (
@@ -12,6 +12,7 @@ import { formatPercentRaw } from "../../utils/format";
12
12
  import { formatMarketPriceWithCurrency, liveQuoteFormatOptions } from "../../market-data/market/format";
13
13
  import { getSessionMoveDisplay, marketStateColor, marketStateCountdown, marketStateLabel } from "../../market-data/market/status";
14
14
  import type { Quote } from "../../types/financials";
15
+ import { useFreeQuoteDelay } from "../../api-client/plan-access";
15
16
 
16
17
  /**
17
18
  * SPY rides the shared feed. The snapshot poll only runs while the feed has
@@ -42,6 +43,10 @@ export interface MarketSummary {
42
43
  marketLabelShort: string;
43
44
  spyColor: string;
44
45
  spyText: string;
46
+ /** `spyText` without its move (`SPY $762.43`), for a delayed SPY in a header too narrow for both and the mark. */
47
+ spyPriceText: string;
48
+ /** Minutes SPY's price is late for this account (signed out or free), drawn as the delayed mark beside it; null when live. */
49
+ spyDelayMinutes: number | null;
45
50
  }
46
51
 
47
52
  export interface MarketSummaryFit {
@@ -49,6 +54,8 @@ export interface MarketSummaryFit {
49
54
  showCountdown: boolean;
50
55
  showState: boolean;
51
56
  showSpy: boolean;
57
+ /** SPY keeps its move; false when it gave the move up to stay (`spyMoveWidth`). */
58
+ showSpyMove: boolean;
52
59
  }
53
60
 
54
61
  /**
@@ -57,12 +64,16 @@ export interface MarketSummaryFit {
57
64
  * base currency, then the countdown suffix that widens the label. A narrowing
58
65
  * header therefore sheds the countdown, then the currency, then the state, and
59
66
  * keeps SPY longest. `countdownWidth` is what the suffix adds to the label.
67
+ * `spyMoveWidth` is what SPY's move adds to `spyWidth`, for a SPY that gives
68
+ * its move up before it gives way: a delayed SPY, whose mark makes it wider,
69
+ * so a narrow header still shows it and says it is late.
60
70
  */
61
71
  export function resolveMarketSummaryFit(options: {
62
72
  available: number;
63
73
  baseCurrencyWidth: number;
64
74
  countdownWidth: number;
65
75
  spyWidth: number;
76
+ spyMoveWidth?: number;
66
77
  stateWidth: number;
67
78
  }): MarketSummaryFit {
68
79
  let remaining = options.available;
@@ -71,11 +82,13 @@ export function resolveMarketSummaryFit(options: {
71
82
  remaining -= width;
72
83
  return true;
73
84
  };
74
- const showSpy = take(options.spyWidth);
85
+ const spyMoveWidth = options.spyMoveWidth ?? 0;
86
+ const showSpyMove = take(options.spyWidth);
87
+ const showSpy = showSpyMove || (spyMoveWidth > 0 && take(options.spyWidth - spyMoveWidth));
75
88
  const showState = take(options.stateWidth);
76
89
  const showBaseCurrency = take(options.baseCurrencyWidth);
77
90
  const showCountdown = showState && take(options.countdownWidth);
78
- return { showBaseCurrency, showCountdown, showState, showSpy };
91
+ return { showBaseCurrency, showCountdown, showState, showSpy, showSpyMove: showSpy && showSpyMove };
79
92
  }
80
93
 
81
94
  /**
@@ -87,15 +100,17 @@ export function resolveMarketSummaryFit(options: {
87
100
  * The price carries its currency's sign ($), so the base currency label
88
101
  * after it is never read as its unit.
89
102
  */
90
- export function spySummary(quote: Quote | null | undefined, colors: ThemeColors): Pick<MarketSummary, "spyColor" | "spyText"> {
103
+ export function spySummary(quote: Quote | null | undefined, colors: ThemeColors): Pick<MarketSummary, "spyColor" | "spyPriceText" | "spyText"> {
91
104
  const display = getSessionMoveDisplay(quote);
92
105
  // Fixed decimals and a percent slot as wide as "+0.53%", so a streamed tick
93
106
  // never slides the market-state label or re-fits the cluster.
107
+ const spyPriceText = display
108
+ ? `SPY ${formatMarketPriceWithCurrency(display.price, quote?.currency || "USD", liveQuoteFormatOptions(quote, quote?.currency, "ETF"))}`
109
+ : "SPY —";
94
110
  return {
95
111
  spyColor: display?.change != null ? priceColor(display.change, colors) : colors.textDim,
96
- spyText: display
97
- ? `SPY ${formatMarketPriceWithCurrency(display.price, quote?.currency || "USD", liveQuoteFormatOptions(quote, quote?.currency, "ETF"))} ${formatPercentRaw(display.changePercent).padStart(6)}`
98
- : "SPY —",
112
+ spyPriceText,
113
+ spyText: display ? `${spyPriceText} ${formatPercentRaw(display.changePercent).padStart(6)}` : spyPriceText,
99
114
  };
100
115
  }
101
116
 
@@ -135,7 +150,8 @@ export function useMarketSummary(): MarketSummary {
135
150
  return () => { clearInterval(id); };
136
151
  }, [appActive]);
137
152
 
138
- const { spyColor, spyText } = spySummary(spyQuote, colors);
153
+ const { spyColor, spyPriceText, spyText } = spySummary(spyQuote, colors);
154
+ const spyDelayMinutes = useFreeQuoteDelay(spyQuote);
139
155
 
140
156
  const mktCountdown = mktState ? marketStateCountdown(mktState, now) : null;
141
157
  const marketLabelShort = mktState ? t(marketStateLabel(mktState)) : "";
@@ -149,6 +165,8 @@ export function useMarketSummary(): MarketSummary {
149
165
  marketLabel,
150
166
  marketLabelShort,
151
167
  spyColor,
168
+ spyPriceText,
152
169
  spyText,
170
+ spyDelayMinutes,
153
171
  };
154
172
  }
@@ -24,6 +24,7 @@ import {
24
24
  } from "./model";
25
25
 
26
26
  export {
27
+ ageFooterSegment,
27
28
  companyDataFooterSegment,
28
29
  datesPaneData,
29
30
  DELAYED_SEGMENT,
@@ -80,10 +81,12 @@ function terminalSegmentKey(segment: PaneFooterSegment, focused: boolean): strin
80
81
  : "";
81
82
  }
82
83
 
83
- /** Columns an icon segment keeps when hints crowd the footer. */
84
+ /** Columns an icon segment keeps when hints crowd the footer: the icon, and on the desktop its `iconText` after it. */
84
85
  function iconSegmentReserve(segment: PaneFooterSegment, focused: boolean, nativePaneChrome: boolean): number {
85
86
  if (!segment.icon) return 0;
86
- return nativePaneChrome ? 3 : Math.max(3, 1 + terminalSegmentKey(segment, focused).length);
87
+ return nativePaneChrome
88
+ ? 3 + (segment.iconText ? segment.iconText.length + 1 : 0)
89
+ : Math.max(3, 1 + terminalSegmentKey(segment, focused).length);
87
90
  }
88
91
 
89
92
  function SegmentView({ segment, focused }: { segment: PaneFooterSegment; focused: boolean }) {
@@ -124,6 +127,7 @@ function SegmentView({ segment, focused }: { segment: PaneFooterSegment; focused
124
127
  data-icon={segment.icon}
125
128
  >
126
129
  <Icon name={segment.icon} size={14} color={segment.disabled ? colors.textMuted : footerToneColor(segment.parts[0] ?? { text: "" })} />
130
+ {segment.iconText ? <Text fg={colors.textMuted} selectable={false} data-gloom-role="pane-footer-icon-text">{segment.iconText}</Text> : null}
127
131
  </Box>
128
132
  );
129
133
  }
@@ -140,6 +144,7 @@ function SegmentView({ segment, focused }: { segment: PaneFooterSegment; focused
140
144
  disabled={!interactive}
141
145
  color={segment.disabled ? colors.textMuted : footerToneColor(segment.parts[0] ?? { text: "" })}
142
146
  onPress={() => segment.onPress?.()}
147
+ text={segment.iconText}
143
148
  />
144
149
  );
145
150
  }