7ed2ccc414
The reference-round workflow, run end to end for a real template: Taste system (how we learn the user's taste, persisted): - references/TASTE_PROFILE.md (living design contract) + references/README.md (the daily loop) + a "reference round" stage in docs/TEMPLATE_BRIEF.md (provide refs or I suggest+mock directions). Design-quality before/after: - HeroDemo — the fix recipe vs the faint default: layered-depth background, a proper big video type scale, and a bold composed focal object. (Backgrounds were naked, text too small, scenes had no objects.) - YaldaSofreh3D + IGPromoDirections + IGProfileMock — reference-match proofs (low-poly 3D, 3 IG-promo style directions, the realistic IG-light page). Instagram channel-promo template (the deliverable — a flexible 5-scene FlexStory): - igkit + 5 blocks: IGIntro, IGProfile (realistic IG-light profile, scales to all aspects), IGFeed (post grid), IGStats (animated count-up), IGFollowCTA (Follow taps to "Following"). - FlexStory gains a `finish` toggle so the IG-light scenes render clean (no brand grade). INSTAGRAM_PROMO preset + 3 aspect comps in Root. Verified: a still of every scene at 9:16 renders clean; full preview MP4 rendering. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
280 lines
8.4 KiB
TypeScript
280 lines
8.4 KiB
TypeScript
import { Composition } from "remotion";
|
|
import { ASPECTS } from "./lib/aspect";
|
|
import { TEMPLATES } from "./templates";
|
|
import { Three3DTest } from "./compositions/Three3DTest";
|
|
import { YaldaSofreh3D } from "./compositions/YaldaSofreh3D";
|
|
import { HeroDemo } from "./compositions/HeroDemo";
|
|
import { IGPromoDirections, igPromoSchema } from "./compositions/IGPromoDirections";
|
|
import { IGProfileMock } from "./compositions/IGProfileMock";
|
|
import { AssetSheet } from "./compositions/AssetSheet";
|
|
import { StoryScenes, STORY_SCENES_DURATION } from "./compositions/StoryScenes";
|
|
import { FlexStory, flexStorySchema, flexStoryDefaults, calcFlexStoryMetadata } from "./compositions/FlexStory";
|
|
import { LogoMotion3D, logoMotion3DSchema, logoMotion3DDefaults } from "./compositions/LogoMotion3D";
|
|
import { CHARACTER_JOURNEY, INSTAGRAM_PROMO } from "./scenes/presets";
|
|
import {
|
|
IlluminatedCircles,
|
|
illuminatedCirclesSchema,
|
|
} from "./compositions/IlluminatedCircles";
|
|
import {
|
|
KineticQuote,
|
|
kineticQuoteSchema,
|
|
} from "./compositions/KineticQuote";
|
|
import {
|
|
GradientPromo,
|
|
gradientPromoSchema,
|
|
} from "./compositions/GradientPromo";
|
|
import {
|
|
VerticalStory,
|
|
verticalStorySchema,
|
|
} from "./compositions/VerticalStory";
|
|
|
|
const FPS = 30;
|
|
|
|
export const RemotionRoot: React.FC = () => {
|
|
return (
|
|
<>
|
|
{/* Logo intro — 16:9 */}
|
|
<Composition
|
|
id="IlluminatedCircles"
|
|
component={IlluminatedCircles}
|
|
durationInFrames={FPS * 6}
|
|
fps={FPS}
|
|
width={1920}
|
|
height={1080}
|
|
schema={illuminatedCirclesSchema}
|
|
defaultProps={{
|
|
logoText: "FLATRENDER",
|
|
tagline: "MOTION MADE SIMPLE",
|
|
accentColor: "#3ba7ff",
|
|
secondaryColor: "#a855f7",
|
|
backgroundColor: "#04060f",
|
|
}}
|
|
/>
|
|
|
|
{/* Kinetic typography quote — 1:1 social */}
|
|
<Composition
|
|
id="KineticQuote"
|
|
component={KineticQuote}
|
|
durationInFrames={FPS * 7}
|
|
fps={FPS}
|
|
width={1080}
|
|
height={1080}
|
|
schema={kineticQuoteSchema}
|
|
defaultProps={{
|
|
quote: "Great motion design is felt long before it is noticed.",
|
|
author: "FlatRender Studio",
|
|
accentColor: "#22d3ee",
|
|
secondaryColor: "#6366f1",
|
|
backgroundColor: "#0a0a12",
|
|
}}
|
|
/>
|
|
|
|
{/* Marketing / sale promo — 16:9 */}
|
|
<Composition
|
|
id="GradientPromo"
|
|
component={GradientPromo}
|
|
durationInFrames={FPS * 6}
|
|
fps={FPS}
|
|
width={1920}
|
|
height={1080}
|
|
schema={gradientPromoSchema}
|
|
defaultProps={{
|
|
eyebrow: "Limited time offer",
|
|
headline: "Make videos that move people.",
|
|
subheadline:
|
|
"Customizable code-based templates, rendered in the cloud in minutes.",
|
|
ctaText: "Start free →",
|
|
badgeText: "50% OFF",
|
|
accentColor: "#fb7185",
|
|
secondaryColor: "#f59e0b",
|
|
backgroundColor: "#0c0a14",
|
|
}}
|
|
/>
|
|
|
|
{/* Vertical social story — 9:16 */}
|
|
<Composition
|
|
id="VerticalStory"
|
|
component={VerticalStory}
|
|
durationInFrames={FPS * 6}
|
|
fps={FPS}
|
|
width={1080}
|
|
height={1920}
|
|
schema={verticalStorySchema}
|
|
defaultProps={{
|
|
kicker: "New drop",
|
|
line1: "Your story.",
|
|
line2: "Your style.",
|
|
line3: "One tap.",
|
|
ctaText: "Swipe up",
|
|
accentColor: "#34d399",
|
|
secondaryColor: "#3b82f6",
|
|
backgroundColor: "#060b0a",
|
|
}}
|
|
/>
|
|
|
|
{/* Tech/3D logo motion — quality-preview composition */}
|
|
{ASPECTS.map((a) => (
|
|
<Composition
|
|
key={`LogoMotion3D-${a.id}`}
|
|
id={`LogoMotion3D-${a.id}`}
|
|
component={LogoMotion3D}
|
|
durationInFrames={5 * FPS}
|
|
fps={FPS}
|
|
width={a.width}
|
|
height={a.height}
|
|
schema={logoMotion3DSchema}
|
|
defaultProps={logoMotion3DDefaults}
|
|
/>
|
|
))}
|
|
|
|
{/* 3D feasibility test */}
|
|
<Composition
|
|
id="Three3DTest"
|
|
component={Three3DTest}
|
|
durationInFrames={120}
|
|
fps={30}
|
|
width={1280}
|
|
height={720}
|
|
/>
|
|
|
|
{/* Design-quality before/after demo (rich bg + big type + bold object) */}
|
|
{ASPECTS.map((a) => (
|
|
<Composition
|
|
key={`HeroDemo-${a.id}`}
|
|
id={`HeroDemo-${a.id}`}
|
|
component={HeroDemo}
|
|
durationInFrames={5 * FPS}
|
|
fps={FPS}
|
|
width={a.width}
|
|
height={a.height}
|
|
/>
|
|
))}
|
|
|
|
{/* Instagram profile mock — realistic light-theme page (gate still) */}
|
|
<Composition
|
|
id="IGProfileMock"
|
|
component={IGProfileMock}
|
|
durationInFrames={150}
|
|
fps={FPS}
|
|
width={1080}
|
|
height={1920}
|
|
/>
|
|
|
|
{/* Instagram promo — reference round (3 style directions to pick from) */}
|
|
<Composition
|
|
id="IGPromoDir"
|
|
component={IGPromoDirections}
|
|
durationInFrames={150}
|
|
fps={FPS}
|
|
width={1080}
|
|
height={1920}
|
|
schema={igPromoSchema}
|
|
defaultProps={{ variant: "A" as const }}
|
|
/>
|
|
|
|
{/* Low-poly Yalda sofreh — reference-match challenge */}
|
|
<Composition
|
|
id="YaldaSofreh3D"
|
|
component={YaldaSofreh3D}
|
|
durationInFrames={150}
|
|
fps={30}
|
|
width={1080}
|
|
height={1080}
|
|
/>
|
|
|
|
{/* Dev preview: vendored CC0 character library (not a customer template) */}
|
|
<Composition
|
|
id="AssetSheet"
|
|
component={AssetSheet}
|
|
durationInFrames={60}
|
|
fps={30}
|
|
width={1920}
|
|
height={1080}
|
|
/>
|
|
|
|
{/* 2.5D story scenes proof (Three.js room + flat CC0 characters) — dev preview */}
|
|
{ASPECTS.map((a) => (
|
|
<Composition
|
|
key={`StoryScenes-${a.id}`}
|
|
id={`StoryScenes-${a.id}`}
|
|
component={StoryScenes}
|
|
durationInFrames={STORY_SCENES_DURATION * FPS}
|
|
fps={FPS}
|
|
width={a.width}
|
|
height={a.height}
|
|
/>
|
|
))}
|
|
|
|
{/* FlexStory — the scene engine: an ordered list of editable scene blocks,
|
|
duration computed dynamically from the per-scene durations. */}
|
|
{ASPECTS.map((a) => (
|
|
<Composition
|
|
key={`FlexStory-${a.id}`}
|
|
id={`FlexStory-${a.id}`}
|
|
component={FlexStory}
|
|
durationInFrames={26 * FPS}
|
|
fps={FPS}
|
|
width={a.width}
|
|
height={a.height}
|
|
schema={flexStorySchema}
|
|
defaultProps={flexStoryDefaults}
|
|
calculateMetadata={calcFlexStoryMetadata}
|
|
/>
|
|
))}
|
|
|
|
{/* CharacterJourney — pilot template: a curated FlexStory scene list (theme + story). */}
|
|
{ASPECTS.map((a) => (
|
|
<Composition
|
|
key={`CharacterJourney-${a.id}`}
|
|
id={`CharacterJourney-${a.id}`}
|
|
component={FlexStory}
|
|
durationInFrames={26 * FPS}
|
|
fps={FPS}
|
|
width={a.width}
|
|
height={a.height}
|
|
schema={flexStorySchema}
|
|
defaultProps={CHARACTER_JOURNEY}
|
|
calculateMetadata={calcFlexStoryMetadata}
|
|
/>
|
|
))}
|
|
|
|
{/* InstagramPromo — "follow our channel" template (realistic IG-light page). */}
|
|
{ASPECTS.map((a) => (
|
|
<Composition
|
|
key={`InstagramPromo-${a.id}`}
|
|
id={`InstagramPromo-${a.id}`}
|
|
component={FlexStory}
|
|
durationInFrames={18 * FPS}
|
|
fps={FPS}
|
|
width={a.width}
|
|
height={a.height}
|
|
schema={flexStorySchema}
|
|
defaultProps={INSTAGRAM_PROMO}
|
|
calculateMetadata={calcFlexStoryMetadata}
|
|
/>
|
|
))}
|
|
|
|
{/* Branded templates — each registered in all three aspects. A template may
|
|
supply a dedicated component per aspect (componentsByAspect) when its
|
|
design differs structurally; otherwise the shared `component` adapts
|
|
responsively. */}
|
|
{TEMPLATES.flatMap((tpl) =>
|
|
ASPECTS.map((a) => (
|
|
<Composition
|
|
key={`${tpl.id}-${a.id}`}
|
|
id={`${tpl.id}-${a.id}`}
|
|
component={tpl.componentsByAspect?.[a.id] ?? tpl.component}
|
|
durationInFrames={Math.round(FPS * tpl.durationSec)}
|
|
fps={FPS}
|
|
width={a.width}
|
|
height={a.height}
|
|
schema={tpl.schema}
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
defaultProps={tpl.defaultProps as any}
|
|
/>
|
|
))
|
|
)}
|
|
</>
|
|
);
|
|
};
|