auto-save 2026-05-13 14:43 (~6)

This commit is contained in:
2026-05-13 14:44:00 +08:00
parent 9421836a6d
commit 59f6c16225
6 changed files with 106 additions and 69 deletions

View File

@@ -7,7 +7,7 @@ import {
Mic, Languages, FileEdit, Sparkles, Film, FileVideo, Loader2, Plus, X, LayoutGrid,
} from "lucide-react"
import { NodeShell, type NodeStatus, type NodeKind } from "./node-shell"
import { type Job, frameUrl, effectiveFrameUrl, videoUrl, generatedImageUrl, cutoutUrl } from "@/lib/api"
import { type Job, frameUrl, effectiveFrameUrl, videoUrl, generatedImageUrl, cutoutUrl, hasCutout, representativeCutoutUrl } from "@/lib/api"
export interface NodeData {
job: Job | null // 当前 active job
@@ -389,13 +389,13 @@ export function KeyframeNode({ data, selected }: any) {
{isSel && (
<div className="absolute inset-0 bg-emerald-400/15 rounded-md pointer-events-none" />
)}
{(f.cleaned_url || (f.elements?.some((e) => e.cutout_id))) && (
{(f.cleaned_url || (f.elements?.some((e) => hasCutout(e)))) && (
<div className="absolute top-0 left-0 flex items-center gap-0.5 px-1 py-0.5 rounded-br-md leading-none">
{f.cleaned_url && (
<span title="已清洗" className="bg-cyan-500/85 text-white text-[8px] font-bold px-1 py-0.5 rounded-sm"></span>
)}
{(() => {
const cutN = f.elements?.filter((e) => e.cutout_id).length ?? 0
const cutN = f.elements?.filter((e) => hasCutout(e)).length ?? 0
return cutN > 0 ? (
<span title={`${cutN} 个元素已抠图`} className="bg-violet-500/85 text-white text-[8px] font-mono font-bold px-1 py-0.5 rounded-sm">
{cutN}
@@ -440,7 +440,7 @@ export function KeyframeNode({ data, selected }: any) {
{frames.length > 0 ? (() => {
const cleanedCount = frames.filter((x) => x.cleaned_url).length
const elementsCount = frames.reduce((s, x) => s + (x.elements?.length ?? 0), 0)
const cutoutCount = frames.reduce((s, x) => s + (x.elements?.filter((e) => e.cutout_id).length ?? 0), 0)
const cutoutCount = frames.reduce((s, x) => s + (x.elements?.filter((e) => hasCutout(e)).length ?? 0), 0)
return (
<div className="text-[11.5px] leading-relaxed text-[var(--text-soft)]">
<span className="text-[var(--text-strong)] font-medium">{frames.length}</span>
@@ -606,13 +606,17 @@ export function ImageGenNode({ data, selected }: any) {
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])
// 上方浮条 = 所有 frame 的 elements crop"分镜头编排"的输入素材)
type ElPreview = { frameIdx: number; elementId: string; name: string }
// 上方浮条 = 所有 frame 的 elements 已提取图"分镜头编排"的输入素材)
type ElPreview = { frameIdx: number; elementId: string; name: string; src: string }
const elementCrops: ElPreview[] = job
? job.frames.flatMap((f) =>
(f.elements ?? [])
.filter((e) => !!e.cutout_id)
.map((e) => ({ frameIdx: f.index, elementId: e.id, name: e.name_zh })),
.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 }
})
.filter((p) => p.src),
)
: []
@@ -644,7 +648,7 @@ export function ImageGenNode({ data, selected }: any) {
className="absolute inset-0 w-full h-full"
>
<img
src={cutoutUrl(job.id, p.frameIdx, p.elementId)}
src={p.src}
alt={p.name}
className="absolute inset-0 w-full h-full object-contain"
/>
@@ -704,7 +708,7 @@ export function ImageGenNode({ data, selected }: any) {
>
<div className="rounded-2xl overflow-hidden border border-white/25 bg-black" style={{ boxShadow: "0 30px 80px -10px rgba(0,0,0,0.85), 0 0 0 1px rgba(255,255,255,0.06)" }}>
<img
src={cutoutUrl(job.id, p.frameIdx, p.elementId)}
src={p.src}
alt={`preview ${p.elementId}`}
className="block"
style={{ width: w, height: h, objectFit: "contain" }}