@devinilabs/reelstack 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +128 -0
- package/README.md +125 -0
- package/cli/beats.js +124 -0
- package/cli/bootstrap.js +124 -0
- package/cli/capture.js +34 -0
- package/cli/direction.js +114 -0
- package/cli/icons.js +49 -0
- package/cli/index.js +101 -0
- package/cli/init.js +253 -0
- package/cli/license.js +168 -0
- package/cli/lint.js +865 -0
- package/cli/preview.js +59 -0
- package/cli/render.js +404 -0
- package/cli/scaffold.js +239 -0
- package/cli/smoke.js +76 -0
- package/cli/update.js +26 -0
- package/cli/utils.js +184 -0
- package/docs/buyers-guide.md +220 -0
- package/docs/design-discipline.md +130 -0
- package/docs/family-galleries/dark.md +95 -0
- package/docs/family-galleries/forbidden.md +78 -0
- package/docs/family-galleries/glass.md +98 -0
- package/docs/family-galleries/paper.md +82 -0
- package/docs/family-galleries/warm.md +86 -0
- package/docs/superpowers/plans/2026-05-09-reelstack-init-readiness-gate.md +1166 -0
- package/docs/superpowers/specs/2026-05-09-reelstack-init-readiness-gate-design.md +233 -0
- package/families/dark/components/DriftingSpotlights.tsx +59 -0
- package/families/dark/components/FilmGrain.tsx +44 -0
- package/families/dark/components/ForestCard.tsx +43 -0
- package/families/dark/components/GridBackground.tsx +29 -0
- package/families/dark/components/RadialVignette.tsx +21 -0
- package/families/dark/components/Scanlines.tsx +35 -0
- package/families/dark/components/SegmentOpacity.ts +37 -0
- package/families/dark/components/index.ts +13 -0
- package/families/dark/index.ts +31 -0
- package/families/dark/palette.ts +98 -0
- package/families/dark/presets/claudedispatch.ts +46 -0
- package/families/dark/presets/codedrop.ts +37 -0
- package/families/dark/presets/gpt55.ts +54 -0
- package/families/dark/presets/notebooklm.ts +50 -0
- package/families/dark/presets/resourcescta.ts +35 -0
- package/families/dark/presets/skills.ts +40 -0
- package/families/dark/presets/stitch.ts +46 -0
- package/families/dark/presets/stitch2.ts +43 -0
- package/families/dark/typography.ts +16 -0
- package/families/forbidden/components/ForbiddenCausticBlobs.tsx +52 -0
- package/families/forbidden/components/NewsprintTexture.tsx +28 -0
- package/families/forbidden/components/TintedShadow.tsx +36 -0
- package/families/forbidden/components/index.ts +38 -0
- package/families/forbidden/index.ts +17 -0
- package/families/forbidden/palette.ts +88 -0
- package/families/forbidden/presets/heretic.ts +44 -0
- package/families/forbidden/typography.ts +18 -0
- package/families/glass/components/BreakdownCard.tsx +158 -0
- package/families/glass/components/CausticBlobs.tsx +49 -0
- package/families/glass/components/Counter.tsx +72 -0
- package/families/glass/components/EyebrowPill.tsx +59 -0
- package/families/glass/components/FilmStrip.tsx +202 -0
- package/families/glass/components/FloatingGlyphs.tsx +78 -0
- package/families/glass/components/GlassCard.tsx +58 -0
- package/families/glass/components/GlassCardBezel.tsx +45 -0
- package/families/glass/components/HairlineGrid.tsx +30 -0
- package/families/glass/components/IridescentRing.tsx +114 -0
- package/families/glass/components/IridescentText.tsx +98 -0
- package/families/glass/components/LightBeam.tsx +46 -0
- package/families/glass/components/ParticleBurst.tsx +62 -0
- package/families/glass/components/SonarRings.tsx +81 -0
- package/families/glass/components/StaggeredWords.tsx +74 -0
- package/families/glass/components/index.ts +20 -0
- package/families/glass/index.ts +31 -0
- package/families/glass/palette.ts +93 -0
- package/families/glass/presets/claudewatch.ts +64 -0
- package/families/glass/presets/claudewatchcta.ts +43 -0
- package/families/glass/presets/graphify.ts +45 -0
- package/families/glass/presets/gstack.ts +48 -0
- package/families/glass/presets/jcode.ts +50 -0
- package/families/glass/presets/lilagents.ts +52 -0
- package/families/glass/presets/paperclip.ts +43 -0
- package/families/glass/typography.ts +15 -0
- package/families/index.ts +49 -0
- package/families/paper/components/CardSpring.tsx +42 -0
- package/families/paper/components/CreamGrid.tsx +26 -0
- package/families/paper/components/EditorialSerifText.tsx +51 -0
- package/families/paper/components/GreenAccentCard.tsx +10 -0
- package/families/paper/components/PaperShadow.tsx +30 -0
- package/families/paper/components/ScaleBlurText.tsx +40 -0
- package/families/paper/components/index.ts +11 -0
- package/families/paper/index.ts +23 -0
- package/families/paper/palette.ts +102 -0
- package/families/paper/presets/designreel.ts +32 -0
- package/families/paper/presets/devini3d.ts +45 -0
- package/families/paper/presets/justdrop.ts +39 -0
- package/families/paper/presets/opus.ts +48 -0
- package/families/paper/typography.ts +17 -0
- package/families/warm/components/AccentGlow.tsx +60 -0
- package/families/warm/components/BentoCell.tsx +56 -0
- package/families/warm/components/BentoGrid.tsx +30 -0
- package/families/warm/components/FilmGrain.tsx +36 -0
- package/families/warm/components/ScaleBlurCounter.tsx +71 -0
- package/families/warm/components/WarmSurface.tsx +35 -0
- package/families/warm/components/index.ts +11 -0
- package/families/warm/index.ts +19 -0
- package/families/warm/palette.ts +81 -0
- package/families/warm/presets/huashu.ts +49 -0
- package/families/warm/presets/mempalace.ts +51 -0
- package/families/warm/typography.ts +17 -0
- package/package.json +85 -0
- package/reference/dark/claudedispatch.tsx +2441 -0
- package/reference/dark/notebooklm.tsx +2316 -0
- package/reference/dark/stitch.tsx +3040 -0
- package/reference/forbidden/heretic.tsx +2636 -0
- package/reference/glass/claudewatch.tsx +3827 -0
- package/reference/glass/graphify.tsx +2418 -0
- package/reference/glass/paperclip.tsx +2218 -0
- package/reference/paper/designreel.tsx +883 -0
- package/reference/paper/justdrop.tsx +1898 -0
- package/reference/paper/opus.tsx +1770 -0
- package/reference/warm/huashu.tsx +3413 -0
- package/reference/warm/mempalace.tsx +2909 -0
- package/skill/SKILL.md +229 -0
- package/skill/commands/reelstack-beats.md +20 -0
- package/skill/commands/reelstack-capture.md +24 -0
- package/skill/commands/reelstack-critique.md +15 -0
- package/skill/commands/reelstack-dark.md +40 -0
- package/skill/commands/reelstack-direction.md +17 -0
- package/skill/commands/reelstack-forbidden.md +25 -0
- package/skill/commands/reelstack-glass.md +39 -0
- package/skill/commands/reelstack-icons.md +22 -0
- package/skill/commands/reelstack-init.md +17 -0
- package/skill/commands/reelstack-lint.md +22 -0
- package/skill/commands/reelstack-paper.md +36 -0
- package/skill/commands/reelstack-render.md +20 -0
- package/skill/commands/reelstack-warm.md +36 -0
- package/templates/dark/template.tsx +115 -0
- package/templates/forbidden/template.tsx +111 -0
- package/templates/glass/template.tsx +201 -0
- package/templates/paper/template.tsx +133 -0
- package/templates/warm/template.tsx +210 -0
- package/utils/ai-purple-blocklist.ts +13 -0
- package/utils/banned-fonts.ts +11 -0
- package/utils/cubic-bezier.ts +36 -0
- package/utils/easing.ts +84 -0
- package/utils/grid.ts +13 -0
- package/utils/render-presets.json +56 -0
- package/utils/safe-zones.tsx +57 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-scaffolded by ReelStack.
|
|
3
|
+
*
|
|
4
|
+
* Family: Dark Cinematic
|
|
5
|
+
* Preset: __PRESET_NAME__ (source: __SOURCE_REEL__)
|
|
6
|
+
* Duration: __DURATION_FRAMES__ frames @ __FPS__ fps
|
|
7
|
+
*
|
|
8
|
+
* Late-night ad-film mood. Drifting spotlights + radial vignette + crossfaded
|
|
9
|
+
* scenes via the SegmentOpacity helper.
|
|
10
|
+
*/
|
|
11
|
+
import React from "react";
|
|
12
|
+
import { AbsoluteFill, getInputProps, useCurrentFrame, useVideoConfig } from "remotion";
|
|
13
|
+
import { palette } from "@devinilabs/reelstack/families/dark";
|
|
14
|
+
import {
|
|
15
|
+
DriftingSpotlights,
|
|
16
|
+
GridBackground,
|
|
17
|
+
RadialVignette,
|
|
18
|
+
ForestCard,
|
|
19
|
+
so,
|
|
20
|
+
} from "@devinilabs/reelstack/families/dark/components";
|
|
21
|
+
import { ease } from "@devinilabs/reelstack/utils/easing";
|
|
22
|
+
import { SafeZones, TOP_SAFE, BOTTOM_SAFE } from "@devinilabs/reelstack/utils/safe-zones";
|
|
23
|
+
|
|
24
|
+
const SEGS = {
|
|
25
|
+
hook: [0, 8.0],
|
|
26
|
+
body: [8.0, 30.0],
|
|
27
|
+
anchor: [30.0, 50.0],
|
|
28
|
+
cta: [50.0, 60.0],
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
/** Read inputProps for reduceMotion at module load.
|
|
32
|
+
* Buyers can render a low-motion variant via:
|
|
33
|
+
* npx remotion render <id> out/x.mp4 --props='{"reduceMotion":true}'
|
|
34
|
+
*/
|
|
35
|
+
const _inputProps = (getInputProps() as { reduceMotion?: boolean }) || {};
|
|
36
|
+
const REDUCE_MOTION = _inputProps.reduceMotion === true;
|
|
37
|
+
|
|
38
|
+
export const __REEL_NAME__Reel: React.FC = () => {
|
|
39
|
+
const { fps } = useVideoConfig();
|
|
40
|
+
const frame = useCurrentFrame();
|
|
41
|
+
return (
|
|
42
|
+
<AbsoluteFill style={{ background: palette.bg, fontFamily: "Geist, system-ui", color: palette.fg }}>
|
|
43
|
+
<DriftingSpotlights reduceMotion={REDUCE_MOTION} />
|
|
44
|
+
<GridBackground />
|
|
45
|
+
<RadialVignette />
|
|
46
|
+
|
|
47
|
+
<div style={{ position: "absolute", inset: 0, opacity: so(frame, fps, ...SEGS.hook) }}>
|
|
48
|
+
<HookScene />
|
|
49
|
+
</div>
|
|
50
|
+
<div style={{ position: "absolute", inset: 0, opacity: so(frame, fps, ...SEGS.body) }}>
|
|
51
|
+
<BodyScene />
|
|
52
|
+
</div>
|
|
53
|
+
<div style={{ position: "absolute", inset: 0, opacity: so(frame, fps, ...SEGS.anchor) }}>
|
|
54
|
+
<AnchorScene />
|
|
55
|
+
</div>
|
|
56
|
+
<div style={{ position: "absolute", inset: 0, opacity: so(frame, fps, ...SEGS.cta) }}>
|
|
57
|
+
<CtaScene />
|
|
58
|
+
</div>
|
|
59
|
+
<SafeZones visible={false} />
|
|
60
|
+
</AbsoluteFill>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const HookScene: React.FC = () => {
|
|
65
|
+
const frame = useCurrentFrame();
|
|
66
|
+
const t = ease.power4Out(Math.min(1, frame / 30));
|
|
67
|
+
return (
|
|
68
|
+
<AbsoluteFill style={{
|
|
69
|
+
paddingTop: TOP_SAFE + 80, paddingInline: 64, paddingBottom: BOTTOM_SAFE + 80,
|
|
70
|
+
display: "flex", flexDirection: "column", gap: 20, justifyContent: "center",
|
|
71
|
+
}}>
|
|
72
|
+
<span style={{
|
|
73
|
+
fontSize: 84, fontWeight: 700, letterSpacing: -1.5, lineHeight: 1.05,
|
|
74
|
+
opacity: t, transform: `translateY(${(1 - t) * 24}px)`,
|
|
75
|
+
}}>
|
|
76
|
+
__HOOK__
|
|
77
|
+
</span>
|
|
78
|
+
<span style={{
|
|
79
|
+
fontSize: 36, color: palette.fgSoft, opacity: t,
|
|
80
|
+
transform: `translateY(${(1 - t) * 24}px)`,
|
|
81
|
+
}}>
|
|
82
|
+
__SUB__
|
|
83
|
+
</span>
|
|
84
|
+
</AbsoluteFill>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const BodyScene: React.FC = () => (
|
|
89
|
+
<AbsoluteFill style={{ paddingTop: TOP_SAFE + 80, paddingInline: 64, display: "flex", flexDirection: "column", gap: 24 }}>
|
|
90
|
+
<span style={{ fontSize: 64, fontWeight: 700, letterSpacing: -1 }}>Body scene.</span>
|
|
91
|
+
<span style={{ fontSize: 30, color: palette.fgSoft }}>
|
|
92
|
+
Replace with your narrative beat. SegmentOpacity owns scene transitions.
|
|
93
|
+
</span>
|
|
94
|
+
</AbsoluteFill>
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const AnchorScene: React.FC = () => (
|
|
98
|
+
<AbsoluteFill style={{ paddingTop: TOP_SAFE + 100, paddingInline: 64, display: "flex", flexDirection: "column", gap: 24 }}>
|
|
99
|
+
<ForestCard>
|
|
100
|
+
<span style={{ fontSize: 56, fontWeight: 700, letterSpacing: -1 }}>Anchor.</span>
|
|
101
|
+
<p style={{ fontSize: 26, color: palette.fgSoft, marginTop: 12 }}>
|
|
102
|
+
Forest-green card surface — the family's signature embedded UI frame.
|
|
103
|
+
</p>
|
|
104
|
+
</ForestCard>
|
|
105
|
+
</AbsoluteFill>
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const CtaScene: React.FC = () => (
|
|
109
|
+
<AbsoluteFill style={{
|
|
110
|
+
paddingInline: 64, paddingBottom: BOTTOM_SAFE + 80,
|
|
111
|
+
display: "flex", flexDirection: "column", gap: 20, justifyContent: "flex-end",
|
|
112
|
+
}}>
|
|
113
|
+
<span style={{ fontSize: 64, fontWeight: 700, color: palette.claude }}>__CTA__</span>
|
|
114
|
+
</AbsoluteFill>
|
|
115
|
+
);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-scaffolded by ReelStack.
|
|
3
|
+
*
|
|
4
|
+
* Family: Forbidden
|
|
5
|
+
* Preset: __PRESET_NAME__ (source: __SOURCE_REEL__)
|
|
6
|
+
* Duration: __DURATION_FRAMES__ frames @ __FPS__ fps
|
|
7
|
+
*
|
|
8
|
+
* Cream-rose paper background with desaturated forbidden palette stops.
|
|
9
|
+
* Inherits Glass primitives via Forbidden's own components folder, which
|
|
10
|
+
* re-exports them — so every primitive imported here is the right one for
|
|
11
|
+
* this family. Never reference glass/components directly from a Forbidden
|
|
12
|
+
* template; the alias layer is what keeps presets palette-correct.
|
|
13
|
+
*
|
|
14
|
+
* Tinted shadows are the family's secret weapon: every shadow uses the
|
|
15
|
+
* rose-purple stack, never pure black.
|
|
16
|
+
*/
|
|
17
|
+
import React from "react";
|
|
18
|
+
import { AbsoluteFill, Sequence, getInputProps } from "remotion";
|
|
19
|
+
|
|
20
|
+
import { palette } from "@devinilabs/reelstack/families/forbidden";
|
|
21
|
+
import {
|
|
22
|
+
CausticBlobs,
|
|
23
|
+
HairlineGrid,
|
|
24
|
+
GlassCard,
|
|
25
|
+
EyebrowPill,
|
|
26
|
+
StaggeredWords,
|
|
27
|
+
Counter,
|
|
28
|
+
SonarRings,
|
|
29
|
+
ParticleBurst,
|
|
30
|
+
FloatingGlyphs,
|
|
31
|
+
tintedShadow,
|
|
32
|
+
} from "@devinilabs/reelstack/families/forbidden/components";
|
|
33
|
+
import { SafeZones, TOP_SAFE, BOTTOM_SAFE } from "@devinilabs/reelstack/utils/safe-zones";
|
|
34
|
+
|
|
35
|
+
const BEATS = {
|
|
36
|
+
hook: 0,
|
|
37
|
+
reveal: 144,
|
|
38
|
+
body: 386,
|
|
39
|
+
anchor: 961,
|
|
40
|
+
cta: 1500,
|
|
41
|
+
} as const;
|
|
42
|
+
|
|
43
|
+
/** Read inputProps for reduceMotion at module load.
|
|
44
|
+
* Buyers can render a low-motion variant via:
|
|
45
|
+
* npx remotion render <id> out/x.mp4 --props='{"reduceMotion":true}'
|
|
46
|
+
*/
|
|
47
|
+
const _inputProps = (getInputProps() as { reduceMotion?: boolean }) || {};
|
|
48
|
+
const REDUCE_MOTION = _inputProps.reduceMotion === true;
|
|
49
|
+
|
|
50
|
+
export const __REEL_NAME__Reel: React.FC = () => (
|
|
51
|
+
<AbsoluteFill style={{ background: palette.bg, fontFamily: "Geist, system-ui" }}>
|
|
52
|
+
<CausticBlobs reduceMotion={REDUCE_MOTION} />
|
|
53
|
+
<HairlineGrid strokeOpacity={0.04} />
|
|
54
|
+
<FloatingGlyphs count={12} color={palette.inkMuted} reduceMotion={REDUCE_MOTION} />
|
|
55
|
+
|
|
56
|
+
<Sequence from={BEATS.hook}><HookScene /></Sequence>
|
|
57
|
+
<Sequence from={BEATS.reveal}><RevealScene /></Sequence>
|
|
58
|
+
<Sequence from={BEATS.body}><BodyScene /></Sequence>
|
|
59
|
+
<Sequence from={BEATS.anchor}><AnchorScene /></Sequence>
|
|
60
|
+
<Sequence from={BEATS.cta}><CtaScene /></Sequence>
|
|
61
|
+
|
|
62
|
+
<SafeZones visible={false} />
|
|
63
|
+
</AbsoluteFill>
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const HookScene: React.FC = () => (
|
|
67
|
+
<AbsoluteFill style={{
|
|
68
|
+
paddingTop: TOP_SAFE + 80, paddingInline: 64, paddingBottom: BOTTOM_SAFE + 80,
|
|
69
|
+
display: "flex", flexDirection: "column", gap: 24, justifyContent: "center",
|
|
70
|
+
}}>
|
|
71
|
+
<EyebrowPill startFrame={0} tint="violet" reduceMotion={REDUCE_MOTION}>__PRESET_NAME__</EyebrowPill>
|
|
72
|
+
<StaggeredWords startFrame={6} text={"__HOOK__"} fontSize={84} color={palette.ink} reduceMotion={REDUCE_MOTION} />
|
|
73
|
+
<SonarRings cx={540} cy={840} stroke={palette.ultraviolet} count={4} startFrame={0} reduceMotion={REDUCE_MOTION} />
|
|
74
|
+
<ParticleBurst cx={540} cy={840} startFrame={28}
|
|
75
|
+
colors={[palette.ember, palette.crimson, palette.ultraviolet, palette.plasma]} reduceMotion={REDUCE_MOTION} />
|
|
76
|
+
</AbsoluteFill>
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const RevealScene: React.FC = () => (
|
|
80
|
+
<AbsoluteFill style={{ paddingInline: 64 }}>
|
|
81
|
+
<GlassCard variant="strong" style={{ position: "absolute", top: 720, left: 64, right: 64, padding: 36, ...tintedShadow("mid") }}>
|
|
82
|
+
<StaggeredWords startFrame={0} text={"__SUB__"} fontSize={48} color={palette.ink} reduceMotion={REDUCE_MOTION} />
|
|
83
|
+
</GlassCard>
|
|
84
|
+
</AbsoluteFill>
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const BodyScene: React.FC = () => (
|
|
88
|
+
<AbsoluteFill style={{ paddingTop: TOP_SAFE + 100, paddingInline: 64, display: "flex", flexDirection: "column", gap: 24 }}>
|
|
89
|
+
<Counter startFrame={0} toValue={42} fontSize={196} color={palette.crimson} reduceMotion={REDUCE_MOTION} />
|
|
90
|
+
<StaggeredWords startFrame={20} text="Body scene — replace with your forbidden narrative." fontSize={36} color={palette.inkSoft} reduceMotion={REDUCE_MOTION} />
|
|
91
|
+
</AbsoluteFill>
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const AnchorScene: React.FC = () => (
|
|
95
|
+
<AbsoluteFill style={{ paddingInline: 64 }}>
|
|
96
|
+
<GlassCard variant="heavy" style={{ position: "absolute", top: 600, left: 64, right: 64, ...tintedShadow("deep") }}>
|
|
97
|
+
<StaggeredWords startFrame={0} text="Anchor — keep the energy unsettling." fontSize={42} color={palette.ink} reduceMotion={REDUCE_MOTION} />
|
|
98
|
+
</GlassCard>
|
|
99
|
+
<SonarRings cx={540} cy={1100} stroke={palette.plasma} count={3} startFrame={0} reduceMotion={REDUCE_MOTION} />
|
|
100
|
+
</AbsoluteFill>
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const CtaScene: React.FC = () => (
|
|
104
|
+
<AbsoluteFill style={{
|
|
105
|
+
paddingInline: 64, paddingBottom: BOTTOM_SAFE + 80,
|
|
106
|
+
display: "flex", flexDirection: "column", gap: 20, justifyContent: "flex-end",
|
|
107
|
+
}}>
|
|
108
|
+
<EyebrowPill startFrame={0} tint="teal" reduceMotion={REDUCE_MOTION}>CTA</EyebrowPill>
|
|
109
|
+
<StaggeredWords startFrame={4} text={"__CTA__"} fontSize={56} color={palette.crimson} reduceMotion={REDUCE_MOTION} />
|
|
110
|
+
</AbsoluteFill>
|
|
111
|
+
);
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-scaffolded by ReelStack.
|
|
3
|
+
*
|
|
4
|
+
* Family: Glass Iridescent
|
|
5
|
+
* Preset: __PRESET_NAME__ (source: __SOURCE_REEL__)
|
|
6
|
+
* Duration: __DURATION_FRAMES__ frames @ __FPS__ fps
|
|
7
|
+
*
|
|
8
|
+
* Generated rules baked in:
|
|
9
|
+
* - IG safe zones reserved (top 290px / bottom 422px)
|
|
10
|
+
* - Motion floor primer (CausticBlobs + HairlineGrid + LightBeam + FloatingGlyphs)
|
|
11
|
+
* - GSAP-vocabulary easings via reelstack/utils/easing
|
|
12
|
+
* - Audio-lock skeleton (BEATS const ready for /reelstack-beats output)
|
|
13
|
+
*/
|
|
14
|
+
import React from "react";
|
|
15
|
+
import {
|
|
16
|
+
AbsoluteFill,
|
|
17
|
+
Sequence,
|
|
18
|
+
getInputProps,
|
|
19
|
+
useCurrentFrame,
|
|
20
|
+
} from "remotion";
|
|
21
|
+
|
|
22
|
+
import { palette } from "@devinilabs/reelstack/families/glass";
|
|
23
|
+
import {
|
|
24
|
+
CausticBlobs,
|
|
25
|
+
HairlineGrid,
|
|
26
|
+
GlassCard,
|
|
27
|
+
EyebrowPill,
|
|
28
|
+
StaggeredWords,
|
|
29
|
+
Counter,
|
|
30
|
+
SonarRings,
|
|
31
|
+
ParticleBurst,
|
|
32
|
+
LightBeam,
|
|
33
|
+
FloatingGlyphs,
|
|
34
|
+
} from "@devinilabs/reelstack/families/glass/components";
|
|
35
|
+
import { ease } from "@devinilabs/reelstack/utils/easing";
|
|
36
|
+
import { SafeZones, TOP_SAFE, BOTTOM_SAFE } from "@devinilabs/reelstack/utils/safe-zones";
|
|
37
|
+
|
|
38
|
+
// ─── BEATS ────────────────────────────────────────────────────────────────
|
|
39
|
+
// Run /reelstack-beats <vo.wav> to populate this from whisper-cli SRT.
|
|
40
|
+
// Hand-eyeballing beats causes 6+ second drift on 90s reels — don't.
|
|
41
|
+
const BEATS = {
|
|
42
|
+
hook: 0,
|
|
43
|
+
reveal: 90,
|
|
44
|
+
body: 540,
|
|
45
|
+
anchor: 1200,
|
|
46
|
+
cta: 1620,
|
|
47
|
+
} as const;
|
|
48
|
+
|
|
49
|
+
/** Read inputProps for reduceMotion at module load.
|
|
50
|
+
* Buyers can render a low-motion variant via:
|
|
51
|
+
* npx remotion render <id> out/x.mp4 --props='{"reduceMotion":true}'
|
|
52
|
+
*/
|
|
53
|
+
const _inputProps = (getInputProps() as { reduceMotion?: boolean }) || {};
|
|
54
|
+
const REDUCE_MOTION = _inputProps.reduceMotion === true;
|
|
55
|
+
|
|
56
|
+
export const __REEL_NAME__Reel: React.FC = () => {
|
|
57
|
+
return (
|
|
58
|
+
<AbsoluteFill style={{ background: palette.bg, fontFamily: "Geist, system-ui" }}>
|
|
59
|
+
{/* Background motion layers (the family's signature ambient) */}
|
|
60
|
+
<CausticBlobs reduceMotion={REDUCE_MOTION} />
|
|
61
|
+
<HairlineGrid />
|
|
62
|
+
<LightBeam color={palette.iriGold} cx="50%" cy="32%" reduceMotion={REDUCE_MOTION} />
|
|
63
|
+
<FloatingGlyphs count={14} color={palette.inkMuted} reduceMotion={REDUCE_MOTION} />
|
|
64
|
+
|
|
65
|
+
{/* Hero band — between safe zones */}
|
|
66
|
+
<Sequence from={BEATS.hook}>
|
|
67
|
+
<HeroScene />
|
|
68
|
+
</Sequence>
|
|
69
|
+
|
|
70
|
+
{/* Reveal — name drop, preset-specific */}
|
|
71
|
+
<Sequence from={BEATS.reveal}>
|
|
72
|
+
<RevealScene />
|
|
73
|
+
</Sequence>
|
|
74
|
+
|
|
75
|
+
{/* Body — your scenes go here */}
|
|
76
|
+
<Sequence from={BEATS.body}>
|
|
77
|
+
<BodyScene />
|
|
78
|
+
</Sequence>
|
|
79
|
+
|
|
80
|
+
{/* Anchor — second motion floor anchor */}
|
|
81
|
+
<Sequence from={BEATS.anchor}>
|
|
82
|
+
<AnchorScene />
|
|
83
|
+
</Sequence>
|
|
84
|
+
|
|
85
|
+
{/* CTA */}
|
|
86
|
+
<Sequence from={BEATS.cta}>
|
|
87
|
+
<CtaScene />
|
|
88
|
+
</Sequence>
|
|
89
|
+
|
|
90
|
+
{/* Dev-only safe-zone overlay. Set visible={false} for production. */}
|
|
91
|
+
<SafeZones visible={false} />
|
|
92
|
+
</AbsoluteFill>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// ─── Scenes ──────────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
const HeroScene: React.FC = () => (
|
|
99
|
+
<AbsoluteFill
|
|
100
|
+
style={{
|
|
101
|
+
paddingTop: TOP_SAFE + 80,
|
|
102
|
+
paddingBottom: BOTTOM_SAFE + 80,
|
|
103
|
+
paddingInline: 64,
|
|
104
|
+
display: "flex",
|
|
105
|
+
flexDirection: "column",
|
|
106
|
+
gap: 24,
|
|
107
|
+
alignItems: "flex-start",
|
|
108
|
+
justifyContent: "center",
|
|
109
|
+
}}
|
|
110
|
+
>
|
|
111
|
+
<EyebrowPill startFrame={0} tint="violet" reduceMotion={REDUCE_MOTION}>__PRESET_NAME__</EyebrowPill>
|
|
112
|
+
<StaggeredWords startFrame={6} text={"__HOOK__"} fontSize={84} reduceMotion={REDUCE_MOTION} />
|
|
113
|
+
<SonarRings cx={540} cy={840} count={4} startFrame={0} reduceMotion={REDUCE_MOTION} />
|
|
114
|
+
<ParticleBurst cx={540} cy={840} startFrame={28} count={20} reduceMotion={REDUCE_MOTION} />
|
|
115
|
+
</AbsoluteFill>
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const RevealScene: React.FC = () => (
|
|
119
|
+
<AbsoluteFill style={{ paddingInline: 64 }}>
|
|
120
|
+
<GlassCard
|
|
121
|
+
variant="strong"
|
|
122
|
+
style={{
|
|
123
|
+
position: "absolute",
|
|
124
|
+
top: 720,
|
|
125
|
+
left: 64,
|
|
126
|
+
right: 64,
|
|
127
|
+
padding: 36,
|
|
128
|
+
}}
|
|
129
|
+
>
|
|
130
|
+
<StaggeredWords
|
|
131
|
+
startFrame={0}
|
|
132
|
+
text={"__SUB__"}
|
|
133
|
+
fontSize={56}
|
|
134
|
+
color={palette.ink}
|
|
135
|
+
reduceMotion={REDUCE_MOTION}
|
|
136
|
+
/>
|
|
137
|
+
</GlassCard>
|
|
138
|
+
</AbsoluteFill>
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const BodyScene: React.FC = () => (
|
|
142
|
+
<AbsoluteFill
|
|
143
|
+
style={{
|
|
144
|
+
paddingTop: TOP_SAFE + 100,
|
|
145
|
+
paddingInline: 64,
|
|
146
|
+
display: "flex",
|
|
147
|
+
flexDirection: "column",
|
|
148
|
+
gap: 32,
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
<Counter
|
|
152
|
+
startFrame={0}
|
|
153
|
+
durationFrames={48}
|
|
154
|
+
toValue={1000}
|
|
155
|
+
suffix="+"
|
|
156
|
+
fontSize={196}
|
|
157
|
+
reduceMotion={REDUCE_MOTION}
|
|
158
|
+
/>
|
|
159
|
+
<StaggeredWords
|
|
160
|
+
startFrame={24}
|
|
161
|
+
text="Your body copy. Replace with the narrative beat that follows your reveal."
|
|
162
|
+
fontSize={42}
|
|
163
|
+
color={palette.inkSoft}
|
|
164
|
+
reduceMotion={REDUCE_MOTION}
|
|
165
|
+
/>
|
|
166
|
+
</AbsoluteFill>
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const AnchorScene: React.FC = () => {
|
|
170
|
+
const frame = useCurrentFrame();
|
|
171
|
+
// Gentle camera pan via easing
|
|
172
|
+
const t = ease.power3Out(Math.min(1, frame / 60));
|
|
173
|
+
return (
|
|
174
|
+
<AbsoluteFill
|
|
175
|
+
style={{ paddingInline: 64, transform: `translateY(${(1 - t) * 24}px)` }}
|
|
176
|
+
>
|
|
177
|
+
<GlassCard variant="heavy" style={{ position: "absolute", top: 600, left: 64, right: 64 }}>
|
|
178
|
+
<StaggeredWords startFrame={0} text="Anchor scene — energy must not drop here." fontSize={48} reduceMotion={REDUCE_MOTION} />
|
|
179
|
+
</GlassCard>
|
|
180
|
+
<SonarRings cx={540} cy={1100} count={3} period={240} startFrame={0} reduceMotion={REDUCE_MOTION} />
|
|
181
|
+
<ParticleBurst cx={540} cy={1100} startFrame={20} count={14} reduceMotion={REDUCE_MOTION} />
|
|
182
|
+
</AbsoluteFill>
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const CtaScene: React.FC = () => (
|
|
187
|
+
<AbsoluteFill
|
|
188
|
+
style={{
|
|
189
|
+
paddingTop: TOP_SAFE + 200,
|
|
190
|
+
paddingInline: 64,
|
|
191
|
+
paddingBottom: BOTTOM_SAFE + 80,
|
|
192
|
+
display: "flex",
|
|
193
|
+
flexDirection: "column",
|
|
194
|
+
gap: 20,
|
|
195
|
+
justifyContent: "flex-end",
|
|
196
|
+
}}
|
|
197
|
+
>
|
|
198
|
+
<EyebrowPill startFrame={0} tint="teal" reduceMotion={REDUCE_MOTION}>CTA</EyebrowPill>
|
|
199
|
+
<StaggeredWords startFrame={4} text={"__CTA__"} fontSize={64} reduceMotion={REDUCE_MOTION} />
|
|
200
|
+
</AbsoluteFill>
|
|
201
|
+
);
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-scaffolded by ReelStack.
|
|
3
|
+
*
|
|
4
|
+
* Family: Cream Paper
|
|
5
|
+
* Preset: __PRESET_NAME__ (source: __SOURCE_REEL__)
|
|
6
|
+
* Duration: __DURATION_FRAMES__ frames @ __FPS__ fps
|
|
7
|
+
*
|
|
8
|
+
* Editorial print mood. Cream paper background with dark-green card stack.
|
|
9
|
+
* Card springs use damping 20 / stiffness 200 — the family's signature motion.
|
|
10
|
+
*
|
|
11
|
+
* Generated rules baked in:
|
|
12
|
+
* - IG safe zones reserved (top 290px / bottom 422px)
|
|
13
|
+
* - Cream-paper grid + radial fade (CreamGrid)
|
|
14
|
+
* - Hero text via power4Out scale + blur (ScaleBlurText)
|
|
15
|
+
* - Forest-green spring cards (CardSpring / GreenAccentCard)
|
|
16
|
+
* - Audio-lock skeleton (BEATS const ready for /reelstack-beats output)
|
|
17
|
+
*/
|
|
18
|
+
import React from "react";
|
|
19
|
+
import { AbsoluteFill, Sequence, getInputProps } from "remotion";
|
|
20
|
+
|
|
21
|
+
import { palette } from "@devinilabs/reelstack/families/paper";
|
|
22
|
+
import {
|
|
23
|
+
CardSpring,
|
|
24
|
+
ScaleBlurText,
|
|
25
|
+
CreamGrid,
|
|
26
|
+
} from "@devinilabs/reelstack/families/paper/components";
|
|
27
|
+
import { SafeZones, TOP_SAFE, BOTTOM_SAFE } from "@devinilabs/reelstack/utils/safe-zones";
|
|
28
|
+
|
|
29
|
+
// ─── BEATS ────────────────────────────────────────────────────────────────
|
|
30
|
+
// Run /reelstack-beats <vo.wav> to populate this from whisper-cli SRT.
|
|
31
|
+
// Hand-eyeballing beats causes 6+ second drift on 90s reels — don't.
|
|
32
|
+
const BEATS = {
|
|
33
|
+
hook: 0,
|
|
34
|
+
body: 240,
|
|
35
|
+
anchor: 900,
|
|
36
|
+
cta: 1500,
|
|
37
|
+
} as const;
|
|
38
|
+
|
|
39
|
+
/** Read inputProps for reduceMotion at module load.
|
|
40
|
+
* Buyers can render a low-motion variant via:
|
|
41
|
+
* npx remotion render <id> out/x.mp4 --props='{"reduceMotion":true}'
|
|
42
|
+
*/
|
|
43
|
+
const _inputProps = (getInputProps() as { reduceMotion?: boolean }) || {};
|
|
44
|
+
const REDUCE_MOTION = _inputProps.reduceMotion === true;
|
|
45
|
+
|
|
46
|
+
export const __REEL_NAME__Reel: React.FC = () => (
|
|
47
|
+
<AbsoluteFill style={{ background: palette.bg, fontFamily: "Geist, system-ui" }}>
|
|
48
|
+
{/* Background paper texture */}
|
|
49
|
+
<CreamGrid />
|
|
50
|
+
|
|
51
|
+
{/* Hook — hero ScaleBlurText */}
|
|
52
|
+
<Sequence from={BEATS.hook}>
|
|
53
|
+
<HookScene />
|
|
54
|
+
</Sequence>
|
|
55
|
+
|
|
56
|
+
{/* Body — narrative beat in a forest-green card */}
|
|
57
|
+
<Sequence from={BEATS.body}>
|
|
58
|
+
<BodyScene />
|
|
59
|
+
</Sequence>
|
|
60
|
+
|
|
61
|
+
{/* Anchor — second motion floor, two stacked cards */}
|
|
62
|
+
<Sequence from={BEATS.anchor}>
|
|
63
|
+
<AnchorScene />
|
|
64
|
+
</Sequence>
|
|
65
|
+
|
|
66
|
+
{/* CTA */}
|
|
67
|
+
<Sequence from={BEATS.cta}>
|
|
68
|
+
<CtaScene />
|
|
69
|
+
</Sequence>
|
|
70
|
+
|
|
71
|
+
{/* Dev-only safe-zone overlay. Set visible={false} for production. */}
|
|
72
|
+
<SafeZones visible={false} />
|
|
73
|
+
</AbsoluteFill>
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// ─── Scenes ──────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
const HookScene: React.FC = () => (
|
|
79
|
+
<AbsoluteFill
|
|
80
|
+
style={{
|
|
81
|
+
paddingTop: TOP_SAFE + 80,
|
|
82
|
+
paddingBottom: BOTTOM_SAFE + 80,
|
|
83
|
+
paddingInline: 64,
|
|
84
|
+
display: "flex",
|
|
85
|
+
flexDirection: "column",
|
|
86
|
+
gap: 24,
|
|
87
|
+
justifyContent: "center",
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
<ScaleBlurText startFrame={0} text="__HOOK__" size={88} reduceMotion={REDUCE_MOTION} />
|
|
91
|
+
<ScaleBlurText startFrame={18} text="__SUB__" size={48} color={palette.body} reduceMotion={REDUCE_MOTION} />
|
|
92
|
+
</AbsoluteFill>
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const BodyScene: React.FC = () => (
|
|
96
|
+
<AbsoluteFill style={{ paddingTop: TOP_SAFE + 80, paddingInline: 64 }}>
|
|
97
|
+
<CardSpring startFrame={0} style={{ position: "absolute", top: 460, left: 64, right: 64 }} reduceMotion={REDUCE_MOTION}>
|
|
98
|
+
<span style={{ fontSize: 48, fontWeight: 700, letterSpacing: -1 }}>Body card.</span>
|
|
99
|
+
<p style={{ fontSize: 26, color: palette.cardForestBody, marginTop: 12 }}>
|
|
100
|
+
Replace with your narrative beat.
|
|
101
|
+
</p>
|
|
102
|
+
</CardSpring>
|
|
103
|
+
</AbsoluteFill>
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const AnchorScene: React.FC = () => (
|
|
107
|
+
<AbsoluteFill style={{ paddingInline: 64 }}>
|
|
108
|
+
<CardSpring startFrame={0} style={{ position: "absolute", top: 760, left: 64, right: 64 }} reduceMotion={REDUCE_MOTION}>
|
|
109
|
+
<span style={{ fontSize: 56, fontWeight: 700, letterSpacing: -1 }}>Anchor.</span>
|
|
110
|
+
<p style={{ fontSize: 26, color: palette.cardForestBody, marginTop: 12 }}>
|
|
111
|
+
Second motion floor — keep energy peaking.
|
|
112
|
+
</p>
|
|
113
|
+
</CardSpring>
|
|
114
|
+
<CardSpring startFrame={12} style={{ position: "absolute", top: 1100, left: 64, right: 64 }} reduceMotion={REDUCE_MOTION}>
|
|
115
|
+
<span style={{ fontSize: 36, fontWeight: 600, color: palette.terminalGreen }}>$ install</span>
|
|
116
|
+
</CardSpring>
|
|
117
|
+
</AbsoluteFill>
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const CtaScene: React.FC = () => (
|
|
121
|
+
<AbsoluteFill
|
|
122
|
+
style={{
|
|
123
|
+
paddingInline: 64,
|
|
124
|
+
paddingBottom: BOTTOM_SAFE + 80,
|
|
125
|
+
display: "flex",
|
|
126
|
+
flexDirection: "column",
|
|
127
|
+
gap: 24,
|
|
128
|
+
justifyContent: "flex-end",
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
<ScaleBlurText startFrame={0} text="__CTA__" size={68} color={palette.accent} reduceMotion={REDUCE_MOTION} />
|
|
132
|
+
</AbsoluteFill>
|
|
133
|
+
);
|