@xenon-device-management/xenon 2.8.0 → 2.8.1

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 (75) hide show
  1. package/lib/package.json +1 -1
  2. package/lib/public/assets/{AnnotationOverlay-XmDGA9CJ.js → AnnotationOverlay-D9bdTRcw.js} +1 -1
  3. package/lib/public/assets/{ApiKeyGate-BilRCzB-.js → ApiKeyGate-CHrLtNyc.js} +1 -1
  4. package/lib/public/assets/{BugReportButton-_buEV98z.js → BugReportButton-Cvnfp_lP.js} +1 -1
  5. package/lib/public/assets/DeviceMosaicView-B--dBAh0.js +2 -0
  6. package/lib/public/assets/{EmptyState-BhQfPUga.js → EmptyState-Bd-GNhqS.js} +1 -1
  7. package/lib/public/assets/{FieldGroup-VPsU3LwN.js → FieldGroup-B6TJlJGM.js} +1 -1
  8. package/lib/public/assets/{FilterMenu-Br9o7dBS.js → FilterMenu-Cp_hdlhf.js} +1 -1
  9. package/lib/public/assets/{Menu-dF_HlxcB.js → Menu-shaiCwvG.js} +1 -1
  10. package/lib/public/assets/{Modal-51305CEc.js → Modal-CVlHheGn.js} +1 -1
  11. package/lib/public/assets/{RecordingPage-BYQfiIEd.js → RecordingPage-BglT6aA7.js} +1 -1
  12. package/lib/public/assets/{RecordingsPage-C7A2ZMU_.js → RecordingsPage-y3OOMfJ2.js} +1 -1
  13. package/lib/public/assets/{SegmentedControl--_Zp_x-E.js → SegmentedControl-Btui_BrO.js} +1 -1
  14. package/lib/public/assets/{SettingCard-DKlLEbu9.js → SettingCard-BojNKc9f.js} +1 -1
  15. package/lib/public/assets/{Table-mknFnLyL.js → Table-CgoNnHi5.js} +1 -1
  16. package/lib/public/assets/{activity-D4657Amj.js → activity-BB1GKieH.js} +1 -1
  17. package/lib/public/assets/{ai-settings-_sXFJMDz.js → ai-settings-DabayswA.js} +1 -1
  18. package/lib/public/assets/{api-keys-BV7AQW2N.js → api-keys-azY7czw6.js} +1 -1
  19. package/lib/public/assets/{apps-D-qPk58H.js → apps-DFvDvrJh.js} +1 -1
  20. package/lib/public/assets/{arrow-left-B6xZI-lv.js → arrow-left-D2R0-wu3.js} +1 -1
  21. package/lib/public/assets/{arrow-right-KOusXQT_.js → arrow-right-XChCsVs4.js} +1 -1
  22. package/lib/public/assets/{arrow-up-right--y5SjylA.js → arrow-up-right-DMqbmzLo.js} +1 -1
  23. package/lib/public/assets/{auth-shell-BOGfjiRi.js → auth-shell-DXDgAjsz.js} +2 -2
  24. package/lib/public/assets/{builds-page-CgobTBVN.js → builds-page-3L3rEp7Q.js} +1 -1
  25. package/lib/public/assets/{button-Ci58y6Ou.js → button-BZ8Paskr.js} +1 -1
  26. package/lib/public/assets/{calendar-BYLnN-JG.js → calendar-N2T0cT9m.js} +1 -1
  27. package/lib/public/assets/{check-BZFRUKhB.js → check-iKGL7VPu.js} +1 -1
  28. package/lib/public/assets/{chevron-left-C-olFL5E.js → chevron-left-C0H3PyOa.js} +1 -1
  29. package/lib/public/assets/{chevron-right-CmxzLyEE.js → chevron-right-jSMYhKfu.js} +1 -1
  30. package/lib/public/assets/{circle-check-Duvlh9lf.js → circle-check-8drTpet1.js} +1 -1
  31. package/lib/public/assets/{circle-x-DoQgpxKQ.js → circle-x-dWikpR-a.js} +1 -1
  32. package/lib/public/assets/{clock-yvaEQMYo.js → clock-Gn9P_xeq.js} +1 -1
  33. package/lib/public/assets/{copy-CIcmMH4s.js → copy-CRO4NZQZ.js} +1 -1
  34. package/lib/public/assets/{device-explorer-Cf-d-udL.js → device-explorer-Dt31Uik2.js} +54 -54
  35. package/lib/public/assets/{download-Ck75G4Sr.js → download-DPxWk60W.js} +1 -1
  36. package/lib/public/assets/{forgot-password-DcOi08XK.js → forgot-password-vNOHTq63.js} +1 -1
  37. package/lib/public/assets/{funnel-CafvAmsz.js → funnel-0f0ehxyn.js} +1 -1
  38. package/lib/public/assets/{index-9Npot8Nw.js → index-BKtEL_K4.js} +2 -2
  39. package/lib/public/assets/{index-QT33_ARg.js → index-D_T9qS1E.js} +1 -1
  40. package/lib/public/assets/{input-DUA3Tk33.js → input-CC4-VE9G.js} +1 -1
  41. package/lib/public/assets/{keycodes-Dj17elyp.js → keycodes-BWUMrNPy.js} +5 -5
  42. package/lib/public/assets/{layers-5SyoCGaK.js → layers-C8yrdLpc.js} +1 -1
  43. package/lib/public/assets/{list-checks-Blsk6Sts.js → list-checks-C2BNGDYf.js} +1 -1
  44. package/lib/public/assets/{lock-DUyg9vcf.js → lock-CrwjpPiF.js} +1 -1
  45. package/lib/public/assets/{login-DWRTaaNU.js → login-eVu1fnMN.js} +1 -1
  46. package/lib/public/assets/{maintenance-settings-CwthiKtl.js → maintenance-settings-CMFadU0m.js} +1 -1
  47. package/lib/public/assets/{monitor-D7kOVM-I.js → monitor-Dpxo7IUi.js} +1 -1
  48. package/lib/public/assets/{mouse-pointer-2-BzwITYs-.js → mouse-pointer-2-Bd9UBsUB.js} +1 -1
  49. package/lib/public/assets/{network-3c26VFLV.js → network-BeTXa8Ww.js} +1 -1
  50. package/lib/public/assets/{overview-BamopQBx.js → overview-cVHbQJBa.js} +1 -1
  51. package/lib/public/assets/{page-header-Cr568Bj2.js → page-header-Dtt6ySD7.js} +1 -1
  52. package/lib/public/assets/{plus-egFVGGUW.js → plus-Dalpx132.js} +1 -1
  53. package/lib/public/assets/{profile-page-BetCnNKO.js → profile-page-CLVVG6gR.js} +1 -1
  54. package/lib/public/assets/{reset-password-BgjuXPN-.js → reset-password-BCBz06iw.js} +1 -1
  55. package/lib/public/assets/{runbook-page-BbAREoE4.js → runbook-page-2nwa4t1z.js} +1 -1
  56. package/lib/public/assets/{select-BfZw_3u2.js → select-DP8zFEM7.js} +1 -1
  57. package/lib/public/assets/{selector-detail-page-BqswNrpr.js → selector-detail-page-Cbh2njcH.js} +1 -1
  58. package/lib/public/assets/{selector-health-Cbdio_9h.js → selector-health-Bvsjfwv0.js} +1 -1
  59. package/lib/public/assets/{selector-health-page-qdM7e6Lx.js → selector-health-page-DaUsNk74.js} +1 -1
  60. package/lib/public/assets/{session-detail-page-DFnhbs82.js → session-detail-page-Bxvmu6zZ.js} +1 -1
  61. package/lib/public/assets/{settings-Bg81ksOI.js → settings-DhK4nDW3.js} +1 -1
  62. package/lib/public/assets/{stat-tile-CCR1O7iE.js → stat-tile-C6UElbbT.js} +1 -1
  63. package/lib/public/assets/{tablet-DAHOR-iq.js → tablet-BxwMEQ5P.js} +1 -1
  64. package/lib/public/assets/{teams-Bz13DBCK.js → teams-CoHMZ77q.js} +1 -1
  65. package/lib/public/assets/{trash-2-BgqAVxUr.js → trash-2-BBdLg7FK.js} +1 -1
  66. package/lib/public/assets/{upload-Rvl2CCKn.js → upload-Bph2BQlh.js} +1 -1
  67. package/lib/public/assets/{use-password-reset-mode--PRWElm2.js → use-password-reset-mode-NQmLPUN0.js} +1 -1
  68. package/lib/public/assets/{users-O123SAYx.js → users-D8F7_TeC.js} +1 -1
  69. package/lib/public/assets/{users-BRkxjzNQ.js → users-TdXvYiCq.js} +1 -1
  70. package/lib/public/assets/{video-off-DkNxxAWE.js → video-off-Dw7aZgDU.js} +1 -1
  71. package/lib/public/assets/{webhook-settings-D6UmShyI.js → webhook-settings-BqcYFTth.js} +1 -1
  72. package/lib/public/assets/{zap-D6UAiMKL.js → zap-Z2pU9Fzi.js} +1 -1
  73. package/lib/public/index.html +1 -1
  74. package/package.json +1 -1
  75. package/lib/public/assets/DeviceMosaicView-DTEiC4gk.js +0 -2
