'use client' import { useEffect, useRef, useState } from 'react' import { Send, Trash2 } from 'lucide-react' interface ConsoleViewerProps { serverId: string readOnly?: boolean } export default function ConsoleViewer({ serverId, readOnly = false }: ConsoleViewerProps) { const [lines, setLines] = useState([]) const [command, setCommand] = useState('') const [sending, setSending] = useState(false) const containerRef = useRef(null) const inputRef = useRef(null) // Auto-scroll to bottom on new lines useEffect(() => { if (containerRef.current) { containerRef.current.scrollTop = containerRef.current.scrollHeight } }, [lines]) // SSE log streaming useEffect(() => { const eventSource = new EventSource(`/api/servers/${serverId}/console`) eventSource.onmessage = (event) => { try { const data = JSON.parse(event.data) const line = data.line || event.data setLines(prev => [...prev.slice(-500), line]) } catch { setLines(prev => [...prev.slice(-500), event.data]) } } eventSource.onerror = () => { eventSource.close() } return () => { eventSource.close() } }, [serverId]) const handleSendCommand = async () => { if (!command.trim() || sending) return const cmd = command.trim() // Show the command in the console setLines(prev => [...prev.slice(-500), `> ${cmd}`]) setCommand('') inputRef.current?.focus() setSending(true) try { const res = await fetch(`/api/servers/${serverId}/console`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ command: cmd }), }) if (!res.ok) { const data = await res.json() setLines(prev => [...prev.slice(-500), `§c Error: ${data.error || 'Command failed'}`]) } // Output arrives via SSE log stream — no need to parse response } catch (error) { console.error('Failed to send command:', error) setLines(prev => [...prev.slice(-500), '§c Error: Failed to send command']) } finally { setSending(false) } } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.preventDefault() handleSendCommand() } } return (
{/* Console Output */}
{lines.length === 0 ? (

Waiting for server output...

) : ( lines.map((line, i) => (
{line}
)) )}
{/* Command Input */} {!readOnly && (
> setCommand(e.target.value)} onKeyDown={handleKeyDown} placeholder="Type a command..." className="flex-1 bg-transparent text-gray-100 font-mono text-sm placeholder-gray-600 focus:outline-none" />
)}
) }