gloomberb 0.16.1 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (574) 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 +24 -0
  14. package/src/cli/commands/command-utils.ts +8 -0
  15. package/src/cli/commands/crypto-hints.ts +75 -0
  16. package/src/cli/commands/market.ts +414 -106
  17. package/src/cli/commands/overview.ts +77 -32
  18. package/src/cli/commands/ticker.ts +101 -43
  19. package/src/cli/desktop-pane-shot.ts +34 -4
  20. package/src/cli/freshness.ts +61 -0
  21. package/src/cli/help.ts +1 -0
  22. package/src/cli/helpers.ts +4 -8
  23. package/src/cli/index.ts +12 -2
  24. package/src/cli/listing-arg.ts +321 -0
  25. package/src/cli/pane-functions/access-gate.ts +42 -0
  26. package/src/cli/pane-functions/catalog.ts +28 -1
  27. package/src/cli/pane-functions/data.ts +3 -1
  28. package/src/cli/pane-functions/dom.ts +20 -2
  29. package/src/cli/pane-functions/freshness.ts +400 -0
  30. package/src/cli/pane-functions/function-help.ts +27 -20
  31. package/src/cli/pane-functions/headless.ts +39 -11
  32. package/src/cli/pane-functions/index.ts +15 -4
  33. package/src/cli/pane-functions/options.ts +3 -0
  34. package/src/cli/pane-functions/report.ts +4 -0
  35. package/src/cli/pane-functions/resolver.ts +35 -7
  36. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  37. package/src/cli/pane-functions/screenshot.ts +7 -3
  38. package/src/cli/result.ts +41 -27
  39. package/src/cli/ticker-resolution.ts +7 -5
  40. package/src/components/chart/composite/composite-chart.tsx +88 -2536
  41. package/src/components/chart/composite/drawing-store.ts +59 -0
  42. package/src/components/chart/composite/legend.tsx +319 -0
  43. package/src/components/chart/composite/panel-bitmap.ts +139 -0
  44. package/src/components/chart/composite/panel-gestures.ts +59 -0
  45. package/src/components/chart/composite/panel-labels.ts +107 -0
  46. package/src/components/chart/composite/panel-surface.tsx +876 -0
  47. package/src/components/chart/composite/tool-catalog.ts +98 -0
  48. package/src/components/chart/composite/toolbar.tsx +186 -0
  49. package/src/components/chart/composite/tools.ts +8 -0
  50. package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
  51. package/src/components/chart/composite/use-composite-viewport.ts +143 -0
  52. package/src/components/command-bar/list/model.ts +5 -0
  53. package/src/components/command-bar/list/view.tsx +16 -3
  54. package/src/components/command-bar/pane-templates/workflow.ts +1 -0
  55. package/src/components/command-bar/routes/ticker-search/actions.ts +5 -0
  56. package/src/components/command-bar/workflow/ops.ts +1 -1
  57. package/src/components/command-bar/workflow/tickers.ts +6 -0
  58. package/src/components/data-table/view.tsx +29 -10
  59. package/src/components/feedback-dialog.tsx +2 -1
  60. package/src/components/form-modal/field-row.tsx +2 -2
  61. package/src/components/layout/detached-pane-shell.tsx +6 -3
  62. package/src/components/layout/pane/content.tsx +23 -10
  63. package/src/components/layout/pane/error-boundary.tsx +103 -0
  64. package/src/components/layout/pane/footer/registration.tsx +68 -23
  65. package/src/components/layout/pane/header.tsx +11 -11
  66. package/src/components/layout/shell/menu.ts +2 -1
  67. package/src/components/layout/shell/pane/actions.ts +2 -1
  68. package/src/components/layout/shell/pane/layers.tsx +14 -3
  69. package/src/components/metric-treemap/heat-surface.tsx +43 -16
  70. package/src/components/metric-treemap/heat-tiles.ts +18 -0
  71. package/src/components/metric-treemap/layout.ts +2 -0
  72. package/src/components/onboarding/onboarding-keyboard.ts +311 -0
  73. package/src/components/onboarding/onboarding-wizard.tsx +101 -788
  74. package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
  75. package/src/components/onboarding/use-onboarding-progress.ts +298 -0
  76. package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
  77. package/src/components/ticker/list-table-view.tsx +13 -1
  78. package/src/components/ui/fields.tsx +8 -0
  79. package/src/components/ui/range-track.tsx +10 -3
  80. package/src/components/ui/status.tsx +8 -2
  81. package/src/components/ui/tabs.tsx +7 -4
  82. package/src/components/ui/text-prompt-dialog.tsx +4 -2
  83. package/src/data/config/store/normalize.ts +17 -1
  84. package/src/i18n/es.ts +19 -1
  85. package/src/i18n/ja.ts +19 -1
  86. package/src/i18n/ko.ts +19 -1
  87. package/src/i18n/zh-cn.ts +19 -1
  88. package/src/i18n/zh-tw.ts +19 -1
  89. package/src/market-data/coordinator/chart.ts +1 -1
  90. package/src/market-data/coordinator/quotes.ts +23 -4
  91. package/src/market-data/fx-closes.ts +72 -0
  92. package/src/market-data/market/freshness.ts +78 -6
  93. package/src/market-data/market/session-close-note.ts +57 -0
  94. package/src/market-data/market/status.ts +151 -0
  95. package/src/market-data/published-apac-sessions.ts +66 -0
  96. package/src/market-data/quotes/contributions.ts +2 -0
  97. package/src/market-data/quotes/resolution.ts +2 -0
  98. package/src/market-data/quotes/screener-live-quotes.ts +49 -4
  99. package/src/plugins/builtin/account-management/pane.tsx +4 -0
  100. package/src/plugins/builtin/alerts/cli.ts +10 -5
  101. package/src/plugins/builtin/alerts/index.tsx +1 -0
  102. package/src/plugins/builtin/analytics/metrics.ts +8 -3
  103. package/src/plugins/builtin/analytics/risk-client.ts +191 -66
  104. package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
  105. package/src/plugins/builtin/analytics/risk-headless.ts +19 -13
  106. package/src/plugins/builtin/analytics/risk-model.ts +45 -17
  107. package/src/plugins/builtin/analytics/risk-pane.tsx +1 -0
  108. package/src/plugins/builtin/analytics/risk-test-data.ts +25 -1
  109. package/src/plugins/builtin/attention/headless.ts +6 -4
  110. package/src/plugins/builtin/attention/index.tsx +1 -1
  111. package/src/plugins/builtin/attention/pane.tsx +1 -1
  112. package/src/plugins/builtin/awards/headless.ts +1 -0
  113. package/src/plugins/builtin/awards/pane.tsx +1 -1
  114. package/src/plugins/builtin/backtest/headless.ts +1 -0
  115. package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
  116. package/src/plugins/builtin/buildout/index.tsx +1 -0
  117. package/src/plugins/builtin/catalysts/headless.ts +3 -1
  118. package/src/plugins/builtin/cds/headless.ts +2 -0
  119. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
  120. package/src/plugins/builtin/chart-composer/headless.ts +38 -3
  121. package/src/plugins/builtin/chart-composer/index.tsx +3 -1
  122. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
  123. package/src/plugins/builtin/chat/channels.ts +19 -3
  124. package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
  125. package/src/plugins/builtin/chat/content/footer.tsx +15 -6
  126. package/src/plugins/builtin/chat/content/index.tsx +120 -21
  127. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
  128. package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
  129. package/src/plugins/builtin/chat/direct-messages.ts +200 -0
  130. package/src/plugins/builtin/chat/index.tsx +1 -0
  131. package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
  132. package/src/plugins/builtin/chat/message/header.tsx +16 -6
  133. package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
  134. package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
  135. package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
  136. package/src/plugins/builtin/chat/message/types.ts +14 -2
  137. package/src/plugins/builtin/chat/profile-popover.ts +18 -13
  138. package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
  139. package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
  140. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  141. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  142. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  143. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  144. package/src/plugins/builtin/comment-letters/index.tsx +1 -0
  145. package/src/plugins/builtin/company-attention/headless.ts +5 -1
  146. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  147. package/src/plugins/builtin/company-kpis/headless.ts +1 -0
  148. package/src/plugins/builtin/congress-trades/headless.ts +1 -0
  149. package/src/plugins/builtin/correlation/compute.ts +59 -16
  150. package/src/plugins/builtin/correlation/geo.ts +34 -0
  151. package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
  152. package/src/plugins/builtin/correlation/headless.ts +42 -8
  153. package/src/plugins/builtin/correlation/history.ts +2 -0
  154. package/src/plugins/builtin/correlation/index.tsx +68 -14
  155. package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
  156. package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
  157. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  158. package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
  159. package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
  160. package/src/plugins/builtin/correlation/settings.ts +1 -1
  161. package/src/plugins/builtin/cot/headless.ts +6 -3
  162. package/src/plugins/builtin/cot/index.tsx +3 -3
  163. package/src/plugins/builtin/cot/model.ts +36 -3
  164. package/src/plugins/builtin/cpi/headless.ts +4 -0
  165. package/src/plugins/builtin/credit-boards/headless.ts +2 -0
  166. package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
  167. package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
  168. package/src/plugins/builtin/credit-documents/headless.ts +2 -0
  169. package/src/plugins/builtin/crypto-board/headless.ts +3 -0
  170. package/src/plugins/builtin/custom-view/loader.ts +1 -1
  171. package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
  172. package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
  173. package/src/plugins/builtin/doe/headless.ts +7 -0
  174. package/src/plugins/builtin/doe/range-bar.tsx +7 -37
  175. package/src/plugins/builtin/earnings/headless.ts +5 -0
  176. package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
  177. package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
  178. package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
  179. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
  180. package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
  181. package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
  182. package/src/plugins/builtin/econ/index.tsx +1 -0
  183. package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
  184. package/src/plugins/builtin/equity-screener/headless.ts +2 -0
  185. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  186. package/src/plugins/builtin/executives/index.tsx +1 -0
  187. package/src/plugins/builtin/exposure/headless.ts +3 -1
  188. package/src/plugins/builtin/fear-greed/headless.ts +2 -0
  189. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  190. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  191. package/src/plugins/builtin/futures-curve/headless.ts +39 -10
  192. package/src/plugins/builtin/futures-curve/index.tsx +3 -3
  193. package/src/plugins/builtin/futures-curve/model.ts +188 -4
  194. package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
  195. package/src/plugins/builtin/gpu/headless.ts +7 -3
  196. package/src/plugins/builtin/gpu/ladder.tsx +26 -30
  197. package/src/plugins/builtin/gpu/model.ts +27 -1
  198. package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
  199. package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
  200. package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
  201. package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
  202. package/src/plugins/builtin/holders/headless.ts +139 -5
  203. package/src/plugins/builtin/holders/index.tsx +10 -3
  204. package/src/plugins/builtin/holders/pane.tsx +20 -3
  205. package/src/plugins/builtin/holders/report-header.ts +95 -0
  206. package/src/plugins/builtin/holders/table-model.ts +20 -5
  207. package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
  208. package/src/plugins/builtin/holders/types.ts +2 -1
  209. package/src/plugins/builtin/insider/headless.ts +2 -0
  210. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
  211. package/src/plugins/builtin/iv-history/headless.ts +4 -1
  212. package/src/plugins/builtin/jobs/headless.ts +1 -0
  213. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
  214. package/src/plugins/builtin/macro-day/headless.ts +2 -0
  215. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  216. package/src/plugins/builtin/market-heatmap/data.ts +2 -0
  217. package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
  218. package/src/plugins/builtin/market-heatmap/index.tsx +39 -46
  219. package/src/plugins/builtin/market-heatmap/model.ts +146 -0
  220. package/src/plugins/builtin/market-heatmap/portfolio.ts +23 -4
  221. package/src/plugins/builtin/market-movers/headless.ts +2 -0
  222. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  223. package/src/plugins/builtin/members/client.ts +20 -3
  224. package/src/plugins/builtin/members/headless.ts +5 -3
  225. package/src/plugins/builtin/members/model.ts +10 -2
  226. package/src/plugins/builtin/members/pane.tsx +13 -6
  227. package/src/plugins/builtin/mna/distress/headless.ts +1 -0
  228. package/src/plugins/builtin/mna/headless.ts +1 -0
  229. package/src/plugins/builtin/money-markets/headless.ts +1 -0
  230. package/src/plugins/builtin/news/headless.ts +2 -0
  231. package/src/plugins/builtin/news/wire/index.tsx +8 -1
  232. package/src/plugins/builtin/notes/cli.ts +14 -6
  233. package/src/plugins/builtin/notes/index.tsx +1 -0
  234. package/src/plugins/builtin/openfda/index.tsx +1 -0
  235. package/src/plugins/builtin/options-calculator/headless.ts +2 -0
  236. package/src/plugins/builtin/options-positioning/headless.ts +3 -0
  237. package/src/plugins/builtin/options-scenario/headless.ts +5 -0
  238. package/src/plugins/builtin/pe-band/client.ts +48 -3
  239. package/src/plugins/builtin/pe-band/headless.ts +25 -7
  240. package/src/plugins/builtin/pe-band/model.ts +92 -16
  241. package/src/plugins/builtin/pe-band/pane.tsx +33 -16
  242. package/src/plugins/builtin/perps/client.ts +50 -3
  243. package/src/plugins/builtin/perps/headless.ts +144 -16
  244. package/src/plugins/builtin/perps/index.tsx +6 -6
  245. package/src/plugins/builtin/perps/model.ts +189 -12
  246. package/src/plugins/builtin/perps/pane.tsx +248 -47
  247. package/src/plugins/builtin/portfolio-list/allocation.ts +245 -0
  248. package/src/plugins/builtin/portfolio-list/cached-account.ts +33 -0
  249. package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
  250. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
  251. package/src/plugins/builtin/portfolio-list/cli/render.ts +230 -39
  252. package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
  253. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
  254. package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
  255. package/src/plugins/builtin/portfolio-list/headless.ts +98 -16
  256. package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
  257. package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
  258. package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
  259. package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
  260. package/src/plugins/builtin/portfolio-list/pane/index.tsx +47 -13
  261. package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
  262. package/src/plugins/builtin/portfolio-list/summary/index.tsx +24 -1
  263. package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
  264. package/src/plugins/builtin/power/headless.ts +3 -1
  265. package/src/plugins/builtin/rate-path/headless.ts +2 -0
  266. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  267. package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
  268. package/src/plugins/builtin/research/analyst-headless.ts +3 -0
  269. package/src/plugins/builtin/research/events-headless.ts +2 -0
  270. package/src/plugins/builtin/research/headless.ts +2 -0
  271. package/src/plugins/builtin/research/index.tsx +1 -0
  272. package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
  273. package/src/plugins/builtin/research/relative-valuation-model.ts +11 -3
  274. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  275. package/src/plugins/builtin/research-search/index.tsx +1 -0
  276. package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
  277. package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
  278. package/src/plugins/builtin/risk-factors/headless.ts +2 -0
  279. package/src/plugins/builtin/scanner/flow-history.ts +2 -13
  280. package/src/plugins/builtin/scanner/headless.ts +14 -0
  281. package/src/plugins/builtin/seasonality/headless.ts +2 -0
  282. package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
  283. package/src/plugins/builtin/sec/filing-content.ts +42 -17
  284. package/src/plugins/builtin/sec/forms.ts +12 -0
  285. package/src/plugins/builtin/sec/headless.ts +2 -0
  286. package/src/plugins/builtin/sectors/client.ts +1 -0
  287. package/src/plugins/builtin/sectors/headless.ts +9 -1
  288. package/src/plugins/builtin/sectors/sector-model.ts +2 -0
  289. package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
  290. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  291. package/src/plugins/builtin/shared/report-freshness.ts +57 -0
  292. package/src/plugins/builtin/shared/research-cells.tsx +4 -9
  293. package/src/plugins/builtin/short-interest/headless.ts +2 -0
  294. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  295. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  296. package/src/plugins/builtin/social-mentions/headless.ts +1 -0
  297. package/src/plugins/builtin/supply-chain/headless.ts +7 -1
  298. package/src/plugins/builtin/supply-chain/index.tsx +10 -1
  299. package/src/plugins/builtin/supply-chain/model.ts +27 -2
  300. package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
  301. package/src/plugins/builtin/themes/headless.ts +3 -0
  302. package/src/plugins/builtin/thirteenf/headless.ts +110 -25
  303. package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
  304. package/src/plugins/builtin/ticker-detail/headless.ts +7 -1
  305. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  306. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -2
  307. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +26 -26
  308. package/src/plugins/builtin/time-sales/headless.ts +5 -1
  309. package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
  310. package/src/plugins/builtin/volatility/headless.ts +6 -1
  311. package/src/plugins/builtin/world-indices/headless.ts +2 -0
  312. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +157 -0
  313. package/src/plugins/builtin/world-venue-map/basemap.ts +214 -0
  314. package/src/plugins/builtin/world-venue-map/client.ts +45 -0
  315. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
  316. package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
  317. package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
  318. package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
  319. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
  320. package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
  321. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +447 -0
  322. package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
  323. package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
  324. package/src/plugins/builtin/world-venue-map/index.ts +67 -4
  325. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +552 -0
  326. package/src/plugins/builtin/world-venue-map/layers.ts +348 -0
  327. package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
  328. package/src/plugins/builtin/world-venue-map/map.tsx +381 -45
  329. package/src/plugins/builtin/world-venue-map/model.ts +7 -4
  330. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +199 -0
  331. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
  332. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
  333. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
  334. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
  335. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
  336. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
  337. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
  338. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
  339. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
  340. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
  341. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
  342. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
  343. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
  344. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
  345. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
  346. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
  347. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
  348. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
  349. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
  350. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
  351. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
  352. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
  353. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
  354. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
  355. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
  356. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
  357. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
  358. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
  359. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
  360. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
  361. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
  362. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
  363. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
  364. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
  365. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
  366. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
  367. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
  368. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
  369. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
  370. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
  371. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
  372. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
  373. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
  374. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
  375. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
  376. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
  377. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
  378. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
  379. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
  380. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
  381. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
  382. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
  383. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
  384. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
  385. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
  386. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
  387. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
  388. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
  389. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
  390. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
  391. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
  392. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
  393. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
  394. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
  395. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
  396. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
  397. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
  398. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
  399. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
  400. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
  401. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
  402. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
  403. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
  404. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
  405. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
  406. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
  407. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
  408. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
  409. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
  410. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
  411. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
  412. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
  413. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
  414. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
  415. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
  416. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
  417. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
  418. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
  419. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
  420. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
  421. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
  422. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
  423. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
  424. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
  425. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
  426. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
  429. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
  430. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
  431. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
  432. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
  433. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
  434. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
  435. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
  436. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
  437. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
  438. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
  439. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
  440. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
  441. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
  442. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
  443. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
  444. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
  445. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
  446. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
  447. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
  448. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
  449. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
  450. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
  451. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
  452. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
  453. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
  454. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
  455. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
  456. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
  457. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
  458. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
  459. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
  460. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
  461. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
  462. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
  463. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
  464. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
  465. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
  466. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
  467. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
  468. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
  469. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
  470. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
  471. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
  472. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
  473. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
  474. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
  475. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
  476. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
  477. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
  478. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
  479. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
  480. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
  481. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
  482. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
  483. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
  484. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
  485. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
  486. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
  487. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
  488. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
  489. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
  490. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
  491. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
  492. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
  493. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
  494. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
  495. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
  496. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
  497. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
  498. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
  499. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
  500. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
  501. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
  502. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
  503. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
  504. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
  505. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
  506. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
  507. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
  508. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
  509. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
  510. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
  511. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
  512. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
  513. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
  514. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
  515. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
  516. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
  517. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
  518. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
  519. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
  520. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
  521. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
  522. package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
  523. package/src/plugins/builtin/world-venue-map/pane.tsx +28 -43
  524. package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
  525. package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
  526. package/src/plugins/builtin/yield-curve/headless.ts +16 -0
  527. package/src/plugins/loader.ts +13 -1
  528. package/src/plugins/registry/pane-settings.ts +1 -0
  529. package/src/renderers/dom/build-assets.ts +81 -3
  530. package/src/renderers/dom/data-table/row.tsx +2 -0
  531. package/src/renderers/dom/desktop/controls.tsx +7 -3
  532. package/src/renderers/dom/desktop/list-view.tsx +35 -10
  533. package/src/renderers/dom/desktop/popover.tsx +73 -14
  534. package/src/renderers/dom/host/input.tsx +14 -3
  535. package/src/renderers/dom/styles.css +6 -7
  536. package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
  537. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -0
  538. package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
  539. package/src/renderers/opentui/host.tsx +5 -0
  540. package/src/sources/gloomberb-cloud/index.ts +25 -4
  541. package/src/sources/gloomberb-cloud/normalizers.ts +27 -3
  542. package/src/sources/provider-router/cached-routes.ts +4 -1
  543. package/src/sources/provider-router/financials.ts +8 -7
  544. package/src/sources/provider-router/history.ts +15 -10
  545. package/src/sources/sec-registrant.ts +75 -0
  546. package/src/state/app/context/index.tsx +24 -8
  547. package/src/state/hooks/quote-streaming.ts +41 -26
  548. package/src/sync/core-contributors.ts +4 -0
  549. package/src/telemetry/crash-reports.ts +4 -1
  550. package/src/test-support/cli-context.ts +10 -2
  551. package/src/theme/heat-colors.ts +266 -84
  552. package/src/time-series/resolve.ts +1 -1
  553. package/src/time-series/use-chart-resolution.ts +1 -1
  554. package/src/types/context-menu.ts +6 -0
  555. package/src/types/data-provider.ts +2 -0
  556. package/src/types/financials.ts +3 -0
  557. package/src/types/headless.ts +79 -2
  558. package/src/types/plugin.ts +20 -1
  559. package/src/types/ticker.ts +15 -0
  560. package/src/ui/host.tsx +14 -0
  561. package/src/updater/index.ts +9 -11
  562. package/src/utils/async-deadline.ts +21 -0
  563. package/src/utils/cli-output.ts +5 -1
  564. package/src/utils/crypto-pair.ts +41 -0
  565. package/src/utils/currency-units.ts +14 -0
  566. package/src/utils/depositary-receipt.ts +26 -0
  567. package/src/utils/exchanges.ts +72 -0
  568. package/src/utils/fire-and-forget.ts +24 -0
  569. package/src/utils/format.ts +13 -0
  570. package/src/utils/price-earnings.ts +70 -3
  571. package/src/utils/price-history.ts +58 -13
  572. package/src/utils/rpc-timeout-error.ts +27 -0
  573. package/src/utils/sec.ts +13 -1
  574. package/src/utils/utc-time.ts +79 -0
