Captures the full output of autodev existing-code Phase A through Step 4 (Code Testability Revision) for the Azaion UI workspace: - Step 1 Document: _docs/02_document/ (FINAL_report, architecture, glossary, components/, modules/, diagrams/, system-flows, module-layout) plus _docs/00_problem/ + _docs/01_solution/ + _docs/legacy/ + _docs/how_to_test + README. - Step 2 Architecture Baseline: architecture_compliance_baseline.md. - Step 3 Test Spec: _docs/02_document/tests/ (environment, test-data, blackbox/performance/resilience/security/ resource-limit tests, traceability-matrix), enum_spec_snapshot, expected_results/results_report.md (98 rows), plus the run-tests.sh + run-performance-tests.sh runners. - Step 4 Code Testability Revision: 01-testability-refactoring/ run dir (list-of-changes C01-C07, deferred_to_refactor, analysis/research_findings + refactoring_roadmap) and the 7 child task specs AZ-448..AZ-454 under _docs/02_tasks/todo/ plus _dependencies_table.md. - _docs/_autodev_state.md pins the cursor at Step 4 / refactor Phase 4 entry so /autodev resumes cleanly. Epic AZ-447 (UI testability gates) tracks the 7 child tasks that will land in subsequent commits. Co-authored-by: Cursor <cursoragent@cursor.com>
11 KiB
06 — Annotations
1. High-Level Overview
Purpose: Image / video annotation editor — bounding boxes, classes, affiliation, combat readiness, AI detection (sync + async via SSE), and the media-list browser scoped to the active flight.
Architectural Pattern: Page composition (AnnotationsPage) wiring MediaList + VideoPlayer (or static image) + CanvasEditor + AnnotationsSidebar + DetectionClasses.
Upstream dependencies: 00_foundation, 01_api-transport, 03_shared-ui (FlightContext, ConfirmDialog, DetectionClasses), 11_class-colors.
Downstream consumers: 10_app-shell (routed at /annotations); 07_dataset imports CanvasEditor directly (cross-feature edge — see baseline scan).
2. Internal Interfaces
| Export | Notes |
|---|---|
AnnotationsPage() |
Top-level route component. Manages selected media, panel widths (via useResizablePanel), and the open annotation under edit. |
Internal modules:
| Module | Role |
|---|---|
AnnotationsPage.tsx |
Orchestrator (route component) |
MediaList.tsx |
Left panel: thumbnail browser + search/filter; consumes useFlight() |
VideoPlayer.tsx |
HTML5 video + frame seek + per-frame annotation overlays |
CanvasEditor.tsx |
Bounding-box draw / move / resize layer (also used by 07_dataset) |
AnnotationsSidebar.tsx |
Right panel: annotation list, AI-detect controls |
3. External API Specification
| Method | Path | Purpose |
|---|---|---|
| GET | /api/annotations/media?flightId=... |
List media |
| GET | /api/annotations/media/{id} |
Media metadata |
| GET | /api/annotations/media/{id}/annotations |
Bbox list |
| POST | /api/annotations |
Create one |
| PUT | /api/annotations/{id} |
Update one |
| DELETE | /api/annotations/{id} |
Delete one |
| POST | /api/detect/{mediaId} |
Sync image AI-detect |
| POST | /api/detect/video/{mediaId} |
Async video AI-detect — should send X-Refresh-Token, currently does not (finding #30) |
| GET | /api/detect/classes |
Detection class list |
| SSE | /api/detect/stream/{jobId} |
Async detect progress (subscribed via 01_api-transport/sse.ts) — but AnnotationsPage does not subscribe today (finding #10) |
5. Implementation Details
State Management: Page-local for the open annotation + selection; useResizablePanel for panel widths (not persisted — finding #11).
Time-window math (CanvasEditor, finding #6, post-cross-check correction): implementation is symmetric ±200 ms (400 ms total). Spec wants asymmetric 50 ms before + 150 ms after (200 ms total). UI is 4× too wide and not centred per spec.
Gradient cap (finding #9, post-cross-check correction): annotation row alpha gradient maxes at 0x28 = 16 % opacity due to a * 40 literal that is decimal, not hex. Wireframe demands 0x40 = 25 %.
handleSave body (finding #32): currently { classNum, x, y, w, h, time }. Suite spec requires { classNum, x, y, w, h, videoTime, Source, WaypointId }. Owner: this component, also touches Foundation types.
handleDownload (image export) — risks tainting the <canvas> if the video is from a blob: URL with cross-origin frames; finding #12.
Missing affordances (findings #13–18):
- Affiliation icons absent (spec mandates per-bbox).
- Combat-readiness indicator absent.
AFFILIATION_COLORSdefined but unused (dead).- No keyboard shortcuts R / V / PageUp / PageDown.
- No camera-config side panel.
- No tile zoom indicator for
splitTilemedia.
AI-detect controls (AnnotationsSidebar, findings #21–23):
- Async progress is not streamed (no SSE subscription).
- Errors silently
console.error'd, no UI feedback.
MediaList (findings #24–26):
- Uses
alert()for "no media" state. blob:local previews ignore the search filter.- No virtualisation — long flights render all thumbnails.
Cross-feature leak: 07_dataset imports CanvasEditor directly (caveat from 00_discovery.md §8). The right home is a shared components/canvas/ directory; not done in this step.
Enum drift cross-link: findings #27–34 capture the enum drift (AnnotationStatus, Affiliation, CombatReadiness, MediaStatus, AnnotationSource) — wire payloads using current src/types/index.ts values are wrong. Owner of fix: 00_foundation/types/index.ts. Two findings (#31, #33) are NO UI CHANGE — PARENT-DOC FIX and have already been applied to the suite docs (state.json 02:18Z note); the rest are Step 4.
Key Dependencies: HTML5 <canvas> + <video>, react-dropzone (upload).
6b. WPF gap analysis (vs suite/annotations-research)
Output of the Step 2 BLOCKING-gate cross-check (2026-05-10). The legacy WPF
Azaion.Annotatorwindow is the design source for this component; the file_docs/legacy/wpf-era.md§10 calls out which features must be ported. The list below is the delta — features that exist in the WPF source and are NOT yet present in the React port. Each entry names the WPF anchor and the React owner; numeric findings (#) come from_docs/02_document/modules/src__features__annotations.md.
| WPF feature | Anchor in annotations-research |
React status | Owner |
|---|---|---|---|
Time window asymmetric 50/150 ms (interval-tree _thresholdBefore=50ms, _thresholdAfter=150ms) |
Azaion.Annotator/Annotator.xaml.cs:53-54 |
Wrong (symmetric ±200 ms). Finding #6. | CanvasEditor.tsx |
Keyboard shortcut [Space] = pause / resume |
Annotator.xaml PauseClick button tooltip "[Пробіл]" |
Missing (no global key listener) | VideoPlayer + AnnotationsPage |
Keyboard [Left] / [Right] = prev / next frame; +Ctrl = ±5 sec |
PreviousFrameClick / NextFrameClick tooltips |
Missing | VideoPlayer |
Keyboard [Enter] = save annotations and continue |
SaveAnnotationsClick tooltip "[Ентер]" |
Missing. Finding #16 broadens this. | AnnotationsPage |
Keyboard [Del] = delete selected annotations (with confirm) |
Annotator.xaml.cs:204-222 (DgAnnotations.KeyUp) |
Missing | AnnotationsSidebar + ConfirmDialog |
Keyboard [X] = delete ALL annotations |
RemoveAllClick tooltip "[X]" |
Missing | AnnotationsPage |
Keyboard [M] = mute volume; also toggles GPS panel (context-sensitive) |
TurnOffVolume + SwitchGpsPanel tooltips both "[M]" |
Missing | VideoPlayer |
Keyboard [R] = AI Detect |
AIDetectBtn_OnClick tooltip "[R]" |
Missing. Finding #16. | AnnotationsSidebar |
Keyboard [Ctrl+click] = multi-select; [Ctrl+drag] = pan; [Ctrl+wheel] = zoom |
Azaion.Common/Controls/CanvasEditor.cs |
Pan / zoom missing (finding listed); multi-select unverified | CanvasEditor.tsx |
Volume slider (UpdatableProgressBar Volume, range 0–100, mediator VolumeChangedEvent) |
Annotator.xaml:500-507 |
Missing entirely | VideoPlayer |
Status bar — clock mm:ss / mm:ss |
Annotator.xaml.cs:237 StatusClock.Text = ... |
Missing | VideoPlayer (display) + AnnotationsPage (slot) |
Status bar — contextual help text (HelpTextEnum.{Initial, PlayVideo, PauseForAnnotations, AnnotationHelp}, BlinkHelp flicker pattern) |
Azaion.Annotator/HelpTexts.cs + Annotator.xaml.cs:144-158 |
Missing | AnnotationsPage (or a global toast) |
Status bar — generic status text (StatusBarItem Status) |
Annotator.xaml:653 |
Missing | shared chrome (could live in App Shell or AnnotationsPage) |
| Sound Detections feature ("Show objects detected by audio analysis", own button, distinct from AI Detect) | Annotator.xaml:565-617 SoundDetections button + handler |
Entirely missing. Not mentioned anywhere in _docs/legacy/wpf-era.md §10 "What survived" — needs a user decision: port or drop. |
TBD |
| Drone Maintenance feature ("Аналіз стану БПЛА" — UAV state analysis, [K]) | Annotator.xaml:618-630 RunDroneMaintenance button + handler |
Entirely missing. Same status as above — port-or-drop decision required. | TBD |
| Resizable panel widths persisted (left + right panels) | Annotator.xaml.cs:243-252 SaveUserSettings writes UIConfig.LeftPanelWidth / RightPanelWidth |
Missing — useResizablePanel does not persist (finding #11). |
00_foundation/useResizablePanel + Settings backend |
| Camera config side panel (altitude / focal / sensor → GSD) | Annotator.xaml:196-203 CameraConfigControl |
Missing (finding #17) | AnnotationsPage |
| Affiliation icons + Combat readiness indicator on bbox label | Azaion.Common/DTO/AffiliationEnum, _docs/ui_design/README.md |
Missing (findings #14–15) | CanvasEditor.tsx + types |
| Annotation list seek + zoom on double-click | Annotator.xaml.cs:197-202, OpenAnnotationResult (seek + ZoomTo for split tiles) |
Partial — seek probably works, "open split-tile zoom" not verified | AnnotationsSidebar |
| Help window "Як анотувати" (6 quality rules) | HelpWindow.xaml, _docs/legacy/wpf-era.md §10 "Help window" |
HelpModal exists but GUIDELINES is a hardcoded string in source — not moved to i18n bundle. Step 4. |
03_shared-ui/HelpModal |
Decisions required at Step 4.5 (Architecture Vision)
- Sound Detections — port, drop, or move to a different module (e.g., a future audio-pipeline service)?
- Drone Maintenance — same.
- Camera config persistence — was per-
AppConfigin WPF; in the React port should it be per-user-settings, per-flight, or per-detect-job? - Status bar / help-text blinking — keep the WPF "blink twice" UX, replace with toasts, or drop?
These are Architecture-Vision-level questions, not Step 2 component-graph decisions. Recorded here so Step 4.5 can pick them up.
7. Caveats & Edge Cases
- 26 findings in
src__features__annotations.md. Cross-cutting blockers cluster on enum drift +handleSavebody shape + missingX-Refresh-Token. - Time-window and gradient math wrong against spec.
- Video AI-detect not wired to SSE — long-running jobs appear to hang from the user's POV.
- WPF gap analysis above lists ~17 missing affordances. Highest user-impact: keyboard shortcuts, volume slider, status bar with clock + help text. Highest design-impact: Sound Detections + Drone Maintenance (both require a port-or-drop decision).
8. Dependency Graph
Must be implemented after: 00_foundation, 01_api-transport, 03_shared-ui, 11_class-colors.
Can be implemented in parallel with: every other feature page (note: 07_dataset imports CanvasEditor, so a CanvasEditor extraction must coordinate).
Blocks: 07_dataset (direct import dependency on CanvasEditor), 10_app-shell.
Module Inventory
Single consolidated module doc: _docs/02_document/modules/src__features__annotations.md. classColors.ts is moved into a separate component (11_class-colors) — its module doc is referenced from there, not here.