Spaces:
Sleeping
Sleeping
David Prince
fix: add all missing local module stubs (remote_mcp_registry, mcp_auth, mcp_transport, etc)
cce8120 | 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> | |
| ); | |
| } | |