# 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_COLORS` defined but unused (dead).
- No keyboard shortcuts R / V / PageUp / PageDown.
- No camera-config side panel.
- No tile zoom indicator for `splitTile` media.

**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.Annotator` window 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-`AppConfig` in 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 + `handleSave` body shape + missing `X-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.