@@ -0,0 +1,200 @@
1
+ import type { ChatChannel, ChatUserSummary } from "../../../api-client";
2
+ import { ApiRequestError } from "../../../api-client/errors";
3
+ import { t, tf } from "../../../i18n";
4
+
5
+ /**
6
+ * The server's code for "this user only takes DMs from people they have
7
+ * written to first". Any other refusal, a block among them, stays the neutral
8
+ * generic line so the reason never reads as "they blocked you".
9
+ */
10
+ const DMS_CLOSED_CODE = "dms_closed";
11
+
12
+ /** Why a DM cannot be started: the user's own setting, or an account nobody can DM (a bot, a bridged Discord user). */
13
+ type DirectMessageRefusal = "dms-off" | "no-dms";
14
+
15
+ export type DirectMessageAvailability =
16
+ | { kind: "self" }
17
+ /** A DM with this user exists; opening it needs no new conversation. */
18
+ | { kind: "open"; channelId: string }
19
+ | { kind: "start" }
20
+ | { kind: "refused"; reason: DirectMessageRefusal };
21
+
22
+ function normalizedUsername(user: Pick<ChatUserSummary, "username">): string | null {
23
+ return user.username?.trim().replace(/^@+/, "").toLowerCase() || null;
24
+ }
25
+
26
+ export function findDirectChannelWith(channels: readonly ChatChannel[], user: Pick<ChatUserSummary, "id" | "username">): ChatChannel | null {
27
+ const username = normalizedUsername(user);
28
+ return channels.find((channel) => {
29
+ if (channel.kind !== "direct" || !channel.dmUser) return false;
30
+ if (channel.dmUser.id === user.id) return true;
31
+ return !!username && normalizedUsername(channel.dmUser) === username;
32
+ }) ?? null;
33
+ }
34
+
35
+ function refusalOf(user: ChatUserSummary): DirectMessageRefusal | null {
36
+ if (user.accountType === "managed_llm" || user.accountType === "discord") return "no-dms";
37
+ if (user.acceptUnknownDms === false) return "dms-off";
38
+ return null;
39
+ }
40
+
41
+ /**
42
+ * What "Message @name" does for this user: open the DM you already share,
43
+ * start one, or nothing. The server only lets you start a DM with someone
44
+ * whose settings take DMs from people they have not talked to; one that
45
+ * already exists stays open to both sides. A summary from an older server
46
+ * that leaves the setting out is assumed to take DMs.
47
+ */
48
+ export function directMessageAvailability(
49
+ user: ChatUserSummary,
50
+ { currentUserId, channels }: { currentUserId: string | null | undefined; channels: readonly ChatChannel[] },
51
+ ): DirectMessageAvailability {
52
+ if (currentUserId && user.id === currentUserId) return { kind: "self" };
53
+ const existing = findDirectChannelWith(channels, user);
54
+ if (existing) return { kind: "open", channelId: existing.id };
55
+ const reason = refusalOf(user);
56
+ return reason ? { kind: "refused", reason } : { kind: "start" };
57
+ }
58
+
59
+ export interface DirectMessageAction {
60
+ /**
61
+ * Opens the DM you already share, starts one, or cannot: their settings or
62
+ * the account refuse a new DM. A menu shows a refused one disabled, the
63
+ * card of someone with no public profile says why, and a profile card or
64
+ * the pane menu leaves it out.
65
+ */
66
+ kind: "open" | "start" | "refused";
67
+ username: string;
68
+ /** For a menu: "Message @name", or "Open DM with @name" when one exists. */
69
+ menuLabel: string;
70
+ /** For a button beside the name: "Message" or "Open DM". */
71
+ buttonLabel: string;
72
+ /**
73
+ * A refused one: why, for a line where the button would be. It leaves the
74
+ * name out; the card it sits on already shows it.
75
+ */
76
+ refusal?: string;
77
+ }
78
+
79
+ /**
80
+ * The "Message" action on a user's name, card or message. Null for yourself
81
+ * and before you can send. Refused for a bot, a bridged Discord user, and
82
+ * someone who takes no DMs from people they have not talked to, unless you
83
+ * already share one.
84
+ */
85
+ export function directMessageAction(
86
+ user: ChatUserSummary,
87
+ context: { currentUserId: string | null | undefined; channels: readonly ChatChannel[]; canSend: boolean },
88
+ ): DirectMessageAction | null {
89
+ const username = normalizedUsername(user);
90
+ if (!context.canSend || !username) return null;
91
+ const availability = directMessageAvailability(user, context);
92
+ if (availability.kind === "open") {
93
+ return {
94
+ kind: "open",
95
+ username,
96
+ menuLabel: tf("Open DM with @{username}", { username }),
97
+ buttonLabel: t("Open DM"),
98
+ };
99
+ }
100
+ if (availability.kind === "self") return null;
101
+ return {
102
+ kind: availability.kind,
103
+ username,
104
+ menuLabel: tf("Message @{username}", { username }),
105
+ buttonLabel: t("Message"),
106
+ ...(availability.kind === "refused" ? {
107
+ refusal: availability.reason === "dms-off"
108
+ ? t("Only takes DMs from people they have written to first.")
109
+ : t("Does not take DMs."),
110
+ } : {}),
111
+ };
112
+ }
113
+
114
+ function directMessageRefusalText(username: string, reason: DirectMessageRefusal): string {
115
+ return reason === "dms-off"
116
+ ? tf("@{username} only takes DMs from people they have written to first.", { username })
117
+ : tf("@{username} does not take DMs.", { username });
118
+ }
119
+
120
+ /**
121
+ * The first named user a group chat cannot include. Unlike a direct message,
122
+ * a DM already shared with them does not help: every member of a new group
123
+ * has to take DMs from people they have not talked to.
124
+ */
125
+ function groupChatRefusal(
126
+ usernames: readonly string[],
127
+ userByUsername: ReadonlyMap<string, ChatUserSummary>,
128
+ ): { username: string; reason: DirectMessageRefusal } | null {
129
+ for (const username of usernames) {
130
+ const user = userByUsername.get(username);
131
+ const reason = user ? refusalOf(user) : null;
132
+ if (reason) return { username, reason };
133
+ }
134
+ return null;
135
+ }
136
+
137
+ /**
138
+ * What the app knows, before asking the server, about why these names cannot
139
+ * start a conversation. Null when it knows of no reason (an unknown name, an
140
+ * existing DM, or users who take DMs).
141
+ */
142
+ export function knownConversationRefusal(
143
+ usernames: readonly string[],
144
+ {
145
+ userByUsername,
146
+ currentUserId,
147
+ channels,
148
+ }: {
149
+ userByUsername: ReadonlyMap<string, ChatUserSummary>;
150
+ currentUserId: string | null | undefined;
151
+ channels: readonly ChatChannel[];
152
+ },
153
+ ): string | null {
154
+ if (usernames.length === 1) {
155
+ const user = userByUsername.get(usernames[0]!);
156
+ if (!user) return null;
157
+ const availability = directMessageAvailability(user, { currentUserId, channels });
158
+ return availability.kind === "refused" ? directMessageRefusalText(usernames[0]!, availability.reason) : null;
159
+ }
160
+ const refusal = groupChatRefusal(usernames, userByUsername);
161
+ return refusal ? directMessageRefusalText(refusal.username, refusal.reason) : null;
162
+ }
163
+
164
+ function joinUsernames(usernames: readonly string[]): string {
165
+ return usernames.map((username) => `@${username}`).join(", ");
166
+ }
167
+
168
+ /**
169
+ * The line shown when opening a DM or group chat failed. A name the server
170
+ * does not know is named; a user whose settings refuse DMs from new people is
171
+ * named with that reason; every other refusal, a block included, stays
172
+ * neutral and only carries the status.
173
+ */
174
+ export function describeConversationStartError(
175
+ error: unknown,
176
+ usernames: readonly string[],
177
+ userByUsername: ReadonlyMap<string, ChatUserSummary>,
178
+ ): string {
179
+ const status = error instanceof ApiRequestError ? error.status : undefined;
180
+ if (status === 404) {
181
+ // The server does not say which member of a group it could not find; the
182
+ // names this app has never seen are the candidates.
183
+ const missing = usernames.length === 1 ? usernames : usernames.filter((username) => !userByUsername.has(username));
184
+ if (missing.length === 1) return tf("@{username} was not found.", { username: missing[0]! });
185
+ if (missing.length > 1) return tf("{usernames} were not found.", { usernames: joinUsernames(missing) });
186
+ return t("One of those users was not found.");
187
+ }
188
+ if (status === 403 && error instanceof ApiRequestError && error.code === DMS_CLOSED_CODE) {
189
+ const named = typeof error.details?.username === "string" ? error.details.username.toLowerCase() : null;
190
+ const username = named
191
+ ?? (usernames.length === 1 ? usernames[0] : groupChatRefusal(usernames, userByUsername)?.username)
192
+ ?? null;
193
+ return username
194
+ ? directMessageRefusalText(username, "dms-off")
195
+ : t("Someone in that group only takes DMs from people they have written to first.");
196
+ }
197
+ return status
198
+ ? tf("Could not start the conversation (error {status}).", { status })
199
+ : t("Could not start the conversation. Check your connection and try again.");
200
+ }
@@ -31,6 +31,7 @@ export const chatModule: PluginModule = {
31
31
  },
