mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-04 16:42:27 +00:00
fix(skills): stage SVGs that capture wrote into capture/assets/svgs/ (#3336)
`hyperframes capture` extracts inline SVGs into capture/assets/svgs/, and the
capture manifest advertises them to the agent as `assets/svgs/<name>.svg`, so a
frame names one in `asset_candidates` exactly the way it names a screenshot.
stageAssets searched only capture/{assets,assets/videos,screenshots}, so every
captured SVG resolved to nothing: logged as a non-fatal anomaly, and the frame
404'd the brand mark it had been told to use.
Add the directory to the search list, and cover it with a test that fails
without the fix.
lib/assets.mjs is byte-identical across product-launch-video,
faceless-explainer and pr-to-video, so the fix lands in all three. Folding it
into hyperframes-core/scripts/lib/, where frame-packets-core.mjs already lives,
is a separate change.
Co-authored-by: anikam13 <22992075+anikam13@users.noreply.github.com>
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
"files": 138
|
||||
},
|
||||
"faceless-explainer": {
|
||||
"hash": "f37cb1dba2890b79",
|
||||
"hash": "7d587ff36c975d9a",
|
||||
"files": 24
|
||||
},
|
||||
"figma": {
|
||||
@@ -62,12 +62,12 @@
|
||||
"files": 132
|
||||
},
|
||||
"pr-to-video": {
|
||||
"hash": "8ea0227e18ab5fc6",
|
||||
"hash": "14250b018114d26d",
|
||||
"files": 30
|
||||
},
|
||||
"product-launch-video": {
|
||||
"hash": "e89a8d11d99b4edf",
|
||||
"files": 28
|
||||
"hash": "12c0895d4963aa90",
|
||||
"files": 29
|
||||
},
|
||||
"remotion-to-hyperframes": {
|
||||
"hash": "bf184a65059b95e8",
|
||||
|
||||
@@ -17,8 +17,8 @@ export function basenamesFromCandidates(value) {
|
||||
}
|
||||
|
||||
// Copy each frame's asset_candidates from capture/{assets,assets/videos,
|
||||
// screenshots} into assets/. Already-staged files are left as is (first-wins),
|
||||
// so calling this twice is safe. Returns { staged, wanted, anomalies }.
|
||||
// assets/svgs, screenshots} into assets/. Already-staged files are left as is
|
||||
// (first-wins), so calling this twice is safe. Returns { staged, wanted, anomalies }.
|
||||
export function stageAssets({ hyperframesDir, frames }) {
|
||||
const wanted = new Set();
|
||||
for (const f of frames) {
|
||||
@@ -27,6 +27,7 @@ export function stageAssets({ hyperframesDir, frames }) {
|
||||
const captureDirs = [
|
||||
join(hyperframesDir, "capture/assets"),
|
||||
join(hyperframesDir, "capture/assets/videos"), // videos download into a subdir
|
||||
join(hyperframesDir, "capture/assets/svgs"), // inline SVGs extract into a subdir
|
||||
join(hyperframesDir, "capture/screenshots"),
|
||||
];
|
||||
const assetsDir = join(hyperframesDir, "assets");
|
||||
|
||||
@@ -17,8 +17,8 @@ export function basenamesFromCandidates(value) {
|
||||
}
|
||||
|
||||
// Copy each frame's asset_candidates from capture/{assets,assets/videos,
|
||||
// screenshots} into assets/. Already-staged files are left as is (first-wins),
|
||||
// so calling this twice is safe. Returns { staged, wanted, anomalies }.
|
||||
// assets/svgs, screenshots} into assets/. Already-staged files are left as is
|
||||
// (first-wins), so calling this twice is safe. Returns { staged, wanted, anomalies }.
|
||||
export function stageAssets({ hyperframesDir, frames }) {
|
||||
const wanted = new Set();
|
||||
for (const f of frames) {
|
||||
@@ -27,6 +27,7 @@ export function stageAssets({ hyperframesDir, frames }) {
|
||||
const captureDirs = [
|
||||
join(hyperframesDir, "capture/assets"),
|
||||
join(hyperframesDir, "capture/assets/videos"), // videos download into a subdir
|
||||
join(hyperframesDir, "capture/assets/svgs"), // inline SVGs extract into a subdir
|
||||
join(hyperframesDir, "capture/screenshots"),
|
||||
];
|
||||
const assetsDir = join(hyperframesDir, "assets");
|
||||
|
||||
@@ -17,8 +17,8 @@ export function basenamesFromCandidates(value) {
|
||||
}
|
||||
|
||||
// Copy each frame's asset_candidates from capture/{assets,assets/videos,
|
||||
// screenshots} into assets/. Already-staged files are left as is (first-wins),
|
||||
// so calling this twice is safe. Returns { staged, wanted, anomalies }.
|
||||
// assets/svgs, screenshots} into assets/. Already-staged files are left as is
|
||||
// (first-wins), so calling this twice is safe. Returns { staged, wanted, anomalies }.
|
||||
export function stageAssets({ hyperframesDir, frames }) {
|
||||
const wanted = new Set();
|
||||
for (const f of frames) {
|
||||
@@ -27,6 +27,7 @@ export function stageAssets({ hyperframesDir, frames }) {
|
||||
const captureDirs = [
|
||||
join(hyperframesDir, "capture/assets"),
|
||||
join(hyperframesDir, "capture/assets/videos"), // videos download into a subdir
|
||||
join(hyperframesDir, "capture/assets/svgs"), // inline SVGs extract into a subdir
|
||||
join(hyperframesDir, "capture/screenshots"),
|
||||
];
|
||||
const assetsDir = join(hyperframesDir, "assets");
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { existsSync, mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
import { stageAssets } from "./lib/assets.mjs";
|
||||
|
||||
// ── captured SVGs are stageable ──────────────────────────────────────────────
|
||||
// Regression: `hyperframes capture` extracts inline SVGs into capture/assets/svgs/
|
||||
// (assetDownloader.ts), and the capture manifest advertises them to the agent as
|
||||
// `assets/svgs/<name>.svg`, so a frame names one in `asset_candidates` exactly as
|
||||
// it names a screenshot. stageAssets only searched capture/{assets,assets/videos,
|
||||
// screenshots}, so every captured SVG resolved to nothing — reported as a
|
||||
// non-fatal anomaly, and the frame 404'd the logo it had been told to use.
|
||||
|
||||
function projectWithCapturedSvg() {
|
||||
const dir = mkdtempSync(join(tmpdir(), "product-launch-stage-assets-"));
|
||||
mkdirSync(join(dir, "capture/assets/svgs"), { recursive: true });
|
||||
mkdirSync(join(dir, "capture/screenshots"), { recursive: true });
|
||||
writeFileSync(join(dir, "capture/assets/svgs/brand-mark.svg"), "<svg/>");
|
||||
writeFileSync(join(dir, "capture/screenshots/hero.png"), "png");
|
||||
return dir;
|
||||
}
|
||||
|
||||
const frames = [
|
||||
{ extra: { asset_candidates: "assets/svgs/brand-mark.svg — the mark; assets/hero.png — hero" } },
|
||||
];
|
||||
|
||||
test("stages an SVG that capture wrote into capture/assets/svgs/", () => {
|
||||
const dir = projectWithCapturedSvg();
|
||||
|
||||
const { staged, wanted, anomalies } = stageAssets({ hyperframesDir: dir, frames });
|
||||
|
||||
assert.equal(wanted.size, 2);
|
||||
assert.equal(staged, 2, `expected both assets staged, got anomalies: ${anomalies.join("; ")}`);
|
||||
assert.deepEqual(anomalies, []);
|
||||
assert.ok(existsSync(join(dir, "assets/brand-mark.svg")));
|
||||
assert.ok(existsSync(join(dir, "assets/hero.png")));
|
||||
});
|
||||
|
||||
test("still reports an asset that exists nowhere under capture/", () => {
|
||||
const dir = projectWithCapturedSvg();
|
||||
|
||||
const { staged, anomalies } = stageAssets({
|
||||
hyperframesDir: dir,
|
||||
frames: [{ extra: { asset_candidates: "assets/svgs/absent.svg — never captured" } }],
|
||||
});
|
||||
|
||||
assert.equal(staged, 0);
|
||||
assert.equal(anomalies.length, 1);
|
||||
assert.match(anomalies[0], /absent\.svg/);
|
||||
});
|
||||
Reference in New Issue
Block a user