Commit Graph

119 Commits

Author SHA1 Message Date
karim 6a13ede6b7 Startup-Splash (petrol-gruen) waehrend Plugin-Init
Verdeckt visuell die 3+ Sekunden Wartezeit beim Cold-Start (Panel-
Registration + WindowLayout-Apply). Stilistisch identisch zum
Launcher-Splash: petrol-gruener Verlauf mit "DOSSIER."-Logo, pulsierendem
Dot, animierter Progress-Bar.

Architektur:
- _startup_splash.py: zentrale show() / hide() Helpers
  - Borderless Eto.Forms.Form (420x160), Topmost, kein Taskbar-Eintrag
  - WebView mit Inline-HTML (gleicher Stil wie launcher/public/splash.html)
  - Sticky-Key _dossier_startup_splash haelt die Form-Referenz
  - Safety-Timeout 8s falls hide() vergessen wird

- startup.py _load_all: show() ganz am Anfang (bevor Imports laufen)
- oberleiste._on_ready: hide() via 200ms-Timer NACH window-layout-apply
  (bzw. nach skip) — Layout-Animation ist auf Panels in Finalposition
  kurz sichtbar bevor Splash verschwindet

Effekt: User sieht sofort einen schoenen Branded-Loading-Screen statt
3s grauer Rhino-UI mit halb-geladenen Panels.
2026-05-27 19:31:00 +02:00
karim edaf83229b Startup-Performance: WindowLayout-Apply skippen bei Quick-Restart
Diagnose-Log zeigte: 3046 ms (= 93% der 3.3s Cold-Start-Zeit) gehen
allein in den _-WindowLayout RunScript-Call beim ersten oberleiste-
_on_ready. Rhino dockt dabei alle Panels neu — teuer.

Optimierung: Marker-File-basiertes Skip
- Nach erfolgreichem Apply: ~/Library/Application Support/
  ch.gabrielevarano.Dossier/layout_marker.json mit Timestamp + Name
- Bei _on_ready: wenn Marker < 10 min alt UND derselbe Layout-Name →
  skip (Rhino "remembers" die Panel-Positionen meistens noch im
  internen State, nur unzuverlaessig zwischen LANGEN Pausen)
- Sonst (kalter Tag, langer Reboot, anderer Name): apply normal

Effekt fuer typischen Dev-Workflow (haeufige Quick-Restarts):
- Erster Restart heute: 3s Apply (wie bisher) + Marker
- Naechste Restarts < 10 min spaeter: ~0ms (skip)
- Nach Pause > 10 min: erneuter Apply

Helpers _is_layout_recently_applied + _mark_layout_applied. Marker
wird auch geschrieben wenn User manuell apply (z.B. via Settings-
Dialog oder Launcher pendingApplyLayout) → konsistent.

Falls Layout doch falsch ist nach Skip: Oberleiste-Settings →
"Jetzt anwenden"-Button erzwingt Apply.
2026-05-27 19:24:02 +02:00
karim 6fee7bd143 Startup-Diagnose: Imports + Display-Modes + Window-Layout messen
Bisher: Wall-time 3.2s vs gemessene Arbeit 0.9s → 2.3s unmeasured.
Jetzt werden auch die bisher blinden Stellen getrackt:

- 9× Modul-Import (rhinopanel, oberleiste, ausschnitte, ...) als
  phase="import", label=mod_id
- oberleiste display-mode-Ensure-Loop (4× DisplayModeDescription-Calls)
  als phase="display_modes"
- _apply_window_layout RunScript-Sequenz als phase="window_layout"
- text_editor doppelklick-Hook + view-modes assign + unit-check als
  post_init/hook Phases

Nach Reload sollte das Summary zeigen wo die 2.3s versickern → daraus
gezielte Optimierungen.
2026-05-27 19:08:02 +02:00
karim 61923e1b2b SIA-Bilanz CSV-Export aus Elemente-Uebersicht
Download-Button (file_download Icon) neben den Expand/Collapse-Buttons
in der Toolbar. Klick → SaveFileDialog (Default 'sia_bilanz.csv') →
schreibt Excel-kompatible CSV.

Backend (elemente_uebersicht._export_bilanz):
- Wide-Format: Spalten = Kategorie + ein Geschoss + Total
- Zeilen: HNF, NNF, NF, VF, FF, NGF, GF, AGF, Räume (count), Personen
- Werte via _elm.compute_sia_bilanz fuer jeden Scope (gleiche Logik
  wie Bilanz-Stempel + Uebersicht — single source of truth)
- Format: Semikolon-Separator + UTF-8 BOM + Komma als Dezimaltrenner
  (Excel CH/DE-kompatibel, oeffnet ohne Umweg)
- Flaechen mit 2 Nachkommastellen, Raume/Personen als int
2026-05-27 01:13:08 +02:00
karim e2d66a5d64 Personen-Belegung pro Raum + Stempel-Aggregation
Architekten-Workflow: bei Schulen/Buero/Versammlungsstaetten muss die
Personenzahl pro Raum erfasst werden (SIA: m²/Person als Indikator).

Backend:
- UserString dossier_raum_personen (int)
- _attach_meta + _read_meta + state-emit
- _update_wall raum-Branch akzeptiert "personen" im Patch
- compute_sia_bilanz aggregiert personen-Summe ueber Scope
- Stempel: neue Show-Flag stempel_show_personen (default false) +
  Bilanz-Renderer-Zeile "N Personen"
- Stempel-Stil-Field showPersonen mit dabei

Frontend (RaumProperties):
- Personen-Input (number, min 0) zwischen Funktion + Layout-Builder
- Nur sichtbar bei normalen Raeumen (nicht GF/AGF)
- Live-Anzeige "m²/Person" Suffix wenn area + personen > 0
  → User sieht sofort ob die Belegung sinnvoll ist (SIA-Vergleich)

Frontend (StempelProperties):
- Neuer Show-Toggle "Personen" (default off)
- Live-Vorschau zeigt Personen-Summe wenn aktiv + > 0
- _OFF_BY_DEFAULT Set generalisiert Default-Handling (showCount, showPersonen)
2026-05-27 01:08:58 +02:00
karim 1c3b0f3919 Ebenen: Plangrafik 60 → 80, RAEUME/GF/AGF auf 60/61/62
Doppelbelegung Code 60: alte Konvention "Plangrafik" steht im Weg fuer
die neuen Raum-Layer (RAEUME 60, GF 61, AGF 62). Wenn ein Doc beides
hat, fiel die Auto-Add-Logik in _find_ebene_sublayer_name aus → Raeume
landeten auf Rhino-Layer "60_RAEUME" der aber im Dossier-Ebenen-Panel
nie auftauchte (dort stand 60 = Plangrafik).

Fix in zwei Teilen:

1) Frontend-Default-Schema (App.jsx INITIAL_EBENEN):
   - 60: RAEUME (neu, fuer HNF/NNF/VF/FF)
   - 61: GF (Geschossflaeche)
   - 62: AGF (Aussengeschossflaeche)
   - 80: Plangrafik (verschoben von 60)

2) One-shot Migration in elemente._migrate_plangrafik_60_to_80_once():
   - Detect: dossier_ebenen hat code=60 + name=Plangrafik
   - Action: code 60 → 80, RAEUME/GF/AGF auf 60/61/62 hinzufuegen
   - Rhino-Layer rename: alle "60_Plangrafik" Layer → "80_Plangrafik"
   - build_layers + broadcast_state → Ebenen-Manager UI aktualisiert
   - Sticky-Flag verhindert Re-Run

Plus kleinerer UX-Fix: Skala-Dropdown-Labels gekuerzt
("fix (m)" / "massstaeblich (mm)" statt langer Beschreibungen).
2026-05-27 01:00:33 +02:00
karim 2a838aee93 Stempelstile-Tab in ProjectSettings (Manager analog Raumstile)
Beide Stempel-Familien (Raum-Stempel + Bilanz-Stempel) sind jetzt
konsistent verwaltbar in den Project-Settings.

Backend:
- elemente.py: DUPLICATE_STEMPEL_STIL + REORDER_STEMPEL_STILE handlers
- rhinopanel.py ProjectSettings-Bridge:
  - stempel_stile in params (initial)
  - 4 neue dispatch-Handler (SAVE/DELETE/DUPLICATE/REORDER_STEMPEL_STIL)
  - direkter Dispatch zu elemente.load/save_stempel_stile (kein Roundtrip)
  - STEMPEL_STILE_UPDATED-Message nach jeder Op

