Files
Benjamin Boenisch 5a31f52310 Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:26 +01:00

480 lines
16 KiB
TypeScript

'use client'
import { useState, useEffect } from 'react'
import Link from 'next/link'
import AdminLayout from '@/components/admin/AdminLayout'
import {
WizardStepper,
WizardNavigation,
EducationCard,
type WizardStep,
} from '@/components/wizard'
// ==============================================
// Types
// ==============================================
interface BridgeEducationContent {
title: string
content: string[]
tips?: string[]
}
// ==============================================
// Constants
// ==============================================
const STEPS: WizardStep[] = [
{ id: 'welcome', name: 'Willkommen', icon: '🔌', status: 'pending' },
{ id: 'dev-status', name: 'Entwicklungsstand', icon: '📊', status: 'pending' },
{ id: 'what-is-bridge', name: 'Was ist die Bridge?', icon: '❓', status: 'pending' },
{ id: 'start-server', name: 'Server starten', icon: '▶️', status: 'pending' },
{ id: 'api-endpoints', name: 'API Endpoints', icon: '🔗', status: 'pending' },
{ id: 'live-demo', name: 'Live Demo', icon: '🎮', status: 'pending' },
{ id: 'claude-usage', name: 'Mit Claude nutzen', icon: '🤖', status: 'pending' },
{ id: 'troubleshooting', name: 'Fehlerbehebung', icon: '🔧', status: 'pending' },
]
const EDUCATION_CONTENT: Record<string, BridgeEducationContent> = {
'welcome': {
title: 'Unity AI Bridge - Wizard',
content: [
'**Status: MVP KOMPLETT** - Breakpilot Drive ist spielbar!',
'',
'Die **Unity AI Bridge** ist eine REST API, die im Unity Editor laeuft.',
'',
'Sie ermoeglicht Claude Code und anderen Tools:',
'• Fehler und Warnungen automatisch zu erkennen',
'• Die Szenen-Hierarchie zu inspizieren',
'• GameObjects zu untersuchen',
'• Das Spiel zu starten und zu stoppen',
'• Diagnosen durchzufuehren',
'',
'Die Bridge laeuft auf **Port 8090** und ist nur lokal erreichbar.',
'',
'**Letztes Update:** 2026-01-13',
],
},
'dev-status': {
title: 'Breakpilot Drive - Entwicklungsstand',
content: [
'**MVP STATUS: KOMPLETT** - Proof of Concept erreicht!',
'',
'---',
'',
'**Phase 1: Quiz-System** ✅',
'• Multiple-Choice Fragen mit 4 Optionen',
'• QuestionManager mit Schwierigkeitsgrad-System',
'• QuizPanel UI mit Touch-Buttons',
'• 20 Fragen (Mathe, Deutsch, Englisch)',
'',
'**Phase 2: State Machine** ✅',
'• GameState: MainMenu, Playing, QuizActive, QuizFeedback, GameOver',
'• GameManager mit Event-System',
'• Zentrale Spielsteuerung',
'',
'**Phase 3: Touch-Steuerung** ✅',
'• Swipe-Gesten fuer iPad (Links/Rechts)',
'• Keyboard-Fallback (A/D, Pfeiltasten)',
'• InputManager mit Plattform-Erkennung',
'',
'**Phase 4: UI System** ✅',
'• UIManager mit Canvas-Runtime-Generierung',
'• HUDPanel: Score, Muenzen, Distanz, Leben, Power-Ups',
'• MainMenuPanel: Titel, Spielen-Button',
'• GameOverPanel: Statistiken, Neustart',
'',
'**Phase 5: Feedback & Belohnungen** ✅',
'• FeedbackSystem: Floating Text, Screen Flash, Konfetti',
'• PowerUpSystem: Magnet, Shield, Speed Boost, Double Points',
'• Visuelles Feedback bei Quiz-Antworten',
'',
'**Phase 6: Fragen-Content** ✅ (Basis)',
'• 20 Fragen in question_pack_v1.json',
'• Mathe, Deutsch, Englisch (Schwierigkeit 1-3)',
'• Erweiterbar fuer mehr Content',
],
tips: [
'Das Spiel ist jetzt ein vollstaendig spielbarer PoC',
'Weitere Fragen koennen in question_pack_v1.json hinzugefuegt werden',
'Naechste Schritte: Polishing, iPad-Test, Audio',
],
},
'what-is-bridge': {
title: 'Was ist die Unity AI Bridge?',
content: [
'**Problem:** Unity hat keine native API fuer externe Tools.',
'',
'**Loesung:** Ein HTTP-Server im Editor, der:',
'• Console Logs erfasst (Fehler, Warnungen, Info)',
'• Die Szenen-Hierarchie exportiert',
'• GameObject-Details bereitstellt',
'• Play/Stop/QuickSetup ausfuehrt',
'• Diagnostik-Checks durchfuehrt',
'',
'**Architektur:**',
'```',
'Claude Code <--HTTP--> Unity Bridge <--Editor API--> Unity Editor',
' (curl) Port 8090 (EditorApplication) (Scene)',
'```',
'',
'**Dateien im Projekt:**',
'• `Assets/Editor/Bridge/UnityBridge.cs` - HTTP Server',
'• `Assets/Editor/Bridge/ConsoleCapture.cs` - Log Erfassung',
'• `Assets/Editor/Bridge/BridgeCommands.cs` - API Befehle',
],
},
'start-server': {
title: 'Server in Unity starten',
content: [
'**Schritt 1: Unity oeffnen**',
'Oeffne das BreakpilotDrive Projekt in Unity Hub.',
'',
'**Schritt 2: Server starten (Option A)**',
'Menu: `BreakpilotDrive → AI Bridge → Start Server`',
'',
'**Schritt 2: Server starten (Option B)**',
'Menu: `BreakpilotDrive → AI Bridge → Status Window`',
'Klicke auf "Server starten"',
'',
'**Auto-Start aktivieren (empfohlen):**',
'Menu: `BreakpilotDrive → AI Bridge → Auto-Start beim Editor-Start`',
'',
'Nach dem Start siehst du im Console-Log:',
'```',
'[UnityBridge] Server gestartet auf http://localhost:8090',
'```',
'',
'**Tipps:**',
'• Bei Port-Konflikt: `lsof -i :8090` zeigt blockierende Prozesse',
'• Der Server stoppt automatisch, wenn Unity geschlossen wird',
],
},
'api-endpoints': {
title: 'Verfuegbare API Endpoints',
content: [
'**Status & Info:**',
'```bash',
'curl http://localhost:8090/status',
'curl http://localhost:8090/compile',
'```',
'',
'**Console Logs:**',
'```bash',
'curl http://localhost:8090/logs',
'curl http://localhost:8090/logs/errors',
'curl http://localhost:8090/logs/warnings',
'```',
'',
'**Szene & Objekte:**',
'```bash',
'curl http://localhost:8090/scene',
'curl http://localhost:8090/object/PlayerCar_Lamborghini',
'```',
'',
'**Kontrolle:**',
'```bash',
'curl http://localhost:8090/play',
'curl http://localhost:8090/stop',
'curl http://localhost:8090/quicksetup',
'curl -X POST http://localhost:8090/diagnose',
'```',
],
},
'live-demo': {
title: 'Live Demo',
content: [
'Teste die API direkt hier im Admin Panel.',
'',
'Die Knoepfe unten rufen die echten API-Endpoints auf.',
'',
'Voraussetzung: Unity Bridge muss laufen!',
],
},
'claude-usage': {
title: 'Mit Claude Code nutzen',
content: [
'**Claude Code kennt die Bridge automatisch!**',
'',
'In `.claude/UNITY_BRIDGE.md` sind die Befehle dokumentiert.',
'',
'**Typischer Workflow:**',
'',
'1. Du schreibst: "Starte das Spiel"',
'2. Claude fuehrt aus: `curl http://localhost:8090/play`',
'',
'3. Du schreibst: "Gibt es Fehler?"',
'4. Claude fuehrt aus: `curl http://localhost:8090/logs/errors`',
'',
'5. Du schreibst: "Was ist in der Szene?"',
'6. Claude fuehrt aus: `curl http://localhost:8090/scene`',
'',
'**Automatische Fehlererkennung:**',
'Claude kann nach Code-Aenderungen automatisch auf Kompilierungsfehler pruefen.',
'',
'**Tipps:**',
'• Sage Claude: "Pruefe ob Unity kompiliert"',
'• Sage Claude: "Fuehre die Diagnostik aus"',
],
},
'troubleshooting': {
title: 'Fehlerbehebung',
content: [
'**Connection refused**',
'→ Server in Unity starten',
'→ Pruefe: Menu → BreakpilotDrive → AI Bridge → Start Server',
'',
'**Port 8090 belegt**',
'→ `lsof -i :8090` zeigt den Prozess',
'→ `kill -9 <PID>` beendet ihn',
'',
'**Keine Antwort / Timeout**',
'→ Unity ist moeglicherweise blockiert',
'→ Lange Kompilierung oder Script-Fehler',
'→ Warte oder starte Unity neu',
'',
'**Befehle werden nicht ausgefuehrt**',
'→ Viele Befehle nutzen `EditorApplication.delayCall`',
'→ Warte kurz und pruefe dann das Ergebnis',
'',
'**Console Capture fehlt**',
'→ Stelle sicher, dass `ConsoleCapture.cs` in `Assets/Editor/Bridge/` liegt',
],
},
}
// ==============================================
// Live Demo Component
// ==============================================
interface BridgeStatus {
status: string
unity_version: string
project: string
scene: string
is_playing: boolean
is_compiling: boolean
errors: number
warnings: number
}
function LiveDemoPanel() {
const [response, setResponse] = useState<string>('')
const [isLoading, setIsLoading] = useState(false)
const [bridgeStatus, setBridgeStatus] = useState<BridgeStatus | null>(null)
const runCommand = async (action: string, method: string = 'GET') => {
setIsLoading(true)
setResponse('Loading...')
try {
const url = `/api/admin/unity-bridge?action=${action}`
const res = await fetch(url, { method })
if (res.ok) {
const data = await res.json()
setResponse(JSON.stringify(data, null, 2))
if (action === 'status' && !data.offline) {
setBridgeStatus(data)
}
} else {
const data = await res.json()
setResponse(JSON.stringify(data, null, 2))
}
} catch (error) {
setResponse(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`)
}
setIsLoading(false)
}
useEffect(() => {
runCommand('status')
}, [])
return (
<div className="mt-6 space-y-4">
{/* Status Badge */}
<div className="flex items-center gap-2">
{bridgeStatus ? (
<>
<span className="w-3 h-3 bg-green-500 rounded-full" />
<span className="text-green-700 font-medium">
Bridge Online - {bridgeStatus.scene}
</span>
</>
) : (
<>
<span className="w-3 h-3 bg-red-500 rounded-full animate-pulse" />
<span className="text-red-700 font-medium">Bridge Offline</span>
</>
)}
</div>
{/* Command Buttons */}
<div className="flex flex-wrap gap-2">
<button
onClick={() => runCommand('status')}
disabled={isLoading}
className="px-3 py-1.5 bg-blue-600 text-white rounded text-sm hover:bg-blue-700 disabled:opacity-50"
>
/status
</button>
<button
onClick={() => runCommand('logs')}
disabled={isLoading}
className="px-3 py-1.5 bg-blue-600 text-white rounded text-sm hover:bg-blue-700 disabled:opacity-50"
>
/logs
</button>
<button
onClick={() => runCommand('errors')}
disabled={isLoading}
className="px-3 py-1.5 bg-red-600 text-white rounded text-sm hover:bg-red-700 disabled:opacity-50"
>
/logs/errors
</button>
<button
onClick={() => runCommand('scene')}
disabled={isLoading}
className="px-3 py-1.5 bg-purple-600 text-white rounded text-sm hover:bg-purple-700 disabled:opacity-50"
>
/scene
</button>
<button
onClick={() => runCommand('diagnose', 'POST')}
disabled={isLoading}
className="px-3 py-1.5 bg-orange-600 text-white rounded text-sm hover:bg-orange-700 disabled:opacity-50"
>
/diagnose
</button>
</div>
{/* Play/Stop Buttons */}
<div className="flex gap-2">
<button
onClick={() => runCommand('play')}
disabled={isLoading || (bridgeStatus?.is_playing ?? false)}
className="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 disabled:opacity-50 flex items-center gap-2"
>
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z" />
</svg>
Play
</button>
<button
onClick={() => runCommand('stop')}
disabled={isLoading || !(bridgeStatus?.is_playing ?? false)}
className="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 disabled:opacity-50 flex items-center gap-2"
>
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<rect x="6" y="6" width="12" height="12" />
</svg>
Stop
</button>
</div>
{/* Response */}
<div className="bg-slate-900 text-slate-100 rounded-lg p-4 font-mono text-sm overflow-x-auto max-h-64 overflow-y-auto">
<pre>{response || 'Klicke auf einen Button um die API zu testen'}</pre>
</div>
</div>
)
}
// ==============================================
// Main Component
// ==============================================
export default function UnityBridgeWizardPage() {
const [currentStep, setCurrentStep] = useState(0)
const [steps, setSteps] = useState<WizardStep[]>(STEPS)
// Update step status
useEffect(() => {
setSteps((prev) =>
prev.map((step, index) => ({
...step,
status: index < currentStep ? 'completed' : index === currentStep ? 'active' : 'pending',
}))
)
}, [currentStep])
const currentContent = EDUCATION_CONTENT[steps[currentStep].id]
const handlePrev = () => {
if (currentStep > 0) {
setCurrentStep(currentStep - 1)
}
}
const handleNext = () => {
if (currentStep < steps.length - 1) {
setCurrentStep(currentStep + 1)
}
}
return (
<AdminLayout
title="Unity AI Bridge Wizard"
description="Schritt-fuer-Schritt Anleitung zur Bridge-Nutzung"
>
{/* Back Link */}
<Link
href="/admin/unity-bridge"
className="inline-flex items-center gap-1 text-sm text-gray-600 hover:text-gray-900 mb-6"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
Zurueck zum Dashboard
</Link>
{/* Stepper */}
<WizardStepper steps={steps} currentStep={currentStep} onStepClick={setCurrentStep} />
{/* Content */}
<div className="mt-8">
{currentContent && (
<EducationCard content={currentContent} />
)}
{/* Live Demo Panel */}
{steps[currentStep].id === 'live-demo' && <LiveDemoPanel />}
</div>
{/* Navigation */}
<WizardNavigation
currentStep={currentStep}
totalSteps={steps.length}
onPrev={handlePrev}
onNext={handleNext}
prevLabel="Zurueck"
nextLabel={currentStep === steps.length - 1 ? 'Fertig' : 'Weiter'}
/>
{/* Completion Message */}
{currentStep === steps.length - 1 && (
<div className="mt-6 p-4 bg-green-50 border border-green-200 rounded-lg">
<div className="flex items-center gap-3">
<svg className="w-6 h-6 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
<div>
<p className="font-medium text-green-800">Wizard abgeschlossen!</p>
<p className="text-sm text-green-600">
Du kennst jetzt die Unity AI Bridge. Gehe zum{' '}
<Link href="/admin/unity-bridge" className="underline hover:no-underline">
Dashboard
</Link>{' '}
um sie zu nutzen.
</p>
</div>
</div>
</div>
)}
</AdminLayout>
)
}