RYP / src /components /Compiler /TerminalPanel.tsx
Soumya79's picture
Upload 1361 files
f91a684 verified
import { useRef, useEffect, useState, type KeyboardEvent } from 'react';
interface TerminalLine {
type: 'prompt' | 'stdout' | 'stderr' | 'info' | 'dim';
text: string;
}
interface TerminalPanelProps {
lines: TerminalLine[];
onCommand: (cmd: string) => void;
isRunning: boolean;
onSendInput: (text: string) => void;
}
export type { TerminalLine };
export default function TerminalPanel({ lines, onCommand, isRunning, onSendInput }: TerminalPanelProps) {
const [commandInput, setCommandInput] = useState('');
const [activeTab, setActiveTab] = useState<'terminal' | 'problems' | 'output'>('terminal');
const scrollRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (scrollRef.current) {
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
}
}, [lines, commandInput, isRunning]);
useEffect(() => {
if (activeTab === 'terminal' && inputRef.current) {
inputRef.current.focus();
}
}, [isRunning, activeTab, lines]);
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
if (isRunning) {
onSendInput(commandInput);
} else if (commandInput.trim()) {
onCommand(commandInput.trim());
}
setCommandInput('');
}
};
return (
<div className="flex flex-col h-full" style={{ background: '#000000' }}>
{/* Tab bar */}
<div
className="flex items-center justify-between px-3"
style={{ borderBottom: '1px solid #222233', background: '#000000', minHeight: 36 }}
>
<div className="flex items-center gap-1">
{(['terminal', 'problems', 'output'] as const).map((tab) => (
<button
key={tab}
onClick={() => setActiveTab(tab)}
className="px-3 py-1.5 text-xs font-bold uppercase tracking-wide transition-colors rounded"
style={{
color: activeTab === tab ? '#e2e2f0' : '#55556a',
background: activeTab === tab ? '#111111' : 'transparent',
border: 'none',
cursor: 'pointer',
}}
>
{tab}
</button>
))}
</div>
<button
onClick={() => onCommand('clear')}
className="text-xs font-semibold transition-colors"
style={{ color: '#55556a', background: 'none', border: 'none', cursor: 'pointer' }}
onMouseEnter={(e) => (e.currentTarget.style.color = '#e2e2f0')}
onMouseLeave={(e) => (e.currentTarget.style.color = '#55556a')}
>
Clear
</button>
</div>
{/* Terminal body — unified output + inline input */}
<div
ref={scrollRef}
onClick={() => inputRef.current?.focus()}
className={`flex-1 overflow-auto min-h-0 p-3 ryp-compiler-scroll ryp-terminal ${activeTab !== 'terminal' ? 'hidden' : ''}`}
>
{lines.map((line, i) => (
<div key={i} className="flex gap-0 whitespace-pre-wrap" style={{ minHeight: '1.7em' }}>
{line.type === 'prompt' && <span className="ryp-terminal__prompt">{line.text}</span>}
{line.type === 'stdout' && <span className="ryp-terminal__stdout">{line.text}</span>}
{line.type === 'stderr' && <span className="ryp-terminal__stderr">{line.text}</span>}
{line.type === 'info' && <span className="ryp-terminal__info">{line.text}</span>}
{line.type === 'dim' && <span className="ryp-terminal__dim">{line.text}</span>}
</div>
))}
{/* Inline input line — last line of the terminal, scrolls with output */}
<div className="flex items-center whitespace-pre-wrap" style={{ minHeight: '1.7em' }}>
{!isRunning && <span className="ryp-terminal__prompt mr-1">~/ryp $</span>}
<input
ref={inputRef}
value={commandInput}
onChange={(e) => setCommandInput(e.target.value)}
onKeyDown={handleKeyDown}
className="flex-1 bg-transparent border-none outline-none p-0 m-0 font-mono text-sm"
style={{
color: '#e2e2f0',
caretColor: '#e2e2f0',
fontFamily: "'JetBrains Mono', 'Fira Code', Consolas, 'Courier New', monospace",
}}
spellCheck={false}
autoComplete="off"
autoFocus
/>
</div>
</div>
{/* Problems Tab Content */}
<div className={`flex-1 flex items-center justify-center text-[#55556a] text-xs ${activeTab !== 'problems' ? 'hidden' : ''}`}>
No problems detected.
</div>
{/* Output Tab Content */}
<div className={`flex-1 flex items-center justify-center text-[#55556a] text-xs ${activeTab !== 'output' ? 'hidden' : ''}`}>
Execution output will appear here.
</div>
</div>
);
}