diff --git a/openspec/changes/enhance-annotation-features-and-deployment/specs/docker-deployment/spec.md b/openspec/changes/enhance-annotation-features-and-deployment/specs/docker-deployment/spec.md new file mode 100644 index 0000000..cef3871 --- /dev/null +++ b/openspec/changes/enhance-annotation-features-and-deployment/specs/docker-deployment/spec.md @@ -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: }` + +#### 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 diff --git a/openspec/changes/enhance-annotation-features-and-deployment/specs/hacker-theme/spec.md b/openspec/changes/enhance-annotation-features-and-deployment/specs/hacker-theme/spec.md new file mode 100644 index 0000000..0cca719 --- /dev/null +++ b/openspec/changes/enhance-annotation-features-and-deployment/specs/hacker-theme/spec.md @@ -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: completed [ items]` in green text + +#### Scenario: Error message format +- **WHEN** an error occurs +- **THEN** displays message in format: `> ERROR: [code: ]` in red text + +#### Scenario: Info message format +- **WHEN** displaying informational message +- **THEN** displays message in format: `> INFO: ` 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 diff --git a/openspec/changes/enhance-annotation-features-and-deployment/specs/label-management/spec.md b/openspec/changes/enhance-annotation-features-and-deployment/specs/label-management/spec.md new file mode 100644 index 0000000..a246b5b --- /dev/null +++ b/openspec/changes/enhance-annotation-features-and-deployment/specs/label-management/spec.md @@ -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: }` + +#### 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: }` + +#### 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: }` + +#### 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' diff --git a/openspec/changes/enhance-annotation-features-and-deployment/specs/line-deletion/spec.md b/openspec/changes/enhance-annotation-features-and-deployment/specs/line-deletion/spec.md new file mode 100644 index 0000000..d8bfaab --- /dev/null +++ b/openspec/changes/enhance-annotation-features-and-deployment/specs/line-deletion/spec.md @@ -0,0 +1,69 @@ +## ADDED Requirements + +### Requirement: Delete selected line with keyboard +The system SHALL allow users to delete the currently selected line annotation using keyboard shortcuts (Delete key or Backspace key). + +#### Scenario: Delete selected line with Delete key +- **WHEN** a line is selected (selectedLineId is set) and user presses Delete key +- **THEN** system sends DELETE request to `/api/annotations/{id}`, removes line from display, clears selection state, and triggers annotation refresh + +#### Scenario: Delete selected line with Backspace key +- **WHEN** a line is selected and user presses Backspace key +- **THEN** system sends DELETE request to `/api/annotations/{id}`, removes line from display, clears selection state, and triggers annotation refresh + +#### Scenario: No line selected when pressing delete +- **WHEN** no line is selected (selectedLineId is null) and user presses Delete or Backspace key +- **THEN** system takes no action and does not make any API calls + +#### Scenario: Delete key during line drawing +- **WHEN** user is actively drawing a line (drawingLine state is not null) and presses Delete key +- **THEN** system cancels the line drawing operation but does NOT delete any existing lines + +### Requirement: Delete all lines with confirmation +The system SHALL provide a UI button in the Toolbox to delete all line annotations at once with user confirmation. + +#### Scenario: User clicks Delete All Lines button +- **WHEN** user clicks "Delete All Lines" button in Toolbox +- **THEN** system displays a confirmation dialog with message "Delete all line annotations? This cannot be undone." and Cancel/Confirm buttons + +#### Scenario: User confirms delete all lines +- **WHEN** confirmation dialog is open and user clicks Confirm button +- **THEN** system sends DELETE request to `/api/annotations?type=line`, removes all lines from display, clears selection state, triggers annotation refresh, and closes dialog + +#### Scenario: User cancels delete all lines +- **WHEN** confirmation dialog is open and user clicks Cancel button +- **THEN** system closes dialog without making any API calls or removing any lines + +#### Scenario: No lines exist when clicking delete all +- **WHEN** user clicks "Delete All Lines" button and no line annotations exist in database +- **THEN** system still shows confirmation dialog (user confirms/cancels normally) + +### Requirement: API support for bulk line deletion +The API SHALL support deleting multiple annotations by type via query parameters. + +#### Scenario: Delete all lines via API +- **WHEN** DELETE request sent to `/api/annotations?type=line` +- **THEN** system deletes all annotations where label_type equals 'line' and returns `{ success: true, deleted: }` + +#### Scenario: Delete all annotations via API +- **WHEN** DELETE request sent to `/api/annotations?all=true` +- **THEN** system deletes all annotations regardless of type and returns `{ success: true, deleted: }` + +#### Scenario: Invalid type parameter +- **WHEN** DELETE request sent to `/api/annotations?type=invalid_type` +- **THEN** system returns 400 error with message "Invalid annotation type" + +### Requirement: Delete button in Toolbox +The Toolbox SHALL display a "Delete All Lines" button when line annotations exist. + +#### Scenario: Lines exist in database +- **WHEN** one or more line annotations exist in the database +- **THEN** Toolbox displays "Delete All Lines" button in destructive styling (red theme) + +#### Scenario: No lines exist +- **WHEN** no line annotations exist in database +- **THEN** Toolbox displays "Delete All Lines" button in disabled state with reduced opacity + +#### Scenario: Button position in Toolbox +- **WHEN** Toolbox renders +- **THEN** "Delete All Lines" button appears below the color picker section and above the "Delete" tool button