From 3958b51f54e9758d52bf67b1bc97989233ad339b Mon Sep 17 00:00:00 2001 From: kang Date: Wed, 13 May 2026 09:42:47 +0800 Subject: [PATCH] auto-save 2026-05-13 09:42 (~2) --- .memory/worklog.json | 13 +++++ web/components/lightbox.tsx | 98 +++++++++++++++++++++++++++++++------ 2 files changed, 95 insertions(+), 16 deletions(-) diff --git a/.memory/worklog.json b/.memory/worklog.json index 4dbc0ee..508b5fa 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1131,6 +1131,19 @@ "message": "auto-save 2026-05-13 09:31 (~3)", "hash": "fdc3162", "files_changed": 3 + }, + { + "ts": "2026-05-13T09:37:15+08:00", + "type": "commit", + "message": "auto-save 2026-05-13 09:37 (~4)", + "hash": "839a3f6", + "files_changed": 4 + }, + { + "ts": "2026-05-13T01:37:36Z", + "type": "session-heartbeat", + "message": "Claude 会话活跃 · 最近命令:claude · 1 项未提交变更 · 最近提交:auto-save 2026-05-13 09:37 (~4)", + "files_changed": 1 } ] } diff --git a/web/components/lightbox.tsx b/web/components/lightbox.tsx index a1e8956..8c9f63c 100644 --- a/web/components/lightbox.tsx +++ b/web/components/lightbox.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react" import { createPortal } from "react-dom" import { X, ChevronLeft, ChevronRight, Check, Sparkles, Wand2, Loader2, Eye, RefreshCw, Plus } from "lucide-react" -import { frameUrl, describeFrame, translateText, type KeyFrame, type Job } from "@/lib/api" +import { frameUrl, describeFrame, translateText, generateImage, generatedImageUrl, type KeyFrame, type Job } from "@/lib/api" import { toast } from "sonner" type CustomItem = { id: string; zh: string; en: string; translating: boolean } @@ -21,6 +21,7 @@ interface Props { export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, onChange, onToggleSelect, onJobUpdate, embedded = false }: Props) { const [describing, setDescribing] = useState(false) + const [generating, setGenerating] = useState(false) const [mounted, setMounted] = useState(false) // 自定义提取元素 — 按 frame 隔离,切换 frame 后回到同一帧时还能看到之前加的 const [customsByFrame, setCustomsByFrame] = useState>({}) @@ -79,6 +80,35 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o const isSelected = selected.has(f.index) const desc = f.description + const handleGenerateMat = async () => { + if (activeIndex === null || !f) return + const base = f.description?.suggested_prompt?.trim() + if (!base) { + toast.error("请先识别此分镜(右上角『识别』按钮)") + return + } + // 自动选用此帧 → ImageGenCard 才会渲染 + if (!selected.has(f.index)) onToggleSelect(f.index) + + const extraEn = customs.filter((c) => c.en).map((c) => c.en).join(", ") + setGenerating(true) + try { + const updated = await generateImage(jobId, f.index, { + prompt: base, + extra_prompt: extraEn, + negative_prompt: "水印, @用户名, TikTok logo, 平台文字, 浮水印", + model: "gemini-3-pro-image-preview", + mode: "edit", + }) + onJobUpdate?.(updated) + toast.success(`分镜 ${f.index + 1} 垫图生成完成 · 已加入生图卡`) + } catch (e) { + toast.error("生图失败:" + (e instanceof Error ? e.message : String(e))) + } finally { + setGenerating(false) + } + } + const handleDescribe = async () => { setDescribing(true) try { @@ -310,25 +340,61 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o + {!desc?.suggested_prompt && ( +
先识别此分镜,再生成垫图
+ )} + {desc?.suggested_prompt && customs.length === 0 && ( +
未加自定义元素 · 将仅按识别结果生成
+ )} - {/* 已提取 */} -
-
- - 已提取的元素 -
-
- 暂无 · 提取后会出现在这里 -
-
+ {/* 已生成的垫图(与生图卡同源) */} + {f.generated_images && f.generated_images.length > 0 && ( +
+
+
+ + 已生成垫图 + · {f.generated_images.length} +
+ 同步到 →「生图」节点 +
+
+ {f.generated_images.map((g) => ( + + {`gen + {g.selected && ( +
+ +
+ )} +
+ ))} +
+
+ )} +