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>
This commit is contained in:
Aodhan Collins
2026-03-04 21:48:34 +00:00
commit f644937604
56 changed files with 14012 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
'use client';
import type { Dispatch } from 'react';
import type { AppState, AppAction } from '@/types';
import { useCodeExecution } from '@/hooks/useCodeExecution';
import CodeEditor from './CodeEditor';
import EditorToolbar from './EditorToolbar';
import OutputPanel from './OutputPanel';
import HtmlPreview from './HtmlPreview';
interface Props {
state: AppState;
dispatch: Dispatch<AppAction>;
providerLabel: string;
onSubmit: () => void;
onReset: () => void;
onOpenSettings: () => void;
savedIndicator: boolean;
authUser: { id: string; email: string } | null;
onShowAuth: () => void;
onLogout: () => void;
}
export default function EditorPane({ state, dispatch, providerLabel, onSubmit, onReset, onOpenSettings, savedIndicator, authUser, onShowAuth, onLogout }: Props) {
const { execute } = useCodeExecution(dispatch);
const language = state.topic!.language;
function handleRun() {
execute(language, state.code);
}
return (
<div className="flex flex-col h-full bg-[#1e1e1e]">
<EditorToolbar
language={language}
providerLabel={providerLabel}
isExecuting={state.phase === 'executing'}
isStreaming={state.isStreaming}
onRun={handleRun}
onSubmit={onSubmit}
onReset={onReset}
onOpenSettings={onOpenSettings}
savedIndicator={savedIndicator}
authUser={authUser}
onShowAuth={onShowAuth}
onLogout={onLogout}
responseMode={state.responseMode}
onToggleHintMode={() =>
dispatch({ type: 'SET_RESPONSE_MODE', payload: { hintMode: !state.responseMode.hintMode } })
}
onToggleStrict={() =>
dispatch({ type: 'SET_RESPONSE_MODE', payload: { strict: !state.responseMode.strict } })
}
/>
<div className="flex-1 min-h-0">
<CodeEditor
language={language}
value={state.code}
onChange={(val) => dispatch({ type: 'CODE_CHANGE', payload: val })}
height="100%"
/>
</div>
{language === 'html' && <HtmlPreview code={state.code} />}
<OutputPanel
result={state.executionResult}
isLoading={state.phase === 'executing'}
/>
</div>
);
}