Frontend:
- rhinoBridge.js: reorderStempelStile + duplicateStempelStil exports
- ProjectSettingsDialog:
  - Neuer Tab "Stempelstile" parallel zu "Raumstile"
  - Drag-Reorder via HTML5 native, Inline-Rename, Duplicate, Delete
  - Mini-Preview: Header + Anzahl aktiver Show-Flags
  - Name wird mit Font/Bold/Italic des Stils gerendert als Vorschau
2026-05-27 00:51:57 +02:00
karim 975071c995 Stempel: Stile + Layout-Customisation (Header, Show-Toggles)
Bilanz-Stempel hat jetzt:
- Custom Header (Default "Nutzflächen", frei editierbar)
- Per-Stempel Show-Toggles: scope, hnf, nnf, nf, vf, ff, ngf, gf, agf,
  count (Anzahl Raeume), separators (Trennlinien)
- Stempel-Stile (Presets) per Doc — separate Storage von Raumstempel-
  Stilen (dossier_stempel_stile)

Backend (elemente.py):
- UserStrings: dossier_stempel_header + dossier_stempel_show_* (11 Flags)
  + dossier_stempel_stil_id
- _make_stempel_text erweitert: header_text, show_scope, show_separators,
  show_count, visibility-dict
- _format_bilanz_lines: visibility + show_separators + show_count Args
- load_stempel_stile / save_stempel_stile (eigener doc.Strings-Key)
- Bridge-Handler SAVE/DELETE/APPLY_STEMPEL_STIL (analog Raum-Stil-Pattern,
  inkl. Bulk-Apply via Selection + applyToIds-Mechanism wie SaveRaum-Stil)
- _sync_stempel_to_source spiegelt Font/Bold/Italic/TextHeight vom Live-
  TextEntity zur UserString-Storage (Oberleiste-Edits ueberleben Regen)
- _update_wall stempel-Branch um header + show-Flags erweitert

Frontend (StempelProperties):
- Stil-Picker-Dropdown analog Raum (Stil wählen / + speichern / 🗑 löschen)
- Header-Input (Inline-Text)
- 11 Show-Toggles als kompakte Grid (Check-Box-Stil)
- Live-Vorschau respektiert Visibility-Flags + bilanz.count
2026-05-27 00:36:31 +02:00
karim ac7b2f2ee5 GF/AGF-Outlines: eigene Layer + minimaler Stempel/UI
Geschossflaeche (GF) und Aussengeschossflaeche (AGF) sind reine
Flaechen-Ausweisungs-Outlines — keine Raeume mit Name/Nummer/Funktion.

Backend:
- Neue Layer-Helpers _layer_path_raum_gf (61_GF) und _layer_path_raum_agf
  (62_AGF) — eigene Sublayer pro Geschoss, eigene Default-Farben
- _layer_path_for_raum_sia(doc, gname, sia): routet sia=gf → GF-Layer,
  sia=agf → AGF-Layer, sonst RAEUME-Layer (HNF/NNF/VF/FF/leer)
- _regenerate_element_body raum_outline-Branch nutzt das Routing →
  Source-Outline migriert automatisch auf den richtigen Layer
- _update_wall raum-Branch: bei SIA-Wechsel (z.B. HNF → GF) auch
  Layer-Migration
- _make_raum_stamp_text: bei sia=gf/agf default-layout
  override auf [["sia", "area"]] → Stempel zeigt nur "GF 234.5 m²" /
  "AGF 18.0 m²" ohne Nummer/Name/Funktion

Frontend (RaumProperties):
- Conditional isFlaeche = sia in (gf, agf)
- Versteckt bei isFlaeche: Stil-Picker, Nummer-Input, Name-Input,
  Funktion-Input, StempelLayoutBuilder
- Bleibt sichtbar: Geschoss, SIA-Tag-Selector, Fuellung, Rundung,
  Skala-Modus, Flaeche/Umfang-Footer
- Info-Zeile zeigt bei isFlaeche: "Flaechen-Outline (GF) auf eigenem
  Layer · Stempel zeigt nur GF + Flaeche"
2026-05-27 00:24:57 +02:00
karim 2386366566 Stempel-Element: SIA-Bilanz als platzierbares Viewport-Objekt
Neuer Element-Type "stempel" — TextEntity die automatisch eine SIA-416
Bilanz aggregiert und im Plan platziert wird. Re-rendert sich live wenn
sich Raeume im Scope aendern.

Backend (elemente.py):
- Neue SIA-Tags: GF (Geschossflaeche), AGF (Aussengeschossflaeche)
  mit eigenen Labels + Pastell-Farben in _SIA_COLORS_HEX
- "stempel" als SOURCE_TYPE; eigene UserStrings:
  - stempel_scope: "total" | "geschoss:<gid>"
  - stempel_txt_h, stempel_font, stempel_bold, stempel_italic
- compute_sia_bilanz(doc, scope): aggregiert nach SIA-Tags, liefert
  HNF/NNF/VF/FF/GF/AGF + abgeleitet NF/NGF/count + Scope-Label
- _format_bilanz_lines: kompakte Stempel-Textzeilen ("HNF  120.5 m²"),
  Trennlinien + nur Kategorien > 0
- _make_stempel_text: TextEntity-Builder mit Header "Nutzflächen · {Scope}"
- _regenerate_element_body "stempel"-Branch: in-place Replace mit
  aktualisiertem Text (Position bleibt aus alter Geometrie)
- _regenerate_stempel_for_geschoss: regennt alle Stempel im selben
  Geschoss + alle "total"-Stempel
- Auto-Cascade: raum_outline-Regen setzt sticky-marker; nach REGEN_BUSY-
  Release wird _regenerate_stempel_for_geschoss aufgerufen
- _cmd_create_stempel: GetPoint im Viewport, layer = aktives Geschoss
  Raum-Sublayer, default-Scope = aktives Geschoss
- _update_wall "stempel"-Branch: scope/txtH/font/bold/italic via patch
- elemente_uebersicht: SIA-Bilanz um gf/agf/ngf erweitert; "stempel"
  als KIND-Eintrag

Frontend:
- createStempel-Bridge-Export
- "Stempel"-Pill-Button in der "Raeume"-PillGroup
- StempelProperties-Component: Scope-Dropdown (Total + alle Geschosse),
  Bilanz-Vorschau mit Hervorhebung der NF (Accent-Farbe)
- KIND_META + RAUM_SIA_KINDS um GF/AGF erweitert

Workflow: Pill "Stempel" klicken → Punkt im Viewport → Stempel erscheint
mit Header "Nutzflächen · EG" + Bilanz. Properties: Scope auf Total
umstellen oder anderes Geschoss waehlen. Neue Raeume taggen mit SIA-
Tag → Stempel aktualisiert sich automatisch.
2026-05-27 00:10:02 +02:00
karim 7fbda8c289 Stempel-Stil-Bulk-Apply + Manager-Tab in ProjectSettings
PHASE 1 — Bulk-Apply auf mehrere selektierte Raeume:
- Backend _cmd_apply_raum_stil: leere ids im Patch → nimmt automatisch
  alle aktuell SELEKTIERTEN raum_outlines aus dem Doc (Bulk-Fallback)
- Frontend sendet leere ids fuer die Default-Bulk-Variante. User
  selektiert N Raeume im Viewport → klickt Stil im Properties-Picker →
  Stil wird auf alle angewendet. Inkludiert immer den Properties-Raum.

PHASE 2 — Stil-Manager als Settings-Tab:
- Neuer Tab "Raumstile" in ProjectSettings-Dialog
- Liste aller Stile mit Drag-Reorder (HTML5 native), Inline-Rename,
  Duplicate (content_copy-Icon), Delete (mit Confirm)
- Mini-Preview: zeigt Layout-Struktur (z.B. "nummer·name / funktion / area")
  und der Name wird mit Font/Bold/Italic des Stils gerendert als Vorschau
- Backend _cmd_reorder_raum_stile + _cmd_duplicate_raum_stil in elemente.py
- ProjectSettings-Bridge dispatcht direkt zu elemente.load/save_raum_stempel_stile
  (kein Roundtrip via Elemente-Bridge noetig)
- STILE_UPDATED-Message nach jeder Op pusht die neue Liste zum Dialog
- params bei _open_project_settings enthalten jetzt raumStempelStile + fonts

Bridge-Exports: reorderRaumStile / duplicateRaumStil in rhinoBridge.js
2026-05-26 23:47:38 +02:00
karim f208e7fc00 Raumstempel-Panel: Hoehe + Ausrichtung weg, kommt aus Oberleiste
UX-Konsolidierung: Texthoehe und Ausrichtung wurden bisher doppelt
gesetzt (Panel + Oberleiste-Text-Block). Jetzt nur noch via Oberleiste
wenn der Stempel selektiert ist — Panel zeigt nur noch den Skala-Modus
(fix/masstab) als kompakten Dropdown.

