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>
  );
}