Add HyperFrames skills
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011h6NB8JQtfPc6eRCefEzBB
This commit is contained in:
964 files changed
+142321
No files matched your search
@@ -0,0 +1,131 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import {
|
||||
cpSync,
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
readdirSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import test from "node:test";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const scriptsDir = dirname(fileURLToPath(import.meta.url));
|
||||
const skillDir = resolve(scriptsDir, "..");
|
||||
const fixturesDir = join(scriptsDir, "fixtures", "heroless");
|
||||
const makeTheme = join(scriptsDir, "make-theme.cjs");
|
||||
const themeNames = readdirSync(join(skillDir, "themes"))
|
||||
.filter((name) => name.endsWith(".json"))
|
||||
.map((name) => name.slice(0, -5))
|
||||
.sort();
|
||||
const expectedThemes = [
|
||||
"anchor",
|
||||
"arcade",
|
||||
"aurora",
|
||||
"biolume",
|
||||
"brush",
|
||||
"chalkboard",
|
||||
"dossier",
|
||||
"graffiti",
|
||||
"hologram",
|
||||
"inkwater",
|
||||
"laser",
|
||||
"lastpage",
|
||||
"neonsign",
|
||||
"ordnance",
|
||||
"papercut",
|
||||
"popup",
|
||||
"ransom",
|
||||
"scoreboard",
|
||||
"spectrum",
|
||||
"stardust",
|
||||
"stomp",
|
||||
"terminal",
|
||||
"thunder",
|
||||
"transit",
|
||||
"vhs",
|
||||
];
|
||||
const fixtureTheme = JSON.parse(readFileSync(join(fixturesDir, "theme.json"), "utf8"));
|
||||
const fixtureWords = fixtureTheme.lines.flat();
|
||||
|
||||
test("every embedded-caption theme compiles a sane heroless body timeline", async (t) => {
|
||||
assert.deepEqual(themeNames, expectedThemes);
|
||||
assert.equal(Object.hasOwn(fixtureTheme, "hero"), false);
|
||||
|
||||
const workspace = mkdtempSync(join(tmpdir(), "embedded-captions-heroless-"));
|
||||
t.after(() => rmSync(workspace, { recursive: true, force: true }));
|
||||
|
||||
for (const themeName of themeNames) {
|
||||
await t.test(themeName, () => {
|
||||
const project = join(workspace, themeName);
|
||||
cpSync(fixturesDir, project, { recursive: true });
|
||||
writeFileSync(
|
||||
join(project, "theme.json"),
|
||||
JSON.stringify({ ...fixtureTheme, dna: themeName }),
|
||||
);
|
||||
|
||||
const result = spawnSync(process.execPath, [makeTheme, project], {
|
||||
encoding: "utf8",
|
||||
timeout: 10_000,
|
||||
});
|
||||
assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
|
||||
|
||||
const dna = JSON.parse(readFileSync(join(skillDir, "themes", `${themeName}.json`), "utf8"));
|
||||
const bodyPage = dna.body.layer === "bg" ? "index.html" : "rail.html";
|
||||
const generated = readFileSync(join(project, bodyPage), "utf8");
|
||||
const generatedPages = readdirSync(project)
|
||||
.filter((name) => name.endsWith(".html"))
|
||||
.map((name) => readFileSync(join(project, name), "utf8"));
|
||||
|
||||
assert.match(generated, /data-composition-id="main"[^>]+data-duration="6"/s);
|
||||
assert.match(generated, /window\.__timelines\["main"\] = tl;/);
|
||||
for (const word of fixtureWords) assert.match(generated, new RegExp(`\\b${word}\\b`, "i"));
|
||||
for (const page of generatedPages) assert.doesNotMatch(page, /-99\d(?:\.\d+)?/);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
for (const heroless of [false, true]) {
|
||||
for (const grain of [0, 3, undefined]) {
|
||||
test(`postfx preserves grain ${grain} with heroless=${heroless}`, (t) => {
|
||||
const workspace = mkdtempSync(join(tmpdir(), "embedded-captions-grain-"));
|
||||
t.after(() => rmSync(workspace, { recursive: true, force: true }));
|
||||
const project = join(workspace, "project");
|
||||
cpSync(fixturesDir, project, { recursive: true });
|
||||
mkdirSync(join(workspace, "scripts"));
|
||||
mkdirSync(join(workspace, "themes"));
|
||||
const compiler = join(workspace, "scripts", "make-theme.cjs");
|
||||
cpSync(makeTheme, compiler);
|
||||
const dna = JSON.parse(readFileSync(join(skillDir, "themes", "anchor.json"), "utf8"));
|
||||
dna.plate.grain = grain;
|
||||
writeFileSync(join(workspace, "themes", "anchor.json"), JSON.stringify(dna));
|
||||
writeFileSync(
|
||||
join(project, "theme.json"),
|
||||
JSON.stringify({
|
||||
...fixtureTheme,
|
||||
dna: "anchor",
|
||||
...(heroless
|
||||
? {}
|
||||
: {
|
||||
hero: { match: "hero" },
|
||||
lines: fixtureTheme.lines.map((line) => line.filter((word) => word !== "hero")),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
const result = spawnSync(process.execPath, [compiler, project], {
|
||||
encoding: "utf8",
|
||||
timeout: 10_000,
|
||||
});
|
||||
assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
|
||||
const postfx = readFileSync(join(project, "_postfx.sh"), "utf8");
|
||||
if (grain === 0) assert.doesNotMatch(postfx, /noise=/);
|
||||
else assert.match(postfx, new RegExp(`noise=alls=${grain ?? 5}:allf=t\\+u`));
|
||||
assert.doesNotMatch(postfx, /,\s*\[v\]/);
|
||||
assert.match(postfx, heroless ? /\[0:v\]null/ : /zoompan=/);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user