diff --git a/HANDOVER.md b/HANDOVER.md index c4b64cd..356772f 100644 --- a/HANDOVER.md +++ b/HANDOVER.md @@ -1,968 +1,42 @@ -# HANDOVER — Browser-BIM (cad), Standalone-Port von DOSSIER - -> Für die nächste Instanz. Stand: 2026-06-29. Lies zuerst -> `CONVENTIONS.md`, `ROADMAP.md` und die Projektnotizen (siehe unten). - -## >>> COMMIT-REGEL (verbindlich, IMMER beachten) <<< - -Dieses Repo darf **keinerlei Hinweise auf KI-Werkzeuge** enthalten — weder im -Code/Doku noch in der Git-Historie. - -- **Commit-Messages:** sachlich, in der Sprache des Projekts. **NIEMALS** - `Co-Authored-By:`-Trailer, „Generated with …"-Zeilen, Tool-/Modellnamen oder - sonstige Urheber-Hinweise auf einen Assistenten. -- **Dateien/Kommentare:** keine Erwähnung von Assistenten, Modellen oder Agenten. - Wer hier weiterarbeitet, schreibt so, als wäre es Handarbeit des Teams. -- Vor jedem Push kurz prüfen: `git log` und `git diff` frei von solchen Spuren. - -## >>> KOORDINATION & MEMORY (bei mehreren Instanzen/Agents) <<< - -**Mehrere Hauptinstanzen gleichzeitig:** -- Git: nur **eine** Instanz committet/pusht auf `master`, ODER jede arbeitet auf - eigenem Branch und merged kontrolliert. `App.tsx`/`types.ts` ist der serielle - Flaschenhals — verschiedene Features ≠ dieselbe Datei. -- Dieser HANDOVER ist der **Koordinationskanal** zwischen Instanzen (liegt im - Repo, wird mitgepusht). Stand hier kurz festhalten, bevor du übergibst. - -**Subagents:** -- Bekommen **kein** Memory automatisch — nur was im Prompt steht. Regeln (v. a. - die COMMIT-REGEL oben) explizit mitgeben, sonst kennt der Agent sie nicht. -- Agents **nicht** committen und **nicht** ins Memory schreiben lassen. Sie - liefern Diffs/Dateien/Ergebnisse zurück; die Hauptinstanz committet und pflegt - das Gedächtnis. - -**Memory (`~/.claude/...`, außerhalb des Repos — leakt nie hierher):** -- In den Kontext geladen wird nur der schlanke **Index** (eine Zeile je Eintrag); - einzelne Fakten erscheinen nur bei Relevanz. Größe ist daher selten ein Problem. -- **Keine automatische Bereinigung.** Gepflegt wird beim Schreiben (Duplikate - aktualisieren statt anlegen, Überholtes löschen) oder auf Ansage. -- Nur **beständige** Fakten ablegen, ein Fakt pro Datei, Index-Zeile knapp. Bei - parallelen Schreibvorgängen ist der Index (MEMORY.md) die Contention-Stelle — - vor dem Edit frisch lesen (der „modified since read"-Guard verhindert blindes - Überschreiben). - -## >>> STAND 2026-07-03 Abend (Nordstern-Session, Übergabe an neue Instanz) — ZUERST LESEN <<< - -Ein Master-Branch (alle Feature-/Worktree-Branches konsolidiert und gelöscht), alles auf -origin/master gepusht, Working Tree sauber bis auf eine vorbestehende, unabhängige -Materials-Library-WIP (`.gitignore`, `public/assets/materials/manifest.json`, -`src/materials/library.ts`, `scripts/fetch-materials.mjs` — NICHT anfassen/reverten, ist -fremde in Arbeit befindliche Sache, nicht Teil der Engine-Session). Heute gelandet, acht -Commits in dieser Reihenfolge: - -1. **7ca5197** Resource Manager: Materialien-Tab (PBR-Kugel-Vorschau via geteiltem Offscreen- - Renderer, Suche + Kategorie-Chips, lazy per IntersectionObserver). -2. **c8a4188** render3d als WASM/WebGPU-Viewport in der App (`?engine=wasm` bzw. localStorage - `cad.rendererMode`; three.js bleibt Default). Probe: scripts/probe-engine3d.mjs (HEADFUL=1 - nötig — headless Chromium präsentiert keine WebGPU-Frames, gilt auch für render2d). -3. **54211b1 + 76029db** render3d/src/section.rs: Schnittebene → Cut-Polygone + sichtbare/ - verdeckte projizierte Kanten, inkl. Öffnungen (Brüstung/Sturz-Teilrechtecke, Durchblick). - 29 Tests, Beweis-SVG docs/welle-c-hlr-spike/section-engine-proof.svg, Doku - docs/design/engine-section-pipeline.md. Eigen-Engine-Ersatz für den OCCT-Pfad (66 MB WASM). - **mesh.rs (3D-Vollkörper-Extrusion) berücksichtigt Öffnungen weiterhin NICHT** — dokumentierte, - bewusst offene Lücke (Cut/Ansicht-Pipeline und 3D-Solid-Mesh sind bis dahin inkonsistent). -4. **ce6bd26** 2D-Engine-Parität: WASM-Pfad deckungsgleich zum SVG-Referenzpfad (**Referenz ist - `?gl=0`**, nicht der Default!). Fixes: text_scale entkoppelt, glyphon ColorMode::Web, - z-basierte Maler-Reihenfolge (draw_sequence), CSS-Klassenfarben in toRenderScene, greyed 0.3, - Dämmwellen-Bézier + userSpaceOnUse-Anker + widthScreen-Schraffurbreite. Probe: - scripts/probe-engine-parity.mjs. -5. **4b02e40** render2d Headless: Feature `headless`, HeadlessRenderer ohne Surface (Vulkan), - CLI-Bin render_png, Golden-Test (bit-exakt) tests/golden/demo.png, Doku - docs/design/engine-headless.md. -6. **382771b** Vektor-PDF aus der RenderScene (**Nordstern 2 abgeschlossen**): exportPdf baut - jetzt `planToRenderScene(plan)` — derselbe Aufruf wie der Viewport — und serialisiert über - den neuen `src/export/sceneToPrintSvg.ts` nach Papier-mm (PEN_STEPS-Quantisierung wie bisher, - widthScreen-Schraffurbreiten korrekt in mm umgerechnet, Texte jetzt als echte Vektor-Texte — - der alte Pfad liess sie komplett weg). `planToPrintSvg.ts` bleibt nur als Referenz stehen - (Kopfkommentar markiert sie als abgelöst). Verifiziert per `pdftoppm`: 53.51×43.69mm vs. - erwartet 53.45×43.45mm bei 1:100. **Bekannte Alt-Lücke, nicht neu:** Zeilenabstand - mehrzeiliger Stempeltexte in `toRenderScene.ts` ist nur bei Massstab 1:100 exakt (STAMP_REF_N), - bei anderen Export-Massstäben proportional leicht daneben — betrifft Viewport identisch, nicht - PDF-spezifisch, absichtlich nicht mitgefixt (out of scope für den additiven Umbau). -7. **b4c4a2c** wgpu 22 → 29, glyphon 0.6 → 0.11 (glyphon pinnt `^29`, obwohl wgpu 30 existiert). - `src/engine/requestDeviceShim.ts` komplett entfernt (das 22er-Limit - `maxInterStageShaderComponents` wird von 29 nicht mehr gesendet). Verifiziert: cargo check - nativ (render2d/render3d/src-tauri), cargo test render2d 17/17 + Golden weiterhin bit-exakt, - render3d 29/29, wasm32 --features web für beide, tsc + npm build — alles grün. - -**Damit sind Nordstern 2, 3, 4 (Kern) und 5 gelandet.** Verifiziert per Screenshot: App läuft im -Electron-Fenster (`env -u ELECTRON_RUN_AS_NODE npm run electron`), 3D-Perspektive rendert das -echte Modell über den Engine-Renderer (Umschalter unten rechts: WebGL2/Engine). - -**WICHTIG — was NICHT fertig ist (User hat das selbst am Screenshot bemerkt):** Die -Zeichnungsebenen vom Typ „Schnitt"/„Ansicht" (z.B. „Schnitt A", „Ansicht Süd" in der -Ebenen-Liste rechts) sind weiterhin **Stubs** — sie zeigen KEINEN echten Inhalt. Das -render3d-Schnitt-Modul (section.rs, Commits 54211b1/76029db) liefert das Rohmaterial -(SectionOutput: cut_polygons + visible_edges/hidden_edges), ist aber **noch nicht an -App.tsx/das Dokumentmodell angebunden** — es gibt noch keinen Code-Pfad, der beim Öffnen -einer Schnitt-/Ansichts-Zeichnungsebene das render3d-Modell durchschneidet und das Ergebnis -in Plan-Primitive für PlanView/render2d übersetzt. Das ist der wichtigste nächste Schritt -(Nordstern 4b, siehe Liste unten). - -**Nächste Schritte, priorisiert:** -1. **[Nordstern 4b] Schnitt-Pipeline an UI anbinden** — grösster Brocken: SectionOutput - (Rust/render3d) → TS-Seite (WASM-Aufruf analog `toWalls3d.ts`/`nativeSync.ts`) → neue - Plan-Primitive-Kind(e) für Cut-Polygone + sichtbare/gestrichelte Kanten → generatePlan.ts - bindet das für DrawingLevel vom Typ Schnitt/Ansicht ein, statt des aktuellen Stub-Verhaltens. - PlanView.tsx rendert es wie normale Primitive (Parität-Infrastruktur aus ce6bd26 gilt 1:1). -2. **mesh.rs: Öffnungen im 3D-Vollkörper** — 3D-Ansicht zeigt Öffnungen zwar korrekt (das war - schon vorher so, per Mesh-Aussparung), aber das Schnitt-Modul rechnet gegen dieselben - Prismen — sobald 4b. steht, hier gegenprüfen ob mesh.rs nachgezogen werden muss oder ob es - getrennte Datenwege bleiben. -3. **Aufräumen:** `src-tauri/`-Ordner umbenennen/entflechten — User will keine Tauri-Reste im - Namensraum, Electron ist die Shell. render2d/render3d/geometry sind eigenständige Workspaces - ohne Tauri-Abhängigkeit, könnten z.B. nach `engine/` oder `crates/` wandern. Toten Tauri-Host - (`cad-tauri`, tauri.conf.json, native.rs/native2d/native3d) explizit mit dem User klären: - behalten (Beweis für späteren All-Native-Schritt) oder entfernen. -4. **[Nordstern 1]** Papier-mm-exakte Strichbreiten-Audit über den gesamten Engine-Pfad - (Screen bei jedem Zoom/Massstab = Druck) — nach 2/3/4/5 jetzt der einzige verbliebene - unbearbeitete Nordstern-Punkt. -5. Nicht-Engine-CAD-Backlog (Wand-Referenzlinien, Editier-Welle, nahtfreie Verschneidung, - Overrides/Multi-Page-Layouts) — weiter unten in diesem Dokument, nachrangig zur Engine-Arbeit. - -**Arbeitsweise dieser Session (fortführen):** Pipeline aus bis zu 2 parallelen Sonnet-Subagents -(User-Regel: immer 2 am Laufen halten, nachfüllen sobald einer fertig ist). Jeder Agent bekommt -explizit zugewiesenes Terrain (Dateipfade), damit sich parallele Agents nicht überschreiben. -Ergebnisse werden von der Hauptinstanz verifiziert (Screenshots ansehen, Gates laufen lassen) -und committet — Agents committen nie selbst. Nach Prozessabstürzen (kam heute zweimal vor) -lässt sich ein Agent per SendMessage mit seiner agentId aus dem Transcript fortsetzen, sein -Working-Tree-Zwischenstand bleibt erhalten. - -## >>> ENGINE-NORDSTERN (User-Direktive 2026-07-02): die volle Engine-Vision <<< - -Der User hat explizit beauftragt, die Engine-Vision KOMPLETT anzustreben ("wir brauchen -exakte breiten usw. strebe bitte das komplett an!"). render2d/render3d sind nicht nur -Viewport-Ersatz, sondern werden die EINZIGE Render-Wahrheit des CAD: - -1. **Papier-mm-exakte Strichbreiten** überall — Bildschirm bei jedem Maßstab/Zoom = Druck. -2. **Deterministische Plan-Ausgabe:** Druck/PDF mm-genau identisch zum Bildschirm, beides aus - render2d (kein separater Druckpfad). Vektor-PDF-Backend = dieselbe Szene, anderes Target. -3. **Headless-Rendering:** PNG/PDF-Export und Golden-Image-Tests ohne Fenster (die - serde-only/GPU-Trennung in render2d existiert genau dafür; wgpu kann offscreen). -4. **HLR/Schnitt-Pipelines** durch die Engine (render3d → Kantenextraktion → render2d-Szene). -5. **Eine Wahrheit:** SVG-/WebGL2-Geometriepfade im Browser schrittweise durch die - WASM/WebGPU-Engine ersetzen (`?engine=wasm`-Spike ist der Anfang); SVG bleibt nur - Interaktions-/Overlay-Schicht. Bereits gefundene Duplikations-Bugs: Wandecken-Kerben, - fehlende Strichelung im WebGL2-Pfad. - -6. **WebKit-Unabhängigkeit** (explizit vom User betont): kein Render- oder Ausgabepfad darf - von WebKitGTK abhängen. Brücke = Chromium-Shell (`npm run shell`), Endzustand = All-Native - (ein Rust-Prozess, Engines + natives UI, kein Webview). - -**RICHTUNGSENTSCHEIDUNG 2026-07-02: Electron statt Tauri, all-native aufgeschoben.** -Abgewogen mit dem User: (a) „auf Tauri bleiben" = WebKitGTK, das WEDER WebGPU (kein WASM- -Viewport) NOCH native-wgpu-Composite ins Fenster (Wayland tot) kann → Engine nur als separate -Fenster möglich, kein integrierter Viewport. (b) „all-native jetzt" = kein Shortcut, sondern -das GRÖSSERE Rewrite: ~46k Zeilen TS (UI ~18k + Modell-Gehirn ~5k + Interaktion ~23k) müssten -nach Rust; die Rust-Engines sind erst ~10% der App. → **Gewählt: Electron/Chromium-Shell + -WASM/WebGPU-Engine im Webview** (behält die funktionierende React-UI, verlässt WebKitGTK, -integrierter Engine-Viewport möglich). Die Engine-Rust-Crates sind in einem späteren All-Native- -Schritt 1:1 wiederverwendbar; nur die dünne WASM-Bindeschicht wäre dann Wegwerf. All-native -bleibt legitimes Fernziel, ist aber KEIN Weg zurück zu Tauri (Tauri IST der Webview-Shell). - ->>> ELECTRON-PROTOTYP: FERTIG (2026-07-02). <<< -`electron` als devDep, `scripts/electron-main.cjs` (BrowserWindow, `Menu.setApplicationMenu(null)` -+ `frame:false`/`autoHideMenuBar` für randloses Fenster wie chromium-shell.sh, WebGPU-Flags -`enable-unsafe-webgpu`+`enable-features=Vulkan`), `scripts/electron-shell.sh` (Dev-Server-Guard -wie chromium-shell.sh), npm-Script `npm run electron`. Verifiziert per Screenshot: App startet -als eigenes randloses Fenster, lädt den echten Grundriss (nicht leer). WICHTIG: -- **Datei muss `.cjs` sein** (`package.json` hat `"type":"module"`, Electron-Main läuft als CJS). -- **`ELECTRON_RUN_AS_NODE=1`** kann aus dem umgebenden Prozess (z.B. VSCode-Electron-Host) - vererbt sein — zwingt Electron in den Node-Modus (`require('electron')` liefert nur einen - Pfad-String statt `{app,...}` → `app` ist undefined). Immer mit `env -u ELECTRON_RUN_AS_NODE` - starten, falls das Fenster nicht öffnet / `app.commandLine` undefined ist. -- **WebGPU/`?engine=wasm` funktioniert in Electron** — verifiziert per Screenshot (RENDERER-Anzeige - zeigt "Engine" aktiv, Grundriss korrekt gezeichnet). Ein einzelner Fehlversuch vorher war ein - GPU-Prozess-Init-Timing-Rennen (zu früh abgefragt), kein echter Bug — `app.getGPUFeatureStatus()` - zeigt nach ~2s alles `enabled`/`enabled_on` inkl. `webgpu` und `vulkan` (AMD RX 7800 XT/RADV). - KEINE Ozone-Platform-Flags nötig, `--ozone-platform=x11` NICHT verwenden (bricht die - Fenstererstellung, Vulkan-Surface/GetGeometry-Fehler). `electron --version` meldet v24, weil - `ELECTRON_RUN_AS_NODE=1` den Prozess in den Node-Modus zwingt und dann Nodes eigene Bundle- - Version zeigt (kein echter Versions-Mismatch — `env -u ELECTRON_RUN_AS_NODE electron --version` - meldet korrekt v43). -Danach: render3d-WASM, Doppeltext-Fix.<<< -Klarstellung an den User (er fragte, ob Webview = „billig"): NEIN — Figma (eigene C++/WASM-Engine -im Web-Shell), Onshape, VS Code zeigen, dass „eigene Engine + Web-UI" Top-Tier-Architektur ist. -CAD/BIM-Substanz = Modell + Engine + Normen (haben wir: parametrische Wände, SIA 416, DOSSIER- -Ebenen, ein-Modell→alle-Sichten), NICHT der Fenster-Shell. - -Praxisregel ab jetzt: Darstellungs-Features NICHT mehr mehrfach (SVG/WebGL2/nativ) bauen, -sondern einmal in der Engine + dünne Anbindungen. - -## >>> AUFGABE FÜR NEUE INSTANZ: 2D-Plan auf WebGL-GPU-Renderer (ZUERST) <<< - -**Warum:** Tauri-Linux-Webview = **WebKitGTK**, das SVG/2D auf der **CPU (Cairo)** rastert. -User-GPU (AMD RX 7800 XT) langweilt sich; bei **144 Hz** (~6,9 ms/Frame) ruckelt Pan/Zoom/ -**Objekt-Bewegen** sichtbar. three.js/WebGL (3D) läuft GPU-beschleunigt → NUR der 2D-SVG-Pfad -ist CPU-gebunden. Entscheidung mit User: 2D-Plan **von SVG auf WebGL2 (GPU)** umbauen. - -**Sofortlösung fürs Vorführen/Entwickeln — Chromium-App-Shell:** `npm run shell` -(`scripts/chromium-shell.sh`) startet bei Bedarf den Vite-Dev-Server (Port 5187, Polling per -`curl`) und öffnet ihn dann in einem randlosen Chromium-Fenster (`--app=…`, eigenes Profil unter -`~/.cache/cad-chromium-shell`), statt im WebKitGTK-Webview von `tauri:dev`. Chromium rendert -dieselbe Seite GPU-beschleunigt und spürbar flüssiger. Kein Ersatz für den nativen wgpu-Pfad -(`render2d`/`render3d`), aber ein schneller Weg zu einer ruckelfreien Oberfläche, solange der -Electron/CEF-Shell (siehe Notizen zu „Render path decision") noch aussteht. Läuft rein im -Browser ohne Tauri-Backend — `src/compute/index.ts` guardet den einzigen `invoke()`-Aufruf -bereits per dynamischem Import mit TS-Fallback. - -**Architektur (mit User abgestimmt):** -- **Hybrid:** WebGL2-Canvas rendert die schwere Geometrie (Poché-Polygone, Linien, Schraffuren, - Kreise/Bögen als Segmente). **Dünne SVG-Ebene DARÜBER** behält Text (Raumstempel/Labels), - Griffe, Snap-Marker, Werkzeug-Vorschau (wenige Elemente → billig, DOM-Hit-Test + scharfer Text bleibt). -- **Raw WebGL2, KEINE neue npm-Dependency** (User will nicht „abhängiger" werden; nativ). -- **Geometrie pro Plan EINMAL tessellieren** (Cache), Pan/Zoom = nur Transform-Matrix-Uniform → - keine Re-Tessellation, GPU-buttrig. Objekt-Bewegen: Plan ändert sich → re-tessellieren (leichter als SVG-Reconcile+Cairo). -- **Linienbreite konstant in Screen-px** (wie heutiges `non-scaling-stroke`): im Vertex-Shader - expandieren (Position world → clip, dann Normalen-Offset in clip via Viewport-px). Miter an Ecken - später; Spike: Quads pro Segment. -- **SVG-Renderer als Fallback hinter einem Flag behalten**, bis GL-Parität erreicht → kein Risiko. -- Vorgehen: **erst Spike** (Linien+gefüllte Polygone + GPU-Pan/Zoom), Glätte bei 144 Hz messen - (dev :5187 läuft; `tauri:dev`-Fenster läuft), DANN Schraffur/Feinschliff/Text-Overlay/Hit-Test. - -**Fixpunkte im Code:** -- `src/plan/PlanView.tsx` (~2500 Z.): `toScreen()` = Modell-m → viewBox-Einheiten, FIXER Ursprung, - `PX_PER_M=90`; `view={x,y,w,h}` treibt ``; Pan = setView, Zoom = setView(onWheel). - BEREITS optimiert: `primitiveEls`/`drawingRunEls` `useMemo` (Pan/Zoom 312→0 Re-Renders) + - rAF-Coalescing beim Objekt-Drag (`flushDragMove`/`enqueueDragMove`, 40→1 Update/Frame). Diese - bleiben nützlich; der GL-Renderer ersetzt die SVG-Primitiv-Ebene, NICHT die Interaktions-Logik/Griffe. -- `src/plan/generatePlan.ts`: `export type Primitive` (Z.133) — kinds: `polygon`{pts,fill,stroke, - strokeWidthMm,hatch,*Id}, `line`{a,b,weightMm?,strokeWidthMm?,dash?,color,cls,greyed,drawingId?}, - `arc`{center,r,…}, `text`{at,doc,extraLines,basePt,color} (Text → SVG-Overlay, NICHT WebGL). -- Koordinaten/Breiten-Logik im SVG-Renderer: `PrimitiveShape` (~Z.2280) + `DrawingRunShape` — dort - steht, wie Haarlinie/mm/`paperScale`/`non-scaling-stroke` die Strichstärke bestimmen (für GL nachbilden). -- Hit-Test/Auswahl/Griffe laufen weiter über die bestehenden PlanView-Pfade (modellbasiert); GL ist nur Anzeige. -- **Vektor-PDF/-Export bleibt** (kommt aus `generatePlan`/DXF, nicht aus der Anzeigefläche) — NICHT anfassen. - -**Gates:** `npx tsc -b` 0, `npm run build` grün, Trace-Scan sauber (keine KI-Spuren, COMMIT-REGEL oben). -Kein Commit ohne Ansage. Verifizieren wie üblich per Puppeteer gegen :5187 (Render korrekt + Glätte messen). - -**GESAMT-TODO-LISTE (Stand 2026-07-01 Nacht) — im Hinterkopf behalten:** -ERLEDIGT+verifiziert: U/I/O/P-Kopiermodi · Backlog#1 Transform→CommandLine · Backlog#4 3D-Zeichnen + -Engine-Confirm-Bugfix · Raumstempel#2 (Feld-Modell) · Plan-Perf (Memo + rAF-Coalesce) · Tauri-M1-PoC -(geometry-Crate 4/4 + Parität + `tauri:dev`-Boot). OFFEN: **(A) 2D-WebGL-Renderer [DIESE AUFGABE]** · -(B) Tauri-M1 Rest: Compute-Boundary in Render-Pfad einhängen (generatePlan sync → precompute+cache) + -nächste Ops (kernel2d→DXF/DWG→detectRooms) · (C) three.js→wgpu (M2, nach 2D-WebGL) · Parametric Walls -Phase B (andere Instanz) · Wand-Referenzlinien-Option unter Bezugspunkt · Viewport/Editier-Welle -(Treppen-Griffe, Öffnung-entlang-Wand, 2D auf Zeichenebenen, runde Linienenden, schattiert-Politur) · -Smart-Join/Split + nahtfreie Wandverschneidung · Text/Stempel-Annotation (Frame+Rotation) · Welle B -(3D-Schnitt+Stencil-Capping) · Welle C (HLR verdrahten) · Overrides/Ausschnitte/Multi-Page-Layouts. - -## >>> STAND 2026-07-01 (Tauri-PoC — App-frei, gelandet) <<< - -**Tauri v2 + Rust-Compute-Boundary — Proof-of-Concept steht (3 App-freie Agents).** -Alle Gates grün, nichts an `App.tsx`/`src/model/` angefasst (nur NEUE Dateien + -additive Config). Ref: `docs/design/tauri-migration-plan.md` / `tauri-architecture.md`. - -- **`src-tauri/` (Rust, neu):** Cargo-**Workspace** aus zwei Crates: - - `src-tauri/geometry/` — **serde-only** Lib, Port von `computeJoins` (aus - `src/model/joins.ts`) inkl. Vektor-Helfer. Input flach: `WallInput{ id,start,end, - thickness,referenceOffset }`; Output `WallCuts{ wallId,startCut,endCut }` (Line= - point+dir). **`cargo test` grün: 4/4** (L-Ecke=geteilte Gehrung, freies Ende, - T-Stoss, kollinear → alle korrekt). Braucht KEIN webkit → hier testbar. - - `src-tauri/` App-Crate (`cad-tauri`) — Tauri-v2-Shell (`lib.rs` mit - `#[tauri::command] compute_joins` + `run()`, `main.rs`, `build.rs`, - `tauri.conf.json` devUrl **:5187**, `icons/icon.png`). **`cargo check` GRÜN** — - wider Erwarten NICHT von webkit blockiert: tauri v2 bindet `webkit2gtk-4.1` - (vorhanden); nur `-6.0` fehlte. `src-tauri/target` + `gen/` sind Build-Artefakte - (gitignored). -- **`src/compute/index.ts` (neu, Boundary):** einzige Stelle für schwere Ops. - `computeJoins(project,walls)` flacht ab → `invoke("compute_joins")` (geguardeter - dynamischer Import von `@tauri-apps/api/core`, Build bleibt grün OHNE das Paket) - → bei null/Fehler `console.warn` + TS-Fallback (`joins.ts`). Zusätzlich - Durchreicher `detectRooms` (roomBoundary.ts) + `parseShapeFromDwg`(=`parseDwg`); - `computeKernel2D` bewusst ausgelassen (kein Einzel-Entry, ~30 Primitive). -- **`package.json`/`vite.config.ts`:** `@tauri-apps/cli`+`api` (v2) installiert, - Scripts `tauri`/`tauri:dev`/`tauri:build`; vite Port 5187 `strictPort`, - `clearScreen:false`, `envPrefix` — rein additiv. - -**NÄCHSTE SCHRITTE (Tauri):** (1) **Echte TS↔Rust-Numerik-Parität** end-to-end prüfen -— erst sinnvoll, wenn die Boundary in einen Caller verdrahtet ist (aktuell bewusst -NICHT verdrahtet, weil das `generatePlan`/`App` async machen würde). (2) `computeJoins` -in den Wand-Render-Pfad einhängen (async-Welle). (3) `tauri:dev` end-to-end auf einer -Maschine MIT `webkit2gtk` (hier nur `cargo check`, kein Fenster-Boot getestet). -(4) Nächste Ops laut Plan: `kernel2d` → DXF/DWG → `detectRooms`. - -## >>> STAND 2026-07-01 (Parametric Walls Phase A — ISOLIERT) <<< - -**Neue Instanz (Opus) — Parallelisierung mit Agents (Sonnet):** -- **Branch:** `feature/parametric-walls` (isoliert, keine App.tsx-Änderungen) -- **Phase A FERTIG (alle Agents grün):** - - **Agent 1:** Types (`ParametricWall`, 5 `ParametricRule` Varianten) + Engine - (`resolveParametricWall()`, 5 rule handlers, helpers) in `src/model/parametricWalls.ts` - - **Agent 2:** 50 Unit-Tests (Vitest), alle grün; `npm test -- parametricWalls.test.ts` - - **Agent 3:** Design-Doku `docs/design/parametric-walls.md` + `docs/README.md` aktualisiert - - Verification: `tsc -b` ✓, `npm run build` ✓ (577 modules, 3.72s) -- **Phase B (UI-Integration) — für nächste Instanz mit Agents:** - - Agent 1: TopBar ParametricWall-Picker + UI-Komponente - - Agent 2: Command `pw` (`cmds/parametricWall.ts`) - - Agent 3: ResourceManager-Integration (ParametricWall CRUD) - - Dann merge → `master`, Screenshot-Verifikation -- **Koordination:** andere Instanz committet parallel (Text, Raum, HLR) auf `master`, - keine Konflikte (nur `types.ts`+`src/model/` erweitert, nicht `App.tsx`). - -**Backlog #4 ERLEDIGT (Zeichnen im 3D-View — vorherige Instanz):** Werkzeug-Gate `toolsEnabled` - (`App.tsx` ~1877) von `viewType === "grundriss"` auf `activeLevel.kind === - "floor"` erweitert → Werkzeuge sind jetzt AUCH in der Perspektive aktiv. Die - gesamte Zeichen-Pipeline war bereits verdrahtet (`Viewport3D.workplaneModel()` - raycastet den Cursor auf die Geschoss-OKFF; `onWorkplanePoint`/`onWorkplaneConfirm` - speisen dieselbe Engine wie der Grundriss). Ende-zu-Ende per Puppeteer verifiziert: - in iso-Ansicht Linie mit 2 Klicks gezeichnet → committet (`draw2d` 0→1, Prompt - zurück auf „Befehl:"); Picks liefern gültige Modell-Vec2 (raycast korrekt). -- **BUG-FIX unterwegs (Engine-Confirm, `src/commands/engine.ts` `confirm()`):** - Beim Testen von #4 fiel ein VORBESTEHENDER Bug auf (reproduziert IDENTISCH im - Grundriss, nicht durch #4 verursacht): mehrteilige Befehle (Wand/Polylinie) - liessen sich per Enter/Rechtsklick NIE abschliessen. Ursache: `confirm()` nahm - IMMER den Tab-Feld-Zweig (`fieldResultPoint`), sobald der Schritt Felder hatte — - auch OHNE gelocktes Feld → es wurde ein Cursor-Punkt gefüttert (bei Wand: Null- - Strecke, ignoriert) statt `onConfirm` (das den Zug committet) aufzurufen. Fix: - Feld-Zweig nur noch bei tatsächlich GELOCKTEM Feld (`Object.keys(this.locks).length - > 0`); ohne Lock fällt `confirm()` auf `onConfirm` durch. Verifiziert: Wand - schliesst jetzt per Rechtsklick im Grundriss (91→98) UND in 3D (99→106); Tab-Feld- - Fluss „Länge 4 tippen → Enter lockt → leeres Enter setzt Punkt" unverändert; - Linie-Auto-Commit (2 Klicks) unverändert. Gegatet: `tsc -b` 0, `build` grün, keine - Konsolenfehler. KEIN Commit. - -- **Backlog #1 ERLEDIGT (Befehlsleiste-Transform vereinheitlicht):** Die laufende - Auswahl-Transformation (nur noch `rotate`/Taste `d` mit U/I/O/P; move/mirror - laufen längst über die Engine) speist jetzt die `CommandLine` statt der - schwebenden `TransformBar`. Dritter Zweig in der ``-Ternäre parallel - zu `gripEdit` (`gripEdit ? … : activeTransform ? … : engineView…`): Prompt = - `transform.hint.${op}.${…}`, Inline-Optionen = U/I/O/P (`transformOptions()`, - aktiver Modus hervorgehoben via neuem `active?`-Feld an `CommandLineOption` + - `.cmdline-option.active` in styles.css, `×count` bei array/distribute), Live-/ - lockbares **Winkel**-Feld beim Drehen bzw. **Distanz** beim Bewegen - (`transformFields()` aus neuem `transformDragInfoRef`), getippte Zahl committet - über `submitTransformValue()`. `d` fokussiert die CommandLine (wie `m`/`s`). - `src/ui/TransformBar.tsx` gelöscht; verwaiste `.transform-bar`/`.tf-*`-CSS + - `transform.bar`-Key harmlos stehen gelassen. Gegatet: `tsc -b` 0 Fehler, `build` - grün, Trace-Scan sauber, Puppeteer-Probe end-to-end (nach `d`: „Drehzentrum - klicken" + U/I/O/P; nach Zentrum+Bezug: „Zielwinkel klicken" + Live-Winkelfeld; - keine Konsolenfehler). KEIN Commit (Konvention der Vorsessions beibehalten). - -## >>> STAND 2026-07-01 (Abend) — Raum + Topbar-Umbau + Backlog — ZUERST lesen <<< - -Alle unten genannten Agenten GRÜN gelandet und gegatet (`tsc -b` 0 Fehler, -`npm run build` grün, Trace-Scan sauber, Boot-Probe ohne Konsolenfehler). Dev- -Server läuft/hält auf :5187. KEIN Commit gemacht (Commit-Regel oben beachten). - -**Diese Session gelandet:** -- **Rich-Text-Kern (komplett):** `src/text/richText.ts` (Marks/RichTextDoc/TextRange, - toggleMark/applyMark/isMarkActive/DEFAULT_PRESETS/serialize…), `src/text/renderHtml.ts` - (docToHtml/docToLines/docToSvgText), `src/text/RichTextEditor.tsx` (contentEditable, - Toolbar B/I/U/S + Grösse/Farbe/Presets). -- **Raum-Slice (SIA-416), Agent gelandet:** `Room` in `model/types.ts` (id, floorId, - categoryCode "60", siaCategory HNF/NNF/VF/FF/KGF, boundary, stampAnchor?, stampDoc?); - `cmds/room.ts` (Modus inside=click-inside via `roomFromPointInside`, manual=Polylinie); - Plan: neues `text`-Primitive + Füllpolygon + Stempel via `docToLines` + Live-Fläche; - `panels/RoomBalancePanel.tsx` (SIA-Bilanz + CSV). **App-State für Stempel:** - `selectedRoomId`/`selectedRoom.stampDoc`, `projectSlice.setRoomStampDoc(roomId,doc)`, - `moveRoomStamp`, `setRoomStampAnchor`, `stampEditorRoomId`. Stempel-Editor = kleines - schwebendes Fenster (kein Footer). -- **DOSSIER-Referenz studiert → Spec `docs/design/topbar-dossier.md`** (verbindlich für - Topbar-Arbeit; Referenz-Klon lag unter `/tmp/DOSSIER`). Kernbefund: Text-Styling ist im - Original eine FEST in der Oberleiste sitzende Text-Gruppe (Stil/Font/Grösse + B/I/U + - L/C/R + „+Text"), Akzent-Glow bei Auswahl; Raumstempel-Typografie darüber; Text-INHALT - in eigenem kleinem Fenster; Zoom/Massstab als 2×2-Cluster (kombinierte Stat-Pille). -- **Topbar-Umbau (Agent gelandet), `src/ui/TopBar.tsx` + `styles.css` + `App.tsx` + - `ui/TextEditorDialog.tsx` (neu) + i18n:** Zoom-Doppelung entfernt; 2×2-Massstab/Zoom- - Cluster; einheitliche Segmentpillen (neue `Segment`-Komponente); **Text-Gruppe** in der - Topbar (`textTarget`-Prop = `null | {doc,range,apply}`, aus selektiertem Raum abgeleitet, - `apply`→`setRoomStampDoc`; ohne range wirkt Format auf ganzes Doc); Doppelklick öffnet - `TextEditorDialog`. Verwaiste alte CSS-Regeln (`.tb-zoom*`, `.stamp-editor*`) bewusst - stehen gelassen (harmlos). „+ Text"-Button verdrahtet, aber DEAKTIVIERT (noch kein - Text-Annotations-Werkzeug). - -**Backlog / nächste Schritte (Priorität dieser Session, siehe auch Todo-Liste):** -1. **Befehlsleiste-Transform vereinheitlichen:** laufende Transformation (move/mirror/rotate) - soll die `CommandLine` speisen (Prompt „Basispunkt/Zielpunkt" + U/I/O/P als Inline- - Optionen) statt der separaten schwebenden `TransformBar`; freies Befehl-Tippen während der - Geste unterdrücken, Zahleneingabe (Distanz/Winkel) via `fields` bleibt. Muster: parallel - zum bestehenden `gripEdit`-Zweig bei `` (~App.tsx:2793). U/I/O/P-Routing - (App.tsx ~1543–1548) NICHT kaputt machen — nur zusätzlich in die CommandLine spiegeln. -2. **Strukturierter Raumstempel (DOSSIER `RaumProperties`):** Stempel ist KEIN Freitext, - sondern ein FELD-Modell: Raumnummer · Raumname · Raumname-Zeile2 (in Listen als ein Name - zusammengezogen) · Bodenfläche (an/aus + Präfix) · Fensterfläche (an/aus + Präfix) · - Nutzung HNF/… (an/aus). Zeilen-Layout-Editor (welches Feld auf welche Zeile) + Typografie - PRO Feld-Einheit (Raumname grösser, andere kleiner, eine Zeile kursiv). Ersetzt den - Freitext-`stampDoc`-Zwischenstand; die Topbar-Text-Gruppe formatiert dann die gewählte - Feld-Einheit (löst auch die „ganzes Doc statt Selektion"-Einschränkung). -3. **Wand-Optionen:** Referenzlinien-Option UNTER den Bezugspunkt setzen, gleiche Optik/ - Anordnung wie die übrigen Optionen (`cmds/wall.ts` / ToolsPanel-Optionsreihenfolge). -4. **Zeichnen im 3D-View:** Cursor per Raycast auf die Arbeitsebene (okff des Geschosses) - projizieren → Weltkoordinaten als `engine.move/pick` einspeisen; `toolsEnabled`-Gate - (App.tsx ~1834, heute `viewType==="grundriss"`) auch für Perspektive öffnen. -5. **Viewport/Editier-Welle:** Treppen-Griffe (App.tsx grips-memo hat keinen stair-Fall), - Öffnung entlang Host-Wand verschieben (kein opening-Handler im Body-Move; `moveOpeningBy` - fehlt), 2D-Tools auf Zeichnungsebenen (`toolsEnabled` erweitern + Bau-Tool-Buttons grauen), - runde Linienenden (`stroke-linecap:round` auf offene Linien), „schattiert"-Politur - (ACES-Tonemapping+sRGB, HemisphereLight/RoomEnvironment-PMREM, MeshLambert→MeshStandard in - `Viewport3D.tsx` ~416–449). -6. **Smart-Join/Split + NAHT-FREIE Wandverschneidung** (Poché-Union an Stössen, keine sichtbare - innere Naht, Gehrung/T-Stoss). 7. **Text/Stempel-Annotation** (aktiviert „+Text"; - Frame+Rotation). 8. **Welle B** (3D-Schnittebene + Stencil-Capping + Schnittschraffur). - 9. **Welle C** (HLR verdrahten; `src/section/hlr.ts` bereit). 10. Overrides/Ausschnitte/ - Multi-Page-Layouts. - -**Kollisions-Regel bestätigt:** `App.tsx`, `types.ts`, `generatePlan.ts`, `Viewport3D.tsx`, -`registry.ts`, `PlanView.tsx`, i18n, `styles.css` sind Hotspots → nur EIN App.tsx-Agent pro -Welle; parallele Tracks nur auf genuin App-freien neuen Modulen. - -## >>> STAND 2026-07-01 (Bug-Batch Runde 1) — ZUERST lesen <<< - -Runde-1-Agenten alle GRÜN gelandet (`tsc -b` + build + Probe verifiziert): -- **3D-Editier-Kamera-Bug (kritisch, `src/viewport/Viewport3D.tsx`):** Ursache = schwerer - Szenen-Aufbau-Effekt mit `project`-Dependency → jede Griff-Mutation baute Szene neu auf und - `applyView3d` resettete die Kamera auf Preset (→ „springt in Top-View", Rechts/Links-Pan-Illusion). - Fix: `camStateRef`-Snapshot (Pos/Target/up/ortho-Zoom/aktive Kam) im Render-Loop fortgeschrieben, - bei Neuaufbau WIEDERHERGESTELLT statt `applyView3d`; `applyView3d` nur beim ersten Aufbau; - `view3d`-Effekt guarded via `gripDragRef`; Top-View `enableRotate=false`; 2D-Drawings bekommen - `move`-Griff (Anker=center/at) → in 3D verschiebbar. Kamera pos/target numerisch identisch vor/nach - Drag/Löschen bewiesen. -- **Print-Linienstärke invertiert → korrekt (`src/plan/PlanView.tsx`, `styles.css`):** Print zeichnet jetzt - echte Papier-mm in viewBox-Einheiten OHNE `non-scaling-stroke`: `strokeVb=(mm·N/1000)·PX_PER_M`, - N=`paperScale` stabil (nur bei `applyScale`/erstem Messen gesetzt). Rein=dicker, raus=dünner; - 1:100 vs 1:10 = exakt ×10. Display/Haarlinie unverändert. **Ecken-Miter:** CSS `stroke-linejoin:miter` - + `buildDrawingRuns` bündelt zusammenhängende `line`-Primitive gleicher drawingId zu einem - `polyline`/`polygon` (Ringschluss erkannt) → gehrte Ecken statt Butt-Cap-Stufe. Primitive-Modell - (und Print-Export) unverändert. -- **ambientCG-Live-Bibliothek (`src/materials/ambientcg.ts` neu, `ResourceManager.tsx`):** komplette CC0-Lib - live durchsuchbar (Suche/Kategorie/Auflösung 1K-4K), jszip-Entpackung → Blob-URLs, NormalGL bevorzugt. - **CORS:** Such-JSON + `/get` brauchen Proxy → Vite-Dev-Proxy `/ambientcg` in `vite.config.ts` - (`VITE_AMBIENTCG_PROXY` für Prod); optionale Prod-Route `openbureau-core/api/src/routes/ambientcg.js` - (lesend, gemountet VOR Auth-Gate). Thumbnails direkt (ACAO:*). Offline → Schnellauswahl-Fallback. - -**Runde 2 FERTIG (2026-07-01, alle GRÜN + Probe-verifiziert):** -- **2a** [App/engine/editors/registry, neu `cmds/mirror.ts`+`cmds/join.ts`]: Snapping erster Punkt gefixt - (Modifier shift/ctrl wurden im Command-Pfad verschluckt → jetzt via `EngineMods` durch `move/pick`→`host.snap` - gefädelt); Segment-Move für offene Polylinien/Linien (`EdgeGrip.free`, ganzer 2D-Delta, Nachbar-Segmente - folgen über geteilte Vertices); Kürzel↔Befehl vereinheitlicht (`M`→engine.start("move"), `S`→"mirror", - `Ctrl+J`→"join", identisch zum Tippen; `Command.autoRun` für nicht-interaktive Befehle; `CommandSelection.drawingIds` - für Mehrfachauswahl); **Mirror-Befehl** (2-Punkt-Achse, Vorschau, `commitTransform mirror/copy`). -- **2b** [cmds/{polyline,rect,wall}]: Polylinie **offen/geschlossen** (Toggle `m`, Ring-Fill); Rechteck **2pt/3pt/Zentrum** - (3pt = gedreht → closed polyline); **Wandstärke-Tab-Feld** nur bei einschichtigen Wandtypen (baut on-commit - einen einschichtigen Wandtyp der Zieldicke, Original bleibt). -- **Swisstopo/OSM-Kontext-Importer** [io/{lv95,geoContext,swissTopo,osm}, ui/ContextImportDialog, SitePanel]: - „Standort importieren" — geocode (geo.admin SearchServer, LV95 direkt), swisstopo-Gebäude - (`MapServer/identify` layer `ch.swisstopo.vec25-gebaeude`), OSM-Overpass (Gebäude/Strassen/Wasser/Grün). - LV95↔WGS84 (Bern round-trip 0.38 m) + Origin-Shift. CORS: geo.admin direkt, Overpass via neuer read-only-Route - `openbureau-core/api/src/routes/geoproxy.js` (vor Auth-Gate, host-allowlist) wenn `VITE_GEO_PROXY` gesetzt. - -**TIER-1 FORTSCHRITT (2026-07-01):** -- **DXF-Vektor-Export FERTIG** [src/export/{dxfWriter,exportDxf}, ui/ExportDxfDialog, TopBar+App-Button]: - hand-rollierter R2000/AC1015-ASCII-Writer, `$INSUNITS=6` (Meter), 23 Layer aus Kategorien (ACI+True-Color+lw), - LINE/LWPOLYLINE(closed)/CIRCLE/ARC/TEXT; Quelle = `generatePlan()` in Meter-Modellraum. 5 m-Wand→5.345 m Span - verifiziert. DWG NICHT geshippt (libredwg-web nur lesend — ehrlich weggelassen). -- **Decke (Ceiling) FERTIG** [geometry/ceiling, cmds/ceiling(alias d/decke), types.ts `Ceiling`, projectSlice/selectionSlice, - generatePlan `addCeilingPoche`, PlanView-Hittest, Viewport3D `addCeilingMesh`(Extrude+Material+Modi), ToolsPanel-Button+ - Deckentyp-Picker, ObjectInfoPanel `CeilingSection`, host.ts, App-Grips/Selection]. Wand-Muster: floorId + categoryCode "30" - + wallTypeId/Component + VerticalAnchor(OK=Geschoss-Top) + optionale Dickenüberschreibung; statt Achse/Band ein - closed `outline: Vec2[]`. Plan = Umriss-Polygon (Fill+Hatch + schwere Outline) VOR den Wänden gezeichnet. Grips Vertex+Edge+Body. -- ⚠️ MERKE: **App.tsx ist der Serialisierungs-Engpass** — pro Welle darf NUR EIN Agent App.tsx editieren (DXF+Decke - liefen versehentlich gleichzeitig darauf; Merge hat diesmal geklappt, aber nicht drauf verlassen). - -- **Öffnung (Fenster/Tür) FERTIG** [geometry/opening, cmds/opening(alias f/fenster, t/tuer/tür, oe), types.ts `Opening`, - generatePlan(`wallGaps`), PlanView, Viewport3D(`addWallMeshes` segmentiert + Sturz/Brüstung, `addOpeningMeshes` Rahmen/Glas/Flügel), - slices, App, ToolsPanel(Fenster+Tür-Buttons), ObjectInfoPanel(editierbar)]. **Host-Sync:** Öffnung speichert KEINE - Weltkoordinaten — alles wird pro Render aus `wall.start/end` neu abgeleitet → Wand verschieben bewegt Öffnung automatisch. - Plan-Symbole je Detailgrad (Tür: Blatt→+Schwenkbogen→+Anschlag; Fenster: 1→+Rahmen→+2 Glaslinien). -- **SIA-416-Engine FERTIG** [geometry/{roomArea,roomBoundary} — pure, isoliert, 34/34 Tests]: Raumerkennung - (planar-arrangement+half-edge face-tracing, `detectRooms`/`roomFromPointInside`), Fläche/Umfang/Zentroid, - SIA-Hierarchie (GF→KGF+NGF; NGF→NF(HNF/NNF)/VF/FF), `balance()`, `roomsToCsv`. Bereit für den Raum-Slice. - -- **Treppe FERTIG** [geometry/stair, cmds/stair(alias treppe/tp — `tr`=trim!), types.ts `Stair`, model/wall `stairVerticalExtent`, - generatePlan `addStairSymbol`(Schnittbruchlinie+Pfeil), Viewport3D `addStairMeshes`(Stufenprofil), slices/App/Panels/i18n]. - gerade/L/Wendel (Wendel = vereinfachtes Keil-Modell), SIA-17/29, geschoss-übergreifend (totalRise=Geschosshöhe). -- **HLR-Spike FERTIG (Welle-C-Fundament)** [src/section/{hlr,occt,occt-wasm.d}.ts, docs/welle-c-hlr-spike/, package.json - +opencascade.js, vite.config aliases]. **API-Pfad = `HLRAppli_ReflectLines`** (NICHT HLRBRep_Algo — im Prebuilt nicht - konstruierbar). Lazy via dynamic import, Haupt-Bundle unberührt. 62 MB WASM → später Custom-Build auf 5-15 MB. - -**SESSION-LIMIT-STOP (2026-07-01 ~11:00, reset 13:00 Europe/Zurich):** Raum- + Rich-Text-Agenten wurden vom -Session-Limit abgeschnitten. Repo ist trotzdem GRÜN & stabil (tsc+build grün, dev :5187 läuft). Genauer Stand: -- **Raum: NICHT begonnen** — kein `cmds/room.ts`, kein `Room`-Typ, keine Verdrahtung. Sauber neu starten. SIA-Engine - (`geometry/roomArea.ts`+`roomBoundary.ts`) liegt fertig & getestet bereit zum Import. -- **Rich-Text: FAST FERTIG** — `src/text/richText.ts` (Doc-Modell + Marks + plainText/applyMark/toggleMark/serialize/Presets) - FERTIG. `src/text/renderHtml.ts` (docToHtml für Editor + docToLines/docToSvgText für SVG-Plan, gemeinsame Mark→Stil-Map) - JETZT FERTIG & kompiliert (im Hauptthread nachgezogen, da Agent am Limit war). FEHLT nur noch: `src/text/RichTextEditor.tsx` - (WYSIWYG-Komponente mit Toolbar bold/italic/underline/strike/Grösse/Farbe + Presets, controlled: value/onChange). -- Zwei Helfer-Subagenten ("Map/Extract …wiring in App.tsx") liefen ebenfalls ins Limit; App.tsx ist laut tsc+build INTAKT. - -**RESUME NACH RESET (Reihenfolge):** (1) Rich-Text-Kern fertig (renderHtml + Editor-Komponente) — App-frei, isoliert. -(2) Raum-Slice (nutzt SIA-Engine, click-inside, Stempel, Bilanz+CSV) — App-Slice. (3) Text/Stempel-Slice (nutzt Rich-Text-Kern). -(4) Smart-Join/Split. (5) Welle B (3D-Schnitt+Stencil-Capping) → Welle C (HLR verdrahten, `src/section/hlr.ts` bereit). -(6) Overrides-Engine, Ausschnitte, Multi-Page-Layouts. REGEL: nur EIN App.tsx-Agent pro Welle; Parallel-Track nur wenn App-frei. - ---- - -## >>> STAND 2026-06-30 (Fortsetzung, spät) <<< - -Fortsetzungs-Lauf (UI + Import + Wand-Attribute + Selektion/Editieren). Alles -unten ist `npx tsc -b` + `npm run build` GRÜN und per Screenshot/Probe verifiziert, -außer „LÄUFT" markiert. Viel wurde an **Subagenten** delegiert (Dateien landen -auf der Platte, unabhängig von Benachrichtigungen). - -**Fertig & verifiziert (diese Fortsetzung):** -- **Vektor-PDF-Export des Grundrisses (KERN-ZIEL — fertig & verifiziert):** - `src/export/planToPrintSvg.ts` (Druck-SVG aus `generatePlan`, mm-Mapping im Maßstab, - echte mm-Linienstärken/ISO-Stifte, Schraffuren als Vektorlinien) + `src/export/exportPdf.ts` - (`jspdf`+`svg2pdf.js` → echtes Vektor-PDF, A4/A3 ×Hoch/Quer, Schriftfeld). TopBar-Button - „PDF" + `src/ui/ExportPdfDialog.tsx`. Verifiziert: 522 Pfade, 0 Rasterbilder, maßstabsgenau - (5,345×4,345 m → 53,45×43,45 mm @1:100). `scripts/probe-pdf.mjs`. -- **2D-Boolean (Union/Differenz/Schnitt):** `src/editors/booleanOps.ts` (polygon-clipping), - Kontextmenü „Vereinigen/Subtrahieren/Schneiden" + Ctrl/Cmd+Shift+U/D/I; geschlossene - Drawing2D, Differenz=erstes Element minus Rest, Loch→Außenring gefüllt + Innenringe als - Umriss. Verifiziert (Union 8/Differenz 6/Schnitt 4 Ecken). `scripts/probe-boolean.mjs`. -- **3D-1 — ERSTELLEN in 3D (Meilenstein):** der 3D-Viewport ist Erstellungs-Fläche; - dieselben Befehle (line/wall/rect/polyline) erzeugen Elemente direkt in 3D. Raycast auf - Geschoss-Arbeitsebene (`THREE.Plane` y=gridElevation) → Modell-`Vec2` (model.x=P.x, - model.y=P.z, aus `addDrawing2DLines` abgeleitet) → dieselbe `CommandEngine` - (`move/pick/confirm`). Live-3D-Draft (Rubber-Band + Snap-Marker, eigene `draftGroup`), - OrbitControls bei aktivem Befehl umgeschaltet, `pxPerMeter`-Fallback 90. Viewport3D-Props - `commandActive`/`onWorkplanePoint`/`onWorkplaneConfirm`/`draft`. Verifiziert (Wand in 3D → - in 3D + Plan sichtbar). `scripts/probe-3d-create-*.png`. -- **3D-2 — EDITIEREN in 3D (Meilenstein):** 3D-Griffe (eigene `gripGroup`) bei genau 1 - Auswahl + `!commandActive`. Wand: Endpunkt-Kugeln (Achse), Höhen-Griff (OK via - `updateWall top:{mode:"custom",z}`), Verschiebe-Griff; Drawing2D: Vertices. Drag raycastet - auf Arbeitsebene bzw. vertikale Ebene → DIESELBEN Store-Actions wie Plan-Griffe - (`moveGripOf`/`moveElementByOf`/`updateWall`) → Plan+3D konsistent. Laufender Drag in - `gripDragRef` (übersteht den Szenen-Neuaufbau pro Move). Verifiziert (Endpunkt + Höhe in 3D - editiert, im Plan deckungsgleich). `scripts/probe-3d-edit-*.png`. **Offen (→ Editier-Welle 3): - 3D-Snap beim Drag + Griffe „immer oben" (Tiefentest).** -- **Zwei-Ton-Dark-Theme + KEIN Petrol-Grün mehr:** `src/styles.css` Dark-Tokens neutral - (`--bg #0e0e0e`, `--panel #1d1d1d`, `--accent #4d4d4d` …); Kontextmenü-Tokens - `--ctx-hover/-text` neutral; grüner Fokus-Ring entfernt. -- **Topbar DOSSIER-Stil:** Icon-Grid 4-oben/3-unten (Grundriss integriert), Kombis - gestapelt, kompakte Selects. `src/ui/TopBar.tsx`. -- **Themed Dropdowns** statt nativer `