Commit Graph

306 Commits

Author SHA1 Message Date
karim 73d147b37d 2D: Inline-Text-Editor-Nachbesserung (unsichtbarer Anker) + Textspalte als echtes Rechteck
Nutzer-Report nach der Umstellung auf Inline-Editing: beides funktionierte
nicht — "Text" liess sich kein Ankerpunkt setzen, "Textspalte" sollte ein
Rechteck statt nur eine Breite sein.

Root Cause für "Text": `.planview-inline-text` bekam ohne Spaltenbreite
`width: undefined` — bei position:fixed ohne rechten Rand greift shrink-to-
fit-Sizing, das für ein LEERES contentEditable auf ~0px kollabiert. Der Klick
erzeugte tatsächlich ein Element + öffnete den Editor, beides war nur
unsichtbar (eine 0px-Box lässt sich nicht anklicken/fokussieren). Fix: neue
Klasse .planview-inline-text-free (kein wrapWidth) erzwingt `width:
max-content` + `white-space: pre` auf Editor/Surface (Box wächst mit dem
Inhalt statt umzubrechen — "Text" bleibt einzeilig bis Enter, wie gewünscht)
plus eine CSS-Mindestbreite (160px) für den leeren Startzustand.

"Textspalte" (textbox.ts) zeichnet jetzt ein RECHTECK (zwei diagonale Ecken,
beliebige Zugrichtung) statt nur eine horizontale Breitenlinie — die
aufgezogene Höhe wird nicht im Modell gespeichert (das Textformat kennt nur
`width` für den Wortumbruch, keine feste Rahmenhöhe), sondern als
Editor-MINDESThöhe durchgereicht: CommandResult/EngineHost.focusDrawing um
`focusMinHeightM` erweitert (Passthrough wie focusDrawingId), App.tsx hält
sie in einem neuen editTextMinHeightM-State (useContextMenuState.ts),
PlanView nutzt sie als CSS-min-height des Editors — der Rahmen wächst bei
mehr Text darüber hinaus, schrumpft aber nicht darunter (InDesign-Verhalten).
Beim Doppelklick-Editieren bestehender Elemente wird die Mindesthöhe
zurückgesetzt (kein Nachwirken einer vorigen Textspalten-Erstellung).

textbox.test.ts an die neue Rechteck-Geste angepasst (+ Test für beliebige
Zugrichtung). tsc -b / vitest run (919/919) / npm run build grün. Die
eigentliche Sichtbarkeits-Vermutung (CSS-Kollaps) konnte ich nicht
interaktiv im Browser verifizieren (kein Browser-Tool verfügbar) — Nutzer
prüft erneut in der laufenden App.
2026-08-20 21:44:37 +02:00
karim f2d169cc67 2D: Text/Textspalte auf Inline-Editing direkt auf der Zeichenfläche umgestellt
Nutzer-Report: Textfelder waren nicht mehrzeilig. Ursache: die Text-/
Textspalte-Werkzeuge holten ihre Eingabe über die Befehlszeile
(CommandLine.tsx) — ein einzeiliges <input>, Enter committet sofort, kein
Zeilenumbruch möglich. Das betraf auch "Textspalte", obwohl die explizit für
mehrzeiligen Wortumbruch gedacht ist.

Lösung (Nutzer-Vorgabe: kein Dialog, direktes Editieren auf der Fläche wie
im Layout-System): text.ts/textbox.ts committen nach dem Platzieren (Anker
bzw. Anker+Breite) sofort ein LEERES Text-Element und melden
`focusDrawingId` — dafür CommandResult/EngineHost um einen Passthrough
erweitert (engine.ts applyResult ruft host.focusDrawing(id)). App.tsx setzt
darauf Auswahl + editTextId (dieselbe State-Variable wie beim bisherigen
Doppelklick-Editieren). PlanView rendert bei gesetztem editTextId jetzt
einen RichTextEditor-Overlay direkt über dem Element (position:fixed via
vbToClient/toScreen, eigene Mini-Toolbar erscheint darüber) statt des
bisherigen TextEditorDialog-Modals — Prinzip 1:1 vom Layout-Blatt-Inline-
Editor (LayoutSheet.tsx) übernommen. Die neuen Props (editTextId/
onCommitTextEdit/onCloseTextEdit) laufen durch Content/LevelPlanView/
SectionPlanView (viewportContent.tsx) bis zu PlanView durch.

Verhalten der beiden Werkzeuge (Nutzer-Vorgabe): "text" bleibt ohne
Wortumbruch (kein `width`) — Enter erzeugt weitere Zeilen, aber keine
automatische Breite. "textbox" setzt `width` und verhält sich damit wie ein
InDesign-Textrahmen (echter Wortumbruch live beim Tippen, kein reines
Render-Detail mehr).

commitTextEdit (useMouseSelectionHandlers.ts) schreibt jetzt bei JEDEM
Tastendruck (kein Guard mehr gegen leeren Text — nötig, damit Löschen bis
auf null Zeichen sich sofort spiegelt); neues closeTextEdit räumt ein beim
Schliessen noch leeres Element auf (verhindert Leichen bei abgebrochener
Neuerstellung).

4 neue Tests (text/textbox: sofortiger Commit + focusDrawingId). tsc -b /
vitest run (918/918) / npm run build grün. Rendering/Positionierung des
Overlays selbst nicht automatisiert testbar — Nutzer prüft visuell in der
Tauri-App.
2026-08-20 21:34:40 +02:00
karim 4f48b73267 2D: Multiline-Werkzeug (Basislinie + N-1 parallele Kopien in einer Geste)
Neuer Befehl multiline.ts, baut auf derselben Offset-Geometrie auf wie der
bestehende offset-Befehl (offsetSegment) — erzeugt aber alle Kopien auf
einmal statt einzeln nacheinander offsetten zu müssen. Deckt gerade
Basislinien ab (häufigster Fall: Grenzlinie+Abstand, Leitungspaar,
Randlinien), keine mehrsegmentige Variante.

Ablauf: Start-/Endpunkt (Tab-Felder Länge/Winkel wie line.ts), dann Abstand
per Maus-Klick (Seite bestimmt Vorzeichen, wie bei offset.ts) oder getippt;
die Anzahl (Default 3, Bereich 2-6) zyklt über eine Inline-Option und bleibt
wie Abstand/Seite über Aufrufe hinweg persistent.

In Registry/Aliase (ml/parallel), ToolsPanel (COMMANDS_EDIT neben Offset),
CommandIcon, i18n de/en aufgenommen.

