fix: address PR #339 review — 7 issues

1. Catalog failure safety: warn when catalog is empty or throws, so
   capture doesn't silently produce zero images
2. Dead path: extract-audio-data.py → skills/gsap/scripts/ (was
   skills/hyperframes/scripts/)
3. --json fonts compat: emit both `fonts` (string[]) and
   `fontsDetailed` (FontToken[]) to avoid breaking external consumers
4. Restore .cursorrules writing alongside AGENTS.md + CLAUDE.md
5. .gitignore: remove over-broad `projects/` and `videos/` entries,
   keep scoped `cursor-tests/` and `launch-video*/`
6. agentPromptGenerator: mark unused params as reserved with comments,
   remove _animations from buildPrompt
7. Cookie filter: threshold 20 → 8 chars to preserve footer copy like
   "© 2026 Stripe" (16 chars) and "Privacy & Terms" (15 chars)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ukimsanov
2026-04-19 15:34:15 -04:00
co-authored by Claude Opus 4.6
parent 517e327294
commit 4e87f28cd9
7 changed files with 21 additions and 22 deletions
+3 -5
View File
@@ -79,10 +79,8 @@ captures/
*-tour/
*-brand/
# Local regression-test scratch directories (website-to-hyperframes runs).
# These hold per-site captures, SCRIPT/STORYBOARD/DESIGN.md, compositions,
# narration audio, rendered MP4s — all reproducible, never commit.
# Local regression-test and video project scratch directories.
# These hold per-site captures, compositions, narration audio, rendered MP4s.
cursor-tests/
basecamp-video/
projects/
videos/
launch-video*/
@@ -20,30 +20,22 @@ export function generateAgentPrompt(
outputDir: string,
url: string,
tokens: DesignTokens,
animations: AnimationCatalog | undefined,
_animations: AnimationCatalog | undefined, // reserved for future animation summary
hasScreenshot: boolean,
hasLottie?: boolean,
hasShaders?: boolean,
_catalogedAssets?: CatalogedAsset[],
_catalogedAssets?: CatalogedAsset[], // reserved for future asset inventory
detectedLibraries?: string[],
): void {
const prompt = buildPrompt(
url,
tokens,
animations,
hasScreenshot,
hasLottie,
hasShaders,
detectedLibraries,
);
const prompt = buildPrompt(url, tokens, hasScreenshot, hasLottie, hasShaders, detectedLibraries);
writeFileSync(join(outputDir, "AGENTS.md"), prompt, "utf-8");
writeFileSync(join(outputDir, "CLAUDE.md"), prompt, "utf-8");
writeFileSync(join(outputDir, ".cursorrules"), prompt, "utf-8");
}
function buildPrompt(
url: string,
tokens: DesignTokens,
_animations: AnimationCatalog | undefined,
hasScreenshot: boolean,
hasLottie?: boolean,
hasShaders?: boolean,
@@ -57,6 +57,8 @@ export async function downloadAssets(
}
// 3. Images — use the catalog as the single source of truth (highest resolution, deduplicated)
// If the catalog is empty, asset download produces zero images — this is surfaced as a warning
// so the capture doesn't silently produce a half-empty dataset.
const imageUrls: { url: string; isPoster: boolean }[] = [];
if (catalogedAssets && catalogedAssets.length > 0) {
+3 -2
View File
@@ -134,9 +134,10 @@ export async function extractVisibleText(page: Page): Promise<string> {
if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') continue;
var tag = el.tagName.toLowerCase();
if (tag === 'script' || tag === 'style' || tag === 'noscript') continue;
// Skip short text inside nav/footer (catches nav links, cookie consent)
// Skip very short text inside nav/footer (catches single-word nav links)
// Threshold is 8 chars to preserve footer copy like "© 2026 Stripe" (16 chars)
var inNavOrFooter = el.closest('nav, footer, [role="navigation"]');
if (inNavOrFooter && text.length < 20) continue;
if (inNavOrFooter && text.length < 8) continue;
// Skip common cookie/consent patterns
if (cookieRe.test(text)) continue;
texts.push('[' + tag + '] ' + text);
+6 -1
View File
@@ -339,8 +339,13 @@ export async function captureWebsite(
const { catalogAssets } = await import("./assetCataloger.js");
catalogedAssets = await catalogAssets(page1);
progress("design", `${catalogedAssets.length} assets cataloged`);
if (catalogedAssets.length === 0) {
warnings.push(
"Asset catalog is empty — no images will be downloaded. The page may use non-standard image loading.",
);
}
} catch (err) {
warnings.push(`Asset cataloging failed: ${err}`);
warnings.push(`Asset cataloging failed (no images will be downloaded): ${err}`);
}
// ── MUTATION phase: extractHtml modifies the live DOM (converts images to data URLs) ──
+2 -1
View File
@@ -115,7 +115,8 @@ export default defineCommand({
screenshots: result.screenshots.length,
assets: result.assets.length,
detectedSections: result.tokens.sections.length,
fonts: result.tokens.fonts,
fonts: result.tokens.fonts.map((f) => f.family),
fontsDetailed: result.tokens.fonts,
animations: result.animationCatalog?.summary,
warnings: result.warnings,
},
@@ -367,7 +367,7 @@ for (var f = 0; f < AUDIO_DATA.totalFrames; f++) {
Per-frame sampling is required — a single tween will not react. Use the extract script:
```bash
python3 skills/hyperframes/scripts/extract-audio-data.py narration.wav --fps 30 --bands 16 -o audio-data.json
python3 skills/gsap/scripts/extract-audio-data.py narration.wav --fps 30 --bands 16 -o audio-data.json
```
Keep text/logo intensity subtle (≤5% scale, ≤30% glow) — audio-reactive motion on tiny elements reads as jitter. Bigger backgrounds can push to 1030%.