hyperframes 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +249 -103
- package/dist/hyperframe-runtime.js +3 -3
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +3 -3
- package/dist/skills/captions/SKILL.md +138 -0
- package/dist/skills/compose-video/SKILL.md +150 -0
- package/dist/skills/compose-video/data-in-motion.md +19 -0
- package/dist/skills/compose-video/house-style.md +130 -0
- package/dist/skills/compose-video/palettes/bold-energetic.md +14 -0
- package/dist/skills/compose-video/palettes/clean-corporate.md +14 -0
- package/dist/skills/compose-video/palettes/dark-premium.md +14 -0
- package/dist/skills/compose-video/palettes/jewel-rich.md +14 -0
- package/dist/skills/compose-video/palettes/monochrome.md +14 -0
- package/dist/skills/compose-video/palettes/nature-earth.md +14 -0
- package/dist/skills/compose-video/palettes/neon-electric.md +14 -0
- package/dist/skills/compose-video/palettes/pastel-soft.md +14 -0
- package/dist/skills/compose-video/palettes/warm-editorial.md +14 -0
- package/dist/skills/compose-video/patterns.md +118 -0
- package/dist/templates/_shared/CLAUDE.md +50 -0
- package/package.json +7 -2
package/dist/cli.js
CHANGED
|
@@ -422,7 +422,7 @@ var VERSION;
|
|
|
422
422
|
var init_version = __esm({
|
|
423
423
|
"src/version.ts"() {
|
|
424
424
|
"use strict";
|
|
425
|
-
VERSION = true ? "0.1.
|
|
425
|
+
VERSION = true ? "0.1.8" : "0.0.0-dev";
|
|
426
426
|
}
|
|
427
427
|
});
|
|
428
428
|
|
|
@@ -2748,6 +2748,25 @@ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync, rmSync
|
|
|
2748
2748
|
import { join as join3, dirname } from "path";
|
|
2749
2749
|
import { homedir as homedir3 } from "os";
|
|
2750
2750
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
2751
|
+
function hasNpx() {
|
|
2752
|
+
try {
|
|
2753
|
+
execFileSync2("npx", ["--version"], { stdio: "ignore", timeout: 5e3 });
|
|
2754
|
+
return true;
|
|
2755
|
+
} catch {
|
|
2756
|
+
return false;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
function runSkillsAdd(repo, agents, global) {
|
|
2760
|
+
const args = ["skills", "add", repo, "-y"];
|
|
2761
|
+
if (global) args.push("-g");
|
|
2762
|
+
for (const agent of agents) {
|
|
2763
|
+
args.push("-a", agent);
|
|
2764
|
+
}
|
|
2765
|
+
execFileSync2("npx", args, {
|
|
2766
|
+
stdio: "ignore",
|
|
2767
|
+
timeout: 12e4
|
|
2768
|
+
});
|
|
2769
|
+
}
|
|
2751
2770
|
function hasGit2() {
|
|
2752
2771
|
try {
|
|
2753
2772
|
execFileSync2("git", ["--version"], { stdio: "ignore", timeout: 5e3 });
|
|
@@ -2764,6 +2783,7 @@ function gitClone(repo, dest) {
|
|
|
2764
2783
|
});
|
|
2765
2784
|
}
|
|
2766
2785
|
function fetchRepo(source) {
|
|
2786
|
+
const gitUrl = `https://github.com/${source.repo}.git`;
|
|
2767
2787
|
if (existsSync3(source.cache)) {
|
|
2768
2788
|
try {
|
|
2769
2789
|
execFileSync2("git", ["pull", "--ff-only"], {
|
|
@@ -2773,18 +2793,17 @@ function fetchRepo(source) {
|
|
|
2773
2793
|
env: GIT_ENV
|
|
2774
2794
|
});
|
|
2775
2795
|
} catch {
|
|
2776
|
-
const
|
|
2777
|
-
if (existsSync3(
|
|
2778
|
-
return skillsDir;
|
|
2779
|
-
}
|
|
2796
|
+
const skillsDir2 = join3(source.cache, source.skillsPath);
|
|
2797
|
+
if (existsSync3(skillsDir2)) return skillsDir2;
|
|
2780
2798
|
rmSync2(source.cache, { recursive: true, force: true });
|
|
2781
|
-
gitClone(
|
|
2799
|
+
gitClone(gitUrl, source.cache);
|
|
2782
2800
|
}
|
|
2783
2801
|
} else {
|
|
2784
2802
|
mkdirSync3(dirname(source.cache), { recursive: true });
|
|
2785
|
-
gitClone(
|
|
2803
|
+
gitClone(gitUrl, source.cache);
|
|
2786
2804
|
}
|
|
2787
|
-
|
|
2805
|
+
const skillsDir = join3(source.cache, source.skillsPath);
|
|
2806
|
+
return existsSync3(skillsDir) ? skillsDir : void 0;
|
|
2788
2807
|
}
|
|
2789
2808
|
function installSkillsFromDir(sourceDir, targetDir, sourceName) {
|
|
2790
2809
|
const installed = [];
|
|
@@ -2795,24 +2814,20 @@ function installSkillsFromDir(sourceDir, targetDir, sourceName) {
|
|
|
2795
2814
|
const skillFile = join3(sourceDir, entry.name, "SKILL.md");
|
|
2796
2815
|
if (!existsSync3(skillFile)) continue;
|
|
2797
2816
|
const destDir = join3(targetDir, entry.name);
|
|
2798
|
-
|
|
2799
|
-
if (overwritten) rmSync2(destDir, { recursive: true, force: true });
|
|
2817
|
+
if (existsSync3(destDir)) rmSync2(destDir, { recursive: true, force: true });
|
|
2800
2818
|
mkdirSync3(destDir, { recursive: true });
|
|
2801
2819
|
cpSync(join3(sourceDir, entry.name), destDir, { recursive: true });
|
|
2802
|
-
installed.push({ name: entry.name, source: sourceName
|
|
2820
|
+
installed.push({ name: entry.name, source: sourceName });
|
|
2803
2821
|
}
|
|
2804
2822
|
return installed;
|
|
2805
2823
|
}
|
|
2806
|
-
|
|
2807
|
-
if (!hasGit2()) return { count: 0, targets: [], skipped: SOURCES.map((s) => s.name) };
|
|
2808
|
-
const targets = targetNames ? TARGETS.filter((t2) => targetNames.includes(t2.flag)) : TARGETS.filter((t2) => t2.defaultEnabled);
|
|
2809
|
-
let totalCount = 0;
|
|
2824
|
+
function fallbackInstall(targets) {
|
|
2810
2825
|
const skipped = [];
|
|
2811
2826
|
const fetched = [];
|
|
2812
2827
|
for (const source of SOURCES) {
|
|
2813
2828
|
try {
|
|
2814
2829
|
const skillsDir = fetchRepo(source);
|
|
2815
|
-
if (
|
|
2830
|
+
if (skillsDir) {
|
|
2816
2831
|
fetched.push({ source, skillsDir });
|
|
2817
2832
|
} else {
|
|
2818
2833
|
skipped.push(source.name);
|
|
@@ -2821,21 +2836,45 @@ async function installAllSkills(targetNames) {
|
|
|
2821
2836
|
skipped.push(source.name);
|
|
2822
2837
|
}
|
|
2823
2838
|
}
|
|
2824
|
-
const [
|
|
2825
|
-
|
|
2826
|
-
|
|
2839
|
+
const [first, ...rest] = targets;
|
|
2840
|
+
const allInstalled = [];
|
|
2841
|
+
if (first) {
|
|
2842
|
+
mkdirSync3(first.dir, { recursive: true });
|
|
2827
2843
|
for (const { skillsDir, source } of fetched) {
|
|
2828
|
-
|
|
2829
|
-
totalCount += skills.length;
|
|
2844
|
+
allInstalled.push(...installSkillsFromDir(skillsDir, first.dir, source.name));
|
|
2830
2845
|
}
|
|
2831
2846
|
}
|
|
2832
|
-
for (const target of
|
|
2847
|
+
for (const target of rest) {
|
|
2833
2848
|
mkdirSync3(target.dir, { recursive: true });
|
|
2834
2849
|
for (const { skillsDir, source } of fetched) {
|
|
2835
2850
|
installSkillsFromDir(skillsDir, target.dir, source.name);
|
|
2836
2851
|
}
|
|
2837
2852
|
}
|
|
2838
|
-
return { count:
|
|
2853
|
+
return { count: allInstalled.length, installed: allInstalled, skipped };
|
|
2854
|
+
}
|
|
2855
|
+
async function installAllSkills(targetNames) {
|
|
2856
|
+
const targets = targetNames ? TARGETS.filter((t2) => targetNames.includes(t2.flag)) : TARGETS.filter((t2) => t2.defaultEnabled);
|
|
2857
|
+
const agents = targets.map((t2) => t2.skillsAgent);
|
|
2858
|
+
if (hasNpx()) {
|
|
2859
|
+
const skipped = [];
|
|
2860
|
+
let count = 0;
|
|
2861
|
+
for (const source of SOURCES) {
|
|
2862
|
+
try {
|
|
2863
|
+
runSkillsAdd(source.repo, agents, true);
|
|
2864
|
+
count += 1;
|
|
2865
|
+
} catch {
|
|
2866
|
+
skipped.push(source.name);
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
if (count > 0) {
|
|
2870
|
+
return { count, targets: targets.map((t2) => t2.name), skipped };
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
if (!hasGit2()) {
|
|
2874
|
+
return { count: 0, targets: [], skipped: SOURCES.map((s) => s.name) };
|
|
2875
|
+
}
|
|
2876
|
+
const result = fallbackInstall(targets);
|
|
2877
|
+
return { count: result.count, targets: targets.map((t2) => t2.name), skipped: result.skipped };
|
|
2839
2878
|
}
|
|
2840
2879
|
function resolveTargets(args) {
|
|
2841
2880
|
const hasAnyFlag = TARGETS.some((t2) => args[t2.flag] === true);
|
|
@@ -2846,70 +2885,57 @@ function resolveTargets(args) {
|
|
|
2846
2885
|
}
|
|
2847
2886
|
async function runInstall({ args }) {
|
|
2848
2887
|
Wt2(c.bold("hyperframes skills"));
|
|
2849
|
-
if (!hasGit2()) {
|
|
2850
|
-
R2.error(c.error("git is required to install skills. Install git and retry."));
|
|
2851
|
-
Gt(c.warn("No skills installed."));
|
|
2852
|
-
return;
|
|
2853
|
-
}
|
|
2854
2888
|
const targets = resolveTargets(args);
|
|
2855
|
-
const
|
|
2856
|
-
|
|
2857
|
-
const
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
const
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
spinner.stop(c.
|
|
2889
|
+
const agents = targets.map((t2) => t2.skillsAgent);
|
|
2890
|
+
if (hasNpx()) {
|
|
2891
|
+
const installed = [];
|
|
2892
|
+
const skippedSources = [];
|
|
2893
|
+
for (const source of SOURCES) {
|
|
2894
|
+
const spinner = be();
|
|
2895
|
+
spinner.start(`Installing ${source.name} skills...`);
|
|
2896
|
+
try {
|
|
2897
|
+
runSkillsAdd(source.repo, agents, true);
|
|
2898
|
+
installed.push(source.name);
|
|
2899
|
+
spinner.stop(c.success(`${source.name} skills installed`));
|
|
2900
|
+
} catch {
|
|
2901
|
+
skippedSources.push(source.name);
|
|
2902
|
+
spinner.stop(c.dim(`${source.name} skills skipped (unavailable)`));
|
|
2866
2903
|
}
|
|
2867
|
-
} catch {
|
|
2868
|
-
spinner.stop(c.dim(`${source.name} skills skipped (repo not accessible)`));
|
|
2869
2904
|
}
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
const spinner = be();
|
|
2875
|
-
spinner.start(`Installing to ${target.name}...`);
|
|
2876
|
-
mkdirSync3(target.dir, { recursive: true });
|
|
2877
|
-
let count = 0;
|
|
2878
|
-
for (const { source, skillsDir } of fetched) {
|
|
2879
|
-
const skills = installSkillsFromDir(skillsDir, target.dir, source.name);
|
|
2880
|
-
count += skills.length;
|
|
2881
|
-
if (!counted) allInstalled.push(...skills);
|
|
2905
|
+
console.log();
|
|
2906
|
+
console.log(` ${c.dim("Targets:")} ${targets.map((t2) => t2.name).join(", ")}`);
|
|
2907
|
+
if (skippedSources.length > 0) {
|
|
2908
|
+
console.log(` ${c.dim("Skipped:")} ${skippedSources.join(", ")}`);
|
|
2882
2909
|
}
|
|
2883
|
-
|
|
2884
|
-
|
|
2910
|
+
console.log();
|
|
2911
|
+
if (installed.length > 0) {
|
|
2912
|
+
Gt(c.success(`${installed.join(" + ")} skills installed.`));
|
|
2913
|
+
return;
|
|
2914
|
+
}
|
|
2915
|
+
R2.warn("npx skills add failed \u2014 trying fallback...");
|
|
2916
|
+
}
|
|
2917
|
+
if (!hasGit2()) {
|
|
2918
|
+
R2.error(c.error("Neither npx nor git available. Install Node.js or git and retry."));
|
|
2919
|
+
Gt(c.warn("No skills installed."));
|
|
2920
|
+
return;
|
|
2885
2921
|
}
|
|
2922
|
+
R2.info(c.dim("Using git fallback..."));
|
|
2923
|
+
const result = fallbackInstall(targets);
|
|
2886
2924
|
console.log();
|
|
2887
2925
|
for (const source of SOURCES) {
|
|
2888
|
-
const names =
|
|
2926
|
+
const names = result.installed.filter((s) => s.source === source.name).map((s) => s.name);
|
|
2889
2927
|
if (names.length > 0) {
|
|
2890
2928
|
const label2 = `${source.name}:`.padEnd(14);
|
|
2891
2929
|
console.log(` ${c.dim(label2)} ${names.map((s) => c.accent(s)).join(", ")}`);
|
|
2892
2930
|
}
|
|
2893
2931
|
}
|
|
2894
2932
|
console.log(` ${c.dim("Targets:")} ${targets.map((t2) => t2.name).join(", ")}`);
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
if (skippedSources.length > 0) {
|
|
2898
|
-
console.log(
|
|
2899
|
-
` ${c.dim("Skipped:")} ${skippedSources.map((s) => s.name).join(", ")} (repo not accessible)`
|
|
2900
|
-
);
|
|
2933
|
+
if (result.skipped.length > 0) {
|
|
2934
|
+
console.log(` ${c.dim("Skipped:")} ${result.skipped.join(", ")}`);
|
|
2901
2935
|
}
|
|
2902
2936
|
console.log();
|
|
2903
|
-
if (
|
|
2904
|
-
|
|
2905
|
-
const skippedNames = skippedSources.map((s) => s.name).join(", ");
|
|
2906
|
-
Gt(
|
|
2907
|
-
c.warn(
|
|
2908
|
-
`${allInstalled.length} skills ready (${readySources}). Unavailable: ${skippedNames}.`
|
|
2909
|
-
)
|
|
2910
|
-
);
|
|
2911
|
-
} else if (allInstalled.length > 0) {
|
|
2912
|
-
Gt(c.success(`${allInstalled.length} skills ready.`));
|
|
2937
|
+
if (result.count > 0) {
|
|
2938
|
+
Gt(c.success(`${result.count} skills ready.`));
|
|
2913
2939
|
} else {
|
|
2914
2940
|
Gt(c.warn("No skills installed."));
|
|
2915
2941
|
}
|
|
@@ -2925,24 +2951,28 @@ var init_install_skills = __esm({
|
|
|
2925
2951
|
{
|
|
2926
2952
|
name: "Claude Code",
|
|
2927
2953
|
flag: "claude",
|
|
2954
|
+
skillsAgent: "claude-code",
|
|
2928
2955
|
dir: join3(homedir3(), ".claude", "skills"),
|
|
2929
2956
|
defaultEnabled: true
|
|
2930
2957
|
},
|
|
2931
2958
|
{
|
|
2932
2959
|
name: "Gemini CLI",
|
|
2933
2960
|
flag: "gemini",
|
|
2961
|
+
skillsAgent: "gemini-cli",
|
|
2934
2962
|
dir: join3(homedir3(), ".gemini", "skills"),
|
|
2935
2963
|
defaultEnabled: true
|
|
2936
2964
|
},
|
|
2937
2965
|
{
|
|
2938
2966
|
name: "Codex CLI",
|
|
2939
2967
|
flag: "codex",
|
|
2968
|
+
skillsAgent: "codex",
|
|
2940
2969
|
dir: join3(homedir3(), ".codex", "skills"),
|
|
2941
2970
|
defaultEnabled: true
|
|
2942
2971
|
},
|
|
2943
2972
|
{
|
|
2944
2973
|
name: "Cursor",
|
|
2945
2974
|
flag: "cursor",
|
|
2975
|
+
skillsAgent: "cursor",
|
|
2946
2976
|
get dir() {
|
|
2947
2977
|
return join3(process.cwd(), ".cursor", "skills");
|
|
2948
2978
|
},
|
|
@@ -2952,13 +2982,13 @@ var init_install_skills = __esm({
|
|
|
2952
2982
|
SOURCES = [
|
|
2953
2983
|
{
|
|
2954
2984
|
name: "HyperFrames",
|
|
2955
|
-
repo: "
|
|
2985
|
+
repo: "heygen-com/hyperframes",
|
|
2956
2986
|
skillsPath: "skills",
|
|
2957
2987
|
cache: join3(homedir3(), ".cache", "hyperframes", "hyperframes-skills")
|
|
2958
2988
|
},
|
|
2959
2989
|
{
|
|
2960
2990
|
name: "GSAP",
|
|
2961
|
-
repo: "
|
|
2991
|
+
repo: "greensock/gsap-skills",
|
|
2962
2992
|
skillsPath: "skills",
|
|
2963
2993
|
cache: join3(homedir3(), ".cache", "hyperframes", "gsap-skills")
|
|
2964
2994
|
}
|
|
@@ -3469,6 +3499,37 @@ function lintHyperframeHtml(html, options = {}) {
|
|
|
3469
3499
|
fixHint: "Register each composition timeline on `window.__timelines[compositionId]`."
|
|
3470
3500
|
});
|
|
3471
3501
|
}
|
|
3502
|
+
if (TIMELINE_REGISTRY_ASSIGN_PATTERN.test(source) && !TIMELINE_REGISTRY_INIT_PATTERN.test(source)) {
|
|
3503
|
+
pushFinding({
|
|
3504
|
+
code: "timeline_registry_missing_init",
|
|
3505
|
+
severity: "error",
|
|
3506
|
+
message: "`window.__timelines[\u2026] = \u2026` is used without initializing `window.__timelines` first.",
|
|
3507
|
+
fixHint: "Add `window.__timelines = window.__timelines || {};` before any timeline assignment."
|
|
3508
|
+
});
|
|
3509
|
+
}
|
|
3510
|
+
{
|
|
3511
|
+
const htmlCompIds = /* @__PURE__ */ new Set();
|
|
3512
|
+
const timelineRegKeys = /* @__PURE__ */ new Set();
|
|
3513
|
+
const compIdRe = /data-composition-id\s*=\s*["']([^"']+)["']/gi;
|
|
3514
|
+
const tlKeyRe = /window\.__timelines\[\s*["']([^"']+)["']\s*\]/g;
|
|
3515
|
+
let m;
|
|
3516
|
+
while ((m = compIdRe.exec(source)) !== null) {
|
|
3517
|
+
if (m[1]) htmlCompIds.add(m[1]);
|
|
3518
|
+
}
|
|
3519
|
+
while ((m = tlKeyRe.exec(source)) !== null) {
|
|
3520
|
+
if (m[1]) timelineRegKeys.add(m[1]);
|
|
3521
|
+
}
|
|
3522
|
+
for (const key2 of timelineRegKeys) {
|
|
3523
|
+
if (!htmlCompIds.has(key2)) {
|
|
3524
|
+
pushFinding({
|
|
3525
|
+
code: "timeline_id_mismatch",
|
|
3526
|
+
severity: "error",
|
|
3527
|
+
message: `Timeline registered as "${key2}" but no element has data-composition-id="${key2}". The runtime cannot auto-nest this timeline.`,
|
|
3528
|
+
fixHint: `Change window.__timelines["${key2}"] to match the data-composition-id attribute, or vice versa.`
|
|
3529
|
+
});
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3472
3533
|
if (INVALID_SCRIPT_CLOSE_PATTERN.test(source)) {
|
|
3473
3534
|
pushFinding({
|
|
3474
3535
|
code: "invalid_inline_script_syntax",
|
|
@@ -23766,7 +23827,11 @@ __export(render_exports, {
|
|
|
23766
23827
|
default: () => render_default
|
|
23767
23828
|
});
|
|
23768
23829
|
import { existsSync as existsSync24, mkdirSync as mkdirSync16, statSync as statSync9 } from "fs";
|
|
23830
|
+
import { cpus as cpus2 } from "os";
|
|
23769
23831
|
import { resolve as resolve13, dirname as dirname12, join as join22 } from "path";
|
|
23832
|
+
function defaultWorkerCount() {
|
|
23833
|
+
return Math.max(1, Math.min(Math.floor(CPU_CORE_COUNT / 2), 4));
|
|
23834
|
+
}
|
|
23770
23835
|
async function renderDocker(projectDir, outputPath, options) {
|
|
23771
23836
|
const producer = await loadProducer();
|
|
23772
23837
|
const startTime = Date.now();
|
|
@@ -23780,7 +23845,11 @@ async function renderDocker(projectDir, outputPath, options) {
|
|
|
23780
23845
|
});
|
|
23781
23846
|
await producer.executeRenderJob(job, projectDir, outputPath);
|
|
23782
23847
|
} catch (error) {
|
|
23783
|
-
trackRenderError({
|
|
23848
|
+
trackRenderError({
|
|
23849
|
+
fps: options.fps,
|
|
23850
|
+
quality: options.quality,
|
|
23851
|
+
docker: true
|
|
23852
|
+
});
|
|
23784
23853
|
const message = error instanceof Error ? error.message : String(error);
|
|
23785
23854
|
errorBox("Render failed", message, "Check Docker is running: docker info");
|
|
23786
23855
|
process.exit(1);
|
|
@@ -23790,7 +23859,7 @@ async function renderDocker(projectDir, outputPath, options) {
|
|
|
23790
23859
|
durationMs: elapsed,
|
|
23791
23860
|
fps: options.fps,
|
|
23792
23861
|
quality: options.quality,
|
|
23793
|
-
workers: options.workers
|
|
23862
|
+
workers: options.workers,
|
|
23794
23863
|
docker: true,
|
|
23795
23864
|
gpu: options.gpu
|
|
23796
23865
|
});
|
|
@@ -23815,7 +23884,11 @@ async function renderLocal(projectDir, outputPath, options) {
|
|
|
23815
23884
|
try {
|
|
23816
23885
|
await producer.executeRenderJob(job, projectDir, outputPath, onProgress);
|
|
23817
23886
|
} catch (error) {
|
|
23818
|
-
trackRenderError({
|
|
23887
|
+
trackRenderError({
|
|
23888
|
+
fps: options.fps,
|
|
23889
|
+
quality: options.quality,
|
|
23890
|
+
docker: false
|
|
23891
|
+
});
|
|
23819
23892
|
const message = error instanceof Error ? error.message : String(error);
|
|
23820
23893
|
errorBox("Render failed", message, "Try --docker for containerized rendering");
|
|
23821
23894
|
process.exit(1);
|
|
@@ -23825,7 +23898,7 @@ async function renderLocal(projectDir, outputPath, options) {
|
|
|
23825
23898
|
durationMs: elapsed,
|
|
23826
23899
|
fps: options.fps,
|
|
23827
23900
|
quality: options.quality,
|
|
23828
|
-
workers: options.workers
|
|
23901
|
+
workers: options.workers,
|
|
23829
23902
|
docker: false,
|
|
23830
23903
|
gpu: options.gpu
|
|
23831
23904
|
});
|
|
@@ -23843,7 +23916,7 @@ function printRenderComplete(outputPath, elapsedMs, quiet) {
|
|
|
23843
23916
|
console.log(c.success("\u25C7") + " " + c.accent(outputPath));
|
|
23844
23917
|
console.log(" " + c.bold(fileSize) + c.dim(" \xB7 " + duration + " \xB7 completed"));
|
|
23845
23918
|
}
|
|
23846
|
-
var VALID_FPS, VALID_QUALITY, VALID_FORMAT, render_default;
|
|
23919
|
+
var VALID_FPS, VALID_QUALITY, VALID_FORMAT, CPU_CORE_COUNT, render_default;
|
|
23847
23920
|
var init_render = __esm({
|
|
23848
23921
|
"src/commands/render.ts"() {
|
|
23849
23922
|
"use strict";
|
|
@@ -23857,6 +23930,7 @@ var init_render = __esm({
|
|
|
23857
23930
|
VALID_FPS = /* @__PURE__ */ new Set([24, 30, 60]);
|
|
23858
23931
|
VALID_QUALITY = /* @__PURE__ */ new Set(["draft", "standard", "high"]);
|
|
23859
23932
|
VALID_FORMAT = /* @__PURE__ */ new Set(["mp4", "webm"]);
|
|
23933
|
+
CPU_CORE_COUNT = cpus2().length;
|
|
23860
23934
|
render_default = defineCommand({
|
|
23861
23935
|
meta: {
|
|
23862
23936
|
name: "render",
|
|
@@ -23869,19 +23943,45 @@ Examples:
|
|
|
23869
23943
|
hyperframes render --docker --output deterministic.mp4`
|
|
23870
23944
|
},
|
|
23871
23945
|
args: {
|
|
23872
|
-
dir: {
|
|
23873
|
-
|
|
23874
|
-
|
|
23875
|
-
|
|
23946
|
+
dir: {
|
|
23947
|
+
type: "positional",
|
|
23948
|
+
description: "Project directory",
|
|
23949
|
+
required: false
|
|
23950
|
+
},
|
|
23951
|
+
output: {
|
|
23952
|
+
type: "string",
|
|
23953
|
+
description: "Output path (default: renders/<name>.mp4)"
|
|
23954
|
+
},
|
|
23955
|
+
fps: {
|
|
23956
|
+
type: "string",
|
|
23957
|
+
description: "Frame rate: 24, 30, 60",
|
|
23958
|
+
default: "30"
|
|
23959
|
+
},
|
|
23960
|
+
quality: {
|
|
23961
|
+
type: "string",
|
|
23962
|
+
description: "Quality: draft, standard, high",
|
|
23963
|
+
default: "standard"
|
|
23964
|
+
},
|
|
23876
23965
|
format: {
|
|
23877
23966
|
type: "string",
|
|
23878
23967
|
description: "Output format: mp4, webm (WebM renders with transparency)",
|
|
23879
23968
|
default: "mp4"
|
|
23880
23969
|
},
|
|
23881
|
-
workers: {
|
|
23882
|
-
|
|
23970
|
+
workers: {
|
|
23971
|
+
type: "string",
|
|
23972
|
+
description: "Parallel render workers (1-8 or 'auto'). Default: half your CPU cores, max 4. Each worker launches a separate Chrome process."
|
|
23973
|
+
},
|
|
23974
|
+
docker: {
|
|
23975
|
+
type: "boolean",
|
|
23976
|
+
description: "Use Docker for deterministic render",
|
|
23977
|
+
default: false
|
|
23978
|
+
},
|
|
23883
23979
|
gpu: { type: "boolean", description: "Use GPU encoding", default: false },
|
|
23884
|
-
quiet: {
|
|
23980
|
+
quiet: {
|
|
23981
|
+
type: "boolean",
|
|
23982
|
+
description: "Suppress verbose output",
|
|
23983
|
+
default: false
|
|
23984
|
+
}
|
|
23885
23985
|
},
|
|
23886
23986
|
async run({ args }) {
|
|
23887
23987
|
const project = resolveProject(args.dir);
|
|
@@ -23904,10 +24004,10 @@ Examples:
|
|
|
23904
24004
|
}
|
|
23905
24005
|
const format = formatRaw;
|
|
23906
24006
|
let workers;
|
|
23907
|
-
if (args.workers != null) {
|
|
24007
|
+
if (args.workers != null && args.workers !== "auto") {
|
|
23908
24008
|
const parsed = parseInt(args.workers, 10);
|
|
23909
24009
|
if (isNaN(parsed) || parsed < 1 || parsed > 8) {
|
|
23910
|
-
errorBox("Invalid workers", `Got "${args.workers}". Must be
|
|
24010
|
+
errorBox("Invalid workers", `Got "${args.workers}". Must be 1-8 or "auto".`);
|
|
23911
24011
|
process.exit(1);
|
|
23912
24012
|
}
|
|
23913
24013
|
workers = parsed;
|
|
@@ -23915,22 +24015,18 @@ Examples:
|
|
|
23915
24015
|
const rendersDir = resolve13("renders");
|
|
23916
24016
|
const ext = format === "webm" ? ".webm" : ".mp4";
|
|
23917
24017
|
const outputPath = args.output ? resolve13(args.output) : join22(rendersDir, `${project.name}${ext}`);
|
|
23918
|
-
|
|
23919
|
-
if (!existsSync24(outputDir)) {
|
|
23920
|
-
mkdirSync16(outputDir, { recursive: true });
|
|
23921
|
-
}
|
|
24018
|
+
mkdirSync16(dirname12(outputPath), { recursive: true });
|
|
23922
24019
|
const useDocker = args.docker ?? false;
|
|
23923
24020
|
const useGpu = args.gpu ?? false;
|
|
23924
24021
|
const quiet = args.quiet ?? false;
|
|
23925
|
-
const workerCount = workers ??
|
|
24022
|
+
const workerCount = workers ?? defaultWorkerCount();
|
|
23926
24023
|
if (!quiet) {
|
|
24024
|
+
const workerLabel = args.workers != null ? `${workerCount} workers` : `${workerCount} workers (auto \u2014 half of ${CPU_CORE_COUNT} cores)`;
|
|
23927
24025
|
console.log("");
|
|
23928
24026
|
console.log(
|
|
23929
24027
|
c.accent("\u25C6") + " Rendering " + c.accent(project.name) + c.dim(" \u2192 " + outputPath)
|
|
23930
24028
|
);
|
|
23931
|
-
console.log(
|
|
23932
|
-
c.dim(" " + fps + "fps \xB7 " + quality + " \xB7 " + workerCount + " workers")
|
|
23933
|
-
);
|
|
24029
|
+
console.log(c.dim(" " + fps + "fps \xB7 " + quality + " \xB7 " + workerLabel));
|
|
23934
24030
|
console.log("");
|
|
23935
24031
|
}
|
|
23936
24032
|
if (!useDocker) {
|
|
@@ -23977,7 +24073,7 @@ Examples:
|
|
|
23977
24073
|
fps,
|
|
23978
24074
|
quality,
|
|
23979
24075
|
format,
|
|
23980
|
-
workers,
|
|
24076
|
+
workers: workerCount,
|
|
23981
24077
|
gpu: useGpu,
|
|
23982
24078
|
quiet
|
|
23983
24079
|
});
|
|
@@ -23986,7 +24082,7 @@ Examples:
|
|
|
23986
24082
|
fps,
|
|
23987
24083
|
quality,
|
|
23988
24084
|
format,
|
|
23989
|
-
workers,
|
|
24085
|
+
workers: workerCount,
|
|
23990
24086
|
gpu: useGpu,
|
|
23991
24087
|
quiet,
|
|
23992
24088
|
browserPath
|
|
@@ -24261,12 +24357,21 @@ function transcodeToMp4(inputPath, outputPath) {
|
|
|
24261
24357
|
child.on("error", () => resolvePromise(false));
|
|
24262
24358
|
});
|
|
24263
24359
|
}
|
|
24264
|
-
function
|
|
24265
|
-
const
|
|
24266
|
-
const devPath = resolve14(
|
|
24267
|
-
const builtPath = resolve14(
|
|
24360
|
+
function resolveAssetDir(devSegments, builtSegments) {
|
|
24361
|
+
const base = dirname13(fileURLToPath5(import.meta.url));
|
|
24362
|
+
const devPath = resolve14(base, ...devSegments);
|
|
24363
|
+
const builtPath = resolve14(base, ...builtSegments);
|
|
24268
24364
|
return existsSync26(devPath) ? devPath : builtPath;
|
|
24269
24365
|
}
|
|
24366
|
+
function getStaticTemplateDir(templateId) {
|
|
24367
|
+
return resolveAssetDir(["..", "templates", templateId], ["templates", templateId]);
|
|
24368
|
+
}
|
|
24369
|
+
function getSharedTemplateDir() {
|
|
24370
|
+
return resolveAssetDir(["..", "templates", "_shared"], ["templates", "_shared"]);
|
|
24371
|
+
}
|
|
24372
|
+
function getBundledSkillsDir() {
|
|
24373
|
+
return resolveAssetDir(["..", "..", "..", "..", "skills"], ["skills"]);
|
|
24374
|
+
}
|
|
24270
24375
|
function patchVideoSrc(dir, videoFilename, durationSeconds) {
|
|
24271
24376
|
const htmlFiles = readdirSync7(dir, { withFileTypes: true, recursive: true }).filter((e) => e.isFile() && e.name.endsWith(".html")).map((e) => join24(e.parentPath ?? e.path, e.name));
|
|
24272
24377
|
for (const file of htmlFiles) {
|
|
@@ -24417,6 +24522,28 @@ function scaffoldProject(destDir, name, templateId, localVideoName, durationSeco
|
|
|
24417
24522
|
),
|
|
24418
24523
|
"utf-8"
|
|
24419
24524
|
);
|
|
24525
|
+
const sharedDir = getSharedTemplateDir();
|
|
24526
|
+
if (existsSync26(sharedDir)) {
|
|
24527
|
+
for (const entry of readdirSync7(sharedDir, { withFileTypes: true })) {
|
|
24528
|
+
const src = join24(sharedDir, entry.name);
|
|
24529
|
+
const dest = resolve14(destDir, entry.name);
|
|
24530
|
+
if (entry.isFile() || entry.isSymbolicLink()) {
|
|
24531
|
+
copyFileSync3(src, dest);
|
|
24532
|
+
}
|
|
24533
|
+
}
|
|
24534
|
+
}
|
|
24535
|
+
const skillsSrcDir = getBundledSkillsDir();
|
|
24536
|
+
if (existsSync26(skillsSrcDir)) {
|
|
24537
|
+
const projectSkills = ["compose-video", "captions"];
|
|
24538
|
+
for (const skill of projectSkills) {
|
|
24539
|
+
const src = join24(skillsSrcDir, skill);
|
|
24540
|
+
if (existsSync26(src)) {
|
|
24541
|
+
const dest = resolve14(destDir, ".claude", "skills", skill);
|
|
24542
|
+
mkdirSync18(dest, { recursive: true });
|
|
24543
|
+
cpSync2(src, dest, { recursive: true });
|
|
24544
|
+
}
|
|
24545
|
+
}
|
|
24546
|
+
}
|
|
24420
24547
|
}
|
|
24421
24548
|
async function nextStepLoop(destDir) {
|
|
24422
24549
|
while (true) {
|
|
@@ -24571,9 +24698,28 @@ Example: hyperframes init my-video --template blank --video video.mp4`);
|
|
|
24571
24698
|
await installSkills(false);
|
|
24572
24699
|
}
|
|
24573
24700
|
console.log(c.success(`Created ${c.accent(name2 + "/")}`));
|
|
24574
|
-
for (const f of readdirSync7(destDir2)) {
|
|
24701
|
+
for (const f of readdirSync7(destDir2).filter((f2) => !f2.startsWith("."))) {
|
|
24575
24702
|
console.log(` ${c.accent(f)}`);
|
|
24576
24703
|
}
|
|
24704
|
+
console.log();
|
|
24705
|
+
console.log("Next steps:");
|
|
24706
|
+
console.log(
|
|
24707
|
+
` ${c.accent(`cd ${name2}`)} && ${c.accent("npx hyperframes dev")} ${c.dim("# preview in studio")}`
|
|
24708
|
+
);
|
|
24709
|
+
console.log(
|
|
24710
|
+
` ${c.accent(`cd ${name2}`)} && ${c.accent("npx hyperframes render")} ${c.dim("# render to MP4")}`
|
|
24711
|
+
);
|
|
24712
|
+
console.log(
|
|
24713
|
+
` ${c.accent("npx hyperframes docs")} ${c.dim("<topic>")} ${c.dim("# learn composition syntax")}`
|
|
24714
|
+
);
|
|
24715
|
+
console.log(
|
|
24716
|
+
` ${c.dim("topics: data-attributes, gsap, compositions, rendering, templates, troubleshooting")}`
|
|
24717
|
+
);
|
|
24718
|
+
console.log();
|
|
24719
|
+
console.log(
|
|
24720
|
+
` ${c.dim("AI skills installed \u2014 open this folder in your AI coding agent to get started.")}`
|
|
24721
|
+
);
|
|
24722
|
+
console.log(` ${c.dim("Full docs: hyperframes.heygen.com")}`);
|
|
24577
24723
|
return;
|
|
24578
24724
|
}
|
|
24579
24725
|
Wt2("Create a new HyperFrames project");
|