gloomberb 0.17.6 → 0.18.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 (768) hide show
  1. package/README.md +2 -0
  2. package/README.zh-CN.md +2 -0
  3. package/package.json +6 -1
  4. package/patches/electrobun@1.18.1.patch +74 -0
  5. package/src/account/auth-model.ts +3 -2
  6. package/src/account/device-signin-dialog.tsx +38 -13
  7. package/src/account/device-signin.ts +2 -1
  8. package/src/account/upgrade-dialog.tsx +6 -2
  9. package/src/api-client/askg.ts +5 -3
  10. package/src/api-client/bank-figures.ts +73 -0
  11. package/src/api-client/credit-boards.ts +21 -1
  12. package/src/api-client/crypto-markets.ts +88 -1
  13. package/src/api-client/data.ts +104 -4
  14. package/src/api-client/debt-maturities.ts +18 -0
  15. package/src/api-client/equity-screener.ts +3 -0
  16. package/src/api-client/errors.ts +13 -0
  17. package/src/api-client/fund-facts.ts +72 -0
  18. package/src/api-client/futures-curve.ts +26 -3
  19. package/src/api-client/geo.ts +11 -1
  20. package/src/api-client/index.ts +25 -2
  21. package/src/api-client/insider-signals.ts +95 -11
  22. package/src/api-client/normalizers.ts +1 -0
  23. package/src/api-client/paths.ts +20 -1
  24. package/src/api-client/request.ts +11 -3
  25. package/src/api-client/research-activity.ts +15 -8
  26. package/src/api-client/socket.ts +6 -3
  27. package/src/api-client/supply-chain-graph.ts +2 -2
  28. package/src/api-client/supply-chain.ts +8 -0
  29. package/src/api-client/tape.ts +12 -0
  30. package/src/api-client/types.ts +66 -2
  31. package/src/api-client/upgrade-experiment.ts +4 -3
  32. package/src/api-client/wall-experiments.ts +5 -3
  33. package/src/api-client/wall-summary.ts +3 -1
  34. package/src/api-client/web-experiments.ts +5 -2
  35. package/src/api-client/yield-curves.ts +17 -0
  36. package/src/app/command-bar-intro.ts +38 -0
  37. package/src/app/keybindings/actions.ts +1 -1
  38. package/src/app/pane-runtime/pane-template-runtime.ts +35 -7
  39. package/src/app/pane-runtime/plugin-bindings.ts +4 -0
  40. package/src/app/pane-runtime/ticker-open-runtime.ts +4 -14
  41. package/src/app/pane-runtime/ticker-open-target.ts +31 -0
  42. package/src/app/runtime/desktop-deeplink.ts +3 -4
  43. package/src/app/runtime/plugin-bindings.ts +3 -2
  44. package/src/app/runtime/startup.ts +3 -4
  45. package/src/app/runtime/ticker-refresh.ts +13 -12
  46. package/src/app/runtime/update.ts +3 -2
  47. package/src/architecture/core-builtin-allowlist.json +0 -3
  48. package/src/architecture/silent-catch-allowlist.json +3 -0
  49. package/src/architecture/ui-literals-allowlist.json +186 -0
  50. package/src/architecture/ui-tokens-allowlist.json +40 -0
  51. package/src/ask-gloom/pane-context.ts +3 -1
  52. package/src/brokers/signed-in/catalog.ts +2 -1
  53. package/src/brokers/signed-in/client.ts +7 -2
  54. package/src/brokers/signed-in/sign-in-dialog.tsx +120 -26
  55. package/src/brokers/signed-in/sign-in.ts +22 -20
  56. package/src/brokers/sync-broker-instance.ts +2 -1
  57. package/src/capabilities/chart-series.ts +2 -0
  58. package/src/capabilities/registry.ts +1 -3
  59. package/src/cli/bank-section.ts +147 -0
  60. package/src/cli/base-converter.ts +5 -2
  61. package/src/cli/board-coins.ts +70 -0
  62. package/src/cli/commands/fred-series.ts +90 -0
  63. package/src/cli/commands/market.ts +340 -69
  64. package/src/cli/commands/news-search.ts +35 -2
  65. package/src/cli/commands/options-chain.ts +7 -5
  66. package/src/cli/commands/options-leaps.ts +3 -3
  67. package/src/cli/commands/overview.ts +78 -29
  68. package/src/cli/commands/plugins.ts +1 -0
  69. package/src/cli/commands/search.ts +87 -24
  70. package/src/cli/commands/system.ts +5 -2
  71. package/src/cli/commands/ticker.ts +337 -60
  72. package/src/cli/context.ts +2 -1
  73. package/src/cli/desktop-pane-shot.ts +11 -3
  74. package/src/cli/earnings-calendar.ts +2 -1
  75. package/src/cli/errors.ts +10 -0
  76. package/src/cli/export-source.ts +18 -3
  77. package/src/cli/glossary.ts +196 -14
  78. package/src/cli/history-rows.ts +60 -9
  79. package/src/cli/index-roots.ts +26 -0
  80. package/src/cli/index.ts +34 -9
  81. package/src/cli/known-gaps.ts +156 -22
  82. package/src/cli/listing-arg.ts +156 -12
  83. package/src/cli/listing-suggestion.ts +4 -2
  84. package/src/cli/name-entries.ts +76 -0
  85. package/src/cli/not-a-ticker.ts +15 -6
  86. package/src/cli/pane-functions/access-gate.ts +22 -0
  87. package/src/cli/pane-functions/capabilities.ts +62 -4
  88. package/src/cli/pane-functions/catalog.ts +94 -39
  89. package/src/cli/pane-functions/data.ts +6 -5
  90. package/src/cli/pane-functions/discovery.ts +1 -1
  91. package/src/cli/pane-functions/dom.ts +3 -3
  92. package/src/cli/pane-functions/freshness.ts +49 -8
  93. package/src/cli/pane-functions/function-help.ts +13 -10
  94. package/src/cli/pane-functions/headless.ts +177 -42
  95. package/src/cli/pane-functions/index.ts +64 -8
  96. package/src/cli/pane-functions/options.ts +17 -3
  97. package/src/cli/pane-functions/report-notices.ts +17 -3
  98. package/src/cli/pane-functions/resolver.ts +48 -13
  99. package/src/cli/pane-functions/screenshot.ts +124 -23
  100. package/src/cli/pane-functions/shot-options.ts +68 -0
  101. package/src/cli/perpetual-contract.ts +3 -1
  102. package/src/cli/report-tables.ts +10 -2
  103. package/src/cli/staged-tickers.ts +5 -0
  104. package/src/cli/statement-filings.ts +261 -0
  105. package/src/cli/symbol-args.ts +47 -8
  106. package/src/cli/symbol-list.ts +9 -2
  107. package/src/cli/symbol-rows.ts +269 -29
  108. package/src/cli/ticker-resolution.ts +148 -34
  109. package/src/cli/treasury-yields.ts +86 -0
  110. package/src/cli/types.ts +3 -0
  111. package/src/cli/uncovered-instrument.ts +20 -0
  112. package/src/components/chart/composite/axis-overlays.tsx +16 -9
  113. package/src/components/chart/composite/composite-chart.tsx +85 -6
  114. package/src/components/chart/composite/downsample.ts +19 -3
  115. package/src/components/chart/composite/format.ts +3 -0
  116. package/src/components/chart/composite/legend.tsx +155 -76
  117. package/src/components/chart/composite/panel-bitmap.ts +8 -2
  118. package/src/components/chart/composite/panel-surface.tsx +3 -3
  119. package/src/components/chart/composite/toolbar.tsx +12 -7
  120. package/src/components/chart/composite/types.ts +6 -0
  121. package/src/components/chart/composite/use-composite-chart-keyboard.ts +28 -1
  122. package/src/components/chart/curve/curve-surface.tsx +1 -1
  123. package/src/components/chart/static/series.ts +39 -0
  124. package/src/components/chart/static/trail-chart-surface.tsx +2 -1
  125. package/src/components/command-bar/commands/collection-list.ts +45 -15
  126. package/src/components/command-bar/commands/collection.ts +10 -3
  127. package/src/components/command-bar/commands/registry.ts +2 -2
  128. package/src/components/command-bar/list/badge.ts +4 -3
  129. package/src/components/command-bar/list/model.ts +5 -0
  130. package/src/components/command-bar/list/view.tsx +1 -1
  131. package/src/components/command-bar/panel/index.tsx +19 -8
  132. package/src/components/command-bar/panel/native-surface.ts +6 -4
  133. package/src/components/command-bar/panel/prompt-example.ts +22 -0
  134. package/src/components/command-bar/routes/root/provider-search.ts +3 -9
  135. package/src/components/command-bar/routes/root/results.ts +13 -0
  136. package/src/components/command-bar/routes/root/start-here.ts +90 -0
  137. package/src/components/command-bar/routes/ticker-search/results.ts +42 -3
  138. package/src/components/command-bar/routes/ticker-search/route.ts +3 -10
  139. package/src/components/command-bar/theme-picker.tsx +3 -1
  140. package/src/components/command-bar/workflow/builtin.ts +5 -1
  141. package/src/components/command-bar/workflow/collection-actions.ts +21 -12
  142. package/src/components/command-bar/workflow/ops.ts +21 -1
  143. package/src/{plugins/builtin/portfolio-list/command-bar.ts → components/command-bar/workflow/portfolio-position.ts} +42 -9
  144. package/src/components/command-bar/workflow/tickers.ts +6 -4
  145. package/src/components/feedback-dialog.tsx +1 -1
  146. package/src/components/form-modal/broker-step.tsx +7 -63
  147. package/src/components/form-modal/content.tsx +7 -2
  148. package/src/components/form-modal/field-row.tsx +6 -8
  149. package/src/components/form-modal/host.tsx +3 -3
  150. package/src/components/form-modal/model.ts +3 -0
  151. package/src/components/form-modal/routes.ts +17 -2
  152. package/src/components/index.ts +6 -6
  153. package/src/components/input-search-bar.tsx +43 -6
  154. package/src/components/layout/detached-pane-shell.tsx +0 -1
  155. package/src/components/layout/floating-pane.tsx +0 -1
  156. package/src/components/layout/header.tsx +8 -7
  157. package/src/components/layout/pane/footer/index.tsx +7 -4
  158. package/src/components/layout/pane/footer/model.ts +28 -2
  159. package/src/components/layout/pane/header.tsx +9 -19
  160. package/src/components/layout/pane/index.tsx +0 -1
  161. package/src/components/layout/pane/instrument-coverage-gate.tsx +57 -0
  162. package/src/components/layout/pane/pane-tabs.tsx +4 -3
  163. package/src/components/layout/pane/sizing.ts +9 -0
  164. package/src/components/layout/pane/ticker-fate-gate.tsx +8 -2
  165. package/src/components/layout/shell/pane/capture.ts +44 -20
  166. package/src/components/layout/status-bar.tsx +3 -6
  167. package/src/components/markdown-text.tsx +17 -2
  168. package/src/components/market-board/index.tsx +19 -8
  169. package/src/components/metric-treemap/heat-surface.tsx +2 -7
  170. package/src/components/onboarding/account-step/qr-panel.tsx +5 -2
  171. package/src/components/onboarding/onboarding-frame.tsx +24 -21
  172. package/src/components/onboarding/portfolio-step/positions-panel.tsx +20 -10
  173. package/src/components/onboarding/wizard-positions.ts +40 -11
  174. package/src/components/pane-settings-dialog/desktop.tsx +12 -50
  175. package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -1
  176. package/src/components/price-performance.tsx +18 -2
  177. package/src/components/reader/article-reader.tsx +11 -10
  178. package/src/components/reader/detail-reader.tsx +9 -5
  179. package/src/components/reader/reader-text.tsx +51 -24
  180. package/src/components/sign-in-code-panel.tsx +129 -75
  181. package/src/components/ticker/badge/index.tsx +2 -1
  182. package/src/components/ui/action-row.tsx +5 -2
  183. package/src/components/ui/audio-player.tsx +70 -0
  184. package/src/components/ui/card.tsx +75 -0
  185. package/src/components/ui/data-table/index.tsx +4 -2
  186. package/src/components/ui/data-table/types.ts +11 -0
  187. package/src/components/ui/detail-scroll-body.tsx +1 -1
  188. package/src/components/ui/dialog-actions.tsx +1 -21
  189. package/src/components/ui/display.tsx +13 -5
  190. package/src/components/ui/external-link.tsx +4 -2
  191. package/src/components/ui/fields.tsx +12 -0
  192. package/src/components/ui/figure.tsx +5 -3
  193. package/src/components/ui/frame.tsx +40 -6
  194. package/src/components/ui/icon.tsx +4 -0
  195. package/src/components/ui/index.ts +4 -1
  196. package/src/components/ui/list-view.tsx +8 -0
  197. package/src/components/ui/prose.tsx +97 -25
  198. package/src/components/ui/query-bar-keys.tsx +3 -1
  199. package/src/components/ui/query-bar.tsx +6 -0
  200. package/src/components/ui/quote.ts +42 -0
  201. package/src/components/ui/range-track.tsx +15 -2
  202. package/src/components/ui/reveal-in-scroll-box.ts +21 -3
  203. package/src/components/ui/select-button.tsx +38 -3
  204. package/src/components/ui/status.tsx +73 -4
  205. package/src/components/ui/table-layout.ts +20 -0
  206. package/src/components/use-pinned-moment.ts +19 -0
  207. package/src/core/app-runtime.ts +2 -2
  208. package/src/core/state/app/instrument.ts +18 -1
  209. package/src/core/state/app/layout.ts +6 -0
  210. package/src/core/state/app/state.ts +14 -2
  211. package/src/data/cached-query.ts +1 -1
  212. package/src/data/config/chart-settings.ts +3 -1
  213. package/src/data/config/home.ts +4 -1
  214. package/src/data/config/store/node.ts +9 -4
  215. package/src/data/config/store/normalize.ts +9 -0
  216. package/src/data/json-storage.ts +6 -3
  217. package/src/data/json-ticker-repository.ts +3 -1
  218. package/src/data/plugin-cache.ts +5 -1
  219. package/src/data/sqlite/json.ts +3 -1
  220. package/src/data/ticker-repository.ts +5 -2
  221. package/src/feedback/diagnostics.ts +2 -6
  222. package/src/i18n/es.ts +86 -6
  223. package/src/i18n/index.ts +3 -9
  224. package/src/i18n/ja.ts +86 -6
  225. package/src/i18n/ko.ts +86 -6
  226. package/src/i18n/zh-cn.ts +86 -6
  227. package/src/i18n/zh-tw.ts +86 -6
  228. package/src/layout-marketplace/gallery-desktop.tsx +8 -4
  229. package/src/market-data/bars-per-year.ts +43 -0
  230. package/src/market-data/bond-funds.ts +85 -0
  231. package/src/market-data/coordinator/auxiliary.ts +5 -2
  232. package/src/market-data/coordinator/index.ts +6 -5
  233. package/src/market-data/futures-contract.ts +102 -5
  234. package/src/market-data/hooks.tsx +20 -11
  235. package/src/market-data/market/bar-period.ts +36 -0
  236. package/src/market-data/market/format.ts +5 -13
  237. package/src/market-data/market/freshness.ts +1 -3
  238. package/src/market-data/market/index-level.ts +51 -0
  239. package/src/market-data/model-dividend-yield.ts +57 -16
  240. package/src/market-data/quotes/live-valuation.ts +48 -2
  241. package/src/market-data/realized-volatility.ts +16 -7
  242. package/src/market-data/tax-equivalent.ts +344 -0
  243. package/src/news/aggregator.ts +4 -2
  244. package/src/notifications/notification-log.ts +5 -3
  245. package/src/plugins/auto-update-node.ts +1 -0
  246. package/src/plugins/builtin/account-management/footer.ts +1 -1
  247. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  248. package/src/plugins/builtin/account-management/model.ts +1 -0
  249. package/src/plugins/builtin/account-management/pane.tsx +71 -19
  250. package/src/plugins/builtin/account-management/trial-cancel.ts +40 -0
  251. package/src/plugins/builtin/alerts/cli.ts +7 -18
  252. package/src/plugins/builtin/alerts/constants.ts +8 -0
  253. package/src/plugins/builtin/alerts/event-rows.ts +16 -0
  254. package/src/plugins/builtin/alerts/events-pane.tsx +2 -14
  255. package/src/plugins/builtin/alerts/headless.ts +182 -0
  256. package/src/plugins/builtin/alerts/index.tsx +3 -0
  257. package/src/plugins/builtin/alerts/pane.tsx +1 -7
  258. package/src/plugins/builtin/alerts/storage.ts +17 -1
  259. package/src/plugins/builtin/analytics/index.tsx +6 -1
  260. package/src/plugins/builtin/analytics/metrics.ts +47 -9
  261. package/src/plugins/builtin/analytics/risk-headless.ts +14 -2
  262. package/src/plugins/builtin/analytics/risk-math.ts +83 -0
  263. package/src/plugins/builtin/analytics/risk-model.ts +97 -4
  264. package/src/plugins/builtin/analytics/risk-pane.tsx +11 -6
  265. package/src/plugins/builtin/analytics/sector-model.ts +14 -3
  266. package/src/plugins/builtin/awards/headless.ts +6 -5
  267. package/src/plugins/builtin/awards/index.tsx +1 -1
  268. package/src/plugins/builtin/backtest/client.ts +38 -8
  269. package/src/plugins/builtin/backtest/engine.ts +25 -13
  270. package/src/plugins/builtin/backtest/export.ts +27 -0
  271. package/src/plugins/builtin/backtest/format.ts +19 -3
  272. package/src/plugins/builtin/backtest/headless.ts +42 -28
  273. package/src/plugins/builtin/backtest/index.tsx +3 -0
  274. package/src/plugins/builtin/backtest/pane.tsx +39 -10
  275. package/src/plugins/builtin/backtest/report.ts +160 -0
  276. package/src/plugins/builtin/backtest/rules.ts +2 -0
  277. package/src/plugins/builtin/backtest/window.ts +112 -0
  278. package/src/plugins/builtin/bond-calculator/headless.ts +3 -2
  279. package/src/plugins/builtin/broker-manager/add-flow.tsx +4 -4
  280. package/src/plugins/builtin/catalysts/headless.ts +24 -13
  281. package/src/plugins/builtin/catalysts/index.tsx +4 -2
  282. package/src/plugins/builtin/catalysts/model.ts +18 -1
  283. package/src/plugins/builtin/catalysts/pane.tsx +2 -2
  284. package/src/plugins/builtin/cds/client.ts +28 -5
  285. package/src/plugins/builtin/cds/headless.ts +183 -31
  286. package/src/plugins/builtin/cds/index.tsx +1 -1
  287. package/src/plugins/builtin/cds/model.ts +229 -53
  288. package/src/plugins/builtin/cds/pane.tsx +82 -26
  289. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -1
  290. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +39 -1
  291. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +77 -0
  292. package/src/plugins/builtin/chart-composer/chart-spec.ts +15 -0
  293. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +22 -2
  294. package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +2 -2
  295. package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -2
  296. package/src/plugins/builtin/chart-composer/headless.ts +99 -21
  297. package/src/plugins/builtin/chart-composer/index.tsx +70 -13
  298. package/src/plugins/builtin/chart-composer/pane.tsx +65 -15
  299. package/src/plugins/builtin/chart-composer/presets.ts +79 -11
  300. package/src/plugins/builtin/chart-composer/series-catalog.ts +50 -14
  301. package/src/plugins/builtin/chart-composer/series-expression.ts +9 -7
  302. package/src/plugins/builtin/chart-composer/series-listings.ts +178 -0
  303. package/src/plugins/builtin/chart-composer/session-range.ts +87 -0
  304. package/src/plugins/builtin/chat/attachments/desktop.tsx +4 -1
  305. package/src/plugins/builtin/chat/content/composer-runtime.ts +49 -14
  306. package/src/plugins/builtin/chat/content/composer.tsx +22 -1
  307. package/src/plugins/builtin/chat/content/index.tsx +35 -5
  308. package/src/plugins/builtin/chat/content/layout-metrics.ts +3 -0
  309. package/src/plugins/builtin/chat/content/shortcuts.ts +32 -13
  310. package/src/plugins/builtin/chat/content/snapshot.ts +29 -6
  311. package/src/plugins/builtin/chat/ghost-user.ts +11 -3
  312. package/src/plugins/builtin/chat/layout.ts +2 -0
  313. package/src/plugins/builtin/chat/message/desktop.tsx +13 -5
  314. package/src/plugins/builtin/chat/message/header.tsx +26 -4
  315. package/src/plugins/builtin/chat/message/pane-chip.tsx +131 -35
  316. package/src/plugins/builtin/chat/message/render-state.ts +28 -1
  317. package/src/plugins/builtin/chat/message/terminal.tsx +9 -3
  318. package/src/plugins/builtin/chat/pane-links/composer.ts +87 -0
  319. package/src/plugins/builtin/chat/pane-links/model.ts +1 -1
  320. package/src/plugins/builtin/chat/share/flow.tsx +9 -73
  321. package/src/plugins/builtin/cloud/askg/controller.ts +5 -3
  322. package/src/plugins/builtin/cloud/askg/executor.ts +3 -1
  323. package/src/plugins/builtin/cloud/askg/lock-note.ts +16 -0
  324. package/src/plugins/builtin/cloud/askg/pane.tsx +6 -2
  325. package/src/plugins/builtin/cloud/auth-actions.tsx +4 -2
  326. package/src/plugins/builtin/cloud/company-picker.tsx +21 -26
  327. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +5 -3
  328. package/src/plugins/builtin/cloud/plugin.tsx +2 -0
  329. package/src/plugins/builtin/cloud/team/module.tsx +5 -1
  330. package/src/plugins/builtin/cloud/team/pane.tsx +11 -4
  331. package/src/plugins/builtin/cloud/team/views.ts +11 -4
  332. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +9 -4
  333. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +3 -2
  334. package/src/plugins/builtin/cloud/thesis/detail.tsx +2 -1
  335. package/src/plugins/builtin/cloud/thesis/exposure.ts +3 -0
  336. package/src/plugins/builtin/cloud/thesis/prompts.tsx +3 -2
  337. package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +2 -1
  338. package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +2 -1
  339. package/src/plugins/builtin/cloud/verification-status-widget.tsx +2 -1
  340. package/src/plugins/builtin/commodities-energy/index.tsx +8 -5
  341. package/src/plugins/builtin/commodities-energy/view-state.ts +11 -0
  342. package/src/plugins/builtin/company-attention/headless.ts +15 -7
  343. package/src/plugins/builtin/company-attention/index.tsx +1 -1
  344. package/src/plugins/builtin/company-attention/model.ts +49 -4
  345. package/src/plugins/builtin/company-attention/pane.tsx +11 -9
  346. package/src/plugins/builtin/company-kpis/headless.ts +4 -3
  347. package/src/plugins/builtin/company-kpis/index.tsx +2 -2
  348. package/src/plugins/builtin/company-kpis/pane.tsx +9 -3
  349. package/src/plugins/builtin/congress-trades/headless.ts +23 -2
  350. package/src/plugins/builtin/correlation/relationship/pane.tsx +18 -46
  351. package/src/plugins/builtin/cot/headless.ts +5 -2
  352. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +41 -13
  353. package/src/plugins/builtin/credit-boards/client.ts +9 -2
  354. package/src/plugins/builtin/credit-boards/headless.ts +93 -27
  355. package/src/plugins/builtin/credit-boards/index.tsx +15 -4
  356. package/src/plugins/builtin/credit-boards/model.ts +105 -6
  357. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +14 -3
  358. package/src/plugins/builtin/credit-documents/detail.tsx +2 -1
  359. package/src/plugins/builtin/credit-documents/index.tsx +3 -0
  360. package/src/plugins/builtin/credit-documents/model.ts +3 -10
  361. package/src/plugins/builtin/crypto-board/client.ts +152 -2
  362. package/src/plugins/builtin/crypto-board/headless.ts +138 -19
  363. package/src/plugins/builtin/crypto-board/index.tsx +5 -3
  364. package/src/plugins/builtin/crypto-board/model.ts +210 -12
  365. package/src/plugins/builtin/crypto-board/pane.tsx +148 -9
  366. package/src/plugins/builtin/crypto-board/series.ts +52 -1
  367. package/src/plugins/builtin/crypto-board/supply.ts +123 -0
  368. package/src/plugins/builtin/debt-maturities/client.ts +54 -1
  369. package/src/plugins/builtin/debt-maturities/headless.ts +27 -3
  370. package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
  371. package/src/plugins/builtin/debt-maturities/model.ts +58 -13
  372. package/src/plugins/builtin/debt-maturities/pane.tsx +47 -7
  373. package/src/plugins/builtin/dividend-yield/client.ts +42 -36
  374. package/src/plugins/builtin/dividend-yield/headless.ts +113 -18
  375. package/src/plugins/builtin/dividend-yield/index.tsx +5 -2
  376. package/src/plugins/builtin/dividend-yield/pane.tsx +37 -13
  377. package/src/plugins/builtin/dividend-yield/provider-client.ts +7 -3
  378. package/src/plugins/builtin/dividend-yield/ranking.ts +187 -0
  379. package/src/plugins/builtin/dividend-yield/reference-price.ts +28 -5
  380. package/src/plugins/builtin/dividend-yield/types.ts +6 -1
  381. package/src/plugins/builtin/dividend-yield/view.ts +1 -1
  382. package/src/plugins/builtin/docs/blocks.tsx +54 -8
  383. package/src/plugins/builtin/docs/pane.tsx +8 -13
  384. package/src/plugins/builtin/earnings/headless.ts +3 -2
  385. package/src/plugins/builtin/earnings-calls/data.ts +51 -0
  386. package/src/plugins/builtin/earnings-calls/headless.ts +7 -0
  387. package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
  388. package/src/plugins/builtin/earnings-calls/model.ts +97 -0
  389. package/src/plugins/builtin/earnings-calls/pane.tsx +107 -7
  390. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +140 -22
  391. package/src/plugins/builtin/earnings-calls/use-call-playback.ts +194 -0
  392. package/src/plugins/builtin/econ/calendar-model.ts +22 -0
  393. package/src/plugins/builtin/econ/calendar-source.ts +35 -0
  394. package/src/plugins/builtin/econ/index.tsx +40 -8
  395. package/src/plugins/builtin/econ-statistics/defs.ts +7 -0
  396. package/src/plugins/builtin/econ-statistics/detail.tsx +8 -1
  397. package/src/plugins/builtin/econ-statistics/headless.ts +26 -5
  398. package/src/plugins/builtin/econ-statistics/pane.tsx +4 -2
  399. package/src/plugins/builtin/econ-statistics/stats.ts +10 -0
  400. package/src/plugins/builtin/econ-statistics/view.ts +19 -0
  401. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  402. package/src/plugins/builtin/equity-screener/model.ts +46 -4
  403. package/src/plugins/builtin/equity-screener/pane.tsx +3 -0
  404. package/src/plugins/builtin/estimate-revisions/client.ts +32 -22
  405. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  406. package/src/plugins/builtin/estimate-revisions/index.tsx +1 -0
  407. package/src/plugins/builtin/estimate-revisions/pane.tsx +5 -3
  408. package/src/plugins/builtin/executives/index.tsx +1 -0
  409. package/src/plugins/builtin/exposure/editor.tsx +3 -3
  410. package/src/plugins/builtin/fear-greed/index.tsx +9 -0
  411. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  412. package/src/plugins/builtin/futures/index.tsx +2 -2
  413. package/src/plugins/builtin/futures/model.ts +34 -21
  414. package/src/plugins/builtin/futures/table.tsx +1 -1
  415. package/src/plugins/builtin/futures/use-board-terms.ts +2 -2
  416. package/src/plugins/builtin/futures-curve/headless.ts +4 -2
  417. package/src/plugins/builtin/futures-curve/model.ts +5 -7
  418. package/src/plugins/builtin/help/index.tsx +55 -8
  419. package/src/plugins/builtin/help/shortcut-table.tsx +15 -2
  420. package/src/plugins/builtin/holders/headless.ts +12 -0
  421. package/src/plugins/builtin/insider/headless.ts +311 -41
  422. package/src/plugins/builtin/insider/index.tsx +280 -106
  423. package/src/plugins/builtin/insider/insiders.tsx +224 -0
  424. package/src/plugins/builtin/insider/signals-cells.tsx +52 -0
  425. package/src/plugins/builtin/insider/signals-client.ts +135 -11
  426. package/src/plugins/builtin/insider/signals-model.ts +628 -56
  427. package/src/plugins/builtin/insider/signals.tsx +283 -83
  428. package/src/plugins/builtin/ipo-calendar/headless.ts +17 -7
  429. package/src/plugins/builtin/ipo-calendar/index.tsx +23 -3
  430. package/src/plugins/builtin/ipo-calendar/model.ts +84 -36
  431. package/src/plugins/builtin/ipo-calendar/pane.tsx +20 -8
  432. package/src/plugins/builtin/iv-history/headless.ts +2 -0
  433. package/src/plugins/builtin/jobs/headless.ts +1 -0
  434. package/src/plugins/builtin/jobs/index.tsx +1 -0
  435. package/src/plugins/builtin/jodi/headless.ts +2 -0
  436. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  437. package/src/plugins/builtin/kelly-sizer/portfolio.ts +1 -1
  438. package/src/plugins/builtin/market-heatmap/headless.ts +1 -0
  439. package/src/plugins/builtin/market-heatmap/portfolio.ts +3 -1
  440. package/src/plugins/builtin/market-valuation/pane.tsx +1 -1
  441. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +31 -57
  442. package/src/plugins/builtin/mna/distress/headless.ts +2 -1
  443. package/src/plugins/builtin/mna/headless.ts +14 -2
  444. package/src/plugins/builtin/mna/index.tsx +1 -0
  445. package/src/plugins/builtin/news/headless.ts +4 -0
  446. package/src/plugins/builtin/news/wire/news/footer.ts +5 -3
  447. package/src/plugins/builtin/news/wire/news/table.tsx +57 -11
  448. package/src/plugins/builtin/notes/cli.ts +8 -4
  449. package/src/plugins/builtin/notes/files.ts +66 -20
  450. package/src/plugins/builtin/notes/migration.ts +18 -6
  451. package/src/plugins/builtin/notes/owner.tsx +3 -2
  452. package/src/plugins/builtin/notes/quick-notes-pane.tsx +3 -2
  453. package/src/plugins/builtin/options-scenario/client.ts +7 -4
  454. package/src/plugins/builtin/options-scenario/headless.ts +5 -3
  455. package/src/plugins/builtin/pe-band/index.tsx +1 -1
  456. package/src/plugins/builtin/pe-band/model.ts +1 -15
  457. package/src/plugins/builtin/perps/headless.ts +30 -13
  458. package/src/plugins/builtin/perps/model.ts +45 -3
  459. package/src/plugins/builtin/perps/pane.tsx +7 -7
  460. package/src/plugins/builtin/portfolio-list/cell-version.ts +10 -4
  461. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +128 -14
  462. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +108 -46
  463. package/src/plugins/builtin/portfolio-list/cli/render.ts +368 -45
  464. package/src/plugins/builtin/portfolio-list/cli/shared.ts +8 -2
  465. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +24 -34
  466. package/src/plugins/builtin/portfolio-list/grid.tsx +3 -1
  467. package/src/plugins/builtin/portfolio-list/headless.ts +151 -29
  468. package/src/plugins/builtin/portfolio-list/income.ts +296 -0
  469. package/src/plugins/builtin/portfolio-list/metrics.ts +2 -2
  470. package/src/plugins/builtin/portfolio-list/pane/futures-terms.ts +58 -0
  471. package/src/plugins/builtin/portfolio-list/pane/index.tsx +27 -10
  472. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +47 -28
  473. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +3 -2
  474. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +10 -3
  475. package/src/plugins/builtin/portfolio-list/summary/index.tsx +26 -4
  476. package/src/plugins/builtin/portfolio-list/summary/totals.ts +31 -3
  477. package/src/plugins/builtin/portfolio-list/table.tsx +25 -5
  478. package/src/plugins/builtin/power/prices-headless.ts +2 -1
  479. package/src/plugins/builtin/realized-vol/chart-model.ts +29 -5
  480. package/src/plugins/builtin/realized-vol/charts.tsx +14 -8
  481. package/src/plugins/builtin/realized-vol/evidence.ts +2 -0
  482. package/src/plugins/builtin/realized-vol/headless.ts +6 -4
  483. package/src/plugins/builtin/realized-vol/model.ts +5 -1
  484. package/src/plugins/builtin/realized-vol/pane.tsx +31 -11
  485. package/src/plugins/builtin/realized-vol/settings.ts +1 -1
  486. package/src/plugins/builtin/research/analyst-headless.ts +4 -0
  487. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -0
  488. package/src/plugins/builtin/research/event-model.ts +22 -2
  489. package/src/plugins/builtin/research/headless-schema.ts +1 -0
  490. package/src/plugins/builtin/research/headless.ts +3 -0
  491. package/src/plugins/builtin/research/index.tsx +3 -0
  492. package/src/plugins/builtin/research/relative-valuation-columns.ts +27 -5
  493. package/src/plugins/builtin/research/relative-valuation-headless.ts +2 -0
  494. package/src/plugins/builtin/revenue-breakdown/index.tsx +1 -0
  495. package/src/plugins/builtin/reverse-dcf/headless.ts +9 -6
  496. package/src/plugins/builtin/reverse-dcf/index.tsx +2 -2
  497. package/src/plugins/builtin/reverse-dcf/model.ts +51 -4
  498. package/src/plugins/builtin/reverse-dcf/pane.tsx +23 -10
  499. package/src/plugins/builtin/risk-factors/headless.ts +7 -0
  500. package/src/plugins/builtin/risk-factors/index.tsx +1 -0
  501. package/src/plugins/builtin/scanner/feed.ts +5 -3
  502. package/src/plugins/builtin/scanner/headless.ts +9 -57
  503. package/src/plugins/builtin/scanner/index.ts +1 -2
  504. package/src/plugins/builtin/sec/forms.ts +2 -6
  505. package/src/plugins/builtin/sec/fund-facts-view.tsx +53 -0
  506. package/src/plugins/builtin/sec/fund-facts.ts +328 -0
  507. package/src/plugins/builtin/sec/headless.ts +140 -44
  508. package/src/plugins/builtin/sec/index.tsx +95 -30
  509. package/src/plugins/builtin/sec/model.ts +2 -44
  510. package/src/plugins/builtin/shared/headless-options.ts +10 -0
  511. package/src/plugins/builtin/shared/locked-rows.tsx +3 -1
  512. package/src/plugins/builtin/shared/move-rank.ts +108 -0
  513. package/src/plugins/builtin/shared/report-freshness.ts +25 -9
  514. package/src/plugins/builtin/shared/scanner-capture.ts +95 -0
  515. package/src/plugins/builtin/shared/treasury-cutoffs.ts +15 -0
  516. package/src/plugins/builtin/shared/use-quote-board.ts +1 -0
  517. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -0
  518. package/src/plugins/builtin/shared/volatility-indices.ts +32 -0
  519. package/src/plugins/builtin/short-interest/headless.ts +3 -0
  520. package/src/plugins/builtin/short-interest/watch-model.ts +2 -2
  521. package/src/plugins/builtin/social-mentions/client.ts +8 -1
  522. package/src/plugins/builtin/social-mentions/pane.tsx +8 -6
  523. package/src/plugins/builtin/supply-chain/client.ts +12 -3
  524. package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +9 -5
  525. package/src/plugins/builtin/supply-chain/evidence.ts +3 -1
  526. package/src/plugins/builtin/supply-chain/flow-layout.ts +2 -1
  527. package/src/plugins/builtin/supply-chain/flow.tsx +23 -16
  528. package/src/plugins/builtin/supply-chain/graph-client.ts +8 -5
  529. package/src/plugins/builtin/supply-chain/graph-evidence.ts +1 -1
  530. package/src/plugins/builtin/supply-chain/graph-model.ts +10 -2
  531. package/src/plugins/builtin/supply-chain/graph-pane.tsx +9 -7
  532. package/src/plugins/builtin/supply-chain/graph.tsx +8 -6
  533. package/src/plugins/builtin/supply-chain/headless.ts +69 -27
  534. package/src/plugins/builtin/supply-chain/index.tsx +1 -1
  535. package/src/plugins/builtin/supply-chain/model.ts +76 -15
  536. package/src/plugins/builtin/supply-chain/pane.tsx +29 -15
  537. package/src/plugins/builtin/supply-chain/row-evidence.tsx +5 -4
  538. package/src/plugins/builtin/themes/model.ts +2 -5
  539. package/src/plugins/builtin/thirteenf/api.ts +50 -2
  540. package/src/plugins/builtin/thirteenf/data.ts +250 -11
  541. package/src/plugins/builtin/thirteenf/headless.ts +311 -28
  542. package/src/plugins/builtin/thirteenf/index.tsx +4 -1
  543. package/src/plugins/builtin/thirteenf/model.ts +244 -19
  544. package/src/plugins/builtin/thirteenf/overlap-pane.tsx +133 -33
  545. package/src/plugins/builtin/thirteenf/overlap.ts +1 -1
  546. package/src/plugins/builtin/thirteenf/pane.tsx +24 -5
  547. package/src/plugins/builtin/thirteenf/report-notes.ts +78 -0
  548. package/src/plugins/builtin/thirteenf/types.ts +49 -0
  549. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -8
  550. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +17 -1
  551. package/src/plugins/builtin/ticker-detail/financials/model.ts +2 -42
  552. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +1 -1
  553. package/src/plugins/builtin/ticker-detail/headless.ts +23 -10
  554. package/src/plugins/builtin/ticker-detail/overview/components.tsx +27 -3
  555. package/src/plugins/builtin/ticker-detail/overview/dividend.ts +60 -0
  556. package/src/plugins/builtin/ticker-detail/overview/model.ts +46 -9
  557. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +3 -0
  558. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +36 -3
  559. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +18 -19
  560. package/src/plugins/builtin/time-sales/client.ts +7 -4
  561. package/src/plugins/builtin/time-sales/headless.ts +71 -22
  562. package/src/plugins/builtin/time-sales/model.ts +12 -0
  563. package/src/plugins/builtin/time-sales/pane.tsx +31 -7
  564. package/src/plugins/builtin/time-sales/window.ts +128 -0
  565. package/src/plugins/builtin/trace-bonds/client.ts +351 -33
  566. package/src/plugins/builtin/trace-bonds/headless.ts +60 -22
  567. package/src/plugins/builtin/trace-bonds/index.tsx +4 -2
  568. package/src/plugins/builtin/trace-bonds/model.ts +27 -25
  569. package/src/plugins/builtin/trace-bonds/pane.tsx +63 -45
  570. package/src/plugins/builtin/treasury-auctions/cache.ts +61 -15
  571. package/src/plugins/builtin/treasury-auctions/client.ts +45 -10
  572. package/src/plugins/builtin/treasury-auctions/headless.ts +35 -13
  573. package/src/plugins/builtin/treasury-auctions/index.tsx +5 -2
  574. package/src/plugins/builtin/treasury-auctions/model.ts +20 -16
  575. package/src/plugins/builtin/treasury-auctions/pane.tsx +29 -12
  576. package/src/plugins/builtin/vol-surface/charts.tsx +2 -1
  577. package/src/plugins/builtin/vol-surface/headless.ts +3 -2
  578. package/src/plugins/builtin/vol-surface/pane.tsx +2 -1
  579. package/src/plugins/builtin/vol-surface/stored.ts +4 -7
  580. package/src/plugins/builtin/volatility/chart-model.ts +16 -15
  581. package/src/plugins/builtin/volatility/charts.tsx +5 -5
  582. package/src/plugins/builtin/volatility/client.ts +73 -2
  583. package/src/plugins/builtin/volatility/evidence.ts +13 -14
  584. package/src/plugins/builtin/volatility/headless.ts +13 -7
  585. package/src/plugins/builtin/volatility/index.tsx +1 -1
  586. package/src/plugins/builtin/volatility/model.ts +104 -38
  587. package/src/plugins/builtin/volatility/pane.tsx +42 -24
  588. package/src/plugins/builtin/volatility/series.ts +22 -13
  589. package/src/plugins/builtin/world-venue-map/geo-series.ts +9 -0
  590. package/src/plugins/builtin/world-venue-map/headless.ts +8 -5
  591. package/src/plugins/builtin/world-venue-map/index.ts +16 -2
  592. package/src/plugins/builtin/world-venue-map/map.tsx +13 -17
  593. package/src/plugins/builtin/yield-curve/curves.ts +29 -5
  594. package/src/plugins/builtin/yield-curve/headless.ts +41 -8
  595. package/src/plugins/builtin/yield-curve/history.ts +5 -5
  596. package/src/plugins/builtin/yield-curve/index.tsx +30 -13
  597. package/src/plugins/compat.ts +3 -1
  598. package/src/plugins/dependencies.ts +8 -2
  599. package/src/plugins/host-link.ts +12 -5
  600. package/src/plugins/installer.ts +16 -7
  601. package/src/plugins/loader.ts +9 -2
  602. package/src/plugins/marketplace/activation.ts +10 -4
  603. package/src/plugins/registry/host-actions.ts +3 -0
  604. package/src/plugins/registry/index.ts +3 -1
  605. package/src/{plugins/builtin/portfolio-list → portfolio}/allocation.ts +74 -12
  606. package/src/{plugins/builtin/portfolio-list → portfolio}/column-values.ts +140 -53
  607. package/src/portfolio/futures-positions.ts +213 -0
  608. package/src/portfolio/futures-terms.ts +75 -0
  609. package/src/portfolio/manual-lots.ts +78 -0
  610. package/src/portfolio/mutations.ts +54 -10
  611. package/src/portfolio/position-metrics.ts +44 -10
  612. package/src/portfolio/valuation-basis.ts +31 -4
  613. package/src/public/__snapshots__/public-api.test.ts.snap +4 -0
  614. package/src/public/time-series.ts +2 -1
  615. package/src/remote/confirm-change.tsx +2 -1
  616. package/src/remote/server.ts +1 -1
  617. package/src/remote/watchlist.ts +9 -1
  618. package/src/renderers/dom/data-table/row.tsx +1 -1
  619. package/src/renderers/dom/desktop/audio-player.tsx +312 -0
  620. package/src/renderers/dom/desktop/control-styles.ts +17 -82
  621. package/src/renderers/dom/desktop/controls.tsx +39 -63
  622. package/src/renderers/dom/desktop/dialog-actions.tsx +13 -53
  623. package/src/renderers/dom/desktop/icons.tsx +8 -0
  624. package/src/renderers/dom/desktop/list-view.tsx +6 -6
  625. package/src/renderers/dom/desktop/popover.tsx +0 -3
  626. package/src/renderers/dom/desktop/query-bar.tsx +6 -4
  627. package/src/renderers/dom/desktop/select-field.tsx +9 -8
  628. package/src/renderers/dom/desktop/stack-header.ts +3 -0
  629. package/src/renderers/dom/dom-ui-host.tsx +3 -2
  630. package/src/renderers/dom/host/chart-surface.tsx +3 -3
  631. package/src/renderers/dom/host/size-observer.ts +33 -4
  632. package/src/renderers/dom/host/surface-3d.tsx +3 -2
  633. package/src/renderers/dom/host/surface-fit.ts +4 -1
  634. package/src/renderers/dom/host/tabs.tsx +22 -132
  635. package/src/renderers/dom/styles.css +358 -160
  636. package/src/renderers/electrobun/bun/core-capabilities.ts +1 -1
  637. package/src/renderers/electrobun/bun/desktop/backend.ts +1 -1
  638. package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
  639. package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +43 -1
  640. package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +5 -1
  641. package/src/renderers/electrobun/shared/rpc-codec.ts +7 -0
  642. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +21 -17
  643. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
  644. package/src/renderers/electrobun/view/desktop/window/bridge.ts +6 -8
  645. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +18 -0
  646. package/src/series/capability.ts +1 -0
  647. package/src/series/files.ts +9 -3
  648. package/src/series/registry.ts +40 -14
  649. package/src/series/resolve.ts +121 -18
  650. package/src/series/standalone.ts +3 -2
  651. package/src/series/types.ts +55 -1
  652. package/src/series/validate.ts +39 -0
  653. package/src/shares/api.ts +26 -7
  654. package/src/shares/bug-report-dialog.tsx +2 -2
  655. package/src/shares/bug-report-flow.tsx +4 -7
  656. package/src/shares/capture-flow.ts +5 -4
  657. package/src/shares/capture-preview.tsx +2 -2
  658. package/src/shares/chat.ts +7 -27
  659. package/src/shares/flow.tsx +86 -40
  660. package/src/shares/location.ts +1 -3
  661. package/src/shares/pane-image.ts +11 -9
  662. package/src/shares/pane.ts +5 -2
  663. package/src/shares/payload.ts +1 -1
  664. package/src/shares/personal-pane.ts +3 -3
  665. package/src/shares/portable-layout.ts +9 -2
  666. package/src/shares/ready.ts +42 -0
  667. package/src/shares/test-harness.ts +62 -0
  668. package/src/sources/gloomberb-cloud/batched-lookups.ts +73 -0
  669. package/src/sources/gloomberb-cloud/index.ts +64 -12
  670. package/src/sources/gloomberb-cloud/normalizers.ts +31 -2
  671. package/src/sources/gloomberb-cloud/perpetuals.ts +20 -1
  672. package/src/sources/history-omitted.ts +34 -0
  673. package/src/sources/history-result.ts +3 -0
  674. package/src/sources/history-retention.ts +1 -1
  675. package/src/sources/provider-errors.ts +43 -1
  676. package/src/sources/provider-router/batches.ts +3 -2
  677. package/src/sources/provider-router/cache.ts +2 -1
  678. package/src/sources/provider-router/cached-routes.ts +9 -3
  679. package/src/sources/provider-router/history.ts +9 -8
  680. package/src/sources/provider-router/index.ts +18 -3
  681. package/src/sources/provider-router/primary.ts +5 -4
  682. package/src/sources/provider-router/quote-route.ts +3 -1
  683. package/src/sources/provider-router/routing.ts +2 -1
  684. package/src/sources/provider-router/search.ts +47 -13
  685. package/src/sources/provider-router/streaming.ts +7 -4
  686. package/src/sources/sec-edgar.ts +1 -3
  687. package/src/state/app/context/index.tsx +9 -13
  688. package/src/state/app/tab-cursor.ts +149 -12
  689. package/src/state/hooks/quote-streaming.ts +3 -2
  690. package/src/state/hooks/session-snapshot.ts +3 -2
  691. package/src/state/persist-scheduler.ts +12 -4
  692. package/src/sync/controller.ts +22 -1
  693. package/src/sync/core-contributors.ts +58 -15
  694. package/src/team/collections-sync.ts +14 -4
  695. package/src/team/collections.ts +40 -11
  696. package/src/team/store.ts +5 -4
  697. package/src/telemetry/attention-counts.ts +8 -2
  698. package/src/telemetry/crash-reports-dom.ts +3 -0
  699. package/src/telemetry/crash-reports-node.ts +7 -4
  700. package/src/telemetry/crash-reports.ts +11 -11
  701. package/src/telemetry/usage-counts.ts +16 -12
  702. package/src/theme/colors.ts +2 -0
  703. package/src/theme/font-scale.ts +2 -0
  704. package/src/theme/ui-tokens.ts +71 -0
  705. package/src/tickers/batched-lookups.ts +186 -0
  706. package/src/tickers/company-name-listing.ts +97 -0
  707. package/src/tickers/company-name.ts +13 -0
  708. package/src/tickers/instrument-coverage.ts +112 -0
  709. package/src/tickers/listing-fate.ts +49 -9
  710. package/src/tickers/listing-venues.ts +24 -5
  711. package/src/tickers/open-target.ts +13 -5
  712. package/src/tickers/search/index.ts +112 -35
  713. package/src/tickers/search/ranking.ts +49 -1
  714. package/src/tickers/terminal-codes.ts +79 -0
  715. package/src/tickers/ticker-changes.ts +74 -3
  716. package/src/tickers/us-listing.ts +2 -1
  717. package/src/time-series/chart-data.ts +79 -8
  718. package/src/time-series/fundamentals.ts +55 -7
  719. package/src/time-series/ice-credit.ts +17 -2
  720. package/src/time-series/live-bars.ts +21 -8
  721. package/src/time-series/live-quotes.ts +2 -3
  722. package/src/time-series/price-comparison.ts +6 -1
  723. package/src/time-series/resolve.ts +65 -10
  724. package/src/time-series/spec.ts +2 -1
  725. package/src/time-series/studies.ts +3 -3
  726. package/src/time-series/types.ts +22 -0
  727. package/src/time-series/use-chart-resolution.ts +2 -1
  728. package/src/types/config.ts +7 -0
  729. package/src/types/data-provider.ts +20 -0
  730. package/src/types/headless.ts +142 -1
  731. package/src/types/instrument.ts +6 -0
  732. package/src/types/plugin.ts +32 -9
  733. package/src/types/price-history.ts +5 -0
  734. package/src/types/ticker-changes.ts +19 -0
  735. package/src/types/ticker.ts +10 -2
  736. package/src/ui/context-menu.tsx +2 -0
  737. package/src/ui/host.tsx +40 -4
  738. package/src/ui/measured-cells.ts +13 -7
  739. package/src/ui/qr.ts +7 -5
  740. package/src/updater/index.ts +5 -5
  741. package/src/utils/calendar-date.ts +86 -27
  742. package/src/utils/currency-units.ts +17 -0
  743. package/src/utils/debug-log.ts +1 -1
  744. package/src/utils/dividend-cash.ts +48 -0
  745. package/src/utils/dom-screenshot-rasters.ts +3 -1
  746. package/src/utils/dom-screenshot.ts +14 -10
  747. package/src/utils/exchange-rate-snapshot.ts +9 -4
  748. package/src/utils/external-url.ts +1 -0
  749. package/src/utils/fiscal-period.ts +47 -0
  750. package/src/utils/format.ts +5 -2
  751. package/src/utils/forward-dividend.ts +160 -11
  752. package/src/utils/futures-generic.ts +34 -8
  753. package/src/utils/http-proxy-response.ts +2 -1
  754. package/src/utils/local-utc-offset.ts +2 -0
  755. package/src/utils/log-failure.ts +31 -0
  756. package/src/utils/option-expiry.ts +12 -12
  757. package/src/utils/price-history-integrity.ts +56 -0
  758. package/src/utils/price-history.ts +13 -1
  759. package/src/utils/sec-filings.ts +245 -0
  760. package/src/utils/selection-clipboard.ts +5 -2
  761. package/src/utils/throttled-fetch.ts +1 -0
  762. package/src/{plugins/builtin/dividend-yield/trailing-cash.ts → utils/trailing-dividend.ts} +55 -8
  763. package/src/utils/url.ts +1 -0
  764. package/src/utils/utc-time.ts +11 -0
  765. package/src/plugins/builtin/chat/share/targets.ts +0 -27
  766. package/src/renderers/dom/host/top-surface.ts +0 -119
  767. package/src/shares/dialog.tsx +0 -221
  768. package/src/shares/live.ts +0 -19
