@limrun/ui 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});require('./index.css');const b=require("react/jsx-runtime"),N=require("react");function ne(i){var u,D,s="";if(typeof i=="string"||typeof i=="number")s+=i;else if(typeof i=="object")if(Array.isArray(i)){var l=i.length;for(u=0;u<l;u++)i[u]&&(D=ne(i[u]))&&(s&&(s+=" "),s+=D)}else for(D in i)i[D]&&(s&&(s+=" "),s+=D);return s}function _e(){for(var i,u,D=0,s="",l=arguments.length;D<l;D++)(i=arguments[D])&&(u=ne(i))&&(s&&(s+=" "),s+=u);return s}const j={INJECT_KEYCODE:0,INJECT_TOUCH_EVENT:2,SET_CLIPBOARD:9},F={ACTION_DOWN:0,ACTION_UP:1,ACTION_MOVE:2,ACTION_CANCEL:3,BUTTON_PRIMARY:1},e={ACTION_DOWN:0,ACTION_UP:1,META_NONE:0,META_SHIFT_ON:1,META_ALT_ON:2,META_CTRL_ON:4096,META_META_ON:65536,KEYCODE_0:7,KEYCODE_1:8,KEYCODE_2:9,KEYCODE_3:10,KEYCODE_4:11,KEYCODE_5:12,KEYCODE_6:13,KEYCODE_7:14,KEYCODE_8:15,KEYCODE_9:16,DPAD_UP:19,DPAD_DOWN:20,DPAD_LEFT:21,DPAD_RIGHT:22,KEYCODE_A:29,KEYCODE_B:30,KEYCODE_C:31,KEYCODE_D:32,KEYCODE_E:33,KEYCODE_F:34,KEYCODE_G:35,KEYCODE_H:36,KEYCODE_I:37,KEYCODE_J:38,KEYCODE_K:39,KEYCODE_L:40,KEYCODE_M:41,KEYCODE_N:42,KEYCODE_O:43,KEYCODE_P:44,KEYCODE_Q:45,KEYCODE_R:46,KEYCODE_S:47,KEYCODE_T:48,KEYCODE_U:49,KEYCODE_V:50,KEYCODE_W:51,KEYCODE_X:52,KEYCODE_Y:53,KEYCODE_Z:54,KEYCODE_COMMA:55,KEYCODE_PERIOD:56,KEYCODE_ALT_LEFT:57,KEYCODE_ALT_RIGHT:58,KEYCODE_SHIFT_LEFT:59,KEYCODE_SHIFT_RIGHT:60,KEYCODE_TAB:61,KEYCODE_SPACE:62,ENTER:66,DEL:67,KEYCODE_GRAVE:68,KEYCODE_MINUS:69,KEYCODE_EQUALS:70,KEYCODE_LEFT_BRACKET:71,KEYCODE_RIGHT_BRACKET:72,KEYCODE_BACKSLASH:73,KEYCODE_SEMICOLON:74,KEYCODE_APOSTROPHE:75,KEYCODE_SLASH:76,MENU:82,KEYCODE_PAGE_UP:92,KEYCODE_PAGE_DOWN:93,KEYCODE_ESCAPE:111,FORWARD_DEL:112,KEYCODE_CTRL_LEFT:113,KEYCODE_CTRL_RIGHT:114,KEYCODE_META_LEFT:117,KEYCODE_META_RIGHT:118,KEYCODE_MOVE_HOME:122,KEYCODE_MOVE_END:123,KEYCODE_INSERT:124,KEYCODE_F1:131,KEYCODE_F2:132,KEYCODE_F3:133,KEYCODE_F4:134,KEYCODE_F5:135,KEYCODE_F6:136,KEYCODE_F7:137,KEYCODE_F8:138,KEYCODE_F9:139,KEYCODE_F10:140,KEYCODE_F11:141,KEYCODE_F12:142,KEYCODE_NUMPAD_0:144,KEYCODE_NUMPAD_1:145,KEYCODE_NUMPAD_2:146,KEYCODE_NUMPAD_3:147,KEYCODE_NUMPAD_4:148,KEYCODE_NUMPAD_5:149,KEYCODE_NUMPAD_6:150,KEYCODE_NUMPAD_7:151,KEYCODE_NUMPAD_8:152,KEYCODE_NUMPAD_9:153,KEYCODE_NUMPAD_DIVIDE:154,KEYCODE_NUMPAD_MULTIPLY:155,KEYCODE_NUMPAD_SUBTRACT:156,KEYCODE_NUMPAD_ADD:157,KEYCODE_NUMPAD_DOT:158,KEYCODE_NUMPAD_COMMA:159,KEYCODE_NUMPAD_ENTER:160,KEYCODE_NUMPAD_EQUALS:161},re={KeyA:e.KEYCODE_A,KeyB:e.KEYCODE_B,KeyC:e.KEYCODE_C,KeyD:e.KEYCODE_D,KeyE:e.KEYCODE_E,KeyF:e.KEYCODE_F,KeyG:e.KEYCODE_G,KeyH:e.KEYCODE_H,KeyI:e.KEYCODE_I,KeyJ:e.KEYCODE_J,KeyK:e.KEYCODE_K,KeyL:e.KEYCODE_L,KeyM:e.KEYCODE_M,KeyN:e.KEYCODE_N,KeyO:e.KEYCODE_O,KeyP:e.KEYCODE_P,KeyQ:e.KEYCODE_Q,KeyR:e.KEYCODE_R,KeyS:e.KEYCODE_S,KeyT:e.KEYCODE_T,KeyU:e.KEYCODE_U,KeyV:e.KEYCODE_V,KeyW:e.KEYCODE_W,KeyX:e.KEYCODE_X,KeyY:e.KEYCODE_Y,KeyZ:e.KEYCODE_Z,Digit0:e.KEYCODE_0,Digit1:e.KEYCODE_1,Digit2:e.KEYCODE_2,Digit3:e.KEYCODE_3,Digit4:e.KEYCODE_4,Digit5:e.KEYCODE_5,Digit6:e.KEYCODE_6,Digit7:e.KEYCODE_7,Digit8:e.KEYCODE_8,Digit9:e.KEYCODE_9,Backquote:e.KEYCODE_GRAVE,Minus:e.KEYCODE_MINUS,Equal:e.KEYCODE_EQUALS,BracketLeft:e.KEYCODE_LEFT_BRACKET,BracketRight:e.KEYCODE_RIGHT_BRACKET,Backslash:e.KEYCODE_BACKSLASH,Semicolon:e.KEYCODE_SEMICOLON,Quote:e.KEYCODE_APOSTROPHE,Comma:e.KEYCODE_COMMA,Period:e.KEYCODE_PERIOD,Slash:e.KEYCODE_SLASH,Space:e.KEYCODE_SPACE,Tab:e.KEYCODE_TAB,Escape:e.KEYCODE_ESCAPE,ArrowUp:e.DPAD_UP,ArrowDown:e.DPAD_DOWN,ArrowLeft:e.DPAD_LEFT,ArrowRight:e.DPAD_RIGHT,Enter:e.ENTER,Backspace:e.DEL,Delete:e.FORWARD_DEL,Home:e.KEYCODE_MOVE_HOME,End:e.KEYCODE_MOVE_END,PageUp:e.KEYCODE_PAGE_UP,PageDown:e.KEYCODE_PAGE_DOWN,Insert:e.KEYCODE_INSERT,F1:e.KEYCODE_F1,F2:e.KEYCODE_F2,F3:e.KEYCODE_F3,F4:e.KEYCODE_F4,F5:e.KEYCODE_F5,F6:e.KEYCODE_F6,F7:e.KEYCODE_F7,F8:e.KEYCODE_F8,F9:e.KEYCODE_F9,F10:e.KEYCODE_F10,F11:e.KEYCODE_F11,F12:e.KEYCODE_F12,ShiftLeft:e.KEYCODE_SHIFT_LEFT,ShiftRight:e.KEYCODE_SHIFT_RIGHT,ControlLeft:e.KEYCODE_CTRL_LEFT,ControlRight:e.KEYCODE_CTRL_RIGHT,AltLeft:e.KEYCODE_ALT_LEFT,AltRight:e.KEYCODE_ALT_RIGHT,MetaLeft:e.KEYCODE_META_LEFT,MetaRight:e.KEYCODE_META_RIGHT,ContextMenu:e.MENU,Numpad0:e.KEYCODE_NUMPAD_0,Numpad1:e.KEYCODE_NUMPAD_1,Numpad2:e.KEYCODE_NUMPAD_2,Numpad3:e.KEYCODE_NUMPAD_3,Numpad4:e.KEYCODE_NUMPAD_4,Numpad5:e.KEYCODE_NUMPAD_5,Numpad6:e.KEYCODE_NUMPAD_6,Numpad7:e.KEYCODE_NUMPAD_7,Numpad8:e.KEYCODE_NUMPAD_8,Numpad9:e.KEYCODE_NUMPAD_9,NumpadDivide:e.KEYCODE_NUMPAD_DIVIDE,NumpadMultiply:e.KEYCODE_NUMPAD_MULTIPLY,NumpadSubtract:e.KEYCODE_NUMPAD_SUBTRACT,NumpadAdd:e.KEYCODE_NUMPAD_ADD,NumpadDecimal:e.KEYCODE_NUMPAD_DOT,NumpadComma:e.KEYCODE_NUMPAD_COMMA,NumpadEnter:e.KEYCODE_NUMPAD_ENTER,NumpadEqual:e.KEYCODE_NUMPAD_EQUALS};function Ce(i,u,D,s,l,A,o=1,E=0,d=0){const C=new ArrayBuffer(32),T=new DataView(C);let _=0;return T.setUint8(_,j.INJECT_TOUCH_EVENT),_+=1,T.setUint8(_,i),_+=1,T.setBigInt64(_,BigInt(u)),_+=8,T.setInt32(_,Math.round(l),!0),_+=4,T.setInt32(_,Math.round(A),!0),_+=4,T.setUint16(_,D,!0),_+=2,T.setUint16(_,s,!0),_+=2,T.setInt16(_,Math.round(o*65535),!0),_+=2,T.setInt32(_,E,!0),_+=4,T.setInt32(_,d,!0),C}function De(i,u=!0){const s=new TextEncoder().encode(i),l=new ArrayBuffer(14+s.length),A=new DataView(l);let o=0;return A.setUint8(o,j.SET_CLIPBOARD),o+=1,A.setBigInt64(o,BigInt(0),!1),o+=8,A.setUint8(o,u?1:0),o+=1,A.setUint32(o,s.length,!1),o+=4,new Uint8Array(l,o).set(s),l}function W(i,u,D=0,s=0){const l=new ArrayBuffer(14),A=new DataView(l);let o=0;return A.setUint8(o,j.INJECT_KEYCODE),o+=1,A.setUint8(o,i),o+=1,A.setInt32(o,u,!0),o+=4,A.setInt32(o,D,!0),o+=4,A.setInt32(o,s,!0),l}const g=(...i)=>{window.debugRemoteControl&&console.log(...i)},Y=(...i)=>{window.debugRemoteControl&&console.warn(...i)};function Oe(i){const u=i.code,D=re[u];if(!D)return Y(`Unknown event.code: ${u}, key: ${i.key}`),null;let s=e.META_NONE;const l=u>="KeyA"&&u<="KeyZ",A=i.getModifierState("CapsLock"),o=i.shiftKey;let E=o;return l&&(E=o!==A),E&&(s|=e.META_SHIFT_ON),i.ctrlKey&&(s|=e.META_CTRL_ON),i.altKey&&(s|=e.META_ALT_ON),i.metaKey&&(s|=e.META_META_ON),{keycode:D,metaState:s}}const Ke=N.forwardRef(({className:i,url:u,token:D,sessionId:s,openUrl:l},A)=>{const o=N.useRef(null),E=N.useRef(null),d=N.useRef(null),C=N.useRef(null),[T,_]=N.useState(!1),k=N.useRef(void 0),I=N.useRef(new Map),U=N.useRef(new Map),m=N.useRef(new Map),R=N.useMemo(()=>s||Math.random().toString(36).substring(2,15)+Math.random().toString(36).substring(2,15),[s]),O=t=>{g(t)},L=t=>{!C.current||C.current.readyState!=="open"||C.current.send(t)},S=t=>{if(t.preventDefault(),t.stopPropagation(),!C.current||C.current.readyState!=="open"||!o.current)return;const K=o.current,c=K.getBoundingClientRect(),r=K.videoWidth,n=K.videoHeight;if(!r||!n)return;const y=(a,f,p,M)=>{const h=c.width,v=c.height,$=r/n,se=h/v;let H=h,B=v;$>se?B=h/$:H=v*$;const ae=(h-H)/2,de=(v-B)/2,V=f-c.left-ae,G=p-c.top-de,x=V>=0&&V<=H&&G>=0&&G<=B;let J=0,q=0;x&&(J=Math.max(0,Math.min(r,V/H*r)),q=Math.max(0,Math.min(n,G/B*n)));let w=null,P=null,ue=1;const ee=F.BUTTON_PRIMARY;switch(M){case"down":x?(w=F.ACTION_DOWN,P={x:J,y:q},m.current.set(a,P),a===-1&&o.current?.focus()):m.current.delete(a);break;case"move":m.current.has(a)&&x&&(w=F.ACTION_MOVE,P={x:J,y:q},m.current.set(a,P));break;case"up":case"cancel":m.current.has(a)&&(w=M==="cancel"?F.ACTION_CANCEL:F.ACTION_UP,P=m.current.get(a),m.current.delete(a));break}if(w!==null&&P!==null){const te=Ce(w,a,r,n,P.x,P.y,ue,ee,ee);te&&L(te)}else(M==="up"||M==="cancel")&&m.current.delete(a)};if("touches"in t){const a=t.changedTouches;let f;switch(t.type){case"touchstart":f="down";break;case"touchmove":f="move";break;case"touchend":f="up";break;case"touchcancel":f="cancel";break;default:return}for(let p=0;p<a.length;p++){const M=a[p];y(M.identifier,M.clientX,M.clientY,f)}}else{let f=null;switch(t.type){case"mousedown":t.button===0&&(f="down");break;case"mousemove":m.current.has(-1)&&(f="move");break;case"mouseup":t.button===0&&(f="up");break;case"mouseleave":m.current.has(-1)&&(f="up");break}f&&y(-1,t.clientX,t.clientY,f)}},Q=t=>{if(t.preventDefault(),t.stopPropagation(),g("Keyboard event:",{type:t.type,key:t.key,keyCode:t.keyCode,code:t.code,target:t.target.tagName,focused:document.activeElement===o.current}),document.activeElement!==o.current){Y("Video element not focused, skipping keyboard event");return}if(!C.current||C.current.readyState!=="open"){Y("Data channel not ready for keyboard event:",C.current?.readyState);return}if(t.type==="keydown"){if(t.key.toLowerCase()==="v"&&(t.metaKey||t.ctrlKey)){g("Paste shortcut detected"),navigator.clipboard.readText().then(c=>{if(c){g("Pasting text via SET_CLIPBOARD:",c.substring(0,20)+(c.length>20?"...":""));const r=De(c,!0);L(r)}}).catch(c=>{console.error("Failed to read clipboard contents: ",c)});return}if(t.key.toLowerCase()==="m"&&(t.metaKey||t.ctrlKey)){g("Menu shortcut detected");const c=W(e.ACTION_DOWN,e.MENU,0,e.META_NONE);L(c);const r=W(e.ACTION_UP,e.MENU,0,e.META_NONE);L(r);return}}const K=Oe(t);if(K){const{keycode:c,metaState:r}=K,n=t.type==="keydown"?e.ACTION_DOWN:e.ACTION_UP;g(`Sending Keycode: key=${t.key}, code=${c}, action=${n}, meta=${r}`);const y=W(n,c,0,r);L(y)}else g(`Ignoring unhandled key event: type=${t.type}, key=${t.key}`)},oe=()=>{E.current&&E.current.readyState===WebSocket.OPEN&&E.current.send(JSON.stringify({type:"keepAlive",sessionId:R}))},X=()=>{k.current&&window.clearInterval(k.current),k.current=window.setInterval(oe,1e4)},Z=()=>{k.current&&(window.clearInterval(k.current),k.current=void 0)},z=()=>{document.hidden?Z():X()},ce=async()=>{try{E.current=new WebSocket(`${u}?token=${D}`),E.current.onerror=r=>{O("WebSocket error: "+r)},E.current.onclose=()=>{O("WebSocket closed")},await new Promise((r,n)=>{E.current&&(E.current.onopen=r,setTimeout(()=>n(new Error("WebSocket connection timeout")),5e3))});const K=await new Promise((r,n)=>{const y=setTimeout(()=>n(new Error("RTCConfiguration timeout")),5e3),a=f=>{try{const p=JSON.parse(f.data);p.type==="rtcConfiguration"&&(clearTimeout(y),E.current?.removeEventListener("message",a),r(p.rtcConfiguration))}catch(p){console.error("Error handling RTC configuration:",p),n(p)}};E.current?.addEventListener("message",a),E.current?.send(JSON.stringify({type:"requestRtcConfiguration",sessionId:R}))});d.current=new RTCPeerConnection(K),d.current.addTransceiver("audio",{direction:"recvonly"});const c=d.current.addTransceiver("video",{direction:"recvonly"});if(RTCRtpReceiver.getCapabilities){const r=RTCRtpReceiver.getCapabilities("video");if(r&&r.codecs){const y=r.codecs.sort((a,f)=>{const p=M=>{const h=M.mimeType.toLowerCase();return h.includes("vp9")?1:h.includes("h265")||h.includes("hevc")?2:h.includes("h264")||h.includes("avc")?3:4};return p(a)-p(f)});c.setCodecPreferences(y),g("Set codec preferences:",y.map(a=>a.mimeType).join(", "))}}if(C.current=d.current.createDataChannel("control",{ordered:!0,negotiated:!0,id:1}),C.current.onopen=()=>{if(O("Control channel opened"),E.current&&(E.current.send(JSON.stringify({type:"requestFrame",sessionId:R})),l))try{const r=decodeURIComponent(l);O("Opening URL"),E.current.send(JSON.stringify({type:"openUrl",url:r,sessionId:R}))}catch(r){console.error({error:r},"Error decoding URL, falling back to the original URL"),E.current.send(JSON.stringify({type:"openUrl",url:l,sessionId:R}))}},C.current.onclose=()=>{O("Control channel closed")},C.current.onerror=r=>{console.error("Control channel error:",r),O("Control channel error: "+r)},d.current.onconnectionstatechange=()=>{O("Connection state: "+d.current?.connectionState),_(d.current?.connectionState==="connected")},d.current.oniceconnectionstatechange=()=>{O("ICE state: "+d.current?.iceConnectionState)},d.current.ontrack=r=>{O("Received remote track: "+r.track.kind),r.track.kind==="video"&&o.current&&(g(`[${new Date().toISOString()}] Video track received:`,r.track),o.current.srcObject=r.streams[0])},d.current.onicecandidate=r=>{if(r.candidate&&E.current){const n={type:"candidate",candidate:r.candidate.candidate,sdpMid:r.candidate.sdpMid,sdpMLineIndex:r.candidate.sdpMLineIndex,sessionId:R};E.current.send(JSON.stringify(n)),O("Sent ICE candidate")}else O("ICE candidate gathering completed")},E.current.onmessage=async r=>{let n;try{n=JSON.parse(r.data)}catch(y){Y("Error parsing message:",y);return}switch(O("Received: "+n.type),n.type){case"answer":if(!d.current){O("No peer connection, skipping answer");break}await d.current.setRemoteDescription(new RTCSessionDescription({type:"answer",sdp:n.sdp})),O("Set remote description");break;case"candidate":if(!d.current){O("No peer connection, skipping candidate");break}await d.current.addIceCandidate(new RTCIceCandidate({candidate:n.candidate,sdpMid:n.sdpMid,sdpMLineIndex:n.sdpMLineIndex})),O("Added ICE candidate");break;case"screenshot":if(typeof n.id!="string"||typeof n.dataUri!="string"){Y("Received invalid screenshot success message:",n);break}const y=I.current.get(n.id);if(!y){Y(`Received screenshot data for unknown or handled id: ${n.id}`);break}g(`Received screenshot data for id ${n.id}`),y({dataUri:n.dataUri}),I.current.delete(n.id),U.current.delete(n.id);break;case"screenshotError":if(typeof n.id!="string"||typeof n.message!="string"){Y("Received invalid screenshot error message:",n);break}const a=U.current.get(n.id);if(!a){Y(`Received screenshot error for unknown or handled id: ${n.id}`);break}Y(`Received screenshot error for id ${n.id}: ${n.message}`),a(new Error(n.message)),I.current.delete(n.id),U.current.delete(n.id);break;default:Y(`Received unhandled message type: ${n.type}`,n);break}},d.current){const r=await d.current.createOffer({offerToReceiveVideo:!0,offerToReceiveAudio:!1});await d.current.setLocalDescription(r),E.current&&E.current.send(JSON.stringify({type:"offer",sdp:r.sdp,sessionId:R})),O("Sent offer")}}catch(t){O("Error: "+t)}},Ee=()=>{E.current&&(E.current.close(),E.current=null),d.current&&(d.current.close(),d.current=null),o.current&&(o.current.srcObject=null),C.current&&(C.current.close(),C.current=null),_(!1),O("Stopped")};N.useEffect(()=>(ce(),document.hidden||X(),document.addEventListener("visibilitychange",z),()=>{Z(),Ee(),document.removeEventListener("visibilitychange",z)}),[u,D,s]);const ie=()=>{o.current&&o.current.focus()};return N.useImperativeHandle(A,()=>({openUrl:t=>{if(!E.current||E.current.readyState!==WebSocket.OPEN){Y("WebSocket not open, cannot send open_url command via ref.");return}try{const K=decodeURIComponent(t);O("Opening URL"),E.current.send(JSON.stringify({type:"openUrl",url:K,sessionId:R}))}catch(K){Y("Error decoding or sending URL via ref:",{error:K,url:t}),E.current.send(JSON.stringify({type:"openUrl",url:t,sessionId:R}))}},sendKeyEvent:t=>{if(!C.current||C.current.readyState!=="open"){Y("Data channel not ready for imperative key command:",C.current?.readyState);return}const K=re[t.code];if(!K){Y(`Unknown event.code for imperative command: ${t.code}`);return}let c=e.META_NONE;t.shiftKey&&(c|=e.META_SHIFT_ON),t.altKey&&(c|=e.META_ALT_ON),t.ctrlKey&&(c|=e.META_CTRL_ON),t.metaKey&&(c|=e.META_META_ON);const r=t.type==="keydown"?e.ACTION_DOWN:e.ACTION_UP;g(`Sending Imperative Key Command: code=${t.code}, keycode=${K}, action=${r}, meta=${c}`);const n=W(r,K,0,c);n&&L(n)},screenshot:()=>new Promise((t,K)=>{if(!E.current||E.current.readyState!==WebSocket.OPEN)return Y("WebSocket not open, cannot send screenshot command."),K(new Error("WebSocket is not connected or connection is not open."));const c=`ui-ss-${Date.now()}-${Math.random().toString(36).substring(2,9)}`,r={type:"screenshot",id:c};I.current.set(c,t),U.current.set(c,K),g("Sending screenshot request:",r);try{E.current.send(JSON.stringify(r))}catch(n){Y("Failed to send screenshot request immediately:",n),I.current.delete(c),U.current.delete(c),K(n);return}setTimeout(()=>{I.current.has(c)&&(Y(`Screenshot request timed out for id ${c}`),U.current.get(c)?.(new Error("Screenshot request timed out")),I.current.delete(c),U.current.delete(c))},3e4)})})),b.jsxs("div",{className:_e("rc-container",i),style:{touchAction:"none"},onMouseDown:S,onMouseMove:S,onMouseUp:S,onMouseLeave:S,onTouchStart:S,onTouchMove:S,onTouchEnd:S,onTouchCancel:S,children:[b.jsx("video",{ref:o,className:"rc-video",autoPlay:!0,playsInline:!0,muted:!0,tabIndex:0,style:{outline:"none",pointerEvents:"none"},onKeyDown:Q,onKeyUp:Q,onClick:ie,onFocus:()=>{o.current&&(o.current.style.outline="none")},onBlur:()=>{o.current&&(o.current.style.outline="none")}}),!T&&b.jsxs("div",{className:"rc-placeholder-wrapper",children:[b.jsx("div",{className:"rc-spinner"}),b.jsx("p",{className:"rc-placeholder-content",children:"Connecting..."})]})]})});exports.RemoteControl=Ke;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});require('./index.css');const b=require("react/jsx-runtime"),T=require("react");function ne(i){var u,D,s="";if(typeof i=="string"||typeof i=="number")s+=i;else if(typeof i=="object")if(Array.isArray(i)){var l=i.length;for(u=0;u<l;u++)i[u]&&(D=ne(i[u]))&&(s&&(s+=" "),s+=D)}else for(D in i)i[D]&&(s&&(s+=" "),s+=D);return s}function _e(){for(var i,u,D=0,s="",l=arguments.length;D<l;D++)(i=arguments[D])&&(u=ne(i))&&(s&&(s+=" "),s+=u);return s}const j={INJECT_KEYCODE:0,INJECT_TOUCH_EVENT:2,SET_CLIPBOARD:9},F={ACTION_DOWN:0,ACTION_UP:1,ACTION_MOVE:2,ACTION_CANCEL:3,BUTTON_PRIMARY:1},e={ACTION_DOWN:0,ACTION_UP:1,META_NONE:0,META_SHIFT_ON:1,META_ALT_ON:2,META_CTRL_ON:4096,META_META_ON:65536,KEYCODE_0:7,KEYCODE_1:8,KEYCODE_2:9,KEYCODE_3:10,KEYCODE_4:11,KEYCODE_5:12,KEYCODE_6:13,KEYCODE_7:14,KEYCODE_8:15,KEYCODE_9:16,DPAD_UP:19,DPAD_DOWN:20,DPAD_LEFT:21,DPAD_RIGHT:22,KEYCODE_A:29,KEYCODE_B:30,KEYCODE_C:31,KEYCODE_D:32,KEYCODE_E:33,KEYCODE_F:34,KEYCODE_G:35,KEYCODE_H:36,KEYCODE_I:37,KEYCODE_J:38,KEYCODE_K:39,KEYCODE_L:40,KEYCODE_M:41,KEYCODE_N:42,KEYCODE_O:43,KEYCODE_P:44,KEYCODE_Q:45,KEYCODE_R:46,KEYCODE_S:47,KEYCODE_T:48,KEYCODE_U:49,KEYCODE_V:50,KEYCODE_W:51,KEYCODE_X:52,KEYCODE_Y:53,KEYCODE_Z:54,KEYCODE_COMMA:55,KEYCODE_PERIOD:56,KEYCODE_ALT_LEFT:57,KEYCODE_ALT_RIGHT:58,KEYCODE_SHIFT_LEFT:59,KEYCODE_SHIFT_RIGHT:60,KEYCODE_TAB:61,KEYCODE_SPACE:62,ENTER:66,DEL:67,KEYCODE_GRAVE:68,KEYCODE_MINUS:69,KEYCODE_EQUALS:70,KEYCODE_LEFT_BRACKET:71,KEYCODE_RIGHT_BRACKET:72,KEYCODE_BACKSLASH:73,KEYCODE_SEMICOLON:74,KEYCODE_APOSTROPHE:75,KEYCODE_SLASH:76,MENU:82,KEYCODE_PAGE_UP:92,KEYCODE_PAGE_DOWN:93,KEYCODE_ESCAPE:111,FORWARD_DEL:112,KEYCODE_CTRL_LEFT:113,KEYCODE_CTRL_RIGHT:114,KEYCODE_META_LEFT:117,KEYCODE_META_RIGHT:118,KEYCODE_MOVE_HOME:122,KEYCODE_MOVE_END:123,KEYCODE_INSERT:124,KEYCODE_F1:131,KEYCODE_F2:132,KEYCODE_F3:133,KEYCODE_F4:134,KEYCODE_F5:135,KEYCODE_F6:136,KEYCODE_F7:137,KEYCODE_F8:138,KEYCODE_F9:139,KEYCODE_F10:140,KEYCODE_F11:141,KEYCODE_F12:142,KEYCODE_NUMPAD_0:144,KEYCODE_NUMPAD_1:145,KEYCODE_NUMPAD_2:146,KEYCODE_NUMPAD_3:147,KEYCODE_NUMPAD_4:148,KEYCODE_NUMPAD_5:149,KEYCODE_NUMPAD_6:150,KEYCODE_NUMPAD_7:151,KEYCODE_NUMPAD_8:152,KEYCODE_NUMPAD_9:153,KEYCODE_NUMPAD_DIVIDE:154,KEYCODE_NUMPAD_MULTIPLY:155,KEYCODE_NUMPAD_SUBTRACT:156,KEYCODE_NUMPAD_ADD:157,KEYCODE_NUMPAD_DOT:158,KEYCODE_NUMPAD_COMMA:159,KEYCODE_NUMPAD_ENTER:160,KEYCODE_NUMPAD_EQUALS:161},re={KeyA:e.KEYCODE_A,KeyB:e.KEYCODE_B,KeyC:e.KEYCODE_C,KeyD:e.KEYCODE_D,KeyE:e.KEYCODE_E,KeyF:e.KEYCODE_F,KeyG:e.KEYCODE_G,KeyH:e.KEYCODE_H,KeyI:e.KEYCODE_I,KeyJ:e.KEYCODE_J,KeyK:e.KEYCODE_K,KeyL:e.KEYCODE_L,KeyM:e.KEYCODE_M,KeyN:e.KEYCODE_N,KeyO:e.KEYCODE_O,KeyP:e.KEYCODE_P,KeyQ:e.KEYCODE_Q,KeyR:e.KEYCODE_R,KeyS:e.KEYCODE_S,KeyT:e.KEYCODE_T,KeyU:e.KEYCODE_U,KeyV:e.KEYCODE_V,KeyW:e.KEYCODE_W,KeyX:e.KEYCODE_X,KeyY:e.KEYCODE_Y,KeyZ:e.KEYCODE_Z,Digit0:e.KEYCODE_0,Digit1:e.KEYCODE_1,Digit2:e.KEYCODE_2,Digit3:e.KEYCODE_3,Digit4:e.KEYCODE_4,Digit5:e.KEYCODE_5,Digit6:e.KEYCODE_6,Digit7:e.KEYCODE_7,Digit8:e.KEYCODE_8,Digit9:e.KEYCODE_9,Backquote:e.KEYCODE_GRAVE,Minus:e.KEYCODE_MINUS,Equal:e.KEYCODE_EQUALS,BracketLeft:e.KEYCODE_LEFT_BRACKET,BracketRight:e.KEYCODE_RIGHT_BRACKET,Backslash:e.KEYCODE_BACKSLASH,Semicolon:e.KEYCODE_SEMICOLON,Quote:e.KEYCODE_APOSTROPHE,Comma:e.KEYCODE_COMMA,Period:e.KEYCODE_PERIOD,Slash:e.KEYCODE_SLASH,Space:e.KEYCODE_SPACE,Tab:e.KEYCODE_TAB,Escape:e.KEYCODE_ESCAPE,ArrowUp:e.DPAD_UP,ArrowDown:e.DPAD_DOWN,ArrowLeft:e.DPAD_LEFT,ArrowRight:e.DPAD_RIGHT,Enter:e.ENTER,Backspace:e.DEL,Delete:e.FORWARD_DEL,Home:e.KEYCODE_MOVE_HOME,End:e.KEYCODE_MOVE_END,PageUp:e.KEYCODE_PAGE_UP,PageDown:e.KEYCODE_PAGE_DOWN,Insert:e.KEYCODE_INSERT,F1:e.KEYCODE_F1,F2:e.KEYCODE_F2,F3:e.KEYCODE_F3,F4:e.KEYCODE_F4,F5:e.KEYCODE_F5,F6:e.KEYCODE_F6,F7:e.KEYCODE_F7,F8:e.KEYCODE_F8,F9:e.KEYCODE_F9,F10:e.KEYCODE_F10,F11:e.KEYCODE_F11,F12:e.KEYCODE_F12,ShiftLeft:e.KEYCODE_SHIFT_LEFT,ShiftRight:e.KEYCODE_SHIFT_RIGHT,ControlLeft:e.KEYCODE_CTRL_LEFT,ControlRight:e.KEYCODE_CTRL_RIGHT,AltLeft:e.KEYCODE_ALT_LEFT,AltRight:e.KEYCODE_ALT_RIGHT,MetaLeft:e.KEYCODE_META_LEFT,MetaRight:e.KEYCODE_META_RIGHT,ContextMenu:e.MENU,Numpad0:e.KEYCODE_NUMPAD_0,Numpad1:e.KEYCODE_NUMPAD_1,Numpad2:e.KEYCODE_NUMPAD_2,Numpad3:e.KEYCODE_NUMPAD_3,Numpad4:e.KEYCODE_NUMPAD_4,Numpad5:e.KEYCODE_NUMPAD_5,Numpad6:e.KEYCODE_NUMPAD_6,Numpad7:e.KEYCODE_NUMPAD_7,Numpad8:e.KEYCODE_NUMPAD_8,Numpad9:e.KEYCODE_NUMPAD_9,NumpadDivide:e.KEYCODE_NUMPAD_DIVIDE,NumpadMultiply:e.KEYCODE_NUMPAD_MULTIPLY,NumpadSubtract:e.KEYCODE_NUMPAD_SUBTRACT,NumpadAdd:e.KEYCODE_NUMPAD_ADD,NumpadDecimal:e.KEYCODE_NUMPAD_DOT,NumpadComma:e.KEYCODE_NUMPAD_COMMA,NumpadEnter:e.KEYCODE_NUMPAD_ENTER,NumpadEqual:e.KEYCODE_NUMPAD_EQUALS};function Ce(i,u,D,s,l,A,o=1,c=0,d=0){const C=new ArrayBuffer(32),N=new DataView(C);let _=0;return N.setUint8(_,j.INJECT_TOUCH_EVENT),_+=1,N.setUint8(_,i),_+=1,N.setBigInt64(_,BigInt(u)),_+=8,N.setInt32(_,Math.round(l),!0),_+=4,N.setInt32(_,Math.round(A),!0),_+=4,N.setUint16(_,D,!0),_+=2,N.setUint16(_,s,!0),_+=2,N.setInt16(_,Math.round(o*65535),!0),_+=2,N.setInt32(_,c,!0),_+=4,N.setInt32(_,d,!0),C}function De(i,u=!0){const s=new TextEncoder().encode(i),l=new ArrayBuffer(14+s.length),A=new DataView(l);let o=0;return A.setUint8(o,j.SET_CLIPBOARD),o+=1,A.setBigInt64(o,BigInt(0),!1),o+=8,A.setUint8(o,u?1:0),o+=1,A.setUint32(o,s.length,!1),o+=4,new Uint8Array(l,o).set(s),l}function W(i,u,D=0,s=0){const l=new ArrayBuffer(14),A=new DataView(l);let o=0;return A.setUint8(o,j.INJECT_KEYCODE),o+=1,A.setUint8(o,i),o+=1,A.setInt32(o,u,!0),o+=4,A.setInt32(o,D,!0),o+=4,A.setInt32(o,s,!0),l}const g=(...i)=>{window.debugRemoteControl&&console.log(...i)},Y=(...i)=>{window.debugRemoteControl&&console.warn(...i)};function Oe(i){const u=i.code,D=re[u];if(!D)return Y(`Unknown event.code: ${u}, key: ${i.key}`),null;let s=e.META_NONE;const l=u>="KeyA"&&u<="KeyZ",A=i.getModifierState("CapsLock"),o=i.shiftKey;let c=o;return l&&(c=o!==A),c&&(s|=e.META_SHIFT_ON),i.ctrlKey&&(s|=e.META_CTRL_ON),i.altKey&&(s|=e.META_ALT_ON),i.metaKey&&(s|=e.META_META_ON),{keycode:D,metaState:s}}const Ke=T.forwardRef(({className:i,url:u,token:D,sessionId:s,openUrl:l},A)=>{const o=T.useRef(null),c=T.useRef(null),d=T.useRef(null),C=T.useRef(null),[N,_]=T.useState(!1),k=T.useRef(void 0),I=T.useRef(new Map),U=T.useRef(new Map),m=T.useRef(new Map),R=T.useMemo(()=>s||Math.random().toString(36).substring(2,15)+Math.random().toString(36).substring(2,15),[s]),O=n=>{g(n)},L=n=>{!C.current||C.current.readyState!=="open"||C.current.send(n)},S=n=>{if(n.preventDefault(),n.stopPropagation(),!C.current||C.current.readyState!=="open"||!o.current)return;const K=o.current,E=K.getBoundingClientRect(),t=K.videoWidth,r=K.videoHeight;if(!t||!r)return;const y=(a,f,p,M)=>{const h=E.width,v=E.height,$=t/r,se=h/v;let H=h,B=v;$>se?B=h/$:H=v*$;const ae=(h-H)/2,de=(v-B)/2,V=f-E.left-ae,G=p-E.top-de,x=V>=0&&V<=H&&G>=0&&G<=B;let J=0,q=0;x&&(J=Math.max(0,Math.min(t,V/H*t)),q=Math.max(0,Math.min(r,G/B*r)));let w=null,P=null,ue=1;const ee=F.BUTTON_PRIMARY;switch(M){case"down":x?(w=F.ACTION_DOWN,P={x:J,y:q},m.current.set(a,P),a===-1&&o.current?.focus()):m.current.delete(a);break;case"move":m.current.has(a)&&x&&(w=F.ACTION_MOVE,P={x:J,y:q},m.current.set(a,P));break;case"up":case"cancel":m.current.has(a)&&(w=M==="cancel"?F.ACTION_CANCEL:F.ACTION_UP,P=m.current.get(a),m.current.delete(a));break}if(w!==null&&P!==null){const te=Ce(w,a,t,r,P.x,P.y,ue,ee,ee);te&&L(te)}else(M==="up"||M==="cancel")&&m.current.delete(a)};if("touches"in n){const a=n.changedTouches;let f;switch(n.type){case"touchstart":f="down";break;case"touchmove":f="move";break;case"touchend":f="up";break;case"touchcancel":f="cancel";break;default:return}for(let p=0;p<a.length;p++){const M=a[p];y(M.identifier,M.clientX,M.clientY,f)}}else{let f=null;switch(n.type){case"mousedown":n.button===0&&(f="down");break;case"mousemove":m.current.has(-1)&&(f="move");break;case"mouseup":n.button===0&&(f="up");break;case"mouseleave":m.current.has(-1)&&(f="up");break}f&&y(-1,n.clientX,n.clientY,f)}},Q=n=>{if(n.preventDefault(),n.stopPropagation(),g("Keyboard event:",{type:n.type,key:n.key,keyCode:n.keyCode,code:n.code,target:n.target.tagName,focused:document.activeElement===o.current}),document.activeElement!==o.current){Y("Video element not focused, skipping keyboard event");return}if(!C.current||C.current.readyState!=="open"){Y("Data channel not ready for keyboard event:",C.current?.readyState);return}if(n.type==="keydown"){if(n.key.toLowerCase()==="v"&&(n.metaKey||n.ctrlKey)){g("Paste shortcut detected"),navigator.clipboard.readText().then(E=>{if(E){g("Pasting text via SET_CLIPBOARD:",E.substring(0,20)+(E.length>20?"...":""));const t=De(E,!0);L(t)}}).catch(E=>{console.error("Failed to read clipboard contents: ",E)});return}if(n.key.toLowerCase()==="m"&&(n.metaKey||n.ctrlKey)){g("Menu shortcut detected");const E=W(e.ACTION_DOWN,e.MENU,0,e.META_NONE);L(E);const t=W(e.ACTION_UP,e.MENU,0,e.META_NONE);L(t);return}}const K=Oe(n);if(K){const{keycode:E,metaState:t}=K,r=n.type==="keydown"?e.ACTION_DOWN:e.ACTION_UP;g(`Sending Keycode: key=${n.key}, code=${E}, action=${r}, meta=${t}`);const y=W(r,E,0,t);L(y)}else g(`Ignoring unhandled key event: type=${n.type}, key=${n.key}`)},oe=()=>{c.current&&c.current.readyState===WebSocket.OPEN&&c.current.send(JSON.stringify({type:"keepAlive",sessionId:R}))},X=()=>{k.current&&window.clearInterval(k.current),k.current=window.setInterval(oe,1e4)},Z=()=>{k.current&&(window.clearInterval(k.current),k.current=void 0)},z=()=>{document.hidden?Z():X()},ce=async()=>{try{c.current=new WebSocket(`${u}?token=${D}`),c.current.onerror=t=>{O("WebSocket error: "+t)},c.current.onclose=()=>{O("WebSocket closed")},await new Promise((t,r)=>{c.current&&(c.current.onopen=t,setTimeout(()=>r(new Error("WebSocket connection timeout")),3e4))});const K=await new Promise((t,r)=>{const y=setTimeout(()=>r(new Error("RTCConfiguration timeout")),3e4),a=f=>{try{const p=JSON.parse(f.data);p.type==="rtcConfiguration"&&(clearTimeout(y),c.current?.removeEventListener("message",a),t(p.rtcConfiguration))}catch(p){console.error("Error handling RTC configuration:",p),r(p)}};c.current?.addEventListener("message",a),c.current?.send(JSON.stringify({type:"requestRtcConfiguration",sessionId:R}))});d.current=new RTCPeerConnection(K),d.current.addTransceiver("audio",{direction:"recvonly"});const E=d.current.addTransceiver("video",{direction:"recvonly"});if(RTCRtpReceiver.getCapabilities){const t=RTCRtpReceiver.getCapabilities("video");if(t&&t.codecs){const y=t.codecs.sort((a,f)=>{const p=M=>{const h=M.mimeType.toLowerCase();return h.includes("vp9")?1:h.includes("h265")||h.includes("hevc")?2:h.includes("h264")||h.includes("avc")?3:4};return p(a)-p(f)});E.setCodecPreferences(y),g("Set codec preferences:",y.map(a=>a.mimeType).join(", "))}}if(C.current=d.current.createDataChannel("control",{ordered:!0,negotiated:!0,id:1}),C.current.onopen=()=>{if(O("Control channel opened"),c.current){for(let t=0;t<12;t++)setTimeout(()=>{c.current&&c.current.send(JSON.stringify({type:"requestFrame",sessionId:R}))},t*125);if(l)try{const t=decodeURIComponent(l);O("Opening URL"),c.current.send(JSON.stringify({type:"openUrl",url:t,sessionId:R}))}catch(t){console.error({error:t},"Error decoding URL, falling back to the original URL"),c.current.send(JSON.stringify({type:"openUrl",url:l,sessionId:R}))}}},C.current.onclose=()=>{O("Control channel closed")},C.current.onerror=t=>{console.error("Control channel error:",t),O("Control channel error: "+t)},d.current.onconnectionstatechange=()=>{O("Connection state: "+d.current?.connectionState),_(d.current?.connectionState==="connected")},d.current.oniceconnectionstatechange=()=>{O("ICE state: "+d.current?.iceConnectionState)},d.current.ontrack=t=>{O("Received remote track: "+t.track.kind),t.track.kind==="video"&&o.current&&(g(`[${new Date().toISOString()}] Video track received:`,t.track),o.current.srcObject=t.streams[0])},d.current.onicecandidate=t=>{if(t.candidate&&c.current){const r={type:"candidate",candidate:t.candidate.candidate,sdpMid:t.candidate.sdpMid,sdpMLineIndex:t.candidate.sdpMLineIndex,sessionId:R};c.current.send(JSON.stringify(r)),O("Sent ICE candidate")}else O("ICE candidate gathering completed")},c.current.onmessage=async t=>{let r;try{r=JSON.parse(t.data)}catch(y){Y("Error parsing message:",y);return}switch(O("Received: "+r.type),r.type){case"answer":if(!d.current){O("No peer connection, skipping answer");break}await d.current.setRemoteDescription(new RTCSessionDescription({type:"answer",sdp:r.sdp})),O("Set remote description");break;case"candidate":if(!d.current){O("No peer connection, skipping candidate");break}await d.current.addIceCandidate(new RTCIceCandidate({candidate:r.candidate,sdpMid:r.sdpMid,sdpMLineIndex:r.sdpMLineIndex})),O("Added ICE candidate");break;case"screenshot":if(typeof r.id!="string"||typeof r.dataUri!="string"){Y("Received invalid screenshot success message:",r);break}const y=I.current.get(r.id);if(!y){Y(`Received screenshot data for unknown or handled id: ${r.id}`);break}g(`Received screenshot data for id ${r.id}`),y({dataUri:r.dataUri}),I.current.delete(r.id),U.current.delete(r.id);break;case"screenshotError":if(typeof r.id!="string"||typeof r.message!="string"){Y("Received invalid screenshot error message:",r);break}const a=U.current.get(r.id);if(!a){Y(`Received screenshot error for unknown or handled id: ${r.id}`);break}Y(`Received screenshot error for id ${r.id}: ${r.message}`),a(new Error(r.message)),I.current.delete(r.id),U.current.delete(r.id);break;default:Y(`Received unhandled message type: ${r.type}`,r);break}},d.current){const t=await d.current.createOffer({offerToReceiveVideo:!0,offerToReceiveAudio:!1});await d.current.setLocalDescription(t),c.current&&c.current.send(JSON.stringify({type:"offer",sdp:t.sdp,sessionId:R})),O("Sent offer")}}catch(n){O("Error: "+n)}},Ee=()=>{c.current&&(c.current.close(),c.current=null),d.current&&(d.current.close(),d.current=null),o.current&&(o.current.srcObject=null),C.current&&(C.current.close(),C.current=null),_(!1),O("Stopped")};T.useEffect(()=>(ce(),document.hidden||X(),document.addEventListener("visibilitychange",z),()=>{Z(),Ee(),document.removeEventListener("visibilitychange",z)}),[u,D,s]);const ie=()=>{o.current&&o.current.focus()};return T.useImperativeHandle(A,()=>({openUrl:n=>{if(!c.current||c.current.readyState!==WebSocket.OPEN){Y("WebSocket not open, cannot send open_url command via ref.");return}try{const K=decodeURIComponent(n);O("Opening URL"),c.current.send(JSON.stringify({type:"openUrl",url:K,sessionId:R}))}catch(K){Y("Error decoding or sending URL via ref:",{error:K,url:n}),c.current.send(JSON.stringify({type:"openUrl",url:n,sessionId:R}))}},sendKeyEvent:n=>{if(!C.current||C.current.readyState!=="open"){Y("Data channel not ready for imperative key command:",C.current?.readyState);return}const K=re[n.code];if(!K){Y(`Unknown event.code for imperative command: ${n.code}`);return}let E=e.META_NONE;n.shiftKey&&(E|=e.META_SHIFT_ON),n.altKey&&(E|=e.META_ALT_ON),n.ctrlKey&&(E|=e.META_CTRL_ON),n.metaKey&&(E|=e.META_META_ON);const t=n.type==="keydown"?e.ACTION_DOWN:e.ACTION_UP;g(`Sending Imperative Key Command: code=${n.code}, keycode=${K}, action=${t}, meta=${E}`);const r=W(t,K,0,E);r&&L(r)},screenshot:()=>new Promise((n,K)=>{if(!c.current||c.current.readyState!==WebSocket.OPEN)return Y("WebSocket not open, cannot send screenshot command."),K(new Error("WebSocket is not connected or connection is not open."));const E=`ui-ss-${Date.now()}-${Math.random().toString(36).substring(2,9)}`,t={type:"screenshot",id:E};I.current.set(E,n),U.current.set(E,K),g("Sending screenshot request:",t);try{c.current.send(JSON.stringify(t))}catch(r){Y("Failed to send screenshot request immediately:",r),I.current.delete(E),U.current.delete(E),K(r);return}setTimeout(()=>{I.current.has(E)&&(Y(`Screenshot request timed out for id ${E}`),U.current.get(E)?.(new Error("Screenshot request timed out")),I.current.delete(E),U.current.delete(E))},3e4)})})),b.jsxs("div",{className:_e("rc-container",i),style:{touchAction:"none"},onMouseDown:S,onMouseMove:S,onMouseUp:S,onMouseLeave:S,onTouchStart:S,onTouchMove:S,onTouchEnd:S,onTouchCancel:S,children:[b.jsx("video",{ref:o,className:"rc-video",autoPlay:!0,playsInline:!0,muted:!0,tabIndex:0,style:{outline:"none",pointerEvents:"none"},onKeyDown:Q,onKeyUp:Q,onClick:ie,onFocus:()=>{o.current&&(o.current.style.outline="none")},onBlur:()=>{o.current&&(o.current.style.outline="none")}}),!N&&b.jsxs("div",{className:"rc-placeholder-wrapper",children:[b.jsx("div",{className:"rc-spinner"}),b.jsx("p",{className:"rc-placeholder-content",children:"Connecting..."})]})]})});exports.RemoteControl=Ke;
package/dist/index.js CHANGED
@@ -256,10 +256,10 @@ const Q = {
256
256
  NumpadEnter: e.KEYCODE_NUMPAD_ENTER,
257
257
  NumpadEqual: e.KEYCODE_NUMPAD_EQUALS
258
258
  };
