fix: exemplar draw keeps other classes' full-set preview (REQ-182 amended)

- AutoAnnotateModal: render merges last full-set detections for
  non-exemplared classes with conditioned results for exemplared ones
  (selected-not-exemplared filter); drawing an example no longer clears
  the other classes' detections
- Run Preview now always refreshes full-set first (exemplars: []) then
  re-runs exemplared classes
- docs: REQ-182 amended (merge semantics), design/ui-spec/tasks synced
This commit is contained in:
asus committed 2026-10-02 15:20:46 +07:00
1 parent 8fb65d2de9
commit 8f41c6c85a
5 files changed
+59 -38

No files matched your search

+17 -17
View File
@@ -20,7 +20,6 @@ export default function AutoAnnotateModal({
const [minBoxFrac, setMinBoxFrac] = useState(0.0)
const [classParams, setClassParams] = useState({})
// Class selection state
const [selectedClasses, setSelectedClasses] = useState(() => {
if (engine === 'base_model') {
@@ -85,20 +84,25 @@ export default function AutoAnnotateModal({
})
const activePool = activeClassName ? pools[activeClassName] || [] : []
// While any selected class has examples, only those classes contribute.
const exemplaredClasses = selectedClasses.filter(name => pools[name]?.length)
// merged: exemplared classes show conditioned results (replacing their full-set
// boxes); everyone else keeps the last full-set run (amended REQ-182)
const nameById = Object.fromEntries(project.classes.map(c => [c.class_id, c.name]))
const exemplaredSet = new Set(exemplaredClasses)
const selectedSet = new Set(selectedClasses)
const renderShapes = exemplaredClasses.length > 0
? exemplaredClasses.flatMap(name => shapesByClass[name] || [])
? [
...previewShapes.filter(s => {
const n = nameById[s.class_id]
return selectedSet.has(n) && !exemplaredSet.has(n)
}),
...exemplaredClasses.flatMap(name => shapesByClass[name] || []),
]
: previewShapes
const handlePreview = () => {
if (exemplaredClasses.length > 0) {
run(exemplaredClasses)
return
}
const frame = currentFrame
if (!frame) return
let isMounted = true
setIsLoadingPreview(true)
setPreviewError('')
@@ -111,8 +115,8 @@ export default function AutoAnnotateModal({
target_class_names: selectedClasses,
class_params: buildClassParams(classParams),
custom_model_path: customModelStagedPath,
exemplars: engine === 'sam3' ? activePool : [],
exemplar_class_name: engine === 'sam3' ? activeClassName : null
exemplars: [],
exemplar_class_name: null
}).then(res => {
if (isMounted && res.shapes) {
setPreviewShapes(res.shapes)
@@ -123,7 +127,9 @@ export default function AutoAnnotateModal({
setPreviewError(err.message || 'Preview failed')
setPreviewShapes([])
}).finally(() => {
setIsLoadingPreview(false)
if (!isMounted) return
if (exemplaredClasses.length > 0) run(exemplaredClasses) // manages busy itself
else setIsLoadingPreview(false)
})
}
@@ -212,11 +218,9 @@ export default function AutoAnnotateModal({
border: '1px solid rgba(168, 85, 247, 0.4)', background: '#18181b',
overflowY: 'auto', borderRadius: 8
}}>
{/* Left Side: Preview */}
<div style={{ flex: '1 1 450px', display: 'flex', flexDirection: 'column', minWidth: 280 }}>
<h3 style={{ margin: '0 0 8px 0', color: '#e4e4e7', fontSize: '1rem' }}>Preview</h3>
<div style={{ position: 'relative', width: '100%', background: '#000', borderRadius: 6, overflow: 'hidden', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
{currentFrame ? (
/* This wrapper must hug the image and nothing else: both overlays
@@ -294,7 +298,6 @@ export default function AutoAnnotateModal({
</>
)}
</div>
<div style={{ marginTop: 10 }}>
<div className="row" style={{ justifyContent: 'space-between', marginBottom: 4 }}>
<span className="hint" style={{ fontSize: '0.8rem' }}>Preview Frame ({frameIndex + 1} / {frames.length}):</span>
@@ -380,7 +383,6 @@ export default function AutoAnnotateModal({
promptError={promptError}
savedPrompt={activeClass ? classPrompts[activeClass.class_id] : ''}
/>
<div className="row" style={{ justifyContent: 'flex-end', gap: 10, marginTop: 16, paddingTop: 10, borderTop: '1px solid rgba(255,255,255,0.08)' }}>
<button className="btn btn-ghost" onClick={onClose} disabled={isSubmitting}>Cancel</button>
<button
@@ -392,9 +394,7 @@ export default function AutoAnnotateModal({
</button>
</div>
</div>
</div>
</div>
)
}