Panels poliert: Ebenenkombi in Oberleiste, Satelliten-Dialoge, Caps weg, Perf
- Ebenenkombination raus aus Ebenen-Panel, in Oberleiste-Topbar + Editor-Satellite (AusschnittLayerDialog embedded). doc.Strings haelt active_comb_name, auto-clear bei manueller Eye/Lock-Aenderung. - EbenenSettingsDialog jetzt Satellite mit Ebene-Picker-Dropdown (auto-save on switch via SAVE_KEEP). - Per-Ausschnitt Einstellungen-Satellite (Massstab, Display, Overrides, Ebenenkombi). Alte 'Sichtbarkeit bearbeiten'-Option entfernt. - Layouts/Ausschnitte: Top-Header weg, Sticky-Footer mit Anzahl + Aktionen. LayoutDialog ist jetzt Satellite mit Format-Live-Preview. - Panel-Captions + Default-Ebenen-Namen auf Mixed-Case (Ausschnitte, Ebenen, Waende ...). Nur DOSSIER bleibt caps. - DimensionenApp: Card-Optik raus, REF-Wuerfel mit Kreisen statt Quadraten + Hover-Scale. - GeschossManager angeglichen an EbenenManager: Rechtsklick-Menue, Lock-Button, Delete-X, Duplizieren. layer_builder honoriert z.locked. - Active Sublayer folgt jetzt dem Geschoss-Wechsel (gleicher Code unter neuem Parent). Performance Geschoss-Wechsel: - elemente._send_state() ersetzt durch _notify_active_geschoss() (Partial-Push statt 200+ Elements re-enumerieren). - _apply_visibility dedupe via sticky last-applied-signature (STATE_SYNC-Echo loopt nicht mehr durch alle Layer). - _update_clipping nur wenn alt oder neu hasClipping=True. - Redundante doc.Views.Redraw() im CPlane-Pfad entfernt — die folgende apply_visibility-Roundtrip redrawt 30ms spaeter ohnehin. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+25
-58
@@ -1,7 +1,6 @@
|
||||
import { useState, useEffect, useMemo } from 'react'
|
||||
import Icon from './components/Icon'
|
||||
import ContextMenu from './components/ContextMenu'
|
||||
import AusschnittLayerDialog from './components/AusschnittLayerDialog'
|
||||
import {
|
||||
onMessage, notifyReady,
|
||||
listAusschnitte, saveAusschnitt, updateAusschnitt,
|
||||
@@ -9,8 +8,7 @@ import {
|
||||
renameAusschnitt, deleteAusschnitt,
|
||||
setAusschnittFolder, setAusschnittScale,
|
||||
duplicateAusschnitt, addAusschnittFolder, removeAusschnittFolder,
|
||||
getAusschnittLayers, updateAusschnittLayers,
|
||||
saveLayerPreset, deleteLayerPreset,
|
||||
openAusschnittSettings,
|
||||
} from './lib/rhinoBridge'
|
||||
|
||||
function EditableInline({ value, onCommit, autoEdit, style, fontSize }) {
|
||||
@@ -247,22 +245,16 @@ function RootDropZone({ children, onDragOver, onDragLeave, onDrop, dragOver, emp
|
||||
export default function AusschnitteApp() {
|
||||
const [snaps, setSnaps] = useState([])
|
||||
const [extraFolders, setExtraFolders] = useState([])
|
||||
const [presets, setPresets] = useState([])
|
||||
const [newName, setNewName] = useState('')
|
||||
const [ctxMenu, setCtxMenu] = useState(null)
|
||||
const [collapsed, setCollapsed] = useState({})
|
||||
const [draggingId, setDraggingId] = useState(null)
|
||||
const [dragTarget, setDragTarget] = useState(null)
|
||||
const [layerDialog, setLayerDialog] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
onMessage('LIST', ({ snapshots, folders, presets }) => {
|
||||
onMessage('LIST', ({ snapshots, folders }) => {
|
||||
setSnaps(snapshots || [])
|
||||
setExtraFolders(folders || [])
|
||||
setPresets(presets || [])
|
||||
})
|
||||
onMessage('LAYERS_DATA', ({ id, name, layers, presets }) => {
|
||||
setLayerDialog({ id, name, layers: layers || [], presets: presets || [] })
|
||||
})
|
||||
notifyReady()
|
||||
const blockContext = (ev) => ev.preventDefault()
|
||||
@@ -301,7 +293,7 @@ export default function AusschnitteApp() {
|
||||
{ label: 'Wiederherstellen', icon: 'restore', onClick: () => restoreAusschnitt(id) },
|
||||
{ label: 'Auf Detail anwenden', icon: 'crop_landscape', onClick: () => applyAusschnittToDetail(id) },
|
||||
{ divider: true },
|
||||
{ label: 'Sichtbarkeit bearbeiten…', icon: 'layers', onClick: () => getAusschnittLayers(id) },
|
||||
{ label: 'Ausschnittseinstellungen…', icon: 'tune', onClick: () => openAusschnittSettings(id) },
|
||||
{ divider: true },
|
||||
{ label: 'Duplizieren', icon: 'content_copy', onClick: () => duplicateAusschnitt(id) },
|
||||
{ label: 'Aktualisieren', icon: 'sync', onClick: () => updateAusschnitt(id) },
|
||||
@@ -361,17 +353,6 @@ export default function AusschnitteApp() {
|
||||
/>
|
||||
)
|
||||
|
||||
const actions = (
|
||||
<div style={{ display: 'flex', gap: 4 }}>
|
||||
<button className="btn-icon-tonal" onClick={handleAddFolder} title="Neuer Ordner">
|
||||
<Icon name="create_new_folder" size={14} />
|
||||
</button>
|
||||
<button className="btn-icon-tonal" onClick={() => listAusschnitte()} title="Aktualisieren">
|
||||
<Icon name="refresh" size={14} />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
|
||||
const rootItems = groups[''] || []
|
||||
const isEmpty = snaps.length === 0 && allFolders.length === 0
|
||||
|
||||
@@ -382,21 +363,6 @@ export default function AusschnitteApp() {
|
||||
background: 'var(--bg-base)',
|
||||
position: 'relative',
|
||||
}}>
|
||||
{/* Fixed Header — wie Layouts/Overrides Pattern */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
padding: '8px 10px',
|
||||
borderBottom: '1px solid var(--border)',
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
<span style={{ flex: 1, fontWeight: 600, letterSpacing: '0.08em',
|
||||
color: 'var(--text-primary)' }}>
|
||||
AUSSCHNITTE
|
||||
</span>
|
||||
<span className="chip" style={{ fontSize: 8 }}>{snaps.length}</span>
|
||||
{actions}
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1, overflowY: 'auto', padding: 8 }}>
|
||||
{/* Save-Bar als Card */}
|
||||
<div style={{
|
||||
@@ -481,6 +447,28 @@ export default function AusschnitteApp() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sticky Footer: Anzahl + Ordner erstellen + Reload */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 8,
|
||||
padding: '8px 10px',
|
||||
borderTop: '1px solid var(--border)',
|
||||
background: 'var(--bg-panel)',
|
||||
flexShrink: 0,
|
||||
}}>
|
||||
<span className="chip" style={{
|
||||
fontSize: 9, minWidth: 22, justifyContent: 'center',
|
||||
}}>{snaps.length}</span>
|
||||
<span style={{ fontSize: 10, color: 'var(--text-muted)', flex: 1 }}>
|
||||
Ausschnitte
|
||||
</span>
|
||||
<button className="btn-icon-tonal" onClick={handleAddFolder} title="Neuer Ordner">
|
||||
<Icon name="create_new_folder" size={14} />
|
||||
</button>
|
||||
<button className="btn-icon-tonal" onClick={() => listAusschnitte()} title="Aktualisieren">
|
||||
<Icon name="refresh" size={14} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{ctxMenu && (
|
||||
<ContextMenu
|
||||
x={ctxMenu.x} y={ctxMenu.y}
|
||||
@@ -489,27 +477,6 @@ export default function AusschnitteApp() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{layerDialog && (
|
||||
<AusschnittLayerDialog
|
||||
snapName={layerDialog.name}
|
||||
layers={layerDialog.layers}
|
||||
presets={layerDialog.presets}
|
||||
onSave={(layers) => {
|
||||
updateAusschnittLayers(layerDialog.id,
|
||||
layers.map(l => ({ id: l.id, visible: l.visible, locked: l.locked })))
|
||||
setLayerDialog(null)
|
||||
}}
|
||||
onClose={() => setLayerDialog(null)}
|
||||
onSavePreset={(name, layers) => {
|
||||
saveLayerPreset(name, layers)
|
||||
setLayerDialog(d => d ? { ...d, presets: [...d.presets.filter(p => p.name !== name), { name, layers }] } : d)
|
||||
}}
|
||||
onDeletePreset={(name) => {
|
||||
deleteLayerPreset(name)
|
||||
setLayerDialog(d => d ? { ...d, presets: d.presets.filter(p => p.name !== name) } : d)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user