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,32 @@
1
+ import type { CloudWorldVenuePayload } from "../../../api-client";
2
+ import { colors } from "../../../theme/colors";
3
+ import { Box, Text, TextAttributes } from "../../../ui";
4
+ import { formatVenueCountdown, formatVenueLocalTime, venueRemainingSeconds } from "./model";
5
+
6
+ export function SelectedVenueHeader({
7
+ venue,
8
+ checkedAt,
9
+ now,
10
+ width,
11
+ }: {
12
+ venue: CloudWorldVenuePayload | null;
13
+ checkedAt: number;
14
+ now: number;
15
+ width: number;
16
+ }) {
17
+ if (!venue) return <Box height={2} />;
18
+ const remaining = formatVenueCountdown(venueRemainingSeconds(venue, checkedAt, now));
19
+ const state = venue.isOpen ? "OPEN" : "CLOSED";
20
+ const transition = remaining ? `${venue.isOpen ? "closes" : "opens"} ${remaining}` : "";
21
+ return (
22
+ <Box flexDirection="column" height={2} width={width} paddingX={1}>
23
+ <Box flexDirection="row" justifyContent="space-between" width="100%">
24
+ <Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{venue.mic}</Text>
25
+ <Text fg={venue.isOpen ? colors.positive : colors.textDim}>{state}</Text>
26
+ </Box>
27
+ <Text fg={colors.textMuted}>
28
+ {`${venue.city}, ${venue.country} · ${formatVenueLocalTime(venue.timezone, now)}${transition ? ` · ${transition}` : ""}`}
29
+ </Text>
30
+ </Box>
31
+ );
32
+ }
@@ -52,6 +52,18 @@ const defaultDependencies: Required<YieldCurveHeadlessDependencies> = {
52
52
 
53
53
  const bp = (value: number | null) => value == null ? null : Math.round(value * 100);
54
54
 
55
+ /** The official publisher of each curve, as docs/research-data.md lists them. */
56
+ const CURVE_PUBLISHERS: Record<CurveId, string> = {
57
+ us: "US Treasury",
58
+ "us-real": "US Treasury",
59
+ "us-breakeven": "US Treasury",
60
+ "eu-aaa": "ECB",
61
+ de: "Bundesbank",
62
+ gb: "Bank of England",
63
+ jp: "Japan Ministry of Finance",
64
+ ca: "Bank of Canada",
65
+ };
66
+
55
67
  export function createYieldCurveHeadless(
56
68
  dependencies: YieldCurveHeadlessDependencies = defaultDependencies,
57
69
  ): HeadlessPaneDefinition<"rows"> {
@@ -80,6 +92,7 @@ export function createYieldCurveHeadless(
80
92
  if (args.options.tab === "world") {
81
93
  const rows = await loadWorld(context);
82
94
  return {
95
+ freshness: { source: "Government bond publishers", status: "not-a-feed", basis: "daily curves" },
83
96
  columns: WORLD_COLUMNS,
84
97
  rows: rows.map((row) => ({ market: row.market, twoYear: row.twoYear, tenYear: row.tenYear, thirtyYear: row.thirtyYear,
85
98
  twosTensBasisPoints: bp(row.twosTens), tenYearChangeBasisPoints: bp(row.tenYearChange), asOf: row.asOf })),
@@ -105,6 +118,9 @@ export function createYieldCurveHeadless(
105
118
  const spread = (id: string) => bp(spreads.find((entry) => entry.id === id)?.value ?? null);
106
119
  const twosTens = spread("2s10s");
107
120
  return {
121
+ // Each curve is its official publisher's daily close; a past date is a historical curve, not a feed.
122
+ freshness: { source: CURVE_PUBLISHERS[curve], status: "not-a-feed",
123
+ ...(requestedDate ? { basis: "historical curve" } : { basis: "daily curve", cadence: "daily" }) },
108
124
  ...(comparePoints ? { columns: COMPARE_COLUMNS } : {}),
109
125
  rows: rows.map((point) => {
110
126
  const compare = compareAt(point.maturity);
@@ -204,6 +204,18 @@ export function pluginsMissingHostExports(): string[] {
204
204
  /** Bun's wording when an import does not resolve, from a native import. */
205
205
  const UNRESOLVED_MODULE = /Cannot find (?:module|package) '([^']+)'/;
206
206
 
207
+ /**
208
+ * The specifier an import failed to resolve. Bun puts it on the error as
209
+ * `specifier`; the message is only a fallback, because from Bun 1.4 it names
210
+ * the package alone ("Cannot find package 'peer' imported from …" for
211
+ * `peer/bridge`), and the subpath decides whether the peer has the file.
212
+ */
213
+ export function unresolvedSpecifier(err: unknown, message: string): string | undefined {
214
+ const specifier = (err as { specifier?: unknown } | null)?.specifier;
215
+ if (typeof specifier === "string" && specifier) return specifier;
216
+ return UNRESOLVED_MODULE.exec(message)?.[1];
217
+ }
218
+
207
219
  /** Imported at another commit earlier in this process, with files besides the entry that Bun keeps. */
208
220
  function hasStaleModules(pluginDir: string, commit: string | null): boolean {
209
221
  if (!importedCommits.has(pluginDir) || importedCommits.get(pluginDir) === commit) return false;
@@ -300,7 +312,7 @@ export async function loadExternalPlugin(
300
312
  // A sibling plugin it imports that is linked now but was not when this
301
313
  // process first resolved it. Bun keeps that failure until a restart, which
302
314
  // is all the plugin needs: not a crash.
303
- const unresolved = UNRESOLVED_MODULE.exec(message)?.[1];
315
+ const unresolved = unresolvedSpecifier(err, message);
304
316
  if (unresolved && isLinkedPeerModule(pluginDir, unresolved)) {
305
317
  loaderLog.info(`${directory} loads after a restart: ${message}`);
306
318
  return { ...base, plugin: placeholder, needsRestart: true };
@@ -44,6 +44,7 @@ export function resolveRegistryPaneQuickSettings(
44
44
  return resolved.paneDef.quickSettings.flatMap((quickSetting): ResolvedRegistryPaneQuickSetting[] => {
45
45
  const field = resolved.settingsDef.fields.find((candidate) => candidate.key === quickSetting.key);
46
46
  if (!field) return [];
47
+ if (quickSetting.visible && !quickSetting.visible(resolved.context)) return [];
47
48
  if (quickSetting.onValue != null) {
48
49
  if (field.type !== "select") return [];
49
50
  const onValue = quickSetting.onValue;
@@ -5,6 +5,7 @@ import { createAppRuntime } from "../../core/app-runtime";
5
5
  import { createGloomberbCloudCapabilities, createGloomberbCloudProvider } from "../../sources/gloomberb-cloud";
6
6
  import { AssetDataRouter } from "../../sources/provider-router";
7
7
  import { JsonPersistence } from "../../data/json-persistence";
8
+ import { isBrokerAccountSnapshotKey } from "../../brokers/account-cache";
8
9
  import { JsonTickerRepository } from "../../data/json-ticker-repository";
9
10
 
10
11
  export function createBrowserAppServices({ config, plugins, externalPlugins }: AppServicesFactoryOptions) {
@@ -15,7 +16,8 @@ export function createBrowserAppServices({ config, plugins, externalPlugins }: A
15
16
  );
16
17
  return createAppRuntime({
17
18
  config, plugins, externalPlugins, dataProvider,
18
- persistence: new JsonPersistence(localStorage),
19
+ // Broker account snapshots are kept with the positions, so a reload keeps the account's equity and cash.
20
+ persistence: new JsonPersistence(localStorage, { keepResource: isBrokerAccountSnapshotKey }),
19
21
  tickerRepository: new JsonTickerRepository(localStorage),
20
22
  configure({ pluginRegistry, newsService }) {
21
23
  dataProvider.attachRegistry(pluginRegistry);
@@ -1,5 +1,6 @@
1
1
  import { readFile, writeFile } from "fs/promises";
2
- import { join, relative } from "path";
2
+ import { dirname, join, relative, resolve } from "path";
3
+ import type { BunPlugin } from "bun";
3
4
  import { TITLEBAR_OVERLAY_HEIGHT_PX } from "../../components/layout/titlebar-overlay";
4
5
 
5
6
  /** Imports whose path ends with the first entry resolve to the second, relative to the view directory. */
@@ -27,6 +28,13 @@ interface ViewBundleOptions {
27
28
  }
28
29
 
29
30
  const ELECTROBUN_VIEW_DIR = join(process.cwd(), "src", "renderers", "electrobun", "view");
31
+
32
+ /**
33
+ * Where the world map finds its land data, relative to the map's
34
+ * `basemap.ts`. Only view bundles define it, so the terminal binary, which
35
+ * never draws that map, leaves the data out.
36
+ */
37
+ export const WORLD_MAP_DATA_SPECIFIER = "./natural-earth/index.chunk.js";
30
38
  const DOM_RENDERER_DIR = join(process.cwd(), "src", "renderers", "dom");
31
39
 
32
40
  export function electrobunViewPath(...parts: string[]): string {
@@ -50,6 +58,7 @@ export async function buildViewBundle({
50
58
  failureMessage,
51
59
  missingEntryMessage,
52
60
  }: ViewBundleOptions): Promise<{ script: string; stylesheet: string | null }> {
61
+ const chunks = new Map<string, string>();
53
62
  const result = await Bun.build({
54
63
  entrypoints: [entrypoint],
55
64
  outdir,
@@ -58,8 +67,15 @@ export async function buildViewBundle({
58
67
  splitting: false,
59
68
  sourcemap,
60
69
  minify: true,
61
- define: { "process.env.NODE_ENV": "\"production\"", ...define },
62
- plugins: aliasRules.length > 0 ? [electrobunViewAliasPlugin(aliasRules)] : [],
70
+ define: {
71
+ "process.env.NODE_ENV": "\"production\"",
72
+ __GLOOM_WORLD_MAP_DATA__: JSON.stringify(WORLD_MAP_DATA_SPECIFIER),
73
+ ...define,
74
+ },
75
+ plugins: [
76
+ lazyChunkPlugin(outdir, outdir, chunks),
77
+ ...(aliasRules.length > 0 ? [electrobunViewAliasPlugin(aliasRules)] : []),
78
+ ],
63
79
  });
64
80
 
65
81
  if (!result.success) {
@@ -69,6 +85,7 @@ export async function buildViewBundle({
69
85
 
70
86
  const script = result.outputs.find((output) => output.kind === "entry-point" && output.path.endsWith(".js"));
71
87
  if (!script) throw new Error(missingEntryMessage);
88
+ await buildLazyChunks(outdir, chunks, failureMessage);
72
89
 
73
90
  const stylesheet = result.outputs.find((output) => output.path.endsWith(".css"))?.path ?? null;
74
91
  if (stylesheet) await writeFile(stylesheet, withTitlebarOverlayHeight(await readFile(stylesheet, "utf8")));
@@ -122,6 +139,67 @@ ${bootstrapScript}
122
139
  `;
123
140
  }
124
141
 
142
+ /**
143
+ * The view bundle is one file (no splitting), so a module reached through
144
+ * `import()` would land in it. A module named `*.chunk.ts` and imported as
145
+ * `import("./path/name.chunk.js")` instead ships as its own file at that path
146
+ * next to the importing bundle, fetched the first time it is asked for: large
147
+ * data only some panes draw, such as the world map's coastlines. The terminal
148
+ * and the tests run the source, where the same `import()` loads the module.
149
+ */
150
+ function lazyChunkPlugin(rootOutdir: string, emitDir: string, chunks: Map<string, string>): BunPlugin {
151
+ return {
152
+ name: "lazy-chunks",
153
+ setup(build) {
154
+ build.onResolve({ filter: /\.chunk(\.js|\.ts)?$/ }, (args) => {
155
+ if (args.kind.startsWith("entry-point") || /[\\/]node_modules[\\/]/.test(args.importer)) return undefined;
156
+ if (args.kind !== "dynamic-import") {
157
+ throw new Error(`${args.path} is a lazy chunk; load it with import() (from ${args.importer})`);
158
+ }
159
+ if (!args.path.startsWith("./") || !args.path.endsWith(".chunk.js")) {
160
+ throw new Error(`Lazy chunk ${args.path} must be imported as "./<path>.chunk.js" (from ${args.importer})`);
161
+ }
162
+ const source = resolve(dirname(args.importer), args.path.replace(/\.js$/, ".ts"));
163
+ const output = resolve(emitDir, args.path);
164
+ if (relative(rootOutdir, output).startsWith("..")) throw new Error(`Lazy chunk ${args.path} would land outside ${rootOutdir}`);
165
+ const existing = chunks.get(output);
166
+ if (existing && existing !== source) throw new Error(`Two lazy chunks emit ${output}: ${existing} and ${source}`);
167
+ chunks.set(output, source);
168
+ return { path: args.path, external: true };
169
+ });
170
+ },
171
+ };
172
+ }
173
+
174
+ /** Builds each lazy chunk on its own, and the chunks those load in turn. */
175
+ async function buildLazyChunks(rootOutdir: string, chunks: Map<string, string>, failureMessage: string): Promise<void> {
176
+ const built = new Set<string>();
177
+ let pending = [...chunks.entries()];
178
+ while (pending.length) {
179
+ const nested = new Map<string, string>();
180
+ for (const [output, source] of pending) {
181
+ if (built.has(output)) continue;
182
+ built.add(output);
183
+ const result = await Bun.build({
184
+ entrypoints: [source],
185
+ outdir: dirname(output),
186
+ naming: relative(dirname(output), output),
187
+ target: "browser",
188
+ format: "esm",
189
+ splitting: false,
190
+ sourcemap: "none",
191
+ minify: true,
192
+ plugins: [lazyChunkPlugin(rootOutdir, dirname(output), nested)],
193
+ });
194
+ if (!result.success) {
195
+ const details = result.logs.map((log) => log.message).filter(Boolean).join("\n");
196
+ throw new Error(`${failureMessage}: lazy chunk ${source}\n${details}`);
197
+ }
198
+ }
199
+ pending = [...nested.entries()];
200
+ }
201
+ }
202
+
125
203
  function electrobunViewAliasPlugin(aliasRules: AliasRule[]) {
126
204
  return {
127
205
  name: "electrobun-view-aliases",
@@ -143,6 +143,24 @@ export function useScrollBoxHandle(
143
143
  get scrollWidth() {
144
144
  return toCellX(elementRef.current?.scrollWidth ?? 0);
145
145
  },
146
+ // Whole rows are rounded, so the scroll keys read the ends in pixels.
147
+ get scrollTopPx() {
148
+ return options.headerOnly ? 0 : Math.max(0, elementRef.current?.scrollTop ?? 0);
149
+ },
150
+ get scrollHeightPx() {
151
+ const element = elementRef.current;
152
+ if (!element) return 0;
153
+ return options.headerOnly ? tableHeaderPx() : Math.max(0, element.scrollHeight - (options.viewportTopInsetPx ?? 0));
154
+ },
155
+ get viewportPx() {
156
+ const element = elementRef.current;
157
+ return {
158
+ width: Math.max(0, element?.clientWidth ?? 0),
159
+ height: options.headerOnly
160
+ ? tableHeaderPx()
161
+ : Math.max(0, (element?.clientHeight ?? 0) - (options.viewportTopInsetPx ?? 0)),
162
+ };
163
+ },
146
164
  get viewport() {
147
165
  const element = elementRef.current;
148
166
  if (options.headerOnly) {
@@ -161,6 +179,19 @@ export function useScrollBoxHandle(
161
179
  },
162
180
  horizontalScrollBar,
163
181
  verticalScrollBar,
182
+ scrollToPixels(target: number | { x?: number; y?: number }, y?: number) {
183
+ const element = elementRef.current;
184
+ if (!element || options.headerOnly) return;
185
+ if (typeof target === "number") {
186
+ element.scrollTop = Math.max(0, target);
187
+ if (typeof y === "number") element.scrollLeft = Math.max(0, y);
188
+ return;
189
+ }
190
+ element.scrollTo({
191
+ left: Math.max(0, target.x ?? element.scrollLeft),
192
+ top: Math.max(0, target.y ?? element.scrollTop),
193
+ });
194
+ },
164
195
  scrollTo(target: number | { x?: number; y?: number }, y?: number) {
165
196
  const element = elementRef.current;
166
197
  if (!element) return;
@@ -11,6 +11,7 @@ import {
11
11
  } from "react";
12
12
  import { EmptyState } from "../../../components/ui/status";
13
13
  import { usePaneInstance } from "../../../state/app/context";
14
+ import { useRegisterPaneScrollBox } from "../../../state/pane-scroll-registry";
14
15
  import { useRafCallback } from "../../../react/use-raf-callback";
15
16
  import { measurePerf } from "../../../utils/perf-marks";
16
17
  import type {
@@ -284,6 +285,10 @@ export function WebDataTable<T, C extends DataTableColumn = DataTableColumn>({
284
285
  bodyVertical.bar,
285
286
  { viewportTopInsetPx: headerPx },
286
287
  );
288
+ // The terminal table body is a ScrollBox the pane scroll keys already reach;
289
+ // this is the same for the desktop one. A table with a cursor takes the keys
290
+ // first, so only a table without one is scrolled by them.
291
+ useRegisterPaneScrollBox(scrollRef, { enabled: true });
287
292
 
288
293
  useEffect(() => {
289
294
  headerHorizontal.bar.visible = false;
@@ -353,6 +353,8 @@ function WebDataTableRowInner<
353
353
  <div
354
354
  key={column.id}
355
355
  data-gloom-role="data-table-cell"
356
+ // The instant behind a shortened time ("Wed 11:27"), so a rendered-view report can print it whole.
357
+ data-gloom-cell-instant={cell.value instanceof Date && Number.isFinite(cell.value.getTime()) ? cell.value.toISOString() : undefined}
356
358
  style={{
357
359
  minWidth: 0,
358
360
  position: freezeFirstColumn && columnIndex === 0 ? "sticky" : undefined,
@@ -345,8 +345,10 @@ export function WebTextField({
345
345
  focusedBackgroundColor={plain ? "transparent" : resolvedBackgroundColor}
346
346
  cursorColor={colors.textBright}
347
347
  style={{
348
- paddingLeft: plain ? 0 : comfortable ? 8 : 10,
349
- paddingRight: plain ? 0 : comfortable ? 8 : 10,
348
+ // A framed field keeps its text one cell off the frame; a plain one
349
+ // sits flush in the row or band that holds it.
350
+ paddingLeft: plain ? 0 : "var(--cell-w)",
351
+ paddingRight: plain ? 0 : "var(--cell-w)",
350
352
  borderRadius: plain ? 0 : CONTROL_RADIUS,
351
353
  // The field is taller than a cell, so the input takes the inner
352
354
  // height and centers its text on the box's own line.
@@ -581,8 +583,10 @@ export function WebDialogFrame({
581
583
  </Box>
582
584
  {children}
583
585
  {footer && (
586
+ // Sized by its line, not one cell: a cell-high box with this padding
587
+ // left the key line hanging out of its bottom.
584
588
  <Box
585
- height={1}
589
+ flexShrink={0}
586
590
  style={{
587
591
  borderTop: `1px solid ${panelBorder(colors)}`,
588
592
  paddingTop: 8,
@@ -5,6 +5,7 @@ import { TextAttributes } from "../../../ui";
5
5
  import type { ListRowState, ListViewItem, ListViewProps } from "../../../components/ui/list-view";
6
6
  import { blendHex, hoverBg, type ThemeColors } from "../../../theme/colors";
7
7
  import { useThemeColors } from "../../../theme/theme-context";
8
+ import { WEB_CELL_HEIGHT } from "../../../theme/font-scale";
8
9
  import { isInsideDialogSurface } from "../host/focus-scope";
9
10
  import { isEditableKeyboardTarget } from "../key-event";
10
11
  import {
@@ -58,6 +59,10 @@ function DefaultDesktopRow({
58
59
  );
59
60
  }
60
61
 
62
+ const FRAMED_LIST_PADDING_PX = 4;
63
+ /** What a framed list adds above and below its rows: padding and a 1px border on each side. */
64
+ const FRAMED_LIST_CHROME_PX = 2 * (FRAMED_LIST_PADDING_PX + 1);
65
+
61
66
  function listRowStyle(selected: boolean, disabled: boolean, colors: ThemeColors): CSSProperties {
62
67
  return {
63
68
  borderRadius: CONTROL_RADIUS,
@@ -109,6 +114,16 @@ export function WebListView({
109
114
  const tabStopIndex = selectedIndex >= 0 && items[selectedIndex] && !items[selectedIndex].disabled
110
115
  ? selectedIndex : items.findIndex((item) => !item.disabled);
111
116
  const effectiveSurface = surface ?? (scrollable ? "framed" : "plain");
117
+ // `height` counts terminal rows. A list that is not meant to scroll takes
118
+ // only the rows it has, up to that many; one with more rows than that
119
+ // scrolls inside the height instead of drawing over whatever follows it.
120
+ const rowsHeight = items.length === 0 ? 1 : items.length * rowHeight + (items.length - 1) * rowGap;
121
+ const overflowsHeight = !scrollable && height != null && rowsHeight > height;
122
+ const scrolls = scrollable || overflowsHeight;
123
+ // The frame's padding and border sit outside the rows it was asked to hold.
124
+ const scrollHeight = overflowsHeight
125
+ ? `${height * WEB_CELL_HEIGHT + (effectiveSurface === "plain" ? 0 : FRAMED_LIST_CHROME_PX)}px`
126
+ : height;
112
127
  const frameStyle = effectiveSurface === "plain"
113
128
  ? {
114
129
  border: "none",
@@ -119,12 +134,12 @@ export function WebListView({
119
134
  : {
120
135
  border: `1px solid ${panelBorder(colors)}`,
121
136
  borderRadius: CONTROL_RADIUS,
122
- padding: 4,
137
+ padding: FRAMED_LIST_PADDING_PX,
123
138
  backgroundColor: subtlePanelFill(colors),
124
139
  };
125
140
 
126
141
  useEffect(() => {
127
- if (!scrollable || !autoScrollToIndex || activeScrollIndex < 0) return;
142
+ if (!scrolls || !autoScrollToIndex || activeScrollIndex < 0) return;
128
143
  const scrollBox = scrollRef.current;
129
144
  if (!scrollBox) return;
130
145
  const safeIndex = Math.min(activeScrollIndex, items.length - 1);
@@ -136,16 +151,16 @@ export function WebListView({
136
151
  } else if (rowBottom > scrollBox.scrollTop + viewportHeight) {
137
152
  scrollBox.scrollTo(rowBottom - viewportHeight);
138
153
  }
139
- }, [activeScrollIndex, autoScrollToIndex, items.length, rowHeight, rowStride, scrollable]);
154
+ }, [activeScrollIndex, autoScrollToIndex, items.length, rowHeight, rowStride, scrolls]);
140
155
 
141
156
  useEffect(() => {
142
- if (!scrollable) return;
157
+ if (!scrolls) return;
143
158
  const scrollBox = scrollRef.current;
144
159
  if (!scrollBox) return;
145
160
  if (scrollBox.verticalScrollBar) {
146
161
  scrollBox.verticalScrollBar.visible = items.length * rowStride - rowGap > (scrollBox.viewport?.height ?? 0);
147
162
  }
148
- }, [items.length, height, flexGrow, rowGap, rowStride, scrollable]);
163
+ }, [items.length, height, flexGrow, rowGap, rowStride, scrolls]);
149
164
 
150
165
  // The dialog or pane around the list moves its cursor (j/k, Home/End). A row
151
166
  // Tab or a press left focused follows it, or Space and Enter would act on the
@@ -156,8 +171,8 @@ export function WebListView({
156
171
  const active = document.activeElement;
157
172
  if (!list || !active || !list.contains(active) || isEditableKeyboardTarget(active)) return;
158
173
  const row = list.querySelectorAll<HTMLElement>('[role="option"]')[selectedIndex];
159
- if (row && !row.contains(active)) row.focus({ preventScroll: scrollable });
160
- }, [listDomId, scrollable, selectedIndex]);
174
+ if (row && !row.contains(active)) row.focus({ preventScroll: scrolls });
175
+ }, [listDomId, scrolls, selectedIndex]);
161
176
 
162
177
  const rows = items.length === 0
163
178
  ? (
@@ -233,11 +248,21 @@ export function WebListView({
233
248
  });
234
249
 
235
250
  return (
236
- <Box id={listDomId} flexDirection="column" height={height} flexGrow={flexGrow} gap={1} role="listbox" aria-label={remoteLabel}>
237
- {scrollable ? (
251
+ <Box
252
+ id={listDomId}
253
+ flexDirection="column"
254
+ height={scrollable ? height : undefined}
255
+ // A list sized by its container gives up height to it, and scrolls.
256
+ minHeight={scrollable && height == null ? 0 : undefined}
257
+ flexGrow={flexGrow}
258
+ gap={1}
259
+ role="listbox"
260
+ aria-label={remoteLabel}
261
+ >
262
+ {scrolls ? (
238
263
  <ScrollBox
239
264
  ref={scrollRef}
240
- height={height}
265
+ height={scrollHeight}
241
266
  flexGrow={flexGrow}
242
267
  scrollY
243
268
  focusable={false}
@@ -2,12 +2,16 @@
2
2
 
3
3
  import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
4
4
  import { createPortal } from "react-dom";
5
- import type { HostPopoverProps } from "../../../ui";
5
+ import type { BoxRenderable, HostPopoverProps } from "../../../ui";
6
6
  import { blendHex } from "../../../theme/colors";
7
+ import { WEB_CELL_HEIGHT, WEB_CELL_WIDTH } from "../../../theme/font-scale";
7
8
  import { useThemeColors } from "../../../theme/theme-context";
8
9
 
9
10
  const VIEWPORT_MARGIN = 10;
11
+ /** How far a popover kept inside its pane stays off the pane's edge. */
12
+ const PANE_MARGIN = 4;
10
13
  const POPOVER_GAP = 6;
14
+ const PANE_SELECTOR = '[data-gloom-role="pane-window"], [data-gloom-role="detached-pane-window"]';
11
15
  /** `.gloom-popover` in styles.css: above dialogs and toasts. */
12
16
  const POPOVER_Z_INDEX = 10_001;
13
17
 
@@ -21,6 +25,51 @@ function cssSize(value: number | string | undefined): number | string | undefine
21
25
  return typeof value === "number" ? `${value}px` : value;
22
26
  }
23
27
 
28
+ interface Edges {
29
+ left: number;
30
+ top: number;
31
+ right: number;
32
+ bottom: number;
33
+ }
34
+
35
+ /**
36
+ * Where an anchor element is now, in viewport pixels. Null once it is gone
37
+ * from the page (a removed message), so the popover falls back to its trigger.
38
+ */
39
+ function anchorEdges(anchor: BoxRenderable): Edges | null {
40
+ const rect = anchor.getBoundingClientRect?.();
41
+ if (rect) {
42
+ if (rect.width === 0 && rect.height === 0) return null;
43
+ return { left: rect.x, top: rect.y, right: rect.x + rect.width, bottom: rect.y + rect.height };
44
+ }
45
+ const bounds = anchor.absoluteBounds;
46
+ if (!bounds) return null;
47
+ return {
48
+ left: bounds.x * WEB_CELL_WIDTH,
49
+ top: bounds.y * WEB_CELL_HEIGHT,
50
+ right: (bounds.x + bounds.width) * WEB_CELL_WIDTH,
51
+ bottom: (bounds.y + bounds.height) * WEB_CELL_HEIGHT,
52
+ };
53
+ }
54
+
55
+ /** The area a popover must stay in: the window, and for `pane` the pane that holds its trigger too. */
56
+ function placementEdges(wrapper: Element | null, boundary: HostPopoverProps["boundary"]): Edges {
57
+ const viewport = {
58
+ left: VIEWPORT_MARGIN,
59
+ top: VIEWPORT_MARGIN,
60
+ right: window.innerWidth - VIEWPORT_MARGIN,
61
+ bottom: window.innerHeight - VIEWPORT_MARGIN,
62
+ };
63
+ const pane = boundary === "pane" ? wrapper?.closest(PANE_SELECTOR)?.getBoundingClientRect() : null;
64
+ if (!pane) return viewport;
65
+ return {
66
+ left: Math.max(viewport.left, pane.left + PANE_MARGIN),
67
+ top: Math.max(viewport.top, pane.top + PANE_MARGIN),
68
+ right: Math.min(viewport.right, pane.right - PANE_MARGIN),
69
+ bottom: Math.min(viewport.bottom, pane.bottom - PANE_MARGIN),
70
+ };
71
+ }
72
+
24
73
  /**
25
74
  * The popover is portalled to <body>, so its z-index only competes in the root
26
75
  * stacking context. A trigger inside a surface stacked above the popover layer
@@ -45,6 +94,10 @@ export function WebPopover({
45
94
  trigger,
46
95
  children,
47
96
  anchorPoint,
97
+ anchor,
98
+ boundary = "viewport",
99
+ onPointerEnter,
100
+ onPointerLeave,
48
101
  placement = "bottom-start",
49
102
  minWidth = 280,
50
103
  maxWidth = "min(420px, calc(100vw - 20px))",
@@ -59,18 +112,19 @@ export function WebPopover({
59
112
  const [zIndex, setZIndex] = useState<number | undefined>(undefined);
60
113
 
61
114
  const updatePosition = useCallback(() => {
62
- const anchor = anchorRef.current;
115
+ const wrapper = anchorRef.current;
63
116
  const popover = popoverRef.current;
64
- if (!anchor || !popover) return;
117
+ if (!wrapper || !popover) return;
65
118
 
66
119
  const popoverRect = popover.getBoundingClientRect();
67
- const triggerRect = anchor.getBoundingClientRect();
120
+ const triggerRect = wrapper.getBoundingClientRect();
121
+ const elementEdges = anchor ? anchorEdges(anchor) : null;
68
122
  // Keyboard-driven popovers can intentionally omit a visible trigger. In
69
123
  // that case the anchor wrapper still inherits a line-height, so its DOM
70
124
  // rect is not truly empty even though there is nothing useful to anchor to.
71
125
  // Center these unanchored menus; explicit pointer/footer coordinates still
72
126
  // take precedence below.
73
- if (!anchorPoint && (trigger == null || (triggerRect.width === 0 && triggerRect.height === 0))) {
127
+ if (!elementEdges && !anchorPoint && (trigger == null || (triggerRect.width === 0 && triggerRect.height === 0))) {
74
128
  setPosition({
75
129
  left: Math.max(
76
130
  VIEWPORT_MARGIN,
@@ -90,28 +144,26 @@ export function WebPopover({
90
144
  });
91
145
  return;
92
146
  }
93
- const anchorRect = anchorPoint
147
+ const anchorRect = elementEdges ?? (anchorPoint
94
148
  ? {
95
149
  left: anchorPoint.x,
96
150
  right: anchorPoint.x,
97
151
  top: anchorPoint.y,
98
152
  bottom: anchorPoint.y,
99
153
  }
100
- : triggerRect;
154
+ : triggerRect);
155
+ const edges = placementEdges(wrapper, boundary);
101
156
  const preferredLeft = placement === "bottom-end"
102
157
  ? anchorRect.right - popoverRect.width
103
158
  : anchorRect.left;
104
- const left = Math.max(
105
- VIEWPORT_MARGIN,
106
- Math.min(preferredLeft, window.innerWidth - popoverRect.width - VIEWPORT_MARGIN),
107
- );
159
+ const left = Math.max(edges.left, Math.min(preferredLeft, edges.right - popoverRect.width));
108
160
  const below = anchorRect.bottom + POPOVER_GAP;
109
161
  const above = anchorRect.top - popoverRect.height - POPOVER_GAP;
110
- const top = below + popoverRect.height <= window.innerHeight - VIEWPORT_MARGIN
162
+ const top = below + popoverRect.height <= edges.bottom
111
163
  ? below
112
- : Math.max(VIEWPORT_MARGIN, above);
164
+ : Math.max(edges.top, above);
113
165
  setPosition({ left, top, visible: true });
114
- }, [anchorPoint, placement, trigger]);
166
+ }, [anchor, anchorPoint, boundary, placement, trigger]);
115
167
 
116
168
  useLayoutEffect(() => {
117
169
  if (!open) {
@@ -123,10 +175,15 @@ export function WebPopover({
123
175
  const frame = window.requestAnimationFrame(updatePosition);
124
176
  window.addEventListener("resize", updatePosition);
125
177
  document.addEventListener("scroll", updatePosition, true);
178
+ // New content (a hover card swapped to another name) can change the size
179
+ // the placement was worked out for.
180
+ const resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(updatePosition) : null;
181
+ if (popoverRef.current) resizeObserver?.observe(popoverRef.current);
126
182
  return () => {
127
183
  window.cancelAnimationFrame(frame);
128
184
  window.removeEventListener("resize", updatePosition);
129
185
  document.removeEventListener("scroll", updatePosition, true);
186
+ resizeObserver?.disconnect();
130
187
  };
131
188
  }, [open, updatePosition]);
132
189
 
@@ -181,6 +238,8 @@ export function WebPopover({
181
238
  tabIndex={-1}
182
239
  onMouseDown={(event) => event.stopPropagation()}
183
240
  onClick={(event) => event.stopPropagation()}
241
+ onMouseEnter={onPointerEnter}
242
+ onMouseLeave={onPointerLeave}
184
243
  style={{
185
244
  left: position.left,
186
245
  top: position.top,