auto-save 2026-05-21 02:25 (~2)

This commit is contained in:
2026-05-21 02:25:21 +08:00
parent da12ed0bbb
commit 7524b3caf1
2 changed files with 67 additions and 46 deletions

View File

@@ -1874,6 +1874,13 @@
"message": "auto-save 2026-05-21 02:09 (~5)", "message": "auto-save 2026-05-21 02:09 (~5)",
"hash": "fa6e32b", "hash": "fa6e32b",
"files_changed": 5 "files_changed": 5
},
{
"ts": "2026-05-21T02:19:55+08:00",
"type": "commit",
"message": "auto-save 2026-05-21 02:19 (~2)",
"hash": "da12ed0",
"files_changed": 2
} }
] ]
} }

View File

@@ -427,77 +427,91 @@ function VideoSection({ videoLoading, primaryImage, locked, session, onGenerateV
</div> </div>
</div> </div>
<div className="px-4 pb-4 border-t border-white/[0.05] space-y-2 pt-3"> <div className="grid grid-cols-1 gap-3 border-t border-white/[0.05] p-4 xl:grid-cols-2">
{videoItems.map(item => { {videoItems.map(item => {
const isOpen = showPromptId === item.id; const isOpen = showPromptId === item.id;
const task = byTemplate.get(item.id); const task = byTemplate.get(item.id);
const loadingThis = videoLoading === item.id; const loadingThis = videoLoading === item.id;
return ( return (
<div key={item.id} className="grid grid-cols-[72px_minmax(0,1fr)_auto] gap-3 p-3 rounded-[8px] bg-white/[0.025] ring-1 ring-white/[0.05] hover:ring-white/[0.1] transition-all"> <div key={item.id} className="min-w-0 overflow-hidden rounded-[8px] bg-white/[0.025] ring-1 ring-white/[0.05] transition-all hover:ring-white/[0.12]">
<div className="aspect-square rounded-[8px] bg-gradient-to-br from-[#e6f578]/15 to-[#8cb478]/15 ring-1 ring-[#e6f578]/20 flex flex-col items-center justify-center text-[#e6f578] text-[9px] font-mono gap-1"> <div className="relative bg-black/70" style={{ aspectRatio: item.ratio.replace(':', ' / ') }}>
{task?.videoUrl ? ( {task?.videoUrl ? (
<div className="relative h-full w-full overflow-hidden rounded-[8px] bg-black"> <HoverVideoPreview
<HoverVideoPreview src={task.videoUrl}
src={task.videoUrl} aspectRatio={item.ratio}
aspectRatio={item.ratio} className="h-full w-full object-contain"
className="h-full w-full object-contain" />
/>
<span className="pointer-events-none absolute bottom-1 left-1 rounded-[6px] bg-black/70 px-1.5 py-0.5 text-[8px] font-semibold text-[#e6f578]">
{item.duration}s
</span>
</div>
) : ( ) : (
<> <div className="flex h-full w-full flex-col items-center justify-center gap-1 bg-gradient-to-br from-[#e6f578]/12 to-[#8cb478]/12 text-[#e6f578]">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"> <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M8 5.2v13.6L18.5 12 8 5.2z" /> <path d="M8 5.2v13.6L18.5 12 8 5.2z" />
</svg> </svg>
<span className="text-[8px]">{item.duration}s</span> <span className="text-[10px] font-mono">{item.duration}s</span>
</> </div>
)} )}
</div> <div className="pointer-events-none absolute inset-x-0 bottom-0 flex items-center justify-between bg-gradient-to-t from-black/82 to-transparent p-2">
<div className="min-w-0 space-y-1"> <span className="rounded-[6px] bg-black/58 px-2 py-0.5 text-[9px] font-semibold text-white/72">{item.ratio}</span>
<div className="flex items-center gap-1.5 flex-wrap"> <span className="rounded-[6px] bg-[#e6f578]/90 px-2 py-0.5 text-[9px] font-bold text-[#081006]">{item.duration}s</span>
<span className="text-[13px] font-medium text-white">{item.title}</span> </div>
<span className="chip chip-neutral text-[10px] py-0">{item.ratio}</span> </div>
{!item.template && <span className="chip chip-live text-[10px] py-0"></span>} <div className="space-y-2 p-3">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<div className="flex items-center gap-1.5 flex-wrap">
<span className="text-[13px] font-semibold text-white">{item.title}</span>
{!item.template && <span className="chip chip-live text-[10px] py-0"></span>}
</div>
<p className="mt-1 line-clamp-2 text-[11px] leading-relaxed text-white/45">{item.description}</p>
</div>
<span className={`shrink-0 text-[10px] ${task ? 'text-[#dff5a8]' : 'text-white/25'}`}>
{task?.status ?? '待提交'}
</span>
</div> </div>
<p className="text-[11px] text-white/45 line-clamp-1">{item.description}</p>
{task && ( {task && (
<div className="mt-2 rounded-lg bg-black/28 p-2 text-[10px] leading-relaxed text-white/48 ring-1 ring-white/[0.06]"> <div className="rounded-lg bg-black/28 p-2 text-[10px] leading-relaxed text-white/48 ring-1 ring-white/[0.06]">
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
<span className="text-[#dff5a8]">{task.status}</span> <span className="text-[#dff5a8]">{task.status}</span>
{task.taskId && <span className="font-mono text-white/34">{task.taskId}</span>} {task.taskId && <span className="font-mono text-white/34">{task.taskId}</span>}
{task.videoUrl && (
<a href={task.videoUrl} target="_blank" rel="noreferrer" className="text-[#e6f578] hover:text-white">
</a>
)}
</div> </div>
</div> </div>
)} )}
<button <div className="flex flex-wrap items-center justify-between gap-2">
onClick={() => setShowPromptId(isOpen ? null : item.id)} <button
className="text-[10px] text-white/30 hover:text-[#e6f578] transition-colors flex items-center gap-1" onClick={() => setShowPromptId(isOpen ? null : item.id)}
> className="text-[10px] text-white/30 hover:text-[#e6f578] transition-colors flex items-center gap-1"
<svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"> >
<path d={isOpen ? 'M6 9l6 6 6-6' : 'M9 6l6 6-6 6'} strokeLinecap="round" /> <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
</svg> <path d={isOpen ? 'M6 9l6 6 6-6' : 'M9 6l6 6-6 6'} strokeLinecap="round" />
Prompt </svg>
</button> Prompt
</button>
<div className="flex items-center gap-2">
{task?.videoUrl && (
<>
<a href={task.videoUrl} target="_blank" rel="noreferrer" className="btn btn-outline px-3 py-1.5 text-[11px]">
</a>
<a href={task.videoUrl} download={`${session.id}_${item.id}.mp4`} className="btn btn-outline px-3 py-1.5 text-[11px]">
</a>
</>
)}
<button
onClick={() => task?.taskId ? onRefreshVideo(task.taskId) : item.template ? onGenerateVideo(primaryImage, item.template) : undefined}
disabled={Boolean(videoLoading) || locked || task?.status === 'succeeded' || (!item.template && !task?.taskId)}
className="btn btn-primary text-[11px] px-3 py-1.5 disabled:opacity-40"
title={locked ? '四个图片包完成后解锁视频任务' : undefined}
>
{locked ? '锁定' : loadingThis ? '...' : task ? task.status === 'succeeded' ? '完成' : '刷新' : '提交'}
</button>
</div>
</div>
{isOpen && ( {isOpen && (
<pre className="p-2 text-[10px] text-white/60 bg-black/40 rounded-lg ring-1 ring-white/[0.07] font-mono whitespace-pre-wrap break-all max-h-28 overflow-y-auto"> <pre className="p-2 text-[10px] text-white/60 bg-black/40 rounded-lg ring-1 ring-white/[0.07] font-mono whitespace-pre-wrap break-all max-h-28 overflow-y-auto">
{item.promptTemplate} {item.promptTemplate}
</pre> </pre>
)} )}
</div> </div>
<button
onClick={() => task?.taskId ? onRefreshVideo(task.taskId) : item.template ? onGenerateVideo(primaryImage, item.template) : undefined}
disabled={Boolean(videoLoading) || locked || task?.status === 'succeeded' || (!item.template && !task?.taskId)}
className="self-center btn btn-primary text-[11px] px-3 py-1.5 disabled:opacity-40"
title={locked ? '四个图片包完成后解锁视频任务' : undefined}
>
{locked ? '锁定' : loadingThis ? '...' : task ? task.status === 'succeeded' ? '完成' : '刷新' : '提交'}
</button>
</div> </div>
); );
})} })}