@xenon-device-management/xenon 1.7.4 → 1.7.5
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.
- package/lib/package.json +1 -1
- package/lib/public/assets/{ApiKeyGate-DX4HKDBA.js → ApiKeyGate-BVaB1Eg_.js} +1 -1
- package/lib/public/assets/{BugReportButton-N-NBBb3l.js → BugReportButton-CioDV7QP.js} +1 -1
- package/lib/public/assets/DeviceMosaicView-BtRxZTXc.js +2 -0
- package/lib/public/assets/{EmptyState-X7DYPoPj.js → EmptyState-UcJej2vi.js} +1 -1
- package/lib/public/assets/{FieldGroup-a9gzUh4W.js → FieldGroup-CYlyr34Q.js} +1 -1
- package/lib/public/assets/{Modal-Cuva-zxU.js → Modal-CjmzIR4Y.js} +1 -1
- package/lib/public/assets/{SegmentedControl-Bdc4A0UR.js → SegmentedControl-wRDvkYAQ.js} +1 -1
- package/lib/public/assets/{SettingCard-DVZUpQJR.js → SettingCard-BzjYlAYS.js} +1 -1
- package/lib/public/assets/{Table-Casq-_Hh.js → Table-ta1UYEeC.js} +1 -1
- package/lib/public/assets/{activity-KiLkzk_U.js → activity-jltyhcrF.js} +1 -1
- package/lib/public/assets/{ai-settings-0dEPftlc.js → ai-settings-Dfjxb1SZ.js} +1 -1
- package/lib/public/assets/{api-keys-Cg1cbSUt.js → api-keys-cFOese97.js} +1 -1
- package/lib/public/assets/{apps-CyetRRfI.js → apps-sHtZU8T_.js} +1 -1
- package/lib/public/assets/{arrow-left-C1v7ukDM.js → arrow-left-DGZzN6Sl.js} +1 -1
- package/lib/public/assets/{arrow-up-right-B-YmrLSY.js → arrow-up-right-DtZUe0YH.js} +1 -1
- package/lib/public/assets/{auth-shell-BXwxuIPK.js → auth-shell-D0eF5eyS.js} +1 -1
- package/lib/public/assets/{builds-page-wquO6Xb2.js → builds-page-DxDwaq_t.js} +1 -1
- package/lib/public/assets/{button-xZrEx8cE.js → button-3RkAu6xE.js} +1 -1
- package/lib/public/assets/{check-CDOZKQih.js → check-D6DDcFwM.js} +1 -1
- package/lib/public/assets/{chevron-right-CnpP0W_l.js → chevron-right-BtA7DR8A.js} +1 -1
- package/lib/public/assets/{circle-check-DDqpikKW.js → circle-check-Bjgvpq5F.js} +1 -1
- package/lib/public/assets/{circle-x-DcBe5J67.js → circle-x-_TOawobA.js} +1 -1
- package/lib/public/assets/{clock-CfNmWqai.js → clock-pm4LDp97.js} +1 -1
- package/lib/public/assets/{copy-DkXT5Ggc.js → copy-x-UuUQkc.js} +1 -1
- package/lib/public/assets/{device-explorer-B3PK2XW9.js → device-explorer-BDih8ZO9.js} +1 -1
- package/lib/public/assets/{download-B0tbRUtY.js → download-CuDjBGgs.js} +1 -1
- package/lib/public/assets/{forgot-password-DvHMwK0-.js → forgot-password-lgHuL3gW.js} +1 -1
- package/lib/public/assets/{funnel-DfsfiM0o.js → funnel-DQuWVevA.js} +1 -1
- package/lib/public/assets/{index-wbxPcwMM.js → index--TKsSVZp.js} +2 -2
- package/lib/public/assets/{layers-D-4LYQG3.js → layers-CdE4q4fN.js} +1 -1
- package/lib/public/assets/{lock-Cf_J2NEl.js → lock-D2Vg6I6G.js} +1 -1
- package/lib/public/assets/{login-DCIOMQb8.js → login-C2dtnYcH.js} +1 -1
- package/lib/public/assets/{maintenance-settings-CDDl4Q8B.js → maintenance-settings-Ck0suBTt.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-DoCqhosb.js → mouse-pointer-2-JuM7rDhC.js} +1 -1
- package/lib/public/assets/{overview-DNeH4FfG.js → overview-BBzmQBEl.js} +1 -1
- package/lib/public/assets/{page-header-DZpQDeCw.js → page-header-D5tpZwkI.js} +1 -1
- package/lib/public/assets/{plus-CTjPrkU5.js → plus-tcA3_CAo.js} +1 -1
- package/lib/public/assets/{profile-page-DKiZOb_Y.js → profile-page-BuuLTEtS.js} +1 -1
- package/lib/public/assets/{reset-password-6rl6YA7S.js → reset-password-ERq0WAyA.js} +1 -1
- package/lib/public/assets/{runbook-page-DTTw_cb3.js → runbook-page-BubL06xm.js} +1 -1
- package/lib/public/assets/{selector-detail-page-BVPMp3ZK.js → selector-detail-page-DgL7bNdy.js} +1 -1
- package/lib/public/assets/{selector-health-BVJ8fGyw.js → selector-health-B-V7vomq.js} +1 -1
- package/lib/public/assets/{selector-health-page-CZ_AAjtV.js → selector-health-page-DWy7bTdU.js} +1 -1
- package/lib/public/assets/{session-detail-page-BApz2wZr.js → session-detail-page-COgS5oAy.js} +1 -1
- package/lib/public/assets/{settings-CuYjTdtb.js → settings-D0fQ4JNR.js} +1 -1
- package/lib/public/assets/{shield-DVYhrtS6.js → shield-DINNxWHp.js} +1 -1
- package/lib/public/assets/{shield-alert-BhxlmXys.js → shield-alert-DDDbE5ER.js} +1 -1
- package/lib/public/assets/{status-pill-outline-DjKuXDLG.js → status-pill-outline-CFz5xaoF.js} +1 -1
- package/lib/public/assets/{tablet-CxgLRRaU.js → tablet-DoE7LIzI.js} +1 -1
- package/lib/public/assets/{teams-EuX6i7H2.js → teams-DpW-IUPC.js} +1 -1
- package/lib/public/assets/{trash-2-8zA1h40t.js → trash-2-Dvr08Dpq.js} +1 -1
- package/lib/public/assets/{useSocket-qyqK9PWr.js → useSocket-COssbQIj.js} +1 -1
- package/lib/public/assets/{users-CPafGHcL.js → users-DKmLh2WK.js} +1 -1
- package/lib/public/assets/{webhook-settings-y8EAM79b.js → webhook-settings-nwtLvp2Q.js} +1 -1
- package/lib/public/assets/{zap-C-b2-XlO.js → zap-CM2cWetx.js} +1 -1
- package/lib/public/index.html +1 -1
- package/lib/src/device-managers/ios/IOSStreamService.js +55 -7
- package/lib/src/device-managers/ios/iosStreamDiagnostics.js +20 -0
- package/lib/src/services/PortAllocator.js +14 -0
- package/lib/test/unit/PortAllocator.test.js +21 -0
- package/lib/test/unit/iosStreamDiagnostics.spec.js +21 -0
- package/package.json +1 -1
- package/lib/public/assets/DeviceMosaicView-BEDnJqk5.js +0 -2
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as m,j as t,X as T}from"./index-wbxPcwMM.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":{const n=e.tiles.map(l=>({...l,recordingId:void 0}));return{...e,recording:!1,tiles:n}}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||!!o.offline,R=o.offline?"Offline":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 ${o.offline?"bg-zinc-600":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,'".']}),t.jsxs("div",{className:"text-[10px] text-[var(--text-dim)] px-1 pt-2 flex items-center gap-3",children:[t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-emerald-400 inline-block"})," available"]}),t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-yellow-500 inline-block"})," in use"]}),t.jsxs("span",{className:"inline-flex items-center gap-1",children:[t.jsx("span",{className:"w-1.5 h-1.5 rounded-full bg-zinc-600 inline-block"})," offline"]})]})]})}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 C(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:C({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:[C({x:1.5,y:3,width:5,height:8}),C({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:[C({x:1.5,y:1.5,width:5,height:5}),C({x:7.5,y:1.5,width:5,height:5}),C({x:1.5,y:7.5,width:5,height:5}),C({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:[C({x:1,y:1.5,width:3.3,height:5}),C({x:5.35,y:1.5,width:3.3,height:5}),C({x:9.7,y:1.5,width:3.3,height:5}),C({x:1,y:7.5,width:3.3,height:5}),C({x:5.35,y:7.5,width:3.3,height:5}),C({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 ke({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,k)=>{const A=N,D=v,M=Math.min(1,Math.max(0,(a-k.left)/k.width)),I=Math.min(1,Math.max(0,(h-k.top)/k.height));return{x:Math.round(M*A),y:Math.round(I*D)}},Z=(a,h,k)=>{const A=Date.now()+Math.random(),D=a-k.left,M=h-k.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 k=a.clientX-h.startX,A=a.clientY-h.startY,D=Math.hypot(k,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(k){console.error(`[DeviceTile] keystroke "${h}" failed for ${e}`,k)}},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 k=await fetch(`data:image/png;base64,${h}`).then(M=>M.blob()),A=URL.createObjectURL(k),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(Ce,{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 Ce({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(ke,{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,offline:!!e.offline}}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};
|