Skip to content
File

Blob: AGENTS.md

Markdown97 lines

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 build

There 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 component useState.
  • Preview must match export. The filter string in src/lib/filter.ts is the single source of truth used both as react-easy-crop's mediaStyle.filter and the worker's ctx.filter. The crop/rotate/flip coordinate math mirrors react-easy-crop's getCroppedImg recipe (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 via src/lib/exportClient.ts. Don't move pixel/encoding work onto the main thread.
  • Templates are plain data. Add presets by appending rows to TEMPLATES in src/templates.ts; no code changes needed for a new preset.
  • React Compiler is on. Don't hand-add useMemo/useCallback for performance; let the compiler handle memoization. Write idiomatic components.
  • Tailwind v4, no config file. Design tokens live in @theme in src/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.