## 1. Database Schema & Migrations - [x] 1.1 Add `span_label_types` table to Drizzle schema (id, name, display_name, color, hotkey, is_active, sort_order, created_at) - [x] 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) - [x] 1.3 Run migration to create both tables - [x] 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 - [x] 2.1 Create `GET /api/span-label-types` endpoint — return all active label types sorted by sort_order - [x] 2.2 Create `POST /api/span-label-types` endpoint — create a new label type - [x] 2.3 Create `PATCH /api/span-label-types/[id]` endpoint — update label type fields - [x] 2.4 Create `DELETE /api/span-label-types/[id]` endpoint — delete a label type ## 3. Span Annotations CRUD API - [x] 3.1 Create `GET /api/span-annotations?chartId=X` endpoint — return all span annotations for a chart, sorted by start_time desc - [x] 3.2 Create `POST /api/span-annotations` endpoint — create span annotation with start/end time swap validation (start <= end) - [x] 3.3 Create `PATCH /api/span-annotations/[id]` endpoint — update label, confidence, outcome, notes, sub_spans - [x] 3.4 Create `DELETE /api/span-annotations/[id]` endpoint — delete a span annotation ## 4. SpanRectanglePrimitive (Chart Rendering) - [x] 4.1 Create `SpanRectanglePrimitive.ts` implementing `ISeriesPrimitive` with data-space rectangle coordinates (start_time, end_time, max_high, min_low) - [x] 4.2 Implement `updateAllViews()` with a pane renderer that converts time/price to pixel coordinates and draws a filled semi-transparent rectangle using `useBitmapCoordinateSpace` - [x] 4.3 Implement label tag text rendering above the rectangle (pattern name) - [x] 4.4 Implement `hitTest()` to detect clicks within the rectangle bounds - [x] 4.5 Add highlight state (thicker border / increased opacity) for selected spans - [x] 4.6 Set `zOrder: 'bottom'` so rectangles render behind candlesticks ## 5. Span Tool State & Integration - [x] 5.1 Extend `activeTool` type in `page.tsx` to include `'span'` tool mode - [x] 5.2 Add span-related state to `page.tsx`: `spanAnnotations[]`, `selectedSpanId`, `spanLabelTypes[]` - [x] 5.3 Add `fetchSpanAnnotations(chartId)` and `fetchSpanLabelTypes()` data fetching functions - [x] 5.4 Load span annotations and label types when chart changes (alongside existing annotation loading) - [x] 5.5 Add "Span" tool button to Toolbox component alongside existing tools ## 6. Two-Click Span Selection & Preview - [x] 6.1 Create `SpanAnnotationManager.tsx` component that manages span interaction state (idle / first-click-done / popover-open) - [x] 6.2 Implement first-click handler: snap to nearest candle, store start candle, render start marker via a preview primitive - [x] 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 - [x] 6.4 Implement second-click handler: snap to nearest candle, finalize span range, swap if end < start, trigger popover - [x] 6.5 Implement Escape key to cancel span selection and clear preview ## 7. Label Assignment Popover - [x] 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 - [x] 7.2 Position popover near the end-click position with collision avoidance - [x] 7.3 Wire Save button: POST to API, attach SpanRectanglePrimitive to chart series, update spanAnnotations state, close popover - [x] 7.4 Wire Cancel button / Escape: discard span, clear preview, close popover - [x] 7.5 Disable Save when no label is selected (validation) ## 8. Span Selection, Editing & Deletion - [x] 8.1 Implement span click-to-select using hitTest: set selectedSpanId, highlight rectangle, scroll sidebar list to selected item - [x] 8.2 Implement click-to-deselect (click selected span again or click outside any span) - [x] 8.3 Implement double-click / Enter to open edit popover pre-populated with current span data - [x] 8.4 Wire edit Save: PATCH to API, update primitive color/label, update state - [x] 8.5 Implement Delete/Backspace keyboard shortcut for selected span: DELETE API call, remove primitive, clear selection, update state - [x] 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