5.6 KiB
Local Image Editor
A fast, privacy-friendly browser image overlay editor. Upload a PNG, JPEG, or WebP image, add one or more predefined or custom overlays, position them precisely, and download the result at the source image's original resolution.
All user image processing happens locally in the browser. The project has no backend, database, image upload endpoint, account system, or credentials.
The interface supports English and Portuguese (Portugal). It uses the browser language by default and remembers the selected language locally.
Highlights
- Multiple independent overlays
- Runtime-configured overlay library
- Custom local overlay uploads
- Mouse, touch, and numeric X/Y/width controls
- Accessible overlay selector for keyboard users
- Four quick corner positions with bounds enforcement
- Original-image pixels remain the canonical coordinate system
- Low-resolution interactive Fabric.js backing canvas for better mobile memory usage
- Full-resolution PNG/JPEG export only when needed
- JPEG export explicitly flattens transparency onto white
- No server-side image processing
Run locally
Requirements: Node.js 22 or newer and npm.
npm ci
npm run dev
Open the URL printed by Vite.
Useful checks:
npm run type-check
npm test
npm run lint
npm run security-check
npm run build
Or run all non-build checks together:
npm run check
The application uses Fabric only in the browser. CI and Docker use npm ci so installs are reproducible from the committed lockfile.
Docker
Create an optional local .env from the public example and set a host port or overlay directory if needed:
cp .env.example .env
docker compose up --build
The application is served at http://localhost:8080 unless APP_PORT is changed.
Compose mounts OVERLAYS_DIR read-only at /usr/share/nginx/html/overlays in the container. It defaults to ./public/overlays:
OVERLAYS_DIR=/srv/local-image-editor/overlays
Images and overlays.json in that directory are served with revalidation rather than week-long caching, so a replaced logo is picked up on reload without rebuilding the container.
The included nginx configuration adds a restrictive Content Security Policy and basic browser security headers.
Overlay library
Predefined overlays are loaded at runtime from public/overlays/overlays.json. In Docker deployments, this is the directory configured by OVERLAYS_DIR.
Example entry:
{
"id": "example-badge",
"name": "Example badge",
"file": "example-badge.png"
}
Overlay configuration is intentionally restricted to simple local PNG/JPEG/WebP filenames. Empty fields, duplicate IDs/files, nested paths, remote URLs, path traversal, and unsupported extensions are rejected.
To add an overlay:
- Copy the image into the overlay directory.
- Add its entry to
overlays.json. - Reload the application.
No Vue source change or image rebuild is required.
Coordinate and rendering model
The source image's original dimensions are always the logical coordinate system. For example, a 4032 x 3024 image remains a 4032 x 3024 logical scene even when the editor is displayed at 900 x 675.
Fabric.js uses a viewport transform to render that scene into a smaller interactive backing store. This keeps X/Y/width values pixel-accurate while avoiding multiple full-resolution canvas surfaces during normal editing.
During export, the canvas is temporarily rendered at the exact source dimensions. The normal low-resolution editor state and selection are restored in a finally block even if export fails.
Public repository and configuration security
The repository is designed to be safe to publish.
.envand.env.*are ignored by Git and Docker, except.env.example..env.examplecontains only non-sensitive Docker Compose values.- There are no
VITE_*deployment settings because Vite values are compiled into browser JavaScript and therefore cannot hold secrets. - If a future feature requires a real secret, it must be handled by a server-side component or deployment secret store and must never be bundled into this frontend.
- CI runs a small repository security check and a production dependency audit.
Do not commit credentials, tokens, passwords, private keys, private certificates, or private deployment configuration.
Runtime/deployment path
The Docker/nginx setup expects the application to be exposed at the root of a hostname, for example:
https://editor.example.com/
The Vite bundle itself uses relative asset URLs, but if a reverse proxy publishes it under a path prefix, the proxy must strip that prefix before forwarding requests to the included nginx container so /overlays/ continues to resolve correctly.
Quality checks
A Gitea Actions workflow is provided in .gitea/workflows/quality.yml. It runs:
npm ci
npm run check
npm run build
npm audit --omit=dev --omit=optional --audit-level=high
Unit tests cover coordinate/display scaling, transformed overlay bounds, corner positioning, and overlay-library validation. The repository check also detects common formatting/configuration mistakes and basic accidental secret material.
Replacing the background
If overlays already exist, the app asks for confirmation before replacing the background because the current composition will be cleared. The header branding is intentionally not a link so it cannot accidentally reload and discard a composition.
Licensing
This repository may be publicly readable, but public visibility alone does not grant reuse or redistribution rights. Choose and add a LICENSE file explicitly if the project is intended to be open source.