- 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>
6.5 KiB
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"andgeometry: {"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 aPrimitiveHoveredItemwith the annotation ID asexternalId
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