auto-save 2026-05-13 12:40 (~4)

This commit is contained in:
2026-05-13 12:40:56 +08:00
parent b5ae3e7b6d
commit 95b1354f9f
4 changed files with 64 additions and 3 deletions

View File

@@ -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
}
]
}

View File

@@ -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。

View File

@@ -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 && (
<div className="rounded-lg border border-emerald-400/40 bg-emerald-500/5 p-2 space-y-1.5">
<div className="flex items-center justify-between">
<div className="relative rounded-lg border border-emerald-400/40 bg-emerald-500/5 p-2 space-y-1.5">
<div className="flex items-center justify-between pr-5">
<div className="text-[10px] text-emerald-300 inline-flex items-center gap-1 font-medium">
<Sparkle className="h-2.5 w-2.5" />
</div>
<span className="text-[9px] text-white/40"></span>
</div>
<button
onClick={handleDiscardCleaned}
title="丢弃这次清洗结果"
className="absolute top-1.5 right-1.5 h-5 w-5 rounded-full bg-black/40 hover:bg-rose-500/80 text-white/70 hover:text-white inline-flex items-center justify-center transition"
>
<X className="h-2.5 w-2.5" />
</button>
<img
src={cleanedSrc}
alt={`cleaned ${f.index}`}

View File

@@ -242,6 +242,15 @@ export async function applyCleanedFrame(jobId: string, frameIdx: number): Promis
return res.json()
}
export async function discardCleanedFrame(jobId: string, frameIdx: number): Promise<Job> {
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,