gloomberb 0.18.2 → 0.18.4

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 (954) hide show
  1. package/README.md +8 -8
  2. package/README.zh-CN.md +11 -11
  3. package/bin/{gloomberb → gloom} +3 -1
  4. package/package.json +6 -2
  5. package/patches/electrobun@1.18.1.patch +106 -5
  6. package/src/account/checkout-handoff.tsx +225 -0
  7. package/src/account/device-signin-dialog.tsx +2 -2
  8. package/src/account/device-signin.ts +2 -2
  9. package/src/account/upgrade-dialog.tsx +1 -1
  10. package/src/api-client/askg.ts +2 -2
  11. package/src/api-client/auth.ts +2 -2
  12. package/src/api-client/central-bank-rates.ts +6 -0
  13. package/src/api-client/collections.ts +5 -1
  14. package/src/api-client/cot.ts +2 -0
  15. package/src/api-client/crypto-etf-flows.ts +37 -0
  16. package/src/api-client/crypto-markets.ts +60 -0
  17. package/src/api-client/data.ts +99 -5
  18. package/src/api-client/doe.ts +15 -0
  19. package/src/api-client/equity-screener.ts +28 -0
  20. package/src/api-client/exchange-announcements.ts +38 -0
  21. package/src/api-client/fund-facts.ts +39 -0
  22. package/src/api-client/futures-curve.ts +9 -1
  23. package/src/api-client/hiring.ts +301 -0
  24. package/src/api-client/index.ts +24 -5
  25. package/src/api-client/market-discovery.ts +14 -0
  26. package/src/api-client/members.ts +6 -0
  27. package/src/api-client/mna.ts +6 -0
  28. package/src/api-client/paths.ts +18 -1
  29. package/src/api-client/request.ts +26 -7
  30. package/src/api-client/short-side-publications.ts +37 -0
  31. package/src/api-client/socket.ts +64 -4
  32. package/src/api-client/swap-curve.ts +35 -0
  33. package/src/api-client/telemetry.ts +17 -0
  34. package/src/api-client/themes.ts +7 -1
  35. package/src/api-client/types.ts +90 -2
  36. package/src/app/global-shortcuts.ts +1 -16
  37. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  38. package/src/app/runtime/desktop-deeplink.ts +5 -5
  39. package/src/app/runtime/plugin-bindings.ts +2 -2
  40. package/src/app/runtime/startup.ts +13 -0
  41. package/src/app/runtime/update.ts +28 -8
  42. package/src/app/star-prompt/status-line.tsx +2 -2
  43. package/src/architecture/core-builtin-allowlist.json +5 -2
  44. package/src/architecture/pane-tabs-allowlist.json +65 -0
  45. package/src/architecture/ui-literals-allowlist.json +3 -3
  46. package/src/architecture/ui-tokens-allowlist.json +1 -1
  47. package/src/brokers/broker-portfolio-sync.ts +37 -0
  48. package/src/brokers/directory.ts +11 -0
  49. package/src/brokers/logos.ts +3 -1
  50. package/src/brokers/signed-in/adapter.ts +304 -26
  51. package/src/brokers/signed-in/client.ts +14 -2
  52. package/src/brokers/signed-in/connect.ts +10 -2
  53. package/src/brokers/sync-broker-instance.ts +9 -1
  54. package/src/capabilities/chart-series.ts +22 -6
  55. package/src/cli/bank-section.ts +1 -1
  56. package/src/cli/base-converter.ts +23 -14
  57. package/src/cli/central-bank-events.ts +1 -1
  58. package/src/cli/command-options.ts +59 -4
  59. package/src/cli/commands/api.ts +9 -6
  60. package/src/cli/commands/broker.ts +2 -2
  61. package/src/cli/commands/command-utils.ts +2 -2
  62. package/src/cli/commands/crypto-hints.ts +7 -20
  63. package/src/cli/commands/fred-series.ts +4 -2
  64. package/src/cli/commands/keybindings.ts +2 -2
  65. package/src/cli/commands/market.ts +1189 -296
  66. package/src/cli/commands/news-hints.ts +2 -2
  67. package/src/cli/commands/news-search.ts +59 -6
  68. package/src/cli/commands/options-leaps.ts +41 -26
  69. package/src/cli/commands/overview.ts +121 -48
  70. package/src/cli/commands/plugins.ts +9 -9
  71. package/src/cli/commands/remote.ts +13 -10
  72. package/src/cli/commands/search.ts +64 -44
  73. package/src/cli/commands/system.ts +27 -19
  74. package/src/cli/commands/themes.ts +1 -1
  75. package/src/cli/commands/ticker.ts +272 -70
  76. package/src/cli/context.ts +3 -2
  77. package/src/cli/desktop-pane-shot-view.ts +2 -2
  78. package/src/cli/desktop-pane-shot.ts +1 -1
  79. package/src/cli/earnings-calendar.ts +4 -4
  80. package/src/cli/entry.ts +18 -1
  81. package/src/cli/errors.ts +20 -2
  82. package/src/cli/exit-codes.ts +1 -1
  83. package/src/cli/export-source.ts +10 -4
  84. package/src/cli/freshness.ts +2 -8
  85. package/src/cli/fx-forward.ts +130 -0
  86. package/src/cli/fx-pair.ts +10 -9
  87. package/src/cli/glossary.ts +64 -20
  88. package/src/cli/help.ts +20 -14
  89. package/src/cli/history-export.ts +126 -0
  90. package/src/cli/history-rows.ts +196 -43
  91. package/src/cli/index-roots.ts +5 -45
  92. package/src/cli/index.ts +32 -18
  93. package/src/cli/issuer-membership.ts +103 -0
  94. package/src/cli/known-gaps.ts +118 -25
  95. package/src/cli/listing-arg.ts +315 -43
  96. package/src/cli/name-entries.ts +73 -22
  97. package/src/cli/native-smoke.ts +2 -0
  98. package/src/cli/not-a-ticker.ts +19 -9
  99. package/src/cli/opentui-libc.ts +5 -0
  100. package/src/cli/options.ts +12 -0
  101. package/src/cli/pane-functions/capabilities.ts +77 -20
  102. package/src/cli/pane-functions/catalog.ts +40 -23
  103. package/src/cli/pane-functions/discovery.ts +1 -1
  104. package/src/cli/pane-functions/dom.ts +12 -8
  105. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  106. package/src/cli/pane-functions/freshness.ts +174 -23
  107. package/src/cli/pane-functions/function-help.ts +66 -27
  108. package/src/cli/pane-functions/headless.ts +48 -16
  109. package/src/cli/pane-functions/index.ts +82 -21
  110. package/src/cli/pane-functions/options.ts +22 -2
  111. package/src/cli/pane-functions/report-notices.ts +12 -4
  112. package/src/cli/pane-functions/report.ts +5 -5
  113. package/src/cli/pane-functions/resolver.ts +65 -14
  114. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  115. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  116. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  117. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  118. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  119. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  120. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  121. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  122. package/src/cli/pane-functions/shot-options.ts +8 -3
  123. package/src/cli/peso-dollar-rates.ts +79 -0
  124. package/src/cli/quote-answer.ts +38 -3
  125. package/src/cli/report-tables.ts +190 -27
  126. package/src/cli/restore-plugins.ts +29 -14
  127. package/src/cli/result.ts +122 -25
  128. package/src/cli/series-search.ts +5 -5
  129. package/src/cli/statement-filings.ts +19 -0
  130. package/src/cli/suffixed-listing-rows.ts +49 -0
  131. package/src/cli/symbol-args.ts +50 -15
  132. package/src/cli/symbol-cells.ts +120 -0
  133. package/src/cli/symbol-list.ts +226 -60
  134. package/src/cli/symbol-rows.ts +131 -25
  135. package/src/cli/ticker-resolution.ts +108 -11
  136. package/src/cli/treasury-yields.ts +5 -5
  137. package/src/cli/types.ts +2 -0
  138. package/src/cli/uncovered-instrument.ts +3 -3
  139. package/src/cli/unknown-command.ts +6 -6
  140. package/src/components/chart/composite/chart-layout.ts +131 -0
  141. package/src/components/chart/composite/composite-chart.tsx +387 -21
  142. package/src/components/chart/composite/drawing-store.ts +26 -6
  143. package/src/components/chart/composite/interactions.ts +9 -3
  144. package/src/components/chart/composite/legend.tsx +75 -3
  145. package/src/components/chart/composite/levels.ts +5 -0
  146. package/src/components/chart/composite/panel-gestures.ts +41 -0
  147. package/src/components/chart/composite/panel-labels.ts +2 -1
  148. package/src/components/chart/composite/panel-surface.tsx +316 -11
  149. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  150. package/src/components/chart/composite/rasterizer.ts +44 -19
  151. package/src/components/chart/composite/scene.ts +16 -4
  152. package/src/components/chart/composite/text-renderer.ts +48 -21
  153. package/src/components/chart/composite/toolbar.tsx +52 -5
  154. package/src/components/chart/composite/types.ts +19 -0
  155. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  156. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  157. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  158. package/src/components/chart/composite/value-scale.ts +149 -0
  159. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  160. package/src/components/chart/core/pointer.ts +2 -0
  161. package/src/components/chart-table/index.ts +2 -0
  162. package/src/components/chart-table/read.tsx +74 -0
  163. package/src/components/command-bar/commands/collection-list.ts +41 -19
  164. package/src/components/command-bar/commands/collection.ts +9 -3
  165. package/src/components/command-bar/commands/direct/index.ts +1 -1
  166. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  167. package/src/components/command-bar/pane-templates/items.ts +59 -6
  168. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  169. package/src/components/command-bar/routes/root/command-items.ts +32 -6
  170. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  171. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  172. package/src/components/command-bar/routes/root/results.ts +18 -4
  173. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  174. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  175. package/src/components/command-bar/routes/root/selection.ts +6 -1
  176. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  177. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  178. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  179. package/src/components/command-bar/selection-runtime.ts +50 -10
  180. package/src/components/command-bar/surface/index.tsx +24 -2
  181. package/src/components/command-bar/view-model.ts +7 -0
  182. package/src/components/command-bar/workflow/broker.ts +2 -2
  183. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  184. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  185. package/src/components/command-bar/workflow/fields.ts +8 -2
  186. package/src/components/command-bar/workflow/ops.ts +18 -4
  187. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  188. package/src/components/command-bar/workflow/submit.ts +3 -2
  189. package/src/components/command-bar/workflow/tickers.ts +5 -1
  190. package/src/components/company-logo.tsx +39 -0
  191. package/src/components/data-table/sections.ts +4 -2
  192. package/src/components/data-table/view.tsx +23 -3
  193. package/src/components/form-modal/confirm.tsx +2 -1
  194. package/src/components/form-modal/request.ts +5 -0
  195. package/src/components/form-modal/routes.ts +4 -1
  196. package/src/components/index.ts +13 -6
  197. package/src/components/layout/detached-pane-shell.tsx +1 -0
  198. package/src/components/layout/floating-pane.tsx +1 -0
  199. package/src/components/layout/header.tsx +234 -60
  200. package/src/components/layout/market-summary.ts +9 -6
  201. package/src/components/layout/pane/capture-status.ts +1 -1
  202. package/src/components/layout/pane/footer/model.ts +11 -5
  203. package/src/components/layout/pane/header.tsx +10 -3
  204. package/src/components/layout/pane/index.tsx +1 -0
  205. package/src/components/layout/pane/pane-tabs.tsx +9 -1
  206. package/src/components/layout/shell/chrome.ts +19 -6
  207. package/src/components/layout/shell/index.tsx +2 -1
  208. package/src/components/layout/shell/pane/layers.tsx +19 -1
  209. package/src/components/layout/status-bar.tsx +2 -2
  210. package/src/components/market-board/index.tsx +11 -3
  211. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  212. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  213. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  214. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  215. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  216. package/src/components/pane-settings-dialog/value.ts +8 -1
  217. package/src/components/pane-settings-dialog.tsx +44 -13
  218. package/src/components/sign-in-code-panel.tsx +27 -19
  219. package/src/components/ui/column-grid.tsx +48 -4
  220. package/src/components/ui/country-flag-art.tsx +180 -0
  221. package/src/components/ui/country-flag.tsx +118 -12
  222. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  223. package/src/components/ui/data-table/opentui/model.ts +109 -2
  224. package/src/components/ui/data-table/types.ts +1 -1
  225. package/src/components/ui/display.tsx +5 -1
  226. package/src/components/ui/index.ts +3 -1
  227. package/src/components/ui/logo-mark.tsx +11 -2
  228. package/src/components/ui/query-bar.tsx +1 -1
  229. package/src/components/ui/row-notes.ts +79 -0
  230. package/src/components/ui/stat-grid.tsx +28 -3
  231. package/src/components/ui/status.tsx +3 -1
  232. package/src/core/app-services.ts +4 -0
  233. package/src/core/state/app/layout.ts +4 -4
  234. package/src/data/cached-query.ts +8 -1
  235. package/src/data/config/chart-settings.ts +3 -3
  236. package/src/data/config/home.ts +10 -2
  237. package/src/data/config/layout.ts +4 -1
  238. package/src/data/config/store/node.ts +4 -0
  239. package/src/data/config/store/normalize.ts +13 -3
  240. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  241. package/src/data/json-storage.ts +2 -0
  242. package/src/data/saved-copies.ts +84 -0
  243. package/src/docs/snapshot.json +65 -32
  244. package/src/i18n/es.ts +51 -12
  245. package/src/i18n/ja.ts +51 -12
  246. package/src/i18n/ko.ts +51 -12
  247. package/src/i18n/zh-cn.ts +51 -12
  248. package/src/i18n/zh-tw.ts +51 -12
  249. package/src/layout/desks.ts +1 -1
  250. package/src/layout/launch-layout.ts +1 -1
  251. package/src/layout/pane-manager/floating-actions.ts +5 -3
  252. package/src/layout/pane-manager/floating.ts +43 -1
  253. package/src/layout/pane-manager/index.ts +2 -0
  254. package/src/layout/selected-ticker.ts +74 -0
  255. package/src/market-data/depositary-pair.ts +251 -0
  256. package/src/market-data/futures-board.ts +1 -1
  257. package/src/market-data/futures-contract.ts +124 -40
  258. package/src/market-data/fx-closes.ts +17 -2
  259. package/src/market-data/hooks.tsx +19 -11
  260. package/src/market-data/market/change-basis.ts +49 -7
  261. package/src/market-data/market/freshness.ts +34 -5
  262. package/src/market-data/market/period-return.ts +163 -0
  263. package/src/market-data/market/trading-sessions.ts +13 -0
  264. package/src/market-data/market/year-to-date.ts +8 -13
  265. package/src/market-data/performance.ts +20 -14
  266. package/src/market-data/perpetual-premium.ts +32 -0
  267. package/src/market-data/published-apac-sessions.ts +2 -1
  268. package/src/market-data/published-futures-sessions.ts +69 -0
  269. package/src/market-data/quotes/contributions.ts +5 -1
  270. package/src/market-data/quotes/freshness.ts +42 -0
  271. package/src/market-data/quotes/intraday-bar.ts +37 -0
  272. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  273. package/src/market-data/quotes/resolution.ts +4 -2
  274. package/src/market-data/tax-equivalent.ts +1 -1
  275. package/src/market-data/ticker-price-returns.ts +3 -1
  276. package/src/news/aggregator.ts +5 -1
  277. package/src/news/news-model.ts +4 -0
  278. package/src/news/types.ts +11 -0
  279. package/src/notifications/app-notifier.ts +1 -1
  280. package/src/plugins/auto-update.ts +2 -2
  281. package/src/plugins/builtin/account-management/index.tsx +1 -0
  282. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  283. package/src/plugins/builtin/alerts/alert-engine.ts +30 -18
  284. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  285. package/src/plugins/builtin/alerts/cli.ts +485 -76
  286. package/src/plugins/builtin/alerts/command.ts +5 -10
  287. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  288. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  289. package/src/plugins/builtin/alerts/events.ts +108 -5
  290. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  291. package/src/plugins/builtin/alerts/format.ts +2 -1
  292. package/src/plugins/builtin/alerts/headless.ts +100 -39
  293. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  294. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  295. package/src/plugins/builtin/alerts/index.tsx +45 -18
  296. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  297. package/src/plugins/builtin/alerts/levels.ts +3 -3
  298. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  299. package/src/plugins/builtin/alerts/price.ts +35 -0
  300. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  301. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  302. package/src/plugins/builtin/alerts/research-rules.ts +77 -3
  303. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  304. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  305. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  306. package/src/plugins/builtin/alerts/storage.ts +13 -10
  307. package/src/plugins/builtin/alerts/targets.ts +12 -3
  308. package/src/plugins/builtin/analytics/index.tsx +8 -2
  309. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  310. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  311. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  312. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  313. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  314. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  315. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  316. package/src/plugins/builtin/analytics/risk-pane.tsx +107 -43
  317. package/src/plugins/builtin/attention/pane.tsx +22 -16
  318. package/src/plugins/builtin/awards/headless.ts +7 -3
  319. package/src/plugins/builtin/awards/model.ts +18 -0
  320. package/src/plugins/builtin/awards/pane.tsx +12 -9
  321. package/src/plugins/builtin/backtest/export.ts +1 -1
  322. package/src/plugins/builtin/backtest/headless.ts +3 -3
  323. package/src/plugins/builtin/backtest/rules.ts +41 -5
  324. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  325. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  326. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  327. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  328. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  329. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  330. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  331. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  332. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  333. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  334. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  335. package/src/plugins/builtin/broker-trading/dock.tsx +24 -4
  336. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  337. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  338. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  339. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  340. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  341. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  342. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  343. package/src/plugins/builtin/broker-trading/pane.tsx +115 -26
  344. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  345. package/src/plugins/builtin/broker-trading/ticket.tsx +122 -36
  346. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  347. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  348. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  349. package/src/plugins/builtin/buildout/pane/index.tsx +34 -18
  350. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  351. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  352. package/src/plugins/builtin/catalysts/client.ts +2 -2
  353. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  354. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  355. package/src/plugins/builtin/catalysts/model.ts +49 -4
  356. package/src/plugins/builtin/catalysts/pane.tsx +9 -11
  357. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  358. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  359. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  360. package/src/plugins/builtin/changelog/index.tsx +1 -0
  361. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  362. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  363. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  364. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  365. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  366. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  367. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  368. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  369. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  370. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  371. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  372. package/src/plugins/builtin/chart-composer/pane.tsx +128 -28
  373. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  374. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  375. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  376. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  377. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  378. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  379. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  380. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  381. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  382. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  383. package/src/plugins/builtin/chat/people.ts +1 -1
  384. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  385. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  386. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  387. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  388. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  389. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  390. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  391. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  392. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  393. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  394. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  395. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  396. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  397. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  398. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  399. package/src/plugins/builtin/commodity-balances/pane.tsx +14 -8
  400. package/src/plugins/builtin/company-attention/app-rank.tsx +11 -10
  401. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  402. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  403. package/src/plugins/builtin/company-attention/client.ts +117 -1
  404. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  405. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  406. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  407. package/src/plugins/builtin/company-attention/model.ts +33 -7
  408. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  409. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  410. package/src/plugins/builtin/company-attention/pane.tsx +89 -31
  411. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  412. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  413. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  414. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  415. package/src/plugins/builtin/company-kpis/pane.tsx +5 -4
  416. package/src/plugins/builtin/composite-plugins.ts +2 -1
  417. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  418. package/src/plugins/builtin/connections/index.ts +1 -0
  419. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  420. package/src/plugins/builtin/correlation/headless.ts +7 -4
  421. package/src/plugins/builtin/correlation/relationship/model.ts +1 -0
  422. package/src/plugins/builtin/correlation/settings.ts +1 -0
  423. package/src/plugins/builtin/cot/headless.ts +28 -6
  424. package/src/plugins/builtin/cot/model.ts +12 -0
  425. package/src/plugins/builtin/cot/pane.tsx +7 -5
  426. package/src/plugins/builtin/cpi/headless.ts +5 -1
  427. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  428. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  429. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  430. package/src/plugins/builtin/credit-documents/pane.tsx +8 -7
  431. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  432. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  433. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  434. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  435. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  436. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  437. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  438. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  439. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  440. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  441. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  442. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  443. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  444. package/src/plugins/builtin/debt-maturities/pane.tsx +4 -3
  445. package/src/plugins/builtin/debug/index.tsx +1 -0
  446. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  447. package/src/plugins/builtin/docs/index.tsx +1 -1
  448. package/src/plugins/builtin/docs/pane.tsx +2 -2
  449. package/src/plugins/builtin/docs/targets.ts +1 -1
  450. package/src/plugins/builtin/doe/client.ts +5 -1
  451. package/src/plugins/builtin/doe/headless.ts +12 -6
  452. package/src/plugins/builtin/doe/model.ts +43 -6
  453. package/src/plugins/builtin/doe/pane.tsx +19 -12
  454. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  455. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  456. package/src/plugins/builtin/earnings/board.tsx +253 -50
  457. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  458. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  459. package/src/plugins/builtin/earnings/client.ts +4 -3
  460. package/src/plugins/builtin/earnings/headless.ts +53 -15
  461. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  462. package/src/plugins/builtin/earnings/index.tsx +15 -22
  463. package/src/plugins/builtin/earnings/model.ts +17 -12
  464. package/src/plugins/builtin/earnings/scope.ts +89 -0
  465. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  466. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  467. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  468. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  469. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  470. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  471. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  472. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  473. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  474. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  475. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  476. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  477. package/src/plugins/builtin/econ/index.tsx +112 -25
  478. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  479. package/src/plugins/builtin/econ/types.ts +32 -1
  480. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  481. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  482. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  483. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  484. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  485. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  486. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  487. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  488. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  489. package/src/plugins/builtin/equity-screener/headless.ts +77 -5
  490. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  491. package/src/plugins/builtin/equity-screener/model.ts +34 -0
  492. package/src/plugins/builtin/equity-screener/pane.tsx +148 -106
  493. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  494. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  495. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  496. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  497. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  498. package/src/plugins/builtin/exposure/headless.ts +51 -6
  499. package/src/plugins/builtin/exposure/index.tsx +28 -2
  500. package/src/plugins/builtin/exposure/model.ts +7 -0
  501. package/src/plugins/builtin/exposure/pane.tsx +16 -10
  502. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  503. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  504. package/src/plugins/builtin/futures/model.ts +10 -9
  505. package/src/plugins/builtin/futures/returns.ts +8 -36
  506. package/src/plugins/builtin/futures/table.tsx +1 -1
  507. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  508. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  509. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  510. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  511. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  512. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  513. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  514. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  515. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  516. package/src/plugins/builtin/gpu/board.tsx +4 -1
  517. package/src/plugins/builtin/gpu/changes.tsx +10 -10
  518. package/src/plugins/builtin/gpu/equities.tsx +4 -1
  519. package/src/plugins/builtin/gpu/headless.ts +9 -4
  520. package/src/plugins/builtin/gpu/history.tsx +4 -2
  521. package/src/plugins/builtin/gpu/pane.tsx +5 -3
  522. package/src/plugins/builtin/help/index.tsx +13 -3
  523. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  524. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  525. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  526. package/src/plugins/builtin/holders/client.ts +15 -2
  527. package/src/plugins/builtin/holders/format.ts +28 -17
  528. package/src/plugins/builtin/holders/headless.ts +48 -8
  529. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  530. package/src/plugins/builtin/holders/pane.tsx +79 -15
  531. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  532. package/src/plugins/builtin/holders/table-model.ts +2 -1
  533. package/src/plugins/builtin/holders/thirteenf-match.ts +3 -1
  534. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  535. package/src/plugins/builtin/insider/base-values.ts +11 -7
  536. package/src/plugins/builtin/insider/client.ts +3 -0
  537. package/src/plugins/builtin/insider/headless.ts +20 -7
  538. package/src/plugins/builtin/insider/index.tsx +17 -8
  539. package/src/plugins/builtin/insider/issuer.ts +34 -4
  540. package/src/plugins/builtin/insider/signals-client.ts +3 -3
  541. package/src/plugins/builtin/insider/signals.tsx +1 -1
  542. package/src/plugins/builtin/insider/table-model.ts +35 -13
  543. package/src/plugins/builtin/insider/table.tsx +5 -2
  544. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  545. package/src/plugins/builtin/iv-history/client.ts +20 -0
  546. package/src/plugins/builtin/iv-history/events.ts +145 -0
  547. package/src/plugins/builtin/iv-history/format.ts +3 -0
  548. package/src/plugins/builtin/iv-history/headless.ts +91 -7
  549. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  550. package/src/plugins/builtin/iv-history/model.ts +13 -4
  551. package/src/plugins/builtin/iv-history/series.ts +3 -1
  552. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  553. package/src/plugins/builtin/jobs/model.ts +4 -15
  554. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  555. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  556. package/src/plugins/builtin/jodi/pane.tsx +14 -8
  557. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  558. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  559. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  560. package/src/plugins/builtin/macro-day/model.ts +29 -8
  561. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  562. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  563. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  564. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  565. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  566. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  567. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  568. package/src/plugins/builtin/market-movers/client.ts +36 -21
  569. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  570. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  571. package/src/plugins/builtin/market-movers/screener.ts +33 -11
  572. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  573. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  574. package/src/plugins/builtin/market-movers/table.ts +4 -1
  575. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  576. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  577. package/src/plugins/builtin/members/headless.ts +6 -5
  578. package/src/plugins/builtin/members/pane.tsx +11 -4
  579. package/src/plugins/builtin/mna/client.ts +1 -0
  580. package/src/plugins/builtin/mna/detail.tsx +20 -32
  581. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  582. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  583. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  584. package/src/plugins/builtin/mna/headless.ts +6 -13
  585. package/src/plugins/builtin/mna/model.ts +93 -15
  586. package/src/plugins/builtin/mna/pane.tsx +5 -4
  587. package/src/plugins/builtin/news/headless.ts +12 -6
  588. package/src/plugins/builtin/news/index.tsx +10 -1
  589. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  590. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  591. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  592. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  593. package/src/plugins/builtin/notes/cli.ts +50 -15
  594. package/src/plugins/builtin/notes/export.ts +17 -6
  595. package/src/plugins/builtin/notes/files.ts +16 -5
  596. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  597. package/src/plugins/builtin/options/carry.ts +2 -2
  598. package/src/plugins/builtin/options/enrichment-model.ts +10 -9
  599. package/src/plugins/builtin/options/index.tsx +1 -1
  600. package/src/plugins/builtin/options/model-spot.ts +70 -4
  601. package/src/plugins/builtin/options/view.tsx +51 -23
  602. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  603. package/src/plugins/builtin/options-calculator/pane.tsx +3 -2
  604. package/src/plugins/builtin/options-positioning/headless.ts +11 -3
  605. package/src/plugins/builtin/options-positioning/model.ts +24 -0
  606. package/src/plugins/builtin/options-positioning/pane.tsx +10 -12
  607. package/src/plugins/builtin/options-scenario/client.ts +142 -29
  608. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  609. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  610. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  611. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  612. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  613. package/src/plugins/builtin/options-scenario/headless.ts +49 -21
  614. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  615. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  616. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  617. package/src/plugins/builtin/options-scenario/pane.tsx +22 -10
  618. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  619. package/src/plugins/builtin/perps/freshness.ts +8 -5
  620. package/src/plugins/builtin/perps/headless.ts +3 -1
  621. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  622. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  623. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  624. package/src/plugins/builtin/perps/pane.tsx +36 -17
  625. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  626. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  627. package/src/plugins/builtin/plugin-marketplace/pane.tsx +145 -60
  628. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +78 -27
  629. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  630. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  631. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  632. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  633. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  634. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  635. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  636. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  637. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  638. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  639. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  640. package/src/plugins/builtin/power/client.ts +6 -3
  641. package/src/plugins/builtin/power/evidence.ts +22 -7
  642. package/src/plugins/builtin/power/gas.tsx +5 -1
  643. package/src/plugins/builtin/power/pane.tsx +44 -32
  644. package/src/plugins/builtin/power/prices.tsx +10 -3
  645. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  646. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  647. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  648. package/src/plugins/builtin/research/event-model.ts +3 -1
  649. package/src/plugins/builtin/research/event-sources.ts +12 -4
  650. package/src/plugins/builtin/research/index.tsx +2 -1
  651. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  652. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  653. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  654. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  655. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  656. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  657. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  658. package/src/plugins/builtin/reverse-dcf/client.ts +2 -1
  659. package/src/plugins/builtin/reverse-dcf/model.ts +10 -5
  660. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  661. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  662. package/src/plugins/builtin/seasonality/client.ts +2 -1
  663. package/src/plugins/builtin/sec/forms.ts +13 -0
  664. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  665. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  666. package/src/plugins/builtin/sec/headless.ts +85 -8
  667. package/src/plugins/builtin/sec/index.tsx +58 -12
  668. package/src/plugins/builtin/sectors/client.ts +6 -4
  669. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  670. package/src/plugins/builtin/series-inventory.ts +2 -0
  671. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  672. package/src/plugins/builtin/shared/cloud-resource.ts +15 -2
  673. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  674. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  675. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  676. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  677. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  678. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  679. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  680. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  681. package/src/plugins/builtin/shared/research-trading.tsx +23 -0
  682. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  683. package/src/plugins/builtin/shared/series-list-detail.tsx +3 -1
  684. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  685. package/src/plugins/builtin/shared/volatility/options.ts +2 -1
  686. package/src/plugins/builtin/short-interest/client.ts +2 -0
  687. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  688. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  689. package/src/plugins/builtin/short-interest/model.ts +70 -19
  690. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  691. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  692. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  693. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  694. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  695. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  696. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  697. package/src/plugins/builtin/short-interest/types.ts +9 -0
  698. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  699. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  700. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  701. package/src/plugins/builtin/social-mentions/client.ts +2 -1
  702. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  703. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  704. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  705. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  706. package/src/plugins/builtin/themes/headless.ts +8 -5
  707. package/src/plugins/builtin/themes/model.ts +14 -4
  708. package/src/plugins/builtin/themes/pane.tsx +24 -5
  709. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  710. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  711. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  712. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  713. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  714. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  715. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  716. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  717. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  718. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  719. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  720. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  721. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  722. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  723. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  724. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  725. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  726. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  727. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  728. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  729. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  730. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  731. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  732. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  733. package/src/plugins/builtin/vol-surface/client.ts +28 -7
  734. package/src/plugins/builtin/vol-surface/headless.ts +43 -15
  735. package/src/plugins/builtin/vol-surface/model.ts +34 -12
  736. package/src/plugins/builtin/vol-surface/pane.tsx +33 -13
  737. package/src/plugins/builtin/vol-surface/stored.ts +34 -3
  738. package/src/plugins/builtin/vol-surface/tables.ts +14 -0
  739. package/src/plugins/builtin/volatility/model.ts +5 -10
  740. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  741. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  742. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  743. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  744. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  745. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  746. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  747. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  748. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  749. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  750. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  751. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  752. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  753. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  754. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  755. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  756. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  757. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  758. package/src/plugins/bundle.ts +1 -1
  759. package/src/plugins/catalog-browser.ts +2 -1
  760. package/src/plugins/compat.ts +3 -3
  761. package/src/plugins/dependencies.ts +75 -14
  762. package/src/plugins/extracted-plugins.ts +132 -34
  763. package/src/plugins/host-link.ts +149 -34
  764. package/src/plugins/installer.ts +35 -10
  765. package/src/plugins/loader.ts +206 -38
  766. package/src/plugins/manager-node.ts +2 -2
  767. package/src/plugins/marketplace/deactivation.ts +22 -0
  768. package/src/plugins/marketplace/model.ts +2 -2
  769. package/src/plugins/marketplace/packs.ts +1 -1
  770. package/src/plugins/ownership.ts +1 -1
  771. package/src/plugins/peer-retry.ts +155 -0
  772. package/src/plugins/registry/contributions.ts +3 -1
  773. package/src/plugins/registry/index.ts +4 -3
  774. package/src/plugins/registry/plugin-state.ts +3 -0
  775. package/src/plugins/seed-notice.ts +20 -0
  776. package/src/plugins/shortcut-argument.ts +39 -0
  777. package/src/portfolio/allocation.ts +1 -1
  778. package/src/portfolio/collection-name.ts +31 -0
  779. package/src/portfolio/column-values.ts +10 -8
  780. package/src/portfolio/cost-fx.ts +296 -19
  781. package/src/portfolio/manual-lots.ts +75 -0
  782. package/src/portfolio/mutations.ts +60 -22
  783. package/src/portfolio/suffixed-listings.ts +209 -0
  784. package/src/portfolio/totals-currency.ts +24 -0
  785. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  786. package/src/public/time-series.ts +11 -8
  787. package/src/remote/schema.ts +1 -1
  788. package/src/renderers/browser/config-host.ts +3 -0
  789. package/src/renderers/browser/main.tsx +23 -3
  790. package/src/renderers/dom/boot-frame-page.ts +187 -0
  791. package/src/renderers/dom/boot-frame.ts +237 -0
  792. package/src/renderers/dom/build-assets.ts +46 -8
  793. package/src/renderers/dom/desktop/controls.tsx +1 -1
  794. package/src/renderers/dom/desktop/query-bar.tsx +2 -0
  795. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  796. package/src/renderers/dom/host/ascii-text.ts +16 -2
  797. package/src/renderers/dom/host/box.tsx +43 -2
  798. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  799. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  800. package/src/renderers/dom/host/image-surface.tsx +49 -0
  801. package/src/renderers/dom/host/media-surface.tsx +24 -18
  802. package/src/renderers/dom/host/mouse.ts +30 -0
  803. package/src/renderers/dom/host/size-observer.ts +1 -1
  804. package/src/renderers/dom/host/style.ts +1 -1
  805. package/src/renderers/dom/host/tabs.tsx +16 -5
  806. package/src/renderers/dom/host/top-surface.ts +127 -0
  807. package/src/renderers/dom/styles.css +115 -0
  808. package/src/renderers/dom/test-utils.tsx +2 -1
  809. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  810. package/src/renderers/electrobun/bun/core-capabilities.ts +9 -1
  811. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  812. package/src/renderers/electrobun/bun/desktop/backend.ts +56 -3
  813. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  814. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  815. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  816. package/src/renderers/electrobun/bun/desktop/update.ts +210 -30
  817. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  818. package/src/renderers/electrobun/bun/index.ts +11 -1
  819. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  820. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  821. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  822. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  823. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  824. package/src/renderers/electrobun/view/config-host.ts +3 -0
  825. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  826. package/src/renderers/electrobun/view/main.tsx +30 -4
  827. package/src/renderers/electrobun/view/news-source.ts +9 -7
  828. package/src/renderers/electrobun/view/remote/asset-data-client.ts +9 -2
  829. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  830. package/src/renderers/opentui/browser-open.ts +88 -0
  831. package/src/renderers/opentui/host.tsx +5 -14
  832. package/src/renderers/opentui/image/loader.ts +3 -2
  833. package/src/renderers/opentui/native-smoke.ts +10 -1
  834. package/src/renderers/opentui/pane-image/index.ts +2 -1
  835. package/src/renderers/opentui/render-lib.ts +253 -0
  836. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  837. package/src/renderers/opentui/start.tsx +8 -1
  838. package/src/renderers/share/layout-view.tsx +2 -2
  839. package/src/renderers/share/main.tsx +4 -4
  840. package/src/renderers/share/view.tsx +1 -1
  841. package/src/series/resolve.ts +22 -6
  842. package/src/series/standalone.ts +30 -2
  843. package/src/series/types.ts +8 -1
  844. package/src/shares/api.ts +4 -1
  845. package/src/shares/pane.ts +1 -1
  846. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  847. package/src/sources/gloomberb-cloud/index.ts +39 -16
  848. package/src/sources/gloomberb-cloud/news.ts +39 -0
  849. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  850. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  851. package/src/sources/listing-symbols.ts +37 -0
  852. package/src/sources/provider-errors.ts +16 -2
  853. package/src/sources/provider-router/financial-routes.ts +21 -3
  854. package/src/sources/provider-router/financials.ts +15 -2
  855. package/src/sources/provider-router/history.ts +30 -8
  856. package/src/sources/provider-router/index.ts +16 -0
  857. package/src/sources/provider-router/primary.ts +1 -1
  858. package/src/state/app/bootstrap.ts +23 -0
  859. package/src/state/hooks/inline-tickers.ts +30 -5
  860. package/src/sync/controller.ts +1 -1
  861. package/src/sync/core-contributors.ts +46 -14
  862. package/src/team/collections-sync.ts +2 -1
  863. package/src/team/collections.ts +178 -17
  864. package/src/telemetry/crash-reports.ts +21 -4
  865. package/src/telemetry/update-failures-node.ts +59 -0
  866. package/src/telemetry/update-failures.ts +307 -0
  867. package/src/test-support/builtin-panes.ts +1 -1
  868. package/src/theme/colors.ts +3 -30
  869. package/src/theme/ui-tokens.ts +37 -1
  870. package/src/tickers/company-name-listing.ts +215 -36
  871. package/src/tickers/company-name.ts +43 -0
  872. package/src/tickers/index-roots.ts +47 -0
  873. package/src/tickers/instrument-label.ts +5 -1
  874. package/src/tickers/listing-fate.ts +30 -0
  875. package/src/tickers/metadata.ts +10 -0
  876. package/src/tickers/perpetual-listing.ts +5 -2
  877. package/src/tickers/search/home-listing.ts +75 -0
  878. package/src/tickers/search/index.ts +76 -27
  879. package/src/tickers/search/ranking.ts +54 -4
  880. package/src/tickers/search/upsert.ts +29 -0
  881. package/src/tickers/terminal-codes.ts +416 -40
  882. package/src/time-series/bar-aggregation.ts +243 -0
  883. package/src/time-series/base-series.ts +307 -0
  884. package/src/time-series/chart-bounds.ts +349 -0
  885. package/src/time-series/chart-seed.ts +142 -0
  886. package/src/time-series/economic.ts +5 -0
  887. package/src/time-series/fundamentals.ts +18 -3
  888. package/src/time-series/history-acquisition.ts +499 -0
  889. package/src/time-series/leverage.ts +100 -0
  890. package/src/time-series/market-resolution.ts +4 -2
  891. package/src/time-series/market.ts +2 -1
  892. package/src/time-series/price-comparison.ts +175 -30
  893. package/src/time-series/range.ts +9 -1
  894. package/src/time-series/resolution.ts +174 -16
  895. package/src/time-series/resolve-context.ts +157 -0
  896. package/src/time-series/resolve.ts +126 -1577
  897. package/src/time-series/series-name.ts +38 -0
  898. package/src/time-series/series-presentation.ts +208 -0
  899. package/src/time-series/spec.ts +11 -6
  900. package/src/time-series/studies.ts +109 -12
  901. package/src/time-series/tail-reconcile.ts +121 -0
  902. package/src/time-series/types.ts +29 -2
  903. package/src/time-series/use-chart-resolution.ts +4 -5
  904. package/src/types/broker.ts +10 -0
  905. package/src/types/config.ts +14 -0
  906. package/src/types/data-provider.ts +20 -0
  907. package/src/types/exchange-rate.ts +6 -0
  908. package/src/types/financials.ts +104 -7
  909. package/src/types/headless.ts +83 -6
  910. package/src/types/instrument.ts +3 -0
  911. package/src/types/issuer-listings.ts +28 -0
  912. package/src/types/plugin.ts +65 -8
  913. package/src/types/price-history.ts +2 -0
  914. package/src/types/ticker.ts +13 -0
  915. package/src/types/trading.ts +59 -1
  916. package/src/ui/ascii-font.ts +13 -1
  917. package/src/ui/host.tsx +6 -1
  918. package/src/updater/command-names.ts +175 -0
  919. package/src/updater/failure.ts +163 -0
  920. package/src/updater/index.ts +97 -28
  921. package/src/utils/calendar-date.ts +32 -15
  922. package/src/utils/cash-runway.ts +241 -0
  923. package/src/utils/central-banks.ts +42 -0
  924. package/src/utils/cli-output.ts +259 -80
  925. package/src/utils/contract-month.ts +16 -0
  926. package/src/utils/crypto-pair.ts +61 -3
  927. package/src/utils/currency-pair.ts +26 -0
  928. package/src/utils/currency-units.ts +29 -0
  929. package/src/utils/datetime-format.ts +10 -0
  930. package/src/utils/debug-log.ts +1 -1
  931. package/src/utils/decimal-comma.ts +39 -6
  932. package/src/utils/exchange-announcements.ts +71 -0
  933. package/src/utils/exchange-rate-snapshot.ts +10 -0
  934. package/src/utils/financial-statements.ts +83 -4
  935. package/src/utils/format.ts +34 -16
  936. package/src/utils/fundamentals.ts +150 -34
  937. package/src/utils/fx-market-hours.ts +10 -1
  938. package/src/utils/latest-financial-period.ts +8 -0
  939. package/src/utils/linux-libc.ts +54 -0
  940. package/src/utils/market-capitalization.ts +111 -15
  941. package/src/utils/max-drawdown.ts +64 -0
  942. package/src/utils/network-failure.ts +83 -0
  943. package/src/utils/option-expiry.ts +139 -7
  944. package/src/utils/price-earnings.ts +11 -5
  945. package/src/utils/price-history.ts +18 -8
  946. package/src/utils/price-window.ts +131 -0
  947. package/src/utils/release-date.ts +47 -0
  948. package/src/utils/sec.ts +71 -0
  949. package/src/utils/throttled-fetch.ts +11 -4
  950. package/src/utils/trailing-eps.ts +67 -0
  951. package/src/utils/typed-text.ts +21 -0
  952. package/src/utils/utc-time.ts +24 -1
  953. package/src/version.ts +6 -0
  954. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -0,0 +1,29 @@
