@gloom-sh/terminal 0.18.5 → 0.19.0

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 (436) hide show
  1. package/package.json +1 -1
  2. package/patches/electrobun@1.18.1.patch +140 -7
  3. package/src/api-client/cot.ts +6 -2
  4. package/src/api-client/credit-documents.ts +6 -0
  5. package/src/api-client/data.ts +17 -6
  6. package/src/api-client/equity-screener.ts +24 -0
  7. package/src/api-client/fund-facts.ts +45 -4
  8. package/src/api-client/index.ts +11 -0
  9. package/src/api-client/market-discovery.ts +10 -0
  10. package/src/api-client/market-movers.ts +7 -0
  11. package/src/api-client/members.ts +1 -1
  12. package/src/api-client/perps.ts +43 -0
  13. package/src/api-client/research-activity.ts +3 -2
  14. package/src/api-client/types.ts +70 -2
  15. package/src/app/pane-runtime/plugin-bindings.ts +9 -5
  16. package/src/app/runtime/broker-import.ts +0 -2
  17. package/src/app/runtime/plugin-bindings.ts +73 -56
  18. package/src/architecture/pane-tabs-allowlist.json +3 -0
  19. package/src/architecture/ttl-caches-allowlist.json +19 -0
  20. package/src/brokers/sync-broker-instance.ts +2 -8
  21. package/src/capabilities/chart-series.ts +1 -1
  22. package/src/cli/commands/crypto-hints.ts +40 -24
  23. package/src/cli/commands/market.ts +72 -26
  24. package/src/cli/commands/options-chain.ts +48 -7
  25. package/src/cli/commands/options-leaps.ts +9 -3
  26. package/src/cli/commands/overview.ts +61 -12
  27. package/src/cli/commands/search.ts +21 -1
  28. package/src/cli/commands/themes.ts +11 -2
  29. package/src/cli/commands/ticker.ts +55 -34
  30. package/src/cli/company-name-hint.ts +41 -0
  31. package/src/cli/earnings-calendar.ts +1 -1
  32. package/src/cli/errors.ts +8 -0
  33. package/src/cli/glossary.ts +25 -2
  34. package/src/cli/history-rows.ts +57 -9
  35. package/src/cli/index.ts +2 -0
  36. package/src/cli/listing-arg.ts +180 -52
  37. package/src/cli/output-file.ts +68 -0
  38. package/src/cli/pane-functions/capabilities.ts +3 -0
  39. package/src/cli/pane-functions/catalog.ts +80 -7
  40. package/src/cli/pane-functions/dom.ts +13 -3
  41. package/src/cli/pane-functions/freshness.ts +86 -3
  42. package/src/cli/pane-functions/function-help.ts +13 -10
  43. package/src/cli/pane-functions/headless.ts +17 -7
  44. package/src/cli/pane-functions/index.ts +49 -14
  45. package/src/cli/pane-functions/market-session.ts +66 -4
  46. package/src/cli/pane-functions/options.ts +19 -1
  47. package/src/cli/pane-functions/resolver.ts +19 -8
  48. package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
  49. package/src/cli/pane-functions/screenshot-types.ts +5 -0
  50. package/src/cli/quote-answer.ts +22 -11
  51. package/src/cli/ticker-resolution.ts +3 -2
  52. package/src/components/chart/composite/composite-chart.tsx +4 -0
  53. package/src/components/chart/composite/interactions.ts +6 -0
  54. package/src/components/chart/composite/legend.tsx +45 -7
  55. package/src/components/chart/composite/panel-gestures.ts +33 -0
  56. package/src/components/chart/composite/panel-surface.tsx +52 -3
  57. package/src/components/chart/composite/rasterizer.ts +24 -13
  58. package/src/components/chart/composite/scene.ts +39 -4
  59. package/src/components/chart/composite/series-cues.ts +14 -0
  60. package/src/components/chart/composite/text-renderer.ts +9 -2
  61. package/src/components/chart/composite/tool-catalog.ts +36 -0
  62. package/src/components/chart/composite/tools.ts +203 -44
  63. package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
  64. package/src/components/command-bar/assist/inventory.ts +2 -1
  65. package/src/components/command-bar/commands/direct/index.ts +28 -13
  66. package/src/components/command-bar/pane-templates/items.ts +3 -2
  67. package/src/components/command-bar/workflow/collection-actions.ts +3 -3
  68. package/src/components/onboarding/onboarding-wizard.tsx +4 -1
  69. package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
  70. package/src/components/onboarding/wizard-broker-sync.ts +0 -1
  71. package/src/components/onboarding/wizard-positions.ts +3 -3
  72. package/src/components/ticker/list-table-view.tsx +4 -1
  73. package/src/components/ui/choice-dialog.tsx +23 -18
  74. package/src/components/ui/choice-groups.ts +18 -0
  75. package/src/components/ui/data-table/opentui/index.tsx +9 -7
  76. package/src/components/ui/data-table/opentui/model.ts +26 -1
  77. package/src/components/ui/data-table/types.ts +2 -0
  78. package/src/components/ui/list-view.tsx +8 -4
  79. package/src/components/ui/query-bar-keys.tsx +2 -0
  80. package/src/components/ui/query-bar.tsx +1 -0
  81. package/src/components/ui/select-button.tsx +5 -1
  82. package/src/components/ui/select-field.tsx +1 -0
  83. package/src/components/ui/table-layout.ts +8 -5
  84. package/src/core/app-runtime.ts +2 -0
  85. package/src/core/app-services.ts +2 -2
  86. package/src/data/config/chart-settings.ts +1 -1
  87. package/src/data/json-ticker-repository.ts +17 -8
  88. package/src/data/ticker-repository.ts +16 -9
  89. package/src/data/ticker-writes.ts +81 -0
  90. package/src/data/ttl-cache.ts +165 -0
  91. package/src/docs/snapshot.json +71 -137
  92. package/src/i18n/es.ts +18 -2
  93. package/src/i18n/ja.ts +18 -2
  94. package/src/i18n/ko.ts +18 -2
  95. package/src/i18n/zh-cn.ts +18 -2
  96. package/src/i18n/zh-tw.ts +18 -2
  97. package/src/market-data/depositary-pair.ts +2 -2
  98. package/src/market-data/fund-facts.ts +50 -0
  99. package/src/market-data/market/change-basis.ts +16 -3
  100. package/src/market-data/market/format.ts +25 -0
  101. package/src/market-data/market/freshness.ts +2 -1
  102. package/src/market-data/options-alternatives.ts +81 -4
  103. package/src/news/aggregator.ts +113 -9
  104. package/src/news/cloud-session.ts +21 -0
  105. package/src/news/hooks.ts +1 -0
  106. package/src/news/news-model.ts +1 -0
  107. package/src/news/types.ts +11 -0
  108. package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
  109. package/src/plugins/builtin/alerts/alert-log.ts +33 -0
  110. package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
  111. package/src/plugins/builtin/alerts/cli.ts +336 -77
  112. package/src/plugins/builtin/alerts/command.ts +25 -1
  113. package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
  114. package/src/plugins/builtin/alerts/fired-log.ts +73 -9
  115. package/src/plugins/builtin/alerts/format.ts +75 -5
  116. package/src/plugins/builtin/alerts/headless.ts +24 -6
  117. package/src/plugins/builtin/alerts/history-rows.ts +3 -2
  118. package/src/plugins/builtin/alerts/history.ts +1 -1
  119. package/src/plugins/builtin/alerts/index.tsx +79 -16
  120. package/src/plugins/builtin/alerts/metric-command.ts +77 -4
  121. package/src/plugins/builtin/alerts/metric-data.ts +54 -13
  122. package/src/plugins/builtin/alerts/metric-model.ts +163 -14
  123. package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
  124. package/src/plugins/builtin/alerts/pane.tsx +61 -23
  125. package/src/plugins/builtin/alerts/quotes.ts +6 -0
  126. package/src/plugins/builtin/alerts/storage.ts +12 -8
  127. package/src/plugins/builtin/alerts/types.ts +4 -0
  128. package/src/plugins/builtin/analytics/index.tsx +2 -1
  129. package/src/plugins/builtin/analytics/risk-client.ts +16 -5
  130. package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
  131. package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
  132. package/src/plugins/builtin/analytics/risk-model.ts +55 -9
  133. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  134. package/src/plugins/builtin/attention/index.tsx +2 -1
  135. package/src/plugins/builtin/attention/listing.ts +14 -0
  136. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  137. package/src/plugins/builtin/buildout/cells.tsx +12 -4
  138. package/src/plugins/builtin/buildout/format.ts +38 -0
  139. package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
  140. package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
  141. package/src/plugins/builtin/buildout/table-model.ts +23 -2
  142. package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
  143. package/src/plugins/builtin/changelog/index.tsx +9 -6
  144. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
  145. package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
  146. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
  147. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
  148. package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
  149. package/src/plugins/builtin/chart-composer/headless.ts +6 -1
  150. package/src/plugins/builtin/chart-composer/index.tsx +1 -1
  151. package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
  152. package/src/plugins/builtin/chart-composer/settings.ts +27 -0
  153. package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
  154. package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
  155. package/src/plugins/builtin/clinical-trials/model.ts +0 -9
  156. package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
  157. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
  158. package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
  159. package/src/plugins/builtin/company-attention/building.tsx +5 -1
  160. package/src/plugins/builtin/company-attention/client.ts +8 -9
  161. package/src/plugins/builtin/company-attention/evidence.ts +4 -1
  162. package/src/plugins/builtin/company-attention/moves.tsx +5 -1
  163. package/src/plugins/builtin/company-attention/pane.tsx +9 -2
  164. package/src/plugins/builtin/company-attention/signals.tsx +5 -1
  165. package/src/plugins/builtin/correlation/headless.ts +13 -5
  166. package/src/plugins/builtin/correlation/history.ts +22 -0
  167. package/src/plugins/builtin/cot/client.ts +22 -18
  168. package/src/plugins/builtin/cot/headless.ts +15 -10
  169. package/src/plugins/builtin/cot/model.ts +14 -1
  170. package/src/plugins/builtin/cot/pane.tsx +22 -14
  171. package/src/plugins/builtin/credit-boards/client.ts +6 -10
  172. package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
  173. package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
  174. package/src/plugins/builtin/credit-conditions/model.ts +4 -0
  175. package/src/plugins/builtin/credit-conditions/series.ts +3 -1
  176. package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
  177. package/src/plugins/builtin/credit-documents/export.ts +173 -0
  178. package/src/plugins/builtin/credit-documents/headless.ts +7 -7
  179. package/src/plugins/builtin/credit-documents/model.ts +104 -14
  180. package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
  181. package/src/plugins/builtin/custom-view/sources.ts +2 -0
  182. package/src/plugins/builtin/dividend-yield/client.ts +131 -23
  183. package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
  184. package/src/plugins/builtin/dividend-yield/model.ts +8 -5
  185. package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
  186. package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
  187. package/src/plugins/builtin/dividend-yield/types.ts +20 -2
  188. package/src/plugins/builtin/dividend-yield/view.ts +30 -2
  189. package/src/plugins/builtin/docs/targets.ts +5 -1
  190. package/src/plugins/builtin/earnings/board-model.ts +16 -15
  191. package/src/plugins/builtin/earnings/board.tsx +62 -71
  192. package/src/plugins/builtin/earnings/headless.ts +40 -4
  193. package/src/plugins/builtin/earnings/scope.ts +31 -43
  194. package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
  195. package/src/plugins/builtin/earnings-calls/format.ts +31 -2
  196. package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
  197. package/src/plugins/builtin/earnings-calls/model.ts +6 -1
  198. package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
  199. package/src/plugins/builtin/econ/types.ts +2 -0
  200. package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
  201. package/src/plugins/builtin/econ-statistics/headless.ts +6 -4
  202. package/src/plugins/builtin/econ-statistics/stats.ts +36 -9
  203. package/src/plugins/builtin/equity-screener/categories.ts +179 -0
  204. package/src/plugins/builtin/equity-screener/client.ts +4 -2
  205. package/src/plugins/builtin/equity-screener/headless.ts +144 -13
  206. package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
  207. package/src/plugins/builtin/equity-screener/model.ts +99 -10
  208. package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
  209. package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
  210. package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
  211. package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
  212. package/src/plugins/builtin/fx-matrix/client.ts +6 -3
  213. package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
  214. package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
  215. package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
  216. package/src/plugins/builtin/gpu/headless.ts +8 -2
  217. package/src/plugins/builtin/help/function-index.ts +4 -1
  218. package/src/plugins/builtin/help/index.tsx +1 -0
  219. package/src/plugins/builtin/help/shortcut-model.ts +1 -1
  220. package/src/plugins/builtin/holders/fund-holders.ts +51 -0
  221. package/src/plugins/builtin/holders/headless.ts +18 -1
  222. package/src/plugins/builtin/iv-history/client.ts +52 -11
  223. package/src/plugins/builtin/iv-history/format.ts +36 -23
  224. package/src/plugins/builtin/iv-history/headless.ts +33 -18
  225. package/src/plugins/builtin/iv-history/index.tsx +2 -2
  226. package/src/plugins/builtin/iv-history/model.ts +163 -27
  227. package/src/plugins/builtin/iv-history/pane.tsx +10 -6
  228. package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
  229. package/src/plugins/builtin/iv-history/universe.ts +5 -1
  230. package/src/plugins/builtin/jobs/client.ts +25 -29
  231. package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
  232. package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
  233. package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
  234. package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
  235. package/src/plugins/builtin/macro-day/release-table.ts +2 -2
  236. package/src/plugins/builtin/market-heatmap/data.ts +8 -33
  237. package/src/plugins/builtin/market-movers/client.ts +25 -7
  238. package/src/plugins/builtin/market-movers/headless.ts +32 -12
  239. package/src/plugins/builtin/market-movers/index.tsx +26 -14
  240. package/src/plugins/builtin/market-movers/min-float.ts +74 -0
  241. package/src/plugins/builtin/market-movers/model.ts +4 -1
  242. package/src/plugins/builtin/market-movers/screener.ts +14 -0
  243. package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
  244. package/src/plugins/builtin/market-movers/session-table.ts +4 -3
  245. package/src/plugins/builtin/market-movers/table.ts +17 -5
  246. package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
  247. package/src/plugins/builtin/news/index.tsx +1 -0
  248. package/src/plugins/builtin/news/wire/categories.ts +19 -3
  249. package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
  250. package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
  251. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
  252. package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
  253. package/src/plugins/builtin/openfda/client.ts +14 -2
  254. package/src/plugins/builtin/openfda/index.tsx +3 -2
  255. package/src/plugins/builtin/openfda/model.ts +9 -0
  256. package/src/plugins/builtin/openfda/pane.tsx +42 -6
  257. package/src/plugins/builtin/options/analytics.ts +29 -5
  258. package/src/plugins/builtin/options/enrichment.ts +17 -5
  259. package/src/plugins/builtin/options/table.ts +5 -2
  260. package/src/plugins/builtin/options/types.ts +2 -0
  261. package/src/plugins/builtin/options/view.tsx +17 -4
  262. package/src/plugins/builtin/options-positioning/client.ts +11 -0
  263. package/src/plugins/builtin/options-positioning/headless.ts +21 -8
  264. package/src/plugins/builtin/options-positioning/index.tsx +1 -1
  265. package/src/plugins/builtin/options-positioning/model.ts +78 -9
  266. package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
  267. package/src/plugins/builtin/options-scenario/client.ts +116 -47
  268. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
  269. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
  270. package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
  271. package/src/plugins/builtin/options-scenario/csp.ts +8 -2
  272. package/src/plugins/builtin/options-scenario/index.tsx +9 -7
  273. package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
  274. package/src/plugins/builtin/pe-band/model.ts +12 -49
  275. package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
  276. package/src/plugins/builtin/perps/cascade.ts +236 -0
  277. package/src/plugins/builtin/perps/client.ts +56 -2
  278. package/src/plugins/builtin/perps/headless.ts +65 -9
  279. package/src/plugins/builtin/perps/index.tsx +7 -5
  280. package/src/plugins/builtin/perps/model.ts +18 -4
  281. package/src/plugins/builtin/perps/pane.tsx +19 -22
  282. package/src/plugins/builtin/perps/status-line.ts +25 -0
  283. package/src/plugins/builtin/plugin-module.ts +5 -1
  284. package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
  285. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
  286. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
  287. package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
  288. package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
  289. package/src/plugins/builtin/portfolio-list/pane/index.tsx +4 -1
  290. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
  291. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
  292. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
  293. package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
  294. package/src/plugins/builtin/primary-dealers/client.ts +13 -38
  295. package/src/plugins/builtin/realized-vol/index.tsx +2 -2
  296. package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
  297. package/src/plugins/builtin/relative-rotation/client.ts +18 -4
  298. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  299. package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
  300. package/src/plugins/builtin/relative-rotation/model.ts +16 -40
  301. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
  302. package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
  303. package/src/plugins/builtin/research/event-model.ts +10 -8
  304. package/src/plugins/builtin/research/headless.ts +1 -1
  305. package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
  306. package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
  307. package/src/plugins/builtin/research/relative-valuation-pane.tsx +7 -5
  308. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
  309. package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
  310. package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
  311. package/src/plugins/builtin/scanner/flow-model.ts +50 -4
  312. package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
  313. package/src/plugins/builtin/scanner/flow-session.ts +146 -0
  314. package/src/plugins/builtin/scanner/headless.ts +204 -35
  315. package/src/plugins/builtin/scanner/index.ts +16 -5
  316. package/src/plugins/builtin/scanner/live-headless.ts +27 -3
  317. package/src/plugins/builtin/scanner/live-model.ts +11 -0
  318. package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
  319. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  320. package/src/plugins/builtin/seasonality/headless.ts +10 -1
  321. package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
  322. package/src/plugins/builtin/sec/fund-facts.ts +116 -34
  323. package/src/plugins/builtin/sec/headless.ts +2 -1
  324. package/src/plugins/builtin/series-inventory.ts +2 -0
  325. package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
  326. package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
  327. package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
  328. package/src/plugins/builtin/shared/earnings-report.ts +2 -2
  329. package/src/plugins/builtin/shared/job-posting.ts +1 -0
  330. package/src/plugins/builtin/shared/members-menu.tsx +3 -2
  331. package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
  332. package/src/plugins/builtin/shared/report-freshness.ts +3 -2
  333. package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
  334. package/src/plugins/builtin/shared/ticker-company.ts +77 -0
  335. package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
  336. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
  337. package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
  338. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
  339. package/src/plugins/builtin/themes/model.ts +16 -7
  340. package/src/plugins/builtin/themes/pane.tsx +5 -5
  341. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
  342. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
  343. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
  344. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
  345. package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
  346. package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
  347. package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
  348. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
  349. package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
  350. package/src/plugins/builtin/time-sales/pane.tsx +31 -19
  351. package/src/plugins/builtin/treasury-daily/client.ts +41 -5
  352. package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
  353. package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
  354. package/src/plugins/builtin/treasury-daily/model.ts +50 -3
  355. package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
  356. package/src/plugins/builtin/vol-surface/client.ts +3 -13
  357. package/src/plugins/builtin/vol-surface/headless.ts +3 -3
  358. package/src/plugins/builtin/vol-surface/index.tsx +1 -1
  359. package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
  360. package/src/plugins/builtin/volatility/index.tsx +2 -2
  361. package/src/plugins/builtin/volatility/pane.tsx +2 -2
  362. package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
  363. package/src/plugins/catalog-manifest.ts +2 -1
  364. package/src/plugins/marketplace/model.ts +18 -5
  365. package/src/portfolio/futures-positions.ts +6 -3
  366. package/src/portfolio/position-quantity.ts +15 -9
  367. package/src/remote/resources.ts +2 -1
  368. package/src/renderers/dom/data-table/row.tsx +26 -0
  369. package/src/renderers/dom/desktop/list-view.tsx +13 -10
  370. package/src/renderers/dom/desktop/query-bar.tsx +4 -2
  371. package/src/renderers/dom/desktop/select-field.tsx +4 -2
  372. package/src/renderers/dom/host/chart-surface.tsx +21 -0
  373. package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
  374. package/src/renderers/electrobun/bun/desktop/backend.ts +12 -0
  375. package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
  376. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
  377. package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
  378. package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
  379. package/src/renderers/electrobun/shared/app-name.ts +13 -0
  380. package/src/renderers/electrobun/shared/protocol.ts +10 -0
  381. package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
  382. package/src/renderers/electrobun/view/main.tsx +24 -1
  383. package/src/renderers/electrobun/view/news-source.ts +3 -1
  384. package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
  385. package/src/renderers/electrobun/view/view-ids.ts +85 -0
  386. package/src/renderers/opentui/start.tsx +5 -0
  387. package/src/series/registry.ts +1 -0
  388. package/src/series/types.ts +6 -0
  389. package/src/series/validate.ts +1 -0
  390. package/src/sources/gloomberb-cloud/index.ts +1 -0
  391. package/src/sources/gloomberb-cloud/news.ts +47 -7
  392. package/src/sources/provider-router/financials.ts +15 -3
  393. package/src/sources/provider-router/index.ts +8 -5
  394. package/src/state/ticker-cleanup.ts +50 -0
  395. package/src/sync/controller.ts +4 -3
  396. package/src/sync/core-contributors.ts +90 -31
  397. package/src/telemetry/stall-reports-dom.ts +38 -0
  398. package/src/telemetry/stall-reports-node.ts +75 -0
  399. package/src/telemetry/stall-reports.ts +214 -0
  400. package/src/test-support/preload.ts +14 -0
  401. package/src/theme/themes.ts +10 -0
  402. package/src/tickers/company-name.ts +44 -3
  403. package/src/tickers/listing-venues.ts +15 -3
  404. package/src/tickers/terminal-codes.ts +19 -0
  405. package/src/time-series/alignment.ts +1 -1
  406. package/src/time-series/economic.ts +18 -4
  407. package/src/time-series/field-catalog.ts +1 -1
  408. package/src/time-series/filed-trailing-eps.ts +197 -0
  409. package/src/time-series/fundamentals.ts +44 -1
  410. package/src/time-series/ice-credit.ts +3 -0
  411. package/src/time-series/price-comparison.ts +30 -2
  412. package/src/time-series/resolve-context.ts +3 -1
  413. package/src/time-series/resolve.ts +56 -4
  414. package/src/time-series/session-bar.ts +11 -0
  415. package/src/time-series/spec.ts +3 -2
  416. package/src/time-series/studies.ts +8 -2
  417. package/src/time-series/total-return.ts +160 -0
  418. package/src/time-series/transforms.ts +2 -3
  419. package/src/time-series/types.ts +22 -1
  420. package/src/types/financials.ts +32 -1
  421. package/src/types/headless.ts +21 -3
  422. package/src/types/news-source.ts +1 -0
  423. package/src/types/plugin.ts +9 -0
  424. package/src/ui/host.tsx +3 -0
  425. package/src/updater/failure.ts +4 -1
  426. package/src/utils/cli-output.ts +42 -1
  427. package/src/utils/crypto-pair.ts +44 -0
  428. package/src/utils/forward-dividend.ts +2 -0
  429. package/src/utils/fundamentals.ts +24 -3
  430. package/src/utils/fx-market-hours.ts +26 -3
  431. package/src/utils/main-thread-stall.ts +35 -2
  432. package/src/utils/market-capitalization.ts +24 -1
  433. package/src/utils/price-history.ts +7 -1
  434. package/src/utils/shell-word.ts +8 -0
  435. package/src/utils/trailing-dividend.ts +53 -4
  436. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
