@vibecuting/video-project-core 0.1.27 → 0.1.29
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/package.json +2 -2
- package/src/base/BaseRemotionScene.tsx +4 -3
- package/src/core/index.test.ts +40 -0
- package/src/core/scene/2dscene/default-2d-scene-Horizontal.stories.tsx +1 -1
- package/src/core/scene/2dscene/default-2d-scene.stories.tsx +1 -1
- package/src/core/scene/3dscene/default-3d-scene-Horizontal.stories.tsx +1 -1
- package/src/core/scene/3dscene/default-3d-scene.stories.tsx +1 -1
- package/src/core/scene/index.ts +3 -0
- package/src/core/scene/photo/3dvr-photo/default-3dvr-photo-scene-Horizontal.stories.tsx +46 -0
- package/src/core/scene/photo/3dvr-photo/default-3dvr-photo-scene-Horizontal.tsx +20 -0
- package/src/core/scene/photo/3dvr-photo/default-3dvr-photo-scene-Landscape.tsx +20 -0
- package/src/core/scene/photo/3dvr-photo/default-3dvr-photo-scene.stories.tsx +46 -0
- package/src/core/scene/photo/3dvr-photo/default-3dvr-photo-scene.tsx +255 -0
- package/src/core/scene/photo/3dvr-photo/index.ts +3 -0
- package/src/core/scene/photo/default-live-photo-scene-Horizontal.stories.tsx +33 -0
- package/src/core/scene/photo/default-live-photo-scene-Horizontal.tsx +20 -0
- package/src/core/scene/photo/default-live-photo-scene-Landscape.tsx +20 -0
- package/src/core/scene/photo/default-live-photo-scene.stories.tsx +33 -0
- package/src/core/scene/photo/default-live-photo-scene.tsx +432 -0
- package/src/core/scene/photo/default-photo-strip-scene-Horizontal.tsx +20 -0
- package/src/core/scene/photo/default-photo-strip-scene-Landscape.tsx +20 -0
- package/src/core/scene/photo/default-photo-strip-scene.stories.tsx +38 -0
- package/src/core/scene/photo/default-photo-strip-scene.tsx +19 -0
- package/src/core/scene/photo/default-photo-wall-scene-Horizontal.tsx +20 -0
- package/src/core/scene/photo/default-photo-wall-scene-Landscape.tsx +20 -0
- package/src/core/scene/photo/default-photo-wall-scene.stories.tsx +38 -0
- package/src/core/scene/photo/default-photo-wall-scene.tsx +19 -0
- package/src/core/scene/photo/effects-photo/default-effects-photo-scene-Horizontal.stories.tsx +44 -0
- package/src/core/scene/photo/effects-photo/default-effects-photo-scene-Horizontal.tsx +20 -0
- package/src/core/scene/photo/effects-photo/default-effects-photo-scene-Landscape.tsx +20 -0
- package/src/core/scene/photo/effects-photo/default-effects-photo-scene.stories.tsx +44 -0
- package/src/core/scene/photo/effects-photo/default-effects-photo-scene.tsx +352 -0
- package/src/core/scene/photo/effects-photo/index.ts +3 -0
- package/src/core/scene/photo/index.ts +25 -0
- package/src/core/scene/photo/photo-storybook.test.tsx +112 -0
- package/src/core/scene/slide/default-slide-scene-Horizontal.animated.stories.tsx +1 -1
- package/src/core/scene/slide/default-slide-scene-Horizontal.stories.tsx +1 -1
- package/src/core/scene/slide/default-slide-scene.animated.stories.tsx +1 -1
- package/src/core/scene/slide/default-slide-scene.stories.tsx +1 -1
- package/src/core/scene/video/default-video-scene-Horizontal.stories.tsx +26 -0
- package/src/core/scene/video/default-video-scene-Horizontal.tsx +20 -0
- package/src/core/scene/video/default-video-scene-Landscape.tsx +20 -0
- package/src/core/scene/video/default-video-scene.stories.tsx +26 -0
- package/src/core/scene/video/default-video-scene.tsx +283 -0
- package/src/core/scene/video/default-video-split-scene-Horizontal.tsx +20 -0
- package/src/core/scene/video/default-video-split-scene-Landscape.tsx +20 -0
- package/src/core/scene/video/default-video-split-scene.stories.tsx +41 -0
- package/src/core/scene/video/default-video-split-scene.tsx +19 -0
- package/src/core/scene/video/default-video-wall-scene-Horizontal.tsx +20 -0
- package/src/core/scene/video/default-video-wall-scene-Landscape.tsx +20 -0
- package/src/core/scene/video/default-video-wall-scene.stories.tsx +40 -0
- package/src/core/scene/video/default-video-wall-scene.tsx +19 -0
- package/src/core/scene/video/index.ts +9 -0
- package/src/storybook/remotion-preview.tsx +112 -6
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react'
|
|
2
|
+
|
|
3
|
+
import { VideoComponent, type ScenePluginMetadata } from '@vibecuting/component-project-helper'
|
|
4
|
+
|
|
5
|
+
import { DefaultEffectsPhotoScene, componentMetadata as baseComponentMetadata } from './default-effects-photo-scene'
|
|
6
|
+
|
|
7
|
+
export const componentMetadata: ScenePluginMetadata = {
|
|
8
|
+
...baseComponentMetadata,
|
|
9
|
+
name: 'DefaultEffectsPhotoSceneHorizontal',
|
|
10
|
+
description: 'Photo effects scene for 9:16 portrait previews with a vertical layout',
|
|
11
|
+
sourceFile: 'src/core/scene/photo/effects-photo/default-effects-photo-scene-Horizontal.tsx',
|
|
12
|
+
aspectRatio: '9:16',
|
|
13
|
+
tags: Array.from(new Set([...(baseComponentMetadata.tags ?? []), 'horizontal'])),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const DefaultEffectsPhotoSceneHorizontal = VideoComponent(componentMetadata)(function DefaultEffectsPhotoSceneHorizontal(
|
|
17
|
+
props: ComponentProps<typeof DefaultEffectsPhotoScene>,
|
|
18
|
+
) {
|
|
19
|
+
return <DefaultEffectsPhotoScene {...props} />
|
|
20
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react'
|
|
2
|
+
|
|
3
|
+
import { VideoComponent, type ScenePluginMetadata } from '@vibecuting/component-project-helper'
|
|
4
|
+
|
|
5
|
+
import { DefaultEffectsPhotoScene, componentMetadata as baseComponentMetadata } from './default-effects-photo-scene'
|
|
6
|
+
|
|
7
|
+
export const componentMetadata: ScenePluginMetadata = {
|
|
8
|
+
...baseComponentMetadata,
|
|
9
|
+
name: 'DefaultEffectsPhotoSceneLandscape',
|
|
10
|
+
description: 'Photo effects scene for 16:9 landscape previews with a wide cinematic layout',
|
|
11
|
+
sourceFile: 'src/core/scene/photo/effects-photo/default-effects-photo-scene-Landscape.tsx',
|
|
12
|
+
aspectRatio: '16:9',
|
|
13
|
+
tags: Array.from(new Set([...(baseComponentMetadata.tags ?? []), 'landscape'])),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const DefaultEffectsPhotoSceneLandscape = VideoComponent(componentMetadata)(function DefaultEffectsPhotoSceneLandscape(
|
|
17
|
+
props: ComponentProps<typeof DefaultEffectsPhotoScene>,
|
|
18
|
+
) {
|
|
19
|
+
return <DefaultEffectsPhotoScene {...props} />
|
|
20
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { DefaultEffectsPhotoSceneLandscape } from './default-effects-photo-scene-Landscape'
|
|
2
|
+
|
|
3
|
+
const photoUrl = 'https://heyaai-statics-1342239856.cos.ap-guangzhou.myqcloud.com/tmp/premium_photo-1779404552087-0f905162376f-121.jpeg'
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'core/scene/photo/effects-photo/DefaultEffectsPhotoSceneLandscape',
|
|
7
|
+
component: DefaultEffectsPhotoSceneLandscape,
|
|
8
|
+
args: {
|
|
9
|
+
title: 'Golden Echo',
|
|
10
|
+
subtitle: 'Layered photo grading with glow, grain, warmth and a cinematic vignette.',
|
|
11
|
+
badge: 'Effects Photo',
|
|
12
|
+
imageSrc: photoUrl,
|
|
13
|
+
effects: [
|
|
14
|
+
{ type: 'glow', intensity: 0.55 },
|
|
15
|
+
{ type: 'vignette', intensity: 0.65 },
|
|
16
|
+
{ type: 'grain', intensity: 0.12 },
|
|
17
|
+
{ type: 'warmth', intensity: 0.18, hue: '#f59e0b' },
|
|
18
|
+
],
|
|
19
|
+
accent: '#020617',
|
|
20
|
+
secondaryAccent: '#1d4ed8',
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default meta
|
|
25
|
+
|
|
26
|
+
export const Default = {
|
|
27
|
+
parameters: {
|
|
28
|
+
scenePreviewAspectRatio: '16:9',
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const Animated = {
|
|
33
|
+
parameters: {
|
|
34
|
+
scenePreviewAspectRatio: '16:9',
|
|
35
|
+
scenePreviewMode: 'animation',
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const Both = {
|
|
40
|
+
parameters: {
|
|
41
|
+
scenePreviewAspectRatio: '16:9',
|
|
42
|
+
scenePreviewMode: 'both',
|
|
43
|
+
},
|
|
44
|
+
}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
import { VideoComponent, defineScenePluginMetadata } from '@vibecuting/component-project-helper'
|
|
6
|
+
import { Easing, Img, interpolate, spring, useCurrentFrame, useVideoConfig } from 'remotion'
|
|
7
|
+
|
|
8
|
+
import { BaseRemotionScene, type BaseRemotionSceneProps, useRemotionSceneRuntime, useSceneConfig } from '../../../../base'
|
|
9
|
+
import { defineSceneComponent } from '../../../../plugins/scene-component'
|
|
10
|
+
|
|
11
|
+
export type PhotoEffectType = 'vignette' | 'grain' | 'glow' | 'warmth' | 'soft-focus'
|
|
12
|
+
|
|
13
|
+
export interface PhotoEffect {
|
|
14
|
+
type: PhotoEffectType
|
|
15
|
+
intensity?: number
|
|
16
|
+
hue?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface DefaultEffectsPhotoSceneProps extends BaseRemotionSceneProps {
|
|
20
|
+
sceneId?: string
|
|
21
|
+
sceneName?: string
|
|
22
|
+
title?: string
|
|
23
|
+
subtitle?: string
|
|
24
|
+
badge?: string
|
|
25
|
+
imageSrc: string
|
|
26
|
+
effects?: PhotoEffect[]
|
|
27
|
+
accent?: string
|
|
28
|
+
secondaryAccent?: string
|
|
29
|
+
textColor?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const componentMetadata = defineScenePluginMetadata({
|
|
33
|
+
resourceKind: 'scene',
|
|
34
|
+
name: 'DefaultEffectsPhotoScene',
|
|
35
|
+
description: 'Photo scene with layered Remotion effects and cinematic grading',
|
|
36
|
+
sourceFile: 'src/core/scene/photo/effects-photo/default-effects-photo-scene.tsx',
|
|
37
|
+
pluginKey: 'core.photo.effects-photo',
|
|
38
|
+
tags: ['scene', 'photo', 'effects', 'image', 'cinematic'],
|
|
39
|
+
aspectRatio: '16:9',
|
|
40
|
+
sceneType: 'scene',
|
|
41
|
+
sceneFamily: 'custom',
|
|
42
|
+
rootLayout: 'absolute-fill',
|
|
43
|
+
propsTypeName: 'DefaultEffectsPhotoSceneProps',
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
function matchesEffect(effects: PhotoEffect[] | undefined, type: PhotoEffectType) {
|
|
47
|
+
return effects?.some((effect) => effect.type === type) ?? false
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function effectIntensity(effects: PhotoEffect[] | undefined, type: PhotoEffectType, fallback: number) {
|
|
51
|
+
return effects?.find((effect) => effect.type === type)?.intensity ?? fallback
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const DefaultEffectsPhotoScene = VideoComponent(componentMetadata)(
|
|
55
|
+
defineSceneComponent({
|
|
56
|
+
family: 'custom',
|
|
57
|
+
propsSchema: z.object({
|
|
58
|
+
sceneId: z.string().optional(),
|
|
59
|
+
sceneName: z.string().optional(),
|
|
60
|
+
title: z.string().optional(),
|
|
61
|
+
subtitle: z.string().optional(),
|
|
62
|
+
badge: z.string().optional(),
|
|
63
|
+
imageSrc: z.string(),
|
|
64
|
+
effects: z
|
|
65
|
+
.array(
|
|
66
|
+
z.object({
|
|
67
|
+
type: z.enum(['vignette', 'grain', 'glow', 'warmth', 'soft-focus']),
|
|
68
|
+
intensity: z.number().min(0).max(1).optional(),
|
|
69
|
+
hue: z.string().optional(),
|
|
70
|
+
}),
|
|
71
|
+
)
|
|
72
|
+
.optional(),
|
|
73
|
+
accent: z.string().optional(),
|
|
74
|
+
secondaryAccent: z.string().optional(),
|
|
75
|
+
textColor: z.string().optional(),
|
|
76
|
+
}),
|
|
77
|
+
component: function DefaultEffectsPhotoScene({
|
|
78
|
+
id,
|
|
79
|
+
name,
|
|
80
|
+
sceneId,
|
|
81
|
+
sceneName,
|
|
82
|
+
title,
|
|
83
|
+
subtitle,
|
|
84
|
+
badge,
|
|
85
|
+
imageSrc,
|
|
86
|
+
effects,
|
|
87
|
+
accent,
|
|
88
|
+
secondaryAccent,
|
|
89
|
+
textColor,
|
|
90
|
+
}: DefaultEffectsPhotoSceneProps) {
|
|
91
|
+
const runtime = useRemotionSceneRuntime()
|
|
92
|
+
const sceneConfig = useSceneConfig()
|
|
93
|
+
const frame = useCurrentFrame()
|
|
94
|
+
const { fps } = useVideoConfig()
|
|
95
|
+
const portrait = runtime.height > runtime.width
|
|
96
|
+
const animated = sceneConfig.sceneType === 'animation'
|
|
97
|
+
const animationProgress =
|
|
98
|
+
sceneConfig.sceneType === 'animation'
|
|
99
|
+
? spring({
|
|
100
|
+
frame,
|
|
101
|
+
fps,
|
|
102
|
+
config: { damping: 22, mass: 0.8, stiffness: 130 },
|
|
103
|
+
})
|
|
104
|
+
: 1
|
|
105
|
+
const reveal = sceneConfig.sceneType === 'animation' ? spring({ frame, fps, delay: 6, config: { damping: 20, mass: 0.7, stiffness: 150 } }) : 1
|
|
106
|
+
const focusDrift = sceneConfig.sceneType === 'animation' ? Math.sin((frame / fps) * Math.PI * 0.75) : 0
|
|
107
|
+
const scale = 1.01 + (1 - animationProgress) * 0.06 + focusDrift * 0.008
|
|
108
|
+
const translateX = (1 - animationProgress) * (portrait ? -8 : -12) + focusDrift * (portrait ? 8 : 12)
|
|
109
|
+
const translateY = (1 - animationProgress) * 10 + focusDrift * 16
|
|
110
|
+
const titleSize = Math.round(runtime.height * (portrait ? 0.034 : 0.04))
|
|
111
|
+
const subtitleSize = Math.round(runtime.height * 0.017)
|
|
112
|
+
const framePaddingX = Math.round(runtime.width * (portrait ? 0.015 : 0.014))
|
|
113
|
+
const framePaddingY = Math.round(runtime.height * (portrait ? 0.014 : 0.012))
|
|
114
|
+
const frameLeft = framePaddingX
|
|
115
|
+
const frameTop = framePaddingY
|
|
116
|
+
const frameWidth = Math.max(1, runtime.width - framePaddingX * 2)
|
|
117
|
+
const frameHeight = Math.max(1, runtime.height - framePaddingY * 2)
|
|
118
|
+
const imageFilter = animated ? 'brightness(1.08) contrast(1.04) saturate(1.08)' : 'brightness(1.9) contrast(1.18) saturate(1.22)'
|
|
119
|
+
const vignette = matchesEffect(effects, 'vignette')
|
|
120
|
+
const grain = matchesEffect(effects, 'grain')
|
|
121
|
+
const glow = matchesEffect(effects, 'glow')
|
|
122
|
+
const warmth = matchesEffect(effects, 'warmth')
|
|
123
|
+
const softFocus = matchesEffect(effects, 'soft-focus')
|
|
124
|
+
const vignetteOpacity = effectIntensity(effects, 'vignette', 0.52) * (0.65 + reveal * 0.35)
|
|
125
|
+
const glowOpacity = effectIntensity(effects, 'glow', 0.45) * (0.7 + reveal * 0.3)
|
|
126
|
+
const grainOpacity = effectIntensity(effects, 'grain', 0.12)
|
|
127
|
+
const warmthHue = effects?.find((effect) => effect.type === 'warmth')?.hue ?? accent ?? '#f97316'
|
|
128
|
+
const focusOverlay = interpolate(reveal, [0, 1], [0, 1], {
|
|
129
|
+
easing: Easing.inOut(Easing.quad),
|
|
130
|
+
extrapolateLeft: 'clamp',
|
|
131
|
+
extrapolateRight: 'clamp',
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
const effectLayers = useMemo(
|
|
135
|
+
() => (
|
|
136
|
+
<>
|
|
137
|
+
{glow ? (
|
|
138
|
+
<div
|
|
139
|
+
aria-hidden="true"
|
|
140
|
+
style={{
|
|
141
|
+
position: 'absolute',
|
|
142
|
+
inset: '4%',
|
|
143
|
+
opacity: glowOpacity * 0.55,
|
|
144
|
+
filter: 'blur(20px) saturate(1.18) brightness(0.98)',
|
|
145
|
+
transform: `scale(${1.03 + (1 - animationProgress) * 0.02})`,
|
|
146
|
+
pointerEvents: 'none',
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
<Img
|
|
150
|
+
src={imageSrc}
|
|
151
|
+
alt={title ?? 'Effects photo'}
|
|
152
|
+
style={{
|
|
153
|
+
width: '100%',
|
|
154
|
+
height: '100%',
|
|
155
|
+
objectFit: 'cover',
|
|
156
|
+
}}
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
) : null}
|
|
160
|
+
{softFocus ? (
|
|
161
|
+
<div
|
|
162
|
+
aria-hidden="true"
|
|
163
|
+
style={{
|
|
164
|
+
position: 'absolute',
|
|
165
|
+
inset: 0,
|
|
166
|
+
opacity: 0.18 + focusOverlay * 0.08,
|
|
167
|
+
filter: 'blur(10px) saturate(1.04)',
|
|
168
|
+
transform: `scale(${1.02 + focusOverlay * 0.01})`,
|
|
169
|
+
pointerEvents: 'none',
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
<Img
|
|
173
|
+
src={imageSrc}
|
|
174
|
+
alt={title ?? 'Effects photo'}
|
|
175
|
+
style={{
|
|
176
|
+
width: '100%',
|
|
177
|
+
height: '100%',
|
|
178
|
+
objectFit: 'cover',
|
|
179
|
+
}}
|
|
180
|
+
/>
|
|
181
|
+
</div>
|
|
182
|
+
) : null}
|
|
183
|
+
{vignette ? (
|
|
184
|
+
<div
|
|
185
|
+
aria-hidden="true"
|
|
186
|
+
style={{
|
|
187
|
+
position: 'absolute',
|
|
188
|
+
inset: 0,
|
|
189
|
+
background:
|
|
190
|
+
'radial-gradient(circle at center, rgba(255,255,255,0.04) 0%, rgba(255,255,255,0.01) 28%, rgba(2,6,23,0.14) 56%, rgba(2,6,23,0.46) 100%)',
|
|
191
|
+
opacity: vignetteOpacity * 0.72,
|
|
192
|
+
pointerEvents: 'none',
|
|
193
|
+
}}
|
|
194
|
+
/>
|
|
195
|
+
) : null}
|
|
196
|
+
{warmth ? (
|
|
197
|
+
<div
|
|
198
|
+
aria-hidden="true"
|
|
199
|
+
style={{
|
|
200
|
+
position: 'absolute',
|
|
201
|
+
inset: 0,
|
|
202
|
+
background: `linear-gradient(135deg, ${warmthHue} 0%, rgba(255,255,255,0.02) 36%, ${secondaryAccent ?? 'rgba(59,130,246,0.14)'} 100%)`,
|
|
203
|
+
mixBlendMode: 'screen',
|
|
204
|
+
opacity: 0.08 + focusOverlay * 0.08,
|
|
205
|
+
pointerEvents: 'none',
|
|
206
|
+
}}
|
|
207
|
+
/>
|
|
208
|
+
) : null}
|
|
209
|
+
{grain ? (
|
|
210
|
+
<div
|
|
211
|
+
aria-hidden="true"
|
|
212
|
+
style={{
|
|
213
|
+
position: 'absolute',
|
|
214
|
+
inset: 0,
|
|
215
|
+
opacity: grainOpacity,
|
|
216
|
+
backgroundImage:
|
|
217
|
+
'radial-gradient(rgba(255,255,255,0.35) 0.7px, transparent 0.7px), radial-gradient(rgba(0,0,0,0.3) 0.7px, transparent 0.7px)',
|
|
218
|
+
backgroundSize: '4px 4px, 4px 4px',
|
|
219
|
+
backgroundPosition: '0 0, 2px 2px',
|
|
220
|
+
mixBlendMode: 'soft-light',
|
|
221
|
+
pointerEvents: 'none',
|
|
222
|
+
}}
|
|
223
|
+
/>
|
|
224
|
+
) : null}
|
|
225
|
+
</>
|
|
226
|
+
),
|
|
227
|
+
[
|
|
228
|
+
animationProgress,
|
|
229
|
+
focusOverlay,
|
|
230
|
+
grain,
|
|
231
|
+
grainOpacity,
|
|
232
|
+
glow,
|
|
233
|
+
glowOpacity,
|
|
234
|
+
imageSrc,
|
|
235
|
+
secondaryAccent,
|
|
236
|
+
softFocus,
|
|
237
|
+
title,
|
|
238
|
+
vignette,
|
|
239
|
+
vignetteOpacity,
|
|
240
|
+
warmth,
|
|
241
|
+
warmthHue,
|
|
242
|
+
],
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<BaseRemotionScene
|
|
247
|
+
id={id ?? sceneId}
|
|
248
|
+
name={name ?? sceneName ?? title}
|
|
249
|
+
style={{
|
|
250
|
+
background:
|
|
251
|
+
portrait
|
|
252
|
+
? `linear-gradient(180deg, ${accent ?? '#020617'} 0%, #000000 100%)`
|
|
253
|
+
: `radial-gradient(circle at 50% 18%, ${secondaryAccent ?? 'rgba(255,255,255,0.08)'} 0%, ${accent ?? '#020617'} 52%, #000000 100%)`,
|
|
254
|
+
color: textColor ?? '#f8fafc',
|
|
255
|
+
padding: Math.round(runtime.height * (portrait ? 0.02 : 0.03)),
|
|
256
|
+
}}
|
|
257
|
+
>
|
|
258
|
+
<div
|
|
259
|
+
style={{
|
|
260
|
+
position: 'absolute',
|
|
261
|
+
inset: 0,
|
|
262
|
+
overflow: 'hidden',
|
|
263
|
+
border: '1px solid rgba(255,255,255,0.1)',
|
|
264
|
+
background: 'rgba(2,6,23,0.82)',
|
|
265
|
+
isolation: 'isolate',
|
|
266
|
+
}}
|
|
267
|
+
>
|
|
268
|
+
<div
|
|
269
|
+
style={{
|
|
270
|
+
position: 'absolute',
|
|
271
|
+
top: frameTop,
|
|
272
|
+
left: frameLeft,
|
|
273
|
+
width: frameWidth,
|
|
274
|
+
height: frameHeight,
|
|
275
|
+
border: animated ? '1px solid rgba(255,255,255,0.1)' : '1px solid rgba(255,255,255,0.16)',
|
|
276
|
+
boxShadow: animated ? '0 18px 56px rgba(0,0,0,0.24)' : '0 12px 32px rgba(0,0,0,0.14)',
|
|
277
|
+
backgroundColor: animated ? 'rgba(2,6,23,0.14)' : 'rgba(248,250,252,0.06)',
|
|
278
|
+
backgroundImage: `url(${imageSrc})`,
|
|
279
|
+
backgroundSize: 'cover',
|
|
280
|
+
backgroundPosition: `${50}% ${portrait ? 44 : 52}%`,
|
|
281
|
+
backgroundRepeat: 'no-repeat',
|
|
282
|
+
overflow: 'hidden',
|
|
283
|
+
filter: imageFilter,
|
|
284
|
+
}}
|
|
285
|
+
>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
<div
|
|
289
|
+
style={{
|
|
290
|
+
position: 'absolute',
|
|
291
|
+
top: frameTop,
|
|
292
|
+
left: frameLeft,
|
|
293
|
+
width: frameWidth,
|
|
294
|
+
height: frameHeight,
|
|
295
|
+
}}
|
|
296
|
+
>
|
|
297
|
+
{effectLayers}
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
<div
|
|
301
|
+
aria-hidden="true"
|
|
302
|
+
style={{
|
|
303
|
+
position: 'absolute',
|
|
304
|
+
inset: 0,
|
|
305
|
+
background:
|
|
306
|
+
'linear-gradient(180deg, rgba(2,6,23,0.04) 0%, rgba(2,6,23,0.12) 38%, rgba(2,6,23,0.52) 100%)',
|
|
307
|
+
pointerEvents: 'none',
|
|
308
|
+
}}
|
|
309
|
+
/>
|
|
310
|
+
|
|
311
|
+
<div
|
|
312
|
+
style={{
|
|
313
|
+
position: 'absolute',
|
|
314
|
+
inset: 0,
|
|
315
|
+
display: 'grid',
|
|
316
|
+
alignContent: 'space-between',
|
|
317
|
+
padding: Math.round(runtime.height * (portrait ? 0.02 : 0.03)),
|
|
318
|
+
zIndex: 2,
|
|
319
|
+
pointerEvents: 'none',
|
|
320
|
+
}}
|
|
321
|
+
>
|
|
322
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start', gap: 12 }}>
|
|
323
|
+
<div
|
|
324
|
+
style={{
|
|
325
|
+
display: 'inline-flex',
|
|
326
|
+
alignItems: 'center',
|
|
327
|
+
gap: 8,
|
|
328
|
+
border: '1px solid rgba(255,255,255,0.18)',
|
|
329
|
+
background: 'rgba(2,6,23,0.6)',
|
|
330
|
+
color: '#f8fafc',
|
|
331
|
+
padding: '8px 12px',
|
|
332
|
+
letterSpacing: '0.22em',
|
|
333
|
+
textTransform: 'uppercase',
|
|
334
|
+
fontSize: 11,
|
|
335
|
+
lineHeight: 1,
|
|
336
|
+
}}
|
|
337
|
+
>
|
|
338
|
+
{badge ?? 'Effects Photo'}
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
|
|
342
|
+
<div style={{ display: 'grid', gap: 8, maxWidth: portrait ? '86%' : '58%' }}>
|
|
343
|
+
{title ? <div style={{ fontSize: titleSize, fontWeight: 800, letterSpacing: '-0.06em', lineHeight: 0.94 }}>{title}</div> : null}
|
|
344
|
+
{subtitle ? <div style={{ fontSize: subtitleSize, lineHeight: 1.45, opacity: 0.84 }}>{subtitle}</div> : null}
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
</BaseRemotionScene>
|
|
349
|
+
)
|
|
350
|
+
},
|
|
351
|
+
}),
|
|
352
|
+
)
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { DefaultEffectsPhotoScene, componentMetadata as DefaultEffectsPhotoSceneMetadata } from './default-effects-photo-scene'
|
|
2
|
+
export { DefaultEffectsPhotoSceneLandscape, componentMetadata as DefaultEffectsPhotoSceneLandscapeMetadata } from './default-effects-photo-scene-Landscape'
|
|
3
|
+
export { DefaultEffectsPhotoSceneHorizontal, componentMetadata as DefaultEffectsPhotoSceneHorizontalMetadata } from './default-effects-photo-scene-Horizontal'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export { DefaultLivePhotoScene, componentMetadata as DefaultLivePhotoSceneMetadata } from './default-live-photo-scene'
|
|
2
|
+
export { DefaultLivePhotoSceneLandscape, componentMetadata as DefaultLivePhotoSceneLandscapeMetadata } from './default-live-photo-scene-Landscape'
|
|
3
|
+
export { DefaultLivePhotoSceneHorizontal, componentMetadata as DefaultLivePhotoSceneHorizontalMetadata } from './default-live-photo-scene-Horizontal'
|
|
4
|
+
export { DefaultPhotoWallScene, componentMetadata as DefaultPhotoWallSceneMetadata } from './default-photo-wall-scene'
|
|
5
|
+
export { DefaultPhotoWallSceneLandscape, componentMetadata as DefaultPhotoWallSceneLandscapeMetadata } from './default-photo-wall-scene-Landscape'
|
|
6
|
+
export { DefaultPhotoWallSceneHorizontal, componentMetadata as DefaultPhotoWallSceneHorizontalMetadata } from './default-photo-wall-scene-Horizontal'
|
|
7
|
+
export { DefaultPhotoStripScene, componentMetadata as DefaultPhotoStripSceneMetadata } from './default-photo-strip-scene'
|
|
8
|
+
export { DefaultPhotoStripSceneLandscape, componentMetadata as DefaultPhotoStripSceneLandscapeMetadata } from './default-photo-strip-scene-Landscape'
|
|
9
|
+
export { DefaultPhotoStripSceneHorizontal, componentMetadata as DefaultPhotoStripSceneHorizontalMetadata } from './default-photo-strip-scene-Horizontal'
|
|
10
|
+
export {
|
|
11
|
+
DefaultEffectsPhotoScene,
|
|
12
|
+
DefaultEffectsPhotoSceneLandscape,
|
|
13
|
+
DefaultEffectsPhotoSceneHorizontal,
|
|
14
|
+
DefaultEffectsPhotoSceneMetadata,
|
|
15
|
+
DefaultEffectsPhotoSceneLandscapeMetadata,
|
|
16
|
+
DefaultEffectsPhotoSceneHorizontalMetadata,
|
|
17
|
+
} from './effects-photo'
|
|
18
|
+
export {
|
|
19
|
+
Default3DVRPhotoScene,
|
|
20
|
+
Default3DVRPhotoSceneLandscape,
|
|
21
|
+
Default3DVRPhotoSceneHorizontal,
|
|
22
|
+
Default3DVRPhotoSceneMetadata,
|
|
23
|
+
Default3DVRPhotoSceneLandscapeMetadata,
|
|
24
|
+
Default3DVRPhotoSceneHorizontalMetadata,
|
|
25
|
+
} from './3dvr-photo'
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
vi.mock('remotion', async () => import('../../../storybook/remotion-preview'))
|
|
4
|
+
|
|
5
|
+
import { SceneViewport } from '../../../storybook/SceneViewport'
|
|
6
|
+
import { Default3DVRPhotoSceneLandscape } from './3dvr-photo/default-3dvr-photo-scene-Landscape'
|
|
7
|
+
import { DefaultEffectsPhotoSceneLandscape } from './effects-photo/default-effects-photo-scene-Landscape'
|
|
8
|
+
|
|
9
|
+
const photoUrl = 'https://heyaai-statics-1342239856.cos.ap-guangzhou.myqcloud.com/tmp/premium_photo-1779404552087-0f905162376f-121.jpeg'
|
|
10
|
+
const { renderToStaticMarkup } = require('/data/projects/foldspace-stack-apps/vibecuting/packages/@vibecuting/video-project-core/node_modules/.pnpm/react-dom@19.2.7_react@19.2.7/node_modules/react-dom/server')
|
|
11
|
+
|
|
12
|
+
describe('photo scene storybook rendering', () => {
|
|
13
|
+
it('renders 3dvr-photo in storybook preview', () => {
|
|
14
|
+
const html = renderToStaticMarkup(
|
|
15
|
+
<SceneViewport width={1280} height={720} aspectRatio="16:9" mode="static">
|
|
16
|
+
<Default3DVRPhotoSceneLandscape
|
|
17
|
+
title="Stereo Window"
|
|
18
|
+
subtitle="3D camera controls over a still frame with subtle orbit, pitch and depth."
|
|
19
|
+
badge="3D VR Photo"
|
|
20
|
+
imageSrc={photoUrl}
|
|
21
|
+
yaw={12}
|
|
22
|
+
pitch={4}
|
|
23
|
+
roll={0}
|
|
24
|
+
perspective={980}
|
|
25
|
+
zoom={1}
|
|
26
|
+
depth={26}
|
|
27
|
+
focusX={0.5}
|
|
28
|
+
focusY={0.45}
|
|
29
|
+
accent="#020617"
|
|
30
|
+
secondaryAccent="#1d4ed8"
|
|
31
|
+
/>
|
|
32
|
+
</SceneViewport>,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
expect(html).toContain('Stereo Window')
|
|
36
|
+
expect(html).toContain(photoUrl)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('renders 3dvr-photo animation preview in storybook preview', () => {
|
|
40
|
+
const html = renderToStaticMarkup(
|
|
41
|
+
<SceneViewport width={1280} height={720} aspectRatio="16:9" mode="animation">
|
|
42
|
+
<Default3DVRPhotoSceneLandscape
|
|
43
|
+
title="Stereo Window"
|
|
44
|
+
subtitle="3D camera controls over a still frame with subtle orbit, pitch and depth."
|
|
45
|
+
badge="3D VR Photo"
|
|
46
|
+
imageSrc={photoUrl}
|
|
47
|
+
yaw={12}
|
|
48
|
+
pitch={4}
|
|
49
|
+
roll={0}
|
|
50
|
+
perspective={980}
|
|
51
|
+
zoom={1}
|
|
52
|
+
depth={26}
|
|
53
|
+
focusX={0.5}
|
|
54
|
+
focusY={0.45}
|
|
55
|
+
accent="#020617"
|
|
56
|
+
secondaryAccent="#1d4ed8"
|
|
57
|
+
/>
|
|
58
|
+
</SceneViewport>,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
expect(html).toContain('Stereo Window')
|
|
62
|
+
expect(html).toContain(photoUrl)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('renders effects-photo in storybook preview', () => {
|
|
66
|
+
const html = renderToStaticMarkup(
|
|
67
|
+
<SceneViewport width={1280} height={720} aspectRatio="16:9" mode="static">
|
|
68
|
+
<DefaultEffectsPhotoSceneLandscape
|
|
69
|
+
title="Golden Echo"
|
|
70
|
+
subtitle="Layered photo grading with glow, grain, warmth and a cinematic vignette."
|
|
71
|
+
badge="Effects Photo"
|
|
72
|
+
imageSrc={photoUrl}
|
|
73
|
+
effects={[
|
|
74
|
+
{ type: 'glow', intensity: 0.55 },
|
|
75
|
+
{ type: 'vignette', intensity: 0.65 },
|
|
76
|
+
{ type: 'grain', intensity: 0.12 },
|
|
77
|
+
{ type: 'warmth', intensity: 0.18, hue: '#f59e0b' },
|
|
78
|
+
]}
|
|
79
|
+
accent="#020617"
|
|
80
|
+
secondaryAccent="#1d4ed8"
|
|
81
|
+
/>
|
|
82
|
+
</SceneViewport>,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
expect(html).toContain('Golden Echo')
|
|
86
|
+
expect(html).toContain(photoUrl)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('renders effects-photo animation preview in storybook preview', () => {
|
|
90
|
+
const html = renderToStaticMarkup(
|
|
91
|
+
<SceneViewport width={1280} height={720} aspectRatio="16:9" mode="animation">
|
|
92
|
+
<DefaultEffectsPhotoSceneLandscape
|
|
93
|
+
title="Golden Echo"
|
|
94
|
+
subtitle="Layered photo grading with glow, grain, warmth and a cinematic vignette."
|
|
95
|
+
badge="Effects Photo"
|
|
96
|
+
imageSrc={photoUrl}
|
|
97
|
+
effects={[
|
|
98
|
+
{ type: 'glow', intensity: 0.55 },
|
|
99
|
+
{ type: 'vignette', intensity: 0.65 },
|
|
100
|
+
{ type: 'grain', intensity: 0.12 },
|
|
101
|
+
{ type: 'warmth', intensity: 0.18, hue: '#f59e0b' },
|
|
102
|
+
]}
|
|
103
|
+
accent="#020617"
|
|
104
|
+
secondaryAccent="#1d4ed8"
|
|
105
|
+
/>
|
|
106
|
+
</SceneViewport>,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
expect(html).toContain('Golden Echo')
|
|
110
|
+
expect(html).toContain(photoUrl)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DefaultSlideSceneHorizontal } from './default-slide-scene-Horizontal'
|
|
2
2
|
|
|
3
3
|
const meta = {
|
|
4
|
-
title: 'core/scene/DefaultSlideSceneHorizontal/Animated',
|
|
4
|
+
title: 'core/scene/slide/default-slide-scene/DefaultSlideSceneHorizontal/Animated',
|
|
5
5
|
component: DefaultSlideSceneHorizontal,
|
|
6
6
|
args: {
|
|
7
7
|
title: 'Slide Scene',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DefaultSlideSceneHorizontal } from './default-slide-scene-Horizontal'
|
|
2
2
|
|
|
3
3
|
const meta = {
|
|
4
|
-
title: 'core/scene/DefaultSlideSceneHorizontal',
|
|
4
|
+
title: 'core/scene/slide/default-slide-scene/DefaultSlideSceneHorizontal',
|
|
5
5
|
component: DefaultSlideSceneHorizontal,
|
|
6
6
|
args: {
|
|
7
7
|
title: 'Slide Scene',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DefaultSlideSceneLandscape } from './default-slide-scene-Landscape'
|
|
2
2
|
|
|
3
3
|
const meta = {
|
|
4
|
-
title: 'core/scene/DefaultSlideSceneLandscape/Animated',
|
|
4
|
+
title: 'core/scene/slide/default-slide-scene/DefaultSlideSceneLandscape/Animated',
|
|
5
5
|
component: DefaultSlideSceneLandscape,
|
|
6
6
|
args: {
|
|
7
7
|
title: 'Slide Scene',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DefaultSlideSceneLandscape } from './default-slide-scene-Landscape'
|
|
2
2
|
|
|
3
3
|
const meta = {
|
|
4
|
-
title: 'core/scene/DefaultSlideSceneLandscape',
|
|
4
|
+
title: 'core/scene/slide/default-slide-scene/DefaultSlideSceneLandscape',
|
|
5
5
|
component: DefaultSlideSceneLandscape,
|
|
6
6
|
args: {
|
|
7
7
|
title: 'Slide Scene',
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { DefaultVideoSceneHorizontal } from './default-video-scene-Horizontal'
|
|
2
|
+
|
|
3
|
+
const photoUrl = 'https://heyaai-statics-1342239856.cos.ap-guangzhou.myqcloud.com/tmp/premium_photo-1779404552087-0f905162376f-121.jpeg'
|
|
4
|
+
const frame = <img src={photoUrl} alt="Video preview" />
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: 'core/scene/video/DefaultVideoSceneHorizontal',
|
|
8
|
+
component: DefaultVideoSceneHorizontal,
|
|
9
|
+
args: {
|
|
10
|
+
title: 'Launch Cut',
|
|
11
|
+
subtitle: 'A portrait video preview with full-bleed media and editorial framing.',
|
|
12
|
+
badge: 'Video',
|
|
13
|
+
runtimeLabel: '03:18',
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default meta
|
|
18
|
+
|
|
19
|
+
export const Default = {
|
|
20
|
+
render: (storyArgs: (typeof meta)['args']) => <DefaultVideoSceneHorizontal {...storyArgs} video={frame} poster={frame} />,
|
|
21
|
+
parameters: { scenePreviewAspectRatio: '9:16' },
|
|
22
|
+
}
|
|
23
|
+
export const Animated = {
|
|
24
|
+
render: (storyArgs: (typeof meta)['args']) => <DefaultVideoSceneHorizontal {...storyArgs} video={frame} poster={frame} />,
|
|
25
|
+
parameters: { scenePreviewAspectRatio: '9:16', scenePreviewMode: 'animation' },
|
|
26
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react'
|
|
2
|
+
|
|
3
|
+
import { VideoComponent, type ScenePluginMetadata } from '@vibecuting/component-project-helper'
|
|
4
|
+
|
|
5
|
+
import { DefaultVideoScene, componentMetadata as baseComponentMetadata } from './default-video-scene'
|
|
6
|
+
|
|
7
|
+
export const componentMetadata: ScenePluginMetadata = {
|
|
8
|
+
...baseComponentMetadata,
|
|
9
|
+
name: 'DefaultVideoSceneHorizontal',
|
|
10
|
+
description: 'Video preview scene (Portrait 9:16 variant)',
|
|
11
|
+
sourceFile: 'src/core/scene/video/default-video-scene-Horizontal.tsx',
|
|
12
|
+
aspectRatio: '9:16',
|
|
13
|
+
tags: Array.from(new Set([...(baseComponentMetadata.tags ?? []), 'horizontal'])),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const DefaultVideoSceneHorizontal = VideoComponent(componentMetadata)(function DefaultVideoSceneHorizontal(
|
|
17
|
+
props: ComponentProps<typeof DefaultVideoScene>,
|
|
18
|
+
) {
|
|
19
|
+
return <DefaultVideoScene {...props} />
|
|
20
|
+
})
|