@@ -1,4 +1,4 @@
1
- import{c as o}from"./index-9Npot8Nw.js";/**
1
+ import{c as o}from"./index-BKtEL_K4.js";/**
2
2
  * @license lucide-react v0.555.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{t as P,r,X as c,j as e,J as u,A as b,C as U,K as D}from"./index-9Npot8Nw.js";/* empty css */import{F as S}from"./FieldGroup-VPsU3LwN.js";import{P as O}from"./page-header-Cr568Bj2.js";import{T as V}from"./trash-2-BgqAVxUr.js";import{P as x}from"./plus-egFVGGUW.js";import{C as B}from"./circle-x-DoQgpxKQ.js";import{C as H}from"./circle-check-Duvlh9lf.js";import{Z as I}from"./zap-D6UAiMKL.js";import{A as J}from"./activity-D4657Amj.js";const C=[{id:"device_offline",label:"Device offline",icon:U,tone:"red"},{id:"device_new",label:"New device",icon:x,tone:"green"},{id:"session_failed",label:"Session failed",icon:B,tone:"amber"},{id:"selector_health_digest",label:"Selector health digest",icon:D,tone:"green"}],X=["udid","host","name","sessionId","failureReason","eventType","platform"],ae=()=>{const{toast:o}=P(),[g,T]=r.useState([]),[A,w]=r.useState(!0),[l,j]=r.useState(""),[d,k]=r.useState(["device_offline","session_failed"]),[h,f]=r.useState(!1),[v,N]=r.useState(!1),[n,_]=r.useState(!1),[y,m]=r.useState("");r.useEffect(()=>{p()},[]);const p=async()=>{w(!0);try{const s=await c.getWebhookConfigs();T(s||[])}catch(s){console.error("Failed to load webhook configs",s),o("Failed to load webhooks.","error")}finally{w(!1)}},z=async()=>{if(!l.trim()){o("Webhook URL is required.","error");return}if(d.length===0){o("Select at least one trigger event.","error");return}f(!0);try{await c.addWebhookConfig(l.trim(),d,"slack",y||void 0),j(""),m(""),_(!1),k(["device_offline","session_failed"]),await p(),o("Webhook saved.","success")}catch(s){console.error("Failed to add webhook",s),o(`Failed to add webhook: ${s.message||"unknown error"}`,"error")}finally{f(!1)}},E=async(s,t)=>{if(window.confirm(`Remove webhook for "${t}"?`))try{await c.deleteWebhookConfig(s),await p(),o("Webhook removed.","success")}catch(a){console.error("Failed to delete webhook",a),o(`Failed to delete: ${a.message||"unknown error"}`,"error")}},L=async()=>{if(l.trim()){N(!0);try{await c.testWebhook(l.trim(),"slack"),o("Test payload delivered.","success")}catch(s){o(`Test failed: ${s.message||"check the URL"}`,"error")}finally{N(!1)}}},$=s=>{k(t=>t.includes(s)?t.filter(a=>a!==s):[...t,s])},R=s=>{m(t=>t+`{{${s}}} `)};return e.jsxs("div",{className:"settings-container",children:[e.jsx(O,{icon:u,title:"Notifications",subtitle:"Configure Slack or generic webhooks to receive alerts for critical infrastructure events."}),e.jsxs("div",{className:"settings-content",children:[A?e.jsxs("div",{className:"settings-loading",style:{height:200},children:[e.jsx(b,{className:"animate-spin",size:28}),e.jsx("span",{children:"Loading webhooks…"})]}):g.length===0?e.jsxs("div",{className:"empty-state",children:[e.jsx("div",{className:"empty-state__icon",children:e.jsx(u,{size:28})}),e.jsx("h3",{className:"empty-state__title",children:"No webhooks configured"}),e.jsx("p",{className:"empty-state__copy",children:"Wire up a Slack incoming webhook or any HTTPS endpoint to receive alerts when devices go offline, new devices register, or sessions fail. Configure your first webhook below."})]}):e.jsx("div",{className:"webhook-list-grid",children:g.map(s=>{const t=(()=>{try{return JSON.parse(s.events)}catch{return[]}})();return e.jsxs("div",{className:"webhook-row-card",children:[e.jsxs("div",{className:"webhook-row-card__header",children:[e.jsx("span",{className:`pill-chip ${s.payloadTemplate?"pill-chip--admin":"pill-chip--scope"}`,children:s.payloadTemplate?"CUSTOM":"SLACK"}),e.jsx("span",{className:"webhook-row-card__url",title:s.url,children:s.url}),e.jsxs("button",{type:"button",className:"row-action row-action--danger",onClick:()=>E(s.id,s.url),"aria-label":`Remove webhook for ${s.url}`,children:[e.jsx(V,{size:13}),e.jsx("span",{children:"Remove"})]})]}),e.jsx("div",{className:"webhook-row-card__events",children:t.length===0?e.jsx("span",{className:"webhook-row-card__no-events",children:"No triggers"}):t.map(a=>{const i=C.find(F=>F.id===a),W=(i==null?void 0:i.icon)??u;return e.jsxs("span",{className:`event-pill event-pill--${(i==null?void 0:i.tone)||"gray"}`,children:[e.jsx(W,{size:11}),(i==null?void 0:i.label)||a]},a)})})]},s.id)})}),e.jsxs("div",{className:"add-webhook-card",children:[e.jsxs("div",{className:"add-webhook-card__header",children:[e.jsx(x,{size:14}),e.jsx("span",{children:"Add a new webhook"})]}),e.jsxs("div",{className:"add-webhook-card__body",children:[e.jsx(S,{label:"Webhook URL",description:"The endpoint we POST webhook events to.",htmlFor:"webhook-url",children:e.jsx("div",{className:"setting-input-wrapper",children:e.jsx("input",{id:"webhook-url",type:"url",placeholder:"https://hooks.slack.com/services/...",value:l,onChange:s=>j(s.target.value)})})}),e.jsx(S,{label:"Trigger events",children:e.jsx("div",{className:"event-toggle-grid",children:C.map(s=>{const t=d.includes(s.id),a=s.icon;return e.jsxs("button",{type:"button",role:"checkbox","aria-checked":t,className:`event-toggle event-toggle--${s.tone} ${t?"is-selected":""}`,onClick:()=>$(s.id),children:[e.jsx(a,{size:14,className:"event-toggle__lead"}),e.jsx("span",{children:s.label}),t&&e.jsx(H,{size:14,className:"event-toggle__check"})]},s.id)})})}),e.jsxs("div",{className:"template-section",children:[e.jsxs("button",{type:"button",className:"template-section__toggle",onClick:()=>_(!n),"aria-expanded":n,children:[e.jsx(I,{size:14,className:n?"tone-amber":"tone-dim"}),e.jsx("span",{children:"Use custom payload (optional)"}),e.jsx("span",{className:"template-section__indicator",children:n?"−":"+"})]}),n&&e.jsxs("div",{className:"template-editor",children:[e.jsx("p",{className:"template-editor__hint",children:"Define a JSON or text template. Click a variable below to insert it."}),e.jsx("div",{className:"template-editor__chips",children:X.map(s=>e.jsx("button",{type:"button",className:"template-editor__chip",onClick:()=>R(s),children:`{{${s}}}`},s))}),e.jsx("textarea",{className:"template-editor__textarea",placeholder:'Example: { "text": "Alert: Device {{udid}} is offline!" }',value:y,onChange:s=>m(s.target.value),rows:4})]})]})]}),e.jsxs("div",{className:"add-webhook-card__footer",children:[e.jsxs("button",{type:"button",className:"page-header-action page-header-action--ghost",onClick:L,disabled:!l.trim()||v,children:[v?e.jsx(b,{size:14,className:"animate-spin"}):e.jsx(J,{size:14}),e.jsx("span",{children:"Test payload"})]}),e.jsxs("button",{type:"button",className:"page-header-action",onClick:z,disabled:h||!l.trim(),children:[h?e.jsx(b,{size:14,className:"animate-spin"}):e.jsx(x,{size:14}),e.jsx("span",{children:h?"Saving…":"Save webhook"})]})]})]})]})]})};export{ae as WebhookSettings};
