Snapshot: Wand/Öffnung Multi-Surface-Select + Z-Drag + Brüstungs-Mitnahme

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 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 01:50:45 +02:00
parent 1180d7bedf
commit 961b3c0396
52 changed files with 10760 additions and 765 deletions
+26 -6
View File
@@ -92,10 +92,21 @@ export default function App() {
setCombDialog(d => d ? { ...d, layers: layers || d.layers, presets: presets || [] } : d)
}
})
onMessage('FIRST_RUN', () => {
applyAll(INITIAL_ZEICHNUNGSEBENEN, INITIAL_EBENEN)
onMessage('FIRST_RUN', ({ defaultEbenen } = {}) => {
// Wenn der Dossier-Launcher ein eigenes Schema definiert hat, nutzen wir
// das statt der hardcoded INITIAL_EBENEN. Felder ohne `visible`/`locked`
// werden mit Defaults ergaenzt damit die UI-Komponenten keine undefineds
// sehen.
const useEbenen = (Array.isArray(defaultEbenen) && defaultEbenen.length)
? defaultEbenen.map(e => ({
visible: true, locked: false,
...e,
}))
: INITIAL_EBENEN
setEbenen(useEbenen)
applyAll(INITIAL_ZEICHNUNGSEBENEN, useEbenen)
setAppliedZ(INITIAL_ZEICHNUNGSEBENEN)
setAppliedE(INITIAL_EBENEN)
setAppliedE(useEbenen)
const active = INITIAL_ZEICHNUNGSEBENEN.find(zz => zz.id === activeId) || INITIAL_ZEICHNUNGSEBENEN[0]
if (active) {
setActiveZeichnungsebene(active)
@@ -136,13 +147,22 @@ export default function App() {
if (!f || !f.pattern || f.pattern === 'None') return ''
return [f.pattern, f.source || 'layer', f.color || '', f.scale ?? 1, f.rotation ?? 0].join('|')
}
// WICHTIG: alle Felder die das Backend braucht hier mit drin haben — sonst
// triggert Aenderung an z.B. hasClipping/schnitthoehe kein Apply, und das
// Backend sieht den neuen Stand nie. Frueher waren nur id/name/isGeschoss
// drin -> Clipping-Toggle blieb wirkungslos.
const zSig = (z) => [
z.id, z.name, z.isGeschoss ? 1 : 0,
z.hoehe ?? '', z.schnitthoehe ?? '',
z.hasClipping ? 1 : 0,
].join(':')
const structureKey = useMemo(() => (
zeichnungsebenen.map(z => `${z.id}:${z.name}:${z.isGeschoss ? 1 : 0}`).join(',') + '|' +
zeichnungsebenen.map(zSig).join(',') + '|' +
ebenen.map(e => `${e.code}:${e.name}:${fillSig(e)}`).join(',')
), [zeichnungsebenen, ebenen])
const appliedStructureKey = useMemo(() => (
appliedZ.map(z => `${z.id}:${z.name}:${z.isGeschoss ? 1 : 0}`).join(',') + '|' +
appliedZ.map(zSig).join(',') + '|' +
appliedE.map(e => `${e.code}:${e.name}:${fillSig(e)}`).join(',')
), [appliedZ, appliedE])
@@ -176,7 +196,7 @@ export default function App() {
const name = (window.prompt('Name für Ebenenkombination:', suggested) || '').trim()
if (!name) return
if (combinations.some(p => p.name === name) &&
!window.confirm(`"${name}" ueberschreiben?`)) return
!window.confirm(`"${name}" überschreiben?`)) return
saveCurrentAsCombination(name)
setActiveCombName(name)
}