Backend: _sync_raum_stamps_to_source spiegelt jetzt auch
TextHorizontalAlignment vom Stempel zur Source (Left/Center/Right →
links/mid/rechts). Damit kommt die User-Wahl aus der Oberleiste sauber
in die UserStrings — und wird beim naechsten Regen + bei Stil-Speichern
korrekt uebernommen.

Frontend RaumProperties:
- Ausrichtung-Zeile (3 BarToggles) raus
- Hoehe-Zeile reduziert auf Skala-Modus-Dropdown:
  "fix · Hoehe = Oberleiste-Wert (m)" | "masstab · skaliert mit Plan-Massstab"
- Local-State txtH + setTxtH entfernt (unused)
- Stempel-Stil-Speichern liest weiter raum.txtH + raum.align (kommen
  jetzt vom Sync) → captured korrekt
2026-05-26 23:33:38 +02:00
karim eff0878f53 Raumstempel-Stile: Active-Stil-Tracking
UserString dossier_raum_stil_id auf der raum_outline. Wird gesetzt:
- bei APPLY_RAUM_STIL → schreibt sid auf alle Ziel-Raeume
- bei SAVE_RAUM_STIL mit applyToIds=[…] → schreibt neue sid auf
  uebergebene Raeume (Frontend nutzt das beim "+ Speichern…"-Flow um
  den aktuellen Raum mit dem neuen Stil zu verknuepfen)

_read_meta liest stil_id mit → State-Emit feldet "stilId" zum Raum.
Frontend's Stil-Dropdown markiert dadurch automatisch den aktiven Stil
(via activeStilId = raum.stilId).

Manuelle Edits (UPDATE_ELEMENT) touchen stil_id NICHT — User kann
seinen Stil leicht tweaken ohne dass die Verknuepfung verloren geht.
Wenn der Tweak weit weg ist, kann er den Stil aktualisieren via
"+ Aktuelle Settings als Stil speichern" (uebernimmt dann mit der
NEUEN stil_id).
2026-05-26 23:23:42 +02:00
karim 3c28d2e29c Elemente-Uebersicht: SIA-416 Bilanz pro Geschoss
Aggregiert alle raum_outline-Flaechen nach raum_sia-Klassifikation
(hnf/nnf/vf/ff) und zeigt sie als kompakte Mini-Tabelle direkt unter
dem Geschoss-Header in der Project-Browser-Uebersicht.

Backend (_build_overview):
- Neuer Returnschluessel siaBilanz: {geschossId: {hnf, nnf, vf, ff,
  ohne, nf, total, count}} in m^2
- NF = HNF + NNF (Nutzflaeche nach SIA 416)
- Raeume ohne SIA-Tag landen in "ohne"

Frontend (ElementeUebersichtApp):
- Direkt unter dem Geschoss-Header eine Inline-Tabelle mit nur den
  Klassen die > 0 sind (kein Spam wenn nichts klassifiziert ist)
- NF separat hervorgehoben (Accent-Farbe) als wichtigste Kennzahl
- Read-only, aktualisiert sich mit jedem state-emit (Raum-Aenderung,
  SIA-Tag setzen, neue Raeume) automatisch
2026-05-26 23:20:00 +02:00
karim f1860ae85d Raumstempel-Stile (Presets) — speichern + anwenden per Doc
Damit User wiederkehrende Stempel-Configs (Wettbewerb / Bauantrag /
Mobiliar etc.) nicht jedes Mal neu klicken muss.

Backend (elemente.py):
- Storage in doc.Strings dossier_raum_stempel_stile als JSON-Array
- load_raum_stempel_stile / save_raum_stempel_stile Helpers
- Bridge-Handler:
  - SAVE_RAUM_STIL: upsert by id (neu wenn leer)
  - DELETE_RAUM_STIL: remove by id
  - APPLY_RAUM_STIL: schreibt Stil-Felder auf Raum-IDs + Regen
- _RAUM_STIL_FIELDS umfasst font/bold/italic/txtH/txtModus/align/
  rundung/fuellung/showSia/layout (alles was die Optik bestimmt)
- raumStempelStile im STATE-Emit zum Frontend

Frontend:
- saveRaumStil/deleteRaumStil/applyRaumStil in rhinoBridge.js
- RaumProperties: neue "Stil"-Sektion oben mit Dropdown
  * gespeicherte Stile + "+ Aktuelle Settings als Stil speichern" +
    "🗑 Aktiven Stil loeschen"
  * Klick auf Stil → applyRaumStil mit aktueller Raum-ID
- Beide PropertiesView-Aufrufe (inline + satellite) bekommen die Liste
2026-05-26 23:17:08 +02:00
karim da0fd365f2 Arbeitseinheit als Project-Setting + Doc-Open-Check
Statt jeden Wert im Code zu konvertieren wird sichergestellt dass das
Doc in der gewuenschten Unit ist:

- defaults.unit ('meters'|'millimeters'|'centimeters') in
  dossier_project_settings, Default 'meters'
- ProjectSettings-Dialog "Voreinstellungen" Tab: neue Sektion
  "Arbeitseinheit" mit Toggle-Group fuer m/cm/mm
- get_project_unit() + get_project_unit_enum() Helper in rhinopanel
- startup._check_doc_unit() prueft beim Doc-Open ob ModelUnitSystem
  matched — bei Mismatch Eto-MessageBox "Doc auf X umstellen?"
- "Yes" ruft _-Units _Model _<Unit> _Yes (Geometrie wird mit-skaliert)
- "No" setzt doc.Strings-Flag dossier_unit_checked → keine erneute Frage
- Check laeuft beim _on_doc_opened-Hook + initial fuer aktives Doc

Vorgehen ist deutlich sauberer als der vor-revert unit-aware Code
(135 Zeilen Konvertierungslogik vs 80 Zeilen Check+Convert).
2026-05-26 23:11:36 +02:00
karim cd626b0707 Revert "Unit-aware: m → Doc-Units im Regen-Pfad"
This reverts commit dd5ccec881.
2026-05-26 22:06:27 +02:00
karim dd5ccec881 Unit-aware: m → Doc-Units im Regen-Pfad
DOSSIER-UI nimmt immer Meter entgegen — bisher wurden die Werte 1:1 als
Doc-Units verwendet, was bei Doc=Millimeter winzige Geometrie ergab
(0.25m getippt → 0.25mm Wand).

Storage-Konvention bleibt METER (UI-friendly). Konvertierung passiert
beim Geometrie-Bau:

- Neue Helpers _m2u / _u2m via Rhino.RhinoMath.UnitScale
- _regenerate_element_body normalisiert ALLE m-typischen Meta-Felder am
  Eingang via _m2u — Geometrie-Code darunter bleibt unveraendert und
  arbeitet in Doc-Units (funktioniert in jedem Unit-System)
- Lokaler _gs() Wrapper konvertiert Geschoss-OKFF + Hoehe → Doc-Units
- _resolve_uk_ok / _resolve_decke_z / _resolve_dach_base konvertieren
  Geschoss-Heights aus JSON (m) → Doc-Units
- _resolve_raum_text_height_m masstab-Pfad: m-Result → Doc-Units
- _sync_raum_stamps_to_source: Stempel-TextHeight + Position-Delta sind
  in Doc-Units, werden vor Storage via _u2m → m konvertiert

Effekt:
- Doc in Metern: kein sichtbarer Unterschied (UnitScale=1, no-op)
- Doc in Millimeter: 0.25 (m) wird zu 250 (mm) Wand → richtig dick
- State-Emit zum Frontend bleibt in m → UI konsistent
2026-05-26 21:59:33 +02:00
karim 95678d4394 Massstab: Diagnose-Prints raus (Magnify funktioniert wie erwartet) 2026-05-26 21:38:16 +02:00
karim 067cb56584 Massstab: Diagnose-Prints fuer _apply_scale (pre/post Magnify) 2026-05-26 21:24:21 +02:00
karim b10760a704 Masstab: Persist vor Raum-Regen — sonst regent mit alter Skala
regen_masstab_raeume liest get_applied_scale_ratio() um die paper-mm-
Hoehe in Modell-m umzurechnen. Lief bisher VOR _set_applied_scale_for_vp
→ las immer noch den alten Wert. Resultat: nach Skala-Wechsel war die
Stempel-Hoehe um die alte Skala berechnet, naechster Wechsel um die
vorletzte usw.

Reihenfolge korrigiert: erst write_user_scale + set_applied_scale_for_vp,
DANN regen_masstab_raeume.
2026-05-26 21:10:28 +02:00
karim e56ee2cb8f Raumstempel: Auto-Konvertierung beim Modus-Wechsel + Font-API-Fix
Bug: Beim Toggle fix↔masstab blieb txt_h derselbe Zahlenwert mit anderer
Einheit. 0.20m wurde zu 0.20mm@1:100 = 0.02m Text (2cm). Stempel
unsichtbar klein.

