diff --git a/.gitignore b/.gitignore index fac7ef9..6e816a3 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,31 @@ assets/ref-voice.wav *.bak skills/argo-guide-workspace/ videos/clips/ + +# Hyperframes-installed blocks — install via `npx hyperframes add ` per +# project (see demos/composition-trio.demo.ts for the install command). Blocks +# are version-pinned by hyperframes' CLI; tracking them here would fork the +# upstream artifacts. +models/ +compositions/apple-* +compositions/blue-sweater-* +compositions/vfx-* +compositions/experimental-* +compositions/components/ +assets/joe-sai-avatar.png +assets/sfx-production.wav +assets/sfx/ + +# Hyperframes-installed AI agent skills (`hyperframes skills` install). +# These are version-pinned by their CLI and would fork the upstream. +# Argo's own argo-guide skill stays tracked in skills/argo-guide and +# .agents/skills/argo-guide (symlinks). +.agents/skills/* +!.agents/skills/argo-guide +.claude/skills/ +skills/* +!skills/argo-guide +skills-lock.json + +# Hyperframes website-capture output — regenerable via `hyperframes capture`. +argo-showcase-captured/ diff --git a/compositions/argo-launch-intro.html b/compositions/argo-launch-intro.html new file mode 100644 index 0000000..14280f3 --- /dev/null +++ b/compositions/argo-launch-intro.html @@ -0,0 +1,48 @@ + + + + +Argo launch intro + + + +
+
+
+
+
Now demoing
+
Argo
+
Playwright in. Launch assets out.
+
+
+ + + + + diff --git a/compositions/argo-launch-outro.html b/compositions/argo-launch-outro.html new file mode 100644 index 0000000..ca9151c --- /dev/null +++ b/compositions/argo-launch-outro.html @@ -0,0 +1,41 @@ + + + + +Argo launch outro + + + +
+
+
+ +
Demos as code.
+
npx argo init
+
+
+ + + + + diff --git a/compositions/intro.html b/compositions/intro.html new file mode 100644 index 0000000..3a913c1 --- /dev/null +++ b/compositions/intro.html @@ -0,0 +1,46 @@ + + + + +Argo intro composition + + + +
+
+
+
Argo
+
Demos as code.
+
+
+ + + + + diff --git a/demos/argo-launch.config.mjs b/demos/argo-launch.config.mjs new file mode 100644 index 0000000..47f9292 --- /dev/null +++ b/demos/argo-launch.config.mjs @@ -0,0 +1,34 @@ +import { defineConfig } from '@argo-video/cli'; + +// Argo × Hyperframes crossover demo (Plan A). +// +// Stable chromium + jpeg-stitch + DSF=2 (Argo's high-quality recording path). +// Compositions are GSAP-only — no Canary / html-in-canvas needed. +// +// Run: +// BASE_URL=http://127.0.0.1:8976 \ +// npx argo pipeline argo-launch --config demos/argo-launch.config.mjs +export default defineConfig({ + baseURL: 'http://127.0.0.1:8976', + demosDir: 'demos', + outputDir: 'videos', + tts: { defaultVoice: 'af_heart', defaultSpeed: 1.0 }, + video: { + width: 1920, + height: 1080, + fps: 30, + browser: 'chromium', + captureMode: 'jpeg-stitch', + deviceScaleFactor: 2, + }, + export: { + preset: 'medium', + crf: 18, + encoder: 'cpu', + transition: { type: 'fade-through-black', durationMs: 600 }, + audio: { loudnorm: true }, + }, + overlays: { + autoBackground: true, + }, +}); diff --git a/demos/argo-launch.demo.ts b/demos/argo-launch.demo.ts new file mode 100644 index 0000000..caaf7e2 --- /dev/null +++ b/demos/argo-launch.demo.ts @@ -0,0 +1,44 @@ +// Argo × Hyperframes crossover demo (Plan A — tight launch teaser). +// +// Three-scene structure: +// 1. argo-launch-intro composition (4s, hand-rolled, hyperframes contract) +// 2. recorded Argo showcase hero (8s, the real running app) +// 3. argo-launch-outro composition (3s, hand-rolled) +// +// Total ~15s. Demonstrates the crossover seam: composition scenes book-end +// a real Playwright recording in a single argo pipeline invocation. +// +// Prerequisite: showcase server running — +// python3 -m http.server 8976 --directory demos +// +// Run: BASE_URL=http://127.0.0.1:8976 \ +// npx argo pipeline argo-launch --config demos/argo-launch.config.mjs +import { test } from '@argo-video/cli'; +import { renderComposition, spotlight, focusRing, resetCamera } from '@argo-video/cli'; + +test('argo-launch', async ({ page, narration }) => { + test.setTimeout(120_000); + + // Scene 1 — intro composition. Starts recording itself after the + // composition is ready, so the recording's first frame is the intro's + // first animated frame. + await renderComposition(page, narration, 'compositions/argo-launch-intro.html', { + scene: 'intro', + }); + + // Scene 2 — recorded Argo showcase hero. The recording is already active + // from the intro composition; just navigate to the showcase and let + // CDP-direct capture the page. + await page.goto('/showcase.html'); + await page.waitForTimeout(400); + narration.mark('hero'); + spotlight(page, '#hero-command', { duration: 5000, padding: 18 }); + focusRing(page, '#hero', { color: '#60a5fa', duration: 3500 }); + await page.waitForTimeout(7600); + await resetCamera(page); + + // Scene 3 — outro composition. + await renderComposition(page, narration, 'compositions/argo-launch-outro.html', { + scene: 'outro', + }); +}); diff --git a/demos/argo-launch.scenes.json b/demos/argo-launch.scenes.json new file mode 100644 index 0000000..44a8991 --- /dev/null +++ b/demos/argo-launch.scenes.json @@ -0,0 +1,5 @@ +[ + { "scene": "intro" }, + { "scene": "hero", "text": "This is Argo. Playwright in. Launch assets out." }, + { "scene": "outro" } +] diff --git a/demos/composition-intro.config.mjs b/demos/composition-intro.config.mjs new file mode 100644 index 0000000..d3ce82f --- /dev/null +++ b/demos/composition-intro.config.mjs @@ -0,0 +1,23 @@ +import { defineConfig } from '@argo-video/cli'; + +// Composition demo — renders compositions/intro.html as a single Argo scene. +// Demonstrates the renderComposition primitive without involving any +// recorded app content. +export default defineConfig({ + baseURL: 'about:blank', + demosDir: 'demos', + outputDir: 'videos', + video: { + width: 1920, + height: 1080, + fps: 30, + browser: 'chromium', + captureMode: 'jpeg-stitch', + deviceScaleFactor: 2, + }, + export: { + preset: 'medium', + crf: 18, + encoder: 'cpu', + }, +}); diff --git a/demos/composition-intro.demo.ts b/demos/composition-intro.demo.ts new file mode 100644 index 0000000..3d65f95 --- /dev/null +++ b/demos/composition-intro.demo.ts @@ -0,0 +1,17 @@ +// Proves out renderComposition end-to-end. The demo is composed entirely of +// composition scenes — no recorded app — to validate the primitive in +// isolation. Real demos will mix recorded scenes with composition scenes +// (intros, outros, device frames around the recording). +import { test } from '@argo-video/cli'; +import { renderComposition } from '@argo-video/cli'; + +test('composition-intro', async ({ page, narration }) => { + test.setTimeout(30_000); + + await narration.startRecording(page); + + await renderComposition(page, narration, 'compositions/intro.html', { + scene: 'intro', + // durationMs omitted — composition's data-duration (3.5s) is honored. + }); +}); diff --git a/demos/composition-intro.scenes.json b/demos/composition-intro.scenes.json new file mode 100644 index 0000000..dbf23e5 --- /dev/null +++ b/demos/composition-intro.scenes.json @@ -0,0 +1,3 @@ +[ + { "scene": "intro" } +] diff --git a/demos/composition-iphone.config.mjs b/demos/composition-iphone.config.mjs new file mode 100644 index 0000000..c24650a --- /dev/null +++ b/demos/composition-iphone.config.mjs @@ -0,0 +1,27 @@ +import { defineConfig } from '@argo-video/cli'; + +// Render an imported hyperframes block (vfx-iphone-device) as an Argo +// composition scene. The block uses html-in-canvas (CanvasDrawElement), +// which is currently Canary-only behind a flag. +// +// Run: npx argo pipeline composition-iphone --config demos/composition-iphone.config.mjs +export default defineConfig({ + baseURL: 'about:blank', + demosDir: 'demos', + outputDir: 'videos', + video: { + width: 1920, + height: 1080, + fps: 30, + browser: 'chromium', + captureMode: 'jpeg-stitch', + deviceScaleFactor: 2, + experimentalCanvasDrawElement: true, + browserChannel: 'chrome-canary', + }, + export: { + preset: 'medium', + crf: 18, + encoder: 'cpu', + }, +}); diff --git a/demos/composition-iphone.demo.ts b/demos/composition-iphone.demo.ts new file mode 100644 index 0000000..36a5321 --- /dev/null +++ b/demos/composition-iphone.demo.ts @@ -0,0 +1,50 @@ +// Imports a hyperframes block via `npx hyperframes add vfx-iphone-device` +// and renders it as an Argo composition scene. Proves the A+B crossover +// shapes — shared composition contract and renderComposition embedding — +// against a real hyperframes block (not a hand-rolled sample). +// +// Block install (one-time, requires Node 22+ for the hyperframes CLI): +// PATH="/opt/homebrew/opt/node@22/bin:$PATH" \ +// npx hyperframes add vfx-iphone-device +// +// Drops `compositions/vfx-iphone-device.html` plus `models/{iphone,macbook}.glb` +// + texture PNGs into the project. These are gitignored — install per checkout. +import { writeFileSync, appendFileSync } from 'node:fs'; +import { test } from '@argo-video/cli'; +import { renderComposition } from '@argo-video/cli'; + +// Browser-side logs and errors are buffered by record.ts and only surfaced +// on a Playwright failure, so write them directly to disk for diagnosis +// during iteration. Tail with: tail -f /tmp/comp-iphone-browser.log +const LOG = '/tmp/comp-iphone-browser.log'; +writeFileSync(LOG, ''); + +test('composition-iphone', async ({ page, narration }) => { + test.setTimeout(60_000); + + // Forward all page console messages + errors to /tmp/comp-iphone-browser.log + // since record.ts buffers stdout/stderr and only surfaces it on Playwright failure. + page.on('console', (msg) => appendFileSync(LOG, `[${msg.type()}] ${msg.text()}\n`)); + page.on('pageerror', (err) => appendFileSync(LOG, `[pageerror] ${err.message}\n${err.stack ?? ''}\n`)); + page.on('requestfailed', (req) => appendFileSync(LOG, `[reqfail] ${req.url()} ${req.failure()?.errorText ?? ''}\n`)); + page.on('response', (resp) => { + if (!resp.ok()) appendFileSync(LOG, `[http ${resp.status()}] ${resp.url()}\n`); + }); + + // Don't call narration.startRecording here — renderComposition starts the + // recording itself AFTER the composition's warmup (page load + DRACO + + // GLTF + Three.js init), so the captured video doesn't include 5-10s of + // black setup time. Mixed demos that have recorded app scenes BEFORE the + // first composition should still call startRecording themselves. + + // Block uses html-in-canvas (CanvasDrawElement). Requires + // experimentalCanvasDrawElement: true + browserChannel: 'chrome-canary' + // in the config. Block's data-duration is 15s. + await renderComposition(page, narration, 'compositions/vfx-iphone-device.html', { + scene: 'iphone-showcase', + // ready signal mismatch: hyperframes block uses internal paintReady + // flag rather than window.__compositionReady — we wait through the + // readyTimeoutMs and rely on data-duration to size the scene window. + readyTimeoutMs: 12_000, + }); +}); diff --git a/demos/composition-iphone.scenes.json b/demos/composition-iphone.scenes.json new file mode 100644 index 0000000..8ee7df6 --- /dev/null +++ b/demos/composition-iphone.scenes.json @@ -0,0 +1,3 @@ +[ + { "scene": "iphone-showcase" } +] diff --git a/demos/composition-trio.config.mjs b/demos/composition-trio.config.mjs new file mode 100644 index 0000000..2bfd8b9 --- /dev/null +++ b/demos/composition-trio.config.mjs @@ -0,0 +1,22 @@ +import { defineConfig } from '@argo-video/cli'; + +// Stable chromium config — apple-money-count and blue-sweater-intro-video +// are GSAP-only blocks, no html-in-canvas / WebGL / GLTF dependency. +export default defineConfig({ + baseURL: 'about:blank', + demosDir: 'demos', + outputDir: 'videos', + video: { + width: 1920, + height: 1080, + fps: 30, + browser: 'chromium', + captureMode: 'jpeg-stitch', + deviceScaleFactor: 2, + }, + export: { + preset: 'medium', + crf: 18, + encoder: 'cpu', + }, +}); diff --git a/demos/composition-trio.demo.ts b/demos/composition-trio.demo.ts new file mode 100644 index 0000000..907e414 --- /dev/null +++ b/demos/composition-trio.demo.ts @@ -0,0 +1,30 @@ +// Renders three hyperframes blocks back-to-back as a demo of the +// renderComposition primitive against real catalog blocks (no Canary +// required). Validates that compositional crossover works for the +// stable-chromium subset of the hyperframes catalog. +// +// Block install (one-time, requires Node 22+ for the hyperframes CLI): +// PATH="/opt/homebrew/opt/node@22/bin:$PATH" npx hyperframes add apple-money-count +// PATH="/opt/homebrew/opt/node@22/bin:$PATH" npx hyperframes add blue-sweater-intro-video +// +// All composition + asset files are gitignored — install per checkout. +import { test } from '@argo-video/cli'; +import { renderComposition } from '@argo-video/cli'; + +test('composition-trio', async ({ page, narration }) => { + test.setTimeout(120_000); + + // Don't call startRecording here — the first renderComposition call will + // start it after that composition's warmup so the recording's first + // frame is the first animated frame. + + // Block 1 — blue-sweater-intro-video (12s) — AI creator intro card. + await renderComposition(page, narration, 'compositions/blue-sweater-intro-video.html', { + scene: 'blue-sweater-intro-video', + }); + + // Block 2 — apple-money-count (5s) — finance counter with money burst. + await renderComposition(page, narration, 'compositions/apple-money-count.html', { + scene: 'apple-money-count', + }); +}); diff --git a/demos/composition-trio.scenes.json b/demos/composition-trio.scenes.json new file mode 100644 index 0000000..a62100c --- /dev/null +++ b/demos/composition-trio.scenes.json @@ -0,0 +1,4 @@ +[ + { "scene": "blue-sweater-intro-video" }, + { "scene": "apple-money-count" } +] diff --git a/skills/argo-guide/SKILL.md b/skills/argo-guide/SKILL.md index dd5714c..f582802 100644 --- a/skills/argo-guide/SKILL.md +++ b/skills/argo-guide/SKILL.md @@ -72,6 +72,7 @@ import { showOverlay, withOverlay } from '@argo-video/cli'; | `showOverlay(page, scene, durationMs)` | Show manifest overlay for N ms, then auto-remove. | | `withOverlay(page, scene, action)` | Show manifest overlay during an async action, auto-remove when done. | | `demoType(page, selectorOrLocator, text, delay?)` | Character-by-character typing (60ms default). Accepts CSS selector or Playwright Locator. | +| `renderComposition(page, narration, htmlPath, opts)` | Render a self-contained HTML composition (Hyperframes-shaped, paused GSAP timeline + `data-duration`) as a scene. **Use for content the real app can't show** — title cards, abstract concepts, comparisons, brand intros/outros. See "Mixing in Compositions" below. | ### Camera & Effects @@ -420,6 +421,78 @@ In the preview editor: scrub the timeline, click "Add scene at current time" to This enables the "record externally, post-produce in Argo" workflow — useful for desktop apps, mobile screen recordings, or any video source outside Playwright. +### Mixing in Compositions (when recording isn't the right tool) + +Argo's strength is recording the **real running app**. But some scenes communicate better as authored motion graphics — concepts the product can't show on its own. `renderComposition(page, narration, htmlPath, { scene })` mounts a self-contained HTML composition as a scene in your demo, sandwiched between recorded scenes. + +**Default to recording. Reach for a composition when:** + +- **The content is conceptual or abstract** — "the wedge thesis", before/after framing, side-by-side comparisons, the *idea* of how the product fits in a workflow. A real recording would be forced. +- **You need a brand moment** — title cards, logo intros, kinetic-text outros, "now showing" interstitials between major sections. Argo's overlay templates handle simple cases; compositions handle the polished ones. +- **You're comparing things the recording can't capture together** — code-as-source vs screen-recorder side-by-side, before/after states of an external product, hypothetical workflows. +- **The visual is fundamentally a motion graphic** — animated stats counters, kinetic typography, schematic explainers, data visualizations of the product's story. + +**Stick with recording when** the content is the actual product UI, real interactions, real network state, or anything that benefits from being demonstrably real (not idealized). + +**The pattern**: + +```typescript +import { test } from '@argo-video/cli'; +import { renderComposition } from '@argo-video/cli'; + +test('mydemo', async ({ page, narration }) => { + // Composition intro — bookends the recorded portion with brand polish + await renderComposition(page, narration, 'compositions/intro.html', { scene: 'intro' }); + + // Recorded app demo — the bulk of the video, what only Argo can do + await page.goto('/'); + narration.mark('hero'); + // ... regular Argo recording with overlays / camera effects + await page.waitForTimeout(8000); + + // Optional intermediate composition for a concept that doesn't capture well + await renderComposition(page, narration, 'compositions/the-wedge.html', { scene: 'wedge' }); + + // Back to recorded app for the next major section + await page.goto('/preview'); + narration.mark('preview'); + // ... + + // Composition outro + await renderComposition(page, narration, 'compositions/outro.html', { scene: 'outro' }); +}); +``` + +**Hand-roll vs import from the catalog:** + +- For **specific concepts** that only your product would visualize (wedge thesis, custom comparison, schematic for your specific architecture), hand-roll a small composition. 30-60 lines of HTML+GSAP usually does it. +- For **generic polish moments** (animated logo intros/outros, kinetic title cards, lower-thirds, follow cards), reach for the Hyperframes catalog instead of writing your own. The blocks are battle-tested, brand-tunable, and ship with motion design no one wants to re-author. Examples: `logo-outro` for an animated logo reveal, `apple-money-count` for kinetic counters, `blue-sweater-intro-video` for AI-creator intros, `vfx-iphone-device` for "your app inside a real iPhone screen" (3D, needs Canary). + +```bash +PATH="/opt/homebrew/opt/node@22/bin:$PATH" npx hyperframes add logo-outro +# drops compositions/logo-outro.html + any model/asset files into the project +``` + +The catalog is at . Most blocks work on stable chromium; 3D/WebGL ones need `experimentalCanvasDrawElement: true` + `browserChannel: 'chrome-canary'`. See `references/compositions.md` for the import workflow. + +**Composition contract** (compatible with hyperframes blocks — `npx hyperframes add ` works): + +```html +
+ +
+ + +``` + +`renderComposition` waits for the timeline to register, marks the scene, plays the timeline, then holds for `data-duration`. Audio elements inside the composition (`