Compare commits
42 Commits
e6fc300fba
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 677c62e6eb | |||
| e0eb29488a | |||
| ae3787241a | |||
| 9e105606a3 | |||
| c6bb275d05 | |||
| 4273e899ca | |||
| 0361e05acb | |||
| 33cce93064 | |||
| a2a591420c | |||
| 3c764a1652 | |||
| 954ad48486 | |||
| 368cace143 | |||
| 46926c66e7 | |||
| a448ff10de | |||
| 80a92ea4c4 | |||
| 63a774c4af | |||
| 1ed198ca69 | |||
| d94bf77522 | |||
| 5e314bb581 | |||
| 0391513928 | |||
| 22d15f9a13 | |||
| bbb797cc62 | |||
| 4af7181384 | |||
| cda1b5b5bd | |||
| 2b4fd31ea2 | |||
| 168af3bf95 | |||
| 0bef54c51c | |||
| 85ba8e4261 | |||
| 39e18c3331 | |||
| ca02a621da | |||
| b13a46ec65 | |||
| e93ab38b05 | |||
| 052b737653 | |||
| b8de25e9d1 | |||
| 8b194fd7cd | |||
| b7ffb2b857 | |||
| c6d1097a30 | |||
| 494a6a53fc | |||
| ebb2092ec1 | |||
| 9447500725 | |||
| 46f5389036 | |||
| f9113ef9c8 |
@@ -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/<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`
|
||||
@@ -0,0 +1,35 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Build outputs
|
||||
dist/
|
||||
dist-ssr/
|
||||
build/
|
||||
out/
|
||||
|
||||
# Caches
|
||||
.cache/
|
||||
.vite/
|
||||
|
||||
# Coverage
|
||||
coverage/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Environment / secrets
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Editor / IDE
|
||||
.vscode/
|
||||
!.vscode/extensions.json
|
||||
.idea/
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Generated
+147
-120
@@ -23,6 +23,7 @@
|
||||
"moment-jalaali": "^0.10.4",
|
||||
"page-flip": "^2.0.7",
|
||||
"react": "^19.2.0",
|
||||
"react-colorful": "^5.7.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-dropzone": "^14.3.8",
|
||||
"react-i18next": "^16.3.0",
|
||||
@@ -54,13 +55,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
|
||||
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
|
||||
"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.29.7",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
@@ -69,9 +70,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/compat-data": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
|
||||
"integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
|
||||
"integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -79,21 +80,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/core": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
|
||||
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
"@babel/helper-compilation-targets": "^7.27.2",
|
||||
"@babel/helper-module-transforms": "^7.28.3",
|
||||
"@babel/helpers": "^7.28.4",
|
||||
"@babel/parser": "^7.28.5",
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/traverse": "^7.28.5",
|
||||
"@babel/types": "^7.28.5",
|
||||
"@babel/code-frame": "^7.29.7",
|
||||
"@babel/generator": "^7.29.7",
|
||||
"@babel/helper-compilation-targets": "^7.29.7",
|
||||
"@babel/helper-module-transforms": "^7.29.7",
|
||||
"@babel/helpers": "^7.29.7",
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@babel/template": "^7.29.7",
|
||||
"@babel/traverse": "^7.29.7",
|
||||
"@babel/types": "^7.29.7",
|
||||
"@jridgewell/remapping": "^2.3.5",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"debug": "^4.1.0",
|
||||
@@ -110,14 +111,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
|
||||
"integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
|
||||
"integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.28.5",
|
||||
"@babel/types": "^7.28.5",
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@babel/types": "^7.29.7",
|
||||
"@jridgewell/gen-mapping": "^0.3.12",
|
||||
"@jridgewell/trace-mapping": "^0.3.28",
|
||||
"jsesc": "^3.0.2"
|
||||
@@ -127,14 +128,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-compilation-targets": {
|
||||
"version": "7.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
|
||||
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
|
||||
"integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.27.2",
|
||||
"@babel/helper-validator-option": "^7.27.1",
|
||||
"@babel/compat-data": "^7.29.7",
|
||||
"@babel/helper-validator-option": "^7.29.7",
|
||||
"browserslist": "^4.24.0",
|
||||
"lru-cache": "^5.1.1",
|
||||
"semver": "^6.3.1"
|
||||
@@ -144,9 +145,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-globals": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
|
||||
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
|
||||
"integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -154,29 +155,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-imports": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
|
||||
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
|
||||
"integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.27.1",
|
||||
"@babel/types": "^7.27.1"
|
||||
"@babel/traverse": "^7.29.7",
|
||||
"@babel/types": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-transforms": {
|
||||
"version": "7.28.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
|
||||
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
|
||||
"integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.27.1",
|
||||
"@babel/traverse": "^7.28.3"
|
||||
"@babel/helper-module-imports": "^7.29.7",
|
||||
"@babel/helper-validator-identifier": "^7.29.7",
|
||||
"@babel/traverse": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -196,9 +197,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
|
||||
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -206,9 +207,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
|
||||
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -216,9 +217,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-option": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
|
||||
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
|
||||
"integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -226,27 +227,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helpers": {
|
||||
"version": "7.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
|
||||
"integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
|
||||
"integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/types": "^7.28.4"
|
||||
"@babel/template": "^7.29.7",
|
||||
"@babel/types": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
|
||||
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
|
||||
"integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.28.5"
|
||||
"@babel/types": "^7.29.7"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -297,33 +298,33 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.27.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
|
||||
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
|
||||
"integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/parser": "^7.27.2",
|
||||
"@babel/types": "^7.27.1"
|
||||
"@babel/code-frame": "^7.29.7",
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@babel/types": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
|
||||
"integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
|
||||
"integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
"@babel/helper-globals": "^7.28.0",
|
||||
"@babel/parser": "^7.28.5",
|
||||
"@babel/template": "^7.27.2",
|
||||
"@babel/types": "^7.28.5",
|
||||
"@babel/code-frame": "^7.29.7",
|
||||
"@babel/generator": "^7.29.7",
|
||||
"@babel/helper-globals": "^7.29.7",
|
||||
"@babel/parser": "^7.29.7",
|
||||
"@babel/template": "^7.29.7",
|
||||
"@babel/types": "^7.29.7",
|
||||
"debug": "^4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -331,14 +332,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
|
||||
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
|
||||
"version": "7.29.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
|
||||
"integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.27.1",
|
||||
"@babel/helper-validator-identifier": "^7.28.5"
|
||||
"@babel/helper-string-parser": "^7.29.7",
|
||||
"@babel/helper-validator-identifier": "^7.29.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -2570,13 +2571,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.8.25",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz",
|
||||
"integrity": "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==",
|
||||
"version": "2.10.38",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz",
|
||||
"integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"baseline-browser-mapping": "dist/cli.js"
|
||||
"baseline-browser-mapping": "dist/cli.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
@@ -2604,9 +2608,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.28.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz",
|
||||
"integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==",
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz",
|
||||
"integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -2624,11 +2628,11 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.25",
|
||||
"caniuse-lite": "^1.0.30001754",
|
||||
"electron-to-chromium": "^1.5.249",
|
||||
"node-releases": "^2.0.27",
|
||||
"update-browserslist-db": "^1.1.4"
|
||||
"baseline-browser-mapping": "^2.10.38",
|
||||
"caniuse-lite": "^1.0.30001799",
|
||||
"electron-to-chromium": "^1.5.376",
|
||||
"node-releases": "^2.0.48",
|
||||
"update-browserslist-db": "^1.2.3"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
@@ -2661,9 +2665,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001754",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz",
|
||||
"integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==",
|
||||
"version": "1.0.30001799",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
|
||||
"integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -2875,9 +2879,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.250",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.250.tgz",
|
||||
"integrity": "sha512-/5UMj9IiGDMOFBnN4i7/Ry5onJrAGSbOGo3s9FEKmwobGq6xw832ccET0CE3CkkMBZ8GJSlUIesZofpyurqDXw==",
|
||||
"version": "1.5.376",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.376.tgz",
|
||||
"integrity": "sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -3338,16 +3342,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
|
||||
"integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
"hasown": "^2.0.4",
|
||||
"mime-types": "^2.1.35"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
@@ -3537,9 +3541,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
|
||||
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
@@ -3742,10 +3746,20 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
|
||||
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/puzrin"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/nodeca"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
@@ -4292,11 +4306,14 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.27",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
||||
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
|
||||
"version": "2.0.48",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz",
|
||||
"integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
@@ -4540,6 +4557,16 @@
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-colorful": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.7.0.tgz",
|
||||
"integrity": "sha512-fuesYIemttah97XmsIHmz4OORDHiSFzyc9HMAIrCHJou2jaRQmL8cFJ76K4zQhhj8jzwOBlOi4BaGTjjOZCfTg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-date-object": {
|
||||
"version": "2.1.9",
|
||||
"resolved": "https://registry.npmjs.org/react-date-object/-/react-date-object-2.1.9.tgz",
|
||||
@@ -5137,9 +5164,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
|
||||
"integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==",
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
||||
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -5206,9 +5233,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.3.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz",
|
||||
"integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==",
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.5.tgz",
|
||||
"integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"moment-jalaali": "^0.10.4",
|
||||
"page-flip": "^2.0.7",
|
||||
"react": "^19.2.0",
|
||||
"react-colorful": "^5.7.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-dropzone": "^14.3.8",
|
||||
"react-i18next": "^16.3.0",
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.join(__dirname, "../src/assets/fonts/irancell/fonts-editor");
|
||||
const OUT_JSON = path.join(ROOT, "fonts.json");
|
||||
const OUT_CSS = path.join(ROOT, "fonts.css");
|
||||
|
||||
const WEIGHT_LABELS = {
|
||||
100: "Thin",
|
||||
200: "Extra Light",
|
||||
300: "Light",
|
||||
400: "Regular",
|
||||
500: "Medium",
|
||||
600: "Semi Bold",
|
||||
700: "Bold",
|
||||
800: "Extra Bold",
|
||||
900: "Black",
|
||||
};
|
||||
|
||||
/** @type {Array<{ id: string; label: string; cssFamily: string; category: "persian" | "english"; faces: Array<{ weight: number; src: string; format?: string }> }>} */
|
||||
const FONT_FAMILIES = [
|
||||
{
|
||||
id: "irancell",
|
||||
label: "ایرانسل",
|
||||
cssFamily: "editor-irancell",
|
||||
category: "persian",
|
||||
defaultWeight: "300",
|
||||
faces: [
|
||||
{ weight: 200, src: "../irancell-extralight.ttf" },
|
||||
{ weight: 300, src: "../irancell-light.ttf" },
|
||||
{ weight: 600, src: "../irancell-bold.ttf" },
|
||||
{ weight: 700, src: "../irancell-bold.ttf" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "peyda",
|
||||
label: "پیدا",
|
||||
cssFamily: "editor-peyda",
|
||||
category: "persian",
|
||||
faces: [
|
||||
{ weight: 100, src: "Persian Font/peyda/woff2/PeydaWeb-Thin.woff2", format: "woff2" },
|
||||
{ weight: 200, src: "Persian Font/peyda/woff2/peydaWeb-extralight.woff2", format: "woff2" },
|
||||
{ weight: 300, src: "Persian Font/peyda/woff2/peydaWeb-light.woff2", format: "woff2" },
|
||||
{ weight: 400, src: "Persian Font/peyda/woff2/PeydaWeb-Regular.woff2", format: "woff2" },
|
||||
{ weight: 500, src: "Persian Font/peyda/woff2/PeydaWeb-Medium.woff2", format: "woff2" },
|
||||
{ weight: 600, src: "Persian Font/peyda/woff2/PeydaWeb-SemiBold.woff2", format: "woff2" },
|
||||
{ weight: 700, src: "Persian Font/peyda/woff2/PeydaWeb-Bold.woff2", format: "woff2" },
|
||||
{ weight: 800, src: "Persian Font/peyda/woff2/PeydaWeb-ExtraBold.woff2", format: "woff2" },
|
||||
{ weight: 900, src: "Persian Font/peyda/woff2/PeydaWeb-Black.woff2", format: "woff2" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "iransans-fanum",
|
||||
label: "ایرانسنس (اعداد فارسی)",
|
||||
cssFamily: "editor-iransans-fanum",
|
||||
category: "persian",
|
||||
faces: [
|
||||
{ weight: 200, src: "Persian Font/IRANSansWeb(FaNum)_UltraLight.ttf" },
|
||||
{ weight: 300, src: "Persian Font/IRANSansWeb(FaNum)_Light.ttf" },
|
||||
{ weight: 400, src: "Persian Font/IRANSansWeb(FaNum).ttf" },
|
||||
{ weight: 500, src: "Persian Font/IRANSansWeb(FaNum)_Medium.ttf" },
|
||||
{ weight: 700, src: "Persian Font/IRANSansWeb(FaNum)_Bold.ttf" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "sahel",
|
||||
label: "ساحل",
|
||||
cssFamily: "editor-sahel",
|
||||
category: "persian",
|
||||
faces: [{ weight: 400, src: "Persian Font/Sahel-VF.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "yekan",
|
||||
label: "یکان",
|
||||
cssFamily: "editor-yekan",
|
||||
category: "persian",
|
||||
faces: [
|
||||
{ weight: 400, src: "Persian Font/Yekan.ttf" },
|
||||
{ weight: 700, src: "Persian Font/BYekan.ttf" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "btehran",
|
||||
label: "تهران",
|
||||
cssFamily: "editor-btehran",
|
||||
category: "persian",
|
||||
faces: [{ weight: 400, src: "Persian Font/BTehran.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "byagut",
|
||||
label: "یاقوت",
|
||||
cssFamily: "editor-byagut",
|
||||
category: "persian",
|
||||
faces: [{ weight: 400, src: "Persian Font/BYagut.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "byas",
|
||||
label: "یاس",
|
||||
cssFamily: "editor-byas",
|
||||
category: "persian",
|
||||
faces: [{ weight: 400, src: "Persian Font/BYas.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "bzar",
|
||||
label: "زر",
|
||||
cssFamily: "editor-bzar",
|
||||
category: "persian",
|
||||
faces: [{ weight: 700, src: "Persian Font/BZarBd.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "bkoodak",
|
||||
label: "کودک",
|
||||
cssFamily: "editor-bkoodak",
|
||||
category: "persian",
|
||||
faces: [{ weight: 700, src: "Persian Font/BKoodakBold.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "bbaran",
|
||||
label: "باران",
|
||||
cssFamily: "editor-bbaran",
|
||||
category: "persian",
|
||||
faces: [{ weight: 400, src: "Persian Font/BBaran.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "broya",
|
||||
label: "رویا",
|
||||
cssFamily: "editor-broya",
|
||||
category: "persian",
|
||||
faces: [{ weight: 400, src: "Persian Font/BRoya.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "bmahsa",
|
||||
label: "مهسا",
|
||||
cssFamily: "editor-bmahsa",
|
||||
category: "persian",
|
||||
faces: [{ weight: 400, src: "Persian Font/BMahsa.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "bmorvarid",
|
||||
label: "مروارید",
|
||||
cssFamily: "editor-bmorvarid",
|
||||
category: "persian",
|
||||
faces: [{ weight: 400, src: "Persian Font/BMorvarid.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "bbc-nassim",
|
||||
label: "نسیم",
|
||||
cssFamily: "editor-bbc-nassim",
|
||||
category: "persian",
|
||||
faces: [{ weight: 400, src: "Persian Font/BBCNassim.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "far-titr",
|
||||
label: "تیتر",
|
||||
cssFamily: "editor-far-titr",
|
||||
category: "persian",
|
||||
faces: [{ weight: 400, src: "Persian Font/Far_TitrDF.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "roboto",
|
||||
label: "Roboto",
|
||||
cssFamily: "editor-roboto",
|
||||
category: "english",
|
||||
faces: [
|
||||
{ weight: 100, src: "English Font/Roboto-Thin-webfont.ttf" },
|
||||
{ weight: 300, src: "English Font/Roboto-Light-webfont.ttf" },
|
||||
{ weight: 400, src: "English Font/Roboto-Regular-webfont.ttf" },
|
||||
{ weight: 500, src: "English Font/Roboto-Medium-webfont.ttf" },
|
||||
{ weight: 700, src: "English Font/Roboto-Bold-webfont.ttf" },
|
||||
{ weight: 900, src: "English Font/Roboto-Black-webfont.ttf" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "roboto-condensed",
|
||||
label: "Roboto Condensed",
|
||||
cssFamily: "editor-roboto-condensed",
|
||||
category: "english",
|
||||
faces: [
|
||||
{ weight: 400, src: "English Font/Roboto-Condensed-webfont.ttf" },
|
||||
{ weight: 700, src: "English Font/Roboto-BoldCondensed-webfont.ttf" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "vollkorn",
|
||||
label: "Vollkorn",
|
||||
cssFamily: "editor-vollkorn",
|
||||
category: "english",
|
||||
faces: [
|
||||
{ weight: 400, src: "English Font/Vollkorn-Regular.ttf" },
|
||||
{ weight: 500, src: "English Font/Vollkorn-Medium.ttf" },
|
||||
{ weight: 600, src: "English Font/Vollkorn-Semibold.ttf" },
|
||||
{ weight: 700, src: "English Font/Vollkorn-Bold.ttf" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "caviar-dreams",
|
||||
label: "Caviar Dreams",
|
||||
cssFamily: "editor-caviar-dreams",
|
||||
category: "english",
|
||||
faces: [
|
||||
{ weight: 400, src: "English Font/CaviarDreams.ttf" },
|
||||
{ weight: 700, src: "English Font/CaviarDreams_Bold.ttf" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "raleway",
|
||||
label: "Raleway",
|
||||
cssFamily: "editor-raleway",
|
||||
category: "english",
|
||||
faces: [
|
||||
{ weight: 200, src: "English Font/raleway-extralight-webfont.ttf" },
|
||||
{ weight: 400, src: "English Font/raleway-regular-webfont.ttf" },
|
||||
{ weight: 500, src: "English Font/raleway-medium-webfont.ttf" },
|
||||
{ weight: 600, src: "English Font/raleway-semibold-webfont.ttf" },
|
||||
{ weight: 700, src: "English Font/raleway-bold-webfont.ttf" },
|
||||
{ weight: 800, src: "English Font/raleway-extrabold-webfont.ttf" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "proxima-nova",
|
||||
label: "Proxima Nova",
|
||||
cssFamily: "editor-proxima-nova",
|
||||
category: "english",
|
||||
faces: [
|
||||
{ weight: 300, src: "English Font/proximanova-light-webfont.ttf" },
|
||||
{ weight: 400, src: "English Font/proximanova-reg-webfont.ttf" },
|
||||
{ weight: 600, src: "English Font/proximanova-semibold-webfont.ttf" },
|
||||
{ weight: 700, src: "English Font/proximanova-bold-webfont.ttf" },
|
||||
{ weight: 800, src: "English Font/proximanova-extrabold-webfont.ttf" },
|
||||
{ weight: 900, src: "English Font/proximanova-black-webfont.ttf" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "proxima-nova-condensed",
|
||||
label: "Proxima Nova Condensed",
|
||||
cssFamily: "editor-proxima-nova-condensed",
|
||||
category: "english",
|
||||
faces: [
|
||||
{ weight: 300, src: "English Font/proximanovacond-light-webfont.ttf" },
|
||||
{ weight: 600, src: "English Font/proximanovacond-semiboldit-webfont.ttf" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "din-pro",
|
||||
label: "DIN Pro",
|
||||
cssFamily: "editor-din-pro",
|
||||
category: "english",
|
||||
faces: [
|
||||
{ weight: 400, src: "English Font/dinpro-regular.ttf" },
|
||||
{ weight: 500, src: "English Font/dinpro-medium.ttf" },
|
||||
{ weight: 700, src: "English Font/dinpro-bold.ttf" },
|
||||
{ weight: 900, src: "English Font/dinpro-black.ttf" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "din-alternate",
|
||||
label: "DIN Alternate",
|
||||
cssFamily: "editor-din-alternate",
|
||||
category: "english",
|
||||
faces: [
|
||||
{ weight: 300, src: "English Font/din-lightalternate.ttf" },
|
||||
{ weight: 400, src: "English Font/din-regularalternate.ttf" },
|
||||
{ weight: 500, src: "English Font/din-mediumalternate.ttf" },
|
||||
{ weight: 700, src: "English Font/din-boldalternate.ttf" },
|
||||
{ weight: 900, src: "English Font/din-blackalternate.ttf" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "abril-fatface",
|
||||
label: "Abril Fatface",
|
||||
cssFamily: "editor-abril-fatface",
|
||||
category: "english",
|
||||
faces: [{ weight: 400, src: "English Font/AbrilFatface-Regular.otf", format: "opentype" }],
|
||||
},
|
||||
{
|
||||
id: "anton",
|
||||
label: "Anton",
|
||||
cssFamily: "editor-anton",
|
||||
category: "english",
|
||||
faces: [{ weight: 400, src: "English Font/Anton.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "bebas-neue",
|
||||
label: "Bebas Neue",
|
||||
cssFamily: "editor-bebas-neue",
|
||||
category: "english",
|
||||
faces: [{ weight: 400, src: "English Font/Bebas Neue.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "kepler-std",
|
||||
label: "Kepler Std",
|
||||
cssFamily: "editor-kepler-std",
|
||||
category: "english",
|
||||
faces: [{ weight: 400, src: "English Font/kepler-std.ttf" }],
|
||||
},
|
||||
{
|
||||
id: "playfair-display",
|
||||
label: "Playfair Display",
|
||||
cssFamily: "editor-playfair-display",
|
||||
category: "english",
|
||||
faces: [{ weight: 900, src: "English Font/playfairdisplay-black-webfont.ttf" }],
|
||||
},
|
||||
];
|
||||
|
||||
function encodeUrlSegment(segment) {
|
||||
return segment
|
||||
.split("/")
|
||||
.map((part) => encodeURIComponent(part))
|
||||
.join("/");
|
||||
}
|
||||
|
||||
function buildCss(families) {
|
||||
const blocks = families.flatMap((family) =>
|
||||
family.faces.map((face) => {
|
||||
const formatSuffix = face.format ? ` format("${face.format}")` : "";
|
||||
const url = encodeUrlSegment(face.src);
|
||||
return `@font-face {
|
||||
font-family: "${family.cssFamily}";
|
||||
font-style: normal;
|
||||
font-weight: ${face.weight};
|
||||
src: url("${url}")${formatSuffix};
|
||||
}`;
|
||||
}),
|
||||
);
|
||||
|
||||
return `/* Auto-generated by scripts/generate-editor-fonts.mjs — do not edit manually */\n\n${blocks.join("\n\n")}\n`;
|
||||
}
|
||||
|
||||
function buildJson(families) {
|
||||
const fonts = families.map((family) => {
|
||||
const srcToFace = new Map();
|
||||
for (const face of family.faces) {
|
||||
const existing = srcToFace.get(face.src);
|
||||
if (!existing || face.weight > existing.weight) {
|
||||
srcToFace.set(face.src, face);
|
||||
}
|
||||
}
|
||||
|
||||
const uniqueWeightFaces = [...srcToFace.values()].sort(
|
||||
(a, b) => a.weight - b.weight,
|
||||
);
|
||||
|
||||
const uniqueWeights = uniqueWeightFaces.map((f) => f.weight);
|
||||
|
||||
return {
|
||||
id: family.id,
|
||||
label: family.label,
|
||||
cssFamily: family.cssFamily,
|
||||
category: family.category,
|
||||
defaultWeight: family.defaultWeight
|
||||
? String(family.defaultWeight)
|
||||
: uniqueWeights.includes(400)
|
||||
? "400"
|
||||
: String(uniqueWeights[0]),
|
||||
weights: uniqueWeightFaces.map((face) => ({
|
||||
value: String(face.weight),
|
||||
label: WEIGHT_LABELS[face.weight] || String(face.weight),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
version: 1,
|
||||
fonts,
|
||||
};
|
||||
}
|
||||
|
||||
function verifySources(families) {
|
||||
const missing = [];
|
||||
for (const family of families) {
|
||||
for (const face of family.faces) {
|
||||
const resolved = face.src.startsWith("../")
|
||||
? path.join(ROOT, face.src)
|
||||
: path.join(ROOT, face.src);
|
||||
if (!fs.existsSync(resolved)) {
|
||||
missing.push(resolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (missing.length) {
|
||||
console.error("Missing font files:\n" + missing.map((m) => ` - ${m}`).join("\n"));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
verifySources(FONT_FAMILIES);
|
||||
|
||||
const json = buildJson(FONT_FAMILIES);
|
||||
const css = buildCss(FONT_FAMILIES);
|
||||
|
||||
fs.writeFileSync(OUT_JSON, JSON.stringify(json, null, 2) + "\n");
|
||||
fs.writeFileSync(OUT_CSS, css);
|
||||
|
||||
console.log(`Wrote ${OUT_JSON}`);
|
||||
console.log(`Wrote ${OUT_CSS}`);
|
||||
console.log(`Fonts: ${json.fonts.length}`);
|
||||
@@ -7,6 +7,7 @@ import { I18nextProvider } from "react-i18next";
|
||||
import "react-multi-date-picker/styles/layouts/mobile.css";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import "./assets/fonts/irancell/style.css";
|
||||
import "./assets/fonts/irancell/fonts-editor/fonts.css";
|
||||
import ToastContainer from "./components/Toast";
|
||||
import MainRouter from "./router/MainRouter";
|
||||
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user