---
name: dpage-editor
description: Guides development in the dpage-editor React/Konva catalogue editor and HTML flip-book viewer. Use when editing editor tools, viewer rendering, Zustand store, catalogue autosave, entrance animations, masks, or any code under src/pages/editor or src/pages/viewer.
---

# dpage-editor

## Stack

- React 19 + TypeScript + Vite
- Canvas editor: **Konva** / **react-konva**
- Viewer: **react-pageflip** (HTML/CSS, not Konva)
- State: **Zustand** (`editorStore`)
- Data: **TanStack Query** + axios services
- Styling: **Tailwind CSS 4**, `clx()` from `@/helpers/utils`
- i18n: react-i18next (Persian UI)

## Architecture

| Area | Path | Role |
|------|------|------|
| Editor page | `src/pages/editor/Editor.tsx` | Loads catalogue, autosave, Konva lifecycle |
| Editor store | `src/pages/editor/store/editorStore.ts` | Pages, objects, undo, selection |
| Object types | `src/pages/editor/store/editorStore.types.ts` | `EditorObject`, `ToolType`, animations |
| Canvas | `src/pages/editor/components/canvas/` | Layers, drawing, transform, snap |
| Tools (Konva) | `src/pages/editor/components/tools/` | One component per shape/media type |
| Sidebar | `src/pages/editor/components/sidebar/` | ToolsBar, settings, instructions |
| Viewer | `src/pages/viewer/` | Flip-book, entrance animations, magnifier |
| Book render | `src/pages/viewer/components/BookPage.tsx` | HTML mirror of `EditorObject` |
| Catalogue API | `src/pages/catalogue/hooks/useCatalogueData.ts` | Load/save `content` JSON |

**Source of truth:** `EditorObject[]` per page, serialized into catalogue `content`. Editor and viewer must stay in sync.

## Critical conventions

### Konva text rendering

In `Editor.tsx`, `Konva.legacyTextRendering = true` is required. Konva 10's new text baseline breaks transformer bounds for Persian/Latin text. Do not remove without fixing text measurement.

### Store sync

`objects` and `pages[currentPageId].objects` must stay synced. Use existing helpers (`withSyncedCurrentPageObjects`, store actions) — never update one without the other.

### IDs and cloning

- Use `createScopedId` / helpers in `editorStore.helpers.ts` for new IDs.
- Undo history uses `structuredClone` on object lists; new fields on `EditorObject` must be JSON-serializable.

### Path alias

Use `@/` imports (e.g. `@/pages/editor/store/editorStore`).

## Adding or changing a tool type

Checklist — touch all relevant layers:

1. **`ToolType`** in `editorStore.types.ts` — add union member + fields on `EditorObject` if needed.
2. **Konva shape** — `src/pages/editor/components/tools/<Name>Shape.tsx`, export from `tools/index.ts`.
3. **`ObjectRenderer.tsx`** — `switch (obj.type)` case + props wiring.
4. **Sidebar** — `ToolsBar.tsx`, `ToolInstructions.tsx`, optional `instructions/` + `settings/`.
5. **Creation defaults** — store action or drawing handler that sets initial `x`, `y`, `width`, `height`.
6. **Viewer** — `BookPage.tsx` `renderObject` branch; reuse utils from `src/pages/editor/utils/` (gradient, fontFamily, textStyle, customShape).
7. **Entrance animation** (if applicable) — `entranceAnimations.css`, `entranceAnimationStyle.ts`, `useBookEntranceController.ts`.

Default tool prop pattern:

```typescript
type ShapeProps = {
  obj: EditorObject;
  isSelected: boolean;
  transformerRef: React.RefObject<Konva.Transformer | null>;
  onSelect: (id: string, node: Konva.Node, e?: Konva.KonvaEventObject<MouseEvent>) => void;
  onUpdate: (id: string, updates: Partial<EditorObject>) => void;
  draggable: boolean;
};
```

## Editor vs viewer

| Concern | Editor | Viewer |
|---------|--------|--------|
| Rendering | Konva nodes | HTML/CSS (`BookPage`) |
| Interaction | Select, transform, draw | Flip pages, links, autoplay |
| Scale | Stage zoom | `VIEWER_SCALE` + viewport hooks |
| Media | Full controls in sidebar | `staticMedia` option for thumbnails |
| RTL | Stage coordinates | `RTL_COVER_PAD_PAGE_ID` for odd page counts |

Viewer-specific logic belongs in `src/pages/viewer/hooks/` and `utils/`, not in editor components.

## Autosave (Editor.tsx)

- `useUpdateCatalog` debounces saves.
- On catalogue `id` change: `cancelPendingUpdate`, `resetEditor`, reload once per id (`loadedCatalogIdRef`).
- Skip first autosave after load (`skipNextAutosaveRef`).

## Code style for this repo

- Prefer extending existing components over new abstractions.
- Keep comments for non-obvious Konva/RTL/safari workarounds (see `BookViewer.tsx`, `Editor.tsx`).
- Match Persian UI strings in surrounding files.
- Minimize diff scope; do not refactor unrelated editor/viewer code.
- Run `npm run build` after substantive changes (strict TypeScript).

## Common pitfalls

- Adding an `EditorObject` field but forgetting viewer render → object invisible in preview.
- Transform commits: use `transformCommit.ts` patterns so scale/rotation persist correctly.
- Mask/group: check `groupId`, `maskId`, `isMask` in both editor layers and `maskStyle.ts`.
- Page background: editor `documentSettings` ↔ viewer `resolvePageBackground`.
- Fullscreen/viewer chrome: `isViewerPath` hides app header/sidebar in `MainRouter.tsx`.

## Key routes

- Editor: `/editor/:id`
- Viewer: `/viewer/:id` or `/viewer/:businessSlug/:slug`
- Catalogue list: see `src/config/Paths.ts`
