@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
@@ -1,5 +1,5 @@
1
1
  import { displayWidth } from "../../../../utils/format";
2
- import { tableColumnLeadGap } from "../../table-layout";
2
+ import { fitTableCellText, tableColumnLeadGap } from "../../table-layout";
3
3
 
4
4
  /**
5
5
  * Where the viewport cuts a column: `inset` cells hidden on its left (past the
@@ -46,6 +46,31 @@ export function cutColumnText(
46
46
  return `${" ".repeat(start - clip.inset)}${body}${" ".repeat(shownEnd - end)}`;
47
47
  }
48
48
 
49
+ /** A note uses only spare room: neither the label nor part of the note is cut to fit it. */
50
+ export function fitColumnCellText(
51
+ text: string,
52
+ width: number,
53
+ align: string | undefined,
54
+ clip: DataTableColumnClip | null,
55
+ note?: string,
56
+ ): { text: string; note?: string; padding?: string } {
57
+ const fit = (value: string) => clip
58
+ ? cutColumnText(value, align, width, clip, (body, shown) => fitTableCellText(body, shown, align))
59
+ : width > 0 ? fitTableCellText(value, width, align) : "";
60
+ const combined = note ? `${text} ${note}` : "";
61
+ if (combined && displayWidth(combined) <= (clip?.width ?? width)) {
62
+ const fitted = fit(combined);
63
+ const start = fitted.indexOf(combined);
64
+ // A right-aligned value may fit the visible width but sit beyond its edge.
65
+ if (start >= 0) return {
66
+ text: fitted.slice(0, start + text.length + 1),
67
+ note,
68
+ padding: fitted.slice(start + combined.length),
69
+ };
70
+ }
71
+ return { text: fit(text) };
72
+ }
73
+
49
74
  function sameClip(a: DataTableColumnClip | null, b: DataTableColumnClip | null): boolean {
50
75
  return a === b || (a !== null && b !== null && a.inset === b.inset && a.width === b.width);
51
76
  }
@@ -24,6 +24,8 @@ export type DataTableColumn = Pick<
24
24
 
25
25
  export interface DataTableCell {
26
26
  text: string;
27
+ /** Muted context after `text`, shown whole only when both fit on one line. Does not change the exported value. */
28
+ note?: string;
27
29
  /**
28
30
  * What CSV export writes instead of `text`: the full-precision number behind
29
31
  * it (3.45 for +3.45%, 1234567890 for 1.23B), or a date as an ISO string or
@@ -7,6 +7,7 @@ import { Box, Text, TextAttributes, useUiCapabilities } from "../../ui";
7
7
  import { truncateToDisplayWidth } from "../../utils/format";
8
8
  import { CountryFlagLead } from "./country-flag";
9
9
  import { usePaneLinkMenuEntry } from "./external-link";
10
+ import { ICON_GLYPHS, Icon, type IconName } from "./icon";
10
11
  import { titleCase } from "./header-case";
11
12
  import { statToneColor, type StatTone } from "./stat-grid";
12
13
 
@@ -199,6 +200,10 @@ export interface BadgeProps {
199
200
  /** For a domain scale, such as a sentiment score or chart series. */
200
201
  color?: string;
201
202
  variant?: "subtle" | "solid";
203
+ /** A mark before the label, in its colour: an SVG on the desktop, its glyph in the terminal. */
204
+ icon?: IconName;
205
+ /** False for a label that is not English to translate, such as a username. */
206
+ translate?: boolean;
202
207
  }
203
208
 
204
209
  /**
@@ -210,8 +215,9 @@ export const NATIVE_BADGE_STYLE = { height: "calc(100% - 2px)", borderRadius: "v
210
215
  /** How far a subtle chip's fill is tinted toward its tone. */
211
216
  export const BADGE_TINT = 0.28;
212
217
  const NATIVE_BADGE_TEXT_STYLE = { fontWeight: "var(--gloom-weight-regular)" };
