@gloom-sh/terminal 0.19.1 → 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 (554) hide show
  1. package/README.md +7 -1
  2. package/package.json +2 -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 +58 -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/runtime/startup.ts +3 -1
  20. package/src/app.tsx +23 -1
  21. package/src/architecture/core-builtin-allowlist.json +2 -1
  22. package/src/capabilities/chart-series.ts +2 -1
  23. package/src/capabilities/factories.ts +1 -0
  24. package/src/cli/ascii-output.ts +42 -0
  25. package/src/cli/base-converter.ts +10 -2
  26. package/src/cli/command-options.ts +16 -1
  27. package/src/cli/commands/crypto-hints.ts +23 -4
  28. package/src/cli/commands/market.ts +101 -27
  29. package/src/cli/commands/news-search.ts +24 -5
  30. package/src/cli/commands/overview.ts +14 -10
  31. package/src/cli/commands/search.ts +84 -25
  32. package/src/cli/commands/system.ts +22 -30
  33. package/src/cli/commands/ticker.ts +49 -9
  34. package/src/cli/commands/version.ts +33 -0
  35. package/src/cli/context.ts +6 -9
  36. package/src/cli/data-dir.ts +7 -0
  37. package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
  38. package/src/cli/desktop-pane-shot-browser.ts +320 -0
  39. package/src/cli/desktop-pane-shot-routes.ts +26 -1
  40. package/src/cli/desktop-pane-shot.ts +196 -256
  41. package/src/cli/early-command.ts +42 -0
  42. package/src/cli/earnings-calendar.ts +9 -0
  43. package/src/cli/entry.ts +19 -13
  44. package/src/cli/errors.ts +12 -2
  45. package/src/cli/glossary.ts +2 -1
  46. package/src/cli/help.ts +1 -1
  47. package/src/cli/history-export.ts +22 -2
  48. package/src/cli/history-rows.ts +189 -11
  49. package/src/cli/index.ts +18 -1
  50. package/src/cli/known-gaps.ts +51 -5
  51. package/src/cli/listing-arg.ts +63 -10
  52. package/src/cli/options.ts +1 -1
  53. package/src/cli/output-file.ts +60 -13
  54. package/src/cli/pane-functions/catalog.ts +14 -3
  55. package/src/cli/pane-functions/data.ts +1 -1
  56. package/src/cli/pane-functions/dom.ts +45 -0
  57. package/src/cli/pane-functions/freshness.ts +46 -9
  58. package/src/cli/pane-functions/function-help.ts +17 -9
  59. package/src/cli/pane-functions/headless.ts +22 -14
  60. package/src/cli/pane-functions/index.ts +6 -2
  61. package/src/cli/pane-functions/options.ts +6 -2
  62. package/src/cli/pane-functions/resolver.ts +27 -5
  63. package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
  64. package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
  65. package/src/cli/pane-functions/screenshot-http.ts +70 -0
  66. package/src/cli/pane-functions/screenshot-render.ts +19 -5
  67. package/src/cli/quote-answer.ts +49 -6
  68. package/src/cli/registry.ts +41 -13
  69. package/src/cli/result.ts +12 -6
  70. package/src/cli/ticker-resolution.ts +2 -1
  71. package/src/cli/types.ts +2 -0
  72. package/src/components/chart/composite/format.ts +3 -0
  73. package/src/components/chart-table/header.tsx +1 -1
  74. package/src/components/chart-table/layout.ts +28 -11
  75. package/src/components/command-bar/action-runtime.ts +4 -0
  76. package/src/components/command-bar/commands/direct/index.ts +18 -33
  77. package/src/components/command-bar/commands/direct/runtime.ts +5 -0
  78. package/src/components/command-bar/commands/registry.ts +10 -1
  79. package/src/components/command-bar/routes/root/command-items.ts +9 -4
  80. package/src/components/command-bar/routes/root/runtime.ts +51 -6
  81. package/src/components/command-bar/selection-runtime.ts +12 -29
  82. package/src/components/command-bar/surface/index.tsx +9 -3
  83. package/src/components/company-logo.tsx +2 -1
  84. package/src/components/layout/detached-pane-shell.tsx +2 -2
  85. package/src/components/layout/header.tsx +10 -5
  86. package/src/components/layout/market-summary.ts +25 -7
  87. package/src/components/layout/pane/footer/index.tsx +8 -2
  88. package/src/components/layout/pane/footer/model.ts +73 -6
  89. package/src/components/layout/pane/header.tsx +2 -2
  90. package/src/components/layout/pane/status-footer.ts +2 -1
  91. package/src/components/market-board/index.tsx +3 -2
  92. package/src/components/onboarding/desks-step.tsx +12 -4
  93. package/src/components/onboarding/first-run-workspace.ts +81 -4
  94. package/src/components/onboarding/onboarding-frame.tsx +4 -2
  95. package/src/components/onboarding/onboarding-keyboard.ts +8 -5
  96. package/src/components/onboarding/onboarding-wizard.tsx +60 -26
  97. package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
  98. package/src/components/onboarding/run-setup-again.ts +24 -0
  99. package/src/components/onboarding/use-onboarding-progress.ts +33 -15
  100. package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
  101. package/src/components/onboarding/wizard-model.ts +57 -3
  102. package/src/components/onboarding/wizard-positions.ts +1 -1
  103. package/src/components/ui/delayed-mark.tsx +56 -0
  104. package/src/components/ui/display.tsx +16 -5
  105. package/src/components/ui/external-link.tsx +3 -6
  106. package/src/components/ui/icon.tsx +16 -3
  107. package/src/components/ui/logo-mark.tsx +8 -5
  108. package/src/components/ui/stat-grid.tsx +263 -19
  109. package/src/core/app-services.ts +11 -2
  110. package/src/data/app-persistence.ts +9 -7
  111. package/src/data/config/store/node.ts +11 -5
  112. package/src/data/config/store/normalize.ts +16 -3
  113. package/src/data/plugin-cache.ts +15 -0
  114. package/src/data/plugin-state-store.ts +81 -24
  115. package/src/data/profile-permissions.ts +47 -0
  116. package/src/data/resource-store.ts +165 -90
  117. package/src/data/session-store.ts +31 -8
  118. package/src/data/sqlite/access.ts +324 -0
  119. package/src/data/sqlite/checkpointer.ts +285 -0
  120. package/src/data/sqlite/database.ts +80 -4
  121. package/src/data/sqlite/retry.ts +50 -9
  122. package/src/data/ticker-store.ts +57 -23
  123. package/src/docs/snapshot.json +191 -78
  124. package/src/i18n/es.ts +18 -2
  125. package/src/i18n/ja.ts +18 -2
  126. package/src/i18n/ko.ts +18 -2
  127. package/src/i18n/zh-cn.ts +18 -2
  128. package/src/i18n/zh-tw.ts +18 -2
  129. package/src/layout/desk-keys.ts +4 -1
  130. package/src/layout/desks.ts +38 -5
  131. package/src/layout-marketplace/gallery-desktop.tsx +45 -16
  132. package/src/market-data/batched-reads.ts +16 -0
  133. package/src/market-data/coordinator/auxiliary.ts +4 -2
  134. package/src/market-data/coordinator/index.ts +11 -2
  135. package/src/market-data/futures-board.ts +1 -1
  136. package/src/market-data/futures-contract.ts +3 -1
  137. package/src/market-data/market/index-level.ts +6 -4
  138. package/src/market-data/published-apac-sessions.ts +6 -0
  139. package/src/market-data/realized-volatility.ts +4 -2
  140. package/src/news/aggregator.ts +7 -4
  141. package/src/news/news-model.ts +11 -0
  142. package/src/news/story-id.ts +22 -0
  143. package/src/news/ticker-symbols.ts +17 -1
  144. package/src/notifications/app-notifier.ts +35 -14
  145. package/src/plugins/auto-update-node.ts +10 -6
  146. package/src/plugins/builtin/account-management/form-components.tsx +1 -1
  147. package/src/plugins/builtin/account-management/index.tsx +2 -3
  148. package/src/plugins/builtin/account-management/model.ts +1 -1
  149. package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
  150. package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
  151. package/src/plugins/builtin/alerts/events.ts +10 -2
  152. package/src/plugins/builtin/alerts/index.tsx +5 -5
  153. package/src/plugins/builtin/alerts/research-builder.ts +5 -1
  154. package/src/plugins/builtin/alerts/research-rules.ts +18 -0
  155. package/src/plugins/builtin/alerts/targets.ts +4 -1
  156. package/src/plugins/builtin/attention/headless.ts +1 -1
  157. package/src/plugins/builtin/attention/index.tsx +2 -4
  158. package/src/plugins/builtin/attention/listing.ts +1 -1
  159. package/src/plugins/builtin/attention/series.ts +1 -1
  160. package/src/plugins/builtin/awards/index.tsx +1 -2
  161. package/src/plugins/builtin/backtest/client.ts +1 -0
  162. package/src/plugins/builtin/backtest/index.tsx +1 -2
  163. package/src/plugins/builtin/backtest/rules.ts +3 -1
  164. package/src/plugins/builtin/backtest/window.ts +8 -7
  165. package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
  166. package/src/plugins/builtin/broker-manager/index.tsx +1 -1
  167. package/src/plugins/builtin/buildout/cells.tsx +5 -0
  168. package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
  169. package/src/plugins/builtin/buildout/company/id.ts +2 -0
  170. package/src/plugins/builtin/buildout/company/model.ts +247 -0
  171. package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
  172. package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
  173. package/src/plugins/builtin/buildout/facilities.ts +309 -0
  174. package/src/plugins/builtin/buildout/format.ts +9 -0
  175. package/src/plugins/builtin/buildout/index.tsx +51 -5
  176. package/src/plugins/builtin/buildout/model/index.ts +62 -10
  177. package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
  178. package/src/plugins/builtin/buildout/model/types.ts +11 -2
  179. package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
  180. package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
  181. package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
  182. package/src/plugins/builtin/buildout/table-model.ts +26 -2
  183. package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
  184. package/src/plugins/builtin/cds/headless.ts +10 -2
  185. package/src/plugins/builtin/cds/model.ts +7 -3
  186. package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
  187. package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
  188. package/src/plugins/builtin/central-bank-rates/series.ts +2 -3
  189. package/src/plugins/builtin/changelog/index.tsx +3 -2
  190. package/src/plugins/builtin/chart-composer/cli-options.ts +16 -0
  191. package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -0
  192. package/src/plugins/builtin/chart-composer/headless.ts +88 -21
  193. package/src/plugins/builtin/chart-composer/index.tsx +2 -3
  194. package/src/plugins/builtin/chart-composer/pane.tsx +30 -2
  195. package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
  196. package/src/plugins/builtin/chart-composer/session-range.ts +5 -2
  197. package/src/plugins/builtin/chart-composer/studies.ts +1 -1
  198. package/src/plugins/builtin/chat/controller/index.ts +1 -1
  199. package/src/plugins/builtin/chat/controller/messages.ts +15 -4
  200. package/src/plugins/builtin/chat/ghost-user.ts +8 -0
  201. package/src/plugins/builtin/chat/index.tsx +5 -5
  202. package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
  203. package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
  204. package/src/plugins/builtin/cloud/index.tsx +1 -2
  205. package/src/plugins/builtin/cloud/plugin.tsx +6 -4
  206. package/src/plugins/builtin/cloud/team/module.tsx +1 -1
  207. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
  208. package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
  209. package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
  210. package/src/plugins/builtin/comment-letters/index.tsx +1 -2
  211. package/src/plugins/builtin/commodity-balances/client.ts +4 -1
  212. package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
  213. package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
  214. package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
  215. package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
  216. package/src/plugins/builtin/congress-trades/headless.ts +2 -0
  217. package/src/plugins/builtin/congress-trades/model.ts +92 -64
  218. package/src/plugins/builtin/congress-trades/table.tsx +14 -2
  219. package/src/plugins/builtin/connections/index.ts +2 -3
  220. package/src/plugins/builtin/cot/client.ts +28 -2
  221. package/src/plugins/builtin/cot/headless.ts +4 -1
  222. package/src/plugins/builtin/cot/index.tsx +5 -4
  223. package/src/plugins/builtin/cot/model.ts +58 -7
  224. package/src/plugins/builtin/cot/pane.tsx +6 -2
  225. package/src/plugins/builtin/cpi/client.ts +6 -5
  226. package/src/plugins/builtin/cpi/index.tsx +1 -2
  227. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
  228. package/src/plugins/builtin/credit-boards/client.ts +7 -2
  229. package/src/plugins/builtin/credit-boards/headless.ts +25 -12
  230. package/src/plugins/builtin/credit-boards/model.ts +29 -0
  231. package/src/plugins/builtin/credit-boards/series.ts +3 -4
  232. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
  233. package/src/plugins/builtin/credit-conditions/client.ts +22 -14
  234. package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
  235. package/src/plugins/builtin/crypto-board/client.ts +5 -5
  236. package/src/plugins/builtin/crypto-board/headless.ts +4 -3
  237. package/src/plugins/builtin/crypto-board/index.tsx +4 -2
  238. package/src/plugins/builtin/crypto-board/series.ts +6 -4
  239. package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
  240. package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
  241. package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
  242. package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
  243. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
  244. package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
  245. package/src/plugins/builtin/debt-maturities/model.ts +17 -1
  246. package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
  247. package/src/plugins/builtin/debt-maturities/series.ts +3 -3
  248. package/src/plugins/builtin/debug/index.tsx +1 -1
  249. package/src/plugins/builtin/dividend-yield/client.ts +32 -2
  250. package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
  251. package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
  252. package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
  253. package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
  254. package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
  255. package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
  256. package/src/plugins/builtin/dividend-yield/view.ts +21 -0
  257. package/src/plugins/builtin/docs/index.tsx +2 -3
  258. package/src/plugins/builtin/doe/client.ts +6 -5
  259. package/src/plugins/builtin/doe/headless.ts +16 -3
  260. package/src/plugins/builtin/doe/index.tsx +5 -4
  261. package/src/plugins/builtin/doe/prices.ts +2 -3
  262. package/src/plugins/builtin/doe/series.ts +4 -4
  263. package/src/plugins/builtin/earnings/board.tsx +2 -1
  264. package/src/plugins/builtin/earnings/history.tsx +2 -1
  265. package/src/plugins/builtin/earnings-calls/calendar.tsx +10 -2
  266. package/src/plugins/builtin/earnings-calls/data.ts +19 -11
  267. package/src/plugins/builtin/earnings-calls/pane.tsx +27 -14
  268. package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
  269. package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
  270. package/src/plugins/builtin/econ/calendar-model.ts +29 -6
  271. package/src/plugins/builtin/econ/detail-view.tsx +16 -2
  272. package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
  273. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  274. package/src/plugins/builtin/equity-screener/headless.ts +1 -0
  275. package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
  276. package/src/plugins/builtin/equity-screener/index.tsx +1 -2
  277. package/src/plugins/builtin/equity-screener/model.ts +28 -4
  278. package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
  279. package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
  280. package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
  281. package/src/plugins/builtin/executives/data.ts +6 -1
  282. package/src/plugins/builtin/executives/pane.tsx +9 -21
  283. package/src/plugins/builtin/exposure/holdings.ts +35 -13
  284. package/src/plugins/builtin/exposure/index.tsx +1 -2
  285. package/src/plugins/builtin/fear-greed/cache.ts +4 -0
  286. package/src/plugins/builtin/fear-greed/cli.ts +44 -0
  287. package/src/plugins/builtin/fear-greed/index.tsx +5 -53
  288. package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
  289. package/src/plugins/builtin/fear-greed/series.ts +6 -4
  290. package/src/plugins/builtin/fixed-income/index.tsx +1 -2
  291. package/src/plugins/builtin/futures/board-reads.ts +25 -0
  292. package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
  293. package/src/plugins/builtin/futures/index.tsx +15 -0
  294. package/src/plugins/builtin/futures/model.ts +14 -0
  295. package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
  296. package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
  297. package/src/plugins/builtin/futures-curve/headless.ts +6 -1
  298. package/src/plugins/builtin/futures-curve/index.tsx +1 -2
  299. package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
  300. package/src/plugins/builtin/gpu/board.tsx +51 -12
  301. package/src/plugins/builtin/gpu/client.ts +19 -3
  302. package/src/plugins/builtin/gpu/equities.tsx +5 -2
  303. package/src/plugins/builtin/gpu/headless.ts +14 -2
  304. package/src/plugins/builtin/gpu/index.tsx +1 -2
  305. package/src/plugins/builtin/gpu/model.ts +20 -2
  306. package/src/plugins/builtin/gpu/pane.tsx +1 -1
  307. package/src/plugins/builtin/gpu/series.ts +2 -3
  308. package/src/plugins/builtin/help/index.tsx +1 -1
  309. package/src/plugins/builtin/holders/beneficial-view.tsx +1 -2
  310. package/src/plugins/builtin/holders/index-holders.ts +24 -12
  311. package/src/plugins/builtin/holders/index.tsx +2 -3
  312. package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
  313. package/src/plugins/builtin/iv-history/index.tsx +1 -2
  314. package/src/plugins/builtin/jodi/client.ts +4 -1
  315. package/src/plugins/builtin/jodi/index.tsx +1 -2
  316. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
  317. package/src/plugins/builtin/layout-manager/index.tsx +9 -8
  318. package/src/plugins/builtin/macro-day/pane.tsx +4 -2
  319. package/src/plugins/builtin/market-halts/index.tsx +1 -2
  320. package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
  321. package/src/plugins/builtin/market-movers/client.ts +1 -0
  322. package/src/plugins/builtin/market-movers/index.tsx +16 -5
  323. package/src/plugins/builtin/market-movers/screener.ts +23 -6
  324. package/src/plugins/builtin/market-movers/session-body.tsx +6 -2
  325. package/src/plugins/builtin/market-valuation/index.tsx +1 -2
  326. package/src/plugins/builtin/members/index.tsx +1 -2
  327. package/src/plugins/builtin/money-markets/index.tsx +1 -2
  328. package/src/plugins/builtin/money-markets/pane.tsx +2 -1
  329. package/src/plugins/builtin/money-markets/series.ts +3 -5
  330. package/src/plugins/builtin/news/headless.ts +7 -0
  331. package/src/plugins/builtin/news/wire/index.tsx +1 -1
  332. package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
  333. package/src/plugins/builtin/notes/index.tsx +3 -6
  334. package/src/plugins/builtin/openfda/index.tsx +1 -2
  335. package/src/plugins/builtin/options/view.tsx +6 -5
  336. package/src/plugins/builtin/options-calculator/index.tsx +1 -2
  337. package/src/plugins/builtin/options-positioning/index.tsx +1 -2
  338. package/src/plugins/builtin/options-scenario/index.tsx +2 -4
  339. package/src/plugins/builtin/perps/index.tsx +1 -2
  340. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
  341. package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
  342. package/src/plugins/builtin/plugin-module.ts +16 -2
  343. package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
  344. package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
  345. package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
  346. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
  347. package/src/plugins/builtin/power/gas-client.ts +2 -1
  348. package/src/plugins/builtin/power/index.tsx +1 -2
  349. package/src/plugins/builtin/power/prices-client.ts +3 -2
  350. package/src/plugins/builtin/rate-path/index.tsx +1 -2
  351. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  352. package/src/plugins/builtin/realized-vol/index.tsx +1 -2
  353. package/src/plugins/builtin/realized-vol/model.ts +16 -2
  354. package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
  355. package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
  356. package/src/plugins/builtin/research/analyst-headless.ts +33 -6
  357. package/src/plugins/builtin/research/analyst-model.ts +37 -2
  358. package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
  359. package/src/plugins/builtin/research/index.tsx +5 -8
  360. package/src/plugins/builtin/research-search/index.tsx +1 -2
  361. package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
  362. package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
  363. package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
  364. package/src/plugins/builtin/risk-factors/data.ts +8 -4
  365. package/src/plugins/builtin/risk-factors/index.tsx +3 -1
  366. package/src/plugins/builtin/risk-factors/pane.tsx +5 -8
  367. package/src/plugins/builtin/roadmap/index.tsx +2 -3
  368. package/src/plugins/builtin/roadmap/pane.tsx +49 -2
  369. package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
  370. package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
  371. package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
  372. package/src/plugins/builtin/scanner/lock-note.ts +26 -3
  373. package/src/plugins/builtin/sec/filing-content.ts +38 -6
  374. package/src/plugins/builtin/sec/headless.ts +5 -1
  375. package/src/plugins/builtin/sec/index.tsx +4 -2
  376. package/src/plugins/builtin/sec/model.ts +2 -0
  377. package/src/plugins/builtin/sectors/index.tsx +11 -5
  378. package/src/plugins/builtin/series-inventory.ts +2 -0
  379. package/src/plugins/builtin/shared/cloud-resource.ts +73 -8
  380. package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
  381. package/src/plugins/builtin/shared/forming-bars.ts +29 -0
  382. package/src/plugins/builtin/shared/fred-points.ts +6 -1
  383. package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
  384. package/src/plugins/builtin/shared/pro-lock-note.ts +23 -8
  385. package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
  386. package/src/plugins/builtin/shared/series-warning.ts +13 -4
  387. package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
  388. package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
  389. package/src/plugins/builtin/short-volume/pane.tsx +2 -1
  390. package/src/plugins/builtin/short-volume/series.ts +2 -3
  391. package/src/plugins/builtin/social-mentions/client.ts +15 -10
  392. package/src/plugins/builtin/social-mentions/index.tsx +1 -2
  393. package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
  394. package/src/plugins/builtin/social-mentions/series.ts +2 -3
  395. package/src/plugins/builtin/supply-chain/client.ts +22 -1
  396. package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
  397. package/src/plugins/builtin/supply-chain/index.tsx +1 -2
  398. package/src/plugins/builtin/thirteenf/api.ts +127 -29
  399. package/src/plugins/builtin/thirteenf/data.ts +165 -35
  400. package/src/plugins/builtin/thirteenf/index.tsx +2 -4
  401. package/src/plugins/builtin/thirteenf/model.ts +2 -0
  402. package/src/plugins/builtin/thirteenf/overlap-pane.tsx +5 -1
  403. package/src/plugins/builtin/thirteenf/pane.tsx +41 -16
  404. package/src/plugins/builtin/thirteenf/types.ts +14 -0
  405. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -6
  406. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
  407. package/src/plugins/builtin/ticker-detail/financials/model.ts +35 -2
  408. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +53 -18
  409. package/src/plugins/builtin/ticker-detail/headless.ts +70 -11
  410. package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
  411. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
  412. package/src/plugins/builtin/time-sales/index.tsx +1 -2
  413. package/src/plugins/builtin/time-sales/pane.tsx +3 -1
  414. package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
  415. package/src/plugins/builtin/trace-bonds/model.ts +44 -10
  416. package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
  417. package/src/plugins/builtin/treasury-auctions/cache.ts +20 -8
  418. package/src/plugins/builtin/treasury-auctions/client.ts +3 -3
  419. package/src/plugins/builtin/treasury-auctions/headless.ts +3 -1
  420. package/src/plugins/builtin/treasury-auctions/pane.tsx +10 -4
  421. package/src/plugins/builtin/vol-surface/client.ts +18 -3
  422. package/src/plugins/builtin/vol-surface/index.tsx +1 -2
  423. package/src/plugins/builtin/volatility/client.ts +43 -16
  424. package/src/plugins/builtin/volatility/headless.ts +5 -2
  425. package/src/plugins/builtin/volatility/index.tsx +1 -2
  426. package/src/plugins/builtin/volatility/model.ts +17 -6
  427. package/src/plugins/builtin/volatility/pane.tsx +6 -2
  428. package/src/plugins/builtin/volatility/series.ts +2 -1
  429. package/src/plugins/builtin/world-indices/footer.ts +6 -1
  430. package/src/plugins/builtin/world-venue-map/client.ts +10 -1
  431. package/src/plugins/builtin/world-venue-map/index.ts +6 -2
  432. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
  433. package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
  434. package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
  435. package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
  436. package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
  437. package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
  438. package/src/plugins/builtin/yield-curve/client.ts +4 -6
  439. package/src/plugins/builtin/yield-curve/curves.ts +16 -2
  440. package/src/plugins/builtin/yield-curve/headless.ts +3 -3
  441. package/src/plugins/bundle.ts +52 -2
  442. package/src/plugins/catalog-browser.ts +2 -2
  443. package/src/plugins/catalog-cli.ts +20 -0
  444. package/src/plugins/host-link.ts +13 -1
  445. package/src/plugins/installer.ts +64 -42
  446. package/src/plugins/loader.ts +43 -11
  447. package/src/plugins/peer-imports.ts +201 -0
  448. package/src/plugins/peer-retry.ts +26 -29
  449. package/src/plugins/plugin-process.ts +128 -0
  450. package/src/plugins/redirection-guard.ts +45 -0
  451. package/src/remote/endpoint.ts +16 -3
  452. package/src/renderers/browser/main.tsx +3 -2
  453. package/src/renderers/cloudflare/http-proxy.ts +7 -123
  454. package/src/renderers/dom/desktop/icons.tsx +5 -2
  455. package/src/renderers/dom/host/image-surface.tsx +46 -21
  456. package/src/renderers/dom/styles.css +7 -0
  457. package/src/renderers/dom/toast-host.tsx +2 -0
  458. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  459. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +23 -1
  460. package/src/renderers/electrobun/bun/desktop/initialization.ts +12 -3
  461. package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
  462. package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
  463. package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
  464. package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
  465. package/src/renderers/electrobun/bun/window/attention.ts +54 -0
  466. package/src/renderers/electrobun/bun/window/create.ts +6 -2
  467. package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
  468. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
  469. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
  470. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
  471. package/src/renderers/electrobun/view/main.tsx +7 -2
  472. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
  473. package/src/renderers/electrobun/view/ui-host.tsx +9 -2
  474. package/src/renderers/opentui/browser-open.ts +10 -4
  475. package/src/renderers/opentui/image/surface.tsx +1 -1
  476. package/src/renderers/opentui/start.tsx +12 -6
  477. package/src/renderers/opentui/toast-host.tsx +2 -1
  478. package/src/series/registry.ts +18 -0
  479. package/src/series/resolve.ts +2 -1
  480. package/src/series/standalone.ts +10 -1
  481. package/src/series/types.ts +12 -0
  482. package/src/series/validate.ts +11 -2
  483. package/src/sources/gloomberb-cloud/fred-series.ts +5 -3
  484. package/src/sources/gloomberb-cloud/index.ts +28 -5
  485. package/src/sources/gloomberb-cloud/news.ts +24 -3
  486. package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
  487. package/src/sources/provider-errors.ts +26 -3
  488. package/src/sources/provider-router/history.ts +5 -0
  489. package/src/sources/provider-router/index.ts +13 -0
  490. package/src/sync/core-contributors.ts +32 -0
  491. package/src/telemetry/attention-counts.ts +64 -6
  492. package/src/telemetry/attention-setting.ts +33 -0
  493. package/src/telemetry/crash-reports-node.ts +3 -1
  494. package/src/telemetry/stall-clocks.ts +93 -0
  495. package/src/telemetry/stall-reports-node.ts +25 -9
  496. package/src/telemetry/stall-reports.ts +218 -13
  497. package/src/test-support/outage.ts +8 -0
  498. package/src/test-support/preload.ts +9 -0
  499. package/src/tickers/company-name-listing.ts +59 -29
  500. package/src/tickers/company-name.ts +84 -1
  501. package/src/tickers/listing-fate.ts +14 -3
  502. package/src/tickers/saved-instrument.ts +17 -0
  503. package/src/tickers/search/home-listing.ts +104 -8
  504. package/src/tickers/search/index.ts +5 -2
  505. package/src/tickers/search/types.ts +6 -1
  506. package/src/time-series/bar-aggregation.ts +7 -1
  507. package/src/time-series/base-series.ts +3 -3
  508. package/src/time-series/chart-bounds.ts +1 -1
  509. package/src/time-series/chart-data.ts +5 -2
  510. package/src/time-series/chart-seed.ts +1 -1
  511. package/src/time-series/coin-session-close.ts +211 -0
  512. package/src/time-series/live-bars.ts +13 -2
  513. package/src/time-series/live-quotes.ts +1 -1
  514. package/src/time-series/market.ts +5 -3
  515. package/src/time-series/official-close.ts +159 -0
  516. package/src/time-series/price-comparison.ts +75 -8
  517. package/src/time-series/resolve.ts +32 -4
  518. package/src/time-series/series-presentation.ts +1 -1
  519. package/src/time-series/studies/formula.ts +205 -0
  520. package/src/time-series/studies/indicators.ts +299 -0
  521. package/src/time-series/studies/leverage.ts +75 -0
  522. package/src/time-series/studies/pair.ts +138 -0
  523. package/src/time-series/studies/resolve.ts +333 -0
  524. package/src/time-series/studies/shared.ts +141 -0
  525. package/src/time-series/studies/units.ts +243 -0
  526. package/src/time-series/studies/vwap.ts +274 -0
  527. package/src/time-series/types.ts +7 -0
  528. package/src/types/config.ts +18 -2
  529. package/src/types/data-provider.ts +23 -0
  530. package/src/types/financials.ts +40 -3
  531. package/src/types/headless.ts +42 -3
  532. package/src/types/plugin.ts +32 -3
  533. package/src/ui/host.tsx +17 -1
  534. package/src/ui/toast.tsx +26 -1
  535. package/src/utils/ascii-text.ts +140 -0
  536. package/src/utils/cli-output.ts +91 -18
  537. package/src/utils/concurrency.ts +24 -0
  538. package/src/utils/exchanges.ts +6 -0
  539. package/src/utils/external-url.ts +20 -7
  540. package/src/utils/financial-statements.ts +73 -16
  541. package/src/utils/fundamentals.ts +17 -5
  542. package/src/utils/futures-generic.ts +4 -4
  543. package/src/utils/http-proxy-upstream.ts +124 -0
  544. package/src/utils/lease-liabilities.ts +54 -0
  545. package/src/utils/main-thread-stall.ts +122 -5
  546. package/src/utils/perf-marks.ts +58 -12
  547. package/src/utils/price-history-integrity.ts +47 -1
  548. package/src/utils/private-files.ts +141 -0
  549. package/src/utils/public-address.ts +99 -0
  550. package/src/account/upgrade-personalization.ts +0 -57
  551. package/src/account/upgrade-tickers.ts +0 -124
  552. package/src/api-client/upgrade-experiment.ts +0 -91
  553. package/src/plugins/builtin/cloud/browser.tsx +0 -3
  554. package/src/time-series/studies.ts +0 -1628
