@xenon-device-management/xenon 1.5.0 → 1.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 (327) hide show
  1. package/README.md +67 -34
  2. package/lib/package.json +18 -3
  3. package/lib/public/assets/ApiKeyGate-BSN8crwz.js +1 -0
  4. package/lib/public/assets/BugReportButton-DNja1wQ1.js +10 -0
  5. package/lib/public/assets/BugReportButton-DeF8gwF1.css +1 -0
  6. package/lib/public/assets/DeviceMosaicView-DVk1IpIR.js +2 -0
  7. package/lib/public/assets/EmptyState-BSDHFxd1.css +1 -0
  8. package/lib/public/assets/EmptyState-CqQ21n-H.js +1 -0
  9. package/lib/public/assets/FieldGroup-DE3u01Nu.js +1 -0
  10. package/lib/public/assets/FieldGroup-trY-2qmW.css +1 -0
  11. package/lib/public/assets/Modal-BJcFdiBP.js +45 -0
  12. package/lib/public/assets/Modal-qvYudaqQ.css +1 -0
  13. package/lib/public/assets/SegmentedControl-CSHjmrvy.js +1 -0
  14. package/lib/public/assets/SegmentedControl-M7StbYAD.css +1 -0
  15. package/lib/public/assets/SettingCard-C7wPlNh9.css +1 -0
  16. package/lib/public/assets/SettingCard-M-lFn_Wm.js +11 -0
  17. package/lib/public/assets/Table-B3EUgBi6.js +1 -0
  18. package/lib/public/assets/Table-BIjXN_-_.css +1 -0
  19. package/lib/public/assets/activity-DK3R5GAb.js +6 -0
  20. package/lib/public/assets/ai-settings-AKMDh8pd.js +21 -0
  21. package/lib/public/assets/api-keys-B5Q1EkGN.js +1 -0
  22. package/lib/public/assets/apps-CWWrkTZW.js +21 -0
  23. package/lib/public/assets/apps-Dt5dUIag.css +1 -0
  24. package/lib/public/assets/{arrow-left-BwArUbbw.js → arrow-left-9933DNlF.js} +1 -1
  25. package/lib/public/assets/arrow-up-right-B7q_1mXs.js +6 -0
  26. package/lib/public/assets/builds-page-DgWXOgh5.js +6 -0
  27. package/lib/public/assets/button-DtC0WGPu.css +1 -0
  28. package/lib/public/assets/button-lkxVyarW.js +16 -0
  29. package/lib/public/assets/check-BkT38An9.js +6 -0
  30. package/lib/public/assets/chevron-right-WELZ1Z8v.js +6 -0
  31. package/lib/public/assets/circle-check--2DSsfaI.js +6 -0
  32. package/lib/public/assets/circle-x-x7R8vTBo.js +6 -0
  33. package/lib/public/assets/{clock-l9_xGBHG.js → clock-DIpGd4Pn.js} +1 -1
  34. package/lib/public/assets/copy-CEcFYYHm.js +6 -0
  35. package/lib/public/assets/device-explorer-3H1hf8ld.css +1 -0
  36. package/lib/public/assets/device-explorer-nV6L3w7x.js +210 -0
  37. package/lib/public/assets/{download-d2T6Z1ir.js → download-CaKkGvKw.js} +1 -1
  38. package/lib/public/assets/forgot-password-CsAF11d2.js +1 -0
  39. package/lib/public/assets/funnel-D2rLIKlM.js +6 -0
  40. package/lib/public/assets/index-BrVV0Kla.js +209 -0
  41. package/lib/public/assets/index-Dz1K6OJU.css +1 -0
  42. package/lib/public/assets/keycodes-bIEJfQzr.js +1 -0
  43. package/lib/public/assets/layers-DZwF8z_0.js +11 -0
  44. package/lib/public/assets/{lock-B7xVV3Z-.js → lock-CVUUnm1V.js} +2 -2
  45. package/lib/public/assets/login-BavGkTli.js +1 -0
  46. package/lib/public/assets/maintenance-settings-Vr_u8hoR.js +6 -0
  47. package/lib/public/assets/mouse-pointer-2-n4xUMxHJ.js +11 -0
  48. package/lib/public/assets/overview-DVgoOoYF.js +11 -0
  49. package/lib/public/assets/page-header-C0pnt4xz.js +1 -0
  50. package/lib/public/assets/{plus-DLYHVBS0.js → plus-BObE-_z6.js} +2 -2
  51. package/lib/public/assets/profile-page-DNKD4MS2.js +1 -0
  52. package/lib/public/assets/reset-password-C2gSMy8a.js +1 -0
  53. package/lib/public/assets/runbook-page-BRxjWJVQ.js +104 -0
  54. package/lib/public/assets/runbook-page-BcfSJRZ-.css +1 -0
  55. package/lib/public/assets/selector-detail-page-DcV8jQuX.js +1 -0
  56. package/lib/public/assets/selector-health-BwHGcem9.css +1 -0
  57. package/lib/public/assets/selector-health-DraY2a4b.js +16 -0
  58. package/lib/public/assets/selector-health-page-tb5e3CXy.js +16 -0
  59. package/lib/public/assets/session-detail-page-BikvFUAQ.css +1 -0
  60. package/lib/public/assets/session-detail-page-CEtQr37W.js +13 -0
  61. package/lib/public/assets/settings-CpRbubta.css +1 -0
  62. package/lib/public/assets/settings-Cr-YjLbX.js +1 -0
  63. package/lib/public/assets/shield-BBc4qisQ.js +11 -0
  64. package/lib/public/assets/{shield-alert-Dh3YY8cn.js → shield-alert-BfoPJ8q_.js} +1 -1
  65. package/lib/public/assets/status-pill-outline-Cc4PF7Mg.js +1 -0
  66. package/lib/public/assets/tablet-CaOqzQv_.js +11 -0
  67. package/lib/public/assets/teams-B3Tt_9i2.js +1 -0
  68. package/lib/public/assets/{trash-2-ChnCxqG8.js → trash-2-CE5TRHb2.js} +1 -1
  69. package/lib/public/assets/{useSocket-DmYfFoKK.js → useSocket-Bkt5i-U6.js} +1 -1
  70. package/lib/public/assets/users-BIvjvjM6.js +11 -0
  71. package/lib/public/assets/users-cwrwUhwo.js +1 -0
  72. package/lib/public/assets/webhook-settings-DEZQtSgr.css +1 -0
  73. package/lib/public/assets/webhook-settings-dAp8wwEB.js +1 -0
  74. package/lib/public/assets/{zap-dJlXA3YR.js → zap-BzdRuL9F.js} +1 -1
  75. package/lib/public/index.html +2 -2
  76. package/lib/schema.json +73 -4
  77. package/lib/src/XenonCapabilityManager.js +114 -24
  78. package/lib/src/app/index.js +29 -8
  79. package/lib/src/app/routers/apikeys.js +24 -3
  80. package/lib/src/app/routers/apps.js +5 -2
  81. package/lib/src/app/routers/auth.js +198 -8
  82. package/lib/src/app/routers/bug-report.js +104 -0
  83. package/lib/src/app/routers/build-export.js +98 -0
  84. package/lib/src/app/routers/config.js +6 -2
  85. package/lib/src/app/routers/control.js +26 -2
  86. package/lib/src/app/routers/dashboard.js +700 -4
  87. package/lib/src/app/routers/grid.js +102 -28
  88. package/lib/src/app/routers/interceptor.js +177 -0
  89. package/lib/src/app/routers/ports.js +63 -0
  90. package/lib/src/app/routers/processes.js +2 -0
  91. package/lib/src/app/routers/profile.js +129 -0
  92. package/lib/src/app/routers/recordings.js +299 -0
  93. package/lib/src/app/routers/reservation.js +14 -0
  94. package/lib/src/app/routers/sdk-leases.js +113 -0
  95. package/lib/src/app/routers/sdk-version.js +18 -0
  96. package/lib/src/app/routers/teams.js +14 -7
  97. package/lib/src/app/routers/users.js +161 -0
  98. package/lib/src/app/routers/webhook.js +10 -5
  99. package/lib/src/app/swagger-docs.js +1426 -0
  100. package/lib/src/app/swagger.js +74 -1
  101. package/lib/src/config.js +19 -4
  102. package/lib/src/dashboard/event-manager.js +76 -5
  103. package/lib/src/data-service/device-service.js +25 -0
  104. package/lib/src/data-service/device-store.js +15 -6
  105. package/lib/src/data-service/prisma-store.js +21 -7
  106. package/lib/src/device-managers/AndroidDeviceManager.js +11 -2
  107. package/lib/src/device-managers/HealthMonitorService.js +6 -4
  108. package/lib/src/device-managers/NodeDevices.js +14 -16
  109. package/lib/src/device-managers/ios/IOSDiscoveryService.js +16 -3
  110. package/lib/src/device-managers/ios/IOSStreamService.js +73 -15
  111. package/lib/src/device-utils.js +70 -11
  112. package/lib/src/enums/SocketEvents.js +18 -0
  113. package/lib/src/generated/client/edge.js +132 -5
  114. package/lib/src/generated/client/index-browser.js +129 -2
  115. package/lib/src/generated/client/index.d.ts +20253 -5896
  116. package/lib/src/generated/client/index.js +132 -5
  117. package/lib/src/generated/client/package.json +1 -1
  118. package/lib/src/generated/client/schema.prisma +177 -4
  119. package/lib/src/generated/client/wasm.js +129 -2
  120. package/lib/src/helpers/UniversalMjpegProxy.js +27 -11
  121. package/lib/src/interceptors/CommandInterceptor.js +160 -16
  122. package/lib/src/interfaces/IPluginArgs.js +6 -0
  123. package/lib/src/logger.js +60 -19
  124. package/lib/src/middleware/authMiddleware.js +188 -0
  125. package/lib/src/middleware/csrfMiddleware.js +6 -5
  126. package/lib/src/middleware/leaseTokenMiddleware.js +17 -0
  127. package/lib/src/middleware/loginRateLimiter.js +67 -0
  128. package/lib/src/middleware/roleGuard.js +27 -0
  129. package/lib/src/middleware/scopeGuard.js +8 -7
  130. package/lib/src/plugin.js +20 -1
  131. package/lib/src/prisma.js +2 -1
  132. package/lib/src/scripts/run-migrations.js +6 -0
  133. package/lib/src/services/AIService.js +3 -3
  134. package/lib/src/services/ApiKeyService.js +31 -24
  135. package/lib/src/services/EmailService.js +60 -0
  136. package/lib/src/services/InspectorService.js +49 -18
  137. package/lib/src/services/InterceptorService.js +346 -0
  138. package/lib/src/services/NotificationService.js +36 -0
  139. package/lib/src/services/PasswordResetService.js +92 -0
  140. package/lib/src/services/SelectorStateService.js +311 -0
  141. package/lib/src/services/SelectorVerificationJob.js +149 -0
  142. package/lib/src/services/ServerManager.js +25 -13
  143. package/lib/src/services/SessionLifecycleService.js +55 -22
  144. package/lib/src/services/SocketClient.js +13 -9
  145. package/lib/src/services/SocketServer.js +24 -27
  146. package/lib/src/services/TeamService.js +22 -24
  147. package/lib/src/services/TracingService.js +141 -26
  148. package/lib/src/services/UserService.js +193 -0
  149. package/lib/src/services/UserSessionService.js +109 -0
  150. package/lib/src/services/VideoPipelineService.js +174 -4
  151. package/lib/src/services/autowait/AutowaitService.js +139 -0
  152. package/lib/src/services/autowait/waitFor.js +52 -0
  153. package/lib/src/services/bug-report/BugReportService.js +188 -0
  154. package/lib/src/services/bug-report/archive.js +24 -0
  155. package/lib/src/services/bug-report/har-collector.js +31 -0
  156. package/lib/src/services/bug-report/manifest.js +54 -0
  157. package/lib/src/services/bug-report/readme.js +57 -0
  158. package/lib/src/services/bug-report/types.js +6 -0
  159. package/lib/src/services/bug-report/video-slice.js +40 -0
  160. package/lib/src/services/bug-report/window.js +28 -0
  161. package/lib/src/services/healing/FuzzyXmlHealingProvider.js +2 -0
  162. package/lib/src/services/healing/HealingMetrics.js +50 -0
  163. package/lib/src/services/healing/HealingOrchestrator.js +44 -3
  164. package/lib/src/services/healing/LlmHealingProvider.js +2 -0
  165. package/lib/src/services/healing/OcrHealingProvider.js +4 -0
  166. package/lib/src/services/healing/ResilioTreeHealingProvider.js +2 -0
  167. package/lib/src/services/healing/VisualAiHealingProvider.js +2 -0
  168. package/lib/src/services/healing/types.js +15 -0
  169. package/lib/src/services/identity/bootstrap.js +56 -0
  170. package/lib/src/services/identity/sessionCleanupCron.js +32 -0
  171. package/lib/src/services/identity/userAuthorization.js +50 -0
  172. package/lib/src/services/interceptor/AndroidProxyAdapter.js +87 -0
  173. package/lib/src/services/interceptor/CertManager.js +131 -0
  174. package/lib/src/services/interceptor/HarBuilder.js +48 -0
  175. package/lib/src/services/interceptor/HostFilter.js +50 -0
  176. package/lib/src/services/interceptor/MitmProxyHost.js +475 -0
  177. package/lib/src/services/interceptor/MockEngine.js +73 -0
  178. package/lib/src/services/interceptor/RequestBuffer.js +92 -0
  179. package/lib/src/services/interceptor/SessionArchive.js +93 -0
  180. package/lib/src/services/interceptor/types.js +2 -0
  181. package/lib/src/services/lease/LeaseOrphanSweeper.js +64 -0
  182. package/lib/src/services/lease/LeaseService.js +261 -0
  183. package/lib/src/services/lease/buildCapabilityBag.js +33 -0
  184. package/lib/src/services/lease/leaseToken.js +25 -0
  185. package/lib/src/services/ports/PortAllocatorClient.js +61 -0
  186. package/lib/src/services/ports/PortAllocatorService.js +60 -0
  187. package/lib/src/services/recording/RecordingOrchestrator.js +624 -0
  188. package/lib/src/services/recording/annotation-render.js +151 -0
  189. package/lib/src/services/recording/busy-precheck.js +89 -0
  190. package/lib/src/services/recording/concurrency-gate.js +51 -0
  191. package/lib/src/services/recording/manualLock.js +63 -0
  192. package/lib/src/services/recording/proof-bundle.js +162 -0
  193. package/lib/src/services/recording/recording-store.js +100 -0
  194. package/lib/src/services/telemetry/attributes.js +55 -0
  195. package/lib/src/types/identity.js +13 -0
  196. package/lib/test/helpers/seedUser.js +50 -0
  197. package/lib/test/integration/auth-flow.spec.js +84 -0
  198. package/lib/test/integration/auth-rate-limit.spec.js +40 -0
  199. package/lib/test/integration/bug-report-route.spec.js +124 -0
  200. package/lib/test/integration/cross-workflow.spec.js +218 -0
  201. package/lib/test/integration/forgot-password-rate-limit.spec.js +40 -0
  202. package/lib/test/integration/forgot-password.spec.js +89 -0
  203. package/lib/test/integration/{testHelpers.js → legacy-deprecation-headers.spec.js} +23 -29
  204. package/lib/test/integration/migration.spec.js +27 -0
  205. package/lib/test/integration/node-pair-auth-rest.spec.js +73 -0
  206. package/lib/test/integration/ports-router.spec.js +85 -0
  207. package/lib/test/integration/profile-tokens.spec.js +80 -0
  208. package/lib/test/integration/recordings-router.spec.js +117 -0
  209. package/lib/test/integration/reset-revokes-sessions.spec.js +61 -0
  210. package/lib/test/integration/role-matrix.spec.js +118 -0
  211. package/lib/test/integration/sdk-leases-router.spec.js +126 -0
  212. package/lib/test/integration/sdk-version.spec.js +69 -0
  213. package/lib/test/integration/selector-state-flow.spec.js +231 -0
  214. package/lib/test/integration/team-membership-migration.spec.js +57 -0
  215. package/lib/test/integration/team-visibility-dashboard.spec.js +151 -0
  216. package/lib/test/integration/team-visibility-grid.spec.js +127 -0
  217. package/lib/test/integration/team-visibility-recordings.spec.js +129 -0
  218. package/lib/test/integration/users-flow.spec.js +73 -0
  219. package/lib/test/integration/users-lockout.spec.js +76 -0
  220. package/lib/test/unit/ApiKeyService.test.js +117 -32
  221. package/lib/test/unit/AutowaitService.spec.js +163 -0
  222. package/lib/test/unit/CommandInterceptor.autowait.spec.js +167 -0
  223. package/lib/test/unit/EmailService.test.js +76 -0
  224. package/lib/test/unit/PasswordResetService.test.js +77 -0
  225. package/lib/test/unit/TeamService.test.js +78 -0
  226. package/lib/test/unit/UserService.test.js +160 -0
  227. package/lib/test/unit/UserSessionService.test.js +76 -0
  228. package/lib/test/unit/XenonCapabilityManager.spec.js +50 -0
  229. package/lib/test/unit/aggregate-hotspots-tuple-key.spec.js +179 -0
  230. package/lib/test/unit/authMiddleware.test.js +195 -0
  231. package/lib/test/unit/bootstrap-identity.test.js +67 -0
  232. package/lib/test/unit/bug-report/archive.spec.js +67 -0
  233. package/lib/test/unit/bug-report/har-collector.spec.js +47 -0
  234. package/lib/test/unit/bug-report/manifest.spec.js +92 -0
  235. package/lib/test/unit/bug-report/readme.spec.js +50 -0
  236. package/lib/test/unit/bug-report/route.spec.js +63 -0
  237. package/lib/test/unit/bug-report/service.spec.js +63 -0
  238. package/lib/test/unit/bug-report/video-slice.spec.js +51 -0
  239. package/lib/test/unit/bug-report/window.spec.js +46 -0
  240. package/lib/test/unit/build-export.spec.js +112 -0
  241. package/lib/test/unit/busy-precheck.spec.js +85 -0
  242. package/lib/test/unit/concurrency-gate.spec.js +28 -0
  243. package/lib/test/unit/event-manager-regression-hook.spec.js +113 -0
  244. package/lib/test/unit/extract-access-key-token-pair.test.js +22 -0
  245. package/lib/test/unit/findAndLockDevice-leasemerge.spec.js +99 -0
  246. package/lib/test/unit/healing-instrumentation.spec.js +225 -0
  247. package/lib/test/unit/healing-state-endpoints.spec.js +249 -0
  248. package/lib/test/unit/interceptor/AndroidProxyAdapter.spec.js +147 -0
  249. package/lib/test/unit/interceptor/CertManager.spec.js +101 -0
  250. package/lib/test/unit/interceptor/HarBuilder.spec.js +56 -0
  251. package/lib/test/unit/interceptor/HostFilter.spec.js +93 -0
  252. package/lib/test/unit/interceptor/MitmProxyHost.spec.js +535 -0
  253. package/lib/test/unit/interceptor/MockEngine.spec.js +130 -0
  254. package/lib/test/unit/interceptor/RequestBuffer.spec.js +116 -0
  255. package/lib/test/unit/interceptor/SessionArchive.spec.js +128 -0
  256. package/lib/test/unit/lease/LeaseOrphanSweeper.spec.js +86 -0
  257. package/lib/test/unit/lease/LeaseService.spec.js +261 -0
  258. package/lib/test/unit/lease/buildCapabilityBag.spec.js +38 -0
  259. package/lib/test/unit/lease/leaseToken.spec.js +26 -0
  260. package/lib/test/unit/loginRateLimiter.test.js +38 -0
  261. package/lib/test/unit/ports/PortAllocatorService.spec.js +101 -0
  262. package/lib/test/unit/profile-router.test.js +108 -0
  263. package/lib/test/unit/proof-bundle.spec.js +198 -0
  264. package/lib/test/unit/recording-instrumentation.spec.js +241 -0
  265. package/lib/test/unit/recording-orchestrator.spec.js +265 -0
  266. package/lib/test/unit/recording-store.spec.js +88 -0
  267. package/lib/test/unit/roleGuard.test.js +61 -0
  268. package/lib/test/unit/run-migrations.test.js +38 -0
  269. package/lib/test/unit/selector-state-service.spec.js +413 -0
  270. package/lib/test/unit/selector-verification-job.spec.js +206 -0
  271. package/lib/test/unit/team-filter.test.js +84 -0
  272. package/lib/test/unit/tracing-service-init.spec.js +147 -0
  273. package/lib/test/unit/userAuthorization.test.js +64 -0
  274. package/lib/test/unit/users-router.test.js +105 -0
  275. package/lib/test/unit/video-pipeline-outputpath.spec.js +53 -0
  276. package/lib/tsconfig.tsbuildinfo +1 -1
  277. package/package.json +18 -3
  278. package/prisma/migrations/20260425122254_add_healing_tier/migration.sql +2 -0
  279. package/prisma/migrations/20260425154208_selector_state_and_strategy/migration.sql +33 -0
  280. package/prisma/migrations/20260427000001_add_recording_models/migration.sql +57 -0
  281. package/prisma/migrations/20260428230621_phase_1_identity/migration.sql +65 -0
  282. package/prisma/migrations/20260429001349_apikey_userid_required/migration.sql +32 -0
  283. package/prisma/migrations/20260429160654_phase_2_password_reset/migration.sql +19 -0
  284. package/prisma/migrations/20260430001747_phase_3_team_members/migration.sql +23 -0
  285. package/prisma/migrations/20260430140000_add_apikey_expires_at/migration.sql +9 -0
  286. package/prisma/migrations/20260514033625_add_lease_model/migration.sql +53 -0
  287. package/prisma/schema.prisma +173 -0
  288. package/schema.json +73 -4
  289. package/scripts/generate-types-from-schema.js +8 -0
  290. package/lib/public/assets/Layouts-CTHnLLVC.js +0 -11
  291. package/lib/public/assets/SegmentedControl-CWwID0UE.css +0 -1
  292. package/lib/public/assets/SegmentedControl-Djoofu92.js +0 -11
  293. package/lib/public/assets/StatusCode-B4hmGFGD.js +0 -1
  294. package/lib/public/assets/StatusCode-BOPh6PZl.css +0 -1
  295. package/lib/public/assets/ai-settings-D4he_pGT.js +0 -11
  296. package/lib/public/assets/api-keys-CPzHLCCq.js +0 -1
  297. package/lib/public/assets/apps-BdKoUzaF.js +0 -21
  298. package/lib/public/assets/apps-CcM77dgg.css +0 -1
  299. package/lib/public/assets/badge-B1nKs8zj.css +0 -1
  300. package/lib/public/assets/badge-CgQ11zjO.js +0 -1
  301. package/lib/public/assets/button-B3ezKnC2.js +0 -1
  302. package/lib/public/assets/button-EEJNuTRG.css +0 -1
  303. package/lib/public/assets/calendar-2wVD9rUs.js +0 -6
  304. package/lib/public/assets/copy-DQorhFqO.js +0 -11
  305. package/lib/public/assets/cpu-7oqpiVxJ.js +0 -6
  306. package/lib/public/assets/device-explorer-CNC4l8og.js +0 -180
  307. package/lib/public/assets/device-explorer-Cmwd6FXJ.css +0 -1
  308. package/lib/public/assets/index-DNqd5_Lo.js +0 -204
  309. package/lib/public/assets/index-OdlIdVG-.css +0 -1
  310. package/lib/public/assets/maintenance-settings-CkXB2Y2d.js +0 -6
  311. package/lib/public/assets/mouse-pointer-2-DA2XQu9a.js +0 -6
  312. package/lib/public/assets/overview-CuJx-CmT.css +0 -1
  313. package/lib/public/assets/overview-CwzfXsb6.js +0 -1
  314. package/lib/public/assets/session-dashboard-CmtZIfX0.js +0 -62
  315. package/lib/public/assets/session-dashboard-DE4UrMsz.css +0 -1
  316. package/lib/public/assets/settings-Btl-YN1e.js +0 -1
  317. package/lib/public/assets/settings-DPMls9vh.css +0 -1
  318. package/lib/public/assets/teams-oP3Qiqlu.js +0 -1
  319. package/lib/public/assets/upload-Ct8wzOVS.js +0 -11
  320. package/lib/public/assets/utils-BQPBMaRB.js +0 -1
  321. package/lib/public/assets/webhook-settings-CDPgsgkb.css +0 -1
  322. package/lib/public/assets/webhook-settings-CX4_q3r4.js +0 -1
  323. package/lib/src/auth/nodeSecret.js +0 -33
  324. package/lib/src/middleware/apiKeyMiddleware.js +0 -69
  325. package/lib/src/middleware/nodeSecretMiddleware.js +0 -38
  326. package/lib/test/unit/apiKeyMiddleware.test.js +0 -58
  327. package/lib/test/unit/nodeSecretMiddleware.test.js +0 -38
