@opengeni/react 6.5.2-canary.41607687019001 → 6.7.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 (1154) hide show
  1. package/dist/app/app-state.d.ts +4 -0
  2. package/dist/app/app-state.js +19 -14
  3. package/dist/app/components/Composer.js +25 -23
  4. package/dist/app/components/agent/agent-capability-picker.js +10 -8
  5. package/dist/app/components/artifacts/artifact-browse-controls.js +4 -4
  6. package/dist/app/components/artifacts/artifact-library.js +21 -19
  7. package/dist/app/components/artifacts/artifact-page-chrome.js +3 -3
  8. package/dist/app/components/artifacts/artifact-viewer.js +23 -22
  9. package/dist/app/components/artifacts/artifact-viewer.js.map +1 -1
  10. package/dist/app/components/artifacts/chat-interactive-block.js +12 -12
  11. package/dist/app/components/artifacts/copy-artifact-link.js +7 -5
  12. package/dist/app/components/artifacts/file-reach-dialog.js +11 -9
  13. package/dist/app/components/artifacts/inline-chat-image.js +18 -18
  14. package/dist/app/components/artifacts/retained-file-preview.js +24 -23
  15. package/dist/app/components/artifacts/retained-text-preview.js +3 -3
  16. package/dist/app/components/artifacts/site-audience.js +6 -5
  17. package/dist/app/components/artifacts/site-conversations.js +16 -14
  18. package/dist/app/components/artifacts/site-data.js +18 -16
  19. package/dist/app/components/artifacts/site-public-link.js +3 -2
  20. package/dist/app/components/artifacts/site-publishing-page.js +39 -37
  21. package/dist/app/components/artifacts/site-publishing.js +34 -32
  22. package/dist/app/components/artifacts/site-settings.js +38 -36
  23. package/dist/app/components/artifacts/site-version-menu.js +8 -7
  24. package/dist/app/components/billing-class-mark.js +3 -3
  25. package/dist/app/components/bots/bot-launch-strip.js +17 -17
  26. package/dist/app/components/bots/bot-template-dialog.js +30 -28
  27. package/dist/app/components/bots/bot-template-shortcuts.js +19 -19
  28. package/dist/app/components/bots/bot-templates-page.js +38 -36
  29. package/dist/app/components/bots/bot-templates-page.js.map +1 -1
  30. package/dist/app/components/capabilities/capability-detail-sheet.js +13 -11
  31. package/dist/app/components/capabilities/capability-logo-source.js +13 -13
  32. package/dist/app/components/capabilities/capability-logo.js +4 -3
  33. package/dist/app/components/capabilities/connector-tool-permissions.js +8 -6
  34. package/dist/app/components/capabilities/native-connect-setup.js +14 -14
  35. package/dist/app/components/capabilities/own-logins-shared-heads-up.js +7 -6
  36. package/dist/app/components/capabilities/perform-capability-action.js +7 -6
  37. package/dist/app/components/capabilities/prepared-mcp-setup-card.js +17 -17
  38. package/dist/app/components/capabilities/session-capability-card.js +72 -70
  39. package/dist/app/components/capabilities/session-capability-card.js.map +1 -1
  40. package/dist/app/components/capabilities/session-chrome-card.js +28 -26
  41. package/dist/app/components/capabilities/session-connection-accounts.js +4 -3
  42. package/dist/app/components/capabilities/session-custom-mcp-card.js +20 -19
  43. package/dist/app/components/capabilities/session-github-card.js +22 -20
  44. package/dist/app/components/capabilities/session-machine-card.js +27 -25
  45. package/dist/app/components/capabilities/session-personal-github-card.js +16 -16
  46. package/dist/app/components/capabilities/session-plugin-card.js +10 -9
  47. package/dist/app/components/capabilities/use-capabilities-catalog.js +4 -3
  48. package/dist/app/components/capabilities/use-connection-accounts.js +5 -4
  49. package/dist/app/components/command-menu/chat-status.js +15 -13
  50. package/dist/app/components/command-menu/chat-switcher.js +35 -33
  51. package/dist/app/components/command-menu/chat-switcher.js.map +1 -1
  52. package/dist/app/components/command-menu/command-menu.js +53 -50
  53. package/dist/app/components/command-menu/command-menu.js.map +1 -1
  54. package/dist/app/components/command-menu/shortcut-sheet.js +18 -16
  55. package/dist/app/components/command-menu/shortcut-sheet.js.map +1 -1
  56. package/dist/app/components/common.js +4 -3
  57. package/dist/app/components/composer-capabilities-menu-body.js +4 -4
  58. package/dist/app/components/composer-mobile-plus-panel.js +8 -8
  59. package/dist/app/components/composer-mobile-plus.js +1 -1
  60. package/dist/app/components/credit-required-gate.js +3 -3
  61. package/dist/app/components/credit-required-prompt.js +10 -8
  62. package/dist/app/components/credit-required-prompt.js.map +1 -1
  63. package/dist/app/components/credits/checkout-credits-celebration.js +3 -2
  64. package/dist/app/components/credits/checkout-return.js +3 -2
  65. package/dist/app/components/credits/checkout-return.js.map +1 -1
  66. package/dist/app/components/credits/coupon-redeem.js +5 -4
  67. package/dist/app/components/credits/credit-refusal-notice.js +7 -5
  68. package/dist/app/components/cross-slot-deep-link-recovery.js +5 -4
  69. package/dist/app/components/desktop/app-shots.js +13 -11
  70. package/dist/app/components/desktop/computer-use.js +6 -4
  71. package/dist/app/components/desktop/this-computer.js +9 -7
  72. package/dist/app/components/feedback.js +5 -4
  73. package/dist/app/components/follow-up-repository-menu-body.js +8 -6
  74. package/dist/app/components/follow-up-repository-menu-body.js.map +1 -1
  75. package/dist/app/components/follow-up-repository-picker.js +1 -1
  76. package/dist/app/components/github-app-connect-launcher.js +5 -4
  77. package/dist/app/components/in-app-help-link.js +3 -3
  78. package/dist/app/components/knowledge/agent-learning-settings.js +3 -2
  79. package/dist/app/components/knowledge/agent-learning-settings.js.map +1 -1
  80. package/dist/app/components/machines/connect-machine-dialog.js +18 -16
  81. package/dist/app/components/manual-repository-editor.js +3 -2
  82. package/dist/app/components/markdown.js +6 -6
  83. package/dist/app/components/models/models-ui.js +12 -10
  84. package/dist/app/components/my-things.js +12 -10
  85. package/dist/app/components/my-things.js.map +1 -1
  86. package/dist/app/components/new-session-starters.js +3 -3
  87. package/dist/app/components/own-where-it-works.js +9 -7
  88. package/dist/app/components/personal-resource-attachment-surface.js +4 -4
  89. package/dist/app/components/pickers.js +5 -5
  90. package/dist/app/components/rail/channel-create-dialog.js +7 -5
  91. package/dist/app/components/rail/project-glyph.js +17 -17
  92. package/dist/app/components/rail/project-profile.js +31 -29
  93. package/dist/app/components/rail/rail-context.js +3 -3
  94. package/dist/app/components/rail/rail-sections.js +68 -66
  95. package/dist/app/components/rail/session-header.js +59 -57
  96. package/dist/app/components/rail/session-list.js +66 -64
  97. package/dist/app/components/rail/session-row-content.js +4 -4
  98. package/dist/app/components/rail/session-row-hover-details.js +14 -12
  99. package/dist/app/components/rail/session-shell.js +90 -88
  100. package/dist/app/components/rail/site-session-group-heading.js +8 -8
  101. package/dist/app/components/repository-picker.js +8 -6
  102. package/dist/app/components/secrets/session-secrets-panel.js +4 -3
  103. package/dist/app/components/secrets/vault-model.js +3 -2
  104. package/dist/app/components/session/agent-configuration-panel.js +25 -23
  105. package/dist/app/components/session/agent-configuration-panel.js.map +1 -1
  106. package/dist/app/components/session/artifact-link-boundary.js +3 -3
  107. package/dist/app/components/session/artifact-session-page.js +11 -9
  108. package/dist/app/components/session/banners.js +8 -8
  109. package/dist/app/components/session/codex-usage-notice.js +5 -3
  110. package/dist/app/components/session/console-new-conversation.js +42 -40
  111. package/dist/app/components/session/context-text-reader.js +3 -2
  112. package/dist/app/components/session/descendant-requests.js +39 -37
  113. package/dist/app/components/session/descendant-requests.js.map +1 -1
  114. package/dist/app/components/session/editable-artifacts-workspace.js +36 -34
  115. package/dist/app/components/session/editable-artifacts-workspace.js.map +1 -1
  116. package/dist/app/components/session/failed-session-banner.js +13 -11
  117. package/dist/app/components/session/failed-session-banner.js.map +1 -1
  118. package/dist/app/components/session/failure-recovery-boundary.js +3 -3
  119. package/dist/app/components/session/human-input-delegation-record.js +2 -2
  120. package/dist/app/components/session/human-input.js +33 -31
  121. package/dist/app/components/session/inspector.js +57 -55
  122. package/dist/app/components/session/inspector.js.map +1 -1
  123. package/dist/app/components/session/message-actions.js +5 -4
  124. package/dist/app/components/session/message-actions.js.map +1 -1
  125. package/dist/app/components/session/message-resource-attachments.js +12 -11
  126. package/dist/app/components/session/message-resource-attachments.js.map +1 -1
  127. package/dist/app/components/session/model-context-inspector.js +6 -4
  128. package/dist/app/components/session/model-recovery-notice.js +3 -3
  129. package/dist/app/components/session/new-session-settings-menu.js +11 -9
  130. package/dist/app/components/session/new-session-setup-strip.js +18 -18
  131. package/dist/app/components/session/sandbox-recovery-actions.js +3 -3
  132. package/dist/app/components/session/sandbox-switcher.js +10 -9
  133. package/dist/app/components/session/sandbox-workspace.js +47 -46
  134. package/dist/app/components/session/search-markdown-highlight.js +7 -7
  135. package/dist/app/components/session/search-results-view.js +8 -8
  136. package/dist/app/components/session/session-admin-access.js +13 -11
  137. package/dist/app/components/session/session-admission-notice.js +7 -5
  138. package/dist/app/components/session/session-avatar-picker.js +22 -20
  139. package/dist/app/components/session/session-avatar.js +16 -16
  140. package/dist/app/components/session/session-machine-access-notice.js +7 -5
  141. package/dist/app/components/session/session-profile-dialog.js +29 -27
  142. package/dist/app/components/session/session-profile-editor.js +30 -28
  143. package/dist/app/components/session/session-retention.js +10 -8
  144. package/dist/app/components/session/session-search-dialog.js +10 -10
  145. package/dist/app/components/session/session-skill-reviews.js +2 -1
  146. package/dist/app/components/session/session-skill-reviews.js.map +1 -1
  147. package/dist/app/components/session/session-tenancy-control.js +13 -11
  148. package/dist/app/components/session/session-tenancy-control.js.map +1 -1
  149. package/dist/app/components/session/session-unattended-setting.js +7 -5
  150. package/dist/app/components/session/session-usage-limit-section.js +11 -9
  151. package/dist/app/components/session/share-chat-dialog.js +9 -7
  152. package/dist/app/components/session/site-origin-link.js +3 -3
  153. package/dist/app/components/session/subagents.js +8 -8
  154. package/dist/app/components/session/subscription-account-indicator.js +6 -6
  155. package/dist/app/components/session-connectors-menu-body.js +4 -3
  156. package/dist/app/components/session-connectors-menu-body.js.map +1 -1
  157. package/dist/app/components/settings/row-select.js +7 -5
  158. package/dist/app/components/toaster.js +3 -2
  159. package/dist/app/components/transcription-settings.js +11 -9
  160. package/dist/app/components/ui/destructive-confirm.js +11 -9
  161. package/dist/app/components/usage/usage-entry.js +5 -5
  162. package/dist/app/components/window-bar.js +16 -14
  163. package/dist/app/components/workspace-composer-plus.js +22 -21
  164. package/dist/app/components/workspace-reach.js +6 -4
  165. package/dist/app/components/workspace-tenant-boundary.js +5 -4
  166. package/dist/app/embed/memory-navigation.js +3 -3
  167. package/dist/app/embed/session-app.js +211 -209
  168. package/dist/app/lib/actions/actions-provider.js +15 -13
  169. package/dist/app/lib/agent-capabilities.js +3 -3
  170. package/dist/app/lib/app-surfaces.js +7 -6
  171. package/dist/app/lib/archive-session.d.ts +9 -4
  172. package/dist/app/lib/archive-session.js +4 -2
  173. package/dist/app/lib/bot-templates.js +3 -2
  174. package/dist/app/lib/capabilities.js +3 -2
  175. package/dist/app/lib/codex-usage-query.js +2 -2
  176. package/dist/app/lib/composer-connectors.js +17 -16
  177. package/dist/app/lib/console-site.js +2 -2
  178. package/dist/app/lib/console-text.js +2 -2
  179. package/dist/app/lib/events.js +9 -9
  180. package/dist/app/lib/failed-session-copy.js +3 -3
  181. package/dist/app/lib/inbox.js +4 -2
  182. package/dist/app/lib/model-access-onboarding.js +3 -3
  183. package/dist/app/lib/move-chat-to-machine.js +3 -3
  184. package/dist/app/lib/navigation.js +2 -2
  185. package/dist/app/lib/session-branch-cache.js +6 -6
  186. package/dist/app/lib/session-create.js +4 -4
  187. package/dist/app/lib/session-group-pagination.js +6 -6
  188. package/dist/app/lib/session-hover-facts.js +13 -11
  189. package/dist/app/lib/session-model.js +2 -2
  190. package/dist/app/lib/session-rail.js +3 -3
  191. package/dist/app/lib/session-rename.js +3 -3
  192. package/dist/app/lib/session-restart-operation-controller.js +4 -3
  193. package/dist/app/lib/session-tenancy.js +3 -2
  194. package/dist/app/lib/session-usage-limit.js +3 -2
  195. package/dist/app/lib/sessions-group.js +5 -5
  196. package/dist/app/lib/site-publishing.js +3 -2
  197. package/dist/app/lib/site-tool-consent-dialog.js +9 -7
  198. package/dist/app/lib/site-tool-consent-dialog.js.map +1 -1
  199. package/dist/app/lib/site-tool-consent.js +1 -1
  200. package/dist/app/lib/subscription-plans.js +3 -2
  201. package/dist/app/lib/toast.js +2 -1
  202. package/dist/app/lib/turn-failure-analytics.js +4 -4
  203. package/dist/app/lib/use-duplicate-site.js +6 -5
  204. package/dist/app/lib/use-follow-up-repositories.js +6 -5
  205. package/dist/app/lib/use-workspace-integrations.js +4 -3
  206. package/dist/app/lib/use-workspace-machines.js +4 -4
  207. package/dist/app/lib/use-workspace-model-catalog.js +5 -4
  208. package/dist/app/lib/use-workspace-rigs.js +2 -2
  209. package/dist/app/lib/workspace-navigation-preference.js +4 -4
  210. package/dist/app/lib/workspace-scope-context.js +3 -3
  211. package/dist/app/routes/artifacts.js +67 -66
  212. package/dist/app/routes/artifacts.js.map +1 -1
  213. package/dist/app/routes/retained-artifact.js +42 -40
  214. package/dist/app/routes/retained-artifact.js.map +1 -1
  215. package/dist/app/routes/session-unavailable.js +8 -7
  216. package/dist/app/routes/session.js +119 -117
  217. package/dist/app/routes/sessions-index-hydration.js +5 -5
  218. package/dist/app/routes/sessions-index.js +104 -102
  219. package/dist/app/routes/site-view.js +67 -65
  220. package/dist/app/routes/site-view.js.map +1 -1
  221. package/dist/artifacts-document.js +4 -4
  222. package/dist/artifacts-presentation.js +4 -4
  223. package/dist/artifacts-spreadsheet.js +4 -4
  224. package/dist/artifacts.js +10 -10
  225. package/dist/automations.js +17 -15
  226. package/dist/automations.js.map +1 -1
  227. package/dist/browser-viewer-TC27OCIP.js +23 -0
  228. package/dist/chat-interactive-block-RWPQBHVS.js +18 -0
  229. package/dist/chunk-2BDT3UZQ.js +229 -0
  230. package/dist/chunk-2CLNIEK4.js +373 -0
  231. package/dist/chunk-2FSBSCZJ.js +451 -0
  232. package/dist/chunk-2I7HDP53.js +199 -0
  233. package/dist/chunk-2INLDVK3.js +15 -0
  234. package/dist/chunk-2KALVELR.js +126 -0
  235. package/dist/chunk-2M3J7ZWQ.js +418 -0
  236. package/dist/chunk-2OP6WLGV.js +708 -0
  237. package/dist/chunk-2RG7NIAS.js +2936 -0
  238. package/dist/chunk-2STUPWEL.js +135 -0
  239. package/dist/chunk-2YUOVM3G.js +271 -0
  240. package/dist/chunk-2Z3NQNTM.js +3670 -0
  241. package/dist/chunk-3CPWWGNK.js +920 -0
  242. package/dist/chunk-3ED2U6XE.js +37 -0
  243. package/dist/chunk-3GZA4BUW.js +216 -0
  244. package/dist/chunk-3IK6UYO6.js +1022 -0
  245. package/dist/chunk-3PZ7JZI2.js +431 -0
  246. package/dist/chunk-4DVJ6ONS.js +50 -0
  247. package/dist/chunk-4DVNUCRR.js +2386 -0
  248. package/dist/chunk-4DVNUCRR.js.map +1 -0
  249. package/dist/chunk-4E3ZJRTZ.js +41 -0
  250. package/dist/chunk-4SAWSRAA.js +141 -0
  251. package/dist/chunk-53AGXXZM.js +307 -0
  252. package/dist/chunk-54YW6SZZ.js +818 -0
  253. package/dist/chunk-57AZS3IE.js +2317 -0
  254. package/dist/chunk-5CPEEAFD.js +143 -0
  255. package/dist/chunk-5EWSRZ2Y.js +340 -0
  256. package/dist/chunk-5IE76VCY.js +387 -0
  257. package/dist/chunk-5NWEKLDZ.js +170 -0
  258. package/dist/chunk-5O2FCCHZ.js +198 -0
  259. package/dist/chunk-5QAY6K77.js +399 -0
  260. package/dist/chunk-5YKW2K6I.js +625 -0
  261. package/dist/chunk-5YKW2K6I.js.map +1 -0
  262. package/dist/chunk-6BCXNDEI.js +4116 -0
  263. package/dist/chunk-6BCXNDEI.js.map +1 -0
  264. package/dist/chunk-6DK6Z4UN.js +2599 -0
  265. package/dist/chunk-6DVCC7QS.js +702 -0
  266. package/dist/chunk-6JHU2UBS.js +2058 -0
  267. package/dist/chunk-6PZ7OBYU.js +490 -0
  268. package/dist/chunk-6WD2INST.js +539 -0
  269. package/dist/chunk-6WD2INST.js.map +1 -0
  270. package/dist/chunk-6WGRDRMA.js +163 -0
  271. package/dist/chunk-77WAKEAH.js +186 -0
  272. package/dist/chunk-7T2ZE3VT.js +5558 -0
  273. package/dist/chunk-7T2ZE3VT.js.map +1 -0
  274. package/dist/chunk-7TDO3CUM.js +393 -0
  275. package/dist/chunk-7TIX2UAR.js +247 -0
  276. package/dist/chunk-7V5REBCP.js +148 -0
  277. package/dist/chunk-7VMZGLYI.js +339 -0
  278. package/dist/chunk-7W37NS3I.js +236 -0
  279. package/dist/chunk-7X6SDK2C.js +111 -0
  280. package/dist/chunk-7ZNUHRP5.js +95 -0
  281. package/dist/chunk-A36PZALD.js +84 -0
  282. package/dist/chunk-AUI6VA63.js +107 -0
  283. package/dist/chunk-AW2MQB37.js +24 -0
  284. package/dist/chunk-AWKES45Y.js +198 -0
  285. package/dist/chunk-AXHZYXUV.js +132 -0
  286. package/dist/chunk-BHNSJVBC.js +129 -0
  287. package/dist/chunk-BJEJFISW.js +50 -0
  288. package/dist/chunk-BJFH6JI5.js +422 -0
  289. package/dist/chunk-BMICEQZQ.js +960 -0
  290. package/dist/chunk-BTLBYBLU.js +29 -0
  291. package/dist/chunk-BXBDYNRP.js +89 -0
  292. package/dist/chunk-C372BFOV.js +357 -0
  293. package/dist/chunk-C43KRXGA.js +234 -0
  294. package/dist/chunk-C43KRXGA.js.map +1 -0
  295. package/dist/chunk-CMKGILJK.js +4824 -0
  296. package/dist/chunk-CRAOSVHC.js +50 -0
  297. package/dist/chunk-CRU35QQH.js +281 -0
  298. package/dist/chunk-CUIWCGMA.js +172 -0
  299. package/dist/chunk-D5TWNFFE.js +38 -0
  300. package/dist/chunk-DF5EAHDN.js +4461 -0
  301. package/dist/chunk-DJAXC5OK.js +56 -0
  302. package/dist/chunk-DXBYWSL5.js +3550 -0
  303. package/dist/chunk-DXR5PDFP.js +84 -0
  304. package/dist/chunk-DZWARC4M.js +267 -0
  305. package/dist/chunk-DZXEDSQI.js +106 -0
  306. package/dist/chunk-E7XERZWC.js +48 -0
  307. package/dist/chunk-ECJBYNLJ.js +79 -0
  308. package/dist/chunk-EDI6SJ2N.js +43 -0
  309. package/dist/chunk-EWSOC6WF.js +114 -0
  310. package/dist/chunk-F2OPHAUN.js +103 -0
  311. package/dist/chunk-F4ISQGGM.js +81 -0
  312. package/dist/chunk-FO4LYKJL.js +197 -0
  313. package/dist/chunk-FPXASDG5.js +73 -0
  314. package/dist/chunk-FSOQDUCG.js +117 -0
  315. package/dist/chunk-FSOQDUCG.js.map +1 -0
  316. package/dist/chunk-FVXBNBHO.js +157 -0
  317. package/dist/chunk-FYWT2O76.js +34 -0
  318. package/dist/chunk-FYWT2O76.js.map +1 -0
  319. package/dist/chunk-FZC5N55X.js +981 -0
  320. package/dist/chunk-GAVYGBFI.js +99 -0
  321. package/dist/chunk-GFIMOUDT.js +846 -0
  322. package/dist/chunk-GFIMOUDT.js.map +1 -0
  323. package/dist/chunk-GKVJIDHF.js +33 -0
  324. package/dist/chunk-GVN5SZLX.js +212 -0
  325. package/dist/chunk-H3T7IWNA.js +134 -0
  326. package/dist/chunk-HAX4LCR5.js +261 -0
  327. package/dist/chunk-HC4JFZVU.js +121 -0
  328. package/dist/chunk-HEFPPMBQ.js +581 -0
  329. package/dist/chunk-HGKTHF2U.js +1356 -0
  330. package/dist/chunk-HGKTHF2U.js.map +1 -0
  331. package/dist/chunk-HNLR7Q4Z.js +83 -0
  332. package/dist/chunk-HSOXMJOT.js +12 -0
  333. package/dist/chunk-I4MRDSUF.js +281 -0
  334. package/dist/chunk-I4QEJ62Y.js +202 -0
  335. package/dist/chunk-I5B5L6Q4.js +56 -0
  336. package/dist/chunk-I5QZJODL.js +264 -0
  337. package/dist/chunk-I7ITSXSJ.js +603 -0
  338. package/dist/chunk-I7XIZRFD.js +390 -0
  339. package/dist/chunk-IDANQPRH.js +82 -0
  340. package/dist/chunk-IFB3WJ36.js +212 -0
  341. package/dist/chunk-IHTAKKTK.js +264 -0
  342. package/dist/chunk-IKXNLWAL.js +205 -0
  343. package/dist/chunk-IKXNLWAL.js.map +1 -0
  344. package/dist/chunk-ILDXWY37.js +173 -0
  345. package/dist/chunk-IMDQUHBJ.js +570 -0
  346. package/dist/chunk-IMZCQBOT.js +119 -0
  347. package/dist/chunk-IXPTECSC.js +403 -0
  348. package/dist/chunk-J255A3N5.js +71 -0
  349. package/dist/chunk-J5F5HPPW.js +141 -0
  350. package/dist/chunk-JDTGRRNU.js +5045 -0
  351. package/dist/chunk-JDTGRRNU.js.map +1 -0
  352. package/dist/chunk-JEA3QDX7.js +2469 -0
  353. package/dist/chunk-JEUPAVYY.js +156 -0
  354. package/dist/chunk-JKHKLL5W.js +194 -0
  355. package/dist/chunk-JQL7ZH6C.js +650 -0
  356. package/dist/chunk-JWB44UGJ.js +57 -0
  357. package/dist/chunk-JZ4NLCAP.js +333 -0
  358. package/dist/chunk-K2A5PXQ5.js +412 -0
  359. package/dist/chunk-K56XV3C3.js +623 -0
  360. package/dist/chunk-K6GAXXVA.js +23 -0
  361. package/dist/chunk-KCBMFSMS.js +199 -0
  362. package/dist/chunk-KF2FV3ZJ.js +268 -0
  363. package/dist/chunk-KJD65EJP.js +278 -0
  364. package/dist/chunk-KLEUWIMZ.js +386 -0
  365. package/dist/chunk-KODELWYT.js +309 -0
  366. package/dist/chunk-KRDR5RJD.js +83 -0
  367. package/dist/chunk-KUDGXAF7.js +914 -0
  368. package/dist/chunk-KY2R73CG.js +171 -0
  369. package/dist/chunk-KY2R73CG.js.map +1 -0
  370. package/dist/chunk-KYS77RPN.js +85 -0
  371. package/dist/chunk-L7YTNTIK.js +368 -0
  372. package/dist/chunk-LHPNABDH.js +686 -0
  373. package/dist/chunk-MFROMEKA.js +401 -0
  374. package/dist/chunk-MIHN76LX.js +2656 -0
  375. package/dist/chunk-MIYFS2XN.js +42 -0
  376. package/dist/chunk-MJUCFQMS.js +65 -0
  377. package/dist/chunk-MM4FNWI4.js +3064 -0
  378. package/dist/chunk-MNZG4OHU.js +790 -0
  379. package/dist/chunk-MVW6HNPI.js +250 -0
  380. package/dist/chunk-MVW6HNPI.js.map +1 -0
  381. package/dist/chunk-N6AACDUW.js +200 -0
  382. package/dist/chunk-NC55IFN3.js +781 -0
  383. package/dist/chunk-NC7PVOEK.js +98 -0
  384. package/dist/chunk-NCYMNCJO.js +70 -0
  385. package/dist/chunk-ND2BXV3P.js +259 -0
  386. package/dist/chunk-NDBTFUO6.js +272 -0
  387. package/dist/chunk-NDQJZ7HF.js +206 -0
  388. package/dist/chunk-NGJJJPZZ.js +162 -0
  389. package/dist/chunk-NJTSNNE7.js +251 -0
  390. package/dist/chunk-NPGISCU6.js +4159 -0
  391. package/dist/chunk-NPGISCU6.js.map +1 -0
  392. package/dist/chunk-NRTE7DNV.js +1270 -0
  393. package/dist/chunk-NULUNM7H.js +290 -0
  394. package/dist/chunk-NUNZS36W.js +227 -0
  395. package/dist/chunk-NW53H5RW.js +2424 -0
  396. package/dist/chunk-NXCMHIKI.js +263 -0
  397. package/dist/chunk-O3LKCBP5.js +159 -0
  398. package/dist/chunk-O4EA4F3W.js +103 -0
  399. package/dist/chunk-O4EA4F3W.js.map +1 -0
  400. package/dist/chunk-O4OOG3D6.js +116 -0
  401. package/dist/chunk-O4OOG3D6.js.map +1 -0
  402. package/dist/chunk-O6N4ABCA.js +376 -0
  403. package/dist/chunk-O732GAFC.js +112 -0
  404. package/dist/chunk-OF4TIKYC.js +690 -0
  405. package/dist/chunk-OMPVOMPP.js +70 -0
  406. package/dist/chunk-OPJJSKUK.js +118 -0
  407. package/dist/chunk-OPWCMA4D.js +196 -0
  408. package/dist/chunk-ORJPS5VN.js +1193 -0
  409. package/dist/chunk-OSY5K7L7.js +187 -0
  410. package/dist/chunk-OV6QJRT4.js +45 -0
  411. package/dist/chunk-OVKYVZMW.js +108 -0
  412. package/dist/chunk-OZCBM3UC.js +171 -0
  413. package/dist/chunk-P3A2EINJ.js +61 -0
  414. package/dist/chunk-PTAG4PDC.js +27 -0
  415. package/dist/chunk-PTF5B7TJ.js +298 -0
  416. package/dist/chunk-Q3DPEYX4.js +848 -0
  417. package/dist/chunk-Q6CGOPJD.js +215 -0
  418. package/dist/chunk-Q6CGOPJD.js.map +1 -0
  419. package/dist/chunk-QB2KMIDP.js +100 -0
  420. package/dist/chunk-QCSJ4CWE.js +1067 -0
  421. package/dist/chunk-QIOQ2NPQ.js +414 -0
  422. package/dist/chunk-QK5EFEUT.js +353 -0
  423. package/dist/chunk-QPQBLYQJ.js +41 -0
  424. package/dist/chunk-QPSGCC5D.js +184 -0
  425. package/dist/chunk-QZAAUQRN.js +474 -0
  426. package/dist/chunk-QZM6YDJW.js +489 -0
  427. package/dist/chunk-QZM6YDJW.js.map +1 -0
  428. package/dist/chunk-RGBI63KJ.js +188 -0
  429. package/dist/chunk-RLBKLAEY.js +138 -0
  430. package/dist/chunk-RMFKVOM7.js +304 -0
  431. package/dist/chunk-ROKQU7WG.js +258 -0
  432. package/dist/chunk-RPVREAU7.js +251 -0
  433. package/dist/chunk-RQKLF42C.js +723 -0
  434. package/dist/chunk-SC6RRD2D.js +38 -0
  435. package/dist/chunk-SC6RRD2D.js.map +1 -0
  436. package/dist/chunk-SEO7TICN.js +94 -0
  437. package/dist/chunk-SHHJCL7X.js +432 -0
  438. package/dist/chunk-SIRLACNZ.js +114 -0
  439. package/dist/chunk-SKPHMYAA.js +237 -0
  440. package/dist/chunk-SODC7T6L.js +1583 -0
  441. package/dist/chunk-SQJIKMN3.js +366 -0
  442. package/dist/chunk-SVINDKVW.js +26 -0
  443. package/dist/chunk-SYCDHPZF.js +335 -0
  444. package/dist/chunk-T2QX3FDP.js +133 -0
  445. package/dist/chunk-T3OWFVMS.js +1187 -0
  446. package/dist/chunk-T6XI5FLY.js +61 -0
  447. package/dist/chunk-T7OXJZCX.js +62 -0
  448. package/dist/chunk-TAT5USZN.js +353 -0
  449. package/dist/chunk-TPQTRNER.js +172 -0
  450. package/dist/chunk-TUIN4MB7.js +266 -0
  451. package/dist/chunk-TX3VECZU.js +71 -0
  452. package/dist/chunk-TXLIEOJX.js +9624 -0
  453. package/dist/chunk-U3DN4HEY.js +27 -0
  454. package/dist/chunk-U7XM7CB4.js +191 -0
  455. package/dist/chunk-UJK53QFL.js +140 -0
  456. package/dist/chunk-UMTXA2TH.js +319 -0
  457. package/dist/chunk-UPDSXFK5.js +199 -0
  458. package/dist/chunk-UTIAXH52.js +269 -0
  459. package/dist/chunk-UVN5TMHU.js +1184 -0
  460. package/dist/chunk-V4Z4B2E3.js +52 -0
  461. package/dist/chunk-V4Z4B2E3.js.map +1 -0
  462. package/dist/chunk-VGYXW3VG.js +246 -0
  463. package/dist/chunk-VS7AHPCZ.js +255 -0
  464. package/dist/chunk-VTTAJOQ7.js +121 -0
  465. package/dist/chunk-VWPOLUAO.js +99 -0
  466. package/dist/chunk-X3MWB2QU.js +3851 -0
  467. package/dist/chunk-X6KAJI3V.js +211 -0
  468. package/dist/chunk-XBLRHV5R.js +50 -0
  469. package/dist/chunk-XDFP2LMW.js +605 -0
  470. package/dist/chunk-XEQMZB3C.js +525 -0
  471. package/dist/chunk-XEYLT5VA.js +223 -0
  472. package/dist/chunk-XHIQ5I6N.js +918 -0
  473. package/dist/chunk-XLH7QY2R.js +378 -0
  474. package/dist/chunk-XWFAMUZQ.js +113 -0
  475. package/dist/chunk-Y4BB2KS2.js +294 -0
  476. package/dist/chunk-Y4BB2KS2.js.map +1 -0
  477. package/dist/chunk-Y4OBB2VN.js +84 -0
  478. package/dist/chunk-YCVD6CI3.js +234 -0
  479. package/dist/chunk-YENLNISG.js +270 -0
  480. package/dist/chunk-YIRVLKAX.js +351 -0
  481. package/dist/chunk-YL73CVNK.js +390 -0
  482. package/dist/chunk-YLS6QE5G.js +288 -0
  483. package/dist/chunk-YNF2OC54.js +366 -0
  484. package/dist/chunk-YNF2OC54.js.map +1 -0
  485. package/dist/chunk-YPWU5KPE.js +147 -0
  486. package/dist/chunk-YQXTDZSU.js +2984 -0
  487. package/dist/chunk-YSJEAAOL.js +178 -0
  488. package/dist/chunk-YWIFM7D7.js +499 -0
  489. package/dist/chunk-YWVI6ZMK.js +103 -0
  490. package/dist/chunk-Z4FMAFNH.js +847 -0
  491. package/dist/chunk-Z654FWNU.js +136 -0
  492. package/dist/chunk-Z7Y523PS.js +130 -0
  493. package/dist/chunk-ZD67RM6G.js +60 -0
  494. package/dist/chunk-ZEG4VPBL.js +187 -0
  495. package/dist/chunk-ZHDFQITM.js +283 -0
  496. package/dist/chunk-ZHDFQITM.js.map +1 -0
  497. package/dist/chunk-ZLYCOBHW.js +75 -0
  498. package/dist/chunk-ZNJ5MTXV.js +89 -0
  499. package/dist/chunk-ZSGY3H53.js +155 -0
  500. package/dist/chunk-ZTPXACWB.js +1788 -0
  501. package/dist/chunk-ZUA3JMI6.js +107 -0
  502. package/dist/chunk-ZYAZUPPV.js +559 -0
  503. package/dist/composer.js +8 -8
  504. package/dist/computer-viewer-TDDO25AV.js +21 -0
  505. package/dist/connect.js +12 -12
  506. package/dist/desktop-dictation.js +12 -10
  507. package/dist/desktop-dictation.js.map +1 -1
  508. package/dist/desktop.js +2 -2
  509. package/dist/editor.js +2 -2
  510. package/dist/embedded-new-voice-2G3VLPVQ.js +24 -0
  511. package/dist/embedded-voice-T5IWOWGD.js +24 -0
  512. package/dist/fleet-decision-row-WM64KXP3.js +361 -0
  513. package/dist/in-chat-connect-model.js +5 -5
  514. package/dist/index.js +51 -51
  515. package/dist/interaction.js +5 -5
  516. package/dist/machines.js +3 -3
  517. package/dist/meetings-recorder.js +1 -1
  518. package/dist/meetings.js +40 -32
  519. package/dist/meetings.js.map +1 -1
  520. package/dist/messages/en.d.ts +3 -0
  521. package/dist/messages.js +1 -1
  522. package/dist/model-policy-picker-menu-SMC6J6QN.js +22 -0
  523. package/dist/payment-page-card-G4U63KUV.js +223 -0
  524. package/dist/platform-activity-row-BUZGZAKZ.js +128 -0
  525. package/dist/queue-surface-implementation-4KUZ4PJV.js +908 -0
  526. package/dist/realtime.js +4 -4
  527. package/dist/recorder-host-WF2RRR56.js +69 -0
  528. package/dist/recorder-host-WF2RRR56.js.map +1 -0
  529. package/dist/recordings.js +3 -3
  530. package/dist/sandbox-files-ESDMOFSD.js +27 -0
  531. package/dist/session-app.js +211 -209
  532. package/dist/session-connect-cards-Q53KMMYY.js +25 -0
  533. package/dist/session-list-model.js +2 -2
  534. package/dist/session-realtime-CF3I35WJ.js +30 -0
  535. package/dist/session-realtime.js +2 -2
  536. package/dist/session-ui.js +33 -31
  537. package/dist/session.js +5 -5
  538. package/dist/sites.js +2 -2
  539. package/dist/terminal.js +2 -2
  540. package/dist/this-computer.js +3 -3
  541. package/dist/timeline-annotation-markers-CJK4RYZ4.js +157 -0
  542. package/dist/timeline-annotation-selection-67GF5H26.js +208 -0
  543. package/dist/timeline-annotations-dialog-ONJPQDJQ.js +283 -0
  544. package/dist/ui/access-list.js +11 -9
  545. package/dist/ui/access-list.js.map +1 -1
  546. package/dist/ui/api-error.d.ts +6 -3
  547. package/dist/ui/api-error.js +2 -1
  548. package/dist/ui/cadence-picker.js +6 -4
  549. package/dist/ui/choice-cards.js +5 -3
  550. package/dist/ui/confirm-dialog.js +2 -2
  551. package/dist/ui/copy-field.js +5 -3
  552. package/dist/ui/detail-page.js +2 -2
  553. package/dist/ui/detail-sheet.js +2 -2
  554. package/dist/ui/diff-view.js +4 -3
  555. package/dist/ui/disclosure.js +3 -3
  556. package/dist/ui/email-chips-input.js +5 -3
  557. package/dist/ui/error-message.js +2 -1
  558. package/dist/ui/error-reference.d.ts +15 -0
  559. package/dist/ui/error-text.d.ts +10 -0
  560. package/dist/ui/error-text.js +9 -0
  561. package/dist/ui/error-text.js.map +1 -0
  562. package/dist/ui/field.js +4 -2
  563. package/dist/ui/flush-form-page.js +8 -6
  564. package/dist/ui/form-dialog.js +7 -5
  565. package/dist/ui/list-row.js +3 -3
  566. package/dist/ui/load-failure.js +3 -2
  567. package/dist/ui/load-failure.js.map +1 -1
  568. package/dist/ui/notice.js +3 -1
  569. package/dist/ui/relative-time.js +2 -2
  570. package/dist/ui/revision-history.js +9 -7
  571. package/dist/ui/revision-history.js.map +1 -1
  572. package/dist/ui/role-select.js +10 -8
  573. package/dist/ui/segmented-control.js +4 -2
  574. package/dist/ui/select-menu.js +5 -3
  575. package/dist/ui/setting-row.js +3 -1
  576. package/dist/ui/switch.js +4 -2
  577. package/dist/ui/usage-meter.js +3 -3
  578. package/dist/ui/word-list-row.js +4 -2
  579. package/dist/usage.js +2 -2
  580. package/dist/workbench.js +4 -4
  581. package/package.json +5 -6
  582. package/src/app/app-state.tsx +126 -14
  583. package/src/app/components/artifacts/site-data.tsx +2 -2
  584. package/src/app/components/artifacts/site-settings.tsx +2 -2
  585. package/src/app/components/rail/session-list.tsx +7 -8
  586. package/src/app/components/rail/session-shell.tsx +3 -3
  587. package/src/app/components/repository-picker.tsx +8 -1
  588. package/src/app/components/session/model-context-inspector.tsx +2 -1
  589. package/src/app/components/session/session-admin-access.tsx +2 -2
  590. package/src/app/lib/actions/chat-operations.ts +4 -4
  591. package/src/app/lib/archive-session.test.tsx +265 -2
  592. package/src/app/lib/archive-session.tsx +175 -9
  593. package/src/app/lib/copy-link.ts +2 -2
  594. package/src/app/lib/inbox.tsx +2 -2
  595. package/src/app/lib/session-failure-toasts.ts +3 -2
  596. package/src/app/lib/toast.test.tsx +215 -0
  597. package/src/app/lib/toast.tsx +67 -2
  598. package/src/app/personal-github-reconnect.test.tsx +356 -0
  599. package/src/app/routes/artifacts.tsx +1 -2
  600. package/src/components/tool-action-review.tsx +14 -1
  601. package/src/meetings/delete-meeting-dialog.tsx +2 -1
  602. package/src/meetings/meeting-ask.tsx +2 -1
  603. package/src/meetings/meeting-detail-page.tsx +2 -1
  604. package/src/meetings/meeting-notes.tsx +2 -1
  605. package/src/meetings/meeting-settings.tsx +4 -1
  606. package/src/meetings/meeting-transcript.tsx +4 -1
  607. package/src/meetings/recorder-context.tsx +10 -0
  608. package/src/meetings/recorder-host.test.tsx +60 -2
  609. package/src/meetings/recorder-host.tsx +16 -3
  610. package/src/meetings/recorder-panel.tsx +2 -1
  611. package/src/meetings/recorder-workspace.test.tsx +26 -1
  612. package/src/messages/en.ts +6 -0
  613. package/src/ui/api-error.test.ts +29 -5
  614. package/src/ui/api-error.tsx +10 -4
  615. package/src/ui/error-message.tsx +8 -0
  616. package/src/ui/error-reference.test.ts +54 -0
  617. package/src/ui/error-reference.ts +51 -0
  618. package/src/ui/error-text.test.tsx +66 -0
  619. package/src/ui/error-text.tsx +49 -0
  620. package/src/ui/field.tsx +4 -1
  621. package/src/ui/notice.tsx +7 -2
  622. package/src/ui/setting-row.tsx +4 -1
  623. package/styles/app.css +12 -0
  624. package/styles/compiled.css +9 -0
  625. package/dist/browser-viewer-NOP5EAUO.js +0 -23
  626. package/dist/chat-interactive-block-G7O44JHR.js +0 -18
  627. package/dist/chunk-26RWVLRD.js +0 -135
  628. package/dist/chunk-2AHTY7OT.js +0 -247
  629. package/dist/chunk-2GDNWKSC.js +0 -3670
  630. package/dist/chunk-2JENKGCE.js +0 -339
  631. package/dist/chunk-2KYQC4T5.js +0 -155
  632. package/dist/chunk-2RREGDQH.js +0 -708
  633. package/dist/chunk-2T7DX6KR.js +0 -353
  634. package/dist/chunk-2VB2O4FQ.js +0 -920
  635. package/dist/chunk-2VTTKH23.js +0 -94
  636. package/dist/chunk-2XNG2KNT.js +0 -172
  637. package/dist/chunk-2YC6JQRO.js +0 -399
  638. package/dist/chunk-2ZVRJRXC.js +0 -27
  639. package/dist/chunk-35737UT4.js +0 -162
  640. package/dist/chunk-366XZ4PM.js +0 -41
  641. package/dist/chunk-36MIDGAH.js +0 -199
  642. package/dist/chunk-3FD76LXM.js +0 -212
  643. package/dist/chunk-3OVIXY4O.js +0 -213
  644. package/dist/chunk-3OVIXY4O.js.map +0 -1
  645. package/dist/chunk-3QNRBEYH.js +0 -432
  646. package/dist/chunk-3QX55WLC.js +0 -15
  647. package/dist/chunk-3USJPT2S.js +0 -1022
  648. package/dist/chunk-42CMQ5VR.js +0 -251
  649. package/dist/chunk-4LPOWQJY.js +0 -42
  650. package/dist/chunk-4NBTZRKT.js +0 -79
  651. package/dist/chunk-4OXI6HCV.js +0 -118
  652. package/dist/chunk-4SZZAFYQ.js +0 -2317
  653. package/dist/chunk-4TQL3WP2.js +0 -140
  654. package/dist/chunk-4Y6BK7IU.js +0 -196
  655. package/dist/chunk-4YD7YZC5.js +0 -373
  656. package/dist/chunk-5DTST3TP.js +0 -81
  657. package/dist/chunk-5FH5LQVS.js +0 -130
  658. package/dist/chunk-5KWMYPJV.js +0 -847
  659. package/dist/chunk-5MUJDC6C.js +0 -84
  660. package/dist/chunk-5QM2WPKV.js +0 -41
  661. package/dist/chunk-5WUN7XPD.js +0 -1184
  662. package/dist/chunk-64PO2WXU.js +0 -1067
  663. package/dist/chunk-6AHATAAY.js +0 -431
  664. package/dist/chunk-6ED2YWBT.js +0 -56
  665. package/dist/chunk-6GXVYAGR.js +0 -170
  666. package/dist/chunk-6H4SJBPE.js +0 -85
  667. package/dist/chunk-6MT3HHUB.js +0 -84
  668. package/dist/chunk-6UHCBH4G.js +0 -198
  669. package/dist/chunk-6WLX3NR3.js +0 -198
  670. package/dist/chunk-73NXYYWE.js +0 -112
  671. package/dist/chunk-76ERNKIZ.js +0 -603
  672. package/dist/chunk-776QZ573.js +0 -57
  673. package/dist/chunk-7BYWOFVH.js +0 -171
  674. package/dist/chunk-7O5CGGIQ.js +0 -108
  675. package/dist/chunk-7QOHRZ67.js +0 -251
  676. package/dist/chunk-7WIDB4PL.js +0 -2058
  677. package/dist/chunk-7WKP3DYJ.js +0 -37
  678. package/dist/chunk-7XOVTPDI.js +0 -227
  679. package/dist/chunk-AB5MBIXY.js +0 -281
  680. package/dist/chunk-ADNMXBCX.js +0 -1193
  681. package/dist/chunk-AFBJTUFK.js +0 -202
  682. package/dist/chunk-ALVRWCXE.js +0 -61
  683. package/dist/chunk-AOHU6O3M.js +0 -247
  684. package/dist/chunk-AOHU6O3M.js.map +0 -1
  685. package/dist/chunk-APW4T4SW.js +0 -270
  686. package/dist/chunk-AXBSYWKX.js +0 -2656
  687. package/dist/chunk-B2I5ZW35.js +0 -422
  688. package/dist/chunk-B3LMRTRF.js +0 -223
  689. package/dist/chunk-BE2XENG2.js +0 -292
  690. package/dist/chunk-BE2XENG2.js.map +0 -1
  691. package/dist/chunk-BF4SADKX.js +0 -29
  692. package/dist/chunk-BKP43KK4.js +0 -1788
  693. package/dist/chunk-BM3NEZ6X.js +0 -781
  694. package/dist/chunk-BT3BAGB5.js +0 -216
  695. package/dist/chunk-BTSYDIVJ.js +0 -357
  696. package/dist/chunk-BUBNGKWG.js +0 -70
  697. package/dist/chunk-BVUJV5WR.js +0 -126
  698. package/dist/chunk-BZO5ZMUP.js +0 -27
  699. package/dist/chunk-C3HUF3M2.js +0 -157
  700. package/dist/chunk-C5QCAOES.js +0 -264
  701. package/dist/chunk-CGK6ALQU.js +0 -159
  702. package/dist/chunk-CPYGO76Q.js +0 -335
  703. package/dist/chunk-CQH4FWYA.js +0 -490
  704. package/dist/chunk-CZP5QXNB.js +0 -234
  705. package/dist/chunk-D2TOWEYH.js +0 -266
  706. package/dist/chunk-D6L4GHRH.js +0 -340
  707. package/dist/chunk-DKT3N37I.js +0 -50
  708. package/dist/chunk-DMMJIFKA.js +0 -84
  709. package/dist/chunk-DO74DQC7.js +0 -26
  710. package/dist/chunk-DOFJZ2JU.js +0 -83
  711. package/dist/chunk-DSXVY5WZ.js +0 -119
  712. package/dist/chunk-E337C2VK.js +0 -82
  713. package/dist/chunk-E6NRSRBE.js +0 -114
  714. package/dist/chunk-E6SQKHAU.js +0 -114
  715. package/dist/chunk-E7UIVD7F.js +0 -236
  716. package/dist/chunk-EGBII4CP.js +0 -229
  717. package/dist/chunk-EILSAQVJ.js +0 -200
  718. package/dist/chunk-ELL6HSYR.js +0 -307
  719. package/dist/chunk-EUBGLFFR.js +0 -73
  720. package/dist/chunk-EW6RNWSH.js +0 -212
  721. package/dist/chunk-EYUT5Y6V.js +0 -2599
  722. package/dist/chunk-F7U3QPZ3.js +0 -304
  723. package/dist/chunk-FBTJKSTM.js +0 -1270
  724. package/dist/chunk-FC2FTSK3.js +0 -2424
  725. package/dist/chunk-FOGNA3BU.js +0 -148
  726. package/dist/chunk-FQFKHNC7.js +0 -650
  727. package/dist/chunk-FTCHM755.js +0 -50
  728. package/dist/chunk-FXSHP4JZ.js +0 -3550
  729. package/dist/chunk-G6ZZC543.js +0 -818
  730. package/dist/chunk-G7FU6PUA.js +0 -100
  731. package/dist/chunk-GA4YEAMX.js +0 -3851
  732. package/dist/chunk-GE33BGB7.js +0 -12
  733. package/dist/chunk-GQ67HSJY.js +0 -570
  734. package/dist/chunk-GXXZ3WAK.js +0 -106
  735. package/dist/chunk-H2TCRX3H.js +0 -237
  736. package/dist/chunk-HLL4PBO7.js +0 -60
  737. package/dist/chunk-HLOEPV5C.js +0 -259
  738. package/dist/chunk-HUAGBS4O.js +0 -56
  739. package/dist/chunk-I36GSRUJ.js +0 -211
  740. package/dist/chunk-I6VBHT7S.js +0 -261
  741. package/dist/chunk-ICNNNOXE.js +0 -333
  742. package/dist/chunk-ICX64J4U.js +0 -172
  743. package/dist/chunk-ID36Y46W.js +0 -5046
  744. package/dist/chunk-ID36Y46W.js.map +0 -1
  745. package/dist/chunk-IGVVXLKJ.js +0 -559
  746. package/dist/chunk-IKJLSBUI.js +0 -2984
  747. package/dist/chunk-IKMUFDVS.js +0 -50
  748. package/dist/chunk-IKUNOX7F.js +0 -255
  749. package/dist/chunk-ILKDYBON.js +0 -288
  750. package/dist/chunk-IVYJX3OY.js +0 -187
  751. package/dist/chunk-JCY4CMDH.js +0 -107
  752. package/dist/chunk-JJZPLGIP.js +0 -474
  753. package/dist/chunk-JW33RV4V.js +0 -143
  754. package/dist/chunk-JWPA6ZPZ.js +0 -351
  755. package/dist/chunk-JZ4QXS6D.js +0 -50
  756. package/dist/chunk-K42OUZQ3.js +0 -386
  757. package/dist/chunk-K5TOVX5A.js +0 -206
  758. package/dist/chunk-K63HGHZX.js +0 -99
  759. package/dist/chunk-K6R5T44I.js +0 -75
  760. package/dist/chunk-KHJ5PXFM.js +0 -914
  761. package/dist/chunk-KJZRQ55O.js +0 -2319
  762. package/dist/chunk-KJZRQ55O.js.map +0 -1
  763. package/dist/chunk-KKI55ZMW.js +0 -65
  764. package/dist/chunk-KRPLAAFP.js +0 -199
  765. package/dist/chunk-KVFEZDBO.js +0 -134
  766. package/dist/chunk-L4I665WK.js +0 -45
  767. package/dist/chunk-L4OF77LE.js +0 -110
  768. package/dist/chunk-L4OF77LE.js.map +0 -1
  769. package/dist/chunk-LA5T52RM.js +0 -70
  770. package/dist/chunk-LM2W5YAT.js +0 -401
  771. package/dist/chunk-LQ62ECE6.js +0 -390
  772. package/dist/chunk-LQDUHU3Y.js +0 -71
  773. package/dist/chunk-LRJIYTZW.js +0 -4157
  774. package/dist/chunk-LRJIYTZW.js.map +0 -1
  775. package/dist/chunk-LRN6GYAZ.js +0 -138
  776. package/dist/chunk-M4SDVTKJ.js +0 -790
  777. package/dist/chunk-MA6O7CM6.js +0 -99
  778. package/dist/chunk-MBIBB6GI.js +0 -197
  779. package/dist/chunk-MF3ZFB4H.js +0 -199
  780. package/dist/chunk-MJJGJMYJ.js +0 -98
  781. package/dist/chunk-MLJADQFI.js +0 -4824
  782. package/dist/chunk-MQG7RV3B.js +0 -62
  783. package/dist/chunk-MTTGSQA5.js +0 -186
  784. package/dist/chunk-MW7Y3T55.js +0 -525
  785. package/dist/chunk-MYUFMVCH.js +0 -121
  786. package/dist/chunk-N3OARGH2.js +0 -61
  787. package/dist/chunk-NB2HMRT2.js +0 -353
  788. package/dist/chunk-NFDFYWMV.js +0 -141
  789. package/dist/chunk-NWGNFN3N.js +0 -1187
  790. package/dist/chunk-NWJRPDIO.js +0 -840
  791. package/dist/chunk-NWJRPDIO.js.map +0 -1
  792. package/dist/chunk-NXWBBZJV.js +0 -267
  793. package/dist/chunk-NY2QWROL.js +0 -71
  794. package/dist/chunk-O2IOPP3R.js +0 -848
  795. package/dist/chunk-O2UWH5RG.js +0 -141
  796. package/dist/chunk-O5YWDLYF.js +0 -393
  797. package/dist/chunk-OBMCZO7E.js +0 -121
  798. package/dist/chunk-OD3DCCCB.js +0 -132
  799. package/dist/chunk-OMH7R6I3.js +0 -390
  800. package/dist/chunk-OSA5EQG5.js +0 -113
  801. package/dist/chunk-OSJ3MUTZ.js +0 -2936
  802. package/dist/chunk-OWAYJ2OV.js +0 -368
  803. package/dist/chunk-OYXBFSGK.js +0 -269
  804. package/dist/chunk-PC2EBA5G.js +0 -4116
  805. package/dist/chunk-PC2EBA5G.js.map +0 -1
  806. package/dist/chunk-PC2MVXIP.js +0 -363
  807. package/dist/chunk-PC2MVXIP.js.map +0 -1
  808. package/dist/chunk-PENGLDR5.js +0 -309
  809. package/dist/chunk-PSMAWRPL.js +0 -191
  810. package/dist/chunk-PW4IMTYR.js +0 -702
  811. package/dist/chunk-PW54V7T5.js +0 -387
  812. package/dist/chunk-Q2EXYSMO.js +0 -24
  813. package/dist/chunk-Q2XJK5I4.js +0 -51
  814. package/dist/chunk-Q2XJK5I4.js.map +0 -1
  815. package/dist/chunk-Q4EFRI6X.js +0 -258
  816. package/dist/chunk-Q52CXW6C.js +0 -48
  817. package/dist/chunk-Q6XQ2R37.js +0 -184
  818. package/dist/chunk-QAJ53XBW.js +0 -23
  819. package/dist/chunk-QDTKECT4.js +0 -129
  820. package/dist/chunk-QE3ZFGI3.js +0 -43
  821. package/dist/chunk-QHHSUR2Y.js +0 -298
  822. package/dist/chunk-QHNVNR7B.js +0 -173
  823. package/dist/chunk-QIK2Q6N5.js +0 -376
  824. package/dist/chunk-QWL4L366.js +0 -38
  825. package/dist/chunk-R3J2PTVQ.js +0 -690
  826. package/dist/chunk-RBRX5ZS3.js +0 -319
  827. package/dist/chunk-REX6JIC7.js +0 -33
  828. package/dist/chunk-RGZ7EEC3.js +0 -537
  829. package/dist/chunk-RGZ7EEC3.js.map +0 -1
  830. package/dist/chunk-RIPS7GFR.js +0 -960
  831. package/dist/chunk-RTBZNFNV.js +0 -1354
  832. package/dist/chunk-RTBZNFNV.js.map +0 -1
  833. package/dist/chunk-SKMNW4SW.js +0 -187
  834. package/dist/chunk-SRFARKXC.js +0 -623
  835. package/dist/chunk-SWIHWCTW.js +0 -4461
  836. package/dist/chunk-TA5CFTO4.js +0 -103
  837. package/dist/chunk-TF4ZVJLI.js +0 -156
  838. package/dist/chunk-TG7DADJR.js +0 -9624
  839. package/dist/chunk-TI4R2ZD5.js +0 -623
  840. package/dist/chunk-TI4R2ZD5.js.map +0 -1
  841. package/dist/chunk-TNKWOUWP.js +0 -412
  842. package/dist/chunk-TNUSPXPW.js +0 -5555
  843. package/dist/chunk-TNUSPXPW.js.map +0 -1
  844. package/dist/chunk-TNYFHKVV.js +0 -366
  845. package/dist/chunk-TPMZBOFH.js +0 -178
  846. package/dist/chunk-TW3LKRJF.js +0 -263
  847. package/dist/chunk-TZAO65I4.js +0 -83
  848. package/dist/chunk-U5EFEMYU.js +0 -89
  849. package/dist/chunk-U6M4LS3M.js +0 -378
  850. package/dist/chunk-UI4HI5QK.js +0 -918
  851. package/dist/chunk-UJDOWCQ3.js +0 -272
  852. package/dist/chunk-UJUQ2QOX.js +0 -581
  853. package/dist/chunk-UQ3IM6FY.js +0 -133
  854. package/dist/chunk-UYE47LO6.js +0 -246
  855. package/dist/chunk-VADHIT26.js +0 -89
  856. package/dist/chunk-VFQ3H6CO.js +0 -163
  857. package/dist/chunk-VGUN6VB2.js +0 -188
  858. package/dist/chunk-VGWWEVSD.js +0 -723
  859. package/dist/chunk-VIEFJ2DM.js +0 -2469
  860. package/dist/chunk-VLXM36VS.js +0 -268
  861. package/dist/chunk-W3BFRR2G.js +0 -499
  862. package/dist/chunk-W5MPXR3A.js +0 -981
  863. package/dist/chunk-W5OWKVZG.js +0 -136
  864. package/dist/chunk-W6FRORMX.js +0 -486
  865. package/dist/chunk-W6FRORMX.js.map +0 -1
  866. package/dist/chunk-W7XROSZO.js +0 -232
  867. package/dist/chunk-W7XROSZO.js.map +0 -1
  868. package/dist/chunk-WEZ5PXZP.js +0 -403
  869. package/dist/chunk-WHWUWN2X.js +0 -197
  870. package/dist/chunk-WHWUWN2X.js.map +0 -1
  871. package/dist/chunk-WLEDGIVS.js +0 -114
  872. package/dist/chunk-WLEDGIVS.js.map +0 -1
  873. package/dist/chunk-WNDAKLHG.js +0 -281
  874. package/dist/chunk-WTJ4PLMU.js +0 -103
  875. package/dist/chunk-WV2KPJUW.js +0 -107
  876. package/dist/chunk-X3VY5EGY.js +0 -277
  877. package/dist/chunk-X3VY5EGY.js.map +0 -1
  878. package/dist/chunk-XDBYWVCW.js +0 -418
  879. package/dist/chunk-XF7MA4U5.js +0 -36
  880. package/dist/chunk-XF7MA4U5.js.map +0 -1
  881. package/dist/chunk-XOSQP3YA.js +0 -605
  882. package/dist/chunk-XYBV4ZJQ.js +0 -271
  883. package/dist/chunk-YBKVGLQR.js +0 -147
  884. package/dist/chunk-YDCUBUWH.js +0 -1583
  885. package/dist/chunk-YG6NARQY.js +0 -51
  886. package/dist/chunk-YG6NARQY.js.map +0 -1
  887. package/dist/chunk-YSVD5HI4.js +0 -278
  888. package/dist/chunk-YT3WGNG6.js +0 -290
  889. package/dist/chunk-YYICQDK4.js +0 -414
  890. package/dist/chunk-YYLMZLI6.js +0 -686
  891. package/dist/chunk-ZAMTKG5U.js +0 -95
  892. package/dist/chunk-ZDLYSXKN.js +0 -111
  893. package/dist/chunk-ZG447FSS.js +0 -194
  894. package/dist/chunk-ZIT5KD4Q.js +0 -264
  895. package/dist/chunk-ZUDOVDKR.js +0 -3064
  896. package/dist/chunk-ZW2ECJ3A.js +0 -451
  897. package/dist/computer-viewer-LPZ763KF.js +0 -21
  898. package/dist/embedded-new-voice-YVMKSLKJ.js +0 -24
  899. package/dist/embedded-voice-BJPQFXSU.js +0 -24
  900. package/dist/fleet-decision-row-AA4H5CDT.js +0 -361
  901. package/dist/model-policy-picker-menu-HSMWVO4P.js +0 -22
  902. package/dist/payment-page-card-3LTSFJGN.js +0 -223
  903. package/dist/platform-activity-row-5VYV6TX4.js +0 -128
  904. package/dist/queue-surface-implementation-NXHZUKD2.js +0 -908
  905. package/dist/recorder-host-ZKRWTDBM.js +0 -57
  906. package/dist/recorder-host-ZKRWTDBM.js.map +0 -1
  907. package/dist/sandbox-files-YCMLQUFU.js +0 -27
  908. package/dist/session-connect-cards-KSQLMRM5.js +0 -25
  909. package/dist/session-realtime-ZR6JLG3Y.js +0 -30
  910. package/dist/timeline-annotation-markers-YAHDPWVG.js +0 -157
  911. package/dist/timeline-annotation-selection-KHXNCEJP.js +0 -208
  912. package/dist/timeline-annotations-dialog-Y4XD3N7B.js +0 -283
  913. /package/dist/{browser-viewer-NOP5EAUO.js.map → browser-viewer-TC27OCIP.js.map} +0 -0
  914. /package/dist/{chat-interactive-block-G7O44JHR.js.map → chat-interactive-block-RWPQBHVS.js.map} +0 -0
  915. /package/dist/{chunk-EGBII4CP.js.map → chunk-2BDT3UZQ.js.map} +0 -0
  916. /package/dist/{chunk-4YD7YZC5.js.map → chunk-2CLNIEK4.js.map} +0 -0
  917. /package/dist/{chunk-ZW2ECJ3A.js.map → chunk-2FSBSCZJ.js.map} +0 -0
  918. /package/dist/{chunk-KRPLAAFP.js.map → chunk-2I7HDP53.js.map} +0 -0
  919. /package/dist/{chunk-3QX55WLC.js.map → chunk-2INLDVK3.js.map} +0 -0
  920. /package/dist/{chunk-BVUJV5WR.js.map → chunk-2KALVELR.js.map} +0 -0
  921. /package/dist/{chunk-XDBYWVCW.js.map → chunk-2M3J7ZWQ.js.map} +0 -0
  922. /package/dist/{chunk-2RREGDQH.js.map → chunk-2OP6WLGV.js.map} +0 -0
  923. /package/dist/{chunk-OSJ3MUTZ.js.map → chunk-2RG7NIAS.js.map} +0 -0
  924. /package/dist/{chunk-26RWVLRD.js.map → chunk-2STUPWEL.js.map} +0 -0
  925. /package/dist/{chunk-XYBV4ZJQ.js.map → chunk-2YUOVM3G.js.map} +0 -0
  926. /package/dist/{chunk-2GDNWKSC.js.map → chunk-2Z3NQNTM.js.map} +0 -0
  927. /package/dist/{chunk-2VB2O4FQ.js.map → chunk-3CPWWGNK.js.map} +0 -0
  928. /package/dist/{chunk-7WKP3DYJ.js.map → chunk-3ED2U6XE.js.map} +0 -0
  929. /package/dist/{chunk-BT3BAGB5.js.map → chunk-3GZA4BUW.js.map} +0 -0
  930. /package/dist/{chunk-3USJPT2S.js.map → chunk-3IK6UYO6.js.map} +0 -0
  931. /package/dist/{chunk-6AHATAAY.js.map → chunk-3PZ7JZI2.js.map} +0 -0
  932. /package/dist/{chunk-JZ4QXS6D.js.map → chunk-4DVJ6ONS.js.map} +0 -0
  933. /package/dist/{chunk-5QM2WPKV.js.map → chunk-4E3ZJRTZ.js.map} +0 -0
  934. /package/dist/{chunk-NFDFYWMV.js.map → chunk-4SAWSRAA.js.map} +0 -0
  935. /package/dist/{chunk-ELL6HSYR.js.map → chunk-53AGXXZM.js.map} +0 -0
  936. /package/dist/{chunk-G6ZZC543.js.map → chunk-54YW6SZZ.js.map} +0 -0
  937. /package/dist/{chunk-4SZZAFYQ.js.map → chunk-57AZS3IE.js.map} +0 -0
  938. /package/dist/{chunk-JW33RV4V.js.map → chunk-5CPEEAFD.js.map} +0 -0
  939. /package/dist/{chunk-D6L4GHRH.js.map → chunk-5EWSRZ2Y.js.map} +0 -0
  940. /package/dist/{chunk-PW54V7T5.js.map → chunk-5IE76VCY.js.map} +0 -0
  941. /package/dist/{chunk-6GXVYAGR.js.map → chunk-5NWEKLDZ.js.map} +0 -0
  942. /package/dist/{chunk-6UHCBH4G.js.map → chunk-5O2FCCHZ.js.map} +0 -0
  943. /package/dist/{chunk-2YC6JQRO.js.map → chunk-5QAY6K77.js.map} +0 -0
  944. /package/dist/{chunk-EYUT5Y6V.js.map → chunk-6DK6Z4UN.js.map} +0 -0
  945. /package/dist/{chunk-PW4IMTYR.js.map → chunk-6DVCC7QS.js.map} +0 -0
  946. /package/dist/{chunk-7WIDB4PL.js.map → chunk-6JHU2UBS.js.map} +0 -0
  947. /package/dist/{chunk-CQH4FWYA.js.map → chunk-6PZ7OBYU.js.map} +0 -0
  948. /package/dist/{chunk-VFQ3H6CO.js.map → chunk-6WGRDRMA.js.map} +0 -0
  949. /package/dist/{chunk-MTTGSQA5.js.map → chunk-77WAKEAH.js.map} +0 -0
  950. /package/dist/{chunk-O5YWDLYF.js.map → chunk-7TDO3CUM.js.map} +0 -0
  951. /package/dist/{chunk-2AHTY7OT.js.map → chunk-7TIX2UAR.js.map} +0 -0
  952. /package/dist/{chunk-FOGNA3BU.js.map → chunk-7V5REBCP.js.map} +0 -0
  953. /package/dist/{chunk-2JENKGCE.js.map → chunk-7VMZGLYI.js.map} +0 -0
  954. /package/dist/{chunk-E7UIVD7F.js.map → chunk-7W37NS3I.js.map} +0 -0
  955. /package/dist/{chunk-ZDLYSXKN.js.map → chunk-7X6SDK2C.js.map} +0 -0
  956. /package/dist/{chunk-ZAMTKG5U.js.map → chunk-7ZNUHRP5.js.map} +0 -0
  957. /package/dist/{chunk-DMMJIFKA.js.map → chunk-A36PZALD.js.map} +0 -0
  958. /package/dist/{chunk-WV2KPJUW.js.map → chunk-AUI6VA63.js.map} +0 -0
  959. /package/dist/{chunk-Q2EXYSMO.js.map → chunk-AW2MQB37.js.map} +0 -0
  960. /package/dist/{chunk-6WLX3NR3.js.map → chunk-AWKES45Y.js.map} +0 -0
  961. /package/dist/{chunk-OD3DCCCB.js.map → chunk-AXHZYXUV.js.map} +0 -0
  962. /package/dist/{chunk-QDTKECT4.js.map → chunk-BHNSJVBC.js.map} +0 -0
  963. /package/dist/{chunk-FTCHM755.js.map → chunk-BJEJFISW.js.map} +0 -0
  964. /package/dist/{chunk-B2I5ZW35.js.map → chunk-BJFH6JI5.js.map} +0 -0
  965. /package/dist/{chunk-RIPS7GFR.js.map → chunk-BMICEQZQ.js.map} +0 -0
  966. /package/dist/{chunk-BF4SADKX.js.map → chunk-BTLBYBLU.js.map} +0 -0
  967. /package/dist/{chunk-U5EFEMYU.js.map → chunk-BXBDYNRP.js.map} +0 -0
  968. /package/dist/{chunk-BTSYDIVJ.js.map → chunk-C372BFOV.js.map} +0 -0
  969. /package/dist/{chunk-MLJADQFI.js.map → chunk-CMKGILJK.js.map} +0 -0
  970. /package/dist/{chunk-DKT3N37I.js.map → chunk-CRAOSVHC.js.map} +0 -0
  971. /package/dist/{chunk-WNDAKLHG.js.map → chunk-CRU35QQH.js.map} +0 -0
  972. /package/dist/{chunk-2XNG2KNT.js.map → chunk-CUIWCGMA.js.map} +0 -0
  973. /package/dist/{chunk-QWL4L366.js.map → chunk-D5TWNFFE.js.map} +0 -0
  974. /package/dist/{chunk-SWIHWCTW.js.map → chunk-DF5EAHDN.js.map} +0 -0
  975. /package/dist/{chunk-HUAGBS4O.js.map → chunk-DJAXC5OK.js.map} +0 -0
  976. /package/dist/{chunk-FXSHP4JZ.js.map → chunk-DXBYWSL5.js.map} +0 -0
  977. /package/dist/{chunk-5MUJDC6C.js.map → chunk-DXR5PDFP.js.map} +0 -0
  978. /package/dist/{chunk-NXWBBZJV.js.map → chunk-DZWARC4M.js.map} +0 -0
  979. /package/dist/{chunk-GXXZ3WAK.js.map → chunk-DZXEDSQI.js.map} +0 -0
  980. /package/dist/{chunk-Q52CXW6C.js.map → chunk-E7XERZWC.js.map} +0 -0
  981. /package/dist/{chunk-4NBTZRKT.js.map → chunk-ECJBYNLJ.js.map} +0 -0
  982. /package/dist/{chunk-QE3ZFGI3.js.map → chunk-EDI6SJ2N.js.map} +0 -0
  983. /package/dist/{chunk-E6NRSRBE.js.map → chunk-EWSOC6WF.js.map} +0 -0
  984. /package/dist/{chunk-TA5CFTO4.js.map → chunk-F2OPHAUN.js.map} +0 -0
  985. /package/dist/{chunk-5DTST3TP.js.map → chunk-F4ISQGGM.js.map} +0 -0
  986. /package/dist/{chunk-MBIBB6GI.js.map → chunk-FO4LYKJL.js.map} +0 -0
  987. /package/dist/{chunk-EUBGLFFR.js.map → chunk-FPXASDG5.js.map} +0 -0
  988. /package/dist/{chunk-C3HUF3M2.js.map → chunk-FVXBNBHO.js.map} +0 -0
  989. /package/dist/{chunk-W5MPXR3A.js.map → chunk-FZC5N55X.js.map} +0 -0
  990. /package/dist/{chunk-MA6O7CM6.js.map → chunk-GAVYGBFI.js.map} +0 -0
  991. /package/dist/{chunk-REX6JIC7.js.map → chunk-GKVJIDHF.js.map} +0 -0
  992. /package/dist/{chunk-EW6RNWSH.js.map → chunk-GVN5SZLX.js.map} +0 -0
  993. /package/dist/{chunk-KVFEZDBO.js.map → chunk-H3T7IWNA.js.map} +0 -0
  994. /package/dist/{chunk-I6VBHT7S.js.map → chunk-HAX4LCR5.js.map} +0 -0
  995. /package/dist/{chunk-OBMCZO7E.js.map → chunk-HC4JFZVU.js.map} +0 -0
  996. /package/dist/{chunk-UJUQ2QOX.js.map → chunk-HEFPPMBQ.js.map} +0 -0
  997. /package/dist/{chunk-DOFJZ2JU.js.map → chunk-HNLR7Q4Z.js.map} +0 -0
  998. /package/dist/{chunk-GE33BGB7.js.map → chunk-HSOXMJOT.js.map} +0 -0
  999. /package/dist/{chunk-AB5MBIXY.js.map → chunk-I4MRDSUF.js.map} +0 -0
  1000. /package/dist/{chunk-AFBJTUFK.js.map → chunk-I4QEJ62Y.js.map} +0 -0
  1001. /package/dist/{chunk-6ED2YWBT.js.map → chunk-I5B5L6Q4.js.map} +0 -0
  1002. /package/dist/{chunk-C5QCAOES.js.map → chunk-I5QZJODL.js.map} +0 -0
  1003. /package/dist/{chunk-76ERNKIZ.js.map → chunk-I7ITSXSJ.js.map} +0 -0
  1004. /package/dist/{chunk-OMH7R6I3.js.map → chunk-I7XIZRFD.js.map} +0 -0
  1005. /package/dist/{chunk-E337C2VK.js.map → chunk-IDANQPRH.js.map} +0 -0
  1006. /package/dist/{chunk-3FD76LXM.js.map → chunk-IFB3WJ36.js.map} +0 -0
  1007. /package/dist/{chunk-ZIT5KD4Q.js.map → chunk-IHTAKKTK.js.map} +0 -0
  1008. /package/dist/{chunk-QHNVNR7B.js.map → chunk-ILDXWY37.js.map} +0 -0
  1009. /package/dist/{chunk-GQ67HSJY.js.map → chunk-IMDQUHBJ.js.map} +0 -0
  1010. /package/dist/{chunk-DSXVY5WZ.js.map → chunk-IMZCQBOT.js.map} +0 -0
  1011. /package/dist/{chunk-WEZ5PXZP.js.map → chunk-IXPTECSC.js.map} +0 -0
  1012. /package/dist/{chunk-NY2QWROL.js.map → chunk-J255A3N5.js.map} +0 -0
  1013. /package/dist/{chunk-O2UWH5RG.js.map → chunk-J5F5HPPW.js.map} +0 -0
  1014. /package/dist/{chunk-VIEFJ2DM.js.map → chunk-JEA3QDX7.js.map} +0 -0
  1015. /package/dist/{chunk-TF4ZVJLI.js.map → chunk-JEUPAVYY.js.map} +0 -0
  1016. /package/dist/{chunk-ZG447FSS.js.map → chunk-JKHKLL5W.js.map} +0 -0
  1017. /package/dist/{chunk-FQFKHNC7.js.map → chunk-JQL7ZH6C.js.map} +0 -0
  1018. /package/dist/{chunk-776QZ573.js.map → chunk-JWB44UGJ.js.map} +0 -0
  1019. /package/dist/{chunk-ICNNNOXE.js.map → chunk-JZ4NLCAP.js.map} +0 -0
  1020. /package/dist/{chunk-TNKWOUWP.js.map → chunk-K2A5PXQ5.js.map} +0 -0
  1021. /package/dist/{chunk-SRFARKXC.js.map → chunk-K56XV3C3.js.map} +0 -0
  1022. /package/dist/{chunk-QAJ53XBW.js.map → chunk-K6GAXXVA.js.map} +0 -0
  1023. /package/dist/{chunk-MF3ZFB4H.js.map → chunk-KCBMFSMS.js.map} +0 -0
  1024. /package/dist/{chunk-VLXM36VS.js.map → chunk-KF2FV3ZJ.js.map} +0 -0
  1025. /package/dist/{chunk-YSVD5HI4.js.map → chunk-KJD65EJP.js.map} +0 -0
  1026. /package/dist/{chunk-K42OUZQ3.js.map → chunk-KLEUWIMZ.js.map} +0 -0
  1027. /package/dist/{chunk-PENGLDR5.js.map → chunk-KODELWYT.js.map} +0 -0
  1028. /package/dist/{chunk-TZAO65I4.js.map → chunk-KRDR5RJD.js.map} +0 -0
  1029. /package/dist/{chunk-KHJ5PXFM.js.map → chunk-KUDGXAF7.js.map} +0 -0
  1030. /package/dist/{chunk-6H4SJBPE.js.map → chunk-KYS77RPN.js.map} +0 -0
  1031. /package/dist/{chunk-OWAYJ2OV.js.map → chunk-L7YTNTIK.js.map} +0 -0
  1032. /package/dist/{chunk-YYLMZLI6.js.map → chunk-LHPNABDH.js.map} +0 -0
  1033. /package/dist/{chunk-LM2W5YAT.js.map → chunk-MFROMEKA.js.map} +0 -0
  1034. /package/dist/{chunk-AXBSYWKX.js.map → chunk-MIHN76LX.js.map} +0 -0
  1035. /package/dist/{chunk-4LPOWQJY.js.map → chunk-MIYFS2XN.js.map} +0 -0
  1036. /package/dist/{chunk-KKI55ZMW.js.map → chunk-MJUCFQMS.js.map} +0 -0
  1037. /package/dist/{chunk-ZUDOVDKR.js.map → chunk-MM4FNWI4.js.map} +0 -0
  1038. /package/dist/{chunk-M4SDVTKJ.js.map → chunk-MNZG4OHU.js.map} +0 -0
  1039. /package/dist/{chunk-EILSAQVJ.js.map → chunk-N6AACDUW.js.map} +0 -0
  1040. /package/dist/{chunk-BM3NEZ6X.js.map → chunk-NC55IFN3.js.map} +0 -0
  1041. /package/dist/{chunk-MJJGJMYJ.js.map → chunk-NC7PVOEK.js.map} +0 -0
  1042. /package/dist/{chunk-LA5T52RM.js.map → chunk-NCYMNCJO.js.map} +0 -0
  1043. /package/dist/{chunk-HLOEPV5C.js.map → chunk-ND2BXV3P.js.map} +0 -0
  1044. /package/dist/{chunk-UJDOWCQ3.js.map → chunk-NDBTFUO6.js.map} +0 -0
  1045. /package/dist/{chunk-K5TOVX5A.js.map → chunk-NDQJZ7HF.js.map} +0 -0
  1046. /package/dist/{chunk-35737UT4.js.map → chunk-NGJJJPZZ.js.map} +0 -0
  1047. /package/dist/{chunk-42CMQ5VR.js.map → chunk-NJTSNNE7.js.map} +0 -0
  1048. /package/dist/{chunk-FBTJKSTM.js.map → chunk-NRTE7DNV.js.map} +0 -0
  1049. /package/dist/{chunk-YT3WGNG6.js.map → chunk-NULUNM7H.js.map} +0 -0
  1050. /package/dist/{chunk-7XOVTPDI.js.map → chunk-NUNZS36W.js.map} +0 -0
  1051. /package/dist/{chunk-FC2FTSK3.js.map → chunk-NW53H5RW.js.map} +0 -0
  1052. /package/dist/{chunk-TW3LKRJF.js.map → chunk-NXCMHIKI.js.map} +0 -0
  1053. /package/dist/{chunk-CGK6ALQU.js.map → chunk-O3LKCBP5.js.map} +0 -0
  1054. /package/dist/{chunk-QIK2Q6N5.js.map → chunk-O6N4ABCA.js.map} +0 -0
  1055. /package/dist/{chunk-73NXYYWE.js.map → chunk-O732GAFC.js.map} +0 -0
  1056. /package/dist/{chunk-R3J2PTVQ.js.map → chunk-OF4TIKYC.js.map} +0 -0
  1057. /package/dist/{chunk-BUBNGKWG.js.map → chunk-OMPVOMPP.js.map} +0 -0
  1058. /package/dist/{chunk-4OXI6HCV.js.map → chunk-OPJJSKUK.js.map} +0 -0
  1059. /package/dist/{chunk-4Y6BK7IU.js.map → chunk-OPWCMA4D.js.map} +0 -0
  1060. /package/dist/{chunk-ADNMXBCX.js.map → chunk-ORJPS5VN.js.map} +0 -0
  1061. /package/dist/{chunk-SKMNW4SW.js.map → chunk-OSY5K7L7.js.map} +0 -0
  1062. /package/dist/{chunk-L4I665WK.js.map → chunk-OV6QJRT4.js.map} +0 -0
  1063. /package/dist/{chunk-7O5CGGIQ.js.map → chunk-OVKYVZMW.js.map} +0 -0
  1064. /package/dist/{chunk-7BYWOFVH.js.map → chunk-OZCBM3UC.js.map} +0 -0
  1065. /package/dist/{chunk-ALVRWCXE.js.map → chunk-P3A2EINJ.js.map} +0 -0
  1066. /package/dist/{chunk-BZO5ZMUP.js.map → chunk-PTAG4PDC.js.map} +0 -0
  1067. /package/dist/{chunk-QHHSUR2Y.js.map → chunk-PTF5B7TJ.js.map} +0 -0
  1068. /package/dist/{chunk-O2IOPP3R.js.map → chunk-Q3DPEYX4.js.map} +0 -0
  1069. /package/dist/{chunk-G7FU6PUA.js.map → chunk-QB2KMIDP.js.map} +0 -0
  1070. /package/dist/{chunk-64PO2WXU.js.map → chunk-QCSJ4CWE.js.map} +0 -0
  1071. /package/dist/{chunk-YYICQDK4.js.map → chunk-QIOQ2NPQ.js.map} +0 -0
  1072. /package/dist/{chunk-NB2HMRT2.js.map → chunk-QK5EFEUT.js.map} +0 -0
  1073. /package/dist/{chunk-366XZ4PM.js.map → chunk-QPQBLYQJ.js.map} +0 -0
  1074. /package/dist/{chunk-Q6XQ2R37.js.map → chunk-QPSGCC5D.js.map} +0 -0
  1075. /package/dist/{chunk-JJZPLGIP.js.map → chunk-QZAAUQRN.js.map} +0 -0
  1076. /package/dist/{chunk-VGUN6VB2.js.map → chunk-RGBI63KJ.js.map} +0 -0
  1077. /package/dist/{chunk-LRN6GYAZ.js.map → chunk-RLBKLAEY.js.map} +0 -0
  1078. /package/dist/{chunk-F7U3QPZ3.js.map → chunk-RMFKVOM7.js.map} +0 -0
  1079. /package/dist/{chunk-Q4EFRI6X.js.map → chunk-ROKQU7WG.js.map} +0 -0
  1080. /package/dist/{chunk-7QOHRZ67.js.map → chunk-RPVREAU7.js.map} +0 -0
  1081. /package/dist/{chunk-VGWWEVSD.js.map → chunk-RQKLF42C.js.map} +0 -0
  1082. /package/dist/{chunk-2VTTKH23.js.map → chunk-SEO7TICN.js.map} +0 -0
  1083. /package/dist/{chunk-3QNRBEYH.js.map → chunk-SHHJCL7X.js.map} +0 -0
  1084. /package/dist/{chunk-E6SQKHAU.js.map → chunk-SIRLACNZ.js.map} +0 -0
  1085. /package/dist/{chunk-H2TCRX3H.js.map → chunk-SKPHMYAA.js.map} +0 -0
  1086. /package/dist/{chunk-YDCUBUWH.js.map → chunk-SODC7T6L.js.map} +0 -0
  1087. /package/dist/{chunk-TNYFHKVV.js.map → chunk-SQJIKMN3.js.map} +0 -0
  1088. /package/dist/{chunk-DO74DQC7.js.map → chunk-SVINDKVW.js.map} +0 -0
  1089. /package/dist/{chunk-CPYGO76Q.js.map → chunk-SYCDHPZF.js.map} +0 -0
  1090. /package/dist/{chunk-UQ3IM6FY.js.map → chunk-T2QX3FDP.js.map} +0 -0
  1091. /package/dist/{chunk-NWGNFN3N.js.map → chunk-T3OWFVMS.js.map} +0 -0
  1092. /package/dist/{chunk-N3OARGH2.js.map → chunk-T6XI5FLY.js.map} +0 -0
  1093. /package/dist/{chunk-MQG7RV3B.js.map → chunk-T7OXJZCX.js.map} +0 -0
  1094. /package/dist/{chunk-2T7DX6KR.js.map → chunk-TAT5USZN.js.map} +0 -0
  1095. /package/dist/{chunk-ICX64J4U.js.map → chunk-TPQTRNER.js.map} +0 -0
  1096. /package/dist/{chunk-D2TOWEYH.js.map → chunk-TUIN4MB7.js.map} +0 -0
  1097. /package/dist/{chunk-LQDUHU3Y.js.map → chunk-TX3VECZU.js.map} +0 -0
  1098. /package/dist/{chunk-TG7DADJR.js.map → chunk-TXLIEOJX.js.map} +0 -0
  1099. /package/dist/{chunk-2ZVRJRXC.js.map → chunk-U3DN4HEY.js.map} +0 -0
  1100. /package/dist/{chunk-PSMAWRPL.js.map → chunk-U7XM7CB4.js.map} +0 -0
  1101. /package/dist/{chunk-4TQL3WP2.js.map → chunk-UJK53QFL.js.map} +0 -0
  1102. /package/dist/{chunk-RBRX5ZS3.js.map → chunk-UMTXA2TH.js.map} +0 -0
  1103. /package/dist/{chunk-36MIDGAH.js.map → chunk-UPDSXFK5.js.map} +0 -0
  1104. /package/dist/{chunk-OYXBFSGK.js.map → chunk-UTIAXH52.js.map} +0 -0
  1105. /package/dist/{chunk-5WUN7XPD.js.map → chunk-UVN5TMHU.js.map} +0 -0
  1106. /package/dist/{chunk-UYE47LO6.js.map → chunk-VGYXW3VG.js.map} +0 -0
  1107. /package/dist/{chunk-IKUNOX7F.js.map → chunk-VS7AHPCZ.js.map} +0 -0
  1108. /package/dist/{chunk-MYUFMVCH.js.map → chunk-VTTAJOQ7.js.map} +0 -0
  1109. /package/dist/{chunk-K63HGHZX.js.map → chunk-VWPOLUAO.js.map} +0 -0
  1110. /package/dist/{chunk-GA4YEAMX.js.map → chunk-X3MWB2QU.js.map} +0 -0
  1111. /package/dist/{chunk-I36GSRUJ.js.map → chunk-X6KAJI3V.js.map} +0 -0
  1112. /package/dist/{chunk-IKMUFDVS.js.map → chunk-XBLRHV5R.js.map} +0 -0
  1113. /package/dist/{chunk-XOSQP3YA.js.map → chunk-XDFP2LMW.js.map} +0 -0
  1114. /package/dist/{chunk-MW7Y3T55.js.map → chunk-XEQMZB3C.js.map} +0 -0
  1115. /package/dist/{chunk-B3LMRTRF.js.map → chunk-XEYLT5VA.js.map} +0 -0
  1116. /package/dist/{chunk-UI4HI5QK.js.map → chunk-XHIQ5I6N.js.map} +0 -0
  1117. /package/dist/{chunk-U6M4LS3M.js.map → chunk-XLH7QY2R.js.map} +0 -0
  1118. /package/dist/{chunk-OSA5EQG5.js.map → chunk-XWFAMUZQ.js.map} +0 -0
  1119. /package/dist/{chunk-6MT3HHUB.js.map → chunk-Y4OBB2VN.js.map} +0 -0
  1120. /package/dist/{chunk-CZP5QXNB.js.map → chunk-YCVD6CI3.js.map} +0 -0
  1121. /package/dist/{chunk-APW4T4SW.js.map → chunk-YENLNISG.js.map} +0 -0
  1122. /package/dist/{chunk-JWPA6ZPZ.js.map → chunk-YIRVLKAX.js.map} +0 -0
  1123. /package/dist/{chunk-LQ62ECE6.js.map → chunk-YL73CVNK.js.map} +0 -0
  1124. /package/dist/{chunk-ILKDYBON.js.map → chunk-YLS6QE5G.js.map} +0 -0
  1125. /package/dist/{chunk-YBKVGLQR.js.map → chunk-YPWU5KPE.js.map} +0 -0
  1126. /package/dist/{chunk-IKJLSBUI.js.map → chunk-YQXTDZSU.js.map} +0 -0
  1127. /package/dist/{chunk-TPMZBOFH.js.map → chunk-YSJEAAOL.js.map} +0 -0
  1128. /package/dist/{chunk-W3BFRR2G.js.map → chunk-YWIFM7D7.js.map} +0 -0
  1129. /package/dist/{chunk-WTJ4PLMU.js.map → chunk-YWVI6ZMK.js.map} +0 -0
  1130. /package/dist/{chunk-5KWMYPJV.js.map → chunk-Z4FMAFNH.js.map} +0 -0
  1131. /package/dist/{chunk-W5OWKVZG.js.map → chunk-Z654FWNU.js.map} +0 -0
  1132. /package/dist/{chunk-5FH5LQVS.js.map → chunk-Z7Y523PS.js.map} +0 -0
  1133. /package/dist/{chunk-HLL4PBO7.js.map → chunk-ZD67RM6G.js.map} +0 -0
  1134. /package/dist/{chunk-IVYJX3OY.js.map → chunk-ZEG4VPBL.js.map} +0 -0
  1135. /package/dist/{chunk-K6R5T44I.js.map → chunk-ZLYCOBHW.js.map} +0 -0
  1136. /package/dist/{chunk-VADHIT26.js.map → chunk-ZNJ5MTXV.js.map} +0 -0
  1137. /package/dist/{chunk-2KYQC4T5.js.map → chunk-ZSGY3H53.js.map} +0 -0
  1138. /package/dist/{chunk-BKP43KK4.js.map → chunk-ZTPXACWB.js.map} +0 -0
  1139. /package/dist/{chunk-JCY4CMDH.js.map → chunk-ZUA3JMI6.js.map} +0 -0
  1140. /package/dist/{chunk-IGVVXLKJ.js.map → chunk-ZYAZUPPV.js.map} +0 -0
  1141. /package/dist/{computer-viewer-LPZ763KF.js.map → computer-viewer-TDDO25AV.js.map} +0 -0
  1142. /package/dist/{embedded-new-voice-YVMKSLKJ.js.map → embedded-new-voice-2G3VLPVQ.js.map} +0 -0
  1143. /package/dist/{embedded-voice-BJPQFXSU.js.map → embedded-voice-T5IWOWGD.js.map} +0 -0
  1144. /package/dist/{fleet-decision-row-AA4H5CDT.js.map → fleet-decision-row-WM64KXP3.js.map} +0 -0
  1145. /package/dist/{model-policy-picker-menu-HSMWVO4P.js.map → model-policy-picker-menu-SMC6J6QN.js.map} +0 -0
  1146. /package/dist/{payment-page-card-3LTSFJGN.js.map → payment-page-card-G4U63KUV.js.map} +0 -0
  1147. /package/dist/{platform-activity-row-5VYV6TX4.js.map → platform-activity-row-BUZGZAKZ.js.map} +0 -0
  1148. /package/dist/{queue-surface-implementation-NXHZUKD2.js.map → queue-surface-implementation-4KUZ4PJV.js.map} +0 -0
  1149. /package/dist/{sandbox-files-YCMLQUFU.js.map → sandbox-files-ESDMOFSD.js.map} +0 -0
  1150. /package/dist/{session-connect-cards-KSQLMRM5.js.map → session-connect-cards-Q53KMMYY.js.map} +0 -0
  1151. /package/dist/{session-realtime-ZR6JLG3Y.js.map → session-realtime-CF3I35WJ.js.map} +0 -0
  1152. /package/dist/{timeline-annotation-markers-YAHDPWVG.js.map → timeline-annotation-markers-CJK4RYZ4.js.map} +0 -0
  1153. /package/dist/{timeline-annotation-selection-KHXNCEJP.js.map → timeline-annotation-selection-67GF5H26.js.map} +0 -0
  1154. /package/dist/{timeline-annotations-dialog-Y4XD3N7B.js.map → timeline-annotations-dialog-ONJPQDJQ.js.map} +0 -0
@@ -1,4824 +0,0 @@
1
- "use client";
2
- import {
3
- InteractionInterventionBanner,
4
- isInteractionControlUnavailable,
5
- isNonRetryableInteractionError,
6
- isSourcePlacementChangedError,
7
- useInteractionInterventions,
8
- useInteractionInvalidation,
9
- useViewerMenuDismiss
10
- } from "./chunk-QIK2Q6N5.js";
11
- import {
12
- useClientConfigFlags
13
- } from "./chunk-XESA3B7W.js";
14
- import {
15
- copyTextToClipboard
16
- } from "./chunk-PEHTX3V7.js";
17
- import {
18
- usePageLiveActivity
19
- } from "./chunk-RV3O5OCQ.js";
20
- import {
21
- formatBytes
22
- } from "./chunk-5MOJZORA.js";
23
- import {
24
- interactionHostPlatform
25
- } from "./chunk-ZUWF4NF7.js";
26
- import {
27
- STREAM_CLOSE_REASON_NORMAL,
28
- STREAM_KIND_BROWSER,
29
- STREAM_ROLE_CLIENT,
30
- decodeStreamFrame,
31
- decodeStreamOpenAck,
32
- encodeStreamClose,
33
- encodeStreamOpen
34
- } from "./chunk-TNEZNMSN.js";
35
- import {
36
- useEmbeddedBrowserInteraction
37
- } from "./chunk-TVQCDDSZ.js";
38
- import {
39
- cn
40
- } from "./chunk-TXS65BB2.js";
41
- import {
42
- useOpenGeniText
43
- } from "./chunk-TNUSPXPW.js";
44
-
45
- // src/components/browser-viewer.tsx
46
- import { interactionControlFailureFromError } from "@opengeni/sdk/interaction";
47
- import {
48
- BugIcon,
49
- ArchiveIcon,
50
- ArrowLeftIcon,
51
- ArrowRightIcon,
52
- CheckIcon,
53
- ChevronDownIcon,
54
- CircleAlertIcon,
55
- DownloadIcon,
56
- FileCheck2Icon,
57
- Globe2Icon,
58
- LoaderCircleIcon,
59
- MonitorIcon,
60
- PlusIcon,
61
- RefreshCwIcon,
62
- RotateCcwIcon,
63
- SaveIcon,
64
- UserRoundIcon,
65
- XIcon,
66
- ZapIcon
67
- } from "lucide-react";
68
- import {
69
- useCallback as useCallback8,
70
- useEffect as useEffect9,
71
- useId,
72
- useLayoutEffect as useLayoutEffect2,
73
- useMemo as useMemo2,
74
- useRef as useRef9,
75
- useState as useState9
76
- } from "react";
77
-
78
- // src/hooks/use-browser-frame-stream.ts
79
- import {
80
- browserFrameSocketUrl,
81
- decodeBrowserFrameMessage
82
- } from "@opengeni/sdk/interaction";
83
- import { useCallback, useEffect, useRef, useState } from "react";
84
- import { errorMessage } from "@opengeni/contracts/values";
85
- var RELAY_TAG_OPEN = 1;
86
- var RELAY_TAG_OPEN_ACK = 2;
87
- var RELAY_TAG_FRAME = 3;
88
- var RELAY_TAG_CLOSE = 4;
89
- var MAX_AUTOMATIC_RECONNECTS_WITHOUT_A_FRAME = 2;
90
- function useBrowserFrameStream(options) {
91
- const t = useOpenGeniText();
92
- const { client, workspaceId } = useEmbeddedBrowserInteraction(options);
93
- const pageLive = usePageLiveActivity();
94
- const enabled = (options.enabled ?? true) && pageLive;
95
- const [nonce, setNonce] = useState(0);
96
- const [result, setResult] = useState({
97
- state: "idle",
98
- frame: null,
99
- attachment: null,
100
- error: null
101
- });
102
- const latestRef = useRef({
103
- key: "",
104
- sequence: -1
105
- });
106
- const factoryRef = useRef(options.webSocketFactory);
107
- const streamRef = useRef(options.stream);
108
- factoryRef.current = options.webSocketFactory;
109
- streamRef.current = options.stream;
110
- const reconnect = useCallback(() => setNonce((value) => value + 1), []);
111
- useEffect(() => {
112
- const browserSessionId = options.browserSessionId;
113
- const targetId = options.targetId;
114
- if (!enabled || !browserSessionId || !targetId || typeof window === "undefined") {
115
- setResult({ state: "idle", frame: null, attachment: null, error: null });
116
- return;
117
- }
118
- latestRef.current = { key: "", sequence: -1 };
119
- setResult({
120
- state: "attaching",
121
- frame: null,
122
- attachment: null,
123
- error: null
124
- });
125
- let disposed = false;
126
- let socket = null;
127
- let socketHandlers = null;
128
- let reconnectTimer = null;
129
- let expiryTimer = null;
130
- let failures = 0;
131
- let controllerGeneration = null;
132
- let activeAttachment = null;
133
- let activeStream = null;
134
- let attachmentExpiresAt = 0;
135
- let lastRelaySequence = null;
136
- let relayAccepted = false;
137
- let pendingFrame = null;
138
- let decodingFrame = false;
139
- const attachmentAbort = new AbortController();
140
- const clearSocket = (terminateProducer = false) => {
141
- if (!socket) return;
142
- const current = socket;
143
- const handlers = socketHandlers;
144
- if (terminateProducer && activeStream?.kind === "relay" && socket.readyState === 1) {
145
- try {
146
- socket.send(
147
- relayDatagram(
148
- RELAY_TAG_CLOSE,
149
- encodeStreamClose({
150
- channelId: activeStream.channel.channelId,
151
- reason: STREAM_CLOSE_REASON_NORMAL,
152
- message: "viewer detached"
153
- })
154
- )
155
- );
156
- } catch {
157
- }
158
- }
159
- if (handlers) {
160
- current.removeEventListener("open", handlers.open);
161
- current.removeEventListener("message", handlers.message);
162
- current.removeEventListener("error", handlers.error);
163
- current.removeEventListener("close", handlers.close);
164
- }
165
- if (current.readyState === 0 || current.readyState === 1) {
166
- current.close(1e3, "viewer detached");
167
- }
168
- socket = null;
169
- socketHandlers = null;
170
- pendingFrame = null;
171
- };
172
- const scheduleReconnect = () => {
173
- if (disposed || reconnectTimer) return;
174
- if (failures >= MAX_AUTOMATIC_RECONNECTS_WITHOUT_A_FRAME) return;
175
- const delay = Math.min(5e3, 250 * 2 ** Math.min(failures, 5));
176
- failures += 1;
177
- setResult((current) => ({ ...current, state: "reconnecting" }));
178
- reconnectTimer = setTimeout(() => {
179
- reconnectTimer = null;
180
- connectSocket();
181
- }, delay);
182
- };
183
- const fail = (cause, reconnectAutomatically = true) => {
184
- if (disposed) return;
185
- const error = cause instanceof Error ? cause : new Error(String(cause));
186
- setResult((current) => ({ ...current, state: "error", error }));
187
- clearSocket();
188
- if (reconnectAutomatically) scheduleReconnect();
189
- };
190
- const onOpen = (source) => {
191
- if (disposed || source !== socket) return;
192
- if (activeStream?.kind === "relay") {
193
- const body = encodeStreamOpen({
194
- channel: {
195
- ...activeStream.channel,
196
- kind: STREAM_KIND_BROWSER
197
- },
198
- token: activeStream.token,
199
- role: STREAM_ROLE_CLIENT,
200
- resumeFromSeq: lastRelaySequence === null ? "0" : (BigInt(lastRelaySequence) + 1n).toString()
201
- });
202
- try {
203
- source.send(relayDatagram(RELAY_TAG_OPEN, body));
204
- } catch (error) {
205
- fail(error);
206
- }
207
- return;
208
- }
209
- setResult((current) => ({ ...current, state: "live", error: null }));
210
- };
211
- const drainLatestFrame = async () => {
212
- if (decodingFrame) return;
213
- decodingFrame = true;
214
- try {
215
- while (pendingFrame) {
216
- if (disposed) break;
217
- const pending = pendingFrame;
218
- pendingFrame = null;
219
- const { bytes, source } = pending;
220
- const frame = decodeBrowserFrameMessage(bytes);
221
- if (disposed || source !== socket) continue;
222
- if (frame.browserSessionId !== browserSessionId || frame.targetId !== targetId) {
223
- throw new Error("browser frame belongs to another resource");
224
- }
225
- if (!controllerGeneration || frame.controllerGeneration !== controllerGeneration) {
226
- throw new Error("browser frame belongs to a stale controller");
227
- }
228
- const key = `${browserSessionId}:${targetId}:${frame.controllerGeneration}:${frame.targetGeneration}:${frame.documentGeneration}`;
229
- if (latestRef.current.key === key && frame.sequence <= latestRef.current.sequence)
230
- continue;
231
- latestRef.current = { key, sequence: frame.sequence };
232
- failures = 0;
233
- setResult((current) => ({
234
- ...current,
235
- state: "live",
236
- frame,
237
- error: null
238
- }));
239
- }
240
- } catch (cause) {
241
- fail(cause);
242
- } finally {
243
- decodingFrame = false;
244
- if (!disposed && pendingFrame) void drainLatestFrame();
245
- }
246
- };
247
- const onMessage = (source, event) => {
248
- void (async () => {
249
- try {
250
- const bytes = await messageBytes(event.data);
251
- if (disposed || source !== socket) return;
252
- let frameBytes = bytes;
253
- if (activeStream?.kind === "relay") {
254
- if (bytes.length < 1) throw new Error("browser relay returned an empty message");
255
- const tag = bytes[0];
256
- const body = bytes.subarray(1);
257
- if (tag === RELAY_TAG_OPEN_ACK) {
258
- const ack = decodeStreamOpenAck(body);
259
- if (!ack.accepted) {
260
- activeAttachment = null;
261
- activeStream = null;
262
- attachmentExpiresAt = 0;
263
- throw new Error(ack.error?.message ?? "browser stream was rejected by the relay");
264
- }
265
- relayAccepted = true;
266
- return;
267
- }
268
- if (tag === RELAY_TAG_CLOSE) {
269
- activeAttachment = null;
270
- activeStream = null;
271
- attachmentExpiresAt = 0;
272
- lastRelaySequence = null;
273
- relayAccepted = false;
274
- fail(new Error("Browser frame source ended."), false);
275
- return;
276
- }
277
- if (tag !== RELAY_TAG_FRAME || !relayAccepted) return;
278
- const relayFrame = decodeStreamFrame(body);
279
- lastRelaySequence = relayFrame.seq;
280
- frameBytes = relayFrame.data;
281
- }
282
- pendingFrame = { bytes: frameBytes, source };
283
- void drainLatestFrame();
284
- } catch (cause) {
285
- fail(cause);
286
- }
287
- })();
288
- };
289
- const onError = (source) => {
290
- if (source !== socket) return;
291
- fail(new Error(t("hooks.useBrowserFrameStream.browserViewLostConnection")));
292
- };
293
- const onClose = (source) => {
294
- if (disposed || source !== socket) return;
295
- clearSocket();
296
- scheduleReconnect();
297
- };
298
- const connectSocket = () => {
299
- if (disposed) return;
300
- clearSocket();
301
- if (!activeAttachment || !activeStream || attachmentExpiresAt <= Date.now() + 1e3) {
302
- void attach();
303
- return;
304
- }
305
- relayAccepted = false;
306
- setResult((current) => ({
307
- ...current,
308
- state: "connecting",
309
- error: null
310
- }));
311
- const createSocket = factoryRef.current ?? ((url, protocols) => new WebSocket(url, protocols));
312
- const openedSocket = createSocket(
313
- activeStream.kind === "relay" ? activeStream.url : browserFrameSocketUrl(activeAttachment, streamRef.current),
314
- activeStream.kind === "direct_websocket" ? [...activeStream.protocols] : []
315
- );
316
- latestRef.current = { key: "", sequence: -1 };
317
- socket = openedSocket;
318
- socketHandlers = {
319
- open: () => onOpen(openedSocket),
320
- message: (event) => onMessage(openedSocket, event),
321
- error: () => onError(openedSocket),
322
- close: () => onClose(openedSocket)
323
- };
324
- openedSocket.binaryType = "arraybuffer";
325
- openedSocket.addEventListener("open", socketHandlers.open);
326
- openedSocket.addEventListener("message", socketHandlers.message);
327
- openedSocket.addEventListener("error", socketHandlers.error);
328
- openedSocket.addEventListener("close", socketHandlers.close);
329
- };
330
- const attach = async () => {
331
- if (disposed) return;
332
- clearSocket();
333
- if (expiryTimer) clearTimeout(expiryTimer);
334
- setResult((current) => ({
335
- ...current,
336
- state: current.attachment ? "reconnecting" : "attaching",
337
- error: null
338
- }));
339
- try {
340
- const attachment = await client.attachBrowserSession(
341
- workspaceId,
342
- browserSessionId,
343
- {
344
- targetId,
345
- expiresInSeconds: 120,
346
- ...streamRef.current ? { stream: streamRef.current } : {}
347
- },
348
- { signal: attachmentAbort.signal }
349
- );
350
- if (disposed) return;
351
- if (attachment.browserSessionId !== browserSessionId || attachment.targetId !== targetId) {
352
- throw new Error("browser attachment does not match the requested resource");
353
- }
354
- controllerGeneration = attachment.controllerGeneration;
355
- activeAttachment = attachment;
356
- activeStream = attachment.stream;
357
- attachmentExpiresAt = Date.parse(attachment.expiresAt);
358
- lastRelaySequence = null;
359
- relayAccepted = false;
360
- setResult((current) => ({
361
- ...current,
362
- state: "connecting",
363
- attachment: {
364
- browserSessionId: attachment.browserSessionId,
365
- controllerGeneration: attachment.controllerGeneration,
366
- targetId: attachment.targetId,
367
- expiresAt: attachment.expiresAt,
368
- ...attachment.fencedInputBatches === true ? { fencedInputBatches: true } : {},
369
- ...attachment.focusedInputObservations === true ? { focusedInputObservations: true } : {}
370
- },
371
- error: null
372
- }));
373
- const refreshIn = Number.isFinite(attachmentExpiresAt) ? Math.max(1e3, attachmentExpiresAt - Date.now() - 5e3) : 6e4;
374
- expiryTimer = setTimeout(() => {
375
- if (disposed) return;
376
- clearSocket(true);
377
- activeAttachment = null;
378
- activeStream = null;
379
- attachmentExpiresAt = 0;
380
- void attach();
381
- }, refreshIn);
382
- connectSocket();
383
- } catch (cause) {
384
- if (attachmentAbort.signal.aborted || disposed) return;
385
- fail(cause, !isPlacementGenerationLossError(cause));
386
- }
387
- };
388
- void attach();
389
- return () => {
390
- disposed = true;
391
- attachmentAbort.abort();
392
- if (reconnectTimer) clearTimeout(reconnectTimer);
393
- if (expiryTimer) clearTimeout(expiryTimer);
394
- clearSocket(true);
395
- };
396
- }, [client, enabled, nonce, options.browserSessionId, options.targetId, workspaceId, t]);
397
- return { ...result, reconnect };
398
- }
399
- function relayDatagram(tag, body) {
400
- const message = new Uint8Array(body.length + 1);
401
- message[0] = tag;
402
- message.set(body, 1);
403
- return message.buffer;
404
- }
405
- async function messageBytes(value) {
406
- if (value instanceof ArrayBuffer) return new Uint8Array(value);
407
- if (ArrayBuffer.isView(value)) {
408
- return new Uint8Array(value.buffer, value.byteOffset, value.byteLength).slice();
409
- }
410
- if (typeof Blob !== "undefined" && value instanceof Blob) {
411
- return new Uint8Array(await value.arrayBuffer());
412
- }
413
- throw new Error("browser frame stream returned a non-binary message");
414
- }
415
- function isPlacementGenerationLossError(cause) {
416
- const message = errorMessage(cause);
417
- return /placement instance changed|controller authority changed/i.test(message);
418
- }
419
-
420
- // src/hooks/use-attached-browsers.ts
421
- import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
422
- function useAttachedBrowsers(options = {}) {
423
- const { client, workspaceId, workspaceInteractionEvent, workspaceInteractionConnectionState } = useEmbeddedBrowserInteraction(options);
424
- const enabled = options.enabled ?? true;
425
- const includeDisconnected = options.includeDisconnected ?? false;
426
- const [state, setState] = useState2(() => emptyState(workspaceId, enabled));
427
- const requestRef = useRef2({
428
- id: 0,
429
- controller: null
430
- });
431
- const mountedRef = useRef2(true);
432
- const visible = state.workspaceId === workspaceId ? state : emptyState(workspaceId, enabled);
433
- const cancelLoad = useCallback2(() => {
434
- const id = requestRef.current.id + 1;
435
- requestRef.current.controller?.abort();
436
- requestRef.current = { id, controller: null };
437
- }, []);
438
- const load = useCallback2(
439
- async (foreground) => {
440
- if (!enabled) return;
441
- const id = requestRef.current.id + 1;
442
- requestRef.current.controller?.abort();
443
- const controller = new AbortController();
444
- requestRef.current = { id, controller };
445
- setState((current) => ({
446
- ...current.workspaceId === workspaceId ? current : emptyState(workspaceId, true),
447
- loading: foreground,
448
- refreshing: !foreground,
449
- error: foreground || current.workspaceId !== workspaceId ? null : current.error
450
- }));
451
- try {
452
- const response = await client.listAttachedBrowsers(workspaceId, {
453
- includeDisconnected,
454
- signal: controller.signal
455
- });
456
- if (!mountedRef.current || requestRef.current.id !== id) return;
457
- setState({
458
- workspaceId,
459
- revision: response.revision,
460
- bridges: response.bridges,
461
- devices: sortDevices(response.devices),
462
- loading: false,
463
- refreshing: false,
464
- error: null
465
- });
466
- } catch (cause) {
467
- if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
468
- return;
469
- setState((current) => ({
470
- ...current.workspaceId === workspaceId ? current : emptyState(workspaceId, true),
471
- loading: false,
472
- refreshing: false,
473
- error: cause instanceof Error ? cause : new Error(String(cause))
474
- }));
475
- } finally {
476
- if (requestRef.current.id === id) requestRef.current = { id, controller: null };
477
- }
478
- },
479
- [client, enabled, includeDisconnected, workspaceId]
480
- );
481
- const refresh = useCallback2(async () => await load(false), [load]);
482
- useEffect2(() => {
483
- mountedRef.current = true;
484
- if (!enabled) {
485
- cancelLoad();
486
- setState(emptyState(workspaceId, false));
487
- return;
488
- }
489
- void load(true);
490
- return () => {
491
- mountedRef.current = false;
492
- cancelLoad();
493
- };
494
- }, [cancelLoad, enabled, load, workspaceId]);
495
- useInteractionInvalidation({
496
- workspaceId,
497
- key: `${workspaceId}:${includeDisconnected}`,
498
- enabled,
499
- event: workspaceInteractionEvent,
500
- connectionState: workspaceInteractionConnectionState,
501
- knownRevision: visible.revision,
502
- refresh,
503
- fallbackPollIntervalMs: options.pollIntervalMs
504
- });
505
- return {
506
- revision: visible.revision,
507
- bridges: visible.bridges,
508
- devices: visible.devices,
509
- loading: visible.loading,
510
- refreshing: visible.refreshing,
511
- error: visible.error,
512
- refresh
513
- };
514
- }
515
- function emptyState(workspaceId, loading) {
516
- return {
517
- workspaceId,
518
- revision: 0,
519
- bridges: [],
520
- devices: [],
521
- loading,
522
- refreshing: false,
523
- error: null
524
- };
525
- }
526
- function sortDevices(devices) {
527
- return [...devices].sort(
528
- (left, right) => Number(right.state === "connected") - Number(left.state === "connected") || timestamp(right.lastSeenAt) - timestamp(left.lastSeenAt) || left.name.localeCompare(right.name)
529
- );
530
- }
531
- function timestamp(value) {
532
- const parsed = Date.parse(value);
533
- return Number.isFinite(parsed) ? parsed : 0;
534
- }
535
-
536
- // src/hooks/use-browser-identities.ts
537
- import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
538
- function useBrowserIdentities(options = {}) {
539
- const { client, workspaceId, workspaceInteractionEvent, workspaceInteractionConnectionState } = useEmbeddedBrowserInteraction(options);
540
- const enabled = options.enabled ?? true;
541
- const includeArchived = options.includeArchived ?? false;
542
- const [state, setState] = useState3(() => emptyState2(workspaceId, enabled));
543
- const [mutationCount, setMutationCount] = useState3(0);
544
- const requestRef = useRef3({
545
- id: 0,
546
- controller: null
547
- });
548
- const mountedRef = useRef3(true);
549
- const visibleState = state.workspaceId === workspaceId ? state : emptyState2(workspaceId, enabled);
550
- const cancelLoad = useCallback3(() => {
551
- const id = requestRef.current.id + 1;
552
- requestRef.current.controller?.abort();
553
- requestRef.current = { id, controller: null };
554
- }, []);
555
- const load = useCallback3(
556
- async (foreground) => {
557
- if (!enabled) return;
558
- const id = requestRef.current.id + 1;
559
- requestRef.current.controller?.abort();
560
- const controller = new AbortController();
561
- requestRef.current = { id, controller };
562
- setState((current) => ({
563
- ...current.workspaceId === workspaceId ? current : emptyState2(workspaceId, true),
564
- loading: foreground,
565
- refreshing: !foreground,
566
- error: foreground || current.workspaceId !== workspaceId ? null : current.error
567
- }));
568
- try {
569
- const response = await client.listBrowserIdentities(workspaceId, {
570
- includeArchived,
571
- signal: controller.signal
572
- });
573
- if (!mountedRef.current || requestRef.current.id !== id) return;
574
- setState({
575
- workspaceId,
576
- revision: response.revision,
577
- identities: sortIdentities(response.identities),
578
- loading: false,
579
- refreshing: false,
580
- error: null
581
- });
582
- } catch (cause) {
583
- if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
584
- return;
585
- setState((current) => ({
586
- ...current.workspaceId === workspaceId ? current : emptyState2(workspaceId, true),
587
- loading: false,
588
- refreshing: false,
589
- error: cause instanceof Error ? cause : new Error(String(cause))
590
- }));
591
- } finally {
592
- if (requestRef.current.id === id) requestRef.current = { id, controller: null };
593
- }
594
- },
595
- [client, enabled, includeArchived, workspaceId]
596
- );
597
- const refresh = useCallback3(async () => await load(false), [load]);
598
- useEffect3(() => {
599
- mountedRef.current = true;
600
- if (!enabled) {
601
- cancelLoad();
602
- setState(emptyState2(workspaceId, false));
603
- return;
604
- }
605
- void load(true);
606
- return () => {
607
- mountedRef.current = false;
608
- cancelLoad();
609
- };
610
- }, [cancelLoad, enabled, load, workspaceId]);
611
- useInteractionInvalidation({
612
- workspaceId,
613
- key: workspaceId,
614
- enabled,
615
- event: workspaceInteractionEvent,
616
- connectionState: workspaceInteractionConnectionState,
617
- knownRevision: visibleState.revision,
618
- refresh
619
- });
620
- const mutate = useCallback3(async (operation) => {
621
- setMutationCount((count) => count + 1);
622
- try {
623
- return await operation();
624
- } finally {
625
- if (mountedRef.current) setMutationCount((count) => Math.max(0, count - 1));
626
- }
627
- }, []);
628
- const create = useCallback3(
629
- async (request) => await mutate(async () => {
630
- const response = await client.createBrowserIdentity(workspaceId, {
631
- ...request,
632
- operationId: request.operationId ?? crypto.randomUUID()
633
- });
634
- setState((current) => mergeIdentity(current, workspaceId, response.identity));
635
- return response;
636
- }),
637
- [client, mutate, workspaceId]
638
- );
639
- const revisions = useCallback3(
640
- async (identityId) => await client.listBrowserRevisions(workspaceId, identityId),
641
- [client, workspaceId]
642
- );
643
- const update = useCallback3(
644
- async (identityId, request) => await mutate(async () => {
645
- const response = await client.updateBrowserIdentity(workspaceId, identityId, {
646
- ...request,
647
- operationId: request.operationId ?? crypto.randomUUID()
648
- });
649
- setState(
650
- (current) => mergeIdentity(current, workspaceId, response.identity, includeArchived)
651
- );
652
- return response;
653
- }),
654
- [client, includeArchived, mutate, workspaceId]
655
- );
656
- const publish = useCallback3(
657
- async (browserSessionId, request) => await mutate(async () => {
658
- const response = await client.publishBrowserRevision(workspaceId, browserSessionId, {
659
- ...request,
660
- operationId: request.operationId ?? crypto.randomUUID()
661
- });
662
- setState((current) => mergeIdentity(current, workspaceId, response.identity));
663
- return response;
664
- }),
665
- [client, mutate, workspaceId]
666
- );
667
- return {
668
- revision: visibleState.revision,
669
- identities: visibleState.identities,
670
- loading: visibleState.loading,
671
- refreshing: visibleState.refreshing,
672
- mutating: mutationCount > 0,
673
- error: visibleState.error,
674
- refresh,
675
- create,
676
- update,
677
- revisions,
678
- publish
679
- };
680
- }
681
- function emptyState2(workspaceId, loading) {
682
- return {
683
- workspaceId,
684
- revision: 0,
685
- identities: [],
686
- loading,
687
- refreshing: false,
688
- error: null
689
- };
690
- }
691
- function sortIdentities(identities) {
692
- return [...identities].sort(
693
- (left, right) => timestamp2(right.updatedAt) - timestamp2(left.updatedAt) || left.name.localeCompare(right.name)
694
- );
695
- }
696
- function timestamp2(value) {
697
- const parsed = Date.parse(value);
698
- return Number.isFinite(parsed) ? parsed : 0;
699
- }
700
- function mergeIdentity(current, workspaceId, identity, includeArchived = false) {
701
- if (current.workspaceId !== workspaceId) return current;
702
- return {
703
- ...current,
704
- identities: sortIdentities(
705
- identity.status === "archived" && !includeArchived ? current.identities.filter((candidate) => candidate.id !== identity.id) : [...current.identities.filter((candidate) => candidate.id !== identity.id), identity]
706
- ),
707
- error: null
708
- };
709
- }
710
-
711
- // src/hooks/use-browser-downloads.ts
712
- import { useCallback as useCallback4, useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
713
- function useBrowserDownloads(options) {
714
- const { client, workspaceId } = useEmbeddedBrowserInteraction(options);
715
- const browserSessionId = options.browserSessionId;
716
- const enabled = (options.enabled ?? true) && browserSessionId !== null;
717
- const pageLive = usePageLiveActivity();
718
- const pollIntervalMs = Math.max(750, options.pollIntervalMs ?? 2e3);
719
- const scope = browserSessionId ?? "none";
720
- const [state, setState] = useState4(() => emptyState3(scope, enabled));
721
- const [saving, setSaving] = useState4({});
722
- const visible = state.scope === scope ? state : emptyState3(scope, enabled);
723
- const requestRef = useRef4({
724
- id: 0,
725
- controller: null
726
- });
727
- const mountedRef = useRef4(true);
728
- const retryOperationsRef = useRef4(/* @__PURE__ */ new Map());
729
- const cancelLoad = useCallback4(() => {
730
- requestRef.current.controller?.abort();
731
- requestRef.current = { id: requestRef.current.id + 1, controller: null };
732
- }, []);
733
- const load = useCallback4(
734
- async (foreground) => {
735
- if (!enabled || !browserSessionId) return;
736
- const id = requestRef.current.id + 1;
737
- requestRef.current.controller?.abort();
738
- const controller = new AbortController();
739
- requestRef.current = { id, controller };
740
- setState((current) => ({
741
- ...current.scope === scope ? current : emptyState3(scope, true),
742
- loading: foreground,
743
- refreshing: !foreground,
744
- error: foreground || current.scope !== scope ? null : current.error
745
- }));
746
- try {
747
- const response = await client.listBrowserDownloads(workspaceId, browserSessionId, {
748
- signal: controller.signal
749
- });
750
- if (!mountedRef.current || requestRef.current.id !== id) return;
751
- setState({
752
- scope,
753
- downloads: sortDownloads(response.downloads),
754
- loading: false,
755
- refreshing: false,
756
- error: null
757
- });
758
- } catch (cause) {
759
- if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
760
- return;
761
- setState((current) => ({
762
- ...current.scope === scope ? current : emptyState3(scope, true),
763
- loading: false,
764
- refreshing: false,
765
- error: asError(cause)
766
- }));
767
- } finally {
768
- if (requestRef.current.id === id) requestRef.current = { id, controller: null };
769
- }
770
- },
771
- [browserSessionId, client, enabled, scope, workspaceId]
772
- );
773
- const refresh = useCallback4(async () => await load(false), [load]);
774
- useEffect4(() => {
775
- retryOperationsRef.current.clear();
776
- setSaving({});
777
- }, [scope]);
778
- useEffect4(() => {
779
- mountedRef.current = true;
780
- if (!enabled) {
781
- cancelLoad();
782
- setState(emptyState3(scope, false));
783
- return () => {
784
- mountedRef.current = false;
785
- cancelLoad();
786
- };
787
- }
788
- void load(true);
789
- return () => {
790
- mountedRef.current = false;
791
- cancelLoad();
792
- };
793
- }, [cancelLoad, enabled, load, scope]);
794
- useEffect4(() => {
795
- if (!enabled || !pageLive || Object.keys(saving).length > 0) return;
796
- const timer = setInterval(() => {
797
- if (!requestRef.current.controller) void refresh();
798
- }, pollIntervalMs);
799
- return () => clearInterval(timer);
800
- }, [enabled, pageLive, pollIntervalMs, refresh, saving]);
801
- const saveToWorkspace = useCallback4(
802
- async (downloadId, destinationPath, saveOptions = {}) => {
803
- if (!browserSessionId) throw new Error("No BrowserSession is selected.");
804
- const overwrite = saveOptions.overwrite ?? false;
805
- const attemptKey = `${browserSessionId}\0${downloadId}\0${destinationPath}\0${overwrite}`;
806
- const operationId = saveOptions.operationId ?? retryOperationsRef.current.get(attemptKey) ?? crypto.randomUUID();
807
- retryOperationsRef.current.set(attemptKey, operationId);
808
- setSaving((current) => ({ ...current, [downloadId]: (current[downloadId] ?? 0) + 1 }));
809
- try {
810
- const response = await client.saveBrowserDownload(
811
- workspaceId,
812
- browserSessionId,
813
- downloadId,
814
- { operationId, destinationPath, overwrite }
815
- );
816
- retryOperationsRef.current.delete(attemptKey);
817
- if (mountedRef.current) {
818
- setState(
819
- (current) => current.scope === scope ? {
820
- ...current,
821
- downloads: sortDownloads([
822
- ...current.downloads.filter((download) => download.id !== response.download.id),
823
- response.download
824
- ]),
825
- error: null
826
- } : current
827
- );
828
- }
829
- return response;
830
- } finally {
831
- if (mountedRef.current) {
832
- setSaving((current) => {
833
- const count = (current[downloadId] ?? 1) - 1;
834
- if (count > 0) return { ...current, [downloadId]: count };
835
- const { [downloadId]: _removed, ...rest } = current;
836
- return rest;
837
- });
838
- }
839
- }
840
- },
841
- [browserSessionId, client, scope, workspaceId]
842
- );
843
- return {
844
- downloads: visible.downloads,
845
- loading: visible.loading,
846
- refreshing: visible.refreshing,
847
- savingDownloadIds: Object.keys(saving),
848
- error: visible.error,
849
- refresh,
850
- saveToWorkspace
851
- };
852
- }
853
- function emptyState3(scope, loading) {
854
- return {
855
- scope,
856
- downloads: [],
857
- loading,
858
- refreshing: false,
859
- error: null
860
- };
861
- }
862
- function sortDownloads(downloads) {
863
- return [...downloads].sort(
864
- (left, right) => right.startedAt.localeCompare(left.startedAt) || left.id.localeCompare(right.id)
865
- );
866
- }
867
- function asError(cause) {
868
- return cause instanceof Error ? cause : new Error(String(cause));
869
- }
870
-
871
- // src/hooks/use-browser-session.ts
872
- import { OpenGeniApiError } from "@opengeni/sdk";
873
- import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef5, useState as useState5 } from "react";
874
- function useBrowserSession(options) {
875
- const t = useOpenGeniText();
876
- const { client, workspaceId } = useEmbeddedBrowserInteraction(options);
877
- const browserSessionId = options.browserSessionId;
878
- const enabled = (options.enabled ?? true) && browserSessionId !== null;
879
- const sourceRef = useRef5({ client, workspaceId, browserSessionId, enabled });
880
- if (sourceRef.current.client !== client || sourceRef.current.workspaceId !== workspaceId || sourceRef.current.browserSessionId !== browserSessionId || sourceRef.current.enabled !== enabled) {
881
- sourceRef.current = { client, workspaceId, browserSessionId, enabled };
882
- }
883
- const source = sourceRef.current;
884
- const semanticObservationEnabled = options.semanticObservationEnabled ?? true;
885
- const semanticObservationEnabledRef = useRef5(semanticObservationEnabled);
886
- semanticObservationEnabledRef.current = semanticObservationEnabled;
887
- const previousSemanticObservationRef = useRef5({
888
- browserSessionId,
889
- enabled: semanticObservationEnabled
890
- });
891
- const pageLive = usePageLiveActivity();
892
- const pollIntervalMs = Math.max(750, options.pollIntervalMs ?? 2e3);
893
- const [state, setState] = useState5(() => ({ ...emptyState4(browserSessionId, enabled), source }));
894
- const visible = state.source === source && state.browserSessionId === browserSessionId ? state : { ...emptyState4(browserSessionId, enabled), source };
895
- const refreshBlocked = isNonRetryableInteractionError(visible.error);
896
- const inputFailureRef = useRef5(visible.inputFailure);
897
- inputFailureRef.current = visible.inputFailure;
898
- const selectedTargetIdRef = useRef5(visible.selectedTargetId);
899
- selectedTargetIdRef.current = visible.selectedTargetId;
900
- const selectionRevisionRef = useRef5(0);
901
- const actionResultOrderRef = useRef5({ next: 0, settled: 0 });
902
- const selectedTargetRef = useRef5(
903
- visible.targets.find((target) => target.id === visible.selectedTargetId) ?? null
904
- );
905
- selectedTargetRef.current = visible.targets.find((target) => target.id === visible.selectedTargetId) ?? null;
906
- const observationRef = useRef5({ browserSessionId, observation: visible.observation });
907
- const lastObservationAtRef = useRef5(0);
908
- if (observationRef.current.browserSessionId !== browserSessionId) {
909
- observationRef.current = {
910
- browserSessionId,
911
- observation: visible.observation
912
- };
913
- } else {
914
- observationRef.current.observation = visible.observation;
915
- }
916
- const requestRef = useRef5({
917
- id: 0,
918
- controller: null
919
- });
920
- const mutationRef = useRef5({
921
- browserSessionId,
922
- source,
923
- count: 0,
924
- needsInventoryReconcile: false
925
- });
926
- const mountedRef = useRef5(true);
927
- const isCurrentSource = useCallback5(
928
- () => mountedRef.current && sourceRef.current === source,
929
- [source]
930
- );
931
- const invalidateRefresh = useCallback5(() => {
932
- const id = requestRef.current.id + 1;
933
- requestRef.current.controller?.abort();
934
- requestRef.current = { id, controller: null };
935
- }, []);
936
- const refreshInventory = useCallback5(
937
- async (checkInput = false, observeSelectedTarget = true) => {
938
- if (!enabled || !browserSessionId || !isCurrentSource()) return;
939
- const checkedInputFailure = inputFailureRef.current;
940
- const id = requestRef.current.id + 1;
941
- requestRef.current.controller?.abort();
942
- const controller = new AbortController();
943
- requestRef.current = { id, controller };
944
- try {
945
- const [session, targetResponse] = await Promise.all([
946
- client.getBrowserSession(workspaceId, browserSessionId, {
947
- signal: controller.signal
948
- }),
949
- client.listBrowserTargets(workspaceId, browserSessionId, {
950
- signal: controller.signal
951
- })
952
- ]);
953
- if (!isCurrentSource() || requestRef.current.id !== id) return;
954
- const selected = chooseTarget(targetResponse.targets, selectedTargetIdRef.current);
955
- selectedTargetIdRef.current = selected?.id ?? null;
956
- selectedTargetRef.current = selected;
957
- const previousObservation = observationRef.current.browserSessionId === browserSessionId ? observationRef.current.observation : null;
958
- const retainedObservation = selected && previousObservation && sameObservationTarget(previousObservation, selected) ? previousObservation : null;
959
- observationRef.current = {
960
- browserSessionId,
961
- observation: retainedObservation
962
- };
963
- setState(
964
- (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? {
965
- ...current,
966
- session,
967
- targets: targetResponse.targets,
968
- selectedTargetId: selected?.id ?? null,
969
- observation: retainedObservation,
970
- loading: false,
971
- error: null,
972
- // Media and background polling cannot confirm an input outcome.
973
- // Only an explicit fresh check clears the notice it began with.
974
- inputFailure: checkInput && session.id === browserSessionId && targetResponse.browserSessionId === browserSessionId && current.inputFailure === checkedInputFailure ? null : current.inputFailure
975
- } : current
976
- );
977
- if (!selected || !observeSelectedTarget || !semanticObservationEnabledRef.current) return;
978
- try {
979
- const observation = await client.observeBrowserTarget(
980
- workspaceId,
981
- browserSessionId,
982
- selected.id,
983
- { signal: controller.signal }
984
- );
985
- if (!isCurrentSource() || requestRef.current.id !== id) return;
986
- const currentSelected = selectedTargetRef.current;
987
- if (observationRef.current.browserSessionId !== browserSessionId || currentSelected?.id !== selected.id || observation.browserSessionId !== browserSessionId || observation.target.id !== selected.id || observation.target.controllerGeneration !== currentSelected.controllerGeneration || observation.target.targetGeneration !== currentSelected.targetGeneration)
988
- return;
989
- selectedTargetRef.current = observation.target;
990
- observationRef.current = { browserSessionId, observation };
991
- lastObservationAtRef.current = Date.now();
992
- setState(
993
- (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId && current.selectedTargetId === observation.target.id ? {
994
- ...current,
995
- observation,
996
- targets: replaceTarget(current.targets, observation.target)
997
- } : current
998
- );
999
- } catch (cause) {
1000
- if (controller.signal.aborted || !isCurrentSource() || requestRef.current.id !== id)
1001
- return;
1002
- void cause;
1003
- }
1004
- } catch (cause) {
1005
- if (controller.signal.aborted || !isCurrentSource() || requestRef.current.id !== id) return;
1006
- setState(
1007
- (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? {
1008
- ...current,
1009
- loading: false,
1010
- error: cause instanceof Error ? cause : new Error(String(cause))
1011
- } : current
1012
- );
1013
- } finally {
1014
- if (requestRef.current.id === id) {
1015
- requestRef.current = { id, controller: null };
1016
- }
1017
- }
1018
- },
1019
- [browserSessionId, client, enabled, isCurrentSource, source, workspaceId]
1020
- );
1021
- const refresh = useCallback5(async () => await refreshInventory(true), [refreshInventory]);
1022
- const refreshSemantic = useCallback5(async () => {
1023
- if (!enabled || !browserSessionId || !isCurrentSource()) return;
1024
- const target = selectedTargetRef.current;
1025
- if (!target) return;
1026
- const id = requestRef.current.id + 1;
1027
- requestRef.current.controller?.abort();
1028
- const controller = new AbortController();
1029
- requestRef.current = { id, controller };
1030
- try {
1031
- const observation = await client.observeBrowserTarget(
1032
- workspaceId,
1033
- browserSessionId,
1034
- target.id,
1035
- { signal: controller.signal }
1036
- );
1037
- if (!isCurrentSource() || requestRef.current.id !== id) return;
1038
- if (!sameObservationTarget(observation, selectedTargetRef.current)) return;
1039
- observationRef.current = { browserSessionId, observation };
1040
- lastObservationAtRef.current = Date.now();
1041
- setState(
1042
- (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId && current.selectedTargetId === target.id ? { ...current, observation } : current
1043
- );
1044
- } catch {
1045
- } finally {
1046
- if (requestRef.current.id === id) requestRef.current = { id, controller: null };
1047
- }
1048
- }, [browserSessionId, client, enabled, isCurrentSource, source, workspaceId]);
1049
- useEffect5(() => {
1050
- mountedRef.current = true;
1051
- if (!enabled) {
1052
- invalidateRefresh();
1053
- setState({ ...emptyState4(browserSessionId, false), source });
1054
- return;
1055
- }
1056
- setState(
1057
- (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? { ...current, loading: true } : { ...emptyState4(browserSessionId, true), source }
1058
- );
1059
- void refreshInventory();
1060
- return () => {
1061
- mountedRef.current = false;
1062
- invalidateRefresh();
1063
- };
1064
- }, [browserSessionId, enabled, invalidateRefresh, isCurrentSource, refreshInventory, source]);
1065
- useEffect5(() => {
1066
- const previous = previousSemanticObservationRef.current;
1067
- previousSemanticObservationRef.current = {
1068
- browserSessionId,
1069
- enabled: semanticObservationEnabled
1070
- };
1071
- if (enabled && semanticObservationEnabled && previous.browserSessionId === browserSessionId && !previous.enabled) {
1072
- const currentObservation = observationRef.current.observation;
1073
- if (currentObservation && sameObservationTarget(currentObservation, selectedTargetRef.current) && Date.now() - lastObservationAtRef.current < pollIntervalMs) {
1074
- return;
1075
- }
1076
- observationRef.current = { browserSessionId, observation: null };
1077
- setState(
1078
- (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? { ...current, observation: null } : current
1079
- );
1080
- void refreshSemantic();
1081
- }
1082
- }, [
1083
- browserSessionId,
1084
- enabled,
1085
- isCurrentSource,
1086
- pollIntervalMs,
1087
- refreshSemantic,
1088
- semanticObservationEnabled,
1089
- source
1090
- ]);
1091
- useEffect5(() => {
1092
- if (!enabled || !pageLive || visible.mutating || refreshBlocked) return;
1093
- const timer = setInterval(() => {
1094
- if (!requestRef.current.controller) void refreshInventory();
1095
- }, pollIntervalMs);
1096
- return () => clearInterval(timer);
1097
- }, [enabled, pageLive, pollIntervalMs, refreshInventory, refreshBlocked, visible.mutating]);
1098
- useEffect5(() => {
1099
- if (!enabled || !browserSessionId || !pageLive || refreshBlocked) return;
1100
- let disposed = false;
1101
- const heartbeat = () => {
1102
- if (!isCurrentSource()) return;
1103
- void client.heartbeatBrowserSession(workspaceId, browserSessionId).catch(() => {
1104
- if (!disposed) void refreshInventory();
1105
- });
1106
- };
1107
- const timer = setInterval(heartbeat, 3e4);
1108
- return () => {
1109
- disposed = true;
1110
- clearInterval(timer);
1111
- };
1112
- }, [
1113
- browserSessionId,
1114
- client,
1115
- enabled,
1116
- isCurrentSource,
1117
- pageLive,
1118
- refreshInventory,
1119
- refreshBlocked,
1120
- workspaceId
1121
- ]);
1122
- const runMutation = useCallback5(
1123
- async (scopeBrowserSessionId, operation, selectionRead = false, changesInventory = false) => {
1124
- if (!enabled || !isCurrentSource()) {
1125
- throw new Error(t("hooks.useBrowserSession.theBrowserSourceIsNo"));
1126
- }
1127
- const selectionRevision = ++selectionRevisionRef.current;
1128
- invalidateRefresh();
1129
- const readRequestId = requestRef.current.id;
1130
- if (mutationRef.current.browserSessionId !== scopeBrowserSessionId || mutationRef.current.source !== source) {
1131
- mutationRef.current = {
1132
- browserSessionId: scopeBrowserSessionId,
1133
- source,
1134
- count: 0,
1135
- needsInventoryReconcile: false
1136
- };
1137
- }
1138
- mutationRef.current.count += 1;
1139
- setState(
1140
- (current) => isCurrentSource() && current.source === source && current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating: true, error: null } : current
1141
- );
1142
- try {
1143
- return await operation(selectionRevision, readRequestId);
1144
- } catch (cause) {
1145
- const error = cause instanceof Error ? cause : new Error(String(cause));
1146
- if (!selectionRead || requestRef.current.id === readRequestId) {
1147
- setState(
1148
- (current) => isCurrentSource() && selectionRevisionRef.current === selectionRevision && current.source === source && current.browserSessionId === scopeBrowserSessionId ? { ...current, error } : current
1149
- );
1150
- }
1151
- throw error;
1152
- } finally {
1153
- if (mutationRef.current.browserSessionId === scopeBrowserSessionId && mutationRef.current.source === source) {
1154
- if (changesInventory && (selectionRevisionRef.current !== selectionRevision || requestRef.current.id !== readRequestId)) {
1155
- mutationRef.current.needsInventoryReconcile = true;
1156
- }
1157
- mutationRef.current.count = Math.max(0, mutationRef.current.count - 1);
1158
- const mutating = mutationRef.current.count > 0;
1159
- setState(
1160
- (current) => isCurrentSource() && current.source === source && current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating } : current
1161
- );
1162
- if (!mutating && mutationRef.current.needsInventoryReconcile && isCurrentSource()) {
1163
- mutationRef.current.needsInventoryReconcile = false;
1164
- void refreshInventory(false, false);
1165
- }
1166
- }
1167
- }
1168
- },
1169
- [enabled, invalidateRefresh, isCurrentSource, refreshInventory, source, t]
1170
- );
1171
- const selectTarget = useCallback5(
1172
- async (targetId) => {
1173
- if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1174
- return await runMutation(
1175
- browserSessionId,
1176
- async (selectionRevision, readRequestId) => {
1177
- const isCurrentSelection = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision;
1178
- const isCurrentSelectionRead = () => isCurrentSelection() && requestRef.current.id === readRequestId;
1179
- let targets = visible.targets;
1180
- let target;
1181
- let observation;
1182
- try {
1183
- observation = await client.selectBrowserTarget(workspaceId, browserSessionId, targetId);
1184
- target = observation.target;
1185
- } catch (cause) {
1186
- if (!isCurrentSelectionRead() || !isMissingBrowserTarget(cause)) throw cause;
1187
- const response = await client.listBrowserTargets(workspaceId, browserSessionId);
1188
- if (!isCurrentSelectionRead()) throw cause;
1189
- targets = response.targets;
1190
- const fallback = chooseTarget(targets, null);
1191
- if (!fallback) {
1192
- selectedTargetIdRef.current = null;
1193
- selectedTargetRef.current = null;
1194
- observationRef.current = { browserSessionId, observation: null };
1195
- setState(
1196
- (current) => isCurrentSelection() && current.source === source && current.browserSessionId === browserSessionId ? {
1197
- ...current,
1198
- targets,
1199
- selectedTargetId: null,
1200
- observation: null
1201
- } : current
1202
- );
1203
- throw cause;
1204
- }
1205
- observation = await client.selectBrowserTarget(
1206
- workspaceId,
1207
- browserSessionId,
1208
- fallback.id
1209
- );
1210
- target = observation.target;
1211
- }
1212
- if (!isCurrentSelectionRead()) return target;
1213
- selectedTargetIdRef.current = target.id;
1214
- selectedTargetRef.current = target;
1215
- observationRef.current = { browserSessionId, observation };
1216
- lastObservationAtRef.current = Date.now();
1217
- setState(
1218
- (current) => isCurrentSelection() && current.source === source && current.browserSessionId === browserSessionId ? {
1219
- ...current,
1220
- targets: replaceTarget(targets, target).map((candidate) => ({
1221
- ...candidate,
1222
- selected: candidate.id === target.id
1223
- })),
1224
- selectedTargetId: target.id,
1225
- observation
1226
- } : current
1227
- );
1228
- return target;
1229
- },
1230
- true
1231
- );
1232
- },
1233
- [browserSessionId, client, isCurrentSource, runMutation, source, visible.targets, workspaceId]
1234
- );
1235
- const openTarget = useCallback5(
1236
- async (url) => {
1237
- if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1238
- return await runMutation(
1239
- browserSessionId,
1240
- async (selectionRevision, readRequestId) => {
1241
- const isCurrentSelection = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision;
1242
- const isCurrentSelectionRead = () => isCurrentSelection() && requestRef.current.id === readRequestId;
1243
- const observation = await client.openBrowserTarget(
1244
- workspaceId,
1245
- browserSessionId,
1246
- url === void 0 ? {} : { url }
1247
- );
1248
- const target = observation.target;
1249
- if (!isCurrentSource()) return target;
1250
- if (!isCurrentSelectionRead()) return target;
1251
- selectedTargetIdRef.current = target.id;
1252
- selectedTargetRef.current = target;
1253
- observationRef.current = { browserSessionId, observation };
1254
- lastObservationAtRef.current = Date.now();
1255
- setState(
1256
- (current) => isCurrentSelection() && current.source === source && current.browserSessionId === browserSessionId ? {
1257
- ...current,
1258
- targets: [
1259
- ...current.targets.map((candidate) => ({
1260
- ...candidate,
1261
- selected: false
1262
- })),
1263
- target
1264
- ],
1265
- selectedTargetId: target.id,
1266
- observation
1267
- } : current
1268
- );
1269
- return target;
1270
- },
1271
- false,
1272
- true
1273
- );
1274
- },
1275
- [browserSessionId, client, isCurrentSource, runMutation, source, workspaceId]
1276
- );
1277
- const closeTarget = useCallback5(
1278
- async (targetId) => {
1279
- if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1280
- await runMutation(
1281
- browserSessionId,
1282
- async (selectionRevision, readRequestId) => {
1283
- const isCurrentSelection = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision;
1284
- const isCurrentSelectionRead = () => isCurrentSelection() && requestRef.current.id === readRequestId;
1285
- const response = await client.closeBrowserTarget(workspaceId, browserSessionId, targetId);
1286
- if (!isCurrentSource()) return;
1287
- if (!isCurrentSelectionRead()) return;
1288
- const selected = chooseTarget(response.targets, selectedTargetIdRef.current);
1289
- const observed = selected ? await client.observeBrowserTarget(workspaceId, browserSessionId, selected.id) : null;
1290
- if (!isCurrentSource()) return;
1291
- if (!isCurrentSelectionRead()) return;
1292
- const observation = observed && selected && observed.browserSessionId === browserSessionId && observed.target.id === selected.id && observed.target.controllerGeneration === selected.controllerGeneration && observed.target.targetGeneration === selected.targetGeneration ? observed : null;
1293
- const currentTarget = observation?.target ?? selected;
1294
- selectedTargetIdRef.current = currentTarget?.id ?? null;
1295
- selectedTargetRef.current = currentTarget;
1296
- observationRef.current = { browserSessionId, observation };
1297
- if (observation) lastObservationAtRef.current = Date.now();
1298
- setState(
1299
- (current) => isCurrentSelection() && current.source === source && current.browserSessionId === browserSessionId ? {
1300
- ...current,
1301
- targets: currentTarget ? replaceTarget(response.targets, currentTarget) : response.targets,
1302
- selectedTargetId: currentTarget?.id ?? null,
1303
- observation
1304
- } : current
1305
- );
1306
- },
1307
- false,
1308
- true
1309
- );
1310
- },
1311
- [browserSessionId, client, isCurrentSource, runMutation, source, workspaceId]
1312
- );
1313
- const dispatchAction = useCallback5(
1314
- async (action, operationId, frame, observationMode = "none") => {
1315
- if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1316
- if (!enabled || !isCurrentSource()) {
1317
- throw new Error(t("hooks.useBrowserSession.theBrowserSourceIsNo"));
1318
- }
1319
- if (!selectedTargetRef.current) {
1320
- throw new Error("The browser page is not ready for input.");
1321
- }
1322
- if (frame && frame.browserSessionId !== browserSessionId) {
1323
- throw new Error("The displayed browser frame belongs to another BrowserSession.");
1324
- }
1325
- const currentObservation = observationRef.current.browserSessionId === browserSessionId ? observationRef.current.observation : null;
1326
- const fence = frame ? {
1327
- targetId: frame.targetId,
1328
- expectedTargetGeneration: frame.targetGeneration,
1329
- expectedDocumentGeneration: frame.documentGeneration,
1330
- expectedFrameId: frame.frameId
1331
- } : currentObservation && sameObservationTarget(currentObservation, selectedTargetRef.current) ? {
1332
- targetId: currentObservation.target.id,
1333
- expectedTargetGeneration: currentObservation.target.targetGeneration,
1334
- expectedDocumentGeneration: currentObservation.target.documentGeneration,
1335
- expectedFrameId: currentObservation.frameId
1336
- } : null;
1337
- if (!fence) {
1338
- throw new Error("The browser page is not ready for input.");
1339
- }
1340
- const expectedControllerGeneration = frame?.controllerGeneration ?? currentObservation.target.controllerGeneration;
1341
- const selectionRevision = selectionRevisionRef.current;
1342
- const resultOrder = ++actionResultOrderRef.current.next;
1343
- let resultDocumentGeneration = fence.expectedDocumentGeneration;
1344
- const isCurrentResultView = () => {
1345
- const selected = selectedTargetRef.current;
1346
- return isCurrentSource() && selectionRevisionRef.current === selectionRevision && selected?.browserSessionId === browserSessionId && selected.id === fence.targetId && selected.controllerGeneration === expectedControllerGeneration && selected.targetGeneration === fence.expectedTargetGeneration && (selected.documentGeneration === fence.expectedDocumentGeneration || selected.documentGeneration === resultDocumentGeneration);
1347
- };
1348
- const canProjectResult = () => isCurrentResultView() && resultOrder >= actionResultOrderRef.current.settled;
1349
- const settleResult = () => {
1350
- if (!canProjectResult()) return false;
1351
- actionResultOrderRef.current.settled = resultOrder;
1352
- invalidateRefresh();
1353
- return true;
1354
- };
1355
- try {
1356
- const receipt = await client.actInBrowser(workspaceId, browserSessionId, {
1357
- operationId,
1358
- ...fence,
1359
- observationMode,
1360
- action
1361
- });
1362
- resultDocumentGeneration = receipt.observation?.target.documentGeneration ?? fence.expectedDocumentGeneration;
1363
- const settled = receipt.state === "completed" || receipt.state === "failed" || receipt.state === "outcome_unknown";
1364
- if (!canProjectResult() || settled && !settleResult()) return receipt;
1365
- const receivedObservation = receipt.observation;
1366
- const selectedTarget = selectedTargetRef.current;
1367
- let projectedObservation = null;
1368
- if (settled && receivedObservation && canProjectResult() && receipt.browserSessionId === browserSessionId && receipt.controllerGeneration === expectedControllerGeneration && receipt.targetId === fence.targetId && observationRef.current.browserSessionId === browserSessionId && receivedObservation.browserSessionId === browserSessionId && selectedTarget?.browserSessionId === browserSessionId && selectedTarget.id === fence.targetId && receivedObservation.target.id === fence.targetId && selectedTarget.controllerGeneration === expectedControllerGeneration && receivedObservation.target.controllerGeneration === expectedControllerGeneration && selectedTarget.targetGeneration === fence.expectedTargetGeneration && receivedObservation.target.targetGeneration === fence.expectedTargetGeneration && (selectedTarget.documentGeneration === fence.expectedDocumentGeneration || selectedTarget.documentGeneration === receivedObservation.target.documentGeneration)) {
1369
- projectedObservation = receivedObservation;
1370
- observationRef.current = {
1371
- browserSessionId,
1372
- observation: receivedObservation
1373
- };
1374
- selectedTargetRef.current = receivedObservation.target;
1375
- lastObservationAtRef.current = Date.now();
1376
- }
1377
- setState(
1378
- (current) => isCurrentResultView() && current.source === source && current.browserSessionId === browserSessionId && current.actionResultOrder <= resultOrder ? {
1379
- ...current,
1380
- actionResultOrder: settled ? resultOrder : current.actionResultOrder,
1381
- error: receipt.state === "completed" ? null : current.error,
1382
- inputFailure: receipt.state === "completed" ? current.inputFailure : {
1383
- operationId: receipt.operationId,
1384
- state: receipt.state,
1385
- error: receipt.error
1386
- },
1387
- ...projectedObservation ? {
1388
- observation: projectedObservation,
1389
- targets: replaceTarget(current.targets, projectedObservation.target)
1390
- } : {}
1391
- } : current
1392
- );
1393
- return receipt;
1394
- } catch (cause) {
1395
- const error = cause instanceof Error ? cause : new Error(String(cause));
1396
- if (settleResult()) {
1397
- setState(
1398
- (current) => isCurrentResultView() && current.source === source && current.browserSessionId === browserSessionId && current.actionResultOrder <= resultOrder ? { ...current, error, actionResultOrder: resultOrder } : current
1399
- );
1400
- }
1401
- throw error;
1402
- }
1403
- },
1404
- [browserSessionId, client, enabled, invalidateRefresh, isCurrentSource, source, workspaceId, t]
1405
- );
1406
- const act = useCallback5(
1407
- async (action, operationId = crypto.randomUUID()) => await dispatchAction(action, operationId, null),
1408
- [dispatchAction]
1409
- );
1410
- const actFromFrame = useCallback5(
1411
- async (action, frame, operationId = crypto.randomUUID(), observationMode = "none") => await dispatchAction(action, operationId, frame, observationMode),
1412
- [dispatchAction]
1413
- );
1414
- const observeForInput = useCallback5(async () => {
1415
- const target = selectedTargetRef.current;
1416
- if (!enabled || !isCurrentSource()) {
1417
- throw new Error(t("hooks.useBrowserSession.theBrowserSourceIsNo"));
1418
- }
1419
- if (!browserSessionId || !target)
1420
- throw new Error(t("hooks.useBrowserSession.noBrowserTabIsSelected"));
1421
- const observation = await client.observeBrowserTarget(workspaceId, browserSessionId, target.id);
1422
- if (!isCurrentSource() || observation.browserSessionId !== browserSessionId || !sameObservationTarget(observation, selectedTargetRef.current)) {
1423
- throw new Error(t("hooks.useBrowserSession.theBrowserPageChangedOpen"));
1424
- }
1425
- return observation;
1426
- }, [browserSessionId, client, enabled, isCurrentSource, workspaceId, t]);
1427
- const actFromObservation = useCallback5(
1428
- async (action, observation) => {
1429
- if (!sameObservationTarget(observation, selectedTargetRef.current)) {
1430
- throw new Error(t("hooks.useBrowserSession.theBrowserPageChangedOpen"));
1431
- }
1432
- return await dispatchAction(action, crypto.randomUUID(), {
1433
- browserSessionId: observation.browserSessionId,
1434
- controllerGeneration: observation.target.controllerGeneration,
1435
- targetId: observation.target.id,
1436
- targetGeneration: observation.target.targetGeneration,
1437
- documentGeneration: observation.target.documentGeneration,
1438
- frameId: observation.frameId
1439
- });
1440
- },
1441
- [dispatchAction, t]
1442
- );
1443
- const diagnostics = useCallback5(
1444
- async (diagnosticOptions = {}) => {
1445
- if (!enabled || !isCurrentSource()) {
1446
- throw new Error(t("hooks.useBrowserSession.theBrowserSourceIsNo"));
1447
- }
1448
- const targetId = selectedTargetIdRef.current;
1449
- if (!browserSessionId || !targetId) {
1450
- throw new Error(t("hooks.useBrowserSession.noBrowserTabIsSelected"));
1451
- }
1452
- return await client.listBrowserDiagnostics(
1453
- workspaceId,
1454
- browserSessionId,
1455
- targetId,
1456
- diagnosticOptions
1457
- );
1458
- },
1459
- [browserSessionId, client, enabled, isCurrentSource, workspaceId, t]
1460
- );
1461
- const readClipboard = useCallback5(async () => {
1462
- if (!enabled || !isCurrentSource()) {
1463
- throw new Error(t("hooks.useBrowserSession.theBrowserSourceIsNo"));
1464
- }
1465
- if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1466
- const clipboard = await client.readBrowserClipboard(workspaceId, browserSessionId);
1467
- if (!isCurrentSource() || clipboard.browserSessionId !== browserSessionId) {
1468
- throw new Error("Browser clipboard belongs to another BrowserSession.");
1469
- }
1470
- return clipboard;
1471
- }, [browserSessionId, client, enabled, isCurrentSource, workspaceId, t]);
1472
- return {
1473
- session: visible.session,
1474
- targets: visible.targets,
1475
- selectedTarget: visible.targets.find((target) => target.id === visible.selectedTargetId) ?? null,
1476
- observation: visible.observation,
1477
- loading: visible.loading,
1478
- mutating: visible.mutating,
1479
- error: visible.error,
1480
- inputFailure: visible.inputFailure,
1481
- refresh,
1482
- observeForInput,
1483
- actFromObservation,
1484
- selectTarget,
1485
- openTarget,
1486
- closeTarget,
1487
- act,
1488
- actFromFrame,
1489
- readClipboard,
1490
- diagnostics
1491
- };
1492
- }
1493
- function emptyState4(browserSessionId, loading) {
1494
- return {
1495
- browserSessionId,
1496
- session: null,
1497
- targets: [],
1498
- selectedTargetId: null,
1499
- observation: null,
1500
- loading,
1501
- mutating: false,
1502
- error: null,
1503
- inputFailure: null,
1504
- actionResultOrder: 0
1505
- };
1506
- }
1507
- function chooseTarget(targets, preferredId) {
1508
- return targets.find((target) => target.id === preferredId) ?? targets.find((target) => target.selected && target.kind === "page") ?? targets.find((target) => target.kind === "page") ?? targets[0] ?? null;
1509
- }
1510
- function replaceTarget(targets, target) {
1511
- const next = targets.filter((candidate) => candidate.id !== target.id);
1512
- next.push(target);
1513
- return next.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
1514
- }
1515
- function sameObservationTarget(observation, target) {
1516
- return target !== null && observation.browserSessionId === target.browserSessionId && observation.target.id === target.id && observation.target.controllerGeneration === target.controllerGeneration && observation.target.targetGeneration === target.targetGeneration && observation.target.documentGeneration === target.documentGeneration;
1517
- }
1518
- function isMissingBrowserTarget(error) {
1519
- return error instanceof OpenGeniApiError && error.code === "target_not_found";
1520
- }
1521
-
1522
- // src/hooks/use-browser-sessions.ts
1523
- import { useCallback as useCallback6, useEffect as useEffect6, useMemo, useRef as useRef6, useState as useState6 } from "react";
1524
- function useBrowserSessions(options = {}) {
1525
- const {
1526
- client,
1527
- workspaceId,
1528
- registerSessionReconciler,
1529
- workspaceInteractionEvent,
1530
- workspaceInteractionConnectionState
1531
- } = useEmbeddedBrowserInteraction(options);
1532
- const enabled = options.enabled ?? true;
1533
- const [state, setState] = useState6(() => emptyState5(workspaceId, enabled));
1534
- const requestRef = useRef6({
1535
- id: 0,
1536
- controller: null
1537
- });
1538
- const loadQueueRef = useRef6(null);
1539
- const mountedRef = useRef6(true);
1540
- const visibleState = state.workspaceId === workspaceId ? state : emptyState5(workspaceId, enabled);
1541
- const cancelLoad = useCallback6(() => {
1542
- loadQueueRef.current = null;
1543
- const id = requestRef.current.id + 1;
1544
- requestRef.current.controller?.abort();
1545
- requestRef.current = { id, controller: null };
1546
- }, []);
1547
- const loadOnce = useCallback6(
1548
- async (foreground) => {
1549
- if (!enabled) return;
1550
- const id = requestRef.current.id + 1;
1551
- requestRef.current.controller?.abort();
1552
- const controller = new AbortController();
1553
- requestRef.current = { id, controller };
1554
- setState((current) => ({
1555
- ...current.workspaceId === workspaceId ? current : emptyState5(workspaceId, true),
1556
- active: true,
1557
- loading: foreground,
1558
- refreshing: !foreground,
1559
- error: foreground || current.workspaceId !== workspaceId ? null : current.error
1560
- }));
1561
- try {
1562
- const response = await client.listBrowserSessions(workspaceId, {
1563
- signal: controller.signal
1564
- });
1565
- if (!mountedRef.current || requestRef.current.id !== id) return;
1566
- setState({
1567
- workspaceId,
1568
- active: true,
1569
- revision: response.revision,
1570
- sessions: sortBrowserSessions(response.sessions),
1571
- loading: false,
1572
- refreshing: false,
1573
- error: null
1574
- });
1575
- } catch (cause) {
1576
- if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
1577
- return;
1578
- const error = cause instanceof Error ? cause : new Error(String(cause));
1579
- setState((current) => ({
1580
- ...current.workspaceId === workspaceId ? current : emptyState5(workspaceId, true),
1581
- loading: false,
1582
- refreshing: false,
1583
- error
1584
- }));
1585
- } finally {
1586
- if (requestRef.current.id === id) {
1587
- requestRef.current = { id, controller: null };
1588
- }
1589
- }
1590
- },
1591
- [client, enabled, workspaceId]
1592
- );
1593
- const load = useCallback6(
1594
- (foreground) => {
1595
- const current = loadQueueRef.current;
1596
- if (current) {
1597
- current.pending = true;
1598
- return current.promise;
1599
- }
1600
- const queue = { pending: false, promise: Promise.resolve() };
1601
- loadQueueRef.current = queue;
1602
- queue.promise = Promise.resolve().then(async () => {
1603
- while (loadQueueRef.current === queue) {
1604
- queue.pending = false;
1605
- await loadOnce(foreground);
1606
- foreground = false;
1607
- if (!queue.pending) break;
1608
- }
1609
- if (loadQueueRef.current === queue) loadQueueRef.current = null;
1610
- }).finally(() => {
1611
- if (loadQueueRef.current === queue) loadQueueRef.current = null;
1612
- });
1613
- return queue.promise;
1614
- },
1615
- [loadOnce]
1616
- );
1617
- const refresh = useCallback6(async () => await load(false), [load]);
1618
- useEffect6(() => {
1619
- mountedRef.current = true;
1620
- if (!enabled) {
1621
- cancelLoad();
1622
- setState(emptyState5(workspaceId, false));
1623
- return;
1624
- }
1625
- void load(true);
1626
- return () => {
1627
- mountedRef.current = false;
1628
- cancelLoad();
1629
- };
1630
- }, [cancelLoad, enabled, load, workspaceId]);
1631
- useInteractionInvalidation({
1632
- workspaceId,
1633
- key: workspaceId,
1634
- enabled,
1635
- event: workspaceInteractionEvent,
1636
- connectionState: workspaceInteractionConnectionState,
1637
- knownRevision: visibleState.revision,
1638
- refresh,
1639
- fallbackPollIntervalMs: options.pollIntervalMs
1640
- });
1641
- useEffect6(() => {
1642
- if (!enabled || !options.sessionId) return;
1643
- return registerSessionReconciler(options.sessionId, `browser-sessions:${workspaceId}`, refresh);
1644
- }, [enabled, options.sessionId, refresh, registerSessionReconciler, workspaceId]);
1645
- const create = useCallback6(
1646
- async (request) => {
1647
- const response = await client.createBrowserSession(workspaceId, {
1648
- ...request,
1649
- operationId: request.operationId ?? crypto.randomUUID()
1650
- });
1651
- setState((current) => mergeSession(current, workspaceId, response.session));
1652
- return response;
1653
- },
1654
- [client, workspaceId]
1655
- );
1656
- const end = useCallback6(
1657
- async (browserSessionId, operationId = crypto.randomUUID()) => {
1658
- const response = await client.endBrowserSession(workspaceId, browserSessionId, {
1659
- operationId
1660
- });
1661
- setState((current) => mergeSession(current, workspaceId, response.session));
1662
- return response;
1663
- },
1664
- [client, workspaceId]
1665
- );
1666
- const suspend = useCallback6(
1667
- async (browserSessionId, operationId = crypto.randomUUID()) => {
1668
- const response = await client.suspendBrowserSession(workspaceId, browserSessionId, {
1669
- operationId
1670
- });
1671
- setState((current) => mergeSession(current, workspaceId, response.session));
1672
- return response;
1673
- },
1674
- [client, workspaceId]
1675
- );
1676
- const resume = useCallback6(
1677
- async (browserSessionId, operationId = crypto.randomUUID()) => {
1678
- const response = await client.resumeBrowserSession(workspaceId, browserSessionId, {
1679
- operationId
1680
- });
1681
- setState((current) => mergeSession(current, workspaceId, response.session));
1682
- return response;
1683
- },
1684
- [client, workspaceId]
1685
- );
1686
- const relevantSessions = useMemo(
1687
- () => options.sessionId ? visibleState.sessions.filter(
1688
- (session) => session.associations.some((association) => association.sessionId === options.sessionId)
1689
- ) : [],
1690
- [options.sessionId, visibleState.sessions]
1691
- );
1692
- return {
1693
- revision: visibleState.revision,
1694
- sessions: visibleState.sessions,
1695
- relevantSessions,
1696
- // A disabled registry is deliberately empty. Expose the first re-enabled
1697
- // render as loading so consumers do not reconcile against that empty gap.
1698
- loading: enabled ? !visibleState.active || visibleState.loading : false,
1699
- refreshing: visibleState.refreshing,
1700
- error: visibleState.error,
1701
- refresh,
1702
- create,
1703
- suspend,
1704
- resume,
1705
- end
1706
- };
1707
- }
1708
- function emptyState5(workspaceId, loading) {
1709
- return {
1710
- workspaceId,
1711
- active: false,
1712
- revision: 0,
1713
- sessions: [],
1714
- loading,
1715
- refreshing: false,
1716
- error: null
1717
- };
1718
- }
1719
- function sortBrowserSessions(sessions) {
1720
- return [...sessions].sort((left, right) => {
1721
- const live = Number(isLiveBrowser(right)) - Number(isLiveBrowser(left));
1722
- return live || timestamp3(right.lastUsedAt) - timestamp3(left.lastUsedAt) || left.id.localeCompare(right.id);
1723
- });
1724
- }
1725
- function isLiveBrowser(session) {
1726
- return !["ending", "ended", "failed", "lost"].includes(session.lifecycle);
1727
- }
1728
- function timestamp3(value) {
1729
- const parsed = Date.parse(value);
1730
- return Number.isFinite(parsed) ? parsed : 0;
1731
- }
1732
- function mergeSession(current, workspaceId, session) {
1733
- if (current.workspaceId !== workspaceId) return current;
1734
- const sessions = current.sessions.filter((candidate) => candidate.id !== session.id);
1735
- sessions.push(session);
1736
- return { ...current, sessions: sortBrowserSessions(sessions), error: null };
1737
- }
1738
-
1739
- // src/hooks/use-site-auth-connections.ts
1740
- import { useCallback as useCallback7, useEffect as useEffect7, useRef as useRef7, useState as useState7 } from "react";
1741
- function useSiteAuthConnections(options = {}) {
1742
- const { client, workspaceId, workspaceInteractionEvent, workspaceInteractionConnectionState } = useEmbeddedBrowserInteraction(options);
1743
- const enabled = options.enabled ?? true;
1744
- const includeArchived = options.includeArchived ?? false;
1745
- const key = `${workspaceId}:${includeArchived ? "all" : "active"}`;
1746
- const [state, setState] = useState7(() => emptyState6(key, enabled));
1747
- const [mutationCount, setMutationCount] = useState7(0);
1748
- const requestRef = useRef7({
1749
- id: 0,
1750
- controller: null
1751
- });
1752
- const mountedRef = useRef7(true);
1753
- const visible = state.key === key ? state : emptyState6(key, enabled);
1754
- const cancelLoad = useCallback7(() => {
1755
- requestRef.current.controller?.abort();
1756
- requestRef.current = { id: requestRef.current.id + 1, controller: null };
1757
- }, []);
1758
- const load = useCallback7(
1759
- async (foreground) => {
1760
- if (!enabled) return;
1761
- const id = requestRef.current.id + 1;
1762
- requestRef.current.controller?.abort();
1763
- const controller = new AbortController();
1764
- requestRef.current = { id, controller };
1765
- setState((current) => ({
1766
- ...current.key === key ? current : emptyState6(key, true),
1767
- loading: foreground,
1768
- refreshing: !foreground,
1769
- error: foreground || current.key !== key ? null : current.error
1770
- }));
1771
- try {
1772
- const response = await client.listSiteAuthConnections(workspaceId, {
1773
- includeArchived,
1774
- signal: controller.signal
1775
- });
1776
- if (!mountedRef.current || requestRef.current.id !== id) return;
1777
- setState({
1778
- key,
1779
- revision: response.revision,
1780
- connections: sortConnections(response.connections),
1781
- loading: false,
1782
- refreshing: false,
1783
- error: null
1784
- });
1785
- } catch (cause) {
1786
- if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
1787
- return;
1788
- setState((current) => ({
1789
- ...current.key === key ? current : emptyState6(key, true),
1790
- loading: false,
1791
- refreshing: false,
1792
- error: asError2(cause)
1793
- }));
1794
- } finally {
1795
- if (requestRef.current.id === id) requestRef.current = { id, controller: null };
1796
- }
1797
- },
1798
- [client, enabled, includeArchived, key, workspaceId]
1799
- );
1800
- const refresh = useCallback7(async () => await load(false), [load]);
1801
- useEffect7(() => {
1802
- mountedRef.current = true;
1803
- if (!enabled) {
1804
- cancelLoad();
1805
- setState(emptyState6(key, false));
1806
- return;
1807
- }
1808
- void load(true);
1809
- return () => {
1810
- mountedRef.current = false;
1811
- cancelLoad();
1812
- };
1813
- }, [cancelLoad, enabled, key, load]);
1814
- useInteractionInvalidation({
1815
- workspaceId,
1816
- key,
1817
- enabled,
1818
- event: workspaceInteractionEvent,
1819
- connectionState: workspaceInteractionConnectionState,
1820
- knownRevision: visible.revision,
1821
- refresh
1822
- });
1823
- const mutate = useCallback7(async (operation) => {
1824
- setMutationCount((count) => count + 1);
1825
- try {
1826
- return await operation();
1827
- } finally {
1828
- if (mountedRef.current) setMutationCount((count) => Math.max(0, count - 1));
1829
- }
1830
- }, []);
1831
- const merge = useCallback7(
1832
- (connection) => {
1833
- setState((current) => {
1834
- if (current.key !== key) return current;
1835
- const connections = current.connections.filter(
1836
- (candidate) => candidate.id !== connection.id
1837
- );
1838
- if (includeArchived || connection.status !== "archived") connections.push(connection);
1839
- return { ...current, connections: sortConnections(connections), error: null };
1840
- });
1841
- },
1842
- [includeArchived, key]
1843
- );
1844
- const create = useCallback7(
1845
- async (request) => await mutate(async () => {
1846
- const response = await client.createSiteAuthConnection(workspaceId, {
1847
- ...request,
1848
- operationId: request.operationId ?? crypto.randomUUID()
1849
- });
1850
- merge(response.connection);
1851
- return response;
1852
- }),
1853
- [client, merge, mutate, workspaceId]
1854
- );
1855
- const update = useCallback7(
1856
- async (connectionId, request) => await mutate(async () => {
1857
- const response = await client.updateSiteAuthConnection(workspaceId, connectionId, {
1858
- ...request,
1859
- operationId: request.operationId ?? crypto.randomUUID()
1860
- });
1861
- merge(response.connection);
1862
- return response;
1863
- }),
1864
- [client, merge, mutate, workspaceId]
1865
- );
1866
- return {
1867
- revision: visible.revision,
1868
- connections: visible.connections,
1869
- loading: visible.loading,
1870
- refreshing: visible.refreshing,
1871
- mutating: mutationCount > 0,
1872
- error: visible.error,
1873
- refresh,
1874
- create,
1875
- update
1876
- };
1877
- }
1878
- function emptyState6(key, loading) {
1879
- return {
1880
- key,
1881
- revision: 0,
1882
- connections: [],
1883
- loading,
1884
- refreshing: false,
1885
- error: null
1886
- };
1887
- }
1888
- function sortConnections(connections) {
1889
- return [...connections].sort(
1890
- (left, right) => Number(left.status === "archived") - Number(right.status === "archived") || left.name.localeCompare(right.name) || left.accountLabel.localeCompare(right.accountLabel)
1891
- );
1892
- }
1893
- function asError2(cause) {
1894
- return cause instanceof Error ? cause : new Error(String(cause));
1895
- }
1896
-
1897
- // src/components/browser-input.ts
1898
- var HUMAN_BROWSER_HOME_URL = "https://www.google.com/";
1899
- var HUMAN_BROWSER_SEARCH_URL = "https://www.google.com/search?q=";
1900
- function normalizeBrowserAddress(value) {
1901
- const trimmed = value.trim();
1902
- if (!trimmed) return null;
1903
- if (/^(?:https?|about):/iu.test(trimmed)) {
1904
- try {
1905
- const url = new URL(trimmed);
1906
- return ["http:", "https:", "about:"].includes(url.protocol) ? url.href : null;
1907
- } catch {
1908
- return `${HUMAN_BROWSER_SEARCH_URL}${encodeURIComponent(trimmed)}`;
1909
- }
1910
- }
1911
- const isLocal = /^(?:localhost|127(?:\.\d{1,3}){3}|\[::1\])(?::\d+)?(?:[/?#]|$)/iu.test(trimmed);
1912
- const looksLikeHost = !/\s/u.test(trimmed) && (isLocal || /^(?:[^./\s]+\.)+[^./\s]+(?::\d+)?(?:[/?#]|$)/u.test(trimmed));
1913
- if (!looksLikeHost) return `${HUMAN_BROWSER_SEARCH_URL}${encodeURIComponent(trimmed)}`;
1914
- try {
1915
- return new URL(`${isLocal ? "http" : "https"}://${trimmed}`).href;
1916
- } catch {
1917
- return `${HUMAN_BROWSER_SEARCH_URL}${encodeURIComponent(trimmed)}`;
1918
- }
1919
- }
1920
- function browserKey(event, hostPlatform = interactionHostPlatform()) {
1921
- if (["Alt", "AltGraph", "Control", "Meta", "Shift"].includes(event.key)) return null;
1922
- const special = /* @__PURE__ */ new Set([
1923
- "Enter",
1924
- "Tab",
1925
- "Escape",
1926
- "Backspace",
1927
- "Delete",
1928
- "ArrowUp",
1929
- "ArrowDown",
1930
- "ArrowLeft",
1931
- "ArrowRight"
1932
- ]);
1933
- const modified = event.altKey || event.ctrlKey || event.metaKey;
1934
- if (!modified && !special.has(event.key)) return null;
1935
- const parts = [];
1936
- if (event.ctrlKey) parts.push(hostPlatform === "mac" ? "Control" : "Mod");
1937
- if (event.altKey) parts.push("Alt");
1938
- if (event.metaKey) parts.push(hostPlatform === "mac" ? "Mod" : "Meta");
1939
- if (event.shiftKey) parts.push("Shift");
1940
- parts.push(event.key === " " ? "Space" : event.key);
1941
- return parts.join("+");
1942
- }
1943
-
1944
- // src/components/browser-select-control.tsx
1945
- import { useEffect as useEffect8, useLayoutEffect, useRef as useRef8, useState as useState8 } from "react";
1946
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1947
- function focusedSelect(observation) {
1948
- if (observation.semantic?.kind !== "snapshot") return null;
1949
- const pending = [...observation.semantic.roots];
1950
- while (pending.length) {
1951
- const node = pending.pop();
1952
- if (node.children) pending.push(...node.children);
1953
- if (node.ref !== observation.focusedRef || !node.actions.includes("select") || node.native?.platform !== "dom")
1954
- continue;
1955
- const data = node.native.data;
1956
- if (!data || data.kind !== "native-select" || typeof data.multiple !== "boolean" || typeof data.disabled !== "boolean" || !Array.isArray(data.options) || data.options.length > 200)
1957
- return null;
1958
- if (data.options.some(
1959
- (option) => !option || typeof option.value !== "string" || typeof option.label !== "string" || typeof option.selected !== "boolean" || typeof option.disabled !== "boolean"
1960
- ))
1961
- return null;
1962
- return { node, control: data };
1963
- }
1964
- return null;
1965
- }
1966
- function BrowserSelectControl(props) {
1967
- const t = useOpenGeniText();
1968
- const [snapshot, setSnapshot] = useState8(null);
1969
- const [open, setOpen] = useState8(false);
1970
- const [busy, setBusy] = useState8(false);
1971
- const [error, setError] = useState8(null);
1972
- const [values, setValues] = useState8([]);
1973
- const [anchor, setAnchor] = useState8(null);
1974
- const popupRef = useRef8(null);
1975
- const [position, setPosition] = useState8(null);
1976
- const panelRef = useRef8(null);
1977
- const request = useRef8(0);
1978
- const optionKeys = useRef8(/* @__PURE__ */ new WeakMap());
1979
- const nextOptionKey = useRef8(0);
1980
- const optionKey = (option) => {
1981
- let key = optionKeys.current.get(option);
1982
- if (key === void 0) {
1983
- key = ++nextOptionKey.current;
1984
- optionKeys.current.set(option, key);
1985
- }
1986
- return key;
1987
- };
1988
- useEffect8(
1989
- () => () => {
1990
- request.current += 1;
1991
- },
1992
- []
1993
- );
1994
- useEffect8(() => {
1995
- if (props.activation === void 0) return;
1996
- ++request.current;
1997
- const observation = props.activation?.observation;
1998
- const selected2 = observation ? focusedSelect(observation) : null;
1999
- setSnapshot(selected2 && observation ? observation : null);
2000
- setAnchor(selected2 ? props.activation.anchor : null);
2001
- setOpen(Boolean(selected2));
2002
- setBusy(false);
2003
- setError(null);
2004
- setValues(
2005
- selected2?.control.options.filter((option) => option.selected).map((option) => option.value) ?? []
2006
- );
2007
- }, [props.activation]);
2008
- useEffect8(() => {
2009
- if (open && !busy) panelRef.current?.focus({ preventScroll: true });
2010
- }, [open, busy]);
2011
- useLayoutEffect(() => {
2012
- if (!anchor || !open) {
2013
- setPosition(null);
2014
- return;
2015
- }
2016
- const place = () => {
2017
- const popup = popupRef.current;
2018
- const parent = popup?.parentElement;
2019
- if (!popup || !parent) return;
2020
- setPosition({
2021
- x: Math.max(12, Math.min(anchor.x, parent.clientWidth - popup.offsetWidth - 12)),
2022
- y: Math.max(12, Math.min(anchor.y, parent.clientHeight - popup.offsetHeight - 12)),
2023
- height: Math.max(48, parent.clientHeight - 60)
2024
- });
2025
- };
2026
- place();
2027
- const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(place);
2028
- if (popupRef.current?.parentElement) observer?.observe(popupRef.current.parentElement);
2029
- if (popupRef.current) observer?.observe(popupRef.current);
2030
- return () => observer?.disconnect();
2031
- }, [anchor, open, busy, snapshot, error]);
2032
- const selected = snapshot ? focusedSelect(snapshot) : null;
2033
- const ambiguousValue = (value) => (selected?.control.options.filter((option) => option.value === value || option.label === value).length ?? 0) !== 1;
2034
- const load = async () => {
2035
- const current = ++request.current;
2036
- setOpen(true);
2037
- setBusy(true);
2038
- setSnapshot(null);
2039
- setError(null);
2040
- setAnchor(null);
2041
- try {
2042
- const observation = await props.observe();
2043
- if (current !== request.current) return;
2044
- setSnapshot(observation);
2045
- setValues(
2046
- focusedSelect(observation)?.control.options.filter((option) => option.selected).map((option) => option.value) ?? []
2047
- );
2048
- } catch (cause) {
2049
- if (current === request.current)
2050
- setError(
2051
- cause instanceof Error ? cause.message : t("browserSelectControl.couldNotReadOptions")
2052
- );
2053
- } finally {
2054
- if (current === request.current) setBusy(false);
2055
- }
2056
- };
2057
- const apply = async (next) => {
2058
- if (!snapshot || !selected || busy) return;
2059
- const current = ++request.current;
2060
- setBusy(true);
2061
- setError(null);
2062
- try {
2063
- const receipt = await props.act(
2064
- { type: "select", locator: { kind: "ref", ref: selected.node.ref }, values: next },
2065
- snapshot
2066
- );
2067
- if (receipt.state !== "completed")
2068
- throw new Error(
2069
- receipt.error?.message ?? t("browserSelectControl.selectionDidNotComplete")
2070
- );
2071
- if (current === request.current) {
2072
- setOpen(false);
2073
- setSnapshot(null);
2074
- props.onDismiss?.();
2075
- }
2076
- } catch (cause) {
2077
- if (current === request.current) {
2078
- setError(
2079
- cause instanceof Error ? cause.message : t("browserSelectControl.selectionFailedReadTheOptions")
2080
- );
2081
- setSnapshot(null);
2082
- }
2083
- } finally {
2084
- if (current === request.current) setBusy(false);
2085
- }
2086
- };
2087
- if (props.manualFallback === false && !open) return null;
2088
- return /* @__PURE__ */ jsxs(
2089
- "div",
2090
- {
2091
- ref: popupRef,
2092
- className: "absolute z-10 max-w-[calc(100%-1.5rem)] text-og-control",
2093
- style: anchor && open ? {
2094
- left: position?.x ?? anchor.x,
2095
- top: position?.y ?? anchor.y
2096
- } : { bottom: 12, left: 12 },
2097
- children: [
2098
- props.manualFallback !== false && (!anchor || !open) || error ? /* @__PURE__ */ jsx(
2099
- "button",
2100
- {
2101
- type: "button",
2102
- className: "rounded-og-sm border border-og-border bg-og-bg px-2 py-1 text-og-fg",
2103
- disabled: busy,
2104
- onClick: () => void load(),
2105
- "aria-expanded": open,
2106
- children: t("browserSelectControl.chooseOption")
2107
- }
2108
- ) : null,
2109
- open ? /* @__PURE__ */ jsxs(
2110
- "section",
2111
- {
2112
- ref: panelRef,
2113
- tabIndex: -1,
2114
- "aria-label": t("browserSelectControl.pageSelectionOptions"),
2115
- style: position ? { maxHeight: Math.min(288, position.height) } : void 0,
2116
- onKeyDown: (event) => {
2117
- if (event.key !== "Escape") return;
2118
- event.preventDefault();
2119
- event.stopPropagation();
2120
- ++request.current;
2121
- setOpen(false);
2122
- setSnapshot(null);
2123
- setBusy(false);
2124
- props.onDismiss?.();
2125
- },
2126
- className: "mt-1 max-h-72 w-72 max-w-full overflow-auto rounded-og-sm border border-og-border bg-og-bg p-3 text-og-fg shadow-lg",
2127
- children: [
2128
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2129
- /* @__PURE__ */ jsx("strong", { children: selected?.node.name || t("browserSelectControl.pageOptions") }),
2130
- /* @__PURE__ */ jsx(
2131
- "button",
2132
- {
2133
- type: "button",
2134
- disabled: busy,
2135
- onClick: () => {
2136
- ++request.current;
2137
- setOpen(false);
2138
- setSnapshot(null);
2139
- props.onDismiss?.();
2140
- },
2141
- "aria-label": t("browserSelectControl.closePageOptions"),
2142
- children: t("browserSelectControl.close")
2143
- }
2144
- )
2145
- ] }),
2146
- busy ? /* @__PURE__ */ jsx("p", { role: "status", children: t("browserSelectControl.loading") }) : error ? /* @__PURE__ */ jsx("p", { role: "alert", children: error }) : !selected ? /* @__PURE__ */ jsx("p", { children: t("browserSelectControl.clickADropdownInThe") }) : selected.control.disabled ? /* @__PURE__ */ jsx("p", { children: t("browserSelectControl.thisControlIsDisabled") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2147
- selected.control.options.map((option) => {
2148
- const ambiguous = ambiguousValue(option.value);
2149
- const disabled = busy || option.disabled || ambiguous;
2150
- return selected.control.multiple ? /* @__PURE__ */ jsxs("label", { className: "my-1 flex items-center gap-2", children: [
2151
- /* @__PURE__ */ jsx(
2152
- "input",
2153
- {
2154
- type: "checkbox",
2155
- disabled,
2156
- checked: values.includes(option.value),
2157
- onChange: (event) => setValues(
2158
- event.target.checked ? [...values, option.value] : values.filter((value) => value !== option.value)
2159
- )
2160
- }
2161
- ),
2162
- option.label || t("browserSelectControl.empty"),
2163
- ambiguous ? ` ${t("browserSelectControl.ambiguousValue")}` : ""
2164
- ] }, optionKey(option)) : /* @__PURE__ */ jsxs(
2165
- "button",
2166
- {
2167
- type: "button",
2168
- disabled,
2169
- "aria-pressed": option.selected,
2170
- className: "my-1 block w-full rounded-og-sm border border-og-border px-2 py-1 text-left disabled:opacity-50",
2171
- onClick: () => void apply([option.value]),
2172
- children: [
2173
- option.label || t("browserSelectControl.empty"),
2174
- option.selected ? " \u2713" : "",
2175
- ambiguous ? ` ${t("browserSelectControl.ambiguousValue")}` : ""
2176
- ]
2177
- },
2178
- optionKey(option)
2179
- );
2180
- }),
2181
- selected.control.multiple ? /* @__PURE__ */ jsx(
2182
- "button",
2183
- {
2184
- type: "button",
2185
- disabled: busy || values.some(ambiguousValue),
2186
- onClick: () => void apply(values),
2187
- children: t("browserSelectControl.applySelection")
2188
- }
2189
- ) : null
2190
- ] })
2191
- ]
2192
- }
2193
- ) : null
2194
- ]
2195
- }
2196
- );
2197
- }
2198
-
2199
- // src/components/browser-viewer.tsx
2200
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2201
- var STALE_BROWSER_END_MAX_ATTEMPTS = 3;
2202
- var STALE_BROWSER_END_RETRY_BASE_MS = 5e3;
2203
- function staleBrowserEndRetryDelayMs(cause, failedAttempts) {
2204
- const status = cause?.status;
2205
- if (typeof status === "number" && status >= 400 && status < 500) return null;
2206
- if (failedAttempts >= STALE_BROWSER_END_MAX_ATTEMPTS) return null;
2207
- return STALE_BROWSER_END_RETRY_BASE_MS * 2 ** (failedAttempts - 1);
2208
- }
2209
- function BrowserViewer({
2210
- sessionId,
2211
- enabled = true,
2212
- active = true,
2213
- className,
2214
- onNotify,
2215
- webSocketFactory,
2216
- renderEmpty,
2217
- createLinkedComputer,
2218
- onOpenComputer,
2219
- initialBrowserSessionId,
2220
- onBrowserSessionIdChange,
2221
- browserExtensionSetupUrl,
2222
- ...override
2223
- }) {
2224
- const t = useOpenGeniText();
2225
- const registry = useBrowserSessions({ ...override, sessionId, enabled });
2226
- const attached = useAttachedBrowsers({ ...override, enabled });
2227
- const { client: configClient } = useEmbeddedBrowserInteraction(override);
2228
- const browserExtensionName = useClientConfigFlags(configClient).browserExtension?.name;
2229
- const onlineAttachedBridges = useMemo2(
2230
- () => attached.bridges.filter((bridge) => bridge.state === "online"),
2231
- [attached.bridges]
2232
- );
2233
- const profiles = useBrowserIdentities({ ...override, enabled, includeArchived: true });
2234
- const siteAuth = useSiteAuthConnections({
2235
- ...override,
2236
- enabled: enabled && profiles.identities.length > 0
2237
- });
2238
- const createRegistryBrowser = registry.create;
2239
- const refreshRegistry = registry.refresh;
2240
- const loadProfileRevisions = profiles.revisions;
2241
- const liveSessions = useMemo2(
2242
- () => registry.sessions.filter((session) => isLiveBrowser2(session)),
2243
- [registry.sessions]
2244
- );
2245
- const attachedGenerationLoss = useMemo2(
2246
- () => attachedChromeGenerationLoss(registry.sessions),
2247
- [registry.sessions]
2248
- );
2249
- const endedGenerationLossRef = useRef9(/* @__PURE__ */ new Set());
2250
- const endStaleBrowserAttemptsRef = useRef9(/* @__PURE__ */ new Map());
2251
- const endStaleBrowser = registry.end;
2252
- useEffect9(() => {
2253
- if (!enabled) return;
2254
- for (const session of registry.sessions) {
2255
- if (session.lifecycle !== "lost" || session.failureCode !== "controller_transition_expired" || session.placement.kind !== "attached_device") {
2256
- continue;
2257
- }
2258
- if (endedGenerationLossRef.current.has(session.id)) continue;
2259
- endedGenerationLossRef.current.add(session.id);
2260
- void endStaleBrowser(session.id).catch((cause) => {
2261
- const attempts = (endStaleBrowserAttemptsRef.current.get(session.id) ?? 0) + 1;
2262
- endStaleBrowserAttemptsRef.current.set(session.id, attempts);
2263
- const delay = staleBrowserEndRetryDelayMs(cause, attempts);
2264
- if (delay === null) return;
2265
- setTimeout(() => endedGenerationLossRef.current.delete(session.id), delay);
2266
- });
2267
- }
2268
- }, [enabled, endStaleBrowser, registry.sessions]);
2269
- const relevant = useMemo2(
2270
- () => registry.relevantSessions.filter((session) => isLiveBrowser2(session)),
2271
- [registry.relevantSessions]
2272
- );
2273
- const latestRelevantBrowser = registry.relevantSessions[0];
2274
- const unavailableBrowser = latestRelevantBrowser?.lifecycle === "lost" || latestRelevantBrowser?.lifecycle === "failed" ? latestRelevantBrowser : void 0;
2275
- const [selection, setSelection] = useState9(
2276
- () => initialBrowserSessionId ? { sessionId: initialBrowserSessionId, pinned: true } : null
2277
- );
2278
- const selectBrowserSession = useCallback8(
2279
- (next) => {
2280
- setSelection(next);
2281
- onBrowserSessionIdChange?.(next?.sessionId ?? null);
2282
- },
2283
- [onBrowserSessionIdChange]
2284
- );
2285
- const interventions = useInteractionInterventions({
2286
- ...override,
2287
- enabled,
2288
- resourceKind: "browser_session"
2289
- });
2290
- const [creating, setCreating] = useState9(false);
2291
- const [savingProfile, setSavingProfile] = useState9(false);
2292
- const [baseRevisionOrdinal, setBaseRevisionOrdinal] = useState9(null);
2293
- const [profileHistory, setProfileHistory] = useState9(null);
2294
- const [resumeAttempt, setResumeAttempt] = useState9(null);
2295
- const resumeAttemptsRef = useRef9(/* @__PURE__ */ new Map());
2296
- const previousSessionIdRef = useRef9(sessionId);
2297
- const seenInterventionIdsRef = useRef9(/* @__PURE__ */ new Set());
2298
- const diagnosticRequestRef = useRef9(0);
2299
- const viewportFocusHandoffRef = useRef9(null);
2300
- const [diagnosticsView, setDiagnosticsView] = useState9(null);
2301
- const notifyError = useCallback8(
2302
- (cause, fallback) => {
2303
- const message = cause instanceof Error ? cause.message : fallback;
2304
- onNotify?.({ kind: "error", message });
2305
- },
2306
- [onNotify]
2307
- );
2308
- useEffect9(() => {
2309
- if (previousSessionIdRef.current !== sessionId) {
2310
- previousSessionIdRef.current = sessionId;
2311
- setSelection(
2312
- initialBrowserSessionId ? { sessionId: initialBrowserSessionId, pinned: true } : null
2313
- );
2314
- }
2315
- }, [initialBrowserSessionId, sessionId]);
2316
- useEffect9(() => {
2317
- if (!enabled || registry.loading) return;
2318
- const selectedStillLive = liveSessions.some((session) => session.id === selection?.sessionId);
2319
- if (selection?.pinned && selectedStillLive) return;
2320
- const preferred = relevant[0] ?? null;
2321
- if (!preferred) {
2322
- if (selection) selectBrowserSession(null);
2323
- return;
2324
- }
2325
- if (!selectedStillLive || !selection?.pinned) {
2326
- if (selection?.sessionId !== preferred.id || selection.pinned) {
2327
- selectBrowserSession({ sessionId: preferred.id, pinned: false });
2328
- }
2329
- }
2330
- }, [enabled, liveSessions, registry.loading, relevant, selectBrowserSession, selection]);
2331
- const selectedRegistrySession = useMemo2(
2332
- () => liveSessions.find((session) => session.id === selection?.sessionId) ?? null,
2333
- [liveSessions, selection?.sessionId]
2334
- );
2335
- const interventionCounts = useMemo2(
2336
- () => countInterventions(interventions.interventions),
2337
- [interventions.interventions]
2338
- );
2339
- const selectedInterventions = useMemo2(
2340
- () => interventions.interventions.filter(
2341
- (intervention) => intervention.resourceId === selection?.sessionId
2342
- ),
2343
- [interventions.interventions, selection?.sessionId]
2344
- );
2345
- const controllerReady = selectedRegistrySession?.lifecycle === "active";
2346
- const resumeSession = registry.resume;
2347
- const [hasLiveFrame, setHasLiveFrame] = useState9(false);
2348
- const wakeBrowser = useCallback8(
2349
- async (session, retry = false) => {
2350
- const current = resumeAttemptsRef.current.get(session.id);
2351
- if (current?.running || !retry && current) return;
2352
- const operationId = current && !current.terminal ? current.operationId : crypto.randomUUID();
2353
- const attempt = {
2354
- operationId,
2355
- running: true,
2356
- terminal: false,
2357
- error: null
2358
- };
2359
- resumeAttemptsRef.current.set(session.id, attempt);
2360
- setResumeAttempt({ sessionId: session.id, attempt });
2361
- try {
2362
- const response = await resumeSession(session.id, operationId);
2363
- if (response.operation.state !== "completed") {
2364
- throw Object.assign(
2365
- new Error(
2366
- response.operation.error?.message ?? t("browserViewer.browserCouldNotReopen2")
2367
- ),
2368
- { terminal: true }
2369
- );
2370
- }
2371
- resumeAttemptsRef.current.delete(session.id);
2372
- setResumeAttempt((visible) => visible?.sessionId === session.id ? null : visible);
2373
- } catch (cause) {
2374
- const error = cause instanceof Error ? cause : new Error(String(cause));
2375
- const failed = {
2376
- operationId,
2377
- running: false,
2378
- terminal: typeof cause === "object" && cause !== null && "terminal" in cause ? cause.terminal === true : false,
2379
- error
2380
- };
2381
- resumeAttemptsRef.current.set(session.id, failed);
2382
- setResumeAttempt({ sessionId: session.id, attempt: failed });
2383
- notifyError(error, t("browserViewer.couldNotReopenThisBrowser"));
2384
- }
2385
- },
2386
- [notifyError, resumeSession, t]
2387
- );
2388
- useEffect9(() => {
2389
- if (!selectedRegistrySession) return;
2390
- if (selectedRegistrySession.lifecycle === "active") {
2391
- resumeAttemptsRef.current.delete(selectedRegistrySession.id);
2392
- setResumeAttempt(
2393
- (visible) => visible?.sessionId === selectedRegistrySession.id ? null : visible
2394
- );
2395
- }
2396
- }, [selectedRegistrySession]);
2397
- const browser = useBrowserSession({
2398
- ...override,
2399
- browserSessionId: selection?.sessionId ?? null,
2400
- enabled: enabled && selection !== null && controllerReady,
2401
- semanticObservationEnabled: active && !hasLiveFrame
2402
- });
2403
- useEffect9(() => {
2404
- if (!isSourcePlacementChangedError(browser.error, "browser_session")) return;
2405
- void refreshRegistry();
2406
- }, [browser.error, refreshRegistry]);
2407
- const downloads = useBrowserDownloads({
2408
- ...override,
2409
- browserSessionId: selection?.sessionId ?? null,
2410
- enabled: enabled && diagnosticsView !== null && controllerReady && browser.session?.capabilities.downloads === true
2411
- });
2412
- const loadBrowserDiagnostics = browser.diagnostics;
2413
- useEffect9(() => {
2414
- for (const intervention of interventions.interventions) {
2415
- if (seenInterventionIdsRef.current.has(intervention.id)) continue;
2416
- seenInterventionIdsRef.current.add(intervention.id);
2417
- onNotify?.({
2418
- kind: "info",
2419
- message: `${interventionTitle(t, intervention)}: ${intervention.reason}`
2420
- });
2421
- }
2422
- }, [interventions.interventions, onNotify, t]);
2423
- const resolveIntervention = useCallback8(
2424
- (intervention, outcome) => {
2425
- void interventions.resolve(intervention.id, {
2426
- expectedVersion: intervention.version,
2427
- outcome
2428
- }).then(() => {
2429
- onNotify?.({
2430
- kind: "info",
2431
- message: outcome === "completed" ? t("browserViewer.agentNotifiedContinuingWork") : t("browserViewer.requestCancelled")
2432
- });
2433
- }).catch((cause) => notifyError(cause, t("browserViewer.couldNotUpdateTheBrowser")));
2434
- },
2435
- [interventions, notifyError, onNotify, t]
2436
- );
2437
- const frames = useBrowserFrameStream({
2438
- ...override,
2439
- browserSessionId: selection?.sessionId ?? null,
2440
- targetId: browser.selectedTarget?.id ?? null,
2441
- enabled: enabled && selection !== null && controllerReady && browser.selectedTarget !== null && browser.session?.capabilities.liveFrames === true,
2442
- stream: { format: "jpeg", quality: 76, maxWidth: 1920, maxHeight: 1200 },
2443
- ...webSocketFactory ? { webSocketFactory } : {}
2444
- });
2445
- const receivedFrame = frameMatchesSelectedTarget(
2446
- frames.frame,
2447
- browser.session,
2448
- browser.selectedTarget
2449
- ) ? frames.frame : null;
2450
- const displayedFrame = useMemo2(
2451
- () => receivedFrame ? { ...receivedFrame, data: receivedFrame.data.slice().buffer } : null,
2452
- [receivedFrame]
2453
- );
2454
- const frameIsLive = frames.state === "live" && displayedFrame !== null;
2455
- useEffect9(() => {
2456
- setHasLiveFrame(frameIsLive);
2457
- }, [frameIsLive]);
2458
- const supportsLiveFrames = (browser.session ?? selectedRegistrySession)?.capabilities.liveFrames === true;
2459
- const controlUnavailable = isInteractionControlUnavailable(browser.error);
2460
- const connectionError = controlUnavailable ? browser.error : frames.error ?? browser.error;
2461
- const selectedPlacement = (browser.session ?? selectedRegistrySession)?.placement;
2462
- const selectedChromeGenerationLoss = selectedPlacement?.kind === "attached_device" && isAttachedChromeGenerationLossError(connectionError);
2463
- const replacementChromeDevice = selectedChromeGenerationLoss && selectedPlacement.kind === "attached_device" ? attached.devices.find((candidate) => candidate.id === selectedPlacement.deviceId) : null;
2464
- const displayConnectionState = connectionError && (!frameIsLive || controlUnavailable) ? "error" : !browser.session ? "connecting" : supportsLiveFrames ? frames.state === "live" && !displayedFrame ? "connecting" : frames.state : "semantic";
2465
- const selectedProfile = useMemo2(
2466
- () => profiles.identities.find(
2467
- (identity) => identity.id === (browser.session?.identityId ?? selectedRegistrySession?.identityId)
2468
- ) ?? null,
2469
- [browser.session?.identityId, profiles.identities, selectedRegistrySession?.identityId]
2470
- );
2471
- const selectedProfileAuth = useMemo2(
2472
- () => selectedProfile ? siteAuth.connections.filter(
2473
- (connection) => connection.preferredIdentityId === selectedProfile.id
2474
- ) : [],
2475
- [selectedProfile, siteAuth.connections]
2476
- );
2477
- useEffect9(() => {
2478
- diagnosticRequestRef.current += 1;
2479
- setDiagnosticsView(null);
2480
- }, [browser.session?.id, browser.selectedTarget?.id]);
2481
- const loadDiagnostics = useCallback8(() => {
2482
- const browserSessionId = browser.session?.id;
2483
- const targetId = browser.selectedTarget?.id;
2484
- if (!browserSessionId || !targetId) return;
2485
- const requestId = diagnosticRequestRef.current + 1;
2486
- diagnosticRequestRef.current = requestId;
2487
- setDiagnosticsView((current) => ({
2488
- browserSessionId,
2489
- targetId,
2490
- loading: true,
2491
- batch: current?.browserSessionId === browserSessionId && current.targetId === targetId ? current.batch : null,
2492
- error: null
2493
- }));
2494
- void loadBrowserDiagnostics({ limit: 100 }).then((batch) => {
2495
- if (diagnosticRequestRef.current !== requestId) return;
2496
- setDiagnosticsView({ browserSessionId, targetId, loading: false, batch, error: null });
2497
- }).catch((cause) => {
2498
- if (diagnosticRequestRef.current !== requestId) return;
2499
- setDiagnosticsView({
2500
- browserSessionId,
2501
- targetId,
2502
- loading: false,
2503
- batch: null,
2504
- error: cause instanceof Error ? cause : new Error(String(cause))
2505
- });
2506
- });
2507
- }, [browser.selectedTarget?.id, browser.session?.id, loadBrowserDiagnostics]);
2508
- const closeDiagnostics = useCallback8(() => {
2509
- diagnosticRequestRef.current += 1;
2510
- setDiagnosticsView(null);
2511
- }, []);
2512
- const refreshDiagnostics = useCallback8(() => {
2513
- loadDiagnostics();
2514
- if (browser.session?.capabilities.downloads) void downloads.refresh();
2515
- }, [browser.session?.capabilities.downloads, downloads, loadDiagnostics]);
2516
- const saveDownload = useCallback8(
2517
- async (downloadId, destinationPath, overwrite) => {
2518
- const response = await downloads.saveToWorkspace(downloadId, destinationPath, { overwrite });
2519
- onNotify?.({
2520
- kind: "info",
2521
- message: t("browserViewer.filenameSavedToDestinationpath", {
2522
- filename: response.download.filename,
2523
- destinationPath: response.destinationPath
2524
- })
2525
- });
2526
- return response;
2527
- },
2528
- [downloads, onNotify, t]
2529
- );
2530
- useEffect9(() => {
2531
- const identityId = browser.session?.identityId ?? selectedRegistrySession?.identityId;
2532
- const revisionId = browser.session?.baseRevisionId ?? selectedRegistrySession?.baseRevisionId;
2533
- if (!identityId) {
2534
- setBaseRevisionOrdinal(null);
2535
- setProfileHistory(null);
2536
- return;
2537
- }
2538
- let disposed = false;
2539
- setBaseRevisionOrdinal(null);
2540
- setProfileHistory({ identityId, loading: true, revisions: [] });
2541
- void loadProfileRevisions(identityId).then((response) => {
2542
- if (!disposed) {
2543
- setBaseRevisionOrdinal(
2544
- revisionId ? response.revisions.find((revision) => revision.id === revisionId)?.ordinal ?? null : null
2545
- );
2546
- setProfileHistory({ identityId, loading: false, revisions: response.revisions });
2547
- }
2548
- }).catch(() => {
2549
- if (!disposed) setProfileHistory({ identityId, loading: false, revisions: [] });
2550
- });
2551
- return () => {
2552
- disposed = true;
2553
- };
2554
- }, [
2555
- browser.session?.baseRevisionId,
2556
- browser.session?.identityId,
2557
- loadProfileRevisions,
2558
- selectedRegistrySession?.baseRevisionId,
2559
- selectedRegistrySession?.identityId
2560
- ]);
2561
- const createBrowser = useCallback8(
2562
- (choice = { kind: "clean" }) => {
2563
- if (creating) return;
2564
- const identity = choice.kind === "profile" ? profiles.identities.find((candidate) => candidate.id === choice.identityId) : null;
2565
- if (choice.kind === "profile" && (!identity || identity.status !== "active")) {
2566
- onNotify?.({
2567
- kind: "error",
2568
- message: t("browserViewer.thisBrowserProfileIsNo")
2569
- });
2570
- return;
2571
- }
2572
- const device = choice.kind === "attached" ? choice.device : null;
2573
- const browserName = device?.profileLabel ?? device?.name ?? (identity ? `${identity.name} browser` : t("browserViewer.browser"));
2574
- setCreating(true);
2575
- void (async () => {
2576
- let linkedComputer = null;
2577
- let computerViewUnavailable = false;
2578
- if (createLinkedComputer) {
2579
- try {
2580
- linkedComputer = await createLinkedComputer(
2581
- `${browserName} desktop`,
2582
- device ? { kind: "attached_device", deviceId: device.id } : void 0
2583
- );
2584
- } catch {
2585
- computerViewUnavailable = true;
2586
- }
2587
- }
2588
- const response = await createRegistryBrowser({
2589
- sessionId,
2590
- name: browserName,
2591
- // Human-created browsers are always ordinary visual Chromium/Chrome.
2592
- // Lightpanda remains an agent-only optimization requested explicitly
2593
- // through browser_open; if an agent creates one it is still visible in
2594
- // the shared BrowserSession switcher.
2595
- headless: false,
2596
- initialUrl: HUMAN_BROWSER_HOME_URL,
2597
- ...device ? {
2598
- placement: {
2599
- kind: "attached_device",
2600
- deviceId: device.id
2601
- }
2602
- } : {},
2603
- ...linkedComputer ? {
2604
- linkedComputerSessionId: linkedComputer.id,
2605
- placement: linkedComputer.placement
2606
- } : {},
2607
- ...identity ? {
2608
- identityId: identity.id,
2609
- ...choice.kind === "profile" && choice.baseRevisionId ? { baseRevisionId: choice.baseRevisionId } : {}
2610
- } : {}
2611
- });
2612
- selectBrowserSession({ sessionId: response.session.id, pinned: true });
2613
- if (computerViewUnavailable) {
2614
- onNotify?.({
2615
- kind: "info",
2616
- message: t("browserViewer.browserOpenedDesktopViewIs")
2617
- });
2618
- }
2619
- })().catch((cause) => notifyError(cause, t("browserViewer.couldNotOpenABrowser"))).finally(() => setCreating(false));
2620
- },
2621
- [
2622
- createLinkedComputer,
2623
- createRegistryBrowser,
2624
- creating,
2625
- notifyError,
2626
- onNotify,
2627
- selectBrowserSession,
2628
- profiles.identities,
2629
- sessionId,
2630
- t
2631
- ]
2632
- );
2633
- const saveProfileVersion = useCallback8(
2634
- async (newProfileName) => {
2635
- const session = browser.session;
2636
- if (!session || savingProfile || !session.capabilities.identityPublication) return false;
2637
- setSavingProfile(true);
2638
- try {
2639
- let identity = selectedProfile;
2640
- if (!identity) {
2641
- const name = newProfileName?.trim() ?? "";
2642
- if (!name) return false;
2643
- identity = profiles.identities.find(
2644
- (candidate) => candidate.status === "active" && candidate.revisionCount === 0 && candidate.name.localeCompare(name, void 0, { sensitivity: "base" }) === 0
2645
- ) ?? (await profiles.create({ name })).identity;
2646
- }
2647
- const response = await profiles.publish(session.id, {
2648
- identityId: identity.id,
2649
- expectedHeadGeneration: identity.headGeneration,
2650
- advanceDefault: true
2651
- });
2652
- setBaseRevisionOrdinal(response.revision.ordinal);
2653
- setProfileHistory((current) => ({
2654
- identityId: response.identity.id,
2655
- loading: false,
2656
- revisions: current?.identityId === response.identity.id ? [
2657
- ...current.revisions.filter((revision) => revision.id !== response.revision.id),
2658
- response.revision
2659
- ] : [response.revision]
2660
- }));
2661
- await Promise.all([browser.refresh(), registry.refresh()]);
2662
- onNotify?.({
2663
- kind: "info",
2664
- message: response.outcome === "saved_as_default" ? t("browserViewer.nameVersionOrdinalSavedFor", {
2665
- name: response.identity.name,
2666
- ordinal: response.revision.ordinal
2667
- }) : t("browserViewer.nameVersionOrdinalSavedAnother", {
2668
- name: response.identity.name,
2669
- ordinal: response.revision.ordinal
2670
- })
2671
- });
2672
- return true;
2673
- } catch (cause) {
2674
- notifyError(cause, t("browserViewer.couldNotSaveThisBrowser"));
2675
- return false;
2676
- } finally {
2677
- setSavingProfile(false);
2678
- }
2679
- },
2680
- [browser, notifyError, onNotify, profiles, registry, savingProfile, selectedProfile, t]
2681
- );
2682
- const updateProfile = useCallback8(
2683
- async (identity, update) => {
2684
- if (savingProfile) return false;
2685
- setSavingProfile(true);
2686
- try {
2687
- const response = await profiles.update(identity.id, {
2688
- expectedVersion: identity.version,
2689
- ...update
2690
- });
2691
- if (update.defaultRevisionId) {
2692
- const ordinal = profileHistory?.revisions.find(
2693
- (revision) => revision.id === update.defaultRevisionId
2694
- )?.ordinal;
2695
- onNotify?.({
2696
- kind: "info",
2697
- message: ordinal ? t("browserViewer.nameVersionOpensByDefault", {
2698
- name: response.identity.name,
2699
- ordinal
2700
- }) : t("browserViewer.nameOpensByDefault", { name: response.identity.name })
2701
- });
2702
- } else if (update.status === "archived") {
2703
- onNotify?.({
2704
- kind: "info",
2705
- message: t("browserViewer.nameHiddenFromNewBrowsers", { name: response.identity.name })
2706
- });
2707
- } else if (update.status === "active") {
2708
- onNotify?.({ kind: "info", message: `${response.identity.name} restored.` });
2709
- }
2710
- return true;
2711
- } catch (cause) {
2712
- notifyError(cause, t("browserViewer.couldNotUpdateThisBrowser"));
2713
- await profiles.refresh().catch(() => void 0);
2714
- return false;
2715
- } finally {
2716
- setSavingProfile(false);
2717
- }
2718
- },
2719
- [notifyError, onNotify, profileHistory?.revisions, profiles, savingProfile, t]
2720
- );
2721
- if (!enabled) return null;
2722
- if (registry.loading && liveSessions.length === 0) {
2723
- return /* @__PURE__ */ jsx2(
2724
- BrowserNotice,
2725
- {
2726
- icon: /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "animate-spin" }),
2727
- text: t("browserViewer.findingBrowsers"),
2728
- ...className ? { className } : {}
2729
- }
2730
- );
2731
- }
2732
- if (liveSessions.length === 0) {
2733
- if (renderEmpty && !unavailableBrowser)
2734
- return /* @__PURE__ */ jsx2(Fragment2, { children: renderEmpty(() => createBrowser(), creating) });
2735
- return /* @__PURE__ */ jsx2(
2736
- "div",
2737
- {
2738
- className: cn("flex h-full min-h-0 items-center justify-center bg-og-bg p-6", className),
2739
- children: /* @__PURE__ */ jsxs2("div", { className: "max-w-sm text-center", children: [
2740
- /* @__PURE__ */ jsx2("span", { className: "mx-auto grid size-10 place-items-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted", children: /* @__PURE__ */ jsx2(Globe2Icon, { className: "size-4.5" }) }),
2741
- unavailableBrowser && !attachedGenerationLoss ? /* @__PURE__ */ jsx2(BrowserUnavailableNotice, { session: unavailableBrowser }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
2742
- /* @__PURE__ */ jsx2("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: attachedGenerationLoss ? t("browserViewer.chromeReconnectedOpenAFresh") : t("browserViewer.noBrowserOpen") }),
2743
- /* @__PURE__ */ jsx2("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: attachedGenerationLoss ? t("browserViewer.thisChromeProfileIsLive") : t("browserViewer.aBrowserAppearsHereWhen") })
2744
- ] }),
2745
- /* @__PURE__ */ jsx2(
2746
- BrowserLaunchMenu,
2747
- {
2748
- attachedBridges: onlineAttachedBridges,
2749
- attachedDevices: attached.devices,
2750
- identities: profiles.identities,
2751
- creating,
2752
- mutatingProfile: savingProfile,
2753
- onCreate: createBrowser,
2754
- onRestore: (identity) => updateProfile(identity, { status: "active" }),
2755
- setupUrl: browserExtensionSetupUrl,
2756
- extensionName: browserExtensionName,
2757
- prominent: true
2758
- }
2759
- ),
2760
- registry.error ? /* @__PURE__ */ jsx2("p", { className: "mt-3 text-og-control text-og-danger", children: registry.error.message }) : null
2761
- ] })
2762
- }
2763
- );
2764
- }
2765
- return /* @__PURE__ */ jsxs2(
2766
- "div",
2767
- {
2768
- className: cn("relative flex h-full min-h-0 flex-col overflow-hidden bg-og-bg", className),
2769
- children: [
2770
- /* @__PURE__ */ jsx2(
2771
- BrowserToolbar,
2772
- {
2773
- sessions: liveSessions,
2774
- relevantSessionIds: new Set(relevant.map((session) => session.id)),
2775
- selectedSessionId: selection?.sessionId ?? null,
2776
- identities: profiles.identities,
2777
- attachedBridges: onlineAttachedBridges,
2778
- attachedDevices: attached.devices,
2779
- selectedProfile,
2780
- baseRevisionOrdinal,
2781
- profileHistory,
2782
- authConnections: selectedProfileAuth,
2783
- creating,
2784
- savingProfile,
2785
- refreshing: registry.refreshing || attached.refreshing,
2786
- interventionCounts,
2787
- onSelect: (browserSessionId) => selectBrowserSession({ sessionId: browserSessionId, pinned: true }),
2788
- onFollow: () => {
2789
- const preferred = relevant[0];
2790
- if (preferred) selectBrowserSession({ sessionId: preferred.id, pinned: false });
2791
- },
2792
- onCreate: createBrowser,
2793
- onSaveProfile: saveProfileVersion,
2794
- onUpdateProfile: updateProfile,
2795
- onOpenProfileVersion: (identityId, baseRevisionId) => createBrowser({ kind: "profile", identityId, baseRevisionId }),
2796
- onRefresh: () => void Promise.all([registry.refresh(), attached.refresh()]),
2797
- setupUrl: browserExtensionSetupUrl,
2798
- extensionName: browserExtensionName
2799
- }
2800
- ),
2801
- /* @__PURE__ */ jsx2(
2802
- InteractionInterventionBanner,
2803
- {
2804
- interventions: selectedInterventions,
2805
- activeTargetId: browser.selectedTarget?.id ?? null,
2806
- mutating: interventions.mutating,
2807
- onOpen: (intervention) => void browser.selectTarget(intervention.targetId).catch((cause) => notifyError(cause, t("browserViewer.couldNotOpenTheRequested"))),
2808
- onResolve: resolveIntervention
2809
- }
2810
- ),
2811
- !selectedRegistrySession ? /* @__PURE__ */ jsx2(
2812
- BrowserUnselectedPanel,
2813
- {
2814
- unavailableBrowser,
2815
- peerCount: liveSessions.length,
2816
- creating,
2817
- onCreate: () => createBrowser()
2818
- }
2819
- ) : !controllerReady ? /* @__PURE__ */ jsx2(
2820
- BrowserLifecyclePanel,
2821
- {
2822
- session: selectedRegistrySession,
2823
- attempt: resumeAttempt?.sessionId === selectedRegistrySession.id ? resumeAttempt.attempt : null,
2824
- onResume: () => void wakeBrowser(selectedRegistrySession, true)
2825
- }
2826
- ) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
2827
- /* @__PURE__ */ jsx2(
2828
- BrowserTabs,
2829
- {
2830
- targets: browser.targets,
2831
- selectedTargetId: browser.selectedTarget?.id ?? null,
2832
- mutating: savingProfile,
2833
- tabControl: browser.session?.capabilities.tabs === true,
2834
- onSelect: (targetId) => void browser.selectTarget(targetId).catch((cause) => notifyError(cause, t("browserViewer.couldNotSwitchTabs"))),
2835
- onClose: (targetId) => void browser.closeTarget(targetId).catch((cause) => notifyError(cause, t("browserViewer.couldNotCloseTheTab"))),
2836
- onOpen: () => void browser.openTarget(HUMAN_BROWSER_HOME_URL).catch((cause) => notifyError(cause, t("browserViewer.couldNotOpenATab")))
2837
- }
2838
- ),
2839
- /* @__PURE__ */ jsx2(
2840
- BrowserAddressBar,
2841
- {
2842
- target: browser.selectedTarget,
2843
- loading: browser.loading || browser.mutating || savingProfile,
2844
- onNavigate: (url) => void browser.act({ type: "navigate", url }).catch((cause) => notifyError(cause, t("browserViewer.couldNotNavigate"))),
2845
- onHistory: async (direction) => {
2846
- try {
2847
- const action = { type: "history", direction };
2848
- if (receivedFrame) await browser.actFromFrame(action, receivedFrame);
2849
- else {
2850
- if (!browser.observation)
2851
- throw new Error("The browser page is not ready for input.");
2852
- await browser.actFromObservation(action, browser.observation);
2853
- }
2854
- } catch (cause) {
2855
- notifyError(
2856
- cause,
2857
- direction === "back" ? t("browserViewer.couldNotGoBack") : t("browserViewer.couldNotGoForward")
2858
- );
2859
- }
2860
- },
2861
- onReload: () => {
2862
- const url = browser.selectedTarget?.url;
2863
- if (url)
2864
- void browser.act({ type: "navigate", url }).catch((cause) => notifyError(cause, t("browserViewer.couldNotReload")));
2865
- }
2866
- }
2867
- ),
2868
- browser.inputFailure ? /* @__PURE__ */ jsxs2(
2869
- "div",
2870
- {
2871
- role: "alert",
2872
- "aria-label": t("browserViewer.browserInputStatus"),
2873
- className: "flex shrink-0 items-center gap-2 border-t border-og-border bg-og-surface-1 px-3 py-2 text-og-xs",
2874
- children: [
2875
- /* @__PURE__ */ jsx2(CircleAlertIcon, { className: "size-4 shrink-0 text-og-status-failed" }),
2876
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
2877
- /* @__PURE__ */ jsx2("span", { className: "font-medium", children: browser.inputFailure.state === "failed" ? t("browserViewer.browserInputFailed") : t("browserViewer.inputResultUnknown") }),
2878
- /* @__PURE__ */ jsx2("p", { className: "text-og-fg-subtle", children: browser.inputFailure.error?.message ?? t("browserViewer.inspectThePageBeforeContinuing") })
2879
- ] }),
2880
- /* @__PURE__ */ jsx2(
2881
- "button",
2882
- {
2883
- type: "button",
2884
- onClick: () => void browser.refresh(),
2885
- className: "shrink-0 rounded border border-og-border px-2 py-1 transition hover:bg-og-surface-2",
2886
- children: t("browserViewer.checkBrowser")
2887
- }
2888
- )
2889
- ]
2890
- }
2891
- ) : null,
2892
- /* @__PURE__ */ jsxs2("div", { className: "relative flex min-h-0 min-w-0 flex-1 overflow-hidden", children: [
2893
- /* @__PURE__ */ jsx2(
2894
- BrowserViewport,
2895
- {
2896
- focusHandoffRef: viewportFocusHandoffRef,
2897
- focusScope: JSON.stringify([
2898
- selection?.sessionId,
2899
- browser.selectedTarget?.id,
2900
- browser.selectedTarget?.targetGeneration
2901
- ]),
2902
- frame: displayedFrame,
2903
- connectionState: displayConnectionState,
2904
- supportsLiveFrames,
2905
- connectionError,
2906
- controlUnavailable,
2907
- observation: browser.observation,
2908
- mutating: browser.mutating || savingProfile,
2909
- activityLabel: savingProfile ? t("browserViewer.savingBrowserVersion") : void 0,
2910
- clipboardEnabled: browser.session?.capabilities.clipboard === true,
2911
- inputBatchAttachment: frames.attachment,
2912
- onAction: async (action, frame) => {
2913
- const focusedInput = frame && action.type === "pointer" && action.action === "click" && (action.button === void 0 || action.button === "left") && frames.attachment?.focusedInputObservations === true && frames.attachment.browserSessionId === frame.browserSessionId && frames.attachment.controllerGeneration === frame.controllerGeneration && frames.attachment.targetId === frame.targetId && Date.parse(frames.attachment.expiresAt) > Date.now();
2914
- const receipt = frame ? await browser.actFromFrame(
2915
- action,
2916
- frame,
2917
- void 0,
2918
- focusedInput ? "input" : "none"
2919
- ) : await browser.act(action);
2920
- if (receipt.state !== "completed") {
2921
- throw new Error(
2922
- receipt.error?.message ?? t("browserViewer.browserInputDidNotComplete")
2923
- );
2924
- }
2925
- return receipt;
2926
- },
2927
- onReadClipboard: browser.readClipboard,
2928
- onObserveForInput: browser.observeForInput,
2929
- onSelectFromObservation: browser.actFromObservation,
2930
- onReconnect: selectedChromeGenerationLoss ? () => {
2931
- if (replacementChromeDevice) {
2932
- createBrowser({ kind: "attached", device: replacementChromeDevice });
2933
- }
2934
- } : () => {
2935
- void browser.refresh();
2936
- frames.reconnect();
2937
- },
2938
- reconnectLabel: selectedChromeGenerationLoss && replacementChromeDevice ? t("browserViewer.openAFreshConnectedChrome") : void 0,
2939
- reconnectMessage: selectedChromeGenerationLoss ? t("browserViewer.chromeReconnectedOpenAFresh") : void 0,
2940
- onError: (cause) => notifyError(cause, t("browserViewer.browserInputFailed2"))
2941
- },
2942
- JSON.stringify([
2943
- selection?.sessionId,
2944
- browser.selectedTarget?.id,
2945
- browser.selectedTarget?.targetGeneration,
2946
- browser.selectedTarget?.documentGeneration,
2947
- frames.attachment?.controllerGeneration
2948
- ])
2949
- ),
2950
- diagnosticsView ? /* @__PURE__ */ jsx2(
2951
- BrowserDiagnosticsDrawer,
2952
- {
2953
- session: browser.session,
2954
- profile: selectedProfile,
2955
- target: browser.selectedTarget,
2956
- observation: browser.observation,
2957
- baseRevisionOrdinal,
2958
- state: diagnosticsView,
2959
- downloads: downloads.downloads,
2960
- downloadsEnabled: browser.session?.capabilities.downloads === true,
2961
- downloadsLoading: downloads.loading,
2962
- downloadsRefreshing: downloads.refreshing,
2963
- downloadsError: downloads.error,
2964
- savingDownloadIds: downloads.savingDownloadIds,
2965
- onSaveDownload: saveDownload,
2966
- onRefreshDownloads: () => void downloads.refresh(),
2967
- onRefresh: refreshDiagnostics,
2968
- onClose: closeDiagnostics
2969
- }
2970
- ) : null
2971
- ] }),
2972
- /* @__PURE__ */ jsx2(
2973
- BrowserStatusBar,
2974
- {
2975
- session: browser.session,
2976
- profile: selectedProfile,
2977
- target: browser.selectedTarget,
2978
- observation: browser.observation,
2979
- inputFailure: browser.inputFailure,
2980
- connectionState: displayConnectionState,
2981
- refreshing: registry.refreshing,
2982
- diagnosticsOpen: diagnosticsView !== null,
2983
- onOpenComputer: browser.session?.linkedComputerSessionId && onOpenComputer ? () => {
2984
- const computerSessionId = browser.session?.linkedComputerSessionId;
2985
- if (computerSessionId) onOpenComputer(computerSessionId);
2986
- } : void 0,
2987
- onDiagnostics: diagnosticsView ? closeDiagnostics : loadDiagnostics
2988
- }
2989
- )
2990
- ] })
2991
- ]
2992
- }
2993
- );
2994
- }
2995
- function BrowserLifecyclePanel(props) {
2996
- const t = useOpenGeniText();
2997
- const failed = props.attempt?.error ?? null;
2998
- const label = failed ? t("browserViewer.browserCouldNotReopen") : props.session.lifecycle === "suspending" ? t("browserViewer.savingBrowserState") : props.session.lifecycle === "restoring" || props.attempt?.running ? t("browserViewer.openingBrowser") : props.session.lifecycle === "starting" ? t("browserViewer.startingBrowser") : t("browserViewer.browserIsSleeping");
2999
- const detail = failed ? failed.message : props.session.lifecycle === "suspended" ? t("browserViewer.itsPrivateStateIsSaved") : t("browserViewer.theBrowserWillReconnectHere");
3000
- return /* @__PURE__ */ jsx2("div", { className: "grid min-h-0 flex-1 place-items-center bg-og-bg p-6 text-center", children: /* @__PURE__ */ jsxs2("div", { className: "max-w-sm", children: [
3001
- /* @__PURE__ */ jsx2("span", { className: "mx-auto grid size-10 place-items-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted", children: failed ? /* @__PURE__ */ jsx2(CircleAlertIcon, { className: "size-4.5 text-og-danger" }) : /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-4.5 animate-spin" }) }),
3002
- /* @__PURE__ */ jsx2("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: label }),
3003
- /* @__PURE__ */ jsx2("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: detail }),
3004
- failed || props.session.lifecycle === "suspended" && !props.attempt?.running ? /* @__PURE__ */ jsx2(
3005
- "button",
3006
- {
3007
- type: "button",
3008
- onClick: props.onResume,
3009
- className: "mt-4 inline-flex h-8 items-center rounded-og-sm border border-og-primary-border bg-og-primary px-3 text-og-control font-medium text-og-primary-fg transition hover:bg-og-primary-hover",
3010
- children: t("browserViewer.openBrowser")
3011
- }
3012
- ) : null
3013
- ] }) });
3014
- }
3015
- function BrowserToolbar(props) {
3016
- const t = useOpenGeniText();
3017
- const detailsRef = useViewerMenuDismiss();
3018
- const selected = props.sessions.find((session) => session.id === props.selectedSessionId);
3019
- const current = props.sessions.filter((session) => props.relevantSessionIds.has(session.id));
3020
- const others = props.sessions.filter((session) => !props.relevantSessionIds.has(session.id));
3021
- const choose = (id) => {
3022
- props.onSelect(id);
3023
- detailsRef.current?.removeAttribute("open");
3024
- };
3025
- return /* @__PURE__ */ jsxs2("div", { className: "flex h-10 shrink-0 items-center gap-1.5 border-b border-og-border bg-og-surface-1 px-2", children: [
3026
- /* @__PURE__ */ jsxs2("details", { ref: detailsRef, className: "min-w-0", children: [
3027
- /* @__PURE__ */ jsxs2("summary", { className: "flex h-7 max-w-52 cursor-pointer list-none items-center gap-2 rounded-og-sm px-2 text-og-control text-og-fg transition hover:bg-og-surface-2 [&::-webkit-details-marker]:hidden", children: [
3028
- /* @__PURE__ */ jsx2(Globe2Icon, { className: "size-3.5 shrink-0 text-og-fg-muted" }),
3029
- /* @__PURE__ */ jsx2("span", { className: "truncate font-medium", children: selected?.name ?? t("browserViewer.browser") }),
3030
- (props.interventionCounts.get(selected?.id ?? "") ?? 0) > 0 ? /* @__PURE__ */ jsx2("span", { className: "size-1.5 shrink-0 rounded-full bg-og-status-waiting" }) : null,
3031
- /* @__PURE__ */ jsx2(ChevronDownIcon, { className: "size-3 shrink-0 text-og-fg-subtle" })
3032
- ] }),
3033
- /* @__PURE__ */ jsxs2("div", { className: "absolute left-2 top-10 z-30 flex max-h-[calc(100%-3rem)] w-72 max-w-[calc(100%-1rem)] flex-col overflow-hidden rounded-og-md border border-og-border bg-og-surface-1 p-1 shadow-xl", children: [
3034
- /* @__PURE__ */ jsxs2("div", { className: "min-h-0 max-h-96 overflow-y-auto overscroll-contain", children: [
3035
- /* @__PURE__ */ jsx2(
3036
- BrowserSessionGroup,
3037
- {
3038
- label: t("browserViewer.currentAgent"),
3039
- sessions: current,
3040
- identities: props.identities,
3041
- selectedId: props.selectedSessionId,
3042
- interventionCounts: props.interventionCounts,
3043
- onSelect: choose
3044
- }
3045
- ),
3046
- /* @__PURE__ */ jsx2(
3047
- BrowserSessionGroup,
3048
- {
3049
- label: t("browserViewer.workspaceBrowsers"),
3050
- sessions: others,
3051
- identities: props.identities,
3052
- selectedId: props.selectedSessionId,
3053
- interventionCounts: props.interventionCounts,
3054
- onSelect: choose
3055
- }
3056
- )
3057
- ] }),
3058
- /* @__PURE__ */ jsxs2("div", { className: "mt-1 flex shrink-0 gap-1 border-t border-og-border pt-1", children: [
3059
- current.length > 0 ? /* @__PURE__ */ jsx2(MenuButton, { onClick: props.onFollow, children: t("browserViewer.followAgent") }) : null,
3060
- /* @__PURE__ */ jsxs2(MenuButton, { onClick: () => props.onCreate(), disabled: props.creating, children: [
3061
- /* @__PURE__ */ jsx2(PlusIcon, { className: "size-3.5" }),
3062
- " ",
3063
- t("browserViewer.newBrowser")
3064
- ] })
3065
- ] })
3066
- ] })
3067
- ] }),
3068
- /* @__PURE__ */ jsx2("span", { className: "min-w-0 flex-1 truncate text-og-xs text-og-fg-subtle", children: placementLabel(t, selected) }),
3069
- /* @__PURE__ */ jsx2(
3070
- BrowserProfileMenu,
3071
- {
3072
- session: selected ?? null,
3073
- identity: props.selectedProfile,
3074
- baseRevisionOrdinal: props.baseRevisionOrdinal,
3075
- history: props.profileHistory,
3076
- authConnections: props.authConnections,
3077
- saving: props.savingProfile,
3078
- onSave: props.onSaveProfile,
3079
- onUpdate: props.onUpdateProfile,
3080
- onOpenVersion: props.onOpenProfileVersion
3081
- }
3082
- ),
3083
- /* @__PURE__ */ jsx2(
3084
- "button",
3085
- {
3086
- type: "button",
3087
- onClick: props.onRefresh,
3088
- className: "grid size-7 shrink-0 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg",
3089
- "aria-label": t("browserViewer.refreshBrowsers"),
3090
- children: /* @__PURE__ */ jsx2(RefreshCwIcon, { className: cn("size-3.5", props.refreshing && "animate-spin") })
3091
- }
3092
- ),
3093
- /* @__PURE__ */ jsx2(
3094
- BrowserLaunchMenu,
3095
- {
3096
- attachedBridges: props.attachedBridges,
3097
- attachedDevices: props.attachedDevices,
3098
- identities: props.identities,
3099
- creating: props.creating,
3100
- mutatingProfile: props.savingProfile,
3101
- onCreate: props.onCreate,
3102
- onRestore: (identity) => props.onUpdateProfile(identity, { status: "active" }),
3103
- setupUrl: props.setupUrl,
3104
- extensionName: props.extensionName
3105
- }
3106
- )
3107
- ] });
3108
- }
3109
- function BrowserUnselectedPanel(props) {
3110
- const t = useOpenGeniText();
3111
- return /* @__PURE__ */ jsx2("div", { className: "grid min-h-0 flex-1 place-items-center bg-og-bg p-6 text-center", children: /* @__PURE__ */ jsxs2("div", { className: "max-w-sm", children: [
3112
- /* @__PURE__ */ jsx2("span", { className: "mx-auto grid size-10 place-items-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted", children: /* @__PURE__ */ jsx2(Globe2Icon, { className: "size-4.5" }) }),
3113
- props.unavailableBrowser ? /* @__PURE__ */ jsx2(BrowserUnavailableNotice, { session: props.unavailableBrowser }) : /* @__PURE__ */ jsx2("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: t("browserViewer.noBrowserForThisAgent") }),
3114
- /* @__PURE__ */ jsx2("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: props.peerCount === 1 ? t("browserViewer.oneWorkspaceBrowserIsAvailable") : t("browserViewer.peercountWorkspaceBrowsersAreAvailable", {
3115
- peerCount: props.peerCount
3116
- }) }),
3117
- /* @__PURE__ */ jsxs2(
3118
- "button",
3119
- {
3120
- type: "button",
3121
- disabled: props.creating,
3122
- onClick: props.onCreate,
3123
- className: "mt-4 inline-flex h-8 items-center gap-1.5 rounded-og-sm border border-og-primary-border bg-og-primary px-3 text-og-control font-medium text-og-primary-fg transition hover:bg-og-primary-hover disabled:cursor-not-allowed disabled:opacity-50",
3124
- children: [
3125
- /* @__PURE__ */ jsx2(PlusIcon, { className: "size-3.5" }),
3126
- props.creating ? t("browserViewer.opening") : t("browserViewer.newBrowser")
3127
- ]
3128
- }
3129
- )
3130
- ] }) });
3131
- }
3132
- function BrowserUnavailableNotice({ session }) {
3133
- const t = useOpenGeniText();
3134
- const reason = session.failureCode === "provider_deadline_rotation" ? t("browserViewer.theComputerRunningItReached") : session.failureCode === "source_placement_changed" ? t("browserViewer.thisChatMovedToAnother") : session.failureCode === "idle_released" ? t("browserViewer.itStoppedAfterItWent") : session.lifecycle === "failed" ? t("browserViewer.itStoppedUnexpectedly") : t("browserViewer.theConnectionToItWas");
3135
- return /* @__PURE__ */ jsxs2("div", { role: "status", children: [
3136
- /* @__PURE__ */ jsx2("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: t("browserViewer.browserUnavailable") }),
3137
- /* @__PURE__ */ jsx2("p", { className: "mt-1 break-words text-og-control leading-5 text-og-fg-muted", children: t("browserViewer.nameReasonOpenANew", { name: session.name, reason }) })
3138
- ] });
3139
- }
3140
- function BrowserSessionGroup(props) {
3141
- const t = useOpenGeniText();
3142
- if (props.sessions.length === 0) return null;
3143
- return /* @__PURE__ */ jsxs2("div", { className: "py-1", children: [
3144
- /* @__PURE__ */ jsx2("p", { className: "px-2 pb-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-fg-subtle", children: props.label }),
3145
- props.sessions.map((session) => {
3146
- const identity = props.identities.find((candidate) => candidate.id === session.identityId);
3147
- const interventionCount = props.interventionCounts.get(session.id) ?? 0;
3148
- return /* @__PURE__ */ jsxs2(
3149
- "button",
3150
- {
3151
- type: "button",
3152
- "aria-label": `${session.name}${identity ? ` \xB7 ${identity.name}` : ""} \xB7 ${placementLabel(t, session)}`,
3153
- "aria-pressed": session.id === props.selectedId,
3154
- onClick: () => props.onSelect(session.id),
3155
- className: cn(
3156
- "flex w-full items-center gap-2 rounded-og-sm px-2 py-1.5 text-left transition hover:bg-og-surface-2",
3157
- session.id === props.selectedId && "bg-og-surface-2"
3158
- ),
3159
- children: [
3160
- /* @__PURE__ */ jsx2(
3161
- "span",
3162
- {
3163
- className: cn(
3164
- "size-1.5 rounded-full",
3165
- session.lifecycle === "active" ? "bg-og-status-running" : "bg-og-fg-subtle"
3166
- )
3167
- }
3168
- ),
3169
- /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
3170
- /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-control text-og-fg", children: session.name }),
3171
- /* @__PURE__ */ jsxs2("span", { className: "block truncate text-og-xs text-og-fg-subtle", children: [
3172
- identity ? `${identity.name} \xB7 ` : "",
3173
- placementLabel(t, session)
3174
- ] })
3175
- ] }),
3176
- interventionCount > 0 ? /* @__PURE__ */ jsx2("span", { className: "rounded-full bg-og-status-waiting/12 px-1.5 py-0.5 text-[10px] font-medium text-og-status-waiting", children: interventionCount }) : null
3177
- ]
3178
- },
3179
- session.id
3180
- );
3181
- })
3182
- ] });
3183
- }
3184
- function BrowserLaunchMenu(props) {
3185
- const t = useOpenGeniText();
3186
- const detailsRef = useViewerMenuDismiss();
3187
- const choose = (choice) => {
3188
- detailsRef.current?.removeAttribute("open");
3189
- props.onCreate(choice);
3190
- };
3191
- const restore = async (identity) => {
3192
- if (await props.onRestore(identity)) detailsRef.current?.removeAttribute("open");
3193
- };
3194
- const busy = props.creating || props.mutatingProfile;
3195
- const activeIdentities = props.identities.filter((identity) => identity.status === "active");
3196
- const archivedIdentities = props.identities.filter((identity) => identity.status === "archived");
3197
- return /* @__PURE__ */ jsxs2("details", { ref: detailsRef, className: cn(props.prominent && "mt-4 inline-block"), children: [
3198
- /* @__PURE__ */ jsxs2(
3199
- "summary",
3200
- {
3201
- onClick: (event) => {
3202
- if (busy) event.preventDefault();
3203
- },
3204
- className: cn(
3205
- "cursor-pointer list-none items-center justify-center gap-1.5 text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg [&::-webkit-details-marker]:hidden",
3206
- props.prominent ? "inline-flex h-8 rounded-og-sm border border-og-border bg-og-surface-1 px-3 text-og-control font-medium text-og-fg" : "grid size-7 place-items-center rounded-og-sm",
3207
- busy && "pointer-events-none opacity-50"
3208
- ),
3209
- "aria-label": t("browserViewer.newBrowser"),
3210
- children: [
3211
- busy ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx2(PlusIcon, { className: "size-3.5" }),
3212
- props.prominent ? t("browserViewer.newBrowser") : null,
3213
- props.prominent ? /* @__PURE__ */ jsx2(ChevronDownIcon, { className: "size-3" }) : null
3214
- ]
3215
- }
3216
- ),
3217
- /* @__PURE__ */ jsxs2(
3218
- "div",
3219
- {
3220
- className: cn(
3221
- "absolute z-40 max-h-[calc(100%-3rem)] w-72 max-w-[calc(100%-1rem)] overflow-y-auto rounded-og-md border border-og-border bg-og-surface-1 p-1 text-left shadow-xl",
3222
- props.prominent ? "left-1/2 top-2 -translate-x-1/2" : "right-2 top-10"
3223
- ),
3224
- children: [
3225
- props.attachedDevices.length > 0 || props.attachedBridges.length > 0 ? /* @__PURE__ */ jsxs2("div", { className: "pb-1", children: [
3226
- /* @__PURE__ */ jsx2("p", { className: "px-2 pb-1 pt-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-fg-subtle", children: t("browserViewer.connectedChrome") }),
3227
- props.attachedDevices.map((device) => /* @__PURE__ */ jsxs2(
3228
- "button",
3229
- {
3230
- type: "button",
3231
- onClick: () => choose({ kind: "attached", device }),
3232
- className: "flex w-full items-center gap-2 rounded-og-sm px-2 py-2 text-left transition hover:bg-og-surface-2",
3233
- children: [
3234
- /* @__PURE__ */ jsx2(MonitorIcon, { className: "size-3.5 shrink-0 text-og-fg-muted" }),
3235
- /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
3236
- /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-control text-og-fg", children: device.profileLabel ?? device.name }),
3237
- /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-xs text-og-fg-subtle", children: t("browserViewer.openTabs", {
3238
- browserName: device.browserName,
3239
- count: device.tabCount
3240
- }) })
3241
- ] }),
3242
- /* @__PURE__ */ jsx2("span", { className: "size-1.5 shrink-0 rounded-full bg-og-status-running" })
3243
- ]
3244
- },
3245
- device.id
3246
- )),
3247
- props.attachedBridges.length > 0 ? props.setupUrl ? /* @__PURE__ */ jsxs2(
3248
- "a",
3249
- {
3250
- href: props.setupUrl,
3251
- target: "_blank",
3252
- rel: "noreferrer",
3253
- onClick: () => detailsRef.current?.removeAttribute("open"),
3254
- className: "flex w-full items-center gap-2 rounded-og-sm px-2 py-2 text-left transition hover:bg-og-surface-2",
3255
- children: [
3256
- /* @__PURE__ */ jsx2(PlusIcon, { className: "size-3.5 shrink-0 text-og-fg-muted" }),
3257
- /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
3258
- /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-control text-og-fg", children: props.attachedDevices.length > 0 ? t("browserViewer.connectAnotherChromeProfile") : t("browserViewer.connectThisChromeProfile") }),
3259
- /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-xs text-og-fg-subtle", children: t("browserViewer.machineConnectedChromeExtensionMissing") })
3260
- ] })
3261
- ]
3262
- }
3263
- ) : /* @__PURE__ */ jsxs2("div", { className: "flex w-full items-center gap-2 px-2 py-2 text-left", children: [
3264
- /* @__PURE__ */ jsx2(MonitorIcon, { className: "size-3.5 shrink-0 text-og-fg-muted" }),
3265
- /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
3266
- /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-control text-og-fg", children: t("browserViewer.chromeConnectionReady") }),
3267
- /* @__PURE__ */ jsx2("span", { className: "block text-og-xs leading-4 text-og-fg-subtle", children: t("browserViewer.installTheOpengeniBrowserExtension", {
3268
- extensionName: props.extensionName ?? "Opengeni Browser"
3269
- }) })
3270
- ] })
3271
- ] }) : null
3272
- ] }) : null,
3273
- /* @__PURE__ */ jsx2(
3274
- "p",
3275
- {
3276
- className: cn(
3277
- "px-2 pb-1 pt-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-fg-subtle",
3278
- (props.attachedDevices.length > 0 || props.attachedBridges.length > 0) && "border-t border-og-border"
3279
- ),
3280
- children: t("browserViewer.openIsolatedBrowser")
3281
- }
3282
- ),
3283
- /* @__PURE__ */ jsxs2(
3284
- "button",
3285
- {
3286
- type: "button",
3287
- onClick: () => choose({ kind: "clean" }),
3288
- className: "flex w-full items-center gap-2 rounded-og-sm px-2 py-2 text-left transition hover:bg-og-surface-2",
3289
- children: [
3290
- /* @__PURE__ */ jsx2(Globe2Icon, { className: "size-3.5 text-og-fg-muted" }),
3291
- /* @__PURE__ */ jsxs2("span", { children: [
3292
- /* @__PURE__ */ jsx2("span", { className: "block text-og-control text-og-fg", children: t("browserViewer.freshBrowser") }),
3293
- /* @__PURE__ */ jsx2("span", { className: "block text-og-xs text-og-fg-subtle", children: t("browserViewer.visualBrowserNoSavedState") })
3294
- ] })
3295
- ]
3296
- }
3297
- ),
3298
- activeIdentities.length > 0 ? /* @__PURE__ */ jsx2("div", { className: "mt-1 border-t border-og-border pt-1", children: activeIdentities.map((identity) => /* @__PURE__ */ jsxs2(
3299
- "button",
3300
- {
3301
- type: "button",
3302
- onClick: () => choose({ kind: "profile", identityId: identity.id }),
3303
- className: "flex w-full items-center gap-2 rounded-og-sm px-2 py-2 text-left transition hover:bg-og-surface-2",
3304
- children: [
3305
- /* @__PURE__ */ jsx2(UserRoundIcon, { className: "size-3.5 text-og-fg-muted" }),
3306
- /* @__PURE__ */ jsxs2("span", { className: "min-w-0", children: [
3307
- /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-control text-og-fg", children: identity.name }),
3308
- /* @__PURE__ */ jsx2("span", { className: "block text-og-xs text-og-fg-subtle", children: identity.defaultRevisionId ? t("browserViewer.savedVersions", { count: identity.revisionCount }) : t("browserViewer.readyForFirstSignIn") })
3309
- ] })
3310
- ]
3311
- },
3312
- identity.id
3313
- )) }) : null,
3314
- archivedIdentities.length > 0 ? /* @__PURE__ */ jsxs2("details", { className: "mt-1 border-t border-og-border pt-1", children: [
3315
- /* @__PURE__ */ jsxs2("summary", { className: "flex cursor-pointer list-none items-center gap-1.5 rounded-og-sm px-2 py-1.5 text-og-xs text-og-fg-subtle transition hover:bg-og-surface-2 hover:text-og-fg [&::-webkit-details-marker]:hidden", children: [
3316
- /* @__PURE__ */ jsx2(ArchiveIcon, { className: "size-3" }),
3317
- " ",
3318
- t("browserViewer.hiddenProfilesLength", { length: archivedIdentities.length }),
3319
- /* @__PURE__ */ jsx2(ChevronDownIcon, { className: "ml-auto size-3" })
3320
- ] }),
3321
- archivedIdentities.map((identity) => /* @__PURE__ */ jsxs2(
3322
- "button",
3323
- {
3324
- type: "button",
3325
- disabled: busy,
3326
- onClick: () => void restore(identity),
3327
- className: "flex w-full items-center gap-2 rounded-og-sm px-2 py-2 text-left transition hover:bg-og-surface-2 disabled:opacity-50",
3328
- children: [
3329
- /* @__PURE__ */ jsx2(RotateCcwIcon, { className: "size-3.5 text-og-fg-muted" }),
3330
- /* @__PURE__ */ jsx2("span", { className: "min-w-0 flex-1 truncate text-og-control text-og-fg", children: t("browserViewer.restoreName", { name: identity.name }) })
3331
- ]
3332
- },
3333
- identity.id
3334
- ))
3335
- ] }) : null
3336
- ]
3337
- }
3338
- )
3339
- ] });
3340
- }
3341
- function BrowserProfileMenu(props) {
3342
- const t = useOpenGeniText();
3343
- const detailsRef = useViewerMenuDismiss();
3344
- const nameInputId = useId();
3345
- const [name, setName] = useState9("");
3346
- const attached = props.session?.placement.kind === "attached_device";
3347
- const canSave = (props.session?.capabilities.identityPublication ?? false) && props.identity?.status !== "archived";
3348
- const revisions = props.identity && props.history?.identityId === props.identity.id ? [...props.history.revisions].sort((left, right) => right.ordinal - left.ordinal) : [];
3349
- const save = async (newProfileName) => {
3350
- if (await props.onSave(newProfileName)) {
3351
- setName("");
3352
- detailsRef.current?.removeAttribute("open");
3353
- }
3354
- };
3355
- const openVersion = (identityId, baseRevisionId) => {
3356
- detailsRef.current?.removeAttribute("open");
3357
- props.onOpenVersion(identityId, baseRevisionId);
3358
- };
3359
- const versionLabel = attached ? "live" : props.identity ? props.baseRevisionOrdinal ? `v${props.baseRevisionOrdinal}` : props.session?.baseRevisionId ? "saved" : "unsaved" : "temporary";
3360
- return /* @__PURE__ */ jsxs2("details", { ref: detailsRef, className: "min-w-0", children: [
3361
- /* @__PURE__ */ jsxs2("summary", { className: "flex h-7 max-w-40 cursor-pointer list-none items-center gap-1.5 rounded-og-sm px-2 text-og-xs text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg [&::-webkit-details-marker]:hidden", children: [
3362
- props.saving ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3 animate-spin" }) : attached ? /* @__PURE__ */ jsx2(MonitorIcon, { className: "size-3" }) : /* @__PURE__ */ jsx2(UserRoundIcon, { className: "size-3" }),
3363
- /* @__PURE__ */ jsx2("span", { className: "truncate", children: attached ? props.session?.name : props.identity?.name ?? t("browserViewer.temporary") }),
3364
- attached || props.identity ? /* @__PURE__ */ jsxs2("span", { className: "shrink-0 text-og-fg-subtle", children: [
3365
- "\xB7 ",
3366
- versionLabel
3367
- ] }) : null,
3368
- /* @__PURE__ */ jsx2(ChevronDownIcon, { className: "size-3 shrink-0 text-og-fg-subtle" })
3369
- ] }),
3370
- /* @__PURE__ */ jsxs2("div", { className: "absolute right-2 top-10 z-40 max-h-[calc(100%-3rem)] w-72 max-w-[calc(100%-1rem)] overflow-y-auto rounded-og-md border border-og-border bg-og-surface-1 p-3 shadow-xl", children: [
3371
- /* @__PURE__ */ jsxs2("div", { className: "flex items-start gap-2", children: [
3372
- /* @__PURE__ */ jsx2("span", { className: "grid size-7 shrink-0 place-items-center rounded-og-sm bg-og-surface-2 text-og-fg-muted", children: attached ? /* @__PURE__ */ jsx2(MonitorIcon, { className: "size-3.5" }) : /* @__PURE__ */ jsx2(UserRoundIcon, { className: "size-3.5" }) }),
3373
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
3374
- /* @__PURE__ */ jsx2("p", { className: "truncate text-og-control font-medium text-og-fg", children: attached ? props.session?.name : props.identity?.name ?? t("browserViewer.temporaryBrowser") }),
3375
- /* @__PURE__ */ jsx2("p", { className: "mt-0.5 text-og-xs leading-4 text-og-fg-subtle", children: attached ? t("browserViewer.thisSessionDrivesTheExisting") : props.identity?.status === "archived" ? t("browserViewer.hiddenFromNewBrowsersThis") : props.identity ? props.baseRevisionOrdinal ? t("browserViewer.startedFromVersionBaserevisionordinal", {
3376
- baseRevisionOrdinal: props.baseRevisionOrdinal
3377
- }) : t("browserViewer.notSavedYet") : t("browserViewer.thisBrowserIsNotReusable") })
3378
- ] })
3379
- ] }),
3380
- props.identity && revisions.length > 0 ? /* @__PURE__ */ jsxs2("div", { className: "mt-3 border-t border-og-border pt-2", children: [
3381
- /* @__PURE__ */ jsx2("p", { className: "px-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-fg-subtle", children: t("browserViewer.savedVersions2") }),
3382
- /* @__PURE__ */ jsx2("div", { className: "mt-1 max-h-40 overflow-y-auto", children: revisions.map((revision) => {
3383
- const isDefault = revision.id === props.identity?.defaultRevisionId;
3384
- const isCurrent = revision.ordinal === props.baseRevisionOrdinal;
3385
- const materialization = revisionMaterializationSummary(t, revision);
3386
- return /* @__PURE__ */ jsxs2(
3387
- "div",
3388
- {
3389
- className: "flex w-full items-center gap-1 rounded-og-sm transition hover:bg-og-surface-2",
3390
- children: [
3391
- /* @__PURE__ */ jsxs2(
3392
- "button",
3393
- {
3394
- type: "button",
3395
- disabled: props.saving || props.identity?.status === "archived",
3396
- "aria-label": t("browserViewer.openNameVersionOrdinal", {
3397
- name: props.identity.name,
3398
- ordinal: revision.ordinal
3399
- }),
3400
- onClick: () => openVersion(props.identity.id, revision.id),
3401
- className: "flex min-w-0 flex-1 items-center gap-2 px-2 py-1.5 text-left disabled:opacity-50",
3402
- children: [
3403
- /* @__PURE__ */ jsx2("span", { className: "grid size-4 place-items-center text-og-fg-muted", children: isCurrent ? /* @__PURE__ */ jsx2(CheckIcon, { className: "size-3.5 text-og-accent" }) : null }),
3404
- /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
3405
- /* @__PURE__ */ jsx2("span", { className: "block text-og-control text-og-fg", children: t("browserViewer.versionOrdinal", { ordinal: revision.ordinal }) }),
3406
- /* @__PURE__ */ jsx2("span", { className: "block truncate text-[10px] text-og-fg-subtle", children: materialization.label })
3407
- ] }),
3408
- isCurrent ? /* @__PURE__ */ jsx2("span", { className: "text-[10px] text-og-fg-subtle", children: t("browserViewer.current") }) : null
3409
- ]
3410
- }
3411
- ),
3412
- /* @__PURE__ */ jsx2(
3413
- "button",
3414
- {
3415
- type: "button",
3416
- disabled: isDefault || props.saving || props.identity?.status === "archived",
3417
- "aria-label": isDefault ? t("browserViewer.nameVersionOrdinalIsThe", {
3418
- name: props.identity.name,
3419
- ordinal: revision.ordinal
3420
- }) : t("browserViewer.useNameVersionOrdinalBy", {
3421
- name: props.identity.name,
3422
- ordinal: revision.ordinal
3423
- }),
3424
- title: isDefault ? t("browserViewer.defaultVersion") : t("browserViewer.useByDefault"),
3425
- onClick: () => void props.onUpdate(props.identity, { defaultRevisionId: revision.id }),
3426
- className: "mr-1 shrink-0 rounded-og-sm px-2 py-1 text-[10px] text-og-fg-subtle transition hover:bg-og-surface-3 hover:text-og-fg disabled:cursor-default disabled:opacity-70",
3427
- children: isDefault ? t("browserViewer.default") : t("browserViewer.makeDefault")
3428
- }
3429
- )
3430
- ]
3431
- },
3432
- revision.id
3433
- );
3434
- }) }),
3435
- /* @__PURE__ */ jsx2("p", { className: "mt-1 px-2 text-[10px] leading-4 text-og-fg-subtle", children: t("browserViewer.changingTheDefaultAffectsOnly") })
3436
- ] }) : props.identity && props.history?.identityId === props.identity.id && props.history.loading ? /* @__PURE__ */ jsxs2("p", { className: "mt-3 flex items-center gap-1.5 border-t border-og-border px-2 pt-2 text-og-xs text-og-fg-subtle", children: [
3437
- /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3 animate-spin" }),
3438
- " ",
3439
- t("browserViewer.loadingSavedVersions")
3440
- ] }) : null,
3441
- props.identity && props.authConnections.length > 0 ? /* @__PURE__ */ jsxs2("div", { className: "mt-3 border-t border-og-border pt-2", children: [
3442
- /* @__PURE__ */ jsx2("p", { className: "px-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-fg-subtle", children: t("browserViewer.loginHealth") }),
3443
- /* @__PURE__ */ jsx2("div", { className: "mt-1 space-y-1", children: props.authConnections.map((connection) => {
3444
- const health = siteAuthHealth(t, connection);
3445
- return /* @__PURE__ */ jsxs2(
3446
- "div",
3447
- {
3448
- className: "flex items-start gap-2 rounded-og-sm px-2 py-1.5",
3449
- children: [
3450
- /* @__PURE__ */ jsx2("span", { className: cn("mt-1 size-1.5 shrink-0 rounded-full", health.dotClass) }),
3451
- /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
3452
- /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-control text-og-fg", children: connection.name }),
3453
- /* @__PURE__ */ jsxs2("span", { className: "block truncate text-[10px] text-og-fg-subtle", children: [
3454
- connection.accountLabel,
3455
- " \xB7 ",
3456
- health.label
3457
- ] })
3458
- ] })
3459
- ]
3460
- },
3461
- connection.id
3462
- );
3463
- }) })
3464
- ] }) : null,
3465
- canSave ? props.identity ? /* @__PURE__ */ jsxs2(
3466
- "button",
3467
- {
3468
- type: "button",
3469
- onClick: () => void save(),
3470
- disabled: props.saving,
3471
- className: "mt-3 flex h-8 w-full items-center justify-center gap-1.5 rounded-og-sm border border-og-border bg-og-surface-2 px-3 text-og-control font-medium text-og-fg transition hover:bg-og-surface-3 disabled:opacity-50",
3472
- children: [
3473
- props.saving ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx2(SaveIcon, { className: "size-3.5" }),
3474
- props.saving ? t("browserViewer.savingBrowserVersion") : t("browserViewer.saveNewVersion")
3475
- ]
3476
- }
3477
- ) : /* @__PURE__ */ jsxs2(
3478
- "form",
3479
- {
3480
- className: "mt-3",
3481
- onSubmit: (event) => {
3482
- event.preventDefault();
3483
- if (name.trim()) void save(name);
3484
- },
3485
- children: [
3486
- /* @__PURE__ */ jsx2("label", { className: "text-og-xs font-medium text-og-fg-muted", htmlFor: nameInputId, children: t("browserViewer.profileName") }),
3487
- /* @__PURE__ */ jsxs2("div", { className: "mt-1 flex gap-1.5", children: [
3488
- /* @__PURE__ */ jsx2(
3489
- "input",
3490
- {
3491
- id: nameInputId,
3492
- value: name,
3493
- onInput: (event) => setName(event.currentTarget.value),
3494
- maxLength: 200,
3495
- placeholder: t("browserViewer.work"),
3496
- className: "h-8 min-w-0 flex-1 rounded-og-sm border border-og-border bg-og-bg px-2 text-og-control text-og-fg placeholder:text-og-fg-subtle focus:border-og-accent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent"
3497
- }
3498
- ),
3499
- /* @__PURE__ */ jsxs2(
3500
- "button",
3501
- {
3502
- type: "submit",
3503
- disabled: !name.trim() || props.saving,
3504
- className: "inline-flex h-8 items-center gap-1 rounded-og-sm border border-og-primary-border bg-og-primary text-og-primary-fg px-2.5 text-og-control font-medium transition hover:bg-og-primary-hover disabled:opacity-50",
3505
- children: [
3506
- props.saving ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx2(SaveIcon, { className: "size-3.5" }),
3507
- t("browserViewer.save")
3508
- ]
3509
- }
3510
- )
3511
- ] })
3512
- ]
3513
- }
3514
- ) : /* @__PURE__ */ jsx2("p", { className: "mt-3 rounded-og-sm bg-og-surface-2 px-2.5 py-2 text-og-xs text-og-fg-subtle", children: attached ? t("browserViewer.chromeKeepsThisProfileS") : props.identity?.status === "archived" ? t("browserViewer.restoreThisProfileBeforeSaving") : t("browserViewer.thisBrowserCannotSaveReusable") }),
3515
- !attached && props.identity ? /* @__PURE__ */ jsxs2(
3516
- "button",
3517
- {
3518
- type: "button",
3519
- disabled: props.saving,
3520
- onClick: () => void props.onUpdate(props.identity, {
3521
- status: props.identity.status === "active" ? "archived" : "active"
3522
- }),
3523
- className: "mt-2 flex h-8 w-full items-center justify-center gap-1.5 rounded-og-sm px-3 text-og-control text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50",
3524
- children: [
3525
- props.identity.status === "active" ? /* @__PURE__ */ jsx2(ArchiveIcon, { className: "size-3.5" }) : /* @__PURE__ */ jsx2(RotateCcwIcon, { className: "size-3.5" }),
3526
- props.identity.status === "active" ? t("browserViewer.hideFromNewBrowsers") : t("browserViewer.restoreProfile")
3527
- ]
3528
- }
3529
- ) : null,
3530
- canSave ? /* @__PURE__ */ jsx2("p", { className: "mt-2 text-[10px] leading-4 text-og-fg-subtle", children: t("browserViewer.savingRestartsThisBrowserSave") }) : null
3531
- ] })
3532
- ] });
3533
- }
3534
- function revisionMaterializationSummary(t, revision) {
3535
- const materializations = revision.components.map((component) => component.materialization);
3536
- const placementBound = materializations.find(
3537
- (materialization) => materialization.portability === "placement_bound"
3538
- );
3539
- if (placementBound) {
3540
- return {
3541
- label: placementBound.reason ? t("browserViewer.thisMachineOnlyReason", { reason: placementBound.reason }) : t("browserViewer.thisMachineOnly")
3542
- };
3543
- }
3544
- const providerBound = materializations.find(
3545
- (materialization) => materialization.portability === "provider_bound"
3546
- );
3547
- if (providerBound) {
3548
- return {
3549
- label: providerBound.reason ? t("browserViewer.providerBoundReason", { reason: providerBound.reason }) : t("browserViewer.providerBound")
3550
- };
3551
- }
3552
- return { label: t("browserViewer.portableBrowserData") };
3553
- }
3554
- function siteAuthHealth(t, connection) {
3555
- switch (connection.verificationState) {
3556
- case "verified":
3557
- return { label: t("browserViewer.verified"), dotClass: "bg-og-status-running" };
3558
- case "needs_repair":
3559
- return { label: t("browserViewer.signInNeedsAttention"), dotClass: "bg-og-status-waiting" };
3560
- case "failed":
3561
- return { label: t("browserViewer.verificationFailed"), dotClass: "bg-og-status-failed" };
3562
- case "unknown":
3563
- return { label: t("browserViewer.notCheckedYet"), dotClass: "bg-og-fg-subtle" };
3564
- }
3565
- }
3566
- function MenuButton(props) {
3567
- return /* @__PURE__ */ jsx2(
3568
- "button",
3569
- {
3570
- type: "button",
3571
- onClick: props.onClick,
3572
- disabled: props.disabled,
3573
- className: "flex h-7 flex-1 items-center justify-center gap-1 rounded-og-sm px-2 text-og-control text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50",
3574
- children: props.children
3575
- }
3576
- );
3577
- }
3578
- function BrowserTabs(props) {
3579
- const t = useOpenGeniText();
3580
- const pages = props.targets.filter((target) => target.kind === "page" || target.kind === "popup");
3581
- return /* @__PURE__ */ jsxs2("div", { className: "flex h-9 shrink-0 items-end gap-px overflow-x-auto border-b border-og-border bg-og-surface-1 px-1 pt-1", children: [
3582
- pages.map((target) => /* @__PURE__ */ jsxs2(
3583
- "div",
3584
- {
3585
- className: cn(
3586
- "group flex h-8 min-w-24 max-w-48 items-center rounded-t-og-sm border border-b-0 border-transparent px-2",
3587
- target.id === props.selectedTargetId ? "border-og-border bg-og-bg text-og-fg" : "text-og-fg-muted hover:bg-og-surface-1"
3588
- ),
3589
- children: [
3590
- /* @__PURE__ */ jsx2(
3591
- "button",
3592
- {
3593
- type: "button",
3594
- onClick: () => props.onSelect(target.id),
3595
- className: "min-w-0 flex-1 truncate text-left text-og-control",
3596
- children: target.title || shortUrl(target.url)
3597
- }
3598
- ),
3599
- props.tabControl ? /* @__PURE__ */ jsx2(
3600
- "button",
3601
- {
3602
- type: "button",
3603
- onClick: () => props.onClose(target.id),
3604
- disabled: props.mutating,
3605
- className: "ml-1 grid size-4 shrink-0 place-items-center rounded opacity-0 transition hover:bg-og-surface-3 group-hover:opacity-100 focus:opacity-100 disabled:opacity-30",
3606
- "aria-label": t("browserViewer.closeTitle", { title: target.title || "tab" }),
3607
- children: /* @__PURE__ */ jsx2(XIcon, { className: "size-3" })
3608
- }
3609
- ) : null
3610
- ]
3611
- },
3612
- target.id
3613
- )),
3614
- props.tabControl ? /* @__PURE__ */ jsx2(
3615
- "button",
3616
- {
3617
- type: "button",
3618
- onClick: props.onOpen,
3619
- disabled: props.mutating,
3620
- className: "mb-1 grid size-6 shrink-0 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-40",
3621
- "aria-label": t("browserViewer.newTab"),
3622
- children: /* @__PURE__ */ jsx2(PlusIcon, { className: "size-3.5" })
3623
- }
3624
- ) : null
3625
- ] });
3626
- }
3627
- function BrowserAddressBar(props) {
3628
- const t = useOpenGeniText();
3629
- const [draft, setDraft] = useState9(props.target?.url ?? "");
3630
- const focusedRef = useRef9(false);
3631
- const historyPendingRef = useRef9(false);
3632
- const [historyPending, setHistoryPending] = useState9(false);
3633
- useEffect9(() => {
3634
- if (!focusedRef.current) setDraft(props.target?.url ?? "");
3635
- }, [props.target?.id, props.target?.url]);
3636
- const submit = (event) => {
3637
- event.preventDefault();
3638
- if (historyPendingRef.current) return;
3639
- const normalized = normalizeBrowserAddress(draft);
3640
- if (normalized) props.onNavigate(normalized);
3641
- };
3642
- const navigateHistory = async (direction) => {
3643
- if (!props.target || props.loading || historyPendingRef.current) return;
3644
- historyPendingRef.current = true;
3645
- setHistoryPending(true);
3646
- try {
3647
- await props.onHistory(direction);
3648
- } finally {
3649
- historyPendingRef.current = false;
3650
- setHistoryPending(false);
3651
- }
3652
- };
3653
- return /* @__PURE__ */ jsxs2(
3654
- "form",
3655
- {
3656
- onSubmit: submit,
3657
- "aria-busy": historyPending || void 0,
3658
- className: "flex h-10 shrink-0 items-center gap-1.5 border-b border-og-border bg-og-bg px-2",
3659
- children: [
3660
- ["back", "forward"].map((direction) => /* @__PURE__ */ jsx2(
3661
- "button",
3662
- {
3663
- type: "button",
3664
- onClick: () => void navigateHistory(direction),
3665
- disabled: !props.target || props.loading,
3666
- "aria-disabled": historyPending || void 0,
3667
- className: cn(
3668
- "grid size-7 shrink-0 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent disabled:opacity-35",
3669
- historyPending && "opacity-50"
3670
- ),
3671
- "aria-label": direction === "back" ? t("browserViewer.back") : t("browserViewer.forward"),
3672
- title: direction === "back" ? t("browserViewer.back") : t("browserViewer.forward"),
3673
- children: direction === "back" ? /* @__PURE__ */ jsx2(ArrowLeftIcon, { className: "size-3.5" }) : /* @__PURE__ */ jsx2(ArrowRightIcon, { className: "size-3.5" })
3674
- },
3675
- direction
3676
- )),
3677
- /* @__PURE__ */ jsx2(
3678
- "button",
3679
- {
3680
- type: "button",
3681
- onClick: props.onReload,
3682
- disabled: !props.target || props.loading || historyPending,
3683
- className: "grid size-7 shrink-0 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-35",
3684
- "aria-label": t("browserViewer.reload"),
3685
- children: /* @__PURE__ */ jsx2(
3686
- RefreshCwIcon,
3687
- {
3688
- className: cn("size-3.5", (props.loading || historyPending) && "animate-spin")
3689
- }
3690
- )
3691
- }
3692
- ),
3693
- /* @__PURE__ */ jsxs2("div", { className: "flex h-7 min-w-0 flex-1 items-center gap-2 rounded-og-sm border border-og-border bg-og-surface-1 px-2 focus-within:border-og-accent/60", children: [
3694
- /* @__PURE__ */ jsx2(Globe2Icon, { className: "size-3 shrink-0 text-og-fg-subtle" }),
3695
- /* @__PURE__ */ jsx2(
3696
- "input",
3697
- {
3698
- value: draft,
3699
- onChange: (event) => setDraft(event.target.value),
3700
- onFocus: () => {
3701
- focusedRef.current = true;
3702
- },
3703
- onBlur: () => {
3704
- focusedRef.current = false;
3705
- },
3706
- onKeyDown: (event) => {
3707
- if (event.key !== "Enter") return;
3708
- event.preventDefault();
3709
- if (historyPendingRef.current) return;
3710
- const normalized = normalizeBrowserAddress(draft);
3711
- if (normalized) props.onNavigate(normalized);
3712
- },
3713
- disabled: !props.target,
3714
- className: "min-w-0 flex-1 bg-transparent text-og-control text-og-fg placeholder:text-og-fg-subtle focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent disabled:opacity-50",
3715
- "aria-label": t("browserViewer.address"),
3716
- spellCheck: false,
3717
- placeholder: t("browserViewer.searchOrEnterAddress")
3718
- }
3719
- )
3720
- ] })
3721
- ]
3722
- }
3723
- );
3724
- }
3725
- function BrowserViewport(props) {
3726
- const t = useOpenGeniText();
3727
- const canvasRef = useRef9(null);
3728
- const inputRef = useRef9(null);
3729
- const [selectPopup, setSelectPopup] = useState9(null);
3730
- const inputSequenceRef = useRef9(0);
3731
- const dismissSelectPopup = useCallback8(() => {
3732
- inputSequenceRef.current += 1;
3733
- setSelectPopup({ observation: null, anchor: null });
3734
- }, []);
3735
- const composingRef = useRef9(false);
3736
- const pointerStartRef = useRef9(null);
3737
- const lastClickRef = useRef9(null);
3738
- const wheelRef = useRef9(null);
3739
- const pendingTextRef = useRef9(null);
3740
- const actionRef = useRef9(props.onAction);
3741
- const readClipboardRef = useRef9(props.onReadClipboard);
3742
- const observeInputRef = useRef9(props.onObserveForInput);
3743
- const errorRef = useRef9(props.onError);
3744
- const actionTailRef = useRef9(Promise.resolve());
3745
- const actionQueueEpochRef = useRef9(0);
3746
- const queuedTypingRef = useRef9(null);
3747
- const queuedFrameRef = useRef9(null);
3748
- const currentFrameRef = useRef9(props.frame);
3749
- const paintedFrameRef = useRef9(null);
3750
- const [paintedFrame, setPaintedFrame] = useState9(null);
3751
- const decodingFrameRef = useRef9(false);
3752
- const mountedRef = useRef9(true);
3753
- actionRef.current = props.onAction;
3754
- readClipboardRef.current = props.onReadClipboard;
3755
- observeInputRef.current = props.onObserveForInput;
3756
- errorRef.current = props.onError;
3757
- const clearBufferedInput = useCallback8(() => {
3758
- dismissSelectPopup();
3759
- if (wheelRef.current?.timer) clearTimeout(wheelRef.current.timer);
3760
- if (pendingTextRef.current?.timer) clearTimeout(pendingTextRef.current.timer);
3761
- wheelRef.current = null;
3762
- pendingTextRef.current = null;
3763
- queuedTypingRef.current = null;
3764
- pointerStartRef.current = null;
3765
- lastClickRef.current = null;
3766
- composingRef.current = false;
3767
- if (inputRef.current) inputRef.current.value = "";
3768
- }, [dismissSelectPopup]);
3769
- const paintQueuedFrames = useCallback8(() => {
3770
- if (decodingFrameRef.current) return;
3771
- decodingFrameRef.current = true;
3772
- void (async () => {
3773
- try {
3774
- while (mountedRef.current) {
3775
- const frame = queuedFrameRef.current;
3776
- queuedFrameRef.current = null;
3777
- if (!frame) break;
3778
- let objectUrl = null;
3779
- try {
3780
- const blob = new Blob([frame.data], {
3781
- type: frame.mediaType
3782
- });
3783
- if (typeof createImageBitmap === "function") {
3784
- const bitmap = await createImageBitmap(blob);
3785
- try {
3786
- const canvas2 = canvasRef.current;
3787
- if (mountedRef.current && canvas2 && currentFrameRef.current && sameBrowserDocument(frame, currentFrameRef.current)) {
3788
- paintCanvas(canvas2, bitmap, frame.width, frame.height);
3789
- paintedFrameRef.current = frame;
3790
- setPaintedFrame(frame);
3791
- }
3792
- } finally {
3793
- bitmap.close();
3794
- }
3795
- continue;
3796
- }
3797
- objectUrl = URL.createObjectURL(blob);
3798
- const image = await loadImage(objectUrl);
3799
- const canvas = canvasRef.current;
3800
- if (mountedRef.current && canvas && currentFrameRef.current && sameBrowserDocument(frame, currentFrameRef.current)) {
3801
- paintCanvas(canvas, image, frame.width, frame.height);
3802
- paintedFrameRef.current = frame;
3803
- setPaintedFrame(frame);
3804
- }
3805
- } catch (cause) {
3806
- if (mountedRef.current && currentFrameRef.current && sameBrowserDocument(frame, currentFrameRef.current))
3807
- errorRef.current(cause);
3808
- } finally {
3809
- if (objectUrl) URL.revokeObjectURL(objectUrl);
3810
- }
3811
- }
3812
- } finally {
3813
- decodingFrameRef.current = false;
3814
- if (mountedRef.current && queuedFrameRef.current) paintQueuedFrames();
3815
- }
3816
- })();
3817
- }, []);
3818
- useLayoutEffect2(() => {
3819
- const keyboardInput = inputRef.current;
3820
- const focusHandoff = props.focusHandoffRef;
3821
- mountedRef.current = true;
3822
- if (focusHandoff.current === props.focusScope) {
3823
- keyboardInput?.focus({ preventScroll: true });
3824
- }
3825
- focusHandoff.current = null;
3826
- return () => {
3827
- if (document.activeElement === keyboardInput) {
3828
- focusHandoff.current = props.focusScope;
3829
- }
3830
- mountedRef.current = false;
3831
- actionQueueEpochRef.current += 1;
3832
- queuedFrameRef.current = null;
3833
- clearBufferedInput();
3834
- };
3835
- }, [clearBufferedInput, props.focusHandoffRef, props.focusScope]);
3836
- useLayoutEffect2(() => {
3837
- if (currentFrameRef.current && !props.frame) {
3838
- actionQueueEpochRef.current += 1;
3839
- clearBufferedInput();
3840
- }
3841
- currentFrameRef.current = props.frame;
3842
- if (!props.frame) {
3843
- queuedFrameRef.current = null;
3844
- paintedFrameRef.current = null;
3845
- setPaintedFrame(null);
3846
- return;
3847
- }
3848
- queuedFrameRef.current = props.frame;
3849
- paintQueuedFrames();
3850
- }, [clearBufferedInput, paintQueuedFrames, props.frame]);
3851
- const enqueue = useCallback8(
3852
- (action, frame, after, selectAnchor) => {
3853
- const inputSequence = ++inputSequenceRef.current;
3854
- setSelectPopup({ observation: null, anchor: null });
3855
- frame = frame ? {
3856
- browserSessionId: frame.browserSessionId,
3857
- controllerGeneration: frame.controllerGeneration,
3858
- targetId: frame.targetId,
3859
- targetGeneration: frame.targetGeneration,
3860
- documentGeneration: frame.documentGeneration,
3861
- frameId: frame.frameId
3862
- } : null;
3863
- const epoch = actionQueueEpochRef.current;
3864
- const attachment = props.inputBatchAttachment;
3865
- const canBatchTyping = action.type === "type" && !after && frame && attachment?.fencedInputBatches === true && attachment.browserSessionId === frame.browserSessionId && attachment.controllerGeneration === frame.controllerGeneration && attachment.targetId === frame.targetId && Date.parse(attachment.expiresAt) > Date.now();
3866
- const previous = queuedTypingRef.current;
3867
- if (canBatchTyping && frame && previous && previous.epoch === epoch && sameFrameFence(previous.frame, frame) && previous.actions.length < 16) {
3868
- previous.actions.push(action);
3869
- return;
3870
- }
3871
- const typing = canBatchTyping && frame ? { actions: [action], frame, epoch } : null;
3872
- queuedTypingRef.current = typing;
3873
- actionTailRef.current = actionTailRef.current.catch(() => void 0).then(async () => {
3874
- if (!mountedRef.current || epoch !== actionQueueEpochRef.current) return;
3875
- if (queuedTypingRef.current === typing) queuedTypingRef.current = null;
3876
- const dispatchedAction = typing && typing.actions.length > 1 ? { type: "batch", actions: typing.actions, fenceEachAction: true } : action;
3877
- const receipt = await actionRef.current(dispatchedAction, frame);
3878
- if (!mountedRef.current || epoch !== actionQueueEpochRef.current) return;
3879
- const keyboardSelect = dispatchedAction.type === "press" && dispatchedAction.key === "Alt+ArrowDown";
3880
- if ((selectAnchor || keyboardSelect) && inputSequence === inputSequenceRef.current) {
3881
- const observation = keyboardSelect ? await observeInputRef.current() : receipt.observation;
3882
- if (mountedRef.current && epoch === actionQueueEpochRef.current && inputSequence === inputSequenceRef.current && observation) {
3883
- setSelectPopup({ observation, anchor: selectAnchor ?? null });
3884
- }
3885
- }
3886
- await after?.(receipt);
3887
- }).catch((cause) => {
3888
- if (!mountedRef.current || epoch !== actionQueueEpochRef.current) return;
3889
- actionQueueEpochRef.current += 1;
3890
- clearBufferedInput();
3891
- errorRef.current(cause);
3892
- });
3893
- },
3894
- [clearBufferedInput, props.inputBatchAttachment]
3895
- );
3896
- const point = useCallback8(
3897
- (frame, clientX, clientY) => browserPoint(canvasRef.current, frame, clientX, clientY),
3898
- []
3899
- );
3900
- const flushPendingText = useCallback8(() => {
3901
- const pending = pendingTextRef.current;
3902
- if (!pending) return;
3903
- clearTimeout(pending.timer);
3904
- pendingTextRef.current = null;
3905
- enqueue({ type: "type", text: pending.text }, pending.frame);
3906
- }, [enqueue]);
3907
- const flushPendingClick = useCallback8(() => {
3908
- lastClickRef.current = null;
3909
- }, []);
3910
- const flushPendingWheel = useCallback8(() => {
3911
- const batch = wheelRef.current;
3912
- if (!batch) return;
3913
- if (batch.timer) clearTimeout(batch.timer);
3914
- wheelRef.current = null;
3915
- enqueue(
3916
- {
3917
- type: "pointer",
3918
- action: "scroll",
3919
- x: batch.x,
3920
- y: batch.y,
3921
- deltaX: batch.deltaX,
3922
- deltaY: batch.deltaY
3923
- },
3924
- batch.frame
3925
- );
3926
- }, [enqueue]);
3927
- const pointerDown = (event) => {
3928
- const frame = paintedFrameRef.current;
3929
- if (!frame || event.button !== 0) return;
3930
- event.preventDefault();
3931
- dismissSelectPopup();
3932
- flushPendingWheel();
3933
- flushPendingText();
3934
- pointerStartRef.current = {
3935
- x: event.clientX,
3936
- y: event.clientY,
3937
- pointerId: event.pointerId,
3938
- frame
3939
- };
3940
- event.currentTarget.setPointerCapture?.(event.pointerId);
3941
- inputRef.current?.focus({ preventScroll: true });
3942
- };
3943
- const pointerUp = (event) => {
3944
- const start = pointerStartRef.current;
3945
- pointerStartRef.current = null;
3946
- if (!start || start.pointerId !== event.pointerId) return;
3947
- const from = point(start.frame, start.x, start.y);
3948
- const to = point(start.frame, event.clientX, event.clientY);
3949
- if (!from || !to) return;
3950
- if (Math.hypot(event.clientX - start.x, event.clientY - start.y) > 5) {
3951
- flushPendingClick();
3952
- enqueue(
3953
- {
3954
- type: "pointer",
3955
- action: "drag",
3956
- x: from.x,
3957
- y: from.y,
3958
- endX: to.x,
3959
- endY: to.y
3960
- },
3961
- start.frame
3962
- );
3963
- return;
3964
- }
3965
- const now = Date.now();
3966
- const previous = lastClickRef.current;
3967
- if (previous && now - previous.at < 280 && Math.hypot(previous.x - to.x, previous.y - to.y) < 6 && sameFrameFence(previous.frame, start.frame)) {
3968
- lastClickRef.current = null;
3969
- enqueue({ type: "pointer", action: "click", clickCount: 2, x: to.x, y: to.y }, start.frame);
3970
- return;
3971
- }
3972
- lastClickRef.current = { at: now, x: to.x, y: to.y, frame: start.frame };
3973
- const viewportBounds = canvasRef.current?.parentElement?.getBoundingClientRect();
3974
- enqueue(
3975
- { type: "pointer", action: "click", x: to.x, y: to.y },
3976
- start.frame,
3977
- void 0,
3978
- viewportBounds ? { x: event.clientX - viewportBounds.left, y: event.clientY - viewportBounds.top + 8 } : void 0
3979
- );
3980
- };
3981
- const contextMenu = (event) => {
3982
- event.preventDefault();
3983
- const frame = paintedFrameRef.current;
3984
- if (!frame) return;
3985
- flushPendingWheel();
3986
- flushPendingText();
3987
- flushPendingClick();
3988
- const at = point(frame, event.clientX, event.clientY);
3989
- if (at) enqueue({ type: "pointer", action: "click", x: at.x, y: at.y, button: "right" }, frame);
3990
- };
3991
- const wheel = (event) => {
3992
- dismissSelectPopup();
3993
- const frame = paintedFrameRef.current;
3994
- if (!frame) return;
3995
- const at = point(frame, event.clientX, event.clientY);
3996
- if (!at) return;
3997
- flushPendingText();
3998
- flushPendingClick();
3999
- event.preventDefault();
4000
- let pending = wheelRef.current;
4001
- if (pending && (!sameBrowserDocument(pending.frame, frame) || Math.hypot(pending.x - at.x, pending.y - at.y) > 6)) {
4002
- flushPendingWheel();
4003
- pending = null;
4004
- }
4005
- wheelRef.current = {
4006
- x: at.x,
4007
- y: at.y,
4008
- deltaX: (pending?.deltaX ?? 0) + event.deltaX,
4009
- deltaY: (pending?.deltaY ?? 0) + event.deltaY,
4010
- frame,
4011
- // Retain the first event's deadline so a continuous gesture keeps moving.
4012
- timer: pending?.timer ?? setTimeout(flushPendingWheel, 45)
4013
- };
4014
- };
4015
- const keyDown = (event) => {
4016
- if (composingRef.current || event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) {
4017
- return;
4018
- }
4019
- dismissSelectPopup();
4020
- flushPendingWheel();
4021
- const command = event.metaKey || event.ctrlKey;
4022
- if (props.clipboardEnabled && command && !event.altKey && ["c", "v"].includes(event.key.toLowerCase())) {
4023
- flushPendingText();
4024
- return;
4025
- }
4026
- const key = browserKey(event);
4027
- if (!key) return;
4028
- event.preventDefault();
4029
- flushPendingText();
4030
- flushPendingClick();
4031
- enqueue({ type: "press", key }, paintedFrameRef.current);
4032
- };
4033
- const finishCopy = useCallback8(async () => {
4034
- const clipboard = await readClipboardRef.current();
4035
- if (!mountedRef.current) return;
4036
- if (clipboard.text.length === 0) return;
4037
- if (!await copyTextToClipboard(clipboard.text)) {
4038
- throw new Error(t("browserViewer.browserTextCouldNotBe"));
4039
- }
4040
- }, [t]);
4041
- const copy = (event) => {
4042
- if (!props.clipboardEnabled) return;
4043
- event.preventDefault();
4044
- flushPendingWheel();
4045
- flushPendingText();
4046
- flushPendingClick();
4047
- enqueue({ type: "clipboard", operation: "copy" }, paintedFrameRef.current, finishCopy);
4048
- };
4049
- const paste = (event) => {
4050
- if (!props.clipboardEnabled) return;
4051
- event.preventDefault();
4052
- flushPendingWheel();
4053
- flushPendingText();
4054
- flushPendingClick();
4055
- enqueue(
4056
- {
4057
- type: "clipboard",
4058
- operation: "paste",
4059
- text: event.clipboardData.getData("text/plain")
4060
- },
4061
- paintedFrameRef.current
4062
- );
4063
- };
4064
- const input = (value, nativeComposing = false) => {
4065
- dismissSelectPopup();
4066
- if (composingRef.current || nativeComposing) return;
4067
- if (!value) return;
4068
- flushPendingWheel();
4069
- flushPendingClick();
4070
- const pending = pendingTextRef.current;
4071
- if (pending && sameOptionalBrowserFrame(pending.frame, paintedFrameRef.current)) {
4072
- clearTimeout(pending.timer);
4073
- pending.text += value;
4074
- pending.timer = setTimeout(flushPendingText, 16);
4075
- } else {
4076
- flushPendingText();
4077
- pendingTextRef.current = {
4078
- text: value,
4079
- frame: paintedFrameRef.current,
4080
- timer: setTimeout(flushPendingText, 16)
4081
- };
4082
- }
4083
- if (inputRef.current) inputRef.current.value = "";
4084
- };
4085
- const compositionStart = () => {
4086
- composingRef.current = true;
4087
- };
4088
- const compositionEnd = (event) => {
4089
- composingRef.current = false;
4090
- input(event.currentTarget.value || event.data);
4091
- };
4092
- const showCanvas = !props.controlUnavailable && props.frame !== null && paintedFrame !== null && sameBrowserDocument(props.frame, paintedFrame);
4093
- return /* @__PURE__ */ jsxs2("div", { className: "relative min-h-0 flex-1 overflow-hidden bg-black", children: [
4094
- /* @__PURE__ */ jsx2(
4095
- "canvas",
4096
- {
4097
- ref: canvasRef,
4098
- className: cn(
4099
- "absolute inset-0 m-auto max-h-full max-w-full touch-none focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent",
4100
- !showCanvas && "invisible"
4101
- ),
4102
- onPointerDown: pointerDown,
4103
- onPointerUp: pointerUp,
4104
- onPointerCancel: () => {
4105
- pointerStartRef.current = null;
4106
- },
4107
- onContextMenu: contextMenu,
4108
- onWheel: wheel,
4109
- "aria-label": t("browserViewer.interactiveBrowserPage")
4110
- }
4111
- ),
4112
- /* @__PURE__ */ jsx2(
4113
- "textarea",
4114
- {
4115
- ref: inputRef,
4116
- disabled: props.controlUnavailable,
4117
- defaultValue: "",
4118
- onInput: (event) => input(event.currentTarget.value, event.nativeEvent.isComposing),
4119
- onCompositionStart: compositionStart,
4120
- onCompositionEnd: compositionEnd,
4121
- onKeyDown: keyDown,
4122
- onCopy: copy,
4123
- onPaste: paste,
4124
- className: "pointer-events-none absolute left-1/2 top-1/2 size-px resize-none overflow-hidden opacity-0",
4125
- "aria-label": t("browserViewer.browserKeyboardInput"),
4126
- "aria-keyshortcuts": "Alt+ArrowDown",
4127
- autoCapitalize: "off",
4128
- autoCorrect: "off",
4129
- spellCheck: false
4130
- }
4131
- ),
4132
- !showCanvas ? /* @__PURE__ */ jsx2(
4133
- SemanticBrowserFallback,
4134
- {
4135
- observation: props.observation,
4136
- connectionState: props.connectionState,
4137
- supportsLiveFrames: props.supportsLiveFrames,
4138
- error: props.connectionError,
4139
- controlUnavailable: props.controlUnavailable,
4140
- onAction: (action) => enqueue(action, null),
4141
- onReconnect: props.onReconnect,
4142
- reconnectLabel: props.reconnectLabel,
4143
- reconnectMessage: props.reconnectMessage
4144
- }
4145
- ) : null,
4146
- showCanvas ? /* @__PURE__ */ jsx2(
4147
- BrowserSelectControl,
4148
- {
4149
- manualFallback: props.inputBatchAttachment?.focusedInputObservations !== true,
4150
- activation: selectPopup,
4151
- onDismiss: () => inputRef.current?.focus({ preventScroll: true }),
4152
- observe: async () => {
4153
- flushPendingText();
4154
- flushPendingWheel();
4155
- await actionTailRef.current;
4156
- if (!mountedRef.current) throw new Error(t("browserViewer.theBrowserPageChanged"));
4157
- return await props.onObserveForInput();
4158
- },
4159
- act: props.onSelectFromObservation
4160
- }
4161
- ) : null,
4162
- props.mutating ? /* @__PURE__ */ jsxs2("div", { className: "pointer-events-none absolute right-3 top-3 flex items-center gap-1.5 rounded-full border border-white/10 bg-black/65 px-2.5 py-1 text-[11px] text-white/80 backdrop-blur", children: [
4163
- /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3 animate-spin" }),
4164
- " ",
4165
- props.activityLabel ?? t("browserViewer.acting")
4166
- ] }) : null
4167
- ] });
4168
- }
4169
- function SemanticBrowserFallback(props) {
4170
- const t = useOpenGeniText();
4171
- const controlFailure = interactionControlFailureFromError(props.error);
4172
- const { controlUnavailable } = props;
4173
- const generationLoss = Boolean(props.reconnectMessage);
4174
- const nodes = semanticNodes(
4175
- props.observation?.semantic?.kind === "snapshot" ? props.observation.semantic.roots : []
4176
- );
4177
- const interactive = nodes.filter((node) => node.actions.includes("click")).slice(0, 8);
4178
- return /* @__PURE__ */ jsx2("div", { className: "absolute inset-0 grid place-items-center bg-og-bg p-6", children: /* @__PURE__ */ jsxs2("div", { className: "w-full max-w-md rounded-og-lg border border-og-border bg-og-surface-1 p-4 shadow-lg", children: [
4179
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
4180
- props.error ? /* @__PURE__ */ jsx2(CircleAlertIcon, { className: "size-4 text-og-danger" }) : props.connectionState === "semantic" ? /* @__PURE__ */ jsx2(ZapIcon, { className: "size-4 text-og-fg-muted" }) : /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-4 animate-spin text-og-fg-muted" }),
4181
- /* @__PURE__ */ jsx2("p", { className: "text-og-menu font-medium text-og-fg", children: generationLoss ? t("browserViewer.chromeReconnectedOpenAFresh") : controlUnavailable ? t("browserViewer.browserControlsUnavailable") : props.error ? props.supportsLiveFrames ? t("browserViewer.liveViewDisconnected") : t("browserViewer.browserUnavailable") : props.connectionState === "semantic" ? t("browserViewer.semanticBrowser") : browserConnectionLabel(t, props.connectionState) })
4182
- ] }),
4183
- props.error || props.reconnectMessage ? /* @__PURE__ */ jsx2("p", { className: "mt-2 text-og-control leading-5 text-og-fg-muted", children: props.reconnectMessage ?? controlFailure?.message ?? props.error?.message }) : null,
4184
- interactive.length > 0 && !controlUnavailable ? /* @__PURE__ */ jsxs2("div", { className: "mt-3 border-t border-og-border pt-3", children: [
4185
- /* @__PURE__ */ jsx2("p", { className: "mb-2 text-og-xs text-og-fg-subtle", children: props.supportsLiveFrames ? t("browserViewer.pageControlsRemainAvailable") : t("browserViewer.availablePageControls") }),
4186
- /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap gap-1.5", children: interactive.map((node) => /* @__PURE__ */ jsx2(
4187
- "button",
4188
- {
4189
- type: "button",
4190
- onClick: () => props.onAction({
4191
- type: "click",
4192
- locator: { kind: "ref", ref: node.ref }
4193
- }),
4194
- className: "rounded-og-sm border border-og-border bg-og-surface-1 px-2 py-1 text-og-control text-og-fg transition hover:bg-og-surface-2",
4195
- children: node.name || node.role
4196
- },
4197
- node.ref
4198
- )) })
4199
- ] }) : null,
4200
- props.error && (!generationLoss || props.reconnectLabel) ? /* @__PURE__ */ jsx2(
4201
- "button",
4202
- {
4203
- type: "button",
4204
- onClick: props.onReconnect,
4205
- className: "mt-3 text-og-control font-medium og-link",
4206
- children: props.reconnectLabel ?? (controlFailure?.retryable === false ? t("browserViewer.tryAgain") : t("browserViewer.reconnect"))
4207
- }
4208
- ) : null
4209
- ] }) });
4210
- }
4211
- function BrowserStatusBar(props) {
4212
- const t = useOpenGeniText();
4213
- const errors = (props.observation?.diagnostics.consoleErrorCount ?? 0) + (props.observation?.diagnostics.pageErrorCount ?? 0);
4214
- const failed = props.observation?.diagnostics.failedRequestCount ?? 0;
4215
- return /* @__PURE__ */ jsxs2("div", { className: "flex h-7 shrink-0 items-center gap-2 border-t border-og-border bg-og-surface-1 px-2 text-og-xs text-og-fg-subtle", children: [
4216
- /* @__PURE__ */ jsx2(
4217
- "span",
4218
- {
4219
- className: cn(
4220
- "size-1.5 rounded-full",
4221
- props.connectionState === "live" || props.connectionState === "semantic" ? "bg-og-status-running" : "bg-og-fg-subtle"
4222
- )
4223
- }
4224
- ),
4225
- /* @__PURE__ */ jsx2("span", { children: props.connectionState === "live" ? t("browserViewer.live") : props.connectionState === "semantic" ? t("browserViewer.semantic") : browserConnectionLabel(t, props.connectionState) }),
4226
- props.inputFailure ? /* @__PURE__ */ jsx2("span", { className: "text-og-status-failed", children: props.inputFailure.state === "failed" ? t("browserViewer.inputFailed") : t("browserViewer.inputResultUnknown") }) : null,
4227
- /* @__PURE__ */ jsx2("span", { children: props.profile?.name ?? t("browserViewer.temporaryBrowser") }),
4228
- /* @__PURE__ */ jsx2("span", { className: "min-w-0 flex-1 truncate", children: props.target?.title }),
4229
- props.refreshing ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3 animate-spin" }) : null,
4230
- props.onOpenComputer ? /* @__PURE__ */ jsxs2(
4231
- "button",
4232
- {
4233
- type: "button",
4234
- onClick: props.onOpenComputer,
4235
- className: "flex items-center gap-1 rounded px-1.5 py-0.5 transition hover:bg-og-surface-2 hover:text-og-fg",
4236
- "aria-label": t("browserViewer.openThisBrowserWindowIn"),
4237
- children: [
4238
- /* @__PURE__ */ jsx2(MonitorIcon, { className: "size-3" }),
4239
- t("browserViewer.desktop")
4240
- ]
4241
- }
4242
- ) : null,
4243
- /* @__PURE__ */ jsxs2(
4244
- "button",
4245
- {
4246
- type: "button",
4247
- onClick: props.onDiagnostics,
4248
- "aria-expanded": props.diagnosticsOpen,
4249
- "aria-controls": "browser-diagnostics-drawer",
4250
- className: cn(
4251
- "flex items-center gap-1 rounded px-1.5 py-0.5 transition hover:bg-og-surface-2 hover:text-og-fg",
4252
- props.diagnosticsOpen && "bg-og-surface-2 text-og-fg"
4253
- ),
4254
- children: [
4255
- /* @__PURE__ */ jsx2(BugIcon, { className: "size-3" }),
4256
- errors + failed > 0 ? errors + failed : t("browserViewer.debug")
4257
- ]
4258
- }
4259
- )
4260
- ] });
4261
- }
4262
- function BrowserDiagnosticsDrawer(props) {
4263
- const t = useOpenGeniText();
4264
- const session = props.session;
4265
- const diagnostics = props.observation?.diagnostics;
4266
- const semanticMode = props.observation?.semantic ? t("browserViewer.semanticPageStructureAvailable") : props.observation?.screenshot ? t("browserViewer.visualFallbackInUse") : t("browserViewer.waitingForPageObservation");
4267
- return /* @__PURE__ */ jsxs2(
4268
- "aside",
4269
- {
4270
- id: "browser-diagnostics-drawer",
4271
- role: "region",
4272
- "aria-label": t("browserViewer.browserDiagnostics"),
4273
- className: "absolute inset-y-0 right-0 z-40 flex min-h-0 w-full max-w-md flex-col border-l border-og-border bg-og-surface-1 shadow-2xl",
4274
- children: [
4275
- /* @__PURE__ */ jsxs2("div", { className: "flex min-h-11 shrink-0 items-center gap-2 border-b border-og-border px-3 py-2", children: [
4276
- /* @__PURE__ */ jsx2(BugIcon, { className: "size-4 shrink-0 text-og-fg-muted" }),
4277
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
4278
- /* @__PURE__ */ jsx2("p", { className: "text-og-control font-semibold text-og-fg", children: t("browserViewer.browserDiagnostics") }),
4279
- /* @__PURE__ */ jsx2("p", { className: "truncate text-og-xs text-og-fg-subtle", children: props.target?.title || shortUrl(props.target?.url ?? "") || t("browserViewer.currentTab") })
4280
- ] }),
4281
- /* @__PURE__ */ jsx2(
4282
- "button",
4283
- {
4284
- type: "button",
4285
- onClick: props.onRefresh,
4286
- disabled: props.state.loading,
4287
- className: "grid size-7 shrink-0 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-40",
4288
- "aria-label": t("browserViewer.refreshBrowserDiagnostics"),
4289
- children: /* @__PURE__ */ jsx2(RefreshCwIcon, { className: cn("size-3.5", props.state.loading && "animate-spin") })
4290
- }
4291
- ),
4292
- /* @__PURE__ */ jsx2(
4293
- "button",
4294
- {
4295
- type: "button",
4296
- onClick: props.onClose,
4297
- className: "grid size-7 shrink-0 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg",
4298
- "aria-label": t("browserViewer.closeBrowserDiagnostics"),
4299
- children: /* @__PURE__ */ jsx2(XIcon, { className: "size-3.5" })
4300
- }
4301
- )
4302
- ] }),
4303
- /* @__PURE__ */ jsxs2("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [
4304
- /* @__PURE__ */ jsxs2("section", { className: "border-b border-og-border p-3", "aria-labelledby": "browser-runtime-title", children: [
4305
- /* @__PURE__ */ jsx2(
4306
- "h3",
4307
- {
4308
- id: "browser-runtime-title",
4309
- className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-og-fg-subtle",
4310
- children: t("browserViewer.runtime")
4311
- }
4312
- ),
4313
- /* @__PURE__ */ jsxs2("dl", { className: "mt-2 grid grid-cols-[7rem_minmax(0,1fr)] gap-x-3 gap-y-1.5 text-og-xs", children: [
4314
- /* @__PURE__ */ jsx2(
4315
- DiagnosticFact,
4316
- {
4317
- label: t("browserViewer.engine"),
4318
- value: browserEngineLabel(t, session)
4319
- }
4320
- ),
4321
- /* @__PURE__ */ jsx2(
4322
- DiagnosticFact,
4323
- {
4324
- label: t("browserViewer.driver"),
4325
- value: session?.driverId ?? t("browserViewer.unavailable")
4326
- }
4327
- ),
4328
- /* @__PURE__ */ jsx2(
4329
- DiagnosticFact,
4330
- {
4331
- label: t("browserViewer.placement"),
4332
- value: placementLabel(t, session ?? void 0)
4333
- }
4334
- ),
4335
- /* @__PURE__ */ jsx2(
4336
- DiagnosticFact,
4337
- {
4338
- label: t("browserViewer.profile"),
4339
- value: props.profile ? `${props.profile.name}${props.baseRevisionOrdinal ? ` \xB7 v${props.baseRevisionOrdinal}` : ""}` : t("browserViewer.temporary")
4340
- }
4341
- ),
4342
- /* @__PURE__ */ jsx2(
4343
- DiagnosticFact,
4344
- {
4345
- label: t("browserViewer.controller"),
4346
- value: shortGeneration(t, session?.controller?.controllerGeneration)
4347
- }
4348
- ),
4349
- /* @__PURE__ */ jsx2(
4350
- DiagnosticFact,
4351
- {
4352
- label: t("browserViewer.network"),
4353
- value: session?.networkRouteId ? t("browserViewer.customRoute") : t("browserViewer.placementDefault")
4354
- }
4355
- ),
4356
- /* @__PURE__ */ jsx2(DiagnosticFact, { label: t("browserViewer.observation"), value: semanticMode })
4357
- ] })
4358
- ] }),
4359
- /* @__PURE__ */ jsxs2("section", { className: "border-b border-og-border p-3", "aria-labelledby": "browser-page-title", children: [
4360
- /* @__PURE__ */ jsx2(
4361
- "h3",
4362
- {
4363
- id: "browser-page-title",
4364
- className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-og-fg-subtle",
4365
- children: t("browserViewer.tabDiagnostics")
4366
- }
4367
- ),
4368
- /* @__PURE__ */ jsx2("p", { className: "mt-1 text-og-xs text-og-fg-subtle", children: t("browserViewer.includesEarlierPagesInThis") }),
4369
- /* @__PURE__ */ jsxs2("dl", { className: "mt-2 grid grid-cols-2 gap-2", children: [
4370
- /* @__PURE__ */ jsx2(
4371
- DiagnosticCount,
4372
- {
4373
- label: t("browserViewer.consoleErrors"),
4374
- value: diagnostics?.consoleErrorCount
4375
- }
4376
- ),
4377
- /* @__PURE__ */ jsx2(
4378
- DiagnosticCount,
4379
- {
4380
- label: t("browserViewer.pageErrors"),
4381
- value: diagnostics?.pageErrorCount
4382
- }
4383
- ),
4384
- /* @__PURE__ */ jsx2(
4385
- DiagnosticCount,
4386
- {
4387
- label: t("browserViewer.failedRequests"),
4388
- value: diagnostics?.failedRequestCount
4389
- }
4390
- ),
4391
- /* @__PURE__ */ jsx2(
4392
- DiagnosticCount,
4393
- {
4394
- label: t("browserViewer.downloads"),
4395
- value: diagnostics?.downloadCount
4396
- }
4397
- )
4398
- ] }),
4399
- session?.failureCode ? /* @__PURE__ */ jsx2("p", { className: "mt-2 rounded-og-sm border border-og-danger/30 bg-og-danger/5 px-2.5 py-2 text-og-xs text-og-danger", children: t("browserViewer.browserStateFailurecode", { failureCode: session.failureCode }) }) : null
4400
- ] }),
4401
- props.downloadsEnabled ? /* @__PURE__ */ jsx2(
4402
- BrowserDownloadsPanel,
4403
- {
4404
- downloads: props.downloads,
4405
- loading: props.downloadsLoading,
4406
- refreshing: props.downloadsRefreshing,
4407
- error: props.downloadsError,
4408
- savingDownloadIds: props.savingDownloadIds,
4409
- onSave: props.onSaveDownload,
4410
- onRefresh: props.onRefreshDownloads
4411
- }
4412
- ) : null,
4413
- /* @__PURE__ */ jsxs2("section", { className: "p-3", "aria-labelledby": "browser-events-title", children: [
4414
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-2", children: [
4415
- /* @__PURE__ */ jsx2(
4416
- "h3",
4417
- {
4418
- id: "browser-events-title",
4419
- className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-og-fg-subtle",
4420
- children: t("browserViewer.recentEvents")
4421
- }
4422
- ),
4423
- props.state.batch?.truncated ? /* @__PURE__ */ jsx2("span", { className: "text-[10px] text-og-fg-subtle", children: t("browserViewer.newest100") }) : null
4424
- ] }),
4425
- props.state.loading && !props.state.batch ? /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 py-6 text-og-control text-og-fg-muted", children: [
4426
- /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3.5 animate-spin" }),
4427
- " ",
4428
- t("browserViewer.loadingDiagnostics")
4429
- ] }) : props.state.error ? /* @__PURE__ */ jsxs2("div", { className: "py-5", children: [
4430
- /* @__PURE__ */ jsx2("p", { className: "text-og-control text-og-danger", children: props.state.error.message }),
4431
- /* @__PURE__ */ jsx2(
4432
- "button",
4433
- {
4434
- type: "button",
4435
- onClick: props.onRefresh,
4436
- className: "mt-2 text-og-control font-medium og-link",
4437
- children: t("browserViewer.tryAgain")
4438
- }
4439
- )
4440
- ] }) : props.state.batch?.entries.length ? /* @__PURE__ */ jsx2("ol", { className: "mt-2 space-y-2", children: props.state.batch.entries.map((entry) => /* @__PURE__ */ jsxs2(
4441
- "li",
4442
- {
4443
- className: "rounded-og-sm border border-og-border bg-og-bg p-2.5",
4444
- children: [
4445
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 text-[10px] text-og-fg-subtle", children: [
4446
- /* @__PURE__ */ jsx2(
4447
- "span",
4448
- {
4449
- className: cn(
4450
- "font-semibold uppercase tracking-wide",
4451
- diagnosticTone(entry.level)
4452
- ),
4453
- children: diagnosticKindLabel(t, entry.kind)
4454
- }
4455
- ),
4456
- /* @__PURE__ */ jsx2("span", { className: "ml-auto", children: formatDiagnosticTime(t, entry.occurredAt) })
4457
- ] }),
4458
- /* @__PURE__ */ jsx2("p", { className: "mt-1 whitespace-pre-wrap break-words text-og-xs leading-5 text-og-fg", children: entry.message }),
4459
- entry.url || entry.filename || entry.method || entry.status ? /* @__PURE__ */ jsx2("p", { className: "mt-1 truncate font-og-mono text-[10px] text-og-fg-subtle", children: [entry.method, entry.status, entry.filename, entry.url].filter((value) => value !== null).join(" \xB7 ") }) : null
4460
- ]
4461
- },
4462
- entry.sequence
4463
- )) }) : /* @__PURE__ */ jsx2("p", { className: "py-6 text-og-control text-og-fg-muted", children: t("browserViewer.noDiagnosticsForThisTab") })
4464
- ] })
4465
- ] })
4466
- ]
4467
- }
4468
- );
4469
- }
4470
- function BrowserDownloadsPanel(props) {
4471
- const t = useOpenGeniText();
4472
- const [paths, setPaths] = useState9({});
4473
- const [overwrite, setOverwrite] = useState9({});
4474
- const [savedPaths, setSavedPaths] = useState9({});
4475
- const [errors, setErrors] = useState9({});
4476
- const saving = useMemo2(() => new Set(props.savingDownloadIds), [props.savingDownloadIds]);
4477
- const save = (download) => {
4478
- const destinationPath = (paths[download.id] ?? defaultDownloadDestination(download)).trim();
4479
- if (!destinationPath || saving.has(download.id)) return;
4480
- setErrors((current) => ({ ...current, [download.id]: "" }));
4481
- void props.onSave(download.id, destinationPath, overwrite[download.id] ?? false).then((response) => {
4482
- setSavedPaths((current) => ({ ...current, [download.id]: response.destinationPath }));
4483
- }).catch((cause) => {
4484
- setErrors((current) => ({
4485
- ...current,
4486
- [download.id]: cause instanceof Error ? cause.message : t("browserViewer.couldNotSaveThisDownload")
4487
- }));
4488
- });
4489
- };
4490
- return /* @__PURE__ */ jsxs2("section", { className: "border-b border-og-border p-3", "aria-labelledby": "browser-downloads-title", children: [
4491
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
4492
- /* @__PURE__ */ jsx2(DownloadIcon, { className: "size-3.5 text-og-fg-muted" }),
4493
- /* @__PURE__ */ jsx2(
4494
- "h3",
4495
- {
4496
- id: "browser-downloads-title",
4497
- className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-og-fg-subtle",
4498
- children: t("browserViewer.downloads")
4499
- }
4500
- ),
4501
- props.refreshing ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "ml-auto size-3 animate-spin text-og-fg-muted" }) : null
4502
- ] }),
4503
- props.loading ? /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 py-5 text-og-control text-og-fg-muted", children: [
4504
- /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3.5 animate-spin" }),
4505
- " ",
4506
- t("browserViewer.loadingDownloads")
4507
- ] }) : props.error ? /* @__PURE__ */ jsxs2("div", { className: "py-4", children: [
4508
- /* @__PURE__ */ jsx2("p", { className: "text-og-control text-og-danger", children: props.error.message }),
4509
- /* @__PURE__ */ jsx2(
4510
- "button",
4511
- {
4512
- type: "button",
4513
- onClick: props.onRefresh,
4514
- className: "mt-2 text-og-control font-medium og-link",
4515
- children: t("browserViewer.tryAgain")
4516
- }
4517
- )
4518
- ] }) : props.downloads.length === 0 ? /* @__PURE__ */ jsx2("p", { className: "py-4 text-og-control text-og-fg-muted", children: t("browserViewer.noFilesDownloadedYet") }) : /* @__PURE__ */ jsx2("ol", { className: "mt-2 space-y-2", children: props.downloads.map((download) => {
4519
- const destinationPath = paths[download.id] ?? defaultDownloadDestination(download);
4520
- const isSaving = saving.has(download.id);
4521
- const isSaved = savedPaths[download.id] === destinationPath;
4522
- return /* @__PURE__ */ jsxs2(
4523
- "li",
4524
- {
4525
- className: "rounded-og-sm border border-og-border bg-og-bg p-2.5",
4526
- children: [
4527
- /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 items-start gap-2", children: [
4528
- /* @__PURE__ */ jsx2("span", { className: "mt-0.5 grid size-6 shrink-0 place-items-center rounded bg-og-surface-2 text-og-fg-muted", children: isSaved ? /* @__PURE__ */ jsx2(FileCheck2Icon, { className: "size-3.5 text-og-status-idle" }) : /* @__PURE__ */ jsx2(DownloadIcon, { className: "size-3.5" }) }),
4529
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
4530
- /* @__PURE__ */ jsx2(
4531
- "p",
4532
- {
4533
- className: "truncate text-og-xs font-medium text-og-fg",
4534
- title: download.filename,
4535
- children: download.filename
4536
- }
4537
- ),
4538
- /* @__PURE__ */ jsx2("p", { className: "mt-0.5 text-[10px] text-og-fg-subtle", children: downloadStatusLabel(t, download) })
4539
- ] })
4540
- ] }),
4541
- download.status === "completed" ? /* @__PURE__ */ jsxs2("div", { className: "mt-2", children: [
4542
- /* @__PURE__ */ jsxs2("div", { className: "flex gap-1.5", children: [
4543
- /* @__PURE__ */ jsx2(
4544
- "input",
4545
- {
4546
- type: "text",
4547
- value: destinationPath,
4548
- onInput: (event) => {
4549
- const value = event.currentTarget.value;
4550
- setPaths((current) => ({ ...current, [download.id]: value }));
4551
- setErrors((current) => ({ ...current, [download.id]: "" }));
4552
- },
4553
- onKeyDown: (event) => {
4554
- if (event.key === "Enter") save(download);
4555
- },
4556
- disabled: isSaving,
4557
- "aria-label": t("browserViewer.workspacePathForFilename", {
4558
- filename: download.filename
4559
- }),
4560
- className: "h-7 min-w-0 flex-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-og-mono text-[10px] text-og-fg transition focus:border-og-accent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent disabled:opacity-50"
4561
- }
4562
- ),
4563
- /* @__PURE__ */ jsxs2(
4564
- "button",
4565
- {
4566
- type: "button",
4567
- onClick: () => save(download),
4568
- disabled: isSaving || !destinationPath.trim() || isSaved && !overwrite[download.id],
4569
- className: "inline-flex h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 text-[10px] font-medium text-og-fg transition hover:bg-og-surface-2 disabled:opacity-40",
4570
- children: [
4571
- isSaving ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx2(SaveIcon, { className: "size-3" }),
4572
- isSaving ? t("browserViewer.saving") : isSaved ? t("browserViewer.saved") : overwrite[download.id] ? t("browserViewer.replace") : t("browserViewer.save")
4573
- ]
4574
- }
4575
- )
4576
- ] }),
4577
- /* @__PURE__ */ jsxs2("label", { className: "mt-1.5 flex w-fit items-center gap-1.5 text-[10px] text-og-fg-subtle", children: [
4578
- /* @__PURE__ */ jsx2(
4579
- "input",
4580
- {
4581
- type: "checkbox",
4582
- checked: overwrite[download.id] ?? false,
4583
- onChange: (event) => {
4584
- setOverwrite((current) => ({
4585
- ...current,
4586
- [download.id]: event.currentTarget.checked
4587
- }));
4588
- setErrors((current) => ({ ...current, [download.id]: "" }));
4589
- },
4590
- disabled: isSaving,
4591
- className: "size-3 accent-og-accent"
4592
- }
4593
- ),
4594
- t("browserViewer.replaceAnExistingWorkspaceFile")
4595
- ] }),
4596
- errors[download.id] ? /* @__PURE__ */ jsx2("p", { className: "mt-1.5 break-words text-[10px] text-og-danger", children: errors[download.id] }) : null
4597
- ] }) : null
4598
- ]
4599
- },
4600
- download.id
4601
- );
4602
- }) })
4603
- ] });
4604
- }
4605
- function downloadStatusLabel(t, download) {
4606
- if (download.status === "completed") return formatBytes(download.receivedBytes);
4607
- if (download.status === "in_progress") {
4608
- const received = formatBytes(download.receivedBytes);
4609
- return download.totalBytes === null ? t("browserViewer.downloadingReceived", { received }) : t("browserViewer.downloadingReceivedOfBytes", {
4610
- received,
4611
- bytes: formatBytes(download.totalBytes)
4612
- });
4613
- }
4614
- if (download.status === "cancelled") return t("browserViewer.cancelled");
4615
- if (download.status === "unavailable") return t("browserViewer.noLongerAvailable");
4616
- return t("browserViewer.downloadFailed");
4617
- }
4618
- function defaultDownloadDestination(download) {
4619
- const leaf = download.filename.split(/[\\/]/u).at(-1)?.trim() ?? "";
4620
- const portable = leaf.normalize("NFC").replace(/[<>:"|?*\u0000-\u001f\u007f]/gu, "-").replace(/[ .]+$/u, "");
4621
- if (portable.length === 0 || /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu.test(portable)) {
4622
- return `download-${download.id.slice(0, 8)}`;
4623
- }
4624
- return portable;
4625
- }
4626
- function DiagnosticFact(props) {
4627
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
4628
- /* @__PURE__ */ jsx2("dt", { className: "text-og-fg-subtle", children: props.label }),
4629
- /* @__PURE__ */ jsx2("dd", { className: "min-w-0 truncate text-og-fg", title: props.value, children: props.value })
4630
- ] });
4631
- }
4632
- function DiagnosticCount(props) {
4633
- const t = useOpenGeniText();
4634
- return /* @__PURE__ */ jsxs2("div", { className: "rounded-og-sm border border-og-border bg-og-bg px-2.5 py-2", children: [
4635
- /* @__PURE__ */ jsx2("dt", { className: "text-[10px] text-og-fg-subtle", children: props.label }),
4636
- /* @__PURE__ */ jsx2(
4637
- "dd",
4638
- {
4639
- className: cn(
4640
- "mt-0.5 text-og-sm font-semibold",
4641
- props.value ? "text-og-fg" : "text-og-fg-muted"
4642
- ),
4643
- children: props.value ?? t("browserViewer.unavailable")
4644
- }
4645
- )
4646
- ] });
4647
- }
4648
- function browserEngineLabel(t, session) {
4649
- if (!session) return t("browserViewer.unavailable");
4650
- const version = session.engineVersion ? ` ${session.engineVersion}` : "";
4651
- return `${session.engine}${version} \xB7 ${session.headless ? "headless" : "headed"}`;
4652
- }
4653
- function shortGeneration(t, value) {
4654
- if (!value) return t("browserViewer.unavailable");
4655
- return value.length > 16 ? `${value.slice(0, 8)}\u2026${value.slice(-6)}` : value;
4656
- }
4657
- function diagnosticKindLabel(t, kind) {
4658
- switch (kind) {
4659
- case "console":
4660
- return t("browserViewer.console");
4661
- case "page_error":
4662
- return t("browserViewer.pageError");
4663
- case "failed_request":
4664
- return t("browserViewer.request");
4665
- case "download":
4666
- return t("browserViewer.download");
4667
- }
4668
- }
4669
- function diagnosticTone(level) {
4670
- if (level === "error") return "text-og-danger";
4671
- if (level === "warning") return "text-og-status-waiting";
4672
- return "text-og-fg-muted";
4673
- }
4674
- function formatDiagnosticTime(t, value) {
4675
- const date = new Date(value);
4676
- return Number.isNaN(date.getTime()) ? "" : date.toLocaleTimeString(t.locale, { hour: "2-digit", minute: "2-digit", second: "2-digit" });
4677
- }
4678
- function BrowserNotice(props) {
4679
- return /* @__PURE__ */ jsx2(
4680
- "div",
4681
- {
4682
- className: cn(
4683
- "grid h-full place-items-center bg-og-bg text-og-control text-og-fg-muted",
4684
- props.className
4685
- ),
4686
- children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
4687
- props.icon,
4688
- props.text
4689
- ] })
4690
- }
4691
- );
4692
- }
4693
- function paintCanvas(canvas, image, width, height) {
4694
- canvas.width = width;
4695
- canvas.height = height;
4696
- const context = canvas.getContext("2d", { alpha: false });
4697
- if (!context) throw new Error("Browser canvas is unavailable.");
4698
- context.drawImage(image, 0, 0, width, height);
4699
- }
4700
- function loadImage(url) {
4701
- return new Promise((resolve, reject) => {
4702
- const image = new Image();
4703
- image.onload = () => resolve(image);
4704
- image.onerror = () => reject(new Error("Browser frame image could not be decoded."));
4705
- image.src = url;
4706
- });
4707
- }
4708
- function browserPoint(canvas, frame, clientX, clientY) {
4709
- if (!canvas || !frame) return null;
4710
- const rect = canvas.getBoundingClientRect();
4711
- if (rect.width <= 0 || rect.height <= 0) return null;
4712
- const pixelX = (clientX - rect.left) / rect.width * frame.width;
4713
- const pixelY = (clientY - rect.top) / rect.height * frame.height;
4714
- return {
4715
- x: Math.max(0, pixelX / frame.deviceScaleFactor),
4716
- y: Math.max(0, pixelY / frame.deviceScaleFactor)
4717
- };
4718
- }
4719
- function semanticNodes(roots) {
4720
- const result = [];
4721
- const stack = [...roots].reverse();
4722
- while (stack.length > 0) {
4723
- const node = stack.pop();
4724
- result.push(node);
4725
- if (node.children) stack.push(...[...node.children].reverse());
4726
- }
4727
- return result;
4728
- }
4729
- function frameMatchesSelectedTarget(frame, session, target) {
4730
- return Boolean(
4731
- frame && session && target && frame.browserSessionId === session.id && frame.targetId === target.id && frame.targetGeneration === target.targetGeneration && frame.documentGeneration === target.documentGeneration
4732
- );
4733
- }
4734
- function sameFrameFence(left, right) {
4735
- return sameBrowserDocument(left, right) && left.frameId === right.frameId;
4736
- }
4737
- function sameBrowserDocument(left, right) {
4738
- return left.browserSessionId === right.browserSessionId && left.controllerGeneration === right.controllerGeneration && left.targetId === right.targetId && left.targetGeneration === right.targetGeneration && left.documentGeneration === right.documentGeneration;
4739
- }
4740
- function sameOptionalBrowserFrame(left, right) {
4741
- return left === null || right === null ? left === right : sameFrameFence(left, right);
4742
- }
4743
- function isLiveBrowser2(session) {
4744
- return !["ending", "ended", "failed", "lost"].includes(session.lifecycle);
4745
- }
4746
- function attachedChromeGenerationLoss(sessions) {
4747
- const lost = sessions.find(
4748
- (session) => session.lifecycle === "lost" && session.failureCode === "controller_transition_expired" && session.placement.kind === "attached_device"
4749
- );
4750
- return lost?.placement.kind === "attached_device" ? { deviceId: lost.placement.deviceId } : null;
4751
- }
4752
- function isAttachedChromeGenerationLossError(error) {
4753
- if (!error) return false;
4754
- return /placement instance changed|controller authority changed/i.test(error.message);
4755
- }
4756
- function countInterventions(interventions) {
4757
- const counts = /* @__PURE__ */ new Map();
4758
- for (const intervention of interventions) {
4759
- counts.set(intervention.resourceId, (counts.get(intervention.resourceId) ?? 0) + 1);
4760
- }
4761
- return counts;
4762
- }
4763
- function interventionTitle(t, intervention) {
4764
- switch (intervention.kind) {
4765
- case "manual_login":
4766
- return t("browserViewer.signInNeeded");
4767
- case "mfa":
4768
- return t("browserViewer.verificationNeeded");
4769
- case "external_action":
4770
- case "confirmation":
4771
- return t("browserViewer.actionNeeded");
4772
- case "other":
4773
- return t("browserViewer.browserNeedsYourHelp");
4774
- }
4775
- }
4776
- function placementLabel(t, session) {
4777
- if (!session) return "";
4778
- switch (session.placement.kind) {
4779
- case "sandbox_group":
4780
- return t("browserViewer.agentSandbox");
4781
- case "connected_machine":
4782
- return t("browserViewer.connectedMachine");
4783
- case "attached_device":
4784
- return t("browserViewer.yourBrowser");
4785
- case "external_provider":
4786
- return t("browserViewer.cloudBrowser");
4787
- }
4788
- }
4789
- function browserConnectionLabel(t, state) {
4790
- switch (state) {
4791
- case "attaching":
4792
- return t("browserViewer.openingBrowser");
4793
- case "connecting":
4794
- return t("browserViewer.connecting");
4795
- case "reconnecting":
4796
- return t("browserViewer.reconnecting");
4797
- case "error":
4798
- return t("browserViewer.disconnected");
4799
- case "semantic":
4800
- return t("browserViewer.semantic");
4801
- default:
4802
- return t("browserViewer.waitingForBrowser");
4803
- }
4804
- }
4805
- function shortUrl(value) {
4806
- try {
4807
- return new URL(value).hostname || value;
4808
- } catch {
4809
- return value;
4810
- }
4811
- }
4812
-
4813
- export {
4814
- useBrowserFrameStream,
4815
- useAttachedBrowsers,
4816
- useBrowserIdentities,
4817
- useBrowserDownloads,
4818
- useBrowserSession,
4819
- useBrowserSessions,
4820
- useSiteAuthConnections,
4821
- staleBrowserEndRetryDelayMs,
4822
- BrowserViewer
4823
- };
4824
- //# sourceMappingURL=chunk-MLJADQFI.js.map