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
@@ -1,27 +1,35 @@
1
- import { useCallback, useEffect, useMemo, useRef } from "react";
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import {
3
3
  DataTableStackView,
4
+ PageStackView,
4
5
  PaneStatusBody,
5
6
  QueryBar,
6
7
  usePaneFooter,
7
8
  usePaneMenuItems,
8
9
  usePaneStatusFooter,
9
10
  type DataTableKeyEvent,
11
+ type QueryBarFilter,
12
+ type QueryBarView,
10
13
  } from "../../../components";
11
- import { handleRefreshKey } from "../../../components/data-table/table-pane";
14
+ import { handleRefreshKey, usePaneRefreshKey } from "../../../components/data-table/table-pane";
15
+ import { openFormModal } from "../../../components/form-modal";
16
+ import { FILL_REST } from "../../../components/ui/fill";
12
17
  import type { EarningsCalendarQuery } from "../../../api-client/earnings";
13
- import { useAppSelector, usePaneSettingValue } from "../../../state/app/context";
18
+ import { useAppSelector, usePaneAppConfig, usePaneSettingValue } from "../../../state/app/context";
14
19
  import { useLiveStreamingSetting } from "../../../state/hooks/live-streaming";
15
20
  import { useAsyncResource, useAutoRefresh, usePluginPaneState, useUpdatedAgo } from "../../../public/react";
16
21
  import type { EarningsEvent } from "../../../types/data-provider";
22
+ import { getSharedRegistry } from "../../registry/shared";
23
+ import { Box, useUiCapabilities } from "../../../ui";
17
24
  import { useAssetData, usePluginAppActions, usePluginTickerActions } from "../../runtime";
25
+ import { earningsResultsAlertValues } from "../alerts/research-builder";
26
+ import { cachedMembers, loadMembers } from "../members/client";
18
27
  import { isBoardRowLoading, useQuoteBoard } from "../shared/use-quote-board";
