Files
image-studio/core/AGENTS.md
2026-07-03 09:24:08 +02:00

2.4 KiB

Core Domain Model Rules

Follow these rules for all changes in core/.

Purpose

  • core/ contains pure domain types for the image editor: documents, artboards, layers, assets, ids, and geometry.
  • Keep it framework-free, runtime-light, and reusable by renderer, commands, persistence, and tests.

Hard Rules

  • Do not import React, DOM APIs, UI components, storage, networking, filesystem, command handlers, or renderer code.
  • Do not add side effects, global state, caches, singletons, or environment-dependent behavior.
  • Prefer exported TypeScript types. Add runtime code only when it is pure, deterministic, and domain-generic.
  • Keep domain files small and focused. One concept per file; re-export public types from core/index.ts.
  • Use import type / export type for type-only dependencies.
  • Preserve discriminated unions. Every Layer variant must have a stable type string.
  • Do not weaken domain types with any, broad string | number unions, optional fields, or nullable values unless the domain truly allows absence.
  • IDs are opaque aliases from id.ts; do not inline plain string ID fields in models.
  • Do not duplicate geometry shapes outside geometry.ts. Use Vec2D, Size, Rect, Bounds, Transform, Mat2D, and CoordinateSpace.
  • Avoid app/workflow concerns in names and fields. Domain models describe image-editing state, not UI state.

Model Invariants

  • ImageDocument owns artboards and shared assets.
  • Artboard owns top-level layers and has document-space bounds.
  • LayerGroup.children owns nested layers; only groups have children.
  • ImageLayer.assetId must reference an Asset.id in the same document.
  • BaseLayer.opacity is normalized 0..1; visible and locked are explicit booleans.
  • Transform stores position, scale, and rotation only; derived matrices/bounds should not be persisted on models.
  • clippingMask.maskLayerId references another layer by LayerId; do not embed mask layer objects.

Changing Models

  • Before adding a field, decide whether it is core persisted state or derived/UI state. Derived/UI state does not belong here.
  • When adding a new domain type, create a focused file and export it from index.ts.
  • When adding a new layer kind, update the union in layer.ts, add a discriminant, and document its required relationships.
  • Keep names stable and serialization-friendly; assume these types may be saved, loaded, diffed, and migrated.