gloomberb 0.16.5 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (691) hide show
  1. package/package.json +3 -1
  2. package/src/api-client/central-bank-rates.ts +6 -1
  3. package/src/api-client/chat-pane-link.ts +47 -0
  4. package/src/api-client/chat.ts +35 -1
  5. package/src/api-client/credit-boards.ts +2 -0
  6. package/src/api-client/data.ts +85 -1
  7. package/src/api-client/equity-screener.ts +3 -0
  8. package/src/api-client/europe-energy.ts +197 -0
  9. package/src/api-client/index.ts +15 -0
  10. package/src/api-client/letters.ts +41 -0
  11. package/src/api-client/market-discovery.ts +3 -0
  12. package/src/api-client/normalizers.ts +68 -6
  13. package/src/api-client/organization-tickers.ts +44 -0
  14. package/src/api-client/perps.ts +8 -0
  15. package/src/api-client/request.ts +11 -2
  16. package/src/api-client/socket.ts +29 -0
  17. package/src/api-client/themes.ts +22 -1
  18. package/src/api-client/types.ts +88 -2
  19. package/src/app/global-shortcuts.ts +35 -1
  20. package/src/app/keybindings/actions.ts +12 -1
  21. package/src/app/pane-runtime/plugin-bindings.ts +2 -2
  22. package/src/app/pane-runtime/ticker-open-runtime.ts +9 -3
  23. package/src/app/runtime/desktop-deeplink.ts +58 -20
  24. package/src/app/runtime/startup.ts +1 -1
  25. package/src/app.tsx +6 -0
  26. package/src/architecture/core-builtin-allowlist.json +5 -14
  27. package/src/architecture/dom-cell-metrics-allowlist.json +28 -0
  28. package/src/ask-gloom/pane-context.ts +118 -0
  29. package/src/ask-gloom/pane-request.ts +70 -0
  30. package/src/ask-gloom/pane-text.ts +201 -0
  31. package/src/capabilities/chart-series.ts +8 -1
  32. package/src/capabilities/factories.ts +1 -0
  33. package/src/cli/central-bank-events.ts +23 -0
  34. package/src/cli/command-options.ts +17 -5
  35. package/src/cli/commands/crypto-hints.ts +28 -2
  36. package/src/cli/commands/fred-series.ts +155 -4
  37. package/src/cli/commands/market.ts +175 -35
  38. package/src/cli/commands/overview.ts +62 -20
  39. package/src/cli/commands/search.ts +254 -23
  40. package/src/cli/commands/ticker.ts +137 -24
  41. package/src/cli/desktop-pane-shot-view.ts +114 -0
  42. package/src/cli/desktop-pane-shot.ts +53 -37
  43. package/src/cli/entry.ts +7 -0
  44. package/src/cli/errors.ts +7 -5
  45. package/src/cli/exit-codes.ts +8 -0
  46. package/src/cli/export-source.ts +101 -0
  47. package/src/cli/freshness.ts +18 -3
  48. package/src/cli/glossary.ts +79 -8
  49. package/src/cli/help.ts +10 -0
  50. package/src/cli/helpers.ts +2 -5
  51. package/src/cli/history-rows.ts +244 -13
  52. package/src/cli/index.ts +9 -3
  53. package/src/cli/known-gaps.ts +76 -8
  54. package/src/cli/listing-arg.ts +201 -90
  55. package/src/cli/listing-suggestion.ts +159 -0
  56. package/src/cli/native-smoke.ts +13 -0
  57. package/src/cli/not-a-ticker.ts +24 -4
  58. package/src/cli/pane-functions/access-gate.ts +8 -2
  59. package/src/cli/pane-functions/capabilities.ts +17 -9
  60. package/src/cli/pane-functions/catalog.ts +183 -39
  61. package/src/cli/pane-functions/data.ts +30 -0
  62. package/src/cli/pane-functions/dom.ts +44 -14
  63. package/src/cli/pane-functions/freshness.ts +48 -24
  64. package/src/cli/pane-functions/function-help.ts +66 -12
  65. package/src/cli/pane-functions/headless.ts +81 -22
  66. package/src/cli/pane-functions/index.ts +90 -18
  67. package/src/cli/pane-functions/options.ts +8 -0
  68. package/src/cli/pane-functions/report-notices.ts +6 -3
  69. package/src/cli/pane-functions/report.ts +16 -2
  70. package/src/cli/pane-functions/resolver.ts +32 -11
  71. package/src/cli/pane-functions/screenshot.ts +145 -9
  72. package/src/cli/perpetual-contract.ts +60 -0
  73. package/src/cli/registry.ts +3 -1
  74. package/src/cli/report-tables.ts +53 -13
  75. package/src/cli/result.ts +91 -17
  76. package/src/cli/series-search.ts +111 -0
  77. package/src/cli/staged-tickers.ts +1 -1
  78. package/src/cli/symbol-args.ts +18 -0
  79. package/src/cli/symbol-list.ts +1 -19
  80. package/src/cli/symbol-rows.ts +171 -0
  81. package/src/cli/ticker-resolution.ts +99 -8
  82. package/src/components/chart/composite/axis-overlays.tsx +10 -1
  83. package/src/components/chart/composite/composite-chart.tsx +24 -15
  84. package/src/components/chart/composite/downsample.ts +1 -1
  85. package/src/components/chart/composite/format.ts +29 -0
  86. package/src/components/chart/composite/interactions.ts +9 -2
  87. package/src/components/chart/composite/legend.tsx +57 -33
  88. package/src/components/chart/composite/panel-bitmap.ts +70 -35
  89. package/src/components/chart/composite/panel-labels.ts +18 -17
  90. package/src/components/chart/composite/panel-series.ts +1 -0
  91. package/src/components/chart/composite/panel-surface.tsx +6 -8
  92. package/src/components/chart/composite/price-axis-labels.tsx +1 -0
  93. package/src/components/chart/composite/rasterizer.ts +50 -8
  94. package/src/components/chart/composite/scene.ts +2 -1
  95. package/src/components/chart/composite/time-scale.ts +26 -9
  96. package/src/components/chart/composite/types.ts +18 -0
  97. package/src/components/chart/composite/use-composite-viewport.ts +30 -2
  98. package/src/components/chart/native/raster/crosshair.ts +8 -3
  99. package/src/components/chart/native/raster/primitives.ts +85 -21
  100. package/src/components/chart/native/surface/manager.ts +33 -8
  101. package/src/components/chart/surface3d/chart.tsx +1 -1
  102. package/src/components/command-bar/action-runtime.ts +1 -1
  103. package/src/components/command-bar/commands/collection-list.ts +210 -0
  104. package/src/components/command-bar/commands/collection.ts +40 -1
  105. package/src/components/command-bar/commands/direct/index.ts +4 -0
  106. package/src/components/command-bar/commands/registry.ts +8 -0
  107. package/src/components/command-bar/list/model.ts +2 -0
  108. package/src/components/command-bar/pane-templates/items.ts +1 -1
  109. package/src/components/command-bar/routes/root/plugin-install.ts +4 -4
  110. package/src/components/command-bar/routes/ticker-search/actions.ts +1 -0
  111. package/src/components/command-bar/routes/ticker-search/results.ts +3 -1
  112. package/src/components/command-bar/routing/actions.ts +1 -1
  113. package/src/components/command-bar/workflow/ops.ts +8 -97
  114. package/src/components/command-bar/workflow/pane-reuse.ts +177 -0
  115. package/src/components/command-bar/workflow/tickers.ts +35 -29
  116. package/src/components/data-table/view.tsx +1 -0
  117. package/src/components/feed-data-table/stack-view.tsx +18 -42
  118. package/src/components/layout/detached-pane-shell.tsx +59 -39
  119. package/src/components/layout/floating-pane.tsx +7 -1
  120. package/src/components/layout/header.tsx +5 -0
  121. package/src/components/layout/missing-pane.tsx +5 -3
  122. package/src/components/layout/pane/capture-stamp.ts +41 -0
  123. package/src/components/layout/pane/capture-status.ts +12 -0
  124. package/src/components/layout/pane/footer/keyboard.ts +3 -0
  125. package/src/components/layout/pane/footer/model.ts +7 -1
  126. package/src/components/layout/pane/header.tsx +26 -0
  127. package/src/components/layout/pane/index.tsx +9 -1
  128. package/src/components/layout/pane/sidebar.tsx +10 -11
  129. package/src/components/layout/shell/cursor-occlusion.ts +2 -1
  130. package/src/components/layout/shell/index.tsx +62 -60
  131. package/src/components/layout/shell/menu.ts +9 -1
  132. package/src/components/layout/shell/pane/actions.ts +32 -10
  133. package/src/components/layout/shell/pane/ask.ts +136 -0
  134. package/src/components/layout/shell/pane/capture.ts +136 -0
  135. package/src/components/layout/shell/pane/layers.tsx +26 -5
  136. package/src/components/layout/shell/pane/management-shortcuts.ts +46 -1
  137. package/src/components/layout/shell/shortcuts.ts +21 -4
  138. package/src/components/markdown-text.tsx +37 -6
  139. package/src/components/metric-treemap/heat-surface.tsx +1 -3
  140. package/src/components/reader/article-reader.tsx +467 -0
  141. package/src/components/reader/article-text.ts +527 -0
  142. package/src/components/reader/detail-reader.tsx +210 -0
  143. package/src/components/reader/reader-text.tsx +239 -0
  144. package/src/components/table-view-shared.tsx +8 -4
  145. package/src/components/ticker/badge/list.tsx +15 -4
  146. package/src/components/ticker/list-table-view.tsx +19 -1
  147. package/src/components/ui/choice-dialog.tsx +3 -0
  148. package/src/components/ui/data-table/index.tsx +1 -0
  149. package/src/components/ui/data-table/types.ts +7 -0
  150. package/src/components/ui/detail-scroll-body.tsx +31 -4
  151. package/src/components/ui/display.tsx +46 -1
  152. package/src/components/ui/icon.tsx +14 -1
  153. package/src/components/ui/list-view.tsx +3 -1
  154. package/src/components/ui/query-bar-keys.tsx +2 -0
  155. package/src/components/ui/query-bar.tsx +18 -1
  156. package/src/components/ui/select-button.tsx +3 -0
  157. package/src/data/config/store/fold-panes.ts +202 -11
  158. package/src/data/config/store/migrations.ts +20 -1
  159. package/src/data/config/store/portfolio-as-of-column.ts +57 -0
  160. package/src/docs/snapshot.json +191 -23
  161. package/src/i18n/es.ts +18 -1
  162. package/src/i18n/ja.ts +18 -1
  163. package/src/i18n/ko.ts +18 -1
  164. package/src/i18n/zh-cn.ts +18 -1
  165. package/src/i18n/zh-tw.ts +18 -1
  166. package/src/layout/desk-keys.ts +7 -0
  167. package/src/layout/desks.ts +2 -2
  168. package/src/layout-marketplace/gallery.tsx +1 -1
  169. package/src/market-data/market/freshness.ts +3 -2
  170. package/src/market-data/market/session-close-note.ts +3 -2
  171. package/src/market-data/market/status.ts +67 -6
  172. package/src/market-data/quotes/contributions.ts +17 -0
  173. package/src/market-data/quotes/metadata.ts +12 -0
  174. package/src/market-data/quotes/regular-session-valuation.ts +131 -0
  175. package/src/market-data/quotes/resolution.ts +7 -0
  176. package/src/market-data/snapshot-provider.ts +6 -3
  177. package/src/notifications/app-badge.ts +30 -0
  178. package/src/notifications/app-notifier.ts +17 -1
  179. package/src/notifications/notification-log.ts +180 -0
  180. package/src/plugins/auto-update-node.ts +2 -2
  181. package/src/plugins/auto-update.ts +2 -2
  182. package/src/plugins/builtin/account-management/calendar-tab.tsx +1 -1
  183. package/src/plugins/builtin/account-management/form-components.tsx +53 -1
  184. package/src/plugins/builtin/account-management/index.tsx +1 -0
  185. package/src/plugins/builtin/account-management/keyboard.ts +5 -0
  186. package/src/plugins/builtin/account-management/model.ts +27 -0
  187. package/src/plugins/builtin/account-management/pane.tsx +62 -2
  188. package/src/plugins/builtin/alerts/catalysts.ts +1 -1
  189. package/src/plugins/builtin/alerts/cli.ts +6 -4
  190. package/src/plugins/builtin/alerts/focus.ts +27 -0
  191. package/src/plugins/builtin/alerts/index.tsx +6 -2
  192. package/src/plugins/builtin/alerts/pane.tsx +19 -1
  193. package/src/plugins/builtin/analytics/index.tsx +5 -2
  194. package/src/plugins/builtin/analytics/risk-headless.ts +30 -15
  195. package/src/plugins/builtin/attention/headless.ts +1 -1
  196. package/src/plugins/builtin/awards/headless.ts +88 -18
  197. package/src/plugins/builtin/awards/model.ts +41 -1
  198. package/src/plugins/builtin/awards/pane.tsx +48 -30
  199. package/src/plugins/builtin/backtest/headless.ts +2 -2
  200. package/src/plugins/builtin/backtest/pane.tsx +2 -1
  201. package/src/plugins/builtin/bond-calculator/index.tsx +3 -5
  202. package/src/plugins/builtin/broker-manager/index.tsx +1 -0
  203. package/src/plugins/builtin/buildout/detail/company.tsx +7 -4
  204. package/src/plugins/builtin/builtin-plugin-meta.ts +5 -4
  205. package/src/plugins/builtin/catalysts/headless.ts +1 -1
  206. package/src/plugins/builtin/catalysts/model.ts +92 -10
  207. package/src/plugins/builtin/catalysts/pane.tsx +6 -4
  208. package/src/plugins/builtin/cds/client.ts +11 -31
  209. package/src/plugins/builtin/cds/headless.ts +5 -2
  210. package/src/plugins/builtin/cds/index.tsx +21 -30
  211. package/src/plugins/builtin/cds/model.ts +21 -1
  212. package/src/plugins/builtin/cds/pane.tsx +6 -4
  213. package/src/plugins/builtin/central-bank-rates/client.ts +4 -0
  214. package/src/plugins/builtin/central-bank-rates/headless.ts +46 -16
  215. package/src/plugins/builtin/central-bank-rates/index.tsx +1 -1
  216. package/src/plugins/builtin/central-bank-rates/model.ts +99 -0
  217. package/src/plugins/builtin/central-bank-rates/pane.tsx +1 -1
  218. package/src/plugins/builtin/central-bank-rates/series.ts +19 -25
  219. package/src/plugins/builtin/changelog/index.tsx +11 -12
  220. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  221. package/src/plugins/builtin/chart-composer/headless-schema.ts +3 -1
  222. package/src/plugins/builtin/chart-composer/headless.ts +151 -9
  223. package/src/plugins/builtin/chart-composer/index.tsx +6 -1
  224. package/src/plugins/builtin/chart-composer/pane.tsx +84 -40
  225. package/src/plugins/builtin/chart-composer/session-history.ts +20 -5
  226. package/src/plugins/builtin/chat/command-bar.ts +6 -8
  227. package/src/plugins/builtin/chat/content/composer-runtime.ts +6 -1
  228. package/src/plugins/builtin/chat/content/composer.tsx +15 -2
  229. package/src/plugins/builtin/chat/content/footer.tsx +27 -5
  230. package/src/plugins/builtin/chat/content/handle-prompt.tsx +326 -0
  231. package/src/plugins/builtin/chat/content/index.tsx +201 -13
  232. package/src/plugins/builtin/chat/content/layout-metrics.ts +4 -0
  233. package/src/plugins/builtin/chat/content/new-messages.tsx +89 -0
  234. package/src/plugins/builtin/chat/content/open-targets.ts +20 -4
  235. package/src/plugins/builtin/chat/content/row-activity.ts +132 -0
  236. package/src/plugins/builtin/chat/content/scroll.ts +133 -28
  237. package/src/plugins/builtin/chat/content/shortcuts.ts +2 -2
  238. package/src/plugins/builtin/chat/content/transcript.tsx +155 -79
  239. package/src/plugins/builtin/chat/content/types.ts +1 -0
  240. package/src/plugins/builtin/chat/controller/fetch.ts +8 -2
  241. package/src/plugins/builtin/chat/controller/index.ts +121 -1
  242. package/src/plugins/builtin/chat/controller/message-loading.ts +3 -0
  243. package/src/plugins/builtin/chat/controller/messages.ts +7 -2
  244. package/src/plugins/builtin/chat/controller/realtime.ts +10 -1
  245. package/src/plugins/builtin/chat/controller/send.ts +61 -1
  246. package/src/plugins/builtin/chat/controller/state.ts +8 -0
  247. package/src/plugins/builtin/chat/ghost-user.ts +20 -0
  248. package/src/plugins/builtin/chat/handle-prompt.ts +128 -0
  249. package/src/plugins/builtin/chat/index.tsx +66 -25
  250. package/src/plugins/builtin/chat/layout.ts +84 -18
  251. package/src/plugins/builtin/chat/message/desktop.tsx +111 -14
  252. package/src/plugins/builtin/chat/message/header.tsx +81 -21
  253. package/src/plugins/builtin/chat/message/pane-chip.tsx +223 -0
  254. package/src/plugins/builtin/chat/message/person-icons.tsx +53 -0
  255. package/src/plugins/builtin/chat/message/profile-popover.tsx +77 -9
  256. package/src/plugins/builtin/chat/message/reactions.tsx +210 -0
  257. package/src/plugins/builtin/chat/message/render-state.ts +33 -4
  258. package/src/plugins/builtin/chat/message/terminal.tsx +72 -8
  259. package/src/plugins/builtin/chat/message/types.ts +15 -0
  260. package/src/plugins/builtin/chat/pane-links/model.ts +149 -0
  261. package/src/plugins/builtin/chat/pane-links/open.ts +29 -0
  262. package/src/plugins/builtin/chat/pane-links/previews.ts +119 -0
  263. package/src/plugins/builtin/chat/pane-state.ts +5 -8
  264. package/src/plugins/builtin/chat/people.ts +202 -0
  265. package/src/plugins/builtin/chat/profile-popover.ts +18 -2
  266. package/src/plugins/builtin/chat/reactions.ts +197 -0
  267. package/src/plugins/builtin/chat/share/flow.tsx +87 -0
  268. package/src/plugins/builtin/chat/share/targets.ts +27 -0
  269. package/src/plugins/builtin/chat/status-widget.tsx +7 -9
  270. package/src/plugins/builtin/chat/unread-badge.ts +65 -0
  271. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +151 -27
  272. package/src/plugins/builtin/chat/unread-inbox.ts +22 -0
  273. package/src/plugins/builtin/clinical-trials/client.ts +26 -1
  274. package/src/plugins/builtin/clinical-trials/index.tsx +10 -2
  275. package/src/plugins/builtin/clinical-trials/model.ts +269 -0
  276. package/src/plugins/builtin/clinical-trials/pane.tsx +247 -62
  277. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +156 -0
  278. package/src/plugins/builtin/cloud/askg/attached-pane.tsx +69 -0
  279. package/src/plugins/builtin/cloud/askg/pane-attachment.ts +94 -0
  280. package/src/plugins/builtin/cloud/askg/pane.tsx +141 -12
  281. package/src/plugins/builtin/cloud/askg/pending-question.ts +11 -0
  282. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +1 -0
  283. package/src/plugins/builtin/cloud/plugin.tsx +19 -2
  284. package/src/plugins/builtin/cloud/team/model.ts +5 -0
  285. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  286. package/src/plugins/builtin/cloud/team/store.ts +8 -3
  287. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  288. package/src/plugins/builtin/comment-letters/pane.tsx +30 -11
  289. package/src/plugins/builtin/commodities-energy/index.tsx +179 -0
  290. package/src/plugins/builtin/commodity-balances/client.ts +208 -0
  291. package/src/plugins/builtin/commodity-balances/headless.ts +66 -0
  292. package/src/plugins/builtin/commodity-balances/index.tsx +32 -0
  293. package/src/plugins/builtin/commodity-balances/model.ts +261 -0
  294. package/src/plugins/builtin/commodity-balances/pane.tsx +227 -0
  295. package/src/plugins/builtin/company-attention/headless.ts +2 -2
  296. package/src/plugins/builtin/company-kpis/headless.ts +97 -10
  297. package/src/plugins/builtin/company-kpis/model.ts +24 -2
  298. package/src/plugins/builtin/company-kpis/pane.tsx +13 -14
  299. package/src/plugins/builtin/composite-plugins.ts +26 -1
  300. package/src/plugins/builtin/connections/index.ts +1 -0
  301. package/src/plugins/builtin/cot/headless.ts +1 -1
  302. package/src/plugins/builtin/cpi/headless.ts +3 -3
  303. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +10 -1
  304. package/src/plugins/builtin/credit-boards/client.ts +6 -2
  305. package/src/plugins/builtin/credit-boards/headless.ts +108 -31
  306. package/src/plugins/builtin/credit-boards/index.tsx +21 -42
  307. package/src/plugins/builtin/credit-boards/model.ts +11 -3
  308. package/src/plugins/builtin/credit-conditions/client.ts +12 -9
  309. package/src/plugins/builtin/credit-conditions/headless.ts +13 -2
  310. package/src/plugins/builtin/credit-conditions/index.tsx +20 -22
  311. package/src/plugins/builtin/credit-conditions/model.ts +19 -11
  312. package/src/plugins/builtin/credit-conditions/series.ts +8 -18
  313. package/src/plugins/builtin/credit-documents/headless.ts +2 -2
  314. package/src/plugins/builtin/credit-documents/index.tsx +27 -10
  315. package/src/plugins/builtin/debt-maturities/headless.ts +17 -11
  316. package/src/plugins/builtin/debt-maturities/index.tsx +3 -17
  317. package/src/plugins/builtin/debug/index.tsx +2 -2
  318. package/src/plugins/builtin/dividend-yield/client.ts +6 -1
  319. package/src/plugins/builtin/dividend-yield/headless.ts +8 -3
  320. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  321. package/src/plugins/builtin/dividend-yield/pane.tsx +6 -2
  322. package/src/plugins/builtin/dividend-yield/provider-client.ts +3 -1
  323. package/src/plugins/builtin/dividend-yield/types.ts +2 -0
  324. package/src/plugins/builtin/docs/blocks.tsx +4 -3
  325. package/src/plugins/builtin/docs/pane.tsx +16 -8
  326. package/src/plugins/builtin/doe/headless.ts +12 -8
  327. package/src/plugins/builtin/earnings/index.tsx +11 -0
  328. package/src/plugins/builtin/earnings-calls/headless.ts +28 -2
  329. package/src/plugins/builtin/earnings-calls/index.tsx +1 -1
  330. package/src/plugins/builtin/earnings-calls/pane.tsx +2 -12
  331. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -0
  332. package/src/plugins/builtin/econ/fred-catalog.ts +9 -0
  333. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  334. package/src/plugins/builtin/equity-screener/headless.ts +39 -5
  335. package/src/plugins/builtin/equity-screener/model.ts +121 -68
  336. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -3
  337. package/src/plugins/builtin/executives/pane.tsx +3 -1
  338. package/src/plugins/builtin/exposure/drivers.ts +13 -0
  339. package/src/plugins/builtin/exposure/headless.ts +205 -13
  340. package/src/plugins/builtin/exposure/index.tsx +1 -0
  341. package/src/plugins/builtin/exposure/model.ts +23 -3
  342. package/src/plugins/builtin/exposure/pane.tsx +11 -11
  343. package/src/plugins/builtin/fear-greed/index.tsx +14 -10
  344. package/src/plugins/builtin/filing-events/index.tsx +20 -0
  345. package/src/plugins/builtin/fixed-income/index.tsx +46 -0
  346. package/src/plugins/builtin/fixed-income/model.ts +27 -0
  347. package/src/plugins/builtin/fixed-income/pane.tsx +162 -0
  348. package/src/plugins/builtin/fixed-income/shortcut.ts +52 -0
  349. package/src/plugins/builtin/futures/index.tsx +24 -7
  350. package/src/plugins/builtin/futures/series.ts +23 -2
  351. package/src/plugins/builtin/futures/use-front-returns.ts +3 -2
  352. package/src/plugins/builtin/futures-curve/headless.ts +1 -1
  353. package/src/plugins/builtin/futures-curve/index.tsx +1 -1
  354. package/src/plugins/builtin/futures-curve/pane.tsx +6 -4
  355. package/src/plugins/builtin/fx-matrix/client.ts +63 -0
  356. package/src/plugins/builtin/fx-matrix/export.ts +2 -1
  357. package/src/plugins/builtin/fx-matrix/headless.ts +154 -0
  358. package/src/plugins/builtin/fx-matrix/index.tsx +3 -3
  359. package/src/plugins/builtin/gpu/headless.ts +4 -4
  360. package/src/plugins/builtin/help/function-index.ts +2 -0
  361. package/src/plugins/builtin/help/index.tsx +14 -1
  362. package/src/plugins/builtin/help/shortcut-table.tsx +4 -2
  363. package/src/plugins/builtin/insider/detail.tsx +20 -12
  364. package/src/plugins/builtin/insider/index.tsx +2 -2
  365. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -1
  366. package/src/plugins/builtin/iv-history/client.ts +6 -2
  367. package/src/plugins/builtin/iv-history/format.ts +9 -0
  368. package/src/plugins/builtin/iv-history/headless.ts +50 -18
  369. package/src/plugins/builtin/iv-history/index.tsx +3 -0
  370. package/src/plugins/builtin/iv-history/model.ts +35 -4
  371. package/src/plugins/builtin/iv-history/screen-pane.tsx +13 -5
  372. package/src/plugins/builtin/jobs/headless.ts +6 -8
  373. package/src/plugins/builtin/jodi/client.ts +220 -0
  374. package/src/plugins/builtin/jodi/headless.ts +63 -0
  375. package/src/plugins/builtin/jodi/index.tsx +32 -0
  376. package/src/plugins/builtin/jodi/model.ts +75 -0
  377. package/src/plugins/builtin/jodi/pane.tsx +141 -0
  378. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  379. package/src/plugins/builtin/market-heatmap/evidence.ts +13 -4
  380. package/src/plugins/builtin/market-heatmap/headless.ts +22 -7
  381. package/src/plugins/builtin/market-heatmap/index.tsx +47 -23
  382. package/src/plugins/builtin/market-heatmap/live.ts +2 -1
  383. package/src/plugins/builtin/market-heatmap/portfolio.ts +67 -21
  384. package/src/plugins/builtin/market-movers/table.ts +2 -2
  385. package/src/plugins/builtin/market-valuation/align.ts +10 -9
  386. package/src/plugins/builtin/members/headless.ts +1 -1
  387. package/src/plugins/builtin/members/pane.tsx +3 -2
  388. package/src/plugins/builtin/mna/distress/headless.ts +4 -4
  389. package/src/plugins/builtin/mna/distress/model.ts +52 -1
  390. package/src/plugins/builtin/mna/distress/pane.tsx +17 -12
  391. package/src/plugins/builtin/mna/headless.ts +3 -3
  392. package/src/plugins/builtin/mna/index.tsx +2 -0
  393. package/src/plugins/builtin/money-markets/headless.ts +5 -4
  394. package/src/plugins/builtin/news/index.tsx +0 -2
  395. package/src/plugins/builtin/news/wire/index.tsx +2 -2
  396. package/src/plugins/builtin/news/wire/industry-pane.tsx +28 -14
  397. package/src/plugins/builtin/news/wire/letters/client.ts +108 -0
  398. package/src/plugins/builtin/news/wire/letters/model.ts +69 -0
  399. package/src/plugins/builtin/news/wire/letters/pane.tsx +155 -0
  400. package/src/plugins/builtin/news/wire/mutes.ts +2 -1
  401. package/src/plugins/builtin/news/wire/news/detail-view.tsx +53 -104
  402. package/src/plugins/builtin/news/wire/news/footer.ts +2 -25
  403. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +0 -2
  404. package/src/plugins/builtin/news/wire/news/query-presets.ts +4 -1
  405. package/src/plugins/builtin/notes/index.tsx +1 -0
  406. package/src/plugins/builtin/openfda/client.ts +123 -15
  407. package/src/plugins/builtin/openfda/model.ts +163 -0
  408. package/src/plugins/builtin/openfda/pane.tsx +109 -65
  409. package/src/plugins/builtin/openfda/types.ts +25 -0
  410. package/src/plugins/builtin/options/index.tsx +3 -0
  411. package/src/plugins/builtin/options/view.tsx +1 -1
  412. package/src/plugins/builtin/options-positioning/headless.ts +3 -3
  413. package/src/plugins/builtin/pe-band/model.ts +3 -2
  414. package/src/plugins/builtin/perps/client.ts +50 -10
  415. package/src/plugins/builtin/perps/equity-row.tsx +29 -16
  416. package/src/plugins/builtin/perps/headless.ts +2 -2
  417. package/src/plugins/builtin/perps/index.tsx +5 -3
  418. package/src/plugins/builtin/perps/list-perpetuals.ts +118 -0
  419. package/src/plugins/builtin/perps/open-market.ts +15 -0
  420. package/src/plugins/builtin/plugin-marketplace/index.tsx +1 -1
  421. package/src/plugins/builtin/plugin-marketplace/pane.tsx +7 -7
  422. package/src/plugins/builtin/plugin-module.ts +3 -1
  423. package/src/plugins/builtin/portfolio-list/allocation.ts +5 -4
  424. package/src/plugins/builtin/portfolio-list/cell-version.ts +8 -1
  425. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +24 -136
  426. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +78 -19
  427. package/src/plugins/builtin/portfolio-list/cli/render.ts +249 -52
  428. package/src/plugins/builtin/portfolio-list/cli/shared.ts +44 -12
  429. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +21 -7
  430. package/src/plugins/builtin/portfolio-list/column-values.ts +103 -25
  431. package/src/plugins/builtin/portfolio-list/headless.ts +51 -15
  432. package/src/plugins/builtin/portfolio-list/index.tsx +5 -1
  433. package/src/plugins/builtin/portfolio-list/live-valuation.ts +25 -13
  434. package/src/plugins/builtin/portfolio-list/mutations.ts +2 -9
  435. package/src/plugins/builtin/portfolio-list/pane/data.ts +5 -1
  436. package/src/plugins/builtin/portfolio-list/pane/index.tsx +46 -8
  437. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +16 -10
  438. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +21 -1
  439. package/src/plugins/builtin/portfolio-list/settings.ts +17 -0
  440. package/src/plugins/builtin/portfolio-list/summary/index.tsx +12 -0
  441. package/src/plugins/builtin/portfolio-list/summary/totals.ts +8 -3
  442. package/src/plugins/builtin/portfolio-list/table.tsx +30 -6
  443. package/src/plugins/builtin/power/evidence.ts +86 -8
  444. package/src/plugins/builtin/power/gas-client.ts +110 -0
  445. package/src/plugins/builtin/power/gas-model.ts +194 -0
  446. package/src/plugins/builtin/power/gas.tsx +145 -0
  447. package/src/plugins/builtin/power/headless.ts +9 -2
  448. package/src/plugins/builtin/power/index.tsx +35 -3
  449. package/src/plugins/builtin/power/model.ts +2 -2
  450. package/src/plugins/builtin/power/pane.tsx +7 -3
  451. package/src/plugins/builtin/power/prices-client.ts +139 -0
  452. package/src/plugins/builtin/power/prices-headless.ts +205 -0
  453. package/src/plugins/builtin/power/prices-model.ts +103 -0
  454. package/src/plugins/builtin/power/prices.tsx +202 -0
  455. package/src/plugins/builtin/primary-dealers/client.ts +108 -0
  456. package/src/plugins/builtin/primary-dealers/headless.ts +53 -0
  457. package/src/plugins/builtin/primary-dealers/index.tsx +17 -0
  458. package/src/plugins/builtin/primary-dealers/model.ts +206 -0
  459. package/src/plugins/builtin/primary-dealers/pane.tsx +149 -0
  460. package/src/plugins/builtin/rate-path/headless.ts +38 -18
  461. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  462. package/src/plugins/builtin/relative-rotation/index.tsx +3 -0
  463. package/src/plugins/builtin/research/analyst-model.ts +9 -5
  464. package/src/plugins/builtin/research/headless-schema.ts +2 -1
  465. package/src/plugins/builtin/research/index.tsx +2 -0
  466. package/src/plugins/builtin/research/relative-valuation-headless.ts +68 -32
  467. package/src/plugins/builtin/research/relative-valuation-model.ts +45 -24
  468. package/src/plugins/builtin/research/relative-valuation-pane.tsx +39 -4
  469. package/src/plugins/builtin/research-search/article.ts +65 -0
  470. package/src/plugins/builtin/research-search/document-view.tsx +33 -215
  471. package/src/plugins/builtin/research-search/model.ts +0 -9
  472. package/src/plugins/builtin/research-search/pane.tsx +41 -30
  473. package/src/plugins/builtin/research-search/snippet.ts +0 -29
  474. package/src/plugins/builtin/revenue-breakdown/headless.ts +18 -6
  475. package/src/plugins/builtin/scanner/flow-pane.tsx +8 -1
  476. package/src/plugins/builtin/sec/filing-display.tsx +3 -36
  477. package/src/plugins/builtin/sec/headless.ts +25 -3
  478. package/src/plugins/builtin/sec/index.tsx +133 -44
  479. package/src/plugins/builtin/sec/model.ts +148 -2
  480. package/src/plugins/builtin/sec/row-summaries.ts +105 -0
  481. package/src/plugins/builtin/sectors/index.tsx +2 -0
  482. package/src/plugins/builtin/sectors/sector-model.ts +2 -2
  483. package/src/plugins/builtin/series-inventory.ts +3 -0
  484. package/src/plugins/builtin/shared/cloud-denied.ts +15 -0
  485. package/src/plugins/builtin/shared/free-preview.ts +47 -0
  486. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  487. package/src/plugins/builtin/shared/report-freshness.ts +3 -1
  488. package/src/plugins/builtin/shared/use-quote-board.ts +179 -43
  489. package/src/plugins/builtin/shared/volatility/options.ts +4 -1
  490. package/src/plugins/builtin/shared/wall-teaser.tsx +2 -2
  491. package/src/plugins/builtin/shared/zip-text.ts +46 -0
  492. package/src/plugins/builtin/short-interest/watch-headless.ts +2 -2
  493. package/src/plugins/builtin/short-interest/watch-model.ts +11 -2
  494. package/src/plugins/builtin/short-volume/headless.ts +1 -1
  495. package/src/plugins/builtin/social-mentions/headless.ts +1 -1
  496. package/src/plugins/builtin/supply-chain/headless.ts +43 -11
  497. package/src/plugins/builtin/supply-chain/model.ts +13 -0
  498. package/src/plugins/builtin/supply-chain/pane.tsx +5 -5
  499. package/src/plugins/builtin/themes/headless.ts +74 -10
  500. package/src/plugins/builtin/themes/model.ts +71 -4
  501. package/src/plugins/builtin/themes/pane.tsx +22 -5
  502. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +114 -0
  503. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +57 -2
  504. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +19 -55
  505. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +11 -0
  506. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +81 -19
  507. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +1 -1
  508. package/src/plugins/builtin/ticker-detail/headless-schema.ts +30 -0
  509. package/src/plugins/builtin/ticker-detail/headless.ts +88 -2
  510. package/src/plugins/builtin/ticker-detail/index.tsx +23 -11
  511. package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -1
  512. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +8 -6
  513. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +8 -6
  514. package/src/plugins/builtin/time-sales/headless.ts +23 -1
  515. package/src/plugins/builtin/time-sales/index.tsx +2 -1
  516. package/src/plugins/builtin/trace-bonds/client.ts +196 -0
  517. package/src/plugins/builtin/trace-bonds/headless.ts +69 -0
  518. package/src/plugins/builtin/trace-bonds/index.tsx +33 -0
  519. package/src/plugins/builtin/trace-bonds/model.ts +115 -0
  520. package/src/plugins/builtin/trace-bonds/pane.tsx +191 -0
  521. package/src/plugins/builtin/treasury-auctions/index.tsx +33 -47
  522. package/src/plugins/builtin/treasury-daily/client.ts +96 -0
  523. package/src/plugins/builtin/treasury-daily/headless.ts +69 -0
  524. package/src/plugins/builtin/treasury-daily/index.tsx +17 -0
  525. package/src/plugins/builtin/treasury-daily/model.ts +69 -0
  526. package/src/plugins/builtin/treasury-daily/pane.tsx +140 -0
  527. package/src/plugins/builtin/vol-surface/evidence.ts +7 -1
  528. package/src/plugins/builtin/vol-surface/headless.ts +50 -17
  529. package/src/plugins/builtin/vol-surface/heat-grid.tsx +113 -0
  530. package/src/plugins/builtin/vol-surface/model.ts +39 -6
  531. package/src/plugins/builtin/vol-surface/pane.tsx +14 -13
  532. package/src/plugins/builtin/vol-surface/stored.ts +63 -3
  533. package/src/plugins/builtin/vol-surface/surface.tsx +30 -2
  534. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +6 -5
  535. package/src/plugins/builtin/world-venue-map/basemap.ts +6 -3
  536. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +15 -2
  537. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +2 -1
  538. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +6 -6
  539. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +16 -9
  540. package/src/plugins/builtin/world-venue-map/map.tsx +145 -13
  541. package/src/plugins/builtin/world-venue-map/model.ts +56 -19
  542. package/src/plugins/builtin/world-venue-map/pane.tsx +5 -2
  543. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  544. package/src/plugins/builtin-editorial.ts +1 -1
  545. package/src/plugins/catalog-browser.ts +25 -2
  546. package/src/plugins/installer.ts +2 -2
  547. package/src/plugins/loader.ts +15 -0
  548. package/src/plugins/manager-node.ts +1 -1
  549. package/src/plugins/{builtin/plugin-marketplace → marketplace}/feed.ts +2 -2
  550. package/src/plugins/{builtin/plugin-marketplace → marketplace}/model.ts +63 -27
  551. package/src/plugins/{builtin/plugin-marketplace → marketplace}/store.ts +1 -1
  552. package/src/plugins/registry/host-actions.ts +11 -0
  553. package/src/portfolio/collection-name.ts +31 -0
  554. package/src/portfolio/valuation-basis.ts +79 -0
  555. package/src/public/time-series.ts +1 -1
  556. package/src/react/input.ts +33 -1
  557. package/src/react/key-repeat.ts +86 -0
  558. package/src/remote/controller.ts +21 -1
  559. package/src/remote/schema.ts +3 -2
  560. package/src/renderers/browser/main.tsx +1 -1
  561. package/src/renderers/browser/research-entry.ts +4 -2
  562. package/src/renderers/dom/build-assets.ts +14 -5
  563. package/src/renderers/dom/data-table/index.tsx +5 -3
  564. package/src/renderers/dom/data-table/row.tsx +3 -0
  565. package/src/renderers/dom/desktop/controls.tsx +2 -1
  566. package/src/renderers/dom/desktop/icons.tsx +34 -0
  567. package/src/renderers/dom/desktop/list-view.tsx +1 -1
  568. package/src/renderers/dom/desktop/menu.tsx +1 -1
  569. package/src/renderers/dom/desktop/query-bar.tsx +3 -1
  570. package/src/renderers/dom/host/block-glyph-canvas.tsx +6 -3
  571. package/src/renderers/dom/host/box.tsx +7 -2
  572. package/src/renderers/dom/host/chart-surface.tsx +34 -2
  573. package/src/renderers/dom/host/resize-rows.ts +111 -0
  574. package/src/renderers/dom/host/scroll-box.tsx +32 -0
  575. package/src/renderers/dom/host/size-observer.ts +24 -9
  576. package/src/renderers/dom/host/sticky-scroll.ts +147 -0
  577. package/src/renderers/dom/host/style.ts +15 -1
  578. package/src/renderers/dom/host/surface-3d.tsx +15 -7
  579. package/src/renderers/dom/host/surface-fit.ts +410 -0
  580. package/src/renderers/dom/host/text.tsx +2 -2
  581. package/src/renderers/dom/input-host.tsx +1 -0
  582. package/src/renderers/dom/styles.css +109 -16
  583. package/src/renderers/electrobun/bun/application-menu/click.ts +18 -0
  584. package/src/renderers/electrobun/bun/desktop/app-badge.ts +37 -0
  585. package/src/renderers/electrobun/bun/desktop/backend.ts +1 -0
  586. package/src/renderers/electrobun/bun/desktop/host-requests.ts +8 -0
  587. package/src/renderers/electrobun/bun/desktop/macos-dock-badge.ts +37 -0
  588. package/src/renderers/electrobun/bun/desktop/macos-dock-icon.ts +2 -40
  589. package/src/renderers/electrobun/bun/desktop/macos-objc.ts +41 -0
  590. package/src/renderers/electrobun/bun/index.ts +3 -2
  591. package/src/renderers/electrobun/bun/tui-entry.ts +18 -1
  592. package/src/renderers/electrobun/shared/protocol.ts +3 -1
  593. package/src/renderers/electrobun/view/app-badge-host.ts +17 -0
  594. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +20 -3
  595. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +59 -24
  596. package/src/renderers/electrobun/view/main.tsx +5 -3
  597. package/src/renderers/electrobun/view/remote/asset-data-client.ts +7 -1
  598. package/src/renderers/opentui/chart-surface.tsx +1 -0
  599. package/src/renderers/opentui/frame-capture.ts +33 -0
  600. package/src/renderers/opentui/host.tsx +10 -13
  601. package/src/renderers/opentui/pane-image/glyphs-license.txt +107 -0
  602. package/src/renderers/opentui/pane-image/glyphs.json +1 -0
  603. package/src/renderers/opentui/pane-image/index.ts +107 -0
  604. package/src/renderers/opentui/pane-image/raster.ts +428 -0
  605. package/src/renderers/opentui/start.tsx +3 -3
  606. package/src/renderers/opentui/test-utils.tsx +3 -0
  607. package/src/renderers/opentui/ui-host.tsx +29 -1
  608. package/src/series/resolve.ts +3 -2
  609. package/src/series/types.ts +8 -0
  610. package/src/series/validate.ts +2 -2
  611. package/src/shares/api.ts +5 -1
  612. package/src/shares/chat.ts +34 -0
  613. package/src/shares/dialog.tsx +295 -0
  614. package/src/shares/flow.tsx +56 -0
  615. package/src/shares/live.ts +14 -22
  616. package/src/shares/location.ts +31 -0
  617. package/src/shares/open.ts +26 -0
  618. package/src/shares/pane-image.ts +164 -0
  619. package/src/shares/personal-pane.ts +52 -0
  620. package/src/shares/portable-layout.ts +4 -2
  621. package/src/shares/prepare.ts +20 -0
  622. package/src/sources/gloomberb-cloud/index.ts +109 -5
  623. package/src/sources/gloomberb-cloud/normalizers.ts +7 -0
  624. package/src/sources/gloomberb-cloud/perpetuals.ts +249 -0
  625. package/src/sources/history-coverage.ts +22 -4
  626. package/src/sources/history-result.ts +3 -1
  627. package/src/sources/history-retention.ts +11 -0
  628. package/src/sources/listing-target.ts +4 -0
  629. package/src/sources/provider-errors.ts +2 -1
  630. package/src/sources/provider-router/batches.ts +18 -2
  631. package/src/sources/provider-router/index.ts +16 -1
  632. package/src/state/app/context/index.tsx +3 -2
  633. package/src/state/hooks/inline-tickers.ts +49 -11
  634. package/src/test-support/builtin-panes.ts +31 -0
  635. package/src/test-support/shell-pane-keys.tsx +1 -0
  636. package/src/theme/theme-context.tsx +15 -1
  637. package/src/tickers/instrument-kind.ts +9 -1
  638. package/src/tickers/listing-name.ts +34 -0
  639. package/src/tickers/listing-venues.ts +154 -0
  640. package/src/tickers/open-target.ts +11 -3
  641. package/src/tickers/perpetual-listing.ts +151 -0
  642. package/src/tickers/prediction-listing.ts +26 -0
  643. package/src/tickers/search/index.ts +34 -6
  644. package/src/tickers/search/ranking.ts +85 -4
  645. package/src/tickers/ticker-changes.ts +213 -0
  646. package/src/time-series/chart-data.ts +2 -0
  647. package/src/time-series/economic.ts +2 -6
  648. package/src/time-series/ice-credit.ts +39 -0
  649. package/src/time-series/parsed-history-cache.ts +41 -11
  650. package/src/time-series/resolve.ts +163 -28
  651. package/src/time-series/studies.ts +1 -0
  652. package/src/time-series/types.ts +8 -0
  653. package/src/types/config.ts +4 -2
  654. package/src/types/data-provider.ts +19 -1
  655. package/src/types/financials.ts +23 -0
  656. package/src/types/headless.ts +74 -0
  657. package/src/types/instrument.ts +4 -0
  658. package/src/types/plugin.ts +60 -2
  659. package/src/types/price-history.ts +2 -0
  660. package/src/types/ticker-changes.ts +26 -0
  661. package/src/ui/context-menu.tsx +13 -1
  662. package/src/ui/host.tsx +49 -7
  663. package/src/ui/live-resize.ts +10 -0
  664. package/src/ui/live-stretch.ts +51 -19
  665. package/src/ui/sticky-scroll.ts +7 -0
  666. package/src/utils/central-banks.ts +76 -0
  667. package/src/utils/cli-output.ts +126 -16
  668. package/src/utils/crypto-pair.ts +1 -1
  669. package/src/utils/currency-pair.ts +66 -0
  670. package/src/utils/currency-units.ts +11 -0
  671. package/src/utils/datetime-format.ts +27 -6
  672. package/src/utils/decimal-comma.ts +57 -0
  673. package/src/utils/dom-screenshot.ts +3 -1
  674. package/src/utils/double-escape-close.ts +15 -1
  675. package/src/utils/exchanges.ts +36 -0
  676. package/src/utils/format.ts +19 -2
  677. package/src/utils/forward-dividend.ts +14 -0
  678. package/src/utils/fx-status.ts +6 -1
  679. package/src/utils/guards.ts +25 -0
  680. package/src/utils/inline-content-tokenizer.ts +22 -3
  681. package/src/utils/local-utc-offset.ts +37 -0
  682. package/src/utils/market-capitalization.ts +8 -0
  683. package/src/utils/one-shot.ts +73 -0
  684. package/src/utils/options.ts +37 -8
  685. package/src/utils/plugin-proxy-hosts.json +8 -1
  686. package/src/utils/utc-time.ts +12 -0
  687. package/src/plugins/builtin/credit-boards/pane.tsx +0 -30
  688. package/src/ui/pane-fill.ts +0 -11
  689. /package/src/plugins/{builtin/plugin-marketplace → marketplace}/activation.ts +0 -0
  690. /package/src/plugins/{builtin/plugin-marketplace → marketplace}/ids.ts +0 -0
  691. /package/src/plugins/{builtin/plugin-marketplace → marketplace}/packs.ts +0 -0