@@ -518,10 +518,16 @@ export interface CloudEconEventPayload {
518
518
  source?: { name: string; url?: string };
519
519
  /**
520
520
  * The indicator this release is of, the same from one release to the next
521
- * (US CPI m/m on every month's row), where `id` names the one row. Absent
522
- * until Gloom Cloud names indicators; exports leave the column empty.
521
+ * (`us-unemployment-rate` on every month's row), where `id` names the one
522
+ * row. Absent from a server that names none; exports leave the column empty.
523
523
  */
524
524
  indicatorId?: string | null;
525
+ /**
526
+ * Where the forecast (the consensus) was read from, `week-feed` or
527
+ * `release-calendar`, and when that feed was read. Absent on a row with no
528
+ * forecast and on one kept before the read was recorded.
529
+ */
530
+ consensus?: { source: string; readAt: string } | null;
525
531
  }
526
532
 
527
533
  /** One currency's short-term rate, as GET /cloud/econ/short-rates serves it. */
@@ -891,6 +897,13 @@ export interface CloudEarningsCallPayload {
891
897
  * New York day). Absent from a server that predates it.
892
898
  */
893
899
  callTimeKnown?: boolean;
900
+ /**
901
+ * "other" for an event that is not an earnings call (a deal call, a
902
+ * conference, an investor day), named by eventLabel and with no fiscal
903
+ * period; null when unclear. Older servers omit both.
904
+ */
905
+ eventKind?: "earnings" | "other" | null;
906
+ eventLabel?: string | null;
894
907
  }