2 neue Tests. tsc -b / vitest run (916/916) / npm run build grün.
2026-08-20 21:07:21 +02:00
karim 245b7d7fe1 2D: Chamfer (Eckpunkt-Fasung) neu gebaut und verdrahtet
Anders als Fillet/Extend existierte hierfür noch gar keine Geometrie —
chamferCorner (src/geometry/kernel2d.ts) ist neu: gleicher Abstand vom Eck
auf beiden Schenkeln, gerade verbunden (klassischer Gleichabstands-Chamfer,
analog zu filletCorner, nur ohne Bogen — daher exakt statt tesselliert).

commands/cmds/chamfer.ts wortwörtlich nach dem fillet.ts-Muster: Ecke wählen,
Distanz per Maus oder getippt (Tab-Feld). In Registry/Aliase
(ch/fasen), ToolsPanel (COMMANDS_EDIT neben Fillet), CommandIcon, i18n
de/en aufgenommen.

3 neue Tests (Fasung, Fehltreffer, Distanz-zu-gross-No-op). tsc -b /
vitest run (914/914) / npm run build grün.
2026-08-20 21:01:15 +02:00
karim 51e594ee90 2D: Fillet und Extend als Befehle verdrahtet (Geometrie existierte bereits)
filletCorner (Eckpunkt-Verrundung) und extendSegment (Gegenstück zu Trim)
existierten fertig implementiert und getestet im Kernel (kernel2d.ts), hatten
aber keinen einzigen Aufrufer — kein Command, kein Werkzeug-Knopf, für den
Nutzer nicht erreichbar. Reine Verdrahtungsarbeit, kein neues Geometrie-
Problem:

- fillet.ts: Ecke einer polyline/rect wählen (nächstgelegener Eckpunkt mit
  zwei Nachbarn), Radius per Maus oder getippt (Tab-Feld wie bei circle.ts).
  Da Polylinien im Modell keine Bogen-Segmente kennen (kein "Bulge" —
  bekannte, separate Lücke), wird die Verrundung als kurze Punktfolge
  tessselliert statt als echtes Arc-Objekt gespeichert; ein rect wird beim
  ersten Fillet zu einer polyline. Ehrlich im Kopfkommentar dokumentiert.
- extend.ts: offene Kurve (line/offene polyline) nahe einem Ende anklicken,
  dieses Ende wird bis zur nächsten anderen Kurve verlängert (implizite
  Cutter wie bei Trim). Kein Treffer → No-op, Befehl bleibt aktiv.

Beide in COMMANDS/ALIASES registriert (fi/verrunden, ext/verlaengern),
i18n de/en ergänzt, in ToolsPanel COMMANDS_EDIT neben Trim/Join aufgenommen,
eigene Icons in CommandIcon.tsx (bisher: unbekannter Name → kein Icon).

Rotate NICHT angefasst: der ursprüngliche Befund „Rotate ist keine getippte
Kommandozeilen-Aktion, nur ein UI-Button" war unvollständig — App.tsx routet
sowohl den Ribbon-Button als auch das getippte Wort „rotate"/„drehen" ganz
bewusst an startTransformOp() (den reicheren U/I/O/P-Move/Copy/Array/
Verteilen-Fluss), nicht an die generische Befehls-Engine (Kommentar dort
erklärt das explizit). Ein zuerst geschriebener, einfacher rotateCommand
(nur „move"-Modus) wäre über diese Sonderfälle nie erreicht worden UND hätte
über neue Aliase wie „ro" einen inkonsistenten Zweit-Pfad geöffnet (voller
Funktionsumfang bei „rotate", abgespeckter bei „ro") — verworfen, bevor
committet.

6 neue Tests (fillet: Verrundung + Fehltreffer; extend: Verlängerung +
Kein-Cutter-No-op). tsc -b / vitest run (911/911) / npm run build grün.
2026-08-20 20:53:31 +02:00
karim f450a1aa08 2D: By-Layer/By-Object-Attribute für Wand/Decke konsistent verdrahtet
resolveBackground (generatePlan/shared.ts) existierte korrekt implementiert,
wurde aber nirgends aufgerufen — Wand-/Decken-Poché nutzte überall fest
HATCH_INK/HATCH_PAPER, die By-Layer-Hintergrundfarbe kam nie im Renderer an.
Signatur an resolveForeground angeglichen (string | undefined statt
erzwungenem Component.color-Fallback), damit "kein Override gesetzt" weiter
auf die neutrale SIA-Poché-Konvention fällt statt auf die rohe Bauteilfarbe —
sonst hätte jedes bestehende Projekt ohne gesetzten Override optisch
umgeschlagen. Verdrahtet in walls.ts/ceilings.ts (Grundriss) und neu auch in
splitWallLayers/splitSlabLayers (Schnitt, toSection.ts) — dort fehlte bei
MEHRSCHICHTIGEM Wand-/Deckentyp zusätzlich foreground/hatchId komplett
(owner.foreground/hatchSource wurden nie an resolveForeground/resolveHatchId
übergeben, nur der Default lief); splitSlabLayers bekam dafür einen neuen
`owner: Ceiling`-Parameter (die einschichtigen Pfade über
resolveWallSectionStyle/resolveCeilingSectionStyle waren bereits korrekt).

Ceiling.strokeWeight/strokeWeightSource war komplett unverdrahtet (fixe
LAYER_LINE_MM-Konstante) — das Attribut-Panel bietet aber einen Editier-
Umschalter dafür (identisch zur Wand). addCeilingPoche bekommt jetzt eine
aufgelöste lwMm (resolveStrokeWeight, wie schon bei der Wand); die
Deckenkontur bleibt weiterhin die gestrichelte Überkopf-Ansichtslinie, nur
die Dicke folgt jetzt dem Override — Verhaltensänderung auch im Default-Fall,
wenn eine Kategorie eine von LAYER_LINE_MM abweichende Strichstärke trägt.

Bewusst NICHT angefasst: Wand-/Decken-Strichstärke im Schnitt-Umriss
(SECTION_CUT_OUTLINE_MM in generateSectionPlan) — eine einzige, uniforme
Konstante für ALLE Schnitt-Polygone, kein Per-Bauteil-Wert; das wirkt wie
eine bewusste Zeichnungskonvention (analog zur "grob"-Vollschwarz-Poché nach
SIA 400 B.9/36), nicht wie der "UI verspricht etwas, Renderer hält es nicht"-
Bugmuster der übrigen Funde. Würde ausserdem eine Erweiterung von
SectionCutPolygon um ein Gewichtsfeld brauchen (heute keins) — grösserer,
separat zu scopender Eingriff.

