@opengeni/react 1.0.1 → 1.0.2-canary.37202341988001

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 (632) hide show
  1. package/README.md +660 -21
  2. package/dist/accounts.d.ts +64 -0
  3. package/dist/accounts.js +822 -0
  4. package/dist/accounts.js.map +1 -0
  5. package/dist/approvals.d.ts +2 -1
  6. package/dist/artifacts-document.js +3 -2
  7. package/dist/artifacts-presentation.js +3 -2
  8. package/dist/artifacts-spreadsheet.js +2 -2
  9. package/dist/artifacts.d.ts +4 -1
  10. package/dist/artifacts.js +518 -42
  11. package/dist/artifacts.js.map +1 -1
  12. package/dist/browser-viewer-SHYKQRNX.js +16 -0
  13. package/dist/capability-catalog-row.d.ts +20 -0
  14. package/dist/capability-logo-fallback.d.ts +3 -0
  15. package/dist/chat-interactive-block-UGBTNB3L.js +11 -0
  16. package/dist/chat-interactive-block-UGBTNB3L.js.map +1 -0
  17. package/dist/chunk-26ZNDDLZ.js +453 -0
  18. package/dist/chunk-26ZNDDLZ.js.map +1 -0
  19. package/dist/chunk-3CTRFMAK.js +359 -0
  20. package/dist/chunk-3CTRFMAK.js.map +1 -0
  21. package/dist/chunk-447JLCBU.js +209 -0
  22. package/dist/chunk-447JLCBU.js.map +1 -0
  23. package/dist/chunk-4RVYOY4O.js +379 -0
  24. package/dist/chunk-4RVYOY4O.js.map +1 -0
  25. package/dist/chunk-5CVXZA3Y.js +292 -0
  26. package/dist/chunk-5CVXZA3Y.js.map +1 -0
  27. package/dist/chunk-5J4FOW6Q.js +108 -0
  28. package/dist/chunk-5J4FOW6Q.js.map +1 -0
  29. package/dist/chunk-742CZ3L3.js +481 -0
  30. package/dist/chunk-742CZ3L3.js.map +1 -0
  31. package/dist/chunk-A2PI6UMY.js +41 -0
  32. package/dist/chunk-A2PI6UMY.js.map +1 -0
  33. package/dist/{chunk-7WCLAEFV.js → chunk-A4UJB2MM.js} +37 -27
  34. package/dist/chunk-A4UJB2MM.js.map +1 -0
  35. package/dist/chunk-B3V6TG77.js +41 -0
  36. package/dist/chunk-B3V6TG77.js.map +1 -0
  37. package/dist/chunk-BRM6FVZA.js +302 -0
  38. package/dist/chunk-BRM6FVZA.js.map +1 -0
  39. package/dist/chunk-C5X5AJXK.js +182 -0
  40. package/dist/chunk-C5X5AJXK.js.map +1 -0
  41. package/dist/{chunk-6KLX2RH5.js → chunk-DO544OSV.js} +1075 -79
  42. package/dist/chunk-DO544OSV.js.map +1 -0
  43. package/dist/{chunk-R3AZOAB5.js → chunk-DYWBUX5Q.js} +29 -123
  44. package/dist/chunk-DYWBUX5Q.js.map +1 -0
  45. package/dist/{chunk-HA46CYQK.js → chunk-EESD3PY3.js} +792 -537
  46. package/dist/chunk-EESD3PY3.js.map +1 -0
  47. package/dist/{chunk-5UQPNLIO.js → chunk-EN3YTHKR.js} +4117 -3896
  48. package/dist/chunk-EN3YTHKR.js.map +1 -0
  49. package/dist/chunk-ERGYJIBB.js +21 -0
  50. package/dist/chunk-ERGYJIBB.js.map +1 -0
  51. package/dist/chunk-FZCP527Y.js +116 -0
  52. package/dist/chunk-FZCP527Y.js.map +1 -0
  53. package/dist/chunk-GIMRELBW.js +14 -0
  54. package/dist/chunk-GIMRELBW.js.map +1 -0
  55. package/dist/chunk-GYVRFZDY.js +2733 -0
  56. package/dist/chunk-GYVRFZDY.js.map +1 -0
  57. package/dist/{chunk-LLOLUHVG.js → chunk-HDJWDK7W.js} +1358 -2746
  58. package/dist/chunk-HDJWDK7W.js.map +1 -0
  59. package/dist/chunk-I7IWRQLN.js +1608 -0
  60. package/dist/chunk-I7IWRQLN.js.map +1 -0
  61. package/dist/chunk-IT4BJUGF.js +13055 -0
  62. package/dist/chunk-IT4BJUGF.js.map +1 -0
  63. package/dist/chunk-IWIDV7SB.js +1 -0
  64. package/dist/chunk-IWIDV7SB.js.map +1 -0
  65. package/dist/chunk-J2QWVSQY.js +312 -0
  66. package/dist/chunk-J2QWVSQY.js.map +1 -0
  67. package/dist/chunk-J6DBS4T3.js +371 -0
  68. package/dist/chunk-J6DBS4T3.js.map +1 -0
  69. package/dist/{chunk-B6WPZSTT.js → chunk-JIQYUGVL.js} +1331 -1163
  70. package/dist/chunk-JIQYUGVL.js.map +1 -0
  71. package/dist/chunk-JT4JW7BC.js +54 -0
  72. package/dist/chunk-JT4JW7BC.js.map +1 -0
  73. package/dist/chunk-K3HQAKQX.js +24 -0
  74. package/dist/chunk-K3HQAKQX.js.map +1 -0
  75. package/dist/{chunk-PEI6RANI.js → chunk-PNY3JKBJ.js} +309 -74
  76. package/dist/chunk-PNY3JKBJ.js.map +1 -0
  77. package/dist/chunk-RHREZ7BO.js +11 -0
  78. package/dist/chunk-RHREZ7BO.js.map +1 -0
  79. package/dist/chunk-RLUSC2WA.js +299 -0
  80. package/dist/chunk-RLUSC2WA.js.map +1 -0
  81. package/dist/{chunk-IGC6A2ZI.js → chunk-RSI7XYXL.js} +777 -246
  82. package/dist/chunk-RSI7XYXL.js.map +1 -0
  83. package/dist/chunk-SCOTPE2I.js +589 -0
  84. package/dist/chunk-SCOTPE2I.js.map +1 -0
  85. package/dist/{chunk-C5ZPSSS5.js → chunk-TKOTKRWY.js} +5 -4
  86. package/dist/{chunk-C5ZPSSS5.js.map → chunk-TKOTKRWY.js.map} +1 -1
  87. package/dist/chunk-TMYGL6X5.js +742 -0
  88. package/dist/chunk-TMYGL6X5.js.map +1 -0
  89. package/dist/chunk-UGLEYEMP.js +33 -0
  90. package/dist/chunk-UGLEYEMP.js.map +1 -0
  91. package/dist/chunk-UK4TINKT.js +27 -0
  92. package/dist/chunk-UK4TINKT.js.map +1 -0
  93. package/dist/chunk-UTRTG6LH.js +220 -0
  94. package/dist/chunk-UTRTG6LH.js.map +1 -0
  95. package/dist/chunk-WEHMPOXK.js +39 -0
  96. package/dist/chunk-WEHMPOXK.js.map +1 -0
  97. package/dist/chunk-WGXVEJO5.js +319 -0
  98. package/dist/chunk-WGXVEJO5.js.map +1 -0
  99. package/dist/chunk-WRHIZYZF.js +988 -0
  100. package/dist/chunk-WRHIZYZF.js.map +1 -0
  101. package/dist/{chunk-HUIKIDM5.js → chunk-WUISZ3WU.js} +223 -115
  102. package/dist/chunk-WUISZ3WU.js.map +1 -0
  103. package/dist/chunk-XCHWDJ22.js +107 -0
  104. package/dist/chunk-XCHWDJ22.js.map +1 -0
  105. package/dist/{chunk-KCGXPP5V.js → chunk-Y2PRMYLC.js} +11 -6
  106. package/dist/chunk-Y2PRMYLC.js.map +1 -0
  107. package/dist/chunk-YBXQDQFX.js +1772 -0
  108. package/dist/chunk-YBXQDQFX.js.map +1 -0
  109. package/dist/chunk-YM2DE7BQ.js +3124 -0
  110. package/dist/chunk-YM2DE7BQ.js.map +1 -0
  111. package/dist/{chunk-KZ73RL76.js → chunk-YQEAA6LT.js} +7 -3
  112. package/dist/chunk-YQEAA6LT.js.map +1 -0
  113. package/dist/chunk-YV4L3ILF.js +9 -0
  114. package/dist/chunk-YV4L3ILF.js.map +1 -0
  115. package/dist/chunk-ZYRGEKWS.js +216 -0
  116. package/dist/chunk-ZYRGEKWS.js.map +1 -0
  117. package/dist/client.d.ts +7 -4
  118. package/dist/clipboard.js +1 -0
  119. package/dist/commands/types.d.ts +2 -0
  120. package/dist/components/anthropic-mark.d.ts +3 -0
  121. package/dist/components/approval-surface.d.ts +15 -0
  122. package/dist/components/artifacts/artifact-chrome.d.ts +120 -0
  123. package/dist/components/artifacts/artifact-sandbox.d.ts +27 -0
  124. package/dist/components/artifacts/artifact-surface.d.ts +3 -1
  125. package/dist/components/artifacts/chat-interactive-block.d.ts +29 -0
  126. package/dist/components/artifacts/deferred-chat-media.d.ts +15 -0
  127. package/dist/components/artifacts/document-editor.d.ts +4 -2
  128. package/dist/components/artifacts/editable-artifact-ui.d.ts +2 -1
  129. package/dist/components/artifacts/editable-artifact-view.d.ts +46 -0
  130. package/dist/components/artifacts/editable-document.d.ts +1 -1
  131. package/dist/components/artifacts/editable-presentation.d.ts +2 -2
  132. package/dist/components/artifacts/editable-spreadsheet.d.ts +2 -1
  133. package/dist/components/artifacts/index.d.ts +8 -1
  134. package/dist/components/artifacts/inline-html-document.d.ts +2 -0
  135. package/dist/components/artifacts/latest-task-runner.d.ts +15 -0
  136. package/dist/components/artifacts/presentation-editor.d.ts +6 -2
  137. package/dist/components/artifacts/published-html-artifact-frame.d.ts +48 -3
  138. package/dist/components/artifacts/session-artifact-viewer.d.ts +39 -0
  139. package/dist/components/artifacts/site-view.d.ts +23 -0
  140. package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
  141. package/dist/components/artifacts/visualization-assets.gen.d.ts +2 -0
  142. package/dist/components/browser-select-control.d.ts +16 -0
  143. package/dist/components/browser-viewer.d.ts +10 -1
  144. package/dist/components/chat-composer.d.ts +21 -3
  145. package/dist/components/child-session-link.d.ts +5 -0
  146. package/dist/components/claude-mark.d.ts +3 -0
  147. package/dist/components/code-editor.d.ts +1 -4
  148. package/dist/components/composer.d.ts +45 -4
  149. package/dist/components/computer-viewer.d.ts +4 -1
  150. package/dist/components/copy-button.d.ts +3 -1
  151. package/dist/components/desktop-viewer.d.ts +1 -1
  152. package/dist/components/enrollment-consent.d.ts +6 -3
  153. package/dist/components/file-browser.d.ts +10 -1
  154. package/dist/components/fleet-tile.d.ts +2 -2
  155. package/dist/components/grok-mark.d.ts +26 -0
  156. package/dist/components/human-input-form.d.ts +21 -2
  157. package/dist/components/human-input-surface.d.ts +6 -3
  158. package/dist/components/machine-card.d.ts +4 -1
  159. package/dist/components/machines/machine-detail.d.ts +4 -1
  160. package/dist/components/machines/machine-operation-policy.d.ts +11 -0
  161. package/dist/components/machines-dashboard.d.ts +3 -1
  162. package/dist/components/markdown-table-layout.d.ts +12 -0
  163. package/dist/components/markdown.d.ts +47 -3
  164. package/dist/components/message-timeline.d.ts +72 -9
  165. package/dist/components/model-mark.d.ts +29 -0
  166. package/dist/components/model-policy-picker-menu.d.ts +9 -0
  167. package/dist/components/model-policy-picker.d.ts +62 -13
  168. package/dist/components/open-geni-chat.d.ts +58 -0
  169. package/dist/components/open-geni-links.d.ts +68 -0
  170. package/dist/components/openrouter-mark.d.ts +6 -0
  171. package/dist/components/pierre-file.d.ts +1 -1
  172. package/dist/components/preview-loading.d.ts +2 -0
  173. package/dist/components/queue-item-content.d.ts +9 -0
  174. package/dist/components/sandbox-files.d.ts +13 -2
  175. package/dist/components/sandbox-workspace.d.ts +43 -3
  176. package/dist/components/session-capability-frame.d.ts +35 -0
  177. package/dist/components/session-chrome.d.ts +66 -8
  178. package/dist/components/session-commands-panel.d.ts +6 -0
  179. package/dist/components/session-commands.d.ts +6 -0
  180. package/dist/components/session-connection-request.d.ts +8 -0
  181. package/dist/components/session-conversation.d.ts +89 -0
  182. package/dist/components/session-list.d.ts +36 -0
  183. package/dist/components/session-mcp-capability-card.d.ts +41 -0
  184. package/dist/components/session-proxy-scope.d.ts +21 -0
  185. package/dist/components/timeline-anchor.d.ts +24 -0
  186. package/dist/components/timeline-annotation-chrome.d.ts +25 -0
  187. package/dist/components/timeline-annotation-layout.d.ts +42 -0
  188. package/dist/components/timeline-annotation-markers.d.ts +8 -0
  189. package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
  190. package/dist/components/timeline-annotation-shared.d.ts +31 -0
  191. package/dist/components/timeline-annotations-dialog.d.ts +3 -2
  192. package/dist/components/timeline-annotations.d.ts +13 -3
  193. package/dist/components/timeline-reading-progress.d.ts +11 -0
  194. package/dist/components/timeline-search.d.ts +34 -0
  195. package/dist/components/timeline-settlement.d.ts +16 -0
  196. package/dist/components/tip-follow.d.ts +16 -0
  197. package/dist/components/usage-limit-notice.d.ts +49 -0
  198. package/dist/components/usage-member-list.d.ts +91 -0
  199. package/dist/components/usage-meter.d.ts +73 -0
  200. package/dist/components/use-viewer-menu-dismiss.d.ts +2 -0
  201. package/dist/components/user-message-body.d.ts +10 -1
  202. package/dist/components/workspace-dock.d.ts +6 -2
  203. package/dist/composer.d.ts +5 -4
  204. package/dist/composer.js +15 -9
  205. package/dist/computer-viewer-6SRGWRUX.js +19 -0
  206. package/dist/computer-viewer-6SRGWRUX.js.map +1 -0
  207. package/dist/connect-account-identity.d.ts +8 -0
  208. package/dist/connect-accounts.d.ts +15 -0
  209. package/dist/connect-chooser.d.ts +18 -0
  210. package/dist/connect-panel.d.ts +18 -0
  211. package/dist/connect-setup.d.ts +16 -0
  212. package/dist/connect.d.ts +21 -0
  213. package/dist/connect.js +2129 -0
  214. package/dist/connect.js.map +1 -0
  215. package/dist/connection-catalog.d.ts +38 -0
  216. package/dist/connection-discovery.d.ts +12 -0
  217. package/dist/connection-installed.d.ts +17 -0
  218. package/dist/connection-logo.d.ts +9 -0
  219. package/dist/connection-service-logo.d.ts +8 -0
  220. package/dist/connection-service-presentation.d.ts +13 -0
  221. package/dist/connection-skeleton.d.ts +5 -0
  222. package/dist/connection-type-picker.d.ts +6 -0
  223. package/dist/conversation-timeline.d.ts +5 -0
  224. package/dist/desktop.d.ts +7 -0
  225. package/dist/desktop.js +26 -0
  226. package/dist/desktop.js.map +1 -0
  227. package/dist/device-authorization.d.ts +23 -0
  228. package/dist/diffs.d.ts +8 -0
  229. package/dist/diffs.js +14 -0
  230. package/dist/diffs.js.map +1 -0
  231. package/dist/discovery-cache.d.ts +5 -0
  232. package/dist/editor.d.ts +7 -0
  233. package/dist/editor.js +21 -0
  234. package/dist/editor.js.map +1 -0
  235. package/dist/embedded-session-client.d.ts +23 -0
  236. package/dist/file-node-visibility.d.ts +15 -0
  237. package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-YZ3ZKWMM.js} +2 -1
  238. package/dist/fleet-decision-row-YZ3ZKWMM.js.map +1 -0
  239. package/dist/hooks/channel-cache.d.ts +23 -0
  240. package/dist/hooks/latest-question.d.ts +25 -0
  241. package/dist/hooks/shared-machines-list.d.ts +14 -0
  242. package/dist/hooks/use-attached-browsers.d.ts +2 -1
  243. package/dist/hooks/use-browser-frame-stream.d.ts +1 -1
  244. package/dist/hooks/use-browser-session.d.ts +12 -1
  245. package/dist/hooks/use-channels.d.ts +1 -0
  246. package/dist/hooks/use-codex-accounts.d.ts +25 -7
  247. package/dist/hooks/use-composer.d.ts +45 -9
  248. package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
  249. package/dist/hooks/use-computer-input-posture.d.ts +13 -0
  250. package/dist/hooks/use-computer-session.d.ts +2 -0
  251. package/dist/hooks/use-connect.d.ts +22 -0
  252. package/dist/hooks/use-desktop-stream.d.ts +1 -1
  253. package/dist/hooks/use-file-attachments.d.ts +21 -2
  254. package/dist/hooks/use-machine-chip.d.ts +3 -3
  255. package/dist/hooks/use-machines.d.ts +19 -1
  256. package/dist/hooks/use-sandbox-files.d.ts +5 -1
  257. package/dist/hooks/use-session-background-commands.d.ts +14 -0
  258. package/dist/hooks/use-session-events.d.ts +30 -8
  259. package/dist/hooks/use-session-lineage.d.ts +4 -0
  260. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  261. package/dist/hooks/use-session.d.ts +6 -0
  262. package/dist/hooks/use-turn-queue.d.ts +12 -0
  263. package/dist/hooks/use-usage.d.ts +40 -0
  264. package/dist/hooks/use-workspace-edit.d.ts +4 -0
  265. package/dist/hooks/use-workspace-sessions.d.ts +27 -5
  266. package/dist/human-input.d.ts +3 -1
  267. package/dist/identity-link-accounts.d.ts +11 -0
  268. package/dist/identity-link-consent.d.ts +18 -0
  269. package/dist/index.d.ts +60 -17
  270. package/dist/index.js +2238 -6201
  271. package/dist/index.js.map +1 -1
  272. package/dist/interaction.d.ts +1 -1
  273. package/dist/interaction.js +35 -184
  274. package/dist/interaction.js.map +1 -1
  275. package/dist/latest-question-HTRIS4KJ.js +117 -0
  276. package/dist/latest-question-HTRIS4KJ.js.map +1 -0
  277. package/dist/lib/error-message.d.ts +7 -0
  278. package/dist/lib/format.d.ts +36 -2
  279. package/dist/lib/host-theme.d.ts +70 -0
  280. package/dist/lib/interaction-errors.d.ts +6 -0
  281. package/dist/lib/menu-styles.d.ts +22 -0
  282. package/dist/lib/pierre-diffs-loader.d.ts +20 -0
  283. package/dist/lib/workbench-peers.d.ts +36 -0
  284. package/dist/lib/xterm-theme.d.ts +7 -5
  285. package/dist/machines.d.ts +4 -2
  286. package/dist/machines.js +14 -4
  287. package/dist/markdown-table-layout-QXSLK4BY.js +106 -0
  288. package/dist/markdown-table-layout-QXSLK4BY.js.map +1 -0
  289. package/dist/mcp-connection-status.d.ts +14 -0
  290. package/dist/model-policy-picker-menu-M54ISBMN.js +14 -0
  291. package/dist/model-policy-picker-menu-M54ISBMN.js.map +1 -0
  292. package/dist/model-policy.d.ts +19 -3
  293. package/dist/model-policy.js +9 -1
  294. package/dist/older-history.d.ts +30 -0
  295. package/dist/platform-activity-row-627BFZ46.js +182 -0
  296. package/dist/platform-activity-row-627BFZ46.js.map +1 -0
  297. package/dist/plugin-details.d.ts +14 -0
  298. package/dist/plugin-discovery.d.ts +25 -0
  299. package/dist/provider.d.ts +13 -1
  300. package/dist/{queue-surface-implementation-2KVJTYXH.js → queue-surface-implementation-ZHFI5JBP.js} +131 -44
  301. package/dist/queue-surface-implementation-ZHFI5JBP.js.map +1 -0
  302. package/dist/realtime/realtime-control.d.ts +6 -3
  303. package/dist/realtime.js +133 -74
  304. package/dist/realtime.js.map +1 -1
  305. package/dist/sandbox-files-GORZOUUJ.js +20 -0
  306. package/dist/sandbox-files-GORZOUUJ.js.map +1 -0
  307. package/dist/session-auth-recommendation.d.ts +5 -0
  308. package/dist/session-capability-policy.d.ts +12 -0
  309. package/dist/session-ui.d.ts +30 -2
  310. package/dist/session-ui.js +85 -7
  311. package/dist/session-ui.js.map +1 -1
  312. package/dist/session.d.ts +8 -2
  313. package/dist/session.js +30 -19
  314. package/dist/sites-ui.d.ts +34 -0
  315. package/dist/sites.d.ts +3 -0
  316. package/dist/sites.js +11 -0
  317. package/dist/sites.js.map +1 -0
  318. package/dist/skill-discovery.d.ts +47 -0
  319. package/dist/terminal.d.ts +8 -0
  320. package/dist/terminal.js +31 -0
  321. package/dist/terminal.js.map +1 -0
  322. package/dist/timeline/activity-rail.d.ts +11 -2
  323. package/dist/timeline/allowance-exhausted-row.d.ts +24 -0
  324. package/dist/timeline/compaction-copy.d.ts +10 -0
  325. package/dist/timeline/entrance.d.ts +13 -1
  326. package/dist/timeline/genie-loading.d.ts +34 -0
  327. package/dist/timeline/index.d.ts +6 -2
  328. package/dist/timeline/knowledge-receipt.d.ts +17 -0
  329. package/dist/timeline/platform-activity-row.d.ts +14 -0
  330. package/dist/timeline/presented-image.d.ts +6 -0
  331. package/dist/timeline/projection.d.ts +31 -2
  332. package/dist/timeline/registry.d.ts +2 -0
  333. package/dist/timeline/retained-image.d.ts +18 -0
  334. package/dist/timeline/rolling-activity.d.ts +11 -0
  335. package/dist/timeline/screenshot-lightbox.d.ts +16 -1
  336. package/dist/timeline/shared.d.ts +8 -3
  337. package/dist/timeline/startup-preference.d.ts +3 -0
  338. package/dist/timeline/startup-timings.d.ts +4 -0
  339. package/dist/timeline/tool-display-name.d.ts +2 -1
  340. package/dist/timeline/turn-summary.d.ts +34 -3
  341. package/dist/timeline/types.d.ts +147 -7
  342. package/dist/timeline-annotation-markers-GNDHTGBD.js +165 -0
  343. package/dist/timeline-annotation-markers-GNDHTGBD.js.map +1 -0
  344. package/dist/timeline-annotation-selection-DMLMM2TX.js +241 -0
  345. package/dist/timeline-annotation-selection-DMLMM2TX.js.map +1 -0
  346. package/dist/timeline-annotations-dialog-ELRDBHTS.js +273 -0
  347. package/dist/timeline-annotations-dialog-ELRDBHTS.js.map +1 -0
  348. package/dist/types/machines.d.ts +2 -2
  349. package/dist/usage/allowance-copy.d.ts +25 -0
  350. package/dist/usage/summary.d.ts +35 -0
  351. package/dist/usage.d.ts +13 -0
  352. package/dist/usage.js +1074 -0
  353. package/dist/usage.js.map +1 -0
  354. package/package.json +44 -4
  355. package/src/accounts.tsx +1072 -0
  356. package/src/approvals.ts +8 -1
  357. package/src/artifacts.ts +42 -0
  358. package/src/assets/README.md +8 -0
  359. package/src/assets/gmail.ico +0 -0
  360. package/src/assets/outlook.ico +0 -0
  361. package/src/capability-catalog-row.tsx +94 -0
  362. package/src/capability-logo-fallback.ts +22 -0
  363. package/src/client.ts +22 -13
  364. package/src/commands/registry.ts +18 -15
  365. package/src/commands/types.ts +2 -0
  366. package/src/components/anthropic-mark.tsx +10 -0
  367. package/src/components/approval-surface.tsx +171 -37
  368. package/src/components/artifacts/artifact-chrome.tsx +413 -0
  369. package/src/components/artifacts/artifact-sandbox.tsx +185 -0
  370. package/src/components/artifacts/artifact-surface.tsx +39 -19
  371. package/src/components/artifacts/chat-interactive-block.tsx +260 -0
  372. package/src/components/artifacts/deferred-chat-media.tsx +67 -0
  373. package/src/components/artifacts/document-editor.tsx +141 -16
  374. package/src/components/artifacts/editable-artifact-ui.tsx +4 -2
  375. package/src/components/artifacts/editable-artifact-view.tsx +187 -0
  376. package/src/components/artifacts/editable-artifact-workbench.tsx +5 -2
  377. package/src/components/artifacts/editable-document.tsx +37 -36
  378. package/src/components/artifacts/editable-presentation.tsx +36 -18
  379. package/src/components/artifacts/editable-spreadsheet.tsx +5 -2
  380. package/src/components/artifacts/index.ts +44 -0
  381. package/src/components/artifacts/inline-html-document.ts +24 -0
  382. package/src/components/artifacts/latest-task-runner.ts +49 -0
  383. package/src/components/artifacts/presentation-editor.tsx +323 -67
  384. package/src/components/artifacts/published-html-artifact-frame.tsx +338 -5
  385. package/src/components/artifacts/session-artifact-viewer.tsx +376 -0
  386. package/src/components/artifacts/site-view.tsx +116 -0
  387. package/src/components/artifacts/spreadsheet-canvas.ts +5 -10
  388. package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
  389. package/src/components/artifacts/spreadsheet-grid.tsx +12 -13
  390. package/src/components/artifacts/visualization-assets/base.css +1015 -0
  391. package/src/components/artifacts/visualization-assets/helpers.html +349 -0
  392. package/src/components/artifacts/visualization-assets.gen.ts +3 -0
  393. package/src/components/browser-select-control.tsx +312 -0
  394. package/src/components/browser-viewer.tsx +1054 -314
  395. package/src/components/chat-composer.tsx +131 -85
  396. package/src/components/child-session-link.tsx +27 -0
  397. package/src/components/claude-mark.tsx +10 -0
  398. package/src/components/code-editor.tsx +13 -16
  399. package/src/components/composer-transcription-control.tsx +7 -5
  400. package/src/components/composer.tsx +376 -49
  401. package/src/components/computer-viewer.tsx +1041 -329
  402. package/src/components/copy-button.tsx +13 -5
  403. package/src/components/desktop-viewer.tsx +1 -1
  404. package/src/components/enrollment-consent.tsx +65 -7
  405. package/src/components/enrollment-device-flow.tsx +1 -1
  406. package/src/components/file-browser.tsx +158 -34
  407. package/src/components/fleet-tile.tsx +8 -16
  408. package/src/components/generated-video-player.tsx +4 -2
  409. package/src/components/grok-mark.tsx +33 -0
  410. package/src/components/human-input-form.tsx +455 -114
  411. package/src/components/human-input-surface.tsx +18 -7
  412. package/src/components/interaction-intervention-banner.tsx +2 -2
  413. package/src/components/machine-card.tsx +119 -4
  414. package/src/components/machine-dock-bar.tsx +1 -1
  415. package/src/components/machine-input-display.ts +23 -2
  416. package/src/components/machines/machine-detail.tsx +52 -2
  417. package/src/components/machines/machine-operation-policy.tsx +179 -0
  418. package/src/components/machines-dashboard.tsx +8 -17
  419. package/src/components/markdown-table-layout.ts +132 -0
  420. package/src/components/markdown.tsx +473 -44
  421. package/src/components/message-timeline.tsx +2754 -665
  422. package/src/components/model-mark.tsx +90 -0
  423. package/src/components/model-picker.tsx +15 -4
  424. package/src/components/model-policy-picker-menu.tsx +424 -0
  425. package/src/components/model-policy-picker.tsx +337 -381
  426. package/src/components/open-geni-chat.tsx +338 -0
  427. package/src/components/open-geni-links.tsx +179 -0
  428. package/src/components/openrouter-mark.tsx +13 -0
  429. package/src/components/pierre-diff.tsx +41 -16
  430. package/src/components/pierre-file.tsx +36 -11
  431. package/src/components/preview-loading.tsx +154 -0
  432. package/src/components/queue-item-content.ts +14 -0
  433. package/src/components/queue-surface-implementation.tsx +132 -42
  434. package/src/components/queue-surface-state.tsx +3 -1
  435. package/src/components/sandbox-files.tsx +267 -35
  436. package/src/components/sandbox-terminal.tsx +48 -11
  437. package/src/components/sandbox-workspace.tsx +337 -135
  438. package/src/components/session-capability-frame.tsx +192 -0
  439. package/src/components/session-chrome.tsx +1027 -328
  440. package/src/components/session-commands-panel.tsx +126 -0
  441. package/src/components/session-commands.tsx +18 -0
  442. package/src/components/session-connection-request.tsx +97 -0
  443. package/src/components/session-conversation.tsx +526 -0
  444. package/src/components/session-list.tsx +250 -0
  445. package/src/components/session-mcp-capability-card.tsx +680 -0
  446. package/src/components/session-proxy-scope.tsx +92 -0
  447. package/src/components/session-status.tsx +1 -1
  448. package/src/components/timeline-anchor.tsx +119 -0
  449. package/src/components/timeline-annotation-chrome.tsx +196 -0
  450. package/src/components/timeline-annotation-layout.ts +268 -0
  451. package/src/components/timeline-annotation-markers.tsx +185 -0
  452. package/src/components/timeline-annotation-reveal-context.ts +9 -0
  453. package/src/components/timeline-annotation-selection.tsx +106 -54
  454. package/src/components/timeline-annotation-shared.ts +245 -0
  455. package/src/components/timeline-annotations-dialog.tsx +185 -128
  456. package/src/components/timeline-annotations.tsx +189 -45
  457. package/src/components/timeline-reading-progress.ts +80 -0
  458. package/src/components/timeline-search.tsx +168 -0
  459. package/src/components/timeline-settlement.ts +60 -0
  460. package/src/components/tip-follow.ts +70 -7
  461. package/src/components/tooltip.tsx +38 -5
  462. package/src/components/usage-limit-notice.tsx +179 -0
  463. package/src/components/usage-member-list.tsx +792 -0
  464. package/src/components/usage-meter.tsx +382 -0
  465. package/src/components/use-viewer-menu-dismiss.ts +33 -0
  466. package/src/components/user-message-body.tsx +194 -49
  467. package/src/components/workspace-dock.tsx +141 -175
  468. package/src/composer.ts +6 -1
  469. package/src/connect-account-identity.tsx +34 -0
  470. package/src/connect-accounts.tsx +233 -0
  471. package/src/connect-chooser.tsx +336 -0
  472. package/src/connect-panel.tsx +99 -0
  473. package/src/connect-setup.tsx +307 -0
  474. package/src/connect.ts +60 -0
  475. package/src/connection-assets.d.ts +4 -0
  476. package/src/connection-catalog.tsx +162 -0
  477. package/src/connection-discovery.tsx +235 -0
  478. package/src/connection-installed.tsx +51 -0
  479. package/src/connection-logo.tsx +37 -0
  480. package/src/connection-service-logo.tsx +55 -0
  481. package/src/connection-service-presentation.ts +46 -0
  482. package/src/connection-skeleton.tsx +16 -0
  483. package/src/connection-type-picker.tsx +36 -0
  484. package/src/conversation-timeline.ts +92 -0
  485. package/src/desktop.ts +24 -0
  486. package/src/device-authorization.tsx +98 -0
  487. package/src/diffs.ts +16 -0
  488. package/src/discovery-cache.ts +42 -0
  489. package/src/editor.ts +13 -0
  490. package/src/embedded-session-client.ts +104 -0
  491. package/src/file-node-visibility.ts +66 -0
  492. package/src/hooks/channel-cache.ts +104 -0
  493. package/src/hooks/internal.ts +3 -2
  494. package/src/hooks/latest-question.ts +164 -0
  495. package/src/hooks/shared-machines-list.ts +239 -0
  496. package/src/hooks/use-attached-browsers.ts +5 -1
  497. package/src/hooks/use-available-models.ts +20 -14
  498. package/src/hooks/use-browser-frame-stream.ts +25 -3
  499. package/src/hooks/use-browser-session.ts +271 -71
  500. package/src/hooks/use-browser-sessions.ts +7 -1
  501. package/src/hooks/use-channels.ts +32 -10
  502. package/src/hooks/use-codex-accounts.ts +78 -21
  503. package/src/hooks/use-composer.ts +1034 -320
  504. package/src/hooks/use-computer-frame-stream.ts +39 -16
  505. package/src/hooks/use-computer-input-posture.ts +94 -0
  506. package/src/hooks/use-computer-session.ts +110 -32
  507. package/src/hooks/use-computer-sessions.ts +8 -1
  508. package/src/hooks/use-connect.ts +23 -0
  509. package/src/hooks/use-desktop-stream.ts +19 -28
  510. package/src/hooks/use-file-attachments.ts +118 -10
  511. package/src/hooks/use-goal.ts +36 -9
  512. package/src/hooks/use-human-input.ts +31 -4
  513. package/src/hooks/use-last-started-turn-policy.ts +15 -13
  514. package/src/hooks/use-machine-chip.ts +23 -6
  515. package/src/hooks/use-machines.ts +104 -6
  516. package/src/hooks/use-sandbox-files.ts +253 -150
  517. package/src/hooks/use-sandbox-git.ts +58 -29
  518. package/src/hooks/use-session-background-commands.ts +67 -0
  519. package/src/hooks/use-session-capabilities.ts +21 -2
  520. package/src/hooks/use-session-events.ts +759 -433
  521. package/src/hooks/use-session-lineage.ts +28 -6
  522. package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
  523. package/src/hooks/use-session.ts +75 -16
  524. package/src/hooks/use-slash-commands.ts +6 -11
  525. package/src/hooks/use-turn-queue.ts +113 -13
  526. package/src/hooks/use-usage.ts +100 -0
  527. package/src/hooks/use-video-artifact-playback.ts +1 -1
  528. package/src/hooks/use-voice-input.ts +12 -1
  529. package/src/hooks/use-workspace-capture.ts +3 -2
  530. package/src/hooks/use-workspace-edit.ts +23 -5
  531. package/src/hooks/use-workspace-sessions.ts +99 -11
  532. package/src/human-input.ts +12 -1
  533. package/src/identity-link-accounts.tsx +148 -0
  534. package/src/identity-link-consent.tsx +192 -0
  535. package/src/index.ts +157 -12
  536. package/src/interaction.ts +1 -0
  537. package/src/lib/error-message.ts +37 -0
  538. package/src/lib/format.ts +150 -14
  539. package/src/lib/host-theme.ts +511 -0
  540. package/src/lib/interaction-errors.ts +33 -0
  541. package/src/lib/menu-styles.ts +44 -0
  542. package/src/lib/pierre-diffs-loader.ts +53 -0
  543. package/src/lib/workbench-peers.ts +62 -0
  544. package/src/lib/xterm-theme.ts +23 -21
  545. package/src/machines.ts +9 -1
  546. package/src/mcp-connection-status.ts +35 -0
  547. package/src/model-policy.ts +223 -69
  548. package/src/older-history.ts +87 -0
  549. package/src/plugin-details.tsx +185 -0
  550. package/src/plugin-discovery.tsx +196 -0
  551. package/src/provider.tsx +55 -14
  552. package/src/realtime/dropdown-menu.tsx +19 -12
  553. package/src/realtime/realtime-control.tsx +133 -46
  554. package/src/session-auth-recommendation.ts +48 -0
  555. package/src/session-capability-policy.ts +83 -0
  556. package/src/session-context.ts +4 -3
  557. package/src/session-ui.ts +66 -2
  558. package/src/session.ts +18 -1
  559. package/src/sites-ui.tsx +402 -0
  560. package/src/sites.ts +3 -0
  561. package/src/skill-discovery.tsx +208 -0
  562. package/src/terminal.ts +26 -0
  563. package/src/timeline/activity-rail.tsx +171 -80
  564. package/src/timeline/allowance-exhausted-row.tsx +86 -0
  565. package/src/timeline/compaction-copy.ts +42 -0
  566. package/src/timeline/entrance.tsx +28 -3
  567. package/src/timeline/fleet-decision-projection.ts +2 -1
  568. package/src/timeline/fleet-decision-row.tsx +1 -0
  569. package/src/timeline/genie-loading.tsx +136 -0
  570. package/src/timeline/index.ts +11 -0
  571. package/src/timeline/knowledge-receipt.tsx +100 -0
  572. package/src/timeline/parsers.ts +2 -2
  573. package/src/timeline/platform-activity-row.tsx +216 -0
  574. package/src/timeline/presented-image.ts +49 -0
  575. package/src/timeline/projection.ts +1609 -84
  576. package/src/timeline/registry.ts +15 -3
  577. package/src/timeline/retained-image.ts +97 -0
  578. package/src/timeline/rolling-activity.tsx +92 -0
  579. package/src/timeline/screenshot-lightbox.tsx +115 -17
  580. package/src/timeline/shared.tsx +47 -15
  581. package/src/timeline/startup-preference.ts +33 -0
  582. package/src/timeline/startup-timings.tsx +145 -0
  583. package/src/timeline/tool-display-name.ts +8 -6
  584. package/src/timeline/tool-renderers.tsx +497 -128
  585. package/src/timeline/turn-summary.tsx +218 -44
  586. package/src/timeline/types.ts +192 -7
  587. package/src/types/machines.ts +10 -0
  588. package/src/usage/allowance-copy.ts +64 -0
  589. package/src/usage/summary.ts +93 -0
  590. package/src/usage.ts +50 -0
  591. package/styles/compiled.css +2098 -757
  592. package/styles/connect.css +284 -0
  593. package/styles/connect.d.ts +1 -0
  594. package/styles/effective-tokens.css +1 -0
  595. package/styles/index.css +201 -9
  596. package/styles/responsive.css +27 -1
  597. package/styles/session-capability.css +44 -0
  598. package/styles/tokens.css +135 -65
  599. package/dist/chunk-5UQPNLIO.js.map +0 -1
  600. package/dist/chunk-6KLX2RH5.js.map +0 -1
  601. package/dist/chunk-7WCLAEFV.js.map +0 -1
  602. package/dist/chunk-B6WPZSTT.js.map +0 -1
  603. package/dist/chunk-FK6VG4LL.js +0 -90
  604. package/dist/chunk-FK6VG4LL.js.map +0 -1
  605. package/dist/chunk-HA46CYQK.js.map +0 -1
  606. package/dist/chunk-HUIKIDM5.js.map +0 -1
  607. package/dist/chunk-IGC6A2ZI.js.map +0 -1
  608. package/dist/chunk-KCGXPP5V.js.map +0 -1
  609. package/dist/chunk-KZ73RL76.js.map +0 -1
  610. package/dist/chunk-LAKLF4PA.js +0 -141
  611. package/dist/chunk-LAKLF4PA.js.map +0 -1
  612. package/dist/chunk-LLOLUHVG.js.map +0 -1
  613. package/dist/chunk-MKPIAGVL.js +0 -465
  614. package/dist/chunk-MKPIAGVL.js.map +0 -1
  615. package/dist/chunk-PEI6RANI.js.map +0 -1
  616. package/dist/chunk-R3AZOAB5.js.map +0 -1
  617. package/dist/chunk-WSK7G5LE.js +0 -170
  618. package/dist/chunk-WSK7G5LE.js.map +0 -1
  619. package/dist/chunk-XBZWOM2K.js +0 -218
  620. package/dist/chunk-XBZWOM2K.js.map +0 -1
  621. package/dist/chunk-Y7OEDUJ5.js +0 -8810
  622. package/dist/chunk-Y7OEDUJ5.js.map +0 -1
  623. package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
  624. package/dist/hooks/use-packs.d.ts +0 -32
  625. package/dist/interactive-workbench-viewers-TOHBXJEE.js +0 -15
  626. package/dist/queue-surface-implementation-2KVJTYXH.js.map +0 -1
  627. package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
  628. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
  629. package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
  630. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
  631. package/src/hooks/use-packs.ts +0 -157
  632. /package/dist/{interactive-workbench-viewers-TOHBXJEE.js.map → browser-viewer-SHYKQRNX.js.map} +0 -0
