From 92a5ef419b01d1462722032466c92491ef1ed243 Mon Sep 17 00:00:00 2001 From: ukimsanov Date: Sat, 18 Apr 2026 18:48:37 -0400 Subject: [PATCH] feat(capture): improve capture quality + clean up CLAUDE.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Capture improvements: - Font weights via document.fonts API + DOM sampling (variable font detection) - Section background-image extraction (no more false #FFFFFF on hero photos) - Detected libraries surfaced in CLAUDE.md brand summary - Structured visible-text.txt with [tag] prefixes, cookie/nav noise filtered - tokens.json cleaned: removed images/paragraphs/icons (duplicated elsewhere), filtered sections to heading-only, trimmed cssVariables to design-relevant - Removed redundant scroll pass in htmlExtractor (2-5s faster per capture) - Font cap at 20 families, Placeholder/Fallback fonts filtered CLAUDE.md rewrite: - Removed prescriptive tone ("use exact strings" → "rephrase freely") - Removed fluff sections (How to Create, DESIGN.md warning, Example Prompts, Source Patterns) - asset-descriptions.md promoted to first data row - Removed assets-catalog.json from inventory Skill fixes: - Dead shader refs → point to packages/shader-transitions/README.md - Google Fonts import in techniques.md → local @font-face placeholder - Added Stripe DESIGN.md as light-brand example - Removed HANDOFF.md generation from step-7 - Updated step-1 for new font weight + visible-text formats --- .../cli/src/capture/agentPromptGenerator.ts | 160 +++++++----------- packages/cli/src/capture/assetDownloader.ts | 16 +- packages/cli/src/capture/contentExtractor.ts | 8 +- packages/cli/src/capture/htmlExtractor.ts | 19 +-- packages/cli/src/capture/index.ts | 9 +- packages/cli/src/capture/scaffolding.ts | 2 + packages/cli/src/capture/tokenExtractor.ts | 100 ++++++++--- packages/cli/src/capture/types.ts | 23 ++- packages/cli/src/commands/capture.ts | 15 +- .../references/step-1-capture.md | 4 +- .../references/step-2-design.md | 50 ++++++ .../references/step-4-storyboard.md | 4 +- .../references/step-6-build.md | 2 +- .../references/step-7-validate.md | 37 ---- .../references/techniques.md | 10 +- 15 files changed, 249 insertions(+), 210 deletions(-) diff --git a/packages/cli/src/capture/agentPromptGenerator.ts b/packages/cli/src/capture/agentPromptGenerator.ts index 9df7f38dd..fb977d5c1 100644 --- a/packages/cli/src/capture/agentPromptGenerator.ts +++ b/packages/cli/src/capture/agentPromptGenerator.ts @@ -20,7 +20,8 @@ export function generateAgentPrompt( hasScreenshot: boolean, hasLottie?: boolean, hasShaders?: boolean, - catalogedAssets?: CatalogedAsset[], + _catalogedAssets?: CatalogedAsset[], + detectedLibraries?: string[], ): void { const prompt = buildPrompt( url, @@ -29,7 +30,7 @@ export function generateAgentPrompt( hasScreenshot, hasLottie, hasShaders, - catalogedAssets, + detectedLibraries, ); writeFileSync(join(outputDir, "CLAUDE.md"), prompt, "utf-8"); writeFileSync(join(outputDir, ".cursorrules"), prompt, "utf-8"); @@ -38,122 +39,81 @@ export function generateAgentPrompt( function buildPrompt( url: string, tokens: DesignTokens, - animations: AnimationCatalog | undefined, + _animations: AnimationCatalog | undefined, hasScreenshot: boolean, hasLottie?: boolean, hasShaders?: boolean, - catalogedAssets?: CatalogedAsset[], + detectedLibraries?: string[], ): string { - const hostname = new URL(url).hostname.replace(/^www\./, ""); - const title = tokens.title || hostname; - const cues = detectImplementationCues(tokens, animations); + const title = tokens.title || new URL(url).hostname.replace(/^www\./, ""); const colorSummary = tokens.colors.slice(0, 10).join(", "); - const fontSummary = tokens.fonts.join(", ") || "none detected"; - const sectionCount = tokens.sections?.length ?? 0; - const headingCount = tokens.headings?.length ?? 0; - const ctaCount = tokens.ctas?.length ?? 0; + const fontSummary = + tokens.fonts + .map( + (f) => + f.family + + (f.variable && f.weightRange + ? ` (${f.weightRange[0]}-${f.weightRange[1]} variable)` + : f.weights.length > 0 + ? ` (${f.weights.join(",")})` + : ""), + ) + .join(", ") || "none detected"; - const videoUrls = catalogedAssets - ? catalogedAssets - .filter((a) => a.type === "Video" && a.url.startsWith("http")) - .map((a) => a.url) - .filter((u, i, arr) => arr.indexOf(u) === i) // deduplicate - : []; + // Build the data inventory table rows + const tableRows: string[] = []; + if (hasScreenshot) { + tableRows.push( + "| `screenshots/scroll-*.png` | Viewport screenshots of the full page. Start with `scroll-000.png` (hero). |", + ); + } + tableRows.push( + "| `extracted/asset-descriptions.md` | One-line description of every downloaded asset. **Read this first.** |", + ); + tableRows.push( + `| \`extracted/tokens.json\` | Design tokens: ${tokens.colors.length} colors, ${tokens.fonts.length} fonts, ${tokens.headings?.length ?? 0} headings, ${tokens.ctas?.length ?? 0} CTAs |`, + ); + tableRows.push( + "| `extracted/visible-text.txt` | Page text in DOM order, prefixed with HTML tag (`[h1]`, `[p]`, `[a]`). Use as context — rephrase freely. |", + ); + if (hasLottie) { + tableRows.push( + "| `extracted/lottie-manifest.json` | Lottie animations with previews at `assets/lottie/previews/`. |", + ); + } + if (hasShaders) { + tableRows.push("| `extracted/shaders.json` | WebGL shader source (GLSL). |"); + } + if (detectedLibraries && detectedLibraries.length > 0) { + tableRows.push( + `| \`extracted/detected-libraries.json\` | Libraries: ${detectedLibraries.join(", ")} |`, + ); + } + tableRows.push("| `assets/` | Downloaded images, SVGs, and font files. |"); - return `# ${title} — Captured Website + // Brand summary — just the essentials + const brandLines: string[] = []; + brandLines.push(`- **Colors**: ${colorSummary || "see tokens.json"}`); + brandLines.push(`- **Fonts**: ${fontSummary}`); + if (detectedLibraries && detectedLibraries.length > 0) { + brandLines.push(`- **Built with**: ${detectedLibraries.join(", ")}`); + } + + return `# ${title} Source: ${url} -## How to Create a Video - -Invoke the \`/website-to-hyperframes\` skill. It walks you through the full workflow: read data → create DESIGN.md → plan video → build compositions → lint/validate/preview. - -If you don't have the skill installed, run: \`npx skills add heygen-com/hyperframes\` +To create a video from this capture, use the \`/website-to-hyperframes\` skill. ## What's in This Capture | File | Contents | |------|----------| -${hasScreenshot ? "| `screenshots/scroll-*.png` | Viewport screenshots covering the full page (1920x1080 each, 30% overlap). **View scroll-000.png FIRST** (hero section), then scan through the rest to understand the full page. |" : ""} -| \`extracted/tokens.json\` | Design tokens: ${tokens.colors.length} colors, ${tokens.fonts.length} fonts, ${headingCount} headings, ${ctaCount} CTAs, ${sectionCount} sections | -| \`extracted/visible-text.txt\` | All visible text content in DOM order — use exact strings, never paraphrase | -| \`extracted/assets-catalog.json\` | Every asset URL (images, fonts, videos, icons) with HTML context | -| \`extracted/animations.json\` | Animation catalog: ${animations?.summary?.webAnimations ?? 0} web animations, ${animations?.summary?.scrollTargets ?? 0} scroll triggers, ${animations?.summary?.canvases ?? 0} canvases | -| \`assets/svgs/\` | Extracted inline SVGs (logos, icons, illustrations) | -| \`assets/\` | Downloaded images and font files — **Read every image file to see what it contains** | -${hasLottie ? "| `extracted/lottie-manifest.json` | Lottie animations found on this site — read this to see what animations are available (name, dimensions, duration). Embed via `lottie.loadAnimation({ path: 'assets/lottie/animation-0.json' })`. Do NOT read the raw JSON files — they are machine data. |" : ""} -${videoUrls.length > 0 ? "| `extracted/video-manifest.json` | Video manifest: every `