claude-desktop/src-tauri/src/teaching.rs
Eddy 120715982b Phasen 12-15: Hooks, VSCodium-Bridge, Programm-Steuerung, Schulungsmodus
Phase 12 Hook-System (hooks.rs + HooksPanel):
- HookManager mit Event-Registry + Ausfuehrungs-Log
- 5 Built-in Hooks (SessionStart, PreToolUse, PostToolUse,
  BeforeCompacting, AfterCompacting)
- Tauri-Commands: list_hooks, set_hook_enabled, get_hook_executions, fire_hook
- HooksPanel.svelte mit Live-Ausfuehrungs-Log

Phase 13 VSCodium-Integration:
- vscode-extension/: WebSocket-Server auf Port 7890
  (Commands: openFile, goToLine, formatDocument, findInFiles,
   openTerminal, getStatus, executeCommand, ping)
- src-tauri/src/ide.rs: WebSocket-Client via tokio-tungstenite
- IdePanel.svelte: Status, Port-Konfig, Ping-Test, Live-Anzeige aktive Datei

Phase 14 Programm-Steuerung (programs.rs + ProgramsPanel):
- D-Bus: dbus_call + dbus_list_services
- Xvfb: start/stop/status + screenshot (scrot)
- Playwright-Info (MCP-Verweis)
- ProgramsPanel mit 4 Sektionen (VSCodium, Playwright, D-Bus, Xvfb)

Phase 15 Schulungsmodus (teaching.rs + presentation/+page.svelte):
- Separates Tauri-Webview-Fenster
- MermaidDiagram.svelte (dynamic import mermaid)
- AnimatedCode.svelte mit WPM-Steuerung
- Tauri-Commands: presentation_open/close/send_slide/clear
- 🎓-Button in der Titelbar
- Capabilities um core:webview:allow-create-webview-window erweitert

Deps:
- Cargo: +tokio-tungstenite 0.23, +futures-util 0.3
- npm: +mermaid ^11.4.0 (npm install erforderlich)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 19:10:41 +02:00

64 lines
2 KiB
Rust

// Claude Desktop — Schulungsmodus (Phase 15)
// Oeffnet separates Praesentations-Fenster + sendet Slides
use serde::{Deserialize, Serialize};
use tauri::{AppHandle, Emitter, Manager, WebviewUrl, WebviewWindowBuilder};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Slide {
pub r#type: String, // "mermaid" | "code" | "text"
pub content: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
}
#[tauri::command]
pub async fn presentation_open(app: AppHandle) -> Result<(), String> {
// Falls Fenster bereits existiert, nach vorne holen
if let Some(win) = app.get_webview_window("presentation") {
let _ = win.show();
let _ = win.set_focus();
return Ok(());
}
WebviewWindowBuilder::new(&app, "presentation", WebviewUrl::App("/presentation".into()))
.title("Claude — Schulungsmodus")
.inner_size(1200.0, 800.0)
.center()
.build()
.map_err(|e| e.to_string())?;
Ok(())
}
#[tauri::command]
pub async fn presentation_close(app: AppHandle) -> Result<(), String> {
if let Some(win) = app.get_webview_window("presentation") {
let _ = win.close();
}
Ok(())
}
#[tauri::command]
pub async fn presentation_send_slide(app: AppHandle, slide: Slide) -> Result<(), String> {
// Fenster oeffnen falls noch nicht offen
if app.get_webview_window("presentation").is_none() {
let _ = presentation_open(app.clone()).await;
tokio::time::sleep(std::time::Duration::from_millis(400)).await;
}
if let Some(win) = app.get_webview_window("presentation") {
win.emit("presentation-slide", &slide).map_err(|e| e.to_string())?;
}
Ok(())
}
#[tauri::command]
pub async fn presentation_clear(app: AppHandle) -> Result<(), String> {
if let Some(win) = app.get_webview_window("presentation") {
win.emit("presentation-clear", ()).map_err(|e| e.to_string())?;
}
Ok(())
}