gloomberb 0.18.3 → 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 (886) 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/src/account/checkout-handoff.tsx +225 -0
  6. package/src/account/device-signin-dialog.tsx +2 -2
  7. package/src/account/device-signin.ts +2 -2
  8. package/src/account/upgrade-dialog.tsx +1 -1
  9. package/src/api-client/askg.ts +2 -2
  10. package/src/api-client/auth.ts +2 -2
  11. package/src/api-client/central-bank-rates.ts +6 -0
  12. package/src/api-client/collections.ts +5 -1
  13. package/src/api-client/cot.ts +2 -0
  14. package/src/api-client/crypto-etf-flows.ts +37 -0
  15. package/src/api-client/crypto-markets.ts +60 -0
  16. package/src/api-client/data.ts +84 -5
  17. package/src/api-client/doe.ts +15 -0
  18. package/src/api-client/equity-screener.ts +28 -0
  19. package/src/api-client/exchange-announcements.ts +38 -0
  20. package/src/api-client/fund-facts.ts +39 -0
  21. package/src/api-client/futures-curve.ts +9 -1
  22. package/src/api-client/hiring.ts +301 -0
  23. package/src/api-client/index.ts +22 -5
  24. package/src/api-client/market-discovery.ts +14 -0
  25. package/src/api-client/members.ts +6 -0
  26. package/src/api-client/mna.ts +6 -0
  27. package/src/api-client/paths.ts +18 -1
  28. package/src/api-client/request.ts +21 -4
  29. package/src/api-client/short-side-publications.ts +37 -0
  30. package/src/api-client/socket.ts +64 -4
  31. package/src/api-client/swap-curve.ts +35 -0
  32. package/src/api-client/themes.ts +7 -1
  33. package/src/api-client/types.ts +90 -2
  34. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  35. package/src/app/runtime/desktop-deeplink.ts +5 -5
  36. package/src/app/runtime/plugin-bindings.ts +2 -2
  37. package/src/app/runtime/startup.ts +13 -0
  38. package/src/app/star-prompt/status-line.tsx +2 -2
  39. package/src/architecture/core-builtin-allowlist.json +5 -2
  40. package/src/architecture/ui-literals-allowlist.json +3 -3
  41. package/src/architecture/ui-tokens-allowlist.json +1 -1
  42. package/src/brokers/broker-portfolio-sync.ts +37 -0
  43. package/src/brokers/directory.ts +11 -0
  44. package/src/brokers/logos.ts +3 -1
  45. package/src/brokers/signed-in/adapter.ts +304 -26
  46. package/src/brokers/signed-in/client.ts +14 -2
  47. package/src/brokers/signed-in/connect.ts +10 -2
  48. package/src/brokers/sync-broker-instance.ts +9 -1
  49. package/src/capabilities/chart-series.ts +22 -6
  50. package/src/cli/bank-section.ts +1 -1
  51. package/src/cli/base-converter.ts +23 -14
  52. package/src/cli/central-bank-events.ts +1 -1
  53. package/src/cli/command-options.ts +59 -4
  54. package/src/cli/commands/api.ts +9 -6
  55. package/src/cli/commands/broker.ts +2 -2
  56. package/src/cli/commands/command-utils.ts +2 -2
  57. package/src/cli/commands/crypto-hints.ts +7 -20
  58. package/src/cli/commands/fred-series.ts +4 -2
  59. package/src/cli/commands/keybindings.ts +2 -2
  60. package/src/cli/commands/market.ts +1117 -264
  61. package/src/cli/commands/news-hints.ts +2 -2
  62. package/src/cli/commands/news-search.ts +59 -6
  63. package/src/cli/commands/options-leaps.ts +41 -26
  64. package/src/cli/commands/overview.ts +121 -48
  65. package/src/cli/commands/plugins.ts +9 -9
  66. package/src/cli/commands/remote.ts +13 -10
  67. package/src/cli/commands/search.ts +64 -44
  68. package/src/cli/commands/system.ts +27 -19
  69. package/src/cli/commands/themes.ts +1 -1
  70. package/src/cli/commands/ticker.ts +192 -47
  71. package/src/cli/context.ts +3 -2
  72. package/src/cli/desktop-pane-shot-view.ts +2 -2
  73. package/src/cli/desktop-pane-shot.ts +1 -1
  74. package/src/cli/earnings-calendar.ts +4 -4
  75. package/src/cli/entry.ts +18 -1
  76. package/src/cli/errors.ts +20 -2
  77. package/src/cli/exit-codes.ts +1 -1
  78. package/src/cli/export-source.ts +10 -4
  79. package/src/cli/freshness.ts +2 -8
  80. package/src/cli/fx-forward.ts +130 -0
  81. package/src/cli/fx-pair.ts +10 -9
  82. package/src/cli/glossary.ts +64 -20
  83. package/src/cli/help.ts +19 -13
  84. package/src/cli/history-export.ts +126 -0
  85. package/src/cli/history-rows.ts +196 -43
  86. package/src/cli/index-roots.ts +5 -45
  87. package/src/cli/index.ts +24 -18
  88. package/src/cli/known-gaps.ts +118 -25
  89. package/src/cli/listing-arg.ts +300 -41
  90. package/src/cli/name-entries.ts +30 -18
  91. package/src/cli/native-smoke.ts +2 -0
  92. package/src/cli/not-a-ticker.ts +19 -9
  93. package/src/cli/opentui-libc.ts +5 -0
  94. package/src/cli/options.ts +12 -0
  95. package/src/cli/pane-functions/capabilities.ts +75 -20
  96. package/src/cli/pane-functions/catalog.ts +24 -23
  97. package/src/cli/pane-functions/discovery.ts +1 -1
  98. package/src/cli/pane-functions/dom.ts +10 -7
  99. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  100. package/src/cli/pane-functions/freshness.ts +174 -23
  101. package/src/cli/pane-functions/function-help.ts +66 -27
  102. package/src/cli/pane-functions/headless.ts +24 -11
  103. package/src/cli/pane-functions/index.ts +18 -16
  104. package/src/cli/pane-functions/options.ts +22 -2
  105. package/src/cli/pane-functions/report-notices.ts +12 -4
  106. package/src/cli/pane-functions/report.ts +5 -5
  107. package/src/cli/pane-functions/resolver.ts +47 -10
  108. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  109. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  110. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  111. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  112. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  113. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  114. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  115. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  116. package/src/cli/pane-functions/shot-options.ts +8 -3
  117. package/src/cli/peso-dollar-rates.ts +79 -0
  118. package/src/cli/quote-answer.ts +38 -3
  119. package/src/cli/report-tables.ts +61 -16
  120. package/src/cli/restore-plugins.ts +29 -14
  121. package/src/cli/result.ts +111 -22
  122. package/src/cli/series-search.ts +5 -5
  123. package/src/cli/statement-filings.ts +19 -0
  124. package/src/cli/suffixed-listing-rows.ts +49 -0
  125. package/src/cli/symbol-args.ts +50 -15
  126. package/src/cli/symbol-cells.ts +120 -0
  127. package/src/cli/symbol-list.ts +198 -68
  128. package/src/cli/symbol-rows.ts +131 -25
  129. package/src/cli/ticker-resolution.ts +108 -11
  130. package/src/cli/treasury-yields.ts +5 -5
  131. package/src/cli/types.ts +2 -0
  132. package/src/cli/uncovered-instrument.ts +3 -3
  133. package/src/cli/unknown-command.ts +6 -6
  134. package/src/components/chart/composite/chart-layout.ts +131 -0
  135. package/src/components/chart/composite/composite-chart.tsx +373 -16
  136. package/src/components/chart/composite/interactions.ts +7 -2
  137. package/src/components/chart/composite/legend.tsx +75 -3
  138. package/src/components/chart/composite/levels.ts +5 -0
  139. package/src/components/chart/composite/panel-gestures.ts +41 -0
  140. package/src/components/chart/composite/panel-labels.ts +2 -1
  141. package/src/components/chart/composite/panel-surface.tsx +305 -11
  142. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  143. package/src/components/chart/composite/rasterizer.ts +44 -19
  144. package/src/components/chart/composite/scene.ts +16 -4
  145. package/src/components/chart/composite/text-renderer.ts +48 -21
  146. package/src/components/chart/composite/toolbar.tsx +52 -5
  147. package/src/components/chart/composite/types.ts +13 -0
  148. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  149. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  150. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  151. package/src/components/chart/composite/value-scale.ts +149 -0
  152. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  153. package/src/components/chart/core/pointer.ts +2 -0
  154. package/src/components/chart-table/index.ts +2 -0
  155. package/src/components/chart-table/read.tsx +74 -0
  156. package/src/components/command-bar/commands/collection-list.ts +41 -19
  157. package/src/components/command-bar/commands/collection.ts +9 -3
  158. package/src/components/command-bar/commands/direct/index.ts +1 -1
  159. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  160. package/src/components/command-bar/pane-templates/items.ts +59 -6
  161. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  162. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  163. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  164. package/src/components/command-bar/routes/root/results.ts +18 -4
  165. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  166. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  167. package/src/components/command-bar/routes/root/selection.ts +6 -1
  168. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  169. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  170. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  171. package/src/components/command-bar/selection-runtime.ts +50 -10
  172. package/src/components/command-bar/surface/index.tsx +24 -2
  173. package/src/components/command-bar/view-model.ts +7 -0
  174. package/src/components/command-bar/workflow/broker.ts +2 -2
  175. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  176. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  177. package/src/components/command-bar/workflow/fields.ts +8 -2
  178. package/src/components/command-bar/workflow/ops.ts +18 -4
  179. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  180. package/src/components/command-bar/workflow/submit.ts +3 -2
  181. package/src/components/command-bar/workflow/tickers.ts +5 -1
  182. package/src/components/company-logo.tsx +39 -0
  183. package/src/components/data-table/sections.ts +4 -2
  184. package/src/components/form-modal/confirm.tsx +2 -1
  185. package/src/components/form-modal/request.ts +5 -0
  186. package/src/components/form-modal/routes.ts +4 -1
  187. package/src/components/index.ts +12 -6
  188. package/src/components/layout/header.tsx +7 -6
  189. package/src/components/layout/market-summary.ts +9 -6
  190. package/src/components/layout/pane/capture-status.ts +1 -1
  191. package/src/components/layout/pane/footer/model.ts +11 -5
  192. package/src/components/layout/shell/chrome.ts +19 -6
  193. package/src/components/layout/shell/index.tsx +2 -1
  194. package/src/components/layout/shell/pane/layers.tsx +19 -1
  195. package/src/components/layout/status-bar.tsx +2 -2
  196. package/src/components/market-board/index.tsx +11 -3
  197. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  198. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  199. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  200. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  201. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  202. package/src/components/pane-settings-dialog/value.ts +8 -1
  203. package/src/components/pane-settings-dialog.tsx +44 -13
  204. package/src/components/sign-in-code-panel.tsx +27 -19
  205. package/src/components/ui/column-grid.tsx +48 -4
  206. package/src/components/ui/country-flag-art.tsx +180 -0
  207. package/src/components/ui/country-flag.tsx +118 -12
  208. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  209. package/src/components/ui/data-table/opentui/model.ts +109 -2
  210. package/src/components/ui/data-table/types.ts +1 -1
  211. package/src/components/ui/display.tsx +5 -1
  212. package/src/components/ui/index.ts +3 -1
  213. package/src/components/ui/logo-mark.tsx +11 -2
  214. package/src/components/ui/query-bar.tsx +1 -1
  215. package/src/components/ui/row-notes.ts +79 -0
  216. package/src/core/state/app/layout.ts +4 -4
  217. package/src/data/cached-query.ts +8 -1
  218. package/src/data/config/chart-settings.ts +3 -3
  219. package/src/data/config/home.ts +10 -2
  220. package/src/data/config/layout.ts +4 -1
  221. package/src/data/config/store/node.ts +4 -0
  222. package/src/data/config/store/normalize.ts +13 -3
  223. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  224. package/src/data/json-storage.ts +2 -0
  225. package/src/data/saved-copies.ts +84 -0
  226. package/src/docs/snapshot.json +65 -32
  227. package/src/i18n/es.ts +29 -12
  228. package/src/i18n/ja.ts +29 -12
  229. package/src/i18n/ko.ts +29 -12
  230. package/src/i18n/zh-cn.ts +29 -12
  231. package/src/i18n/zh-tw.ts +29 -12
  232. package/src/layout/desks.ts +1 -1
  233. package/src/layout/launch-layout.ts +1 -1
  234. package/src/layout/pane-manager/floating-actions.ts +5 -3
  235. package/src/layout/pane-manager/floating.ts +43 -1
  236. package/src/layout/pane-manager/index.ts +2 -0
  237. package/src/layout/selected-ticker.ts +74 -0
  238. package/src/market-data/depositary-pair.ts +251 -0
  239. package/src/market-data/futures-board.ts +1 -1
  240. package/src/market-data/futures-contract.ts +124 -40
  241. package/src/market-data/fx-closes.ts +17 -2
  242. package/src/market-data/hooks.tsx +19 -11
  243. package/src/market-data/market/change-basis.ts +49 -7
  244. package/src/market-data/market/freshness.ts +34 -5
  245. package/src/market-data/market/period-return.ts +163 -0
  246. package/src/market-data/market/trading-sessions.ts +13 -0
  247. package/src/market-data/market/year-to-date.ts +8 -13
  248. package/src/market-data/performance.ts +20 -14
  249. package/src/market-data/perpetual-premium.ts +32 -0
  250. package/src/market-data/published-apac-sessions.ts +2 -1
  251. package/src/market-data/published-futures-sessions.ts +69 -0
  252. package/src/market-data/quotes/contributions.ts +5 -1
  253. package/src/market-data/quotes/freshness.ts +42 -0
  254. package/src/market-data/quotes/intraday-bar.ts +37 -0
  255. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  256. package/src/market-data/quotes/resolution.ts +4 -2
  257. package/src/market-data/tax-equivalent.ts +1 -1
  258. package/src/market-data/ticker-price-returns.ts +3 -1
  259. package/src/news/aggregator.ts +5 -1
  260. package/src/news/news-model.ts +4 -0
  261. package/src/news/types.ts +11 -0
  262. package/src/notifications/app-notifier.ts +1 -1
  263. package/src/plugins/auto-update.ts +2 -2
  264. package/src/plugins/builtin/account-management/index.tsx +1 -0
  265. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  266. package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
  267. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  268. package/src/plugins/builtin/alerts/cli.ts +485 -76
  269. package/src/plugins/builtin/alerts/command.ts +5 -10
  270. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  271. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  272. package/src/plugins/builtin/alerts/events.ts +107 -5
  273. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  274. package/src/plugins/builtin/alerts/format.ts +2 -1
  275. package/src/plugins/builtin/alerts/headless.ts +100 -39
  276. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  277. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  278. package/src/plugins/builtin/alerts/index.tsx +45 -18
  279. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  280. package/src/plugins/builtin/alerts/levels.ts +3 -3
  281. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  282. package/src/plugins/builtin/alerts/price.ts +35 -0
  283. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  284. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  285. package/src/plugins/builtin/alerts/research-rules.ts +76 -3
  286. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  287. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  288. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  289. package/src/plugins/builtin/alerts/storage.ts +13 -10
  290. package/src/plugins/builtin/alerts/targets.ts +12 -3
  291. package/src/plugins/builtin/analytics/index.tsx +8 -2
  292. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  293. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  294. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  295. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  296. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  297. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  298. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  299. package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
  300. package/src/plugins/builtin/attention/pane.tsx +6 -2
  301. package/src/plugins/builtin/awards/headless.ts +7 -3
  302. package/src/plugins/builtin/awards/model.ts +18 -0
  303. package/src/plugins/builtin/awards/pane.tsx +6 -3
  304. package/src/plugins/builtin/backtest/headless.ts +3 -3
  305. package/src/plugins/builtin/backtest/rules.ts +41 -5
  306. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  307. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  308. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  309. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  310. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  311. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  312. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  313. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  314. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  315. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  316. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  317. package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
  318. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  319. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  320. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  321. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  322. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  323. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  324. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  325. package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
  326. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  327. package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
  328. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  329. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  330. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  331. package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
  332. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  333. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  334. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  335. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  336. package/src/plugins/builtin/catalysts/model.ts +49 -4
  337. package/src/plugins/builtin/catalysts/pane.tsx +6 -8
  338. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  339. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  340. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  341. package/src/plugins/builtin/changelog/index.tsx +1 -0
  342. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  343. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  344. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  345. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  346. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  347. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  348. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  349. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  350. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  351. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  352. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  353. package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
  354. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  355. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  356. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  357. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  358. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  359. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  360. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  361. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  362. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  363. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  364. package/src/plugins/builtin/chat/people.ts +1 -1
  365. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  366. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  367. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  368. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  369. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  370. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  371. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  372. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  373. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  374. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  375. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  376. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  377. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  378. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  379. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  380. package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
  381. package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
  382. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  383. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  384. package/src/plugins/builtin/company-attention/client.ts +117 -1
  385. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  386. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  387. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  388. package/src/plugins/builtin/company-attention/model.ts +33 -7
  389. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  390. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  391. package/src/plugins/builtin/company-attention/pane.tsx +79 -23
  392. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  393. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  394. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  395. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  396. package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
  397. package/src/plugins/builtin/composite-plugins.ts +2 -1
  398. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  399. package/src/plugins/builtin/connections/index.ts +1 -0
  400. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  401. package/src/plugins/builtin/correlation/headless.ts +5 -3
  402. package/src/plugins/builtin/cot/headless.ts +28 -6
  403. package/src/plugins/builtin/cot/model.ts +12 -0
  404. package/src/plugins/builtin/cot/pane.tsx +3 -2
  405. package/src/plugins/builtin/cpi/headless.ts +5 -1
  406. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  407. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  408. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  409. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  410. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  411. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  412. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  413. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  414. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  415. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  416. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  417. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  418. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  419. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  420. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  421. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  422. package/src/plugins/builtin/debug/index.tsx +1 -0
  423. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  424. package/src/plugins/builtin/docs/index.tsx +1 -1
  425. package/src/plugins/builtin/docs/pane.tsx +2 -2
  426. package/src/plugins/builtin/docs/targets.ts +1 -1
  427. package/src/plugins/builtin/doe/client.ts +5 -1
  428. package/src/plugins/builtin/doe/headless.ts +12 -6
  429. package/src/plugins/builtin/doe/model.ts +43 -6
  430. package/src/plugins/builtin/doe/pane.tsx +18 -11
  431. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  432. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  433. package/src/plugins/builtin/earnings/board.tsx +253 -50
  434. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  435. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  436. package/src/plugins/builtin/earnings/client.ts +4 -3
  437. package/src/plugins/builtin/earnings/headless.ts +53 -15
  438. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  439. package/src/plugins/builtin/earnings/index.tsx +15 -22
  440. package/src/plugins/builtin/earnings/model.ts +17 -12
  441. package/src/plugins/builtin/earnings/scope.ts +89 -0
  442. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  443. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  444. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  445. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  446. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  447. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  448. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  449. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  450. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  451. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  452. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  453. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  454. package/src/plugins/builtin/econ/index.tsx +112 -25
  455. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  456. package/src/plugins/builtin/econ/types.ts +32 -1
  457. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  458. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  459. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  460. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  461. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  462. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  463. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  464. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  465. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  466. package/src/plugins/builtin/equity-screener/headless.ts +68 -6
  467. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  468. package/src/plugins/builtin/equity-screener/model.ts +6 -0
  469. package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
  470. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  471. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  472. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  473. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  474. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  475. package/src/plugins/builtin/exposure/headless.ts +51 -6
  476. package/src/plugins/builtin/exposure/index.tsx +28 -2
  477. package/src/plugins/builtin/exposure/model.ts +7 -0
  478. package/src/plugins/builtin/exposure/pane.tsx +6 -1
  479. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  480. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  481. package/src/plugins/builtin/futures/model.ts +10 -9
  482. package/src/plugins/builtin/futures/returns.ts +8 -36
  483. package/src/plugins/builtin/futures/table.tsx +1 -1
  484. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  485. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  486. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  487. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  488. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  489. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  490. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  491. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  492. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  493. package/src/plugins/builtin/gpu/headless.ts +9 -4
  494. package/src/plugins/builtin/help/index.tsx +13 -3
  495. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  496. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  497. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  498. package/src/plugins/builtin/holders/client.ts +13 -1
  499. package/src/plugins/builtin/holders/format.ts +28 -17
  500. package/src/plugins/builtin/holders/headless.ts +46 -7
  501. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  502. package/src/plugins/builtin/holders/pane.tsx +79 -15
  503. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  504. package/src/plugins/builtin/holders/table-model.ts +2 -1
  505. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  506. package/src/plugins/builtin/insider/headless.ts +1 -1
  507. package/src/plugins/builtin/insider/signals-client.ts +1 -1
  508. package/src/plugins/builtin/insider/signals.tsx +1 -1
  509. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  510. package/src/plugins/builtin/iv-history/client.ts +20 -0
  511. package/src/plugins/builtin/iv-history/headless.ts +4 -4
  512. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  513. package/src/plugins/builtin/iv-history/model.ts +13 -4
  514. package/src/plugins/builtin/iv-history/series.ts +3 -1
  515. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  516. package/src/plugins/builtin/jobs/model.ts +4 -15
  517. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  518. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  519. package/src/plugins/builtin/jodi/pane.tsx +10 -4
  520. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  521. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  522. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  523. package/src/plugins/builtin/macro-day/model.ts +29 -8
  524. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  525. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  526. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  527. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  528. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  529. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  530. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  531. package/src/plugins/builtin/market-movers/client.ts +33 -19
  532. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  533. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  534. package/src/plugins/builtin/market-movers/screener.ts +29 -8
  535. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  536. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  537. package/src/plugins/builtin/market-movers/table.ts +4 -1
  538. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  539. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  540. package/src/plugins/builtin/members/headless.ts +6 -5
  541. package/src/plugins/builtin/members/pane.tsx +11 -4
  542. package/src/plugins/builtin/mna/client.ts +1 -0
  543. package/src/plugins/builtin/mna/detail.tsx +20 -32
  544. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  545. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  546. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  547. package/src/plugins/builtin/mna/headless.ts +6 -13
  548. package/src/plugins/builtin/mna/model.ts +93 -15
  549. package/src/plugins/builtin/mna/pane.tsx +5 -4
  550. package/src/plugins/builtin/news/headless.ts +12 -6
  551. package/src/plugins/builtin/news/index.tsx +10 -1
  552. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  553. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  554. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  555. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  556. package/src/plugins/builtin/notes/cli.ts +49 -14
  557. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  558. package/src/plugins/builtin/options/carry.ts +2 -2
  559. package/src/plugins/builtin/options/index.tsx +1 -1
  560. package/src/plugins/builtin/options/view.tsx +10 -10
  561. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  562. package/src/plugins/builtin/options-scenario/client.ts +123 -28
  563. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  564. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  565. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  566. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  567. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  568. package/src/plugins/builtin/options-scenario/headless.ts +36 -15
  569. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  570. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  571. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  572. package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
  573. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  574. package/src/plugins/builtin/perps/freshness.ts +8 -5
  575. package/src/plugins/builtin/perps/headless.ts +3 -1
  576. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  577. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  578. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  579. package/src/plugins/builtin/perps/pane.tsx +36 -17
  580. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  581. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  582. package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
  583. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
  584. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  585. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  586. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  587. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  588. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  589. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  590. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  591. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  592. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  593. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  594. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  595. package/src/plugins/builtin/power/client.ts +4 -1
  596. package/src/plugins/builtin/power/evidence.ts +22 -7
  597. package/src/plugins/builtin/power/gas.tsx +5 -1
  598. package/src/plugins/builtin/power/pane.tsx +8 -1
  599. package/src/plugins/builtin/power/prices.tsx +10 -3
  600. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  601. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  602. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  603. package/src/plugins/builtin/research/event-model.ts +3 -1
  604. package/src/plugins/builtin/research/event-sources.ts +12 -4
  605. package/src/plugins/builtin/research/index.tsx +2 -1
  606. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  607. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  608. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  609. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  610. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  611. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  612. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  613. package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
  614. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  615. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  616. package/src/plugins/builtin/sec/forms.ts +13 -0
  617. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  618. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  619. package/src/plugins/builtin/sec/headless.ts +85 -8
  620. package/src/plugins/builtin/sec/index.tsx +58 -12
  621. package/src/plugins/builtin/sectors/client.ts +2 -1
  622. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  623. package/src/plugins/builtin/series-inventory.ts +2 -0
  624. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  625. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  626. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  627. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  628. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  629. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  630. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  631. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  632. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  633. package/src/plugins/builtin/shared/research-trading.tsx +21 -0
  634. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  635. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  636. package/src/plugins/builtin/short-interest/client.ts +2 -0
  637. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  638. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  639. package/src/plugins/builtin/short-interest/model.ts +70 -19
  640. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  641. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  642. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  643. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  644. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  645. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  646. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  647. package/src/plugins/builtin/short-interest/types.ts +9 -0
  648. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  649. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  650. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  651. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  652. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  653. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  654. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  655. package/src/plugins/builtin/themes/headless.ts +8 -5
  656. package/src/plugins/builtin/themes/model.ts +14 -4
  657. package/src/plugins/builtin/themes/pane.tsx +24 -5
  658. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  659. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  660. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  661. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  662. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  663. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  664. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  665. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  666. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  667. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  668. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  669. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  670. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  671. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  672. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  673. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  674. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  675. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  676. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  677. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  678. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  679. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  680. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  681. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  682. package/src/plugins/builtin/vol-surface/client.ts +5 -4
  683. package/src/plugins/builtin/vol-surface/headless.ts +12 -3
  684. package/src/plugins/builtin/vol-surface/model.ts +4 -2
  685. package/src/plugins/builtin/volatility/model.ts +5 -10
  686. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  687. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  688. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  689. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  690. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  691. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  692. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  693. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  694. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  695. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  696. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  697. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  698. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  699. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  700. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  701. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  702. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  703. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  704. package/src/plugins/bundle.ts +1 -1
  705. package/src/plugins/catalog-browser.ts +2 -1
  706. package/src/plugins/compat.ts +3 -3
  707. package/src/plugins/dependencies.ts +75 -14
  708. package/src/plugins/extracted-plugins.ts +132 -34
  709. package/src/plugins/host-link.ts +149 -34
  710. package/src/plugins/installer.ts +35 -10
  711. package/src/plugins/loader.ts +206 -38
  712. package/src/plugins/manager-node.ts +2 -2
  713. package/src/plugins/marketplace/deactivation.ts +22 -0
  714. package/src/plugins/marketplace/model.ts +2 -2
  715. package/src/plugins/marketplace/packs.ts +1 -1
  716. package/src/plugins/ownership.ts +1 -1
  717. package/src/plugins/peer-retry.ts +155 -0
  718. package/src/plugins/registry/contributions.ts +3 -1
  719. package/src/plugins/registry/index.ts +4 -3
  720. package/src/plugins/registry/plugin-state.ts +3 -0
  721. package/src/plugins/seed-notice.ts +20 -0
  722. package/src/plugins/shortcut-argument.ts +39 -0
  723. package/src/portfolio/allocation.ts +1 -1
  724. package/src/portfolio/collection-name.ts +31 -0
  725. package/src/portfolio/column-values.ts +10 -8
  726. package/src/portfolio/cost-fx.ts +296 -19
  727. package/src/portfolio/manual-lots.ts +75 -0
  728. package/src/portfolio/mutations.ts +60 -22
  729. package/src/portfolio/suffixed-listings.ts +209 -0
  730. package/src/portfolio/totals-currency.ts +24 -0
  731. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  732. package/src/public/time-series.ts +11 -8
  733. package/src/remote/schema.ts +1 -1
  734. package/src/renderers/browser/config-host.ts +3 -0
  735. package/src/renderers/browser/main.tsx +23 -3
  736. package/src/renderers/dom/boot-frame-page.ts +187 -0
  737. package/src/renderers/dom/boot-frame.ts +237 -0
  738. package/src/renderers/dom/build-assets.ts +46 -8
  739. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  740. package/src/renderers/dom/host/ascii-text.ts +16 -2
  741. package/src/renderers/dom/host/box.tsx +43 -2
  742. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  743. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  744. package/src/renderers/dom/host/image-surface.tsx +49 -0
  745. package/src/renderers/dom/host/media-surface.tsx +24 -18
  746. package/src/renderers/dom/host/mouse.ts +30 -0
  747. package/src/renderers/dom/host/size-observer.ts +1 -1
  748. package/src/renderers/dom/host/style.ts +1 -1
  749. package/src/renderers/dom/styles.css +76 -0
  750. package/src/renderers/dom/test-utils.tsx +2 -1
  751. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  752. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  753. package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
  754. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  755. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  756. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  757. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  758. package/src/renderers/electrobun/bun/index.ts +11 -1
  759. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  760. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  761. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  762. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  763. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  764. package/src/renderers/electrobun/view/config-host.ts +3 -0
  765. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  766. package/src/renderers/electrobun/view/main.tsx +30 -4
  767. package/src/renderers/electrobun/view/news-source.ts +5 -5
  768. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
  769. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  770. package/src/renderers/opentui/browser-open.ts +88 -0
  771. package/src/renderers/opentui/host.tsx +5 -14
  772. package/src/renderers/opentui/image/loader.ts +3 -2
  773. package/src/renderers/opentui/native-smoke.ts +10 -1
  774. package/src/renderers/opentui/pane-image/index.ts +2 -1
  775. package/src/renderers/opentui/render-lib.ts +253 -0
  776. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  777. package/src/renderers/opentui/start.tsx +1 -1
  778. package/src/renderers/share/layout-view.tsx +2 -2
  779. package/src/renderers/share/main.tsx +4 -4
  780. package/src/renderers/share/view.tsx +1 -1
  781. package/src/series/resolve.ts +22 -6
  782. package/src/series/standalone.ts +30 -2
  783. package/src/series/types.ts +8 -1
  784. package/src/shares/api.ts +4 -1
  785. package/src/shares/pane.ts +1 -1
  786. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  787. package/src/sources/gloomberb-cloud/index.ts +32 -16
  788. package/src/sources/gloomberb-cloud/news.ts +39 -0
  789. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  790. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  791. package/src/sources/listing-symbols.ts +37 -0
  792. package/src/sources/provider-errors.ts +16 -2
  793. package/src/sources/provider-router/financial-routes.ts +21 -3
  794. package/src/sources/provider-router/financials.ts +15 -2
  795. package/src/sources/provider-router/history.ts +30 -8
  796. package/src/sources/provider-router/primary.ts +1 -1
  797. package/src/state/app/bootstrap.ts +23 -0
  798. package/src/state/hooks/inline-tickers.ts +30 -5
  799. package/src/sync/controller.ts +1 -1
  800. package/src/sync/core-contributors.ts +46 -14
  801. package/src/team/collections-sync.ts +2 -1
  802. package/src/team/collections.ts +178 -17
  803. package/src/telemetry/crash-reports.ts +17 -4
  804. package/src/test-support/builtin-panes.ts +1 -1
  805. package/src/theme/colors.ts +3 -30
  806. package/src/theme/ui-tokens.ts +37 -1
  807. package/src/tickers/company-name-listing.ts +210 -33
  808. package/src/tickers/index-roots.ts +47 -0
  809. package/src/tickers/instrument-label.ts +5 -1
  810. package/src/tickers/metadata.ts +10 -0
  811. package/src/tickers/perpetual-listing.ts +5 -2
  812. package/src/tickers/search/home-listing.ts +75 -0
  813. package/src/tickers/search/index.ts +76 -27
  814. package/src/tickers/search/ranking.ts +54 -4
  815. package/src/tickers/search/upsert.ts +29 -0
  816. package/src/tickers/terminal-codes.ts +416 -40
  817. package/src/time-series/bar-aggregation.ts +243 -0
  818. package/src/time-series/base-series.ts +307 -0
  819. package/src/time-series/chart-bounds.ts +349 -0
  820. package/src/time-series/chart-seed.ts +142 -0
  821. package/src/time-series/economic.ts +5 -0
  822. package/src/time-series/fundamentals.ts +18 -3
  823. package/src/time-series/history-acquisition.ts +499 -0
  824. package/src/time-series/leverage.ts +100 -0
  825. package/src/time-series/market-resolution.ts +4 -2
  826. package/src/time-series/market.ts +2 -1
  827. package/src/time-series/price-comparison.ts +175 -30
  828. package/src/time-series/range.ts +9 -1
  829. package/src/time-series/resolution.ts +174 -16
  830. package/src/time-series/resolve-context.ts +157 -0
  831. package/src/time-series/resolve.ts +126 -1577
  832. package/src/time-series/series-name.ts +38 -0
  833. package/src/time-series/series-presentation.ts +208 -0
  834. package/src/time-series/spec.ts +11 -6
  835. package/src/time-series/studies.ts +109 -12
  836. package/src/time-series/tail-reconcile.ts +121 -0
  837. package/src/time-series/types.ts +29 -2
  838. package/src/time-series/use-chart-resolution.ts +4 -5
  839. package/src/types/broker.ts +10 -0
  840. package/src/types/config.ts +14 -0
  841. package/src/types/data-provider.ts +14 -0
  842. package/src/types/exchange-rate.ts +6 -0
  843. package/src/types/financials.ts +104 -7
  844. package/src/types/headless.ts +76 -6
  845. package/src/types/instrument.ts +3 -0
  846. package/src/types/plugin.ts +65 -8
  847. package/src/types/price-history.ts +2 -0
  848. package/src/types/ticker.ts +13 -0
  849. package/src/types/trading.ts +59 -1
  850. package/src/ui/ascii-font.ts +13 -1
  851. package/src/ui/host.tsx +6 -1
  852. package/src/updater/command-names.ts +175 -0
  853. package/src/updater/index.ts +10 -5
  854. package/src/utils/calendar-date.ts +32 -15
  855. package/src/utils/cash-runway.ts +241 -0
  856. package/src/utils/central-banks.ts +42 -0
  857. package/src/utils/cli-output.ts +259 -80
  858. package/src/utils/contract-month.ts +16 -0
  859. package/src/utils/crypto-pair.ts +61 -3
  860. package/src/utils/currency-units.ts +29 -0
  861. package/src/utils/datetime-format.ts +10 -0
  862. package/src/utils/debug-log.ts +1 -1
  863. package/src/utils/decimal-comma.ts +39 -6
  864. package/src/utils/exchange-announcements.ts +71 -0
  865. package/src/utils/exchange-rate-snapshot.ts +10 -0
  866. package/src/utils/financial-statements.ts +83 -4
  867. package/src/utils/format.ts +34 -16
  868. package/src/utils/fundamentals.ts +150 -34
  869. package/src/utils/fx-market-hours.ts +10 -1
  870. package/src/utils/latest-financial-period.ts +8 -0
  871. package/src/utils/linux-libc.ts +54 -0
  872. package/src/utils/market-capitalization.ts +111 -15
  873. package/src/utils/max-drawdown.ts +64 -0
  874. package/src/utils/network-failure.ts +83 -0
  875. package/src/utils/option-expiry.ts +139 -7
  876. package/src/utils/price-earnings.ts +11 -5
  877. package/src/utils/price-history.ts +18 -8
  878. package/src/utils/price-window.ts +131 -0
  879. package/src/utils/release-date.ts +47 -0
  880. package/src/utils/sec.ts +3 -0
  881. package/src/utils/throttled-fetch.ts +11 -4
  882. package/src/utils/trailing-eps.ts +67 -0
  883. package/src/utils/typed-text.ts +21 -0
  884. package/src/utils/utc-time.ts +24 -1
  885. package/src/version.ts +6 -0
  886. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -0,0 +1,229 @@
