feat: configurable track forward, track to end, skip-and-mark (REQ-189)

Track 5 Frames becomes Track Forward with a 1-100 count input, remembered in
localStorage (default 5). A separate -> End action runs to the last frame
behind a confirm and can be cancelled mid-flight.

Frames that already hold a shape of the tracked class are skipped instead of
re-annotated: hidden and dimmed shapes count as present. Skips are reported in
the result banner and marked with a dot plus a class name on the filmstrip
thumbnail. frames() now returns the class ids each frame holds, the same query
row, no migration.

Chaining and seed growth stay out: every frame is still seeded from the drawn
box only.
This commit is contained in:
asus committed 2026-10-05 13:46:24 +07:00
1 parent f6b0335c93
commit 4f15335092
9 files changed
+226 -52

No files matched your search

+21 -11
View File
@@ -205,7 +205,7 @@ SAM3: Ready | Off ← green when ready
```
Icons are SVG (Heroicons/Lucide-style), never emoji. *(Some emoji survive inside page bodies —
`🔄 Reset Auto`, `🏷️ Next Shape`, `📋 Copy Prev`, `🚀 Track 5`, `🤖`, `📦`, `⚡`, `🖼️`. These are
`🔄 Reset Auto`, `🏷️ Next Shape`, `📋 Copy Prev`, `🚀 Track Forward`, `🤖`, `📦`, `⚡`, `🖼️`. These are
legacy and **should** become SVG icons in a rebuild; that is the one place the current UI is
off-spec.)*
@@ -897,7 +897,7 @@ worked". The jump happens once per load, and again after an auto-label job finis
### 5.2 Data model in play
```
frame { id, idx, filename, width, height, review_status, annotation_count }
frame { id, idx, filename, width, height, review_status, annotation_count, class_ids }
annotation { id, class_id, geometry, score, source: "auto"|"manual" }
batch { …, review: { pending, approved, rejected }, annotation_count, status }
```
@@ -982,27 +982,37 @@ previous geometry** and shows the error. Same optimistic-with-rollback pattern f
`POST /annotations/bulk-reclass` and `POST /annotations/bulk-delete`.
**Frame bar.** `←` · `n / N` + a status pill (`pending`/`approved`/`rejected`) · `→` ·
**Next Shape [N]** · **Copy Prev [C]** · **Track 5 Frames [T]** · an "asking SAM3…" indicator ·
**Next Shape [N]** · **Copy Prev [C]** · **Track Forward [T]** (+ N input, *→ End*) ·
an "asking SAM3…" indicator ·
**Reject [X]** · **Approve [A]**.
- *Next Shape* jumps to the next frame with annotations, wrapping to the first.
- *Copy Prev* copies every annotation from frame `n-1` onto this frame (one POST per shape);
disabled at index 0 or when the previous frame is empty.
- *Track 5 Frames* takes the selected shape's bounding box and runs SAM3 assist on the next 5
frames with it. Works for both geometry types: a bbox shape supplies its own box, a polygon
is reduced to its bounding box. Nothing selected, an unusable shape, or the last frame
- *Track Forward* takes the selected shape's bounding box and runs SAM3 assist on the next N
frames with it, N from the number input beside the button (1–100, remembered, default 5).
*→ End* runs to the last frame of the batch behind a confirm. Works for both geometry types:
a bbox shape supplies its own box, a polygon is reduced to its bounding box. Nothing
selected, an unusable shape, or the last frame
says so in the error slot instead of silently doing nothing; a frame SAM3 refuses (nothing
inside the box) does not stop the rest — the banner then **starts** with `Tracked N of M`
(M = frames actually ahead, ≤ 5) followed by the per-frame failures. A second press while
a run is in flight is ignored. Shapes are written `source=manual`, exactly as a hand-drawn
one (REQ-189).
(M = frames actually ahead) followed by `skipped K already had <class>` and the per-frame
failures. Skipped frames are those that already hold a shape of the tracked class (hidden or
dimmed counts); they get a dot on their filmstrip thumb and the thumb's tooltip says why. The
"asking SAM3…" indicator becomes a **Cancel** button while a run is in flight (Cancel stops the
remaining frames; the frame already in flight can still be saved by the server, and the banner
says so). A second
press while a run is in flight is ignored. Shapes are written `source=manual`, exactly as a
hand-drawn one (REQ-189).
**Quick reclass bar** appears whenever a shape is selected: one button per class
(`[n] name`, class-coloured) plus *Delete [Del]*.
**Filmstrip.** Every frame as a thumbnail (`GET /frames/{id}/image?w=120`, lazy-loaded), border
coloured by review status, an annotation-count badge, `aria-current` on the active one, which
is scrolled into view (`block: nearest, inline: center`) on every index change.
is scrolled into view (`block: nearest, inline: center`) on every index change. A frame that
Track Forward skipped carries a `--warn` dot top-left and a tooltip naming the class it already
had; the marking clears on the next run (REQ-189).
### 5.6 The exemplar flow — the most intricate part of the app
@@ -1114,7 +1124,7 @@ Handlers are attached in the **capture** phase on `document`, and:
| `U` | jump to next pending frame (`GET /batches/{id}/next-pending?after_idx=`) | same |
| `N` | jump to next annotated frame | same |
| `C` | copy annotations from previous frame | same |
| `T` | track selected shape forward 5 frames | same |
| `T` | track selected shape forward N frames (N = the count input) | same |
| `1`–`9` | set active class; reclass the selected shape if any | reclass **all marked** shapes |
| `Del` / `Backspace` | delete the selected shape | delete **all marked** shapes (confirmed) |
| `H` | hide/show the selected shape(s); no-op with nothing selected | same |