- Archived change to openspec/changes/archive/2026-02-17-line-rectangle-annotations/ - Updated annotation-tools spec: added rectangle tool mode, TrendLine plugin rendering, line hit testing, line selection handles; updated line drawing and delete requirements; removed SVG overlay rendering - Created new rectangle-annotation spec with full requirements for rectangle drawing, rendering, hit testing, selection, deletion, and database storage Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
105 lines
6.5 KiB
Markdown
105 lines
6.5 KiB
Markdown
## ADDED Requirements
|
|
|
|
### Requirement: Rectangle tool mode
|
|
The Toolbox SHALL include a "rectangle" tool button. When activated, the chart enters rectangle drawing mode. Only one tool SHALL be active at a time — activating the rectangle tool deactivates any other active tool.
|
|
|
|
#### Scenario: Activate rectangle tool
|
|
- **WHEN** user clicks the rectangle tool button in the Toolbox
|
|
- **THEN** the rectangle tool becomes active, the button appears visually selected, and the chart cursor changes to crosshair
|
|
|
|
#### Scenario: Deactivate rectangle tool
|
|
- **WHEN** user clicks the already-active rectangle tool button
|
|
- **THEN** the tool deactivates, the mode returns to "select", and the cursor returns to default
|
|
|
|
#### Scenario: Rectangle tool deactivates other tools
|
|
- **WHEN** the "line" tool is active and user clicks the rectangle tool button
|
|
- **THEN** the line tool deactivates and the rectangle tool becomes active
|
|
|
|
### Requirement: Two-click rectangle drawing
|
|
When the "rectangle" tool is active, the system SHALL implement a two-click interaction to define a rectangle. The first click sets one corner point (time, price). The second click sets the opposite corner point (time, price). The rectangle is defined by these two arbitrary data-coordinate corners — it is NOT constrained to candle boundaries.
|
|
|
|
#### Scenario: Draw a rectangle
|
|
- **WHEN** "rectangle" tool is active and user clicks two points on the chart
|
|
- **THEN** the system saves a rectangle annotation with the two corner coordinates and renders a filled semi-transparent rectangle on the chart
|
|
|
|
#### Scenario: First click registers corner
|
|
- **WHEN** "rectangle" tool is active and user clicks on the chart
|
|
- **THEN** the system records the click position as {time, price} for the first corner
|
|
|
|
#### Scenario: Second click completes rectangle
|
|
- **WHEN** user has set the first corner and clicks a second position
|
|
- **THEN** the system saves a rectangle annotation via POST /api/annotations with `label_type: "rectangle"` and `geometry: {"startTime", "startPrice", "endTime", "endPrice"}`
|
|
|
|
### Requirement: Rectangle preview during drawing
|
|
After the first click, the system SHALL display a preview rectangle that stretches from the first corner to the current cursor position. The preview SHALL have a dashed border and reduced opacity to distinguish it from saved rectangles.
|
|
|
|
#### Scenario: Preview follows cursor
|
|
- **WHEN** user has clicked the first corner and moves the mouse
|
|
- **THEN** a semi-transparent preview rectangle renders from the first corner to the cursor position, updating in real-time via crosshair move events
|
|
|
|
#### Scenario: Preview disappears on cancel
|
|
- **WHEN** user presses Escape during rectangle drawing (after first click)
|
|
- **THEN** the preview rectangle disappears and the drawing is cancelled without saving
|
|
|
|
### Requirement: Rectangle rendering via ISeriesPrimitive
|
|
The system SHALL render saved rectangle annotations using a `RectangleDrawingPrimitive` class that implements `ISeriesPrimitive<Time>`. Each rectangle annotation SHALL have one primitive instance attached to the candlestick series via `series.attachPrimitive()`.
|
|
|
|
#### Scenario: Rectangle renders for saved annotation
|
|
- **WHEN** a rectangle annotation exists for the active chart
|
|
- **THEN** a semi-transparent filled rectangle renders on the chart canvas at the stored corner coordinates
|
|
|
|
#### Scenario: Rectangle uses annotation color
|
|
- **WHEN** a rectangle annotation has a color value
|
|
- **THEN** the rectangle renders with that color as fill (at reduced opacity) and border
|
|
|
|
#### Scenario: Rectangle updates on zoom/pan
|
|
- **WHEN** user zooms or pans the chart
|
|
- **THEN** rectangle primitives automatically reposition via the ISeriesPrimitive lifecycle (coordinate conversion in paneView.update())
|
|
|
|
#### Scenario: Rectangle z-order
|
|
- **WHEN** rectangle annotations render on the chart
|
|
- **THEN** they SHALL render at "bottom" z-order (behind candlesticks), consistent with span rectangles
|
|
|
|
### Requirement: Rectangle hit testing
|
|
The `RectangleDrawingPrimitive` SHALL implement `hitTest(x, y)` to detect clicks within the rectangle bounds. Hit testing SHALL convert pixel coordinates to data coordinates and check if the point falls within the rectangle's time/price range.
|
|
|
|
#### Scenario: Click inside rectangle detected
|
|
- **WHEN** user clicks at a position inside a rectangle's bounds
|
|
- **THEN** `hitTest()` returns a `PrimitiveHoveredItem` with the annotation ID as `externalId`
|
|
|
|
#### Scenario: Click outside rectangle not detected
|
|
- **WHEN** user clicks at a position outside all rectangle bounds
|
|
- **THEN** `hitTest()` returns null
|
|
|
|
### Requirement: Rectangle selection
|
|
The system SHALL allow users to select a rectangle annotation by clicking within its bounds when the "rectangle" or "select" tool is active.
|
|
|
|
#### Scenario: Click to select rectangle
|
|
- **WHEN** user clicks within a rectangle annotation
|
|
- **THEN** the rectangle appears selected (thicker border or increased opacity)
|
|
|
|
#### Scenario: Click to deselect rectangle
|
|
- **WHEN** user clicks outside all rectangle annotations while one is selected
|
|
- **THEN** the selection is cleared
|
|
|
|
### Requirement: Rectangle deletion
|
|
The system SHALL allow users to delete rectangle annotations via the delete tool.
|
|
|
|
#### Scenario: Delete rectangle with delete tool
|
|
- **WHEN** the "delete" tool is active and user clicks within a rectangle
|
|
- **THEN** the system sends DELETE /api/annotations/{id}, removes the primitive from the chart, and updates the annotation list
|
|
|
|
#### Scenario: Delete selected rectangle with keyboard
|
|
- **WHEN** a rectangle is selected and user presses Delete or Backspace
|
|
- **THEN** the system deletes the rectangle annotation and removes it from the chart
|
|
|
|
### Requirement: Rectangle database storage
|
|
Rectangle annotations SHALL be stored in the existing `annotations` table with `label_type: "rectangle"` and `geometry` containing JSON: `{"startTime": <unix>, "startPrice": <float>, "endTime": <float>, "endPrice": <float>}`. The `startTime/startPrice` represents one corner, `endTime/endPrice` the opposite corner.
|
|
|
|
#### Scenario: Rectangle annotation persisted
|
|
- **WHEN** user completes a two-click rectangle drawing
|
|
- **THEN** the system sends POST /api/annotations with label_type "rectangle", the active chart_id, selected color, and geometry JSON with the two corner coordinates
|
|
|
|
#### Scenario: Rectangle annotation loaded on chart switch
|
|
- **WHEN** user switches to a chart that has rectangle annotations
|
|
- **THEN** the system fetches annotations, creates RectangleDrawingPrimitive instances for each rectangle annotation, and attaches them to the series
|