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
70
openspec/specs/annotation-tools/spec.md
Normal file
70
openspec/specs/annotation-tools/spec.md
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Active tool mode
|
||||
The system SHALL maintain an "active tool" state that determines what happens when the user clicks on the chart. Available tool modes are: "select" (default, no action on click), "break_up" (label Break Up), "break_down" (label Break Down), "line" (draw trend line), and "delete" (remove annotation). Only one tool SHALL be active at a time.
|
||||
|
||||
#### Scenario: Tool activation
|
||||
- **WHEN** user clicks a tool button in the sidebar
|
||||
- **THEN** that tool becomes the active tool and the button appears visually selected
|
||||
|
||||
#### Scenario: Tool deactivation
|
||||
- **WHEN** user clicks the already-active tool button
|
||||
- **THEN** the tool deactivates and the mode returns to "select"
|
||||
|
||||
### Requirement: Break Up labeling
|
||||
When the "break_up" tool is active and the user clicks on the chart, the system SHALL identify the nearest candle to the click coordinates using `chart.timeScale().coordinateToTime()`. The system SHALL save an annotation with `label_type: "break_up"` and the candle's timestamp to the database. A green upward arrow marker SHALL appear on the chart immediately.
|
||||
|
||||
#### Scenario: Place Break Up label
|
||||
- **WHEN** "break_up" tool is active and user clicks on a candle
|
||||
- **THEN** system saves a "break_up" annotation for that candle's timestamp and displays a green arrow marker above the bar
|
||||
|
||||
#### Scenario: Click between candles
|
||||
- **WHEN** "break_up" tool is active and user clicks between two candles
|
||||
- **THEN** system snaps to the nearest candle timestamp and places the annotation there
|
||||
|
||||
### Requirement: Break Down labeling
|
||||
When the "break_down" tool is active and the user clicks on the chart, the system SHALL behave identically to Break Up labeling but save `label_type: "break_down"` and display a red downward arrow below the bar.
|
||||
|
||||
#### Scenario: Place Break Down label
|
||||
- **WHEN** "break_down" tool is active and user clicks on a candle
|
||||
- **THEN** system saves a "break_down" annotation for that candle's timestamp and displays a red arrow marker below the bar
|
||||
|
||||
### Requirement: Two-click line drawing
|
||||
When the "line" tool is active, the system SHALL implement a two-click drawing interaction. The first click sets the start point (time, price). The second click sets the end point (time, price). After the second click, the system SHALL save an annotation with `label_type: "line"` and `geometry` containing JSON: `{"startTime": <unix>, "startPrice": <float>, "endTime": <unix>, "endPrice": <float>}`. The line SHALL render immediately on the SVG overlay.
|
||||
|
||||
#### Scenario: Draw a trend line
|
||||
- **WHEN** "line" tool is active and user clicks two points on the chart
|
||||
- **THEN** system saves a line annotation with start/end coordinates and renders the line on the overlay
|
||||
|
||||
#### Scenario: Visual feedback during line drawing
|
||||
- **WHEN** "line" tool is active and user has clicked the first point but not the second
|
||||
- **THEN** system displays a preview line from the first point to the current cursor position
|
||||
|
||||
#### Scenario: Cancel line drawing
|
||||
- **WHEN** user presses Escape during a two-click line drawing (after first click)
|
||||
- **THEN** system cancels the line drawing and clears the preview without saving
|
||||
|
||||
### Requirement: Delete annotation
|
||||
When the "delete" tool is active and the user clicks on or near an existing annotation (marker or line), the system SHALL remove that annotation from the database and update the chart display immediately.
|
||||
|
||||
#### Scenario: Delete a marker annotation
|
||||
- **WHEN** "delete" tool is active and user clicks on a candle that has a marker annotation
|
||||
- **THEN** system removes the annotation from the database and the marker disappears from the chart
|
||||
|
||||
#### Scenario: Delete a line annotation
|
||||
- **WHEN** "delete" tool is active and user clicks near an existing line on the overlay
|
||||
- **THEN** system removes the line annotation from the database and the line disappears from the overlay
|
||||
|
||||
### Requirement: Coordinate mapping
|
||||
The system SHALL convert mouse click pixel coordinates to chart data coordinates (time and price) using the lightweight-charts API: `chart.timeScale().coordinateToTime(x)` for time and `series.coordinateToPrice(y)` for price. For point annotations, the time SHALL be snapped to the nearest candle timestamp.
|
||||
|
||||
#### Scenario: Pixel to data coordinate conversion
|
||||
- **WHEN** user clicks at pixel position (x, y) on the chart
|
||||
- **THEN** system correctly converts to the corresponding time and price values using the chart API
|
||||
|
||||
### Requirement: Annotations database table
|
||||
The system SHALL store annotations in an `annotations` table with columns: `id` (integer primary key, auto-increment), `timestamp` (integer, Unix timestamp referencing a candle time), `label_type` (text: "break_up", "break_down", or "line"), `geometry` (text, nullable, JSON string for line coordinates), `created_at` (integer, Unix timestamp of creation).
|
||||
|
||||
#### Scenario: Schema structure
|
||||
- **WHEN** the database is initialized
|
||||
- **THEN** the `annotations` table exists with all required columns
|
||||
75
openspec/specs/backend-api/spec.md
Normal file
75
openspec/specs/backend-api/spec.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Upload endpoint
|
||||
The system SHALL provide a `POST /api/upload` endpoint that accepts a CSV file via multipart form data. The endpoint SHALL parse the CSV using papaparse, validate the format, and insert all candle records into the `candles` table within a single database transaction. On success, the endpoint SHALL return a JSON response with the count of inserted records. On failure, it SHALL return an appropriate error status and message.
|
||||
|
||||
#### Scenario: Successful upload
|
||||
- **WHEN** a valid CSV file is sent to POST /api/upload
|
||||
- **THEN** endpoint returns `{ "success": true, "count": <number> }` with HTTP 200
|
||||
|
||||
#### Scenario: Invalid CSV upload
|
||||
- **WHEN** a CSV with missing or invalid headers is sent to POST /api/upload
|
||||
- **THEN** endpoint returns `{ "error": "<description>" }` with HTTP 400
|
||||
|
||||
#### Scenario: No file provided
|
||||
- **WHEN** POST /api/upload is called without a file
|
||||
- **THEN** endpoint returns `{ "error": "No file provided" }` with HTTP 400
|
||||
|
||||
### Requirement: Get annotations endpoint
|
||||
The system SHALL provide a `GET /api/annotations` endpoint that returns all annotations from the database as a JSON array. Each annotation object SHALL include: `id`, `timestamp`, `label_type`, `geometry` (parsed from JSON string or null), and `created_at`.
|
||||
|
||||
#### Scenario: Fetch all annotations
|
||||
- **WHEN** GET /api/annotations is called
|
||||
- **THEN** endpoint returns a JSON array of all annotation objects with HTTP 200
|
||||
|
||||
#### Scenario: No annotations exist
|
||||
- **WHEN** GET /api/annotations is called and no annotations are in the database
|
||||
- **THEN** endpoint returns an empty JSON array `[]` with HTTP 200
|
||||
|
||||
### Requirement: Create annotation endpoint
|
||||
The system SHALL provide a `POST /api/annotations` endpoint that accepts a JSON body with fields: `timestamp` (required, integer), `label_type` (required, string), and `geometry` (optional, object). The endpoint SHALL validate the input, serialize geometry to JSON string if present, and insert the record into the `annotations` table. On success, it SHALL return the created annotation object with its assigned `id`.
|
||||
|
||||
#### Scenario: Create a marker annotation
|
||||
- **WHEN** POST /api/annotations is called with `{ "timestamp": 1700000000, "label_type": "break_up" }`
|
||||
- **THEN** endpoint saves the annotation and returns the created object with `id` and HTTP 201
|
||||
|
||||
#### Scenario: Create a line annotation
|
||||
- **WHEN** POST /api/annotations is called with `{ "timestamp": 1700000000, "label_type": "line", "geometry": { "startTime": 1700000000, "startPrice": 1.05, "endTime": 1700100000, "endPrice": 1.06 } }`
|
||||
- **THEN** endpoint saves the annotation with serialized geometry JSON and returns the created object with HTTP 201
|
||||
|
||||
#### Scenario: Invalid annotation data
|
||||
- **WHEN** POST /api/annotations is called with missing required fields
|
||||
- **THEN** endpoint returns `{ "error": "<description>" }` with HTTP 400
|
||||
|
||||
### Requirement: Delete annotation endpoint
|
||||
The system SHALL provide a `DELETE /api/annotations/[id]` endpoint that removes an annotation by its ID. On success, it SHALL return HTTP 200. If the annotation does not exist, it SHALL return HTTP 404.
|
||||
|
||||
#### Scenario: Delete existing annotation
|
||||
- **WHEN** DELETE /api/annotations/5 is called and annotation with id 5 exists
|
||||
- **THEN** endpoint removes the annotation and returns HTTP 200
|
||||
|
||||
#### Scenario: Delete non-existent annotation
|
||||
- **WHEN** DELETE /api/annotations/999 is called and no annotation with that id exists
|
||||
- **THEN** endpoint returns HTTP 404
|
||||
|
||||
### Requirement: Export annotations endpoint
|
||||
The system SHALL provide a `GET /api/export` endpoint that returns all annotations as a downloadable CSV file. The CSV SHALL have columns: `timestamp`, `label_type`, `price` (extracted from geometry for lines, or the candle's close price for markers). The response SHALL set `Content-Type: text/csv` and `Content-Disposition: attachment; filename="annotations.csv"` headers.
|
||||
|
||||
#### Scenario: Export as CSV
|
||||
- **WHEN** GET /api/export is called and annotations exist
|
||||
- **THEN** endpoint returns a CSV file download with all annotations
|
||||
|
||||
#### Scenario: Export with no data
|
||||
- **WHEN** GET /api/export is called and no annotations exist
|
||||
- **THEN** endpoint returns a CSV file with only the header row
|
||||
|
||||
### Requirement: Get candles endpoint
|
||||
The system SHALL provide a `GET /api/candles` endpoint that returns all candle records from the database as a JSON array, ordered by time ascending. Each object SHALL include: `time`, `open`, `high`, `low`, `close`.
|
||||
|
||||
#### Scenario: Fetch all candles
|
||||
- **WHEN** GET /api/candles is called
|
||||
- **THEN** endpoint returns a JSON array of candle objects ordered by time ascending with HTTP 200
|
||||
|
||||
#### Scenario: No candles exist
|
||||
- **WHEN** GET /api/candles is called and no candle data is in the database
|
||||
- **THEN** endpoint returns an empty JSON array `[]` with HTTP 200
|
||||
52
openspec/specs/chart-canvas/spec.md
Normal file
52
openspec/specs/chart-canvas/spec.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Candlestick chart rendering
|
||||
The system SHALL render candle data as a candlestick chart using the `lightweight-charts` library (v4). The chart MUST display OHLC data with standard candlestick visuals (green for bullish, red for bearish). The chart SHALL be a client-side React component.
|
||||
|
||||
#### Scenario: Chart renders candle data
|
||||
- **WHEN** candle data exists in the database and the page loads
|
||||
- **THEN** the chart renders all candles as a candlestick series with correct open/high/low/close values
|
||||
|
||||
#### Scenario: Empty state
|
||||
- **WHEN** no candle data exists in the database
|
||||
- **THEN** the chart area displays an empty chart with a prompt to upload CSV data
|
||||
|
||||
### Requirement: Chart interactivity
|
||||
The chart SHALL support zooming (mouse wheel), panning (click and drag on time axis), and crosshair display (showing price/time on hover). These are built-in lightweight-charts behaviors that MUST be enabled.
|
||||
|
||||
#### Scenario: Zoom and pan
|
||||
- **WHEN** user scrolls the mouse wheel over the chart
|
||||
- **THEN** the chart zooms in or out on the time axis
|
||||
|
||||
#### Scenario: Crosshair display
|
||||
- **WHEN** user hovers the mouse over the chart
|
||||
- **THEN** a crosshair displays with the current price and time at the cursor position
|
||||
|
||||
### Requirement: Responsive chart layout
|
||||
The chart MUST fill the available width of the main content area (excluding the sidebar). The chart height SHALL be responsive, using the full viewport height minus header/toolbar areas. The chart MUST resize when the browser window is resized.
|
||||
|
||||
#### Scenario: Window resize
|
||||
- **WHEN** user resizes the browser window
|
||||
- **THEN** the chart resizes to fill the available space without requiring a page reload
|
||||
|
||||
### Requirement: Dark theme chart
|
||||
The chart SHALL use a dark color scheme consistent with the application's Slate-900 dark theme. Background MUST be dark, grid lines subtle, and text/crosshair in light colors.
|
||||
|
||||
#### Scenario: Dark theme applied
|
||||
- **WHEN** the chart renders
|
||||
- **THEN** the chart background, grid, text, and crosshair colors match the dark theme (dark background, light text)
|
||||
|
||||
### Requirement: Annotation markers on chart
|
||||
The chart SHALL display visual markers for existing annotations using the `series.setMarkers()` API. Break Up annotations MUST appear as green upward arrows above the bar. Break Down annotations MUST appear as red downward arrows below the bar. Markers MUST update when annotations are added or deleted.
|
||||
|
||||
#### Scenario: Break Up marker display
|
||||
- **WHEN** a Break Up annotation exists for a candle timestamp
|
||||
- **THEN** a green upward arrow marker appears above that candle on the chart
|
||||
|
||||
#### Scenario: Break Down marker display
|
||||
- **WHEN** a Break Down annotation exists for a candle timestamp
|
||||
- **THEN** a red downward arrow marker appears below that candle on the chart
|
||||
|
||||
#### Scenario: Marker updates on annotation change
|
||||
- **WHEN** user adds or deletes an annotation
|
||||
- **THEN** chart markers update immediately without requiring a page reload
|
||||
42
openspec/specs/data-ingestion/spec.md
Normal file
42
openspec/specs/data-ingestion/spec.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: CSV file upload
|
||||
The system SHALL provide a file upload component that accepts CSV files containing OHLC candle data. The CSV format MUST have columns: `time`, `open`, `high`, `low`, `close`. The `time` column SHALL accept both `YYYY-MM-DD` date strings and Unix timestamps (integer seconds).
|
||||
|
||||
#### Scenario: Valid CSV upload
|
||||
- **WHEN** user uploads a CSV file with valid headers (time, open, high, low, close) and valid data rows
|
||||
- **THEN** system parses all rows and stores them in the `candles` database table
|
||||
|
||||
#### Scenario: CSV with Unix timestamps
|
||||
- **WHEN** user uploads a CSV where the `time` column contains Unix timestamps (e.g., 1700000000)
|
||||
- **THEN** system stores the timestamps as integers in the database and renders candles correctly on the chart
|
||||
|
||||
#### Scenario: CSV with date strings
|
||||
- **WHEN** user uploads a CSV where the `time` column contains date strings (e.g., "2024-01-15")
|
||||
- **THEN** system converts dates to Unix timestamps and stores them in the database
|
||||
|
||||
#### Scenario: Invalid CSV format
|
||||
- **WHEN** user uploads a CSV missing required headers or containing malformed data
|
||||
- **THEN** system displays an error message describing the issue and does not store any partial data
|
||||
|
||||
#### Scenario: Duplicate upload
|
||||
- **WHEN** user uploads a CSV containing candle times that already exist in the database
|
||||
- **THEN** system replaces existing candle records with the new data (upsert behavior)
|
||||
|
||||
### Requirement: CSV parsing with papaparse
|
||||
The system SHALL use the `papaparse` library for CSV parsing. Parsing SHALL handle large files by using streaming mode for files exceeding 10,000 rows. Parsed records SHALL be inserted into SQLite within a single database transaction for atomicity.
|
||||
|
||||
#### Scenario: Large file parsing
|
||||
- **WHEN** user uploads a CSV with more than 10,000 rows
|
||||
- **THEN** system uses streaming parse and batch inserts within a transaction, completing without memory issues
|
||||
|
||||
#### Scenario: Transaction atomicity
|
||||
- **WHEN** a parse error occurs midway through a CSV file
|
||||
- **THEN** system rolls back the entire transaction and no partial data is stored
|
||||
|
||||
### Requirement: Candles database table
|
||||
The system SHALL store candle data in a `candles` table with columns: `id` (integer primary key, auto-increment), `time` (integer, Unix timestamp, unique), `open` (real), `high` (real), `low` (real), `close` (real). The `time` column MUST have a unique constraint.
|
||||
|
||||
#### Scenario: Schema structure
|
||||
- **WHEN** the database is initialized
|
||||
- **THEN** the `candles` table exists with all required columns and constraints
|
||||
203
openspec/specs/docker-deployment/spec.md
Normal file
203
openspec/specs/docker-deployment/spec.md
Normal file
|
|
@ -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
|
||||
274
openspec/specs/hacker-theme/spec.md
Normal file
274
openspec/specs/hacker-theme/spec.md
Normal file
|
|
@ -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
|
||||
166
openspec/specs/label-management/spec.md
Normal file
166
openspec/specs/label-management/spec.md
Normal file
|
|
@ -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'
|
||||
44
openspec/specs/ui-shell/spec.md
Normal file
44
openspec/specs/ui-shell/spec.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Dark mode layout
|
||||
The application SHALL use a dark theme based on Tailwind's Slate-900 color palette. The root layout MUST set a dark background. All UI components (sidebar, buttons, text) SHALL use colors consistent with the dark theme.
|
||||
|
||||
#### Scenario: Dark theme renders
|
||||
- **WHEN** the application loads
|
||||
- **THEN** the page background is Slate-900 (dark), text is light, and all UI elements follow the dark color scheme
|
||||
|
||||
### Requirement: Sidebar toolbox
|
||||
The application SHALL display a fixed sidebar on the left side of the viewport. The sidebar SHALL contain tool buttons: "Label: Break Up", "Label: Break Down", "Draw Line", "Delete". Each button SHALL use a lucide-react icon and a text label. The currently active tool button SHALL be visually highlighted.
|
||||
|
||||
#### Scenario: Sidebar renders with tools
|
||||
- **WHEN** the application loads
|
||||
- **THEN** the sidebar displays all four tool buttons with icons and labels
|
||||
|
||||
#### Scenario: Active tool highlight
|
||||
- **WHEN** user selects a tool
|
||||
- **THEN** that tool's button is visually highlighted (distinct background/border color) and other buttons return to default state
|
||||
|
||||
### Requirement: Main chart area
|
||||
The main content area SHALL occupy the full viewport width minus the sidebar width. The chart component MUST fill this area. The layout SHALL use CSS flexbox or grid to ensure the sidebar and chart area are side by side.
|
||||
|
||||
#### Scenario: Layout structure
|
||||
- **WHEN** the application loads with candle data
|
||||
- **THEN** the sidebar appears on the left and the chart fills the remaining space
|
||||
|
||||
### Requirement: File upload interface
|
||||
The application SHALL provide a CSV file upload interface accessible from the UI. This MAY be a button in the sidebar or a dropzone overlay. The upload component SHALL trigger the POST /api/upload endpoint and refresh the chart data on success.
|
||||
|
||||
#### Scenario: Upload via UI
|
||||
- **WHEN** user selects a CSV file through the upload interface
|
||||
- **THEN** the file is sent to the upload API and the chart refreshes with the new data on success
|
||||
|
||||
#### Scenario: Upload error display
|
||||
- **WHEN** the upload API returns an error
|
||||
- **THEN** the UI displays the error message to the user
|
||||
|
||||
### Requirement: Export button
|
||||
The application SHALL provide an "Export" button that triggers a download of the annotations CSV. Clicking the button SHALL navigate to or fetch from the `GET /api/export` endpoint, resulting in a CSV file download.
|
||||
|
||||
#### Scenario: Export annotations
|
||||
- **WHEN** user clicks the "Export" button
|
||||
- **THEN** a CSV file named "annotations.csv" downloads containing all annotation data
|
||||
Loading…
Add table
Add a link
Reference in a new issue