auto-save 2026-05-17 22:35 (~3)

This commit is contained in:
2026-05-17 22:35:50 +08:00
parent d3bcceda9d
commit 18d2c5e426
3 changed files with 65 additions and 11 deletions

View File

@@ -1255,6 +1255,7 @@ function SourceReferenceBuildPanel({
const [extracting, setExtracting] = useState(false)
const [subjectBusy, setSubjectBusy] = useState(false)
const [deletingFrame, setDeletingFrame] = useState<number | null>(null)
const [framePreview, setFramePreview] = useState<{ index: number; left: number; top: number } | null>(null)
const frames = useMemo(() => [...job.frames].sort((a, b) => a.timestamp - b.timestamp), [job.frames])
const selectedReferenceFrames = useMemo(
() => frames.filter((frame) => selectedFrames.has(frame.index)),
@@ -1273,6 +1274,7 @@ function SourceReferenceBuildPanel({
return null
}, [frames, selectedReferenceFrames])
const actorAssets = actorSource?.element.subject_assets ?? []
const previewFrame = framePreview ? frames.find((frame) => frame.index === framePreview.index) ?? null : null
const extractKeyframes = async () => {
setExtracting(true)
@@ -1345,8 +1347,44 @@ function SourceReferenceBuildPanel({
}
}
const updateFramePreviewPosition = (event: ReactMouseEvent<HTMLDivElement>, frameIndex: number) => {
const margin = 16
const previewWidth = Math.min(340, window.innerWidth - margin * 2)
const previewHeight = previewWidth * 16 / 9 + 44
let left = event.clientX + 18
let top = event.clientY + 18
if (left + previewWidth > window.innerWidth - margin) {
left = event.clientX - previewWidth - 18
}
if (top + previewHeight > window.innerHeight - margin) {
top = window.innerHeight - previewHeight - margin
}
setFramePreview({
index: frameIndex,
left: Math.max(margin, left),
top: Math.max(margin, top),
})
}
const framePreviewPortal = framePreview && previewFrame && typeof document !== "undefined"
? createPortal(
<div
className="pointer-events-none fixed z-[9999] w-[min(340px,calc(100vw-32px))] rounded-xl border border-white/15 bg-black/94 p-3 shadow-[0_28px_80px_rgba(0,0,0,0.72)]"
style={{ left: framePreview.left, top: framePreview.top }}
>
<img src={effectiveFrameUrl(job.id, previewFrame)} alt="" className="aspect-[9/16] w-full rounded-lg bg-black object-contain" />
<div className="mt-2 flex items-center justify-between gap-3 text-[11px] text-white/62">
<span> {String(frames.findIndex((frame) => frame.index === previewFrame.index) + 1).padStart(2, "0")}</span>
<span className="font-mono">{previewFrame.timestamp.toFixed(1)}s</span>
</div>
</div>,
document.body,
)
: null
return (
<div className="min-w-0">
{framePreviewPortal}
<div className="mb-2 flex items-center justify-between gap-3">
<SectionTitle icon={<ImageIcon className="h-4 w-4" />} title="关键帧 / 相似主角" />
<span className="rounded-md border border-white/10 bg-black/35 px-2 py-1 text-[11px] text-white/45">
@@ -1382,6 +1420,9 @@ function SourceReferenceBuildPanel({
return (
<div
key={frame.index}
onMouseEnter={(event) => updateFramePreviewPosition(event, frame.index)}
onMouseMove={(event) => updateFramePreviewPosition(event, frame.index)}
onMouseLeave={() => setFramePreview(null)}
className={`group relative aspect-[9/16] overflow-hidden rounded border bg-black transition ${
selected ? "border-emerald-300/70" : "border-white/10 hover:border-cyan-300/40"
}`}