Files
hyperframes/packages/producer/tests/style-5-prod/src/compositions/data-graphics.html
T
Vance IngallsandClaude Opus 4.6 9f8e5ba5a1 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>
2026-03-21 22:43:56 -07:00

327 lines
10 KiB
HTML

<template id="data-graphics-template">
<div data-composition-id="data-graphics" data-width="1920" data-height="1080" data-duration="16.04">
<div class="grid-container">
<!-- Graphic 1: 47% NEED MOTION GRAPHICS -->
<div id="graphic-1" class="graphic-box" style="opacity: 0;">
<div class="label">47% NEED MOTION GRAPHICS</div>
<div class="progress-bar-container">
<div class="progress-bar-bg"></div>
<div id="bar-1" class="progress-bar-fill"></div>
</div>
<div id="counter-1" class="counter">00%</div>
</div>
<!-- Graphic 2: 62% LOSE ATTENTION -->
<div id="graphic-2" class="graphic-box" style="opacity: 0;">
<div class="label">62% LOSE ATTENTION</div>
<div class="chart-container">
<div class="bar-group">
<div class="bar-label">STATIC</div>
<div class="bar-wrapper"><div id="bar-2-static" class="bar-fill gray"></div></div>
</div>
<div class="bar-group">
<div class="bar-label">MOTION</div>
<div id="counter-2" class="counter small">00%</div>
<div class="bar-wrapper"><div id="bar-2-motion" class="bar-fill green"></div></div>
</div>
</div>
</div>
<!-- Graphic 3: 75% LACK SKILLS -->
<div id="graphic-3" class="graphic-box" style="opacity: 0;">
<div class="label">75% LACK SKILLS</div>
<div class="radial-container">
<div class="radial-svg-wrapper">
<svg viewBox="0 0 100 100" class="radial-svg">
<circle cx="50" cy="50" r="45" class="radial-bg" />
<circle id="radial-fill" cx="50" cy="50" r="45" class="radial-progress" />
</svg>
</div>
<div id="counter-3" class="counter">00%</div>
</div>
</div>
</div>
<style>
@import url('https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&display=swap');
[data-composition-id="data-graphics"] {
background: transparent;
font-family: 'Space Mono', monospace;
text-transform: uppercase;
color: #8A8A8A;
overflow: hidden;
width: 1920px;
height: 1080px;
}
[data-composition-id="data-graphics"] .grid-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: grid;
grid-template-columns: repeat(24, 1fr);
grid-template-rows: repeat(12, 1fr);
padding: 40px;
gap: 0; /* Removed gap for precise grid snapping */
}
[data-composition-id="data-graphics"] .graphic-box {
grid-column: 18 / 25; /* Positioned on the right side for A-roll scale down */
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(138, 138, 138, 0.4);
padding: 30px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
}
[data-composition-id="data-graphics"] #graphic-1 { grid-row: 2 / 5; margin-bottom: 10px; }
[data-composition-id="data-graphics"] #graphic-2 { grid-row: 5 / 8; margin-bottom: 10px; }
[data-composition-id="data-graphics"] #graphic-3 { grid-row: 8 / 11; }
[data-composition-id="data-graphics"] .label {
font-size: 20px;
font-weight: 700;
margin-bottom: 20px;
letter-spacing: 1px;
color: rgba(255, 255, 255, 0.9);
}
[data-composition-id="data-graphics"] .counter {
font-size: 48px;
font-weight: 700;
color: rgba(51, 255, 102, 0.9); /* Softened green with opacity */
margin-top: 15px;
}
[data-composition-id="data-graphics"] .counter.small {
font-size: 32px;
margin-top: 0;
margin-bottom: 10px;
}
/* Progress Bar Styles */
[data-composition-id="data-graphics"] .progress-bar-container {
width: 100%;
height: 24px;
position: relative;
background: rgba(138, 138, 138, 0.1);
border: 1px solid rgba(138, 138, 138, 0.3);
}
[data-composition-id="data-graphics"] .progress-bar-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: repeating-linear-gradient(90deg, rgba(138, 138, 138, 0.2), rgba(138, 138, 138, 0.2) 2px, transparent 2px, transparent 12px);
}
[data-composition-id="data-graphics"] .progress-bar-fill {
position: absolute;
top: 0;
left: 0;
height: 100%;
background: rgba(51, 255, 102, 0.8); /* Softened green */
width: 0%;
}
/* Bar Chart Styles */
[data-composition-id="data-graphics"] .chart-container {
width: 100%;
display: flex;
gap: 30px;
height: 140px;
align-items: flex-end;
}
[data-composition-id="data-graphics"] .bar-group {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
[data-composition-id="data-graphics"] .bar-label {
font-size: 12px;
margin-bottom: 10px;
color: rgba(138, 138, 138, 0.8);
}
[data-composition-id="data-graphics"] .bar-wrapper {
width: 100%;
height: 100px;
background: rgba(138, 138, 138, 0.1);
border: 1px solid rgba(138, 138, 138, 0.3);
position: relative;
}
[data-composition-id="data-graphics"] .bar-fill {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 0%;
}
[data-composition-id="data-graphics"] .bar-fill.green { background: rgba(51, 255, 102, 0.8); }
[data-composition-id="data-graphics"] .bar-fill.gray { background: rgba(138, 138, 138, 0.5); }
/* Radial Styles */
[data-composition-id="data-graphics"] .radial-container {
width: 100%;
display: flex;
align-items: center;
gap: 40px;
}
[data-composition-id="data-graphics"] .radial-svg-wrapper {
width: 110px;
height: 110px;
transform: rotate(-90deg);
}
[data-composition-id="data-graphics"] .radial-svg {
width: 100%;
height: 100%;
}
[data-composition-id="data-graphics"] .radial-bg {
fill: none;
stroke: rgba(138, 138, 138, 0.2);
stroke-width: 10;
stroke-dasharray: 4 4;
}
[data-composition-id="data-graphics"] .radial-progress {
fill: none;
stroke: rgba(51, 255, 102, 0.8);
stroke-width: 10;
stroke-dasharray: 282.74; /* 2 * PI * 45 */
stroke-dashoffset: 282.74;
stroke-linecap: round;
}
</style>
<script>
(function() {
const tl = gsap.timeline({ paused: true });
const REVEAL_DURATION = 0.8;
const EASE = "power2.out";
const graphicSelectors = [
'[data-composition-id="data-graphics"] #graphic-1',
'[data-composition-id="data-graphics"] #graphic-2',
'[data-composition-id="data-graphics"] #graphic-3'
];
// Helper for counter animation
function animateCounter(id, target, duration, startTime) {
const obj = { val: 0 };
const el = document.querySelector(`[data-composition-id="data-graphics"] #${id}`);
if (!el) return;
tl.to(obj, {
val: target,
duration: duration,
ease: EASE,
onUpdate: () => {
el.innerText = Math.floor(obj.val).toString().padStart(2, '0') + '%';
}
}, startTime);
}
gsap.set(graphicSelectors, { x: 24 });
// 1. 47% NEED MOTION GRAPHICS at 1.839s
tl.to('[data-composition-id="data-graphics"] #graphic-1', {
opacity: 1,
x: 0,
duration: 0.5,
ease: "power2.out"
}, 1.839);
tl.to('[data-composition-id="data-graphics"] #bar-1', {
width: '47%',
duration: REVEAL_DURATION,
ease: EASE
}, 1.839 + 0.2);
animateCounter('counter-1', 47, REVEAL_DURATION, 1.839 + 0.2);
// 2. 62% LOSE ATTENTION at 4.659s
tl.to('[data-composition-id="data-graphics"] #graphic-2', {
opacity: 1,
duration: 0.5,
ease: "power2.out"
}, 4.659);
tl.to('[data-composition-id="data-graphics"] #bar-2-static', {
height: '30%',
duration: REVEAL_DURATION,
ease: EASE
}, 4.659 + 0.2);
tl.to('[data-composition-id="data-graphics"] #bar-2-motion', {
height: '62%',
duration: REVEAL_DURATION,
ease: EASE
}, 4.659 + 0.4);
animateCounter('counter-2', 62, REVEAL_DURATION, 4.659 + 0.4);
// 3. 75% LACK SKILLS at 8.88s
tl.to('[data-composition-id="data-graphics"] #graphic-3', {
opacity: 1,
duration: 0.5,
ease: "power2.out"
}, 8.88);
const circumference = 282.74;
const offset = circumference - (0.75 * circumference);
tl.to('[data-composition-id="data-graphics"] #radial-fill', {
strokeDashoffset: offset,
duration: REVEAL_DURATION,
ease: EASE
}, 8.88 + 0.2);
animateCounter('counter-3', 75, REVEAL_DURATION, 8.88 + 0.2);
// A one-way settle keeps the UI alive without repeat/yoyo timeline noise.
const greenElements = [
'[data-composition-id="data-graphics"] .progress-bar-fill',
'[data-composition-id="data-graphics"] .bar-fill.green',
'[data-composition-id="data-graphics"] .radial-progress',
'[data-composition-id="data-graphics"] .counter'
];
gsap.set(greenElements, { opacity: 0.85 });
tl.to(greenElements, {
opacity: 1,
duration: 1.2,
stagger: 0.08,
ease: "none"
}, 2);
// Outro: Fade all out near the end
tl.to(['[data-composition-id="data-graphics"] #graphic-1', '[data-composition-id="data-graphics"] #graphic-2', '[data-composition-id="data-graphics"] #graphic-3'], {
opacity: 0,
y: 20,
duration: 0.5,
ease: "power2.in"
}, 15.5);
window.__timelines["data-graphics"] = tl;
})();
</script>
</div>
</template>