Generated
+136
-120
@@ -54,13 +54,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 +69,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 +79,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 +110,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 +127,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 +144,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 +154,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 +196,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 +206,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 +216,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 +226,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 +297,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 +331,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 +2570,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 +2607,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 +2627,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 +2664,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 +2878,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 +3341,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 +3540,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 +3745,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 +4305,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",
|
||||
@@ -5137,9 +5153,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 +5222,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",
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { type FC, useRef, useCallback, useState, useEffect, useMemo, useSyncExternalStore } from 'react';
|
||||
import HTMLFlipBook from 'react-pageflip';
|
||||
import { type PageData } from '../types';
|
||||
import BookPage from './BookPage';
|
||||
import { ArrowLeft2, ArrowRight2, Maximize4, Pause, Play, RowVertical } from 'iconsax-react';
|
||||
import { getPaperDimensions } from '@/config/paperSizes';
|
||||
import type { DocumentSettings } from '@/pages/editor/store/editorStore';
|
||||
import { useBookEntranceController } from '@/pages/viewer/hooks/useBookEntranceController';
|
||||
import { useViewerViewport } from '@/pages/viewer/hooks/useViewerViewport';
|
||||
import { useIsBrowserFullscreen } from '@/pages/viewer/hooks/useIsBrowserFullscreen';
|
||||
import { toggleViewerFullscreen } from '@/pages/viewer/utils/viewerFullscreen';
|
||||
import { isLegacyIOSSafari } from '@/pages/viewer/utils/isLegacyIOSSafari';
|
||||
import { clx } from '@/helpers/utils';
|
||||
import { getPaperDimensions } from "@/config/paperSizes";
|
||||
import { clx } from "@/helpers/utils";
|
||||
import type { DocumentSettings } from "@/pages/editor/store/editorStore";
|
||||
import { useBookEntranceController } from "@/pages/viewer/hooks/useBookEntranceController";
|
||||
import { useIsBrowserFullscreen } from "@/pages/viewer/hooks/useIsBrowserFullscreen";
|
||||
import { useViewerViewport } from "@/pages/viewer/hooks/useViewerViewport";
|
||||
import { isLegacyIOSSafari } from "@/pages/viewer/utils/isLegacyIOSSafari";
|
||||
import { toggleViewerFullscreen } from "@/pages/viewer/utils/viewerFullscreen";
|
||||
import { ArrowLeft2, ArrowRight2, Maximize4, Pause, Play, RowVertical } from "iconsax-react";
|
||||
import { type FC, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
||||
import HTMLFlipBook from "react-pageflip";
|
||||
import { type PageData } from "../types";
|
||||
import BookPage from "./BookPage";
|
||||
|
||||
/** فاصله پیشفرض بین هر ورق در پخش خودکار (میلیثانیه) */
|
||||
const AUTO_PLAY_INTERVAL_MS = 3000;
|
||||
@@ -22,19 +22,19 @@ const BOOK_TOP_GAP_MOBILE = 32;
|
||||
/** نسخهٔ فعلی `page-flip.mp3` دو ضربهٔ ورق پشتهم دارد؛ فقط تا این سهم از طول پخش میشود */
|
||||
const PAGE_FLIP_SOUND_PLAY_FRACTION = 0.5;
|
||||
/** همراستا با `md` تیلویند (768px) — موبایل: زیر این عرض */
|
||||
const MOBILE_QUERY = '(min-width: 768px)';
|
||||
const MOBILE_QUERY = "(min-width: 768px)";
|
||||
|
||||
function subscribeDesktopMql(callback: () => void) {
|
||||
const mq = window.matchMedia(MOBILE_QUERY);
|
||||
mq.addEventListener('change', callback);
|
||||
return () => mq.removeEventListener('change', callback);
|
||||
mq.addEventListener("change", callback);
|
||||
return () => mq.removeEventListener("change", callback);
|
||||
}
|
||||
|
||||
function useIsDesktop() {
|
||||
return useSyncExternalStore(
|
||||
subscribeDesktopMql,
|
||||
() => window.matchMedia(MOBILE_QUERY).matches,
|
||||
() => true
|
||||
() => true,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ function useLegacyIOSSafari() {
|
||||
return useSyncExternalStore(
|
||||
() => () => {},
|
||||
isLegacyIOSSafari,
|
||||
() => false
|
||||
() => false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -88,14 +88,10 @@ const AutoPlayToggleButton: FC<AutoPlayToggleButtonProps> = ({ isActive, onToggl
|
||||
type="button"
|
||||
onClick={onToggle}
|
||||
className="px-3 py-1.5 text-xs md:text-sm font-medium inline-flex items-center justify-center gap-1.5 rounded-full border border-gray-300 hover:bg-gray-100 active:bg-gray-200 transition-all"
|
||||
aria-label={isActive ? 'توقف پخش خودکار' : 'شروع پخش خودکار'}
|
||||
aria-label={isActive ? "توقف پخش خودکار" : "شروع پخش خودکار"}
|
||||
>
|
||||
{isActive ? (
|
||||
<Pause color='black' size={16} className="text-gray-700 md:w-[18px] md:h-[18px]" />
|
||||
) : (
|
||||
<Play color='black' size={16} className="text-gray-700 md:w-[18px] md:h-[18px]" />
|
||||
)}
|
||||
<span>{isActive ? 'توقف پخش' : 'پخش خودکار'}</span>
|
||||
{isActive ? <Pause color="black" size={16} className="text-gray-700 md:w-[18px] md:h-[18px]" /> : <Play color="black" size={16} className="text-gray-700 md:w-[18px] md:h-[18px]" />}
|
||||
<span>{isActive ? "توقف پخش" : "پخش خودکار"}</span>
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -133,24 +129,26 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
const autoPlay = documentSettings?.autoPlay ?? false;
|
||||
const pageFlipSound = documentSettings?.pageFlipSound ?? true;
|
||||
const leftToRightFlip = documentSettings?.leftToRightFlip ?? false;
|
||||
const displayStyle = documentSettings?.displayStyle ?? 'double';
|
||||
const backgroundType = documentSettings?.backgroundType ?? 'color';
|
||||
const backgroundColor = documentSettings?.backgroundColor ?? '#ffffff';
|
||||
/** react-pageflip از RTL پشتیبانی نمیکند؛ ترتیب صفحات برعکس + شروع از آخر (فقط دسکتاپ) */
|
||||
const isRtlBook = !leftToRightFlip && isDesktop;
|
||||
const displayPages = useMemo(() => (isRtlBook ? [...pages].reverse() : pages), [pages, isRtlBook]);
|
||||
const toLogicalIndex = useCallback((flipIndex: number) => (isRtlBook ? pages.length - 1 - flipIndex : flipIndex), [isRtlBook, pages.length]);
|
||||
const toFlipIndex = useCallback((logicalIndex: number) => (isRtlBook ? pages.length - 1 - logicalIndex : logicalIndex), [isRtlBook, pages.length]);
|
||||
const displayStyle = documentSettings?.displayStyle ?? "double";
|
||||
const backgroundType = documentSettings?.backgroundType ?? "color";
|
||||
const backgroundColor = documentSettings?.backgroundColor ?? "#ffffff";
|
||||
const backgroundGradient = documentSettings?.backgroundGradient;
|
||||
const backgroundImageUrl = documentSettings?.backgroundImageUrl ?? '';
|
||||
const backgroundImageUrl = documentSettings?.backgroundImageUrl ?? "";
|
||||
|
||||
useEffect(() => {
|
||||
setIsAutoPlayActive(autoPlay);
|
||||
}, [autoPlay]);
|
||||
|
||||
// سایز کتاب بر اساس catalogSize (A3/A4/A5)
|
||||
const { width: bookWidth, height: bookHeight } = useMemo(
|
||||
() => getPaperDimensions(catalogSize ?? 'a4'),
|
||||
[catalogSize]
|
||||
);
|
||||
const { width: bookWidth, height: bookHeight } = useMemo(() => getPaperDimensions(catalogSize ?? "a4"), [catalogSize]);
|
||||
const hasMultiplePages = pages.length > 1;
|
||||
const viewport = useViewerViewport();
|
||||
const usePortrait = !isDesktop || displayStyle === 'single';
|
||||
const usePortrait = !isDesktop || displayStyle === "single";
|
||||
|
||||
const viewportFit = useMemo(() => {
|
||||
const padX = isDesktop ? 48 : 20;
|
||||
@@ -175,7 +173,7 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
const pagePixelHeight = viewportFit.h;
|
||||
const contentScale = viewportFit.contentScale;
|
||||
|
||||
const flipKey = `${usePortrait ? 'portrait' : 'spread'}-${pagePixelWidth}-${pagePixelHeight}`;
|
||||
const flipKey = `${usePortrait ? "portrait" : "spread"}-${pagePixelWidth}-${pagePixelHeight}`;
|
||||
|
||||
const {
|
||||
scheduleEntranceForSpread,
|
||||
@@ -190,10 +188,10 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
const commitEntranceAtCurrentSpread = useCallback(() => {
|
||||
const api = bookRef.current?.pageFlip();
|
||||
const idx = api?.getCurrentPageIndex?.();
|
||||
if (typeof idx !== 'number' || pages.length <= 0) return;
|
||||
const clamped = Math.max(0, Math.min(idx, pages.length - 1));
|
||||
scheduleEntranceForSpread(clamped);
|
||||
}, [pages.length, scheduleEntranceForSpread]);
|
||||
if (typeof idx !== "number" || pages.length <= 0) return;
|
||||
const flipIdx = Math.max(0, Math.min(idx, pages.length - 1));
|
||||
scheduleEntranceForSpread(toLogicalIndex(flipIdx));
|
||||
}, [pages.length, scheduleEntranceForSpread, toLogicalIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
resetEntrance();
|
||||
@@ -218,14 +216,14 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
|
||||
useEffect(() => {
|
||||
const base = import.meta.env.BASE_URL;
|
||||
const src = `${base}${base.endsWith('/') ? '' : '/'}sounds/page-flip.mp3`;
|
||||
const src = `${base}${base.endsWith("/") ? "" : "/"}sounds/page-flip.mp3`;
|
||||
const audio = new Audio(src);
|
||||
audio.preload = 'auto';
|
||||
audio.preload = "auto";
|
||||
pageFlipSoundRef.current = audio;
|
||||
return () => {
|
||||
const trim = pageFlipSoundTrimListenerRef.current;
|
||||
if (trim) {
|
||||
audio.removeEventListener('timeupdate', trim);
|
||||
audio.removeEventListener("timeupdate", trim);
|
||||
pageFlipSoundTrimListenerRef.current = null;
|
||||
}
|
||||
audio.pause();
|
||||
@@ -238,7 +236,7 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
if (!audio) return;
|
||||
const prevTrim = pageFlipSoundTrimListenerRef.current;
|
||||
if (prevTrim) {
|
||||
audio.removeEventListener('timeupdate', prevTrim);
|
||||
audio.removeEventListener("timeupdate", prevTrim);
|
||||
pageFlipSoundTrimListenerRef.current = null;
|
||||
}
|
||||
const onTimeUpdate = () => {
|
||||
@@ -248,44 +246,49 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
if (audio.currentTime >= end) {
|
||||
audio.pause();
|
||||
audio.currentTime = 0;
|
||||
audio.removeEventListener('timeupdate', onTimeUpdate);
|
||||
audio.removeEventListener("timeupdate", onTimeUpdate);
|
||||
pageFlipSoundTrimListenerRef.current = null;
|
||||
}
|
||||
};
|
||||
pageFlipSoundTrimListenerRef.current = onTimeUpdate;
|
||||
try {
|
||||
audio.currentTime = 0;
|
||||
audio.addEventListener('timeupdate', onTimeUpdate);
|
||||
audio.addEventListener("timeupdate", onTimeUpdate);
|
||||
void audio.play().catch(() => {
|
||||
audio.removeEventListener('timeupdate', onTimeUpdate);
|
||||
audio.removeEventListener("timeupdate", onTimeUpdate);
|
||||
pageFlipSoundTrimListenerRef.current = null;
|
||||
/* autoplay policy — معمولاً بعد از تعامل کاربر ورق میخورد */
|
||||
});
|
||||
} catch {
|
||||
audio.removeEventListener('timeupdate', onTimeUpdate);
|
||||
audio.removeEventListener("timeupdate", onTimeUpdate);
|
||||
pageFlipSoundTrimListenerRef.current = null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handlePageFlip = useCallback((e: FlipEvent) => {
|
||||
const handlePageFlip = useCallback(
|
||||
(e: FlipEvent) => {
|
||||
// react-pageflip شماره صفحه را به صورت 0-based برمیگرداند
|
||||
// اما باید مطمئن شویم که در محدوده معتبر است
|
||||
let pageNum = e.data;
|
||||
let flipIndex = e.data;
|
||||
|
||||
// اگر pageNum منفی است یا بزرگتر از تعداد صفحات، آن را محدود میکنیم
|
||||
if (pageNum < 0) {
|
||||
pageNum = 0;
|
||||
} else if (pageNum >= pages.length) {
|
||||
pageNum = pages.length - 1;
|
||||
// اگر flipIndex منفی است یا بزرگتر از تعداد صفحات، آن را محدود میکنیم
|
||||
if (flipIndex < 0) {
|
||||
flipIndex = 0;
|
||||
} else if (flipIndex >= pages.length) {
|
||||
flipIndex = pages.length - 1;
|
||||
}
|
||||
|
||||
const pageNum = toLogicalIndex(flipIndex);
|
||||
|
||||
if (pageFlipSound && lastFlipPageRef.current !== null && lastFlipPageRef.current !== pageNum) {
|
||||
playPageFlipSound();
|
||||
}
|
||||
lastFlipPageRef.current = pageNum;
|
||||
|
||||
setCurrentPage(pageNum);
|
||||
}, [pages.length, pageFlipSound, playPageFlipSound]);
|
||||
},
|
||||
[pages.length, pageFlipSound, playPageFlipSound, toLogicalIndex],
|
||||
);
|
||||
|
||||
pageFlipHandlerRef.current = handlePageFlip;
|
||||
|
||||
@@ -296,28 +299,29 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
const syncPageIndexFromBook = useCallback(() => {
|
||||
const api = bookRef.current?.pageFlip();
|
||||
const idx = api?.getCurrentPageIndex?.();
|
||||
if (typeof idx !== 'number' || pages.length <= 0) return;
|
||||
const clamped = Math.max(0, Math.min(idx, pages.length - 1));
|
||||
setCurrentPage(clamped);
|
||||
}, [pages.length]);
|
||||
if (typeof idx !== "number" || pages.length <= 0) return;
|
||||
const flipIdx = Math.max(0, Math.min(idx, pages.length - 1));
|
||||
setCurrentPage(toLogicalIndex(flipIdx));
|
||||
}, [pages.length, toLogicalIndex]);
|
||||
|
||||
const handleBookInit = useCallback(
|
||||
(e: PageFlipInitEvent) => {
|
||||
const p = e.data?.page;
|
||||
if (typeof p !== 'number' || pages.length <= 0) return;
|
||||
const clamped = Math.max(0, Math.min(p, pages.length - 1));
|
||||
setCurrentPage(clamped);
|
||||
lastFlipPageRef.current = clamped;
|
||||
if (typeof p !== "number" || pages.length <= 0) return;
|
||||
const flipIdx = Math.max(0, Math.min(p, pages.length - 1));
|
||||
const logicalIdx = toLogicalIndex(flipIdx);
|
||||
setCurrentPage(logicalIdx);
|
||||
lastFlipPageRef.current = logicalIdx;
|
||||
// اولین اسپرد قبل از onChangeState('read') هم باید انیمیشن ورود بگیرد
|
||||
scheduleEntranceForSpread(clamped);
|
||||
scheduleEntranceForSpread(logicalIdx);
|
||||
},
|
||||
[pages.length, scheduleEntranceForSpread],
|
||||
[pages.length, scheduleEntranceForSpread, toLogicalIndex],
|
||||
);
|
||||
|
||||
/** بعد از اتمام انیمیشن ورق، ایندکس واقعی کتاب را میگیریم و انیمیشن ورود را یکبار شروع میکنیم */
|
||||
const handleChangeState = useCallback(
|
||||
(e: PageFlipStateEvent) => {
|
||||
if (e.data !== 'read') return;
|
||||
if (e.data !== "read") return;
|
||||
syncPageIndexFromBook();
|
||||
commitEntranceAtCurrentSpread();
|
||||
},
|
||||
@@ -325,16 +329,24 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
);
|
||||
|
||||
const goToNextPage = useCallback(() => {
|
||||
if (currentPage < pages.length - 1) {
|
||||
bookRef.current?.pageFlip()?.flipNext();
|
||||
if (currentPage >= pages.length - 1) return;
|
||||
const api = bookRef.current?.pageFlip() as PageFlipWithFlipController | undefined;
|
||||
if (isRtlBook) {
|
||||
triggerFlipPrev(api);
|
||||
} else {
|
||||
api?.flipNext();
|
||||
}
|
||||
}, [currentPage, pages.length]);
|
||||
}, [currentPage, pages.length, isRtlBook]);
|
||||
|
||||
const goToPrevPage = useCallback(() => {
|
||||
if (currentPage <= 0) return;
|
||||
const api = bookRef.current?.pageFlip() as PageFlipWithFlipController | undefined;
|
||||
if (isRtlBook) {
|
||||
api?.flipNext();
|
||||
} else {
|
||||
triggerFlipPrev(api);
|
||||
}, [currentPage]);
|
||||
}
|
||||
}, [currentPage, isRtlBook]);
|
||||
|
||||
const stopAutoPlayByUser = useCallback(() => {
|
||||
if (isAutoPlayActive) {
|
||||
@@ -344,7 +356,7 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
|
||||
const toggleAutoPlay = useCallback(() => {
|
||||
if (pages.length <= 1) return;
|
||||
setIsAutoPlayActive(prev => !prev);
|
||||
setIsAutoPlayActive((prev) => !prev);
|
||||
}, [pages.length]);
|
||||
|
||||
const navigateToPageIndex = useCallback(
|
||||
@@ -352,12 +364,13 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
const pageFlipAPI = bookRef.current?.pageFlip();
|
||||
if (!pageFlipAPI) return;
|
||||
|
||||
const clamped = Math.max(0, Math.min(targetIndex, pages.length - 1));
|
||||
pageFlipAPI.turnToPage(clamped);
|
||||
lastFlipPageRef.current = clamped;
|
||||
scheduleEntranceForSpread(clamped);
|
||||
const logicalIdx = Math.max(0, Math.min(targetIndex, pages.length - 1));
|
||||
const flipIdx = toFlipIndex(logicalIdx);
|
||||
pageFlipAPI.turnToPage(flipIdx);
|
||||
lastFlipPageRef.current = logicalIdx;
|
||||
scheduleEntranceForSpread(logicalIdx);
|
||||
},
|
||||
[pages.length, scheduleEntranceForSpread],
|
||||
[pages.length, scheduleEntranceForSpread, toFlipIndex],
|
||||
);
|
||||
|
||||
const handleLinkClick = useCallback(
|
||||
@@ -365,17 +378,17 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
if (pages.length <= 1) return;
|
||||
stopAutoPlayByUser();
|
||||
|
||||
if (!linkUrl.startsWith('page://')) return;
|
||||
if (!linkUrl.startsWith("page://")) return;
|
||||
|
||||
const action = linkUrl.replace('page://', '');
|
||||
const action = linkUrl.replace("page://", "");
|
||||
|
||||
if (action === 'next') {
|
||||
if (action === "next") {
|
||||
goToNextPage();
|
||||
} else if (action === 'prev') {
|
||||
} else if (action === "prev") {
|
||||
goToPrevPage();
|
||||
} else if (action === 'first') {
|
||||
} else if (action === "first") {
|
||||
navigateToPageIndex(0);
|
||||
} else if (action === 'last') {
|
||||
} else if (action === "last") {
|
||||
navigateToPageIndex(pages.length - 1);
|
||||
} else {
|
||||
const pageNum = parseInt(action, 10);
|
||||
@@ -392,40 +405,49 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
if (prevFlipKeyRef.current !== flipKey) {
|
||||
prevFlipKeyRef.current = flipKey;
|
||||
lastFlipPageRef.current = currentPage;
|
||||
setStartPage(currentPage);
|
||||
setStartPage(toFlipIndex(currentPage));
|
||||
}
|
||||
}, [flipKey, currentPage]);
|
||||
}, [flipKey, currentPage, toFlipIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
if (pages.length <= 0) return;
|
||||
const initialFlipPage = isRtlBook ? pages.length - 1 : 0;
|
||||
setStartPage(initialFlipPage);
|
||||
setCurrentPage(0);
|
||||
lastFlipPageRef.current = 0;
|
||||
}, [pages.length, pages[0]?.id, isRtlBook]);
|
||||
|
||||
// پخش خودکار
|
||||
useEffect(() => {
|
||||
if (!isAutoPlayActive || pages.length <= 1) return;
|
||||
const timer = setInterval(() => {
|
||||
if (currentPage >= pages.length - 1) {
|
||||
bookRef.current?.pageFlip()?.turnToPage?.(0);
|
||||
const firstFlipPage = isRtlBook ? pages.length - 1 : 0;
|
||||
bookRef.current?.pageFlip()?.turnToPage?.(firstFlipPage);
|
||||
setCurrentPage(0);
|
||||
lastFlipPageRef.current = 0;
|
||||
return;
|
||||
}
|
||||
bookRef.current?.pageFlip()?.flipNext();
|
||||
const api = bookRef.current?.pageFlip() as PageFlipWithFlipController | undefined;
|
||||
if (isRtlBook) {
|
||||
triggerFlipPrev(api);
|
||||
} else {
|
||||
api?.flipNext();
|
||||
}
|
||||
}, AUTO_PLAY_INTERVAL_MS);
|
||||
return () => clearInterval(timer);
|
||||
}, [isAutoPlayActive, currentPage, pages.length]);
|
||||
}, [isAutoPlayActive, currentPage, pages.length, isRtlBook]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col items-center gap-4 md:gap-6 w-full h-full max-md:min-h-0 max-md:max-w-full max-md:overflow-x-hidden max-md:pb-[env(safe-area-inset-bottom,0px)]"
|
||||
dir="rtl"
|
||||
>
|
||||
<div
|
||||
className="relative w-full flex flex-col flex-1 max-md:min-h-0 pt-8 md:pt-12 min-h-0 overflow-hidden"
|
||||
>
|
||||
<div className="flex flex-col items-center gap-4 md:gap-6 w-full h-full max-md:min-h-0 max-md:max-w-full max-md:overflow-x-hidden max-md:pb-[env(safe-area-inset-bottom,0px)]" dir="rtl">
|
||||
<div className="relative w-full flex flex-col flex-1 max-md:min-h-0 pt-8 md:pt-12 min-h-0 overflow-hidden">
|
||||
<div className="relative flex flex-1 min-h-0 w-full items-center justify-center">
|
||||
{/* Border و Shadow عمودی در وسط (فقط حالت دوصفحهای دسکتاپ) */}
|
||||
{!usePortrait && (
|
||||
<div
|
||||
className="hidden md:block absolute inset-y-0 left-1/2 -translate-x-1/2 w-px border-r border-gray-300 pointer-events-none z-50"
|
||||
style={{
|
||||
boxShadow: '-5px 0 15px rgba(0, 0, 0, 0.15), 5px 0 15px rgba(0, 0, 0, 0.15)',
|
||||
boxShadow: "-5px 0 15px rgba(0, 0, 0, 0.15), 5px 0 15px rgba(0, 0, 0, 0.15)",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -437,12 +459,10 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
dir="ltr"
|
||||
className="max-w-full flex justify-center shrink-0 rounded-sm"
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
overflow: "hidden",
|
||||
width: usePortrait ? pagePixelWidth : pagePixelWidth * 2,
|
||||
height: pagePixelHeight,
|
||||
boxShadow: isDesktop
|
||||
? undefined
|
||||
: '0 8px 32px rgba(0, 0, 0, 0.14), 0 2px 8px rgba(0, 0, 0, 0.08)',
|
||||
boxShadow: isDesktop ? undefined : "0 8px 32px rgba(0, 0, 0, 0.14), 0 2px 8px rgba(0, 0, 0, 0.08)",
|
||||
}}
|
||||
>
|
||||
<HTMLFlipBook
|
||||
@@ -469,13 +489,13 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
startZIndex={1}
|
||||
showPageCorners={hasMultiplePages}
|
||||
disableFlipByClick={true}
|
||||
className={clx('flipbook-container mx-auto max-w-full', legacyIOS && 'flipbook-legacy-ios')}
|
||||
className={clx("flipbook-container mx-auto max-w-full", legacyIOS && "flipbook-legacy-ios")}
|
||||
style={{}}
|
||||
onFlip={onFlipForwarded}
|
||||
onInit={handleBookInit}
|
||||
onChangeState={handleChangeState}
|
||||
>
|
||||
{pages.map((page) => (
|
||||
{displayPages.map((page) => (
|
||||
<BookPage
|
||||
key={page.id}
|
||||
page={page}
|
||||
@@ -512,9 +532,9 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
}}
|
||||
disabled={leftToRightFlip ? currentPage <= 0 : currentPage >= pages.length - 1}
|
||||
className="p-2 md:p-2 min-h-11 min-w-11 md:min-h-0 md:min-w-0 inline-flex items-center justify-center rounded-full hover:bg-gray-100 active:bg-gray-200 disabled:opacity-30 disabled:cursor-not-allowed transition-all"
|
||||
aria-label={leftToRightFlip ? 'صفحه قبل' : 'صفحه بعد'}
|
||||
aria-label={leftToRightFlip ? "صفحه قبل" : "صفحه بعد"}
|
||||
>
|
||||
<ArrowRight2 color='black' size={20} className="text-gray-700 md:w-6 md:h-6" />
|
||||
<ArrowRight2 color="black" size={20} className="text-gray-700 md:w-6 md:h-6" />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
@@ -526,8 +546,6 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{hasMultiplePages && (
|
||||
<>
|
||||
{/* دکمه چپ: فارسی = صفحه قبل | لاتین = صفحه بعد */}
|
||||
@@ -543,29 +561,23 @@ const BookViewer: FC<BookViewerProps> = ({ pages, catalogSize, documentSettings
|
||||
}}
|
||||
disabled={leftToRightFlip ? currentPage >= pages.length - 1 : currentPage <= 0}
|
||||
className="p-2 md:p-2 min-h-11 min-w-11 md:min-h-0 md:min-w-0 inline-flex items-center justify-center rounded-full hover:bg-gray-100 active:bg-gray-200 disabled:opacity-30 disabled:cursor-not-allowed transition-all"
|
||||
aria-label={leftToRightFlip ? 'صفحه بعد' : 'صفحه قبل'}
|
||||
aria-label={leftToRightFlip ? "صفحه بعد" : "صفحه قبل"}
|
||||
>
|
||||
<ArrowLeft2 color='black' size={20} className="text-gray-700 md:w-6 md:h-6" />
|
||||
<ArrowLeft2 color="black" size={20} className="text-gray-700 md:w-6 md:h-6" />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{hasMultiplePages && autoPlay && (
|
||||
<AutoPlayToggleButton isActive={isAutoPlayActive} onToggle={toggleAutoPlay} />
|
||||
)}
|
||||
{hasMultiplePages && autoPlay && <AutoPlayToggleButton isActive={isAutoPlayActive} onToggle={toggleAutoPlay} />}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleViewerFullscreen}
|
||||
className="p-2 md:p-2 min-h-11 min-w-11 md:min-h-0 md:min-w-0 inline-flex items-center justify-center rounded-full hover:bg-gray-100 active:bg-gray-200 transition-all"
|
||||
aria-label={isBrowserFullscreen ? 'خروج از تمامصفحه' : 'تمامصفحه مرورگر'}
|
||||
title={isBrowserFullscreen ? 'خروج از تمامصفحه' : 'تمامصفحه مرورگر'}
|
||||
aria-label={isBrowserFullscreen ? "خروج از تمامصفحه" : "تمامصفحه مرورگر"}
|
||||
title={isBrowserFullscreen ? "خروج از تمامصفحه" : "تمامصفحه مرورگر"}
|
||||
>
|
||||
{isBrowserFullscreen ? (
|
||||
<RowVertical color="black" size={20} className="text-gray-700 md:w-6 md:h-6" />
|
||||
) : (
|
||||
<Maximize4 color="black" size={20} className="text-gray-700 md:w-6 md:h-6" />
|
||||
)}
|
||||
{isBrowserFullscreen ? <RowVertical color="black" size={20} className="text-gray-700 md:w-6 md:h-6" /> : <Maximize4 color="black" size={20} className="text-gray-700 md:w-6 md:h-6" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user