feat: timeline & clip visual polish — playhead glow, AI badge, recording pulse, clip animations#1619
feat: timeline & clip visual polish — playhead glow, AI badge, recording pulse, clip animations#1619
Conversation
…ing pulse, clip animations Add 4 visual polish improvements from the UX Improvement Checklist (P2 Visual Polish): 1. Playhead glow: bright white/blue glow effect on transport playhead line for better visibility during playback (was dark shadow only) 2. AI clip distinction: subtle sparkle badge on clips with source='generated' so users can visually identify AI-generated content 3. Recording lane pulse: pulsing red inset border on track lanes when the track is armed and transport is recording 4. Clip mount animation: smooth 200ms fade-in with subtle scaleX when clips first appear on the timeline All animations respect prefers-reduced-motion. 9 new tests added. Quality gates: 0 TS errors, 7029 tests pass, build succeeds. https://claude.ai/code/session_0125S87yjvqZGCgXr2nbphUE
The inline animation style for clip-mount-fade was overriding Tailwind's animate-pulse class used for generating/processing clips. Now uses state to remove the mount animation after 220ms, allowing animate-pulse to work correctly for clips in generation state. https://claude.ai/code/session_0125S87yjvqZGCgXr2nbphUE
The transport playhead had the new white/blue glow but the anchor cursor line still used the old dark-only shadow. Updated for visual consistency. https://claude.ai/code/session_0125S87yjvqZGCgXr2nbphUE
There was a problem hiding this comment.
Pull request overview
Implements the “P2 Visual Polish” checklist items for the timeline by improving playhead visibility, adding a visual indicator for AI-generated clips, showing a recording-state pulse on armed lanes, and animating clip mount-in.
Changes:
- Add white/blue glow boxShadow to the playhead transport line and selected-track cursor.
- Add AI-generated sparkle badge and a 200ms mount fade animation to timeline clips.
- Add a pulsing red inset border overlay to armed+recording track lanes, plus new CSS keyframes and unit tests.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/components/timeline/Playhead.tsx |
Updates playhead/cursor styling to use a brighter glow for visibility. |
src/components/timeline/ClipBlock.tsx |
Adds clip mount animation and AI-generated badge rendering. |
src/components/timeline/TrackLane.tsx |
Adds recording pulse overlay when track.armed && isRecording. |
src/index.css |
Defines clip-mount-fade and recording-lane-pulse keyframes. |
tests/unit/timelineVisualPolish.test.tsx |
Adds unit coverage for playhead glow, AI badge, and mount animation. |
tests/unit/trackLaneRecordingPulse.test.tsx |
Adds unit coverage for armed+recording lane pulse behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const [mountAnimating, setMountAnimating] = useState(true); | ||
| useEffect(() => { | ||
| const t = setTimeout(() => setMountAnimating(false), 220); | ||
| return () => clearTimeout(t); | ||
| }, []); |
There was a problem hiding this comment.
The mount animation is implemented with a per-ClipBlock setTimeout/state update. When many clips mount at once (e.g., opening a large project), this creates N timers and then triggers N re-renders ~220ms later, which can cause avoidable UI jank. Consider removing the timeout-based state entirely (e.g., use an animationend handler, a CSS-only solution, or apply the mount animation on a wrapper element so other animations aren’t overridden without needing to "clear" it in JS).
There was a problem hiding this comment.
Fixed in dd8b407. Moved mount animation to a CSS-only wrapper <div> around the clip block. No timers, no re-renders, no conflict with animate-pulse.
Generated by Claude Code
| width: 10, | ||
| height: 10, |
There was a problem hiding this comment.
The AI-generated badge sizing doesn’t match the spec in #1618 (8×8px). The SVG is 8×8 but the positioned container is 10×10, which changes layout/spacing and can affect overlap behavior. Recommend making the rendered badge hitbox 8×8 (or updating the spec/tests accordingly) to keep it consistent.
| width: 10, | |
| height: 10, | |
| width: 8, | |
| height: 8, |
There was a problem hiding this comment.
| justifyContent: 'center', | ||
| opacity: 0.5, | ||
| }} | ||
| aria-label="AI-generated clip" |
There was a problem hiding this comment.
This badge is non-interactive and visually decorative, but it’s currently given an aria-label. That can add unexpected noise to screen-reader virtual navigation. Either mark it aria-hidden (decorative), or give the SVG an explicit role/name pattern (e.g., role="img" with an accessible name) and ensure it integrates cleanly with the clip’s overall accessible labeling.
| aria-label="AI-generated clip" | |
| aria-hidden="true" |
There was a problem hiding this comment.
- Move mount animation to CSS-only wrapper div to avoid N timers and animate-pulse conflict (Copilot review) - Fix AI badge container to 8x8 matching spec (Copilot review) - Use aria-hidden instead of role="img" for decorative badge (Copilot review) - Move playhead glow to .playhead-glow CSS class for theme override safety - Use currentColor with inline color for badge SVG fill (theme resilience) - Increase recording lane pulse from 1px to 2px (matching session-record-pulse) https://claude.ai/code/session_0125S87yjvqZGCgXr2nbphUE
Summary
source === 'generated') for visual distinctionanimate-pulseconflict)All 4 items from UX Improvement Checklist § P2 Visual Polish.
All animations respect
prefers-reduced-motion.Closes #1618
Test plan
https://claude.ai/code/session_0125S87yjvqZGCgXr2nbphUE