1
+ import{t as P,r,X as c,j as e,J as u,A as b,C as U,K as D}from"./index-BKtEL_K4.js";/* empty css */import{F as S}from"./FieldGroup-B6TJlJGM.js";import{P as O}from"./page-header-Dtt6ySD7.js";import{T as V}from"./trash-2-BBdLg7FK.js";import{P as x}from"./plus-Dalpx132.js";import{C as B}from"./circle-x-dWikpR-a.js";import{C as H}from"./circle-check-8drTpet1.js";import{Z as I}from"./zap-Z2pU9Fzi.js";import{A as J}from"./activity-BB1GKieH.js";const C=[{id:"device_offline",label:"Device offline",icon:U,tone:"red"},{id:"device_new",label:"New device",icon:x,tone:"green"},{id:"session_failed",label:"Session failed",icon:B,tone:"amber"},{id:"selector_health_digest",label:"Selector health digest",icon:D,tone:"green"}],X=["udid","host","name","sessionId","failureReason","eventType","platform"],ae=()=>{const{toast:o}=P(),[g,T]=r.useState([]),[A,w]=r.useState(!0),[l,j]=r.useState(""),[d,k]=r.useState(["device_offline","session_failed"]),[h,f]=r.useState(!1),[v,N]=r.useState(!1),[n,_]=r.useState(!1),[y,m]=r.useState("");r.useEffect(()=>{p()},[]);const p=async()=>{w(!0);try{const s=await c.getWebhookConfigs();T(s||[])}catch(s){console.error("Failed to load webhook configs",s),o("Failed to load webhooks.","error")}finally{w(!1)}},z=async()=>{if(!l.trim()){o("Webhook URL is required.","error");return}if(d.length===0){o("Select at least one trigger event.","error");return}f(!0);try{await c.addWebhookConfig(l.trim(),d,"slack",y||void 0),j(""),m(""),_(!1),k(["device_offline","session_failed"]),await p(),o("Webhook saved.","success")}catch(s){console.error("Failed to add webhook",s),o(`Failed to add webhook: ${s.message||"unknown error"}`,"error")}finally{f(!1)}},E=async(s,t)=>{if(window.confirm(`Remove webhook for "${t}"?`))try{await c.deleteWebhookConfig(s),await p(),o("Webhook removed.","success")}catch(a){console.error("Failed to delete webhook",a),o(`Failed to delete: ${a.message||"unknown error"}`,"error")}},L=async()=>{if(l.trim()){N(!0);try{await c.testWebhook(l.trim(),"slack"),o("Test payload delivered.","success")}catch(s){o(`Test failed: ${s.message||"check the URL"}`,"error")}finally{N(!1)}}},$=s=>{k(t=>t.includes(s)?t.filter(a=>a!==s):[...t,s])},R=s=>{m(t=>t+`{{${s}}} `)};return e.jsxs("div",{className:"settings-container",children:[e.jsx(O,{icon:u,title:"Notifications",subtitle:"Configure Slack or generic webhooks to receive alerts for critical infrastructure events."}),e.jsxs("div",{className:"settings-content",children:[A?e.jsxs("div",{className:"settings-loading",style:{height:200},children:[e.jsx(b,{className:"animate-spin",size:28}),e.jsx("span",{children:"Loading webhooks…"})]}):g.length===0?e.jsxs("div",{className:"empty-state",children:[e.jsx("div",{className:"empty-state__icon",children:e.jsx(u,{size:28})}),e.jsx("h3",{className:"empty-state__title",children:"No webhooks configured"}),e.jsx("p",{className:"empty-state__copy",children:"Wire up a Slack incoming webhook or any HTTPS endpoint to receive alerts when devices go offline, new devices register, or sessions fail. Configure your first webhook below."})]}):e.jsx("div",{className:"webhook-list-grid",children:g.map(s=>{const t=(()=>{try{return JSON.parse(s.events)}catch{return[]}})();return e.jsxs("div",{className:"webhook-row-card",children:[e.jsxs("div",{className:"webhook-row-card__header",children:[e.jsx("span",{className:`pill-chip ${s.payloadTemplate?"pill-chip--admin":"pill-chip--scope"}`,children:s.payloadTemplate?"CUSTOM":"SLACK"}),e.jsx("span",{className:"webhook-row-card__url",title:s.url,children:s.url}),e.jsxs("button",{type:"button",className:"row-action row-action--danger",onClick:()=>E(s.id,s.url),"aria-label":`Remove webhook for ${s.url}`,children:[e.jsx(V,{size:13}),e.jsx("span",{children:"Remove"})]})]}),e.jsx("div",{className:"webhook-row-card__events",children:t.length===0?e.jsx("span",{className:"webhook-row-card__no-events",children:"No triggers"}):t.map(a=>{const i=C.find(F=>F.id===a),W=(i==null?void 0:i.icon)??u;return e.jsxs("span",{className:`event-pill event-pill--${(i==null?void 0:i.tone)||"gray"}`,children:[e.jsx(W,{size:11}),(i==null?void 0:i.label)||a]},a)})})]},s.id)})}),e.jsxs("div",{className:"add-webhook-card",children:[e.jsxs("div",{className:"add-webhook-card__header",children:[e.jsx(x,{size:14}),e.jsx("span",{children:"Add a new webhook"})]}),e.jsxs("div",{className:"add-webhook-card__body",children:[e.jsx(S,{label:"Webhook URL",description:"The endpoint we POST webhook events to.",htmlFor:"webhook-url",children:e.jsx("div",{className:"setting-input-wrapper",children:e.jsx("input",{id:"webhook-url",type:"url",placeholder:"https://hooks.slack.com/services/...",value:l,onChange:s=>j(s.target.value)})})}),e.jsx(S,{label:"Trigger events",children:e.jsx("div",{className:"event-toggle-grid",children:C.map(s=>{const t=d.includes(s.id),a=s.icon;return e.jsxs("button",{type:"button",role:"checkbox","aria-checked":t,className:`event-toggle event-toggle--${s.tone} ${t?"is-selected":""}`,onClick:()=>$(s.id),children:[e.jsx(a,{size:14,className:"event-toggle__lead"}),e.jsx("span",{children:s.label}),t&&e.jsx(H,{size:14,className:"event-toggle__check"})]},s.id)})})}),e.jsxs("div",{className:"template-section",children:[e.jsxs("button",{type:"button",className:"template-section__toggle",onClick:()=>_(!n),"aria-expanded":n,children:[e.jsx(I,{size:14,className:n?"tone-amber":"tone-dim"}),e.jsx("span",{children:"Use custom payload (optional)"}),e.jsx("span",{className:"template-section__indicator",children:n?"−":"+"})]}),n&&e.jsxs("div",{className:"template-editor",children:[e.jsx("p",{className:"template-editor__hint",children:"Define a JSON or text template. Click a variable below to insert it."}),e.jsx("div",{className:"template-editor__chips",children:X.map(s=>e.jsx("button",{type:"button",className:"template-editor__chip",onClick:()=>R(s),children:`{{${s}}}`},s))}),e.jsx("textarea",{className:"template-editor__textarea",placeholder:'Example: { "text": "Alert: Device {{udid}} is offline!" }',value:y,onChange:s=>m(s.target.value),rows:4})]})]})]}),e.jsxs("div",{className:"add-webhook-card__footer",children:[e.jsxs("button",{type:"button",className:"page-header-action page-header-action--ghost",onClick:L,disabled:!l.trim()||v,children:[v?e.jsx(b,{size:14,className:"animate-spin"}):e.jsx(J,{size:14}),e.jsx("span",{children:"Test payload"})]}),e.jsxs("button",{type:"button",className:"page-header-action",onClick:z,disabled:h||!l.trim(),children:[h?e.jsx(b,{size:14,className:"animate-spin"}):e.jsx(x,{size:14}),e.jsx("span",{children:h?"Saving…":"Save webhook"})]})]})]})]})]})};export{ae as WebhookSettings};
@@ -1,4 +1,4 @@
1
- import{c as a}from"./index-9Npot8Nw.js";/**
1
+ import{c as a}from"./index-BKtEL_K4.js";/**
2
2
  * @license lucide-react v0.555.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -36,7 +36,7 @@
36
36
  Learn how to configure a non-root public URL by running `npm run build`.
37
37
  -->
38
38
  <title>Xenon</title>
39
- <script type="module" crossorigin src="/xenon/assets/index-9Npot8Nw.js"></script>
39
+ <script type="module" crossorigin src="/xenon/assets/index-BKtEL_K4.js"></script>
40
40
  <link rel="stylesheet" crossorigin href="/xenon/assets/index-C8pvxG1f.css">
41
41
  </head>
42
42
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenon-device-management/xenon",
3
- "version": "2.8.0",
3
+ "version": "2.8.1",
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": {
@@ -1,2 +0,0 @@
1
- import{r as d,j as r,z as $e,w as Me,X as D,v as Pe,e as Le,Y as Be,T as He,Z as We}from"./index-9Npot8Nw.js";import{p as Ve}from"./labels-C21SA4JG.js";import{C as ze}from"./circle-check-Duvlh9lf.js";import{C as xe,A as ee,H as Ue,S as be,a as Fe,I as Ge,h as ve}from"./keycodes-Dj17elyp.js";import{f as Ke,A as Ye}from"./AnnotationOverlay-XmDGA9CJ.js";import{V as Xe}from"./video-off-DkNxxAWE.js";import{C as qe}from"./chevron-left-C-olFL5E.js";import{P as Je}from"./plus-egFVGGUW.js";import{a as Ze}from"./SegmentedControl--_Zp_x-E.js";import{v as Qe,a as et,c as tt,s as rt,R as nt,b as ot,g as st,d as le,e as at}from"./recordings-BWe1ydXm.js";import{D as oe}from"./download-Ck75G4Sr.js";import{B as de}from"./button-Ci58y6Ou.js";import{C as it}from"./clock-yvaEQMYo.js";import{a as ct}from"./page-header-Cr568Bj2.js";import"./utils-CDN07tui.js";function lt(e,t){return e!=="auto"?e:t<=1?"1":t===2?"2x1":t<=4?"2x2":"3x2"}const dt={selected:new Set,layout:"auto",tiles:[],groupId:null,compositeEnabled:!1,downloadableVideoCount:0,recordingPhase:"idle",recording:!1,startedAt:null,annotateMode:!1,shape:"RECT",color:"#ff3333",overlayAnnotations:{},errorBanner:null,banner:null};function ut(e,t){var n;switch(t.type){case"SET_LAYOUT":return{...e,layout:t.layout};case"TOGGLE_SELECTED":{const o=new Set(e.selected);return o.has(t.udid)?o.delete(t.udid):o.add(t.udid),{...e,selected:o}}case"CLEAR_SELECTED":return{...e,selected:new Set};case"SET_TILES":return{...e,tiles:t.tiles};case"ADD_TILE":return e.tiles.some(o=>o.udid===t.tile.udid)?e:{...e,tiles:[...e.tiles,t.tile]};case"PATCH_TILE_DIMS":{let o=!1;const i=e.tiles.map(g=>g.udid!==t.udid||g.screenWidth&&g.screenHeight?g:(o=!0,{...g,screenWidth:t.screenWidth,screenHeight:t.screenHeight,aspect:t.aspect??g.aspect}));return o?{...e,tiles:i}:e}case"REMOVE_TILE":return e.recordingPhase!=="idle"?e:{...e,tiles:e.tiles.filter(o=>o.udid!==t.udid)};case"BIND_RECORDING_IDS":{const o=e.tiles.map(i=>({...i,recordingId:t.map[i.udid]??i.recordingId}));return{...e,tiles:o}}case"SET_RECORDING_PHASE":{const o=t.phase;return{...e,recordingPhase:o,recording:o==="recording"||o==="stopping"}}case"START_RECORDING":{const o=e.tiles.map(i=>({...i,recordingId:t.tileIds[i.udid]??i.recordingId}));return{...e,groupId:t.groupId,compositeEnabled:t.compositeEnabled===!0,downloadableVideoCount:0,recordingPhase:"recording",recording:!0,annotateMode:!1,startedAt:t.startedAt,overlayAnnotations:{},tiles:o}}case"REHYDRATE_RECORDING":{const o=e.tiles.map(i=>({...i,recordingId:t.tileIds[i.udid]??i.recordingId}));return{...e,groupId:t.groupId,compositeEnabled:t.compositeEnabled,downloadableVideoCount:0,recordingPhase:"recording",recording:!0,annotateMode:!1,startedAt:t.startedAt,overlayAnnotations:t.overlayAnnotations,tiles:o}}case"STOP_RECORDING":{const o=e.tiles.map(i=>({...i,recordingId:void 0}));return{...e,recording:!1,recordingPhase:"idle",annotateMode:!1,overlayAnnotations:{},downloadableVideoCount:t.downloadableVideoCount??e.downloadableVideoCount,compositeEnabled:t.compositeEnabled!==void 0?t.compositeEnabled:e.compositeEnabled,tiles:o}}case"SET_ANNOTATE_MODE":return{...e,annotateMode:t.enabled};case"SET_SHAPE":return{...e,shape:t.shape};case"SET_COLOR":return{...e,color:t.color};case"SET_OVERLAY_ANNOTATIONS":return{...e,overlayAnnotations:{...e.overlayAnnotations,[t.recordingId]:t.annotations}};case"CLEAR_OVERLAY_ANNOTATIONS":return{...e,overlayAnnotations:{}};case"SET_ERROR_BANNER":return{...e,errorBanner:t.message,banner:t.message?{tone:"error",message:t.message}:null};case"SET_BANNER":return{...e,banner:t.banner,errorBanner:((n=t.banner)==null?void 0:n.tone)==="error"?t.banner.message:null};default:return e}}const ye=d.createContext(null);function ft(){const e=d.useContext(ye);if(!e)throw new Error("useMosaic must be used inside <MosaicContext.Provider>");return e}function ht(){return d.useReducer(ut,dt)}function pt(e){const t=Math.max(0,Math.floor(e/1e3)),n=Math.floor(t/3600),o=Math.floor(t%3600/60),i=t%60;return n>0?`${n}:${String(o).padStart(2,"0")}:${String(i).padStart(2,"0")}`:`${o}:${String(i).padStart(2,"0")}`}function mt(e){switch(e){case"automation":return"In automation";case"manual_other":return"Manual control by another user";case"manual_self":return"On your grid";case"recording_other_group":return"Recording in another group";default:return e?"Busy":null}}const ue=[{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}],Y={available:"bg-[var(--color-success)]",inUse:"bg-[var(--color-warning)]",offline:"bg-[rgb(var(--rgb-offline))]"},gt=[["available",Y.available],["in use",Y.inUse],["offline",Y.offline]];function fe(e){return Ve(e)}function xt({devices:e,inMosaic:t,onToggle:n,status:o="ready"}){const[i,g]=d.useState(""),[u,N]=d.useState({}),p=d.useMemo(()=>{const l=i.trim().toLowerCase();return l?e.filter(v=>v.udid.toLowerCase().includes(l)||(v.name??"").toLowerCase().includes(l)||(v.altName??"").toLowerCase().includes(l)||(v.platform??"").toLowerCase().includes(l)):e},[e,i]),E=d.useMemo(()=>{const l=ue.map(f=>({id:f.id,label:f.label,rows:[]})),v=f=>l.find((j,y)=>ue[y].match(f))??l[l.length-1];for(const f of p)v((f.platform??"").toLowerCase()).rows.push(f);return l.filter(f=>f.rows.length>0)},[p]);if(e.length===0){const l=o==="loading"?"Loading devices…":o==="error"?"Couldn’t load devices. Retrying…":"No devices online.";return r.jsx("div",{role:"status",className:"text-xs text-[var(--text-dim)] p-3",children:l})}return r.jsxs("div",{className:"flex flex-col gap-2",children:[r.jsxs("div",{className:"relative",children:[r.jsx($e,{"aria-hidden":!0,size:12,className:"absolute left-2 top-1/2 -translate-y-1/2 text-[var(--text-dim)]"}),r.jsx("input",{type:"text",value:i,placeholder:"Filter devices...",onChange:l=>g(l.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(--color-info)]"})]}),E.map(l=>{const v=u[l.id];return r.jsxs("section",{className:"flex flex-col gap-1",children:[r.jsxs("button",{type:"button","aria-expanded":!v,onClick:()=>N(f=>({...f,[l.id]:!f[l.id]})),className:"flex items-center justify-between w-full text-[11px] text-[var(--text-dim)] hover:text-[rgb(var(--rgb-fg))] px-1 py-0.5",children:[r.jsxs("span",{className:"flex items-center gap-1.5",children:[r.jsx(Me,{"aria-hidden":!0,size:12,className:`transition-transform ${v?"-rotate-90":"rotate-0"}`}),l.label]}),r.jsx("span",{children:l.rows.length})]}),!v&&r.jsx("ul",{className:"flex flex-col gap-1 text-sm",children:l.rows.map(f=>{const j=f.busyReason==="manual_self",y=t.has(f.udid),x=!!f.busy&&!j&&!y||!!f.offline,R=f.offline?"Offline":mt(f.busyReason),O=!x,T=[f.name??f.udid,f.platform&&fe(f.platform)].concat(x&&R?[R]:[]).filter(Boolean).join(", ");return r.jsx("li",{children:r.jsxs("button",{type:"button",disabled:x,draggable:!x,onDragStart:w=>{w.dataTransfer.effectAllowed="copy",w.dataTransfer.setData("application/x-xenon-udid",f.udid),w.dataTransfer.setData("text/plain",f.udid)},onClick:()=>n(f.udid),"aria-label":T,"aria-pressed":y,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":y?"border-[rgb(var(--rgb-accent)/0.4)] bg-[rgb(var(--rgb-accent)/0.05)] hover:bg-[rgb(var(--rgb-accent)/0.1)]":"border-transparent hover:border-[var(--border)] hover:bg-[var(--surface-2)]"}`,title:x?`${R} — release first`:y?"Click to remove from the grid":"Click to add to the grid (or drag to a cell)",children:[y?r.jsx(ze,{"aria-hidden":!0,size:14,className:"shrink-0 text-[var(--color-accent-soft)]"}):r.jsx(xe,{"aria-hidden":!0,size:14,className:"shrink-0 text-[var(--text-dim)]"}),r.jsx("span",{"aria-hidden":!0,className:`inline-block w-1.5 h-1.5 rounded-full ${f.offline?Y.offline:O?Y.available:Y.inUse}`}),r.jsx("span",{className:"font-medium flex-1 truncate text-[13px]",children:f.name??f.udid}),f.platform&&r.jsx("span",{className:"text-[11px] px-1.5 py-0.5 rounded bg-[var(--surface-2)] text-[var(--text-dim)]",children:fe(f.platform)})]})},f.udid)})})]},l.id)}),i&&E.length===0&&r.jsxs("div",{className:"text-xs text-[var(--text-dim)] px-1 py-2",children:['No devices match "',i,'".']}),r.jsx("div",{className:"text-[10px] text-[var(--text-dim)] px-1 pt-2 flex items-center gap-3",children:gt.map(([l,v])=>r.jsxs("span",{className:"inline-flex items-center gap-1",children:[r.jsx("span",{className:`w-1.5 h-1.5 rounded-full inline-block ${v}`}),l]},l))})]})}const bt=[{id:"1",label:"1",icon:r.jsx(yt,{})},{id:"2x1",label:"2×1",icon:r.jsx(wt,{})},{id:"2x2",label:"2×2",icon:r.jsx(Et,{})},{id:"3x2",label:"3×2",icon:r.jsx(Nt,{})},{id:"auto",label:"Auto",icon:r.jsx(Rt,{})}];function vt({value:e,onChange:t}){return r.jsx("div",{role:"tablist",className:"inline-flex border border-[var(--border)] rounded-md overflow-hidden text-xs bg-[rgb(var(--rgb-white)/0.02)]",children:bt.map(n=>{const o=e===n.id;return r.jsxs("button",{role:"tab","aria-selected":o,onClick:()=>t(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 ${o?"bg-[var(--surface-2)] text-[rgb(var(--rgb-fg))]":"text-[var(--text-dim)] hover:bg-[var(--surface-2)] hover:text-[rgb(var(--rgb-fg))]"}`,title:`Layout: ${n.label}`,children:[r.jsx("span",{className:"w-3.5 h-3.5 inline-flex items-center justify-center",children:n.icon}),r.jsx("span",{children:n.label})]},n.id)})})}function B(e){return r.jsx("rect",{rx:1.2,ry:1.2,fill:"currentColor",...e})}function yt(){return r.jsx("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:B({x:2,y:2,width:10,height:10})})}function wt(){return r.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:[B({x:1.5,y:3,width:5,height:8}),B({x:7.5,y:3,width:5,height:8})]})}function Et(){return r.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:[B({x:1.5,y:1.5,width:5,height:5}),B({x:7.5,y:1.5,width:5,height:5}),B({x:1.5,y:7.5,width:5,height:5}),B({x:7.5,y:7.5,width:5,height:5})]})}function Nt(){return r.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",children:[B({x:1,y:1.5,width:3.3,height:5}),B({x:5.35,y:1.5,width:3.3,height:5}),B({x:9.7,y:1.5,width:3.3,height:5}),B({x:1,y:7.5,width:3.3,height:5}),B({x:5.35,y:7.5,width:3.3,height:5}),B({x:9.7,y:7.5,width:3.3,height:5})]})}function Rt(){return r.jsxs("svg",{viewBox:"0 0 14 14",className:"w-3.5 h-3.5",fill:"none",stroke:"currentColor",strokeWidth:"1.4",children:[r.jsx("rect",{x:"2",y:"2",width:"10",height:"10",rx:"1.5"}),r.jsx("circle",{cx:"7",cy:"7",r:"1.5",fill:"currentColor",stroke:"none"})]})}function St(e,t,n,o,i,g,u=!1){const N=Ke(n.width,n.height,g);let p=(e-n.left-N.left)/N.width,E=(t-n.top-N.top)/N.height;return!(p>=0&&p<=1&&E>=0&&E<=1)&&!u?null:(p=Math.min(1,Math.max(0,p)),E=Math.min(1,Math.max(0,E)),{x:Math.round(p*o),y:Math.round(E*i)})}const jt=5e3;function Tt(e,t){if(e.httpStatus===404)return{ended:!0,startedAt:t};if(e.httpStatus!==200)return{ended:!1,startedAt:t};const n=e.body??{};return n.status!=="running"?{ended:!0,startedAt:t}:t&&n.startedAt&&n.startedAt!==t?{ended:!0,startedAt:t}:{ended:!1,startedAt:t??n.startedAt}}function Ct(e,t,n,o=jt){const i=d.useRef(n);i.current=n,d.useEffect(()=>{if(!t)return;let g=!1,u=!1,N;const p=async()=>{if(!(u||g)){u=!0;try{const l=await fetch(`/xenon/api/control/${encodeURIComponent(e)}/stream/status`),v=l.ok?await l.json().catch(()=>{}):void 0;if(g)return;const f=Tt({httpStatus:l.status,body:v},N);N=f.startedAt,f.ended&&(g=!0,i.current())}catch{}finally{u=!1}}};p();const E=setInterval(p,o);return()=>{g=!0,clearInterval(E)}},[e,t,o])}const At=3,Dt=45e3,he=[2e3,5e3,1e4];function It(e){const t=Math.max(0,Math.min(e,he.length-1));return he[t]}function _t(e){return e<At}function pe(e){const t=(e.lastError||"").toLowerCase();return/did not find test app|not installed|webdriveragentrunner|applicationverification/.test(t)?"WebDriverAgent is not installed on this device. Install and trust WDA, then retry.":/unsupported ios version|manualpairingtunnelstart/.test(t)?"This device’s iOS version is not supported for live streaming on this host.":/econnrefused|econnreset|connection reset|tunnel/.test(t)?"Lost the connection to the device tunnel. Reconnect the device, then retry.":e.lastError&&e.lastError.trim()?e.lastError.trim():"We couldn’t start the live stream for this device."}function kt(e){for(let t=0;t+4<e.length;t++){const n=e[t]===0&&e[t+1]===0&&e[t+2]===0&&e[t+3]===1,o=e[t]===0&&e[t+1]===0&&e[t+2]===1;if(!n&&!o)continue;const i=t+(n?4:3);if((e[i]&31)===7){const g=u=>u.toString(16).padStart(2,"0");return`avc1.${g(e[i+1])}${g(e[i+2])}${g(e[i+3])}`}}return"avc1.42e01e"}const Ot=({wsUrl:e,onFatal:t,onReady:n,onFrameSize:o,className:i})=>{const g=d.useRef(null),u=d.useRef(t);u.current=t;const N=d.useRef(n);N.current=n;const p=d.useRef(o);return p.current=o,d.useEffect(()=>{var T;const E=window,l=g.current;if(typeof E.VideoDecoder>"u"||!l){(T=u.current)==null||T.call(u);return}const v=l.getContext("2d");let f=!1,j=!1,y=!0,x=0;const R=new E.VideoDecoder({output:w=>{var C,$;if(f){w.close();return}(l.width!==w.displayWidth||l.height!==w.displayHeight)&&(l.width=w.displayWidth,l.height=w.displayHeight,(C=p.current)==null||C.call(p,w.displayWidth,w.displayHeight)),v==null||v.drawImage(w,0,0),w.close(),y&&(y=!1,($=N.current)==null||$.call(N))},error:()=>{var w;return(w=u.current)==null?void 0:w.call(u)}}),O=new WebSocket(e);return O.binaryType="arraybuffer",O.onerror=()=>{var w;return(w=u.current)==null?void 0:w.call(u)},O.onclose=()=>{var w;f||(w=u.current)==null||w.call(u)},O.onmessage=w=>{var m;const C=w.data,$=new DataView(C).getUint8(0),V=new Uint8Array(C,1);if($===0){if(j)return;try{R.configure({codec:kt(V),optimizeForLatency:!0}),j=!0}catch{(m=u.current)==null||m.call(u)}return}if(!(!j||R.state!=="configured"))try{R.decode(new E.EncodedVideoChunk({type:$===1?"key":"delta",timestamp:x,data:V})),x+=11111}catch{}},()=>{f=!0;try{O.close()}catch{}try{R.state!=="closed"&&R.close()}catch{}}},[e]),r.jsx("canvas",{ref:g,className:i,style:{width:"100%",height:"100%",objectFit:"contain",display:"block"}})};function $t(e,t,n){return(e==="android"||e==="androidtv"||e==="android-tv")&&t==="h264"&&n?"h264":"mjpeg"}const Mt=10,Pt=500;function Lt({udid:e,name:t,recordingId:n,annotateMode:o,shape:i,color:g,aspect:u="9 / 16",screenWidth:N,screenHeight:p,platform:E,onAnnotation:l,overlayAnnotations:v,onOverlayAnnotationsChange:f,onRemove:j}){const[y,x]=d.useState("connecting"),[R,O]=d.useState(),T=d.useCallback((a,h)=>{if(!(a>0)||!(h>0))return;const _=a/h;O(k=>k!==void 0&&Math.abs(k-_)<.001?k:_)},[]),[w,C]=d.useState(0),[$,V]=d.useState(null),m=d.useRef(0),s=d.useRef(null),[c,b]=d.useState(""),[S,H]=d.useState([]),[U,M]=d.useState(!1),z=d.useRef(null),W=d.useRef(null),P=E==="android"||E==="androidtv"||E==="android-tv",A=E==="ios"||E==="tvos";d.useEffect(()=>{let a=!1;if(n){V(null),x("connecting"),C(Date.now()),D.startStream(e).catch(()=>{});return}const h=typeof window.VideoDecoder<"u";return(async()=>{try{const _=encodeURIComponent(e),k=await fetch(`/xenon/api/control/${_}/stream/status`);if(!k.ok)return;const I=await k.json();if($t(E||"",I==null?void 0:I.type,h)!=="h264"||!(I!=null&&I.h264Path))return;const G=await fetch(`/xenon/api/control/${_}/stream/ticket`,{method:"POST"});if(!G.ok)return;const{ticket:L}=await G.json();if(a||!L)return;const Z=`${window.location.protocol==="https:"?"wss":"ws"}://${window.location.host}${I.h264Path}?ticket=${encodeURIComponent(L)}`;V(Z)}catch{}})(),()=>{a=!0}},[e,E,n]);const F=!o&&!!N&&!!p,re=(a,h,_,k=!1)=>St(a,h,_,N,p,R,k),Re=(a,h,_)=>{const k=Date.now()+Math.random(),I=a-_.left,G=h-_.top;H(L=>[...L,{id:k,x:I,y:G}]),window.setTimeout(()=>{H(L=>L.filter(q=>q.id!==k))},500)},Se=a=>{if(!F||y==="unavailable"||a.button!==0)return;const h=a.currentTarget;h.focus(),re(a.clientX,a.clientY,h.getBoundingClientRect())&&(h.setPointerCapture(a.pointerId),W.current={startX:a.clientX,startY:a.clientY,startTime:Date.now(),rect:h.getBoundingClientRect()},Re(a.clientX,a.clientY,W.current.rect))},je=async a=>{var Z,ce;const h=W.current;if(W.current=null,!h||!F)return;try{a.currentTarget.releasePointerCapture(a.pointerId)}catch{}const _=a.clientX-h.startX,k=a.clientY-h.startY,I=Math.hypot(_,k),G=Date.now()-h.startTime,L=re(h.startX,h.startY,h.rect,!0),q=I<Mt;try{if(q&&G<Pt)await D.tap(e,L.x,L.y);else if(q)await((ce=(Z=D).touchAndHold)==null?void 0:ce.call(Z,e,L.x,L.y,G));else{const Q=re(a.clientX,a.clientY,h.rect,!0);await D.swipe(e,L.x,L.y,Q.x,Q.y)}}catch(Q){console.error(`[DeviceTile] gesture failed for ${e}`,Q)}},Te=()=>{W.current=null},Ce=async a=>{if(!F||y==="unavailable"||a.metaKey||a.ctrlKey||a.altKey)return;const h=a.key;try{if(h.length===1){a.preventDefault(),await D.typeText(e,h);return}if(h==="Backspace"){a.preventDefault(),P?await D.pressKey(e,67):await D.typeText(e,"\b");return}if(h==="Enter"){a.preventDefault(),P?await D.pressKey(e,66):await D.typeText(e,`
2
- `);return}if(h==="Tab"){a.preventDefault(),await D.typeText(e," ");return}h==="Escape"&&P&&(a.preventDefault(),await D.pressKey(e,ee.BACK))}catch(_){console.error(`[DeviceTile] keystroke "${h}" failed for ${e}`,_)}},Ae=()=>D.pressKey(e,P?ee.HOME:Ge.HOME).catch(a=>console.error(`[DeviceTile] home failed for ${e}`,a)),De=()=>D.pressKey(e,ee.BACK).catch(a=>console.error(`[DeviceTile] back failed for ${e}`,a)),Ie=()=>D.pressKey(e,ee.APP_SWITCH).catch(a=>console.error(`[DeviceTile] app-switch failed for ${e}`,a)),_e=async()=>{try{const a=await D.getScreenshot(e),h=(a==null?void 0:a.screenshot)??(a==null?void 0:a.value)??a;if(!h)return;const _=await fetch(`data:image/png;base64,${h}`).then(G=>G.blob()),k=URL.createObjectURL(_),I=document.createElement("a");I.href=k,I.download=`${t||e}-${Date.now()}.png`,document.body.appendChild(I),I.click(),I.remove(),setTimeout(()=>URL.revokeObjectURL(k),1e3)}catch(a){console.error(`[DeviceTile] screenshot failed for ${e}`,a)}};d.useEffect(()=>{console.log(`[DeviceTile] Rendering ${e} in state: ${y}`)},[e,y]);const ke=`/xenon/api/control/${encodeURIComponent(e)}/stream?t=${w}`,ie=d.useCallback(async()=>{try{const a=await fetch(`/xenon/api/control/${encodeURIComponent(e)}/stream/status`);if(!a.ok)throw new Error(String(a.status));const h=await a.json();b(pe({lastError:h==null?void 0:h.lastError,status:h==null?void 0:h.status}))}catch{b(pe({}))}},[e]),ne=d.useCallback(()=>{const a=m.current;_t(a)?(m.current=a+1,s.current&&clearTimeout(s.current),s.current=setTimeout(()=>{x("connecting"),C(Date.now())},It(a))):(x("unavailable"),ie())},[ie]);d.useEffect(()=>{if(y!=="connecting")return;const a=setTimeout(ne,Dt);return()=>clearTimeout(a)},[y,w,ne]),Ct(e,y==="live"&&!$,()=>{console.warn(`[DeviceTile] MJPEG stream ended for ${e}; reconnecting`),x("connecting"),C(Date.now())}),d.useEffect(()=>()=>{s.current&&clearTimeout(s.current)},[]);const Oe=()=>{s.current&&clearTimeout(s.current),m.current=0,b(""),x("connecting"),C(Date.now())},X=!!n,J=t||e.slice(0,16)+"…";return r.jsxs("div",{className:"flex items-stretch justify-center w-full h-full min-h-0 min-w-0 gap-2",children:[r.jsxs("div",{className:"theme-dark relative bg-[var(--black)] rounded-lg overflow-hidden border border-[var(--border)] group max-h-full max-w-full self-center",style:{aspectRatio:u,height:"100%"},children:[y==="connecting"&&r.jsxs("div",{role:"status","aria-live":"polite",className:"flex flex-col items-center justify-center gap-1 px-4 text-center bg-[rgb(var(--rgb-well)/0.8)] text-[var(--text-dim)] absolute inset-0 z-20 pointer-events-none",children:[r.jsx("div",{"aria-hidden":!0,className:"w-10 h-10 mb-3 border-4 border-[var(--border)] border-t-[var(--text-dim)] rounded-full animate-spin"}),r.jsx("div",{className:"font-semibold text-[var(--text)]",children:"Starting stream…"}),r.jsx("div",{className:"text-xs text-[var(--text-dim)] truncate max-w-full",children:J}),r.jsx("div",{className:"text-[11px] mt-3 text-[var(--text-dim)] leading-snug",children:A?"iPhone streams can take up to 20 seconds to start.":"Connecting to the device…"})]}),y==="unavailable"&&r.jsx("div",{role:"alert",className:"flex items-center justify-center bg-[var(--black)] text-[var(--text-dim)] absolute inset-0 z-20",children:r.jsxs("div",{className:"text-center p-6",children:[r.jsx(Xe,{"aria-hidden":!0,size:36,className:"mx-auto mb-3 text-[var(--text-dim)]"}),r.jsx("div",{className:"font-semibold text-[var(--text)]",children:"Connection failed"}),r.jsx("p",{className:"text-xs mt-2 text-[var(--text-dim)] leading-relaxed max-w-[240px] mx-auto",children:c||"We couldn’t start the live stream for this device."}),r.jsx("button",{type:"button",className:"mt-5 text-sm font-semibold px-4 py-2 rounded bg-[var(--color-accent)] hover:bg-[var(--color-accent-strong)] text-[var(--color-on-accent)] transition-colors",onClick:Oe,children:"Retry"})]})}),$?r.jsx(Ot,{wsUrl:$,className:"absolute inset-0 w-full h-full object-contain bg-black select-none pointer-events-none",onReady:()=>x("live"),onFrameSize:T,onFatal:()=>{console.warn(`[DeviceTile] H.264 fatal for ${e}; falling back to MJPEG`),V(null),x("connecting"),C(Date.now())}}):r.jsx("img",{src:ke,alt:J,className:"absolute inset-0 w-full h-full object-contain bg-black select-none pointer-events-none",draggable:!1,style:{opacity:y==="live"?1:0,transition:"opacity 0.3s ease-in-out"},onLoad:a=>{console.log(`[DeviceTile] Image LOADED for ${e}`),T(a.currentTarget.naturalWidth,a.currentTarget.naturalHeight),m.current=0,s.current&&clearTimeout(s.current),x("live")},onError:a=>{console.error(`[DeviceTile] Image ERROR for ${e}`,a),ne()}},w),F&&y!=="unavailable"&&r.jsx("div",{ref:z,className:`absolute inset-0 z-[35] touch-none outline-none ${U?"ring-2 ring-[rgb(var(--rgb-accent)/0.6)] ring-inset rounded-lg":""}`,tabIndex:0,onPointerDown:Se,onPointerUp:je,onPointerCancel:Te,onKeyDown:Ce,onFocus:()=>M(!0),onBlur:()=>M(!1)}),r.jsx("div",{className:"absolute inset-0 z-20 pointer-events-none overflow-hidden",children:S.map(a=>r.jsx("span",{className:"absolute block w-10 h-10 rounded-full border-2 border-[var(--color-accent-soft)] animate-ping",style:{left:a.x-20,top:a.y-20}},a.id))}),X&&r.jsx(Ye,{enabled:o,shape:i,color:g,committed:v,mediaAspect:R,onCommittedChange:f,onCommit:(a,h)=>l(n,a,h)}),X&&o&&y==="live"&&r.jsx("div",{className:"absolute top-3 left-14 right-3 z-30 flex justify-end pointer-events-none",children:r.jsx("span",{className:"px-2 py-0.5 rounded-sm bg-[var(--color-info)] text-white text-[10px] font-semibold shadow-lg whitespace-nowrap overflow-hidden text-ellipsis",children:"Annotating · Esc to stop"})}),X&&r.jsx("div",{className:"absolute top-3 left-3 z-30 pointer-events-none",children:r.jsx("span",{className:"px-2 py-0.5 rounded-sm bg-[var(--red-600)] text-white text-[9px] font-black shadow-lg",children:"REC"})}),r.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:[y==="live"&&r.jsx("span",{className:"px-2 py-0.5 rounded-sm bg-[var(--color-success)] text-[var(--color-on-success)] text-[11px] font-semibold shadow-lg w-fit",children:"Live"}),r.jsx("span",{className:"px-2 py-1 rounded bg-[rgb(var(--rgb-well)/0.8)] text-[var(--text)] text-[11px] font-medium backdrop-blur-md border border-[rgb(var(--rgb-fg)/0.1)] shadow-xl truncate max-w-[180px]",children:J})]})]}),r.jsx(Bt,{streamState:y,recording:X,isIOS:A,isAndroid:P,onClose:!X&&j?()=>j(e):void 0,sendHome:Ae,sendBack:De,sendAppSwitch:Ie,captureScreenshot:_e,displayName:J})]})}const te="w-7 h-7 flex items-center justify-center rounded-md hover:bg-[var(--surface-2)] hover:text-[var(--text)] transition-colors";function Bt({streamState:e,recording:t,isIOS:n,isAndroid:o,onClose:i,sendHome:g,sendBack:u,sendAppSwitch:N,captureScreenshot:p,displayName:E}){const l=e==="live";return!i&&!l?null:r.jsxs("div",{className:"self-stretch w-9 flex flex-col items-center justify-between gap-1 py-2 rounded-lg bg-[var(--surface)] border border-[var(--border)] text-[var(--text-dim)]",children:[r.jsx("div",{className:"flex flex-col items-center gap-1",children:i&&r.jsx("button",{type:"button","aria-label":`Remove ${E}`,title:"Remove from the grid",onClick:i,className:"w-7 h-7 flex items-center justify-center rounded-md hover:bg-[rgb(var(--rgb-red)/0.15)] hover:text-[var(--color-danger)] transition-colors",children:r.jsx(Pe,{"aria-hidden":!0,size:16})})}),l&&r.jsxs("div",{className:"flex flex-col items-center gap-1.5 my-auto",children:[(o||n)&&r.jsx("button",{type:"button","aria-label":"Home",title:"Home",onClick:g,className:te,children:r.jsx(Ue,{"aria-hidden":!0,size:15})}),o&&r.jsx("button",{type:"button","aria-label":"Back",title:"Back",onClick:u,className:te,children:r.jsx(qe,{"aria-hidden":!0,size:16})}),o&&r.jsx("button",{type:"button","aria-label":"Recent apps",title:"Recent apps",onClick:N,className:te,children:r.jsx(be,{"aria-hidden":!0,size:13})})]}),l&&r.jsx("div",{className:"flex flex-col items-center gap-1",children:r.jsx("button",{type:"button","aria-label":"Screenshot",title:"Screenshot",onClick:p,className:te,children:r.jsx(Fe,{"aria-hidden":!0,size:15})})})]})}const Ht={1:"1fr","2x1":"1fr 1fr","2x2":"1fr 1fr","3x2":"1fr 1fr 1fr"},Wt={1:"1fr","2x1":"1fr","2x2":"1fr 1fr","3x2":"1fr 1fr"},Vt={1:1,"2x1":2,"2x2":4,"3x2":6};function zt({layout:e,tiles:t,annotateMode:n,shape:o,color:i,onAnnotation:g,overlayAnnotationsByRecording:u,onOverlayAnnotationsChange:N,onRemove:p,onDropDevice:E}){const l=lt(e,t.length),v=Vt[l];t.length;const f=t.length,j=e==="auto"?Math.max(f,1):v,y=Math.max(0,j-f);return r.jsxs("div",{style:{display:"grid",gap:8,gridTemplateColumns:Ht[l],gridTemplateRows:Wt[l]},className:"h-full",children:[t.map(x=>r.jsx(Lt,{udid:x.udid,name:x.name,mjpegPort:x.mjpegPort,recordingId:x.recordingId,aspect:x.aspect,screenWidth:x.screenWidth,screenHeight:x.screenHeight,platform:x.platform,annotateMode:n,shape:o,color:i,overlayAnnotations:x.recordingId?u==null?void 0:u[x.recordingId]:void 0,onOverlayAnnotationsChange:x.recordingId&&N?R=>N(x.recordingId,R):void 0,onAnnotation:g,onRemove:p},x.udid)),Array.from({length:y},(x,R)=>r.jsx(Ut,{onDropDevice:E},`empty-${R}`))]})}function Ut({onDropDevice:e}){const[t,n]=d.useState(!1);return r.jsxs("div",{onDragOver:o=>{e&&(o.preventDefault(),o.dataTransfer.dropEffect="copy",t||n(!0))},onDragLeave:()=>n(!1),onDrop:o=>{if(n(!1),!e)return;o.preventDefault();const i=o.dataTransfer.getData("application/x-xenon-udid")||o.dataTransfer.getData("text/plain");i&&e(i)},className:`flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed transition-colors ${t?"border-[rgb(var(--rgb-accent)/0.6)] bg-[rgb(var(--rgb-accent)/0.05)]":"border-[var(--border)] bg-[rgb(var(--rgb-fg)/0.02)]"} text-[var(--text-dim)] min-h-[120px]`,children:[r.jsx("div",{"aria-hidden":"true",className:`w-8 h-8 rounded-full flex items-center justify-center ${t?"bg-[rgb(var(--rgb-accent)/0.15)] text-[var(--color-accent-soft)]":"bg-[rgb(var(--rgb-fg)/0.05)] text-[rgb(var(--rgb-fg)/0.6)]"}`,children:r.jsx(Je,{size:16})}),r.jsx("div",{className:"text-xs font-medium",children:"Drag a device here"}),r.jsx("div",{className:"text-[10px] opacity-70",children:"or click one in the list"})]})}function Ft(e,t){return t==="starting"?"Starting…":e<=1?"Record":`Record ${e} devices`}const K={"aria-hidden":!0,size:14,className:"shrink-0"};function Gt({selectedUdids:e,onClearMarks:t}){const{state:n,dispatch:o}=ft(),[i,g]=d.useState(()=>Date.now());d.useEffect(()=>{if(n.recordingPhase!=="recording"&&n.recordingPhase!=="stopping")return;g(Date.now());const m=window.setInterval(()=>g(Date.now()),250);return()=>window.clearInterval(m)},[n.recordingPhase]);const u=n.startedAt&&(n.recordingPhase==="recording"||n.recordingPhase==="stopping")?i-n.startedAt:0,N=n.recordingPhase==="starting"||n.recordingPhase==="stopping",p=n.recordingPhase==="idle"&&e.length>0&&!N,E=n.recordingPhase==="recording"||n.recording&&n.tiles.some(m=>!!m.recordingId),l=E&&!N,v=l,f=v&&n.annotateMode,j=!!n.groupId&&n.recordingPhase==="idle",y=e.length>1||n.downloadableVideoCount>1,x=j&&n.compositeEnabled,R=j&&!n.compositeEnabled&&(n.downloadableVideoCount===1||n.downloadableVideoCount===0&&e.length<=1),O=async()=>{var m;if(p){o({type:"SET_RECORDING_PHASE",phase:"starting"}),o({type:"SET_BANNER",banner:null});try{const s=await rt(e);if(!((m=s.recordings)!=null&&m.length)){o({type:"SET_RECORDING_PHASE",phase:"idle"}),o({type:"SET_BANNER",banner:{tone:"error",message:"Recording did not start. Check that live preview is working, then try again."}});return}const c={};for(const S of s.recordings)c[S.udid]=S.id;const b=e.length-s.recordings.length;o({type:"START_RECORDING",groupId:s.groupId,startedAt:Date.now(),tileIds:c,compositeEnabled:s.compositeEnabled===!0}),b>0?o({type:"SET_BANNER",banner:{tone:"error",message:`Recording started on ${s.recordings.length} of ${e.length} devices (${b} failed).`}}):o({type:"SET_BANNER",banner:null})}catch(s){if(o({type:"SET_RECORDING_PHASE",phase:"idle"}),s instanceof nt)if(s.body.error==="device_busy"){const c=(s.body.busyDevices??[]).map(b=>`${b.udid} (${b.reason})`).join(", ");o({type:"SET_BANNER",banner:{tone:"error",message:`Cannot start — these devices are busy: ${c}`}})}else o({type:"SET_BANNER",banner:{tone:"error",message:`Server-wide recording cap reached (${s.body.active}/${s.body.limit}).`}});else o({type:"SET_BANNER",banner:{tone:"error",message:`Recording failed: ${s.message}`}})}}},T=async()=>{if(!(!n.groupId||n.recordingPhase!=="recording")){o({type:"SET_RECORDING_PHASE",phase:"stopping"});try{const m=await ot(n.groupId),s=(m.recordings??[]).filter(b=>b.status==="STOPPED"&&(b.sizeBytes??0)>=1024),c=(m.recordings??[]).filter(b=>b.status!=="STOPPED");o({type:"STOP_RECORDING",downloadableVideoCount:s.length,compositeEnabled:n.compositeEnabled}),s.length===0?o({type:"SET_BANNER",banner:{tone:"error",message:c.length>0?`Recording failed on ${c.length} device(s). Nothing to download.`:"Recording stopped but no playable video was produced."}}):c.length>0?o({type:"SET_BANNER",banner:{tone:"info",message:`${s.length} ready, ${c.length} failed. Download with the button above.`}}):o({type:"SET_BANNER",banner:{tone:"info",message:s.length===1?"Video ready. Download it with the button above.":`${s.length} videos ready. Download them with the button above.`}})}catch(m){o({type:"STOP_RECORDING"}),o({type:"SET_BANNER",banner:{tone:"error",message:`Stop failed: ${m.message}`}})}}},w=m=>{const s=m==="annotate";s&&!v||s!==n.annotateMode&&o({type:"SET_ANNOTATE_MODE",enabled:s})};d.useEffect(()=>{if(!n.annotateMode)return;const m=s=>{var b;if(s.key!=="Escape"||s.repeat)return;const c=s.target;c&&((b=c.closest)!=null&&b.call(c,"input, textarea, select")||c.isContentEditable)||o({type:"SET_ANNOTATE_MODE",enabled:!1})};return window.addEventListener("keydown",m),()=>window.removeEventListener("keydown",m)},[n.annotateMode,o]);const C=()=>{v&&t()},$=Object.values(n.overlayAnnotations).some(m=>((m==null?void 0:m.length)??0)>0),V=[{id:"RECT",label:"Rect"},{id:"CIRCLE",label:"Circle"},{id:"ARROW",label:"Arrow"},{id:"FREEHAND",label:"Draw"}];return r.jsxs("div",{className:"flex items-center gap-2 flex-wrap justify-end",children:[n.recordingPhase==="recording"||n.recordingPhase==="stopping"?r.jsxs("span",{className:"inline-flex items-center gap-1.5 px-2 py-1 rounded text-xs font-mono tabular-nums bg-[rgb(var(--rgb-red-600)/0.2)] text-[var(--red-200)] border border-[rgb(var(--rgb-red)/0.4)]",title:"Recording elapsed time",children:[r.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-[var(--red)] animate-pulse"}),"REC ",pt(u),n.recordingPhase==="stopping"?" · Stopping…":""]}):r.jsxs("button",{type:"button",onClick:O,disabled:!p,title:e.length===0?"Add a device to the grid first":"Record every device on the grid",className:"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded bg-[var(--red-600)] text-white disabled:opacity-40 disabled:cursor-not-allowed light:disabled:bg-transparent light:disabled:text-[var(--text)] light:disabled:ring-1 light:disabled:ring-inset light:disabled:ring-[color:var(--border)]",children:[r.jsx(xe,{...K,size:10,fill:"currentColor"}),Ft(e.length,n.recordingPhase)]}),r.jsxs("button",{type:"button",onClick:T,disabled:!l,className:"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded border border-[var(--border)] disabled:opacity-40 disabled:cursor-not-allowed",children:[r.jsx(be,{...K,size:12}),n.recordingPhase==="stopping"?"Stopping…":"Stop"]}),r.jsx(Ze,{size:"sm",value:n.annotateMode?"annotate":"interact",onChange:w,segments:[{value:"interact",label:"Interact",title:"Tap, swipe and type on the devices (Esc)",keyShortcuts:"Escape"},{value:"annotate",label:"Annotate",title:v?"Draw marks on the recording":"Start recording to annotate",disabled:!v}]}),E&&r.jsxs("div",{className:"flex items-center gap-1 ml-1 pl-2 border-l border-[var(--border)]",children:[V.map(m=>r.jsx("button",{type:"button",disabled:!f,"aria-pressed":n.shape===m.id,onClick:()=>o({type:"SET_SHAPE",shape:m.id}),className:`px-2 py-1 text-xs rounded border disabled:opacity-40 disabled:cursor-not-allowed ${n.shape===m.id?"bg-[var(--surface-2)] border-[var(--color-info)] text-[rgb(var(--rgb-fg))]":"border-[var(--border)] opacity-80 hover:opacity-100"}`,children:m.label},m.id)),r.jsx("input",{type:"color",value:n.color,disabled:!f,onChange:m=>o({type:"SET_COLOR",color:m.target.value}),title:"Annotation color","aria-label":"Annotation color",className:"w-7 h-7 rounded border border-[var(--border)] bg-transparent cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed"}),r.jsx("button",{type:"button",onClick:C,disabled:!v||!$,title:"Clear marks from the preview and the recording",className:"px-2 py-1 text-xs rounded border border-[var(--border)] hover:bg-[var(--surface-2)] disabled:opacity-40 disabled:cursor-not-allowed",children:"Clear marks"})]}),j&&R&&r.jsxs("a",{href:Qe(n.groupId),className:"ml-1 inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded border border-[var(--border)] hover:bg-[var(--surface-2)]",download:!0,children:[r.jsx(oe,{...K}),"Download video"]}),j&&!R&&r.jsxs("a",{href:et(n.groupId),className:"ml-1 inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded border border-[var(--border)] hover:bg-[var(--surface-2)]",download:`videos-${n.groupId}.zip`,title:"ZIP of mp4 files only (no JSON extras)",children:[r.jsx(oe,{...K}),"Download ",y?"videos":"video"]}),x&&r.jsxs("a",{href:tt(n.groupId),className:"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded border border-[var(--border)] hover:bg-[var(--surface-2)]",download:`side-by-side-${n.groupId}.mp4`,title:"All devices in one side-by-side video",children:[r.jsx(oe,{...K}),"Side-by-side"]}),j&&n.groupId&&r.jsxs(Le,{to:`/recordings/${encodeURIComponent(n.groupId)}`,className:"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded border border-[var(--border)] hover:bg-[var(--surface-2)]",children:[r.jsx(Be,{...K}),"Open in Recordings"]})]})}function Kt({remainingSec:e,onContinue:t,onReleaseNow:n}){return r.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:r.jsxs("div",{className:"bg-[var(--bg)] border border-[var(--border)] rounded-lg w-full max-w-md p-6",children:[r.jsx("h3",{id:"idle-warning-title",className:"text-base font-semibold mb-2",children:"Are you still there?"}),r.jsxs("p",{className:"text-sm text-[var(--text-dim)] mb-4",children:["Your devices will be released in"," ",r.jsxs("span",{className:"text-[var(--text)] font-mono font-semibold",children:[e,"s"]})," due to inactivity. Click ",r.jsx("strong",{children:"Continue Session"})," to keep them locked."]}),r.jsxs("div",{className:"flex justify-end gap-2",children:[r.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"}),r.jsx("button",{type:"button",autoFocus:!0,onClick:t,className:"h-9 px-4 rounded-md bg-[var(--color-accent)] text-[var(--color-on-accent)] text-sm font-medium",children:"Continue session"})]})]})})}function Yt({message:e,onRestore:t,onDismiss:n}){return r.jsxs("div",{role:"status",className:"text-sm rounded border border-[var(--border)] bg-[var(--surface-2)] text-[var(--text)] px-3 py-2 flex items-center gap-2",children:[r.jsx(it,{"aria-hidden":!0,size:14,className:"shrink-0 text-[var(--text-dim)]"}),r.jsx("span",{className:"flex-1",children:e}),t&&r.jsx(de,{variant:"tonal",size:"sm",onClick:t,children:"Restore devices"}),r.jsx(de,{variant:"ghost",size:"sm",onClick:n,children:"Dismiss"})]})}function we(e){var t;return((t=e.marketingName)==null?void 0:t.trim())||e.name||e.udid}function Ee(e){if(!e)return"9 / 16";const t=Number(e.screenWidth),n=Number(e.screenHeight);if(Number.isFinite(t)&&Number.isFinite(n)&&t>0&&n>0)return`${t} / ${n}`;const o=(e.platform||"").toLowerCase();return o==="tvos"||o==="androidtv"||o==="android-tv"?"16 / 9":"9 / 16"}function se(e){const t=Number(e.screenWidth),n=Number(e.screenHeight);return{udid:e.udid,name:we(e),mjpegPort:0,aspect:Ee(e),screenWidth:Number.isFinite(t)&&t>0?t:void 0,screenHeight:Number.isFinite(n)&&n>0?n:void 0,platform:e.platform}}function Xt(e,t){return t?"blocked":e?"remove":"add"}function ae(e,t,n){return!n||!e?!1:e===`manual_${n}_${t}`}function qt(e,t){if(!e.busy)return;const n=ve(e);return n?n.startsWith("manual_")?ae(n,e.udid,t)?"manual_self":"manual_other":"automation":"unknown"}function Jt(e,t){return e.busy?ae(ve(e),e.udid,t):!1}function Zt(e,t){return e>0&&t==="idle"}function Qt(e,t,n){const o=[],i=[];for(const g of e){const u=t.find(p=>p.udid===g.udid),N=(u==null?void 0:u.name)||g.name||g.udid;u?u.offline?i.push({name:N,reason:"offline"}):u.busy&&!ae(u.session_id,u.udid,n)?i.push({name:N,reason:"in_use"}):o.push(u):i.push({name:N,reason:"gone"})}return{restore:o,skipped:i}}const Ne=(e,t)=>`${e} ${t}${e===1?"":"s"}`;function er(e,t){return`Released ${Ne(e,"device")} after ${t} minutes without activity.`}const tr={in_use:"is now in use by someone else.",offline:"is offline.",gone:"is no longer connected."};function rr(e,t){return t.length===0?null:[e>0?`Restored ${Ne(e,"device")}.`:"No devices restored.",...t.map(o=>`${o.name} ${tr[o.reason]}`)].join(" ")}function nr(){let e=Promise.resolve();return{enqueue(t){const n=e.then(t,t);return e=n.then(()=>{},()=>{}),n}}}const me=["mousemove","mousedown","keydown","touchstart","wheel"];function or({idleAfterMs:e,warningSec:t,onWarning:n,onTimeout:o,enabled:i}){const[g,u]=d.useState(!1),[N,p]=d.useState(null),E=d.useRef(null),l=d.useRef(null),v=d.useRef(!1),f=d.useRef(n),j=d.useRef(o);f.current=n,j.current=o;function y(){E.current&&(clearTimeout(E.current),E.current=null),l.current&&(clearInterval(l.current),l.current=null)}function x(){v.current=!0,u(!0),p(t),f.current(t);let T=t;l.current=setInterval(()=>{T-=1,T<=0?(y(),v.current=!1,u(!1),p(null),j.current()):p(T)},1e3)}function R(){y(),v.current=!1,u(!1),p(null);const T=e-t*1e3;E.current=setTimeout(x,Math.max(0,T))}function O(){i&&R()}return d.useEffect(()=>{if(!i){y(),v.current=!1,u(!1),p(null);return}function T(){v.current||R()}return me.forEach(w=>window.addEventListener(w,T,{passive:!0})),R(),()=>{me.forEach(w=>window.removeEventListener(w,T)),y()}},[i,e,t]),{warning:g,remainingSec:N,reset:O}}const ge=5*60*1e3,sr=30;function ar(e,t){return{udid:e.udid,name:we(e),altName:e.name,platform:e.platform,busy:e.busy,busyReason:qt(e,t),mjpegServerPort:e.mjpegServerPort,offline:!!e.offline}}function Er(){const[e,t]=ht(),[n,o]=d.useState([]),[i,g]=d.useState("loading"),[u,N]=d.useState(0),[p,E]=d.useState(null),l=d.useRef(!1),v=d.useRef(nr());d.useEffect(()=>{let s=!1;return fetch("/xenon/api/auth/me",{credentials:"include"}).then(c=>c.ok?c.json():null).then(c=>{!s&&(c!=null&&c.userId)&&E(c.userId)}).catch(()=>{}),()=>{s=!0}},[]),d.useEffect(()=>{let s=!1;const c=async()=>{try{const S=await D.getDevices();if(s)return;o(Array.isArray(S)?S:[]),g("ready")}catch{s||g("error")}};c();const b=window.setInterval(c,5e3);return()=>{s=!0,window.clearInterval(b)}},[u]),d.useEffect(()=>{let s=!1;if(!(!p||l.current))return l.current=!0,(async()=>{var S;let c=[],b=[];try{const H=await D.getDevices();c=Array.isArray(H)?H:[];const U=c.filter(z=>Jt(z,p));if(b=(await Promise.all(U.map(async z=>{try{const W=await fetch(`/xenon/api/control/${encodeURIComponent(z.udid)}/stream/status`);if(!W.ok)return null;const P=await W.json();return(P==null?void 0:P.status)==="running"?z:null}catch{return null}}))).filter(z=>z!==null).map(se),s)return;b.length>0&&t({type:"SET_TILES",tiles:b})}catch{}try{const{serverNow:H,groups:U}=await st(),M=U[0];if(s||!M)return;const z=new Set(b.map(A=>A.udid)),W=M.recordings.filter(A=>!z.has(A.udid)).map(A=>c.find(F=>F.udid===A.udid)).filter(A=>!!A).map(se);W.length>0&&t({type:"SET_TILES",tiles:[...b,...W]});const P={};for(const A of M.annotations){let F;try{F=JSON.parse(A.geometry)}catch{continue}(P[S=A.recordingId]??(P[S]=[])).push({shape:A.shape,color:A.color,geometry:F,text:A.text??void 0})}t({type:"REHYDRATE_RECORDING",groupId:M.groupId,startedAt:Date.now()-(H-Date.parse(M.startedAt)),tileIds:Object.fromEntries(M.recordings.map(A=>[A.udid,A.id])),compositeEnabled:M.compositeEnabled,overlayAnnotations:P})}catch{}})(),()=>{s=!0}},[p,t]),d.useEffect(()=>{for(const s of e.tiles){if(s.screenWidth&&s.screenHeight)continue;const c=n.find(H=>H.udid===s.udid);if(!c)continue;const b=Number(c.screenWidth),S=Number(c.screenHeight);Number.isFinite(b)&&b>0&&Number.isFinite(S)&&S>0&&t({type:"PATCH_TILE_DIMS",udid:s.udid,screenWidth:b,screenHeight:S,aspect:Ee(c)})}},[n,e.tiles,t]);const f=d.useMemo(()=>new Set(e.tiles.map(s=>s.udid)),[e.tiles]),j=d.useMemo(()=>n.map(s=>ar(s,p)),[n,p]),y=async s=>{t({type:"ADD_TILE",tile:se(s)});const c=await D.startStream(s.udid).catch(()=>null);We(c)&&t({type:"REMOVE_TILE",udid:s.udid})},x=async s=>{const c=Xt(e.tiles.some(S=>S.udid===s),e.recording);if(c==="blocked"){t({type:"SET_ERROR_BANNER",message:"Stop recording before changing the devices on the grid."});return}if(c==="remove"){await R(s);return}const b=n.find(S=>S.udid===s);b&&await y(b)},R=async s=>{t({type:"REMOVE_TILE",udid:s});try{await fetch(`/xenon/api/control/${encodeURIComponent(s)}/stream/leave`,{method:"POST"})}catch{}N(c=>c+1)},O=(s,c,b)=>{if(!e.groupId)return;const S=e.groupId,H=e.startedAt?Date.now()-e.startedAt:0,U={recordingId:s,timecodeMs:H,shape:c.shape,geometry:JSON.stringify(c.geometry),color:c.color,text:c.text};v.current.enqueue(async()=>{try{return await le(S,b?{...U,image:b}:U)}catch(M){if(!b)throw M;return le(S,U)}}).catch(M=>t({type:"SET_ERROR_BANNER",message:`Annotation failed: ${M.message}`}))},T=()=>{if(!e.groupId)return;const s=e.groupId,c=e.startedAt?Date.now()-e.startedAt:0;t({type:"CLEAR_OVERLAY_ANNOTATIONS"}),v.current.enqueue(()=>at(s,c)).catch(()=>t({type:"SET_ERROR_BANNER",message:"Couldn't clear marks from the recording. They will still appear in the video."}))},w=()=>{for(const s of e.tiles){const c=`/xenon/api/control/${encodeURIComponent(s.udid)}/stream/leave`;try{navigator.sendBeacon?navigator.sendBeacon(c):fetch(c,{method:"POST",keepalive:!0}).catch(()=>{})}catch{}}e.tiles.forEach(s=>t({type:"REMOVE_TILE",udid:s.udid}))},[C,$]=d.useState(null),V=async()=>{const s=C==null?void 0:C.tiles;if(!s)return;const c=Qt(s,n,p);for(const S of c.restore)await y(S);const b=rr(c.restore.length,c.skipped);$(b?{message:b,tiles:null}:null),N(S=>S+1)},m=or({idleAfterMs:ge,warningSec:sr,enabled:Zt(e.tiles.length,e.recordingPhase),onWarning:()=>{},onTimeout:()=>{const s=e.tiles;w(),$({message:er(s.length,ge/6e4),tiles:s})}});return r.jsxs(ye.Provider,{value:{state:e,dispatch:t},children:[r.jsxs("div",{className:"flex flex-col h-full p-4 gap-3",children:[r.jsxs("header",{className:"flex items-center justify-between gap-3",children:[r.jsxs("div",{className:"flex items-center gap-3",children:[r.jsx(ct,{icon:He,children:"Live devices"}),r.jsx(vt,{value:e.layout,onChange:s=>t({type:"SET_LAYOUT",layout:s})})]}),r.jsx(Gt,{selectedUdids:e.tiles.map(s=>s.udid),onClearMarks:T})]}),e.banner&&r.jsxs("div",{role:"alert",className:`text-sm rounded border px-3 py-2 flex items-center gap-2 ${e.banner.tone==="error"?"border-[rgb(var(--rgb-red)/0.4)] bg-[rgb(var(--rgb-red)/0.1)] text-[var(--red-100)]":"border-[rgb(var(--rgb-success)/0.4)] bg-[rgb(var(--rgb-success)/0.1)] text-[var(--text)]"}`,children:[r.jsx("span",{className:"flex-1",children:e.banner.message}),r.jsx("button",{type:"button",className:"text-xs opacity-80 hover:opacity-100",onClick:()=>t({type:"SET_BANNER",banner:null}),children:"Dismiss"})]}),C&&r.jsx(Yt,{message:C.message,onRestore:C.tiles&&!e.recording?V:void 0,onDismiss:()=>$(null)}),r.jsxs("div",{className:"grid grid-cols-[260px_1fr] gap-3 flex-1 min-h-0",children:[r.jsxs("aside",{className:"flex flex-col gap-2 border border-[var(--border)] rounded p-3 overflow-y-auto min-h-0",children:[r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsx("span",{className:"text-xs text-[var(--text-dim)]",children:"Devices"}),r.jsx("button",{className:"text-xs underline opacity-70 hover:opacity-100",onClick:()=>N(s=>s+1),children:"Refresh"})]}),r.jsx(xt,{devices:j,inMosaic:f,onToggle:x,status:i}),r.jsx("p",{className:"mt-2 text-[11px] text-[var(--text-dim)] leading-relaxed",children:"Click a device to add it to the grid. Record captures every device on the grid; after Stop, download the video."})]}),r.jsx("main",{className:"border border-[var(--border)] rounded p-2 overflow-y-auto h-full min-h-0",children:r.jsx(zt,{layout:e.layout,tiles:e.tiles,annotateMode:e.annotateMode,shape:e.shape,color:e.color,overlayAnnotationsByRecording:e.overlayAnnotations,onOverlayAnnotationsChange:(s,c)=>t({type:"SET_OVERLAY_ANNOTATIONS",recordingId:s,annotations:c}),onAnnotation:O,onRemove:R,onDropDevice:x})})]})]}),m.warning&&m.remainingSec!==null&&r.jsx(Kt,{remainingSec:m.remainingSec,onContinue:m.reset,onReleaseNow:w})]})}export{Er as default};