feat: per-class autolabel params + frame-scoped class clear (REQ-180, REQ-181)

- REQ-181: class_params {name: {threshold?, iou_threshold?, min_box_frac?}}
  on /preview and /autolabel, per-class override table in both modals;
  empty overrides take the unchanged global path
- REQ-180: x button on each review sidebar class row clears that class on
  the current frame only via bulk-delete, no confirmation
- includes REQ-178 empty date-folder cycle fix (archive_index.py)
This commit is contained in:
asus committed 2026-10-01 17:01:54 +07:00
1 parent c244ab9795
commit 1a99f2ffe4
16 files changed
+372 -45

No files matched your search

+21
View File
@@ -392,6 +392,26 @@ export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }
} catch (exc) { setError(exc.message) }
}
async function clearClassInFrame(item) {
const doomed = annotations.filter((row) => row.class_id === item.class_id)
if (!doomed.length || !frame) return
const ids = new Set(doomed.map((row) => row.id))
const previous = annotations
const previousCount = frame.annotation_count ?? 0
setAnnotations((rows) => rows.filter((row) => !ids.has(row.id)))
setMarkedIds((rows) => rows.filter((id) => !ids.has(id)))
patchFrameLocally(frame.id, { annotation_count: Math.max(0, previousCount - doomed.length) })
try {
await api.bulkDeleteAnnotations(doomed.map((row) => row.id))
setBatch(await api.getBatch(batchId))
} catch (exc) {
setAnnotations(previous)
setMarkedIds((rows) => [...rows, ...doomed.filter((row) => !rows.includes(row.id)).map((row) => row.id)])
patchFrameLocally(frame.id, { annotation_count: previousCount })
setError(exc.message)
}
}
return (
<>
<div className="page-head">
@@ -602,6 +622,7 @@ export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }
activeClass={activeClass}
reclass={reclass}
clearClassInBatch={clearClassInBatch}
clearClassInFrame={clearClassInFrame}
annotations={annotations}
selectedId={selectedId}
setSelectedId={setSelectedId}