# Tauri-Migration + Compute-Boundary — Aufgabe für nächste Instanz **Entscheidung (fix, 2026-07-01):** Browser-CAD → **Desktop Tauri-App mit Rust-Backend + wgpu-Rendering.** **Grund:** komplexe Möbel mit vielen Polygonen (100k+) + mehrere parallele rechenintensive Ops → WebGL/three.js wird zum Blocker. **wgpu** (low-level GPU-API) + Rust-Compute skaliert native. **Scope:** Desktop-only Release (Milestone 1). WASM-Fallback für Browser später wenn gebraucht. **Rendering-Engine:** three.js → **wgpu** (Milestone 2) --- ## Aufgabe: Shell aufsetzen + Compute-Boundary + erste Op migrieren ### Schritt 0 — Compute-Boundary (TS-Kontrakt) **Neu:** `src/compute/index.ts` — die einzige Stelle, durch die alle rechenintensiven Ops laufen. ```typescript // src/compute/index.ts — einheitliche Schnittstelle export async function computeKernel2D(op: 'offset'|'trim', …): Promise { … } export async function detectRooms(…): Promise { … } export async function computeJoins(…): Promise { … } // ← erste Op export async function parseShapeFromDwg(…): Promise { … } ``` **Hinter der Boundary:** - Erst Tauri-invoke zu Rust `#[tauri::command]` - Fallback auf lokale TS-Impl (bleibt unberührt, bis Rust stabil) - `catch(err) → console.warn('Rust failed, using TS fallback'); return tsImpl(…)` **Effekt:** Aufrufort im Code bleibt stabil; Caller sieht nicht, ob Op schon in Rust ist oder noch TS. --- ### Schritt 1 — Tauri-Shell aufsetzen **Struktur:** ``` repo/ src-tauri/ ← neue Rust-Seite (Tauri-Konvention) src/ main.rs ← Tauri window + invoke handlers geometry.rs ← compute_joins() + weitere Ops später ... Cargo.toml src/ ← React/TS (unverändert) compute/ index.ts ← Compute-Boundary ... vite.config.ts ← Tauri plugin integrieren package.json ← tauri scripts ``` **Setup:** 1. `cargo init --name cad-tauri src-tauri` (oder `src-tauri` manuell anlegen) 2. `Cargo.toml`: Tauri v2 einbinden mit Feature `webkit2gtk-6.0` (GTK4) ```toml [dependencies] tauri = { version = "2", features = ["webkit2gtk-6.0"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" ``` 3. `src-tauri/src/main.rs`: Minimal-Fenster, invoke-Handler registrieren ```rust #[tauri::command] async fn compute_joins(input: JoinInput) -> Result { // Rust-Impl geometry::compute_joins(input).map_err(|e| e.to_string()) } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![compute_joins]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` 4. `vite.config.ts`: Tauri plugin + dev-server-Integration ```typescript import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], server: { port: 5173 } // Tauri dev zeigt hier drauf }) ``` 5. `package.json`: Tauri scripts hinzufügen ```json "scripts": { "tauri": "tauri", "tauri:dev": "tauri dev", "tauri:build": "tauri build" } ``` --- ### Schritt 2 — Erste Op migrieren: `computeJoins` (Wand-Eckverbindungen) **Warum `computeJoins` zuerst?** - Läuft häufig (bei jedem Wall-Edit) - Klein und fokussiert (~50 Zeilen Kernlogik) - Proof-of-Concept für Tauri-invoke-Flow - Danach `kernel2d` parallel hochfahren **Rust-Impl:** `src-tauri/src/geometry.rs` ```rust use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] pub struct Vec2 { pub x: f64, pub y: f64 } #[derive(Serialize, Deserialize)] pub struct WallJoinInput { pub walls: Vec, pub joints: Vec<(usize, usize)>, // wall indices } #[derive(Serialize, Deserialize)] pub struct JoinInfo { /* … */ } pub fn compute_joins(input: WallJoinInput) -> Result, Box> { // Port der Logik aus src/model/joins.ts // L-Ecken, T-Stösse, +-Kreuzungen Ok(vec![]) // Placeholder } ``` **TS-Fallback bleibt:** `src/model/joins.ts` (LS vor Rust-Port) **Compute-Boundary:** `src/compute/index.ts` ```typescript export async function computeJoins(walls: Wall[], joints: Array<[number, number]>): Promise { try { return await invoke('compute_joins', { walls, joints }); } catch (err) { console.warn('Rust compute_joins failed, using TS fallback:', err); return joinsTS(walls, joints); // Fallback } } ``` --- ### Schritt 3 — Migrations-Parität testen **Test-Fixtures:** - Aus `src/model/sampleProject.ts` exportieren: Wand-Arrays mit bekannten L/T/±-Konfigurationen - Rust-Unit-Tests: gleiche Fixtures → gleiche JoinInfo-Outputs - Vergleich: `actual == expected` **Beispiel (Rust):** ```rust #[cfg(test)] mod tests { use super::*; #[test] fn test_l_corner() { let input = WallJoinInput { /* L-shaped walls */ }; let result = compute_joins(input).unwrap(); assert_eq!(result[0].kind, JoinKind::LCorner); } } ``` --- ### Schritt 4 — Vite/Tauri-Integration **Dev-Workflow:** ```bash npm run tauri:dev # → Vite dev-server (localhost:5173) lädt React-App # → Tauri-window zeigt auf :5173 # → invoke() ruft Rust-Commands auf ``` **Build-Workflow:** ```bash npm run build # Vite → dist/ npm run tauri:build # Tauri packt dist/ + Rust-Binary ``` --- ## Deliverable (Proof-of-Concept) **Git-Stand nach dieser Aufgabe:** - [ ] `src-tauri/` Verzeichnis mit `Cargo.toml` + `src/main.rs` + `src/geometry.rs` - [ ] `Cargo.toml` buildet sauber (`cargo check` 0 Fehler) - [ ] `src/compute/index.ts` mit `computeJoins()` Schnittstelle (invoke + TS-fallback) - [ ] Rust `compute_joins()` implementiert, Tests pass (`cargo test`) - [ ] `package.json` `tauri` scripts hinzugefügt - [ ] **App läuft:** `npm run tauri:dev` → Tauri-Fenster öffnet, Wand-Edit triggert Rust-Op, Output identisch TS-Version - [ ] **Trace-Scan sauber** (kein TS/Rust-Code übrig, beide Impl. aktiv) - [ ] HANDOVER.md aktualisiert: `computeJoins` migriert, nächste Ops in Queue **Verification:** ```bash # Build-Check cargo check # 0 Fehler # Rust-Tests cargo test # App end-to-end npm run tauri:dev # → Wand zeichnen + editieren → computeJoins() über Rust aufgerufen # → Plan + 3D aktualisiert wie vorher ``` --- ## Nächste Ops (Priorisierung) Nach `computeJoins` stabil: 1. **`kernel2d`** (Offset/Trim/Extend) — großer Hebel, läuft häufig 2. **DXF/DWG-Parser** (Geometrie) — heavy, aber niedrige Frequenz 3. **`detectRooms`** (SIA-Raumerkennung) — async, kann auf Hintergrund ziehen 4. **`booleanOps`** (Union/Differenz/Schnitt) — Kandidat für später --- ## Offene Punkte (NICHT jetzt) - **GPU-Compute (wgpu):** Erst nach CPU-Ops stabil (kernel2d, joins, parsing) - **WASM-Fallback:** Nur wenn Browser-Support nötig wird - **Flatpak-Distribution:** Nach Tauri-Shell stable + erste Ops migriert --- ## Referenzen - Tauri v2 Docs: https://tauri.app/v1/guides/getting-started/prerequisites - Serde: https://serde.rs/ - CONVENTIONS.md: Identifiers englisch, UI-Text via `t()`, … - Commit-Regel: kein AI-Attribution im Repo