gloomberb 0.19.2 → 0.19.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/README.md +7 -1
  2. package/package.json +1 -1
  3. package/patches/electrobun@1.18.1.patch +108 -1
  4. package/src/account/upgrade-dialog.tsx +4 -23
  5. package/src/api-client/coalesce.ts +118 -0
  6. package/src/api-client/credit-boards.ts +13 -0
  7. package/src/api-client/crypto-etf-flows.ts +6 -0
  8. package/src/api-client/crypto-markets.ts +40 -0
  9. package/src/api-client/data.ts +201 -14
  10. package/src/api-client/equity-screener.ts +10 -0
  11. package/src/api-client/errors.ts +20 -0
  12. package/src/api-client/gpu.ts +29 -0
  13. package/src/api-client/index.ts +5 -2
  14. package/src/api-client/plan-access.ts +10 -1
  15. package/src/api-client/plan-rules.ts +35 -0
  16. package/src/api-client/research-activity.ts +1 -32
  17. package/src/api-client/types.ts +21 -1
  18. package/src/app/attention-notice.tsx +94 -0
  19. package/src/app.tsx +23 -1
  20. package/src/architecture/core-builtin-allowlist.json +2 -1
  21. package/src/capabilities/factories.ts +1 -0
  22. package/src/cli/ascii-output.ts +42 -0
  23. package/src/cli/base-converter.ts +10 -2
  24. package/src/cli/command-options.ts +16 -1
  25. package/src/cli/commands/crypto-hints.ts +23 -4
  26. package/src/cli/commands/market.ts +92 -23
  27. package/src/cli/commands/news-search.ts +24 -5
  28. package/src/cli/commands/overview.ts +9 -4
  29. package/src/cli/commands/search.ts +71 -12
  30. package/src/cli/commands/system.ts +22 -30
  31. package/src/cli/commands/ticker.ts +49 -9
  32. package/src/cli/commands/version.ts +33 -0
  33. package/src/cli/context.ts +5 -9
  34. package/src/cli/data-dir.ts +7 -0
  35. package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
  36. package/src/cli/desktop-pane-shot-browser.ts +320 -0
  37. package/src/cli/desktop-pane-shot-routes.ts +26 -1
  38. package/src/cli/desktop-pane-shot.ts +196 -256
  39. package/src/cli/early-command.ts +42 -0
  40. package/src/cli/earnings-calendar.ts +9 -0
  41. package/src/cli/entry.ts +19 -13
  42. package/src/cli/errors.ts +12 -2
  43. package/src/cli/help.ts +1 -1
  44. package/src/cli/history-export.ts +22 -2
  45. package/src/cli/history-rows.ts +154 -4
  46. package/src/cli/index.ts +18 -1
  47. package/src/cli/known-gaps.ts +40 -5
  48. package/src/cli/listing-arg.ts +46 -9
  49. package/src/cli/options.ts +1 -1
  50. package/src/cli/output-file.ts +60 -13
  51. package/src/cli/pane-functions/catalog.ts +14 -3
  52. package/src/cli/pane-functions/data.ts +1 -1
  53. package/src/cli/pane-functions/dom.ts +45 -0
  54. package/src/cli/pane-functions/freshness.ts +46 -9
  55. package/src/cli/pane-functions/function-help.ts +17 -9
  56. package/src/cli/pane-functions/headless.ts +22 -14
  57. package/src/cli/pane-functions/index.ts +6 -2
  58. package/src/cli/pane-functions/options.ts +6 -2
  59. package/src/cli/pane-functions/resolver.ts +27 -5
  60. package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
  61. package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
  62. package/src/cli/pane-functions/screenshot-http.ts +70 -0
  63. package/src/cli/pane-functions/screenshot-render.ts +19 -5
  64. package/src/cli/quote-answer.ts +20 -0
  65. package/src/cli/registry.ts +41 -13
  66. package/src/cli/result.ts +12 -6
  67. package/src/cli/ticker-resolution.ts +2 -1
  68. package/src/cli/types.ts +2 -0
  69. package/src/components/chart/composite/format.ts +3 -0
  70. package/src/components/chart-table/header.tsx +1 -1
  71. package/src/components/chart-table/layout.ts +28 -11
  72. package/src/components/command-bar/action-runtime.ts +4 -0
  73. package/src/components/command-bar/commands/direct/index.ts +18 -33
  74. package/src/components/command-bar/commands/direct/runtime.ts +5 -0
  75. package/src/components/command-bar/commands/registry.ts +10 -1
  76. package/src/components/command-bar/routes/root/command-items.ts +9 -4
  77. package/src/components/command-bar/routes/root/runtime.ts +24 -1
  78. package/src/components/command-bar/surface/index.tsx +9 -3
  79. package/src/components/company-logo.tsx +2 -1
  80. package/src/components/layout/header.tsx +8 -3
  81. package/src/components/layout/market-summary.ts +25 -7
  82. package/src/components/layout/pane/footer/index.tsx +7 -2
  83. package/src/components/layout/pane/footer/model.ts +40 -5
  84. package/src/components/market-board/index.tsx +3 -2
  85. package/src/components/onboarding/desks-step.tsx +12 -4
  86. package/src/components/onboarding/first-run-workspace.ts +81 -4
  87. package/src/components/onboarding/onboarding-frame.tsx +2 -1
  88. package/src/components/onboarding/onboarding-keyboard.ts +8 -5
  89. package/src/components/onboarding/onboarding-wizard.tsx +54 -23
  90. package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
  91. package/src/components/onboarding/run-setup-again.ts +24 -0
  92. package/src/components/onboarding/use-onboarding-progress.ts +33 -15
  93. package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
  94. package/src/components/onboarding/wizard-model.ts +57 -3
  95. package/src/components/onboarding/wizard-positions.ts +1 -1
  96. package/src/components/ui/delayed-mark.tsx +56 -0
  97. package/src/components/ui/external-link.tsx +3 -6
  98. package/src/components/ui/icon.tsx +8 -1
  99. package/src/components/ui/logo-mark.tsx +8 -5
  100. package/src/components/ui/stat-grid.tsx +263 -19
  101. package/src/data/config/store/node.ts +11 -5
  102. package/src/data/config/store/normalize.ts +16 -3
  103. package/src/data/profile-permissions.ts +47 -0
  104. package/src/data/resource-store.ts +34 -31
  105. package/src/data/sqlite/access.ts +19 -8
  106. package/src/data/sqlite/checkpointer.ts +95 -8
  107. package/src/data/sqlite/database.ts +40 -7
  108. package/src/docs/snapshot.json +191 -78
  109. package/src/i18n/es.ts +18 -2
  110. package/src/i18n/ja.ts +18 -2
  111. package/src/i18n/ko.ts +18 -2
  112. package/src/i18n/zh-cn.ts +18 -2
  113. package/src/i18n/zh-tw.ts +18 -2
  114. package/src/layout/desk-keys.ts +4 -1
  115. package/src/layout/desks.ts +38 -5
  116. package/src/layout-marketplace/gallery-desktop.tsx +45 -16
  117. package/src/market-data/batched-reads.ts +16 -0
  118. package/src/market-data/coordinator/auxiliary.ts +4 -2
  119. package/src/market-data/coordinator/index.ts +11 -2
  120. package/src/market-data/futures-board.ts +1 -1
  121. package/src/market-data/futures-contract.ts +3 -1
  122. package/src/market-data/market/index-level.ts +6 -4
  123. package/src/market-data/published-apac-sessions.ts +6 -0
  124. package/src/news/aggregator.ts +7 -4
  125. package/src/news/news-model.ts +11 -0
  126. package/src/news/story-id.ts +22 -0
  127. package/src/news/ticker-symbols.ts +17 -1
  128. package/src/notifications/app-notifier.ts +35 -14
  129. package/src/plugins/builtin/account-management/form-components.tsx +1 -1
  130. package/src/plugins/builtin/account-management/index.tsx +2 -3
  131. package/src/plugins/builtin/account-management/model.ts +1 -1
  132. package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
  133. package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
  134. package/src/plugins/builtin/alerts/events.ts +10 -2
  135. package/src/plugins/builtin/alerts/index.tsx +5 -5
  136. package/src/plugins/builtin/alerts/research-builder.ts +5 -1
  137. package/src/plugins/builtin/alerts/research-rules.ts +18 -0
  138. package/src/plugins/builtin/alerts/targets.ts +4 -1
  139. package/src/plugins/builtin/attention/headless.ts +1 -1
  140. package/src/plugins/builtin/attention/index.tsx +2 -4
  141. package/src/plugins/builtin/attention/listing.ts +1 -1
  142. package/src/plugins/builtin/attention/series.ts +1 -1
  143. package/src/plugins/builtin/awards/index.tsx +1 -2
  144. package/src/plugins/builtin/backtest/client.ts +1 -0
  145. package/src/plugins/builtin/backtest/index.tsx +1 -2
  146. package/src/plugins/builtin/backtest/rules.ts +3 -1
  147. package/src/plugins/builtin/backtest/window.ts +8 -7
  148. package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
  149. package/src/plugins/builtin/broker-manager/index.tsx +1 -1
  150. package/src/plugins/builtin/buildout/cells.tsx +5 -0
  151. package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
  152. package/src/plugins/builtin/buildout/company/id.ts +2 -0
  153. package/src/plugins/builtin/buildout/company/model.ts +247 -0
  154. package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
  155. package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
  156. package/src/plugins/builtin/buildout/facilities.ts +309 -0
  157. package/src/plugins/builtin/buildout/format.ts +9 -0
  158. package/src/plugins/builtin/buildout/index.tsx +51 -5
  159. package/src/plugins/builtin/buildout/model/index.ts +62 -10
  160. package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
  161. package/src/plugins/builtin/buildout/model/types.ts +11 -2
  162. package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
  163. package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
  164. package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
  165. package/src/plugins/builtin/buildout/table-model.ts +26 -2
  166. package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
  167. package/src/plugins/builtin/cds/headless.ts +10 -2
  168. package/src/plugins/builtin/cds/model.ts +7 -3
  169. package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
  170. package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
  171. package/src/plugins/builtin/changelog/index.tsx +3 -2
  172. package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -2
  173. package/src/plugins/builtin/chart-composer/headless.ts +88 -21
  174. package/src/plugins/builtin/chart-composer/index.tsx +2 -3
  175. package/src/plugins/builtin/chart-composer/pane.tsx +26 -2
  176. package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
  177. package/src/plugins/builtin/chart-composer/studies.ts +1 -1
  178. package/src/plugins/builtin/chat/controller/index.ts +1 -1
  179. package/src/plugins/builtin/chat/controller/messages.ts +15 -4
  180. package/src/plugins/builtin/chat/ghost-user.ts +8 -0
  181. package/src/plugins/builtin/chat/index.tsx +5 -5
  182. package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
  183. package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
  184. package/src/plugins/builtin/cloud/index.tsx +1 -2
  185. package/src/plugins/builtin/cloud/plugin.tsx +6 -4
  186. package/src/plugins/builtin/cloud/team/module.tsx +1 -1
  187. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
  188. package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
  189. package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
  190. package/src/plugins/builtin/comment-letters/index.tsx +1 -2
  191. package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
  192. package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
  193. package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
  194. package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
  195. package/src/plugins/builtin/congress-trades/headless.ts +2 -0
  196. package/src/plugins/builtin/congress-trades/model.ts +92 -64
  197. package/src/plugins/builtin/congress-trades/table.tsx +14 -2
  198. package/src/plugins/builtin/connections/index.ts +2 -3
  199. package/src/plugins/builtin/cot/index.tsx +1 -2
  200. package/src/plugins/builtin/cpi/index.tsx +1 -2
  201. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
  202. package/src/plugins/builtin/credit-boards/client.ts +7 -2
  203. package/src/plugins/builtin/credit-boards/headless.ts +25 -12
  204. package/src/plugins/builtin/credit-boards/model.ts +29 -0
  205. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
  206. package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
  207. package/src/plugins/builtin/crypto-board/headless.ts +4 -3
  208. package/src/plugins/builtin/crypto-board/index.tsx +4 -2
  209. package/src/plugins/builtin/crypto-board/series.ts +2 -0
  210. package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
  211. package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
  212. package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
  213. package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
  214. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
  215. package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
  216. package/src/plugins/builtin/debt-maturities/model.ts +17 -1
  217. package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
  218. package/src/plugins/builtin/debug/index.tsx +1 -1
  219. package/src/plugins/builtin/dividend-yield/client.ts +32 -2
  220. package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
  221. package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
  222. package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
  223. package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
  224. package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
  225. package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
  226. package/src/plugins/builtin/dividend-yield/view.ts +21 -0
  227. package/src/plugins/builtin/docs/index.tsx +2 -3
  228. package/src/plugins/builtin/doe/headless.ts +16 -3
  229. package/src/plugins/builtin/doe/index.tsx +5 -4
  230. package/src/plugins/builtin/earnings/board.tsx +2 -1
  231. package/src/plugins/builtin/earnings/history.tsx +2 -1
  232. package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
  233. package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
  234. package/src/plugins/builtin/econ/calendar-model.ts +29 -6
  235. package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
  236. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  237. package/src/plugins/builtin/equity-screener/headless.ts +1 -0
  238. package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
  239. package/src/plugins/builtin/equity-screener/index.tsx +1 -2
  240. package/src/plugins/builtin/equity-screener/model.ts +28 -4
  241. package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
  242. package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
  243. package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
  244. package/src/plugins/builtin/exposure/holdings.ts +35 -13
  245. package/src/plugins/builtin/exposure/index.tsx +1 -2
  246. package/src/plugins/builtin/fear-greed/cli.ts +44 -0
  247. package/src/plugins/builtin/fear-greed/index.tsx +5 -53
  248. package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
  249. package/src/plugins/builtin/fixed-income/index.tsx +1 -2
  250. package/src/plugins/builtin/futures/board-reads.ts +25 -0
  251. package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
  252. package/src/plugins/builtin/futures/index.tsx +15 -0
  253. package/src/plugins/builtin/futures/model.ts +14 -0
  254. package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
  255. package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
  256. package/src/plugins/builtin/futures-curve/headless.ts +6 -1
  257. package/src/plugins/builtin/futures-curve/index.tsx +1 -2
  258. package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
  259. package/src/plugins/builtin/gpu/board.tsx +51 -12
  260. package/src/plugins/builtin/gpu/client.ts +15 -2
  261. package/src/plugins/builtin/gpu/equities.tsx +5 -2
  262. package/src/plugins/builtin/gpu/headless.ts +14 -2
  263. package/src/plugins/builtin/gpu/index.tsx +1 -2
  264. package/src/plugins/builtin/gpu/model.ts +20 -2
  265. package/src/plugins/builtin/gpu/pane.tsx +1 -1
  266. package/src/plugins/builtin/help/index.tsx +1 -1
  267. package/src/plugins/builtin/holders/index-holders.ts +24 -12
  268. package/src/plugins/builtin/holders/index.tsx +2 -3
  269. package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
  270. package/src/plugins/builtin/iv-history/index.tsx +1 -2
  271. package/src/plugins/builtin/jodi/index.tsx +1 -2
  272. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
  273. package/src/plugins/builtin/layout-manager/index.tsx +9 -8
  274. package/src/plugins/builtin/macro-day/pane.tsx +4 -2
  275. package/src/plugins/builtin/market-halts/index.tsx +1 -2
  276. package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
  277. package/src/plugins/builtin/market-movers/index.tsx +7 -1
  278. package/src/plugins/builtin/market-movers/session-body.tsx +4 -1
  279. package/src/plugins/builtin/market-valuation/index.tsx +1 -2
  280. package/src/plugins/builtin/members/index.tsx +1 -2
  281. package/src/plugins/builtin/money-markets/index.tsx +1 -2
  282. package/src/plugins/builtin/money-markets/pane.tsx +2 -1
  283. package/src/plugins/builtin/news/headless.ts +7 -0
  284. package/src/plugins/builtin/news/wire/index.tsx +1 -1
  285. package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
  286. package/src/plugins/builtin/notes/index.tsx +3 -6
  287. package/src/plugins/builtin/openfda/index.tsx +1 -2
  288. package/src/plugins/builtin/options/view.tsx +6 -5
  289. package/src/plugins/builtin/options-calculator/index.tsx +1 -2
  290. package/src/plugins/builtin/options-positioning/index.tsx +1 -2
  291. package/src/plugins/builtin/options-scenario/index.tsx +2 -4
  292. package/src/plugins/builtin/perps/index.tsx +1 -2
  293. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
  294. package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
  295. package/src/plugins/builtin/plugin-module.ts +16 -2
  296. package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
  297. package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
  298. package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
  299. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
  300. package/src/plugins/builtin/power/index.tsx +1 -2
  301. package/src/plugins/builtin/rate-path/index.tsx +1 -2
  302. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  303. package/src/plugins/builtin/realized-vol/index.tsx +1 -2
  304. package/src/plugins/builtin/realized-vol/model.ts +16 -2
  305. package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
  306. package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
  307. package/src/plugins/builtin/research/analyst-headless.ts +33 -6
  308. package/src/plugins/builtin/research/analyst-model.ts +37 -2
  309. package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
  310. package/src/plugins/builtin/research/index.tsx +5 -8
  311. package/src/plugins/builtin/research-search/index.tsx +1 -2
  312. package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
  313. package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
  314. package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
  315. package/src/plugins/builtin/roadmap/index.tsx +2 -3
  316. package/src/plugins/builtin/roadmap/pane.tsx +49 -2
  317. package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
  318. package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
  319. package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
  320. package/src/plugins/builtin/sec/filing-content.ts +38 -6
  321. package/src/plugins/builtin/sec/headless.ts +5 -1
  322. package/src/plugins/builtin/sec/index.tsx +4 -2
  323. package/src/plugins/builtin/sec/model.ts +2 -0
  324. package/src/plugins/builtin/sectors/index.tsx +11 -5
  325. package/src/plugins/builtin/series-inventory.ts +2 -0
  326. package/src/plugins/builtin/shared/cloud-resource.ts +60 -0
  327. package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
  328. package/src/plugins/builtin/shared/forming-bars.ts +29 -0
  329. package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
  330. package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
  331. package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
  332. package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
  333. package/src/plugins/builtin/short-volume/pane.tsx +2 -1
  334. package/src/plugins/builtin/social-mentions/index.tsx +1 -2
  335. package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
  336. package/src/plugins/builtin/supply-chain/client.ts +22 -1
  337. package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
  338. package/src/plugins/builtin/supply-chain/index.tsx +1 -2
  339. package/src/plugins/builtin/thirteenf/api.ts +116 -13
  340. package/src/plugins/builtin/thirteenf/data.ts +132 -30
  341. package/src/plugins/builtin/thirteenf/index.tsx +2 -4
  342. package/src/plugins/builtin/thirteenf/model.ts +2 -0
  343. package/src/plugins/builtin/thirteenf/pane.tsx +20 -11
  344. package/src/plugins/builtin/thirteenf/types.ts +12 -0
  345. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +17 -4
  346. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
  347. package/src/plugins/builtin/ticker-detail/headless.ts +11 -3
  348. package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
  349. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
  350. package/src/plugins/builtin/time-sales/index.tsx +1 -2
  351. package/src/plugins/builtin/time-sales/pane.tsx +3 -1
  352. package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
  353. package/src/plugins/builtin/trace-bonds/model.ts +44 -10
  354. package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
  355. package/src/plugins/builtin/treasury-auctions/pane.tsx +2 -2
  356. package/src/plugins/builtin/vol-surface/client.ts +18 -3
  357. package/src/plugins/builtin/vol-surface/index.tsx +1 -2
  358. package/src/plugins/builtin/volatility/index.tsx +1 -2
  359. package/src/plugins/builtin/world-indices/footer.ts +6 -1
  360. package/src/plugins/builtin/world-venue-map/client.ts +10 -1
  361. package/src/plugins/builtin/world-venue-map/index.ts +6 -2
  362. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
  363. package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
  364. package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
  365. package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
  366. package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
  367. package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
  368. package/src/plugins/builtin/yield-curve/curves.ts +16 -2
  369. package/src/plugins/builtin/yield-curve/headless.ts +3 -3
  370. package/src/plugins/bundle.ts +52 -2
  371. package/src/plugins/catalog-browser.ts +2 -2
  372. package/src/plugins/catalog-cli.ts +20 -0
  373. package/src/plugins/host-link.ts +13 -1
  374. package/src/plugins/installer.ts +15 -4
  375. package/src/plugins/loader.ts +43 -11
  376. package/src/plugins/peer-imports.ts +201 -0
  377. package/src/plugins/peer-retry.ts +26 -29
  378. package/src/plugins/plugin-process.ts +128 -0
  379. package/src/plugins/redirection-guard.ts +45 -0
  380. package/src/remote/endpoint.ts +16 -3
  381. package/src/renderers/browser/main.tsx +3 -2
  382. package/src/renderers/cloudflare/http-proxy.ts +7 -123
  383. package/src/renderers/dom/desktop/icons.tsx +2 -0
  384. package/src/renderers/dom/host/image-surface.tsx +46 -21
  385. package/src/renderers/dom/styles.css +7 -0
  386. package/src/renderers/dom/toast-host.tsx +2 -0
  387. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  388. package/src/renderers/electrobun/bun/desktop/initialization.ts +3 -2
  389. package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
  390. package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
  391. package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
  392. package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
  393. package/src/renderers/electrobun/bun/window/create.ts +4 -2
  394. package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
  395. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
  396. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
  397. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
  398. package/src/renderers/electrobun/view/main.tsx +7 -2
  399. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
  400. package/src/renderers/opentui/browser-open.ts +10 -4
  401. package/src/renderers/opentui/image/surface.tsx +1 -1
  402. package/src/renderers/opentui/start.tsx +12 -6
  403. package/src/renderers/opentui/toast-host.tsx +2 -1
  404. package/src/series/registry.ts +18 -0
  405. package/src/series/standalone.ts +10 -1
  406. package/src/series/types.ts +7 -0
  407. package/src/series/validate.ts +11 -2
  408. package/src/sources/gloomberb-cloud/index.ts +28 -5
  409. package/src/sources/gloomberb-cloud/news.ts +24 -3
  410. package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
  411. package/src/sources/provider-errors.ts +26 -3
  412. package/src/sources/provider-router/history.ts +5 -0
  413. package/src/sources/provider-router/index.ts +13 -0
  414. package/src/sync/core-contributors.ts +32 -0
  415. package/src/telemetry/attention-counts.ts +64 -6
  416. package/src/telemetry/attention-setting.ts +33 -0
  417. package/src/telemetry/crash-reports-node.ts +3 -1
  418. package/src/telemetry/stall-clocks.ts +93 -0
  419. package/src/telemetry/stall-reports-node.ts +12 -7
  420. package/src/telemetry/stall-reports.ts +34 -5
  421. package/src/test-support/preload.ts +9 -0
  422. package/src/tickers/company-name.ts +19 -0
  423. package/src/tickers/listing-fate.ts +14 -3
  424. package/src/tickers/saved-instrument.ts +17 -0
  425. package/src/tickers/search/home-listing.ts +13 -2
  426. package/src/tickers/search/index.ts +4 -1
  427. package/src/tickers/search/types.ts +6 -1
  428. package/src/time-series/bar-aggregation.ts +7 -1
  429. package/src/time-series/base-series.ts +3 -3
  430. package/src/time-series/chart-bounds.ts +1 -1
  431. package/src/time-series/chart-data.ts +5 -2
  432. package/src/time-series/chart-seed.ts +1 -1
  433. package/src/time-series/coin-session-close.ts +211 -0
  434. package/src/time-series/live-bars.ts +13 -2
  435. package/src/time-series/live-quotes.ts +1 -1
  436. package/src/time-series/official-close.ts +159 -0
  437. package/src/time-series/price-comparison.ts +55 -6
  438. package/src/time-series/resolve.ts +12 -3
  439. package/src/time-series/series-presentation.ts +1 -1
  440. package/src/time-series/studies/formula.ts +205 -0
  441. package/src/time-series/studies/indicators.ts +299 -0
  442. package/src/time-series/studies/leverage.ts +75 -0
  443. package/src/time-series/studies/pair.ts +138 -0
  444. package/src/time-series/studies/resolve.ts +333 -0
  445. package/src/time-series/studies/shared.ts +141 -0
  446. package/src/time-series/studies/units.ts +243 -0
  447. package/src/time-series/studies/vwap.ts +274 -0
  448. package/src/types/config.ts +18 -2
  449. package/src/types/data-provider.ts +23 -0
  450. package/src/types/financials.ts +22 -0
  451. package/src/types/headless.ts +42 -3
  452. package/src/types/plugin.ts +32 -3
  453. package/src/ui/host.tsx +11 -0
  454. package/src/ui/toast.tsx +26 -1
  455. package/src/utils/ascii-text.ts +140 -0
  456. package/src/utils/cli-output.ts +91 -18
  457. package/src/utils/concurrency.ts +24 -0
  458. package/src/utils/exchanges.ts +6 -0
  459. package/src/utils/external-url.ts +20 -7
  460. package/src/utils/futures-generic.ts +4 -4
  461. package/src/utils/http-proxy-upstream.ts +124 -0
  462. package/src/utils/main-thread-stall.ts +75 -3
  463. package/src/utils/perf-marks.ts +36 -2
  464. package/src/utils/private-files.ts +141 -0
  465. package/src/utils/public-address.ts +99 -0
  466. package/src/account/upgrade-personalization.ts +0 -57
  467. package/src/account/upgrade-tickers.ts +0 -124
  468. package/src/api-client/upgrade-experiment.ts +0 -91
  469. package/src/plugins/builtin/cloud/browser.tsx +0 -3
  470. package/src/renderers/electrobun/launcher/preload.ts +0 -13
  471. package/src/renderers/electrobun/launcher/windows-emulation.ts +0 -50
  472. package/src/time-series/studies.ts +0 -1628
