gloomberb 0.16.1 → 0.16.3

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 (643) 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 +26 -8
  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 +2 -0
  8. package/src/api-client/market-discovery.ts +10 -0
  9. package/src/api-client/paths.ts +30 -1
  10. package/src/api-client/perps.ts +37 -0
  11. package/src/api-client/request.ts +5 -2
  12. package/src/api-client/types.ts +2 -0
  13. package/src/brokers/account-cache.ts +69 -0
  14. package/src/cli/command-options.ts +74 -0
  15. package/src/cli/commands/command-utils.ts +37 -0
  16. package/src/cli/commands/crypto-hints.ts +75 -0
  17. package/src/cli/commands/market.ts +560 -136
  18. package/src/cli/commands/overview.ts +136 -48
  19. package/src/cli/commands/ticker.ts +220 -67
  20. package/src/cli/desktop-pane-shot.ts +37 -4
  21. package/src/cli/freshness.ts +61 -0
  22. package/src/cli/help.ts +27 -6
  23. package/src/cli/helpers.ts +4 -8
  24. package/src/cli/history-rows.ts +187 -15
  25. package/src/cli/index.ts +23 -8
  26. package/src/cli/listing-arg.ts +321 -0
  27. package/src/cli/options.ts +5 -1
  28. package/src/cli/pane-functions/access-gate.ts +42 -0
  29. package/src/cli/pane-functions/capabilities.ts +5 -0
  30. package/src/cli/pane-functions/catalog.ts +28 -1
  31. package/src/cli/pane-functions/data.ts +3 -1
  32. package/src/cli/pane-functions/dom.ts +87 -16
  33. package/src/cli/pane-functions/freshness.ts +400 -0
  34. package/src/cli/pane-functions/function-help.ts +31 -22
  35. package/src/cli/pane-functions/headless.ts +201 -41
  36. package/src/cli/pane-functions/index.ts +24 -6
  37. package/src/cli/pane-functions/options.ts +3 -0
  38. package/src/cli/pane-functions/report.ts +12 -1
  39. package/src/cli/pane-functions/resolver.ts +63 -9
  40. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  41. package/src/cli/pane-functions/screenshot.ts +11 -3
  42. package/src/cli/registry.ts +6 -3
  43. package/src/cli/report-tables.ts +460 -0
  44. package/src/cli/result.ts +53 -29
  45. package/src/cli/ticker-resolution.ts +7 -5
  46. package/src/components/chart/composite/composite-chart.tsx +88 -2536
  47. package/src/components/chart/composite/drawing-store.ts +59 -0
  48. package/src/components/chart/composite/legend.tsx +319 -0
  49. package/src/components/chart/composite/panel-bitmap.ts +139 -0
  50. package/src/components/chart/composite/panel-gestures.ts +59 -0
  51. package/src/components/chart/composite/panel-labels.ts +107 -0
  52. package/src/components/chart/composite/panel-surface.tsx +876 -0
  53. package/src/components/chart/composite/tool-catalog.ts +98 -0
  54. package/src/components/chart/composite/toolbar.tsx +186 -0
  55. package/src/components/chart/composite/tools.ts +8 -0
  56. package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
  57. package/src/components/chart/composite/use-composite-viewport.ts +143 -0
  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/ticker-search/actions.ts +5 -0
  62. package/src/components/command-bar/workflow/ops.ts +1 -1
  63. package/src/components/command-bar/workflow/tickers.ts +6 -0
  64. package/src/components/data-table/export.ts +6 -51
  65. package/src/components/data-table/view.tsx +32 -11
  66. package/src/components/feedback-dialog.tsx +2 -1
  67. package/src/components/form-modal/field-row.tsx +2 -2
  68. package/src/components/layout/detached-pane-shell.tsx +6 -3
  69. package/src/components/layout/pane/content.tsx +23 -10
  70. package/src/components/layout/pane/error-boundary.tsx +103 -0
  71. package/src/components/layout/pane/footer/registration.tsx +68 -23
  72. package/src/components/layout/pane/header.tsx +11 -11
  73. package/src/components/layout/shell/active-drag.ts +67 -35
  74. package/src/components/layout/shell/cursor-occlusion.ts +6 -5
  75. package/src/components/layout/shell/drag/index.ts +12 -7
  76. package/src/components/layout/shell/drag/live.ts +122 -0
  77. package/src/components/layout/shell/drag/overlays.tsx +179 -35
  78. package/src/components/layout/shell/drag/runtime.ts +62 -50
  79. package/src/components/layout/shell/drag/slide.ts +10 -0
  80. package/src/components/layout/shell/index.tsx +37 -51
  81. package/src/components/layout/shell/menu.ts +2 -1
  82. package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
  83. package/src/components/layout/shell/native/surfaces.ts +30 -14
  84. package/src/components/layout/shell/pane/actions.ts +2 -1
  85. package/src/components/layout/shell/pane/layers.tsx +419 -168
  86. package/src/components/layout/shell/terminal-pointer-runtime.ts +18 -18
  87. package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
  88. package/src/components/metric-treemap/heat-surface.tsx +43 -16
  89. package/src/components/metric-treemap/heat-tiles.ts +18 -0
  90. package/src/components/metric-treemap/layout.ts +2 -0
  91. package/src/components/onboarding/onboarding-keyboard.ts +311 -0
  92. package/src/components/onboarding/onboarding-wizard.tsx +101 -788
  93. package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
  94. package/src/components/onboarding/use-onboarding-progress.ts +298 -0
  95. package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
  96. package/src/components/ticker/list-table-view.tsx +13 -1
  97. package/src/components/ui/fields.tsx +8 -0
  98. package/src/components/ui/range-track.tsx +10 -3
  99. package/src/components/ui/status.tsx +8 -2
  100. package/src/components/ui/tabs.tsx +7 -4
  101. package/src/components/ui/text-prompt-dialog.tsx +4 -2
  102. package/src/data/config/store/normalize.ts +17 -1
  103. package/src/data/json-persistence.ts +17 -3
  104. package/src/data/json-storage.ts +2 -0
  105. package/src/data/memory-resource-store.ts +46 -0
  106. package/src/i18n/es.ts +19 -1
  107. package/src/i18n/ja.ts +19 -1
  108. package/src/i18n/ko.ts +19 -1
  109. package/src/i18n/zh-cn.ts +19 -1
  110. package/src/i18n/zh-tw.ts +19 -1
  111. package/src/market-data/coordinator/chart.ts +1 -1
  112. package/src/market-data/coordinator/quotes.ts +23 -4
  113. package/src/market-data/fx-closes.ts +72 -0
  114. package/src/market-data/market/freshness.ts +123 -6
  115. package/src/market-data/market/session-close-note.ts +57 -0
  116. package/src/market-data/market/status.ts +151 -0
  117. package/src/market-data/published-apac-sessions.ts +66 -0
  118. package/src/market-data/quotes/contributions.ts +2 -0
  119. package/src/market-data/quotes/freshness.ts +5 -0
  120. package/src/market-data/quotes/resolution.ts +2 -0
  121. package/src/market-data/quotes/screener-live-quotes.ts +49 -4
  122. package/src/plugins/builtin/account-management/pane.tsx +4 -0
  123. package/src/plugins/builtin/alerts/cli.ts +10 -5
  124. package/src/plugins/builtin/alerts/index.tsx +1 -0
  125. package/src/plugins/builtin/analytics/metrics.ts +8 -3
  126. package/src/plugins/builtin/analytics/pane-model.ts +10 -6
  127. package/src/plugins/builtin/analytics/risk-client.ts +191 -66
  128. package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
  129. package/src/plugins/builtin/analytics/risk-headless.ts +19 -13
  130. package/src/plugins/builtin/analytics/risk-model.ts +45 -17
  131. package/src/plugins/builtin/analytics/risk-pane.tsx +1 -0
  132. package/src/plugins/builtin/analytics/risk-test-data.ts +25 -1
  133. package/src/plugins/builtin/attention/headless.ts +6 -4
  134. package/src/plugins/builtin/attention/index.tsx +1 -1
  135. package/src/plugins/builtin/attention/pane.tsx +2 -2
  136. package/src/plugins/builtin/awards/headless.ts +1 -0
  137. package/src/plugins/builtin/awards/pane.tsx +1 -1
  138. package/src/plugins/builtin/backtest/headless.ts +1 -0
  139. package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
  140. package/src/plugins/builtin/buildout/index.tsx +1 -0
  141. package/src/plugins/builtin/catalysts/detail.tsx +1 -1
  142. package/src/plugins/builtin/catalysts/headless.ts +3 -1
  143. package/src/plugins/builtin/cds/headless.ts +2 -0
  144. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
  145. package/src/plugins/builtin/chart-composer/headless.ts +38 -3
  146. package/src/plugins/builtin/chart-composer/index.tsx +3 -1
  147. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
  148. package/src/plugins/builtin/chat/channels.ts +19 -3
  149. package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
  150. package/src/plugins/builtin/chat/content/footer.tsx +15 -6
  151. package/src/plugins/builtin/chat/content/index.tsx +120 -21
  152. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
  153. package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
  154. package/src/plugins/builtin/chat/direct-messages.ts +200 -0
  155. package/src/plugins/builtin/chat/index.tsx +1 -0
  156. package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
  157. package/src/plugins/builtin/chat/message/header.tsx +16 -6
  158. package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
  159. package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
  160. package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
  161. package/src/plugins/builtin/chat/message/types.ts +14 -2
  162. package/src/plugins/builtin/chat/profile-popover.ts +18 -13
  163. package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
  164. package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
  165. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  166. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  167. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  168. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  169. package/src/plugins/builtin/comment-letters/index.tsx +1 -0
  170. package/src/plugins/builtin/company-attention/headless.ts +5 -1
  171. package/src/plugins/builtin/company-attention/pane.tsx +2 -2
  172. package/src/plugins/builtin/company-kpis/headless.ts +1 -0
  173. package/src/plugins/builtin/congress-trades/headless.ts +1 -0
  174. package/src/plugins/builtin/correlation/compute.ts +59 -16
  175. package/src/plugins/builtin/correlation/geo.ts +34 -0
  176. package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
  177. package/src/plugins/builtin/correlation/headless.ts +42 -8
  178. package/src/plugins/builtin/correlation/history.ts +2 -0
  179. package/src/plugins/builtin/correlation/index.tsx +68 -14
  180. package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
  181. package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
  182. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  183. package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
  184. package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
  185. package/src/plugins/builtin/correlation/settings.ts +1 -1
  186. package/src/plugins/builtin/cot/headless.ts +6 -3
  187. package/src/plugins/builtin/cot/index.tsx +3 -3
  188. package/src/plugins/builtin/cot/model.ts +36 -3
  189. package/src/plugins/builtin/cpi/headless.ts +4 -0
  190. package/src/plugins/builtin/credit-boards/headless.ts +2 -0
  191. package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
  192. package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
  193. package/src/plugins/builtin/credit-documents/headless.ts +2 -0
  194. package/src/plugins/builtin/crypto-board/headless.ts +3 -0
  195. package/src/plugins/builtin/custom-view/loader.ts +1 -1
  196. package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
  197. package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
  198. package/src/plugins/builtin/doe/headless.ts +7 -0
  199. package/src/plugins/builtin/doe/range-bar.tsx +7 -37
  200. package/src/plugins/builtin/earnings/headless.ts +5 -0
  201. package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
  202. package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
  203. package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
  204. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
  205. package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
  206. package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
  207. package/src/plugins/builtin/econ/index.tsx +1 -0
  208. package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
  209. package/src/plugins/builtin/equity-screener/headless.ts +2 -0
  210. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  211. package/src/plugins/builtin/executives/index.tsx +1 -0
  212. package/src/plugins/builtin/exposure/headless.ts +3 -1
  213. package/src/plugins/builtin/fear-greed/headless.ts +2 -0
  214. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  215. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  216. package/src/plugins/builtin/futures-curve/headless.ts +39 -10
  217. package/src/plugins/builtin/futures-curve/index.tsx +3 -3
  218. package/src/plugins/builtin/futures-curve/model.ts +188 -4
  219. package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
  220. package/src/plugins/builtin/gpu/headless.ts +7 -3
  221. package/src/plugins/builtin/gpu/ladder.tsx +26 -30
  222. package/src/plugins/builtin/gpu/model.ts +27 -1
  223. package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
  224. package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
  225. package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
  226. package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
  227. package/src/plugins/builtin/holders/client.ts +3 -6
  228. package/src/plugins/builtin/holders/format.ts +31 -1
  229. package/src/plugins/builtin/holders/headless.ts +161 -6
  230. package/src/plugins/builtin/holders/index.tsx +10 -3
  231. package/src/plugins/builtin/holders/pane.tsx +42 -16
  232. package/src/plugins/builtin/holders/report-header.ts +136 -0
  233. package/src/plugins/builtin/holders/table-model.ts +20 -5
  234. package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
  235. package/src/plugins/builtin/holders/types.ts +2 -1
  236. package/src/plugins/builtin/insider/headless.ts +2 -0
  237. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
  238. package/src/plugins/builtin/iv-history/headless.ts +4 -1
  239. package/src/plugins/builtin/jobs/headless.ts +1 -0
  240. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
  241. package/src/plugins/builtin/kelly-sizer/pane.tsx +12 -6
  242. package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
  243. package/src/plugins/builtin/macro-day/headless.ts +2 -0
  244. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  245. package/src/plugins/builtin/market-heatmap/data.ts +2 -0
  246. package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
  247. package/src/plugins/builtin/market-heatmap/index.tsx +39 -46
  248. package/src/plugins/builtin/market-heatmap/model.ts +146 -0
  249. package/src/plugins/builtin/market-heatmap/portfolio.ts +23 -4
  250. package/src/plugins/builtin/market-movers/headless.ts +2 -0
  251. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  252. package/src/plugins/builtin/members/client.ts +20 -3
  253. package/src/plugins/builtin/members/headless.ts +5 -3
  254. package/src/plugins/builtin/members/model.ts +10 -2
  255. package/src/plugins/builtin/members/pane.tsx +23 -6
  256. package/src/plugins/builtin/mna/distress/headless.ts +1 -0
  257. package/src/plugins/builtin/mna/headless.ts +1 -0
  258. package/src/plugins/builtin/money-markets/headless.ts +1 -0
  259. package/src/plugins/builtin/news/headless.ts +2 -0
  260. package/src/plugins/builtin/news/wire/index.tsx +8 -1
  261. package/src/plugins/builtin/notes/cli.ts +14 -6
  262. package/src/plugins/builtin/notes/index.tsx +1 -0
  263. package/src/plugins/builtin/openfda/index.tsx +1 -0
  264. package/src/plugins/builtin/options-calculator/headless.ts +2 -0
  265. package/src/plugins/builtin/options-positioning/headless.ts +3 -0
  266. package/src/plugins/builtin/options-scenario/headless.ts +5 -0
  267. package/src/plugins/builtin/pe-band/client.ts +48 -3
  268. package/src/plugins/builtin/pe-band/headless.ts +25 -7
  269. package/src/plugins/builtin/pe-band/model.ts +92 -16
  270. package/src/plugins/builtin/pe-band/pane.tsx +33 -16
  271. package/src/plugins/builtin/perps/client.ts +50 -3
  272. package/src/plugins/builtin/perps/headless.ts +144 -16
  273. package/src/plugins/builtin/perps/index.tsx +6 -6
  274. package/src/plugins/builtin/perps/model.ts +189 -12
  275. package/src/plugins/builtin/perps/pane.tsx +249 -48
  276. package/src/plugins/builtin/portfolio-list/account-metrics.ts +16 -1
  277. package/src/plugins/builtin/portfolio-list/allocation.ts +248 -0
  278. package/src/plugins/builtin/portfolio-list/cached-account.ts +42 -0
  279. package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
  280. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
  281. package/src/plugins/builtin/portfolio-list/cli/render.ts +232 -39
  282. package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
  283. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
  284. package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
  285. package/src/plugins/builtin/portfolio-list/headless.ts +100 -16
  286. package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
  287. package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
  288. package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
  289. package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
  290. package/src/plugins/builtin/portfolio-list/pane/index.tsx +53 -16
  291. package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
  292. package/src/plugins/builtin/portfolio-list/summary/index.tsx +46 -5
  293. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
  294. package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
  295. package/src/plugins/builtin/power/headless.ts +3 -1
  296. package/src/plugins/builtin/rate-path/headless.ts +2 -0
  297. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  298. package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
  299. package/src/plugins/builtin/research/analyst-headless.ts +3 -0
  300. package/src/plugins/builtin/research/events-headless.ts +2 -0
  301. package/src/plugins/builtin/research/headless.ts +2 -0
  302. package/src/plugins/builtin/research/index.tsx +1 -0
  303. package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
  304. package/src/plugins/builtin/research/relative-valuation-model.ts +18 -6
  305. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  306. package/src/plugins/builtin/research-search/index.tsx +1 -0
  307. package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
  308. package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
  309. package/src/plugins/builtin/reverse-dcf/model.ts +2 -1
  310. package/src/plugins/builtin/risk-factors/headless.ts +2 -0
  311. package/src/plugins/builtin/scanner/flow-history.ts +2 -13
  312. package/src/plugins/builtin/scanner/headless.ts +14 -0
  313. package/src/plugins/builtin/seasonality/headless.ts +2 -0
  314. package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
  315. package/src/plugins/builtin/sec/filing-content.ts +42 -17
  316. package/src/plugins/builtin/sec/forms.ts +12 -0
  317. package/src/plugins/builtin/sec/headless.ts +2 -0
  318. package/src/plugins/builtin/sectors/client.ts +1 -0
  319. package/src/plugins/builtin/sectors/headless.ts +9 -1
  320. package/src/plugins/builtin/sectors/sector-model.ts +2 -0
  321. package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
  322. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  323. package/src/plugins/builtin/shared/members-menu.tsx +260 -0
  324. package/src/plugins/builtin/shared/report-freshness.ts +57 -0
  325. package/src/plugins/builtin/shared/research-cells.tsx +4 -9
  326. package/src/plugins/builtin/short-interest/headless.ts +2 -0
  327. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  328. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  329. package/src/plugins/builtin/short-volume/pane.tsx +1 -1
  330. package/src/plugins/builtin/social-mentions/headless.ts +1 -0
  331. package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
  332. package/src/plugins/builtin/supply-chain/headless.ts +7 -1
  333. package/src/plugins/builtin/supply-chain/index.tsx +10 -1
  334. package/src/plugins/builtin/supply-chain/model.ts +27 -2
  335. package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
  336. package/src/plugins/builtin/themes/headless.ts +3 -0
  337. package/src/plugins/builtin/themes/pane.tsx +3 -0
  338. package/src/plugins/builtin/thirteenf/headless.ts +110 -25
  339. package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
  340. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
  341. package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
  342. package/src/plugins/builtin/ticker-detail/headless.ts +27 -5
  343. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  344. package/src/plugins/builtin/ticker-detail/overview/model.ts +17 -5
  345. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +27 -26
  346. package/src/plugins/builtin/time-sales/headless.ts +5 -1
  347. package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
  348. package/src/plugins/builtin/volatility/headless.ts +6 -1
  349. package/src/plugins/builtin/world-indices/headless.ts +2 -0
  350. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +159 -0
  351. package/src/plugins/builtin/world-venue-map/basemap.ts +232 -0
  352. package/src/plugins/builtin/world-venue-map/client.ts +45 -0
  353. package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
  354. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
  355. package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
  356. package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
  357. package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
  358. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
  359. package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
  360. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +448 -0
  361. package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
  362. package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
  363. package/src/plugins/builtin/world-venue-map/index.ts +69 -6
  364. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +565 -0
  365. package/src/plugins/builtin/world-venue-map/layers.ts +406 -0
  366. package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
  367. package/src/plugins/builtin/world-venue-map/map.tsx +557 -85
  368. package/src/plugins/builtin/world-venue-map/model.ts +23 -4
  369. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +202 -0
  370. package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
  371. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
  372. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
  373. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
  374. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
  375. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
  376. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
  377. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
  378. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
  379. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
  380. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
  381. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
  382. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
  383. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
  384. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
  385. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
  386. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
  387. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
  388. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
  389. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
  390. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
  391. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
  392. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
  393. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
  394. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
  395. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
  396. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
  397. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
  398. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
  399. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
  400. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
  401. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
  402. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
  403. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
  404. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
  405. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
  406. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
  407. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
  408. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
  409. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
  410. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
  411. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
  412. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
  413. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
  414. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
  415. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
  416. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
  417. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
  418. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
  419. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
  420. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
  421. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
  422. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
  423. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
  424. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
  425. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
  426. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
  429. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
  430. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
  431. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
  432. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
  433. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
  434. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
  435. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
  436. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
  437. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
  438. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
  439. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
  440. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
  441. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
  442. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
  443. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
  444. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
  445. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
  446. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
  447. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
  448. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
  449. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
  450. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
  451. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
  452. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
  453. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
  454. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
  455. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
  456. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
  457. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
  458. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
  459. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
  460. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
  461. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
  462. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
  463. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
  464. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
  465. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
  466. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
  467. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
  468. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
  469. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
  470. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
  471. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
  472. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
  473. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
  474. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
  475. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
  476. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
  477. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
  478. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
  479. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
  480. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
  481. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
  482. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
  483. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
  484. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
  485. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
  486. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
  487. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
  488. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
  489. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
  490. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
  491. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
  492. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
  493. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
  494. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
  495. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
  496. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
  497. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
  498. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
  499. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
  500. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
  501. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
  502. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
  503. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
  504. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
  505. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
  506. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
  507. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
  508. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
  509. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
  510. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
  511. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
  512. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
  513. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
  514. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
  515. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
  516. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
  517. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
  518. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
  519. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
  520. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
  521. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
  522. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
  523. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
  524. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
  525. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
  526. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
  527. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
  528. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
  529. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
  530. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
  531. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
  532. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
  533. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
  534. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
  535. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
  536. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
  537. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
  538. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
  539. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
  540. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
  541. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
  542. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
  543. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
  544. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
  545. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
  546. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
  547. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
  548. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
  549. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
  550. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
  551. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
  552. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
  553. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
  554. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
  555. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
  556. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
  557. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
  558. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
  559. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
  560. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
  561. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
  562. package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
  563. package/src/plugins/builtin/world-venue-map/pane.tsx +35 -44
  564. package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
  565. package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
  566. package/src/plugins/builtin/yield-curve/headless.ts +16 -0
  567. package/src/plugins/loader.ts +13 -1
  568. package/src/plugins/registry/pane-settings.ts +1 -0
  569. package/src/renderers/browser/app-services.ts +3 -1
  570. package/src/renderers/dom/build-assets.ts +81 -3
  571. package/src/renderers/dom/data-table/dom.ts +31 -0
  572. package/src/renderers/dom/data-table/index.tsx +5 -0
  573. package/src/renderers/dom/data-table/row.tsx +2 -0
  574. package/src/renderers/dom/desktop/controls.tsx +7 -3
  575. package/src/renderers/dom/desktop/list-view.tsx +35 -10
  576. package/src/renderers/dom/desktop/popover.tsx +73 -14
  577. package/src/renderers/dom/host/box.tsx +113 -17
  578. package/src/renderers/dom/host/drag-shield.ts +24 -0
  579. package/src/renderers/dom/host/input.tsx +14 -3
  580. package/src/renderers/dom/host/live-frame.ts +54 -0
  581. package/src/renderers/dom/host/mouse.ts +8 -1
  582. package/src/renderers/dom/host/scroll-box.tsx +6 -5
  583. package/src/renderers/dom/host/tabs.tsx +3 -2
  584. package/src/renderers/dom/styles.css +16 -9
  585. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
  586. package/src/renderers/electrobun/bun/desktop/backend.ts +2 -0
  587. package/src/renderers/electrobun/bun/desktop/initialization.ts +13 -0
  588. package/src/renderers/electrobun/shared/protocol.ts +10 -1
  589. package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
  590. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -0
  591. package/src/renderers/electrobun/view/host/native.ts +0 -10
  592. package/src/renderers/electrobun/view/host/window-move.ts +28 -0
  593. package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
  594. package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
  595. package/src/renderers/electrobun/view/ui-host.tsx +2 -5
  596. package/src/renderers/opentui/host.tsx +5 -0
  597. package/src/renderers/opentui/ui-host.tsx +7 -1
  598. package/src/sources/gloomberb-cloud/index.ts +25 -4
  599. package/src/sources/gloomberb-cloud/normalizers.ts +43 -23
  600. package/src/sources/provider-router/batches.ts +3 -3
  601. package/src/sources/provider-router/cached-routes.ts +4 -1
  602. package/src/sources/provider-router/financial-routes.ts +2 -2
  603. package/src/sources/provider-router/financials.ts +76 -12
  604. package/src/sources/provider-router/history.ts +15 -10
  605. package/src/sources/provider-router/primary.ts +2 -2
  606. package/src/sources/sec-registrant.ts +75 -0
  607. package/src/state/app/context/index.tsx +24 -8
  608. package/src/state/hooks/quote-streaming.ts +41 -26
  609. package/src/state/pane-scroll-registry.tsx +67 -35
  610. package/src/sync/core-contributors.ts +4 -0
  611. package/src/telemetry/crash-reports.ts +4 -1
  612. package/src/test-support/cli-context.ts +10 -2
  613. package/src/test-support/margin-account.ts +108 -0
  614. package/src/theme/heat-colors.ts +266 -84
  615. package/src/time-series/resolution.ts +15 -0
  616. package/src/time-series/resolve.ts +1 -1
  617. package/src/time-series/use-chart-resolution.ts +1 -1
  618. package/src/types/context-menu.ts +6 -0
  619. package/src/types/data-provider.ts +2 -0
  620. package/src/types/financials.ts +35 -3
  621. package/src/types/headless.ts +79 -2
  622. package/src/types/plugin.ts +20 -1
  623. package/src/types/ticker.ts +15 -0
  624. package/src/ui/host.tsx +26 -0
  625. package/src/ui/index.tsx +8 -3
  626. package/src/ui/scroll-box-axis.ts +26 -0
  627. package/src/updater/index.ts +9 -11
  628. package/src/utils/async-deadline.ts +21 -0
  629. package/src/utils/cli-output.ts +5 -1
  630. package/src/utils/crypto-pair.ts +41 -0
  631. package/src/utils/currency-units.ts +19 -0
  632. package/src/utils/depositary-receipt.ts +70 -0
  633. package/src/utils/display-number.ts +63 -0
  634. package/src/utils/exchanges.ts +72 -0
  635. package/src/utils/fire-and-forget.ts +24 -0
  636. package/src/utils/format.ts +13 -0
  637. package/src/utils/fundamentals.ts +17 -1
  638. package/src/utils/price-earnings.ts +70 -3
  639. package/src/utils/price-history-integrity.ts +19 -6
  640. package/src/utils/price-history.ts +58 -13
  641. package/src/utils/rpc-timeout-error.ts +27 -0
  642. package/src/utils/sec.ts +13 -1
  643. package/src/utils/utc-time.ts +79 -0
