Files

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:

  1. Copy the image into the overlay directory.
  2. Add its entry to overlays.json.
  3. 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.

  • .env and .env.* are ignored by Git and Docker, except .env.example.
  • .env.example contains 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.