package/README.md CHANGED
@@ -62,9 +62,24 @@
62
62
  - ✅ **Multi-Modal Fallback** - Syntactic -> OCR -> Visual AI -> LLM reasoning
63
63
  - ✅ **Infrastructure-Free** - Works with existing LokiJS (local) or PostgreSQL (remote)
64
64
 
65
+ #### Selector lifecycle (Trust & Truth layer)
66
+
67
+ Healed selectors flow through a state machine: **Active → Pending → Resolved**, with an optional **Muted** branch.
68
+
69
+ - **Active** — a hot selector that's healing in flight; surfaces on the Selector Health dashboard, the CI gate, and the webhook digest.
70
+ - **Mark as Fixed** — after rewriting the selector in your test source, click "Mark as Fixed" in the dashboard. The row moves to **Pending**.
71
+ - **Pending** — Xenon watches subsequent CI builds. When 3 distinct `build_id`s have run the selector with no heals, it auto-promotes to **Resolved** (`SelectorVerificationJob` runs every 15 minutes).
72
+ - **Resolved** — the rewrite stuck. Excluded from the CI gate and digest. A future heal flips the row back to Active with a regression badge (institutional memory).
73
+ - **Muted** — a selector you've intentionally chosen to ignore (legacy flow, etc.). Hidden from dashboard, CI gate, and digest until unmuted.
74
+
75
+ The dashboard shows strategy + value (`Accessibility ID: login-btn`) so suggested rewrites are copy-ready in JS / Java / Python / C# / Ruby — your client language is remembered in `localStorage.xenon.copyLang`.
76
+
77
+ > **CI gate behavior change:** muted/pending/resolved selectors no longer count toward `/healing/hotspots/violations`. If your CI was passing/failing based on that endpoint, the signal will get quieter after this release — selectors actively being managed are no longer flagged. Pass `?status=all` to opt back into the old behavior.
78
+
65
79
  ### Recording & Artifacts
66
80
  - ✅ **Video recording** - Full session capture
67
81
  - ✅ **Screenshot capture** - On-demand and per-command
