Overrides-Fenster aufgeräumt + Rule-Templates
UX-Cleanup:
- Globaler AN/AUS-Toggle entfernt — den gibt's bereits in der
Oberleiste, doppelt war redundant.
- Reload/Refresh-Button entfernt — Backend re-applied automatisch
bei jeder Regel-Aenderung, manuelles Reload nicht noetig.
- + (Neue Regel) wurde aus dem Header in eine neue Sektion
UNTER der Kombinationen-Card verschoben.
Neues Feature: Rule-Templates (einzelne wiederverwendbare Regeln)
- Storage: ~/Library/.../override_rule_templates.json (cross-doc,
parallel zu den Kombinationen-Presets)
- API in overrides.py: list/save/load/delete_rule_template
- Bridge-Messages: SAVE_RULE_TEMPLATE, DELETE_RULE_TEMPLATE,
ADD_FROM_TEMPLATE
- State enthaelt jetzt ruleTemplates: [{name, rule}]
UI:
- Neuer Bereich "Neue Regel" unter Kombinationen: [+ leer] +
[+ Aus Vorlage ▼ dropdown]
- Vorlage waehlen → insert auf hoechste Prio (gleich wie addRule)
- Im Dropdown unten: "🗑 <name> loeschen" zum Entfernen einer Vorlage
- Im Rule-Kontextmenue: neuer Eintrag "Als Vorlage speichern…"
fragt nach Name, speichert die Regel cross-doc
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+61
-28
@@ -6,6 +6,7 @@ import {
|
||||
setOverridesEnabled, addRule, updateRule, deleteRule,
|
||||
reorderRules, duplicateRule, reapplyOverrides, clearOverrideRules,
|
||||
savePreset, loadPreset, deletePreset,
|
||||
saveRuleTemplate, addFromTemplate, deleteRuleTemplate,
|
||||
} from './lib/rhinoBridge'
|
||||
|
||||
const COND_TYPES = [
|
||||
@@ -383,9 +384,10 @@ function RuleCard({ rule, index, total, layers, linetypes, hatchPatterns, onPatc
|
||||
export default function OverridesApp() {
|
||||
const [state, setState] = useState({
|
||||
enabled: false, rules: [], layers: [], linetypes: [], hatchPatterns: [], presets: [],
|
||||
activePreset: null,
|
||||
activePreset: null, ruleTemplates: [],
|
||||
})
|
||||
const [selectedPreset, setSelectedPreset] = useState('')
|
||||
const [selectedTemplate, setSelectedTemplate] = useState('')
|
||||
const [ctxMenu, setCtxMenu] = useState(null) // {x, y, ruleId}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -435,6 +437,14 @@ export default function OverridesApp() {
|
||||
{ label: 'Duplizieren',
|
||||
icon: 'content_copy',
|
||||
onClick: () => duplicateRule(ruleId) },
|
||||
{ label: 'Als Vorlage speichern…',
|
||||
icon: 'bookmark_add',
|
||||
onClick: () => {
|
||||
const def = rule.name || 'Vorlage'
|
||||
const name = window.prompt('Name für Vorlage:', def)
|
||||
if (!name || !name.trim()) return
|
||||
saveRuleTemplate(name.trim(), rule)
|
||||
} },
|
||||
{ divider: true },
|
||||
{ label: 'Löschen',
|
||||
icon: 'delete', danger: true,
|
||||
@@ -454,30 +464,10 @@ export default function OverridesApp() {
|
||||
background: 'var(--bg-base)',
|
||||
boxSizing: 'border-box',
|
||||
}}>
|
||||
{/* Header — globaler Toggle + Refresh + FAB neue Regel */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<button
|
||||
onClick={() => setOverridesEnabled(!state.enabled)}
|
||||
className={state.enabled ? 'btn-contained' : 'btn-outlined'}
|
||||
style={{ flex: 1 }}
|
||||
title="Overrides global an/aus"
|
||||
>
|
||||
<Icon name={state.enabled ? 'visibility' : 'visibility_off'} size={14} />
|
||||
<span>{state.enabled ? 'Overrides AN' : 'Overrides AUS'}</span>
|
||||
</button>
|
||||
<button onClick={reapplyOverrides} className="btn-icon-tonal" title="Regeln neu anwenden">
|
||||
<Icon name="refresh" size={14} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => addRule({})}
|
||||
className="btn-add"
|
||||
title="Neue Regel oben einfügen (höchste Priorität)"
|
||||
>
|
||||
<Icon name="add" size={16} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Override-Kombinationen — Dropdown plus kontextabhaengiger Save. */}
|
||||
{/* Override-Kombinationen — Dropdown plus kontextabhaengiger Save.
|
||||
Globaler AN/AUS-Toggle ist jetzt in der Oberleiste, hier ueber-
|
||||
fluessig. Reapply-Button raus: Backend re-applied automatisch
|
||||
bei jeder Aenderung. */}
|
||||
<div style={{
|
||||
display: 'flex', flexDirection: 'column', gap: 6,
|
||||
padding: 10,
|
||||
@@ -494,9 +484,6 @@ export default function OverridesApp() {
|
||||
if (v) {
|
||||
loadPreset(v, 'replace')
|
||||
} else {
|
||||
// "— neu / keine —" → Editor wirklich leeren, sonst bleiben
|
||||
// die Regeln der vorigen Kombination stehen und der User
|
||||
// baut versehentlich auf altem Stand weiter.
|
||||
clearOverrideRules()
|
||||
}
|
||||
}}
|
||||
@@ -549,6 +536,52 @@ export default function OverridesApp() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Neue Regel: leere Regel ODER aus Vorlage. */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6,
|
||||
padding: '8px 10px',
|
||||
background: 'var(--bg-section)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 'var(--r-lg)',
|
||||
}}>
|
||||
<button
|
||||
onClick={() => addRule({})}
|
||||
className="btn-add"
|
||||
title="Leere Regel oben einfuegen"
|
||||
>
|
||||
<Icon name="add" size={16} />
|
||||
</button>
|
||||
<select
|
||||
value={selectedTemplate}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
if (!v) { setSelectedTemplate(''); return }
|
||||
if (v === '__delete__') {
|
||||
if (!selectedTemplate) return
|
||||
if (!window.confirm(`Vorlage "${selectedTemplate}" dauerhaft loeschen?`)) return
|
||||
deleteRuleTemplate(selectedTemplate)
|
||||
setSelectedTemplate('')
|
||||
return
|
||||
}
|
||||
addFromTemplate(v)
|
||||
setSelectedTemplate(v)
|
||||
}}
|
||||
style={{ flex: 1, minWidth: 0 }}
|
||||
title="Regel aus Vorlage einfuegen"
|
||||
>
|
||||
<option value="">+ Aus Vorlage…</option>
|
||||
{(state.ruleTemplates || []).map(t => (
|
||||
<option key={t.name} value={t.name}>{t.name}</option>
|
||||
))}
|
||||
{selectedTemplate && (state.ruleTemplates || []).some(t => t.name === selectedTemplate) && (
|
||||
<>
|
||||
<option disabled>──────────</option>
|
||||
<option value="__delete__">🗑 "{selectedTemplate}" loeschen</option>
|
||||
</>
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style={{ fontSize: 10, color: 'var(--text-muted)', fontStyle: 'italic', lineHeight: 1.4 }}>
|
||||
Regeln sind additiv. Bei Konflikt gewinnt die <b>oberste</b>.
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user