@@ -7,25 +7,145 @@ import EditorFile from "lib/editorFile";
77import  TerminalComponent  from  "./terminal" ; 
88import  "@xterm/xterm/css/xterm.css" ; 
99import  toast  from  "components/toast" ; 
10+ import  helpers  from  "utils/helpers" ; 
11+ 
12+ const  TERMINAL_SESSION_STORAGE_KEY  =  "acodeTerminalSessions" ; 
1013
1114class  TerminalManager  { 
1215	constructor ( )  { 
1316		this . terminals  =  new  Map ( ) ; 
1417		this . terminalCounter  =  0 ; 
1518	} 
1619
20+ 	getPersistedSessions ( )  { 
21+ 		try  { 
22+ 			const  stored  =  helpers . parseJSON ( 
23+ 				localStorage . getItem ( TERMINAL_SESSION_STORAGE_KEY ) , 
24+ 			) ; 
25+ 			if  ( ! Array . isArray ( stored ) )  return  [ ] ; 
26+ 			return  stored 
27+ 				. map ( ( entry )  =>  { 
28+ 					if  ( ! entry )  return  null ; 
29+ 					if  ( typeof  entry  ===  "string" )  { 
30+ 						return  {  pid : entry ,  name : `Terminal ${ entry }  `  } ; 
31+ 					} 
32+ 					if  ( typeof  entry  ===  "object"  &&  entry . pid )  { 
33+ 						const  pid  =  String ( entry . pid ) ; 
34+ 						return  { 
35+ 							pid, 
36+ 							name : entry . name  ||  `Terminal ${ pid }  ` , 
37+ 						} ; 
38+ 					} 
39+ 					return  null ; 
40+ 				} ) 
41+ 				. filter ( Boolean ) ; 
42+ 		}  catch  ( error )  { 
43+ 			console . error ( "Failed to read persisted terminal sessions:" ,  error ) ; 
44+ 			return  [ ] ; 
45+ 		} 
46+ 	} 
47+ 
48+ 	savePersistedSessions ( sessions )  { 
49+ 		try  { 
50+ 			localStorage . setItem ( 
51+ 				TERMINAL_SESSION_STORAGE_KEY , 
52+ 				JSON . stringify ( sessions ) , 
53+ 			) ; 
54+ 		}  catch  ( error )  { 
55+ 			console . error ( "Failed to persist terminal sessions:" ,  error ) ; 
56+ 		} 
57+ 	} 
58+ 
59+ 	persistTerminalSession ( pid ,  name )  { 
60+ 		if  ( ! pid )  return ; 
61+ 
62+ 		const  pidStr  =  String ( pid ) ; 
63+ 		const  sessions  =  this . getPersistedSessions ( ) ; 
64+ 		const  existingIndex  =  sessions . findIndex ( 
65+ 			( session )  =>  session . pid  ===  pidStr , 
66+ 		) ; 
67+ 		const  sessionData  =  { 
68+ 			pid : pidStr , 
69+ 			name : name  ||  `Terminal ${ pidStr }  ` , 
70+ 		} ; 
71+ 
72+ 		if  ( existingIndex  >=  0 )  { 
73+ 			sessions [ existingIndex ]  =  { 
74+ 				...sessions [ existingIndex ] , 
75+ 				...sessionData , 
76+ 			} ; 
77+ 		}  else  { 
78+ 			sessions . push ( sessionData ) ; 
79+ 		} 
80+ 
81+ 		this . savePersistedSessions ( sessions ) ; 
82+ 	} 
83+ 
84+ 	removePersistedSession ( pid )  { 
85+ 		if  ( ! pid )  return ; 
86+ 
87+ 		const  pidStr  =  String ( pid ) ; 
88+ 		const  sessions  =  this . getPersistedSessions ( ) ; 
89+ 		const  nextSessions  =  sessions . filter ( ( session )  =>  session . pid  !==  pidStr ) ; 
90+ 
91+ 		if  ( nextSessions . length  !==  sessions . length )  { 
92+ 			this . savePersistedSessions ( nextSessions ) ; 
93+ 		} 
94+ 	} 
95+ 
96+ 	async  restorePersistedSessions ( )  { 
97+ 		const  sessions  =  this . getPersistedSessions ( ) ; 
98+ 		if  ( ! sessions . length )  return ; 
99+ 
100+ 		const  manager  =  window . editorManager ; 
101+ 		const  activeFileId  =  manager ?. activeFile ?. id ; 
102+ 		const  restoredTerminals  =  [ ] ; 
103+ 
104+ 		for  ( const  session  of  sessions )  { 
105+ 			if  ( ! session ?. pid )  continue ; 
106+ 			if  ( this . terminals . has ( session . pid ) )  continue ; 
107+ 
108+ 			try  { 
109+ 				const  instance  =  await  this . createServerTerminal ( { 
110+ 					pid : session . pid , 
111+ 					name : session . name , 
112+ 					reconnecting : true , 
113+ 					render : false , 
114+ 				} ) ; 
115+ 				if  ( instance )  restoredTerminals . push ( instance ) ; 
116+ 			}  catch  ( error )  { 
117+ 				console . error ( 
118+ 					`Failed to restore terminal session ${ session . pid }  :` , 
119+ 					error , 
120+ 				) ; 
121+ 				this . removePersistedSession ( session . pid ) ; 
122+ 			} 
123+ 		} 
124+ 
125+ 		if  ( activeFileId  &&  manager ?. getFile )  { 
126+ 			const  fileToRestore  =  manager . getFile ( activeFileId ,  "id" ) ; 
127+ 			fileToRestore ?. makeActive ( ) ; 
128+ 		}  else  if  ( ! manager ?. activeFile  &&  restoredTerminals . length )  { 
129+ 			restoredTerminals [ 0 ] ?. file ?. makeActive ( ) ; 
130+ 		} 
131+ 	} 
132+ 
17133	/** 
18134	 * Create a new terminal session 
19135	 * @param  {object } options - Terminal options 
20136	 * @returns  {Promise<object> } Terminal instance info 
21137	 */ 
22138	async  createTerminal ( options  =  { } )  { 
23139		try  { 
140+ 			const  {  render,  serverMode,  ...terminalOptions  }  =  options ; 
141+ 			const  shouldRender  =  render  !==  false ; 
142+ 			const  isServerMode  =  serverMode  !==  false ; 
143+ 
24144			const  terminalId  =  `terminal_${ ++ this . terminalCounter }  ` ; 
25145			const  terminalName  =  options . name  ||  `Terminal ${ this . terminalCounter }  ` ; 
26146
27147			// Check if terminal is installed before proceeding 
28- 			if  ( options . serverMode   !==   false )  { 
148+ 			if  ( isServerMode )  { 
29149				const  installationResult  =  await  this . checkAndInstallTerminal ( ) ; 
30150				if  ( ! installationResult . success )  { 
31151					throw  new  Error ( installationResult . error ) ; 
@@ -34,8 +154,8 @@ class TerminalManager {
34154
35155			// Create terminal component 
36156			const  terminalComponent  =  new  TerminalComponent ( { 
37- 				serverMode : options . serverMode   !==   false , 
38- 				...options , 
157+ 				serverMode : isServerMode , 
158+ 				...terminalOptions , 
39159			} ) ; 
40160
41161			// Create container 
@@ -59,7 +179,7 @@ class TerminalManager {
59179				type : "terminal" , 
60180				content : terminalContainer , 
61181				tabIcon : "licons terminal" , 
62- 				render : true , 
182+ 				render : shouldRender , 
63183			} ) ; 
64184
65185			// Wait for tab creation and setup 
@@ -71,7 +191,7 @@ class TerminalManager {
71191
72192						// Connect to session if in server mode 
73193						if  ( terminalComponent . serverMode )  { 
74- 							await  terminalComponent . connectToSession ( ) ; 
194+ 							await  terminalComponent . connectToSession ( terminalOptions . pid ) ; 
75195						}  else  { 
76196							// For local mode, just write a welcome message 
77197							terminalComponent . write ( 
@@ -98,6 +218,10 @@ class TerminalManager {
98218						} ; 
99219
100220						this . terminals . set ( uniqueId ,  instance ) ; 
221+ 
222+ 						if  ( terminalComponent . serverMode  &&  terminalComponent . pid )  { 
223+ 							this . persistTerminalSession ( terminalComponent . pid ,  terminalName ) ; 
224+ 						} 
101225						resolve ( instance ) ; 
102226					}  catch  ( error )  { 
103227						console . error ( "Failed to initialize terminal:" ,  error ) ; 
@@ -368,6 +492,10 @@ class TerminalManager {
368492				const  formattedTitle  =  `Terminal ${ this . terminalCounter }   - ${ title }  ` ; 
369493				terminalFile . filename  =  formattedTitle ; 
370494
495+ 				if  ( terminalComponent . serverMode  &&  terminalComponent . pid )  { 
496+ 					this . persistTerminalSession ( terminalComponent . pid ,  formattedTitle ) ; 
497+ 				} 
498+ 
371499				// Refresh the header subtitle if this terminal is active 
372500				if  ( 
373501					editorManager . activeFile  && 
@@ -421,6 +549,10 @@ class TerminalManager {
421549		if  ( ! terminal )  return ; 
422550
423551		try  { 
552+ 			if  ( terminal . component . serverMode  &&  terminal . component . pid )  { 
553+ 				this . removePersistedSession ( terminal . component . pid ) ; 
554+ 			} 
555+ 
424556			// Cleanup resize observer 
425557			if  ( terminal . file . _resizeObserver )  { 
426558				terminal . file . _resizeObserver . disconnect ( ) ; 
0 commit comments