diff --git a/docs/catalog/components/vignette.mdx b/docs/catalog/components/vignette.mdx
new file mode 100644
index 000000000..2a0d500a6
--- /dev/null
+++ b/docs/catalog/components/vignette.mdx
@@ -0,0 +1,38 @@
+---
+title: "Vignette"
+description: "Cinematic radial vignette overlay using a pure-CSS gradient — darkens the edges to pull focus toward the center"
+---
+
+# Vignette
+
+Cinematic radial vignette overlay using a pure-CSS gradient — darkens the edges to pull focus toward the center
+
+`vignette` `overlay` `cinematic` `effect`
+
+
+
+## Install
+
+
+
+```bash Terminal
+npx hyperframes add vignette
+```
+
+
+
+## Details
+
+| Property | Value |
+| --- | --- |
+| Type | Component |
+
+## Files
+
+| File | Target | Type |
+| --- | --- | --- |
+| `vignette.html` | `compositions/components/vignette.html` | hyperframes:snippet |
+
+## Usage
+
+Open `compositions/components/vignette.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
diff --git a/docs/docs.json b/docs/docs.json
index ed4095bcd..f85035cec 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -180,7 +180,8 @@
"catalog/components/grain-overlay",
"catalog/components/grid-pixelate-wipe",
"catalog/components/shimmer-sweep",
- "catalog/components/texture-mask-text"
+ "catalog/components/texture-mask-text",
+ "catalog/components/vignette"
]
},
{
diff --git a/docs/public/catalog-index.json b/docs/public/catalog-index.json
index 736651727..b25cab621 100644
--- a/docs/public/catalog-index.json
+++ b/docs/public/catalog-index.json
@@ -650,6 +650,20 @@
"href": "/catalog/blocks/vfx-text-cursor",
"preview": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/vfx-text-cursor.png"
},
+ {
+ "name": "vignette",
+ "type": "component",
+ "title": "Vignette",
+ "description": "Cinematic radial vignette overlay using a pure-CSS gradient — darkens the edges to pull focus toward the center",
+ "tags": [
+ "vignette",
+ "overlay",
+ "cinematic",
+ "effect"
+ ],
+ "href": "/catalog/components/vignette",
+ "preview": "https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/vignette.png"
+ },
{
"name": "vpn-youtube-spot",
"type": "block",
diff --git a/packages/studio/src/player/hooks/usePlaybackKeyboard.test.ts b/packages/studio/src/player/hooks/usePlaybackKeyboard.test.ts
index 679cf617e..ab6f7b623 100644
--- a/packages/studio/src/player/hooks/usePlaybackKeyboard.test.ts
+++ b/packages/studio/src/player/hooks/usePlaybackKeyboard.test.ts
@@ -93,7 +93,7 @@ describe("usePlaybackKeyboard — keyboard layout independence (#834)", () => {
dispatch(keydown({ code: "KeyA", key: "a" }));
});
- expect(spies.seek).toHaveBeenCalledWith(1.5);
+ expect(spies.seek).toHaveBeenCalledWith(1.5, { keepPlaying: true });
});
it("'Jump to in-point' fires on AZERTY (physical KeyQ produces e.key='a')", () => {
@@ -104,7 +104,7 @@ describe("usePlaybackKeyboard — keyboard layout independence (#834)", () => {
dispatch(keydown({ code: "KeyQ", key: "a" }));
});
- expect(spies.seek).toHaveBeenCalledWith(2.5);
+ expect(spies.seek).toHaveBeenCalledWith(2.5, { keepPlaying: true });
});
it("AZERTY 'A' physical key (e.key='q') no longer triggers in-point seek", () => {
diff --git a/registry/components/vignette/demo.html b/registry/components/vignette/demo.html
new file mode 100644
index 000000000..d96e7ef03
--- /dev/null
+++ b/registry/components/vignette/demo.html
@@ -0,0 +1,232 @@
+
+
+
+
+
+ Vignette — Demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
VIGNETTE
+
Frame the eye. Hold the moment.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/registry/components/vignette/registry-item.json b/registry/components/vignette/registry-item.json
new file mode 100644
index 000000000..71a11291c
--- /dev/null
+++ b/registry/components/vignette/registry-item.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
+ "name": "vignette",
+ "type": "hyperframes:component",
+ "title": "Vignette",
+ "description": "Cinematic radial vignette overlay using a pure-CSS gradient — darkens the edges to pull focus toward the center",
+ "tags": ["vignette", "overlay", "cinematic", "effect"],
+ "files": [
+ {
+ "path": "vignette.html",
+ "target": "compositions/components/vignette.html",
+ "type": "hyperframes:snippet"
+ }
+ ]
+}
diff --git a/registry/components/vignette/vignette.html b/registry/components/vignette/vignette.html
new file mode 100644
index 000000000..18ba2d0d5
--- /dev/null
+++ b/registry/components/vignette/vignette.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
diff --git a/registry/registry.json b/registry/registry.json
index 9becb0bbd..415976368 100644
--- a/registry/registry.json
+++ b/registry/registry.json
@@ -63,6 +63,10 @@
"name": "texture-mask-text",
"type": "hyperframes:component"
},
+ {
+ "name": "vignette",
+ "type": "hyperframes:component"
+ },
{
"name": "instagram-follow",
"type": "hyperframes:block"
diff --git a/scripts/generate-catalog-previews.ts b/scripts/generate-catalog-previews.ts
index 32b62fda3..8e0889a2f 100644
--- a/scripts/generate-catalog-previews.ts
+++ b/scripts/generate-catalog-previews.ts
@@ -243,7 +243,7 @@ async function generateThumbnail(item: CatalogItem, projectDir: string): Promise
const session = await createCaptureSession(fileServer.url, framesDir, {
width,
height,
- fps: 30,
+ fps: { num: 30, den: 1 },
format: "png",
});
await initializeSession(session);
@@ -276,7 +276,7 @@ async function generateVideo(item: CatalogItem, projectDir: string): Promise