@@ -5,7 +5,18 @@ import { colors } from "../../../../theme/colors";
5
5
  import type { ChatUserSummary, PublicPortfolioAnalytics } from "../../../../api-client";
6
6
  import { displayWidth, formatNumber, truncateToDisplayWidth } from "../../../../utils/format";
7
7
  import { truncateWithEllipsis } from "../../../../utils/text-wrap";
8
- import { isDiscordGhost } from "../ghost-user";
8
+ import { t } from "../../../../i18n";
9
+ import { isDiscordGhost, isManagedBot } from "../ghost-user";
10
+ import {
11
+ CHAT_PERSON_ICON_WIDTH,
12
+ chatAuthorNameColor,
13
+ chatPersonFacts,
14
+ chatPersonIcons,
15
+ fitChatPersonIcons,
16
+ type ChatPersonFacts,
17
+ } from "../people";
18
+ import { ChatPersonIcons } from "./person-icons";
19
+ import { localUtcOffsetMinutes } from "../../../../utils/local-utc-offset";
9
20
  import type { ChatUserAnchor } from "./types";
10
21
 
11
22
  /**
@@ -25,6 +36,8 @@ function hasPortfolioAnalytics(analytics: PublicPortfolioAnalytics | null | unde
25
36
  }
26
37
 
27
38
  export function hasPublicChatProfileInfo(user: ChatUserSummary): boolean {
39
+ // Gloom runs a managed assistant and says so on its card, whatever the profile switch holds.
40
+ if (isManagedBot(user)) return true;
28
41
  if (user.profilePublic === false || isDiscordGhost(user)) return false;
29
42
  return Boolean(user.bio?.trim() || user.title?.trim() || user.company?.trim() || hasPortfolioAnalytics(user.portfolioAnalytics));
30
43
  }
@@ -144,21 +157,73 @@ function nameCardRow(user: ChatUserSummary): { handle: string; name: string | nu
144
157
 
145
158
  function nameCardNaturalWidth(user: ChatUserSummary): number {
146
159
  const { handle, name } = nameCardRow(user);
147
- return displayWidth(handle) + (name ? 1 + displayWidth(name) : 0);
160
+ return displayWidth(handle) + chatPersonIcons(user).length * CHAT_PERSON_ICON_WIDTH + (name ? 1 + displayWidth(name) : 0);
161
+ }
162
+
163
+ /**
164
+ * The handle in the name's colour, then the person's icons, Team and Pro
165
+ * included without a pointer on anything: the line below says what each one
166
+ * is. A handle too long for them keeps its room and the icons go first.
167
+ */
168
+ function CardHandle({ user, handle, width }: { user: ChatUserSummary; handle: string; width: number }) {
169
+ const icons = fitChatPersonIcons(chatPersonIcons(user), width - displayWidth(handle));
170
+ return (
171
+ <>
172
+ <Text fg={chatAuthorNameColor(user)} attributes={TextAttributes.BOLD}>
173
+ {truncateToDisplayWidth(handle, Math.max(1, width - icons.length * CHAT_PERSON_ICON_WIDTH))}
174
+ </Text>
175
+ <ChatPersonIcons icons={icons} />
176
+ </>
177
+ );
148
178
  }
