auto-save 2026-05-17 22:35 (~3)
This commit is contained in:
@@ -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"
|
||||
}`}
|
||||
|
||||
Reference in New Issue
Block a user