Zeichnungsmanager Master-Controls + Scheren + Startup-Perf + Oeffnung-Preview
UI: - GeschossManager: Master-Eye + Master-Lock im Header (analog EbenenManager). Scheren-Button pro Geschoss togglet hasClipping. Auge ganz links wie bei Ebenen. Eye-Logik klar 4-Wege: aktive Z immer hell+on, in 'active'/'all_force' fuer non-active gedimmt, sonst spiegelt Flag direkt. Schrift wird NIE gegrayt. Neuer Mode 'all_force' = "Alle anzeigen" (ignoriert Eye), 'all' jetzt mit Label "Ausgewaehlte" (respektiert Eye). Klick aufs Auge in 'active'/'all_force' wechselt automatisch in "Ausgewaehlte" damit Aktion sofort wirkt. - layer_builder.apply_visibility: neuer z_mode 'all_force' vor visible-Check — zeigt jede Z auch wenn Eye=false war. - elemente._cmd_create_oeffnung: gruene Live-Preview (vertikales Oeffnungs- Rechteck + Breiten-Marker + Diagonale) waehrend Fenster/Tuer-Platzierung entlang der Wand-Achse. Brueest-Offset aus Geschoss-UK korrekt im Z. Performance Cold-Start: - panel_base: Inlined-HTML als Modul-Cache (1x build, n-mal mount). Pro Panel-Mount nur noch str.replace + LoadHtml. Spart bei 10 Panels 9x ~395 KB Disk-Read + Regex-Pass. Cache-Key = mtime von dist/index.html. - Timing-Instrumentierung: _t_mark + print_startup_summary. Hook in startup.py feuert 3s nach Plugin-Load + listet Wall-time, Top-10, Aggregat pro Phase. - OberleisteBridge: Command-Enumeration (~1000 Commands) jetzt lazy via Rhino.RhinoApp.Idle statt synchron im __init__. Cold-Start nicht blockiert, Autocomplete kommt ~1 Frame spaeter. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -9,10 +9,38 @@ function GeschossBadge({ name }) {
|
||||
|
||||
function ZeichnungsebeneRow({
|
||||
z, active, mode, onClick, onContextMenu,
|
||||
onToggleVisible, onToggleLock, onDelete,
|
||||
onToggleVisible, onToggleLock, onToggleClipping, onDelete,
|
||||
}) {
|
||||
const eyeShown = mode !== 'active'
|
||||
const isGeschoss = !!z.isGeschoss
|
||||
// Eye-Logik: die aktive Z ist IMMER sichtbar (Backend forciert das), also
|
||||
// zeigen wir ihr Auge immer als "an" — ohne Ruecksicht aufs visible-Flag.
|
||||
// Nicht-aktive: in 'all_force' ist visible-Flag ueberschrieben (alle an),
|
||||
// in 'active' ueberschrieben (alle aus) — Auge dimmt. Sonst (Ausgewaehlte/
|
||||
// grey) reflektiert es das Flag direkt.
|
||||
let eyeIcon, eyeOn, eyeOpacity, eyeTitle
|
||||
if (active) {
|
||||
eyeIcon = 'visibility'
|
||||
eyeOn = true
|
||||
eyeOpacity = 1
|
||||
eyeTitle = z.visible !== false
|
||||
? 'Sichtbar (aktive Zeichnungsebene)'
|
||||
: 'Normalerweise ausgeblendet — wird gezeigt weil aktiv'
|
||||
} else if (mode === 'all_force') {
|
||||
eyeIcon = 'visibility'
|
||||
eyeOn = true
|
||||
eyeOpacity = 0.35
|
||||
eyeTitle = 'Im „Alle anzeigen"-Mode immer sichtbar — Klick wechselt in „Ausgewählte"'
|
||||
} else if (mode === 'active') {
|
||||
eyeIcon = z.visible !== false ? 'visibility' : 'visibility_off'
|
||||
eyeOn = false
|
||||
eyeOpacity = 0.35
|
||||
eyeTitle = 'Im „Nur aktive"-Mode ausgeblendet — Klick wechselt in „Ausgewählte"'
|
||||
} else {
|
||||
eyeIcon = z.visible !== false ? 'visibility' : 'visibility_off'
|
||||
eyeOn = z.visible !== false
|
||||
eyeOpacity = 1
|
||||
eyeTitle = z.visible !== false ? 'Ausblenden' : 'Einblenden'
|
||||
}
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
@@ -21,18 +49,22 @@ function ZeichnungsebeneRow({
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
padding: '4px 12px',
|
||||
margin: active ? '1px 6px' : '0',
|
||||
background: active ? 'var(--active-dim)'
|
||||
: (z.visible !== false) ? 'var(--bg-item)'
|
||||
: 'var(--bg-panel)',
|
||||
background: active ? 'var(--active-dim)' : 'var(--bg-item)',
|
||||
borderRadius: active ? 999 : 0,
|
||||
borderLeft: active ? 'none' : '3px solid transparent',
|
||||
borderBottom: active ? 'none' : '1px solid var(--border-light)',
|
||||
boxShadow: active ? 'inset 0 0 0 1px var(--active-light)' : 'none',
|
||||
cursor: 'pointer',
|
||||
userSelect: 'none',
|
||||
opacity: (!active && z.visible === false && mode !== 'all') ? 0.45 : 1,
|
||||
}}
|
||||
>
|
||||
<button
|
||||
className={`btn-icon-sm ${eyeOn ? 'is-on' : ''}`}
|
||||
onClick={(ev) => { ev.stopPropagation(); onToggleVisible() }}
|
||||
title={eyeTitle}
|
||||
style={{ opacity: eyeOpacity }}
|
||||
><Icon name={eyeIcon} size={14} /></button>
|
||||
|
||||
<span style={{
|
||||
fontWeight: active ? 700 : 500,
|
||||
fontSize: 12,
|
||||
@@ -49,22 +81,15 @@ function ZeichnungsebeneRow({
|
||||
|
||||
{isGeschoss && <GeschossBadge name={z.name} />}
|
||||
|
||||
{isGeschoss && z.hasClipping && (
|
||||
<Icon name="content_cut" size={12} style={{ color: 'var(--accent)', flexShrink: 0 }} title="Clipping Plane aktiv" />
|
||||
)}
|
||||
|
||||
{eyeShown ? (
|
||||
{isGeschoss ? (
|
||||
<button
|
||||
className={`btn-icon-sm ${z.visible !== false ? 'is-on' : ''}`}
|
||||
onClick={(ev) => { ev.stopPropagation(); onToggleVisible() }}
|
||||
title={
|
||||
active
|
||||
? (z.visible !== false
|
||||
? 'Normalerweise sichtbar (aktive Zeichnungsebene wird trotzdem gezeigt)'
|
||||
: 'Normalerweise ausgeblendet — wird nur sichtbar weil aktiv')
|
||||
: (z.visible !== false ? 'Ausblenden' : 'Einblenden')
|
||||
}
|
||||
><Icon name={z.visible !== false ? 'visibility' : 'visibility_off'} size={14} /></button>
|
||||
className={`btn-icon-xs ${z.hasClipping ? 'is-on' : ''}`}
|
||||
onClick={(ev) => { ev.stopPropagation(); onToggleClipping() }}
|
||||
title={z.hasClipping
|
||||
? 'Clipping Plane ausschalten'
|
||||
: 'Clipping Plane einschalten (Schnitt auf Schnitthöhe)'}
|
||||
style={{ color: z.hasClipping ? 'var(--accent)' : undefined }}
|
||||
><Icon name="content_cut" size={12} /></button>
|
||||
) : (
|
||||
<span style={{ width: 18, flexShrink: 0 }} />
|
||||
)}
|
||||
@@ -86,7 +111,8 @@ function ZeichnungsebeneRow({
|
||||
}
|
||||
|
||||
const MODES = [
|
||||
{ value: 'all', label: 'Alle anzeigen' },
|
||||
{ value: 'all_force', label: 'Alle anzeigen' },
|
||||
{ value: 'all', label: 'Ausgewählte' },
|
||||
{ value: 'active', label: 'Nur aktive' },
|
||||
{ value: 'grey', label: 'Andere grau' },
|
||||
{ value: 'grey_locked', label: 'Andere grau & gesperrt' },
|
||||
@@ -119,12 +145,20 @@ export default function GeschossManager({
|
||||
|
||||
const toggleVisible = (id) => {
|
||||
onChange(zeichnungsebenen.map(z => z.id === id ? { ...z, visible: !(z.visible !== false) } : z))
|
||||
// In "active" / "all_force" greift visible-Flag nicht — wer aufs Auge
|
||||
// klickt will offensichtlich Sichtbarkeit kontrollieren, also direkt
|
||||
// in den "Ausgewählte"-Mode wechseln damit die Aktion wirkt.
|
||||
if (mode === 'active' || mode === 'all_force') onModeChange('all')
|
||||
}
|
||||
|
||||
const toggleLock = (id) => {
|
||||
onChange(zeichnungsebenen.map(z => z.id === id ? { ...z, locked: !z.locked } : z))
|
||||
}
|
||||
|
||||
const toggleClipping = (id) => {
|
||||
onChange(zeichnungsebenen.map(z => z.id === id ? { ...z, hasClipping: !z.hasClipping } : z))
|
||||
}
|
||||
|
||||
const duplicate = (id) => {
|
||||
const src = zeichnungsebenen.find(z => z.id === id)
|
||||
if (!src) return
|
||||
@@ -211,6 +245,52 @@ export default function GeschossManager({
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Master-Row: Master-Eye links + Master-Lock rechts (analog
|
||||
EbenenManager). */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 5,
|
||||
padding: '2px 14px',
|
||||
background: 'var(--bg-section)',
|
||||
borderBottom: '1px solid var(--border)',
|
||||
}}>
|
||||
<button
|
||||
className="btn-icon-xs"
|
||||
onClick={() => {
|
||||
const anyVisible = zeichnungsebenen.some(z => z.visible !== false)
|
||||
onChange(zeichnungsebenen.map(z => ({ ...z, visible: !anyVisible })))
|
||||
if (mode === 'active' || mode === 'all_force') onModeChange('all')
|
||||
}}
|
||||
title={zeichnungsebenen.every(z => z.visible !== false)
|
||||
? 'Alle Zeichnungsebenen ausblenden'
|
||||
: 'Alle Zeichnungsebenen einblenden'}
|
||||
style={{ width: 18, height: 18,
|
||||
opacity: (mode === 'active' || mode === 'all_force') ? 0.5 : 1 }}
|
||||
>
|
||||
<Icon
|
||||
name={zeichnungsebenen.every(z => z.visible !== false) ? 'visibility' : 'visibility_off'}
|
||||
size={12}
|
||||
/>
|
||||
</button>
|
||||
<span style={{ flex: 1 }} />
|
||||
<button
|
||||
className="btn-icon-xs"
|
||||
onClick={() => {
|
||||
const anyLocked = zeichnungsebenen.some(z => z.locked === true)
|
||||
onChange(zeichnungsebenen.map(z => ({ ...z, locked: !anyLocked })))
|
||||
}}
|
||||
title={zeichnungsebenen.every(z => z.locked === true)
|
||||
? 'Alle Zeichnungsebenen entsperren'
|
||||
: 'Alle Zeichnungsebenen sperren'}
|
||||
style={{ width: 18, height: 18 }}
|
||||
>
|
||||
<Icon
|
||||
name={zeichnungsebenen.every(z => z.locked === true) ? 'lock' : 'lock_open'}
|
||||
size={11}
|
||||
/>
|
||||
</button>
|
||||
<div style={{ width: 18 }} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{sorted.map(z => (
|
||||
<ZeichnungsebeneRow
|
||||
@@ -222,6 +302,7 @@ export default function GeschossManager({
|
||||
onContextMenu={(ev) => openContextMenu(ev, z.id)}
|
||||
onToggleVisible={() => toggleVisible(z.id)}
|
||||
onToggleLock={() => toggleLock(z.id)}
|
||||
onToggleClipping={() => toggleClipping(z.id)}
|
||||
onDelete={() => remove(z.id)}
|
||||
/>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user