19
28
  import {
20
29
  addDays,
21
30
  boardReport,
22
31
  boardRows,
23
32
  fallbackReport,
24
- marketDays,
25
33
  newYorkToday,
26
34
  ownershipBySymbol,
27
35
  relativeDays,
@@ -31,9 +39,36 @@ import {
31
39
  type BoardSort,
32
40
  } from "./board-model";
33
41
  import { boardColumns, boardHasActuals, renderBoardCell, renderBoardSection, SORTABLE, type BoardCellContext, type BoardColumn } from "./board-table";
34
- import { cachedEarningsCalendar, loadEarningsBoard } from "./client";
42
+ import { EarningsCalendarView, CALENDAR_GROUP_ROWS, calendarSlotsPerRow } from "./calendar";
43
+ import {
44
+ boardRangeDays,
45
+ boardRangeLabel,
46
+ boardRangeWeek,
47
+ boardRangeWords,
48
+ CALENDAR_WEEK_OFFSETS,
49
+ calendarDays,
50
+ calendarGroupKey,
51
+ calendarWeekDates,
52
+ calendarWeekLabel,
53
+ isBoardRangeDay,
54
+ requestedBoardRange,
55
+ type BoardRange,
56
+ } from "./calendar-model";
57
+ import { cachedEarningsCalendar, earningsCalendarKey, loadEarningsBoard } from "./client";
35
58
  import { loadEarningsCalendar } from "./data/cache";
36
59
  import { EarningsHistoryView } from "./history";
60
+ import {
61
+ ALL_SCOPE,
62
+ earningsScopeOptions,
63
+ MINE_SCOPE,
64
+ readEarningsScope,
65
+ SECTOR_HOLDINGS_FUND,
66
+ sectorSymbols,
67
+ type EarningsScope,
68
+ } from "./scope";
69
+
70
+ const NO_SYMBOLS: string[] = [];
71
+ const NONE_EXPANDED: ReadonlySet<string> = new Set();
37
72
 
38
73
  /** The stored calendar reaches about three months ahead. */
39
74
  const HORIZON_DAYS = 90;
@@ -42,9 +77,21 @@ const MARKET_PER_DAY = 200;
42
77
  /** The server reads at most this many named companies. */
43
78
  const MAX_SYMBOLS = 200;
44
79
 
80
+ const MARKET_SCOPE: EarningsScope = { kind: "all" };
81
+ const loadSectorHoldings = (force: boolean) => loadMembers(SECTOR_HOLDINGS_FUND, force);
82
+ /** A sector fund's companies the board reads, its largest first. */
83
+ const sectorNames = (...args: Parameters<typeof sectorSymbols>) => sectorSymbols(...args).slice(0, MAX_SYMBOLS);
84
+
45
85
  type Order = "cap" | "mine";
86
+ type BoardView = "table" | "calendar";
87
+
88
+ const ORDER_OPTIONS: ReadonlyArray<{ value: Order; label: string }> = [{ value: "cap", label: "Size" }, { value: "mine", label: "Mine first" }];
89
+ /** The week as logos is for the desktop and the web; the terminal draws no logos and keeps the table. */
90
+ const ERN_VIEWS: ReadonlyArray<{ value: BoardView; label: string }> = [{ value: "table", label: "Table" }, { value: "calendar", label: "Calendar" }];
46
91
 
47
92
  interface BoardData {
93
+ /** The query read, by `earningsCalendarKey`: kept data from another week is not this week's. */
94
+ queryKey: string;
48
95
  reports: BoardReport[];
49
96
  stale: boolean;
50
97
  refreshError: string | null;
@@ -53,7 +100,8 @@ interface BoardData {
53
100
  /**
54
101
  * ERN without a ticker: the market's report days, largest companies first,
55
102
  * with the implied move and each company's average move. With tickers, or the
56
- * Mine filter, the upcoming reports of those companies instead.
103
+ * Names filter (Mine, a portfolio, a watchlist or a sector fund's holdings),
104
+ * the upcoming reports of those companies instead.
57
105
  */
58
106
  export function EarningsBoard({ focused, width, height, scopedSymbols, highlight = null }: {
59
107
  focused: boolean;
@@ -66,7 +114,7 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
66
114
  }) {
67
115
  const dataProvider = useAssetData();
68
116
  const { navigateTicker } = usePluginTickerActions();
69
- const { createPaneFromTemplate } = usePluginAppActions();
117
+ const { createPaneFromTemplate, notify } = usePluginAppActions();
70
118
  usePaneMenuItems("earnings:catalysts", () => [{ id: "catalysts", label: "Catalyst calendar", onSelect: () => createPaneFromTemplate("catalysts-pane") }], [createPaneFromTemplate]);
71
119
  const tickers = useAppSelector((state) => state.tickers);
72
120
  // Ticker records change for many reasons; the board reloads only when the held and watched names do.
@@ -78,60 +126,128 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
78
126
  () => new Map(ownedKey ? ownedKey.split(",").map((entry) => entry.split(":") as [string, "held" | "watched"]) : []),
79
127
  [ownedKey],
80
128
  );
81
- const [mineOnly, setMineOnly] = usePluginPaneState<boolean>("mineOnly", false);
129
+ // Before the Names filter, a Mine toggle; a layout saved with it on opens on Mine.
130
+ const [mineOnly] = usePluginPaneState<boolean>("mineOnly", false);
131
+ const [savedScope, setScope] = usePluginPaneState<string | null>("scope", null);
82
132
  const [order, setOrder] = usePluginPaneState<Order>("order", "cap");
83
133
  const [sort, setSort] = usePluginPaneState<BoardSort>("sort", { column: "cap", direction: "desc" });
84
134
  // Keyed by symbol and date, so a reload or a shared layout keeps the row.
85
135
  const [selectedKey, setSelectedKey] = usePluginPaneState<string | null>("selectedKey", null);
86
136
  const [openKey, setOpenKey] = usePluginPaneState<string | null>("openEvent", null);
87
- // A layout or `gloomberb shot ERN NVDA,AMD --open NVDA` lands on that company's history.
137
+ // A layout or `gloom shot ERN NVDA,AMD --open NVDA` lands on that company's history.
88
138
  const [openSymbol] = usePaneSettingValue<string>("open", "");
139
+ // A layout's `view` setting opens on the week; a choice made in the pane wins.
140
+ const [requestedView] = usePaneSettingValue<string>("view", "");
141
+ const [chosenView, setView] = usePluginPaneState<BoardView | null>("view", null);
142
+ // `EVTS --week last` or `--date 2026-10-15` opens on those days; a choice in the bar's Range wins. One
143
+ // range for both views: the table draws its days, the calendar its week (this week for the days ahead).
144
+ const [requestedDate] = usePaneSettingValue<string>("date", "");
145
+ const [requestedWeek] = usePaneSettingValue<string>("week", "");
146
+ const [range, setRange] = usePluginPaneState<BoardRange>("range", requestedBoardRange(requestedDate, requestedWeek));
147
+ // The calendar's groups opened past their "+N more" slot, by `calendarGroupKey`.
148
+ const [expanded, setExpanded] = useState(NONE_EXPANDED);
149
+ const desktop = useUiCapabilities().nativePaneChrome === true;
150
+ const config = usePaneAppConfig();
151
+ const scopeOptions = useMemo(
152
+ () => earningsScopeOptions({ portfolios: config.portfolios, watchlists: config.watchlists }),
153
+ [config.portfolios, config.watchlists],
154
+ );
155
+ const chosenScope = readEarningsScope(savedScope ?? (mineOnly ? MINE_SCOPE : ALL_SCOPE), scopeOptions);
156
+ // The terminal keeps its Mine toggle, so a choice only the desktop offers reads as the market there.
157
+ const scopeOption = desktop || chosenScope.value === MINE_SCOPE ? chosenScope : scopeOptions[0]!;
158
+ const scoped = scopedSymbols.length > 0;
159
+ const scope = scoped ? MARKET_SCOPE : scopeOption.scope;
160
+ const view: BoardView = !desktop ? "table"
161
+ : chosenView ?? (ERN_VIEWS.some((option) => option.value === requestedView) ? requestedView as BoardView : "table");
162
+ const calendar = view === "calendar";
89
163
  const openedSymbol = useRef<string | null>(null);
90
164
  const highlighted = useRef<string | null>(null);
91
165
 
92
166
  const today = newYorkToday();
93
- const market = scopedSymbols.length === 0 && !mineOnly;
94
- const named = useMemo(
95
- () => (scopedSymbols.length > 0 ? [...scopedSymbols] : [...owned.keys()]).slice(0, MAX_SYMBOLS),
96
- [owned, scopedSymbols],
97
- );
98
- const days = useMemo(() => (market ? marketDays(today) : relativeDays(today)), [market, today]);
99
- const query = useMemo<EarningsCalendarQuery>(() => market
100
- ? { from: days[0]!.from, to: days.at(-1)!.to, perDay: MARKET_PER_DAY, symbols: named }
101
- : { from: today, to: addDays(today, HORIZON_DAYS), perDay: 0, symbols: named },
102
- [days, market, named, today]);
167
+ const market = !scoped && scope.kind === "all";
168
+ // Keyed by content: ticker records change for many reasons, the board reloads only when the list's names do.
169
+ const collectionKey = useMemo(() => {
170
+ if (scope.kind !== "portfolio" && scope.kind !== "watchlist") return "";
171
+ return [...tickers.values()]
172
+ .filter(({ metadata }) => (scope.kind === "portfolio" ? metadata.portfolios : metadata.watchlists).includes(scope.id))
173
+ .map(({ metadata }) => metadata.ticker.trim().toUpperCase())
174
+ .sort()
175
+ .join(",");
176
+ }, [scope, tickers]);
177
+ const sector = scope.kind === "sector" ? scope : null;
178
+ // A sector fund's companies come with its holdings, which the loader reads; the others are known here.
179
+ const named = useMemo(() => {
180
+ const names = scoped ? [...scopedSymbols]
181
+ : scope.kind === "portfolio" || scope.kind === "watchlist" ? (collectionKey ? collectionKey.split(",") : [])
182
+ : scope.kind === "sector" ? [] : [...owned.keys()];
183
+ return names.slice(0, MAX_SYMBOLS);
184
+ }, [collectionKey, owned, scope.kind, scoped, scopedSymbols]);
185
+ // Named companies also go to the per-ticker calendar, which covers listings the stored one does not;
186
+ // a sector fund's are S&P 500 companies, all in the stored one.
187
+ const perTicker = !market && !sector;
188
+ // The days ahead by default; a chosen week or day, past or ahead, for the market and for named companies alike.
189
+ const days = useMemo(() => (market || range ? boardRangeDays(range, today) : relativeDays(today)), [market, range, today]);
190
+ const weekOffset = boardRangeWeek(range, today);
191
+ const week = useMemo(() => calendarWeekDates(today, weekOffset), [today, weekOffset]);
192
+ // The calendar reads its week, reported days included; the table its days.
193
+ const query = useMemo<EarningsCalendarQuery>(() => calendar
194
+ ? { from: week[0]!, to: week.at(-1)!, perDay: market ? MARKET_PER_DAY : 0, symbols: named }
195
+ : market || range
196
+ ? { from: days[0]!.from, to: days.at(-1)!.to, perDay: market ? MARKET_PER_DAY : 0, symbols: named }
197
+ : { from: today, to: addDays(today, HORIZON_DAYS), perDay: 0, symbols: named },
198
+ [calendar, days, market, named, range, today, week]);
199
+ // A sector fund's read is named by the fund: its companies are known once its holdings are.
200
+ const queryKey = sector ? `${sector.etf}|${earningsCalendarKey(query)}` : earningsCalendarKey(query);
103
201
 
104
202
  const loader = useCallback(async (force: boolean): Promise<BoardData> => {
105
- if (!market && named.length === 0) return { reports: [], stale: false, refreshError: null };
106
- // Named companies also go to the per-ticker calendar, which covers listings the stored one does not.
203
+ const names = sector ? sectorNames((await loadSectorHoldings(force)).payload.members, sector.sectors) : named;
204
+ if (!market && names.length === 0) return { queryKey, reports: [], stale: false, refreshError: null };
205
+ const read = sector ? { ...query, symbols: names } : query;
107
206
  const [cloud, fallback] = await Promise.allSettled([
108
- loadEarningsBoard(query, force),
109
- market ? Promise.resolve([] as EarningsEvent[]) : loadEarningsCalendar(dataProvider, named, { force }).then((result) => result.events),
207
+ loadEarningsBoard(read, force),
208
+ !perTicker ? Promise.resolve([] as EarningsEvent[]) : loadEarningsCalendar(dataProvider, names, { force }).then((result) => result.events
209
+ // The per-ticker calendar is upcoming reports; a week or a chosen range keeps those in it.
210
+ .filter((event) => (!calendar && !range) || (event.earningsDate.toISOString().slice(0, 10) >= read.from && event.earningsDate.toISOString().slice(0, 10) <= read.to))),
110
211
  ]);
111
- if (cloud.status === "rejected" && (market || fallback.status === "rejected")) throw cloud.reason;
212
+ if (cloud.status === "rejected" && (!perTicker || fallback.status === "rejected")) throw cloud.reason;
112
213
  const reports = cloud.status === "fulfilled" ? cloud.value.payload.reports.map((report) => boardReport(report, owned)) : [];
113
214
  const covered = new Set(reports.map((report) => report.symbol));
114
215
  const extra = fallback.status === "fulfilled"
115
- ? fallback.value.filter((event) => !covered.has(event.symbol)).map((event) => fallbackReport(event, owned))
216
+ ? fallback.value.filter((event) => !covered.has(event.symbol)).map((event) => fallbackReport(event, owned, today))
116
217
  : [];
117
218
  return {
219
+ queryKey,
118
220
  reports: [...reports, ...extra],
119
221
  stale: cloud.status === "fulfilled" && cloud.value.stale,
120
222
  refreshError: cloud.status === "rejected"
121
223
  ? (cloud.reason instanceof Error ? cloud.reason.message : String(cloud.reason))
122
224
  : cloud.value.refreshError,
123
225
  };
124
- }, [dataProvider, market, named, owned, query]);
226
+ }, [calendar, dataProvider, market, named, owned, perTicker, query, queryKey, range, sector, today]);
125
227
  const resource = useAsyncResource(loader, {
126
228
  keepPreviousData: true,
127
229
  initialData: () => {
128
- const cached = cachedEarningsCalendar(query);
129
- return cached ? { reports: cached.payload.reports.map((report) => boardReport(report, owned)), stale: false, refreshError: null } : null;
230
+ const holdings = sector ? cachedMembers(SECTOR_HOLDINGS_FUND) : null;
231
+ if (sector && !holdings) return null;
232
+ const cached = cachedEarningsCalendar(sector && holdings ? { ...query, symbols: sectorNames(holdings.payload.members, sector.sectors) } : query);
233
+ return cached ? { queryKey, reports: cached.payload.reports.map((report) => boardReport(report, owned)), stale: false, refreshError: null } : null;
130
234
  },
131
235
  });
132
236
  const data = resource.data;
237
+ // The week on screen is only the one read for it, never the last week's kept while this one loads or after it failed.
238
+ const weekData = data?.queryKey === queryKey ? data : null;
133
239
  const rows = useMemo(() => boardRows(data?.reports ?? [], days, sort, order === "mine"), [data, days, order, sort]);
134
240
  const reportRows = useMemo(() => rows.filter((row): row is BoardRow & { kind: "report" } => row.kind === "report"), [rows]);
241
+ const slotsPerRow = calendarSlotsPerRow(width);
242
+ const weekDays = useMemo(
243
+ () => (calendar
244
+ ? calendarDays(weekData?.reports ?? [], week, { today, mineFirst: order === "mine", limit: slotsPerRow * CALENDAR_GROUP_ROWS, expanded })
245
+ : []),
246
+ [calendar, expanded, order, slotsPerRow, today, week, weekData],
247
+ );
248
+ const selectedReport = calendar
249
+ ? weekData?.reports.find((report) => report.key === selectedKey) ?? null
250
+ : (reportRows.find((row) => row.key === selectedKey) ?? reportRows[0] ?? null)?.report ?? null;
135
251
  const selected = reportRows.find((row) => row.key === selectedKey) ?? reportRows[0] ?? null;
136
252
  const open = reportRows.find((row) => row.key === openKey)?.report
137
253
  ?? (openKey ? data?.reports.find((report) => report.key === openKey) : undefined) ?? null;
@@ -144,14 +260,16 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
144
260
  .map((row) => row.report.symbol))].join(",");
