Communitygithub.com

suxrobGM/logistics-app

Playwright-driven teaser/demo/product launch video: storyboard, authenticated capture, PII review, animated title cards, MP4+GIF.

logistics-app란 무엇인가요?

logistics-app is a Claude Code agent skill that playwright-driven teaser/demo/product launch video: storyboard, authenticated capture, PII review, animated title cards, MP4+GIF.

지원 대상✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/suxrobGM/logistics-app/tree/main/.claude/skills/teaser-video

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Teaser video

Produce a launch-quality teaser (~30-45s) of a running app: storyboard it, film it by driving the real UI, cut it with ffmpeg, and hand back an MP4 plus a GIF for the README.

The output must look like a product video, not a screen recording. The difference is direction - staging, framing, and rhythm - not resolution.

Reference implementations live in reference/; they are working ESM modules, not pseudocode. Set up in a scratch directory - never inside the project:

cp -r <skill>/reference/* "$SCRATCH/" && cd "$SCRATCH" && npm install
export TEASER_BASE_URL="https://the-app.example.com"

reference/package.json declares "type": "module" and the three dependencies. Every script reads TEASER_BASE_URL, so nothing is hardcoded to one app.

Before anything else: the two hard rules

1. Never trigger real-world side effects without explicit, itemized consent.

Filming a real app means clicking real buttons. Some of those send email, post publicly, charge money, or submit forms to third parties. Before you touch the app:

  • Enumerate every action in your storyboard that leaves the machine.
  • Tell the user exactly what each one does and how many times, then get agreement on the count.
  • Prefer a mode that stops short of the irreversible step (a preview, a dry-run, a confirmation gate you can film and then decline).

There is no such thing as a read-only probe against a live system. A "diagnostic" click that lands in an already-running session fires for real. If you are debugging why an action did not work, assume your next click will work, and be ready for it.

If something fires that you did not intend: stop the process at its source immediately (kill the session/worker, not just the UI button, which may not take effect), verify the stopped state by reading it back, and tell the user plainly in your final message - count included.

2. Assume every frame is public and full of PII.

Real accounts carry names, emails, addresses, and documents. Decide the policy with the user up front:

  • Frame around it (default) - compose shots that exclude identity. Crop persistent chrome (sidebars, avatars, account menus) at the ffmpeg stage.
  • Hide before rolling - page.evaluate a visibility: hidden on offending cards before the camera rolls. Never film it and hope to cut around it.
  • Use a seeded demo account - cleanest when one exists.

Review every frame of the final cut at full resolution before delivering. Not the contact sheet - the frames.

Workflow

1. Scout and storyboard

Take authenticated screenshots of every candidate screen first (reference/scout.js). Look at them. Then write a storyboard table: beat, shot, what the viewer should feel, and length. A shot with no assigned job gets cut before it is filmed.

Arc that works: tension (the problem) → reveal (the product) → proof (it really does the thing) → scale (the results) → trust (where it runs) → close.

Note in the storyboard which screens hold PII and which are safe.

2. Stage the set

Curate before rolling, never after:

  • Pick views where the data looks its best - full charts, active lists, recognizable names, non-zero counts. Never film a spinner, an empty state, or a half-rendered chart.
  • Set UI state deliberately (panel widths, collapsed/expanded, filters, scroll position) via localStorage in an init script or a pre-roll page.evaluate.
  • Dismiss toasts, banners, and cookie bars off-camera.
  • Let data settle: networkidle plus a few seconds.

3. Film

Use CDP screencast (Page.startScreencast), not screen recording. It captures the page off-screen at ~100fps, so nothing the user is doing appears in frame and the machine stays usable. Playwright drives; CDP films. reference/rig.js implements it.

  • One subject per shot. Frame the thing, not "the whole app".
  • Camera moves come from scripted eased scrolling inside the page (cameraScroll), never from a filter. See the zoompan warning below.
  • The cursor is an actor: use the injected SVG cursor (cursorClick) that glides with easing and dips on press, or hide it. No idle drift.
  • Rehearse each take, extract frames, look at them, adjust, then film for real.

Filming an external window (a browser the app itself opens, a desktop app) needs OS capture - gdigrab/x11grab/avfoundation (recordDesktop in the rig). This captures everything on screen, including the user's own work. Warn the user, ask them to step away, and review the result frame by frame; delete it if it caught anything private.

4. Title cards

Animated HTML/CSS filmed in-browser, not static images. Inject the card markup into a blank page on the app's own origin so its web fonts and palette resolve, then screencast it. Pull the real display font off the live page (getComputedStyle) and pin a known-good mono stack rather than sniffing one.

Match the app's motion language (its own easing curves and durations). Load the frontend-design skill before designing them if the project has no established card style.

5. Cut

reference/build.js normalizes each segment to a common size/fps, then crossfades the chain. Keep cuts every ~2.5-4s. Give the payoff moment room at normal speed while compressing the routine around it.

6. Review, then deliver

  • Generate a contact sheet and per-second full-resolution frames.
  • Read the frames for PII, clipped panels, and unreadable text.
  • Verify duration and dimensions with a probe.
  • Deliver an MP4 plus, for READMEs, a palette-optimized GIF (GitHub will not inline-play a committed MP4). Keep the GIF under ~10 MB.
  • Leave it silent unless asked; you cannot license music. Say so.

Hard-won specifics

Never use zoompan for push-ins. It recomputes zoom per frame and rounds the crop window to whole pixels, so the image snaps back and forth - it reads as an earthquake. Use locked-off shots plus real in-page scrolling. If you must have a push-in, pre-scale the source 4x first, and verify with a frame-delta measurement (reference/jitter.js): a locked-off static shot should measure well under 1.0 mean delta; visible shake reads 3+.

Screencast stops emitting frames when animation settles. A card whose animation ends after 1.2s yields a 1.2s clip. Pad with tpad=stop_mode=clone:stop_duration=N and cut to the length you want with -t.

Assemble timestamped frames, not a fixed rate. Screencast frames arrive with metadata.timestamp; write an ffmpeg concat list with real per-frame durations, then resample to constant 60fps. Assuming a fixed interval produces judder.

ffmpeg-static as a dev dependency, resolved by module path. Never depend on ffmpeg being on PATH. (ffprobe is not in that package - parse ffmpeg -i stderr instead.)

Authenticated capture: log in once headed, save storageState, reuse it across every take (reference/login.js). Sessions expire - finish filming the same day or re-run it. Also seed any localStorage UI preferences there.

Localhost from a deployed origin is blocked by Chrome's local-network access policy. If the page must reach a local service, launch with --disable-features=LocalNetworkAccessChecks,PrivateNetworkAccessChecks,BlockInsecurePrivateNetworkRequests.

Crop persistent chrome at a fixed offset. Film at viewport width 1920 + railWidth, then crop=1920:1080:railWidth:0 to drop the sidebar and land exactly on 1080p.

Deliverables

Report the path, duration, and dimensions; state plainly what is still needed from the user (music, a GitHub-native video upload); and disclose anything that happened during filming that they would want to know.

관련 스킬