auto-save 2026-05-13 12:40 (~4)
This commit is contained in:
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
22
api/main.py
22
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。
|
||||
|
||||
@@ -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}`}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user