diff --git a/.cursor/skills/dpage-editor/SKILL.md b/.cursor/skills/dpage-editor/SKILL.md new file mode 100644 index 0000000..e11911a --- /dev/null +++ b/.cursor/skills/dpage-editor/SKILL.md @@ -0,0 +1,116 @@ +--- +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/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; + onSelect: (id: string, node: Konva.Node, e?: Konva.KonvaEventObject) => void; + onUpdate: (id: string, updates: Partial) => 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`