1
+ import type { HeadlessPaneDefinition } from "../../../types/plugin";
2
+ import { fetchPublication, type PublicationKind } from "./publications-client";
3
+ import { publicationReportColumns, publicationRows } from "./publications-model";
4
+
5
+ export function publicationHeadless(kind: PublicationKind): HeadlessPaneDefinition<"bundle"> {
6
+ const fails = kind === "ftd";
7
+ const subject = fails ? "Fails to deliver" : "Weekly ATS / non-ATS volume";
8
+ return {
9
+ shape: "bundle", options: [],
10
+ argument: { kind: "ticker", description: "US equity ticker.", placeholder: "ticker" },
11
+ usListingsOnly: fails ? "SEC fails to deliver" : "FINRA weekly off-exchange volume",
12
+ freshness: { source: fails ? "SEC" : "FINRA", status: "not-a-feed", basis: fails ? "settlement-date outstanding fails" : "published off-exchange volume",
13
+ schedule: fails ? "published twice monthly" : "published weekly with a delay", observedKey: "date", oldest: null },
14
+ discovery: {
15
+ aliases: fails ? ["FTD", "fails to deliver", "fails-to-deliver"] : ["ATS", "dark pool", "dark pools", "non-ATS", "OTC transparency"],
16
+ dataRequirements: [fails ? "SEC settlement fails" : "FINRA weekly off-exchange volume"],
17
+ limitations: fails ? ["Outstanding fails on each settlement date; never add them across dates.", "Multiple CUSIPs remain separate. An absent record does not establish zero fails."]
18
+ : ["ATS share divides ATS shares by published ATS plus non-ATS shares, not total market volume.", "Missing categories stay blank; published off-exchange volume is not short volume.", "FINRA publishes weeks with a delay, which varies by security tier."],
19
+ },
20
+ describe: (args) => `${subject} | ${args.symbols[0]}`,
21
+ async load(args, ctx) {
22
+ const data = await fetchPublication(args.symbols[0]!, kind, ctx.apiClient);
23
+ return { complete: data.status === "available", errors: data.warnings,
24
+ sections: [{ title: subject, columns: publicationReportColumns(kind), rows: publicationRows(data) }],
25
+ metadata: { symbol: data.symbol, asOf: data.asOf, sourceAsOf: data.sourceAsOf, loadedAt: data.loadedAt, source: data.source, coverage: data.coverage },
26
+ };
27
+ },
28
+ };
29
+ }
@@ -0,0 +1,50 @@
1
+ import type { DataTableColumn } from "../../../components";
2
+ import type { HeadlessPaneColumn } from "../../../types/plugin";
3
+ import type { PublicationKind, PublicationPayload } from "./publications-client";
4
+ import { formatCompact } from "../../../utils/format";
5
+
6
+ export interface PublicationRow {
7
+ id: string;
8
+ date: string;
9
+ sourceUrl: string;
10
+ [key: string]: string | number | null;
11
+ }
12
+ export interface PublicationColumn extends DataTableColumn {
13
+ detailLabel: string;
14
+ format?: (value: string | number | null) => string;
15
+ }
16
+ const shares = (value: string | number | null) => value == null ? "--" : formatCompact(Number(value), { fixedDecimals: true });
17
+ const percentage = (value: string | number | null) => value == null ? "--" : `${Number(value).toFixed(2)}%`;
18
+ const price = (value: string | number | null) => value == null ? "--" : `$${Number(value).toFixed(2)}`;
19
+ const FTD_COLUMNS: PublicationColumn[] = [
20
+ { id: "date", label: "SETTLEMENT", detailLabel: "Settlement date", width: 12, align: "left" },
21
+ { id: "quantity", label: "FAIL SHARES", detailLabel: "Outstanding fail shares", width: 14, align: "right", format: shares },
22
+ { id: "cusip", label: "CUSIP", detailLabel: "CUSIP", width: 11, align: "left" },
23
+ { id: "price", label: "PRICE (USD)", detailLabel: "Reported price (USD)", width: 13, align: "right", format: price },
24
+ { id: "description", label: "SECURITY", detailLabel: "Security", width: 20, flexGrow: 1, align: "left" },
25
+ ];
26
+ const ATS_COLUMNS: PublicationColumn[] = [
27
+ { id: "date", label: "WEEK OF", detailLabel: "Week starting", width: 12, align: "left" },
28
+ { id: "atsShares", label: "ATS SHARES", detailLabel: "ATS shares", width: 14, align: "right", format: shares },
29
+ { id: "nonAtsShares", label: "NON-ATS SHARES", detailLabel: "Non-ATS shares", width: 16, align: "right", format: shares },
30
+ { id: "atsSharePercent", label: "ATS %", title: "ATS shares / published ATS plus non-ATS shares (%)", detailLabel: "ATS %", width: 14, align: "right", format: percentage },
31
+ { id: "totalShares", label: "TOTAL SHARES", detailLabel: "Total off-exchange shares", width: 14, align: "right", format: shares },
32
+ { id: "atsTrades", label: "ATS TRADES", detailLabel: "ATS trades", width: 14, align: "right", format: shares },
33
+ { id: "nonAtsTrades", label: "NON-ATS TRADES", detailLabel: "Non-ATS trades", width: 16, align: "right", format: shares },
34
+ ];
35
+ export const publicationColumns = (kind: PublicationKind) => kind === "ftd" ? FTD_COLUMNS : ATS_COLUMNS;
36
+ export function publicationRows(data: PublicationPayload): PublicationRow[] {
37
+ return data.history.toReversed().map((row) => "cusip" in row
38
+ ? { ...row, id: `${row.date}:${row.cusip}` }
39
+ : { ...row, id: row.weekStart, date: row.weekStart });
40
+ }
41
+ export const publicationCell = (column: PublicationColumn, value: string | number | null) =>
42
+ column.format ? column.format(value) : String(value ?? "--");
43
+ /** Reports retain exact quantities; compact display values are never the exported data. */
44
+ export function publicationReportColumns(kind: PublicationKind): HeadlessPaneColumn[] {
45
+ return publicationColumns(kind).map((column) => ({
46
+ key: column.id, header: column.detailLabel, align: column.align,
47
+ ...(column.id === "atsSharePercent" ? { format: (value: unknown) => percentage(value as number | null) } : {}),
48
+ }));
49
+
50
+ }
@@ -0,0 +1,66 @@
1
+ import { useCallback, useMemo } from "react";
2
+ import { DataTableStackView, EmptyState, PaneStatusBody, usePaneNoticeFooter, usePaneStatusLinkFooter, type DataTableCell } from "../../../components";
3
+ import { DetailReader } from "../../../components/reader/detail-reader";
4
+ import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
5
+ import { useAsyncResource, useAutoRefresh, usePluginPaneState, useUpdatedAgo } from "../../../public/react";
6
+ import { colors } from "../../../theme/colors";
7
+ import { usePaneTickerIdentity } from "../../../state/hooks/pane-ticker";
8
+ import type { PaneProps } from "../../../types/plugin";
9
+ import { isKnownNonUsEquityTicker } from "../../../utils/sec";
10
+ import { listingIdentity } from "../shared/ticker-request";
11
+ import { cachedPublication, loadPublication, type PublicationKind } from "./publications-client";
12
+ import { publicationCell, publicationColumns, publicationRows, type PublicationColumn, type PublicationRow } from "./publications-model";
13
+
14
+ function renderCell(row: PublicationRow, column: PublicationColumn, _index: number, state: { selected: boolean }): DataTableCell {
15
+ const value = row[column.id] ?? null;
16
+ return { text: publicationCell(column, value), value, color: state.selected ? colors.selectedText : value === null ? colors.textMuted : colors.text };
17
+ }
18
+ export function ShortSidePublicationPane({ kind, width, height, focused }: Pick<PaneProps, "width" | "height" | "focused"> & { kind: PublicationKind }) {
19
+ const { ticker } = usePaneTickerIdentity();
20
+ const symbol = listingIdentity(ticker?.metadata.ticker)?.symbol ?? null;
21
+ const foreign = !!ticker && isKnownNonUsEquityTicker(ticker);
22
+ const subject = kind === "ftd" ? "fails to deliver" : "weekly off-exchange volume";
23
+ const loader = useCallback((force: boolean) => loadPublication(symbol!, kind, force), [symbol, kind]);
24
+ const resource = useAsyncResource(symbol && !foreign ? loader : null, {
25
+ initialData: () => symbol && !foreign ? cachedPublication(symbol, kind) : null,
26
+ });
27
+ const data = resource.data?.payload;
28
+ const rows = useMemo(() => data ? publicationRows(data) : [], [data]);
29
+ const columns = publicationColumns(kind);
30
+ const [selectedId, setSelectedId] = usePluginPaneState<string | null>(`${kind}:selected`, null);
31
+ const [openId, setOpenId] = usePluginPaneState<string | null>(`${kind}:open`, null);
32
+ const rowKey = (row: PublicationRow) => `${symbol}:${row.id}`;
33
+ const selectedIndex = Math.max(0, rows.findIndex((row) => rowKey(row) === selectedId));
34
+ const openRow = rows.find((row) => rowKey(row) === openId);
35
+ const selected = openRow ?? rows[selectedIndex];
36
+ const updatedAgo = useUpdatedAgo(resource.updatedAt);
37
+ useAutoRefresh(resource.updatedAt, resource.load);
38
+ usePaneRefreshKey(() => void resource.reload(), { focused });
39
+ usePaneNoticeFooter({ registrationId: `${kind}:notices`, focused,
40
+ notices: [...(data?.warnings ?? []), ...(resource.data?.refreshError ? [resource.data.refreshError] : [])] });
41
+ usePaneStatusLinkFooter({ registrationId: kind, focused, loading: resource.loading, error: resource.error,
42
+ url: selected?.sourceUrl ?? data?.source.url ?? null, showOpenHint: true,
43
+ info: data && updatedAgo ? [{ id: "updated", parts: [{ text: updatedAgo, tone: "muted" as const }] }] : [],
44
+ stale: !!data && resource.data?.stale,
45
+ });
46
+ if (!symbol) return <EmptyState title="No ticker selected." message={`Select a US stock to view ${subject}.`} />;
47
+ if (foreign) return <EmptyState title="US listings only." message={`Select a US stock to view ${subject}.`} />;
48
+ return <PaneStatusBody loading={resource.loading && !data} error={!data ? resource.error : null} subject={subject}
49
+ empty={!resource.loading && !resource.error && !!data && !rows.length} emptyTitle={`No published ${subject} for this stock.`}>
50
+ {data ? <DataTableStackView<PublicationRow, PublicationColumn>
51
+ focused={focused} rootWidth={width} rootHeight={height} columns={columns} items={rows} freezeFirstColumn
52
+ sortColumnId={null} sortDirection="desc" emptyStateTitle={`No published ${subject} for this stock.`}
53
+ getItemKey={rowKey} renderCell={renderCell} resetScrollKey={`${kind}:${symbol}`}
54
+ selection={{ kind: "index", selectedIndex: rows.length ? selectedIndex : -1, onChange: (index) => setSelectedId(rows[index] ? rowKey(rows[index]!) : null) }}
55
+ onActivate={(row) => setOpenId(rowKey(row))} detailOpen={!!openRow} onBack={() => setOpenId(null)}
56
+ detailTitle={openRow ? kind === "ftd" ? `${openRow.date} · ${openRow.cusip}` : `Week of ${openRow.date}` : undefined}
57
+ detailContent={openRow ? <DetailReader width={width} resetScrollKey={rowKey(openRow)} facts={[
58
+ ...columns.filter((column) => column.id !== "date" && column.id !== "cusip").map((column) => ({ label: column.detailLabel,
59
+ value: openRow[column.id] == null ? "Not published" : typeof openRow[column.id] === "number" && column.id !== "atsSharePercent" && column.id !== "price"
60
+ ? Number(openRow[column.id]).toLocaleString("en-US", { maximumFractionDigits: 6 }) : publicationCell(column, openRow[column.id] ?? null) })),
61
+ { label: "Published through", value: data.sourceAsOf ?? "--" },
62
+ { label: "Retrieved", value: data.loadedAt?.replace("T", " ").replace(/\.\d+Z$/, " UTC") ?? "--" },
63
+ ]} /> : null}
64
+ /> : null}
65
+ </PaneStatusBody>;
66
+ }
@@ -1,3 +1,4 @@
1
+ import type { PaneFooterSegment } from "../../../components";
1
2
  import { getPublishedUsEquityCalendarDay } from "../../../market-data/published-us-sessions";