@@ -0,0 +1,135 @@
1
+ import type { HeadlessPaneColumn, HeadlessPaneDefinition } from "../../../types/plugin";
2
+ import { formatCompact, formatPercentRaw } from "../../../utils/format";
3
+ import { MARKET_HEATMAP_REQUEST_COUNT, MARKET_HEATMAP_UNIVERSES, fetchMarketHeatmap, type MarketHeatmapUniverseId } from "./data";
4
+ import { heatmapMove, summarizeHeatmapGroups, type HeatmapGroupBy, type HeatmapGroupSummary } from "./model";
5
+
6
+ /**
7
+ * A few names always lack a move (foreign listings and new tickers the snapshot
8
+ * has no change for). They are listed in a notice and in `metadata.noMove`; the
9
+ * report is incomplete only when so many lack one that the groups mislead.
10
+ */
11
+ const MIN_MOVE_COVERAGE = 0.9;
12
+
13
+ const percent = (value: unknown) => typeof value === "number" ? formatPercentRaw(value) : "—";
14
+ const share = (value: unknown) => typeof value === "number" ? `${value.toFixed(1)}%` : "—";
15
+ const dollars = (value: unknown) => typeof value === "number" && value > 0 ? `$${formatCompact(value)}` : "—";
16
+ const member = (symbol: unknown, move: unknown) => typeof symbol === "string" && typeof move === "number"
17
+ ? `${symbol} ${formatPercentRaw(move)}`
18
+ : "—";
19
+
20
+ function columns(universe: MarketHeatmapUniverseId, groupBy: HeatmapGroupBy, flat: boolean): HeadlessPaneColumn[] {
21
+ return [
22
+ ...(flat ? [] : groupBy === "industry"
23
+ ? [{ key: "group", header: "Industry" }, { key: "sector", header: "Sector" }]
24
+ : [{ key: "group", header: "Sector" }]),
25
+ { key: "names", header: "Names", align: "right" },
26
+ { key: "size", header: universe === "us-etf" ? "Net assets" : "Mkt cap", align: "right", format: dollars },
27
+ ...(flat ? [] : [{ key: "sharePercent", header: "Share", align: "right" as const, format: share }]),
28
+ { key: "move", header: universe === "us-etf" ? "Move (asset-wtd)" : "Move (cap-wtd)", align: "right", format: percent },
29
+ { key: "up", header: "Up", align: "right" },
30
+ { key: "down", header: "Down", align: "right" },
31
+ { key: "bestSymbol", header: "Best", format: (value, row) => member(value, row.bestMove) },
32
+ { key: "worstSymbol", header: "Worst", format: (value, row) => member(value, row.worstMove) },
33
+ ];
34
+ }
35
+
36
+ function noMoveNotice(symbols: readonly string[], names: number): string {
37
+ const shown = symbols.slice(0, 8).join(", ");
38
+ const rest = symbols.length > 8 ? ` and ${symbols.length - 8} more` : "";
39
+ return `${symbols.length} of ${names} names have no move yet and count toward size only: ${shown}${rest}.`;
40
+ }
41
+
42
+ function projectRow(summary: HeatmapGroupSummary, groupBy: HeatmapGroupBy, whole = false) {
43
+ return {
44
+ group: whole ? "All" : summary.group ?? (summary.sector ? "No industry" : groupBy === "industry" ? "Unclassified" : "No sector"),
45
+ ...(groupBy === "industry" ? { sector: summary.sector ?? "—" } : {}),
46
+ names: summary.names,
47
+ size: summary.size,
48
+ sharePercent: summary.share * 100,
49
+ move: summary.move,
50
+ moved: summary.moved,
51
+ up: summary.up,
52
+ down: summary.down,
53
+ unchanged: summary.unchanged,
54
+ bestSymbol: summary.best?.symbol ?? null,
55
+ bestMove: summary.best?.move ?? null,
56
+ worstSymbol: summary.worst?.symbol ?? null,
57
+ worstMove: summary.worst?.move ?? null,
58
+ };
59
+ }
60
+
61
+ export const marketHeatmapHeadless: HeadlessPaneDefinition<"rows"> = {
62
+ shape: "rows",
63
+ description: "The heat map's sectors or industries: each group's size-weighted day move, how many names rose and fell, and its best and worst name, for the 500 largest US stocks or large US ETFs.",
64
+ argument: { kind: "none" },
65
+ options: [
66
+ {
67
+ key: "universe",
68
+ description: "Board: the 500 largest US stocks, or large US ETFs (one group, as the ETF map has no sectors).",
69
+ type: "enum",
70
+ values: [
71
+ { value: "us-equity", aliases: ["stocks", "equity", "us"] },
72
+ { value: "us-etf", aliases: ["etf", "etfs"] },
73
+ ],
74
+ defaultValue: "us-equity",
75
+ settingKey: "universe",
76
+ },
77
+ {
78
+ key: "group",
79
+ description: "Group the stocks by sector, or by industry within each sector.",
80
+ type: "enum",
81
+ values: [{ value: "sector", aliases: ["sectors"] }, { value: "industry", aliases: ["industries"] }],
82
+ defaultValue: "sector",
83
+ },
84
+ ],
85
+ discovery: {
86
+ aliases: ["HM", "heatmap"],
87
+ dataRequirements: ["Gloom Cloud market heatmap snapshot"],
88
+ limitations: [
89
+ "The snapshot the pane starts from, refreshed about once a minute; the pane's live-streamed colors are not in the report",
90
+ "Moves are weighted by market cap (net assets for ETFs), not by the pane's square-root tile area",
91
+ ],
92
+ },
93
+ // A board snapshot refreshed on a schedule, not a feed; the pane streams on top of it, the report does not.
94
+ freshness: { status: "delayed" },
95
+ describe: (args) => {
96
+ const universe = MARKET_HEATMAP_UNIVERSES.find((entry) => entry.id === args.options.universe)?.label ?? "US Stocks";
97
+ return args.options.universe === "us-etf" ? `Market Heatmap | ${universe}` : `Market Heatmap | ${universe} by ${String(args.options.group)}`;
98
+ },
99
+ async load(args, ctx) {
100
+ const universe: MarketHeatmapUniverseId = args.options.universe === "us-etf" ? "us-etf" : "us-equity";
101
+ const groupBy: HeatmapGroupBy = args.options.group === "industry" ? "industry" : "sector";
102
+ const result = await fetchMarketHeatmap(
103
+ universe,
104
+ { count: MARKET_HEATMAP_REQUEST_COUNT[universe], forceRefresh: ctx.refresh },
105
+ { client: ctx.apiClient },
106
+ );
107
+ ctx.signal.throwIfAborted();
108
+ const { grouping, groups, board } = summarizeHeatmapGroups(result.assets, groupBy);
109
+ const flat = grouping === "flat";
110
+ const noMove = result.assets.filter((asset) => heatmapMove(asset) == null).map((asset) => asset.symbol);
111
+ const covered = result.assets.length > 0 && board.moved / board.names >= MIN_MOVE_COVERAGE;
112
+ return {
113
+ columns: columns(universe, groupBy, flat),
114
+ rows: groups.map((summary) => projectRow(summary, groupBy, flat)),
115
+ complete: !result.stale && covered,
116
+ unavailableSymbols: covered ? [] : noMove,
117
+ metadata: {
118
+ universe,
119
+ groupBy: flat ? null : groupBy,
120
+ asOf: new Date(result.fetchedAt).toISOString(),
121
+ stale: result.stale === true,
122
+ session: result.session ?? null,
123
+ regularSessionDate: result.regularSessionDate ?? null,
124
+ names: result.assets.length,
125
+ noMove,
126
+ ...(noMove.length > 0 ? { notices: [noMoveNotice(noMove, result.assets.length)] } : {}),
127
+ board: projectRow(board, groupBy, true),
128
+ unit: "move and sharePercent in percent; size in USD",
129
+ weighting: universe === "us-etf"
130
+ ? "Each group's move is its names' day moves weighted by net assets, over the names with a move."
131
+ : "Each group's move is its names' day moves weighted by market cap, over the names with a move.",
132
+ },
133
+ };
134
+ },
135
+ };
@@ -29,7 +29,7 @@ import { t, tf } from "../../../i18n";
29
29
  import { priceColor } from "../../../theme/colors";
