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