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

+7 -2
View File
@@ -137,11 +137,16 @@ def frames(batch_id: int) -> List[dict]:
with db.cursor() as cur:
cur.execute(
"""SELECT f.*, (SELECT COUNT(*) FROM annotations a WHERE a.frame_id = f.id)
AS annotation_count
AS annotation_count,
(SELECT GROUP_CONCAT(DISTINCT a.class_id) FROM annotations a
WHERE a.frame_id = f.id) AS class_ids
FROM frames f WHERE f.batch_id = ? ORDER BY f.idx""",
(batch_id,),
)
return [dict(row) for row in cur.fetchall()]
rows = [dict(row) for row in cur.fetchall()]
for row in rows:
row["class_ids"] = [int(v) for v in row["class_ids"].split(",")] if row["class_ids"] else []
return rows
def frame_path(frame_id: int) -> Optional[str]: