824 lines
19 KiB
Markdown
824 lines
19 KiB
Markdown
# AGENTS.md
|
||
|
||
## Purpose
|
||
|
||
This repository contains **Local Image Editor**, a small privacy-friendly browser application for placing one or more image overlays on top of a user-supplied background image and exporting the result at the background image's original resolution.
|
||
|
||
The application is intentionally simple:
|
||
|
||
```text
|
||
Upload background
|
||
↓
|
||
Add predefined or custom overlays
|
||
↓
|
||
Drag / resize / fine-tune coordinates
|
||
↓
|
||
Download PNG or JPEG
|
||
```
|
||
|
||
This file defines the expectations and constraints for coding agents working in this repository.
|
||
|
||
Read **README.md** and **PLAN.md** before making non-trivial changes.
|
||
|
||
---
|
||
|
||
## Core principles
|
||
|
||
These are non-negotiable unless the user explicitly changes the product requirements.
|
||
|
||
1. **User images stay local to the browser.**
|
||
- Do not upload user images to a server.
|
||
- Do not add analytics that capture image content.
|
||
- Do not add cloud storage or third-party image processing.
|
||
- Do not send user files to external APIs.
|
||
|
||
2. **The original image pixel dimensions are the canonical coordinate system.**
|
||
- X, Y, width, height, bounds, quick positioning, and export must all be expressed in source-image pixels.
|
||
- Browser/display size must never change logical object coordinates.
|
||
|
||
3. **Do not use a full-resolution interactive Fabric canvas for normal editing.**
|
||
- The current editor intentionally uses a smaller backing store plus a Fabric viewport transform.
|
||
- This is important for mobile memory usage and large phone photos.
|
||
- Full source resolution should only be used transiently when producing the final export.
|
||
|
||
4. **Export must preserve the exact source dimensions.**
|
||
- A 4032×3024 background must produce a 4032×3024 export.
|
||
- UI scaling must not affect export resolution or object placement.
|
||
|
||
5. **The project is frontend-only.**
|
||
- Do not introduce a backend, database, authentication, user accounts, or server-side image processing unless explicitly requested.
|
||
|
||
6. **The repository is public.**
|
||
- Never commit secrets or private infrastructure data.
|
||
- Do not assume obscurity, minification, Docker images, or frontend environment variables provide secrecy.
|
||
|
||
7. **Keep the product simple.**
|
||
- Prefer a small, understandable implementation over speculative abstractions or feature creep.
|
||
|
||
---
|
||
|
||
## Technology baseline
|
||
|
||
Use the existing stack unless the task specifically requires a change:
|
||
|
||
- Vue 3
|
||
- Vite
|
||
- TypeScript
|
||
- Fabric.js
|
||
- Node.js 22+
|
||
- nginx
|
||
- Docker / Docker Compose
|
||
|
||
Do not downgrade Fabric below the security baseline already enforced by the repository checks.
|
||
|
||
Avoid adding dependencies when the browser, Vue, Fabric, or a small local helper can solve the problem cleanly.
|
||
|
||
If adding or changing dependencies:
|
||
|
||
- update `package.json`;
|
||
- update and commit `package-lock.json`;
|
||
- run the full checks;
|
||
- inspect the dependency for current security issues;
|
||
- avoid packages that add remote runtime dependencies or tracking.
|
||
|
||
---
|
||
|
||
## Repository map
|
||
|
||
Important files and responsibilities:
|
||
|
||
```text
|
||
src/
|
||
├── App.vue
|
||
├── main.ts
|
||
├── components/
|
||
│ ├── BackgroundUpload.vue
|
||
│ ├── ExportControls.vue
|
||
│ ├── ImageEditor.vue
|
||
│ ├── OverlayControls.vue
|
||
│ └── OverlayPicker.vue
|
||
├── composables/
|
||
│ ├── useFabricEditor.ts
|
||
│ └── useLocale.ts
|
||
├── types/
|
||
│ └── editor.ts
|
||
├── utils/
|
||
│ ├── editorMath.ts
|
||
│ ├── imageFiles.ts
|
||
│ └── overlayConfig.ts
|
||
└── styles/
|
||
└── main.css
|
||
|
||
public/
|
||
└── overlays/
|
||
├── overlays.json
|
||
└── <overlay image files>
|
||
|
||
tests/
|
||
├── editorMath.test.ts
|
||
└── overlayConfig.test.ts
|
||
|
||
scripts/
|
||
├── quality-check.mjs
|
||
└── security-check.mjs
|
||
```
|
||
|
||
### Ownership boundaries
|
||
|
||
Keep these boundaries intact:
|
||
|
||
- **`useFabricEditor.ts`** owns Fabric.js lifecycle and canvas manipulation.
|
||
- **`editorMath.ts`** contains pure coordinate, size, bounds, and positioning logic.
|
||
- **`overlayConfig.ts`** validates the runtime overlay library.
|
||
- Vue components should primarily handle UI, props/events, and presentation.
|
||
- Do not spread direct Fabric manipulation throughout multiple components.
|
||
|
||
If new Fabric behavior is needed, prefer adding it to `useFabricEditor.ts` or a focused utility rather than manipulating the canvas directly from a UI component.
|
||
|
||
---
|
||
|
||
## Development commands
|
||
|
||
Install dependencies:
|
||
|
||
```sh
|
||
npm ci
|
||
```
|
||
|
||
Run locally:
|
||
|
||
```sh
|
||
npm run dev
|
||
```
|
||
|
||
Type check:
|
||
|
||
```sh
|
||
npm run type-check
|
||
```
|
||
|
||
Run tests:
|
||
|
||
```sh
|
||
npm test
|
||
```
|
||
|
||
Repository quality checks:
|
||
|
||
```sh
|
||
npm run lint
|
||
```
|
||
|
||
Security checks:
|
||
|
||
```sh
|
||
npm run security-check
|
||
```
|
||
|
||
Run all non-build checks:
|
||
|
||
```sh
|
||
npm run check
|
||
```
|
||
|
||
Production build:
|
||
|
||
```sh
|
||
npm run build
|
||
```
|
||
|
||
Production dependency audit:
|
||
|
||
```sh
|
||
npm audit --omit=dev --omit=optional --audit-level=high
|
||
```
|
||
|
||
Docker:
|
||
|
||
```sh
|
||
docker compose up --build
|
||
```
|
||
|
||
Before considering a code change complete, normally run:
|
||
|
||
```sh
|
||
npm run check
|
||
npm run build
|
||
```
|
||
|
||
If dependencies changed, also run the production dependency audit.
|
||
|
||
If Docker/nginx files changed, build and validate the container as well.
|
||
|
||
---
|
||
|
||
## Required agent workflow
|
||
|
||
For non-trivial tasks:
|
||
|
||
1. Read the relevant implementation before editing.
|
||
2. Read related tests.
|
||
3. Check `README.md` and `PLAN.md` for product constraints.
|
||
4. Identify the smallest set of files that should own the change.
|
||
5. Preserve the coordinate/rendering model described below.
|
||
6. Add or update tests for logic changes.
|
||
7. Run `npm run check`.
|
||
8. Run `npm run build`.
|
||
9. Review the diff for accidental scope expansion, secrets, generated files, and documentation drift.
|
||
10. Update `README.md` and/or `PLAN.md` when behavior, deployment, architecture, supported formats, or security assumptions change.
|
||
|
||
Do not claim a check passed unless it was actually run.
|
||
|
||
If the local environment prevents a check from running, state exactly which check could not be completed and why.
|
||
|
||
---
|
||
|
||
## Coordinate and rendering model
|
||
|
||
This is the most important technical invariant in the project.
|
||
|
||
Assume a source image is:
|
||
|
||
```text
|
||
4032 × 3024
|
||
```
|
||
|
||
while the editor is shown at:
|
||
|
||
```text
|
||
900 × 675
|
||
```
|
||
|
||
Objects still use logical coordinates such as:
|
||
|
||
```text
|
||
x = 3200
|
||
y = 80
|
||
width = 700
|
||
```
|
||
|
||
Those values are **source-image pixels**, not screen pixels.
|
||
|
||
### Interactive editing
|
||
|
||
The interactive Fabric canvas should use a display-sized backing store and a viewport transform:
|
||
|
||
```text
|
||
logical scene: 4032 × 3024
|
||
interactive backing: 900 × 675
|
||
viewport scale: 900 / 4032
|
||
```
|
||
|
||
Do not regress to allocating one or more 4032×3024 Fabric canvases during normal editing.
|
||
|
||
### Browser resize
|
||
|
||
Resizing the browser or editor container may change the viewport/display scale.
|
||
|
||
It must **not** change:
|
||
|
||
- object X/Y;
|
||
- object logical width/height;
|
||
- background logical dimensions;
|
||
- exported coordinates.
|
||
|
||
Use container-based resize behavior. Prefer `ResizeObserver` for editor layout changes rather than relying only on `window.resize`.
|
||
|
||
### Export
|
||
|
||
Export must temporarily render at source resolution and then restore the interactive editor state.
|
||
|
||
Any export implementation must:
|
||
|
||
- preserve the original background dimensions;
|
||
- preserve overlay transforms;
|
||
- hide Fabric controls/selection from output;
|
||
- restore selection and viewport state in `finally`;
|
||
- handle allocation/encoding failures gracefully;
|
||
- avoid leaving the editor in a modified state after failure.
|
||
|
||
Prefer asynchronous Blob-based downloads over large synchronous Base64 URLs.
|
||
|
||
For JPEG export, explicitly flatten transparency onto white unless product requirements change.
|
||
|
||
---
|
||
|
||
## Fabric.js rules
|
||
|
||
### Background
|
||
|
||
The background image:
|
||
|
||
- is not a normal editable overlay;
|
||
- must be non-selectable;
|
||
- must be non-evented;
|
||
- defines the logical canvas dimensions.
|
||
|
||
Replacing the background clears the current composition, so preserve the existing user confirmation behavior whenever overlays exist.
|
||
|
||
### Overlays
|
||
|
||
Overlay objects are independent Fabric image objects.
|
||
|
||
Each must:
|
||
|
||
- have a stable internal ID;
|
||
- have a human-readable name;
|
||
- be independently selectable;
|
||
- support drag and uniform resize;
|
||
- preserve aspect ratio;
|
||
- stay within background bounds;
|
||
- be removable.
|
||
|
||
For the MVP:
|
||
|
||
- rotation is disabled;
|
||
- skewing is disabled;
|
||
- flipping should not occur;
|
||
- edge-only non-uniform scaling controls should remain disabled.
|
||
|
||
Do not silently re-enable distortion controls.
|
||
|
||
### Transform updates
|
||
|
||
When moving or resizing an overlay:
|
||
|
||
- enforce uniform scale;
|
||
- clamp it into the logical background bounds;
|
||
- call the relevant Fabric coordinate update methods;
|
||
- publish the updated selection values;
|
||
- request rendering only as needed.
|
||
|
||
Do not assume `object.width` is the rendered width. Fabric source dimensions and transformed/scaled dimensions are different concepts.
|
||
|
||
### Async image loading
|
||
|
||
Background and overlay decoding are asynchronous.
|
||
|
||
Preserve the generation/invalidation logic so stale image loads cannot overwrite newer user actions.
|
||
|
||
A background replacement must invalidate overlay loads started for the previous background.
|
||
|
||
If adding new asynchronous editor operations, design them so the latest user action wins.
|
||
|
||
### Fabric lifecycle
|
||
|
||
Use one controlled Fabric canvas per editor instance.
|
||
|
||
On component teardown:
|
||
|
||
- remove/clean listeners as needed;
|
||
- dispose the Fabric canvas;
|
||
- avoid stale references.
|
||
|
||
Do not repeatedly recreate Fabric canvases during normal Vue updates.
|
||
|
||
---
|
||
|
||
## Bounds and sizing
|
||
|
||
Pure transform rules belong in `src/utils/editorMath.ts` where possible.
|
||
|
||
Overlay bounds should normally satisfy:
|
||
|
||
```text
|
||
x >= 0
|
||
y >= 0
|
||
x + width <= backgroundWidth
|
||
y + height <= backgroundHeight
|
||
```
|
||
|
||
Manual numeric inputs, drag, resize, and quick-position actions must all obey the same rules.
|
||
|
||
Do not implement separate inconsistent bounds algorithms in different components.
|
||
|
||
Default overlay placement is intentionally resolution-independent:
|
||
|
||
- default width is proportional to background width;
|
||
- margin is proportional to image dimensions;
|
||
- default position is near the top-right corner.
|
||
|
||
If these defaults change, add/update tests.
|
||
|
||
---
|
||
|
||
## Supported image formats
|
||
|
||
The current supported formats are:
|
||
|
||
- PNG
|
||
- JPEG
|
||
- WebP
|
||
|
||
Keep validation centralized in `src/utils/imageFiles.ts`.
|
||
|
||
Do not add SVG support casually.
|
||
|
||
If SVG support is explicitly requested, treat it as a deliberate feature:
|
||
- review current Fabric SVG security considerations;
|
||
- update validation;
|
||
- add tests;
|
||
- update CSP assumptions if relevant;
|
||
- update README/PLAN.
|
||
|
||
Do not trust only file extensions for user uploads.
|
||
|
||
---
|
||
|
||
## Object URLs and browser resources
|
||
|
||
Custom background and overlay files remain local.
|
||
|
||
If using `URL.createObjectURL()`:
|
||
|
||
- revoke URLs after decoding or when no longer needed;
|
||
- do not leak object URLs across repeated uploads;
|
||
- do not revoke an object URL before Fabric/image decoding has completed.
|
||
|
||
Avoid retaining large decoded images or canvases unnecessarily.
|
||
|
||
Performance on mobile Safari and large phone photos is a first-class concern.
|
||
|
||
---
|
||
|
||
## Overlay library
|
||
|
||
Runtime predefined overlays live under:
|
||
|
||
```text
|
||
public/overlays/
|
||
```
|
||
|
||
The library is configured by:
|
||
|
||
```text
|
||
public/overlays/overlays.json
|
||
```
|
||
|
||
Example:
|
||
|
||
```json
|
||
{
|
||
"id": "crr-logo",
|
||
"name": "CRR Logo",
|
||
"file": "crr-logo.png"
|
||
}
|
||
```
|
||
|
||
The runtime config intentionally accepts only simple local PNG/JPEG/WebP filenames.
|
||
|
||
Do not loosen validation to arbitrary URLs or nested paths without a clear requirement and security review.
|
||
|
||
Current validation should reject:
|
||
|
||
- empty fields;
|
||
- duplicate IDs;
|
||
- duplicate files;
|
||
- remote URLs;
|
||
- nested paths;
|
||
- path traversal;
|
||
- unsupported file extensions.
|
||
|
||
Adding a predefined overlay should normally require only:
|
||
|
||
1. copying the image into the overlay directory;
|
||
2. adding an entry to `overlays.json`.
|
||
|
||
Do not require a Vue rebuild just to add a new runtime overlay.
|
||
|
||
The Docker/nginx configuration intentionally makes `/overlays/` revalidate rather than cache for a week, because deployed logos may be replaced without rebuilding the application.
|
||
|
||
---
|
||
|
||
## UI and accessibility
|
||
|
||
The editor must work on:
|
||
|
||
- desktop;
|
||
- tablet;
|
||
- mobile;
|
||
- mouse;
|
||
- touch;
|
||
- keyboard for non-drag alternatives.
|
||
|
||
Do not make drag the only way to manipulate an object.
|
||
|
||
Maintain:
|
||
|
||
- numeric X control;
|
||
- numeric Y control;
|
||
- numeric width control;
|
||
- accessible overlay selection;
|
||
- quick corner buttons;
|
||
- visible focus states;
|
||
- useful labels.
|
||
|
||
If multiple overlays exist, keyboard users must have a way to select an existing overlay without clicking the canvas.
|
||
|
||
Do not introduce horizontal page scrolling for normal mobile layouts.
|
||
|
||
The editor is the primary visual element. Avoid turning the app into a complex dashboard.
|
||
|
||
---
|
||
|
||
## Internationalization
|
||
|
||
The interface supports:
|
||
|
||
- English;
|
||
- Portuguese (Portugal).
|
||
|
||
User-visible application strings should go through `useLocale.ts`.
|
||
|
||
Do not hardcode new English UI text directly in components unless it is truly non-user-visible.
|
||
|
||
When adding a string:
|
||
|
||
- add the English translation;
|
||
- add the PT-PT translation;
|
||
- use Portuguese from Portugal, not Brazilian Portuguese;
|
||
- preserve proper accents and spelling.
|
||
|
||
Examples of preferred PT-PT wording:
|
||
|
||
- `ficheiro`
|
||
- `imagem`
|
||
- `sobreposição`
|
||
- `carregar`
|
||
- `descarregar`
|
||
- `área de edição`
|
||
|
||
Avoid Brazilian-specific UI vocabulary where a natural PT-PT alternative exists.
|
||
|
||
ARIA labels that are user-facing should also be translated.
|
||
|
||
---
|
||
|
||
## Security and public-repository rules
|
||
|
||
This repository is public.
|
||
|
||
### Secrets
|
||
|
||
Never commit:
|
||
|
||
- API keys;
|
||
- passwords;
|
||
- access tokens;
|
||
- private keys;
|
||
- private certificates;
|
||
- deployment credentials;
|
||
- private infrastructure details that are intended to remain private.
|
||
|
||
Real local environment files must remain ignored.
|
||
|
||
The repository should contain:
|
||
|
||
```text
|
||
.env.example
|
||
```
|
||
|
||
with safe example/non-secret values only.
|
||
|
||
### Frontend environment variables
|
||
|
||
Do not put secrets in Vite/frontend variables.
|
||
|
||
Anything bundled into browser JavaScript is public.
|
||
|
||
In particular:
|
||
|
||
```text
|
||
VITE_*
|
||
```
|
||
|
||
must never be treated as secret storage.
|
||
|
||
The current project intentionally avoids Vite deployment configuration where runtime static configuration is simpler.
|
||
|
||
If a future feature requires a real secret, do not "hide" it in the frontend. Propose a server-side boundary or a deployment secret mechanism.
|
||
|
||
### External services
|
||
|
||
Do not add:
|
||
|
||
- telemetry;
|
||
- analytics;
|
||
- remote fonts;
|
||
- remote scripts;
|
||
- CDN runtime dependencies;
|
||
- third-party upload services;
|
||
|
||
unless explicitly requested and reviewed against privacy/CSP requirements.
|
||
|
||
The current restrictive nginx Content Security Policy is intentional.
|
||
|
||
### Secret scanning
|
||
|
||
Preserve and run:
|
||
|
||
```sh
|
||
npm run security-check
|
||
```
|
||
|
||
Do not change secret scanning to print actual discovered secret values into CI logs. Report filenames/types, not secret contents.
|
||
|
||
### Dependency security
|
||
|
||
Do not weaken the Fabric minimum-version security check.
|
||
|
||
Keep nginx on a maintained/patched release.
|
||
|
||
When changing dependencies, inspect current advisories and run the dependency audit.
|
||
|
||
---
|
||
|
||
## Docker and nginx
|
||
|
||
The production image is a static nginx deployment.
|
||
|
||
Keep it that way unless requirements change.
|
||
|
||
Important behavior:
|
||
|
||
- `.env` must not be copied into the image;
|
||
- overlay directory is mounted read-only;
|
||
- runtime overlays are served from `/overlays/`;
|
||
- overlay files should revalidate so replacements appear promptly;
|
||
- the app is normally deployed at the root of a hostname;
|
||
- security headers and CSP are intentional.
|
||
|
||
If changing nginx:
|
||
|
||
- preserve SPA/static asset behavior;
|
||
- preserve overlay runtime behavior;
|
||
- validate the nginx configuration;
|
||
- review CSP implications of any new browser capability.
|
||
|
||
Do not hardcode private domains, tokens, or infrastructure credentials in Docker/nginx files.
|
||
|
||
---
|
||
|
||
## Testing expectations
|
||
|
||
Tests should focus on behavior that is easy to regress.
|
||
|
||
Pure logic belongs in utilities so it can be tested without a browser where possible.
|
||
|
||
At minimum, add/update tests when changing:
|
||
|
||
- coordinate scaling;
|
||
- bounds;
|
||
- resize behavior;
|
||
- corner positioning;
|
||
- default overlay sizing;
|
||
- overlay configuration parsing/validation.
|
||
|
||
For editor behavior that depends on Fabric/browser APIs, prefer focused browser/integration tests if a unit test would only mock away the behavior being verified.
|
||
|
||
Important scenarios include:
|
||
|
||
```text
|
||
1280 × 1280
|
||
1920 × 1080
|
||
1080 × 1920
|
||
4032 × 3024
|
||
```
|
||
|
||
Verify that browser/editor resizing does not change logical coordinates.
|
||
|
||
When touching export logic, verify:
|
||
|
||
- PNG output dimensions;
|
||
- JPEG output dimensions;
|
||
- object placement;
|
||
- multiple overlay stacking;
|
||
- absence of Fabric controls;
|
||
- editor state restoration after export;
|
||
- failure handling where practical.
|
||
|
||
---
|
||
|
||
## Code style
|
||
|
||
Follow the existing TypeScript/Vue style.
|
||
|
||
General expectations:
|
||
|
||
- strict TypeScript;
|
||
- explicit types at important boundaries;
|
||
- small focused functions;
|
||
- pure helpers for math/validation;
|
||
- avoid `any`;
|
||
- avoid non-null assertions unless the invariant is genuinely guaranteed;
|
||
- avoid duplicated logic;
|
||
- prefer early returns for invalid state;
|
||
- keep async error handling explicit;
|
||
- clean up browser resources and listeners.
|
||
|
||
Do not add abstractions solely to anticipate hypothetical future features.
|
||
|
||
Do not commit:
|
||
|
||
- `dist/`;
|
||
- `node_modules/`;
|
||
- local `.env`;
|
||
- editor/IDE junk;
|
||
- temporary output images;
|
||
- debug artifacts.
|
||
|
||
Keep LF line endings and no trailing whitespace.
|
||
|
||
---
|
||
|
||
## Error handling
|
||
|
||
Errors shown to users should be:
|
||
|
||
- concise;
|
||
- human-readable;
|
||
- translated;
|
||
- actionable where possible.
|
||
|
||
Do not expose stack traces in the normal UI.
|
||
|
||
A failure loading one predefined overlay should not make custom uploads unusable.
|
||
|
||
Export and decode failures should leave the existing editor state usable.
|
||
|
||
Do not swallow unexpected errors silently if they prevent the requested action.
|
||
|
||
---
|
||
|
||
## Scope discipline
|
||
|
||
The following are currently **non-goals** unless explicitly requested:
|
||
|
||
- authentication;
|
||
- user accounts;
|
||
- database;
|
||
- server-side persistence;
|
||
- cloud storage;
|
||
- project history;
|
||
- full layers panel;
|
||
- text editor;
|
||
- rotation UI;
|
||
- filters;
|
||
- cropping;
|
||
- image effects;
|
||
- collaborative editing;
|
||
- admin interface;
|
||
- remote APIs.
|
||
|
||
Potential future features may be mentioned in `PLAN.md`, but their presence there is not permission to implement them during unrelated tasks.
|
||
|
||
Do not choose or add a software license on the user's behalf unless explicitly requested.
|
||
|
||
---
|
||
|
||
## Documentation rules
|
||
|
||
Update documentation when a change affects what users or deployers need to know.
|
||
|
||
Update **README.md** for changes to:
|
||
|
||
- setup;
|
||
- commands;
|
||
- supported formats;
|
||
- deployment;
|
||
- overlay configuration;
|
||
- privacy/security behavior;
|
||
- browser-visible behavior.
|
||
|
||
Update **PLAN.md** for changes to:
|
||
|
||
- architecture;
|
||
- requirements;
|
||
- acceptance criteria;
|
||
- project scope;
|
||
- implementation direction.
|
||
|
||
Keep documentation aligned with actual code. Do not document features that are not implemented.
|
||
|
||
---
|
||
|
||
## Definition of done
|
||
|
||
A change is normally complete when:
|
||
|
||
- the requested behavior is implemented;
|
||
- core product invariants remain intact;
|
||
- relevant tests are added or updated;
|
||
- TypeScript passes;
|
||
- tests pass;
|
||
- repository quality checks pass;
|
||
- security checks pass;
|
||
- production build passes;
|
||
- dependency audit is reviewed if dependencies changed;
|
||
- Docker/nginx is validated if deployment files changed;
|
||
- no secrets or generated artifacts were introduced;
|
||
- README/PLAN are updated when needed;
|
||
- the final diff contains no unrelated refactors.
|
||
|
||
The goal is not merely to make the requested change work once. It should remain correct for large images, mobile devices, multiple overlays, public deployment, and pixel-accurate export.
|