feat: allow clip animation + ship <hyperframes-player> web component - #209
Conversation
0c10210 to
f57a822
Compare
3d1cc87 to
c3bd8b7
Compare
f57a822 to
11e6111
Compare
This stack of pull requests is managed by Graphite. Learn more about stacking. |
482d8fd to
c8d10c9
Compare
c7ca658 to
645f89f
Compare
f9017ec to
f72835b
Compare
vanceingalls
left a comment
There was a problem hiding this comment.
Review
Nice work — the clip rule relaxation is clean and well-tested, and the player web component is a solid v1. Three items to address:
Must fix
Binary file corruption in play command (packages/cli/src/commands/play.ts)
The /composition/* handler reads all files with readFileSync(filePath, "utf-8"), including images, video, and audio. Binary files will be silently corrupted. Use readFileSync(filePath) (no encoding) for non-text types — the types map already distinguishes them.
Should fix
! non-null assertion (packages/player/src/hyperframes-player.ts)
connectedCallback uses this.getAttribute("src")! — project convention is to use a guard or fallback instead of ! assertions.
Hardcoded 30fps (packages/player/src/hyperframes-player.ts)
DEFAULT_FPS = 30 is used for all duration/seek math (frame / 30, timeInSeconds * 30). Compositions rendered at 60fps will have wrong currentTime and broken seek(). The runtime's timeline message already knows the real fps — include it in the message payload and use it here.
645f89f to
baa3d81
Compare
f72835b to
d75eb66
Compare
d75eb66 to
7ae8359
Compare
vanceingalls
left a comment
There was a problem hiding this comment.
Correction to previous review
Withdrawing the binary file corruption issue. I misread the control flow in play.ts — the non-HTML branch does a second readFileSync(filePath) without encoding, so binary files are served correctly. The initial UTF-8 read is wasteful but not a bug. Apologies for the false alarm.
The two remaining items from the original review still apply:
!non-null assertion (hyperframes-player.ts:71) —this.getAttribute("src")!should use a guard per project convention- Hardcoded
DEFAULT_FPS = 30(hyperframes-player.ts) — seek/duration math will be wrong for 60fps compositions; consider having the runtime communicate fps via thetimelinemessage
Neither is critical — this is good to merge as-is if you'd prefer to address these as follow-ups.
Merge activity
|
…eygen-com#209) ## Summary Two independent initiatives that improve agent DX and expand HyperFrames' reach. ### Initiative 1: Fix the Clip Animation Footgun - `gsap_animates_clip_element` lint rule now uses smart detection — only errors when GSAP animates `visibility` or `display` on a clip element - All other properties (opacity, transform, x, y, scale, etc.) are allowed silently - This was the #1 agent failure in QA (10/10 agents hit it on v0.2.1) ### Initiative 2: `<hyperframes-player>` Web Component - New `@hyperframes/player` package — zero dependencies, 3.3KB gzipped - Iframe-based web component with Shadow DOM for perfect isolation - Video-like API: `play()`, `pause()`, `seek()`, `currentTime`, `duration`, events - Controls overlay with play/pause, scrubber (mouse + touch), time display, auto-hide - Full docs page at `docs/packages/player.mdx` ## Before / After ### Clip animation lint **Before (10/10 agents hit this):** ``` ✗ gsap_animates_clip_element: GSAP animation targets a clip element. Selector "#title" resolves to element <div id="title" class="clip">. The framework manages clip visibility — animate an inner wrapper instead. Fix: Wrap content in a child <div> and target that with GSAP. ``` **After (only errors on actual conflicts):** ``` # This passes lint — no error: tl.from("#title", { opacity: 0, y: -50, scale: 0.8 }, 0); # This still errors — actual conflict with runtime: tl.to("#title", { visibility: "hidden" }, 3); ✗ gsap_animates_clip_element: GSAP animation sets visibility on a clip element. Fix: Remove the visibility/display tween. Use opacity for fade effects. ``` ### Embeddable player **Before:** No way to embed a composition in a web page. **After:** ```html <script src="https://cdn.jsdelivr.net/npm/@hyperframes/player"></script> <hyperframes-player src="./composition/index.html" controls></hyperframes-player> ``` ```js const player = document.querySelector('hyperframes-player'); player.play(); player.pause(); player.seek(2.5); player.addEventListener('ready', (e) => console.log('Duration:', e.detail.duration)); ``` ## Test plan - [x] 427 core tests pass (20 GSAP lint tests with smart detection) - [x] 7 player tests pass (formatTime + element registration) - [x] TypeScript compiles cleanly (core + player) - [x] Lint: GSAP animating clip with safe props → 0 errors - [x] Lint: GSAP animating clip with `visibility` → 1 error (correct) - [x] Player builds to 3.3KB gzipped ESM - [x] Lockfile updated for CI - [x] Docs page added at `docs/packages/player.mdx`

Summary
Two independent initiatives that improve agent DX and expand HyperFrames' reach.
Initiative 1: Fix the Clip Animation Footgun
gsap_animates_clip_elementlint rule now uses smart detection — only errors when GSAP animatesvisibilityordisplayon a clip elementInitiative 2:
<hyperframes-player>Web Component@hyperframes/playerpackage — zero dependencies, 3.3KB gzippedplay(),pause(),seek(),currentTime,duration, eventsdocs/packages/player.mdxBefore / After
Clip animation lint
Before (10/10 agents hit this):
After (only errors on actual conflicts):
Embeddable player
Before: No way to embed a composition in a web page.
After:
Test plan
visibility→ 1 error (correct)docs/packages/player.mdx