chore: archive both OpenSpec changes and sync specs to main

This commit is contained in:
Marko Djordjevic 2026-02-12 17:56:25 +01:00
parent a6e763c153
commit 50229e2ccf
25 changed files with 927 additions and 1 deletions

View file

@ -1,197 +0,0 @@
# Implementation Tasks
## 1. Setup and Configuration
- [ ] 1.1 Load JetBrains Mono font from Google Fonts in `src/app/layout.tsx` (add to head with weights 400, 500, 700)
- [ ] 1.2 Update `next.config.js` to enable standalone output mode (`output: 'standalone'`)
- [ ] 1.3 Verify all existing features work (run dev server, test line drawing, label placement, delete tool)
## 2. API Extensions for Bulk Operations
- [ ] 2.1 Read existing `src/app/api/annotations/route.ts` DELETE handler
- [ ] 2.2 Add query parameter parsing in DELETE handler (`type` and `all` params from `request.nextUrl.searchParams`)
- [ ] 2.3 Add conditional WHERE clause logic: if `type` param exists, filter by `label_type IN (comma-separated types)`, if `all=true` delete everything, else return 400 error
- [ ] 2.4 Update return value to include count: `{ success: true, deleted: <count> }` using `result.length` from Drizzle delete operation
- [ ] 2.5 Test bulk delete via curl: `curl -X DELETE "http://localhost:3000/api/annotations?type=break_up"` (verify count returned)
- [ ] 2.6 Create new file `src/app/api/health/route.ts` with GET handler returning `{ status: 'ok', timestamp: Date.now() }`
- [ ] 2.7 Add optional database check in health endpoint: if `?check=db` query param exists, attempt simple `SELECT 1` query and return 503 on failure
- [ ] 2.8 Test health endpoint: `curl http://localhost:3000/api/health` (verify 200 response)
## 3. Hacker Theme - CSS Variables and Tailwind
- [ ] 3.1 Read current `src/app/globals.css` and note existing CSS variable names
- [ ] 3.2 Replace CSS variable values in `:root` with hacker theme colors: `--background: 120 100% 4%` (#0a0e0a), `--foreground: 120 100% 50%` (#00ff41), `--primary: 120 100% 50%`, `--destructive: 348 100% 50%` (#ff0040), `--border: 120 100% 10%` (#003311), map all other variables to theme equivalents
- [ ] 3.3 Update body font-family in `globals.css` to: `font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;`
- [ ] 3.4 Add custom selection styling in `globals.css`: `::selection { background: rgba(0, 255, 65, 0.4); color: #00ff41; }`
- [ ] 3.5 Add custom scrollbar styles in `globals.css`: `::-webkit-scrollbar { width: 8px; }`, `::-webkit-scrollbar-track { background: #003311; }`, `::-webkit-scrollbar-thumb { background: #00ff41; border-radius: 4px; }`
- [ ] 3.6 Read `tailwind.config.ts` and locate `theme.extend.colors` object
- [ ] 3.7 Add custom colors to Tailwind config: `matrix: '#00ff41'`, `matrixDim: '#00cc33'`, `matrixDark: '#003311'`, `neonRed: '#ff0040'`, `neonCyan: '#00d4ff'`, `neonYellow: '#ffff00'`, `terminal: '#0a0e0a'`, `terminalLight: '#0d110d'`
- [ ] 3.8 Add custom fontFamily to Tailwind config: `mono: ['JetBrains Mono', 'Fira Code', 'Courier New', 'monospace']`
- [ ] 3.9 Add custom boxShadow to Tailwind config: `'glow-sm': '0 0 8px #00ff41'`, `'glow': '0 0 15px #00ff41, 0 0 30px rgba(0,255,65,0.5)'`, `'glow-lg': '0 0 20px #00ff41, 0 0 40px rgba(0,255,65,0.5)'`
- [ ] 3.10 Add keyframes to Tailwind config: `'glow-pulse': { '0%, 100%': { boxShadow: '0 0 15px #00ff41' }, '50%': { boxShadow: '0 0 30px #00ff41, 0 0 50px rgba(0,255,65,0.5)' } }`, `'flicker': { '0%, 100%': { opacity: '1' }, '50%': { opacity: '0.8' } }`
- [ ] 3.11 Add animations to Tailwind config: `'glow-pulse': 'glow-pulse 2s ease-in-out infinite'`, `'flicker': 'flicker 0.1s ease-in-out'`
- [ ] 3.12 Test theme in browser: verify green text, black background, monospace font on all elements
## 4. Hacker Theme - Component Styling
- [ ] 4.1 Read `src/components/Toolbox.tsx` and identify button elements
- [ ] 4.2 Update button hover states in Toolbox to add glow effect: add `hover:shadow-glow` class to all Button components
- [ ] 4.3 Update active tool buttons in Toolbox: when `activeTool` matches, add `animate-glow-pulse` class
- [ ] 4.4 Update input field styling (search input in label list, if exists): add `focus:shadow-glow-sm focus:border-matrix` classes
- [ ] 4.5 Read `src/components/ui/button.tsx` and add destructive variant glow: in destructive variant, change hover shadow to red: `hover:shadow-[0_0_15px_#ff0040]`
- [ ] 4.6 Test button styling: hover over buttons (should glow green), click to activate tool (should pulse), hover delete button (should glow red)
## 5. Label Management - State and Selection
- [ ] 5.1 Read `src/app/page.tsx` and locate existing state declarations (activeTool, selectedColor, etc.)
- [ ] 5.2 Add new state in page.tsx: `const [selectedLabelId, setSelectedLabelId] = useState<number | null>(null)`
- [ ] 5.3 Pass selectedLabelId and setSelectedLabelId as props to CandleChart component
- [ ] 5.4 Read `src/components/CandleChart.tsx` and locate marker creation code (where break_up/break_down markers are added)
- [ ] 5.5 Add onClick handler to marker options: in marker creation config, add `onClick: () => props.onLabelSelect?.(annotation.id)`
- [ ] 5.6 Update CandleChart props interface to accept `selectedLabelId: number | null`, `onLabelSelect: (id: number) => void`
- [ ] 5.7 Wire up onLabelSelect in page.tsx: `onLabelSelect={(id) => setSelectedLabelId(id === selectedLabelId ? null : id)}`
- [ ] 5.8 Add visual highlight for selected marker: when rendering markers, if `annotation.id === selectedLabelId`, set marker size to 1.5x and add glow effect (modify marker shape/color options)
- [ ] 5.9 Test label selection: click on break_up arrow (should highlight), click again (should deselect), click different marker (should switch selection)
## 6. Label Management - Keyboard Delete
- [ ] 6.1 Read `src/app/page.tsx` and locate any existing keyboard event handlers
- [ ] 6.2 Add global keyboard handler in page.tsx useEffect: `window.addEventListener('keydown', handleKeyDown)`
- [ ] 6.3 Implement handleKeyDown function: if `e.key === 'Delete' || e.key === 'Backspace'`, check if `selectedLabelId !== null`
- [ ] 6.4 In handleKeyDown, when Delete pressed with selected label: call `fetch('/api/annotations/' + selectedLabelId, { method: 'DELETE' })`
- [ ] 6.5 After successful delete, call chart refresh method and reset `setSelectedLabelId(null)`
- [ ] 6.6 Add cleanup in useEffect: `return () => window.removeEventListener('keydown', handleKeyDown)`
- [ ] 6.7 Test keyboard delete: select a label marker, press Delete key (marker should disappear), verify database updated
## 7. Label Management - Sidebar List UI Structure
- [ ] 7.1 Read `src/components/Toolbox.tsx` and locate the export button section (bottom of component)
- [ ] 7.2 Import Collapsible components from shadcn/ui: check if `src/components/ui/collapsible.tsx` exists, if not install via `npx shadcn@latest add collapsible`
- [ ] 7.3 Add state in Toolbox for collapsed state: `const [labelsExpanded, setLabelsExpanded] = useState(true)`
- [ ] 7.4 Add new section above Export button: wrap in Collapsible component with `open={labelsExpanded}` and `onOpenChange={setLabelsExpanded}`
- [ ] 7.5 Create section header button: "Label Annotations (X)" with ChevronDown/ChevronUp icon from lucide-react, clicking toggles `labelsExpanded`
- [ ] 7.6 Inside Collapsible.Content, add container div with `className="max-h-96 overflow-y-auto space-y-2 p-2"`
- [ ] 7.7 Add count display in header: calculate `breakUpCount` and `breakDownCount` from props.annotations filtered by `label_type`, display as "Break Up: X | Break Down: Y"
- [ ] 7.8 Test collapsible: click header (should expand/collapse), verify smooth animation
## 8. Label Management - Sidebar List Content
- [ ] 8.1 Update Toolbox props to accept `annotations: Annotation[]`, `selectedLabelId: number | null`, `onLabelSelect: (id: number) => void`, `onLabelDelete: (id: number) => void`
- [ ] 8.2 Pass annotations from page.tsx to Toolbox (retrieve from CandleChart or fetch directly in page.tsx)
- [ ] 8.3 Filter annotations in Toolbox: `const labelAnnotations = annotations.filter(a => a.label_type === 'break_up' || a.label_type === 'break_down')`
- [ ] 8.4 Sort labelAnnotations by timestamp descending: `labelAnnotations.sort((a, b) => b.timestamp - a.timestamp)`
- [ ] 8.5 Map over labelAnnotations to render list items: each item in a div with border, padding, cursor-pointer, onClick calls onLabelSelect
- [ ] 8.6 Format timestamp in each item: `new Date(timestamp * 1000).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })`
- [ ] 8.7 Add colored badge for label type: break_up shows "BREAK UP" in green bg, break_down shows "BREAK DOWN" in red bg
- [ ] 8.8 Add delete button (trash icon) to each item: onClick calls `onLabelDelete(annotation.id)` with stopPropagation to prevent selection
- [ ] 8.9 Highlight selected item: if `annotation.id === selectedLabelId`, add border-matrix and bg-matrix/10 classes
- [ ] 8.10 Handle empty state: if labelAnnotations.length === 0, show message "No labels yet. Click Break Up or Break Down tools to add labels."
- [ ] 8.11 Test list rendering: add several labels, verify they appear sorted by time, click to select, click delete button (should remove)
## 9. Label Management - Search and Filter
- [ ] 9.1 Add state for search in Toolbox: `const [searchText, setSearchText] = useState('')`
- [ ] 9.2 Add state for filter in Toolbox: `const [filterType, setFilterType] = useState<'all' | 'break_up' | 'break_down'>('all')`
- [ ] 9.3 Add search input above label list: Input component with placeholder "Search by timestamp...", value={searchText}, onChange={e => setSearchText(e.target.value)}
- [ ] 9.4 Add filter dropdown above label list: Select or dropdown with options "All", "Break Up", "Break Down", value={filterType}, onChange={setFilterType}
- [ ] 9.5 Apply filter to labelAnnotations: if filterType !== 'all', filter array by `a.label_type === filterType`
- [ ] 9.6 Apply search to labelAnnotations: filter by formatted timestamp includes searchText (case-insensitive): `formattedTimestamp.toLowerCase().includes(searchText.toLowerCase())`
- [ ] 9.7 Update count display to show filtered count: "Showing X of Y labels"
- [ ] 9.8 Test search: type "Feb" in search (should filter to February dates), test filter: select "Break Up" (should show only break_up labels)
## 10. Label Management - Delete All Labels Button
- [ ] 10.1 Import Dialog components from shadcn/ui: check if `src/components/ui/dialog.tsx` exists, if not install via `npx shadcn@latest add dialog`
- [ ] 10.2 Add state for confirmation dialog: `const [deleteAllDialogOpen, setDeleteAllDialogOpen] = useState(false)`
- [ ] 10.3 Add "Delete All Labels" button below search/filter controls: Button with destructive variant, onClick opens dialog, disabled if labelAnnotations.length === 0
- [ ] 10.4 Create Dialog component with AlertDialog pattern: AlertDialogTitle "Delete all label annotations?", AlertDialogDescription "This will remove all Break Up and Break Down markers. This cannot be undone."
- [ ] 10.5 Add Cancel button in dialog: onClick closes dialog without action
- [ ] 10.6 Add Confirm button in dialog: destructive variant, onClick calls API DELETE with type=break_up,break_down
- [ ] 10.7 Implement delete all handler: `fetch('/api/annotations?type=break_up,break_down', { method: 'DELETE' })`, on success refresh annotations and close dialog
- [ ] 10.8 Show toast notification after delete: use shadcn toast to display "> SUCCESS: Deleted all labels [X items]"
- [ ] 10.9 Test delete all: add multiple labels, click Delete All Labels, confirm dialog (all should disappear), verify database emptied
## 11. Toast Feedback System
- [ ] 11.1 Install shadcn toast if not present: `npx shadcn@latest add toast`
- [ ] 11.2 Read `src/components/ui/toast.tsx` and `src/hooks/use-toast.ts`
- [ ] 11.3 Update toast styling in `toast.tsx`: add `font-mono` class, update border color to use matrix theme colors
- [ ] 11.4 Import and add Toaster component in `src/app/layout.tsx` (add `<Toaster />` in body)
- [ ] 11.5 Create useToast hook wrapper in components that need feedback: import `{ useToast } from '@/hooks/use-toast'`
- [ ] 11.6 Add toast calls after successful operations: delete label → `toast({ title: "> SUCCESS", description: "Deleted label [1 item]" })`
- [ ] 11.7 Add toast calls for errors: catch API errors → `toast({ title: "> ERROR", description: "Failed to delete [code: 500]", variant: "destructive" })`
- [ ] 11.8 Test toast: perform delete operation (should see green success toast), cause error by disconnecting network (should see red error toast)
## 12. Docker - Dockerfile Creation
- [ ] 12.1 Create `.dockerignore` file in project root with contents: `node_modules`, `.next`, `.git`, `data/`, `*.md`, `.env*`, `*.log`, `coverage/`, `.DS_Store`
- [ ] 12.2 Create `Dockerfile` in project root: start with `FROM node:18-alpine AS builder`
- [ ] 12.3 Add build stage instructions: `WORKDIR /app`, `COPY package*.json ./`, `RUN npm ci`, `COPY . .`, `RUN npm run build`
- [ ] 12.4 Add production stage: `FROM node:18-alpine`, `WORKDIR /app`
- [ ] 12.5 Create non-root user: `RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001`
- [ ] 12.6 Copy standalone files: `COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./`
- [ ] 12.7 Copy static files: `COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static`
- [ ] 12.8 Copy public files: `COPY --from=builder --chown=nextjs:nodejs /app/public ./public`
- [ ] 12.9 Create data directory: `RUN mkdir -p /app/data && chown nextjs:nodejs /app/data`
- [ ] 12.10 Set environment variables: `ENV NODE_ENV=production PORT=3000 HOSTNAME=0.0.0.0`
- [ ] 12.11 Set user and expose port: `USER nextjs`, `EXPOSE 3000`
- [ ] 12.12 Add healthcheck: `HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1`
- [ ] 12.13 Set CMD: `CMD ["node", "server.js"]`
- [ ] 12.14 Test Dockerfile: run `docker build -t candle-annotator .` (should build successfully, check image size with `docker images`)
## 13. Docker - Compose Configuration
- [ ] 13.1 Create `docker-compose.yml` in project root
- [ ] 13.2 Set compose version: `version: '3.8'` (or use no version for latest)
- [ ] 13.3 Define service: `services: candle-annotator:`
- [ ] 13.4 Add build context: `build: .`
- [ ] 13.5 Add port mapping: `ports: - "3000:3000"`
- [ ] 13.6 Add volume mount: `volumes: - candle-data:/app/data`
- [ ] 13.7 Add environment variables: `environment: - NODE_ENV=production` (optionally use `env_file: .env`)
- [ ] 13.8 Add restart policy: `restart: unless-stopped`
- [ ] 13.9 Define named volume: `volumes: candle-data:`
- [ ] 13.10 Create `.env.example` file with template variables: `NODE_ENV=production`, `PORT=3000`, `DATABASE_PATH=/app/data/candles.db`
- [ ] 13.11 Test compose: run `docker-compose up --build` (should start container and be accessible at http://localhost:3000)
- [ ] 13.12 Test persistence: upload CSV, add annotations, stop container (`docker-compose down`), restart (`docker-compose up`), verify data persists
## 14. Documentation Updates
- [ ] 14.1 Read existing `DEPLOYMENT.md` file
- [ ] 14.2 Add "Docker Deployment" section with prerequisites (Docker, docker-compose installed)
- [ ] 14.3 Add build instructions: `docker-compose build` or `docker build -t candle-annotator .`
- [ ] 14.4 Add run instructions: `docker-compose up -d` for detached mode, `docker-compose logs -f` to view logs
- [ ] 14.5 Add environment setup: copy `.env.example` to `.env`, edit variables as needed
- [ ] 14.6 Add volume management: explain data persists in `candle-data` volume, show backup command `docker cp candle-annotator:/app/data/candles.db ./backup.db`
- [ ] 14.7 Add troubleshooting section: port conflicts (change PORT in .env), permission errors (check volume ownership), build failures (clear cache with `docker-compose build --no-cache`)
- [ ] 14.8 Add update procedure: `git pull`, `docker-compose down`, `docker-compose up --build -d`
- [ ] 14.9 Update `README.md` with Docker quickstart: add section "Docker Deployment" linking to DEPLOYMENT.md
- [ ] 14.10 Update `CLAUDE_DESCRIPTION.md` with new features: label management sidebar, hacker theme, Docker support
## 15. Integration Testing and Validation
- [ ] 15.1 Test full label workflow: create 10 break_up and 10 break_down labels using chart tools
- [ ] 15.2 Test label selection: click markers on chart (should highlight), verify selection state reflected in sidebar list
- [ ] 15.3 Test sidebar list: verify all 20 labels appear sorted by timestamp, click list item (should highlight marker on chart)
- [ ] 15.4 Test search: type partial timestamp (should filter list), clear search (should show all)
- [ ] 15.5 Test filter: select "Break Up" filter (should show only 10 items), select "Break Down" (should show other 10), select "All" (should show 20)
- [ ] 15.6 Test delete individual label: click trash icon on list item (should remove from list and chart), verify database updated
- [ ] 15.7 Test delete all labels: click "Delete All Labels" button, confirm dialog (all 20 should disappear), verify success toast appears
- [ ] 15.8 Test keyboard delete: create label, click to select, press Delete key (should remove), create another, press Backspace (should remove)
- [ ] 15.9 Test existing line features: draw lines (should still work), select lines (should still work), drag endpoints (should still work), delete lines (should still work)
- [ ] 15.10 Test theme visual appearance: verify monospace font on all text, green color scheme throughout, glow effects on hover, borders and shadows match specs
- [ ] 15.11 Test health endpoint: `curl http://localhost:3000/api/health` (should return 200 with { status: 'ok' }), test with db check `curl http://localhost:3000/api/health?check=db`
- [ ] 15.12 Test Docker deployment: build image, start container, access http://localhost:3000, upload CSV, create annotations, verify persistence after restart
- [ ] 15.13 Test contrast and accessibility: use browser dev tools to check contrast ratios (should meet WCAG AA 4.5:1), test with prefers-reduced-motion (animations should disable)
- [ ] 15.14 Verify no breaking changes: compare current functionality with pre-change state, confirm all original features work identically
- [ ] 15.15 Final smoke test: clean database, fresh Docker build, complete annotation workflow from CSV upload to export, verify output CSV includes all annotations
## 16. Commit and Cleanup
- [ ] 16.1 Review all changed files with `git status` and `git diff`
- [ ] 16.2 Commit label management changes: `git add src/app/page.tsx src/components/CandleChart.tsx src/components/Toolbox.tsx` and commit with message "feat: implement label management with sidebar list and search"
- [ ] 16.3 Commit API changes: `git add src/app/api/` and commit with message "feat: add bulk delete API and health endpoint"
- [ ] 16.4 Commit theme changes: `git add src/app/globals.css tailwind.config.ts src/app/layout.tsx src/components/ui/` and commit with message "feat: implement hacker theme with matrix colors and monospace fonts"
- [ ] 16.5 Commit Docker files: `git add Dockerfile docker-compose.yml .dockerignore .env.example next.config.js` and commit with message "feat: add Docker deployment with multi-stage build"
- [ ] 16.6 Commit documentation: `git add README.md DEPLOYMENT.md CLAUDE_DESCRIPTION.md` and commit with message "docs: update deployment and feature documentation"
- [ ] 16.7 Tag release: `git tag v2.0.0 -m "Release: Label management, Docker deployment, hacker theme"`
- [ ] 16.8 Push to remote: `git push origin master --tags`