Communitygithub.com

Sachin-pro-dev/PITCHFRAME-push-to-prod

Generate the Remotion scene plan for a launch video and render it to MP4, extending the beat components only when a plan needs something they genuinely cannot express. Used by Pitchframe for the code-generation and render steps.

O que é PITCHFRAME-push-to-prod?

PITCHFRAME-push-to-prod is a Cursor agent skill that generate the Remotion scene plan for a launch video and render it to MP4, extending the beat components only when a plan needs something they genuinely cannot express. Used by Pitchframe for the code-generation and render steps.

Funciona com~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/Sachin-pro-dev/PITCHFRAME-push-to-prod/tree/HEAD/skills/pitchframe-remotion

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Code generation and render

You are turning a brief into plan.json and rendering it. The reference project already implements the launch-video grammar — you are handing it data, not writing a video from scratch.

The polish level of the reference project is the polish level of every video this tool will ever produce. So the strong default is: write plan.json, change nothing else. Every line of bespoke animation you add to one run is polish that never reaches the next video, and a new way for the render to break.

The file map

pitchframe/
├── plan.json               ← the scene plan. This is the video.
├── palette.json            ← extracted colours. Data, never prose.
├── public/assets/*.png     ← captured screenshots.
├── scripts/
│   ├── extract-palette.mjs ← tailwind → CSS vars → live URL
│   ├── apply-palette.mjs   ← writes src/theme.ts from palette.json
│   └── capture.mjs         ← dev server + Playwright capture
├── src/
│   ├── Root.tsx            ← registers the LaunchVideo composition
│   ├── LaunchVideo.tsx     ← maps beats to Sequences. Owns the cross-dissolve.
│   ├── theme.ts            ← GENERATED. Every colour resolves here.
│   ├── lib/
│   │   ├── types.ts        ← the plan contract
│   │   └── motion.ts       ← easing, fades, click pulse, held drift
│   ├── components/         ← Background, Cursor, FocusOverlay, Zoom, Panel, …
│   └── scenes/
│       ├── TypographyBeat.tsx
│       ├── InteractionBeat.tsx   ← the hero moment
│       ├── UIBeat.tsx
│       └── CTABeat.tsx
└── output.mp4

Writing the plan

Load pitchframe-launch-video for the structure and schema. The short version:

  • Four parts — setup, hero, payoff, CTA. In that order. Always.
  • Exactly one interaction beat, and it gets at least 30% of the frames.
  • Duration comes from thesis_weight in the brief, not from a default.
  • Copy video_thesis and hero_moment into the plan so the critique pass can check the video against the claim.

Get the arithmetic right before you render

A failed render costs two minutes; a plan whose beats drift costs a re-render plus the time to notice. Check by hand, every time:

  • Beats contiguous — each frames[0] equals the previous frames[1].
  • First beat starts at 0; last beat ends exactly at total_frames.
  • total_frames === duration_seconds * fps.
  • No typography beat under 45 frames.
  • Interaction beat ≥ 30% of total_frames.

Reason about coordinates, don't guess them

target_element.coordinates and zoom_target are percentages of the screenshot. They are claims about where the interesting pixels are, and a focal point 10% off centres the hero shot on empty chrome.

Open the screenshot, find the element, convert to percentages, and sanity-check the result against what the beat is supposed to show. When the consequence of an action happens somewhere other than the target — a drag's destination, a field that updates elsewhere — point zoom_target at the consequence, not the thing the cursor touched.

Colours: never in the plan, never in a component

theme.ts is generated by apply-palette.mjs from palette.json. It is the only place a colour is written down.

node pitchframe/scripts/extract-palette.mjs      # → palette.json
node pitchframe/scripts/apply-palette.mjs        # → src/theme.ts

Rules that follow from that:

  • Never put a colour in plan.json. The plan says which word is accented; the theme says what the accent is.
  • Never put a colour literal in a component. If you need a shade the theme doesn't expose, add a derived token to the generator so every future video gets it too.
  • Never hand-edit theme.ts. It is overwritten on the next generate.

If two products' videos look identically coloured, theme.ts was not regenerated. That is the first thing to check, before touching anything else.

The render

cd pitchframe && npx remotion render LaunchVideo output.mp4

Three checks first, costing a second and saving a two-minute failed render:

  1. plan.json parses; the arithmetic above holds.
  2. Every screenshot named in the plan exists in public/assets/. An interaction beat needs both before_screenshot and after_screenshot.
  3. node_modules exists in pitchframe/. If not, npm install there first.

Expect 20–60 seconds for a 20-second video.

When the render fails

Read the actual error before changing anything.

SymptomCauseFix
Cannot find module '../plan.json'Scaffold incompleteRe-run npx pitchframe install
Cannot find name 'ACCENT' etc.theme.ts not generatedRun apply-palette.mjs
Delay render timeout on fontsOffline, or a font not on Google FontsSet fonts.sans to Inter in palette.json and regenerate
Image decode errorCorrupt or partial screenshotRe-capture that shot
Composition duration mismatchtotal_frames disagrees with the beatsFix the arithmetic
OOM / Chrome crashHuge screenshotsDownscale anything over ~4000px wide; --concurrency=2

Never respond to a render failure by rewriting the composition. Fix the data.

If the render fails twice, stop and preserve the evidence. Leave plan.json, palette.json and the assets in place and say which file to look at. A failed render with an inspectable plan is recoverable; one that has been "fixed" by a rewrite is not.

When you do have to touch the components

Only when a plan needs something the beat components genuinely cannot express. Three rules if you get there:

  1. Add, never rewrite. A new camera move is an entry in SHOTS in lib/camera.ts, not a restructured component — and it has to be listed in pitchframe-motion-language or nothing will ever choose it.
  2. Use lib/motion.ts and lib/camera.ts. EASE, EASE_OUT, clickPulse, heldDrift, entranceBlur are the genre. Hand-rolled easing is how one beat ends up looking like it came from a different video. Springs, bounces and elastic curves are banned. There is exactly one camera system. Do not add perspective or a 3D transform outside Stage.tsx; this repo had two once, they drifted, and the shots stopped looking like they were filmed by the same person.
  3. Keep it data-driven. Anything you add must be reachable from a field in plan.json. If the only way to trigger it is editing the component, it is a one-off and it does not belong in the reference.

Always wrong here: a colour literal; the product's name or copy hardcoded into a component; a new top-level composition; changing the 1920×1080 / 30fps contract; importing an animation library.

Adding a beat type is almost always the wrong answer. The schema has four because a launch video needs four. A new beat type is usually a feature beat wearing a disguise, and the moment the schema can express a feature list, plans start containing one.

After a successful render

Report the path and the edit affordance:

Done. Video ready at ./pitchframe/output.mp4

To edit, just ask:
  "hold the hero moment longer"
  "zoom on the field that changed instead"

Editing is handled by pitchframe-editing, not by re-running the pipeline.

Habilidades Relacionadas