mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 14:50:02 +00:00
feat(skills): product-launch-video skill + consolidate motion knowledge into hyperframes-animation (#1745)
* feat(skills): product-launch-video + consolidate motion knowledge into hyperframes-animation
- Add the product-launch-video skill: shot-sequence architecture where each
visual frame is a time-coded shot sequence picked from a blueprint menu and
paced to the voiceover (anti-PowerPoint). Includes the frame-worker sub-agent,
story/visual/motion-design references, and audio/captions/transitions/
stage-assets/assemble-index scripts.
- Consolidate motion knowledge in hyperframes-animation as the single source of
truth: promote the updated atomic rules (31 -> 36) and rename product-launch-
video's archetypes into hyperframes-animation blueprints (13 -> 15, replacing
the old set). product-launch-video, faceless-explainer, and pr-to-video now
reference them via ../hyperframes-animation/{rules-index,blueprints-index}.md
and the rules/blueprints dirs. Fixes the discrete-text-sequence broken links;
blueprints no longer ship per-id runnable examples, so example references in
the consumers were dropped.
- Default HeyGen TTS voice to Marcia (deterministic; was the API's first English
voice, which drifts on catalog re-sort). Override with --voice.
- assemble-index pre-assembly frame guards: auto-repair a sub-comp root missing
canvas dims; hard-fail on <video>/<audio> inside a sub-comp; hard-fail on a
timed non-root element missing class="clip" or overlapping same-track clips.
- Lint/CLI: lint media inside sub-compositions as an error; stop false-positive
caption layout/lint findings; contrast/layout-audit skip elements hidden by an
invisible ancestor.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(skills): clear CodeQL alerts in assemble-index.mjs
- script/style blanking regex now matches closing tags with trailing
whitespace (</script >, </style >) — js/bad-tag-filter (high).
- drop the existsSync precheck before reading/repairing a frame file; read
directly and handle ENOENT, removing the check->write TOCTOU window —
js/file-system-race (high).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
413d8187fd
commit
05af482f22
@@ -36,7 +36,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { parseStoryboard } from "./lib/storyboard.mjs";
|
||||
import { captionBand, parseFormat } from "./lib/dimensions.mjs";
|
||||
import { parseColors, parseFonts, semanticColors } from "./lib/tokens.mjs";
|
||||
import { parseColors, parseFonts, semanticColors, lum } from "./lib/tokens.mjs";
|
||||
|
||||
const flag = (argv, name, def) => {
|
||||
const i = argv.indexOf(`--${name}`);
|
||||
@@ -276,6 +276,20 @@ function buildFromSkin(skin, groups, total, W, H, tokens, die, faces = "", fonts
|
||||
// overflow:hidden — nothing is clipped); zeroing it would need an airy line-height that
|
||||
// balloons the pill, which is worse. Override only if a brand font genuinely clips.
|
||||
out += "\n<style>\n .caption-line { line-height: 1.1 !important; }\n</style>";
|
||||
// dark-ground caption contrast (auto). The preset caption skins are tuned for a LIGHT
|
||||
// pill (cream); on a dark brand ground the pill goes near-black, so the skin's faint
|
||||
// upcoming-word mix and light highlight block turn unreadable. When the resolved caption
|
||||
// canvas is dark, override the three word states: a brighter muted upcoming color + an
|
||||
// on-brand ACCENT highlight block with light text. Light grounds are left untouched.
|
||||
const capCanvas = (tokens.match(/--cap-canvas:\s*(#[0-9a-fA-F]{6})/) || [])[1];
|
||||
if (capCanvas && (lum(capCanvas) ?? 255) < 90) {
|
||||
out +=
|
||||
"\n<style>\n" +
|
||||
" .caption-word { color: color-mix(in srgb, var(--cap-ink) 64%, var(--cap-canvas)); }\n" +
|
||||
" .caption-word.is-active { color: var(--cap-ink); background: var(--cap-accent); box-shadow: 0 0 0 0.06em var(--cap-accent); }\n" +
|
||||
" .caption-word.is-spoken { color: var(--cap-ink); background: transparent; box-shadow: none; }\n" +
|
||||
"</style>";
|
||||
}
|
||||
return `<template id="captions-template" data-composition-id="captions" data-width="${W}" data-height="${H}">\n${out.trim()}\n</template>\n`;
|
||||
}
|
||||
|
||||
@@ -291,8 +305,11 @@ function brandFontFaces(framePath, hyperframesDir) {
|
||||
];
|
||||
if (!families.length) return "";
|
||||
const dirs = [
|
||||
{ abs: join(hyperframesDir, "assets/fonts"), rel: "../assets/fonts" },
|
||||
{ abs: join(hyperframesDir, "capture/assets/fonts"), rel: "../capture/assets/fonts" },
|
||||
// ROOT-RELATIVE — compositions are served with the project root as their base URL, so a
|
||||
// "../" prefix escapes the root (lint: invalid_parent_traversal_in_asset_path) and 404s in
|
||||
// Studio/preview. Mirror what the frame workers use for images.
|
||||
{ abs: join(hyperframesDir, "assets/fonts"), rel: "assets/fonts" },
|
||||
{ abs: join(hyperframesDir, "capture/assets/fonts"), rel: "capture/assets/fonts" },
|
||||
].filter((d) => existsSync(d.abs));
|
||||
const weightOf = (n) => {
|
||||
const s = n.toLowerCase();
|
||||
|
||||
Reference in New Issue
Block a user