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>
480 lines
16 KiB
TypeScript
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>
|
|
)
|
|
}
|