30
30
  import { clipToDisplayWidth, formatCompact, formatCurrency, formatPercentRaw } from "../../../utils/format";
31
31
  import { isPlainKey } from "../../../utils/keyboard";
32
- import { useAppSelector, usePaneAppConfig, usePaneSettingValue } from "../../../state/app/context";
32
+ import { useAppSelector, usePaneAppConfig, usePaneSettingValue, usePaneStateValue } from "../../../state/app/context";
33
33
  import { usePluginTickerActions } from "../../runtime";
34
34
  import { useLiveQuoteEntries } from "../../../state/hooks/quote-streaming";
35
35
  import { useFxRatesMap, useTickerFinancialsMap } from "../../../market-data/hooks";
@@ -44,18 +44,21 @@ import { resolvePortfolioTotalsCurrency } from "../portfolio-list/metrics";
44
44
  import { useThrottledMemo } from "../portfolio-list/use-throttled-memo";
45
45
  import { PORTFOLIO_REORDER_THROTTLE_MS } from "../portfolio-list/use-throttled-ticker-order";
46
46
  import {
47
+ MARKET_HEATMAP_REQUEST_COUNT,
47
48
  MARKET_HEATMAP_UNIVERSES,
48
49
  fetchMarketHeatmap,
49
50
  resetMarketHeatmapCache,
50
51
  type MarketHeatmapUniverseId,
51
52
  } from "./data";
