gloomberb 0.16.3 → 0.16.4

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 (451) hide show
  1. package/package.json +4 -1
  2. package/src/api-client/askg.ts +43 -0
  3. package/src/api-client/auth.ts +15 -0
  4. package/src/api-client/beneficial-owners.ts +4 -0
  5. package/src/api-client/chat.ts +19 -0
  6. package/src/api-client/company-kpis.ts +14 -2
  7. package/src/api-client/data.ts +34 -16
  8. package/src/api-client/index.ts +12 -0
  9. package/src/api-client/normalizers.ts +27 -0
  10. package/src/api-client/paths.ts +88 -23
  11. package/src/api-client/request.ts +2 -1
  12. package/src/api-client/research-activity.ts +31 -0
  13. package/src/api-client/socket.ts +39 -8
  14. package/src/api-client/types.ts +57 -1
  15. package/src/api-client/upgrade-experiment.ts +90 -0
  16. package/src/app/global-shortcuts.ts +11 -1
  17. package/src/app/keybindings/actions.ts +10 -0
  18. package/src/app/pane-runtime/index.tsx +8 -0
  19. package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
  20. package/src/app/pane-runtime/plugin-bindings.ts +65 -25
  21. package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
  22. package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
  23. package/src/app/runtime/desktop-deeplink.ts +5 -0
  24. package/src/app/star-prompt/model.ts +98 -0
  25. package/src/app/star-prompt/runtime.ts +41 -0
  26. package/src/app/star-prompt/status-line.tsx +190 -0
  27. package/src/app.tsx +13 -7
  28. package/src/architecture/core-builtin-allowlist.json +70 -0
  29. package/src/brokers/signed-in/adapter.ts +33 -2
  30. package/src/brokers/signed-in/client.ts +10 -0
  31. package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
  32. package/src/brokers/signed-in/sign-in.ts +62 -5
  33. package/src/capabilities/registry.ts +8 -1
  34. package/src/capabilities/types.ts +2 -0
  35. package/src/cli/commands/crypto-hints.ts +12 -4
  36. package/src/cli/commands/market.ts +359 -99
  37. package/src/cli/commands/options-chain.ts +172 -0
  38. package/src/cli/commands/options-leaps.ts +422 -0
  39. package/src/cli/commands/overview.ts +164 -26
  40. package/src/cli/commands/search.ts +29 -8
  41. package/src/cli/commands/system.ts +99 -19
  42. package/src/cli/commands/themes.ts +64 -0
  43. package/src/cli/commands/ticker.ts +62 -30
  44. package/src/cli/desktop-pane-shot.ts +97 -16
  45. package/src/cli/errors.ts +25 -2
  46. package/src/cli/freshness.ts +7 -4
  47. package/src/cli/fx-pair.ts +57 -0
  48. package/src/cli/help.ts +14 -1
  49. package/src/cli/index-roots.ts +51 -0
  50. package/src/cli/index.ts +9 -6
  51. package/src/cli/listing-arg.ts +211 -3
  52. package/src/cli/not-a-ticker.ts +37 -0
  53. package/src/cli/options.ts +50 -6
  54. package/src/cli/pane-functions/capabilities.ts +8 -2
  55. package/src/cli/pane-functions/catalog.ts +13 -2
  56. package/src/cli/pane-functions/data.ts +36 -5
  57. package/src/cli/pane-functions/dom.ts +21 -13
  58. package/src/cli/pane-functions/freshness.ts +205 -26
  59. package/src/cli/pane-functions/function-help.ts +67 -22
  60. package/src/cli/pane-functions/headless.ts +5 -4
  61. package/src/cli/pane-functions/index.ts +3 -0
  62. package/src/cli/pane-functions/market-session.ts +133 -0
  63. package/src/cli/pane-functions/options.ts +15 -4
  64. package/src/cli/pane-functions/report-notices.ts +63 -0
  65. package/src/cli/pane-functions/report.ts +4 -0
  66. package/src/cli/pane-functions/resolver.ts +4 -6
  67. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  68. package/src/cli/pane-functions/screenshot.ts +105 -23
  69. package/src/cli/registry.ts +2 -1
  70. package/src/cli/result.ts +50 -13
  71. package/src/cli/row-window.ts +76 -0
  72. package/src/cli/scroll-truncation.ts +34 -0
  73. package/src/components/chart/composite/composite-chart.tsx +43 -4
  74. package/src/components/chart/composite/legend.tsx +39 -6
  75. package/src/components/chart/composite/rasterizer.ts +64 -4
  76. package/src/components/chart/composite/series-cues.ts +71 -0
  77. package/src/components/chart/composite/text-renderer.ts +16 -8
  78. package/src/components/chart/composite/types.ts +3 -0
  79. package/src/components/command-bar/commands/direct/index.ts +9 -1
  80. package/src/components/command-bar/commands/plugin/items.ts +3 -1
  81. package/src/components/command-bar/commands/registry.ts +7 -0
  82. package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
  83. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  84. package/src/components/command-bar/routes/root/command-items.ts +2 -0
  85. package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
  86. package/src/components/command-bar/routes/root/results.ts +40 -10
  87. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  88. package/src/components/command-bar/routes/root/search-providers.ts +97 -14
  89. package/src/components/command-bar/surface/index.tsx +18 -4
  90. package/src/components/command-bar/theme-picker.tsx +48 -13
  91. package/src/components/command-bar/workflow/ops.ts +5 -3
  92. package/src/components/data-table/view.tsx +28 -9
  93. package/src/components/layout/floating-pane.tsx +6 -2
  94. package/src/components/layout/market-summary.ts +23 -9
  95. package/src/components/layout/pane/content.tsx +46 -29
  96. package/src/components/layout/pane/cycle-order.ts +2 -11
  97. package/src/components/layout/pane/header.tsx +2 -1
  98. package/src/components/layout/pane/title.ts +1 -1
  99. package/src/components/layout/presentation.ts +22 -0
  100. package/src/components/layout/shell/active-drag.ts +1 -1
  101. package/src/components/layout/shell/drag/index.ts +4 -0
  102. package/src/components/layout/shell/drag/live.ts +76 -5
  103. package/src/components/layout/shell/drag/runtime.ts +8 -8
  104. package/src/components/layout/shell/index.tsx +47 -6
  105. package/src/components/layout/shell/layout-state.ts +5 -11
  106. package/src/components/layout/shell/menu.ts +28 -1
  107. package/src/components/layout/shell/pane/actions.ts +2 -1
  108. package/src/components/layout/shell/pane/layers.tsx +51 -10
  109. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  110. package/src/components/layout/shell/pane/new-layout.ts +116 -0
  111. package/src/components/layout/shell/shortcuts.ts +5 -2
  112. package/src/components/layout/shell/visible-layout.ts +44 -1
  113. package/src/components/layout/status-bar.tsx +4 -1
  114. package/src/components/markdown-text.tsx +9 -5
  115. package/src/components/metric-treemap/heat-surface.tsx +27 -8
  116. package/src/components/metric-treemap/heat-tiles.ts +48 -17
  117. package/src/components/onboarding/onboarding-frame.tsx +33 -10
  118. package/src/components/onboarding/onboarding-wizard.tsx +49 -40
  119. package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
  120. package/src/components/onboarding/pro-step-layout.ts +86 -0
  121. package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
  122. package/src/components/ui/button.tsx +10 -0
  123. package/src/components/ui/data-table/index.tsx +38 -0
  124. package/src/components/ui/data-table/types.ts +8 -1
  125. package/src/components/ui/data-table/visible-range.ts +2 -0
  126. package/src/components/ui/icon.tsx +11 -2
  127. package/src/components/ui/index.ts +1 -1
  128. package/src/core/connection-health.ts +1 -1
  129. package/src/core/state/app/layout-reducer.ts +89 -3
  130. package/src/core/state/app/state.ts +8 -5
  131. package/src/core/state/app/types.ts +13 -2
  132. package/src/data/config/layout.ts +55 -21
  133. package/src/data/config/store/fold-panes.ts +149 -0
  134. package/src/data/config/store/migrations.ts +7 -0
  135. package/src/data/config/store/node.ts +27 -6
  136. package/src/data/config/store/normalize.ts +37 -3
  137. package/src/i18n/es.ts +14 -1
  138. package/src/i18n/ja.ts +14 -1
  139. package/src/i18n/ko.ts +14 -1
  140. package/src/i18n/zh-cn.ts +14 -1
  141. package/src/i18n/zh-tw.ts +14 -1
  142. package/src/layout/desks.ts +1 -2
  143. package/src/layout/pane-layout-move.ts +264 -0
  144. package/src/layout/pane-manager/hidden-panes.ts +160 -0
  145. package/src/layout/pane-manager/index.ts +1 -0
  146. package/src/market-data/coordinator/entries.ts +7 -1
  147. package/src/market-data/coordinator/quotes.ts +2 -2
  148. package/src/market-data/market/freshness.ts +118 -18
  149. package/src/market-data/market/status.ts +40 -0
  150. package/src/market-data/options-alternatives.ts +69 -0
  151. package/src/market-data/quotes/contributions.ts +19 -2
  152. package/src/market-data/quotes/metadata.ts +16 -0
  153. package/src/market-data/quotes/screener-live-quotes.ts +56 -5
  154. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  155. package/src/plugins/builtin/account-management/model.ts +1 -0
  156. package/src/plugins/builtin/account-management/navigation.ts +1 -1
  157. package/src/plugins/builtin/account-management/pane.tsx +31 -4
  158. package/src/plugins/builtin/alerts/quotes.ts +3 -0
  159. package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
  160. package/src/plugins/builtin/analytics/risk-model.ts +37 -8
  161. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
  162. package/src/plugins/builtin/backtest/pane.tsx +1 -1
  163. package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
  164. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  165. package/src/plugins/builtin/broker-manager/model.ts +8 -0
  166. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
  167. package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
  168. package/src/plugins/builtin/broker-manager/table.ts +5 -0
  169. package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
  170. package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
  171. package/src/plugins/builtin/chat/channels.ts +34 -36
  172. package/src/plugins/builtin/chat/command-bar.ts +138 -0
  173. package/src/plugins/builtin/chat/composer-commands.ts +21 -1
  174. package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
  175. package/src/plugins/builtin/chat/content/footer.tsx +5 -3
  176. package/src/plugins/builtin/chat/content/index.tsx +21 -1
  177. package/src/plugins/builtin/chat/content/mentions.ts +2 -1
  178. package/src/plugins/builtin/chat/content/user-map.ts +3 -1
  179. package/src/plugins/builtin/chat/controller/index.ts +48 -0
  180. package/src/plugins/builtin/chat/conversations.ts +256 -0
  181. package/src/plugins/builtin/chat/discord-command.ts +91 -0
  182. package/src/plugins/builtin/chat/edit-window.ts +12 -6
  183. package/src/plugins/builtin/chat/ghost-user.ts +14 -0
  184. package/src/plugins/builtin/chat/index.tsx +40 -2
  185. package/src/plugins/builtin/chat/layout.ts +2 -0
  186. package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
  187. package/src/plugins/builtin/chat/message/header.tsx +24 -15
  188. package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
  189. package/src/plugins/builtin/chat/message/render-state.ts +3 -0
  190. package/src/plugins/builtin/chat/pane-requests.ts +60 -0
  191. package/src/plugins/builtin/chat/pane-state.ts +32 -2
  192. package/src/plugins/builtin/chat/pane.tsx +8 -0
  193. package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
  194. package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
  195. package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
  196. package/src/plugins/builtin/cloud/askg/model.ts +129 -70
  197. package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
  198. package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
  199. package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
  200. package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
  201. package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
  202. package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
  203. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
  204. package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
  205. package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
  206. package/src/plugins/builtin/cloud/plugin.tsx +36 -1
  207. package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
  208. package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
  209. package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
  210. package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
  211. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
  212. package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
  213. package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
  214. package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
  215. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
  216. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
  217. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
  218. package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
  219. package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
  220. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
  221. package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
  222. package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
  223. package/src/plugins/builtin/comment-letters/client.ts +1 -3
  224. package/src/plugins/builtin/company-kpis/client.ts +23 -8
  225. package/src/plugins/builtin/company-kpis/headless.ts +3 -1
  226. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  227. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
  228. package/src/plugins/builtin/composite-plugins.ts +107 -64
  229. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
  230. package/src/plugins/builtin/credit-boards/index.tsx +21 -21
  231. package/src/plugins/builtin/credit-boards/model.ts +0 -1
  232. package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
  233. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
  234. package/src/plugins/builtin/debt-maturities/client.ts +37 -9
  235. package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
  236. package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
  237. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  238. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  239. package/src/plugins/builtin/earnings-calls/data.ts +28 -7
  240. package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
  241. package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
  242. package/src/plugins/builtin/econ/calendar-model.ts +42 -17
  243. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  244. package/src/plugins/builtin/econ/index.tsx +68 -22
  245. package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
  246. package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
  247. package/src/plugins/builtin/econ-statistics/view.ts +17 -0
  248. package/src/plugins/builtin/executives/data.ts +17 -6
  249. package/src/plugins/builtin/executives/pane.tsx +16 -7
  250. package/src/plugins/builtin/filing-events/pane.tsx +22 -5
  251. package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
  252. package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
  253. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
  254. package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
  255. package/src/plugins/builtin/gpu/headless.ts +9 -3
  256. package/src/plugins/builtin/gpu/model.ts +14 -5
  257. package/src/plugins/builtin/help/index.tsx +1 -0
  258. package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
  259. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  260. package/src/plugins/builtin/holders/headless.ts +6 -1
  261. package/src/plugins/builtin/holders/pane.tsx +3 -0
  262. package/src/plugins/builtin/insider/index.tsx +4 -4
  263. package/src/plugins/builtin/iv-history/charts.tsx +21 -7
  264. package/src/plugins/builtin/iv-history/client.ts +12 -2
  265. package/src/plugins/builtin/iv-history/format.ts +33 -2
  266. package/src/plugins/builtin/iv-history/headless.ts +22 -10
  267. package/src/plugins/builtin/iv-history/index.tsx +3 -3
  268. package/src/plugins/builtin/iv-history/model.ts +61 -13
  269. package/src/plugins/builtin/iv-history/pane.tsx +11 -3
  270. package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
  271. package/src/plugins/builtin/jobs/pane.tsx +1 -1
  272. package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
  273. package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
  274. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  275. package/src/plugins/builtin/kelly-sizer/pane.tsx +109 -39
  276. package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
  277. package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
  278. package/src/plugins/builtin/layout-manager/index.tsx +9 -1
  279. package/src/plugins/builtin/macro-day/client.ts +24 -0
  280. package/src/plugins/builtin/macro-day/headless.ts +11 -8
  281. package/src/plugins/builtin/macro-day/index.tsx +3 -0
  282. package/src/plugins/builtin/macro-day/model.ts +11 -6
  283. package/src/plugins/builtin/macro-day/pane.tsx +12 -8
  284. package/src/plugins/builtin/macro-day/release-table.ts +40 -0
  285. package/src/plugins/builtin/macro-day/releases.ts +62 -44
  286. package/src/plugins/builtin/market-heatmap/data.ts +25 -5
  287. package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
  288. package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
  289. package/src/plugins/builtin/market-heatmap/model.ts +94 -3
  290. package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
  291. package/src/plugins/builtin/market-movers/headless.ts +31 -4
  292. package/src/plugins/builtin/market-movers/index.tsx +8 -1
  293. package/src/plugins/builtin/market-movers/model.ts +71 -17
  294. package/src/plugins/builtin/market-movers/table.ts +50 -23
  295. package/src/plugins/builtin/news/headless.ts +9 -2
  296. package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
  297. package/src/plugins/builtin/openfda/client.ts +1 -1
  298. package/src/plugins/builtin/options/carry.ts +95 -0
  299. package/src/plugins/builtin/options/index.tsx +58 -5
  300. package/src/plugins/builtin/options/strike-window.ts +183 -0
  301. package/src/plugins/builtin/options/table.ts +55 -3
  302. package/src/plugins/builtin/options/types.ts +8 -1
  303. package/src/plugins/builtin/options/view.tsx +161 -12
  304. package/src/plugins/builtin/options-positioning/headless.ts +4 -2
  305. package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
  306. package/src/plugins/builtin/options-scenario/client.ts +73 -17
  307. package/src/plugins/builtin/options-scenario/headless.ts +71 -24
  308. package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
  309. package/src/plugins/builtin/options-scenario/index.tsx +9 -2
  310. package/src/plugins/builtin/options-scenario/model.ts +33 -0
  311. package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
  312. package/src/plugins/builtin/pe-band/model.ts +2 -2
  313. package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
  314. package/src/plugins/builtin/perps/equity-row.tsx +8 -5
  315. package/src/plugins/builtin/perps/model.ts +16 -5
  316. package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
  317. package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
  318. package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
  319. package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
  320. package/src/plugins/builtin/plugin-module.ts +87 -4
  321. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
  322. package/src/plugins/builtin/portfolio-list/cli/render.ts +87 -18
  323. package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
  324. package/src/plugins/builtin/portfolio-list/headless.ts +38 -7
  325. package/src/plugins/builtin/realized-vol/headless.ts +3 -0
  326. package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
  327. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
  328. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
  329. package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
  330. package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
  331. package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
  332. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
  333. package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
  334. package/src/plugins/builtin/risk-factors/data.ts +23 -9
  335. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  336. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  337. package/src/plugins/builtin/seasonality/pane.tsx +1 -1
  338. package/src/plugins/builtin/sec/index.tsx +5 -5
  339. package/src/plugins/builtin/sectors/client.ts +10 -2
  340. package/src/plugins/builtin/sectors/headless.ts +35 -6
  341. package/src/plugins/builtin/sectors/index.tsx +18 -1
  342. package/src/plugins/builtin/sectors/sector-model.ts +150 -41
  343. package/src/plugins/builtin/shared/command-tab.ts +51 -0
  344. package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
  345. package/src/plugins/builtin/shared/members-menu.tsx +4 -4
  346. package/src/plugins/builtin/shared/report-freshness.ts +50 -2
  347. package/src/plugins/builtin/shared/ticker-request.ts +25 -1
  348. package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
  349. package/src/plugins/builtin/shared/volatility/options.ts +1 -46
  350. package/src/plugins/builtin/short-interest/index.tsx +19 -4
  351. package/src/plugins/builtin/short-interest/surface.tsx +4 -4
  352. package/src/plugins/builtin/short-volume/index.tsx +1 -10
  353. package/src/plugins/builtin/supply-chain/model.ts +1 -1
  354. package/src/plugins/builtin/ticker-detail/headless.ts +43 -21
  355. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
  356. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
  357. package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
  358. package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
  359. package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
  360. package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
  361. package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
  362. package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
  363. package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
  364. package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
  365. package/src/plugins/builtin/vol-surface/client.ts +6 -2
  366. package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
  367. package/src/plugins/builtin/vol-surface/headless.ts +124 -20
  368. package/src/plugins/builtin/vol-surface/model.ts +23 -11
  369. package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
  370. package/src/plugins/builtin/vol-surface/tables.ts +124 -0
  371. package/src/plugins/builtin/world-indices/client.ts +22 -4
  372. package/src/plugins/builtin/world-indices/headless.ts +54 -27
  373. package/src/plugins/builtin/world-indices/model.ts +4 -2
  374. package/src/plugins/builtin/world-indices/table.tsx +13 -2
  375. package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
  376. package/src/plugins/builtin/world-venue-map/index.ts +1 -1
  377. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  378. package/src/plugins/builtin-editorial.ts +126 -0
  379. package/src/plugins/catalog-backend.ts +16 -9
  380. package/src/plugins/catalog-browser.ts +127 -63
  381. package/src/plugins/catalog-manifest.ts +73 -40
  382. package/src/plugins/catalog-ui.ts +26 -6
  383. package/src/plugins/extracted-plugins.ts +7 -1
  384. package/src/plugins/ownership.ts +150 -7
  385. package/src/plugins/plugin-functions.ts +54 -0
  386. package/src/plugins/plugin-off.ts +36 -0
  387. package/src/plugins/registry/host-actions.ts +16 -0
  388. package/src/plugins/registry/index.ts +84 -24
  389. package/src/plugins/registry/plugin-state.ts +25 -1
  390. package/src/plugins/runtime/context.tsx +29 -0
  391. package/src/react/input.ts +48 -1
  392. package/src/remote/app-host.tsx +5 -0
  393. package/src/remote/controller.ts +3 -53
  394. package/src/remote/matching.ts +69 -0
  395. package/src/remote/schema.ts +1 -1
  396. package/src/remote/semantic-tree.tsx +14 -0
  397. package/src/remote/types.ts +2 -0
  398. package/src/renderers/dom/desktop/icons.tsx +14 -0
  399. package/src/renderers/dom/desktop/query-bar.tsx +59 -32
  400. package/src/renderers/dom/host/box.tsx +1 -1
  401. package/src/renderers/dom/host/drag-shield.ts +17 -6
  402. package/src/renderers/dom/host/live-frame.ts +10 -2
  403. package/src/renderers/dom/input-host.tsx +6 -1
  404. package/src/renderers/dom/styles.css +13 -0
  405. package/src/renderers/electrobun/bun/desktop/backend.ts +5 -1
  406. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
  407. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -1
  408. package/src/renderers/electrobun/shared/protocol.ts +2 -0
  409. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  410. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
  411. package/src/renderers/electrobun/view/main.tsx +4 -1
  412. package/src/renderers/opentui/input-host.tsx +6 -1
  413. package/src/renderers/opentui/start.tsx +9 -0
  414. package/src/shares/portable-layout.ts +16 -6
  415. package/src/sources/gloomberb-cloud/index.ts +19 -10
  416. package/src/sources/gloomberb-cloud/normalizers.ts +4 -1
  417. package/src/sources/listing-symbols.ts +11 -5
  418. package/src/sources/provider-errors.ts +88 -3
  419. package/src/sources/provider-router/cached-routes.ts +8 -4
  420. package/src/sources/provider-router/financial-routes.ts +11 -5
  421. package/src/sources/provider-router/financials.ts +42 -15
  422. package/src/sources/provider-router/history.ts +27 -11
  423. package/src/sources/provider-router/index.ts +1 -1
  424. package/src/sources/provider-router/primary.ts +23 -5
  425. package/src/sources/sec-edgar.ts +68 -8
  426. package/src/sync/core-contributors.ts +4 -3
  427. package/src/test-support/cli-context.ts +2 -1
  428. package/src/test-support/data-provider.ts +34 -0
  429. package/src/test-support/shell-pane-keys.tsx +1 -0
  430. package/src/theme/series-colors.ts +46 -0
  431. package/src/theme/themes.ts +149 -0
  432. package/src/tickers/search/index.ts +22 -2
  433. package/src/tickers/search/ranking.ts +45 -1
  434. package/src/tickers/search/types.ts +3 -1
  435. package/src/time-series/resolve.ts +1 -14
  436. package/src/types/config.ts +61 -1
  437. package/src/types/headless.ts +11 -0
  438. package/src/types/instrument.ts +8 -0
  439. package/src/types/plugin.ts +49 -3
  440. package/src/ui/host.tsx +8 -4
  441. package/src/ui/index.tsx +2 -0
  442. package/src/utils/cli-output.ts +14 -1
  443. package/src/utils/exchanges.ts +13 -0
  444. package/src/utils/format.ts +2 -1
  445. package/src/utils/fx-market-hours.ts +34 -0
  446. package/src/utils/option-expiry.ts +126 -0
  447. package/src/utils/price-history.ts +39 -6
  448. package/src/utils/rpc-timeout-error.ts +6 -2
  449. package/src/utils/sec-eps-basis.ts +159 -30
  450. package/src/utils/sec.ts +38 -34
  451. package/src/utils/throttled-fetch.ts +51 -22
