From 961b3c03960e8c611f35c947a73d8c87b25bd160 Mon Sep 17 00:00:00 2001 From: karim Date: Mon, 18 May 2026 01:50:45 +0200 Subject: [PATCH] =?UTF-8?q?Snapshot:=20Wand/=C3=96ffnung=20Multi-Surface-S?= =?UTF-8?q?elect=20+=20Z-Drag=20+=20Br=C3=BCstungs-Mitnahme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stable working state after a long iteration session. The plugin now supports: - Multi-Surface-Select für alle Element-Typen (Türen/Fenster/Treppen/Tragwerk) - Wand-Z-Drag → unbound mode (UK/OK-Override, Wand vom Geschoss entkoppelt) - Wand-Z-Drag nimmt verknüpfte Öffnungen mit (Brüstung += delta_z via Idle-Pfad) - Öffnungs-XY-Drag snapt direktional auf Wand-Tangente - Öffnungs-Z-Drag passt Brüstung an (Fenster sofort sync, Tür deferred) - Wand-Delete kaskadiert Öffnungen (deferred via Idle, robust gegen _Rotate/_Move) - Source-Cascade beim Öffnungs-Delete (deferred analog Wand-Kaskade) - Listener-Cleanup robust gegen _reset_panels.py Reload (Refs in _dossier_runtime_event_refs gespeichert, vor Re-Install deregistriert) - _count_same_id_type filtert IsDeleted (verhindert Source-Duplikat-Bug bei Move) - Frontend: Brüstungs-Slider für Tür ("Schwelle"), Flügel-Block nur bei Fenster Plus aus früherer Phase dieser Session: - Dossier-Launcher Auto-Load via Rhinos StartupCommands-XML - Default-Pfad zeigt auf gebundeltes startup.py (out-of-the-box für neue User) - Splash-Window beim Plugin-Load mit native macOS rounded corners - Diverse Launcher-Verbesserungen (Brüstungs-Default, tauri.conf, capabilities) Known issue: bei Multi-Select-Move mit vielen Sub-Volumen kann sporadisch "Unable to transform" auftreten (Rhinos Move-Operation kollidiert mit Wand- Regen). Tür-spezifischer Defer-Pfad mildert das, Fenster läuft sync. Co-Authored-By: Claude Opus 4.7 --- ARCHITECTURE.md | 207 ++ CLAUDE.md | 265 +- README.md | 2 +- launcher/README.md | 2 +- launcher/index.html | 3 + launcher/latest.json | 10 +- launcher/package-lock.json | 24 +- launcher/package.json | 4 +- launcher/public/splash.html | 146 + launcher/scripts/release.sh | 104 + launcher/src-tauri/Cargo.lock | 501 ++- launcher/src-tauri/Cargo.toml | 13 +- launcher/src-tauri/capabilities/default.json | 9 +- launcher/src-tauri/src/lib.rs | 883 ++++- launcher/src-tauri/tauri.conf.json | 38 +- launcher/src/App.jsx | 1928 +++++++++- launcher/src/components/Icon.jsx | 100 + launcher/src/components/UpdateNotifier.jsx | 144 + launcher/src/styles.css | 1009 +++++- launcher/src/utils/updater.js | 48 + package-lock.json | 8 +- rhino/_reset_panels.py | 36 + rhino/ausschnitte.py | 4 +- rhino/clean.py | 2 +- rhino/clean_layers.py | 2 +- rhino/dimensionen.py | 5 +- rhino/elemente.py | 3311 +++++++++++++++++- rhino/gestaltung.py | 4 +- rhino/inspect_section.py | 4 +- rhino/layer_builder.py | 298 +- rhino/layouts.py | 5 +- rhino/massstab.py | 4 +- rhino/oberleiste.py | 693 +++- rhino/overrides.py | 85 +- rhino/overrides_panel.py | 4 +- rhino/panel_base.py | 271 +- rhino/rhinopanel.py | 94 +- rhino/startup.py | 24 +- rhino/startup.py3 | 142 + rhino/werkzeuge.py | 4 +- src/App.jsx | 32 +- src/DimensionenApp.jsx | 2 +- src/ElementeApp.jsx | 783 ++++- src/GestaltungApp.jsx | 2 +- src/LayoutsApp.jsx | 10 +- src/MassstabApp.jsx | 12 +- src/OberleisteApp.jsx | 18 +- src/OverridesApp.jsx | 10 +- src/WerkzeugeApp.jsx | 12 +- src/components/EbenenSettingsDialog.jsx | 180 +- src/components/GeschossSettingsDialog.jsx | 12 +- src/lib/rhinoBridge.js | 12 + 52 files changed, 10760 insertions(+), 765 deletions(-) create mode 100644 ARCHITECTURE.md create mode 100644 launcher/public/splash.html create mode 100755 launcher/scripts/release.sh create mode 100644 launcher/src/components/Icon.jsx create mode 100644 launcher/src/components/UpdateNotifier.jsx create mode 100644 launcher/src/utils/updater.js create mode 100644 rhino/_reset_panels.py create mode 100644 rhino/startup.py3 diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..a30247d --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,207 @@ +# Dossier — Architektur + +Stand: 2026-05-17. Dieses Dokument beschreibt wie die Module zusammenspielen, +welche Konventionen gelten und wo bekannte Schwachstellen liegen. + +> ⚠️ **Runtime-Realität — Migration in Arbeit:** Trotz `# ! python3` +> Shebangs in allen Files läuft der Plugin-Code aktuell als **IronPython 2.7** +> unter .NET 8 auf Mac Rhino 8.31 — verifiziert via `sys.version`. Migration +> zu Rhinos neuer CPython-3-Engine läuft (siehe CLAUDE.md für Stand). Bis +> dahin gelten IPy-2.7-Idiome im Code: `__builtin__`, kein f-string-Vertrauen, +> `print` mit/ohne Klammern gemischt, `import System` direkt (statt `clr.AddReference`). + +**Bei jeder Code-Änderung in `rhino/` zuerst dieses Dokument lesen.** Wenn +sich Patterns hier ändern, dieses Dokument mit-aktualisieren — sonst rotted +es. + +--- + +## 1. Module-Map (`rhino/`) + +| Modul | LOC | Rolle | Hängt ab von | +|---|---:|---|---| +| `panel_base.py` | 697 | **Fundament**: BaseBridge, WebView-IO, Panel-Registration, Icons, Legacy-Migration | — | +| `rhinopanel.py` (EBENEN) | 798 | Zeichnungsebenen (Layer-Hierarchie, Presets) | panel_base, layer_builder, massstab (sticky) | +| `elemente.py` (ELEMENTE) | **7244** | Smart Elements: Wände, Decken, Öffnungen, Treppen, Tragwerk, Räume (SIA-416) | panel_base, overrides+rhinopanel (sticky) | +| `gestaltung.py` (GESTALTUNG) | 1635 | Selektions-Attribute: Farbe, Lineweight, Linetype, Hatch, Plot-Sync | panel_base, massstab (sticky) | +| `oberleiste.py` (OBERLEISTE) | 981 | Top-Bar: View/Display/Massstab-Proxy, Snaps, Window-Layout, Settings | panel_base, massstab, overrides (sticky) | +| `massstab.py` (MASSSTAB) | 1096 | Viewport-Skala 1:N, Auto-DPI (CoreGraphics), PlotWeight | panel_base, layer_builder | +| `overrides.py` | 797 | Engine: regelbasierte Overrides (Bedingung→Aktion), Presets cross-doc | — (Library) | +| `overrides_panel.py` | 226 | UI auf overrides-Engine | panel_base, overrides, oberleiste (sticky) | +| `ausschnitte.py` (AUSSCHNITTE) | 708 | Viewport-Snapshots (Kamera + Display + Layer) | panel_base, massstab | +| `dimensionen.py` (DIMENSIONEN) | 613 | Bemaßungs-Panel (Wand-Dicken, Geschoss-Höhen, Öffnungen) | panel_base | +| `layouts.py` (LAYOUTS) | 749 | Layout-Editor für Druckplatten | panel_base | +| `werkzeuge.py` (WERKZEUGE) | 58 | Quick-Tools (Batch-Operationen) | panel_base | +| `layer_builder.py` | 436 | Helper: Ebenen-Hierarchie aufbauen, Sublayer-Sync | — (Library) | +| `startup.py` | 136 | Initialisierer: liest `dossier.project.json`, lädt Module selektiv | panel_base + Module via `__import__` | +| `clean.py` / `clean_layers.py` / `_reset_panels.py` / `inspect_section.py` | 48-163 | Wartung / Debugging | — | + +--- + +## 2. Tragende Patterns + +### 2.1 Bridge-Pattern (Pflicht für jedes Panel) + +```python +class MyBridge(panel_base.BaseBridge): + def __init__(self): + panel_base.BaseBridge.__init__(self, "mymodule") + + def _on_ready(self): + self.send("STATE_SYNC", {...}) # WebView fertig geladen + + def handle(self, data): + t = data.get("type") + if t == "ACTION": self._do_action() + +def _bridge_factory(): + b = MyBridge() + _install_listeners(b) # Rhino-Events registrieren + return b + +panel_base.register_and_open( + "mymodule", "MY PANEL", PANEL_GUID_STR, + _bridge_factory, + icon_spec=("foundation", "#5fa896"), # Material-Icon-Name + Petrol + min_size=(400, 300), +) +``` + +### 2.2 React ↔ Python Kommunikation + +- **React → Python**: `document.title = "RHINOMSG::{json}"` — gepollt im Idle-Handler in `panel_base`. +- **Python → React**: `bridge.send(type, payload)` → `webview.ExecuteScript("window.onRhinoMessage(...)")` +- **Chunking**: Messages > 200 KB werden in `panel_base.handle_raw` automatisch gesplittet + reassembliert. Subklassen kümmern sich nicht drum. + +### 2.3 Sticky-Storage (Cross-Module-State) + +Konventionen für Keys: + +- `"{modul}_bridge"` — Bridge-Instanz (in `_bridge_factory` registriert) +- `"{modul}_listeners"` — Bool-Flag: Listener bereits registriert? (verhindert Doppel-Hook) +- `"_dossier_*"` — globale States (z.B. `_dossier_joints_cache`, `_dossier_timing_enabled`, `_dossier_layout_applied`) +- `"{modul}_*_cache"` — Modul-Cache (z.B. `_JOINTS_CACHE_KEY` in elemente) + +### 2.4 Listener-Hookup (Idempotent) + +```python +def _on_idle(s, e): + b = sc.sticky.get("mymodule_bridge") + if b is not None: # IMMER None-Check + try: b._send_state() + except Exception: pass + +def _install_listeners(bridge): + flag = "mymodule_listeners" + sc.sticky["mymodule_bridge"] = bridge + if sc.sticky.get(flag): return # Schon registriert + Rhino.RhinoApp.Idle += _on_idle + Rhino.RhinoDoc.ActiveDocumentChanged += _on_view_change + sc.sticky[flag] = True +``` + +### 2.5 Cache-Pattern + +- **Joint-Cache** (`elemente.py: _JOINTS_CACHE_KEY`): pro Geschoss; invalidiert bei Add/Delete/Replace. +- **Material-Cache** (`elemente.py`): Hex→MaterialIndex; stale-Check beim Lesen. +- **Hatch-Curve-Link** (`gestaltung.py`): UUID→Hatch in Sticky, weil Rhino UserStrings bei Move/Replace teils wegwischt. +- **Display-Modes-Cache** (`oberleiste.py`): einmalig gelesen, Sticky-gecacht. +- **Pending-Hatch TTL** (`gestaltung.py`): 3 s Fenster nach Drag/Move, in dem Hatch-Metadaten wiederherstellbar sind. + +### 2.6 Settings-File + +Pfad-Hierarchie: + +1. **Primär** (Launcher schreibt): `~/Library/Application Support/ch.gabrielevarano.Dossier/dossier_settings.json` +2. **Legacy-Fallback** (read-only): `~/Library/Application Support/RhinoPanel/dossier_settings.json` + +Bekannte Keys: `windowLayout`, `autoApplyLayout`, `pendingApplyLayout`, `rhinoApp`, `templatePath`. + +Normalisierung: Legacy `defaultLayout` → `windowLayout` in `_settings_load`. + +### 2.7 Window-Layouts auf Mac (XML, nicht .rwl!) + +- Speicherort: `~/Library/Application Support/McNeel/Rhinoceros/8.0/settings/Scheme__Default/workspaces/.xml` +- Display-Name aus `` Attribut. +- Apply: Reflection über `Rhino.UI.WindowLayout.*`, Fallback `_-SetActiveLayout "Name" _Enter`. +- Live-Apply aus dem Launcher: setzt `pendingApplyLayout` im Settings-JSON; `oberleiste.tick_idle()` pollt + clearet. + +--- + +## 3. Cross-Module-Pfade (Sticky-Bus) + +| Sender → Empfänger | Trigger | Effekt | +|---|---|---| +| `rhinopanel` → `elemente` | Apply von Ebenen-Struktur (Höhen/OKFF) | `elemente_bridge._regenerate_all()` regeneriert Wände/Decken | +| `elemente` → `rhinopanel` | Wand/Decken-Delete | `ebenen_bridge_ref._send_state()` (Fallback-Chain) | +| `oberleiste` → `overrides` | Preset-Auswahl in Topbar | `overrides_bridge._send_state()` | +| `massstab` ↔ `ausschnitte` | Viewport-/Zoom-Wechsel | Bi-direktional Skala lesen/setzen | +| `gestaltung` ↔ `rhinopanel` | Hatch-Pattern auf Selektion | Pattern+Scale+Rotation-Signature vergleichen | + +**Risiko**: Sticky-Reads ohne `is not None`-Check sind verstreut (vor allem in `oberleiste.py` an einigen Stellen). Bei Refactor immer Schutz einbauen. + +--- + +## 4. Bekannte Schwachstellen + +### 4.1 `elemente.py` Monolith (7244 LOC) +Enthält Wand-Achse+Volumen, Wand-Miter/T-Junction, Decken (Brep-Extrusion), Öffnungen (Fenster/Türen mit Rahmen+Sims+Flügel), Treppen (gerade/L/Wendel), Tragwerk (Stütze/Träger/I-Profil), Räume (SIA-416 Stempel + Farben). Vorschlag für späteren Refactor: Split in `wand.py`, `decke.py`, `oeffnung.py`, `treppe.py`, `tragwerk.py`, `raum.py` + Shared-Utils-Modul (`_read_meta`, `_geschoss_lookup`, `_active_geschoss_id`). Aktuell **nicht kritisch**, aber bremst Navigation und Tests. + +### 4.2 Duplizierter Code +- `_color_to_hex` / `_hex_to_color` in 3 Modulen (gestaltung, panel_base, layer_builder) +- Geschoss-Lookup-Helper in elemente.py (gehören in layer_builder) +- Layer-Hierarchie-Aufbau split zwischen layer_builder.py und rhinopanel.py + +### 4.3 Cache-Stale-Risiken +- **Joint-Cache** invalidiert bei Add/Delete/Replace — Undo/Redo wird **nicht** abgefangen +- **Material-Cache** erkennt erst beim Zugriff dass ein Index ungültig ist; bei Material-Delete in Rhino bleibt Cache bis Restart stale +- Workaround: `clean.py` für manuellen Cache-Clear, aber nicht automatisch + +### 4.4 None-Check-Lücken in Sticky-Reads +Beispiel `oberleiste.py:733` (Pfad variiert): +```python +b = sc.sticky.get("overrides_bridge") # kann None sein +b._send_state() # → AttributeError wenn None +``` +Beim nächsten Touch dieser Stellen: `if b is not None:` einziehen. + +### 4.5 `ebenen_bridge_ref` Fallback-Chain +Drei Lookup-Namen aus historischen Gründen (`ebenen_bridge_ref` → `ebenen_bridge` → `rhinopanel_bridge`). Bei nächstem Touch konsolidieren. + +### 4.6 Doppel-Listener-Risiko +Wenn zwei Module gleichzeitig laden und beide einen globalen Idle-Handler registrieren — Flag-Schutz pro Modul gut, aber kein zentrales Lock. Bisher in Praxis kein Problem. + +--- + +## 5. Was die Architektur richtig macht (nicht anfassen ohne Grund) + +1. **BaseBridge-Abstraktion** ist sauber: alle Bridges folgen demselben Lifecycle, WebView-Integration ist transparent. +2. **Chunk-Handling** für Large Messages (>200 KB) in `panel_base.handle_raw` — elegant, Subklassen merken nichts. +3. **Migration-Strategy** (`traite_` → `pause_` → `dossier_` Sticky-Prefixes): idempotent, per-Doc-Flag verhindert Mehrfach-Lauf. +4. **Icon-System**: Multi-Fallback PNG → SVG → Material-Font → Buchstabe; gecacht. +5. **Selective Module-Loading** über `startup.py` + `dossier.project.json`: ein Projekt zieht nur die benötigten Module. +6. **DPI-Auto-Detection** via CoreGraphics auf Mac — robuster als die meisten alternativen Ansätze. +7. **UTF-8 Handling konsequent**: `ensure_ascii=False`, defensive int()-Casts vor Format — vermeidet Rhino-Encoder-Bugs. +8. **Defensives Error-Handling**: Try/Except mit `[MODUL]`-Präfix-Logging in der Rhino-Konsole; Plugin bricht nicht ab bei Einzelfehlern. + +--- + +## 6. Launcher-Anbindung (Tauri) + +Der **Dossier-Launcher** (`launcher/`) ist eine separate Tauri-App: +- Verwaltet Projekt-Liste + Settings + Updates (auto via `tauri-plugin-updater`) +- Schreibt `dossier_settings.json` in den oben (§2.6) genannten Primär-Pfad +- Live-Push an laufende Rhino-Session: `pendingApplyLayout`-Key in Settings, `oberleiste.tick_idle()` pollt + clearet +- System-Tray mit Quick-Open der letzten 5 Projekte (`refresh_tray_menu`-Command nach jedem Recent-Update) + +Rhino kann ohne Launcher laufen; Launcher kann ohne Rhino laufen. IPC ist bewusst dateibasiert, kein Socket. + +--- + +## 7. Wenn du was änderst + +1. **Lies dieses Dokument** + den `## tragenden Patterns`-Block der CLAUDE.md +2. **Halt dich an die Naming-Konventionen** (Sticky-Keys, Bridge-Factory) +3. **Bei Sticky-Reads: `is not None`-Check** (siehe §4.4) +4. **Cache invalidieren wenn dein Code Source-Daten ändert** (siehe §2.5) +5. **Dieses Dokument up-to-date halten** wenn sich Patterns/Schwachstellen ändern diff --git a/CLAUDE.md b/CLAUDE.md index be1b7ed..01edc87 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,160 +1,147 @@ -# RhinoPanel — Projektdokumentation für Claude +# Dossier — Projekt-Anweisungen für Claude -## Was ist das? +## Was das ist -Ein React-Plugin für Rhino 8 (Mac) das als schwebendes Fenster läuft und Architektur-Workflows aus Vectorworks/ArchiCAD nachbildet. Die React-UI wird in Rhinos Eto.Forms WebView über `LoadHtml` (inline) eingebettet. +**Dossier** ist ein Rhino 8 Plugin (Mac) mit React-WebView-Panels für +architektonische Workflows (Wände, Decken, Öffnungen, Räume, SIA-416, +Plan-Layouts). Teil der OpenStudio-Suite. Schwester-App: Rapport. -## Kommunikation React ↔ Python +**Dossier-Launcher** ist eine separate Tauri-App (`launcher/`), die Projekte +verwaltet, Settings hält (auch für den Plugin), Auto-Updates liefert und im +System-Tray lebt. -**React → Python:** `document.title = "RHINOMSG::{json}"` (queue-basiert, 80ms delay) -**Python → React:** `webview.ExecuteScript("window.onRhinoMessage({...})")` +## Runtime: Python 3.9 CPython (verifiziert 2026-05-17) -Nachrichten-Typen: -- `APPLY` — Ebenen auf Rhino anwenden, GH triggern -- `LAYER_VISIBILITY` — Layer sofort ein/ausblenden -- `LAYER_LOCK` — Layer sperren/entsperren -- `SET_ACTIVE` — Aktiven Rhino-Layer setzen -- `STATE_SYNC` — Python → React, beim Panel-Start +Dieser Ordner `DOSSIER/` läuft mit Rhinos **neuem Python-3-Engine** (CPython +3.9.10 via Script Editor). Der Vorgänger `rhino-panel/` bleibt **frozen** als +IPy-2.7-Referenz. -## Datenmodell +**Wie geladen wird (WICHTIG):** +- ✅ **`_ScriptEditor`** → Datei öffnen + Run-Button → CPython 3.9 (Shebang + `#! python3` wird respektiert). Das ist der **funktionierende Pfad**. +- ✅ **Rhino Options → General → Command Lists → „Run these commands every + time a model is opened"** → Form: `_-RunPythonScript "/voller/pfad.py"` + (mit Dash + Quotes). Persistiert in `Options/General/StartupCommands` der + `settings-Scheme__Default.xml`. Lädt das Skript bei jedem Rhino-Start + silent, ohne File-Dialog. Trotz Dash bleibt Shebang `#! python 3` + wirksam → CPython 3.9 (verifiziert 2026-05-17 Mac Rhino 8). Der Launcher + trägt diesen Eintrag automatisch ein. +- ⚠️ `_-RunPythonScript "path.py"` mit Dash in der **interaktiven Command- + Line** → IronPython 2.7 (Legacy). NICHT benutzen für DOSSIER, sonst + crasht die SectionStyle-Logik etc. +- ⚠️ `_RunPythonScript path.py` ohne Dash — in der Command-Line OK (Py3 + via Shebang, ohne Quotes, in einer Zeile). Im StartupCommands-Feld + öffnet diese Form aber einen File-Dialog statt zu laufen. -```json -[ - {"id": "eg", "name": "EG", "type": "grundriss", "hoehe": 3.50, "schnitthoehe": 1.00, "okff": 0.00}, - {"id": "1og", "name": "1OG", "type": "grundriss", "hoehe": 3.00, "schnitthoehe": 1.00, "okff": 3.50}, - {"id": "saa", "name": "Schnitt A-A", "type": "schnitt"}, - {"id": "nor", "name": "Nordansicht", "type": "ansicht"} -] +**Migration-Stand:** +- ✅ Repo kopiert nach `DOSSIER/`, alle Pfade umgestellt +- ✅ Shebangs aller Files auf `#! python3` (ohne Space) — Format das Rhino erkennt +- ✅ Code war bereits Py3-syntax-kompatibel (kein xrange/iteritems/unicode()) +- ✅ Plugin läuft als CPython 3.9.10, alle 9 Panels registrieren +- ✅ Reflection.Emit + Eto.Forms + Bridge funktionieren +- ✅ **`Rhino.DocObjects.SectionStyle()` instanziierbar** + `layer.SetCustomSectionStyle()` + verfügbar — die volle Section-Style-API ist jetzt zugänglich (Anlass der Migration) + +## Bei Code-Arbeit — Reihenfolge + +1. **`ARCHITECTURE.md` zuerst lesen** — Module-Map, Konventionen, Schwachstellen. +2. **Dann das relevante Modul lesen**. Nicht raten was drin steht. +3. **Erst danach editieren**. + +## Anti-Over-Engineering + +Diese Regeln sind nicht verhandelbar — Verstöße kosten Zeit beim Aufräumen. + +- **Keine Abstraktionen einführen, die ein konkretes Problem lösen.** + Drei ähnliche Zeilen sind besser als eine Hilfsfunktion mit drei Aufrufstellen. +- **Kein „aufräumen drumherum".** Ein Bugfix bleibt ein Bugfix. +- **Kein Error-Handling für Szenarien, die nicht eintreten können.** +- **Keine Feature-Flags, keine Migrations-Shims parallel zur alten Funktion.** +- **Keine Kommentare die WAS sagen** — nur WARUM-Kommentare wenn nicht-offensichtlich. +- **Keine erfundenen Module/Funktionen/Flags.** Erst `grep`, dann editieren. + +## Anti-Patterns (aus echten Sessions) + +- **`try/except: pass` als Bug-Verstecker.** Lieber `print("[MODUL] err:", ex)` und weiter. +- **Sticky-Reads ohne `is not None`-Check.** Siehe `ARCHITECTURE.md §4.4`. +- **„Cleveres" Refactoring von Wand-Geometrie.** `elemente.py` (7244 LOC) + enthält BIM-Logik die in Echtbau-Projekten läuft. NIE in einem Rutsch + modularisieren ohne expliziten Auftrag + Test-Plan. +- **Mac vs. Windows Rhino-Pfade verwechseln.** Mac Rhino 8 speichert + Window-Layouts als **XML in `Scheme__Default/workspaces/.xml`**, nicht + als `.rwl`. +- **Python-Runtime annehmen statt prüfen.** Diagnose mit `print(sys.version)` + — das hat uns 4 Wochen versteckte IPy2.7 gekostet. +- **`document.title` mit Umlauten kaputt machen.** UI-Strings dürfen Umlaute; + **Python-Backend bevorzugt `ue/oe/ae`** in Identifiern, Layer-Codes, + UserString-Values. + +## Python-Konventionen — POST-MIGRATION TARGET + +Sobald die Migration durch ist, gilt: + +- Datei-Header: `# ! python3` + `# -*- coding: utf-8 -*-` (werden dann wirksam) +- **Aufruf in Rhino:** `_RunPythonScript "path"` (ohne Dash!) — sonst startet + IPy 2.7. Alternative: `_-ScriptEditor` mit Code-Engine +- `print(x)` — IMMER mit Klammern (Python-3-Style) +- `builtins` statt `__builtin__` +- f-strings erlaubt: `f"value: {x}"` +- CLR: `import clr; clr.AddReference("System.Drawing"); from System.Drawing import Color` +- UI-Strings dürfen Umlaute, Code-Identifier nicht (`tuer`, nicht `tür`) + +## Build & Reset (Cheatsheet) + +```bash +# Rhino-Panels Frontend +npm run build # im Repo-Root + +# Launcher Frontend +cd launcher && npm run build + +# Launcher Backend Check +cd launcher/src-tauri && cargo check + +# Launcher Release (signiert, schreibt latest.json) +cd launcher && ./scripts/release.sh + +# Python-Syntax-Check (kein Rhino nötig) +python3 -c "import ast; ast.parse(open('rhino/elemente.py').read())" + +# Runtime-Verify in Rhino (welcher Python-Engine läuft wirklich?) +_RunPythonScript /Users/karim/STUDIO/DOSSIER/rhino/startup.py +# Erste Zeile sollte zeigen: [STARTUP] Python: 3.x ... nach Migration ``` -Gespeichert in `doc.Strings["rhinopanel_ebenen"]` (JSON). OKFF wird nur für `type: "grundriss"` berechnet (kumulativ). Schnitt/Ansicht haben keine Höhenparameter. +**Plugin reset in Rhino** (nach Python-Änderungen): -## Rhino Layer-Hierarchie - -``` -10_GRUNDRISSE - └── EG - ├── 01_WAND (schwarz, lw 0.50) - ├── 02_TUER_FENSTER (blau, lw 0.25) - ├── 03_MOEBEL (grau, lw 0.13) - ├── 04_TEXT (hellgrau, lw 0.13) - ├── 05_TREPPEN (gold, lw 0.35) - └── 06_3D_VOLUMEN (lila, lw 0.25) - └── 1OG (gleiche Sublayer) -20_SCHNITTE - └── Schnitt A-A - ├── 21_PROFIL (rot, lw 0.70) - ├── 22_WAND (orange, lw 0.25) - └── 23_TEXT (hellgrau, lw 0.13) -30_ANSICHTEN - └── Nordansicht - ├── 31_FASSADE (türkis, lw 0.35) - └── 32_TEXT (hellgrau, lw 0.13) -00_RASTER, 01_VERMESSUNG, 40_SITUATION, 90_REFERENZEN, 99_KONSTRUKTION -``` - -## Dateistruktur - -``` -rhino-panel/ -├── src/ -│ ├── App.jsx # Hauptkomponente, State-Management -│ ├── main.jsx # Entry point, window.onerror handler -│ ├── index.css # Dark theme, CSS-Variablen (Swiss minimal) -│ ├── lib/ -│ │ └── rhinoBridge.js # Kommunikation React↔Python -│ └── components/ -│ ├── EbenenManager.jsx # Zeichnungsebenen-Liste (GR/SC/AN Badges) -│ ├── EbenenDialog.jsx # Dialog zum Bearbeiten der Ebenen -│ ├── LayerPanel.jsx # Layer-Sichtbarkeit/Sperre togglen -│ ├── BottomBar.jsx # "Auf Rhino anwenden" Button -│ └── Section.jsx # Ausklappbarer Abschnitt -├── rhino/ -│ ├── rhinopanel.py # Panel starten, Bridge, LoadHtml-Inline -│ ├── layer_builder.py # Rhino-Layer erstellen/aktualisieren -│ └── INSTALL.md # Setup-Anleitung inkl. GH -├── dist/ # Gebaute App (npm run build) -└── vite.config.js # base: './' wichtig für file:// URLs -``` - -**Alte Dateien (nicht mehr aktiv, können gelöscht werden):** -- `src/components/GeschossManager.jsx` — ersetzt durch EbenenManager -- `src/components/GeschossDialog.jsx` — ersetzt durch EbenenDialog - -## Kritische technische Details - -### Warum LoadHtml statt file:// URL -Rhinos WKWebView blockiert `