From c3bd8b777b7c39596c42ebbec309bf8234a7774b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Fri, 3 Apr 2026 13:26:53 +0200 Subject: [PATCH 1/2] fix: address QA report P0-P2 issues for 10/10 agent experience P0: Remove broken captions sub-composition from blank template (caused 404s for 10/10 agents), add clear animation pattern comment showing clip + inner wrapper structure, make compositions command introspect sub-compositions via data-composition-src. P1: Font mapping warnings now list all mapped fonts and suggest alternatives (including local install), browser 404s prefixed with [non-blocking] instead of [Browser:ERROR], default render concurrency increased from cores/2 (max 4) to cores*3/4 (max 6) with --concurrency alias. P2: gsap_css_transform_conflict lint rule now suggests exact GSAP property replacements (e.g. xPercent: -50), upgrade --yes actually runs the install instead of just printing the command. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/cli/src/commands/compositions.ts | 79 ++++++++++++++- packages/cli/src/commands/render.ts | 11 ++- packages/cli/src/commands/upgrade.ts | 31 ++++-- .../blank/compositions/captions.html | 95 ------------------- packages/cli/src/templates/blank/index.html | 19 ++-- packages/core/src/lint/rules/gsap.ts | 51 +++++++++- packages/engine/src/services/frameCapture.ts | 19 +++- .../src/services/deterministicFonts.ts | 24 ++++- 8 files changed, 196 insertions(+), 133 deletions(-) delete mode 100644 packages/cli/src/templates/blank/compositions/captions.html diff --git a/packages/cli/src/commands/compositions.ts b/packages/cli/src/commands/compositions.ts index 55220658..302ed303 100644 --- a/packages/cli/src/commands/compositions.ts +++ b/packages/cli/src/commands/compositions.ts @@ -1,6 +1,7 @@ import { defineCommand } from "citty"; import type { Example } from "./_examples.js"; -import { readFileSync } from "node:fs"; +import { existsSync, readFileSync } from "node:fs"; +import { resolve, dirname } from "node:path"; export const examples: Example[] = [ ["List compositions in the current project", "hyperframes compositions"], @@ -17,9 +18,10 @@ interface CompositionInfo { width: number; height: number; elementCount: number; + source?: string; } -function parseCompositions(html: string): CompositionInfo[] { +function parseCompositions(html: string, baseDir: string): CompositionInfo[] { const parser = new DOMParser(); const doc = parser.parseFromString(html, "text/html"); @@ -30,6 +32,18 @@ function parseCompositions(html: string): CompositionInfo[] { const id = div.getAttribute("data-composition-id") ?? "unknown"; const width = parseInt(div.getAttribute("data-width") ?? "1920", 10); const height = parseInt(div.getAttribute("data-height") ?? "1080", 10); + const compositionSrc = div.getAttribute("data-composition-src"); + + // If this references an external sub-composition, parse that file + if (compositionSrc) { + const subPath = resolve(baseDir, compositionSrc); + if (existsSync(subPath)) { + const subHtml = readFileSync(subPath, "utf-8"); + const subInfo = parseSubComposition(subHtml, id, width, height); + compositions.push({ ...subInfo, source: compositionSrc }); + return; + } + } const timedChildren = div.querySelectorAll("[data-start]"); let maxEnd = 0; @@ -67,6 +81,62 @@ function parseCompositions(html: string): CompositionInfo[] { return compositions; } +function parseSubComposition( + html: string, + fallbackId: string, + fallbackWidth: number, + fallbackHeight: number, +): CompositionInfo { + const parser = new DOMParser(); + const doc = parser.parseFromString(html, "text/html"); + + // Sub-compositions may use