@@ -1,13 +1,103 @@
1
- import type { CryptoAssetKind } from "../../../api-client/crypto-markets";
1
+ import type { CryptoAssetKind, StablecoinSupplyPayload } from "../../../api-client/crypto-markets";
2
+ import type { HeadlessExportTable, HeadlessPaneColumn } from "../../../types/headless";
2
3
  import type { HeadlessPaneDefinition } from "../../../types/plugin";
3
- import { formatCompact } from "../../../utils/format";
4
- import { fetchCryptoMarkets } from "./client";
5
- import { buildCryptoRows, CRYPTO_BOARD_FRESHNESS, formatCryptoPercent } from "./model";
4
+ import { formatCompact, formatCompactAmount } from "../../../utils/format";
5
+ import { errorMessage } from "../../../utils/errors";
6
+ import { fetchCryptoMarkets, fetchStablecoinSupply } from "./client";
7
+ import {
8
+ buildCryptoRows,
9
+ CRYPTO_BOARD_FRESHNESS,
10
+ formatCryptoPercent,
11
+ formatDayRangeTime,
12
+ formatOffPeg,
13
+ withCryptoSupply,
14
+ } from "./model";
15
+ import { stackedSupply, supplyByCode, supplyFailure, supplyHistoryRows, supplySummaryLine } from "./supply";
6
16
 
