gloomberb 0.16.0 → 0.16.2

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 (691) hide show
  1. package/README.md +2 -1
  2. package/package.json +2 -1
  3. package/src/api-client/beneficial-owners.ts +92 -0
  4. package/src/api-client/data.ts +44 -11
  5. package/src/api-client/errors.ts +12 -0
  6. package/src/api-client/geo.ts +363 -0
  7. package/src/api-client/index.ts +5 -0
  8. package/src/api-client/market-discovery.ts +10 -0
  9. package/src/api-client/members.ts +26 -0
  10. package/src/api-client/paths.ts +30 -1
  11. package/src/api-client/perps.ts +41 -0
  12. package/src/api-client/request.ts +5 -2
  13. package/src/api-client/types.ts +9 -1
  14. package/src/app/global-shortcuts.ts +14 -6
  15. package/src/app/keybindings/actions.ts +1 -1
  16. package/src/app/runtime/update.ts +38 -2
  17. package/src/app.tsx +10 -6
  18. package/src/brokers/account-cache.ts +24 -0
  19. package/src/cli/commands/command-utils.ts +8 -0
  20. package/src/cli/commands/crypto-hints.ts +75 -0
  21. package/src/cli/commands/market.ts +414 -106
  22. package/src/cli/commands/overview.ts +77 -32
  23. package/src/cli/commands/ticker.ts +101 -43
  24. package/src/cli/desktop-pane-shot.ts +34 -4
  25. package/src/cli/freshness.ts +61 -0
  26. package/src/cli/help.ts +1 -0
  27. package/src/cli/helpers.ts +4 -8
  28. package/src/cli/index.ts +12 -2
  29. package/src/cli/listing-arg.ts +321 -0
  30. package/src/cli/pane-functions/access-gate.ts +42 -0
  31. package/src/cli/pane-functions/catalog.ts +28 -1
  32. package/src/cli/pane-functions/data.ts +3 -1
  33. package/src/cli/pane-functions/dom.ts +20 -2
  34. package/src/cli/pane-functions/freshness.ts +400 -0
  35. package/src/cli/pane-functions/function-help.ts +36 -22
  36. package/src/cli/pane-functions/headless.ts +39 -11
  37. package/src/cli/pane-functions/index.ts +15 -4
  38. package/src/cli/pane-functions/options.ts +3 -0
  39. package/src/cli/pane-functions/report.ts +4 -0
  40. package/src/cli/pane-functions/resolver.ts +35 -7
  41. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  42. package/src/cli/pane-functions/screenshot.ts +7 -3
  43. package/src/cli/result.ts +41 -27
  44. package/src/cli/ticker-resolution.ts +7 -5
  45. package/src/components/chart/composite/composite-chart.tsx +88 -2536
  46. package/src/components/chart/composite/drawing-store.ts +59 -0
  47. package/src/components/chart/composite/legend.tsx +319 -0
  48. package/src/components/chart/composite/panel-bitmap.ts +139 -0
  49. package/src/components/chart/composite/panel-gestures.ts +59 -0
  50. package/src/components/chart/composite/panel-labels.ts +107 -0
  51. package/src/components/chart/composite/panel-surface.tsx +876 -0
  52. package/src/components/chart/composite/tool-catalog.ts +98 -0
  53. package/src/components/chart/composite/toolbar.tsx +186 -0
  54. package/src/components/chart/composite/tools.ts +8 -0
  55. package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
  56. package/src/components/chart/composite/use-composite-viewport.ts +143 -0
  57. package/src/components/command-bar/commands/collection.ts +5 -1
  58. package/src/components/command-bar/list/model.ts +5 -0
  59. package/src/components/command-bar/list/view.tsx +16 -3
  60. package/src/components/command-bar/pane-templates/workflow.ts +1 -0
  61. package/src/components/command-bar/routes/root/command-items.ts +9 -2
  62. package/src/components/command-bar/routes/root/plugin-install.ts +2 -2
  63. package/src/components/command-bar/routes/root/provider-search.ts +19 -5
  64. package/src/components/command-bar/routes/root/runtime.ts +14 -5
  65. package/src/components/command-bar/routes/ticker-search/actions.ts +5 -0
  66. package/src/components/command-bar/routes/ticker-search/results.ts +31 -5
  67. package/src/components/command-bar/routes/ticker-search/route.ts +2 -2
  68. package/src/components/command-bar/routing/actions.ts +2 -0
  69. package/src/components/command-bar/selection-runtime.ts +38 -0
  70. package/src/components/command-bar/surface/index.tsx +7 -2
  71. package/src/components/command-bar/workflow/ops.ts +1 -1
  72. package/src/components/command-bar/workflow/tickers.ts +6 -0
  73. package/src/components/data-table/view.tsx +29 -10
  74. package/src/components/feedback-dialog.tsx +2 -1
  75. package/src/components/form-modal/field-row.tsx +2 -2
  76. package/src/components/index.ts +2 -2
  77. package/src/components/layout/detached-pane-shell.tsx +6 -3
  78. package/src/components/layout/floating-pane.tsx +6 -0
  79. package/src/components/layout/header.tsx +24 -7
  80. package/src/components/layout/missing-pane.tsx +2 -2
  81. package/src/components/layout/pane/content.tsx +23 -10
  82. package/src/components/layout/pane/error-boundary.tsx +103 -0
  83. package/src/components/layout/pane/footer/registration.tsx +68 -23
  84. package/src/components/layout/pane/header.tsx +61 -28
  85. package/src/components/layout/pane/index.tsx +6 -0
  86. package/src/components/layout/pane/title.ts +31 -7
  87. package/src/components/layout/shell/drag/index.ts +6 -5
  88. package/src/components/layout/shell/drag/runtime.ts +3 -0
  89. package/src/components/layout/shell/index.tsx +6 -0
  90. package/src/components/layout/shell/menu.ts +2 -1
  91. package/src/components/layout/shell/pane/actions.ts +2 -1
  92. package/src/components/layout/shell/pane/layers.tsx +29 -10
  93. package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -3
  94. package/src/components/layout/shell/ticker-link-menu.ts +23 -9
  95. package/src/components/metric-treemap/heat-surface.tsx +745 -0
  96. package/src/components/metric-treemap/heat-tiles.ts +228 -0
  97. package/src/components/metric-treemap/index.tsx +2 -0
  98. package/src/components/metric-treemap/layout.ts +8 -0
  99. package/src/components/metric-treemap/scene.ts +432 -0
  100. package/src/components/onboarding/onboarding-keyboard.ts +311 -0
  101. package/src/components/onboarding/onboarding-wizard.tsx +101 -788
  102. package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
  103. package/src/components/onboarding/use-onboarding-progress.ts +298 -0
  104. package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
  105. package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -0
  106. package/src/components/ticker/list-table-view.tsx +13 -1
  107. package/src/components/toggle-list.tsx +38 -12
  108. package/src/components/ui/country-flag-art.tsx +449 -0
  109. package/src/components/ui/country-flag.tsx +41 -0
  110. package/src/components/ui/data-table/types.ts +13 -0
  111. package/src/components/ui/fields.tsx +8 -0
  112. package/src/components/ui/icon.tsx +2 -0
  113. package/src/components/ui/index.ts +4 -0
  114. package/src/components/ui/multi-select/dialog.tsx +108 -22
  115. package/src/components/ui/range-track.tsx +53 -0
  116. package/src/components/ui/status.tsx +8 -2
  117. package/src/components/ui/tabs.tsx +11 -4
  118. package/src/components/ui/text-prompt-dialog.tsx +4 -2
  119. package/src/core/state/app/types.ts +10 -1
  120. package/src/data/config/store/normalize.ts +17 -1
  121. package/src/i18n/es.ts +24 -4
  122. package/src/i18n/ja.ts +24 -4
  123. package/src/i18n/ko.ts +24 -4
  124. package/src/i18n/zh-cn.ts +24 -4
  125. package/src/i18n/zh-tw.ts +24 -4
  126. package/src/layout/ticker-navigation.ts +28 -2
  127. package/src/market-data/coordinator/chart.ts +1 -1
  128. package/src/market-data/coordinator/quotes.ts +23 -4
  129. package/src/market-data/fx-closes.ts +72 -0
  130. package/src/market-data/market/freshness.ts +78 -6
  131. package/src/market-data/market/session-close-note.ts +57 -0
  132. package/src/market-data/market/status.ts +151 -0
  133. package/src/market-data/published-apac-sessions.ts +66 -0
  134. package/src/market-data/quotes/contributions.ts +8 -0
  135. package/src/market-data/quotes/freshness.ts +36 -0
  136. package/src/market-data/quotes/resolution.ts +24 -8
  137. package/src/market-data/quotes/screener-live-quotes.ts +49 -4
  138. package/src/market-data/quotes/time.ts +16 -0
  139. package/src/plugins/builtin/account-management/pane.tsx +4 -0
  140. package/src/plugins/builtin/alerts/cli.ts +10 -5
  141. package/src/plugins/builtin/alerts/index.tsx +1 -0
  142. package/src/plugins/builtin/analytics/metrics.ts +8 -3
  143. package/src/plugins/builtin/analytics/risk-client.ts +330 -79
  144. package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
  145. package/src/plugins/builtin/analytics/risk-headless.ts +61 -49
  146. package/src/plugins/builtin/analytics/risk-math.ts +33 -13
  147. package/src/plugins/builtin/analytics/risk-model.ts +282 -64
  148. package/src/plugins/builtin/analytics/risk-pane.tsx +62 -30
  149. package/src/plugins/builtin/analytics/risk-test-data.ts +206 -0
  150. package/src/plugins/builtin/attention/headless.ts +6 -4
  151. package/src/plugins/builtin/attention/index.tsx +1 -1
  152. package/src/plugins/builtin/attention/pane.tsx +1 -1
  153. package/src/plugins/builtin/awards/headless.ts +1 -0
  154. package/src/plugins/builtin/awards/pane.tsx +1 -1
  155. package/src/plugins/builtin/backtest/headless.ts +1 -0
  156. package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
  157. package/src/plugins/builtin/buildout/cells.tsx +15 -12
  158. package/src/plugins/builtin/buildout/detail/company.tsx +3 -3
  159. package/src/plugins/builtin/buildout/detail/index.tsx +0 -1
  160. package/src/plugins/builtin/buildout/detail/intel.tsx +6 -6
  161. package/src/plugins/builtin/buildout/detail/site.tsx +4 -7
  162. package/src/plugins/builtin/buildout/detail/ui.tsx +0 -19
  163. package/src/plugins/builtin/buildout/index.tsx +1 -0
  164. package/src/plugins/builtin/catalysts/headless.ts +3 -1
  165. package/src/plugins/builtin/catalysts/index.tsx +2 -2
  166. package/src/plugins/builtin/catalysts/pane.tsx +3 -1
  167. package/src/plugins/builtin/cds/headless.ts +2 -0
  168. package/src/plugins/builtin/cds/index.tsx +1 -0
  169. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
  170. package/src/plugins/builtin/chart-composer/headless.ts +38 -3
  171. package/src/plugins/builtin/chart-composer/index.tsx +3 -1
  172. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
  173. package/src/plugins/builtin/chat/channels.ts +19 -3
  174. package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
  175. package/src/plugins/builtin/chat/content/footer.tsx +15 -6
  176. package/src/plugins/builtin/chat/content/index.tsx +120 -21
  177. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
  178. package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
  179. package/src/plugins/builtin/chat/direct-messages.ts +200 -0
  180. package/src/plugins/builtin/chat/index.tsx +1 -0
  181. package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
  182. package/src/plugins/builtin/chat/message/header.tsx +16 -6
  183. package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
  184. package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
  185. package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
  186. package/src/plugins/builtin/chat/message/types.ts +14 -2
  187. package/src/plugins/builtin/chat/profile-popover.ts +18 -13
  188. package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
  189. package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
  190. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  191. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  192. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  193. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  194. package/src/plugins/builtin/comment-letters/index.tsx +1 -0
  195. package/src/plugins/builtin/company-attention/headless.ts +5 -1
  196. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  197. package/src/plugins/builtin/company-kpis/headless.ts +1 -0
  198. package/src/plugins/builtin/congress-trades/headless.ts +1 -0
  199. package/src/plugins/builtin/correlation/compute.ts +59 -16
  200. package/src/plugins/builtin/correlation/geo.ts +34 -0
  201. package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
  202. package/src/plugins/builtin/correlation/headless.ts +42 -8
  203. package/src/plugins/builtin/correlation/history.ts +2 -0
  204. package/src/plugins/builtin/correlation/index.tsx +68 -14
  205. package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
  206. package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
  207. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  208. package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
  209. package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
  210. package/src/plugins/builtin/correlation/settings.ts +1 -1
  211. package/src/plugins/builtin/cot/headless.ts +6 -3
  212. package/src/plugins/builtin/cot/index.tsx +3 -3
  213. package/src/plugins/builtin/cot/model.ts +36 -3
  214. package/src/plugins/builtin/cpi/headless.ts +4 -0
  215. package/src/plugins/builtin/credit-boards/headless.ts +2 -0
  216. package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
  217. package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
  218. package/src/plugins/builtin/credit-documents/headless.ts +2 -0
  219. package/src/plugins/builtin/crypto-board/headless.ts +3 -0
  220. package/src/plugins/builtin/custom-view/loader.ts +1 -1
  221. package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
  222. package/src/plugins/builtin/debt-maturities/wall-bar.tsx +14 -41
  223. package/src/plugins/builtin/dividend-yield/client.ts +4 -2
  224. package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
  225. package/src/plugins/builtin/doe/headless.ts +7 -0
  226. package/src/plugins/builtin/doe/range-bar.tsx +7 -37
  227. package/src/plugins/builtin/earnings/client.ts +2 -2
  228. package/src/plugins/builtin/earnings/headless.ts +5 -0
  229. package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
  230. package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
  231. package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
  232. package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
  233. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
  234. package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
  235. package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
  236. package/src/plugins/builtin/econ/index.tsx +1 -0
  237. package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
  238. package/src/plugins/builtin/equity-screener/headless.ts +2 -0
  239. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  240. package/src/plugins/builtin/executives/index.tsx +1 -0
  241. package/src/plugins/builtin/exposure/headless.ts +3 -1
  242. package/src/plugins/builtin/fear-greed/headless.ts +2 -0
  243. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  244. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  245. package/src/plugins/builtin/futures-curve/headless.ts +39 -10
  246. package/src/plugins/builtin/futures-curve/index.tsx +3 -3
  247. package/src/plugins/builtin/futures-curve/model.ts +188 -4
  248. package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
  249. package/src/plugins/builtin/fx-matrix/direction.ts +81 -0
  250. package/src/plugins/builtin/fx-matrix/index.tsx +71 -33
  251. package/src/plugins/builtin/fx-matrix/live-legs.ts +17 -0
  252. package/src/plugins/builtin/fx-matrix/pairs.ts +68 -14
  253. package/src/plugins/builtin/fx-matrix/settings.ts +62 -0
  254. package/src/plugins/builtin/gpu/headless.ts +7 -3
  255. package/src/plugins/builtin/gpu/ladder.tsx +26 -30
  256. package/src/plugins/builtin/gpu/model.ts +27 -1
  257. package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
  258. package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
  259. package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
  260. package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
  261. package/src/plugins/builtin/holders/headless.ts +139 -5
  262. package/src/plugins/builtin/holders/index.tsx +10 -3
  263. package/src/plugins/builtin/holders/pane.tsx +20 -3
  264. package/src/plugins/builtin/holders/report-header.ts +95 -0
  265. package/src/plugins/builtin/holders/table-model.ts +20 -5
  266. package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
  267. package/src/plugins/builtin/holders/types.ts +2 -1
  268. package/src/plugins/builtin/insider/headless.ts +2 -0
  269. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
  270. package/src/plugins/builtin/iv-history/headless.ts +4 -1
  271. package/src/plugins/builtin/jobs/headless.ts +1 -0
  272. package/src/plugins/builtin/jobs/index.tsx +1 -0
  273. package/src/plugins/builtin/jobs/share-bars.tsx +5 -28
  274. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
  275. package/src/plugins/builtin/macro-day/headless.ts +2 -0
  276. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  277. package/src/plugins/builtin/market-heatmap/data.ts +30 -2
  278. package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
  279. package/src/plugins/builtin/market-heatmap/index.tsx +245 -81
  280. package/src/plugins/builtin/market-heatmap/live.ts +88 -0
  281. package/src/plugins/builtin/market-heatmap/model.ts +146 -0
  282. package/src/plugins/builtin/market-heatmap/portfolio.ts +50 -11
  283. package/src/plugins/builtin/market-movers/client.ts +3 -2
  284. package/src/plugins/builtin/market-movers/headless.ts +10 -1
  285. package/src/plugins/builtin/market-movers/screener.ts +18 -5
  286. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  287. package/src/plugins/builtin/members/client.ts +53 -0
  288. package/src/plugins/builtin/members/headless.ts +35 -0
  289. package/src/plugins/builtin/members/index.tsx +20 -0
  290. package/src/plugins/builtin/members/model.ts +68 -0
  291. package/src/plugins/builtin/members/pane.tsx +175 -0
  292. package/src/plugins/builtin/mna/distress/headless.ts +1 -0
  293. package/src/plugins/builtin/mna/headless.ts +1 -0
  294. package/src/plugins/builtin/mna/index.tsx +2 -1
  295. package/src/plugins/builtin/mna/pane.tsx +5 -2
  296. package/src/plugins/builtin/money-markets/headless.ts +1 -0
  297. package/src/plugins/builtin/news/headless.ts +2 -0
  298. package/src/plugins/builtin/news/wire/index.tsx +8 -1
  299. package/src/plugins/builtin/notes/cli.ts +14 -6
  300. package/src/plugins/builtin/notes/index.tsx +1 -0
  301. package/src/plugins/builtin/openfda/index.tsx +1 -0
  302. package/src/plugins/builtin/options/index.tsx +2 -1
  303. package/src/plugins/builtin/options-calculator/headless.ts +2 -0
  304. package/src/plugins/builtin/options-positioning/headless.ts +3 -0
  305. package/src/plugins/builtin/options-scenario/headless.ts +5 -0
  306. package/src/plugins/builtin/pe-band/client.ts +74 -2
  307. package/src/plugins/builtin/pe-band/headless.ts +26 -8
  308. package/src/plugins/builtin/pe-band/model.ts +102 -22
  309. package/src/plugins/builtin/pe-band/pane.tsx +34 -16
  310. package/src/plugins/builtin/pe-band/report-dates.ts +78 -0
  311. package/src/plugins/builtin/perps/client.ts +66 -5
  312. package/src/plugins/builtin/perps/equity-identity.ts +16 -0
  313. package/src/plugins/builtin/perps/equity-row.tsx +6 -4
  314. package/src/plugins/builtin/perps/headless.ts +144 -16
  315. package/src/plugins/builtin/perps/index.tsx +6 -6
  316. package/src/plugins/builtin/perps/model.ts +189 -12
  317. package/src/plugins/builtin/perps/pane.tsx +248 -47
  318. package/src/plugins/builtin/plugin-marketplace/activation.ts +19 -0
  319. package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -2
  320. package/src/plugins/builtin/plugin-marketplace/store.ts +2 -0
  321. package/src/plugins/builtin/portfolio-list/allocation.ts +245 -0
  322. package/src/plugins/builtin/portfolio-list/cached-account.ts +33 -0
  323. package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
  324. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
  325. package/src/plugins/builtin/portfolio-list/cli/render.ts +230 -39
  326. package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
  327. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
  328. package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
  329. package/src/plugins/builtin/portfolio-list/headless.ts +98 -16
  330. package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
  331. package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
  332. package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
  333. package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
  334. package/src/plugins/builtin/portfolio-list/pane/index.tsx +47 -13
  335. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +43 -2
  336. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +48 -5
  337. package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
  338. package/src/plugins/builtin/portfolio-list/summary/index.tsx +24 -1
  339. package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
  340. package/src/plugins/builtin/power/headless.ts +3 -1
  341. package/src/plugins/builtin/rate-path/headless.ts +2 -0
  342. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  343. package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
  344. package/src/plugins/builtin/research/analyst-headless.ts +3 -0
  345. package/src/plugins/builtin/research/events-headless.ts +2 -0
  346. package/src/plugins/builtin/research/headless.ts +2 -0
  347. package/src/plugins/builtin/research/index.tsx +1 -0
  348. package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
  349. package/src/plugins/builtin/research/relative-valuation-model.ts +11 -3
  350. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  351. package/src/plugins/builtin/research-search/index.tsx +1 -0
  352. package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
  353. package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
  354. package/src/plugins/builtin/risk-factors/headless.ts +2 -0
  355. package/src/plugins/builtin/scanner/flow-history.ts +2 -13
  356. package/src/plugins/builtin/scanner/headless.ts +14 -0
  357. package/src/plugins/builtin/scanner/hilo-bars.tsx +12 -70
  358. package/src/plugins/builtin/scanner/hilo-model.ts +0 -18
  359. package/src/plugins/builtin/seasonality/headless.ts +2 -0
  360. package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
  361. package/src/plugins/builtin/sec/filing-content.ts +42 -17
  362. package/src/plugins/builtin/sec/forms.ts +12 -0
  363. package/src/plugins/builtin/sec/headless.ts +2 -0
  364. package/src/plugins/builtin/sectors/client.ts +1 -0
  365. package/src/plugins/builtin/sectors/headless.ts +9 -1
  366. package/src/plugins/builtin/sectors/move-bar.tsx +10 -32
  367. package/src/plugins/builtin/sectors/sector-model.ts +2 -0
  368. package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
  369. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  370. package/src/plugins/builtin/shared/report-freshness.ts +57 -0
  371. package/src/plugins/builtin/shared/research-cells.tsx +4 -9
  372. package/src/plugins/builtin/short-interest/headless.ts +2 -0
  373. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  374. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  375. package/src/plugins/builtin/social-mentions/headless.ts +1 -0
  376. package/src/plugins/builtin/social-mentions/index.tsx +1 -1
  377. package/src/plugins/builtin/supply-chain/headless.ts +7 -1
  378. package/src/plugins/builtin/supply-chain/index.tsx +10 -1
  379. package/src/plugins/builtin/supply-chain/model.ts +27 -2
  380. package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
  381. package/src/plugins/builtin/themes/headless.ts +3 -0
  382. package/src/plugins/builtin/thirteenf/headless.ts +110 -25
  383. package/src/plugins/builtin/thirteenf/index.tsx +2 -1
  384. package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
  385. package/src/plugins/builtin/ticker-detail/headless.ts +7 -1
  386. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  387. package/src/plugins/builtin/ticker-detail/overview/components.tsx +5 -60
  388. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -2
  389. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +1 -1
  390. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +26 -26
  391. package/src/plugins/builtin/ticker-detail/pane.tsx +1 -1
  392. package/src/plugins/builtin/ticker-detail/quote-footer.ts +7 -1
  393. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +20 -4
  394. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -2
  395. package/src/plugins/builtin/time-sales/headless.ts +5 -1
  396. package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
  397. package/src/plugins/builtin/vol-surface/index.tsx +1 -1
  398. package/src/plugins/builtin/volatility/headless.ts +6 -1
  399. package/src/plugins/builtin/world-indices/headless.ts +2 -0
  400. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +157 -0
  401. package/src/plugins/builtin/world-venue-map/basemap.ts +214 -0
  402. package/src/plugins/builtin/world-venue-map/client.ts +45 -0
  403. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
  404. package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
  405. package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
  406. package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
  407. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
  408. package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
  409. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +447 -0
  410. package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
  411. package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
  412. package/src/plugins/builtin/world-venue-map/index.ts +67 -4
  413. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +552 -0
  414. package/src/plugins/builtin/world-venue-map/layers.ts +348 -0
  415. package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
  416. package/src/plugins/builtin/world-venue-map/map.tsx +381 -45
  417. package/src/plugins/builtin/world-venue-map/model.ts +7 -4
  418. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +199 -0
  419. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
  420. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
  421. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
  422. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
  423. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
  424. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
  425. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
  426. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
  429. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
  430. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
  431. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
  432. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
  433. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
  434. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
  435. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
  436. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
  437. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
  438. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
  439. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
  440. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
  441. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
  442. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
  443. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
  444. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
  445. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
  446. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
  447. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
  448. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
  449. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
  450. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
  451. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
  452. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
  453. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
  454. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
  455. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
  456. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
  457. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
  458. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
  459. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
  460. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
  461. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
  462. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
  463. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
  464. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
  465. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
  466. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
  467. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
  468. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
  469. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
  470. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
  471. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
  472. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
  473. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
  474. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
  475. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
  476. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
  477. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
  478. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
  479. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
  480. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
  481. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
  482. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
  483. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
  484. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
  485. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
  486. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
  487. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
  488. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
  489. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
  490. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
  491. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
  492. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
  493. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
  494. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
  495. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
  496. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
  497. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
  498. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
  499. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
  500. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
  501. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
  502. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
  503. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
  504. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
  505. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
  506. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
  507. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
  508. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
  509. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
  510. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
  511. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
  512. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
  513. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
  514. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
  515. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
  516. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
  517. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
  518. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
  519. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
  520. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
  521. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
  522. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
  523. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
  524. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
  525. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
  526. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
  527. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
  528. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
  529. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
  530. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
  531. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
  532. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
  533. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
  534. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
  535. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
  536. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
  537. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
  538. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
  539. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
  540. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
  541. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
  542. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
  543. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
  544. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
  545. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
  546. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
  547. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
  548. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
  549. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
  550. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
  551. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
  552. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
  553. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
  554. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
  555. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
  556. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
  557. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
  558. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
  559. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
  560. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
  561. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
  562. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
  563. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
  564. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
  565. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
  566. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
  567. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
  568. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
  569. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
  570. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
  571. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
  572. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
  573. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
  574. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
  575. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
  576. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
  577. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
  578. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
  579. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
  580. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
  581. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
  582. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
  583. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
  584. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
  585. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
  586. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
  587. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
  588. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
  589. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
  590. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
  591. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
  592. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
  593. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
  594. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
  595. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
  596. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
  597. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
  598. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
  599. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
  600. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
  601. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
  602. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
  603. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
  604. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
  605. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
  606. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
  607. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
  608. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
  609. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
  610. package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
  611. package/src/plugins/builtin/world-venue-map/pane.tsx +28 -43
  612. package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
  613. package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
  614. package/src/plugins/builtin/yield-curve/headless.ts +16 -0
  615. package/src/plugins/catalog-browser.ts +2 -0
  616. package/src/plugins/catalog-manifest.ts +1 -0
  617. package/src/plugins/catalog-ui.ts +3 -1
  618. package/src/plugins/catalog.ts +1 -1
  619. package/src/plugins/host-link.ts +33 -1
  620. package/src/plugins/installer.ts +48 -12
  621. package/src/plugins/loader.ts +30 -3
  622. package/src/plugins/manager-node.ts +15 -5
  623. package/src/plugins/registry/index.ts +1 -1
  624. package/src/plugins/registry/pane-settings.ts +26 -7
  625. package/src/public/__snapshots__/public-api.test.ts.snap +2 -0
  626. package/src/renderers/dom/build-assets.ts +81 -3
  627. package/src/renderers/dom/data-table/row.tsx +52 -16
  628. package/src/renderers/dom/desktop/controls.tsx +7 -3
  629. package/src/renderers/dom/desktop/icons.tsx +1 -0
  630. package/src/renderers/dom/desktop/list-view.tsx +35 -10
  631. package/src/renderers/dom/desktop/popover.tsx +73 -14
  632. package/src/renderers/dom/host/box.tsx +69 -26
  633. package/src/renderers/dom/host/input.tsx +14 -3
  634. package/src/renderers/dom/host/tabs.tsx +37 -1
  635. package/src/renderers/dom/styles.css +41 -7
  636. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +11 -6
  637. package/src/renderers/electrobun/bun/desktop/backend.ts +1 -0
  638. package/src/renderers/electrobun/bun/desktop/update.ts +181 -87
  639. package/src/renderers/electrobun/bun/index.ts +73 -6
  640. package/src/renderers/electrobun/bun/window/remembered-frame.ts +122 -0
  641. package/src/renderers/electrobun/shared/protocol.ts +3 -0
  642. package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
  643. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +3 -1
  644. package/src/renderers/electrobun/view/main.tsx +7 -1
  645. package/src/renderers/electrobun/view/renderer-window-error.ts +18 -0
  646. package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
  647. package/src/renderers/electrobun/view/update-host.ts +45 -21
  648. package/src/renderers/opentui/host.tsx +5 -0
  649. package/src/sources/gloomberb-cloud/index.ts +25 -4
  650. package/src/sources/gloomberb-cloud/normalizers.ts +36 -5
  651. package/src/sources/provider-router/cached-routes.ts +4 -1
  652. package/src/sources/provider-router/financials.ts +11 -8
  653. package/src/sources/provider-router/history.ts +15 -10
  654. package/src/sources/sec-registrant.ts +75 -0
  655. package/src/state/app/context/index.tsx +24 -8
  656. package/src/state/hooks/quote-streaming.ts +41 -26
  657. package/src/sync/core-contributors.ts +4 -0
  658. package/src/telemetry/benign-window-error.ts +7 -0
  659. package/src/telemetry/crash-reports-dom.ts +2 -0
  660. package/src/telemetry/crash-reports.ts +4 -1
  661. package/src/test-support/cli-context.ts +10 -2
  662. package/src/test-support/daily-nav.ts +25 -0
  663. package/src/theme/heat-colors.ts +357 -2
  664. package/src/tickers/instrument-kind.ts +34 -7
  665. package/src/tickers/search/asset-classes.ts +24 -9
  666. package/src/tickers/search/index.ts +44 -11
  667. package/src/tickers/search/ranking.ts +23 -3
  668. package/src/time-series/resolve.ts +1 -1
  669. package/src/time-series/use-chart-resolution.ts +1 -1
  670. package/src/types/context-menu.ts +6 -0
  671. package/src/types/data-provider.ts +2 -0
  672. package/src/types/financials.ts +5 -0
  673. package/src/types/headless.ts +79 -2
  674. package/src/types/plugin.ts +38 -6
  675. package/src/types/ticker.ts +15 -0
  676. package/src/ui/context-menu.tsx +1 -0
  677. package/src/ui/host.tsx +16 -0
  678. package/src/updater/index.ts +38 -13
  679. package/src/utils/async-deadline.ts +21 -0
  680. package/src/utils/cli-output.ts +5 -1
  681. package/src/utils/crypto-pair.ts +41 -0
  682. package/src/utils/currency-units.ts +14 -0
  683. package/src/utils/depositary-receipt.ts +26 -0
  684. package/src/utils/exchanges.ts +72 -0
  685. package/src/utils/fire-and-forget.ts +24 -0
  686. package/src/utils/format.ts +13 -0
  687. package/src/utils/price-earnings.ts +70 -3
  688. package/src/utils/price-history.ts +58 -13
  689. package/src/utils/rpc-timeout-error.ts +27 -0
  690. package/src/utils/sec.ts +13 -1
  691. package/src/utils/utc-time.ts +79 -0
