Breakdown into 16 phases: 1. Setup and Configuration (3 tasks) 2. API Extensions for Bulk Operations (8 tasks) 3. Hacker Theme - CSS Variables and Tailwind (12 tasks) 4. Hacker Theme - Component Styling (6 tasks) 5. Label Management - State and Selection (9 tasks) 6. Label Management - Keyboard Delete (7 tasks) 7. Label Management - Sidebar List UI Structure (8 tasks) 8. Label Management - Sidebar List Content (11 tasks) 9. Label Management - Search and Filter (8 tasks) 10. Label Management - Delete All Labels Button (9 tasks) 11. Toast Feedback System (8 tasks) 12. Docker - Dockerfile Creation (14 tasks) 13. Docker - Compose Configuration (12 tasks) 14. Documentation Updates (10 tasks) 15. Integration Testing and Validation (15 tasks) 16. Commit and Cleanup (8 tasks) Each task is small, specific, and verifiable. Perfect for Haiku model execution.
19 KiB
19 KiB
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.jsto 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.tsDELETE handler - 2.2 Add query parameter parsing in DELETE handler (
typeandallparams fromrequest.nextUrl.searchParams) - 2.3 Add conditional WHERE clause logic: if
typeparam exists, filter bylabel_type IN (comma-separated types), ifall=truedelete everything, else return 400 error - 2.4 Update return value to include count:
{ success: true, deleted: <count> }usingresult.lengthfrom 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.tswith GET handler returning{ status: 'ok', timestamp: Date.now() } - 2.7 Add optional database check in health endpoint: if
?check=dbquery param exists, attempt simpleSELECT 1query 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.cssand note existing CSS variable names - 3.2 Replace CSS variable values in
:rootwith 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.cssto: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.tsand locatetheme.extend.colorsobject - 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.tsxand identify button elements - 4.2 Update button hover states in Toolbox to add glow effect: add
hover:shadow-glowclass to all Button components - 4.3 Update active tool buttons in Toolbox: when
activeToolmatches, addanimate-glow-pulseclass - 4.4 Update input field styling (search input in label list, if exists): add
focus:shadow-glow-sm focus:border-matrixclasses - 4.5 Read
src/components/ui/button.tsxand 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.tsxand 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.tsxand 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.tsxand 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 ifselectedLabelId !== 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.tsxand locate the export button section (bottom of component) - 7.2 Import Collapsible components from shadcn/ui: check if
src/components/ui/collapsible.tsxexists, if not install vianpx 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}andonOpenChange={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
breakUpCountandbreakDownCountfrom props.annotations filtered bylabel_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.tsxexists, if not install vianpx 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.tsxandsrc/hooks/use-toast.ts - 11.3 Update toast styling in
toast.tsx: addfont-monoclass, 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
.dockerignorefile in project root with contents:node_modules,.next,.git,data/,*.md,.env*,*.log,coverage/,.DS_Store - 12.2 Create
Dockerfilein project root: start withFROM 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 withdocker images)
13. Docker - Compose Configuration
- 13.1 Create
docker-compose.ymlin 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 useenv_file: .env) - 13.8 Add restart policy:
restart: unless-stopped - 13.9 Define named volume:
volumes: candle-data: - 13.10 Create
.env.examplefile 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.mdfile - 14.2 Add "Docker Deployment" section with prerequisites (Docker, docker-compose installed)
- 14.3 Add build instructions:
docker-compose buildordocker build -t candle-annotator . - 14.4 Add run instructions:
docker-compose up -dfor detached mode,docker-compose logs -fto view logs - 14.5 Add environment setup: copy
.env.exampleto.env, edit variables as needed - 14.6 Add volume management: explain data persists in
candle-datavolume, show backup commanddocker 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.mdwith Docker quickstart: add section "Docker Deployment" linking to DEPLOYMENT.md - 14.10 Update
CLAUDE_DESCRIPTION.mdwith 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 checkcurl 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 statusandgit diff - 16.2 Commit label management changes:
git add src/app/page.tsx src/components/CandleChart.tsx src/components/Toolbox.tsxand 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.jsand commit with message "feat: add Docker deployment with multi-stage build" - 16.6 Commit documentation:
git add README.md DEPLOYMENT.md CLAUDE_DESCRIPTION.mdand 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