Spaces:
Sleeping
Sleeping
File size: 3,417 Bytes
cce8120 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | import React, { useState, useRef, useEffect } from 'react';
export default function TerminalPanel() {
const [history, setHistory] = useState([
{ type: 'system', text: '⚡ Interactive Terminal Session Connected' },
{ type: 'system', text: 'Type `help` or execute shell commands directly.' }
]);
const [input, setInput] = useState('');
const [executing, setExecuting] = useState(false);
const terminalEndRef = useRef(null);
useEffect(() => {
terminalEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [history]);
const handleCommandSubmit = async (e) => {
e.preventDefault();
const cmd = input.trim();
if (!cmd || executing) return;
setHistory((prev) => [...prev, { type: 'input', text: `$ ${cmd}` }]);
setInput('');
setExecuting(true);
try {
const response = await fetch('/api/terminal', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ command: cmd })
});
if (!response.ok) {
throw new Error(`HTTP error status: ${response.status}`);
}
const data = await response.json();
const outputText = data.output || data.result || 'Command executed with no output.';
setHistory((prev) => [...prev, { type: 'output', text: outputText }]);
} catch (err) {
setHistory((prev) => [
...prev,
{ type: 'error', text: `Command execution failed: ${err.message}` }
]);
} finally {
setExecuting(false);
}
};
return (
<div className="h-48 bg-slate-950 border-t border-slate-800 flex flex-col font-mono text-xs text-slate-300">
{/* Terminal Bar Header */}
<div className="bg-slate-900 px-3 py-1.5 border-b border-slate-800 flex items-center justify-between text-slate-400 select-none">
<span className="font-semibold text-[11px] uppercase tracking-wide">Terminal</span>
<button
onClick={() => setHistory([])}
className="hover:text-white transition-colors text-[10px]"
>
Clear Output
</button>
</div>
{/* Terminal History Display */}
<div className="flex-1 p-3 overflow-y-auto space-y-1">
{history.map((item, index) => (
<div key={index} className="whitespace-pre-wrap leading-5">
{item.type === 'input' && <span className="text-emerald-400 font-semibold">{item.text}</span>}
{item.type === 'output' && <span className="text-slate-300">{item.text}</span>}
{item.type === 'system' && <span className="text-blue-400 italic">{item.text}</span>}
{item.type === 'error' && <span className="text-rose-400">{item.text}</span>}
</div>
))}
{executing && <div className="text-amber-400 animate-pulse">Executing command...</div>}
<div ref={terminalEndRef} />
</div>
{/* Command Line Input Prompt */}
<form onSubmit={handleCommandSubmit} className="flex items-center px-3 py-1.5 bg-slate-900 border-t border-slate-800">
<span className="text-emerald-400 mr-2 font-bold">$</span>
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
disabled={executing}
placeholder="Run command..."
className="flex-1 bg-transparent text-white outline-none font-mono text-xs"
/>
</form>
</div>
);
}
|