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
@@ -17,6 +17,7 @@ import type { PaneProps } from "../../../../types/plugin";
17
17
  import { useOptionalDialog, type PromptContext } from "../../../../ui/dialog";
18
18
  import { isPlainKey } from "../../../../utils/keyboard";
19
19
  import { logFailure } from "../../../../utils/log-failure";
20
+ import { useRowNotes } from "../../../../components/ui/row-notes";
20
21
  import { nextHeaderSort, type SortPreference } from "../../../../utils/sort-values";
21
22
  import { usePaneStateValue } from "../../../../state/app/context";
22
23
  import { useInlineTickerQuoteFact, useInlineTickers } from "../../../../state/hooks/inline-tickers";
@@ -63,6 +64,15 @@ import { useBuildoutDataRuntime } from "../data-runtime";
63
64
 
64
65
  const BUILDOUT_UPGRADE_URL = "https://thebuildout.ai/pricing";
65
66
 
67
+ /** A star a row's company or site did not take: said by its star cell, in full at the top of its detail. */
68
+ const favoriteNoteKey = (row: BuildoutRow) => favoriteKey(row) ?? rowKey(row);
69
+
70
+ function favoriteFailureText(row: BuildoutRow, starring: boolean, error: unknown): string {
71
+ // The service may answer with a page of its own; its first line says enough.
72
+ const reason = error instanceof Error ? error.message.trim().split("\n")[0]!.slice(0, 240) : "";
73
+ return `Could not ${starring ? "star" : "unstar"} ${rowTitle(row)}${reason ? `: ${reason}` : "."}`;
74
+ }
75
+
66
76
 
67
77
  const NO_TICKER_TEXTS: string[] = [];
68
78
 
@@ -86,6 +96,7 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
86
96
  const dialog = useOptionalDialog();
87
97
  const tableScrollRef = useRef<ScrollBoxRenderable | null>(null);
88
98
  const favoriteBusyKeysRef = useRef<Set<string>>(new Set());
99
+ const clearFavoriteNotesRef = useRef<() => void>(() => {});
89
100
  const [activeTab, setActiveTab] = usePaneStateValue<BuildoutTabId>("activeTab", "companies");
90
101
  const [selectedList, setSelectedList] = useState<BuildoutList | null>(null);
91
102
  const [selectedIndex, setSelectedIndex] = useState(0);
@@ -94,14 +105,13 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
94
105
  const [upgradeBusy, setUpgradeBusy] = useState(false);
95
106
  const [upgradeMessage, setUpgradeMessage] = useState<string | null>(null);
96
107
  const [favoriteBusyKey, setFavoriteBusyKey] = useState<string | null>(null);
97
- const [favoriteMessage, setFavoriteMessage] = useState<string | null>(null);
98
108
 
99
109
  const handleBeforeLoad = useCallback(() => {
100
110
  favoriteBusyKeysRef.current.clear();
101
111
  setDetailRow(null);
102
112
  setSelectedList(null);
103
113
  setUpgradeMessage(null);
104
- setFavoriteMessage(null);
114
+ clearFavoriteNotesRef.current();
105
115
  setFavoriteBusyKey(null);
106
116
  }, []);
107
117
  const {
@@ -136,7 +146,6 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
136
146
  useEffect(() => {
137
147
  setSelectedIndex(0);
138
148
  setDetailRow(null);
139
- setFavoriteMessage(null);
140
149
  const nextColumn: BuildoutColumnId | null = activeTab === "companies"
141
150
  ? selectedList ? "marketCap" : null
142
151
  : activeTab === "sites" ? "capture" : "time";
@@ -147,6 +156,10 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
147
156
  () => sortRows(activeRows(state, activeTab, selectedList), sort.columnId, sort.direction),
148
157
  [activeTab, selectedList, sort, state],
149
158
  );
159
+ // Until the next star on that row, or a reload.
160
+ const favoriteNotes = useRowNotes<BuildoutRow, string>(rows, { key: favoriteNoteKey });
161
+ const { setNote: setFavoriteNote, noteFor: favoriteNoteFor } = favoriteNotes;
162
+ clearFavoriteNotesRef.current = favoriteNotes.clearNotes;
150
163
  const columns = useMemo(() => columnsForTab(activeTab, selectedList, canFavorite), [activeTab, canFavorite, selectedList]);
151
164
  const selectedRow = rows[selectedIndex] ?? rows[0] ?? null;
152
165
  // The table's cells carry their own badges; this catalog only serves the open
@@ -226,7 +239,6 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
226
239
  setSelectedIndex(0);
227
240
  setDetailRow(null);
228
241
  setUpgradeMessage(null);
229
- setFavoriteMessage(null);
230
242
  setSort({ columnId: "marketCap", direction: "desc" });
231
243
  resetCompanies();
232
244
  }, [resetCompanies]);
@@ -236,7 +248,6 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
236
248
  setSelectedIndex(0);
237
249
  setDetailRow(null);
238
250
  setUpgradeMessage(null);
239
- setFavoriteMessage(null);
240
251
  setSort({ columnId: null, direction: "asc" });
241
252
  }, []);
242
253
 
@@ -265,20 +276,21 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
265
276
  const next = !previous;
266
277
  favoriteBusyKeysRef.current.add(key);
267
278
  setFavoriteBusyKey(key);
268
- setFavoriteMessage(null);
279
+ setFavoriteNote(key, null);
269
280
  updateFavorite(key, next);
270
281
 
271
282
  try {
272
283
  const response = await buildoutApi<{ starred?: boolean }>(path, favoriteToken, { method: "POST" });
273
284
  updateFavorite(key, typeof response.starred === "boolean" ? response.starred : next);
274
- } catch {
285
+ } catch (error) {
286
+ // The star goes back, and the row says it did not take.
275
287
  updateFavorite(key, previous);
276
- setFavoriteMessage("favorite failed");
288
+ setFavoriteNote(key, favoriteFailureText(row, next, error));
277
289
  } finally {
278
290
  favoriteBusyKeysRef.current.delete(key);
279
291
  setFavoriteBusyKey((current) => current === key ? null : current);
280
292
  }