2
3
 
3
4
  /**
@@ -100,8 +101,40 @@ export function shortInterestScheduleText(schedule: ShortInterestSchedule): stri
100
101
  return `published ${day(schedule.published)}, next settlement ${day(schedule.nextSettlement)}, published ${day(schedule.nextPublished)}`;
101
102
  }
102
103
 
104
+ /**
105
+ * The schedule in fewer words, for a line too narrow for all of it: `published
106
+ * Fri 9 Oct, next Mon 26 Oct`, then `published Fri 9 Oct`. When the figures
107
+ * were published goes last.
108
+ */
109
+ function shortInterestScheduleVariants(schedule: ShortInterestSchedule): string[] {
110
+ const day = (date: string) => dayText(date, schedule.settlement);
111
+ return [`published ${day(schedule.published)}, next ${day(schedule.nextPublished)}`, `published ${day(schedule.published)}`];
112
+ }
113
+
114
+ /** When FINRA published a settlement (`YYYY-MM-DD`), by the schedule rule; null for a date that is not one. */
115
+ export function shortInterestPublished(settlement: string): string | null {
116
+ return shortInterestSchedule(settlement)?.published ?? null;
117
+ }
118
+
103
119
  /** The report's freshness for the newest settlement it shows; nothing when no settlement is dated. */
