From e2f026e7319abd508738111ebcaa9b2d796ebd84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Wed, 29 Jul 2026 06:58:06 +0000 Subject: [PATCH 1/2] fix(skills): extend roots without explicit duration fixes reported:1785307750.289819:transitions-extend-tail-root-duration-contract-mismatch; PR #2859 and unrelated claims remain unmodified. --- .../scripts/transitions.mjs | 12 ++-- .../scripts/transitions.test.mjs | 68 +++++++++++++++++++ 2 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 skills/faceless-explainer/scripts/transitions.test.mjs diff --git a/skills/faceless-explainer/scripts/transitions.mjs b/skills/faceless-explainer/scripts/transitions.mjs index 2c2c568eab..adfd893309 100644 --- a/skills/faceless-explainer/scripts/transitions.mjs +++ b/skills/faceless-explainer/scripts/transitions.mjs @@ -117,16 +117,18 @@ function extendFrameTail(hyperframesDir, frame, baseDuration, targetDuration, di let extended = 0; const rewritten = html.replace(/<([A-Za-z][\w:-]*)\b([^>]*)>/g, (tag, name, attrs) => { const durationMatch = attrs.match(/\bdata-duration="([\d.]+)"/); - if (!durationMatch) return tag; - const duration = Number(durationMatch[1]); - if (!Number.isFinite(duration)) return tag; - const compositionMatch = attrs.match(/\bdata-composition-id="([^"]+)"/); if (compositionMatch?.[1] === compId && !foundRoot) { foundRoot = true; - return tag.replace(/\bdata-duration="[\d.]+"/, `data-duration="${targetDuration}"`); + return durationMatch + ? tag.replace(/\bdata-duration="[\d.]+"/, `data-duration="${targetDuration}"`) + : tag.replace(/(\s*\/?>)$/, ` data-duration="${targetDuration}"$1`); } + if (!durationMatch) return tag; + const duration = Number(durationMatch[1]); + if (!Number.isFinite(duration)) return tag; + if (name.toLowerCase() === "audio") return tag; const startMatch = attrs.match(/\bdata-start="([\d.]+)"/); if (!startMatch) return tag; diff --git a/skills/faceless-explainer/scripts/transitions.test.mjs b/skills/faceless-explainer/scripts/transitions.test.mjs new file mode 100644 index 0000000000..5cb3a14518 --- /dev/null +++ b/skills/faceless-explainer/scripts/transitions.test.mjs @@ -0,0 +1,68 @@ +import assert from "node:assert/strict"; +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { spawnSync } from "node:child_process"; +import test from "node:test"; + +const script = new URL("./transitions.mjs", import.meta.url).pathname; + +function write(filePath, contents) { + mkdirSync(dirname(filePath), { recursive: true }); + writeFileSync(filePath, contents); +} + +test("inject extends a documented frame root that omits data-duration", (t) => { + const project = mkdtempSync(join(tmpdir(), "faceless-transitions-")); + t.after(() => rmSync(project, { force: true, recursive: true })); + + write( + join(project, "STORYBOARD.md"), + `--- +format: 1920x1080 +--- + +## Frame 1 — First + +- duration: 2s +- transition_in: cut +- status: animated +- src: compositions/frames/01-a.html + +## Frame 2 — Second + +- duration: 2s +- transition_in: crossfade +- status: animated +- src: compositions/frames/02-b.html +`, + ); + write( + join(project, "index.html"), + ` +
+
+
+ +`, + ); + write( + join(project, "compositions/frames/01-a.html"), + ``, + ); + write( + join(project, "compositions/frames/02-b.html"), + ``, + ); + + const result = spawnSync( + process.execPath, + [script, "inject", "--storyboard", join(project, "STORYBOARD.md"), "--hyperframes", project], + { encoding: "utf8" }, + ); + + assert.equal(result.status, 0, result.stderr); + const outgoing = readFileSync(join(project, "compositions/frames/01-a.html"), "utf8"); + assert.match(outgoing, /data-composition-id="01-a" data-duration="2.5"/); + assert.match(outgoing, /class="clip"[^>]*data-duration="2.5"/); +}); From 4c65dfd000d2a12c6ae73d45d6dc2024f1ef6dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Wed, 29 Jul 2026 16:45:22 +0000 Subject: [PATCH 2/2] chore(skills): refresh manifest --- skills-manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skills-manifest.json b/skills-manifest.json index 12d4925207..704d8d4d26 100644 --- a/skills-manifest.json +++ b/skills-manifest.json @@ -6,8 +6,8 @@ "files": 140 }, "faceless-explainer": { - "hash": "72acdcb31403531d", - "files": 23 + "hash": "261a9740ec1378b0", + "files": 24 }, "figma": { "hash": "517e4dc53c13ea05",