Files
DOSSIER-STANDALONE/HANDOVER.md
T
karim e6466a5027 Electron-Prototyp: eigenes App-Fenster statt Tauri/WebKitGTK
Ersetzt den Tauri-Webview-Weg testweise durch eine Electron-Shell (randlos,
ohne native Menüleiste, wie chromium-shell.sh), damit die WASM/WebGPU-Engine
zuverlässig laeuft statt in WebKitGTK. Rust-Backend nicht noetig, da
compute_joins einen TS-Fallback hat. npm run electron zum Starten.
2026-07-02 23:51:23 +02:00

67 KiB
Raw Blame History

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).

>>> 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 in Electron auf diesem System (niri/Wayland) fällt auf WebGL2 zurück (Ozone-Wayland+Vulkan-Warnung beim Start). --ozone-platform=x11 GEHT NICHT (bricht die Fenstererstellung komplett — GetGeometry/Vulkan-Surface-Fehler). System-Chromium (chromium-shell.sh) rendert die WASM-Engine hingegen nachweislich (siehe wasm-engine-viewport- Notizen); die verwendete Electron-Version (Paket sagt ^43, --version meldet v24 — Versions- Mismatch, ggf. stale Binary-Cache) könnte ein älteres/anders konfiguriertes Chromium mitbringen. TODO nächste Session: Electron-Version/Ozone-Flags weiter untersuchen, bevor der Prototyp der primäre Dev-Weg wird — bis dahin bleibt WebGL2-Fallback der Standardpfad, funktional unverändert. 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/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()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/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 <CommandLine>-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, applysetRoomStampDoc; 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 <CommandLine> (~App.tsx:2793). U/I/O/P-Routing (App.tsx ~15431548) 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 ~416449).
  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/pickhost.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 FERTIGsrc/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 <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.html geladen

    • .material-symbols-outlined-Regel in styles.css (sonst Roh-Ligaturtext); Größen runterskaliert (.ctx-menu 11.5px, Icons 1314px, weniger Padding, Radius 10).
  • Geschoss-Z-Raster: Viewport3D Bodenraster auf gridElevation (= aktives Geschoss baseElevation).

  • 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}) in src/model/types.ts; Resolver src/model/wall.ts (wallReferenceOffset, wallVerticalExtent, nextFloorAbove); angewandt in generatePlan, model/joins.ts, Viewport3D. Panel src/panels/ObjectInfoPanel.tsx; src/state/selectionInfo.ts Selection.wall: WallInfo; Host-Setter + projectSlice updateWall/setWallThickness. Einschichtig = 1-Layer-WallType.

  • Panel-Text: .attr-*/.objinfo-* Untertitel fett + Kontrast (--label statt --muted).

  • Selektion-Überarbeitung: selectedDrawingIdselectedDrawingIds[] (selectionSlice); Marquee wählt Linien (marqueeHitDrawings), Ctrl/Cmd+A, Multi-Delete, Multi-Highlight (.plan-sel-draw), Shift-Klick-Mehrfachauswahl (PlanSelection.shiftonPlanSelect toggelt).

  • 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 1623, 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, applyAngleConstraint aus tools/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.ts neu — ARC/CIRCLE/ELLIPSE/SPLINE + INSERT/Block-Expansion (rekursiv, aus db.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, Alias tr, 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.json

    • src/materials/library.ts (PBR).
  • Welle A — 3D-Materialien (FERTIG & verifiziert): Component.material (ComponentMaterial: PBR-Map-URLs + sizeM Kachelgröße), src/materials/runtime.ts (MaterialRuntime → gecachte MeshStandardMaterial, Kacheln in Welt-Metern via Extrude-UVs), renderMode „textured" (TopBar-Darstellung), Material-Picker (ambientCG-Grid) + Upload + Kachelgröße im ResourceManager (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 aus CommandContext.activeWallTypeId/level) + Registry (Aliase w/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):

  1. Editier-Welle 3: koinzidente Endpunkt-Griffe gemeinsam ziehen + verbundene Enden propagieren beim Seiten-Verschieben (topologisch, das Tiefste).
  2. GUI-Werkzeug ↔ Befehlszeile koppeln: Werkzeug-Klick startet denselben Command → Werte unten eintippbar; danach Cursor-Wert entfernen/optional.
  3. Selektions-Direkt-Edit: Länge/Winkel des gewählten Elements im Befehlsfeld (Tab-Zyklus).
  4. Swisstopo/openbureau-Importer-Dialog (Ort+Radius). Fluss steht: Geocode api3.geo.admin.ch/.../SearchServer (sr=2056→LV95) → ±Radius → WGS84-bbox → STAC data.geo.admin.ch/api/stac/v1, Collections ch.swisstopo.swissalti3d + ch.swisstopo.swissbuildings3d_3_0 → Download → TIN/Mesh→Kontext. LV95↔WGS84 nötig; Browser-CORS das Risiko. Referenz: DOSSIER rhino/swisstopo.py.
  5. 3D→2D-Dokument: live + statisch (MVP-Kantenprojektion → HLR) für Vektor-PDF. Hinweis: generatePlan liefert SCHON Vektoren (SVG) → Plan-PDF bereits vektoriell.
  6. Topbar-Rest iconisieren (Zoom/Referenzlinien/Linien-Modus/Ressourcen).
  7. 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 (+Aliase l/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, Aliase m/cp/o. CommandContext um selection erweitert (App host.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, via npx 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), Viewport3D rendert Kontext/Terrain. Deps dxf-parser+delaunator.
  • Echte Isometrie (Orthographic-Kamera): Viewport3D schaltet 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 via resetKey).

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):

  1. Tab-Feld-Zyklus verifizieren (Agent-Gate).
  2. Tier-1-Editierbefehle: Move/Copy/Offset(nutzt kernel2d)/Trim/Split/Join/Explode. Undo/Redo-Stack im Store erwägen (Commits laufen über setProject).
  3. Terrain-Integration: Konturen im Plan (generatePlan/PlanView), Import-/Terrain-Befehle
    • Site-Panel (src/panels/) + i18n + DWG→DXF-Hinweis-UI; .dxf-File-Picker → parseDxf.
  4. Wand-Stärke-Feld: Wand ist noch ein Alt-Tool — als Command portieren, dann Tab-Feld thickness NUR bei einschichtigen/freien Wänden (mehrschichtige Typen: Stärke vorgegeben).
  5. 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 Wiederaufnehmen src/commands/-Existenz + npx tsc -b prü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 aus computeJoins (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); in generatePlan abgeleitet (addDrawing2D, color am line-Primitiv, Bounds erweitert).
  • Snapping: endpoint · midpoint · intersection · onEdge(Lot) · grid · ortho(Shift); Prioritäts- gewichtet (PRIORITY in snapping.ts); Bildschirm-Marker je Art (Quadrat/Dreieck/✕/Raute/Punkt) + Ortho-Hilfslinie; Ctrl = Fang aus. Einstellbar über das Fang-Menü (snap State in App).
  • Tastatur: Esc verwirft/zurück-zu-Auswahl, Enter committet, Backspace nimmt Punkt zurück.
  • Modell: Drawing2D + Project.drawings2d (types.ts), Element erweitert. 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, drawingId am line-Primitiv); Entf/Backspace löscht. Wand- Gehrung folgt live. moveGrip/drawingVertices in 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/moveDrag in PlanView, moveElementBy in 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. activeCategoryCode State.
  • 2D-Geometrie in der 3D-Perspektive: Drawing2D (line/polyline/rect) liegt flach auf der Geschossebene Z=baseElevation (addDrawing2DLines in Viewport3D), 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. Probe probe-transform.mjs/probe-array.mjs (move/copy/array verifiziert). Routing via toolInputActive-Prop in PlanView.
  • Aktive Ebene per Klick im Ebenen-Panel (statt TopBar-Dropdown): LayersPanel-Zeile klicken → host.onSelectCategoryactiveCategoryCode; 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 → PlanView weight()), Print = echte mm-Strichstärken.
  • .lin/.pat-Import: Parser src/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 in builtinPanels, 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 CodegeneratePlan.ts und Viewport3D.tsx nutzen dieselbe computeJoins/clippedBand aus src/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 #13 entschärfen soll.
  • i18n via t() wird eingehalten; tsc -b ist grün.

