From 94ebbac238756c61ddb079ac9a67adeda230fc64 Mon Sep 17 00:00:00 2001 From: th Date: Mon, 20 Apr 2026 21:16:25 +0200 Subject: [PATCH] macro-clips slice 4b: button instance dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ButtonInstanceDialog.vue edits the three instance-scope fields: label (with uniqueness error surface), disabled flag, onClick actions via the shared ActionEditor - commit waits on a successful label set so collisions show inline instead of silently dropping the rename - Timeline wires click on a button-instance block to open the dialog Creation/placement UX (sidebar entry + drag-to-track) and the per-state layer editor are still deferred — this slice only makes existing instances editable once something (devtools, later slice) has put them on a clip track. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/ButtonInstanceDialog.vue | 218 ++++++++++++++++++++++++ src/components/Timeline.vue | 14 ++ src/i18n/messages.ts | 11 ++ 3 files changed, 243 insertions(+) create mode 100644 src/components/ButtonInstanceDialog.vue diff --git a/src/components/ButtonInstanceDialog.vue b/src/components/ButtonInstanceDialog.vue new file mode 100644 index 0000000..7e121ce --- /dev/null +++ b/src/components/ButtonInstanceDialog.vue @@ -0,0 +1,218 @@ + + + + + diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue index 43f5f7c..23b53a4 100644 --- a/src/components/Timeline.vue +++ b/src/components/Timeline.vue @@ -31,6 +31,7 @@ import { sampleLayer } from '@/lib/interpolate' import { snapToCandidates } from '@/lib/timelineSnap' import { confirmDialog, promptDialog } from '@/components/ui' import ScriptFrameDialog from './ScriptFrameDialog.vue' +import ButtonInstanceDialog from './ButtonInstanceDialog.vue' import { t } from '@/i18n' import { kfSelectionKey, useKfSelection, type KfSelectionKey } from '@/composables/useKfSelection' import { showLocked } from '@/composables/useShowLocked' @@ -1484,6 +1485,14 @@ const BUTTON_STATES = ['normal', 'hover', 'pressed', 'disabled'] as const const scriptDialogOpen = ref(false) const scriptDialogFrameId = ref(null) +// Button instance dialog state +const buttonDialogOpen = ref(false) +const buttonDialogInstanceId = ref(null) +function openButtonInstance(id: string) { + buttonDialogInstanceId.value = id + buttonDialogOpen.value = true +} + function openScriptFrame(frameId: string) { scriptDialogFrameId.value = frameId scriptDialogOpen.value = true @@ -2274,6 +2283,7 @@ onBeforeUnmount(() => { :class="{ disabled: bi.disabled }" :style="buttonInstanceStyle(bi)" :title="bi.label ?? buttonClipById.get(bi.buttonClipId)?.name ?? ''" + @click.stop="openButtonInstance(bi.id)" > { v-model="scriptDialogOpen" :frame-id="scriptDialogFrameId" /> + diff --git a/src/i18n/messages.ts b/src/i18n/messages.ts index 5709613..64deedc 100644 --- a/src/i18n/messages.ts +++ b/src/i18n/messages.ts @@ -396,6 +396,17 @@ export const en = { remove: 'Delete frame', done: 'Done', }, + buttonInstanceDialog: { + title: 'Button instance', + clipName: 'Button', + label: 'Label', + labelPlaceholder: 'e.g. playButton', + labelCollision: 'Label "{name}" is already used by another button instance.', + disabled: 'Start disabled', + onClick: 'On click', + remove: 'Delete instance', + done: 'Done', + }, } as const export type Messages = typeof en