@@ -9,6 +9,7 @@ import {
9
9
  PauseIcon,
10
10
  PlayIcon,
11
11
  RotateCwIcon,
12
+ SquareIcon,
12
13
  XIcon,
13
14
  } from "lucide-react";
14
15
  import { AnimatePresence, motion } from "motion/react";
@@ -20,6 +21,7 @@ import {
20
21
  useContext,
21
22
  useEffect,
22
23
  useId,
24
+ useLayoutEffect,
23
25
  useMemo,
24
26
  useRef,
25
27
  useState,
@@ -35,6 +37,7 @@ import {
35
37
  type RefObject,
36
38
  } from "react";
37
39
  import { argHint, defaultCommands } from "../commands/registry";
40
+ import { useErrorMessage } from "../lib/error-message";
38
41
  import type { Notice, SlashCommand } from "../commands/types";
39
42
  import type { ComposerState } from "../hooks/use-composer";
40
43
  import { shouldSteerOnKey, shouldSubmitOnKey } from "../hooks/use-composer";
@@ -51,6 +54,7 @@ import {
51
54
  } from "../lib/composer-responsive-context";
52
55
  import { composerSubmissionErrorMessage, formatBytes, formatRelativeTime } from "../lib/format";
53
56
  import type { PickerModelRow } from "../model-policy";
57
+ import { useLightboxOptional } from "../timeline/screenshot-lightbox";
54
58
  import { OPEN_WORKSTREAM_CONTROL_EVENT } from "../workstream-control-event";
55
59
  import { CommandPalette as CommandPaletteView } from "./command-palette";
56
60
  import { ModelPicker as ModelPickerView } from "./model-picker";
@@ -72,7 +76,16 @@ function ComposerTip({
72
76
 
73
77
  export type ComposerDelivery = Pick<
74
78
  ComposerState,
75
- "value" | "setValue" | "send" | "steer" | "sending" | "canSend" | "error" | "clearError"
79
+ | "value"
80
+ | "setValue"
81
+ | "send"
82
+ | "steer"
83
+ | "sending"
84
+ | "canSend"
85
+ | "error"
86
+ | "clearError"
87
+ | "annotations"
88
+ | "requestAnnotationReview"
76
89
  >;
77
90
 
78
91
  export type ComposerDraftState = Pick<
@@ -106,10 +119,14 @@ export type ChatComposerMessages = {
106
119
  attachFiles: string;
107
120
  pauseAriaLabel: string;
108
121
  pauseTitle: string;
122
+ /** Stop control shown while a response runs (`runControl="stop"`). */
123
+ stopAriaLabel?: string | undefined;
124
+ stopTitle?: string | undefined;
109
125
  sendMessageAriaLabel: string;
110
126
  sendAndResumeAriaLabel: string;
111
127
  sendTitle: string;
112
128
  sendAndResumeTitle: string;
129
+ annotationNotesRequired: string;
113
130
  workspacePaused: string;
114
131
  pausedHere: string;
115
132
  parentBlocker: string;
@@ -134,6 +151,11 @@ export type ChatComposerMessages = {
134
151
  removeRestoredResource: (index: number) => string;
135
152
  uploading: string;
136
153
  uploadFailed: string;
154
+ previewAttachment: (name: string) => string;
155
+ previewUnavailable?: string | undefined;
156
+ attachmentPreviewLabel: string;
157
+ downloadAttachment: (name: string) => string;
158
+ closeAttachmentPreview: string;
137
159
  retryAttachment: (name: string) => string;
138
160
  retryUpload: string;
139
161
  removeAttachment: (name: string) => string;
@@ -156,9 +178,11 @@ export type ChatComposerMessages = {
156
178
 
157
179
  export const defaultChatComposerMessages: ChatComposerMessages = {
158
180
  messagePlaceholder: "Message the agent…",
159
- pausedPlaceholder: "Sending will resume this workstream…",
181
+ pausedPlaceholder: "Message the agent — it will wait in the queue…",
160
182
  inputLabel: "Message the agent",
161
- keyboardHint: "Enter to queue · Cmd/Ctrl+Enter to steer · Shift+Enter for a new line",
183
+ // Shortcuts live in the send button's title; the footer stays quiet unless
184
+ // the host supplies its own hint.
185
+ keyboardHint: "",
162
186
  slashCommandBlocked:
163
187
  "That's a slash command — press Enter in the command list to run it, or edit the line to send a message.",
164
188
  controlChangedError:
@@ -168,19 +192,22 @@ export const defaultChatComposerMessages: ChatComposerMessages = {
168
192
  attachFiles: "Attach files",
169
193
  pauseAriaLabel: "Pause this workstream",
170
194
  pauseTitle: "Pause this workstream; queued prompts and approvals are preserved",
195
+ stopAriaLabel: "Stop",
196
+ stopTitle: "Stop the response",
171
197
  sendMessageAriaLabel: "Send message",
172
- sendAndResumeAriaLabel: "Send and resume",
198
+ sendAndResumeAriaLabel: "Add message to queue",
173
199
  sendTitle: "Queue message (Enter); steer with Cmd/Ctrl+Enter",
174
- sendAndResumeTitle: "Send & resume (Enter); Steer & resume with Cmd/Ctrl+Enter",
200
+ sendAndResumeTitle: "Add to queue (Enter); steer now with Cmd/Ctrl+Enter",
201
+ annotationNotesRequired: "Add a note to each quote before sending.",
175
202
  workspacePaused: "Workspace paused",
176
203
  pausedHere: "Paused here",
177
204
  parentBlocker: "parent",
178
205
  narrowerPause: "a narrower pause",
179
206
  pausedBy: (displayName) => `Paused by ${displayName}`,
180
207
  queuedAhead: (count) =>
181
- `Send & resume queues behind ${count} waiting prompt${count === 1 ? "" : "s"}.`,
182
- resumingAndSending: "Resuming and sending…",
183
- nextMessageResumes: "Your next message resumes this workstream automatically.",
208
+ `${count} waiting prompt${count === 1 ? "" : "s"}; your message joins the queue.`,
209
+ resumingAndSending: "Sending…",
210
+ nextMessageResumes: "Messages wait in the queue until you resume.",
184
211
  resumeThisWorkstream: "Resume this workstream",
185
212
  resumeShort: "Resume",
186
213
  hidePauseDetails: "Hide pause details",
@@ -197,6 +224,11 @@ export const defaultChatComposerMessages: ChatComposerMessages = {
197
224
  removeRestoredResource: (index) => `Remove restored resource ${index + 1}`,
198
225
  uploading: "Uploading",
199
226
  uploadFailed: "Upload failed",
227
+ previewAttachment: (name) => `Preview ${name}`,
228
+ previewUnavailable: "Preview unavailable",
229
+ attachmentPreviewLabel: "Attachment preview",
230
+ downloadAttachment: (name) => `Download ${name}`,
231
+ closeAttachmentPreview: "Close",
200
232
  retryAttachment: (name) => `Retry ${name}`,
201
233
  retryUpload: "Retry upload",
202
234
  removeAttachment: (name) => `Remove ${name}`,
@@ -225,9 +257,21 @@ export type ComposerSubmitBlocker =
225
257
  | "attachment"
226
258
  | "sending"
227
259
  | "command"
260
+ | "annotations"
228
261
  | "empty"
229
262
  | null;
230
263
 
264
+ /**
265
+ * How the composer offers run control.
266
+ * - `pause`: the workstream Pause control and paused-state strip (default; the
267
+ * OpenGeni console).
268
+ * - `stop`: a Stop control only while a response runs. Stopping pauses this
269
+ * conversation; the next message continues it, so people never see a paused
270
+ * state they have to resume. Pauses applied elsewhere still show.
271
+ * - `none`: no run control.
272
+ */
273
+ export type ComposerRunControl = "pause" | "stop" | "none";
274
+
231
275
  export type UseChatComposerControllerOptions = {
232
276
  delivery: ComposerDelivery;
233
277
  draft?: ComposerDraftState | undefined;
@@ -243,6 +287,10 @@ export type UseChatComposerControllerOptions = {
243
287
  onClearView?: (() => void) | undefined;
244
288
  onPaste?: ((event: ClipboardEvent<HTMLTextAreaElement>) => void) | undefined;
245
289
  messages?: Partial<ChatComposerMessages> | undefined;
290
+ /** Run control presentation. Defaults to `pause`. */
291
+ runControl?: ComposerRunControl | undefined;
292
+ /** A response is in progress; shows the Stop control under `runControl="stop"`. */
293
+ running?: boolean | undefined;
246
294
  };
247
295
 
248
296
  /**
@@ -269,7 +317,7 @@ function measureComposerContentHeight(textarea: HTMLTextAreaElement): number {
269
317
  mirror.tabIndex = -1;
270
318
  mirror.rows = 1;
271
319
  mirror.style.cssText =
272
- "position:absolute;top:0;left:0;visibility:hidden;pointer-events:none;height:auto;min-height:0;max-height:none;overflow:hidden;z-index:-1;";
320
+ "position:fixed;top:0;left:-100000px;visibility:hidden;pointer-events:none;height:auto;min-height:0;max-height:none;overflow:hidden;z-index:-1;";
273
321
  composerHeightMirror = mirror;
274
322
  }
275
323
 
@@ -349,7 +397,10 @@ export function useChatComposerController({
349
397
  onClearView,
350
398
  onPaste,
351
399
  messages: messageOverrides,
400
+ runControl = "pause",
401
+ running = false,
352
402
  }: UseChatComposerControllerOptions) {
403
+ const formatError = useErrorMessage();
353
404
  const messages = useMemo(
354
405
  () => ({ ...defaultChatComposerMessages, ...messageOverrides }),
355
406
  [messageOverrides],
@@ -360,13 +411,55 @@ export function useChatComposerController({
360
411
  const pauseButtonRef = useRef<HTMLButtonElement | null>(null);
361
412
  const fileInputRef = useRef<HTMLInputElement | null>(null);
362
413
  const listboxId = useId();
363
- const paused = effectiveControl?.state === "paused";
414
+ const controlPaused = effectiveControl?.state === "paused";
415
+ // Set when this composer's Stop paused the conversation; cleared once it runs
416
+ // again. Pauses from anyone or anywhere else are never reinterpreted.
417
+ const [stoppedHere, setStoppedHere] = useState(false);
418
+ useEffect(() => {
419
+ if (!controlPaused) setStoppedHere(false);
420
+ }, [controlPaused]);
421
+ // Under Stop, the stopped response is not a state to manage: hide it and let
422
+ // the next message continue (a Steer resumes a paused branch). It applies
423
+ // only to this conversation's own pause with nothing queued behind it.
424
+ const resumesOnSend =
425
+ runControl === "stop" &&
426
+ stoppedHere &&
427
+ controlPaused &&
428
+ effectiveControl?.directState === "paused" &&
429
+ effectiveControl.additionalBlockerCount === 0 &&
430
+ queuedAheadCount === 0;
431
+ const paused = controlPaused && !resumesOnSend;
364
432
  const [controlDetailsOpen, setControlDetailsOpen] = useState(false);
365
433
  const [paletteMounted, setPaletteMounted] = useState(false);
366
434
  const [submitting, setSubmitting] = useState(false);
367
435
  const submittingRef = useRef(false);
368
436
  const controlOperationRef = useRef(false);
437
+ const deliveryValue = delivery.value;
438
+ const setDeliveryValue = delivery.setValue;
369
439
  const mountedRef = useRef(true);
440
+ const deliveredValueRef = useRef(deliveryValue);
441
+ const liveValueRef = useRef(deliveryValue);
442
+ const [renderedValue, setRenderedValue] = useState(deliveryValue);
443
+ const deliveryChanged = deliveryValue !== deliveredValueRef.current;
444
+ if (deliveryChanged) {
445
+ deliveredValueRef.current = deliveryValue;
446
+ liveValueRef.current = deliveryValue;
447
+ }
448
+ const setComposerValue = useCallback(
449
+ (next: string) => {
450
+ liveValueRef.current = next;
451
+ setRenderedValue(next);
452
+ setDeliveryValue(next);
453
+ },
454
+ [setDeliveryValue],
455
+ );
456
+ const visibleValue = deliveryChanged ? deliveryValue : renderedValue;
457
+
458
+ useLayoutEffect(() => {
459
+ if (renderedValue !== liveValueRef.current) {
460
+ setRenderedValue(liveValueRef.current);
461
+ }
462
+ }, [deliveryValue, renderedValue]);
370
463
 
371
464
  useEffect(() => {
372
465
  mountedRef.current = true;
@@ -400,7 +493,6 @@ export function useChatComposerController({
400
493
  }, [id, paused]);
401
494
 
402
495
  const blockedByAttachment = attachments?.hasUnresolved === true;
403
- const hasReadyAttachment = (attachments?.readyResources.length ?? 0) > 0;
404
496
 
405
497
  const [dragging, setDragging] = useState(false);
406
498
  const dragCarriesFiles = (event: { dataTransfer: DataTransfer | null }): boolean =>
@@ -409,9 +501,9 @@ export function useChatComposerController({
409
501
  (event: DragEvent<HTMLDivElement>) => {
410
502
  if (!attachments || !dragCarriesFiles(event)) return;
411
503
  event.preventDefault();
412
- setDragging(true);
504
+ if (!disabled) setDragging(true);
413
505
  },
414
- [attachments],
506
+ [attachments, disabled],
415
507
  );
416
508
  const handleDragLeave = useCallback(
417
509
  (event: DragEvent<HTMLDivElement>) => {
@@ -426,11 +518,16 @@ export function useChatComposerController({
426
518
  if (!attachments || !dragCarriesFiles(event)) return;
427
519
  event.preventDefault();
428
520
  setDragging(false);
429
- if (event.dataTransfer.files.length > 0) attachments.addFiles(event.dataTransfer.files);
521
+ if (!disabled && event.dataTransfer.files.length > 0)
522
+ attachments.addFiles(event.dataTransfer.files);
430
523
  },
431
- [attachments],
524
+ [attachments, disabled],
432
525
  );
433
526
 
527
+ useEffect(() => {
528
+ if (disabled) setDragging(false);
529
+ }, [disabled]);
530
+
434
531
  const [notice, setNotice] = useState<Notice | null>(null);
435
532
  const [helpOpen, setHelpOpen] = useState(false);
436
533
  const [confirmState, setConfirmState] = useState<ConfirmState>(null);
@@ -468,7 +565,7 @@ export function useChatComposerController({
468
565
  applyComposerTextareaHeight(textarea, 220);
469
566
  });
470
567
  }, []);
471
- useEffect(() => resizeInput(), [delivery.value, resizeInput]);
568
+ useEffect(() => resizeInput(), [resizeInput, visibleValue]);
472
569
  useEffect(
473
570
  () => () => {
474
571
  if (resizeInputRafRef.current !== null) {
@@ -505,11 +602,14 @@ export function useChatComposerController({
505
602
  commands,
506
603
  context: commandContext,
507
604
  handlers,
508
- value: delivery.value,
509
- setValue: delivery.setValue,
605
+ value: visibleValue,
606
+ setValue: setComposerValue,
510
607
  });
511
608
  const paletteEnabled = commandContext !== undefined;
512
609
  const commandDraftBlocked = paletteEnabled && palette.isCommandDraft;
610
+ const annotationsIncomplete = (delivery.annotations ?? []).some(
611
+ (annotation) => annotation.note.trim().length === 0,
612
+ );
513
613
 
514
614
  const submitBlocker: ComposerSubmitBlocker = disabled
515
615
  ? "disabled"
@@ -519,9 +619,11 @@ export function useChatComposerController({
519
619
  ? "sending"
520
620
  : commandDraftBlocked
521
621
  ? "command"
522
- : delivery.canSend || hasReadyAttachment
523
- ? null
524
- : "empty";
622
+ : annotationsIncomplete
623
+ ? "annotations"
624
+ : delivery.canSend
625
+ ? null
626
+ : "empty";
525
627
  const canSubmit = submitBlocker === null;
526
628
 
527
629
  const submit = useCallback(
@@ -533,23 +635,31 @@ export function useChatComposerController({
533
635
  }
534
636
  if (disabled || blockedByAttachment || delivery.sending || submittingRef.current)
535
637
  return false;
536
- if (!delivery.canSend && !hasReadyAttachment) return false;
638
+ if (annotationsIncomplete) {
639
+ setNotice({ tone: "error", message: messages.annotationNotesRequired });
640
+ delivery.clearError();
641
+ delivery.requestAnnotationReview?.();
642
+ return false;
643
+ }
644
+ if (!delivery.canSend) return false;
537
645
  submittingRef.current = true;
538
646
  setSubmitting(true);
539
647
  try {
540
- return mode === "steer" ? await delivery.steer() : await delivery.send();
648
+ return mode === "steer" || resumesOnSend ? await delivery.steer() : await delivery.send();
541
649
  } finally {
542
650
  submittingRef.current = false;
543
651
  if (mountedRef.current) setSubmitting(false);
544
652
  }
545
653
  },
546
654
  [
655
+ annotationsIncomplete,
547
656
  blockedByAttachment,
548
657
  commandDraftBlocked,
549
658
  delivery,
550
659
  disabled,
551
- hasReadyAttachment,
660
+ messages.annotationNotesRequired,
552
661
  messages.slashCommandBlocked,
662
+ resumesOnSend,
553
663
  ],
554
664
  );
555
665
 
@@ -564,17 +674,18 @@ export function useChatComposerController({
564
674
  );
565
675
  const handlePaste = useCallback(
566
676
  (event: ClipboardEvent<HTMLTextAreaElement>) => {
677
+ if (disabled) return;
567
678
  onPaste?.(event);
568
679
  attachments?.addFromPaste(event);
569
680
  },
570
- [attachments, onPaste],
681
+ [attachments, disabled, onPaste],
571
682
  );
572
683
  const handleFileChange = useCallback(
573
684
  (event: ChangeEvent<HTMLInputElement>) => {
574
- if (event.target.files) attachments?.addFiles(event.target.files);
685
+ if (!disabled && event.target.files) attachments?.addFiles(event.target.files);
575
686
  event.target.value = "";
576
687
  },
577
- [attachments],
688
+ [attachments, disabled],
578
689
  );
579
690
 
580
691
  const helpCommands = useMemo(
@@ -597,7 +708,10 @@ export function useChatComposerController({
597
708
  tone: "error" as const,
598
709
  message: /control changed|paused while/i.test(delivery.error.message)
599
710
  ? messages.controlChangedError
600
- : composerSubmissionErrorMessage(delivery.error) || messages.sendFailedError,
711
+ : formatError(
712
+ delivery.error,
713
+ composerSubmissionErrorMessage(delivery.error) || messages.sendFailedError,
714
+ ),
601
715
  }
602
716
  : null);
603
717
  useEffect(() => {
@@ -608,6 +722,12 @@ export function useChatComposerController({
608
722
  );
609
723
  return () => window.clearTimeout(timer);
610
724
  }, [notice]);
725
+ useEffect(() => {
726
+ if (annotationsIncomplete) return;
727
+ setNotice((current) =>
728
+ current?.message === messages.annotationNotesRequired ? null : current,
729
+ );
730
+ }, [annotationsIncomplete, messages.annotationNotesRequired]);
611
731
 
612
732
  const runControlOperation = useCallback(async (operation: () => Promise<void>) => {
613
733
  if (controlOperationRef.current) return false;
@@ -642,6 +762,12 @@ export function useChatComposerController({
642
762
  [control, runControlOperation],
643
763
  );
644
764
 
765
+ const stop = useCallback(async (): Promise<boolean> => {
766
+ const stopped = await pause();
767
+ if (stopped && mountedRef.current) setStoppedHere(true);
768
+ return stopped;
769
+ }, [pause]);
770
+
645
771
  return {
646
772
  id,
647
773
  rootRef,
@@ -666,9 +792,12 @@ export function useChatComposerController({
666
792
  removeRestoredResource: draft?.removeRestoredResource,
667
793
  resolveDraftConflict: draft?.resolveDraftConflict,
668
794
  hasControl: control !== undefined,
795
+ runControl,
796
+ running,
669
797
  pausing: control?.pausing ?? false,
670
798
  resuming: control?.resuming ?? false,
671
799
  pause,
800
+ stop,
672
801
  resume,
673
802
  resumeScope,
674
803
  paused,
@@ -696,8 +825,8 @@ export function useChatComposerController({
696
825
  handlePaste,
697
826
  handleFileChange,
698
827
  focusInput: () => textareaRef.current?.focus(),
699
- setValue: delivery.setValue,
700
- value: delivery.value,
828
+ setValue: setComposerValue,
829
+ value: visibleValue,
701
830
  };
702
831
  }
703
832
 
@@ -909,8 +1038,11 @@ export function Attachments() {
909
1038
  <AttachmentChips
910
1039
  attachments={controller.attachments.attachments}
911
1040
  messages={controller.messages}
1041
+ disabled={controller.disabled}
912
1042
  onRemove={controller.attachments.remove}
913
1043
  onRetry={controller.attachments.retry}
1044
+ onRetainPreview={controller.attachments.retainPreview}
1045
+ onLoadPreview={controller.attachments.loadPreview}
914
1046
  />
915
1047
  );
916
1048
  }
@@ -928,6 +1060,26 @@ type OwnedInputProps =
928
1060
  | "aria-keyshortcuts";
929
1061
  export type ComposerInputProps = Omit<ComponentPropsWithoutRef<"textarea">, OwnedInputProps>;
930
1062
 
1063
+ const EDITING_FOCUS_SELECTOR =
1064
+ 'input, textarea, select, [contenteditable="true"], [role="textbox"]';
1065
+ const POPUP_FOCUS_SELECTOR =
1066
+ '[role="menu"], [role="menubar"], [role="listbox"], [role="dialog"], [role="alertdialog"]';
1067
+
1068
+ /**
1069
+ * Initial focus is a convenience, not authority to move focus the person has
1070
+ * placed elsewhere. It yields to a field they started editing and to an open
1071
+ * menu, listbox, or dialog that does not contain the composer: moving focus
1072
+ * behind a non-modal popup dismisses it, and a modal surface owns focus until
1073
+ * it closes.
1074
+ */
1075
+ function focusBelongsElsewhere(textarea: HTMLTextAreaElement): boolean {
1076
+ const active = textarea.ownerDocument.activeElement;
1077
+ if (!active || active === textarea) return false;
1078
+ if (active.closest(EDITING_FOCUS_SELECTOR)) return true;
1079
+ const popup = active.closest(POPUP_FOCUS_SELECTOR);
1080
+ return popup !== null && !popup.contains(textarea);
1081
+ }
1082
+
931
1083
  export const Input = forwardRef<HTMLTextAreaElement, ComposerInputProps>(function ComposerInput(
932
1084
  { rows = 1, placeholder, className, "aria-label": ariaLabel, autoFocus = false, ...props },
933
1085
  forwardedRef,
@@ -937,14 +1089,17 @@ export const Input = forwardRef<HTMLTextAreaElement, ComposerInputProps>(functio
937
1089
  const paletteOpen =
938
1090
  controller.paletteEnabled && controller.paletteMounted && controller.palette.open;
939
1091
 
940
- // Native autoFocus loses when the textarea mounts disabled (create-session draft
941
- // hydrate). Retry once the controller becomes interactive.
1092
+ // Focus once the controller is interactive. The textarea can mount disabled
1093
+ // (create-session draft hydrate), and the route can finish loading after the
1094
+ // person has already moved on, so this deliberately does not use the native
1095
+ // autoFocus attribute: that would take focus at mount without the guard.
942
1096
  useEffect(() => {
943
1097
  if (!autoFocus || controller.disabled || autoFocusedRef.current) return;
944
1098
  const frame = window.requestAnimationFrame(() => {
945
1099
  const textarea = controller.textareaRef.current;
946
1100
  if (!textarea || textarea.disabled) return;
947
1101
  autoFocusedRef.current = true;
1102
+ if (focusBelongsElsewhere(textarea)) return;
948
1103
  textarea.focus();
949
1104
  });
950
1105
  return () => window.cancelAnimationFrame(frame);
@@ -965,7 +1120,6 @@ export const Input = forwardRef<HTMLTextAreaElement, ComposerInputProps>(functio
965
1120
  : (placeholder ?? controller.messages.messagePlaceholder)
966
1121
  }
967
1122
  disabled={controller.disabled}
968
- autoFocus={autoFocus && !controller.disabled}
969
1123
  aria-label={ariaLabel ?? controller.messages.inputLabel}
970
1124
  aria-keyshortcuts="Enter Meta+Enter Control+Enter Shift+Enter"
971
1125
  aria-autocomplete={
@@ -1176,12 +1330,19 @@ export const PauseButton = forwardRef<HTMLButtonElement, ComposerPauseButtonProp
1176
1330
  ref,
1177
1331
  ) {
1178
1332
  const controller = useComposerController();
1179
- if (!controller.effectiveControl || controller.paused || !controller.hasControl) return null;
1333
+ if (
1334
+ controller.runControl !== "pause" ||
1335
+ !controller.effectiveControl ||
1336
+ controller.paused ||
1337
+ !controller.hasControl
1338
+ )
1339
+ return null;
1180
1340
  const busy = controller.pausing || controller.resuming;
1181
1341
  const tip = title ?? controller.messages.pauseTitle;
1182
1342
  return (
1183
1343
  <ComposerTip tip={tip}>
1184
1344
  <button
1345
+ data-analytics-action="pause"
1185
1346
  {...props}
1186
1347
  ref={mergeRefs(controller.pauseButtonRef, ref)}
1187
1348
  type="button"
@@ -1208,6 +1369,62 @@ export const PauseButton = forwardRef<HTMLButtonElement, ComposerPauseButtonProp
1208
1369
  },
1209
1370
  );
1210
1371
 
1372
+ export type ComposerStopButtonProps = Omit<
1373
+ ButtonHTMLAttributes<HTMLButtonElement>,
1374
+ OwnedButtonProps
1375
+ >;
1376
+
1377
+ /**
1378
+ * Stop the running response. Rendered only under `runControl="stop"` while a
1379
+ * response runs; stopping pauses this conversation and the next message
1380
+ * continues it.
1381
+ */
1382
+ export const StopButton = forwardRef<HTMLButtonElement, ComposerStopButtonProps>(
1383
+ function ComposerStopButton(
1384
+ { className, "aria-label": ariaLabel, title, children, ...props },
1385
+ ref,
1386
+ ) {
1387
+ const controller = useComposerController();
1388
+ if (
1389
+ controller.runControl !== "stop" ||
1390
+ !controller.running ||
1391
+ !controller.hasControl ||
1392
+ controller.effectiveControl?.state === "paused"
1393
+ )
1394
+ return null;
1395
+ const busy = controller.pausing;
1396
+ const label = ariaLabel ?? controller.messages.stopAriaLabel ?? "Stop";
1397
+ const tip = title ?? controller.messages.stopTitle ?? label;
1398
+ return (
1399
+ <ComposerTip tip={tip}>
1400
+ <button
1401
+ data-analytics-action="stop"
1402
+ data-og-composer-stop=""
1403
+ {...props}
1404
+ ref={ref}
1405
+ type="button"
1406
+ onClick={() => void controller.stop()}
1407
+ disabled={busy}
1408
+ aria-label={label}
1409
+ className={cn(
1410
+ "inline-flex size-8 items-center justify-center rounded-og-md border border-og-border pointer-coarse:size-11",
1411
+ "bg-og-surface-1 text-og-fg transition-colors duration-150",
1412
+ "hover:bg-og-surface-2 disabled:opacity-50",
1413
+ className,
1414
+ )}
1415
+ >
1416
+ {children ??
1417
+ (busy ? (
1418
+ <LoaderCircleIcon className="size-3.5 animate-og-spin" />
1419
+ ) : (
1420
+ <SquareIcon className="size-3 fill-current" />
1421
+ ))}
1422
+ </button>
1423
+ </ComposerTip>
1424
+ );
1425
+ },
1426
+ );
1427
+
1211
1428
  export type ComposerSendButtonProps = Omit<
1212
1429
  ButtonHTMLAttributes<HTMLButtonElement>,
1213
1430
  OwnedButtonProps
@@ -1221,10 +1438,15 @@ export const SendButton = forwardRef<HTMLButtonElement, ComposerSendButtonProps>
1221
1438
  const controller = useComposerController();
1222
1439
  const tip =
1223
1440
  title ??
1224
- (controller.paused ? controller.messages.sendAndResumeTitle : controller.messages.sendTitle);
1441
+ (controller.submitBlocker === "annotations"
1442
+ ? controller.messages.annotationNotesRequired
1443
+ : controller.paused
1444
+ ? controller.messages.sendAndResumeTitle
1445
+ : controller.messages.sendTitle);
1225
1446
  return (
1226
1447
  <ComposerTip tip={tip}>
1227
1448
  <button
1449
+ data-analytics-action="send"
1228
1450
  {...props}
1229
1451
  ref={ref}
1230
1452
  type="button"
@@ -1239,10 +1461,10 @@ export const SendButton = forwardRef<HTMLButtonElement, ComposerSendButtonProps>
1239
1461
  }
1240
1462
  className={cn(
1241
1463
  "inline-flex size-8 items-center justify-center rounded-og-md pointer-coarse:size-11",
1242
- "bg-og-accent text-og-accent-fg shadow-og-sm",
1464
+ "border border-og-primary-border bg-og-primary text-og-primary-fg",
1243
1465
  "transition-[background-color,transform,opacity] duration-150 ease-og-spring",
1244
- "hover:bg-og-accent-strong active:scale-95",
1245
- "disabled:cursor-not-allowed disabled:bg-og-surface-3 disabled:text-og-fg-subtle disabled:shadow-none",
1466
+ "hover:bg-og-primary-hover active:scale-95",
1467
+ "disabled:cursor-not-allowed disabled:opacity-50",
1246
1468
  className,
1247
1469
  )}
1248
1470
  >
@@ -1399,7 +1621,7 @@ function WorkstreamPausedStrip({
1399
1621
  <button
1400
1622
  type="button"
1401
1623
  aria-label={messages.resumeThisWorkstream}
1402
- className="inline-flex min-h-8 shrink-0 items-center gap-1.5 rounded-og-md border border-og-status-waiting/35 bg-og-surface-1 px-2.5 text-og-xs font-medium text-og-fg hover:bg-og-surface-2 pointer-coarse:min-h-11"
1624
+ className="inline-flex min-h-8 shrink-0 items-center gap-1.5 rounded-og-md border border-og-primary-border bg-og-primary px-2.5 text-og-xs font-medium text-og-primary-fg hover:bg-og-primary-hover disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:min-h-11"
1403
1625
  disabled={busy}
1404
1626
  onClick={onResume}
1405
1627
  >
@@ -1599,20 +1821,88 @@ function ConfirmBar({
1599
1821
  function AttachmentChips({
1600
1822
  attachments,
1601
1823
  messages,
1824
+ disabled,
1602
1825
  onRemove,
1603
1826
  onRetry,
1827
+ onRetainPreview,
1828
+ onLoadPreview,
1604
1829
  }: {
1605
1830
  attachments: UseFileAttachmentsResult["attachments"];
1606
1831
  messages: ChatComposerMessages;
1832
+ disabled: boolean;
1607
1833
  onRemove: (id: string) => void;
1608
1834
  onRetry?: ((id: string) => void) | undefined;
1835
+ onRetainPreview: UseFileAttachmentsResult["retainPreview"];
1836
+ onLoadPreview: UseFileAttachmentsResult["loadPreview"];
1609
1837
  }) {
1838
+ const lightbox = useLightboxOptional();
1839
+ const previewRequest = useRef<AbortController | null>(null);
1840
+ const [previewLoadingId, setPreviewLoadingId] = useState<string | null>(null);
1841
+ const [previewErrorId, setPreviewErrorId] = useState<string | null>(null);
1842
+
1843
+ useEffect(() => () => previewRequest.current?.abort(), [onLoadPreview]);
1844
+
1845
+ async function openPreview(
1846
+ attachment: UseFileAttachmentsResult["attachments"][number],
1847
+ source: HTMLButtonElement,
1848
+ canLoadPreview: boolean,
1849
+ ) {
1850
+ previewRequest.current?.abort();
1851
+ const attachmentId = attachment.id;
1852
+ const releaseSource = onRetainPreview(attachmentId);
1853
+ let src = attachment.previewUrl;
1854
+ if (!releaseSource && canLoadPreview) {
1855
+ const controller = new AbortController();
1856
+ previewRequest.current = controller;
1857
+ setPreviewLoadingId(attachmentId);
1858
+ setPreviewErrorId(null);
1859
+ try {
1860
+ src = await onLoadPreview!(attachmentId, controller.signal);
1861
+ if (controller.signal.aborted) return;
1862
+ if (!src) {
1863
+ setPreviewErrorId(attachmentId);
1864
+ return;
1865
+ }
1866
+ } catch {
1867
+ if (!controller.signal.aborted) setPreviewErrorId(attachmentId);
1868
+ return;
1869
+ } finally {
1870
+ if (previewRequest.current === controller) {
1871
+ setPreviewLoadingId(null);
1872
+ }
1873
+ }
1874
+ }
1875
+ lightbox!.open(
1876
+ src!,
1877
+ attachment.name,
1878
+ source,
1879
+ messages.attachmentPreviewLabel,
1880
+ attachment.name,
1881
+ {
1882
+ download: messages.downloadAttachment(attachment.name),
1883
+ close: messages.closeAttachmentPreview,
1884
+ },
1885
+ releaseSource,
1886
+ );
1887
+ }
1888
+
1610
1889
  return (
1611
1890
  <div className="flex flex-wrap gap-2 px-3 py-2">
1612
1891
  {attachments.map((attachment) => {
1613
1892
  const failed = attachment.status === "failed";
1614
- const statusText =
1615
- attachment.status === "uploading"
1893
+ const secureContextRequired = attachment.errorCode === "secure_context_required";
1894
+ const previewFailed = previewErrorId === attachment.id;
1895
+ const previewLoading = previewLoadingId === attachment.id;
1896
+ const canLoadPreview = Boolean(
1897
+ lightbox &&
1898
+ onLoadPreview &&
1899
+ attachment.status === "ready" &&
1900
+ attachment.file &&
1901
+ attachment.contentType.startsWith("image/"),
1902
+ );
1903
+ const statusText = previewFailed
1904
+ ? (messages.previewUnavailable ?? "Preview unavailable")
1905
+ : attachment.status === "uploading"
1616
1906
  ? messages.uploading
1617
1907
  : failed
1618
1908
  ? attachment.error || messages.uploadFailed
@@ -1621,13 +1911,44 @@ function AttachmentChips({
1621
1911
  <div
1622
1912
  key={attachment.id}
1623
1913
  className={cn(
1624
- "flex min-w-0 max-w-[240px] items-center gap-2 rounded-og-md border px-2 py-1.5 text-og-sm",
1914
+ "flex min-w-0 items-center gap-2 rounded-og-md border px-2 py-1.5 text-og-sm",
1625
1915
  failed
1626
- ? "border-og-status-failed/40 bg-og-status-failed/10"
1627
- : "border-og-border bg-og-surface-2",
1916
+ ? cn(
1917
+ "border-og-status-failed/40 bg-og-status-failed/10",
1918
+ secureContextRequired
1919
+ ? "max-w-full items-start sm:max-w-[32rem]"
1920
+ : "max-w-[240px]",
1921
+ )
1922
+ : "max-w-[240px] border-og-border bg-og-surface-2",
1628
1923
  )}
1629
1924
  >
1630
- {attachment.previewUrl ? (
1925
+ {lightbox && (attachment.previewUrl || canLoadPreview) ? (
1926
+ <button
1927
+ type="button"
1928
+ className={cn(
1929
+ "size-8 shrink-0 overflow-hidden rounded outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent",
1930
+ !attachment.previewUrl && "flex items-center justify-center disabled:cursor-wait",
1931
+ )}
1932
+ aria-label={messages.previewAttachment(attachment.name)}
1933
+ aria-busy={previewLoading || undefined}
1934
+ disabled={previewLoading}
1935
+ onClick={(event) =>
1936
+ void openPreview(attachment, event.currentTarget, canLoadPreview)
1937
+ }
1938
+ >
1939
+ {attachment.previewUrl ? (
1940
+ <img
1941
+ src={attachment.previewUrl}
1942
+ alt=""
1943
+ className="h-full w-full object-cover transition-opacity hover:opacity-80"
1944
+ />
1945
+ ) : previewLoading ? (
1946
+ <LoaderCircleIcon className="size-4 animate-og-spin text-og-fg-muted" />
1947
+ ) : (
1948
+ <ImageIcon className="size-4 text-og-fg-muted" />
1949
+ )}
1950
+ </button>
1951
+ ) : attachment.previewUrl ? (
1631
1952
  <img
1632
1953
  src={attachment.previewUrl}
1633
1954
  alt=""
@@ -1640,7 +1961,11 @@ function AttachmentChips({
1640
1961
  )}
1641
1962
  <div className="min-w-0 flex-1">
1642
1963
  <div className="truncate font-medium text-og-fg">{attachment.name}</div>
1643
- {failed ? (
1964
+ {secureContextRequired ? (
1965
+ <div className="break-words text-og-xs leading-4 text-og-status-failed">
1966
+ {statusText}
1967
+ </div>
1968
+ ) : failed || previewFailed ? (
1644
1969
  <ComposerTip tip={statusText}>
1645
1970
  <div className="truncate text-og-xs text-og-status-failed">{statusText}</div>
1646
1971
  </ComposerTip>
@@ -1651,12 +1976,13 @@ function AttachmentChips({
1651
1976
  {attachment.status === "uploading" ? (
1652
1977
  <LoaderCircleIcon className="size-3.5 shrink-0 animate-og-spin" />
1653
1978
  ) : null}
1654
- {failed && onRetry ? (
1979
+ {failed && !secureContextRequired && onRetry ? (
1655
1980
  <ComposerTip tip={messages.retryUpload}>
1656
1981
  <button
1657
1982
  type="button"
1658
1983
  onClick={() => onRetry(attachment.id)}
1659
- className="shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg pointer-coarse:size-10"
1984
+ disabled={disabled}
1985
+ className="shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:size-10"
1660
1986
  aria-label={messages.retryAttachment(attachment.name)}
1661
1987
  >
1662
1988
  <RotateCwIcon className="size-3.5" />
@@ -1666,7 +1992,8 @@ function AttachmentChips({
1666
1992
  <button
1667
1993
  type="button"
1668
1994
  onClick={() => onRemove(attachment.id)}
1669
- className="shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg pointer-coarse:size-10"
1995
+ disabled={disabled}
1996
+ className="shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:size-10"
1670
1997
  aria-label={messages.removeAttachment(attachment.name)}
1671
1998
  >
1672
1999
  <XIcon className="size-3.5" />