auto-save 2026-05-13 15:22 (~5)

This commit is contained in:
2026-05-13 15:22:51 +08:00
parent 6390472c27
commit de1254ff10
5 changed files with 167 additions and 39 deletions

View File

@@ -31,6 +31,7 @@ export interface NodeData {
onDeleteFrame?: (idx: number) => void // 删整张关键帧
onDeleteGenerated?: (frameIdx: number, genId: string) => void // 删单张生成图
onOpenStoryboard?: (frameIdx: number) => void // 打开分镜头编排专属面板
onPushToStoryboard?: (payload: { kind: "keyframe" | "cutout"; frameIdx: number; elementId?: string; cutoutId?: string; label?: string }) => void
}
/* ---- 状态映射工具 ---- */
@@ -408,6 +409,23 @@ export function KeyframeNode({ data, selected }: any) {
{f.timestamp.toFixed(1)}s
</div>
</button>
{/* 上推按钮hover 时浮出 — 推送关键帧本身到分镜头编排 */}
{d.onPushToStoryboard && (
<button
onClick={(e) => {
e.stopPropagation()
d.onPushToStoryboard?.({
kind: "keyframe",
frameIdx: f.index,
label: `分镜 ${f.index + 1} 关键帧`,
})
}}
title="⬆ 上推到分镜头编排"
className="absolute top-1 left-1 h-5 w-5 rounded-full bg-black/70 backdrop-blur text-white/85 hover:bg-violet-500 hover:text-white inline-flex items-center justify-center opacity-0 group-hover:opacity-100 transition z-[70] text-[12px] leading-none font-bold"
>
</button>
)}
{/* 删除按钮hover 时右上角浮出 */}
{d.onDeleteFrame && (
<button
@@ -607,14 +625,17 @@ export function ImageGenNode({ data, selected }: any) {
useEffect(() => setMounted(true), [])
// 上方浮条 = 所有 frame 的 elements 已提取图("分镜头编排"的输入素材)
type ElPreview = { frameIdx: number; elementId: string; name: string; src: string }
type ElPreview = { frameIdx: number; elementId: string; name: string; src: string; cid: string }
const elementCrops: ElPreview[] = job
? job.frames.flatMap((f) =>
(f.elements ?? [])
.filter((e) => hasCutout(e))
.map((e) => {
const src = representativeCutoutUrl(job.id, f.index, e) || ""
return { frameIdx: f.index, elementId: e.id, name: e.name_zh, src }
const cid = (e.cutouts && e.cutouts.length > 0)
? e.cutouts[e.cutouts.length - 1]
: (e.cutout_id ?? "")
return { frameIdx: f.index, elementId: e.id, name: e.name_zh, src, cid }
})
.filter((p) => p.src),
)
@@ -637,14 +658,14 @@ export function ImageGenNode({ data, selected }: any) {
return (
<div
key={key}
className="relative rounded-md border border-violet-300/50 transition shadow-lg hover:-translate-y-0.5 bg-black/40 overflow-hidden"
className="group relative rounded-md border border-violet-300/50 transition shadow-lg hover:-translate-y-0.5 bg-white overflow-hidden"
style={{ aspectRatio: aspect }}
>
<button
onClick={(e) => { e.stopPropagation(); d.onOpenStoryboard?.(p.frameIdx) }}
onMouseEnter={(e) => setHover({ key, rect: (e.currentTarget as HTMLElement).getBoundingClientRect() })}
onMouseLeave={() => setHover(null)}
title={`${p.name} · 来自分镜 ${p.frameIdx + 1} · hover 看大图 · 点击进入分镜头编排`}
title={`${p.name} · 来自分镜 ${p.frameIdx + 1} · hover 看大图`}
className="absolute inset-0 w-full h-full"
>
<img
@@ -653,6 +674,25 @@ export function ImageGenNode({ data, selected }: any) {
className="absolute inset-0 w-full h-full object-contain"
/>
</button>
{/* 上推按钮hover 时浮出 */}
{d.onPushToStoryboard && (
<button
onClick={(e) => {
e.stopPropagation()
d.onPushToStoryboard?.({
kind: "cutout",
frameIdx: p.frameIdx,
elementId: p.elementId,
cutoutId: p.cid,
label: p.name,
})
}}
title="⬆ 上推到分镜头编排"
className="absolute top-1 left-1 h-5 w-5 rounded-full bg-black/70 backdrop-blur text-white/85 hover:bg-violet-500 hover:text-white inline-flex items-center justify-center opacity-0 group-hover:opacity-100 transition z-[70] text-[12px] leading-none font-bold"
>
</button>
)}
</div>
)
})}