281
- }, [favoriteToken, updateFavorite]);
293
+ }, [favoriteToken, setFavoriteNote, updateFavorite]);
282
294
 
283
295
  const toggleFavoriteRow = useCallback((row: BuildoutRow | null) => {
284
296
  if (!canFavorite || !row || !favoriteKey(row)) return false;
@@ -314,14 +326,13 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
314
326
 
315
327
  usePaneFooter("buildout", () => ({
316
328
  info: updateBuildoutFooterInfo(state, activeTab, selectedList, {
317
- favoriteMessage,
318
329
  upgradeMessage,
319
330
  partialList,
320
331
  onUpgrade: startUpgrade,
321
332
  quoteFreshness: detailQuoteFreshness,
322
333
  }),
323
334
  hints: footerHints,
324
- }), [activeTab, detailQuoteFreshness, favoriteMessage, footerHints, partialList, selectedList, startUpgrade, state, upgradeMessage]);
335
+ }), [activeTab, detailQuoteFreshness, footerHints, partialList, selectedList, startUpgrade, state, upgradeMessage]);
325
336
 
326
337
  const activePage = state.status === "ready" ? activeBuildoutPage(state, activeTab, selectedList) : null;
327
338
  const loadMoreActiveRows = useTableLoadMore(
@@ -367,8 +378,9 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
367
378
  rowState: { selected: boolean },
368
379
  ) => renderBuildoutCell(row, column, rowState, {
369
380
  favoriteBusyKey,
381
+ favoriteFailed: favoriteNoteFor(row) !== null,
370
382
  toggleFavorite,
371
- }), [favoriteBusyKey, toggleFavorite]);
383
+ }), [favoriteBusyKey, favoriteNoteFor, toggleFavorite]);
372
384
 
373
385
  const { strip: tabStrip, rows: tabRows } = usePaneTabs({
374
386
  tabs,
@@ -379,12 +391,15 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
379
391
  variant: "bare",
380
392
  });
381
393
 
