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