@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 +1 -1
- package/dist/index.js +155 -149
- package/package.json +1 -1
- package/src/components/remote-control.tsx +9 -3
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,
|
|
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(_,
|
|
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
|
|
286
|
-
return l && (
|
|
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),
|
|
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 = (
|
|
294
|
-
T(
|
|
295
|
-
}, L = (
|
|
296
|
-
!C.current || C.current.readyState !== "open" || C.current.send(
|
|
297
|
-
}, S = (
|
|
298
|
-
if (
|
|
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,
|
|
301
|
-
if (!
|
|
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 =
|
|
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 -
|
|
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(
|
|
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
|
|
339
|
-
const a =
|
|
338
|
+
if ("touches" in n) {
|
|
339
|
+
const a = n.changedTouches;
|
|
340
340
|
let f;
|
|
341
|
-
switch (
|
|
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 (
|
|
363
|
+
switch (n.type) {
|
|
364
364
|
case "mousedown":
|
|
365
|
-
|
|
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
|
-
|
|
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,
|
|
377
|
+
f && p(-1, n.clientX, n.clientY, f);
|
|
378
378
|
}
|
|
379
|
-
}, X = (
|
|
380
|
-
if (
|
|
381
|
-
type:
|
|
382
|
-
key:
|
|
383
|
-
keyCode:
|
|
384
|
-
code:
|
|
385
|
-
target:
|
|
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 (
|
|
396
|
-
if (
|
|
397
|
-
T("Paste shortcut detected"), navigator.clipboard.readText().then((
|
|
398
|
-
if (
|
|
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
|
-
|
|
401
|
+
E.substring(0, 20) + (E.length > 20 ? "..." : "")
|
|
402
402
|
);
|
|
403
|
-
const
|
|
404
|
-
L(
|
|
403
|
+
const t = Ae(E, !0);
|
|
404
|
+
L(t);
|
|
405
405
|
}
|
|
406
|
-
}).catch((
|
|
407
|
-
console.error("Failed to read clipboard contents: ",
|
|
406
|
+
}).catch((E) => {
|
|
407
|
+
console.error("Failed to read clipboard contents: ", E);
|
|
408
408
|
});
|
|
409
409
|
return;
|
|
410
410
|
}
|
|
411
|
-
if (
|
|
411
|
+
if (n.key.toLowerCase() === "m" && (n.metaKey || n.ctrlKey)) {
|
|
412
412
|
T("Menu shortcut detected");
|
|
413
|
-
const
|
|
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(
|
|
421
|
-
const
|
|
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(
|
|
427
|
+
L(t);
|
|
428
428
|
return;
|
|
429
429
|
}
|
|
430
430
|
}
|
|
431
|
-
const K = pe(
|
|
431
|
+
const K = pe(n);
|
|
432
432
|
if (K) {
|
|
433
|
-
const { keycode:
|
|
434
|
-
T(`Sending Keycode: key=${
|
|
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
|
-
|
|
437
|
-
|
|
436
|
+
r,
|
|
437
|
+
E,
|
|
438
438
|
0,
|
|
439
439
|
// repeat count, typically 0 for single presses
|
|
440
|
-
|
|
440
|
+
t
|
|
441
441
|
);
|
|
442
442
|
L(p);
|
|
443
443
|
} else
|
|
444
|
-
T(`Ignoring unhandled key event: type=${
|
|
444
|
+
T(`Ignoring unhandled key event: type=${n.type}, key=${n.key}`);
|
|
445
445
|
}, ce = () => {
|
|
446
|
-
|
|
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
|
-
|
|
461
|
-
O("WebSocket error: " +
|
|
462
|
-
},
|
|
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((
|
|
465
|
-
|
|
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((
|
|
468
|
-
const p = setTimeout(() =>
|
|
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),
|
|
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),
|
|
473
|
+
console.error("Error handling RTC configuration:", y), r(y);
|
|
474
474
|
}
|
|
475
475
|
};
|
|
476
|
-
|
|
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
|
|
484
|
+
const E = d.current.addTransceiver("video", { direction: "recvonly" });
|
|
485
485
|
if (RTCRtpReceiver.getCapabilities) {
|
|
486
|
-
const
|
|
487
|
-
if (
|
|
488
|
-
const p =
|
|
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
|
-
|
|
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"),
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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 = (
|
|
525
|
-
console.error("Control channel error:",
|
|
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 = (
|
|
531
|
-
O("Received remote track: " +
|
|
532
|
-
}, d.current.onicecandidate = (
|
|
533
|
-
if (
|
|
534
|
-
const
|
|
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:
|
|
537
|
-
sdpMid:
|
|
538
|
-
sdpMLineIndex:
|
|
542
|
+
candidate: t.candidate.candidate,
|
|
543
|
+
sdpMid: t.candidate.sdpMid,
|
|
544
|
+
sdpMLineIndex: t.candidate.sdpMLineIndex,
|
|
539
545
|
sessionId: h
|
|
540
546
|
};
|
|
541
|
-
|
|
547
|
+
c.current.send(JSON.stringify(r)), O("Sent ICE candidate");
|
|
542
548
|
} else
|
|
543
549
|
O("ICE candidate gathering completed");
|
|
544
|
-
},
|
|
545
|
-
let
|
|
550
|
+
}, c.current.onmessage = async (t) => {
|
|
551
|
+
let r;
|
|
546
552
|
try {
|
|
547
|
-
|
|
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: " +
|
|
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:
|
|
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:
|
|
573
|
-
sdpMid:
|
|
574
|
-
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
|
|
580
|
-
Y("Received invalid screenshot success message:",
|
|
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(
|
|
589
|
+
const p = I.current.get(r.id);
|
|
584
590
|
if (!p) {
|
|
585
|
-
Y(`Received screenshot data for unknown or handled 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 ${
|
|
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
|
|
592
|
-
Y("Received invalid screenshot error message:",
|
|
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(
|
|
601
|
+
const a = U.current.get(r.id);
|
|
596
602
|
if (!a) {
|
|
597
|
-
Y(`Received screenshot error for unknown or handled 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 ${
|
|
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: ${
|
|
609
|
+
Y(`Received unhandled message type: ${r.type}`, r);
|
|
604
610
|
break;
|
|
605
611
|
}
|
|
606
612
|
}, d.current) {
|
|
607
|
-
const
|
|
613
|
+
const t = await d.current.createOffer({
|
|
608
614
|
offerToReceiveVideo: !0,
|
|
609
615
|
offerToReceiveAudio: !1
|
|
610
616
|
});
|
|
611
|
-
await d.current.setLocalDescription(
|
|
617
|
+
await d.current.setLocalDescription(t), c.current && c.current.send(
|
|
612
618
|
JSON.stringify({
|
|
613
619
|
type: "offer",
|
|
614
|
-
sdp:
|
|
620
|
+
sdp: t.sdp,
|
|
615
621
|
sessionId: h
|
|
616
622
|
})
|
|
617
623
|
), O("Sent offer");
|
|
618
624
|
}
|
|
619
|
-
} catch (
|
|
620
|
-
O("Error: " +
|
|
625
|
+
} catch (n) {
|
|
626
|
+
O("Error: " + n);
|
|
621
627
|
}
|
|
622
628
|
}, ie = () => {
|
|
623
|
-
|
|
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: (
|
|
633
|
-
if (!
|
|
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(
|
|
639
|
-
O("Opening URL"),
|
|
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:
|
|
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:
|
|
656
|
+
url: n,
|
|
651
657
|
sessionId: h
|
|
652
658
|
})
|
|
653
659
|
);
|
|
654
660
|
}
|
|
655
661
|
},
|
|
656
|
-
sendKeyEvent: (
|
|
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[
|
|
667
|
+
const K = oe[n.code];
|
|
662
668
|
if (!K) {
|
|
663
|
-
Y(`Unknown event.code for imperative command: ${
|
|
669
|
+
Y(`Unknown event.code for imperative command: ${n.code}`);
|
|
664
670
|
return;
|
|
665
671
|
}
|
|
666
|
-
let
|
|
667
|
-
|
|
668
|
-
const
|
|
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=${
|
|
676
|
+
`Sending Imperative Key Command: code=${n.code}, keycode=${K}, action=${t}, meta=${E}`
|
|
671
677
|
);
|
|
672
|
-
const
|
|
673
|
-
|
|
678
|
+
const r = B(
|
|
679
|
+
t,
|
|
674
680
|
K,
|
|
675
681
|
0,
|
|
676
682
|
// repeat count, typically 0 for single presses
|
|
677
|
-
|
|
683
|
+
E
|
|
678
684
|
);
|
|
679
|
-
|
|
685
|
+
r && L(r);
|
|
680
686
|
},
|
|
681
|
-
screenshot: () => new Promise((
|
|
682
|
-
if (!
|
|
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
|
|
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:
|
|
693
|
+
id: E
|
|
688
694
|
};
|
|
689
|
-
I.current.set(
|
|
695
|
+
I.current.set(E, n), U.current.set(E, K), T("Sending screenshot request:", t);
|
|
690
696
|
try {
|
|
691
|
-
|
|
692
|
-
} catch (
|
|
693
|
-
Y("Failed to send screenshot request immediately:",
|
|
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(
|
|
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
|
@@ -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')),
|
|
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')),
|
|
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
|
-
|
|
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) {
|