104
- export function shortInterestFreshness(latestSettlement: string | null | undefined): { schedule: string; publishedAt: string } | undefined {
120
+ export function shortInterestFreshness(
121
+ latestSettlement: string | null | undefined,
122
+ ): { schedule: string; scheduleVariants: string[]; publishedAt: string } | undefined {
105
123
  const schedule = latestSettlement ? shortInterestSchedule(latestSettlement) : null;
106
- return schedule ? { schedule: shortInterestScheduleText(schedule), publishedAt: schedule.published } : undefined;
124
+ return schedule
125
+ ? { schedule: shortInterestScheduleText(schedule), scheduleVariants: shortInterestScheduleVariants(schedule), publishedAt: schedule.published }
126
+ : undefined;
127
+ }
128
+
129
+ /**
130
+ * The footer segment SI and SIW date their figures with: when the newest
131
+ * settlement shown was published and when the next is due, the line `fn` and
132
+ * `shot` carry after the settlement date, shortening as the footer narrows.
133
+ * A capture draws its own status line in its place.
134
+ */
135
+ export function shortInterestPublicationSegments(latestSettlement: string | null | undefined): PaneFooterSegment[] {
136
+ const freshness = shortInterestFreshness(latestSettlement);
137
+ if (!freshness) return [];
138
+ const [line, ...shorter] = [freshness.schedule, ...freshness.scheduleVariants].map((text) => [{ text, tone: "muted" as const }]);
139
+ return [{ id: "short-interest-publication", asOf: true, parts: line!, variants: shorter }];
107
140
  }
@@ -5,25 +5,31 @@ import type { PaneProps } from "../../../types/plugin";
5
5
  import { useCommandTab } from "../shared/command-tab";
6
6
  import { ShortVolumePane } from "../short-volume/pane";
7
7
  import { ShortInterestView } from "./pane";
8
+ import { ShortSidePublicationPane } from "./publications-pane";
8
9
 
9
- const TABS = [{ value: "interest", label: "Interest" }, { value: "volume", label: "Daily volume" }];
10
+ type ShortSideTab = "interest" | "volume" | "ftd" | "ats";
11
+ const TABS: { value: ShortSideTab; label: string }[] = [{ value: "interest", label: "Interest" }, { value: "volume", label: "Daily volume" },
12
+ { value: "ftd", label: "Fails to deliver" }, { value: "ats", label: "Weekly ATS/OTC" }];
10
13
  export function ShortInterestSurface({ nested = false, ...props }: Pick<PaneProps, "width" | "height" | "focused"> & {
11
14
  /** Inside Ticker Research, whose own tab strip keeps h/l and the arrows. */
12
15
  nested?: boolean;
13
16
  }) {
14
- const [tab, setTab] = useCommandTab("short-interest", "short-interest:tab", ["interest", "volume"], "interest");
17
+ const [tab, setTab] = useCommandTab("short-interest", "short-interest:tab", ["interest", "volume", "ftd", "ats"], "interest");
15
18
  const [mounted, setMounted] = useState<Set<string>>(() => new Set([tab]));
16
19
  useEffect(() => { setMounted((current) => current.has(tab) ? current : new Set([...current, tab])); }, [tab]);
17
20
  // Nested in Ticker Research the title bar belongs to the research tabs, so
18
21
  // the desktop switches views from the query bar and the terminal strip
19
22
  // leaves h/l to the research tabs.
20
23
  const { strip: tabStrip, rows: tabRows } = usePaneTabs({
21
- tabs: TABS, activeValue: tab, onSelect: (value) => setTab(value === "volume" ? "volume" : "interest"), focused: props.focused, keyboardNavigation: !nested, dense: true, queryBarWidth: props.width,
24
+ tabs: TABS, activeValue: tab, onSelect: (value) => {
25
+ const next = TABS.find((item) => item.value === value);
26
+ if (next) setTab(next.value);
27
+ }, focused: props.focused, keyboardNavigation: !nested, dense: true, queryBarWidth: props.width,
22
28
  });
23
29
  // The strip answers h/l only where it is the pane's own strip; `v` switches
24
30
  // views everywhere, including under Ticker Research's strip.
25
31
  usePaneFooter("short-interest:view", () => ({
26
- hints: [{ id: "view", key: "v", label: "iew", onPress: () => setTab(tab === "interest" ? "volume" : "interest") }],
32
+ hints: [{ id: "view", key: "v", label: "iew", onPress: () => setTab(TABS[(TABS.findIndex(({ value }) => value === tab) + 1) % TABS.length]!.value) }],
27
33
  }), [setTab, tab]);
28
34
  const height = Math.max(1, props.height - tabRows);
29
35
  return <Box width={props.width} height={props.height} flexDirection="column">
@@ -31,6 +37,7 @@ export function ShortInterestSurface({ nested = false, ...props }: Pick<PaneProp
31
37
  {TABS.map(({ value }) => mounted.has(value) || value === tab ? <Box key={value} visible={value === tab} height={height} flexGrow={1} flexBasis={0} overflow="hidden">
32
38
  <PaneFooterScope active={value === tab}>
33
39
  {value === "volume" ? <ShortVolumePane {...props} height={height} focused={props.focused && value === tab} />
40
+ : value === "ftd" || value === "ats" ? <ShortSidePublicationPane {...props} kind={value} height={height} focused={props.focused && value === tab} />
34
41
  : <ShortInterestView {...props} height={height} focused={props.focused && value === tab} />}
35
42
  </PaneFooterScope>
36
43
  </Box> : null)}
@@ -4,4 +4,13 @@ export interface ShortInterestRecord {
4
4
  shortRatio: number | null;
5
5
  averageDailyVolume: number | null;
6
6
  shortPercentFloat: number | null;
7
+ /**
8
+ * The 10-K or 10-Q cover share count known when FINRA published the
9
+ * settlement, which its percent of float is read on; null when none was.
10
+ */
11
+ sharesOutstanding?: number | null;
12
+ /** YYYY-MM-DD that count was taken. */
13
+ sharesOutstandingAsOf?: string | null;
14
+ /** YYYY-MM-DD of the share count `shortPercentFloat` is read on, when that count is dated. */
15
+ floatAsOf?: string | null;
7
16
  }
@@ -6,7 +6,7 @@ import type { DataProvider } from "../../../types/data-provider";
6
6
  import type { PricePoint, TickerFinancials } from "../../../types/financials";
7
7
  import { errorMessage } from "../../../utils/errors";
8
8
  import { loadShortInterest, type ShortInterestResult } from "./client";
9
- import { type SettlementFloatStatistics, withLatestPercentFloat } from "./model";
9
+ import { type CurrentFloat, type SettlementFloatStatistics, withPercentFloat } from "./model";
10
10
  import { buildShortWatchRow, type ShortWatchRow } from "./watch-model";
11
11
  import type { ShortInterestRecord } from "./types";
12
12
 
@@ -31,9 +31,11 @@ export function createShortWatchDependencies(
31
31
  };
32
32
  }
33
33
 
34
- /** A symbol's current float and the statistics' short % of float with its settlement, from its snapshot. */
35
- interface FloatSnapshot {
34
+ /** A symbol's current float, the count it floats out of, and the statistics' short % of float with its settlement, from its snapshot. */
35
+ interface FloatSnapshot extends CurrentFloat {
36
36
  floatShares: number | null;
37
+ floatAsOf: string | null;
38
+ sharesOutstanding: number | null;
37
39
  statistics: SettlementFloatStatistics | null;
38
40
  }
39
41
 
@@ -51,6 +53,8 @@ async function loadFloats(
51
53
  const fundamentals = (entry ? resolveEntryValue(entry) : null)?.fundamentals;
52
54
  return {
53
55
  floatShares: fundamentals?.floatShares ?? null,
56
+ floatAsOf: fundamentals?.floatSharesAsOf ?? null,
57
+ sharesOutstanding: fundamentals?.sharesOutstanding ?? null,
54
58
  statistics: fundamentals?.shortInterestDate
55
59
  ? { settlementDate: fundamentals.shortInterestDate, shortPercentOfFloat: fundamentals.shortPercentOfFloat ?? null }
56
60
  : null,
@@ -59,20 +63,22 @@ async function loadFloats(
59
63
  }
60
64
 
61
65
  /**
62
- * A ticker's settlements with the newest one's percent of float, as SIW reads
63
- * it (settlementPercentFloat). A snapshot that fails leaves the settlements as
64
- * they are.
66
+ * A ticker's settlements, each with its percent of float (withPercentFloat):
67
+ * the newest on the current float, as SIW reads it, the rest on the float of
68
+ * their own day; with `pointInTime`, every one on its own day's. A snapshot
69
+ * that fails leaves the settlements as they are.
65
70
  */
66
71
  export async function loadShortInterestWithFloat(
67
72
  symbol: string,
68
- { marketData, client = apiClient, loadSnapshots = createShortWatchDependencies(marketData, client).loadSnapshots }: {
73
+ { marketData, client = apiClient, loadSnapshots = createShortWatchDependencies(marketData, client).loadSnapshots, pointInTime = false }: {
69
74
  marketData?: DataProvider;
70
75
  client?: Pick<typeof apiClient, "getCloudShortInterest">;
71
76
  loadSnapshots?: ShortWatchDependencies["loadSnapshots"];
77
+ pointInTime?: boolean;
72
78
  } = {},
73
79
  ): Promise<ShortInterestResult> {
74
80
  const [result, [snapshot]] = await Promise.all([loadShortInterest(symbol, client), loadFloats([symbol], loadSnapshots)]);
75
- return { ...result, records: withLatestPercentFloat(result.records, snapshot?.floatShares, snapshot?.statistics) };
81
+ return { ...result, records: snapshot ? withPercentFloat(result.records, snapshot, { pointInTime }) : result.records };
76
82
  }
77
83
 
78
84
  /**
@@ -1,4 +1,5 @@
1
1
  import { realizedVolatilityCadenceIssue } from "../../../market-data/realized-volatility";
2
+ import { periodReturn } from "../../../market-data/market/period-return";
2
3
  import type { PricePoint } from "../../../types/financials";
3
4
  import type { TickerRecord } from "../../../types/ticker";
4
5
  import { isKnownNonUsListing, listingAbroadVenue } from "../../../utils/sec";
@@ -19,9 +20,6 @@ export const SHORT_WATCH_LIMIT = 60;
19
20
  const CROWDED_FLOAT_PERCENT = 10;
20
21
  const CROWDED_DAYS_TO_COVER = 5;
21
22
  const CROWDED_DAYS_MIN_FLOAT_PERCENT = 5;
22
- /** The 1M base is the close a calendar month before the latest one, or the last close up to four days before (a weekend and a holiday). */
23
- const RETURN_BASE_TOLERANCE_DAYS = 4;
24
- const DAY_MS = 86_400_000;
25
23
  const SYMBOL = /^[A-Z][A-Z0-9.]{0,9}$/;
26
24
  const LISTED_TYPES = new Set(["", "STK", "EQUITY", "COMMONSTOCK", "ADR", "ETF", "FUND"]);
27
25
 
@@ -62,10 +60,14 @@ export interface ShortWatchInputs {
62
60
  const isoDate = (date: Date) => date.toISOString().slice(0, 10);
63
61
  const finite = (value: number | null | undefined): value is number => typeof value === "number" && Number.isFinite(value);
64
62
 
63
+ /** The 1M base is the close a calendar month before the latest one, or the last close up to four days before (a weekend and a holiday). */
64
+ const RETURN_BASE_TOLERANCE_DAYS = 4;
65
+
65
66
  /**
66
- * The latest close against the close one calendar month earlier: the last close
67
- * on or before that day, within four days of it, from daily bars only, so a gap in the history leaves the
68
- * return unknown rather than measuring a longer window.
67
+ * The latest close against the close one calendar month earlier: the last
68
+ * close on or before that day (periodReturn), within four days of it, from
69
+ * daily bars only, so a gap in the history leaves the return unknown rather
70
+ * than measuring a longer window.
69
71
  */
70
72
  export function oneMonthReturn(history: readonly PricePoint[]): { percent: number; date: string } | null {
71
73
  // Weekly or intraday bars would measure from the wrong close.
@@ -73,25 +75,10 @@ export function oneMonthReturn(history: readonly PricePoint[]): { percent: numbe
73
75
  // Cached histories come back with ISO-string dates.
74
76
  const closes = history.map((point) => ({ close: point.close, date: new Date(point.date) }))
75
77
  .filter((point) => finite(point.close) && point.close > 0 && !Number.isNaN(point.date.getTime()))
76
- .sort((left, right) => left.date.getTime() - right.date.getTime());
77
- const last = closes.at(-1);
78
- if (!last) return null;
79
- const target = new Date(last.date);
80
- target.setUTCMonth(target.getUTCMonth() - 1);
81
- // 31 March less a month is 3 March; clamp to the month's last day.
82
- if (target.getUTCMonth() === last.date.getUTCMonth()) target.setUTCDate(0);
83
- const targetMs = target.getTime();
84
- let base = closes[0]!;
85
- for (let index = closes.length - 1; index >= 0; index -= 1) {
86
- const point = closes[index]!;
87
- if (point.date.getTime() <= targetMs) {
88
- base = point;
89
- break;
90
- }
91
- }
92
- if (base.date.getTime() > targetMs) return null;
93
- if (targetMs - base.date.getTime() > RETURN_BASE_TOLERANCE_DAYS * DAY_MS) return null;
94
- return { percent: (last.close / base.close - 1) * 100, date: isoDate(last.date) };
78
+ .sort((left, right) => left.date.getTime() - right.date.getTime())
79
+ .map((point) => ({ day: isoDate(point.date), close: point.close }));
80
+ const month = periodReturn(closes, "1M", null, RETURN_BASE_TOLERANCE_DAYS);
81
+ return month ? { percent: month.percent, date: month.end.day } : null;
95
82
  }
96
83
 
97
84
  function shortWatchSetup(row: Pick<ShortWatchRow, "percentFloat" | "daysToCover" | "return1M">): ShortWatchSetup | null {
@@ -18,6 +18,7 @@ import { Box, TextAttributes } from "../../../ui";
18
18
  import { formatShortDate } from "../../../utils/datetime-format";
19
19
  import { formatCompact, formatNumber } from "../../../utils/format";
20
20
  import { nextHeaderSort, type SortDirection } from "../../../utils/sort-values";
21
+ import { shortInterestPublicationSegments } from "./schedule";
21
22
  import { loadShortWatch } from "./watch-client";
22
23
  import {
23
24
  customUniverse,
@@ -103,11 +104,14 @@ export function ShortWatchPane({ width, height, focused }: Pick<PaneProps, "widt
103
104
  unpriced.length ? `No month of daily closes for ${unpriced.join(", ")}.` : null,
104
105
  ].filter((value): value is string => !!value),
105
106
  });
107
+ // When the newest settlement listed was published and the next is due, as `fn SIW` dates it.
108
+ const latestSettlement = useMemo(() => rows.flatMap((row) => row.settlementDate ? [row.settlementDate] : []).sort().at(-1) ?? null, [rows]);
106
109
  usePaneFooter("short-watch", () => ({
107
110
  info: [
108
111
  ...loadingErrorFooterInfo(resource.loading && !!resource.data, null),
112
+ ...shortInterestPublicationSegments(latestSettlement),
109
113
  ],
110
- }), [resource.loading, resource.data]);
114
+ }), [resource.loading, resource.data, latestSettlement]);
111
115
  // Dates every row shares are the table's as-of context.
112
116
  const asOf = [settled ? `settled ${day(settled)}` : null, closed ? `closes to ${day(closed)}` : null]
113
117
  .filter(Boolean).join(" · ") || undefined;
@@ -1,3 +1,4 @@
1
+ import { unavailableText } from "../../../components/ui/status";
1
2
  import { apiClient } from "../../../api-client";
2
3
  import { ApiRequestError, isAccessDenied } from "../../../api-client/errors";
3
4
  import type { SocialMentionDayPosts, SocialMentionPost, SocialMentionsPayload, SocialMentionsRange } from "../../../api-client/social-mentions";
@@ -56,7 +57,7 @@ export async function fetchSocialMentions(symbol: string, range: SocialMentionsR
56
57
  client: Pick<typeof apiClient, "getCloudSocialMentions"> = apiClient): Promise<SocialMentionsPayload> {
57
58
  try { return validateSocialMentions(await client.getCloudSocialMentions(symbol, range), symbol, range); }
58
59
  catch (error) {
59
- if (error instanceof ApiRequestError && error.status === 404) throw new Error("Social mentions are not available yet");
60
+ if (error instanceof ApiRequestError && error.status === 404) throw new Error(unavailableText("social mentions"));
60
61
  throw error;
61
62
  }
62
63
  }
@@ -12,7 +12,7 @@ import { blendHex } from "../../../theme/colors";
12
12
  import { useThemeColors } from "../../../theme/theme-context";
13
13
  import { useStaticChartBitmapSize } from "../../../components/chart/composite/bitmap";
14
14
  import { fillOpaque, fillRect, parseHex } from "../../../components/chart/native/raster/primitives";
15
- import { counterpartyKind, counterpartyLabel, disclosedValue, dollars, nativeValue, periodText, roleLabel, ROLE_COLORS, scopeWords, shareParts, type FlowBand } from "./model";
15
+ import { counterpartyKind, counterpartyLabel, disclosedValue, dollars, nativeValue, periodText, roleLabel, ROLE_COLORS, scopeWords, shareParts, fitShareBasis, type FlowBand, type ShareParts } from "./model";
16
16
  import { entityTicker } from "./graph-model";
17
17
  import { flowChipLabel, flowGeometry, RELATED_LABEL_CELLS, ribbonY, type FlowRibbon, type PositionedNode } from "./flow-layout";
18
18
  import { evidenceDate, evidenceLabel, trustTier } from "./trust";
@@ -30,19 +30,46 @@ function ribbonPolygon(ribbon: FlowRibbon, width: number, height: number, thickn
30
30
  return [...upper, ...lower];
31
31
  }
32
32
 
33
- /** The disclosed figure a card shows under the name, in the reporting company's own terms. */
34
- function nodeMetric(node: PositionedNode, focusId?: string): string {
33
+ /** A share and what it is a share of, the basis in the longest form that fits `cells`. */
34
+ function shareText(share: ShareParts, cells: number): string {
35
+ return `${share.value} ${fitShareBasis(share, cells - displayWidth(share.value) - 1)}`;
36
+ }
37
+
38
+ /** The disclosed figure a card shows under the name, in the reporting company's own terms, in `cells` cells. */
39
+ function nodeMetric(node: PositionedNode, focusId: string | undefined, cells: number): string {
35
40
  const row = node.row!;
36
41
  if (trustTier(row) !== 1) return `${evidenceLabel(row)} · ${evidenceDate(row)}`;
37
42
  if (node.weight !== null && node.weightBasis === "usd") return dollars(row);
38
43
  const share = shareParts(row, focusId);
39
- if (share) return `${share.value} ${share.basis}`;
44
+ if (share) return shareText(share, cells);
40
45
  if (nativeValue(row) !== null || row.usd !== null) return disclosedValue(row);
41
46
  // With no figure, the card says where the relationship is disclosed.
42
47
  const reporter = focusId && row.reportingEntity.id !== focusId ? `${entityTicker(row.reportingEntity) ?? row.reportingEntity.name} ` : "";
43
48
  return `${evidenceLabel(row)} · ${reporter}${row.form ?? "filing"}${row.fiscalYear ? ` · FY${row.fiscalYear}` : ""}`;
44
49
  }
45
50
 
51
+ const GROUP_SEPARATOR = " · ";
52
+ /**
53
+ * The cohort groups and their shares on one line of `cells`. A group shows
54
+ * whole or is left to the table behind "+N more", so no share stands cut off
55
+ * from what it is a share of: the first is always shown, cut to the line if it
56
+ * must be, the others keep a whole form of their basis.
57
+ */
58
+ function groupsText(groups: readonly SupplyRow[], focusId: string | undefined, cells: number): string {
59
+ const entries: string[] = [];
60
+ for (const [index, row] of groups.entries()) {
61
+ const share = shareParts(row, focusId);
62
+ const label = counterpartyLabel(row);
63
+ const rest = groups.length - index - 1;
64
+ const room = cells - displayWidth(entries.join(GROUP_SEPARATOR)) - (entries.length ? displayWidth(GROUP_SEPARATOR) : 0) - (rest ? displayWidth(`${GROUP_SEPARATOR}+${rest} more`) : 0);
65
+ const basisRoom = room - displayWidth(label) - displayWidth(share?.value ?? "") - 2;
66
+ if (entries.length && (share ? !share.forms.some((form) => displayWidth(form) <= basisRoom) : displayWidth(label) > room)) break;
67
+ entries.push(share ? `${label} ${shareText(share, room - displayWidth(label) - 1)}` : label);
68
+ }
69
+ const more = groups.length - entries.length;
70
+ return `${entries.join(GROUP_SEPARATOR)}${more > 0 ? `${GROUP_SEPARATOR}+${more} more` : ""}`;
71
+ }
72
+
46
73
  /**
47
74
  * Suppliers flow in from the left and customers out to the right of the
48
75
  * focus company. Ribbon width follows a disclosed share or dollar figure on
@@ -206,10 +233,7 @@ export function SupplyFlow({ rows, symbol, focusName, focusId, width, height, fo
206
233
  onPress={() => selectNode(node)} onHover={(on) => setHovered(on ? node.id : null)} />)}
207
234
  {geometry.groupRow !== null ? <Box position="absolute" top={geometry.groupRow} left={1} width={width - 2} flexDirection="row" overflow="hidden">
208
235
  <Box width={RELATED_LABEL_CELLS} flexShrink={0}><Text fg={colors.textDim}>Groups</Text></Box>
209
- <ClippedText fg={colors.textDim} text={geometry.groups.map((row) => {
210
- const share = shareParts(row, focusId);
211
- return `${counterpartyLabel(row)}${share ? ` ${share.value} ${share.basis}` : ""}`;
212
- }).join(" · ")} width={width - RELATED_LABEL_CELLS - 3} />
236
+ <ClippedText fg={colors.textDim} text={groupsText(geometry.groups, focusId, width - RELATED_LABEL_CELLS - 3)} width={width - RELATED_LABEL_CELLS - 3} />
213
237
  </Box> : null}
214
238
  {legend ? <Box position="absolute" top={geometry.legendRow} left={1} width={width - 2} flexDirection="row" gap={2} overflow="hidden">
215
239
  {legend.map((entry) => <Box key={entry.id} flexDirection="row" gap={1} flexShrink={0}>
@@ -277,9 +301,9 @@ function FlowCard({ node, focusId, desktop, active, onPress, onHover }: {
277
301
  const accent = role ? ROLE_COLORS[role] : colors.textMuted;
278
302
  const company = node.row ? counterpartyKind(node.row) === "company" : false;
279
303
  const name = node.more ? `+${node.more} more` : node.row ? counterpartyLabel(node.row) : node.label;
280
- const metric = node.row ? nodeMetric(node, focusId) : "";
281
304
  const ticker = node.row ? entityTicker(node.row.counterparty) : null;
282
305
  const inner = Math.max(4, node.width - (desktop ? 2 : 2));
306
+ const metric = node.row ? nodeMetric(node, focusId, inner - (desktop ? 0 : 1)) : "";
283
307
  const nameWidth = Math.max(4, inner - (ticker ? displayWidth(ticker) + 1 : 0));
284
308
  const titleColor = node.more ? colors.textDim : company ? colors.textBright : colors.text;
285
309
  return <Box position="absolute" left={node.x} top={node.y} width={node.width} height={node.more ? 1 : node.height} flexDirection="column"
@@ -1,5 +1,6 @@
1
1
  import type { SupplyChainPayload, SupplyOptions, SupplyRole, SupplyRow } from "../../../api-client/supply-chain";
2
2
  import { SERIES_COLORS } from "../../../theme/series-colors";
3
+ import { clipToDisplayWidth, displayWidth } from "../../../utils/format";
3
4
  import { revenueAmount } from "../revenue-breakdown/model";
4
5
  import { countOf, freePreviewNote } from "../shared/free-preview";
5
6
  import { entityTicker } from "./graph-model";
@@ -37,24 +38,46 @@ export function scopeWords(scope: string): string {
37
38
  .replace(/^non[\s-]?us$/i, "non-US").trim();
38
39
  }
39
40
 
41
+ /** A disclosed share and what it is a share of, in words, and the same basis in shorter forms for a column that cannot hold the whole. */
42
+ export interface ShareParts {
43
+ value: string;
44
+ basis: string;
45
+ /** The basis from its whole wording to its bare form, longest first, `basis` first. Never empty: a share always keeps what it is a share of. */
46
+ forms: readonly string[];
47
+ }
48
+
40
49
  /**
41
50
  * A disclosed share and what it is a share of, in words. The denominator is
42
51
  * always the reporting company's: a supplier saying the focus company is 91%
43
52
  * of its revenue reads "of CRUS revenue", never as the focus company's.
44
53
  */
45
- export function shareParts(row: SupplyRow, focusId?: string | null, { includeReporter = false }: { includeReporter?: boolean } = {}): { value: string; basis: string } | null {
54
+ export function shareParts(row: SupplyRow, focusId?: string | null, { includeReporter = false }: { includeReporter?: boolean } = {}): ShareParts | null {
46
55
  if (row.pctOfRevenue === null || row.pctBasis === null || trustTier(row) !== 1 || isUnconfirmed(row) || !activeRelationship(row)) return null;
47
56
  const reporter = includeReporter || (focusId && row.reportingEntity.id !== focusId) ? `${entityTicker(row.reportingEntity) ?? row.reportingEntity.name} ` : "";
48
57
  const scope = row.pctScope ? scopeWords(row.pctScope) : "";
49
58
  const basis = row.pctBasis === "cost" ? "cost of sales" : row.pctBasis;
59
+ const value = `${Number(row.pctOfRevenue.toFixed(1))}%`;
50
60
  // XBRL dimensions can qualify the numerator, denominator, or both. NVDA's
51
61
  // NonUs fact is a share of total revenue, while TW omits a Data Center qualifier.
52
62
  // Neither supports turning its dimensional member into an "of ..." claim.
53
- if (row.sourceKind === "xbrl" && scope) return { value: `${Number(row.pctOfRevenue.toFixed(1))}%`, basis: `${reporter}${basis} · context: ${scope}` };
63
+ if (row.sourceKind === "xbrl" && scope) return shareWords(value, [`${reporter}${basis} · context: ${scope}`, `${reporter}${basis}`]);
54
64
  // A scope that names its own denominator ("Vendor non-trade receivables") is a description, read in lower case.
55
65
  const scoped = scope && scope.toLowerCase().includes(basis) ? scope.replace(/^[A-Z](?=[a-z])/, (first) => first.toLowerCase()) : `${scope ? `${scope} ` : ""}${basis}`;
56
66
  // Form alone does not identify a metric's duration: a 10-Q includes YTD facts.
57
- return { value: `${Number(row.pctOfRevenue.toFixed(1))}%`, basis: `of ${reporter}${scoped}` };
67
+ return shareWords(value, [`of ${reporter}${scoped}`, `of ${reporter}${basis}`, `${reporter}${basis}`]);
68
+ }
69
+ const shareWords = (value: string, forms: readonly string[]): ShareParts => {
70
+ const distinct = forms.filter((form, index) => forms.indexOf(form) === index);
71
+ return { value, basis: distinct[0]!, forms: distinct };
72
+ };
73
+
74
+ /**
75
+ * The longest form of a share's basis that fits `cells`, else its bare form cut
76
+ * short behind an ellipsis: a share keeps what it is a share of, in whatever
77
+ * room there is. Empty only when there is no room at all.
78
+ */
79
+ export function fitShareBasis(share: Pick<ShareParts, "forms">, cells: number): string {
80
+ return share.forms.find((form) => displayWidth(form) <= cells) ?? clipToDisplayWidth(share.forms[share.forms.length - 1]!, cells);
58
81
  }
59
82
  /** An XBRL fact's tagged dimensions, as the filing reader stores them in place of a sentence: "ConcentrationRiskByType=CustomerConcentrationRisk;MajorCustomers=StandardChartered;". */
60
83
  const TAGGED_DIMENSIONS = /^\s*[A-Za-z][\w.:-]*=[^;=]*(?:;\s*[A-Za-z][\w.:-]*=[^;=]*)*;?\s*$/;
@@ -193,6 +216,31 @@ export function flowGroups(rows: readonly SupplyRow[]): SupplyRow[] {
193
216
  .sort((a, b) => (b.pctOfRevenue ?? -1) - (a.pctOfRevenue ?? -1));
194
217
  }
195
218
 
219
+ export interface ConcentrationPoint { period: string; date: Date; pct: number; rowId: string }
220
+ /**
221
+ * The largest single customer's share of the focus company's receivables at
222
+ * each period end it disclosed, oldest first. Receivables are a balance at a
223
+ * date, so periods compare; revenue shares are not trended, because a period
224
+ * label does not establish whether a share covers a quarter, a half or a year.
225
+ * Only exact period-end dates count, and a share above 100% is not a share. Fewer than three periods is no trend, and none is returned.
226
+ */
227
+ export function receivablesConcentration(rows: readonly SupplyRow[], focusId: string | null | undefined): ConcentrationPoint[] {
228
+ if (!focusId) return [];
229
+ const largest = new Map<string, ConcentrationPoint>();
230
+ for (const row of rows) {
231
+ if (row.role !== "customer" || row.pctBasis !== "receivables" || row.pctScope !== null || row.pctOfRevenue === null || !Number.isFinite(row.pctOfRevenue) || row.pctOfRevenue < 0 || row.pctOfRevenue > 100 || row.counterparty.aggregate || row.entityScope === "group"
232
+ || !PERIOD_END.test(row.period) || row.reportingEntity.id !== focusId || !shareParts(row, focusId)) continue;
233
+ const date = new Date(`${row.period}T00:00:00Z`);
234
+ if (!Number.isFinite(date.getTime()) || date.toISOString().slice(0, 10) !== row.period) continue;
235
+ const held = largest.get(row.period);
236
+ if (!held || row.pctOfRevenue > held.pct) largest.set(row.period, { period: row.period, date, pct: row.pctOfRevenue, rowId: row.id });
237
+ }
238
+ const points = [...largest.values()].filter((point) => Number.isFinite(point.date.getTime())).sort((a, b) => a.period.localeCompare(b.period));
239
+ return points.length >= 3 ? points : [];
240
+ }
241
+ /** "2026-Q1", "FY2025", "2026" or "ongoing" name no balance-sheet date. */
242
+ const PERIOD_END = /^\d{4}-\d{2}-\d{2}$/;
243
+
196
244
  export type FlowBand = "suppliers" | "customers" | "related";
197
245
  export interface FlowNode { id: string; label: string; row: SupplyRow | null; band: FlowBand; more: number; weight: number | null; weightScope: string | null; weightBasis: "revenue" | "usd" | null; }
198
246
  const revenueKey = (row: SupplyRow) => JSON.stringify([row.reportingEntity.id, row.period, row.pctScope]);