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
@@ -1,4 +1,5 @@
1
1
  import { FINANCIAL_VINTAGE_NOTICE, SEC_EPS_BASIS_NOTICE } from "../../../utils/financial-statements";
2
+ import { usePaneSharePreparation } from "../../../shares/prepare";
2
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
4
  import { Box, useUiCapabilities } from "../../../ui";
4
5
  import {
@@ -43,7 +44,7 @@ import {
43
44
  type AppState,
44
45
  } from "../../../state/app/context";
45
46
  import { colors } from "../../../theme/colors";
46
- import { isUsListingExchange, parsePublicTickerKey, publicTickerKey, resolveExchangeTimeZone } from "../../../utils/exchanges";
47
+ import { isUsListingExchange, parsePublicTickerKey, publicTickerKey } from "../../../utils/exchanges";
47
48
  import { isMarketFieldId } from "../../../time-series/field-catalog";
48
49
  import { CHART_COMPOSER_PANE_ID } from "../../../types/config";
49
50
  import { useRemoteUiNode } from "../../../remote/semantic-tree";
@@ -104,6 +105,7 @@ import {
104
105
  } from "./settings";
105
106
  import { resolveChartComposerShortcut } from "./shortcuts";
106
107
  import { describeChartResolution, formatChartDateWindow, formatChartResolution } from "./viewport-labels";
108
+ import { intradaySessionTimeZone } from "./session-history";
107
109
  import { ChartSeriesQuickAdd } from "./quick-add";
108
110
  import { useLiveStreamingSetting } from "../../../state/hooks/live-streaming";
109
111
  import { usePluginAppActions, usePluginConfigState } from "../../runtime";
@@ -116,13 +118,14 @@ import { getActiveQuoteDisplay } from "../../../market-data/market/status";
116
118
  import { resolveEntryData } from "../../../market-data/selectors";
117
119
  import { editPriceLevels, parsePriceLevels, PRICE_LEVELS_KEY, priceLevelTickerKey } from "./price-levels";
118
120
  import { canonicalExchange } from "../../../utils/exchanges";
119
- import { isPlainKey } from "../../../utils/keyboard";
120
121
  import { resolveInstrumentForPane } from "../../../core/state/app/instrument";
121
122
  import { CHART_FOLLOW_SERIES_SETTING_KEY, rebindFollowChartSpec, resolveFollowSeriesIds } from "./follow-binding";
122
123
  import type { InstrumentRef } from "../../../market-data/request-types";
123
124
 
124
125
  const RANGE_OPTIONS = RANGES.map((range, index) => ({ label: range, value: range, hint: String(index + 1) }));
125
126
  const AUTO_VIEWPORT_DEBOUNCE_MS = 350;
127
+ /** A load for a moving view that has not answered in this long no longer holds the next one back. */
128
+ const LOAD_AHEAD_TIMEOUT_MS = 4_000;
126
129
  /**
127
130
  * Drawings persist into pane settings on a short debounce after the pointer
128
131
  * settles. A share reads those settings, so it waits at least that long for a
@@ -144,6 +147,22 @@ interface RuntimeChartViewportState {
144
147
  requestViewport: RuntimeChartViewport;
145
148
  }
146
149
 
150
+ /** The oldest and newest market bars loaded, and whether every listing has said it keeps nothing older. */
151
+ function loadedMarketHistory(series: readonly ResolvedSeries[]): { start: number; end: number; complete: boolean } | null {
152
+ let start = Number.POSITIVE_INFINITY;
153
+ let end = Number.NEGATIVE_INFINITY;
154
+ let complete = true;
155
+ for (const entry of series) {
156
+ if (entry.observationKind !== "market" || entry.points.length === 0) continue;
157
+ const first = entry.points[0]!.date.getTime();
158
+ const last = entry.points.at(-1)!.date.getTime();
159
+ if (Number.isFinite(first)) start = Math.min(start, first);
160
+ if (Number.isFinite(last)) end = Math.max(end, last);
161
+ if (!entry.historyStart) complete = false;
162
+ }
163
+ return Number.isFinite(start) && Number.isFinite(end) ? { start, end, complete } : null;
164
+ }
165
+
147
166
  function runtimeViewportFromSetting(
148
167
  setting: ChartInteractionViewport | null,
149
168
  authoredViewportKey: string,
@@ -258,7 +277,7 @@ function ChartComposerSurface({
258
277
  }: ChartComposerSurfaceProps) {
259
278
  const dialog = useDialog();
260
279
  const dispatch = useAppDispatch();
261
- const { publicSharing, cellWidthPx = 8 } = useUiCapabilities();
280
+ const { cellWidthPx = 8 } = useUiCapabilities();
262
281
  const desktopWeb = useUiCapabilities().nativePaneChrome === true;
263
282
  const paneId = usePaneInstanceId();
264
283
  const liveStreaming = useLiveStreamingSetting();
@@ -351,9 +370,15 @@ function ChartComposerSurface({
351
370
  spec.viewport.range,
352
371
  spec.viewport.resolution,
353
372
  ]);
373
+ // Auto names the bars it is on, which change as the view moves: a pan back
374
+ // past where a source keeps 30-minute bars charts days instead.
375
+ const autoBars = spec.viewport.resolution === "auto" ? resolution.resolution ?? null : null;
354
376
  const resolutionOptions = useMemo(
355
- () => availableResolutions.map((value) => ({ label: formatChartResolution(value), value })),
356
- [availableResolutions],
377
+ () => availableResolutions.map((value) => ({
378
+ label: value === "auto" && autoBars ? `Auto (${formatChartResolution(autoBars)})` : formatChartResolution(value),
379
+ value,
380
+ })),
381
+ [autoBars, availableResolutions],
357
382
  );
358
383
  const selectedStudies = getSelectedBuiltinStudies(spec);
359
384
  const selectedPairStudies = getSelectedPairStudies(spec);
@@ -396,7 +421,7 @@ function ChartComposerSurface({
396
421
  ),
397
422
  [resolution.bufferedSeries, resolution.legendSeries, resolution.series, spec],
398
423
  );
399
- const { sharePane, notify } = usePluginAppActions();
424
+ const { notify } = usePluginAppActions();
400
425
  const { runtime } = usePluginRenderContext();
401
426
  const [levelStore, setLevelStore] = usePluginConfigState<unknown>(PRICE_LEVELS_KEY, null);
402
427
  const alertsJson = useAppSelector((state) => state.config.pluginConfig[PRICE_ALERTS_STORE.pluginId]?.[PRICE_ALERTS_STORE.key]);
@@ -472,10 +497,6 @@ function ChartComposerSurface({
472
497
  ...(alertsOn ? { action: { label: "alert", title: "Alert at Level", run: (level) => { void alertAtLevel(level.value); } } } : {}),
473
498
  };
474
499
  }, [alertAtLevel, alertsJson, alertsOn, editLevels, levelStore, listing]);
475
- const shareTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
476
- useEffect(() => () => {
477
- if (shareTimerRef.current !== null) clearTimeout(shareTimerRef.current);
478
- }, []);
479
500
  const [interactionCaptured, setInteractionCapturedState] = useState(false);
480
501
  // Typing in quick-add must not freeze the plot: it only takes the keyboard.
481
502
  const [modalCaptured, setModalCaptured] = useState(false);
@@ -631,7 +652,19 @@ function ChartComposerSurface({
631
652
  const activatePane = useCallback(() => {
632
653
  if (!focused) dispatch({ type: "FOCUS_PANE", paneId });
633
654
  }, [dispatch, focused, paneId]);
655
+ useEffect(() => () => {
656
+ if (runtimeViewportTimerRef.current !== null) {
657
+ clearTimeout(runtimeViewportTimerRef.current);
658
+ runtimeViewportTimerRef.current = null;
659
+ }
660
+ }, []);
661
+ // The view this pane last saved, so its own write coming back through the
662
+ // settings is not mistaken for another device's view: that would cancel the
663
+ // load a pan still in progress has scheduled and put its window back.
664
+ const savedInteractionViewportRef = useRef<string | null>(null);
634
665
  useEffect(() => {
666
+ if (persistedInteractionViewport && requestViewportOwnerRef.current === authoredViewportKey
667
+ && JSON.stringify(persistedInteractionViewport) === savedInteractionViewportRef.current) return;
635
668
  if (runtimeViewportTimerRef.current !== null) {
636
669
  clearTimeout(runtimeViewportTimerRef.current);
637
670
  runtimeViewportTimerRef.current = null;
@@ -645,30 +678,41 @@ function ChartComposerSurface({
645
678
  requestViewportOwnerRef.current = authoredViewportKey;
646
679
  setRuntimeViewportState((current) => current?.key === authoredViewportKey ? current : restored);
647
680
  if (persistedInteractionViewport && !restored) setStoredInteractionViewport(null);
648
- return () => {
649
- if (runtimeViewportTimerRef.current !== null) {
650
- clearTimeout(runtimeViewportTimerRef.current);
651
- runtimeViewportTimerRef.current = null;
652
- }
653
- };
654
681
  }, [authoredViewportKey, persistedInteractionViewport, setStoredInteractionViewport]);
655
- const commitRuntimeViewport = useCallback(() => {
682
+ // The bars loaded so far, and whether the source has said nothing older exists.
683
+ const loadedHistory = useMemo(() => loadedMarketHistory(plottedSeries), [plottedSeries]);
684
+ const loadedHistoryRef = useRef(loadedHistory);
685
+ loadedHistoryRef.current = loadedHistory;
686
+ // The newest bar seen at all: bars loaded for a moved view (finer ones, after
687
+ // a zoom) can end before it, and a pan forward then has more to ask for.
688
+ const newestBarRef = useRef(Number.NEGATIVE_INFINITY);
689
+ if (loadedHistory) newestBarRef.current = Math.max(newestBarRef.current, loadedHistory.end);
690
+ // A window asked for and not answered yet: the result's view ends where the request does once it lands.
691
+ const loadingWindowRef = useRef<{ end: number; at: number } | null>(null);
692
+ const resultViewportEnd = resolution.viewport?.end.getTime() ?? null;
693
+ if (loadingWindowRef.current && resultViewportEnd !== null
694
+ && Math.abs(resultViewportEnd - loadingWindowRef.current.end) < 1_000) loadingWindowRef.current = null;
695
+ const commitRuntimeViewport = useCallback((save = true) => {
656
696
  const requestViewport = requestViewportRef.current;
657
697
  if (!requestViewport) return;
658
698
  const adaptiveViewport = spec.viewport.resolution === "auto"
659
699
  ? adaptiveViewportRef.current
660
700
  : null;
701
+ loadingWindowRef.current = { end: requestViewport.end.getTime(), at: Date.now() };
661
702
  setRuntimeViewportState({
662
703
  key: authoredViewportKey,
663
704
  adaptiveViewport,
664
705
  requestViewport,
665
706
  });
666
- setStoredInteractionViewport({
707
+ if (!save) return;
708
+ const saved = {
667
709
  authoredViewportKey,
668
710
  start: requestViewport.start.toISOString(),
669
711
  end: requestViewport.end.toISOString(),
670
712
  adaptive: adaptiveViewport !== null,
671
- } satisfies ChartInteractionViewport);
713
+ } satisfies ChartInteractionViewport;
714
+ savedInteractionViewportRef.current = JSON.stringify(parseChartInteractionViewport(saved));
715
+ setStoredInteractionViewport(saved);
672
716
  }, [authoredViewportKey, setStoredInteractionViewport, spec.viewport.resolution]);
673
717
  const handleChartViewportChange = useCallback((
674
718
  next: { start: Date; end: Date } | null,
@@ -681,6 +725,7 @@ function ChartComposerSurface({
681
725
  if (!next) {
682
726
  adaptiveViewportRef.current = null;
683
727
  requestViewportRef.current = null;
728
+ loadingWindowRef.current = null;
684
729
  setRuntimeViewportState(null);
685
730
  setStoredInteractionViewport(null);
686
731
  return;
@@ -694,24 +739,31 @@ function ChartComposerSurface({
694
739
  // A pan can leave the window a provider serves at the current resolution
695
740
  // just as a zoom can, so both re-pick.
696
741
  if (spec.viewport.resolution === "auto") adaptiveViewportRef.current = viewport;
742
+ // Bars are asked for while the view is still moving, once it comes within
743
+ // half a screen of the oldest loaded one (or of the newest, when newer
744
+ // ones exist), so a long pan keeps going instead of running into the end
745
+ // of what is loaded and waiting for the pointer to rest.
746
+ const loaded = loadedHistoryRef.current;
747
+ const loading = loadingWindowRef.current;
748
+ const reach = (end - start) / 2;
749
+ const nearOldest = !!loaded && !loaded.complete && start - loaded.start < reach;
750
+ const nearNewest = !!loaded && loaded.end < newestBarRef.current - reach / 10 && loaded.end - end < reach;
751
+ if ((nearOldest || nearNewest) && (!loading || Date.now() - loading.at > LOAD_AHEAD_TIMEOUT_MS)) {
752
+ commitRuntimeViewport(false);
753
+ }
697
754
  runtimeViewportTimerRef.current = globalThis.setTimeout(() => {
698
755
  runtimeViewportTimerRef.current = null;
699
756
  commitRuntimeViewport();
700
757
  }, AUTO_VIEWPORT_DEBOUNCE_MS);
701
- }, [authoredViewportKey, commitRuntimeViewport, spec.viewport.resolution]);
702
- const shareChart = useCallback(() => {
703
- // The share reads pane settings, so the gesture in flight lands there first.
758
+ }, [authoredViewportKey, commitRuntimeViewport, setStoredInteractionViewport, spec.viewport.resolution]);
759
+ usePaneSharePreparation(paneId, async () => {
704
760
  if (runtimeViewportTimerRef.current !== null) {
705
761
  clearTimeout(runtimeViewportTimerRef.current);
706
762
  runtimeViewportTimerRef.current = null;
707
763
  commitRuntimeViewport();
708
764
  }
709
- if (shareTimerRef.current !== null) return;
710
- shareTimerRef.current = globalThis.setTimeout(() => {
711
- shareTimerRef.current = null;
712
- sharePane(paneId);
713
- }, SHARE_SETTLE_DELAY_MS);
714
- }, [commitRuntimeViewport, paneId, sharePane]);
765
+ await new Promise((resolve) => setTimeout(resolve, SHARE_SETTLE_DELAY_MS));
766
+ });
715
767
 
716
768
  useRemoteUiNode({
717
769
  role: "chart-data",
@@ -800,12 +852,6 @@ function ChartComposerSurface({
800
852
  );
801
853
  useShortcut((event) => {
802
854
  if (interactionCaptureRef.current || dialogOpen) return;
803
- if (publicSharing && isPlainKey(event, "y")) {
804
- event.preventDefault();
805
- event.stopPropagation();
806
- shareChart();
807
- return;
808
- }
809
855
  const shortcut = resolveChartComposerShortcut(event, RANGES.length);
810
856
  if (!shortcut) return;
811
857
  event.preventDefault();
@@ -871,11 +917,10 @@ function ChartComposerSurface({
871
917
  });
872
918
 
873
919
  // Footer registrations compare presentation, so their callbacks must read current actions.
874
- const currentActionsRef = useRef({ openSeriesEditor, openResolutionPicker, shareChart });
875
- currentActionsRef.current = { openSeriesEditor, openResolutionPicker, shareChart };
920
+ const currentActionsRef = useRef({ openSeriesEditor, openResolutionPicker });
921
+ currentActionsRef.current = { openSeriesEditor, openResolutionPicker };
876
922
  const footerSeries = useCallback(() => { void currentActionsRef.current.openSeriesEditor(); }, []);
877
923
  const footerResolution = useCallback(() => { void currentActionsRef.current.openResolutionPicker(); }, []);
878
- const footerShare = useCallback(() => { currentActionsRef.current.shareChart(); }, []);
879
924
  const openIndicators = useCallback((event?: PaneFooterPressEvent) => {
880
925
  indicatorsDialogRef.current?.open(footerAnchorPoint(event));
881
926
  }, []);
@@ -901,9 +946,8 @@ function ChartComposerSurface({
901
946
  { id: "formulas", key: "f", label: "ormulas", onPress: openFormulas, disabled: formulasDisabled },
902
947
  { id: "resolution", key: "t", label: "imeframe", onPress: footerResolution },
903
948
  ...(anchorable ? [{ id: "vwap-anchor", key: "v", label: "wap anchor", title: "Anchor VWAP", onPress: footerAnchor }] : []),
904
- ...(publicSharing ? [{ id: "share", key: "y", label: " share", onPress: footerShare }] : []),
905
949
  ],
906
- }), [resolution.loading, pickingAnchor, footerSeries, openIndicators, indicatorsDisabled, openFormulas, formulasDisabled, footerResolution, anchorable, footerAnchor, publicSharing, footerShare]);
950
+ }), [resolution.loading, pickingAnchor, footerSeries, openIndicators, indicatorsDisabled, openFormulas, formulasDisabled, footerResolution, anchorable, footerAnchor]);
907
951
 
908
952
  // A fixed window (a GIP session) highlights no range, so the bar names it.
909
953
  const dateWindowLabel = useMemo(() => {
@@ -911,7 +955,7 @@ function ChartComposerSurface({
911
955
  if (!window) return undefined;
912
956
  const exchange = spec.series.find((entry) => entry.source.kind === "security")?.source;
913
957
  const timeZone = exchange?.kind === "security"
914
- ? resolveExchangeTimeZone(exchange.instrument.exchange) ?? "UTC"
958
+ ? intradaySessionTimeZone(exchange.instrument.symbol, exchange.instrument.exchange)
915
959
  : "UTC";
916
960
  return formatChartDateWindow(window, timeZone);
917
961
  }, [spec.series, spec.viewport.dateWindow]);
@@ -7,7 +7,8 @@ import {
7
7
  type ChartResolutionSupport,
8
8
  type ManualChartResolution,
9
9
  } from "../../../time-series/resolution";
10
- import { resolveExchangeTimeZone } from "../../../utils/exchanges";
10
+ import { parsePublicTickerKey, resolveExchangeTimeZone } from "../../../utils/exchanges";
11
+ import { listingSuffixExchange } from "../../../sources/listing-symbols";
11
12
  import { getPricePointTimestamp } from "../../../utils/price-history";
12
13
  import { pricePointIntegrity } from "../../../utils/price-history-integrity";
13
14
  import { zonedDateKey, zonedWallClockToUtcMs } from "../../../utils/zoned-date-time";
@@ -98,6 +99,20 @@ function sessionUtcBounds(value: string, timeZone: string): {
98
99
  };
99
100
  }
100
101
 
102
+ /**
103
+ * The zone a listing's sessions are dated in: its exchange's, else the one its
104
+ * key or listing suffix names (RMS.AX trades on the ASX). A session is the
105
+ * bars of one local day, so it starts at the local open even where that falls
106
+ * before UTC midnight (10:00 in Sydney is 23:00 UTC the day before). UTC for a
107
+ * listing without a known venue, such as a bare US symbol.
108
+ */
109
+ export function intradaySessionTimeZone(symbol: string, exchange: string | undefined): string {
110
+ const parsed = parsePublicTickerKey(symbol);
111
+ return resolveExchangeTimeZone(exchange)
112
+ ?? resolveExchangeTimeZone(parsed.exchange ?? listingSuffixExchange(parsed.symbol))
113
+ ?? "UTC";
114
+ }
115
+
101
116
  function normalizedPoints(points: readonly PricePoint[]): PricePoint[] {
102
117
  const byTimestamp = new Map<number, PricePoint>();
103
118
  for (const point of points) {
@@ -232,11 +247,11 @@ async function loadHistoricalFallback(
232
247
  exchange: string,
233
248
  request: IntradayRequest,
234
249
  now: Date,
250
+ timeZone: string,
235
251
  context?: MarketDataRequestContext,
236
252
  ): Promise<PriceHistoryResult> {
237
253
  const empty = { points: [], resolution: request.resolution };
238
254
  if (request.session) {
239
- const timeZone = resolveExchangeTimeZone(exchange) ?? "UTC";
240
255
  const bounds = sessionUtcBounds(request.session, timeZone);
241
256
  return await fetchHistoryResult(provider, symbol, exchange,
242
257
  { kind: "detail", start: bounds.start, end: bounds.end, interval: request.resolution }, context).catch(() => null) ?? empty;
@@ -256,14 +271,14 @@ export async function loadIntradayWindow(options: {
256
271
  now?: Date;
257
272
  context?: MarketDataRequestContext;
258
273
  }): Promise<LoadedIntradayWindow> {
259
- const timeZone = resolveExchangeTimeZone(options.exchange) ?? "UTC";
274
+ const timeZone = intradaySessionTimeZone(options.symbol, options.exchange);
260
275
  let acquisition = options.request.session
261
276
  ? await loadHistoricalFallback(
262
277
  options.provider,
263
278
  options.symbol,
264
279
  options.exchange,
265
280
  options.request,
266
- options.now ?? new Date(), options.context,
281
+ options.now ?? new Date(), timeZone, options.context,
267
282
  )
268
283
  : await loadTrailingHistory(
269
284
  options.provider,
@@ -292,7 +307,7 @@ export async function loadIntradayWindow(options: {
292
307
  options.symbol,
293
308
  options.exchange,
294
309
  options.request,
295
- options.now ?? new Date(), options.context,
310
+ options.now ?? new Date(), timeZone, options.context,
296
311
  );
297
312
  raw = acquisition.points;
298
313
  window = resolveIntradaySessionWindow(raw, {
@@ -17,10 +17,11 @@ import { requestChatPaneAction } from "./pane-requests";
17
17
 
18
18
  const CHAT_PANE_TEMPLATE_ID = "new-chat-pane";
19
19
  /**
20
- * Pane template value: show the conversation in a Chat pane that is already
21
- * open (switching it over) rather than in a pane of its own.
20
+ * Pane template value, for a template opened with no conversation: leave the
21
+ * open Chat pane on the conversation it shows instead of switching it to the
22
+ * one the bar would open.
22
23
  */
23
- export const CHAT_REUSE_PANE_VALUE = "reusePane";
24
+ export const CHAT_KEEP_CONVERSATION_VALUE = "keepConversation";
24
25
  const CHAT_SEARCH_PROVIDER_ID = "chat:conversations";
25
26
 
26
27
  type OpenChatPane = Pick<GloomPluginContext, "createPaneFromTemplate">;
@@ -31,16 +32,13 @@ type OpenChatPane = Pick<GloomPluginContext, "createPaneFromTemplate">;
31
32
  */
32
33
  export function openChatConversation(ctx: OpenChatPane, channelId: string): void {
33
34
  requestChatPaneAction({ action: "compose", channelId });
34
- ctx.createPaneFromTemplate(CHAT_PANE_TEMPLATE_ID, {
35
- arg: channelId,
36
- values: { [CHAT_REUSE_PANE_VALUE]: "1" },
37
- });
35
+ ctx.createPaneFromTemplate(CHAT_PANE_TEMPLATE_ID, { arg: channelId });
38
36
  }
39
37
 
40
38
  /** The Chat pane's own New DM dialog, in the open Chat pane or a new one. */
41
39
  export function openChatNewDm(ctx: OpenChatPane): void {
42
40
  requestChatPaneAction({ action: "new-dm" });
43
- ctx.createPaneFromTemplate(CHAT_PANE_TEMPLATE_ID, { values: { [CHAT_REUSE_PANE_VALUE]: "1" } });
41
+ ctx.createPaneFromTemplate(CHAT_PANE_TEMPLATE_ID, { values: { [CHAT_KEEP_CONVERSATION_VALUE]: "1" } });
44
42
  }
45
43
 
46
44
  const BADGES: Record<ChatConversationKind, string> = {
@@ -11,6 +11,8 @@ import type { ChatContentController } from "./types";
11
11
  import { getComposerCursorOffset, moveComposerCursorToOffset } from "./composer-cursor";
12
12
  import { insertedText, pastedImagePaths } from "../attachments/transfer";
13
13
  import { chatTextWithImages } from "../attachments/model";
14
+ import { isManagedBot } from "../ghost-user";
15
+ import { CHAT_PERSON_ICON_WIDTH } from "../people";
14
16
 
15
17
  interface MutableRef<T> {
16
18
  current: T;
@@ -398,7 +400,10 @@ export function useChatComposerRuntime({
398
400
  const replyPreview = replyTo
399
401
  ? formatInlinePreview(
400
402
  chatTextWithImages(replyTo.content, replyTo.attachments?.length ?? 0),
401
- Math.max(contentWidth - ` replying to @${replyTo.user.username}: `.length - COMPOSER_ACTION_WIDTH - 1, 0),
403
+ Math.max(
404
+ contentWidth - ` replying to @${replyTo.user.username}`.length - (isManagedBot(replyTo.user) ? CHAT_PERSON_ICON_WIDTH + 1 : 2) - COMPOSER_ACTION_WIDTH - 1,
405
+ 0,
406
+ ),
402
407
  )
403
408
  : "";
404
409
  const editingPreview = editingMessage
@@ -1,3 +1,4 @@
1
+ import type { ReactNode } from "react";
1
2
  import { Box, Text, TextAttributes, useCommandBarShortcut, type TextareaRenderable } from "../../../../ui";
2
3
  import { getMessageComposerBlockHeight, MessageComposer } from "../../../../components/ui";
3
4
  import { colors } from "../../../../theme/colors";
@@ -7,6 +8,8 @@ import type { ChatMessage } from "../../../../api-client";
7
8
  import { InlineAuthActions } from "../../cloud/auth-actions";
8
9
  import { Button, ListView } from "../../../../components/ui";
9
10
  import { COMPOSER_ACTION_WIDTH } from "../layout";
11
+ import { isManagedBot } from "../ghost-user";
12
+ import { ChatBotIcon } from "../message/person-icons";
10
13
  import type { ChatMentionSuggestion } from "./mentions";
11
14
  import type { ChatDraftAttachment } from "../controller/attachments";
12
15
  import { DesktopChatAttachButton, DesktopChatAttachmentStrip } from "../attachments/desktop";
@@ -20,6 +23,8 @@ interface MutableRef<T> {
20
23
  }
21
24
 
22
25
  interface ChatComposerAreaProps {
26
+ /** The first-chat handle prompt, drawn above everything else once you can send. */
27
+ handlePrompt?: ReactNode;
23
28
  canSend: boolean;
24
29
  cancelEditMessage: () => void;
25
30
  clearReplyTarget: () => void;
@@ -85,6 +90,7 @@ function ChatMentionSuggestions({
85
90
  }
86
91
 
87
92
  export function ChatComposerArea({
93
+ handlePrompt,
88
94
  canSend,
89
95
  cancelEditMessage,
90
96
  clearReplyTarget,
@@ -159,6 +165,8 @@ export function ChatComposerArea({
159
165
  />
160
166
  )}
161
167
 
168
+ {handlePrompt}
169
+
162
170
  {editingMessage && (
163
171
  <Box height={1} width={contentWidth} flexDirection="row">
164
172
  <Text fg={colors.textMuted}>{` ${t("editing")} `}</Text>
@@ -176,7 +184,9 @@ export function ChatComposerArea({
176
184
  <Box height={1} width={contentWidth} flexDirection="row">
177
185
  <Text fg={colors.textMuted}>{` ${t("replying to")} `}</Text>
178
186
  <Text fg={colors.positive} attributes={TextAttributes.BOLD}>{`@${replyTo.user.username}`}</Text>
179
- <Text fg={colors.textDim}>{replyPreview ? `: ${replyPreview}` : ""}</Text>
187
+ {isManagedBot(replyTo.user) && <ChatBotIcon />}
188
+ {/* The bot's icon stands where the colon would. */}
189
+ <Text fg={colors.textDim}>{replyPreview ? `${isManagedBot(replyTo.user) ? " " : ": "}${replyPreview}` : ""}</Text>
180
190
  <Box flexGrow={1} />
181
191
  <Button stopPropagation
182
192
  label={t("Cancel")}
@@ -251,6 +261,7 @@ export function getChatComposerAreaHeight({
251
261
  replyTo,
252
262
  mentionSuggestionCount,
253
263
  draftAttachmentCount = 0,
264
+ promptRows = 0,
254
265
  }: {
255
266
  canSend: boolean;
256
267
  composerHeight: number;
@@ -260,9 +271,11 @@ export function getChatComposerAreaHeight({
260
271
  mentionSuggestionCount?: number;
261
272
  /** The terminal draws a row per waiting image; the desktop strip sizes itself. */
262
273
  draftAttachmentCount?: number;
274
+ /** Terminal rows the handle prompt above the composer takes. */
275
+ promptRows?: number;
263
276
  }) {
264
277
  if (!canSend) return 2;
265
- return getMessageComposerBlockHeight({ height: composerHeight, nativePaneChrome })
278
+ return promptRows + getMessageComposerBlockHeight({ height: composerHeight, nativePaneChrome })
266
279
  + (editingMessage || replyTo ? 1 : 0)
267
280
  + Math.max(0, mentionSuggestionCount ?? 0)
268
281
  + (!nativePaneChrome && !editingMessage ? draftAttachmentCount : 0);
@@ -8,7 +8,9 @@ import { useRendererHost, useUiCapabilities } from "../../../../ui";
8
8
  import { useOptionalDialog, type PromptContext } from "../../../../ui/dialog";
9
9
  import { chatAuthorName, isDiscordGhost } from "../ghost-user";
10
10
  import { chatMessageOpenTargets, type ChatOpenTarget } from "./open-targets";
11
+ import { chatMessagePaneLinks, type ChatMessagePaneLink } from "../pane-links/model";
11
12
  import { openChatImageViewer } from "../attachments/desktop";
13
+ import { canReactToMessage } from "../reactions";
12
14
 
13
15
  /**
14
16
  * The chat pane's keys, shown in its footer and listed in its pane menu. The
@@ -27,6 +29,7 @@ export function useChatFooter({
27
29
  focusComposer,
28
30
  catalog,
29
31
  openTicker,
32
+ openPaneLink,
30
33
  openAttachPicker,
31
34
  profilePopoverUser,
32
35
  showProfilePopover,
@@ -45,6 +48,8 @@ export function useChatFooter({
45
48
  needsProfileSetup,
46
49
  openProfileSetup,
47
50
  authorMessageAction,
51
+ reactionsEnabled = false,
52
+ openReactionPicker,
48
53
  }: {
49
54
  /**
50
55
  * The composer or the New DM overlay owns the keyboard. Hints about the
@@ -62,6 +67,8 @@ export function useChatFooter({
62
67
  focusComposer: () => void;
63
68
  catalog: Record<string, InlineTickerCatalogEntry>;
64
69
  openTicker: (symbol: string) => void;
70
+ /** Opens what a message's chip stands for. */
71
+ openPaneLink: (link: ChatMessagePaneLink) => void;
65
72
  /** Opens the composer's file picker; null where images cannot be attached. */
66
73
  openAttachPicker: (() => void) | null;
67
74
  profilePopoverUser: ChatUserSummary | null;
@@ -84,18 +91,26 @@ export function useChatFooter({
84
91
  openProfileSetup: () => void;
85
92
  /** "Message @author" for the selected message, where its author can be written to. */
86
93
  authorMessageAction: { label: string; run: () => void } | null;
94
+ /** The server takes reactions and you can send. */
95
+ reactionsEnabled?: boolean;
96
+ /** Opens the reaction picker for the message at an index. */
97
+ openReactionPicker?: (index: number) => void;
87
98
  }) {
88
99
  const dialog = useOptionalDialog();
89
100
  const rendererHost = useRendererHost();
90
- const content = selectedMessage?.content ?? "";
101
+ const { links: paneLinks, content } = chatMessagePaneLinks(selectedMessage);
91
102
  const attachments = selectedMessage?.attachments;
92
103
  const openTargets = useMemo(
93
- () => chatMessageOpenTargets(content, catalog, attachments),
94
- [attachments, catalog, content],
104
+ () => chatMessageOpenTargets(content, catalog, attachments, paneLinks),
105
+ [attachments, catalog, content, paneLinks],
95
106
  );
96
107
  const { nativePaneChrome } = useUiCapabilities();
97
108
 
98
109
  const openTarget = useCallback((target: ChatOpenTarget) => {
110
+ if (target.kind === "pane") {
111
+ openPaneLink(target.link);
112
+ return;
113
+ }
99
114
  if (target.kind === "ticker") {
100
115
  openTicker(target.symbol);
101
116
  return;
@@ -111,7 +126,7 @@ export function useChatFooter({
111
126
  return;
112
127
  }
113
128
  void rendererHost.openExternal(target.url);
114
- }, [dialog, nativePaneChrome, openTicker, rendererHost, selectedMessage]);
129
+ }, [dialog, nativePaneChrome, openPaneLink, openTicker, rendererHost, selectedMessage]);
115
130
 
116
131
  // One target opens at once; several ask which, in the order the message reads.
117
132
  const openSelectedTargets = useCallback(async () => {
@@ -157,12 +172,15 @@ export function useChatFooter({
157
172
  }, [closeProfilePopover, selectedIdx]);
158
173
 
159
174
  const canEdit = canSend && !!selectedMessage && selectedMessage.id === latestEditableMessageId;
175
+ const canReact = reactionsEnabled && !!openReactionPicker && !!selectedMessage && canReactToMessage(selectedMessage);
160
176
  const replyToId = selectedMessage?.replyToId ?? null;
161
177
  const onlyTarget = openTargets.length === 1 ? openTargets[0]! : null;
162
178
  const failedSend = selectedMessage?.clientStatus === "failed";
163
179
  const canAttachImages = !!openAttachPicker;
164
180
  const openTargetTitle = onlyTarget
165
- ? onlyTarget.kind === "ticker" ? `Open $${onlyTarget.symbol}` : onlyTarget.kind === "image" ? "Open Image" : "Open Link"
181
+ ? onlyTarget.kind === "ticker"
182
+ ? `Open $${onlyTarget.symbol}`
183
+ : onlyTarget.kind === "image" ? "Open Image" : onlyTarget.kind === "pane" ? "Open Pane" : "Open Link"
166
184
  : openTargets.every((target) => target.kind === "ticker")
167
185
  ? "Open Ticker…"
168
186
  : openTargets.every((target) => target.kind === "link")
@@ -188,6 +206,7 @@ export function useChatFooter({
188
206
  setChannelNotificationsEnabled,
189
207
  toggleAuthorProfile,
190
208
  authorMessageAction,
209
+ openReactionPicker,
191
210
  });
192
211
  latest.current = {
193
212
  beginEditMessage,
@@ -206,6 +225,7 @@ export function useChatFooter({
206
225
  setChannelNotificationsEnabled,
207
226
  toggleAuthorProfile,
208
227
  authorMessageAction,
228
+ openReactionPicker,
209
229
  };
210
230
  const authorMessageLabel = composing ? null : authorMessageAction?.label ?? null;
211
231
 
@@ -215,6 +235,7 @@ export function useChatFooter({
215
235
  if (!composing && selectedMessage) {
216
236
  if (canSend && failedSend) hints.push({ id: "retry", key: "Enter", label: "retry", title: "Send Again", onPress: () => latest.current.retryMessage(latest.current.selectedIdx) });
217
237
  else if (canSend) hints.push({ id: "reply", key: "Enter", label: "reply", title: "Reply", onPress: () => latest.current.beginReplyTo(latest.current.selectedIdx, { deferFocus: true }) });
238
+ if (canReact) hints.push({ id: "react", key: "+", label: " react", title: "Add Reaction", onPress: () => latest.current.openReactionPicker?.(latest.current.selectedIdx) });
218
239
  if (canEdit) hints.push({ id: "edit", key: "e", label: "dit", title: "Edit Message", onPress: () => { latest.current.beginEditMessage(latest.current.selectedIdx, { deferFocus: true }); } });
219
240
  if (openTargets.length > 0) hints.push({ id: "open", key: "o", label: "pen", title: openTargetTitle, onPress: () => { void latest.current.openSelectedTargets(); } });
220
241
  // Every author but a Discord ghost has a card, if only their name and a way to write to them.
@@ -271,6 +292,7 @@ export function useChatFooter({
271
292
  failedSend,
272
293
  canCycleChannels,
273
294
  canEdit,
295
+ canReact,
274
296
  canFocusSidebar,
275
297
  canSend,
276
298
  composing,