Hauptkritik (zu beheben):

  1. God-Component gegen eigene Regel. App.tsx ist ~2461 Zeilen mit ~29 useState. CONVENTIONS.md fordert wörtlich „dünner Shell, keine Geschäftslogik". Das ist verletzt.
  2. 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.

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.ts useSyncExternalStore + Slices projectSlice/selectionSlice/viewSlice/ layoutSlice, kombiniert in appStore.ts). App.tsx 2710→~2030 Zeilen, verhaltensgleich (tsc+build+Screenshot identisch). Panels lesen weiter über PanelHostContext (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_TOGGLES aus TopBar.tsx raus.
  • Dark-Theme vertieft (styles.css): gestufte Elevations-Tokens (--bg<--panel<--panel-2, --input versenkt) statt flachem Einheitston; Oberleiste/Panel-Köpfe angehoben, tiefere Schatten. --sheet bleibt hell. Siehe Memory ui-depth-dark.
  • CSS-Altlast behoben: ein */ in einem Kommentar (.nav-*/.res-*) hatte die ganze .dock- Regel verschluckt (display:flex nie aktiv) — gefixt.

Vectorworks-„View-Bar" — A/B/C ERLEDIGT & verifiziert (2026-06-29)

  • A — Ebenen- + Zeichnungskombinationen: src/state/visibilitySets.ts (localStorage cad.layercombos.<name> / cad.drawingcombos.<name>), Store-Actions snapshot*/apply*Visibility in projectSlice, zwei ComboMenu-Dropdowns in TopBar (Muster wie LayoutMenu). Round-Trip per Probe verifiziert. (Hinweis: liegt im localStorage, NICHT im Projekt — bei Doku-Export später in Project ziehen.)
  • B — Darstellungsart-Dropdown (kontextabhängig je viewType): 2D Farbig/Schwarz-Weiss (planColorMode + toMono in generatePlan), 3D Schattiert/Weiss(Clay-Material in Viewport3D)/Drahtgitter/Kanten (RenderMode um "white" erweitert). Ersetzt die alte Render-Modus-Buttongruppe. Screenshots bestätigt.
  • C — 6 Ansichts-Buttons: Front/Oben/Seite/Perspektive/Isometrie + Kamera (FOV-Popover). view3d+fov in viewSlice; applyView3d(camera,controls,bounds,view3d) in Viewport3D (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 + Schraffur hatchId); addDrawing2D pusht ein polygon-Primitiv (mit drawingId → anklickbar). Attribute-Palette hat „Füllfarbe". (Kreis-Füllung offen — Kreis-Primitiv im Plan fehlt noch.)
  • Plan-View wandert nicht mehr mit: PlanView nutzt jetzt einen FIXEN Welt-Ursprung (toScreen modulkonstant, Modell-0,0) statt bounds-gebunden; Ausschnitt wird nur bei resetKey-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: Viewport3D hat jetzt eine OrthographicCamera für front/top/side/iso (OrbitControls per controls.object-Swap umgebunden), Perspektive bleibt PerspectiveCamera. 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 in PlanView erweitern (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

  1. Text-Styling (Nutzer-Wunsch). VORAUSSETZUNG: Text wird noch NICHT gerendertDrawing2D mit geom.shape==='text' misst in generatePlan nur 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).
  2. Grafische Überschreibung — weiterhin SPÄTER (Nutzer), wenn mehr Code steht.
  3. Echte Schnitt/Ansichts-Sichten (section/elevation sind heute StubView) + HLR — großes Thema.
  4. 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) als src/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)

  1. 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 in src/panels/layout.ts (defaultLayout). Neue Panels in builtinPanels/registry anmelden.
  2. 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.)
  3. 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.
  4. Display- vs. Print-Modus: Umschalter. Display = ALLES Haarlinien (konstant dünn); Print = echte mm-Strichstärken. (Globaler View-State + an generatePlan/PlanView durchreichen.)
  5. .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".
  6. „Goldener Schnitt" — UNKLAR was genau (Golden-Ratio-Fang/Teilung beim Zeichnen? Proportions-Hilfslinien?). → beim Nutzer rückfragen, bevor gebaut wird.
  7. Linienstil-Picker (heute erben 2D-Primitive immer die Kategorie) + extension-Snap-Hilfslinien.

Danach (ursprünglicher Plan)

  1. Zeichenwerkzeuge Phase 3 (drawing-tools.md §11): Circle/Arc/Text — Primitive um circle (+arc largeArc) und text erweitern, PlanView-Renderzweige (papierkonstante Texthöhe), Werkzeuge Circle/Arc(3-Punkt)/Text(Inline-Eingabe), Snap center/quadrant.
  2. 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.
  3. State-Refactor (zurückgestellt) — App.tsx God-Component → Store+Slices (docs/design/state-architecture.md). Erst nötig, wenn parallele Code-Workflows gebraucht werden.
  4. 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 (nur docs/). 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.mjsscripts/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 in Viewport3D; 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).