@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 { DefaultVideoScene, componentMetadata as baseComponentMetadata } from './default-video-scene'
|
|
6
|
+
|
|
7
|
+
export const componentMetadata: ScenePluginMetadata = {
|
|
8
|
+
...baseComponentMetadata,
|
|
9
|
+
name: 'DefaultVideoSceneLandscape',
|
|
10
|
+
description: 'Video preview scene (Landscape 16:9 variant)',
|
|
11
|
+
sourceFile: 'src/core/scene/video/default-video-scene-Landscape.tsx',
|
|
12
|
+
aspectRatio: '16:9',
|
|
13
|
+
tags: Array.from(new Set([...(baseComponentMetadata.tags ?? []), 'landscape'])),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const DefaultVideoSceneLandscape = VideoComponent(componentMetadata)(function DefaultVideoSceneLandscape(
|
|
17
|
+
props: ComponentProps<typeof DefaultVideoScene>,
|
|
18
|
+
) {
|
|
19
|
+
return <DefaultVideoScene {...props} />
|
|
20
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { DefaultVideoSceneLandscape } from './default-video-scene-Landscape'
|
|
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/DefaultVideoSceneLandscape',
|
|
8
|
+
component: DefaultVideoSceneLandscape,
|
|
9
|
+
args: {
|
|
10
|
+
title: 'Launch Cut',
|
|
11
|
+
subtitle: 'A single video preview with Apple-style framing and a soft editorial overlay.',
|
|
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']) => <DefaultVideoSceneLandscape {...storyArgs} video={frame} poster={frame} />,
|
|
21
|
+
parameters: { scenePreviewAspectRatio: '16:9' },
|
|
22
|
+
}
|
|
23
|
+
export const Animated = {
|
|
24
|
+
render: (storyArgs: (typeof meta)['args']) => <DefaultVideoSceneLandscape {...storyArgs} video={frame} poster={frame} />,
|
|
25
|
+
parameters: { scenePreviewAspectRatio: '16:9', scenePreviewMode: 'animation' },
|
|
26
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
import { VideoComponent, defineScenePluginMetadata } from '@vibecuting/component-project-helper'
|
|
6
|
+
import { cloneElement, isValidElement } from 'react'
|
|
7
|
+
import { spring, useCurrentFrame, useVideoConfig } from 'remotion'
|
|
8
|
+
|
|
9
|
+
import { BaseRemotionScene, SceneMotionLayer, type BaseRemotionSceneProps, useRemotionSceneRuntime, useSceneConfig } from '../../../base'
|
|
10
|
+
import { defineSceneComponent } from '../../../plugins/scene-component'
|
|
11
|
+
|
|
12
|
+
export interface DefaultVideoSceneProps extends BaseRemotionSceneProps {
|
|
13
|
+
sceneId?: string
|
|
14
|
+
sceneName?: string
|
|
15
|
+
title?: string
|
|
16
|
+
subtitle?: string
|
|
17
|
+
badge?: string
|
|
18
|
+
runtimeLabel?: string
|
|
19
|
+
video: ReactNode
|
|
20
|
+
videos?: ReactNode[]
|
|
21
|
+
poster?: ReactNode
|
|
22
|
+
variant?: 'player' | 'split' | 'wall'
|
|
23
|
+
accent?: string
|
|
24
|
+
secondaryAccent?: string
|
|
25
|
+
textColor?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const componentMetadata = defineScenePluginMetadata({
|
|
29
|
+
resourceKind: 'scene',
|
|
30
|
+
name: 'DefaultVideoScene',
|
|
31
|
+
description: 'Video-first scene with poster-led motion and editorial framing',
|
|
32
|
+
sourceFile: 'src/core/scene/video/default-video-scene.tsx',
|
|
33
|
+
pluginKey: 'core.video.preview',
|
|
34
|
+
tags: ['scene', 'video', 'preview', 'player'],
|
|
35
|
+
aspectRatio: '16:9',
|
|
36
|
+
sceneType: 'scene',
|
|
37
|
+
sceneFamily: 'custom',
|
|
38
|
+
rootLayout: 'absolute-fill',
|
|
39
|
+
propsTypeName: 'DefaultVideoSceneProps',
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
function VideoFrame({
|
|
43
|
+
node,
|
|
44
|
+
isPortrait,
|
|
45
|
+
animationProgress,
|
|
46
|
+
}: {
|
|
47
|
+
node: ReactNode
|
|
48
|
+
isPortrait: boolean
|
|
49
|
+
animationProgress: number
|
|
50
|
+
}) {
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
style={{
|
|
54
|
+
width: '100%',
|
|
55
|
+
height: '100%',
|
|
56
|
+
overflow: 'hidden',
|
|
57
|
+
border: '1px solid rgba(255,255,255,0.14)',
|
|
58
|
+
background: 'rgba(2,6,23,0.86)',
|
|
59
|
+
transform: `scale(${1 + (1 - animationProgress) * 0.012})`,
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
{node}
|
|
63
|
+
</div>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function renderCoverMedia(node: ReactNode) {
|
|
68
|
+
if (isValidElement(node)) {
|
|
69
|
+
const existingStyle = (node.props as { style?: Record<string, unknown> }).style ?? {}
|
|
70
|
+
return cloneElement(node, {
|
|
71
|
+
...(node.props as Record<string, unknown>),
|
|
72
|
+
style: {
|
|
73
|
+
...existingStyle,
|
|
74
|
+
width: '100%',
|
|
75
|
+
height: '100%',
|
|
76
|
+
objectFit: 'cover',
|
|
77
|
+
display: 'block',
|
|
78
|
+
minWidth: 0,
|
|
79
|
+
minHeight: 0,
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return <div style={{ width: '100%', height: '100%', minWidth: 0, minHeight: 0 }}>{node}</div>
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const DefaultVideoScene = VideoComponent(componentMetadata)(
|
|
88
|
+
defineSceneComponent({
|
|
89
|
+
family: 'custom',
|
|
90
|
+
propsSchema: z.object({
|
|
91
|
+
sceneId: z.string().optional(),
|
|
92
|
+
sceneName: z.string().optional(),
|
|
93
|
+
title: z.string().optional(),
|
|
94
|
+
subtitle: z.string().optional(),
|
|
95
|
+
badge: z.string().optional(),
|
|
96
|
+
runtimeLabel: z.string().optional(),
|
|
97
|
+
video: z.any(),
|
|
98
|
+
videos: z.array(z.any()).optional(),
|
|
99
|
+
poster: z.any().optional(),
|
|
100
|
+
variant: z.enum(['player', 'split', 'wall']).optional(),
|
|
101
|
+
accent: z.string().optional(),
|
|
102
|
+
secondaryAccent: z.string().optional(),
|
|
103
|
+
textColor: z.string().optional(),
|
|
104
|
+
}),
|
|
105
|
+
component: function DefaultVideoScene({
|
|
106
|
+
id,
|
|
107
|
+
name,
|
|
108
|
+
sceneId,
|
|
109
|
+
sceneName,
|
|
110
|
+
title,
|
|
111
|
+
subtitle,
|
|
112
|
+
badge,
|
|
113
|
+
runtimeLabel,
|
|
114
|
+
video,
|
|
115
|
+
videos,
|
|
116
|
+
poster,
|
|
117
|
+
variant = 'player',
|
|
118
|
+
accent,
|
|
119
|
+
secondaryAccent,
|
|
120
|
+
textColor,
|
|
121
|
+
}: DefaultVideoSceneProps) {
|
|
122
|
+
const runtime = useRemotionSceneRuntime()
|
|
123
|
+
const sceneConfig = useSceneConfig()
|
|
124
|
+
const frame = useCurrentFrame()
|
|
125
|
+
const { fps } = useVideoConfig()
|
|
126
|
+
const isPortrait = runtime.height > runtime.width
|
|
127
|
+
const animationProgress =
|
|
128
|
+
sceneConfig.sceneType === 'animation'
|
|
129
|
+
? spring({
|
|
130
|
+
frame,
|
|
131
|
+
fps,
|
|
132
|
+
config: {
|
|
133
|
+
damping: 18,
|
|
134
|
+
mass: 0.8,
|
|
135
|
+
stiffness: 110,
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
: 1
|
|
139
|
+
const titleSize = Math.round(runtime.height * (isPortrait ? 0.032 : 0.038))
|
|
140
|
+
const subtitleSize = Math.round(runtime.height * 0.017)
|
|
141
|
+
const frames = (videos?.length ? videos : [video]).slice(0, 4)
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<BaseRemotionScene
|
|
145
|
+
id={id ?? sceneId}
|
|
146
|
+
name={name ?? sceneName ?? title}
|
|
147
|
+
style={{
|
|
148
|
+
background: isPortrait
|
|
149
|
+
? `linear-gradient(180deg, ${accent ?? '#020617'} 0%, #0f172a 55%, #000000 100%)`
|
|
150
|
+
: `radial-gradient(circle at 50% 24%, ${secondaryAccent ?? 'rgba(255,255,255,0.08)'} 0%, ${accent ?? '#020617'} 48%, #000000 100%)`,
|
|
151
|
+
color: textColor ?? '#f8fafc',
|
|
152
|
+
padding: Math.round(runtime.height * (isPortrait ? 0.02 : 0.03)),
|
|
153
|
+
}}
|
|
154
|
+
>
|
|
155
|
+
<section
|
|
156
|
+
style={{
|
|
157
|
+
width: '100%',
|
|
158
|
+
height: '100%',
|
|
159
|
+
display: 'grid',
|
|
160
|
+
gridTemplateRows: 'minmax(0, 1fr) auto',
|
|
161
|
+
gap: 16,
|
|
162
|
+
minWidth: 0,
|
|
163
|
+
minHeight: 0,
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
{variant === 'wall' ? (
|
|
167
|
+
<div
|
|
168
|
+
style={{
|
|
169
|
+
display: 'grid',
|
|
170
|
+
gridTemplateColumns: isPortrait ? '1fr 1fr' : 'repeat(2, minmax(0, 1fr))',
|
|
171
|
+
gridTemplateRows: isPortrait ? 'repeat(2, minmax(0, 1fr))' : 'repeat(2, minmax(0, 1fr))',
|
|
172
|
+
gap: 12,
|
|
173
|
+
minWidth: 0,
|
|
174
|
+
minHeight: 0,
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
{frames.map((item, index) => (
|
|
178
|
+
<SceneMotionLayer key={`video-wall-${index}`} variant="media" delayIndex={index}>
|
|
179
|
+
<VideoFrame node={item} isPortrait={isPortrait} animationProgress={animationProgress} />
|
|
180
|
+
</SceneMotionLayer>
|
|
181
|
+
))}
|
|
182
|
+
</div>
|
|
183
|
+
) : variant === 'split' ? (
|
|
184
|
+
<div
|
|
185
|
+
style={{
|
|
186
|
+
display: 'grid',
|
|
187
|
+
gridTemplateColumns: isPortrait ? '1fr' : 'minmax(0, 1.18fr) minmax(250px, 0.82fr)',
|
|
188
|
+
gap: 16,
|
|
189
|
+
minWidth: 0,
|
|
190
|
+
minHeight: 0,
|
|
191
|
+
}}
|
|
192
|
+
>
|
|
193
|
+
<SceneMotionLayer variant="panel">
|
|
194
|
+
<VideoFrame node={video} isPortrait={isPortrait} animationProgress={animationProgress} />
|
|
195
|
+
</SceneMotionLayer>
|
|
196
|
+
<div
|
|
197
|
+
style={{
|
|
198
|
+
display: 'grid',
|
|
199
|
+
gap: 12,
|
|
200
|
+
alignContent: 'start',
|
|
201
|
+
padding: isPortrait ? '0' : '8px 0',
|
|
202
|
+
minWidth: 0,
|
|
203
|
+
}}
|
|
204
|
+
>
|
|
205
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, alignItems: 'center' }}>
|
|
206
|
+
<div style={{ fontSize: Math.round(runtime.height * 0.014), letterSpacing: '0.24em', textTransform: 'uppercase', opacity: 0.58 }}>
|
|
207
|
+
{badge ?? 'Video'}
|
|
208
|
+
</div>
|
|
209
|
+
{runtimeLabel ? (
|
|
210
|
+
<div style={{ fontSize: Math.round(runtime.height * 0.014), letterSpacing: '0.18em', textTransform: 'uppercase', opacity: 0.58 }}>
|
|
211
|
+
{runtimeLabel}
|
|
212
|
+
</div>
|
|
213
|
+
) : null}
|
|
214
|
+
</div>
|
|
215
|
+
{title ? (
|
|
216
|
+
<SceneMotionLayer variant="hero-title">
|
|
217
|
+
<div style={{ fontSize: titleSize, lineHeight: 0.96, letterSpacing: '-0.06em', fontWeight: 800 }}>{title}</div>
|
|
218
|
+
</SceneMotionLayer>
|
|
219
|
+
) : null}
|
|
220
|
+
{subtitle ? <div style={{ fontSize: subtitleSize, lineHeight: 1.45, opacity: 0.84 }}>{subtitle}</div> : null}
|
|
221
|
+
<div style={{ border: '1px solid rgba(255,255,255,0.12)', background: 'rgba(2,6,23,0.56)', padding: 14, display: 'grid', gap: 10 }}>
|
|
222
|
+
<div style={{ fontSize: Math.round(runtime.height * 0.013), letterSpacing: '0.22em', textTransform: 'uppercase', opacity: 0.56 }}>Editorial Notes</div>
|
|
223
|
+
<div style={{ fontSize: Math.round(runtime.height * 0.018), lineHeight: 1.45, opacity: 0.88 }}>
|
|
224
|
+
Use this layout for review notes, chapter context, or a companion still beside the clip.
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
) : (
|
|
230
|
+
<div
|
|
231
|
+
style={{
|
|
232
|
+
position: 'relative',
|
|
233
|
+
width: '100%',
|
|
234
|
+
height: '100%',
|
|
235
|
+
aspectRatio: isPortrait ? '9 / 16' : '16 / 9',
|
|
236
|
+
minWidth: 0,
|
|
237
|
+
minHeight: 0,
|
|
238
|
+
overflow: 'hidden',
|
|
239
|
+
border: '1px solid rgba(255,255,255,0.14)',
|
|
240
|
+
background: 'rgba(2,6,23,0.84)',
|
|
241
|
+
}}
|
|
242
|
+
>
|
|
243
|
+
<SceneMotionLayer
|
|
244
|
+
variant="media"
|
|
245
|
+
style={{
|
|
246
|
+
position: 'absolute',
|
|
247
|
+
inset: '-8%',
|
|
248
|
+
opacity: 0.26,
|
|
249
|
+
filter: 'blur(22px) saturate(1.06)',
|
|
250
|
+
transform: `scale(${1.12 + (1 - animationProgress) * 0.02})`,
|
|
251
|
+
}}
|
|
252
|
+
>
|
|
253
|
+
{renderCoverMedia(poster ?? video)}
|
|
254
|
+
</SceneMotionLayer>
|
|
255
|
+
<SceneMotionLayer variant="media" style={{ position: 'absolute', inset: 0 }}>
|
|
256
|
+
{renderCoverMedia(poster ?? video)}
|
|
257
|
+
</SceneMotionLayer>
|
|
258
|
+
<div
|
|
259
|
+
aria-hidden="true"
|
|
260
|
+
style={{
|
|
261
|
+
position: 'absolute',
|
|
262
|
+
inset: 0,
|
|
263
|
+
background: 'linear-gradient(180deg, rgba(2,6,23,0.1) 0%, rgba(2,6,23,0.2) 42%, rgba(2,6,23,0.72) 100%)',
|
|
264
|
+
}}
|
|
265
|
+
/>
|
|
266
|
+
<div style={{ position: 'absolute', left: 16, top: 16, display: 'flex', gap: 10, alignItems: 'center' }}>
|
|
267
|
+
<div style={{ width: 10, height: 10, borderRadius: 9999, background: 'rgba(239,68,68,0.95)', boxShadow: '0 0 0 4px rgba(239,68,68,0.16)' }} />
|
|
268
|
+
<div style={{ fontSize: Math.round(runtime.height * 0.013), letterSpacing: '0.24em', textTransform: 'uppercase', opacity: 0.72 }}>
|
|
269
|
+
{badge ?? 'Video Preview'}
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
<div style={{ position: 'absolute', left: 16, right: 16, bottom: 16, display: 'grid', gap: 8 }}>
|
|
273
|
+
{title ? <div style={{ fontSize: titleSize, fontWeight: 800, letterSpacing: '-0.06em', lineHeight: 0.95 }}>{title}</div> : null}
|
|
274
|
+
{subtitle ? <div style={{ fontSize: subtitleSize, lineHeight: 1.45, opacity: 0.86, maxWidth: isPortrait ? '88%' : '60%' }}>{subtitle}</div> : null}
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
)}
|
|
278
|
+
</section>
|
|
279
|
+
</BaseRemotionScene>
|
|
280
|
+
)
|
|
281
|
+
},
|
|
282
|
+
}),
|
|
283
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react'
|
|
2
|
+
|
|
3
|
+
import { VideoComponent, type ScenePluginMetadata } from '@vibecuting/component-project-helper'
|
|
4
|
+
|
|
5
|
+
import { DefaultVideoSplitScene, componentMetadata as baseComponentMetadata } from './default-video-split-scene'
|
|
6
|
+
|
|
7
|
+
export const componentMetadata: ScenePluginMetadata = {
|
|
8
|
+
...baseComponentMetadata,
|
|
9
|
+
name: 'DefaultVideoSplitSceneHorizontal',
|
|
10
|
+
description: 'Video split preview scene (Portrait 9:16 variant)',
|
|
11
|
+
sourceFile: 'src/core/scene/video/default-video-split-scene-Horizontal.tsx',
|
|
12
|
+
aspectRatio: '9:16',
|
|
13
|
+
tags: Array.from(new Set([...(baseComponentMetadata.tags ?? []), 'horizontal'])),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const DefaultVideoSplitSceneHorizontal = VideoComponent(componentMetadata)(function DefaultVideoSplitSceneHorizontal(
|
|
17
|
+
props: ComponentProps<typeof DefaultVideoSplitScene>,
|
|
18
|
+
) {
|
|
19
|
+
return <DefaultVideoSplitScene {...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 { DefaultVideoSplitScene, componentMetadata as baseComponentMetadata } from './default-video-split-scene'
|
|
6
|
+
|
|
7
|
+
export const componentMetadata: ScenePluginMetadata = {
|
|
8
|
+
...baseComponentMetadata,
|
|
9
|
+
name: 'DefaultVideoSplitSceneLandscape',
|
|
10
|
+
description: 'Video split preview scene (Landscape 16:9 variant)',
|
|
11
|
+
sourceFile: 'src/core/scene/video/default-video-split-scene-Landscape.tsx',
|
|
12
|
+
aspectRatio: '16:9',
|
|
13
|
+
tags: Array.from(new Set([...(baseComponentMetadata.tags ?? []), 'landscape'])),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const DefaultVideoSplitSceneLandscape = VideoComponent(componentMetadata)(function DefaultVideoSplitSceneLandscape(
|
|
17
|
+
props: ComponentProps<typeof DefaultVideoSplitScene>,
|
|
18
|
+
) {
|
|
19
|
+
return <DefaultVideoSplitScene {...props} />
|
|
20
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { DefaultVideoSplitSceneLandscape } from './default-video-split-scene-Landscape'
|
|
2
|
+
import { DefaultVideoSplitSceneHorizontal } from './default-video-split-scene-Horizontal'
|
|
3
|
+
|
|
4
|
+
const frame = (
|
|
5
|
+
<div
|
|
6
|
+
style={{
|
|
7
|
+
width: '100%',
|
|
8
|
+
height: '100%',
|
|
9
|
+
background:
|
|
10
|
+
'linear-gradient(180deg, rgba(255,255,255,0.18), rgba(2,6,23,0.84)), linear-gradient(135deg, #0f766e 0%, #1d4ed8 52%, #020617 100%)',
|
|
11
|
+
}}
|
|
12
|
+
/>
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
const args = {
|
|
16
|
+
title: 'Editorial Split',
|
|
17
|
+
subtitle: 'Use this when the clip needs a companion still, notes, or a second framing column.',
|
|
18
|
+
badge: 'Split',
|
|
19
|
+
runtimeLabel: '02:24',
|
|
20
|
+
video: frame,
|
|
21
|
+
poster: frame,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const meta = {
|
|
25
|
+
title: 'core/scene/video/DefaultVideoSplitSceneLandscape',
|
|
26
|
+
component: DefaultVideoSplitSceneLandscape,
|
|
27
|
+
args,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default meta
|
|
31
|
+
|
|
32
|
+
export const Default = { parameters: { scenePreviewAspectRatio: '16:9' } }
|
|
33
|
+
export const Animated = { parameters: { scenePreviewAspectRatio: '16:9', scenePreviewMode: 'animation' } }
|
|
34
|
+
export const Horizontal = {
|
|
35
|
+
render: (storyArgs: typeof args) => <DefaultVideoSplitSceneHorizontal {...storyArgs} />,
|
|
36
|
+
parameters: { scenePreviewAspectRatio: '9:16' },
|
|
37
|
+
}
|
|
38
|
+
export const HorizontalAnimated = {
|
|
39
|
+
render: (storyArgs: typeof args) => <DefaultVideoSplitSceneHorizontal {...storyArgs} />,
|
|
40
|
+
parameters: { scenePreviewAspectRatio: '9:16', scenePreviewMode: 'animation' },
|
|
41
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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: 'DefaultVideoSplitScene',
|
|
10
|
+
description: 'Video split preview scene',
|
|
11
|
+
sourceFile: 'src/core/scene/video/default-video-split-scene.tsx',
|
|
12
|
+
tags: Array.from(new Set([...(baseComponentMetadata.tags ?? []), 'split'])),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const DefaultVideoSplitScene = VideoComponent(componentMetadata)(function DefaultVideoSplitScene(
|
|
16
|
+
props: ComponentProps<typeof DefaultVideoScene>,
|
|
17
|
+
) {
|
|
18
|
+
return <DefaultVideoScene {...props} variant="split" />
|
|
19
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react'
|
|
2
|
+
|
|
3
|
+
import { VideoComponent, type ScenePluginMetadata } from '@vibecuting/component-project-helper'
|
|
4
|
+
|
|
5
|
+
import { DefaultVideoWallScene, componentMetadata as baseComponentMetadata } from './default-video-wall-scene'
|
|
6
|
+
|
|
7
|
+
export const componentMetadata: ScenePluginMetadata = {
|
|
8
|
+
...baseComponentMetadata,
|
|
9
|
+
name: 'DefaultVideoWallSceneHorizontal',
|
|
10
|
+
description: 'Video wall preview scene (Portrait 9:16 variant)',
|
|
11
|
+
sourceFile: 'src/core/scene/video/default-video-wall-scene-Horizontal.tsx',
|
|
12
|
+
aspectRatio: '9:16',
|
|
13
|
+
tags: Array.from(new Set([...(baseComponentMetadata.tags ?? []), 'horizontal'])),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const DefaultVideoWallSceneHorizontal = VideoComponent(componentMetadata)(function DefaultVideoWallSceneHorizontal(
|
|
17
|
+
props: ComponentProps<typeof DefaultVideoWallScene>,
|
|
18
|
+
) {
|
|
19
|
+
return <DefaultVideoWallScene {...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 { DefaultVideoWallScene, componentMetadata as baseComponentMetadata } from './default-video-wall-scene'
|
|
6
|
+
|
|
7
|
+
export const componentMetadata: ScenePluginMetadata = {
|
|
8
|
+
...baseComponentMetadata,
|
|
9
|
+
name: 'DefaultVideoWallSceneLandscape',
|
|
10
|
+
description: 'Video wall preview scene (Landscape 16:9 variant)',
|
|
11
|
+
sourceFile: 'src/core/scene/video/default-video-wall-scene-Landscape.tsx',
|
|
12
|
+
aspectRatio: '16:9',
|
|
13
|
+
tags: Array.from(new Set([...(baseComponentMetadata.tags ?? []), 'landscape'])),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const DefaultVideoWallSceneLandscape = VideoComponent(componentMetadata)(function DefaultVideoWallSceneLandscape(
|
|
17
|
+
props: ComponentProps<typeof DefaultVideoWallScene>,
|
|
18
|
+
) {
|
|
19
|
+
return <DefaultVideoWallScene {...props} />
|
|
20
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DefaultVideoWallSceneHorizontal } from './default-video-wall-scene-Horizontal'
|
|
2
|
+
import { DefaultVideoWallSceneLandscape } from './default-video-wall-scene-Landscape'
|
|
3
|
+
|
|
4
|
+
const frame = (
|
|
5
|
+
<div
|
|
6
|
+
style={{
|
|
7
|
+
width: '100%',
|
|
8
|
+
height: '100%',
|
|
9
|
+
background:
|
|
10
|
+
'linear-gradient(180deg, rgba(255,255,255,0.16), rgba(2,6,23,0.84)), linear-gradient(135deg, #7c3aed 0%, #1d4ed8 42%, #020617 100%)',
|
|
11
|
+
}}
|
|
12
|
+
/>
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
const args = {
|
|
16
|
+
title: 'Clip Wall',
|
|
17
|
+
subtitle: 'Four clips arranged like a screening board or trailer review wall.',
|
|
18
|
+
badge: 'Wall',
|
|
19
|
+
video: frame,
|
|
20
|
+
videos: [frame, frame, frame, frame],
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const meta = {
|
|
24
|
+
title: 'core/scene/video/DefaultVideoWallSceneLandscape',
|
|
25
|
+
component: DefaultVideoWallSceneLandscape,
|
|
26
|
+
args,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default meta
|
|
30
|
+
|
|
31
|
+
export const Default = { parameters: { scenePreviewAspectRatio: '16:9' } }
|
|
32
|
+
export const Animated = { parameters: { scenePreviewAspectRatio: '16:9', scenePreviewMode: 'animation' } }
|
|
33
|
+
export const Horizontal = {
|
|
34
|
+
render: (storyArgs: typeof args) => <DefaultVideoWallSceneHorizontal {...storyArgs} />,
|
|
35
|
+
parameters: { scenePreviewAspectRatio: '9:16' },
|
|
36
|
+
}
|
|
37
|
+
export const HorizontalAnimated = {
|
|
38
|
+
render: (storyArgs: typeof args) => <DefaultVideoWallSceneHorizontal {...storyArgs} />,
|
|
39
|
+
parameters: { scenePreviewAspectRatio: '9:16', scenePreviewMode: 'animation' },
|
|
40
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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: 'DefaultVideoWallScene',
|
|
10
|
+
description: 'Video wall preview scene',
|
|
11
|
+
sourceFile: 'src/core/scene/video/default-video-wall-scene.tsx',
|
|
12
|
+
tags: Array.from(new Set([...(baseComponentMetadata.tags ?? []), 'wall', 'multi'])),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const DefaultVideoWallScene = VideoComponent(componentMetadata)(function DefaultVideoWallScene(
|
|
16
|
+
props: ComponentProps<typeof DefaultVideoScene>,
|
|
17
|
+
) {
|
|
18
|
+
return <DefaultVideoScene {...props} variant="wall" />
|
|
19
|
+
})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { DefaultVideoScene, componentMetadata as DefaultVideoSceneMetadata } from './default-video-scene'
|
|
2
|
+
export { DefaultVideoSceneLandscape, componentMetadata as DefaultVideoSceneLandscapeMetadata } from './default-video-scene-Landscape'
|
|
3
|
+
export { DefaultVideoSceneHorizontal, componentMetadata as DefaultVideoSceneHorizontalMetadata } from './default-video-scene-Horizontal'
|
|
4
|
+
export { DefaultVideoSplitScene, componentMetadata as DefaultVideoSplitSceneMetadata } from './default-video-split-scene'
|
|
5
|
+
export { DefaultVideoSplitSceneLandscape, componentMetadata as DefaultVideoSplitSceneLandscapeMetadata } from './default-video-split-scene-Landscape'
|
|
6
|
+
export { DefaultVideoSplitSceneHorizontal, componentMetadata as DefaultVideoSplitSceneHorizontalMetadata } from './default-video-split-scene-Horizontal'
|
|
7
|
+
export { DefaultVideoWallScene, componentMetadata as DefaultVideoWallSceneMetadata } from './default-video-wall-scene'
|
|
8
|
+
export { DefaultVideoWallSceneLandscape, componentMetadata as DefaultVideoWallSceneLandscapeMetadata } from './default-video-wall-scene-Landscape'
|
|
9
|
+
export { DefaultVideoWallSceneHorizontal, componentMetadata as DefaultVideoWallSceneHorizontalMetadata } from './default-video-wall-scene-Horizontal'
|