145
261
  const reportedSymbols = useMemo(() => (reportedKey ? reportedKey.split(",") : []), [reportedKey]);
146
262
  const liveStreaming = useLiveStreamingSetting();
147
- const { quotes } = useQuoteBoard(reportedSymbols, { liveStreaming, selectedSymbol: selected?.report.symbol ?? null });
263
+ const { quotes } = useQuoteBoard(calendar ? NO_SYMBOLS : reportedSymbols, { liveStreaming, selectedSymbol: calendar ? null : selected?.report.symbol ?? null });
148
264
  const quotesRef = useRef(quotes);
149
265
  quotesRef.current = quotes;
150
266
  const statusShown = columns.some((column) => column.id === "status");
267
+ const logos = desktop;
151
268
  // Stable while the quotes tick: each row redraws by its own quote's version.
152
269
  const renderCell = useCallback((row: BoardRow, column: BoardColumn) => {
153
270
  const context: BoardCellContext = {
154
271
  statusShown,
272
+ logos,
155
273
  move: (report) => {
156
274
  const state = quotesRef.current.get(report.symbol);
157
275
  const move = reportMove(report, state?.quote, today);
@@ -159,7 +277,7 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
159
277
  },
160
278
  };
161
279
  return renderBoardCell(row, column, context);
162
- }, [statusShown, today]);
280
+ }, [logos, statusShown, today]);
163
281
  const rowVersion = useCallback((row: BoardRow) => {
164
282
  if (row.kind !== "report") return row.key;
165
283
  const state = quotes.get(row.report.symbol);
@@ -193,7 +311,9 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
193
311
  stale: !!data?.stale,
194
312
  info: data && updatedAgo ? [{ id: "updated", parts: [{ text: updatedAgo, tone: "muted" as const }] }] : [],
195
313
  });
