| 
1 | 1 | 'use client';  | 
2 | 2 | 
 
  | 
3 |  | -import { useEffect, useRef, useState } from 'react';  | 
 | 3 | +import { useCallback, useEffect, useRef, useState } from 'react';  | 
4 | 4 | import '@xterm/xterm/css/xterm.css';  | 
5 | 5 | import { Button } from './ui/button';  | 
6 | 6 | import {  Play, Square, Trash2, X } from 'lucide-react';  | 
@@ -34,6 +34,60 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate  | 
34 | 34 | 
 
  | 
35 | 35 |   const scriptName = scriptPath.split('/').pop() ?? scriptPath.split('\\').pop() ?? 'Unknown Script';  | 
36 | 36 | 
 
  | 
 | 37 | +  const handleMessage = useCallback((message: TerminalMessage) => {  | 
 | 38 | +    if (!xtermRef.current) return;  | 
 | 39 | + | 
 | 40 | +    const timestamp = new Date(message.timestamp).toLocaleTimeString();  | 
 | 41 | +    const prefix = `[${timestamp}] `;  | 
 | 42 | +      | 
 | 43 | +    switch (message.type) {  | 
 | 44 | +      case 'start':  | 
 | 45 | +        xtermRef.current.writeln(`${prefix}[START] ${message.data}`);  | 
 | 46 | +        setIsRunning(true);  | 
 | 47 | +        break;  | 
 | 48 | +      case 'output':  | 
 | 49 | +        // Write directly to terminal - xterm.js handles ANSI codes natively  | 
 | 50 | +        xtermRef.current.write(message.data);  | 
 | 51 | +        break;  | 
 | 52 | +      case 'error':  | 
 | 53 | +        // Check if this looks like ANSI terminal output (contains escape codes)  | 
 | 54 | +        if (message.data.includes('\x1B[') || message.data.includes('\u001b[')) {  | 
 | 55 | +          // This is likely terminal output sent to stderr, treat it as normal output  | 
 | 56 | +          xtermRef.current.write(message.data);  | 
 | 57 | +        } else if (message.data.includes('TERM environment variable not set')) {  | 
 | 58 | +          // This is a common warning, treat as normal output  | 
 | 59 | +          xtermRef.current.write(message.data);  | 
 | 60 | +        } else if (message.data.includes('exit code') && message.data.includes('clear')) {  | 
 | 61 | +          // This is a script error, show it with error prefix  | 
 | 62 | +          xtermRef.current.writeln(`${prefix}[ERROR] ${message.data}`);  | 
 | 63 | +        } else {  | 
 | 64 | +          // This is a real error, show it with error prefix  | 
 | 65 | +          xtermRef.current.writeln(`${prefix}[ERROR] ${message.data}`);  | 
 | 66 | +        }  | 
 | 67 | +        break;  | 
 | 68 | +      case 'end':  | 
 | 69 | +        // Check if this is an LXC creation script  | 
 | 70 | +        const isLxcCreation = scriptPath.includes('ct/') ||   | 
 | 71 | +                             scriptPath.includes('create_lxc') ||   | 
 | 72 | +                             (containerId != null) ||  | 
 | 73 | +                             scriptName.includes('lxc') ||  | 
 | 74 | +                             scriptName.includes('container');  | 
 | 75 | +          | 
 | 76 | +        if (isLxcCreation && message.data.includes('SSH script execution finished with code: 0')) {  | 
 | 77 | +          // Display prominent LXC creation completion message  | 
 | 78 | +          xtermRef.current.writeln('');  | 
 | 79 | +          xtermRef.current.writeln('#########################################');  | 
 | 80 | +          xtermRef.current.writeln('########## LXC CREATION FINISHED ########');  | 
 | 81 | +          xtermRef.current.writeln('#########################################');  | 
 | 82 | +          xtermRef.current.writeln('');  | 
 | 83 | +        } else {  | 
 | 84 | +          xtermRef.current.writeln(`${prefix}✅ ${message.data}`);  | 
 | 85 | +        }  | 
 | 86 | +        setIsRunning(false);  | 
 | 87 | +        break;  | 
 | 88 | +    }  | 
 | 89 | +  }, [scriptPath, containerId, scriptName]);  | 
 | 90 | + | 
37 | 91 |   // Ensure we're on the client side  | 
38 | 92 |   useEffect(() => {  | 
39 | 93 |     setIsClient(true);  | 
@@ -206,45 +260,7 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate  | 
206 | 260 |         wsRef.current.close();  | 
207 | 261 |       }  | 
208 | 262 |     };  | 
209 |  | -  }, [scriptPath, executionId, mode, server, isUpdate, containerId]);  | 
210 |  | - | 
211 |  | -  const handleMessage = (message: TerminalMessage) => {  | 
212 |  | -    if (!xtermRef.current) return;  | 
213 |  | - | 
214 |  | -    const timestamp = new Date(message.timestamp).toLocaleTimeString();  | 
215 |  | -    const prefix = `[${timestamp}] `;  | 
216 |  | -      | 
217 |  | -    switch (message.type) {  | 
218 |  | -      case 'start':  | 
219 |  | -        xtermRef.current.writeln(`${prefix}[START] ${message.data}`);  | 
220 |  | -        setIsRunning(true);  | 
221 |  | -        break;  | 
222 |  | -      case 'output':  | 
223 |  | -        // Write directly to terminal - xterm.js handles ANSI codes natively  | 
224 |  | -        xtermRef.current.write(message.data);  | 
225 |  | -        break;  | 
226 |  | -      case 'error':  | 
227 |  | -        // Check if this looks like ANSI terminal output (contains escape codes)  | 
228 |  | -        if (message.data.includes('\x1B[') || message.data.includes('\u001b[')) {  | 
229 |  | -          // This is likely terminal output sent to stderr, treat it as normal output  | 
230 |  | -          xtermRef.current.write(message.data);  | 
231 |  | -        } else if (message.data.includes('TERM environment variable not set')) {  | 
232 |  | -          // This is a common warning, treat as normal output  | 
233 |  | -          xtermRef.current.write(message.data);  | 
234 |  | -        } else if (message.data.includes('exit code') && message.data.includes('clear')) {  | 
235 |  | -          // This is a script error, show it with error prefix  | 
236 |  | -          xtermRef.current.writeln(`${prefix}[ERROR] ${message.data}`);  | 
237 |  | -        } else {  | 
238 |  | -          // This is a real error, show it with error prefix  | 
239 |  | -          xtermRef.current.writeln(`${prefix}[ERROR] ${message.data}`);  | 
240 |  | -        }  | 
241 |  | -        break;  | 
242 |  | -      case 'end':  | 
243 |  | -        xtermRef.current.writeln(`${prefix}[SUCCESS] ${message.data}`);  | 
244 |  | -        setIsRunning(false);  | 
245 |  | -        break;  | 
246 |  | -    }  | 
247 |  | -  };  | 
 | 263 | +  }, [scriptPath, executionId, mode, server, isUpdate, containerId, handleMessage]);  | 
248 | 264 | 
 
  | 
249 | 265 |   const startScript = () => {  | 
250 | 266 |     if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {  | 
 | 
0 commit comments