@@ -0,0 +1,146 @@
1
+ import type { MarketHeatmapAsset } from "../../../api-client/market-discovery";
2
+
3
+ /**
4
+ * The board's grouping and move arithmetic, shared by the pane and the `fn HM`
5
+ * report so a sector reads the same in both. Isomorphic: no React or renderer.
6
+ */
7
+
8
+ type HeatmapMoveAsset = Pick<MarketHeatmapAsset, "symbol" | "hasChange" | "changePercent" | "size" | "sector" | "industry"> & {
9
+ /** Treemap area, when it is not `size` itself. */
10
+ weight?: number;
11
+ };
12
+
13
+ /**
14
+ * The move, or null when there is none: no change data is not a flat session.
15
+ * A name without the last session's close still has the move of the open
16
+ * pre-market or after-hours session it streams (`extendedSession`).
17
+ */
18
+ export function heatmapMove(asset: Pick<MarketHeatmapAsset, "hasChange" | "changePercent"> & { extendedSession?: string }): number | null {
19
+ return (asset.hasChange || asset.extendedSession != null) && typeof asset.changePercent === "number" && Number.isFinite(asset.changePercent)
20
+ ? asset.changePercent
21
+ : null;
22
+ }
23
+
24
+ export type HeatmapGrouping = "sector-industry" | "sector" | "flat";
25
+
26
+ /**
27
+ * Sector and industry blocks when the board knows its sectors. A board where
28
+ * most of the value has none (ETFs, or a snapshot whose classification
29
+ * failed) lays out flat rather than as one big unlabeled block.
30
+ */
31
+ export function resolveHeatmapGrouping(assets: readonly HeatmapMoveAsset[], industries: boolean): HeatmapGrouping {
32
+ let total = 0;
33
+ let classified = 0;
34
+ const sectors = new Set<string>();
35
+ for (const asset of assets) {
36
+ const weight = asset.weight ?? asset.size ?? 0;
37
+ total += weight;
38
+ if (asset.sector) {
39
+ classified += weight;
40
+ sectors.add(asset.sector);
41
+ }
42
+ }
43
+ if (sectors.size < 2 || classified < total / 2) return "flat";
44
+ return industries ? "sector-industry" : "sector";
45
+ }
46
+
47
+ /**
48
+ * The move of a set of names weighted by their size (market cap, an ETF's net
49
+ * assets, a position's value), over the names that have both. Null when none do.
50
+ */
51
+ export function sizeWeightedMove(assets: readonly HeatmapMoveAsset[]): number | null {
52
+ let weight = 0;
53
+ let weightedMove = 0;
54
+ for (const asset of assets) {
55
+ const change = heatmapMove(asset);
56
+ const size = asset.size ?? 0;
57
+ if (change == null || !(size > 0)) continue;
58
+ weight += size;
59
+ weightedMove += change * size;
60
+ }
61
+ return weight > 0 ? weightedMove / weight : null;
62
+ }
63
+
64
+ export type HeatmapGroupBy = "sector" | "industry";
65
+
66
+ export interface HeatmapGroupSummary {
67
+ /** The sector or industry; null for names the snapshot does not classify, or every name on a flat board. */
68
+ group: string | null;
69
+ /** The industry's sector, when grouping by industry. */
70
+ sector: string | null;
71
+ names: number;
72
+ /** Total size: market cap, or net assets on the ETF board. */
73
+ size: number;
74
+ /** This group's share of the board's total size. */
75
+ share: number;
76
+ move: number | null;
77
+ /** Names with a move, and the size they carry; the rest have no change data yet. */
78
+ moved: number;
79
+ up: number;
80
+ down: number;
81
+ unchanged: number;
82
+ best: { symbol: string; move: number } | null;
83
+ worst: { symbol: string; move: number } | null;
84
+ }
85
+
86
+ function summarize(group: string | null, sector: string | null, assets: readonly HeatmapMoveAsset[], boardSize: number): HeatmapGroupSummary {
87
+ let up = 0;
88
+ let down = 0;
89
+ let unchanged = 0;
90
+ let size = 0;
91
+ let best: HeatmapGroupSummary["best"] = null;
92
+ let worst: HeatmapGroupSummary["worst"] = null;
93
+ for (const asset of assets) {
94
+ size += asset.size ?? 0;
95
+ const move = heatmapMove(asset);
96
+ if (move == null) continue;
97
+ if (move > 0) up += 1;
98
+ else if (move < 0) down += 1;
99
+ else unchanged += 1;
100
+ if (!best || move > best.move) best = { symbol: asset.symbol, move };
101
+ if (!worst || move < worst.move) worst = { symbol: asset.symbol, move };
102
+ }
103
+ return {
104
+ group,
105
+ sector,
106
+ names: assets.length,
107
+ size,
108
+ share: boardSize > 0 ? size / boardSize : 0,
109
+ move: sizeWeightedMove(assets),
110
+ moved: up + down + unchanged,
111
+ up,
112
+ down,
113
+ unchanged,
114
+ best,
115
+ worst,
116
+ };
117
+ }
118
+
119
+ /**
120
+ * One summary per sector or industry, largest first as the board lays them
121
+ * out. A flat board (ETFs) is one group of every name. Unclassified names form
122
+ * their own group last.
123
+ */
124
+ export function summarizeHeatmapGroups(
125
+ assets: readonly HeatmapMoveAsset[],
126
+ groupBy: HeatmapGroupBy,
127
+ ): { grouping: HeatmapGrouping; groups: HeatmapGroupSummary[]; board: HeatmapGroupSummary } {
128
+ const grouping = resolveHeatmapGrouping(assets, groupBy === "industry");
129
+ const boardSize = assets.reduce((sum, asset) => sum + (asset.size ?? 0), 0);
130
+ const board = summarize(null, null, assets, boardSize);
131
+ if (grouping === "flat") return { grouping, groups: [board], board };
132
+ const buckets = new Map<string, { group: string | null; sector: string | null; assets: HeatmapMoveAsset[] }>();
133
+ for (const asset of assets) {
134
+ const sector = asset.sector ?? null;
135
+ const group = sector == null ? null : grouping === "sector-industry" ? asset.industry ?? null : sector;
136
+ const key = `${sector ?? ""}\u0000${group ?? ""}`;
137
+ const bucket = buckets.get(key) ?? { group, sector, assets: [] };
138
+ bucket.assets.push(asset);
139
+ buckets.set(key, bucket);
140
+ }
141
+ const groups = [...buckets.values()]
142
+ .map((bucket) => summarize(bucket.group, grouping === "sector-industry" ? bucket.sector : null, bucket.assets, boardSize))
143
+ .sort((left, right) => Number(left.sector == null && left.group == null) - Number(right.sector == null && right.group == null)
144
+ || right.size - left.size);
145
+ return { grouping, groups, board };
146
+ }
@@ -3,10 +3,28 @@ import type { MarketHeatmapAsset, MarketHeatmapUniverseId } from "../../../api-c
3
3
  import { useAppSelector, usePaneStateValue } from "../../../state/app/context";
