Skip to content

feat: allow clip animation + ship <hyperframes-player> web component#209

Merged
miguel-heygen merged 1 commit intomainfrom
feat/clip-fix-and-player
Apr 6, 2026
Merged

feat: allow clip animation + ship <hyperframes-player> web component#209
miguel-heygen merged 1 commit intomainfrom
feat/clip-fix-and-player

Conversation

@miguel-heygen
Copy link
Copy Markdown
Collaborator

@miguel-heygen miguel-heygen commented Apr 3, 2026

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 Initial repo setup #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:

<script src="https://cdn.jsdelivr.net/npm/@hyperframes/player"></script>
<hyperframes-player src="./composition/index.html" controls></hyperframes-player>
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

  • 427 core tests pass (20 GSAP lint tests with smart detection)
  • 7 player tests pass (formatTime + element registration)
  • TypeScript compiles cleanly (core + player)
  • Lint: GSAP animating clip with safe props → 0 errors
  • Lint: GSAP animating clip with visibility → 1 error (correct)
  • Player builds to 3.3KB gzipped ESM
  • Lockfile updated for CI
  • Docs page added at docs/packages/player.mdx

@miguel-heygen miguel-heygen force-pushed the feat/clip-fix-and-player branch from 0c10210 to f57a822 Compare April 3, 2026 12:34
@miguel-heygen miguel-heygen changed the base branch from main to fix/qa-report-improvements April 3, 2026 12:34
@miguel-heygen miguel-heygen force-pushed the fix/qa-report-improvements branch from 3d1cc87 to c3bd8b7 Compare April 3, 2026 12:41
@miguel-heygen miguel-heygen force-pushed the feat/clip-fix-and-player branch from f57a822 to 11e6111 Compare April 3, 2026 12:41
Copy link
Copy Markdown
Collaborator Author

miguel-heygen commented Apr 3, 2026

@miguel-heygen miguel-heygen force-pushed the feat/clip-fix-and-player branch from 482d8fd to c8d10c9 Compare April 3, 2026 14:47
@miguel-heygen miguel-heygen force-pushed the fix/qa-report-improvements branch from c7ca658 to 645f89f Compare April 3, 2026 14:52
@miguel-heygen miguel-heygen force-pushed the feat/clip-fix-and-player branch 2 times, most recently from f9017ec to f72835b Compare April 3, 2026 15:29
Copy link
Copy Markdown
Collaborator

@vanceingalls vanceingalls left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@miguel-heygen miguel-heygen changed the base branch from fix/qa-report-improvements to graphite-base/209 April 6, 2026 16:56
@miguel-heygen miguel-heygen force-pushed the feat/clip-fix-and-player branch from f72835b to d75eb66 Compare April 6, 2026 16:57
@graphite-app graphite-app Bot changed the base branch from graphite-base/209 to main April 6, 2026 16:57
@miguel-heygen miguel-heygen force-pushed the feat/clip-fix-and-player branch from d75eb66 to 7ae8359 Compare April 6, 2026 16:58
Copy link
Copy Markdown
Collaborator

@vanceingalls vanceingalls left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. ! non-null assertion (hyperframes-player.ts:71) — this.getAttribute("src")! should use a guard per project convention
  2. Hardcoded DEFAULT_FPS = 30 (hyperframes-player.ts) — seek/duration math will be wrong for 60fps compositions; consider having the runtime communicate fps via the timeline message

Neither is critical — this is good to merge as-is if you'd prefer to address these as follow-ups.

@miguel-heygen miguel-heygen merged commit 5655dab into main Apr 6, 2026
22 of 23 checks passed
Copy link
Copy Markdown
Collaborator Author

Merge activity

@miguel-heygen miguel-heygen deleted the feat/clip-fix-and-player branch April 6, 2026 23:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants