hyperframes 0.5.0-alpha.13 → 0.5.0-alpha.14
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
CHANGED
|
@@ -54,7 +54,7 @@ var VERSION;
|
|
|
54
54
|
var init_version = __esm({
|
|
55
55
|
"src/version.ts"() {
|
|
56
56
|
"use strict";
|
|
57
|
-
VERSION = true ? "0.5.0-alpha.
|
|
57
|
+
VERSION = true ? "0.5.0-alpha.14" : "0.0.0-dev";
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -10218,8 +10218,8 @@ function trackRenderError(props) {
|
|
|
10218
10218
|
memory_free_mb: props.memoryFreeMb
|
|
10219
10219
|
});
|
|
10220
10220
|
}
|
|
10221
|
-
function trackInitTemplate(templateId) {
|
|
10222
|
-
trackEvent("init_template", { template: templateId });
|
|
10221
|
+
function trackInitTemplate(templateId, props) {
|
|
10222
|
+
trackEvent("init_template", { template: templateId, tailwind: props?.tailwind });
|
|
10223
10223
|
}
|
|
10224
10224
|
function trackBrowserInstall() {
|
|
10225
10225
|
trackEvent("browser_install", {});
|
|
@@ -25757,6 +25757,13 @@ function resolveConfig(overrides) {
|
|
|
25757
25757
|
"PRODUCER_ENABLE_STREAMING_ENCODE",
|
|
25758
25758
|
DEFAULT_CONFIG2.enableStreamingEncode
|
|
25759
25759
|
),
|
|
25760
|
+
streamingEncodeMaxDurationSeconds: Math.max(
|
|
25761
|
+
0,
|
|
25762
|
+
envNum(
|
|
25763
|
+
"PRODUCER_STREAMING_ENCODE_MAX_DURATION_SECONDS",
|
|
25764
|
+
DEFAULT_CONFIG2.streamingEncodeMaxDurationSeconds
|
|
25765
|
+
)
|
|
25766
|
+
),
|
|
25760
25767
|
ffmpegEncodeTimeout: envNum("FFMPEG_ENCODE_TIMEOUT_MS", DEFAULT_CONFIG2.ffmpegEncodeTimeout),
|
|
25761
25768
|
ffmpegProcessTimeout: envNum("FFMPEG_PROCESS_TIMEOUT_MS", DEFAULT_CONFIG2.ffmpegProcessTimeout),
|
|
25762
25769
|
ffmpegStreamingTimeout: envNum(
|
|
@@ -25814,7 +25821,8 @@ var init_config2 = __esm({
|
|
|
25814
25821
|
forceScreenshot: false,
|
|
25815
25822
|
enableChunkedEncode: false,
|
|
25816
25823
|
chunkSizeFrames: 360,
|
|
25817
|
-
enableStreamingEncode:
|
|
25824
|
+
enableStreamingEncode: true,
|
|
25825
|
+
streamingEncodeMaxDurationSeconds: 240,
|
|
25818
25826
|
ffmpegEncodeTimeout: 6e5,
|
|
25819
25827
|
ffmpegProcessTimeout: 3e5,
|
|
25820
25828
|
ffmpegStreamingTimeout: 6e5,
|
|
@@ -26444,6 +26452,23 @@ async function applyVideoMetadataHints(page, hints) {
|
|
|
26444
26452
|
[...hints]
|
|
26445
26453
|
);
|
|
26446
26454
|
}
|
|
26455
|
+
async function waitForOptionalTailwindReady(page, timeoutMs) {
|
|
26456
|
+
const hasTailwindReady = await page.evaluate(
|
|
26457
|
+
`(() => { const ready = window.__tailwindReady; return !!ready && typeof ready.then === "function"; })()`
|
|
26458
|
+
);
|
|
26459
|
+
if (!hasTailwindReady) return;
|
|
26460
|
+
const ready = await Promise.race([
|
|
26461
|
+
page.evaluate(
|
|
26462
|
+
`Promise.resolve(window.__tailwindReady).then(() => true, () => false)`
|
|
26463
|
+
),
|
|
26464
|
+
new Promise((resolve39) => setTimeout(() => resolve39(false), timeoutMs))
|
|
26465
|
+
]);
|
|
26466
|
+
if (!ready) {
|
|
26467
|
+
throw new Error(
|
|
26468
|
+
`[FrameCapture] window.__tailwindReady not resolved after ${timeoutMs}ms. Tailwind browser runtime must finish before frame capture starts.`
|
|
26469
|
+
);
|
|
26470
|
+
}
|
|
26471
|
+
}
|
|
26447
26472
|
async function initializeSession(session) {
|
|
26448
26473
|
const { page, serverUrl } = session;
|
|
26449
26474
|
page.on("console", (msg) => {
|
|
@@ -26500,6 +26525,7 @@ async function initializeSession(session) {
|
|
|
26500
26525
|
);
|
|
26501
26526
|
}
|
|
26502
26527
|
await page.evaluate(`document.fonts?.ready`);
|
|
26528
|
+
await waitForOptionalTailwindReady(page, pageReadyTimeout2);
|
|
26503
26529
|
if (session.options.format === "png") {
|
|
26504
26530
|
await initTransparentBackground(session.page);
|
|
26505
26531
|
}
|
|
@@ -26565,6 +26591,7 @@ async function initializeSession(session) {
|
|
|
26565
26591
|
await new Promise((r2) => setTimeout(r2, 100));
|
|
26566
26592
|
}
|
|
26567
26593
|
await page.evaluate(`document.fonts?.ready`);
|
|
26594
|
+
await waitForOptionalTailwindReady(page, pageReadyTimeout);
|
|
26568
26595
|
warmupRunning = false;
|
|
26569
26596
|
session.beginFrameTimeTicks = (warmupTicks + 10) * session.beginFrameIntervalMs;
|
|
26570
26597
|
if (session.options.format === "png") {
|
|
@@ -35061,6 +35088,27 @@ function createRenderJob(config) {
|
|
|
35061
35088
|
function normalizeCompositionSrcPath(srcPath) {
|
|
35062
35089
|
return srcPath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
35063
35090
|
}
|
|
35091
|
+
function createStandaloneEntryRenderClone(root, host) {
|
|
35092
|
+
const hostClone = host.cloneNode(true);
|
|
35093
|
+
hostClone.setAttribute("data-start", "0");
|
|
35094
|
+
if (root === host) return hostClone;
|
|
35095
|
+
const rootClone = root.cloneNode(false);
|
|
35096
|
+
rootClone.appendChild(hostClone);
|
|
35097
|
+
return rootClone;
|
|
35098
|
+
}
|
|
35099
|
+
function replaceBodyWithRenderClone(body, renderClone) {
|
|
35100
|
+
while (body.firstChild) {
|
|
35101
|
+
body.removeChild(body.firstChild);
|
|
35102
|
+
}
|
|
35103
|
+
body.appendChild(renderClone);
|
|
35104
|
+
}
|
|
35105
|
+
function shouldUseStreamingEncode(cfg, outputFormat, workerCount, durationSeconds) {
|
|
35106
|
+
if (!cfg.enableStreamingEncode) return false;
|
|
35107
|
+
if (outputFormat === "png-sequence") return false;
|
|
35108
|
+
if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) return false;
|
|
35109
|
+
if (durationSeconds > cfg.streamingEncodeMaxDurationSeconds) return false;
|
|
35110
|
+
return workerCount === 1;
|
|
35111
|
+
}
|
|
35064
35112
|
function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
|
|
35065
35113
|
const normalizedEntryFile = normalizeCompositionSrcPath(entryFile);
|
|
35066
35114
|
const { document: document2 } = parseHTML(indexHtml);
|
|
@@ -35075,16 +35123,8 @@ function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
|
|
|
35075
35123
|
(candidate) => candidate.hasAttribute("data-composition-id")
|
|
35076
35124
|
) ?? null;
|
|
35077
35125
|
if (!root) return null;
|
|
35078
|
-
const
|
|
35079
|
-
|
|
35080
|
-
body.innerHTML = "";
|
|
35081
|
-
if (root === host) {
|
|
35082
|
-
body.appendChild(hostClone);
|
|
35083
|
-
return document2.toString();
|
|
35084
|
-
}
|
|
35085
|
-
const rootClone = root.cloneNode(false);
|
|
35086
|
-
rootClone.appendChild(hostClone);
|
|
35087
|
-
body.appendChild(rootClone);
|
|
35126
|
+
const renderClone = createStandaloneEntryRenderClone(root, host);
|
|
35127
|
+
replaceBodyWithRenderClone(body, renderClone);
|
|
35088
35128
|
return document2.toString();
|
|
35089
35129
|
}
|
|
35090
35130
|
async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSignal) {
|
|
@@ -35116,7 +35156,6 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35116
35156
|
}
|
|
35117
35157
|
const enableChunkedEncode = cfg.enableChunkedEncode;
|
|
35118
35158
|
const chunkedEncodeSize = cfg.chunkSizeFrames;
|
|
35119
|
-
const enableStreamingEncode = cfg.enableStreamingEncode && !isPngSequence;
|
|
35120
35159
|
let peakRssBytes = 0;
|
|
35121
35160
|
let peakHeapUsedBytes = 0;
|
|
35122
35161
|
const sampleMemory = () => {
|
|
@@ -35685,6 +35724,15 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35685
35724
|
await closeCaptureSession(probeSession);
|
|
35686
35725
|
probeSession = null;
|
|
35687
35726
|
}
|
|
35727
|
+
let useStreamingEncode = shouldUseStreamingEncode(cfg, outputFormat, workerCount, job.duration);
|
|
35728
|
+
log2.info("streaming-encode gate", {
|
|
35729
|
+
enabled: useStreamingEncode,
|
|
35730
|
+
configFlag: cfg.enableStreamingEncode,
|
|
35731
|
+
outputFormat,
|
|
35732
|
+
workerCount,
|
|
35733
|
+
durationSeconds: job.duration,
|
|
35734
|
+
maxDurationSeconds: cfg.streamingEncodeMaxDurationSeconds
|
|
35735
|
+
});
|
|
35688
35736
|
const captureAttempts = [];
|
|
35689
35737
|
const FORMAT_EXT2 = {
|
|
35690
35738
|
mp4: ".mp4",
|
|
@@ -36224,28 +36272,47 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
36224
36272
|
} else {
|
|
36225
36273
|
let streamingEncoder = null;
|
|
36226
36274
|
let streamingEncoderClosed = false;
|
|
36227
|
-
if (
|
|
36228
|
-
|
|
36229
|
-
|
|
36230
|
-
|
|
36231
|
-
|
|
36232
|
-
|
|
36233
|
-
|
|
36234
|
-
|
|
36235
|
-
|
|
36236
|
-
|
|
36237
|
-
|
|
36238
|
-
|
|
36239
|
-
|
|
36240
|
-
|
|
36241
|
-
|
|
36242
|
-
|
|
36243
|
-
|
|
36244
|
-
|
|
36245
|
-
|
|
36275
|
+
if (useStreamingEncode) {
|
|
36276
|
+
try {
|
|
36277
|
+
streamingEncoder = await spawnStreamingEncoder(
|
|
36278
|
+
videoOnlyPath,
|
|
36279
|
+
{
|
|
36280
|
+
fps: job.config.fps,
|
|
36281
|
+
width,
|
|
36282
|
+
height,
|
|
36283
|
+
codec: preset.codec,
|
|
36284
|
+
preset: preset.preset,
|
|
36285
|
+
quality: effectiveQuality,
|
|
36286
|
+
bitrate: effectiveBitrate,
|
|
36287
|
+
pixelFormat: preset.pixelFormat,
|
|
36288
|
+
useGpu: job.config.useGpu,
|
|
36289
|
+
imageFormat: captureOptions.format || "jpeg",
|
|
36290
|
+
hdr: preset.hdr
|
|
36291
|
+
},
|
|
36292
|
+
abortSignal
|
|
36293
|
+
);
|
|
36294
|
+
assertNotAborted();
|
|
36295
|
+
} catch (err) {
|
|
36296
|
+
if (abortSignal?.aborted) {
|
|
36297
|
+
if (streamingEncoder && !streamingEncoderClosed) {
|
|
36298
|
+
await streamingEncoder.close().catch(() => {
|
|
36299
|
+
});
|
|
36300
|
+
streamingEncoderClosed = true;
|
|
36301
|
+
}
|
|
36302
|
+
throw err;
|
|
36303
|
+
}
|
|
36304
|
+
useStreamingEncode = false;
|
|
36305
|
+
streamingEncoder = null;
|
|
36306
|
+
log2.warn("[Render] Streaming encoder spawn failed; falling back to disk-frame encode.", {
|
|
36307
|
+
error: err instanceof Error ? err.message : String(err),
|
|
36308
|
+
outputFormat,
|
|
36309
|
+
workerCount,
|
|
36310
|
+
durationSeconds: job.duration
|
|
36311
|
+
});
|
|
36312
|
+
}
|
|
36246
36313
|
}
|
|
36247
36314
|
try {
|
|
36248
|
-
if (
|
|
36315
|
+
if (useStreamingEncode && streamingEncoder) {
|
|
36249
36316
|
const reorderBuffer = createFrameReorderBuffer(0, totalFrames);
|
|
36250
36317
|
const currentEncoder = streamingEncoder;
|
|
36251
36318
|
if (workerCount > 1) {
|
|
@@ -38094,7 +38161,8 @@ var init_preview2 = __esm({
|
|
|
38094
38161
|
var init_exports = {};
|
|
38095
38162
|
__export(init_exports, {
|
|
38096
38163
|
default: () => init_default,
|
|
38097
|
-
examples: () => examples2
|
|
38164
|
+
examples: () => examples2,
|
|
38165
|
+
injectTailwindBrowserScript: () => injectTailwindBrowserScript
|
|
38098
38166
|
});
|
|
38099
38167
|
import {
|
|
38100
38168
|
existsSync as existsSync39,
|
|
@@ -38185,6 +38253,92 @@ function getStaticTemplateDir(templateId) {
|
|
|
38185
38253
|
function getSharedTemplateDir() {
|
|
38186
38254
|
return resolveAssetDir(["..", "templates", "_shared"], ["templates", "_shared"]);
|
|
38187
38255
|
}
|
|
38256
|
+
function toPackageName(projectName) {
|
|
38257
|
+
const normalized = basename5(projectName).trim().toLowerCase().replace(/^[._]+/, "").replace(/[^a-z0-9._~-]+/g, "-").replace(/-+/g, "-").replace(/^[-.]+|[-.]+$/g, "");
|
|
38258
|
+
return normalized || "hyperframes-project";
|
|
38259
|
+
}
|
|
38260
|
+
function getHyperframesPackageSpecifier() {
|
|
38261
|
+
return VERSION === "0.0.0-dev" ? "hyperframes" : `hyperframes@${VERSION}`;
|
|
38262
|
+
}
|
|
38263
|
+
function hyperframesScript(command2) {
|
|
38264
|
+
return `npx --yes ${getHyperframesPackageSpecifier()} ${command2}`;
|
|
38265
|
+
}
|
|
38266
|
+
function buildPackageScripts() {
|
|
38267
|
+
return {
|
|
38268
|
+
dev: hyperframesScript("preview"),
|
|
38269
|
+
check: `${hyperframesScript("lint")} && ${hyperframesScript("validate")} && ${hyperframesScript("inspect")}`,
|
|
38270
|
+
render: hyperframesScript("render"),
|
|
38271
|
+
publish: hyperframesScript("publish")
|
|
38272
|
+
};
|
|
38273
|
+
}
|
|
38274
|
+
function writeDefaultPackageJson(destDir, projectName) {
|
|
38275
|
+
const packageJsonPath = resolve22(destDir, "package.json");
|
|
38276
|
+
if (existsSync39(packageJsonPath)) return;
|
|
38277
|
+
writeFileSync18(
|
|
38278
|
+
packageJsonPath,
|
|
38279
|
+
`${JSON.stringify(
|
|
38280
|
+
{
|
|
38281
|
+
name: toPackageName(projectName),
|
|
38282
|
+
private: true,
|
|
38283
|
+
type: "module",
|
|
38284
|
+
scripts: buildPackageScripts()
|
|
38285
|
+
},
|
|
38286
|
+
null,
|
|
38287
|
+
2
|
|
38288
|
+
)}
|
|
38289
|
+
`,
|
|
38290
|
+
"utf-8"
|
|
38291
|
+
);
|
|
38292
|
+
}
|
|
38293
|
+
function listHtmlFiles(dir) {
|
|
38294
|
+
const files = [];
|
|
38295
|
+
const ignoredDirs = /* @__PURE__ */ new Set([".git", "dist", "node_modules"]);
|
|
38296
|
+
function walk(currentDir) {
|
|
38297
|
+
for (const entry of readdirSync14(currentDir, { withFileTypes: true })) {
|
|
38298
|
+
const entryPath = join41(currentDir, entry.name);
|
|
38299
|
+
if (entry.isDirectory()) {
|
|
38300
|
+
if (!ignoredDirs.has(entry.name)) walk(entryPath);
|
|
38301
|
+
continue;
|
|
38302
|
+
}
|
|
38303
|
+
if (entry.isFile() && entry.name.endsWith(".html")) {
|
|
38304
|
+
files.push(entryPath);
|
|
38305
|
+
}
|
|
38306
|
+
}
|
|
38307
|
+
}
|
|
38308
|
+
walk(dir);
|
|
38309
|
+
return files;
|
|
38310
|
+
}
|
|
38311
|
+
function injectTailwindBrowserScript(html) {
|
|
38312
|
+
if (html.includes(TAILWIND_BROWSER_SRC)) return html;
|
|
38313
|
+
const script = [
|
|
38314
|
+
`<script>`,
|
|
38315
|
+
`window.__tailwindReady=new Promise(function(resolve){`,
|
|
38316
|
+
`var loaded=document.readyState==="complete";`,
|
|
38317
|
+
`var resolved=false;`,
|
|
38318
|
+
`var observer;`,
|
|
38319
|
+
`function readTailwindCss(){var styles=document.querySelectorAll("style");for(var i=styles.length-1;i>=0;i--){var text=styles[i].textContent||"";if(text.indexOf("tailwindcss v")!==-1)return text;}return "";}`,
|
|
38320
|
+
`function finish(){if(resolved||!loaded||!readTailwindCss())return;resolved=true;if(observer)observer.disconnect();resolve(true);}`,
|
|
38321
|
+
`observer=new MutationObserver(finish);`,
|
|
38322
|
+
`observer.observe(document.documentElement,{childList:true,subtree:true,characterData:true});`,
|
|
38323
|
+
`if(loaded){finish();}else{window.addEventListener("load",function(){loaded=true;finish();},{once:true});}`,
|
|
38324
|
+
`});`,
|
|
38325
|
+
`</script>`,
|
|
38326
|
+
`<script src="${TAILWIND_BROWSER_SRC}" integrity="${TAILWIND_BROWSER_INTEGRITY}" crossorigin="anonymous"></script>`
|
|
38327
|
+
].join("\n");
|
|
38328
|
+
if (/<\/head>/i.test(html)) {
|
|
38329
|
+
return html.replace(/<\/head>/i, (closingHead) => `
|
|
38330
|
+
${script}
|
|
38331
|
+
${closingHead}`);
|
|
38332
|
+
}
|
|
38333
|
+
return `${script}
|
|
38334
|
+
${html}`;
|
|
38335
|
+
}
|
|
38336
|
+
function writeTailwindSupport(destDir) {
|
|
38337
|
+
for (const file of listHtmlFiles(destDir)) {
|
|
38338
|
+
const html = readFileSync27(file, "utf-8");
|
|
38339
|
+
writeFileSync18(file, injectTailwindBrowserScript(html), "utf-8");
|
|
38340
|
+
}
|
|
38341
|
+
}
|
|
38188
38342
|
function patchVideoSrc(dir, videoFilename, durationSeconds) {
|
|
38189
38343
|
const htmlFiles = readdirSync14(dir, { withFileTypes: true, recursive: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".html")).map((e2) => join41(e2.parentPath ?? e2.path, e2.name));
|
|
38190
38344
|
for (const file of htmlFiles) {
|
|
@@ -38290,7 +38444,7 @@ async function handleVideoFile(videoPath, destDir, interactive) {
|
|
|
38290
38444
|
}
|
|
38291
38445
|
return { meta, localVideoName };
|
|
38292
38446
|
}
|
|
38293
|
-
async function scaffoldProject(destDir, name, templateId, localVideoName, durationSeconds) {
|
|
38447
|
+
async function scaffoldProject(destDir, name, templateId, localVideoName, durationSeconds, tailwind = false) {
|
|
38294
38448
|
mkdirSync23(destDir, { recursive: true });
|
|
38295
38449
|
const templateDir = getStaticTemplateDir(templateId);
|
|
38296
38450
|
if (existsSync39(templateDir)) {
|
|
@@ -38299,6 +38453,7 @@ async function scaffoldProject(destDir, name, templateId, localVideoName, durati
|
|
|
38299
38453
|
await fetchRemoteTemplate(templateId, destDir);
|
|
38300
38454
|
}
|
|
38301
38455
|
patchVideoSrc(destDir, localVideoName, durationSeconds);
|
|
38456
|
+
if (tailwind) writeTailwindSupport(destDir);
|
|
38302
38457
|
writeFileSync18(
|
|
38303
38458
|
resolve22(destDir, "meta.json"),
|
|
38304
38459
|
JSON.stringify(
|
|
@@ -38316,6 +38471,7 @@ async function scaffoldProject(destDir, name, templateId, localVideoName, durati
|
|
|
38316
38471
|
const { writeProjectConfig: writeProjectConfig2, DEFAULT_PROJECT_CONFIG: DEFAULT_PROJECT_CONFIG2 } = await Promise.resolve().then(() => (init_projectConfig(), projectConfig_exports));
|
|
38317
38472
|
writeProjectConfig2(destDir, DEFAULT_PROJECT_CONFIG2);
|
|
38318
38473
|
}
|
|
38474
|
+
writeDefaultPackageJson(destDir, name);
|
|
38319
38475
|
const sharedDir = getSharedTemplateDir();
|
|
38320
38476
|
if (existsSync39(sharedDir)) {
|
|
38321
38477
|
for (const entry of readdirSync14(sharedDir, { withFileTypes: true })) {
|
|
@@ -38327,7 +38483,7 @@ async function scaffoldProject(destDir, name, templateId, localVideoName, durati
|
|
|
38327
38483
|
}
|
|
38328
38484
|
}
|
|
38329
38485
|
}
|
|
38330
|
-
var examples2, WEB_CODECS, DEFAULT_META, init_default;
|
|
38486
|
+
var examples2, WEB_CODECS, DEFAULT_META, TAILWIND_BROWSER_VERSION, TAILWIND_BROWSER_SRC, TAILWIND_BROWSER_INTEGRITY, init_default;
|
|
38331
38487
|
var init_init = __esm({
|
|
38332
38488
|
"src/commands/init.ts"() {
|
|
38333
38489
|
"use strict";
|
|
@@ -38339,11 +38495,13 @@ var init_init = __esm({
|
|
|
38339
38495
|
init_remote2();
|
|
38340
38496
|
init_events();
|
|
38341
38497
|
init_manager();
|
|
38498
|
+
init_version();
|
|
38342
38499
|
examples2 = [
|
|
38343
38500
|
["Create a project with the interactive wizard", "hyperframes init my-video"],
|
|
38344
38501
|
["Pick a starter example", "hyperframes init my-video --example warm-grain"],
|
|
38345
38502
|
["Start from an existing video file", "hyperframes init my-video --video clip.mp4"],
|
|
38346
38503
|
["Start from an audio file", "hyperframes init my-video --audio track.mp3"],
|
|
38504
|
+
["Scaffold with Tailwind CSS", "hyperframes init my-video --example blank --tailwind"],
|
|
38347
38505
|
["Non-interactive mode (for CI or AI agents)", "hyperframes init my-video --non-interactive"],
|
|
38348
38506
|
["Skip AI coding skills installation", "hyperframes init my-video --skip-skills"]
|
|
38349
38507
|
];
|
|
@@ -38356,6 +38514,9 @@ var init_init = __esm({
|
|
|
38356
38514
|
hasAudio: false,
|
|
38357
38515
|
videoCodec: "h264"
|
|
38358
38516
|
};
|
|
38517
|
+
TAILWIND_BROWSER_VERSION = "4.2.4";
|
|
38518
|
+
TAILWIND_BROWSER_SRC = `https://cdn.jsdelivr.net/npm/@tailwindcss/browser@${TAILWIND_BROWSER_VERSION}/dist/index.global.js`;
|
|
38519
|
+
TAILWIND_BROWSER_INTEGRITY = "sha384-v5YF9xS+gLRWdvrQ0u/WRbCkjSIH0NjHIPe8tBL1ZRrmI7PiSH6LLdzs0aAIMCuh";
|
|
38359
38520
|
init_default = defineCommand({
|
|
38360
38521
|
meta: {
|
|
38361
38522
|
name: "init",
|
|
@@ -38408,6 +38569,10 @@ var init_init = __esm({
|
|
|
38408
38569
|
"skip-skills": {
|
|
38409
38570
|
type: "boolean",
|
|
38410
38571
|
description: "Skip AI coding skills installation"
|
|
38572
|
+
},
|
|
38573
|
+
tailwind: {
|
|
38574
|
+
type: "boolean",
|
|
38575
|
+
description: "Add Tailwind CSS browser-runtime support"
|
|
38411
38576
|
}
|
|
38412
38577
|
},
|
|
38413
38578
|
async run({ args }) {
|
|
@@ -38425,6 +38590,7 @@ var init_init = __esm({
|
|
|
38425
38590
|
const audioFlag = args.audio;
|
|
38426
38591
|
const skipTranscribe = args["skip-transcribe"] === true;
|
|
38427
38592
|
const skipSkills = args["skip-skills"] === true;
|
|
38593
|
+
const tailwind = args.tailwind === true;
|
|
38428
38594
|
const nonInteractive = args["non-interactive"] === true;
|
|
38429
38595
|
const modelFlag = args.model;
|
|
38430
38596
|
const languageFlag = args.language;
|
|
@@ -38494,7 +38660,8 @@ var init_init = __esm({
|
|
|
38494
38660
|
basename5(destDir2),
|
|
38495
38661
|
templateId2,
|
|
38496
38662
|
localVideoName2,
|
|
38497
|
-
videoDuration2
|
|
38663
|
+
videoDuration2,
|
|
38664
|
+
tailwind
|
|
38498
38665
|
);
|
|
38499
38666
|
} catch (err) {
|
|
38500
38667
|
console.error(
|
|
@@ -38505,7 +38672,7 @@ var init_init = __esm({
|
|
|
38505
38672
|
console.error(c.dim("Use --example blank for offline use."));
|
|
38506
38673
|
process.exit(1);
|
|
38507
38674
|
}
|
|
38508
|
-
trackInitTemplate(templateId2);
|
|
38675
|
+
trackInitTemplate(templateId2, { tailwind });
|
|
38509
38676
|
const transcriptFile2 = resolve22(destDir2, "transcript.json");
|
|
38510
38677
|
if (existsSync39(transcriptFile2)) {
|
|
38511
38678
|
await patchTranscript(destDir2, transcriptFile2);
|
|
@@ -38532,10 +38699,13 @@ var init_init = __esm({
|
|
|
38532
38699
|
console.log(` ${c.dim("More patterns: hyperframes.heygen.com/guides/prompting")}`);
|
|
38533
38700
|
console.log();
|
|
38534
38701
|
console.log(` ${c.accent("4.")} Preview in the browser:`);
|
|
38535
|
-
console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("
|
|
38702
|
+
console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("npm run dev")}`);
|
|
38703
|
+
console.log();
|
|
38704
|
+
console.log(` ${c.accent("5.")} Check the composition:`);
|
|
38705
|
+
console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("npm run check")}`);
|
|
38536
38706
|
console.log();
|
|
38537
|
-
console.log(` ${c.accent("
|
|
38538
|
-
console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("
|
|
38707
|
+
console.log(` ${c.accent("6.")} Render to MP4 when ready:`);
|
|
38708
|
+
console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("npm run render")}`);
|
|
38539
38709
|
console.log();
|
|
38540
38710
|
console.log(` ${c.dim("Full docs: hyperframes.heygen.com")}`);
|
|
38541
38711
|
return;
|
|
@@ -38663,7 +38833,7 @@ var init_init = __esm({
|
|
|
38663
38833
|
spin.start(`Downloading example ${c.accent(templateId)}...`);
|
|
38664
38834
|
}
|
|
38665
38835
|
try {
|
|
38666
|
-
await scaffoldProject(destDir, name, templateId, localVideoName, videoDuration);
|
|
38836
|
+
await scaffoldProject(destDir, name, templateId, localVideoName, videoDuration, tailwind);
|
|
38667
38837
|
if (!isBundled) {
|
|
38668
38838
|
spin.stop(c.success(`Downloaded ${templateId}`));
|
|
38669
38839
|
}
|
|
@@ -38677,7 +38847,7 @@ ${c.dim("Use --example blank for offline use.")}`
|
|
|
38677
38847
|
);
|
|
38678
38848
|
process.exit(1);
|
|
38679
38849
|
}
|
|
38680
|
-
trackInitTemplate(templateId);
|
|
38850
|
+
trackInitTemplate(templateId, { tailwind });
|
|
38681
38851
|
const transcriptFile = resolve22(destDir, "transcript.json");
|
|
38682
38852
|
if (existsSync39(transcriptFile)) {
|
|
38683
38853
|
await patchTranscript(destDir, transcriptFile);
|
|
@@ -25,6 +25,7 @@ npx hyperframes init my-video # interactive wizard
|
|
|
25
25
|
npx hyperframes init my-video --example warm-grain # pick an example
|
|
26
26
|
npx hyperframes init my-video --video clip.mp4 # with video file
|
|
27
27
|
npx hyperframes init my-video --audio track.mp3 # with audio file
|
|
28
|
+
npx hyperframes init my-video --example blank --tailwind # with Tailwind v4 browser runtime
|
|
28
29
|
npx hyperframes init my-video --non-interactive # skip prompts (CI/agents)
|
|
29
30
|
```
|
|
30
31
|
|
|
@@ -32,6 +33,8 @@ Templates: `blank`, `warm-grain`, `play-mode`, `swiss-grid`, `vignelli`, `decisi
|
|
|
32
33
|
|
|
33
34
|
`init` creates the right file structure, copies media, transcribes audio with Whisper, and installs AI coding skills. Use it instead of creating files by hand.
|
|
34
35
|
|
|
36
|
+
When using `--tailwind`, invoke the `tailwind` skill before editing classes or theme tokens. The scaffold uses Tailwind v4.2 via the browser runtime, not Studio's Tailwind v3 setup.
|
|
37
|
+
|
|
35
38
|
## Linting
|
|
36
39
|
|
|
37
40
|
```bash
|
|
@@ -8,15 +8,15 @@ This project uses AI agent skills for framework-specific patterns. Install them
|
|
|
8
8
|
npx skills add heygen-com/hyperframes
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Skills encode patterns like `window.__timelines` registration, `data-*` attribute semantics, and shader-compatible CSS rules that are not in generic web docs. Using them produces correct compositions from the start.
|
|
11
|
+
Skills encode patterns like `window.__timelines` registration, `data-*` attribute semantics, Tailwind v4 browser-runtime styling for `--tailwind` projects, and shader-compatible CSS rules that are not in generic web docs. Using them produces correct compositions from the start.
|
|
12
12
|
|
|
13
13
|
## Commands
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
npm run dev # preview in browser (studio editor)
|
|
17
|
+
npm run check # lint + validate + inspect
|
|
18
|
+
npm run render # render to MP4
|
|
19
|
+
npm run publish # publish and get a shareable link
|
|
20
20
|
npx hyperframes docs <topic> # reference docs in terminal
|
|
21
21
|
```
|
|
22
22
|
|
|
@@ -30,10 +30,10 @@ npx hyperframes docs <topic> # reference docs in terminal
|
|
|
30
30
|
|
|
31
31
|
## Linting — Always Run After Changes
|
|
32
32
|
|
|
33
|
-
After creating or editing any `.html` composition, run the
|
|
33
|
+
After creating or editing any `.html` composition, run the full check before considering the task complete:
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
|
|
36
|
+
npm run check
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Fix all errors before presenting the result.
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
| **hyperframes-cli** | `/hyperframes-cli` | CLI commands: init, lint, preview, render, transcribe, tts |
|
|
11
11
|
| **hyperframes-registry** | `/hyperframes-registry` | Installing blocks and components via `hyperframes add` |
|
|
12
12
|
| **website-to-hyperframes** | `/website-to-hyperframes` | Capturing a URL and turning it into a video — full website-to-video pipeline |
|
|
13
|
+
| **tailwind** | `/tailwind` | Tailwind v4 browser-runtime styles for projects created with `hyperframes init --tailwind` |
|
|
13
14
|
| **gsap** | `/gsap` | GSAP animations for HyperFrames — tweens, timelines, easing, performance |
|
|
14
15
|
| **animejs** | `/animejs` | Anime.js animations registered on `window.__hfAnime` |
|
|
15
16
|
| **css-animations** | `/css-animations` | CSS keyframes that HyperFrames can pause and seek |
|
|
@@ -23,9 +24,10 @@
|
|
|
23
24
|
## Commands
|
|
24
25
|
|
|
25
26
|
```bash
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
npm run dev # preview in browser (studio editor)
|
|
28
|
+
npm run check # lint + validate + inspect
|
|
29
|
+
npm run render # render to MP4
|
|
30
|
+
npm run publish # publish and get a shareable link
|
|
29
31
|
npx hyperframes lint --verbose # include info-level findings
|
|
30
32
|
npx hyperframes lint --json # machine-readable output for CI
|
|
31
33
|
npx hyperframes docs <topic> # reference docs in terminal
|
|
@@ -56,13 +58,13 @@ https://hyperframes.heygen.com/llms.txt
|
|
|
56
58
|
|
|
57
59
|
## Linting — ALWAYS RUN AFTER CHANGES
|
|
58
60
|
|
|
59
|
-
After creating or editing any `.html` composition, **always** run the
|
|
61
|
+
After creating or editing any `.html` composition, **always** run the full check before considering the task complete:
|
|
60
62
|
|
|
61
63
|
```bash
|
|
62
|
-
|
|
64
|
+
npm run check
|
|
63
65
|
```
|
|
64
66
|
|
|
65
|
-
Fix all errors before presenting the result.
|
|
67
|
+
Fix all errors before presenting the result. Inspect warnings should be reviewed before rendering.
|
|
66
68
|
|
|
67
69
|
## Key Rules
|
|
68
70
|
|