auto-save 2026-05-13 11:23 (~4)
This commit is contained in:
@@ -1323,6 +1323,13 @@
|
|||||||
"type": "session-heartbeat",
|
"type": "session-heartbeat",
|
||||||
"message": "Claude 会话活跃 · 最近命令:claude · 2 项未提交变更 · 最近提交:auto-save 2026-05-13 11:12 (~1)",
|
"message": "Claude 会话活跃 · 最近命令:claude · 2 项未提交变更 · 最近提交:auto-save 2026-05-13 11:12 (~1)",
|
||||||
"files_changed": 2
|
"files_changed": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ts": "2026-05-13T11:17:52+08:00",
|
||||||
|
"type": "commit",
|
||||||
|
"message": "auto-save 2026-05-13 11:17 (~2)",
|
||||||
|
"hash": "f4ce533",
|
||||||
|
"files_changed": 2
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
18
api/main.py
18
api/main.py
@@ -1037,10 +1037,22 @@ def cleanup_frame(job_id: str, idx: int, req: CleanupReq | None = None) -> Job:
|
|||||||
if not frame_path.exists():
|
if not frame_path.exists():
|
||||||
raise HTTPException(404, "frame file missing")
|
raise HTTPException(404, "frame file missing")
|
||||||
|
|
||||||
region_phrase = _region_to_phrase(req.region) if (req and req.region) else ""
|
region_phrases: list[str] = []
|
||||||
if region_phrase:
|
if req and req.regions:
|
||||||
|
for r in req.regions:
|
||||||
|
p = _region_to_phrase(r)
|
||||||
|
if p:
|
||||||
|
region_phrases.append(p)
|
||||||
|
# 去重保序
|
||||||
|
region_phrases = list(dict.fromkeys(region_phrases))
|
||||||
|
|
||||||
|
if region_phrases:
|
||||||
|
if len(region_phrases) == 1:
|
||||||
|
zones = f"the {region_phrases[0]} part"
|
||||||
|
else:
|
||||||
|
zones = "these parts: " + ", ".join(region_phrases)
|
||||||
prompt = (
|
prompt = (
|
||||||
f"Erase the text and graphics in the {region_phrase} part of the image. "
|
f"Erase the text and graphics in {zones} of the image. "
|
||||||
"Keep all other parts unchanged."
|
"Keep all other parts unchanged."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -30,9 +30,11 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o
|
|||||||
const [addingZh, setAddingZh] = useState(false)
|
const [addingZh, setAddingZh] = useState(false)
|
||||||
const [addInput, setAddInput] = useState("")
|
const [addInput, setAddInput] = useState("")
|
||||||
const [mounted, setMounted] = useState(false)
|
const [mounted, setMounted] = useState(false)
|
||||||
// 画框模式 + 选区(相对坐标 0-1)
|
// 画框模式 + 多选区(相对坐标 0-1)
|
||||||
|
type Region = { x: number; y: number; w: number; h: number }
|
||||||
const [cropMode, setCropMode] = useState(false)
|
const [cropMode, setCropMode] = useState(false)
|
||||||
const [region, setRegion] = useState<{ x: number; y: number; w: number; h: number } | null>(null)
|
const [regions, setRegions] = useState<Region[]>([])
|
||||||
|
const [draftRegion, setDraftRegion] = useState<Region | null>(null) // 当前正在拖的
|
||||||
const [dragStart, setDragStart] = useState<{ x: number; y: number } | null>(null)
|
const [dragStart, setDragStart] = useState<{ x: number; y: number } | null>(null)
|
||||||
const [extractNamePrompt, setExtractNamePrompt] = useState(false) // 提取模式:要用户填名字
|
const [extractNamePrompt, setExtractNamePrompt] = useState(false) // 提取模式:要用户填名字
|
||||||
const [extractName, setExtractName] = useState("")
|
const [extractName, setExtractName] = useState("")
|
||||||
@@ -43,7 +45,8 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o
|
|||||||
// 切换分镜时清空选区
|
// 切换分镜时清空选区
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCropMode(false)
|
setCropMode(false)
|
||||||
setRegion(null)
|
setRegions([])
|
||||||
|
setDraftRegion(null)
|
||||||
setDragStart(null)
|
setDragStart(null)
|
||||||
setExtractNamePrompt(false)
|
setExtractNamePrompt(false)
|
||||||
setExtractName("")
|
setExtractName("")
|
||||||
@@ -90,13 +93,14 @@ export function FrameLightbox({ jobId, frames, activeIndex, selected, onClose, o
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCleanup = async (withRegion = false) => {
|
const handleCleanup = async (useRegions = false) => {
|
||||||
setCleaning(true)
|
setCleaning(true)
|
||||||
try {
|
try {
|
||||||
const updated = await cleanupFrame(jobId, f.index, withRegion ? region : null)
|
const usable = useRegions ? regions.filter((r) => r.w >= 0.03 && r.h >= 0.03) : null
|
||||||
|
const updated = await cleanupFrame(jobId, f.index, usable && usable.length > 0 ? usable : null)
|
||||||
onJobUpdate?.(updated)
|
onJobUpdate?.(updated)
|
||||||
toast.success(`分镜 ${f.index + 1} 清洗完成 · 下方查看${withRegion ? "(框内)" : ""}`)
|
toast.success(`分镜 ${f.index + 1} 清洗完成${usable && usable.length > 0 ? `(${usable.length} 个区域)` : ""}`)
|
||||||
if (withRegion) { setCropMode(false); setRegion(null) }
|
if (useRegions) { setCropMode(false); setRegions([]); setDraftRegion(null) }
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.error("清洗失败:" + (e instanceof Error ? e.message : String(e)))
|
toast.error("清洗失败:" + (e instanceof Error ? e.message : String(e)))
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -212,12 +212,12 @@ export function cutoutUrl(jobId: string, frameIndex: number, elementId: string):
|
|||||||
export async function cleanupFrame(
|
export async function cleanupFrame(
|
||||||
jobId: string,
|
jobId: string,
|
||||||
frameIdx: number,
|
frameIdx: number,
|
||||||
region?: { x: number; y: number; w: number; h: number } | null,
|
regions?: Array<{ x: number; y: number; w: number; h: number }> | null,
|
||||||
): Promise<Job> {
|
): Promise<Job> {
|
||||||
const res = await fetch(`${API_BASE}/jobs/${jobId}/frames/${frameIdx}/cleanup`, {
|
const res = await fetch(`${API_BASE}/jobs/${jobId}/frames/${frameIdx}/cleanup`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ region: region ?? null }),
|
body: JSON.stringify({ regions: regions ?? null }),
|
||||||
})
|
})
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const txt = await res.text().catch(() => "")
|
const txt = await res.text().catch(() => "")
|
||||||
|
|||||||
Reference in New Issue
Block a user