farm-orchestrator 1.1.91 → 1.1.93

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.
@@ -3080,7 +3080,7 @@ ${ee.map(H=>`- ${H.id.substring(0,8)}…: ${H.error}`).join(`
3080
3080
  ${Dr}
3081
3081
 
3082
3082
  Session ID: ${(p==null?void 0:p.substring(0,8))||"N/A"}
3083
- Timestamp: ${new Date().toISOString()}`}),Le==="connected"&&Ke==="connected"?_e<1e4&&Xr.current===0?(Xr.current=1,console.log("🔑 Stream frozen — requesting keyframe (attempt 1)"),ze("Stream frozen — requesting keyframe from node...","warning"),Ae.requestKeyframe()):_e>=1e4&&_e<2e4&&Xr.current<2?(Xr.current=2,console.log("🔑 Stream still frozen — requesting keyframe (attempt 2)"),ze("Stream still frozen — requesting keyframe again...","warning"),Ae.requestKeyframe()):_e>=2e4&&Xr.current<3&&(Xr.current=3,console.log("🔄 Stream frozen >20s — escalating to full WebRTC reconnection"),ze("Stream frozen >20s — reconnecting WebRTC...","error"),Ae.stopReconnection(),window.dispatchEvent(new CustomEvent("manual-device-reconnect"))):Xr.current===0&&(ze(`Video stream frozen (no frames for ${Math.round(ye/1e3)}s) - ${bt}`,"error"),Xr.current=1)}else if(Zt.current&&ye<Se){const _e=Xr.current;console.log("✅ Stream recovered after",_e,"recovery attempts"),Zt.current=!1,Hl.current=null,Xr.current=0,U(Le=>({...Le,status:"healthy",diagnosticInfo:null})),_e>0&&ze("Stream recovered successfully","success")}},3e3),console.log("✅ Started stream health monitoring")},[j,n,ze]),Xe=m.useCallback(()=>{Ha.current&&(clearInterval(Ha.current),Ha.current=null,console.log("🛑 Stopped stream health monitoring"))},[]),Je=m.useCallback(()=>{const ce=Te.current;if(!(!ce||ce.paused||!ce.buffered.length))try{const pe=ce.buffered.end(ce.buffered.length-1),ye=ce.currentTime,Se=pe-ye;console.log(`⚡ Catching up to live: skipping ${Se.toFixed(2)}s of buffered video`,{currentTime:ye.toFixed(2),bufferedEnd:pe.toFixed(2),latency:Se.toFixed(2)}),ce.currentTime=Math.max(pe-.1,ye),ze(`Caught up to live stream (skipped ${Se.toFixed(1)}s)`,"info")}catch(pe){console.warn("Could not catch up to live:",pe)}},[ze]);m.useEffect(()=>{const ce=()=>{if(document.hidden)Ei.current=Date.now(),console.log("📱 Tab hidden - video decoding may be throttled");else{const pe=Ei.current?Date.now()-Ei.current:0;Ei.current=null,console.log(`📱 Tab visible again after ${(pe/1e3).toFixed(1)}s`),pe>1e3&&j&&n==="connected"&&setTimeout(()=>{Je()},100)}};return document.addEventListener("visibilitychange",ce),()=>{document.removeEventListener("visibilitychange",ce)}},[j,n,Je]),m.useEffect(()=>{if(ea.current&&(clearInterval(ea.current),ea.current=null),!(n!=="connected"||!j))return ea.current=setInterval(()=>{const ce=Te.current;if(!(!ce||ce.paused||!ce.buffered.length))try{const pe=ce.buffered.end(ce.buffered.length-1),ye=ce.currentTime,Se=pe-ye;Se>2&&(console.warn(`⚠️ Video latency detected: ${Se.toFixed(2)}s behind live`,{currentTime:ye.toFixed(2),bufferedEnd:pe.toFixed(2)}),Je())}catch{}},2e3),console.log("✅ Started video latency monitoring"),()=>{ea.current&&(clearInterval(ea.current),ea.current=null,console.log("🛑 Stopped video latency monitoring"))}},[n,j,Je]);const Vt=m.useCallback(()=>{if(!Te.current||!p||(g==null?void 0:g.platform)==="ios")return;const ce=Te.current.videoWidth,pe=Te.current.videoHeight;if(ce>0&&pe>0){const ye=ce>pe,Se=ye?"LANDSCAPE":"PORTRAIT";W(_e=>{if(_e!==Se){if(console.log(`🔄 Syncing orientation from video: ${Se} (${ce}x${pe})`),N(ye),pt.current){const Le=pt.current,Ke=Se==="PORTRAIT"?Le:{width:Le.height,height:Le.width};y(Ke),ft.current=Ke,Ne.current&&Ne.current.updateDeviceScreenInfo(Ke)}return Se}return _e})}},[p,g==null?void 0:g.platform]);m.useEffect(()=>{if((g==null?void 0:g.platform)!=="ios"||!p||n!=="connected")return;const pe=setInterval(async()=>{try{const ye=await be.makeGETRequest(`/wd/hub/session/${p}/orientation`,{}),Se=(ye.value||ye.orientation||"PORTRAIT").toUpperCase();W(_e=>{if(_e!==Se){if(console.log(`🔄 iOS orientation changed (polled): ${_e} → ${Se}`),N(Se==="LANDSCAPE"),pt.current){const Le=pt.current,Ke=Se==="PORTRAIT"?Le:{width:Le.height,height:Le.width};y(Ke),ft.current=Ke,Ne.current&&Ne.current.updateDeviceScreenInfo(Ke)}return Se}return _e})}catch{}},3e3);return()=>clearInterval(pe)},[g==null?void 0:g.platform,p,n]);const cr=(g==null?void 0:g.platform)==="ios"&&D==="LANDSCAPE";m.useEffect(()=>{Ne.current&&Ne.current.setVideoRotation(cr?-90:0)},[cr]);const vs=ce=>{if(console.log("🔄 Connection state changed:",ce),M){console.log("🔌 Connection state changed during close (suppressing errors):",ce);return}const pe=Ae.getConnectionState(),ye=Ae.getICEConnectionState(),Se=Ae.getICEGatheringState();if(console.log("📊 Connection details:",{connectionState:pe,iceConnectionState:ye,iceGatheringState:Se}),ce==="connected"){a("connected"),h(null),oe(!1);let _e=0;const Le=10,Ke=()=>{_e++;const Ge=ft.current;console.log(`🔄 Retry ${_e}/${Le}:`,{hasVideo:!!Te.current,hasCanvas:!!me.current,hasScreenSize:!!Ge,hasHandler:!!Ne.current,deviceScreenSize:Ge}),Te.current&&me.current&&Ge&&!Ne.current?(console.log("✅ All conditions met, calling initializeInteractionHandler"),No()):_e<Le&&!Ne.current?(console.log("⏳ Retrying in 200ms..."),setTimeout(Ke,200)):_e>=Le&&!Ne.current&&(console.error("❌ Failed to initialize interaction handler after",Le,"attempts"),console.error("Final state:",{hasVideo:!!Te.current,hasCanvas:!!me.current,hasScreenSize:!!Ge,deviceScreenSize:Ge}))};setTimeout(Ke,500)}else if(ce==="disconnected"||ce==="failed"){if(!(pe==="disconnected"||pe==="failed"||ce==="failed"&&ye==="failed"||ce==="disconnected"&&pe!=="new"&&pe!=="connecting"&&ye==="disconnected")){console.log("ℹ️ Connection state change during negotiation (not an error):",{state:ce,connectionState:pe,iceConnectionState:ye,iceGatheringState:Se}),(pe==="new"||pe==="connecting")&&a("connecting");return}console.error("❌ WebRTC connection failed or disconnected:",{state:ce,connectionState:pe,iceConnectionState:ye,iceGatheringState:Se});const Le=Ae.getReconnectionStatus();if(Le.isReconnecting){oe(!0),a("connecting"),ze(`WebRTC connection lost. Attempting to reconnect (${Le.attempts}/${Le.maxAttempts})...`,"warning");return}let Ke="";X==="disconnected"?Ke="Node machine disconnected. The device server hosting your device has lost connection. Your session may be interrupted. Please try reconnecting or start a new session.":ee?ce==="failed"||ye==="failed"?Ke="WebRTC connection failed. This may be due to network restrictions or firewall issues. If using hosted servers, ensure TURN servers are configured for NAT traversal. Please try refreshing the page or reconnecting manually.":ce==="disconnected"||ye==="disconnected"?Ke="WebRTC connection lost. The connection to the device was interrupted. This may be due to network issues, device going to sleep, or connection timeout. Automatic reconnection will be attempted.":Ke="WebRTC connection error. The connection to the device could not be established or was lost.":Ke="Your internet connection was lost. The session will be terminated if connection is not restored. Please check your network connection and try again.",h(Ke),oe(!1),a(ce==="failed"?"failed":"disconnected")}else ce==="connecting"&&a("connecting")},$u=async(ce="component-unmount")=>{console.log("[ManualDevice] cleanup() called",{reason:ce,sessionId:p});try{if(F(!0),Xe(),ea.current&&(clearInterval(ea.current),ea.current=null),Ne.current&&(Ne.current.destroy(),Ne.current=null),p){try{await Ve.closeWebRTC(p,ce)}catch(pe){console.warn("Failed to notify hub of WebRTC close:",pe)}try{await yt.deleteSession(p)}catch(pe){console.warn("Failed to delete session:",pe)}}Ae.close(),setTimeout(()=>{Ve.disconnect()},100)}catch(pe){console.error("Error during cleanup:",pe)}},Bh=async()=>{F(!0);try{await $u("user-close-button"),await new Promise(ce=>setTimeout(ce,500)),t("/")}catch(ce){console.error("Error closing session:",ce),F(!1)}},D5=async()=>{await I5(),o(null),c(void 0),d(void 0),h(null),k([]),a("connecting"),Kl()},I5=async()=>{try{Xe(),p&&(console.log("🧹 Cleaning up failed session:",p),await yt.deleteSession(p),v(null)),Ae.close(),Ve.disconnect(),Ne.current&&(Ne.current.destroy(),Ne.current=null)}catch(ce){console.error("Error during error cleanup:",ce)}},Ty=async()=>{await I5(),t("/")},Ay=async()=>{if(Ti(),!p){console.error("Cannot take screenshot: No session ID");return}try{const ce=await fetch(`/wd/hub/session/${p}/screenshot`);if(!ce.ok)throw new Error(`Screenshot request failed: ${ce.status}`);const pe=await ce.json(),ye=(pe==null?void 0:pe.value)??(pe==null?void 0:pe.screenshot);ye?(A(Se=>[...Se,ye]),console.log("✅ Screenshot captured successfully"),C!=="screenshots"&&T("screenshots")):(console.error("No screenshot data in response:",pe),ze("No screenshot data in response","error"))}catch(ce){console.error("❌ Failed to take screenshot:",ce);const pe=ce instanceof Error?ce.message:"Failed to capture screenshot";ze(`Screenshot error: ${pe}`,"error")}},$$=m.useCallback(ce=>{const pe=ce.match(/-(\d+)$/);if(pe){const ye=parseInt(pe[1],10)-1;ye>=0&&ye<E.length&&(A(Se=>Se.filter((_e,Le)=>Le!==ye)),console.log(`🗑️ Deleted screenshot at index ${ye+1}`))}},[E.length]),B$=m.useCallback(async ce=>{if(console.log("🔘 Device control clicked:",ce),Ti(),!p){console.error("❌ Cannot perform action: No session ID"),ze("Cannot perform action: No session ID","error");return}const pe=Ae.isDataChannelOpen();if(console.log("📊 Data channel state:",pe),!pe){console.error("❌ Cannot perform action: WebRTC data channel not open"),ze("WebRTC data channel not open, cannot perform action","error");return}V(ye=>new Set(ye).add(ce)),console.log("⏳ Starting command:",ce);try{switch(ce){case"home":await Ae.sendDeviceCommand({type:"deviceCommand",command:"home",platform:g==null?void 0:g.platform}),console.log(`✅ Home button pressed (${(g==null?void 0:g.platform)||"android"})`);break;case"back":await Ae.sendDeviceCommand({type:"deviceCommand",command:"back",platform:g==null?void 0:g.platform}),console.log("✅ Back button pressed");break;case"backgroundApps":if((g==null?void 0:g.platform)==="ios"){if(!b){console.error("Cannot open recent apps: Device screen size not available"),ze("Cannot open recent apps: Device screen size not available","error");break}try{const ye=b.width/2,Se=b.height/100*90;await Ae.sendDeviceCommand({type:"deviceCommand",command:"backgroundApps",platform:"ios",params:{actions:{actions:[{type:"pointer",id:"finger1",parameters:{pointerType:"touch"},actions:[{type:"pointerMove",duration:0,origin:"viewport",x:ye,y:b.height},{type:"pointerMove",duration:100,origin:"viewport",x:ye,y:Se}]}]}}}),console.log("✅ Recent apps gesture performed (iOS)"),ze("App switcher gesture performed (swipe up from bottom)","info")}catch(ye){const Se=ye instanceof Error?ye.message:"Failed to perform gesture";console.error("❌ Failed to open recent apps on iOS:",ye),ze(`Failed to open recent apps: ${Se}`,"error"),Ie.warn("Opening recent apps on iOS may not work reliably. iOS does not provide a direct API for this feature.",{position:"top-right",autoClose:4e3})}}else await Ae.sendDeviceCommand({type:"deviceCommand",command:"backgroundApps",platform:"android"}),console.log("✅ Recent apps opened (Android)");break;case"volumeUp":await Ae.sendDeviceCommand({type:"deviceCommand",command:"volumeUp",platform:g==null?void 0:g.platform}),console.log("✅ Volume up");break;case"volumeDown":await Ae.sendDeviceCommand({type:"deviceCommand",command:"volumeDown",platform:g==null?void 0:g.platform}),console.log("✅ Volume down");break;case"takeScreenshot":await Ay();break;case"rotate":if((g==null?void 0:g.platform)==="android"||(g==null?void 0:g.platform)==="ios"){const ye=D==="PORTRAIT"?"LANDSCAPE":"PORTRAIT";try{await Ae.sendDeviceCommand({type:"deviceCommand",command:"rotate",platform:g==null?void 0:g.platform,params:{orientation:ye}}),await new Promise(Se=>setTimeout(Se,500));try{const Se=await be.makeGETRequest(`/wd/hub/session/${p}/orientation`,{}),Le=(Se.value||Se.orientation||ye).toUpperCase();if(W(Le),N(Le==="LANDSCAPE"),pt.current){const Ke=pt.current,Ge=Le==="PORTRAIT"?Ke:{width:Ke.height,height:Ke.width};y(Ge),ft.current=Ge,Ne.current&&(Ne.current.updateDeviceScreenInfo(Ge),console.log("🔄 Updated interaction handler with rotated screen size:",Ge))}else if(b){const Ke=Le==="PORTRAIT"?b.width<b.height?b:{width:b.height,height:b.width}:b.width>b.height?b:{width:b.height,height:b.width};y(Ke),ft.current=Ke,Ne.current&&(Ne.current.updateDeviceScreenInfo(Ke),console.log("🔄 Updated interaction handler with rotated screen size:",Ke))}console.log(`✅ Device rotated to ${Le} (verified)`),ze(`Device rotated to ${Le}`,"success")}catch(Se){if(console.warn("⚠️ Could not verify orientation, updating UI optimistically:",Se),W(ye),N(ye==="LANDSCAPE"),pt.current){const _e=pt.current,Le=ye==="PORTRAIT"?_e:{width:_e.height,height:_e.width};y(Le),ft.current=Le,Ne.current&&Ne.current.updateDeviceScreenInfo(Le)}console.log(`✅ Device rotated to ${ye} (optimistic)`),ze(`Device rotated to ${ye}`,"success")}}catch(Se){console.error("❌ Failed to rotate device:",Se);const _e=Se instanceof Error?Se.message:"Failed to rotate device";ze(`Rotation failed: ${_e}`,"error"),Ie.error(`Rotation failed: ${_e}`,{position:"top-right",autoClose:5e3})}}else console.warn("⚠️ Rotation is only supported for Android and iOS devices"),ze("Rotation is only supported for Android and iOS devices","warning");break;default:console.warn("Unknown action:",ce)}}catch(ye){console.error(`❌ Failed to perform action ${ce}:`,ye);const Se=ye instanceof Error?ye.message:`Failed to perform ${ce}`;ze(`Control error: ${Se}`,"error"),ye instanceof Error&&console.error("Error details:",{message:ye.message,stack:ye.stack,dataChannelOpen:Ae.isDataChannelOpen(),connectionState:Ae.getConnectionState(),iceState:Ae.getICEConnectionState()})}finally{V(ye=>{const Se=new Set(ye);return Se.delete(ce),Se}),console.log("✅ Command completed:",ce)}},[p,g==null?void 0:g.platform,b,D,ze,Ay,Ae,be,Ne,pt,ft,W,N,y,Ie]),M5=m.useCallback(()=>{const ce=document.createElement("input");ce.type="file",ce.accept=".apk,.aab",ce.onchange=async pe=>{var Se,_e,Le;const ye=(Se=pe.target.files)==null?void 0:Se[0];if(!(!ye||!p))try{const Ke=new FormData;Ke.append("file",ye);const Ge=localStorage.getItem("token");if(!Ge)throw new Error("Authentication required. Please log in again.");const Dr=await fetch("/api/uploaded-apps/upload",{method:"POST",headers:{Authorization:`Bearer ${Ge}`},body:Ke,credentials:"include"});if(!Dr.ok){const So=await Dr.json().catch(()=>({message:"Failed to upload file"}));throw new Error(So.message||"Failed to upload file")}const bt=await Dr.json();console.log("✅ File uploaded:",bt);const br=((_e=bt.file)==null?void 0:_e.uploadedFileName)||((Le=bt.file)==null?void 0:Le.filename);if(!br)throw new Error("No file path returned from upload");Ae.isDataChannelOpen()?await Ae.sendDeviceCommand({type:"deviceCommand",command:"installApp",platform:g==null?void 0:g.platform,params:{appPath:br}}):await be.makePOSTRequest(`/wd/hub/session/${p}/appium/device/install_app`,{},{appPath:br}),console.log("✅ App installed successfully"),ze(`App ${ye.name} installed successfully`,"success")}catch(Ke){console.error("❌ Failed to upload/install app:",Ke),ze(`Failed to install app: ${Ke instanceof Error?Ke.message:"Unknown error"}`,"error")}},ce.click()},[p,g==null?void 0:g.platform]),U$=m.useMemo(()=>[{action:"home",icon:s.jsx(oM,{className:"w-6 h-6"}),name:"Home",variant:"primary"},{action:"back",icon:s.jsx(av,{className:"w-6 h-6"}),name:"Back",variant:"primary"},{action:"backgroundApps",icon:s.jsx(dM,{className:"w-6 h-6"}),name:"Recent",variant:"primary"},{action:"volumeUp",icon:s.jsx(zY,{className:"w-6 h-6"}),name:"Vol +",variant:"primary"},{action:"volumeDown",icon:s.jsx(FY,{className:"w-6 h-6"}),name:"Vol -",variant:"primary"},{action:"takeScreenshot",icon:s.jsx(uY,{className:"w-6 h-6"}),name:"Capture",variant:"primary"},{action:"uploadFile",icon:s.jsx(fM,{className:"w-6 h-6"}),name:"Upload",onClick:M5},{action:"rotate",icon:s.jsx(AY,{className:"w-6 h-6"}),name:"Rotate"},{action:"keyboard",icon:s.jsx(l0,{className:"w-6 h-6"}),name:R?"Keyboard On":"Keyboard Off",onClick:()=>P(ce=>!ce)}],[g==null?void 0:g.platform,R,M5]);return i?s.jsx("div",{className:"flex items-center justify-center min-h-screen bg-gray-900 text-white",children:s.jsx(lke,{open:!0,error:i,platform:g==null?void 0:g.platform,traceId:l,originalMessage:u,onClose:Ty,onRetry:D5,onBack:Ty})}):s.jsxs("div",{className:"flex flex-col h-screen bg-gray-900 text-white relative overflow-hidden",children:[M&&s.jsx("div",{className:"fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center",children:s.jsx("div",{className:"bg-gray-800 rounded-lg p-8 border border-gray-700 shadow-2xl max-w-lg w-full mx-4",children:s.jsxs("div",{className:"flex flex-col items-center justify-center",children:[s.jsx(cke,{className:"mb-6"}),s.jsx("h3",{className:"text-2xl font-semibold text-white mb-2",children:"Closing Session"}),s.jsx("p",{className:"text-gray-400 text-center",children:"Cleaning up device and closing connection..."})]})})}),Q&&s.jsx("div",{className:"fixed inset-0 bg-black/80 backdrop-blur-md z-50 flex items-center justify-center",children:s.jsx("div",{className:`bg-gray-900 rounded-xl p-8 border-2 ${he<=0?"border-red-500 shadow-[0_0_50px_rgba(239,68,68,0.3)]":"border-yellow-500 shadow-[0_0_50px_rgba(234,179,8,0.3)]"} max-w-md w-full mx-4`,children:s.jsxs("div",{className:"flex flex-col items-center justify-center",children:[s.jsxs("div",{className:"relative mb-6",children:[s.jsx("div",{className:`w-20 h-20 rounded-full ${he<=0?"bg-red-500/20":"bg-yellow-500/20"} flex items-center justify-center animate-pulse`,children:s.jsx(ku,{className:`w-10 h-10 ${he<=0?"text-red-500":"text-yellow-500"}`})}),s.jsx("div",{className:`absolute inset-0 rounded-full border-2 ${he<=0?"border-red-500":"border-yellow-500"} animate-ping opacity-50`})]}),s.jsx("h3",{className:"text-2xl font-bold text-white mb-3",children:he<=0?"Session Terminating...":"Session Idle Warning"}),s.jsx("div",{className:`bg-gray-800 rounded-lg px-8 py-4 mb-4 border ${he<=0?"border-red-700":"border-gray-700"}`,children:he<=0?s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"flex items-center justify-center gap-2",children:[s.jsx("div",{className:"w-2 h-2 bg-red-500 rounded-full animate-bounce"}),s.jsx("div",{className:"w-2 h-2 bg-red-500 rounded-full animate-bounce",style:{animationDelay:"0.1s"}}),s.jsx("div",{className:"w-2 h-2 bg-red-500 rounded-full animate-bounce",style:{animationDelay:"0.2s"}})]}),s.jsx("div",{className:"text-sm text-gray-400 mt-2",children:"Closing session..."})]}):s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"text-5xl font-bold text-yellow-500 tabular-nums",children:Math.ceil(he/1e3)}),s.jsx("div",{className:"text-sm text-gray-400 mt-1",children:"seconds remaining"})]})}),s.jsx("p",{className:"text-gray-300 text-center mb-6",children:he<=0?"Please wait while we close your session...":"Your session will be terminated due to inactivity."}),s.jsx("button",{onClick:Lh,disabled:he<=0,className:`w-full font-bold py-4 px-6 rounded-lg transition-all ${he<=0?"bg-gray-600 text-gray-400 cursor-not-allowed":"bg-yellow-500 hover:bg-yellow-400 text-gray-900 transform hover:scale-[1.02] shadow-lg shadow-yellow-500/25"}`,children:he<=0?"Session Ending...":"Continue Session"}),he>0&&s.jsx("p",{className:"text-xs text-gray-500 mt-4 text-center",children:"Click the button or interact with the device to stay connected"})]})})}),s.jsx("div",{className:"bg-gray-800 border-b border-gray-700 px-6 py-3",children:s.jsxs("div",{className:"flex items-center justify-between gap-6 flex-wrap",children:[s.jsxs("div",{className:"flex items-center gap-4 flex-shrink-0",children:[s.jsx("h1",{className:"text-xl font-bold text-yellow-400",children:(g==null?void 0:g.name)||"Device"}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("div",{className:`w-3 h-3 rounded-full ${n==="connected"?"bg-green-500":n==="connecting"?"bg-yellow-500 animate-pulse":"bg-red-500"}`}),s.jsx("span",{className:"text-sm text-gray-400 capitalize",children:n})]}),n==="connected"&&s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("div",{className:`w-2 h-2 rounded-full ${X==="connected"?"bg-green-500":X==="disconnected"?"bg-red-500":"bg-gray-500"}`}),s.jsx("span",{className:"text-xs text-gray-500",children:X==="connected"?"Node Online":X==="disconnected"?"Node Offline":"Node Status Unknown"})]}),!ee&&s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("div",{className:"w-2 h-2 rounded-full bg-red-500"}),s.jsx("span",{className:"text-xs text-red-400",children:"No Internet"})]}),n==="connected"&&j&&s.jsx("div",{className:"flex items-center gap-2",children:$.status==="frozen"?s.jsxs(s.Fragment,{children:[s.jsx(an,{className:"w-4 h-4 text-red-500"}),s.jsx("span",{className:"text-sm text-red-400",children:"Stream Frozen"}),s.jsxs("button",{onClick:()=>J(!0),className:"text-xs text-red-400 hover:text-red-300 underline flex items-center gap-1",title:"View diagnostic information",children:[s.jsx(B4,{className:"w-3 h-3"}),"Diagnostic Info"]})]}):$.status==="stalled"?s.jsxs(s.Fragment,{children:[s.jsx(eu,{className:"w-4 h-4 text-yellow-500"}),s.jsx("span",{className:"text-sm text-yellow-400",children:"Stream Stalled"}),s.jsxs("button",{onClick:()=>J(!0),className:"text-xs text-yellow-400 hover:text-yellow-300 underline flex items-center gap-1",title:"View diagnostic information",children:[s.jsx(B4,{className:"w-3 h-3"}),"Diagnostic Info"]})]}):$.status==="healthy"?s.jsxs(s.Fragment,{children:[s.jsx(wl,{className:"w-4 h-4 text-green-500"}),s.jsx("span",{className:"text-sm text-green-400",children:"Stream Active"})]}):null})]}),g&&s.jsxs("div",{className:"flex items-center gap-6 text-sm flex-1 min-w-0",children:[s.jsxs("span",{className:"text-gray-400 whitespace-nowrap",children:["UDID: ",s.jsx("span",{className:"text-yellow-400 font-medium",children:g.udid})]}),s.jsxs("span",{className:"text-gray-400 whitespace-nowrap",children:["Platform: ",s.jsx("span",{className:"text-yellow-400 font-medium",children:g.platform})]}),s.jsxs("span",{className:"text-gray-400 whitespace-nowrap",children:["Version: ",s.jsx("span",{className:"text-yellow-400 font-medium",children:g.version})]}),b&&s.jsxs("span",{className:"text-gray-400 whitespace-nowrap",children:["Screen:"," ",s.jsxs("span",{className:"text-yellow-400 font-medium",children:[b.width,"x",b.height]})]}),p&&s.jsxs("span",{className:"text-gray-400 whitespace-nowrap",children:["Session:"," ",s.jsxs("span",{className:"text-yellow-400 font-medium",children:[p.substring(0,8),"..."]})]})]}),s.jsx("button",{onClick:Bh,disabled:M,className:"px-4 py-2 bg-red-600 hover:bg-red-700 rounded transition flex-shrink-0 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2",children:M?s.jsxs(s.Fragment,{children:[s.jsxs("svg",{className:"animate-spin h-4 w-4 text-white",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",children:[s.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),s.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),s.jsx("span",{children:"Closing..."})]}):"Close Session"})]})}),s.jsxs("div",{ref:dt,className:"flex-1 flex flex-row p-6 bg-gray-900 relative overflow-hidden min-h-0",children:[n==="connected"&&j&&s.jsxs("div",{className:"flex flex-col gap-4 flex-shrink-0 overflow-y-auto overflow-x-visible max-h-full pr-1 items-start",children:[s.jsx("div",{className:"bg-gradient-to-b from-gray-900/95 to-gray-800/95 backdrop-blur-xl rounded-2xl p-4 border border-gray-700/50 shadow-2xl flex-shrink-0",children:s.jsx(O5,{controls:U$,onControlClick:B$,disabled:!p,loadingActions:I})}),R&&s.jsx("div",{className:"bg-gradient-to-b from-gray-900/95 to-gray-800/95 backdrop-blur-xl rounded-2xl p-4 border border-gray-700/50 shadow-2xl flex-shrink-0",children:s.jsxs("div",{className:"flex flex-col gap-3 w-[300px]",children:[s.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[s.jsx(l0,{className:"w-5 h-5 text-yellow-400"}),s.jsx("h3",{className:"text-sm font-semibold text-white",children:"Text Input"})]}),s.jsx("textarea",{value:ie,onChange:ce=>q(ce.target.value),onKeyDown:ce=>{(ce.ctrlKey||ce.metaKey)&&ce.key==="Enter"&&(ce.preventDefault(),xe())},placeholder:`Enter or paste text here...
3083
+ Timestamp: ${new Date().toISOString()}`}),Le==="connected"&&Ke==="connected"?_e<1e4&&Xr.current===0?(Xr.current=1,console.log("🔑 Stream frozen — requesting keyframe (attempt 1)"),ze("Stream frozen — requesting keyframe from node...","warning"),Ae.requestKeyframe()):_e>=1e4&&_e<2e4&&Xr.current<2?(Xr.current=2,console.log("🔑 Stream still frozen — requesting keyframe (attempt 2)"),ze("Stream still frozen — requesting keyframe again...","warning"),Ae.requestKeyframe()):_e>=2e4&&Xr.current<3&&(Xr.current=3,console.log("🔄 Stream frozen >20s — escalating to full WebRTC reconnection"),ze("Stream frozen >20s — reconnecting WebRTC...","error"),Ae.stopReconnection(),window.dispatchEvent(new CustomEvent("manual-device-reconnect"))):Xr.current===0&&(ze(`Video stream frozen (no frames for ${Math.round(ye/1e3)}s) - ${bt}`,"error"),Xr.current=1)}else if(Zt.current&&ye<Se){const _e=Xr.current;console.log("✅ Stream recovered after",_e,"recovery attempts"),Zt.current=!1,Hl.current=null,Xr.current=0,U(Le=>({...Le,status:"healthy",diagnosticInfo:null})),_e>0&&ze("Stream recovered successfully","success")}},3e3),console.log("✅ Started stream health monitoring")},[j,n,ze]),Xe=m.useCallback(()=>{Ha.current&&(clearInterval(Ha.current),Ha.current=null,console.log("🛑 Stopped stream health monitoring"))},[]),Je=m.useCallback(()=>{const ce=Te.current;if(!(!ce||ce.paused||!ce.buffered.length))try{const pe=ce.buffered.end(ce.buffered.length-1),ye=ce.currentTime,Se=pe-ye;console.log(`⚡ Catching up to live: skipping ${Se.toFixed(2)}s of buffered video`,{currentTime:ye.toFixed(2),bufferedEnd:pe.toFixed(2),latency:Se.toFixed(2)}),ce.currentTime=Math.max(pe-.1,ye),ze(`Caught up to live stream (skipped ${Se.toFixed(1)}s)`,"info")}catch(pe){console.warn("Could not catch up to live:",pe)}},[ze]);m.useEffect(()=>{const ce=()=>{if(document.hidden)Ei.current=Date.now(),console.log("📱 Tab hidden - video decoding may be throttled");else{const pe=Ei.current?Date.now()-Ei.current:0;Ei.current=null,console.log(`📱 Tab visible again after ${(pe/1e3).toFixed(1)}s`),pe>1e3&&j&&n==="connected"&&setTimeout(()=>{Je()},100)}};return document.addEventListener("visibilitychange",ce),()=>{document.removeEventListener("visibilitychange",ce)}},[j,n,Je]),m.useEffect(()=>{if(ea.current&&(clearInterval(ea.current),ea.current=null),!(n!=="connected"||!j))return ea.current=setInterval(()=>{const ce=Te.current;if(!(!ce||ce.paused||!ce.buffered.length))try{const pe=ce.buffered.end(ce.buffered.length-1),ye=ce.currentTime,Se=pe-ye;Se>2&&(console.warn(`⚠️ Video latency detected: ${Se.toFixed(2)}s behind live`,{currentTime:ye.toFixed(2),bufferedEnd:pe.toFixed(2)}),Je())}catch{}},2e3),console.log("✅ Started video latency monitoring"),()=>{ea.current&&(clearInterval(ea.current),ea.current=null,console.log("🛑 Stopped video latency monitoring"))}},[n,j,Je]);const Vt=m.useCallback(()=>{if(!Te.current||!p||(g==null?void 0:g.platform)==="ios")return;const ce=Te.current.videoWidth,pe=Te.current.videoHeight;if(ce>0&&pe>0){const ye=ce>pe,Se=ye?"LANDSCAPE":"PORTRAIT";W(_e=>{if(_e!==Se){if(console.log(`🔄 Syncing orientation from video: ${Se} (${ce}x${pe})`),N(ye),pt.current){const Le=pt.current,Ke=Se==="PORTRAIT"?Le:{width:Le.height,height:Le.width};y(Ke),ft.current=Ke,Ne.current&&Ne.current.updateDeviceScreenInfo(Ke)}return Se}return _e})}},[p,g==null?void 0:g.platform]);m.useEffect(()=>{if((g==null?void 0:g.platform)!=="ios"||!p||n!=="connected")return;const pe=setInterval(async()=>{try{const ye=await be.makeGETRequest(`/wd/hub/session/${p}/orientation`,{}),Se=(ye.value||ye.orientation||"PORTRAIT").toUpperCase();W(_e=>{if(_e!==Se){if(console.log(`🔄 iOS orientation changed (polled): ${_e} → ${Se}`),N(Se==="LANDSCAPE"),pt.current){const Le=pt.current,Ke=Se==="PORTRAIT"?Le:{width:Le.height,height:Le.width};y(Ke),ft.current=Ke,Ne.current&&Ne.current.updateDeviceScreenInfo(Ke)}return Se}return _e})}catch{}},3e3);return()=>clearInterval(pe)},[g==null?void 0:g.platform,p,n]);const cr=(g==null?void 0:g.platform)==="ios"&&D==="LANDSCAPE";m.useEffect(()=>{Ne.current&&Ne.current.setVideoRotation(cr?-90:0)},[cr]);const vs=ce=>{if(console.log("🔄 Connection state changed:",ce),M){console.log("🔌 Connection state changed during close (suppressing errors):",ce);return}const pe=Ae.getConnectionState(),ye=Ae.getICEConnectionState(),Se=Ae.getICEGatheringState();if(console.log("📊 Connection details:",{connectionState:pe,iceConnectionState:ye,iceGatheringState:Se}),ce==="connected"){a("connected"),h(null),oe(!1);let _e=0;const Le=10,Ke=()=>{_e++;const Ge=ft.current;console.log(`🔄 Retry ${_e}/${Le}:`,{hasVideo:!!Te.current,hasCanvas:!!me.current,hasScreenSize:!!Ge,hasHandler:!!Ne.current,deviceScreenSize:Ge}),Te.current&&me.current&&Ge&&!Ne.current?(console.log("✅ All conditions met, calling initializeInteractionHandler"),No()):_e<Le&&!Ne.current?(console.log("⏳ Retrying in 200ms..."),setTimeout(Ke,200)):_e>=Le&&!Ne.current&&(console.error("❌ Failed to initialize interaction handler after",Le,"attempts"),console.error("Final state:",{hasVideo:!!Te.current,hasCanvas:!!me.current,hasScreenSize:!!Ge,deviceScreenSize:Ge}))};setTimeout(Ke,500)}else if(ce==="disconnected"||ce==="failed"){if(!(pe==="disconnected"||pe==="failed"||ce==="failed"&&ye==="failed"||ce==="disconnected"&&pe!=="new"&&pe!=="connecting"&&ye==="disconnected")){console.log("ℹ️ Connection state change during negotiation (not an error):",{state:ce,connectionState:pe,iceConnectionState:ye,iceGatheringState:Se}),(pe==="new"||pe==="connecting")&&a("connecting");return}console.error("❌ WebRTC connection failed or disconnected:",{state:ce,connectionState:pe,iceConnectionState:ye,iceGatheringState:Se});const Le=Ae.getReconnectionStatus();if(Le.isReconnecting){oe(!0),a("connecting"),ze(`WebRTC connection lost. Attempting to reconnect (${Le.attempts}/${Le.maxAttempts})...`,"warning");return}let Ke="";X==="disconnected"?Ke="Node machine disconnected. The device server hosting your device has lost connection. Your session may be interrupted. Please try reconnecting or start a new session.":ee?ce==="failed"||ye==="failed"?Ke="WebRTC connection failed. This may be due to network restrictions or firewall issues. If using hosted servers, ensure TURN servers are configured for NAT traversal. Please try refreshing the page or reconnecting manually.":ce==="disconnected"||ye==="disconnected"?Ke="WebRTC connection lost. The connection to the device was interrupted. This may be due to network issues, device going to sleep, or connection timeout. Automatic reconnection will be attempted.":Ke="WebRTC connection error. The connection to the device could not be established or was lost.":Ke="Your internet connection was lost. The session will be terminated if connection is not restored. Please check your network connection and try again.",h(Ke),oe(!1),a(ce==="failed"?"failed":"disconnected")}else ce==="connecting"&&a("connecting")},$u=async(ce="component-unmount")=>{console.log("[ManualDevice] cleanup() called",{reason:ce,sessionId:p});try{if(F(!0),Xe(),ea.current&&(clearInterval(ea.current),ea.current=null),Ne.current&&(Ne.current.destroy(),Ne.current=null),p){try{await Ve.closeWebRTC(p,ce)}catch(pe){console.warn("Failed to notify hub of WebRTC close:",pe)}try{await yt.deleteSession(p)}catch(pe){console.warn("Failed to delete session:",pe)}}Ae.close(),setTimeout(()=>{Ve.disconnect()},100)}catch(pe){console.error("Error during cleanup:",pe)}},Bh=async()=>{F(!0);try{await $u("user-close-button"),await new Promise(ce=>setTimeout(ce,500)),t("/")}catch(ce){console.error("Error closing session:",ce),F(!1)}},D5=async()=>{await I5(),o(null),c(void 0),d(void 0),h(null),k([]),a("connecting"),Kl()},I5=async()=>{try{Xe(),p&&(console.log("🧹 Cleaning up failed session:",p),await yt.deleteSession(p),v(null)),Ae.close(),Ve.disconnect(),Ne.current&&(Ne.current.destroy(),Ne.current=null)}catch(ce){console.error("Error during error cleanup:",ce)}},Ty=async()=>{await I5(),t("/")},Ay=async()=>{if(Ti(),!p){console.error("Cannot take screenshot: No session ID");return}try{const ce=await fetch(`/wd/hub/session/${p}/screenshot`);if(!ce.ok)throw new Error(`Screenshot request failed: ${ce.status}`);const pe=await ce.json(),ye=(pe==null?void 0:pe.value)??(pe==null?void 0:pe.screenshot);ye?(A(Se=>[...Se,ye]),console.log("✅ Screenshot captured successfully"),C!=="screenshots"&&T("screenshots")):(console.error("No screenshot data in response:",pe),ze("No screenshot data in response","error"))}catch(ce){console.error("❌ Failed to take screenshot:",ce);const pe=ce instanceof Error?ce.message:"Failed to capture screenshot";ze(`Screenshot error: ${pe}`,"error")}},$$=m.useCallback(ce=>{const pe=ce.match(/-(\d+)$/);if(pe){const ye=parseInt(pe[1],10)-1;ye>=0&&ye<E.length&&(A(Se=>Se.filter((_e,Le)=>Le!==ye)),console.log(`🗑️ Deleted screenshot at index ${ye+1}`))}},[E.length]),B$=m.useCallback(async ce=>{if(console.log("🔘 Device control clicked:",ce),Ti(),!p){console.error("❌ Cannot perform action: No session ID"),ze("Cannot perform action: No session ID","error");return}const pe=Ae.isDataChannelOpen();if(console.log("📊 Data channel state:",pe),!pe){console.error("❌ Cannot perform action: WebRTC data channel not open"),ze("WebRTC data channel not open, cannot perform action","error");return}V(ye=>new Set(ye).add(ce)),console.log("⏳ Starting command:",ce);try{switch(ce){case"home":await Ae.sendDeviceCommand({type:"deviceCommand",command:"home",platform:g==null?void 0:g.platform}),console.log(`✅ Home button pressed (${(g==null?void 0:g.platform)||"android"})`);break;case"back":await Ae.sendDeviceCommand({type:"deviceCommand",command:"back",platform:g==null?void 0:g.platform}),console.log("✅ Back button pressed");break;case"backgroundApps":if((g==null?void 0:g.platform)==="ios"){if(!b){console.error("Cannot open recent apps: Device screen size not available"),ze("Cannot open recent apps: Device screen size not available","error");break}try{const ye=b.width/2,Se=b.height/100*90;await Ae.sendDeviceCommand({type:"deviceCommand",command:"backgroundApps",platform:"ios",params:{actions:{actions:[{type:"pointer",id:"finger1",parameters:{pointerType:"touch"},actions:[{type:"pointerMove",duration:0,origin:"viewport",x:ye,y:b.height},{type:"pointerMove",duration:100,origin:"viewport",x:ye,y:Se}]}]}}}),console.log("✅ Recent apps gesture performed (iOS)"),ze("App switcher gesture performed (swipe up from bottom)","info")}catch(ye){const Se=ye instanceof Error?ye.message:"Failed to perform gesture";console.error("❌ Failed to open recent apps on iOS:",ye),ze(`Failed to open recent apps: ${Se}`,"error"),Ie.warn("Opening recent apps on iOS may not work reliably. iOS does not provide a direct API for this feature.",{position:"top-right",autoClose:4e3})}}else await Ae.sendDeviceCommand({type:"deviceCommand",command:"backgroundApps",platform:"android"}),console.log("✅ Recent apps opened (Android)");break;case"volumeUp":await Ae.sendDeviceCommand({type:"deviceCommand",command:"volumeUp",platform:g==null?void 0:g.platform}),console.log("✅ Volume up");break;case"volumeDown":await Ae.sendDeviceCommand({type:"deviceCommand",command:"volumeDown",platform:g==null?void 0:g.platform}),console.log("✅ Volume down");break;case"takeScreenshot":await Ay();break;case"rotate":if((g==null?void 0:g.platform)==="android"||(g==null?void 0:g.platform)==="ios"){const ye=D==="PORTRAIT"?"LANDSCAPE":"PORTRAIT";try{await Ae.sendDeviceCommand({type:"deviceCommand",command:"rotate",platform:g==null?void 0:g.platform,params:{orientation:ye}}),await new Promise(Se=>setTimeout(Se,500));try{const Se=await be.makeGETRequest(`/wd/hub/session/${p}/orientation`,{}),Le=(Se.value||Se.orientation||ye).toUpperCase();if(W(Le),N(Le==="LANDSCAPE"),pt.current){const Ke=pt.current,Ge=Le==="PORTRAIT"?Ke:{width:Ke.height,height:Ke.width};y(Ge),ft.current=Ge,Ne.current&&(Ne.current.updateDeviceScreenInfo(Ge),console.log("🔄 Updated interaction handler with rotated screen size:",Ge))}else if(b){const Ke=Le==="PORTRAIT"?b.width<b.height?b:{width:b.height,height:b.width}:b.width>b.height?b:{width:b.height,height:b.width};y(Ke),ft.current=Ke,Ne.current&&(Ne.current.updateDeviceScreenInfo(Ke),console.log("🔄 Updated interaction handler with rotated screen size:",Ke))}console.log(`✅ Device rotated to ${Le} (verified)`),ze(`Device rotated to ${Le}`,"success")}catch(Se){if(console.warn("⚠️ Could not verify orientation, updating UI optimistically:",Se),W(ye),N(ye==="LANDSCAPE"),pt.current){const _e=pt.current,Le=ye==="PORTRAIT"?_e:{width:_e.height,height:_e.width};y(Le),ft.current=Le,Ne.current&&Ne.current.updateDeviceScreenInfo(Le)}console.log(`✅ Device rotated to ${ye} (optimistic)`),ze(`Device rotated to ${ye}`,"success")}}catch(Se){console.error("❌ Failed to rotate device:",Se);const _e=Se instanceof Error?Se.message:"Failed to rotate device";ze(`Rotation failed: ${_e}`,"error"),Ie.error(`Rotation failed: ${_e}`,{position:"top-right",autoClose:5e3})}}else console.warn("⚠️ Rotation is only supported for Android and iOS devices"),ze("Rotation is only supported for Android and iOS devices","warning");break;default:console.warn("Unknown action:",ce)}}catch(ye){console.error(`❌ Failed to perform action ${ce}:`,ye);const Se=ye instanceof Error?ye.message:`Failed to perform ${ce}`;ze(`Control error: ${Se}`,"error"),ye instanceof Error&&console.error("Error details:",{message:ye.message,stack:ye.stack,dataChannelOpen:Ae.isDataChannelOpen(),connectionState:Ae.getConnectionState(),iceState:Ae.getICEConnectionState()})}finally{V(ye=>{const Se=new Set(ye);return Se.delete(ce),Se}),console.log("✅ Command completed:",ce)}},[p,g==null?void 0:g.platform,b,D,ze,Ay,Ae,be,Ne,pt,ft,W,N,y,Ie]),M5=m.useCallback(()=>{const ce=document.createElement("input");ce.type="file",ce.accept=".apk,.aab,.ipa,.app,.zip",ce.onchange=async pe=>{var Se,_e,Le;const ye=(Se=pe.target.files)==null?void 0:Se[0];if(!(!ye||!p))try{const Ke=new FormData;Ke.append("file",ye);const Ge=localStorage.getItem("token");if(!Ge)throw new Error("Authentication required. Please log in again.");const Dr=await fetch("/api/uploaded-apps/upload",{method:"POST",headers:{Authorization:`Bearer ${Ge}`},body:Ke,credentials:"include"});if(!Dr.ok){const So=await Dr.json().catch(()=>({message:"Failed to upload file"}));throw new Error(So.message||"Failed to upload file")}const bt=await Dr.json();console.log("✅ File uploaded:",bt);const br=((_e=bt.file)==null?void 0:_e.uploadedFileName)||((Le=bt.file)==null?void 0:Le.filename);if(!br)throw new Error("No file path returned from upload");Ae.isDataChannelOpen()?await Ae.sendDeviceCommand({type:"deviceCommand",command:"installApp",platform:g==null?void 0:g.platform,params:{appPath:br}}):await be.makePOSTRequest(`/wd/hub/session/${p}/appium/device/install_app`,{},{appPath:br}),console.log("✅ App installed successfully"),ze(`App ${ye.name} installed successfully`,"success")}catch(Ke){console.error("❌ Failed to upload/install app:",Ke),ze(`Failed to install app: ${Ke instanceof Error?Ke.message:"Unknown error"}`,"error")}},ce.click()},[p,g==null?void 0:g.platform]),U$=m.useMemo(()=>[{action:"home",icon:s.jsx(oM,{className:"w-6 h-6"}),name:"Home",variant:"primary"},{action:"back",icon:s.jsx(av,{className:"w-6 h-6"}),name:"Back",variant:"primary"},{action:"backgroundApps",icon:s.jsx(dM,{className:"w-6 h-6"}),name:"Recent",variant:"primary"},{action:"volumeUp",icon:s.jsx(zY,{className:"w-6 h-6"}),name:"Vol +",variant:"primary"},{action:"volumeDown",icon:s.jsx(FY,{className:"w-6 h-6"}),name:"Vol -",variant:"primary"},{action:"takeScreenshot",icon:s.jsx(uY,{className:"w-6 h-6"}),name:"Capture",variant:"primary"},{action:"uploadFile",icon:s.jsx(fM,{className:"w-6 h-6"}),name:"Upload",onClick:M5},{action:"rotate",icon:s.jsx(AY,{className:"w-6 h-6"}),name:"Rotate"},{action:"keyboard",icon:s.jsx(l0,{className:"w-6 h-6"}),name:R?"Keyboard On":"Keyboard Off",onClick:()=>P(ce=>!ce)}],[g==null?void 0:g.platform,R,M5]);return i?s.jsx("div",{className:"flex items-center justify-center min-h-screen bg-gray-900 text-white",children:s.jsx(lke,{open:!0,error:i,platform:g==null?void 0:g.platform,traceId:l,originalMessage:u,onClose:Ty,onRetry:D5,onBack:Ty})}):s.jsxs("div",{className:"flex flex-col h-screen bg-gray-900 text-white relative overflow-hidden",children:[M&&s.jsx("div",{className:"fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center",children:s.jsx("div",{className:"bg-gray-800 rounded-lg p-8 border border-gray-700 shadow-2xl max-w-lg w-full mx-4",children:s.jsxs("div",{className:"flex flex-col items-center justify-center",children:[s.jsx(cke,{className:"mb-6"}),s.jsx("h3",{className:"text-2xl font-semibold text-white mb-2",children:"Closing Session"}),s.jsx("p",{className:"text-gray-400 text-center",children:"Cleaning up device and closing connection..."})]})})}),Q&&s.jsx("div",{className:"fixed inset-0 bg-black/80 backdrop-blur-md z-50 flex items-center justify-center",children:s.jsx("div",{className:`bg-gray-900 rounded-xl p-8 border-2 ${he<=0?"border-red-500 shadow-[0_0_50px_rgba(239,68,68,0.3)]":"border-yellow-500 shadow-[0_0_50px_rgba(234,179,8,0.3)]"} max-w-md w-full mx-4`,children:s.jsxs("div",{className:"flex flex-col items-center justify-center",children:[s.jsxs("div",{className:"relative mb-6",children:[s.jsx("div",{className:`w-20 h-20 rounded-full ${he<=0?"bg-red-500/20":"bg-yellow-500/20"} flex items-center justify-center animate-pulse`,children:s.jsx(ku,{className:`w-10 h-10 ${he<=0?"text-red-500":"text-yellow-500"}`})}),s.jsx("div",{className:`absolute inset-0 rounded-full border-2 ${he<=0?"border-red-500":"border-yellow-500"} animate-ping opacity-50`})]}),s.jsx("h3",{className:"text-2xl font-bold text-white mb-3",children:he<=0?"Session Terminating...":"Session Idle Warning"}),s.jsx("div",{className:`bg-gray-800 rounded-lg px-8 py-4 mb-4 border ${he<=0?"border-red-700":"border-gray-700"}`,children:he<=0?s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"flex items-center justify-center gap-2",children:[s.jsx("div",{className:"w-2 h-2 bg-red-500 rounded-full animate-bounce"}),s.jsx("div",{className:"w-2 h-2 bg-red-500 rounded-full animate-bounce",style:{animationDelay:"0.1s"}}),s.jsx("div",{className:"w-2 h-2 bg-red-500 rounded-full animate-bounce",style:{animationDelay:"0.2s"}})]}),s.jsx("div",{className:"text-sm text-gray-400 mt-2",children:"Closing session..."})]}):s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"text-5xl font-bold text-yellow-500 tabular-nums",children:Math.ceil(he/1e3)}),s.jsx("div",{className:"text-sm text-gray-400 mt-1",children:"seconds remaining"})]})}),s.jsx("p",{className:"text-gray-300 text-center mb-6",children:he<=0?"Please wait while we close your session...":"Your session will be terminated due to inactivity."}),s.jsx("button",{onClick:Lh,disabled:he<=0,className:`w-full font-bold py-4 px-6 rounded-lg transition-all ${he<=0?"bg-gray-600 text-gray-400 cursor-not-allowed":"bg-yellow-500 hover:bg-yellow-400 text-gray-900 transform hover:scale-[1.02] shadow-lg shadow-yellow-500/25"}`,children:he<=0?"Session Ending...":"Continue Session"}),he>0&&s.jsx("p",{className:"text-xs text-gray-500 mt-4 text-center",children:"Click the button or interact with the device to stay connected"})]})})}),s.jsx("div",{className:"bg-gray-800 border-b border-gray-700 px-6 py-3",children:s.jsxs("div",{className:"flex items-center justify-between gap-6 flex-wrap",children:[s.jsxs("div",{className:"flex items-center gap-4 flex-shrink-0",children:[s.jsx("h1",{className:"text-xl font-bold text-yellow-400",children:(g==null?void 0:g.name)||"Device"}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("div",{className:`w-3 h-3 rounded-full ${n==="connected"?"bg-green-500":n==="connecting"?"bg-yellow-500 animate-pulse":"bg-red-500"}`}),s.jsx("span",{className:"text-sm text-gray-400 capitalize",children:n})]}),n==="connected"&&s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("div",{className:`w-2 h-2 rounded-full ${X==="connected"?"bg-green-500":X==="disconnected"?"bg-red-500":"bg-gray-500"}`}),s.jsx("span",{className:"text-xs text-gray-500",children:X==="connected"?"Node Online":X==="disconnected"?"Node Offline":"Node Status Unknown"})]}),!ee&&s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("div",{className:"w-2 h-2 rounded-full bg-red-500"}),s.jsx("span",{className:"text-xs text-red-400",children:"No Internet"})]}),n==="connected"&&j&&s.jsx("div",{className:"flex items-center gap-2",children:$.status==="frozen"?s.jsxs(s.Fragment,{children:[s.jsx(an,{className:"w-4 h-4 text-red-500"}),s.jsx("span",{className:"text-sm text-red-400",children:"Stream Frozen"}),s.jsxs("button",{onClick:()=>J(!0),className:"text-xs text-red-400 hover:text-red-300 underline flex items-center gap-1",title:"View diagnostic information",children:[s.jsx(B4,{className:"w-3 h-3"}),"Diagnostic Info"]})]}):$.status==="stalled"?s.jsxs(s.Fragment,{children:[s.jsx(eu,{className:"w-4 h-4 text-yellow-500"}),s.jsx("span",{className:"text-sm text-yellow-400",children:"Stream Stalled"}),s.jsxs("button",{onClick:()=>J(!0),className:"text-xs text-yellow-400 hover:text-yellow-300 underline flex items-center gap-1",title:"View diagnostic information",children:[s.jsx(B4,{className:"w-3 h-3"}),"Diagnostic Info"]})]}):$.status==="healthy"?s.jsxs(s.Fragment,{children:[s.jsx(wl,{className:"w-4 h-4 text-green-500"}),s.jsx("span",{className:"text-sm text-green-400",children:"Stream Active"})]}):null})]}),g&&s.jsxs("div",{className:"flex items-center gap-6 text-sm flex-1 min-w-0",children:[s.jsxs("span",{className:"text-gray-400 whitespace-nowrap",children:["UDID: ",s.jsx("span",{className:"text-yellow-400 font-medium",children:g.udid})]}),s.jsxs("span",{className:"text-gray-400 whitespace-nowrap",children:["Platform: ",s.jsx("span",{className:"text-yellow-400 font-medium",children:g.platform})]}),s.jsxs("span",{className:"text-gray-400 whitespace-nowrap",children:["Version: ",s.jsx("span",{className:"text-yellow-400 font-medium",children:g.version})]}),b&&s.jsxs("span",{className:"text-gray-400 whitespace-nowrap",children:["Screen:"," ",s.jsxs("span",{className:"text-yellow-400 font-medium",children:[b.width,"x",b.height]})]}),p&&s.jsxs("span",{className:"text-gray-400 whitespace-nowrap",children:["Session:"," ",s.jsxs("span",{className:"text-yellow-400 font-medium",children:[p.substring(0,8),"..."]})]})]}),s.jsx("button",{onClick:Bh,disabled:M,className:"px-4 py-2 bg-red-600 hover:bg-red-700 rounded transition flex-shrink-0 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2",children:M?s.jsxs(s.Fragment,{children:[s.jsxs("svg",{className:"animate-spin h-4 w-4 text-white",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",children:[s.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),s.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),s.jsx("span",{children:"Closing..."})]}):"Close Session"})]})}),s.jsxs("div",{ref:dt,className:"flex-1 flex flex-row p-6 bg-gray-900 relative overflow-hidden min-h-0",children:[n==="connected"&&j&&s.jsxs("div",{className:"flex flex-col gap-4 flex-shrink-0 overflow-y-auto overflow-x-visible max-h-full pr-1 items-start",children:[s.jsx("div",{className:"bg-gradient-to-b from-gray-900/95 to-gray-800/95 backdrop-blur-xl rounded-2xl p-4 border border-gray-700/50 shadow-2xl flex-shrink-0",children:s.jsx(O5,{controls:U$,onControlClick:B$,disabled:!p,loadingActions:I})}),R&&s.jsx("div",{className:"bg-gradient-to-b from-gray-900/95 to-gray-800/95 backdrop-blur-xl rounded-2xl p-4 border border-gray-700/50 shadow-2xl flex-shrink-0",children:s.jsxs("div",{className:"flex flex-col gap-3 w-[300px]",children:[s.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[s.jsx(l0,{className:"w-5 h-5 text-yellow-400"}),s.jsx("h3",{className:"text-sm font-semibold text-white",children:"Text Input"})]}),s.jsx("textarea",{value:ie,onChange:ce=>q(ce.target.value),onKeyDown:ce=>{(ce.ctrlKey||ce.metaKey)&&ce.key==="Enter"&&(ce.preventDefault(),xe())},placeholder:`Enter or paste text here...
3084
3084
  Press Ctrl/Cmd+Enter to send`,className:"w-full min-h-[120px] max-h-[200px] px-3 py-2 bg-gray-900 border border-gray-600 rounded-lg text-white placeholder-gray-500 resize-y focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:border-transparent text-sm overflow-y-auto",disabled:B||!p}),s.jsxs("div",{className:"flex items-center justify-between gap-2",children:[s.jsxs("span",{className:"text-xs text-gray-400",children:[ie.length," characters"]}),s.jsx("button",{onClick:xe,disabled:!ie.trim()||B||!p,className:"px-4 py-2 bg-yellow-500 hover:bg-yellow-600 text-gray-900 font-medium rounded-lg transition disabled:opacity-50 disabled:cursor-not-allowed text-sm flex items-center gap-2 whitespace-nowrap",children:B?s.jsxs(s.Fragment,{children:[s.jsxs("svg",{className:"animate-spin h-4 w-4",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",children:[s.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),s.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),"Sending..."]}):"Send Text"})]})]})})]}),s.jsxs("div",{className:"flex items-center justify-center relative ml-6",style:{minWidth:D==="LANDSCAPE"?"400px":"200px",flex:`1 1 ${100-re}%`},children:[s.jsx("div",{className:"relative",style:{visibility:n==="connected"&&j||K?"visible":"hidden",position:n==="connected"&&j||K?"static":"absolute",width:n==="connected"&&j||K?"auto":"1px",height:n==="connected"&&j||K?"auto":"1px",left:n==="connected"&&j||K?"auto":"-9999px",top:n==="connected"&&j||K?"auto":"-9999px",opacity:n==="connected"&&j||K?1:0,pointerEvents:n==="connected"&&j?"auto":"none",zIndex:n==="connected"&&j||K?10:-1},children:s.jsx("div",{className:"relative bg-gray-800 shadow-2xl border border-gray-700",style:{display:"inline-block",padding:"8px",borderRadius:D==="LANDSCAPE"?"20px":"40px",maxWidth:D==="LANDSCAPE"?"calc(50vw - 100px)":"100%",maxHeight:"calc(100vh - 200px)",transition:"all 0.3s ease-in-out",...cr&&{transform:"rotate(-90deg)"}},children:s.jsxs("div",{className:"relative bg-black overflow-hidden",style:{borderRadius:D==="LANDSCAPE"?"12px":"32px",width:"auto",height:"auto",display:"inline-block"},children:[s.jsx("video",{ref:Te,autoPlay:!0,playsInline:!0,muted:!0,style:{objectFit:"contain",backgroundColor:"#000000",display:"block",width:"auto",height:"auto",maxHeight:cr?"calc(50vw - 120px)":"calc(100vh - 220px)",maxWidth:cr?"calc(100vh - 220px)":D==="LANDSCAPE"?"calc(50vw - 120px)":"calc(50vw - 200px)",minWidth:!cr&&D==="LANDSCAPE"?"400px":"auto",transition:"all 0.3s ease-in-out"},preload:"none"}),s.jsx("canvas",{ref:me,className:"absolute top-0 left-0 w-full h-full cursor-pointer",style:{pointerEvents:K?"none":"auto",outline:"none"},tabIndex:0}),$.status==="frozen"&&n==="connected"&&!K&&s.jsxs("div",{className:"absolute inset-0 flex flex-col items-center justify-center bg-black/50 backdrop-blur-[2px] z-20",children:[s.jsx("div",{className:"animate-spin rounded-full h-8 w-8 border-2 border-yellow-400/40 border-t-yellow-400 mb-3"}),s.jsx("p",{className:"text-yellow-300 text-sm font-medium",children:"Stream interrupted"}),s.jsx("p",{className:"text-gray-400 text-xs mt-1",children:"Recovering..."})]}),K&&s.jsxs("div",{className:"absolute inset-0 flex flex-col items-center justify-center bg-black/60 backdrop-blur-sm z-20",children:[s.jsx("div",{className:"animate-spin rounded-full h-10 w-10 border-2 border-gray-400 border-t-white mb-3"}),s.jsx("p",{className:"text-white text-sm font-medium",children:"Reconnecting..."}),s.jsx("p",{className:"text-gray-400 text-xs mt-1",children:"Stream will resume shortly"})]})]})})}),(n==="connecting"&&!K||n==="connected"&&!j)&&s.jsx("div",{className:"flex items-center justify-center",style:{minHeight:"600px",position:"relative",zIndex:1},children:s.jsx(yke,{platform:(g==null?void 0:g.platform)||"android"})}),(n==="disconnected"||n==="failed")&&s.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-gray-900/80 backdrop-blur-sm",children:s.jsx("div",{className:"bg-gray-800/95 backdrop-blur-xl rounded-2xl p-8 border border-red-500/50 shadow-2xl max-w-lg w-full mx-4",children:s.jsxs("div",{className:"flex flex-col items-center justify-center text-center",children:[s.jsx("div",{className:"flex h-16 w-16 shrink-0 items-center justify-center rounded-full bg-red-500/10 ring-1 ring-red-500/20 mb-6",children:s.jsx("svg",{className:"h-8 w-8 text-red-400",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",children:s.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"})})}),s.jsx("h3",{className:"text-2xl font-semibold text-white mb-3",children:n==="failed"?"Connection Failed":"Connection Lost"}),s.jsx("p",{className:"text-gray-300 text-sm leading-relaxed mb-6",children:f||(n==="failed"?"The WebRTC connection could not be established. This may be due to network restrictions or firewall issues.":"The WebRTC connection to the device was interrupted. This may be due to network issues or the device going to sleep.")}),s.jsxs("div",{className:"flex flex-col gap-3 w-full",children:[s.jsx("button",{onClick:D5,className:"inline-flex w-full justify-center rounded-lg px-4 py-2.5 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 transition-colors shadow-lg shadow-blue-900/20",children:"Retry Connection"}),s.jsx("button",{onClick:Ty,className:"inline-flex w-full justify-center rounded-lg px-4 py-2.5 text-sm font-medium text-gray-300 bg-gray-700/50 hover:bg-gray-700 hover:text-white transition-colors ring-1 ring-gray-600/50",children:"Back to Devices"})]}),s.jsxs("div",{className:"mt-6 pt-6 border-t border-gray-700/50",children:[s.jsx("p",{className:"text-xs text-gray-400 mb-2",children:"💡 Troubleshooting tips:"}),s.jsxs("ul",{className:"text-xs text-gray-500 text-left space-y-1",children:[s.jsx("li",{children:"• Check your network connection"}),s.jsx("li",{children:"• Ensure the device is online and unlocked"}),s.jsx("li",{children:"• Try refreshing the page"}),n==="failed"&&s.jsx("li",{children:"• If using hosted servers, verify TURN server configuration"})]})]})]})})})]}),s.jsxs("div",{className:je("w-3 flex-shrink-0 cursor-col-resize group relative mx-1",we&&"bg-yellow-500/20"),onMouseDown:Fh,onDoubleClick:zh,title:"Drag to resize. Double-click to reset.",children:[s.jsx("div",{className:je("absolute inset-y-0 left-1/2 -translate-x-1/2 rounded-full transition-all",we?"bg-yellow-500 w-1":"bg-gray-600 w-0.5 group-hover:bg-yellow-500 group-hover:w-1")}),s.jsxs("div",{className:"absolute inset-0 flex flex-col items-center justify-center gap-1.5",children:[s.jsx("div",{className:je("w-1.5 h-1.5 rounded-full transition-colors",we?"bg-yellow-400":"bg-gray-500 group-hover:bg-yellow-400")}),s.jsx("div",{className:je("w-1.5 h-1.5 rounded-full transition-colors",we?"bg-yellow-400":"bg-gray-500 group-hover:bg-yellow-400")}),s.jsx("div",{className:je("w-1.5 h-1.5 rounded-full transition-colors",we?"bg-yellow-400":"bg-gray-500 group-hover:bg-yellow-400")})]})]}),s.jsxs("div",{className:"flex flex-col gap-4 overflow-hidden",style:{flex:`0 0 ${re}%`,minWidth:"250px"},children:[(n==="connecting"||n==="connected"&&!j)&&s.jsx(gke,{logs:w}),Z&&$.diagnosticInfo&&s.jsx("div",{className:"fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4",children:s.jsxs("div",{className:"bg-gray-800 rounded-lg border border-gray-700 shadow-2xl max-w-2xl w-full max-h-[80vh] flex flex-col",children:[s.jsxs("div",{className:"flex items-center justify-between p-4 border-b border-gray-700",children:[s.jsxs("h3",{className:"text-lg font-semibold text-white flex items-center gap-2",children:[s.jsx(eu,{className:"w-5 h-5 text-yellow-500"}),"Stream Diagnostic Information"]}),s.jsx("button",{onClick:()=>{J(!1),te(!1)},className:"text-gray-400 hover:text-white transition-colors",children:s.jsx(an,{className:"w-5 h-5"})})]}),s.jsxs("div",{className:"p-4 overflow-y-auto flex-1",children:[s.jsx("div",{className:"bg-gray-900 rounded-lg p-4 border border-gray-700 font-mono text-sm text-gray-300 whitespace-pre-wrap",children:$.diagnosticInfo}),s.jsxs("div",{className:"mt-4 p-4 bg-blue-900/20 border border-blue-700/50 rounded-lg",children:[s.jsx("h4",{className:"text-sm font-semibold text-blue-400 mb-2",children:"How to Report This Issue:"}),s.jsxs("ol",{className:"text-sm text-gray-300 space-y-2 list-decimal list-inside",children:[s.jsx("li",{children:'Click the "Copy Diagnostic Info" button below'}),s.jsx("li",{children:"Paste the diagnostic information when reporting the issue"}),s.jsx("li",{children:"Include the timestamp and session ID from the diagnostic info"}),s.jsx("li",{children:'Note what you were doing when the stream froze (e.g., "scrolling", "tapping", "idle")'})]})]})]}),s.jsxs("div",{className:"flex items-center justify-end gap-2 p-4 border-t border-gray-700",children:[s.jsx("button",{onClick:async()=>{try{await navigator.clipboard.writeText($.diagnosticInfo||""),te(!0),setTimeout(()=>te(!1),2e3),Ie.success("Diagnostic info copied to clipboard")}catch{Ie.error("Failed to copy diagnostic info")}},className:"px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm font-medium flex items-center gap-2 transition-colors",children:G?s.jsxs(s.Fragment,{children:[s.jsx(wl,{className:"w-4 h-4"}),"Copied!"]}):s.jsxs(s.Fragment,{children:[s.jsx(Ra,{className:"w-4 h-4"}),"Copy Diagnostic Info"]})}),s.jsx("button",{onClick:()=>{J(!1),te(!1)},className:"px-4 py-2 bg-gray-700 hover:bg-gray-600 text-white rounded-lg text-sm font-medium transition-colors",children:"Close"})]})]})}),n==="connected"&&j&&s.jsxs("div",{className:"flex flex-col h-full bg-gray-900 rounded-lg border border-gray-700 overflow-hidden",children:[s.jsxs("div",{className:"flex-shrink-0 flex gap-1 border-b border-gray-700 px-4",children:[s.jsx("button",{onClick:()=>T("logcat"),className:je("px-4 py-3 text-sm font-medium transition-all border-b-2 relative",C==="logcat"?"text-orange-400 border-orange-400":"text-gray-400 border-transparent hover:text-gray-300 hover:border-gray-600"),children:"DeviceLogs"}),s.jsxs("button",{onClick:()=>T("screenshots"),className:je("px-4 py-3 text-sm font-medium transition-all border-b-2 relative",C==="screenshots"?"text-orange-400 border-orange-400":"text-gray-400 border-transparent hover:text-gray-300 hover:border-gray-600"),children:["Screenshots",E.length>0&&s.jsx("span",{className:"ml-2 px-2 py-0.5 text-xs bg-orange-500/20 text-orange-400 rounded-full",children:E.length})]}),s.jsx("button",{onClick:()=>T("files"),className:je("px-4 py-3 text-sm font-medium transition-all border-b-2 relative",C==="files"?"text-orange-400 border-orange-400":"text-gray-400 border-transparent hover:text-gray-300 hover:border-gray-600"),children:"Files"}),s.jsx("button",{onClick:()=>T("apps"),className:je("px-4 py-3 text-sm font-medium transition-all border-b-2 relative",C==="apps"?"text-orange-400 border-orange-400":"text-gray-400 border-transparent hover:text-gray-300 hover:border-gray-600"),children:"Apps"}),s.jsx("button",{onClick:()=>T("device-info"),className:je("px-4 py-3 text-sm font-medium transition-all border-b-2 relative",C==="device-info"?"text-orange-400 border-orange-400":"text-gray-400 border-transparent hover:text-gray-300 hover:border-gray-600"),children:"Device Info"}),s.jsx("button",{onClick:()=>T("video-quality"),className:je("px-4 py-3 text-sm font-medium transition-all border-b-2 relative",C==="video-quality"?"text-orange-400 border-orange-400":"text-gray-400 border-transparent hover:text-gray-300 hover:border-gray-600"),children:"Video Quality"}),s.jsx("button",{onClick:()=>T("inspector"),disabled:!p,className:je("px-4 py-3 text-sm font-medium transition-all border-b-2 relative",C==="inspector"?"text-orange-400 border-orange-400":"text-gray-400 border-transparent hover:text-gray-300 hover:border-gray-600",!p&&"opacity-50 cursor-not-allowed"),children:"Inspector"}),Pe&&s.jsx("button",{onClick:()=>T("ai-vision"),disabled:!p,className:je("px-4 py-3 text-sm font-medium transition-all border-b-2 relative",C==="ai-vision"?"text-orange-400 border-orange-400":"text-gray-400 border-transparent hover:text-gray-300 hover:border-gray-600",!p&&"opacity-50 cursor-not-allowed"),children:"Vision"})]}),s.jsxs("div",{className:"flex-1 overflow-hidden relative",children:[C==="logcat"&&s.jsx("div",{className:"h-full overflow-y-auto p-4",children:s.jsx("p",{className:"text-gray-400",children:"Logcat functionality coming soon..."})}),C==="device-info"&&s.jsx(vke,{device:g,deviceScreenSize:b,sessionId:p,onTakeScreenshot:Ay}),C==="screenshots"&&s.jsx("div",{className:"absolute inset-0 w-full overflow-hidden",children:E.length===0?s.jsx("div",{className:"h-full flex flex-col items-center justify-center text-center p-4",children:s.jsxs("div",{className:"bg-white/5 rounded-2xl p-6 backdrop-blur-sm border border-white/10 shadow-lg max-w-sm",children:[s.jsx("h3",{className:"text-white/90 text-lg font-medium mb-2",children:"No Screenshots Yet"}),s.jsx("p",{className:"text-gray-400 text-sm leading-relaxed mb-4",children:'Click the "Take Screenshot" button in the Overview tab to capture screenshots of your device.'})]})}):s.jsx(I$,{screenshotAssetIds:[],sessionLogEntries:E.map((ce,pe)=>({timestamp:new Date().toISOString(),commandName:"getScreenshot",method:"GET",url:"/screenshot",requestBody:{},responseStatus:200,responseData:{value:ce}})),baseUrl:window.location.origin,className:"h-full p-4",onDeleteScreenshot:$$})}),C==="files"&&p&&s.jsx(jke,{sessionId:p,platform:(g==null?void 0:g.platform)||"android"}),C==="apps"&&s.jsx(mke,{device:g,sessionId:p,platform:(g==null?void 0:g.platform)||"android"}),C==="video-quality"&&s.jsx(hke,{sessionId:p,onSettingsChange:ce=>{_(ce)},initialSettings:fe}),C==="inspector"&&p&&s.jsx("div",{className:"h-full w-full",children:s.jsx("iframe",{src:(()=>{const{hostname:ce,port:pe,protocol:ye}=window.location,Se=ye==="https:",_e={server:{remote:{hostname:ce,port:pe?parseInt(pe,10):Se?443:80,path:"/wd/hub",ssl:Se}},attachSessId:p};return`/inspector?state=${encodeURIComponent(JSON.stringify(_e))}&autoStart=1`})(),className:"w-full h-full border-0",title:"Appium Inspector"})}),C==="ai-vision"&&p&&s.jsx(kke,{sessionId:p,platform:(g==null?void 0:g.platform)||"android",deviceUdid:(g==null?void 0:g.udid)||""})]})]})]})]}),s.jsx(pke,{peerConnection:Ae.getPeerConnection(),isVisible:ae,onToggle:()=>ne(!ae)})]})},Ske=({activeSection:e,setActiveSection:t})=>{const r=[{id:"password",label:"Password & Authentication"},{id:"apiTokens",label:"API Tokens"}];return s.jsxs("nav",{children:[s.jsx("h2",{className:"text-xl font-semibold mb-6 text-gray-100",children:"Profile Settings"}),s.jsx("ul",{children:r.map(n=>s.jsx("li",{className:"mb-3",children:s.jsx("button",{onClick:()=>t(n.id),className:`w-full text-left px-4 py-2 rounded-md transition-colors duration-200 ease-in-out
3085
3085
  ${e===n.id?"bg-blue-600 text-white font-medium shadow-md":"text-gray-300 hover:bg-gray-700 hover:text-white"}`,children:n.label})},n.id))})]})},$A=()=>{const[e,t]=m.useState(""),[r,n]=m.useState(""),[a,i]=m.useState(null),[o,l]=m.useState(""),[c,u]=m.useState("");m.useEffect(()=>{(async()=>{try{const h=await Nr.getCurrentUser();i(h)}catch{l("Failed to fetch user information")}})()},[]);const d=async f=>{if(f.preventDefault(),l(""),u(""),!a){l("User information not available");return}if(e!==r){l("New passwords do not match");return}try{await Nr.updateUser(a.id,{password:e}),t(""),n(""),u("Password updated successfully")}catch{l("Failed to update password")}};return s.jsxs("div",{className:"max-w-lg bg-gray-900 rounded-xl p-8 shadow-lg",children:[s.jsx("div",{className:"pb-4 mb-6 border-b border-gray-700 text-left",children:s.jsx("h1",{className:"text-2xl font-bold text-gray-100",children:"Update Password"})}),o&&s.jsx("div",{className:"mb-4 p-3 bg-red-500/10 border border-red-500 rounded-lg text-red-500",children:o}),c&&s.jsx("div",{className:"mb-4 p-3 bg-green-500/10 border border-green-500 rounded-lg text-green-500",children:c}),s.jsxs("form",{onSubmit:d,className:"space-y-6",children:[s.jsxs("div",{className:"space-y-2",children:[s.jsx("label",{htmlFor:"newPassword",className:"block text-sm font-medium text-gray-300 text-left",children:"New password"}),s.jsx("input",{type:"password",id:"newPassword",value:e,onChange:f=>t(f.target.value),required:!0,className:"w-full px-4 py-2 bg-gray-800 border border-gray-700 rounded-lg text-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 hover:border-gray-600"})]}),s.jsxs("div",{className:"space-y-2",children:[s.jsx("label",{htmlFor:"confirmPassword",className:"block text-sm font-medium text-gray-300 text-left",children:"Confirm new password"}),s.jsx("input",{type:"password",id:"confirmPassword",value:r,onChange:f=>n(f.target.value),required:!0,className:"w-full px-4 py-2 bg-gray-800 border border-gray-700 rounded-lg text-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 hover:border-gray-600"})]}),s.jsx("div",{className:"pt-6 flex justify-start",children:s.jsx("button",{type:"submit",className:"px-6 py-2 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 transition-all duration-200 transform hover:scale-[1.02] active:scale-[0.98] shadow-lg hover:shadow-blue-500/20",children:"Update Password"})})]})]})};class Cke{async createApiToken(t){try{const r=await be.makePOSTRequest("/api-tokens",{},t);return r.value||r}catch(r){const a=(r.data||{}).message||r.message||"Error creating API token";throw new Error(a)}}async regenerateApiToken(t,r,n){try{const a={};r&&(a.name=r),n&&(a.expiresAt=n);const i=await be.makePOSTRequest(`/api-tokens/${t}/regenerate`,{},a);return i.value||i}catch(a){const o=(a.data||{}).message||a.message||"Error regenerating API token";throw new Error(o)}}async deleteApiToken(t){try{await be.makeDELETERequest(`/api-tokens/${t}`)}catch(r){const a=(r.data||{}).message||r.message||"Error deleting API token";throw new Error(a)}}async listApiTokens(){try{const t=await be.makeGETRequest("/api-tokens",{}),r=t.value||t;return Array.isArray(r)?r:[]}catch(t){const n=(t.data||{}).message||t.message||"Error listing API tokens";throw new Error(n)}}}const Wp=new Cke,BA=e=>e?new Date(e).toLocaleString():"-",Eke=()=>{const[e,t]=m.useState([]),[r,n]=m.useState(""),[a,i]=m.useState(!1),[o,l]=m.useState(""),[c,u]=m.useState(""),[d,f]=m.useState(!1),[h,p]=m.useState(""),[v,g]=m.useState(null),{user:x}=Gr(),[b,y]=m.useState(!1),[w,k]=m.useState({}),[j,S]=m.useState({}),[E,A]=m.useState(!0),[C,T]=m.useState(!1),[R,P]=m.useState(null),[O,N]=m.useState(!1),[D,W]=m.useState("free"),{refreshUser:M}=Gr();m.useEffect(()=>{if(x!=null&&x.accessKey)A(!1);else{const X=setTimeout(()=>{A(!1)},1e3);return()=>clearTimeout(X)}},[x==null?void 0:x.accessKey]);const F=async()=>{f(!0);try{const X=await Wp.listApiTokens();t(X)}catch{p("Failed to load tokens")}f(!1)};m.useEffect(()=>{F()},[]),m.useEffect(()=>{(async()=>{try{const L=await dv.getStatus();P(L.organizationId||null),W(L.tier||"free")}catch(L){console.error("Failed to fetch subscription status:",L)}})()},[]);const I=async()=>{if(p(""),!o.trim()){p("Description is required");return}f(!0);try{const X={name:o};c&&(X.expiresAt=c);const L=await Wp.createApiToken(X);g(L),i(!1),l(""),u(""),F()}catch{p("Failed to create token")}f(!1)},V=async(X,L)=>{if(window.confirm("Are you sure you want to regenerate this token? The old token will no longer work and you will need to update any applications using it.")){f(!0),p("");try{const ee=await Wp.regenerateApiToken(X,L);g(ee),await F()}catch(ee){p(ee.message||"Failed to regenerate token")}f(!1)}},$=async X=>{if(window.confirm("Delete this token?")){f(!0);try{t(L=>L.filter(ee=>ee.id!==X)),await Wp.deleteApiToken(X),await F()}catch{p("Failed to delete token")}f(!1)}},U=X=>{const L=document.createElement("textarea");L.value=X,L.style.top="0",L.style.left="0",L.style.position="fixed",L.style.opacity="0",document.body.appendChild(L),L.focus(),L.select();try{const ee=document.execCommand("copy");console.log(ee?"Fallback: Copying text command was successful":"Fallback: Unable to copy")}catch(ee){console.error("Fallback: Oops, unable to copy",ee)}document.body.removeChild(L)},Z=async()=>{try{x!=null&&x.accessKey&&(await navigator.clipboard.writeText(x.accessKey),y(!0),setTimeout(()=>y(!1),1500))}catch(X){console.error("Failed to copy access key:",X),x!=null&&x.accessKey&&(U(x.accessKey),y(!0),setTimeout(()=>y(!1),1500))}},J=async()=>{try{R&&(await navigator.clipboard.writeText(R),N(!0),setTimeout(()=>N(!1),1500))}catch(X){console.error("Failed to copy organisation ID:",X),R&&(U(R),N(!0),setTimeout(()=>N(!1),1500))}},G=async(X,L)=>{try{await navigator.clipboard.writeText(L),k(ee=>({...ee,[X]:!0})),setTimeout(()=>{k(ee=>({...ee,[X]:!1}))},1500)}catch(ee){console.error("Failed to copy token:",ee),U(L),k(H=>({...H,[X]:!0})),setTimeout(()=>{k(H=>({...H,[X]:!1}))},1500)}},te=X=>{S(L=>({...L,[X]:!L[X]}))},ae=async()=>{if(window.confirm("Are you sure you want to regenerate your access key? The old key will no longer work.")){T(!0),p("");try{await Nr.regenerateAccessKey(),await M(),y(!1)}catch(X){p(X.message||"Failed to regenerate access key")}finally{T(!1)}}},ne=e.filter(X=>X.name.toLowerCase().includes(r.toLowerCase())||X.id.toLowerCase().includes(r.toLowerCase()));return s.jsxs("div",{className:"w-full",children:[s.jsxs("div",{className:"flex items-center gap-4 mb-6 w-full max-w-2xl",children:[s.jsx("label",{className:"block text-sm font-medium text-gray-100 whitespace-nowrap",children:"Access Key"}),s.jsx("div",{className:"relative flex-1 max-w-xs",children:E?s.jsx("div",{className:"w-full max-w-xs bg-gray-100/5 border border-gray-700 rounded-lg px-4 py-2 text-gray-100 text-sm font-mono pr-10",children:"Loading..."}):x!=null&&x.accessKey?s.jsxs(s.Fragment,{children:[s.jsx("input",{type:"text",value:x.accessKey,readOnly:!0,className:"w-full max-w-xs bg-gray-100/5 border border-gray-700 rounded-lg px-4 py-2 text-gray-100 text-sm font-mono pr-20 focus:outline-none focus:ring-2 focus:ring-blue-600/40 transition",style:{letterSpacing:"0.01em"}}),s.jsx("button",{type:"button",onClick:Z,className:"absolute right-10 top-1/2 -translate-y-1/2 p-1 rounded hover:bg-gray-700 transition text-gray-400 hover:text-blue-400 focus:outline-none",title:"Copy access key",children:s.jsx(Ra,{size:16})}),s.jsx("button",{type:"button",onClick:ae,disabled:C,className:"absolute right-1 top-1/2 -translate-y-1/2 p-1 rounded hover:bg-gray-700 transition text-gray-400 hover:text-orange-400 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed",title:"Regenerate access key",children:s.jsx(za,{size:16,className:C?"animate-spin":""})}),b&&s.jsx("span",{className:"absolute right-28 top-1/2 -translate-y-1/2 text-xs text-green-400 bg-gray-900 px-2 py-1 rounded shadow",children:"Copied!"})]}):s.jsx("div",{className:"w-full max-w-xs bg-gray-100/5 border border-gray-700 rounded-lg px-4 py-2 text-gray-100 text-sm font-mono pr-10",children:"No access key available"})})]}),s.jsxs("div",{className:"mb-8 p-4 bg-gray-800/50 border border-gray-700 rounded-lg max-w-4xl",children:[s.jsxs("div",{className:"flex items-center gap-3 mb-4",children:[D==="pro"?s.jsx(mf,{className:"text-yellow-400",size:24}):s.jsx($Y,{className:"text-blue-400",size:24}),s.jsxs("h3",{className:"text-lg font-semibold",children:["Subscription: ",D==="pro"?"Pro":"Free"," Tier"]}),D==="pro"&&s.jsx("span",{className:"px-2 py-0.5 text-xs font-medium bg-yellow-500/20 text-yellow-400 rounded-full",children:"Active"})]}),R&&s.jsxs("div",{className:"flex items-center gap-3 mb-4 text-sm",children:[s.jsx("span",{className:"text-gray-400",children:"Organisation ID:"}),s.jsx("code",{className:"bg-gray-900 px-2 py-1 rounded text-gray-300 font-mono text-xs",children:R}),s.jsx("button",{type:"button",onClick:J,className:"p-1 rounded hover:bg-gray-700 transition text-gray-400 hover:text-blue-400 focus:outline-none",title:"Copy organisation ID",children:s.jsx(Ra,{size:14})}),O&&s.jsx("span",{className:"text-xs text-green-400",children:"Copied!"})]}),s.jsx("div",{className:"overflow-x-auto",children:s.jsxs("table",{className:"w-full text-sm",children:[s.jsx("thead",{children:s.jsxs("tr",{className:"border-b border-gray-700",children:[s.jsx("th",{className:"text-left py-2 px-3 text-gray-400 font-medium",children:"Feature"}),s.jsx("th",{className:"text-center py-2 px-3 text-gray-400 font-medium",children:s.jsx("span",{className:D==="free"?"text-blue-400":"",children:"Free"})}),s.jsx("th",{className:"text-center py-2 px-3 text-gray-400 font-medium",children:s.jsx("span",{className:D==="pro"?"text-yellow-400":"",children:"Pro"})})]})}),s.jsxs("tbody",{className:"text-gray-300",children:[s.jsxs("tr",{className:"border-b border-gray-700/50",children:[s.jsx("td",{className:"py-2 px-3",children:"Concurrent Manual Sessions"}),s.jsx("td",{className:"text-center py-2 px-3",children:"2"}),s.jsx("td",{className:"text-center py-2 px-3 text-green-400",children:"Unlimited"})]}),s.jsxs("tr",{className:"border-b border-gray-700/50",children:[s.jsx("td",{className:"py-2 px-3",children:"Monthly Session Minutes"}),s.jsx("td",{className:"text-center py-2 px-3",children:"60"}),s.jsx("td",{className:"text-center py-2 px-3 text-green-400",children:"Unlimited"})]}),s.jsxs("tr",{className:"border-b border-gray-700/50",children:[s.jsx("td",{className:"py-2 px-3",children:"Monthly Builds"}),s.jsx("td",{className:"text-center py-2 px-3",children:"5"}),s.jsx("td",{className:"text-center py-2 px-3 text-green-400",children:"Unlimited"})]}),s.jsxs("tr",{children:[s.jsx("td",{className:"py-2 px-3",children:"Parallel Automation Sessions"}),s.jsx("td",{className:"text-center py-2 px-3",children:"3"}),s.jsx("td",{className:"text-center py-2 px-3 text-green-400",children:"Unlimited"})]})]})]})}),D==="free"&&s.jsx("div",{className:"mt-4 pt-4 border-t border-gray-700",children:s.jsx("p",{className:"text-sm text-gray-400",children:"Want unlimited access? Contact us to upgrade to Pro tier."})})]}),s.jsxs("div",{className:"flex items-center justify-between mb-5",children:[s.jsx("h3",{className:"text-lg font-semibold",children:"Identity Tokens"}),s.jsxs("div",{className:"flex items-center justify-center gap-2",children:[s.jsx("button",{className:"bg-blue-600 hover:bg-blue-700 text-white font-semibold py-1 px-4 rounded shadow transition",onClick:()=>{i(!0),g(null),p("")},children:"Generate New Token"}),s.jsx("input",{type:"text",placeholder:"Search",className:"bg-gray-800 border border-gray-700 rounded px-3 py-1 text-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-600",value:r,onChange:X=>n(X.target.value)})]})]}),s.jsx("div",{className:"overflow-x-auto bg-gray-800 border border-gray-700",children:s.jsxs("table",{className:"min-w-full text-sm text-gray-200 table-fixed border border-gray-700 border-separate border-spacing-0",children:[s.jsx("thead",{children:s.jsxs("tr",{className:"bg-gray-700/80",children:[s.jsx("th",{className:"py-3 px-4 text-left w-1/5 box-border font-semibold text-base tracking-wide border-b border-gray-700",children:"Name"}),s.jsx("th",{className:"py-3 px-4 text-left w-2/5 box-border font-semibold text-base tracking-wide border-b border-gray-700",children:"Token"}),s.jsx("th",{className:"py-3 px-4 text-left w-1/5 box-border font-semibold text-base tracking-wide border-b border-gray-700",children:"Issued At"}),s.jsx("th",{className:"py-3 px-4 text-left w-1/5 box-border font-semibold text-base tracking-wide border-b border-gray-700",children:"Expiry Date"}),s.jsx("th",{className:"py-3 px-4 box-border border-b border-gray-700",children:"Actions"})]})}),s.jsxs("tbody",{children:[ne.length===0&&s.jsx("tr",{children:s.jsx("td",{colSpan:5,className:"py-12 px-4 text-center text-gray-400 border-b border-gray-700",children:s.jsx("div",{className:"flex flex-col items-center justify-center gap-3",children:e.length===0?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"text-lg font-semibold text-gray-300",children:"No tokens found"}),s.jsx("div",{className:"text-gray-400 mb-2",children:"You haven't created any identity tokens yet."}),s.jsx("button",{className:"mt-2 px-5 py-2 rounded bg-blue-600 text-white font-semibold hover:bg-blue-700 transition",onClick:()=>i(!0),children:"Generate your first Identity Token"})]}):s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"text-lg font-semibold text-gray-300",children:"No tokens match your search."}),s.jsx("div",{className:"text-gray-400 mb-2",children:"Try a different keyword or clear your search filter."}),s.jsx("button",{className:"mt-2 px-5 py-2 rounded bg-gray-700 text-white font-semibold hover:bg-gray-600 transition",onClick:()=>n(""),children:"Clear search"})]})})})}),ne.map(X=>s.jsxs("tr",{className:"hover:bg-gray-700/60 transition",children:[s.jsx("td",{className:"py-3 px-4 w-1/5 box-border text-left align-middle border-b border-gray-700",children:X.name}),s.jsx("td",{className:"py-3 px-4 w-2/5 font-mono box-border text-left align-middle border-b border-gray-700",children:s.jsxs("div",{className:"relative flex items-center",children:[s.jsx("input",{type:j[X.id]?"text":"password",value:X.token||"",readOnly:!0,className:"w-full bg-transparent border-none text-gray-200 font-mono pr-16 focus:outline-none text-sm",style:{letterSpacing:"0.01em"}}),s.jsxs("div",{className:"absolute right-1 top-1/2 -translate-y-1/2 flex items-center gap-1",children:[s.jsx("button",{type:"button",onClick:()=>G(X.id,X.token||""),className:"p-1 rounded hover:bg-gray-700 transition text-gray-400 hover:text-blue-400 focus:outline-none",title:"Copy token",children:s.jsx(Ra,{size:14})}),w[X.id]&&s.jsx("span",{className:"absolute right-8 top-1/2 -translate-y-1/2 text-xs text-green-400 bg-gray-900 px-2 py-1 rounded shadow",children:"Copied!"}),s.jsx("button",{type:"button",onClick:()=>te(X.id),className:"p-1 rounded hover:bg-gray-700 transition text-gray-400 hover:text-blue-400 focus:outline-none",title:j[X.id]?"Hide token":"Show token",children:j[X.id]?s.jsx(nM,{size:14}):s.jsx(Qf,{size:14})})]})]})}),s.jsx("td",{className:"py-3 px-4 w-1/5 box-border text-left align-middle border-b border-gray-700",children:BA(X.createdAt)}),s.jsx("td",{className:"py-3 px-4 w-1/5 box-border text-left align-middle border-b border-gray-700",children:BA(X.expiresAt)}),s.jsx("td",{className:"py-3 px-4 box-border text-left align-middle border-b border-gray-700",children:s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("button",{className:"text-orange-500 hover:text-orange-700 transition p-1 rounded hover:bg-gray-700",onClick:()=>V(X.id,X.name),title:"Regenerate token",disabled:d,children:s.jsx(za,{size:14,className:d?"animate-spin":""})}),s.jsx("button",{className:"text-red-500 hover:text-red-700 transition p-1 rounded hover:bg-gray-700",onClick:()=>$(X.id),title:"Delete",disabled:d,children:s.jsx(nr,{size:14})})]})})]},X.id))]})]})}),a&&s.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-60",children:s.jsxs("div",{className:"bg-gray-900 rounded-lg shadow-lg p-8 w-full max-w-md",children:[s.jsx("h3",{className:"text-xl font-semibold mb-4",children:"Generate Identity Token"}),s.jsxs("div",{className:"mb-4",children:[s.jsx("label",{className:"block mb-1 text-gray-300",children:"Description"}),s.jsx("input",{className:"w-full px-3 py-2 rounded bg-gray-800 border border-gray-700 text-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-600",value:o,onChange:X=>l(X.target.value),placeholder:"e.g. mac, CI, etc."})]}),s.jsxs("div",{className:"mb-4",children:[s.jsx("label",{className:"block mb-1 text-gray-300",children:"Expiry Date (optional)"}),s.jsx("input",{type:"datetime-local",className:"w-full px-3 py-2 rounded bg-gray-800 border border-gray-700 text-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-600",value:c,onChange:X=>u(X.target.value),min:new Date().toISOString().slice(0,16)})]}),h&&s.jsx("div",{className:"text-red-400 mb-2",children:h}),s.jsxs("div",{className:"flex justify-end gap-2",children:[s.jsx("button",{className:"px-4 py-2 rounded bg-gray-700 text-gray-200 hover:bg-gray-600",onClick:()=>i(!1),children:"Cancel"}),s.jsx("button",{className:"px-4 py-2 rounded bg-blue-600 text-white font-semibold hover:bg-blue-700",onClick:I,disabled:d,children:d?"Creating...":"Create"})]})]})}),v&&s.jsxs("div",{className:"mt-6 bg-gray-800 border border-blue-700 rounded-lg p-4",children:[s.jsx("div",{className:"text-green-400 font-semibold mb-2",children:"Token created!"}),s.jsxs("div",{children:[s.jsx("span",{className:"font-semibold",children:"Token:"}),s.jsx("span",{className:"ml-2 font-mono bg-gray-900 px-2 py-1 rounded text-blue-300",children:v.token})]})]})]})},Pke=()=>{const[e,t]=m.useState("password"),r=()=>{switch(e){case"password":return s.jsx($A,{});case"apiTokens":return s.jsx(Eke,{});default:return s.jsx($A,{})}};return s.jsxs("div",{className:"flex h-screen bg-gray-900 text-gray-200",children:[s.jsx("div",{className:"w-1/4 border-r border-gray-700 p-6",children:s.jsx(Ske,{activeSection:e,setActiveSection:t})}),s.jsx("div",{className:"w-3/4 p-10",children:r()})]})},Tke=()=>{var U,Z,J,G;const{sessionId:e}=Lk(),t=ya(),{user:r}=Gr(),[n,a]=m.useState(null),[i,o]=m.useState(!0),[l,c]=m.useState(null),[u,d]=m.useState(!1),[f,h]=m.useState(!1),[p,v]=m.useState("new"),[g,x]=m.useState(""),[b,y]=m.useState(!1),[w,k]=m.useState(""),[j,S]=m.useState(!1),[E]=m.useState(new Set),A=m.useRef(null),C=m.useRef(null),T=m.useRef(null),R=m.useRef(null),P=m.useRef(null),O=m.useRef(null),N=m.useRef(null),D=m.useCallback((te,ae)=>{te.srcObject=ae,te.play().catch(ne=>{console.warn("video.play() rejected:",ne.message)}),te.onloadedmetadata=()=>{console.log("[WatchSession] onloadedmetadata",{videoWidth:te.videoWidth,videoHeight:te.videoHeight}),te.videoWidth>0&&te.videoHeight>0&&d(!0)},te.readyState>=1&&te.videoWidth>0&&te.videoHeight>0&&d(!0)},[]);m.useEffect(()=>{!i&&A.current&&O.current&&D(A.current,O.current)},[i,D]),m.useEffect(()=>{if(!(n!=null&&n.start_time))return;const te=new Date(n.start_time).getTime(),ae=()=>{const ne=Date.now()-te,X=Math.floor(ne/6e4),L=Math.floor(ne%6e4/1e3);x(`${X}m ${L}s`)};return ae(),P.current=setInterval(ae,1e3),()=>{P.current&&clearInterval(P.current)}},[n==null?void 0:n.start_time]);const W=m.useCallback(async te=>{try{await Ae.sendTouchEvent(te)}catch{}},[]);m.useEffect(()=>{if(u&&A.current&&C.current&&(n!=null&&n.device)){const te=A.current;if(te.videoWidth>0&&te.videoHeight>0){let ae=te.videoWidth,ne=te.videoHeight;const X=n.device;if(X.metadata){const ee=typeof X.metadata=="string"?JSON.parse(X.metadata):X.metadata,H=parseInt(String((ee==null?void 0:ee.screenWidth)||(ee==null?void 0:ee.width)),10),fe=parseInt(String((ee==null?void 0:ee.screenHeight)||(ee==null?void 0:ee.height)),10);H>0&&fe>0&&(ae=Math.min(H,fe),ne=Math.max(H,fe))}const L={width:ae,height:ne};R.current=new z$(te,C.current,L,W)}}return()=>{var te;(te=R.current)==null||te.destroy(),R.current=null}},[u,n==null?void 0:n.device,W]);const M=m.useCallback(async te=>{var ne;const ae=(ne=n==null?void 0:n.device)==null?void 0:ne.platform;try{switch(te){case"home":await Ae.sendDeviceCommand({type:"deviceCommand",command:"home",platform:ae});break;case"back":await Ae.sendDeviceCommand({type:"deviceCommand",command:"back",platform:ae});break;case"backgroundApps":await Ae.sendDeviceCommand({type:"deviceCommand",command:"backgroundApps",platform:ae});break}}catch(X){console.warn("[WatchSession] Device control failed:",te,X)}},[(U=n==null?void 0:n.device)==null?void 0:U.platform]),F=m.useMemo(()=>[{action:"home",icon:s.jsx(oM,{className:"w-6 h-6"}),name:"Home",variant:"primary"},{action:"back",icon:s.jsx(av,{className:"w-6 h-6"}),name:"Back",variant:"primary"},{action:"backgroundApps",icon:s.jsx(dM,{className:"w-6 h-6"}),name:"Recent",variant:"primary"},{action:"keyboard",icon:s.jsx(l0,{className:"w-6 h-6"}),name:b?"Keyboard On":"Keyboard Off",onClick:()=>y(te=>!te)}],[b]);m.useEffect(()=>(u&&C.current&&!N.current&&(N.current=new F$(async te=>{try{const ae=JSON.parse(te);await Ae.sendKeyboardInput(ae)}catch(ae){console.warn("[WatchSession] Keyboard input error:",ae)}},C.current)),()=>{var te;(te=N.current)==null||te.detachListeners()}),[u]),m.useEffect(()=>{N.current&&(b?N.current.attachListeners():N.current.detachListeners())},[b]);const I=m.useCallback(async()=>{if(!(!w.trim()||j)){S(!0);try{const te=new Ng(w);await Ae.sendKeyboardInput(te.toJSON()),k("")}catch(te){console.warn("[WatchSession] Text input error:",te)}finally{S(!1)}}},[w,j]);m.useEffect(()=>{if(!e||!r)return;let te=!1;return(async()=>{var ne,X,L,ee;try{const H=await be.makeGETRequest(`/sessions/${e}/watchable`,{});if(!H.watchable){c(((ne=H.session)==null?void 0:ne.status)==="running"?"Session has no assigned node":`Session is not running (status: ${((X=H.session)==null?void 0:X.status)||"unknown"})`),o(!1);return}a(H.session),await Ve.connect(window.location.origin,r.id);const _=((L=(await be.makeGETRequest("/webrtc/ice-servers",{})).data)==null?void 0:L.iceServers)||[];if(te)return;const ie=le=>{console.log("[WatchSession] onTrack — received stream"),O.current=le,A.current&&D(A.current,le)},q=le=>{console.log("[WatchSession] connectionState:",le),v(le)};await Ae.initialize(e,_,ie,q);const B=[];Ae.onICECandidate=le=>{T.current?Ve.sendViewerICECandidate(T.current,le.toJSON()):B.push(le.toJSON())},Ve.onViewerICECandidate(async le=>{if(!te&&le.candidate)try{await Ae.addICECandidate(le.candidate)}catch(he){console.warn("Failed to add viewer ICE candidate:",he)}}),Ve.onViewerSessionEnded(le=>{le.sessionId===e&&h(!0)});const z=await Ae.createOffer();console.log("[WatchSession] sending watch offer...");const Q=await Ve.sendWatchOffer(e,z);if(console.log("[WatchSession] watch offer ack",{viewerId:(ee=Q.viewerId)==null?void 0:ee.substring(0,16)}),T.current=Q.viewerId,await Ae.handleAnswer(Q.answer,""),console.log("[WatchSession] setRemoteDescription done"),B.length>0){console.log(`[WatchSession] Flushing ${B.length} buffered local ICE candidates`);for(const le of B)Ve.sendViewerICECandidate(T.current,le)}o(!1)}catch(H){te||(c(H instanceof Error?H.message:String(H)),o(!1))}})(),()=>{var ne,X;te=!0,T.current&&Ve.stopWatching(T.current),Ae.close(),A.current&&(A.current.srcObject=null),(ne=R.current)==null||ne.destroy(),R.current=null,(X=N.current)==null||X.detachListeners(),N.current=null,Ve.offViewerEvents(),Ve.disconnect()}},[e,r]);const V=()=>{T.current&&Ve.stopWatching(T.current),t(-1)},$=()=>{t("/builds")};return i?s.jsx("div",{className:"flex items-center justify-center h-screen bg-gray-900",children:s.jsxs("div",{className:"text-center text-white",children:[s.jsx("div",{className:"animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500 mx-auto mb-4"}),s.jsx("p",{children:"Connecting to session..."})]})}):l?s.jsx("div",{className:"flex items-center justify-center h-screen bg-gray-900",children:s.jsxs("div",{className:"text-center text-white",children:[s.jsx("p",{className:"text-red-400 mb-4",children:l}),s.jsx("button",{onClick:()=>t(-1),className:"px-4 py-2 bg-gray-700 rounded hover:bg-gray-600",children:"Go Back"})]})}):s.jsxs("div",{className:"flex flex-col h-screen bg-gray-900",children:[s.jsxs("div",{className:"flex items-center justify-between px-4 py-2 bg-gray-800 border-b border-gray-700",children:[s.jsxs("div",{className:"flex items-center gap-4",children:[s.jsx("button",{onClick:V,className:"text-gray-400 hover:text-white",children:"← Back"}),s.jsxs("div",{className:"text-white text-sm",children:[s.jsx("span",{className:"font-semibold",children:((Z=n==null?void 0:n.device)==null?void 0:Z.name)||"Device"}),s.jsxs("span",{className:"text-gray-400 ml-2",children:[(J=n==null?void 0:n.device)==null?void 0:J.platform," ",(G=n==null?void 0:n.device)==null?void 0:G.version]})]}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx("span",{className:`inline-block w-2 h-2 rounded-full ${f?"bg-gray-500":p==="connected"?"bg-green-500":p==="connecting"?"bg-yellow-500 animate-pulse":"bg-red-500"}`}),s.jsx("span",{className:"text-gray-400 text-xs",children:f?"Ended":p})]})]}),s.jsxs("div",{className:"flex items-center gap-4",children:[g&&s.jsx("span",{className:"text-gray-400 text-xs",children:g}),s.jsx("span",{className:"text-xs px-2 py-0.5 rounded bg-blue-600 text-white",children:"LIVE"}),s.jsx("button",{onClick:V,className:"px-3 py-1 text-sm bg-red-600 text-white rounded hover:bg-red-500",children:"Stop Watching"})]})]}),s.jsxs("div",{className:"flex-1 flex items-center justify-center relative overflow-hidden",children:[f&&s.jsx("div",{className:"absolute inset-0 z-20 flex items-center justify-center bg-black bg-opacity-75",children:s.jsxs("div",{className:"text-center text-white",children:[s.jsx("p",{className:"text-xl mb-4",children:"Automation session has ended"}),s.jsxs("div",{className:"flex gap-3 justify-center",children:[s.jsx("button",{onClick:$,className:"px-4 py-2 bg-blue-600 rounded hover:bg-blue-500",children:"View Results"}),s.jsx("button",{onClick:()=>t(-1),className:"px-4 py-2 bg-gray-700 rounded hover:bg-gray-600",children:"Go Back"})]})]})}),s.jsxs("div",{className:"flex items-center gap-4",children:[s.jsxs("div",{className:"relative",children:[s.jsx("video",{ref:A,autoPlay:!0,playsInline:!0,muted:!0,style:{objectFit:"contain",backgroundColor:"#000000",display:"block",maxHeight:"calc(100vh - 60px)",maxWidth:"100vw"}}),s.jsx("canvas",{ref:C,className:"absolute top-0 left-0 w-full h-full cursor-pointer",style:{pointerEvents:f?"none":"auto",outline:"none"},tabIndex:0})]}),p==="connected"&&u&&s.jsxs("div",{className:"flex flex-col gap-4 flex-shrink-0 overflow-y-auto max-h-full items-start",children:[s.jsx("div",{className:"bg-gradient-to-b from-gray-900/95 to-gray-800/95 backdrop-blur-xl rounded-2xl p-4 border border-gray-700/50 shadow-2xl flex-shrink-0",children:s.jsx(O5,{controls:F,onControlClick:M,disabled:f,loadingActions:E})}),b&&s.jsx("div",{className:"bg-gradient-to-b from-gray-900/95 to-gray-800/95 backdrop-blur-xl rounded-2xl p-4 border border-gray-700/50 shadow-2xl flex-shrink-0",children:s.jsxs("div",{className:"flex flex-col gap-3 w-[240px]",children:[s.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[s.jsx(l0,{className:"w-5 h-5 text-yellow-400"}),s.jsx("h3",{className:"text-sm font-semibold text-white",children:"Text Input"})]}),s.jsx("textarea",{value:w,onChange:te=>k(te.target.value),onKeyDown:te=>{(te.ctrlKey||te.metaKey)&&te.key==="Enter"&&(te.preventDefault(),I())},placeholder:`Enter text here...
3086
3086
  Ctrl+Enter to send`,className:"w-full min-h-[80px] max-h-[160px] px-3 py-2 bg-gray-900 border border-gray-600 rounded-lg text-white placeholder-gray-500 resize-y focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:border-transparent text-sm",disabled:j||f}),s.jsx("button",{onClick:I,disabled:!w.trim()||j||f,className:"px-4 py-2 bg-yellow-500 hover:bg-yellow-600 text-gray-900 font-medium rounded-lg transition disabled:opacity-50 disabled:cursor-not-allowed text-sm",children:j?"Sending...":"Send Text"})]})})]})]}),!u&&!f&&s.jsx("div",{className:"absolute inset-0 flex items-center justify-center",children:s.jsxs("div",{className:"text-center text-white",children:[s.jsx("div",{className:"animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500 mx-auto mb-4"}),s.jsx("p",{className:"text-sm text-gray-400",children:"Waiting for video stream..."})]})})]})]})};class Ake{streamHubLogs(t,r=!0,n){const a=new AbortController,i={};r||(i.tail="false"),n&&(i.lines=n.toString());const o=be.formatUrl("/api/logs/hub",i),l=typeof o=="string"?o:o.toString(),c=localStorage.getItem("token")||sessionStorage.getItem("token"),u={"Cache-Control":"no-cache",Accept:"text/event-stream"};return c&&(u.Authorization=`Bearer ${c}`),fetch(l,{method:"GET",headers:u,signal:a.signal}).then(d=>{var g;if(!d.ok)throw new Error(`HTTP error! status: ${d.status}`);if(!d.body)throw new Error("Response body is null");(g=t.onOpen)==null||g.call(t);const f=d.body.getReader(),h=new TextDecoder;let p="";const v=()=>f.read().then(({done:x,value:b})=>{var w;if(x){(w=t.onClose)==null||w.call(t);return}p+=h.decode(b,{stream:!0});const y=p.split(`
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <meta name="theme-color" content="#000000" />
7
7
  <title>Device Farm</title>
8
- <script type="module" crossorigin src="/frontend/ui-assets/index-DAFblsKS.js"></script>
8
+ <script type="module" crossorigin src="/frontend/ui-assets/index-Y1zCk6SK.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/frontend/ui-assets/index-n-48xNrM.css">
10
10
  </head>
11
11
  <body class="bg-gray-900 antialiased">