6 neue Tests (splitWallLayers/splitSlabLayers mit/ohne Override, Grundriss
weiterhin über die 907 Gesamttests abgedeckt). tsc -b / vitest run (907/907)
/ npm run build grün.
2026-08-20 20:41:28 +02:00
karim 1c3fb289d0 2D: Kreis/Bogen-Zeichenelemente vollständig anwählbar und editierbar
Seit der Umstellung auf echte SVG-Kreis-/Bogen-Primitive (drawingCircle/
drawingArc statt 64-Ecken-Polygon-Annäherung) wurde die Interaktionsschicht
nie nachgezogen — betraf drei unabhängige Stellen mit derselben Ursache:

- Einzelklick: pickDrawing() testete nur kind==="line", fiel sonst auf
  Text-/Bild-Bbox zurück. Neuer distToDrawingRing()-Zweig: Ring-Abstand in
  Bildschirm-Pixeln (zoom-unabhängig wie die Linien-Toleranz), bei gefüllten
  Kreisen zählt auch innerhalb als Treffer; Bögen zusätzlich auf ihre
  Winkel-Spanne begrenzt. grabbedBody() (Körper-Verschieben) profitiert
  automatisch mit, da es denselben Picker nutzt.
- Marquee (Rahmenauswahl): marqueeHitDrawings() sammelte nur line/polygon —
  drawingCircle/drawingArc UND drawingText/drawingImage fehlten komplett
  (Text/Bild waren nie per Rahmen erfassbar, unabhängig von der Kreis-Frage).
  Kreis/Bogen werden für den Punkt-Test auf Ring-Stützpunkte abgetastet
  (Bogen nur innerhalb seiner Winkel-Spanne, nicht als Vollkreis), Text/Bild
  über ihre Bounding-Box.
- Griffe: drawingVertices() hatte keinen Fall für circle/arc → leeres Array,
  selbst nach Fix von Klick+Marquee also unverschiebbar/nicht editierbar.
  Kreis bekommt einen Radius-Griff (Ost), Bogen Start-/End-Winkel-Griffe plus
  einen Radius-Griff auf halbem Bogen — moveGrip() entsprechend erweitert.

10 neue Tests (drawingVertices/moveGripOf für Kreis/Bogen, marqueeHitDrawings
für alle vier zuvor fehlenden Primitive-Arten). tsc -b / vitest run
(901/901) / npm run build grün. Der Einzelklick-Pfad selbst (PlanView.tsx-
Closures) ist nicht automatisiert testbar — visuell in der Tauri-App prüfen.
2026-08-20 20:31:46 +02:00
karim bdbd87bf85 Zweite Leichen-Runde: weitere tote Exporte + orphaned ribbonItems.ts entfernt
ts-prune nach dem ersten Durchgang erneut laufen lassen. compute/index.ts
(101 Z., PoC-Brücke zu einem nie gebauten Rust-Command, im eigenen
Kopfkommentar schon als ungenutzt markiert) komplett gelöscht. ribbonItems.ts
war nur noch für das bereits gelöschte RibbonBar.tsx da (kein anderer
Importeur mehr) — ebenfalls komplett weg.

Einzelne tote Typen/Konstanten: SiaLabel (roomArea.ts), GeoImportResult
(geoContext.ts, plus den dadurch verwaisten GeoOrigin-Import), die drei
MATERIAL_LIBRARY_*-Konstanten (library.ts), ResolveContext-Re-Export
(parametricWalls.ts), roofTotalThickness (types.ts — Duplikat, die echte
Rechnung läuft in toWalls3d/bands.ts inline), DropTarget (panelDrag.tsx).

resolveBackground (generatePlan/shared.ts) bewusst NICHT gelöscht: anders als
das analoge resolveForeground wird es nirgends aufgerufen, obwohl
backgroundSource für Wand/Decke im Modell und im Attribut-Panel existiert —
sieht nach einer echten Lücke aus (By-Layer-Hintergrundfarbe kommt nie im
Renderer an), nicht nach totem Code.

tsc -b / vitest run (891/891) / npm run build grün.
2026-08-20 20:01:26 +02:00
karim 003fde4320 Leichen-Suche: tote Dateien/Funktionen entfernt, revokeMaterial-Bug behoben
ts-prune + cargo check über alle Crates durchsucht, jeder Fund per grep
gegengeprüft. Gelöscht: ResourcesPanel.tsx (abgelöstes Alt-Panel),
RibbonBar.tsx (Ribbon-Phase-1-Überbleibsel), sowie diverse Funktionen ohne
jeden Aufrufer (sampleTerrainColors, docToSvgText/tspanToSvg,
isEmpty/deserialize/fromJson, findAccentSwatch, unregisterPanel,
removeFromDockLayout, TOOL_ORDER).

revokeMaterial (materials/ambientcg.ts) existierte, wurde aber nie
aufgerufen — jeder Material-Wechsel/-Upload liess Blob-URLs leaken. Jetzt
über revokeMaterialUrls() in ComponentsTab.tsx beim Ersetzen/Löschen/
Bauteil-Löschen verdrahtet.

tsc -b / vitest run (891/891) / npm run build grün.
2026-08-20 19:51:52 +02:00
karim cbdc3d5d76 Maus-Schema aus App.tsx in eigenen Hook extrahiert (state/useMouseSelectionHandlers.ts).
Übersetzt rohe Pick-/Marquee-/Kontextmenü-Events aus PlanView/Viewport3D in
Selektions-Mutationen (onPlanSelect/onPlanMarquee/onPlanContextMenu/
onViewportSelect{Wall,Stair,Ceiling,Opening}/onViewport3dPick/
onViewportContextMenu) + zwei eng verwandte Doppelklick-Trigger
(onOpenStampEditor, onEditDrawingText) + commitTextEdit. Reine Verschiebung,
keine Verhaltensänderung. Der useImageImportHandling()-Aufruf, der bisher
mitten in diesem Abschnitt sass (unabhängig vom Rest), ist dabei direkt hinter
den neuen Hook-Aufruf gerutscht statt mit extrahiert zu werden.

App.tsx: 3655 → 3278 Zeilen.

