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>
153 lines
5.4 KiB
TypeScript
153 lines
5.4 KiB
TypeScript
// ─── App Mode ────────────────────────────────────────────────────────────────
|
|
|
|
export type AppMode = 'classroom' | 'homework';
|
|
|
|
// ─── Response Mode ──────────────────────────────────────────────────────────
|
|
|
|
export interface ResponseMode {
|
|
hintMode: boolean; // true = AI proactively suggests hints
|
|
strict: boolean; // true = only the exact required approach is accepted
|
|
}
|
|
|
|
// ─── Languages & Topics ────────────────────────────────────────────────────
|
|
|
|
export type SupportedLanguage = 'python' | 'html';
|
|
|
|
export interface Topic {
|
|
id: string;
|
|
label: string;
|
|
language: SupportedLanguage;
|
|
description: string;
|
|
starterCode: string;
|
|
}
|
|
|
|
// ─── Tasks (AI generated) ──────────────────────────────────────────────────
|
|
|
|
export interface Task {
|
|
title: string;
|
|
description: string;
|
|
hints: string[];
|
|
starterCode: string;
|
|
}
|
|
|
|
// ─── Messages ──────────────────────────────────────────────────────────────
|
|
|
|
export type MessageRole = 'user' | 'assistant';
|
|
export type MessageType = 'task' | 'review' | 'chat';
|
|
|
|
export interface Message {
|
|
id: string;
|
|
role: MessageRole;
|
|
content: string;
|
|
timestamp: number;
|
|
type: MessageType;
|
|
}
|
|
|
|
// ─── Provider Config ───────────────────────────────────────────────────────
|
|
|
|
export type ProviderId = 'anthropic' | 'openrouter' | 'lmstudio' | 'ollama';
|
|
|
|
export interface ProviderConfig {
|
|
provider: ProviderId;
|
|
model: string;
|
|
apiKey?: string; // Anthropic key override or OpenRouter key
|
|
baseUrl?: string; // LM Studio / Ollama custom host:port
|
|
}
|
|
|
|
// ─── AI API ────────────────────────────────────────────────────────────────
|
|
|
|
export type AIMode = 'generate_task' | 'review_code' | 'chat' | 'generate_lesson' | 'classroom_chat';
|
|
|
|
export interface AIRequestBody {
|
|
mode: AIMode;
|
|
topic: Topic;
|
|
code: string;
|
|
executionResult?: ExecutionResult;
|
|
messages: Pick<Message, 'role' | 'content'>[];
|
|
userMessage?: string;
|
|
providerConfig: ProviderConfig;
|
|
responseMode?: ResponseMode;
|
|
}
|
|
|
|
// ─── Code Execution ────────────────────────────────────────────────────────
|
|
|
|
export interface ExecutionResult {
|
|
stdout: string;
|
|
stderr: string;
|
|
exitCode: number;
|
|
timedOut: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
export interface ExecuteRequestBody {
|
|
language: SupportedLanguage;
|
|
code: string;
|
|
stdin?: string;
|
|
}
|
|
|
|
// ─── App State (useReducer) ────────────────────────────────────────────────
|
|
|
|
export type AppPhase =
|
|
| 'selecting'
|
|
| 'loading_task'
|
|
| 'ready'
|
|
| 'executing'
|
|
| 'reviewing';
|
|
|
|
export interface AppState {
|
|
phase: AppPhase;
|
|
appMode: AppMode;
|
|
topic: Topic | null;
|
|
task: Task | null;
|
|
code: string;
|
|
messages: Message[];
|
|
classroomMessages: Message[];
|
|
lessonContent: string | null;
|
|
executionResult: ExecutionResult | null;
|
|
streamingContent: string;
|
|
isStreaming: boolean;
|
|
error: string | null;
|
|
responseMode: ResponseMode;
|
|
}
|
|
|
|
// ─── Auth ───────────────────────────────────────────────────────────────────
|
|
|
|
export interface AuthUser {
|
|
id: string;
|
|
email: string;
|
|
}
|
|
|
|
// ─── App Actions ───────────────────────────────────────────────────────────
|
|
|
|
export type AppAction =
|
|
| { type: 'SELECT_TOPIC'; payload: Topic }
|
|
| { type: 'TASK_STREAM_START' }
|
|
| { type: 'TASK_STREAM_CHUNK'; payload: string }
|
|
| { type: 'TASK_STREAM_DONE'; payload: Task }
|
|
| { type: 'CODE_CHANGE'; payload: string }
|
|
| { type: 'EXECUTE_START' }
|
|
| { type: 'EXECUTE_DONE'; payload: ExecutionResult }
|
|
| { type: 'REVIEW_START' }
|
|
| { type: 'STREAM_CHUNK'; payload: string }
|
|
| { type: 'STREAM_DONE'; payload: Message }
|
|
| { type: 'SEND_USER_MESSAGE'; payload: string }
|
|
| { type: 'SET_APP_MODE'; payload: AppMode }
|
|
| { type: 'LESSON_STREAM_START' }
|
|
| { type: 'LESSON_STREAM_DONE'; payload: string }
|
|
| { type: 'SEND_CLASSROOM_MESSAGE'; payload: string }
|
|
| { type: 'CLASSROOM_MESSAGE_DONE'; payload: Message }
|
|
| { type: 'SET_ERROR'; payload: string }
|
|
| { type: 'CLEAR_ERROR' }
|
|
| { type: 'SET_RESPONSE_MODE'; payload: Partial<ResponseMode> }
|
|
| { type: 'RESET' }
|
|
| {
|
|
type: 'RESTORE_SESSION';
|
|
payload: {
|
|
topic: Topic;
|
|
task: Task;
|
|
code: string;
|
|
messages: Message[];
|
|
executionResult: ExecutionResult | null;
|
|
};
|
|
};
|