Browser-BIM (cad): semantisches Modell, abgeleitete 2D/3D-Sichten, Zeichenwerkzeuge

Standalone-Browser-Port von DOSSIER. Enthaelt das semantische Modell mit
Plan-/3D-Ableitung, Zeichen- und Editierwerkzeuge, Rhino-artiges Befehlssystem,
dockbares Panel-System, Resource-Manager, DXF/.lin/.pat-Import, i18n (de/en)
sowie Projektdokumentation und Probe-Harness.
This commit is contained in:
2026-06-30 20:52:27 +02:00
commit ca859c4aa4
157 changed files with 37921 additions and 0 deletions
+422
View File
@@ -0,0 +1,422 @@
# 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.
## >>> STAND 2026-06-30 (Fortsetzung, spät) — ZUERST lesen <<<
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):**
- **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:** `selectedDrawingId``selectedDrawingIds[]`
(`selectionSlice`); Marquee wählt **Linien** (`marqueeHitDrawings`), **Ctrl/Cmd+A**,
**Multi-Delete**, **Multi-Highlight** (`.plan-sel-draw`), **Shift-Klick-Mehrfachauswahl**
(`PlanSelection.shift``onPlanSelect` toggelt).
**LÄUFT bei Übergabe (auf Resume verifizieren, NICHT doppelt bauen):**
- **Editier-Welle 2 — Split/Join/Segment** (Agent): Ctrl+S Split (Rechteck→zwei
geschlossene; nur bis Schnittpunkt), Ctrl+J Join, Alt+Klick=Segment löschen. Berührt
`App.tsx`, `PlanView.tsx`, `kernel2d.ts` (`splitPolylineAtParam`/`splitClosedByChord`/
`removeSegment`/`splitAtIntersections`/`joinChains`), `projectSlice.ts`, ggf.
`src/editors/splitJoin.ts`. **Resume:** `git diff --stat`, `npx tsc -b`, `npm run build`,
`node scripts/probe-splitjoin.mjs`.
**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.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 → 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 Code** — `generatePlan.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 gerendert**
`Drawing2D` 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.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 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).