Apply/revert buttons for overrides, container instances and child positions

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Opus 5 committed 2026-09-07 17:59:35 +02:00
1 parent 0b78382bcc
commit 3379232e64
7 files changed
+544 -37

No files matched your search

+27 -8
View File
@@ -22,12 +22,21 @@ export interface FieldRow {
export const FIELD_LABEL_WIDTH = 64;
/** One way of writing a changed value back to its source ("Apply to asset", "Apply to container"). */
export interface ApplyAction {
label: string;
onSelect: () => void;
}
/**
* Maps one PropertySchema entry to a labelled row containing the matching widget.
* The inspector is entirely generated through this function: adding a new property
* kind means adding a case here, adding a property means adding a schema line.
*
* A modified row shows its actions at the right: ↺ resets to the base value, ↑ applies the
* value to its source – directly with one action, via a menu (↑ ▾) with several.
*/
export function createFieldRow(def: PropertyDef, value: PropertyValue, handlers: FieldHandlers, onReset?: () => void): FieldRow {
export function createFieldRow(def: PropertyDef, value: PropertyValue, handlers: FieldHandlers, onReset?: () => void, apply?: ApplyAction[]): FieldRow {
const row = new HStack({ gap: Theme.gap, align: 'center' });
row.minHeight = Theme.rowHeight;
const label = new Label(def.label, { style: 'muted' });
@@ -38,11 +47,21 @@ export function createFieldRow(def: PropertyDef, value: PropertyValue, handlers:
row.add(field.widget, field.grow ? 1 : 0);
if (!field.grow) row.add(new Label(''), 1);
let reset: Button | null = null;
if (onReset) {
reset = new Button('↺', { variant: 'ghost', width: Theme.controlHeight, onClick: onReset });
reset.visible = false;
row.add(reset);
const actions: Button[] = [];
if (apply && apply.length > 0) {
const single = apply.length === 1;
const button = new Button(single ? '↑' : '↑ ▾', { variant: 'ghost', width: single ? Theme.controlHeight : undefined });
button.onClick = () => {
if (single) return apply[0]!.onSelect();
const g = button.getGlobalPosition();
new Menu(apply.map((a) => ({ label: a.label, onSelect: a.onSelect }))).open(g.x, g.y + button.h + 2);
};
actions.push(button);
}
if (onReset) actions.push(new Button('↺', { variant: 'ghost', width: Theme.controlHeight, onClick: onReset }));
for (const button of actions) {
button.visible = false;
row.add(button);
}
return {
@@ -50,8 +69,8 @@ export function createFieldRow(def: PropertyDef, value: PropertyValue, handlers:
setValue: field.setValue,
setModified(modified) {
label.setStyle(modified ? 'bodyBold' : 'muted');
if (reset && reset.visible !== modified) {
reset.visible = modified;
if (actions.length > 0 && actions[0]!.visible !== modified) {
for (const button of actions) button.visible = modified;
row.relayout();
}
},