Fix: Bridge konvertiert txt_h automatisch wenn der Modus wechselt UND
kein expliziter txtH-Wert im Patch ist:
  fix → masstab: paper_mm = m * 1000 / scale
  masstab → fix: m       = paper_mm * scale / 1000
Frontend sendet beim Modus-Toggle nur { txtModus } ohne txtH → Backend-
Konvertierung greift.

Plus: _list_system_fonts probiert jetzt InstalledFonts() + AvailableFontFaces
(InstalledFontsAsString existiert auf Mac Rhino 8 nicht); Ergebnis wird
session-weit gecacht damit der Error nicht jeden state-emit spamt.
2026-05-26 20:58:28 +02:00
karim 238d7d062b Raumstempel: masstab-Modus — Texthoehe als Paper-mm @ Plan-Massstab
Neuer UserString dossier_raum_txt_modus = "fix" | "masstab" (default fix).
- fix:     raum_txt_h ist Meter (Modellhoehe, bisheriges Verhalten)
- masstab: raum_txt_h ist Paper-mm. Render-Hoehe (m) =
           paper_mm * applied_scale / 1000 — wird zur Render-Zeit aus
           massstab.get_applied_scale_ratio() gelesen, Fallback 1:100.

Massstab-Sync:
- massstab._apply_scale ruft nach Skala-Wechsel elemente.regen_masstab_raeume(doc)
  → alle Raeume im masstab-Modus regennen automatisch, Texthoehe folgt der
  neuen Skala (z.B. Switch 1:100 → 1:50 halbiert die Modellhoehe).

_sync_raum_stamps_to_source masstab-aware: im masstab-Modus wird die
TextHeight am Stempel NICHT zurueck auf raum_txt_h gespiegelt (sie ist
abgeleitet, nicht die Wahrheit) — sonst waere der naechste Regen sofort
falsch positioniert. Offset + Font/Style werden weiterhin gespiegelt.

UI: Modus-Toggle "fix m" / "masstab mm" + Hoehen-Input + Einheits-Suffix
in RaumProperties zwischen Ausrichtung und Funktion.
2026-05-26 20:49:26 +02:00
karim 01b6501a0c Raumstempel: Drag-Drop-Layout + persistente Position + Oberleiste-Sync
Datenmodell auf der raum_outline:
- dossier_raum_stamp_dx/dy: Stempel-Offset zum Outline-Centroid
- dossier_raum_layout: JSON-Array of-Rows fuer Multi-Field-pro-Zeile
- dossier_raum_txt_font/bold/italic + raum_show_*: Typografie-Overrides
- Legacy show_*-Flags bleiben Fallback wenn kein Layout gesetzt

Backend:
- _make_raum_stamp_text: Layout-Renderer (Rows zu Lines), Offset wird in
  _regenerate_element_body auf den Centroid addiert
- _sync_raum_stamps_to_source: laeuft am Anfang von _on_command_end,
  spiegelt aktuelle Stempel-Position + Font/Size/Style auf die Source
  zurueck → User-Edits via Move/Oberleiste/Properties ueberleben Regen
- _list_system_fonts: System-Fonts fuer Frontend-Dropdown
- Raumstempel-Bug-Fix: raum_outline jetzt in _on_command_end Regen-Pfad
  per Length-Check getriggert, snapshot um length erweitert. Vertex-Drag
  aktualisiert Flaechen-Wert. Outline-Sources fuegen affected_walls hinzu.
- raum_stamp aus _PAIRED_VOLUME_TYPES entfernt → einzeln greifbar/
  verschiebbar; Klick auf Outline pairt weiter alles drei.

Frontend (ElementeApp.jsx):
- StempelLayoutBuilder: HTML5 drag-and-drop UI, verfuegbare Felder als
  Pills oben (Klick = neue Row, Drag = in bestehende Row), bestehende
  Rows als drop-targets, × zum Entfernen
- Typografie-Block raus aus RaumProperties; Hinweis-Text auf Oberleiste
- PropertiesView nimmt jetzt fonts={state.fonts} (auch Satellite-Window)
2026-05-26 20:42:32 +02:00
karim 02a00a9b4a Display-Modes: 3D-Template + Auto-Assign + Material/Raytracing-Slots
- Neues Template rhino/templates/dossier_3d.ini fuer perspektivische Views
- Registry-Loop in oberleiste.py generalisiert (Plan + 3D + Material +
  Raytracing) — Material/Raytracing skippen wenn kein Template vorhanden,
  um Cycles-Pipeline-Clone-Crash zu vermeiden
- Guid-Replace praezisiert: nur Section-Header-Guid, nie PipelineId
- Plan-spezifische ini-Patches auf target_name=="Dossier Plan" gegated
- Auto-Assign in startup.py: Parallel-Viewports -> Plan, Perspective -> 3D,
  einmal-pro-Doc via doc.Strings-Flag (User-Overrides bleiben)
- schnitte.activate_schnitt setzt Dossier Plan explizit (Hatches auch in
  Schnittperspektive sichtbar)
- GestaltungApp Section-Block neu strukturiert: PenBlock fuer 3D als 'Fill'
  innerhalb der Section, Solid-Fill-Toggle entfernt (war Duplikat)
2026-05-26 18:26:49 +02:00
karim 13a5e1eb7a AGPL-3.0 Dual-Lizenz + Pill-Stil-UI + Section-Style-Overhaul + Plan-Mode-Template
Lizenz:
- AGPL-3.0 LICENSE-File im Repo-Root (GNU Volltext)
- SPDX-Header + Copyright in allen Source-Files (Python/JSX/JS/Rust)
- license-Feld in package.json + Cargo.toml
- About-App komplett neu: Dual-Lizenz-Block (AGPL + Commercial),
  openbureau-Branding, Version-Pills, made-in-Switzerland-Footer

UI-Restyle (3 Wellen) — alle Dialoge + Satellites + Panel-Sidebars
auf gemeinsamen Pill-Stil aus BarControls (BarToggle/BarButton/BarCombo):
- Welle 1: GeschossDialog/Settings, AusschnittSettings, LayoutDialog
- Welle 2: ConfirmDeleteEbene, Kamera, MasseSettings, Osm, Swisstopo,
  TextEditor, AusschnittLayerDialog, LayerCombinations
- Welle 3: LayoutsApp, MassstabApp, WerkzeugeApp, OverridesApp,
  ZeichnungsebenenApp; Werkzeuge mit ElementeApp-PillGroup-Layout

GeschossDialog Header-Refactor: +Geschoss/+Zeichnung in Toolbar oben,
move-Pfeile-Spalte breiter (kein Overlap mit G-Haken)

Ausschnitte Rows als Pills, kein Outer-Border ums Suchfeld

Section-Style komplett neu (gestaltung.py + GestaltungApp.jsx):
- ObjectSectionAttributesSource.FromObject (richtiger Enum-Name fuer Mac)
- HatchPatternPrintColor + BoundaryPrintColor mit-setzen (Display = Print)
- BoundaryColor nur bei explizitem User-Override, sonst Rhino-Default
- background_color_hex Parameter (BackgroundFillMode=SolidColor)
- Readback aus GetCustomSectionStyle statt direkt aus Attributes
- UI: Schnittkante > Section Style > Solid-Fill mit proper SectionHead
- 'Boundary' (3D Pen) -> 'Background' weil sich's wie Section-Hintergrund verhaelt

Plan-Mode 'Dossier Plan' via Template:
- rhino/templates/dossier_plan.ini wird direkt geladen
- Fallback auf Technical-Clone + ini-Patch wenn Template fehlt
- Auto-Cleanup von Orphan-Modes vor Import (Name- oder Guid-Match)
- ClipSectionUsage=1 + TechnicalMask=15 als bekannte Soll-Werte
- Bei Template-Pfad keine ini-Patches (1:1 wie User exportiert)
- Sanity-Print listet alle registrierten Modes nach Anlegen

Bridge-Unification: 4 Settings-Apps (Ebenen/Project/Geschoss*Dialog)
benutzen jetzt chunkende send() statt eigene bridgeSend ohne Chunk-
Logik -> grosse Payloads (Hatch-Refs etc.) kommen nicht mehr truncated
bei Python an (loeste 'JSON-Fehler char 990'-Regression in Ebenen-
Settings)

Library-Imports robust: 'import library' jetzt Top-Level in elemente.py
+ rhinopanel.py (statt Lazy in Methoden) -> 'No module named library'-
Crashes weg auch wenn sys.path zwischendurch resettet wird