149
179
 
150
180
  function NameCardRow({ user, width }: { user: ChatUserSummary; width: number }) {
151
181
  const { handle, name } = nameCardRow(user);
152
- const handleText = truncateToDisplayWidth(handle, width);
153
- const nameWidth = width - displayWidth(handleText) - 1;
182
+ const handleWidth = Math.min(width, displayWidth(handle) + chatPersonIcons(user).length * CHAT_PERSON_ICON_WIDTH);
183
+ const nameWidth = width - handleWidth - 1;
154
184
  return (
155
185
  <Box height={1} width={width} flexDirection="row">
156
- <Text fg={colors.positive} attributes={TextAttributes.BOLD}>{handleText}</Text>
186
+ <CardHandle user={user} handle={handle} width={handleWidth} />
157
187
  {name && nameWidth >= 4 ? <Text fg={colors.textDim}>{` ${truncateToDisplayWidth(name, nameWidth)}`}</Text> : null}
158
188
  </Box>
159
189
  );
160
190
  }
161
191
 
192
+ /**
193
+ * The card's lines about who someone is beyond their profile: their role and
194
+ * Pro, their local time and what they trade. Each is one short line, or
195
+ * nothing. The keyboard reaches them too: `p` on a message opens this card.
196
+ */
197
+ function personFactRows(facts: ChatPersonFacts): Array<{ id: string; label: string | null; value: string }> {
198
+ return [
199
+ ...(facts.roles.length > 0 ? [{ id: "role", label: null, value: facts.roles.map((role) => t(role)).join(" · ") }] : []),
200
+ ...(facts.localTime ? [{ id: "local-time", label: t("Local time"), value: facts.localTime }] : []),
201
+ ...(facts.trades.length > 0 ? [{ id: "trades", label: t("Trades"), value: facts.trades.map((desk) => t(desk)).join(", ") }] : []),
202
+ ];
203
+ }
204
+
205
+ function personFactRowWidth(row: { label: string | null; value: string }): number {
206
+ return (row.label ? displayWidth(row.label) + 1 : 0) + displayWidth(row.value);
207
+ }
208
+
209
+ function PersonFactRows({ rows, width }: { rows: ReturnType<typeof personFactRows>; width: number }) {
210
+ return (
211
+ <>
212
+ {rows.map((row) => {
213
+ const labelText = row.label ? truncateToDisplayWidth(row.label, Math.max(1, width - 2)) : "";
214
+ const valueWidth = Math.max(1, width - (labelText ? displayWidth(labelText) + 1 : 0));
215
+ // A long list of desks wraps under itself rather than losing its end.
216
+ return (
217
+ <Box key={row.id} width={width} flexDirection="row" data-gloom-role={`chat-profile-${row.id}`}>
218
+ {labelText ? <Text fg={colors.textDim}>{`${labelText} `}</Text> : null}
219
+ <Text fg={row.label ? colors.text : colors.textDim} wrapText width={valueWidth}>{row.value}</Text>
220
+ </Box>
221
+ );
222
+ })}
223
+ </>
224
+ );
225
+ }
226
+
162
227
  export function UserProfilePopover({
163
228
  user,
164
229
  anchor = null,
@@ -202,11 +267,13 @@ export function UserProfilePopover({
202
267
  const maxPopoverWidth = Math.max(24, Math.min(38, width - 4));
203
268
  // Anyone without a public profile still gets a card: the name and a way to write to them.
204
269
  const nameOnly = !isOwnProfile && !hasPublicChatProfileInfo(user);
270
+ const factRows = personFactRows(chatPersonFacts(user, Date.now(), localUtcOffsetMinutes()));
205
271
  const popoverWidth = nameOnly
206
272
  ? Math.min(maxPopoverWidth, Math.max(
207
273
  NAME_CARD_MIN_WIDTH,
208
274
  nameCardNaturalWidth(user),
209
275
  messageRefusal ? displayWidth(messageRefusal) : 0,
276
+ ...factRows.map(personFactRowWidth),
210
277
  ) + chromeWidth)
211
278
  : maxPopoverWidth;
212
279
  const meta = [user.title, user.company].filter(Boolean).join(" · ");
@@ -224,6 +291,7 @@ export function UserProfilePopover({
224
291
  const content = nameOnly ? (
225
292
  <>
226
293
  <NameCardRow user={user} width={headerWidth} />
294
+ <PersonFactRows rows={factRows} width={headerWidth} />
227
295
  {messageAction ? (
228
296
  <Button label={messageAction.label} width={headerWidth} variant="ghost" compact stopPropagation onPress={messageAction.onPress} />
229
297
  ) : messageRefusal ? (
@@ -233,10 +301,8 @@ export function UserProfilePopover({
233
301
  ) : (
234
302
  <>
235
303
  <Box height={1} width={headerWidth} flexDirection="row">
236
- <Box width={usernameWidth}>
237
- <Text fg={colors.positive} attributes={TextAttributes.BOLD}>
238
- {truncateWithEllipsis(user.username ? `@${user.username}` : user.displayName, usernameWidth)}
239
- </Text>
304
+ <Box width={usernameWidth} flexDirection="row">
305
+ <CardHandle user={user} handle={user.username ? `@${user.username}` : user.displayName} width={usernameWidth} />
240
306
  </Box>
241
307
  {statsWidth > 0 ? (
242
308
  <>
@@ -246,7 +312,9 @@ export function UserProfilePopover({
246
312
  ) : null}
247
313
  <Box flexGrow={1} />
248
314
  </Box>
315
+ <PersonFactRows rows={factRows.filter((row) => row.id === "role")} width={headerWidth} />
249
316
  {meta ? <Text fg={colors.textDim}>{truncateWithEllipsis(meta, headerWidth)}</Text> : null}
317
+ <PersonFactRows rows={factRows.filter((row) => row.id !== "role")} width={headerWidth} />
250
318
  {bio ? (
251
319
  <Text fg={colors.text} wrapText width={headerWidth}>
252
320
  {bio}
@@ -0,0 +1,210 @@
1
+ import { useState } from "react";
2
+ import type { ChatMessage, ChatReaction } from "../../../../api-client";
3
+ import { Button, MenuPopover, type MenuItem } from "../../../../components/ui";
4
+ import { colors } from "../../../../theme/colors";
5
+ import { t } from "../../../../i18n";
6
+ import { Box, Text } from "../../../../ui";
7
+ import { displayWidth, truncateToDisplayWidth } from "../../../../utils/format";
8
+ import {
9
+ isCustomReactionEmoji,
10
+ reactedByText,
11
+ reactionChipText,
12
+ reactionPickerChoices,
13
+ terminalReactionChipWidth,
14
+ terminalReactionGlyph,
15
+ terminalReactionRows,
16
+ } from "../reactions";
17
+
18
+ interface ChatReactionChipsProps {
19
+ msg: ChatMessage;
20
+ currentUserId: string | null;
21
+ /** Signed in, and the server takes reactions: a chip toggles your reaction. */
22
+ canReact: boolean;
23
+ onToggle: (messageId: string, emoji: string) => void;
24
+ }
25
+
26
+ function chipLabel(reaction: ChatReaction, currentUserId: string | null): string {
27
+ const who = reactedByText(reaction, currentUserId);
28
+ return who ? `${reaction.emoji} ${reaction.count}: ${who}` : `${reaction.emoji} ${reaction.count}`;
29
+ }
30
+
31
+ function chipPress(
32
+ props: ChatReactionChipsProps,
33
+ reaction: ChatReaction,
34
+ ): (() => void) | undefined {
35
+ // A custom Discord emoji is added and taken back on Discord only.
36
+ if (!props.canReact || isCustomReactionEmoji(reaction.emoji)) return undefined;
37
+ return () => props.onToggle(props.msg.id, reaction.emoji);
38
+ }
39
+
40
+ /**
41
+ * The reactions under a terminal message: a row of chips (wrapping when the
42
+ * body is narrow), yours filled. Hovering a chip names who reacted in a
43
+ * one-line tip just above it, over the message's own last line (drawn before
44
+ * the chips, so the tip stays on top).
45
+ */
46
+ export function TerminalChatReactions({
47
+ rowProps,
48
+ width,
49
+ ...props
50
+ }: ChatReactionChipsProps & {
51
+ /** Host row props: width, background and hover or selection hooks. */
52
+ rowProps: Record<string, unknown>;
53
+ /** Cells the body has. */
54
+ width: number;
55
+ }) {
56
+ const [hovered, setHovered] = useState<string | null>(null);
57
+ const reactions = props.msg.reactions ?? [];
58
+ if (reactions.length === 0) return null;
59
+ const rows = terminalReactionRows(reactions, width);
60
+ return (
61
+ <>
62
+ {rows.map((row, rowIndex) => {
63
+ let offset = 0;
64
+ const offsets = row.map((reaction, index) => {
65
+ const at = offset + (index > 0 ? 1 : 0);
66
+ offset = at + terminalReactionChipWidth(reaction);
67
+ return at;
68
+ });
69
+ const hoveredIndex = row.findIndex((reaction) => reaction.emoji === hovered);
70
+ const hoveredReaction = hoveredIndex >= 0 ? row[hoveredIndex]! : null;
71
+ const who = hoveredReaction ? reactedByText(hoveredReaction, props.currentUserId) : "";
72
+ const glyph = hoveredReaction ? terminalReactionGlyph(hoveredReaction.emoji) : null;
73
+ const tipText = glyph && who ? truncateToDisplayWidth(who, Math.max(1, width - glyph.width - 3)) : "";
74
+ const tipWidth = glyph && tipText ? glyph.width + displayWidth(tipText) + 3 : 0;
75
+ const tipLeft = hoveredIndex >= 0 ? Math.max(0, Math.min(offsets[hoveredIndex]!, width - tipWidth)) : 0;
76
+ return (
77
+ <Box
78
+ key={`${props.msg.id}:reactions:${rowIndex}`}
79
+ {...rowProps}
80
+ paddingLeft={3}
81
+ height={1}
82
+ flexDirection="row"
83
+ position="relative"
84
+ data-gloom-role="chat-message-reactions"
85
+ >
86
+ {row.map((reaction, index) => (
87
+ <Box
88
+ key={reaction.emoji}
89
+ flexDirection="row"
90
+ height={1}
91
+ onMouseOver={() => setHovered(reaction.emoji)}
92
+ onMouseOut={() => setHovered((current) => (current === reaction.emoji ? null : current))}
93
+ >
94
+ {index > 0 && <Text> </Text>}
95
+ <Button
96
+ label={chipLabel(reaction, props.currentUserId)}
97
+ displayLabel={reactionChipText(reaction, "terminal")}
98
+ variant={reaction.me ? "primary" : "secondary"}
99
+ stopPropagation
100
+ onPress={chipPress(props, reaction)}
101
+ />
102
+ </Box>
103
+ ))}
104
+ {tipWidth > 0 && glyph && (
105
+ <Box
106
+ position="absolute"
107
+ top={-1}
108
+ left={3 + tipLeft}
109
+ width={tipWidth}
110
+ height={1}
111
+ flexDirection="row"
112
+ // Inverted, so the tip reads as one over the chips and text it covers.
113
+ backgroundColor={colors.text}
114
+ style={{ zIndex: 5 }}
115
+ >
116
+ <Text fg={colors.bg}>{" "}</Text>
117
+ <Text fg={colors.bg}>{glyph.text}</Text>
118
+ <Text fg={colors.bg}>{` ${tipText} `}</Text>
119
+ </Box>
120
+ )}
121
+ </Box>
122
+ );
123
+ })}
124
+ </>
125
+ );
126
+ }
127
+
128
+ /** The reactions under a desktop message, wrapping with the width; each chip's tooltip names who reacted. */
129
+ export function DesktopChatReactions(props: ChatReactionChipsProps) {
130
+ const reactions = props.msg.reactions ?? [];
131
+ if (reactions.length === 0) return null;
132
+ return (
133
+ <Box
134
+ flexDirection="row"
135
+ data-gloom-role="chat-message-reactions"
136
+ style={{ flexWrap: "wrap", gap: 4, minWidth: 0, paddingTop: 2, paddingBottom: 4 }}
137
+ >
138
+ {reactions.map((reaction) => (
139
+ <Button
140
+ key={reaction.emoji}
141
+ label={chipLabel(reaction, props.currentUserId)}
142
+ displayLabel={reactionChipText(reaction, "desktop")}
143
+ title={reactedByText(reaction, props.currentUserId) || undefined}
144
+ // Yours carries the focus outline, which reads on a selected row too.
145
+ active={reaction.me}
146
+ stopPropagation
147
+ onPress={chipPress(props, reaction)}
148
+ />
149
+ ))}
150
+ </Box>
151
+ );
152
+ }
153
+
154
+ /** The picker's entries: each emoji, ticked when you reacted, with its count and who reacted. */
155
+ function reactionMenuItems(msg: ChatMessage, currentUserId: string | null): MenuItem[] {
156
+ return reactionPickerChoices(msg).map((reaction) => ({
157
+ id: reaction.emoji,
158
+ label: reaction.emoji,
159
+ checked: reaction.me,
160
+ ...(reaction.count > 0
161
+ ? { hint: String(reaction.count), description: reactedByText(reaction, currentUserId) }
162
+ : {}),
163
+ }));
164
+ }
165
+
166
+ /**
167
+ * The React action on the desktop: a button in the message's actions that
168
+ * opens the picker beside it (also from the keyboard, on the selected message).
169
+ */
170
+ export function DesktopReactionAction({
171
+ msg,
172
+ currentUserId,
173
+ open,
174
+ width,
175
+ variant,
176
+ onOpenChange,
177
+ onToggle,
178
+ }: {
179
+ msg: ChatMessage;
180
+ currentUserId: string | null;
181
+ open: boolean;
182
+ width: number;
183
+ variant: "primary" | "secondary";
184
+ onOpenChange: (open: boolean) => void;
185
+ onToggle: (messageId: string, emoji: string) => void;
186
+ }) {
187
+ const trigger = (
188
+ <Button
189
+ stopPropagation
190
+ label={t("React")}
191
+ width={width}
192
+ variant={variant}
193
+ expanded={open}
194
+ onPress={() => onOpenChange(!open)}
195
+ />
196
+ );
197
+ if (!open) return trigger;
198
+ return (
199
+ <MenuPopover
200
+ open
201
+ onOpenChange={onOpenChange}
202
+ trigger={trigger}
203
+ placement="bottom-end"
204
+ minWidth={150}
205
+ label={t("React")}
206
+ items={reactionMenuItems(msg, currentUserId)}
207
+ onSelect={(emoji) => onToggle(msg.id, emoji)}
208
+ />
209
+ );
210
+ }
@@ -1,17 +1,36 @@
1
1
  import { TextAttributes } from "../../../../ui";
2
- import { colors, hoverBg } from "../../../../theme/colors";
2
+ import { blendHex, colors, hoverBg } from "../../../../theme/colors";
3
3
  import type { ChatMessage } from "../../../../api-client";
4
4
  import { formatTimeAgo } from "../../../../utils/datetime-format";
5
- import { isGroupedWithPrevious } from "../layout";
5
+ import { isGroupedWithPrevious, MESSAGE_ACTION_WIDTH } from "../layout";
6
+ import { chatAuthorNameColor } from "../people";
7
+
8
+ /** The band on a message the reader was sent to: the accent at low strength, between the hover and the selection. */
9
+ export function chatHighlightBg(palette = colors): string {
10
+ return blendHex(palette.bg, palette.borderFocused, 0.22);
11
+ }
6
12
 
7
13
  export interface ChatMessageRenderState {
8
14
  isSelected: boolean;
9
15
  isHovered: boolean;
16
+ isHighlighted: boolean;
10
17
  grouped: boolean;
11
18
  showReplyAction: boolean;
19
+ /** Reactions are on offer here: the server takes them and the message can have them. */
20
+ showReactAction: boolean;
12
21
  showEditAction: boolean;
13
22
  /** A failed send of yours offers Retry in place of Reply and Edit. */
14
23
  showRetryAction: boolean;
24
+ /**
25
+ * Cells the header's actions take whenever they show, hovered or not, so a
26
+ * narrow row fits its icons once instead of again on every hover.
27
+ */
28
+ actionsWidth: number;
29
+ /**
30
+ * The Team and Pro icons show: the row is under the pointer or selected.
31
+ * A bot's icon shows always.
32
+ */
33
+ showPersonIcons: boolean;
15
34
  /** Still pending past the slow threshold: drawn dim. */
16
35
  isSending: boolean;
17
36
  bgColor: string | undefined;
@@ -33,8 +52,10 @@ export function getChatMessageRenderState({
33
52
  messages,
34
53
  selectedIdx,
35
54
  hoveredIdx,
55
+ highlighted = false,
36
56
  canSend,
37
57
  canEdit,
58
+ canReact = false,
38
59
  slowSend,
39
60
  host,
40
61
  }: {
@@ -43,8 +64,11 @@ export function getChatMessageRenderState({
43
64
  messages: ChatMessage[];
44
65
  selectedIdx: number;
45
66
  hoveredIdx: number | null;
67
+ highlighted?: boolean;
46
68
  canSend: boolean;
47
69
  canEdit: boolean;
70
+ /** The message takes a reaction from you. */
71
+ canReact?: boolean;
48
72
  /** A pending send that has waited past the threshold; a fresh one is drawn like a sent message. */
49
73
  slowSend: boolean;
50
74
  /** The desktop reveals actions on hover with CSS, so it renders them on every row. */
@@ -52,6 +76,7 @@ export function getChatMessageRenderState({
52
76
  }): ChatMessageRenderState {
53
77
  const isSelected = index === selectedIdx;
54
78
  const isHovered = index === hoveredIdx && !isSelected;
79
+ const isHighlighted = highlighted && !isSelected;
55
80
  const grouped = isGroupedWithPrevious(messages, index);
56
81
  const isSending = msg.clientStatus === "sending" && slowSend;
57
82
  const hasFailed = msg.clientStatus === "failed";
@@ -67,12 +92,16 @@ export function getChatMessageRenderState({
67
92
  return {
68
93
  isSelected,
69
94
  isHovered,
95
+ isHighlighted,
70
96
  grouped,
71
97
  showReplyAction,
98
+ showReactAction: showReplyAction && canReact,
72
99
  showEditAction: showReplyAction && canEdit,
73
100
  showRetryAction: showActions && hasFailed,
101
+ actionsWidth: !canSend ? 0 : 1 + MESSAGE_ACTION_WIDTH * (hasFailed ? 1 : 1 + (canReact ? 1 : 0) + (canEdit ? 1 : 0)),
102
+ showPersonIcons: isSelected || hoveredIdx === index,
74
103
  isSending,
75
- bgColor: isSelected ? colors.selected : isHovered ? hoverBg() : undefined,
104
+ bgColor: isSelected ? colors.selected : isHighlighted ? chatHighlightBg() : isHovered ? hoverBg() : undefined,
76
105
  selectedTextColor,
77
106
  replyMetaColor: isSelected ? selectedTextColor : colors.textMuted,
78
107
  replyAuthorColor: isSelected ? selectedTextColor : colors.textDim,
@@ -85,7 +114,7 @@ export function getChatMessageRenderState({
85
114
  ? colors.negative
86
115
  : colors.textMuted,
87
116
  originTagColor: isSelected ? selectedTextColor : colors.textDim,
88
- authorColor: isSelected ? selectedTextColor : hasFailed ? colors.negative : colors.positive,
117
+ authorColor: isSelected ? selectedTextColor : hasFailed ? colors.negative : chatAuthorNameColor(msg.user),
89
118
  authorAttributes: (isSending ? TextAttributes.DIM : 0) | TextAttributes.BOLD,
90
119
  bodyColor: isSelected ? selectedTextColor : hasFailed ? colors.negative : isSending ? colors.textDim : colors.text,
91
120
  };
@@ -1,10 +1,18 @@
1
1
  import { Box, Text, TextAttributes, useRendererHost } from "../../../../ui";
2
2
  import { colors } from "../../../../theme/colors";
3
3
  import { t } from "../../../../i18n";
4
- import { formatInlinePreview, getMessageBodyTokenLines } from "../layout";
4
+ import { formatInlinePreview, getMessageBodyTokenLines, MESSAGE_ACTION_WIDTH } from "../layout";
5
5
  import { chatImageLabel, chatImageReviewNote, chatReplyQuoteText } from "../attachments/model";
6
+ import { isQuotedAuthorManagedBot } from "../ghost-user";
7
+ import { ChatBotIcon } from "./person-icons";
8
+ import { CHAT_PERSON_ICON_WIDTH } from "../people";
6
9
  import { ChatMessageActions, ChatMessageHeader } from "./header";
10
+ import { Button } from "../../../../components/ui";
11
+ import { canReactToMessage } from "../reactions";
12
+ import { TerminalChatReactions } from "./reactions";
7
13
  import { ResponsiveTickerBadgeText } from "./inline-tokens";
14
+ import { TerminalChatPaneChip } from "./pane-chip";
15
+ import { chatMessagePaneLinks } from "../pane-links/model";
8
16
  import { useSlowPendingSend } from "./pending-send";
9
17
  import { getChatMessageRenderState } from "./render-state";
10
18
  import type { ChatMessageBaseProps } from "./types";
@@ -21,6 +29,7 @@ export function TerminalChatMessage({
21
29
  messages,
22
30
  selectedIdx,
23
31
  hoveredIdx,
32
+ highlighted,
24
33
  canSend,
25
34
  contentWidth,
26
35
  messageBodyWidth,
@@ -37,28 +46,47 @@ export function TerminalChatMessage({
37
46
  latestEditableMessageId,
38
47
  retryMessage,
39
48
  setHoveredIdx,
49
+ reactionsEnabled = false,
50
+ currentUserId = null,
51
+ toggleReaction,
52
+ openReactionPicker,
53
+ openPaneLink,
40
54
  }: TerminalChatMessageProps) {
41
55
  const rendererHost = useRendererHost();
42
56
  const slowSend = useSlowPendingSend(msg);
57
+ const canReact = reactionsEnabled && canReactToMessage(msg);
43
58
  const state = getChatMessageRenderState({
44
59
  msg,
45
60
  index,
46
61
  messages,
47
62
  selectedIdx,
48
63
  hoveredIdx,
64
+ highlighted,
49
65
  canSend,
50
66
  canEdit: msg.id === latestEditableMessageId,
67
+ canReact: canReact && !!openReactionPicker,
51
68
  slowSend,
52
69
  host: "terminal",
53
70
  });
54
- const actionProps = { state, index, beginReplyTo, beginEditMessage, retryMessage };
71
+ const reactAction = openReactionPicker ? (
72
+ <Button stopPropagation
73
+ label={t("React")}
74
+ width={MESSAGE_ACTION_WIDTH}
75
+ variant={state.isSelected ? "primary" : "secondary"}
76
+ onPress={() => openReactionPicker(index)}
77
+ />
78
+ ) : null;
79
+ const actionProps = { state, index, beginReplyTo, beginEditMessage, retryMessage, reactAction };
55
80
  const attachments = msg.attachments ?? [];
81
+ // A share link is its chip's row; a message that was only one has no text row.
82
+ const { links: paneLinks, content } = chatMessagePaneLinks(msg);
56
83
  // An image-only message has no text row; its image rows start where the text would.
57
- const bodyLines = msg.content.length > 0 || attachments.length === 0
58
- ? getMessageBodyTokenLines(msg.content, messageBodyWidth, catalog)
84
+ const bodyLines = content.length > 0 || (attachments.length === 0 && paneLinks.length === 0)
85
+ ? getMessageBodyTokenLines(content, messageBodyWidth, catalog)
59
86
  : [];
60
87
  const reviewNote = chatImageReviewNote(msg);
61
88
  const imageColor = state.isSelected ? state.selectedTextColor : colors.textDim;
89
+ const quotedAuthorIsBot = !!msg.replyTo && isQuotedAuthorManagedBot(msg.replyTo, userByUsername);
62
90
  const setHovered = () => setHoveredIdx((current) => (current === index ? current : index));
63
91
  const clearHovered = () => setHoveredIdx((current) => (current === index ? null : current));
64
92
  const messageRowProps = {
@@ -81,11 +109,20 @@ export function TerminalChatMessage({
81
109
  }}
82
110
  >
83
111
  <Text fg={state.replyMetaColor}>{`${t("reply")} `}</Text>
84
- <Text fg={state.replyAuthorColor}>{msg.replyTo.user.username}: </Text>
112
+ {quotedAuthorIsBot ? (
113
+ // The bot's icon stands where the colon would.
114
+ <>
115
+ <Text fg={state.replyAuthorColor}>{msg.replyTo.user.username}</Text>
116
+ <ChatBotIcon selectedColor={state.isSelected ? state.selectedTextColor : undefined} />
117
+ <Text fg={state.replyAuthorColor}> </Text>
118
+ </>
119
+ ) : (
120
+ <Text fg={state.replyAuthorColor}>{msg.replyTo.user.username}: </Text>
121
+ )}
85
122
  <Text fg={state.replyMetaColor}>
86
123
  {formatInlinePreview(
87
124
  chatReplyQuoteText(msg.replyTo),
88
- Math.max(messageBodyWidth - `reply ${msg.replyTo.user.username}: `.length, 0),
125
+ Math.max(messageBodyWidth - `reply ${msg.replyTo.user.username}`.length - (quotedAuthorIsBot ? CHAT_PERSON_ICON_WIDTH + 1 : 2), 0),
89
126
  )}
90
127
  </Text>
91
128
  </Box>
@@ -128,6 +165,25 @@ export function TerminalChatMessage({
128
165
  {lineIndex === 0 && state.grouped && <ChatMessageActions floating {...actionProps} />}
129
166
  </Box>
130
167
  ))}
168
+ {paneLinks.map((link, linkIndex) => (
169
+ <Box
170
+ key={`${msg.id}:pane:${link.kind === "share" ? link.id : link.link.kind}`}
171
+ {...messageRowProps}
172
+ paddingLeft={3}
173
+ height={1}
174
+ flexDirection="row"
175
+ position={state.grouped && bodyLines.length === 0 && linkIndex === 0 ? "relative" : undefined}
176
+ >
177
+ <TerminalChatPaneChip
178
+ link={link}
179
+ width={messageBodyWidth}
180
+ selected={state.isSelected}
181
+ selectedTextColor={state.selectedTextColor}
182
+ onOpen={openPaneLink}
183
+ />
184
+ {state.grouped && bodyLines.length === 0 && linkIndex === 0 && <ChatMessageActions floating {...actionProps} />}
185
+ </Box>
186
+ ))}
131
187
  {attachments.map((attachment, imageIndex) => (
132
188
  <Box
133
189
  key={`${msg.id}:image:${attachment.id}`}
@@ -135,7 +191,7 @@ export function TerminalChatMessage({
135
191
  paddingLeft={3}
136
192
  height={1}
137
193
  flexDirection="row"
138
- position={state.grouped && bodyLines.length === 0 && imageIndex === 0 ? "relative" : undefined}
194
+ position={state.grouped && bodyLines.length === 0 && paneLinks.length === 0 && imageIndex === 0 ? "relative" : undefined}
139
195
  >
140
196
  <Box
141
197
  height={1}
@@ -149,7 +205,7 @@ export function TerminalChatMessage({
149
205
  {formatInlinePreview(`[${chatImageLabel(attachment)}]`, messageBodyWidth)}
150
206
  </Text>
151
207
  </Box>
152
- {state.grouped && bodyLines.length === 0 && imageIndex === 0 && <ChatMessageActions floating {...actionProps} />}
208
+ {state.grouped && bodyLines.length === 0 && paneLinks.length === 0 && imageIndex === 0 && <ChatMessageActions floating {...actionProps} />}
153
209
  </Box>
154
210
  ))}
155
211
  {reviewNote && (
@@ -159,6 +215,14 @@ export function TerminalChatMessage({
159
215
  </Text>
160
216
  </Box>
161
217
  )}
218
+ <TerminalChatReactions
219
+ msg={msg}
220
+ currentUserId={currentUserId}
221
+ canReact={canReact && !!toggleReaction}
222
+ onToggle={toggleReaction ?? (() => {})}
223
+ rowProps={messageRowProps}
224
+ width={messageBodyWidth}
225
+ />
162
226
  </Box>
163
227
  );
164
228
  }
@@ -1,6 +1,7 @@
1
1
  import type { InlineTickerCatalogEntry } from "../../../../state/hooks/inline-tickers";
2
2
  import type { ChatAttachment, ChatMessage, ChatUserSummary } from "../../../../api-client";
3
3
  import type { BoxRenderable } from "../../../../ui";
4
+ import type { ChatMessagePaneLink } from "../pane-links/model";
4
5
 
5
6
  /** The name or @mention a user's card opens beside, where the renderer places cards next to it. */
6
7
  export type ChatUserAnchor = BoxRenderable | null;
@@ -17,6 +18,8 @@ export interface ChatMessageBaseProps {
17
18
  messages: ChatMessage[];
18
19
  selectedIdx: number;
19
20
  hoveredIdx: number | null;
21
+ /** A message the reader was sent to, marked for a moment. */
22
+ highlighted?: boolean;
20
23
  canSend: boolean;
21
24
  catalog: Record<string, InlineTickerCatalogEntry>;
22
25
  userByUsername: Map<string, ChatUserSummary>;
@@ -34,4 +37,16 @@ export interface ChatMessageBaseProps {
34
37
  retryMessage?: (index: number) => void;
35
38
  /** An image did not load, perhaps because its signed link expired. */
36
39
  onImageLoadError?: (attachment: ChatAttachment) => void;
40
+ /** The server takes reactions and you can send: chips toggle and React is on offer. */
41
+ reactionsEnabled?: boolean;
42
+ currentUserId?: string | null;
43
+ /** Adds or takes back your reaction with one emoji. */
44
+ toggleReaction?: (messageId: string, emoji: string) => void;
45
+ /** Opens the reaction picker for a message. */
46
+ openReactionPicker?: (index: number) => void;
47
+ /** The message whose picker is open on the desktop. */
48
+ reactionPickerMessageId?: string | null;
49
+ closeReactionPicker?: () => void;
50
+ /** Opens what a chip under the message stands for: a shared pane, a Wire post's story or calendar. */
51
+ openPaneLink: (link: ChatMessagePaneLink) => void;
37
52
  }