7
17
  const NO_QUOTES = new Map();
8
18
  const number = (value: unknown) => (typeof value === "number" ? value : null);
9
19
  const percent = (value: unknown) => formatCryptoPercent(number(value));
10
20
  const compact = (value: unknown) => (number(value) == null ? "—" : formatCompact(number(value)!, { fixedDecimals: true }));
21
+ const text = (key: string) => (_value: unknown, row: Record<string, unknown>) => String(row[key] ?? "—");
22
+ /** Exports keep the date; the text table reads `Sat 11:30`, the weekday enough inside 24 hours. */
23
+ const isoMinute = (value: unknown) => typeof value === "string" ? `${value.slice(0, 16).replace("T", " ")} UTC` : "";
24
+ const dayTime = (value: unknown) => formatDayRangeTime(typeof value === "string" ? Date.parse(value) : null);
25
+ const signedCompact = (value: unknown) => (number(value) == null ? "—" : formatCompactAmount(number(value)!, { signed: true }));
26
+ const share = (value: unknown) => (number(value) == null ? "—" : `${number(value)!.toFixed(2)}%`);
27
+
28
+ const COIN_COLUMNS: HeadlessPaneColumn[] = [
29
+ { key: "rank", header: "#", align: "right" },
30
+ { key: "code", header: "Coin" },
31
+ { key: "name", header: "Name" },
32
+ { key: "price", header: "Price", align: "right", format: (_value, row) => String(row.priceText ?? "—") },
33
+ { key: "changePercent", header: "Chg %", align: "right", format: percent },
34
+ { key: "return7d", header: "7D %", align: "right", format: percent },
35
+ { key: "return30d", header: "30D %", align: "right", format: percent },
36
+ { key: "return1y", header: "1Y %", align: "right", format: percent },
37
+ { key: "volume24h", header: "Vol 24h", align: "right", format: compact },
38
+ { key: "marketCap", header: "Market cap", align: "right", format: compact },
39
+ ];
40
+
41
+ /**
42
+ * A stablecoin's peg, how far off it is and its 24-hour low and high with
43
+ * their times follow its price. Fitted to a narrow terminal, the returns and
44
+ * volume leave the table first.
45
+ */
46
+ const STABLECOIN_COLUMNS: HeadlessPaneColumn[] = [
47
+ ...COIN_COLUMNS.slice(0, 4),
48
+ { key: "peg", header: "Peg", literalHeader: true, format: text("pegText") },
49
+ { key: "offPegBp", header: "Off peg", literalHeader: true, align: "right", format: (value) => formatOffPeg(number(value)) },
50
+ { key: "low24h", header: "24h low", literalHeader: true, align: "right", format: text("low24hText") },
51
+ { key: "low24hAt", header: "Low UTC", literalHeader: true, shrink: false, format: isoMinute, textFormat: dayTime },
52
+ { key: "high24h", header: "24h high", literalHeader: true, align: "right", format: text("high24hText") },
53
+ { key: "high24hAt", header: "High UTC", literalHeader: true, shrink: false, format: isoMinute, textFormat: dayTime },
54
+ ...COIN_COLUMNS.slice(4).map((column, index) => column.key === "marketCap"
55
+ ? column
56
+ : { ...column, stackPriority: 4 - index, stackRows: 0 }),
57
+ ];
58
+
59
+ /**
60
+ * With supply loaded, each coin's supply follows the market cap. In a narrow
61
+ * terminal the supply's dollar changes and its share leave after the returns.
62
+ */
63
+ const STABLECOIN_SUPPLY_COLUMNS: HeadlessPaneColumn[] = [
64
+ ...STABLECOIN_COLUMNS,
65
+ { key: "supply", header: "Supply", align: "right", format: compact },
66
+ { key: "supply7dPercent", header: "Supply 7D %", align: "right", format: percent },
67
+ { key: "supply7d", header: "Supply 7D", align: "right", format: signedCompact, stackPriority: 0.6, stackRows: 0 },
68
+ { key: "supply30dPercent", header: "Supply 30D %", align: "right", format: percent },
69
+ { key: "supply30d", header: "Supply 30D", align: "right", format: signedCompact, stackPriority: 0.8, stackRows: 0 },
70
+ { key: "supplyShare", header: "Share", align: "right", format: share, stackPriority: 0.4, stackRows: 0 },
71
+ ];
72
+
73
+ /** Daily supply in USD, oldest first: the total, the charted coins and the rest, for `--csv`. */
74
+ function supplyHistoryTable(payload: StablecoinSupplyPayload): HeadlessExportTable | null {
75
+ const layers = stackedSupply(payload, "ALL")?.layers;
76
+ const rows = supplyHistoryRows(payload);
77
+ if (!layers || !rows.length) return null;
78
+ return {
79
+ title: "Stablecoin supply",
80
+ unit: "USD",
81
+ interval: "1d",
82
+ columns: [
83
+ { key: "date", header: "Date" },
84
+ { key: "total", header: "Total", align: "right" },
85
+ ...layers.map((layer) => ({ key: layer.id, header: layer.label, literalHeader: true, align: "right" as const })),
86
+ ],
87
+ rows,
88
+ };
89
+ }
90
+
91
+ /** What the JSON report carries of the supply: the total line's figures and when they are from. */
92
+ function supplyMetadata(payload: StablecoinSupplyPayload) {
93
+ return {
94
+ status: payload.status,
95
+ asOf: payload.asOf,
96
+ fetchedAt: payload.fetchedAt,
97
+ total: payload.total,
98
+ charted: payload.history?.coins.map((coin) => coin.symbol) ?? [],
99
+ };
100
+ }
11
101
 