196
- const symbol = open?.symbol ?? selected?.report.symbol ?? null;
314
+ const symbol = open?.symbol ?? selectedReport?.symbol ?? null;
315
+ // The desktop and the web only: the terminal's footer stays as it was.
316
+ const alerts = desktop && getSharedRegistry()?.commands.has("set-event-alert") === true;
197
317
  usePaneFooter("earnings-actions", () => ({
198
318
  order: 10,
199
319
  hints: symbol
@@ -203,27 +323,112 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
203
323
  { id: "estimates", key: "e", label: "stimates", onPress: () => createPaneFromTemplate("earnings-estimates-pane", { symbol }) },
204
324
  { id: "calls", key: "c", label: "alls", onPress: () => createPaneFromTemplate("earnings-calls-pane", { symbol }) },
205
325
  { id: "analysts", key: "a", label: "nalysts", onPress: () => createPaneFromTemplate("analyst-research-pane", { symbol }) },
326
+ // ALRT's earnings alert, set to go off when the results are out.
327
+ ...(alerts ? [{ id: "alert", key: "n", label: "otify", title: "Earnings alert", onPress: () => {
328
+ if (!openEarningsAlert(symbol)) notify({ body: "Open this from the main window.", type: "info" });
329
+ } }] : []),
206
330
  ]
207
331
  : [],
208
- }), [createPaneFromTemplate, navigateTicker, symbol]);
332
+ }), [alerts, createPaneFromTemplate, navigateTicker, notify, symbol]);
209
333
 
