candle-annotator/openspec/changes/line-rectangle-annotations/specs/annotation-tools/spec.md
Marko Djordjevic 65f00e6ce7 feat: complete prediction UI feedback tasks (11.2, 11.4, 11.5)
- Implement disagreement visual highlighting with distinct colors
  - Yellow highlight for 'missed_by_human' predictions
  - Orange for 'label_mismatch' disagreements
  - Warning icon on disagreement markers
- Add click-to-convert prediction feedback
  - Click disagreement predictions to create span annotations
  - Auto-fill with predicted label and times
  - Set source as 'model_confirmed' or 'model_corrected'
- Add dismiss action for false positive predictions
  - Alt+Click or Ctrl+Click to dismiss predictions
  - Saves negative annotation with label 'O'
  - Records original prediction in model_prediction field
- Filter predictions when 'Show only disagreements' is enabled
2026-02-16 11:40:55 +01:00

83 lines
5.7 KiB
Markdown

## ADDED Requirements
### Requirement: Active tool includes rectangle mode
The system SHALL add "rectangle" to the available tool modes alongside "select", "break_up", "break_down", "line", "span", and "delete". Only one tool SHALL be active at a time.
#### Scenario: Rectangle tool in tool list
- **WHEN** the Toolbox renders
- **THEN** a rectangle tool button is available alongside the existing tool buttons
### Requirement: Line rendering via TrendLine plugin
The system SHALL render saved line annotations using the `TrendLine` class (implementing `ISeriesPrimitive<Time>`) instead of SVG `<line>` elements. Each line annotation SHALL have one `TrendLine` primitive instance attached to the candlestick series via `series.attachPrimitive()`. The `SvgOverlay` component SHALL be removed.
#### Scenario: Saved lines render as canvas primitives
- **WHEN** line annotations exist for the active chart
- **THEN** each line renders via a TrendLine primitive on the chart canvas (not SVG overlay)
#### Scenario: Lines participate in autoscaling
- **WHEN** a line annotation's price range extends beyond visible candle data
- **THEN** the chart autoscale includes the line's price range via `autoscaleInfo()`
#### Scenario: Lines update on zoom/pan
- **WHEN** user zooms or pans the chart
- **THEN** line primitives automatically reposition via the ISeriesPrimitive lifecycle
### Requirement: Line hit testing
The `TrendLine` class SHALL implement `hitTest(x, y)` to detect clicks near the line. Hit testing SHALL calculate the perpendicular distance from the click point to the line segment and return a hit if within 10 CSS pixels (scaled by device pixel ratio).
#### Scenario: Click near line detected
- **WHEN** user clicks within 10 CSS pixels of a line segment
- **THEN** `hitTest()` returns a `PrimitiveHoveredItem` with the annotation ID as `externalId`
#### Scenario: Click far from line not detected
- **WHEN** user clicks more than 10 CSS pixels from any line segment
- **THEN** `hitTest()` returns null
### Requirement: Line selection handles via plugin
When a line is selected, the `TrendLine` renderer SHALL draw circular endpoint handles (radius 6px) at both endpoints of the line. The handles SHALL be rendered as part of the canvas draw call, not as separate SVG elements.
#### Scenario: Handles appear on selection
- **WHEN** a line is selected (via click with line tool active)
- **THEN** circular handles render at both endpoints of the line on the canvas
#### Scenario: Handles disappear on deselection
- **WHEN** the selected line is deselected (Escape key or clicking elsewhere)
- **THEN** the endpoint handles no longer render
## MODIFIED Requirements
### Requirement: Two-click line drawing
When the "line" tool is active, the system SHALL implement a two-click drawing interaction using `chart.subscribeClick()` for click detection and `chart.subscribeCrosshairMove()` for preview updates. The first click sets the start point (time, price). The second click sets the end point (time, price). After the second click, the system SHALL save an annotation with `label_type: "line"` and `geometry` containing JSON: `{"startTime": <unix>, "startPrice": <float>, "endTime": <unix>, "endPrice": <float>}`. The line SHALL render immediately as a TrendLine primitive attached to the candlestick series.
#### Scenario: Draw a trend line
- **WHEN** "line" tool is active and user clicks two points on the chart
- **THEN** system saves a line annotation with start/end coordinates and renders the line as a TrendLine primitive
#### Scenario: Visual feedback during line drawing
- **WHEN** "line" tool is active and user has clicked the first point but not the second
- **THEN** system displays a preview TrendLine primitive (dashed or semi-transparent) from the first point to the current crosshair position, updating via `subscribeCrosshairMove()`
#### Scenario: Cancel line drawing
- **WHEN** user presses Escape during a two-click line drawing (after first click)
- **THEN** system cancels the line drawing, detaches the preview primitive, and clears the drawing state without saving
### Requirement: Delete annotation
When the "delete" tool is active and the user clicks on or near an existing annotation (marker, line, or rectangle), the system SHALL remove that annotation from the database and update the chart display immediately. Line and rectangle hit detection SHALL use the primitive's `hitTest()` method instead of SVG proximity calculation.
#### Scenario: Delete a marker annotation
- **WHEN** "delete" tool is active and user clicks on a candle that has a marker annotation
- **THEN** system removes the annotation from the database and the marker disappears from the chart
#### Scenario: Delete a line annotation
- **WHEN** "delete" tool is active and user clicks near an existing line
- **THEN** system detects the hit via `TrendLine.hitTest()`, sends DELETE /api/annotations/{id}, detaches the primitive from the series, and updates the annotation list
#### Scenario: Delete a rectangle annotation
- **WHEN** "delete" tool is active and user clicks within a rectangle
- **THEN** system detects the hit via `RectangleDrawingPrimitive.hitTest()`, sends DELETE /api/annotations/{id}, detaches the primitive, and updates the annotation list
## REMOVED Requirements
### Requirement: SVG overlay rendering
**Reason**: Replaced by TrendLine plugin rendering. Line annotations now render via `ISeriesPrimitive` on the chart canvas instead of SVG `<line>` elements in an overlay. All coordinate conversion, hit detection, and interaction handling moves to the plugin system and chart native events.
**Migration**: Remove `SvgOverlay.tsx` component. Remove its usage from `CandleChart.tsx`. Line annotation data in the database is unchanged — the same geometry format is used by the TrendLine plugin.