hyperframes 0.1.9 → 0.1.10
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 +591 -249
- package/dist/skills/{captions → hyperframes-captions}/SKILL.md +1 -1
- package/dist/skills/{compose-video → hyperframes-compose}/SKILL.md +1 -1
- package/dist/templates/_shared/CLAUDE.md +21 -9
- package/dist/templates/play-mode/index.html +1 -1
- package/dist/templates/swiss-grid/compositions/graphics.html +1 -1
- package/dist/templates/swiss-grid/index.html +1 -1
- package/dist/templates/vignelli/index.html +1 -1
- package/dist/templates/warm-grain/index.html +1 -1
- package/package.json +2 -2
- /package/dist/skills/{compose-video → hyperframes-compose}/data-in-motion.md +0 -0
- /package/dist/skills/{compose-video → hyperframes-compose}/house-style.md +0 -0
- /package/dist/skills/{compose-video → hyperframes-compose}/palettes/bold-energetic.md +0 -0
- /package/dist/skills/{compose-video → hyperframes-compose}/palettes/clean-corporate.md +0 -0
- /package/dist/skills/{compose-video → hyperframes-compose}/palettes/dark-premium.md +0 -0
- /package/dist/skills/{compose-video → hyperframes-compose}/palettes/jewel-rich.md +0 -0
- /package/dist/skills/{compose-video → hyperframes-compose}/palettes/monochrome.md +0 -0
- /package/dist/skills/{compose-video → hyperframes-compose}/palettes/nature-earth.md +0 -0
- /package/dist/skills/{compose-video → hyperframes-compose}/palettes/neon-electric.md +0 -0
- /package/dist/skills/{compose-video → hyperframes-compose}/palettes/pastel-soft.md +0 -0
- /package/dist/skills/{compose-video → hyperframes-compose}/palettes/warm-editorial.md +0 -0
- /package/dist/skills/{compose-video → hyperframes-compose}/patterns.md +0 -0
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.10" : "0.0.0-dev";
|
|
426
426
|
}
|
|
427
427
|
});
|
|
428
428
|
|
|
@@ -599,6 +599,97 @@ var init_env = __esm({
|
|
|
599
599
|
}
|
|
600
600
|
});
|
|
601
601
|
|
|
602
|
+
// src/telemetry/system.ts
|
|
603
|
+
var system_exports = {};
|
|
604
|
+
__export(system_exports, {
|
|
605
|
+
bytesToMb: () => bytesToMb,
|
|
606
|
+
getFreeDiskMb: () => getFreeDiskMb,
|
|
607
|
+
getShmSizeMb: () => getShmSizeMb,
|
|
608
|
+
getSystemMeta: () => getSystemMeta
|
|
609
|
+
});
|
|
610
|
+
import { cpus, totalmem, platform, release } from "os";
|
|
611
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, statfsSync } from "fs";
|
|
612
|
+
function bytesToMb(bytes) {
|
|
613
|
+
return Math.trunc(bytes / (1024 * 1024));
|
|
614
|
+
}
|
|
615
|
+
function getSystemMeta() {
|
|
616
|
+
if (cached) return cached;
|
|
617
|
+
const cpuInfo = cpus();
|
|
618
|
+
const firstCpu = cpuInfo[0] ?? null;
|
|
619
|
+
cached = {
|
|
620
|
+
os_release: release(),
|
|
621
|
+
cpu_count: cpuInfo.length,
|
|
622
|
+
cpu_model: firstCpu?.model?.trim() ?? null,
|
|
623
|
+
cpu_speed: firstCpu?.speed ?? null,
|
|
624
|
+
memory_total_mb: bytesToMb(totalmem()),
|
|
625
|
+
is_docker: detectDocker(),
|
|
626
|
+
is_ci: detectCI(),
|
|
627
|
+
ci_name: getCIName(),
|
|
628
|
+
is_wsl: detectWSL(),
|
|
629
|
+
is_tty: Boolean(process.stdout?.isTTY)
|
|
630
|
+
};
|
|
631
|
+
return cached;
|
|
632
|
+
}
|
|
633
|
+
function detectDocker() {
|
|
634
|
+
try {
|
|
635
|
+
if (existsSync2("/.dockerenv")) return true;
|
|
636
|
+
if (platform() === "linux") {
|
|
637
|
+
const cgroup = readFileSync2("/proc/1/cgroup", "utf-8");
|
|
638
|
+
if (cgroup.includes("docker") || cgroup.includes("containerd")) return true;
|
|
639
|
+
}
|
|
640
|
+
} catch {
|
|
641
|
+
}
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
function detectCI() {
|
|
645
|
+
return process.env["CI"] === "true" || process.env["CI"] === "1" || process.env["CONTINUOUS_INTEGRATION"] === "true" || process.env["GITHUB_ACTIONS"] === "true" || process.env["GITLAB_CI"] === "true" || process.env["CIRCLECI"] === "true" || process.env["JENKINS_URL"] != null || process.env["BUILDKITE"] === "true" || process.env["TRAVIS"] === "true" || false;
|
|
646
|
+
}
|
|
647
|
+
function getCIName() {
|
|
648
|
+
if (process.env["GITHUB_ACTIONS"] === "true") return "github_actions";
|
|
649
|
+
if (process.env["GITLAB_CI"] === "true") return "gitlab_ci";
|
|
650
|
+
if (process.env["CIRCLECI"] === "true") return "circleci";
|
|
651
|
+
if (process.env["JENKINS_URL"] != null) return "jenkins";
|
|
652
|
+
if (process.env["BUILDKITE"] === "true") return "buildkite";
|
|
653
|
+
if (process.env["TRAVIS"] === "true") return "travis";
|
|
654
|
+
if (detectCI()) return "unknown";
|
|
655
|
+
return null;
|
|
656
|
+
}
|
|
657
|
+
function detectWSL() {
|
|
658
|
+
if (platform() !== "linux") return false;
|
|
659
|
+
try {
|
|
660
|
+
const osRelease = release().toLowerCase();
|
|
661
|
+
if (osRelease.includes("microsoft") || osRelease.includes("wsl")) return true;
|
|
662
|
+
const procVersion = readFileSync2("/proc/version", "utf-8").toLowerCase();
|
|
663
|
+
return procVersion.includes("microsoft") || procVersion.includes("wsl");
|
|
664
|
+
} catch {
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function getShmSizeMb() {
|
|
669
|
+
if (platform() !== "linux") return null;
|
|
670
|
+
try {
|
|
671
|
+
const stats = statfsSync("/dev/shm");
|
|
672
|
+
return bytesToMb(stats.bsize * stats.blocks);
|
|
673
|
+
} catch {
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
function getFreeDiskMb(path = ".") {
|
|
678
|
+
try {
|
|
679
|
+
const stats = statfsSync(path);
|
|
680
|
+
return bytesToMb(stats.bsize * stats.bavail);
|
|
681
|
+
} catch {
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
var cached;
|
|
686
|
+
var init_system = __esm({
|
|
687
|
+
"src/telemetry/system.ts"() {
|
|
688
|
+
"use strict";
|
|
689
|
+
cached = null;
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
|
|
602
693
|
// src/telemetry/client.ts
|
|
603
694
|
function shouldTrack() {
|
|
604
695
|
if (telemetryEnabled !== null) return telemetryEnabled;
|
|
@@ -624,6 +715,7 @@ function shouldTrack() {
|
|
|
624
715
|
}
|
|
625
716
|
function trackEvent(event, properties = {}) {
|
|
626
717
|
if (!shouldTrack()) return;
|
|
718
|
+
const sys = getSystemMeta();
|
|
627
719
|
eventQueue.push({
|
|
628
720
|
event,
|
|
629
721
|
properties: {
|
|
@@ -631,7 +723,17 @@ function trackEvent(event, properties = {}) {
|
|
|
631
723
|
cli_version: VERSION,
|
|
632
724
|
os: process.platform,
|
|
633
725
|
arch: process.arch,
|
|
634
|
-
node_version: process.version
|
|
726
|
+
node_version: process.version,
|
|
727
|
+
os_release: sys.os_release,
|
|
728
|
+
cpu_count: sys.cpu_count,
|
|
729
|
+
cpu_model: sys.cpu_model ?? void 0,
|
|
730
|
+
cpu_speed: sys.cpu_speed ?? void 0,
|
|
731
|
+
memory_total_mb: sys.memory_total_mb,
|
|
732
|
+
is_docker: sys.is_docker,
|
|
733
|
+
is_ci: sys.is_ci,
|
|
734
|
+
ci_name: sys.ci_name ?? void 0,
|
|
735
|
+
is_wsl: sys.is_wsl,
|
|
736
|
+
is_tty: sys.is_tty
|
|
635
737
|
},
|
|
636
738
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
637
739
|
});
|
|
@@ -655,7 +757,7 @@ async function flush() {
|
|
|
655
757
|
try {
|
|
656
758
|
await fetch(`${POSTHOG_HOST}/batch/`, {
|
|
657
759
|
method: "POST",
|
|
658
|
-
headers: { "Content-Type": "application/json" },
|
|
760
|
+
headers: { "Content-Type": "application/json", Connection: "close" },
|
|
659
761
|
body: JSON.stringify({ api_key: POSTHOG_API_KEY, batch }),
|
|
660
762
|
signal: controller.signal
|
|
661
763
|
});
|
|
@@ -713,6 +815,7 @@ var init_client = __esm({
|
|
|
713
815
|
init_version();
|
|
714
816
|
init_colors();
|
|
715
817
|
init_env();
|
|
818
|
+
init_system();
|
|
716
819
|
POSTHOG_API_KEY = "phc_zjjbX0PnWxERXrMHhkEJWj9A9BhGVLRReICgsfTMmpx";
|
|
717
820
|
POSTHOG_HOST = "https://us.i.posthog.com";
|
|
718
821
|
FLUSH_TIMEOUT_MS = 5e3;
|
|
@@ -722,6 +825,14 @@ var init_client = __esm({
|
|
|
722
825
|
});
|
|
723
826
|
|
|
724
827
|
// src/telemetry/events.ts
|
|
828
|
+
var events_exports = {};
|
|
829
|
+
__export(events_exports, {
|
|
830
|
+
trackBrowserInstall: () => trackBrowserInstall,
|
|
831
|
+
trackCommand: () => trackCommand,
|
|
832
|
+
trackInitTemplate: () => trackInitTemplate,
|
|
833
|
+
trackRenderComplete: () => trackRenderComplete,
|
|
834
|
+
trackRenderError: () => trackRenderError
|
|
835
|
+
});
|
|
725
836
|
function trackCommand(command2) {
|
|
726
837
|
trackEvent("cli_command", { command: command2 });
|
|
727
838
|
}
|
|
@@ -732,14 +843,30 @@ function trackRenderComplete(props) {
|
|
|
732
843
|
quality: props.quality,
|
|
733
844
|
workers: props.workers,
|
|
734
845
|
docker: props.docker,
|
|
735
|
-
gpu: props.gpu
|
|
846
|
+
gpu: props.gpu,
|
|
847
|
+
composition_duration_ms: props.compositionDurationMs,
|
|
848
|
+
composition_width: props.compositionWidth,
|
|
849
|
+
composition_height: props.compositionHeight,
|
|
850
|
+
total_frames: props.totalFrames,
|
|
851
|
+
speed_ratio: props.speedRatio,
|
|
852
|
+
capture_avg_ms: props.captureAvgMs,
|
|
853
|
+
capture_peak_ms: props.capturePeakMs,
|
|
854
|
+
peak_memory_mb: props.peakMemoryMb,
|
|
855
|
+
memory_free_mb: props.memoryFreeMb
|
|
736
856
|
});
|
|
737
857
|
}
|
|
738
858
|
function trackRenderError(props) {
|
|
739
859
|
trackEvent("render_error", {
|
|
740
860
|
fps: props.fps,
|
|
741
861
|
quality: props.quality,
|
|
742
|
-
docker: props.docker
|
|
862
|
+
docker: props.docker,
|
|
863
|
+
workers: props.workers,
|
|
864
|
+
gpu: props.gpu,
|
|
865
|
+
failed_stage: props.failedStage,
|
|
866
|
+
error_message: props.errorMessage,
|
|
867
|
+
elapsed_ms: props.elapsedMs,
|
|
868
|
+
peak_memory_mb: props.peakMemoryMb,
|
|
869
|
+
memory_free_mb: props.memoryFreeMb
|
|
743
870
|
});
|
|
744
871
|
}
|
|
745
872
|
function trackInitTemplate(templateId) {
|
|
@@ -772,7 +899,10 @@ async function checkForUpdate(force) {
|
|
|
772
899
|
try {
|
|
773
900
|
const controller = new AbortController();
|
|
774
901
|
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
775
|
-
const res = await fetch(NPM_REGISTRY_URL, {
|
|
902
|
+
const res = await fetch(NPM_REGISTRY_URL, {
|
|
903
|
+
signal: controller.signal,
|
|
904
|
+
headers: { Connection: "close" }
|
|
905
|
+
});
|
|
776
906
|
clearTimeout(timeout);
|
|
777
907
|
if (!res.ok) return fallbackResult(config.latestVersion);
|
|
778
908
|
const data = await res.json();
|
|
@@ -2536,7 +2666,15 @@ var init_generators = __esm({
|
|
|
2536
2666
|
{ id: "warm-grain", label: "Warm Grain", hint: "Cream aesthetic with grain texture" },
|
|
2537
2667
|
{ id: "play-mode", label: "Play Mode", hint: "Playful elastic animations" },
|
|
2538
2668
|
{ id: "swiss-grid", label: "Swiss Grid", hint: "Structured grid layout" },
|
|
2539
|
-
{ id: "vignelli", label: "Vignelli", hint: "Bold typography with red accents" }
|
|
2669
|
+
{ id: "vignelli", label: "Vignelli", hint: "Bold typography with red accents" },
|
|
2670
|
+
{ id: "decision-tree", label: "Decision Tree", hint: "Animated flowchart with branching paths" },
|
|
2671
|
+
{ id: "kinetic-type", label: "Kinetic Type", hint: "Bold kinetic typography promo" },
|
|
2672
|
+
{
|
|
2673
|
+
id: "product-promo",
|
|
2674
|
+
label: "Product Promo",
|
|
2675
|
+
hint: "Multi-scene product showcase with SVG assets"
|
|
2676
|
+
},
|
|
2677
|
+
{ id: "nyt-graph", label: "NYT Graph", hint: "Animated data chart in print editorial style" }
|
|
2540
2678
|
];
|
|
2541
2679
|
}
|
|
2542
2680
|
});
|
|
@@ -2553,8 +2691,8 @@ __export(manager_exports, {
|
|
|
2553
2691
|
hasFFmpeg: () => hasFFmpeg
|
|
2554
2692
|
});
|
|
2555
2693
|
import { execFileSync } from "child_process";
|
|
2556
|
-
import { existsSync as
|
|
2557
|
-
import { homedir as homedir2, platform } from "os";
|
|
2694
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, createWriteStream, rmSync } from "fs";
|
|
2695
|
+
import { homedir as homedir2, platform as platform2 } from "os";
|
|
2558
2696
|
import { join as join2 } from "path";
|
|
2559
2697
|
import { get as httpsGet } from "https";
|
|
2560
2698
|
import { pipeline } from "stream/promises";
|
|
@@ -2597,7 +2735,7 @@ function whichBinary(name) {
|
|
|
2597
2735
|
}
|
|
2598
2736
|
function findFromEnv() {
|
|
2599
2737
|
const envPath = process.env["HYPERFRAMES_WHISPER_PATH"];
|
|
2600
|
-
if (envPath &&
|
|
2738
|
+
if (envPath && existsSync3(envPath)) {
|
|
2601
2739
|
return { executablePath: envPath, source: "env" };
|
|
2602
2740
|
}
|
|
2603
2741
|
return void 0;
|
|
@@ -2607,9 +2745,9 @@ function findFromSystem() {
|
|
|
2607
2745
|
const path = whichBinary(name);
|
|
2608
2746
|
if (path) return { executablePath: path, source: "system" };
|
|
2609
2747
|
}
|
|
2610
|
-
if (
|
|
2748
|
+
if (platform2() === "darwin") {
|
|
2611
2749
|
for (const p of ["/opt/homebrew/bin/whisper-cli", "/usr/local/bin/whisper-cli"]) {
|
|
2612
|
-
if (
|
|
2750
|
+
if (existsSync3(p)) return { executablePath: p, source: "system" };
|
|
2613
2751
|
}
|
|
2614
2752
|
}
|
|
2615
2753
|
return void 0;
|
|
@@ -2619,15 +2757,15 @@ function findBuiltBinary() {
|
|
|
2619
2757
|
join2(BUILD_DIR, "build", "bin", "whisper-cli"),
|
|
2620
2758
|
join2(BUILD_DIR, "build", "whisper-cli")
|
|
2621
2759
|
]) {
|
|
2622
|
-
if (
|
|
2760
|
+
if (existsSync3(p)) return { executablePath: p, source: "build" };
|
|
2623
2761
|
}
|
|
2624
2762
|
return void 0;
|
|
2625
2763
|
}
|
|
2626
2764
|
function buildFromSource(onProgress) {
|
|
2627
|
-
if (
|
|
2765
|
+
if (existsSync3(BUILD_DIR) && !findBuiltBinary()) {
|
|
2628
2766
|
rmSync(BUILD_DIR, { recursive: true, force: true });
|
|
2629
2767
|
}
|
|
2630
|
-
if (!
|
|
2768
|
+
if (!existsSync3(BUILD_DIR)) {
|
|
2631
2769
|
onProgress?.("Downloading whisper.cpp...");
|
|
2632
2770
|
mkdirSync2(join2(homedir2(), ".cache", "hyperframes", "whisper"), {
|
|
2633
2771
|
recursive: true
|
|
@@ -2670,7 +2808,7 @@ function findWhisper() {
|
|
|
2670
2808
|
return findFromEnv() ?? findFromSystem() ?? findBuiltBinary();
|
|
2671
2809
|
}
|
|
2672
2810
|
function getInstallInstructions() {
|
|
2673
|
-
if (
|
|
2811
|
+
if (platform2() === "darwin") {
|
|
2674
2812
|
return "brew install whisper-cpp";
|
|
2675
2813
|
}
|
|
2676
2814
|
return "See https://github.com/ggml-org/whisper.cpp#building";
|
|
@@ -2687,7 +2825,7 @@ function hasCmake() {
|
|
|
2687
2825
|
async function ensureWhisper(options) {
|
|
2688
2826
|
const existing = findWhisper();
|
|
2689
2827
|
if (existing) return existing;
|
|
2690
|
-
if (
|
|
2828
|
+
if (platform2() === "darwin" && hasBrew()) {
|
|
2691
2829
|
options?.onProgress?.("Installing whisper-cpp via Homebrew...");
|
|
2692
2830
|
try {
|
|
2693
2831
|
execFileSync("brew", ["install", "whisper-cpp"], {
|
|
@@ -2709,11 +2847,11 @@ async function ensureWhisper(options) {
|
|
|
2709
2847
|
}
|
|
2710
2848
|
async function ensureModel(model = DEFAULT_MODEL, options) {
|
|
2711
2849
|
const modelPath = join2(MODELS_DIR, `ggml-${model}.bin`);
|
|
2712
|
-
if (
|
|
2850
|
+
if (existsSync3(modelPath)) return modelPath;
|
|
2713
2851
|
mkdirSync2(MODELS_DIR, { recursive: true });
|
|
2714
2852
|
options?.onProgress?.(`Downloading model ${model}...`);
|
|
2715
2853
|
await downloadFile(getModelUrl(model), modelPath);
|
|
2716
|
-
if (!
|
|
2854
|
+
if (!existsSync3(modelPath)) {
|
|
2717
2855
|
throw new Error(`Model download failed: ${model}`);
|
|
2718
2856
|
}
|
|
2719
2857
|
return modelPath;
|
|
@@ -2744,7 +2882,7 @@ __export(install_skills_exports, {
|
|
|
2744
2882
|
default: () => install_skills_default,
|
|
2745
2883
|
installAllSkills: () => installAllSkills
|
|
2746
2884
|
});
|
|
2747
|
-
import { existsSync as
|
|
2885
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync, rmSync as rmSync2, cpSync } from "fs";
|
|
2748
2886
|
import { join as join3, dirname } from "path";
|
|
2749
2887
|
import { homedir as homedir3 } from "os";
|
|
2750
2888
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -2784,7 +2922,7 @@ function gitClone(repo, dest) {
|
|
|
2784
2922
|
}
|
|
2785
2923
|
function fetchRepo(source) {
|
|
2786
2924
|
const gitUrl = `https://github.com/${source.repo}.git`;
|
|
2787
|
-
if (
|
|
2925
|
+
if (existsSync4(source.cache)) {
|
|
2788
2926
|
try {
|
|
2789
2927
|
execFileSync2("git", ["pull", "--ff-only"], {
|
|
2790
2928
|
cwd: source.cache,
|
|
@@ -2794,7 +2932,7 @@ function fetchRepo(source) {
|
|
|
2794
2932
|
});
|
|
2795
2933
|
} catch {
|
|
2796
2934
|
const skillsDir2 = join3(source.cache, source.skillsPath);
|
|
2797
|
-
if (
|
|
2935
|
+
if (existsSync4(skillsDir2)) return skillsDir2;
|
|
2798
2936
|
rmSync2(source.cache, { recursive: true, force: true });
|
|
2799
2937
|
gitClone(gitUrl, source.cache);
|
|
2800
2938
|
}
|
|
@@ -2803,18 +2941,18 @@ function fetchRepo(source) {
|
|
|
2803
2941
|
gitClone(gitUrl, source.cache);
|
|
2804
2942
|
}
|
|
2805
2943
|
const skillsDir = join3(source.cache, source.skillsPath);
|
|
2806
|
-
return
|
|
2944
|
+
return existsSync4(skillsDir) ? skillsDir : void 0;
|
|
2807
2945
|
}
|
|
2808
2946
|
function installSkillsFromDir(sourceDir, targetDir, sourceName) {
|
|
2809
2947
|
const installed = [];
|
|
2810
|
-
if (!
|
|
2948
|
+
if (!existsSync4(sourceDir)) return installed;
|
|
2811
2949
|
const entries2 = readdirSync(sourceDir, { withFileTypes: true });
|
|
2812
2950
|
for (const entry of entries2) {
|
|
2813
2951
|
if (!entry.isDirectory()) continue;
|
|
2814
2952
|
const skillFile = join3(sourceDir, entry.name, "SKILL.md");
|
|
2815
|
-
if (!
|
|
2953
|
+
if (!existsSync4(skillFile)) continue;
|
|
2816
2954
|
const destDir = join3(targetDir, entry.name);
|
|
2817
|
-
if (
|
|
2955
|
+
if (existsSync4(destDir)) rmSync2(destDir, { recursive: true, force: true });
|
|
2818
2956
|
mkdirSync3(destDir, { recursive: true });
|
|
2819
2957
|
cpSync(join3(sourceDir, entry.name), destDir, { recursive: true });
|
|
2820
2958
|
installed.push({ name: entry.name, source: sourceName });
|
|
@@ -3844,6 +3982,76 @@ ${right.raw}`)
|
|
|
3844
3982
|
});
|
|
3845
3983
|
}
|
|
3846
3984
|
}
|
|
3985
|
+
for (const script of scripts) {
|
|
3986
|
+
const templateLiteralSelectorPattern = /(?:querySelector|querySelectorAll)\s*\(\s*`[^`]*\$\{[^}]+\}[^`]*`\s*\)/g;
|
|
3987
|
+
let tlMatch;
|
|
3988
|
+
while ((tlMatch = templateLiteralSelectorPattern.exec(script.content)) !== null) {
|
|
3989
|
+
pushFinding({
|
|
3990
|
+
code: "template_literal_selector",
|
|
3991
|
+
severity: "error",
|
|
3992
|
+
message: "querySelector uses a template literal variable (e.g. `${compId}`). The HTML bundler's CSS parser crashes on these. Use a hardcoded string instead.",
|
|
3993
|
+
file: filePath,
|
|
3994
|
+
fixHint: "Replace the template literal variable with a hardcoded string. The bundler's CSS parser cannot handle interpolated variables in script content.",
|
|
3995
|
+
snippet: truncateSnippet(tlMatch[0])
|
|
3996
|
+
});
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
{
|
|
4000
|
+
const cssTranslateSelectors = /* @__PURE__ */ new Map();
|
|
4001
|
+
const cssScaleSelectors = /* @__PURE__ */ new Map();
|
|
4002
|
+
for (const style of styles) {
|
|
4003
|
+
for (const [, selector, body] of style.content.matchAll(
|
|
4004
|
+
/([#.][a-zA-Z0-9_-]+)\s*\{([^}]+)\}/g
|
|
4005
|
+
)) {
|
|
4006
|
+
const tMatch = body?.match(/transform\s*:\s*([^;]+)/);
|
|
4007
|
+
if (!tMatch || !tMatch[1]) continue;
|
|
4008
|
+
const transformVal = tMatch[1].trim();
|
|
4009
|
+
if (/translate/i.test(transformVal)) {
|
|
4010
|
+
cssTranslateSelectors.set((selector ?? "").trim(), transformVal);
|
|
4011
|
+
}
|
|
4012
|
+
if (/scale/i.test(transformVal)) {
|
|
4013
|
+
cssScaleSelectors.set((selector ?? "").trim(), transformVal);
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
if (cssTranslateSelectors.size > 0 || cssScaleSelectors.size > 0) {
|
|
4018
|
+
for (const script of scripts) {
|
|
4019
|
+
if (!/gsap\.timeline/.test(script.content)) continue;
|
|
4020
|
+
const windows = extractGsapWindows(script.content);
|
|
4021
|
+
const conflicts = /* @__PURE__ */ new Map();
|
|
4022
|
+
for (const win of windows) {
|
|
4023
|
+
if (win.method === "fromTo") continue;
|
|
4024
|
+
const sel = win.targetSelector;
|
|
4025
|
+
const cssKey = sel.startsWith("#") || sel.startsWith(".") ? sel : `#${sel}`;
|
|
4026
|
+
const translateProps = win.properties.filter(
|
|
4027
|
+
(p) => ["x", "y", "xPercent", "yPercent"].includes(p)
|
|
4028
|
+
);
|
|
4029
|
+
const scaleProps = win.properties.filter((p) => p === "scale");
|
|
4030
|
+
const cssFromTranslate = translateProps.length > 0 ? cssTranslateSelectors.get(cssKey) : void 0;
|
|
4031
|
+
const cssFromScale = scaleProps.length > 0 ? cssScaleSelectors.get(cssKey) : void 0;
|
|
4032
|
+
if (!cssFromTranslate && !cssFromScale) continue;
|
|
4033
|
+
const existing = conflicts.get(sel) ?? {
|
|
4034
|
+
cssTransform: [cssFromTranslate, cssFromScale].filter(Boolean).join(" "),
|
|
4035
|
+
props: /* @__PURE__ */ new Set(),
|
|
4036
|
+
raw: win.raw
|
|
4037
|
+
};
|
|
4038
|
+
for (const p of [...translateProps, ...scaleProps]) existing.props.add(p);
|
|
4039
|
+
conflicts.set(sel, existing);
|
|
4040
|
+
}
|
|
4041
|
+
for (const [sel, { cssTransform, props, raw }] of conflicts) {
|
|
4042
|
+
const propList = [...props].join("/");
|
|
4043
|
+
pushFinding({
|
|
4044
|
+
code: "gsap_css_transform_conflict",
|
|
4045
|
+
severity: "warning",
|
|
4046
|
+
message: `"${sel}" has CSS \`transform: ${cssTransform}\` and a GSAP tween animates ${propList}. GSAP will overwrite the full CSS transform, discarding any translateX(-50%) centering or CSS scale value.`,
|
|
4047
|
+
selector: sel,
|
|
4048
|
+
fixHint: `Remove the transform from CSS and use tl.fromTo('${sel}', { xPercent: -50, x: -1000 }, { xPercent: -50, x: 0 }) so GSAP owns the full transform state. tl.fromTo is exempt from this rule.`,
|
|
4049
|
+
snippet: truncateSnippet(raw)
|
|
4050
|
+
});
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
3847
4055
|
const errorCount = findings.filter((finding) => finding.severity === "error").length;
|
|
3848
4056
|
const warningCount = findings.length - errorCount;
|
|
3849
4057
|
return {
|
|
@@ -3987,6 +4195,7 @@ function extractGsapWindows(script) {
|
|
|
3987
4195
|
end: animation.position + meta.effectiveDuration,
|
|
3988
4196
|
properties: meta.properties.length > 0 ? meta.properties : Object.keys(animation.properties),
|
|
3989
4197
|
overwriteAuto: meta.overwriteAuto,
|
|
4198
|
+
method: match[1] ?? "to",
|
|
3990
4199
|
raw
|
|
3991
4200
|
});
|
|
3992
4201
|
}
|
|
@@ -4172,7 +4381,7 @@ var init_staticGuard = __esm({
|
|
|
4172
4381
|
});
|
|
4173
4382
|
|
|
4174
4383
|
// ../core/src/compiler/htmlBundler.ts
|
|
4175
|
-
import { readFileSync as
|
|
4384
|
+
import { readFileSync as readFileSync3, existsSync as existsSync5 } from "fs";
|
|
4176
4385
|
import { join as join4, resolve as resolve2, isAbsolute, sep } from "path";
|
|
4177
4386
|
import * as cheerio from "cheerio";
|
|
4178
4387
|
import { transformSync } from "esbuild";
|
|
@@ -4235,17 +4444,17 @@ function isRelativeUrl(url) {
|
|
|
4235
4444
|
return !url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("//") && !url.startsWith("data:") && !isAbsolute(url);
|
|
4236
4445
|
}
|
|
4237
4446
|
function safeReadFile(filePath) {
|
|
4238
|
-
if (!
|
|
4447
|
+
if (!existsSync5(filePath)) return null;
|
|
4239
4448
|
try {
|
|
4240
|
-
return
|
|
4449
|
+
return readFileSync3(filePath, "utf-8");
|
|
4241
4450
|
} catch {
|
|
4242
4451
|
return null;
|
|
4243
4452
|
}
|
|
4244
4453
|
}
|
|
4245
4454
|
function safeReadFileBuffer(filePath) {
|
|
4246
|
-
if (!
|
|
4455
|
+
if (!existsSync5(filePath)) return null;
|
|
4247
4456
|
try {
|
|
4248
|
-
return
|
|
4457
|
+
return readFileSync3(filePath);
|
|
4249
4458
|
} catch {
|
|
4250
4459
|
return null;
|
|
4251
4460
|
}
|
|
@@ -4433,8 +4642,8 @@ function stripJsCommentsParserSafe(source) {
|
|
|
4433
4642
|
}
|
|
4434
4643
|
async function bundleToSingleHtml(projectDir, options) {
|
|
4435
4644
|
const indexPath = join4(projectDir, "index.html");
|
|
4436
|
-
if (!
|
|
4437
|
-
const rawHtml =
|
|
4645
|
+
if (!existsSync5(indexPath)) throw new Error("index.html not found in project directory");
|
|
4646
|
+
const rawHtml = readFileSync3(indexPath, "utf-8");
|
|
4438
4647
|
const compiled = await compileHtml(rawHtml, projectDir, options?.probeMediaDuration);
|
|
4439
4648
|
const staticGuard = validateHyperframeHtmlContract(compiled);
|
|
4440
4649
|
if (!staticGuard.isValid) {
|
|
@@ -17298,7 +17507,7 @@ var init_config2 = __esm({
|
|
|
17298
17507
|
});
|
|
17299
17508
|
|
|
17300
17509
|
// ../engine/src/services/browserManager.ts
|
|
17301
|
-
import { existsSync as
|
|
17510
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2 } from "fs";
|
|
17302
17511
|
import { join as join5 } from "path";
|
|
17303
17512
|
import { homedir as homedir4 } from "os";
|
|
17304
17513
|
async function getPuppeteer() {
|
|
@@ -17321,7 +17530,7 @@ function resolveHeadlessShellPath(config) {
|
|
|
17321
17530
|
return process.env.PRODUCER_HEADLESS_SHELL_PATH;
|
|
17322
17531
|
}
|
|
17323
17532
|
const baseDir = join5(homedir4(), ".cache", "puppeteer", "chrome-headless-shell");
|
|
17324
|
-
if (!
|
|
17533
|
+
if (!existsSync6(baseDir)) return void 0;
|
|
17325
17534
|
try {
|
|
17326
17535
|
const versions = readdirSync2(baseDir).sort().reverse();
|
|
17327
17536
|
for (const version of versions) {
|
|
@@ -17332,7 +17541,7 @@ function resolveHeadlessShellPath(config) {
|
|
|
17332
17541
|
join5(baseDir, version, "chrome-headless-shell-win64", "chrome-headless-shell.exe")
|
|
17333
17542
|
];
|
|
17334
17543
|
for (const binary of candidates) {
|
|
17335
|
-
if (
|
|
17544
|
+
if (existsSync6(binary)) return binary;
|
|
17336
17545
|
}
|
|
17337
17546
|
}
|
|
17338
17547
|
} catch {
|
|
@@ -18025,9 +18234,9 @@ async function beginFrameCapture(page, options, frameTimeTicks, interval) {
|
|
|
18025
18234
|
buffer = Buffer.from(result.screenshotData, "base64");
|
|
18026
18235
|
lastFrameCache.set(page, buffer);
|
|
18027
18236
|
} else {
|
|
18028
|
-
const
|
|
18029
|
-
if (
|
|
18030
|
-
buffer =
|
|
18237
|
+
const cached2 = lastFrameCache.get(page);
|
|
18238
|
+
if (cached2) {
|
|
18239
|
+
buffer = cached2;
|
|
18031
18240
|
} else {
|
|
18032
18241
|
const retry = await client.send("HeadlessExperimental.beginFrame", {
|
|
18033
18242
|
frameTimeTicks: frameTimeTicks + 1e-3,
|
|
@@ -18163,10 +18372,10 @@ var init_screenshotService = __esm({
|
|
|
18163
18372
|
});
|
|
18164
18373
|
|
|
18165
18374
|
// ../engine/src/services/frameCapture.ts
|
|
18166
|
-
import { existsSync as
|
|
18375
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
18167
18376
|
import { join as join6 } from "path";
|
|
18168
18377
|
async function createCaptureSession(serverUrl, outputDir, options, onBeforeCapture = null, config) {
|
|
18169
|
-
if (!
|
|
18378
|
+
if (!existsSync7(outputDir)) mkdirSync4(outputDir, { recursive: true });
|
|
18170
18379
|
const headlessShell = resolveHeadlessShellPath(config);
|
|
18171
18380
|
const isLinux = process.platform === "linux";
|
|
18172
18381
|
const forceScreenshot = config?.forceScreenshot ?? DEFAULT_CONFIG2.forceScreenshot;
|
|
@@ -18326,7 +18535,7 @@ async function initializeSession(session) {
|
|
|
18326
18535
|
async function captureFrameErrorDiagnostics(session, frameIndex, time, error) {
|
|
18327
18536
|
try {
|
|
18328
18537
|
const diagnosticsDir = join6(session.outputDir, "diagnostics");
|
|
18329
|
-
if (!
|
|
18538
|
+
if (!existsSync7(diagnosticsDir)) mkdirSync4(diagnosticsDir, { recursive: true });
|
|
18330
18539
|
const base = join6(diagnosticsDir, `frame-error-${frameIndex}`);
|
|
18331
18540
|
await session.page.screenshot({ path: `${base}.png`, type: "png", fullPage: true });
|
|
18332
18541
|
const html = await session.page.content();
|
|
@@ -18440,7 +18649,7 @@ async function closeCaptureSession(session) {
|
|
|
18440
18649
|
session.isInitialized = false;
|
|
18441
18650
|
}
|
|
18442
18651
|
function prepareCaptureSessionForReuse(session, outputDir, onBeforeCapture) {
|
|
18443
|
-
if (!
|
|
18652
|
+
if (!existsSync7(outputDir)) {
|
|
18444
18653
|
mkdirSync4(outputDir, { recursive: true });
|
|
18445
18654
|
}
|
|
18446
18655
|
session.outputDir = outputDir;
|
|
@@ -18595,7 +18804,7 @@ var init_runFfmpeg = __esm({
|
|
|
18595
18804
|
|
|
18596
18805
|
// ../engine/src/services/chunkEncoder.ts
|
|
18597
18806
|
import { spawn as spawn3 } from "child_process";
|
|
18598
|
-
import { copyFileSync, existsSync as
|
|
18807
|
+
import { copyFileSync, existsSync as existsSync8, mkdirSync as mkdirSync5, readdirSync as readdirSync3, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
18599
18808
|
import { join as join7, dirname as dirname3 } from "path";
|
|
18600
18809
|
function getEncoderPreset(quality, format = "mp4") {
|
|
18601
18810
|
const base = ENCODER_PRESETS[quality];
|
|
@@ -18678,7 +18887,7 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
|
|
|
18678
18887
|
async function encodeFramesFromDir(framesDir, framePattern, outputPath, options, signal, config) {
|
|
18679
18888
|
const startTime = Date.now();
|
|
18680
18889
|
const outputDir = dirname3(outputPath);
|
|
18681
|
-
if (!
|
|
18890
|
+
if (!existsSync8(outputDir)) mkdirSync5(outputDir, { recursive: true });
|
|
18682
18891
|
const files = readdirSync3(framesDir).filter((f) => f.match(/\.(jpg|jpeg|png)$/i));
|
|
18683
18892
|
const frameCount = files.length;
|
|
18684
18893
|
if (frameCount === 0) {
|
|
@@ -18744,7 +18953,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
18744
18953
|
});
|
|
18745
18954
|
return;
|
|
18746
18955
|
}
|
|
18747
|
-
const fileSize =
|
|
18956
|
+
const fileSize = existsSync8(outputPath) ? statSync(outputPath).size : 0;
|
|
18748
18957
|
resolve17({ success: true, outputPath, durationMs, framesEncoded: frameCount, fileSize });
|
|
18749
18958
|
});
|
|
18750
18959
|
ffmpeg.on("error", (err) => {
|
|
@@ -18777,7 +18986,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
18777
18986
|
const chunkSize = Math.max(30, Math.floor(chunkSizeFrames));
|
|
18778
18987
|
const chunkCount = Math.ceil(files.length / chunkSize);
|
|
18779
18988
|
const chunkDir = join7(dirname3(outputPath), "chunk-encode");
|
|
18780
|
-
if (!
|
|
18989
|
+
if (!existsSync8(chunkDir)) mkdirSync5(chunkDir, { recursive: true });
|
|
18781
18990
|
const chunkPaths = [];
|
|
18782
18991
|
for (let i = 0; i < chunkCount; i++) {
|
|
18783
18992
|
if (signal?.aborted) {
|
|
@@ -18873,7 +19082,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
18873
19082
|
error: concatResult.error
|
|
18874
19083
|
};
|
|
18875
19084
|
}
|
|
18876
|
-
const fileSize =
|
|
19085
|
+
const fileSize = existsSync8(outputPath) ? statSync(outputPath).size : 0;
|
|
18877
19086
|
return {
|
|
18878
19087
|
success: true,
|
|
18879
19088
|
outputPath,
|
|
@@ -18884,7 +19093,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
18884
19093
|
}
|
|
18885
19094
|
async function muxVideoWithAudio(videoPath, audioPath, outputPath, signal, config) {
|
|
18886
19095
|
const outputDir = dirname3(outputPath);
|
|
18887
|
-
if (!
|
|
19096
|
+
if (!existsSync8(outputDir)) mkdirSync5(outputDir, { recursive: true });
|
|
18888
19097
|
const isWebm = outputPath.endsWith(".webm");
|
|
18889
19098
|
const args = ["-i", videoPath, "-i", audioPath, "-c:v", "copy"];
|
|
18890
19099
|
if (isWebm) {
|
|
@@ -18951,7 +19160,7 @@ var init_chunkEncoder = __esm({
|
|
|
18951
19160
|
|
|
18952
19161
|
// ../engine/src/services/streamingEncoder.ts
|
|
18953
19162
|
import { spawn as spawn4 } from "child_process";
|
|
18954
|
-
import { existsSync as
|
|
19163
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync6, statSync as statSync2 } from "fs";
|
|
18955
19164
|
import { dirname as dirname4 } from "path";
|
|
18956
19165
|
function createFrameReorderBuffer(startFrame, endFrame) {
|
|
18957
19166
|
let nextFrame = startFrame;
|
|
@@ -19060,7 +19269,7 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
|
|
|
19060
19269
|
}
|
|
19061
19270
|
async function spawnStreamingEncoder(outputPath, options, signal, config) {
|
|
19062
19271
|
const outputDir = dirname4(outputPath);
|
|
19063
|
-
if (!
|
|
19272
|
+
if (!existsSync9(outputDir)) mkdirSync6(outputDir, { recursive: true });
|
|
19064
19273
|
let gpuEncoder = null;
|
|
19065
19274
|
if (options.useGpu) {
|
|
19066
19275
|
gpuEncoder = await getCachedGpuEncoder();
|
|
@@ -19140,7 +19349,7 @@ Process error: ${err.message}`;
|
|
|
19140
19349
|
error: `FFmpeg exited with code ${exitCode}`
|
|
19141
19350
|
};
|
|
19142
19351
|
}
|
|
19143
|
-
const fileSize =
|
|
19352
|
+
const fileSize = existsSync9(outputPath) ? statSync2(outputPath).size : 0;
|
|
19144
19353
|
return { success: true, durationMs, fileSize };
|
|
19145
19354
|
},
|
|
19146
19355
|
getExitStatus: () => exitStatus
|
|
@@ -19168,9 +19377,9 @@ function parseFrameRate(frameRateStr) {
|
|
|
19168
19377
|
return parseFloat(frameRateStr) || 0;
|
|
19169
19378
|
}
|
|
19170
19379
|
async function extractVideoMetadata(filePath) {
|
|
19171
|
-
const
|
|
19172
|
-
if (
|
|
19173
|
-
return
|
|
19380
|
+
const cached2 = videoMetadataCache.get(filePath);
|
|
19381
|
+
if (cached2) {
|
|
19382
|
+
return cached2;
|
|
19174
19383
|
}
|
|
19175
19384
|
const probePromise = new Promise((resolve17, reject) => {
|
|
19176
19385
|
const args = [
|
|
@@ -19240,9 +19449,9 @@ async function extractVideoMetadata(filePath) {
|
|
|
19240
19449
|
return probePromise;
|
|
19241
19450
|
}
|
|
19242
19451
|
async function extractAudioMetadata(filePath) {
|
|
19243
|
-
const
|
|
19244
|
-
if (
|
|
19245
|
-
return
|
|
19452
|
+
const cached2 = audioMetadataCache.get(filePath);
|
|
19453
|
+
if (cached2) {
|
|
19454
|
+
return cached2;
|
|
19246
19455
|
}
|
|
19247
19456
|
const probePromise = new Promise((resolve17, reject) => {
|
|
19248
19457
|
const args = [
|
|
@@ -19318,7 +19527,7 @@ var init_ffprobe = __esm({
|
|
|
19318
19527
|
});
|
|
19319
19528
|
|
|
19320
19529
|
// ../engine/src/utils/urlDownloader.ts
|
|
19321
|
-
import { createWriteStream as createWriteStream2, existsSync as
|
|
19530
|
+
import { createWriteStream as createWriteStream2, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "fs";
|
|
19322
19531
|
import { createHash } from "crypto";
|
|
19323
19532
|
import { join as join8, extname } from "path";
|
|
19324
19533
|
import { Readable } from "stream";
|
|
@@ -19331,19 +19540,19 @@ function getFilenameFromUrl(url) {
|
|
|
19331
19540
|
}
|
|
19332
19541
|
async function downloadToTemp(url, destDir, timeoutMs = 3e5) {
|
|
19333
19542
|
const cachedPath = downloadPathCache.get(url);
|
|
19334
|
-
if (cachedPath &&
|
|
19543
|
+
if (cachedPath && existsSync10(cachedPath)) {
|
|
19335
19544
|
return cachedPath;
|
|
19336
19545
|
}
|
|
19337
19546
|
const inFlight = inFlightDownloads.get(url);
|
|
19338
19547
|
if (inFlight) {
|
|
19339
19548
|
return inFlight;
|
|
19340
19549
|
}
|
|
19341
|
-
if (!
|
|
19550
|
+
if (!existsSync10(destDir)) {
|
|
19342
19551
|
mkdirSync7(destDir, { recursive: true });
|
|
19343
19552
|
}
|
|
19344
19553
|
const filename = getFilenameFromUrl(url);
|
|
19345
19554
|
const localPath = join8(destDir, filename);
|
|
19346
|
-
if (
|
|
19555
|
+
if (existsSync10(localPath)) {
|
|
19347
19556
|
downloadPathCache.set(url, localPath);
|
|
19348
19557
|
return localPath;
|
|
19349
19558
|
}
|
|
@@ -19391,7 +19600,7 @@ var init_urlDownloader = __esm({
|
|
|
19391
19600
|
|
|
19392
19601
|
// ../engine/src/services/videoFrameExtractor.ts
|
|
19393
19602
|
import { spawn as spawn6 } from "child_process";
|
|
19394
|
-
import { existsSync as
|
|
19603
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readdirSync as readdirSync4, rmSync as rmSync3 } from "fs";
|
|
19395
19604
|
import { join as join9 } from "path";
|
|
19396
19605
|
function parseVideoElements(html) {
|
|
19397
19606
|
const videos = [];
|
|
@@ -19420,7 +19629,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
19420
19629
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
|
|
19421
19630
|
const { fps, outputDir, quality = 95, format = "jpg" } = options;
|
|
19422
19631
|
const videoOutputDir = join9(outputDir, videoId);
|
|
19423
|
-
if (!
|
|
19632
|
+
if (!existsSync11(videoOutputDir)) mkdirSync8(videoOutputDir, { recursive: true });
|
|
19424
19633
|
const metadata = await extractVideoMetadata(videoPath);
|
|
19425
19634
|
const framePattern = `frame_%05d.${format}`;
|
|
19426
19635
|
const outputPattern = join9(videoOutputDir, framePattern);
|
|
@@ -19515,7 +19724,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config) {
|
|
|
19515
19724
|
mkdirSync8(downloadDir, { recursive: true });
|
|
19516
19725
|
videoPath = await downloadToTemp(videoPath, downloadDir);
|
|
19517
19726
|
}
|
|
19518
|
-
if (!
|
|
19727
|
+
if (!existsSync11(videoPath)) {
|
|
19519
19728
|
return { error: { videoId: video.id, error: `Video file not found: ${videoPath}` } };
|
|
19520
19729
|
}
|
|
19521
19730
|
let videoDuration = video.end - video.start;
|
|
@@ -19669,7 +19878,7 @@ var init_videoFrameExtractor = __esm({
|
|
|
19669
19878
|
}
|
|
19670
19879
|
cleanup() {
|
|
19671
19880
|
for (const video of this.videos.values()) {
|
|
19672
|
-
if (
|
|
19881
|
+
if (existsSync11(video.extracted.outputDir)) {
|
|
19673
19882
|
rmSync3(video.extracted.outputDir, { recursive: true, force: true });
|
|
19674
19883
|
}
|
|
19675
19884
|
}
|
|
@@ -19700,10 +19909,10 @@ function createFrameDataUriCache(cacheLimit) {
|
|
|
19700
19909
|
return dataUri;
|
|
19701
19910
|
}
|
|
19702
19911
|
async function get(framePath) {
|
|
19703
|
-
const
|
|
19704
|
-
if (
|
|
19705
|
-
remember(framePath,
|
|
19706
|
-
return
|
|
19912
|
+
const cached2 = cache.get(framePath);
|
|
19913
|
+
if (cached2) {
|
|
19914
|
+
remember(framePath, cached2);
|
|
19915
|
+
return cached2;
|
|
19707
19916
|
}
|
|
19708
19917
|
const existing = inFlight.get(framePath);
|
|
19709
19918
|
if (existing) {
|
|
@@ -19771,7 +19980,7 @@ var init_videoFrameInjector = __esm({
|
|
|
19771
19980
|
});
|
|
19772
19981
|
|
|
19773
19982
|
// ../engine/src/services/audioMixer.ts
|
|
19774
|
-
import { existsSync as
|
|
19983
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync9, rmSync as rmSync4 } from "fs";
|
|
19775
19984
|
import { join as join10, dirname as dirname5 } from "path";
|
|
19776
19985
|
function parseAudioElements(html) {
|
|
19777
19986
|
const elements = [];
|
|
@@ -19823,7 +20032,7 @@ function parseAudioElements(html) {
|
|
|
19823
20032
|
async function extractAudioFromVideo(videoPath, outputPath, options, signal, config) {
|
|
19824
20033
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
|
|
19825
20034
|
const outputDir = dirname5(outputPath);
|
|
19826
|
-
if (!
|
|
20035
|
+
if (!existsSync12(outputDir)) mkdirSync9(outputDir, { recursive: true });
|
|
19827
20036
|
const args = ["-i", videoPath];
|
|
19828
20037
|
if (options?.startTime !== void 0) args.push("-ss", String(options.startTime));
|
|
19829
20038
|
if (options?.duration !== void 0) args.push("-t", String(options.duration));
|
|
@@ -19850,7 +20059,7 @@ async function extractAudioFromVideo(videoPath, outputPath, options, signal, con
|
|
|
19850
20059
|
async function prepareAudioTrack(srcPath, outputPath, mediaStart, duration, signal, config) {
|
|
19851
20060
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
|
|
19852
20061
|
const outputDir = dirname5(outputPath);
|
|
19853
|
-
if (!
|
|
20062
|
+
if (!existsSync12(outputDir)) mkdirSync9(outputDir, { recursive: true });
|
|
19854
20063
|
const args = [
|
|
19855
20064
|
"-ss",
|
|
19856
20065
|
String(mediaStart),
|
|
@@ -19886,7 +20095,7 @@ async function prepareAudioTrack(srcPath, outputPath, mediaStart, duration, sign
|
|
|
19886
20095
|
async function generateSilence(outputPath, duration, signal, config) {
|
|
19887
20096
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
|
|
19888
20097
|
const outputDir = dirname5(outputPath);
|
|
19889
|
-
if (!
|
|
20098
|
+
if (!existsSync12(outputDir)) mkdirSync9(outputDir, { recursive: true });
|
|
19890
20099
|
const args = [
|
|
19891
20100
|
"-f",
|
|
19892
20101
|
"lavfi",
|
|
@@ -19929,7 +20138,7 @@ async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config)
|
|
|
19929
20138
|
};
|
|
19930
20139
|
}
|
|
19931
20140
|
const outputDir = dirname5(outputPath);
|
|
19932
|
-
if (!
|
|
20141
|
+
if (!existsSync12(outputDir)) mkdirSync9(outputDir, { recursive: true });
|
|
19933
20142
|
const inputs = [];
|
|
19934
20143
|
const filterParts = [];
|
|
19935
20144
|
tracks.forEach((track, i) => {
|
|
@@ -19990,7 +20199,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
19990
20199
|
const startMs = Date.now();
|
|
19991
20200
|
const tracks = [];
|
|
19992
20201
|
const errors = [];
|
|
19993
|
-
if (!
|
|
20202
|
+
if (!existsSync12(workDir)) mkdirSync9(workDir, { recursive: true });
|
|
19994
20203
|
await Promise.all(
|
|
19995
20204
|
elements.map(async (element) => {
|
|
19996
20205
|
if (signal?.aborted) {
|
|
@@ -20012,7 +20221,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
20012
20221
|
return;
|
|
20013
20222
|
}
|
|
20014
20223
|
}
|
|
20015
|
-
if (!
|
|
20224
|
+
if (!existsSync12(srcPath)) {
|
|
20016
20225
|
errors.push(`Source not found: ${element.id}`);
|
|
20017
20226
|
return;
|
|
20018
20227
|
}
|
|
@@ -20092,8 +20301,8 @@ var init_audioMixer = __esm({
|
|
|
20092
20301
|
});
|
|
20093
20302
|
|
|
20094
20303
|
// ../engine/src/services/parallelCoordinator.ts
|
|
20095
|
-
import { cpus, freemem, totalmem } from "os";
|
|
20096
|
-
import { existsSync as
|
|
20304
|
+
import { cpus as cpus2, freemem, totalmem as totalmem2 } from "os";
|
|
20305
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync10, readdirSync as readdirSync5 } from "fs";
|
|
20097
20306
|
import { copyFile, rename } from "fs/promises";
|
|
20098
20307
|
import { join as join11 } from "path";
|
|
20099
20308
|
function calculateOptimalWorkers(totalFrames, requested, config) {
|
|
@@ -20111,9 +20320,9 @@ function calculateOptimalWorkers(totalFrames, requested, config) {
|
|
|
20111
20320
|
return Math.max(MIN_WORKERS, Math.min(effectiveMaxWorkers, requested));
|
|
20112
20321
|
}
|
|
20113
20322
|
if (totalFrames < MIN_FRAMES_PER_WORKER * 2) return 1;
|
|
20114
|
-
const cpuCount =
|
|
20323
|
+
const cpuCount = cpus2().length;
|
|
20115
20324
|
const cpuBasedWorkers = Math.max(1, cpuCount - 2);
|
|
20116
|
-
const totalMemoryMB = Math.round(
|
|
20325
|
+
const totalMemoryMB = Math.round(totalmem2() / (1024 * 1024));
|
|
20117
20326
|
const memoryBasedWorkers = Math.max(1, Math.floor(totalMemoryMB * 0.5 / MEMORY_PER_WORKER_MB));
|
|
20118
20327
|
const frameBasedWorkers = Math.floor(totalFrames / MIN_FRAMES_PER_WORKER);
|
|
20119
20328
|
const optimal = Math.min(cpuBasedWorkers, memoryBasedWorkers, frameBasedWorkers);
|
|
@@ -20146,7 +20355,7 @@ function distributeFrames(totalFrames, workerCount, workDir) {
|
|
|
20146
20355
|
async function executeWorkerTask(task, serverUrl, captureOptions, createBeforeCaptureHook, signal, onFrameCaptured, onFrameBuffer, config) {
|
|
20147
20356
|
const startTime = Date.now();
|
|
20148
20357
|
let framesCaptured = 0;
|
|
20149
|
-
if (!
|
|
20358
|
+
if (!existsSync13(task.outputDir)) mkdirSync10(task.outputDir, { recursive: true });
|
|
20150
20359
|
let session = null;
|
|
20151
20360
|
let perf;
|
|
20152
20361
|
try {
|
|
@@ -20236,11 +20445,11 @@ async function executeParallelCapture(serverUrl, workDir, tasks, captureOptions,
|
|
|
20236
20445
|
return results;
|
|
20237
20446
|
}
|
|
20238
20447
|
async function mergeWorkerFrames(workDir, tasks, outputDir) {
|
|
20239
|
-
if (!
|
|
20448
|
+
if (!existsSync13(outputDir)) mkdirSync10(outputDir, { recursive: true });
|
|
20240
20449
|
let totalFrames = 0;
|
|
20241
20450
|
const sortedTasks = [...tasks].sort((a, b) => a.startFrame - b.startFrame);
|
|
20242
20451
|
for (const task of sortedTasks) {
|
|
20243
|
-
if (!
|
|
20452
|
+
if (!existsSync13(task.outputDir)) {
|
|
20244
20453
|
continue;
|
|
20245
20454
|
}
|
|
20246
20455
|
const files = readdirSync5(task.outputDir).filter((f) => f.startsWith("frame_") && (f.endsWith(".jpg") || f.endsWith(".png"))).sort();
|
|
@@ -20275,7 +20484,7 @@ var init_parallelCoordinator = __esm({
|
|
|
20275
20484
|
// ../engine/src/services/fileServer.ts
|
|
20276
20485
|
import { Hono } from "hono";
|
|
20277
20486
|
import { serve } from "@hono/node-server";
|
|
20278
|
-
import { readFileSync as
|
|
20487
|
+
import { readFileSync as readFileSync4, existsSync as existsSync14, statSync as statSync3 } from "fs";
|
|
20279
20488
|
import { join as join12, extname as extname2 } from "path";
|
|
20280
20489
|
var init_fileServer = __esm({
|
|
20281
20490
|
"../engine/src/services/fileServer.ts"() {
|
|
@@ -20306,7 +20515,7 @@ var init_src2 = __esm({
|
|
|
20306
20515
|
|
|
20307
20516
|
// ../producer/src/services/hyperframeRuntimeLoader.ts
|
|
20308
20517
|
import { createHash as createHash2 } from "crypto";
|
|
20309
|
-
import { existsSync as
|
|
20518
|
+
import { existsSync as existsSync15, readFileSync as readFileSync5 } from "fs";
|
|
20310
20519
|
import { dirname as dirname6, resolve as resolve4 } from "path";
|
|
20311
20520
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
20312
20521
|
function resolveHyperframeManifestPath() {
|
|
@@ -20319,7 +20528,7 @@ function resolveHyperframeManifestPath() {
|
|
|
20319
20528
|
MODULE_RELATIVE_MANIFEST_PATH
|
|
20320
20529
|
];
|
|
20321
20530
|
for (const candidate of candidates) {
|
|
20322
|
-
if (
|
|
20531
|
+
if (existsSync15(candidate)) {
|
|
20323
20532
|
return candidate;
|
|
20324
20533
|
}
|
|
20325
20534
|
}
|
|
@@ -20330,12 +20539,12 @@ function getVerifiedHyperframeRuntimeSource() {
|
|
|
20330
20539
|
}
|
|
20331
20540
|
function resolveVerifiedHyperframeRuntime() {
|
|
20332
20541
|
const manifestPath = resolveHyperframeManifestPath();
|
|
20333
|
-
if (!
|
|
20542
|
+
if (!existsSync15(manifestPath)) {
|
|
20334
20543
|
throw new Error(
|
|
20335
20544
|
`[HyperframeRuntimeLoader] Missing manifest at ${manifestPath}. Build core runtime artifacts before rendering.`
|
|
20336
20545
|
);
|
|
20337
20546
|
}
|
|
20338
|
-
const manifestRaw =
|
|
20547
|
+
const manifestRaw = readFileSync5(manifestPath, "utf8");
|
|
20339
20548
|
const manifest = JSON.parse(manifestRaw);
|
|
20340
20549
|
const runtimeFileName = manifest.artifacts?.iife;
|
|
20341
20550
|
if (!runtimeFileName || !manifest.sha256) {
|
|
@@ -20344,10 +20553,10 @@ function resolveVerifiedHyperframeRuntime() {
|
|
|
20344
20553
|
);
|
|
20345
20554
|
}
|
|
20346
20555
|
const runtimePath = resolve4(dirname6(manifestPath), runtimeFileName);
|
|
20347
|
-
if (!
|
|
20556
|
+
if (!existsSync15(runtimePath)) {
|
|
20348
20557
|
throw new Error(`[HyperframeRuntimeLoader] Missing runtime artifact at ${runtimePath}.`);
|
|
20349
20558
|
}
|
|
20350
|
-
const runtimeSource =
|
|
20559
|
+
const runtimeSource = readFileSync5(runtimePath, "utf8");
|
|
20351
20560
|
const runtimeSha = createHash2("sha256").update(runtimeSource, "utf8").digest("hex");
|
|
20352
20561
|
if (runtimeSha !== manifest.sha256) {
|
|
20353
20562
|
throw new Error(
|
|
@@ -20386,7 +20595,7 @@ var init_hyperframeRuntimeLoader = __esm({
|
|
|
20386
20595
|
// ../producer/src/services/fileServer.ts
|
|
20387
20596
|
import { Hono as Hono2 } from "hono";
|
|
20388
20597
|
import { serve as serve2 } from "@hono/node-server";
|
|
20389
|
-
import { readFileSync as
|
|
20598
|
+
import { readFileSync as readFileSync6, existsSync as existsSync16, statSync as statSync4 } from "fs";
|
|
20390
20599
|
import { join as join13, extname as extname3 } from "path";
|
|
20391
20600
|
function stripEmbeddedRuntimeScripts2(html) {
|
|
20392
20601
|
if (!html) return html;
|
|
@@ -20458,21 +20667,21 @@ function createFileServer2(options) {
|
|
|
20458
20667
|
const relativePath = requestPath.replace(/^\//, "");
|
|
20459
20668
|
const compiledPath = compiledDir ? join13(compiledDir, relativePath) : null;
|
|
20460
20669
|
const hasCompiledFile = Boolean(
|
|
20461
|
-
compiledPath &&
|
|
20670
|
+
compiledPath && existsSync16(compiledPath) && statSync4(compiledPath).isFile()
|
|
20462
20671
|
);
|
|
20463
20672
|
const filePath = hasCompiledFile ? compiledPath : join13(projectDir, relativePath);
|
|
20464
|
-
if (!
|
|
20673
|
+
if (!existsSync16(filePath) || !statSync4(filePath).isFile()) {
|
|
20465
20674
|
return c2.text("Not found", 404);
|
|
20466
20675
|
}
|
|
20467
20676
|
const ext = extname3(filePath).toLowerCase();
|
|
20468
20677
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
20469
20678
|
if (ext === ".html") {
|
|
20470
|
-
const rawHtml =
|
|
20679
|
+
const rawHtml = readFileSync6(filePath, "utf-8");
|
|
20471
20680
|
const isIndex = relativePath === "index.html";
|
|
20472
20681
|
const html = isIndex ? injectScriptsIntoHtml(rawHtml, headScripts, bodyScripts, stripEmbeddedRuntime) : rawHtml;
|
|
20473
20682
|
return c2.text(html, 200, { "Content-Type": contentType });
|
|
20474
20683
|
}
|
|
20475
|
-
const content =
|
|
20684
|
+
const content = readFileSync6(filePath);
|
|
20476
20685
|
return new Response(content, {
|
|
20477
20686
|
status: 200,
|
|
20478
20687
|
headers: { "Content-Type": contentType }
|
|
@@ -20924,7 +21133,7 @@ var init_deterministicFonts = __esm({
|
|
|
20924
21133
|
});
|
|
20925
21134
|
|
|
20926
21135
|
// ../producer/src/services/htmlCompiler.ts
|
|
20927
|
-
import { readFileSync as
|
|
21136
|
+
import { readFileSync as readFileSync7, existsSync as existsSync17, mkdirSync as mkdirSync11 } from "fs";
|
|
20928
21137
|
import { join as join14, dirname as dirname7, resolve as resolve5 } from "path";
|
|
20929
21138
|
function dedupeElementsById(elements) {
|
|
20930
21139
|
const deduped = /* @__PURE__ */ new Map();
|
|
@@ -20936,7 +21145,7 @@ function dedupeElementsById(elements) {
|
|
|
20936
21145
|
async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagName19) {
|
|
20937
21146
|
let filePath = src;
|
|
20938
21147
|
if (isHttpUrl(src)) {
|
|
20939
|
-
if (!
|
|
21148
|
+
if (!existsSync17(downloadDir)) mkdirSync11(downloadDir, { recursive: true });
|
|
20940
21149
|
try {
|
|
20941
21150
|
filePath = await downloadToTemp(src, downloadDir);
|
|
20942
21151
|
} catch {
|
|
@@ -20945,7 +21154,7 @@ async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagNa
|
|
|
20945
21154
|
} else if (!filePath.startsWith("/")) {
|
|
20946
21155
|
filePath = join14(baseDir, filePath);
|
|
20947
21156
|
}
|
|
20948
|
-
if (!
|
|
21157
|
+
if (!existsSync17(filePath)) {
|
|
20949
21158
|
return { duration: 0, resolvedPath: filePath };
|
|
20950
21159
|
}
|
|
20951
21160
|
const metadata = tagName19 === "video" ? await extractVideoMetadata(filePath) : await extractAudioMetadata(filePath);
|
|
@@ -21012,10 +21221,10 @@ async function parseSubCompositions(html, projectDir, downloadDir, parentOffset
|
|
|
21012
21221
|
if (visited.has(filePath)) {
|
|
21013
21222
|
continue;
|
|
21014
21223
|
}
|
|
21015
|
-
if (!
|
|
21224
|
+
if (!existsSync17(filePath)) {
|
|
21016
21225
|
continue;
|
|
21017
21226
|
}
|
|
21018
|
-
const rawSubHtml =
|
|
21227
|
+
const rawSubHtml = readFileSync7(filePath, "utf-8");
|
|
21019
21228
|
const nestedVisited = new Set(visited);
|
|
21020
21229
|
nestedVisited.add(filePath);
|
|
21021
21230
|
workItems.push({ srcPath, absoluteStart, absoluteEnd, filePath, rawSubHtml, nestedVisited });
|
|
@@ -21209,8 +21418,8 @@ function inlineSubCompositions(html, subCompositions, projectDir) {
|
|
|
21209
21418
|
let compHtml = subCompositions.get(srcPath) || null;
|
|
21210
21419
|
if (!compHtml) {
|
|
21211
21420
|
const filePath = resolve5(projectDir, srcPath);
|
|
21212
|
-
if (
|
|
21213
|
-
compHtml =
|
|
21421
|
+
if (existsSync17(filePath)) {
|
|
21422
|
+
compHtml = readFileSync7(filePath, "utf-8");
|
|
21214
21423
|
}
|
|
21215
21424
|
}
|
|
21216
21425
|
if (!compHtml) {
|
|
@@ -21330,7 +21539,7 @@ ${html}
|
|
|
21330
21539
|
</html>`;
|
|
21331
21540
|
}
|
|
21332
21541
|
async function compileForRender(projectDir, htmlPath, downloadDir) {
|
|
21333
|
-
const rawHtml =
|
|
21542
|
+
const rawHtml = readFileSync7(htmlPath, "utf-8");
|
|
21334
21543
|
const { html: compiledHtml, unresolvedCompositions } = await compileHtmlFile(
|
|
21335
21544
|
rawHtml,
|
|
21336
21545
|
projectDir,
|
|
@@ -21518,10 +21727,10 @@ var init_logger = __esm({
|
|
|
21518
21727
|
|
|
21519
21728
|
// ../producer/src/services/renderOrchestrator.ts
|
|
21520
21729
|
import {
|
|
21521
|
-
existsSync as
|
|
21730
|
+
existsSync as existsSync18,
|
|
21522
21731
|
mkdirSync as mkdirSync12,
|
|
21523
21732
|
rmSync as rmSync5,
|
|
21524
|
-
readFileSync as
|
|
21733
|
+
readFileSync as readFileSync8,
|
|
21525
21734
|
writeFileSync as writeFileSync4,
|
|
21526
21735
|
copyFileSync as copyFileSync2,
|
|
21527
21736
|
appendFileSync
|
|
@@ -21683,28 +21892,28 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
21683
21892
|
};
|
|
21684
21893
|
job.startedAt = /* @__PURE__ */ new Date();
|
|
21685
21894
|
assertNotAborted();
|
|
21686
|
-
if (!
|
|
21895
|
+
if (!existsSync18(workDir)) mkdirSync12(workDir, { recursive: true });
|
|
21687
21896
|
if (job.config.debug) {
|
|
21688
21897
|
const logPath = join15(workDir, "render.log");
|
|
21689
21898
|
restoreLogger = installDebugLogger(logPath, log);
|
|
21690
21899
|
}
|
|
21691
21900
|
const entryFile = job.config.entryFile || "index.html";
|
|
21692
21901
|
let htmlPath = join15(projectDir, entryFile);
|
|
21693
|
-
if (!
|
|
21902
|
+
if (!existsSync18(htmlPath)) {
|
|
21694
21903
|
throw new Error(`Entry file not found: ${htmlPath}`);
|
|
21695
21904
|
}
|
|
21696
21905
|
assertNotAborted();
|
|
21697
|
-
const rawEntry =
|
|
21906
|
+
const rawEntry = readFileSync8(htmlPath, "utf-8");
|
|
21698
21907
|
if (entryFile !== "index.html" && rawEntry.trimStart().startsWith("<template")) {
|
|
21699
21908
|
const wrapperPath = join15(workDir, "standalone-entry.html");
|
|
21700
21909
|
const projectIndexPath = join15(projectDir, "index.html");
|
|
21701
|
-
if (!
|
|
21910
|
+
if (!existsSync18(projectIndexPath)) {
|
|
21702
21911
|
throw new Error(
|
|
21703
21912
|
`Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
|
|
21704
21913
|
);
|
|
21705
21914
|
}
|
|
21706
21915
|
const standaloneHtml = extractStandaloneEntryFromIndex(
|
|
21707
|
-
|
|
21916
|
+
readFileSync8(projectIndexPath, "utf-8"),
|
|
21708
21917
|
entryFile
|
|
21709
21918
|
);
|
|
21710
21919
|
if (!standaloneHtml) {
|
|
@@ -21952,7 +22161,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
21952
22161
|
assertNotAborted();
|
|
21953
22162
|
}
|
|
21954
22163
|
const framesDir = join15(workDir, "captured-frames");
|
|
21955
|
-
if (!
|
|
22164
|
+
if (!existsSync18(framesDir)) mkdirSync12(framesDir, { recursive: true });
|
|
21956
22165
|
const captureOptions = {
|
|
21957
22166
|
width,
|
|
21958
22167
|
height,
|
|
@@ -22240,7 +22449,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
22240
22449
|
}
|
|
22241
22450
|
}
|
|
22242
22451
|
if (job.config.debug) {
|
|
22243
|
-
if (
|
|
22452
|
+
if (existsSync18(outputPath)) {
|
|
22244
22453
|
const debugOutput = join15(workDir, isWebm ? "output.webm" : "output.mp4");
|
|
22245
22454
|
copyFileSync2(outputPath, debugOutput);
|
|
22246
22455
|
}
|
|
@@ -22317,7 +22526,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
22317
22526
|
await safeCleanup(
|
|
22318
22527
|
"remove workDir (error)",
|
|
22319
22528
|
() => {
|
|
22320
|
-
if (
|
|
22529
|
+
if (existsSync18(workDir)) rmSync5(workDir, { recursive: true, force: true });
|
|
22321
22530
|
},
|
|
22322
22531
|
log
|
|
22323
22532
|
);
|
|
@@ -22379,7 +22588,7 @@ var init_lint = __esm({
|
|
|
22379
22588
|
});
|
|
22380
22589
|
|
|
22381
22590
|
// ../producer/src/services/hyperframeLint.ts
|
|
22382
|
-
import { existsSync as
|
|
22591
|
+
import { existsSync as existsSync19, readFileSync as readFileSync9, statSync as statSync5 } from "fs";
|
|
22383
22592
|
import { resolve as resolve7, join as join16 } from "path";
|
|
22384
22593
|
function isStringRecord(value) {
|
|
22385
22594
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -22408,7 +22617,7 @@ function pickEntryFile(files, preferredEntryFile) {
|
|
|
22408
22617
|
}
|
|
22409
22618
|
function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
22410
22619
|
const absProjectDir = resolve7(projectDir);
|
|
22411
|
-
if (!
|
|
22620
|
+
if (!existsSync19(absProjectDir) || !statSync5(absProjectDir).isDirectory()) {
|
|
22412
22621
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
22413
22622
|
}
|
|
22414
22623
|
const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
|
|
@@ -22419,10 +22628,10 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
|
22419
22628
|
if (!absoluteEntryPath.startsWith(absProjectDir)) {
|
|
22420
22629
|
return { error: `Entry file must stay inside project directory: ${entryFile}` };
|
|
22421
22630
|
}
|
|
22422
|
-
if (
|
|
22631
|
+
if (existsSync19(absoluteEntryPath) && statSync5(absoluteEntryPath).isFile()) {
|
|
22423
22632
|
return {
|
|
22424
22633
|
entryFile,
|
|
22425
|
-
html:
|
|
22634
|
+
html: readFileSync9(absoluteEntryPath, "utf-8"),
|
|
22426
22635
|
source: "projectDir"
|
|
22427
22636
|
};
|
|
22428
22637
|
}
|
|
@@ -22493,7 +22702,7 @@ var init_paths = __esm({
|
|
|
22493
22702
|
|
|
22494
22703
|
// ../producer/src/server.ts
|
|
22495
22704
|
import {
|
|
22496
|
-
existsSync as
|
|
22705
|
+
existsSync as existsSync20,
|
|
22497
22706
|
mkdirSync as mkdirSync13,
|
|
22498
22707
|
statSync as statSync6,
|
|
22499
22708
|
mkdtempSync,
|
|
@@ -22523,11 +22732,11 @@ async function prepareRenderBody(body) {
|
|
|
22523
22732
|
const projectDir = typeof body.projectDir === "string" ? body.projectDir : void 0;
|
|
22524
22733
|
if (projectDir) {
|
|
22525
22734
|
const absProjectDir = resolve9(projectDir);
|
|
22526
|
-
if (!
|
|
22735
|
+
if (!existsSync20(absProjectDir) || !statSync6(absProjectDir).isDirectory()) {
|
|
22527
22736
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
22528
22737
|
}
|
|
22529
22738
|
const entry = options.entryFile || "index.html";
|
|
22530
|
-
if (!
|
|
22739
|
+
if (!existsSync20(resolve9(absProjectDir, entry))) {
|
|
22531
22740
|
return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
|
|
22532
22741
|
}
|
|
22533
22742
|
return { prepared: { input: { projectDir: absProjectDir, ...options } } };
|
|
@@ -22674,7 +22883,7 @@ function createRenderHandlers(options = {}) {
|
|
|
22674
22883
|
log
|
|
22675
22884
|
);
|
|
22676
22885
|
const outputDir = dirname9(absoluteOutputPath);
|
|
22677
|
-
if (!
|
|
22886
|
+
if (!existsSync20(outputDir)) mkdirSync13(outputDir, { recursive: true });
|
|
22678
22887
|
log.info("render started", {
|
|
22679
22888
|
requestId,
|
|
22680
22889
|
projectDir: input.projectDir,
|
|
@@ -22699,7 +22908,7 @@ function createRenderHandlers(options = {}) {
|
|
|
22699
22908
|
log.info(`render progress ${pct}%`, { requestId, stage: j2.currentStage, message });
|
|
22700
22909
|
}
|
|
22701
22910
|
});
|
|
22702
|
-
const fileSize =
|
|
22911
|
+
const fileSize = existsSync20(absoluteOutputPath) ? statSync6(absoluteOutputPath).size : 0;
|
|
22703
22912
|
const durationMs = Date.now() - t0;
|
|
22704
22913
|
const outputToken = store.register(absoluteOutputPath);
|
|
22705
22914
|
const outputUrl = `${outputUrlPrefix}/${outputToken}`;
|
|
@@ -22782,7 +22991,7 @@ function createRenderHandlers(options = {}) {
|
|
|
22782
22991
|
log
|
|
22783
22992
|
);
|
|
22784
22993
|
const outputDir = dirname9(absoluteOutputPath);
|
|
22785
|
-
if (!
|
|
22994
|
+
if (!existsSync20(outputDir)) mkdirSync13(outputDir, { recursive: true });
|
|
22786
22995
|
log.info("render-stream started", { requestId, projectDir: input.projectDir });
|
|
22787
22996
|
const job = createRenderJob({
|
|
22788
22997
|
fps: input.fps,
|
|
@@ -22816,7 +23025,7 @@ function createRenderHandlers(options = {}) {
|
|
|
22816
23025
|
},
|
|
22817
23026
|
abortController.signal
|
|
22818
23027
|
);
|
|
22819
|
-
const fileSize =
|
|
23028
|
+
const fileSize = existsSync20(absoluteOutputPath) ? statSync6(absoluteOutputPath).size : 0;
|
|
22820
23029
|
const outputToken = store.register(absoluteOutputPath);
|
|
22821
23030
|
const outputUrl = `${outputUrlPrefix}/${outputToken}`;
|
|
22822
23031
|
log.info("render-stream completed", { requestId, fileSize, perf: job.perfSummary ?? null });
|
|
@@ -22874,7 +23083,7 @@ function createRenderHandlers(options = {}) {
|
|
|
22874
23083
|
if (!artifact) {
|
|
22875
23084
|
return c2.json({ success: false, error: "Output artifact not found or expired" }, 404);
|
|
22876
23085
|
}
|
|
22877
|
-
if (!
|
|
23086
|
+
if (!existsSync20(artifact.path)) {
|
|
22878
23087
|
store.delete(token);
|
|
22879
23088
|
return c2.json({ success: false, error: "Output artifact file missing" }, 404);
|
|
22880
23089
|
}
|
|
@@ -23008,7 +23217,7 @@ __export(manager_exports2, {
|
|
|
23008
23217
|
setBrowserPath: () => setBrowserPath
|
|
23009
23218
|
});
|
|
23010
23219
|
import { execSync } from "child_process";
|
|
23011
|
-
import { existsSync as
|
|
23220
|
+
import { existsSync as existsSync21, rmSync as rmSync7 } from "fs";
|
|
23012
23221
|
import { homedir as homedir5 } from "os";
|
|
23013
23222
|
import { join as join19 } from "path";
|
|
23014
23223
|
import { Browser, detectBrowserPlatform, getInstalledBrowsers, install } from "@puppeteer/browsers";
|
|
@@ -23028,17 +23237,17 @@ function whichBinary2(name) {
|
|
|
23028
23237
|
}
|
|
23029
23238
|
}
|
|
23030
23239
|
function findFromEnv2() {
|
|
23031
|
-
if (_browserPathOverride &&
|
|
23240
|
+
if (_browserPathOverride && existsSync21(_browserPathOverride)) {
|
|
23032
23241
|
return { executablePath: _browserPathOverride, source: "env" };
|
|
23033
23242
|
}
|
|
23034
23243
|
const envPath = process.env["HYPERFRAMES_BROWSER_PATH"];
|
|
23035
|
-
if (envPath &&
|
|
23244
|
+
if (envPath && existsSync21(envPath)) {
|
|
23036
23245
|
return { executablePath: envPath, source: "env" };
|
|
23037
23246
|
}
|
|
23038
23247
|
return void 0;
|
|
23039
23248
|
}
|
|
23040
23249
|
async function findFromCache() {
|
|
23041
|
-
if (!
|
|
23250
|
+
if (!existsSync21(CACHE_DIR)) {
|
|
23042
23251
|
return void 0;
|
|
23043
23252
|
}
|
|
23044
23253
|
const installed = await getInstalledBrowsers({ cacheDir: CACHE_DIR });
|
|
@@ -23050,7 +23259,7 @@ async function findFromCache() {
|
|
|
23050
23259
|
}
|
|
23051
23260
|
function findFromSystem2() {
|
|
23052
23261
|
for (const p of SYSTEM_CHROME_PATHS) {
|
|
23053
|
-
if (
|
|
23262
|
+
if (existsSync21(p)) {
|
|
23054
23263
|
return { executablePath: p, source: "system" };
|
|
23055
23264
|
}
|
|
23056
23265
|
}
|
|
@@ -23070,21 +23279,21 @@ async function findBrowser() {
|
|
|
23070
23279
|
async function ensureBrowser(options) {
|
|
23071
23280
|
const existing = await findBrowser();
|
|
23072
23281
|
if (existing) return existing;
|
|
23073
|
-
const
|
|
23074
|
-
if (!
|
|
23282
|
+
const platform4 = detectBrowserPlatform();
|
|
23283
|
+
if (!platform4) {
|
|
23075
23284
|
throw new Error(`Unsupported platform: ${process.platform} ${process.arch}`);
|
|
23076
23285
|
}
|
|
23077
23286
|
const installed = await install({
|
|
23078
23287
|
cacheDir: CACHE_DIR,
|
|
23079
23288
|
browser: Browser.CHROMEHEADLESSSHELL,
|
|
23080
23289
|
buildId: CHROME_VERSION,
|
|
23081
|
-
platform:
|
|
23290
|
+
platform: platform4,
|
|
23082
23291
|
downloadProgressCallback: options?.onProgress
|
|
23083
23292
|
});
|
|
23084
23293
|
return { executablePath: installed.executablePath, source: "download" };
|
|
23085
23294
|
}
|
|
23086
23295
|
function clearBrowser() {
|
|
23087
|
-
if (!
|
|
23296
|
+
if (!existsSync21(CACHE_DIR)) {
|
|
23088
23297
|
return false;
|
|
23089
23298
|
}
|
|
23090
23299
|
rmSync7(CACHE_DIR, { recursive: true, force: true });
|
|
@@ -23112,18 +23321,18 @@ __export(studioServer_exports, {
|
|
|
23112
23321
|
});
|
|
23113
23322
|
import { Hono as Hono4 } from "hono";
|
|
23114
23323
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
23115
|
-
import { existsSync as
|
|
23324
|
+
import { existsSync as existsSync22, readFileSync as readFileSync10, readdirSync as readdirSync6, statSync as statSync7, writeFileSync as writeFileSync6, mkdirSync as mkdirSync14 } from "fs";
|
|
23116
23325
|
import { resolve as resolve10, join as join20, sep as sep2, basename as basename2, dirname as dirname10, extname as extname4 } from "path";
|
|
23117
23326
|
function resolveDistDir() {
|
|
23118
23327
|
const builtPath = resolve10(__dirname, "studio");
|
|
23119
|
-
if (
|
|
23328
|
+
if (existsSync22(resolve10(builtPath, "index.html"))) return builtPath;
|
|
23120
23329
|
const devPath = resolve10(__dirname, "..", "..", "..", "studio", "dist");
|
|
23121
|
-
if (
|
|
23330
|
+
if (existsSync22(resolve10(devPath, "index.html"))) return devPath;
|
|
23122
23331
|
return builtPath;
|
|
23123
23332
|
}
|
|
23124
23333
|
function resolveRuntimePath() {
|
|
23125
23334
|
const builtPath = resolve10(__dirname, "hyperframe-runtime.js");
|
|
23126
|
-
if (
|
|
23335
|
+
if (existsSync22(builtPath)) return builtPath;
|
|
23127
23336
|
const devPath = resolve10(
|
|
23128
23337
|
__dirname,
|
|
23129
23338
|
"..",
|
|
@@ -23133,7 +23342,7 @@ function resolveRuntimePath() {
|
|
|
23133
23342
|
"dist",
|
|
23134
23343
|
"hyperframe.runtime.iife.js"
|
|
23135
23344
|
);
|
|
23136
|
-
if (
|
|
23345
|
+
if (existsSync22(devPath)) return devPath;
|
|
23137
23346
|
return builtPath;
|
|
23138
23347
|
}
|
|
23139
23348
|
function isSafePath(base, resolved) {
|
|
@@ -23157,27 +23366,27 @@ function walkDir(dir, prefix = "") {
|
|
|
23157
23366
|
return files;
|
|
23158
23367
|
}
|
|
23159
23368
|
function serveStaticFile(filePath) {
|
|
23160
|
-
if (!
|
|
23369
|
+
if (!existsSync22(filePath) || !statSync7(filePath).isFile()) return null;
|
|
23161
23370
|
const mime = getMimeType(filePath);
|
|
23162
|
-
const content =
|
|
23371
|
+
const content = readFileSync10(filePath);
|
|
23163
23372
|
return new Response(content, {
|
|
23164
23373
|
headers: { "Content-Type": mime, "Cache-Control": "no-store" }
|
|
23165
23374
|
});
|
|
23166
23375
|
}
|
|
23167
23376
|
function buildSubCompositionHtml(projectDir, compPath, runtimeUrl) {
|
|
23168
23377
|
const compFile = resolve10(projectDir, compPath);
|
|
23169
|
-
if (!isSafePath(projectDir, compFile) || !
|
|
23378
|
+
if (!isSafePath(projectDir, compFile) || !existsSync22(compFile) || !statSync7(compFile).isFile()) {
|
|
23170
23379
|
return null;
|
|
23171
23380
|
}
|
|
23172
|
-
let rawComp =
|
|
23381
|
+
let rawComp = readFileSync10(compFile, "utf-8");
|
|
23173
23382
|
const templateMatch = rawComp.match(/<template>([\s\S]*)<\/template>/i);
|
|
23174
23383
|
let content = (templateMatch ? templateMatch[1] : rawComp) ?? rawComp;
|
|
23175
23384
|
content = content.replace(
|
|
23176
23385
|
/(<[^>]*?)(data-composition-src=["']([^"']+)["'])([^>]*>)/g,
|
|
23177
23386
|
(_match, before2, srcAttr, src, after2) => {
|
|
23178
23387
|
const nestedFile = join20(projectDir, src);
|
|
23179
|
-
if (!
|
|
23180
|
-
const nestedRaw =
|
|
23388
|
+
if (!existsSync22(nestedFile)) return before2 + srcAttr + after2;
|
|
23389
|
+
const nestedRaw = readFileSync10(nestedFile, "utf-8");
|
|
23181
23390
|
const nestedTemplate = nestedRaw.match(/<template>([\s\S]*)<\/template>/i);
|
|
23182
23391
|
const nestedContent = (nestedTemplate ? nestedTemplate[1] : nestedRaw) ?? nestedRaw;
|
|
23183
23392
|
const styles = [];
|
|
@@ -23215,8 +23424,8 @@ function createStudioServer(options) {
|
|
|
23215
23424
|
const watcher = createProjectWatcher(projectDir);
|
|
23216
23425
|
const app = new Hono4();
|
|
23217
23426
|
app.get("/api/runtime.js", (c2) => {
|
|
23218
|
-
if (!
|
|
23219
|
-
return c2.body(
|
|
23427
|
+
if (!existsSync22(runtimePath)) return c2.text("runtime not built", 404);
|
|
23428
|
+
return c2.body(readFileSync10(runtimePath, "utf-8"), 200, {
|
|
23220
23429
|
"Content-Type": "text/javascript",
|
|
23221
23430
|
"Cache-Control": "no-store"
|
|
23222
23431
|
});
|
|
@@ -23251,8 +23460,8 @@ function createStudioServer(options) {
|
|
|
23251
23460
|
bundled = await bundleToSingleHtml2(projectDir);
|
|
23252
23461
|
} catch {
|
|
23253
23462
|
const file = join20(projectDir, "index.html");
|
|
23254
|
-
if (!
|
|
23255
|
-
bundled =
|
|
23463
|
+
if (!existsSync22(file)) return c2.text("not found", 404);
|
|
23464
|
+
bundled = readFileSync10(file, "utf-8");
|
|
23256
23465
|
}
|
|
23257
23466
|
const baseTag = `<base href="/api/projects/${projectId}/preview/">`;
|
|
23258
23467
|
if (bundled.includes("<head>")) {
|
|
@@ -23285,11 +23494,11 @@ function createStudioServer(options) {
|
|
|
23285
23494
|
c2.req.path.replace(`/api/projects/${id}/preview/`, "").split("?")[0] ?? ""
|
|
23286
23495
|
);
|
|
23287
23496
|
const file = resolve10(projectDir, subPath);
|
|
23288
|
-
if (!isSafePath(projectDir, file) || !
|
|
23497
|
+
if (!isSafePath(projectDir, file) || !existsSync22(file) || !statSync7(file).isFile()) {
|
|
23289
23498
|
return c2.text("not found", 404);
|
|
23290
23499
|
}
|
|
23291
23500
|
const mime = getMimeType(file);
|
|
23292
|
-
const content =
|
|
23501
|
+
const content = readFileSync10(file);
|
|
23293
23502
|
return new Response(content, {
|
|
23294
23503
|
headers: { "Content-Type": mime, "Cache-Control": "no-store" }
|
|
23295
23504
|
});
|
|
@@ -23299,10 +23508,10 @@ function createStudioServer(options) {
|
|
|
23299
23508
|
if (id !== projectId) return c2.json({ error: "not found" }, 404);
|
|
23300
23509
|
const filePath = decodeURIComponent(c2.req.path.replace(`/api/projects/${id}/files/`, ""));
|
|
23301
23510
|
const file = resolve10(projectDir, filePath);
|
|
23302
|
-
if (!isSafePath(projectDir, file) || !
|
|
23511
|
+
if (!isSafePath(projectDir, file) || !existsSync22(file)) {
|
|
23303
23512
|
return c2.text("not found", 404);
|
|
23304
23513
|
}
|
|
23305
|
-
const content =
|
|
23514
|
+
const content = readFileSync10(file, "utf-8");
|
|
23306
23515
|
return c2.json({ filename: filePath, content });
|
|
23307
23516
|
});
|
|
23308
23517
|
app.put("/api/projects/:id/files/*", async (c2) => {
|
|
@@ -23314,7 +23523,7 @@ function createStudioServer(options) {
|
|
|
23314
23523
|
return c2.json({ error: "forbidden" }, 403);
|
|
23315
23524
|
}
|
|
23316
23525
|
const dir = dirname10(file);
|
|
23317
|
-
if (!
|
|
23526
|
+
if (!existsSync22(dir)) mkdirSync14(dir, { recursive: true });
|
|
23318
23527
|
const body = await c2.req.text();
|
|
23319
23528
|
writeFileSync6(file, body, "utf-8");
|
|
23320
23529
|
return c2.json({ ok: true });
|
|
@@ -23326,7 +23535,7 @@ function createStudioServer(options) {
|
|
|
23326
23535
|
if (id !== projectId) return c2.json({ error: "not found" }, 404);
|
|
23327
23536
|
const jobId = Math.random().toString(36).slice(2, 10);
|
|
23328
23537
|
const outputDir = join20(projectDir, "renders");
|
|
23329
|
-
if (!
|
|
23538
|
+
if (!existsSync22(outputDir)) mkdirSync14(outputDir, { recursive: true });
|
|
23330
23539
|
const outputPath = join20(outputDir, `${projectId}.mp4`);
|
|
23331
23540
|
renderJobs.set(jobId, { status: "rendering", progress: 0, outputPath });
|
|
23332
23541
|
(async () => {
|
|
@@ -23340,7 +23549,11 @@ function createStudioServer(options) {
|
|
|
23340
23549
|
}
|
|
23341
23550
|
} catch {
|
|
23342
23551
|
}
|
|
23552
|
+
const { trackRenderComplete: trackRenderComplete2 } = await Promise.resolve().then(() => (init_events(), events_exports));
|
|
23553
|
+
const { bytesToMb: bytesToMb2 } = await Promise.resolve().then(() => (init_system(), system_exports));
|
|
23554
|
+
const { freemem: freemem5 } = await import("os");
|
|
23343
23555
|
const job = createRenderJob2({ fps: 30, quality: "standard" });
|
|
23556
|
+
const startTime = Date.now();
|
|
23344
23557
|
const onProgress = (j2) => {
|
|
23345
23558
|
const entry2 = renderJobs.get(jobId);
|
|
23346
23559
|
if (entry2) entry2.progress = j2.progress;
|
|
@@ -23351,7 +23564,41 @@ function createStudioServer(options) {
|
|
|
23351
23564
|
entry.status = "complete";
|
|
23352
23565
|
entry.progress = 100;
|
|
23353
23566
|
}
|
|
23567
|
+
const elapsed = Date.now() - startTime;
|
|
23568
|
+
const perf = job.perfSummary;
|
|
23569
|
+
const compositionDurationMs = perf ? Math.round(perf.compositionDurationSeconds * 1e3) : void 0;
|
|
23570
|
+
trackRenderComplete2({
|
|
23571
|
+
durationMs: elapsed,
|
|
23572
|
+
fps: 30,
|
|
23573
|
+
quality: "standard",
|
|
23574
|
+
workers: perf?.workers ?? 1,
|
|
23575
|
+
docker: false,
|
|
23576
|
+
gpu: false,
|
|
23577
|
+
compositionDurationMs,
|
|
23578
|
+
compositionWidth: perf?.resolution.width,
|
|
23579
|
+
compositionHeight: perf?.resolution.height,
|
|
23580
|
+
totalFrames: perf?.totalFrames,
|
|
23581
|
+
speedRatio: compositionDurationMs && compositionDurationMs > 0 && elapsed > 0 ? Math.round(compositionDurationMs / elapsed * 100) / 100 : void 0,
|
|
23582
|
+
captureAvgMs: perf?.captureAvgMs,
|
|
23583
|
+
capturePeakMs: perf?.capturePeakMs,
|
|
23584
|
+
peakMemoryMb: bytesToMb2(process.memoryUsage.rss()),
|
|
23585
|
+
memoryFreeMb: bytesToMb2(freemem5())
|
|
23586
|
+
});
|
|
23354
23587
|
} catch (err) {
|
|
23588
|
+
try {
|
|
23589
|
+
const { trackRenderError: trackRenderError2 } = await Promise.resolve().then(() => (init_events(), events_exports));
|
|
23590
|
+
const { bytesToMb: bytesToMb2 } = await Promise.resolve().then(() => (init_system(), system_exports));
|
|
23591
|
+
const { freemem: freemem5 } = await import("os");
|
|
23592
|
+
trackRenderError2({
|
|
23593
|
+
fps: 30,
|
|
23594
|
+
quality: "standard",
|
|
23595
|
+
docker: false,
|
|
23596
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
23597
|
+
peakMemoryMb: bytesToMb2(process.memoryUsage.rss()),
|
|
23598
|
+
memoryFreeMb: bytesToMb2(freemem5())
|
|
23599
|
+
});
|
|
23600
|
+
} catch {
|
|
23601
|
+
}
|
|
23355
23602
|
const entry = renderJobs.get(jobId);
|
|
23356
23603
|
if (entry) {
|
|
23357
23604
|
entry.status = "failed";
|
|
@@ -23385,10 +23632,10 @@ function createStudioServer(options) {
|
|
|
23385
23632
|
app.get("/api/render/:jobId/download", (c2) => {
|
|
23386
23633
|
const { jobId } = c2.req.param();
|
|
23387
23634
|
const job = renderJobs.get(jobId);
|
|
23388
|
-
if (!job?.outputPath || !
|
|
23635
|
+
if (!job?.outputPath || !existsSync22(job.outputPath)) {
|
|
23389
23636
|
return c2.json({ error: "not found" }, 404);
|
|
23390
23637
|
}
|
|
23391
|
-
const content =
|
|
23638
|
+
const content = readFileSync10(job.outputPath);
|
|
23392
23639
|
return new Response(content, {
|
|
23393
23640
|
headers: {
|
|
23394
23641
|
"Content-Type": "video/mp4",
|
|
@@ -23408,10 +23655,10 @@ function createStudioServer(options) {
|
|
|
23408
23655
|
});
|
|
23409
23656
|
app.get("*", (c2) => {
|
|
23410
23657
|
const indexPath = resolve10(studioDir, "index.html");
|
|
23411
|
-
if (!
|
|
23658
|
+
if (!existsSync22(indexPath)) {
|
|
23412
23659
|
return c2.text("Studio not found. Rebuild with: pnpm run build", 500);
|
|
23413
23660
|
}
|
|
23414
|
-
return c2.html(
|
|
23661
|
+
return c2.html(readFileSync10(indexPath, "utf-8"));
|
|
23415
23662
|
});
|
|
23416
23663
|
return { app, watcher };
|
|
23417
23664
|
}
|
|
@@ -23450,7 +23697,7 @@ __export(dev_exports, {
|
|
|
23450
23697
|
default: () => dev_default
|
|
23451
23698
|
});
|
|
23452
23699
|
import { spawn as spawn7 } from "child_process";
|
|
23453
|
-
import { existsSync as
|
|
23700
|
+
import { existsSync as existsSync23, lstatSync, symlinkSync, unlinkSync, readlinkSync, mkdirSync as mkdirSync15 } from "fs";
|
|
23454
23701
|
import { resolve as resolve11, dirname as dirname11, basename as basename3, join as join21 } from "path";
|
|
23455
23702
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
23456
23703
|
import { createRequire } from "module";
|
|
@@ -23496,7 +23743,7 @@ async function runDevMode(dir) {
|
|
|
23496
23743
|
mkdirSync15(projectsDir, { recursive: true });
|
|
23497
23744
|
let createdSymlink = false;
|
|
23498
23745
|
if (dir !== symlinkPath) {
|
|
23499
|
-
if (
|
|
23746
|
+
if (existsSync23(symlinkPath)) {
|
|
23500
23747
|
try {
|
|
23501
23748
|
const stat = lstatSync(symlinkPath);
|
|
23502
23749
|
if (stat.isSymbolicLink()) {
|
|
@@ -23508,7 +23755,7 @@ async function runDevMode(dir) {
|
|
|
23508
23755
|
} catch {
|
|
23509
23756
|
}
|
|
23510
23757
|
}
|
|
23511
|
-
if (!
|
|
23758
|
+
if (!existsSync23(symlinkPath)) {
|
|
23512
23759
|
symlinkSync(dir, symlinkPath, "dir");
|
|
23513
23760
|
createdSymlink = true;
|
|
23514
23761
|
}
|
|
@@ -23550,7 +23797,7 @@ async function runDevMode(dir) {
|
|
|
23550
23797
|
if (createdSymlink) {
|
|
23551
23798
|
process.on("exit", () => {
|
|
23552
23799
|
try {
|
|
23553
|
-
if (
|
|
23800
|
+
if (existsSync23(symlinkPath)) unlinkSync(symlinkPath);
|
|
23554
23801
|
} catch {
|
|
23555
23802
|
}
|
|
23556
23803
|
});
|
|
@@ -23577,12 +23824,12 @@ async function runLocalStudioMode(dir) {
|
|
|
23577
23824
|
mkdirSync15(projectsDir, { recursive: true });
|
|
23578
23825
|
let createdSymlink = false;
|
|
23579
23826
|
if (dir !== symlinkPath) {
|
|
23580
|
-
if (
|
|
23827
|
+
if (existsSync23(symlinkPath) && lstatSync(symlinkPath).isSymbolicLink()) {
|
|
23581
23828
|
if (resolve11(readlinkSync(symlinkPath)) !== resolve11(dir)) {
|
|
23582
23829
|
unlinkSync(symlinkPath);
|
|
23583
23830
|
}
|
|
23584
23831
|
}
|
|
23585
|
-
if (!
|
|
23832
|
+
if (!existsSync23(symlinkPath)) {
|
|
23586
23833
|
symlinkSync(dir, symlinkPath, "dir");
|
|
23587
23834
|
createdSymlink = true;
|
|
23588
23835
|
}
|
|
@@ -23621,7 +23868,7 @@ async function runLocalStudioMode(dir) {
|
|
|
23621
23868
|
if (createdSymlink) {
|
|
23622
23869
|
process.on("exit", () => {
|
|
23623
23870
|
try {
|
|
23624
|
-
if (
|
|
23871
|
+
if (existsSync23(symlinkPath)) unlinkSync(symlinkPath);
|
|
23625
23872
|
} catch {
|
|
23626
23873
|
}
|
|
23627
23874
|
});
|
|
@@ -23658,6 +23905,9 @@ async function runEmbeddedMode(dir, startPort) {
|
|
|
23658
23905
|
console.log(` ${c.dim("Project")} ${c.accent(projectName)}`);
|
|
23659
23906
|
console.log(` ${c.dim("Studio")} ${c.accent(url)}`);
|
|
23660
23907
|
console.log();
|
|
23908
|
+
console.log(` ${c.dim("Edit with your AI agent \u2014 it has HyperFrames skills installed.")}`);
|
|
23909
|
+
console.log(` ${c.dim("Changes reload automatically in the studio.")}`);
|
|
23910
|
+
console.log();
|
|
23661
23911
|
console.log(` ${c.dim("Press Ctrl+C to stop")}`);
|
|
23662
23912
|
console.log();
|
|
23663
23913
|
import("open").then((mod) => mod.default(`${url}#project/${projectName}`)).catch(() => {
|
|
@@ -23727,17 +23977,17 @@ var init_format = __esm({
|
|
|
23727
23977
|
});
|
|
23728
23978
|
|
|
23729
23979
|
// src/utils/project.ts
|
|
23730
|
-
import { existsSync as
|
|
23980
|
+
import { existsSync as existsSync24, statSync as statSync8 } from "fs";
|
|
23731
23981
|
import { resolve as resolve12, basename as basename4 } from "path";
|
|
23732
23982
|
function resolveProject(dirArg) {
|
|
23733
23983
|
const dir = resolve12(dirArg ?? ".");
|
|
23734
23984
|
const name = basename4(dir);
|
|
23735
23985
|
const indexPath = resolve12(dir, "index.html");
|
|
23736
|
-
if (!
|
|
23986
|
+
if (!existsSync24(dir) || !statSync8(dir).isDirectory()) {
|
|
23737
23987
|
errorBox("Not a directory: " + dir);
|
|
23738
23988
|
process.exit(1);
|
|
23739
23989
|
}
|
|
23740
|
-
if (!
|
|
23990
|
+
if (!existsSync24(indexPath)) {
|
|
23741
23991
|
errorBox(
|
|
23742
23992
|
"No composition found in " + dir,
|
|
23743
23993
|
"No index.html file found.",
|
|
@@ -23826,8 +24076,8 @@ var render_exports = {};
|
|
|
23826
24076
|
__export(render_exports, {
|
|
23827
24077
|
default: () => render_default
|
|
23828
24078
|
});
|
|
23829
|
-
import { existsSync as
|
|
23830
|
-
import { cpus as
|
|
24079
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync16, statSync as statSync9 } from "fs";
|
|
24080
|
+
import { cpus as cpus3, freemem as freemem3 } from "os";
|
|
23831
24081
|
import { resolve as resolve13, dirname as dirname12, join as join22 } from "path";
|
|
23832
24082
|
function defaultWorkerCount() {
|
|
23833
24083
|
return Math.max(1, Math.min(Math.floor(CPU_CORE_COUNT / 2), 4));
|
|
@@ -23835,8 +24085,9 @@ function defaultWorkerCount() {
|
|
|
23835
24085
|
async function renderDocker(projectDir, outputPath, options) {
|
|
23836
24086
|
const producer = await loadProducer();
|
|
23837
24087
|
const startTime = Date.now();
|
|
24088
|
+
let job;
|
|
23838
24089
|
try {
|
|
23839
|
-
|
|
24090
|
+
job = producer.createRenderJob({
|
|
23840
24091
|
fps: options.fps,
|
|
23841
24092
|
quality: options.quality,
|
|
23842
24093
|
format: options.format,
|
|
@@ -23845,24 +24096,10 @@ async function renderDocker(projectDir, outputPath, options) {
|
|
|
23845
24096
|
});
|
|
23846
24097
|
await producer.executeRenderJob(job, projectDir, outputPath);
|
|
23847
24098
|
} catch (error) {
|
|
23848
|
-
|
|
23849
|
-
fps: options.fps,
|
|
23850
|
-
quality: options.quality,
|
|
23851
|
-
docker: true
|
|
23852
|
-
});
|
|
23853
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
23854
|
-
errorBox("Render failed", message, "Check Docker is running: docker info");
|
|
23855
|
-
process.exit(1);
|
|
24099
|
+
handleRenderError(error, options, startTime, true, "Check Docker is running: docker info");
|
|
23856
24100
|
}
|
|
23857
24101
|
const elapsed = Date.now() - startTime;
|
|
23858
|
-
|
|
23859
|
-
durationMs: elapsed,
|
|
23860
|
-
fps: options.fps,
|
|
23861
|
-
quality: options.quality,
|
|
23862
|
-
workers: options.workers,
|
|
23863
|
-
docker: true,
|
|
23864
|
-
gpu: options.gpu
|
|
23865
|
-
});
|
|
24102
|
+
trackRenderMetrics(job, elapsed, options, true);
|
|
23866
24103
|
printRenderComplete(outputPath, elapsed, options.quiet);
|
|
23867
24104
|
}
|
|
23868
24105
|
async function renderLocal(projectDir, outputPath, options) {
|
|
@@ -23884,30 +24121,58 @@ async function renderLocal(projectDir, outputPath, options) {
|
|
|
23884
24121
|
try {
|
|
23885
24122
|
await producer.executeRenderJob(job, projectDir, outputPath, onProgress);
|
|
23886
24123
|
} catch (error) {
|
|
23887
|
-
|
|
23888
|
-
fps: options.fps,
|
|
23889
|
-
quality: options.quality,
|
|
23890
|
-
docker: false
|
|
23891
|
-
});
|
|
23892
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
23893
|
-
errorBox("Render failed", message, "Try --docker for containerized rendering");
|
|
23894
|
-
process.exit(1);
|
|
24124
|
+
handleRenderError(error, options, startTime, false, "Try --docker for containerized rendering");
|
|
23895
24125
|
}
|
|
23896
24126
|
const elapsed = Date.now() - startTime;
|
|
24127
|
+
trackRenderMetrics(job, elapsed, options, false);
|
|
24128
|
+
printRenderComplete(outputPath, elapsed, options.quiet);
|
|
24129
|
+
}
|
|
24130
|
+
function getMemorySnapshot() {
|
|
24131
|
+
return {
|
|
24132
|
+
peakMemoryMb: bytesToMb(process.memoryUsage.rss()),
|
|
24133
|
+
memoryFreeMb: bytesToMb(freemem3())
|
|
24134
|
+
};
|
|
24135
|
+
}
|
|
24136
|
+
function handleRenderError(error, options, startTime, docker, hint) {
|
|
24137
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
24138
|
+
trackRenderError({
|
|
24139
|
+
fps: options.fps,
|
|
24140
|
+
quality: options.quality,
|
|
24141
|
+
docker,
|
|
24142
|
+
workers: options.workers,
|
|
24143
|
+
gpu: options.gpu,
|
|
24144
|
+
elapsedMs: Date.now() - startTime,
|
|
24145
|
+
errorMessage: message,
|
|
24146
|
+
...getMemorySnapshot()
|
|
24147
|
+
});
|
|
24148
|
+
errorBox("Render failed", message, hint);
|
|
24149
|
+
process.exit(1);
|
|
24150
|
+
}
|
|
24151
|
+
function trackRenderMetrics(job, elapsedMs, options, docker) {
|
|
24152
|
+
const perf = job.perfSummary;
|
|
24153
|
+
const compositionDurationMs = perf ? Math.round(perf.compositionDurationSeconds * 1e3) : void 0;
|
|
24154
|
+
const speedRatio = compositionDurationMs && compositionDurationMs > 0 && elapsedMs > 0 ? Math.round(compositionDurationMs / elapsedMs * 100) / 100 : void 0;
|
|
23897
24155
|
trackRenderComplete({
|
|
23898
|
-
durationMs:
|
|
24156
|
+
durationMs: elapsedMs,
|
|
23899
24157
|
fps: options.fps,
|
|
23900
24158
|
quality: options.quality,
|
|
23901
24159
|
workers: options.workers,
|
|
23902
|
-
docker
|
|
23903
|
-
gpu: options.gpu
|
|
24160
|
+
docker,
|
|
24161
|
+
gpu: options.gpu,
|
|
24162
|
+
compositionDurationMs,
|
|
24163
|
+
compositionWidth: perf?.resolution.width,
|
|
24164
|
+
compositionHeight: perf?.resolution.height,
|
|
24165
|
+
totalFrames: perf?.totalFrames,
|
|
24166
|
+
speedRatio,
|
|
24167
|
+
captureAvgMs: perf?.captureAvgMs,
|
|
24168
|
+
capturePeakMs: perf?.capturePeakMs,
|
|
24169
|
+
...getMemorySnapshot()
|
|
23904
24170
|
});
|
|
23905
|
-
printRenderComplete(outputPath, elapsed, options.quiet);
|
|
23906
24171
|
}
|
|
23907
24172
|
function printRenderComplete(outputPath, elapsedMs, quiet) {
|
|
23908
24173
|
if (quiet) return;
|
|
23909
24174
|
let fileSize = "unknown";
|
|
23910
|
-
if (
|
|
24175
|
+
if (existsSync25(outputPath)) {
|
|
23911
24176
|
const stat = statSync9(outputPath);
|
|
23912
24177
|
fileSize = formatBytes(stat.size);
|
|
23913
24178
|
}
|
|
@@ -23927,10 +24192,11 @@ var init_render = __esm({
|
|
|
23927
24192
|
init_format();
|
|
23928
24193
|
init_progress();
|
|
23929
24194
|
init_events();
|
|
24195
|
+
init_system();
|
|
23930
24196
|
VALID_FPS = /* @__PURE__ */ new Set([24, 30, 60]);
|
|
23931
24197
|
VALID_QUALITY = /* @__PURE__ */ new Set(["draft", "standard", "high"]);
|
|
23932
24198
|
VALID_FORMAT = /* @__PURE__ */ new Set(["mp4", "webm"]);
|
|
23933
|
-
CPU_CORE_COUNT =
|
|
24199
|
+
CPU_CORE_COUNT = cpus3().length;
|
|
23934
24200
|
render_default = defineCommand({
|
|
23935
24201
|
meta: {
|
|
23936
24202
|
name: "render",
|
|
@@ -24101,7 +24367,7 @@ __export(transcribe_exports, {
|
|
|
24101
24367
|
transcribe: () => transcribe
|
|
24102
24368
|
});
|
|
24103
24369
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
24104
|
-
import { existsSync as
|
|
24370
|
+
import { existsSync as existsSync26, readFileSync as readFileSync11, mkdirSync as mkdirSync17, unlinkSync as unlinkSync2 } from "fs";
|
|
24105
24371
|
import { join as join23, extname as extname5 } from "path";
|
|
24106
24372
|
import { tmpdir as tmpdir2 } from "os";
|
|
24107
24373
|
function isAudioFile(filePath) {
|
|
@@ -24188,10 +24454,10 @@ async function transcribe(inputPath, outputDir, options) {
|
|
|
24188
24454
|
{ stdio: "ignore", timeout: 3e5 }
|
|
24189
24455
|
);
|
|
24190
24456
|
const transcriptPath = `${outputBase}.json`;
|
|
24191
|
-
if (!
|
|
24457
|
+
if (!existsSync26(transcriptPath)) {
|
|
24192
24458
|
throw new Error("Whisper did not produce output. Check the input file.");
|
|
24193
24459
|
}
|
|
24194
|
-
const transcript = JSON.parse(
|
|
24460
|
+
const transcript = JSON.parse(readFileSync11(transcriptPath, "utf-8"));
|
|
24195
24461
|
const segments = transcript.transcription ?? [];
|
|
24196
24462
|
let wordCount = 0;
|
|
24197
24463
|
let maxEnd = 0;
|
|
@@ -24230,12 +24496,12 @@ __export(init_exports, {
|
|
|
24230
24496
|
default: () => init_default
|
|
24231
24497
|
});
|
|
24232
24498
|
import {
|
|
24233
|
-
existsSync as
|
|
24499
|
+
existsSync as existsSync27,
|
|
24234
24500
|
mkdirSync as mkdirSync18,
|
|
24235
24501
|
copyFileSync as copyFileSync3,
|
|
24236
24502
|
cpSync as cpSync2,
|
|
24237
24503
|
writeFileSync as writeFileSync7,
|
|
24238
|
-
readFileSync as
|
|
24504
|
+
readFileSync as readFileSync12,
|
|
24239
24505
|
readdirSync as readdirSync7
|
|
24240
24506
|
} from "fs";
|
|
24241
24507
|
import { resolve as resolve14, basename as basename5, join as join24, dirname as dirname13 } from "path";
|
|
@@ -24361,7 +24627,7 @@ function resolveAssetDir(devSegments, builtSegments) {
|
|
|
24361
24627
|
const base = dirname13(fileURLToPath5(import.meta.url));
|
|
24362
24628
|
const devPath = resolve14(base, ...devSegments);
|
|
24363
24629
|
const builtPath = resolve14(base, ...builtSegments);
|
|
24364
|
-
return
|
|
24630
|
+
return existsSync27(devPath) ? devPath : builtPath;
|
|
24365
24631
|
}
|
|
24366
24632
|
function getStaticTemplateDir(templateId) {
|
|
24367
24633
|
return resolveAssetDir(["..", "templates", templateId], ["templates", templateId]);
|
|
@@ -24375,7 +24641,7 @@ function getBundledSkillsDir() {
|
|
|
24375
24641
|
function patchVideoSrc(dir, videoFilename, durationSeconds) {
|
|
24376
24642
|
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));
|
|
24377
24643
|
for (const file of htmlFiles) {
|
|
24378
|
-
let content =
|
|
24644
|
+
let content = readFileSync12(file, "utf-8");
|
|
24379
24645
|
if (videoFilename) {
|
|
24380
24646
|
content = content.replaceAll("__VIDEO_SRC__", videoFilename);
|
|
24381
24647
|
} else {
|
|
@@ -24390,7 +24656,7 @@ function patchVideoSrc(dir, videoFilename, durationSeconds) {
|
|
|
24390
24656
|
}
|
|
24391
24657
|
}
|
|
24392
24658
|
function patchTranscript(dir, transcriptPath) {
|
|
24393
|
-
const raw = JSON.parse(
|
|
24659
|
+
const raw = JSON.parse(readFileSync12(transcriptPath, "utf-8"));
|
|
24394
24660
|
const words = [];
|
|
24395
24661
|
for (const seg of raw.transcription ?? []) {
|
|
24396
24662
|
for (const token of seg.tokens ?? []) {
|
|
@@ -24414,7 +24680,7 @@ function patchTranscript(dir, transcriptPath) {
|
|
|
24414
24680
|
const wordsJson = JSON.stringify(words, null, 10).replace(/^\[/, "[").replace(/\n {10}/g, "\n ");
|
|
24415
24681
|
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));
|
|
24416
24682
|
for (const file of htmlFiles) {
|
|
24417
|
-
let content =
|
|
24683
|
+
let content = readFileSync12(file, "utf-8");
|
|
24418
24684
|
const scriptBlocks = content.match(/<script>[\s\S]*?<\/script>/g) ?? [];
|
|
24419
24685
|
let scriptMatch = null;
|
|
24420
24686
|
let transcriptMatch = null;
|
|
@@ -24531,7 +24797,7 @@ function scaffoldProject(destDir, name, templateId, localVideoName, durationSeco
|
|
|
24531
24797
|
"utf-8"
|
|
24532
24798
|
);
|
|
24533
24799
|
const sharedDir = getSharedTemplateDir();
|
|
24534
|
-
if (
|
|
24800
|
+
if (existsSync27(sharedDir)) {
|
|
24535
24801
|
for (const entry of readdirSync7(sharedDir, { withFileTypes: true })) {
|
|
24536
24802
|
const src = join24(sharedDir, entry.name);
|
|
24537
24803
|
const dest = resolve14(destDir, entry.name);
|
|
@@ -24541,11 +24807,11 @@ function scaffoldProject(destDir, name, templateId, localVideoName, durationSeco
|
|
|
24541
24807
|
}
|
|
24542
24808
|
}
|
|
24543
24809
|
const skillsSrcDir = getBundledSkillsDir();
|
|
24544
|
-
if (
|
|
24545
|
-
const projectSkills = ["compose
|
|
24810
|
+
if (existsSync27(skillsSrcDir)) {
|
|
24811
|
+
const projectSkills = ["hyperframes-compose", "hyperframes-captions"];
|
|
24546
24812
|
for (const skill of projectSkills) {
|
|
24547
24813
|
const src = join24(skillsSrcDir, skill);
|
|
24548
|
-
if (
|
|
24814
|
+
if (existsSync27(src)) {
|
|
24549
24815
|
const dest = resolve14(destDir, ".claude", "skills", skill);
|
|
24550
24816
|
mkdirSync18(dest, { recursive: true });
|
|
24551
24817
|
cpSync2(src, dest, { recursive: true });
|
|
@@ -24664,7 +24930,7 @@ Examples:
|
|
|
24664
24930
|
const templateId2 = resolvedTemplate;
|
|
24665
24931
|
const name2 = args.name ?? "my-video";
|
|
24666
24932
|
const destDir2 = resolve14(name2);
|
|
24667
|
-
if (
|
|
24933
|
+
if (existsSync27(destDir2) && readdirSync7(destDir2).length > 0) {
|
|
24668
24934
|
console.error(c.error(`Directory already exists and is not empty: ${name2}`));
|
|
24669
24935
|
process.exit(1);
|
|
24670
24936
|
}
|
|
@@ -24674,7 +24940,7 @@ Examples:
|
|
|
24674
24940
|
let sourceFilePath2;
|
|
24675
24941
|
if (videoFlag) {
|
|
24676
24942
|
const videoPath = resolve14(videoFlag);
|
|
24677
|
-
if (!
|
|
24943
|
+
if (!existsSync27(videoPath)) {
|
|
24678
24944
|
console.error(c.error(`Video file not found: ${videoFlag}`));
|
|
24679
24945
|
process.exit(1);
|
|
24680
24946
|
}
|
|
@@ -24688,7 +24954,7 @@ Examples:
|
|
|
24688
24954
|
}
|
|
24689
24955
|
if (audioFlag) {
|
|
24690
24956
|
const audioPath = resolve14(audioFlag);
|
|
24691
|
-
if (!
|
|
24957
|
+
if (!existsSync27(audioPath)) {
|
|
24692
24958
|
console.error(c.error(`Audio file not found: ${audioFlag}`));
|
|
24693
24959
|
process.exit(1);
|
|
24694
24960
|
}
|
|
@@ -24715,7 +24981,7 @@ Examples:
|
|
|
24715
24981
|
scaffoldProject(destDir2, basename5(destDir2), templateId2, localVideoName2, videoDuration2);
|
|
24716
24982
|
trackInitTemplate(templateId2);
|
|
24717
24983
|
const transcriptFile2 = resolve14(destDir2, "transcript.json");
|
|
24718
|
-
if (
|
|
24984
|
+
if (existsSync27(transcriptFile2)) {
|
|
24719
24985
|
patchTranscript(destDir2, transcriptFile2);
|
|
24720
24986
|
}
|
|
24721
24987
|
if (!skipSkills) {
|
|
@@ -24726,23 +24992,22 @@ Examples:
|
|
|
24726
24992
|
console.log(` ${c.accent(f)}`);
|
|
24727
24993
|
}
|
|
24728
24994
|
console.log();
|
|
24729
|
-
console.log("
|
|
24730
|
-
console.log(
|
|
24731
|
-
|
|
24732
|
-
);
|
|
24733
|
-
console.log(
|
|
24734
|
-
` ${c.accent(`cd ${name2}`)} && ${c.accent("npx hyperframes render")} ${c.dim("# render to MP4")}`
|
|
24735
|
-
);
|
|
24995
|
+
console.log("Get started:");
|
|
24996
|
+
console.log();
|
|
24997
|
+
console.log(` ${c.accent("1.")} Open this project with your AI coding agent:`);
|
|
24736
24998
|
console.log(
|
|
24737
|
-
`
|
|
24999
|
+
` ${c.accent(`cd ${name2}`)} then start ${c.accent("Claude Code")}, ${c.accent("Cursor")}, or your preferred agent`
|
|
24738
25000
|
);
|
|
24739
25001
|
console.log(
|
|
24740
|
-
`
|
|
25002
|
+
` ${c.dim("AI skills are installed \u2014 your agent knows how to create and edit compositions.")}`
|
|
24741
25003
|
);
|
|
24742
25004
|
console.log();
|
|
24743
|
-
console.log(
|
|
24744
|
-
|
|
24745
|
-
);
|
|
25005
|
+
console.log(` ${c.accent("2.")} Preview in the browser:`);
|
|
25006
|
+
console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("npx hyperframes dev")}`);
|
|
25007
|
+
console.log();
|
|
25008
|
+
console.log(` ${c.accent("3.")} Render to MP4 when ready:`);
|
|
25009
|
+
console.log(` ${c.accent(`cd ${name2}`)} && ${c.accent("npx hyperframes render")}`);
|
|
25010
|
+
console.log();
|
|
24746
25011
|
console.log(` ${c.dim("Full docs: hyperframes.heygen.com")}`);
|
|
24747
25012
|
return;
|
|
24748
25013
|
}
|
|
@@ -24764,7 +25029,7 @@ Examples:
|
|
|
24764
25029
|
name = nameResult;
|
|
24765
25030
|
}
|
|
24766
25031
|
const destDir = resolve14(name);
|
|
24767
|
-
if (
|
|
25032
|
+
if (existsSync27(destDir) && readdirSync7(destDir).length > 0) {
|
|
24768
25033
|
const overwrite = await Rt({
|
|
24769
25034
|
message: `Directory ${c.accent(name)} already exists and is not empty. Overwrite?`,
|
|
24770
25035
|
initialValue: false
|
|
@@ -24780,7 +25045,7 @@ Examples:
|
|
|
24780
25045
|
let isAudioOnly = false;
|
|
24781
25046
|
if (videoFlag) {
|
|
24782
25047
|
const videoPath = resolve14(videoFlag);
|
|
24783
|
-
if (!
|
|
25048
|
+
if (!existsSync27(videoPath)) {
|
|
24784
25049
|
R2.error(`File not found: ${videoFlag}`);
|
|
24785
25050
|
Nt("Setup cancelled.");
|
|
24786
25051
|
process.exit(1);
|
|
@@ -24815,7 +25080,7 @@ Examples:
|
|
|
24815
25080
|
validate(val) {
|
|
24816
25081
|
const trimmed = val?.trim();
|
|
24817
25082
|
if (!trimmed) return "Please enter a file path";
|
|
24818
|
-
if (!
|
|
25083
|
+
if (!existsSync27(resolve14(trimmed))) return "File not found";
|
|
24819
25084
|
return void 0;
|
|
24820
25085
|
}
|
|
24821
25086
|
});
|
|
@@ -24901,7 +25166,7 @@ Examples:
|
|
|
24901
25166
|
scaffoldProject(destDir, name, templateId, localVideoName, videoDuration);
|
|
24902
25167
|
trackInitTemplate(templateId);
|
|
24903
25168
|
const transcriptFile = resolve14(destDir, "transcript.json");
|
|
24904
|
-
if (
|
|
25169
|
+
if (existsSync27(transcriptFile)) {
|
|
24905
25170
|
patchTranscript(destDir, transcriptFile);
|
|
24906
25171
|
}
|
|
24907
25172
|
if (!skipSkills) {
|
|
@@ -24909,6 +25174,10 @@ Examples:
|
|
|
24909
25174
|
}
|
|
24910
25175
|
const files = readdirSync7(destDir);
|
|
24911
25176
|
Vt2(files.map((f) => c.accent(f)).join("\n"), c.success(`Created ${name}/`));
|
|
25177
|
+
R2.message(
|
|
25178
|
+
`${c.dim("Tip:")} Open this project with ${c.accent("Claude Code")}, ${c.accent("Cursor")}, or your preferred AI agent.
|
|
25179
|
+
${c.dim(" AI skills are installed \u2014 your agent knows how to create and edit compositions.")}`
|
|
25180
|
+
);
|
|
24912
25181
|
await nextStepLoop(destDir);
|
|
24913
25182
|
}
|
|
24914
25183
|
});
|
|
@@ -24920,7 +25189,7 @@ var lint_exports = {};
|
|
|
24920
25189
|
__export(lint_exports, {
|
|
24921
25190
|
default: () => lint_default
|
|
24922
25191
|
});
|
|
24923
|
-
import { readFileSync as
|
|
25192
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
24924
25193
|
var lint_default;
|
|
24925
25194
|
var init_lint2 = __esm({
|
|
24926
25195
|
"src/commands/lint.ts"() {
|
|
@@ -24938,7 +25207,7 @@ var init_lint2 = __esm({
|
|
|
24938
25207
|
},
|
|
24939
25208
|
async run({ args }) {
|
|
24940
25209
|
const project = resolveProject(args.dir);
|
|
24941
|
-
const html =
|
|
25210
|
+
const html = readFileSync13(project.indexPath, "utf-8");
|
|
24942
25211
|
const result = lintHyperframeHtml(html, { filePath: project.indexPath });
|
|
24943
25212
|
if (args.json) {
|
|
24944
25213
|
console.log(JSON.stringify(withMeta(result), null, 2));
|
|
@@ -24987,7 +25256,7 @@ var info_exports = {};
|
|
|
24987
25256
|
__export(info_exports, {
|
|
24988
25257
|
default: () => info_default
|
|
24989
25258
|
});
|
|
24990
|
-
import { readFileSync as
|
|
25259
|
+
import { readFileSync as readFileSync14, readdirSync as readdirSync8, statSync as statSync10 } from "fs";
|
|
24991
25260
|
import { join as join25 } from "path";
|
|
24992
25261
|
function totalSize(dir) {
|
|
24993
25262
|
let total = 0;
|
|
@@ -25020,7 +25289,7 @@ var init_info = __esm({
|
|
|
25020
25289
|
},
|
|
25021
25290
|
async run({ args }) {
|
|
25022
25291
|
const project = resolveProject(args.dir);
|
|
25023
|
-
const html =
|
|
25292
|
+
const html = readFileSync14(project.indexPath, "utf-8");
|
|
25024
25293
|
ensureDOMParser();
|
|
25025
25294
|
const parsed = parseHtml(html);
|
|
25026
25295
|
const tracks = new Set(parsed.elements.map((el) => el.zIndex));
|
|
@@ -25071,7 +25340,7 @@ var compositions_exports = {};
|
|
|
25071
25340
|
__export(compositions_exports, {
|
|
25072
25341
|
default: () => compositions_default
|
|
25073
25342
|
});
|
|
25074
|
-
import { readFileSync as
|
|
25343
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
25075
25344
|
function parseCompositions(html) {
|
|
25076
25345
|
const parser = new DOMParser();
|
|
25077
25346
|
const doc = parser.parseFromString(html, "text/html");
|
|
@@ -25128,7 +25397,7 @@ var init_compositions = __esm({
|
|
|
25128
25397
|
},
|
|
25129
25398
|
async run({ args }) {
|
|
25130
25399
|
const project = resolveProject(args.dir);
|
|
25131
|
-
const html =
|
|
25400
|
+
const html = readFileSync15(project.indexPath, "utf-8");
|
|
25132
25401
|
ensureDOMParser();
|
|
25133
25402
|
const compositions = parseCompositions(html);
|
|
25134
25403
|
if (compositions.length === 0) {
|
|
@@ -25164,7 +25433,7 @@ var benchmark_exports = {};
|
|
|
25164
25433
|
__export(benchmark_exports, {
|
|
25165
25434
|
default: () => benchmark_default
|
|
25166
25435
|
});
|
|
25167
|
-
import { existsSync as
|
|
25436
|
+
import { existsSync as existsSync28, statSync as statSync11 } from "fs";
|
|
25168
25437
|
import { resolve as resolve15, join as join26 } from "path";
|
|
25169
25438
|
var DEFAULT_CONFIGS, benchmark_default;
|
|
25170
25439
|
var init_benchmark = __esm({
|
|
@@ -25249,7 +25518,7 @@ var init_benchmark = __esm({
|
|
|
25249
25518
|
await producer.executeRenderJob(job, project.dir, outputPath);
|
|
25250
25519
|
const elapsedMs = Date.now() - startTime;
|
|
25251
25520
|
let fileSize = null;
|
|
25252
|
-
if (
|
|
25521
|
+
if (existsSync28(outputPath)) {
|
|
25253
25522
|
const stat = statSync11(outputPath);
|
|
25254
25523
|
fileSize = stat.size;
|
|
25255
25524
|
}
|
|
@@ -25458,7 +25727,7 @@ var docs_exports = {};
|
|
|
25458
25727
|
__export(docs_exports, {
|
|
25459
25728
|
default: () => docs_default
|
|
25460
25729
|
});
|
|
25461
|
-
import { readFileSync as
|
|
25730
|
+
import { readFileSync as readFileSync16, existsSync as existsSync29 } from "fs";
|
|
25462
25731
|
import { resolve as resolve16, dirname as dirname14, join as join27 } from "path";
|
|
25463
25732
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
25464
25733
|
function docsDir() {
|
|
@@ -25466,7 +25735,7 @@ function docsDir() {
|
|
|
25466
25735
|
const dir = dirname14(thisFile);
|
|
25467
25736
|
const devPath = resolve16(dir, "..", "docs");
|
|
25468
25737
|
const builtPath = resolve16(dir, "docs");
|
|
25469
|
-
return
|
|
25738
|
+
return existsSync29(devPath) ? devPath : builtPath;
|
|
25470
25739
|
}
|
|
25471
25740
|
function formatInlineCode(line) {
|
|
25472
25741
|
return line.replace(/`([^`]+)`/g, (_match, code) => c.accent(code));
|
|
@@ -25558,11 +25827,11 @@ var init_docs = __esm({
|
|
|
25558
25827
|
process.exit(1);
|
|
25559
25828
|
}
|
|
25560
25829
|
const filePath = join27(docsDir(), entry.file);
|
|
25561
|
-
if (!
|
|
25830
|
+
if (!existsSync29(filePath)) {
|
|
25562
25831
|
console.error(c.error(`Doc file not found: ${filePath}`));
|
|
25563
25832
|
process.exit(1);
|
|
25564
25833
|
}
|
|
25565
|
-
const content =
|
|
25834
|
+
const content = readFileSync16(filePath, "utf-8");
|
|
25566
25835
|
console.log();
|
|
25567
25836
|
renderMarkdown(content);
|
|
25568
25837
|
}
|
|
@@ -25576,6 +25845,7 @@ __export(doctor_exports, {
|
|
|
25576
25845
|
default: () => doctor_default
|
|
25577
25846
|
});
|
|
25578
25847
|
import { execSync as execSync3 } from "child_process";
|
|
25848
|
+
import { freemem as freemem4, platform as platform3 } from "os";
|
|
25579
25849
|
function checkFFmpeg() {
|
|
25580
25850
|
const path = findFFmpeg();
|
|
25581
25851
|
if (path) {
|
|
@@ -25653,6 +25923,67 @@ function checkVersion() {
|
|
|
25653
25923
|
function checkNode() {
|
|
25654
25924
|
return { ok: true, detail: `${process.version} (${process.platform} ${process.arch})` };
|
|
25655
25925
|
}
|
|
25926
|
+
function checkCPU() {
|
|
25927
|
+
const sys = getSystemMeta();
|
|
25928
|
+
const model = sys.cpu_model ?? "Unknown";
|
|
25929
|
+
const speedStr = sys.cpu_speed ? ` @ ${sys.cpu_speed}MHz` : "";
|
|
25930
|
+
return { ok: true, detail: `${sys.cpu_count} cores \xB7 ${model}${speedStr}` };
|
|
25931
|
+
}
|
|
25932
|
+
function checkMemory() {
|
|
25933
|
+
const sys = getSystemMeta();
|
|
25934
|
+
const freeMb = bytesToMb(freemem4());
|
|
25935
|
+
const totalGb = (sys.memory_total_mb / 1024).toFixed(1);
|
|
25936
|
+
const freeGb = (freeMb / 1024).toFixed(1);
|
|
25937
|
+
if (freeMb < 2048) {
|
|
25938
|
+
return {
|
|
25939
|
+
ok: false,
|
|
25940
|
+
detail: `${totalGb} GB total \xB7 ${freeGb} GB free`,
|
|
25941
|
+
hint: "Low memory \u2014 renders may fail. Close other apps or increase RAM."
|
|
25942
|
+
};
|
|
25943
|
+
}
|
|
25944
|
+
return { ok: true, detail: `${totalGb} GB total \xB7 ${freeGb} GB free` };
|
|
25945
|
+
}
|
|
25946
|
+
function checkShm() {
|
|
25947
|
+
const shmMb = getShmSizeMb();
|
|
25948
|
+
if (shmMb === null) {
|
|
25949
|
+
return { ok: true, detail: "N/A (non-Linux)" };
|
|
25950
|
+
}
|
|
25951
|
+
if (shmMb < 256) {
|
|
25952
|
+
return {
|
|
25953
|
+
ok: false,
|
|
25954
|
+
detail: `${shmMb} MB`,
|
|
25955
|
+
hint: "Chrome needs \u2265256 MB. Use: docker run --shm-size=512m"
|
|
25956
|
+
};
|
|
25957
|
+
}
|
|
25958
|
+
return { ok: true, detail: `${shmMb} MB` };
|
|
25959
|
+
}
|
|
25960
|
+
function checkDisk() {
|
|
25961
|
+
const freeMb = getFreeDiskMb(".");
|
|
25962
|
+
if (freeMb === null) {
|
|
25963
|
+
return { ok: true, detail: "Unable to check" };
|
|
25964
|
+
}
|
|
25965
|
+
const freeGb = (freeMb / 1024).toFixed(1);
|
|
25966
|
+
if (freeMb < 1024) {
|
|
25967
|
+
return {
|
|
25968
|
+
ok: false,
|
|
25969
|
+
detail: `${freeGb} GB free`,
|
|
25970
|
+
hint: "Low disk space \u2014 renders produce large temp files."
|
|
25971
|
+
};
|
|
25972
|
+
}
|
|
25973
|
+
return { ok: true, detail: `${freeGb} GB free` };
|
|
25974
|
+
}
|
|
25975
|
+
function checkEnvironment() {
|
|
25976
|
+
const sys = getSystemMeta();
|
|
25977
|
+
const parts = [];
|
|
25978
|
+
if (sys.is_docker) parts.push("Docker");
|
|
25979
|
+
if (sys.is_wsl) parts.push("WSL");
|
|
25980
|
+
if (sys.is_ci) parts.push(`CI (${sys.ci_name ?? "detected"})`);
|
|
25981
|
+
if (!sys.is_tty) parts.push("non-TTY");
|
|
25982
|
+
if (parts.length === 0) {
|
|
25983
|
+
return { ok: true, detail: "Native terminal" };
|
|
25984
|
+
}
|
|
25985
|
+
return { ok: true, detail: parts.join(" \xB7 ") };
|
|
25986
|
+
}
|
|
25656
25987
|
var doctor_default;
|
|
25657
25988
|
var init_doctor = __esm({
|
|
25658
25989
|
"src/commands/doctor.ts"() {
|
|
@@ -25663,6 +25994,7 @@ var init_doctor = __esm({
|
|
|
25663
25994
|
init_ffmpeg();
|
|
25664
25995
|
init_version();
|
|
25665
25996
|
init_updateCheck();
|
|
25997
|
+
init_system();
|
|
25666
25998
|
doctor_default = defineCommand({
|
|
25667
25999
|
meta: { name: "doctor", description: "Check system dependencies and environment" },
|
|
25668
26000
|
args: {},
|
|
@@ -25673,12 +26005,21 @@ var init_doctor = __esm({
|
|
|
25673
26005
|
const checks = [
|
|
25674
26006
|
{ name: "Version", run: checkVersion },
|
|
25675
26007
|
{ name: "Node.js", run: checkNode },
|
|
26008
|
+
{ name: "CPU", run: checkCPU },
|
|
26009
|
+
{ name: "Memory", run: checkMemory },
|
|
26010
|
+
{ name: "Disk", run: checkDisk }
|
|
26011
|
+
];
|
|
26012
|
+
if (platform3() === "linux") {
|
|
26013
|
+
checks.push({ name: "/dev/shm", run: checkShm });
|
|
26014
|
+
}
|
|
26015
|
+
checks.push(
|
|
26016
|
+
{ name: "Environment", run: checkEnvironment },
|
|
25676
26017
|
{ name: "FFmpeg", run: checkFFmpeg },
|
|
25677
26018
|
{ name: "FFprobe", run: checkFFprobe },
|
|
25678
26019
|
{ name: "Chrome", run: checkChrome },
|
|
25679
26020
|
{ name: "Docker", run: checkDocker },
|
|
25680
26021
|
{ name: "Docker running", run: checkDockerRunning }
|
|
25681
|
-
|
|
26022
|
+
);
|
|
25682
26023
|
let allOk = true;
|
|
25683
26024
|
for (const check of checks) {
|
|
25684
26025
|
const result = await check.run();
|
|
@@ -25877,6 +26218,7 @@ init_version();
|
|
|
25877
26218
|
init_config();
|
|
25878
26219
|
init_client();
|
|
25879
26220
|
init_events();
|
|
26221
|
+
init_system();
|
|
25880
26222
|
|
|
25881
26223
|
// src/cli.ts
|
|
25882
26224
|
init_updateCheck();
|