210
- // t, e, c and a are footer hints, which bind their own keys.
334
+ // t, e, c, a and n are footer hints, which bind their own keys.
211
335
  const handleKeyDown = useCallback((event: DataTableKeyEvent) => handleRefreshKey(event, () => void resource.reload()), [resource.reload]);
336
+ usePaneRefreshKey(() => void resource.reload(), { focused, enabled: calendar && !open });
212
337
 
213
338
  const sortColumn = (Object.entries(SORTABLE).find(([, value]) => value === sort.column)?.[0]) ?? null;
214
- const queryBar = (
339
+ const mineFilter: QueryBarFilter[] = scoped ? [] : [
340
+ { id: "mine", kind: "toggle", label: "Mine", value: scope.kind === "mine", defaultValue: false, onChange: (mine: boolean) => setScope(mine ? MINE_SCOPE : ALL_SCOPE) },
341
+ ];
342
+ // The desktop's Mine is one choice of the Names filter, beside each portfolio, watchlist and sector fund.
343
+ const namesFilter: QueryBarFilter[] = scoped ? [] : [{
344
+ id: "names",
345
+ label: "Names",
346
+ value: scopeOption.value,
347
+ defaultValue: ALL_SCOPE,
348
+ options: scopeOptions.map(({ value, label, description }) => ({ value, label, description })),
349
+ onChange: (value: string) => setScope(value),
350
+ }];
351
+ const orderView: QueryBarView<Order> = { value: order, options: ORDER_OPTIONS, onChange: setOrder };
352
+ // The weeks the bar offers, last week to three ahead, and the one on screen when it is another.
353
+ const weekOptions = (shown: number | null) => [...new Set([...CALENDAR_WEEK_OFFSETS, ...(shown == null ? [] : [shown])])]
354
+ .sort((left, right) => left - right)
355
+ .map((offset) => ({ value: String(offset), label: calendarWeekLabel(today, offset) }));
356
+ // One Range for both views. The table's: the days ahead, a week, or a day asked for (`--date`), which stays a
357
+ // choice once the bar moves off it. The calendar's: the week it draws, a day's own week.
358
+ const rangeDates = [...new Set([requestedDate, range].filter(isBoardRangeDay))];
359
+ const rangeFilter: QueryBarFilter = calendar
360
+ ? { id: "range", label: "Range", value: String(weekOffset), options: weekOptions(weekOffset), onChange: setRange }
361
+ : {
362
+ id: "range",
363
+ label: "Range",
364
+ value: range,
365
+ defaultValue: "",
366
+ options: [
367
+ { value: "", label: "Upcoming" },
368
+ ...weekOptions(range && !isBoardRangeDay(range) ? weekOffset : null),
369
+ ...rangeDates.map((date) => ({ value: date, label: boardRangeLabel(date, today)! })),
370
+ ],
371
+ onChange: (value: string) => { setRange(value); setSelectedKey(null); },
372
+ };
373
+ // On the desktop the view picks the table or the week, and the order is a filter beside the names and the Range.
374
+ const queryBar = !desktop ? <QueryBar width={width} filters={[...mineFilter, rangeFilter]} view={orderView} /> : (
215
375
  <QueryBar
216
376
  width={width}
217
- filters={scopedSymbols.length > 0 ? [] : [
218
- { id: "mine", kind: "toggle", label: "Mine", value: mineOnly, defaultValue: false, onChange: setMineOnly },
377
+ filters={[
378
+ ...namesFilter,
379
+ rangeFilter,
380
+ { id: "order", label: "Order", inline: true, value: order, options: ORDER_OPTIONS, onChange: setOrder },
219
381
  ]}
220
- view={{
221
- value: order,
222
- options: [{ value: "cap", label: "Size" }, { value: "mine", label: "Mine first" }],
223
- onChange: (value: Order) => setOrder(value),
224
- }}
382
+ view={{ value: view, options: ERN_VIEWS, onChange: setView }}
225
383
  />
226
384
  );
385
+ // "last week", "on Thu Oct 15", "in the week of Nov 2": the days the empty board or week is of.
386
+ const rangeWords = calendar ? boardRangeWords(String(weekOffset), today) : boardRangeWords(range, today);
387
+ const emptyStateTitle = market ? `No reports on the calendar ${rangeWords ?? "these days"}.`
388
+ : scope.kind === "mine" && named.length === 0 ? "No portfolio or watchlist names."
389
+ : (scope.kind === "portfolio" || scope.kind === "watchlist") && named.length === 0 ? `No tickers in ${scope.name}.`
390
+ : rangeWords ? `No reports from these companies ${rangeWords}.` : "No upcoming reports.";
391
+ const history = open ? (
392
+ <EarningsHistoryView key={open.symbol} symbol={open.symbol} width={width} height={Math.max(4, height - 1)}
393
+ focused={focused} registrationId="earnings-history" />
394
+ ) : null;
395
+
396
+ if (calendar) {
397
+ // The query bar stays up while a week loads or fails, so another week or the table is a click away.
398
+ return (
399
+ <PageStackView
400
+ focused={focused}
401
+ detailOpen={!!open}
402
+ onBack={() => setOpenKey(null)}
403
+ detailTitle={open ? `${open.symbol} · ${open.name}` : undefined}
404
+ rootContent={(
405
+ <Box flexDirection="column" {...FILL_REST}>
406
+ {queryBar}
407
+ <EarningsCalendarView
408
+ days={weekDays}
409
+ width={width}
410
+ focused={focused && !open}
411
+ selectedKey={selectedKey}
412
+ onSelect={setSelectedKey}
413
+ onOpen={(report) => setOpenKey(report.key)}
414
+ onMore={(day, group) => {
415
+ // The group draws in full, the cursor on the first company its last slot counted.
416
+ const all = calendarDays(weekData?.reports ?? [], [day.date], { today, mineFirst: order === "mine" })[0]
417
+ ?.groups.find((entry) => entry.session === group.session);
418
+ setExpanded((current) => new Set(current).add(calendarGroupKey(day.date, group.session)));
419
+ const next = all?.reports[group.reports.length];
420
+ if (next) setSelectedKey(next.key);
421
+ }}
422
+ loading={!weekData && !resource.error}
423
+ error={weekData ? null : resource.error}
424
+ emptyTitle={emptyStateTitle}
425
+ />
426
+ </Box>
427
+ )}
428
+ detailContent={history}
429
+ />
430
+ );
431
+ }
227
432
 
228
433
  return (
229
434
  <PaneStatusBody loading={resource.loading && !data} error={!data ? resource.error : null} subject="the earnings calendar">
@@ -257,16 +462,14 @@ export function EarningsBoard({ focused, width, height, scopedSymbols, highlight
257
462
  detailOpen={!!open}
258
463
  onBack={() => setOpenKey(null)}
259
464
  detailTitle={open ? `${open.symbol} · ${open.name}` : undefined}
260
- detailContent={open ? (
261
- <EarningsHistoryView key={open.symbol} symbol={open.symbol} width={width} height={Math.max(4, height - 1)}
262
- focused={focused} registrationId="earnings-history" />
263
- ) : null}
264
- emptyStateTitle={
265
- market ? "No reports on the calendar these days."
266
- : named.length === 0 ? "No portfolio or watchlist names."
267
- : "No upcoming reports."
268
- }
465
+ detailContent={history}
466
+ emptyStateTitle={emptyStateTitle}
269
467
  />
270
468
  </PaneStatusBody>
271
469
  );
272
470
  }
471
+
472
+ /** ALRT's form for an earnings alert on `symbol`, set to go off when the results are out. */
473
+ function openEarningsAlert(symbol: string): boolean {
474
+ return openFormModal({ kind: "plugin-command", commandId: "set-event-alert", values: earningsResultsAlertValues(symbol) });
475
+ }
@@ -0,0 +1,188 @@
1
+ import type { EarningsTiming } from "../../../api-client/earnings";
2
+ import { addDays, isSession, marketDays, ownershipRank, type BoardReport, type MarketDay } from "./board-model";
3
+ import { shortDate, weekday } from "./format";
4
+
5
+ /** The weeks the calendar view offers, from the one before this week to three ahead. */
6
+ export const CALENDAR_WEEK_OFFSETS = [-1, 0, 1, 2, 3] as const;
7
+ type CalendarWeekOffset = (typeof CALENDAR_WEEK_OFFSETS)[number];
8
+
9
+ /** Within a day, reports before the open come first, then during the session, after the close, and those with no time yet. */
10
+ const SESSIONS: ReadonlyArray<{ session: EarningsTiming | null; label: string }> = [
11
+ { session: "bmo", label: "Before open" },
12
+ { session: "dmh", label: "During market" },
13
+ { session: "amc", label: "After close" },
14
+ { session: null, label: "Time not set" },
15
+ ];
16
+
17
+ export interface CalendarGroup {
18
+ session: EarningsTiming | null;
19
+ label: string;
20
+ /** The ones drawn, largest first (held, then watched names lead when the board puts mine first). */
21
+ reports: BoardReport[];
22
+ /** Reports of this group past its limit, counted in its last slot until the group is expanded. */
23
+ more: number;
24
+ }
25
+
26
+ /** Names a day's group, so a "+N more" slot can expand that group alone. */
27
+ export function calendarGroupKey(date: string, session: EarningsTiming | null): string {
28
+ return `${date}:${session ?? "unset"}`;
29
+ }
30
+
31
+ export interface CalendarDay {
32
+ date: string;
33
+ /** "Mon" */
34
+ weekday: string;
35
+ /** "Oct 20" */
36
+ label: string;
37
+ today: boolean;
38
+ /** No NYSE session that day. */
39
+ closed: boolean;
40
+ /** Every report that day, drawn or not. */
41
+ total: number;
42
+ /** Only the sessions with a report. */
43
+ groups: CalendarGroup[];
44
+ }
45
+
46
+ /**
47
+ * Monday to Friday of the week `offset` weeks from the one `today` falls in;
48
+ * on a Saturday or Sunday "this week" is the one about to start.
49
+ */
50
+ export function calendarWeekDates(today: string, offset: number): string[] {
51
+ const day = new Date(`${today}T00:00:00Z`).getUTCDay();
52
+ const monday = addDays(today, day === 0 ? 1 : day === 6 ? 2 : 1 - day);
53
+ const start = addDays(monday, offset * 7);
54
+ return [0, 1, 2, 3, 4].map((index) => addDays(start, index));
55
+ }
56
+
57
+ /** "Last week", "This week", "Next week", then "Week of Nov 3". */
58
+ export function calendarWeekLabel(today: string, offset: number): string {
59
+ if (offset === -1) return "Last week";
60
+ if (offset === 0) return "This week";
61
+ if (offset === 1) return "Next week";
62
+ return `Week of ${shortDate(calendarWeekDates(today, offset)[0]!)}`;
63
+ }
64
+
65
+ /** The weeks `--week` names, as offsets from this week. */
66
+ export const BOARD_WEEKS = ["last", "this", "next"] as const;
67
+ export type BoardWeek = typeof BOARD_WEEKS[number];
68
+ const BOARD_WEEK_OFFSET: Record<BoardWeek, CalendarWeekOffset> = { last: -1, this: 0, next: 1 };
69
+
70
+ export function isBoardWeek(value: unknown): value is BoardWeek {
71
+ return typeof value === "string" && (BOARD_WEEKS as readonly string[]).includes(value);
72
+ }
73
+
74
+ /**
75
+ * The days a board shows, as the pane's Range saves them: "" for the days
76
+ * ahead (the default), a week as its offset from this one ("-1" last week,
77
+ * "2" the week after next), or one day as YYYY-MM-DD. The table draws a
78
+ * section per day of it; the calendar draws its week.
79
+ */
80
+ export type BoardRange = string;
81
+
82
+ const ISO_DAY = /^\d{4}-\d{2}-\d{2}$/;
83
+ const WEEK_OFFSET = /^-?\d+$/;
84
+
85
+ /** The range `--date` or `--week` asks for; the days ahead without either. */
86
+ export function requestedBoardRange(date: unknown, week: unknown): BoardRange {
87
+ if (typeof date === "string" && ISO_DAY.test(date)) return date;
88
+ return isBoardWeek(week) ? String(BOARD_WEEK_OFFSET[week]) : "";
89
+ }
90
+
91
+ /** A range that is one day (`--date`), not a week or the days ahead. */
92
+ export function isBoardRangeDay(range: BoardRange): boolean {
93
+ return ISO_DAY.test(range);
94
+ }
95
+
96
+ function mondayOf(date: string): string {
97
+ return calendarWeekDates(date, 0)[0]!;
98
+ }
99
+
100
+ /** The week a range falls in, from this week: a day's own week, and this week for the days ahead. */
101
+ export function boardRangeWeek(range: BoardRange, today: string): number {
102
+ if (WEEK_OFFSET.test(range)) return Number(range);
103
+ if (!ISO_DAY.test(range)) return 0;
104
+ return Math.round((Date.parse(mondayOf(range)) - Date.parse(mondayOf(today))) / (7 * 86_400_000));
105
+ }
106
+
107
+ /** A day of a week or the day asked for: "Today" for today, otherwise "Thu Oct 15". */
108
+ function dayLabel(date: string, today: string): string {
109
+ return date === today ? "Today" : `${weekday(date)} ${shortDate(date)}`;
110
+ }
111
+
112
+ /**
113
+ * The table's sections for `range`: the market's days ahead by default, one
114
+ * section per weekday of a week, or the one day asked for. Days without
115
+ * reports (a holiday) leave no section.
116
+ */
117
+ export function boardRangeDays(range: BoardRange, today: string): MarketDay[] {
118
+ if (ISO_DAY.test(range)) return [{ label: dayLabel(range, today), from: range, to: range }];
119
+ if (!WEEK_OFFSET.test(range)) return marketDays(today);
120
+ return calendarWeekDates(today, Number(range)).map((date) => ({ label: dayLabel(date, today), from: date, to: date }));
121
+ }
122
+
123
+ /** What the range reads as: "Last week", "Week of Nov 2", "Thu Oct 15", or null for the days ahead. */
124
+ export function boardRangeLabel(range: BoardRange, today: string): string | null {
125
+ if (ISO_DAY.test(range)) return dayLabel(range, today);
126
+ return WEEK_OFFSET.test(range) ? calendarWeekLabel(today, Number(range)) : null;
127
+ }
128
+
129
+ /** The range in a sentence ("No reports … last week"): "today", "on Thu Oct 15", "in the week of Nov 2"; null for the days ahead. */
130
+ export function boardRangeWords(range: BoardRange, today: string): string | null {
131
+ if (ISO_DAY.test(range)) return range === today ? "today" : `on ${dayLabel(range, today)}`;
132
+ if (!WEEK_OFFSET.test(range)) return null;
133
+ const offset = Number(range);
134
+ return offset >= -1 && offset <= 1 ? calendarWeekLabel(today, offset).toLowerCase() : `in the week of ${shortDate(calendarWeekDates(today, offset)[0]!)}`;
135
+ }
136
+
137
+ function bySize(left: BoardReport, right: BoardReport, mineFirst: boolean): number {
138
+ const rank = ownershipRank(left, mineFirst) - ownershipRank(right, mineFirst);
139
+ if (rank !== 0) return rank;
140
+ // Missing caps sink, as they do in the table.
141
+ if (left.marketCap !== right.marketCap) {
142
+ if (left.marketCap == null) return 1;
143
+ if (right.marketCap == null) return -1;
144
+ return right.marketCap - left.marketCap;
145
+ }
146
+ return left.symbol.localeCompare(right.symbol);
147
+ }
148
+
149
+ /**
150
+ * The week as the calendar draws it: a column per day, and in each the
151
+ * reports grouped by when they come out, before the open, during the
152
+ * session, after the close, and with no time yet. A group takes at most
153
+ * `limit` slots: past it, its largest companies and a last slot that counts
154
+ * the rest. A group named in `expanded` (see `calendarGroupKey`) draws all
155
+ * of its reports.
156
+ */
157
+ export function calendarDays(
158
+ reports: readonly BoardReport[],
159
+ dates: readonly string[],
160
+ { today, mineFirst = false, limit = Number.POSITIVE_INFINITY, expanded }: {
161
+ today: string;
162
+ mineFirst?: boolean;
163
+ limit?: number;
164
+ expanded?: ReadonlySet<string>;
165
+ },
166
+ ): CalendarDay[] {
167
+ return dates.map((date) => {
168
+ const inDay = reports.filter((report) => report.date === date);
169
+ const groups = SESSIONS.flatMap(({ session, label }): CalendarGroup[] => {
170
+ const inGroup = inDay.filter((report) => report.timing === session).sort((left, right) => bySize(left, right, mineFirst));
171
+ if (inGroup.length === 0) return [];
172
+ // Past the limit, the last slot counts the rest.
173
+ const shown = inGroup.length > limit && !expanded?.has(calendarGroupKey(date, session))
174
+ ? inGroup.slice(0, Math.max(0, limit - 1))
175
+ : inGroup;
176
+ return [{ session, label, reports: shown, more: inGroup.length - shown.length }];
177
+ });
178
+ return {
179
+ date,
180
+ weekday: weekday(date),
181
+ label: shortDate(date),
182
+ today: date === today,
183
+ closed: !isSession(date),
184
+ total: inDay.length,
185
+ groups,
186
+ };
187
+ });
188
+ }