12
102
  export const cryptoBoardHeadless: HeadlessPaneDefinition<"rows"> = {
13
103
  shape: "rows",
@@ -21,20 +111,10 @@ export const cryptoBoardHeadless: HeadlessPaneDefinition<"rows"> = {
21
111
  { value: "stablecoin", aliases: ["stablecoins", "stable", "stables"] },
22
112
  ],
23
113
  defaultValue: "coin",
114
+ example: "--list stablecoin",
24
115
  pluginState: { pluginId: "market-overview", key: "activeTab" },
25
116
  }],
26
- columns: [
27
- { key: "rank", header: "#", align: "right" },
28
- { key: "code", header: "Coin" },
29
- { key: "name", header: "Name" },
30
- { key: "price", header: "Price", align: "right", format: (_value, row) => String(row.priceText ?? "—") },
31
- { key: "changePercent", header: "Chg %", align: "right", format: percent },
32
- { key: "return7d", header: "7D %", align: "right", format: percent },
33
- { key: "return30d", header: "30D %", align: "right", format: percent },
34
- { key: "return1y", header: "1Y %", align: "right", format: percent },
35
- { key: "volume24h", header: "Vol 24h", align: "right", format: compact },
36
- { key: "marketCap", header: "Market cap", align: "right", format: compact },
37
- ],
117
+ columns: COIN_COLUMNS,
38
118
  describe: (args) => `Crypto | ${args.options.list === "stablecoin" ? "Stablecoins" : "Coins"}`,