1
+ import type { DataTableColumn, StatItem } from "../../../components";
2
+ import type { HiringCompanySignals, HiringMovePosting, HiringSignalFlag, HiringSignalGroup, HiringSignalHold, HiringSignalKind, HiringSignalSkip, HiringSignalsBoard, HiringSignalsFilter, HiringWeekSignal } from "../../../api-client/hiring";
3
+ import { formatNumber } from "../../../utils/format";
4
+ import { countOf, freePreviewNote, previewCantFilter } from "../shared/free-preview";
5
+ import { humanLabel } from "../shared/research-cells";
6
+ import type { AttentionRow } from "./model";
7
+
8
+ /**
9
+ * HIRE's Expansion and Anomalies tabs (docs/hiring-app-attention.md):
10
+ * a company's first roles in a country or city, one place or role family far
11
+ * above its own weeks, and role families it never posted, in the latest
12
+ * complete week. "Never" goes back to the company's first read: a first is
13
+ * the first seen since then, never the first ever, and every row says since when.
14
+ */
15
+ export type SignalsView = "signals" | "postings";
16
+ export const SIGNALS_VIEWS: ReadonlyArray<{ value: SignalsView; label: string }> = [{ value: "signals", label: "Signals" }, { value: "postings", label: "Postings" }];
17
+ export const isSignalsView = (value: string): value is SignalsView => SIGNALS_VIEWS.some((view) => view.value === value);
18
+ export const SIGNAL_GROUPS: readonly HiringSignalGroup[] = ["expansion", "anomalies"];
19
+ export const isSignalGroup = (value: string): value is HiringSignalGroup => (SIGNAL_GROUPS as readonly string[]).includes(value);
20
+ export const SIGNAL_FILTERS: Record<HiringSignalGroup, ReadonlyArray<{ value: HiringSignalsFilter; label: string }>> = {
21
+ expansion: [{ value: "all", label: "All" }, { value: "new_country", label: "New countries" }, { value: "new_city", label: "New cities" }],
22
+ anomalies: [{ value: "all", label: "All" }, { value: "location_spike", label: "Place spikes" }, { value: "function_spike", label: "Role family spikes" }, { value: "new_role_type", label: "New role families" }],
23
+ };
24
+ export const isSignalsFilter = (group: HiringSignalGroup, value: string): value is HiringSignalsFilter => SIGNAL_FILTERS[group].some((filter) => filter.value === value);
25
+
26
+ const KIND_LABELS: Record<HiringSignalKind, string> = {
27
+ new_country: "New country", new_city: "New city", location_spike: "Place spike", function_spike: "Family spike", new_role_type: "New family",
28
+ };
29
+ /** Shown, ranked last: the count may not keep to its week. */
30
+ const FLAG_LABELS: Record<HiringSignalFlag, string> = { few_reads: "Few reads", one_day_batch: "One-day batch" };
31
+ const HOLD_LABELS: Record<HiringSignalHold, string> = {
32
+ unmeasured: "Week not measured: the collection changed",
33
+ short_watch: "Read for under three weeks before it",
34
+ recounted: "Postings well above its new roles (re-listed roles)",
35
+ };
36
+ const SKIP_LABELS: Record<HiringSignalSkip, string> = {
37
+ thin_history: "Under 6 weeks of reads or 100 earlier postings",
38
+ unplaced_history: "Under 90% of earlier postings placed",
39
+ unplaced_cities: "Under 90% of earlier postings naming a city",
40
+ unclassified_history: "Under 90% of earlier postings with a role family",
41
+ reformatted: "Its boards wrote their places anew",
42
+ posted_before_watch: "Roles dated before its first read",
43
+ similar_city: "A new city named like a known one",
44
+ one_day: "A spike mostly posted on one day",
45
+ };
46
+ const kindText = (kind: HiringSignalKind) => KIND_LABELS[kind] ?? humanLabel(kind);
47
+ const flagText = (signal: Pick<HiringWeekSignal, "flags">) => signal.flags.map((flag) => FLAG_LABELS[flag] ?? humanLabel(flag)).join(", ") || null;
48
+ export const holdText = (hold: HiringSignalHold) => HOLD_LABELS[hold] ?? humanLabel(hold);
49
+ const skipText = (skips: readonly HiringSignalSkip[]) => skips.map((skip) => SKIP_LABELS[skip] ?? humanLabel(skip)).join(", ");
50
+ const isFirst = (signal: Pick<HiringWeekSignal, "kind">) => signal.kind === "new_country" || signal.kind === "new_city" || signal.kind === "new_role_type";
51
+
52
+ const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
53
+ /** "Oct 12": a week's Monday or a first read's day. */
54
+ function dayLabel(value: string | null | undefined, year = false): string {
55
+ const match = /^(\d{4})-(\d{2})-(\d{2})/.exec(value ?? "");
56
+ return match ? `${MONTHS[Number(match[2]) - 1]} ${Number(match[3])}${year ? `, ${match[1]}` : ""}` : "--";
57
+ }
58
+ const pace = (value: number | null | undefined) => value == null ? "--" : `${formatNumber(value, Math.abs(value) >= 10 ? 0 : 1)}x`;
59
+ const count = (value: number | null | undefined) => value == null ? "--" : formatNumber(value, 0);
60
+ const average = (value: number) => formatNumber(value, value < 10 ? 1 : 0);
61
+ const signed = (value: number, decimals = 0) => `${value > 0 ? "+" : ""}${formatNumber(value, decimals)}`;
62
+ const percent = (value: number | null) => value == null ? "--" : `${Math.round(value * 100)}%`;
63
+
64
+ const PACE_COLUMNS = new Set(["pace", "companyPace"]);
65
+ export const SIGNALS_SIGNED_COLUMNS = new Set(["z"]);
66
+ /** A signals column's number as the pane and the report print it. */
67
+ export function signalsNumber(columnId: string, value: number): string {
68
+ if (PACE_COLUMNS.has(columnId)) return pace(value);
69
+ if (columnId === "z") return signed(value, 1);
70
+ if (columnId === "mean") return average(value);
71
+ return count(value);
72
+ }
73
+
74
+ /** How far back "never" goes for this company: "Sep 17, 2026, none of 703 earlier postings (98% placed)". */
75
+ function neverText(signal: HiringWeekSignal): string {
76
+ const what = signal.dimension === "function" ? "with a role family" : signal.dimension === "city" ? "naming a city" : "placed";
77
+ return `${dayLabel(signal.history.watchedSince, true)}, none of ${count(signal.history.priorPostings)} earlier postings (${percent(signal.history.priorShare)} ${what})`;
78
+ }
79
+
80
+ /** A signal's numbers, as its detail lists them with the sentence that says why. */
81
+ export function signalFacts(signal: HiringWeekSignal): Array<{ label: string; value: string }> {
82
+ const baseline = signal.baseline;
83
+ return [
84
+ { label: "Signal", value: kindText(signal.kind) },
85
+ { label: signal.dimension === "function" ? "Role family" : signal.dimension === "city" ? "City" : "Country", value: signal.label },
86
+ { label: "New roles", value: `${count(signal.newRoles)} under ${countOf(signal.titles, "title")}, week of ${dayLabel(signal.week, true)}` },
87
+ ...(isFirst(signal) ? [{ label: "First seen since", value: neverText(signal) }] : []),
88
+ ...(baseline ? [
89
+ { label: "Average before", value: `${average(baseline.mean)} a week over ${countOf(baseline.weeks, "measured week")}${baseline.values.length ? `: ${baseline.values.map((row) => count(row.newRoles)).join(", ")}` : ""}` },
90
+ { label: "Pace", value: `${pace(signal.ratio)} its average${signal.zScore == null ? "" : `, z ${signed(signal.zScore, 1)}`}` },
91
+ { label: "Company pace", value: `${pace(signal.companyRatio)}, ${signal.dimension === "function" ? "its roles with a role family" : signal.dimension === "city" ? "its roles placed in a city" : "its placed roles"}` },
92
+ ] : []),
93
+ ...(signal.flags.length ? [{ label: "Check", value: `${flagText(signal)}${signal.reads != null ? `, ${countOf(signal.reads, "read")} that week` : ""}` }] : []),
94
+ ...(signal.places.length ? [{ label: signal.dimension === "country" ? "Cities" : "Where", value: signal.places.map((row) => `${row.label} (${count(row.count)})`).join(", ") }] : []),
95
+ ...(signal.topTitles.length ? [{ label: "Top titles", value: signal.topTitles.map((row) => `${row.title.trim()} (${count(row.count)})`).join(", ") }] : []),
96
+ { label: "Boards", value: signal.sourceTickers.join(", ") },
97
+ { label: "First computed", value: signal.firstComputedAt },
98
+ ];
99
+ }
100
+
101
+ export interface SignalsRow extends AttentionRow {
102
+ signal?: HiringWeekSignal;
103
+ jobPosting?: HiringMovePosting;
104
+ }
105
+ export interface SignalsSection {
106
+ columns: DataTableColumn[];
107
+ rows: SignalsRow[];
108
+ empty: string;
109
+ locked: number;
110
+ }
111
+ export interface SignalsModel {
112
+ asOf: string | null;
113
+ week: string | null;
114
+ preview: boolean;
115
+ figures: StatItem[];
116
+ sections: Partial<Record<SignalsView | "board", SignalsSection>>;
117
+ /** Why a company's week gives none, for its report. */
118
+ held: string | null;
119
+ }
120
+
121
+ const column = (id: string, label: string, width: number, numeric = false, flex = false): DataTableColumn => ({ id, label, width, align: numeric ? "right" : "left", ...(flex ? { flexGrow: 1 } : {}) });
122
+ const CHECK_COLUMN = column("check", "Check", 14);
123
+ const EXPANSION_COLUMNS = [column("symbol", "Ticker", 12), column("name", "Company", 20, false, true), column("kind", "Signal", 12), column("label", "Where", 24),
124
+ column("newRoles", "New roles", 10, true), column("titles", "Titles", 7, true), column("prior", "Earlier", 9, true), column("since", "Since", 8), column("title", "Top title", 26)];
125
+ const ANOMALY_COLUMNS = [column("symbol", "Ticker", 12), column("name", "Company", 20, false, true), column("kind", "Signal", 13), column("label", "What", 24),
126
+ column("newRoles", "New roles", 10, true), column("mean", "Avg / week", 11, true), column("pace", "Pace", 7, true), column("z", "Z-score", 9, true), column("companyPace", "Company", 8, true)];
127
+
128
+ /** The row's place for a flag: a country code, or a city's. */
129
+ const placeCountry = (signal: HiringWeekSignal) => signal.dimension === "function" ? null : signal.country;
130
+ const topTitle = (signal: HiringWeekSignal) => signal.topTitles[0]?.title.trim() ?? signal.postings[0]?.title.trim() ?? null;
131
+ const signalRow = (signal: HiringWeekSignal, withWeek = false): SignalsRow => ({
132
+ id: `${signal.symbol}:${signal.week}:${signal.kind}:${signal.key}`, symbol: signal.symbol, signal, date: signal.week,
133
+ values: { ...(withWeek ? { week: signal.week } : {}), symbol: signal.symbol, name: signal.name, kind: kindText(signal.kind), label: signal.label, country: placeCountry(signal),
134
+ newRoles: signal.newRoles, titles: signal.titles, prior: isFirst(signal) ? signal.history.priorPostings : null, since: isFirst(signal) ? dayLabel(signal.history.watchedSince) : null,
135
+ title: topTitle(signal), mean: signal.baseline?.mean ?? null, pace: signal.ratio, z: signal.zScore, companyPace: signal.companyRatio, check: flagText(signal) },
136
+ });
137
+
138
+ const columnsFor = (group: HiringSignalGroup, signals: readonly HiringWeekSignal[]) => {
139
+ const base = group === "expansion" ? EXPANSION_COLUMNS : ANOMALY_COLUMNS;
140
+ return signals.some((signal) => signal.flags.length) ? [...base, CHECK_COLUMN] : base;
141
+ };
142
+
143
+ function boardFigures(board: HiringSignalsBoard): StatItem[] {
144
+ const { coverage } = board;
145
+ const counts = (kind: HiringSignalKind) => count(coverage.counts[kind] ?? 0);
146
+ const held = Object.values(coverage.held).reduce<number>((sum, value) => sum + (value ?? 0), 0);
147
+ const own = board.group === "expansion"
148
+ ? [{ label: "New countries", value: counts("new_country") }, { label: "New cities", value: counts("new_city") }]
149
+ : [{ label: "Place spikes", value: counts("location_spike") }, { label: "Family spikes", value: counts("function_spike") }, { label: "New families", value: counts("new_role_type") }];
150
+ return board.week ? [
151
+ ...own,
152
+ { label: "Measured", value: count(coverage.measured), detail: `week of ${dayLabel(board.week)}`, detailVariants: [`wk ${dayLabel(board.week)}`, ""] },
153
+ // How far back "never" goes: the first reads of the companies measured.
154
+ ...(board.group === "expansion" || coverage.watchedFrom ? [{ label: "First seen since", value: dayLabel(coverage.watchedFrom), detail: "first reads", detailVariants: [""] }] : []),
155
+ { label: "Held", value: count(held), detail: "week not comparable", detailVariants: [""] },
156
+ ] : [];
157
+ }
158
+
159
+ export function signalsBoardModel(board: HiringSignalsBoard, filter: HiringSignalsFilter): SignalsModel {
160
+ const label = board.group === "expansion" ? "No company posted its first roles in a new country or city this week." : "No place or role family stood out from a company's own weeks this week.";
161
+ return {
162
+ asOf: board.coverage.computedAt,
163
+ week: board.week,
164
+ preview: board.preview,
165
+ figures: boardFigures(board),
166
+ held: null,
167
+ sections: { board: { columns: columnsFor(board.group, board.signals), rows: board.signals.map((signal) => signalRow(signal)), locked: board.preview ? board.locked : 0,
168
+ empty: !board.week ? "No week has been computed yet." : filter === "all" ? label : "None of this kind this week." } },
169
+ };
170
+ }
171
+
172
+ const POSTING_COLUMNS = [column("title", "Role", 32, false, true), column("location", "Location", 24), column("signal", "Signal", 22), column("function", "Role family", 16), column("date", "First seen", 12)];
173
+ const roleFamily = (value: string | null) => value === "data_ai" ? "Data / AI" : value ? humanLabel(value) : null;
174
+ const postingRow = (signal: HiringWeekSignal) => (posting: HiringMovePosting): SignalsRow => ({
175
+ id: `${signal.kind}:${signal.key}:${posting.id}`, url: posting.url, jobPosting: posting, signal,
176
+ values: { title: posting.title.trim(), location: posting.location ?? posting.country, country: posting.country, signal: `${kindText(signal.kind)}: ${signal.label}`,
177
+ function: roleFamily(posting.jobFunction), date: posting.firstSeenAt.slice(0, 10) },
178
+ details: [{ label: "Posting title", value: posting.title.trim() }, { label: "Location", value: posting.location ?? "--" }, { label: "Country", value: posting.country ?? "Unknown" },
179
+ { label: "Role family", value: roleFamily(posting.jobFunction) ?? "Unclassified" }, { label: "Signal", value: `${kindText(signal.kind)}: ${signal.label}` },
180
+ { label: "First seen", value: posting.firstSeenAt }, { label: "Board", value: posting.sourceTicker }],
181
+ });
182
+
183
+ /** A company's signals of one group: this week's, then earlier weeks', and the postings behind this week's. */
184
+ export function companySignalsModel(data: HiringCompanySignals, group: HiringSignalGroup): SignalsModel {
185
+ const current = data.signals.filter((signal) => signal.group === group);
186
+ const earlier = data.history.filter((signal) => signal.group === group);
187
+ const held = data.held ? holdText(data.held) : null;
188
+ // A free read counts what it holds back by tab; a server before that, both tabs together.
189
+ const lockedRows = !data.preview ? 0 : data.locked[group] ?? data.locked.signals + data.locked.history;
190
+ const figures: StatItem[] = data.week ? [
191
+ { label: "Signals", value: count(current.length), detail: `week of ${dayLabel(data.week)}`, detailVariants: [`wk ${dayLabel(data.week)}`, ""] },
192
+ ...(data.preview && lockedRows ? [{ label: "In Pro", value: count(lockedRows), detail: "this week and earlier", detailVariants: [""] }] : []),
193
+ ...(data.watchedSince ? [{ label: "First seen since", value: dayLabel(data.watchedSince), detail: "its first read", detailVariants: [""] }] : []),
194
+ ...(held ? [{ label: "No signal", value: held, wide: true }] : data.skipped.length ? [{ label: "Not looked for", value: skipText(data.skipped), wide: true }] : []),
195
+ ] : [];
196
+ const empty = held ? `No signal this week: ${held.toLowerCase()}.` : lockedRows ? `This company's ${group === "expansion" ? "expansion" : "anomaly"} signals are in Pro.`
197
+ : group === "expansion" ? "No first in a new country or city this week." : "Nothing stood out from this company's own weeks this week.";
198
+ return {
199
+ asOf: current[0]?.computedAt ?? data.history[0]?.computedAt ?? null,
200
+ week: data.week,
201
+ preview: data.preview,
202
+ figures,
203
+ held,
204
+ sections: {
205
+ // A company's rows have room for the title that leads them.
206
+ signals: { columns: [column("week", "Week of", 12), ...columnsFor(group, [...current, ...earlier]).filter((entry) => entry.id !== "symbol" && entry.id !== "name" && entry.id !== "title"), column("title", "Top title", 30, false, true)],
207
+ rows: [...current, ...earlier].map((signal) => signalRow(signal, true)), locked: lockedRows, empty },
208
+ postings: { columns: POSTING_COLUMNS, rows: current.flatMap((signal) => signal.postings.map(postingRow(signal))), locked: 0,
209
+ empty: current.length ? "No posting is listed behind this week's signals." : empty },
210
+ },
211
+ };
212
+ }
213
+
214
+ /**
215
+ * The lock line of a free signals read: what free shows and what Pro adds.
216
+ * Null when nothing is held back. A free board is the same three whatever
217
+ * `filter` asked for.
218
+ */
219
+ export function signalsPreviewNote(data: HiringSignalsBoard | HiringCompanySignals, filter?: HiringSignalsFilter): string | null {
220
+ if (!data.preview) return null;
221
+ if ("group" in data) {
222
+ const shows = `${data.signals.length} of ${count(data.total)} ${data.group === "expansion" ? "expansion" : "anomaly"} signals${filter && filter !== "all" ? `: ${previewCantFilter(["kind"])}` : ""}`;
223
+ return data.locked > 0 ? freePreviewNote({ shows, adds: `the other ${count(data.locked)} and every posting behind them` }) : null;
224
+ }
225
+ const { signals, history } = data.locked;
226
+ if (signals + history <= 0) return null;
227
+ const adds = [signals > 0 ? countOf(signals, "signal") : "", history > 0 ? `${countOf(history, "earlier signal")}` : ""].filter(Boolean);
228
+ return freePreviewNote({ shows: "the signals the free boards show", adds: adds.join(" and ") });
229
+ }
@@ -0,0 +1,160 @@
1
+ import { useCallback, useMemo, useRef, type ReactNode } from "react";
2
+ import { isAccessDenied } from "../../../api-client/errors";
3
+ import type { HiringSignalGroup, HiringSignalsFilter, HiringWeekSignal } from "../../../api-client/hiring";
4
+ import { ChartTableHeader, DataTableView, PageStackView, PaneFooterScope, PaneLinkMenu, QueryBar, usePagedRows, usePaneStatusFooter, useTableLoadMore, withCountryFlag, type PaneHint } from "../../../components";
5
+ import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
6
+ import { DetailReader, type DetailFact } from "../../../components/reader/detail-reader";
7
+ import { useAsyncResource, useAutoRefresh, usePaneSettingValue, usePluginAppActions, usePluginPaneState } from "../../../public/react";
8
+ import { useThemeColors } from "../../../theme/theme-context";
9
+ import { Text, useRendererHost, useUiCapabilities, type ScrollBoxRenderable } from "../../../ui";
10
+ import { CLOUD_PLAN_KEY, useCloudUpgradeAction } from "../shared/cloud-upgrade";
11
+ import { Blurred, LockedOverlay, UpgradeLabel } from "../shared/locked-rows";
12
+ import { useOpenDescription } from "../shared/open-description";
13
+ import { listingCell, missingCell, signedColor, toneColor, withoutQuietColumns } from "../shared/research-cells";
14
+ import { cachedSignals, fetchSignals, loadSignals, validateSignals, type SignalsPayload } from "./client";
15
+ import { sortAttentionRows } from "./model";
16
+ import { companySignalsModel, isSignalsFilter, isSignalsView, signalFacts, signalsBoardModel, signalsNumber, SIGNAL_FILTERS, SIGNALS_SIGNED_COLUMNS, SIGNALS_VIEWS, type SignalsRow } from "./signals-model";
17
+
18
+ const rowId = (row: SignalsRow) => row.id;
19
+ const locked = (row: SignalsRow) => row.id.startsWith("locked:");
20
+ /** A board with no first, or a company with no spike, leaves those columns out. */
21
+ const QUIET_COLUMNS: Record<string, readonly string[]> = { prior: [""], since: [""], mean: [""], pace: [""], z: [""], companyPace: [""], title: [""] };
22
+ /** Postings a signal's detail links to, so the sentence after them stays in view; the Postings view lists them all. */
23
+ const DETAIL_POSTINGS = 3;
24
+
25
+ export interface HiringSignalsViewProps {
26
+ group: HiringSignalGroup;
27
+ symbol?: string;
28
+ width: number;
29
+ height: number;
30
+ focused: boolean;
31
+ /** Plan and session, so a cached read is never shown to the wrong account. */
32
+ accessKey: string;
33
+ /** The board opens a company on the same tab, as Moves does. */
34
+ company?: { open: string | null; onOpen: (symbol: string | null) => void; render: (symbol: string, width: number, height: number) => ReactNode };
35
+ }
36
+
37
+ /**
38
+ * HIRE's Expansion and Anomalies tabs. Alone, the latest week's signals of
39
+ * the group: Enter opens the company, `e` says why. On a ticker, the
40
+ * company's own signals of the group, this week's and earlier, and the
41
+ * postings behind this week's, each linking to the employer's page.
42
+ */
43
+ export function HiringSignalsView({ group, symbol, width, height, focused, accessKey, company }: HiringSignalsViewProps) {
44
+ const colors = useThemeColors();
45
+ const desktop = !!useUiCapabilities().nativePaneChrome;
46
+ const host = useRendererHost();
47
+ const { createPaneFromTemplate } = usePluginAppActions();
48
+ const openDescription = useOpenDescription();
49
+ const upgrade = useCloudUpgradeAction("hire");
50
+ const scope = `hiring:${group}:${symbol ?? "board"}`;
51
+ const [openingFilter] = usePaneSettingValue<string>("signalsKind", "all");
52
+ const [storedFilter, setFilter] = usePluginPaneState<string>(`hiring:${group}:filter`, openingFilter);
53
+ const filter: HiringSignalsFilter = isSignalsFilter(group, storedFilter) ? storedFilter : "all";
54
+ const [openingView] = usePaneSettingValue<string>("signalsView", "signals");
55
+ const [storedView, setView] = usePluginPaneState<string>(`${scope}:view`, openingView);
56
+ const view = isSignalsView(storedView) ? storedView : "signals";
57
+ const [snapshotSetting] = usePaneSettingValue<SignalsPayload | null>(`${group}Snapshot`, null);
58
+ // A `gloom shot` hands the payload in; a company opened from a board snapshot still loads its own.
59
+ const snapshot = useMemo(() => {
60
+ try {
61
+ const payload = snapshotSetting ? validateSignals(snapshotSetting) : null;
62
+ return payload && ("group" in payload ? !symbol && payload.group === group : payload.symbol.toUpperCase() === symbol?.toUpperCase()) ? payload : null;
63
+ } catch { return null; }
64
+ }, [snapshotSetting, symbol, group]);
65
+ const loader = useCallback((force: boolean) => snapshot ? Promise.resolve({ payload: snapshot, stale: false, refreshError: null }) : loadSignals(group, symbol, accessKey, filter, force), [snapshot, group, symbol, accessKey, filter]);
66
+ const resource = useAsyncResource(loader, { initialData: () => cachedSignals(group, symbol, accessKey, filter), clearOnError: isAccessDenied, keepPreviousData: true });
67
+ const data = resource.data?.payload;
68
+ const model = useMemo(() => !data ? null : "group" in data ? signalsBoardModel(data, filter) : companySignalsModel(data, group), [data, filter, group]);
69
+ const sectionId = symbol ? view : "board";
70
+ const section = model?.sections[sectionId];
71
+ const pageLoader = useCallback(async ({ offset, signal }: { offset: number; signal: AbortSignal; force: boolean }) => {
72
+ const payload = offset === 0 && data ? data : await fetchSignals(group, undefined, { filter, offset, limit: 100 }, undefined, signal);
73
+ if (!("group" in payload)) return { rows: [], hasMore: false, nextOffset: null };
74
+ const next = !payload.preview && offset + payload.signals.length < payload.total ? offset + payload.signals.length : null;
75
+ return { rows: signalsBoardModel(payload, filter).sections.board!.rows, hasMore: !snapshot && next !== null, nextOffset: next };
76
+ }, [data, group, filter, snapshot]);
77
+ const paged = usePagedRows(data && !symbol ? pageLoader : null, { getId: rowId });
78
+ const sectionRows = !symbol && paged.status !== "idle" ? paged.rows : section?.rows ?? [];
79
+ // The board keeps the server's order (unflagged first, the largest rises first) until a header is clicked.
80
+ const [sort, setSort] = usePluginPaneState<{ column: string; direction: "asc" | "desc" } | null>(`${scope}:${sectionId}:sort`, null);
81
+ const lockedRows = Math.min(3, section?.locked ?? 0);
82
+ const upgradeText = symbol ? "Upgrade for all signals" : `Upgrade for all ${group === "expansion" ? "expansion" : "anomaly"} signals`;
83
+ const rows = useMemo(() => [...(sort ? sortAttentionRows(sectionRows, sort.column, sort.direction) : sectionRows) as SignalsRow[],
84
+ ...Array.from({ length: lockedRows }, (_, index): SignalsRow => ({ id: `locked:${index}`, values: {} }))], [sectionRows, sort, lockedRows]);
85
+ const dataRows = useMemo(() => rows.filter((row) => !locked(row)), [rows]);
86
+ const columns = useMemo(() => !section ? [] : withoutQuietColumns(section.columns, dataRows, (row, id) => row.values[id] ?? "", QUIET_COLUMNS), [section, dataRows]);
87
+ const [selectedId, setSelected] = usePluginPaneState<string | null>(`${scope}:${sectionId}:selected`, null);
88
+ const openCompany = company?.open ?? null;
89
+ const [openRowId, setOpenRow] = usePluginPaneState<string | null>(`${scope}:open`, null);
90
+ const selected = rows.find((row) => row.id === selectedId) ?? rows[0] ?? null;
91
+ const opened = dataRows.find((row) => row.id === openRowId) ?? null;
92
+ const detailOpen = !!openCompany || !!opened;
93
+ const scrollRef = useRef<ScrollBoxRenderable | null>(null);
94
+ const scrollMore = useTableLoadMore(scrollRef, paged.hasMore && !detailOpen, paged.loadMore);
95
+ useAutoRefresh(resource.updatedAt, resource.load);
96
+ usePaneRefreshKey(() => { void resource.reload(); paged.reload(); }, { focused: focused && !detailOpen });
97
+
98
+ const subject = selected?.symbol ?? symbol;
99
+ // A signal opens its first posting; a posting row its own.
100
+ const sourceRow = opened ?? selected;
101
+ const source = sourceRow?.jobPosting?.url ?? sourceRow?.signal?.postings[0]?.url ?? null;
102
+ const hints: PaneHint[] = [
103
+ ...(source ? [{ id: "source", key: "o", label: "pen source", onPress: () => void host.openExternal(source) }] : []),
104
+ // On a company Enter already opens a signal or a posting.
105
+ ...(!symbol && selected?.signal && !opened ? [{ id: "evidence", key: "e", label: "vidence", title: "Why it fired", onPress: () => setOpenRow(selected.id) }] : []),
106
+ ...(subject && !opened ? [
107
+ { id: "description", key: "d", label: "es", onPress: () => openDescription(subject) },
108
+ { id: "financials", key: "f", label: "a", onPress: () => createPaneFromTemplate("financial-analysis-pane", { symbol: subject }) },
109
+ { id: "chart", key: "g", label: "raph", onPress: () => createPaneFromTemplate("chart-composer-pane", { arg: subject }) },
110
+ { id: "related", key: "a", label: "pps", title: "App attention", onPress: () => createPaneFromTemplate("apps-pane", { symbol: subject }) },
111
+ ] : []),
112
+ ...(model?.preview ? [{ id: "upgrade", key: CLOUD_PLAN_KEY, label: "upgrade", title: "Upgrade to Pro", onPress: upgrade }] : []),
113
+ ];
114
+ usePaneStatusFooter({ registrationId: `${scope}:status`, enabled: !openCompany, loading: resource.loading || paged.loadingMore, error: data ? resource.error ?? paged.moreError?.message ?? paged.error?.message : null, stale: resource.data?.stale,
115
+ info: [...(model?.asOf ? [{ id: "observed", asOf: true, parts: [{ text: `as of ${model.asOf.slice(0, 10)}`, tone: "muted" as const }] }] : []), ...(model?.preview ? [{ id: "preview", parts: [{ text: "Pro preview", tone: "warning" as const }] }] : [])], hints });
116
+
117
+ const query = <QueryBar width={width} filters={symbol
118
+ ? [{ id: "view", label: "View", value: view, options: SIGNALS_VIEWS.map((entry) => ({ ...entry })), onChange: (value: string) => { setView(value); setOpenRow(null); } }]
119
+ : [{ id: "kind", label: "Show", value: filter, defaultValue: "all", options: SIGNAL_FILTERS[group].map((entry) => ({ ...entry })), onChange: (value: string) => { setFilter(value); setOpenRow(null); } }]} />;
120
+ const signalDetail = (signal: HiringWeekSignal): DetailFact[] => [
121
+ ...signalFacts(signal),
122
+ // The postings behind it, each opening the employer's page.
123
+ ...signal.postings.slice(0, DETAIL_POSTINGS).map((posting, index): DetailFact => ({ label: index === 0 ? "Postings" : "", value: `${posting.title.trim()}${posting.location ? `, ${posting.location}` : ""}`,
124
+ ...(posting.url ? { link: { label: "Open posting", onOpen: () => void host.openExternal(posting.url!) } } : {}) })),
125
+ ...(signal.postings.length > DETAIL_POSTINGS ? [{ label: "", value: `${signal.postings.length - DETAIL_POSTINGS} more in ${symbol ? "the" : "the company's"} Postings view` }] : []),
126
+ ];
127
+ const detailTitle = openCompany ?? (opened?.jobPosting ? opened.jobPosting.title.trim() : opened?.signal ? `${opened.signal.symbol} · ${opened.signal.label}` : undefined);
128
+ const detail = openCompany && company ? <PaneFooterScope active>{company.render(openCompany, width, height - 1)}</PaneFooterScope>
129
+ : opened ? <PaneLinkMenu><DetailReader resetScrollKey={opened.id} width={width}
130
+ facts={opened.jobPosting ? opened.details ?? [] : opened.signal ? signalDetail(opened.signal) : []} body={opened.jobPosting ? null : opened.signal?.why ?? null} note={opened.jobPosting?.url ?? null} /></PaneLinkMenu> : null;
131
+ return <PageStackView focused={focused} detailOpen={detailOpen} onBack={() => { company?.onOpen(null); setOpenRow(null); }} detailTitle={detailTitle} detailContent={detail}
132
+ rootContent={<DataTableView columns={columns} items={model && section ? rows : []} rootWidth={width} rootHeight={height} focused={focused && !detailOpen} scrollRef={scrollRef} onBodyScrollActivity={scrollMore}
133
+ status={!data ? { loading: resource.loading, error: resource.error, subject: "hiring signals" } : undefined}
134
+ rootBefore={<ChartTableHeader width={width} height={height} tableRows={rows.length} tableColumns={columns} query={query} figures={model?.figures} />}
135
+ selection={{ kind: "id", selectedId: selected?.id ?? null, getId: rowId, onChange: setSelected }} getItemKey={rowId}
136
+ sortColumnId={sort?.column ?? null} sortDirection={sort?.direction ?? "desc"} onHeaderClick={(column) => setSort((old) => ({ column, direction: old?.column === column && old.direction === "desc" ? "asc" : "desc" }))}
137
+ onActivate={(row) => locked(row) ? void upgrade() : !symbol && row.symbol && company ? company.onOpen(row.symbol) : row.signal || row.jobPosting ? setOpenRow(row.id) : undefined}
138
+ renderCell={(row, column, _index, state) => {
139
+ if (locked(row)) {
140
+ if (!desktop && column.id === columns[0]?.id && row.id === "locked:0") return { text: upgradeText, content: <UpgradeLabel text={upgradeText} onPress={upgrade} role="attention-upgrade" /> };
141
+ return desktop ? { text: "", content: <Blurred><Text fg={colors.textDim}>Hidden</Text></Blurred> } : { text: "░░░░", color: colors.textDim };
142
+ }
143
+ const value = row.values[column.id];
144
+ if (value == null || value === "") return missingCell(colors);
145
+ if (column.id === "symbol" && typeof value === "string") return listingCell(value, colors, state.selected);
146
+ // A first is news; a flagged spike is shown, ranked last.
147
+ if (column.id === "kind" && typeof value === "string") {
148
+ const tone = row.signal?.flags.length ? "muted" : "positive";
149
+ return { text: value, color: toneColor(tone, colors), keepColorWhenSelected: tone !== "muted" };
150
+ }
151
+ if (column.id === "check" && typeof value === "string") return { text: value, value, color: toneColor("warning", colors), keepColorWhenSelected: true };
152
+ if (typeof value === "number") {
153
+ return { text: signalsNumber(column.id, value), value, color: SIGNALS_SIGNED_COLUMNS.has(column.id) ? signedColor(value, colors) : column.id === "mean" || column.id === "prior" ? colors.textDim : colors.text };
154
+ }
155
+ if ((column.id === "label" || column.id === "location") && row.values.country) return withCountryFlag({ text: value, value, color: column.id === "label" ? colors.textBright : colors.text }, String(row.values.country), desktop);
156
+ return { text: value, value, color: column.id === "name" || column.id === "title" || column.id === "label" ? colors.textBright : column.id === "date" || column.id === "week" || column.id === "since" ? colors.textDim : colors.text };
157
+ }} selectedTextOverridesCellColor showHorizontalScrollbar resetScrollKey={`${scope}:${sectionId}:${filter}`}
158
+ emptyStateTitle={section?.empty ?? ""} bodyAfter={lockedRows && desktop ? <LockedOverlay rows={lockedRows} text={upgradeText} onPress={upgrade} role="attention-upgrade" /> : undefined} />}
159
+ />;
160
+ }
@@ -1,5 +1,6 @@
1
1
  import type { DataTableColumn } from "../../../components";
