candle-annotator/openspec/changes/span-annotation/tasks.md
2026-02-14 05:51:23 +01:00

93 lines
6.7 KiB
Markdown

## 1. Database Schema & Migrations
- [ ] 1.1 Add `span_label_types` table to Drizzle schema (id, name, display_name, color, hotkey, is_active, sort_order, created_at)
- [ ] 1.2 Add `span_annotations` table to Drizzle schema (id, chart_id FK, start_time, end_time, label, confidence, outcome, notes, sub_spans JSON, color, created_at)
- [ ] 1.3 Run migration to create both tables
- [ ] 1.4 Seed `span_label_types` with default labels: bull_flag, bear_flag, head_and_shoulders, double_bottom, wedge_up, wedge_down, custom (with distinct colors and hotkeys 1-7)
## 2. Span Label Types API
- [ ] 2.1 Create `GET /api/span-label-types` endpoint — return all active label types sorted by sort_order
- [ ] 2.2 Create `POST /api/span-label-types` endpoint — create a new label type
- [ ] 2.3 Create `PATCH /api/span-label-types/[id]` endpoint — update label type fields
- [ ] 2.4 Create `DELETE /api/span-label-types/[id]` endpoint — delete a label type
## 3. Span Annotations CRUD API
- [ ] 3.1 Create `GET /api/span-annotations?chartId=X` endpoint — return all span annotations for a chart, sorted by start_time desc
- [ ] 3.2 Create `POST /api/span-annotations` endpoint — create span annotation with start/end time swap validation (start <= end)
- [ ] 3.3 Create `PATCH /api/span-annotations/[id]` endpoint — update label, confidence, outcome, notes, sub_spans
- [ ] 3.4 Create `DELETE /api/span-annotations/[id]` endpoint — delete a span annotation
## 4. SpanRectanglePrimitive (Chart Rendering)
- [ ] 4.1 Create `SpanRectanglePrimitive.ts` implementing `ISeriesPrimitive` with data-space rectangle coordinates (start_time, end_time, max_high, min_low)
- [ ] 4.2 Implement `updateAllViews()` with a pane renderer that converts time/price to pixel coordinates and draws a filled semi-transparent rectangle using `useBitmapCoordinateSpace`
- [ ] 4.3 Implement label tag text rendering above the rectangle (pattern name)
- [ ] 4.4 Implement `hitTest()` to detect clicks within the rectangle bounds
- [ ] 4.5 Add highlight state (thicker border / increased opacity) for selected spans
- [ ] 4.6 Set `zOrder: 'bottom'` so rectangles render behind candlesticks
## 5. Span Tool State & Integration
- [ ] 5.1 Extend `activeTool` type in `page.tsx` to include `'span'` tool mode
- [ ] 5.2 Add span-related state to `page.tsx`: `spanAnnotations[]`, `selectedSpanId`, `spanLabelTypes[]`
- [ ] 5.3 Add `fetchSpanAnnotations(chartId)` and `fetchSpanLabelTypes()` data fetching functions
- [ ] 5.4 Load span annotations and label types when chart changes (alongside existing annotation loading)
- [ ] 5.5 Add "Span" tool button to Toolbox component alongside existing tools
## 6. Two-Click Span Selection & Preview
- [ ] 6.1 Create `SpanAnnotationManager.tsx` component that manages span interaction state (idle / first-click-done / popover-open)
- [ ] 6.2 Implement first-click handler: snap to nearest candle, store start candle, render start marker via a preview primitive
- [ ] 6.3 Implement mouse-move preview: stretch preview rectangle from start candle to cursor candle, computing price range (min low to max high) of candles in range
- [ ] 6.4 Implement second-click handler: snap to nearest candle, finalize span range, swap if end < start, trigger popover
- [ ] 6.5 Implement Escape key to cancel span selection and clear preview
## 7. Label Assignment Popover
- [ ] 7.1 Create `SpanPopover.tsx` with shadcn Popover/Dialog: label dropdown (from span_label_types), confidence slider (1-5), outcome select (win/loss/breakeven/none), notes textarea, Save/Cancel buttons
- [ ] 7.2 Position popover near the end-click position with collision avoidance
- [ ] 7.3 Wire Save button: POST to API, attach SpanRectanglePrimitive to chart series, update spanAnnotations state, close popover
- [ ] 7.4 Wire Cancel button / Escape: discard span, clear preview, close popover
- [ ] 7.5 Disable Save when no label is selected (validation)
## 8. Span Selection, Editing & Deletion
- [ ] 8.1 Implement span click-to-select using hitTest: set selectedSpanId, highlight rectangle, scroll sidebar list to selected item
- [ ] 8.2 Implement click-to-deselect (click selected span again or click outside any span)
- [ ] 8.3 Implement double-click / Enter to open edit popover pre-populated with current span data
- [ ] 8.4 Wire edit Save: PATCH to API, update primitive color/label, update state
- [ ] 8.5 Implement Delete/Backspace keyboard shortcut for selected span: DELETE API call, remove primitive, clear selection, update state
- [ ] 8.6 Implement delete-tool click on span rectangle: same DELETE flow as keyboard shortcut
## 9. Span Annotation Sidebar List
- [ ] 9.1 Create `SpanAnnotationList.tsx` component: scrollable list of span annotations sorted by start_time desc, showing time range, label with colored badge, delete button
- [ ] 9.2 Add count summary grouped by label type (e.g., "Bull Flag: 3 | Bear Flag: 2")
- [ ] 9.3 Implement click-to-select in list: set selectedSpanId, highlight chart rectangle, scroll chart to center on span's time range
- [ ] 9.4 Highlight selected span entry in list (background/border)
- [ ] 9.5 Wire delete button per list item: DELETE API call, remove primitive, update state
- [ ] 9.6 Show empty state message when no span annotations exist
- [ ] 9.7 Clear and reload span list when active chart changes
## 10. Hotkey Label Assignment
- [ ] 10.1 Add keydown listener for span label hotkeys (active only when span tool is active and span range is selected)
- [ ] 10.2 On hotkey press: save span with mapped label (default confidence/outcome/notes), render rectangle, update sidebar skip popover
- [ ] 10.3 Ignore hotkeys when span tool is inactive or no span range selected
## 11. Export Endpoints
- [ ] 11.1 Create `GET /api/export/spans?chartId=X&format=json` Raw Annotations JSON export with full metadata
- [ ] 11.2 Create `GET /api/export/spans?chartId=X&format=windowed` Windowed Classification CSV with flattened OHLCV columns and configurable `context_padding` (default 10)
- [ ] 11.3 Create `GET /api/export/spans?chartId=X&format=bio` BIO-tagged CSV with one row per candle, B-{label}/I-{label}/O tagging, multi-label columns for overlapping spans
## 12. Integration Testing & Polish
- [ ] 12.1 Verify full create flow: activate span tool two clicks popover save rectangle renders sidebar updates
- [ ] 12.2 Verify edit flow: select span double-click edit popover save updates reflect
- [ ] 12.3 Verify delete flow: select span Delete key / trash icon removed from chart and sidebar
- [ ] 12.4 Verify chart switch: span annotations and sidebar list reload for new chart
- [ ] 12.5 Verify all three export formats produce correct output
- [ ] 12.6 Verify hotkey label assignment works end-to-end