@@ -23,6 +23,21 @@ export const ONBOARDING_DESKTOP = {
23
23
  buttonHeight: "28px",
24
24
  } as const;
25
25
 
26
+ /** Cells the terminal card spends on its border and padding: what its content does not get. */
27
+ export const TERMINAL_CARD_INSET = { columns: 6, rows: 4 } as const;
28
+
29
+ /** The terminal card's size in cells: what a step asks for, clamped to what the viewport leaves. */
30
+ export function terminalCardSize(viewport: { width: number; height: number }, width: number, height: number) {
31
+ const overlayHeight = Math.max(1, viewport.height - 1);
32
+ const availableWidth = Math.max(1, viewport.width - 4);
33
+ const availableHeight = Math.max(1, overlayHeight - 1);
34
+ return {
35
+ overlayHeight,
36
+ width: Math.min(Math.max(42, Math.min(width, availableWidth)), availableWidth),
37
+ height: Math.min(Math.max(10, Math.min(height, availableHeight)), availableHeight),
38
+ };
39
+ }
40
+
26
41
  export function OnboardingModal({
27
42
  children,
28
43
  width = 66,
@@ -81,11 +96,7 @@ export function OnboardingModal({
81
96
  );
82
97
  }
83
98
 
84
- const overlayHeight = Math.max(1, viewport.height - 1);
85
- const availableWidth = Math.max(1, viewport.width - 4);
86
- const availableHeight = Math.max(1, overlayHeight - 1);
87
- const cardWidth = Math.min(Math.max(42, Math.min(width, availableWidth)), availableWidth);
88
- const cardHeight = Math.min(Math.max(10, Math.min(height, availableHeight)), availableHeight);
99
+ const { overlayHeight, width: cardWidth, height: cardHeight } = terminalCardSize(viewport, width, height);
89
100
  const top = Math.max(0, Math.floor((overlayHeight - cardHeight) / 2));
90
101
  const left = Math.max(0, Math.floor((viewport.width - cardWidth) / 2));
91
102
 
@@ -216,11 +227,14 @@ export function OnboardingTitle({
216
227
  titleSuffix,
217
228
  description,
218
229
  standalone = false,
230
+ compact = false,
219
231
  }: {
220
232
  step?: string;
221
233
  title: string;
222
234
  /** No progress header above it, so it sits at the top of the card. */
223
235
  standalone?: boolean;
236
+ /** Terminal only: no blank rows around the description, for a card with no rows to spare. */
237
+ compact?: boolean;
224
238
  /** Short qualifier after the title, e.g. how many months yearly saves. */
225
239
  titleSuffix?: string;
226
240
  description?: string;
@@ -282,8 +296,8 @@ export function OnboardingTitle({
282
296
  </Box>
283
297
  {description ? (
284
298
  <>
285
- <Box height={1} />
286
- <Box minHeight={2}>
299
+ {compact ? null : <Box height={1} />}
300
+ <Box minHeight={compact ? undefined : 2}>
287
301
  <Text fg={colors.textDim} wrapText>{description}</Text>
288
302
  </Box>
289
303
  </>
@@ -420,12 +434,15 @@ export function OnboardingActions({ children, hint }: { children: ReactNode; hin
420
434
  </Box>
421
435
  );
422
436
  }
437
+ // A button that does not fit beside the one before it wraps under it: the
438
+ // card is narrower than the row in a long language or a narrow terminal.
423
439
  return (
424
440
  <Box
425
441
  flexDirection="row"
442
+ flexWrap="wrap"
426
443
  justifyContent="flex-start"
427
444
  alignItems="center"
428
- gap={1}
445
+ columnGap={1}
429
446
  >
430
447
  {children}
431
448
  </Box>
@@ -531,19 +548,25 @@ export function OnboardingChoiceList({
531
548
  export function OnboardingFeature({
532
549
  title,
533
550
  description,
551
+ rows = 2,
534
552
  }: {
535
553
  title: string;
536
554
  description: string;
555
+ /** Terminal rows for the title and the description under it; 1 keeps the title alone. The DOM card sizes to its text. */
556
+ rows?: number;
537
557
  }) {
538
558
  const colors = useThemeColors();
539
559
  const desktop = useUiCapabilities().nativePaneChrome === true;
540
560
  if (!desktop) {
561
+ const descriptionRows = Math.max(0, rows - 1);
541
562
  return (
542
- <Box height={2} flexDirection="row" minWidth={0}>
563
+ <Box height={1 + descriptionRows} flexDirection="row" minWidth={0}>
543
564
  <Box width={2} height={1}><Text fg={colors.positive}>· </Text></Box>
544
565
  <Box flexDirection="column" flexGrow={1} minWidth={0}>
545
566
  <Box height={1} overflow="hidden"><Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{title}</Text></Box>
546
- <Box height={1} overflow="hidden"><Text fg={colors.textMuted}>{description}</Text></Box>
567
+ {descriptionRows > 0 ? (
568
+ <Box height={descriptionRows} overflow="hidden"><Text fg={colors.textMuted}>{description}</Text></Box>
569
+ ) : null}
547
570
  </Box>
548
571
  </Box>
549
572
  );
@@ -20,8 +20,8 @@ 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 "../../plugins/builtin/account-management/model";
23
- import { proStepCopy } from "../../plugins/builtin/cloud/upgrade-dialog";
24
- import { Button, SegmentedControl } from "../ui";
23
+ import { proStepCopy, proStepRealtimeTitle } from "../../plugins/builtin/cloud/upgrade-dialog";
24
+ import { Button, SegmentedControl, terminalButtonColumns } from "../ui";
25
25
  import { AccountStep, PortfolioStep } from "./onboarding-steps";
26
26
  import {
27
27
  ONBOARDING_DESKTOP,
@@ -32,8 +32,11 @@ import {
32
32
  OnboardingHeader,
33
33
  OnboardingModal,
34
34
  OnboardingTitle,
35
+ TERMINAL_CARD_INSET,
36
+ terminalCardSize,
35
37
  type OnboardingSectionId,
36
38
  } from "./onboarding-frame";
39
+ import { planProStep } from "./pro-step-layout";
37
40
  import { useOnboardingAccount } from "./wizard-account";
38
41
  import { useOnboardingBrokerSync } from "./wizard-broker-sync";
39
42
  import { useOnboardingPositions } from "./wizard-positions";
@@ -77,7 +80,7 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
77
80
  const keybindings = useKeybindings();
78
81
  const dialogOpen = useDialogState((dialog) => dialog.isOpen);
79
82
  const commandBarOpen = useAppSelector((state) => state.commandBarOpen);
80
- const { height: viewportHeight } = useViewport();
83
+ const { width: viewportWidth, height: viewportHeight } = useViewport();
81
84
  const dispatch = useAppDispatch();
82
85
  const stateRef = useAppStateRef();
83
86
  const config = useAppSelector((state) => state.config);
@@ -195,12 +198,6 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
195
198
  researchOpenedRef.current = progress.tickerSymbol;
196
199
  recordResearchActivity("ticker_saved");
197
200
  }, [stage, progress.tickerSymbol]);
198
- const {
199
- offer, pricing, billingInterval, setBillingInterval, planAccess, primaryUpgradeAction, continueFree,
200
- } = useOnboardingUpgrade({
201
- stage, progress, saveProgressInBackground, persistProgress,
202
- });
203
-
204
201
  const movePositionCursor = useCallback((delta: number) => {
205
202
  if (positionCursorIndex < 0) return;
206
203
  const next = Math.max(0, Math.min(positionCount - 1, positionCursorIndex + delta));
@@ -240,6 +237,12 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
240
237
  ? findPaneInstance(config.layout, focusedPaneId)
241
238
  : null;
242
239
  const helpFocused = focusedInstance?.paneId === "help";
240
+ const {
241
+ offer, pricing, billingInterval, setBillingInterval, planAccess, primaryUpgradeAction, continueFree, personalTickers,
242
+ } = useOnboardingUpgrade({
243
+ stage, progress, saveProgressInBackground, persistProgress,
244
+ shown: !helpFocused && !dialogOpen && !commandBarOpen,
245
+ });
243
246
 
244
247
  const goToSection = useCallback((section: OnboardingSectionId) => {
245
248
  if (finishingRef.current || isBrokerCommitting) return;
@@ -289,8 +292,9 @@ function useOnboardingWizard({ pluginRegistry, importBrokerPositions, onComplete
289
292
  chooseBroker, activeBrokerFields, brokerFieldIdx, brokerSelectIdx, setBrokerSelectIdx, brokerValues,
290
293
  setBrokerFieldValue, isBrokerSyncing, brokerSyncError, persistenceError, positionCount,
291
294
  openBrokerConnect, continueFromPositions, backPortfolio, continuePortfolio, chosenDesks, deskCursor,
292
- setDeskCursor, setChosenDesks, buildingDesks, finishDesks, account, viewportHeight, continueAccount,
295
+ setDeskCursor, setChosenDesks, buildingDesks, finishDesks, account, viewportWidth, viewportHeight, continueAccount,
293
296
  offer, planAccess, pricing, billingInterval, setBillingInterval, continueFree, primaryUpgradeAction,
297
+ personalTickers,
294
298
  };
295
299
  }
296
300
 
@@ -303,8 +307,9 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
303
307
  chooseBroker, activeBrokerFields, brokerFieldIdx, brokerSelectIdx, setBrokerSelectIdx, brokerValues,
304
308
  setBrokerFieldValue, isBrokerSyncing, brokerSyncError, persistenceError, positionCount,
305
309
  openBrokerConnect, continueFromPositions, backPortfolio, continuePortfolio, chosenDesks, deskCursor,
306
- setDeskCursor, setChosenDesks, buildingDesks, finishDesks, account, viewportHeight, continueAccount,
310
+ setDeskCursor, setChosenDesks, buildingDesks, finishDesks, account, viewportWidth, viewportHeight, continueAccount,
307
311
  offer, planAccess, pricing, billingInterval, setBillingInterval, continueFree, primaryUpgradeAction,
312
+ personalTickers,
308
313
  } = useOnboardingWizard(props);
309
314
 
310
315
  if (helpFocused) {
@@ -604,6 +609,33 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
604
609
  const monthsFree = monthsFreeYearly(pricing);
605
610
  const yearlyLabel = monthsFree > 0 ? tf("Yearly, {months} months free", { months: monthsFree }) : t("Yearly");
606
611
  const priceNote = billingInterval === "year" && monthsFree > 0 ? tf("{months} months free", { months: monthsFree }) : null;
612
+ const proNote = planAccess.hasProAccess
613
+ ? t("This account already has real-time Cloud data.")
614
+ : proCopy.note;
615
+ // Ranked: the data itself first, then what reads it.
616
+ const features = [
617
+ { title: proStepRealtimeTitle(personalTickers), description: t("Free is 15 minutes behind on quotes and 12 hours on news.") },
618
+ { title: t("MCP server"), description: t("Claude Code, Codex or Cursor call Gloom's research tools.") },
619
+ { title: t("Ask Gloom"), description: t("Answers cite filings, calls and news.") },
620
+ { title: t("Earnings calls"), description: t("Transcripts, summaries, guidance and scores.") },
621
+ { title: t("Equity Diagnostic"), description: t("Full report: red and green flags with evidence.") },
622
+ { title: t("Search, theses and flow"), description: t("Instant search with alerts, thesis monitoring, options flow, hiring and compensation data.") },
623
+ ];
624
+ // The terminal card is clamped to the viewport, so the list gets the rows
625
+ // the card leaves it. The DOM card grows with its content instead.
626
+ const card = terminalCardSize({ width: viewportWidth, height: viewportHeight }, 70, 28);
627
+ const layout = desktop ? null : planProStep({
628
+ rows: card.height - TERMINAL_CARD_INSET.rows,
629
+ columns: card.width - TERMINAL_CARD_INSET.columns,
630
+ note: proNote,
631
+ interval: !planAccess.hasProAccess,
632
+ error: persistenceError,
633
+ actions: [
634
+ ...(planAccess.hasProAccess ? [] : [terminalButtonColumns({ label: "Keep Free for now", shortcut: KEEP_FREE_KEY })]),
635
+ terminalButtonColumns({ label: primaryLabel }),
636
+ ],
637
+ descriptions: features.map((feature) => feature.description),
638
+ });
607
639
  return (
608
640
  <OnboardingModal width={70} height={28}>
609
641
  <OnboardingHeader
@@ -618,12 +650,11 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
618
650
  step={desktop ? undefined : t("GLOOM CLOUD PRO")}
619
651
  title={planAccess.hasProAccess ? t("Pro is active") : price}
620
652
  titleSuffix={!planAccess.hasProAccess && priceNote ? priceNote : undefined}
621
- description={planAccess.hasProAccess
622
- ? t("This account already has real-time Cloud data.")
623
- : proCopy.note}
653
+ description={proNote}
654
+ compact={layout?.compact}
624
655
  />
625
656
  {!planAccess.hasProAccess ? (
626
- <Box flexDirection="row" style={desktop ? { marginTop: 12 } : undefined} paddingTop={desktop ? undefined : 1}>
657
+ <Box flexDirection="row" style={desktop ? { marginTop: 12 } : undefined} paddingTop={desktop || layout?.compact ? undefined : 1}>
627
658
  <SegmentedControl
628
659
  options={[
629
660
  { label: t("Monthly"), value: "month" },
@@ -636,32 +667,10 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
636
667
  />
637
668
  </Box>
638
669
  ) : null}
639
- {/* Ranked: the data itself first, then what reads it. */}
640
670
  <Box flexDirection="column" style={desktop ? { marginTop: ONBOARDING_DESKTOP.afterHeader, gap: 10 } : undefined}>
641
- <OnboardingFeature
642
- title={t("Real-time market data")}
643
- description={t("Free is 15 minutes behind on quotes and 12 hours on news.")}
644
- />
645
- <OnboardingFeature
646
- title={t("MCP server")}
647
- description={t("Claude Code, Codex or Cursor call Gloom's research tools.")}
648
- />
649
- <OnboardingFeature
650
- title={t("Ask Gloom")}
651
- description={t("Answers cite filings, calls and news.")}
652
- />
653
- <OnboardingFeature
654
- title={t("Earnings calls")}
655
- description={t("Transcripts, summaries, guidance and scores.")}
656
- />
657
- <OnboardingFeature
658
- title={t("Equity Diagnostic")}
659
- description={t("Full report: red and green flags with evidence.")}
660
- />
661
- <OnboardingFeature
662
- title={t("Search, theses and flow")}
663
- description={t("Instant search with alerts, thesis monitoring, options flow, hiring and compensation data.")}
664
- />
671
+ {features.map((feature, index) => layout?.featureRows[index] === 0 ? null : (
672
+ <OnboardingFeature key={index} {...feature} rows={layout?.featureRows[index]} />
673
+ ))}
665
674
  </Box>
666
675
  {persistenceError ? (
667
676
  <Text fg={colors.negative} wrapText style={desktop ? { marginTop: 10 } : undefined}>
@@ -4,6 +4,7 @@ import { useThemeColors } from "../../../theme/theme-context";
4
4
  import { blendHex, priceColor } from "../../../theme/colors";
5
5
  import { t, tf } from "../../../i18n";
6
6
  import { formatMarketPrice } from "../../../market-data/market/format";
7
+ import { getRegularSessionDisplay } from "../../../market-data/market/status";
7
8
  import { formatPercentRaw } from "../../../utils/format";
8
9
  import { Button, NumberField, TextField } from "../../ui";
9
10
  import { ONBOARDING_DESKTOP } from "../onboarding-frame";
@@ -47,8 +48,10 @@ function PreviewLine({ preview, error }: Pick<OnboardingPositionsState, "preview
47
48
  return <Text fg={colors.textDim}>{tf("{query} checking...", { query: preview.query })}</Text>;
48
49
  }
49
50
  if (preview.status === "missing") return <Text fg={colors.textMuted}>{preview.message}</Text>;
50
- const price = preview.quote?.price;
51
- const change = preview.quote?.changePercent;
51
+ // The regular session's last and move, as `ticker` headlines them: after the close, that close.
52
+ const headline = getRegularSessionDisplay(preview.quote);
53
+ const price = headline?.price;
54
+ const change = headline?.changePercent;
52
55
  return (
53
56
  <Box flexDirection="row" minWidth={0} overflow="hidden">
54
57
  <Text fg={colors.text} attributes={TextAttributes.BOLD}>{preview.symbol}</Text>
@@ -0,0 +1,86 @@
1
+ import { wrapTextLines } from "../../utils/text-wrap";
2
+
3
+ function wrappedRows(text: string, width: number): number {
4
+ return Math.max(1, wrapTextLines(text, Math.max(1, width)).length);
5
+ }
6
+
7
+ /**
8
+ * Rows for each feature of a ranked list in `budget` rows: a title and its
9
+ * wrapped description (`1 + descriptionRows[i]`), a title alone (1) or nothing
10
+ * (0). As many features from the top keep their description as leave a row for
11
+ * every one below them; when not even the titles fit, the lowest ranked drop.
12
+ */
13
+ export function planProFeatureRows(budget: number, descriptionRows: readonly number[]): number[] {
14
+ const count = descriptionRows.length;
15
+ if (budget < count) return descriptionRows.map((_, index) => (index < budget ? 1 : 0));
16
+ let spare = budget - count;
17
+ const rows = descriptionRows.map(() => 1);
18
+ for (const [index, description] of descriptionRows.entries()) {
19
+ if (description > spare) break;
20
+ spare -= description;
21
+ rows[index] = 1 + description;
22
+ }
23
+ return rows;
24
+ }
25
+
26
+ /**
27
+ * Rows a row of buttons takes in `columns` cells when it wraps to the next row
28
+ * instead of running off the card: `widths` are the buttons' cells in order,
29
+ * with one cell between neighbours on a row. A button wider than the row still
30
+ * gets a row of its own.
31
+ */
32
+ export function wrappedActionRows(widths: readonly number[], columns: number): number {
33
+ let rows = 1;
34
+ let used = 0;
35
+ for (const width of widths) {
36
+ if (used > 0 && used + 1 + width > columns) {
37
+ rows += 1;
38
+ used = width;
39
+ } else {
40
+ used += (used > 0 ? 1 : 0) + width;
41
+ }
42
+ }
43
+ return rows;
44
+ }
45
+
46
+ export interface ProStepLayoutInput {
47
+ /** Rows and columns inside the card's border and padding. */
48
+ rows: number;
49
+ columns: number;
50
+ /** The line under the price. */
51
+ note: string;
52
+ /** The Monthly/Yearly control is shown. */
53
+ interval: boolean;
54
+ /** A persistence error line above the actions, or null. */
55
+ error: string | null;
56
+ /** Cells each action button takes; a row that does not fit wraps and costs a row more. */
57
+ actions: readonly number[];
58
+ /** Feature descriptions, ranked. */
59
+ descriptions: readonly string[];
60
+ }
61
+
62
+ /**
63
+ * What the terminal Pro step shows in a card of a given size: the whole list
64
+ * with the step's usual blank rows while it fits, otherwise those rows go
65
+ * (`compact`) and the list degrades as `planProFeatureRows` says. The actions
66
+ * are never part of the budget the list gets: they keep the rows they need,
67
+ * more than one when the buttons wrap.
68
+ */
69
+ export function planProStep(input: ProStepLayoutInput): { compact: boolean; featureRows: number[] } {
70
+ const { rows, columns, note, interval, error, actions, descriptions } = input;
71
+ const noteRows = wrappedRows(note, columns);
72
+ const errorRows = error ? wrappedRows(error, columns) : 0;
73
+ const actionRows = wrappedActionRows(actions, columns);
74
+ const descriptionRows = descriptions.map((description) => wrappedRows(description, columns - 2));
75
+ // Header, step and title lines, the note, the control, the error, the actions.
76
+ const around = (compact: boolean) => 1 + 2
77
+ + (compact ? noteRows : 1 + Math.max(2, noteRows))
78
+ + (interval ? (compact ? 1 : 2) : 0)
79
+ + errorRows + actionRows;
80
+
81
+ const roomy = planProFeatureRows(rows - around(false), descriptionRows);
82
+ if (roomy.every((featureRows, index) => featureRows === 1 + descriptionRows[index]!)) {
83
+ return { compact: false, featureRows: roomy };
84
+ }
85
+ return { compact: true, featureRows: planProFeatureRows(rows - around(true), descriptionRows) };
86
+ }
@@ -6,12 +6,14 @@ import { chatController } from "../../plugins/builtin/chat/controller";
6
6
  import type { CloudBillingInterval } from "../../plugins/builtin/account-management/model";
7
7
  import { loadUpgradeOffer, type UpgradeOffer } from "../../plugins/builtin/cloud/upgrade-dialog";
8
8
  import { useCloudUpgradeAction } from "../../plugins/builtin/shared/cloud-upgrade";
9
+ import { useUpgradePersonalization } from "../../plugins/builtin/cloud/upgrade-personalization";
9
10
  import { usePlanAccess } from "../../api-client/plan-access";
10
11
 
11
12
  export function useOnboardingUpgrade({
12
- stage, progress, saveProgressInBackground, persistProgress,
13
+ stage, progress, saveProgressInBackground, persistProgress, shown,
13
14
  }: {
14
15
  stage: OnboardingStage;
16
+ shown: boolean;
15
17
  progress: OnboardingProgress;
16
18
  saveProgressInBackground: (patch: Partial<OnboardingProgress> & Pick<OnboardingProgress, "stage">, baseConfig?: AppConfig | undefined) => void;
17
19
  persistProgress: (patch: Partial<OnboardingProgress> & Pick<OnboardingProgress, "stage">, baseConfig?: AppConfig | undefined) => Promise<AppConfig>;
@@ -23,6 +25,8 @@ export function useOnboardingUpgrade({
23
25
  const appActive = useAppActive();
24
26
  const planAccess = usePlanAccess();
25
27
  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");
26
30
  // The price and whether this account still has its trial, asked again for
27
31
  // whichever account reaches the step.
28
32
  const accountId = planAccess.signedIn ? apiClient.getCurrentUser()?.id ?? null : null;
@@ -78,6 +82,6 @@ export function useOnboardingUpgrade({
78
82
  }, [progress.accountStatus, saveProgressInBackground]);
79
83
 
80
84
  return {
81
- offer, pricing, billingInterval, setBillingInterval, planAccess, primaryUpgradeAction, continueFree,
85
+ offer, pricing, billingInterval, setBillingInterval, planAccess, primaryUpgradeAction, continueFree, personalTickers,
82
86
  };
83
87
  }
@@ -4,6 +4,7 @@ import { type ComponentType, type ReactNode } from "react";
4
4
  import type { ThemeColors } from "../../theme/colors";
5
5
  import { useThemeColors } from "../../theme/theme-context";
6
6
  import { t } from "../../i18n";
7
+ import { displayWidth } from "../../utils/format";
7
8
  import { useRemoteUiNode, useRemoteUiScope } from "../../remote/semantic-tree";
8
9
  import { useScopedButtonAction } from "./action-scope";
9
10
 
@@ -66,6 +67,15 @@ function resolveButtonColors(variant: ButtonVariant, active: boolean, disabled:
66
67
  }
67
68
  }
68
69
 
70
+ /**
71
+ * Terminal cells a button takes: the padded label, then its key. For a row that
72
+ * has to know whether its buttons fit before it lays them out.
73
+ */
74
+ export function terminalButtonColumns({ label, displayLabel, shortcut, compact = false }: Pick<ButtonProps, "label" | "displayLabel" | "shortcut" | "compact">): number {
75
+ const text = displayLabel ?? t(label);
76
+ return displayWidth(compact ? text : ` ${text} `) + (shortcut ? 1 + displayWidth(shortcut) : 0);
77
+ }
78
+
69
79
  export function Button({
70
80
  label: rawLabel,
71
81
  displayLabel,
@@ -27,6 +27,8 @@ export function DataTable<T, C extends DataTableColumn = DataTableColumn>(
27
27
  const renderer = useRendererHost();
28
28
  const propsRef = useRef(props);
29
29
  propsRef.current = props;
30
+ // The semantic snapshot can be taken every frame; the rows are rebuilt only when what they are drawn from changes.
31
+ const reportedRowsRef = useRef<{ source: readonly unknown[]; rows: ReturnType<typeof reportedRows> } | null>(null);
30
32
 
31
33
  useEffect(() => {
32
34
  if (!paneId || !renderer.saveTextFile) return;
@@ -90,6 +92,7 @@ export function DataTable<T, C extends DataTableColumn = DataTableColumn>(
90
92
  columns: props.columns.map((column) => ({ id: column.id, label: column.label })),
91
93
  rowCount: props.items.length,
92
94
  selectedId: firstSelectedId(props),
95
+ ...(props.reportEveryRow ? { rows: cachedReportedRows(props, reportedRowsRef) } : {}),
93
96
  }),
94
97
  });
95
98
  // Header labels read the same in every table whatever case a pane wrote
@@ -119,6 +122,41 @@ function resolveTableIndex<T, C extends DataTableColumn>(
119
122
  return resolveRemoteItemIndex(input, props.items, { id: key, key });
120
123
  }
121
124
 
125
+ /**
126
+ * Every row as a report reads it: the key, whether it is selected and each
127
+ * column's text, the same text the cells draw. Captured with the semantic
128
+ * snapshot, so a row scrolled out of the viewport still reaches the report.
129
+ */
130
+ function reportedRows<T, C extends DataTableColumn>(props: DataTableProps<T, C>) {
131
+ const normalize = (text: string) => text.replace(/\s+/g, " ").trim();
132
+ return props.items.map((item, index) => {
133
+ const selected = props.isSelected(item, index);
134
+ const section = props.renderSectionHeader?.(item, index);
135
+ const cells = section
136
+ ? [{ columnLabel: "Row", text: normalize(section.text) }]
137
+ : props.columns.map((column) => ({
138
+ columnId: column.id,
139
+ columnLabel: column.label,
140
+ text: normalize(props.renderCell(item, column, index, { selected }).text),
141
+ }));
142
+ return { key: props.getItemKey(item, index), selected, cells: cells.filter((cell) => cell.text.length > 0) };
143
+ });
144
+ }
145
+
146
+ function cachedReportedRows<T, C extends DataTableColumn>(
147
+ props: DataTableProps<T, C>,
148
+ cache: { current: { source: readonly unknown[]; rows: ReturnType<typeof reportedRows> } | null },
149
+ ) {
150
+ const source = [props.items, props.columns, props.renderCell, props.isSelected, props.renderSectionHeader];
151
+ const cached = cache.current;
152
+ if (cached && cached.source.length === source.length && cached.source.every((value, index) => value === source[index])) {
153
+ return cached.rows;
154
+ }
155
+ const rows = reportedRows(props);
156
+ cache.current = { source, rows };
157
+ return rows;
158
+ }
159
+
122
160
  function firstSelectedId<T, C extends DataTableColumn>(props: DataTableProps<T, C>): string | null {
123
161
  const index = props.items.findIndex((item, itemIndex) => props.isSelected(item, itemIndex));
124
162
  const item = props.items[index];
@@ -60,7 +60,8 @@ export interface DataTableSectionHeader {
60
60
  expanded?: boolean;
61
61
  }
62
62
 
63
- export type DataTableScrollAlign = "nearest" | "center";
63
+ /** Where `scrollToIndex` puts its row: the nearest edge, the middle, or the top of the viewport. */
64
+ export type DataTableScrollAlign = "nearest" | "center" | "start";
64
65
 
65
66
  interface DataTableRowState {
66
67
  selected: boolean;
@@ -146,6 +147,12 @@ export interface DataTableProps<
146
147
  freezeFirstColumn?: boolean;
147
148
  /** Append export-only source records after the displayed table. */
148
149
  getExportMetadata?: () => readonly (readonly unknown[])[];
150
+ /**
151
+ * A rendered-view report (`gloomberb fn`) reads every row, not only those
152
+ * the viewport shows. For a bounded table a reader needs whole, such as an
153
+ * option chain's strike window; leave it off for long or paged lists.
154
+ */
155
+ reportEveryRow?: boolean;
149
156
  scrollToIndex?: number | null;
150
157
  scrollToIndexAlign?: DataTableScrollAlign;
151
158
  scrollToIndexVersion?: number;
@@ -36,6 +36,8 @@ export function resolveDataTableScrollTop(
36
36
  let nextTop = currentTop;
37
37
  if (align === "center") {
38
38
  nextTop = targetIndex - Math.floor(visibleHeight / 2);
39
+ } else if (align === "start") {
40
+ nextTop = targetIndex;
39
41
  } else if (targetIndex < currentTop) {
40
42
  nextTop = targetIndex;
41
43
  } else if (targetIndex >= currentTop + visibleHeight) {
@@ -27,7 +27,9 @@ export type IconName =
27
27
  | "sort-up"
28
28
  | "sort-down"
29
29
  | "grip"
30
- | "image";
30
+ | "image"
31
+ | "thumbs-up"
32
+ | "thumbs-down";
31
33
 
32
34
  /** Terminal glyph for each icon; the desktop host draws SVG. */
33
35
  export const ICON_GLYPHS: Record<IconName, string> = {
@@ -54,6 +56,10 @@ export const ICON_GLYPHS: Record<IconName, string> = {
54
56
  "sort-down": "▼",
55
57
  grip: "::",
56
58
  image: "▣",
59
+ // Votes rather than emoji: an emoji keeps its own colours, so it could not
60
+ // show which thumb is chosen.
61
+ "thumbs-up": "+1",
62
+ "thumbs-down": "-1",
57
63
  };
58
64
 
59
65
  export interface IconProps {
@@ -160,7 +166,10 @@ export function IconButton({
160
166
  if (!disabled) onPress?.(event);
161
167
  }}
162
168
  >
163
- <Text fg={disabled ? colors.textMuted : color ?? colors.textDim} selectable={false}>{` ${ICON_GLYPHS[icon]} `}</Text>
169
+ {/* Pressed takes the colour the desktop and the terminal pane header give an active toggle. */}
170
+ <Text fg={disabled ? colors.textMuted : color ?? (pressed ? colors.warning : colors.textDim)} selectable={false}>
171
+ {` ${ICON_GLYPHS[icon]} `}
172
+ </Text>
164
173
  </Box>
165
174
  );
166
175
  }
@@ -5,7 +5,7 @@ export type { ListViewItem, ListViewProps, ListRowState } from "./list-view";
5
5
  export { DataTable } from "./data-table";
6
6
  export type { DataTableCell, DataTableColumn, DataTableProps, DataTableVisibleRange } from "./data-table";
7
7
 
8
- export { Button } from "./button";
8
+ export { Button, terminalButtonColumns } from "./button";
9
9
  export { Icon, IconButton, ICON_GLYPHS } from "./icon";
10
10
  export type { IconButtonPressEvent, IconButtonProps, IconName, IconProps } from "./icon";
11
11
  export { ActionRow } from "./action-row";
@@ -280,7 +280,7 @@ export function registerGloomCloudConnectionSources(health: ConnectionHealthRegi
280
280
  id: GLOOM_CLOUD_FRED_CONNECTION_ID,
281
281
  name: "Gloom / FRED",
282
282
  kind: "api",
283
- ownerId: "macro",
283
+ ownerId: "rates-macro",
284
284
  priority: 2,
285
285
  detail: "api.gloom.sh/cloud/econ/series",
286
286
  }),