2026-03-07 22:39:27 -06:00
# PNGer - Modern PNG Editor & Resizer
2026-03-07 22:37:25 -06:00
2026-03-07 23:06:33 -06:00
A simple, reactive, modern PNG editor and resizer with direct upload and download features. Built with TypeScript and deployed as a single Docker container on Unraid.
2026-03-07 22:39:27 -06:00
## Features
- **Drag & Drop Upload**: Intuitive file upload interface
- **Resize Operations**: Width, height, and aspect ratio controls
2026-03-07 23:06:33 -06:00
- **Crop to Fit**: Smart cropping with position control (center, top, bottom, etc.)
- **Format Conversion**: PNG, WebP, and JPEG output
- **Quality Control**: Adjustable compression settings
2026-03-07 22:39:27 -06:00
- **Direct Download**: No server-side storage, immediate download
2026-03-07 23:06:33 -06:00
- **Modern UI**: Sleek, responsive TypeScript/Svelte design
2026-03-07 22:39:27 -06:00
## Tech Stack
2026-03-07 23:06:33 -06:00
- **Frontend**: Svelte 4 + Vite + TypeScript
- **Backend**: Node.js + Express + TypeScript
- **Image Processing**: Sharp (high-performance image library)
- **Container**: Docker (Alpine-based, multi-stage build)
2026-03-07 22:39:27 -06:00
- **Deployment**: Unraid via Docker Compose
## Quick Start
2026-03-07 23:06:33 -06:00
### 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
2026-03-07 22:39:27 -06:00
```bash
2026-03-07 23:06:33 -06:00
# Backend TypeScript compilation
cd backend
npm run build
2026-03-07 22:39:27 -06:00
2026-03-07 23:06:33 -06:00
# Frontend build
cd frontend
npm run build
2026-03-07 22:39:27 -06:00
```
2026-03-07 23:06:33 -06:00
## Docker Deployment (Manual)
2026-03-07 22:39:27 -06:00
```bash
2026-03-07 23:06:33 -06:00
# Build the image (all dependencies and builds are handled internally)
2026-03-07 22:39:27 -06:00
docker build -t pnger:latest .
# Run the container
2026-03-07 23:06:33 -06:00
docker run -d \
--name pnger \
-p 8080:3000 \
-e MAX_FILE_SIZE=10485760 \
--restart unless-stopped \
pnger:latest
2026-03-07 22:39:27 -06:00
```
## Project Structure
```
pnger/
2026-03-07 23:06:33 -06:00
├── frontend/ # Svelte + TypeScript application
│ ├── src/
│ │ ├── App.svelte # Main UI component
│ │ ├── main.ts # Entry point
│ │ └── lib/
│ │ └── api.ts # API client
│ ├── 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
└── INSTRUCTIONS.md # Development guide
2026-03-07 22:39:27 -06:00
```
2026-03-07 23:06:33 -06:00
## How It Works
1. User uploads an image via the web interface
2. Frontend sends image + transform parameters to backend API
3. Backend processes image using Sharp (resize, crop, compress, convert format)
4. Processed image is returned directly to browser
5. Browser triggers automatic download
6. 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
2026-03-07 22:39:27 -06:00
## Configuration
2026-03-07 23:06:33 -06:00
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` )
2026-03-07 22:39:27 -06:00
## License
MIT License - See LICENSE file for details
## Repository
https://git.alwisp.com/jason/pnger