auto-save 2026-05-13 15:22 (~5)
This commit is contained in:
@@ -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>
|
||||
)
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user