39
119
  discovery: {
40
120
  aliases: ["CRYP"],
@@ -44,8 +124,15 @@ export const cryptoBoardHeadless: HeadlessPaneDefinition<"rows"> = {
44
124
  freshness: { status: "delayed", ...CRYPTO_BOARD_FRESHNESS },
45
125
  async load(args, ctx) {
46
126
  const kind = args.options.list === "stablecoin" ? "stablecoin" : "coin" satisfies CryptoAssetKind;
47
- const data = await fetchCryptoMarkets(ctx.apiClient);
48
- const rows = buildCryptoRows(data.assets, kind, NO_QUOTES).map((row) => ({
127
+ // Supply is its own request: a failure there leaves the prices whole.
128
+ const [data, supplyResult] = await Promise.all([
129
+ fetchCryptoMarkets(ctx.apiClient),
130
+ kind === "stablecoin"
131
+ ? fetchStablecoinSupply(ctx.apiClient).then((payload) => ({ payload }), (error: unknown) => ({ error }))
132
+ : null,
133
+ ]);
134
+ const supply = supplyResult && "payload" in supplyResult ? supplyResult.payload : null;
135
+ const rows = withCryptoSupply(buildCryptoRows(data.assets, kind, NO_QUOTES), supplyByCode(supply)).map((row) => ({
49
136
  rank: row.rank,
50
137
  symbol: row.asset.symbol,
51
138
  code: row.code,
@@ -61,11 +148,43 @@ export const cryptoBoardHeadless: HeadlessPaneDefinition<"rows"> = {
61
148
  circulatingSupply: row.asset.circulatingSupply,
62
149
  maxSupply: row.asset.maxSupply,
63
150
  quoteTime: row.asset.quoteTime,
151
+ ...(kind === "stablecoin" ? {
152
+ peg: row.peg,
153
+ pegText: row.pegText || "—",
154
+ offPegBp: row.offPegBp,
155
+ low24h: row.low24h,
156
+ low24hText: row.low24hText,
157
+ low24hAt: row.low24hAt == null ? null : new Date(row.low24hAt).toISOString(),
158
+ high24h: row.high24h,
159
+ high24hText: row.high24hText,
160
+ high24hAt: row.high24hAt == null ? null : new Date(row.high24hAt).toISOString(),
161
+ supply: row.supply,
162
+ supply7d: row.supply7d,
163
+ supply7dPercent: row.supply7dPercent,
164
+ supply30d: row.supply30d,
165
+ supply30dPercent: row.supply30dPercent,
166
+ supplyShare: row.supplyShare,
167
+ pegType: row.pegType,
168
+ } : {}),
64
169
  }));
170
+ const metadata = { list: kind, status: data.status, asOf: data.asOf, generatedAt: data.generatedAt };
171
+ if (kind !== "stablecoin") return { rows, errors: data.warnings, metadata };
172
+ const summary = supply ? supplySummaryLine(supply) : null;
173
+ const history = supply ? supplyHistoryTable(supply) : null;
174
+ // A supply outage is a caveat, not a failure: the prices are the report, and they are complete.
175
+ const notes = [
176
+ ...(supply?.warnings ?? []),
177
+ ...(supplyResult && "error" in supplyResult
178
+ ? [`${supplyFailure(errorMessage(supplyResult.error)).replace(/\.$/, "")}; the prices are complete without it.`]
179
+ : []),
180
+ ];
65
181
  return {
66
182
  rows,
183
+ columns: supply?.coins.length ? STABLECOIN_SUPPLY_COLUMNS : STABLECOIN_COLUMNS,
184
+ ...(history ? { exportTables: [history] } : {}),
67
185
  errors: data.warnings,
68
- metadata: { list: kind, status: data.status, asOf: data.asOf, generatedAt: data.generatedAt },
186
+ ...(notes.length ? { notes } : {}),
187
+ metadata: { ...metadata, ...(summary ? { notices: [summary] } : {}), ...(supply ? { supply: supplyMetadata(supply) } : {}) },
69
188
  };
70
189
  },
71
190
  };
@@ -1,6 +1,6 @@
1
1
  import type { PluginModule } from "../plugin-module";
2
2
  import { LIVE_STREAMING_QUICK_SETTING, withLiveStreamingSetting } from "../../../state/hooks/live-streaming";
3
- import { cryptoMarketsCache } from "./client";
3
+ import { cryptoMarketsCache, STABLECOIN_SUPPLY_CACHES } from "./client";
4
4
  import { cryptoBoardHeadless } from "./headless";
5
5
  import { CryptoBoardPane } from "./pane";
6
6
  import { CRYPTO_SERIES } from "./series";
@@ -27,16 +27,18 @@ export const cryptoBoardModule: PluginModule = {
27
27
  id: "crypto-board-pane",
28
28
  paneId: "crypto-board",
29
29
  label: "Crypto",
30
- description: "Top crypto assets by market cap with live prices, 7D, 30D and 1Y returns, volume and market cap.",
31
- keywords: ["crypto", "cryp", "bitcoin", "ethereum", "coins", "stablecoins", "digital", "currency"],
30
+ description: "Top crypto assets by market cap with live prices, 7D, 30D and 1Y returns, volume and market cap, and stablecoin supply.",
31
+ keywords: ["crypto", "cryp", "bitcoin", "ethereum", "coins", "stablecoins", "stablecoin supply", "digital", "currency"],
32
32
  shortcut: { prefix: "CRYP" },
33
33
  headless: cryptoBoardHeadless,
34
34
  },
35
35
  ],
36
36
  setup(ctx) {
37
37
  cryptoMarketsCache.attach(ctx.persistence);
38
+ for (const cache of STABLECOIN_SUPPLY_CACHES) cache.attach(ctx.persistence);
38
39
  },
39
40
  dispose() {
40
41
  cryptoMarketsCache.reset();
42
+ for (const cache of STABLECOIN_SUPPLY_CACHES) cache.reset();
41
43
  },
42
44
  };
@@ -1,4 +1,4 @@
1
- import type { CryptoAssetKind, CryptoMarketAsset } from "../../../api-client/crypto-markets";
1
+ import type { CryptoAssetKind, CryptoMarketAsset, CryptoPeg } from "../../../api-client/crypto-markets";
2
2
  import type { DataTableColumn } from "../../../components";
3
3
  import { getTableWidth } from "../../../components/ui/table-layout";
4
4
  import { buildQuoteKey, resolveEntryData } from "../../../market-data/selectors";
@@ -7,6 +7,8 @@ import type { PricePoint, Quote } from "../../../types/financials";
7
7
  import type { HeadlessPaneFreshness } from "../../../types/headless";
8
8
  import { compareSortValues, nextHeaderSort, type SortDirection } from "../../../utils/sort-values";
9
9
  import { isFiniteNumber } from "../../../utils/guards";
10
+ import { formatBasisPoints, toBasisPoints } from "../../../utils/basis-points";
11
+ import { formatUtcWeekdayTime } from "../../../utils/utc-time";
10
12
 
11
13
  const DAY_MS = 86_400_000;
12
14
 
@@ -21,7 +23,32 @@ export const CRYPTO_TABS: Array<{ value: CryptoAssetKind; label: string }> = [
21
23
  { value: "stablecoin", label: "Stablecoins" },
22
24
  ];
23
25
 
24
- export interface CryptoRow {
26
+ /** A stablecoin's supply as its row shows it; every field is null on the coin tab and for a coin the supply list lacks. */
27
+ export interface CryptoRowSupply {
28
+ /** USD. */
29
+ supply: number | null;
30
+ /** The change in USD over 7 and 30 days on the coin's daily supply, and in percent. */
31
+ supply7d: number | null;
32
+ supply7dPercent: number | null;
33
+ supply30d: number | null;
34
+ supply30dPercent: number | null;
35
+ /** Percent of all stablecoin supply. */
36
+ supplyShare: number | null;
37
+ /** The coin's peg currency as the supply source names it (peggedUSD, peggedEUR). */
38
+ pegType: string | null;
39
+ }
40
+
41
+ const NO_SUPPLY: CryptoRowSupply = {
42
+ supply: null,
43
+ supply7d: null,
44
+ supply7dPercent: null,
45
+ supply30d: null,
46
+ supply30dPercent: null,
47
+ supplyShare: null,
48
+ pegType: null,
49
+ };
50
+
51
+ export interface CryptoRow extends CryptoRowSupply {
25
52
  asset: CryptoMarketAsset;
26
53
  id: string;
27
54
  rank: number;
@@ -41,6 +68,20 @@ export interface CryptoRow {
41
68
  /** Epoch ms of the price shown. */
42
69
  updatedAt: number | null;
43
70
  live: boolean;
71
+ /** A stablecoin's peg; null for a coin, or a server that does not send it. */
72
+ peg: CryptoPeg | null;
73
+ /** `USD`, `EUR 1.1242` (the target in dollars), `USD yield`; empty without a peg. */
74
+ pegText: string;
75
+ /** How far the price is off its target, in basis points; null when it accrues yield or the target is unknown. */
76
+ offPegBp: number | null;
77
+ /** The 24-hour low and high, the price shown included, and when each traded (epoch ms). */
78
+ low24h: number | null;
79
+ low24hAt: number | null;
80
+ high24h: number | null;
81
+ high24hAt: number | null;
82
+ /** The low and high at the price's decimals. */
83
+ low24hText: string;
84
+ high24hText: string;
44
85
  }
45
86
 
46
87
  export const cryptoQuoteKey = (asset: Pick<CryptoMarketAsset, "symbol">) =>
@@ -116,6 +157,12 @@ function buildCryptoRow(
116
157
  // The digits follow a price that holds still for the day, so a tick across a
117
158
  // power of ten (0.9998 to 1.0002) keeps the row's decimals.
118
159
  const referencePrice = [asset.previousClose, quote?.previousClose, quote?.open].find(positive) ?? asset.price;
160
+ const peg = asset.kind === "stablecoin" ? asset.peg ?? null : null;
161
+ const updated = isFiniteNumber(updatedAt) ? updatedAt : null;
162
+ const minimumDecimals = asset.kind === "stablecoin" ? 4 : 2;
163
+ const range = asset.kind === "stablecoin"
164
+ ? dayRange(asset, price, updated)
165
+ : { low24h: null, low24hAt: null, high24h: null, high24hAt: null };
119
166
  return {
120
167
  asset,
121
168
  id: asset.symbol,
@@ -123,7 +170,7 @@ function buildCryptoRow(
123
170
  code: asset.code,
124
171
  name: asset.name,
125
172
  price,
126
- priceText: formatCryptoPrice(price, asset.kind === "stablecoin" ? 4 : 2, referencePrice),
173
+ priceText: formatCryptoPrice(price, minimumDecimals, referencePrice),
127
174
  changePercent,
128
175
  return7d: percentChange(price, closeDaysAgo(asset, 7, now)),
129
176
  return30d: percentChange(price, closeDaysAgo(asset, 30, now)),
@@ -131,11 +178,79 @@ function buildCryptoRow(
131
178
  volume24h: asset.volume24h,
132
179
  marketCap,
133
180
  history,
134
- updatedAt: isFiniteNumber(updatedAt) ? updatedAt : null,
181
+ updatedAt: updated,
135
182
  live: !!quote && quote.delivery === "stream" && quote.stale !== true,
183
+ peg,
184
+ pegText: formatPegTarget(peg),
185
+ offPegBp: offPegBasisPoints(price, peg),
186
+ ...range,
187
+ low24hText: formatCryptoPrice(range.low24h, minimumDecimals, referencePrice),
188
+ high24hText: formatCryptoPrice(range.high24h, minimumDecimals, referencePrice),
189
+ ...NO_SUPPLY,
136
190
  };
137
191
  }
138
192
 
193
+ /**
194
+ * What a stablecoin's price is measured against: `USD`; a coin on another
195
+ * currency by its target in dollars, `EUR 1.1242`, so EURC at 1.1234 does not
196
+ * read as a 12% premium; a coin whose yield accrues into its price (USDY) is
197
+ * marked so and has no par to be off.
198
+ */
199
+ export function formatPegTarget(peg: CryptoPeg | null | undefined): string {
200
+ if (!peg) return "";
201
+ if (peg.accrues) return `${peg.currency} yield`;
202
+ if (peg.currency === "USD") return "USD";
203
+ return peg.price == null ? peg.currency : `${peg.currency} ${peg.price.toFixed(4)}`;
204
+ }
205
+
206
+ /** Basis points off the peg, to a tenth: (price / target - 1) x 10,000. Null when the coin accrues yield or the target is unknown. */
207
+ export function offPegBasisPoints(price: number, peg: CryptoPeg | null | undefined): number | null {
208
+ if (!peg || peg.accrues || peg.price == null || !(peg.price > 0) || !(price > 0)) return null;
209
+ return toBasisPoints((price / peg.price - 1) * 100, 1);
210
+ }
211
+
212
+ /** `-12.9bp`; a coin on its peg reads `0.0bp`; blank when there is no par to be off. */
213
+ export function formatOffPeg(bp: number | null): string {
214
+ return bp == null ? "" : formatBasisPoints(bp / 100, 1);
215
+ }
216
+
217
+ /** Off by this many basis points either way a stablecoin is worth a look; by a percent it has lost its peg. */
218
+ const OFF_PEG_WATCH_BP = 25;
219
+ const OFF_PEG_BROKEN_BP = 100;
220
+
221
+ export function offPegSeverity(bp: number | null): "none" | "watch" | "broken" {
222
+ if (bp == null) return "none";
223
+ const size = Math.abs(bp);
224
+ return size >= OFF_PEG_BROKEN_BP ? "broken" : size >= OFF_PEG_WATCH_BP ? "watch" : "none";
225
+ }
226
+
227
+ /**
228
+ * The 24-hour low and high the board reported, widened by a newer price than
229
+ * it had seen, so a live tick below the low is the low, at its time.
230
+ */
231
+ function dayRange(asset: CryptoMarketAsset, price: number, updatedAt: number | null) {
232
+ const range = asset.range24h;
233
+ if (!range) return { low24h: null, low24hAt: null, high24h: null, high24hAt: null };
234
+ let low24h = range.low;
235
+ let low24hAt: number | null = Date.parse(range.lowAt);
236
+ let high24h = range.high;
237
+ let high24hAt: number | null = Date.parse(range.highAt);
238
+ const newer = updatedAt != null && updatedAt > Date.parse(range.end);
239
+ if (newer && price < low24h) [low24h, low24hAt] = [price, updatedAt];
240
+ if (newer && price > high24h) [high24h, high24hAt] = [price, updatedAt];
241
+ return {
242
+ low24h,
243
+ low24hAt: isFiniteNumber(low24hAt) ? low24hAt : null,
244
+ high24h,
245
+ high24hAt: isFiniteNumber(high24hAt) ? high24hAt : null,
246
+ };
247
+ }
248
+
249
+ /** `Sat 11:30` UTC; a dash when unknown. */
250
+ export function formatDayRangeTime(ms: number | null): string {
251
+ return ms == null ? "—" : formatUtcWeekdayTime(ms) || "—";
252
+ }
253
+
139
254
  const rowsByAsset = new WeakMap<CryptoMarketAsset, { quote: Quote | null; day: number; row: CryptoRow }>();
140
255
 
141
256
  export function buildCryptoRows(
@@ -159,6 +274,25 @@ export function buildCryptoRows(
159
274
  });
160
275
  }
161
276
 
277
+ const suppliedRows = new WeakMap<CryptoRow, { supply: CryptoRowSupply; row: CryptoRow }>();
278
+
279
+ /**
280
+ * Each row with its coin's supply, matched by code. A row keeps its identity
281
+ * while its price and its supply hold, so the table still skips it.
282
+ */
283
+ export function withCryptoSupply(rows: readonly CryptoRow[], supply: ReadonlyMap<string, CryptoRowSupply>): CryptoRow[] {
284
+ if (supply.size === 0) return rows as CryptoRow[];
285
+ return rows.map((row) => {
286
+ const own = supply.get(row.code.toUpperCase());
287
+ if (!own) return row;
288
+ const cached = suppliedRows.get(row);
289
+ if (cached && cached.supply === own) return cached.row;
290
+ const supplied = { ...row, ...own };
291
+ suppliedRows.set(row, { supply: own, row: supplied });
292
+ return supplied;
293
+ });
294
+ }
295
+
162
296
  /** What a capture and the footer read of each row: when its price is from, and whether it is streaming live. */
163
297
  export function cryptoRowObservations(rows: readonly CryptoRow[]): Array<{ quoteTime: string; dataSource: "live" | "delayed" }> {
164
298
  return rows.flatMap((row) => row.updatedAt == null ? [] : [{ quoteTime: new Date(row.updatedAt).toISOString(), dataSource: row.live ? "live" as const : "delayed" as const }]);
@@ -200,7 +334,19 @@ type CryptoColumnId =
200
334
  | "return1y"
201
335
  | "trend"
202
336
  | "volume24h"
203
- | "marketCap";
337
+ | "marketCap"
338
+ | "pegText"
339
+ | "offPegBp"
340
+ | "low24h"
341
+ | "low24hAt"
342
+ | "high24h"
343
+ | "high24hAt"
344
+ | "supply"
345
+ | "supply7dPercent"
346
+ | "supply7d"
347
+ | "supply30dPercent"
348
+ | "supply30d"
349
+ | "supplyShare";
204
350
 
205
351
  export type CryptoColumn = DataTableColumn & { id: CryptoColumnId };
206
352
 
@@ -211,7 +357,7 @@ export interface CryptoSortPreference {
211
357
 
212
358
  export const DEFAULT_CRYPTO_SORT: CryptoSortPreference = { columnId: null, direction: "asc" };
213
359
 
214
- const TEXT_COLUMNS = new Set<CryptoColumnId>(["rank", "code", "name"]);
360
+ const TEXT_COLUMNS = new Set<CryptoColumnId>(["rank", "code", "name", "pegText"]);
215
361
 
216
362
  /** Numbers open largest-first (top gainers, biggest caps); names open A-Z. */
217
363
  export function nextCryptoSort(current: CryptoSortPreference, columnId: string): CryptoSortPreference {
@@ -223,15 +369,22 @@ export function nextCryptoSort(current: CryptoSortPreference, columnId: string):
223
369
  });
224
370
  }
225
371
 
372
+ /** Off peg sorts by size, so the first click puts the furthest from its peg on top, above or below it. */
373
+ function sortValue(row: CryptoRow, columnId: Exclude<CryptoColumnId, "trend">) {
374
+ return columnId === "offPegBp" ? row.offPegBp == null ? null : Math.abs(row.offPegBp) : row[columnId];
375
+ }
376
+
226
377
  export function sortCryptoRows(rows: CryptoRow[], sort: CryptoSortPreference): CryptoRow[] {
227
378
  const columnId = sort.columnId;
228
379
  if (!columnId || columnId === "trend") return rows;
229
- return [...rows].sort((left, right) => compareSortValues(left[columnId], right[columnId], sort.direction));
380
+ return [...rows].sort((left, right) => compareSortValues(sortValue(left, columnId), sortValue(right, columnId), sort.direction));
230
381
  }
231
382
 
232
383
  // Columns ---------------------------------------------------------------------
233
384
 
234
- const COLUMN_SPECS: Array<CryptoColumn & { optional?: number }> = [
385
+ type CryptoColumnSpec = CryptoColumn & { optional?: number };
386
+
387
+ const COLUMN_SPECS: CryptoColumnSpec[] = [
235
388
  { id: "rank", label: "#", width: 3, align: "right" },
236
389
  { id: "code", label: "COIN", width: 7, align: "left" },
237
390
  { id: "name", label: "NAME", width: 10, align: "left", optional: 1 },
@@ -245,19 +398,64 @@ const COLUMN_SPECS: Array<CryptoColumn & { optional?: number }> = [
245
398
  { id: "marketCap", label: "MCAP", width: 8, align: "right" },
246
399
  ];
247
400
 
401
+ /**
402
+ * A stablecoin is read by its peg: what it holds to, how far off it is, and
403
+ * the 24-hour low and high with their times, before its returns. Narrowing,
404
+ * the returns, the sparkline and the day change leave first, then volume, the
405
+ * name and the high; the peg, the low and when it traded stay.
406
+ */
407
+ const STABLECOIN_COLUMN_SPECS: CryptoColumnSpec[] = [
408
+ { id: "rank", label: "#", width: 3, align: "right" },
409
+ { id: "code", label: "COIN", width: 7, align: "left" },
410
+ { id: "name", label: "NAME", width: 10, align: "left", optional: 3 },
411
+ { id: "price", label: "PRICE", width: 8, align: "right" },
412
+ { id: "pegText", label: "PEG", width: 10, align: "left" },
413
+ { id: "offPegBp", label: "OFF PEG", width: 8, align: "right" },
414
+ { id: "low24h", label: "24H LOW", width: 8, align: "right" },
415
+ { id: "low24hAt", label: "LOW UTC", width: 9, align: "left" },
416
+ { id: "high24h", label: "24H HIGH", width: 8, align: "right", optional: 1 },
417
+ { id: "high24hAt", label: "HIGH UTC", width: 9, align: "left", optional: 2 },
418
+ { id: "changePercent", label: "CHG%", width: 8, align: "right", optional: 5 },
419
+ { id: "return7d", label: "7D%", width: 8, align: "right", optional: 6 },
420
+ { id: "return30d", label: "30D%", width: 8, align: "right", optional: 8 },
421
+ { id: "return1y", label: "1Y%", width: 10, align: "right", optional: 9 },
422
+ { id: "trend", label: "30D", width: 12, align: "left", optional: 7 },
423
+ { id: "volume24h", label: "VOL 24H", width: 8, align: "right", optional: 4 },
424
+ { id: "marketCap", label: "MCAP", width: 8, align: "right" },
425
+ ];
426
+
427
+ /**
428
+ * With supply loaded, each coin's supply follows the market cap. Every supply
429
+ * column leaves before the name, so the peg columns narrow as they do without
430
+ * supply: the dollar changes go with the price returns, then (after volume)
431
+ * share, the 30D and 7D change and supply itself.
432
+ */
433
+ const STABLECOIN_SUPPLY_COLUMN_SPECS: CryptoColumnSpec[] = [
434
+ ...STABLECOIN_COLUMN_SPECS,
435
+ { id: "supply", label: "SUPPLY", width: 8, align: "right", optional: 3.2 },
436
+ { id: "supply7dPercent", label: "SUP 7D%", width: 8, align: "right", optional: 3.4 },
437
+ { id: "supply7d", label: "SUP 7D", width: 8, align: "right", optional: 4.6 },
438
+ { id: "supply30dPercent", label: "SUP 30D%", width: 8, align: "right", optional: 3.6 },
439
+ { id: "supply30d", label: "SUP 30D", width: 8, align: "right", optional: 4.8 },
440
+ { id: "supplyShare", label: "SHARE", width: 7, align: "right", optional: 3.8 },
441
+ ];
442
+
248
443
  const MAX_NAME_WIDTH = 24;
249
444
 
250
445
  /**
251
446
  * Optional columns leave in a fixed order as the pane narrows (1Y, then the
252
447
  * sparkline, 30D, volume and the name); the name takes what width is left.
448
+ * The stablecoin tab adds the supply columns only `withSupply`, so a supply
449
+ * outage leaves the price columns as they are.
253
450
  */
254
- export function buildCryptoColumns(width: number): CryptoColumn[] {
255
- const optional = COLUMN_SPECS.flatMap((column) => column.optional ?? []).sort((a, b) => b - a);
451
+ export function buildCryptoColumns(width: number, kind: CryptoAssetKind = "coin", withSupply = false): CryptoColumn[] {
452
+ const specs = kind !== "stablecoin" ? COLUMN_SPECS : withSupply ? STABLECOIN_SUPPLY_COLUMN_SPECS : STABLECOIN_COLUMN_SPECS;
453
+ const optional = specs.flatMap((column) => column.optional ?? []).sort((a, b) => b - a);
256
454
  let dropped = new Set<number>();
257
455
  // Measured the way the table draws them (header floors, gaps, padding and the
258
456
  // extra gutter after a right-aligned column), so the last column never clips.
259
- const fits = (columns: typeof COLUMN_SPECS) => getTableWidth(columns) <= width;
260
- const visible = () => COLUMN_SPECS.filter((column) => !column.optional || !dropped.has(column.optional));
457
+ const fits = (columns: typeof specs) => getTableWidth(columns) <= width;
458
+ const visible = () => specs.filter((column) => !column.optional || !dropped.has(column.optional));
261
459
  for (const priority of optional) {
262
460
  if (fits(visible())) break;
263
461
  dropped = new Set([...dropped, priority]);