From 95b1354f9fd3e490db9f5aac32d5f1302b583679 Mon Sep 17 00:00:00 2001 From: kang Date: Wed, 13 May 2026 12:40:56 +0800 Subject: [PATCH] auto-save 2026-05-13 12:40 (~4) --- .memory/worklog.json | 13 +++++++++++++ api/main.py | 22 ++++++++++++++++++++++ web/components/lightbox.tsx | 23 ++++++++++++++++++++--- web/lib/api.ts | 9 +++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/.memory/worklog.json b/.memory/worklog.json index 7cf5f29..09dc7ca 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1463,6 +1463,19 @@ "message": "auto-save 2026-05-13 12:29 (~1)", "hash": "d6f3165", "files_changed": 1 + }, + { + "ts": "2026-05-13T12:35:25+08:00", + "type": "commit", + "message": "auto-save 2026-05-13 12:35 (~1)", + "hash": "b5ae3e7", + "files_changed": 1 + }, + { + "ts": "2026-05-13T04:37:38Z", + "type": "session-heartbeat", + "message": "Claude 会话活跃 · 最近命令:claude · 4 项未提交变更 · 最近提交:auto-save 2026-05-13 12:35 (~1)", + "files_changed": 4 } ] } diff --git a/api/main.py b/api/main.py index c0cb95d..a2c0ab5 100644 --- a/api/main.py +++ b/api/main.py @@ -1087,6 +1087,28 @@ def get_cleaned_frame(job_id: str, idx: int): return FileResponse(p, media_type="image/jpeg") +@app.delete("/jobs/{job_id}/frames/{idx}/cleanup", response_model=Job) +def discard_cleaned(job_id: str, idx: int) -> Job: + """丢弃待应用的清洗版(不影响已应用的)""" + job = JOBS.get(job_id) + if not job: + raise HTTPException(404, "job not found") + frame = next((f for f in job.frames if f.index == idx), None) + if not frame: + raise HTTPException(404, "frame not found") + p = job_dir(job_id) / "cleaned" / f"{idx:03d}.jpg" + if p.exists(): + try: p.unlink() + except OSError: pass + new_frames = [] + for f in job.frames: + if f.index == idx: + f.cleaned_url = None + new_frames.append(f) + update(job, frames=new_frames, message=f"丢弃清洗版 · 分镜 {idx + 1}") + return job + + @app.post("/jobs/{job_id}/frames/{idx}/cleanup/apply", response_model=Job) def apply_cleaned(job_id: str, idx: int) -> Job: """用清洗版替换原关键帧:物理覆盖 frames/{idx}.jpg ← cleaned/{idx}.jpg。 diff --git a/web/components/lightbox.tsx b/web/components/lightbox.tsx index 01b0293..3b4e9a3 100644 --- a/web/components/lightbox.tsx +++ b/web/components/lightbox.tsx @@ -4,7 +4,7 @@ import { createPortal } from "react-dom" import { X, ChevronLeft, ChevronRight, Check, Sparkles, Wand2, Loader2, Eye, RefreshCw, Plus, Sparkle, Crop } from "lucide-react" import { frameUrl, cleanedFrameUrl, cutoutUrl, - describeFrame, cleanupFrame, applyCleanedFrame, addElement, deleteElement, cutoutElement, + describeFrame, cleanupFrame, applyCleanedFrame, discardCleanedFrame, addElement, deleteElement, cutoutElement, type KeyFrame, type Job, } from "@/lib/api" import { toast } from "sonner" @@ -187,6 +187,16 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o } } + const handleDiscardCleaned = async () => { + try { + const updated = await discardCleanedFrame(jobId, f.index) + onJobUpdate?.(updated) + toast.success(`已丢弃清洗版`) + } catch (e) { + toast.error("丢弃失败:" + (e instanceof Error ? e.message : String(e))) + } + } + const handleAddElement = async (name_zh: string, name_en?: string, position?: string, source: "auto" | "manual" = "manual") => { const zh = name_zh.trim() if (!zh) return @@ -439,14 +449,21 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o {/* 下方:清洗版(有待应用版本时显示) */} {hasCleaned && cleanedSrc && ( -
-
+
+
清洗结果
待应用
+ {`cleaned { + const res = await fetch(`${API_BASE}/jobs/${jobId}/frames/${frameIdx}/cleanup`, { method: "DELETE" }) + if (!res.ok) { + const txt = await res.text().catch(() => "") + throw new Error(`discardCleaned ${res.status} ${txt.slice(0, 300)}`) + } + return res.json() +} + export async function addElement( jobId: string, frameIdx: number,