Files
DOSSIER/src/WerkzeugeApp.jsx
T
karim 961b3c0396 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>
2026-05-18 01:50:45 +02:00

114 lines
4.8 KiB
React

import { useEffect } from 'react'
import Icon from './components/Icon'
import { notifyReady, runRhinoCommand } from './lib/rhinoBridge'
// Tool-Definitionen: [icon, label, rhino-command, tooltip]
// Material-Symbol-Namen siehe https://fonts.google.com/icons
const TOOLS = {
'2D Zeichnen': [
['horizontal_rule', 'Line', '_Line', 'Linie zwischen zwei Punkten'],
['polyline', 'Polyline', '_Polyline', 'Polylinie'],
['rectangle', 'Rect', '_Rectangle', 'Rechteck'],
['radio_button_unchecked', 'Circle', '_Circle', 'Kreis'],
['network_intelligence', 'Arc', '_Arc', 'Bogen'],
['gesture', 'Curve', '_Curve', 'Freie Kurve (Spline)'],
['text_fields', 'Text', '_Text', 'Text'],
['grid_view', 'Hatch', '_Hatch', 'Schraffur'],
['straighten', 'Dim', '_Dim', 'Linearbemassung'],
],
'2D Editieren': [
['open_with', 'Move', '_Move', 'Verschieben'],
['content_copy', 'Copy', '_Copy', 'Kopieren'],
['rotate_right', 'Rotate', '_Rotate', 'Drehen'],
['aspect_ratio', 'Scale', '_Scale', 'Skalieren'],
['flip', 'Mirror', '_Mirror', 'Spiegeln'],
['padding', 'Offset', '_Offset', 'Parallelversatz'],
['content_cut', 'Trim', '_Trim', 'Stutzen'],
['swipe_right_alt', 'Extend', '_Extend', 'Verlängern'],
['link', 'Join', '_Join', 'Verbinden'],
['scatter_plot', 'Explode', '_Explode', 'Auflösen'],
['rounded_corner', 'Fillet', '_Fillet', 'Verrunden (Ecke abrunden)'],
['apps', 'Array', '_ArrayPolar','Polar-Array'],
],
'3D Modellieren': [
['vertical_align_top','Extrude', '_ExtrudeCrv', 'Kurve zu 3D extrudieren'],
['square', 'Box', '_Box', 'Quader'],
['join_inner', 'Union', '_BooleanUnion', 'Boolean-Vereinigung'],
['remove', 'Diff', '_BooleanDifference', 'Boolean-Differenz'],
['gradient', 'Intersect','_BooleanIntersection','Boolean-Schnittmenge'],
['roofing', 'Cap', '_Cap', 'Planare Loecher schliessen'],
['cut', 'Section', '_Section', 'Schnittlinien erzeugen'],
['unfold_more', 'Loft', '_Loft', 'Loft (Kurven verbinden)'],
],
'Auswahl': [
['add_link', 'Chain', '_SelChain', 'Tangentiale Kurvenkette wählen'],
['filter_alt', 'Dup', '_SelDup', 'Doppelte Objekte wählen'],
['loop', 'Closed', '_SelClosedCrv', 'Geschlossene Kurven wählen'],
['compare_arrows', 'Invert', '_Invert', 'Auswahl invertieren'],
['select_all', 'All', '_SelAll', 'Alle auswählen'],
['deselect', 'None', '_SelNone', 'Auswahl aufheben'],
],
}
// ---------------------------------------------------------------------------
function ToolButton({ icon, label, cmd, tip }) {
return (
<button
onClick={() => runRhinoCommand(cmd)}
title={`${tip} (${cmd})`}
style={{
width: '100%',
padding: '6px 8px',
display: 'flex', alignItems: 'center', gap: 8,
background: 'var(--bg-item)', border: '1px solid var(--border)',
borderRadius: 'var(--r)', color: 'var(--text-primary)',
cursor: 'pointer', textAlign: 'left',
}}
>
<Icon name={icon} size={16} style={{ flexShrink: 0 }} />
<span style={{ fontSize: 11, fontWeight: 500 }}>{label}</span>
</button>
)
}
function GroupLabel({ children }) {
return (
<div style={{
fontSize: 9, color: 'var(--text-muted)', textTransform: 'uppercase',
letterSpacing: '0.05em',
padding: '8px 4px 4px',
borderTop: '1px solid var(--border)',
}}>{children}</div>
)
}
// ---------------------------------------------------------------------------
export default function WerkzeugeApp() {
useEffect(() => { notifyReady() }, [])
const groups = Object.entries(TOOLS)
return (
<div style={{
width: '100%', height: '100%',
display: 'flex', flexDirection: 'column', gap: 0,
padding: 6,
fontFamily: 'var(--font)', color: 'var(--text-primary)',
background: 'var(--bg-base)',
boxSizing: 'border-box',
overflowY: 'auto', overflowX: 'hidden',
}}>
{groups.map(([title, items], gi) => (
<div key={title} style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
<GroupLabel>{title}</GroupLabel>
{items.map(([icon, label, cmd, tip]) => (
<ToolButton key={cmd} icon={icon} label={label} cmd={cmd} tip={tip} />
))}
</div>
))}
</div>
)
}