# Macro Clips & Scripts — Testing Guide Eine Walkthrough für die Features aus `docs/macro-clips-and-scripts.md`. Start: App mit `npm run dev`, leeres Projekt öffnen („New Project" im Welcome-Dialog). --- ## 1. Marker **Ziel:** Sprungziele auf der Haupt-Timeline setzen und benennen. 1. Playhead an eine beliebige Stelle ziehen (z. B. t = 2 s). 2. `m` drücken → ein Marker wird an der Playhead-Position angelegt und ein Rename-Prompt öffnet sich. Namen eingeben (z. B. `intro`), `Enter`. 3. Marker erscheint als kleine Fahne oben auf der Scrub-Ruler. 4. **Interaktionen am Marker:** - Einfacher Klick → Playhead springt dort hin. - Drag → Marker zeitlich verschieben. - Doppelklick → Rename. - Marker anwählen, `Backspace`/`Delete` → löscht ihn. `Esc` → entauswählt. **Erwartung:** - Mehrere Marker bleiben zeitlich sortiert. - Zweiter Marker mit gleichem Namen bekommt automatisch Suffix (`intro 2`). - Namen müssen Scene-weit unique sein (Rename zeigt Fehler-Dialog bei Kollision und öffnet den Prompt erneut). --- ## 2. Script-Tracks & Action-Frames **Ziel:** Zeit-gebundene Aktionen definieren, die bei Playback feuern. 1. Im Timeline-Header die **Code-Taste** (``-Icon) klicken → ein neuer Script-Track wird angelegt (kleinere Zeile als Clip-Tracks). 2. Auf der leeren Script-Lane an beliebiger Zeitposition klicken → ein neuer Action-Frame wird erstellt und der **ScriptFrameDialog** öffnet sich. 3. Im Dialog **Add action** drücken → ein Dropdown zeigt alle Action-Typen. - Beispiel: `Pause` auswählen → nur Typ, keine Params nötig. - Weitere Action hinzufügen: `Jump to marker` → Marker-Name aus dem Dropdown wählen (dafür muss aus Schritt 1 ein Marker existieren). 4. Dialog schließen mit **Done**. 5. Mehrere Actions pro Frame sind erlaubt — die Zahl rechts neben dem Frame-Dot zeigt deren Anzahl. **Testen:** - Playhead VOR den Action-Frame ziehen, dann `Space` (Play). - Playback läuft, und **genau wenn der Playhead über den Frame hinausgeht**, feuern die Actions in Listen-Reihenfolge. - Beispiel `pause + jumpToMarker intro` → Playback pausiert und Playhead springt zum `intro`-Marker. **Erwartung:** - Beim Scrubben (Ziehen des Playheads oder Marker-Klick) feuern **keine** Actions. - Beim Loop-Wrap (von `playbackOut` zurück nach `playbackIn`) feuern ebenfalls keine Actions. - Rückwärtsbewegen der Zeit feuert nichts. --- ## 3. Button-Clips erzeugen und platzieren **Ziel:** Einen klickbaren Button definieren, auf die Stage bringen und onClick-Actions hinterlegen. 1. In der Asset-Sidebar den Abschnitt **Buttons** → `+` klicken. Namen eingeben (z. B. `PlayButton`), `Enter`. 2. Der Button erscheint in der Liste mit einem Cursor-Icon. Die 4 States sind automatisch als bunte Rects vorausgefüllt (blau / hellblau / dunkelblau / grau). 3. Button-Clip aus der Sidebar auf eine Clip-Track ziehen (Drag-and-Drop). - Wenn keine Clip-Track existiert, in den Timeline-Bereich unter den Tracks droppen → neue Clip-Track entsteht automatisch. 4. Auf der Clip-Track erscheint ein 4-Zellen-Block (`N H P D`) an der Drop-Position. 5. Auf dem Canvas erscheint der Button (standardmäßig in der Stage-Mitte) — wenn der Playhead ≥ Instanz-Startzeit. **Button editieren:** 1. Auf den 4-Zellen-Block klicken → **ButtonInstanceDialog** öffnet sich. 2. **Label** vergeben (z. B. `play`) — wichtig, weil: - Im DSL über `btn:play` referenzierbar. - Im Dropdown von `Set button state`-Actions sichtbar. 3. **On click** → Actions definieren (z. B. `Play` und `Jump to marker intro`). 4. **Start disabled** aktivieren → Button startet grau/schraffiert, reagiert nicht auf Klicks. 5. **Done**. --- ## 4. Button-Runtime auf der Stage testen **Voraussetzung:** Ein Button ist platziert und der Playhead ist auf oder nach dessen Startzeit. 1. Maus über den Button bewegen → Farbe wechselt zu *hellblau* (`hover`). 2. Maus-Down → Farbe wird *dunkelblau* (`pressed`). 3. Maus-Up (immer noch im Button) → `onClick`-Actions feuern; State geht zurück zu `hover`. 4. Maus-Down und **rausziehen** vor dem Maus-Up → kein `onClick`, State kehrt zu `normal` zurück (Standard-Browser-Button-Verhalten). 5. Disabled-Button hat keine Hover-/Press-Reaktion, ignoriert Klicks. **Skript-getriggert:** - In einem ScriptFrame eine `Set button state`-Action mit Target = Button-Label und state = `pressed` anlegen. - Beim Playback überqueren → der Button schaltet visuell in `pressed`, ohne dass der User hovert. - `Set button disabled` / `Enable` toggelt den Disabled-Flag zur Laufzeit. --- ## 5. Transitions zwischen States **Ziel:** Glatter visueller Übergang zwischen States statt hartem Wechsel. 1. ButtonInstance-Dialog öffnen (4-Zellen-Block klicken). 2. Unter **Transitions** → `+ Add transition`. 3. Zeile: `normal → hover`, Duration `0.15`, Easing `easeOut`. 4. Weitere Zeile: `hover → normal`, Duration `0.2`, Easing `easeOut`. 5. Done. 6. Auf dem Canvas Maus über den Button → die Farbe **fade**t zwischen Blau und Hellblau über 150 ms, statt instant zu wechseln. **Hinweise:** - Transitions sind Clip-scoped, d. h. sie gelten für **alle** Instanzen des Buttons. - Layer werden zwischen den beiden States per `name` gematcht (beim Default-Seed heißen alle Face-Layer schlicht `Face` → werden immer interpoliert). Unmatched Layer faden nur rein/raus (Crossfade). - Ohne Transition: State-Wechsel ist sofort (Verhalten wie vor Slice 7). --- ## 6. Script-DSL testen **Ziel:** Komplexere Logik in einer kompakten Textform statt manuellem Zusammenklicken mehrerer Actions. 1. ScriptFrameDialog öffnen (oder ButtonInstanceDialog). 2. **Add action** → Typ `Run script` wählen. 3. Im Textarea: ``` // setzt Pause und springt an den intro-Marker pause jump @intro state btn:play pressed disable btn:play ``` 4. Done. **Testen:** - Frame überqueren → alle parsed Aktionen feuern in Reihenfolge. - Falsche Syntax → Warnung erscheint in der Devtools-Konsole (`[animatr] Script line N: ...`). Die fehlerhafte Zeile wird übersprungen; valide Zeilen feuern trotzdem. **DSL-Referenz (aktueller Sprachumfang):** ``` // Kommentar (bis Zeilenende) play // Playback starten pause // Playback pausieren rewind // Playhead → playbackIn (in-point) jump 2.5 // Zeit in Sekunden jump #135 // Frame-Nummer (durch fps geteilt) jump @markerName // benannter Marker state btn:label // ∈ normal|hover|pressed|disabled enable btn:label disable btn:label ``` --- ## 7. Export-Runtime prüfen **Ziel:** Sicherstellen dass Buttons, Scripts, Marker im exportierten Projekt genauso funktionieren wie im Editor. 1. Menü → Export (oder beliebiger Export-Trigger je nach UI). 2. `AnimatrPlayer`-HTML-Export wählen → Datei öffnen im Browser. 3. **Erwartet:** - Buttons rendern an ihren Positionen, mit Pointer-Reaktion. - onClick feuert die hinterlegten Aktionen (z. B. `jump @intro` springt im exportierten Player-Playhead zum Marker). - Transitions animieren sauber. - Script-Frames feuern bei natürlichem Playback. 4. Devtools-Konsole im Export-Tab → `window.AnimatrPlayer.__v` sollte `'6'` ergeben. Cache-Probleme? Hard-Reload mit Shift+Cmd+R / Ctrl+F5. --- ## 8. Runtime-Tests (in-app) `Ctrl+Shift+Alt+T` öffnet den Test-Runner. Relevante Suiten: - `scene store — markers` / `script tracks & frames` / `button clips & instances` - `actions — evalAction with mock runtime` - `actions — script type expands via evalAction` - `scriptDsl — parser` - `button runtime — state machine + action wiring` - `scene store — tick fires script frames forward-only` Alle sollten grün laufen. --- ## Bekannte Einschränkungen - **Kein Button-State-Editor in der UI:** Die 4 States sind beim Seed mit farbigen Rects vorausgefüllt. Um die Optik anzupassen müsste man aktuell `scene.buttonClips[i].states[…]` direkt im gespeicherten Projekt-JSON editieren. Ein eigener Edit-Modus ist geplant, aber nicht implementiert. - **Server-Render (PNG-Sequence / MP4)** bakt Script-Actions und Button-Interaktionen bewusst nicht ein — das wäre keine deterministische Animation mehr. Scripts wirken nur in der interaktiven Browser-Laufzeit (Editor + `AnimatrPlayer`-HTML-Export). --- ## Devtools-Cheat-Sheet Für Tests ohne UI: ```js // Pinia store im Devtools: const s = __pinia.state.value.scene const store = useSceneStore() // im Vue-Devtools verfügbar // Button-Instanz direkt einen bestimmten State geben: const rt = useButtonRuntime() rt.setState(instanceId, 'pressed') rt.reset() // Alle Marker sehen: console.table(store.scene.markers) // Alle Button-Instanzen: console.table(store.scene.buttonInstances) ```