218
+ const NATIVE_ICON_BADGE_STYLE = { ...NATIVE_BADGE_STYLE, gap: "var(--gloom-space-xs)" };
213
219
 
214
- export function Badge({ label, tone = "neutral", color, variant = "subtle" }: BadgeProps) {
220
+ export function Badge({ label, tone = "neutral", color, variant = "subtle", icon, translate = true }: BadgeProps) {
215
221
  const colors = useThemeColors();
216
222
  const { nativePaneChrome } = useUiCapabilities();
217
223
  const accent = color ?? (tone === "neutral" ? colors.textDim : tone === "accent" ? colors.borderFocused : colors[tone]);
@@ -220,10 +226,19 @@ export function Badge({ label, tone = "neutral", color, variant = "subtle" }: Ba
220
226
  const background = neutral ? colors.selected : solid ? accent : blendHex(colors.bg, accent, BADGE_TINT);
221
227
  const baseForeground = neutral ? colors.selectedText : solid ? colors.bg : accent;
222
228
  const foreground = blendForContrast(baseForeground, background, higherContrast("#000000", "#ffffff", background), 4.5);
229
+ const text = translate ? t(label) : label;
230
+ const nativeIcon = nativePaneChrome && icon;
231
+ // The terminal draws the icon's glyph as text in front of the label.
232
+ const shown = icon && !nativePaneChrome ? [ICON_GLYPHS[icon], text].filter(Boolean).join(" ") : text;
223
233
  const chip = (
224
234
  <Box height={nativePaneChrome ? undefined : 1} paddingX={1} backgroundColor={background}
225
- data-gloom-ui="badge" justifyContent={nativePaneChrome ? "center" : undefined} style={nativePaneChrome ? NATIVE_BADGE_STYLE : undefined}>
226
- <Text fg={foreground} attributes={TextAttributes.BOLD} style={nativePaneChrome ? NATIVE_BADGE_TEXT_STYLE : undefined}>{t(label)}</Text>
235
+ data-gloom-ui="badge" {...(nativeIcon ? { flexDirection: "row", alignItems: "center" } as const : {})}
236
+ justifyContent={nativePaneChrome && !icon ? "center" : undefined}
237
+ style={nativePaneChrome ? icon ? NATIVE_ICON_BADGE_STYLE : NATIVE_BADGE_STYLE : undefined}>
238
+ {nativeIcon ? <Icon name={nativeIcon} color={foreground} /> : null}
239
+ {shown || !nativeIcon ? (
240
+ <Text fg={foreground} attributes={TextAttributes.BOLD} style={nativePaneChrome ? NATIVE_BADGE_TEXT_STYLE : undefined}>{shown}</Text>
241
+ ) : null}
227
242
  </Box>
228
243
  );
229
244
  return nativePaneChrome ? <Box height={1} flexShrink={0} justifyContent="center">{chip}</Box> : chip;
@@ -38,7 +38,8 @@ export type IconName =
38
38
  | "team"
39
39
  | "pro"
40
40
  | "clock"
41
- | "edit";
41
+ | "edit"
42
+ | "mail";
42
43
 
43
44
  /** Terminal glyph for each icon; the desktop host draws SVG. */
44
45
  export const ICON_GLYPHS: Record<IconName, string> = {
@@ -84,6 +85,8 @@ export const ICON_GLYPHS: Record<IconName, string> = {
84
85
  // Time, for data that arrives late (a delayed quote).
85
86
  clock: "◷",
86
87
  edit: "✎",
88
+ // A message written to you (a DM): text presentation, one cell.
89
+ mail: "✉",
87
90
  };
88
91
 
89
92
  export interface IconProps {
@@ -15,6 +15,7 @@ export interface ListViewItem {
15
15
  description?: string;
16
16
  detail?: string;
17
17
  category?: string;
18
+ /** `heading` draws a muted group label; pair it with `disabled: true`. */
18
19
  kind?: string;
19
20
  right?: string;
20
21
  checked?: boolean;
@@ -148,6 +149,7 @@ function DefaultRow({
148
149
  selected: boolean;
149
150
  }) {
150
151
  const colors = useThemeColors();
152
+ if (item.kind === "heading") return <Text fg={colors.textMuted}>{t(item.label)}</Text>;
151
153
  // The label gives way to the detail: a long label is cut with an ellipsis
152
154
  // instead of wrapping under the row or running into the right column.
153
155
  return (
@@ -289,6 +291,7 @@ export function ListView({
289
291
  const rowHoverBg = hoverBgColor ?? hoverBg(colors);
290
292
  const selectedItem = selectedIndex >= 0 ? items[selectedIndex] : undefined;
291
293
  const activeScrollIndex = scrollIndex ?? selectedIndex;
294
+ const headingAboveSelection = items[activeScrollIndex - 1]?.kind === "heading";
292
295
  const terminalRowHeight = rowHeight ?? 1;
293
296
  const terminalRowGap = rowGap ?? 0;
294
297
  const rowStride = terminalRowHeight + terminalRowGap;
@@ -299,13 +302,14 @@ export function ListView({
299
302
  if (!sb) return;
300
303
  const safeIndex = Math.min(activeScrollIndex, items.length - 1);
301
304
  const viewportH = Math.max(sb.viewport?.height ?? 1, 1);
302
- const rowTop = safeIndex * rowStride;
305
+ const rowTop = Math.max(0, safeIndex - (headingAboveSelection ? 1 : 0)) * rowStride;
306
+ const rowBottom = safeIndex * rowStride + terminalRowHeight;
303
307
  if (rowTop < sb.scrollTop) {
304
308
  sb.scrollTo(rowTop);
305
- } else if (rowTop + terminalRowHeight > sb.scrollTop + viewportH) {
306
- sb.scrollTo(rowTop + terminalRowHeight - viewportH);
309
+ } else if (rowBottom > sb.scrollTop + viewportH) {
310
+ sb.scrollTo(rowBottom - viewportH);
307
311
  }
308
- }, [activeScrollIndex, autoScrollToIndex, items.length, rowStride, scrollable, terminalRowHeight]);
312
+ }, [activeScrollIndex, autoScrollToIndex, headingAboveSelection, items.length, rowStride, scrollable, terminalRowHeight]);
309
313
  useEffect(revealActiveRow, [revealActiveRow]);
310
314
  // A list that opens on a row past the fold has nothing to scroll until its
311
315
  // first computed layout sizes the viewport and the rows.
@@ -12,6 +12,7 @@ import type { QueryBarFilter, QueryBarSearch, QueryBarView } from "./query-bar";
12
12
  interface ChoiceOption {
13
13
  value: string;
14
14
  label: string;
15
+ group?: string;
15
16
  description?: string;
16
17
  disabled?: boolean;
17
18
  color?: string;
@@ -70,6 +71,7 @@ export function useQueryBarKeys({
70
71
  choices={options.map((option) => ({
71
72
  id: option.value,
72
73
  label: option.label,
74
+ group: option.group,
73
75
  description: option.description,
74
76
  disabled: option.disabled,
75
77
  color: option.color,
@@ -417,6 +417,7 @@ export function QueryBar({ width, search, filters = [], view, meta: given, asOf,
417
417
  options: filter.options.map((option) => ({
418
418
  value: option.value,
419
419
  label: option.label,
420
+ group: option.group,
420
421
  description: option.description,
421
422
  disabled: option.disabled,
422
423
  selected: option.value === filter.value,
@@ -18,6 +18,8 @@ export interface SelectControl {
18
18
  export interface SelectButtonOption<T extends string = string> {
19
19
  value: T;
20
20
  label: string;
21
+ /** Heading for a contiguous group of choices; never selectable itself. */
22
+ group?: string;
21
23
  /** Trigger wording when the full label is too wide for a pane header. */
22
24
  short?: string;
23
25
  description?: string;
@@ -130,6 +132,7 @@ export function SelectButton<T extends string = string>({
130
132
  choices={options.map((option) => ({
131
133
  id: option.value,
132
134
  label: option.label,
135
+ group: option.group,
133
136
  description: option.description,
134
137
  disabled: option.disabled,
135
138
  color: option.color,
@@ -158,7 +161,7 @@ export function SelectButton<T extends string = string>({
158
161
  selectRef={(element) => { selectFieldRef.current = element; }}
159
162
  onFocus={onFocus}
160
163
  value={value}
161
- options={options.map((option) => ({ value: option.value, label: option.label, disabled: option.disabled }))}
164
+ options={options.map((option) => ({ value: option.value, label: option.label, group: option.group, disabled: option.disabled }))}
162
165
  disabled={disabled}
163
166
  onChange={selectValue}
164
167
  />
@@ -180,6 +183,7 @@ export function SelectButton<T extends string = string>({
180
183
  options={options.map((option) => ({
181
184
  value: option.value,
182
185
  label: option.label,
186
+ group: option.group,
183
187
  disabled: option.disabled,
184
188
  }))}
185
189
  disabled={disabled}
@@ -9,6 +9,7 @@ export interface SelectFieldHandle {
9
9
 
10
10
  interface SelectFieldOption {
11
11
  label: string;
12
+ group?: string;
12
13
  value: string;
13
14
  description?: string;
14
15
  disabled?: boolean;
@@ -106,18 +106,21 @@ export function getTableWidth(
106
106
  * The columns that fit in `width` cells: whole columns in `dropOrder` (least
107
107
  * needed first) give way until the rest fit, rather than the table's right
108
108
  * edge cutting a value, so a narrow pane drops a name that repeats its ticker
109
- * before it cuts a date. Columns not listed stay at every width. A table that
110
- * scrolls vertically passes its width less the scrollbar's cell.
109
+ * before it cuts a date. An entry that lists several ids drops them together,
110
+ * for columns that mean little apart (a bid's venue and an ask's). Columns not
111
+ * listed stay at every width. A table that scrolls vertically passes its width
112
+ * less the scrollbar's cell.
111
113
  */
112
114
  export function dropColumnsToFit<C extends TableWidthColumn & { id: string }>(
113
115
  columns: readonly C[],
114
116
  width: number,
115
- dropOrder: readonly string[],
117
+ dropOrder: readonly (string | readonly string[])[],
116
118
  ): C[] {
117
119
  let shown = [...columns];
118
- for (const id of dropOrder) {
120
+ for (const step of dropOrder) {
119
121
  if (getTableWidth(shown) <= width) break;
120
- shown = shown.filter((column) => column.id !== id);
122
+ const ids = typeof step === "string" ? [step] : step;
123
+ shown = shown.filter((column) => !ids.includes(column.id));
121
124
  }
122
125
  return shown;
123
126
  }
@@ -1,5 +1,6 @@
1
1
  import { MarketDataCoordinator, getSharedMarketDataCoordinator, setSharedMarketDataCoordinator } from "../market-data/coordinator";
2
2
  import { NewsService, type NewsServiceOptions } from "../news/aggregator";
3
+ import { cloudNewsSession } from "../news/cloud-session";
3
4
  import { getSharedNewsService, setSharedNewsService } from "../news/hooks";
4
5
  import { PluginRegistry } from "../plugins/registry";
5
6
  import { releaseSharedRegistry } from "../plugins/registry/shared";
@@ -61,6 +62,7 @@ export function createAppRuntime({
61
62
  const newsService = new NewsService({
62
63
  connectionHealth: pluginRegistry.connectionHealth,
63
64
  pollIntervalMs: () => Math.max(1, config.refreshIntervalMinutes) * 60_000,
65
+ session: cloudNewsSession,
64
66
  ...newsOptions,
65
67
  });
66
68
  const modules = { pluginRegistry, marketData, newsService };
@@ -27,8 +27,8 @@ export function createAppServices({ config, plugins, externalPlugins }: AppServi
27
27
  provider: {
28
28
  fetchNews: async (query) => (await newsService.load(query)).articles,
29
29
  fetchNewsPage: async (query) => {
30
- const { articles, error, nextCursor } = await newsService.load(query);
31
- return { articles, error, nextCursor };
30
+ const { articles, error, nextCursor, access } = await newsService.load(query);
31
+ return { articles, error, nextCursor, access };
32
32
  },
33
33
  getCachedNews: (query) => newsService.getQueryState(query).articles,
34
34
  },
@@ -20,7 +20,7 @@ import {
20
20
  import { recordOrNull } from "../../utils/guards";
21
21
 
22
22
  const CHART_RANGES = new Set<TimeRange>(TIME_RANGES);
23
- const PRICE_RENDER_MODES = new Set<SeriesStyle>(["area", "line", "candles", "ohlc", "hlc"]);
23
+ const PRICE_RENDER_MODES = new Set<SeriesStyle>(["area", "line", "candles", "hollow", "heikin-ashi", "ohlc", "hlc"]);
24
24
  const LEGACY_INDICATOR_IDS = new Set<LegacyChartIndicatorId>([
25
25
  "volume",
26
26
  "sma20",
@@ -4,6 +4,7 @@ import { hydrateTickerMetadata } from "../tickers/metadata";
4
4
  import type { TickerMetadata, TickerRecord } from "../types/ticker";
5
5
  import { BROWSER_STORAGE_KEYS, SafeJsonStorage, type StorageLike } from "./json-storage";
6
6
  import { isRecord } from "../utils/guards";
7
+ import { runTickerWrite } from "./ticker-writes";
7
8
 
8
9
  type StoredTickers = Record<string, TickerMetadata>;
9
10
 
@@ -39,20 +40,28 @@ export class JsonTickerRepository implements AppTickerRepositoryPort {
39
40
  return metadata ? hydrate(metadata) : null;
40
41
  }
41
42
 
42
- async saveTicker(ticker: TickerRecord): Promise<void> {
43
+ private writeTicker(ticker: TickerRecord): void {
43
44
  const symbol = normalizeSymbol(ticker.metadata.ticker);
44
45
  this.data.set({ ...this.data.get(), [symbol]: ticker.metadata });
45
46
  }
46
47
 
47
- async createTicker(metadata: TickerMetadata): Promise<TickerRecord> {
48
+ saveTicker(ticker: TickerRecord): Promise<void> {
49
+ return runTickerWrite(this, ticker.metadata.ticker, () => this.writeTicker(ticker));
50
+ }
51
+
52
+ createTicker(metadata: TickerMetadata): Promise<TickerRecord> {
48
53
  const ticker = { metadata };
49
- await this.saveTicker(ticker);
50
- return ticker;
54
+ return runTickerWrite(this, metadata.ticker, () => {
55
+ this.writeTicker(ticker);
56
+ return ticker;
57
+ });
51
58
  }
52
59
 
53
- async deleteTicker(symbol: string): Promise<void> {
54
- const tickers = { ...this.data.get() };
55
- delete tickers[normalizeSymbol(symbol)];
56
- this.data.set(tickers);
60
+ deleteTicker(symbol: string): Promise<void> {
61
+ return runTickerWrite(this, symbol, () => {
62
+ const tickers = { ...this.data.get() };
63
+ delete tickers[normalizeSymbol(symbol)];
64
+ this.data.set(tickers);
65
+ });
57
66
  }
58
67
  }
@@ -108,11 +108,13 @@ export function createPluginCache<T, Stored = T>(options: {
108
108
  }
109
109
  return { value, fetchedAt, staleAt: fetchedAt + options.policy.staleMs, expiresAt: fetchedAt + options.policy.expireMs, source: options.source };
110
110
  } });
111
+ // A retained copy carries its refresh failure, whatever was thrown (an Error without a message included).
112
+ const failed = "refreshError" in result;
111
113
  const error = result.refreshError;
112
114
  return {
113
- data: result.value, fetchedAt: result.fetchedAt, stale: !!error || result.staleAt <= Date.now(),
114
- source: error ? "stale-fallback" : result === before ? "cache" : "network",
115
- ...(error ? { error, refreshError: error instanceof Error ? error.message : String(error) } : {}),
115
+ data: result.value, fetchedAt: result.fetchedAt, stale: failed || result.staleAt <= Date.now(),
116
+ source: failed ? "stale-fallback" : result === before ? "cache" : "network",
117
+ ...(failed ? { error, refreshError: error instanceof Error ? error.message : String(error) } : {}),
116
118
  };
117
119
  },
118
120
  };
@@ -56,6 +56,14 @@ export function savedCopyMark(value: unknown): object {
56
56
  return savedAt != null ? { [SAVED_AT]: savedAt } : {};
57
57
  }
58
58
 
59
+ /**
60
+ * The refresh failure a command still prints for `result`: none when a saved
61
+ * copy answered for it (`servedFromSavedCopy`), whose closing line says so.
62
+ */
63
+ export function unansweredRefreshError(result: { refreshError?: string | null } | null | undefined): string | null {
64
+ return result?.refreshError && savedCopyAt(result) == null ? result.refreshError : null;
65
+ }
66
+
59
67
  /** When `value` itself was saved, if it is a saved copy served after a failed refresh; null otherwise. */
60
68
  export function savedCopyAt(value: unknown): number | null {
61
69
  const savedAt = value && typeof value === "object" ? (value as Marked)[SAVED_AT] : undefined;
@@ -2,6 +2,7 @@ import { logFailure } from "../utils/log-failure";
2
2
  import type { TickerRecord, TickerMetadata } from "../types/ticker";
3
3
  import type { TickerStore } from "./ticker-store";
4
4
  import { hydrateTickerMetadata } from "../tickers/metadata";
5
+ import { runTickerWrite } from "./ticker-writes";
5
6
 
6
7
  function parseTickerMetadata(json: string): TickerMetadata {
7
8
  return hydrateTickerMetadata(JSON.parse(json) as Record<string, unknown>);
@@ -32,22 +33,28 @@ export class TickerRepository {
32
33
  }
33
34
  }
34
35
 
35
- async saveTicker(ticker: TickerRecord): Promise<void> {
36
- this.tickers.save(ticker.metadata.ticker, ticker.metadata);
36
+ saveTicker(ticker: TickerRecord): Promise<void> {
37
+ return runTickerWrite(this, ticker.metadata.ticker, () => {
38
+ this.tickers.save(ticker.metadata.ticker, ticker.metadata);
39
+ });
37
40
  }
38
41
 
39
42
  /** Saves several tickers in one write. */
40
- async saveTickers(tickers: readonly TickerRecord[]): Promise<void> {
41
- this.tickers.saveMany(tickers.map((ticker) => ({ symbol: ticker.metadata.ticker, metadata: ticker.metadata })));
43
+ saveTickers(tickers: readonly TickerRecord[]): Promise<void> {
44
+ return runTickerWrite(this, tickers.map((ticker) => ticker.metadata.ticker), () => {
45
+ this.tickers.saveMany(tickers.map((ticker) => ({ symbol: ticker.metadata.ticker, metadata: ticker.metadata })));
46
+ });
42
47
  }
43
48
 
44
- async createTicker(metadata: TickerMetadata): Promise<TickerRecord> {
49
+ createTicker(metadata: TickerMetadata): Promise<TickerRecord> {
45
50
  const ticker: TickerRecord = { metadata };
46
- await this.saveTicker(ticker);
47
- return ticker;
51
+ return runTickerWrite(this, metadata.ticker, () => {
52
+ this.tickers.save(metadata.ticker, metadata);
53
+ return ticker;
54
+ });
48
55
  }
49
56
 
50
- async deleteTicker(symbol: string): Promise<void> {
51
- this.tickers.delete(symbol);
57
+ deleteTicker(symbol: string): Promise<void> {
58
+ return runTickerWrite(this, symbol, () => this.tickers.delete(symbol));
52
59
  }
53
60
  }
@@ -0,0 +1,81 @@
1
+ interface TickerWrites {
2
+ revisions: Map<string, number>;
3
+ tails: Map<string, Promise<unknown>>;
4
+ pending: Set<Promise<unknown>>;
5
+ }
6
+
7
+ const repositories = new WeakMap<object, TickerWrites>();
8
+
9
+ function writesFor(repository: object): TickerWrites {
10
+ let writes = repositories.get(repository);
11
+ if (!writes) {
12
+ writes = { revisions: new Map(), tails: new Map(), pending: new Set() };
13
+ repositories.set(repository, writes);
14
+ }
15
+ return writes;
16
+ }
17
+
18
+ function tickerKey(symbol: string): string {
19
+ return symbol.trim().toUpperCase();
20
+ }
21
+
22
+ /** Reserve writes when requested, including ones waiting for an earlier acknowledgement. */
23
+ export function runTickerWrite<Result>(
24
+ repository: object,
25
+ symbols: string | readonly string[],
26
+ write: () => Result | PromiseLike<Result>,
27
+ ): Promise<Result> {
28
+ const writes = writesFor(repository);
29
+ const keys = [...new Set((typeof symbols === "string" ? [symbols] : symbols).map(tickerKey))];
30
+ const previous = [...new Set(keys.flatMap((key) => {
31
+ const tail = writes.tails.get(key);
32
+ return tail ? [tail] : [];
33
+ }))];
34
+ let resolve!: (value: Result | PromiseLike<Result>) => void;
35
+ let reject!: (error: unknown) => void;
36
+ const result = new Promise<Result>((accept, fail) => {
37
+ resolve = accept;
38
+ reject = fail;
39
+ });
40
+ for (const key of keys) {
41
+ writes.revisions.set(key, (writes.revisions.get(key) ?? 0) + 1);
42
+ writes.tails.set(key, result);
43
+ }
44
+ writes.pending.add(result);
45
+ const finished = () => {
46
+ writes.pending.delete(result);
47
+ for (const key of keys) {
48
+ if (writes.tails.get(key) === result) writes.tails.delete(key);
49
+ }
50
+ };
51
+ void result.then(finished, finished);
52
+ const start = () => {
53
+ try {
54
+ resolve(write());
55
+ } catch (error) {
56
+ reject(error);
57
+ }
58
+ };
59
+ if (previous.length) void Promise.allSettled(previous).then(start);
60
+ else start();
61
+ return result;
62
+ }
63
+
64
+ /** Changes immediately when a save or deletion is requested, before its acknowledgement. */
65
+ export function tickerWriteRevision(repository: object, symbol: string): number {
66
+ return repositories.get(repository)?.revisions.get(tickerKey(symbol)) ?? 0;
67
+ }
68
+
69
+ /** Whether a write arrived after a caller last waited for acknowledgements. */
70
+ export function hasPendingTickerWrites(repository: object): boolean {
71
+ return (repositories.get(repository)?.pending.size ?? 0) > 0;
72
+ }
73
+
74
+ /** Let save-await callers publish their edits before cleanup reads the current rows. */
75
+ export async function settleTickerWrites(repository: object): Promise<void> {
76
+ const writes = writesFor(repository);
77
+ do {
78
+ // Each writer handles its own failure; cleanup still needs the last published row.
79
+ await Promise.allSettled([...writes.pending]);
80
+ } while (writes.pending.size);
81
+ }