259
- function Ye(i, u, D, s, l, A, o = 1, E = 0, d = 0) {
259
+ function Ye(i, u, D, s, l, A, o = 1, c = 0, d = 0) {
260
260
  const C = new ArrayBuffer(32), N = new DataView(C);
261
261
  let _ = 0;
262
- return N.setUint8(_, Q.INJECT_TOUCH_EVENT), _ += 1, N.setUint8(_, i), _ += 1, N.setBigInt64(_, BigInt(u)), _ += 8, N.setInt32(_, Math.round(l), !0), _ += 4, N.setInt32(_, Math.round(A), !0), _ += 4, N.setUint16(_, D, !0), _ += 2, N.setUint16(_, s, !0), _ += 2, N.setInt16(_, Math.round(o * 65535), !0), _ += 2, N.setInt32(_, E, !0), _ += 4, N.setInt32(_, d, !0), C;
262
+ return N.setUint8(_, Q.INJECT_TOUCH_EVENT), _ += 1, N.setUint8(_, i), _ += 1, N.setBigInt64(_, BigInt(u)), _ += 8, N.setInt32(_, Math.round(l), !0), _ += 4, N.setInt32(_, Math.round(A), !0), _ += 4, N.setUint16(_, D, !0), _ += 2, N.setUint16(_, s, !0), _ += 2, N.setInt16(_, Math.round(o * 65535), !0), _ += 2, N.setInt32(_, c, !0), _ += 4, N.setInt32(_, d, !0), C;
263
263
  }
264
264
  function Ae(i, u = !0) {
265
265
  const s = new TextEncoder().encode(i), l = new ArrayBuffer(14 + s.length), A = new DataView(l);
@@ -282,30 +282,30 @@ function pe(i) {
282
282
  return Y(`Unknown event.code: ${u}, key: ${i.key}`), null;
283
283
  let s = e.META_NONE;
284
284
  const l = u >= "KeyA" && u <= "KeyZ", A = i.getModifierState("CapsLock"), o = i.shiftKey;
285
- let E = o;
286
- return l && (E = o !== A), E && (s |= e.META_SHIFT_ON), i.ctrlKey && (s |= e.META_CTRL_ON), i.altKey && (s |= e.META_ALT_ON), i.metaKey && (s |= e.META_META_ON), { keycode: D, metaState: s };
285
+ let c = o;
286
+ return l && (c = o !== A), c && (s |= e.META_SHIFT_ON), i.ctrlKey && (s |= e.META_CTRL_ON), i.altKey && (s |= e.META_ALT_ON), i.metaKey && (s |= e.META_META_ON), { keycode: D, metaState: s };
287
287
  }
288
288
  const Te = Ce(
289
289
  ({ className: i, url: u, token: D, sessionId: s, openUrl: l }, A) => {
290
- const o = P(null), E = P(null), d = P(null), C = P(null), [N, _] = De(!1), k = P(void 0), I = P(/* @__PURE__ */ new Map()), U = P(/* @__PURE__ */ new Map()), m = P(/* @__PURE__ */ new Map()), h = Oe(
290
+ const o = P(null), c = P(null), d = P(null), C = P(null), [N, _] = De(!1), k = P(void 0), I = P(/* @__PURE__ */ new Map()), U = P(/* @__PURE__ */ new Map()), m = P(/* @__PURE__ */ new Map()), h = Oe(
291
291
  () => s || Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15),
292
292
  [s]
293
- ), O = (t) => {
294
- T(t);
295
- }, L = (t) => {
296
- !C.current || C.current.readyState !== "open" || C.current.send(t);
297
- }, S = (t) => {
298
- if (t.preventDefault(), t.stopPropagation(), !C.current || C.current.readyState !== "open" || !o.current)
293
+ ), O = (n) => {
294
+ T(n);
295
+ }, L = (n) => {
296
+ !C.current || C.current.readyState !== "open" || C.current.send(n);
297
+ }, S = (n) => {
298
+ if (n.preventDefault(), n.stopPropagation(), !C.current || C.current.readyState !== "open" || !o.current)
299
299
  return;
300
- const K = o.current, c = K.getBoundingClientRect(), r = K.videoWidth, n = K.videoHeight;
301
- if (!r || !n) return;
300
+ const K = o.current, E = K.getBoundingClientRect(), t = K.videoWidth, r = K.videoHeight;
301
+ if (!t || !r) return;
302
302
  const p = (a, f, y, g) => {
303
- const M = c.width, F = c.height, W = r / n, ae = M / F;
303
+ const M = E.width, F = E.height, W = t / r, ae = M / F;
304
304
  let v = M, H = F;
305
305
  W > ae ? H = M / W : v = F * W;
306
- const de = (M - v) / 2, ue = (F - H) / 2, $ = f - c.left - de, V = y - c.top - ue, G = $ >= 0 && $ <= v && V >= 0 && V <= H;
306
+ const de = (M - v) / 2, ue = (F - H) / 2, $ = f - E.left - de, V = y - E.top - ue, G = $ >= 0 && $ <= v && V >= 0 && V <= H;
307
307
  let J = 0, x = 0;
308
- G && (J = Math.max(0, Math.min(r, $ / v * r)), x = Math.max(0, Math.min(n, V / H * n)));
308
+ G && (J = Math.max(0, Math.min(t, $ / v * t)), x = Math.max(0, Math.min(r, V / H * r)));
309
309
  let w = null, R = null, _e = 1;
310
310
  const ee = b.BUTTON_PRIMARY;
311
311
  switch (g) {
@@ -324,8 +324,8 @@ const Te = Ce(
324
324
  const te = Ye(
325
325
  w,
326
326
  a,
327
+ t,
327
328
  r,
328
- n,
329
329
  R.x,
330
330
  R.y,
331
331
  _e,
@@ -335,10 +335,10 @@ const Te = Ce(
335
335
  te && L(te);
336
336
  } else (g === "up" || g === "cancel") && m.current.delete(a);
337
337
  };
338
- if ("touches" in t) {
339
- const a = t.changedTouches;
338
+ if ("touches" in n) {
339
+ const a = n.changedTouches;
340
340
  let f;
341
- switch (t.type) {
341
+ switch (n.type) {
342
342
  case "touchstart":
343
343
  f = "down";
344
344
  break;
@@ -360,29 +360,29 @@ const Te = Ce(
360
360
  }
361
361
  } else {
362
362
  let f = null;
363
- switch (t.type) {
363
+ switch (n.type) {
364
364
  case "mousedown":
365
- t.button === 0 && (f = "down");
365
+ n.button === 0 && (f = "down");
366
366
  break;
367
367
  case "mousemove":
368
368
  m.current.has(-1) && (f = "move");
369
369
  break;
370
370
  case "mouseup":
371
- t.button === 0 && (f = "up");
371
+ n.button === 0 && (f = "up");
372
372
  break;
373
373
  case "mouseleave":
374
374
  m.current.has(-1) && (f = "up");
375
375
  break;
376
376
  }
377
- f && p(-1, t.clientX, t.clientY, f);
377
+ f && p(-1, n.clientX, n.clientY, f);
378
378
  }
379
- }, X = (t) => {
380
- if (t.preventDefault(), t.stopPropagation(), T("Keyboard event:", {
381
- type: t.type,
382
- key: t.key,
383
- keyCode: t.keyCode,
384
- code: t.code,
385
- target: t.target.tagName,
379
+ }, X = (n) => {
380
+ if (n.preventDefault(), n.stopPropagation(), T("Keyboard event:", {
381
+ type: n.type,
382
+ key: n.key,
383
+ keyCode: n.keyCode,
384
+ code: n.code,
385
+ target: n.target.tagName,
386
386
  focused: document.activeElement === o.current
387
387
  }), document.activeElement !== o.current) {
388
388
  Y("Video element not focused, skipping keyboard event");
@@ -392,58 +392,58 @@ const Te = Ce(
392
392
  Y("Data channel not ready for keyboard event:", C.current?.readyState);
393
393
  return;
394
394
  }
395
- if (t.type === "keydown") {
396
- if (t.key.toLowerCase() === "v" && (t.metaKey || t.ctrlKey)) {
397
- T("Paste shortcut detected"), navigator.clipboard.readText().then((c) => {
398
- if (c) {
395
+ if (n.type === "keydown") {
396
+ if (n.key.toLowerCase() === "v" && (n.metaKey || n.ctrlKey)) {
397
+ T("Paste shortcut detected"), navigator.clipboard.readText().then((E) => {
398
+ if (E) {
399
399
  T(
400
400
  "Pasting text via SET_CLIPBOARD:",
401
- c.substring(0, 20) + (c.length > 20 ? "..." : "")
401
+ E.substring(0, 20) + (E.length > 20 ? "..." : "")
402
402
  );
403
- const r = Ae(c, !0);
404
- L(r);
403
+ const t = Ae(E, !0);
404
+ L(t);
405
405
  }
406
- }).catch((c) => {
407
- console.error("Failed to read clipboard contents: ", c);
406
+ }).catch((E) => {
407
+ console.error("Failed to read clipboard contents: ", E);
408
408
  });
409
409
  return;
410
410
  }
411
- if (t.key.toLowerCase() === "m" && (t.metaKey || t.ctrlKey)) {
411
+ if (n.key.toLowerCase() === "m" && (n.metaKey || n.ctrlKey)) {
412
412
  T("Menu shortcut detected");
413
- const c = B(
413
+ const E = B(
414
414
  e.ACTION_DOWN,
415
415
  e.MENU,
416
416
  0,
417
417
  e.META_NONE
418
418
  // Modifiers are handled by the shortcut check, not passed down
419
419
  );
420
- L(c);
421
- const r = B(
420
+ L(E);
421
+ const t = B(
422
422
  e.ACTION_UP,
423
423
  e.MENU,
424
424
  0,
425
425
  e.META_NONE
426
426
  );
427
- L(r);
427
+ L(t);
428
428
  return;
429
429
  }
430
430
  }
431
- const K = pe(t);
431
+ const K = pe(n);
432
432
  if (K) {
433
- const { keycode: c, metaState: r } = K, n = t.type === "keydown" ? e.ACTION_DOWN : e.ACTION_UP;
434
- T(`Sending Keycode: key=${t.key}, code=${c}, action=${n}, meta=${r}`);
433
+ const { keycode: E, metaState: t } = K, r = n.type === "keydown" ? e.ACTION_DOWN : e.ACTION_UP;
434
+ T(`Sending Keycode: key=${n.key}, code=${E}, action=${r}, meta=${t}`);
435
435
  const p = B(
436
- n,
437
- c,
436
+ r,
437
+ E,
438
438
  0,
439
439
  // repeat count, typically 0 for single presses
440
- r
440
+ t
441
441
  );
442
442
  L(p);
443
443
  } else
444
- T(`Ignoring unhandled key event: type=${t.type}, key=${t.key}`);
444
+ T(`Ignoring unhandled key event: type=${n.type}, key=${n.key}`);
445
445
  }, ce = () => {
446
- E.current && E.current.readyState === WebSocket.OPEN && E.current.send(
446
+ c.current && c.current.readyState === WebSocket.OPEN && c.current.send(
447
447
  JSON.stringify({
448
448
  type: "keepAlive",
449
449
  sessionId: h
@@ -457,23 +457,23 @@ const Te = Ce(
457
457
  document.hidden ? Z() : j();
458
458
  }, Ee = async () => {
459
459
  try {
460
- E.current = new WebSocket(`${u}?token=${D}`), E.current.onerror = (r) => {
461
- O("WebSocket error: " + r);
462
- }, E.current.onclose = () => {
460
+ c.current = new WebSocket(`${u}?token=${D}`), c.current.onerror = (t) => {
461
+ O("WebSocket error: " + t);
462
+ }, c.current.onclose = () => {
463
463
  O("WebSocket closed");
464
- }, await new Promise((r, n) => {
465
- E.current && (E.current.onopen = r, setTimeout(() => n(new Error("WebSocket connection timeout")), 5e3));
464
+ }, await new Promise((t, r) => {
465
+ c.current && (c.current.onopen = t, setTimeout(() => r(new Error("WebSocket connection timeout")), 3e4));
466
466
  });
467
- const K = await new Promise((r, n) => {
468
- const p = setTimeout(() => n(new Error("RTCConfiguration timeout")), 5e3), a = (f) => {
467
+ const K = await new Promise((t, r) => {
468
+ const p = setTimeout(() => r(new Error("RTCConfiguration timeout")), 3e4), a = (f) => {
469
469
  try {
470
470
  const y = JSON.parse(f.data);
471
- y.type === "rtcConfiguration" && (clearTimeout(p), E.current?.removeEventListener("message", a), r(y.rtcConfiguration));
471
+ y.type === "rtcConfiguration" && (clearTimeout(p), c.current?.removeEventListener("message", a), t(y.rtcConfiguration));
472
472
  } catch (y) {
473
- console.error("Error handling RTC configuration:", y), n(y);
473
+ console.error("Error handling RTC configuration:", y), r(y);
474
474
  }
475
475
  };
476
- E.current?.addEventListener("message", a), E.current?.send(
476
+ c.current?.addEventListener("message", a), c.current?.send(
477
477
  JSON.stringify({
478
478
  type: "requestRtcConfiguration",
479
479
  sessionId: h
@@ -481,18 +481,18 @@ const Te = Ce(
481
481
  );
482
482
  });
483
483
  d.current = new RTCPeerConnection(K), d.current.addTransceiver("audio", { direction: "recvonly" });
484
- const c = d.current.addTransceiver("video", { direction: "recvonly" });
484
+ const E = d.current.addTransceiver("video", { direction: "recvonly" });
485
485
  if (RTCRtpReceiver.getCapabilities) {
486
- const r = RTCRtpReceiver.getCapabilities("video");
487
- if (r && r.codecs) {
488
- const p = r.codecs.sort((a, f) => {
486
+ const t = RTCRtpReceiver.getCapabilities("video");
487
+ if (t && t.codecs) {
488
+ const p = t.codecs.sort((a, f) => {
489
489
  const y = (g) => {
490
490
  const M = g.mimeType.toLowerCase();
491
491
  return M.includes("vp9") ? 1 : M.includes("h265") || M.includes("hevc") ? 2 : M.includes("h264") || M.includes("avc") ? 3 : 4;
492
492
  };
493
493
  return y(a) - y(f);
494
494
  });
495
- c.setCodecPreferences(p), T("Set codec preferences:", p.map((a) => a.mimeType).join(", "));
495
+ E.setCodecPreferences(p), T("Set codec preferences:", p.map((a) => a.mimeType).join(", "));
496
496
  }
497
497
  }
498
498
  if (C.current = d.current.createDataChannel("control", {
@@ -500,56 +500,62 @@ const Te = Ce(
500
500
  negotiated: !0,
501
501
  id: 1
502
502
  }), C.current.onopen = () => {
503
- if (O("Control channel opened"), E.current && (E.current.send(JSON.stringify({ type: "requestFrame", sessionId: h })), l))
504
- try {
505
- const r = decodeURIComponent(l);
506
- O("Opening URL"), E.current.send(
507
- JSON.stringify({
508
- type: "openUrl",
509
- url: r,
510
- sessionId: h
511
- })
512
- );
513
- } catch (r) {
514
- console.error({ error: r }, "Error decoding URL, falling back to the original URL"), E.current.send(
515
- JSON.stringify({
516
- type: "openUrl",
517
- url: l,
518
- sessionId: h
519
- })
520
- );
521
- }
503
+ if (O("Control channel opened"), c.current) {
504
+ for (let t = 0; t < 12; t++)
505
+ setTimeout(() => {
506
+ c.current && c.current.send(JSON.stringify({ type: "requestFrame", sessionId: h }));
507
+ }, t * 125);
508
+ if (l)
509
+ try {
510
+ const t = decodeURIComponent(l);
511
+ O("Opening URL"), c.current.send(
512
+ JSON.stringify({
513
+ type: "openUrl",
514
+ url: t,
515
+ sessionId: h
516
+ })
517
+ );
518
+ } catch (t) {
519
+ console.error({ error: t }, "Error decoding URL, falling back to the original URL"), c.current.send(
520
+ JSON.stringify({
521
+ type: "openUrl",
522
+ url: l,
523
+ sessionId: h
524
+ })
525
+ );
526
+ }
527
+ }
522
528
  }, C.current.onclose = () => {
523
529
  O("Control channel closed");
524
- }, C.current.onerror = (r) => {
525
- console.error("Control channel error:", r), O("Control channel error: " + r);
530
+ }, C.current.onerror = (t) => {
531
+ console.error("Control channel error:", t), O("Control channel error: " + t);
526
532
  }, d.current.onconnectionstatechange = () => {
527
533
  O("Connection state: " + d.current?.connectionState), _(d.current?.connectionState === "connected");
528
534
  }, d.current.oniceconnectionstatechange = () => {
529
535
  O("ICE state: " + d.current?.iceConnectionState);
530
- }, d.current.ontrack = (r) => {
531
- O("Received remote track: " + r.track.kind), r.track.kind === "video" && o.current && (T(`[${(/* @__PURE__ */ new Date()).toISOString()}] Video track received:`, r.track), o.current.srcObject = r.streams[0]);
532
- }, d.current.onicecandidate = (r) => {
533
- if (r.candidate && E.current) {
534
- const n = {
536
+ }, d.current.ontrack = (t) => {
537
+ O("Received remote track: " + t.track.kind), t.track.kind === "video" && o.current && (T(`[${(/* @__PURE__ */ new Date()).toISOString()}] Video track received:`, t.track), o.current.srcObject = t.streams[0]);
538
+ }, d.current.onicecandidate = (t) => {
539
+ if (t.candidate && c.current) {
540
+ const r = {
535
541
  type: "candidate",
536
- candidate: r.candidate.candidate,
537
- sdpMid: r.candidate.sdpMid,
538
- sdpMLineIndex: r.candidate.sdpMLineIndex,
542
+ candidate: t.candidate.candidate,
543
+ sdpMid: t.candidate.sdpMid,
544
+ sdpMLineIndex: t.candidate.sdpMLineIndex,
539
545
  sessionId: h
540
546
  };
541
- E.current.send(JSON.stringify(n)), O("Sent ICE candidate");
547
+ c.current.send(JSON.stringify(r)), O("Sent ICE candidate");
542
548
  } else
543
549
  O("ICE candidate gathering completed");
544
- }, E.current.onmessage = async (r) => {
545
- let n;
550
+ }, c.current.onmessage = async (t) => {
551
+ let r;
546
552
  try {
547
- n = JSON.parse(r.data);
553
+ r = JSON.parse(t.data);
548
554
  } catch (p) {
549
555
  Y("Error parsing message:", p);
550
556
  return;
551
557
  }
552
- switch (O("Received: " + n.type), n.type) {
558
+ switch (O("Received: " + r.type), r.type) {
553
559
  case "answer":
554
560
  if (!d.current) {
555
561
  O("No peer connection, skipping answer");
@@ -558,7 +564,7 @@ const Te = Ce(
558
564
  await d.current.setRemoteDescription(
559
565
  new RTCSessionDescription({
560
566
  type: "answer",
561
- sdp: n.sdp
567
+ sdp: r.sdp
562
568
  })
563
569
  ), O("Set remote description");
564
570
  break;
@@ -569,58 +575,58 @@ const Te = Ce(
569
575
  }
570
576
  await d.current.addIceCandidate(
571
577
  new RTCIceCandidate({
572
- candidate: n.candidate,
573
- sdpMid: n.sdpMid,
574
- sdpMLineIndex: n.sdpMLineIndex
578
+ candidate: r.candidate,
579
+ sdpMid: r.sdpMid,
580
+ sdpMLineIndex: r.sdpMLineIndex
575
581
  })
576
582
  ), O("Added ICE candidate");
577
583
  break;
578
584
  case "screenshot":
579
- if (typeof n.id != "string" || typeof n.dataUri != "string") {
580
- Y("Received invalid screenshot success message:", n);
585
+ if (typeof r.id != "string" || typeof r.dataUri != "string") {
586
+ Y("Received invalid screenshot success message:", r);
581
587
  break;
582
588
  }
583
- const p = I.current.get(n.id);
589
+ const p = I.current.get(r.id);
584
590
  if (!p) {
585
- Y(`Received screenshot data for unknown or handled id: ${n.id}`);
591
+ Y(`Received screenshot data for unknown or handled id: ${r.id}`);
586
592
  break;
587
593
  }
588
- T(`Received screenshot data for id ${n.id}`), p({ dataUri: n.dataUri }), I.current.delete(n.id), U.current.delete(n.id);
594
+ T(`Received screenshot data for id ${r.id}`), p({ dataUri: r.dataUri }), I.current.delete(r.id), U.current.delete(r.id);
589
595
  break;
590
596
  case "screenshotError":
591
- if (typeof n.id != "string" || typeof n.message != "string") {
592
- Y("Received invalid screenshot error message:", n);
597
+ if (typeof r.id != "string" || typeof r.message != "string") {
598
+ Y("Received invalid screenshot error message:", r);
593
599
  break;
594
600
  }
595
- const a = U.current.get(n.id);
601
+ const a = U.current.get(r.id);
596
602
  if (!a) {
597
- Y(`Received screenshot error for unknown or handled id: ${n.id}`);
603
+ Y(`Received screenshot error for unknown or handled id: ${r.id}`);
598
604
  break;
599
605
  }
600
- Y(`Received screenshot error for id ${n.id}: ${n.message}`), a(new Error(n.message)), I.current.delete(n.id), U.current.delete(n.id);
606
+ Y(`Received screenshot error for id ${r.id}: ${r.message}`), a(new Error(r.message)), I.current.delete(r.id), U.current.delete(r.id);
601
607
  break;
602
608
  default:
603
- Y(`Received unhandled message type: ${n.type}`, n);
609
+ Y(`Received unhandled message type: ${r.type}`, r);
604
610
  break;
605
611
  }
606
612
  }, d.current) {
607
- const r = await d.current.createOffer({
613
+ const t = await d.current.createOffer({
608
614
  offerToReceiveVideo: !0,
609
615
  offerToReceiveAudio: !1
610
616
  });
611
- await d.current.setLocalDescription(r), E.current && E.current.send(
617
+ await d.current.setLocalDescription(t), c.current && c.current.send(
612
618
  JSON.stringify({
613
619
  type: "offer",
614
- sdp: r.sdp,
620
+ sdp: t.sdp,
615
621
  sessionId: h
616
622
  })
617
623
  ), O("Sent offer");
618
624
  }
619
- } catch (t) {
620
- O("Error: " + t);
625
+ } catch (n) {
626
+ O("Error: " + n);
621
627
  }
622
628
  }, ie = () => {
623
- E.current && (E.current.close(), E.current = null), d.current && (d.current.close(), d.current = null), o.current && (o.current.srcObject = null), C.current && (C.current.close(), C.current = null), _(!1), O("Stopped");
629
+ c.current && (c.current.close(), c.current = null), d.current && (d.current.close(), d.current = null), o.current && (o.current.srcObject = null), C.current && (C.current.close(), C.current = null), _(!1), O("Stopped");
624
630
  };
625
631
  Ke(() => (Ee(), document.hidden || j(), document.addEventListener("visibilitychange", z), () => {
626
632
  Z(), ie(), document.removeEventListener("visibilitychange", z);
@@ -629,14 +635,14 @@ const Te = Ce(
629
635
  o.current && o.current.focus();
630
636
  };
631
637
  return le(A, () => ({
632
- openUrl: (t) => {
633
- if (!E.current || E.current.readyState !== WebSocket.OPEN) {
638
+ openUrl: (n) => {
639
+ if (!c.current || c.current.readyState !== WebSocket.OPEN) {
634
640
  Y("WebSocket not open, cannot send open_url command via ref.");
635
641
  return;
636
642
  }
637
643
  try {
638
- const K = decodeURIComponent(t);
639
- O("Opening URL"), E.current.send(
644
+ const K = decodeURIComponent(n);
645
+ O("Opening URL"), c.current.send(
640
646
  JSON.stringify({
641
647
  type: "openUrl",
642
648
  url: K,
@@ -644,57 +650,57 @@ const Te = Ce(
644
650
  })
645
651
  );
646
652
  } catch (K) {
647
- Y("Error decoding or sending URL via ref:", { error: K, url: t }), E.current.send(
653
+ Y("Error decoding or sending URL via ref:", { error: K, url: n }), c.current.send(
648
654
  JSON.stringify({
649
655
  type: "openUrl",
650
- url: t,
656
+ url: n,
651
657
  sessionId: h
652
658
  })
653
659
  );
654
660
  }
655
661
  },
656
- sendKeyEvent: (t) => {
662
+ sendKeyEvent: (n) => {
657
663
  if (!C.current || C.current.readyState !== "open") {
658
664
  Y("Data channel not ready for imperative key command:", C.current?.readyState);
659
665
  return;
660
666
  }
661
- const K = oe[t.code];
667
+ const K = oe[n.code];
662
668
  if (!K) {
663
- Y(`Unknown event.code for imperative command: ${t.code}`);
669
+ Y(`Unknown event.code for imperative command: ${n.code}`);
664
670
  return;
665
671
  }
666
- let c = e.META_NONE;
667
- t.shiftKey && (c |= e.META_SHIFT_ON), t.altKey && (c |= e.META_ALT_ON), t.ctrlKey && (c |= e.META_CTRL_ON), t.metaKey && (c |= e.META_META_ON);
668
- const r = t.type === "keydown" ? e.ACTION_DOWN : e.ACTION_UP;
672
+ let E = e.META_NONE;
673
+ n.shiftKey && (E |= e.META_SHIFT_ON), n.altKey && (E |= e.META_ALT_ON), n.ctrlKey && (E |= e.META_CTRL_ON), n.metaKey && (E |= e.META_META_ON);
674
+ const t = n.type === "keydown" ? e.ACTION_DOWN : e.ACTION_UP;
669
675
  T(
670
- `Sending Imperative Key Command: code=${t.code}, keycode=${K}, action=${r}, meta=${c}`
676
+ `Sending Imperative Key Command: code=${n.code}, keycode=${K}, action=${t}, meta=${E}`
671
677
  );
672
- const n = B(
673
- r,
678
+ const r = B(
679
+ t,
674
680
  K,
675
681
  0,
676
682
  // repeat count, typically 0 for single presses
677
- c
683
+ E
678
684
  );
679
- n && L(n);
685
+ r && L(r);
680
686
  },
681
- screenshot: () => new Promise((t, K) => {
682
- if (!E.current || E.current.readyState !== WebSocket.OPEN)
687
+ screenshot: () => new Promise((n, K) => {
688
+ if (!c.current || c.current.readyState !== WebSocket.OPEN)
683
689
  return Y("WebSocket not open, cannot send screenshot command."), K(new Error("WebSocket is not connected or connection is not open."));
684
- const c = `ui-ss-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`, r = {
690
+ const E = `ui-ss-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`, t = {
685
691
  type: "screenshot",
686
692
  // Matches the type expected by instance API
687
- id: c
693
+ id: E
688
694
  };
689
- I.current.set(c, t), U.current.set(c, K), T("Sending screenshot request:", r);
695
+ I.current.set(E, n), U.current.set(E, K), T("Sending screenshot request:", t);
690
696
  try {
691
- E.current.send(JSON.stringify(r));
692
- } catch (n) {
693
- Y("Failed to send screenshot request immediately:", n), I.current.delete(c), U.current.delete(c), K(n);
697
+ c.current.send(JSON.stringify(t));
698
+ } catch (r) {
699
+ Y("Failed to send screenshot request immediately:", r), I.current.delete(E), U.current.delete(E), K(r);
694
700
  return;
695
701
  }
696
702
  setTimeout(() => {
697
- I.current.has(c) && (Y(`Screenshot request timed out for id ${c}`), U.current.get(c)?.(new Error("Screenshot request timed out")), I.current.delete(c), U.current.delete(c));
703
+ I.current.has(E) && (Y(`Screenshot request timed out for id ${E}`), U.current.get(E)?.(new Error("Screenshot request timed out")), I.current.delete(E), U.current.delete(E));
698
704
  }, 3e4);
699
705
  })
700
706
  })), /* @__PURE__ */ ne(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@limrun/ui",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -452,13 +452,13 @@ export const RemoteControl = forwardRef<RemoteControlHandle, RemoteControlProps>
452
452
  await new Promise((resolve, reject) => {
453
453
  if (wsRef.current) {
454
454
  wsRef.current.onopen = resolve;
455
- setTimeout(() => reject(new Error('WebSocket connection timeout')), 5000);
455
+ setTimeout(() => reject(new Error('WebSocket connection timeout')), 30000);
456
456
  }
457
457
  });
458
458
 
459
459
  // Request RTCConfiguration
460
460
  const rtcConfigPromise = new Promise<RTCConfiguration>((resolve, reject) => {
461
- const timeoutId = setTimeout(() => reject(new Error('RTCConfiguration timeout')), 5000);
461
+ const timeoutId = setTimeout(() => reject(new Error('RTCConfiguration timeout')), 30000);
462
462
 
463
463
  const messageHandler = (event: MessageEvent) => {
464
464
  try {
@@ -523,7 +523,13 @@ export const RemoteControl = forwardRef<RemoteControlHandle, RemoteControlProps>
523
523
  updateStatus('Control channel opened');
524
524
  // Request first frame once we're ready to receive video
525
525
  if (wsRef.current) {
526
- wsRef.current.send(JSON.stringify({ type: 'requestFrame', sessionId: sessionId }));
526
+ for (let i = 0; i < 12; i++) {
527
+ setTimeout(() => {
528
+ if (wsRef.current) {
529
+ wsRef.current.send(JSON.stringify({ type: 'requestFrame', sessionId: sessionId }));
530
+ }
531
+ }, i * 125); // 125ms = quarter second
532
+ }
527
533
 
528
534
  // Send openUrl message if the prop is provided
529
535
  if (openUrl) {