mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
fix(templates): resolve all lint errors and warnings in bundled templates (#197)
## What Added GSAP CDN imports to all template compositions and improved project linting to skip template placeholders. ## Why Templates were missing GSAP script imports, causing JavaScript errors when GSAP animations tried to execute. The linter was also incorrectly flagging template placeholder values like `__VIDEO_SRC__` as missing audio sources. ## How - Added `<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>` to all composition HTML files - Updated `lintAudioSrcNotFound` function to skip template placeholders matching the pattern `__[A-Z_]+__` - Fixed GSAP animation properties by adding `overwrite: "auto"` to prevent conflicts - Replaced CSS transforms with GSAP `gsap.set()` calls for better animation control - Moved video elements outside timed containers in some templates to avoid nesting issues - Standardized transcript data format to use double quotes for JSON consistency - Added proper timeline initialization with `window.__timelines = window.__timelines || {}` - Enhanced caption styling with overflow handling and max-width constraints ## Test plan - [x] Manual testing performed - [x] Verified GSAP animations work correctly in all templates - [x] Confirmed linter no longer flags template placeholders as errors - [x] Tested video playback and audio synchronization
This commit is contained in:
@@ -130,6 +130,7 @@ function lintAudioSrcNotFound(projectDir: string, htmlSources: string[]): Hyperf
|
||||
while ((match = audioSrcRe.exec(html)) !== null) {
|
||||
const src = match[1]!;
|
||||
if (/^(https?:|data:|blob:)/i.test(src)) continue;
|
||||
if (/^__[A-Z_]+__$/.test(src)) continue; // Skip template placeholders
|
||||
const resolved = resolve(projectDir, src);
|
||||
if (!existsSync(resolved)) {
|
||||
missingSrcs.push(src);
|
||||
|
||||
@@ -272,6 +272,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
@@ -358,13 +359,21 @@
|
||||
}, "hold3+=1");
|
||||
|
||||
// Visual click feedback
|
||||
tl.to("#cursor", { scale: 0.8, duration: 0.05, yoyo: true, repeat: 1 }, "hold3+=1");
|
||||
tl.to(
|
||||
"#cursor",
|
||||
{ scale: 0.8, duration: 0.05, yoyo: true, repeat: 1, overwrite: "auto" },
|
||||
"hold3+=1",
|
||||
);
|
||||
|
||||
// 11. Hold 0.3s
|
||||
tl.addLabel("hold4", "+=0.3");
|
||||
|
||||
// 12. Cursor double-clicks "Pythom" — highlight in blue
|
||||
tl.to("#cursor", { scale: 0.8, duration: 0.05, yoyo: true, repeat: 3 }, "hold4");
|
||||
tl.to(
|
||||
"#cursor",
|
||||
{ scale: 0.8, duration: 0.05, yoyo: true, repeat: 3, overwrite: "auto" },
|
||||
"hold4",
|
||||
);
|
||||
tl.add(() => {
|
||||
const textEl = document.querySelector("#python-text");
|
||||
if (!textEl) return;
|
||||
@@ -400,8 +409,12 @@
|
||||
tl.addLabel("hold6", typingEnd + 0.5);
|
||||
|
||||
// 16. Cursor moves slightly and clicks away to deselect
|
||||
tl.to("#cursor", { left: 500, top: 580, duration: 0.3 }, "hold6");
|
||||
tl.to("#cursor", { scale: 0.8, duration: 0.05, yoyo: true, repeat: 1 }, "hold6+=0.3");
|
||||
tl.to("#cursor", { left: 500, top: 580, duration: 0.3, overwrite: "auto" }, "hold6");
|
||||
tl.to(
|
||||
"#cursor",
|
||||
{ scale: 0.8, duration: 0.05, yoyo: true, repeat: 1, overwrite: "auto" },
|
||||
"hold6+=0.3",
|
||||
);
|
||||
tl.to(".selection-border", { opacity: 0, duration: 0.1 }, "hold6+=0.3");
|
||||
|
||||
// 17. 👍 emoji pops in
|
||||
@@ -413,6 +426,7 @@
|
||||
// 19. Fade entire screen to white over 0.5s
|
||||
tl.to("#fade-overlay", { opacity: 1, duration: 0.5 }, "hold7");
|
||||
|
||||
window.__timelines = window.__timelines || {};
|
||||
window.__timelines["decision-tree"] = tl;
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -215,7 +215,6 @@
|
||||
display: flex;
|
||||
padding: 40px;
|
||||
color: black;
|
||||
transform: translateX(100%);
|
||||
z-index: 2;
|
||||
}
|
||||
.swiss-grid {
|
||||
@@ -492,6 +491,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
@@ -523,8 +523,9 @@
|
||||
tl.to("#white-wipe", { xPercent: 100, duration: 0.2, ease: "power2.out" }, 2.2);
|
||||
|
||||
// --- Scene 2: Swiss Style (2.0s - 5.0s) ---
|
||||
gsap.set(".swiss-panel", { xPercent: 100 });
|
||||
tl.to("#scene-2", { opacity: 1, duration: 0.1 }, 2.2);
|
||||
tl.to(".swiss-panel", { x: 0, duration: 0.8, ease: "expo.out" }, 2.3);
|
||||
tl.to(".swiss-panel", { xPercent: 0, duration: 0.8, ease: "expo.out" }, 2.3);
|
||||
tl.to(".swiss-title", { opacity: 1, duration: 0.1 }, 2.5);
|
||||
tl.to(".swiss-subtitle", { opacity: 1, y: 0, duration: 0.4 }, 2.7);
|
||||
tl.to(
|
||||
@@ -534,7 +535,7 @@
|
||||
);
|
||||
tl.to(".swiss-circle", { opacity: 1, scale: 1.2, duration: 0.6, ease: "back.out" }, 3.2);
|
||||
tl.to(".swiss-square", { opacity: 1, rotation: 45, duration: 0.6, ease: "back.out" }, 3.4);
|
||||
tl.to(".swiss-panel", { x: "120%", duration: 0.7, ease: "expo.in" }, 4.7);
|
||||
tl.to(".swiss-panel", { xPercent: 120, duration: 0.7, ease: "expo.in" }, 4.7);
|
||||
tl.to("#scene-2", { opacity: 0, duration: 0.1 }, 5.0);
|
||||
|
||||
// --- Scene 3: Figma Config Style (5.3s - 9.6s) ---
|
||||
|
||||
@@ -443,6 +443,7 @@
|
||||
);
|
||||
|
||||
// Register timeline
|
||||
window.__timelines = window.__timelines || {};
|
||||
window.__timelines["nyt-chart"] = tl;
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const TRANSCRIPT = [
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
@@ -133,7 +133,6 @@
|
||||
<div
|
||||
id="aroll-comp"
|
||||
data-composition-id="aroll-layer"
|
||||
data-start="0"
|
||||
data-duration="__VIDEO_DURATION__"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
@@ -220,15 +219,6 @@
|
||||
data-track-index="150"
|
||||
></div>
|
||||
|
||||
<!-- Audio Clips -->
|
||||
<audio id="sfx-bounce-0" src="" data-start="0" data-track-index="5"></audio>
|
||||
<audio id="sfx-whoosh-1" src="" data-start="1.839" data-track-index="6"></audio>
|
||||
<audio id="sfx-pop-1" src="" data-start="2.039" data-track-index="7"></audio>
|
||||
<audio id="sfx-whoosh-2" src="" data-start="4.659" data-track-index="6"></audio>
|
||||
<audio id="sfx-pop-2" src="" data-start="4.859" data-track-index="7"></audio>
|
||||
<audio id="sfx-whoosh-3" src="" data-start="8.88" data-track-index="6"></audio>
|
||||
<audio id="sfx-pop-3" src="" data-start="9.08" data-track-index="7"></audio>
|
||||
|
||||
<script>
|
||||
window.__timelines = window.__timelines || {};
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
@@ -132,7 +132,6 @@
|
||||
left: 50%;
|
||||
width: 1440px;
|
||||
height: 900px;
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
background: white;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
|
||||
@@ -235,19 +234,16 @@
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
border-radius: 4px;
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
.width-label {
|
||||
top: -30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) scale(0);
|
||||
}
|
||||
|
||||
.height-label {
|
||||
right: -45px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) rotate(90deg) scale(0);
|
||||
}
|
||||
|
||||
.selection-handles {
|
||||
@@ -296,7 +292,6 @@
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.1);
|
||||
transform: translateX(-320px);
|
||||
padding: 24px;
|
||||
z-index: 10;
|
||||
box-shadow: 4px 0 24px rgba(0, 0, 0, 0.2);
|
||||
@@ -421,7 +416,6 @@
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
|
||||
.cursor {
|
||||
@@ -455,6 +449,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
@@ -462,6 +457,11 @@
|
||||
// Organic Cursor Ease
|
||||
const cursorEase = "back.out(1.2)";
|
||||
|
||||
gsap.set(".web-frame", { xPercent: -50, yPercent: -50, scale: 0 });
|
||||
gsap.set(".width-label", { xPercent: -50, scale: 0 });
|
||||
gsap.set(".height-label", { yPercent: -50, rotation: 90, scale: 0 });
|
||||
gsap.set(".comment-bubble", { x: -10 });
|
||||
gsap.set(".assets-panel", { x: -320 });
|
||||
gsap.set(".designer-cursor", { x: 200, y: 200 });
|
||||
gsap.set(".engineer-cursor", { x: -100, y: 500 });
|
||||
gsap.set(".pm-cursor", { x: 2000, y: 1100 });
|
||||
@@ -489,7 +489,11 @@
|
||||
// Collaboration
|
||||
tl.to(".engineer-cursor", { x: 400, y: 400, duration: 1.2, ease: cursorEase }, 3.5);
|
||||
tl.to(".pm-cursor", { x: 1500, y: 800, duration: 1.5, ease: cursorEase }, 3.7);
|
||||
tl.to(".engineer-cursor", { x: 960, y: 680, duration: 0.9, ease: cursorEase }, 4.5);
|
||||
tl.to(
|
||||
".engineer-cursor",
|
||||
{ x: 960, y: 680, duration: 0.9, ease: cursorEase, overwrite: "auto" },
|
||||
4.5,
|
||||
);
|
||||
tl.to(".selection-handles", { opacity: 1, duration: 0.2 }, 5.3);
|
||||
tl.to(".cta-button", { outline: "2px solid #1ABCFE", duration: 0.1 }, 5.3);
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
@@ -45,9 +45,12 @@
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
max-width: 1600px;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const TRANSCRIPT = [
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
@@ -85,36 +85,7 @@
|
||||
data-duration="__VIDEO_DURATION__"
|
||||
>
|
||||
<!-- Background Grid -->
|
||||
<img
|
||||
id="bg-grid"
|
||||
class="background-grid"
|
||||
src="assets/swiss-grid.svg"
|
||||
alt="Grid"
|
||||
data-start="0"
|
||||
data-duration="__VIDEO_DURATION__"
|
||||
data-track-index="0"
|
||||
/>
|
||||
|
||||
<!-- A-roll Video -->
|
||||
<div id="short_mag_cut_frame">
|
||||
<video
|
||||
id="short_mag_cut"
|
||||
src="__VIDEO_SRC__"
|
||||
muted
|
||||
playsinline
|
||||
data-start="0"
|
||||
data-duration="__VIDEO_DURATION__"
|
||||
data-track-index="1"
|
||||
></video>
|
||||
</div>
|
||||
<audio
|
||||
id="a-roll-audio"
|
||||
src="__VIDEO_SRC__"
|
||||
data-start="0"
|
||||
data-duration="__VIDEO_DURATION__"
|
||||
data-track-index="5"
|
||||
data-volume="1"
|
||||
></audio>
|
||||
<img id="bg-grid" class="background-grid" src="assets/swiss-grid.svg" alt="Grid" />
|
||||
|
||||
<!-- Intro Sub-composition -->
|
||||
<div
|
||||
@@ -149,12 +120,6 @@
|
||||
data-track-index="4"
|
||||
></div>
|
||||
|
||||
<!-- Sound Effects (src set dynamically; omitted to avoid 404s) -->
|
||||
<audio id="sfx-intro" data-start="0" data-track-index="5" data-duration="1"></audio>
|
||||
<audio id="sfx-stat1" data-start="1.86" data-track-index="5" data-duration="1"></audio>
|
||||
<audio id="sfx-stat2" data-start="4.68" data-track-index="5" data-duration="1"></audio>
|
||||
<audio id="sfx-stat3" data-start="8.88" data-track-index="5" data-duration="1"></audio>
|
||||
|
||||
<script>
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
@@ -230,5 +195,26 @@
|
||||
window.__timelines["master"] = masterTL;
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<!-- A-roll Video (direct child of body to avoid video_nested_in_timed_element) -->
|
||||
<div id="short_mag_cut_frame">
|
||||
<video
|
||||
id="short_mag_cut"
|
||||
src="__VIDEO_SRC__"
|
||||
muted
|
||||
playsinline
|
||||
data-start="0"
|
||||
data-duration="__VIDEO_DURATION__"
|
||||
data-track-index="1"
|
||||
></video>
|
||||
</div>
|
||||
<audio
|
||||
id="a-roll-audio"
|
||||
src="__VIDEO_SRC__"
|
||||
data-start="0"
|
||||
data-duration="__VIDEO_DURATION__"
|
||||
data-track-index="5"
|
||||
data-volume="1"
|
||||
></audio>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -50,9 +50,12 @@
|
||||
display: inline-block;
|
||||
letter-spacing: -0.02em; /* Tight Helvetica spacing */
|
||||
white-space: nowrap; /* Prevent wrapping at the element level */
|
||||
max-width: 900px;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const TRANSCRIPT = [
|
||||
@@ -171,6 +174,9 @@
|
||||
},
|
||||
group.end - 0.3,
|
||||
);
|
||||
|
||||
// Hard kill to ensure caption is hidden
|
||||
tl.set(div, { opacity: 0, visibility: "hidden" }, group.end);
|
||||
});
|
||||
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
@@ -222,6 +222,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
const TRANSCRIPT = [
|
||||
|
||||
@@ -108,27 +108,6 @@
|
||||
data-start="0"
|
||||
data-duration="__VIDEO_DURATION__"
|
||||
>
|
||||
<!-- A-Roll -->
|
||||
<div id="a-roll-wrapper">
|
||||
<video
|
||||
id="a-roll"
|
||||
src="__VIDEO_SRC__"
|
||||
muted
|
||||
playsinline
|
||||
data-start="0"
|
||||
data-duration="__VIDEO_DURATION__"
|
||||
data-track-index="0"
|
||||
></video>
|
||||
</div>
|
||||
<audio
|
||||
id="a-roll-audio"
|
||||
src="__VIDEO_SRC__"
|
||||
data-start="0"
|
||||
data-duration="__VIDEO_DURATION__"
|
||||
data-track-index="5"
|
||||
data-volume="1"
|
||||
></audio>
|
||||
|
||||
<!-- Transitions -->
|
||||
<div id="curtain-black" class="curtain"></div>
|
||||
<div id="curtain-red" class="curtain red"></div>
|
||||
@@ -186,5 +165,26 @@
|
||||
window.__timelines["main-comp"] = tl;
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<!-- A-Roll (direct child of body to avoid video_nested_in_timed_element) -->
|
||||
<div id="a-roll-wrapper">
|
||||
<video
|
||||
id="a-roll"
|
||||
src="__VIDEO_SRC__"
|
||||
muted
|
||||
playsinline
|
||||
data-start="0"
|
||||
data-duration="__VIDEO_DURATION__"
|
||||
data-track-index="0"
|
||||
></video>
|
||||
</div>
|
||||
<audio
|
||||
id="a-roll-audio"
|
||||
src="__VIDEO_SRC__"
|
||||
data-start="0"
|
||||
data-duration="__VIDEO_DURATION__"
|
||||
data-track-index="5"
|
||||
data-volume="1"
|
||||
></audio>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -43,9 +43,12 @@
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
max-width: 1600px;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const script = [
|
||||
@@ -114,6 +117,7 @@
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
// Fade in and set text
|
||||
tl.set(box, { visibility: "visible" }, line.start);
|
||||
tl.to(
|
||||
box,
|
||||
{
|
||||
@@ -137,6 +141,9 @@
|
||||
},
|
||||
line.end,
|
||||
);
|
||||
|
||||
// Hard kill to ensure caption is hidden
|
||||
tl.set(box, { opacity: 0, visibility: "hidden" }, line.end + 0.1);
|
||||
});
|
||||
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
@@ -175,29 +175,6 @@
|
||||
data-track-index="3"
|
||||
></div>
|
||||
|
||||
<!-- SFX -->
|
||||
<audio
|
||||
id="sfx-swipe-1"
|
||||
src=""
|
||||
data-start="1.8"
|
||||
data-duration="1"
|
||||
data-track-index="4"
|
||||
></audio>
|
||||
<audio
|
||||
id="sfx-swipe-2"
|
||||
src=""
|
||||
data-start="4.6"
|
||||
data-duration="1"
|
||||
data-track-index="4"
|
||||
></audio>
|
||||
<audio
|
||||
id="sfx-swipe-3"
|
||||
src=""
|
||||
data-start="8.8"
|
||||
data-duration="1"
|
||||
data-track-index="4"
|
||||
></audio>
|
||||
|
||||
<script>
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user