4
4
  import type { ColumnConfig } from "../../../types/config";
5
5
  import type { TickerFinancials } from "../../../types/financials";
6
+ import { getSessionMoveDisplay, type ExtendedSession } from "../../../market-data/market/status";
6
7
  import type { TickerRecord } from "../../../types/ticker";
7
8
  import { getSortValue, type ColumnContext } from "../portfolio-list/metrics";
8
9
 
9
10
  export const PORTFOLIO_HEATMAP_TAB = "portfolio";
11
+
12
+ /**
13
+ * How a market board's tile area follows size. The square root, the default,
14
+ * gives mid-size names room next to the largest; groups still sum their
15
+ * tiles, so a sector's block stays the sum of its transformed names.
16
+ * Portfolio holdings always take their position value as it is.
17
+ */
18
+ export type HeatmapSizeBy = "market-cap" | "sqrt-market-cap";
19
+
20
+ /** A saved choice wins; a pane that never chose gets the square root. */
21
+ export function heatmapSizeBy(value: unknown): HeatmapSizeBy {
22
+ return value === "market-cap" ? "market-cap" : "sqrt-market-cap";
23
+ }
24
+
25
+ export function heatmapSizeWeight(size: number, sizeBy: HeatmapSizeBy): number {
26
+ return sizeBy === "sqrt-market-cap" ? Math.sqrt(size) : size;
27
+ }
10
28
  const MAX_PORTFOLIO_TILES = 160;