2
2
  import { DATE_OPTION_EXAMPLES, isoDateOption } from "../../../utils/calendar-date";
3
+ import { optionFlag } from "../../../cli/pane-functions/capabilities";
3
4
  import type { HeadlessBundleSection, HeadlessPaneColumn, HeadlessPaneDefinition } from "../../../types/plugin";
4
5
  import { withoutQuietColumns } from "../shared/research-cells";
5
6
  import { resolveHeadlessIssuerListing } from "../shared/headless-market-data";
@@ -88,7 +89,7 @@ export function companyHeadless(mode: CompanyMode): HeadlessPaneDefinition<"bund
88
89
  { key: "basis", type: "enum", values: [{ value: "reported" }, { value: "adjusted" }, { value: "constant_currency" }, { value: "organic" }], description: "Accounting basis filter (Pro)." },
89
90
  { key: "from", type: "string", placeholder: "date", description: `First calendar period end, as ${DATE_OPTION_EXAMPLES} (Pro).`, normalize: (value) => isoDateOption(value, "from") },
90
91
  { key: "to", type: "string", placeholder: "date", description: `Last calendar period end, as ${DATE_OPTION_EXAMPLES} (Pro).`, normalize: (value) => isoDateOption(value, "to") },
91
- { key: "asOf", type: "string", placeholder: "date", description: `Point-in-time publication cutoff, as ${DATE_OPTION_EXAMPLES} (Pro).`, normalize: (value) => isoDateOption(value, "asOf") },
92
+ { key: "asOf", type: "string", placeholder: "date", description: `Point-in-time publication cutoff, as ${DATE_OPTION_EXAMPLES} (Pro).`, normalize: (value) => isoDateOption(value, "as-of") },
92
93
  ],
