feat: per-class exemplar preview, class hide toggle, container NMS flag (REQ-182, REQ-183, REQ-184)

This commit is contained in:
asus committed 2026-10-02 11:11:33 +07:00
1 parent 1a99f2ffe4
commit 96a00267d9
18 files changed
+537 -124

No files matched your search

+33 -4
View File
@@ -23,6 +23,7 @@ export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }
const [busy, setBusy] = useState(false)
const [error, setError] = useState('')
const [activeJob, setActiveJob] = useState(null)
const [hiddenClasses, setHiddenClasses] = useState(() => new Set())
const batchId = batch?.id || rawBatchId
const stripRef = useRef(null)
@@ -184,7 +185,33 @@ export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }
})
}, [])
const markAll = useCallback(() => setMarkedIds(annotations.map((row) => row.id)), [annotations])
const toggleHide = useCallback((classId) => {
setHiddenClasses((current) => {
const next = new Set(current)
if (next.has(classId)) next.delete(classId)
else next.add(classId)
return next
})
}, [])
// REQ-183: while a class is hidden, no shape of it may be selected or
// marked — however it got there (draw, reclass, copy) — so Delete can
// never hit an invisible shape.
useEffect(() => {
if (!hiddenClasses.size) return
setSelectedId((currentId) => {
const row = annotations.find((a) => a.id === currentId)
return row && hiddenClasses.has(row.class_id) ? null : currentId
})
setMarkedIds((rows) => rows.filter((id) => {
const row = annotations.find((a) => a.id === id)
return !row || !hiddenClasses.has(row.class_id)
}))
}, [hiddenClasses, annotations])
const visibleAnnotations = annotations.filter((row) => !hiddenClasses.has(row.class_id))
const markAll = useCallback(() => setMarkedIds(visibleAnnotations.map((row) => row.id)), [visibleAnnotations])
const removeMarked = useCallback(async () => {
if (!markedIds.length || !frame) return
@@ -460,7 +487,7 @@ export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }
<AnnotationCanvas
frame={frame}
imageUrl={api.frameUrl(frame.id)}
annotations={annotations}
annotations={visibleAnnotations}
selectedId={selectedId}
activeClass={activeClass}
assistMode={assistMode}
@@ -503,9 +530,9 @@ export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }
className="btn"
style={{ padding: '2px 8px', fontSize: '0.78rem' }}
onClick={markAll}
disabled={!annotations.length}
disabled={!visibleAnnotations.length}
>
Select all ({annotations.length})
Select all ({visibleAnnotations.length})
</button>
<span className="muted" style={{ fontSize: '0.78rem' }}>
{markedIds.length
@@ -623,6 +650,8 @@ export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }
reclass={reclass}
clearClassInBatch={clearClassInBatch}
clearClassInFrame={clearClassInFrame}
hiddenClasses={hiddenClasses}
onToggleHide={toggleHide}
annotations={annotations}
selectedId={selectedId}
setSelectedId={setSelectedId}