macro-clips slice 4c: button creation in sidebar + drag to clip tracks

- store.seedDefaultButtonClip(name) adds a button clip with a single
  shared rect asset and four pre-populated state stacks in blue shades
  (normal/hover/pressed/disabled). Layer name 'Face' is consistent so
  transitions can later pair layers by name.
- AssetSidebar gets a Buttons section below Clips: create via prompt,
  rename, delete, drag-to-track. Drag uses a new dataTransfer type
  'application/x-animatr-button-clip' to keep it distinct from
  animation-clip drops.
- Timeline clip-lane drop accepts both types and dispatches
  addClipInstance / addButtonInstance accordingly; the new-track
  drop zone handles both too, so dragging a button to an empty area
  creates a fresh clip track + instance in one step.
- i18n for the sidebar strings and three dialogs.
- test covers seedDefaultButtonClip: four populated states, shared
  asset, consistent layer name.

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 21:31:41 +02:00
1 parent fb58cd18f7
commit 024883943b
5 files changed
+193 -6

No files matched your search

+19
View File
@@ -3189,6 +3189,25 @@ export function registerAllTests(): void {
expect(getB?.label).toBe('mute')
})
test('seedDefaultButtonClip produces four populated state stacks', () => {
const s = makeFreshStore()
const bc = s.seedDefaultButtonClip('Play')
expect(bc.states.normal).toHaveLength(1)
expect(bc.states.hover).toHaveLength(1)
expect(bc.states.pressed).toHaveLength(1)
expect(bc.states.disabled).toHaveLength(1)
// Each state has its own face layer, all pointing at the same asset.
const assetIds = new Set(
[...bc.states.normal, ...bc.states.hover, ...bc.states.pressed, ...bc.states.disabled]
.map((l) => l.assetId),
)
expect(assetIds.size).toBe(1)
// Layer names consistent so transitions can match across states.
for (const state of ['normal', 'hover', 'pressed', 'disabled'] as const) {
expect(bc.states[state][0].name).toBe('Face')
}
})
test('setButtonInstanceOnClick + setButtonInstanceDisabled', () => {
const s = makeFreshStore()
const bc = s.addButtonClip('B')