8.7 KiB
PNGer - Modern PNG Editor & Resizer
A sleek, modern PNG editor and resizer with live preview, dark/light mode theming, and direct upload/download features. 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
- 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
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)
-
Clone or pull this repository to your Unraid server:
cd /mnt/user/appdata git clone https://git.alwisp.com/jason/pnger.git cd pnger -
Build the Docker image:
docker build -t pnger:latest . -
Run via docker-compose:
docker compose up -d -
Access the application:
- Navigate to
http://[unraid-ip]:8080
- Navigate to
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 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
-
Install backend dependencies:
cd backend npm install -
Install frontend dependencies:
cd frontend npm install -
Run development servers:
Terminal 1 (Backend):
cd backend npm run devTerminal 2 (Frontend):
cd frontend npm run dev -
Access dev server:
- Frontend:
http://localhost:5173 - Backend API:
http://localhost:3000/api
- Frontend:
Building for Production
# Backend TypeScript compilation
cd backend
npm run build
# Frontend build
cd frontend
npm run build
Docker Deployment (Manual)
# 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
│ ├── 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
└── UI_UPGRADE_NOTES.md # UI upgrade documentation
How It Works
- User uploads an image via the web interface
- Live preview generates instantly using Canvas API
- User adjusts parameters (width, height, quality, format, etc.)
- Preview updates in real-time (debounced 300ms)
- User sees file size comparison and savings
- When satisfied, user clicks "Transform & Download"
- Frontend sends image + parameters to backend API
- Backend processes using Sharp (resize, crop, compress, convert)
- Processed image returned directly to browser
- Browser triggers automatic download
- 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 formatpng|webp|jpeg(optional, default:png)fit: Resize modeinside|cover(optional, default:inside)position: Crop position whenfit=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
Image Analysis
- Original file size displayed
- Preview size estimation
- Savings/increase percentage
- Visual feedback with color coding
Roadmap
See ROADMAP.md for planned features including:
- Drag & drop upload
- Batch processing
- Smart presets
- Watermarking
- Advanced crop tool
- 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