Files
pnger/README.md

11 KiB

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

  1. Clone or pull this repository to your Unraid server:

    cd /mnt/user/appdata
    git clone https://git.alwisp.com/jason/pnger.git
    cd pnger
    
  2. Build the Docker image:

    docker build -t pnger:latest .
    
  3. Run via docker-compose:

    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 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:

    cd backend
    npm install
    
  2. Install frontend dependencies:

    cd frontend
    npm install
    
  3. Run development servers:

    Terminal 1 (Backend):

    cd backend
    npm run dev
    

    Terminal 2 (Frontend):

    cd frontend
    npm run dev
    
  4. Access dev server:

    • Frontend: http://localhost:5173
    • Backend API: http://localhost:3000/api

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
│   │       └── 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 for detailed implementation notes.

Roadmap

See 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