tsc -b sauber, vitest 891/891 grün, vite build ok.
2026-08-20 00:51:35 +02:00
karim c224f9e4f9 Tab-Drag-Steuerung aus App.tsx in eigenen Hook extrahiert (state/useDockDragState.ts).
Reorder/Einreihen/Stapeln/Lösen (usePanelDrag-Wrapper) + Float-Titel-Drag über
Rand-Andockzonen. Reine Verschiebung, keine Verhaltensänderung.

App.tsx: 3690 → 3655 Zeilen.

tsc -b sauber, vitest 891/891 grün, vite build ok.
2026-08-20 00:48:34 +02:00
karim b6d47b433d Host-Context für Panels aus App.tsx in eigenen Hook extrahiert (state/usePanelHostBase.ts).
Der ~990-zeilige Panel-Host-Vertrag (baseHost-useMemo: Projekt/Selektion/
Snap-/View-State + sämtliche Attribut-Setter/Ausschnitte/Layouts-CRUD, den
PanelHostContext für Docks/Floating-Panels konsumiert) war ein einzelner
riesiger useMemo-Objektliteral direkt in App.tsx. Wortgetreu verschoben (keine
Logik neu geschrieben), damit kein Verhalten unterwegs verändert wird — nur
die Objekt-Konstruktion selbst wandert in eine eigene Funktion.

Der Hook-Aufruf sitzt jetzt spät im Funktionskörper (nach useProjectActions/
useSelectionState/useDialogState/useContextMenuState, kurz vor den frühen
Boot-Returns), weil einige referenzierte Handler (openResources, activeLayout,
patchOpenLayout, openSettings, openLayerSettings, onOpenStampEditor) erst dort
definiert sind — dieselbe Art Vorwärtsreferenz-Falle wie beim vorigen
useGripEditing-Schritt, diesmal mit deutlich mehr betroffenen Namen.

Parameter-Vertrag bündelt wo möglich ganze Hook-Rückgabewerte (projectActions/
selectionState/toolState/topBarState/dialogState/contextMenuState als Pick<>
der jeweiligen Hook-Signatur) statt ~150 Einzelfelder aufzulisten — erhält
volle Typsicherheit ohne manuelles Retippen jedes Handler-Signatur.

App.tsx: 4594 → 3690 Zeilen.

tsc -b sauber, vitest 891/891 grün, vite build ok.
2026-08-20 00:46:22 +02:00
karim 103f704e87 2D-Griff-Editieren aus App.tsx in eigenen Hook extrahiert (state/useGripEditing.ts).
Eckpunkt-/Kanten-Griffe der aktuellen Auswahl (Wand/2D-Element/Decke/Raum/
Öffnung/Treppe), deren Zieh-Handler (Ortho/Snap/koinzidente Nachbar-Ecken)
sowie der Tab-Feld-Controller für präzise Länge/Winkel-Eingabe während eines
Vertex-Drags waren als drei separat benannte, aber tatsächlich zusammen-
hängende Abschnitte in App.tsx verstreut. Jetzt ein Hook mit explizitem
Parameter-Vertrag (Selektion/Projekt/Snap-Settings/Projekt-Mutationen rein,
grips/edgeGrips/gripHandlers/Feld-Controller raus) statt anonymer Closures
über den gesamten Komponenten-Scope.

Reine Verschiebung, keine Verhaltensänderung — einzige Korrektur beim
Verschieben: der Öffnungs-/Wand-Snap in onGripMove nutzte computeSnap direkt
(gegen ein um das gezogene Element bereinigtes Projekt), das im ersten Entwurf
versehentlich mit dem allgemeinen snapFor-Helper vertauscht worden wäre —
beim Nachvollziehen der Original-Logik korrigiert, bevor es committet wurde.

App.tsx: 5096 → 4594 Zeilen.

tsc -b sauber, vitest 891/891 grün, vite build ok.
2026-08-20 00:35:03 +02:00
karim d6fae13326 Viewport3D.tsx aufgeteilt: Kamera-Helfer + Mesh-Builder nach src/viewport/viewport3d/.
Gleiches Muster wie beim PlanView.tsx-Split: die Komponente (ThreeViewport3D)
bleibt, die anschliessenden ~1000 Zeilen reiner three.js-Erzeugung wandern in
zwei fokussierte Module. Reine Verschiebung, keine Verhaltensänderung.

- viewport3d/camera.ts: visibleFloorKey, updateOrthoFrustum, applyView3d
  (Ortho-Frustum-Bemessung + die kanonischen Blickwinkel-Presets).
- viewport3d/meshBuilders.ts: MatCaches/BuildOpts/ContextMats, buildContext,
  buildBuilding (+ private buildFloor/addWallMeshes/addOpeningMeshes/
  addCeilingMesh/addStairMeshes/addLayerPrism/addDrawing2DLines/layerMaterial).

Viewport3D.tsx: 2661 → 1588 Zeilen.

tsc -b sauber, vitest 891/891 grün, vite build ok.
2026-08-19 23:05:25 +02:00
karim 528f213f1f PlanView.tsx aufgeteilt: Mathe/Overlays/Primitiv-Rendering nach src/plan/planView/.
Reine Verschiebung, keine Verhaltensänderung. PlanView.tsx (4169 → 2465
Zeilen) behält nur noch die Komponente selbst + Interaktions-Logik
(Maus/Tastatur/Griffe/Snapping/Undo).

- planView/geometry.ts: ViewBox, toScreen, Papier-Massstab-Umrechnung
  (meetScale/scaleFromView/viewForScale/printStrokeVb), Marquee-/Punkt-
  in-Polygon-Tests (pointInPolygon, marqueeHit, marqueeHitDrawings).
- planView/primitives.tsx: HatchPattern, buildDrawingRuns, DrawingRunShape,
  PrimitiveShape, renderPrimitive (der grosse Primitiv→SVG-Renderer).
- planView/overlays.tsx: DraftOverlay (Werkzeug-Vorschau, Cursor-HUD,
  Winkel-Führungslinie, Snap-Marker).
- planView/PlanRulers.tsx: die Lineale oben/links.

tsc -b sauber, vitest 891/891 grün, vite build ok.
2026-08-19 22:54:58 +02:00
karim 1507c2bf00 God-Files aufgeteilt (ResourceManager/generatePlan/toWalls3d/App.tsx-Handler), Totcode entfernt (geometry-Crate, OCCT/HLR-Spike), PDF/Bild-Import ergänzt.
ResourceManager.tsx, generatePlan.ts und toWalls3d.ts sind nach Bauteil-
Domäne in src/ui/resourceManager/, src/plan/generatePlan/ und
src/plan/toWalls3d/ aufgeteilt (reine Verschiebung, keine Verhaltens-
änderung). App.tsx verliert weitere Handler-Gruppen an eigene Hooks
(useExportHandlers, useImportHandling, useKeyboardShortcuts) sowie
Hit-Testing an src/viewport/planHitTest.ts.

