Oberleiste: Icon roh + Pill-Dropdowns, About-Modal, Overrides/Kombi migriert
User-Feedback: Icon-Box war zu plakativ, lieber roh daneben + Pill-Form. Versionsnummer raus, Logo-Klick → About-Fenster. BarSelect umgebaut: - Icon roh links (vorher: separates Icon-Kompartiment mit Border) - Select-Container pill-foermig (border-radius 999, vorher 4) - joinedRight wird zu flachen rechten Pill-Kanten (statt rechteck) BarButton: pill-foermig + joinedLeft mit flacher linker Kante. View-Toggle: Pill-Container statt Rechteck. Massstab Live-Zoom-Chip: Pill-Form. Custom-Scale-Input: Pill-Form. Logo: - Versionsnummer-Span entfernt - ganzes Logo wird zu Klick-Button → AboutModal-State - AboutModal: zentrierter Backdrop-Dialog mit Launcher/Plugin-Versionen, Autor (Karim Gabriele Varano), Website (gabrielevarano.ch), Lizenz (Proprietaer) Overrides + Kombi migriert (war noch im alten Stack-Layout): - Overrides: BarButton (Toggle) + BarSelect (Preset) joinedRight + BarButton (Settings) joinedLeft, palette-Icon fuer Preset-Picker - Kombi: BarSelect (layers-Icon) mit allen Aktionen im Dropdown (Speichern, Loeschen, Bearbeiten) + BarButton (edit) joinedLeft - Stack-Layout (label-Spalte + 2 Reihen) ist weg, jetzt inline Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+152
-100
@@ -83,51 +83,53 @@ const pillSelect = {
|
|||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
// BarSelect: Icon-Kompartiment links | Native-Select Mitte | Caret rechts
|
// BarSelect: Icon roh links (kein Container) + pill-shaped Native-Select.
|
||||||
|
// joinedRight: rechte Pill-Kante flach, fuer Verkettung mit BarButton.
|
||||||
function BarSelect({ icon, value, onChange, title, disabled, width, children, joinedRight }) {
|
function BarSelect({ icon, value, onChange, title, disabled, width, children, joinedRight }) {
|
||||||
return (
|
return (
|
||||||
<div title={title} style={{
|
<div title={title} style={{
|
||||||
display: 'inline-flex', alignItems: 'center',
|
display: 'inline-flex', alignItems: 'center', gap: 5,
|
||||||
|
opacity: disabled ? 0.5 : 1, flexShrink: 0,
|
||||||
|
}}>
|
||||||
|
{icon && (
|
||||||
|
<Icon name={icon} size={13}
|
||||||
|
style={{ color: 'var(--text-muted)', flexShrink: 0 }} />
|
||||||
|
)}
|
||||||
|
<div style={{
|
||||||
|
position: 'relative',
|
||||||
height: BAR_H, width,
|
height: BAR_H, width,
|
||||||
background: 'var(--bg-input)',
|
background: 'var(--bg-input)',
|
||||||
border: '1px solid var(--border-light)',
|
border: '1px solid var(--border-light)',
|
||||||
borderRadius: joinedRight ? '4px 0 0 4px' : 4,
|
borderTopLeftRadius: 999, borderBottomLeftRadius: 999,
|
||||||
|
borderTopRightRadius: joinedRight ? 0 : 999,
|
||||||
|
borderBottomRightRadius: joinedRight ? 0 : 999,
|
||||||
borderRight: joinedRight ? 'none' : '1px solid var(--border-light)',
|
borderRight: joinedRight ? 'none' : '1px solid var(--border-light)',
|
||||||
overflow: 'hidden', position: 'relative',
|
overflow: 'hidden', flexShrink: 0,
|
||||||
opacity: disabled ? 0.5 : 1, flexShrink: 0,
|
|
||||||
}}>
|
}}>
|
||||||
<div style={{
|
|
||||||
width: 26, height: '100%',
|
|
||||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
||||||
background: 'var(--bg-item)',
|
|
||||||
borderRight: '1px solid var(--border-light)',
|
|
||||||
flexShrink: 0,
|
|
||||||
}}>
|
|
||||||
<Icon name={icon} size={13} style={{ color: 'var(--text-muted)' }} />
|
|
||||||
</div>
|
|
||||||
<select
|
<select
|
||||||
value={value || ''}
|
value={value || ''}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => onChange(e.target.value)}
|
||||||
style={{
|
style={{
|
||||||
flex: 1, height: '100%', minWidth: 0,
|
width: '100%', height: '100%', minWidth: 0,
|
||||||
background: 'transparent', border: 'none', outline: 'none',
|
background: 'transparent', border: 'none', outline: 'none',
|
||||||
padding: '0 18px 0 8px',
|
padding: '0 22px 0 12px',
|
||||||
fontSize: 11, color: 'var(--text-primary)',
|
fontSize: 11, color: 'var(--text-primary)',
|
||||||
appearance: 'none', WebkitAppearance: 'none', MozAppearance: 'none',
|
appearance: 'none', WebkitAppearance: 'none', MozAppearance: 'none',
|
||||||
cursor: disabled ? 'not-allowed' : 'pointer',
|
cursor: disabled ? 'not-allowed' : 'pointer',
|
||||||
}}
|
}}
|
||||||
>{children}</select>
|
>{children}</select>
|
||||||
<Icon name="arrow_drop_down" size={14}
|
<Icon name="arrow_drop_down" size={14}
|
||||||
style={{ position: 'absolute', right: 4, top: '50%',
|
style={{ position: 'absolute', right: 6, top: '50%',
|
||||||
transform: 'translateY(-50%)',
|
transform: 'translateY(-50%)',
|
||||||
color: 'var(--text-muted)', pointerEvents: 'none' }} />
|
color: 'var(--text-muted)', pointerEvents: 'none' }} />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BarButton: quadratischer Icon-Button, gleiche Hoehe wie BarSelect.
|
// BarButton: pill-foermiger Icon-Button. joinedLeft = linke Kante flach,
|
||||||
// joinedLeft: wenn rechts von einem BarSelect sitzt (kein doppelter Border).
|
// so dass er nahtlos an einen BarSelect-joinedRight andockt.
|
||||||
function BarButton({ icon, onClick, title, disabled, joinedLeft, active }) {
|
function BarButton({ icon, onClick, title, disabled, joinedLeft, active }) {
|
||||||
return (
|
return (
|
||||||
<button onClick={onClick} disabled={disabled} title={title}
|
<button onClick={onClick} disabled={disabled} title={title}
|
||||||
@@ -135,7 +137,9 @@ function BarButton({ icon, onClick, title, disabled, joinedLeft, active }) {
|
|||||||
height: BAR_H, width: BAR_H,
|
height: BAR_H, width: BAR_H,
|
||||||
background: active ? 'var(--accent)' : 'var(--bg-input)',
|
background: active ? 'var(--accent)' : 'var(--bg-input)',
|
||||||
border: '1px solid var(--border-light)',
|
border: '1px solid var(--border-light)',
|
||||||
borderRadius: joinedLeft ? '0 4px 4px 0' : 4,
|
borderTopLeftRadius: joinedLeft ? 0 : 999,
|
||||||
|
borderBottomLeftRadius: joinedLeft ? 0 : 999,
|
||||||
|
borderTopRightRadius: 999, borderBottomRightRadius: 999,
|
||||||
borderLeft: joinedLeft ? 'none' : '1px solid var(--border-light)',
|
borderLeft: joinedLeft ? 'none' : '1px solid var(--border-light)',
|
||||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
cursor: disabled ? 'not-allowed' : 'pointer',
|
cursor: disabled ? 'not-allowed' : 'pointer',
|
||||||
@@ -204,6 +208,7 @@ export default function OberleisteApp() {
|
|||||||
const [draft, setDraft] = useState('')
|
const [draft, setDraft] = useState('')
|
||||||
const [customMode, setCustomMode] = useState(false) // Dropdown -> Custom-Input switch
|
const [customMode, setCustomMode] = useState(false) // Dropdown -> Custom-Input switch
|
||||||
const customInputRef = useRef(null)
|
const customInputRef = useRef(null)
|
||||||
|
const [aboutOpen, setAboutOpen] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onMessage('STATE', (s) => {
|
onMessage('STATE', (s) => {
|
||||||
@@ -291,13 +296,16 @@ export default function OberleisteApp() {
|
|||||||
overflowX: 'auto', overflowY: 'hidden',
|
overflowX: 'auto', overflowY: 'hidden',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
}}>
|
}}>
|
||||||
{/* Logo: DOSSIER. (Petrol-Punkt) + Launcher-Version */}
|
{/* Logo: DOSSIER. (Petrol-Punkt) — Klick = About-Fenster */}
|
||||||
<div
|
<button
|
||||||
|
onClick={() => setAboutOpen(true)}
|
||||||
|
title="Über Dossier"
|
||||||
style={{
|
style={{
|
||||||
display: 'flex', alignItems: 'baseline', gap: 8,
|
display: 'flex', alignItems: 'baseline', gap: 8,
|
||||||
flexShrink: 0, userSelect: 'none',
|
flexShrink: 0, userSelect: 'none',
|
||||||
|
background: 'transparent', border: 'none', padding: 0,
|
||||||
|
cursor: 'pointer', color: 'inherit',
|
||||||
}}
|
}}
|
||||||
title={`Dossier ${__LAUNCHER_VERSION__} (Plugin ${__APP_VERSION__}) — Teil von OpenStudio`}
|
|
||||||
>
|
>
|
||||||
<span style={{
|
<span style={{
|
||||||
fontFamily: "Krungthep, 'Archivo Black', sans-serif",
|
fontFamily: "Krungthep, 'Archivo Black', sans-serif",
|
||||||
@@ -308,16 +316,7 @@ export default function OberleisteApp() {
|
|||||||
}}>
|
}}>
|
||||||
DOSSIER<span style={{ color: 'var(--accent)' }}>.</span>
|
DOSSIER<span style={{ color: 'var(--accent)' }}>.</span>
|
||||||
</span>
|
</span>
|
||||||
<span style={{
|
</button>
|
||||||
fontFamily: 'DM Mono, monospace',
|
|
||||||
fontSize: 9,
|
|
||||||
letterSpacing: '0.14em',
|
|
||||||
color: 'var(--text-muted)',
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
}}>
|
|
||||||
v{__LAUNCHER_VERSION__}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => openDossierSettings()}
|
onClick={() => openDossierSettings()}
|
||||||
title="Dossier-Einstellungen"
|
title="Dossier-Einstellungen"
|
||||||
@@ -333,9 +332,9 @@ export default function OberleisteApp() {
|
|||||||
</button>
|
</button>
|
||||||
<div style={sep} />
|
<div style={sep} />
|
||||||
{/* ====== VIEW (Top/Front/Right/Iso/Persp + Kamera) ======
|
{/* ====== VIEW (Top/Front/Right/Iso/Persp + Kamera) ======
|
||||||
Kein Group-Label — die Buttons selber kommunizieren ihren Zweck. */}
|
Pill-foermige Toggle-Gruppe, kein Group-Label. */}
|
||||||
<div style={{ display: 'inline-flex', gap: 0,
|
<div style={{ display: 'inline-flex', gap: 0,
|
||||||
border: '1px solid var(--border-light)', borderRadius: 4,
|
border: '1px solid var(--border-light)', borderRadius: 999,
|
||||||
overflow: 'hidden', flexShrink: 0 }}>
|
overflow: 'hidden', flexShrink: 0 }}>
|
||||||
{VIEWS.map((v, idx) => (
|
{VIEWS.map((v, idx) => (
|
||||||
<button
|
<button
|
||||||
@@ -343,7 +342,7 @@ export default function OberleisteApp() {
|
|||||||
onClick={() => setView(v.value)}
|
onClick={() => setView(v.value)}
|
||||||
title={`Ansicht ${v.label}`}
|
title={`Ansicht ${v.label}`}
|
||||||
style={{
|
style={{
|
||||||
height: BAR_H, padding: '0 8px',
|
height: BAR_H, padding: '0 10px',
|
||||||
background: matchView(v.value) ? 'var(--accent)' : 'var(--bg-input)',
|
background: matchView(v.value) ? 'var(--accent)' : 'var(--bg-input)',
|
||||||
color: matchView(v.value) ? 'var(--bg-panel)' : 'var(--text-primary)',
|
color: matchView(v.value) ? 'var(--bg-panel)' : 'var(--text-primary)',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
@@ -399,14 +398,14 @@ export default function OberleisteApp() {
|
|||||||
<div style={sep} />
|
<div style={sep} />
|
||||||
|
|
||||||
{/* ====== MASSSTAB ====== */}
|
{/* ====== MASSSTAB ====== */}
|
||||||
{/* Live-Zoom Chip mit Mass-Icon */}
|
{/* Live-Zoom Chip — pill, accent wenn Massstab anwendbar */}
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'inline-flex', alignItems: 'center',
|
display: 'inline-flex', alignItems: 'center',
|
||||||
height: BAR_H, padding: '0 10px',
|
height: BAR_H, padding: '0 12px',
|
||||||
background: isPerspective ? 'var(--bg-input)' : 'var(--accent)',
|
background: isPerspective ? 'var(--bg-input)' : 'var(--accent)',
|
||||||
color: isPerspective ? 'var(--text-muted)' : 'var(--bg-panel)',
|
color: isPerspective ? 'var(--text-muted)' : 'var(--bg-panel)',
|
||||||
border: '1px solid var(--border-light)',
|
border: '1px solid var(--border-light)',
|
||||||
borderRadius: 4,
|
borderRadius: 999,
|
||||||
fontFamily: 'DM Mono, monospace', fontSize: 11, fontWeight: 600,
|
fontFamily: 'DM Mono, monospace', fontSize: 11, fontWeight: 600,
|
||||||
minWidth: 64, justifyContent: 'center', flexShrink: 0,
|
minWidth: 64, justifyContent: 'center', flexShrink: 0,
|
||||||
}} title="Live-Zoom">
|
}} title="Live-Zoom">
|
||||||
@@ -428,8 +427,8 @@ export default function OberleisteApp() {
|
|||||||
height: BAR_H, width: 100,
|
height: BAR_H, width: 100,
|
||||||
background: 'var(--bg-input)',
|
background: 'var(--bg-input)',
|
||||||
border: '1px solid var(--border-light)',
|
border: '1px solid var(--border-light)',
|
||||||
borderRadius: 4,
|
borderRadius: 999,
|
||||||
padding: '0 8px', fontSize: 11,
|
padding: '0 12px', fontSize: 11,
|
||||||
fontFamily: 'DM Mono, monospace',
|
fontFamily: 'DM Mono, monospace',
|
||||||
color: 'var(--text-primary)', outline: 'none',
|
color: 'var(--text-primary)', outline: 'none',
|
||||||
}}
|
}}
|
||||||
@@ -478,39 +477,27 @@ export default function OberleisteApp() {
|
|||||||
{/* ====== STACK: Overrides + Kombi uebereinander ======
|
{/* ====== STACK: Overrides + Kombi uebereinander ======
|
||||||
Beide Zeilen haben identisches Spalten-Layout (Label-Spalte fix,
|
Beide Zeilen haben identisches Spalten-Layout (Label-Spalte fix,
|
||||||
Dropdown gleich breit), damit Dropdowns vertikal aligned sind. */}
|
Dropdown gleich breit), damit Dropdowns vertikal aligned sind. */}
|
||||||
{(() => {
|
{/* ====== OVERRIDES ====== */}
|
||||||
const STACK_LABEL_W = 60 // gleich breit fuer beide Zeilen
|
<BarButton
|
||||||
const STACK_DROPDOWN_W = 150
|
|
||||||
const stackLabel = { ...groupLabel, width: STACK_LABEL_W,
|
|
||||||
padding: 0, textAlign: 'left' }
|
|
||||||
return (
|
|
||||||
<div style={{
|
|
||||||
display: 'flex', flexDirection: 'column', gap: 4,
|
|
||||||
flexShrink: 0,
|
|
||||||
}}>
|
|
||||||
{/* Overrides */}
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
||||||
<span style={stackLabel}>Overrides</span>
|
|
||||||
<ToolButton
|
|
||||||
onClick={() => toggleOverrides(!state.overridesEnabled)}
|
|
||||||
active={state.overridesEnabled}
|
|
||||||
icon="auto_fix_high"
|
icon="auto_fix_high"
|
||||||
label={state.overridesEnabled ? 'AN' : 'AUS'}
|
active={state.overridesEnabled}
|
||||||
|
onClick={() => toggleOverrides(!state.overridesEnabled)}
|
||||||
title={state.overridesEnabled
|
title={state.overridesEnabled
|
||||||
? `Grafische Overrides aktiv — klick zum Ausschalten`
|
? 'Grafische Overrides aktiv — klick zum Ausschalten'
|
||||||
: `Grafische Overrides ausgeschaltet`}
|
: 'Grafische Overrides ausgeschaltet'}
|
||||||
/>
|
/>
|
||||||
<select
|
<BarSelect
|
||||||
|
icon="palette"
|
||||||
value={state.overridesActivePreset || '__none__'}
|
value={state.overridesActivePreset || '__none__'}
|
||||||
onChange={(e) => {
|
onChange={(v) => {
|
||||||
const v = e.target.value
|
|
||||||
if (v === '__configure__') { openOverridesPanel(); return }
|
if (v === '__configure__') { openOverridesPanel(); return }
|
||||||
setOverridesPreset(v === '__none__' ? null : v)
|
setOverridesPreset(v === '__none__' ? null : v)
|
||||||
}}
|
}}
|
||||||
style={{ ...pillSelect, width: STACK_DROPDOWN_W }}
|
|
||||||
title={state.overridesActivePreset
|
title={state.overridesActivePreset
|
||||||
? `Aktives Preset: ${state.overridesActivePreset} (${state.overridesCount} Regeln)`
|
? `Aktives Preset: ${state.overridesActivePreset} (${state.overridesCount} Regeln)`
|
||||||
: `Kein Preset aktiv (${state.overridesCount} Regeln, frei editiert)`}
|
: `Kein Preset aktiv (${state.overridesCount} Regeln, frei editiert)`}
|
||||||
|
width={140}
|
||||||
|
joinedRight
|
||||||
>
|
>
|
||||||
<option value="__none__">{state.overridesCount > 0 ? `— (${state.overridesCount} Regeln)` : '—'}</option>
|
<option value="__none__">{state.overridesCount > 0 ? `— (${state.overridesCount} Regeln)` : '—'}</option>
|
||||||
{(state.overridesPresets || []).map(name => (
|
{(state.overridesPresets || []).map(name => (
|
||||||
@@ -518,18 +505,19 @@ export default function OberleisteApp() {
|
|||||||
))}
|
))}
|
||||||
<option disabled>──────────</option>
|
<option disabled>──────────</option>
|
||||||
<option value="__configure__">Konfigurieren…</option>
|
<option value="__configure__">Konfigurieren…</option>
|
||||||
</select>
|
</BarSelect>
|
||||||
<ToolButton
|
<BarButton icon="settings" onClick={openOverridesPanel}
|
||||||
onClick={openOverridesPanel}
|
title="Overrides-Regel-Editor öffnen" joinedLeft />
|
||||||
icon="settings"
|
|
||||||
title="Overrides-Regel-Editor öffnen"
|
<div style={sep} />
|
||||||
/>
|
|
||||||
</div>
|
{/* ====== EBENEN-KOMBINATIONEN ====== */}
|
||||||
{/* Kombi */}
|
<BarSelect
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
icon="layers"
|
||||||
<span style={stackLabel}>Kombi</span>
|
value={state.layerCombinationActive || '__none__'}
|
||||||
<ToolButton
|
onChange={(v) => {
|
||||||
onClick={() => {
|
if (v === '__configure__') { openLayerCombinationsDialog(); return }
|
||||||
|
if (v === '__save__') {
|
||||||
const suggested = state.layerCombinationActive
|
const suggested = state.layerCombinationActive
|
||||||
|| `Kombi ${(state.layerCombinations || []).length + 1}`
|
|| `Kombi ${(state.layerCombinations || []).length + 1}`
|
||||||
const name = (window.prompt('Name für Ebenenkombination:', suggested) || '').trim()
|
const name = (window.prompt('Name für Ebenenkombination:', suggested) || '').trim()
|
||||||
@@ -537,15 +525,8 @@ export default function OberleisteApp() {
|
|||||||
if ((state.layerCombinations || []).includes(name) &&
|
if ((state.layerCombinations || []).includes(name) &&
|
||||||
!window.confirm(`"${name}" überschreiben?`)) return
|
!window.confirm(`"${name}" überschreiben?`)) return
|
||||||
saveLayerCombination(name)
|
saveLayerCombination(name)
|
||||||
}}
|
return
|
||||||
icon="add"
|
}
|
||||||
title="Aktuelle Sichtbarkeit als neue Kombination speichern"
|
|
||||||
/>
|
|
||||||
<select
|
|
||||||
value={state.layerCombinationActive || '__none__'}
|
|
||||||
onChange={(e) => {
|
|
||||||
const v = e.target.value
|
|
||||||
if (v === '__configure__') { openLayerCombinationsDialog(); return }
|
|
||||||
if (v === '__delete__') {
|
if (v === '__delete__') {
|
||||||
if (state.layerCombinationActive &&
|
if (state.layerCombinationActive &&
|
||||||
window.confirm(`Kombination "${state.layerCombinationActive}" löschen?`))
|
window.confirm(`Kombination "${state.layerCombinationActive}" löschen?`))
|
||||||
@@ -554,37 +535,108 @@ export default function OberleisteApp() {
|
|||||||
}
|
}
|
||||||
pickLayerCombination(v === '__none__' ? null : v)
|
pickLayerCombination(v === '__none__' ? null : v)
|
||||||
}}
|
}}
|
||||||
style={{ ...pillSelect, width: STACK_DROPDOWN_W }}
|
|
||||||
title={state.layerCombinationActive
|
title={state.layerCombinationActive
|
||||||
? `Aktive Kombi: ${state.layerCombinationActive}`
|
? `Aktive Kombi: ${state.layerCombinationActive}`
|
||||||
: 'Keine Kombination — manuelle Sichtbarkeit'}
|
: 'Keine Kombination — manuelle Sichtbarkeit'}
|
||||||
|
width={150}
|
||||||
|
joinedRight
|
||||||
>
|
>
|
||||||
<option value="__none__">— Eigene —</option>
|
<option value="__none__">— Eigene —</option>
|
||||||
{(state.layerCombinations || []).map(name => (
|
{(state.layerCombinations || []).map(name => (
|
||||||
<option key={name} value={name}>{name}</option>
|
<option key={name} value={name}>{name}</option>
|
||||||
))}
|
))}
|
||||||
|
<option disabled>──────────</option>
|
||||||
|
<option value="__save__">+ Aktuelle speichern…</option>
|
||||||
{state.layerCombinationActive && (
|
{state.layerCombinationActive && (
|
||||||
<>
|
|
||||||
<option disabled>──────────</option>
|
|
||||||
<option value="__delete__">🗑 Aktuelle löschen</option>
|
<option value="__delete__">🗑 Aktuelle löschen</option>
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
<option disabled>──────────</option>
|
|
||||||
<option value="__configure__">Bearbeiten…</option>
|
<option value="__configure__">Bearbeiten…</option>
|
||||||
</select>
|
</BarSelect>
|
||||||
<ToolButton
|
<BarButton icon="edit" onClick={openLayerCombinationsDialog}
|
||||||
onClick={openLayerCombinationsDialog}
|
title="Ebenenkombinationen bearbeiten" joinedLeft />
|
||||||
icon="edit"
|
|
||||||
title="Ebenenkombinationen bearbeiten"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})()}
|
|
||||||
|
|
||||||
{/* Spacer am rechten Rand */}
|
{/* Spacer am rechten Rand */}
|
||||||
<div style={{ flex: 1 }} />
|
<div style={{ flex: 1 }} />
|
||||||
</div>
|
</div>
|
||||||
|
{aboutOpen && (
|
||||||
|
<AboutModal onClose={() => setAboutOpen(false)} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function AboutModal({ onClose }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onClick={onClose}
|
||||||
|
style={{
|
||||||
|
position: 'fixed', inset: 0, zIndex: 1000,
|
||||||
|
background: 'rgba(0, 0, 0, 0.6)',
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
style={{
|
||||||
|
minWidth: 360, maxWidth: 420,
|
||||||
|
background: 'var(--bg-panel)',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
borderRadius: 8,
|
||||||
|
padding: '24px 28px',
|
||||||
|
boxShadow: '0 12px 48px rgba(0, 0, 0, 0.5)',
|
||||||
|
color: 'var(--text-primary)',
|
||||||
|
fontFamily: 'var(--font)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'baseline', gap: 8,
|
||||||
|
marginBottom: 4 }}>
|
||||||
|
<span style={{
|
||||||
|
fontFamily: "Krungthep, 'Archivo Black', sans-serif",
|
||||||
|
fontSize: 28, letterSpacing: '-0.02em', lineHeight: 1,
|
||||||
|
}}>
|
||||||
|
DOSSIER<span style={{ color: 'var(--accent)' }}>.</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div style={{ fontSize: 11, color: 'var(--text-muted)',
|
||||||
|
letterSpacing: '0.06em', textTransform: 'uppercase',
|
||||||
|
marginBottom: 18 }}>
|
||||||
|
Teil von OpenStudio
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: 'auto 1fr',
|
||||||
|
gap: '6px 14px', fontSize: 11, marginBottom: 18 }}>
|
||||||
|
<span style={{ color: 'var(--text-muted)' }}>Launcher</span>
|
||||||
|
<span style={{ fontFamily: 'DM Mono, monospace' }}>v{__LAUNCHER_VERSION__}</span>
|
||||||
|
<span style={{ color: 'var(--text-muted)' }}>Plugin</span>
|
||||||
|
<span style={{ fontFamily: 'DM Mono, monospace' }}>v{__APP_VERSION__}</span>
|
||||||
|
<span style={{ color: 'var(--text-muted)' }}>Autor</span>
|
||||||
|
<span>Karim Gabriele Varano</span>
|
||||||
|
<span style={{ color: 'var(--text-muted)' }}>Website</span>
|
||||||
|
<a href="https://gabrielevarano.ch" target="_blank" rel="noreferrer"
|
||||||
|
style={{ color: 'var(--accent)', textDecoration: 'none' }}>
|
||||||
|
gabrielevarano.ch
|
||||||
|
</a>
|
||||||
|
<span style={{ color: 'var(--text-muted)' }}>Lizenz</span>
|
||||||
|
<span>Proprietär — © 2026 Karim Gabriele Varano</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ fontSize: 10, color: 'var(--text-muted)',
|
||||||
|
lineHeight: 1.5, marginBottom: 18,
|
||||||
|
paddingTop: 12,
|
||||||
|
borderTop: '1px solid var(--border-light)' }}>
|
||||||
|
Rhino 8 Plugin für architektonische Workflows — Wände, Decken,
|
||||||
|
Öffnungen, Räume, SIA 416, Plan-Layouts. Schwester-App: Rapport.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="btn-contained"
|
||||||
|
style={{ padding: '6px 18px', fontSize: 11 }}
|
||||||
|
>Schliessen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user