revert macro-clips button feature (keep markers + scripts)

The button authoring ended up too far from the intended model after
three correction passes. Pulling it out entirely so we can rebuild
from a clean spec. Markers, script tracks, script frames, the Action
editor, the Script DSL and the runtime for all of the above stay —
they work.

Removed:
- types: ButtonState, StateTransition, ButtonHitArea, ButtonClip,
  ButtonInstance, setButtonState/setButtonDisabled Action variants,
  Scene.buttonClips/buttonInstances
- store: activeButtonClip* getters, all add/remove/setButton* actions,
  seedDefaultButtonClip, enterButtonEdit/setActiveButtonState/
  exitButtonEdit, button-label resolution in actionRuntime
- useButtonRuntime composable, buttonBlend helper,
  ButtonInstanceDialog, sidebar Buttons section, Timeline button
  rendering + pointer handlers, CanvasStage button rendering +
  pointer state machine, exportPlayer button runtime + hit-testing
- DSL commands state/enable/disable and their parse errors
- 35 button-related tests + helpers; stale testing doc; stale design
  doc

Re-applied three uncommitted fixes that were unrelated to the button
work:
- clicking a marker no longer jumps the playhead (only drag / dblclick
  act on it)
- script tracks get a right-click context menu (rename + delete,
  color in the existing palette)
- onTracksPointerDown excludes .script-lane / .script-frame so
  clicks on empty lanes or on frame dots reach the frame-open path
  instead of being swallowed by box-select pointer capture

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.7 committed 2026-04-20 23:37:49 +02:00
1 parent 616973b0fa
commit 31604052ca
16 files changed
+84 -2809

No files matched your search

+1 -69
View File
@@ -8,7 +8,7 @@ import {
} from '@phosphor-icons/vue'
import { useSceneStore } from '@/stores/scene'
import { t } from '@/i18n'
import type { Action, ButtonState } from '@/types/scene'
import type { Action } from '@/types/scene'
const props = defineProps<{ actions: Action[] }>()
const emit = defineEmits<{ 'update:actions': [value: Action[]] }>()
@@ -19,19 +19,6 @@ const markerNames = computed(() =>
(store.scene.markers ?? []).map((m) => m.name),
)
interface ButtonInstanceOption {
id: string
label: string
}
const buttonInstanceOptions = computed<ButtonInstanceOption[]>(() =>
(store.scene.buttonInstances ?? [])
.filter((bi) => !!bi.label)
.map((bi) => ({ id: bi.id, label: bi.label! })),
)
const BUTTON_STATES: ButtonState[] = ['normal', 'hover', 'pressed', 'disabled']
const ACTION_TYPES: Action['type'][] = [
'play',
'pause',
@@ -39,8 +26,6 @@ const ACTION_TYPES: Action['type'][] = [
'jumpTo',
'jumpToFrame',
'jumpToMarker',
'setButtonState',
'setButtonDisabled',
'script',
]
@@ -60,18 +45,6 @@ function makeDefault(type: Action['type']): Action {
return { type: 'jumpToFrame', frame: 0 }
case 'jumpToMarker':
return { type: 'jumpToMarker', name: markerNames.value[0] ?? '' }
case 'setButtonState':
return {
type: 'setButtonState',
instanceId: buttonInstanceOptions.value[0]?.id ?? '',
state: 'normal',
}
case 'setButtonDisabled':
return {
type: 'setButtonDisabled',
instanceId: buttonInstanceOptions.value[0]?.id ?? '',
disabled: true,
}
case 'script':
return { type: 'script', source: '' }
}
@@ -179,47 +152,6 @@ function onTypeChange(index: number, newType: Action['type']) {
</select>
</label>
</div>
<div v-else-if="a.type === 'setButtonState'" class="params">
<label>
{{ t('actions.param.button') }}
<select
:value="a.instanceId"
@change="setAt(i, { type: 'setButtonState', instanceId: ($event.target as HTMLSelectElement).value, state: a.state })"
>
<option v-if="!buttonInstanceOptions.length" value="" disabled>{{ t('actions.noButtons') }}</option>
<option v-for="b in buttonInstanceOptions" :key="b.id" :value="b.id">{{ b.label }}</option>
</select>
</label>
<label>
{{ t('actions.param.state') }}
<select
:value="a.state"
@change="setAt(i, { type: 'setButtonState', instanceId: a.instanceId, state: ($event.target as HTMLSelectElement).value as ButtonState })"
>
<option v-for="s in BUTTON_STATES" :key="s" :value="s">{{ s }}</option>
</select>
</label>
</div>
<div v-else-if="a.type === 'setButtonDisabled'" class="params">
<label>
{{ t('actions.param.button') }}
<select
:value="a.instanceId"
@change="setAt(i, { type: 'setButtonDisabled', instanceId: ($event.target as HTMLSelectElement).value, disabled: a.disabled })"
>
<option v-if="!buttonInstanceOptions.length" value="" disabled>{{ t('actions.noButtons') }}</option>
<option v-for="b in buttonInstanceOptions" :key="b.id" :value="b.id">{{ b.label }}</option>
</select>
</label>
<label class="bool">
<input
type="checkbox"
:checked="a.disabled"
@change="setAt(i, { type: 'setButtonDisabled', instanceId: a.instanceId, disabled: ($event.target as HTMLInputElement).checked })"
/>
{{ t('actions.param.disabled') }}
</label>
</div>
<div v-else-if="a.type === 'script'" class="params">
<label class="block">
{{ t('actions.param.source') }}