"use client"; import { useState, useRef } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Slider } from '@/components/ui/slider'; import { ArrowLeft, ArrowRight, Play, Trash2, Replace, Scissors, Volume2, Edit, Upload, Image, Sparkles, ChevronLeft, ChevronRight, Layers, Pause, File, Ruler, UnfoldHorizontal, RefreshCcw, RotateCcw } from 'lucide-react'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { Checkbox } from '@/components/ui/checkbox'; import { Label } from '@/components/ui/label'; import { Input } from '@/components/ui/input'; interface GenerateShotsStepProps { onNext: () => void; onPrevious: () => void; } const mockChapters = [ { id: 1, title: 'Chapter 1', shots: [ { id: '1-1', type: 'talking-head', duration: 8, shotVideo: 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', generatedVideos:[ 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4' ], description: 'Opening welcome shot with character', transition: 'Selected Automatically by Preset', volume: 55, mediaNumber: 1, }, { id: '1-2', type: 'b-roll', duration: 9, shotVideo: 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', generatedVideos:[ 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4' ], description: 'Technology overview montage', transition: 'Selected Automatically by Preset', volume: 55, mediaNumber: 2, } ], }, { id: 2, title: 'Chapter 2', shots: [ { id: '2-1', type: 'talking-head', duration: 8, shotVideo: 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', generatedVideos:[ 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4' ], description: 'Opening welcome shot with character', transition: 'Selected Automatically by Preset', volume: 55, mediaNumber: 1, }, { id: '2-2', type: 'b-roll', duration: 9, shotVideo: 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', generatedVideos:[ 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4' ], description: 'Technology overview montage', transition: 'Selected Automatically by Preset', volume: 55, mediaNumber: 2, }, { id: '2-3', type: 'animation', duration: 10, shotVideo: 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', generatedVideos:[ 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4' ], description: 'Animation sequence', transition: 'Selected Automatically by Preset', volume: 55, mediaNumber: 3, }, { id: '2-4', type: 'talking-head', duration: 8, shotVideo: 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', generatedVideos:[ 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4', 'https://cdn.qikongjian.com/videos/1750162598_db834807-3f1c-4c52-8f50-b25396bd73ef_text_to_video_0.mp4' ], description: 'Character dialogue', transition: 'Selected Automatically by Preset', volume: 55, mediaNumber: 4, } ] } ]; const transitionTypes = [ { value: 'fade', label: 'Fade' }, { value: 'slide', label: 'Slide' }, { value: 'zoom', label: 'Zoom' }, { value: 'cut', label: 'Cut' }, ]; const replaceMediaTabs = [ { value: 'uploaded', label: 'Uploaded media' }, { value: 'stock', label: 'Stock media' }, { value: 'generative', label: 'Generative media' }, ]; const mediaPropertyTabs = [ { value: 'media', label: 'Media' }, { value: 'audio', label: 'Audio & SFX' }, ]; export function GenerateShotsStep({ onNext, onPrevious }: GenerateShotsStepProps) { const [selectedChapter, setSelectedChapter] = useState(1); const [selectedShot, setSelectedShot] = useState('1-1'); const [chapters, setChapters] = useState(mockChapters); const videoRef = useRef(null); const [isPlaying, setIsPlaying] = useState(false); const currentChapter = chapters.find(ch => ch.id === selectedChapter); const currentShot = currentChapter?.shots.find(shot => shot.id === selectedShot); const [isCheckVideoOpen, setIsCheckVideoOpen] = useState(false); const [isReplaceMediaOpen, setIsReplaceMediaOpen] = useState(false); const [isMediaPropertyOpen, setIsMediaPropertyOpen] = useState(false); const [activeTabReplaceMedia, setActiveTabReplaceMedia] = useState('uploaded'); const [activeTabMediaProperty, setActiveTabMediaProperty] = useState('media'); const handlePlayPause = () => { if (videoRef.current) { if (videoRef.current.paused) { videoRef.current.play(); setIsPlaying(true); } else { videoRef.current.pause(); setIsPlaying(false); } } }; const handleTransitionChange = (shotId: string, transition: string) => { setChapters(chapters.map(ch => ch.id === selectedChapter ? { ...ch, shots: ch.shots.map(shot => shot.id === shotId ? { ...shot, transition } : shot ) } : ch )); }; const handleVolumeChange = (shotId: string, volume: number[]) => { setChapters(chapters.map(ch => ch.id === selectedChapter ? { ...ch, shots: ch.shots.map(shot => shot.id === shotId ? { ...shot, volume: volume[0] } : shot ) } : ch )); }; const handleDeleteShot = (shotId: string) => { setChapters(chapters.map(ch => ch.id === selectedChapter ? { ...ch, shots: ch.shots.filter(shot => shot.id !== shotId) } : ch )); }; const handleOpenReplaceMedia = (tab: string) => { setActiveTabReplaceMedia(tab); setIsReplaceMediaOpen(true); }; const handleOpenMediaProperty = (tab: string) => { setActiveTabMediaProperty(tab); setIsMediaPropertyOpen(true); }; return (
{/* 分镜视频列表 弹窗 */} Media history
{currentShot?.generatedVideos.map((video, index) => (
))}
{/* Apply 按钮 Cancel 按钮 */}
{/* 替换媒体 弹窗;点击弹窗以外 不触发关闭弹窗 */} Replace media {/* 占剩余高度 溢出滚动 */}
{/* 章节列表 */}
{chapters.map((chapter, index) => (
{/* Chapter: index + 1 文字竖着显示 */}
Chapter {chapter.id}
{/* flex: 1 */}
{chapter.shots.map((shot, index) => (
setSelectedShot(shot.id)} >
))}
))}
{/* 标签页 */} {replaceMediaTabs.map((tab) => ( {tab.label} ))}
{/* 上传媒体 */} {/* 上传按钮;筛选下拉框 视频 图片;一行展示 两边对齐 */}
{/* 媒体库 */}
{/* 媒体库 网格展示 */}
{/* 没有数据 提示 请上传媒体 */}

No media uploaded

{/* 素材库 */}
{/* 生成媒体 */}
{/* Apply 按钮 Cancel 按钮 */}
{/* 媒体属性 弹窗 高度全屏 */} Media properties
{/* 左侧内容区域 */}
{/* 标签页 */} {mediaPropertyTabs.map((tab) => ( {tab.label} ))} {/* Duration */}
00m : 10s : 500ms / 00m : 17s : 320ms
{/* Trim */}
{/* Center point */}
{/* Zoom & Rotation */}
{/* Transition */}
{/* Script */}
This part of the script is 21.00 seconds long.
There are 2 media attached to this part of the script:
{/* 章节列表 */}
{chapters.map((chapter, index) => (
{/* Chapter: index + 1 文字竖着显示 */}
Chapter {chapter.id}
{/* flex: 1 */}
{chapter.shots.map((shot, index) => (
setSelectedShot(shot.id)} >
))}
))}
{/* SFX name */}
Airplane Rocket Fire Close
{/* SFX volume */}
{/* Replace audio */}
{/* 右侧预览区域 */}
{currentShot && (
{/* 视频预览 */}
Chapter 1 / media 1 / People gathered in a city square to watch a fireworks display
{/* 音频波形 */}
0:00
0:12
{/* 简单的音频波形显示 */}
{Array.from({ length: 40 }).map((_, i) => (
))}
Chapter 1 / Audio & SFX / Airplane Rocket Fire Close
)}
{/* 底部按钮 */}
{/* Timeline Header */}
{/* 章节列表 */}
{chapters.map((chapter, index) => (
{/* Chapter: index + 1 文字竖着显示 */}
Chapter {chapter.id}
{/* flex: 1 */}
{chapter.shots.map((shot, index) => (
setSelectedShot(shot.id)} >
))}
))}
{/* Chinese Text 不换行 溢出可左右滚动 隐藏滚动条 */}
但我决心要改变它。我的翅膀展开,在千星的光芒中翱翔,降临人生。和星光铸就,我是凤青楗,这就是我重生的故事。不......?
{/* Main Content */}
{/* Left Panel */}
{/* Replace Media Section */} {currentShot && (

Replace media {currentShot.mediaNumber} with:

)} {/* Media Info */} {currentShot && (

Media info:

{/* Chapter/Media Info */}
{/* 文档图标 */} Chapter 1 / media {currentShot.mediaNumber} / Generated media
Delete media
Delete and add blank media
{/* Duration */}
{/* 标尺图标 */} 00m : 08s : 070ms / 00m : 08s : 070ms
Trim
{/* checkbox */}
{/* 按钮 */}
{/* Transition */}
{/* 图标 */} Transition: {currentShot.transition}
Transition
{/* select */}
{/* 按钮 */}
{/* Audio Volume */}
Audio volume: {currentShot.volume}% volume
{/* Right Panel - Preview */}
{currentShot && (
{/* 刷新图标 */}
)}
)} {/* Media Properties */}

handleOpenMediaProperty('media')}>Media properties

{/* Action Buttons */}
); }