@@ -3,7 +3,6 @@ import { commandRequestParams } from "../shared/command-tab";
3
3
  import { ipoCalendarCache } from "./client";
4
4
  import { ipoCalendarHeadless } from "./headless";
5
5
  import { IPO_CALENDAR_PANE_ID } from "./model";
6
- import { IpoCalendarPane } from "./pane";
7
6
 
8
7
  /**
9
8
  * `IPO japan` opens the calendar on that search, and so does a later `IPO europe`: one pane, the open one
@@ -44,7 +43,7 @@ export const ipoCalendarPlugin: GloomPlugin = {
44
43
  id: IPO_CALENDAR_PANE_ID,
45
44
  name: "IPO Calendar",
46
45
  icon: "I",
47
- component: IpoCalendarPane,
46
+ get component(): typeof import("./pane").IpoCalendarPane { const { IpoCalendarPane } = require("./pane"); return IpoCalendarPane; },
48
47
  defaultPosition: "right",
49
48
  defaultMode: "floating",
50
49
  defaultFloatingSize: { width: 110, height: 28 },
@@ -3,14 +3,13 @@ import type { PluginModule } from "../plugin-module";
3
3
  import { IV_SERIES } from "./series";
4
4
  import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
5
5
  import { ivHistoryHeadless, ivScreenHeadless } from "./headless";
6
- import { IvHistoryPane } from "./pane";
7
6
  import { IvScreenPane, VCA_SCOPE_OPTIONS } from "./screen-pane";
8
7
 
9
8
 
10
9
  export const ivHistoryModule: PluginModule = {
11
10
  series: IV_SERIES,
12
11
  panes: [{
13
- id: "iv-history", name: "Implied Volatility History", icon: "V", component: IvHistoryPane,
12
+ id: "iv-history", name: "Implied Volatility History", icon: "V", get component(): typeof import("./pane").IvHistoryPane { const { IvHistoryPane } = require("./pane"); return IvHistoryPane; },
14
13
  defaultPosition: "right", tickerFollower: true, defaultMode: "floating", defaultFloatingSize: { width: 118, height: 36 },
15
14
  tableExport: true,
16
15
  settings: { title: "Implied Volatility History", fields: [
@@ -1,7 +1,6 @@
1
1
  import type { PluginModule } from "../plugin-module";
2
2
  import { jodiHeadless } from "./headless";
3
3
  import { JODI_PANE_ID } from "./model";
4
- import { JodiPane } from "./pane";
5
4
 
6
5
  export const jodiModule: PluginModule = {
7
6
  panes: [
@@ -9,7 +8,7 @@ export const jodiModule: PluginModule = {
9
8
  id: JODI_PANE_ID,
10
9
  name: "Oil and Gas Balances",
11
10
  icon: "O",
12
- component: JodiPane,
11
+ get component(): typeof import("./pane").JodiPane { const { JodiPane } = require("./pane"); return JodiPane; },
13
12
  defaultPosition: "right",
14
13
  defaultMode: "floating",
15
14
  defaultFloatingSize: { width: 110, height: 30 },
@@ -1,7 +1,6 @@
1
1
  import type { PaneTemplateContext, PaneTemplateCreateOptions } from "../../../types/plugin";
2
2
  import type { PluginModule } from "../plugin-module";
3
3
  import { DEFAULT_FLOATING_SIZE, KELLY_PANE_ID } from "./constants";
4
- import { KellySizerPane } from "./pane";
5
4
  import { kellyHeadless } from "./headless";
6
5
 
7
6
  function resolveTemplateSymbol(context: PaneTemplateContext, options?: PaneTemplateCreateOptions): string | null {
@@ -21,7 +20,7 @@ export const positionSizerModule: PluginModule = {
21
20
  icon: "K",
22
21
  // The bankroll is the account's value.
23
22
  personalData: "holdings",
24
- component: KellySizerPane,
23
+ get component(): typeof import("./pane").KellySizerPane { const { KellySizerPane } = require("./pane"); return KellySizerPane; },
25
24
  defaultPosition: "right",
26
25
  defaultMode: "floating",
27
26
  defaultFloatingSize: DEFAULT_FLOATING_SIZE,
@@ -2,7 +2,6 @@ import { findPaneInstance, type LayoutConfig } from "../../../types/config";
2
2
  import type { AppNotificationRequest, CommandResultDef, GloomPluginContext } from "../../../types/plugin";
3
3
  import type { PluginModule } from "../plugin-module";
4
4
  import type { AppAction } from "../../../state/app/context";
5
- import { LayoutMarketplacePane } from "../../../layout-marketplace/pane";
6
5
  import { apiClient } from "../../../api-client";
7
6
  import { resolvePlanAccess } from "../../../api-client/plan-access";
8
7
  import { getSharedRegistry } from "../../registry";
@@ -11,6 +10,7 @@ import {
11
10
  deskFunctions,
12
11
  DESKS,
13
12
  isDeskStock,
13
+ MORE_DESKS,
14
14
  pickDeskCompany,
15
15
  type Desk,
16
16
  } from "../../../layout/desks";
@@ -60,10 +60,11 @@ function getFocusedPane(layout: LayoutConfig, focusedPaneId: string | null) {
60
60
  }
61
61
 
62
62
  /** Desks whose key, name or one of whose functions matches what follows DESK. */
