chore: sync annotation UI, shortcuts, and misc updates

This commit is contained in:
Andrew-AAAA committed 2026-09-10 09:14:28 +07:00
1 parent cf4c3370e1
commit 51e74a253e
11 files changed
+102 -36

No files matched your search

+1 -1
View File
@@ -710,7 +710,7 @@ main.page {
.shortcuts { margin: 0; display: grid; gap: 5px; font-size: 12px; }
.shortcuts > div { display: flex; gap: 10px; align-items: baseline; }
.shortcuts dt { flex: none; width: 62px; }
.shortcuts dt { flex: none; width: 72px; }
.shortcuts dd { margin: 0; color: var(--text-muted); }
kbd {
+13 -1
View File
@@ -75,6 +75,17 @@ export default function AnnotationCanvas({
const additive = useRef(false)
function handleDoubleClick(event) {
if (selecting) return
const [x, y] = pointAt(event)
if (assistMode) {
const seed = [Math.max(0, x - 0.015), Math.max(0, y - 0.015), Math.min(1, x + 0.015), Math.min(1, y + 0.015)]
onAssist(seed)
} else if (onExemplar) {
onExemplar([x, y], !event.shiftKey, { kind: 'point' })
}
}
function startDraw(event) {
if (event.button !== 0) return
const [x, y] = pointAt(event)
@@ -218,6 +229,7 @@ export default function AnnotationCanvas({
onPointerDown={startDraw}
onPointerMove={onPointerMove}
onPointerUp={onPointerUp}
onDoubleClick={handleDoubleClick}
>
{/* A preview replaces this class on the frame wholesale, so its stored
shapes step aside for the proposals — left on screen they read as
@@ -270,7 +282,7 @@ export default function AnnotationCanvas({
)
})}
{!selecting && exemplars.map((item, i) => {
{!selecting && exemplars.filter((item) => item.kind !== 'point' && item.box).map((item, i) => {
const [x0, y0, x1, y1] = item.box
return (
<rect
@@ -7,6 +7,10 @@ export default function ShortcutsPanel() {
<dt><kbd>Drag</kbd></dt>
<dd>Example of this class → preview a re-detect</dd>
</div>
<div>
<dt><kbd>Dbl Click</kbd></dt>
<dd>Quick exemplar point → preview a re-detect</dd>
</div>
<div>
<dt><kbd>Enter</kbd> <kbd>Esc</kbd></dt>
<dd>Apply / discard the preview</dd>
@@ -15,6 +19,10 @@ export default function ShortcutsPanel() {
<dt><kbd>Shift</kbd>+<kbd>Drag</kbd></dt>
<dd>Not this → drop it and re-detect</dd>
</div>
<div>
<dt><kbd>Shift</kbd>+<kbd>Dbl</kbd></dt>
<dd>Quick negative exemplar point</dd>
</div>
<div>
<dt><kbd>V</kbd></dt>
<dd>Draw ⇄ Select mode</dd>
+5 -2
View File
@@ -100,9 +100,12 @@ export default function useExemplarPool({ frameId, classId, onApplied, onError,
timer.current = setTimeout(() => runRef.current?.(), delay)
}, [])
const add = useCallback((box, positive) => {
const add = useCallback((target, positive, { kind = 'box' } = {}) => {
if (!frameId) return
poolRef.current = [...poolRef.current, { box, positive }]
const entry = kind === 'point'
? { point: target, positive, kind: 'point' }
: { box: target, positive, kind: 'box' }
poolRef.current = [...poolRef.current, entry]
setExemplars(poolRef.current)
setActive(true)
// A drag during a pass cannot be in that pass's result, so it always earns
+1 -1
View File
@@ -500,7 +500,7 @@ export default function ReviewPage({ batchId: rawBatchId, projectId, onProject }
? `${pool.exemplars.filter((e) => e.positive).length} example(s), `
+ `${pool.exemplars.filter((e) => !e.positive).length} negative — `
+ 'tune the filters, then Apply'
: 'Drag an example of this class · Shift-drag = not this'}
: 'Drag or double-click an example · Shift-drag/click = not this'}
</span>
)}
</div>