@@ -630,9 +630,9 @@ function onShareLeft() {
630630}
631631
632632function onShareStopped() {
633- // Host stopped sharing: close relay terminal WebSockets
634- for (const ws of relayTerminalWs .values ()) ws .close ();
635- relayTerminalWs .clear ();
633+ // Host stopped sharing: close all relay terminals
634+ for (const handle of relayTerminals .values ()) handle .close ();
635+ relayTerminals .clear ();
636636}
637637
638638async function autoJoinShare(sid : string ) {
@@ -651,71 +651,114 @@ async function autoJoinShare(sid: string) {
651651
652652// Register callbacks for share lifecycle + relay terminal handling
653653import { setOnShareClosed , setOnRelayTerminalCreate , setOnRelayTerminalInput , setOnRelayTerminalResize , setOnRelayTerminalClose } from ' ~/composables/useShare' ;
654- // Map of relay terminalId → local WebSocket (desktop host bridges guest terminals to local PTY)
655- const relayTerminalWs = new Map <string , WebSocket >();
654+
655+ // Unified relay terminal handle — either IPC (Electron) or WebSocket (web/SSH)
656+ interface RelayTerminalHandle {
657+ sendInput(data : string ): void ;
658+ sendResize(cols : number , rows : number ): void ;
659+ close(): void ;
660+ }
661+ const relayTerminals = new Map <string , RelayTerminalHandle >();
656662
657663if (import .meta .client ) {
658664 setOnShareClosed (() => {
659665 resetToFolderSelector ();
660666 });
661667
662- // Desktop host relay: guest creates a terminal → spawn local terminal, pipe I/O through relay WS
668+ // Desktop host relay: guest creates a terminal → spawn local terminal, pipe I/O back through relay WS
663669 setOnRelayTerminalCreate ((msg : any ) => {
664670 const { terminalId, cwd, cols, rows } = msg ;
665671 const { getLocalWsUrl, getSessionId } = useApi ();
666672 const { sendRelayMessage } = useShare ();
667- // Connect to the local terminal WS (bypasses share routing to avoid loop)
668- const localWs = new WebSocket (getLocalWsUrl ());
669- relayTerminalWs .set (terminalId , localWs );
670-
671- localWs .onopen = () => {
672- localWs .send (JSON .stringify ({
673- type: " create" ,
674- cwd: cwd || rootPath .value ,
673+ const electronTerminal = (window as any ).electronTerminal as {
674+ create: (opts : any ) => Promise <{ ok: boolean ; error? : string }>;
675+ write: (id : string , data : string ) => void ;
676+ resize: (id : string , cols : number , rows : number ) => void ;
677+ kill: (id : string ) => void ;
678+ onData: (cb : (p : { id: string ; data: string }) => void ) => () => void ;
679+ onExit: (cb : (p : { id: string ; code: number }) => void ) => () => void ;
680+ } | undefined ;
681+
682+ if (electronTerminal ) {
683+ // Electron: use IPC to main process (node-pty runs there, not in Nuxt subprocess)
684+ const ipcId = ` relay-${terminalId } ` ;
685+ const offData = electronTerminal .onData (({ id , data }: { id: string ; data: string }) => {
686+ if (id === ipcId ) sendRelayMessage ({ type: " terminal-output" , terminalId , data });
687+ });
688+ const offExit = electronTerminal .onExit (({ id , code }: { id: string ; code: number }) => {
689+ if (id !== ipcId ) return ;
690+ sendRelayMessage ({ type: " terminal-exit" , terminalId , code });
691+ offData ();
692+ offExit ();
693+ relayTerminals .delete (terminalId );
694+ });
695+ electronTerminal .create ({
696+ id: ipcId ,
675697 cols: cols || 80 ,
676698 rows: rows || 24 ,
677- sessionId: getSessionId (),
678- }));
679- };
680-
681- localWs .onmessage = (ev ) => {
682- try {
683- const data = JSON .parse (ev .data );
684- if (data .type === " output" ) {
685- sendRelayMessage ({ type: " terminal-output" , terminalId , data: data .data });
686- } else if (data .type === " exit" ) {
687- sendRelayMessage ({ type: " terminal-exit" , terminalId , code: data .code ?? 0 });
688- localWs .close ();
689- relayTerminalWs .delete (terminalId );
699+ cwd: cwd || rootPath .value ,
700+ }).then ((result : { ok: boolean ; error? : string }) => {
701+ if (! result .ok ) {
702+ sendRelayMessage ({ type: " terminal-output" , terminalId , data: ` \r\n\x1b [31m[Terminal error: ${result .error }]\x1b [0m\r\n ` });
703+ offData ();
704+ offExit ();
705+ relayTerminals .delete (terminalId );
690706 }
691- } catch {}
692- };
693-
694- localWs .onclose = () => {
695- relayTerminalWs .delete (terminalId );
696- };
707+ });
708+ relayTerminals .set (terminalId , {
709+ sendInput(data ) { electronTerminal .write (ipcId , data ); },
710+ sendResize(c , r ) { electronTerminal .resize (ipcId , c , r ); },
711+ close() { electronTerminal .kill (ipcId ); offData (); offExit (); relayTerminals .delete (terminalId ); },
712+ });
713+ } else {
714+ // Web/SSH: use WebSocket to local terminal server
715+ const localWs = new WebSocket (getLocalWsUrl ());
716+ localWs .onopen = () => {
717+ localWs .send (JSON .stringify ({
718+ type: " create" ,
719+ cwd: cwd || rootPath .value ,
720+ cols: cols || 80 ,
721+ rows: rows || 24 ,
722+ sessionId: getSessionId (),
723+ }));
724+ };
725+ localWs .onmessage = (ev ) => {
726+ try {
727+ const data = JSON .parse (ev .data );
728+ if (data .type === " output" ) {
729+ sendRelayMessage ({ type: " terminal-output" , terminalId , data: data .data });
730+ } else if (data .type === " exit" ) {
731+ sendRelayMessage ({ type: " terminal-exit" , terminalId , code: data .code ?? 0 });
732+ localWs .close ();
733+ relayTerminals .delete (terminalId );
734+ }
735+ } catch {}
736+ };
737+ localWs .onclose = () => { relayTerminals .delete (terminalId ); };
738+ relayTerminals .set (terminalId , {
739+ sendInput(data ) {
740+ if (localWs .readyState === WebSocket .OPEN )
741+ localWs .send (JSON .stringify ({ type: " input" , data }));
742+ },
743+ sendResize(c , r ) {
744+ if (localWs .readyState === WebSocket .OPEN )
745+ localWs .send (JSON .stringify ({ type: " resize" , cols: c , rows: r }));
746+ },
747+ close() { localWs .close (); relayTerminals .delete (terminalId ); },
748+ });
749+ }
697750 });
698751
699752 setOnRelayTerminalInput ((msg : any ) => {
700- const ws = relayTerminalWs .get (msg .terminalId );
701- if (ws && ws .readyState === WebSocket .OPEN ) {
702- ws .send (JSON .stringify ({ type: " input" , data: msg .data }));
703- }
753+ relayTerminals .get (msg .terminalId )?.sendInput (msg .data );
704754 });
705755
706756 setOnRelayTerminalResize ((msg : any ) => {
707- const ws = relayTerminalWs .get (msg .terminalId );
708- if (ws && ws .readyState === WebSocket .OPEN ) {
709- ws .send (JSON .stringify ({ type: " resize" , cols: msg .cols , rows: msg .rows }));
710- }
757+ relayTerminals .get (msg .terminalId )?.sendResize (msg .cols , msg .rows );
711758 });
712759
713760 setOnRelayTerminalClose ((msg : any ) => {
714- const ws = relayTerminalWs .get (msg .terminalId );
715- if (ws ) {
716- ws .close ();
717- relayTerminalWs .delete (msg .terminalId );
718- }
761+ relayTerminals .get (msg .terminalId )?.close ();
719762 });
720763}
721764
0 commit comments