Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions skills/embedded-captions/scripts/preview-frames.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@
const path = require("path");
const fs = require("fs");
const os = require("os");
const { pathToFileURL } = require("url");

function stripGsapSri(html) {
return html.replace(/<script\b[^>]*\bsrc=["'][^"']*gsap[^"']*["'][^>]*>/gi, (tag) =>
tag
.replace(/\s+integrity\s*=\s*(?:"[^"]*"|'[^']*')/gi, "")
.replace(/\s+crossorigin\s*=\s*(?:"[^"]*"|'[^']*')/gi, ""),
);
}

const HF_ROOTS = [
process.env.HYPERFRAMES_ROOT,
Expand Down Expand Up @@ -66,7 +75,7 @@ for (const r of HF_ROOTS) {
if (g) gsapSource = fs.readFileSync(g, "utf8");
}
}
if (!puppeteer || !sharp) {
if (require.main === module && (!puppeteer || !sharp)) {
console.error("[preview] need puppeteer+sharp — set HYPERFRAMES_ROOT");
process.exit(0);
}
Expand All @@ -80,17 +89,21 @@ async function shotAt(browser, file, W, H, t) {
// evaluation runs while document.head is still null, and gsap's init then
// throws "appendChild of null" — which killed previews for theme projects.
await page.setRequestInterception(true);
const documentUrl = pathToFileURL(file).href;
const documentHtml = stripGsapSri(fs.readFileSync(file, "utf8"));
page.on("request", (req) => {
const u = req.url();
if (req.resourceType() === "script" && /gsap/i.test(u) && /^https?:/i.test(u)) {
if (req.isNavigationRequest() && u === documentUrl) {
req.respond({ status: 200, contentType: "text/html", body: documentHtml });
} else if (req.resourceType() === "script" && /gsap/i.test(u) && /^https?:/i.test(u)) {
if (gsapSource)
req.respond({ status: 200, contentType: "application/javascript", body: gsapSource });
else req.continue(); // no local bundle — let the CDN load (online machines)
} else if (req.resourceType() === "media")
req.abort(); // a-roll pixels come from frames_bg
else req.continue();
});
await page.goto(`file://${file}`, { waitUntil: "load", timeout: 15000 });
await page.goto(documentUrl, { waitUntil: "load", timeout: 15000 });
const t0 = Date.now();
let tlReady = false;
while (Date.now() - t0 < 15000) {
Expand Down Expand Up @@ -260,9 +273,13 @@ async function main() {
await Promise.race([browser.close().catch(() => {}), new Promise((r) => setTimeout(r, 8000))]);
}
}
main()
.then(() => process.exit(0))
.catch((e) => {
console.error("[preview]", e.message);
process.exit(1);
});
if (require.main === module) {
main()
.then(() => process.exit(0))
.catch((e) => {
console.error("[preview]", e.message);
process.exit(1);
});
}

module.exports = { stripGsapSri };
15 changes: 15 additions & 0 deletions skills/embedded-captions/scripts/preview-frames.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const assert = require("node:assert/strict");
const test = require("node:test");
const { stripGsapSri } = require("./preview-frames.cjs");

test("removes SRI attributes only from GSAP script tags", () => {
const html = `
<script src="https://cdn.example/gsap@3.14.2/gsap.min.js" integrity="sha384-old" crossorigin="anonymous"></script>
<script src="https://cdn.example/app.js" integrity="sha384-app" crossorigin="anonymous"></script>
`;
const result = stripGsapSri(html);

assert.match(result, /gsap\.min\.js"><\/script>/);
assert.match(result, /app\.js" integrity="sha384-app" crossorigin="anonymous"/);
assert.doesNotMatch(result.match(/<script[^>]*gsap[^>]*>/i)[0], /integrity|crossorigin/i);
});
Loading