mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 12:54:29 +00:00
## 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`
176 lines
5.0 KiB
Plaintext
176 lines
5.0 KiB
Plaintext
---
|
|
title: "@hyperframes/player"
|
|
description: "Embeddable web component for playing HyperFrames compositions in any web page."
|
|
---
|
|
|
|
The player package provides a `<hyperframes-player>` custom element that embeds a HyperFrames composition anywhere — in any framework or plain HTML. Zero dependencies, 3KB gzipped.
|
|
|
|
```bash
|
|
npm install @hyperframes/player
|
|
```
|
|
|
|
## When to Use
|
|
|
|
**Use `@hyperframes/player` when you need to:**
|
|
- Embed a rendered composition in a website, dashboard, or app
|
|
- Add a video-like player to a landing page or product demo
|
|
- Show compositions in documentation or blog posts
|
|
|
|
**Use a different package if you want to:**
|
|
- Edit compositions interactively — use the [studio](/packages/studio)
|
|
- Preview during development — use the [CLI](/packages/cli) (`npx hyperframes preview`)
|
|
- Render to MP4 — use the [CLI](/packages/cli) or [producer](/packages/producer)
|
|
|
|
## Quick Start
|
|
|
|
### Via CDN
|
|
|
|
```html
|
|
<script src="https://cdn.jsdelivr.net/npm/@hyperframes/player"></script>
|
|
|
|
<hyperframes-player
|
|
src="./my-composition/index.html"
|
|
controls
|
|
autoplay
|
|
muted
|
|
style="width: 100%; max-width: 800px; aspect-ratio: 16/9"
|
|
></hyperframes-player>
|
|
```
|
|
|
|
### Via npm
|
|
|
|
```js
|
|
import '@hyperframes/player';
|
|
```
|
|
|
|
```html
|
|
<hyperframes-player src="/compositions/intro.html" controls></hyperframes-player>
|
|
```
|
|
|
|
## HTML Attributes
|
|
|
|
| Attribute | Type | Default | Description |
|
|
|-----------|------|---------|-------------|
|
|
| `src` | string | required | URL or relative path to composition HTML |
|
|
| `width` | number | 1920 | Composition width in pixels |
|
|
| `height` | number | 1080 | Composition height in pixels |
|
|
| `controls` | boolean | false | Show playback controls overlay |
|
|
| `autoplay` | boolean | false | Start playing on load |
|
|
| `loop` | boolean | false | Loop playback |
|
|
| `muted` | boolean | true | Mute audio (required for autoplay in most browsers) |
|
|
| `poster` | string | — | Image URL to show before first play |
|
|
| `playback-rate` | number | 1 | Playback speed multiplier |
|
|
|
|
## JavaScript API
|
|
|
|
The player mirrors the native `<video>` element API:
|
|
|
|
```js
|
|
const player = document.querySelector('hyperframes-player');
|
|
|
|
// Playback
|
|
player.play();
|
|
player.pause();
|
|
player.seek(2.5); // seek to 2.5 seconds
|
|
|
|
// Properties
|
|
player.currentTime; // number — current position in seconds
|
|
player.currentTime = 5; // seek to 5 seconds
|
|
player.duration; // number — total duration
|
|
player.paused; // boolean
|
|
player.ready; // boolean — true after composition loads
|
|
player.playbackRate; // number — get/set speed
|
|
player.muted; // boolean — get/set mute
|
|
player.loop; // boolean — get/set loop
|
|
```
|
|
|
|
## Events
|
|
|
|
```js
|
|
const player = document.querySelector('hyperframes-player');
|
|
|
|
player.addEventListener('ready', (e) => {
|
|
console.log('Duration:', e.detail.duration);
|
|
});
|
|
|
|
player.addEventListener('timeupdate', (e) => {
|
|
console.log('Time:', e.detail.currentTime);
|
|
});
|
|
|
|
player.addEventListener('play', () => console.log('Playing'));
|
|
player.addEventListener('pause', () => console.log('Paused'));
|
|
player.addEventListener('ended', () => console.log('Ended'));
|
|
player.addEventListener('error', (e) => console.error(e.detail.message));
|
|
```
|
|
|
|
| Event | Detail | Description |
|
|
|-------|--------|-------------|
|
|
| `ready` | `{ duration }` | Composition loaded and timeline discovered |
|
|
| `timeupdate` | `{ currentTime }` | Fires during playback (~30fps) |
|
|
| `play` | — | Playback started |
|
|
| `pause` | — | Playback paused |
|
|
| `ended` | — | Playback reached end |
|
|
| `error` | `{ message }` | Load or runtime error |
|
|
|
|
## Framework Examples
|
|
|
|
### React
|
|
|
|
```jsx
|
|
import '@hyperframes/player';
|
|
|
|
function VideoPreview({ src }) {
|
|
return (
|
|
<hyperframes-player
|
|
src={src}
|
|
controls
|
|
style={{ width: '100%', maxWidth: 800 }}
|
|
/>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Vue
|
|
|
|
```vue
|
|
<template>
|
|
<hyperframes-player :src="compositionUrl" controls />
|
|
</template>
|
|
|
|
<script setup>
|
|
import '@hyperframes/player';
|
|
const compositionUrl = './compositions/intro.html';
|
|
</script>
|
|
```
|
|
|
|
### Programmatic
|
|
|
|
```js
|
|
import '@hyperframes/player';
|
|
|
|
const player = document.createElement('hyperframes-player');
|
|
player.src = './my-composition/index.html';
|
|
player.controls = true;
|
|
player.addEventListener('ready', () => player.play());
|
|
document.getElementById('player-container').appendChild(player);
|
|
```
|
|
|
|
## Architecture
|
|
|
|
The player uses an iframe inside a Shadow DOM container. This provides:
|
|
|
|
- **Isolation** — composition CSS/JS can't leak into or conflict with your page
|
|
- **Security** — iframe sandbox restricts composition capabilities
|
|
- **Scaling** — auto-scales the composition to fit the player's container via CSS transforms
|
|
|
|
The player communicates with the composition via the HyperFrames runtime bridge protocol (`postMessage`). Existing compositions work without modification.
|
|
|
|
## Controls
|
|
|
|
When the `controls` attribute is present, a minimal overlay appears at the bottom:
|
|
|
|
- **Play/Pause** button (left)
|
|
- **Scrub bar** with drag support (mouse + touch)
|
|
- **Time display** showing current / total duration (right)
|
|
- Auto-hides after 3 seconds of inactivity, reappears on hover
|