82
+ - ✅ **Network interceptor** - Live HTTP/HTTPS capture, mocking, and HAR export ([docs](https://xenon-docs.vercel.app/docs/network-interceptor))
68
83
  - ✅ **Distributed Tracing** - OpenTelemetry spans for exact command latency
69
84
  - ✅ **Performance profiling** - CPU, memory, FPS metrics
70
85
  - ✅ **Log aggregation** - Appium, device, app logs
@@ -238,7 +253,7 @@ Xenon supports extended control and reporting via the `xenon:` execute script na
238
253
  | `xenon: addTag` | Add searchable tags to the session | `{"tag": "regression"}` |
239
254
  | `xenon: debug` | Send custom debug logs to Xenon dashboard | `{"message": "API Response: 200 OK"}` |
240
255
 
241
- See [docs/capabilities.md](docs/capabilities.md) for full usage details.
256
+ Full reference for these commands is in the Swagger UI at `/xenon/api-docs`.
242
257
 
243
258
  ---
244
259
 
@@ -391,10 +406,11 @@ The full documentation is available at:
391
406
  **[https://xenon-docs.vercel.app/](https://xenon-docs.vercel.app/)**
392
407
 
393
408
  ### Quick Links
394
- - [Installation Guide](docs/installation.md)
395
- - [Configuration Options](docs/configuration.md)
396
- - [API Reference](docs/api.md)
397
- - [Troubleshooting](docs/troubleshooting.md)
409
+ - [Server arguments & env vars](docs/server-args.md)
410
+ - [Node provisioning](docs/node-provisioning.md) — pair-auth credentials for hub-node deployments
411
+ - [Teams & device access](docs/teams.md)
412
+ - [Data retention & cleanup](docs/retention.md)
413
+ - API reference: live Swagger at `/xenon/api-docs`
398
414
 
399
415
  ---
400
416
 
@@ -423,55 +439,68 @@ npm run test:ios # iOS integration
423
439
 
424
440
  ## 🔐 Authentication
425
441
 
426
- Xenon REST endpoints under `/xenon/api/*` require an API key passed in the `X-Xenon-API-Key` header.
442
+ All `/xenon/api/*` endpoints are authenticated. Xenon supports three shapes pick whichever matches your caller.
427
443
 
428
- ### Bootstrap key
444
+ ### Identity model
429
445
 
430
- On first start, Xenon writes a one-time bootstrap key (admin-scoped) to:
446
+ Xenon ships an enterprise identity stack: **users** with roles (`SUPER_ADMIN` / `ADMIN` / `MEMBER`), **teams** that scope which devices a user can reach, and **API tokens** minted per-user with their own scope set. The dashboard, programmatic clients, and hub-node channel all flow through the same identity.
431
447
 
432
- ```
433
- ~/.cache/xenon/bootstrap-key.txt (0600 permissions)
448
+ ### Auth shapes
449
+
450
+ | Shape | Header(s) | When to use |
451
+ |---|---|---|
452
+ | **Cookie session** | `Cookie: xenon_dashboard_session=…` | Dashboard browser sessions. Set by `POST /api/auth/login` with `{email, password}`. |
453
+ | **Pair auth** | `X-Xenon-Access-Key` + `X-Xenon-Token` | Programmatic clients (CI, SDK, hub→node). Each user has one access key (rotatable) and any number of scoped tokens. |
454
+ | **Auth disabled** | _(none)_ | Local dev only. Set `--plugin-xenon-auth-disabled` (or `XENON_AUTH_DISABLED=true`). A WARN logs every 60 s. |
455
+
456
+ ### First-run bootstrap
457
+
458
+ On first start Xenon creates a `SUPER_ADMIN` user from these env vars (defaults `admin@xenon.local` / `Admin@123`):
459
+
460
+ ```bash
461
+ export XENON_BOOTSTRAP_ADMIN_EMAIL="you@example.com"
462
+ export XENON_BOOTSTRAP_ADMIN_PASSWORD="..." # change me
434
463
  ```
435
464
 
436
- The startup log prints a WARN pointing at this path. **Rotate the bootstrap key within 24 hours.**
465
+ Sign in at `https://<host>/xenon/` with these credentials. From `/profile` you can mint API tokens and rotate your access key. For CI use, programmatically `POST /api/auth/login` to get the cookie, then `POST /api/profile/tokens` to mint a scoped token.
437
466
 
438
- ### Creating a permanent key
467
+ ### Generating a programmatic token
439
468
 
440
469
  ```bash
441
- # Create a scoped key for CI
442
- curl -X POST \
443
- -H "X-Xenon-API-Key: $(cat ~/.cache/xenon/bootstrap-key.txt)" \
470
+ # 1. Get your access key + a fresh token from /profile in the dashboard, or:
471
+ curl -s -X POST -b "xenon_dashboard_session=$COOKIE" \
444
472
  -H 'Content-Type: application/json' \
445
- -d '{"name":"ci","scopes":["sessions","read"],"rateLimit":600}' \
446
- http://localhost:4723/xenon/api/apikeys
473
+ -d '{"name":"ci","scopes":["sessions","read"]}' \
474
+ http://localhost:4723/xenon/api/profile/tokens
447
475
 
448
- # Revoke the bootstrap key after saving the returned `key` value
449
- curl -X DELETE \
450
- -H "X-Xenon-API-Key: $NEW_ADMIN_KEY" \
451
- http://localhost:4723/xenon/api/apikeys/<bootstrap-id>
476
+ # 2. Use it on every subsequent call:
477
+ curl -H "X-Xenon-Access-Key: xen_..." -H "X-Xenon-Token: ..." \
478
+ http://localhost:4723/xenon/api/devices
452
479
  ```
453
480
 
454
481
  ### Scopes
455
482
 
483
+ Tokens carry one or more scopes; the user's role controls which scopes they can grant.
484
+
456
485
  | Scope | Access |
457
486
  |-------|--------|
458
487
  | `read` | GET sessions, devices, logs, apps |
459
488
  | `sessions` | Create/delete sessions and reservations |
460
- | `devices` | Block/unblock devices, install apps |
461
- | `admin` | API key management, webhooks, node registration |
489
+ | `devices` | Block/unblock devices, install apps, hub-node `/register` and `/unblock` |
490
+ | `admin` | User / team / API-key management, webhooks |
462
491
 
463
492
  ### Teams (device access control)
464
493
 
465
- Scopes govern *which verbs* a key can call; **teams** govern *which devices* it can reach. A key bound to a team sees its team's devices plus the shared pool (`teamId = null`). `admin`-scoped keys bypass team filtering.
494
+ Scopes govern *which verbs* a token can call; **teams** govern *which devices* it can reach. A user bound to a team sees the team's devices plus the shared pool (`teamId = null`). `admin`-scope tokens bypass team filtering.
466
495
 
467
- Test clients pass the key via the `xenon:accessKey` capability:
496
+ Test clients pass the access key via the `xenon:accessKey` capability:
468
497
 
469
498
  ```js
470
499
  const caps = {
471
500
  platformName: 'iOS',
472
501
  'appium:automationName': 'XCUITest',
473
- 'xenon:accessKey': process.env.XENON_CI_KEY, // key bound to a team
474
- // optional: 'xenon:team': '<team-id>' to pin allocation to a specific team (admins only for cross-team)
502
+ 'xenon:accessKey': process.env.XENON_CI_KEY, // user with team membership
503
+ // optional: 'xenon:team': '<team-id>' to pin allocation (admins only for cross-team)
475
504
  };
476
505
  ```
477
506
 
@@ -479,13 +508,14 @@ See [docs/teams.md](docs/teams.md) for creating teams, assigning devices, and th
479
508
 
480
509
  ### Hub-node channel
481
510
 
482
- Set `--plugin-xenon-node-secret` (or `XENON_NODE_SECRET`) to the **same value** on both hub and node. When unset, the channel permits with a WARN (back-compat for single-node installs).
511
+ Hub and node authenticate using the same pair-auth shape. Provision a User on the hub for each node, mint a `devices`-scoped token, and set both env vars on the node:
483
512
 
484
- For zero-downtime secret rotation, set `XENON_NODE_SECRET` to the new secret and `XENON_NODE_SECRET_PREVIOUS` to the old one. The hub accepts either during the overlap window — flip nodes one at a time, then drop `XENON_NODE_SECRET_PREVIOUS`.
485
-
486
- ### Local development
513
+ ```bash
514
+ export XENON_HUB_ACCESS_KEY="xen_..."
515
+ export XENON_HUB_TOKEN="..."
516
+ ```
487
517
 
488
- Pass `--plugin-xenon-auth-disabled` to skip auth entirely. A WARN is logged every 60 s as a reminder.
518
+ Both REST `/register` calls and the Socket.io handshake will use this pair. See [docs/node-provisioning.md](docs/node-provisioning.md) for the full provisioning + recovery flow.
489
519
 
490
520
  ---
491
521
 
@@ -505,8 +535,11 @@ Xenon reads these env vars in addition to the CLI flags. Prefer env vars for cre
505
535
  | `XENON_OTEL_DEBUG` | When `true`, OpenTelemetry adds a ConsoleSpanExporter so every span is logged. Dev/tracing only. |
506
536
  | `XENON_DB_PROVIDER` | `sqlite` or `postgresql`. Same as `--plugin-xenon-databaseProvider`. |
507
537
  | `DATABASE_URL` | Prisma database URL. Falls back to `file:~/.cache/xenon/xenon.db`. |
508
- | `XENON_NODE_SECRET` | Shared hub↔node secret (see above). |
509
- | `XENON_NODE_SECRET_PREVIOUS` | Secondary secret accepted during rotation overlap. |
538
+ | `XENON_HUB_ACCESS_KEY` | Node→hub outbound: access key the node sends in `X-Xenon-Access-Key`. Required alongside `XENON_HUB_TOKEN`. See [docs/node-provisioning.md](docs/node-provisioning.md). |
539
+ | `XENON_HUB_TOKEN` | Node→hub outbound: token the node sends in `X-Xenon-Token`. Required alongside `XENON_HUB_ACCESS_KEY`. |
540
+ | `XENON_BOOTSTRAP_ADMIN_EMAIL` / `XENON_BOOTSTRAP_ADMIN_PASSWORD` | First-run super-admin user, created on first hub boot. Defaults `admin@xenon.local` / `Admin@123`. Change in any non-throwaway environment. |
541
+ | `XENON_AUTH_DISABLED` | `true` to disable all auth. Local dev only. |
542
+ | `XENON_AUTO_MIGRATE` | When `true` (default), the hub auto-applies pending schema changes at startup (`prisma db push` for SQLite, `prisma migrate deploy` for PostgreSQL). Set `false` if you manage migrations externally via CI for auditable change-control. |
510
543
 
511
544
  See [`docs/server-args.md`](docs/server-args.md) for the full CLI-flag reference and how these variables interact with config files.
512
545
 
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenon-device-management/xenon",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Xenon - Intelligent Mobile Infrastructure. A self-healing device orchestration platform for Appium.",
5
5
  "main": "./lib/src/index.js",
6
6
  "exports": {
@@ -59,20 +59,27 @@
59
59
  "@fortawesome/react-fontawesome": "^3.1.1",
60
60
  "@google/generative-ai": "^0.24.1",
61
61
  "@opentelemetry/api": "^1.9.0",
62
+ "@opentelemetry/api-logs": "^0.211.0",
63
+ "@opentelemetry/exporter-logs-otlp-http": "^0.211.0",
64
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.211.0",
62
65
  "@opentelemetry/exporter-trace-otlp-http": "^0.211.0",
63
66
  "@opentelemetry/resources": "^2.5.0",
67
+ "@opentelemetry/sdk-logs": "^0.211.0",
68
+ "@opentelemetry/sdk-metrics": "^2.0.0",
64
69
  "@opentelemetry/sdk-node": "^0.211.0",
65
70
  "@opentelemetry/semantic-conventions": "^1.39.0",
66
71
  "@prisma/client": "^5.4.1",
67
72
  "@types/appium-adb": "^9.10.4",
68
73
  "@types/express-fileupload": "^1.5.1",
69
74
  "@types/node-persist": "^3.1.5",
75
+ "@xmldom/xmldom": "^0.8.10",
70
76
  "appium-adb": "^11.0.3",
71
77
  "appium-chromedriver": "^5.6.19",
72
78
  "appium-ios-device": "^2.7.6",
73
79
  "async-lock": "^1.2.8",
74
80
  "async-wait-until": "^2.0.12",
75
81
  "axios": "^0.27.2",
82
+ "bcrypt": "^5.1.1",
76
83
  "cors": "^2.8.5",
77
84
  "download": "^8.0.0",
78
85
  "express": "^4.17.3",
@@ -80,6 +87,7 @@
80
87
  "fast-xml-parser": "^5.3.4",
81
88
  "fs-extra": "^11.1.1",
82
89
  "get-port": "^5.1.1",
90
+ "http-mitm-proxy": "^1.1.0",
83
91
  "http-proxy-agent": "7.0.0",
84
92
  "http-proxy-middleware": "^3.0.0-beta.1",
85
93
  "https-proxy-agent": "7.0.2",
@@ -90,9 +98,11 @@
90
98
  "mjpeg-proxy": "^0.3.0",
91
99
  "node-abort-controller": "^3.1.1",
92
100
  "node-cache": "^5.1.2",
101
+ "node-forge": "^1.4.0",
93
102
  "node-persist": "^3.1.3",
94
103
  "node-schedule": "^2.1.1",
95
104
  "node-simctl": "^7.3.13",
105
+ "nodemailer": "^6.10.1",
96
106
  "normalize-url": "6.1.0",
97
107
  "nyc": "^15.1.0",
98
108
  "openai": "^6.17.0",
@@ -114,7 +124,6 @@
114
124
  "unzipper": "^0.12.3",
115
125
  "usbmux": "^0.1.0",
116
126
  "uuid": "^8.3.2",
117
- "@xmldom/xmldom": "^0.8.10",
118
127
  "xpath": "^0.0.34",
119
128
  "yargs": "^17.7.2",
120
129
  "zod": "^4.3.6"
@@ -135,8 +144,10 @@
135
144
  "@appium/fake-driver": "^6.0.1",
136
145
  "@appium/plugin-test-support": "^1.0.3",
137
146
  "@babel/core": "^7.23.9",
147
+ "@babel/eslint-parser": "^7.23.3",
138
148
  "@babel/plugin-transform-class-properties": "^7.23.3",
139
149
  "@types/async-lock": "1.4.2",
150
+ "@types/bcrypt": "^5.0.2",
140
151
  "@types/bluebird": "^3.5.42",
141
152
  "@types/chai": "4.3.10",
142
153
  "@types/chai-as-promised": "^7.1.8",
@@ -147,12 +158,16 @@
147
158
  "@types/lodash": "^4.14.202",
148
159
  "@types/lokijs": "1.5.14",
149
160
  "@types/mocha": "9.1.1",
161
+ "@types/node-forge": "^1.3.14",
150
162
  "@types/node-schedule": "1.3.2",
163
+ "@types/nodemailer": "^6.4.23",
151
164
  "@types/sinon": "^17.0.2",
152
165
  "@types/sinon-chai": "^3.2.12",
166
+ "@types/supertest": "^7.2.0",
153
167
  "@types/swagger-jsdoc": "^6.0.4",
154
168
  "@types/swagger-ui-express": "^4.1.8",
155
169
  "@types/tcp-port-used": "^1.0.4",
170
+ "@types/unzipper": "^0.10.11",
156
171
  "@types/uuid": "8.3.4",
157
172
  "@types/yargs": "17.0.32",
158
173
  "@typescript-eslint/eslint-plugin": "5.62.0",
@@ -161,7 +176,6 @@
161
176
  "appium": "^3.1.1",
162
177
  "appium-uiautomator2-driver": "^5.0.3",
163
178
  "appium-xcuitest-driver": "^10.1.2",
164
- "@babel/eslint-parser": "^7.23.3",
165
179
  "chai": "^4.3.10",
166
180
  "chai-as-promised": "^7.1.1",
167
181
  "chai-exclude": "^2.1.0",
@@ -179,6 +193,7 @@
179
193
  "ramda": "^0.29.1",
180
194
  "sinon": "^17.0.0",
181
195
  "sinon-chai": "^3.7.0",
196
+ "supertest": "^7.2.2",
182
197
  "ts-node": "^10.9.1",
183
198
  "typescript": "^5.5.4",
184
199
  "vite": "^4.5.0",
@@ -0,0 +1 @@
1
+ import{r as n,j as e}from"./index-BrVV0Kla.js";function x({children:c}){const[i,s]=n.useState(null),[o,l]=n.useState(""),[r,a]=n.useState(null);if(n.useEffect(()=>{fetch("/xenon/api/health").then(()=>fetch("/xenon/api/sessions",{credentials:"include"})).then(t=>s(t.status!==401)).catch(()=>s(!1))},[]),i===null)return e.jsx("div",{style:{padding:40},children:"Loading…"});if(i)return e.jsx(e.Fragment,{children:c});async function u(t){t.preventDefault(),a(null);const d=await fetch("/xenon/api/auth/dashboard-session",{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({apiKey:o.trim()})});if(d.ok)s(!0);else{const p=await d.json().catch(()=>({}));a(p.error||"Invalid key")}}return e.jsxs("div",{style:{maxWidth:420,margin:"120px auto",padding:24},children:[e.jsx("h2",{children:"Sign in to Xenon"}),e.jsxs("p",{children:["Paste an API key. First-time setup: the bootstrap key is in"," ",e.jsx("code",{children:"~/.cache/xenon/bootstrap-key.txt"})," on the server."]}),e.jsxs("form",{onSubmit:u,children:[e.jsx("input",{type:"password",value:o,onChange:t=>l(t.target.value),placeholder:"API key",style:{width:"100%",padding:8,boxSizing:"border-box"},autoFocus:!0}),e.jsx("button",{type:"submit",style:{marginTop:12,padding:"8px 16px"},children:"Continue"})]}),r&&e.jsx("p",{style:{color:"crimson",marginTop:12},children:r})]})}export{x as ApiKeyGate};
@@ -0,0 +1,10 @@
1
+ import{g as h,j as o,r as g,m as f,t as b}from"./index-BrVV0Kla.js";/**
2
+ * @license lucide-react v0.555.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const x=[["path",{d:"M12 20v-9",key:"1qisl0"}],["path",{d:"M14 7a4 4 0 0 1 4 4v3a6 6 0 0 1-12 0v-3a4 4 0 0 1 4-4z",key:"uouzyp"}],["path",{d:"M14.12 3.88 16 2",key:"qol33r"}],["path",{d:"M21 21a4 4 0 0 0-3.81-4",key:"1b0z45"}],["path",{d:"M21 5a4 4 0 0 1-3.55 3.97",key:"5cxbf6"}],["path",{d:"M22 13h-4",key:"1jl80f"}],["path",{d:"M3 21a4 4 0 0 1 3.81-4",key:"1fjd4g"}],["path",{d:"M3 5a4 4 0 0 0 3.55 3.97",key:"1d7oge"}],["path",{d:"M6 13H2",key:"82j7cp"}],["path",{d:"m8 2 1.88 1.88",key:"fmnt4t"}],["path",{d:"M9 7.13V6a3 3 0 1 1 6 0v1.13",key:"1vgav8"}]],m=h("bug",x),v=({tone:e="neutral",children:r,title:s})=>o.jsx("span",{className:`pill pill-${e}`,title:s,children:r});async function y(e){const r=new URLSearchParams({mode:e.mode});e.mode==="slice"&&e.windowSec&&r.set("windowSec",String(e.windowSec));const s=`/xenon/api/sessions/${encodeURIComponent(e.sessionId)}/bug-report?${r.toString()}`,t=await fetch(s,{method:"POST"});if(!t.ok){let u=`HTTP ${t.status}`;try{const p=await t.json();p&&p.error&&(u=p.error)}catch{}throw new Error(u)}const a=t.headers.get("content-disposition")||"",n=/filename="([^"]+)"/.exec(a),d=n?n[1]:`bugreport-${e.sessionId}.zip`,c=await t.blob(),l=URL.createObjectURL(c),i=document.createElement("a");i.href=l,i.download=d,document.body.appendChild(i),i.click(),document.body.removeChild(i),URL.revokeObjectURL(l)}const j=({sessionId:e,mode:r,windowSec:s,variant:t})=>{const[a,n]=g.useState(!1),{toast:d}=f();async function c(){if(!a){n(!0);try{await y({sessionId:e,mode:r,windowSec:s}),d("Bug report downloaded","success")}catch(l){d(`Bug report failed: ${l.message}`,"error")}finally{n(!1)}}}return t==="floating"?o.jsxs("button",{type:"button",onClick:c,disabled:a,title:`Capture last ${s??60}s as bug report`,"aria-label":"Generate bug report",className:`fixed bottom-4 right-4 z-30 inline-flex items-center gap-2 px-3 py-2 rounded-full
7
+ bg-[var(--surface-2)] border border-[var(--border)] text-xs text-[var(--text)]
8
+ shadow-lg hover:bg-[var(--surface-raised)] disabled:opacity-50`,children:[a?o.jsx(b,{className:"h-3.5 w-3.5 animate-spin"}):o.jsx(m,{className:"h-3.5 w-3.5"}),"Bug report"]}):o.jsxs("button",{type:"button",onClick:c,disabled:a,title:"Download a zip of this session for ticketing",className:`inline-flex items-center gap-1.5 px-3 py-1.5 rounded text-xs
9
+ bg-[var(--surface-2)] hover:bg-[var(--surface-raised)] border border-[var(--border)]
10
+ text-[var(--text)] disabled:opacity-50`,children:[a?o.jsx(b,{className:"h-3.5 w-3.5 animate-spin"}):o.jsx(m,{className:"h-3.5 w-3.5"}),"Download bug report"]})};export{j as B,v as P};
@@ -0,0 +1 @@
1
+ .pill{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:10px;font-family:Inter,sans-serif;font-size:11px;font-weight:500;line-height:1.4;max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border:1px solid transparent}.pill-neutral{background:var(--surface-2);color:var(--text)}.pill-accent{background:var(--accent-subtle);color:var(--green);border-color:var(--accent-border)}.pill-ready{background:var(--status-ready-bg);color:var(--status-ready-fg);border-color:var(--status-ready-border)}.pill-busy{background:var(--status-busy-bg);color:var(--status-busy-fg);border-color:var(--status-busy-border)}.pill-reserved{background:var(--status-reserved-bg);color:var(--status-reserved-fg);border-color:var(--status-reserved-border)}.pill-error{background:var(--status-error-bg);color:var(--status-error-fg);border-color:var(--status-error-border)}.pill-offline{background:var(--status-offline-bg);color:var(--status-offline-fg);border-color:var(--status-offline-border)}
@@ -0,0 +1,2 @@
1
+ import{r as m,j as t,X as T}from"./index-BrVV0Kla.js";import{A as U,I as le}from"./keycodes-bIEJfQzr.js";function de(e,r){return e!=="auto"?e:r<=1?"1":r===2?"2x1":r<=4?"2x2":"3x2"}const ue={selected:new Set,layout:"2x2",tiles:[],groupId:null,recording:!1,startedAt:null,annotateMode:!1,shape:"RECT",color:"#ff3333",errorBanner:null};function fe(e,r){switch(r.type){case"SET_LAYOUT":return{...e,layout:r.layout};case"TOGGLE_SELECTED":{const n=new Set(e.selected);return n.has(r.udid)?n.delete(r.udid):n.add(r.udid),{...e,selected:n}}case"CLEAR_SELECTED":return{...e,selected:new Set};case"SET_TILES":return{...e,tiles:r.tiles};case"ADD_TILE":return e.tiles.some(n=>n.udid===r.tile.udid)?e:{...e,tiles:[...e.tiles,r.tile]};case"REMOVE_TILE":return{...e,tiles:e.tiles.filter(n=>n.udid!==r.udid)};case"BIND_RECORDING_IDS":{const n=e.tiles.map(l=>({...l,recordingId:r.map[l.udid]??l.recordingId}));return{...e,tiles:n}}case"START_RECORDING":{const n=e.tiles.map(l=>({...l,recordingId:r.tileIds[l.udid]??l.recordingId}));return{...e,groupId:r.groupId,recording:!0,startedAt:r.startedAt,tiles:n}}case"STOP_RECORDING":return{...e,recording:!1};case"SET_ANNOTATE_MODE":return{...e,annotateMode:r.enabled};case"SET_SHAPE":return{...e,shape:r.shape};case"SET_COLOR":return{...e,color:r.color};case"SET_ERROR_BANNER":return{...e,errorBanner:r.message};default:return e}}const V=m.createContext(null);function me(){const e=m.useContext(V);if(!e)throw new Error("useMosaic must be used inside <MosaicContext.Provider>");return e}function xe(){return m.useReducer(fe,ue)}function he(e){switch(e){case"automation":return"In automation";case"manual_other":return"Manual control by another user";case"manual_self":return"In your mosaic";case"recording_other_group":return"Recording in another group";default:return e?"Busy":null}}const G=[{id:"ios",label:"iOS",match:e=>e==="ios"},{id:"android",label:"Android",match:e=>e==="android"},{id:"tvos",label:"tvOS",match:e=>e==="tvos"},{id:"androidtv",label:"Android TV",match:e=>e==="androidtv"||e==="android-tv"},{id:"other",label:"Other",match:()=>!0}];function pe(e){return e?e==="androidtv"||e==="android-tv"?"ANDROID TV":e.toUpperCase():""}function ge({devices:e,inMosaic:r,onToggle:n}){const[l,b]=m.useState(""),[f,w]=m.useState({}),N=m.useMemo(()=>{const u=l.trim().toLowerCase();return u?e.filter(g=>g.udid.toLowerCase().includes(u)||(g.name??"").toLowerCase().includes(u)||(g.platform??"").toLowerCase().includes(u)):e},[e,l]),v=m.useMemo(()=>{const u=G.map(o=>({id:o.id,label:o.label,rows:[]})),g=o=>u.find((c,s)=>G[s].match(o))??u[u.length-1];for(const o of N)g((o.platform??"").toLowerCase()).rows.push(o);return u.filter(o=>o.rows.length>0)},[N]);return e.length===0?t.jsx("div",{className:"text-xs text-[var(--text-dim)] p-3",children:"No devices online."}):t.jsxs("div",{className:"flex flex-col gap-2",children:[t.jsxs("div",{className:"relative",children:[t.jsx("span",{"aria-hidden":!0,className:"absolute left-2 top-1/2 -translate-y-1/2 text-[var(--text-dim)] text-xs",children:"⌕"}),t.jsx("input",{type:"text",value:l,placeholder:"Filter devices...",onChange:u=>b(u.target.value),className:"w-full pl-7 pr-2 py-1.5 text-xs rounded border border-[var(--border)] bg-transparent placeholder:text-[var(--text-dim)] focus:outline-none focus:border-[var(--accent,#3b82f6)]"})]}),v.map(u=>{const g=f[u.id];return t.jsxs("section",{className:"flex flex-col gap-1",children:[t.jsxs("button",{type:"button",onClick:()=>w(o=>({...o,[u.id]:!o[u.id]})),className:"flex items-center justify-between w-full text-[10px] uppercase tracking-wider text-[var(--text-dim)] hover:text-white px-1 py-0.5",children:[t.jsxs("span",{className:"flex items-center gap-1.5",children:[t.jsx("span",{"aria-hidden":!0,className:`inline-block transition-transform ${g?"-rotate-90":"rotate-0"}`,children:"▾"}),u.label]}),t.jsx("span",{children:u.rows.length})]}),!g&&t.jsx("ul",{className:"flex flex-col gap-1 text-sm",children:u.rows.map(o=>{const c=o.busyReason==="manual_self",s=r.has(o.udid),x=!!o.busy&&!c&&!s,R=he(o.busyReason),i=!x;return t.jsx("li",{children:t.jsxs("button",{type:"button",disabled:x,draggable:!x,onDragStart:d=>{d.dataTransfer.effectAllowed="copy",d.dataTransfer.setData("application/x-xenon-udid",o.udid),d.dataTransfer.setData("text/plain",o.udid)},onClick:()=>n(o.udid),className:`w-full flex items-center gap-2 px-2 py-1.5 rounded text-left border transition-colors ${x?"opacity-60 cursor-not-allowed border-transparent":s?"border-emerald-500/40 bg-emerald-500/5 hover:bg-emerald-500/10":"border-transparent hover:border-[var(--border)] hover:bg-[var(--surface-2,rgba(255,255,255,0.04))]"}`,title:x?`${R} — release first`:s?"Click to remove from mosaic (or drag to a cell)":"Click to add to mosaic (or drag to a cell)",children:[t.jsx("span",{"aria-hidden":!0,className:`inline-flex items-center justify-center w-4 h-4 text-xs ${s?"text-emerald-400":"text-[var(--text-dim)]"}`,children:s?"●":"○"}),t.jsx("span",{"aria-hidden":!0,className:`inline-block w-1.5 h-1.5 rounded-full ${i?"bg-emerald-400 shadow-[0_0_4px_rgba(52,211,153,0.7)]":"bg-yellow-500"}`}),t.jsx("span",{className:"font-medium flex-1 truncate text-[13px]",children:o.name??o.udid}),o.platform&&t.jsx("span",{className:"text-[9px] uppercase tracking-wider px-1.5 py-0.5 rounded bg-[var(--surface-2,rgba(255,255,255,0.06))] text-[var(--text-dim)]",children:pe(o.platform)})]})},o.udid)})})]},u.id)}),l&&v.length===0&&t.jsxs("div",{className:"text-xs text-[var(--text-dim)] px-1 py-2",children:['No devices match "',l,'".']})]})}const be=[{id:"1",label:"1",icon:t.jsx(ye,{})},{id:"2x1",label:"2×1",icon:t.jsx(we,{})},{id:"2x2",label:"2×2",icon:t.jsx(Ne,{})},{id:"3x2",label:"3×2",icon:t.jsx(je,{})},{id:"auto",label:"Auto",icon:t.jsx(Re,{})}];function ve({value:e,onChange:r}){return t.jsx("div",{role:"tablist",className:"inline-flex border border-[var(--border)] rounded-md overflow-hidden text-xs bg-[var(--surface-1,rgba(255,255,255,0.02))]",children:be.map(n=>{const l=e===n.id;return t.jsxs("button",{role:"tab","aria-selected":l,onClick:()=>r(n.id),className:`flex items-center gap-1.5 px-2.5 py-1.5 border-r border-[var(--border)] last:border-r-0 transition-colors ${l?"bg-[var(--surface-2,#2a2a2a)] text-white":"text-[var(--text-dim)] hover:bg-[var(--surface-2,rgba(255,255,255,0.04))] hover:text-white"}`,title:`Layout: ${n.label}`,children:[t.jsx("span",{className:"w-3.5 h-3.5 inline-flex items-center justify-center",children:n.icon}),t.jsx("span",{children:n.label})]},n.id)})})}function k(e){return t.jsx("rect",{rx:1.2,ry:1.2,fill:"currentColor",...e})}function ye(){return t.jsx("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:k({x:2,y:2,width:10,height:10})})}function we(){return t.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:[k({x:1.5,y:3,width:5,height:8}),k({x:7.5,y:3,width:5,height:8})]})}function Ne(){return t.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:[k({x:1.5,y:1.5,width:5,height:5}),k({x:7.5,y:1.5,width:5,height:5}),k({x:1.5,y:7.5,width:5,height:5}),k({x:7.5,y:7.5,width:5,height:5})]})}function je(){return t.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:[k({x:1,y:1.5,width:3.3,height:5}),k({x:5.35,y:1.5,width:3.3,height:5}),k({x:9.7,y:1.5,width:3.3,height:5}),k({x:1,y:7.5,width:3.3,height:5}),k({x:5.35,y:7.5,width:3.3,height:5}),k({x:9.7,y:7.5,width:3.3,height:5})]})}function Re(){return t.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",fill:"none",stroke:"currentColor",strokeWidth:"1.4",children:[t.jsx("rect",{x:"2",y:"2",width:"10",height:"10",rx:"1.5"}),t.jsx("circle",{cx:"7",cy:"7",r:"1.5",fill:"currentColor",stroke:"none"})]})}function Ee({enabled:e,shape:r,color:n,onCommit:l}){const b=m.useRef(null),[f,w]=m.useState(null);m.useEffect(()=>{const c=b.current;if(!c)return;const s=()=>{c.width=c.clientWidth,c.height=c.clientHeight,N()};s();const x=new ResizeObserver(s);return x.observe(c),()=>x.disconnect()},[]);const N=m.useCallback(()=>{const c=b.current;if(!c)return;const s=c.getContext("2d");if(s&&(s.clearRect(0,0,c.width,c.height),!!f)){if(s.strokeStyle=n,s.lineWidth=3,r==="RECT"||r==="ARROW"||r==="FREEHAND")s.strokeRect(Math.min(f.startX,f.curX),Math.min(f.startY,f.curY),Math.abs(f.curX-f.startX),Math.abs(f.curY-f.startY));else if(r==="CIRCLE"){const x=Math.hypot(f.curX-f.startX,f.curY-f.startY);s.beginPath(),s.arc(f.startX,f.startY,x,0,Math.PI*2),s.stroke()}}},[f,n,r]);m.useEffect(()=>{N()},[f,N]);const v=c=>{const x=b.current.getBoundingClientRect();return{px:c.clientX-x.left,py:c.clientY-x.top,w:x.width,h:x.height}},u=c=>{if(!e)return;const s=v(c);w({startX:s.px,startY:s.py,curX:s.px,curY:s.py})},g=c=>{if(!f)return;const s=v(c);w({...f,curX:s.px,curY:s.py})},o=c=>{if(!f||!e)return;const s=v(c),x=Math.min(f.startX,s.px)/s.w,R=Math.min(f.startY,s.py)/s.h,i=Math.abs(s.px-f.startX)/s.w,d=Math.abs(s.py-f.startY)/s.h;i>.005&&d>.005&&l({shape:r,color:n,geometry:{x,y:R,w:i,h:d}}),w(null)};return t.jsx("canvas",{ref:b,style:{position:"absolute",inset:0,pointerEvents:e?"auto":"none",cursor:e?"crosshair":"default"},onMouseDown:u,onMouseMove:g,onMouseUp:o,onMouseLeave:()=>w(null)})}const Te=10,Se=500;function Ce({udid:e,name:r,recordingId:n,annotateMode:l,shape:b,color:f,aspect:w="9 / 16",screenWidth:N,screenHeight:v,platform:u,onAnnotation:g,onRemove:o}){const[c,s]=m.useState("connecting"),[x,R]=m.useState(0),[i,d]=m.useState([]),[p,j]=m.useState(!1),O=m.useRef(null),_=m.useRef(null),y=u==="android"||u==="androidtv"||u==="android-tv",S=u==="ios"||u==="tvos",E=!l&&!!N&&!!v,X=(a,h,C)=>{const A=N,D=v,M=Math.min(1,Math.max(0,(a-C.left)/C.width)),I=Math.min(1,Math.max(0,(h-C.top)/C.height));return{x:Math.round(M*A),y:Math.round(I*D)}},Z=(a,h,C)=>{const A=Date.now()+Math.random(),D=a-C.left,M=h-C.top;d(I=>[...I,{id:A,x:D,y:M}]),window.setTimeout(()=>{d(I=>I.filter(B=>B.id!==A))},500)},Q=a=>{if(!E||c!=="live"||a.button!==0)return;const h=a.currentTarget;h.setPointerCapture(a.pointerId),_.current={startX:a.clientX,startY:a.clientY,startTime:Date.now(),rect:h.getBoundingClientRect()},Z(a.clientX,a.clientY,_.current.rect),h.focus()},ee=async a=>{var H,K;const h=_.current;if(_.current=null,!h||!E)return;try{a.currentTarget.releasePointerCapture(a.pointerId)}catch{}const C=a.clientX-h.startX,A=a.clientY-h.startY,D=Math.hypot(C,A),M=Date.now()-h.startTime,I=X(h.startX,h.startY,h.rect),B=D<Te;try{if(B&&M<Se)await T.tap(e,I.x,I.y);else if(B)await((K=(H=T).touchAndHold)==null?void 0:K.call(H,e,I.x,I.y,M));else{const P=X(a.clientX,a.clientY,h.rect);await T.swipe(e,I.x,I.y,P.x,P.y)}}catch(P){console.error(`[DeviceTile] gesture failed for ${e}`,P)}},te=()=>{_.current=null},re=async a=>{if(!E||c!=="live"||a.metaKey||a.ctrlKey||a.altKey)return;const h=a.key;try{if(h.length===1){a.preventDefault(),await T.typeText(e,h);return}if(h==="Backspace"){a.preventDefault(),y?await T.pressKey(e,67):await T.typeText(e,"\b");return}if(h==="Enter"){a.preventDefault(),y?await T.pressKey(e,66):await T.typeText(e,`
2
+ `);return}if(h==="Tab"){a.preventDefault(),await T.typeText(e," ");return}h==="Escape"&&y&&(a.preventDefault(),await T.pressKey(e,U.BACK))}catch(C){console.error(`[DeviceTile] keystroke "${h}" failed for ${e}`,C)}},ne=()=>T.pressKey(e,y?U.HOME:le.HOME).catch(a=>console.error(`[DeviceTile] home failed for ${e}`,a)),se=()=>T.pressKey(e,U.BACK).catch(a=>console.error(`[DeviceTile] back failed for ${e}`,a)),oe=()=>T.pressKey(e,U.APP_SWITCH).catch(a=>console.error(`[DeviceTile] app-switch failed for ${e}`,a)),ae=async()=>{try{const a=await T.getScreenshot(e),h=(a==null?void 0:a.screenshot)??(a==null?void 0:a.value)??a;if(!h)return;const C=await fetch(`data:image/png;base64,${h}`).then(M=>M.blob()),A=URL.createObjectURL(C),D=document.createElement("a");D.href=A,D.download=`${r||e}-${Date.now()}.png`,document.body.appendChild(D),D.click(),D.remove(),setTimeout(()=>URL.revokeObjectURL(A),1e3)}catch(a){console.error(`[DeviceTile] screenshot failed for ${e}`,a)}};m.useEffect(()=>{console.log(`[DeviceTile] Rendering ${e} in state: ${c}`)},[e,c]);const ie=`/xenon/api/control/${encodeURIComponent(e)}/stream?t=${x}`;m.useEffect(()=>{if(c!=="connecting")return;const a=setTimeout(()=>{s(h=>h==="connecting"?"unavailable":h)},9e4);return()=>clearTimeout(a)},[c,x]);const ce=()=>{s("connecting"),R(Date.now())},L=!!n,W=r||e.slice(0,16)+"…";return t.jsxs("div",{className:"flex items-stretch justify-center w-full h-full min-h-0 min-w-0 gap-2",children:[t.jsxs("div",{className:"relative bg-[#000] rounded-lg overflow-hidden border border-[var(--border)] group max-h-full max-w-full self-center",style:{aspectRatio:w,height:"100%"},children:[c==="connecting"&&t.jsxs("div",{className:"flex flex-col items-center justify-center bg-neutral-900 text-neutral-400 absolute inset-0 z-20",children:[t.jsxs("div",{className:"relative flex items-center justify-center",children:[t.jsx("div",{className:"w-12 h-12 border-4 border-neutral-800 border-t-red-500 rounded-full animate-spin"}),t.jsx("div",{className:"absolute text-[10px] text-red-500 font-bold",children:"WDA"})]}),t.jsx("div",{className:"mt-4 font-bold text-neutral-200",children:"Starting Stream…"}),t.jsx("div",{className:"text-[10px] mt-1 text-neutral-500 opacity-60 font-mono",children:e}),t.jsx("div",{className:"text-[10px] mt-4 text-neutral-400 bg-white/5 px-2 py-1 rounded",children:"Waiting for device connection..."})]}),c==="unavailable"&&t.jsx("div",{className:"flex items-center justify-center bg-black text-neutral-400 absolute inset-0 z-20",children:t.jsxs("div",{className:"text-center p-6",children:[t.jsx("div",{className:"text-5xl mb-4 opacity-20",children:"📵"}),t.jsx("div",{className:"font-bold text-white text-lg",children:"Connection Failed"}),t.jsx("p",{className:"text-xs mt-2 text-neutral-500 leading-relaxed max-w-[240px] mx-auto",children:"We couldn't connect to the WDA MJPEG stream on port 9100. If automation is running, ensure it hasn't blocked the MJPEG port."}),t.jsx("button",{className:"mt-6 text-sm font-semibold px-6 py-2.5 rounded-full bg-red-600 hover:bg-red-500 text-white transition-all shadow-[0_0_20px_rgba(220,38,38,0.3)] active:scale-95",onClick:ce,children:"Retry Connection"})]})}),t.jsx("img",{src:ie,alt:W,className:"absolute inset-0 w-full h-full object-contain bg-black select-none pointer-events-none",draggable:!1,style:{opacity:c==="live"?1:0,transition:"opacity 0.3s ease-in-out"},onLoad:()=>{console.log(`[DeviceTile] Image LOADED for ${e}`),s("live")},onError:a=>{console.error(`[DeviceTile] Image ERROR for ${e}`,a),s("unavailable")}},x),E&&c==="live"&&t.jsx("div",{ref:O,className:`absolute inset-0 z-10 touch-none outline-none ${p?"ring-2 ring-emerald-400/60 ring-inset rounded-lg":""}`,tabIndex:0,onPointerDown:Q,onPointerUp:ee,onPointerCancel:te,onKeyDown:re,onFocus:()=>j(!0),onBlur:()=>j(!1)}),t.jsx("div",{className:"absolute inset-0 z-20 pointer-events-none overflow-hidden",children:i.map(a=>t.jsx("span",{className:"absolute block w-10 h-10 rounded-full border-2 border-emerald-300/80 animate-ping",style:{left:a.x-20,top:a.y-20}},a.id))}),L&&l&&t.jsx(Ee,{enabled:!0,shape:b,color:f,onCommit:a=>g(n,a)}),L&&t.jsx("div",{className:"absolute top-3 left-3 z-30 pointer-events-none",children:t.jsx("span",{className:"px-2 py-0.5 rounded-sm bg-red-600 text-white text-[9px] font-black shadow-lg",children:"REC"})}),t.jsxs("div",{className:"absolute top-3 left-3 z-30 flex flex-col gap-1.5 pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity duration-150",children:[c==="live"&&!L&&t.jsx("span",{className:"px-2 py-0.5 rounded-sm bg-emerald-500 text-black text-[9px] font-black shadow-lg w-fit",children:"LIVE"}),t.jsx("span",{className:"px-2 py-1 rounded bg-black/80 text-white text-[11px] font-medium backdrop-blur-md border border-white/10 shadow-xl truncate max-w-[180px]",children:W})]})]}),t.jsx(ke,{streamState:c,recording:L,isIOS:S,isAndroid:y,platform:u,onClose:!L&&o?()=>o(e):void 0,sendHome:ne,sendBack:se,sendAppSwitch:oe,captureScreenshot:ae,displayName:W})]})}function ke({streamState:e,recording:r,isIOS:n,isAndroid:l,platform:b,onClose:f,sendHome:w,sendBack:N,sendAppSwitch:v,captureScreenshot:u,displayName:g}){const o=e==="live";return!f&&!o?null:t.jsxs("div",{className:"self-stretch w-9 flex flex-col items-center justify-between gap-1 py-2 rounded-lg bg-black/40 border border-white/10 backdrop-blur-md text-white",children:[t.jsx("div",{className:"flex flex-col items-center gap-1",children:f&&t.jsx("button",{type:"button","aria-label":`Remove ${g} from mosaic`,title:"Remove from mosaic",onClick:f,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-red-600/70 transition-colors text-base leading-none",children:"×"})}),o&&t.jsxs("div",{className:"flex flex-col items-center gap-1.5 my-auto",children:[(l||n)&&t.jsx("button",{type:"button",title:`Home${b?` (${b})`:""}`,onClick:w,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/15 transition-colors text-sm",children:"⌂"}),l&&t.jsx("button",{type:"button",title:"Back",onClick:N,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/15 transition-colors text-sm",children:"‹"}),l&&t.jsx("button",{type:"button",title:"Recent apps",onClick:v,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/15 transition-colors text-sm",children:"▢"})]}),o&&t.jsx("div",{className:"flex flex-col items-center gap-1",children:t.jsx("button",{type:"button",title:"Screenshot",onClick:u,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-white/15 transition-colors text-sm",children:"⎙"})})]})}const Ie={1:"1fr","2x1":"1fr 1fr","2x2":"1fr 1fr","3x2":"1fr 1fr 1fr"},De={1:"1fr","2x1":"1fr","2x2":"1fr 1fr","3x2":"1fr 1fr"},_e={1:1,"2x1":2,"2x2":4,"3x2":6};function Ae({layout:e,tiles:r,annotateMode:n,shape:l,color:b,onAnnotation:f,onRemove:w,onDropDevice:N}){const v=de(e,r.length),u=_e[v];r.length;const g=r.length,o=e==="auto"?Math.max(g,1):u,c=Math.max(0,o-g);return t.jsxs("div",{style:{display:"grid",gap:8,gridTemplateColumns:Ie[v],gridTemplateRows:De[v]},className:"h-full",children:[r.map(s=>t.jsx(Ce,{udid:s.udid,name:s.name,mjpegPort:s.mjpegPort,recordingId:s.recordingId,aspect:s.aspect,screenWidth:s.screenWidth,screenHeight:s.screenHeight,platform:s.platform,annotateMode:n,shape:l,color:b,onAnnotation:f,onRemove:w},s.udid)),Array.from({length:c},(s,x)=>t.jsx(Me,{onDropDevice:N},`empty-${x}`))]})}function Me({onDropDevice:e}){const[r,n]=m.useState(!1);return t.jsxs("div",{onDragOver:l=>{e&&(l.preventDefault(),l.dataTransfer.dropEffect="copy",r||n(!0))},onDragLeave:()=>n(!1),onDrop:l=>{if(n(!1),!e)return;l.preventDefault();const b=l.dataTransfer.getData("application/x-xenon-udid")||l.dataTransfer.getData("text/plain");b&&e(b)},className:`flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed transition-colors ${r?"border-emerald-500/60 bg-emerald-500/5":"border-[var(--border)] bg-[var(--surface-1,rgba(255,255,255,0.02))]"} text-[var(--text-dim)] min-h-[120px]`,children:[t.jsx("div",{className:`w-8 h-8 rounded-full flex items-center justify-center text-xl ${r?"bg-emerald-500/15 text-emerald-300":"bg-white/5 text-white/60"}`,children:"+"}),t.jsx("div",{className:"text-xs font-medium",children:"Drag device here"}),t.jsx("div",{className:"text-[10px] opacity-70",children:"or click one in the panel"})]})}const $="/xenon/api/recordings";class J extends Error{constructor(r){super(r.message??r.error),this.body=r}}async function Y(e,r){const n=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:r?JSON.stringify(r):void 0});if(n.status===409)throw new J(await n.json());if(!n.ok)throw new Error(`HTTP ${n.status}`);return n.json()}function Oe(e,r){return Y($,{udids:e,...r})}function $e(e){return Y(`${$}/${encodeURIComponent(e)}/stop`)}function q(e,r,n,l,b){return Y(`${$}/${encodeURIComponent(e)}/bookmark`,{recordingId:r,timecodeMs:n,label:l,note:b})}function Le(e,r){return Y(`${$}/${encodeURIComponent(e)}/annotation`,r)}function Be(e){return`${$}/${encodeURIComponent(e)}/bundle.zip`}function Pe(e){return`${$}/${encodeURIComponent(e)}/composite.mp4`}function Ue({selectedUdids:e}){const{state:r,dispatch:n}=me(),l=async()=>{if(!(r.recording||e.length===0))try{const o=await Oe(e),c={};for(const s of o.recordings)c[s.udid]=s.id;n({type:"START_RECORDING",groupId:o.groupId,startedAt:Date.now(),tileIds:c}),n({type:"SET_ERROR_BANNER",message:null})}catch(o){if(o instanceof J)if(o.body.error==="device_busy"){const c=(o.body.busyDevices??[]).map(s=>`${s.udid} (${s.reason})`).join(", ");n({type:"SET_ERROR_BANNER",message:`Cannot start — these devices are busy: ${c}`})}else n({type:"SET_ERROR_BANNER",message:`Server-wide recording cap reached (${o.body.active}/${o.body.limit}).`});else n({type:"SET_ERROR_BANNER",message:`Recording failed: ${o.message}`})}},b=async()=>{if(r.groupId)try{await $e(r.groupId)}catch(o){n({type:"SET_ERROR_BANNER",message:`Stop failed: ${o.message}`})}finally{n({type:"STOP_RECORDING"})}},f=async()=>{var x;if(!r.groupId||!r.recording)return;const o=window.prompt("Bookmark label?");if(!o)return;const c=r.startedAt?Date.now()-r.startedAt:0,s=(x=r.tiles.find(R=>R.recordingId))==null?void 0:x.recordingId;if(s)try{await q(r.groupId,s,c,o)}catch(R){n({type:"SET_ERROR_BANNER",message:`Bookmark failed: ${R.message}`})}},w=()=>n({type:"SET_ANNOTATE_MODE",enabled:!r.annotateMode}),N=!r.recording&&e.length>0,v=r.recording,u=r.groupId&&!r.recording,g=u&&r.tiles.length>=2;return t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsxs("button",{onClick:l,disabled:!N,className:"px-3 py-1.5 text-sm rounded bg-red-600 text-white disabled:opacity-40 disabled:cursor-not-allowed",children:["● ",e.length>1?"Record All":"Record"]}),t.jsx("button",{onClick:b,disabled:!v,className:"px-3 py-1.5 text-sm rounded border border-[var(--border)] disabled:opacity-40 disabled:cursor-not-allowed",children:"⏹ Stop"}),t.jsx("button",{onClick:f,disabled:!v,className:"px-3 py-1.5 text-sm rounded border border-[var(--border)] disabled:opacity-40 disabled:cursor-not-allowed",children:"🔖 Bookmark"}),t.jsx("button",{onClick:w,disabled:!r.recording,className:`px-3 py-1.5 text-sm rounded border ${r.annotateMode?"bg-[var(--accent,#3b82f6)] text-white border-transparent":"border-[var(--border)]"} disabled:opacity-40 disabled:cursor-not-allowed`,children:"✎ Annotate"}),g&&t.jsx("a",{href:Pe(r.groupId),className:"ml-2 px-3 py-1.5 text-sm rounded border border-[var(--border)] hover:bg-[var(--surface-2,#1a1a1a)]",download:`mosaic-${r.groupId}.mp4`,children:"⤓ Composite mp4"}),u&&t.jsx("a",{href:Be(r.groupId),className:`${g?"ml-1":"ml-2"} px-3 py-1.5 text-sm rounded border border-[var(--border)] hover:bg-[var(--surface-2,#1a1a1a)]`,download:!0,children:"⤓ Proof bundle"})]})}function Ye({remainingSec:e,onContinue:r,onReleaseNow:n}){return t.jsx("div",{role:"dialog","aria-modal":"true","aria-labelledby":"idle-warning-title",className:"fixed inset-0 z-40 bg-black/60 flex items-center justify-center",children:t.jsxs("div",{className:"bg-[var(--bg)] border border-[var(--border)] rounded-lg w-full max-w-md p-6",children:[t.jsx("h3",{id:"idle-warning-title",className:"text-base font-semibold mb-2",children:"Are you still there?"}),t.jsxs("p",{className:"text-sm text-[var(--text-dim)] mb-4",children:["Your devices will be released in"," ",t.jsxs("span",{className:"text-[var(--text)] font-mono font-semibold",children:[e,"s"]})," due to inactivity. Click ",t.jsx("strong",{children:"Continue Session"})," to keep them locked."]}),t.jsxs("div",{className:"flex justify-end gap-2",children:[t.jsx("button",{type:"button",onClick:n,className:"h-9 px-3 rounded-md border border-[var(--border)] text-sm text-[var(--text-dim)] hover:text-[var(--text)]",children:"Release now"}),t.jsx("button",{type:"button",autoFocus:!0,onClick:r,className:"h-9 px-4 rounded-md bg-[var(--green)] text-black text-sm font-medium",children:"Continue Session"})]})]})})}const F=["mousemove","mousedown","keydown","touchstart","wheel"];function We({idleAfterMs:e,warningSec:r,onWarning:n,onTimeout:l,enabled:b}){const[f,w]=m.useState(!1),[N,v]=m.useState(null),u=m.useRef(null),g=m.useRef(null),o=m.useRef(!1),c=m.useRef(n),s=m.useRef(l);c.current=n,s.current=l;function x(){u.current&&(clearTimeout(u.current),u.current=null),g.current&&(clearInterval(g.current),g.current=null)}function R(){o.current=!0,w(!0),v(r),c.current(r);let p=r;g.current=setInterval(()=>{p-=1,p<=0?(x(),o.current=!1,w(!1),v(null),s.current()):v(p)},1e3)}function i(){x(),o.current=!1,w(!1),v(null);const p=e-r*1e3;u.current=setTimeout(R,Math.max(0,p))}function d(){b&&i()}return m.useEffect(()=>{if(!b){x(),o.current=!1,w(!1),v(null);return}function p(){o.current||i()}return F.forEach(j=>window.addEventListener(j,p,{passive:!0})),i(),()=>{F.forEach(j=>window.removeEventListener(j,p)),x()}},[b,e,r]),{warning:f,remainingSec:N,reset:d}}const Xe=5*60*1e3,He=30;function z(e){if(!e)return"9 / 16";const r=Number(e.screenWidth),n=Number(e.screenHeight);if(Number.isFinite(r)&&Number.isFinite(n)&&r>0&&n>0)return`${r} / ${n}`;const l=(e.platform||"").toLowerCase();return l==="tvos"||l==="androidtv"||l==="android-tv"?"16 / 9":"9 / 16"}function Ke(e,r,n){return n?e===`manual_${n}_${r}`:!1}function Ge(e,r){if(e.busy)return e.session_id?e.session_id.startsWith("manual_")?Ke(e.session_id,e.udid,r)?"manual_self":"manual_other":"automation":"unknown"}function Fe(e,r){return{udid:e.udid,name:e.name,platform:e.platform,busy:e.busy,busyReason:Ge(e,r),mjpegServerPort:e.mjpegServerPort}}function Je(){const[e,r]=xe(),[n,l]=m.useState([]),[b,f]=m.useState(0),[w,N]=m.useState(null);m.useEffect(()=>{let i=!1;return fetch("/xenon/api/auth/me",{credentials:"include"}).then(d=>d.ok?d.json():null).then(d=>{!i&&(d!=null&&d.userId)&&N(d.userId)}).catch(()=>{}),()=>{i=!0}},[]),m.useEffect(()=>{let i=!1;const d=async()=>{try{const j=await T.getDevices();i||l(Array.isArray(j)?j:[])}catch{}};d();const p=window.setInterval(d,5e3);return()=>{i=!0,window.clearInterval(p)}},[b]),m.useEffect(()=>{let i=!1;return(async()=>{try{const d=await T.getDevices(),p=(Array.isArray(d)?d:[]).filter(y=>{var S;return y.busy&&((S=y.session_id)==null?void 0:S.startsWith("manual_"))});if(p.length===0)return;const O=(await Promise.all(p.map(async y=>{try{const S=await fetch(`/xenon/api/control/${encodeURIComponent(y.udid)}/stream/status`);if(!S.ok)return null;const E=await S.json();return(E==null?void 0:E.status)==="running"?y:null}catch{return null}}))).filter(y=>y!==null);if(i||O.length===0)return;const _=O.map(y=>{const S=Number(y.screenWidth),E=Number(y.screenHeight);return{udid:y.udid,name:y.name,mjpegPort:0,aspect:z(y),screenWidth:Number.isFinite(S)&&S>0?S:void 0,screenHeight:Number.isFinite(E)&&E>0?E:void 0,platform:y.platform}});r({type:"SET_TILES",tiles:_})}catch{}})(),()=>{i=!0}},[]),m.useEffect(()=>{const i=async d=>{var y,S;if(d.key!=="b"&&d.key!=="B")return;const p=(y=d.target)==null?void 0:y.tagName;if(p==="INPUT"||p==="TEXTAREA"||p==="SELECT"||!e.recording||!e.groupId)return;const j=window.prompt("Bookmark label?");if(!j)return;const O=e.startedAt?Date.now()-e.startedAt:0,_=(S=e.tiles.find(E=>E.recordingId))==null?void 0:S.recordingId;if(_)try{await q(e.groupId,_,O,j)}catch(E){r({type:"SET_ERROR_BANNER",message:`Bookmark failed: ${E.message}`})}};return window.addEventListener("keydown",i),()=>window.removeEventListener("keydown",i)},[e.recording,e.groupId,e.startedAt,e.tiles,r]);const v=m.useMemo(()=>new Set(e.tiles.map(i=>i.udid)),[e.tiles]),u=m.useMemo(()=>n.map(i=>Fe(i,w)),[n,w]),g=i=>{const d=Number(i.screenWidth),p=Number(i.screenHeight);return{udid:i.udid,name:i.name,mjpegPort:0,aspect:z(i),screenWidth:Number.isFinite(d)&&d>0?d:void 0,screenHeight:Number.isFinite(p)&&p>0?p:void 0,platform:i.platform}},o=async i=>{if(e.tiles.some(j=>j.udid===i)){await c(i);return}if(e.recording){r({type:"SET_ERROR_BANNER",message:"Stop recording before changing the mosaic layout."});return}const p=n.find(j=>j.udid===i);p&&(r({type:"ADD_TILE",tile:g(p)}),fetch(`/xenon/api/control/${encodeURIComponent(i)}/stream/start`,{method:"POST"}).catch(()=>{}))},c=async i=>{r({type:"REMOVE_TILE",udid:i});try{await fetch(`/xenon/api/control/${encodeURIComponent(i)}/stream/stop`,{method:"POST"})}catch{}f(d=>d+1)},s=async(i,d)=>{if(e.groupId)try{await Le(e.groupId,{recordingId:i,timecodeMs:e.startedAt?Date.now()-e.startedAt:0,shape:d.shape,geometry:JSON.stringify(d.geometry),color:d.color,text:d.text})}catch(p){r({type:"SET_ERROR_BANNER",message:`Annotation failed: ${p.message}`})}},x=()=>{for(const i of e.tiles){const d=`/xenon/api/control/${encodeURIComponent(i.udid)}/stream/stop`;try{navigator.sendBeacon?navigator.sendBeacon(d):fetch(d,{method:"POST",keepalive:!0}).catch(()=>{})}catch{}}e.tiles.forEach(i=>r({type:"REMOVE_TILE",udid:i.udid}))},R=We({idleAfterMs:Xe,warningSec:He,enabled:e.tiles.length>0,onWarning:()=>{},onTimeout:()=>{x()}});return t.jsxs(V.Provider,{value:{state:e,dispatch:r},children:[t.jsxs("div",{className:"flex flex-col h-full p-4 gap-3",children:[t.jsxs("header",{className:"flex items-center justify-between gap-3",children:[t.jsxs("div",{className:"flex items-center gap-3",children:[t.jsx("h1",{className:"text-base font-semibold",children:"Live Devices"}),t.jsx(ve,{value:e.layout,onChange:i=>r({type:"SET_LAYOUT",layout:i})})]}),t.jsx(Ue,{selectedUdids:e.tiles.map(i=>i.udid)})]}),e.errorBanner&&t.jsxs("div",{role:"alert",className:"text-sm rounded border border-red-500/40 bg-red-500/10 text-red-100 px-3 py-2 flex items-center gap-2",children:[t.jsx("span",{className:"flex-1",children:e.errorBanner}),t.jsx("button",{className:"text-xs opacity-80 hover:opacity-100",onClick:()=>r({type:"SET_ERROR_BANNER",message:null}),children:"Dismiss"})]}),t.jsxs("div",{className:"grid grid-cols-[260px_1fr] gap-3 flex-1 min-h-0",children:[t.jsxs("aside",{className:"flex flex-col gap-2 border border-[var(--border)] rounded p-3 overflow-y-auto min-h-0",children:[t.jsxs("div",{className:"flex items-center justify-between",children:[t.jsx("span",{className:"text-xs uppercase tracking-wide text-[var(--text-dim)]",children:"Devices"}),t.jsx("button",{className:"text-xs underline opacity-70 hover:opacity-100",onClick:()=>f(i=>i+1),children:"Refresh"})]}),t.jsx(ge,{devices:u,inMosaic:v,onToggle:o}),t.jsx("p",{className:"mt-2 text-[11px] text-[var(--text-dim)] leading-relaxed",children:"Click a device to add it to the mosaic. Click again (or use the × on the tile) to remove it."})]}),t.jsx("main",{className:"border border-[var(--border)] rounded p-2 overflow-y-auto h-full min-h-0",children:t.jsx(Ae,{layout:e.layout,tiles:e.tiles,annotateMode:e.annotateMode,shape:e.shape,color:e.color,onAnnotation:s,onRemove:c,onDropDevice:o})})]})]}),R.warning&&R.remainingSec!==null&&t.jsx(Ye,{remainingSec:R.remainingSec,onContinue:R.reset,onReleaseNow:x})]})}export{Je as default};
@@ -0,0 +1 @@
1
+ .empty-state{display:flex;flex-direction:column;align-items:center;text-align:center;padding:40px 24px;color:var(--text-muted);gap:8px}.empty-state-icon{color:var(--text-dim);margin-bottom:4px}.empty-state-title{color:var(--text);font-size:14px;font-weight:600}.empty-state-desc{color:var(--text-muted);font-size:12px;max-width:360px}.empty-state-action{margin-top:12px}
@@ -0,0 +1 @@
1
+ import{j as s}from"./index-BrVV0Kla.js";const i=({icon:e,title:m,description:t,action:a})=>s.jsxs("div",{className:"empty-state",children:[e&&s.jsx("div",{className:"empty-state-icon",children:e}),s.jsx("div",{className:"empty-state-title",children:m}),t&&s.jsx("div",{className:"empty-state-desc",children:t}),a&&s.jsx("div",{className:"empty-state-action",children:a})]});export{i as E};
@@ -0,0 +1 @@
1
+ import{j as s}from"./index-BrVV0Kla.js";const r=({label:e,description:l,children:c,error:a,htmlFor:d})=>s.jsxs("div",{className:"fg",children:[s.jsx("label",{className:"fg-label",htmlFor:d,children:e}),l&&s.jsx("div",{className:"fg-desc",children:l}),s.jsx("div",{className:"fg-control",children:c}),a&&s.jsx("div",{className:"fg-error",children:a})]});export{r as F};
@@ -0,0 +1 @@
1
+ .fg{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}.fg-label{color:var(--text);font-size:12px;font-weight:600;font-family:Inter,sans-serif}.fg-desc{color:var(--text-muted);font-size:11px;line-height:1.4}.fg-control{display:flex;flex-direction:column}.fg-error{color:var(--status-error-fg);font-size:11px}
@@ -0,0 +1,45 @@
1
+ import{r as i,j as h,h as at,R as xe,i as it,n as st}from"./index-BrVV0Kla.js";function me(e,t){if(typeof e=="function")return e(t);e!=null&&(e.current=t)}function De(...e){return t=>{let n=!1;const r=e.map(o=>{const a=me(o,t);return!n&&typeof a=="function"&&(n=!0),a});if(n)return()=>{for(let o=0;o<r.length;o++){const a=r[o];typeof a=="function"?a():me(e[o],null)}}}}function I(...e){return i.useCallback(De(...e),e)}function Oe(e){const t=ct(e),n=i.forwardRef((r,o)=>{const{children:a,...c}=r,s=i.Children.toArray(a),d=s.find(lt);if(d){const u=d.props.children,f=s.map(v=>v===d?i.Children.count(u)>1?i.Children.only(null):i.isValidElement(u)?u.props.children:null:v);return h.jsx(t,{...c,ref:o,children:i.isValidElement(u)?i.cloneElement(u,void 0,f):null})}return h.jsx(t,{...c,ref:o,children:a})});return n.displayName=`${e}.Slot`,n}function ct(e){const t=i.forwardRef((n,r)=>{const{children:o,...a}=n;if(i.isValidElement(o)){const c=ft(o),s=dt(a,o.props);return o.type!==i.Fragment&&(s.ref=r?De(r,c):c),i.cloneElement(o,s)}return i.Children.count(o)>1?i.Children.only(null):null});return t.displayName=`${e}.SlotClone`,t}var ut=Symbol("radix.slottable");function lt(e){return i.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===ut}function dt(e,t){const n={...t};for(const r in t){const o=e[r],a=t[r];/^on[A-Z]/.test(r)?o&&a?n[r]=(...s)=>{const d=a(...s);return o(...s),d}:o&&(n[r]=o):r==="style"?n[r]={...o,...a}:r==="className"&&(n[r]=[o,a].filter(Boolean).join(" "))}return{...e,...n}}function ft(e){var r,o;let t=(r=Object.getOwnPropertyDescriptor(e.props,"ref"))==null?void 0:r.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=(o=Object.getOwnPropertyDescriptor(e,"ref"))==null?void 0:o.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}var vt=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"],D=vt.reduce((e,t)=>{const n=Oe(`Primitive.${t}`),r=i.forwardRef((o,a)=>{const{asChild:c,...s}=o,d=c?n:t;return typeof window<"u"&&(window[Symbol.for("radix-ui")]=!0),h.jsx(d,{...s,ref:a})});return r.displayName=`Primitive.${t}`,{...e,[t]:r}},{});function mt(e,t){e&&at.flushSync(()=>e.dispatchEvent(t))}function ht(e,t){const n=i.createContext(t),r=a=>{const{children:c,...s}=a,d=i.useMemo(()=>s,Object.values(s));return h.jsx(n.Provider,{value:d,children:c})};r.displayName=e+"Provider";function o(a){const c=i.useContext(n);if(c)return c;if(t!==void 0)return t;throw new Error(`\`${a}\` must be used within \`${e}\``)}return[r,o]}function pt(e,t=[]){let n=[];function r(a,c){const s=i.createContext(c),d=n.length;n=[...n,c];const u=v=>{var y;const{scope:m,children:g,...w}=v,l=((y=m==null?void 0:m[e])==null?void 0:y[d])||s,p=i.useMemo(()=>w,Object.values(w));return h.jsx(l.Provider,{value:p,children:g})};u.displayName=a+"Provider";function f(v,m){var l;const g=((l=m==null?void 0:m[e])==null?void 0:l[d])||s,w=i.useContext(g);if(w)return w;if(c!==void 0)return c;throw new Error(`\`${v}\` must be used within \`${a}\``)}return[u,f]}const o=()=>{const a=n.map(c=>i.createContext(c));return function(s){const d=(s==null?void 0:s[e])||a;return i.useMemo(()=>({[`__scope${e}`]:{...s,[e]:d}}),[s,d])}};return o.scopeName=e,[r,gt(o,...t)]}function gt(...e){const t=e[0];if(e.length===1)return t;const n=()=>{const r=e.map(o=>({useScope:o(),scopeName:o.scopeName}));return function(a){const c=r.reduce((s,{useScope:d,scopeName:u})=>{const v=d(a)[`__scope${u}`];return{...s,...v}},{});return i.useMemo(()=>({[`__scope${t.scopeName}`]:c}),[c])}};return n.scopeName=t.scopeName,n}function B(e){const t=i.useRef(e);return i.useEffect(()=>{t.current=e}),i.useMemo(()=>(...n)=>{var r;return(r=t.current)==null?void 0:r.call(t,...n)},[])}var U=globalThis!=null&&globalThis.document?i.useLayoutEffect:()=>{};function A(e,t,{checkForDefaultPrevented:n=!0}={}){return function(o){if(e==null||e(o),n===!1||!o.defaultPrevented)return t==null?void 0:t(o)}}function yt(e,t=globalThis==null?void 0:globalThis.document){const n=B(e);i.useEffect(()=>{const r=o=>{o.key==="Escape"&&n(o)};return t.addEventListener("keydown",r,{capture:!0}),()=>t.removeEventListener("keydown",r,{capture:!0})},[n,t])}var Et="DismissableLayer",ce="dismissableLayer.update",bt="dismissableLayer.pointerDownOutside",Ct="dismissableLayer.focusOutside",he,Ae=i.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set}),Te=i.forwardRef((e,t)=>{const{disableOutsidePointerEvents:n=!1,onEscapeKeyDown:r,onPointerDownOutside:o,onFocusOutside:a,onInteractOutside:c,onDismiss:s,...d}=e,u=i.useContext(Ae),[f,v]=i.useState(null),m=(f==null?void 0:f.ownerDocument)??(globalThis==null?void 0:globalThis.document),[,g]=i.useState({}),w=I(t,E=>v(E)),l=Array.from(u.layers),[p]=[...u.layersWithOutsidePointerEventsDisabled].slice(-1),y=l.indexOf(p),b=f?l.indexOf(f):-1,C=u.layersWithOutsidePointerEventsDisabled.size>0,S=b>=y,R=Rt(E=>{const T=E.target,L=[...u.branches].some(W=>W.contains(T));!S||L||(o==null||o(E),c==null||c(E),E.defaultPrevented||s==null||s())},m),N=Nt(E=>{const T=E.target;[...u.branches].some(W=>W.contains(T))||(a==null||a(E),c==null||c(E),E.defaultPrevented||s==null||s())},m);return yt(E=>{b===u.layers.size-1&&(r==null||r(E),!E.defaultPrevented&&s&&(E.preventDefault(),s()))},m),i.useEffect(()=>{if(f)return n&&(u.layersWithOutsidePointerEventsDisabled.size===0&&(he=m.body.style.pointerEvents,m.body.style.pointerEvents="none"),u.layersWithOutsidePointerEventsDisabled.add(f)),u.layers.add(f),pe(),()=>{n&&u.layersWithOutsidePointerEventsDisabled.size===1&&(m.body.style.pointerEvents=he)}},[f,m,n,u]),i.useEffect(()=>()=>{f&&(u.layers.delete(f),u.layersWithOutsidePointerEventsDisabled.delete(f),pe())},[f,u]),i.useEffect(()=>{const E=()=>g({});return document.addEventListener(ce,E),()=>document.removeEventListener(ce,E)},[]),h.jsx(D.div,{...d,ref:w,style:{pointerEvents:C?S?"auto":"none":void 0,...e.style},onFocusCapture:A(e.onFocusCapture,N.onFocusCapture),onBlurCapture:A(e.onBlurCapture,N.onBlurCapture),onPointerDownCapture:A(e.onPointerDownCapture,R.onPointerDownCapture)})});Te.displayName=Et;var St="DismissableLayerBranch",wt=i.forwardRef((e,t)=>{const n=i.useContext(Ae),r=i.useRef(null),o=I(t,r);return i.useEffect(()=>{const a=r.current;if(a)return n.branches.add(a),()=>{n.branches.delete(a)}},[n.branches]),h.jsx(D.div,{...e,ref:o})});wt.displayName=St;function Rt(e,t=globalThis==null?void 0:globalThis.document){const n=B(e),r=i.useRef(!1),o=i.useRef(()=>{});return i.useEffect(()=>{const a=s=>{if(s.target&&!r.current){let d=function(){Me(bt,n,u,{discrete:!0})};const u={originalEvent:s};s.pointerType==="touch"?(t.removeEventListener("click",o.current),o.current=d,t.addEventListener("click",o.current,{once:!0})):d()}else t.removeEventListener("click",o.current);r.current=!1},c=window.setTimeout(()=>{t.addEventListener("pointerdown",a)},0);return()=>{window.clearTimeout(c),t.removeEventListener("pointerdown",a),t.removeEventListener("click",o.current)}},[t,n]),{onPointerDownCapture:()=>r.current=!0}}function Nt(e,t=globalThis==null?void 0:globalThis.document){const n=B(e),r=i.useRef(!1);return i.useEffect(()=>{const o=a=>{a.target&&!r.current&&Me(Ct,n,{originalEvent:a},{discrete:!1})};return t.addEventListener("focusin",o),()=>t.removeEventListener("focusin",o)},[t,n]),{onFocusCapture:()=>r.current=!0,onBlurCapture:()=>r.current=!1}}function pe(){const e=new CustomEvent(ce);document.dispatchEvent(e)}function Me(e,t,n,{discrete:r}){const o=n.originalEvent.target,a=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:n});t&&o.addEventListener(e,t,{once:!0}),r?mt(o,a):o.dispatchEvent(a)}var Pt=xe[" useId ".trim().toString()]||(()=>{}),xt=0;function ee(e){const[t,n]=i.useState(Pt());return U(()=>{n(r=>r??String(xt++))},[e]),e||(t?`radix-${t}`:"")}var Dt=xe[" useInsertionEffect ".trim().toString()]||U;function Ot({prop:e,defaultProp:t,onChange:n=()=>{},caller:r}){const[o,a,c]=At({defaultProp:t,onChange:n}),s=e!==void 0,d=s?e:o;{const f=i.useRef(e!==void 0);i.useEffect(()=>{const v=f.current;v!==s&&console.warn(`${r} is changing from ${v?"controlled":"uncontrolled"} to ${s?"controlled":"uncontrolled"}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`),f.current=s},[s,r])}const u=i.useCallback(f=>{var v;if(s){const m=Tt(f)?f(e):f;m!==e&&((v=c.current)==null||v.call(c,m))}else a(f)},[s,e,a,c]);return[d,u]}function At({defaultProp:e,onChange:t}){const[n,r]=i.useState(e),o=i.useRef(n),a=i.useRef(t);return Dt(()=>{a.current=t},[t]),i.useEffect(()=>{var c;o.current!==n&&((c=a.current)==null||c.call(a,n),o.current=n)},[n,o]),[n,r,a]}function Tt(e){return typeof e=="function"}var te="focusScope.autoFocusOnMount",ne="focusScope.autoFocusOnUnmount",ge={bubbles:!1,cancelable:!0},Mt="FocusScope",Ie=i.forwardRef((e,t)=>{const{loop:n=!1,trapped:r=!1,onMountAutoFocus:o,onUnmountAutoFocus:a,...c}=e,[s,d]=i.useState(null),u=B(o),f=B(a),v=i.useRef(null),m=I(t,l=>d(l)),g=i.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;i.useEffect(()=>{if(r){let l=function(C){if(g.paused||!s)return;const S=C.target;s.contains(S)?v.current=S:O(v.current,{select:!0})},p=function(C){if(g.paused||!s)return;const S=C.relatedTarget;S!==null&&(s.contains(S)||O(v.current,{select:!0}))},y=function(C){if(document.activeElement===document.body)for(const R of C)R.removedNodes.length>0&&O(s)};document.addEventListener("focusin",l),document.addEventListener("focusout",p);const b=new MutationObserver(y);return s&&b.observe(s,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",l),document.removeEventListener("focusout",p),b.disconnect()}}},[r,s,g.paused]),i.useEffect(()=>{if(s){Ee.add(g);const l=document.activeElement;if(!s.contains(l)){const y=new CustomEvent(te,ge);s.addEventListener(te,u),s.dispatchEvent(y),y.defaultPrevented||(It(jt(Le(s)),{select:!0}),document.activeElement===l&&O(s))}return()=>{s.removeEventListener(te,u),setTimeout(()=>{const y=new CustomEvent(ne,ge);s.addEventListener(ne,f),s.dispatchEvent(y),y.defaultPrevented||O(l??document.body,{select:!0}),s.removeEventListener(ne,f),Ee.remove(g)},0)}}},[s,u,f,g]);const w=i.useCallback(l=>{if(!n&&!r||g.paused)return;const p=l.key==="Tab"&&!l.altKey&&!l.ctrlKey&&!l.metaKey,y=document.activeElement;if(p&&y){const b=l.currentTarget,[C,S]=Lt(b);C&&S?!l.shiftKey&&y===S?(l.preventDefault(),n&&O(C,{select:!0})):l.shiftKey&&y===C&&(l.preventDefault(),n&&O(S,{select:!0})):y===b&&l.preventDefault()}},[n,r,g.paused]);return h.jsx(D.div,{tabIndex:-1,...c,ref:m,onKeyDown:w})});Ie.displayName=Mt;function It(e,{select:t=!1}={}){const n=document.activeElement;for(const r of e)if(O(r,{select:t}),document.activeElement!==n)return}function Lt(e){const t=Le(e),n=ye(t,e),r=ye(t.reverse(),e);return[n,r]}function Le(e){const t=[],n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:r=>{const o=r.tagName==="INPUT"&&r.type==="hidden";return r.disabled||r.hidden||o?NodeFilter.FILTER_SKIP:r.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});for(;n.nextNode();)t.push(n.currentNode);return t}function ye(e,t){for(const n of e)if(!_t(n,{upTo:t}))return n}function _t(e,{upTo:t}){if(getComputedStyle(e).visibility==="hidden")return!0;for(;e;){if(t!==void 0&&e===t)return!1;if(getComputedStyle(e).display==="none")return!0;e=e.parentElement}return!1}function Ft(e){return e instanceof HTMLInputElement&&"select"in e}function O(e,{select:t=!1}={}){if(e&&e.focus){const n=document.activeElement;e.focus({preventScroll:!0}),e!==n&&Ft(e)&&t&&e.select()}}var Ee=kt();function kt(){let e=[];return{add(t){const n=e[0];t!==n&&(n==null||n.pause()),e=be(e,t),e.unshift(t)},remove(t){var n;e=be(e,t),(n=e[0])==null||n.resume()}}}function be(e,t){const n=[...e],r=n.indexOf(t);return r!==-1&&n.splice(r,1),n}function jt(e){return e.filter(t=>t.tagName!=="A")}var Wt="Portal",_e=i.forwardRef((e,t)=>{var s;const{container:n,...r}=e,[o,a]=i.useState(!1);U(()=>a(!0),[]);const c=n||o&&((s=globalThis==null?void 0:globalThis.document)==null?void 0:s.body);return c?it.createPortal(h.jsx(D.div,{...r,ref:t}),c):null});_e.displayName=Wt;function Bt(e,t){return i.useReducer((n,r)=>t[n][r]??n,e)}var q=e=>{const{present:t,children:n}=e,r=Ut(t),o=typeof n=="function"?n({present:r.isPresent}):i.Children.only(n),a=I(r.ref,$t(o));return typeof n=="function"||r.isPresent?i.cloneElement(o,{ref:a}):null};q.displayName="Presence";function Ut(e){const[t,n]=i.useState(),r=i.useRef(null),o=i.useRef(e),a=i.useRef("none"),c=e?"mounted":"unmounted",[s,d]=Bt(c,{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return i.useEffect(()=>{const u=V(r.current);a.current=s==="mounted"?u:"none"},[s]),U(()=>{const u=r.current,f=o.current;if(f!==e){const m=a.current,g=V(u);e?d("MOUNT"):g==="none"||(u==null?void 0:u.display)==="none"?d("UNMOUNT"):d(f&&m!==g?"ANIMATION_OUT":"UNMOUNT"),o.current=e}},[e,d]),U(()=>{if(t){let u;const f=t.ownerDocument.defaultView??window,v=g=>{const l=V(r.current).includes(CSS.escape(g.animationName));if(g.target===t&&l&&(d("ANIMATION_END"),!o.current)){const p=t.style.animationFillMode;t.style.animationFillMode="forwards",u=f.setTimeout(()=>{t.style.animationFillMode==="forwards"&&(t.style.animationFillMode=p)})}},m=g=>{g.target===t&&(a.current=V(r.current))};return t.addEventListener("animationstart",m),t.addEventListener("animationcancel",v),t.addEventListener("animationend",v),()=>{f.clearTimeout(u),t.removeEventListener("animationstart",m),t.removeEventListener("animationcancel",v),t.removeEventListener("animationend",v)}}else d("ANIMATION_END")},[t,d]),{isPresent:["mounted","unmountSuspended"].includes(s),ref:i.useCallback(u=>{r.current=u?getComputedStyle(u):null,n(u)},[])}}function V(e){return(e==null?void 0:e.animationName)||"none"}function $t(e){var r,o;let t=(r=Object.getOwnPropertyDescriptor(e.props,"ref"))==null?void 0:r.get,n=t&&"isReactWarning"in t&&t.isReactWarning;return n?e.ref:(t=(o=Object.getOwnPropertyDescriptor(e,"ref"))==null?void 0:o.get,n=t&&"isReactWarning"in t&&t.isReactWarning,n?e.props.ref:e.props.ref||e.ref)}var re=0;function Vt(){i.useEffect(()=>{const e=document.querySelectorAll("[data-radix-focus-guard]");return document.body.insertAdjacentElement("afterbegin",e[0]??Ce()),document.body.insertAdjacentElement("beforeend",e[1]??Ce()),re++,()=>{re===1&&document.querySelectorAll("[data-radix-focus-guard]").forEach(t=>t.remove()),re--}},[])}function Ce(){const e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.outline="none",e.style.opacity="0",e.style.position="fixed",e.style.pointerEvents="none",e}var x=function(){return x=Object.assign||function(t){for(var n,r=1,o=arguments.length;r<o;r++){n=arguments[r];for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&(t[a]=n[a])}return t},x.apply(this,arguments)};function Fe(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&typeof Object.getOwnPropertySymbols=="function")for(var o=0,r=Object.getOwnPropertySymbols(e);o<r.length;o++)t.indexOf(r[o])<0&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(n[r[o]]=e[r[o]]);return n}function Kt(e,t,n){if(n||arguments.length===2)for(var r=0,o=t.length,a;r<o;r++)(a||!(r in t))&&(a||(a=Array.prototype.slice.call(t,0,r)),a[r]=t[r]);return e.concat(a||Array.prototype.slice.call(t))}var H="right-scroll-bar-position",z="width-before-scroll-bar",Gt="with-scroll-bars-hidden",Xt="--removed-body-scroll-bar-size";function oe(e,t){return typeof e=="function"?e(t):e&&(e.current=t),e}function Yt(e,t){var n=i.useState(function(){return{value:e,callback:t,facade:{get current(){return n.value},set current(r){var o=n.value;o!==r&&(n.value=r,n.callback(r,o))}}}})[0];return n.callback=t,n.facade}var Ht=typeof window<"u"?i.useLayoutEffect:i.useEffect,Se=new WeakMap;function zt(e,t){var n=Yt(null,function(r){return e.forEach(function(o){return oe(o,r)})});return Ht(function(){var r=Se.get(n);if(r){var o=new Set(r),a=new Set(e),c=n.current;o.forEach(function(s){a.has(s)||oe(s,null)}),a.forEach(function(s){o.has(s)||oe(s,c)})}Se.set(n,e)},[e]),n}function Zt(e){return e}function qt(e,t){t===void 0&&(t=Zt);var n=[],r=!1,o={read:function(){if(r)throw new Error("Sidecar: could not `read` from an `assigned` medium. `read` could be used only with `useMedium`.");return n.length?n[n.length-1]:e},useMedium:function(a){var c=t(a,r);return n.push(c),function(){n=n.filter(function(s){return s!==c})}},assignSyncMedium:function(a){for(r=!0;n.length;){var c=n;n=[],c.forEach(a)}n={push:function(s){return a(s)},filter:function(){return n}}},assignMedium:function(a){r=!0;var c=[];if(n.length){var s=n;n=[],s.forEach(a),c=n}var d=function(){var f=c;c=[],f.forEach(a)},u=function(){return Promise.resolve().then(d)};u(),n={push:function(f){c.push(f),u()},filter:function(f){return c=c.filter(f),n}}}};return o}function Qt(e){e===void 0&&(e={});var t=qt(null);return t.options=x({async:!0,ssr:!1},e),t}var ke=function(e){var t=e.sideCar,n=Fe(e,["sideCar"]);if(!t)throw new Error("Sidecar: please provide `sideCar` property to import the right car");var r=t.read();if(!r)throw new Error("Sidecar medium not found");return i.createElement(r,x({},n))};ke.isSideCarExport=!0;function Jt(e,t){return e.useMedium(t),ke}var je=Qt(),ae=function(){},Q=i.forwardRef(function(e,t){var n=i.useRef(null),r=i.useState({onScrollCapture:ae,onWheelCapture:ae,onTouchMoveCapture:ae}),o=r[0],a=r[1],c=e.forwardProps,s=e.children,d=e.className,u=e.removeScrollBar,f=e.enabled,v=e.shards,m=e.sideCar,g=e.noRelative,w=e.noIsolation,l=e.inert,p=e.allowPinchZoom,y=e.as,b=y===void 0?"div":y,C=e.gapMode,S=Fe(e,["forwardProps","children","className","removeScrollBar","enabled","shards","sideCar","noRelative","noIsolation","inert","allowPinchZoom","as","gapMode"]),R=m,N=zt([n,t]),E=x(x({},S),o);return i.createElement(i.Fragment,null,f&&i.createElement(R,{sideCar:je,removeScrollBar:u,shards:v,noRelative:g,noIsolation:w,inert:l,setCallbacks:a,allowPinchZoom:!!p,lockRef:n,gapMode:C}),c?i.cloneElement(i.Children.only(s),x(x({},E),{ref:N})):i.createElement(b,x({},E,{className:d,ref:N}),s))});Q.defaultProps={enabled:!0,removeScrollBar:!0,inert:!1};Q.classNames={fullWidth:z,zeroRight:H};var en=function(){if(typeof __webpack_nonce__<"u")return __webpack_nonce__};function tn(){if(!document)return null;var e=document.createElement("style");e.type="text/css";var t=en();return t&&e.setAttribute("nonce",t),e}function nn(e,t){e.styleSheet?e.styleSheet.cssText=t:e.appendChild(document.createTextNode(t))}function rn(e){var t=document.head||document.getElementsByTagName("head")[0];t.appendChild(e)}var on=function(){var e=0,t=null;return{add:function(n){e==0&&(t=tn())&&(nn(t,n),rn(t)),e++},remove:function(){e--,!e&&t&&(t.parentNode&&t.parentNode.removeChild(t),t=null)}}},an=function(){var e=on();return function(t,n){i.useEffect(function(){return e.add(t),function(){e.remove()}},[t&&n])}},We=function(){var e=an(),t=function(n){var r=n.styles,o=n.dynamic;return e(r,o),null};return t},sn={left:0,top:0,right:0,gap:0},ie=function(e){return parseInt(e||"",10)||0},cn=function(e){var t=window.getComputedStyle(document.body),n=t[e==="padding"?"paddingLeft":"marginLeft"],r=t[e==="padding"?"paddingTop":"marginTop"],o=t[e==="padding"?"paddingRight":"marginRight"];return[ie(n),ie(r),ie(o)]},un=function(e){if(e===void 0&&(e="margin"),typeof window>"u")return sn;var t=cn(e),n=document.documentElement.clientWidth,r=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,r-n+t[2]-t[0])}},ln=We(),j="data-scroll-locked",dn=function(e,t,n,r){var o=e.left,a=e.top,c=e.right,s=e.gap;return n===void 0&&(n="margin"),`
2
+ .`.concat(Gt,` {
3
+ overflow: hidden `).concat(r,`;
4
+ padding-right: `).concat(s,"px ").concat(r,`;
5
+ }
6
+ body[`).concat(j,`] {
7
+ overflow: hidden `).concat(r,`;
8
+ overscroll-behavior: contain;
9
+ `).concat([t&&"position: relative ".concat(r,";"),n==="margin"&&`
10
+ padding-left: `.concat(o,`px;
11
+ padding-top: `).concat(a,`px;
12
+ padding-right: `).concat(c,`px;
13
+ margin-left:0;
14
+ margin-top:0;
15
+ margin-right: `).concat(s,"px ").concat(r,`;
16
+ `),n==="padding"&&"padding-right: ".concat(s,"px ").concat(r,";")].filter(Boolean).join(""),`
17
+ }
18
+
19
+ .`).concat(H,` {
20
+ right: `).concat(s,"px ").concat(r,`;
21
+ }
22
+
23
+ .`).concat(z,` {
24
+ margin-right: `).concat(s,"px ").concat(r,`;
25
+ }
26
+
27
+ .`).concat(H," .").concat(H,` {
28
+ right: 0 `).concat(r,`;
29
+ }
30
+
31
+ .`).concat(z," .").concat(z,` {
32
+ margin-right: 0 `).concat(r,`;
33
+ }
34
+
35
+ body[`).concat(j,`] {
36
+ `).concat(Xt,": ").concat(s,`px;
37
+ }
38
+ `)},we=function(){var e=parseInt(document.body.getAttribute(j)||"0",10);return isFinite(e)?e:0},fn=function(){i.useEffect(function(){return document.body.setAttribute(j,(we()+1).toString()),function(){var e=we()-1;e<=0?document.body.removeAttribute(j):document.body.setAttribute(j,e.toString())}},[])},vn=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,o=r===void 0?"margin":r;fn();var a=i.useMemo(function(){return un(o)},[o]);return i.createElement(ln,{styles:dn(a,!t,o,n?"":"!important")})},ue=!1;if(typeof window<"u")try{var K=Object.defineProperty({},"passive",{get:function(){return ue=!0,!0}});window.addEventListener("test",K,K),window.removeEventListener("test",K,K)}catch{ue=!1}var _=ue?{passive:!1}:!1,mn=function(e){return e.tagName==="TEXTAREA"},Be=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return n[t]!=="hidden"&&!(n.overflowY===n.overflowX&&!mn(e)&&n[t]==="visible")},hn=function(e){return Be(e,"overflowY")},pn=function(e){return Be(e,"overflowX")},Re=function(e,t){var n=t.ownerDocument,r=t;do{typeof ShadowRoot<"u"&&r instanceof ShadowRoot&&(r=r.host);var o=Ue(e,r);if(o){var a=$e(e,r),c=a[1],s=a[2];if(c>s)return!0}r=r.parentNode}while(r&&r!==n.body);return!1},gn=function(e){var t=e.scrollTop,n=e.scrollHeight,r=e.clientHeight;return[t,n,r]},yn=function(e){var t=e.scrollLeft,n=e.scrollWidth,r=e.clientWidth;return[t,n,r]},Ue=function(e,t){return e==="v"?hn(t):pn(t)},$e=function(e,t){return e==="v"?gn(t):yn(t)},En=function(e,t){return e==="h"&&t==="rtl"?-1:1},bn=function(e,t,n,r,o){var a=En(e,window.getComputedStyle(t).direction),c=a*r,s=n.target,d=t.contains(s),u=!1,f=c>0,v=0,m=0;do{if(!s)break;var g=$e(e,s),w=g[0],l=g[1],p=g[2],y=l-p-a*w;(w||y)&&Ue(e,s)&&(v+=y,m+=w);var b=s.parentNode;s=b&&b.nodeType===Node.DOCUMENT_FRAGMENT_NODE?b.host:b}while(!d&&s!==document.body||d&&(t.contains(s)||t===s));return(f&&Math.abs(v)<1||!f&&Math.abs(m)<1)&&(u=!0),u},G=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},Ne=function(e){return[e.deltaX,e.deltaY]},Pe=function(e){return e&&"current"in e?e.current:e},Cn=function(e,t){return e[0]===t[0]&&e[1]===t[1]},Sn=function(e){return`
39
+ .block-interactivity-`.concat(e,` {pointer-events: none;}
40
+ .allow-interactivity-`).concat(e,` {pointer-events: all;}
41
+ `)},wn=0,F=[];function Rn(e){var t=i.useRef([]),n=i.useRef([0,0]),r=i.useRef(),o=i.useState(wn++)[0],a=i.useState(We)[0],c=i.useRef(e);i.useEffect(function(){c.current=e},[e]),i.useEffect(function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(o));var l=Kt([e.lockRef.current],(e.shards||[]).map(Pe),!0).filter(Boolean);return l.forEach(function(p){return p.classList.add("allow-interactivity-".concat(o))}),function(){document.body.classList.remove("block-interactivity-".concat(o)),l.forEach(function(p){return p.classList.remove("allow-interactivity-".concat(o))})}}},[e.inert,e.lockRef.current,e.shards]);var s=i.useCallback(function(l,p){if("touches"in l&&l.touches.length===2||l.type==="wheel"&&l.ctrlKey)return!c.current.allowPinchZoom;var y=G(l),b=n.current,C="deltaX"in l?l.deltaX:b[0]-y[0],S="deltaY"in l?l.deltaY:b[1]-y[1],R,N=l.target,E=Math.abs(C)>Math.abs(S)?"h":"v";if("touches"in l&&E==="h"&&N.type==="range")return!1;var T=window.getSelection(),L=T&&T.anchorNode,W=L?L===N||L.contains(N):!1;if(W)return!1;var $=Re(E,N);if(!$)return!0;if($?R=E:(R=E==="v"?"h":"v",$=Re(E,N)),!$)return!1;if(!r.current&&"changedTouches"in l&&(C||S)&&(r.current=R),!R)return!0;var ve=r.current||R;return bn(ve,p,l,ve==="h"?C:S)},[]),d=i.useCallback(function(l){var p=l;if(!(!F.length||F[F.length-1]!==a)){var y="deltaY"in p?Ne(p):G(p),b=t.current.filter(function(R){return R.name===p.type&&(R.target===p.target||p.target===R.shadowParent)&&Cn(R.delta,y)})[0];if(b&&b.should){p.cancelable&&p.preventDefault();return}if(!b){var C=(c.current.shards||[]).map(Pe).filter(Boolean).filter(function(R){return R.contains(p.target)}),S=C.length>0?s(p,C[0]):!c.current.noIsolation;S&&p.cancelable&&p.preventDefault()}}},[]),u=i.useCallback(function(l,p,y,b){var C={name:l,delta:p,target:y,should:b,shadowParent:Nn(y)};t.current.push(C),setTimeout(function(){t.current=t.current.filter(function(S){return S!==C})},1)},[]),f=i.useCallback(function(l){n.current=G(l),r.current=void 0},[]),v=i.useCallback(function(l){u(l.type,Ne(l),l.target,s(l,e.lockRef.current))},[]),m=i.useCallback(function(l){u(l.type,G(l),l.target,s(l,e.lockRef.current))},[]);i.useEffect(function(){return F.push(a),e.setCallbacks({onScrollCapture:v,onWheelCapture:v,onTouchMoveCapture:m}),document.addEventListener("wheel",d,_),document.addEventListener("touchmove",d,_),document.addEventListener("touchstart",f,_),function(){F=F.filter(function(l){return l!==a}),document.removeEventListener("wheel",d,_),document.removeEventListener("touchmove",d,_),document.removeEventListener("touchstart",f,_)}},[]);var g=e.removeScrollBar,w=e.inert;return i.createElement(i.Fragment,null,w?i.createElement(a,{styles:Sn(o)}):null,g?i.createElement(vn,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function Nn(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}const Pn=Jt(je,Rn);var Ve=i.forwardRef(function(e,t){return i.createElement(Q,x({},e,{ref:t,sideCar:Pn}))});Ve.classNames=Q.classNames;var xn=function(e){if(typeof document>"u")return null;var t=Array.isArray(e)?e[0]:e;return t.ownerDocument.body},k=new WeakMap,X=new WeakMap,Y={},se=0,Ke=function(e){return e&&(e.host||Ke(e.parentNode))},Dn=function(e,t){return t.map(function(n){if(e.contains(n))return n;var r=Ke(n);return r&&e.contains(r)?r:(console.error("aria-hidden",n,"in not contained inside",e,". Doing nothing"),null)}).filter(function(n){return!!n})},On=function(e,t,n,r){var o=Dn(t,Array.isArray(e)?e:[e]);Y[n]||(Y[n]=new WeakMap);var a=Y[n],c=[],s=new Set,d=new Set(o),u=function(v){!v||s.has(v)||(s.add(v),u(v.parentNode))};o.forEach(u);var f=function(v){!v||d.has(v)||Array.prototype.forEach.call(v.children,function(m){if(s.has(m))f(m);else try{var g=m.getAttribute(r),w=g!==null&&g!=="false",l=(k.get(m)||0)+1,p=(a.get(m)||0)+1;k.set(m,l),a.set(m,p),c.push(m),l===1&&w&&X.set(m,!0),p===1&&m.setAttribute(n,"true"),w||m.setAttribute(r,"true")}catch(y){console.error("aria-hidden: cannot operate on ",m,y)}})};return f(t),s.clear(),se++,function(){c.forEach(function(v){var m=k.get(v)-1,g=a.get(v)-1;k.set(v,m),a.set(v,g),m||(X.has(v)||v.removeAttribute(r),X.delete(v)),g||v.removeAttribute(n)}),se--,se||(k=new WeakMap,k=new WeakMap,X=new WeakMap,Y={})}},An=function(e,t,n){n===void 0&&(n="data-aria-hidden");var r=Array.from(Array.isArray(e)?e:[e]),o=xn(e);return o?(r.push.apply(r,Array.from(o.querySelectorAll("[aria-live], script"))),On(r,o,n,"aria-hidden")):function(){return null}},J="Dialog",[Ge]=pt(J),[Tn,P]=Ge(J),Xe=e=>{const{__scopeDialog:t,children:n,open:r,defaultOpen:o,onOpenChange:a,modal:c=!0}=e,s=i.useRef(null),d=i.useRef(null),[u,f]=Ot({prop:r,defaultProp:o??!1,onChange:a,caller:J});return h.jsx(Tn,{scope:t,triggerRef:s,contentRef:d,contentId:ee(),titleId:ee(),descriptionId:ee(),open:u,onOpenChange:f,onOpenToggle:i.useCallback(()=>f(v=>!v),[f]),modal:c,children:n})};Xe.displayName=J;var Ye="DialogTrigger",Mn=i.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,o=P(Ye,n),a=I(t,o.triggerRef);return h.jsx(D.button,{type:"button","aria-haspopup":"dialog","aria-expanded":o.open,"aria-controls":o.contentId,"data-state":fe(o.open),...r,ref:a,onClick:A(e.onClick,o.onOpenToggle)})});Mn.displayName=Ye;var le="DialogPortal",[In,He]=Ge(le,{forceMount:void 0}),ze=e=>{const{__scopeDialog:t,forceMount:n,children:r,container:o}=e,a=P(le,t);return h.jsx(In,{scope:t,forceMount:n,children:i.Children.map(r,c=>h.jsx(q,{present:n||a.open,children:h.jsx(_e,{asChild:!0,container:o,children:c})}))})};ze.displayName=le;var Z="DialogOverlay",Ze=i.forwardRef((e,t)=>{const n=He(Z,e.__scopeDialog),{forceMount:r=n.forceMount,...o}=e,a=P(Z,e.__scopeDialog);return a.modal?h.jsx(q,{present:r||a.open,children:h.jsx(_n,{...o,ref:t})}):null});Ze.displayName=Z;var Ln=Oe("DialogOverlay.RemoveScroll"),_n=i.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,o=P(Z,n);return h.jsx(Ve,{as:Ln,allowPinchZoom:!0,shards:[o.contentRef],children:h.jsx(D.div,{"data-state":fe(o.open),...r,ref:t,style:{pointerEvents:"auto",...r.style}})})}),M="DialogContent",qe=i.forwardRef((e,t)=>{const n=He(M,e.__scopeDialog),{forceMount:r=n.forceMount,...o}=e,a=P(M,e.__scopeDialog);return h.jsx(q,{present:r||a.open,children:a.modal?h.jsx(Fn,{...o,ref:t}):h.jsx(kn,{...o,ref:t})})});qe.displayName=M;var Fn=i.forwardRef((e,t)=>{const n=P(M,e.__scopeDialog),r=i.useRef(null),o=I(t,n.contentRef,r);return i.useEffect(()=>{const a=r.current;if(a)return An(a)},[]),h.jsx(Qe,{...e,ref:o,trapFocus:n.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:A(e.onCloseAutoFocus,a=>{var c;a.preventDefault(),(c=n.triggerRef.current)==null||c.focus()}),onPointerDownOutside:A(e.onPointerDownOutside,a=>{const c=a.detail.originalEvent,s=c.button===0&&c.ctrlKey===!0;(c.button===2||s)&&a.preventDefault()}),onFocusOutside:A(e.onFocusOutside,a=>a.preventDefault())})}),kn=i.forwardRef((e,t)=>{const n=P(M,e.__scopeDialog),r=i.useRef(!1),o=i.useRef(!1);return h.jsx(Qe,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:a=>{var c,s;(c=e.onCloseAutoFocus)==null||c.call(e,a),a.defaultPrevented||(r.current||(s=n.triggerRef.current)==null||s.focus(),a.preventDefault()),r.current=!1,o.current=!1},onInteractOutside:a=>{var d,u;(d=e.onInteractOutside)==null||d.call(e,a),a.defaultPrevented||(r.current=!0,a.detail.originalEvent.type==="pointerdown"&&(o.current=!0));const c=a.target;((u=n.triggerRef.current)==null?void 0:u.contains(c))&&a.preventDefault(),a.detail.originalEvent.type==="focusin"&&o.current&&a.preventDefault()}})}),Qe=i.forwardRef((e,t)=>{const{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:o,onCloseAutoFocus:a,...c}=e,s=P(M,n),d=i.useRef(null),u=I(t,d);return Vt(),h.jsxs(h.Fragment,{children:[h.jsx(Ie,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:o,onUnmountAutoFocus:a,children:h.jsx(Te,{role:"dialog",id:s.contentId,"aria-describedby":s.descriptionId,"aria-labelledby":s.titleId,"data-state":fe(s.open),...c,ref:u,onDismiss:()=>s.onOpenChange(!1)})}),h.jsxs(h.Fragment,{children:[h.jsx(Wn,{titleId:s.titleId}),h.jsx(Un,{contentRef:d,descriptionId:s.descriptionId})]})]})}),de="DialogTitle",Je=i.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,o=P(de,n);return h.jsx(D.h2,{id:o.titleId,...r,ref:t})});Je.displayName=de;var et="DialogDescription",jn=i.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,o=P(et,n);return h.jsx(D.p,{id:o.descriptionId,...r,ref:t})});jn.displayName=et;var tt="DialogClose",nt=i.forwardRef((e,t)=>{const{__scopeDialog:n,...r}=e,o=P(tt,n);return h.jsx(D.button,{type:"button",...r,ref:t,onClick:A(e.onClick,()=>o.onOpenChange(!1))})});nt.displayName=tt;function fe(e){return e?"open":"closed"}var rt="DialogTitleWarning",[zn,ot]=ht(rt,{contentName:M,titleName:de,docsSlug:"dialog"}),Wn=({titleId:e})=>{const t=ot(rt),n=`\`${t.contentName}\` requires a \`${t.titleName}\` for the component to be accessible for screen reader users.
42
+
43
+ If you want to hide the \`${t.titleName}\`, you can wrap it with our VisuallyHidden component.
44
+
45
+ For more information, see https://radix-ui.com/primitives/docs/components/${t.docsSlug}`;return i.useEffect(()=>{e&&(document.getElementById(e)||console.error(n))},[n,e]),null},Bn="DialogDescriptionWarning",Un=({contentRef:e,descriptionId:t})=>{const r=`Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${ot(Bn).contentName}}.`;return i.useEffect(()=>{var a;const o=(a=e.current)==null?void 0:a.getAttribute("aria-describedby");t&&o&&(document.getElementById(t)||console.warn(r))},[r,e,t]),null},$n=Xe,Vn=ze,Kn=Ze,Gn=qe,Xn=Je,Yn=nt;const Zn=({open:e,title:t,onClose:n,footer:r,children:o,width:a=480,closeOnOverlayClick:c=!0})=>{const s=i.useRef(null);return i.useEffect(()=>{e&&(s.current=document.activeElement)},[e]),h.jsx($n,{open:e,onOpenChange:d=>!d&&n(),children:h.jsxs(Vn,{children:[h.jsx(Kn,{className:"modal-overlay"}),h.jsxs(Gn,{className:"modal",style:{width:a},"aria-describedby":void 0,onPointerDownOutside:d=>{c||d.preventDefault()},onCloseAutoFocus:d=>{d.preventDefault();const u=s.current;u&&"focus"in u&&u.focus()},children:[h.jsxs("div",{className:"modal-header",children:[h.jsx(Xn,{asChild:!0,children:h.jsx("div",{className:"modal-title",children:t})}),h.jsx(Yn,{asChild:!0,children:h.jsx("button",{type:"button",className:"modal-close","aria-label":"Close",children:h.jsx(st,{size:18})})})]}),h.jsx("div",{className:"modal-body",children:o}),r&&h.jsx("div",{className:"modal-footer",children:r})]})]})})};export{Te as D,Zn as M,D as P,I as a,B as b,pt as c,De as d,Oe as e,ee as f,A as g,Ot as h,U as u};
@@ -0,0 +1 @@
1
+ .modal-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#0000008c;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:8000;animation:modal-overlay-in .15s ease-out}.modal-overlay[data-state=closed]{animation:modal-overlay-out .15s ease-in}.modal{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:8001;background:var(--surface-2);border:1px solid var(--border-strong);border-radius:12px;box-shadow:0 20px 50px #00000073;max-width:90vw;max-height:90vh;display:flex;flex-direction:column;font-family:Inter,sans-serif;animation:modal-card-in .15s ease-out}.modal[data-state=closed]{animation:modal-card-out .15s ease-in}@keyframes modal-overlay-in{0%{opacity:0}to{opacity:1}}@keyframes modal-overlay-out{0%{opacity:1}to{opacity:0}}@keyframes modal-card-in{0%{opacity:0;transform:translate(-50%,-50%) scale(.97)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}@keyframes modal-card-out{0%{opacity:1;transform:translate(-50%,-50%) scale(1)}to{opacity:0;transform:translate(-50%,-50%) scale(.97)}}.modal-header{padding:16px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;gap:12px}.modal-title{color:var(--text);font-weight:600;font-size:15px;display:flex;align-items:center;gap:10px;flex:1;min-width:0}.modal-close{background:transparent;border:none;color:var(--text);cursor:pointer;padding:6px;border-radius:6px;display:inline-flex;align-items:center;justify-content:center;transition:background .12s ease,color .12s ease}.modal-close:hover{background:#ffffff0f;color:var(--text)}.modal-close:focus-visible{outline:2px solid var(--green);outline-offset:2px}.modal-body{padding:20px;color:var(--text);overflow-y:auto;flex:1;min-height:0}.modal-footer{padding:14px 20px;border-top:1px solid var(--border);display:flex;justify-content:flex-end;gap:8px}
@@ -0,0 +1 @@
1
+ import{j as t}from"./index-BrVV0Kla.js";function r({segments:a,value:n,onChange:l,size:s="md"}){return t.jsx("div",{className:`seg seg-${s}`,role:"tablist",children:a.map(e=>t.jsxs("button",{role:"tab","aria-selected":e.value===n,className:`seg-btn${e.value===n?" seg-btn-active":""}`,onClick:()=>l(e.value),type:"button",children:[t.jsx("span",{children:e.label}),typeof e.count=="number"&&t.jsx("span",{className:"seg-count",children:e.count})]},e.value))})}export{r as S};
@@ -0,0 +1 @@
1
+ .seg{display:inline-flex;gap:2px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-md);padding:2px}.seg-btn{display:inline-flex;align-items:center;gap:6px;background:transparent;border:none;color:var(--text-muted);font-family:Inter,sans-serif;font-weight:500;border-radius:calc(var(--radius-md) - 2px);cursor:pointer;transition:background var(--motion-fast),color var(--motion-fast)}.seg-btn:hover{color:var(--text)}.seg-btn-active{background:var(--surface-2);color:var(--text)}.seg-count{background:var(--bg);color:var(--text-muted);font-family:JetBrains Mono,monospace;font-size:10px;padding:1px 6px;border-radius:8px}.seg-sm .seg-btn{height:24px;padding:0 10px;font-size:11px}.seg-md .seg-btn{height:30px;padding:0 12px;font-size:12px}
@@ -0,0 +1 @@
1
+ .setting-card{background:var(--surface);border:1px solid var(--border-strong);border-radius:var(--radius-lg);padding:20px;position:relative;transition:border-color .2s,box-shadow .2s,background .2s;overflow:hidden;display:flex;flex-direction:column;gap:14px;box-shadow:var(--shadow-sm)}.setting-card:hover{border-color:#22c55e40;background:var(--surface-2);box-shadow:var(--shadow-md)}.setting-card:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;opacity:.15;z-index:0;background:linear-gradient(to bottom,#fff0,#fff0 50%,#00000026 50%,#00000026);background-size:100% 4px;border-radius:inherit}.setting-card:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;border-radius:var(--radius-lg);padding:1px;background:linear-gradient(180deg,#ffffff0f,#fff0);-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;pointer-events:none}.setting-card-header{display:flex;align-items:center;gap:8px;color:var(--green);width:100%}.setting-card-header svg{flex-shrink:0}.setting-card h4{margin:0;font-size:1rem;font-weight:700;font-family:Outfit,sans-serif;color:var(--text);letter-spacing:-.005em}.setting-card-description{color:var(--text-muted);font-size:.8125rem;margin:0;line-height:1.55;max-width:500px;font-weight:400}.setting-card-field{display:flex;flex-direction:column;gap:var(--space-2);width:100%;position:relative;z-index:1}.setting-card-hint{font-size:.7rem;color:var(--text-dim);margin-top:2px;position:relative;z-index:1;line-height:1.5;font-weight:400;letter-spacing:.01em}
@@ -0,0 +1,11 @@
1
+ import{g as i,j as s,v as o}from"./index-BrVV0Kla.js";/**
2
+ * @license lucide-react v0.555.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const h=[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]],x=i("rotate-ccw",h);/**
7
+ * @license lucide-react v0.555.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const j=[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",key:"1c8476"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7",key:"1ydtos"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7",key:"t51u73"}]],m=i("save",j),v=({onSave:d,onDiscard:n,onRestoreDefaults:t,isSaving:e,isValidating:a=!1,isDirty:c=!1,saveLabel:r="Save Configuration",restoreLabel:l="Restore Defaults"})=>s.jsxs("div",{className:"settings-footer",children:[s.jsxs("div",{className:"footer-left",children:[c&&s.jsxs("span",{className:"footer-dirty","aria-live":"polite",children:[s.jsx("span",{className:"footer-dirty__dot"}),s.jsx("span",{children:"Unsaved changes"})]}),c&&t&&s.jsx("span",{className:"footer-divider","aria-hidden":"true"}),t&&s.jsxs("button",{className:"reset-to-defaults-btn",onClick:t,disabled:e||a,children:[s.jsx(x,{size:16}),l]})]}),s.jsxs("div",{className:"footer-right",children:[s.jsx("button",{className:"reset-btn",onClick:n,disabled:e||a,children:"Discard"}),s.jsxs("button",{className:"save-btn",onClick:d,disabled:e||a,children:[e?s.jsx(o,{className:"animate-spin",size:18}):s.jsx(m,{size:18}),e?"Saving...":r]})]})]}),p=({icon:d,title:n,titleExtra:t,description:e,hint:a,children:c})=>s.jsxs("div",{className:"setting-card",children:[s.jsxs("div",{className:"setting-card-header",children:[d,s.jsx("h4",{children:n}),t]}),e&&s.jsx("p",{className:"setting-card-description",children:e}),s.jsx("div",{className:"setting-card-field",children:c}),a&&s.jsx("div",{className:"setting-card-hint",children:a})]});export{v as A,p as S};
@@ -0,0 +1 @@
1
+ import{j as t}from"./index-BrVV0Kla.js";const x=({className:s,...a})=>t.jsx("table",{className:`tbl${s?" "+s:""}`,...a}),j=s=>t.jsx("thead",{...s}),c=s=>t.jsx("tbody",{...s}),e=s=>t.jsx("tr",{...s}),n=s=>t.jsx("th",{...s}),T=s=>t.jsx("td",{...s});export{x as T,j as a,e as b,n as c,c as d,T as e};
@@ -0,0 +1 @@
1
+ .tbl{width:100%;border-collapse:collapse;font-family:Inter,sans-serif;font-size:12px}.tbl thead th{text-align:left;padding:8px 12px;font-weight:600;color:var(--text-muted);border-bottom:1px solid var(--border);background:var(--bg)}.tbl tbody td{padding:10px 12px;border-bottom:1px solid var(--border);color:var(--text);vertical-align:middle}.tbl tbody tr:last-child td{border-bottom:none}.tbl tbody tr:hover{background:var(--surface-2)}
@@ -0,0 +1,6 @@
1
+ import{g as t}from"./index-BrVV0Kla.js";/**
2
+ * @license lucide-react v0.555.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const a=[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]],o=t("activity",a);export{o as A};