initial code (#2)

* feat: initial code port from hyperframes-internal

Port all OSS-ready packages from the internal monorepo:
- @hyperframes/core — shared types, HTML generation, GSAP utilities, runtime
- @hyperframes/cli — CLI for creating, previewing, and rendering compositions
- @hyperframes/engine — framework-agnostic rendering engine (BeginFrame + FFmpeg)
- @hyperframes/producer — video rendering pipeline (Puppeteer + FFmpeg)
- @hyperframes/ui-player — browser-based video player component
- @hyperframes/studio — composition editor (React frontend + Hono backend)

Includes regression test suite with Docker-based test harness.

All HeyGen-internal references, deployment infrastructure, and
proprietary assets have been removed. Package names migrated
from @app/* to @hyperframes/*.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: scrub internal codenames and stale references from OSS port

- Replace static.heygen.ai runtime URLs in test fixtures
- Remove internal CDN publish script (publish-hyperframe-runtime.ts)
- Replace sandbox-studio, sandbox-interceptor, __magicEditRuntime
  with neutral names (studio, hyperframe-runtime, __hyperframeRuntime)
- Fix stale Vault API / localhost references in docs
- Remove broken deprecated_studio link

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: remove remaining internal codenames and stale references

- Delete stale producer README.md and PIPELINE.md (referenced nonexistent files)
- Replace "Cerberus" codename with "HyperFrames" in test design reviews
- Replace magic-edit postMessage identifiers with hf-preview/hf-parent
- Rename debug-magic-edit-timeline.ts to debug-timeline.ts
- Replace "Motion Cut" with "HyperFrames" in Timeline comments
- Fix studio/CLI references to nonexistent archive package
  (use local data/projects/ dir, stub render proxy)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vance Ingalls
2026-03-21 22:43:56 -07:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 10621e7903
commit 9f8e5ba5a1
401 changed files with 54545 additions and 2 deletions
@@ -0,0 +1,17 @@
{
"name": "style-3-prod",
"description": "Regression fixture imported from normalized style pack style-3-prod",
"tags": [
"style-regression",
"prod-style",
"slow",
"landscape"
],
"minPsnr": 30,
"maxFrameFailures": 0,
"minAudioCorrelation": 0.9,
"maxAudioLagWindows": 120,
"renderConfig": {
"fps": 30
}
}
@@ -0,0 +1,116 @@
<svg viewBox="0 0 1920 1080" xmlns="http://www.w3.org/2000/svg">
<rect width="1920" height="1080" fill="#F2F2F2" />
<g id="thin-grid-lines" stroke="#0A1E3D" stroke-width="1" opacity="0.1">
<line id="v-thin-1" x1="60" y1="0" x2="60" y2="1080" />
<line id="v-thin-2" x1="120" y1="0" x2="120" y2="1080" />
<line id="v-thin-3" x1="180" y1="0" x2="180" y2="1080" />
<line id="v-thin-5" x1="300" y1="0" x2="300" y2="1080" />
<line id="v-thin-6" x1="360" y1="0" x2="360" y2="1080" />
<line id="v-thin-7" x1="420" y1="0" x2="420" y2="1080" />
<line id="v-thin-9" x1="540" y1="0" x2="540" y2="1080" />
<line id="v-thin-10" x1="600" y1="0" x2="600" y2="1080" />
<line id="v-thin-11" x1="660" y1="0" x2="660" y2="1080" />
<line id="v-thin-13" x1="780" y1="0" x2="780" y2="1080" />
<line id="v-thin-14" x1="840" y1="0" x2="840" y2="1080" />
<line id="v-thin-15" x1="900" y1="0" x2="900" y2="1080" />
<line id="v-thin-17" x1="1020" y1="0" x2="1020" y2="1080" />
<line id="v-thin-18" x1="1080" y1="0" x2="1080" y2="1080" />
<line id="v-thin-19" x1="1140" y1="0" x2="1140" y2="1080" />
<line id="v-thin-21" x1="1260" y1="0" x2="1260" y2="1080" />
<line id="v-thin-22" x1="1320" y1="0" x2="1320" y2="1080" />
<line id="v-thin-23" x1="1380" y1="0" x2="1380" y2="1080" />
<line id="v-thin-25" x1="1500" y1="0" x2="1500" y2="1080" />
<line id="v-thin-26" x1="1560" y1="0" x2="1560" y2="1080" />
<line id="v-thin-27" x1="1620" y1="0" x2="1620" y2="1080" />
<line id="v-thin-29" x1="1740" y1="0" x2="1740" y2="1080" />
<line id="v-thin-30" x1="1800" y1="0" x2="1800" y2="1080" />
<line id="v-thin-31" x1="1860" y1="0" x2="1860" y2="1080" />
<line id="h-thin-1" x1="0" y1="60" x2="1920" y2="60" />
<line id="h-thin-2" x1="0" y1="120" x2="1920" y2="120" />
<line id="h-thin-3" x1="0" y1="180" x2="1920" y2="180" />
<line id="h-thin-5" x1="0" y1="300" x2="1920" y2="300" />
<line id="h-thin-6" x1="0" y1="360" x2="1920" y2="360" />
<line id="h-thin-7" x1="0" y1="420" x2="1920" y2="420" />
<line id="h-thin-9" x1="0" y1="540" x2="1920" y2="540" />
<line id="h-thin-10" x1="0" y1="600" x2="1920" y2="600" />
<line id="h-thin-11" x1="0" y1="660" x2="1920" y2="660" />
<line id="h-thin-13" x1="0" y1="780" x2="1920" y2="780" />
<line id="h-thin-14" x1="0" y1="840" x2="1920" y2="840" />
<line id="h-thin-15" x1="0" y1="900" x2="1920" y2="900" />
<line id="h-thin-17" x1="0" y1="1020" x2="1920" y2="1020" />
</g>
<g id="thick-grid-lines" stroke="#0A1E3D" stroke-width="3" opacity="0.2">
<line id="v-thick-1" x1="240" y1="0" x2="240" y2="1080" />
<line id="v-thick-2" x1="480" y1="0" x2="480" y2="1080" />
<line id="v-thick-3" x1="720" y1="0" x2="720" y2="1080" />
<line id="v-thick-4" x1="960" y1="0" x2="960" y2="1080" />
<line id="v-thick-5" x1="1200" y1="0" x2="1200" y2="1080" />
<line id="v-thick-6" x1="1440" y1="0" x2="1440" y2="1080" />
<line id="v-thick-7" x1="1680" y1="0" x2="1680" y2="1080" />
<line id="h-thick-1" x1="0" y1="240" x2="1920" y2="240" />
<line id="h-thick-2" x1="0" y1="480" x2="1920" y2="480" />
<line id="h-thick-3" x1="0" y1="720" x2="1920" y2="720" />
<line id="h-thick-4" x1="0" y1="960" x2="1920" y2="960" />
</g>
<g id="intersections" stroke="#0A1E3D" stroke-width="2" opacity="0.3">
<line x1="230" y1="240" x2="250" y2="240" />
<line x1="240" y1="230" x2="240" y2="250" />
<line x1="230" y1="480" x2="250" y2="480" />
<line x1="240" y1="470" x2="240" y2="490" />
<line x1="230" y1="720" x2="250" y2="720" />
<line x1="240" y1="710" x2="240" y2="730" />
<line x1="230" y1="960" x2="250" y2="960" />
<line x1="240" y1="950" x2="240" y2="970" />
<line x1="470" y1="240" x2="490" y2="240" />
<line x1="480" y1="230" x2="480" y2="250" />
<line x1="470" y1="480" x2="490" y2="480" />
<line x1="480" y1="470" x2="480" y2="490" />
<line x1="470" y1="720" x2="490" y2="720" />
<line x1="480" y1="710" x2="480" y2="730" />
<line x1="470" y1="960" x2="490" y2="960" />
<line x1="480" y1="950" x2="480" y2="970" />
<line x1="710" y1="240" x2="730" y2="240" />
<line x1="720" y1="230" x2="720" y2="250" />
<line x1="710" y1="480" x2="730" y2="480" />
<line x1="720" y1="470" x2="720" y2="490" />
<line x1="710" y1="720" x2="730" y2="720" />
<line x1="720" y1="710" x2="720" y2="730" />
<line x1="710" y1="960" x2="730" y2="960" />
<line x1="720" y1="950" x2="720" y2="970" />
<line x1="950" y1="240" x2="970" y2="240" />
<line x1="960" y1="230" x2="960" y2="250" />
<line x1="950" y1="480" x2="970" y2="480" />
<line x1="960" y1="470" x2="960" y2="490" />
<line x1="950" y1="720" x2="970" y2="720" />
<line x1="960" y1="710" x2="960" y2="730" />
<line x1="950" y1="960" x2="970" y2="960" />
<line x1="960" y1="950" x2="960" y2="970" />
<line x1="1190" y1="240" x2="1210" y2="240" />
<line x1="1200" y1="230" x2="1200" y2="250" />
<line x1="1190" y1="480" x2="1210" y2="480" />
<line x1="1200" y1="470" x2="1200" y2="490" />
<line x1="1190" y1="720" x2="1210" y2="720" />
<line x1="1200" y1="710" x2="1200" y2="730" />
<line x1="1190" y1="960" x2="1210" y2="960" />
<line x1="1200" y1="950" x2="1200" y2="970" />
<line x1="1430" y1="240" x2="1450" y2="240" />
<line x1="1440" y1="230" x2="1440" y2="250" />
<line x1="1430" y1="480" x2="1450" y2="480" />
<line x1="1440" y1="470" x2="1440" y2="490" />
<line x1="1430" y1="720" x2="1450" y2="720" />
<line x1="1440" y1="710" x2="1440" y2="730" />
<line x1="1430" y1="960" x2="1450" y2="960" />
<line x1="1440" y1="950" x2="1440" y2="970" />
<line x1="1670" y1="240" x2="1690" y2="240" />
<line x1="1680" y1="230" x2="1680" y2="250" />
<line x1="1670" y1="480" x2="1690" y2="480" />
<line x1="1680" y1="470" x2="1680" y2="490" />
<line x1="1670" y1="720" x2="1690" y2="720" />
<line x1="1680" y1="710" x2="1680" y2="730" />
<line x1="1670" y1="960" x2="1690" y2="960" />
<line x1="1680" y1="950" x2="1680" y2="970" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

@@ -0,0 +1,53 @@
# HyperFrame Schema Compliance Review
## Executive Summary
- Total files reviewed: 4
- Critical issues: 0
- Overall compliance status: PASS
## Critical Issues
None. All files now comply with the HyperFrame schema.
## Compliance Checklist
- [x] All compositions have `data-width` and `data-height` attributes
- [x] All timelines are finite with duration > 0
- [x] All compositions registered in `window.__timelines`
- [x] No use of `Math.random()`, `Date.now()`, or non-deterministic code
- [x] Primitive clips have required data attributes (`id`, `data-start`, `data-track`)
- [x] `data-duration` specified for all `<img>` clips
- [x] No manual media playback control (`video.play()`, `audio.pause()`, etc.)
- [x] No manual clip mounting/unmounting in scripts
- [x] Relative timing references are valid (N/A - absolute used)
- [x] Clips on same track don't overlap in time
- [x] Reusable compositions in separate HTML files
- [x] Composition files use `<template>` tags
- [x] External compositions loaded via `data-composition-src`
- [x] All script-animated content wrapped in compositions
- [x] No infinite or zero-duration timelines
## File Reviews
### index.html
**Status**: COMPLIANT
**Issues Found**:
- None. Correctly uses `data-composition-id`, `data-width`, `data-height`, and `data-duration`. Registers `master` timeline.
### compositions/intro.html
**Status**: COMPLIANT
**Issues Found**:
- None. Correctly uses `<template>`, registers `intro` timeline, and has required attributes.
### compositions/captions.html
**Status**: HAS_ISSUES
**Issues Found**:
- **Line 95**: Attempting to set `data-duration` via script. This should be a static attribute on the composition root (Line 2).
- **Line 2**: `data-duration="30"` is set, but the script calculates a different duration. These should be synchronized manually in the HTML.
### compositions/graphics.html
**Status**: COMPLIANT
**Issues Found**:
- **Line 135**: Redundant GSAP script import inside a composition template. While not a schema violation, it's unnecessary as GSAP is loaded in `index.html`.
@@ -0,0 +1,95 @@
<template id="captions-template">
<div data-composition-id="captions" data-width="1920" data-height="1080" data-duration="16.04">
<div id="caption-container"></div>
<style>
[data-composition-id="captions"] {
width: 1920px;
height: 1080px;
position: relative;
font-family: 'Helvetica', 'Arial', sans-serif;
font-weight: bold;
overflow: hidden;
}
[data-composition-id="captions"] #caption-container {
position: absolute;
bottom: 120px; /* Consistent bottom positioning */
left: 50%;
transform: translateX(-50%);
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
[data-composition-id="captions"] .caption-box {
background-color: #0A1E3D; /* Solid navy */
padding: 20px 40px;
display: none; /* Hidden by default, shown via GSAP */
justify-content: center;
align-items: center;
/* Swiss Grid: sharp corners, solid block */
}
[data-composition-id="captions"] .caption-text {
color: #F2F2F2; /* Off-white */
font-size: 72px;
text-transform: uppercase; /* Swiss style often uses uppercase for impact */
letter-spacing: -2px;
line-height: 1;
white-space: nowrap;
text-align: center;
}
</style>
<script>
(function() {
const TRANSCRIPT = [{'text': 'We', 'start': 0.119, 'end': 0.259}, {'text': 'asked', 'start': 0.319, 'end': 0.479}, {'text': 'what', 'start': 0.519, 'end': 0.659}, {'text': 'you', 'start': 0.699, 'end': 0.819}, {'text': 'needed.', 'start': 0.859, 'end': 1.819}, {'text': 'Forty-seven', 'start': 1.86, 'end': 2.299}, {'text': 'percent', 'start': 2.399, 'end': 2.679}, {'text': 'of', 'start': 2.7, 'end': 2.799}, {'text': 'you', 'start': 2.839, 'end': 2.939}, {'text': 'said', 'start': 3.039, 'end': 3.179}, {'text': 'motion', 'start': 3.24, 'end': 3.559}, {'text': 'graphics,', 'start': 3.579, 'end': 4.599}, {'text': 'sixty-two', 'start': 4.679, 'end': 5.179}, {'text': 'percent', 'start': 5.299, 'end': 5.759}, {'text': 'said', 'start': 5.859, 'end': 5.98}, {'text': 'static', 'start': 6.079, 'end': 6.399}, {'text': 'content', 'start': 6.46, 'end': 6.879}, {'text': 'was', 'start': 6.92, 'end': 7.079}, {'text': 'costing', 'start': 7.099, 'end': 7.48}, {'text': 'you', 'start': 7.5, 'end': 7.579}, {'text': 'attention,', 'start': 7.679, 'end': 8.659}, {'text': 'and', 'start': 8.699, 'end': 8.86}, {'text': 'three', 'start': 8.88, 'end': 9.06}, {'text': 'out', 'start': 9.079, 'end': 9.18}, {'text': 'of', 'start': 9.199, 'end': 9.34}, {'text': 'four', 'start': 9.38, 'end': 9.799}, {'text': 'said', 'start': 9.84, 'end': 10.0}, {'text': 'you', 'start': 10.019, 'end': 10.159}, {'text': 'know', 'start': 10.179, 'end': 10.36}, {'text': 'the', 'start': 10.38, 'end': 10.42}, {'text': 'look', 'start': 10.519, 'end': 10.699}, {'text': 'you', 'start': 10.739, 'end': 10.859}, {'text': 'want', 'start': 10.98, 'end': 11.34}, {'text': 'but', 'start': 11.359, 'end': 11.52}, {'text': "don't", 'start': 11.56, 'end': 11.779}, {'text': 'have', 'start': 11.819, 'end': 11.94}, {'text': 'the', 'start': 11.96, 'end': 12.06}, {'text': 'editing', 'start': 12.079, 'end': 12.4}, {'text': 'skills', 'start': 12.52, 'end': 12.86}, {'text': 'to', 'start': 12.88, 'end': 13.0}, {'text': 'get', 'start': 13.019, 'end': 13.18}, {'text': 'there.', 'start': 13.22, 'end': 14.22}, {'text': 'So', 'start': 14.239, 'end': 14.399}, {'text': 'we', 'start': 14.42, 'end': 14.52}, {'text': 'built', 'start': 14.619, 'end': 14.88}, {'text': 'Editor', 'start': 15.079, 'end': 15.42}, {'text': 'Agent.', 'start': 15.619, 'end': 16.02}];
const container = document.getElementById('caption-container');
const tl = gsap.timeline({ paused: true });
if (!TRANSCRIPT || TRANSCRIPT.length === 0) {
window.__timelines["captions"] = tl;
return;
}
// Group transcript into max 5 words per group
const groups = [];
let currentGroup = [];
TRANSCRIPT.forEach((word, index) => {
currentGroup.push(word);
// Group by 5 words OR if it's the last word
if (currentGroup.length === 5 || index === TRANSCRIPT.length - 1) {
groups.push([...currentGroup]);
currentGroup = [];
}
});
// Create DOM elements and timeline for each group
groups.forEach((group, i) => {
const box = document.createElement('div');
box.className = 'caption-box';
box.id = `group-${i}`;
const text = document.createElement('div');
text.className = 'caption-text';
text.textContent = group.map(w => w.text).join(' ');
box.appendChild(text);
container.appendChild(box);
const startTime = group[0].start;
const endTime = group[group.length - 1].end;
// Hard cut animation (no fades) as requested
tl.set(box, { display: 'flex' }, startTime);
tl.set(box, { display: 'none' }, endTime);
});
window.__timelines["captions"] = tl;
})();
</script>
</div>
</template>
@@ -0,0 +1,198 @@
<template id="graphics-template">
<div data-composition-id="graphics" data-width="1920" data-height="1080" data-duration="15">
<div class="grid-container">
<!-- Stat 1: 47% -->
<div id="stat1" class="stat-group">
<div class="stat-value">47%</div>
<div class="stat-label">NEED MOTION GRAPHICS</div>
</div>
<!-- Stat 2: 62% -->
<div id="stat2" class="stat-group">
<div class="stat-value">62%</div>
<div class="stat-label">LOSING ATTENTION</div>
<div class="progress-container">
<div class="progress-bar"></div>
</div>
</div>
<!-- Stat 3: 3 OF 4 -->
<div id="stat3" class="stat-group">
<div class="stat-value">3 OF 4</div>
<div class="stat-label">LACK EDITING SKILLS</div>
<div class="grid-blocks">
<div class="block gold"></div>
<div class="block gold"></div>
<div class="block gold"></div>
<div class="block navy"></div>
</div>
</div>
</div>
<style>
[data-composition-id="graphics"] {
font-family: 'Helvetica', Arial, sans-serif;
color: #0A1E3D;
background: transparent;
width: 1920px;
height: 1080px;
position: relative;
overflow: hidden;
}
[data-composition-id="graphics"] .grid-container {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: repeat(12, 1fr);
width: 100%;
height: 100%;
padding: 80px;
gap: 20px;
}
[data-composition-id="graphics"] .stat-group {
opacity: 0;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
background: #F2F2F2;
padding: 40px;
border-left: 10px solid #D4A017;
box-shadow: 20px 20px 0px rgba(10, 30, 61, 0.1);
}
/* Positioning Stats on Grid - Adjusted for A-roll structure */
[data-composition-id="graphics"] #stat1 {
grid-column: 2 / 6;
grid-row: 3 / 7;
}
[data-composition-id="graphics"] #stat2 {
grid-column: 2 / 6;
grid-row: 3 / 7;
}
[data-composition-id="graphics"] #stat3 {
grid-column: 7 / 12;
grid-row: 3 / 7;
}
[data-composition-id="graphics"] .stat-value {
font-size: 140px;
font-weight: 900;
color: #D4A017;
line-height: 0.8;
margin-bottom: 15px;
}
[data-composition-id="graphics"] .stat-label {
font-size: 24px;
font-weight: 300; /* Lighter weight */
color: #0A1E3D;
letter-spacing: 2px;
text-transform: uppercase;
}
/* Progress Bar for Stat 2 */
[data-composition-id="graphics"] .progress-container {
width: 100%;
height: 24px;
background: #E0E0E0;
margin-top: 30px;
border: 2px solid #0A1E3D;
position: relative;
}
[data-composition-id="graphics"] .progress-bar {
width: 0%;
height: 100%;
background: #D4A017;
}
/* Grid Blocks for Stat 3 */
[data-composition-id="graphics"] .grid-blocks {
display: flex;
gap: 20px;
margin-top: 30px;
}
[data-composition-id="graphics"] .block {
width: 80px;
height: 80px;
border: 3px solid #0A1E3D;
}
[data-composition-id="graphics"] .block.gold {
background: #D4A017;
}
[data-composition-id="graphics"] .block.navy {
background: #0A1E3D;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script>
(function() {
const tl = gsap.timeline({ paused: true });
// Timing Constants
const S1_START = 1.86;
const S1_END = 4.5;
const S2_START = 4.68;
const S2_END = 8.6;
const S3_START = 8.88;
const S3_END = 14.2;
const SLIDE_DUR = 0.25; // 250ms
const SHARP_EASE = "power2.out";
// Stat 1 Animation
tl.fromTo('#stat1',
{ x: -200, opacity: 0 },
{ x: 0, opacity: 1, duration: SLIDE_DUR, ease: SHARP_EASE },
S1_START
);
tl.to('#stat1',
{ x: 200, opacity: 0, duration: SLIDE_DUR, ease: "power2.in" },
S1_END - SLIDE_DUR
);
// Stat 2 Animation
tl.fromTo('#stat2',
{ y: 200, opacity: 0 },
{ y: 0, opacity: 1, duration: SLIDE_DUR, ease: SHARP_EASE },
S2_START
);
// Progress bar fill
tl.to('#stat2 .progress-bar',
{ width: '62%', duration: S2_END - S2_START - (SLIDE_DUR * 2), ease: "none" },
S2_START + SLIDE_DUR
);
tl.to('#stat2',
{ y: -200, opacity: 0, duration: SLIDE_DUR, ease: "power2.in" },
S2_END - SLIDE_DUR
);
// Stat 3 Animation
tl.fromTo('#stat3',
{ x: 200, opacity: 0 },
{ x: 0, opacity: 1, duration: SLIDE_DUR, ease: SHARP_EASE },
S3_START
);
// Stagger blocks
tl.from('#stat3 .block',
{ scale: 0, duration: 0.3, stagger: 0.1, ease: "back.out(1.7)" },
S3_START + SLIDE_DUR
);
tl.to('#stat3',
{ x: -200, opacity: 0, duration: SLIDE_DUR, ease: "power2.in" },
S3_END - SLIDE_DUR
);
window.__timelines["graphics"] = tl;
})();
</script>
</div>
</template>
@@ -0,0 +1,114 @@
<template id="intro-template">
<div data-composition-id="intro" data-width="1920" data-height="1080" data-duration="1.86">
<div class="container">
<div class="grid-line"></div>
<div class="text-wrapper">
<h1 class="title">EDITOR AGENT</h1>
<h2 class="subtitle">THE SURVEY FINDINGS</h2>
</div>
</div>
<style>
[data-composition-id="intro"] {
background-color: #F5F5F5; /* Off-white */
width: 1920px;
height: 1080px;
display: flex;
align-items: center;
justify-content: center;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
overflow: hidden;
}
[data-composition-id="intro"] .container {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
padding-left: 160px; /* Swiss grid alignment */
}
[data-composition-id="intro"] .grid-line {
position: absolute;
left: 160px;
top: 50%;
width: 0px;
height: 12px;
background-color: #0A1E3D; /* Navy */
transform: translateY(-180px); /* Positioned above the text */
}
[data-composition-id="intro"] .text-wrapper {
display: flex;
flex-direction: column;
gap: 0;
overflow: hidden;
}
[data-composition-id="intro"] .title {
color: #0A1E3D;
font-size: 180px;
font-weight: 900; /* Black weight */
line-height: 0.9;
margin: 0;
letter-spacing: -0.04em;
text-transform: uppercase;
transform: translateX(-100%); /* Start off-screen left */
}
[data-composition-id="intro"] .subtitle {
color: #0A1E3D;
font-size: 64px;
font-weight: 300; /* Lighter weight for contrast */
line-height: 1.2;
margin: 0;
letter-spacing: 0.1em;
text-transform: uppercase;
transform: translateX(-100%); /* Start off-screen left */
margin-top: 20px;
}
</style>
<script>
(function() {
const tl = gsap.timeline({ paused: true });
// Mechanical timing: 200ms (0.2s)
const MECHANICAL_DURATION = 0.2;
const STAGGER = 0.1;
// 1. Animate the grid line first
tl.to('.grid-line', {
width: '1200px',
duration: 0.4,
ease: 'power4.out'
}, 0.2);
// 2. Slide in the title
tl.to('.title', {
x: 0,
duration: MECHANICAL_DURATION,
ease: 'power2.inOut'
}, 0.5);
// 3. Slide in the subtitle with a slight stagger
tl.to('.subtitle', {
x: 0,
duration: MECHANICAL_DURATION,
ease: 'power2.inOut'
}, 0.5 + STAGGER);
// 4. Subtle ambient motion (slow drift) to keep it alive
tl.to('.text-wrapper', {
x: 30,
duration: 1.16, // Adjusted to fit 1.86s total
ease: 'none'
}, 0.7);
window.__timelines["intro"] = tl;
})();
</script>
</div>
</template>
@@ -0,0 +1,57 @@
# HyperFrames Design Review
## First Impression
This looks like a "Swiss Style" template that someone found in a dumpster and tried to glue back together. Its a clinical, soulless mess that mistakes "minimalism" for "I didn't have time to finish the design."
---
## CRITICAL Design Failures
### The "Swiss Grid" is a Lie
**Where:** `assets/swiss-grid.svg` and `index.html`
**What's wrong:** Youve included a grid background that looks like a blueprint for a parking lot. Its distracting, cluttered, and serves no purpose other than to scream "I'm trying to be edgy." The intersections and thick lines create a visual vibration that makes the actual content hard to focus on.
**Why it matters:** A grid should be a guide for the designer, not a cage for the viewer. Its visually exhausting and makes the entire composition feel like a technical drawing rather than a professional video.
**Fix it:** Lower the opacity of the grid to 0.05 or remove it entirely. Use the grid to align elements, don't make the viewer look at it.
### Typography Hierarchy is Non-Existent
**Where:** `compositions/intro.html` and `compositions/graphics.html`
**What's wrong:** Youre using Helvetica (the default choice of the uninspired) and just cranking the font size up to 180px. The "EDITOR AGENT" title is so massive its practically shouting in the viewer's face, while the subtitle is just... there. Theres no elegance, no play with weight, just "BIG TEXT" and "SMALLER TEXT."
**Why it matters:** Without a clear typographic hierarchy, the viewer doesn't know where to look first. It feels amateur and "default."
**Fix it:** Experiment with tighter letter spacing (kerning) and more dramatic weight contrasts. Try a heavier weight for the title and a much lighter, tracked-out weight for the subtitle.
### The "Gold" is Actually Mustard
**Where:** `compositions/graphics.html` (`#D4A017`)
**What's wrong:** Youve paired a sophisticated Navy (`#0A1E3D`) with a "gold" that looks like expired Dijon mustard. Its flat, muddy, and lacks any premium feel.
**Why it matters:** Color choice dictates the "vibe." This color palette feels like a budget airline from the 90s, not a cutting-edge "Editor Agent."
**Fix it:** Use a more vibrant, metallic-leaning gold or a sharp, high-contrast accent color like a neon cyan or a deep crimson to actually give this some life.
---
## Design Improvements
### Robotic Motion
**Where:** `compositions/graphics.html` and `index.html`
**The problem:** The animations are "mechanical" in the worst way. `power2.out` is the "I just started using GSAP" of easing functions. The A-roll video jumps around the screen like a glitchy security camera.
**Make it better:** Use more sophisticated easing like `expo.out` or `customEase`. Add some secondary motion—maybe the text doesn't just slide, it scales slightly or has a subtle blur on entry. Make the transitions feel like they have weight and momentum.
### Caption Box Laziness
**Where:** `compositions/captions.html`
**The problem:** A solid navy box with white text. Its the most basic implementation possible. It covers up the A-roll and looks like a closed-captioning fail.
**Make it better:** Lose the solid box. Use a subtle text shadow or a semi-transparent blurred background (glassmorphism) to keep the text readable without blocking the frame. Or, lean into the Swiss style and use a high-contrast, offset background block that actually feels intentional.
---
## What Actually Works
The **layout logic** in `index.html` where the A-roll moves to accommodate the graphics is actually a smart move. It shows youre thinking about the composition as a whole, even if the execution is currently hideous. The use of a 12x12 grid for positioning is a solid foundation—you just need to stop showing your work.
---
## Design Verdict
**Visual Impact:** 3/10 - Its as exciting as a spreadsheet.
**Color & Typography:** 2/10 - Helvetica and mustard. Groundbreaking.
**Motion & Animation Feel:** 4/10 - Functional, but lacks any soul or "flow."
**Overall Aesthetic:** 3/10 - A "Swiss Style" attempt that fell off the mountain.
**Bottom Line:** This looks like a prototype for a corporate training video that everyone will mute and minimize. It needs a massive injection of personality, better color theory, and motion that doesn't feel like it was programmed by a calculator. Fix the grid, fix the colors, and for the love of design, find a better font.
@@ -0,0 +1,172 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Swiss Grid - Editor Agent</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<style>
body, html {
margin: 0;
padding: 0;
width: 1920px;
height: 1080px;
background-color: #F2F2F2;
overflow: hidden;
font-family: 'Helvetica', Arial, sans-serif;
}
#master-root {
width: 1920px;
height: 1080px;
position: relative;
}
.background-grid {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
}
/* A-roll styling */
#short_mag_cut_frame {
position: absolute;
border-radius: 8px; /* Required */
z-index: 1;
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
overflow: hidden;
background: #000;
}
#short_mag_cut,
#short_mag_cut_frame > img.__render_frame__,
#short_mag_cut_frame > img.__preview_render_frame__ {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
display: block;
}
/* Overlay layers */
.overlay-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
#intro-comp { z-index: 2; }
#graphics-comp { z-index: 3; }
#captions-comp { z-index: 4; }
</style>
</head>
<body>
<div id="master-root" data-composition-id="master" data-width="1920" data-height="1080" data-duration="16.04">
<!-- Background Grid -->
<img id="bg-grid"
class="background-grid"
src="assets/swiss-grid.svg"
alt="Grid"
data-start="0"
data-duration="16.04"
data-track-index="0">
<!-- A-roll Video -->
<div id="short_mag_cut_frame">
<video id="short_mag_cut"
src="https://gen-os-static.s3.us-east-2.amazonaws.com/astral_assets/uploaded_assets/8eb26801_b5ca4d50bb91437dac375c58b86d005c.mp4"
data-start="0"
data-duration="16.04"
data-track-index="1">
</video>
</div>
<!-- Intro Sub-composition -->
<div id="intro-comp"
class="overlay-layer"
data-composition-id="intro"
data-composition-src="compositions/intro.html"
data-start="0"
data-duration="1.86"
data-track-index="2">
</div>
<!-- Graphics Sub-composition -->
<div id="graphics-comp"
class="overlay-layer"
data-composition-id="graphics"
data-composition-src="compositions/graphics.html"
data-start="0"
data-duration="16.04"
data-track-index="3">
</div>
<!-- Captions Sub-composition -->
<div id="captions-comp"
class="overlay-layer"
data-composition-id="captions"
data-composition-src="compositions/captions.html"
data-start="0"
data-duration="16.04"
data-track-index="4">
</div>
<!-- Sound Effects -->
<audio id="sfx-intro" data-start="0" data-track-index="5" data-duration="1" src="https://gen-os-static.s3.us-east-2.amazonaws.com/astral_assets/generated_assets/051b5d38_af2426f283a749de94ab615d459a565d.mp3"></audio>
<audio id="sfx-stat1" data-start="1.86" data-track-index="5" data-duration="1" src="https://gen-os-static.s3.us-east-2.amazonaws.com/astral_assets/generated_assets/bd3cb328_5d3f0ddf41c14bb8bfad8ae242024334.mp3"></audio>
<audio id="sfx-stat2" data-start="4.68" data-track-index="5" data-duration="1" src="https://gen-os-static.s3.us-east-2.amazonaws.com/astral_assets/generated_assets/61ada7ed_142d9432f783410185afebd0d00f1b7a.mp3"></audio>
<audio id="sfx-stat3" data-start="8.88" data-track-index="5" data-duration="1" src="https://gen-os-static.s3.us-east-2.amazonaws.com/astral_assets/generated_assets/bd3cb328_5d3f0ddf41c14bb8bfad8ae242024334.mp3"></audio>
<script>
window.__timelines = window.__timelines || {};
const masterTL = gsap.timeline({ paused: true });
const v = document.getElementById('short_mag_cut_frame');
// --- A-ROLL MOVEMENT (Structured Swiss Layout) ---
// 1. Entrance (0-1.86s): Slide in from left, settle centered for Intro
masterTL.fromTo(v,
{ x: -1000, y: 120, width: 960, height: 840 },
{ x: 480, y: 120, width: 960, height: 840, duration: 0.8, ease: "power2.out" },
0
);
// 2. Stat 1 (1.86s - 4.68s): Move to right side (Graphic is on left)
masterTL.to(v, {
x: 1080, y: 120, width: 720, height: 720,
duration: 0.25, ease: "expo.out"
}, 1.86);
// 3. Stat 2 (4.68s - 8.88s): Stay right, zoom in slightly
masterTL.to(v, {
x: 1080, y: 60, width: 780, height: 780,
duration: 0.25, ease: "expo.out"
}, 4.68);
// 4. Stat 3 (8.88s - 14.2s): Move to left side (Graphic is on right)
masterTL.to(v, {
x: 120, y: 120, width: 720, height: 720,
duration: 0.25, ease: "expo.out"
}, 8.88);
// 5. Conclusion (14.2s - 16.04s): Center large
masterTL.to(v, {
x: 240, y: 60, width: 1440, height: 810,
duration: 0.5, ease: "expo.out"
}, 14.2);
window.__timelines["master"] = masterTL;
</script>
</div>
</body>
</html>