Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions skills-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@
"source": "heygen-com/hyperframes",
"skills": {
"embedded-captions": {
"hash": "7fa25370f9fbb4b3",
"hash": "62aec45830eda54b",
"files": 144
},
"faceless-explainer": {
"hash": "edbe47dd738d14d8",
"hash": "844c54a06fd16d3c",
"files": 17
},
"general-video": {
"hash": "a30225e30ec7b06c",
"files": 1
},
"hyperframes": {
"hash": "55f1e72887f8f983",
"hash": "e0cf1bb2843bc834",
"files": 1
},
"hyperframes-animation": {
"hash": "f789d7f95d9ad2fe",
"hash": "3b592d54ca1d5b6e",
"files": 116
},
"hyperframes-cli": {
Expand All @@ -30,7 +30,7 @@
"files": 13
},
"hyperframes-creative": {
"hash": "8573b34712e14ab1",
"hash": "18a14a79da6cbc06",
"files": 68
},
"hyperframes-media": {
Expand All @@ -42,23 +42,23 @@
"files": 10
},
"media-use": {
"hash": "f0edb0fd7cd513a5",
"hash": "fba6e0963e431b1e",
"files": 19
},
"motion-graphics": {
"hash": "be1d1f159d5eb0e4",
"files": 23
},
"music-to-video": {
"hash": "c188d0d159b926c2",
"hash": "0c5738fac0fe622f",
"files": 132
},
"pr-to-video": {
"hash": "ef4a3aa5a943aeec",
"hash": "132b44ddda774fef",
"files": 21
},
"product-launch-video": {
"hash": "b7bee220096f2ae2",
"hash": "32cab842cc7a6b76",
"files": 18
},
"remotion-to-hyperframes": {
Expand Down
2 changes: 1 addition & 1 deletion skills/embedded-captions/scripts/make-theme.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ function fontCssFor(pageParts) {
}
return out.join("\n");
}
const GSAP = `<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>`;
const GSAP = `<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js" integrity="sha384-sG0Hv1tP1lZCk9KQmrIbY/XNwi+OY84GQqhMscbnsoBFqAz8KNCil1kvfL3Hbbk2" crossorigin="anonymous"></script>`;

function bgSkeleton(stageHtml, css, js) {
const FONT_CSS = fontCssFor(stageHtml + css);
Expand Down
6 changes: 5 additions & 1 deletion skills/embedded-captions/scripts/transcribe.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,15 @@ function main() {
const outDir = path.join(project, "_wx_out");
fs.mkdirSync(outDir, { recursive: true });
const wxModel = model.replace(/\.en$/, ""); // whisperx model names are multilingual ids
// Pin whisperx so `uvx` fetches a reproducible build instead of resolving
// "latest" on every run (a supply-chain + determinism foot-gun). Override
// with $WHISPERX_VERSION if you've validated a different release.
const whisperxSpec = `whisperx==${process.env.WHISPERX_VERSION || "3.8.6"}`;
const wxArgs = [
"--python",
"3.12",
"--from",
"whisperx",
whisperxSpec,
"whisperx",
wav,
"--model",
Expand Down
2 changes: 1 addition & 1 deletion skills/faceless-explainer/scripts/assemble-index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ const html = `<!doctype html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=${WIDTH}, height=${HEIGHT}" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js" integrity="sha384-sG0Hv1tP1lZCk9KQmrIbY/XNwi+OY84GQqhMscbnsoBFqAz8KNCil1kvfL3Hbbk2" crossorigin="anonymous"></script>
<style>
${headStyle}
</style>
Expand Down
2 changes: 1 addition & 1 deletion skills/faceless-explainer/scripts/captions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ function buildCaptionsHtml(groups, total, W, H) {
color: rgba(255, 255, 255, 0.55);
}
</style>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js" integrity="sha384-sG0Hv1tP1lZCk9KQmrIbY/XNwi+OY84GQqhMscbnsoBFqAz8KNCil1kvfL3Hbbk2" crossorigin="anonymous"></script>
<script>
(function () {
var GROUPS = ${JSON.stringify(groups)};
Expand Down
10 changes: 10 additions & 0 deletions skills/hyperframes-animation/scripts/package-loader.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
// package-loader — bootstrap optional helper packages only when missing, with
// defense-in-depth so a malicious or typo'd dependency can't run on install:
// • specs are version-pinned (assertPinnedPackageSpecs) — no floating "latest"
// • install runs `npm install --ignore-scripts` — package lifecycle scripts
// never execute
// • `--no-save` into a throwaway tmp dir — the host project is left untouched
// • requires an interactive y/N (or an explicit $HYPERFRAMES_SKILL_BOOTSTRAP_DEPS=1)
// • npm is spawned with an argv array (no shell) — never a built command string
// The `installLine` strings below are DISPLAY ONLY (shown in the prompt / error
// text); they are never handed to a shell or executed.
import { spawnSync } from "node:child_process";
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
import { createRequire } from "node:module";
Expand Down
10 changes: 10 additions & 0 deletions skills/hyperframes-creative/scripts/package-loader.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
// package-loader — bootstrap optional helper packages only when missing, with
// defense-in-depth so a malicious or typo'd dependency can't run on install:
// • specs are version-pinned (assertPinnedPackageSpecs) — no floating "latest"
// • install runs `npm install --ignore-scripts` — package lifecycle scripts
// never execute
// • `--no-save` into a throwaway tmp dir — the host project is left untouched
// • requires an interactive y/N (or an explicit $HYPERFRAMES_SKILL_BOOTSTRAP_DEPS=1)
// • npm is spawned with an argv array (no shell) — never a built command string
// The `installLine` strings below are DISPLAY ONLY (shown in the prompt / error
// text); they are never handed to a shell or executed.
import { spawnSync } from "node:child_process";
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
import { createRequire } from "node:module";
Expand Down
34 changes: 17 additions & 17 deletions skills/hyperframes/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,24 @@ Routing needs to know **what the video is about** — its input and subject. If

## Workflow cheat-sheet

| Workflow | Use it for |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/product-launch-video` | Marketing / launching / promoting a **product** — from its URL, a brief, or a script (even if the site is only named) |
| `/website-to-video` | Turning a **general website** into a video — site tour, portfolio / landing-page showcase, social clip from the site's visuals |
| `/faceless-explainer` | **Explaining a topic / concept** from text — no product, no URL; every visual is LLM-invented |
| `/pr-to-video` | A **GitHub PR / code change** → changelog / feature-reveal / fix / refactor explainer |
| `/embedded-captions` | Adding **captions / subtitles** to an existing talking-head video (footage untouched) |
| `/talking-head-recut` | Packaging an existing talking-head video with **designed graphic overlays** — lower-thirds, data callouts, kinetic titles, pull-quotes |
| `/motion-graphics` | A short, **unnarrated, design-led motion graphic** — kinetic type, a stat / chart hit, a logo sting, a lower-third overlay |
| `/music-to-video` | A **music track** → a **beat-synced** video — lyric video, slideshow, or kinetic promo; the music drives pacing (optional user images / videos cut onto the beat grid) |
| `/slideshow` | A **presentation / pitch deck / interactive deck** — discrete slides, fragments, branching, hotspots; output is a navigable **deck**, not a rendered video |
| `/general-video` | **Anything else** — longer or multi-scene pieces, a static loop / poster, a custom composition |
| `/remotion-to-hyperframes` | **Porting an existing Remotion (React) composition** to HyperFrames (migration, not creation) |
| Workflow | Use it for |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/product-launch-video` | **Selling a product** (SaaS, app, company / product site) — from a URL, brief, or script → a **promo**. The default for any commercial URL, even if the site is only named. |
| `/website-to-video` | **Showing a site itself** a tour / showcase built from the site's own screenshots. For non-commercial sites (portfolio, blog, docs, personal, event), or when the user wants a tour, not a promo. |
| `/faceless-explainer` | **Explaining a topic / concept** from text — no product, no URL; every visual is LLM-invented |
| `/pr-to-video` | A **GitHub PR / code change** → changelog / feature-reveal / fix / refactor explainer |
| `/embedded-captions` | Adding **captions / subtitles** to an existing talking-head video (footage untouched) |
| `/talking-head-recut` | Packaging an existing talking-head video with **designed graphic overlays** — lower-thirds, data callouts, kinetic titles, pull-quotes |
| `/motion-graphics` | A short, **unnarrated, design-led motion graphic** — kinetic type, a stat / chart hit, a logo sting, a lower-third overlay |
| `/music-to-video` | A **music track** → a **beat-synced** video — lyric video, slideshow, or kinetic promo; the music drives pacing (optional user images / videos cut onto the beat grid) |
| `/slideshow` | A **presentation / pitch deck / interactive deck** — discrete slides, fragments, branching, hotspots; output is a navigable **deck**, not a rendered video |
| `/general-video` | **Anything else** — longer or multi-scene pieces, a static loop / poster, a custom composition |
| `/remotion-to-hyperframes` | **Porting an existing Remotion (React) composition** to HyperFrames (migration, not creation) |

**Disambiguation (only where confusable):**

- **Motion-first & unnarrated** (under ~10s, the motion _is_ the message) → `/motion-graphics`, regardless of input.
- **A URL or script** — markets a specific product (even just naming the site) → `/product-launch-video`; a general non-product site → `/website-to-video`; a GitHub PR link → `/pr-to-video`; explains a concept with no product / site → `/faceless-explainer`. Genuinely unclear product-vs-topic, or launch-vs-general-site → ask one question.
- **A URL or script** — ask one thing: _is the site selling a product?_ **Yes** (SaaS / app / product / company site) → `/product-launch-video` — a promo, and the default for any commercial URL even if the site is only named. **No**, or the user just wants the site shown as-is (portfolio / blog / docs / personal / event) → `/website-to-video` — a tour. A GitHub PR link → `/pr-to-video`; a concept with no product or site → `/faceless-explainer`.
- **Existing footage** — plain spoken-word subtitles → `/embedded-captions`; designed overlay cards → `/talking-head-recut`. Neither edits the footage itself (re-timing / recolor / reframe / reorder / audio is NLE editing — out of scope).
- **A music track is the input** (an audio file, or a video to pull audio from) with **no narration** → `/music-to-video` — the music's beats/energy drive the pacing. (Narrated pieces stay with the input-matched workflow above; `/motion-graphics` is for short unnarrated motion that isn't music-driven.)
- **A presentation / pitch deck / interactive deck** (discrete slides, navigation, presenter mode) → `/slideshow` — output is a navigable deck, not a rendered video. An explicit "slideshow" request proceeds directly; an adjacent trigger ("deck / slides / presentation / convert this page") makes `/slideshow` confirm it's a slideshow before authoring, and switch to the appropriate non-slideshow workflow if not.
Expand Down Expand Up @@ -99,13 +99,13 @@ The CLI also surfaces a one-line reminder when a `render` / `lint` / `validate`
### `/product-launch-video`

- **Input:** A product being marketed — **(a)** a product URL (crawled with headless Chrome for assets + brand tokens), **(b)** a script / brief that names the product's site even without a link (PLV resolves + crawls it, unless the user opts out), or **(c)** a script with no derivable site / "don't scrape" (no-capture mode — pick a style preset that supplies palette + design system). A supplied script can be the **verbatim** voice-over or **restructured** per scene — PLV asks.
- **Output:** product launch / SaaS promo as a HyperFrames composition → MP4. (sweet spot 30–90s).
- **Output:** a product launch / SaaS **promo** as a HyperFrames composition → MP4 (sweet spot 30–90s) — the product's value is the subject, not a walkthrough of the site. For a plain tour of the site, use `/website-to-video`.
- **Triggers:** "launch video for X", "promo for our site", "explain my SaaS in a minute", "turn my script into a 60s promo", "text-only launch video, don't scrape".

### `/website-to-video`

- **Input:** A **general website / URL** to turn into a video — when the goal is a video _of_ the site, not a product launch. Captured with headless Chrome for real screenshots + brand assets.
- **Output:** a site tour / portfolio / landing-page showcase / social clip built from the site's own visuals → MP4.
- **Input:** A website / URL whose goal is to show **the site itself**, not to sell a product. Best for non-commercial sites (portfolio, blog, docs, personal, event), or when the user explicitly wants a tour of a site as-is. Captured with headless Chrome for real screenshots + brand assets. If the site is selling something and the user wants a promo, use `/product-launch-video`.
- **Output:** a site tour / showcase / social clip built from the site's own visuals → MP4.
- **Triggers:** "turn this website into a video", "site tour from ", "social clip from our homepage", "I just have a URL — make something".

### `/faceless-explainer`
Expand Down
Loading
Loading