@gloom-sh/terminal 0.18.5 → 0.19.1

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 (570) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/package.json +1 -1
  4. package/patches/electrobun@1.18.1.patch +163 -8
  5. package/src/api-client/app-attention.ts +3 -0
  6. package/src/api-client/beneficial-owners.ts +3 -0
  7. package/src/api-client/company-kpis.ts +3 -0
  8. package/src/api-client/cot.ts +6 -2
  9. package/src/api-client/credit-documents.ts +6 -0
  10. package/src/api-client/data.ts +19 -7
  11. package/src/api-client/equity-screener.ts +24 -0
  12. package/src/api-client/fund-facts.ts +45 -4
  13. package/src/api-client/hiring.ts +3 -0
  14. package/src/api-client/index.ts +15 -0
  15. package/src/api-client/ipo.ts +24 -1
  16. package/src/api-client/market-discovery.ts +10 -0
  17. package/src/api-client/market-movers.ts +7 -0
  18. package/src/api-client/members.ts +1 -1
  19. package/src/api-client/mna.ts +3 -0
  20. package/src/api-client/paths.ts +9 -1
  21. package/src/api-client/perps.ts +43 -0
  22. package/src/api-client/research-activity.ts +3 -2
  23. package/src/api-client/roadmap.ts +85 -0
  24. package/src/api-client/types.ts +82 -2
  25. package/src/app/pane-runtime/plugin-bindings.ts +9 -5
  26. package/src/app/runtime/broker-import.ts +0 -2
  27. package/src/app/runtime/plugin-bindings.ts +73 -56
  28. package/src/architecture/core-builtin-allowlist.json +2 -2
  29. package/src/architecture/pane-tabs-allowlist.json +3 -0
  30. package/src/architecture/ttl-caches-allowlist.json +19 -0
  31. package/src/brokers/sync-broker-instance.ts +2 -8
  32. package/src/capabilities/chart-series.ts +1 -1
  33. package/src/cli/commands/crypto-hints.ts +40 -24
  34. package/src/cli/commands/market.ts +107 -38
  35. package/src/cli/commands/news-search.ts +22 -0
  36. package/src/cli/commands/options-chain.ts +48 -7
  37. package/src/cli/commands/options-leaps.ts +9 -3
  38. package/src/cli/commands/overview.ts +147 -32
  39. package/src/cli/commands/search.ts +21 -1
  40. package/src/cli/commands/themes.ts +11 -2
  41. package/src/cli/commands/ticker.ts +87 -36
  42. package/src/cli/company-name-hint.ts +41 -0
  43. package/src/cli/earnings-calendar.ts +1 -1
  44. package/src/cli/errors.ts +25 -2
  45. package/src/cli/glossary.ts +31 -2
  46. package/src/cli/history-rows.ts +57 -9
  47. package/src/cli/index-roots.ts +0 -7
  48. package/src/cli/index.ts +2 -0
  49. package/src/cli/listing-arg.ts +235 -60
  50. package/src/cli/output-file.ts +68 -0
  51. package/src/cli/pane-functions/capabilities.ts +4 -0
  52. package/src/cli/pane-functions/catalog.ts +80 -7
  53. package/src/cli/pane-functions/discovery.ts +21 -1
  54. package/src/cli/pane-functions/dom.ts +13 -3
  55. package/src/cli/pane-functions/freshness.ts +93 -4
  56. package/src/cli/pane-functions/function-help.ts +20 -10
  57. package/src/cli/pane-functions/headless.ts +17 -7
  58. package/src/cli/pane-functions/index.ts +54 -16
  59. package/src/cli/pane-functions/market-session.ts +66 -4
  60. package/src/cli/pane-functions/options.ts +19 -1
  61. package/src/cli/pane-functions/resolver.ts +19 -8
  62. package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
  63. package/src/cli/pane-functions/screenshot-types.ts +5 -0
  64. package/src/cli/quote-answer.ts +22 -11
  65. package/src/cli/saved-copy-persistence.ts +88 -0
  66. package/src/cli/ticker-resolution.ts +3 -2
  67. package/src/components/chart/composite/composite-chart.tsx +4 -0
  68. package/src/components/chart/composite/interactions.ts +6 -0
  69. package/src/components/chart/composite/legend.tsx +45 -7
  70. package/src/components/chart/composite/panel-gestures.ts +33 -0
  71. package/src/components/chart/composite/panel-surface.tsx +52 -3
  72. package/src/components/chart/composite/rasterizer.ts +24 -13
  73. package/src/components/chart/composite/scene.ts +39 -4
  74. package/src/components/chart/composite/series-cues.ts +14 -0
  75. package/src/components/chart/composite/text-renderer.ts +9 -2
  76. package/src/components/chart/composite/tool-catalog.ts +36 -0
  77. package/src/components/chart/composite/tools.ts +203 -44
  78. package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
  79. package/src/components/command-bar/assist/inventory.ts +2 -1
  80. package/src/components/command-bar/commands/direct/index.ts +28 -13
  81. package/src/components/command-bar/list/model.ts +5 -0
  82. package/src/components/command-bar/pane-templates/items.ts +3 -2
  83. package/src/components/command-bar/routes/root/runtime.ts +6 -7
  84. package/src/components/command-bar/selection-runtime.ts +52 -3
  85. package/src/components/command-bar/surface/index.tsx +2 -0
  86. package/src/components/command-bar/workflow/collection-actions.ts +3 -3
  87. package/src/components/layout/pane/capture-stamp.ts +2 -2
  88. package/src/components/layout/pane/footer/index.tsx +22 -6
  89. package/src/components/layout/pane/footer/model.ts +83 -15
  90. package/src/components/layout/pane/status-footer.ts +35 -2
  91. package/src/components/layout/status-widget-space.ts +9 -0
  92. package/src/components/onboarding/onboarding-wizard.tsx +4 -1
  93. package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
  94. package/src/components/onboarding/wizard-broker-sync.ts +0 -1
  95. package/src/components/onboarding/wizard-positions.ts +3 -3
  96. package/src/components/ticker/list-table-view.tsx +8 -21
  97. package/src/components/ui/choice-dialog.tsx +23 -18
  98. package/src/components/ui/choice-groups.ts +18 -0
  99. package/src/components/ui/data-table/opentui/index.tsx +9 -7
  100. package/src/components/ui/data-table/opentui/model.ts +26 -1
  101. package/src/components/ui/data-table/types.ts +2 -0
  102. package/src/components/ui/display.tsx +18 -3
  103. package/src/components/ui/icon.tsx +4 -1
  104. package/src/components/ui/list-view.tsx +8 -4
  105. package/src/components/ui/query-bar-keys.tsx +2 -0
  106. package/src/components/ui/query-bar.tsx +1 -0
  107. package/src/components/ui/select-button.tsx +5 -1
  108. package/src/components/ui/select-field.tsx +1 -0
  109. package/src/components/ui/table-layout.ts +8 -5
  110. package/src/core/app-runtime.ts +2 -0
  111. package/src/core/app-services.ts +2 -2
  112. package/src/data/config/chart-settings.ts +1 -1
  113. package/src/data/json-ticker-repository.ts +17 -8
  114. package/src/data/plugin-cache.ts +5 -3
  115. package/src/data/saved-copies.ts +8 -0
  116. package/src/data/ticker-repository.ts +16 -9
  117. package/src/data/ticker-writes.ts +81 -0
  118. package/src/data/ttl-cache.ts +165 -0
  119. package/src/docs/snapshot.json +103 -142
  120. package/src/i18n/es.ts +20 -6
  121. package/src/i18n/ja.ts +20 -6
  122. package/src/i18n/ko.ts +20 -6
  123. package/src/i18n/zh-cn.ts +20 -6
  124. package/src/i18n/zh-tw.ts +20 -6
  125. package/src/market-data/depositary-pair.ts +27 -3
  126. package/src/market-data/fund-facts.ts +50 -0
  127. package/src/market-data/market/change-basis.ts +16 -3
  128. package/src/market-data/market/format.ts +25 -0
  129. package/src/market-data/market/freshness.ts +2 -1
  130. package/src/market-data/options-alternatives.ts +81 -4
  131. package/src/market-data/quotes/metadata.ts +7 -0
  132. package/src/news/aggregator.ts +119 -9
  133. package/src/news/cloud-session.ts +21 -0
  134. package/src/news/hooks.ts +1 -0
  135. package/src/news/news-model.ts +1 -0
  136. package/src/news/types.ts +17 -0
  137. package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
  138. package/src/plugins/builtin/alerts/alert-log.ts +33 -0
  139. package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
  140. package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
  141. package/src/plugins/builtin/alerts/cli.ts +362 -80
  142. package/src/plugins/builtin/alerts/command.ts +25 -1
  143. package/src/plugins/builtin/alerts/event-rows.ts +24 -3
  144. package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
  145. package/src/plugins/builtin/alerts/fired-log.ts +73 -9
  146. package/src/plugins/builtin/alerts/format.ts +75 -5
  147. package/src/plugins/builtin/alerts/headless.ts +34 -12
  148. package/src/plugins/builtin/alerts/history-rows.ts +3 -2
  149. package/src/plugins/builtin/alerts/history.ts +1 -1
  150. package/src/plugins/builtin/alerts/index.tsx +79 -16
  151. package/src/plugins/builtin/alerts/metric-command.ts +77 -4
  152. package/src/plugins/builtin/alerts/metric-data.ts +54 -13
  153. package/src/plugins/builtin/alerts/metric-model.ts +163 -14
  154. package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
  155. package/src/plugins/builtin/alerts/pane.tsx +61 -23
  156. package/src/plugins/builtin/alerts/quotes.ts +6 -0
  157. package/src/plugins/builtin/alerts/storage.ts +12 -8
  158. package/src/plugins/builtin/alerts/types.ts +4 -0
  159. package/src/plugins/builtin/analytics/index.tsx +2 -1
  160. package/src/plugins/builtin/analytics/risk-client.ts +16 -5
  161. package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
  162. package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
  163. package/src/plugins/builtin/analytics/risk-model.ts +55 -9
  164. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  165. package/src/plugins/builtin/attention/index.tsx +2 -1
  166. package/src/plugins/builtin/attention/listing.ts +14 -0
  167. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  168. package/src/plugins/builtin/buildout/cells.tsx +12 -4
  169. package/src/plugins/builtin/buildout/format.ts +38 -0
  170. package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
  171. package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
  172. package/src/plugins/builtin/buildout/table-model.ts +23 -2
  173. package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
  174. package/src/plugins/builtin/cds/client.ts +142 -30
  175. package/src/plugins/builtin/cds/headless.ts +35 -8
  176. package/src/plugins/builtin/cds/model.ts +145 -18
  177. package/src/plugins/builtin/cds/pane.tsx +16 -5
  178. package/src/plugins/builtin/changelog/index.tsx +9 -6
  179. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
  180. package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
  181. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
  182. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
  183. package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
  184. package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
  185. package/src/plugins/builtin/chart-composer/headless.ts +6 -1
  186. package/src/plugins/builtin/chart-composer/index.tsx +5 -6
  187. package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
  188. package/src/plugins/builtin/chart-composer/settings.ts +27 -0
  189. package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
  190. package/src/plugins/builtin/chat/controller/index.ts +5 -1
  191. package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
  192. package/src/plugins/builtin/chat/controller/state.ts +25 -2
  193. package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
  194. package/src/plugins/builtin/chat/controller/utils.ts +7 -2
  195. package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
  196. package/src/plugins/builtin/chat/status-preview.ts +79 -0
  197. package/src/plugins/builtin/chat/status-widget.tsx +147 -10
  198. package/src/plugins/builtin/chat/unread-badge.ts +51 -10
  199. package/src/plugins/builtin/clinical-trials/client.ts +49 -4
  200. package/src/plugins/builtin/clinical-trials/model.ts +86 -31
  201. package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
  202. package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
  203. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
  204. package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
  205. package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
  206. package/src/plugins/builtin/company-attention/building.tsx +5 -1
  207. package/src/plugins/builtin/company-attention/client.ts +8 -9
  208. package/src/plugins/builtin/company-attention/evidence.ts +4 -1
  209. package/src/plugins/builtin/company-attention/headless.ts +2 -1
  210. package/src/plugins/builtin/company-attention/moves.tsx +5 -1
  211. package/src/plugins/builtin/company-attention/pane.tsx +12 -2
  212. package/src/plugins/builtin/company-attention/signals.tsx +5 -1
  213. package/src/plugins/builtin/company-kpis/headless.ts +8 -3
  214. package/src/plugins/builtin/company-kpis/model.ts +73 -5
  215. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  216. package/src/plugins/builtin/composite-plugins.ts +2 -1
  217. package/src/plugins/builtin/correlation/headless.ts +13 -5
  218. package/src/plugins/builtin/correlation/history.ts +22 -0
  219. package/src/plugins/builtin/cot/client.ts +22 -18
  220. package/src/plugins/builtin/cot/headless.ts +15 -10
  221. package/src/plugins/builtin/cot/model.ts +14 -1
  222. package/src/plugins/builtin/cot/pane.tsx +22 -14
  223. package/src/plugins/builtin/credit-boards/client.ts +6 -10
  224. package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
  225. package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
  226. package/src/plugins/builtin/credit-conditions/model.ts +4 -0
  227. package/src/plugins/builtin/credit-conditions/series.ts +3 -1
  228. package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
  229. package/src/plugins/builtin/credit-documents/export.ts +173 -0
  230. package/src/plugins/builtin/credit-documents/headless.ts +10 -10
  231. package/src/plugins/builtin/credit-documents/model.ts +104 -14
  232. package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
  233. package/src/plugins/builtin/custom-view/sources.ts +2 -0
  234. package/src/plugins/builtin/dividend-yield/client.ts +131 -23
  235. package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
  236. package/src/plugins/builtin/dividend-yield/model.ts +8 -5
  237. package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
  238. package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
  239. package/src/plugins/builtin/dividend-yield/types.ts +20 -2
  240. package/src/plugins/builtin/dividend-yield/view.ts +30 -2
  241. package/src/plugins/builtin/docs/targets.ts +5 -1
  242. package/src/plugins/builtin/doe/headless.ts +3 -2
  243. package/src/plugins/builtin/doe/pane.tsx +13 -3
  244. package/src/plugins/builtin/earnings/board-model.ts +16 -15
  245. package/src/plugins/builtin/earnings/board.tsx +62 -71
  246. package/src/plugins/builtin/earnings/headless.ts +40 -4
  247. package/src/plugins/builtin/earnings/scope.ts +31 -43
  248. package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
  249. package/src/plugins/builtin/earnings-calls/data.ts +18 -6
  250. package/src/plugins/builtin/earnings-calls/format.ts +31 -2
  251. package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
  252. package/src/plugins/builtin/earnings-calls/model.ts +6 -1
  253. package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
  254. package/src/plugins/builtin/econ/calendar-model.ts +48 -1
  255. package/src/plugins/builtin/econ/calendar-source.ts +4 -2
  256. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  257. package/src/plugins/builtin/econ/figures.ts +48 -0
  258. package/src/plugins/builtin/econ/index.tsx +14 -5
  259. package/src/plugins/builtin/econ/print-watch.ts +3 -1
  260. package/src/plugins/builtin/econ/types.ts +2 -0
  261. package/src/plugins/builtin/econ-statistics/client.ts +11 -0
  262. package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
  263. package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
  264. package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
  265. package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
  266. package/src/plugins/builtin/equity-screener/categories.ts +179 -0
  267. package/src/plugins/builtin/equity-screener/client.ts +4 -2
  268. package/src/plugins/builtin/equity-screener/headless.ts +144 -13
  269. package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
  270. package/src/plugins/builtin/equity-screener/model.ts +99 -10
  271. package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
  272. package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
  273. package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
  274. package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
  275. package/src/plugins/builtin/fx-matrix/client.ts +6 -3
  276. package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
  277. package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
  278. package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
  279. package/src/plugins/builtin/gpu/headless.ts +8 -2
  280. package/src/plugins/builtin/help/function-index.ts +4 -1
  281. package/src/plugins/builtin/help/index.tsx +62 -59
  282. package/src/plugins/builtin/help/shortcut-model.ts +1 -1
  283. package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
  284. package/src/plugins/builtin/help/table-cells.tsx +13 -2
  285. package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
  286. package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
  287. package/src/plugins/builtin/holders/fund-holders.ts +51 -0
  288. package/src/plugins/builtin/holders/headless.ts +22 -3
  289. package/src/plugins/builtin/holders/pane.tsx +3 -0
  290. package/src/plugins/builtin/holders/report-rows.ts +4 -1
  291. package/src/plugins/builtin/insider/headless.ts +2 -2
  292. package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
  293. package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
  294. package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
  295. package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
  296. package/src/plugins/builtin/iv-history/client.ts +52 -11
  297. package/src/plugins/builtin/iv-history/format.ts +36 -23
  298. package/src/plugins/builtin/iv-history/headless.ts +33 -18
  299. package/src/plugins/builtin/iv-history/index.tsx +2 -2
  300. package/src/plugins/builtin/iv-history/model.ts +163 -27
  301. package/src/plugins/builtin/iv-history/pane.tsx +10 -6
  302. package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
  303. package/src/plugins/builtin/iv-history/universe.ts +5 -1
  304. package/src/plugins/builtin/jobs/client.ts +25 -29
  305. package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
  306. package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
  307. package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
  308. package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
  309. package/src/plugins/builtin/macro-day/headless.ts +12 -5
  310. package/src/plugins/builtin/macro-day/index.tsx +1 -1
  311. package/src/plugins/builtin/macro-day/model.ts +39 -5
  312. package/src/plugins/builtin/macro-day/pane.tsx +27 -19
  313. package/src/plugins/builtin/macro-day/release-table.ts +2 -2
  314. package/src/plugins/builtin/market-heatmap/data.ts +8 -33
  315. package/src/plugins/builtin/market-movers/client.ts +27 -7
  316. package/src/plugins/builtin/market-movers/headless.ts +34 -12
  317. package/src/plugins/builtin/market-movers/index.tsx +26 -14
  318. package/src/plugins/builtin/market-movers/min-float.ts +74 -0
  319. package/src/plugins/builtin/market-movers/model.ts +4 -1
  320. package/src/plugins/builtin/market-movers/screener.ts +22 -4
  321. package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
  322. package/src/plugins/builtin/market-movers/session-table.ts +4 -3
  323. package/src/plugins/builtin/market-movers/table.ts +17 -5
  324. package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
  325. package/src/plugins/builtin/mna/headless.ts +2 -0
  326. package/src/plugins/builtin/mna/pane.tsx +3 -1
  327. package/src/plugins/builtin/news/headless.ts +3 -1
  328. package/src/plugins/builtin/news/index.tsx +4 -0
  329. package/src/plugins/builtin/news/wire/categories.ts +19 -3
  330. package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
  331. package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
  332. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
  333. package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
  334. package/src/plugins/builtin/openfda/client.ts +14 -2
  335. package/src/plugins/builtin/openfda/index.tsx +3 -2
  336. package/src/plugins/builtin/openfda/model.ts +9 -0
  337. package/src/plugins/builtin/openfda/pane.tsx +42 -6
  338. package/src/plugins/builtin/options/analytics.ts +29 -5
  339. package/src/plugins/builtin/options/enrichment.ts +17 -5
  340. package/src/plugins/builtin/options/table.ts +5 -2
  341. package/src/plugins/builtin/options/types.ts +2 -0
  342. package/src/plugins/builtin/options/view.tsx +17 -4
  343. package/src/plugins/builtin/options-positioning/client.ts +11 -0
  344. package/src/plugins/builtin/options-positioning/headless.ts +21 -8
  345. package/src/plugins/builtin/options-positioning/index.tsx +1 -1
  346. package/src/plugins/builtin/options-positioning/model.ts +78 -9
  347. package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
  348. package/src/plugins/builtin/options-scenario/client.ts +116 -47
  349. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
  350. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
  351. package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
  352. package/src/plugins/builtin/options-scenario/csp.ts +8 -2
  353. package/src/plugins/builtin/options-scenario/index.tsx +9 -7
  354. package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
  355. package/src/plugins/builtin/pe-band/model.ts +12 -49
  356. package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
  357. package/src/plugins/builtin/perps/cascade.ts +236 -0
  358. package/src/plugins/builtin/perps/client.ts +56 -2
  359. package/src/plugins/builtin/perps/headless.ts +65 -9
  360. package/src/plugins/builtin/perps/index.tsx +7 -5
  361. package/src/plugins/builtin/perps/model.ts +18 -4
  362. package/src/plugins/builtin/perps/pane.tsx +19 -22
  363. package/src/plugins/builtin/perps/status-line.ts +25 -0
  364. package/src/plugins/builtin/plugin-module.ts +5 -1
  365. package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
  366. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
  367. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
  368. package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
  369. package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
  370. package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
  371. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
  372. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
  373. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
  374. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
  375. package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
  376. package/src/plugins/builtin/primary-dealers/client.ts +13 -38
  377. package/src/plugins/builtin/realized-vol/client.ts +13 -5
  378. package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
  379. package/src/plugins/builtin/realized-vol/headless.ts +8 -4
  380. package/src/plugins/builtin/realized-vol/index.tsx +2 -2
  381. package/src/plugins/builtin/realized-vol/model.ts +37 -6
  382. package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
  383. package/src/plugins/builtin/relative-rotation/client.ts +18 -4
  384. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  385. package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
  386. package/src/plugins/builtin/relative-rotation/model.ts +16 -40
  387. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
  388. package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
  389. package/src/plugins/builtin/research/event-model.ts +40 -12
  390. package/src/plugins/builtin/research/headless.ts +1 -1
  391. package/src/plugins/builtin/research/index.tsx +7 -3
  392. package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
  393. package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
  394. package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
  395. package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
  396. package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
  397. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
  398. package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
  399. package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
  400. package/src/plugins/builtin/risk-factors/data.ts +4 -2
  401. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  402. package/src/plugins/builtin/roadmap/index.tsx +28 -0
  403. package/src/plugins/builtin/roadmap/model.ts +67 -0
  404. package/src/plugins/builtin/roadmap/pane.tsx +244 -0
  405. package/src/plugins/builtin/scanner/flow-model.ts +50 -4
  406. package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
  407. package/src/plugins/builtin/scanner/flow-session.ts +146 -0
  408. package/src/plugins/builtin/scanner/headless.ts +204 -35
  409. package/src/plugins/builtin/scanner/index.ts +16 -5
  410. package/src/plugins/builtin/scanner/live-headless.ts +48 -5
  411. package/src/plugins/builtin/scanner/live-model.ts +11 -0
  412. package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
  413. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  414. package/src/plugins/builtin/seasonality/headless.ts +10 -1
  415. package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
  416. package/src/plugins/builtin/sec/fund-facts.ts +116 -34
  417. package/src/plugins/builtin/sec/headless.ts +4 -2
  418. package/src/plugins/builtin/sec/index.tsx +3 -0
  419. package/src/plugins/builtin/series-inventory.ts +2 -0
  420. package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
  421. package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
  422. package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
  423. package/src/plugins/builtin/shared/earnings-report.ts +2 -2
  424. package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
  425. package/src/plugins/builtin/shared/job-posting.ts +1 -0
  426. package/src/plugins/builtin/shared/members-menu.tsx +3 -2
  427. package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
  428. package/src/plugins/builtin/shared/report-freshness.ts +11 -2
  429. package/src/plugins/builtin/shared/research-cells.tsx +1 -1
  430. package/src/plugins/builtin/shared/series-cache.ts +4 -4
  431. package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
  432. package/src/plugins/builtin/shared/ticker-company.ts +77 -0
  433. package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
  434. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
  435. package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
  436. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
  437. package/src/plugins/builtin/themes/model.ts +16 -7
  438. package/src/plugins/builtin/themes/pane.tsx +5 -5
  439. package/src/plugins/builtin/thirteenf/api.ts +12 -1
  440. package/src/plugins/builtin/thirteenf/data.ts +7 -7
  441. package/src/plugins/builtin/thirteenf/headless.ts +31 -4
  442. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
  443. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
  444. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
  445. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
  446. package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
  447. package/src/plugins/builtin/ticker-detail/overview/model.ts +11 -3
  448. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
  449. package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
  450. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
  451. package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
  452. package/src/plugins/builtin/time-sales/pane.tsx +31 -19
  453. package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
  454. package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
  455. package/src/plugins/builtin/treasury-daily/client.ts +41 -5
  456. package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
  457. package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
  458. package/src/plugins/builtin/treasury-daily/model.ts +50 -3
  459. package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
  460. package/src/plugins/builtin/vol-surface/client.ts +3 -13
  461. package/src/plugins/builtin/vol-surface/headless.ts +3 -3
  462. package/src/plugins/builtin/vol-surface/index.tsx +1 -1
  463. package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
  464. package/src/plugins/builtin/volatility/index.tsx +2 -2
  465. package/src/plugins/builtin/volatility/pane.tsx +7 -4
  466. package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
  467. package/src/plugins/builtin/yield-curve/client.ts +61 -1
  468. package/src/plugins/builtin/yield-curve/headless.ts +9 -6
  469. package/src/plugins/builtin/yield-curve/index.tsx +34 -10
  470. package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
  471. package/src/plugins/catalog-browser.ts +2 -1
  472. package/src/plugins/catalog-manifest.ts +2 -1
  473. package/src/plugins/host-link.ts +1 -1
  474. package/src/plugins/marketplace/model.ts +18 -5
  475. package/src/portfolio/column-values.ts +11 -7
  476. package/src/portfolio/futures-positions.ts +6 -3
  477. package/src/portfolio/position-quantity.ts +15 -9
  478. package/src/remote/resources.ts +2 -1
  479. package/src/renderers/dom/data-table/row.tsx +26 -0
  480. package/src/renderers/dom/desktop/icons.tsx +7 -0
  481. package/src/renderers/dom/desktop/list-view.tsx +13 -10
  482. package/src/renderers/dom/desktop/query-bar.tsx +4 -2
  483. package/src/renderers/dom/desktop/select-field.tsx +4 -2
  484. package/src/renderers/dom/host/chart-surface.tsx +21 -0
  485. package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
  486. package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
  487. package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
  488. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
  489. package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
  490. package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
  491. package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
  492. package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
  493. package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
  494. package/src/renderers/electrobun/bun/index.ts +26 -2
  495. package/src/renderers/electrobun/bun/window/create.ts +43 -4
  496. package/src/renderers/electrobun/bun/window/events.ts +20 -0
  497. package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
  498. package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
  499. package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
  500. package/src/renderers/electrobun/shared/app-name.ts +13 -0
  501. package/src/renderers/electrobun/shared/protocol.ts +12 -0
  502. package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
  503. package/src/renderers/electrobun/view/main.tsx +24 -1
  504. package/src/renderers/electrobun/view/news-source.ts +5 -1
  505. package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
  506. package/src/renderers/electrobun/view/ui-host.tsx +8 -0
  507. package/src/renderers/electrobun/view/view-ids.ts +85 -0
  508. package/src/renderers/opentui/start.tsx +5 -0
  509. package/src/series/registry.ts +1 -0
  510. package/src/series/types.ts +6 -0
  511. package/src/series/validate.ts +1 -0
  512. package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
  513. package/src/sources/gloomberb-cloud/index.ts +43 -8
  514. package/src/sources/gloomberb-cloud/news.ts +47 -7
  515. package/src/sources/provider-router/financials.ts +15 -3
  516. package/src/sources/provider-router/index.ts +8 -5
  517. package/src/state/ticker-cleanup.ts +50 -0
  518. package/src/sync/controller.ts +4 -3
  519. package/src/sync/core-contributors.ts +90 -31
  520. package/src/telemetry/stall-reports-dom.ts +38 -0
  521. package/src/telemetry/stall-reports-node.ts +75 -0
  522. package/src/telemetry/stall-reports.ts +214 -0
  523. package/src/test-support/preload.ts +14 -0
  524. package/src/theme/themes.ts +10 -0
  525. package/src/tickers/company-name-listing.ts +53 -13
  526. package/src/tickers/company-name.ts +50 -4
  527. package/src/tickers/index-roots.ts +7 -0
  528. package/src/tickers/listing-fate.ts +2 -2
  529. package/src/tickers/listing-venues.ts +15 -3
  530. package/src/tickers/search/index.ts +6 -2
  531. package/src/tickers/search/ranking.ts +5 -1
  532. package/src/tickers/search/upsert.ts +13 -0
  533. package/src/tickers/terminal-codes.ts +19 -0
  534. package/src/tickers/ticker-changes.ts +8 -4
  535. package/src/time-series/alignment.ts +1 -1
  536. package/src/time-series/economic.ts +18 -4
  537. package/src/time-series/field-catalog.ts +1 -1
  538. package/src/time-series/filed-trailing-eps.ts +197 -0
  539. package/src/time-series/fundamentals.ts +44 -1
  540. package/src/time-series/ice-credit.ts +3 -0
  541. package/src/time-series/price-comparison.ts +30 -2
  542. package/src/time-series/resolve-context.ts +3 -1
  543. package/src/time-series/resolve.ts +56 -4
  544. package/src/time-series/session-bar.ts +11 -0
  545. package/src/time-series/spec.ts +3 -2
  546. package/src/time-series/studies.ts +8 -2
  547. package/src/time-series/total-return.ts +160 -0
  548. package/src/time-series/transforms.ts +2 -3
  549. package/src/time-series/types.ts +22 -1
  550. package/src/types/company-from.ts +40 -0
  551. package/src/types/data-provider.ts +7 -0
  552. package/src/types/financials.ts +59 -1
  553. package/src/types/headless.ts +27 -3
  554. package/src/types/news-source.ts +1 -0
  555. package/src/types/plugin.ts +9 -0
  556. package/src/ui/host.tsx +3 -0
  557. package/src/updater/failure.ts +4 -1
  558. package/src/utils/cli-output.ts +42 -1
  559. package/src/utils/crypto-pair.ts +165 -7
  560. package/src/utils/declared-dividends.ts +1 -1
  561. package/src/utils/forward-dividend.ts +2 -0
  562. package/src/utils/fundamentals.ts +60 -8
  563. package/src/utils/fx-market-hours.ts +26 -3
  564. package/src/utils/main-thread-stall.ts +35 -2
  565. package/src/utils/market-capitalization.ts +24 -1
  566. package/src/utils/price-history.ts +7 -1
  567. package/src/utils/shell-word.ts +8 -0
  568. package/src/utils/trailing-dividend.ts +53 -4
  569. package/src/version.ts +2 -2
  570. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