32
32
  }, {
33
33
  id: UNREAD_INBOX_PANE_ID,
34
+ reportFreshness: { status: "not-a-feed", basis: "chat messages" },
34
35
  name: "Unread",
35
36
  icon: "U",
36
37
  component: UnreadInboxPane,
@@ -28,6 +28,7 @@ export const DesktopChatMessage = memo(function DesktopChatMessage({
28
28
  onUserHover,
29
29
  onUserHoverEnd,
30
30
  onUserActivate,
31
+ onUserContextMenu,
31
32
  beginReplyTo,
32
33
  beginEditMessage,
33
34
  jumpToMessage,
@@ -113,6 +114,7 @@ export const DesktopChatMessage = memo(function DesktopChatMessage({
113
114
  onUserHover={onUserHover}
114
115
  onUserHoverEnd={onUserHoverEnd}
115
116
  onUserActivate={onUserActivate}
117
+ onUserContextMenu={onUserContextMenu}
116
118
  {...actionProps}
117
119
  />
118
120
  )}
@@ -134,6 +136,7 @@ export const DesktopChatMessage = memo(function DesktopChatMessage({
134
136
  onUserHover={onUserHover}
135
137
  onUserHoverEnd={onUserHoverEnd}
136
138
  onUserActivate={onUserActivate}
139
+ onUserContextMenu={onUserContextMenu}
137
140
  />
138
141
  </Box>
139
142
  {state.grouped && <ChatMessageActions floating {...actionProps} />}
@@ -1,4 +1,5 @@
1
- import { Box, Text } from "../../../../ui";
1
+ import { useRef } from "react";
2
+ import { Box, Text, useUiCapabilities, type BoxRenderable } from "../../../../ui";
2
3
  import { t } from "../../../../i18n";
3
4
  import { Button } from "../../../../components/ui";
4
5
  import { MESSAGE_ACTION_WIDTH } from "../layout";
@@ -73,27 +74,36 @@ export function ChatMessageHeader({
73
74
  onUserHover,
74
75
  onUserHoverEnd,
75
76
  onUserActivate,
77
+ onUserContextMenu,
76
78
  ...actionProps
77
- }: ChatMessageActionProps & Pick<ChatMessageBaseProps, "msg" | "onUserHover" | "onUserHoverEnd" | "onUserActivate"> & {
79
+ }: ChatMessageActionProps & Pick<ChatMessageBaseProps, "msg" | "onUserHover" | "onUserHoverEnd" | "onUserActivate" | "onUserContextMenu"> & {
78
80
  /** Host row props: width, background and hover or selection hooks. */
79
81
  rowProps: Record<string, unknown>;
80
82
  /** The terminal sizes the author cell to its label. */
81
83
  fitAuthorWidth?: boolean;
82
84
  }) {
83
85
  const authorLabel = msg.user.username ?? "anon";
86
+ const { nativeContextMenu } = useUiCapabilities();
87
+ // The card opens beside the name the pointer is on.
88
+ const nameRef = useRef<BoxRenderable | null>(null);
84
89
  return (
85
90
  <Box {...rowProps} flexDirection="row" height={1} paddingLeft={1}>
86
91
  <Box
92
+ ref={nameRef}
87
93
  width={fitAuthorWidth ? authorLabel.length : undefined}
88
94
  height={1}
89
- onMouseOver={() => onUserHover(msg.user)}
90
- onMouseMove={() => onUserHover(msg.user)}
95
+ onMouseOver={() => onUserHover(msg.user, nameRef.current)}
96
+ onMouseMove={() => onUserHover(msg.user, nameRef.current)}
91
97
  onMouseOut={onUserHoverEnd}
92
- onMouseDown={(event: { preventDefault?: () => void; stopPropagation?: () => void }) => {
98
+ data-gloom-context-menu-surface="true"
99
+ onMouseDown={(event: { button?: number; preventDefault?: () => void; stopPropagation?: () => void }) => {
93
100
  event.preventDefault?.();
94
101
  event.stopPropagation?.();
95
- onUserActivate?.(msg.user);
102
+ // A native menu opens on the right-click's contextmenu event instead.
103
+ if (event.button === 2 && nativeContextMenu === true) return;
104
+ onUserActivate?.(msg.user, nameRef.current);
96
105
  }}
106
+ onContextMenu={(event: { preventDefault?: () => void; stopPropagation?: () => void }) => onUserContextMenu?.(msg.user, event, nameRef.current)}
97
107
  style={{ cursor: "pointer" }}
98
108
  >
99
109
  <Text fg={state.authorColor} attributes={state.authorAttributes}>
@@ -1,5 +1,5 @@
1
- import { useMemo, useState } from "react";
2
- import { Box, Text } from "../../../../ui";
1
+ import { useMemo, useRef, useState } from "react";
2
+ import { Box, Text, useUiCapabilities, type BoxRenderable } from "../../../../ui";
3
3
  import { TextAttributes } from "../../../../ui";
4
4
  import { ExternalLinkText } from "../../../../components/ui";
5
5
  import { InlineTickerBadge } from "../../../../components/ticker/badge";
@@ -8,6 +8,59 @@ import { blendHex, colors } from "../../../../theme/colors";
8
8
  import type { ChatUserSummary } from "../../../../api-client";
9
9
  import { tokenizeInlineContent, type InlineContentToken } from "../../../../utils/inline-content-tokenizer";
10
10
  import { chatBadgeTextWidth } from "../layout";
11
+ import type { ChatUserAnchor, ChatUserContextMenuEvent } from "./types";
12
+
13
+ /** An @mention: a name that opens its user's card, when the chat knows the user. */
14
+ function UserMentionToken({
15
+ value,
16
+ user,
17
+ nativeContextMenu,
18
+ onUserHover,
19
+ onUserHoverEnd,
20
+ onUserActivate,
21
+ onUserContextMenu,
22
+ }: {
23
+ value: string;
24
+ user: ChatUserSummary | null;
25
+ nativeContextMenu: boolean;
26
+ onUserHover?: (user: ChatUserSummary, anchor?: ChatUserAnchor) => void;
27
+ onUserHoverEnd?: () => void;
28
+ onUserActivate?: (user: ChatUserSummary, anchor?: ChatUserAnchor) => void;
29
+ onUserContextMenu?: (user: ChatUserSummary, event: ChatUserContextMenuEvent, anchor?: ChatUserAnchor) => void;
30
+ }) {
31
+ // The card opens beside the mention the pointer is on.
32
+ const tokenRef = useRef<BoxRenderable | null>(null);
33
+ return (
34
+ <Box
35
+ ref={tokenRef}
36
+ height={1}
37
+ flexDirection="row"
38
+ backgroundColor={blendHex(colors.panel, colors.positive, 0.24)}
39
+ onMouseOver={() => {
40
+ if (user) onUserHover?.(user, tokenRef.current);
41
+ }}
42
+ onMouseOut={() => {
43
+ if (user) onUserHoverEnd?.();
44
+ }}
45
+ // Only a mention of someone the chat has a summary for has a card or a menu to open.
46
+ onMouseDown={user && onUserActivate ? (event: { button?: number; preventDefault?: () => void; stopPropagation?: () => void }) => {
47
+ event.preventDefault?.();
48
+ event.stopPropagation?.();
49
+ if (event.button === 2 && nativeContextMenu) return;
50
+ onUserActivate(user, tokenRef.current);
51
+ } : undefined}
52
+ {...(user && onUserContextMenu ? {
53
+ "data-gloom-context-menu-surface": "true",
54
+ onContextMenu: (event: ChatUserContextMenuEvent) => onUserContextMenu(user, event, tokenRef.current),
55
+ } : {})}
56
+ style={user && onUserActivate ? { cursor: "pointer" } : undefined}
57
+ >
58
+ <Text fg={colors.positive} attributes={TextAttributes.BOLD}>
59
+ {value}
60
+ </Text>
61
+ </Box>
62
+ );
63
+ }
11
64
 
12
65
  export function ResponsiveTickerBadgeText({
13
66
  text = "",
@@ -20,6 +73,7 @@ export function ResponsiveTickerBadgeText({
20
73
  onUserHover,
21
74
  onUserHoverEnd,
22
75
  onUserActivate,
76
+ onUserContextMenu,
23
77
  }: {
24
78
  text?: string;
25
79
  tokens?: readonly InlineContentToken[];
@@ -28,10 +82,12 @@ export function ResponsiveTickerBadgeText({
28
82
  textColor: string;
29
83
  openTicker: (symbol: string) => void;
30
84
  userByUsername?: Map<string, ChatUserSummary>;
31
- onUserHover?: (user: ChatUserSummary) => void;
85
+ onUserHover?: (user: ChatUserSummary, anchor?: ChatUserAnchor) => void;
32
86
  onUserHoverEnd?: () => void;
33
- onUserActivate?: (user: ChatUserSummary) => void;
87
+ onUserActivate?: (user: ChatUserSummary, anchor?: ChatUserAnchor) => void;
88
+ onUserContextMenu?: (user: ChatUserSummary, event: ChatUserContextMenuEvent, anchor?: ChatUserAnchor) => void;
34
89
  }) {
90
+ const { nativeContextMenu } = useUiCapabilities();
35
91
  const [hoveredSymbol, setHoveredSymbol] = useState<string | null>(null);
36
92
  const tokens = useMemo(() => providedTokens ?? tokenizeInlineContent(text), [providedTokens, text]);
37
93
  const renderTextToken = (value: string, tokenIndex: number) => {
@@ -53,34 +109,18 @@ export function ResponsiveTickerBadgeText({
53
109
  </Text>
54
110
  );
55
111
  };
56
- const renderUsernameToken = (username: string, value: string, tokenIndex: number) => {
57
- const user = userByUsername?.get(username.toLowerCase()) ?? null;
58
- return (
59
- <Box
60
- key={`mention:${tokenIndex}:${username}`}
61
- height={1}
62
- flexDirection="row"
63
- backgroundColor={blendHex(colors.panel, colors.positive, 0.24)}
64
- onMouseOver={() => {
65
- if (user) onUserHover?.(user);
66
- }}
67
- onMouseOut={() => {
68
- if (user) onUserHoverEnd?.();
69
- }}
70
- // Only a mention of someone the chat has a summary for has a card to open.
71
- onMouseDown={user && onUserActivate ? (event: { preventDefault?: () => void; stopPropagation?: () => void }) => {
72
- event.preventDefault?.();
73
- event.stopPropagation?.();
74
- onUserActivate(user);
75
- } : undefined}
76
- style={user && onUserActivate ? { cursor: "pointer" } : undefined}
77
- >
78
- <Text fg={colors.positive} attributes={TextAttributes.BOLD}>
79
- {value}
80
- </Text>
81
- </Box>
82
- );
83
- };
112
+ const renderUsernameToken = (username: string, value: string, tokenIndex: number) => (
113
+ <UserMentionToken
114
+ key={`mention:${tokenIndex}:${username}`}
115
+ value={value}
116
+ user={userByUsername?.get(username.toLowerCase()) ?? null}
117
+ nativeContextMenu={nativeContextMenu === true}
118
+ onUserHover={onUserHover}
119
+ onUserHoverEnd={onUserHoverEnd}
120
+ onUserActivate={onUserActivate}
121
+ onUserContextMenu={onUserContextMenu}
122
+ />
123
+ );
84
124
 
85
125
  return (
86
126
  <Box
@@ -3,10 +3,15 @@ import { Box, Text, useUiCapabilities } from "../../../../ui";
3
3
  import { TextAttributes } from "../../../../ui";
4
4
  import { colors } from "../../../../theme/colors";
5
5
  import type { ChatUserSummary, PublicPortfolioAnalytics } from "../../../../api-client";
6
- import { formatNumber } from "../../../../utils/format";
6
+ import { displayWidth, formatNumber, truncateToDisplayWidth } from "../../../../utils/format";
7
7
  import { truncateWithEllipsis } from "../../../../utils/text-wrap";
8
+ import type { ChatUserAnchor } from "./types";
8
9
 
9
- export const PROFILE_POPOVER_CLOSE_DELAY_MS = 40;
10
+ /**
11
+ * How long a card the pointer left stays: long enough to move from the name
12
+ * onto the card beside it, whose own hover then keeps it open.
13
+ */
14
+ export const PROFILE_POPOVER_CLOSE_DELAY_MS = 250;
10
15
 
11
16
  function hasPortfolioAnalytics(analytics: PublicPortfolioAnalytics | null | undefined): boolean {
12
17
  return Boolean(
@@ -122,31 +127,92 @@ function HeaderAnalyticsStats({
122
127
  );
123
128
  }
124
129
 
130
+ /** The narrowest name-only card, so a short name still has a button to aim at. */
131
+ const NAME_CARD_MIN_WIDTH = 18;
132
+
133
+ /**
134
+ * A name-only card's one row: the handle, then the display name where it says
135
+ * something the handle does not.
136
+ */
137
+ function nameCardRow(user: ChatUserSummary): { handle: string; name: string | null } {
138
+ const displayName = user.displayName?.trim() ?? "";
139
+ if (!user.username) return { handle: displayName, name: null };
140
+ const same = displayName.replace(/^@+/, "").toLowerCase() === user.username.toLowerCase();
141
+ return { handle: `@${user.username}`, name: displayName && !same ? displayName : null };
142
+ }
143
+
144
+ function nameCardNaturalWidth(user: ChatUserSummary): number {
145
+ const { handle, name } = nameCardRow(user);
146
+ return displayWidth(handle) + (name ? 1 + displayWidth(name) : 0);
147
+ }
148
+
149
+ function NameCardRow({ user, width }: { user: ChatUserSummary; width: number }) {
150
+ const { handle, name } = nameCardRow(user);
151
+ const handleText = truncateToDisplayWidth(handle, width);
152
+ const nameWidth = width - displayWidth(handleText) - 1;
153
+ return (
154
+ <Box height={1} width={width} flexDirection="row">
155
+ <Text fg={colors.positive} attributes={TextAttributes.BOLD}>{handleText}</Text>
156
+ {name && nameWidth >= 4 ? <Text fg={colors.textDim}>{` ${truncateToDisplayWidth(name, nameWidth)}`}</Text> : null}
157
+ </Box>
158
+ );
159
+ }
160
+
125
161
  export function UserProfilePopover({
126
162
  user,
163
+ anchor = null,
127
164
  width,
128
165
  onClose,
129
166
  onDismiss = onClose,
130
167
  onKeepOpen,
131
168
  isOwnProfile = false,
132
169
  onSetUpProfile,
170
+ messageAction,
171
+ messageRefusal,
133
172
  }: {
134
173
  user: ChatUserSummary;
174
+ /**
175
+ * The name or @mention it opened from. The desktop and the web place the
176
+ * card just below it, or above it near the pane's foot; without one, and in
177
+ * the terminal, it sits in the pane's top-right corner.
178
+ */
179
+ anchor?: ChatUserAnchor;
135
180
  width: number;
136
181
  /** The pointer left the card. */
137
182
  onClose: () => void;
138
183
  /** A click outside the card or Esc closes it, even a pinned one. */
139
184
  onDismiss?: () => void;
185
+ /** The pointer came onto the card: it stays open while it is used. */
140
186
  onKeepOpen: () => void;
141
187
  isOwnProfile?: boolean;
142
188
  onSetUpProfile?: () => void;
189
+ /** "Message" or "Open DM" for someone you can write to; left out for anyone else. */
190
+ messageAction?: { label: string; onPress: () => void } | null;
191
+ /**
192
+ * Why someone takes no DM from you. The name-only card says it where the
193
+ * button would be; a profile card leaves it out.
194
+ */
195
+ messageRefusal?: string | null;
143
196
  }) {
144
- const popoverWidth = Math.max(24, Math.min(38, width - 4));
197
+ const { nativePaneChrome } = useUiCapabilities();
198
+ // The terminal card's border and padding take two cells a side; the desktop
199
+ // popover pads itself, and its content keeps the width it always had.
200
+ const chromeWidth = nativePaneChrome ? 2 : 4;
201
+ const maxPopoverWidth = Math.max(24, Math.min(38, width - 4));
202
+ // Anyone without a public profile still gets a card: the name and a way to write to them.
203
+ const nameOnly = !isOwnProfile && !hasPublicChatProfileInfo(user);
204
+ const popoverWidth = nameOnly
205
+ ? Math.min(maxPopoverWidth, Math.max(
206
+ NAME_CARD_MIN_WIDTH,
207
+ nameCardNaturalWidth(user),
208
+ messageRefusal ? displayWidth(messageRefusal) : 0,
209
+ ) + chromeWidth)
210
+ : maxPopoverWidth;
145
211
  const meta = [user.title, user.company].filter(Boolean).join(" · ");
146
212
  const bio = user.bio?.trim();
147
213
  const analytics = user.portfolioAnalytics;
148
214
  const metrics = analytics ? analyticsMetrics(analytics) : [];
149
- const headerWidth = Math.max(1, popoverWidth - 2);
215
+ const headerWidth = Math.max(1, popoverWidth - chromeWidth);
150
216
  const maxStatsWidth = Math.max(0, headerWidth - 8);
151
217
  const statsWidth = metrics.length > 0 && maxStatsWidth >= 8
152
218
  ? Math.min(headerMetricsNaturalWidth(metrics), maxStatsWidth)
@@ -154,8 +220,16 @@ export function UserProfilePopover({
154
220
  const usernameWidth = Math.max(1, headerWidth - (statsWidth > 0 ? statsWidth + 1 : 0));
155
221
  const showSetupAction = shouldOfferChatProfileSetup(user, isOwnProfile) && !!onSetUpProfile;
156
222
 
157
- const { nativePaneChrome } = useUiCapabilities();
158
- const content = (
223
+ const content = nameOnly ? (
224
+ <>
225
+ <NameCardRow user={user} width={headerWidth} />
226
+ {messageAction ? (
227
+ <Button label={messageAction.label} width={headerWidth} variant="ghost" compact stopPropagation onPress={messageAction.onPress} />
228
+ ) : messageRefusal ? (
229
+ <Text fg={colors.textDim} wrapText width={headerWidth}>{messageRefusal}</Text>
230
+ ) : null}
231
+ </>
232
+ ) : (
159
233
  <>
160
234
  <Box height={1} width={headerWidth} flexDirection="row">
161
235
  <Box width={usernameWidth}>
@@ -171,33 +245,42 @@ export function UserProfilePopover({
171
245
  ) : null}
172
246
  <Box flexGrow={1} />
173
247
  </Box>
174
- {meta ? <Text fg={colors.textDim}>{truncateWithEllipsis(meta, popoverWidth - 2)}</Text> : null}
248
+ {meta ? <Text fg={colors.textDim}>{truncateWithEllipsis(meta, headerWidth)}</Text> : null}
175
249
  {bio ? (
176
- <Text fg={colors.text} wrapText width={popoverWidth - 2}>
250
+ <Text fg={colors.text} wrapText width={headerWidth}>
177
251
  {bio}
178
252
  </Text>
179
253
  ) : null}
180
254
  {showSetupAction ? (
181
255
  <Button label="Set up profile" width={headerWidth} variant="ghost" compact stopPropagation onPress={onSetUpProfile} />
182
256
  ) : null}
257
+ {messageAction ? (
258
+ <Button label={messageAction.label} width={headerWidth} variant="ghost" compact stopPropagation onPress={messageAction.onPress} />
259
+ ) : null}
183
260
  </>
184
261
  );
185
262
 
186
263
  if (nativePaneChrome) {
187
- // The kit popover, anchored to the chat pane's top-right corner where the
188
- // terminal card sits. It stays open while the pointer is over it.
264
+ // The kit popover, beside the name it opened from so the pointer reaches
265
+ // it in one short move, and inside the pane. Opened with no name (the
266
+ // keyboard on a message that is not on screen), it takes the pane's
267
+ // top-right corner like the terminal card. It stays open while pointed at.
189
268
  return (
190
269
  <Box position="absolute" top={1} right={2} width={1} height={0}>
191
270
  <Popover
192
271
  open
193
272
  onOpenChange={(open) => { if (!open) onDismiss(); }}
194
273
  trigger={<Box width={1} height={0} />}
195
- placement="bottom-end"
274
+ anchor={anchor}
275
+ boundary="pane"
276
+ placement={anchor ? "bottom-start" : "bottom-end"}
196
277
  minWidth={0}
197
278
  focusOnOpen={false}
198
279
  label="User profile"
280
+ onPointerEnter={onKeepOpen}
281
+ onPointerLeave={onClose}
199
282
  >
200
- <Box flexDirection="column" width={headerWidth} onMouseMove={onKeepOpen} onMouseOut={onClose}>
283
+ <Box flexDirection="column" width={headerWidth}>
201
284
  {content}
202
285
  </Box>
203
286
  </Popover>
@@ -216,6 +299,7 @@ export function UserProfilePopover({
216
299
  border
217
300
  borderColor={colors.borderFocused}
218
301
  paddingX={1}
302
+ onMouseOver={onKeepOpen}
219
303
  onMouseMove={onKeepOpen}
220
304
  onMouseOut={onClose}
221
305
  style={{ zIndex: 4 }}
@@ -30,6 +30,7 @@ export function TerminalChatMessage({
30
30
  onUserHover,
31
31
  onUserHoverEnd,
32
32
  onUserActivate,
33
+ onUserContextMenu,
33
34
  beginReplyTo,
34
35
  beginEditMessage,
35
36
  jumpToMessage,
@@ -97,6 +98,7 @@ export function TerminalChatMessage({
97
98
  onUserHover={onUserHover}
98
99
  onUserHoverEnd={onUserHoverEnd}
99
100
  onUserActivate={onUserActivate}
101
+ onUserContextMenu={onUserContextMenu}
100
102
  {...actionProps}
101
103
  />
102
104
  )}
@@ -120,6 +122,7 @@ export function TerminalChatMessage({
120
122
  onUserHover={onUserHover}
121
123
  onUserHoverEnd={onUserHoverEnd}
122
124
  onUserActivate={onUserActivate}
125
+ onUserContextMenu={onUserContextMenu}
123
126
  />
124
127
  </Box>
125
128
  {lineIndex === 0 && state.grouped && <ChatMessageActions floating {...actionProps} />}
@@ -1,5 +1,15 @@
1
1
  import type { InlineTickerCatalogEntry } from "../../../../state/hooks/inline-tickers";
2
2
  import type { ChatAttachment, ChatMessage, ChatUserSummary } from "../../../../api-client";
3
+ import type { BoxRenderable } from "../../../../ui";
4
+
5
+ /** The name or @mention a user's card opens beside, where the renderer places cards next to it. */
6
+ export type ChatUserAnchor = BoxRenderable | null;
7
+
8
+ /** The pointer event a user menu opens from. */
9
+ export interface ChatUserContextMenuEvent {
10
+ preventDefault?: () => void;
11
+ stopPropagation?: () => void;
12
+ }
3
13
 
4
14
  export interface ChatMessageBaseProps {
5
15
  msg: ChatMessage;
@@ -11,9 +21,11 @@ export interface ChatMessageBaseProps {
11
21
  catalog: Record<string, InlineTickerCatalogEntry>;
12
22
  userByUsername: Map<string, ChatUserSummary>;
13
23
  openTicker: (symbol: string) => void;
14
- onUserHover: (user: ChatUserSummary) => void;
24
+ onUserHover: (user: ChatUserSummary, anchor?: ChatUserAnchor) => void;
15
25
  onUserHoverEnd: () => void;
16
- onUserActivate?: (user: ChatUserSummary) => void;
26
+ onUserActivate?: (user: ChatUserSummary, anchor?: ChatUserAnchor) => void;
27
+ /** A right-click on a name or @mention: the user's own menu. */
28
+ onUserContextMenu?: (user: ChatUserSummary, event: ChatUserContextMenuEvent, anchor?: ChatUserAnchor) => void;
17
29
  beginReplyTo: (index: number, options?: { deferFocus?: boolean }) => void;
18
30
  beginEditMessage: (index: number, options?: { deferFocus?: boolean }) => boolean;
19
31
  jumpToMessage: (messageId: string) => void;