Files
professor/components/editor/OutputPanel.tsx
Aodhan Collins f644937604 Initial commit — AI-powered coding tutor (Professor)
Next.js 16, React 19, Monaco editor, Anthropic SDK, multi-provider AI,
Wandbox Python execution, iframe HTML preview, SQLite auth + session persistence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 21:48:34 +00:00

79 lines
2.5 KiB
TypeScript

'use client';
import { useState } from 'react';
import type { ExecutionResult } from '@/types';
interface Props {
result: ExecutionResult | null;
isLoading: boolean;
}
export default function OutputPanel({ result, isLoading }: Props) {
const [collapsed, setCollapsed] = useState(false);
if (!isLoading && !result) return null;
const hasError = result?.error || (result?.stderr && result.stderr.trim());
const hasOutput = result?.stdout && result.stdout.trim();
return (
<div className="border-t border-zinc-700 bg-zinc-900 text-sm font-mono">
{/* Header bar */}
<button
onClick={() => setCollapsed((c) => !c)}
className="flex w-full items-center gap-2 px-4 py-2 text-xs text-zinc-400 hover:text-zinc-200 transition-colors"
>
<span>{collapsed ? '▶' : '▼'}</span>
<span className="font-sans font-medium uppercase tracking-wider">Output</span>
{result && !isLoading && (
<span
className={`ml-auto rounded px-1.5 py-0.5 text-xs font-sans ${
result.exitCode === 0 && !result.error
? 'bg-green-900/50 text-green-400'
: 'bg-red-900/50 text-red-400'
}`}
>
{result.timedOut
? 'timed out'
: result.error
? 'error'
: `exit ${result.exitCode}`}
</span>
)}
</button>
{/* Content */}
{!collapsed && (
<div className="max-h-48 overflow-y-auto px-4 pb-4 space-y-2">
{isLoading && (
<div className="flex items-center gap-2 text-zinc-500">
<div className="h-3 w-3 animate-spin rounded-full border border-zinc-600 border-t-blue-400" />
<span>Running</span>
</div>
)}
{result?.error && (
<pre className="whitespace-pre-wrap text-red-400">{result.error}</pre>
)}
{result?.timedOut && !result.error && (
<p className="text-yellow-400">Execution timed out after 15 seconds.</p>
)}
{hasOutput && (
<pre className="whitespace-pre-wrap text-zinc-200">{result!.stdout}</pre>
)}
{hasError && !result?.error && (
<pre className="whitespace-pre-wrap text-red-400">{result!.stderr}</pre>
)}
{result && !isLoading && !result.error && !hasOutput && !hasError && (
<span className="text-zinc-500 italic">No output</span>
)}
</div>
)}
</div>
);
}