feat(registry): add yt-avatar-pip — circle pip window

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jbernard077
2026-07-06 12:50:58 -04:00
co-authored by Claude Fable 5
parent a4ef52fcb0
commit d924630f23
4 changed files with 176 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
---
title: "Circle PiP Window"
description: "Circular or rounded-rect masked picture-in-picture window with ring and shadow for a second video or image; pop in/out helpers"
---
# Circle PiP Window
Circular or rounded-rect masked picture-in-picture window with ring and shadow for a second video or image; pop in/out helpers
`pip` `overlay` `creator`
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/yt-avatar-pip.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/yt-avatar-pip.png" autoPlay muted loop playsInline />
## Install
<CodeGroup>
```bash Terminal
npx hyperframes add yt-avatar-pip
```
</CodeGroup>
## Details
| Property | Value |
| --- | --- |
| Type | Component |
## Files
| File | Target | Type |
| --- | --- | --- |
| `yt-avatar-pip.html` | `compositions/components/yt-avatar-pip.html` | hyperframes:snippet |
## Usage
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
@@ -0,0 +1,19 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "yt-avatar-pip",
"type": "hyperframes:component",
"title": "Circle PiP Window",
"description": "Circular or rounded-rect masked picture-in-picture window with ring and shadow for a second video or image; pop in/out helpers",
"tags": [
"pip",
"overlay",
"creator"
],
"files": [
{
"path": "yt-avatar-pip.html",
"target": "compositions/components/yt-avatar-pip.html",
"type": "hyperframes:snippet"
}
]
}
+115
View File
@@ -0,0 +1,115 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: transparent;
overflow: hidden;
}
/* ---- yt-avatar-pip ---------------------------------------------------
The screen-recording PiP: a circular (or rounded-rect) masked window
for a second video/image — one drop replaces the manual
crop-mask-resize dance. Size via --yt-pip-size, framing via an
inline transform scale on the inner media (an "inside scale"). */
.yt-pip {
--yt-pip-size: 300px;
--yt-pip-ring: rgba(255, 255, 255, 0.92);
--yt-pip-ring-w: 4px;
position: absolute;
width: var(--yt-pip-size);
height: var(--yt-pip-size);
border-radius: 50%;
overflow: hidden;
border: var(--yt-pip-ring-w) solid var(--yt-pip-ring);
box-shadow: 0 18px 50px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
.yt-pip--rect {
border-radius: 22px;
width: calc(var(--yt-pip-size) * 1.55);
}
.yt-pip > img,
.yt-pip > video,
.yt-pip > .yt-pip-fill {
width: 100%;
height: 100%;
object-fit: cover;
}
/* self-preview stand-in (not part of the snippet) */
.yt-pip-fill {
display: flex;
align-items: center;
justify-content: center;
font-family: "Inter", ui-sans-serif, sans-serif;
font-weight: 700;
font-size: 92px;
color: rgba(255, 255, 255, 0.95);
background: linear-gradient(135deg, #ff8fb8 0%, #7d5cff 100%);
}
</style>
</head>
<body>
<!-- Self-preview wrapper. WIRING: copy the .yt-pip CSS and the markup
pattern below into your host; put a <video data-start ...> or <img>
inside. The media element must live in the HOST root (this pattern
is copied in) — never inside a mounted sub-composition: sub-comps
are never seeked, so framework-owned media there renders black. Position via top/right/bottom/left; add class="clip" +
data-start/duration/track-index for host windowing. Framing: style
the inner media with transform: scale(1.15) etc. Then:
ytPipIn(tl, "#yt-my-pip", 2.0);
ytPipOut(tl, "#yt-my-pip", 12.0);
-->
<div
id="yt-pip-root"
data-composition-id="yt-avatar-pip"
data-start="0"
data-duration="4"
data-width="1920"
data-height="1080"
>
<div class="yt-pip" id="yt-pip-demo" style="left: 120px; bottom: 100px">
<div class="yt-pip-fill">AB</div>
</div>
<div
id="yt-pip-drv"
class="clip"
data-start="0"
data-duration="4"
data-track-index="0"
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
// ---- copy these helpers into the host script ----
window.ytPipIn = function (tl, target, at) {
gsap.set(target, { opacity: 0, scale: 0.55, y: 26 });
tl.to(target, { opacity: 1, scale: 1, y: 0, duration: 0.55, ease: "back.out(1.5)" }, at);
};
window.ytPipOut = function (tl, target, at) {
tl.to(target, { opacity: 0, scale: 0.75, y: 18, duration: 0.35, ease: "power2.in" }, at);
};
// Self-preview timeline (not part of the snippet you copy).
var tl = gsap.timeline({ paused: true });
window.ytPipIn(tl, "#yt-pip-demo", 0.4);
window.ytPipOut(tl, "#yt-pip-demo", 3.4);
tl.set("#yt-pip-root", { visibility: "hidden" }, 3.98);
window.__timelines["yt-avatar-pip"] = tl;
})();
</script>
</body>
</html>
+4
View File
@@ -582,6 +582,10 @@
{ {
"name": "yt-camera-move", "name": "yt-camera-move",
"type": "hyperframes:component" "type": "hyperframes:component"
},
{
"name": "yt-avatar-pip",
"type": "hyperframes:component"
} }
] ]
} }