Tools fuer Display-Mode-Maintenance:
- _clean_display_modes.py (loescht alle Custom-Modes, Built-ins bleiben)
- _inspect_plan_mode.py / _inspect_obj_section.py / _inspect_obj_boundary.py
  (Diagnose-Skripte fuer SectionStyle-Property-Reverse-Engineering)
- _reset_rhino_settings.sh (Backup + Nuke der Rhino-Settings als
  letzte Bastion gegen korrupte Display-Modes)
2026-05-26 17:09:18 +02:00
karim e1b63aa4e6 Library-Thumbnails: Auto-Capture + Base64-Preview in der UI
Beim Hinzufuegen oder Importieren eines Library-Items wird automatisch
ein PNG-Thumbnail vom Item generiert (Top-View, 128x128) und in
library/previews/<id>.png abgelegt. Frontend rendert die Previews als
Base64-Data-URIs (sicher gegen WebKit-file://-Restriktionen).

library.py:
- _previews_dir(): legt previews/-Folder an
- _preview_rel_for(asset_rel): predictable PNG-Pfad pro Item
- _capture_thumbnail_of_objects(): hided andere Objekte temporaer,
  switcht auf Top-Parallel, ZoomBoundingBox, CaptureToBitmap → PNG,
  restored Viewport + Hidden-State
- read_preview_data_uri(): liest PNG + encoded als data:image/png;base64
- Hook in convert_to_3dm_via_import (vor Cleanup) + save_selection_to_asset

rhinopanel.py:
- _enrich_library_items_with_previews(): haengt previewDataUri an
  jedes Item das ein preview-Feld hat
- Initial-Params + _send_library + ElementeBridge._cmd_list_library
  liefern angereicherte Items
- _add_library_file + _save_selection_as_library setzen preview-Pfad
  im Item wenn Thumbnail-Datei existiert

Frontend:
- SymbolPicker.ItemPreview: rendert <div backgroundImage> mit Base64-URI
  wenn vorhanden, sonst Icon-Fallback
- ProjectSettingsDialog Symbole-Tab: List-Row + Detail-Identity zeigen
  Thumbnail (32px in Liste, 56px im Detail), Icon nur als Fallback

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 19:07:33 +02:00
karim 827bd8d4d7 Library: Format-Konvertierung beim Import (.dwg/.obj/.fbx/.dae/.stl/...)
User kann jetzt im Symbole-Tab Dateien in vielen Formaten waehlen — Rhino
konvertiert automatisch nach .3dm via _-Import + File3dm.Write.

library.py — convert_to_3dm_via_import():
- Snapshot der existierenden Object-IDs im aktiven Doc
- User-Selection sichern
- _-Import scripted ausfuehren → neue Objekte in Doc
- Diff -> neue Objekte sammeln
- BoundingBox.Min als Origin → in File3dm packen
- f3.Write nach library/assets/<name>.3dm
- Neue Objekte aus Doc wieder loeschen + User-Selection restoren
- sticky 'dossier_library_import_busy' + 'dossier_swisstopo_busy' damit
  unsere Listener nichts cascaden waehrend Doc kurzzeitig die Importe haelt

rhinopanel.py — _add_library_file():
- .3dm: copy_to_assets (wie bisher)
- .dwg/.dxf/.obj/.fbx/.dae/.stl/.3ds/.skp/.iges/.step/.ply: konvertieren
- Sonst LIBRARY_ERROR

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 18:45:58 +02:00
karim 68b9d14453 Auswahl als Library-Item speichern (Step 3)
User selektiert in Rhino → klickt im Symbole-Tab 'Aus Auswahl' →
Selection wird in eine .3dm-Datei verpackt + Library-Item entsteht
(oder bestehendes wird aktualisiert).

Backend (library.py):
- save_selection_to_asset(doc, target_name): erstellt File3dm aus der
  aktuellen Selection, packt Geometry relativ zu BoundingBox.Min (Block-
  Origin am Ursprung), schreibt nach library/assets/

Backend (ProjectSettingsBridge):
- SAVE_SELECTION_AS_LIBRARY-Handler: holt Selection, fragt bei neuen
  Items via Rhino-GetString nach Name, schreibt .3dm, fuegt Item zum
  Manifest oder updated bestehendes (variant '2d'/'3d')

Frontend (Symbole-Tab):
- List-Footer: 'Aus Datei' + 'Aus Auswahl' Pills (neues Item)
- Pro 2D/3D-Slot im Detail: 'Datei wählen' + 'Aus Auswahl' Pills
  (Variante eines bestehenden Items befüllen)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 04:05:48 +02:00
karim c993935b17 Symbole-Tab in Project-Settings (Library-Item-Management)
Project-Settings hat jetzt 5 Tabs. Neuer 'Symbole'-Tab managt die
Dossier-Library: List/Detail wie Materialien, mit 2D + 3D Slot pro Item.

Backend (library.py):
- save_manifest, update_item, delete_item, add_item — full CRUD aufs
  library.json
- copy_to_assets: kopiert User-Dateien in library/assets/ mit
  Konflikt-Resolution (auto-suffix)

Backend (rhinopanel.py / ProjectSettingsBridge):
- _send_library: aktuelle Items + libraryRoot an Frontend
- _add_library_file: File-Picker (.3dm direkt; .dwg/.obj/etc. zeigt
  Hinweis fuer kuenftige Konvertierung), kopiert + appended ans Item
  (variant 2d/3d) oder erstellt neues Item
- _update_library_item: patch by id
- _delete_library_item: entfernt Eintrag aus Manifest
- LIBRARY_ITEMS + LIBRARY_ERROR Messages ans Frontend

Frontend:
- Neuer 'Symbole'-Tab mit List/Detail
- Liste: Name, Type-Icon, '2D'/'3D' Status-Badge
- Detail rechts: Name-Edit (live persist on blur), Type-Toggle
  (Symbol/Objekt), 2D/3D-File-Slots mit Datei-Picker, Tags-Editor
- 'Neues Objekt' Button im Listen-Footer

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:58:41 +02:00
karim de57c320c2 Symbol-Picker als Satellite-Fenster (statt Modal im Elemente-Panel)
UX-Verbesserung: Modal-Overlay im engen Elemente-Panel war unpraktisch.
Symbol-Picker oeffnet sich jetzt als eigenstaendiges Eto.Form-Fenster
(wie Library/Project-Settings).

Frontend:
- SymbolPicker bekommt embedded-Prop (Satellite-Mount vs Modal-Overlay)
- Neuer SymbolPickerApp Satellite-Wrapper (PANEL_PARAMS lesen + Bridge)
- main.jsx: 'symbol_picker' Mode-Routing
- ElementeApp: Symbol-Button ruft nur noch listLibrary() — Backend
  oeffnet das Fenster

Backend:
- _cmd_list_library oeffnet jetzt das Satellite-Window mit eigener
  Bridge (PICK -> CREATE_SYMBOL, CANCEL -> Close)
- PICK schliesst Fenster + triggert interactive GetPoint im Viewport

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:39:03 +02:00
karim 8184f559fc Symbol-Funktion in Elemente-Panel (Phase S1+S2)
Schema (library.py):
- Item-Format erweitert: files2d + files3d (Backwards-compat zu 'files')
- _build_variant_block + _place_instance + Layer-Routing pro Variante
- import_item akzeptiert at_point + layer2d/layer3d
- _ensure_block_definition mit variant-Suffix (dossier_lib_<id>_2d/_3d)

Backend (elemente.py):
- _layer_path_symbole(geschoss_name, variant) → <geschoss>::40_SYMBOLE::
  SYMBOLE_2D bzw. SYMBOLE_3D
- Default-Ebene 40 SYMBOLE via _find_ebene_sublayer_name
- LIST_LIBRARY-Handler: sendet Library-Manifest als LIBRARY_LIST
- CREATE_SYMBOL-Handler: interactive GetPoint im aktiven Viewport,
  laedt Block-Def + platziert Instanz(en) auf den richtigen Ebenen
- Pair-Items (2D+3D) werden an gleichem Punkt beidseitig platziert →
  Top zeigt 2D-Layer, Persp zeigt 3D-Layer wenn User entsprechend
  Sichtbarkeit setzt

Frontend:
- SymbolPicker Modal-Component: Grid mit Symbol/Object-Cards, Search,
  Type-Filter (Alle/Symbole/Objekte), Doppelklick = Pick
- Symbol-Button in ElementeApp (PillGroup "Library") oeffnet Modal +
  triggert listLibrary() fuer aktuelle Items
- createSymbol(id) → Backend → GetPoint → Place

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:31:54 +02:00
karim 8f691e37c4 Projektdaten in Project-Settings + Swisstopo-Adress-Prefill
Schema-Erweiterung:
- _PROJECT_SETTINGS_DEFAULTS hat jetzt 'project'-Block mit
  name / number / address / bauherr / architekt / notes / projectZeroMum
- _normalize_project_meta stripped Strings + clampt mum als float
- load/save_project_settings handeln das 'project'-feld
- save_project_settings spiegelt projectZeroMum auch in den Legacy-Key
  dossier_project_zero_mum (fuer Geschoss-Settings-Dialog)
- load_project_settings liest Legacy-Key als Fallback wenn neuer Wert
  noch nicht gesetzt

UI:
- InlineTextField + TextareaField Helpers (Pill-Stil)
- Projektdaten-Section in Voreinstellungen-Tab:
  Name, Projekt-Nr., Adresse, Bauherrschaft, Architekt:in,
  EG-Nullpunkt m.ü.M (mit Hinweis auf Swisstopo-Nutzung), Notizen

Swisstopo:
- _cmd_open_swisstopo_dialog laedt Projekt-Adresse + sendet projectAddress
  im SWISSTOPO_STATE
- SwisstopoApp: vorbelegt searchText mit projectAddress wenn Feld leer
  ist (User-Input wird nicht ueberschrieben)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:21:19 +02:00
karim a597b58c93 Linientypen + Schraffuren-Tabs in Project-Settings + Datei-Import
Project-Settings hat jetzt 4 Tabs:
- Voreinstellungen (kompakte InlineNumberField, gruppiert in Sections)
- Materialien (List/Detail, ohne Hatch)
- Linientypen (List/Detail mit SVG-Strich-Vorschau)
- Schraffuren (List/Detail mit echtem HatchLine-Renderer)

Backend (rhinopanel.py):
- _list_linetypes_full liefert Segmente {length, type: Line/Space/Dot}
  (Mac Rhino 8 GetSegment returnt (length, isLine: bool))
- _list_hatch_patterns_full liefert HatchLines mit angle/base/offset/dashes
  (hl.Dashes optional ueber 3 API-Variants)
- CRUD: RENAME / DELETE / LOAD_DEFAULTS
- File-Import: IMPORT_LINETYPE_FILE (.lin), IMPORT_HATCH_FILE (.pat)
  via Eto.OpenFileDialog → Linetypes.Load / HatchPatterns.LoadFromFile

Frontend (ProjectSettingsDialog.jsx):
- LinetypePreview: SVG mit tile-fenster (4 Repetitions), Line als <line>,
  Dot als <circle>, currentColor fuer Renderer-Robustheit
- HatchPreview: rendert pro HatchLine alle parallelen Linien mit Angle,
  Offset (Spacing + Stagger), Dashes als stroke-dasharray
- TABLES_UPDATED Message vom Backend re-rendert Listen
- Import-Pills im List-Footer

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:14:28 +02:00
karim 8d3b3af882 Material/Ebene-Separation: Hatch raus aus Material (Refactor a)
Material ist jetzt rein 3D — Section-Hatch (2D-Schnitt) wird nicht mehr
am Material definiert, sondern am Layer (via Rhino-Layer-Dialog oder
spaeter via Ebenen-Settings + neuer Hatch-Tab im Project-Settings).

Schema-Aenderungen:
- _normalize_material: hatch + scale entfernt
- _MATERIAL_LIBRARY (elemente.py): hatch + scale aus allen Builtin-Mats
- _get_all_materials: ohne hatch
- _send_state materials payload: nur {name, color}
- Library import_material: PBR + Texturen werden weitergegeben

Backend:
- _ensure_material_sublayer: erstellt Sublayer mit Color, RESETTET aber
  alten SectionHatchIndex auf -1 (= "kein eigener Hatch") damit
  Inheritance/User-Override greift. Vorher wurden alte Material-Hatch-
  Werte da haengen geblieben.

Frontend:
- MaterialDetail: Schraffur-Section entfernt
- hatchPatterns-Prop entfernt

Konsequenz: existierende Waende verlieren ihren Section-Hatch beim
naechsten Regen. User definiert Section-Hatches jetzt auf Layer-Ebene.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 02:10:04 +02:00
karim ad56d9e930 Fix Texture-Picker: Custom-Bridge fuer Project-Settings Satellite
open_satellite_window ohne bridge=-Param verwendet eine Inline-Bridge die
nur READY/SAVE/CANCEL kennt — PICK_TEXTURE_FILE-Messages wurden stumm
verworfen.

Fix: _ProjectSettingsBridge als BaseBridge-Subclass mit eigener
PICK_TEXTURE_FILE-Handler-Logik. Sendet TEXTURE_PICKED direkt zurueck an
die selbe WebView.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 18:14:13 +02:00
karim c63bdd5bc1 Material Phase B Stufe 4: PBR auf Rhino-Material applied + Auto-Regen
_ensure_pbr_material baut ein vollständiges Rhino-Material aus dem
Project-Settings-dict:
- ToPhysicallyBased() + BaseColor/Roughness/Metallic/Opacity/OpacityIor
- Diffuse-/Bump-/Transparency-Texture via SetBitmapTexture etc.
- UV-Repeat = 1/uvScaleM
- Cache per Signature (Color+PBR+Texture-Pfade)

_get_all_materials liefert jetzt full-dicts (nicht mehr nur color/hatch/
scale) damit Wand-Regen Zugriff auf PBR + Texturen hat.

Wand-Regen: wenn voll-dict aus Project-Settings vorliegt → PBR-Material,
sonst Fallback auf legacy _ensure_material(hex).

Auto-Regen on Save:
- PBR-Material-Cache + Legacy-Material-Cache invalidieren
- Alle wand_axis im Doc regenerieren (in EINEM Undo-Record)
- User aendert Material-Properties -> existierende Waende updaten sofort

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 17:33:38 +02:00
karim 0bf891641f Project-Settings Material-List/Detail + PBR-UI (Phase B Stufe 2+3)
UI:
- Voreinstellungen: InlineNumberField (Label links, schmaler Number-Input
  rechts mit Einheit-Suffix) statt full-width pills
- Materialien-Tab: List/Detail-Layout im ArchiCAD-Stil
  - Links (240px): suchbare Material-Liste mit Color-Swatch + Source-Badge
  - Rechts: Detail-Panel mit collapsible Sections
- Slider-Component (Pill-Track mit accent-fill + Wert rechts)
- TextureSlot-Component (Preview-Tile + Filename + Picker- + Clear-Button)

Schema:
- Material erweitert: roughness, reflection, transparency, iorN, uvScaleM
- Texturen: diffuse / bump / roughness / transparency (je {path, ...})
- _normalize_material clamped 0..1, IoR 1.0..2.5

Backend:
- Neuer Handler PICK_TEXTURE_FILE oeffnet Eto.OpenFileDialog fuer
  .jpg/.png/.tif/.bmp/.tga, antwortet mit TEXTURE_PICKED {slot, path}

Hinweis: PBR-Werte + Texturen landen aktuell nur in doc.Strings —
Anwendung auf Rhinos Render-Material kommt in Stufe 4.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 17:11:23 +02:00
karim f760d1c54b Library Phase A.2 (Symbol/Object-Import) + Oberleiste-Pill-Restyle
Library Phase A.2:
- import_symbol/import_object via File3dm.Read + InstanceDefinitions.Add
- Stabile Block-Namen 'dossier_lib_<libraryId>' fuer Dedupe
- Seed-Manifest erweitert um Nordpfeil (symbol) + Laubbaum (object)
- ItemCard rendert type-spezifische Preview (Color-Swatch fuer material,
  Material-Icon fuer symbol/object)

Oberleiste-Pill-Restyle:
- OberleisteApp: Version unter DOSSIER-Logo, Settings-Icons vertikal
  gestapelt
- ProjectSettingsDialog: Pill-Tabs, BarToggle-Footer, MaterialRow mit
  Hover-Highlight, Header entfernt (Eto.Form hat eigenen)
- LibraryBrowser: BarButton-Reload, Pill-Typ-Filter, MaterialCard mit
  BarToggle-Pill, Header entfernt
- Globaler select-Stil: bg-input statt bg-item (dunkler im Dark-Mode,
  konsistent zu Oberleiste-BarCombo)

Routing:
- OberleisteBridge delegiert OPEN_PROJECT_SETTINGS + OPEN_LIBRARY an
  EbenenBridge (sticky ebenen_bridge_ref) — vorher kamen die Messages an
  der falschen Bridge an und wurden verschluckt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 16:57:42 +02:00
karim f60d643bb7 Wand-Chain Cmd+Z-Fix: ReplaceObjectProxy als Grip-Drag-Cmd registrieren
Mac Rhino feuert beim Endpunkt-Grip-Drag intern den Command
'ReplaceObjectProxy'. Ohne den Eintrag in _USER_TRANSFORM_CMDS oeffnete
_on_command_begin keinen Undo-Record + nahm keinen Snapshot — unser
chain-Pre-Check + Regen liefen dann erst auf Idle in einem eigenen
Undo-Record ('Elemente regenerieren (N)'). Cmd+Z brauchte deshalb zwei
Schritte: erst Volume-Restore, dann Axis-Restore.

Fix:
- ReplaceObjectProxy in _USER_TRANSFORM_CMDS
- _REGEN_BUSY-Guard in _on_command_begin damit unsere eigenen internen
  Replace-Calls (Chain-Volume-Rebuild) keinen unerwuenschten Snapshot
  triggern

Plus: No-Op-Tolerance 1mm (Architektur-Praezision) im Replace-Handler
faengt CommitChanges-Microdrift ab — keine Chain-Break-Cascade beim
Selektieren mehr.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 16:14:57 +02:00
karim c050b9aeb6 Polyline-Wand-Refactor (Chain-Anchor) + Cmd+Z-Fixes
Chain-Modell: kompatible benachbarte Waende (gleiche dicke/referenz/layers/
material) teilen ein gemeinsames wand_volume via Polyline-Extrusion. Anchor
(= alphabetisch kleinste ID) baut + besitzt das Volume; Non-Anchors haben
keins. wand_chain_members UserString trackt Mitglieder.

- _find_wall_chain/_build_chain_polyline mit korrekter Erst-Seg-Orientierung
- Anchor-Branch im Regen + Non-Anchor-covered-Check (Loop-Schutz)
- Pre-Test des Chain-Breps vor Cleanup → Fallback per-wand bei Brep-Build-Fail
  (z.B. Offset self-intersect auf Innen-Ecke)
- Pre-Check in _on_command_end liest existing chain_members statt Live-Detect
  → erkennt teil-bewegte Chains + aborted pure-transform
- affected_chain_members in regular regen-Pfad → Survivor regenes mit
- _update_wall regen't alte Chain-Members nach Property-Change
- _on_object_replaced No-Op-Check fuer wand_axis (Grips-Toggle skippt regen)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 13:11:16 +02:00
karim a308ba62d2 Projekt-Settings-Dialog + Library Phase A + Material-Merger
- Project-Settings-Dialog (Voreinstellungen Geschoss/Schnitt + Material-Editor)
  ueber Zahnrad-Icon in Oberleiste; Defaults werden in schnitte.pick_schnitt
  + GeschossManager als Vorgabe genommen, pro-Element-Werte unangetastet
- Dossier-Library Phase A (lokal, read-only): rhino/library.py + LibraryBrowser
  Satellite; Seed-Manifest unter ~/Library/Application Support/Dossier/library/
- Material-Merger: _get_all_materials(doc) merged builtin _MATERIAL_LIBRARY
  mit Projekt-Settings-Materialien (inkl. Library-Imports); Wand-Erstellung,
  Sub-Layer-Anlage + Elemente-Material-Dropdown ziehen jetzt aus dem Merge

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 02:19:09 +02:00
karim ee01c7ebdc Snap-Bar + Drag-Reorder + Schnittperspektive + Top-View Z-Guard + Multi-Geschoss-Clipping
Snap-Bar (Oberleiste):
- 4x2 Icon-Grid mit architektonischen Osnap-Modi (End/Mid/Int/Perp/Cen/Near)
- Master-O Toggle + Grid-Sichtbarkeit
- Symbol-Wahl angelehnt an Rhinos eigene Snap-Marker
- Ortho + Grid-Snap raus (in Rhino-Footer)
- Backend: _osnap_flag_map + _get/_set_osnap_modes + _set_grid_visible

Drag-to-Reorder (GeschossManager):
- HTML5-Drag auf jeder Zeile
- Drop-Indikator (accent-Border oben/unten je nach Cursor-Position)
- Gedraggte Row faded auf opacity 0.4
- Array-Reorder + onChange triggert recalcOkff -> OKFFs konsistent

Schnittperspektive:
- projection: 'parallel' | 'perspective' im Schnitt-Settings-Dialog
- Augenhoehe (cameraHeight) nur bei Perspektive sichtbar
- activate_schnitt mit ChangeToPerspectiveProjection(50 FOV)
- skip_view=True bei Grip-Drag-Re-Activate damit View nicht ploetzlich
  in Section springt

Top-View Z-Guard:
- _is_active_view_top_like + _suppress_z_drift_if_top_view in
  _on_object_replaced — bei Plan-View wird Z-Drift einer source-curve
  automatisch zurueckgerollt (gegen ungewolltes Snappen auf z!=0 oder
  Gumball-Z)

Multi-Geschoss-Clipping-UX:
- Klick auf cut-Icon einer nicht-aktiven Geschoss-Zeile aktiviert das
  Geschoss mit + toggelt Clipping → Plane erscheint sofort

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 23:44:12 +02:00
karim 736325fba1 Wand-Grips + Schnitt-Grips + Referenz-Sublayer pro Bauteil + Print-Auto-Hide
Custom-Grip-Overlays via DisplayConduit + MouseCallback:
- wand_grips.py: dicke klickbare Marker an wand_axis-Endpunkten, auch
  wenn die Referenz-Layer ausgeblendet ist. GetPoint mit fixem Anker.
- schnitt_grips.py: 3 Marker pro Schnitt (P1, P2, Mid). Mid translatiert
  ganze Linie, P1/P2 verschieben Endpunkt. Hide Clipping-Planes waehrend
  GetPoint damit kein Verbots-Cursor durch Locked-Edges erscheint.
  skip_view=True bei Re-Activate damit Drag nicht in Section springt.

Referenz-Architektur umgebaut:
- wand_axis + oeffnung_point liegen jetzt unter <Geschoss>::20_Waende::
  20r_Referenz statt eigener top-level 19_Referenzlinien-Ebene.
- Migration v4 zieht existierende Sources auf den neuen Pfad.
- Toggle in Oberleiste keyword-driven: findet alle 'Referenz'-Sub-Ebenen
  rekursiv, toggelt alle Praefixe gemeinsam. Bauteil-uebergreifend.

Oberleiste-Layout:
- Druck-Ansicht-Button hoch neben Massstab-Dropdown (Reihe 1).
- Referenzlinien-Toggle in Reihe 2 neben Zoom-Pill, symmetrisch zum
  Druck-Button. Zoom-Pill auf 3 Buttons reduziert.
- Print-View AN → Referenz-Layer automatisch ausblenden, Snapshot
  restored beim Ausschalten.

Fix: clear_schnitt_clipping respektiert Mode=Locked nicht — vor Delete
auf Normal-Mode wechseln + Modify damit's persistiert. Schnitt-Loeschen
raeumt Clipping-Planes jetzt sauber auf.

Fix: Schnitt-Doppelklick-Handler aktiviert nur bei expliziter Schnitt-
Auswahl, ignoriert andere Selektionen.

Fix: _send_state Selection-Detection mit Source-ODER-Volume-Fallback —
Fenster-Properties erscheinen jetzt auch wenn oeffnung_point auf hidden
Referenz-Layer liegt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 20:58:06 +02:00
karim 059cbf8d4d Schnitt/Ansicht-Feature + Terrain-Volumen + Geschoss-Add-Dialog
Schnitt-Feature V1+V2:
- Neues rhino/schnitte.py mit Pick-Workflow, Activation (Clipping-Planes +
  Parallel-View), 2D-Plan-Symbol auf 18_Schnittlinien-Sublayer
- Doppelklick auf Symbol aktiviert den Schnitt
- Schnitt-Settings (cutAtLine/Tiefe/Höhen/Blickrichtung) im GeschossSettingsDialog
- View-Snapshot + Restore beim Wechsel Schnitt → Geschoss
- Symbol-Cleanup bei Delete via normalem Ebenen-Menü

Terrain als Volumen:
- swisstopo.volumize_terrain_object: Skirt + Bottom-Cap auf Mesh/Brep
  damit Clipping-Planes gefuellte Querschnitte erzeugen
- UI im SwisstopoApp mit Nachbearbeitung-Section + Tiefen-Eingabe

Geschoss-Add mit Dialog:
- + im GeschossManager oeffnet 3-Optionen-Picker (Geschoss/Schnitt/Zeichnung)
- Geschoss-Dialog mit Anker-Dropdown, Position über/unter, Auto-Name,
  Höhen-Prefill aus Anker

Fix: _send_state fallback — Element gilt als selektiert wenn Source ODER
Volume in der Selection ist (robust gegen Layer-Visibility wenn Referenz-
linien-Layer im aktuellen Mode versteckt ist)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 18:28:59 +02:00
karim 3277f61ced Oeffnungen-Sublayer + Sturzlinien + Referenz-Layer + Pill-Inputs + Anordnen-Pill
- Oeffnungen-Subtree (Rahmen/Glas/Tuerblatt/Sims/Pane/Schwung/Sturz) als
  nested Children unter WAENDE im dossier_ebenen-Tree registriert + per-Kind
  Material (Glas mit Transparenz)
- Sturzlinien bei 1:100 Tueren mit Innen/Aussen/Beide/Keine-Dropdown
- Referenzlinien-Layer (19) als eigene Ebene fuer wand_axis + oeffnung_point
- Swisstopo Patch-Terrain (Brep.CreatePatch) ersetzt das falsche Loft
- Pill-Style fuer alle Inputs zentral via index.css
- 2x2 Anordnen-Pill in der Oberleiste (BringToFront/Forward/Backward/SendToBack
  via Rhinos DisplayOrder, kein Z-Offset)
- Chevron-Verschiebung in Ebenen-Panel ohne dass Siblings shiften
- Fix: _update_ebene_field walked nur Top-Level, nested Sublayer-Style-
  Changes wurden nicht persistiert
- Fix: Sturz-Linetype wurde bei jedem Wand-Regen zurueckgesetzt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 16:07:44 +02:00
karim 3dc6e31374 Tueren-Schwung: sweep_sign-Swap + perp am Tuerblatt bei standard/detail
- sweep_sign war vertauscht — closed_pt landete am falschen Ende des
  Blattes. hinge=links → +1 (extends in +tan), rechts → -1.
- Schwung-Perp-Position:
  - einfach (1:100) → Wand-Innenkante (unchanged)
  - standard/detail (1:50/1:20) → Mitte des Rahmens (= wo das Tuer-
    blatt-Brep sitzt). Damit liegt der Bogen direkt am sichtbaren
    Blatt statt am Wand-Innenrand.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 13:33:53 +02:00
karim 655d368c92 Tueren-Schwung: Live-Update + Hinge-Position auf Wand-Innenkante
- Schwung-Curves-Block VOR dem Volume-Replace-Pfad — sonst greifen
  openAngle/hingeSide/swingInvert-Aenderungen nicht (Volume-Anzahl
  bleibt gleich → continue → Swing-Skip).
- Hinge-Punkt sitzt nicht mehr auf der Wand-Achse sondern auf der
  Wand-Innenkante (half_d * inside-Richtung). Tuerblatt + Bogen
  beginnen damit an der Wandflucht statt in der Wandmitte.
- Rotation-Vorzeichen korrigiert (rad = open_angle * sweep_sign *
  aus_sign) damit Tuere wirklich ins Innere schwingt.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 13:23:44 +02:00
karim 0c5f8055a5 Fenster/Tueren LoD + Stile + Phase-3-Ausschnitt-Darstellung + UI-Konsistenz
Fenster/Tueren:
- 3-stufige SIA-400-Darstellung pro Element: einfach (1:100, flache
  Scheibe ohne Tiefe in Wand-Mittelebene), standard (1:50, Rahmen +
  Glas + Sims), detail (1:20, Doppelverglasung).
- Aussenseite-Flag mit Auto-Detection aus der Click-Richtung beim
  Setzen — Sim sitzt automatisch aussen. Im Panel als Umkehren-Toggle.
- Tueren-Rahmen-Typ Zarge|Block — Blockrahmen ragt seitlich raus.
- Rahmen-Offset (m von Wand-Innenseite) ersetzt das 3-Preset Lage-
  Feld. Wirkt auch in der einfachen Darstellung (Pane sitzt auf der
  Rahmen-Mittelebene, nicht in Wand-Mitte).
- Sims nur AUSSEN. Innen entfaellt — der Sim ist gleichzeitig der
  visuelle Indikator fuer die Aussenseite.
- Oeffnungs-Stile: list/save/delete-API mit 6 Default-Presets
  (Fenster Standard/Gross/Bandlage, Tuer Innen/Eingang/Verglast).
  Style-ID per UserString am Objekt persistiert. Im Panel BarCombo
  mit "Aktuelle als Stil speichern…". Beim Rhino-Command "Stil"-
  Option zum Picken vor dem Klick.

Ausschnitt-Darstellung (Phase 3):
- Doc-Level Override dossier_aktive_darstellung gewinnt vor per-
  Object-Setting. Wechsel triggert Regen aller Oeffnungen via neuer
  regenerate_all_oeffnungen-API.
- Ausschnitt-Capture speichert die Darstellung mit, Restore wendet
  sie an und regeneriert.
- Oberleiste-Quick-Switch BarCombo mit 4 Optionen.
- AusschnittSettings-Dialog: Darstellungs-Dropdown.

Gestaltung (SectionStyle Phase 2):
- _set_section_style schreibt per-Object SectionHatchIndex/Scale/
  Rotation/Color mit Multi-Fallback (Property-Namen varieren je
  Rhino-Build). _selection_summary liest die selben zurueck.
- HatchEditor als shared Component fuer Fill + Section.
- geometryKind ignoriert DOSSIER-Source-Curves damit Wand-Selektion
  (Axis + Volume) als 3D klassifiziert wird.

UI-Konsistenz Panels:
- Ebenenkombi zurueck als eigene Section oben im Ebenen-Panel,
  Modelldarstellung-Dropdown an die freigewordene Position in der
  Oberleiste (Row 1 Col 2 im 2x2-Preset-Block).
- BarCombo erweitert: stretch-Prop (Pill waechst auf Container-
  Breite), onSecond/secondIcon/secondTitle fuer 2. Trailing-Button,
  gearIcon-Prop. Plus-Slot immer ganz aussen rechts, Settings-Slot
  direkt nach dem Caret.
- Ebenen + Zeichnungsebenen visuell kohaerent: identisches Padding
  (1px 12px 1px 0), Chevron/Spacer-Slot 12px, Master-Row mit Eye
  16x16 + Lock 14x14, gleiche Border + Borderfarbe. Eye-Icons in
  beiden Panels untereinander ausgerichtet.
- Properties-Container ohne Border (war zuvor accent-gruen, dann
  border — User wollte gar nichts mehr).
- ElementList raus aus dem Elemente-Panel (Uebersicht via Tree-
  Window erreichbar). NeuesElement bleibt voll sichtbar bei
  Selektion (kein Collapse), Properties oben.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 12:34:15 +02:00
karim 9ae8574ab0 Panel-Konsistenz: Gestaltung context-aware + Tabellen-Cleanup
Gestaltung:
- Backend detektiert geometryKind (curve / curveOpen / 3d / mixed),
  ignoriert DOSSIER-Source-Curves (wand_axis etc.) damit die
  Klassifikation einer Wand-Selektion (Axis + Volume) als reines 3D
  durchgeht.
- UI zeigt sektion-spezifische Section-Heads: 2D-Curve → Fill+Pen,
  3D-Solid → Section Style+Boundary. Section Style + Fill nutzen
  jetzt einen gemeinsamen HatchEditor (gleiche Controls).
- _set_section_style schreibt per-Object SectionHatchIndex/Scale/
  Rotation/Color via _try_set_attr-Multifallback.
- Selection-Summary liest dieselben Properties zurueck.
- ColorBar als Pill (borderRadius 999) statt eckig.
- "Attribute"-Header oben raus (war alt + redundant).

Ebenen / Zeichnungsebenen:
- Aktive Zeile wieder als Pill (borderRadius 999) — aber ohne
  Margin/Shift, Inhalt springt nicht. Kein bold-text-change mehr.
- Linker borderLeft komplett raus — vorher leere graue Spalte am
  Panel-Rand sichtbar.
- Inhalt mehr nach links (Padding 8/6 statt 12).
- Zeilen-minHeight 24, kompaktere Icons.
- EbenenSettings: Ebene-Picker auf BarCombo.
- GeschossManager: Gebaeudehoehe-Zeile raus, Stift→Settings-Icon.

ElementeApp:
- Alle btn-contained/btn-outlined → BarToggle (Wand-Aufbau, Raum-
  Align, Treppe-Lage/Modus, Oeffnung-Ref/Rahmen/Fluegel/Glas,
  AutoOverride).
- ReferenzSelector → BarToggle.
- "Neues Element"-Container ohne Box-Border, Header-Slot
  beherbergt Projektuebersicht-Pill statt label.
- Property-Labels (UK/OK/Dicke/...) von text-muted auf
  text-secondary (war zu blass).
- Gruener Accent-Border um Properties-Containern → normaler border.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 01:17:58 +02:00