@@ -8,6 +8,8 @@ import {
8
8
  type SavedLayout,
9
9
  } from "../../types/config";
10
10
  import type { TickerMetadata, TickerRecord } from "../../types/ticker";
11
+ import { MAX_DESK_PICKS, type DeskKey } from "../../layout/desk-keys";
12
+ import { DESKS, isDeskKey } from "../../layout/desks";
11
13
 
12
14
  /**
13
15
  * The watchlist a first run lands with: the two index ETFs and the names most
@@ -211,13 +213,88 @@ export function buildFirstRunLayout({
211
213
  };
212
214
  }
213
215
 
214
- /** Replaces the Home layout with the first-run workspace and makes it active. */
215
- export function applyFirstRunLayout(config: AppConfig, home: SavedLayout): AppConfig {
216
- const layouts = config.layouts.length > 0 ? [home, ...config.layouts.slice(1)] : [home];
216
+ /**
217
+ * The ids of the layouts onboarding makes. Running it again finds its own
218
+ * layouts by these and replaces them, so it never adds a copy; a layout
219
+ * without one is the user's.
220
+ */
221
+ const ONBOARDING_HOME_LAYOUT_ID = "onboarding:home";
222
+ const ONBOARDING_LAYOUT_ID_PREFIX = "onboarding:";
223
+ const ONBOARDING_DESK_LAYOUT_ID_PREFIX = `${ONBOARDING_LAYOUT_ID_PREFIX}desk:`;
224
+
225
+ export function onboardingDeskLayoutId(desk: DeskKey): string {
226
+ return `${ONBOARDING_DESK_LAYOUT_ID_PREFIX}${desk}`;
227
+ }
228
+
229
+ function isOnboardingLayout(layout: SavedLayout): boolean {
230
+ return layout.id?.startsWith(ONBOARDING_LAYOUT_ID_PREFIX) === true;
231
+ }
232
+
233
+ /**
234
+ * Takes back the layouts setup made before it tagged them, so a run from
235
+ * Settings rebuilds them in place instead of copying them. Such a profile
236
+ * has setup's Home first and its desks right after it, under the desks'
237
+ * names. A first layout not called Home is the user's, and so is
238
+ * everything after the desks.
239
+ */
240
+ function adoptUntaggedSetupLayouts(config: AppConfig): AppConfig {
241
+ const [first, ...rest] = config.layouts;
242
+ if (!first || first.name !== "Home" || config.layouts.some(isOnboardingLayout)) return config;
243
+ const desks: SavedLayout[] = [];
244
+ for (const layout of rest) {
245
+ const key = DESKS.find((desk) => desk.name === layout.name)?.key;
246
+ if (!key || desks.length >= MAX_DESK_PICKS || desks.some((desk) => desk.id === onboardingDeskLayoutId(key))) break;
247
+ desks.push({ ...layout, id: onboardingDeskLayoutId(key) });
248
+ }
249
+ return {
250
+ ...config,
251
+ layouts: [{ ...first, id: ONBOARDING_HOME_LAYOUT_ID }, ...desks, ...rest.slice(desks.length)],
252
+ };
253
+ }
254
+
255
+ /** The desks setup made that are still tabs, in tab order: what a run from Settings starts with ticked. */
256
+ export function setupDeskPicks(config: AppConfig): DeskKey[] {
257
+ const picks: DeskKey[] = [];
258
+ for (const layout of adoptUntaggedSetupLayouts(config).layouts) {
259
+ const key = layout.id?.startsWith(ONBOARDING_DESK_LAYOUT_ID_PREFIX)
260
+ ? layout.id.slice(ONBOARDING_DESK_LAYOUT_ID_PREFIX.length)
261
+ : null;
262
+ if (isDeskKey(key) && !picks.includes(key)) picks.push(key);
263
+ }
264
+ return picks.slice(0, MAX_DESK_PICKS);
265
+ }
266
+
267
+ /**
268
+ * Puts the first-run workspace in place and makes Home active: Home, then a
269
+ * tab per picked desk. The layouts an earlier run made are replaced where
270
+ * they stood, and a desk not picked this time goes with them; a first run
271
+ * replaces the stock Home it starts with. Every other layout is the user's
272
+ * and keeps its place and content. A run from Settings (`rerun`) also
273
+ * rebuilds what setup made before it tagged its layouts, and with none of
274
+ * setup's left it puts Home first, replacing nothing.
275
+ */
276
+ export function applyFirstRunLayout(
277
+ config: AppConfig,
278
+ home: SavedLayout,
279
+ desks: ReadonlyArray<SavedLayout & { id: string }> = [],
280
+ { rerun = false }: { rerun?: boolean } = {},
281
+ ): AppConfig {
282
+ const { layouts: current } = rerun ? adoptUntaggedSetupLayouts(config) : config;
283
+ const earlier = current.findIndex(isOnboardingLayout);
284
+ const users = current.filter((layout) => !isOnboardingLayout(layout));
285
+ // Everything before the first layout onboarding made is the user's, so its index holds.
286
+ const at = earlier >= 0 ? earlier : 0;
287
+ const kept = earlier >= 0 || rerun ? users : users.slice(1);
288
+ const layouts = [
289
+ ...kept.slice(0, at),
290
+ { ...home, id: ONBOARDING_HOME_LAYOUT_ID },
291
+ ...desks,
292
+ ...kept.slice(at),
293
+ ];
217
294
  return {
218
295
  ...config,
219
296
  layouts,
220
297
  layout: home.layout,
221
- activeLayoutIndex: 0,
298
+ activeLayoutIndex: at,
222
299
  };
223
300
  }
@@ -219,7 +219,8 @@ export function OnboardingCoach({
219
219
  </Box>
220
220
  <Box height={1} />
221
221
  <Box flexGrow={1} minHeight={0}>{children}</Box>
222
- <Box height={1} flexDirection="row" gap={1}>{actions}</Box>
222
+ {/* As OnboardingActions: a button that does not fit beside the one before it wraps under it. */}
223
+ <Box flexDirection="row" flexWrap="wrap" alignItems="center" columnGap={1} flexShrink={0}>{actions}</Box>
223
224
  </Box>
224
225
  );
225
226
  }
@@ -358,7 +359,8 @@ export function OnboardingHeader({
358
359
  }
359
360
 
360
361
  // Progress, not navigation: three segments, the current one lit, the ones
361
- // behind it filled. A filled segment is still a way back.
362
+ // behind it filled. A filled segment is a way back unless the wizard marks
363
+ // it unavailable: a step done for good stays filled and is not offered again.
362
364
  const activeIndex = sections.findIndex((section) => section.id === active);
363
365
  return (
364
366
  <Box flexDirection="row" alignItems="flex-start" justifyContent="space-between" minWidth={0}>
@@ -58,7 +58,7 @@ export function useOnboardingKeyboard({
58
58
  submitAccountField, backPortfolio, continuePortfolio, setPortfolioOptionIdx, activeBrokerFields,
59
59
  brokerFieldIdx, setBrokerSelectIdx, brokerChoices, openBrokerGuide, chosenDesks, finishDesks,
60
60
  setDeskCursor, deskCursor, setChosenDesks, continueAccount, skipSetup, primaryUpgradeAction,
61
- setBillingInterval, keybindings, dialogOpen, commandBarOpen, saveProgressInBackground,
61
+ setBillingInterval, keybindings, dialogOpen, commandBarOpen, saveProgressInBackground, goBack,
62
62
  }: {
63
63
  stage: OnboardingStage;
64
64
  progress: OnboardingProgress;
@@ -102,6 +102,8 @@ export function useOnboardingKeyboard({
102
102
  dialogOpen: boolean;
103
103
  commandBarOpen: boolean;
104
104
  saveProgressInBackground: (patch: Partial<OnboardingProgress> & Pick<OnboardingProgress, "stage">, baseConfig?: AppConfig | undefined) => void;
105
+ /** Back on the card: to the step the user came from. */
106
+ goBack: () => void;
105
107
  }) {
106
108
  const activeSection: OnboardingSectionId = stage === "portfolio" || stage === "desks"
107
109
  ? "portfolio"
@@ -128,7 +130,8 @@ export function useOnboardingKeyboard({
128
130
 
129
131
  const handled = ((): boolean => {
130
132
  if (isPlainKey(event, "f10")) {
131
- if (stage === "portfolio" || stage === "desks" || isBrokerCommitting) return true;
133
+ // The first run needs a holding; a run from Settings can be left as it was.
134
+ if (isBrokerCommitting || ((stage === "portfolio" || stage === "desks") && !progress.rerun)) return true;
132
135
  if (stage === "upgrade" && !planAccess.hasProAccess) continueFree();
133
136
  else void finish(true);
134
137
  return true;
@@ -234,7 +237,7 @@ export function useOnboardingKeyboard({
234
237
  continueAccount();
235
238
  } else if (escape) {
236
239
  if (account.accountSub === "qr" || account.accountSub === "login") account.returnToAccountForm();
237
- else goToSection("portfolio");
240
+ else goBack();
238
241
  } else if (letter(BROWSER_SIGN_IN_KEY) && accountForm) {
239
242
  account.beginQrSignIn();
240
243
  } else if (letter(SKIP_SETUP_KEY)) {
@@ -251,7 +254,7 @@ export function useOnboardingKeyboard({
251
254
  if (enter) {
252
255
  primaryUpgradeAction();
253
256
  } else if (escape) {
254
- goToSection("cloud");
257
+ goBack();
255
258
  } else if (!planAccess.hasProAccess && letter(KEEP_FREE_KEY)) {
256
259
  continueFree();
257
260
  } else if (!planAccess.hasProAccess && (isPlainKey(event, "left") || letter("h"))) {
@@ -266,7 +269,7 @@ export function useOnboardingKeyboard({
266
269
 
267
270
  if (stage === "ready") {
268
271
  if (enter) void finish();
269
- else if (escape) goToSection(progress.accountStatus === "signed-in" ? "pro" : "cloud");
272
+ else if (escape) goBack();
270
273
  else return false;
271
274
  return true;
272
275
  }
@@ -20,7 +20,7 @@ import { t, tf } from "../../i18n";
20
20
  import { useAppLanguage } from "../../i18n/react";
21
21
  import type { PluginRegistry } from "../../plugins/registry";
22
22
  import { formatCloudPrice, monthsFreeYearly } from "../../account/billing";
23
- import { proStepCopy, proStepRealtimeTitle } from "../../account/upgrade-dialog";
23
+ import { proStepCopy } from "../../account/upgrade-dialog";
24
24
  import { Button, SegmentedControl, terminalButtonColumns } from "../ui";
25
25
  import { AccountStep, PortfolioStep } from "./onboarding-steps";
26
26
  import {
@@ -41,7 +41,7 @@ import { useOnboardingAccount } from "./wizard-account";
41
41
  import { useOnboardingBrokerSync } from "./wizard-broker-sync";
42
42
  import { useOnboardingPositions } from "./wizard-positions";
43
43
  import { DesksStep, toggleDeskChoice } from "./desks-step";
44
- import { getOnboardingProgress } from "./wizard-model";
44
+ import { getOnboardingProgress, isOnboardingWorkspaceBuilt, previousOnboardingStage } from "./wizard-model";
45
45
  import {
46
46
  useOnboardingKeyboard,
47
47
  SKIP_SETUP_KEY,
@@ -86,7 +86,22 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
86
86
  const config = useAppSelector((state) => state.config);
87
87
  const progress = getOnboardingProgress(config);
88
88
  const stage = progress.stage;
89
+ const workspaceBuilt = isOnboardingWorkspaceBuilt(progress);
90
+ /** Opened again from Settings: any step can be left, and the first-run funnel is not told. */
91
+ const rerun = progress.rerun === true;
89
92
  const researchOpenedRef = useRef<string | null>(null);
93
+ /** The stages the user came through, oldest first, for Back to retrace. */
94
+ const visitedRef = useRef<OnboardingStage[]>([]);
95
+ const shownStageRef = useRef(stage);
96
+ const backTargetRef = useRef<OnboardingStage | null>(null);
97
+ useEffect(() => {
98
+ const left = shownStageRef.current;
99
+ if (left === stage) return;
100
+ shownStageRef.current = stage;
101
+ const back = backTargetRef.current === stage;
102
+ backTargetRef.current = null;
103
+ if (!back) visitedRef.current = [...visitedRef.current, left];
104
+ }, [stage]);
90
105
  const [editingField, setEditingField] = useState(false);
91
106
  /** The added position the keyboard acts on once no field is being typed in. */
92
107
  const [positionCursorSymbol, setPositionCursorSymbol] = useState<string | null>(null);
@@ -109,7 +124,7 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
109
124
  // bar, so an unread inbox never stalls the first session.
110
125
  const account = useOnboardingAccount({
111
126
  nextStep: () => {
112
- recordResearchActivity("onboarding_signed_in");
127
+ if (!rerun) recordResearchActivity("onboarding_signed_in");
113
128
  saveProgressInBackground({ stage: "upgrade", accountStatus: "signed-in" });
114
129
  },
115
130
  setEditingField,
@@ -127,12 +142,12 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
127
142
 
128
143
  useEffect(() => {
129
144
  const activity = STAGE_ACTIVITY[stage];
130
- if (activity) recordResearchActivity(activity);
131
- }, [stage]);
145
+ if (activity && !rerun) recordResearchActivity(activity);
146
+ }, [rerun, stage]);
132
147
 
133
148
  useEffect(() => {
134
- if (stage === "portfolio" && positionCount > 0) recordResearchActivity("onboarding_position_added");
135
- }, [positionCount, stage]);
149
+ if (stage === "portfolio" && positionCount > 0 && !rerun) recordResearchActivity("onboarding_position_added");
150
+ }, [positionCount, rerun, stage]);
136
151
 
137
152
  // The portfolio step opens straight into the ticker field.
138
153
  useEffect(() => {
@@ -197,8 +212,8 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
197
212
  useEffect(() => {
198
213
  if (stage !== "research" || !progress.tickerSymbol || researchOpenedRef.current === progress.tickerSymbol) return;
199
214
  researchOpenedRef.current = progress.tickerSymbol;
200
- recordResearchActivity("ticker_saved");
201
- }, [stage, progress.tickerSymbol]);
215
+ if (!rerun) recordResearchActivity("ticker_saved");
216
+ }, [rerun, stage, progress.tickerSymbol]);
202
217
  const movePositionCursor = useCallback((delta: number) => {
203
218
  if (positionCursorIndex < 0) return;
204
219
  const next = Math.max(0, Math.min(positionCount - 1, positionCursorIndex + delta));
@@ -239,16 +254,17 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
239
254
  : null;
240
255
  const helpFocused = focusedInstance?.paneId === "help";
241
256
  const {
242
- offer, pricing, billingInterval, setBillingInterval, planAccess, primaryUpgradeAction, continueFree, personalTickers,
257
+ offer, pricing, billingInterval, setBillingInterval, planAccess, primaryUpgradeAction, continueFree,
243
258
  } = useOnboardingUpgrade({
244
259
  stage, progress, saveProgressInBackground, persistProgress,
245
- shown: !helpFocused && !dialogOpen && !commandBarOpen,
246
260
  });
247
261
 
248
262
  const goToSection = useCallback((section: OnboardingSectionId) => {
249
263
  if (finishingRef.current || isBrokerCommitting) return;
250
264
  setEditingField(false);
251
265
  if (section === "portfolio") {
266
+ // Done for good once the workspace is built: picking it again would build it twice.
267
+ if (workspaceBuilt) return;
252
268
  setPortfolioSub("positions");
253
269
  saveProgressInBackground({ stage: "portfolio" });
254
270
  return;
@@ -260,10 +276,22 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
260
276
  if (planAccess.signedIn) {
261
277
  saveProgressInBackground({ stage: "upgrade", accountStatus: "signed-in" });
262
278
  }
263
- }, [isBrokerCommitting, planAccess.signedIn, saveProgressInBackground]);
279
+ }, [isBrokerCommitting, planAccess.signedIn, saveProgressInBackground, workspaceBuilt]);
280
+
281
+ /** Back and Esc: to the step the user came from, never one that is done for good. */
282
+ const goBack = useCallback(() => {
283
+ if (finishingRef.current || isBrokerCommitting) return;
284
+ const back = previousOnboardingStage(progress, visitedRef.current, { pro: planAccess.hasProAccess });
285
+ if (!back) return;
286
+ visitedRef.current = back.visited;
287
+ backTargetRef.current = back.stage;
288
+ setEditingField(false);
289
+ if (back.stage === "portfolio") setPortfolioSub("positions");
290
+ saveProgressInBackground({ stage: back.stage });
291
+ }, [isBrokerCommitting, planAccess.hasProAccess, progress, saveProgressInBackground]);
264
292
 
265
293
  const sectionAvailability: Partial<Record<OnboardingSectionId, boolean>> = {
266
- portfolio: !isBrokerCommitting,
294
+ portfolio: !isBrokerCommitting && !workspaceBuilt,
267
295
  // The workspace is built when the desks are picked, so that step comes first.
268
296
  cloud: !isBrokerCommitting && stage !== "desks" && (stage === "account" || stage === "upgrade" || stage === "ready" || !!progress.tickerSymbol),
269
297
  pro: !isBrokerCommitting && planAccess.signedIn && (
@@ -282,7 +310,7 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
282
310
  submitAccountField, backPortfolio, continuePortfolio, setPortfolioOptionIdx, activeBrokerFields,
283
311
  brokerFieldIdx, setBrokerSelectIdx, brokerChoices, openBrokerGuide, chosenDesks, finishDesks,
284
312
  setDeskCursor, deskCursor, setChosenDesks, continueAccount, skipSetup, primaryUpgradeAction,
285
- setBillingInterval, keybindings, dialogOpen, commandBarOpen, saveProgressInBackground,
313
+ setBillingInterval, keybindings, dialogOpen, commandBarOpen, saveProgressInBackground, goBack,
286
314
  });
287
315
 
288
316
  return {
@@ -295,7 +323,7 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
295
323
  openBrokerConnect, continueFromPositions, backPortfolio, continuePortfolio, chosenDesks, deskCursor,
296
324
  setDeskCursor, setChosenDesks, buildingDesks, finishDesks, account, viewportWidth, viewportHeight, continueAccount,
297
325
  offer, planAccess, pricing, billingInterval, setBillingInterval, continueFree, primaryUpgradeAction,
298
- personalTickers,
326
+ goBack, rerun,
299
327
  };
300
328
  }
301
329
 
@@ -310,7 +338,7 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
310
338
  openBrokerConnect, continueFromPositions, backPortfolio, continuePortfolio, chosenDesks, deskCursor,
311
339
  setDeskCursor, setChosenDesks, buildingDesks, finishDesks, account, viewportWidth, viewportHeight, continueAccount,
312
340
  offer, planAccess, pricing, billingInterval, setBillingInterval, continueFree, primaryUpgradeAction,
313
- personalTickers,
341
+ goBack, rerun,
314
342
  } = useOnboardingWizard(props);
315
343
 
316
344
  if (helpFocused) {
@@ -322,12 +350,15 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
322
350
  return <OnboardingCoach step={t("YOUR WORKSPACE")}
323
351
  title={tf("Built around {ticker}", { ticker })}
324
352
  actions={<>
325
- {/* The card has no room for both keys: like a toast, the dismiss key
326
- is in the desktop tooltip. F10 also dismisses it. */}
353
+ {/* Each button shows its key, as Connect shows Alt+Enter; in the
354
+ terminal the second wraps under the first. The desktop card has no
355
+ room for both: like a toast, its dismiss key is in the tooltip.
356
+ F10 also dismisses it. */}
327
357
  <OnboardingButton
328
358
  label="Keep exploring"
329
359
  variant="ghost"
330
- title={notificationDismissShortcut ? `${t("Keep exploring")} (${notificationDismissShortcut})` : undefined}
360
+ shortcut={!desktop && notificationDismissShortcut ? notificationDismissShortcut : undefined}
361
+ title={desktop && notificationDismissShortcut ? `${t("Keep exploring")} (${notificationDismissShortcut})` : undefined}
331
362
  onPress={() => { void finish(); }}
332
363
  />
333
364
  <OnboardingButton
@@ -384,7 +415,8 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
384
415
  onDismiss={skipSetup}
385
416
  dismissing={isFinishing}
386
417
  dismissDisabled={isBrokerCommitting}
387
- showDismiss={false}
418
+ // The first run needs a holding; a run from Settings can be left as it was.
419
+ showDismiss={rerun}
388
420
  />
389
421
  <OnboardingTitle
390
422
  step={desktop ? undefined : t("PORTFOLIO")}
@@ -464,12 +496,14 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
464
496
  onNavigate={goToSection}
465
497
  onDismiss={skipSetup}
466
498
  dismissing={isFinishing}
467
- showDismiss={false}
499
+ dismissDisabled={buildingDesks}
500
+ showDismiss={rerun}
468
501
  />
469
502
  <OnboardingTitle
470
503
  step={desktop ? undefined : t("DESKS")}
471
- title={t("What do you trade?")}
472
- description={t("Each one adds a ready-made desk as a tab.")}
504
+ title={t("What do you trade most?")}
505
+ // A line of its own: the picks guide the workspace, they do not limit it.
506
+ description={`${t("Pick up to 3; each adds a ready-made tab.")}\n${t("Everything else is one search away.")}`}
473
507
  />
474
508
  {/* Not allowed to shrink: on a short window the card scrolls instead of the list running under the buttons. */}
475
509
  <DesksStep
@@ -591,7 +625,7 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
591
625
  <OnboardingButton
592
626
  label="Back"
593
627
  variant="ghost"
594
- onPress={account.accountSub === "qr" || account.accountSub === "login" ? account.returnToAccountForm : () => goToSection("portfolio")}
628
+ onPress={account.accountSub === "qr" || account.accountSub === "login" ? account.returnToAccountForm : goBack}
595
629
  />
596
630
  {account.accountSub !== "qr" ? (
597
631
  <OnboardingButton
@@ -618,7 +652,7 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
618
652
  : proCopy.note;
619
653
  // Ranked: the data itself first, then what reads it.
620
654
  const features = [
621
- { title: proStepRealtimeTitle(personalTickers), description: t("Free is 15 minutes behind on quotes and 12 hours on news.") },
655
+ { title: t("Real-time market data"), description: t("Free is 15 minutes behind on quotes and 12 hours on news.") },
622
656
  { title: t("MCP server"), description: t("Claude Code, Codex or Cursor call Gloom's research tools.") },
623
657
  { title: t("Ask Gloom"), description: t("Answers cite filings, calls and news.") },
624
658
  { title: t("Earnings calls"), description: t("Transcripts, summaries, guidance and scores.") },
@@ -731,7 +765,7 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
731
765
  <OnboardingButton
732
766
  label="Back"
733
767
  variant="ghost"
734
- onPress={() => goToSection(progress.accountStatus === "signed-in" ? "pro" : "cloud")}
768
+ onPress={goBack}
735
769
  />
736
770
  <OnboardingButton
737
771
  label={isFinishing ? "Opening workspace..." : "Start exploring"}
@@ -7,6 +7,8 @@ import { formatMarketPrice } from "../../../market-data/market/format";
7
7
  import { getRegularSessionDisplay } from "../../../market-data/market/status";
8
8
  import { formatPercentRaw } from "../../../utils/format";
9
9
  import { Button, NumberField, TextField } from "../../ui";
10
+ import { DelayedMark } from "../../ui/delayed-mark";
11
+ import { useFreeQuoteDelay } from "../../../api-client/plan-access";
10
12
  import { ONBOARDING_DESKTOP } from "../onboarding-frame";
11
13
  import {
12
14
  POSITION_FIELDS,
@@ -31,7 +33,7 @@ const FIELD_LABELS: Record<PositionFieldId, string> = {
31
33
  const FIELD_PLACEHOLDERS: Record<Exclude<PositionFieldId, "currency">, string> = {
32
34
  ticker: "e.g. AAPL",
33
35
  shares: "optional",
34
- avgCost: "current price",
36
+ avgCost: "last price",
35
37
  };
36
38
 
37
39
  /** A field's label; the size counts contracts once the symbol is a future. */
@@ -47,6 +49,8 @@ function fieldPlaceholder(field: PositionFieldId, state: OnboardingPositionsStat
47
49
  /** Company name and last price for the symbol being typed, or why it will not resolve. */
48
50
  function PreviewLine({ preview, error }: Pick<OnboardingPositionsState, "preview" | "error">) {
49
51
  const colors = useThemeColors();
52
+ // Signed out or free, the price is the delayed one, and says so.
53
+ const delay = useFreeQuoteDelay(preview.status === "ready" ? preview.quote : null);
50
54
  if (error) return <Text fg={colors.negative} wrapText>{error}</Text>;
51
55
  if (preview.status === "idle") return null;
52
56
  if (preview.status === "checking") {
@@ -58,11 +62,12 @@ function PreviewLine({ preview, error }: Pick<OnboardingPositionsState, "preview
58
62
  const price = headline?.price;
59
63
  const change = headline?.changePercent;
60
64
  return (
61
- <Box flexDirection="row" minWidth={0} overflow="hidden">
65
+ <Box flexDirection="row" alignItems="center" minWidth={0} overflow="hidden">
62
66
  <Text fg={colors.text} attributes={TextAttributes.BOLD}>{preview.symbol}</Text>
63
67
  {preview.name ? <Text fg={colors.textDim}>{` ${preview.name}`}</Text> : null}
64
68
  {price != null ? <Text fg={colors.text}>{` ${formatMarketPrice(price, { maxWidth: 12 })}`}</Text> : null}
65
69
  {change != null ? <Text fg={shownPriceColor(change, 2, colors)}>{` ${formatPercentRaw(change)}`}</Text> : null}
70
+ {price != null && delay != null ? <Box flexShrink={0} paddingLeft={1}><DelayedMark minutes={delay} /></Box> : null}
66
71
  {preview.duplicate ? <Text fg={colors.textMuted}>{t(" already added")}</Text> : null}
67
72
  </Box>
68
73
  );
@@ -0,0 +1,24 @@
1
+ import { useCallback } from "react";
2
+ import { useAppDispatch, useAppStateRef } from "../../state/app/context";
3
+ import { saveConfigImmediately } from "../../state/config-save-scheduler";
4
+ import type { OnboardingProgress } from "../../types/config";
5
+
6
+ /**
7
+ * Settings' Run Setup Again: setup from its first step, on what the user has.
8
+ * The holdings are the portfolio's own, and the desks setup made start
9
+ * ticked; finishing rebuilds setup's layouts in place and leaves the user's
10
+ * alone. `open` shows the wizard once the progress is saved; without it
11
+ * (a host that cannot run setup) there is nothing to run.
12
+ */
13
+ export function useRunSetupAgain(open: (() => void) | undefined): (() => Promise<void>) | undefined {
14
+ const dispatch = useAppDispatch();
15
+ const stateRef = useAppStateRef();
16
+ const run = useCallback(async () => {
17
+ if (!open) return;
18
+ const progress: OnboardingProgress = { version: 1, stage: "portfolio", rerun: true };
19
+ await saveConfigImmediately({ ...stateRef.current.config, onboardingComplete: false, onboardingProgress: progress });
20
+ dispatch({ type: "SET_ONBOARDING_STATE", complete: false, progress });
21
+ open();
22
+ }, [dispatch, open, stateRef]);
23
+ return open ? run : undefined;
24
+ }
@@ -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
  }