Unbenutzte Rust-geometry-Crate (durch WASM-Joins abgelöst) sowie der alte
OCCT/HLR-Schnitt-Spike (hlr.ts/occt.ts, vite occt-Aliases) entfernt.

Neu: PDF-Import als Rasterbild (src/io/pdfImport.ts) + gemeinsamer
Bild-Asset-Helfer (src/io/imageAsset.ts) für Plan- und Layout-Bilder.

tsc -b sauber, vitest 891/891 grün.
2026-08-19 22:45:41 +02:00
karim 06c0143d32 Start-Sequenz ergänzt: Splash-Screen, dann Startbildschirm mit Neues Projekt/Projekt öffnen/Zuletzt geöffnet.
Zuletzt-geöffnet-Liste (state/recentProjects.ts) trägt sich beim Öffnen und
beim Speichern automatisch ein, inkl. Öffnen über einen bekannten Pfad ohne
Dialog (verwaiste Einträge fliegen raus, wenn die Datei fehlt). Der Splash
wartet bewusst nicht auf den Update-Check — der läuft unabhängig im
Hintergrund weiter, damit ein langsames Netz den Start nie verzögert.
2026-07-31 17:40:35 +02:00
karim 08e74b7e0e Projektdatei-I/O (Speichern/Öffnen/Lock) und Schnellexport (CSV/IFC/OBJ/
STL) sowie die Ausführung eines bestätigten DXF/DWG-Imports aus App.tsx
in useProjectFileActions() extrahiert.
2026-07-31 17:03:41 +02:00
karim 6ee6bf5dd1 Selbst-Update-Funktion ergänzt (Tauri Updater-Plugin) + App-Branding
"dossier" statt "Dossier".

Prüft beim Start still gegen ein Gitea-Release-Manifest (latest.json),
bietet im Settings-/About-Dialog einen manuellen Check sowie einen
Versionsverlauf mit Rollback-Download (versions.json, eigener schlanker
Index neben dem Updater-Manifest). Dafür: tauri-plugin-updater/process/
http/shell + zugehörige Capabilities, neue App-Icons (auch für die
plattformspezifischen Bundle-Formate), sowie ein kleingeschriebener
Produktname samt erweitertem nativen Info-Fenster (macOS-About).
2026-07-31 17:03:23 +02:00
karim 9c9e118d98 TopBar: In-App-Menüleiste unter Tauri ausgeblendet — die native macOS-
Systemmenüleiste (useNativeAppMenu) deckt dieselben Aktionen bereits ab,
die eigene Leiste wäre dort nur Dopplung. Im Browser/Electron ohne
natives App-Menü bleibt sie weiterhin die einzige Quelle.
2026-07-31 17:01:18 +02:00
karim 31fe93c122 Zeichenwerkzeug-Zifferntasten: 1 (Text) und 3 (Kreis) fehlten in der
Shortcut-Map, obwohl beide Befehle längst fertig implementiert waren.

