Blob: AGENTS.md
AGENTS.md
Guidance for AI agents (and humans) working in this repository. For the
user-facing overview see README.md; for the full design see
docs/ARCHITECTURE.md.
What this is
wimp is a fully client-side web image editor: crop, transform, color adjust, and export to JPEG/WebP/PNG/AVIF. Images never leave the browser. React 19 + React Compiler on the main thread; all pixel work and encoding run in a Web Worker.
Commands
Requires Node 22+ and npm.
npm install
npm run dev # Vite dev server
npm run typecheck # tsc, no emit — run this after changes
npm run build # tsc + production build
npm run preview # serve the production buildThere is no test runner or linter configured. The verification bar is:
npm run typecheck passes and npm run build succeeds. For behavioral changes,
verify in the browser (load an image, exercise the affected path, export).
Layout
index.html entry + inline SVG favicon
vite.config.ts react + react-compiler (babel) + tailwind; COOP/COEP headers
src/
main.tsx React root
index.css Tailwind import + @theme tokens
types.ts Template, Adjustments, ExportRequest/Result, worker msgs
templates.ts preset data + helpers
hooks/useEditor.ts reducer: ALL editor state + actions
lib/
image.ts load, filename, download, formatting
exportClient.ts main-thread handle to the worker
filter.ts shared CSS/canvas filter builder
vips.ts guarded lazy wasm-vips loader
cn.ts classnames helper
workers/export.worker.ts decode -> transform -> encode
components/ App.tsx, TopBar, Dropzone, EditorCanvas, TemplatePicker,
panels, ui, Slider, icons
docs/ ARCHITECTURE / WORKLOG / VERIFICATION + deferred/Conventions and invariants
- State lives in one reducer. All editor state and actions are in
src/hooks/useEditor.ts. Add new editor state there, not in scattered componentuseState. - Preview must match export. The filter string in
src/lib/filter.tsis the single source of truth used both as react-easy-crop'smediaStyle.filterand the worker'sctx.filter. The crop/rotate/flip coordinate math mirrors react-easy-crop'sgetCroppedImgrecipe (see ARCHITECTURE.md "Coordinate model"). If you touch one side, touch the other and keep them identical. - Heavy work stays in the worker. Decoding, transforming, resampling, and
encoding belong in
src/workers/export.worker.ts, reached viasrc/lib/exportClient.ts. Don't move pixel/encoding work onto the main thread. - Templates are plain data. Add presets by appending rows to
TEMPLATESinsrc/templates.ts; no code changes needed for a new preset. - React Compiler is on. Don't hand-add
useMemo/useCallbackfor performance; let the compiler handle memoization. Write idiomatic components. - Tailwind v4, no config file. Design tokens live in
@themeinsrc/index.css. Use the existing token utilities (e.g.bg-surface,text-muted,ring-accent-strong).
Cross-origin isolation (read before adding network assets)
The app sets Cross-Origin-Embedder-Policy: require-corp so SharedArrayBuffer
is available for threaded WASM (wasm-vips, jSquash). Under require-corp, any
cross-origin subresource (e.g. an ML model from a CDN) is blocked unless it
carries a Cross-Origin-Resource-Policy header. Self-host model/wasm assets
same-origin rather than fetching from a CDN. See docs/deferred/README.md.
The wasm-vips fast path also requires cross-origin isolation; src/lib/vips.ts
guards the loader and the worker falls back to canvas otherwise. Keep both paths
working.
Scope discipline
Make the change requested and nothing more — no drive-by refactors, no
speculative abstractions, no comments that restate the code. Match the existing
style of nearby files. Deferred/future work is tracked in docs/deferred/;
don't start it unless asked.
Git
Develop on the branch you were assigned, commit with clear messages, and do not open a pull request unless explicitly asked.