forked from 77media/video-flow
最终视频与分镜列表切换
This commit is contained in:
parent
5b35e274b9
commit
3cf9915877
@ -19,6 +19,7 @@ import { Drawer, Tooltip, notification } from 'antd';
|
|||||||
import { showEditingNotification } from "@/components/pages/work-flow/editing-notification";
|
import { showEditingNotification } from "@/components/pages/work-flow/editing-notification";
|
||||||
// import { AIEditingIframeButton } from './work-flow/ai-editing-iframe';
|
// import { AIEditingIframeButton } from './work-flow/ai-editing-iframe';
|
||||||
import { exportVideoWithRetry } from '@/utils/export-service';
|
import { exportVideoWithRetry } from '@/utils/export-service';
|
||||||
|
import { getFirstFrame } from '@/utils/tools';
|
||||||
// 临时禁用视频编辑功能
|
// 临时禁用视频编辑功能
|
||||||
// import { EditPoint as EditPointType } from './work-flow/video-edit/types';
|
// import { EditPoint as EditPointType } from './work-flow/video-edit/types';
|
||||||
import { AIEditingIframeButton } from './work-flow/ai-editing-iframe';
|
import { AIEditingIframeButton } from './work-flow/ai-editing-iframe';
|
||||||
@ -51,6 +52,8 @@ const WorkFlow = React.memo(function WorkFlow() {
|
|||||||
const [previewVideoUrl, setPreviewVideoUrl] = React.useState<string | null>(null);
|
const [previewVideoUrl, setPreviewVideoUrl] = React.useState<string | null>(null);
|
||||||
const [previewVideoId, setPreviewVideoId] = React.useState<string | null>(null);
|
const [previewVideoId, setPreviewVideoId] = React.useState<string | null>(null);
|
||||||
const [isFocusChatInput, setIsFocusChatInput] = React.useState(false);
|
const [isFocusChatInput, setIsFocusChatInput] = React.useState(false);
|
||||||
|
const [selectedView, setSelectedView] = React.useState<'final' | 'video' | null>(null);
|
||||||
|
const [isFinalBarOpen, setIsFinalBarOpen] = React.useState(true);
|
||||||
|
|
||||||
const [aiEditingResult, setAiEditingResult] = React.useState<any>(null);
|
const [aiEditingResult, setAiEditingResult] = React.useState<any>(null);
|
||||||
// const aiEditingButtonRef = useRef<{ handleAIEditing: () => Promise<void> }>(null);
|
// const aiEditingButtonRef = useRef<{ handleAIEditing: () => Promise<void> }>(null);
|
||||||
@ -236,6 +239,13 @@ const WorkFlow = React.memo(function WorkFlow() {
|
|||||||
console.log('changedIndex_work-flow', currentSketchIndex, taskObject);
|
console.log('changedIndex_work-flow', currentSketchIndex, taskObject);
|
||||||
}, [currentSketchIndex, taskObject]);
|
}, [currentSketchIndex, taskObject]);
|
||||||
|
|
||||||
|
// 当最终视频出现时,默认选中最终视频
|
||||||
|
useEffect(() => {
|
||||||
|
if (taskObject?.final?.url && selectedView === null) {
|
||||||
|
setSelectedView('final');
|
||||||
|
}
|
||||||
|
}, [taskObject?.final?.url, selectedView]);
|
||||||
|
|
||||||
// 监听粗剪是否完成
|
// 监听粗剪是否完成
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('🎬 final video useEffect triggered:', {
|
console.log('🎬 final video useEffect triggered:', {
|
||||||
@ -409,6 +419,7 @@ Please process this video editing request.`;
|
|||||||
title={taskObject.title}
|
title={taskObject.title}
|
||||||
current={currentSketchIndex + 1}
|
current={currentSketchIndex + 1}
|
||||||
taskObject={taskObject}
|
taskObject={taskObject}
|
||||||
|
selectedView={selectedView}
|
||||||
currentLoadingText={currentLoadingText}
|
currentLoadingText={currentLoadingText}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@ -431,11 +442,44 @@ Please process this video editing request.`;
|
|||||||
>
|
>
|
||||||
{isDesktop ? (
|
{isDesktop ? (
|
||||||
<div className={`relative heroVideo-FIzuK1 ${['script'].includes(taskObject.currentStage) ? 'h-[calc(100vh-6rem)] w-[calc((100vh-6rem)/9*16)]' : 'h-[calc(100vh-6rem-200px)] w-[calc((100vh-6rem-200px)/9*16)]'}`} style={{ aspectRatio: "16 / 9" }} key={taskObject.currentStage+'_'+currentSketchIndex}>
|
<div className={`relative heroVideo-FIzuK1 ${['script'].includes(taskObject.currentStage) ? 'h-[calc(100vh-6rem)] w-[calc((100vh-6rem)/9*16)]' : 'h-[calc(100vh-6rem-200px)] w-[calc((100vh-6rem-200px)/9*16)]'}`} style={{ aspectRatio: "16 / 9" }} key={taskObject.currentStage+'_'+currentSketchIndex}>
|
||||||
|
{/* 左侧最终视频缩略图栏(仅桌面) */}
|
||||||
|
{taskObject?.final?.url && (
|
||||||
|
<div
|
||||||
|
className="absolute -left-36 top-0 z-[50]"
|
||||||
|
data-alt="final-sidebar"
|
||||||
|
>
|
||||||
|
<div className={`flex items-start`}>
|
||||||
|
{isFinalBarOpen && (
|
||||||
|
<div
|
||||||
|
className="w-28 max-h-[60vh] overflow-y-auto rounded-lg backdrop-blur-lg bg-black/30 border border-white/20 shadow-xl p-2 mr-2"
|
||||||
|
data-alt="final-thumbnails"
|
||||||
|
>
|
||||||
|
{/* 预留历史列表,目前仅展示当前最终视频 */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setSelectedView('final')}
|
||||||
|
className={`block w-full overflow-hidden rounded-md border ${selectedView === 'final' ? 'border-blue-500' : 'border-white/20'}`}
|
||||||
|
data-alt="final-thumb-item"
|
||||||
|
aria-label="Select final video"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={getFirstFrame(taskObject.final.url)}
|
||||||
|
alt="final"
|
||||||
|
className="w-full h-auto object-cover"
|
||||||
|
/>
|
||||||
|
<div className="text-xs text-white/80 text-center py-1">Final</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<MediaViewer
|
<MediaViewer
|
||||||
taskObject={taskObject}
|
taskObject={taskObject}
|
||||||
scriptData={scriptData}
|
scriptData={scriptData}
|
||||||
currentSketchIndex={currentSketchIndex}
|
currentSketchIndex={currentSketchIndex}
|
||||||
isVideoPlaying={isVideoPlaying}
|
isVideoPlaying={isVideoPlaying}
|
||||||
|
selectedView={selectedView}
|
||||||
onEditModalOpen={handleEditModalOpen}
|
onEditModalOpen={handleEditModalOpen}
|
||||||
onToggleVideoPlay={toggleVideoPlay}
|
onToggleVideoPlay={toggleVideoPlay}
|
||||||
setIsPauseWorkFlow={setIsPauseWorkFlow}
|
setIsPauseWorkFlow={setIsPauseWorkFlow}
|
||||||
@ -459,6 +503,7 @@ Please process this video editing request.`;
|
|||||||
taskObject={taskObject}
|
taskObject={taskObject}
|
||||||
scriptData={scriptData}
|
scriptData={scriptData}
|
||||||
currentSketchIndex={currentSketchIndex}
|
currentSketchIndex={currentSketchIndex}
|
||||||
|
selectedView={selectedView}
|
||||||
mode={mode}
|
mode={mode}
|
||||||
setIsPauseWorkFlow={setIsPauseWorkFlow}
|
setIsPauseWorkFlow={setIsPauseWorkFlow}
|
||||||
setAnyAttribute={setAnyAttribute}
|
setAnyAttribute={setAnyAttribute}
|
||||||
@ -474,6 +519,7 @@ Please process this video editing request.`;
|
|||||||
onGotoCut={generateEditPlan}
|
onGotoCut={generateEditPlan}
|
||||||
isSmartChatBoxOpen={isSmartChatBoxOpen}
|
isSmartChatBoxOpen={isSmartChatBoxOpen}
|
||||||
onRetryVideo={(video_id) => handleRetryVideo(video_id)}
|
onRetryVideo={(video_id) => handleRetryVideo(video_id)}
|
||||||
|
onSelectView={(view) => setSelectedView(view)}
|
||||||
// 临时禁用视频编辑功能: enableVideoEdit={true} onVideoEditDescriptionSubmit={handleVideoEditDescriptionSubmit}
|
// 临时禁用视频编辑功能: enableVideoEdit={true} onVideoEditDescriptionSubmit={handleVideoEditDescriptionSubmit}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -484,9 +530,13 @@ Please process this video editing request.`;
|
|||||||
isDisabledFocus={isEditModalOpen || isPauseWorkFlow || isFocusChatInput}
|
isDisabledFocus={isEditModalOpen || isPauseWorkFlow || isFocusChatInput}
|
||||||
taskObject={taskObject}
|
taskObject={taskObject}
|
||||||
currentSketchIndex={currentSketchIndex}
|
currentSketchIndex={currentSketchIndex}
|
||||||
onSketchSelect={setCurrentSketchIndex}
|
onSketchSelect={(index) => {
|
||||||
|
setSelectedView('video');
|
||||||
|
setCurrentSketchIndex(index);
|
||||||
|
}}
|
||||||
onRetryVideo={handleRetryVideo}
|
onRetryVideo={handleRetryVideo}
|
||||||
className={isDesktop ? 'auto-cols-[20%]' : (isTablet ? 'auto-cols-[25%]' : 'auto-cols-[40%]')}
|
className={isDesktop ? 'auto-cols-[20%]' : (isTablet ? 'auto-cols-[25%]' : 'auto-cols-[40%]')}
|
||||||
|
selectedView={selectedView}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -18,6 +18,8 @@ interface H5MediaViewerProps {
|
|||||||
scriptData: any;
|
scriptData: any;
|
||||||
/** 当前索引(视频/分镜阶段用于定位) */
|
/** 当前索引(视频/分镜阶段用于定位) */
|
||||||
currentSketchIndex: number;
|
currentSketchIndex: number;
|
||||||
|
/** 选中视图:final 或 video,用于覆盖阶段渲染 */
|
||||||
|
selectedView?: 'final' | 'video' | null;
|
||||||
/** 渲染模式(仅剧本阶段透传) */
|
/** 渲染模式(仅剧本阶段透传) */
|
||||||
mode: string;
|
mode: string;
|
||||||
/** 以下为剧本阶段透传必需项(与桌面版保持一致) */
|
/** 以下为剧本阶段透传必需项(与桌面版保持一致) */
|
||||||
@ -39,6 +41,8 @@ interface H5MediaViewerProps {
|
|||||||
isSmartChatBoxOpen?: boolean;
|
isSmartChatBoxOpen?: boolean;
|
||||||
/** 失败重试生成视频 */
|
/** 失败重试生成视频 */
|
||||||
onRetryVideo?: (video_id: string) => void;
|
onRetryVideo?: (video_id: string) => void;
|
||||||
|
/** 切换选择视图(final 或 video) */
|
||||||
|
onSelectView?: (view: 'final' | 'video') => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,6 +54,7 @@ export function H5MediaViewer({
|
|||||||
taskObject,
|
taskObject,
|
||||||
scriptData,
|
scriptData,
|
||||||
currentSketchIndex,
|
currentSketchIndex,
|
||||||
|
selectedView,
|
||||||
mode,
|
mode,
|
||||||
setIsPauseWorkFlow,
|
setIsPauseWorkFlow,
|
||||||
setAnyAttribute,
|
setAnyAttribute,
|
||||||
@ -61,7 +66,8 @@ export function H5MediaViewer({
|
|||||||
showGotoCutButton,
|
showGotoCutButton,
|
||||||
onGotoCut,
|
onGotoCut,
|
||||||
isSmartChatBoxOpen,
|
isSmartChatBoxOpen,
|
||||||
onRetryVideo
|
onRetryVideo,
|
||||||
|
onSelectView
|
||||||
}: H5MediaViewerProps) {
|
}: H5MediaViewerProps) {
|
||||||
const carouselRef = useRef<CarouselRef>(null);
|
const carouselRef = useRef<CarouselRef>(null);
|
||||||
const videoRefs = useRef<Array<HTMLVideoElement | null>>([]);
|
const videoRefs = useRef<Array<HTMLVideoElement | null>>([]);
|
||||||
@ -69,9 +75,12 @@ export function H5MediaViewer({
|
|||||||
const [activeIndex, setActiveIndex] = useState<number>(0);
|
const [activeIndex, setActiveIndex] = useState<number>(0);
|
||||||
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
const [isPlaying, setIsPlaying] = useState<boolean>(false);
|
||||||
const [isCatalogOpen, setIsCatalogOpen] = useState<boolean>(false);
|
const [isCatalogOpen, setIsCatalogOpen] = useState<boolean>(false);
|
||||||
|
const [isFinalBarOpen, setIsFinalBarOpen] = useState<boolean>(true);
|
||||||
|
|
||||||
// 计算当前阶段类型
|
// 计算当前阶段类型
|
||||||
const stage = taskObject.currentStage;
|
const stage = (selectedView === 'final' && taskObject.final?.url)
|
||||||
|
? 'final_video'
|
||||||
|
: (selectedView === 'video' ? 'video' : taskObject.currentStage);
|
||||||
|
|
||||||
// 生成各阶段对应的 slides 数据
|
// 生成各阶段对应的 slides 数据
|
||||||
const videoUrls = useMemo(() => {
|
const videoUrls = useMemo(() => {
|
||||||
@ -385,6 +394,27 @@ export function H5MediaViewer({
|
|||||||
// 其他阶段:使用 Carousel
|
// 其他阶段:使用 Carousel
|
||||||
return (
|
return (
|
||||||
<div ref={rootRef} data-alt="h5-media-viewer" className="w-[100vw] relative px-4">
|
<div ref={rootRef} data-alt="h5-media-viewer" className="w-[100vw] relative px-4">
|
||||||
|
{/* 左侧最终视频缩略图栏(H5) */}
|
||||||
|
{taskObject?.final?.url && (
|
||||||
|
<div className="absolute left-4 top-0 z-[60]" data-alt="final-sidebar-h5">
|
||||||
|
<div className="flex items-start">
|
||||||
|
{isFinalBarOpen && (
|
||||||
|
<div className="w-20 max-h-[50vh] overflow-y-auto rounded-md backdrop-blur-lg bg-black/30 border border-white/20 shadow-xl p-1 mr-2" data-alt="final-thumbnails">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => onSelectView && onSelectView('final')}
|
||||||
|
className={`block w-full overflow-hidden rounded border ${selectedView === 'final' ? 'border-blue-500' : 'border-white/20'}`}
|
||||||
|
data-alt="final-thumb-item"
|
||||||
|
aria-label="Select final video"
|
||||||
|
>
|
||||||
|
<img src={getFirstFrame(taskObject.final.url)} alt="final" className="w-full h-auto object-cover" />
|
||||||
|
<div className="text-[10px] text-white/80 text-center py-0.5">Final</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{stage === 'final_video' && videoUrls.length > 0 && renderVideoSlides()}
|
{stage === 'final_video' && videoUrls.length > 0 && renderVideoSlides()}
|
||||||
{stage === 'video' && videoUrls.length > 0 && renderVideoSlides()}
|
{stage === 'video' && videoUrls.length > 0 && renderVideoSlides()}
|
||||||
{(stage === 'scene' || stage === 'character') && imageUrls.length > 0 && renderImageSlides()}
|
{(stage === 'scene' || stage === 'character') && imageUrls.length > 0 && renderImageSlides()}
|
||||||
|
|||||||
@ -14,6 +14,8 @@ interface H5TaskInfoProps {
|
|||||||
taskObject: TaskObject
|
taskObject: TaskObject
|
||||||
className?: string
|
className?: string
|
||||||
currentLoadingText: string
|
currentLoadingText: string
|
||||||
|
/** 选中视图:final 或 video */
|
||||||
|
selectedView?: 'final' | 'video' | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const H5TaskInfo: React.FC<H5TaskInfoProps> = ({
|
const H5TaskInfo: React.FC<H5TaskInfoProps> = ({
|
||||||
@ -21,7 +23,8 @@ const H5TaskInfo: React.FC<H5TaskInfoProps> = ({
|
|||||||
current,
|
current,
|
||||||
taskObject,
|
taskObject,
|
||||||
className,
|
className,
|
||||||
currentLoadingText
|
currentLoadingText,
|
||||||
|
selectedView
|
||||||
}) => {
|
}) => {
|
||||||
type StageIndex = 0 | 1 | 2 | 3
|
type StageIndex = 0 | 1 | 2 | 3
|
||||||
|
|
||||||
@ -72,6 +75,25 @@ const H5TaskInfo: React.FC<H5TaskInfoProps> = ({
|
|||||||
return 0
|
return 0
|
||||||
}, [taskObject, current, total])
|
}, [taskObject, current, total])
|
||||||
|
|
||||||
|
// 构造副标题文本:优先根据 selectedView 覆盖
|
||||||
|
const subtitle = useMemo(() => {
|
||||||
|
if (selectedView === 'final' && taskObject.final?.url) {
|
||||||
|
return 'Final 1/1'
|
||||||
|
}
|
||||||
|
if (selectedView === 'video') {
|
||||||
|
const videosTotal = taskObject.videos?.total_count || taskObject.videos?.data?.length || 0
|
||||||
|
return `Shots ${Math.max(displayCurrent, 1)}/${Math.max(videosTotal, 1)}`
|
||||||
|
}
|
||||||
|
// 回退到原有逻辑
|
||||||
|
if (taskObject.currentStage === 'video' || taskObject.currentStage === 'final_video') {
|
||||||
|
return `Shots ${Math.max(displayCurrent, 1)}/${Math.max(total, 1)}`
|
||||||
|
}
|
||||||
|
if (taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') {
|
||||||
|
return `Roles & Scenes ${Math.max(displayCurrent, 1)}/${Math.max(total, 1)}`
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}, [selectedView, taskObject, displayCurrent, total])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-alt="h5-header"
|
data-alt="h5-header"
|
||||||
@ -89,9 +111,9 @@ const H5TaskInfo: React.FC<H5TaskInfoProps> = ({
|
|||||||
>
|
>
|
||||||
{title || '...'}
|
{title || '...'}
|
||||||
</h1>
|
</h1>
|
||||||
{shouldShowCount && (
|
{shouldShowCount && subtitle && (
|
||||||
<span data-alt="shot-count" className="flex items-center gap-4 text-sm text-slate-300">
|
<span data-alt="shot-count" className="flex items-center gap-4 text-sm text-slate-300">
|
||||||
{taskObject.currentStage === 'video' ? 'Shots' : 'Roles & Scenes '} {displayCurrent}/{Math.max(total, 1)}
|
{subtitle}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@ interface MediaViewerProps {
|
|||||||
scriptData: any;
|
scriptData: any;
|
||||||
currentSketchIndex: number;
|
currentSketchIndex: number;
|
||||||
isVideoPlaying: boolean;
|
isVideoPlaying: boolean;
|
||||||
|
selectedView?: 'final' | 'video' | null;
|
||||||
onEditModalOpen: (tab: string) => void;
|
onEditModalOpen: (tab: string) => void;
|
||||||
onToggleVideoPlay: () => void;
|
onToggleVideoPlay: () => void;
|
||||||
setIsPauseWorkFlow: (isPause: boolean) => void;
|
setIsPauseWorkFlow: (isPause: boolean) => void;
|
||||||
@ -44,6 +45,7 @@ export const MediaViewer = React.memo(function MediaViewer({
|
|||||||
scriptData,
|
scriptData,
|
||||||
currentSketchIndex,
|
currentSketchIndex,
|
||||||
isVideoPlaying,
|
isVideoPlaying,
|
||||||
|
selectedView,
|
||||||
onEditModalOpen,
|
onEditModalOpen,
|
||||||
onToggleVideoPlay,
|
onToggleVideoPlay,
|
||||||
setIsPauseWorkFlow,
|
setIsPauseWorkFlow,
|
||||||
@ -734,20 +736,25 @@ export const MediaViewer = React.memo(function MediaViewer({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 计算生效阶段:selectedView 优先于 taskObject.currentStage
|
||||||
|
const effectiveStage = (selectedView === 'final' && taskObject.final?.url)
|
||||||
|
? 'final_video'
|
||||||
|
: (selectedView === 'video' ? 'video' : taskObject.currentStage);
|
||||||
|
|
||||||
// 根据当前步骤渲染对应内容
|
// 根据当前步骤渲染对应内容
|
||||||
if (taskObject.currentStage === 'final_video') {
|
if (effectiveStage === 'final_video') {
|
||||||
return renderFinalVideo();
|
return renderFinalVideo();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taskObject.currentStage === 'video') {
|
if (effectiveStage === 'video') {
|
||||||
return renderVideoContent(onGotoCut);
|
return renderVideoContent(onGotoCut);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taskObject.currentStage === 'script') {
|
if (effectiveStage === 'script') {
|
||||||
return renderScriptContent();
|
return renderScriptContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') {
|
if (effectiveStage === 'scene' || effectiveStage === 'character') {
|
||||||
return renderSketchContent([...taskObject.roles.data, ...taskObject.scenes.data][currentSketchIndex]);
|
return renderSketchContent([...taskObject.roles.data, ...taskObject.scenes.data][currentSketchIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ interface ThumbnailGridProps {
|
|||||||
onSketchSelect: (index: number) => void;
|
onSketchSelect: (index: number) => void;
|
||||||
onRetryVideo: (video_id: string) => void;
|
onRetryVideo: (video_id: string) => void;
|
||||||
className: string;
|
className: string;
|
||||||
|
selectedView?: 'final' | 'video' | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,7 +27,8 @@ export function ThumbnailGrid({
|
|||||||
currentSketchIndex,
|
currentSketchIndex,
|
||||||
onSketchSelect,
|
onSketchSelect,
|
||||||
onRetryVideo,
|
onRetryVideo,
|
||||||
className
|
className,
|
||||||
|
selectedView
|
||||||
}: ThumbnailGridProps) {
|
}: ThumbnailGridProps) {
|
||||||
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
@ -185,7 +187,7 @@ export function ThumbnailGrid({
|
|||||||
<div
|
<div
|
||||||
key={`video-${urls}-${index}`}
|
key={`video-${urls}-${index}`}
|
||||||
className={`relative aspect-video rounded-lg overflow-hidden
|
className={`relative aspect-video rounded-lg overflow-hidden
|
||||||
${(currentSketchIndex === index && !disabled) ? 'ring-2 ring-blue-500 z-10' : 'hover:ring-2 hover:ring-blue-500/50'}`}
|
${(currentSketchIndex === index && !disabled && selectedView !== 'final') ? 'ring-2 ring-blue-500 z-10' : 'hover:ring-2 hover:ring-blue-500/50'}`}
|
||||||
onClick={() => !isDragging && !disabled && onSketchSelect(index)}
|
onClick={() => !isDragging && !disabled && onSketchSelect(index)}
|
||||||
>
|
>
|
||||||
|
|
||||||
@ -294,6 +296,7 @@ export function ThumbnailGrid({
|
|||||||
className="w-full h-full object-cover select-none"
|
className="w-full h-full object-cover select-none"
|
||||||
src={sketch.url}
|
src={sketch.url}
|
||||||
draggable="false"
|
draggable="false"
|
||||||
|
alt={sketch.type ? String(sketch.type) : 'sketch'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -343,9 +346,8 @@ export function ThumbnailGrid({
|
|||||||
onFocus={() => setIsFocused(true)}
|
onFocus={() => setIsFocused(true)}
|
||||||
onBlur={() => setIsFocused(false)}
|
onBlur={() => setIsFocused(false)}
|
||||||
>
|
>
|
||||||
{taskObject.currentStage === 'video' && renderVideoThumbnails()}
|
{(taskObject.currentStage === 'video' || taskObject.currentStage === 'final_video') && renderVideoThumbnails()}
|
||||||
{(taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') && renderSketchThumbnails(getCurrentData())}
|
{(taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') && renderSketchThumbnails(getCurrentData())}
|
||||||
{taskObject.currentStage === 'final_video' && renderVideoThumbnails(true)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user