Zusätzlich musste man nach jedem gesetzten Punkt Esc drücken, bevor eine
Zifferntaste das nächste Werkzeug wählte — der Fokus blieb im Befehlsfeld
hängen (dort wird nach Abschluss nicht mehr geblurred) und die globale
Kürzel-Prüfung ignorierte jede Eingabe im Feld pauschal. Jetzt gibt die
Engine den Fokus frei, sobald ein Befehl fertig ist (Commit/Abbruch), und
eine Zifferntaste wählt direkt das nächste Werkzeug (Vectorworks-Stil) —
aber nur, wenn das Feld leer ist UND der aktuelle Schritt weder Tab-Felder
noch Freitext erwartet (Textlabel-Eingabe wie „Raum 101" bleibt geschützt).
2026-07-31 17:00:57 +02:00
karim 1ba6f7bcf4 Dach/Wand: Giebel-Endfüllung (dünne Einzelfläche) nicht mehr emittiert — lag
deckungsgleich vor der ohnehin bis zur Dach-Unterkante hochgeklemmten
Giebelwand und wirkte als sichtbare Doppel-Schicht. Zusätzlich werden
aufeinanderfolgende Wandstücke gleicher geklemmter Oberkante (z. B. am First)
wieder zu einem Body verschmolzen statt in viele 0.15-m-Fragmente zu zerfallen.
2026-07-31 17:00:09 +02:00
karim d1de33bd6d Ressourcen-Manager: "+" im nativen Fenster fügte keine neuen Einträge hinzu
"+"-Buttons für Bauteile/Schraffuren/Linienstile/Wand-/Decken-/Dachstile
waren direkt als onClick={onAddX} verdrahtet — React reicht dabei das
Klick-Event als Argument durch. Im In-App-Overlay harmlos (Store-
Funktionen ignorieren Extra-Argumente), aber im nativen Ressourcen-
Fenster läuft jeder Aufruf über die Tauri-Event-Brücke (emitTo), die den
Aufruf als JSON serialisiert — ein React-Event mit zirkulären DOM-
Referenzen lässt sich nicht serialisieren, der Aufruf scheiterte lautlos.
Fix: alle betroffenen Buttons rufen den Handler jetzt explizit ohne
Argument auf. Zusätzlich die bisher stillschweigend verschluckten Fehler
in der Fenster-Sync-Brücke sichtbar gemacht (console.error statt leerem
catch), fürs nächste Mal.
2026-07-22 20:07:31 +02:00
karim 29018f2dff Projekt-Mutationen/Ressourcen-Typ-CRUD aus App.tsx in useProjectActions() extrahiert
Bündelt Projekt-Mutationen (Store-Actions), Wand-/Decken-/Dach-/Tür-/
Fenster-/Treppentyp-CRUD sowie diverse Element-Mutationsaktionen (Teil
der laufenden App.tsx-Verkleinerung).
2026-07-22 20:06:32 +02:00
karim 448546994c Auswahl-Zustand aus App.tsx in useSelectionState() extrahiert
Bündelt die Auswahl-Mengen aller Bauteil-/Zeichnungs-Arten (Wand, 2D-
Zeichnung, Decke, Dach, Öffnung, Treppe, Raum, Extrusion, Stütze,
Kontext-Objekt) plus abgeleitete Einzel-Ids, die 2D-Zwischenablage und
den 3D-Live-Schnitt-Treiber (Teil der laufenden App.tsx-Verkleinerung).
2026-07-22 00:42:07 +02:00
karim b3e6aaaec1 Zeichenwerkzeug-Zustand aus App.tsx in useDrawingToolState() extrahiert
Bündelt aktives Werkzeug, Wandtyp/Linienstil, Snap-Einstellungen,
Werkzeug-Zustand-Ref, Draft-Vorschau, aktive Transformation
(Bewegen/Spiegeln/Drehen) und die Kopienanzahl für Array/Distribute
(Teil der laufenden App.tsx-Verkleinerung).
2026-07-22 00:35:12 +02:00
karim e41821c97a 2D-Massstab: dpi-Berechnung fälschlich mit devicePixelRatio multipliziert
CSS-Pixel sind laut Spezifikation physisch konstant 1/96 Zoll, unabhängig
von devicePixelRatio (der beschreibt nur die Geräte-Pixel-Dichte hinter
einem CSS-Pixel, relevant für Rasterungs-Schärfe, nicht für die physische
Grösse). Die Papier-Massstab-Umrechnung in PlanView.tsx nahm bisher
96 * devicePixelRatio an und verfälschte dadurch den angezeigten wie
gemessenen Massstab auf jedem Retina-/HiDPI-Bildschirm um genau den
Faktor devicePixelRatio (per Lineal am Bildschirm nachgemessen). Betrifft
nur die Bildschirmanzeige, nicht den PDF/DXF-Export (eigene Berechnung).
2026-07-22 00:30:57 +02:00
karim 18aee59cff Oberleisten-/Status-Zustand aus App.tsx in useTopBarState() extrahiert
Bündelt Detailstufe, 3D-Rendermodus, Sichtfeld, Boden-Referenzraster,
Plan-Farbmodus, Achslinien, Linienmodus, Fadenkreuz-/Snap-Farbe,
Papier-Massstab, Live-Zoom, Cursor und Ausschnitt-Auswahl (Teil der
laufenden App.tsx-Verkleinerung).
2026-07-22 00:28:34 +02:00
karim 3b8d48eaf5 Zahnrad-Icons in Zeichnungsebenen/Ebenen öffnen jetzt die passenden Fenster
Bisher öffneten beide Panel-Kopfzeilen die allgemeinen Einstellungen statt
ihrer eigenen: Zeichnungsebenen jetzt den Zeichnungsebenen-Fenster-Manager,
Ebenen die Ebeneneinstellungen der aktiven Kategorie.
2026-07-21 19:51:03 +02:00
karim 476f31ccad Kontextmenü-/Editor-State aus App.tsx in useContextMenuState() extrahiert
Bündelt layerClipboard, menu, editor, nativeLayerSettingsCode,
stampEditorRoomId und editTextId in einem eigenen Hook (Teil der
laufenden App.tsx-Verkleinerung).
2026-07-21 19:31:19 +02:00
karim 85f386a496 App.tsx: Hell/Dunkel-State nach theme/useThemeMode.ts
Reiner Umzug von useState + Setter-Wrapper (persistiert in localStorage via
themeMode.ts) in einen eigenen Hook. Verhaltensgleich.
2026-07-21 16:24:33 +02:00
karim 43ea7c5d8e App.tsx: Dialog-Sichtbarkeits-State nach state/useDialogState.ts
Reiner Umzug von neun useState-Deklarationen (Ressourcen-Fenster, Layout-
Ansicht, Import-/Export-/Einstellungs-Dialoge, Drop-Feedback) in einen
eigenen Hook — bewusst KEIN Store-Slice (dieser State ist laut den
Original-Kommentaren explizit lokal/transient, keine Undo/Redo- oder
Cross-Component-Relevanz). Verhaltensgleich.
2026-07-21 16:15:59 +02:00
karim b621a7da64 App.tsx entschlackt: Kontextmenüs nach src/menus/, Views nach src/views/
Reiner Umzug (verhaltensgleich, byte-identische Funktionskörper): Kontextmenü-
Builder (buildMenuItems + layer/level/plan/viewport-Menüs) nach
src/menus/contextMenu.tsx; InlineEditor sowie Content/LevelPlanView/
SectionPlanView (der View-Router der Hauptansicht) nach src/views/. App.tsx
7130 → 5917 Zeilen. Setzt die in CONVENTIONS.md/state-architecture.md
vorgesehene, bisher nie umgesetzte Struktur endlich um (STATUS.md §4.3).

Dazu ein unabhängiger CSS-Fix: .plan-selected (Auswahl-Hervorhebung im 2D-Plan)
fehlte die fill-opacity, die zwei Regeln weiter unten bei .plan-marquee/
.tool-preview-fill schon gesetzt ist. Im Dunkel-Theme ist --accent-dim
transparent (zufällig unauffällig), im neuen Standard-Hell-Theme aber fast
deckend weiss — dadurch verdeckte die Auswahl die Schraffur der gewählten
Wand/Decke/des Raums vollständig.
2026-07-21 14:47:40 +02:00
karim 76c9d164c9 Materialbibliothek: statisches Broad-Set entfernt, Live-ambientCG bleibt
Der aus der Desktop-Session übernommene statische 88er-Katalog listete 75
Materialien ohne mitgelieferte Texturen (kaputte Thumbnails in der
Schnellauswahl). Bibliothek auf die 13 tatsächlich gebündelten Starter
gekürzt; Fetch-Script und der zugehörige .gitignore-Block entfernt. Alles
darüber hinaus läuft über die bereits vorhandene Live-Suche (1K/2K/4K).
2026-07-21 13:35:44 +02:00
karim 78d3e6a955 Fix: doppelten RoofingTiles013A-Eintrag nach Rebase entfernt 2026-07-20 11:05:08 +02:00
karim a009a5e9b4 Materials-Feature: Bibliothek + Fetch-Script + Manifest (aus Desktop-WIP übernommen) 2026-07-20 11:03:30 +02:00
karim b9e330ecb7 Schnittebenen 2D↔3D Phase 1+2, native Fenster, Hell/Dunkel-Theme, SWISSIMAGE-Import
- Schnittebenen: 3D-Live-Schnitt folgt der gewählten Grundriss-Schnittlinie
  (section3dCutId/sectionPlaneFromLevel), Schalter "Im 3D schneiden" im
  Objekt-Info, Doppelklick auf Schnittlinie springt in 2D-Schnittansicht,
  unsichtbare Ebenen blenden ihre Führungslinie aus
- Eigene native Tauri-Fenster für Kontext-Import/Zeichnungsebenen/
  Ebenen-Einstellungen/Ressourcen/Einstellungen + klassische Menüleiste
  (AppMenuBar) neben der Wortmarke
- Hell/Dunkel-Umschalter (Einstellungen → Darstellung), persistiert,
  flackerfrei vor erstem Render gesetzt
- SWISSIMAGE-Luftbild-Import (swisstopo WMS) als Kontext-Hintergrundebene
- UI-Politur: Werkzeug-Panel Symbole/Liste umschaltbar, Topbar-Quick-Access-
  Icons entfernt, Zahnrad→Einstellungen in Panel-Köpfen, Footerbar/
  Snap-Marker/Maß-HUD auf helle Pillen-Sprache umgestellt
- Neues Dachziegel-Material (RoofingTiles013A)
2026-07-20 10:51:37 +02:00
karim 51e57c368b swissBUILDINGS3D-Import: auf den gesuchten Radius zuschneiden statt die ganze STAC-Kachel zu liefern
Jede STAC-Kachel (DXF) liefert ALLE Gebäude der Kachel als EIN gemeinsames
Mesh (parseDxf sammelt alle 3DFACE/Polyface-Entities eines Files in denselben
Puffer) — ohne Zuschnitt landete bei einem Import IMMER die komplette Kachel
im Modell, teils mehrere Kilometer über den gesuchten Suchradius hinaus.
Live gegen echte STAC-Daten verifiziert: zwei 1.4 km auseinanderliegende
Adressen (gleiche Kachel) lieferten vorher IDENTISCHE, unclipped Geometrie
(62532 Dreiecke); danach jeweils nur die ~400 Dreiecke im eigenen Suchradius.

clipMeshToBbox schneidet + kompaktiert das Mesh (nicht referenzierte Ecken
entfernt, Indizes neu gemappt) — sonst sähe jede Verbraucher-Logik, die roh
über `positions` statt `indices` iteriert (Bounding-Box, Kamera-Fit), weiter
die volle ungeschnittene Ausdehnung.
2026-07-12 21:35:31 +02:00
karim 932a229f09 Fenster/Tür: 2D und 3D setzen "aussen" jetzt auf dieselbe Wandfläche
Nutzer-Report: "im 2D innen bündig und im 3D aussen bündig". Root Cause: das
2D-Vorzeichen für insetFace:"aussen" (windowSymbol) war gegenüber der echten
Aussenschicht der Wand (erste Wandtyp-Schicht, kleinster Achs-Offset in
addWallPoche/pushSegment) invertiert — "aussen" landete am inneren
Wandputz statt am äusseren. Dieselbe Vorzeichenverwechslung steckte auch in
addOpeningFrameBand (Tür-Rahmenband), den Sturzlinien und dem Rollladenkasten.
Die 3D-Seite (openingAxisBox/resolveFrameNormalRange) war bereits korrekt
(zwei kompensierende Vorzeichen ergaben zufällig das richtige Ergebnis) und
bleibt unverändert.

+1 Regressionstest, der 2D- und 3D-Rahmenposition direkt gegen die bekannte
Aussenschicht der Wand prüft.
2026-07-12 21:24:48 +02:00
karim da0f066e49 Fenster: Rahmen-Kontur in Aufsicht separat von den geschnittenen Laibungsblöcken stylebar
Bisher teilten sich die geschnittenen Laibungs-/Stulp-Blöcke (O-Blöcke,
Schnittfläche des Rahmenmaterials) und die durchlaufende Rahmen-Kontur
zwischen ihnen (Aufsicht, nicht geschnitten) dieselbe frameLine-Farbe. Neues
Opening.frameViewLine übersteuert nur noch die Kontur; ohne Angabe fällt sie
weiterhin auf frameLine zurück (bisheriges Verhalten unverändert). frameLine
im UI zu "Rahmen (Schnitt)" umbenannt, um die beiden Kategorien zu unterscheiden.
2026-07-12 20:19:20 +02:00
karim 5be49c5557 Neuer Bezugspunkt: Projekt nach Kataster-/3D-Import auf praktischen Ursprung verschieben
Nach einem Standort-Import landet die Kontext-Geometrie dort, wo der gesuchte
Ort war — das kann weit vom bisherigen Modell-Ursprung liegen. Neuer Befehl
"georef" (Button in SitePanel) verschiebt das GESAMTE Projekt per Klick so,
dass der gewählte Punkt zum neuen Ursprung wird; geoAnchor wandert automatisch
mit, der reale LV95-Bezug bleibt exakt erhalten (rekonstruierbar, auch beim
späteren Export). Bewusst nur Translation, keine Rotation — mehrere Felder
(Roof.ridgeAxis, Column.rotation, Text-/Bild-Rotation) sind achsen-/winkel-
gebunden und bräuchten bei einer Drehung eigene Sorgfalt.
2026-07-12 20:13:22 +02:00
karim 0ab3f70277 Kontext-Objekte im 3D-Viewport anwählbar (Klick, Bounding-Box-Highlight, Löschen)
Importierte Gebäude/Terrain-Meshes waren bisher nur ein Eintrag im Geo-Panel,
nicht direkt im 3D anwählbar. Raycast/Pick-Geometrie um Kontext-Meshes
erweitert, Auswahl-Kanal (selectedContextObjectIds) ergänzt, Highlight als
Bounding-Box-Umriss (volles Dreiecks-Wireframe wäre bei grossen Importen zu
dicht), Entf-Taste löscht die Auswahl, SitePanel hebt sie in der Liste hervor.
2026-07-12 19:54:00 +02:00
karim 02c36bd35b Georeferenzierung: persistenter Standort-Bezug statt Neuberechnung je Import
Neues Project.geoAnchor: verbindet EINEN Modell-Punkt mit seiner realen
LV95-Koordinate. Bisher berechnete jeder Standort-Import (Gebäude/Terrain/
OSM) unabhängig einen neuen Bezug aus der jeweils gesuchten Adresse — bei
mehreren Importen mit leicht unterschiedlichen Suchbegriffen landete
importierter Kontext lagefalsch zueinander.

Der erste Import in einem Projekt setzt den Bezug automatisch (Modell-(0,0)
= gesuchter Ort) und speichert ihn; alle weiteren Importe verwenden densel-
ben Bezug, unabhängig vom neu gesuchten Ort (der bestimmt nur noch WOVON
Daten geladen werden, nicht mehr WOHIN sie im Modell platziert werden).
UI: Anzeige des aktiven Bezugs im Import-Dialog mit Zurücksetzen-Option.

Löst das strukturelle Problem noch nicht vollständig (der Bezug sitzt immer
bei Modell-(0,0) — passt nur, wenn das eigene Gebäude dort gezeichnet ist),
aber behebt die akute Inkonsistenz zwischen mehreren Importen.
2026-07-12 19:33:16 +02:00
karim 450932a0c3 DXF-Import: Polyface-Mesh-Faces (POLYLINE) wurden nie erkannt — swissBUILDINGS3D-Gebäude blieben leer
Root Cause für „Gebäude-Import funktioniert nicht": Die installierte
dxf-parser-Version liefert Face-Vertex-Indizes NICHT als `faces`-Array,
sondern als vier einzelne Felder (Gruppencodes 71–74: faceA/faceB/faceC/
faceD). Unser addPolyfaceMesh/isMeshPolyline prüfte auf ein `faces`-Array,
das in dieser Version nie existiert — jede POLYLINE-Polyface-Mesh (exakt das
Format der swissBUILDINGS3D-DXF-Kacheln) ergab dadurch 0 Dreiecke, obwohl
Positionen geschrieben wurden. Live gegen echte swissBUILDINGS3D-Kacheln
verifiziert: vorher 0 Meshes, jetzt tausende Dreiecke mit realistischen
Schweizer Höhenwerten. +3 Tests (rohes DXF, nicht gemockt, deckt auch die
zugrundeliegende Bibliothek ab), Suite 794 grün.
2026-07-12 19:22:53 +02:00
karim eabc71c5e8 swissBUILDINGS3D-Import: Absturz bei riesigen Kacheln behoben, Übersprungene sichtbar gemacht
Nutzer-Report „funktioniert nicht": Der Import stürzte mit einem harten
RangeError ab, sobald eine STAC-Kachel entpackt die maximale JS-String-Länge
überschritt (DXF komprimiert stark — eine Kachel unter dem 150-MB-Limit kann
trotzdem >700 MB unkomprimierten Text ergeben, real reproduziert für ein
dicht bebautes Stadtzentrum). downloadAssetText prüft jetzt zusätzlich die
JSZip-interne unkomprimierte Grössenschätzung VOR dem Entpacken und fängt
verbleibende Fehler (Netzwerk/ZIP/String-Länge) sicher ab, statt zu werfen.

Zu grosse/fehlgeschlagene Kacheln wurden bisher stumm übersprungen (0 Gebäude,
keine Erklärung — sah wie ein Bug aus). fetchBuildings3d liefert jetzt
skippedTiles mit; der Dialog zeigt „X Kachel(n) übersprungen, zu gross" statt
eines wortlosen Leer-Ergebnisses.
2026-07-12 19:15:45 +02:00
karim b68fd7f58e 3D-Ansicht: neuer Darstellungsmodus "Schattiert mit Kanten" (BIM-Look)
Neuer RenderStyle::ShadedEdges in render3d: wie "shaded" (echte Bauteilfarben,
beleuchtet), zusätzlich dunkle Modell-Kanten obenauf wie bei "hidden" — der
typische Revit/ArchiCAD-Look, der bisher fehlte (nur reines Weiss+Kanten via
"hidden" oder reine Bauteilfarben ohne Kanten via "shaded" waren möglich).
Nutzt dieselbe tiefengebiaste Flächen-Pipeline wie "hidden", damit die Kanten
sauber obenauf liegen (kein Z-Fighting). +6 Rust-Tests (--features render,
83/83 grün). Als neue Option "shaded-edges" in beiden Darstellungsart-
Dropdowns der 3D-Oberleiste; Three.js-Fallback ignoriert den Wert graceful
(fällt auf shaded zurück, bekommt bewusst keine neuen Features).
2026-07-12 18:59:05 +02:00
karim 7a098443f9 Fenster: Linienstärke/Farbe (sashLine) wirkt jetzt auch bei 1:100
Die einzige bei "grob" sichtbare Glaslinie war fest codiert (weder Opening.
color noch die neuen Kategorie-Übersteuerungen griffen dort). Nutzt jetzt
sashLine (Fenster-Kategorie), konsistent mit mittel/fein.
2026-07-12 18:52:08 +02:00
karim 31d8dd7916 Fenster: Flügelstoss-Blöcke separat von Laibungs-Enden steuerbar (sashLine statt frameLine)
windowSymbol trug beide Blocktypen (Laibungs-Enden UND Flügelstoss-Marken)
im selben meetingMarks-Array, dadurch übersteuerte frameLine auch die Marke
zwischen den Flügeln — Nutzer wollte diese separat regelbar. Aufgeteilt in
jambMarks (Laibungs-Enden, zählt zu "Rahmen") und meetingMarks (Flügelstoss,
zählt zu "Fenster"/sashLine).
2026-07-12 18:51:09 +02:00
karim f8383256a8 Attribute: Farbfeld zeigt nur noch ein Quadrat statt zwei
ColorHexField stellte Swatch UND natives <input type="color"> sichtbar
nebeneinander dar (zusätzlich zum separaten Hex-Textfeld) — wirkte wie zwei
Farbfelder pro Zeile. Hex-Textfeld entfernt, natives Color-Input liegt jetzt
unsichtbar über dem Swatch (öffnet die native Palette beim Klick auf das
einzige sichtbare Quadrat). Betrifft alle Attribute-Farbfelder in der App,
nicht nur die neuen Fenster-Linien-Felder.
2026-07-12 18:48:25 +02:00
karim 269aef80e2 Fenster: Farbe/Strichstärke je Linien-Kategorie im Attribute-Panel einstellbar
Neue per-Öffnung-Übersteuerung (Opening.frameLine/sashLine/sillLineStyle,
je {color?, weight?}) für Blendrahmen+Stulp-/Laibungsblöcke, Flügelrahmen/
Sprossen und die Auf-/Untersicht-Sims-Andeutung separat. Fehlt eine
Übersteuerung, gilt weiterhin der bisherige Default (Opening.color/Haarlinie).
UI: drei neue Zeilen (Farbfeld + Strichstärke) im Objekt-Info-Panel bei
selektiertem Fenster.
2026-07-12 18:45:28 +02:00