hyperframes 0.4.40 → 0.4.41
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.4.
|
|
57
|
+
VERSION = true ? "0.4.41" : "0.0.0-dev";
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -10206,8 +10206,8 @@ function trackRenderError(props) {
|
|
|
10206
10206
|
memory_free_mb: props.memoryFreeMb
|
|
10207
10207
|
});
|
|
10208
10208
|
}
|
|
10209
|
-
function trackInitTemplate(templateId) {
|
|
10210
|
-
trackEvent("init_template", { template: templateId });
|
|
10209
|
+
function trackInitTemplate(templateId, props) {
|
|
10210
|
+
trackEvent("init_template", { template: templateId, tailwind: props?.tailwind });
|
|
10211
10211
|
}
|
|
10212
10212
|
function trackBrowserInstall() {
|
|
10213
10213
|
trackEvent("browser_install", {});
|
|
@@ -25516,6 +25516,13 @@ function resolveConfig(overrides) {
|
|
|
25516
25516
|
"PRODUCER_ENABLE_STREAMING_ENCODE",
|
|
25517
25517
|
DEFAULT_CONFIG2.enableStreamingEncode
|
|
25518
25518
|
),
|
|
25519
|
+
streamingEncodeMaxDurationSeconds: Math.max(
|
|
25520
|
+
0,
|
|
25521
|
+
envNum(
|
|
25522
|
+
"PRODUCER_STREAMING_ENCODE_MAX_DURATION_SECONDS",
|
|
25523
|
+
DEFAULT_CONFIG2.streamingEncodeMaxDurationSeconds
|
|
25524
|
+
)
|
|
25525
|
+
),
|
|
25519
25526
|
ffmpegEncodeTimeout: envNum("FFMPEG_ENCODE_TIMEOUT_MS", DEFAULT_CONFIG2.ffmpegEncodeTimeout),
|
|
25520
25527
|
ffmpegProcessTimeout: envNum("FFMPEG_PROCESS_TIMEOUT_MS", DEFAULT_CONFIG2.ffmpegProcessTimeout),
|
|
25521
25528
|
ffmpegStreamingTimeout: envNum(
|
|
@@ -25573,7 +25580,8 @@ var init_config2 = __esm({
|
|
|
25573
25580
|
forceScreenshot: false,
|
|
25574
25581
|
enableChunkedEncode: false,
|
|
25575
25582
|
chunkSizeFrames: 360,
|
|
25576
|
-
enableStreamingEncode:
|
|
25583
|
+
enableStreamingEncode: true,
|
|
25584
|
+
streamingEncodeMaxDurationSeconds: 240,
|
|
25577
25585
|
ffmpegEncodeTimeout: 6e5,
|
|
25578
25586
|
ffmpegProcessTimeout: 3e5,
|
|
25579
25587
|
ffmpegStreamingTimeout: 6e5,
|
|
@@ -26203,6 +26211,23 @@ async function applyVideoMetadataHints(page, hints) {
|
|
|
26203
26211
|
[...hints]
|
|
26204
26212
|
);
|
|
26205
26213
|
}
|
|
26214
|
+
async function waitForOptionalTailwindReady(page, timeoutMs) {
|
|
26215
|
+
const hasTailwindReady = await page.evaluate(
|
|
26216
|
+
`(() => { const ready = window.__tailwindReady; return !!ready && typeof ready.then === "function"; })()`
|
|
26217
|
+
);
|
|
26218
|
+
if (!hasTailwindReady) return;
|
|
26219
|
+
const ready = await Promise.race([
|
|
26220
|
+
page.evaluate(
|
|
26221
|
+
`Promise.resolve(window.__tailwindReady).then(() => true, () => false)`
|
|
26222
|
+
),
|
|
26223
|
+
new Promise((resolve39) => setTimeout(() => resolve39(false), timeoutMs))
|
|
26224
|
+
]);
|
|
26225
|
+
if (!ready) {
|
|
26226
|
+
throw new Error(
|
|
26227
|
+
`[FrameCapture] window.__tailwindReady not resolved after ${timeoutMs}ms. Tailwind browser runtime must finish before frame capture starts.`
|
|
26228
|
+
);
|
|
26229
|
+
}
|
|
26230
|
+
}
|
|
26206
26231
|
async function initializeSession(session) {
|
|
26207
26232
|
const { page, serverUrl } = session;
|
|
26208
26233
|
page.on("console", (msg) => {
|
|
@@ -26259,6 +26284,7 @@ async function initializeSession(session) {
|
|
|
26259
26284
|
);
|
|
26260
26285
|
}
|
|
26261
26286
|
await page.evaluate(`document.fonts?.ready`);
|
|
26287
|
+
await waitForOptionalTailwindReady(page, pageReadyTimeout2);
|
|
26262
26288
|
if (session.options.format === "png") {
|
|
26263
26289
|
await initTransparentBackground(session.page);
|
|
26264
26290
|
}
|
|
@@ -26324,6 +26350,7 @@ async function initializeSession(session) {
|
|
|
26324
26350
|
await new Promise((r2) => setTimeout(r2, 100));
|
|
26325
26351
|
}
|
|
26326
26352
|
await page.evaluate(`document.fonts?.ready`);
|
|
26353
|
+
await waitForOptionalTailwindReady(page, pageReadyTimeout);
|
|
26327
26354
|
warmupRunning = false;
|
|
26328
26355
|
session.beginFrameTimeTicks = (warmupTicks + 10) * session.beginFrameIntervalMs;
|
|
26329
26356
|
if (session.options.format === "png") {
|
|
@@ -34811,6 +34838,27 @@ function createRenderJob(config) {
|
|
|
34811
34838
|
function normalizeCompositionSrcPath(srcPath) {
|
|
34812
34839
|
return srcPath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
34813
34840
|
}
|
|
34841
|
+
function createStandaloneEntryRenderClone(root, host) {
|
|
34842
|
+
const hostClone = host.cloneNode(true);
|
|
34843
|
+
hostClone.setAttribute("data-start", "0");
|
|
34844
|
+
if (root === host) return hostClone;
|
|
34845
|
+
const rootClone = root.cloneNode(false);
|
|
34846
|
+
rootClone.appendChild(hostClone);
|
|
34847
|
+
return rootClone;
|
|
34848
|
+
}
|
|
34849
|
+
function replaceBodyWithRenderClone(body, renderClone) {
|
|
34850
|
+
while (body.firstChild) {
|
|
34851
|
+
body.removeChild(body.firstChild);
|
|
34852
|
+
}
|
|
34853
|
+
body.appendChild(renderClone);
|
|
34854
|
+
}
|
|
34855
|
+
function shouldUseStreamingEncode(cfg, outputFormat, workerCount, durationSeconds) {
|
|
34856
|
+
if (!cfg.enableStreamingEncode) return false;
|
|
34857
|
+
if (outputFormat === "png-sequence") return false;
|
|
34858
|
+
if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) return false;
|
|
34859
|
+
if (durationSeconds > cfg.streamingEncodeMaxDurationSeconds) return false;
|
|
34860
|
+
return workerCount === 1;
|
|
34861
|
+
}
|
|
34814
34862
|
function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
|
|
34815
34863
|
const normalizedEntryFile = normalizeCompositionSrcPath(entryFile);
|
|
34816
34864
|
const { document: document2 } = parseHTML(indexHtml);
|
|
@@ -34825,16 +34873,8 @@ function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
|
|
|
34825
34873
|
(candidate) => candidate.hasAttribute("data-composition-id")
|
|
34826
34874
|
) ?? null;
|
|
34827
34875
|
if (!root) return null;
|
|
34828
|
-
const
|
|
34829
|
-
|
|
34830
|
-
body.innerHTML = "";
|
|
34831
|
-
if (root === host) {
|
|
34832
|
-
body.appendChild(hostClone);
|
|
34833
|
-
return document2.toString();
|
|
34834
|
-
}
|
|
34835
|
-
const rootClone = root.cloneNode(false);
|
|
34836
|
-
rootClone.appendChild(hostClone);
|
|
34837
|
-
body.appendChild(rootClone);
|
|
34876
|
+
const renderClone = createStandaloneEntryRenderClone(root, host);
|
|
34877
|
+
replaceBodyWithRenderClone(body, renderClone);
|
|
34838
34878
|
return document2.toString();
|
|
34839
34879
|
}
|
|
34840
34880
|
async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSignal) {
|
|
@@ -34866,7 +34906,6 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34866
34906
|
}
|
|
34867
34907
|
const enableChunkedEncode = cfg.enableChunkedEncode;
|
|
34868
34908
|
const chunkedEncodeSize = cfg.chunkSizeFrames;
|
|
34869
|
-
const enableStreamingEncode = cfg.enableStreamingEncode && !isPngSequence;
|
|
34870
34909
|
let peakRssBytes = 0;
|
|
34871
34910
|
let peakHeapUsedBytes = 0;
|
|
34872
34911
|
const sampleMemory = () => {
|
|
@@ -35435,6 +35474,15 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35435
35474
|
await closeCaptureSession(probeSession);
|
|
35436
35475
|
probeSession = null;
|
|
35437
35476
|
}
|
|
35477
|
+
let useStreamingEncode = shouldUseStreamingEncode(cfg, outputFormat, workerCount, job.duration);
|
|
35478
|
+
log2.info("streaming-encode gate", {
|
|
35479
|
+
enabled: useStreamingEncode,
|
|
35480
|
+
configFlag: cfg.enableStreamingEncode,
|
|
35481
|
+
outputFormat,
|
|
35482
|
+
workerCount,
|
|
35483
|
+
durationSeconds: job.duration,
|
|
35484
|
+
maxDurationSeconds: cfg.streamingEncodeMaxDurationSeconds
|
|
35485
|
+
});
|
|
35438
35486
|
const captureAttempts = [];
|
|
35439
35487
|
const FORMAT_EXT2 = {
|
|
35440
35488
|
mp4: ".mp4",
|
|
@@ -35974,28 +36022,47 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
35974
36022
|
} else {
|
|
35975
36023
|
let streamingEncoder = null;
|
|
35976
36024
|
let streamingEncoderClosed = false;
|
|
35977
|
-
if (
|
|
35978
|
-
|
|
35979
|
-
|
|
35980
|
-
|
|
35981
|
-
|
|
35982
|
-
|
|
35983
|
-
|
|
35984
|
-
|
|
35985
|
-
|
|
35986
|
-
|
|
35987
|
-
|
|
35988
|
-
|
|
35989
|
-
|
|
35990
|
-
|
|
35991
|
-
|
|
35992
|
-
|
|
35993
|
-
|
|
35994
|
-
|
|
35995
|
-
|
|
36025
|
+
if (useStreamingEncode) {
|
|
36026
|
+
try {
|
|
36027
|
+
streamingEncoder = await spawnStreamingEncoder(
|
|
36028
|
+
videoOnlyPath,
|
|
36029
|
+
{
|
|
36030
|
+
fps: job.config.fps,
|
|
36031
|
+
width,
|
|
36032
|
+
height,
|
|
36033
|
+
codec: preset.codec,
|
|
36034
|
+
preset: preset.preset,
|
|
36035
|
+
quality: effectiveQuality,
|
|
36036
|
+
bitrate: effectiveBitrate,
|
|
36037
|
+
pixelFormat: preset.pixelFormat,
|
|
36038
|
+
useGpu: job.config.useGpu,
|
|
36039
|
+
imageFormat: captureOptions.format || "jpeg",
|
|
36040
|
+
hdr: preset.hdr
|
|
36041
|
+
},
|
|
36042
|
+
abortSignal
|
|
36043
|
+
);
|
|
36044
|
+
assertNotAborted();
|
|
36045
|
+
} catch (err) {
|
|
36046
|
+
if (abortSignal?.aborted) {
|
|
36047
|
+
if (streamingEncoder && !streamingEncoderClosed) {
|
|
36048
|
+
await streamingEncoder.close().catch(() => {
|
|
36049
|
+
});
|
|
36050
|
+
streamingEncoderClosed = true;
|
|
36051
|
+
}
|
|
36052
|
+
throw err;
|
|
36053
|
+
}
|
|
36054
|
+
useStreamingEncode = false;
|
|
36055
|
+
streamingEncoder = null;
|
|
36056
|
+
log2.warn("[Render] Streaming encoder spawn failed; falling back to disk-frame encode.", {
|
|
36057
|
+
error: err instanceof Error ? err.message : String(err),
|
|
36058
|
+
outputFormat,
|
|
36059
|
+
workerCount,
|
|
36060
|
+
durationSeconds: job.duration
|
|
36061
|
+
});
|
|
36062
|
+
}
|
|
35996
36063
|
}
|
|
35997
36064
|
try {
|
|
35998
|
-
if (
|
|
36065
|
+
if (useStreamingEncode && streamingEncoder) {
|
|
35999
36066
|
const reorderBuffer = createFrameReorderBuffer(0, totalFrames);
|
|
36000
36067
|
const currentEncoder = streamingEncoder;
|
|
36001
36068
|
if (workerCount > 1) {
|
|
@@ -37833,7 +37900,8 @@ var init_preview2 = __esm({
|
|
|
37833
37900
|
var init_exports = {};
|
|
37834
37901
|
__export(init_exports, {
|
|
37835
37902
|
default: () => init_default,
|
|
37836
|
-
examples: () => examples2
|
|
37903
|
+
examples: () => examples2,
|
|
37904
|
+
injectTailwindBrowserScript: () => injectTailwindBrowserScript
|
|
37837
37905
|
});
|
|
37838
37906
|
import {
|
|
37839
37907
|
existsSync as existsSync38,
|
|
@@ -37924,6 +37992,92 @@ function getStaticTemplateDir(templateId) {
|
|
|
37924
37992
|
function getSharedTemplateDir() {
|
|
37925
37993
|
return resolveAssetDir(["..", "templates", "_shared"], ["templates", "_shared"]);
|
|
37926
37994
|
}
|
|
37995
|
+
function toPackageName(projectName) {
|
|
37996
|
+
const normalized = basename5(projectName).trim().toLowerCase().replace(/^[._]+/, "").replace(/[^a-z0-9._~-]+/g, "-").replace(/-+/g, "-").replace(/^[-.]+|[-.]+$/g, "");
|
|
37997
|
+
return normalized || "hyperframes-project";
|
|
37998
|
+
}
|
|
37999
|
+
function getHyperframesPackageSpecifier() {
|
|
38000
|
+
return VERSION === "0.0.0-dev" ? "hyperframes" : `hyperframes@${VERSION}`;
|
|
38001
|
+
}
|
|
38002
|
+
function hyperframesScript(command2) {
|
|
38003
|
+
return `npx --yes ${getHyperframesPackageSpecifier()} ${command2}`;
|
|
38004
|
+
}
|
|
38005
|
+
function buildPackageScripts() {
|
|
38006
|
+
return {
|
|
38007
|
+
dev: hyperframesScript("preview"),
|
|
38008
|
+
check: `${hyperframesScript("lint")} && ${hyperframesScript("validate")} && ${hyperframesScript("inspect")}`,
|
|
38009
|
+
render: hyperframesScript("render"),
|
|
38010
|
+
publish: hyperframesScript("publish")
|
|
38011
|
+
};
|
|
38012
|
+
}
|
|
38013
|
+
function writeDefaultPackageJson(destDir, projectName) {
|
|
38014
|
+
const packageJsonPath = resolve22(destDir, "package.json");
|
|
38015
|
+
if (existsSync38(packageJsonPath)) return;
|
|
38016
|
+
writeFileSync18(
|
|
38017
|
+
packageJsonPath,
|
|
38018
|
+
`${JSON.stringify(
|
|
38019
|
+
{
|
|
38020
|
+
name: toPackageName(projectName),
|
|
38021
|
+
private: true,
|
|
38022
|
+
type: "module",
|
|
38023
|
+
scripts: buildPackageScripts()
|
|
38024
|
+
},
|
|
38025
|
+
null,
|
|
38026
|
+
2
|
|
38027
|
+
)}
|
|
38028
|
+
`,
|
|
38029
|
+
"utf-8"
|
|
38030
|
+
);
|
|
38031
|
+
}
|
|
38032
|
+
function listHtmlFiles(dir) {
|
|
38033
|
+
const files = [];
|
|
38034
|
+
const ignoredDirs = /* @__PURE__ */ new Set([".git", "dist", "node_modules"]);
|
|
38035
|
+
function walk(currentDir) {
|
|
38036
|
+
for (const entry of readdirSync13(currentDir, { withFileTypes: true })) {
|
|
38037
|
+
const entryPath = join40(currentDir, entry.name);
|
|
38038
|
+
if (entry.isDirectory()) {
|
|
38039
|
+
if (!ignoredDirs.has(entry.name)) walk(entryPath);
|
|
38040
|
+
continue;
|
|
38041
|
+
}
|
|
38042
|
+
if (entry.isFile() && entry.name.endsWith(".html")) {
|
|
38043
|
+
files.push(entryPath);
|
|
38044
|
+
}
|
|
38045
|
+
}
|
|
38046
|
+
}
|
|
38047
|
+
walk(dir);
|
|
38048
|
+
return files;
|
|
38049
|
+
}
|
|
38050
|
+
function injectTailwindBrowserScript(html) {
|
|
38051
|
+
if (html.includes(TAILWIND_BROWSER_SRC)) return html;
|
|
38052
|
+
const script = [
|
|
38053
|
+
`<script>`,
|
|
38054
|
+
`window.__tailwindReady=new Promise(function(resolve){`,
|
|
38055
|
+
`var loaded=document.readyState==="complete";`,
|
|
38056
|
+
`var resolved=false;`,
|
|
38057
|
+
`var observer;`,
|
|
38058
|
+
`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 "";}`,
|
|
38059
|
+
`function finish(){if(resolved||!loaded||!readTailwindCss())return;resolved=true;if(observer)observer.disconnect();resolve(true);}`,
|
|
38060
|
+
`observer=new MutationObserver(finish);`,
|
|
38061
|
+
`observer.observe(document.documentElement,{childList:true,subtree:true,characterData:true});`,
|
|
38062
|
+
`if(loaded){finish();}else{window.addEventListener("load",function(){loaded=true;finish();},{once:true});}`,
|
|
38063
|
+
`});`,
|
|
38064
|
+
`</script>`,
|
|
38065
|
+
`<script src="${TAILWIND_BROWSER_SRC}" integrity="${TAILWIND_BROWSER_INTEGRITY}" crossorigin="anonymous"></script>`
|
|
38066
|
+
].join("\n");
|
|
38067
|
+
if (/<\/head>/i.test(html)) {
|
|
38068
|
+
return html.replace(/<\/head>/i, (closingHead) => `
|
|
38069
|
+
${script}
|
|
38070
|
+
${closingHead}`);
|
|
38071
|
+
}
|
|
38072
|
+
return `${script}
|
|
38073
|
+
${html}`;
|
|
38074
|
+
}
|
|
38075
|
+
function writeTailwindSupport(destDir) {
|
|
38076
|
+
for (const file of listHtmlFiles(destDir)) {
|
|
38077
|
+
const html = readFileSync27(file, "utf-8");
|
|
38078
|
+
writeFileSync18(file, injectTailwindBrowserScript(html), "utf-8");
|
|
38079
|
+
}
|
|
38080
|
+
}
|
|
37927
38081
|
function patchVideoSrc(dir, videoFilename, durationSeconds) {
|
|
37928
38082
|
const htmlFiles = readdirSync13(dir, { withFileTypes: true, recursive: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".html")).map((e2) => join40(e2.parentPath ?? e2.path, e2.name));
|
|
37929
38083
|
for (const file of htmlFiles) {
|
|
@@ -38029,7 +38183,7 @@ async function handleVideoFile(videoPath, destDir, interactive) {
|
|
|
38029
38183
|
}
|
|
38030
38184
|
return { meta, localVideoName };
|
|
38031
38185
|
}
|
|
38032
|
-
async function scaffoldProject(destDir, name, templateId, localVideoName, durationSeconds) {
|
|
38186
|
+
async function scaffoldProject(destDir, name, templateId, localVideoName, durationSeconds, tailwind = false) {
|
|
38033
38187
|
mkdirSync23(destDir, { recursive: true });
|
|
38034
38188
|
const templateDir = getStaticTemplateDir(templateId);
|
|
38035
38189
|
if (existsSync38(templateDir)) {
|
|
@@ -38038,6 +38192,7 @@ async function scaffoldProject(destDir, name, templateId, localVideoName, durati
|
|
|
38038
38192
|
await fetchRemoteTemplate(templateId, destDir);
|
|
38039
38193
|
}
|
|
38040
38194
|
patchVideoSrc(destDir, localVideoName, durationSeconds);
|
|
38195
|
+
if (tailwind) writeTailwindSupport(destDir);
|
|
38041
38196
|
writeFileSync18(
|
|
38042
38197
|
resolve22(destDir, "meta.json"),
|
|
38043
38198
|
JSON.stringify(
|
|
@@ -38055,6 +38210,7 @@ async function scaffoldProject(destDir, name, templateId, localVideoName, durati
|
|
|
38055
38210
|
const { writeProjectConfig: writeProjectConfig2, DEFAULT_PROJECT_CONFIG: DEFAULT_PROJECT_CONFIG2 } = await Promise.resolve().then(() => (init_projectConfig(), projectConfig_exports));
|
|
38056
38211
|
writeProjectConfig2(destDir, DEFAULT_PROJECT_CONFIG2);
|
|
38057
38212
|
}
|
|
38213
|
+
writeDefaultPackageJson(destDir, name);
|
|
38058
38214
|
const sharedDir = getSharedTemplateDir();
|
|
38059
38215
|
if (existsSync38(sharedDir)) {
|
|
38060
38216
|
for (const entry of readdirSync13(sharedDir, { withFileTypes: true })) {
|
|
@@ -38066,7 +38222,7 @@ async function scaffoldProject(destDir, name, templateId, localVideoName, durati
|
|
|
38066
38222
|
}
|
|
38067
38223
|
}
|
|
38068
38224
|
}
|
|
38069
|
-
var examples2, WEB_CODECS, DEFAULT_META, init_default;
|
|
38225
|
+
var examples2, WEB_CODECS, DEFAULT_META, TAILWIND_BROWSER_VERSION, TAILWIND_BROWSER_SRC, TAILWIND_BROWSER_INTEGRITY, init_default;
|
|
38070
38226
|
var init_init = __esm({
|
|
38071
38227
|
"src/commands/init.ts"() {
|
|
38072
38228
|
"use strict";
|
|
@@ -38078,11 +38234,13 @@ var init_init = __esm({
|
|
|
38078
38234
|
init_remote2();
|
|
38079
38235
|
init_events();
|
|
38080
38236
|
init_manager();
|
|
38237
|
+
init_version();
|
|
38081
38238
|
examples2 = [
|
|
38082
38239
|
["Create a project with the interactive wizard", "hyperframes init my-video"],
|
|
38083
38240
|
["Pick a starter example", "hyperframes init my-video --example warm-grain"],
|
|
38084
38241
|
["Start from an existing video file", "hyperframes init my-video --video clip.mp4"],
|
|
38085
38242
|
["Start from an audio file", "hyperframes init my-video --audio track.mp3"],
|
|
38243
|
+
["Scaffold with Tailwind CSS", "hyperframes init my-video --example blank --tailwind"],
|
|
38086
38244
|
["Non-interactive mode (for CI or AI agents)", "hyperframes init my-video --non-interactive"],
|
|
38087
38245
|
["Skip AI coding skills installation", "hyperframes init my-video --skip-skills"]
|
|
38088
38246
|
];
|
|
@@ -38095,6 +38253,9 @@ var init_init = __esm({
|
|
|
38095
38253
|
hasAudio: false,
|
|
38096
38254
|
videoCodec: "h264"
|
|
38097
38255
|
};
|
|
38256
|
+
TAILWIND_BROWSER_VERSION = "4.2.4";
|
|
38257
|
+
TAILWIND_BROWSER_SRC = `https://cdn.jsdelivr.net/npm/@tailwindcss/browser@${TAILWIND_BROWSER_VERSION}/dist/index.global.js`;
|
|
38258
|
+
TAILWIND_BROWSER_INTEGRITY = "sha384-v5YF9xS+gLRWdvrQ0u/WRbCkjSIH0NjHIPe8tBL1ZRrmI7PiSH6LLdzs0aAIMCuh";
|
|
38098
38259
|
init_default = defineCommand({
|
|
38099
38260
|
meta: {
|
|
38100
38261
|
name: "init",
|
|
@@ -38147,6 +38308,10 @@ var init_init = __esm({
|
|
|
38147
38308
|
"skip-skills": {
|
|
38148
38309
|
type: "boolean",
|
|
38149
38310
|
description: "Skip AI coding skills installation"
|
|
38311
|
+
},
|
|
38312
|
+
tailwind: {
|
|
38313
|
+
type: "boolean",
|
|
38314
|
+
description: "Add Tailwind CSS browser-runtime support"
|
|
38150
38315
|
}
|
|
38151
38316
|
},
|
|
38152
38317
|
async run({ args }) {
|
|
@@ -38164,6 +38329,7 @@ var init_init = __esm({
|
|
|
38164
38329
|
const audioFlag = args.audio;
|
|
38165
38330
|
const skipTranscribe = args["skip-transcribe"] === true;
|
|
38166
38331
|
const skipSkills = args["skip-skills"] === true;
|
|
38332
|
+
const tailwind = args.tailwind === true;
|
|
38167
38333
|
const nonInteractive = args["non-interactive"] === true;
|
|
38168
38334
|
const modelFlag = args.model;
|
|
38169
38335
|
const languageFlag = args.language;
|
|
@@ -38233,7 +38399,8 @@ var init_init = __esm({
|
|
|
38233
38399
|
basename5(destDir2),
|
|
38234
38400
|
templateId2,
|
|
38235
38401
|
localVideoName2,
|
|
38236
|
-
videoDuration2
|
|
38402
|
+
videoDuration2,
|
|
38403
|
+
tailwind
|
|
38237
38404
|
);
|
|
38238
38405
|
} catch (err) {
|
|
38239
38406
|
console.error(
|
|
@@ -38244,7 +38411,7 @@ var init_init = __esm({
|
|
|
38244
38411
|
console.error(c.dim("Use --example blank for offline use."));
|
|
38245
38412
|
process.exit(1);
|
|
38246
38413
|
}
|
|
38247
|
-
trackInitTemplate(templateId2);
|
|
38414
|
+
trackInitTemplate(templateId2, { tailwind });
|
|
38248
38415
|
const transcriptFile2 = resolve22(destDir2, "transcript.json");
|
|
38249
38416
|
if (existsSync38(transcriptFile2)) {
|
|
38250
38417
|
await patchTranscript(destDir2, transcriptFile2);
|
|
@@ -38271,10 +38438,13 @@ var init_init = __esm({
|
|
|
38271
38438
|
console.log(` ${c.dim("More patterns: hyperframes.heygen.com/guides/prompting")}`);
|
|
38272
38439
|
console.log();
|
|
38273
38440
|
console.log(` ${c.accent("4.")} Preview in the browser:`);
|
|
38274
|
-
console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("
|
|
38441
|
+
console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("npm run dev")}`);
|
|
38442
|
+
console.log();
|
|
38443
|
+
console.log(` ${c.accent("5.")} Check the composition:`);
|
|
38444
|
+
console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("npm run check")}`);
|
|
38275
38445
|
console.log();
|
|
38276
|
-
console.log(` ${c.accent("
|
|
38277
|
-
console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("
|
|
38446
|
+
console.log(` ${c.accent("6.")} Render to MP4 when ready:`);
|
|
38447
|
+
console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("npm run render")}`);
|
|
38278
38448
|
console.log();
|
|
38279
38449
|
console.log(` ${c.dim("Full docs: hyperframes.heygen.com")}`);
|
|
38280
38450
|
return;
|
|
@@ -38402,7 +38572,7 @@ var init_init = __esm({
|
|
|
38402
38572
|
spin.start(`Downloading example ${c.accent(templateId)}...`);
|
|
38403
38573
|
}
|
|
38404
38574
|
try {
|
|
38405
|
-
await scaffoldProject(destDir, name, templateId, localVideoName, videoDuration);
|
|
38575
|
+
await scaffoldProject(destDir, name, templateId, localVideoName, videoDuration, tailwind);
|
|
38406
38576
|
if (!isBundled) {
|
|
38407
38577
|
spin.stop(c.success(`Downloaded ${templateId}`));
|
|
38408
38578
|
}
|
|
@@ -38416,7 +38586,7 @@ ${c.dim("Use --example blank for offline use.")}`
|
|
|
38416
38586
|
);
|
|
38417
38587
|
process.exit(1);
|
|
38418
38588
|
}
|
|
38419
|
-
trackInitTemplate(templateId);
|
|
38589
|
+
trackInitTemplate(templateId, { tailwind });
|
|
38420
38590
|
const transcriptFile = resolve22(destDir, "transcript.json");
|
|
38421
38591
|
if (existsSync38(transcriptFile)) {
|
|
38422
38592
|
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
|
|