feat(remotion): theme system + CharacterJourney pilot template
- src/scenes/themes.ts: 6 curated themes (the cohesion rail) — pick one, then
tweak the 4 brand colors; every block derives its shades so a theme re-skins
the whole video coherently (verified: same journey rendered in warm-editorial
vs berry-pop by overriding only the 4 colors).
- src/scenes/presets.ts: CHARACTER_JOURNEY — the pilot template's scene list
("Idea → struggle → tool → win", 7 beats) as a FlexStory preset.
- briefs/character-journey.md: the filled Template Spec from the guided brief.
- Root.tsx: register CharacterJourney per aspect (FlexStory + the preset).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# Template Spec — CharacterJourney (pilot)
|
||||
|
||||
Produced by the guided brief (`docs/TEMPLATE_BRIEF.md`). The first filled spec —
|
||||
serves as the audit trail and the contract a future AI/user-form would emit.
|
||||
|
||||
## Decisions (from the brief)
|
||||
- **Type:** character explainer / story (framework targets ALL Renderforest-style
|
||||
types — logo reveal, opener, slideshow, promo, showcase, personal/commercial — via
|
||||
the shared scene-block engine; this is the first concrete template).
|
||||
- **Story:** Idea → struggle → tool → win.
|
||||
- **Design ingredients (all on):** flat 2.5D characters · 3D depth + parallax ·
|
||||
kinetic typography · film finish (grain/vignette/micro-motion).
|
||||
- **Timing:** standard — 3s default, editable 1–6s per scene.
|
||||
- **Colors:** theme + 4-color tweak (curated `THEMES`, then accent/secondary/bg/text
|
||||
override). Pilot theme: `warm-editorial`.
|
||||
- **Music:** per-category/vibe — a tagged library; this arc → "uplifting build".
|
||||
*(Dependency: vendored CC0 music not yet sourced — audio hooks designed, stubbed.)*
|
||||
- **SFX:** optional, operator-placed (whoosh/click on transitions), user-toggle.
|
||||
*(Dependency: vendored CC0 SFX not yet sourced.)*
|
||||
- **Aspect:** all three (16:9 / 1:1 / 9:16), re-flowed.
|
||||
|
||||
## Scene list (storyboard) — `src/scenes/presets.ts` `CHARACTER_JOURNEY`
|
||||
| # | Block | Dur | Beat | Copy (fa) |
|
||||
|---|---|---|---|---|
|
||||
| 1 | TitleCard | 4s | Hook | «از یک ایده تا واقعیت» |
|
||||
| 2 | CharacterScene | 3s | Idea | «یک ایده» (cup) |
|
||||
| 3 | CharacterScene | 3s | Struggle | «اما سخت بود» |
|
||||
| 4 | CharacterScene | 3s | Tool | «تا اینکه…» (laptop) |
|
||||
| 5 | Slideshow | 6s | Value | «چرا فلترندر؟» × 3 |
|
||||
| 6 | CharacterScene | 3s | Win | «و حالا…» (plant) |
|
||||
| 7 | OutroCTA | 4s | CTA | «فلترندر» + «رایگان شروع کن» |
|
||||
|
||||
Total ≈ 26s (dynamic; tracks Σ per-scene durations).
|
||||
|
||||
## Editable surface (the "rails")
|
||||
- **Content:** per-scene title/caption/text/character/prop/slides (bounded fields,
|
||||
smart defaults).
|
||||
- **Structure:** flexible — add/duplicate/delete/reorder scenes; per-scene duration 1–6s.
|
||||
- **Theme:** pick a `THEMES` entry, then tweak the 4 brand colors.
|
||||
- **Locked (not user-editable):** layout, motion language, finishing pass, type scale —
|
||||
the craft stays in the system.
|
||||
|
||||
## Status
|
||||
- Renders via FlexStory in all 3 aspects (engine = Phase 1, committed).
|
||||
- Pending: theme picker in studio, music/SFX audio assets, seed into the catalog,
|
||||
backend render passthrough (Phase 2).
|
||||
@@ -5,6 +5,7 @@ import { Three3DTest } from "./compositions/Three3DTest";
|
||||
import { AssetSheet } from "./compositions/AssetSheet";
|
||||
import { StoryScenes, STORY_SCENES_DURATION } from "./compositions/StoryScenes";
|
||||
import { FlexStory, flexStorySchema, flexStoryDefaults, calcFlexStoryMetadata } from "./compositions/FlexStory";
|
||||
import { CHARACTER_JOURNEY } from "./scenes/presets";
|
||||
import {
|
||||
IlluminatedCircles,
|
||||
illuminatedCirclesSchema,
|
||||
@@ -156,6 +157,22 @@ export const RemotionRoot: React.FC = () => {
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* 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}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* 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
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { getTheme } from "./themes";
|
||||
import type { SceneInstance } from "./types";
|
||||
|
||||
/**
|
||||
* Template presets — a "template" is a curated default scene list + theme. The
|
||||
* studio instantiates this; the user then edits content / durations / reorders /
|
||||
* adds-removes scenes within the flexible engine.
|
||||
*
|
||||
* CharacterJourney — the pilot: "Idea → struggle → tool → win" (Persian).
|
||||
*/
|
||||
const warm = getTheme("warm-editorial").colors;
|
||||
|
||||
const journeyScenes: SceneInstance[] = [
|
||||
{ blockId: "TitleCard", durationSec: 4, props: { kicker: "داستان شما", title: "از یک ایده تا واقعیت", subtitle: "چطور ایدهات را به یک ویدیوی حرفهای تبدیل میکنی" } },
|
||||
{ blockId: "CharacterScene", durationSec: 3, props: { title: "یک ایده", caption: "همهچیز با یک جرقهٔ کوچک شروع شد", character: "illustrations/dicebear/openpeeps-04.svg", prop: "cup" } },
|
||||
{ blockId: "CharacterScene", durationSec: 3, props: { title: "اما سخت بود", caption: "ساختن یک ویدیوی حرفهای پیچیده بهنظر میرسید", character: "illustrations/dicebear/openpeeps-11.svg", prop: "none" } },
|
||||
{ blockId: "CharacterScene", durationSec: 3, props: { title: "تا اینکه…", caption: "با فلترندر آشنا شدم", character: "illustrations/dicebear/openpeeps-21.svg", prop: "laptop" } },
|
||||
{ blockId: "Slideshow", durationSec: 6, props: { title: "چرا فلترندر؟", slide1: "ساخت در چند دقیقه", slide2: "هزینهٔ بسیار پایین", slide3: "کیفیت حرفهای", slide4: "" } },
|
||||
{ blockId: "CharacterScene", durationSec: 3, props: { title: "و حالا…", caption: "داستان خودم را میسازم", character: "illustrations/dicebear/openpeeps-27.svg", prop: "plant" } },
|
||||
{ blockId: "OutroCTA", durationSec: 4, props: { brandText: "فلترندر", tagline: "همین حالا داستان خود را بساز", cta: "رایگان شروع کن" } },
|
||||
];
|
||||
|
||||
export const CHARACTER_JOURNEY = { scenes: journeyScenes, ...warm };
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { SceneColors } from "./types";
|
||||
|
||||
/**
|
||||
* Curated themes — the cohesion rail. A user picks ONE theme (a pre-balanced
|
||||
* look) and may then fine-tune the four brand colors. Because every block derives
|
||||
* its shades from these four roles (mixHex), one theme re-skins the whole video
|
||||
* coherently — the single biggest lever for the "designed" feel.
|
||||
*/
|
||||
export interface Theme {
|
||||
id: string;
|
||||
label: string; // Persian name shown in the studio
|
||||
colors: SceneColors;
|
||||
}
|
||||
|
||||
export const THEMES: Theme[] = [
|
||||
{ id: "warm-editorial", label: "ادیتوریال گرم", colors: { accentColor: "#cf8a76", secondaryColor: "#6f9d96", backgroundColor: "#ece4d6", textColor: "#2b3a55" } },
|
||||
{ id: "ocean-calm", label: "آرامش اقیانوس", colors: { accentColor: "#3b82c4", secondaryColor: "#5fb3b3", backgroundColor: "#eaf0f4", textColor: "#1f3a4d" } },
|
||||
{ id: "berry-pop", label: "توتفرنگی", colors: { accentColor: "#d6477e", secondaryColor: "#8b5cf6", backgroundColor: "#f3ebf0", textColor: "#3b2440" } },
|
||||
{ id: "forest", label: "جنگل", colors: { accentColor: "#5a9367", secondaryColor: "#c2a76a", backgroundColor: "#ecefe6", textColor: "#26352a" } },
|
||||
{ id: "mono-luxe", label: "لوکس مینیمال", colors: { accentColor: "#c0a062", secondaryColor: "#6b7280", backgroundColor: "#f4f1ea", textColor: "#1c1c1e" } },
|
||||
{ id: "midnight", label: "نیمهشب", colors: { accentColor: "#7c93ff", secondaryColor: "#22d3ee", backgroundColor: "#11141f", textColor: "#eef1f8" } },
|
||||
];
|
||||
|
||||
export const getTheme = (id: string): Theme => THEMES.find((t) => t.id === id) ?? THEMES[0];
|
||||
Reference in New Issue
Block a user