52
53
  import {
54
+ HEATMAP_COLLECTION_KIND_STATE_KEY,
53
55
  PORTFOLIO_HEATMAP_TAB,
54
56
  buildPortfolioHeatmapAssets,
55
57
  fallbackHeatmapCollectionId,
56
58
  heatmapCollectionLabel,
57
59
  heatmapFollowsCollection,
58
60
  heatmapSizeBy,
61
+ heatmapSizeByApplies,
59
62
  heatmapSizeWeight,
60
63
  heatmapTabId,
61
64
  isRemoteHeatmapUniverse,
@@ -76,6 +79,8 @@ import {
76
79
  rankHeatmapQuoteWeights,
77
80
  useSettledValue,
78
81
  } from "./live";
82
+ import { marketHeatmapHeadless } from "./headless";
83
+ import { heatmapMove, resolveHeatmapGrouping, sizeWeightedMove, type HeatmapGrouping } from "./model";
79
84
  import {
80
85
  overlayScreenerQuoteEntries,
81
86
  resolveScreenerQuoteFeedStatus,
@@ -83,8 +88,6 @@ import {
83
88
 
84
89
  const NO_ASSETS: HeatmapBoardAsset[] = [];
85
90
  const NO_TARGETS: QuoteSubscriptionTarget[] = [];
86
- /** The 500 largest US stocks (an older server answers with its own maximum); ETFs stay flat and need fewer. */
87
- const MARKET_HEATMAP_REQUEST_COUNT: Record<MarketHeatmapUniverseId, number> = { "us-equity": 500, "us-etf": 160 };
88
91
  const SNAPSHOT_REFRESH_MS = 60_000;
89
92
  const LAYOUT_THROTTLE_MS = 5_000;
90
93
  /** A size change below this share of a tile's area is not worth moving it. */
@@ -128,34 +131,16 @@ function sizeLabel(asset: HeatmapBoardAsset): string | null {
128
131
  return `${label} ${formatMoneyCompact(asset.size, asset.sizeCurrency ?? asset.currency)}`;
129
132
  }
130
133
 
131
- /** The move, or null when there is none: no change data is not a flat session. */
132
- function changeOf(asset: HeatmapBoardAsset): number | null {
133
- return asset.hasChange && typeof asset.changePercent === "number" && Number.isFinite(asset.changePercent)
134
- ? asset.changePercent
135
- : null;
134
+ /** Marks a pre-market or after-hours move, which is measured from the regular close. */
135
+ function sessionMark(asset: HeatmapBoardAsset): string | null {
136
+ if (asset.extendedSession === "PRE") return "PM";
137
+ if (asset.extendedSession === "POST") return "AH";
138
+ return null;
136
139
  }
137
140
 
138
- type HeatmapGrouping = "sector-industry" | "sector" | "flat";
139
-
140
- /**
141
- * Sector and industry blocks when the board knows its sectors. A board where
142
- * most of the value has none (ETFs, or a snapshot whose classification
143
- * failed) lays out flat rather than as one big unlabeled block.
144
- */
145
- function resolveGrouping(assets: readonly HeatmapBoardAsset[], industries: boolean): HeatmapGrouping {
146
- let total = 0;
147
- let classified = 0;
148
- const sectors = new Set<string>();
149
- for (const asset of assets) {
150
- const weight = asset.weight ?? asset.size ?? 0;
151
- total += weight;
152
- if (asset.sector) {
153
- classified += weight;
154
- sectors.add(asset.sector);
155
- }
156
- }
157
- if (sectors.size < 2 || classified < total / 2) return "flat";
158
- return industries ? "sector-industry" : "sector";
141
+ function formatMove(asset: HeatmapBoardAsset, change: number | null): string {
142
+ const mark = sessionMark(asset);
143
+ return change == null ? "—" : mark ? `${formatPercentRaw(change)} ${mark}` : formatPercentRaw(change);
159
144
  }
160
145
 
161
146
  /**
@@ -191,7 +176,7 @@ const paintItemCache = new WeakMap<HeatmapBoardAsset, MetricTreemapItem<HeatmapB
191
176
  function paintItem(asset: HeatmapBoardAsset): MetricTreemapItem<HeatmapBoardAsset> {
192
177
  const cached = paintItemCache.get(asset);
193
178
  if (cached) return cached;
194
- const change = changeOf(asset);
179
+ const change = heatmapMove(asset);
195
180
  const details = [
196
181
  asset.price > 0 ? formatCurrency(asset.price, asset.currency) : null,
197
182
  sizeLabel(asset),
@@ -205,8 +190,9 @@ function paintItem(asset: HeatmapBoardAsset): MetricTreemapItem<HeatmapBoardAsse
205
190
  weight: asset.weight ?? asset.size ?? 0,
206
191
  colorValue: change,
207
192
  primaryText: formatHeatTileMove(change),
193
+ primaryTextSuffix: sessionMark(asset),
208
194
  tooltip: [
209
- [asset.symbol, name, change == null ? "—" : formatPercentRaw(change)].filter(Boolean).join(" · "),
195
+ [asset.symbol, name, formatMove(asset, change)].filter(Boolean).join(" · "),
210
196
  ...(details.length > 0 ? [details.join(" · ")] : []),
211
197
  ],
212
198
  data: asset,
@@ -224,18 +210,10 @@ function otherSummary(items: readonly MetricTreemapItem<HeatmapBoardAsset>[]): s
224
210
  }
225
211
 
226
212
  function otherTooltip(items: readonly MetricTreemapItem<HeatmapBoardAsset>[]): string[] {
227
- let weight = 0;
228
- let weightedMove = 0;
229
- for (const item of items) {
230
- const change = changeOf(item.data);
231
- const size = item.data.size ?? 0;
232
- if (change == null || !(size > 0)) continue;
233
- weight += size;
234
- weightedMove += change * size;
235
- }
213
+ const move = sizeWeightedMove(items.map((item) => item.data));
236
214
  return [
237
215
  tf("Other · {count} smaller names", { count: items.length }),
238
- ...(weight > 0 ? [`${formatPercentRaw(weightedMove / weight)} ${t("cap-weighted")}`] : []),
216
+ ...(move != null ? [`${formatPercentRaw(move)} ${t("cap-weighted")}`] : []),
239
217
  ];
240
218
  }
241
219
 
@@ -262,6 +240,7 @@ function MarketHeatmapPane({ focused, width, height }: PaneProps) {
262
240
  const linkedPortfolio = useLinkedHeatmapCollection();
263
241
  const collectionId = linkedPortfolio.collectionId ?? fallbackHeatmapCollectionId(config);
264
242
  const collectionKind = getCollectionTypeFromConfig(config, collectionId);
243
+ const [, publishCollectionKind] = usePaneStateValue<string>(HEATMAP_COLLECTION_KIND_STATE_KEY, "");
265
244
  const portfolioLabel = heatmapCollectionLabel(config, collectionId);
266
245
  const universeTabs = useMemo(() => [
267
246
  ...MARKET_HEATMAP_UNIVERSES.map((universe) => ({ label: universe.label, value: universe.id })),
@@ -367,7 +346,7 @@ function MarketHeatmapPane({ focused, width, height }: PaneProps) {
367
346
  liveStreaming,
368
347
  });
369
348
  const resolvedAssets = useMemo(
370
- () => overlayScreenerQuoteEntries(boardAssets, liveQuoteEntries),
349
+ () => overlayScreenerQuoteEntries(boardAssets, liveQuoteEntries, { extendedSessions: true }),
371
350
  [boardAssets, liveQuoteEntries],
372
351
  );
373
352
  const feedStatus = useMemo(
@@ -381,7 +360,7 @@ function MarketHeatmapPane({ focused, width, height }: PaneProps) {
381
360
  // Sizes follow live prices at most every LAYOUT_THROTTLE_MS and only past
382
361
  // LAYOUT_TOLERANCE; a new snapshot relays out at once. Ticks only recolour.
383
362
  const grouping = useMemo(
384
- () => resolveGrouping(boardAssets, activeUniverse === "us-equity"),
363
+ () => resolveHeatmapGrouping(boardAssets, activeUniverse === "us-equity"),
385
364
  [activeUniverse, boardAssets],
386
365
  );
387
366
  const layoutAssets = useThrottledMemo(
@@ -456,6 +435,11 @@ function MarketHeatmapPane({ focused, width, height }: PaneProps) {
456
435
  setSelectedSymbol(null);
457
436
  }, [collectionId, linkPortfolio, setActiveUniverse]);
458
437
 
438
+ // The header shows the square-root control only where it changes the board.
439
+ useEffect(() => {
440
+ publishCollectionKind(collectionKind ?? "");
441
+ }, [collectionKind, publishCollectionKind]);
442
+
459
443
  // The financials map only reads the cache; this fills it for the list on screen.
460
444
  useEffect(() => {
461
445
  if (!portfolioTab) return;
@@ -610,11 +594,12 @@ function MarketHeatmapPane({ focused, width, height }: PaneProps) {
610
594
  parts: [
611
595
  { text: selectedAsset.symbol, tone: "label" as const },
612
596
  {
613
- text: changeOf(selectedAsset) == null ? "—" : formatPercentRaw(changeOf(selectedAsset)!),
597
+ text: heatmapMove(selectedAsset) == null ? "—" : formatPercentRaw(heatmapMove(selectedAsset)!),
614
598
  tone: "value" as const,
615
- color: changeOf(selectedAsset) == null ? undefined : priceColor(changeOf(selectedAsset)!),
599
+ color: heatmapMove(selectedAsset) == null ? undefined : priceColor(heatmapMove(selectedAsset)!),
616
600
  bold: true,
617
601
  },
602
+ ...(sessionMark(selectedAsset) ? [{ text: sessionMark(selectedAsset)!, tone: "muted" as const }] : []),
618
603
  ...(selectedAsset.price > 0
619
604
  ? [{ text: formatCurrency(selectedAsset.price, selectedAsset.currency), tone: "value" as const }]
620
605
  : []),
@@ -696,12 +681,20 @@ export const marketHeatmapPlugin: GloomPlugin = {
696
681
  name: "Market Heatmap",
697
682
  icon: "H",
698
683
  component: MarketHeatmapPane,
684
+ headless: marketHeatmapHeadless,
699
685
  defaultPosition: "right",
700
686
  defaultMode: "floating",
701
687
  defaultFloatingSize: { width: 110, height: 36 },
702
688
  quickSettings: [
703
689
  LIVE_STREAMING_QUICK_SETTING,
704
- { type: "toggle", key: SIZE_BY_SETTING_KEY, icon: "sqrt", onValue: SQRT_SIZE_VALUE, label: "Size by square root of market cap" },
690
+ {
691
+ type: "toggle",
692
+ key: SIZE_BY_SETTING_KEY,
693
+ icon: "sqrt",
694
+ onValue: SQRT_SIZE_VALUE,
695
+ label: "Size by square root of market cap",
696
+ visible: (context) => heatmapSizeByApplies(context.settings.universe, context.paneState[HEATMAP_COLLECTION_KIND_STATE_KEY]),
697
+ },
705
698
  ],
706
699
  settings: (context) => withLiveStreamingSetting({
707
700
  title: "Market Heatmap Settings",
@@ -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,6 +3,7 @@ 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
 
@@ -36,6 +37,8 @@ export interface HeatmapBoardAsset extends MarketHeatmapAsset {
36
37
  sizeCurrency?: string;
37
38
  /** Treemap area, when it is not `size` itself. */
38
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;
39
42
  }
40
43
 
41
44
  export interface HeatmapPortfolioPane {
@@ -48,6 +51,19 @@ export function heatmapTabId(value: string | null | undefined): HeatmapTabId {
48
51
  return "us-equity";
49
52
  }
50
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
+
51
67
  export function isRemoteHeatmapUniverse(value: HeatmapTabId): value is MarketHeatmapUniverseId {
52
68
  return value !== PORTFOLIO_HEATMAP_TAB;
53
69
  }
@@ -200,16 +216,19 @@ export function buildPortfolioHeatmapAssets({
200
216
  const symbol = ticker.metadata.ticker;
201
217
  const snapshot = financials.get(symbol);
202
218
  const quote = snapshot?.quote;
203
- 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;
204
222
  const size = positiveNumber(getSortValue(sizeColumn, ticker, snapshot, context));
205
- const hasChange = quote != null && Number.isFinite(quote.changePercent);
223
+ const hasChange = display?.changePercent != null && Number.isFinite(display.changePercent);
206
224
  return {
207
225
  symbol,
208
226
  name: quote?.name?.trim() || ticker.metadata.name || symbol,
209
227
  price: price ?? 0,
210
- change: quote != null && Number.isFinite(quote.change) ? quote.change : 0,
211
- changePercent: hasChange ? quote.changePercent : 0,
228
+ change: display?.change != null && Number.isFinite(display.change) ? display.change : 0,
229
+ changePercent: hasChange ? display.changePercent! : 0,
212
230
  hasChange,
231
+ ...(display?.session ? { extendedSession: display.session } : {}),
213
232
  size,
214
233
  weight: size == null ? undefined : kind === "portfolio" ? size : heatmapSizeWeight(size, sizeBy),
215
234
  sizeKind: "market-cap",
@@ -81,6 +81,8 @@ export function createMarketMoversHeadless(
81
81
  ): HeadlessPaneDefinition<"rows"> {
82
82
  return {
83
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" },
84
86
  argument: { kind: "none" },
85
87
  options: [{
86
88
  key: "list",
@@ -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",
@@ -1,7 +1,9 @@
1
1
  import { apiClient } from "../../../api-client";
2
+ import { ApiRequestError } from "../../../api-client/errors";
2
3
  import type { FundMembersPayload, FundChangesPayload, MemberFundsPayload } from "../../../api-client/members";
3
4
  import { createPluginCache } from "../../../data/plugin-cache";
4
5
  import { cachedCloudResource, loadCloudResource, unavailableOnServer } from "../shared/cloud-resource";
6
+ import { coveredFund, notCoveredMessage } from "./model";
5
7
  const policy = { staleMs: 60_000, expireMs: 2 * 60 * 60_000 };
6
8
  export const fundListCache = createPluginCache<MemberFundsPayload>({ kind: "member-funds", source: "gloom-cloud", schemaVersion: 1, policy });
7
9
  export const fundMembersCache = createPluginCache<FundMembersPayload>({ kind: "fund-members", source: "gloom-cloud", schemaVersion: 1, policy });
@@ -22,12 +24,27 @@ function validateFunds(data: MemberFundsPayload) {
22
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");
23
25
  return data;
24
26
  }
27
+ const UNAVAILABLE = "Fund members are not available on this server yet.";
25
28
  async function fetchPayload<T>(fetcher: () => Promise<T>, validate: (data: T) => T) {
26
- try { return validate(await fetcher()); } catch (error) { throw unavailableOnServer(error, "Fund members are not available on this server yet."); }
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
+ }
27
44
  }
28
45
  export const fetchFunds = (client: Client = apiClient) => fetchPayload(() => client.getMemberFunds(), validateFunds);
29
- export const fetchMembers = (fund: string, client: Client = apiClient) => fetchPayload(() => client.getFundMembers(fund), validateMembers);
30
- export const fetchChanges = (fund: string, client: Client = apiClient) => fetchPayload(() => client.getFundChanges(fund), validateChanges);
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);
31
48
  export const cachedFunds = () => cachedCloudResource(fundListCache, "list", validateFunds);
32
49
  export const loadFunds = (force = false) => loadCloudResource(fundListCache, "list", () => fetchFunds(), { force, validate: validateFunds });
33
50
  export const cachedMembers = (fund: string) => cachedCloudResource(fundMembersCache, fund, validateMembers);
@@ -15,17 +15,19 @@ export const membersHeadless: HeadlessPaneDefinition<"rows"> = {
15
15
  describe: (args) => args.rawArgument ? `MEMB ${args.rawArgument}` : "Index and ETF members",
16
16
  async load(args, context) {
17
17
  const input = args.symbols[0] ?? args.rawArgument ?? "";
18
- if (!input) return { rows: (await fetchFunds(context.apiClient)).funds.map((fund) => ({ ...fund })), columns: [{ key: "ticker", header: "Fund" }, { key: "name", header: "Index" }, { key: "asOf", header: "Holdings as of" }] };
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
19
  const fund = canonicalFund(input);
20
20
  if (args.options.tab === "changes") {
21
21
  const data = await fetchChanges(fund, context.apiClient);
22
- return { 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" }],
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
23
  complete: data.available && !data.stale, metadata: { ...data, changes: undefined } };
24
24
  }
25
25
  const data = await fetchMembers(fund, context.apiClient);
26
26
  const movers = args.options.tab === "movers";
27
27
  const rows = memberRows(data.members, movers ? "movers" : "members", "", DEFAULT_SORT).flatMap((row) => row.kind === "item" ? [{ ...row.item }] : []);
28
- return { rows, columns: movers ? [...columns.slice(0, 3), { key: "contribution", header: "Contrib. pp", align: "right", format: (value) => percent(value as number | null, "", 3) }] : columns,
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,
29
31
  complete: !data.stale && data.aggregate.fresh1D === data.aggregate.total,
30
32
  unavailableSymbols: data.members.flatMap((row) => row.symbol && row.changePercent === null ? [row.symbol] : []),
31
33
  metadata: { fund: data.fund, asOf: data.asOf, snapshotAsOf: data.snapshotAsOf, aggregate: data.aggregate, stale: data.stale } };