93
94
  describe: (args) => `${mode === "kpis" ? "Company KPIs" : "Company guidance"} | ${args.symbols[0]}`,
94
95
  async load(args, ctx) {
@@ -106,7 +107,7 @@ export function companyHeadless(mode: CompanyMode): HeadlessPaneDefinition<"bund
106
107
  const latest = ("series" in data ? data.series.map((series) => series.latest) : data.guidance).filter(keep);
107
108
  const ignored = data.access === "preview" ? PRO_FILTERS.filter((key) => typeof args.options[key] === "string" && args.options[key] !== "") : [];
108
109
  const notes = [ageNote(data, latest, mode),
109
- ignored.length ? `The free preview is fixed: ${ignored.map((key) => `--${key}`).join(", ")} ${ignored.length > 1 ? "need" : "needs"} Gloom Pro.` : null].filter((note): note is string => !!note);
110
+ ignored.length ? `The free preview is fixed: ${ignored.map(optionFlag).join(", ")} ${ignored.length > 1 ? "need" : "needs"} Gloom Pro.` : null].filter((note): note is string => !!note);
110
111
  return { complete: !data.truncated, errors: data.truncated ? [data.access === "preview" ? companyPreviewNote(data, mode === "guidance") : "Response limit reached; narrow the metric or date range"] : [],
111
112
  ...(notes.length ? { notes } : {}),
112
113
  sections: "series" in data ? [
@@ -45,8 +45,8 @@ export function scopeWords(dimensions: Record<string, string>, which: "parts" |
45
45
  /** The labels that tell a row's series apart: the server's `seriesLabels`, else (a server before them) its stored labels. */
46
46
  export const seriesLabels = (row: Pick<CompanyRow, "dimensions" | "seriesLabels">) => row.seriesLabels ?? row.dimensions;
47
47
  /**
48
- * What tells a series apart from the metric's other series, in plain words
49
- * (GLO-1046): its parts, a basis other than reported, then its qualifiers.
48
+ * What tells a series apart from the metric's other series, in plain words:
49
+ * its parts, a basis other than reported, then its qualifiers.
50
50
  * "Data Center", "Adjusted · diluted", "International · Constant currency".
51
51
  */
52
52
  export function seriesDetail(row: Pick<CompanyRow, "basis" | "dimensions" | "seriesLabels">): string {
@@ -107,7 +107,7 @@ function CompanyView({ symbol, exchange, name, mode, width, height, focused }: P
107
107
  const [selectedId, setSelected] = usePluginPaneState<string | null>(`company:selected:${tab}`, null);
108
108
  const [openId, setOpen] = usePluginPaneState<string | null>("company:open", null);
109
109
  const [sourceId, setSource] = usePluginPaneState<string | null>("company:source", null);
110
- // The desktop lists KPIs by metric, each series named in plain words (GLO-1046); the terminal keeps its frames.
110
+ // The desktop lists KPIs by metric, each series named in plain words; the terminal keeps its frames.
111
111
  const [sort, setSort] = usePluginPaneState<{ column: string; direction: "asc" | "desc" }>("company:sort",
112
112
  desktop && mode === "kpis" ? { column: "metric", direction: "asc" } : { column: "period", direction: "desc" });
113
113
  const detailScrollRef = useRef<ScrollBoxRenderable | null>(null);
@@ -9,6 +9,7 @@ import { exposureModule } from "./exposure";
9
9
  import { supplyChainModule } from "./supply-chain";
10
10
  import { awardsModule } from "./awards";
11
11
  import { cryptoBoardModule } from "./crypto-board";
12
+ import { cryptoEtfFlowsModule } from "./crypto-etf-flows";
12
13
  import { portfolioAnalyticsModule } from "./analytics";
13
14
  import { brokerTradingModule } from "./broker-trading";
14
15
  import { brokerManagerModule } from "./broker-manager";
@@ -221,7 +222,7 @@ export const futuresCommoditiesPlugin = composeBuiltinPlugin({
221
222
 
222
223
  export const cryptoPlugin = composeBuiltinPlugin({
223
224
  ...cryptoPluginMeta,
224
- modules: [cryptoBoardModule, perpsModule],
225
+ modules: [cryptoBoardModule, cryptoEtfFlowsModule, perpsModule],
225
226
  });
226
227
 
227
228
  export const altDataPlugin = composeBuiltinPlugin({
@@ -161,7 +161,7 @@ export function createCongressHeadless(
161
161
  // A ticker's pane lists its trades only. The filters are the pane's filter bar, kept apart for a ticker's pane.
162
162
  shotState: (args, _load, given) => {
163
163
  if (args.symbols.length && args.options.tab !== "trades") {
164
- throw new Error(`The Congress pane for one ticker lists its trades only. Drop --tab ${args.options.tab} or ${args.symbols[0]} from shot, or run the report: gloomberb fn CG ${args.symbols[0]} --tab ${args.options.tab}`);
164
+ throw new Error(`The Congress pane for one ticker lists its trades only. Drop --tab ${args.options.tab} or ${args.symbols[0]} from shot, or run the report: gloom fn CG ${args.symbols[0]} --tab ${args.options.tab}`);
165
165
  }
166
166
  const filters = shotFilters(args.options, given);
167
167
  return filters ? { pluginState: { "gloomberb-cloud": { [`${args.symbols.length ? "congressTicker." : ""}filters`]: filters } } } : {};
@@ -36,6 +36,7 @@ export const connectionsModule: PluginModule = {
36
36
  personalData: "account",
37
37
  icon: "C",
38
38
  component: ConnectionsPane,
39
+ reportFreshness: { status: "not-a-feed", basis: "connection health" },
39
40
  defaultPosition: "right",
40
41
  defaultMode: "floating",
41
42
  defaultFloatingSize: { width: 80, height: 24 },
@@ -33,7 +33,8 @@ export const paneSchemas = {
33
33
  },
34
34
  },
35
35
  "relationship-graph-pane": {
36
- argument: { kind: "tickers", maximum: 2 },
36
+ // The gold/silver ratio is the pair a metals desk asks for first.
37
+ argument: { kind: "tickers", maximum: 2, examples: ["GC=F SI=F", "AMD NVDA"] },
37
38
  options: [
38
39
  {
39
40
  key: "range",
@@ -64,7 +65,7 @@ export const paneSchemas = {
64
65
  ],
65
66
  discovery: {
66
67
  id: "security-relationship",
67
- aliases: ["relationship", "ratio", "beta", "regression", "rolling correlation"],
68
+ aliases: ["relationship", "ratio", "beta", "regression", "rolling correlation", "gold silver ratio", "gold/silver ratio", "pair ratio"],
68
69
  limitations: ["A single ticker is compared with SPY."],
69
70
  screenshotReadiness: "partial",
70
71
  },
@@ -113,17 +113,19 @@ export const relationshipHeadless: HeadlessPaneDefinition<"series"> = {
113
113
  if (analysis.integrity?.right.length) unavailableSymbols.push(symbols[1]!);
114
114
  if (!unavailableSymbols.length && !analysis.returns.length) unavailableSymbols.push(...symbols);
115
115
  const correlations = new Map(analysis.correlationPoints.map(({ date, close }) => [date.getTime(), close]));
116
+ const regression = `${symbols[0]} on ${symbols[1]}`;
116
117
  return {
117
118
  symbols,
118
119
  series: [
119
120
  { id: "ratio", label: `${symbols[0]}/${symbols[1]}`, points: analysis.ratioPoints.map(({ date, close }) => ({ date: date.toISOString(), value: close })) },
120
- { id: "correlation", label: `Rolling correlation (${correlationWindow})`, points: analysis.returns.slice(correlationWindow - 1).map(({ date }) => ({ date: date.toISOString(), value: correlations.get(date.getTime()) ?? null })) },
121
+ { id: "correlation", label: `Rolling correlation (${correlationWindow})`, textDecimals: 3, points: analysis.returns.slice(correlationWindow - 1).map(({ date }) => ({ date: date.toISOString(), value: correlations.get(date.getTime()) ?? null })) },
121
122
  ],
122
123
  stats: [
123
124
  { key: "latestRatio", label: "Latest ratio", value: analysis.latestRatio, formatted: formatSignificant(analysis.latestRatio ?? undefined) },
124
125
  { key: "latestCorrelation", label: `Rolling correlation (${correlationWindow})`, value: analysis.latestCorrelation, formatted: formatNumber(analysis.latestCorrelation ?? undefined, 3) },
125
126
  ...(["beta", "alpha", "rSquared"] as const).map((key) => ({
126
- key, label: { beta: "Beta", alpha: "Alpha", rSquared: "R squared" }[key],
127
+ // The first ticker's daily returns are regressed on the second's, so a beta says which way round it reads.
128
+ key, label: { beta: `${regression}: beta`, alpha: `${regression}: alpha`, rSquared: "R squared" }[key],
127
129
  value: analysis.stats?.[key] ?? null,
128
130
  // Alpha is the regression intercept on daily percent returns.
129
131
  formatted: key === "alpha" && analysis.stats ? `${formatNumber(analysis.stats.alpha, 3)}%` : formatNumber(analysis.stats?.[key], 3),
@@ -141,7 +143,7 @@ export const relationshipHeadless: HeadlessPaneDefinition<"series"> = {
141
143
  lastDate: analysis.aligned.at(-1)?.dateKey ?? null,
142
144
  ...(analysis.integrity ? { integrity: analysis.integrity } : {}),
143
145
  latestRatio: analysis.latestRatio, latestCorrelation: analysis.latestCorrelation,
144
- regression: analysis.stats, alignedPriceCount: analysis.aligned.length, returnCount: analysis.returns.length,
146
+ regression: analysis.stats && { dependent: symbols[0], independent: symbols[1], ...analysis.stats }, alignedPriceCount: analysis.aligned.length, returnCount: analysis.returns.length,
145
147
  },
146
148
  };
147
149
  },
@@ -1,8 +1,9 @@
1
- import type { CotClass, CotClassSummary } from "../../../api-client/cot";
1
+ import type { CotClass, CotClassSummary, CotContractPayload, CotHistoryPoint } from "../../../api-client/cot";
2
2
  import type { HeadlessPaneColumn, HeadlessPaneDefinition } from "../../../types/plugin";
3
3
  import { fetchCotBoard, fetchCotContract } from "./client";
4
- import { COT_MAJOR_CODES, COT_SCOPES, cotClass, cotContractCode, cotInteger, cotMarketName, cotScope, cotUnknownMarketMessage } from "./model";
4
+ import { COT_MAJOR_CODES, COT_SCOPES, cotClass, cotContractCode, cotContractUnitText, cotInteger, cotMarketName, cotScope, cotUnknownMarketMessage } from "./model";
5
5
  import { COMMODITIES_VIEW_STATE } from "../commodities-energy/view-state";
6
+ import { REPORT_ROWS } from "../shared/headless-options";
6
7
 
7
8
  const camelClass = (id: CotClass) => id.replace(/-(\w)/g, (_match, letter: string) => letter.toUpperCase());
8
9
  const rounded = (value: number | null, digits: number) => value == null ? null : Number(value.toFixed(digits));
@@ -17,6 +18,22 @@ function cotPositionRow(row: CotClassSummary) {
17
18
  netPercentOfOpenInterest: rounded(row.netPercentOfOpenInterest, 1), weeklyChange: row.weeklyChange, previousReportDate: row.previousReportDate,
18
19
  oneYearPercentile: rounded(row.percentile1Y.value, 0), threeYearPercentile: rounded(row.percentile3Y.value, 0) };
19
20
  }
21
+ /**
22
+ * A market's title line: its name, the report date (the Tuesday the positions
23
+ * are of) and what one contract is, so a net of +207,879 reads in ounces too.
24
+ */
25
+ function cotContractTitle(data: CotContractPayload, code: string): string {
26
+ const units = cotContractUnitText(data.contract?.contractUnits);
27
+ return [data.contract ? cotMarketName(data.contract.marketName) : code, data.asOf ? `report date ${data.asOf}` : null, units]
28
+ .filter(Boolean).join(" · ");
29
+ }
30
+
31
+ /** The weekly reports newest first, the newest `limit` of them when given. */
32
+ function cotWeeklyRows(history: readonly CotHistoryPoint[], limit: number | null): CotHistoryPoint[] {
33
+ const newest = [...history].reverse();
34
+ return limit == null ? newest : newest.slice(0, limit);
35
+ }
36
+
20
37
  const POSITION_COLUMNS: HeadlessPaneColumn[] = [
21
38
  { key: "net", header: "Net", align: "right", format: signed },
22
39
  { key: "weeklyChange", header: "1W Δ", align: "right", format: signed },
@@ -34,7 +51,10 @@ export const cotHeadless: HeadlessPaneDefinition<"bundle"> = {
34
51
  { key: "traderClass", type: "enum", values: ["noncommercial", "commercial", "producer", "swap", "managed-money", "other-reportable", "nonreportable"].map((value) => ({ value })), description: "Trader class for the extremes board; defaults by report family.",
35
52
  pluginState: { pluginId: COMMODITIES_VIEW_STATE.cot, key: "class" } },
36
53
  { key: "scope", type: "enum", values: COT_SCOPES.map(({ value }) => ({ value })), defaultValue: "major", description: "Markets on the extremes board, as in the pane.",
37
- pluginState: { pluginId: COMMODITIES_VIEW_STATE.cot, key: "scope" } }],
54
+ pluginState: { pluginId: COMMODITIES_VIEW_STATE.cot, key: "scope" } },
55
+ // Weekly reports come newest first, so --tail asks for the same newest n as --limit.
56
+ { key: "limit", type: "integer", aliases: ["tail", "weeks", "rows"], minimum: 1, maximum: 2000, shot: REPORT_ROWS,
57
+ description: "Newest weekly reports a market lists; every one by default." }],
38
58
  discovery: { aliases: ["COT", "CFTC"], dataRequirements: ["Gloom Cloud CFTC history"], limitations: ["Futures-only positions aggregate all maturities.", "Legacy and disaggregated classes differ.", "Observation date is not publication date.", "Some markets have short or suppressed histories."] },
39
59
  describe: "CFTC positioning",
40
60
  async load(args, ctx) {
@@ -44,14 +64,16 @@ export const cotHeadless: HeadlessPaneDefinition<"bundle"> = {
44
64
  if (!code) throw new Error(cotUnknownMarketMessage(args.argument));
45
65
  const data = await fetchCotContract(code, family, ctx.apiClient);
46
66
  const classes = data.positions.map((row) => row.id);
47
- return { sections: [{ title: data.contract ? cotMarketName(data.contract.marketName) : code,
67
+ const limit = args.options.limit == null ? null : Number(args.options.limit);
68
+ const weeks = cotWeeklyRows(data.history, limit);
69
+ return { sections: [{ title: cotContractTitle(data, code),
48
70
  columns: [{ key: "class", header: "Class" }, { key: "long", header: "Long", align: "right", format: count },
49
71
  { key: "short", header: "Short", align: "right", format: count }, ...POSITION_COLUMNS],
50
72
  rows: data.positions.map((row) => ({ class: row.label, ...cotPositionRow(row) })) },
51
- { title: "Weekly positions",
73
+ { title: weeks.length < data.history.length ? `Weekly positions · newest ${weeks.length} of ${data.history.length}` : "Weekly positions",
52
74
  columns: [{ key: "reportDate", header: "Report date" }, { key: "openInterest", header: "Open interest", align: "right", format: count },
53
75
  ...classes.map((id) => ({ key: `${camelClass(id)}Net`, header: `${data.positions.find((row) => row.id === id)?.label ?? id} net`, align: "right" as const, format: signed }))],
54
- rows: data.history.map((row) => ({ reportDate: row.reportDate, openInterest: row.openInterest,
76
+ rows: weeks.map((row) => ({ reportDate: row.reportDate, openInterest: row.openInterest,
55
77
  ...Object.fromEntries(row.positions.map((position) => [`${camelClass(position.id)}Net`, position.net])) })) }],
56
78
  errors: data.gaps, metadata: { ...data, complete: data.status === "available" } };
57
79
  }