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:
1 parent
616973b0fa
commit
31604052ca
16 files changed
+84
-2809
No files matched your search
+6
-172
@@ -4,8 +4,6 @@ import type {
|
||||
AnimatableProps,
|
||||
Asset,
|
||||
BackgroundVideo,
|
||||
ButtonClip,
|
||||
ButtonInstance,
|
||||
Clip,
|
||||
ClipInstance,
|
||||
ClipTrack,
|
||||
@@ -27,10 +25,6 @@ import {
|
||||
scriptFramesInRange,
|
||||
type ActionRuntime,
|
||||
} from '@/lib/actions'
|
||||
import {
|
||||
applyButtonStateChange,
|
||||
useButtonRuntime,
|
||||
} from '@/composables/useButtonRuntime'
|
||||
|
||||
const uid = () => Math.random().toString(36).slice(2, 10)
|
||||
|
||||
@@ -128,8 +122,6 @@ function normalizeScene(scene: Scene): Scene {
|
||||
scene.clipTracks ||= []
|
||||
scene.clipInstances ||= []
|
||||
scene.groups ||= []
|
||||
scene.buttonClips ||= []
|
||||
scene.buttonInstances ||= []
|
||||
scene.scriptTracks ||= []
|
||||
scene.scriptFrames ||= []
|
||||
scene.markers ||= []
|
||||
@@ -566,6 +558,12 @@ export const useSceneStore = defineStore('scene', {
|
||||
else clipTrack.color = color
|
||||
return
|
||||
}
|
||||
const scriptTrack = this.scene.scriptTracks?.find((t) => t.id === trackId)
|
||||
if (scriptTrack) {
|
||||
if (color === null) delete scriptTrack.color
|
||||
else scriptTrack.color = color
|
||||
return
|
||||
}
|
||||
const group = this.scene.groups?.find((g) => g.id === trackId)
|
||||
if (group) {
|
||||
if (color === null) delete group.color
|
||||
@@ -1138,139 +1136,6 @@ export const useSceneStore = defineStore('scene', {
|
||||
this.scene.scriptFrames?.sort((a, b) => a.time - b.time)
|
||||
},
|
||||
|
||||
// -------- Button clips & instances ---------------------------------
|
||||
|
||||
addButtonClip(name?: string): ButtonClip {
|
||||
const clip: ButtonClip = {
|
||||
id: uid(),
|
||||
name: name?.trim() || 'Button',
|
||||
states: { normal: [], hover: [], pressed: [], disabled: [] },
|
||||
}
|
||||
;(this.scene.buttonClips ||= []).push(clip)
|
||||
return clip
|
||||
},
|
||||
/**
|
||||
* Create a ButtonClip pre-populated with a visible rectangle for each
|
||||
* of the four states (different shades per state). The single shared
|
||||
* asset is added to the scene. Intended for the "+ Button" sidebar
|
||||
* flow — advanced users can still edit or replace the layers later.
|
||||
*/
|
||||
seedDefaultButtonClip(name?: string): ButtonClip {
|
||||
const title = name?.trim() || 'Button'
|
||||
const clip = this.addButtonClip(title)
|
||||
const asset = this.addAsset({
|
||||
kind: 'rect',
|
||||
name: `${title} face`,
|
||||
width: 120,
|
||||
height: 40,
|
||||
fill: '#3b82f6',
|
||||
}) as Extract<Asset, { kind: 'rect' }>
|
||||
const makeFace = (fill: string, layerName: string): Layer => ({
|
||||
id: uid(),
|
||||
name: layerName,
|
||||
assetId: asset.id,
|
||||
visible: true,
|
||||
base: { ...DEFAULT_BASE_PROPS, fill },
|
||||
keyframes: [
|
||||
{
|
||||
id: uid(),
|
||||
time: 0,
|
||||
props: { ...DEFAULT_BASE_PROPS, fill },
|
||||
easing: 'linear',
|
||||
},
|
||||
],
|
||||
offsetX: asset.width / 2,
|
||||
offsetY: asset.height / 2,
|
||||
})
|
||||
const target = this.scene.buttonClips?.find((c) => c.id === clip.id)
|
||||
if (target) {
|
||||
target.states = {
|
||||
normal: [makeFace('#3b82f6', 'Face')],
|
||||
hover: [makeFace('#60a5fa', 'Face')],
|
||||
pressed: [makeFace('#2563eb', 'Face')],
|
||||
disabled: [makeFace('#9ca3af', 'Face')],
|
||||
}
|
||||
}
|
||||
return target ?? clip
|
||||
},
|
||||
removeButtonClip(id: string) {
|
||||
if (this.scene.buttonClips) {
|
||||
this.scene.buttonClips = this.scene.buttonClips.filter((c) => c.id !== id)
|
||||
}
|
||||
if (this.scene.buttonInstances) {
|
||||
this.scene.buttonInstances = this.scene.buttonInstances.filter(
|
||||
(bi) => bi.buttonClipId !== id,
|
||||
)
|
||||
}
|
||||
},
|
||||
setButtonClipName(id: string, name: string) {
|
||||
const clip = this.scene.buttonClips?.find((c) => c.id === id)
|
||||
if (clip) clip.name = name.trim() || clip.name
|
||||
},
|
||||
setButtonClipTransitions(
|
||||
id: string,
|
||||
transitions: NonNullable<ButtonClip['transitions']>,
|
||||
) {
|
||||
const clip = this.scene.buttonClips?.find((c) => c.id === id)
|
||||
if (!clip) return
|
||||
if (transitions.length) clip.transitions = [...transitions]
|
||||
else delete clip.transitions
|
||||
},
|
||||
addButtonInstance(
|
||||
buttonClipId: string,
|
||||
clipTrackId: string,
|
||||
startTime: number,
|
||||
): ButtonInstance | null {
|
||||
if (!this.scene.buttonClips?.some((c) => c.id === buttonClipId)) return null
|
||||
if (!this.scene.clipTracks?.some((t) => t.id === clipTrackId)) return null
|
||||
const inst: ButtonInstance = {
|
||||
id: uid(),
|
||||
buttonClipId,
|
||||
clipTrackId,
|
||||
startTime: Math.max(0, Math.min(this.scene.duration, startTime)),
|
||||
x: this.scene.canvas.width / 2,
|
||||
y: this.scene.canvas.height / 2,
|
||||
}
|
||||
;(this.scene.buttonInstances ||= []).push(inst)
|
||||
return inst
|
||||
},
|
||||
setButtonInstancePosition(id: string, x: number, y: number) {
|
||||
const target = this.scene.buttonInstances?.find((bi) => bi.id === id)
|
||||
if (!target) return
|
||||
target.x = x
|
||||
target.y = y
|
||||
},
|
||||
removeButtonInstance(id: string) {
|
||||
if (!this.scene.buttonInstances) return
|
||||
this.scene.buttonInstances = this.scene.buttonInstances.filter((bi) => bi.id !== id)
|
||||
},
|
||||
setButtonInstanceLabel(id: string, label: string): boolean {
|
||||
const instances = this.scene.buttonInstances
|
||||
if (!instances) return false
|
||||
const target = instances.find((bi) => bi.id === id)
|
||||
if (!target) return false
|
||||
const next = label.trim()
|
||||
if (!next) {
|
||||
delete target.label
|
||||
return true
|
||||
}
|
||||
if (next === target.label) return true
|
||||
if (instances.some((bi) => bi.id !== id && bi.label === next)) return false
|
||||
target.label = next
|
||||
return true
|
||||
},
|
||||
setButtonInstanceOnClick(id: string, actions: Action[]) {
|
||||
const target = this.scene.buttonInstances?.find((bi) => bi.id === id)
|
||||
if (!target) return
|
||||
if (actions.length) target.onClick = [...actions]
|
||||
else delete target.onClick
|
||||
},
|
||||
setButtonInstanceDisabled(id: string, disabled: boolean) {
|
||||
const target = this.scene.buttonInstances?.find((bi) => bi.id === id)
|
||||
if (!target) return
|
||||
if (disabled) target.disabled = true
|
||||
else delete target.disabled
|
||||
},
|
||||
setLayerSeparated(layerId: string, separated: boolean) {
|
||||
const layer = this.activeLayers.find((l) => l.id === layerId)
|
||||
if (!layer) return
|
||||
@@ -1558,15 +1423,6 @@ export const useSceneStore = defineStore('scene', {
|
||||
},
|
||||
actionRuntime(): ActionRuntime {
|
||||
const self = this
|
||||
const instances = self.scene.buttonInstances ?? []
|
||||
const byId = new Set(instances.map((bi) => bi.id))
|
||||
const byLabel = new Map<string, string>()
|
||||
for (const bi of instances) if (bi.label) byLabel.set(bi.label, bi.id)
|
||||
function resolve(ref: string): string | null {
|
||||
if (byId.has(ref)) return ref
|
||||
return byLabel.get(ref) ?? null
|
||||
}
|
||||
const btnRt = useButtonRuntime()
|
||||
return {
|
||||
fps: self.scene.fps,
|
||||
playbackIn: self.playbackIn,
|
||||
@@ -1582,28 +1438,6 @@ export const useSceneStore = defineStore('scene', {
|
||||
pause() {
|
||||
self.pause()
|
||||
},
|
||||
setButtonState(ref, state) {
|
||||
const id = resolve(ref)
|
||||
if (!id) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[animatr] unknown button ref:', ref)
|
||||
return
|
||||
}
|
||||
const inst = instances.find((x) => x.id === id)
|
||||
const clip = self.scene.buttonClips?.find(
|
||||
(c) => c.id === inst?.buttonClipId,
|
||||
)
|
||||
applyButtonStateChange(id, state, clip?.transitions, inst?.disabled)
|
||||
},
|
||||
setButtonDisabled(ref, disabled) {
|
||||
const id = resolve(ref)
|
||||
if (!id) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[animatr] unknown button ref:', ref)
|
||||
return
|
||||
}
|
||||
btnRt.setDisabled(id, disabled)
|
||||
},
|
||||
warn(msg: string) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[animatr]', msg)
|
||||
|
||||
Reference in new issue
Block a user