"use client" import { useEffect, useState } from "react" import { createPortal } from "react-dom" import { X, LayoutGrid, Sparkle, Wand2, Brush } from "lucide-react" import { type Job, effectiveFrameUrl, cutoutUrl } from "@/lib/api" interface Props { job: Job | null frameIndex: number | null onClose: () => void } export function StoryboardEditor({ job, frameIndex, onClose }: Props) { const [mounted, setMounted] = useState(false) useEffect(() => setMounted(true), []) useEffect(() => { if (frameIndex === null) return const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose() } window.addEventListener("keydown", onKey) return () => window.removeEventListener("keydown", onKey) }, [frameIndex, onClose]) if (!mounted || !job || frameIndex === null) return null const frame = job.frames.find((f) => f.index === frameIndex) if (!frame) return null const elements = frame.elements ?? [] const elementsWithCutout = elements.filter((e) => e.cutout_id) const seq = job.frames.filter((f) => f.timestamp <= frame.timestamp).length return createPortal(
e.stopPropagation()} className="w-full max-w-5xl bg-black/60 rounded-2xl border border-white/15 overflow-hidden flex flex-col" style={{ maxHeight: "88vh" }} > {/* 顶部栏 — 分镜头编排用紫粉渐变 */}
分镜头编排 分镜 {seq} · {frame.timestamp.toFixed(2)}s
{/* 主体 — 左大图 + 右素材 / 操作 */}
{/* 左:分镜大图 */}
{`frame
{frame.cleaned_applied ? "✨ 已清洗版本" : "原图"}
{/* 右:元素 + Phase 2 操作占位 */}
该分镜的元素清单 · {elementsWithCutout.length}/{elements.length} 已裁切
{elements.length === 0 ? (
暂无元素 · 到关键帧节点画框提取后会出现在这里
) : (
{elements.map((e) => (
{e.cutout_id ? ( {e.name_zh} ) : (
)}
{e.name_zh}
{e.name_en || "(无英文)"}
))}
)}
{/* 编排操作占位(Phase 2) */}
编排操作 · Phase 2 待实施
这些功能稍后填入。当前先用关键帧节点的「清洗 + 提取」准备好素材。
{/* 底部 */}
ESC 关闭
, document.body, ) }