11
29
 
12
30
  export type HeatmapTabId = MarketHeatmapUniverseId | typeof PORTFOLIO_HEATMAP_TAB;
@@ -19,6 +37,8 @@ export interface HeatmapBoardAsset extends MarketHeatmapAsset {
19
37
  sizeCurrency?: string;
20
38
  /** Treemap area, when it is not `size` itself. */
21
39
  weight?: number;
40
+ /** The price and move are this open pre-market or after-hours session's, from the regular close. */
41
+ extendedSession?: ExtendedSession;
22
42
  }
23
43
 
24
44
  export interface HeatmapPortfolioPane {
@@ -31,6 +51,19 @@ export function heatmapTabId(value: string | null | undefined): HeatmapTabId {
31
51
  return "us-equity";
32
52
  }
33
53
 
54
+ /** Pane state where the pane says which kind of list its portfolio tab shows, for the header to read. */
55
+ export const HEATMAP_COLLECTION_KIND_STATE_KEY = "collectionKind";
56
+
57
+ /**
58
+ * Whether Size by changes the board: the market boards and a watchlist, never
59
+ * a portfolio, whose holdings are always sized by position value. Until the
60
+ * pane has said which list its tab shows, the control stays away.
61
+ */
62
+ export function heatmapSizeByApplies(universe: unknown, collectionKind: unknown): boolean {
63
+ return heatmapTabId(typeof universe === "string" ? universe : null) !== PORTFOLIO_HEATMAP_TAB
64
+ || collectionKind === "watchlist";
65
+ }
66
+
34
67
  export function isRemoteHeatmapUniverse(value: HeatmapTabId): value is MarketHeatmapUniverseId {
35
68
  return value !== PORTFOLIO_HEATMAP_TAB;
36
69
  }
@@ -147,11 +180,12 @@ function positiveNumber(value: number | string | null): number | null {
147
180
  /**
148
181
  * A portfolio's holdings are sized by market value, read as the portfolio pane
149
182
  * reads its MKT VALUE column: lots, contract multipliers and price basis, in
150
- * the portfolio's currency. A watchlist's names are sized by the square root
151
- * of market cap in the base currency, so a mega-cap does not hide the rest of
152
- * a short list. A name with no size (no position, no quote, no cap, no FX
153
- * rate) gets the smallest tile and no caption. Past `MAX_PORTFOLIO_TILES`,
154
- * the smallest names are left out and counted in `omitted`.
183
+ * the portfolio's currency, which a holder reads linearly whatever `sizeBy`
184
+ * says. A watchlist's names are sized by market cap in the base currency, by
185
+ * default its square root (`sizeBy`). A name with no
186
+ * size (no position, no quote, no cap, no FX rate) gets the smallest tile and
187
+ * no caption. Past `MAX_PORTFOLIO_TILES`, the smallest names are left out and
188
+ * counted in `omitted`.
155
189
  */
156
190
  export function buildPortfolioHeatmapAssets({
157
191
  tickers,
@@ -160,6 +194,7 @@ export function buildPortfolioHeatmapAssets({
160
194
  kind,
161
195
  currency,
162
196
  exchangeRates,
197
+ sizeBy = "sqrt-market-cap",
163
198
  }: {
164
199
  tickers: readonly TickerRecord[];
165
200
  financials: ReadonlyMap<string, TickerFinancials>;
@@ -168,6 +203,7 @@ export function buildPortfolioHeatmapAssets({
168
203
  /** The portfolio's totals currency, or the base currency for a watchlist. */
169
204
  currency: string;
170
205
  exchangeRates: Map<string, number>;
206
+ sizeBy?: HeatmapSizeBy;
171
207
  }): { assets: HeatmapBoardAsset[]; omitted: number } {
172
208
  const context: ColumnContext = {
173
209
  activeTab: kind === "portfolio" ? collectionId : undefined,
@@ -180,18 +216,21 @@ export function buildPortfolioHeatmapAssets({
180
216
  const symbol = ticker.metadata.ticker;
181
217
  const snapshot = financials.get(symbol);
182
218
  const quote = snapshot?.quote;
183
- const price = quote != null && Number.isFinite(quote.price) ? quote.price : null;
219
+ // Colored like the market boards: an open extended session's move, else the regular session's.
220
+ const display = getSessionMoveDisplay(quote);
221
+ const price = display != null && Number.isFinite(display.price) ? display.price : null;
184
222
  const size = positiveNumber(getSortValue(sizeColumn, ticker, snapshot, context));
185
- const hasChange = quote != null && Number.isFinite(quote.changePercent);
223
+ const hasChange = display?.changePercent != null && Number.isFinite(display.changePercent);
186
224
  return {
187
225
  symbol,
188
226
  name: quote?.name?.trim() || ticker.metadata.name || symbol,
189
227
  price: price ?? 0,
190
- change: quote != null && Number.isFinite(quote.change) ? quote.change : 0,
191
- changePercent: hasChange ? quote.changePercent : 0,
228
+ change: display?.change != null && Number.isFinite(display.change) ? display.change : 0,
229
+ changePercent: hasChange ? display.changePercent! : 0,
192
230
  hasChange,
231
+ ...(display?.session ? { extendedSession: display.session } : {}),
193
232
  size,
194
- weight: size == null ? undefined : kind === "portfolio" ? size : Math.sqrt(size),
233
+ weight: size == null ? undefined : kind === "portfolio" ? size : heatmapSizeWeight(size, sizeBy),
195
234
  sizeKind: "market-cap",
196
235
  sizeCaption: kind === "portfolio" ? "Value" : undefined,
197
236
  sizeCurrency: currency,
@@ -212,7 +251,7 @@ export function buildPortfolioHeatmapAssets({
212
251
  const kept = measured.slice(0, MAX_PORTFOLIO_TILES);
213
252
  const sizes = kept.flatMap((asset) => (asset.size != null ? [asset.size] : []));
214
253
  const floor = sizes.length > 0 ? Math.min(...sizes) : 1;
215
- const floorWeight = kind === "portfolio" ? floor : Math.sqrt(floor);
254
+ const floorWeight = kind === "portfolio" ? floor : heatmapSizeWeight(floor, sizeBy);
216
255
  return {
217
256
  assets: kept.map((asset) => (asset.size != null ? asset : { ...asset, weight: floorWeight })),
218
257
  omitted: measured.length - kept.length,
@@ -10,6 +10,7 @@ import {
10
10
  fetchPreferredMarketMovers,
11
11
  fetchTrending,
12
12
  type MarketMoversResult,
13
+ type PreferredMoversOptions,
13
14
  type ScreenerQuote,
14
15
  type TrendingSymbol,
15
16
  } from "./screener";
@@ -22,7 +23,7 @@ export interface MarketMoverTabDependencies {
22
23
  fetchPreferred(
23
24
  category: "day_gainers" | "day_losers" | "most_actives",
24
25
  count: number,
25
- options?: { forceRefresh?: boolean },
26
+ options?: PreferredMoversOptions,
26
27
  ): Promise<MarketMoversResult>;
27
28
  fetchTrending(count: number, options?: { forceRefresh?: boolean }): Promise<TrendingSymbol[]>;
28
29
  }
@@ -65,7 +66,7 @@ async function hydrateTrending(
65
66
  export async function loadMarketMoverTab(
66
67
  tab: ScreenerTabId,
67
68
  provider: DataProvider | null,
68
- options?: { forceRefresh?: boolean },
69
+ options?: Pick<PreferredMoversOptions, "forceRefresh" | "metadataWaitMs">,
69
70
  dependencies: MarketMoverTabDependencies = defaultDependencies,
70
71
  ): Promise<MarketMoverTabResult> {
71
72
  if (tab === "trending") {
@@ -64,8 +64,15 @@ export interface MarketMoversHeadlessDependencies {
64
64
  ): Promise<CloudSessionMoversPayload>;
65
65
  }
66
66
 
67
+ /**
68
+ * Headless reads, including VIEW sources, wait for average volumes rather
69
+ * than returning a partial first paint. Keep that wait bounded by the usual
70
+ * market request timeout; interactive MOST retains its shorter cutoff.
71
+ */
72
+ const ONE_SHOT_METADATA_WAIT_MS = 10_000;
73
+
67
74
  const defaultDependencies: MarketMoversHeadlessDependencies = {
68
- load: (_args, tab, provider) => loadMarketMoverTab(tab, provider),
75
+ load: (_args, tab, provider) => loadMarketMoverTab(tab, provider, { metadataWaitMs: ONE_SHOT_METADATA_WAIT_MS }),
69
76
  loadSession: (view, side) => loadSessionMovers(view, side),
70
77
  };
71
78
 
@@ -74,6 +81,8 @@ export function createMarketMoversHeadless(
74
81
  ): HeadlessPaneDefinition<"rows"> {
75
82
  return {
76
83
  shape: "rows",
84
+ // A screener snapshot, not a real-time feed; rows keep the time of their own last price.
85
+ freshness: { status: "delayed" },
77
86
  argument: { kind: "none" },
78
87
  options: [{
79
88
  key: "list",
@@ -18,6 +18,7 @@ const CACHE_POLICY = {
18
18
  staleMs: 5 * 60 * 1000,
19
19
  expireMs: 60 * 60 * 1000,
20
20
  } as const;
21
+ /** A pane shows the live ranking first; the backend's average volumes follow if they are quick. */
21
22
  const MARKET_METADATA_WAIT_MS = 1_500;
22
23
 
23
24
  export interface MarketScreenerApi {
@@ -30,6 +31,14 @@ export interface FetchCacheOptions {
30
31
  forceRefresh?: boolean;
31
32
  }
32
33
 
34
+ export interface PreferredMoversOptions extends FetchCacheOptions {
35
+ /**
36
+ * How long a Cloud list waits for the backend's average volumes and market
37
+ * caps, which it borrows by symbol: without them every row has no ratio.
38
+ */
39
+ metadataWaitMs?: number;
40
+ }
41
+
33
42
  let marketMoversPersistence: PluginPersistence | null = null;
34
43
  const activeFetches = new Map<string, Promise<unknown>>();
35
44
  const failedFetches = new Set<string>();
@@ -243,6 +252,8 @@ function mergeCloudScreenerItem(
243
252
  metadata?: ScreenerQuote,
244
253
  ): ScreenerQuote {
245
254
  const currency = typeof item.currency === "string" ? item.currency.trim() : "";
255
+ const directAverage = screenerNumber(item.avgVolume);
256
+ const avgVolume = directAverage != null && directAverage > 0 ? directAverage : metadata?.avgVolume ?? null;
246
257
  const metadataPrice = (value: number | undefined) => convertScreenerPriceUnit(value, metadata?.currency ?? "", currency);
247
258
  return {
248
259
  symbol: item.symbol,
@@ -253,8 +264,8 @@ function mergeCloudScreenerItem(
253
264
  change: screenerNumber(item.change),
254
265
  changePercent: screenerNumber(item.changePercent),
255
266
  volume: screenerVolume(item.volume),
256
- avgVolume: metadata?.avgVolume ?? null,
257
- volumeRatio: screenerVolumeRatio(screenerVolume(item.volume), metadata?.avgVolume ?? null),
267
+ avgVolume,
268
+ volumeRatio: screenerVolumeRatio(screenerVolume(item.volume), avgVolume),
258
269
  marketCap: metadata?.marketCap,
259
270
  currency,
260
271
  fiftyTwoWeekHigh: screenerNumber(item.high52w) ?? metadataPrice(metadata?.fiftyTwoWeekHigh),
@@ -268,13 +279,14 @@ function mergeCloudScreenerItem(
268
279
 
269
280
  async function bestEffortMarketMetadata(
270
281
  request: Promise<CachedResult<ScreenerQuote[]>>,
282
+ waitMs: number,
271
283
  ): Promise<ScreenerQuote[]> {
272
284
  let timeout: ReturnType<typeof setTimeout> | undefined;
273
285
  try {
274
286
  return await Promise.race([
275
287
  request.then((result) => result.data).catch(() => []),
276
288
  new Promise<ScreenerQuote[]>((resolve) => {
277
- timeout = setTimeout(() => resolve([]), MARKET_METADATA_WAIT_MS);
289
+ timeout = setTimeout(() => resolve([]), waitMs);
278
290
  }),
279
291
  ]);
280
292
  } finally {
@@ -303,9 +315,10 @@ export function rankScreenerQuotes(category: ScreenerCategory, quotes: ScreenerQ
303
315
  export async function fetchPreferredMarketMovers(
304
316
  category: ScreenerCategory,
305
317
  count = 25,
306
- options?: FetchCacheOptions,
318
+ preferredOptions?: PreferredMoversOptions,
307
319
  sources: PreferredMarketMoverSources = defaultPreferredMarketMoverSources,
308
320
  ): Promise<MarketMoversResult> {
321
+ const { metadataWaitMs = MARKET_METADATA_WAIT_MS, ...options } = preferredOptions ?? {};
309
322
  if (!sources.isCloudEligible()) {
310
323
  const result = await sources.fetchMarket(category, count, options);
311
324
  return { quotes: rankScreenerQuotes(category, result.data), source: "gloom", stale: result.stale };
@@ -327,7 +340,7 @@ export async function fetchPreferredMarketMovers(
327
340
  && response.data
328
341
  && response.data.items.length > 0
329
342
  ) {
330
- const metadata = await bestEffortMarketMetadata(marketMetadata);
343
+ const metadata = await bestEffortMarketMetadata(marketMetadata, metadataWaitMs);
331
344
  const metadataBySymbol = new Map(metadata.map((quote) => [quote.symbol, quote]));
332
345
  return {
333
346
  quotes: rankScreenerQuotes(category, response.data.items.map((item) => (
@@ -120,6 +120,8 @@ function projectValuationHeadlessBundle(
120
120
 
121
121
  export const marketValuationHeadless: HeadlessPaneDefinition<"bundle"> = {
122
122
  shape: "bundle",
123
+ // Each indicator row checks its own release schedule and says when it is stale.
124
+ freshness: { status: "not-a-feed", basis: "published statistics" },
123
125
  argument: {
124
126
  kind: "free-text",
125
127
  placeholder: "indicator",
@@ -0,0 +1,53 @@
1
+ import { apiClient } from "../../../api-client";
2
+ import { ApiRequestError } from "../../../api-client/errors";
3
+ import type { FundMembersPayload, FundChangesPayload, MemberFundsPayload } from "../../../api-client/members";
4
+ import { createPluginCache } from "../../../data/plugin-cache";
5
+ import { cachedCloudResource, loadCloudResource, unavailableOnServer } from "../shared/cloud-resource";
6
+ import { coveredFund, notCoveredMessage } from "./model";
7
+ const policy = { staleMs: 60_000, expireMs: 2 * 60 * 60_000 };
8
+ export const fundListCache = createPluginCache<MemberFundsPayload>({ kind: "member-funds", source: "gloom-cloud", schemaVersion: 1, policy });
9
+ export const fundMembersCache = createPluginCache<FundMembersPayload>({ kind: "fund-members", source: "gloom-cloud", schemaVersion: 1, policy });
10
+ export const fundChangesCache = createPluginCache<FundChangesPayload>({ kind: "fund-changes", source: "gloom-cloud", schemaVersion: 1, policy });
11
+ type Client = Pick<typeof apiClient, "getMemberFunds" | "getFundMembers" | "getFundChanges">;
12
+ const number = (value: unknown) => value === null || typeof value === "number" && Number.isFinite(value);
13
+ function validateMembers(data: FundMembersPayload) {
14
+ if (!data?.fund?.ticker || !Number.isFinite(Date.parse(data.asOf)) || !Array.isArray(data.members)
15
+ || !data.members.every((row) => typeof row.id === "string" && typeof row.name === "string" && [row.weight, row.price, row.changePercent, row.return1WPercent, row.return1MPercent, row.returnYtdPercent, row.contribution].every(number))
16
+ || !data.aggregate || !number(data.aggregate.sum)) throw new Error("Unreadable fund holdings response");
17
+ return data;
18
+ }
19
+ function validateChanges(data: FundChangesPayload) {
20
+ if (!data?.fund?.ticker || !Array.isArray(data.changes) || !data.changes.every((row) => typeof row.id === "string" && typeof row.reason === "string" && Array.isArray(row.estimates))) throw new Error("Unreadable fund changes response");
21
+ return data;
22
+ }
23
+ function validateFunds(data: MemberFundsPayload) {
24
+ if (!data || !Array.isArray(data.funds) || !data.funds.every((fund) => typeof fund.ticker === "string" && typeof fund.name === "string")) throw new Error("Unreadable covered funds response");
25
+ return data;
26
+ }
27
+ const UNAVAILABLE = "Fund members are not available on this server yet.";
28
+ async function fetchPayload<T>(fetcher: () => Promise<T>, validate: (data: T) => T) {
29
+ try { return validate(await fetcher()); } catch (error) { throw unavailableOnServer(error, UNAVAILABLE); }
30
+ }
31
+ /**
32
+ * A 404 for one fund means either an uncovered fund or a server without the route, and the two read the
33
+ * same. The covered list tells them apart: a fund missing from a readable list is not covered; a list
34
+ * that cannot be read, or that holds the fund, leaves the server-side message.
35
+ */
36
+ async function fetchFundPayload<T>(fund: string, fetcher: () => Promise<T>, validate: (data: T) => T, client: Client) {
37
+ try { return validate(await fetcher()); } catch (error) {
38
+ if (error instanceof ApiRequestError && error.status === 404) {
39
+ const funds = await fetchFunds(client).then((payload) => payload.funds, () => []);
40
+ if (funds.length && !coveredFund(funds, fund)) throw new Error(notCoveredMessage(fund, funds));
41
+ }
42
+ throw unavailableOnServer(error, UNAVAILABLE);
43
+ }
44
+ }
45
+ export const fetchFunds = (client: Client = apiClient) => fetchPayload(() => client.getMemberFunds(), validateFunds);
46
+ export const fetchMembers = (fund: string, client: Client = apiClient) => fetchFundPayload(fund, () => client.getFundMembers(fund), validateMembers, client);
47
+ export const fetchChanges = (fund: string, client: Client = apiClient) => fetchFundPayload(fund, () => client.getFundChanges(fund), validateChanges, client);
48
+ export const cachedFunds = () => cachedCloudResource(fundListCache, "list", validateFunds);
49
+ export const loadFunds = (force = false) => loadCloudResource(fundListCache, "list", () => fetchFunds(), { force, validate: validateFunds });
50
+ export const cachedMembers = (fund: string) => cachedCloudResource(fundMembersCache, fund, validateMembers);
51
+ export const loadMembers = (fund: string, force = false) => loadCloudResource(fundMembersCache, fund, () => fetchMembers(fund), { force, validate: validateMembers });
52
+ export const cachedChanges = (fund: string) => cachedCloudResource(fundChangesCache, fund, validateChanges);
53
+ export const loadChanges = (fund: string, force = false) => loadCloudResource(fundChangesCache, fund, () => fetchChanges(fund), { force, validate: validateChanges });
@@ -0,0 +1,35 @@
1
+ import type { HeadlessPaneColumn, HeadlessPaneDefinition } from "../../../types/plugin";
2
+ import { fetchChanges, fetchFunds, fetchMembers } from "./client";
3
+ import { canonicalFund, changeReason, decimal, DEFAULT_SORT, memberRows, percent } from "./model";
4
+ const columns: HeadlessPaneColumn[] = [
5
+ { key: "symbol", header: "Ticker" }, { key: "name", header: "Name" },
6
+ { key: "weight", header: "Weight %", align: "right", format: (value) => value == null ? "--" : decimal(Number(value) * 100) },
7
+ { key: "shares", header: "Shares", align: "right", format: (value) => decimal(value as number | null, 0) },
8
+ { key: "price", header: "Price $", align: "right", format: (value) => decimal(value as number | null) },
9
+ ...["changePercent", "return1WPercent", "return1MPercent", "returnYtdPercent"].map((key, i) => ({ key, header: ["1D", "1W", "1M", "YTD"][i]!, align: "right" as const, format: (value: unknown) => percent(value as number | null) })),
10
+ ];
11
+ export const membersHeadless: HeadlessPaneDefinition<"rows"> = {
12
+ shape: "rows", argument: { kind: "ticker", optional: true, placeholder: "fund", description: "Covered fund or index alias; omit for the fund list." },
13
+ options: [{ key: "tab", type: "enum", values: [{ value: "members" }, { value: "movers" }, { value: "changes" }], defaultValue: "members", description: "Members, movers or changes." }],
14
+ discovery: { aliases: ["MEMB", "MRR", "IMOV"], limitations: ["ETF holdings, with file dates", "Partial delayed member return coverage", "Nasdaq-100 is not covered"] },
15
+ describe: (args) => args.rawArgument ? `MEMB ${args.rawArgument}` : "Index and ETF members",
16
+ async load(args, context) {
17
+ const input = args.symbols[0] ?? args.rawArgument ?? "";
18
+ if (!input) return { freshness: { status: "not-a-feed", basis: "fund holdings files" }, rows: (await fetchFunds(context.apiClient)).funds.map((fund) => ({ ...fund })), columns: [{ key: "ticker", header: "Fund" }, { key: "name", header: "Index" }, { key: "asOf", header: "Holdings as of" }] };
19
+ const fund = canonicalFund(input);
20
+ if (args.options.tab === "changes") {
21
+ const data = await fetchChanges(fund, context.apiClient);
22
+ return { freshness: { status: "not-a-feed", basis: "index changes" }, rows: data.changes.map((row) => ({ ...row, reason: changeReason(row) })), columns: [{ key: "effectiveDate", header: "Effective" }, { key: "added", header: "Added" }, { key: "removed", header: "Removed" }, { key: "daysToGo", header: "In days", align: "right" }, { key: "reason", header: "Reason" }],
23
+ complete: data.available && !data.stale, metadata: { ...data, changes: undefined } };
24
+ }
25
+ const data = await fetchMembers(fund, context.apiClient);
26
+ const movers = args.options.tab === "movers";
27
+ const rows = memberRows(data.members, movers ? "movers" : "members", "", DEFAULT_SORT).flatMap((row) => row.kind === "item" ? [{ ...row.item }] : []);
28
+ // Member prices are the pane's 15-minute delayed quotes. As in the pane, the fund's snapshot says
29
+ // whether it is stale; the members' own flags are not what the pane reads.
30
+ return { freshness: { status: data.stale ? "stale" : "delayed", delayMinutes: 15, ignoreStaleFlags: true }, rows, columns: movers ? [...columns.slice(0, 3), { key: "contribution", header: "Contrib. pp", align: "right", format: (value) => percent(value as number | null, "", 3) }] : columns,
31
+ complete: !data.stale && data.aggregate.fresh1D === data.aggregate.total,
32
+ unavailableSymbols: data.members.flatMap((row) => row.symbol && row.changePercent === null ? [row.symbol] : []),
33
+ metadata: { fund: data.fund, asOf: data.asOf, snapshotAsOf: data.snapshotAsOf, aggregate: data.aggregate, stale: data.stale } };
34
+ },
35
+ };
@@ -0,0 +1,20 @@
1
+ import type { GloomPlugin } from "../../../types/plugin";
2
+ import { fundChangesCache, fundListCache, fundMembersCache } from "./client";
3
+ import { membersHeadless } from "./headless";
4
+ import { MembersPane } from "./pane";
5
+ import { membersTitle } from "./model";
6
+ export const membersPlugin: GloomPlugin = {
7
+ id: "members", name: "Index and ETF members", version: "1.0.0", description: "ETF holdings, member returns, contributions and index changes", toggleable: true,
8
+ setup(ctx) { fundListCache.attach(ctx.persistence); fundMembersCache.attach(ctx.persistence); fundChangesCache.attach(ctx.persistence); },
9
+ dispose() { fundListCache.reset(); fundMembersCache.reset(); fundChangesCache.reset(); },
10
+ panes: [{ id: "members", name: "Index and ETF members", icon: "M", component: MembersPane, tickerFollower: true,
11
+ defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 132, height: 32 }, tableExport: true,
12
+ settings: { fields: [{ key: "tab", label: "View", type: "select", options: [{ value: "members", label: "Members" }, { value: "movers", label: "Movers" }, { value: "changes", label: "Changes" }] }] } }],
13
+ paneTemplates: [{ id: "members-pane", paneId: "members", label: "Index and ETF members", description: "ETF holdings, member returns, daily contributions and index changes.",
14
+ keywords: ["members", "index", "ETF", "holdings", "weights", "rebalance", "MRR", "IMOV"],
15
+ shortcut: { prefix: "MEMB", argKind: "ticker", argPlaceholder: "fund", argOptional: true, openWithoutArg: true }, headless: membersHeadless,
16
+ createInstance: (_context, options) => { const symbol = (options?.arg ?? options?.symbol)?.trim().toUpperCase();
17
+ return { placement: "floating", title: membersTitle(symbol ?? ""),
18
+ ...(symbol ? { binding: { kind: "fixed", symbol } } : {}), settings: { tab: options?.values?.tab ?? "members" } }; },
19
+ }],
20
+ };
@@ -0,0 +1,68 @@
1
+ import type { FundChange, FundMember, MemberFundsPayload } from "../../../api-client/members";
2
+ import type { DataTableColumn } from "../../../components";
3
+ import { buildSectionedRows, type SectionedRow } from "../../../components/data-table/sections";
4
+ import { getTableWidth } from "../../../components/ui/table-layout";
5
+ import { compareSortValues, type SortPreference } from "../../../utils/sort-values";
6
+ export type MembersTab = "members" | "movers" | "changes";
7
+ export const TABS = [{ value: "members", label: "Members" }, { value: "movers", label: "Movers" }, { value: "changes", label: "Changes" }];
8
+ export const isTab = (value: unknown): value is MembersTab => TABS.some((tab) => tab.value === value);
9
+ const COVERED = [{ ticker: "IVV", name: "S&P 500" }, { ticker: "IJH", name: "S&P 400" }, { ticker: "IJR", name: "S&P 600" }, { ticker: "IWM", name: "Russell 2000" }, { ticker: "IWB", name: "Russell 1000" }];
10
+ export function canonicalFund(input: string) { const symbol = input.split(":")[0]!.trim().toUpperCase(); return ["SPX", "SPY", "^GSPC"].includes(symbol) ? "IVV" : symbol; }
11
+ /** The covered fund the user asked for, by ticker or by an alias such as SPX, or undefined when it is not covered. */
12
+ export function coveredFund(funds: MemberFundsPayload["funds"], input: string) {
13
+ const symbol = canonicalFund(input);
14
+ return funds.find((fund) => fund.ticker === symbol || fund.aliases.some((alias) => alias.toUpperCase() === symbol));
15
+ }
16
+ export function notCoveredMessage(input: string, funds: MemberFundsPayload["funds"]) {
17
+ return `${input.split(":")[0]!.trim().toUpperCase()} is not a covered fund. Covered: ${funds.map((fund) => fund.ticker).join(", ")}.`;
18
+ }
19
+ export function membersTitle(input: string) {
20
+ const requested = input.split(":")[0]!.trim().toUpperCase();
21
+ const fund = canonicalFund(requested);
22
+ const definition = COVERED.find((item) => item.ticker === fund);
23
+ return definition ? `${definition.name}, ${fund} holdings${requested !== fund ? ` (${requested})` : ""}`
24
+ : requested ? `MEMB ${requested}` : "Index and ETF members";
25
+ }
26
+ export const DEFAULT_SORT: SortPreference<string> = { columnId: "weight", direction: "desc" };
27
+ export const percent = (value: number | null, suffix = "%", digits = 2) => value == null || !Number.isFinite(value) ? "--" : `${value > 0 ? "+" : ""}${value.toFixed(digits)}${suffix}`;
28
+ export const decimal = (value: number | null, digits = 2) => value == null || !Number.isFinite(value) ? "--" : value.toLocaleString("en-US", { minimumFractionDigits: digits, maximumFractionDigits: digits });
29
+ export function sortMembers(rows: readonly FundMember[], sort: SortPreference<string>) {
30
+ return [...rows].sort((a, b) => compareSortValues(a[sort.columnId as keyof FundMember] as number | string | null,
31
+ b[sort.columnId as keyof FundMember] as number | string | null, sort.direction) || a.id.localeCompare(b.id));
32
+ }
33
+ export function memberRows(rows: readonly FundMember[], tab: MembersTab, query: string, sort: SortPreference<string>): SectionedRow<FundMember>[] {
34
+ const needle = query.trim().toLowerCase();
35
+ const filtered = rows.filter((row) => `${row.symbol ?? ""} ${row.name} ${row.sector ?? ""}`.toLowerCase().includes(needle));
36
+ if (tab !== "movers") return sortMembers(filtered, sort).map((item) => ({ kind: "item", key: item.id, item }));
37
+ const ranked = sortMembers(filtered.filter((row) => row.contribution !== null), { columnId: "contribution", direction: "desc" });
38
+ return buildSectionedRows([
39
+ { label: "Contributors", items: ranked.filter((row) => row.contribution! > 0).slice(0, 10) },
40
+ { label: "Detractors", items: ranked.filter((row) => row.contribution! < 0).reverse().slice(0, 10) },
41
+ ], (row) => row.id);
42
+ }
43
+ const column = (id: string, label: string, width: number, align: "left" | "right" = "right"): DataTableColumn => ({ id, label, width, align });
44
+ function fit(columns: DataTableColumn[], width: number, drops: string[]) {
45
+ for (const id of drops) { if (getTableWidth(columns) <= width) break; columns = columns.filter((col) => col.id !== id); }
46
+ const flexible = columns.find((col) => col.id === "name" || col.id === "reason") ?? columns[0]!;
47
+ flexible.width = Math.max(6, flexible.width + Math.max(0, width - getTableWidth(columns)));
48
+ flexible.flexGrow = 1;
49
+ return columns;
50
+ }
51
+ export function memberColumns(width: number, movers = false): DataTableColumn[] {
52
+ const compact = width < 50;
53
+ return fit([column("symbol", "Ticker", 8, "left"), column("name", "Name", 22, "left"),
54
+ column("weight", compact ? "Wt. %" : "Weight %", compact ? 7 : 10), ...(movers ? [column("contribution", compact ? "pp" : "Contrib. pp", compact ? 7 : 13)] : []),
55
+ ...(!movers ? [column("shares", "Shares", 14), column("price", "Price $", 11)] : []),
56
+ column("changePercent", "1D", compact ? 8 : 9), ...(!movers ? [column("return1WPercent", "1W", 9), column("return1MPercent", "1M", 9), column("returnYtdPercent", "YTD", 9), column("sector", "Sector", 22, "left")] : [])], width,
57
+ ["sector", "shares", "returnYtdPercent", "return1MPercent", "return1WPercent", "price", "name"]);
58
+ }
59
+ export function changeColumns(width: number) {
60
+ const compact = width < 50;
61
+ return fit([column("effectiveDate", compact ? "Date" : "Effective", compact ? 10 : 12, "left"), column("daysToGo", compact ? "In" : "In days", compact ? 4 : 9), column("added", "Added", compact ? 7 : 8, "left"), column("removed", "Removed", 9, "left"), column("estimate", "Est. ADV days", 15), column("reason", "Reason", 28, "left")], width, ["estimate", "reason"]);
62
+ }
63
+ export const changeLabel = (row: FundChange) => row.added && row.removed ? `${row.added} replaces ${row.removed}`
64
+ : row.added ? `${row.added} added` : row.removed ? `${row.removed} removed` : "Index announcement";
65
+ export function changeReason(row: FundChange) {
66
+ if (row.kind !== "announcement" || row.reason !== row.headline || !row.added && !row.removed) return row.reason;
67
+ return row.added && row.removed ? "Index replacement" : row.added ? "Index addition" : "Index deletion";
68
+ }