73 KiB
HANDOVER — Browser-BIM (cad), Standalone-Port von DOSSIER
Für die nächste Instanz. Stand: 2026-06-29. Lies zuerst
CONVENTIONS.md,ROADMAP.mdund 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 logundgit difffrei 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.tsist 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:
7ca5197Resource Manager: Materialien-Tab (PBR-Kugel-Vorschau via geteiltem Offscreen- Renderer, Suche + Kategorie-Chips, lazy per IntersectionObserver).c8a4188render3d als WASM/WebGPU-Viewport in der App (?engine=wasmbzw. localStoragecad.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).54211b1+76029dbrender3d/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).ce6bd262D-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.4b02e40render2d Headless: Featureheadless, HeadlessRenderer ohne Surface (Vulkan), CLI-Bin render_png, Golden-Test (bit-exakt) tests/golden/demo.png, Doku docs/design/engine-headless.md.382771bVektor-PDF aus der RenderScene (Nordstern 2 abgeschlossen): exportPdf baut jetztplanToRenderScene(plan)— derselbe Aufruf wie der Viewport — und serialisiert über den neuensrc/export/sceneToPrintSvg.tsnach 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.tsbleibt nur als Referenz stehen (Kopfkommentar markiert sie als abgelöst). Verifiziert perpdftoppm: 53.51×43.69mm vs. erwartet 53.45×43.45mm bei 1:100. Bekannte Alt-Lücke, nicht neu: Zeilenabstand mehrzeiliger Stempeltexte intoRenderScene.tsist 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).b4c4a2cwgpu 22 → 29, glyphon 0.6 → 0.11 (glyphon pinnt^29, obwohl wgpu 30 existiert).src/engine/requestDeviceShim.tskomplett entfernt (das 22er-LimitmaxInterStageShaderComponentswird 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:
- [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 ausce6bd26gilt 1:1). - 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.
- 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. nachengine/odercrates/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. - [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.
- 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:
-
Papier-mm-exakte Strichbreiten überall — Bildschirm bei jedem Maßstab/Zoom = Druck.
-
Deterministische Plan-Ausgabe: Druck/PDF mm-genau identisch zum Bildschirm, beides aus render2d (kein separater Druckpfad). Vektor-PDF-Backend = dieselbe Szene, anderes Target.
-
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).
-
HLR/Schnitt-Pipelines durch die Engine (render3d → Kantenextraktion → render2d-Szene).
-
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. -
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). <<<
electronals devDep,scripts/electron-main.cjs(BrowserWindow,Menu.setApplicationMenu(null)
frame:false/autoHideMenuBarfür randloses Fenster wie chromium-shell.sh, WebGPU-Flagsenable-unsafe-webgpu+enable-features=Vulkan),scripts/electron-shell.sh(Dev-Server-Guard wie chromium-shell.sh), npm-Scriptnpm run electron. Verifiziert per Screenshot: App startet als eigenes randloses Fenster, lädt den echten Grundriss (nicht leer). WICHTIG:
- Datei muss
.cjssein (package.jsonhat"type":"module", Electron-Main läuft als CJS). ELECTRON_RUN_AS_NODE=1kann 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,...}→appist undefined). Immer mitenv -u ELECTRON_RUN_AS_NODEstarten, falls das Fenster nicht öffnet /app.commandLineundefined ist.- WebGPU/
?engine=wasmfunktioniert 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 allesenabled/enabled_oninkl.webgpuundvulkan(AMD RX 7800 XT/RADV). KEINE Ozone-Platform-Flags nötig,--ozone-platform=x11NICHT verwenden (bricht die Fenstererstellung, Vulkan-Surface/GetGeometry-Fehler).electron --versionmeldet v24, weilELECTRON_RUN_AS_NODE=1den Prozess in den Node-Modus zwingt und dann Nodes eigene Bundle- Version zeigt (kein echter Versions-Mismatch —env -u ELECTRON_RUN_AS_NODE electron --versionmeldet 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<svg viewBox>; Pan = setView, Zoom = setView(onWheel). BEREITS optimiert:primitiveEls/drawingRunElsuseMemo(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-strokedie 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 voncomputeJoins(aussrc/model/joins.ts) inkl. Vektor-Helfer. Input flach:WallInput{ id,start,end, thickness,referenceOffset }; OutputWallCuts{ wallId,startCut,endCut }(Line= point+dir).cargo testgrü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.rsmit#[tauri::command] compute_joins+run(),main.rs,build.rs,tauri.conf.jsondevUrl :5187,icons/icon.png).cargo checkGRÜN — wider Erwarten NICHT von webkit blockiert: tauri v2 bindetwebkit2gtk-4.1(vorhanden); nur-6.0fehlte.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/Fehlerconsole.warn+ TS-Fallback (joins.ts). Zusätzlich DurchreicherdetectRooms(roomBoundary.ts) +parseShapeFromDwg(=parseDwg);computeKernel2Dbewusst ausgelassen (kein Einzel-Entry, ~30 Primitive).package.json/vite.config.ts:@tauri-apps/cli+api(v2) installiert, Scriptstauri/tauri:dev/tauri:build; vite Port 5187strictPort,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, 5ParametricRuleVarianten) + Engine (resolveParametricWall(), 5 rule handlers, helpers) insrc/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.mdaktualisiert - Verification:
tsc -b✓,npm run build✓ (577 modules, 3.72s)
- Agent 1: Types (
- 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 (nurtypes.ts+src/model/erweitert, nichtApp.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.tsconfirm()): 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) stattonConfirm(das den Zug committet) aufzurufen. Fix: Feld-Zweig nur noch bei tatsächlich GELOCKTEM Feld (`Object.keys(this.locks).length0
); ohne Lock fälltconfirm()aufonConfirmdurch. 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 -b0,build` grün, keine Konsolenfehler. KEIN Commit. -
Backlog #1 ERLEDIGT (Befehlsleiste-Transform vereinheitlicht): Die laufende Auswahl-Transformation (nur noch
rotate/Tastedmit U/I/O/P; move/mirror laufen längst über die Engine) speist jetzt dieCommandLinestatt der schwebendenTransformBar. Dritter Zweig in der<CommandLine>-Ternäre parallel zugripEdit(gripEdit ? … : activeTransform ? … : engineView…): Prompt =transform.hint.${op}.${…}, Inline-Optionen = U/I/O/P (transformOptions(), aktiver Modus hervorgehoben via neuemactive?-Feld anCommandLineOption+.cmdline-option.activein styles.css,×countbei array/distribute), Live-/ lockbares Winkel-Feld beim Drehen bzw. Distanz beim Bewegen (transformFields()aus neuemtransformDragInfoRef), getippte Zahl committet übersubmitTransformValue().dfokussiert die CommandLine (wiem/s).src/ui/TransformBar.tsxgelöscht; verwaiste.transform-bar/.tf-*-CSS +transform.bar-Key harmlos stehen gelassen. Gegatet:tsc -b0 Fehler,buildgrün, Trace-Scan sauber, Puppeteer-Probe end-to-end (nachd: „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:
Roominmodel/types.ts(id, floorId, categoryCode "60", siaCategory HNF/NNF/VF/FF/KGF, boundary, stampAnchor?, stampDoc?);cmds/room.ts(Modus inside=click-inside viaroomFromPointInside, manual=Polylinie); Plan: neuestext-Primitive + Füllpolygon + Stempel viadocToLines+ 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 (neueSegment-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 öffnetTextEditorDialog. 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):
- Befehlsleiste-Transform vereinheitlichen: laufende Transformation (move/mirror/rotate)
soll die
CommandLinespeisen (Prompt „Basispunkt/Zielpunkt" + U/I/O/P als Inline- Optionen) statt der separaten schwebendenTransformBar; freies Befehl-Tippen während der Geste unterdrücken, Zahleneingabe (Distanz/Winkel) viafieldsbleibt. Muster: parallel zum bestehendengripEdit-Zweig bei<CommandLine>(~App.tsx:2793). U/I/O/P-Routing (App.tsx ~1543–1548) NICHT kaputt machen — nur zusätzlich in die CommandLine spiegeln. - 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). - Wand-Optionen: Referenzlinien-Option UNTER den Bezugspunkt setzen, gleiche Optik/
Anordnung wie die übrigen Optionen (
cmds/wall.ts/ ToolsPanel-Optionsreihenfolge). - Zeichnen im 3D-View: Cursor per Raycast auf die Arbeitsebene (okff des Geschosses)
projizieren → Weltkoordinaten als
engine.move/pickeinspeisen;toolsEnabled-Gate (App.tsx ~1834, heuteviewType==="grundriss") auch für Perspektive öffnen. - Viewport/Editier-Welle: Treppen-Griffe (App.tsx grips-memo hat keinen stair-Fall),
Öffnung entlang Host-Wand verschieben (kein opening-Handler im Body-Move;
moveOpeningByfehlt), 2D-Tools auf Zeichnungsebenen (toolsEnablederweitern + Bau-Tool-Buttons grauen), runde Linienenden (stroke-linecap:roundauf offene Linien), „schattiert"-Politur (ACES-Tonemapping+sRGB, HemisphereLight/RoomEnvironment-PMREM, MeshLambert→MeshStandard inViewport3D.tsx~416–449). - 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.tsbereit). 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 mitproject-Dependency → jede Griff-Mutation baute Szene neu auf undapplyView3dresettete 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 stattapplyView3d;applyView3dnur beim ersten Aufbau;view3d-Effekt guarded viagripDragRef; Top-ViewenableRotate=false; 2D-Drawings bekommenmove-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 OHNEnon-scaling-stroke:strokeVb=(mm·N/1000)·PX_PER_M, N=paperScalestabil (nur beiapplyScale/erstem Messen gesetzt). Rein=dicker, raus=dünner; 1:100 vs 1:10 = exakt ×10. Display/Haarlinie unverändert. Ecken-Miter: CSSstroke-linejoin:miterbuildDrawingRunsbündelt zusammenhängendeline-Primitive gleicher drawingId zu einempolyline/polygon(Ringschluss erkannt) → gehrte Ecken statt Butt-Cap-Stufe. Primitive-Modell (und Print-Export) unverändert.
- ambientCG-Live-Bibliothek (
src/materials/ambientcg.tsneu,ResourceManager.tsx): komplette CC0-Lib live durchsuchbar (Suche/Kategorie/Auflösung 1K-4K), jszip-Entpackung → Blob-URLs, NormalGL bevorzugt. CORS: Such-JSON +/getbrauchen Proxy → Vite-Dev-Proxy/ambientcginvite.config.ts(VITE_AMBIENTCG_PROXYfür Prod); optionale Prod-Routeopenbureau-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 viaEngineModsdurchmove/pick→host.snapgefä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.autoRunfür nicht-interaktive Befehle;CommandSelection.drawingIdsfü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/identifylayerch.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-Routeopenbureau-core/api/src/routes/geoproxy.js(vor Auth-Gate, host-allowlist) wennVITE_GEO_PROXYgesetzt.
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, generatePlanaddCeilingPoche, PlanView-Hittest, Viewport3DaddCeilingMesh(Extrude+Material+Modi), ToolsPanel-Button+ Deckentyp-Picker, ObjectInfoPanelCeilingSection, 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.
- wallTypeId/Component + VerticalAnchor(OK=Geschoss-Top) + optionale Dickenüberschreibung; statt Achse/Band ein
closed
-
⚠️ 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(addWallMeshessegmentiert + Sturz/Brüstung,addOpeningMeshesRahmen/Glas/Flügel), slices, App, ToolsPanel(Fenster+Tür-Buttons), ObjectInfoPanel(editierbar)]. Host-Sync: Öffnung speichert KEINE Weltkoordinaten — alles wird pro Render auswall.start/endneu 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.tsStair, model/wallstairVerticalExtent, generatePlanaddStairSymbol(Schnittbruchlinie+Pfeil), Viewport3DaddStairMeshes(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, keinRoom-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 ausgeneratePlan, 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.Planey=gridElevation) → Modell-Vec2(model.x=P.x, model.y=P.z, ausaddDrawing2DLinesabgeleitet) → dieselbeCommandEngine(move/pick/confirm). Live-3D-Draft (Rubber-Band + Snap-Marker, eigenedraftGroup), OrbitControls bei aktivem Befehl umgeschaltet,pxPerMeter-Fallback 90. Viewport3D-PropscommandActive/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 viaupdateWall 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 ingripDragRef(ü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.cssDark-Tokens neutral (--bg #0e0e0e,--panel #1d1d1d,--accent #4d4d4d…); Kontextmenü-Tokens--ctx-hover/-textneutral; 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
<select>:src/ui/Dropdown.tsx(Wert- + Aktions- Modus, Popover im Kontextmenü-Stil, kein blaues OS-Menü). In TopBar verdrahtet. -
Kontextmenü/Dropdown-Politur: Material-Symbols-Icon-Font in
index.htmlgeladen.material-symbols-outlined-Regel in styles.css (sonst Roh-Ligaturtext); Größen runterskaliert (.ctx-menu11.5px, Icons 13–14px, weniger Padding, Radius 10).
-
Geschoss-Z-Raster:
Viewport3DBodenraster aufgridElevation(= aktives GeschossbaseElevation). -
DXF/DWG-Import als DIALOG + Drag&Drop:
src/ui/ImportDialog.tsx,src/io/dxfToDrawings.ts; Ziel-Zeichnungsebene wählen / neue „Zeichnung" anlegen; Layer→Kategorie-Handling; App-weiter Drop-Handler. -
DWG in-app parsen:
@mlightcad/libredwg-web(WASM, installiert),src/io/dwgParser.ts(lazy, mappt DwgDatabase→{meshes,contours}),vite.config.ts-Aliase für WASM (virtual:libredwg-glue+?url),src/io/libredwg-web.d.ts. Gegen echte DWGs getestet (11.668 Konturen). Fallback→ODA-Hinweis bei Lib-Fehler. -
Wand-Objekt-Info-Attribute (additiv, abwärtskompatibel): Modell
Wall.referenceLine(left|center|right, default center) +Wall.bottom/top: VerticalAnchor({mode:"floor",floorId,offset?}|{mode:"custom",z}) insrc/model/types.ts; Resolversrc/model/wall.ts(wallReferenceOffset,wallVerticalExtent,nextFloorAbove); angewandt ingeneratePlan,model/joins.ts,Viewport3D. Panelsrc/panels/ObjectInfoPanel.tsx;src/state/selectionInfo.tsSelection.wall: WallInfo; Host-Setter +projectSliceupdateWall/setWallThickness. Einschichtig = 1-Layer-WallType. -
Panel-Text:
.attr-*/.objinfo-*Untertitel fett + Kontrast (--labelstatt--muted). -
Selektion-Überarbeitung:
selectedDrawingId→selectedDrawingIds[](selectionSlice); Marquee wählt Linien (marqueeHitDrawings), Ctrl/Cmd+A, Multi-Delete, Multi-Highlight (.plan-sel-draw), Shift-Klick-Mehrfachauswahl (PlanSelection.shift→onPlanSelecttoggelt). -
Editier-Welle 2 — Split/Join/Segment-Löschen (FERTIG & verifiziert): Ctrl+S Split (Rechteck→zwei geschlossene; nur bis Schnittpunkt), Ctrl+J Join (koinzidente Enden), Alt+Klick=Segment löschen (Schere).
kernel2d.ts(splitPolylineAtParam/splitClosedByChord/removeSegment/splitAtIntersections/joinChains; Tests 16–23, 25 ok),src/editors/splitJoin.ts, App-Keydown (Ctrl/Cmd+S/J,preventDefault) +onSegmentCut, PlanView Alt-Klick. Verifiziert (Rechteck→2 geschlossene, Join→1 Polylinie, Alt→Segment weg).scripts/probe-splitjoin.mjs. Wände bleiben no-op. -
Editier-Welle 3 + Eingabe-Vereinheitlichung + Snap-Feinschliff (FERTIG): 3D-Drag-Snap (gleiche
computeSnap) · Griffe/Marker immer-oben · koinzidente Endpunkt-Griffe ziehen gemeinsam (Viewport3D). GUI-Werkzeug (line/polyline/rect) →engine.start+ Befehlsfeld-Fokus; Shift = H/V-Ortho beim Griff-Drag (2D+3D,applyAngleConstraintaustools/snapping). OFFEN: „Wand" noch Legacy (Wand-als-Command); Editieren läuft noch AD-HOC, nicht durch die Engine → Tab-Wert + Referenz-Tracking offen. Snap-Marker vereinheitlicht: 2D Endpunkt=Kreis/Mittelpunkt=Strich, 3D-Snap=#ffcc44, Marker kleiner; 2D-Vorschaulinie grau-Volllinie. -
DWG-Parser erweitert (FIX „keine Geometrie gefunden") + Trim (FERTIG):
io/dwgParser.tsneu — ARC/CIRCLE/ELLIPSE/SPLINE + INSERT/Block-Expansion (rekursiv, ausdb.tables.BLOCK_RECORD.entries, volle 2D-Affin-Transform) + Diagnose-Histogramm (DxfImportResult.diagnostics, in ImportDialog). Real-DWG 37 INSERT → 67k Konturen. Trim (commands/cmds/trim.ts, Aliastr,kernel2d.trimPolyline) — Quick-Trim bis Schnittpunkt. Beide verifiziert. -
Material-Bibliothek (Daten, FERTIG): 12 CC0-Materialien von ambientCG →
public/assets/materials/<id>/{color,normal,roughness,displacement,ao}.jpg+manifest.jsonsrc/materials/library.ts(PBR).
-
Welle A — 3D-Materialien (FERTIG & verifiziert):
Component.material(ComponentMaterial: PBR-Map-URLs +sizeMKachelgröße),src/materials/runtime.ts(MaterialRuntime→ gecachteMeshStandardMaterial, Kacheln in Welt-Metern via Extrude-UVs),renderMode„textured" (TopBar-Darstellung), Material-Picker (ambientCG-Grid) + Upload + Kachelgröße imResourceManager(Bauteile-Tab). Backstein mit Normal-Map-Tiefe verifiziert.scripts/probe-materials.mjs. Schnittschraffur-Slot frei für Welle B. -
Editier-Eingabe + Topbar-Politur (FERTIG): Befehlszeile zeigt Live-Länge/-Winkel beim Zeichnen (
Command.fieldValues), Cursor-HUD entfernt; Seiten-Verschieben propagiert verbundene Enden (2D+3D); Tab beim Vertex-Editieren setzt Wert (gripEdit-Controller in App — noch ad-hoc, NICHT durch die Engine). Topbar: Rest iconisiert (Referenzlinien/Linien-Modus/Ressourcen), Linien-Modus → Dropdown, Aktions- Buttons als Dropdown-Pill-Familie, Detailgrad+Massstab gestapelt + 1:N/Zoom% kompakt (.tb-zoomstack). -
Wand-als-Command (FERTIG & verifiziert):
commands/cmds/wall.ts(chainend, Achs+Band- Draft, Tab-Felder; Wandfelder ausCommandContext.activeWallTypeId/level) + Registry (Aliasew/wand) + App-Kopplung (TOOL_COMMAND.wall, Legacy-Wand umgeleitet). Wände in 2D+3D.scripts/probe-wall-cmd.mjs.
OFFENE WELLEN (nächste Schritte): Referenzobjekt-Achs-Tracking + Editieren VOLL durch die
Engine (heute gripEdit ad-hoc); Welle B (3D-Clip/Schnittebene + Stencil-Capping +
Schnittschraffur — nutzt das Material-System aus Welle A); Welle C (3D→2D-Schnitt/
Perspektive via HLR, opencascade.js-Spike); Swisstopo-Importer (Ort+Radius → Gebäude+
Gelände, geo.admin STAC/Geocode, CORS prüfen); Bögen/Kreise-Trim; Wandstärke-Tab bei
einschichtigen Wänden.
Offene Wellen (Todo-Spiegel — Todo-Liste lebt im Kontext):
- Editier-Welle 3: koinzidente Endpunkt-Griffe gemeinsam ziehen + verbundene Enden propagieren beim Seiten-Verschieben (topologisch, das Tiefste).
- GUI-Werkzeug ↔ Befehlszeile koppeln: Werkzeug-Klick startet denselben Command → Werte unten eintippbar; danach Cursor-Wert entfernen/optional.
- Selektions-Direkt-Edit: Länge/Winkel des gewählten Elements im Befehlsfeld (Tab-Zyklus).
- Swisstopo/openbureau-Importer-Dialog (Ort+Radius). Fluss steht: Geocode
api3.geo.admin.ch/.../SearchServer(sr=2056→LV95) → ±Radius → WGS84-bbox → STACdata.geo.admin.ch/api/stac/v1, Collectionsch.swisstopo.swissalti3d+ch.swisstopo.swissbuildings3d_3_0→ Download → TIN/Mesh→Kontext. LV95↔WGS84 nötig; Browser-CORS das Risiko. Referenz: DOSSIERrhino/swisstopo.py. - 3D→2D-Dokument: live + statisch (MVP-Kantenprojektion → HLR) für Vektor-PDF.
Hinweis:
generatePlanliefert SCHON Vektoren (SVG) → Plan-PDF bereits vektoriell. - Topbar-Rest iconisieren (Zoom/Referenzlinien/Linien-Modus/Ressourcen).
- Wand als Command (+ Wandstärke-Tab nur bei einschichtig) + Trim.
Resume-Checkliste: 1. CONVENTIONS.md + diesen HANDOVER + Memory lesen. 2. npx tsc -b &
npm run build (grün?). 3. npm run dev -- --port 5187 --strictPort (User schaut :5187).
4. Split/Join-Stand prüfen (git diff, Probe). 5. Arbeitsweise: autonom, keine
Rückfragen (User hat Vollrechte), substanzielle Arbeit an Subagenten delegieren,
jede Änderung per tsc+build+Screenshot verifizieren. node scripts/probe.mjs.
>>> STAND 2026-06-30 (Über-Nacht-Run) — älter <<<
Großer autonomer Build-Lauf. Alles unten ist tsc + build GRÜN und per Screenshot
verifiziert (sofern nicht „läuft" markiert). Befehlssystem-Bauplan:
docs/design/rhino-command-system.md.
Fertig & verifiziert:
- Rhino-Befehlssystem (Tier 0 + Tier 1 Zeichnen):
src/commands/—types.ts(Command-Interface),engine.ts(State-Machine + lastCommand + Eingabe-Routing),parseInput.ts(5,3·r5,3·5<45·<45·nackte Zahl),registry.ts(+Aliasel/pl/rec/c),cmds/{line,polyline,rect,circle}.ts. UI:src/ui/CommandLine.tsx(über der Statusleiste, Tab fokussiert). Befehle: Line, Polyline (Close/Undo), Rectangle, Circle — alle mit getippten Koordinaten, per Probe gezeichnet. Koexistenz mit Alt-Tools (Werkzeugleiste).scripts/probe-command-line.mjs,probe-draw-commands.mjs. - Tab-Feld-Zyklus (Rhino-Präzision §2.7): beim Zeichnen Tab durch Felder
(Linie/Polylinie: Länge→Winkel; Rechteck: Breite→Höhe; Kreis: Radius). Zahl lockt
das aktive Feld, ungelockte folgen der Maus. Additive optionale Command-Methoden
fields()/pointFromFields()+ Engine-Feldzustand + CommandLine-Boxen. Verifiziert exakt (3 m/45°, Rechteck 4×2).scripts/probe-tab-fields.mjs. - Tier-1-Editierbefehle: Move (Auswahl verschieben), Copy (wiederholend
duplizieren), Offset (parallele Kurve via
kernel2d.offsetPolyline, persistente Distanz, Seite per Klick) —cmds/{move,copy,offset}.ts, Aliasem/cp/o.CommandContextumselectionerweitert (Apphost.context()füllt sie aus dem Store). Offset verifiziert (paralleler Versatz exakt 0.5 m).scripts/probe-offset2.mjs. - 2D-Geometrie-Kernel
src/geometry/kernel2d.ts— Offset/Trim/Extend/Fillet + Segment-/Linien-/Kreis-Schnitt + Fläche/Wicklung. 16/16 Unit-Checks (scripts/test-kernel2d.ts, vianpx esbuild … | node). Basis für Tier-1-Editierbefehle. - Kontext/Gelände Phase 1 (paralleler Strang):
src/io/dxfParser.ts(DXF: 3DFACE/ MESH/POLYLINE→Mesh, LWPOLYLINE/LINE→Konturen; DWG bewusst nur via ODA→DXF-Hinweis),src/model/terrain.ts(generateTerrainFromContours→ TIN via delaunator),src/state/siteSlice.ts(addContextObject/generateTerrain/…),Project.context(ContextObject = ImportedMesh|ContourSet|TerrainMesh, „dumme" Kontext-Geometrie, NICHT semantisch),Viewport3Drendert Kontext/Terrain. Depsdxf-parser+delaunator. - Echte Isometrie (Orthographic-Kamera):
Viewport3Dschaltet front/top/side/iso auf OrthographicCamera (parallele Projektion), Perspektive bleibt perspektivisch. Verifiziert. - Plan-Tinte-Fix: 2D-Linien waren unsichtbar (CSS
.draw2d{stroke:var(--ink)}hell auf hellem Papier). CSS-stroke entfernt + dunkler Fallback in generatePlan → 2D dunkel sichtbar. - Bugfixes: 2D-Füllungen (rect/polyline rendern Fill+Schraffur+
fillColor); Kanten-Griff- Dreiecke sitzen auf der Linie + inkrementelles Dragging (keine Akkumulation); Plan-View wandert nicht mehr mit (fixer Welt-Ursprung, Reset nur bei Geschoss-Wechsel viaresetKey).
LÄUFT gerade (Agent):
- Tab-Feld-Zyklus (Nutzer-Wunsch): beim Zeichnen Tab durch Felder (Länge→Winkel→
Breite/Höhe/Radius), Zahl lockt Feld, Rest folgt Maus. Additive optionale Command-Methoden
fields()/pointFromFields()+ Engine-Feldzustand + CommandLine-Boxen.
NÄCHSTE SCHRITTE (Reihenfolge):
- Tab-Feld-Zyklus verifizieren (Agent-Gate).
- Tier-1-Editierbefehle: Move/Copy/Offset(nutzt kernel2d)/Trim/Split/Join/Explode.
Undo/Redo-Stack im Store erwägen (Commits laufen über
setProject). - Terrain-Integration: Konturen im Plan (generatePlan/PlanView), Import-/Terrain-Befehle
- Site-Panel (
src/panels/) + i18n + DWG→DXF-Hinweis-UI;.dxf-File-Picker →parseDxf.
- Site-Panel (
- Wand-Stärke-Feld: Wand ist noch ein Alt-Tool — als Command portieren, dann Tab-Feld
thicknessNUR bei einschichtigen/freien Wänden (mehrschichtige Typen: Stärke vorgegeben). - Tier 2 (Rotate/Scale/Mirror/Arc/Fillet/Array/Group/Gumball2D), Tier 3 (ExtrudeCrv/Box/ PushPull + analytische Wand-Öffnungen, §3.0 Bauplan).
Gotchas (frisch gelernt):
- Ein Prozess-Neustart killt laufende Hintergrund-Agenten (Status
failed); nach jedem Wiederaufnehmensrc/commands/-Existenz +npx tsc -bprüfen, Abgestürztes neu starten. - Inline
{...s, feld}als Tupel-Return löst TS-Excess-Property-Check aus → erst typisierte Variable (siehe cmds/line.ts-Muster). - Ein Subagent ist bei diesem Befehls-Build geflaket (spawnte Research statt zu bauen) → verzahnte/quer schneidende Arbeit an einen fähigen Subagenten delegieren oder selbst machen.
Was das ist
Standalone-Browser-BIM (React+TS+Vite+Three.js) für Wohnbau — die Browser-Variante
des Rhino-Plugins DOSSIER (Referenz-Repo, public/klonbar: https://git.kgva.ch/karim/DOSSIER;
falls weg: neu klonen nach scratchpad/DOSSIER). Prinzip: ein semantisches Modell →
alle Sichten (Plan/3D/Schnitt) abgeleitet, Darstellung erst beim Rendern.
Aktueller Stand (tsc + build GRÜN)
Funktioniert & verifiziert: semantisches Modell · mehrschichtige Wände mit
L-Ecken-Gehrung · Tür mit Öffnung + Schwenkbogen · Dokumentmodell (Zeichnungsebenen
= Geschosse+Schnitte/Ansichten/Zeichnungen; Ebenen = Kategorie-Baum, DOSSIER-Codes 1:1) ·
3D zeigt EG+OG gestapelt · Resource-Manager (Component/Hatch/Line, als Fenster-Overlay) ·
Panel-System (Docks links/rechts, Tabs, 5 Anzeige-Modi) · Top-Bar + Footer (Massstab
echt 1:N, Detailgrad, Render-Modus Schattiert/Draht/Kanten, Referenzlinien, Cursor X/Y live) ·
Maus: Mitte=Pan/Orbit, Links=Auswahl(+Marquee), Rechts=eigenes Kontextmenü · Native-App
(kein Browser-Rechtsklick/Textauswahl) · i18n (src/i18n/, de+en, t('key')) ·
aktive Zeichenwerkzeuge (Wand/Linie/Polylinie/Rechteck + Snapping endpoint/midpoint/
intersection/onEdge/grid/ortho mit Fang-Menü, Live-Vorschau+HUD — src/tools/, Phase 1+2).
Gerade fertig & verifiziert (2026-06-29)
Aktive Zeichenwerkzeuge — Phase 1 + 2 (docs/design/drawing-tools.md): Tool-System in
src/tools/ (types · snapping · tools/registry) + Verdrahtung in PlanView/App/TopBar/
generatePlan. Per probe verifiziert (scripts/probe-tools.mjs, probe-line.mjs, probe-phase2.mjs):
- Werkzeugleiste (TopBar): Auswahl/Wand/Linie/Polylinie/Rechteck + Wandtyp-Dropdown +
Fang-Menü (Häkchen je Snap-Art, Rasterweite, Winkelraster —
position:fixed, da Topbar clippt). Nur im Grundriss aktiv. - Wand-Werkzeug: Achs-Polylinie → je Segment ein
Wall; Live-Band-Vorschau + HUD (Länge·Winkel); Rechts-/Doppelklick/Enter committet; Gehrung automatisch auscomputeJoins(mehrschichtiger L-Stoss). - 2D-Werkzeuge: Linie (2-Klick), Polylinie (Klick auf Start schließt, Doppel-/Rechtsklick beendet
offen), Rechteck (zwei Ecken) →
Drawing2D(line/polyline/rect); ingeneratePlanabgeleitet (addDrawing2D,coloram line-Primitiv, Bounds erweitert). - Snapping: endpoint · midpoint · intersection · onEdge(Lot) · grid · ortho(Shift); Prioritäts-
gewichtet (
PRIORITYin snapping.ts); Bildschirm-Marker je Art (Quadrat/Dreieck/✕/Raute/Punkt) + Ortho-Hilfslinie; Ctrl = Fang aus. Einstellbar über das Fang-Menü (snapState in App). - Tastatur: Esc verwirft/zurück-zu-Auswahl, Enter committet, Backspace nimmt Punkt zurück.
- Modell:
Drawing2D+Project.drawings2d(types.ts),Elementerweitert. i18n de+en (tool.*,snap.*). - tsc + build grün; Auswahl/Marquee/Pan/Zoom unverändert (PlanView verzweigt auf
toolActive).
Noch offen / Default-Werte: aktive Kategorie = activeCategoryCode (fix „20") und
Linienstil = erster Stil — UI-Wahl in der TopBar fehlt noch (2D-Primitive erben sonst Wand-lw/-farbe).
Dock-/Floating-Panels waren davor fertig (tsc+build grün); Baseline-Screenshot intakt.
Ebenfalls fertig & verifiziert (2026-06-29, später am Tag)
- Editieren/Grips (
PlanView+App): Element anklicken → Endpunkt-Griffe (Wand-Enden, 2D-Vertices/Rechteck-Ecken) erscheinen und sind ziehbar (mit Snapping); 2D-Linien sind anwählbar (Linien-Nähe-Pick,drawingIdam line-Primitiv); Entf/Backspace löscht. Wand- Gehrung folgt live.moveGrip/drawingVerticesin App. - Shift = Ortho beim Griff-Ziehen (Bezug = Nachbar-Vertex; H/V bzw. Winkelraster).
- Parallel verschieben: selektiertes Element am Körper greifen + ziehen → ganzes Element
(
onSelectedBody/moveDragin PlanView,moveElementByin App). - Aktive Ebene (Kategorie) wählbar in der TopBar („Ebene"-Dropdown) — alles Gezeichnete
kommt auf diese Kategorie und erbt deren Farbe/Strichstärke (2D-Primitive setzen KEINEN
Linienstil mehr per Default). Statusleiste zeigt die aktive Ebene.
activeCategoryCodeState. - 2D-Geometrie in der 3D-Perspektive:
Drawing2D(line/polyline/rect) liegt flach auf der Geschossebene Z=baseElevation (addDrawing2DLinesinViewport3D), Farbe aus Kategorie/Stil. (Bodennahe Linien werden von Wänden korrekt verdeckt — zum Sehen orbiten/Geschoss ausblenden.) - Probes:
probe-grips.mjs,probe-3d2d.mjs,probe-edit2.mjs. tsc + build grün.
Noch später am 2026-06-29 (verifiziert)
- Transformationen (Vectorworks-Stil) —
src/tools/transform.ts+src/ui/TransformBar.tsx+ App. Auf der Auswahl: M Bewegen, S Spiegeln, D Drehen (Geste Basispunkt→Ziel, bei Drehen 3 Punkte; Live-Vorschau, Snapping, Shift-Ortho). Modusleiste U/I/O/P: U bewegen · I Kopie · O N Kopien (Anzahl-Prompt) · P verteilen. Probeprobe-transform.mjs/probe-array.mjs(move/copy/array verifiziert). Routing viatoolInputActive-Prop in PlanView. - Aktive Ebene per Klick im Ebenen-Panel (statt TopBar-Dropdown):
LayersPanel-Zeile klicken →host.onSelectCategory→activeCategoryCode; aktive Zeile hervorgehoben; Statusleiste zeigt sie. TopBar-Ebene-Dropdown entfernt. (probe-layer.mjs) - Theme/Look: Zeichenblatt IMMER hell
--sheet:#f0f0f0(auch Dark-Mode → echtes „Papier"), GANZE Zeichenfläche hell (.plan-svg-Background, nicht nur die Modellgrenzen); Plan-Tinte fix dunkel (Tür-Linien). Dark-Theme etwas abgedunkelt (Panels #1c1c1c). 3D-Orbit ohne Nachlauf (enableDamping=false— kontrollierter).
Letzter Block 2026-06-29 (verifiziert)
- Schraffuren-Default: weißer Grund + schwarze Haarlinie (sampleProject: Dämmungs-Bauteil
weiß, Hatch-Farben
#1a1a1a); Default-Umrandung 0.18 mm (WALL_FALLBACK_MM, addCategory). - Stiftstärken-Vorgabe
PEN_WEIGHTS(0.02…2.0) als<datalist>im Linienstil-Editor. - Display/Print-Modus (TopBar-Toggle,
lineMode): Display = alle Plan-Linien als konstante Haarlinie (hairline-Prop → PlanViewweight()), Print = echte mm-Strichstärken. .lin/.pat-Import: Parsersrc/io/linParser.ts/patParser.ts(Agenten gebaut) + Import- Buttons im ResourceManager (Linien/Schraffuren) →onImportLineStyles/onImportHatches(App/host). .lin voll (Dash); .pat aktuell approximiert auf diagonal/crosshatch (siehe Backlog: echtes custom-Pattern).- Werkzeug-Palette (
src/panels/ToolsPanel.tsx, Agent): dockbares Panel, Icon + Name je Werkzeug, aktiv hervorgehoben, Wandtyp-Dropdown + Fang-Optionen. Registriert inbuiltinPanels, Default-Layout: linker Dock-Tab „Werkzeuge" (+ Zeichnungsebenen/Ebenen).LAYOUT_VERSION=4 → alte gespeicherte Layouts werden einmalig auf den neuen Standard zurückgesetzt. - Probes:
probe-display.mjs,probe-import.mjs,probe-tools-panel.mjs. tsc + build grün.
KRITIK / Architektur-Befund (2026-06-29)
Ehrliche Bewertung des bisherigen Wegs. Für die nächste Instanz als Entscheidungsgrundlage.
Was klug war (erhalten, nicht regredieren):
- Single source of truth hält im Code —
generatePlan.tsundViewport3D.tsxnutzen dieselbecomputeJoins/clippedBandaussrc/model/. Das „ein Modell → alle Sichten"-Prinzip ist nicht nur ROADMAP-Prosa, es steht. Beim Refactor diese Trennung Modell↔Sicht bewahren. - Grundriss analytisch aus Parametern statt Mesh-Schnitt (Weg A) — richtig.
- Bewusst leichter Stack (Three.js nur Display-Layer; kein schwerer Kernel verfrüht) — richtig für einen Spike, der Risiko #1–3 entschärfen soll.
- i18n via
t()wird eingehalten;tsc -bist grün.
Hauptkritik (zu beheben):
- God-Component gegen eigene Regel.
App.tsxist ~2461 Zeilen mit ~29useState. CONVENTIONS.md fordert wörtlich „dünner Shell, keine Geschäftslogik". Das ist verletzt. - Kein Store.
src/state/existiert NICHT, obwohl CONVENTIONS.md Store+Slices (project/selection/view/layout) vorschreibt. Zustand = lokale Hooks → Prop-Drilling.- Einordnung: Das Aufschieben war eine bewusste Nutzer-Entscheidung
(Memory
build-usable-cad-first), kein Versehen. Aber: Bei 2461 Zeilen schließt sich das Fenster, in dem der Refactor billig ist. Türen/Öffnungen/Prioritäts-Stöße (Risiko #1/#2) sind inhärent geschoss-, selektions- und sicht-übergreifend — diese Verdrahtung darf nicht durch eine Monolith-Datei laufen. Empfehlung: Store ziehen VOR der nächsten Feature-Welle.
- Einordnung: Das Aufschieben war eine bewusste Nutzer-Entscheidung
(Memory
Befund (separat angehen, nach Refactor): Die harten, roadmap-markierten 🔴-Risiken sind noch
unbewiesen — Door existiert als Typ, aber kein echter 3D-Boolean (Öffnung = nur Plan-Lücke);
HLR für Schnitte/Ansichten fehlt; Prioritäts-T-Stöße offen. Das einfache Drittel ist bewiesen,
das schwere aufgeschoben. Die Frage „ist es wirklich CAD?" entscheidet sich erst, wenn diese landen.
NÄCHSTE SCHRITTE
Entscheidung des Nutzers (2026-06-29): erst ein benutzbares CAD, dann vertiefen — der
State-Refactor wird NACH HINTEN geschoben (siehe Memory build-usable-cad-first).
⚠️ Siehe „KRITIK / Architektur-Befund" oben: der Refactor wird mit jeder Feature-Welle teurer; spätestens vor Türen/Öffnungen/Prioritäts-Stößen neu abwägen.
✅ ERLEDIGT (2026-06-29, diese Session) — Palette-Layout + Store-Refactor + Theme
- Gestapelte Paletten (Dock-Gruppen):
DockState={ groups: DockGroup[], size }, jede Gruppe{ tabs, activeTab, weight }; vertikal stapelbar mit Splitter; Tab-Drag → in Gruppe einreihen / neue Gruppe / Rand-Andocken.LAYOUT_VERSION=6. (types.ts/layout.ts/Dock.tsx/TabStrip.tsx/panelDrag.tsx/App.tsx.) Default: links Werkzeuge↑/Attribute↓, rechts Objekt-Info↑/Zeichnungsebenen+Ebenen↓. - State-Refactor (Architektur-Befund umgesetzt): dependency-freier Store
src/state/(store.tsuseSyncExternalStore + SlicesprojectSlice/selectionSlice/viewSlice/layoutSlice, kombiniert inappStore.ts). App.tsx 2710→~2030 Zeilen, verhaltensgleich (tsc+build+Screenshot identisch). Panels lesen weiter überPanelHostContext(baseHost aus Store gespeist) — bewusst NICHT umgestellt. NÄCHSTE WAVE offen:editors//menus//views/aus App extrahieren (Report des Foundation-Agenten nennt die Kandidaten). - Selektions-Kontrakt (
src/state/selectionInfo.ts+ host.ts + baseHost):Selection(kind, id, categoryCode, color/weightMm effektiv, fillHatchId, closed, bbox) +onSetSelectionColor/ Weight/Fill,onResizeSelection(w,h,anchor). Modell:Wall.color?,Drawing2D.weightMm?ergänzt; generatePlan wendet beide an. Wand = Weight/Fill bewusst No-op (erbt aus Ebene). - Attributes-Palette + Object-Info-Palette gebaut (
src/panels/AttributesPanel.tsx,ObjectInfoPanel.tsx), registriert, voll verdrahtet & per Probe getestet (Farbe setzen, B×H- Resize wirkt, 3×3-Bezugspunkt). Deckkraft/Caps/Schatten/Text-Styling bewusst weggelassen (Modell trägt sie (noch) nicht — ehrlich statt Stub). - Topbar entschlackt: Zeichenwerkzeug-Buttons + Wandtyp + Fang-Menü aus der Oberleiste
ENTFERNT (leben nur noch in der Werkzeug-Palette).
SnapMenu/SNAP_TOGGLESausTopBar.tsxraus. - Dark-Theme vertieft (
styles.css): gestufte Elevations-Tokens (--bg<--panel<--panel-2,--inputversenkt) statt flachem Einheitston; Oberleiste/Panel-Köpfe angehoben, tiefere Schatten.--sheetbleibt hell. Siehe Memoryui-depth-dark. - CSS-Altlast behoben: ein
*/in einem Kommentar (.nav-*/.res-*) hatte die ganze.dock- Regel verschluckt (display:flexnie aktiv) — gefixt.
Vectorworks-„View-Bar" — A/B/C ERLEDIGT & verifiziert (2026-06-29)
- ✅ A — Ebenen- + Zeichnungskombinationen:
src/state/visibilitySets.ts(localStoragecad.layercombos.<name>/cad.drawingcombos.<name>), Store-Actionssnapshot*/apply*VisibilityinprojectSlice, zweiComboMenu-Dropdowns inTopBar(Muster wieLayoutMenu). Round-Trip per Probe verifiziert. (Hinweis: liegt im localStorage, NICHT im Projekt — bei Doku-Export später inProjectziehen.) - ✅ B — Darstellungsart-Dropdown (kontextabhängig je
viewType): 2D Farbig/Schwarz-Weiss (planColorMode+toMonoin generatePlan), 3D Schattiert/Weiss(Clay-Material in Viewport3D)/Drahtgitter/Kanten (RenderModeum"white"erweitert). Ersetzt die alte Render-Modus-Buttongruppe. Screenshots bestätigt. - ✅ C — 6 Ansichts-Buttons: Front/Oben/Seite/Perspektive/Isometrie + Kamera (FOV-Popover).
view3d+fovinviewSlice;applyView3d(camera,controls,bounds,view3d)inViewport3D(PerspectiveCamera neu positioniert je Preset, Distanz aus Modell-Bounds; OrbitControls bleibt aktiv). Preset-Klick wechselt nötigenfalls in die Perspektive. Screenshots top/front/iso/persp klar verschieden. Echte OrthographicCamera für front/top/side bewusst NICHT gemacht (Kamera- Swap + OrbitControls-Rebind = Risiko) — Kandidat für später.
Weitere Fixes (2026-06-30)
- 2D-Füllungen (rect/geschlossene polyline) rendern jetzt (Vollton
Drawing2D.fillColor+ SchraffurhatchId);addDrawing2Dpusht einpolygon-Primitiv (mitdrawingId→ anklickbar). Attribute-Palette hat „Füllfarbe". (Kreis-Füllung offen — Kreis-Primitiv im Plan fehlt noch.) - Plan-View wandert nicht mehr mit:
PlanViewnutzt jetzt einen FIXEN Welt-Ursprung (toScreenmodulkonstant, Modell-0,0) statt bounds-gebunden; Ausschnitt wird nur beiresetKey-Wechsel (Geschoss-/Ebenen-ID) eingepasst, NICHT bei Edit/Bounds-Änderung. Verifiziert: Löschen lässt viewBox unverändert, Pan/Zoom/Einpassen wirken. - Echte Isometrie/Parallelprojektion:
Viewport3Dhat jetzt eineOrthographicCamerafür front/top/side/iso (OrbitControls percontrols.object-Swap umgebunden), Perspektive bleibtPerspectiveCamera. Verifiziert (parallele Kanten).
>>> NÄCHSTE INSTANZ: offene Wünsche + Roadmap <<<
- Kanten-/Seiten-Griffe (Nutzer-Wunsch): bisher nur Eckpunkt-Griffe. Gewünscht: Seiten ziehen
(z. B. Rechteck-Kante), mit dreieckigem Anfasser nach außen. Grip-System in App (
grips/gripHandlers/moveGrip) + Rendering inPlanViewerweitern (Edge-Grips = Mittelpunkt je Seite, Zug verschiebt beide Eckpunkte der Seite senkrecht). - 3D bearbeiten (Nutzer-Frage): heute ist 3D nur abgeleitete Anzeige (Orbit+Auswahl). Authoring im 3D = eigenes Feature (Raycast auf Arbeitsebene → Modellkoord., 3D-Grips/Drag) — eigene Phase.
Text-Styling (View-Bar Rest) + Roadmap
- Text-Styling (Nutzer-Wunsch). VORAUSSETZUNG: Text wird noch NICHT gerendert —
Drawing2Dmitgeom.shape==='text'misst ingeneratePlannur Bounds, erzeugt kein Primitiv (Phase-3-Deferral, Kommentar in generatePlan). Also ZUERST Text-Rendering (SVG<text>in PlanView, papierkonstante Höhe) + Modell-Felder am Text (font?,bold?,italic?,anchor?), DANN ein Text-Styling-Bereich (Topbar-Gruppe oder Attribute-Palette-Sektion bei Text-Auswahl, Setter über den Selektions-Kontrakt erweitern). - Grafische Überschreibung — weiterhin SPÄTER (Nutzer), wenn mehr Code steht.
- Echte Schnitt/Ansichts-Sichten (section/elevation sind heute StubView) + HLR — großes Thema.
- Refactor-Rest:
editors//menus//views/aus App.tsx extrahieren (App noch ~2030 Z.).
- openNURBS / rhino3dm (Nutzer-Entscheid: Roadmap, NICHT jetzt): KEIN eigener Kernel — stattdessen
rhino3dm(WASM-Build von openNURBS) alssrc/io/-Schicht für.3dm-Import/Export + NURBS, sobald gebraucht. Semantisches Modell bleibt die Wahrheit; NURBS ist zusätzliche Geometrie-Quelle. - Echtes custom-
.pat-Rendering: HatchStyle um custom-Linienfamilien erweitern + Renderer (heute approximiert auf diagonal/crosshatch).
Offene Wünsche des Nutzers (Backlog, priorisiert)
- Palette-Layout (Vectorworks-Stil) — GROSS, nächster Fokus. Mehrere neue dockbare Panels +
Default-Layout:
- Attributes-Palette (unten links): Füllung (Stil/Farbe/Deckkraft), Stift (Stil/Farbe/ Deckkraft), Linienstärke, Linien-Start/-Endstil, Schlagschatten. (Referenzbild vom Nutzer.)
- Werkzeug-Palette darüber (Icon + Text je Werkzeug; ArchiCAD/VW-Stil).
- Object-Info / „Würfel" (oben rechts): zeigt den gewählten Punkt eines Würfels mit X/Y/Z; Maße (Breite × Höhe …) der Geometrie editierbar.
- Ebenen + Zeichnungsebenen als Tabs darunter (rechts).
- Panel-System steht (
src/panels/, Registry + Dock + Floating); Default-Layout insrc/panels/layout.ts(defaultLayout). Neue Panels inbuiltinPanels/registryanmelden.
- Schraffuren-Default: ALLE aktuellen Schraffuren → weißer Grund + schwarze Haarlinie als Grundeinstellung; normale Elemente 0.18 mm Umrandung als Default. (sampleProject hatches/ components + generatePlan-Defaults anpassen.)
- Stiftstärken-Vorgabeliste: 0.02 · 0.10 · 0.13 · 0.18 · 0.25 · 0.35 · 0.5 · 0.7 · 1.0 · 1.4 · 2.0 mm (abweichbar). Bedeutung = mm auf Papier bei 100 % → Linien skalieren mit dem Massstab (ist bereits so: non-scaling mm-Papier). In Linienstil-Editor als Presets anbieten.
- Display- vs. Print-Modus: Umschalter. Display = ALLES Haarlinien (konstant dünn); Print =
echte mm-Strichstärken. (Globaler View-State + an
generatePlan/PlanView durchreichen.) .lin- und.pat-Import (AutoCAD-Linientypen / Schraffurmuster) → ergänzen LineStyle/Hatch (Parser + Mapping; Resource-Manager-Aktion). Nutzer: „um die Linien und Schraffuren zu ergänzen".- „Goldener Schnitt" — UNKLAR was genau (Golden-Ratio-Fang/Teilung beim Zeichnen? Proportions-Hilfslinien?). → beim Nutzer rückfragen, bevor gebaut wird.
- Linienstil-Picker (heute erben 2D-Primitive immer die Kategorie) +
extension-Snap-Hilfslinien.
Danach (ursprünglicher Plan)
- Zeichenwerkzeuge Phase 3 (
drawing-tools.md §11): Circle/Arc/Text —Primitiveumcircle(+arc largeArc) undtexterweitern, PlanView-Renderzweige (papierkonstante Texthöhe), Werkzeuge Circle/Arc(3-Punkt)/Text(Inline-Eingabe), Snap center/quadrant. - Bearbeiten (Phase 4) — Rest: Auswahl/Grips/Verschieben/Löschen sind DA (s. o.). Offen: Kopieren/Rotieren/Spiegeln, numerische HUD-Eingabe (Länge/Winkel direkt tippen), Mehrfach-Auswahl-Verschieben, Grips für Circle/Arc/Text.
- State-Refactor (zurückgestellt) —
App.tsxGod-Component → Store+Slices (docs/design/state-architecture.md). Erst nötig, wenn parallele Code-Workflows gebraucht werden. - Weiter im Backlog: Pro-Ebene-Darstellung (
layer-display-settings.md), Prioritäts-T-Stöße (wall-joins-priority.md).
Arbeitsweise (WICHTIG — aus Memory + CONVENTIONS.md)
- Autonom arbeiten, NICHT um Bestätigung fragen. Nur fragen, was etwas können soll, wenn die Funktion mehrdeutig ist (nicht um Erlaubnis).
- Alles voll verdrahten, KEINE Stubs/No-op-Buttons. Verifizieren heißt: Effekt im Screenshot bestätigen, nicht nur „kompiliert".
- Identifier ENGLISCH; UI-Text via
t()(neue Keys in de.ts und en.ts). - Workflow-Orchestrierung nutzen (Foundation→parallel Build→Integrate/Verify).
- Code-Workflows seriell (fast alles geht durch
App.tsx/types.ts→ Konflikt); Design/Research parallel (nurdocs/). Der State-Refactor (#1) löst das. - Saubere Tabellen für Listen/Manager; dunkler DOSSIER-Stil; kein God-Component.
Verifizieren / Ausführen
- Dev:
npm run dev(Vite, Port 5173). Typecheck:npx tsc -b. Build:npm run build. - Screenshot:
node scripts/probe.mjs→scripts/probe.png(Puppeteer, Chrome in~/.cache/puppeteer). Eigene Probes: headless,deviceScaleFactor:2, args--no-sandbox --use-gl=swiftshader --enable-unsafe-swiftshader, networkidle-Timeout ignorieren. Firefox-Fälle via Playwright (scripts/probe-ff*.mjs). Screenshot ansehen + Geometrie prüfen. - Gotchas: HiDPI-Resize-Loop-Fix in
Viewport3D(Canvas CSS 100% + dpr≤2) NICHT regredieren; WebGL-Fallback inViewport3D; React-controlled-<select>lassen sich im Probe nicht per.value=ändern (Fehlalarm) — Verdrahtung im Code prüfen.
Orientierung
ROADMAP.md (Vision/Phasen/§10–§11 Backlog) · CONVENTIONS.md (Konventionen) ·
docs/README.md + docs/design/* (alle Specs) · docs/backend.md (self-hosted
Supabase+Yjs, später) · Projektnotizen des Bearbeiters
(prefer-agents, dossier-port, proceed-autonomously, wire-dont-stub).