382
- if (state.status === "loading") {
383
- return <PaneStatusBody loading subject={BUILDOUT_NAME} width={width} height={height} />;
384
- }
385
-
386
- if (state.status === "error") {
387
- return <PaneStatusBody error={state.message} subject={BUILDOUT_NAME} width={width} height={height} retry={{ onRetry: refresh }} />;
394
+ // The strip stays over the first load and a failed one, as over every tab's own.
395
+ if (state.status === "loading" || state.status === "error") {
396
+ return (
397
+ <Box flexDirection="column" width={width} height={height} overflow="hidden">
398
+ {tabStrip && <Box height={1}>{tabStrip}</Box>}
399
+ {state.status === "loading" ? <PaneStatusBody loading subject={BUILDOUT_NAME} />
400
+ : <PaneStatusBody error={state.message} subject={BUILDOUT_NAME} retry={{ onRetry: refresh }} />}
401
+ </Box>
402
+ );
388
403
  }
389
404
 
390
405
  const listOpen = activeTab === "companies" && !!selectedList;
@@ -398,6 +413,7 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
398
413
  detailContent={(
399
414
  <BuildoutDetail
400
415
  row={detailRow}
416
+ note={detailRow ? favoriteNoteFor(detailRow) : null}
401
417
  width={width}
402
418
  height={height}
403
419
  catalog={tickerCatalog}
@@ -26,7 +26,6 @@ export function updateBuildoutFooterInfo(
26
26
  activeTab: BuildoutTabId,
27
27
  selectedList: BuildoutList | null,
28
28
  messages: {
29
- favoriteMessage: string | null;
30
29
  upgradeMessage: string | null;
31
30
  partialList: boolean;
32
31
  onUpgrade: () => void;
@@ -66,9 +65,6 @@ export function updateBuildoutFooterInfo(
66
65
  if (page?.error) {
67
66
  info.push({ id: "page-error", parts: [{ text: page.error, tone: "negative" }] });
68
67
  }
69
- if (messages.favoriteMessage) {
70
- info.push({ id: "favorite-error", parts: [{ text: messages.favoriteMessage, tone: "negative" }] });
71
- }
72
68
  if (messages.upgradeMessage) {
73
69
  info.push({ id: "upgrade-error", parts: [{ text: messages.upgradeMessage, tone: "negative" }] });
74
70
  }
@@ -107,7 +107,7 @@ export const cryptoPluginMeta = {
107
107
  stateId: "market-overview",
108
108
  name: "Crypto & Perps",
109
109
  version: "1.0.0",
110
- description: "Top crypto assets by market cap, and perpetual funding, open interest and premiums.",
110
+ description: "Top crypto assets by market cap, daily spot bitcoin and ether ETF flows, and perpetual funding, open interest and premiums.",
111
111
  toggleable: true,
112
112
  };
113
113
 
@@ -23,6 +23,6 @@ export function validateCatalystDetail(payload: CatalystDetail, id?: string): Ca
23
23
  export const fetchCatalysts = async (query: CatalystFilters, client: Pick<typeof apiClient, "getCloudCatalysts"> = apiClient, signal?: AbortSignal) => validateCatalysts(await client.getCloudCatalysts(query, signal));
24
24
  export const fetchCatalystDetail = async (id: string, client: Pick<typeof apiClient, "getCloudCatalystEvent"> = apiClient, signal?: AbortSignal, query: { offset?: number; limit?: number } = {}) => validateCatalystDetail(await client.getCloudCatalystEvent(id, signal, query), id);
25
25
  export const loadCatalysts = (query: CatalystFilters, accessKey: string, force = false, signal?: AbortSignal) =>
26
- loadCloudResource(catalystCache, `${accessKey}:${JSON.stringify(query)}`, () => fetchCatalysts(query, apiClient, signal), { force, validate: validateCatalysts });
26
+ loadCloudResource(catalystCache, `${accessKey}:${JSON.stringify(query)}`, () => fetchCatalysts(query, apiClient, signal), { force, validate: validateCatalysts, signal });
27
27
  export const loadCatalystDetail = (id: string, accessKey: string, force = false, offset = 0, signal?: AbortSignal) =>
28
- loadCloudResource(catalystDetailCache, `${accessKey}:${id}:${offset}`, () => fetchCatalystDetail(id, apiClient, signal, { offset, limit: 100 }), { force, validate: (payload) => validateCatalystDetail(payload, id) });
28
+ loadCloudResource(catalystDetailCache, `${accessKey}:${id}:${offset}`, () => fetchCatalystDetail(id, apiClient, signal, { offset, limit: 100 }), { force, validate: (payload) => validateCatalystDetail(payload, id), signal });
@@ -6,7 +6,7 @@ import { usePluginPaneState } from "../../../public/react";
6
6
  import { useThemeColors } from "../../../theme/theme-context";
7
7
  import { Box, ScrollBox, Text, useRendererHost, useUiCapabilities, type ScrollBoxRenderable } from "../../../ui";
8
8
  import { UpgradeLabel } from "../shared/locked-rows";
9
- import { catalystAgency, catalystChangeText, catalystDate, catalystDateBasis, catalystFacts, catalystLabel } from "./model";
9
+ import { CATALYST_DATE_WITHHELD, catalystAgency, catalystChangeText, catalystDate, catalystDateBasis, catalystDateWithheld, catalystFacts, catalystLabel } from "./model";
10
10
  import { humanLabel } from "../shared/research-cells";
11
11
  import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
12
12
  import { loadCatalystDetail } from "./client";
@@ -23,7 +23,8 @@ function Evidence({ event, width, height }: { event: CatalystEvent; width: numbe
23
23
  <KeyValueRow label="Status" value={humanLabel(event.status)} detail={`Revision ${event.revision}`} labelWidth={18} />
24
24
  <KeyValueRow label="Announced" value={catalystDate(event, "announced")} labelWidth={18} />
25
25
  {event.effectiveDate ? <KeyValueRow label={catalystDateBasis(event, "effective")} value={catalystDate(event, "effective")} labelWidth={18} /> : null}
26
- {event.deadlineDate ? <KeyValueRow label={catalystDateBasis(event, "deadline")} value={catalystDate(event, "deadline")} labelWidth={18} /> : null}
26
+ {event.deadlineDate ? <KeyValueRow label={catalystDateBasis(event, "deadline")} value={catalystDate(event, "deadline")} labelWidth={18} />
27
+ : catalystDateWithheld(event) ? <KeyValueRow label={catalystLabel("deadline")} value={CATALYST_DATE_WITHHELD} labelWidth={18} /> : null}
27
28
  <KeyValueRow label="Observed (UTC)" value={catalystDate(event, "observed")} labelWidth={18} />
28
29
  <KeyValueRow label="Confidence" value={`${Math.round(event.confidence * 100)}%`} labelWidth={18} />
29
30
  {facts.map((fact) => <KeyValueRow key={fact.label} label={fact.label} value={fact.value} labelWidth={18} />)}
@@ -23,7 +23,7 @@ export const catalystsModule: PluginModule = {
23
23
  instruments: ["equity", "fund"], tableExport: true, headless: litigationHeadless },
24
24
  ],
25
25
  paneTemplates: [
26
- { id: "catalysts-pane", paneId: "catalysts", label: "Catalysts", description: "Pro regulatory, clinical, legal and trade-policy calendar with source evidence and revision history.", keywords: ["catalysts", "calendar", "FDA", "clinical", "regulatory", "deadlines", "antitrust", "sanctions"],
26
+ { id: "catalysts-pane", paneId: "catalysts", label: "Catalysts", description: "Pro regulatory, clinical, legal and trade-policy calendar with source evidence and revision history.", keywords: ["catalysts", "calendar", "FDA", "clinical", "regulatory", "deadlines", "antitrust", "sanctions", "PDUFA", "FDA decision", "approval", "approvals"],
27
27
  shortcut: { prefix: "CATL", argKind: "ticker", argPlaceholder: "ticker", argOptional: true, openWithoutArg: true }, headless: catalystsHeadless,
28
28
  createInstance: (_context, options) => { const symbol = (options?.symbol ?? options?.arg ?? "").trim().toUpperCase(); const event = options?.values?.event; const open = event && /^[a-zA-Z0-9:_-]{1,160}$/.test(event) ? event : undefined; return { title: symbol ? `CATL ${symbol}` : "Catalysts", placement: "floating", ...(symbol ? { binding: { kind: "fixed" as const, symbol } } : {}), settings: { symbol, ...(open ? { open } : {}) } }; } },
29
29
  { ...createTickerSurfacePaneTemplate({ id: "litigation-pane", paneId: "litigation", label: "Litigation", description: "Pro company litigation, enforcement and antitrust dockets with dated source evidence.", keywords: ["litigation", "legal", "docket", "court", "enforcement", "antitrust"], shortcut: "LITI" }), headless: litigationHeadless },
@@ -1,5 +1,5 @@
1
1
  import type { CatalystEvent, CatalystFilters, CatalystResponse, CatalystType } from "../../../api-client/catalysts";
2
- import type { DataTableColumn } from "../../../components";
2
+ import type { DataTableColumn, TableSection } from "../../../components";
3
3
  import { publicTickerKey } from "../../../utils/exchanges";
4
4
  import { humanLabel, type StateTone } from "../shared/research-cells";
5
5
 
@@ -13,8 +13,17 @@ export const catalystSymbol = (event: CatalystEvent): string | null => {
13
13
  return party?.ticker ? publicTickerKey(party.ticker, party.exchange ?? undefined) : null;
14
14
  };
15
15
  export const catalystTickers = (event: CatalystEvent) => [...new Set(event.parties.filter((p) => p.ticker).map((p) => publicTickerKey(p.ticker!, p.exchange ?? undefined)))].join(", ");
16
+ /**
17
+ * A source named the event but withheld its date (a company's PDUFA target
18
+ * without the day). The filing's date is when it was said, so the row has no
19
+ * date of its own; only the announcement and observation dates stand.
20
+ */
21
+ export const catalystDateWithheld = (event: Pick<CatalystEvent, "metadata">, field: CatalystFilters["dateField"] = "any") =>
22
+ event.metadata?.dateWithheld === true && field !== "announced" && field !== "observed";
23
+ export const CATALYST_DATE_WITHHELD = "Date not disclosed";
16
24
  export const catalystDate = (event: CatalystEvent, field: CatalystFilters["dateField"] = "any"): string => {
17
25
  if (field === "observed") return event.observedAt.slice(0, 16).replace("T", " ");
26
+ if (catalystDateWithheld(event, field)) return "--";
18
27
  const key = field === "announced" || field === "effective" || field === "deadline" ? field
19
28
  : event.deadlineDate ? "deadline" : event.effectiveDate ? "effective" : "announced";
20
29
  const value = event[`${key}Date`];
@@ -35,15 +44,22 @@ function fitColumns(columns: FitColumn[], width: number): DataTableColumn[] {
35
44
  let kept = columns;
36
45
  const drop = (id: string) => { if (tableNeed(kept) > width) kept = kept.filter((column) => column.id !== id); };
37
46
  for (const id of ["country", "agency", "type"]) drop(id);
47
+ // A long date basis gives up only the cells the table lacks before every column shrinks.
48
+ const over = tableNeed(kept) - width;
49
+ if (over > 0) kept = kept.map((column) => column.id === "basis" ? { ...column, width: Math.max(column.min ?? column.width, column.width - over) } : column);
38
50
  if (tableNeed(kept) > width) kept = kept.map((column) => ({ ...column, width: column.min ?? column.width }));
39
51
  for (const id of ["basis", "parties"]) drop(id);
40
52
  return kept.map(({ min: _min, ...column }) => column);
41
53
  }
42
54
 
43
- export function catalystColumns(width: number, changes: boolean, litigation: boolean): DataTableColumn[] {
55
+ /** The date basis column as wide as its longest label on screen ("Primary completion (actual)"), never narrower than its heading. */
56
+ export const catalystBasisWidth = (events: readonly CatalystEvent[], field: CatalystFilters["dateField"]) =>
57
+ Math.min(27, events.reduce((widest, event) => Math.max(widest, catalystDateBasis(event, field).length), 10));
58
+
59
+ export function catalystColumns(width: number, changes: boolean, litigation: boolean, basisWidth = 10): DataTableColumn[] {
44
60
  return fitColumns([
45
61
  { id: "date", label: changes ? "Observed (UTC)" : "Date", width: changes ? 16 : 10, align: "left" },
46
- ...(!changes ? [{ id: "basis", label: "Date basis", width: 10, align: "left" as const }] : []),
62
+ ...(!changes ? [{ id: "basis", label: "Date basis", width: basisWidth, min: 10, align: "left" as const }] : []),
47
63
  ...(!litigation ? [{ id: "ticker", label: "Ticker", width: width < 110 ? 12 : 14, min: 10, align: "left" as const }] : []),
48
64
  // The entity the event is about, which the title alone buries.
49
65
  { id: "parties", label: "Parties", width: Math.max(16, Math.min(28, Math.round(width * 0.16))), min: 14, align: "left" },
@@ -151,9 +167,30 @@ export function catalystChangeText(change: CatalystEvent["changes"][number]): st
151
167
  return `${field}: ${changeValue(change.before)} → ${changeValue(change.after)}`;
152
168
  }
153
169
  const MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
170
+ /**
171
+ * Rows under their section headings in the order the server sends them:
172
+ * upcoming dates first, then past ones. A heading starts wherever the label
173
+ * changes, so a month today falls in heads its upcoming days and again its
174
+ * past days, after the later months, never pulling a past day ahead of them.
175
+ */
176
+ export function catalystSections(rows: readonly CatalystEvent[], field: CatalystFilters["dateField"], changes: boolean): TableSection<CatalystEvent>[] {
177
+ const sections: TableSection<CatalystEvent>[] = [];
178
+ const seen = new Map<string, number>();
179
+ for (const event of rows) {
180
+ const label = catalystSection(event, field, changes);
181
+ const last = sections.at(-1);
182
+ if (last?.label === label) { last.items.push(event); continue; }
183
+ const repeat = seen.get(label) ?? 0;
184
+ seen.set(label, repeat + 1);
185
+ sections.push({ label, key: repeat ? `${label}:${repeat}` : label, items: [event] });
186
+ }
187
+ return sections;
188
+ }
189
+
154
190
  /** The calendar groups by the month of the date it shows; the change feed by the day a change was observed. */
155
191
  export function catalystSection(event: CatalystEvent, field: CatalystFilters["dateField"], changes: boolean): string {
156
192
  if (changes) return event.observedAt.slice(0, 10);
193
+ if (catalystDateWithheld(event, field)) return CATALYST_DATE_WITHHELD;
157
194
  const date = catalystDate(event, field);
158
195
  if (date === "--") return "Undated";
159
196
  return /^\d{4}-\d{2}/.test(date) ? `${MONTHS[Number(date.slice(5, 7)) - 1]} ${date.slice(0, 4)}` : date;
@@ -206,10 +243,18 @@ export function catalystEmptyTitle(data: Pick<CatalystResponse, "coverage"> | nu
206
243
  return `No cases linked to ${query.symbol}.${courts.length ? ` ${courts.join(" and ")} are not collected yet, so only agency and regulator actions show.` : ""}`;
207
244
  }
208
245
 
246
+ /** "Primary completion (est.)" or "(actual)", as the registry types the date; never a bare "Completion". */
247
+ function primaryCompletionLabel(event: Pick<CatalystEvent, "metadata">): string {
248
+ const completion = event.metadata?.primaryCompletion;
249
+ const type = completion && typeof completion === "object" && "type" in completion ? String(completion.type).toUpperCase() : "";
250
+ return type === "ESTIMATED" ? "Primary completion (est.)" : type === "ACTUAL" ? "Primary completion (actual)" : "Primary completion";
251
+ }
252
+
209
253
  export function catalystDateBasis(event: CatalystEvent, field: CatalystFilters["dateField"] = "any"): string {
254
+ if (catalystDateWithheld(event, field)) return CATALYST_DATE_WITHHELD;
210
255
  const basis = field === "any" || !field ? event.deadlineDate ? "deadline" : event.effectiveDate ? "effective" : event.announcedDate ? "announced" : "undated" : field;
211
256
  const meaning = String(event.metadata?.[`${basis}Meaning`] ?? event.metadata?.[`${basis}DateMeaning`] ?? "");
212
- if (/primary completion/i.test(meaning)) return "Completion";
257
+ if (/primary completion/i.test(meaning)) return primaryCompletionLabel(event);
213
258
  if (/comment/i.test(meaning)) return "Comments";
214
259
  if (/meeting/i.test(meaning)) return "Meeting";
215
260
  return catalystLabel(basis);
@@ -12,6 +12,7 @@ import { usePaneTickerIdentity } from "../../../state/hooks/pane-ticker";
12
12
  import { useThemeColors } from "../../../theme/theme-context";
13
13
  import type { PaneProps } from "../../../types/plugin";
14
14
  import { Box, Text, useUiCapabilities, type ScrollBoxRenderable } from "../../../ui";
15
+ import { withCountryFlag } from "../../../components/ui/country-flag";
15
16
  import { SignInWall } from "../cloud/auth-actions";
16
17
  import { CLOUD_PLAN_KEY, useCloudUpgradeAction } from "../shared/cloud-upgrade";
17
18
  import { useOpenDescription } from "../shared/open-description";
@@ -20,7 +21,7 @@ import { listingCell, toneColor } from "../shared/research-cells";
20
21
  import { isCloudSessionRequired, useResearchCloudSession } from "../shared/research-cloud-session";
21
22
  import { loadCatalystDetail, loadCatalysts, validateCatalysts } from "./client";
22
23
  import { CatalystEventDetail } from "./detail";
23
- import { catalystAgency, catalystCell, catalystColumns, catalystEmptyTitle, catalystLabel, catalystQuery, catalystSection, catalystStatusTone, catalystSymbol, catalystTab, catalystTypeTone, CATALYST_TABS } from "./model";
24
+ import { catalystAgency, catalystBasisWidth, catalystCell, catalystColumns, catalystEmptyTitle, catalystLabel, catalystQuery, catalystSections, catalystStatusTone, catalystSymbol, catalystTab, catalystTypeTone, CATALYST_TABS } from "./model";
24
25
 
25
26
  type Row = SectionedRow<CatalystEvent> | { kind: "locked"; key: string };
26
27
  const rowKey = (row: Row) => row.key;
@@ -81,12 +82,7 @@ export function CatalystView({ width, height, focused, symbol, litigation = fals
81
82
  const lockedRows = Math.min(3, data?.access?.lockedRows ?? 0);
82
83
  // Grouped by month on the calendar and by day in the change feed, in the order the server sends them.
83
84
  const items = useMemo<Row[]>(() => {
84
- const groups = new Map<string, CatalystEvent[]>();
85
- for (const event of rows) {
86
- const label = catalystSection(event, query.dateField, tab === "changes");
87
- groups.set(label, [...(groups.get(label) ?? []), event]);
88
- }
89
- return [...buildSectionedRows([...groups].map(([label, events]) => ({ label, items: events })), (event) => event.id),
85
+ return [...buildSectionedRows(catalystSections(rows, query.dateField, tab === "changes"), (event) => event.id),
90
86
  ...Array.from({ length: lockedRows }, (_, index): Row => ({ kind: "locked", key: `locked:${index}` }))];
91
87
  }, [rows, query.dateField, tab, lockedRows]);
92
88
  const listedOpen = rows.find((event) => event.id === openId) ?? null;
@@ -124,7 +120,7 @@ export function CatalystView({ width, height, focused, symbol, litigation = fals
124
120
  ...(tab === "calendar" ? [{ id: "upcoming", kind: "toggle" as const, label: "Upcoming", value: filters.upcoming === true, onChange: (value: boolean) => changeFilter("upcoming", value) },
125
121
  { id: "date", label: "Date", value: String(filters.dateField ?? "any"), options: [{ value: "any", label: "Any date" }, { value: "announced", label: "Announced" }, { value: "effective", label: "Effective" }, { value: "deadline", label: "Deadlines" }], onChange: (value: string) => changeFilter("dateField", value) }] : []),
126
122
  ]} />;
127
- const columns = catalystColumns(width, tab === "changes", litigation);
123
+ const columns = catalystColumns(width, tab === "changes", litigation, catalystBasisWidth(rows, query.dateField));
128
124
  const renderCell = (row: Row, column: (typeof columns)[number], _index: number, state: { selected: boolean }): DataTableCell => {
129
125
  if (row.kind === "locked") {
130
126
  if (!desktop && column.id === "title" && row.key === "locked:0") return { text: "Upgrade for every catalyst and its history", content: <UpgradeLabel text="Upgrade for every catalyst and its history" onPress={openUpgrade} role="catalysts-upgrade" />, onMouseDown: openUpgrade };
@@ -141,16 +137,19 @@ export function CatalystView({ width, height, focused, symbol, litigation = fals
141
137
  }
142
138
  if (column.id === "parties") return { text, color: text === "--" ? colors.textMuted : colors.textBright };
143
139
  if (column.id === "type") return { text, color: toneColor(catalystTypeTone(event.type), colors) };
140
+ // The column has room for the flag beside a code.
141
+ if (column.id === "country") return withCountryFlag({ text, color: colors.textDim }, text, desktop);
144
142
  if (column.id === "status") return { text, color: tab === "changes" ? colors.text : toneColor(catalystStatusTone(event.status), colors) };
145
143
  return { text, color: column.id === "title" || column.id === "date" ? colors.text : colors.textDim };
146
144
  };
147
145
  if (isCloudSessionRequired(error?.message)) return <SignInWall placement="catalysts-signin" action="view catalysts" needsVerification={session.needsVerification} />;
148
146
  return <Box width={width} height={height} flexDirection="column">
149
147
  {strip}
150
- <PaneStatusBody loading={!openId && !data && resource.loading} error={!openId && (!data || denied) ? error?.message : null} subject={litigation ? "company litigation" : "catalysts"}>
151
- <PageStackView focused={focused} detailOpen={!!openId} onBack={() => setOpenId("")} detailTitle={open?.title ?? "Catalyst"}
148
+ <PageStackView focused={focused} detailOpen={!!openId} onBack={() => setOpenId("")} detailTitle={open?.title ?? "Catalyst"}
152
149
  detailContent={open ? <CatalystEventDetail key={open.id} event={open} accessKey={accessKey} snapshot={detailSnapshots[open.id]} width={width} height={Math.max(3, height - 1)} focused={focused} initialTab={detailTab} openUpgrade={openUpgrade} /> : <PaneStatusBody loading={externalOpen.loading} error={externalOpen.error} subject="catalyst event"><EmptyState title="Event unavailable." /></PaneStatusBody>}
153
150
  rootContent={<DataTableView<Row> rootWidth={width} rootHeight={Math.max(3, height - tabRows)} rootBefore={queryBar} focused={focused && !openId && !searchActive && !dateFieldActive}
151
+ // A tab or filter change loads under the query bar, which stays.
152
+ status={{ loading: !data && resource.loading, error: !data || denied ? error?.message : null, subject: litigation ? "company litigation" : "catalysts" }}
154
153
  items={items} sortColumnId={null} sortDirection="asc" columns={columns} getItemKey={rowKey}
155
154
  isNavigable={isSelectable} renderSectionHeader={(row) => row.kind === "locked" ? null : renderSectionedRowHeader(row)}
156
155
  selection={{ kind: "id", selectedId: selected?.id ?? null, getId: rowKey, onChange: (id) => { if (!id.startsWith("locked:")) setSelectedId(id); } }}
@@ -160,6 +159,5 @@ export function CatalystView({ width, height, focused, symbol, litigation = fals
160
159
  emptyStateTitle={catalystEmptyTitle(data, query, tab === "changes")}
161
160
  bodyAfter={lockedRows && desktop ? <LockedOverlay rows={lockedRows} text="Upgrade for every catalyst and its history" onPress={openUpgrade} role="catalysts-upgrade" /> : undefined} />}
162
161
  />
163
- </PaneStatusBody>
164
162
  </Box>;
165
163
  }
@@ -49,6 +49,11 @@ export function validateCentralBankRates(payload: CentralBankRatesPayload): Cent
49
49
  if (row.nextMeeting && row.nextMeeting.kind !== undefined && row.nextMeeting.kind !== "decision" && row.nextMeeting.kind !== "fixing") delete row.nextMeeting.kind;
50
50
  if (row.nextMeetingStatus !== undefined && !["scheduled", "not-published", "not-verified", "no-policy-rate"].includes(row.nextMeetingStatus)) delete row.nextMeetingStatus;
51
51
  if (row.seriesNotes !== undefined && !strings(row.seriesNotes)) delete row.seriesNotes;
52
+ // A decision date is a citation: one that is not a real day on or before the change it is for, from an https page, is left out.
53
+ const decision = row.lastDecision;
54
+ if (decision !== undefined && decision !== null && (typeof decision !== "object" || decision.date == null || !dateOrNull(decision.date)
55
+ || row.lastChangeDate == null || decision.date > row.lastChangeDate || decision.sourceUrl == null || !urlOrNull(decision.sourceUrl)
56
+ || !Number.isFinite(Date.parse(decision.verifiedAt)) || decision.kind !== undefined && decision.kind !== "decision" && decision.kind !== "fixing")) delete row.lastDecision;
52
57
  }
53
58
  return payload;
54
59
  }
@@ -1,10 +1,17 @@
1
1
  import type { HeadlessPaneColumn, HeadlessPaneDefinition } from "../../../types/plugin";
2
+ import { CENTRAL_BANKS, centralBankNames } from "../../../utils/central-banks";
2
3
  import { fetchCentralBankRates } from "./client";
3
- import { hasNoPolicyRate, policyExportRow, policyNextMeetingText, policyNotices } from "./model";
4
+ import {
5
+ hasNoPolicyRate, policyExportRow, policyLastDecisionText, policyNextMeetingText, policyNotices, policyRate, policyUnlistedNote,
6
+ } from "./model";
4
7
 
5
- /** The citation block every export row carries, headed as a journalist's spreadsheet names it. */
8
+ /**
9
+ * The citation block every export row carries, headed as a journalist's
10
+ * spreadsheet names it. `source` and `as_of` are the row's own, so the
11
+ * export's fixed columns of the same names are not added again.
12
+ */
6
13
  const CITATION_FIELDS = [
7
- "central_bank", "source", "series_id", "source_url", "as_of",
14
+ "central_bank", "source", "series_id", "source_url", "as_of", "last_decision",
8
15
  "next_meeting", "next_meeting_kind", "next_meeting_source_url", "next_meeting_verified_at", "citation",
9
16
  ];
10
17
 
@@ -16,8 +23,22 @@ const BOARD_COLUMNS: HeadlessPaneColumn[] = [
16
23
  { key: "asOf", header: "As of", shrink: false }, { key: "status", header: "Status", stackPriority: 2 }, { key: "instrument", header: "Instrument", stackPriority: 3 },
17
24
  ];
18
25
 
26
+ const percentCell = (value: unknown) => policyRate(typeof value === "number" ? value : null);
27
+
28
+ /**
29
+ * The board's columns as CSV and NDJSON write them: the rate as a number (a
30
+ * target range's midpoint, which the board ranks and charts) with the range's
31
+ * bounds beside it, where the text draws "3.75-4.00%".
32
+ */
33
+ const EXPORT_BOARD_COLUMNS: HeadlessPaneColumn[] = BOARD_COLUMNS.flatMap((column): HeadlessPaneColumn[] => column.key !== "valueText" ? [column] : [
34
+ { key: "value", header: "Rate", align: "right", format: percentCell },
35
+ { key: "rangeLower", header: "Range lower", align: "right", format: percentCell },
36
+ { key: "rangeUpper", header: "Range upper", align: "right", format: percentCell },
37
+ ]);
38
+
19
39
  export const centralBankRatesHeadless: HeadlessPaneDefinition<"bundle"> = {
20
- discovery: { aliases: ["CBR", "CBRT"], dataRequirements: ["Gloom Cloud central-bank-rates endpoint"],
40
+ // A bank's name or short form typed as a function (`fn ECB`, `fn BOJ`) points here, unless a function owns the word (FOMC is WIRP).
41
+ discovery: { aliases: ["CBR", "CBRT"], suggestFor: CENTRAL_BANKS.flatMap(centralBankNames), dataRequirements: ["Gloom Cloud central-bank-rates endpoint"],
21
42
  limitations: ["Published policy observations with individual dates.", "Policy instruments differ.",
22
43
  "Next meetings come from each bank's published calendar; SAMA publishes none, and China's date is the monthly LPR fixing."] },
23
44
  shape: "bundle",
@@ -33,19 +54,24 @@ export const centralBankRatesHeadless: HeadlessPaneDefinition<"bundle"> = {
33
54
  // A row says "stale" in its status, not in a `stale` flag: hand the report its rows' staleness so its status line and a capture count them.
34
55
  const observations = data.rows.filter((row) => row.status !== "unavailable")
35
56
  .map((row) => ({ asOf: row.asOf, stale: row.status === "stale" }));
36
- const exported = data.rows.map(policyExportRow);
57
+ // Members with no policy rate stay off the board, as the pane keeps them, so every output counts the same rows; a note says why.
58
+ const listed = data.rows.filter((row) => !hasNoPolicyRate(row));
59
+ const exported = listed.map(policyExportRow);
37
60
  // A caveat on most rows ("Change date is the first observed new level...") is said once for the report.
38
61
  const shared = [...new Set(exported.flatMap((row) => row.notes))]
39
62
  .filter((note) => exported.filter((row) => row.notes.includes(note)).length > 1);
40
63
  const rows = exported.map((row) => ({ ...row, notes: row.notes.filter((note) => !shared.includes(note)) }));
41
- return { complete, freshness: { observations }, ...(shared.length ? { notes: shared } : {}), sections: [{ title: "Policy rates",
42
- columns: [...BOARD_COLUMNS, ...CITATION_FIELDS.map((key): HeadlessPaneColumn => ({ key, header: key, literalHeader: true }))],
64
+ const notes = [...shared, ...data.rows.filter(hasNoPolicyRate).map(policyUnlistedNote)];
65
+ return { complete, freshness: { observations }, ...(notes.length ? { notes } : {}), sections: [{ title: "Policy rates",
66
+ columns: [...EXPORT_BOARD_COLUMNS, ...CITATION_FIELDS.map((key): HeadlessPaneColumn => ({ key, header: key, literalHeader: true })),
67
+ { key: "notes", header: "notes", literalHeader: true, format: (value) => Array.isArray(value) ? value.join(" ") : "" }],
43
68
  rows,
44
- // The text keeps the board's table; each row's bank, next meeting and citation go under it.
69
+ // The text keeps the board's table; each row's bank, last and next decision, citation and own caveats go under it.
45
70
  textColumns: [...BOARD_COLUMNS, { key: "detail", header: "Detail", underRow: true }],
46
71
  textRows: rows.map((row, index) => {
47
- const source = data.rows[index]!;
48
- return { ...row, detail: `${row.central_bank ?? "No central bank"} · ${policyNextMeetingText(source)}\n${row.citation}` };
72
+ const source = listed[index]!;
73
+ const detail = [`${row.central_bank ?? "No central bank"} · ${policyLastDecisionText(source)}${policyNextMeetingText(source)}`, row.citation];
74
+ return { ...row, detail: [...detail, ...(row.notes.length ? [row.notes.join(" ")] : [])].join("\n") };
49
75
  }),
50
76
  }], errors, unavailableSymbols: data.rows.filter((row) => row.status === "unavailable" && !hasNoPolicyRate(row)).map((row) => row.id),
51
77
  // A year of daily history per row is most of the payload, so JSON carries it only on request.
@@ -20,7 +20,9 @@ export function policyHistory(row: CentralBankRow) {
20
20
  }
21
21
  export interface PolicyBoardRow extends MarketBoardRow { observation: CentralBankRow }
22
22
  export function policyBoardRow(row: CentralBankRow): PolicyBoardRow {
23
- return { id: row.id, label: row.label, labelDetail: row.instrument, value: row.value, valueText: policyLevel(row),
23
+ // One country's bank leads with its flag; the euro area's with the EU's.
24
+ const country = row.countryCodes.length === 1 ? row.countryCodes[0]! : row.label;
25
+ return { id: row.id, label: row.label, labelDetail: row.instrument, country, value: row.value, valueText: policyLevel(row),
24
26
  change: row.changeBps, changeText: policyChange(row), changeAsOf: row.lastChangeDate,
25
27
  percentile: row.percentile.value, asOf: row.asOf, status: row.status, observation: row,
26
28
  history: policyHistory(row).flatMap((point) => point.value == null ? [] : [{ date: new Date(point.date), close: point.value }]) };
@@ -97,27 +99,50 @@ export function policyNextMeetingText(row: CentralBankRow): string {
97
99
  }
98
100
 
99
101
  /**
100
- * What an export says about one row, history aside: the board's figures and
101
- * a citation block, `central_bank` to `citation`, named as CSV heads them.
102
- * Notes keep the caveats on the latest rate; the BIS series definition goes
103
- * with the history (`--history`).
102
+ * `last decision 2026-09-18 · ` (or `last LPR fixing`) before the next
103
+ * meeting on a row's detail line; nothing when the server gives no date.
104
+ */
105
+ export function policyLastDecisionText(row: CentralBankRow): string {
106
+ const decision = row.lastDecision;
107
+ if (!decision) return "";
108
+ return `last ${decision.kind === "fixing" ? "LPR fixing" : "decision"} ${decision.date} · `;
109
+ }
110
+
111
+ /**
112
+ * What an export says about one row, history aside: the board's figures, a
113
+ * target range's bounds as numbers (`rangeLower`, `rangeUpper`; the rate is
114
+ * its midpoint) and a citation block, `central_bank` to `citation`, named as
115
+ * CSV heads them. `last_decision` is the bank's latest kept decision on or
116
+ * before the observed change. Notes keep the caveats on the latest rate; the
117
+ * BIS series definition goes with the history (`--history`).
104
118
  */
105
119
  export function policyExportRow(row: CentralBankRow) {
106
120
  const board = policyBoardRow({ ...row, history: [] });
107
121
  const meeting = policyNextMeeting(row);
108
122
  return {
109
123
  id: row.id, label: row.label, instrument: row.instrument, central_bank: row.centralBank,
110
- value: row.value, unit: row.unit, range: row.range, valueText: board.valueText,
111
- changeBps: row.changeBps, changeText: board.changeText, lastChangeDate: row.lastChangeDate,
124
+ value: row.value, unit: row.unit, range: row.range, rangeLower: row.range?.lower ?? null, rangeUpper: row.range?.upper ?? null,
125
+ valueText: board.valueText, changeBps: row.changeBps, changeText: board.changeText, lastChangeDate: row.lastChangeDate,
112
126
  previousValue: row.previousValue, percentileValue: row.percentile.value,
113
127
  asOf: row.asOf, lagDays: row.lagDays, status: row.status, unavailableReason: row.unavailableReason,
114
128
  source: policySource(row), series_id: row.sourceSeriesIds.join(", ") || null, source_url: policySeriesUrls(row).join(" ") || null,
115
- as_of: row.asOf, next_meeting: meeting.text, next_meeting_kind: meeting.kind,
129
+ as_of: row.asOf, last_decision: row.lastDecision?.date ?? null, next_meeting: meeting.text, next_meeting_kind: meeting.kind,
116
130
  next_meeting_source_url: meeting.sourceUrl, next_meeting_verified_at: meeting.verifiedAt,
117
131
  citation: policyCitation(row), notes: row.notes.filter((note) => !row.seriesNotes?.includes(note)),
118
132
  };
119
133
  }
120
134
 
135
+ /**
136
+ * Why a member with no policy rate is left off the board and its exports:
137
+ * `Argentina is not listed: no adopted policy rate (BIS series D.AR, https://...).`
138
+ */
139
+ export function policyUnlistedNote(row: CentralBankRow): string {
140
+ const source = policySource(row);
141
+ const urls = policySeriesUrls(row);
142
+ const series = source && row.sourceSeriesIds.length > 0 ? `${source} series ${row.sourceSeriesIds.join(" and ")}${urls.length ? `, ${urls.join(" ")}` : ""}` : null;
143
+ return `${row.label} is not listed: ${row.instrument.toLowerCase()}${series ? ` (${series})` : ""}.`;
144
+ }
145
+
121
146
  /** Who a chart of the row's series cites: `FRED series ECBDFR`, published by the European Central Bank. */
122
147
  export function policySeriesAttribution(row: CentralBankRow): { source: string; publisher?: string } | null {
123
148
  const source = policySource(row);
@@ -286,6 +286,7 @@ export const changelogModule: PluginModule = {
286
286
  name: "Changelog",
287
287
  icon: "L",
288
288
  component: ChangelogPane,
289
+ reportFreshness: { status: "not-a-feed", basis: "release notes" },
289
290
  defaultPosition: "right",
290
291
  defaultMode: "floating",
291
292
  defaultFloatingSize: { width: 100, height: 32 },
@@ -59,6 +59,7 @@ const GROUP_ORDER = [
59
59
  "Inflation",
60
60
  "FX",
61
61
  "Energy",
62
+ "Commodities",
62
63
  "Volatility",
63
64
  "Sentiment",
64
65
  "Positioning",
@@ -416,6 +416,7 @@ export function panelsForSeries(series: readonly ChartSeriesSpec[], studies: rea
416
416
  ...(id === "rsi" || id === "macd" || id === "atr" ? { label: id.toUpperCase(), height: 0.28 } : {}),
417
417
  ...(id === "formula" ? { label: "Formula", height: 0.3 } : {}),
418
418
  ...(id === "correlation" ? { label: "Correlation", height: 0.3 } : {}),
419
+ ...(id === "decay" ? { label: "Decay", height: 0.3 } : {}),
419
420
  ...(id === "realized-vol" ? { label: "Realized Volatility", height: 0.3 } : {}),
420
421
  ...(/^panel-\d+$/.test(id) ? { label: `Panel ${id.slice("panel-".length)}`, height: 0.35 } : {}),
421
422
  }));
@@ -495,7 +496,7 @@ export function reconcilePanels(
495
496
  ): ChartPanelSpec[] {
496
497
  const defaults = panelsForSeries(series, studies);
497
498
  const requiredIds = new Set(defaults.map((panel) => panel.id));
498
- const managedStudyPanelIds = new Set(["volume", "rsi", "macd", "atr", "formula", "correlation", "realized-vol"]);
499
+ const managedStudyPanelIds = new Set(["volume", "rsi", "macd", "atr", "formula", "correlation", "realized-vol", "decay"]);
499
500
  const retained = existing.filter((panel) => (
500
501
  requiredIds.has(panel.id) || !managedStudyPanelIds.has(panel.id)
501
502
  ));