Merge pull request 'cleanup' (#12) from review-opti into main
Reviewed-on: #12
This commit was merged in pull request #12.
This commit is contained in:
@@ -1,167 +0,0 @@
|
||||
# Docker Build Fix - Simplified Dependency Installation
|
||||
|
||||
## Issue
|
||||
Docker build was failing with:
|
||||
```
|
||||
npm error The `npm ci` command can only install with an existing package-lock.json
|
||||
```
|
||||
|
||||
## Root Cause
|
||||
The original Dockerfile used `npm ci` which requires fully resolved `package-lock.json` files. These lockfiles were missing and stub lockfiles don't work because `npm ci` requires the complete dependency tree.
|
||||
|
||||
## Solution Applied
|
||||
|
||||
### Simplified Approach
|
||||
The Dockerfile now uses **`npm install`** instead of `npm ci`. This is simpler and more reliable:
|
||||
|
||||
```dockerfile
|
||||
RUN npm install
|
||||
```
|
||||
|
||||
**Why this works:**
|
||||
- ✅ No lockfile required
|
||||
- ✅ Automatically resolves and installs all dependencies
|
||||
- ✅ Works consistently across all environments
|
||||
- ✅ No manual lockfile maintenance needed
|
||||
- ✅ Simpler Dockerfile = easier to maintain
|
||||
|
||||
### Trade-offs
|
||||
|
||||
| Approach | Speed | Lockfile Required | Deterministic | Maintenance |
|
||||
|----------|-------|-------------------|---------------|-------------|
|
||||
| `npm ci` | Fastest | ✅ Yes | ✅ Yes | High |
|
||||
| **`npm install`** | Fast | ❌ No | ⚠️ By version ranges | **Low** |
|
||||
|
||||
**Note:** While `npm install` resolves versions at build time (not 100% deterministic), your `package.json` uses caret ranges (e.g., `^4.19.0`) which only allow minor/patch updates, providing reasonable stability.
|
||||
|
||||
### Files Modified
|
||||
|
||||
1. **Dockerfile** - Simplified to use `npm install` in all three stages
|
||||
2. **.dockerignore** - Optimizes build context
|
||||
3. **backend/package.json** - Updated multer to v2.1.0 (v1.4.5 no longer exists)
|
||||
|
||||
### Dependency Updates
|
||||
|
||||
- **multer**: `^1.4.5` → `^2.1.0` (security fixes, v1.x removed from npm)
|
||||
- **@types/multer**: `^1.4.7` → `^2.1.0` (matching types)
|
||||
|
||||
## How It Works
|
||||
|
||||
### Build Flow
|
||||
|
||||
1. **Copy package.json** into build stage
|
||||
2. **Run npm install** - automatically resolves and installs all dependencies
|
||||
3. **Build the application**
|
||||
|
||||
No lockfile generation or validation needed!
|
||||
|
||||
## Usage
|
||||
|
||||
### Build Docker Image
|
||||
```bash
|
||||
docker build -t pnger:latest .
|
||||
```
|
||||
|
||||
The build will:
|
||||
- Install dependencies from npm registry
|
||||
- Build frontend and backend
|
||||
- Create production image with only runtime dependencies
|
||||
- Complete successfully every time
|
||||
|
||||
### Run Container
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 3000:3000 \
|
||||
-e MAX_FILE_SIZE=10485760 \
|
||||
--name pnger \
|
||||
pnger:latest
|
||||
```
|
||||
|
||||
### Unraid Deployment
|
||||
The Docker image builds cleanly in Unraid without any configuration needed.
|
||||
|
||||
## Optional: Add Lockfiles for Deterministic Builds
|
||||
|
||||
If you want 100% deterministic builds with locked dependency versions:
|
||||
|
||||
```bash
|
||||
# Generate lockfiles locally
|
||||
cd frontend && npm install && cd ..
|
||||
cd backend && npm install && cd ..
|
||||
|
||||
# Commit them
|
||||
git add frontend/package-lock.json backend/package-lock.json
|
||||
git commit -m "Add lockfiles for deterministic builds"
|
||||
|
||||
# Update Dockerfile to use npm ci instead of npm install
|
||||
```
|
||||
|
||||
**Benefits of lockfiles:**
|
||||
- Guaranteed exact dependency versions
|
||||
- Slightly faster builds
|
||||
- Better for production environments
|
||||
|
||||
**Drawbacks:**
|
||||
- Must update lockfiles when changing dependencies
|
||||
- More complex maintenance
|
||||
|
||||
## Build Optimization
|
||||
|
||||
The `.dockerignore` file excludes:
|
||||
- `node_modules/` (prevents copying local dependencies)
|
||||
- Development files (`.env`, `.vscode/`, etc.)
|
||||
- Build artifacts
|
||||
- Documentation and test files
|
||||
|
||||
This keeps build context small and fast.
|
||||
|
||||
## Verification
|
||||
|
||||
Test the complete build:
|
||||
```bash
|
||||
# Build image
|
||||
docker build -t pnger:test .
|
||||
|
||||
# Run container
|
||||
docker run -d -p 3000:3000 --name pnger-test pnger:test
|
||||
|
||||
# Check health
|
||||
curl http://localhost:3000
|
||||
|
||||
# View logs
|
||||
docker logs pnger-test
|
||||
|
||||
# Cleanup
|
||||
docker stop pnger-test && docker rm pnger-test
|
||||
```
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Multi-Stage Build
|
||||
|
||||
1. **frontend-builder**: Builds Svelte/Vite frontend with all dev dependencies
|
||||
2. **backend-builder**: Compiles TypeScript backend with all dev dependencies
|
||||
3. **production**: Combines compiled outputs with production dependencies only (`--omit=dev`)
|
||||
|
||||
### Security Features
|
||||
|
||||
- Runs as non-root user (`node`)
|
||||
- Health check endpoint configured
|
||||
- Minimal production dependencies
|
||||
- Alpine Linux base (smaller attack surface)
|
||||
- No unnecessary dev tools in production image
|
||||
|
||||
### Multer v2.1.0 Upgrade
|
||||
|
||||
Upgraded from v1.4.5 (no longer available) to v2.1.0:
|
||||
- ✅ Security fixes (CVE-2026-2359 and others)
|
||||
- ✅ Backward compatible API
|
||||
- ✅ Better performance
|
||||
- ✅ Active maintenance
|
||||
|
||||
---
|
||||
|
||||
**Created**: 2026-03-08
|
||||
**Branch**: `fix/add-package-lockfiles`
|
||||
**Status**: Ready to merge
|
||||
**Issue**: Docker build failing ✅ RESOLVED
|
||||
379
INSTRUCTIONS.md
379
INSTRUCTIONS.md
@@ -1,86 +1,76 @@
|
||||
# PNGer Development Instructions
|
||||
# PNGer Development & Technical Instructions
|
||||
|
||||
## Project Overview
|
||||
|
||||
PNGer is a single-container web application for PNG editing and resizing, designed for deployment on Unraid with Gitea version control.
|
||||
|
||||
## Development Roadmap
|
||||
|
||||
### Phase 1: MVP Foundation (Completed ✅)
|
||||
- [x] Repository setup
|
||||
- [x] Project structure initialization
|
||||
- [x] Backend API scaffold
|
||||
- [x] Frontend scaffold
|
||||
- [x] Basic upload/download flow
|
||||
- [x] Dockerfile configuration
|
||||
- [x] Live preview implementation
|
||||
- [x] Dark/light mode theming
|
||||
- [x] Image resize and compression
|
||||
- [x] Format conversion (PNG, WebP, JPEG)
|
||||
|
||||
### Phase 2: Sprint 1 - Enhanced UX (Completed ✅)
|
||||
- [x] Drag & drop file upload
|
||||
- [x] Clipboard paste (Ctrl+V)
|
||||
- [x] Smart presets (8 configurations)
|
||||
- [x] Keyboard shortcuts (Enter, Ctrl+Enter, ?, Esc)
|
||||
- [x] Enhanced visual feedback
|
||||
- [x] Responsive UI improvements
|
||||
- [x] Theme contrast fixes
|
||||
|
||||
### Phase 3: Advanced Features (Upcoming)
|
||||
- [ ] Batch processing
|
||||
- [ ] Advanced crop tool with visual selector
|
||||
- [ ] Watermarking
|
||||
- [ ] Image filters and effects
|
||||
- [ ] Undo/redo functionality
|
||||
- [ ] History/recent files
|
||||
PNGer is a single-container web application for PNG editing and resizing, designed for deployment on Unraid with Gitea version control. It features a modern Svelte frontend and a high-performance Node.js/Sharp backend.
|
||||
|
||||
## Technical Architecture
|
||||
|
||||
### Architecture Overview
|
||||
|
||||
PNGer uses a multi-layered architecture designed for responsiveness and efficiency:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[User Browser] --> B[Svelte Frontend]
|
||||
B --> C[Canvas API (Live Preview)]
|
||||
B --> D[Express API (Backend)]
|
||||
D --> E[Sharp Image Library]
|
||||
D --> F[Runtime Environment (Docker)]
|
||||
```
|
||||
|
||||
### Backend (Express + Sharp)
|
||||
|
||||
**Endpoints:**
|
||||
The backend is built with Node.js and TypeScript, using Express for the API and Sharp for high-performance image processing.
|
||||
|
||||
**Key Endpoints:**
|
||||
- `POST /api/transform` - Transform image (resize, crop, compress, convert)
|
||||
- `GET /api/health` - Health check
|
||||
|
||||
**Key Dependencies:**
|
||||
- express: Web framework
|
||||
- multer: File upload handling
|
||||
- sharp: Image processing
|
||||
- cors: Cross-origin support
|
||||
- `sharp`: High-performance image processing (handles resizing, cropping, and format conversion).
|
||||
- `multer`: Middleware for handling `multipart/form-data`, used for file uploads.
|
||||
- `express`: Web framework for the API.
|
||||
|
||||
### Frontend (Svelte + Vite)
|
||||
|
||||
**Components:**
|
||||
- `App.svelte` - Main container with all features
|
||||
- `lib/api.ts` - API client
|
||||
- `lib/preview.ts` - Live preview logic using Canvas API
|
||||
- `lib/theme.ts` - Dark/light theme management
|
||||
- `lib/presets.ts` - Smart presets configuration
|
||||
The frontend is a reactive Svelte application that prioritizes real-time feedback and UX.
|
||||
|
||||
**Key Features:**
|
||||
- Drag & drop upload
|
||||
- Clipboard paste support
|
||||
- Real-time preview (debounced 300ms)
|
||||
- Side-by-side comparison
|
||||
- File size analysis
|
||||
- 8 smart presets
|
||||
- Keyboard shortcuts
|
||||
- Persistent theme preference
|
||||
**Core Components & Modules:**
|
||||
- `App.svelte`: Main application container managing state and UI.
|
||||
- `lib/api.ts`: API client for backend communication.
|
||||
- `lib/preview.ts`: Client-side preview logic using the Canvas API for instant feedback.
|
||||
- `lib/theme.ts`: Dark/light theme management with persistent storage.
|
||||
- `lib/presets.ts`: Configuration for smart image presets.
|
||||
|
||||
**Key Dependencies:**
|
||||
- svelte: Reactive framework
|
||||
- vite: Build tool
|
||||
- axios: HTTP client
|
||||
### Design System (Dark/Light Modes)
|
||||
|
||||
### Docker Strategy
|
||||
The UI uses a modern design system with CSS custom properties for theming:
|
||||
- **Light Mode**: Clean white background with dark gold (`#b8860b`) accents.
|
||||
- **Dark Mode**: Deep black (`#0a0a0a`) background with light gold (`#daa520`) accents.
|
||||
- **Transparencies**: Dark/Light toggling allows users to inspect PNG transparency against different backgrounds.
|
||||
|
||||
**Multi-stage Build:**
|
||||
1. Stage 1: Build frontend (Vite build)
|
||||
2. Stage 2: Copy frontend + setup backend
|
||||
3. Final image: Alpine-based Node.js
|
||||
## Implementation Details
|
||||
|
||||
**Image Size Target:** < 150MB
|
||||
### Live Preview System
|
||||
|
||||
Live preview is implemented using a client-side Canvas-based approach to provide instant feedback (< 100ms) without server round-trips.
|
||||
|
||||
**How it works:**
|
||||
1. User uploads an image.
|
||||
2. The browser loads the image into an HTML5 Canvas.
|
||||
3. On any parameter change (width, quality, etc.), a debounced (300ms) update applies transformations to the canvas.
|
||||
4. The canvas content is displayed side-by-side with the original for comparison.
|
||||
5. File sizes are estimated from the Canvas data URL to provide immediate feedback on optimization savings.
|
||||
|
||||
### Docker Strategy & Fixes
|
||||
|
||||
PNGer uses a multi-stage Docker build to minimize image size and maximize security.
|
||||
|
||||
**Optimization Fixes applied:**
|
||||
- **Dependency Installation**: Uses `npm install` instead of `npm ci` to handle missing lockfiles gracefully while maintaining stability via caret ranges in `package.json`.
|
||||
- **Security**: Runs as a non-root `node` user in the final Alpine-based image.
|
||||
- **Multer Upgrade**: Upgraded to `v2.1.0` for improved security and performance.
|
||||
|
||||
## Local Development Setup
|
||||
|
||||
@@ -89,7 +79,7 @@ PNGer is a single-container web application for PNG editing and resizing, design
|
||||
- npm or pnpm
|
||||
- Docker (optional)
|
||||
|
||||
### Initial Setup
|
||||
### Setup Steps
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
@@ -101,259 +91,44 @@ cd backend
|
||||
npm install
|
||||
npm run dev
|
||||
|
||||
# Setup frontend (new terminal)
|
||||
# Setup frontend (separate terminal)
|
||||
cd frontend
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Development Workflow
|
||||
|
||||
1. **Feature Branch**: Create from `main`
|
||||
2. **Develop**: Make changes with hot-reload
|
||||
3. **Test**: Manual testing + health checks
|
||||
4. **Commit**: Descriptive commit messages
|
||||
5. **Push**: Push to Gitea
|
||||
6. **Review**: Self-review changes
|
||||
7. **Merge**: Merge to `main`
|
||||
|
||||
### Environment Variables
|
||||
|
||||
**Backend (.env):**
|
||||
```
|
||||
PORT=3000
|
||||
NODE_ENV=development
|
||||
MAX_FILE_SIZE=10485760
|
||||
CORS_ORIGIN=http://localhost:5173
|
||||
```
|
||||
- `PORT`: 3000 (internal)
|
||||
- `MAX_FILE_SIZE`: 10485760 (10MB default)
|
||||
- `CORS_ORIGIN`: http://localhost:5173
|
||||
|
||||
**Frontend (.env):**
|
||||
```
|
||||
VITE_API_URL=http://localhost:3000/api
|
||||
```
|
||||
- `VITE_API_URL`: http://localhost:3000/api
|
||||
|
||||
## Docker Build & Test
|
||||
## Development Workflow & Standards
|
||||
|
||||
```bash
|
||||
# Build image
|
||||
docker build -t pnger:test .
|
||||
### Workflow
|
||||
1. **Feature Branch**: Create from `main`.
|
||||
2. **Develop**: Use hot-reload (`npm run dev`).
|
||||
3. **Test**: Perform manual testing of image operations and presets.
|
||||
4. **Commit**: Use `type: description` format (e.g., `feat: add rotation`).
|
||||
5. **Merge**: Merge into `main` after review.
|
||||
|
||||
# Run container
|
||||
docker run -p 8080:3000 --name pnger-test pnger:test
|
||||
|
||||
# Test endpoints
|
||||
curl http://localhost:8080/api/health
|
||||
|
||||
# View logs
|
||||
docker logs pnger-test
|
||||
|
||||
# Stop and remove
|
||||
docker stop pnger-test && docker rm pnger-test
|
||||
```
|
||||
|
||||
## Unraid Deployment
|
||||
|
||||
### Setup Steps
|
||||
|
||||
1. **SSH into Unraid**
|
||||
2. **Navigate to docker configs**: `/mnt/user/appdata/pnger`
|
||||
3. **Clone repository**:
|
||||
```bash
|
||||
git clone https://git.alwisp.com/jason/pnger.git
|
||||
cd pnger
|
||||
```
|
||||
4. **Build and run**:
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
5. **Access**: http://[unraid-ip]:8080
|
||||
|
||||
### Update Process
|
||||
|
||||
```bash
|
||||
cd /mnt/user/appdata/pnger
|
||||
git pull origin main
|
||||
docker-compose down
|
||||
docker-compose build
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Code Standards
|
||||
|
||||
### JavaScript/TypeScript
|
||||
- Use ES6+ features
|
||||
- Async/await for asynchronous operations
|
||||
- Descriptive variable names
|
||||
- Comments for complex logic only
|
||||
- Type safety with TypeScript
|
||||
|
||||
### File Organization
|
||||
- One component per file
|
||||
- Group related utilities
|
||||
- Keep components under 300 lines (split if larger)
|
||||
- Organize by feature when possible
|
||||
|
||||
### Commit Messages
|
||||
- Format: `type: description`
|
||||
- Types: feat, fix, docs, style, refactor, test, chore
|
||||
- Example: `feat: add drag and drop upload`
|
||||
- Be specific and descriptive
|
||||
|
||||
## Feature Implementation Notes
|
||||
|
||||
### Drag & Drop Upload
|
||||
- Uses HTML5 Drag and Drop API
|
||||
- Visual feedback on dragover/dragleave
|
||||
- File type validation on drop
|
||||
- Falls back to file input for browser support
|
||||
|
||||
### Clipboard Paste
|
||||
- Listens for paste events on document
|
||||
- Extracts image from clipboard items
|
||||
- Supports screenshots and copied images
|
||||
- Works across all major browsers
|
||||
|
||||
### Smart Presets
|
||||
- 8 predefined configurations:
|
||||
1. Web Optimized (1920x1080, 80%, WebP)
|
||||
2. Social Media (1080x1080, 85%, JPEG)
|
||||
3. Profile Picture (400x400, 90%, PNG)
|
||||
4. Email Friendly (800x600, 75%, JPEG)
|
||||
5. Tiny Icon (64x64, 100%, PNG)
|
||||
6. Retina 2x (2x size, 90%)
|
||||
7. Icon Small (256x256, 95%, PNG)
|
||||
8. Icon Large (512x512, 95%, PNG)
|
||||
- Applies all settings with one click
|
||||
- Visual icons for easy identification
|
||||
|
||||
### Keyboard Shortcuts
|
||||
- Global document listeners
|
||||
- Context-aware Enter key (checks focus)
|
||||
- Help dialog with ? key
|
||||
- Esc to close dialogs
|
||||
- Cross-platform support (Ctrl/Cmd)
|
||||
|
||||
### Live Preview
|
||||
- Client-side Canvas API rendering
|
||||
- Debounced updates (300ms)
|
||||
- Reactive state management
|
||||
- Automatic size estimation
|
||||
- Side-by-side comparison
|
||||
### Code Standards
|
||||
- **TypeScript**: Use strict types where possible.
|
||||
- **Svelte**: Keep components modular and under 300 lines.
|
||||
- **Async/Await**: Use for all asynchronous operations.
|
||||
- **Semantic HTML**: Ensure accessibility and proper structure.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
- **Port in use**: `lsof -ti:3000 | xargs kill -9`
|
||||
- **Sharp issues**: `npm rebuild sharp`
|
||||
- **Docker Cache**: `docker builder prune` if builds fail unexpectedly.
|
||||
- **Preview Glitches**: Check browser console for Canvas API errors.
|
||||
|
||||
**Port already in use:**
|
||||
```bash
|
||||
lsof -ti:3000 | xargs kill -9
|
||||
```
|
||||
---
|
||||
|
||||
**Sharp installation issues:**
|
||||
```bash
|
||||
npm rebuild sharp
|
||||
```
|
||||
|
||||
**Docker build fails:**
|
||||
- Check Docker daemon is running
|
||||
- Verify Dockerfile syntax
|
||||
- Clear Docker cache: `docker builder prune`
|
||||
|
||||
**Preview not updating:**
|
||||
- Check browser console for errors
|
||||
- Verify Canvas API support
|
||||
- Clear browser cache
|
||||
|
||||
**Drag & drop not working:**
|
||||
- Check file type validation
|
||||
- Verify event handlers are attached
|
||||
- Test with different browsers
|
||||
|
||||
## Performance Targets
|
||||
|
||||
- Upload handling: < 100ms (for 5MB file)
|
||||
- Image processing: < 2s (for 10MP image)
|
||||
- Download generation: < 500ms
|
||||
- UI response time: < 100ms
|
||||
- Preview generation: < 500ms (client-side)
|
||||
- Docker image size: < 150MB
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- File type validation (image/* only)
|
||||
- File size limits (10MB default, configurable)
|
||||
- No persistent storage of user files
|
||||
- Memory cleanup after processing
|
||||
- CORS configuration
|
||||
- Input sanitization
|
||||
- Error message sanitization (no path disclosure)
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Upload Methods
|
||||
- [ ] File input (click to browse)
|
||||
- [ ] Drag & drop
|
||||
- [ ] Clipboard paste (Ctrl+V)
|
||||
- [ ] File validation errors
|
||||
- [ ] Size limit handling
|
||||
|
||||
### Image Operations
|
||||
- [ ] Resize with width only
|
||||
- [ ] Resize with height only
|
||||
- [ ] Resize with both dimensions
|
||||
- [ ] Crop to fit (cover mode)
|
||||
- [ ] All 9 crop positions
|
||||
- [ ] Format conversion (PNG, WebP, JPEG)
|
||||
- [ ] Quality adjustment
|
||||
|
||||
### Presets
|
||||
- [ ] All 8 presets apply correctly
|
||||
- [ ] Preset values match specifications
|
||||
- [ ] Visual feedback on selection
|
||||
|
||||
### Keyboard Shortcuts
|
||||
- [ ] Ctrl+V pastes image
|
||||
- [ ] Enter downloads (not in input)
|
||||
- [ ] Ctrl+Enter downloads (anywhere)
|
||||
- [ ] ? shows help dialog
|
||||
- [ ] Esc closes dialog
|
||||
|
||||
### UI/UX
|
||||
- [ ] Theme toggle works
|
||||
- [ ] Theme persists on reload
|
||||
- [ ] Live preview updates
|
||||
- [ ] File size analysis accurate
|
||||
- [ ] Error messages clear
|
||||
- [ ] Loading states visible
|
||||
- [ ] Responsive on mobile
|
||||
|
||||
### Browser Compatibility
|
||||
- [ ] Chrome/Edge
|
||||
- [ ] Firefox
|
||||
- [ ] Safari
|
||||
- [ ] Mobile browsers
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ✅ Create backend folder structure
|
||||
2. ✅ Create frontend folder structure
|
||||
3. ✅ Initialize package.json files
|
||||
4. ✅ Create Dockerfile
|
||||
5. ✅ Create docker-compose.yml
|
||||
6. ✅ Implement MVP features
|
||||
7. ✅ Add drag & drop upload
|
||||
8. ✅ Add clipboard paste
|
||||
9. ✅ Add smart presets
|
||||
10. ✅ Add keyboard shortcuts
|
||||
11. [ ] Implement batch processing
|
||||
12. [ ] Add advanced crop tool
|
||||
13. [ ] Add watermarking
|
||||
14. [ ] Add filters/effects
|
||||
|
||||
## Resources
|
||||
|
||||
- [Sharp Documentation](https://sharp.pixelplumbing.com/)
|
||||
- [Svelte Documentation](https://svelte.dev/docs)
|
||||
- [Canvas API Reference](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)
|
||||
- [Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API)
|
||||
- [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API)
|
||||
**Last Updated**: March 12, 2026
|
||||
346
README.md
346
README.md
@@ -1,341 +1,49 @@
|
||||
# PNGer - Modern PNG Editor & Resizer
|
||||
|
||||
A sleek, modern PNG editor and resizer with **live preview**, **drag & drop**, **smart presets**, **keyboard shortcuts**, and **dark/light mode theming**. Built with TypeScript and deployed as a single Docker container on Unraid.
|
||||
A sleek, modern PNG editor and resizer with **live preview**, **drag & drop**, **smart presets**, **keyboard shortcuts**, and **dark/light mode theming**. Built with TypeScript and optimized for deployment as a single Docker container.
|
||||
|
||||
## ✨ Features
|
||||
|
||||
### 🎨 Modern UI with Dark/Light Mode
|
||||
- **Dark Mode**: Black background (#0a0a0a) with light gold (#daa520) accents
|
||||
- **Light Mode**: White background with dark gold (#b8860b) accents
|
||||
- Perfect for inspecting PNG transparency on different backgrounds
|
||||
- Persistent theme preference
|
||||
- Smooth color transitions
|
||||
- **🎨 Modern UI**: Beautiful dark/light mode for inspecting transparency.
|
||||
- **⚡ Live Preview**: Instant side-by-side comparison with file size analysis.
|
||||
- **🚀 Efficiency**: Drag & Drop upload, clipboard paste (`Ctrl+V`), and smart presets.
|
||||
- **🖼️ Precision**: Control width, height, quality, and crop positions (9 modes).
|
||||
- **📦 Reliable Deployment**: Multi-stage Docker build optimized for Unraid and Gitea.
|
||||
|
||||
### ⚡ Live Preview
|
||||
- **Real-time preview** of transformations before download
|
||||
- **Side-by-side comparison** (original vs transformed)
|
||||
- **File size analysis** showing savings or increase
|
||||
- **Instant feedback** using client-side Canvas API (< 500ms)
|
||||
- No server round-trip needed for preview
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 🖼️ Image Operations
|
||||
- **Resize Operations**: Width, height, and aspect ratio controls
|
||||
- **Crop to Fit**: Smart cropping with position control (9 positions)
|
||||
- **Format Conversion**: PNG, WebP, and JPEG output
|
||||
- **Quality Control**: Adjustable compression settings (10-100%)
|
||||
- **Fit Modes**: Inside (resize only) or Cover (crop to fill)
|
||||
|
||||
### 🚀 Performance & Usability
|
||||
- **Drag & Drop**: Drag images directly into the app or use file browser
|
||||
- **Clipboard Paste**: Paste images with Ctrl+V (Cmd+V on Mac)
|
||||
- **Smart Presets**: Quick access to common configurations:
|
||||
- 📷 Web Optimized (1920x1080, 80% quality, WebP)
|
||||
- 📱 Social Media (1080x1080, 85% quality, JPEG)
|
||||
- 👤 Profile Picture (400x400, 90% quality, PNG)
|
||||
- 📧 Email Friendly (800x600, 75% quality, JPEG)
|
||||
- ⭐ Tiny Icon (64x64, 100% quality, PNG)
|
||||
- 🔍 Retina 2x (2x dimensions, 90% quality)
|
||||
- 💎 Icon Small (256x256, 95% quality, PNG)
|
||||
- 🔶 Icon Large (512x512, 95% quality, PNG)
|
||||
- **Keyboard Shortcuts**: Fast workflow with keyboard controls (see below)
|
||||
- **Direct Download**: No server-side storage, immediate download
|
||||
- **Modern UI**: Sleek, responsive TypeScript/Svelte design
|
||||
- **File Analysis**: Original size, transformed size, savings percentage
|
||||
- **Debounced Updates**: Smooth preview generation (300ms delay)
|
||||
- **Visual Feedback**: Loading states, error messages, success indicators
|
||||
|
||||
### ⌨️ Keyboard Shortcuts
|
||||
- **Ctrl+V** (Cmd+V): Paste image from clipboard
|
||||
- **Enter**: Transform & Download (works when input not focused)
|
||||
- **Ctrl+Enter** (Cmd+Enter): Transform & Download (works anywhere)
|
||||
- **?**: Show/hide keyboard shortcuts help
|
||||
- **Esc**: Close shortcuts dialog
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- **Frontend**: Svelte 4 + Vite + TypeScript
|
||||
- **Backend**: Node.js + Express + TypeScript
|
||||
- **Image Processing**: Sharp (high-performance image library)
|
||||
- **Preview**: Canvas API (client-side)
|
||||
- **Container**: Docker (Alpine-based, multi-stage build)
|
||||
- **Deployment**: Unraid via Docker Compose
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Unraid Deployment (Recommended)
|
||||
|
||||
1. **Clone or pull this repository to your Unraid server:**
|
||||
### Docker/Unraid Deployment
|
||||
1. **Clone & Build**:
|
||||
```bash
|
||||
cd /mnt/user/appdata
|
||||
git clone https://git.alwisp.com/jason/pnger.git
|
||||
cd pnger
|
||||
```
|
||||
|
||||
2. **Build the Docker image:**
|
||||
```bash
|
||||
docker build -t pnger:latest .
|
||||
```
|
||||
|
||||
3. **Run via docker-compose:**
|
||||
2. **Run**:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
3. **Access**: `http://localhost:8080` (or your Unraid IP)
|
||||
|
||||
4. **Access the application:**
|
||||
- Navigate to `http://[unraid-ip]:8080`
|
||||
### Local Development
|
||||
1. **Install & Run Backend**: `cd backend && npm install && npm run dev`
|
||||
2. **Install & Run Frontend**: `cd frontend && npm install && npm run dev`
|
||||
3. **Access**: `http://localhost:5173`
|
||||
|
||||
### Unraid Environment Variables (Configurable via UI)
|
||||
## 📚 Documentation
|
||||
|
||||
When setting up in Unraid Docker UI, you can configure:
|
||||
For more detailed information, please refer to:
|
||||
- **[INSTRUCTIONS.md](./INSTRUCTIONS.md)**: Technical architecture, development setup, code standards, and troubleshooting.
|
||||
- **[ROADMAP.md](./ROADMAP.md)**: Project history, sprint updates, and future feature plans.
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `HOST_PORT` | `8080` | External port to access the app |
|
||||
| `MAX_FILE_SIZE` | `10485760` | Max upload size in bytes (10MB default) |
|
||||
| `MEM_LIMIT` | `512m` | Memory limit for container |
|
||||
| `CPU_LIMIT` | `1.0` | CPU limit (1.0 = 1 full core) |
|
||||
| `NODE_ENV` | `production` | Node environment |
|
||||
## ⌨️ Keyboard Shortcuts
|
||||
|
||||
### Unraid Docker Template Example
|
||||
- `Ctrl+V`: Paste image from clipboard
|
||||
- `Enter`: Download (when input not focused)
|
||||
- `?`: Show shortcuts help
|
||||
- `Esc`: Close dialogs
|
||||
|
||||
```xml
|
||||
<?xml version="1.0"?>
|
||||
<Container version="2">
|
||||
<Name>pnger</Name>
|
||||
<Repository>pnger:latest</Repository>
|
||||
<Network>bridge</Network>
|
||||
<Privileged>false</Privileged>
|
||||
<WebUI>http://[IP]:[PORT:8080]</WebUI>
|
||||
<Config Name="WebUI Port" Target="3000" Default="8080" Mode="tcp" Description="Port for web interface" Type="Port" Display="always" Required="true" Mask="false">8080</Config>
|
||||
<Config Name="Max File Size" Target="MAX_FILE_SIZE" Default="10485760" Mode="" Description="Maximum upload file size in bytes" Type="Variable" Display="advanced" Required="false" Mask="false">10485760</Config>
|
||||
<Config Name="Memory Limit" Target="" Default="512m" Mode="" Description="Container memory limit" Type="Variable" Display="advanced" Required="false" Mask="false">512m</Config>
|
||||
</Container>
|
||||
```
|
||||
---
|
||||
|
||||
## Local Development
|
||||
|
||||
### Prerequisites
|
||||
- Node.js 20+
|
||||
- npm or yarn
|
||||
|
||||
### Setup
|
||||
|
||||
1. **Install backend dependencies:**
|
||||
```bash
|
||||
cd backend
|
||||
npm install
|
||||
```
|
||||
|
||||
2. **Install frontend dependencies:**
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
```
|
||||
|
||||
3. **Run development servers:**
|
||||
|
||||
Terminal 1 (Backend):
|
||||
```bash
|
||||
cd backend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Terminal 2 (Frontend):
|
||||
```bash
|
||||
cd frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
4. **Access dev server:**
|
||||
- Frontend: `http://localhost:5173`
|
||||
- Backend API: `http://localhost:3000/api`
|
||||
|
||||
### Building for Production
|
||||
|
||||
```bash
|
||||
# Backend TypeScript compilation
|
||||
cd backend
|
||||
npm run build
|
||||
|
||||
# Frontend build
|
||||
cd frontend
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Docker Deployment (Manual)
|
||||
|
||||
```bash
|
||||
# Build the image (all dependencies and builds are handled internally)
|
||||
docker build -t pnger:latest .
|
||||
|
||||
# Run the container
|
||||
docker run -d \
|
||||
--name pnger \
|
||||
-p 8080:3000 \
|
||||
-e MAX_FILE_SIZE=10485760 \
|
||||
--restart unless-stopped \
|
||||
pnger:latest
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
pnger/
|
||||
├── frontend/ # Svelte + TypeScript application
|
||||
│ ├── src/
|
||||
│ │ ├── App.svelte # Main UI component (with live preview)
|
||||
│ │ ├── main.ts # Entry point
|
||||
│ │ ├── app.css # Design system (dark/light modes)
|
||||
│ │ └── lib/
|
||||
│ │ ├── api.ts # API client
|
||||
│ │ ├── preview.ts # Live preview logic
|
||||
│ │ ├── theme.ts # Theme management store
|
||||
│ │ └── presets.ts # Smart presets configuration
|
||||
│ ├── package.json
|
||||
│ ├── tsconfig.json
|
||||
│ └── vite.config.ts
|
||||
├── backend/ # Express + TypeScript API server
|
||||
│ ├── src/
|
||||
│ │ ├── index.ts # Express server
|
||||
│ │ ├── routes/
|
||||
│ │ │ └── image.ts # Image transform endpoint
|
||||
│ │ └── types/
|
||||
│ │ └── image.ts # TypeScript types
|
||||
│ ├── package.json
|
||||
│ └── tsconfig.json
|
||||
├── Dockerfile # Multi-stage build (frontend + backend)
|
||||
├── docker-compose.yml # Unraid deployment config
|
||||
├── ROADMAP.md # Feature roadmap
|
||||
├── SPRINT1_CHANGES.md # Sprint 1 implementation details
|
||||
└── UI_UPGRADE_NOTES.md # UI upgrade documentation
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. User uploads an image via drag & drop, file browser, or clipboard paste
|
||||
2. **Live preview** generates instantly using Canvas API
|
||||
3. User adjusts parameters (width, height, quality, format, etc.) or selects a preset
|
||||
4. Preview updates in real-time (debounced 300ms)
|
||||
5. User sees file size comparison and savings
|
||||
6. When satisfied, user clicks "Transform & Download" or presses Enter
|
||||
7. Frontend sends image + parameters to backend API
|
||||
8. Backend processes using Sharp (resize, crop, compress, convert)
|
||||
9. Processed image returned directly to browser
|
||||
10. Browser triggers automatic download
|
||||
11. No files stored on server (stateless operation)
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### POST /api/transform
|
||||
|
||||
Transform an image with resize, crop, and format conversion.
|
||||
|
||||
**Request:**
|
||||
- Method: `POST`
|
||||
- Content-Type: `multipart/form-data`
|
||||
- Body:
|
||||
- `file`: Image file (PNG/JPEG/WebP)
|
||||
- `width`: Target width (optional)
|
||||
- `height`: Target height (optional)
|
||||
- `quality`: Quality 10-100 (optional, default: 80)
|
||||
- `format`: Output format `png|webp|jpeg` (optional, default: `png`)
|
||||
- `fit`: Resize mode `inside|cover` (optional, default: `inside`)
|
||||
- `position`: Crop position when `fit=cover` (optional, default: `center`)
|
||||
|
||||
**Response:**
|
||||
- Content-Type: `image/[format]`
|
||||
- Body: Transformed image binary
|
||||
|
||||
## Configuration
|
||||
|
||||
All configuration is handled via environment variables passed through Docker/Unraid:
|
||||
|
||||
- `PORT`: Server port (default: `3000`, internal)
|
||||
- `MAX_FILE_SIZE`: Maximum upload size in bytes (default: `10485760` = 10MB)
|
||||
- `TEMP_DIR`: Temporary directory for uploads (default: `/app/temp`)
|
||||
- `NODE_ENV`: Node environment (default: `production`)
|
||||
|
||||
## UI Features in Detail
|
||||
|
||||
### Dark/Light Mode
|
||||
- **Toggle Button**: Sun (☀️) / Moon (🌙) icon in header
|
||||
- **Persistent**: Saved to localStorage
|
||||
- **System Detection**: Uses OS preference on first visit
|
||||
- **Smooth Transitions**: Colors fade smoothly (250ms)
|
||||
- **Use Case**: Compare PNG transparency on black vs white backgrounds
|
||||
|
||||
### Live Preview
|
||||
- **Side-by-Side**: Original image on left, preview on right
|
||||
- **File Size**: Shows before and after sizes
|
||||
- **Savings Indicator**: Green for reduction, yellow for increase
|
||||
- **Instant Updates**: Debounced at 300ms for smooth performance
|
||||
- **Canvas-Based**: No server load, runs in browser
|
||||
|
||||
### Drag & Drop Upload
|
||||
- **Drop Zone**: Large, responsive drop target with visual feedback
|
||||
- **Multiple Methods**: Drag & drop, click to browse, or paste from clipboard
|
||||
- **File Validation**: Instant validation with clear error messages
|
||||
- **Visual States**: Hover and dragging states for better UX
|
||||
|
||||
### Smart Presets
|
||||
- **8 Pre-configured Options**: Common use cases ready to go
|
||||
- **One-Click Apply**: Instantly sets all parameters
|
||||
- **Visual Icons**: Easy identification of each preset
|
||||
- **Responsive Grid**: Adapts to screen size
|
||||
|
||||
### Keyboard Shortcuts
|
||||
- **Productivity Focused**: Common operations accessible via keyboard
|
||||
- **Help Dialog**: Press ? to see all shortcuts
|
||||
- **Context Aware**: Enter works differently based on focus
|
||||
- **Cross-Platform**: Supports both Ctrl and Cmd modifiers
|
||||
|
||||
### Image Analysis
|
||||
- Original file size displayed
|
||||
- Preview size estimation
|
||||
- Savings/increase percentage
|
||||
- Visual feedback with color coding
|
||||
|
||||
## Recent Updates (Sprint 1)
|
||||
|
||||
✅ **Drag & Drop Upload** - Drag images directly into the app
|
||||
✅ **Clipboard Paste** - Paste images with Ctrl+V
|
||||
✅ **Smart Presets** - 8 quick-access presets for common scenarios
|
||||
✅ **Keyboard Shortcuts** - Fast workflow with keyboard controls
|
||||
✅ **Enhanced UX** - Better visual feedback and error handling
|
||||
✅ **Theme Contrast Fixes** - Improved text visibility in both themes
|
||||
|
||||
See [SPRINT1_CHANGES.md](./SPRINT1_CHANGES.md) for detailed implementation notes.
|
||||
|
||||
## Roadmap
|
||||
|
||||
See [ROADMAP.md](./ROADMAP.md) for planned features including:
|
||||
- Batch processing
|
||||
- Advanced crop tool with visual selector
|
||||
- Watermarking
|
||||
- Image filters and effects
|
||||
- And more!
|
||||
|
||||
## License
|
||||
|
||||
MIT License - See LICENSE file for details
|
||||
|
||||
## Repository
|
||||
|
||||
https://git.alwisp.com/jason/pnger
|
||||
|
||||
## Screenshots
|
||||
|
||||
### Light Mode
|
||||
Clean white interface with dark gold accents, perfect for inspecting dark images
|
||||
|
||||
### Dark Mode
|
||||
Sleek black interface with light gold accents, ideal for viewing light/transparent PNGs
|
||||
|
||||
### Live Preview
|
||||
Side-by-side comparison showing original and transformed image with file size analysis
|
||||
|
||||
### Smart Presets
|
||||
Quick-access buttons for common image optimization scenarios
|
||||
|
||||
### Keyboard Shortcuts
|
||||
Built-in help dialog showing all available shortcuts
|
||||
**License**: MIT
|
||||
**Repository**: [https://git.alwisp.com/jason/pnger](https://git.alwisp.com/jason/pnger)
|
||||
411
ROADMAP.md
411
ROADMAP.md
@@ -1,385 +1,62 @@
|
||||
# PNGer Feature Roadmap
|
||||
|
||||
PNGer is evolved through intentional sprints focusing on user experience, performance, and professional-grade features.
|
||||
|
||||
## Completed Features ✅
|
||||
|
||||
### Core Foundation
|
||||
- ✅ Basic image upload (file picker, drag & drop, clipboard paste)
|
||||
- ✅ Width/height resizing
|
||||
- ✅ Aspect ratio control (inside/cover)
|
||||
- ✅ Crop positioning (9 positions)
|
||||
- ✅ Format conversion (PNG, WebP, JPEG)
|
||||
- ✅ Quality adjustment (10-100%)
|
||||
- ✅ Direct download (no server storage)
|
||||
- ✅ Docker deployment
|
||||
- ✅ **Live Preview** - Real-time side-by-side comparison with file size analysis
|
||||
- ✅ **Dark/Light Mode** - Theme toggle for better PNG transparency inspection
|
||||
- ✅ **Modern UI** - Sleek design with smooth transitions and visual feedback
|
||||
### Sprint 0: Foundation (MVP)
|
||||
- ✅ **Core Repository Setup**: Project structure and build systems.
|
||||
- ✅ **Basic Image Operations**: Width/height resizing and quality adjustment.
|
||||
- ✅ **Format Support**: Conversion between PNG, WebP, and JPEG.
|
||||
- ✅ **Docker Deployment**: Multi-stage build for Unraid/Docker environments.
|
||||
- ✅ **Stateless Processing**: Direct download from memory; no server storage.
|
||||
|
||||
### Sprint 1: Enhanced UX (Completed March 2026)
|
||||
- ✅ **Drag & Drop Upload** - Drag images directly into the app
|
||||
- ✅ **Clipboard Paste** - Paste with Ctrl+V (Cmd+V on Mac)
|
||||
- ✅ **Smart Presets** - 8 quick-access presets:
|
||||
- Web Optimized (1920x1080, 80%, WebP)
|
||||
- Social Media (1080x1080, 85%, JPEG)
|
||||
- Profile Picture (400x400, 90%, PNG)
|
||||
- Email Friendly (800x600, 75%, JPEG)
|
||||
- Tiny Icon (64x64, 100%, PNG)
|
||||
- Retina 2x (2x dimensions, 90%)
|
||||
- Icon Small (256x256, 95%, PNG)
|
||||
- Icon Large (512x512, 95%, PNG)
|
||||
- ✅ **Keyboard Shortcuts** - Fast workflow:
|
||||
- Ctrl+V / Cmd+V: Paste image
|
||||
- Enter: Transform & Download
|
||||
- Ctrl+Enter / Cmd+Enter: Transform & Download (anywhere)
|
||||
- ?: Show shortcuts help
|
||||
- Esc: Close dialogs
|
||||
- ✅ **Enhanced Visual Feedback** - Better loading states and error messages
|
||||
- ✅ **Theme Contrast Fixes** - Improved text visibility in both themes
|
||||
### Sprint 1: Enhanced UX & Live Preview (March 2026)
|
||||
- ✅ **Live Preview**: Real-time side-by-side comparison with file size analysis.
|
||||
- ✅ **Modern Design System**: Dark/Light mode toggle with persistent storage.
|
||||
- ✅ **Drag & Drop Upload**: Intuitive drop zone with visual feedback.
|
||||
- ✅ **Clipboard Paste**: Paste images directly with `Ctrl+V`.
|
||||
- ✅ **Smart Presets**: 8 quick-access configurations for common use cases (Social Media, Web, Icons, etc.).
|
||||
- ✅ **Keyboard Shortcuts**: Fast workflow with `Enter`, `?`, and `Esc`.
|
||||
- ✅ **Performance Optimization**: Client-side Canvas rendering for instant feedback.
|
||||
|
||||
---
|
||||
|
||||
## Sprint 2: Batch Processing & Advanced Operations (Next Priority)
|
||||
**Focus**: Handle multiple files and add essential image operations
|
||||
**Effort**: ~12-15 hours | **Impact**: Very High | **Timeline**: Week 1-2
|
||||
## Future Roadmap 🚀
|
||||
|
||||
### 2.1 Batch Processing 🚀 **HIGH VALUE**
|
||||
**Why**: Process multiple images at once - massive time saver for bulk operations
|
||||
**Effort**: Medium (6-8 hours) | **Impact**: Very High
|
||||
### Sprint 2: Batch Processing & Advanced Operations
|
||||
**Focus**: Efficiency and essential power-user tools.
|
||||
- [ ] **Batch Processing**: Upload multiple images and process as a single ZIP.
|
||||
- [ ] **Basic Transformations**: Rotate (90/180/Custom), Flip (H/V), and Grayscale.
|
||||
- [ ] **Content-Aware Resize**: Smart cropping and aspect ratio enforcement.
|
||||
- [ ] **Auto-Optimize**: One-click "best quality/size" ratio finder.
|
||||
|
||||
- [ ] Upload multiple images (10+ files)
|
||||
- [ ] Apply same transformations to all
|
||||
- [ ] Individual preview for each image
|
||||
- [ ] Download as ZIP file
|
||||
- [ ] Progress indicator for batch operations
|
||||
- [ ] Cancel/remove individual files from queue
|
||||
- [ ] Batch rename with pattern (e.g., `image-{index}.png`)
|
||||
- [ ] Memory optimization for large batches
|
||||
### Sprint 3: Polish & Professional Tools
|
||||
**Focus**: Precision and customization.
|
||||
- [ ] **Custom Crop Tool**: Visual selector with aspect ratio lock.
|
||||
- [ ] **Watermarking**: Text and image-based branding.
|
||||
- [ ] **Image Filters**: Brightness, Contrast, Saturation, and Sharpen.
|
||||
- [ ] **Format Intelligence**: Suggestions based on image content (e.g., suggesting WebP for large images).
|
||||
|
||||
**Implementation Notes**:
|
||||
- Use JSZip library for ZIP creation
|
||||
- Process images in parallel (4-6 concurrent)
|
||||
- Track individual file progress
|
||||
- Client-side queue management
|
||||
|
||||
### 2.2 Essential Image Operations 🎨
|
||||
**Why**: Basic operations users expect
|
||||
**Effort**: Low-Medium (4-6 hours) | **Impact**: High
|
||||
|
||||
- [ ] **Rotation**: 90°, 180°, 270°, custom angle
|
||||
- [ ] **Flip**: Horizontal / Vertical
|
||||
- [ ] **Grayscale**: Quick B&W conversion
|
||||
- [ ] **Auto-rotate**: Based on EXIF orientation
|
||||
- [ ] **Trim**: Remove transparent/white borders automatically
|
||||
|
||||
**Technical**: All supported natively by Sharp library
|
||||
|
||||
### 2.3 Image Optimization ⚡ **HIGH VALUE**
|
||||
**Why**: One-click file size reduction without quality loss
|
||||
**Effort**: Low (2-3 hours) | **Impact**: Very High
|
||||
|
||||
- [ ] **Auto-Optimize** button - Smart quality/compression
|
||||
- [ ] **Lossless PNG** optimization (pngquant-style)
|
||||
- [ ] **Strip EXIF** metadata (privacy + file size)
|
||||
- [ ] **Progressive JPEG** encoding
|
||||
- [ ] **Compression comparison** - Show savings for each format
|
||||
- [ ] **Target file size** - Optimize to hit specific size (e.g., "under 1MB")
|
||||
|
||||
**Implementation**: Use Sharp's built-in optimization + custom quality detection
|
||||
### Sprint 4: Workflow & Automation
|
||||
**Focus**: Reusability and productivity.
|
||||
- [ ] **Custom Presets**: Save and export personal transformation pipelines.
|
||||
- [ ] **Processing History**: Recent files list (localStorage).
|
||||
- [ ] **Undo/Redo**: History for parameter adjustments.
|
||||
|
||||
---
|
||||
|
||||
## Sprint 3: User Experience Polish (High Utility)
|
||||
**Focus**: Make the app faster and more intuitive
|
||||
**Effort**: ~10-12 hours | **Impact**: High | **Timeline**: Week 3-4
|
||||
## Priority Matrix
|
||||
|
||||
### 3.1 Quick Access Features ⏱️
|
||||
**Why**: Speed up repetitive tasks
|
||||
**Effort**: Low (2-3 hours) | **Impact**: Medium-High
|
||||
|
||||
- [ ] **Copy to Clipboard** - Copy processed image directly
|
||||
- [ ] **Recent Files** - Quick access to last 10 processed images (localStorage)
|
||||
- [ ] **Undo/Redo** - Revert parameter changes
|
||||
- [ ] **Favorites** - Star frequently used presets
|
||||
- [ ] **Quick Settings** - Toggle common options in header
|
||||
|
||||
### 3.2 Image Analysis & Info 🔍 **HIGH VALUE**
|
||||
**Why**: Understand your images better
|
||||
**Effort**: Low (2-3 hours) | **Impact**: Medium-High
|
||||
|
||||
- [ ] **Metadata Display**: Dimensions, format, color space, DPI
|
||||
- [ ] **Color Palette Extraction**: Show dominant colors (5-8)
|
||||
- [ ] **Transparency Detection**: Warn if converting transparent to JPEG
|
||||
- [ ] **File Size Projection**: Estimate final size before download
|
||||
- [ ] **Aspect Ratio Calculator**: Calculate dimensions maintaining ratio
|
||||
- [ ] **Memory Usage**: Show current memory consumption
|
||||
|
||||
### 3.3 Format Intelligence 🧠
|
||||
**Why**: Help users choose the right format
|
||||
**Effort**: Low (2-3 hours) | **Impact**: Medium
|
||||
|
||||
- [ ] **Format Recommendations**: Suggest best format based on image
|
||||
- [ ] **Browser Support Detection**: Show WebP/AVIF compatibility
|
||||
- [ ] **Multi-Format Export**: Generate PNG + WebP + JPEG at once
|
||||
- [ ] **Responsive Image Sets**: Export multiple sizes for responsive design
|
||||
|
||||
### 3.4 UI/UX Improvements
|
||||
**Effort**: Low-Medium (3-4 hours) | **Impact**: Medium
|
||||
|
||||
- [ ] **Mobile Responsive Design**: Better mobile/tablet experience
|
||||
- [ ] **Toast Notifications**: Non-blocking feedback messages
|
||||
- [ ] **Drag to Reorder**: Reorder batch queue
|
||||
- [ ] **Compare Mode**: Slider to compare before/after
|
||||
- [ ] **Zoom Controls**: Zoom in on preview images
|
||||
- [ ] **Grid/List Toggle**: Switch preview layout
|
||||
| Feature | Impact | Effort | Priority |
|
||||
|---------|--------|--------|----------|
|
||||
| Batch Processing | Very High | Medium | 1 |
|
||||
| Auto-Optimize | High | Low | 1 |
|
||||
| Custom Crop | High | High | 2 |
|
||||
| Watermarking | Medium | Medium | 3 |
|
||||
|
||||
---
|
||||
|
||||
## Sprint 4: Advanced Features (Power Users)
|
||||
**Focus**: Professional-grade tools
|
||||
**Effort**: ~15-20 hours | **Impact**: Medium-High | **Timeline**: Week 5-6
|
||||
|
||||
### 4.1 Custom Crop Tool ✂️
|
||||
**Why**: Precise control over cropping
|
||||
**Effort**: High (8-10 hours) | **Impact**: Medium-High
|
||||
|
||||
- [ ] **Visual Crop Selector**: Interactive drag handles
|
||||
- [ ] **Aspect Ratio Lock**: 16:9, 4:3, 1:1, custom
|
||||
- [ ] **Freeform Cropping**: No ratio constraints
|
||||
- [ ] **Rule of Thirds Grid**: Photography composition guide
|
||||
- [ ] **Golden Ratio Grid**: Advanced composition
|
||||
- [ ] **Zoom for Precision**: Zoom in for pixel-perfect crops
|
||||
- [ ] **Crop Presets**: Social media sizes (Instagram, Facebook, etc.)
|
||||
|
||||
### 4.2 Filters & Effects 🌈
|
||||
**Why**: Quick image enhancements
|
||||
**Effort**: Medium (5-6 hours) | **Impact**: Medium
|
||||
|
||||
- [ ] **Brightness/Contrast**: Adjust exposure
|
||||
- [ ] **Saturation**: Boost or reduce colors
|
||||
- [ ] **Sharpness**: Enhance edges
|
||||
- [ ] **Blur**: Gaussian blur with radius control
|
||||
- [ ] **Tint**: Apply color overlay
|
||||
- [ ] **Vintage Filter**: Sepia tone
|
||||
- [ ] **Invert Colors**: Negative effect
|
||||
- [ ] **Filter Presets**: Quick apply common filters
|
||||
|
||||
### 4.3 Watermarking 📝
|
||||
**Why**: Protect and brand images
|
||||
**Effort**: Medium (4-5 hours) | **Impact**: Medium
|
||||
|
||||
- [ ] **Text Watermark**: Custom text with font selection
|
||||
- [ ] **Image Watermark**: Upload logo overlay
|
||||
- [ ] **Position Control**: 9 positions + custom X/Y
|
||||
- [ ] **Opacity**: Adjust transparency (0-100%)
|
||||
- [ ] **Repeat Pattern**: Tile watermark across image
|
||||
- [ ] **Batch Watermark**: Apply to all images in batch
|
||||
|
||||
---
|
||||
|
||||
## Sprint 5: Workflow & Automation (Productivity)
|
||||
**Focus**: Save time with reusable workflows
|
||||
**Effort**: ~8-10 hours | **Impact**: Medium | **Timeline**: Week 7+
|
||||
|
||||
### 5.1 Custom Presets & Templates
|
||||
**Why**: Reusable transformation pipelines
|
||||
**Effort**: Low-Medium (3-4 hours) | **Impact**: Medium
|
||||
|
||||
- [ ] **Save Custom Presets**: Create your own quick-access presets
|
||||
- [ ] **Preset Categories**: Organize by use case
|
||||
- [ ] **Import/Export Presets**: Share as JSON files
|
||||
- [ ] **Transformation Chains**: Combine multiple operations
|
||||
- [ ] **Preset Shortcuts**: Assign keyboard shortcuts (1-9 keys)
|
||||
- [ ] **Community Presets**: Gallery of user-submitted presets
|
||||
|
||||
### 5.2 History & Session Management
|
||||
**Why**: Track and repeat common tasks
|
||||
**Effort**: Low (2-3 hours) | **Impact**: Low-Medium
|
||||
|
||||
- [ ] **Processing History**: Last 50 transformations
|
||||
- [ ] **Quick Re-apply**: Reload previous settings
|
||||
- [ ] **Export History**: Save session as JSON
|
||||
- [ ] **Clear History**: Privacy button
|
||||
- [ ] **Session Restore**: Resume after browser close
|
||||
|
||||
---
|
||||
|
||||
## Future: Extended Format Support (Lower Priority)
|
||||
**Focus**: Support more file types
|
||||
**Effort**: Variable | **Impact**: Situational
|
||||
|
||||
### Additional Formats
|
||||
- [ ] **AVIF Support**: Next-gen image format
|
||||
- [ ] **TIFF Support**: Professional photography
|
||||
- [ ] **SVG to Raster**: Convert vector to bitmap
|
||||
- [ ] **ICO Generation**: Create favicons (16, 32, 48, 64, 128, 256px)
|
||||
- [ ] **PDF to Image**: Extract first page as image
|
||||
- [ ] **GIF Frame Extraction**: Convert animated GIF to frames
|
||||
- [ ] **HEIC/HEIF Support**: Apple photo format
|
||||
|
||||
---
|
||||
|
||||
## Enterprise & API (Optional)
|
||||
**Focus**: For hosted/managed deployments
|
||||
**Effort**: High | **Impact**: Situational
|
||||
|
||||
### API Access
|
||||
- [ ] Generate API keys in UI
|
||||
- [ ] Rate limiting per key
|
||||
- [ ] Usage statistics dashboard
|
||||
- [ ] Webhook support for async processing
|
||||
- [ ] OpenAPI/Swagger documentation
|
||||
- [ ] SDK libraries (Python, Node.js, PHP)
|
||||
|
||||
### User Accounts (Optional)
|
||||
- [ ] User authentication
|
||||
- [ ] Per-user quotas
|
||||
- [ ] Cloud storage integration (S3, MinIO)
|
||||
- [ ] Saved presets sync
|
||||
- [ ] Usage analytics
|
||||
- [ ] Team collaboration features
|
||||
|
||||
---
|
||||
|
||||
## New High-Value Ideas 💡
|
||||
|
||||
### Image Comparison Tool 🔍
|
||||
**Why**: A/B test different settings
|
||||
**Effort**: Low-Medium (3-4 hours) | **Impact**: Medium
|
||||
|
||||
- [ ] Side-by-side comparison of 2-4 versions
|
||||
- [ ] Slider reveal to show differences
|
||||
- [ ] Quality vs file size comparison matrix
|
||||
- [ ] Export comparison settings for sharing
|
||||
|
||||
### Background Removal ✨ **HIGH VALUE**
|
||||
**Why**: Remove backgrounds automatically
|
||||
**Effort**: Medium-High (6-8 hours) | **Impact**: Very High
|
||||
|
||||
- [ ] AI-powered background removal (using @imgly/background-removal or similar)
|
||||
- [ ] Replace with solid color
|
||||
- [ ] Replace with gradient
|
||||
- [ ] Replace with uploaded image
|
||||
- [ ] Transparency preservation
|
||||
- [ ] Edge refinement controls
|
||||
|
||||
### Smart Resize 🦾 **HIGH VALUE**
|
||||
**Why**: Content-aware resizing
|
||||
**Effort**: Medium (4-5 hours) | **Impact**: High
|
||||
|
||||
- [ ] **Content-aware scaling**: Preserve important areas
|
||||
- [ ] **Face detection**: Protect faces during crop
|
||||
- [ ] **Center of interest**: Auto-detect focal point
|
||||
- [ ] **Smart crop suggestions**: AI-suggested crop areas
|
||||
|
||||
### Color Adjustments 🎨
|
||||
**Why**: Fine-tune image colors
|
||||
**Effort**: Medium (4-5 hours) | **Impact**: Medium
|
||||
|
||||
- [ ] **Hue/Saturation/Lightness** sliders
|
||||
- [ ] **Color temperature** (warm/cool)
|
||||
- [ ] **Vibrance** (selective saturation)
|
||||
- [ ] **Color balance** (shadows, midtones, highlights)
|
||||
- [ ] **White balance correction**
|
||||
- [ ] **Color grading presets**
|
||||
|
||||
### Border & Frame Effects 🖼️
|
||||
**Why**: Add decorative elements
|
||||
**Effort**: Low-Medium (3-4 hours) | **Impact**: Low-Medium
|
||||
|
||||
- [ ] **Solid borders**: Color and thickness
|
||||
- [ ] **Padding**: Add transparent/colored space
|
||||
- [ ] **Rounded corners**: Border radius control
|
||||
- [ ] **Shadow effects**: Drop shadow with blur
|
||||
- [ ] **Frame templates**: Decorative frames
|
||||
- [ ] **Polaroid effect**: Instant camera style
|
||||
|
||||
### Image Merge & Collage 🖼️
|
||||
**Why**: Combine multiple images
|
||||
**Effort**: High (8-10 hours) | **Impact**: Medium
|
||||
|
||||
- [ ] **Side-by-side merge**: Horizontal/vertical
|
||||
- [ ] **Grid layout**: 2x2, 3x3, custom
|
||||
- [ ] **Collage templates**: Pre-designed layouts
|
||||
- [ ] **Spacing control**: Gap between images
|
||||
- [ ] **Background color**: For gaps
|
||||
|
||||
### Conditional Processing 🤖
|
||||
**Why**: Smart automation based on image properties
|
||||
**Effort**: Medium (4-5 hours) | **Impact**: Medium
|
||||
|
||||
- [ ] **Size-based rules**: Different settings for large vs small images
|
||||
- [ ] **Format detection**: Auto-convert based on transparency
|
||||
- [ ] **Quality ranges**: Set min/max quality constraints
|
||||
- [ ] **Dimension limits**: Enforce max width/height
|
||||
|
||||
---
|
||||
|
||||
## Technical Debt & Infrastructure
|
||||
|
||||
### Code Quality
|
||||
- [ ] Unit tests (backend) - Vitest/Jest
|
||||
- [ ] E2E tests (frontend) - Playwright
|
||||
- [ ] TypeScript strict mode
|
||||
- [ ] ESLint + Prettier configuration
|
||||
- [ ] CI/CD pipeline (GitHub Actions or Gitea Actions)
|
||||
- [ ] Code coverage reporting (>80%)
|
||||
- [ ] Automated dependency updates (Renovate)
|
||||
|
||||
### Performance
|
||||
- [ ] Image processing queue (for heavy operations)
|
||||
- [ ] Web Workers for client-side processing
|
||||
- [ ] Service Worker for offline support
|
||||
- [ ] Lazy loading for UI components
|
||||
- [ ] Memory leak detection and fixes
|
||||
- [ ] Performance monitoring (Lighthouse CI)
|
||||
|
||||
### Security
|
||||
- [ ] Rate limiting (prevent abuse)
|
||||
- [ ] File type validation (magic bytes check)
|
||||
- [ ] Max file size enforcement (configurable)
|
||||
- [ ] CORS configuration hardening
|
||||
- [ ] CSP headers
|
||||
- [ ] Input sanitization audit
|
||||
- [ ] Security headers (HSTS, X-Frame-Options)
|
||||
- [ ] Dependency vulnerability scanning
|
||||
|
||||
### Monitoring & Analytics
|
||||
- [ ] Prometheus metrics endpoint
|
||||
- [ ] Error tracking (Sentry integration)
|
||||
- [ ] Performance monitoring (Real User Monitoring)
|
||||
- [ ] Usage analytics (privacy-focused, self-hosted)
|
||||
- [ ] Health check dashboard
|
||||
|
||||
---
|
||||
|
||||
## Priority Matrix (Updated)
|
||||
|
||||
### Immediate Next Steps (Sprint 2)
|
||||
1. 🚀 **Batch Processing** - Highest utility, medium effort
|
||||
2. ⚡ **Auto-Optimize** - Quick win, very high value
|
||||
3. 🎨 **Basic Operations** - Essential features (rotate, flip, grayscale)
|
||||
|
||||
### High Value, Medium Effort (Sprint 3-4)
|
||||
4. 🔍 **Image Analysis** - Understand images better
|
||||
5. ✨ **Background Removal** - AI-powered, huge demand
|
||||
6. 🦾 **Smart Resize** - Content-aware scaling
|
||||
7. ✂️ **Custom Crop Tool** - Professional precision
|
||||
|
||||
### Polish & Productivity (Sprint 5+)
|
||||
8. ⏱️ **Quick Access Features** - Speed up workflow
|
||||
9. 📝 **Custom Presets** - Reusable workflows
|
||||
10. 🎨 **Filters & Effects** - Creative options
|
||||
|
||||
---
|
||||
|
||||
## Community Ideas
|
||||
|
||||
Have suggestions? Open an issue on the repository:
|
||||
- Feature requests
|
||||
- Bug reports
|
||||
- Use case discussions
|
||||
- Performance improvements
|
||||
|
||||
**Maintainer**: jason
|
||||
**Repository**: https://git.alwisp.com/jason/pnger
|
||||
**Last Updated**: March 8, 2026 (Sprint 1 Complete)
|
||||
**Project Maintainer**: jason
|
||||
**Repository**: [jason/pnger](https://git.alwisp.com/jason/pnger)
|
||||
**Last Strategy Sync**: March 12, 2026
|
||||
@@ -1,239 +0,0 @@
|
||||
# Sprint 1 Changes - UX Enhancements
|
||||
|
||||
**Branch**: `feature/sprint1-dragdrop-presets-shortcuts`
|
||||
**Date**: 2026-03-08
|
||||
**Status**: Ready for Testing
|
||||
|
||||
## 🎯 Overview
|
||||
|
||||
This sprint focuses on making PNGer significantly more intuitive and powerful with three major feature additions plus a critical bug fix.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Bug Fix: Preview File Size Calculation
|
||||
|
||||
### Problem
|
||||
- Preview file size was not calculating correctly
|
||||
- Size didn't update when adjusting quality slider
|
||||
- Format changes weren't reflected in estimated size
|
||||
|
||||
### Solution
|
||||
- Fixed base64 size estimation algorithm in `preview.ts`
|
||||
- Properly map format to MIME types (png, jpeg, webp)
|
||||
- Quality parameter now correctly applied to JPEG and WebP
|
||||
- Improved padding calculation for accurate byte estimation
|
||||
|
||||
### Files Changed
|
||||
- `frontend/src/lib/preview.ts`
|
||||
|
||||
---
|
||||
|
||||
## 🆕 Feature 1: Drag & Drop Upload
|
||||
|
||||
### What's New
|
||||
- **Drag & drop zone** with visual feedback
|
||||
- Hover state shows accent color
|
||||
- Dragging over triggers highlight animation
|
||||
- **Clipboard paste support** (Ctrl+V / Cmd+V)
|
||||
- File info displayed after upload (name + size)
|
||||
- One-click "Clear File" button
|
||||
|
||||
### User Benefits
|
||||
- No more hunting for file picker
|
||||
- Instant image upload from screenshots (paste)
|
||||
- Modern, expected behavior
|
||||
- Faster workflow
|
||||
|
||||
### Technical Implementation
|
||||
- `dragover`, `dragleave`, `drop` event handlers
|
||||
- Clipboard paste event listener
|
||||
- File type validation
|
||||
- Visual state management with `isDragging` flag
|
||||
|
||||
### Files Changed
|
||||
- `frontend/src/App.svelte` (drag handlers + paste support)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Feature 2: Smart Presets
|
||||
|
||||
### What's New
|
||||
8 built-in presets for common use cases:
|
||||
|
||||
1. **🖼️ Web Thumbnail** - 300x300, WebP, 75% quality, cover
|
||||
2. **📱 Social Media** - 1200x630 Open Graph, PNG, 85%, cover
|
||||
3. **👤 Profile Picture** - 400x400 square, PNG, 85%, cover
|
||||
4. **📧 Email Friendly** - 600px wide, JPEG, 70%, optimized
|
||||
5. **⭐ HD Quality** - 1920px wide, PNG, 90%, high-res
|
||||
6. **🔍 Retina @2x** - Doubles current dimensions, PNG, 85%
|
||||
7. **🔷 Icon Small** - 64x64, PNG, 100%, cover
|
||||
8. **🔶 Icon Large** - 256x256, PNG, 100%, cover
|
||||
|
||||
### User Benefits
|
||||
- One-click transformations for common tasks
|
||||
- No need to remember optimal settings
|
||||
- Saves time on repetitive operations
|
||||
- Perfect for non-technical users
|
||||
|
||||
### Technical Implementation
|
||||
- New `presets.ts` module with preset definitions
|
||||
- `applyPreset()` function with special Retina @2x logic
|
||||
- 4-column grid layout
|
||||
- Hover effects with elevation
|
||||
- Icon + name display
|
||||
|
||||
### Files Changed
|
||||
- `frontend/src/lib/presets.ts` (new file)
|
||||
- `frontend/src/App.svelte` (preset UI + selection logic)
|
||||
|
||||
---
|
||||
|
||||
## ⌨️ Feature 3: Keyboard Shortcuts
|
||||
|
||||
### What's New
|
||||
|
||||
**Shortcuts Available:**
|
||||
- `Ctrl+V` / `Cmd+V` - Paste image from clipboard
|
||||
- `Enter` - Transform & Download (when not in input)
|
||||
- `Ctrl+Enter` / `Cmd+Enter` - Transform & Download (anywhere)
|
||||
- `?` - Show/hide shortcuts help
|
||||
- `Esc` - Close shortcuts dialog
|
||||
|
||||
**Shortcuts Help Modal:**
|
||||
- Clean, centered modal
|
||||
- Keyboard key styling (`<kbd>`)
|
||||
- Click outside to close
|
||||
- Fade-in animation
|
||||
|
||||
### User Benefits
|
||||
- Power users can work without mouse
|
||||
- Faster workflow for repetitive tasks
|
||||
- Discoverable via `?` key
|
||||
- Professional touch
|
||||
|
||||
### Technical Implementation
|
||||
- Document-level `keydown` event listener
|
||||
- Active element detection (skip Enter if input focused)
|
||||
- Modal overlay with portal pattern
|
||||
- `onMount` setup and cleanup
|
||||
|
||||
### Files Changed
|
||||
- `frontend/src/App.svelte` (keyboard handlers + modal)
|
||||
|
||||
---
|
||||
|
||||
## 🎨 UI/UX Improvements
|
||||
|
||||
### Additional Polish
|
||||
- **Shortcuts button** in header (⌨️ icon)
|
||||
- **Hint text** under download button: "Press Enter to download"
|
||||
- **Drop zone improvements**: better empty state messaging
|
||||
- **Preset icons**: visual indicators for each preset type
|
||||
- **Modal styling**: professional overlay with backdrop blur
|
||||
- **Responsive kbd tags**: monospace font with shadow effect
|
||||
|
||||
---
|
||||
|
||||
## 📊 Testing Checklist
|
||||
|
||||
### Bug Fix Validation
|
||||
- [ ] Upload image, adjust quality slider - size updates in real-time
|
||||
- [ ] Change format PNG → JPEG → WebP - size reflects format
|
||||
- [ ] Compare preview size with actual downloaded file size
|
||||
|
||||
### Drag & Drop
|
||||
- [ ] Drag image file onto drop zone - uploads successfully
|
||||
- [ ] Drag non-image file - shows error
|
||||
- [ ] Hover during drag - shows visual feedback
|
||||
- [ ] Drop outside zone - no action
|
||||
|
||||
### Clipboard Paste
|
||||
- [ ] Take screenshot, press Ctrl+V - pastes image
|
||||
- [ ] Copy image from browser, paste - works
|
||||
- [ ] Paste non-image - no error
|
||||
|
||||
### Presets
|
||||
- [ ] Click "Web Thumbnail" - sets 300x300, WebP, 75%, cover
|
||||
- [ ] Click "Social Media" - sets 1200x630, PNG, 85%, cover
|
||||
- [ ] Click "Retina @2x" with 500x500 image - doubles to 1000x1000
|
||||
- [ ] All 8 presets apply correctly
|
||||
|
||||
### Keyboard Shortcuts
|
||||
- [ ] Press `?` - shows shortcuts modal
|
||||
- [ ] Press `Esc` in modal - closes modal
|
||||
- [ ] Press `Enter` with image loaded - downloads
|
||||
- [ ] Press `Enter` while typing in input - types Enter (doesn't download)
|
||||
- [ ] Press `Ctrl+Enter` anywhere - downloads
|
||||
- [ ] Press `Ctrl+V` - pastes from clipboard
|
||||
|
||||
### Cross-Browser
|
||||
- [ ] Chrome/Edge - all features work
|
||||
- [ ] Firefox - all features work
|
||||
- [ ] Safari - all features work (Cmd key instead of Ctrl)
|
||||
|
||||
---
|
||||
|
||||
## 📝 Files Changed Summary
|
||||
|
||||
### New Files
|
||||
1. `frontend/src/lib/presets.ts` - Preset definitions and apply logic
|
||||
2. `SPRINT1_CHANGES.md` - This document
|
||||
|
||||
### Modified Files
|
||||
1. `frontend/src/lib/preview.ts` - Fixed size calculation bug
|
||||
2. `frontend/src/App.svelte` - Major update with all 3 features
|
||||
3. `ROADMAP.md` - Marked Phase 1.1 complete, added sprint plan
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Performance Notes
|
||||
|
||||
- **No performance impact**: All features are client-side
|
||||
- **Preview debounce**: Still 300ms, works great with presets
|
||||
- **Modal render**: Only renders when `showShortcuts = true`
|
||||
- **Drag handlers**: Lightweight event listeners
|
||||
- **Preset selection**: Instant application (<10ms)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Development Notes
|
||||
|
||||
### Code Quality
|
||||
- TypeScript strict types maintained
|
||||
- Svelte reactivity patterns followed
|
||||
- Event cleanup in `onMount` return
|
||||
- CSS animations for smooth UX
|
||||
- Semantic HTML structure
|
||||
|
||||
### Future Enhancements
|
||||
- [ ] Multi-file batch processing (use drag & drop foundation)
|
||||
- [ ] Custom preset saving (localStorage)
|
||||
- [ ] Preset import/export
|
||||
- [ ] More keyboard shortcuts (arrow keys for presets?)
|
||||
|
||||
---
|
||||
|
||||
## ✅ Ready for Merge
|
||||
|
||||
This branch is ready to merge to `main` once testing is complete.
|
||||
|
||||
**Merge Command:**
|
||||
```bash
|
||||
git checkout main
|
||||
git merge feature/sprint1-dragdrop-presets-shortcuts
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**Deployment:**
|
||||
No backend changes - just rebuild frontend Docker image.
|
||||
|
||||
---
|
||||
|
||||
## 💬 Next Sprint Suggestions
|
||||
|
||||
After this sprint, consider:
|
||||
1. **Sprint 2A**: Batch processing (multi-file upload)
|
||||
2. **Sprint 2B**: Additional transformations (rotate, flip, filters)
|
||||
3. **Sprint 2C**: Auto-optimize feature
|
||||
|
||||
See `ROADMAP.md` for full feature planning.
|
||||
@@ -1,413 +0,0 @@
|
||||
# UI Upgrade - Dark Mode & Live Preview
|
||||
|
||||
## Overview
|
||||
|
||||
This branch introduces a complete UI overhaul with modern design, dark/light mode theming, and real-time live preview functionality.
|
||||
|
||||
## What's New
|
||||
|
||||
### 🎨 Modern Design System
|
||||
|
||||
**Color Themes:**
|
||||
- **Light Mode**: Clean white background with dark gold (#b8860b) accents
|
||||
- **Dark Mode**: Deep black (#0a0a0a) background with light gold (#daa520) accents
|
||||
- Smooth transitions between themes
|
||||
- System preference detection on first load
|
||||
|
||||
**Design Tokens:**
|
||||
- CSS custom properties for consistent spacing, colors, shadows
|
||||
- Responsive typography scale
|
||||
- Smooth animations and transitions
|
||||
- Modern card-based layout
|
||||
- Professional shadows and borders
|
||||
|
||||
### 🌙 Dark/Light Mode Toggle
|
||||
|
||||
- One-click theme switching
|
||||
- Persistent preference (localStorage)
|
||||
- Smooth color transitions
|
||||
- Icon indicators (☀️/🌙)
|
||||
- Perfect for comparing PNG transparency on different backgrounds
|
||||
|
||||
### ⚡ Live Preview
|
||||
|
||||
**Instant Visual Feedback:**
|
||||
- Real-time preview updates as you adjust settings
|
||||
- Side-by-side comparison (original vs transformed)
|
||||
- No server round-trip required (client-side Canvas API)
|
||||
- Debounced updates (300ms) for performance
|
||||
|
||||
**Preview Features:**
|
||||
- Shows exact transformations before download
|
||||
- File size comparison
|
||||
- Savings/increase indicator with percentage
|
||||
- Color-coded feedback (green = savings, yellow = increase)
|
||||
- Maintains aspect ratio and crop preview
|
||||
|
||||
### 📊 Enhanced Information Display
|
||||
|
||||
- Original file name and size shown
|
||||
- Preview file size estimation
|
||||
- Savings calculation with visual indicators
|
||||
- Quality slider with percentage display
|
||||
- Clear visual separation of controls and preview
|
||||
|
||||
### 💅 Visual Improvements
|
||||
|
||||
**Layout:**
|
||||
- Two-column grid layout (controls | preview)
|
||||
- Card-based design with subtle shadows
|
||||
- Proper spacing and visual hierarchy
|
||||
- Responsive design (mobile-friendly)
|
||||
|
||||
**Interactions:**
|
||||
- Smooth hover effects on buttons
|
||||
- Focus states with accent color
|
||||
- Loading spinners for processing states
|
||||
- Fade-in animations
|
||||
- Button transforms on hover
|
||||
|
||||
**Typography:**
|
||||
- System font stack (native look & feel)
|
||||
- Proper heading hierarchy
|
||||
- Readable line heights
|
||||
- Color-coded text (primary, secondary, accent)
|
||||
|
||||
## Files Modified
|
||||
|
||||
### New Files
|
||||
|
||||
1. **`frontend/src/lib/preview.ts`**
|
||||
- Client-side preview generation using Canvas API
|
||||
- Image transformation calculations
|
||||
- File size estimation
|
||||
- Utility functions (debounce, format bytes, calculate savings)
|
||||
|
||||
2. **`frontend/src/lib/theme.ts`**
|
||||
- Svelte store for theme management
|
||||
- localStorage persistence
|
||||
- System preference detection
|
||||
- Theme toggle functionality
|
||||
|
||||
### Updated Files
|
||||
|
||||
3. **`frontend/src/app.css`**
|
||||
- Complete design system rewrite
|
||||
- CSS custom properties for theming
|
||||
- Dark mode support via `[data-theme="dark"]`
|
||||
- Modern component styles (buttons, inputs, cards)
|
||||
- Utility classes for layout
|
||||
- Responsive breakpoints
|
||||
- Custom scrollbar styling
|
||||
- Animation keyframes
|
||||
|
||||
4. **`frontend/src/App.svelte`**
|
||||
- Complete UI restructuring
|
||||
- Two-column layout with grid
|
||||
- Live preview integration
|
||||
- Theme toggle button
|
||||
- Enhanced file upload UI
|
||||
- Clear file button
|
||||
- Improved error handling display
|
||||
- Loading states with spinners
|
||||
- Side-by-side image comparison
|
||||
- Savings indicator card
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Preview Implementation
|
||||
|
||||
**How it works:**
|
||||
1. User uploads image
|
||||
2. Canvas API loads image into memory
|
||||
3. Transformations applied client-side:
|
||||
- Resize calculations (aspect ratio aware)
|
||||
- Crop positioning (9 positions supported)
|
||||
- Quality adjustment via canvas.toDataURL()
|
||||
4. Preview updates on parameter change (debounced)
|
||||
5. File size estimated from base64 data URL
|
||||
|
||||
**Performance:**
|
||||
- Debounced at 300ms to avoid excessive redraws
|
||||
- Canvas operations run on main thread (future: Web Worker)
|
||||
- Preview max size limited by browser memory
|
||||
- No server load for preview generation
|
||||
|
||||
### Theme System
|
||||
|
||||
**Storage:**
|
||||
```typescript
|
||||
localStorage.setItem('theme', 'dark' | 'light')
|
||||
```
|
||||
|
||||
**Application:**
|
||||
```html
|
||||
<html data-theme="dark">
|
||||
<!-- CSS custom properties change based on data-theme -->
|
||||
</html>
|
||||
```
|
||||
|
||||
**CSS Variables:**
|
||||
```css
|
||||
:root { --color-accent: #b8860b; } /* Light mode */
|
||||
[data-theme="dark"] { --color-accent: #daa520; } /* Dark mode */
|
||||
```
|
||||
|
||||
### Design Tokens
|
||||
|
||||
**Spacing Scale:**
|
||||
- xs: 0.25rem (4px)
|
||||
- sm: 0.5rem (8px)
|
||||
- md: 1rem (16px)
|
||||
- lg: 1.5rem (24px)
|
||||
- xl: 2rem (32px)
|
||||
- 2xl: 3rem (48px)
|
||||
|
||||
**Color Palette:**
|
||||
|
||||
| Light Mode | Dark Mode | Purpose |
|
||||
|------------|-----------|----------|
|
||||
| #ffffff | #0a0a0a | Primary BG |
|
||||
| #f8f9fa | #1a1a1a | Secondary BG |
|
||||
| #e9ecef | #2a2a2a | Tertiary BG |
|
||||
| #b8860b | #daa520 | Accent (Gold) |
|
||||
| #1a1a1a | #e9ecef | Text Primary |
|
||||
| #6c757d | #adb5bd | Text Secondary |
|
||||
|
||||
**Shadows:**
|
||||
- sm: Subtle card elevation
|
||||
- md: Button hover states
|
||||
- lg: Modal/dropdown shadows
|
||||
- xl: Maximum elevation
|
||||
|
||||
## User Experience Improvements
|
||||
|
||||
### Before vs After
|
||||
|
||||
**Before:**
|
||||
- Static form with no feedback
|
||||
- Download to see results
|
||||
- Trial and error workflow
|
||||
- Basic styling
|
||||
- No theme options
|
||||
|
||||
**After:**
|
||||
- ✅ Real-time preview
|
||||
- ✅ See before download
|
||||
- ✅ Immediate feedback loop
|
||||
- ✅ Modern, professional design
|
||||
- ✅ Dark/light mode for different PNGs
|
||||
- ✅ File size visibility
|
||||
- ✅ Savings indicator
|
||||
|
||||
### Use Cases Enhanced
|
||||
|
||||
1. **Comparing Transparency**
|
||||
- Toggle dark/light mode to see PNG transparency
|
||||
- Useful for logos, icons with transparency
|
||||
|
||||
2. **Optimizing File Size**
|
||||
- See file size impact immediately
|
||||
- Adjust quality until size is acceptable
|
||||
- Green indicator shows successful optimization
|
||||
|
||||
3. **Precise Cropping**
|
||||
- See crop position in real-time
|
||||
- Try all 9 positions visually
|
||||
- No guesswork needed
|
||||
|
||||
4. **Format Comparison**
|
||||
- Compare PNG vs WebP vs JPEG quality
|
||||
- See size differences instantly
|
||||
- Make informed format choice
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
**Tested On:**
|
||||
- Chrome 90+
|
||||
- Firefox 88+
|
||||
- Safari 14+
|
||||
- Edge 90+
|
||||
|
||||
**Requirements:**
|
||||
- Canvas API support
|
||||
- CSS Custom Properties
|
||||
- localStorage
|
||||
- ES6 modules
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
**Preview Generation:**
|
||||
- Small images (< 1MB): ~50-100ms
|
||||
- Medium images (1-5MB): ~200-400ms
|
||||
- Large images (5-10MB): ~500ms-1s
|
||||
|
||||
**Memory Usage:**
|
||||
- Canvas limited by browser (typically 512MB max)
|
||||
- Preview auto-cleanup on file change
|
||||
- No memory leaks detected
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### Planned (Not in This Branch)
|
||||
|
||||
- [ ] Slider comparison (drag to reveal differences)
|
||||
- [ ] Zoom on preview for detail inspection
|
||||
- [ ] Web Worker for preview generation
|
||||
- [ ] Server-side preview option (Sharp accuracy)
|
||||
- [ ] Multiple preview sizes simultaneously
|
||||
- [ ] Drag & drop file upload
|
||||
- [ ] Batch preview mode
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Manual Testing
|
||||
|
||||
- [x] Upload PNG image
|
||||
- [x] Upload JPEG image
|
||||
- [x] Upload WebP image
|
||||
- [x] Adjust width only
|
||||
- [x] Adjust height only
|
||||
- [x] Adjust both dimensions
|
||||
- [x] Change quality slider
|
||||
- [x] Switch between formats
|
||||
- [x] Toggle fit mode (inside/cover)
|
||||
- [x] Test all 9 crop positions
|
||||
- [x] Toggle dark/light mode
|
||||
- [x] Verify theme persistence (refresh page)
|
||||
- [x] Clear file and re-upload
|
||||
- [x] Download transformed image
|
||||
- [x] Compare downloaded vs preview
|
||||
- [x] Test on mobile (responsive)
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- [ ] Very large image (> 10MB)
|
||||
- [ ] Very small image (< 10KB)
|
||||
- [ ] Square images
|
||||
- [ ] Panoramic images (extreme aspect ratios)
|
||||
- [ ] Images with transparency
|
||||
- [ ] Animated GIFs (should show first frame)
|
||||
|
||||
### Performance
|
||||
|
||||
- [ ] Preview updates < 500ms
|
||||
- [ ] No UI blocking during preview
|
||||
- [ ] Smooth theme transitions
|
||||
- [ ] No console errors
|
||||
- [ ] Memory cleanup verified
|
||||
|
||||
## Deployment Notes
|
||||
|
||||
### Build Requirements
|
||||
|
||||
- No new dependencies added
|
||||
- Uses existing Svelte + Vite setup
|
||||
- Compatible with current Docker build
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- None - fully backward compatible
|
||||
- API unchanged
|
||||
- Old URL parameters still work
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- No new env vars required
|
||||
- Theme stored client-side only
|
||||
|
||||
## Usage Guide
|
||||
|
||||
### For End Users
|
||||
|
||||
1. **Upload Image**: Click "Select Image" or use file picker
|
||||
2. **Adjust Settings**: Use controls on the left
|
||||
3. **Watch Preview**: See changes in real-time on the right
|
||||
4. **Toggle Theme**: Click sun/moon button for dark/light mode
|
||||
5. **Check Savings**: Green box shows file size reduction
|
||||
6. **Download**: Click "Transform & Download" when satisfied
|
||||
|
||||
### For Developers
|
||||
|
||||
**Adding a New Control:**
|
||||
```svelte
|
||||
<script>
|
||||
let newParam = defaultValue;
|
||||
$: if (file) updatePreview(); // Auto-trigger on change
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<label>New Parameter</label>
|
||||
<input bind:value={newParam} />
|
||||
</div>
|
||||
```
|
||||
|
||||
**Extending Theme:**
|
||||
```css
|
||||
/* In app.css */
|
||||
:root {
|
||||
--color-new-token: #value;
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--color-new-token: #dark-value;
|
||||
}
|
||||
```
|
||||
|
||||
## Screenshots (Conceptual)
|
||||
|
||||
### Light Mode
|
||||
```
|
||||
┌───────────────────────────────────────┐
|
||||
│ PNGer 🌙 Dark │
|
||||
│ Modern PNG Editor & Resizer │
|
||||
├──────────────────┬────────────────────┤
|
||||
│ Upload & Transform │ Live Preview │
|
||||
│ │ │
|
||||
│ [File picker] │ [Original] [Prev] │
|
||||
│ Width: [ ] │ │
|
||||
│ Height: [ ] │ ↓ 450KB saved │
|
||||
│ Quality: 80% │ │
|
||||
│ Format: PNG │ │
|
||||
│ [Download Button] │ │
|
||||
└──────────────────┴────────────────────┘
|
||||
```
|
||||
|
||||
### Dark Mode
|
||||
```
|
||||
┌───────────────────────────────────────┐
|
||||
│ ✨PNGer✨ ☀️ Light │
|
||||
│ Modern PNG Editor & Resizer │
|
||||
├──────────────────┬────────────────────┤
|
||||
│ 💻Upload & Transform│ 🖼️Live Preview │
|
||||
│ (Black BG) │ (Black BG) │
|
||||
│ Gold accents │ Gold borders │
|
||||
└──────────────────┴────────────────────┘
|
||||
```
|
||||
|
||||
## Merge Checklist
|
||||
|
||||
- [x] All new files created
|
||||
- [x] All existing files updated
|
||||
- [x] No console errors
|
||||
- [x] Dark mode works
|
||||
- [x] Light mode works
|
||||
- [x] Theme persists across refreshes
|
||||
- [x] Preview generates correctly
|
||||
- [x] File size calculations accurate
|
||||
- [x] Responsive design tested
|
||||
- [ ] Ready to merge to main
|
||||
|
||||
---
|
||||
|
||||
**Branch**: `feature/ui-upgrade-dark-mode-preview`
|
||||
**Created**: 2026-03-08
|
||||
**Status**: ✅ Ready for Testing
|
||||
**Merge Target**: `main`
|
||||
|
||||
**Next Steps**:
|
||||
1. Build and test locally
|
||||
2. Deploy to staging
|
||||
3. User acceptance testing
|
||||
4. Merge to main
|
||||
5. Deploy to production
|
||||
@@ -1,366 +0,0 @@
|
||||
# Live Preview Implementation Guide
|
||||
|
||||
## Overview
|
||||
|
||||
Live Preview is the #1 priority feature for PNGer. This guide outlines the implementation approach.
|
||||
|
||||
## Goals
|
||||
|
||||
1. **Instant Feedback**: Show preview within 100ms of parameter change
|
||||
2. **Accurate Rendering**: Match final output as closely as possible
|
||||
3. **Performance**: Don't block UI, handle large images efficiently
|
||||
4. **Progressive**: Show low-quality preview immediately, high-quality after
|
||||
|
||||
## Architecture
|
||||
|
||||
### Approach: Hybrid Client + Server Preview
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Upload │
|
||||
│ Image │
|
||||
└──────┬──────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────────────────────┐
|
||||
│ Client-Side Preview (Canvas) │ <-- Instant (< 100ms)
|
||||
│ - Fast, approximate rendering │
|
||||
│ - Uses browser native resize │
|
||||
│ - Good for basic operations │
|
||||
└─────────┬───────────────────────┘
|
||||
│
|
||||
v
|
||||
┌─────────────────────────────────┐
|
||||
│ Server Preview API (Optional) │ <-- Accurate (500ms-2s)
|
||||
│ - Uses Sharp (same as export) │
|
||||
│ - Exact output representation │
|
||||
│ - Debounced to avoid spam │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Phase 1: Client-Side Preview (Quick Win)
|
||||
|
||||
**Files to Modify:**
|
||||
- `frontend/src/App.svelte`
|
||||
- `frontend/src/lib/preview.ts` (new)
|
||||
|
||||
**Key Features:**
|
||||
1. Canvas-based image rendering
|
||||
2. Debounced updates (300ms after parameter change)
|
||||
3. Show original and preview side-by-side
|
||||
4. Display file size estimate
|
||||
|
||||
**Code Skeleton:**
|
||||
|
||||
```typescript
|
||||
// frontend/src/lib/preview.ts
|
||||
export async function generateClientPreview(
|
||||
file: File,
|
||||
options: TransformOptions
|
||||
): Promise<string> {
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d')!
|
||||
|
||||
img.onload = () => {
|
||||
// Calculate dimensions
|
||||
const { width, height } = calculateDimensions(img, options);
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
// Apply transforms
|
||||
if (options.fit === 'cover') {
|
||||
drawCover(ctx, img, width, height, options.position);
|
||||
} else {
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
}
|
||||
|
||||
// Apply filters (grayscale, blur, etc.)
|
||||
applyFilters(ctx, options);
|
||||
|
||||
// Convert to data URL
|
||||
const quality = options.quality / 100;
|
||||
const dataUrl = canvas.toDataURL(`image/${options.format}`, quality);
|
||||
resolve(dataUrl);
|
||||
};
|
||||
|
||||
img.src = URL.createObjectURL(file);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**UI Updates:**
|
||||
|
||||
```svelte
|
||||
<!-- App.svelte additions -->
|
||||
<script lang="ts">
|
||||
import { generateClientPreview } from './lib/preview';
|
||||
import { debounce } from './lib/utils';
|
||||
|
||||
let previewUrl: string | null = null;
|
||||
let originalSize: number = 0;
|
||||
let previewSize: number = 0;
|
||||
|
||||
// Debounced preview generation
|
||||
const updatePreview = debounce(async () => {
|
||||
if (!file) return;
|
||||
previewUrl = await generateClientPreview(file, {
|
||||
width, height, quality, format, fit, position
|
||||
});
|
||||
// Calculate sizes
|
||||
originalSize = file.size;
|
||||
previewSize = estimateSize(previewUrl);
|
||||
}, 300);
|
||||
|
||||
// Call on any parameter change
|
||||
$: if (file) updatePreview();
|
||||
</script>
|
||||
|
||||
<!-- Preview Section -->
|
||||
{#if file && previewUrl}
|
||||
<div class="preview-container">
|
||||
<div class="image-comparison">
|
||||
<div class="original">
|
||||
<h3>Original</h3>
|
||||
<img src={URL.createObjectURL(file)} alt="Original" />
|
||||
<p>{formatFileSize(originalSize)}</p>
|
||||
</div>
|
||||
<div class="preview">
|
||||
<h3>Preview</h3>
|
||||
<img src={previewUrl} alt="Preview" />
|
||||
<p>{formatFileSize(previewSize)}</p>
|
||||
<p class="savings">
|
||||
{calculateSavings(originalSize, previewSize)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
```
|
||||
|
||||
### Phase 2: Server Preview API (Accurate)
|
||||
|
||||
**Files to Modify:**
|
||||
- `backend/src/routes/image.ts`
|
||||
|
||||
**New Endpoint:**
|
||||
|
||||
```typescript
|
||||
// POST /api/preview (returns base64 or temp URL)
|
||||
router.post(
|
||||
"/preview",
|
||||
upload.single("file"),
|
||||
async (req, res): Promise<void> => {
|
||||
// Same processing as /transform
|
||||
// But return as base64 data URL or temp storage URL
|
||||
// Max preview size: 1200px (for performance)
|
||||
|
||||
const previewBuffer = await image.toBuffer();
|
||||
const base64 = previewBuffer.toString('base64');
|
||||
|
||||
res.json({
|
||||
preview: `data:image/${format};base64,${base64}`,
|
||||
size: previewBuffer.length,
|
||||
dimensions: { width: metadata.width, height: metadata.height }
|
||||
});
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Exact rendering (uses Sharp like final output)
|
||||
- Shows actual file size
|
||||
- Handles complex operations client can't do
|
||||
|
||||
**Trade-offs:**
|
||||
- Slower (network round-trip)
|
||||
- Server load (mitigate with rate limiting)
|
||||
|
||||
### Phase 3: Progressive Loading
|
||||
|
||||
**Enhancement**: Show low-quality preview first, then high-quality
|
||||
|
||||
```typescript
|
||||
// Generate two previews:
|
||||
// 1. Immediate low-res (client-side, 200px max)
|
||||
// 2. Delayed high-res (server-side, full resolution)
|
||||
|
||||
async function generateProgressivePreview() {
|
||||
// Step 1: Fast low-res
|
||||
const lowRes = await generateClientPreview(file, {
|
||||
...options,
|
||||
width: Math.min(options.width || 200, 200),
|
||||
height: Math.min(options.height || 200, 200)
|
||||
});
|
||||
previewUrl = lowRes; // Show immediately
|
||||
|
||||
// Step 2: High-res from server (debounced)
|
||||
const highRes = await fetchServerPreview(file, options);
|
||||
previewUrl = highRes; // Replace when ready
|
||||
}
|
||||
```
|
||||
|
||||
## File Size Estimation
|
||||
|
||||
```typescript
|
||||
function estimateSize(dataUrl: string): number {
|
||||
// Base64 data URL size (approximate)
|
||||
const base64Length = dataUrl.split(',')[1].length;
|
||||
return Math.ceil((base64Length * 3) / 4); // Convert base64 to bytes
|
||||
}
|
||||
|
||||
function formatFileSize(bytes: number): string {
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
||||
}
|
||||
|
||||
function calculateSavings(original: number, preview: number): string {
|
||||
const diff = original - preview;
|
||||
const percent = ((diff / original) * 100).toFixed(1);
|
||||
if (diff > 0) return `↓ ${formatFileSize(diff)} saved (${percent}%)`;
|
||||
if (diff < 0) return `↑ ${formatFileSize(-diff)} larger (${Math.abs(Number(percent))}%)`;
|
||||
return 'Same size';
|
||||
}
|
||||
```
|
||||
|
||||
## UI/UX Considerations
|
||||
|
||||
### Layout Options
|
||||
|
||||
**Option A: Side-by-Side**
|
||||
```
|
||||
┌──────────────┬──────────────┐
|
||||
│ Original │ Preview │
|
||||
│ │ │
|
||||
│ [Image] │ [Image] │
|
||||
│ 2.4 MB │ 450 KB │
|
||||
│ 1920x1080 │ 800x600 │
|
||||
└──────────────┴──────────────┘
|
||||
```
|
||||
|
||||
**Option B: Slider Compare**
|
||||
```
|
||||
┌────────────────────────────┐
|
||||
│ [<──── Slider ────>] │
|
||||
│ Original │ Preview │
|
||||
│ │ │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
**Option C: Tabs**
|
||||
```
|
||||
┌─ Original ─┬─ Preview ─────┐
|
||||
│ │
|
||||
│ [Image] │
|
||||
│ │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
**Recommendation**: Start with Option A (simplest), add Option B later for detail comparison.
|
||||
|
||||
## Performance Optimizations
|
||||
|
||||
### 1. Debouncing
|
||||
```typescript
|
||||
function debounce<T extends (...args: any[]) => any>(
|
||||
func: T,
|
||||
wait: number
|
||||
): (...args: Parameters<T>) => void {
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
return (...args: Parameters<T>) => {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func(...args), wait);
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Image Downsampling
|
||||
- Preview max size: 1200px (retina displays)
|
||||
- Original size only for final download
|
||||
- Reduces memory usage and processing time
|
||||
|
||||
### 3. Worker Thread (Advanced)
|
||||
- Offload canvas operations to Web Worker
|
||||
- Keeps UI responsive during processing
|
||||
|
||||
```typescript
|
||||
// preview.worker.ts
|
||||
self.onmessage = async (e) => {
|
||||
const { file, options } = e.data;
|
||||
const preview = await generatePreview(file, options);
|
||||
self.postMessage({ preview });
|
||||
};
|
||||
```
|
||||
|
||||
## Testing Plan
|
||||
|
||||
### Unit Tests
|
||||
- [ ] `calculateDimensions()` with various aspect ratios
|
||||
- [ ] `formatFileSize()` edge cases
|
||||
- [ ] `debounce()` timing
|
||||
|
||||
### Integration Tests
|
||||
- [ ] Preview updates on parameter change
|
||||
- [ ] Preview matches final output (within tolerance)
|
||||
- [ ] Large image handling (> 10MB)
|
||||
- [ ] Multiple format conversions
|
||||
|
||||
### Manual Tests
|
||||
- [ ] Mobile responsiveness
|
||||
- [ ] Slow network simulation
|
||||
- [ ] Various image formats (PNG, JPEG, WebP)
|
||||
- [ ] Edge cases (1x1px, 10000x10000px)
|
||||
|
||||
## Rollout Strategy
|
||||
|
||||
### Step 1: Feature Flag
|
||||
```typescript
|
||||
// Enable via environment variable
|
||||
const ENABLE_PREVIEW = import.meta.env.VITE_ENABLE_PREVIEW === 'true';
|
||||
```
|
||||
|
||||
### Step 2: Beta Testing
|
||||
- Deploy to staging environment
|
||||
- Gather user feedback
|
||||
- Monitor performance metrics
|
||||
|
||||
### Step 3: Gradual Rollout
|
||||
- Enable for 10% of users
|
||||
- Monitor error rates
|
||||
- Full rollout if stable
|
||||
|
||||
## Success Metrics
|
||||
|
||||
- **User Engagement**: Time spent on page increases
|
||||
- **Conversion**: More downloads completed
|
||||
- **Performance**: Preview renders in < 500ms (p95)
|
||||
- **Accuracy**: Preview matches output 95%+ of time
|
||||
- **Satisfaction**: User feedback positive
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- [ ] Before/after slider with drag handle
|
||||
- [ ] Zoom on preview (inspect details)
|
||||
- [ ] Multiple preview sizes simultaneously
|
||||
- [ ] A/B comparison (compare 2-4 settings)
|
||||
- [ ] Preview history (undo/redo preview)
|
||||
- [ ] Export preview settings as preset
|
||||
|
||||
---
|
||||
|
||||
**Estimated Effort**: 2-3 days for Phase 1 (client preview)
|
||||
**Complexity**: Medium
|
||||
**Impact**: ⭐⭐⭐⭐⭐ (Highest)
|
||||
|
||||
**Next Steps**:
|
||||
1. Create feature branch `feature/live-preview`
|
||||
2. Implement client-side preview
|
||||
3. Add UI components
|
||||
4. Test thoroughly
|
||||
5. Merge to main
|
||||
6. Deploy and monitor
|
||||
Reference in New Issue
Block a user