341 lines
11 KiB
Markdown
341 lines
11 KiB
Markdown
# 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.
|
|
|
|
## ✨ 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
|
|
|
|
### ⚡ 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
|
|
|
|
### 🖼️ 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:**
|
|
```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:**
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
4. **Access the application:**
|
|
- Navigate to `http://[unraid-ip]:8080`
|
|
|
|
### Unraid Environment Variables (Configurable via UI)
|
|
|
|
When setting up in Unraid Docker UI, you can configure:
|
|
|
|
| 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 |
|
|
|
|
### Unraid Docker Template Example
|
|
|
|
```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 |