chore: archive both OpenSpec changes and sync specs to main
This commit is contained in:
parent
a6e763c153
commit
50229e2ccf
25 changed files with 927 additions and 1 deletions
|
|
@ -0,0 +1,2 @@
|
|||
schema: spec-driven
|
||||
created: 2026-02-12
|
||||
|
|
@ -0,0 +1,439 @@
|
|||
# Design: Annotation Features and Deployment Enhancement
|
||||
|
||||
## Context
|
||||
|
||||
### Current State
|
||||
The candle annotator has a working annotation system with:
|
||||
- Line drawing with color selection, visual feedback, selection, and endpoint dragging (Phase 1-4 complete)
|
||||
- Label markers (break_up/break_down) added by clicking candles
|
||||
- Delete tool that removes annotations by clicking near them
|
||||
- SQLite database storing all annotations with geometry and color
|
||||
- Next.js 16 app with lightweight-charts for visualization
|
||||
- shadcn/ui components with dark slate theme
|
||||
- Local development workflow only
|
||||
|
||||
### Constraints
|
||||
- **No breaking changes**: Existing functionality must remain intact
|
||||
- **No new dependencies**: Use existing Next.js, React, Tailwind, shadcn/ui stack
|
||||
- **Single-user application**: No authentication or multi-user considerations
|
||||
- **SQLite database**: Must maintain compatibility with existing schema
|
||||
- **Lightweight-charts integration**: Chart library API is fixed, work within SVG overlay pattern
|
||||
- **Performance**: Chart rendering must maintain 60fps during annotation interactions
|
||||
|
||||
### Stakeholders
|
||||
- **Primary user**: Marko (developer/trader creating training data)
|
||||
- **Deployment target**: Server environment with Docker support
|
||||
- **Implementation**: Haiku model must be able to follow the implementation plan
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
1. Enable efficient bulk annotation management (delete all labels, delete all lines)
|
||||
2. Provide visibility into all annotations via sidebar list with search/filter
|
||||
3. Enable one-click production deployment via Docker
|
||||
4. Transform UI to distinctive hacker/terminal aesthetic while maintaining usability
|
||||
5. Create implementation plan detailed enough for Haiku model to execute
|
||||
|
||||
**Non-Goals:**
|
||||
1. Multi-user support or authentication
|
||||
2. Undo/redo functionality (can be added later)
|
||||
3. Import/export of individual annotations (CSV export already exists)
|
||||
4. Real-time collaboration features
|
||||
5. Mobile responsive design (desktop-focused tool)
|
||||
6. Internationalization (English only)
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision 1: Label Selection Architecture
|
||||
|
||||
**Choice**: Use separate state management for label selection parallel to existing line selection
|
||||
|
||||
**Why?**
|
||||
- Lines and labels are fundamentally different (geometry vs point markers)
|
||||
- Existing line selection in `SvgOverlay.tsx` works well, don't disturb it
|
||||
- Labels rendered by `CandleChart.tsx` using lightweight-charts markers API
|
||||
- Parallel state (`selectedLabelId` alongside `selectedLineId`) keeps concerns separated
|
||||
|
||||
**Alternatives Considered:**
|
||||
- **Unified selection system**: Single `selectedAnnotationId` for both lines and labels
|
||||
- ❌ Rejected: Would require refactoring working line selection code, higher risk
|
||||
- ❌ Different selection behaviors (lines: click geometry, labels: click marker)
|
||||
- **Event bus pattern**: Pub/sub for selection events
|
||||
- ❌ Rejected: Over-engineering for single-user app, adds complexity
|
||||
|
||||
**Implementation:**
|
||||
- Add `selectedLabelId` state in `page.tsx`
|
||||
- Pass `selectedLabelId` and `setSelectedLabelId` to `CandleChart.tsx`
|
||||
- Make markers clickable via `onClick` handler in marker creation
|
||||
- Highlight selected marker with custom scale and color properties
|
||||
- Deselect on Escape key (global keyboard handler in page component)
|
||||
|
||||
### Decision 2: Label List UI Structure
|
||||
|
||||
**Choice**: Collapsible section within existing Toolbox sidebar with virtualized scrolling
|
||||
|
||||
**Why?**
|
||||
- Keeps all controls in one place (consistency)
|
||||
- Toolbox already has vertical space for expansion
|
||||
- Users may have hundreds/thousands of labels, need efficient rendering
|
||||
- No need for new layout areas or floating panels
|
||||
|
||||
**Alternatives Considered:**
|
||||
- **Separate right sidebar**: New panel on right side of chart
|
||||
- ❌ Rejected: Takes screen real estate from chart, awkward horizontal layout
|
||||
- **Modal/overlay**: Floating panel over chart
|
||||
- ❌ Rejected: Blocks chart view, poor UX for browsing annotations while viewing data
|
||||
- **Bottom panel**: Horizontal list below chart
|
||||
- ❌ Rejected: Vertical list more natural for timestamped items, less vertical space for chart
|
||||
|
||||
**Implementation:**
|
||||
- Add `<Collapsible>` section in Toolbox below tool buttons, above Export
|
||||
- Section header: "Label Annotations (X)" with expand/collapse icon
|
||||
- When expanded: search input, filter dropdown, scrollable list (max-height: 400px)
|
||||
- Each list item: formatted timestamp, colored badge, delete button
|
||||
- Click item to select/highlight on chart
|
||||
- Use CSS `overflow-y: auto` with custom scrollbar styling (no virtualization needed initially)
|
||||
- If performance issues with 1000+ items, add `react-window` in future iteration
|
||||
|
||||
### Decision 3: API Design for Bulk Operations
|
||||
|
||||
**Choice**: Extend existing DELETE `/api/annotations` with query parameters
|
||||
|
||||
**Why?**
|
||||
- RESTful approach: DELETE resource with filters
|
||||
- No new endpoints to maintain
|
||||
- Backwards compatible (no query params = existing single-delete behavior still works via `/api/annotations/[id]`)
|
||||
- Simple to implement and test
|
||||
|
||||
**Query Parameter Schema:**
|
||||
```
|
||||
DELETE /api/annotations?type=line // Delete all lines
|
||||
DELETE /api/annotations?type=break_up // Delete all break_up labels
|
||||
DELETE /api/annotations?type=break_down // Delete all break_down labels
|
||||
DELETE /api/annotations?type=break_up,break_down // Delete all labels
|
||||
DELETE /api/annotations?all=true // Delete everything (nuclear option)
|
||||
```
|
||||
|
||||
**Alternatives Considered:**
|
||||
- **POST /api/annotations/bulk-delete**: Separate endpoint
|
||||
- ❌ Rejected: Not RESTful (POST for delete), adds complexity
|
||||
- **DELETE with request body**: `{ types: ['break_up', 'break_down'] }`
|
||||
- ❌ Rejected: DELETE with body is controversial in HTTP semantics, some proxies strip bodies
|
||||
- **Separate endpoints per type**: `/api/annotations/lines`, `/api/annotations/labels`
|
||||
- ❌ Rejected: More endpoints to maintain, inconsistent with current design
|
||||
|
||||
**Implementation:**
|
||||
- Modify `src/app/api/annotations/route.ts` DELETE handler
|
||||
- Parse query params: `const { type, all } = request.nextUrl.searchParams`
|
||||
- Use Drizzle ORM with conditional WHERE clause
|
||||
- Return `{ success: true, deleted: <count> }` for confirmation UI
|
||||
|
||||
### Decision 4: Docker Strategy - Standalone Output
|
||||
|
||||
**Choice**: Next.js standalone output mode with multi-stage build
|
||||
|
||||
**Why?**
|
||||
- Minimal production bundle (~50MB for Next.js runtime vs ~500MB with full node_modules)
|
||||
- Fast container builds (cached layers for dependencies)
|
||||
- Official Next.js recommendation for containerization
|
||||
- Better-sqlite3 works in standalone mode (native module properly bundled)
|
||||
|
||||
**Dockerfile Structure:**
|
||||
```dockerfile
|
||||
# Stage 1: Build
|
||||
FROM node:18-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Production
|
||||
FROM node:18-alpine
|
||||
WORKDIR /app
|
||||
RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
||||
USER nextjs
|
||||
EXPOSE 3000
|
||||
CMD ["node", "server.js"]
|
||||
```
|
||||
|
||||
**Alternatives Considered:**
|
||||
- **Full node_modules copy**: Copy entire node_modules to production
|
||||
- ❌ Rejected: 5-10x larger images, slower deployments, unnecessary dev dependencies
|
||||
- **Distroless base images**: Google distroless/nodejs
|
||||
- ❌ Rejected: No shell for debugging, alpine is small enough, better-sqlite3 compatibility unclear
|
||||
- **Docker layer caching with pnpm/yarn**: Different package manager
|
||||
- ❌ Rejected: Project uses npm, no reason to change, npm v7+ has similar caching
|
||||
|
||||
**Database Persistence:**
|
||||
- Mount `/app/data` as Docker volume
|
||||
- SQLite file at `/app/data/candles.db`
|
||||
- Named volume in docker-compose: `candle-data:/app/data`
|
||||
|
||||
### Decision 5: Theme Implementation - CSS Variables + Tailwind Extension
|
||||
|
||||
**Choice**: Replace existing CSS variables in `globals.css` with hacker theme values, extend Tailwind config with custom colors and animations
|
||||
|
||||
**Why?**
|
||||
- Minimal refactoring: existing shadcn/ui components already use CSS variables
|
||||
- Tailwind extension provides utility classes for new theme features (glow effects, animations)
|
||||
- No component rewrites needed, just style overrides
|
||||
- Easy to toggle/revert if needed (swap CSS variable values)
|
||||
|
||||
**Color Mapping Strategy:**
|
||||
```css
|
||||
/* globals.css - override existing variables */
|
||||
:root {
|
||||
--background: 120 100% 4%; /* #0a0e0a - very dark green */
|
||||
--foreground: 120 100% 50%; /* #00ff41 - matrix green */
|
||||
--primary: 120 100% 50%; /* #00ff41 */
|
||||
--destructive: 348 100% 50%; /* #ff0040 - neon red */
|
||||
--border: 120 100% 10%; /* #003311 - dim green */
|
||||
/* ... map all existing variables to hacker theme equivalents */
|
||||
}
|
||||
```
|
||||
|
||||
**Tailwind Extension:**
|
||||
```typescript
|
||||
// tailwind.config.ts - extend existing config
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
matrix: '#00ff41',
|
||||
matrixDim: '#00cc33',
|
||||
matrixDark: '#003311',
|
||||
neonRed: '#ff0040',
|
||||
neonCyan: '#00d4ff',
|
||||
// ... keep existing shadcn colors
|
||||
},
|
||||
fontFamily: {
|
||||
mono: ['JetBrains Mono', 'Fira Code', 'Courier New', 'monospace'],
|
||||
},
|
||||
boxShadow: {
|
||||
'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)',
|
||||
},
|
||||
keyframes: {
|
||||
'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' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'glow-pulse': 'glow-pulse 2s ease-in-out infinite',
|
||||
'flicker': 'flicker 0.1s ease-in-out',
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Alternatives Considered:**
|
||||
- **Complete rewrite with different UI library**: Styled-components, Emotion
|
||||
- ❌ Rejected: Massive effort, no benefit, Tailwind + shadcn works well
|
||||
- **Theme toggle system**: Support multiple themes with switcher
|
||||
- ❌ Rejected: Not in requirements, adds complexity, hacker theme is the goal
|
||||
- **CSS-in-JS for glow effects**: Inline styles or styled-jsx
|
||||
- ❌ Rejected: Inconsistent with existing Tailwind approach, harder to maintain
|
||||
|
||||
**Font Loading:**
|
||||
- Add JetBrains Mono via Google Fonts in `layout.tsx` `<head>`
|
||||
- Fallback chain: JetBrains Mono → Fira Code → Courier New → system monospace
|
||||
- Set `font-family: var(--font-mono)` on body element
|
||||
|
||||
### Decision 6: Feedback Messages - Toast Component
|
||||
|
||||
**Choice**: Create reusable `Toast` component using shadcn/ui toast primitive, styled for terminal aesthetic
|
||||
|
||||
**Why?**
|
||||
- Consistent with existing shadcn/ui architecture
|
||||
- Already have toast primitive in `components/ui/` (likely from shadcn init)
|
||||
- Position: top-center, auto-dismiss after 4s
|
||||
- Terminal-style formatting: `> SUCCESS: Operation completed [3 items]`
|
||||
|
||||
**Implementation:**
|
||||
```typescript
|
||||
// src/components/ui/toast.tsx - style overrides
|
||||
// Add terminal-style formatting
|
||||
className="font-mono border-matrix bg-terminal text-matrix"
|
||||
|
||||
// Usage in components:
|
||||
import { useToast } from '@/hooks/use-toast'
|
||||
const { toast } = useToast()
|
||||
|
||||
toast({
|
||||
title: "> SUCCESS",
|
||||
description: "Deleted all labels [15 items]",
|
||||
variant: "default", // green
|
||||
})
|
||||
|
||||
toast({
|
||||
title: "> ERROR",
|
||||
description: "Failed to delete annotations [code: 500]",
|
||||
variant: "destructive", // red
|
||||
})
|
||||
```
|
||||
|
||||
**Alternatives Considered:**
|
||||
- **Custom notification system**: Build from scratch
|
||||
- ❌ Rejected: Reinventing the wheel, shadcn toast is battle-tested
|
||||
- **Browser alert()**: Native dialogs
|
||||
- ❌ Rejected: Ugly, blocks UI, no styling, poor UX
|
||||
- **Console.log only**: No visual feedback
|
||||
- ❌ Rejected: User won't see feedback, poor UX
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
### Risk 1: Theme Accessibility
|
||||
**Risk**: Neon green on black may strain eyes during extended use, contrast issues for colorblind users
|
||||
|
||||
**Mitigation:**
|
||||
- Ensure WCAG AA contrast ratios (4.5:1 for text, 3:1 for UI elements)
|
||||
- Use #00ff41 which has sufficient contrast on #0a0e0a background
|
||||
- Provide option to revert theme in future (environment variable: `THEME=classic`)
|
||||
- Include prefers-reduced-motion support to disable animations
|
||||
|
||||
**Trade-off**: Distinctive aesthetic vs universal accessibility (accepting some users may prefer different theme)
|
||||
|
||||
### Risk 2: Label List Performance with Large Datasets
|
||||
**Risk**: 10,000+ labels may cause slow rendering and sluggish scrolling in sidebar list
|
||||
|
||||
**Mitigation:**
|
||||
- Set reasonable max-height (400px) for scrollable area
|
||||
- If performance issues arise, add pagination or react-window virtualization in follow-up
|
||||
- Monitor performance during development with test data (1000, 5000, 10000 labels)
|
||||
|
||||
**Trade-off**: Simple initial implementation vs premature optimization (YAGNI principle - add virtualization only if needed)
|
||||
|
||||
### Risk 3: Docker Image Size for Better-SQLite3
|
||||
**Risk**: Native SQLite module may require additional build dependencies in alpine, increasing image size
|
||||
|
||||
**Mitigation:**
|
||||
- Node:18-alpine includes necessary build tools (python, make, g++)
|
||||
- Better-sqlite3 compiles during npm install in build stage
|
||||
- Final image only includes compiled binary, not build tools
|
||||
- Test build locally: `docker build --progress=plain .` to verify size (<200MB target)
|
||||
|
||||
**Trade-off**: Alpine convenience vs potential build complexity (alpine is still best choice despite native modules)
|
||||
|
||||
### Risk 4: Breaking Existing Line Selection
|
||||
**Risk**: Adding label selection state and keyboard handlers may interfere with existing line selection logic
|
||||
|
||||
**Mitigation:**
|
||||
- Keep line and label selection completely separate (different state variables)
|
||||
- Keyboard handler checks both states: if label selected, delete label; if line selected, delete line
|
||||
- Thoroughly test all existing line operations (draw, select, drag, delete) after changes
|
||||
- Use TypeScript strict mode to catch state management issues
|
||||
|
||||
**Trade-off**: Duplicate code for selection logic vs refactoring working code (choose stability)
|
||||
|
||||
### Risk 5: Next.js Standalone Mode Compatibility
|
||||
**Risk**: Standalone output may not include all necessary files (static assets, environment configs)
|
||||
|
||||
**Mitigation:**
|
||||
- Follow official Next.js documentation for standalone mode
|
||||
- Explicitly copy `.next/static/` and `public/` directories in Dockerfile
|
||||
- Test docker build locally before committing
|
||||
- Verify all pages, API routes, and static assets work in container
|
||||
|
||||
**Trade-off**: Smaller image size vs potential missing files (standalone is well-tested by community)
|
||||
|
||||
### Risk 6: Confirmation Dialog UX
|
||||
**Risk**: Users may accidentally confirm bulk delete operations
|
||||
|
||||
**Mitigation:**
|
||||
- Use clear, explicit confirmation messages: "Delete all X annotations? This cannot be undone."
|
||||
- Require deliberate click on "Confirm" button (not Enter key, no auto-focus)
|
||||
- Show count of items to be deleted in confirmation message
|
||||
- Use destructive button styling (red) to signal danger
|
||||
- No undo system (out of scope), but users can re-annotate from CSV data
|
||||
|
||||
**Trade-off**: Safety vs speed (prioritize safety for destructive operations)
|
||||
|
||||
## Migration Plan
|
||||
|
||||
### Phase 1: Label Management (No Deployment Impact)
|
||||
1. Add `selectedLabelId` state and selection handlers in `CandleChart.tsx`
|
||||
2. Build label list UI in `Toolbox.tsx` (collapsible section)
|
||||
3. Implement search/filter functionality
|
||||
4. Add keyboard delete handler for labels
|
||||
5. Test with existing database, no schema changes
|
||||
6. **Deployment**: None (dev only), commit after testing
|
||||
|
||||
### Phase 2: API Extensions (Backwards Compatible)
|
||||
1. Modify `DELETE /api/annotations` to accept query params
|
||||
2. Add bulk delete logic with Drizzle ORM
|
||||
3. Create `GET /api/health` endpoint
|
||||
4. Test bulk operations with Postman/curl
|
||||
5. **Deployment**: None (API changes backwards compatible), commit after testing
|
||||
|
||||
### Phase 3: Hacker Theme (Visual Only)
|
||||
1. Load JetBrains Mono font in `layout.tsx`
|
||||
2. Update `globals.css` with new CSS variables
|
||||
3. Extend `tailwind.config.ts` with custom colors, shadows, animations
|
||||
4. Apply theme classes to existing components (Toolbox, buttons, inputs)
|
||||
5. Test visual appearance and contrast ratios
|
||||
6. **Deployment**: None (CSS/styling only), commit after visual review
|
||||
|
||||
### Phase 4: Docker Setup (New Deployment Path)
|
||||
1. Update `next.config.js` to enable standalone output
|
||||
2. Create `Dockerfile` with multi-stage build
|
||||
3. Create `docker-compose.yml` with volume configuration
|
||||
4. Create `.dockerignore` and `.env.example`
|
||||
5. Test local build: `docker-compose up --build`
|
||||
6. Verify database persistence across container restarts
|
||||
7. Update `DEPLOYMENT.md` with Docker instructions
|
||||
8. **Deployment**: Push image to registry, deploy to production server
|
||||
|
||||
### Rollback Strategy
|
||||
- **Label Management**: No rollback needed (additive feature, disable by not using)
|
||||
- **API Extensions**: Backwards compatible, no rollback needed
|
||||
- **Hacker Theme**: Revert `globals.css` and `tailwind.config.ts` commits
|
||||
- **Docker**: Rollback to direct Node.js deployment, use existing dev workflow
|
||||
|
||||
### Testing Checklist
|
||||
- [ ] All existing line drawing features work unchanged
|
||||
- [ ] Label selection highlights correct marker on chart
|
||||
- [ ] Label list displays all annotations with correct formatting
|
||||
- [ ] Search/filter correctly narrows label list
|
||||
- [ ] Delete individual label removes from chart and list
|
||||
- [ ] Delete all labels removes all markers after confirmation
|
||||
- [ ] Bulk delete API endpoints return correct counts
|
||||
- [ ] Health check endpoint returns 200
|
||||
- [ ] Theme maintains WCAG AA contrast ratios
|
||||
- [ ] All text uses monospace font
|
||||
- [ ] Glow effects appear on hover/active states
|
||||
- [ ] Docker container starts successfully
|
||||
- [ ] Database persists across container restarts
|
||||
- [ ] Application accessible on http://localhost:3000 in container
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Should we add label editing (change type)?**
|
||||
- Not in current scope, but consider for future
|
||||
- Would require modal dialog to change break_up ↔ break_down
|
||||
|
||||
2. **Should label list show price information?**
|
||||
- Specs don't mention it, but could be useful
|
||||
- Decision: Show timestamp and type only initially, add price in follow-up if requested
|
||||
|
||||
3. **Should we limit the number of labels displayed in sidebar?**
|
||||
- Pagination vs infinite scroll vs show all
|
||||
- Decision: Show all initially, add pagination only if performance issues arise
|
||||
|
||||
4. **Should Docker image include sample CSV data?**
|
||||
- Useful for testing, but increases image size
|
||||
- Decision: No, keep image minimal, provide sample CSV in repo documentation
|
||||
|
||||
5. **Should we add keyboard shortcuts for tool switching (e.g., 'L' for line tool)?**
|
||||
- Not in requirements, but could enhance UX
|
||||
- Decision: Out of scope for this change, consider in future UX iteration
|
||||
|
||||
These questions do not block implementation - proceed with spec-defined behavior and defer enhancements.
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
## Why
|
||||
|
||||
The candle annotator currently has basic line drawing functionality (Phase 1-4 complete: color support, visual feedback, selection, and endpoint dragging). Users need more comprehensive annotation management including bulk deletion, label CRUD operations, production deployment capability, and an improved UI theme. These features are essential for efficient annotation workflows and real-world deployment scenarios.
|
||||
|
||||
## What Changes
|
||||
|
||||
### Label Management System
|
||||
- **Delete All Labels**: Add button to remove all break_up/break_down markers with confirmation
|
||||
- **Select Label**: Make label markers (arrows) clickable to select/highlight them
|
||||
- **Delete Specific Label**: Delete individual selected label marker (keyboard or UI button)
|
||||
- **Label CRUD UI**: Add collapsible section in Toolbox sidebar showing:
|
||||
- List of all label annotations (timestamp, type, color-coded)
|
||||
- Click to select/highlight on chart
|
||||
- Delete button per label
|
||||
- Count of each label type
|
||||
- Search/filter by type
|
||||
|
||||
### Docker Deployment
|
||||
- **Dockerization**: Create production-ready Docker setup:
|
||||
- Multi-stage Dockerfile (build + runtime)
|
||||
- docker-compose.yml for easy deployment
|
||||
- Volume mounting for persistent SQLite database
|
||||
- Environment variable configuration
|
||||
- Health check endpoint
|
||||
- Production build optimization
|
||||
- Documentation in DEPLOYMENT.md
|
||||
|
||||
### UI Theme Enhancement
|
||||
- **Hacker-Style Theme**: Transform interface to minimal, terminal-inspired design:
|
||||
- Monospace fonts throughout
|
||||
- Matrix-style green (#00ff41) accents on dark background
|
||||
- Neon glow effects on active elements
|
||||
- Simplified borders with ASCII-style corners
|
||||
- Terminal-like command feedback messages
|
||||
- Minimalist icons or ASCII art alternatives
|
||||
- High contrast for readability
|
||||
- Cyber/retro aesthetic without sacrificing usability
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `label-management`: Complete CRUD interface for label annotations in sidebar
|
||||
- `docker-deployment`: Containerized deployment with docker-compose
|
||||
- `hacker-theme`: Terminal-inspired minimal dark theme with neon accents
|
||||
|
||||
### Modified Capabilities
|
||||
- (none - only adding new features, not changing existing requirements)
|
||||
|
||||
## Impact
|
||||
|
||||
### Code Changes
|
||||
- **Database**: No schema changes needed (existing tables support all operations)
|
||||
- **Components**:
|
||||
- `Toolbox.tsx`: Add label list UI, delete buttons, collapsible sections, theme styling
|
||||
- `CandleChart.tsx`: Add label selection click handlers, highlight logic
|
||||
- `page.tsx`: Add state for selected label, pass callbacks
|
||||
- **API Routes**:
|
||||
- `DELETE /api/annotations`: Add query param for bulk delete (by type or all)
|
||||
- `GET /api/health`: New endpoint for Docker health checks
|
||||
- **Styling**: Update `globals.css` and Tailwind config for hacker theme
|
||||
- **New Files**:
|
||||
- `Dockerfile`
|
||||
- `docker-compose.yml`
|
||||
- `.dockerignore`
|
||||
- `.env.example`
|
||||
|
||||
### Dependencies
|
||||
- No new npm packages required (existing stack covers all needs)
|
||||
- Docker runtime required for deployment
|
||||
|
||||
### Deployment
|
||||
- Current dev workflow unchanged
|
||||
- New production deployment path via Docker
|
||||
- Database persistence via volume mounts
|
||||
- Environment-based configuration
|
||||
|
||||
### User Experience
|
||||
- More efficient annotation workflows (bulk operations)
|
||||
- Better annotation visibility and organization (sidebar list)
|
||||
- Easier deployment to servers (docker-compose up)
|
||||
- More visually distinctive theme (hacker aesthetic)
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Multi-stage Dockerfile
|
||||
The project SHALL include a Dockerfile with multi-stage build for optimized production images.
|
||||
|
||||
#### Scenario: Build stage setup
|
||||
- **WHEN** Dockerfile build stage executes
|
||||
- **THEN** uses Node.js 18-alpine base image, copies package files, installs ALL dependencies including devDependencies, copies source code, and runs `npm run build`
|
||||
|
||||
#### Scenario: Runtime stage setup
|
||||
- **WHEN** Dockerfile runtime stage executes
|
||||
- **THEN** uses Node.js 18-alpine base image, creates non-root user 'appuser', copies only production dependencies and built files from build stage, and sets USER to appuser
|
||||
|
||||
#### Scenario: Working directory structure
|
||||
- **WHEN** container runs
|
||||
- **THEN** application files are in /app directory, database volume mounts to /app/data, and permissions allow appuser to write to /app/data
|
||||
|
||||
#### Scenario: Environment variables in Dockerfile
|
||||
- **WHEN** Dockerfile defines environment
|
||||
- **THEN** sets NODE_ENV=production, PORT=3000, and HOSTNAME=0.0.0.0 for Next.js standalone server
|
||||
|
||||
#### Scenario: Exposed ports
|
||||
- **WHEN** container is built
|
||||
- **THEN** Dockerfile exposes port 3000 for HTTP traffic
|
||||
|
||||
#### Scenario: Container startup
|
||||
- **WHEN** container starts
|
||||
- **THEN** executes `node server.js` (Next.js standalone output) as the CMD
|
||||
|
||||
### Requirement: Docker Compose configuration
|
||||
The project SHALL include docker-compose.yml for simplified deployment orchestration.
|
||||
|
||||
#### Scenario: Service definition
|
||||
- **WHEN** docker-compose.yml is parsed
|
||||
- **THEN** defines single service named 'candle-annotator' using Dockerfile from current directory
|
||||
|
||||
#### Scenario: Port mapping
|
||||
- **WHEN** docker-compose up runs
|
||||
- **THEN** maps host port 3000 to container port 3000 (configurable via PORT environment variable)
|
||||
|
||||
#### Scenario: Volume mounting for database
|
||||
- **WHEN** docker-compose up runs
|
||||
- **THEN** mounts named volume 'candle-data' to /app/data in container for SQLite database persistence
|
||||
|
||||
#### Scenario: Environment variable configuration
|
||||
- **WHEN** docker-compose.yml is used
|
||||
- **THEN** supports loading environment variables from .env file for NODE_ENV, PORT, and other configs
|
||||
|
||||
#### Scenario: Restart policy
|
||||
- **WHEN** container crashes or stops
|
||||
- **THEN** docker-compose automatically restarts container unless explicitly stopped (restart: unless-stopped)
|
||||
|
||||
#### Scenario: Volume declaration
|
||||
- **WHEN** docker-compose.yml is parsed
|
||||
- **THEN** declares 'candle-data' as named volume in volumes section
|
||||
|
||||
### Requirement: Environment variable configuration
|
||||
The project SHALL use environment variables for runtime configuration.
|
||||
|
||||
#### Scenario: .env.example file
|
||||
- **WHEN** repository is cloned
|
||||
- **THEN** includes .env.example file documenting all configurable environment variables with example values
|
||||
|
||||
#### Scenario: PORT configuration
|
||||
- **WHEN** PORT environment variable is set
|
||||
- **THEN** Next.js server listens on specified port (default: 3000)
|
||||
|
||||
#### Scenario: NODE_ENV configuration
|
||||
- **WHEN** NODE_ENV environment variable is set to 'production'
|
||||
- **THEN** Next.js runs in production mode with optimizations enabled
|
||||
|
||||
#### Scenario: Database path configuration
|
||||
- **WHEN** DATABASE_PATH environment variable is set (optional)
|
||||
- **THEN** SQLite database file is created at specified path (default: ./data/candles.db)
|
||||
|
||||
#### Scenario: HOSTNAME configuration
|
||||
- **WHEN** HOSTNAME environment variable is set
|
||||
- **THEN** Next.js server binds to specified hostname (default: 0.0.0.0 for containers)
|
||||
|
||||
### Requirement: Health check endpoint
|
||||
The API SHALL provide a health check endpoint for container orchestration.
|
||||
|
||||
#### Scenario: Health check endpoint responds
|
||||
- **WHEN** GET request sent to `/api/health`
|
||||
- **THEN** system returns 200 status with JSON `{ status: 'ok', timestamp: <unix_timestamp> }`
|
||||
|
||||
#### Scenario: Database connection check
|
||||
- **WHEN** GET request sent to `/api/health?check=db`
|
||||
- **THEN** system attempts simple database query and returns 200 if successful, 503 if database unavailable
|
||||
|
||||
#### Scenario: Health check in Dockerfile
|
||||
- **WHEN** Dockerfile defines HEALTHCHECK
|
||||
- **THEN** runs `curl -f http://localhost:3000/api/health || exit 1` every 30 seconds with 3 retries
|
||||
|
||||
### Requirement: .dockerignore file
|
||||
The project SHALL include .dockerignore to exclude unnecessary files from Docker context.
|
||||
|
||||
#### Scenario: Excluded files
|
||||
- **WHEN** Docker build context is created
|
||||
- **THEN** .dockerignore excludes node_modules, .next, .git, data/, *.md, .env*, and test files
|
||||
|
||||
#### Scenario: Included files
|
||||
- **WHEN** Docker build context is created
|
||||
- **THEN** includes package.json, package-lock.json, source code in src/, and required config files
|
||||
|
||||
### Requirement: Next.js standalone output
|
||||
The build SHALL use Next.js standalone output mode for minimal production bundle.
|
||||
|
||||
#### Scenario: next.config.js standalone setting
|
||||
- **WHEN** next.config.js is read
|
||||
- **THEN** output property is set to 'standalone'
|
||||
|
||||
#### Scenario: Standalone build output
|
||||
- **WHEN** npm run build executes
|
||||
- **THEN** Next.js creates .next/standalone directory with minimal runtime files and dependencies
|
||||
|
||||
#### Scenario: Copy standalone files to image
|
||||
- **WHEN** Dockerfile runtime stage executes
|
||||
- **THEN** copies .next/standalone/ contents to /app, copies .next/static to /app/.next/static, and copies public/ to /app/public
|
||||
|
||||
### Requirement: Production build optimization
|
||||
The Docker image SHALL be optimized for production use with minimal size.
|
||||
|
||||
#### Scenario: Use alpine base images
|
||||
- **WHEN** Dockerfile specifies base images
|
||||
- **THEN** uses node:18-alpine for both build and runtime stages
|
||||
|
||||
#### Scenario: Multi-stage build cleanup
|
||||
- **WHEN** Docker image is built
|
||||
- **THEN** build artifacts, devDependencies, and source files are not included in final image
|
||||
|
||||
#### Scenario: Layer caching optimization
|
||||
- **WHEN** Dockerfile is structured
|
||||
- **THEN** package.json and package-lock.json are copied and dependencies installed before source code copy for better layer caching
|
||||
|
||||
#### Scenario: Final image size
|
||||
- **WHEN** Docker image build completes
|
||||
- **THEN** final image size is under 200MB (excluding data volume)
|
||||
|
||||
### Requirement: Database persistence
|
||||
The deployment SHALL ensure SQLite database persists across container restarts.
|
||||
|
||||
#### Scenario: Volume mounting
|
||||
- **WHEN** container runs with volume mount
|
||||
- **THEN** /app/data directory is mounted from host or Docker volume
|
||||
|
||||
#### Scenario: Database file location
|
||||
- **WHEN** application initializes database
|
||||
- **THEN** SQLite file is created at /app/data/candles.db (inside mounted volume)
|
||||
|
||||
#### Scenario: Container restart preserves data
|
||||
- **WHEN** container is stopped and restarted
|
||||
- **THEN** existing database file is reused and all annotations and candles remain intact
|
||||
|
||||
#### Scenario: File permissions
|
||||
- **WHEN** container creates database file
|
||||
- **THEN** appuser has read/write permissions to /app/data directory
|
||||
|
||||
### Requirement: Deployment documentation
|
||||
DEPLOYMENT.md SHALL include comprehensive Docker deployment instructions.
|
||||
|
||||
#### Scenario: Docker deployment section
|
||||
- **WHEN** DEPLOYMENT.md is read
|
||||
- **THEN** includes dedicated "Docker Deployment" section with prerequisites, build steps, and run commands
|
||||
|
||||
#### Scenario: Quick start commands
|
||||
- **WHEN** following deployment docs
|
||||
- **THEN** provides complete commands: `docker-compose up -d` for production and `docker-compose up --build` for rebuilding
|
||||
|
||||
#### Scenario: Environment setup instructions
|
||||
- **WHEN** following deployment docs
|
||||
- **THEN** explains how to copy .env.example to .env and configure required variables
|
||||
|
||||
#### Scenario: Volume backup instructions
|
||||
- **WHEN** following deployment docs
|
||||
- **THEN** provides commands to backup database: `docker cp candle-annotator:/app/data/candles.db ./backup.db`
|
||||
|
||||
#### Scenario: Troubleshooting section
|
||||
- **WHEN** deployment issues occur
|
||||
- **THEN** DEPLOYMENT.md includes troubleshooting for common Docker issues: port conflicts, permission errors, build failures
|
||||
|
||||
#### Scenario: Update and maintenance
|
||||
- **WHEN** updating deployed application
|
||||
- **THEN** documentation provides steps: pull new code, rebuild image, restart containers with data preservation
|
||||
|
||||
### Requirement: Container security
|
||||
The Docker setup SHALL follow security best practices.
|
||||
|
||||
#### Scenario: Non-root user
|
||||
- **WHEN** container runs
|
||||
- **THEN** application process runs as non-root user 'appuser' (UID 1000)
|
||||
|
||||
#### Scenario: Read-only filesystem where possible
|
||||
- **WHEN** container runs
|
||||
- **THEN** only /app/data directory requires write permissions, all other files are read-only to appuser
|
||||
|
||||
#### Scenario: No sensitive data in image
|
||||
- **WHEN** Docker image is built
|
||||
- **THEN** .env files, secrets, and database files are not included in image layers
|
||||
|
||||
#### Scenario: Minimal attack surface
|
||||
- **WHEN** container runs
|
||||
- **THEN** only port 3000 is exposed, no SSH, no unnecessary services, alpine base reduces package vulnerabilities
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Monospace typography
|
||||
The application SHALL use monospace fonts throughout the entire interface.
|
||||
|
||||
#### Scenario: Primary font family
|
||||
- **WHEN** CSS font-family is applied
|
||||
- **THEN** system uses font stack: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace
|
||||
|
||||
#### Scenario: Font loading
|
||||
- **WHEN** application loads
|
||||
- **THEN** system loads JetBrains Mono font from Google Fonts or local files with weights: 400, 500, 700
|
||||
|
||||
#### Scenario: All text elements
|
||||
- **WHEN** rendering any text (buttons, labels, inputs, tooltips, headings)
|
||||
- **THEN** all text uses monospace font family without exception
|
||||
|
||||
#### Scenario: Number formatting
|
||||
- **WHEN** displaying numbers (prices, timestamps, counts)
|
||||
- **THEN** uses tabular-nums CSS property for aligned columns
|
||||
|
||||
### Requirement: Matrix-style color scheme
|
||||
The application SHALL use a dark background with neon green (#00ff41) accent color scheme.
|
||||
|
||||
#### Scenario: Background colors
|
||||
- **WHEN** rendering main layout
|
||||
- **THEN** page background is #0a0e0a (near black), sidebar background is #0d110d, and chart background is #000000
|
||||
|
||||
#### Scenario: Primary accent color
|
||||
- **WHEN** highlighting interactive elements, active states, or success messages
|
||||
- **THEN** uses #00ff41 (matrix green) as the primary accent color
|
||||
|
||||
#### Scenario: Secondary accent colors
|
||||
- **WHEN** rendering labels or status indicators
|
||||
- **THEN** uses #ff0040 (neon red) for break_down/destructive, #00ff41 (neon green) for break_up/success, #00d4ff (neon cyan) for informational, #ffff00 (neon yellow) for warnings
|
||||
|
||||
#### Scenario: Text colors
|
||||
- **WHEN** rendering text
|
||||
- **THEN** primary text is #00ff41, secondary text is #00cc33 (dimmer green), and disabled text is #003311 (very dim green)
|
||||
|
||||
#### Scenario: Border colors
|
||||
- **WHEN** rendering borders
|
||||
- **THEN** uses #00ff41 with 1px solid for active elements, #003311 for inactive borders
|
||||
|
||||
### Requirement: Neon glow effects
|
||||
The application SHALL apply glow effects to active and interactive elements.
|
||||
|
||||
#### Scenario: Button hover glow
|
||||
- **WHEN** user hovers over any button
|
||||
- **THEN** button shows box-shadow: 0 0 10px #00ff41, 0 0 20px #00ff4180
|
||||
|
||||
#### Scenario: Active tool glow
|
||||
- **WHEN** a tool is active (selected)
|
||||
- **THEN** button shows pulsing glow animation with box-shadow: 0 0 15px #00ff41, 0 0 30px #00ff4180
|
||||
|
||||
#### Scenario: Selected element glow
|
||||
- **WHEN** a line or label is selected
|
||||
- **THEN** element shows glow effect with filter: drop-shadow(0 0 8px #00ff41)
|
||||
|
||||
#### Scenario: Input focus glow
|
||||
- **WHEN** user focuses on text input or search field
|
||||
- **THEN** input shows border glow: box-shadow: 0 0 8px #00ff41
|
||||
|
||||
#### Scenario: Cursor circle glow
|
||||
- **WHEN** drawing line with cursor circle visible
|
||||
- **THEN** cursor circle has stroke glow with filter: drop-shadow(0 0 4px currentColor)
|
||||
|
||||
### Requirement: ASCII-style borders
|
||||
The application SHALL use terminal-inspired border styling with corner characters.
|
||||
|
||||
#### Scenario: Container borders
|
||||
- **WHEN** rendering bordered containers (Toolbox, label list sections)
|
||||
- **THEN** uses 1px solid borders with #00ff41 color
|
||||
|
||||
#### Scenario: Border corners with pseudo-elements
|
||||
- **WHEN** rendering section headers or important containers
|
||||
- **THEN** uses CSS ::before and ::after pseudo-elements to display ASCII corner characters: ┌ ┐ └ ┘
|
||||
|
||||
#### Scenario: Separator lines
|
||||
- **WHEN** rendering section separators
|
||||
- **THEN** uses border-top: 1px solid #003311 with optional ASCII characters (─) via pseudo-elements
|
||||
|
||||
#### Scenario: Card-style containers
|
||||
- **WHEN** rendering label list entries or modal dialogs
|
||||
- **THEN** containers have 1px solid border with subtle inset shadow for depth
|
||||
|
||||
### Requirement: Terminal-like feedback messages
|
||||
The application SHALL display command-style feedback for user actions.
|
||||
|
||||
#### Scenario: Success message format
|
||||
- **WHEN** user completes an action successfully (delete, create, export)
|
||||
- **THEN** displays message in format: `> SUCCESS: <action> completed [<count> items]` in green text
|
||||
|
||||
#### Scenario: Error message format
|
||||
- **WHEN** an error occurs
|
||||
- **THEN** displays message in format: `> ERROR: <description> [code: <error_code>]` in red text
|
||||
|
||||
#### Scenario: Info message format
|
||||
- **WHEN** displaying informational message
|
||||
- **THEN** displays message in format: `> INFO: <message>` in cyan text
|
||||
|
||||
#### Scenario: Message container styling
|
||||
- **WHEN** displaying any feedback message
|
||||
- **THEN** container has monospace font, left-aligned text, subtle border, and auto-dismiss after 4 seconds
|
||||
|
||||
#### Scenario: Message animation
|
||||
- **WHEN** feedback message appears
|
||||
- **THEN** slides in from top with fade-in animation (0.3s ease-out)
|
||||
|
||||
### Requirement: Minimalist icon treatment
|
||||
The application SHALL use simplified, outline-style icons or ASCII alternatives.
|
||||
|
||||
#### Scenario: Lucide icon styling
|
||||
- **WHEN** rendering Lucide React icons
|
||||
- **THEN** icons use stroke-width: 1.5, size: 20px, and color matches text color (#00ff41)
|
||||
|
||||
#### Scenario: Icon hover effect
|
||||
- **WHEN** user hovers over icon button
|
||||
- **THEN** icon glows with filter: drop-shadow(0 0 4px currentColor)
|
||||
|
||||
#### Scenario: ASCII alternative icons (optional)
|
||||
- **WHEN** rendering simple actions (delete, expand, collapse)
|
||||
- **THEN** optionally uses ASCII characters: [×] for delete, [▼] for expand, [▲] for collapse, [>] for actions
|
||||
|
||||
#### Scenario: Icon color consistency
|
||||
- **WHEN** icon is in active/selected state
|
||||
- **THEN** icon color is #00ff41
|
||||
- **WHEN** icon is in disabled state
|
||||
- **THEN** icon color is #003311
|
||||
|
||||
### Requirement: High contrast for readability
|
||||
The application SHALL maintain WCAG AA contrast ratios for all text.
|
||||
|
||||
#### Scenario: Primary text contrast
|
||||
- **WHEN** displaying primary text (#00ff41) on dark background (#0a0e0a)
|
||||
- **THEN** contrast ratio is at least 4.5:1 (WCAG AA standard)
|
||||
|
||||
#### Scenario: Secondary text contrast
|
||||
- **WHEN** displaying secondary text (#00cc33) on dark background
|
||||
- **THEN** contrast ratio is at least 4.5:1
|
||||
|
||||
#### Scenario: Button text contrast
|
||||
- **WHEN** button is in any state (default, hover, active, disabled)
|
||||
- **THEN** text maintains minimum 4.5:1 contrast with background
|
||||
|
||||
#### Scenario: Chart element visibility
|
||||
- **WHEN** rendering lines and markers on chart
|
||||
- **THEN** all annotations have sufficient contrast against black background and candles
|
||||
|
||||
### Requirement: Simplified button styling
|
||||
The application SHALL use minimal, flat button design with terminal aesthetic.
|
||||
|
||||
#### Scenario: Default button appearance
|
||||
- **WHEN** rendering buttons in default state
|
||||
- **THEN** buttons have transparent background, 1px solid #00ff41 border, #00ff41 text, 4px padding, and no rounded corners (border-radius: 0)
|
||||
|
||||
#### Scenario: Hover button appearance
|
||||
- **WHEN** user hovers over button
|
||||
- **THEN** background changes to #00ff4110 (10% opacity), border glows, and text remains #00ff41
|
||||
|
||||
#### Scenario: Active button appearance
|
||||
- **WHEN** button is active/pressed
|
||||
- **THEN** background is #00ff4120 (20% opacity), border is 2px solid, and includes pulsing glow animation
|
||||
|
||||
#### Scenario: Disabled button appearance
|
||||
- **WHEN** button is disabled
|
||||
- **THEN** background is transparent, border is #003311, text is #003311, and no hover effects
|
||||
|
||||
#### Scenario: Destructive button styling
|
||||
- **WHEN** button represents destructive action (delete)
|
||||
- **THEN** uses #ff0040 (neon red) for border and text instead of green, with matching red glow on hover
|
||||
|
||||
### Requirement: Cyber-retro aesthetic elements
|
||||
The application SHALL include subtle design elements that enhance the hacker theme.
|
||||
|
||||
#### Scenario: Scanline effect (optional)
|
||||
- **WHEN** rendering main container
|
||||
- **THEN** applies subtle CSS repeating-linear-gradient overlay simulating CRT scanlines with 2px spacing at 5% opacity
|
||||
|
||||
#### Scenario: Noise texture (optional)
|
||||
- **WHEN** rendering background
|
||||
- **THEN** applies subtle noise texture using CSS filter or background-image at 3% opacity for grain effect
|
||||
|
||||
#### Scenario: Typewriter animation for messages
|
||||
- **WHEN** displaying new feedback message
|
||||
- **THEN** text appears with brief letter-by-letter typing animation (0.02s per character, max 0.5s total)
|
||||
|
||||
#### Scenario: Flicker animation on focus
|
||||
- **WHEN** user focuses on input field
|
||||
- **THEN** border shows brief flicker animation (0.1s) simulating electrical surge
|
||||
|
||||
#### Scenario: ASCII art branding (optional)
|
||||
- **WHEN** application loads or in header
|
||||
- **THEN** displays application name in ASCII art style using monospace font
|
||||
|
||||
### Requirement: Responsive dark theme
|
||||
The application SHALL maintain theme consistency across all components and states.
|
||||
|
||||
#### Scenario: Modal dialogs
|
||||
- **WHEN** displaying confirmation dialogs
|
||||
- **THEN** modal has dark background (#0d110d), neon green border, monospace font, and terminal-style title (e.g., "> CONFIRM ACTION")
|
||||
|
||||
#### Scenario: Input fields
|
||||
- **WHEN** rendering text inputs or search fields
|
||||
- **THEN** inputs have transparent background, #00ff41 border, #00ff41 text, #00ff4120 placeholder, and cursor color #00ff41
|
||||
|
||||
#### Scenario: Dropdown menus
|
||||
- **WHEN** displaying filter or select dropdowns
|
||||
- **THEN** dropdown has dark background (#0d110d), neon green border, #00ff41 option text, hover state with #00ff4120 background
|
||||
|
||||
#### Scenario: Scrollbars
|
||||
- **WHEN** content requires scrolling
|
||||
- **THEN** custom scrollbar with #003311 track, #00ff41 thumb, and 8px width
|
||||
|
||||
#### Scenario: Tooltips
|
||||
- **WHEN** displaying tooltips on hover
|
||||
- **THEN** tooltip has dark background (#0d110d), 1px #00ff41 border, monospace font, and terminal-style text
|
||||
|
||||
### Requirement: Animation performance
|
||||
The application SHALL use performant CSS animations that don't impact chart rendering.
|
||||
|
||||
#### Scenario: GPU acceleration
|
||||
- **WHEN** applying animations (glow, pulse, fade)
|
||||
- **THEN** uses CSS transform and opacity properties with will-change hints for GPU acceleration
|
||||
|
||||
#### Scenario: Animation duration
|
||||
- **WHEN** defining animations
|
||||
- **THEN** all animations complete within 0.5s, with most hover effects at 0.2s
|
||||
|
||||
#### Scenario: Reduced motion support
|
||||
- **WHEN** user has prefers-reduced-motion enabled
|
||||
- **THEN** system disables glow animations, typewriter effects, and pulsing, keeping only instant transitions
|
||||
|
||||
#### Scenario: Chart performance
|
||||
- **WHEN** theme is active and chart is rendering
|
||||
- **THEN** SVG overlay and chart rendering maintains 60fps without jank from theme effects
|
||||
|
||||
### Requirement: Tailwind CSS configuration
|
||||
The Tailwind config SHALL be updated to support hacker theme tokens.
|
||||
|
||||
#### Scenario: Custom colors in tailwind.config
|
||||
- **WHEN** tailwind.config.js is parsed
|
||||
- **THEN** defines custom colors: matrix: '#00ff41', matrixDim: '#00cc33', matrixDark: '#003311', neonRed: '#ff0040', neonCyan: '#00d4ff', neonYellow: '#ffff00', terminal: '#0a0e0a', terminalLight: '#0d110d'
|
||||
|
||||
#### Scenario: Custom font families
|
||||
- **WHEN** tailwind.config.js is parsed
|
||||
- **THEN** defines fontFamily: { mono: ['JetBrains Mono', 'Fira Code', 'Courier New', 'monospace'] }
|
||||
|
||||
#### Scenario: Custom animations
|
||||
- **WHEN** tailwind.config.js is parsed
|
||||
- **THEN** defines keyframes for 'glow-pulse', 'flicker', 'scanline-move' and corresponding animation utilities
|
||||
|
||||
#### Scenario: Custom shadows
|
||||
- **WHEN** tailwind.config.js is parsed
|
||||
- **THEN** defines boxShadow: { 'glow-sm': '0 0 8px #00ff41', 'glow': '0 0 15px #00ff41, 0 0 30px #00ff4180', 'glow-lg': '0 0 20px #00ff41, 0 0 40px #00ff4180' }
|
||||
|
||||
### Requirement: globals.css updates
|
||||
The globals.css file SHALL define base styles and CSS variables for the hacker theme.
|
||||
|
||||
#### Scenario: CSS custom properties
|
||||
- **WHEN** globals.css is loaded
|
||||
- **THEN** defines CSS variables: --color-matrix, --color-matrix-dim, --color-matrix-dark, --color-neon-red, --color-neon-cyan, --color-terminal, --font-mono
|
||||
|
||||
#### Scenario: Base body styles
|
||||
- **WHEN** page loads
|
||||
- **THEN** body element has background: --color-terminal, color: --color-matrix, font-family: --font-mono, and -webkit-font-smoothing: antialiased
|
||||
|
||||
#### Scenario: Selection styling
|
||||
- **WHEN** user selects text
|
||||
- **THEN** selection background is #00ff4140 (40% opacity) and text is #00ff41
|
||||
|
||||
#### Scenario: Scrollbar styling
|
||||
- **WHEN** globals.css is loaded
|
||||
- **THEN** defines custom scrollbar styles using ::-webkit-scrollbar pseudo-elements with theme colors
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Select label by clicking marker
|
||||
The system SHALL allow users to select label annotations by clicking on their marker symbols on the chart.
|
||||
|
||||
#### Scenario: Click on break_up marker
|
||||
- **WHEN** user clicks on a green arrow (break_up) marker on the chart
|
||||
- **THEN** system sets selectedLabelId state to the annotation ID, highlights the marker with increased size and glow effect, and scrolls to the label in the sidebar list
|
||||
|
||||
#### Scenario: Click on break_down marker
|
||||
- **WHEN** user clicks on a red arrow (break_down) marker on the chart
|
||||
- **THEN** system sets selectedLabelId state to the annotation ID, highlights the marker with increased size and glow effect, and scrolls to the label in the sidebar list
|
||||
|
||||
#### Scenario: Click already selected marker
|
||||
- **WHEN** user clicks on a marker that is already selected
|
||||
- **THEN** system deselects the marker by setting selectedLabelId to null and removes highlight effects
|
||||
|
||||
#### Scenario: Click different marker while one is selected
|
||||
- **WHEN** user clicks on a different marker while another marker is already selected
|
||||
- **THEN** system deselects the previous marker, selects the new marker, and updates the highlight to the new marker
|
||||
|
||||
#### Scenario: Tool mode during label selection
|
||||
- **WHEN** user selects a label marker
|
||||
- **THEN** selection works regardless of active tool (works in all modes: none, line, delete, break_up, break_down)
|
||||
|
||||
### Requirement: Delete selected label with keyboard
|
||||
The system SHALL allow users to delete the currently selected label annotation using keyboard shortcuts.
|
||||
|
||||
#### Scenario: Delete selected label with Delete key
|
||||
- **WHEN** a label is selected (selectedLabelId is set) and user presses Delete key
|
||||
- **THEN** system sends DELETE request to `/api/annotations/{id}`, removes marker from display, clears selection state, and triggers annotation refresh
|
||||
|
||||
#### Scenario: Delete selected label with Backspace key
|
||||
- **WHEN** a label is selected and user presses Backspace key
|
||||
- **THEN** system sends DELETE request to `/api/annotations/{id}`, removes marker from display, clears selection state, and triggers annotation refresh
|
||||
|
||||
#### Scenario: No label selected when pressing delete
|
||||
- **WHEN** no label is selected (selectedLabelId is null) and user presses Delete or Backspace key
|
||||
- **THEN** system takes no action on labels (may still delete selected line if line is selected)
|
||||
|
||||
### Requirement: Label list in sidebar
|
||||
The Toolbox SHALL display a collapsible section showing all label annotations with interactive controls.
|
||||
|
||||
#### Scenario: Display label list section
|
||||
- **WHEN** Toolbox renders
|
||||
- **THEN** system displays "Label Annotations" section below the annotation tools with collapse/expand toggle button
|
||||
|
||||
#### Scenario: Label list expanded
|
||||
- **WHEN** "Label Annotations" section is expanded
|
||||
- **THEN** system displays scrollable list of all label annotations sorted by timestamp (newest first), with each entry showing timestamp, label type badge, and delete button
|
||||
|
||||
#### Scenario: Label list collapsed
|
||||
- **WHEN** user clicks collapse button on "Label Annotations" section
|
||||
- **THEN** system hides the label list but shows count summary "Labels: X break_up, Y break_down"
|
||||
|
||||
#### Scenario: Empty label list
|
||||
- **WHEN** no label annotations exist in database
|
||||
- **THEN** section displays message "No labels yet. Click Break Up or Break Down tools to add labels."
|
||||
|
||||
#### Scenario: Label entry format
|
||||
- **WHEN** displaying a label in the list
|
||||
- **THEN** each entry shows formatted timestamp (e.g., "Feb 12, 14:30"), colored badge ("BREAK UP" in green or "BREAK DOWN" in red), and trash icon delete button
|
||||
|
||||
### Requirement: Click label in list to select on chart
|
||||
The system SHALL allow users to click a label in the sidebar list to select and highlight it on the chart.
|
||||
|
||||
#### Scenario: Click label entry in sidebar
|
||||
- **WHEN** user clicks on a label entry in the sidebar list
|
||||
- **THEN** system sets selectedLabelId to that annotation ID, highlights the corresponding marker on chart, and centers the chart view on that timestamp
|
||||
|
||||
#### Scenario: Click already selected label in list
|
||||
- **WHEN** user clicks on a label entry that is already selected
|
||||
- **THEN** system deselects the label by setting selectedLabelId to null and removes highlights
|
||||
|
||||
#### Scenario: Selected label visual in list
|
||||
- **WHEN** a label is selected
|
||||
- **THEN** the corresponding entry in the sidebar list has a highlighted background and border
|
||||
|
||||
### Requirement: Delete label from sidebar
|
||||
The system SHALL provide delete buttons for each label in the sidebar list.
|
||||
|
||||
#### Scenario: Click delete button for label
|
||||
- **WHEN** user clicks trash icon button next to a label in the sidebar
|
||||
- **THEN** system sends DELETE request to `/api/annotations/{id}`, removes label from list and chart, clears selection if that label was selected, and triggers annotation refresh
|
||||
|
||||
#### Scenario: Delete button styling
|
||||
- **WHEN** displaying delete buttons in label list
|
||||
- **THEN** buttons use red/destructive color on hover and show tooltip "Delete this label"
|
||||
|
||||
### Requirement: Search and filter labels
|
||||
The Toolbox label section SHALL provide search and filter controls.
|
||||
|
||||
#### Scenario: Filter by label type
|
||||
- **WHEN** user selects filter dropdown and chooses "Break Up"
|
||||
- **THEN** system displays only break_up annotations in the list
|
||||
|
||||
#### Scenario: Filter by label type - Break Down
|
||||
- **WHEN** user selects filter dropdown and chooses "Break Down"
|
||||
- **THEN** system displays only break_down annotations in the list
|
||||
|
||||
#### Scenario: Show all labels
|
||||
- **WHEN** user selects filter dropdown and chooses "All"
|
||||
- **THEN** system displays all label annotations regardless of type
|
||||
|
||||
#### Scenario: Search by timestamp
|
||||
- **WHEN** user types text into search input field
|
||||
- **THEN** system filters list to show only labels whose formatted timestamp contains the search text (case-insensitive)
|
||||
|
||||
#### Scenario: Combined search and filter
|
||||
- **WHEN** user has both search text and type filter active
|
||||
- **THEN** system shows labels that match both criteria (AND logic)
|
||||
|
||||
### Requirement: Label count display
|
||||
The Toolbox SHALL display counts of each label type.
|
||||
|
||||
#### Scenario: Display label counts
|
||||
- **WHEN** Toolbox renders and labels exist
|
||||
- **THEN** system displays count summary "Break Up: X | Break Down: Y" at top of label section
|
||||
|
||||
#### Scenario: Zero labels
|
||||
- **WHEN** no labels exist in database
|
||||
- **THEN** count summary displays "Break Up: 0 | Break Down: 0"
|
||||
|
||||
#### Scenario: Count updates after delete
|
||||
- **WHEN** user deletes a label
|
||||
- **THEN** count summary updates immediately to reflect the new totals
|
||||
|
||||
### Requirement: Delete all labels with confirmation
|
||||
The system SHALL provide a "Delete All Labels" button with confirmation dialog.
|
||||
|
||||
#### Scenario: Click Delete All Labels button
|
||||
- **WHEN** user clicks "Delete All Labels" button in Toolbox
|
||||
- **THEN** system displays confirmation dialog with message "Delete all label annotations (Break Up and Break Down)? This cannot be undone." and Cancel/Confirm buttons
|
||||
|
||||
#### Scenario: User confirms delete all labels
|
||||
- **WHEN** confirmation dialog is open and user clicks Confirm button
|
||||
- **THEN** system sends DELETE request to `/api/annotations?type=break_up,break_down`, removes all label markers from chart, clears label list, clears selection state, triggers annotation refresh, and closes dialog
|
||||
|
||||
#### Scenario: User cancels delete all labels
|
||||
- **WHEN** confirmation dialog is open and user clicks Cancel button
|
||||
- **THEN** system closes dialog without making any API calls or removing any labels
|
||||
|
||||
#### Scenario: Delete All Labels button visibility
|
||||
- **WHEN** one or more label annotations exist
|
||||
- **THEN** "Delete All Labels" button is enabled with destructive styling
|
||||
- **WHEN** no label annotations exist
|
||||
- **THEN** "Delete All Labels" button is disabled with reduced opacity
|
||||
|
||||
### Requirement: API support for label operations
|
||||
The API SHALL support filtering and bulk deletion of label annotations.
|
||||
|
||||
#### Scenario: Delete all break_up labels
|
||||
- **WHEN** DELETE request sent to `/api/annotations?type=break_up`
|
||||
- **THEN** system deletes all annotations where label_type equals 'break_up' and returns `{ success: true, deleted: <count> }`
|
||||
|
||||
#### Scenario: Delete all break_down labels
|
||||
- **WHEN** DELETE request sent to `/api/annotations?type=break_down`
|
||||
- **THEN** system deletes all annotations where label_type equals 'break_down' and returns `{ success: true, deleted: <count> }`
|
||||
|
||||
#### Scenario: Delete multiple types at once
|
||||
- **WHEN** DELETE request sent to `/api/annotations?type=break_up,break_down`
|
||||
- **THEN** system deletes all annotations where label_type is either 'break_up' or 'break_down' and returns `{ success: true, deleted: <count> }`
|
||||
|
||||
#### Scenario: Get labels with type filter
|
||||
- **WHEN** GET request sent to `/api/annotations?type=break_up`
|
||||
- **THEN** system returns only annotations where label_type equals 'break_up'
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
# 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`
|
||||
Loading…
Add table
Add a link
Reference in a new issue