63
- function matchDesks(query: string): Desk[] {
63
+ function matchDesks(query: string): Desk<string>[] {
64
64
  const needle = query.trim().toLowerCase();
65
- if (!needle) return [...DESKS];
66
- return DESKS.filter((desk) => (
65
+ const desks: Desk<string>[] = [...DESKS, ...MORE_DESKS];
66
+ if (!needle) return desks;
67
+ return desks.filter((desk) => (
67
68
  desk.key.startsWith(needle)
68
69
  || desk.label.toLowerCase().includes(needle)
69
70
  || desk.name.toLowerCase().includes(needle)
@@ -75,7 +76,7 @@ function matchDesks(query: string): Desk[] {
75
76
  * Opens the desk as a new tab after the others. Its company is the focused
76
77
  * pane's stock, else the most recent stock the user looked at.
77
78
  */
78
- async function addDesk(ctx: GloomPluginContext, desk: Desk): Promise<void> {
79
+ async function addDesk(ctx: GloomPluginContext, desk: Desk<string>): Promise<void> {
79
80
  const registry = getSharedRegistry();
80
81
  if (!registry || !dispatchRef || !getStateRef) return;
81
82
  const config = ctx.getConfig();
@@ -110,8 +111,8 @@ export const layoutManagerModule: PluginModule = {
110
111
  id: "layout-marketplace",
111
112
  name: "Layouts",
112
113
  icon: "L",
113
- component: LayoutMarketplacePane,
114
- reportFreshness: { status: "not-a-feed", basis: "layout listings" },
114
+ get component(): typeof import("../../../layout-marketplace/pane").LayoutMarketplacePane { const { LayoutMarketplacePane } = require("../../../layout-marketplace/pane"); return LayoutMarketplacePane; },
115
+ reportFreshness: { status: "not-a-feed", basis: "layout listings", local: true },
115
116
  defaultPosition: "right",
116
117
  defaultMode: "floating",
117
118
  defaultFloatingSize: { width: 118, height: 34 },
@@ -128,7 +129,7 @@ export const layoutManagerModule: PluginModule = {
128
129
  label: "Add a Desk",
129
130
  description: "Add a ready-made desk for one kind of trading as a new layout tab",
130
131
  keywords: ["desk", "desks", "workspace", "starter", "equities", "stocks", "options", "volatility", "futures",
131
- "commodities", "rates", "credit", "fx", "macro", "active trading", "day trading"],
132
+ "commodities", "rates", "credit", "fx", "macro", "active trading", "day trading", "buildout", "ai buildout", "data centers"],
132
133
  category: "config",
133
134
  shortcut: "DESK",
134
135
  shortcutArg: { placeholder: "desk", kind: "text", parse: (arg) => ({ query: arg.trim() }) },
@@ -152,7 +152,9 @@ export function MacroDayPane({ width, height, focused }: PaneProps) {
152
152
  return { text: times(event.multiple), value: event.multiple };
153
153
  }, [colors, model?.basis, release]);
154
154
 
155
- const contentHeight = Math.max(4, height - 1 - statGridRows(stats, width));
155
+ // A figure wraps only into rows the table's least leaves, so the pane never spills.
156
+ const statMaxLines = height - 1 - 4;
157
+ const contentHeight = Math.max(4, height - 1 - statGridRows(stats, width, undefined, { maxLines: statMaxLines }));
156
158
  const key = (event: MacroDayEvent) => `${event.kind}:${event.date}`;
157
159
 
158
160
  return <Box width={width} height={height} flexDirection="column" overflow="hidden">
@@ -164,7 +166,7 @@ export function MacroDayPane({ width, height, focused }: PaneProps) {
164
166
  {!symbol ? <EmptyState title="Select a ticker." /> : <PaneStatusBody subject="macro-day moves" loading={history.loading && !model}
165
167
  error={!model ? history.error ?? identityError ?? null : null} empty={!!model && !model.events.length}>
166
168
  {model ? <>
167
- <StatGrid items={stats} width={width} />
169
+ <StatGrid items={stats} width={width} maxLines={statMaxLines} />
168
170
  <DataTableView<MacroDayEvent> focused={focused} columns={columns} items={rows} rootWidth={width} rootHeight={contentHeight}
169
171
  getItemKey={key} emptyStateTitle={surprise === "all" ? "No release days." : `No ${MACRO_SURPRISE_LABELS[surprise].toLowerCase()} prints.`} sortColumnId={null} sortDirection="desc" selectedTextOverridesCellColor
170
172
  selection={{ kind: "id", selectedId: selectedRow, getId: key, onChange: (id) => setSelectedRow(id) }}
@@ -1,6 +1,5 @@
1
1
  import type { GloomPlugin } from "../../../types/plugin";
2
2
  import { MARKET_HALTS_PANE_ID } from "./model";
3
- import { MarketHaltsPane } from "./pane";
4
3
 
5
4
  export const marketHaltsPlugin: GloomPlugin = {
6
5
  id: "market-halts",
@@ -20,7 +19,7 @@ export const marketHaltsPlugin: GloomPlugin = {
20
19
  reportFreshness: { source: "Nasdaq Trader" },
21
20
  name: "Market Halts",
22
21
  icon: "H",
23
- component: MarketHaltsPane,
22
+ get component(): typeof import("./pane").MarketHaltsPane { const { MarketHaltsPane } = require("./pane"); return MarketHaltsPane; },
24
23
  defaultPosition: "right",
25
24
  defaultMode: "floating",
26
25
  defaultFloatingSize: { width: 124, height: 26 },
@@ -70,6 +70,7 @@ import {
70
70
  type HeatmapSizeBy,
71
71
  } from "./portfolio";
72
72
  import { useAutoRefresh, useUpdatedAgo } from "../../../react/auto-refresh";
73
+ import { useQuoteDelayFooter } from "../shared/cloud-upgrade";
73
74
  import {
74
75
  LIVE_STREAMING_QUICK_SETTING,
75
76
  useLiveStreamingSetting,
@@ -635,6 +636,13 @@ function MarketHeatmapPane({ focused, width, height }: PaneProps) {
635
636
  useAutoRefresh(board?.checkedAt ?? null, autoRefresh, { intervalMs: heatmapCheckIntervalMs(board) });
636
637
 
637
638
  usePaneStatusFooter({ registrationId: "market-heatmap-retained", stale: hasBoard && stale && !portfolioTab });
639
+ // Signed out or free, a market board is the delayed one, and the holdings' quotes are delayed ones.
640
+ const quoteDelay = useQuoteDelayFooter(
641
+ portfolioTab
642
+ ? Array.from(financials.values(), (entry) => entry?.quote)
643
+ : shownBoard ? [{ dataSource: shownBoard.access === "realtime" ? "live" as const : "delayed" as const }] : [],
644
+ "heatmap-delay",
645
+ );
638
646
  usePaneNoticeFooter({
639
647
  registrationId: "market-heatmap-omitted",
640
648
  notices: portfolioTab && portfolioBoard.omitted > 0
@@ -677,7 +685,7 @@ function MarketHeatmapPane({ focused, width, height }: PaneProps) {
677
685
  // The caption takes the room the rest leaves, with "loading" counted in
678
686
  // always, so a refresh never squeezes the row; a narrow pane drops its
679
687
  // least important parts rather than truncating every segment.
680
- const others = [...selected, ...failed, ...feed];
688
+ const others = [...selected, ...failed, ...feed, ...(quoteDelay ? [quoteDelay] : [])];
681
689
  const room = width - others.reduce((total, segment) => total + footerSegmentWidth(segment) + 1, 0) - LOADING_FOOTER_WIDTH - 1;
682
690
  const caption = shownBoard ? heatmapBoardCaption(shownBoard, { now: Date.now(), checked, failed: failed.length > 0, maxWidth: room }) : null;
683
691
  return {
@@ -687,9 +695,10 @@ function MarketHeatmapPane({ focused, width, height }: PaneProps) {
687
695
  ...(caption ? [{ id: "board", parts: [{ text: caption, tone: "muted" as const }] }] : []),
688
696
  ...feed,
689
697
  ...loadingSegment,
698
+ ...(quoteDelay ? [quoteDelay] : []),
690
699
  ],
691
700
  };
692
- }, [checked, feedStatus, hasBoard, loadError, loading, portfolioTab, selectedAsset, shownBoard, width]);
701
+ }, [checked, feedStatus, hasBoard, loadError, loading, portfolioTab, quoteDelay, selectedAsset, shownBoard, width]);
693
702
 
694
703
  return (
695
704
  <Box flexDirection="column" width={width} height={height}>
@@ -14,6 +14,7 @@ import { useAssetData, usePluginPaneState, usePluginTickerActions } from "../../
14
14
  import { useLiveQuoteEntries } from "../../../state/hooks/quote-streaming";
15
15
  import { useAutoRefresh } from "../../../react/auto-refresh";
16
16
  import { useQuoteBoard } from "../shared/use-quote-board";
17
+ import { useQuoteDelayFooter } from "../shared/cloud-upgrade";
17
18
  import {
18
19
  attachMarketMoversPersistence,
19
20
  MARKET_SUMMARY_SYMBOLS,
@@ -62,6 +63,8 @@ import {
62
63
  const NO_SAVED_SELECTION: string[] = [];
63
64
  const SESSION_CHECK_MS = 30_000;
64
65
  const NO_QUOTES: MoverQuote[] = [];
66
+ /** A list without Pro is the one built a delay before: the delayed clock reads it as one late price. */
67
+ const DELAYED_LIST = [{ dataSource: "delayed" as const }];
65
68
  const moverKey = (row: Pick<ScreenerQuote, "symbol" | "exchange">) => publicTickerKey(row.symbol, row.exchange);
66
69
 
67
70
  /** What is trading in New York, re-read every half minute so the pane follows 04:00, 09:30 and 16:00. */
@@ -285,6 +288,8 @@ function ScreenerMoversBody({ activeTab, focused, width, summaryQuotes, liveStre
285
288
  handleRefreshKey(event, () => loadTab(activeTab, { forceRefresh: true }), { stopPropagation: true })
286
289
  ), [activeTab, loadTab]);
287
290
 
291
+ // Without Pro the lists are the delayed ones (with Pro the real-time ones), and say so.
292
+ const quoteDelay = useQuoteDelayFooter(rows.length > 0 ? DELAYED_LIST : [], "movers-delay");
288
293
  usePaneFooter("market-movers", () => ({
289
294
  info: [
290
295
  ...(loadError ? [{ id: "load-error", parts: [{ text: loadError, tone: "warning" as const }] }] : []),
@@ -299,8 +304,9 @@ function ScreenerMoversBody({ activeTab, focused, width, summaryQuotes, liveStre
299
304
  ...(moversStale && moversSavedAt == null && loadedKey === listKey ? [STALE_SEGMENT] : []),
300
305
  // The FLOAT column shows while a minimum is set; a float the source does not date says so once.
301
306
  ...partialFloatFooter(columns.some((column) => column.id === "floatShares"), rows),
307
+ ...(quoteDelay ? [quoteDelay] : []),
302
308
  ],
303
- }), [columns, feedStatus, listKey, loadedKey, loadError, loading, moversStale, moversSavedAt, rows, summaryQuotes]);
309
+ }), [columns, feedStatus, listKey, loadedKey, loadError, loading, moversStale, moversSavedAt, quoteDelay, rows, summaryQuotes]);
304
310
 
305
311
  return (
306
312
  <DataTableView<MarketMoverRow, MarketMoverColumn>
@@ -5,6 +5,7 @@ import type {
5
5
  CloudSessionMoversSide,
6
6
  } from "../../../api-client/market-movers";
7
7
  import { usePlanAccess } from "../../../api-client/plan-access";
8
+ import { useQuoteDelayFooter } from "../shared/cloud-upgrade";
8
9
  import { DataTableView, EmptyState, QueryBar, usePaneFooter, type DataTableKeyEvent } from "../../../components";
9
10
  import { STALE_SEGMENT } from "../../../components/layout/pane/status-footer";
10
11
  import { handleRefreshKey } from "../../../components/data-table/table-pane";
@@ -191,6 +192,7 @@ function SessionMoversTable({ view, session, focused, width, summaryQuotes, live
191
192
  const sessionLabel = payload ? earlierSessionLabel(payload.session, session) : null;
192
193
  // While its session trades, a free list says when it was built; a finished one is final and its date says enough.
193
194
  const asOfLabel = payload && delayed && sessionTrading ? listAsOfLabel(payload.asOf, payload.session, session) : null;
195
+ const quoteDelay = useQuoteDelayFooter(payload ? [{ dataSource: delayed ? "delayed" : "live" }] : [], "movers-delay");
194
196
  usePaneFooter("market-movers", () => ({
195
197
  info: [
196
198
  ...(loadError ? [{ id: "load-error", parts: [{ text: loadError, tone: "warning" as const }] }] : []),
@@ -205,8 +207,9 @@ function SessionMoversTable({ view, session, focused, width, summaryQuotes, live
205
207
  ...(payload?.stale ? [STALE_SEGMENT] : []),
206
208
  // A float the source does not date says so once, while the FLOAT column shows.
207
209
  ...partialFloatFooter(columns.some((column) => column.id === "floatShares"), rows),
210
+ ...(quoteDelay ? [quoteDelay] : []),
208
211
  ],
209
- }), [asOfLabel, columns, feedStatus, loadError, loading, payload?.stale, rows, sessionLabel, summaryQuotes]);
212
+ }), [asOfLabel, columns, feedStatus, loadError, loading, payload?.stale, quoteDelay, rows, sessionLabel, summaryQuotes]);
210
213
 
211
214
  const openRow = useCallback((row: SessionMoverRow) => {
212
215
  pinTicker(rowKey(row), { floating: true, paneType: TICKER_RESEARCH_PANE_ID, instrument: null });
@@ -3,7 +3,6 @@ import { valuationCache } from "./cache";
3
3
  import { valuationSeriesDefinitions } from "./chart-series";
4
4
  import { INDICATORS, resolveIndicatorArg } from "./indicators";
5
5
  import { marketValuationHeadless } from "./headless";
6
- import { MarketValuationPane } from "./pane";
7
6
  import { buildValuationSettingsDef, VALUATION_DEFAULTS } from "./settings";
8
7
 
9
8
 
@@ -41,7 +40,7 @@ export const marketValuationModule: PluginModule = {
41
40
  id: MARKET_VALUATION_PANE_ID,
42
41
  name: "Market Valuation",
43
42
  icon: "V",
44
- component: MarketValuationPane,
43
+ get component(): typeof import("./pane").MarketValuationPane { const { MarketValuationPane } = require("./pane"); return MarketValuationPane; },
45
44
  defaultPosition: "right",
46
45
  defaultMode: "floating",
47
46
  defaultFloatingSize: { width: 88, height: 34 },
@@ -1,13 +1,12 @@
1
1
  import type { GloomPlugin } from "../../../types/plugin";
2
2
  import { fundBreadthCache, fundChangesCache, fundListCache, fundMembersCache } from "./client";
3
3
  import { membersHeadless } from "./headless";
4
- import { MembersPane } from "./pane";
5
4
  import { membersTitle, TABS } from "./model";
6
5
  export const membersPlugin: GloomPlugin = {
7
6
  id: "members", name: "Index and ETF members", version: "1.0.0", description: "ETF holdings, member returns, contributions, breadth and index changes", toggleable: true,
8
7
  setup(ctx) { fundBreadthCache.attach(ctx.persistence); fundListCache.attach(ctx.persistence); fundMembersCache.attach(ctx.persistence); fundChangesCache.attach(ctx.persistence); },
9
8
  dispose() { fundBreadthCache.reset(); fundListCache.reset(); fundMembersCache.reset(); fundChangesCache.reset(); },
10
- panes: [{ id: "members", name: "Index and ETF members", icon: "M", component: MembersPane, tickerFollower: true,
9
+ panes: [{ id: "members", name: "Index and ETF members", icon: "M", get component(): typeof import("./pane").MembersPane { const { MembersPane } = require("./pane"); return MembersPane; }, tickerFollower: true,
11
10
  defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 132, height: 32 }, tableExport: true,
12
11
  settings: { fields: [{ key: "tab", label: "View", type: "select", options: TABS }] } }],
13
12
  paneTemplates: [{ id: "members-pane", paneId: "members", label: "Index and ETF members", description: "ETF holdings, member returns, daily contributions, breadth and index changes.",
@@ -1,12 +1,11 @@
1
1
  import type { PluginModule } from "../plugin-module";
2
2
  import { moneyMarketsCache } from "./client";
3
3
  import { moneyMarketsHeadless } from "./headless";
4
- import { MoneyMarketsPane } from "./pane";
5
4
  import { MONEY_MARKET_SERIES } from "./series";
6
5
 
7
6
  export const moneyMarketsModule: PluginModule = {
8
7
  series: MONEY_MARKET_SERIES,
9
- panes: [{ id: "money-markets", name: "Money Markets", icon: "$", component: MoneyMarketsPane,
8
+ panes: [{ id: "money-markets", name: "Money Markets", icon: "$", get component(): typeof import("./pane").MoneyMarketsPane { const { MoneyMarketsPane } = require("./pane"); return MoneyMarketsPane; },
10
9
  defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 88, height: 28 },
11
10
  tableExport: true, headless: moneyMarketsHeadless,
12
11
  settings: { title: "Money Markets", fields: [{ key: "tab", label: "View", type: "select",
@@ -2,6 +2,7 @@ import { useCallback, useMemo } from "react";
2
2
  import { Box } from "../../../ui";
3
3
  import { useAsyncResource, useAutoRefresh, usePaneSettingValue, usePluginPaneState, useUpdatedAgo } from "../../../public/react";
4
4
  import { ChartTableHeader, CompositeChart, CurveSurface, curveGhostColors, EmptyState, formatPercentAxis, MarketBoardStack, PaneStatusBody, useChartTableSelection, usePaneNoticeFooter, usePaneStatusLinkFooter, usePaneTabs, type ChartTableChart, type MarketBoardRow, type StatItem } from "../../../components";
5
+ import { ageFooterSegment } from "../../../components/layout/pane/footer";
5
6
  import { curveStrip, curveSurfaceMinRows } from "../../../components/chart/curve";
6
7
  import { colors } from "../../../theme/colors";
7
8
  import { useThemeColors } from "../../../theme/theme-context";
@@ -125,7 +126,7 @@ export function MoneyMarketsPane({ width, height, focused }: PaneProps) {
125
126
  notices: [...(data ? moneyMarketNotices(data) : []), ...(resource.data?.refreshError ? [resource.data.refreshError] : [])] });
126
127
  usePaneStatusLinkFooter({ registrationId: "money-markets", focused, loading: resource.loading, error: resource.error,
127
128
  url: selected?.observation.sourceUrl ?? null, showOpenHint: true,
128
- info: data && updatedAgo ? [{ id: "updated", parts: [{ text: updatedAgo, tone: "muted" as const }] }] : [],
129
+ info: data && updatedAgo ? [ageFooterSegment(updatedAgo)] : [],
129
130
  stale: !!data && resource.data?.stale,
130
131
  });
131
132
  return <Box width={width} height={height} flexDirection="column">
@@ -14,6 +14,8 @@ import {
14
14
  import { parsePublicTickerKey } from "../../../utils/exchanges";
15
15
  import { companyDataNotes, readCompanyFrom } from "../../../types/company-from";
16
16
  import { formatUtcSeconds } from "../../../utils/utc-time";
17
+ import { newsSummaryLine } from "../../../news/news-model";
18
+ import { shortStoryId } from "../../../news/story-id";
17
19
  import { cloudNewsFreshness, cloudRealtimeAccess } from "../shared/report-freshness";
18
20
  import { REPORT_FILTER, REPORT_ROWS } from "../shared/headless-options";
19
21
 
@@ -25,6 +27,11 @@ const NEWS_COLUMNS = [
25
27
  { key: "tickers", header: "Tickers", textFormat: (value: unknown) => Array.isArray(value) ? value.join(", ") : String(value ?? "") },
26
28
  { key: "sentiment", header: "Sentiment" },
27
29
  { key: "importance", header: "Score", align: "right" as const },
30
+ // The short id `fn news-story-pane` opens the story by, and its summary on one line under the row; exports keep both whole.
31
+ { key: "id", header: "ID", textFormat: (value: unknown) => typeof value === "string" ? shortStoryId(value) : "" },
32
+ { key: "summary", header: "Summary", underRow: "line" as const,
33
+ textFormat: (value: unknown, row: Record<string, unknown>) =>
34
+ newsSummaryLine({ title: String(row.headline ?? ""), summary: typeof value === "string" ? value : undefined }) ?? "" },
28
35
  ];
29
36
 
30
37
  export interface NewsHeadlessDependencies {
@@ -92,7 +92,7 @@ const newsWirePanes: PluginModule["panes"] = [
92
92
  {
93
93
  id: NEWS_STORY_PANE_ID,
94
94
  reportFreshness: NEWS_REPORT_FRESHNESS,
95
- reportArgument: { kind: "free-text", placeholder: "story id", description: "The id of the story to open." },
95
+ reportArgument: { kind: "free-text", placeholder: "story id", description: "The id of the story to open.", identifier: true },
96
96
  name: "Story",
97
97
  icon: "A",
98
98
  component: NewsStoryPane,
@@ -7,6 +7,7 @@ import { usePaneInstance } from "../../../../../state/app/context";
7
7
  import type { PaneProps, PaneTemplateCreateOptions, PaneTemplateDef } from "../../../../../types/plugin";
8
8
  import { usePluginAppActions } from "../../../../runtime";
9
9
  import { usePersistedNewsArticles } from "../persisted-articles";
10
+ import { storyIdMatches } from "../../../../../news/story-id";
10
11
  import { NewsDetailView } from "./detail-view";
11
12
  import { useNewsArticleFooter } from "./footer";
12
13
 
@@ -33,7 +34,11 @@ function handOffNewsStory(article: NewsArticle): void {
33
34
 
34
35
  function handedOffStory(articleId: string): NewsArticle | null {
35
36
  if (!articleId) return null;
36
- return handedOffStories.get(articleId) ?? null;
37
+ const exact = handedOffStories.get(articleId);
38
+ if (exact) return exact;
39
+ // A pane opened by a short id (`fn news-story-pane 6067684e`) takes the story whose id starts with it.
40
+ for (const [id, article] of handedOffStories) if (storyIdMatches(id, articleId)) return article;
41
+ return null;
37
42
  }
38
43
 
39
44
  export const openNewsStoryPane = (
@@ -82,9 +87,10 @@ export function NewsStoryPane({ focused, width }: PaneProps) {
82
87
  const [loaded, setLoaded] = useState<NewsArticle | null>(() => handedOffStory(articleId));
83
88
  // The pane keeps a copy of its story, so a relaunch or a shared layout shows
84
89
  // it at once, including a story from an RSS feed that cannot be fetched by id.
85
- const story = useMemo(() => (loaded?.id === articleId ? [loaded] : NO_STORY), [articleId, loaded]);
90
+ // A story opened by its short id (`fn news-story-pane 6067684e`) is the one whose id starts with it.
91
+ const story = useMemo(() => (loaded && storyIdMatches(loaded.id, articleId) ? [loaded] : NO_STORY), [articleId, loaded]);
86
92
  const kept = usePersistedNewsArticles("story", story);
87
- const article = kept.find((entry) => entry.id === articleId) ?? null;
93
+ const article = kept.find((entry) => storyIdMatches(entry.id, articleId)) ?? null;
88
94
  const [loading, setLoading] = useState(false);
89
95
  const [error, setError] = useState<string | null>(null);
90
96
 
@@ -1,3 +1,4 @@
1
+ import { notesPluginMeta } from "../builtin-plugin-meta";
1
2
  import { apiClient } from "../../../api-client";
2
3
  import type { GloomPlugin } from "../../../types/plugin";
3
4
  import { teamStore } from "../../../team/store";
@@ -12,11 +13,7 @@ import { createNotesTab } from "./ticker-notes-tab";
12
13
  let disposeNotes: (() => void) | null = null;
13
14
 
14
15
  export const notesPlugin: GloomPlugin = {
15
- id: "notes",
16
- name: "Notes",
17
- version: "2.0.0",
18
- description: "Markdown notes on tickers and on their own, personal or shared with a team.",
19
- toggleable: true,
16
+ ...notesPluginMeta,
20
17
  cliCommands: [notesCliCommand],
21
18
 
22
19
  setup(ctx) {
@@ -79,7 +76,7 @@ export const notesPlugin: GloomPlugin = {
79
76
 
80
77
  ctx.registerPane({
81
78
  id: "quick-notes",
82
- reportFreshness: { source: "Your notes", status: "not-a-feed", basis: "your notes" },
79
+ reportFreshness: { source: "Your notes", status: "not-a-feed", basis: "your notes", local: true },
83
80
  name: "Notes",
84
81
  personalData: "messages",
85
82
  icon: "N",
@@ -1,5 +1,4 @@
1
1
  import type { GloomPlugin, PaneTemplateCreateOptions } from "../../../types/plugin";
2
- import { OpenFdaPane } from "./pane";
3
2
  import { OPENFDA_PANE_ID, OPENFDA_PLUGIN_ID } from "./types";
4
3
 
5
4
  const createOpenFdaPaneInstance = (options?: PaneTemplateCreateOptions) => {
@@ -35,7 +34,7 @@ export const openFdaPlugin: GloomPlugin = {
35
34
  reportFreshness: { source: "US FDA", status: "not-a-feed", basis: "published reports" },
36
35
  name: "FDA Reports",
37
36
  icon: "F",
38
- component: OpenFdaPane,
37
+ get component(): typeof import("./pane").OpenFdaPane { const { OpenFdaPane } = require("./pane"); return OpenFdaPane; },
39
38
  defaultPosition: "right",
40
39
  defaultMode: "floating",
41
40
  defaultFloatingSize: { width: 100, height: 30 },
@@ -32,6 +32,7 @@ import {
32
32
  type QueryBarFilter,
33
33
  type DataTableVisibleRange,
34
34
  } from "../../../components";
35
+ import { statGridWithin } from "../../../components/ui/stat-grid";
35
36
  import { useShortcut } from "../../../react/input";
36
37
  import { useOptionalDialog, type AlertContext } from "../../../ui/dialog";
37
38
  import { useLiveQuoteEntries, useQuoteUpdates } from "../../../state/hooks/quote-streaming";
@@ -980,10 +981,10 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
980
981
  const expiryInline = Math.floor((statWidth - 2 * (cellColumns - 1)) / cellColumns) >= expiryCells;
981
982
  const statColumns = expiryInline ? cellColumns : figureColumns;
982
983
  const statItems = [expiryInline ? expiryItem : { ...expiryItem, wide: true }, ...figures];
983
- const summaryRowCount = Math.min(statGridRows(statItems, statWidth, statColumns), Math.max(0, height - 6));
984
- // A short band gives up figures from the end; the expiry stays.
985
- const shownStatItems = summaryRowCount === 0 ? []
986
- : [statItems[0]!, ...figures.slice(0, summaryRowCount * statColumns - (expiryInline ? 1 : statColumns))];
984
+ // A short band gives up figures from the end, by lines: a row that wraps
985
+ // takes two. The expiry leads, so it stays.
986
+ const summaryLines = Math.min(statGridRows(statItems, statWidth, statColumns), Math.max(0, height - 6));
987
+ const { items: shownStatItems, lines: summaryRowCount } = statGridWithin(statItems, statWidth, summaryLines, statColumns);
987
988
  // No term here follows the selection or the load, so an empty cold-expiry
988
989
  // response cannot resize the table: growing it during loading would turn a
989
990
  // clamped scroll into apparent user navigation.
@@ -997,7 +998,7 @@ export function OptionsView({ width, height, focused, nestedInTabs = false, ivRa
997
998
  <QueryBar width={Math.max(1, width - 2)} filters={queryFilters} />
998
999
 
999
1000
  {summaryRowCount > 0 && (
1000
- <StatGrid items={shownStatItems} width={statWidth} columns={statColumns} />
1001
+ <StatGrid items={shownStatItems} width={statWidth} columns={statColumns} maxLines={summaryLines} />
1001
1002
  )}
1002
1003
 
1003
1004
  {isOpt && parsed && (
@@ -3,7 +3,6 @@ import {
3
3
  OPTIONS_CALCULATOR_PANE_ID,
4
4
  OPTIONS_CALCULATOR_TEMPLATE_ID,
5
5
  } from "./model";
6
- import { OptionsCalculatorPane } from "./pane";
7
6
  import { optionsCalculatorHeadless } from "./headless";
8
7
 
9
8
  export const optionsCalculatorModule: PluginModule = {
@@ -12,7 +11,7 @@ export const optionsCalculatorModule: PluginModule = {
12
11
  id: OPTIONS_CALCULATOR_PANE_ID,
13
12
  name: "Options Calculator",
14
13
  icon: "V",
15
- component: OptionsCalculatorPane,
14
+ get component(): typeof import("./pane").OptionsCalculatorPane { const { OptionsCalculatorPane } = require("./pane"); return OptionsCalculatorPane; },
16
15
  defaultPosition: "right",
17
16
  defaultMode: "floating",
18
17
  defaultFloatingSize: { width: 96, height: 28 },
@@ -3,7 +3,6 @@ import type { PluginModule } from "../plugin-module";
3
3
  import { createTickerSurfacePaneTemplate, type TickerSurfacePaneTemplateOptions } from "../shared/ticker-surface";
4
4
  import { optionsPositioningHeadless } from "./headless";
5
5
  import { isPositioningTab, type PositioningTab } from "./model";
6
- import { OptionsPositioningPane } from "./pane";
7
6
 
8
7
  /** The underlying OPX opens on when no ticker is active. */
9
8
  const DEFAULT_UNDERLYING = "SPY";
@@ -45,7 +44,7 @@ export const optionsPositioningModule: PluginModule = {
45
44
  id: "options-positioning",
46
45
  name: "Options Positioning",
47
46
  icon: "O",
48
- component: OptionsPositioningPane,
47
+ get component(): typeof import("./pane").OptionsPositioningPane { const { OptionsPositioningPane } = require("./pane"); return OptionsPositioningPane; },
49
48
  defaultPosition: "right",
50
49
  tickerFollower: true,
51
50
  defaultMode: "floating",
@@ -1,8 +1,6 @@
1
1
  import type { PluginModule } from "../plugin-module";
2
2
  import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
3
- import { OptionsScenarioPane } from "./pane";
4
3
  import { optionsScenarioHeadless } from "./headless";
5
- import { CspScreenPane } from "./csp-screen-pane";
6
4
  import { cspScreenHeadless } from "./csp-screen-headless";
7
5
 
8
6
  const scenarioSettingKeys = ["legs", "strategy", "strike", "delta", "putStrike", "callStrike", "shares", "expiration", "spot", "rate", "dividendYield", "currency", "asOf", "date", "volShift",
@@ -10,7 +8,7 @@ const scenarioSettingKeys = ["legs", "strategy", "strike", "delta", "putStrike",
10
8
 
11
9
  export const optionsScenarioModule: PluginModule = {
12
10
  panes: [{
13
- id: "options-scenario", name: "Options Scenario", icon: "V", component: OptionsScenarioPane,
11
+ id: "options-scenario", name: "Options Scenario", icon: "V", get component(): typeof import("./pane").OptionsScenarioPane { const { OptionsScenarioPane } = require("./pane"); return OptionsScenarioPane; },
14
12
  defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 126, height: 36 },
15
13
  tableExport: true, headless: optionsScenarioHeadless,
16
14
  settings: {
@@ -26,7 +24,7 @@ export const optionsScenarioModule: PluginModule = {
26
24
  ],
27
25
  },
28
26
  }, {
29
- id: "csp-screen", name: "Options Strategy Screen", icon: "V", component: CspScreenPane,
27
+ id: "csp-screen", name: "Options Strategy Screen", icon: "V", get component(): typeof import("./csp-screen-pane").CspScreenPane { const { CspScreenPane } = require("./csp-screen-pane"); return CspScreenPane; },
30
28
  defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 144, height: 24 },
31
29
  tableExport: true, headless: cspScreenHeadless,
32
30
  settings: {
@@ -4,7 +4,6 @@ import { commandRequestParams } from "../shared/command-tab";
4
4
  import { PERP_SERIES } from "./series";
5
5
  import { perpsCache, perpsDepthCache, perpsHistoryCache, perpsLiquidationsCache, perpsListCache, perpsMarketCache, perpsRankingsCache } from "./client";
6
6
  import { perpsHeadless } from "./headless";
7
- import { PerpsPane } from "./pane";
8
7
 
9
8
  /**
10
9
  * A named market has its own pane, and asking for it again (PERP, a stock's perpetual row in DES or QQ) brings that
@@ -22,7 +21,7 @@ const PERP_CACHES = [perpsCache, perpsHistoryCache, perpsListCache, perpsMarketC
22
21
 
23
22
  export const perpsModule: PluginModule = {
24
23
  series: PERP_SERIES,
25
- panes: [{ id: "perps", name: "Perpetuals", icon: "P", component: PerpsPane, defaultPosition: "right", defaultMode: "floating",
24
+ panes: [{ id: "perps", name: "Perpetuals", icon: "P", get component(): typeof import("./pane").PerpsPane { const { PerpsPane } = require("./pane"); return PerpsPane; }, defaultPosition: "right", defaultMode: "floating",
26
25
  defaultFloatingSize: { width: 138, height: 34 }, tableExport: true, headless: perpsHeadless }],
27
26
  paneTemplates: [{ id: "perps-pane", paneId: "perps", label: "Perpetuals", description: "Perpetual funding, open interest and premiums by market and venue, with rankings, a venue comparison, liquidations, order book depth and Pro history.",
28
27
  keywords: ["perp", "perpetual", "perpetuals", "funding", "funding rate", "open interest", "hip-3", "stock perps", "basis", "basis trade", "carry", "venue", "funding spread", "long short ratio", "long/short", "positioning", "liquidations", "liquidation", "order book depth", "market depth", "liquidity"],
@@ -1,7 +1,6 @@
1
1
  import { PLUGIN_AUTO_UPDATE_SETTING } from "../../auto-update";
2
2
  import type { PluginModule } from "../plugin-module";
3
3
  import { PLUGIN_MARKETPLACE_PANE_ID } from "../../marketplace/ids";
4
- import { PluginMarketplacePane } from "./pane";
5
4
 
6
5
  export const pluginMarketplaceModule: PluginModule = {
7
6
  panes: [
@@ -9,8 +8,8 @@ export const pluginMarketplaceModule: PluginModule = {
9
8
  id: PLUGIN_MARKETPLACE_PANE_ID,
10
9
  name: "Plugins",
11
10
  icon: "P",
12
- component: PluginMarketplacePane,
13
- reportFreshness: { status: "not-a-feed", basis: "plugin listings" },
11
+ get component(): typeof import("./pane").PluginMarketplacePane { const { PluginMarketplacePane } = require("./pane"); return PluginMarketplacePane; },
12
+ reportFreshness: { status: "not-a-feed", basis: "plugin listings", local: true },
14
13
  defaultPosition: "right",
15
14
  defaultMode: "floating",
16
15
  defaultFloatingSize: { width: 118, height: 34 },
@@ -21,6 +21,7 @@ import {
21
21
  type DataTableKeyEvent,
22
22
  type PaneHint,
23
23
  } from "../../../components";
24
+ import { ageFooterSegment } from "../../../components/layout/pane/footer";
24
25
  import { loadingErrorFooterInfo } from "../../../components/data-table/table-pane";
25
26
  import { useRowNotes } from "../../../components/ui/row-notes";
26
27
  import { colors } from "../../../theme/colors";
@@ -922,7 +923,7 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
922
923
  info.push({ id: "restart", parts: [{ text: "restart to finish loading", tone: "warning" }] });
923
924
  }
924
925
  if (status === "ready" && fetchedAt && !stale && !busy) {
925
- info.push({ id: "updated", parts: [{ text: formatRelativeAge(fetchedAt), tone: "muted" }] });
926
+ info.push(ageFooterSegment(formatRelativeAge(fetchedAt)));
926
927
  }
927
928
 
928
929
  const hints: PaneHint[] = [];