feat: simplify keyframe selection pool
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -2044,77 +2044,40 @@ function SourceKeyframePicker({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid min-h-[205px] gap-1.5 rounded-md border border-white/10 bg-black/32 p-1.5 xl:grid-cols-[minmax(0,1fr)_86px] 2xl:min-h-[260px] 2xl:grid-cols-[minmax(0,1fr)_96px]">
|
||||
<div className="min-w-0">
|
||||
<div className="mb-1 flex items-center justify-between gap-2">
|
||||
<span className="text-[10px] text-white/34">参考帧池</span>
|
||||
<span className="text-[9.5px] text-white/28">悬停放大</span>
|
||||
</div>
|
||||
<div className="grid max-h-[178px] grid-cols-[repeat(auto-fill,minmax(38px,1fr))] gap-1 overflow-y-auto pr-0.5 2xl:max-h-[232px]">
|
||||
{frames.map((frame, index) => {
|
||||
const selected = selectedFrames.has(frame.index)
|
||||
return (
|
||||
<MediaAssetTile
|
||||
key={frame.index}
|
||||
src={effectiveFrameUrl(job.id, frame)}
|
||||
alt={`关键帧 ${index + 1}`}
|
||||
label={`参考帧 ${String(index + 1).padStart(2, "0")}`}
|
||||
meta={`${frame.timestamp.toFixed(1)}s`}
|
||||
className="aspect-[9/16]"
|
||||
objectFit="contain"
|
||||
selected={selected}
|
||||
title={`关键帧 ${index + 1} · ${frame.timestamp.toFixed(1)}s`}
|
||||
onClick={() => onToggleFrame(frame.index)}
|
||||
topLeft={<span className="rounded bg-black/72 px-1 font-mono text-[9px] text-white/70">{String(index + 1).padStart(2, "0")}</span>}
|
||||
topRight={<span className="rounded-full bg-black/72 p-0.5">{selected ? <Check className="h-3 w-3 text-emerald-200" /> : <Circle className="h-3 w-3 text-white/50" />}</span>}
|
||||
onDelete={onDeleteFrame ? () => onDeleteFrame(frame.index) : undefined}
|
||||
deleting={deletingFrame === frame.index}
|
||||
deleteLabel={`删除关键帧 ${index + 1}`}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{!frames.length && (
|
||||
<div className="col-span-full flex h-[106px] items-center justify-center rounded border border-dashed border-white/12 px-2 text-center text-[10.5px] leading-snug text-white/34">
|
||||
自动抽帧或在原版视频上用当前点抽帧。
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="min-h-[205px] rounded-md border border-white/10 bg-black/32 p-1.5 2xl:min-h-[260px]">
|
||||
<div className="mb-1 flex items-center justify-between gap-2">
|
||||
<span className="text-[10px] text-white/34">参考帧池</span>
|
||||
<span className="text-[9.5px] text-white/28">点选即为已选,悬停放大</span>
|
||||
</div>
|
||||
|
||||
<aside className="min-w-0 rounded-md border border-emerald-300/12 bg-emerald-300/[0.035] p-1">
|
||||
<div className="mb-1 flex items-center justify-between gap-1">
|
||||
<span className="text-[9.5px] font-semibold text-emerald-50/70">已选</span>
|
||||
<span className="font-mono text-[9px] text-emerald-50/55">{selectedReferenceFrames.length}</span>
|
||||
</div>
|
||||
{selectedReferenceFrames.length ? (
|
||||
<div className="grid max-h-[168px] grid-cols-2 gap-1 overflow-y-auto pr-0.5 2xl:max-h-[222px]">
|
||||
{selectedReferenceFrames.map((frame) => {
|
||||
const order = frames.findIndex((item) => item.index === frame.index)
|
||||
const label = order >= 0 ? String(order + 1).padStart(2, "0") : String(frame.index)
|
||||
return (
|
||||
<MediaAssetTile
|
||||
key={frame.index}
|
||||
src={effectiveFrameUrl(job.id, frame)}
|
||||
alt={`已选关键帧 ${label}`}
|
||||
label={`已选 ${label}`}
|
||||
meta={`${frame.timestamp.toFixed(1)}s`}
|
||||
className="aspect-[9/16]"
|
||||
objectFit="contain"
|
||||
selected
|
||||
title={`点击取消选择 · ${frame.timestamp.toFixed(1)}s`}
|
||||
onClick={() => onToggleFrame(frame.index)}
|
||||
topLeft={<span className="rounded bg-black/72 px-1 font-mono text-[8.5px] text-emerald-100/80">{label}</span>}
|
||||
topRight={<span className="rounded-full bg-black/72 p-0.5"><Check className="h-2.5 w-2.5 text-emerald-200" /></span>}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-[90px] items-center justify-center rounded border border-dashed border-emerald-300/12 px-1 text-center text-[9.5px] leading-snug text-white/30">
|
||||
未选时默认使用全部帧。
|
||||
<div className="grid max-h-[178px] grid-cols-[repeat(auto-fill,minmax(38px,1fr))] gap-1 overflow-y-auto pr-0.5 2xl:max-h-[232px]">
|
||||
{frames.map((frame, index) => {
|
||||
const selected = selectedFrames.has(frame.index)
|
||||
return (
|
||||
<MediaAssetTile
|
||||
key={frame.index}
|
||||
src={effectiveFrameUrl(job.id, frame)}
|
||||
alt={`关键帧 ${index + 1}`}
|
||||
label={`参考帧 ${String(index + 1).padStart(2, "0")}`}
|
||||
meta={`${frame.timestamp.toFixed(1)}s`}
|
||||
className="aspect-[9/16]"
|
||||
objectFit="contain"
|
||||
selected={selected}
|
||||
title={`${selected ? "已选 · 点击取消" : "点击选择"} · 关键帧 ${index + 1} · ${frame.timestamp.toFixed(1)}s`}
|
||||
onClick={() => onToggleFrame(frame.index)}
|
||||
topLeft={<span className="rounded bg-black/72 px-1 font-mono text-[9px] text-white/70">{String(index + 1).padStart(2, "0")}</span>}
|
||||
topRight={<span className="rounded-full bg-black/72 p-0.5">{selected ? <Check className="h-3 w-3 text-emerald-200" /> : <Circle className="h-3 w-3 text-white/50" />}</span>}
|
||||
onDelete={onDeleteFrame ? () => onDeleteFrame(frame.index) : undefined}
|
||||
deleting={deletingFrame === frame.index}
|
||||
deleteLabel={`删除关键帧 ${index + 1}`}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{!frames.length && (
|
||||
<div className="col-span-full flex h-[106px] items-center justify-center rounded border border-dashed border-white/12 px-2 text-center text-[10.5px] leading-snug text-white/34">
|
||||
自动抽帧或在原版视频上用当前点抽帧。
|
||||
</div>
|
||||
)}
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user