895
908
 
896
909
  interface CloudJobsSeriesPoint {
@@ -1255,6 +1268,13 @@ export interface CloudEarningsTranscriptPayload {
1255
1268
  companyName: string | null;
1256
1269
  fiscalYear: number | null;
1257
1270
  fiscalQuarter: number | null;
1271
+ /**
1272
+ * "other" for an event that is not an earnings call (a deal call, a
1273
+ * conference, an investor day), named by eventLabel and with no fiscal
1274
+ * period; null when unclear. Older servers omit both.
1275
+ */
1276
+ eventKind?: "earnings" | "other" | null;
1277
+ eventLabel?: string | null;
1258
1278
  callAt: string | null;
1259
1279
  timing: string | null;
1260
1280
  webcastUrl: string | null;
@@ -1536,6 +1556,8 @@ export interface CloudNewsPayload {
1536
1556
  export interface CloudNewsListResponse {
1537
1557
  items: CloudNewsPayload[];
1538
1558
  nextCursor: string | null;
1559
+ /** Which news the service answered with, when it says: the live wire, or the one held back for a free account. */
1560
+ access?: "realtime" | "delayed";
1539
1561
  }
1540
1562
 
1541
1563
  interface CloudTweetUserPayload {
@@ -1739,6 +1761,14 @@ export interface CloudMarketScreenerItem extends CloudMoverSessionFields {
1739
1761
  exchange: string;
1740
1762
  lastUpdated: number;
1741
1763
  dataSource: "live";
1764
+ /** The listing's float as the equity screener serves it; absent where it serves none. */
1765
+ floatShares?: number;
1766
+ /** Set with a float the source states no measurement date for: `partial`. */
1767
+ floatSharesState?: "partial";
1768
+ /** Why the float is partial, as the server says it. */
1769
+ floatSharesReason?: string;
1770
+ /** When the statistics the float is read from were retrieved (ISO). */
1771
+ floatSharesRetrievedAt?: string;
1742
1772
  }
1743
1773
 
1744
1774
  export interface CloudMarketScreenerPayload {
@@ -1933,6 +1963,44 @@ export interface ScannerFlowHistoryPage {
1933
1963
  hasMore: boolean;
1934
1964
  }
1935
1965
 
1966
+ /**
1967
+ * Premium of a slice of the session's recorded prints by where it traded: at
1968
+ * or above the ask (bought) or at or below the bid (sold). Mid and unknown
1969
+ * prints count as neither.
1970
+ */
1971
+ interface ScannerFlowSidePremium {
1972
+ callAsk: number;
1973
+ callBid: number;
1974
+ putAsk: number;
1975
+ putBid: number;
1976
+ }
1977
+
1978
+ /** The session's prints in one time bucket, which starts at `at`. */
1979
+ export interface ScannerFlowSessionBucket extends ScannerFlowSidePremium {
1980
+ at: number;
1981
+ }
1982
+
1983
+ /** What one name printed at one expiry and strike during the session. */
1984
+ export interface ScannerFlowSessionStrike extends ScannerFlowSidePremium {
1985
+ expiry: string;
1986
+ strike: number;
1987
+ /** Every call print's premium, mid and unknown included. */
1988
+ callPremium: number;
1989
+ putPremium: number;
1990
+ prints: number;
1991
+ }
1992
+
1993
+ /** The latest session's recorded prints under the FLOW pane's filters, summed. */
1994
+ export interface ScannerFlowSession {
1995
+ /** The New York date of the session; null before any print was recorded. */
1996
+ session: string | null;
1997
+ bucketMs: number;
1998
+ /** Buckets with a matching print, oldest first. */
1999
+ buckets: ScannerFlowSessionBucket[];
2000
+ /** Only when one name was asked for: most premium first. */
2001
+ strikes: ScannerFlowSessionStrike[] | null;
2002
+ }
2003
+
1936
2004
  export type ScannerPayload = ScannerHiloPayload | ScannerFlowPayload;
1937
2005
 
1938
2006
  /** What a scanner subscriber receives: data, or the entitlement refusal. */
@@ -513,13 +513,17 @@ export function bindAppPanePluginRegistry({
513
513
  removeExternalPlugin(pluginId, directory);
514
514
  },
515
515
  contributions: (pluginId) => {
516
+ // An unlisted function (`listed: false`) and a pane only it opens are left out, as of every list.
517
+ const registered = pluginRegistry.getPluginPaneTemplateIds(pluginId)
518
+ .map((id) => ({ id, template: pluginRegistry.paneTemplates.get(id) }));
519
+ const listedPaneIds = new Set(registered.flatMap(({ template }) => template && template.listed !== false ? [template.paneId] : []));
520
+ const unlistedPaneIds = new Set(registered.flatMap(({ template }) => template?.listed === false && !listedPaneIds.has(template.paneId) ? [template.paneId] : []));
516
521
  const panes = pluginRegistry.getPluginPaneIds(pluginId)
522
+ .filter((id) => !unlistedPaneIds.has(id))
517
523
  .map((id) => ({ id, name: pluginRegistry.panes.get(id)?.name ?? id }));
518
- const templates = pluginRegistry.getPluginPaneTemplateIds(pluginId)
519
- .map((id) => {
520
- const template = pluginRegistry.paneTemplates.get(id);
521
- return { id, label: template?.label ?? id, ...(template?.shortcut?.prefix ? { prefix: template.shortcut.prefix } : {}) };
522
- });
524
+ const templates = registered
525
+ .filter(({ template }) => template?.listed !== false)
526
+ .map(({ id, template }) => ({ id, label: template?.label ?? id, ...(template?.shortcut?.prefix ? { prefix: template.shortcut.prefix } : {}) }));
523
527
  const commands = [...pluginRegistry.commands.entries()]
524
528
  .filter(([id]) => pluginRegistry.getCommandPluginId(id) === pluginId && !id.endsWith(":setup"))
525
529
  .map(([id, command]) => ({ id, label: command.label, ...(command.shortcut ? { shortcut: command.shortcut } : {}) }));
@@ -28,7 +28,6 @@ export interface AppBrokerImportRuntime {
28
28
  interface BrokerImportOptions {
29
29
  refreshImportedTickers?: boolean;
30
30
  config?: AppConfig;
31
- persistResolvedBrokerConfig?: boolean;
32
31
  signal?: AbortSignal;
33
32
  onCommitStart?: () => void;
34
33
  onCommitEnd?: () => void;
@@ -122,7 +121,6 @@ export function useBrokerImportRuntime({
122
121
  tickerRepository,
123
122
  existingTickers: tickerMap ?? new Map(stateRef.current.tickers),
124
123
  resources: pluginRegistry.persistence.resources,
125
- persistResolvedBrokerConfig: options?.persistResolvedBrokerConfig,
126
124
  signal: options?.signal,
127
125
  deferPersistence: true,
128
126
  });
@@ -10,6 +10,8 @@ import type { MarketDataCoordinator } from "../../market-data/coordinator";
10
10
  import { instrumentFromTicker } from "../../market-data/request-types";
11
11
  import type { PluginRegistry } from "../../plugins/registry";
12
12
  import { saveConfigImmediately } from "../../state/config-save-scheduler";
13
+ import { persistTickerCleanup } from "../../state/ticker-cleanup";
14
+ import { hasPendingTickerWrites } from "../../data/ticker-writes";
13
15
  import { settleWithin } from "../../utils/async-deadline";
14
16
  import type { AppAction, AppState } from "../../state/app/context";
15
17
  import type { AppConfig, BrokerInstanceConfig } from "../../types/config";
@@ -215,66 +217,81 @@ export function bindPluginRegistryRuntimeAccess({
215
217
  else removedPortfolioIds.add(portfolio.id);
216
218
  }
217
219
 
218
- const nextPortfolios = stateRef.current.config.portfolios.flatMap((portfolio) => {
219
- if (removedPortfolioIds.has(portfolio.id)) return [];
220
- const heirId = heirs.get(portfolio.id);
221
- return [heirId ? { ...portfolio, brokerInstanceId: heirId } : portfolio];
220
+ const removeProfile = (config: AppConfig): AppConfig => ({
221
+ ...config,
222
+ brokerInstances: config.brokerInstances.filter((entry) => entry.id !== instanceId),
223
+ portfolios: config.portfolios.flatMap((portfolio) => {
224
+ if (removedPortfolioIds.has(portfolio.id)) return [];
225
+ const heirId = heirs.get(portfolio.id);
226
+ return [heirId ? { ...portfolio, brokerInstanceId: heirId } : portfolio];
227
+ }),
222
228
  });
223
- const nextTickers = new Map(stateRef.current.tickers);
224
- const nextBrokerInstances = stateRef.current.config.brokerInstances.filter((entry) => entry.id !== instanceId);
225
- // What older removals and pulls from other devices left behind goes as well.
226
- const known = knownBrokerHoldings({ portfolios: nextPortfolios, brokerInstances: nextBrokerInstances });
227
229
 
228
- for (const ticker of stateRef.current.tickers.values()) {
229
- // A position here without a profile came from another device's sync of this one.
230
- const nextPositions = ticker.metadata.positions.filter((position) => position.brokerInstanceId !== instanceId
231
- && !(removedPortfolioIds.has(position.portfolio) && !position.brokerInstanceId));
232
- const heldPortfolioIds = new Set(nextPositions.map((position) => position.portfolio));
233
- const nextPortfolioRefs = ticker.metadata.portfolios.filter((portfolioId) =>
234
- !removedPortfolioIds.has(portfolioId) && (!heirs.has(portfolioId) || heldPortfolioIds.has(portfolioId)));
235
- const nextBrokerContracts = (ticker.metadata.broker_contracts ?? []).filter((contract) => contract.brokerInstanceId !== instanceId);
236
-
237
- const removedTicker: TickerRecord = {
238
- ...ticker,
239
- metadata: {
240
- ...ticker.metadata,
241
- positions: nextPositions,
242
- portfolios: nextPortfolioRefs,
243
- broker_contracts: nextBrokerContracts,
244
- },
245
- };
246
- const nextTicker = clearLeftoverBrokerHoldings(removedTicker, known) ?? removedTicker;
247
-
248
- const shouldDeleteTicker =
249
- nextTicker.metadata.positions.length === 0
250
- && nextTicker.metadata.portfolios.length === 0
251
- && nextTicker.metadata.watchlists.length === 0
252
- && (nextTicker.metadata.broker_contracts ?? []).length === 0
253
- && nextTicker.metadata.tags.length === 0
254
- && Object.keys(nextTicker.metadata.custom).length === 0;
230
+ const cleanTickers = () => persistTickerCleanup({
231
+ tickerRepository,
232
+ getTickers: () => stateRef.current.tickers,
233
+ transform: (ticker) => {
234
+ // A position without a profile came from another device's sync of this one.
235
+ const nextPositions = ticker.metadata.positions.filter((position) => position.brokerInstanceId !== instanceId
236
+ && !(removedPortfolioIds.has(position.portfolio) && !position.brokerInstanceId));
237
+ const heldPortfolioIds = new Set(nextPositions.map((position) => position.portfolio));
238
+ const nextPortfolioRefs = ticker.metadata.portfolios.filter((portfolioId) =>
239
+ !removedPortfolioIds.has(portfolioId) && (!heirs.has(portfolioId) || heldPortfolioIds.has(portfolioId)));
240
+ const nextBrokerContracts = (ticker.metadata.broker_contracts ?? []).filter((contract) => contract.brokerInstanceId !== instanceId);
241
+ const changed = nextPositions.length !== ticker.metadata.positions.length
242
+ || nextPortfolioRefs.length !== ticker.metadata.portfolios.length
243
+ || nextBrokerContracts.length !== (ticker.metadata.broker_contracts ?? []).length;
244
+ const removedTicker: TickerRecord = changed ? {
245
+ ...ticker,
246
+ metadata: {
247
+ ...ticker.metadata,
248
+ positions: nextPositions,
249
+ portfolios: nextPortfolioRefs,
250
+ broker_contracts: nextBrokerContracts,
251
+ },
252
+ } : ticker;
253
+ // Also clear holdings left behind by older removals or pulls.
254
+ const known = knownBrokerHoldings(removeProfile(stateRef.current.config));
255
+ const nextTicker = clearLeftoverBrokerHoldings(removedTicker, known) ?? removedTicker;
256
+ if (nextTicker === ticker) return ticker;
257
+ const empty = nextTicker.metadata.positions.length === 0
258
+ && nextTicker.metadata.portfolios.length === 0
259
+ && nextTicker.metadata.watchlists.length === 0
260
+ && (nextTicker.metadata.broker_contracts ?? []).length === 0
261
+ && nextTicker.metadata.tags.length === 0
262
+ && Object.keys(nextTicker.metadata.custom).length === 0;
263
+ return empty ? null : nextTicker;
264
+ },
265
+ publish: (symbol, ticker) => {
266
+ if (ticker) dispatch({ type: "UPDATE_TICKER", ticker });
267
+ else {
268
+ dispatch({ type: "REMOVE_TICKER", symbol });
269
+ pluginRegistry.events.emit("ticker:removed", { symbol });
270
+ }
271
+ },
272
+ });
255
273
 
256
- if (shouldDeleteTicker) {
257
- nextTickers.delete(ticker.metadata.ticker);
258
- await tickerRepository.deleteTicker(ticker.metadata.ticker);
259
- dispatch({ type: "REMOVE_TICKER", symbol: ticker.metadata.ticker });
260
- pluginRegistry.events.emit("ticker:removed", { symbol: ticker.metadata.ticker });
261
- } else {
262
- await tickerRepository.saveTicker(nextTicker);
263
- nextTickers.set(nextTicker.metadata.ticker, nextTicker);
264
- dispatch({ type: "UPDATE_TICKER", ticker: nextTicker });
265
- }
274
+ // Keep the profile available for retry until cleanup and config both
275
+ // succeed. Rebase after callbacks and edits, including during config saves.
276
+ let savedRemoval = false;
277
+ for (;;) {
278
+ const cleaned = await cleanTickers().catch(async (error: unknown) => {
279
+ // A config acknowledgement can precede a late row write. If that
280
+ // cleanup fails, keep its removal intent durable for a restart too.
281
+ if (savedRemoval) await saveConfigImmediately(stateRef.current.config);
282
+ throw error;
283
+ });
284
+ if (hasPendingTickerWrites(tickerRepository) || stateRef.current.tickers !== cleaned) continue;
285
+ const source = stateRef.current.config;
286
+ const nextConfig = removeProfile(source);
287
+ await saveConfigImmediately(nextConfig);
288
+ savedRemoval = true;
289
+ if (stateRef.current.config !== source || stateRef.current.tickers !== cleaned
290
+ || hasPendingTickerWrites(tickerRepository)) continue;
291
+ dispatch({ type: "SET_CONFIG", config: nextConfig });
292
+ pluginRegistry.events.emit("config:changed", { config: nextConfig });
293
+ break;
266
294
  }
267
-
268
- const nextConfig = {
269
- ...stateRef.current.config,
270
- brokerInstances: nextBrokerInstances,
271
- portfolios: nextPortfolios,
272
- };
273
-
274
- dispatch({ type: "SET_CONFIG", config: nextConfig });
275
- dispatch({ type: "SET_TICKERS", tickers: nextTickers });
276
- await saveConfigImmediately(nextConfig);
277
- pluginRegistry.events.emit("config:changed", { config: nextConfig });
278
295
  // The profile that kept a portfolio fills in what the removed one held there.
279
296
  for (const heirId of new Set(heirs.values())) {
280
297
  void importBrokerPositions(heirId).catch(logFailure("app.runtime.plugin-bindings", "Replacement broker positions import failed"));
@@ -53,6 +53,9 @@
53
53
  "src/plugins/builtin/research-search/pane.tsx": {
54
54
  "signInRequired || verificationRequired": "Signed out or unverified, every mode is the same sign-in wall."
55
55
  },
56
+ "src/plugins/builtin/scanner/flow-pane.tsx": {
57
+ "!feed.denied": "Signed out or without Pro, every tab is the same sign-in or plan wall."
58
+ },
56
59
  "src/plugins/builtin/shared/filing-year-reader.tsx": {
57
60
  "years.length > 1": "The tabs are the filings on record, known once their list answers; a single filing has no other year to pick."
58
61
  },
@@ -0,0 +1,19 @@
1
+ {
2
+ "src/plugins/builtin/alerts/metric-model.ts": {
3
+ "deadline: now < cached.retryAt": "A byte-identical copy of Gloom Cloud's delivery model (metric-parity-fixture.json pins its hash), so it cannot import the helper. Its failed-read backoff is the rejectionMs rule; move it when both copies move together.",
4
+ "map: new Map<string, { day: string; retryAt: number; result: Promise<MetricReading>; }>()": "A byte-identical copy of Gloom Cloud's delivery model (metric-parity-fixture.json pins its hash), so it cannot import the helper. Its failed-read backoff is the rejectionMs rule; move it when both copies move together."
5
+ },
6
+ "src/plugins/builtin/cloud-tweets/table.tsx": {
7
+ "age: Date.now() - cached.fetchedAt < TWEET_CACHE_TTL_MS": "Feed results kept across tab switches and shown stale while a refetch runs; the entry's fetchedAt also drives the pane's auto-refresh and moves with every appended page, so the stamp is state the view reads.",
8
+ "map: new Map<string, { data: CloudTweetSearchResponse; fetchedAt: number; hasMore: boolean; }>()": "Feed results kept across tab switches and shown stale while a refetch runs; the entry's fetchedAt also drives the pane's auto-refresh and moves with every appended page, so the stamp is state the view reads."
9
+ },
10
+ "src/plugins/builtin/correlation/index.tsx": {
11
+ "map: new Map<string, { values: DailyClose[]; fetchedAt: number; }>()": "Not a TTL cache: the last good series per range, kept so a failed refresh still draws it. Nothing expires, and fetchedAt is shown with the data."
12
+ },
13
+ "src/plugins/builtin/portfolio-list/since-last-seen.ts": {
14
+ "map: new Map<string, Departure>()": "Not loaded data: what a pane showed when it went out of sight, read once on return against the reader's own away threshold, then used up."
15
+ },
16
+ "src/plugins/builtin/sec/command-bar-search.ts": {
17
+ "map: new Map<string, FocusRequest>()": "Not loaded data: a filing to focus handed from the command bar to the SEC pane, taken on first read and dropped after a few seconds unread."
18
+ }
19
+ }
@@ -32,7 +32,6 @@ export interface SyncBrokerInstanceArgs {
32
32
  tickerRepository: AppTickerRepositoryPort;
33
33
  existingTickers?: Map<string, TickerRecord>;
34
34
  resources?: AppResourceStorePort;
35
- persistResolvedBrokerConfig?: boolean;
36
35
  signal?: AbortSignal;
37
36
  deferPersistence?: boolean;
38
37
  }
@@ -69,7 +68,6 @@ export interface SyncBrokerInstancesArgs {
69
68
  tickerRepository: AppTickerRepositoryPort;
70
69
  existingTickers: Map<string, TickerRecord>;
71
70
  resources?: AppResourceStorePort;
72
- persistResolvedBrokerConfig?: boolean;
73
71
  /** Leave each result's ticker writes to `onResult`, which calls `result.commit()`. */
74
72
  deferPersistence?: boolean;
75
73
  onResult?: (result: SyncBrokerInstanceResult, instance: BrokerInstanceConfig, previousConfig: AppConfig) => void | Promise<void>;
@@ -168,9 +166,8 @@ async function maybePersistResolvedBrokerConfig(
168
166
  config: AppConfig,
169
167
  instance: BrokerInstanceConfig,
170
168
  broker: BrokerAdapter,
171
- persistResolvedBrokerConfig: boolean,
172
169
  ): Promise<AppConfig> {
173
- if (!persistResolvedBrokerConfig || !broker.getPersistedConfigUpdate) {
170
+ if (!broker.getPersistedConfigUpdate) {
174
171
  return config;
175
172
  }
176
173
 
@@ -203,7 +200,6 @@ export async function syncBrokerInstance({
203
200
  tickerRepository,
204
201
  existingTickers,
205
202
  resources,
206
- persistResolvedBrokerConfig = false,
207
203
  signal,
208
204
  deferPersistence = false,
209
205
  }: SyncBrokerInstanceArgs): Promise<SyncBrokerInstanceResult> {
@@ -336,7 +332,7 @@ export async function syncBrokerInstance({
336
332
  : []);
337
333
 
338
334
  nextConfig = removeStaleBrokerPortfolios(nextConfig, instance.id, currentPortfolioIds);
339
- nextConfig = await maybePersistResolvedBrokerConfig(nextConfig, instance, broker, persistResolvedBrokerConfig);
335
+ nextConfig = await maybePersistResolvedBrokerConfig(nextConfig, instance, broker);
340
336
  throwIfBrokerImportCancelled(signal);
341
337
  nextConfig = markBrokerInstanceSynced(nextConfig, instance.id, syncedAt);
342
338
  const syncedConfig = nextConfig;
@@ -436,7 +432,6 @@ export async function syncBrokerInstances({
436
432
  tickerRepository,
437
433
  existingTickers,
438
434
  resources,
439
- persistResolvedBrokerConfig = false,
440
435
  deferPersistence = false,
441
436
  onResult,
442
437
  }: SyncBrokerInstancesArgs): Promise<SyncBrokerInstancesResult> {
@@ -457,7 +452,6 @@ export async function syncBrokerInstances({
457
452
  tickerRepository,
458
453
  existingTickers: nextTickers,
459
454
  resources,
460
- persistResolvedBrokerConfig,
461
455
  deferPersistence,
462
456
  });
463
457
 
@@ -28,7 +28,7 @@ const SERIES_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._~%:/-]{0,239}$/;
28
28
  const chartSeriesLog = debugLog.createLogger("chart-series");
29
29
  const ranges = new Set<string>(TIME_RANGES);
30
30
  const periods = new Set(["auto", "daily", "weekly", "monthly", "quarterly", "annual", "ttm"]);
31
- const styles = new Set(["line", "area", "step", "columns", "points", "candles", "ohlc", "hlc"]);
31
+ const styles = new Set(["line", "area", "step", "columns", "points", "candles", "hollow", "heikin-ashi", "ohlc", "hlc"]);
32
32
  const transforms = new Set(["raw", "percent", "index100", "yoy", "qoq", "log"]);
33
33
  const shapes = new Set(["scalar", "ohlcv", "event", "band"]);
34
34
  const axes = new Set(["left", "right"]);
@@ -1,7 +1,7 @@
1
1
  import { isCryptoInstrumentType } from "../../tickers/search/ranking";
2
2
  import { isCryptoVenuePerpetual } from "../../tickers/instrument-kind";
3
3
  import type { Quote } from "../../types/financials";
4
- import { isCryptoPairSymbol, MAJOR_COIN_NAMES } from "../../utils/crypto-pair";
4
+ import { isCryptoPairSymbol, TOP_COIN_NAMES } from "../../utils/crypto-pair";
5
5
  import { isNotATickerMessage } from "../not-a-ticker";
6
6
  import { groupByCompany } from "../../tickers/company-name";
7
7
  import { DATA_SERVICE_UNREACHABLE_MESSAGE } from "../../utils/network-failure";
@@ -10,27 +10,49 @@ import type { TickerRecord } from "../../types/ticker";
10
10
  /** Where a crypto pair that will not load can still be read. `fn CRYP` takes no argument. */
11
11
  export const CRYPTO_BOARD_HINT = "Crypto prices may be briefly unavailable; the CRYP function (gloom fn CRYP) shows the crypto board.";
12
12
 
13
- // How the note names the coin, and what a listing's name has to say before the note applies.
14
- const MAJOR_COINS = MAJOR_COIN_NAMES;
13
+ // How the note names the coin, and which tickers it speaks for.
14
+ const MAJOR_COINS = TOP_COIN_NAMES;
15
+
16
+ /** What a coin note reads of a listing: its quote's, or what a saved listing keeps. */
17
+ interface CoinListing {
18
+ name?: string | null;
19
+ instrumentType?: string | null;
20
+ /** The feed says the price is old. */
21
+ stale?: boolean;
22
+ /** The exchange removed the listing, or its last trade is all that is known. */
23
+ delisting?: unknown;
24
+ }
15
25
 
16
26
  /**
17
- * A bare coin ticker usually resolves to a listed fund, not the coin. The note applies only when
18
- * the listing is not crypto and its own name says the coin (a "Bitcoin" trust), so a company that
19
- * happens to share the letters (Interlink Electronics on LINK) is left alone. Every command that
20
- * reads a bare symbol as a listing says it: quote, ticker, and the portfolio and watchlist adds.
27
+ * A bare coin ticker usually resolves to a listing that is not the coin. The note applies to a
28
+ * top coin's symbol whenever the listing is not a crypto product, and says how sure it is:
29
+ *
30
+ * - "SOL is Emeren Group ..., not solana. For solana use SOL-USD." when the listing cannot be
31
+ * what was meant: its name says the coin (a "Bitcoin" trust), or it is stale or delisted
32
+ * (SOL's Emeren Group last traded months ago).
33
+ * - "LINK is Interlink Electronics, Inc. Also a coin: LINK-USD." for a live company that
34
+ * happens to share the letters, which may well be the one meant.
35
+ *
36
+ * Every command that reads a bare symbol as a listing says it: quote, ticker, history, options,
37
+ * news, charts, alerts, and the portfolio and watchlist adds.
21
38
  */
22
- export function notTheCoinNote(symbol: string, quote: Pick<Quote, "name" | "instrumentType"> | null | undefined): string | null {
39
+ export function notTheCoinNote(symbol: string, listing: CoinListing | null | undefined): string | null {
23
40
  const coin = MAJOR_COINS.get(symbol);
24
- const type = quote?.instrumentType?.trim();
25
- const name = quote?.name?.trim();
41
+ const type = listing?.instrumentType?.trim();
42
+ const name = listing?.name?.trim();
26
43
  if (!coin || !name || !type || isCryptoInstrumentType(type)) return null;
27
- if (!new RegExp(`\\b${coin}\\b`, "i").test(name)) return null;
28
- return `${symbol} is ${name}, not ${coin}. For ${coin} use ${symbol}-USD.`;
44
+ const cannotBeIt = listing?.stale === true || !!listing?.delisting || new RegExp(`\\b${coin}\\b`, "i").test(name);
45
+ return cannotBeIt
46
+ ? `${symbol} is ${name}, not ${coin}. For ${coin} use ${symbol}-USD.`
47
+ : `${symbol} is ${name}${/[.!?]$/.test(name) ? "" : "."} Also a coin: ${symbol}-USD.`;
29
48
  }
30
49
 
31
- /** `notTheCoinNote` for a saved listing, by the name and asset category it was saved with. */
32
- export function savedListingCoinNote(symbol: string, ticker: Pick<TickerRecord, "metadata">): string | null {
33
- return notTheCoinNote(symbol, { name: ticker.metadata.name, instrumentType: ticker.metadata.assetCategory });
50
+ /**
51
+ * `notTheCoinNote` for a saved listing, by the name and asset category it was saved with.
52
+ * `ended` says the listing stopped trading, which the saved record does not keep.
53
+ */
54
+ export function savedListingCoinNote(symbol: string, ticker: Pick<TickerRecord, "metadata">, { ended = false }: { ended?: boolean } = {}): string | null {
55
+ return notTheCoinNote(symbol, { name: ticker.metadata.name, instrumentType: ticker.metadata.assetCategory, stale: ended });
34
56
  }
35
57
 
36
58
  /**
@@ -70,10 +92,10 @@ function failureReason(message: string, symbol: string): string {
70
92
 
71
93
  /**
72
94
  * Text-mode notes under a quote table: one line per distinct failure reason (with the crypto board
73
- * pointer when a failed symbol is a crypto pair, unless nothing could be reached), then a line for each bare coin ticker that
74
- * resolved to something else. `--exchange` names the listing on purpose, so it gets no coin note.
95
+ * pointer when a failed symbol is a crypto pair, unless nothing could be reached), then the perpetual futures line.
96
+ * A bare coin ticker read as another listing is said with the listing (`listingChoiceNotes`).
75
97
  */
76
- export function quoteNotes(results: QuoteNoteResult[], options: { exchange?: string } = {}): string[] {
98
+ export function quoteNotes(results: QuoteNoteResult[]): string[] {
77
99
  const failures = new Map<string, string[]>();
78
100
  for (const result of results) {
79
101
  if (!result.error || result.quote) continue;
@@ -87,12 +109,6 @@ export function quoteNotes(results: QuoteNoteResult[], options: { exchange?: str
87
109
  ? reason
88
110
  : `${symbols.join(", ")}: ${reason}${symbols.some(isCryptoPairSymbol) && reason !== DATA_SERVICE_UNREACHABLE_MESSAGE ? `${/[.!?]$/.test(reason) ? "" : "."} ${CRYPTO_BOARD_HINT}` : ""}`
89
111
  ));
90
- if (!options.exchange) {
91
- for (const result of results) {
92
- const note = notTheCoinNote(result.target.symbol, result.quote);
93
- if (note) notes.push(note);
94
- }
95
- }
96
112
  const perpetuals = results
97
113
  .filter((result) => result.quote && isCryptoVenuePerpetual(result.quote.instrumentType, result.quote.name))
98
114
  .map((result) => result.target.symbol);