@@ -400,15 +400,21 @@ function resolveVolume(spec: ChartStudySpec, input: ResolvedSeries, color: strin
400
400
  // No bar has volume to plot (spot FX quotes 0 for every bar; a coin's window
401
401
  // can miss every bar's), so the study adds no panel rather than an empty one.
402
402
  if (input.points.length > 0 && !input.points.some((point) => isFiniteNumber(point.volume) && point.volume !== 0)) return [];
403
+ // Left to its own colour, volume follows the candles: the price's colour for
404
+ // a bar that rose, the falling colour for one that fell. A colour picked for it stays flat.
405
+ const directional = !spec.color;
403
406
  const points = [...input.points]
404
407
  .sort((left, right) => left.date.getTime() - right.date.getTime())
405
408
  .flatMap((point) => isFiniteNumber(point.volume)
406
- ? [derivedPoint({ point, value: point.volume }, point.volume)]
409
+ ? [{
410
+ ...derivedPoint({ point, value: point.volume }, point.volume),
411
+ ...(directional && isFiniteNumber(point.open) && isFiniteNumber(point.close) && point.close < point.open ? { fell: true } : {}),
412
+ }]
407
413
  : []);
408
414
  return [outputSeries(spec, input, {
409
415
  label: nameInput ? `Volume ${input.label}` : "Volume",
410
416
  points,
411
- color,
417
+ color: directional ? input.color : color,
412
418
  unit: input.volumeUnit ?? "",
413
419
  unitGroup: "volume",
414
420
  style: "columns",
@@ -0,0 +1,160 @@
1
+ import type { CorporateActionsData } from "../types/financials";
2
+ import { cashInMajorUnits } from "../utils/dividend-cash";
3
+ import { resolveCurrencyUnit } from "../utils/currency-units";
4
+ import { logFailure } from "../utils/log-failure";
5
+ import { dividendReferencePrice } from "../utils/trailing-dividend";
6
+ import { observationDate } from "./price-comparison";
7
+ import { CHART_RESOLUTION_STEP_MS, chartBarSizeStepMs } from "./resolution";
8
+ import type { ResolvedSeries, TimeSeriesPoint } from "./types";
9
+
10
+ /** One cash distribution per share: its ex-date (`YYYY-MM-DD`) and its amount in the listing's major currency unit. */
11
+ export interface CashDistribution {
12
+ exDate: string;
13
+ amount: number;
14
+ }
15
+
16
+ export interface TotalReturnOptions {
17
+ /** The session day a bar belongs to, `YYYY-MM-DD`. */
18
+ dayOf: (point: TimeSeriesPoint) => string;
19
+ /**
20
+ * A bar's price in the distributions' currency unit, or null when the two
21
+ * currencies differ (nothing is converted).
22
+ */
23
+ cashPrice: (price: number) => number | null;
24
+ /**
25
+ * Bars longer than a session (weekly, monthly) are dated at their start, so a
26
+ * distribution belongs to the bar that holds its ex-date, not the first one
27
+ * dated on or after it. This is how many days a bar holds; the last bar holds
28
+ * that many from its own date, and a distribution after that precedes no bar.
29
+ */
30
+ barDays?: number;
31
+ /** Today, `YYYY-MM-DD`: a distribution dated after it has not gone ex. */
32
+ asOf?: string;
33
+ }
34
+
35
+ export interface TotalReturnResult {
36
+ points: TimeSeriesPoint[];
37
+ /** Distributions reinvested inside the series; one before its first bar or after its last is not. */
38
+ applied: number;
39
+ }
40
+
41
+ /** The day a bar of `barDays` days dated `day` ends on; 30-day multiples are calendar months. */
42
+ function barEnd(day: string, barDays: number): string {
43
+ const end = new Date(`${day}T00:00:00Z`);
44
+ if (barDays >= 28 && barDays % 30 === 0) end.setUTCMonth(end.getUTCMonth() + barDays / 30);
45
+ else end.setUTCDate(end.getUTCDate() + barDays);
46
+ return end.toISOString().slice(0, 10);
47
+ }
48
+
49
+ function scaled(value: number | null | undefined, factor: number): number | null | undefined {
50
+ return typeof value === "number" && Number.isFinite(value) ? value * factor : value;
51
+ }
52
+
53
+ /**
54
+ * A price series as its total return: every cash distribution is reinvested at
55
+ * the close of its ex-date bar, so the series moves by (close + cash) over the
56
+ * bar before. Each price is multiplied by the shares the cash has bought
57
+ * since the series began, which leaves its first bar as it was; volume is
58
+ * left alone. A distribution dated at or before the first bar precedes the
59
+ * series, so nothing is reinvested for it.
60
+ */
61
+ export function reinvestDistributions(
62
+ points: readonly TimeSeriesPoint[],
63
+ distributions: readonly CashDistribution[],
64
+ { dayOf, cashPrice, barDays, asOf }: TotalReturnOptions,
65
+ ): TotalReturnResult {
66
+ const ordered = [...points].sort((left, right) => left.date.getTime() - right.date.getTime());
67
+ const holdingBar = barDays !== undefined && barDays > 1;
68
+ const cash = distributions
69
+ .filter((entry) => /^\d{4}-\d{2}-\d{2}$/.test(entry.exDate) && Number.isFinite(entry.amount) && entry.amount > 0
70
+ && (asOf === undefined || entry.exDate <= asOf))
71
+ .sort((left, right) => left.exDate.localeCompare(right.exDate));
72
+ const days = ordered.map(dayOf);
73
+ let factor = 1;
74
+ let applied = 0;
75
+ let next = 0;
76
+ const adjusted = ordered.map((point, index) => {
77
+ const barDay = days[index]!;
78
+ const before = holdingBar ? days[index + 1] ?? barEnd(barDay, barDays!) : null;
79
+ while (next < cash.length) {
80
+ const entry = cash[next]!;
81
+ if (before !== null ? entry.exDate >= before : entry.exDate > barDay) break;
82
+ if (index === 0) { next += 1; continue; }
83
+ const close = point.close ?? point.value;
84
+ // A bar with no price leaves the cash for the next bar that has one.
85
+ if (typeof close !== "number" || !Number.isFinite(close) || close <= 0) break;
86
+ // Cash in another currency than the price buys nothing: the series stays a price series.
87
+ const price = cashPrice(close);
88
+ if (price !== null && price > 0) {
89
+ factor *= 1 + entry.amount / price;
90
+ applied += 1;
91
+ }
92
+ next += 1;
93
+ }
94
+ if (factor === 1) return point;
95
+ return {
96
+ ...point,
97
+ value: scaled(point.value, factor) ?? null,
98
+ ...point.rawValue !== undefined ? { rawValue: scaled(point.rawValue, factor) ?? null } : {},
99
+ ...point.open !== undefined ? { open: scaled(point.open, factor) } : {},
100
+ ...point.high !== undefined ? { high: scaled(point.high, factor) } : {},
101
+ ...point.low !== undefined ? { low: scaled(point.low, factor) } : {},
102
+ ...point.close !== undefined ? { close: scaled(point.close, factor) } : {},
103
+ };
104
+ });
105
+ return { points: adjusted, applied };
106
+ }
107
+
108
+ export interface TotalReturnOutcome {
109
+ series: ResolvedSeries;
110
+ /** Why the series stays a price series, when it does. */
111
+ notice?: string;
112
+ }
113
+
114
+ const DAY_MS = 86_400_000;
115
+
116
+ /** How many days a bar holds when it is longer than a session (a month counts as 30), so a distribution belongs to the bar holding its ex-date. */
117
+ function barDaysLongerThanSession(series: Pick<ResolvedSeries, "barSize" | "historyResolution">): number | undefined {
118
+ const step = series.barSize ? chartBarSizeStepMs(series.barSize)
119
+ : series.historyResolution ? CHART_RESOLUTION_STEP_MS[series.historyResolution] : null;
120
+ return step !== null && step > DAY_MS ? Math.round(step / DAY_MS) : undefined;
121
+ }
122
+
123
+ /**
124
+ * A price series as its total return, from the cash history its listing pays.
125
+ * The series says which basis it ended up on: `total` once its distributions
126
+ * are reinvested (a listing that pays none is its price), `price` with the
127
+ * reason when the history cannot be read, covers no distributions, or is in
128
+ * another currency than the prices. Nothing is converted.
129
+ */
130
+ export async function totalReturnSeries(
131
+ series: ResolvedSeries,
132
+ loadActions: () => Promise<CorporateActionsData | null>,
133
+ priceCurrency: string | undefined,
134
+ asOf: Date,
135
+ ): Promise<TotalReturnOutcome> {
136
+ const priceOnly = (notice: string): TotalReturnOutcome => ({ series: { ...series, returnBasis: "price" }, notice });
137
+ const actions = await loadActions().catch(logFailure("total-return", "Loading distribution history failed", null));
138
+ if (!actions) return priceOnly("distribution history unavailable; showing the price return.");
139
+ const cash = cashInMajorUnits(actions);
140
+ const dividends = Array.isArray(cash.dividends) ? cash.dividends : [];
141
+ if (cash.coverage?.dividends === "unavailable" || (cash.coverage?.dividends !== "available" && dividends.length === 0)) {
142
+ return priceOnly("distribution history unavailable; showing the price return.");
143
+ }
144
+ const currency = resolveCurrencyUnit(cash.currency).currency;
145
+ if (dividends.length > 0 && !currency) return priceOnly("distribution currency unknown; showing the price return.");
146
+ if (dividends.length > 0 && !resolveCurrencyUnit(priceCurrency).currency) {
147
+ return priceOnly("price currency unknown; showing the price return.");
148
+ }
149
+ if (dividends.length > 0 && dividendReferencePrice(1, priceCurrency, currency) === null) {
150
+ return priceOnly(`distributions are in ${currency}, not the price currency; showing the price return.`);
151
+ }
152
+ const { points } = reinvestDistributions(series.points, dividends, {
153
+ dayOf: (point) => observationDate(point.date.getTime(), series),
154
+ cashPrice: (price) => dividendReferencePrice(price, priceCurrency, currency),
155
+ barDays: barDaysLongerThanSession(series),
156
+ asOf: asOf.toISOString().slice(0, 10),
157
+ });
158
+ // "ECC Close" is its price: a label the chart wrote from the field says what the series is now.
159
+ return { series: { ...series, points, returnBasis: "total", label: series.label.replace(/ (?:Close|Price|Open|High|Low)$/, " Total return") } };
160
+ }
@@ -1,6 +1,7 @@
1
1
  import type { ResolvedSeries, SeriesTransform, TimeSeriesPoint } from "./types";
2
2
  import { calendarMonthsBefore } from "../utils/calendar-date";
3
3
  import { isFiniteNumber } from "../utils/guards";
4
+ import { isOhlcSeriesStyle } from "./spec";
4
5
 
5
6
  const DAY_MS = 24 * 60 * 60 * 1_000;
6
7
  // Volume is separate supporting data, not another price. A volume series has
@@ -166,9 +167,7 @@ export function applyResolvedSeriesTransform(
166
167
  unit: percent ? "%" : transform === "index100" ? "index" : "log",
167
168
  unitGroup: percent ? "percent" : transform === "index100" ? "index" : "log",
168
169
  dataShape: "scalar",
169
- style: series.style === "candles" || series.style === "ohlc" || series.style === "hlc"
170
- ? "line"
171
- : series.style,
170
+ style: isOhlcSeriesStyle(series.style) ? "line" : series.style,
172
171
  points: applySeriesTransform(series.points, transform, options),
173
172
  };
174
173
  }
@@ -10,7 +10,7 @@ export type SeriesPeriod = "auto" | "daily" | "weekly" | "monthly" | "quarterly"
10
10
  * "band" draws a line through each point's value inside a shaded range from
11
11
  * its `low` to its `high`: an average within its historical range.
12
12
  */
13
- export type SeriesStyle = "line" | "area" | "step" | "columns" | "points" | "candles" | "ohlc" | "hlc" | "band";
13
+ export type SeriesStyle = "line" | "area" | "step" | "columns" | "points" | "candles" | "hollow" | "heikin-ashi" | "ohlc" | "hlc" | "band";
14
14
  export type SeriesTransform = "raw" | "percent" | "index100" | "yoy" | "qoq" | "log";
15
15
  export type SeriesAxis = "auto" | "left" | "right";
16
16
  export type SeriesInterpolation = "none" | "step-after";
@@ -110,6 +110,11 @@ export interface ChartViewportSpec {
110
110
  maxPoints?: number;
111
111
  /** Intraday market history includes pre-market and after-hours bars where the venue has them. */
112
112
  extendedHours?: boolean;
113
+ /**
114
+ * Security price series are drawn as total returns: each cash distribution
115
+ * is reinvested at the close of its ex-date bar. Price returns otherwise.
116
+ */
117
+ totalReturn?: boolean;
113
118
  }
114
119
 
115
120
  export interface ChartSpec {
@@ -141,6 +146,8 @@ export interface TimeSeriesPoint {
141
146
  low?: number | null;
142
147
  close?: number | null;
143
148
  volume?: number | null;
149
+ /** The bar this figure traded in closed below its open: volume draws it in the falling colour. */
150
+ fell?: boolean;
144
151
  periodLabel?: string;
145
152
  /**
146
153
  * A financial statement period as the FA columns name it: `FY2025`, `Q3 FY26`,
@@ -183,6 +190,12 @@ export interface ResolvedSeries {
183
190
  unit: string;
184
191
  /** Unit of rawValue, retained when presentation transforms change unit. */
185
192
  rawUnit?: string;
193
+ /**
194
+ * What a price series' returns are, set when the chart asked for total
195
+ * returns: `total` once its distributions are reinvested, `price` when the
196
+ * source gave none to reinvest (`warning` says why).
197
+ */
198
+ returnBasis?: "total" | "price";
186
199
  unitGroup: string;
187
200
  /**
188
201
  * The unit the legend states beside each value, read bare as FUT prints its
@@ -311,6 +324,14 @@ export interface ChartResolutionResult {
311
324
  loading: boolean;
312
325
  errors: string[];
313
326
  warnings: string[];
327
+ /**
328
+ * Where a series' source starts after the range asked for, and why (a
329
+ * publisher's cap on history), as `warnings` also carries it, so a report
330
+ * says it where it would otherwise print a range that quietly starts later.
331
+ */
332
+ coverageNotices?: string[];
333
+ /** What the price levels of a total-return chart without a comparison are (`warnings` also carries it). */
334
+ returnBasisNotice?: string;
314
335
  /**
315
336
  * Each charted listing that no longer trades: its symbol and label
316
337
  * ("Delisted Oct 27, 2022"), which `warnings` carries as "TWTR: Delisted Oct 27, 2022".
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The listing a pane's company data comes from when it is not the listing on
3
+ * screen: a receipt or a foreign line (SKHY, HY9H in Frankfurt) reads its
4
+ * company's home line (000660 on KRX) for news, filings, holders, hiring and
5
+ * the other company panes. Gloom Cloud names it on those answers.
6
+ */
7
+ export interface CompanyFrom {
8
+ symbol: string;
9
+ /** The venue's code (KRX, FWB2, NASDAQ). */
10
+ exchange: string;
11
+ /** The company's name. */
12
+ name?: string;
13
+ }
14
+
15
+ const US_VENUES = new Set(["NASDAQ", "NYSE", "AMEX", "ARCA", "BATS", "CBOE", "IEX"]);
16
+
17
+ /** The listing as the note names it: `000660 KRX`, or `SKHY` on a US venue. */
18
+ export function companyFromLabel(from: CompanyFrom): string {
19
+ const exchange = from.exchange.trim().toUpperCase();
20
+ return exchange && !US_VENUES.has(exchange) ? `${from.symbol} ${exchange}` : from.symbol;
21
+ }
22
+
23
+ /** The note's listing from an answer's field, or null when the answer names none or something malformed. */
24
+ export function readCompanyFrom(value: unknown): CompanyFrom | null {
25
+ if (!value || typeof value !== "object") return null;
26
+ const { symbol, exchange, name } = value as Record<string, unknown>;
27
+ if (typeof symbol !== "string" || !symbol.trim() || typeof exchange !== "string") return null;
28
+ return { symbol: symbol.trim(), exchange: exchange.trim(), ...(typeof name === "string" && name.trim() ? { name: name.trim() } : {}) };
29
+ }
30
+
31
+ /**
32
+ * What a report says under company data read from another listing, as the
33
+ * pane's footer does: "Company data from 000660 KRX (SK hynix)." Empty when
34
+ * the data is the listing's own, so a report's notes spread it as they are.
35
+ */
36
+ export function companyDataNotes(from: CompanyFrom | null | undefined): string[] {
37
+ if (!from) return [];
38
+ const listing = companyFromLabel(from);
39
+ return [`Company data from ${listing}${from.name && from.name !== from.symbol ? ` (${from.name})` : ""}.`];
40
+ }
@@ -1,3 +1,4 @@
1
+ import type { CompanyFrom } from "./company-from";
1
2
  import type { ExchangeRateSnapshot } from "./exchange-rate";
2
3
  import type {
3
4
  AnalystResearchData,
@@ -61,6 +62,12 @@ export interface SecFilingItem {
61
62
  * series list reaches).
62
63
  */
63
64
  fundSeries?: boolean;
65
+ /**
66
+ * The company's home line the filing was read from, when the listing asked
67
+ * is a receipt or a foreign line with no filer of its own (HY9H in
68
+ * Frankfurt reads SK hynix's). Absent on the listing's own filings.
69
+ */
70
+ companyFrom?: CompanyFrom;
64
71
  }
65
72
 
66
73
  export interface SecFilingDocument {
@@ -1,3 +1,4 @@
1
+ import type { CompanyFrom } from "./company-from";
1
2
  import type { PerpBoardRow } from "../api-client/perps";
2
3
  import type { PriceBasis } from "./instrument";
3
4
 
@@ -87,6 +88,26 @@ export interface DepositaryPair {
87
88
  ordinary: string[];
88
89
  }
89
90
 
91
+ /**
92
+ * A Buenos Aires CEDEAR's ratio from BYMA's published list, as Gloom Cloud
93
+ * serves it: 20 AAPL:BYMA are 1 Apple share. Its market cap is the company's,
94
+ * in the currency of the listing it stands for.
95
+ */
96
+ export interface CedearRatio {
97
+ /** The CEDEAR's listing: AAPL:BYMA. */
98
+ listing: string;
99
+ /** CEDEARs that make `shares` of the company's shares: 20 for Apple. */
100
+ receipts: number;
101
+ /** The company's shares `receipts` CEDEARs stand for: 1 for Apple, 8 for BABA's 9 (one receipt of 8 shares). */
102
+ shares: number;
103
+ /** The company as the list names it: "Apple Inc.". */
104
+ company: string;
105
+ /** The listing it stands for, as a ticker: AAPL, VALE3.SA. */
106
+ underlying: string;
107
+ /** The date of BYMA's list. */
108
+ listedAt: string;
109
+ }
110
+
90
111
  /** How a depositary receipt relates to the ordinary shares, as Gloom Cloud states it. */
91
112
  interface DepositaryReceiptFacts {
92
113
  /** Whether the listing is a depositary receipt; absent when unknown. */
@@ -95,6 +116,8 @@ interface DepositaryReceiptFacts {
95
116
  adrRatio?: number;
96
117
  /** The receipt and its home lines, on the receipt's records and each home line's. */
97
118
  depositaryPair?: DepositaryPair;
119
+ /** A Buenos Aires CEDEAR's ratio, on its records. */
120
+ cedearRatio?: CedearRatio;
98
121
  }
99
122
 
100
123
  export interface Quote extends DepositaryReceiptFacts {
@@ -330,7 +353,12 @@ export interface Fundamentals extends DepositaryReceiptFacts {
330
353
  /** Set where Gloom Cloud checked `enterpriseValue` against market cap + total debt - cash; pairs with that value only. */
331
354
  enterpriseValueCheck?: EnterpriseValueCheck;
332
355
  /** Explicit provider retractions; omitted fields alone remain eligible for fallback. */
333
- unavailableFields?: Array<"enterpriseValue" | "enterpriseToRevenue">;
356
+ unavailableFields?: Array<"enterpriseValue" | "enterpriseToRevenue" | "marketCap">;
357
+ /**
358
+ * Why `marketCap` is retracted, with "marketCap" in `unavailableFields`: a depositary receipt its source priced as
359
+ * an ordinary share (the receipt's price times the company's ordinary shares), with no ratio to convert them.
360
+ */
361
+ marketCapUnavailable?: "receipt-priced-as-ordinary";
334
362
  operatingCashFlow?: number;
335
363
  freeCashFlow?: number;
336
364
  dividendYield?: number;
@@ -345,6 +373,18 @@ export interface Fundamentals extends DepositaryReceiptFacts {
345
373
  revenue?: number;
346
374
  netIncome?: number;
347
375
  eps?: number;
376
+ /**
377
+ * Where `eps` and `trailingPE` come from: `filed-quarters` when `eps` is the
378
+ * diluted EPS of the last four filed quarters (or the fiscal year they end
379
+ * with), as PEB and GE read the statements, its last period ending
380
+ * `trailingEpsPeriodEnd`, and `trailingPE` the price over it; `served` when
381
+ * those quarters give none (or one that cannot be priced: see
382
+ * time-series/filed-trailing-eps) and both are Gloom Cloud's trailing
383
+ * figures, including a P/E it serves with no EPS (a fund's). Absent when
384
+ * there is neither an EPS nor a P/E.
385
+ */
386
+ trailingEpsSource?: "filed-quarters" | "served";
387
+ trailingEpsPeriodEnd?: string;
348
388
  operatingMargin?: number;
349
389
  profitMargin?: number;
350
390
  revenueGrowth?: number;
@@ -419,6 +459,8 @@ export interface HolderData extends DepositaryReceiptFacts {
419
459
  asOf?: string;
420
460
  summary?: HolderSummary;
421
461
  holders: HolderRecord[];
462
+ /** The company's home line the data is read from, when not the listing asked (a receipt, a foreign line). */
463
+ companyFrom?: CompanyFrom;
422
464
  }
423
465
 
424
466
  interface AnalystPriceTarget {
@@ -497,6 +539,8 @@ export interface DeclaredDividendAction {
497
539
  declarationDate?: string;
498
540
  recordDate?: string;
499
541
  payDate?: string;
542
+ /** A one-off beside the regular payments (AIOK's $3.50), as the server marks it. */
543
+ special?: true;
500
544
  }
501
545
 
502
546
  export interface SplitAction {
@@ -556,10 +600,24 @@ export interface CorporateActionsData {
556
600
  dividends: DividendAction[];
557
601
  /** Declared and unpaid, for the listings Gloom Cloud has them for; absent elsewhere and from older servers. */
558
602
  declaredDividends?: DeclaredDividendAction[];
603
+ /**
604
+ * What each paid row was declared as when the company declares in another
605
+ * currency than the listing pays (BP's dollars beside its London pence), per
606
+ * ordinary share, keyed by the paid row's ex-date. Absent elsewhere and from older servers.
607
+ */
608
+ declaredCash?: DeclaredCash;
609
+ /** Set when `dividends` is the last good history standing in for a refresh that could not read it: when that history was read. */
610
+ dividendsAsOf?: string;
559
611
  splits: SplitAction[];
560
612
  earnings: EarningsAction[];
561
613
  }
562
614
 
615
+ /** Paid rows as their company declared them, in `currency` (its major unit). */
616
+ export interface DeclaredCash {
617
+ currency: string;
618
+ rows: DividendAction[];
619
+ }
620
+
563
621
  export interface CompanyProfile {
564
622
  description?: string;
565
623
  /**
@@ -34,6 +34,12 @@ export interface HeadlessPaneArgumentDef {
34
34
  examples?: readonly string[];
35
35
  }
36
36
 
37
+ /** A list the catalog prints under a function's options, such as the sector names a screen's criteria match. */
38
+ export interface HeadlessPaneReference {
39
+ title: string;
40
+ items: readonly string[];
41
+ }
42
+
37
43
  export interface HeadlessPaneOptionValue {
38
44
  value: string;
39
45
  aliases?: string[];
@@ -83,6 +89,8 @@ export interface HeadlessPaneOptionDef {
83
89
  * absent.
84
90
  */
85
91
  valueWords?: number;
92
+ /** The value is a comma list (`--metric trailingPE,dividendYieldPercent`), and the flag typed again adds to it. */
93
+ list?: boolean;
86
94
  /** The option in use as typed after the function, such as `--expiration 2028-01-21`; the catalog shows it as an example. */
87
95
  example?: string;
88
96
  /** The example runs without the function's optional argument, as an option that works only then does (`fn INS --side sell`). */
@@ -138,8 +146,10 @@ export interface HeadlessPaneContext {
138
146
  * known. A code several companies list (BDI) throws asking which, with
139
147
  * `quote`'s message; `argumentWith(key)` is the function's argument naming
140
148
  * that listing instead (`BDI:TSX`), for the retry each listing offers.
149
+ * `coinNote` is what `quote` says of a bare coin ticker read as a listing
150
+ * that is not the coin (ETH, the Grayscale fund), for the report's warnings.
141
151
  */
142
- resolveListing?: (symbol: string, argumentWith: (key: string) => string) => Promise<{ symbol: string; exchange?: string; name?: string } | null>;
152
+ resolveListing?: (symbol: string, argumentWith: (key: string) => string) => Promise<{ symbol: string; exchange?: string; name?: string; coinNote?: string } | null>;
143
153
  /**
144
154
  * What a plugin keeps on this device only (its `ctx.resume` state, never
145
155
  * synced), such as ALRT's log of the alerts the app fired; null when none.
@@ -393,6 +403,12 @@ export interface HeadlessPaneFreshness {
393
403
  * `newest story` dates a news list `newest story Tue 29 Sep 2026 20:17 UTC`.
394
404
  */
395
405
  asOfLabel?: string;
406
+ /**
407
+ * Say when the rows were retrieved too, after the newest one's date: a list
408
+ * dated by its newest filing was not read then (`newest filing Tue 27 Oct
409
+ * 2026 · retrieved Wed 28 Oct 2026 19:07 UTC`).
410
+ */
411
+ retrievedToo?: boolean;
396
412
  /** What the figures are, said after the status: `funding = current estimate, pays 08:00 UTC`. */
397
413
  note?: string;
398
414
  /**
@@ -602,6 +618,8 @@ export interface HeadlessSeries {
602
618
  interval?: string;
603
619
  /** Decimal places the text report prints the latest value to (a correlation to 3); JSON and CSV keep every digit. */
604
620
  textDecimals?: number;
621
+ /** The text report prints the latest value as the tables print an amount (63.33B), for a company's reported total; JSON and CSV keep every digit. */
622
+ textCompact?: boolean;
605
623
  /** Each point's `value` is its bar's close: `--csv` and `--ndjson` write Close alone, JSON keeps both. */
606
624
  valueIsClose?: boolean;
607
625
  points: HeadlessSeriesPoint[];
@@ -615,6 +633,8 @@ export interface HeadlessSeriesResult extends HeadlessPaneResultBase {
615
633
  export interface HeadlessSnapshotResult extends HeadlessPaneResultBase {
616
634
  asOf: string | number | Date;
617
635
  items: HeadlessPaneRow[];
636
+ /** What the report says when `items` is empty, in place of "No data.": why there are none. */
637
+ empty?: string;
618
638
  }
619
639
 
620
640
  export interface HeadlessPaneResultByShape {
@@ -640,6 +660,8 @@ export interface HeadlessPaneDefinition<Shape extends HeadlessPaneShape = Headle
640
660
  intents?: string[];
641
661
  dataRequirements?: string[];
642
662
  limitations?: string[];
663
+ /** Names an option's values match, listed by the catalog under each title: EQS's sectors and industries. */
664
+ reference?: HeadlessPaneReference[];
643
665
  screenshotReadiness?: "ready" | "partial" | "live-dom" | "unsupported";
644
666
  };
645
667
  /**
@@ -672,9 +694,11 @@ export interface HeadlessPaneDefinition<Shape extends HeadlessPaneShape = Headle
672
694
  /**
673
695
  * A listing the function has no data for, answered before any request with
674
696
  * what exists instead: OPX and OVDV answer a coin nothing lists options on
675
- * (SOL-USD) with BTC and ETH options. `key` is the listing as read.
697
+ * (SOL-USD) with BTC and ETH options, and OVDV, VCA and HIVG an FX pair with
698
+ * the currency funds' options. `key` is the listing as read; `details` is
699
+ * the line after `message`, left out when the message says it all.
676
700
  */
677
- refuseSymbol?(key: string): { message: string; details: string } | null;
701
+ refuseSymbol?(key: string): { message: string; details?: string } | null;
678
702
  /**
679
703
  * `fn` exits 1 when the report has no name with data: no rows, or every row
680
704
  * is one of `unavailableSymbols`. A list that gives data for some names
@@ -1,4 +1,5 @@
1
1
  export type {
2
+ NewsAccess,
2
3
  NewsArticle,
3
4
  NewsQuery,
4
5
  NewsQueryState,
@@ -20,6 +20,7 @@ export type {
20
20
  HeadlessPaneOptionType,
21
21
  HeadlessPaneOptionValue,
22
22
  HeadlessPaneOptionValues,
23
+ HeadlessPaneReference,
23
24
  HeadlessPaneResult,
24
25
  HeadlessPaneResultByShape,
25
26
  HeadlessPaneRow,
@@ -529,6 +530,14 @@ export interface PaneTemplateDef {
529
530
  description: string;
530
531
  keywords?: string[];
531
532
  shortcut?: PaneTemplateShortcut;
533
+ /**
534
+ * False leaves the function out of every list and search of functions
535
+ * (command-bar browse, search and assistant, Help, `gloom catalog` and
536
+ * search, the plugin directory, chat suggestions) while typing its code
537
+ * still opens it, `fn` and `shot` still run it and its help still answers.
538
+ * ATTN until it publishes (attention/listing.ts).
539
+ */
540
+ listed?: boolean;
532
541
  /** Template-specific headless model. Takes precedence over the pane-level model. */
533
542
  headless?: HeadlessPaneDefinition;
534
543
  /**
package/src/ui/host.tsx CHANGED
@@ -82,6 +82,8 @@ export interface ChartVectorShape {
82
82
  gradient?: readonly string[];
83
83
  /** Opacity of the whole shape, so others can recede behind a highlighted one. */
84
84
  opacity?: number;
85
+ /** Text set just above the shape's first point, such as a retracement level's ratio and price. */
86
+ label?: string;
85
87
  }
86
88
 
87
89
  interface TextEditBuffer {
@@ -508,6 +510,7 @@ export interface HostCheckboxProps {
508
510
  interface HostQueryBarOption {
509
511
  value: string;
510
512
  label: string;
513
+ group?: string;
511
514
  description?: string;
512
515
  disabled?: boolean;
513
516
  selected: boolean;
@@ -24,7 +24,7 @@ const UPDATE_FAILURE_CODES = [
24
24
  "no_build",
25
25
  /** The download stopped before the whole file arrived. */
26
26
  "interrupted",
27
- /** The file arrived whole but does not match its checksum, or does not unpack. */
27
+ /** The download does not match its checksum, or does not unpack (part of it is missing or damaged). */
28
28
  "corrupt",
29
29
  "permission_denied",
30
30
  /** The macOS app runs from where it cannot be replaced: a disk image, App Translocation, another volume. */
@@ -112,6 +112,9 @@ export function classifyUpdateError(error: unknown): UpdateFailureCode {
112
112
  if (status) return httpStatusFailureCode(Number(status));
113
113
  if (/checksum mismatch|hash mismatch/i.test(message)) return "corrupt";
114
114
  if (/zstd|decompress|gunzip|incorrect header|unexpected end of (?:file|data)/i.test(message)) return "corrupt";
115
+ // Bun.Archive on a tar that does not read: cut off, damaged, or none at all.
116
+ // Unpacking says only "ReadError", whatever stopped it.
117
+ if (/\bReadError\b|truncated tar|damaged tar|unrecognized archive format/i.test(message)) return "corrupt";
115
118
  if (/timed? ?out/i.test(message)) return "timeout";
116
119
  if (/rate limit/i.test(message)) return "rate_limited";
117
120
  if (/read-only file system/i.test(message)) return "read_only";
@@ -74,6 +74,47 @@ const PLAIN_EMPTY_CELL = "—";
74
74
  let colorEnabledOverride: boolean | null = null;
75
75
  let widthOverride: number | null = null;
76
76
  let plainOverride: boolean | null = null;
77
+ /** Output is being rendered for a file (`renderForFile`), which has no terminal width. */
78
+ let renderingForFile = false;
79
+
80
+ /** Renders for a file from here on; the function returned puts the terminal's rendering back. */
81
+ function enterFileRendering(): () => void {
82
+ const saved = { color: colorEnabledOverride, plain: plainOverride, file: renderingForFile };
83
+ colorEnabledOverride = false;
84
+ plainOverride = true;
85
+ renderingForFile = true;
86
+ return () => {
87
+ colorEnabledOverride = saved.color;
88
+ plainOverride = saved.plain;
89
+ renderingForFile = saved.file;
90
+ };
91
+ }
92
+
93
+ /**
94
+ * Output rendered for a file (`fn --output`) as a redirect gets it: plain,
95
+ * without colour, and fitted to no width but `--width`'s.
96
+ */
97
+ export function renderForFile<T>(render: () => T): T {
98
+ const restore = enterFileRendering();
99
+ try {
100
+ return render();
101
+ } finally {
102
+ restore();
103
+ }
104
+ }
105
+
106
+ /**
107
+ * `renderForFile` around work that renders as it loads: a report whose text is
108
+ * drawn while it is built is drawn for the file, though stdout is a terminal.
109
+ */
110
+ export async function buildForFile<T>(build: () => Promise<T>): Promise<T> {
111
+ const restore = enterFileRendering();
112
+ try {
113
+ return await build();
114
+ } finally {
115
+ restore();
116
+ }
117
+ }
77
118
 
78
119
  function colorEnabled(): boolean {
79
120
  if (colorEnabledOverride != null) return colorEnabledOverride;
@@ -176,7 +217,7 @@ export function setCliWidthOverride(value: number | null): void {
176
217
  /** Columns the output is fitted to: `--width`, else the terminal's, or null when output is piped or redirected. */
177
218
  export function cliTerminalWidth(): number | null {
178
219
  if (widthOverride != null) return widthOverride;
179
- if (!process.stdout.isTTY) return null;
220
+ if (renderingForFile || !process.stdout.isTTY) return null;
180
221
  const columns = process.stdout.columns;
181
222
  return typeof columns === "number" && columns > 0 ? columns : null;
182
223
  }