mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-11-16 01:21:16 +01:00
Viewer update and autozoom
This commit is contained in:
parent
8060934ee9
commit
1bc8e7613f
385
frontend/package-lock.json
generated
385
frontend/package-lock.json
generated
@ -10,24 +10,24 @@
|
||||
"license": "SEE LICENSE IN https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/refs/heads/main/proprietary/LICENSE",
|
||||
"dependencies": {
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.7.7",
|
||||
"@embedpdf/core": "^1.3.14",
|
||||
"@embedpdf/engines": "^1.3.14",
|
||||
"@embedpdf/plugin-annotation": "^1.3.14",
|
||||
"@embedpdf/plugin-export": "^1.3.14",
|
||||
"@embedpdf/plugin-history": "^1.3.14",
|
||||
"@embedpdf/plugin-interaction-manager": "^1.3.14",
|
||||
"@embedpdf/plugin-loader": "^1.3.14",
|
||||
"@embedpdf/plugin-pan": "^1.3.14",
|
||||
"@embedpdf/plugin-render": "^1.3.14",
|
||||
"@embedpdf/plugin-rotate": "^1.3.14",
|
||||
"@embedpdf/plugin-scroll": "^1.3.14",
|
||||
"@embedpdf/plugin-search": "^1.3.14",
|
||||
"@embedpdf/plugin-selection": "^1.3.14",
|
||||
"@embedpdf/plugin-spread": "^1.3.14",
|
||||
"@embedpdf/plugin-thumbnail": "^1.3.14",
|
||||
"@embedpdf/plugin-tiling": "^1.3.14",
|
||||
"@embedpdf/plugin-viewport": "^1.3.14",
|
||||
"@embedpdf/plugin-zoom": "^1.3.14",
|
||||
"@embedpdf/core": "^1.4.1",
|
||||
"@embedpdf/engines": "^1.4.1",
|
||||
"@embedpdf/plugin-annotation": "^1.4.1",
|
||||
"@embedpdf/plugin-export": "^1.4.1",
|
||||
"@embedpdf/plugin-history": "^1.4.1",
|
||||
"@embedpdf/plugin-interaction-manager": "^1.4.1",
|
||||
"@embedpdf/plugin-loader": "^1.4.1",
|
||||
"@embedpdf/plugin-pan": "^1.4.1",
|
||||
"@embedpdf/plugin-render": "^1.4.1",
|
||||
"@embedpdf/plugin-rotate": "^1.4.1",
|
||||
"@embedpdf/plugin-scroll": "^1.4.1",
|
||||
"@embedpdf/plugin-search": "^1.4.1",
|
||||
"@embedpdf/plugin-selection": "^1.4.1",
|
||||
"@embedpdf/plugin-spread": "^1.4.1",
|
||||
"@embedpdf/plugin-thumbnail": "^1.4.1",
|
||||
"@embedpdf/plugin-tiling": "^1.4.1",
|
||||
"@embedpdf/plugin-viewport": "^1.4.1",
|
||||
"@embedpdf/plugin-zoom": "^1.4.1",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@iconify/react": "^6.0.2",
|
||||
@ -502,63 +502,65 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/core": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/core/-/core-1.3.14.tgz",
|
||||
"integrity": "sha512-lE/vfhA53CxamaCfGWEibrEPr+JeZT42QCF+cOELUwv4+Zt6b+IE6+4wsznx/8wjjJYwllXJ3GJ/un1UzTqARw==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/core/-/core-1.4.1.tgz",
|
||||
"integrity": "sha512-TGpxn2CvAKRnOJWJ3bsK+dKBiCp75ehxftRUmv7wAmPomhnG5XrDfoWJungvO+zbbqAwso6PocdeXINVt3hlAw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/engines": "1.3.14",
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/engines": "1.4.1",
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/engines": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/engines/-/engines-1.3.14.tgz",
|
||||
"integrity": "sha512-+/FPW2gAzj2lQYvsMH/Oj9+MEXgkyEuyYDC+HFkltTuXvmiP2S/3BD0YslZDX9K4BzcmMxnWB+BiQpNJokbDVg==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/engines/-/engines-1.4.1.tgz",
|
||||
"integrity": "sha512-yugIb5OwTI/1VnAaEvSYxAd2DvYBPkV/D7wytagyaOq98o3sqzcY2Q9zHt+LhnawA5KKG1e/FDPjCd4qm8gsvg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14",
|
||||
"@embedpdf/pdfium": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1",
|
||||
"@embedpdf/pdfium": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/models": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/models/-/models-1.3.14.tgz",
|
||||
"integrity": "sha512-BujY4bmr8b2DQdoZkOge03SzoRVoWxzfIQATLSPPtp4WiFh1U4BPp6cADlGuCwGkp6zBcH/aM4h8PwwA75d/eg==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/models/-/models-1.4.1.tgz",
|
||||
"integrity": "sha512-2nTg8Q1qpplBvspZJXMCZOA+/OILpfdNRPddlplxZXY/Upx0rzKXx/e6pXWW7AuOgtfGneT4h9tMs3A595/PdQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@embedpdf/pdfium": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/pdfium/-/pdfium-1.3.14.tgz",
|
||||
"integrity": "sha512-TQMZabXzHmzvvfPwopubFcYgQuYV7POvMgjICYu3Pgfn3sgr+UdIUh3aNXR/COcl3q8sXPMFQ2GDuyOHR9QQnA==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/pdfium/-/pdfium-1.4.1.tgz",
|
||||
"integrity": "sha512-BekKEK4UNCwzj7xOffKn6WpL0FQHxq+mTj2iGI3N7OwAX2J/BO2G+rDOB+lvojQG+Dkpg8uqm427ZKJDRyLgVQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-annotation": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-annotation/-/plugin-annotation-1.3.14.tgz",
|
||||
"integrity": "sha512-JJYqEWwUKCdBZsXCDq/CW96p3pVLn8N+XZ4W3OyL7djI2fvYC9x6ys9m82vwlSathAVOxk1D7xXiY8AzJQVF0Q==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-annotation/-/plugin-annotation-1.4.1.tgz",
|
||||
"integrity": "sha512-d4HibNy6ecyDqx2Y2R8VjaqppSdjNofAJmU6VenOd88wn080sAUqvnkeVJ6ehJH5BoND4ymQrcAkcbVeYK0myA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14",
|
||||
"@embedpdf/utils": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1",
|
||||
"@embedpdf/utils": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/plugin-history": "1.3.14",
|
||||
"@embedpdf/plugin-interaction-manager": "1.3.14",
|
||||
"@embedpdf/plugin-selection": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"@embedpdf/plugin-history": "1.4.1",
|
||||
"@embedpdf/plugin-interaction-manager": "1.4.1",
|
||||
"@embedpdf/plugin-selection": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
@ -566,31 +568,32 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-export": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-export/-/plugin-export-1.3.14.tgz",
|
||||
"integrity": "sha512-fMGp2YxvI4uTRIViUKxfnJts2Jw/vktEM45XUNGNSjT/kAW6znVNgdceYjpK++xU8CGs2grAQ1i5UvMd3aRNDA==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-export/-/plugin-export-1.4.1.tgz",
|
||||
"integrity": "sha512-g89fREFM/zkt2Ai2Q5dWwDkhXgC/JmVyUniaMgm1fTG/MZ0Z05E7f34DUzX/CKcJyVjxEgl6tojBTMeUbm15bA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-history": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-history/-/plugin-history-1.3.14.tgz",
|
||||
"integrity": "sha512-77hnNLp0W0FHw8lT7SeqzCgp8bOClfeOAPZdcInu/jPDhVASUGYbtE/0fkLhiaqPH7kyMirNCLif4sF6n4b5vg==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-history/-/plugin-history-1.4.1.tgz",
|
||||
"integrity": "sha512-5WLDiNMH6tACkLGGv/lJtNsDeozOhSbrh0mjD1btHun8u7Yscu/Vf8tdJRUOsd+nULivo2nQ2NFNKu0OTbVo8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
@ -598,232 +601,245 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-interaction-manager": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-interaction-manager/-/plugin-interaction-manager-1.3.14.tgz",
|
||||
"integrity": "sha512-nR0ZxNoTQtGqOHhweFh6QJ+nUJ4S4Ag1wWur6vAUAi8U95HUOfZhOEa0polZo0zR9WmmblGqRWjFM+mVSOoi1w==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-interaction-manager/-/plugin-interaction-manager-1.4.1.tgz",
|
||||
"integrity": "sha512-Ng02S9SFIAi9JZS5rI+NXSnZZ1Yk9YYRw4MlN2pig49qOyivZdz0oScZaYxQPewo8ccJkLeghjdeWswOBW/6cA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-loader": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-loader/-/plugin-loader-1.3.14.tgz",
|
||||
"integrity": "sha512-KoJX1MacEWE2DrO1OeZeG/Ehz76//u+ida/xb4r9BfwqAp5TfYlksq09cOvcF8LMW5FY4pbAL+AHKI1Hjz+HNA==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-loader/-/plugin-loader-1.4.1.tgz",
|
||||
"integrity": "sha512-m3ZOk8JygsLxoa4cZ+0BVB5pfRWuBCg2/gPqjhoFZNKTqAFw4J6HGUrhYKg94GRYe+w1cTJl/NbTBYuU5DOrsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-pan": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-pan/-/plugin-pan-1.3.14.tgz",
|
||||
"integrity": "sha512-7EG+I5nn8yDCV8pT4x/g5mv7zJli2t3wPrh6Kt8uIpUorPHNb6J0Z67gl0uc/8rEasNzuKOuT0er46Y6/UYLzQ==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-pan/-/plugin-pan-1.4.1.tgz",
|
||||
"integrity": "sha512-zmOZJ9dUqXiaV0F5GPf/5WTWf3jAEkiv153Tl3x8HT9Rfff+WQhV48NruCIBAy/T4jVt4aH7D1zt/B/ftvcdkA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/plugin-interaction-manager": "1.3.14",
|
||||
"@embedpdf/plugin-viewport": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"@embedpdf/plugin-interaction-manager": "1.4.1",
|
||||
"@embedpdf/plugin-viewport": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-render": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-render/-/plugin-render-1.3.14.tgz",
|
||||
"integrity": "sha512-IPj7GCQXJBsY++JaU+z7y+FwX5NaDBj4YYV6hsHNtSGf42Y1AdlwJzDYetivG2bA84xmk7KgD1X2Y3eIFBhjwA==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-render/-/plugin-render-1.4.1.tgz",
|
||||
"integrity": "sha512-gKCdNKw6WBHBEpTc2DLBWIWOxzsNnaNbpfeY6C4f2Bum0EO+XW3Hl2oIx1uaRHjIhhnXso1J3QweqelsPwDGwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-rotate": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-rotate/-/plugin-rotate-1.3.14.tgz",
|
||||
"integrity": "sha512-OroEm11x/fPPXI9C0X+nm9LOjwaI0MvsToZRH+HpV60/FbQeOJvt6D8wThCDVLK95Na6A+JeYIMEu+Hiix7H+A==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-rotate/-/plugin-rotate-1.4.1.tgz",
|
||||
"integrity": "sha512-hVzHkKwMNH3tUhxqJGsj5qTLpYZXbj6E74AEcG0w/fz5FrK7EnofPqt0gRfYmIzxnQGIh+39BRtcp8gmx8UNnw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-scroll": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-scroll/-/plugin-scroll-1.3.14.tgz",
|
||||
"integrity": "sha512-fQbt7OlRMLQJMuZj/Bzh0qpRxMw1ld5Qe/OTw8N54b/plljnFA52joE7cITl3H03huWWyHS3NKOScbw7f34dog==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-scroll/-/plugin-scroll-1.4.1.tgz",
|
||||
"integrity": "sha512-Y9O+matB4j4fLim5s/jn7qIi+lMC9vmDJRpJhiWe8bvD9oYLP2xfD/DdhFgAjRKcNhPoxC+j8q8QN5BMeGAv2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/plugin-viewport": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"@embedpdf/plugin-viewport": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-search": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-search/-/plugin-search-1.3.14.tgz",
|
||||
"integrity": "sha512-tlZEgR2tG+GSNnh2u1SjCxhUHfTDgcr38sE/xRK1bRLDGPZWlr6Ln7qP7JSWqeYBGni75sGrj0iZqcZbPWyJag==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-search/-/plugin-search-1.4.1.tgz",
|
||||
"integrity": "sha512-8JG4CbOcUsLuT0vHJJ4cECmu+Yn53EokWFUVXi2Mo/XvHjhrQuWmD7+y6s/qQPEpctFYWmUCXTDAX9ynPud+2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/plugin-loader": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"@embedpdf/plugin-loader": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-selection": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-selection/-/plugin-selection-1.3.14.tgz",
|
||||
"integrity": "sha512-EXENuaAsse3rT6cjA1nYzyrNvoy62ojJl28wblCng6zcs3HSlGPemIQZAvaYKPUxoY608M+6nKlcMQ5neRnk/A==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-selection/-/plugin-selection-1.4.1.tgz",
|
||||
"integrity": "sha512-lo5Ytk1PH0PrRKv6zKVupm4t02VGsqIrnSIeP6NO8Ujx0wfqEhj//sqIuO/EwfFVJD8lcQIP9UUo9y8baCrEog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/plugin-interaction-manager": "1.3.14",
|
||||
"@embedpdf/plugin-viewport": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"@embedpdf/plugin-interaction-manager": "1.4.1",
|
||||
"@embedpdf/plugin-viewport": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-spread": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-spread/-/plugin-spread-1.3.14.tgz",
|
||||
"integrity": "sha512-DVlk6tDgUoDRkp2S4Jc3LrRTuf4DPMlph9vywJw5z6Qpbh0vgcMnObg896/S0Eu5FgACNAj0WGcXpLrcrn5b9Q==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-spread/-/plugin-spread-1.4.1.tgz",
|
||||
"integrity": "sha512-l+SrDVGTiiItkt2cEtzv7V/X5HhmLbYHcQ8CFobGeIKdJtzKS1Nu/JSKqg7Ki7eCNgyPL1yMNfNE92bNKYVN4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/plugin-loader": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"@embedpdf/plugin-loader": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-thumbnail": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-thumbnail/-/plugin-thumbnail-1.3.14.tgz",
|
||||
"integrity": "sha512-cnwb5dG8Jph8XSArys1WFCQ6kK2R5FKoO0B5mDrHFv9Fcm2pKszlmZC/NDoskX4pgNUgSnwhI1X3cP37ebF9Ng==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-thumbnail/-/plugin-thumbnail-1.4.1.tgz",
|
||||
"integrity": "sha512-bN3msjI0PovazgbPK3LyugYVTwIDo0RyBUhBaG42FgJxeY3hmFOWTPgfUH1QF7twHlySnksIvHRFYR3nViryVw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/plugin-render": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"@embedpdf/plugin-render": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-tiling": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-tiling/-/plugin-tiling-1.3.14.tgz",
|
||||
"integrity": "sha512-SaCTo2LdZwGeE6jCqkwJxvwt8YKbsI3QGxa9S7Ez+5OcBchlhHeTfLQswcErDQ3WH2p8WHtGuucAcOLrVVOm0A==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-tiling/-/plugin-tiling-1.4.1.tgz",
|
||||
"integrity": "sha512-wgTfj5T8HV6KP61iiR63DVNrbVp8sPxTqa1Sm+2/D0jY+EPSSCmpt1/qYWiAXd1X+t78foOjCnfbo7fEMn5/pg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/plugin-render": "1.3.14",
|
||||
"@embedpdf/plugin-scroll": "1.3.14",
|
||||
"@embedpdf/plugin-viewport": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"@embedpdf/plugin-render": "1.4.1",
|
||||
"@embedpdf/plugin-scroll": "1.4.1",
|
||||
"@embedpdf/plugin-viewport": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-viewport": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-viewport/-/plugin-viewport-1.3.14.tgz",
|
||||
"integrity": "sha512-mfJ7EbbU68eKk6oFvQ4ozGJNpxUxWbjQ5Gm3uuB+Gj5/tWgBocBOX36k/9LgivEEeX7g2S0tOgyErljApmH8Vg==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-viewport/-/plugin-viewport-1.4.1.tgz",
|
||||
"integrity": "sha512-+TgFHKPCLTBiDYe2DdsmTS37hwQgcZ3dYIc7bE0l5cp+GVwouu1h0MTmjL+90loizeWwCiu10E/zXR6hz+CUaQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14"
|
||||
"@embedpdf/models": "1.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/plugin-zoom": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-zoom/-/plugin-zoom-1.3.14.tgz",
|
||||
"integrity": "sha512-/N5tyMk+8OzhObrS3O9yPkcmX8EPiuTo+WaT2QCVSmIUqKnOO4AnKpHJ6Vl0uVhcuXHCMwLucZKyhJ7tRqavwg==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/plugin-zoom/-/plugin-zoom-1.4.1.tgz",
|
||||
"integrity": "sha512-9HocmXnPZxqN06q7kyNAmLjgDHOEW8/8QfgNE3nMpRyNHIgnAjxvsWc9lApgp5ErDPG0cSDt0Cduil6nB3wSBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@embedpdf/models": "1.3.14",
|
||||
"@embedpdf/models": "1.4.1",
|
||||
"hammerjs": "^2.0.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@embedpdf/core": "1.3.14",
|
||||
"@embedpdf/plugin-interaction-manager": "1.3.14",
|
||||
"@embedpdf/plugin-scroll": "1.3.14",
|
||||
"@embedpdf/plugin-viewport": "1.3.14",
|
||||
"@embedpdf/core": "1.4.1",
|
||||
"@embedpdf/plugin-interaction-manager": "1.4.1",
|
||||
"@embedpdf/plugin-scroll": "1.4.1",
|
||||
"@embedpdf/plugin-viewport": "1.4.1",
|
||||
"preact": "^10.26.4",
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"svelte": ">=5 <6",
|
||||
"vue": ">=3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@embedpdf/utils": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/utils/-/utils-1.3.14.tgz",
|
||||
"integrity": "sha512-gxEJD12nageCMqAjdbicNfDQolXU3nvnV0EX96OdZITRNj0Q1tisutVYoaxcCiJu3vvIEOzipjsAnQOubbFCEA==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@embedpdf/utils/-/utils-1.4.1.tgz",
|
||||
"integrity": "sha512-vvJ51Qsz3PyJWR2YvDMMpJXg4+YqdV7Vn2cusmW9sx+4EnAiBiw0HevEE+FepgFV8k+A0WbwXzmsujDIQJ7R4A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"preact": "^10.26.4",
|
||||
@ -3045,6 +3061,16 @@
|
||||
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/acorn-typescript": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.6.tgz",
|
||||
"integrity": "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"acorn": "^8.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core": {
|
||||
"version": "1.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz",
|
||||
@ -3802,7 +3828,6 @@
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/gapi": {
|
||||
@ -4743,7 +4768,6 @@
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@ -5147,6 +5171,16 @@
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axobject-query": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
|
||||
"integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/b4a": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz",
|
||||
@ -7176,6 +7210,13 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/esm-env": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
|
||||
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/espree": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
|
||||
@ -7234,6 +7275,16 @@
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/esrap": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.1.tgz",
|
||||
"integrity": "sha512-ebTT9B6lOtZGMgJ3o5r12wBacHctG7oEWazIda8UlPfA3HD/Wrv8FdXoVo73vzdpwCxNyXjPauyN2bbJzMkB9A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.15"
|
||||
}
|
||||
},
|
||||
"node_modules/esrecurse": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
|
||||
@ -8747,6 +8798,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-reference": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
|
||||
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/is-regex": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
|
||||
@ -9623,6 +9684,13 @@
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-character": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
|
||||
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
@ -12959,6 +13027,42 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "5.43.0",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.0.tgz",
|
||||
"integrity": "sha512-1sRxVbgJAB+UGzwkc3GUoiBSzEOf0jqzccMaVoI2+pI+kASUe9qubslxace8+Mzhqw19k4syTA5niCIJwfXpOA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/remapping": "^2.3.4",
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||
"@sveltejs/acorn-typescript": "^1.0.5",
|
||||
"@types/estree": "^1.0.5",
|
||||
"acorn": "^8.12.1",
|
||||
"aria-query": "^5.3.1",
|
||||
"axobject-query": "^4.1.0",
|
||||
"clsx": "^2.1.1",
|
||||
"esm-env": "^1.2.1",
|
||||
"esrap": "^2.1.0",
|
||||
"is-reference": "^3.0.3",
|
||||
"locate-character": "^3.0.0",
|
||||
"magic-string": "^0.30.11",
|
||||
"zimmerframe": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte/node_modules/aria-query": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
|
||||
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/symbol-tree": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||
@ -14447,6 +14551,13 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/zimmerframe": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz",
|
||||
"integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
|
||||
@ -6,24 +6,24 @@
|
||||
"proxy": "http://localhost:8080",
|
||||
"dependencies": {
|
||||
"@atlaskit/pragmatic-drag-and-drop": "^1.7.7",
|
||||
"@embedpdf/core": "^1.3.14",
|
||||
"@embedpdf/engines": "^1.3.14",
|
||||
"@embedpdf/plugin-annotation": "^1.3.14",
|
||||
"@embedpdf/plugin-export": "^1.3.14",
|
||||
"@embedpdf/plugin-history": "^1.3.14",
|
||||
"@embedpdf/plugin-interaction-manager": "^1.3.14",
|
||||
"@embedpdf/plugin-loader": "^1.3.14",
|
||||
"@embedpdf/plugin-pan": "^1.3.14",
|
||||
"@embedpdf/plugin-render": "^1.3.14",
|
||||
"@embedpdf/plugin-rotate": "^1.3.14",
|
||||
"@embedpdf/plugin-scroll": "^1.3.14",
|
||||
"@embedpdf/plugin-search": "^1.3.14",
|
||||
"@embedpdf/plugin-selection": "^1.3.14",
|
||||
"@embedpdf/plugin-spread": "^1.3.14",
|
||||
"@embedpdf/plugin-thumbnail": "^1.3.14",
|
||||
"@embedpdf/plugin-tiling": "^1.3.14",
|
||||
"@embedpdf/plugin-viewport": "^1.3.14",
|
||||
"@embedpdf/plugin-zoom": "^1.3.14",
|
||||
"@embedpdf/core": "^1.4.1",
|
||||
"@embedpdf/engines": "^1.4.1",
|
||||
"@embedpdf/plugin-annotation": "^1.4.1",
|
||||
"@embedpdf/plugin-export": "^1.4.1",
|
||||
"@embedpdf/plugin-history": "^1.4.1",
|
||||
"@embedpdf/plugin-interaction-manager": "^1.4.1",
|
||||
"@embedpdf/plugin-loader": "^1.4.1",
|
||||
"@embedpdf/plugin-pan": "^1.4.1",
|
||||
"@embedpdf/plugin-render": "^1.4.1",
|
||||
"@embedpdf/plugin-rotate": "^1.4.1",
|
||||
"@embedpdf/plugin-scroll": "^1.4.1",
|
||||
"@embedpdf/plugin-search": "^1.4.1",
|
||||
"@embedpdf/plugin-selection": "^1.4.1",
|
||||
"@embedpdf/plugin-spread": "^1.4.1",
|
||||
"@embedpdf/plugin-thumbnail": "^1.4.1",
|
||||
"@embedpdf/plugin-tiling": "^1.4.1",
|
||||
"@embedpdf/plugin-viewport": "^1.4.1",
|
||||
"@embedpdf/plugin-zoom": "^1.4.1",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@iconify/react": "^6.0.2",
|
||||
|
||||
@ -35,14 +35,12 @@ const EmbedPdfViewerContent = ({
|
||||
const viewerRef = React.useRef<HTMLDivElement>(null);
|
||||
const [isViewerHovered, setIsViewerHovered] = React.useState(false);
|
||||
|
||||
const { isThumbnailSidebarVisible, toggleThumbnailSidebar, zoomActions, spreadActions, panActions: _panActions, rotationActions: _rotationActions, getScrollState, getZoomState, getSpreadState, getRotationState, isAnnotationMode, isAnnotationsVisible, exportActions } = useViewer();
|
||||
const { isThumbnailSidebarVisible, toggleThumbnailSidebar, zoomActions, panActions: _panActions, rotationActions: _rotationActions, getScrollState, getRotationState, isAnnotationMode, isAnnotationsVisible, exportActions } = useViewer();
|
||||
|
||||
// Register viewer right-rail buttons
|
||||
useViewerRightRailButtons();
|
||||
|
||||
const scrollState = getScrollState();
|
||||
const zoomState = getZoomState();
|
||||
const spreadState = getSpreadState();
|
||||
const rotationState = getRotationState();
|
||||
|
||||
// Track initial rotation to detect changes
|
||||
@ -320,15 +318,6 @@ const EmbedPdfViewerContent = ({
|
||||
<PdfViewerToolbar
|
||||
currentPage={scrollState.currentPage}
|
||||
totalPages={scrollState.totalPages}
|
||||
onPageChange={(page) => {
|
||||
// Page navigation handled by scrollActions
|
||||
console.log('Navigate to page:', page);
|
||||
}}
|
||||
dualPage={spreadState.isDualPage}
|
||||
onDualPageToggle={() => {
|
||||
spreadActions.toggleSpreadMode();
|
||||
}}
|
||||
currentZoom={zoomState.zoomPercent}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -8,7 +8,7 @@ import { Viewport, ViewportPluginPackage } from '@embedpdf/plugin-viewport/react
|
||||
import { Scroller, ScrollPluginPackage, ScrollStrategy } from '@embedpdf/plugin-scroll/react';
|
||||
import { LoaderPluginPackage } from '@embedpdf/plugin-loader/react';
|
||||
import { RenderPluginPackage } from '@embedpdf/plugin-render/react';
|
||||
import { ZoomPluginPackage } from '@embedpdf/plugin-zoom/react';
|
||||
import { ZoomPluginPackage, ZoomMode } from '@embedpdf/plugin-zoom/react';
|
||||
import { InteractionManagerPluginPackage, PagePointerProvider, GlobalPointerProvider } from '@embedpdf/plugin-interaction-manager/react';
|
||||
import { SelectionLayer, SelectionPluginPackage } from '@embedpdf/plugin-selection/react';
|
||||
import { TilingLayer, TilingPluginPackage } from '@embedpdf/plugin-tiling/react';
|
||||
@ -114,9 +114,9 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
|
||||
|
||||
// Register zoom plugin with configuration
|
||||
createPluginRegistration(ZoomPluginPackage, {
|
||||
defaultZoomLevel: 1.4, // Start at 140% zoom for better readability
|
||||
defaultZoomLevel: ZoomMode.FitWidth, // Start with FitWidth, will be adjusted in ZoomAPIBridge
|
||||
minZoom: 0.2,
|
||||
maxZoom: 3.0,
|
||||
maxZoom: 5.0,
|
||||
}),
|
||||
|
||||
// Register tiling plugin (depends on Render, Scroll, Viewport)
|
||||
@ -287,6 +287,8 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
|
||||
minHeight: 0,
|
||||
minWidth: 0,
|
||||
contain: 'strict',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Scroller
|
||||
@ -295,6 +297,9 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
|
||||
<Rotate key={document?.id} pageSize={{ width, height }}>
|
||||
<PagePointerProvider pageIndex={pageIndex} pageWidth={width} pageHeight={height} scale={scale} rotation={rotation}>
|
||||
<div
|
||||
data-page-index={pageIndex}
|
||||
data-page-width={width}
|
||||
data-page-height={height}
|
||||
style={{
|
||||
width,
|
||||
height,
|
||||
|
||||
@ -14,30 +14,32 @@ interface PdfViewerToolbarProps {
|
||||
currentPage?: number;
|
||||
totalPages?: number;
|
||||
onPageChange?: (page: number) => void;
|
||||
|
||||
// Dual page toggle (placeholder for now)
|
||||
dualPage?: boolean;
|
||||
onDualPageToggle?: () => void;
|
||||
|
||||
// Zoom controls (connected via ViewerContext)
|
||||
currentZoom?: number;
|
||||
}
|
||||
|
||||
export function PdfViewerToolbar({
|
||||
currentPage = 1,
|
||||
totalPages: _totalPages = 1,
|
||||
onPageChange,
|
||||
dualPage = false,
|
||||
onDualPageToggle,
|
||||
currentZoom: _currentZoom = 100,
|
||||
}: PdfViewerToolbarProps) {
|
||||
const { t } = useTranslation();
|
||||
const { getScrollState, getZoomState, scrollActions, zoomActions, registerImmediateZoomUpdate, registerImmediateScrollUpdate } = useViewer();
|
||||
const {
|
||||
getScrollState,
|
||||
getZoomState,
|
||||
getSpreadState,
|
||||
scrollActions,
|
||||
zoomActions,
|
||||
spreadActions,
|
||||
registerImmediateZoomUpdate,
|
||||
registerImmediateScrollUpdate,
|
||||
registerImmediateSpreadUpdate,
|
||||
} = useViewer();
|
||||
|
||||
const scrollState = getScrollState();
|
||||
const zoomState = getZoomState();
|
||||
const spreadState = getSpreadState();
|
||||
const [pageInput, setPageInput] = useState(scrollState.currentPage || currentPage);
|
||||
const [displayZoomPercent, setDisplayZoomPercent] = useState(zoomState.zoomPercent || 140);
|
||||
const [isDualPageActive, setIsDualPageActive] = useState(spreadState.isDualPage);
|
||||
|
||||
// Register for immediate scroll updates and sync with actual scroll state
|
||||
useEffect(() => {
|
||||
@ -53,6 +55,13 @@ export function PdfViewerToolbar({
|
||||
setDisplayZoomPercent(zoomState.zoomPercent || 140);
|
||||
}, [zoomState.zoomPercent, registerImmediateZoomUpdate]);
|
||||
|
||||
useEffect(() => {
|
||||
registerImmediateSpreadUpdate((_mode, isDual) => {
|
||||
setIsDualPageActive(isDual);
|
||||
});
|
||||
setIsDualPageActive(spreadState.isDualPage);
|
||||
}, [registerImmediateSpreadUpdate, spreadState.isDualPage]);
|
||||
|
||||
const handleZoomOut = () => {
|
||||
zoomActions.zoomOut();
|
||||
};
|
||||
@ -69,6 +78,10 @@ export function PdfViewerToolbar({
|
||||
setPageInput(page);
|
||||
};
|
||||
|
||||
const handleDualPageToggle = () => {
|
||||
spreadActions.toggleSpreadMode();
|
||||
};
|
||||
|
||||
const handleFirstPage = () => {
|
||||
scrollActions.scrollToFirstPage();
|
||||
};
|
||||
@ -188,15 +201,19 @@ export function PdfViewerToolbar({
|
||||
|
||||
{/* Dual Page Toggle */}
|
||||
<Button
|
||||
variant={dualPage ? "filled" : "light"}
|
||||
variant={isDualPageActive ? "filled" : "light"}
|
||||
color="blue"
|
||||
size="md"
|
||||
radius="xl"
|
||||
onClick={onDualPageToggle}
|
||||
onClick={handleDualPageToggle}
|
||||
style={{ minWidth: '2.5rem' }}
|
||||
title={dualPage ? t("viewer.singlePageView", "Single Page View") : t("viewer.dualPageView", "Dual Page View")}
|
||||
title={
|
||||
isDualPageActive
|
||||
? t("viewer.singlePageView", "Single Page View")
|
||||
: t("viewer.dualPageView", "Dual Page View")
|
||||
}
|
||||
>
|
||||
{dualPage ? <DescriptionIcon fontSize="small" /> : <ViewWeekIcon fontSize="small" />}
|
||||
{isDualPageActive ? <DescriptionIcon fontSize="small" /> : <ViewWeekIcon fontSize="small" />}
|
||||
</Button>
|
||||
|
||||
{/* Zoom Controls */}
|
||||
|
||||
@ -7,33 +7,36 @@ import { useViewer } from '@app/contexts/ViewerContext';
|
||||
*/
|
||||
export function SpreadAPIBridge() {
|
||||
const { provides: spread, spreadMode } = useSpread();
|
||||
const { registerBridge } = useViewer();
|
||||
const { registerBridge, triggerImmediateSpreadUpdate } = useViewer();
|
||||
|
||||
useEffect(() => {
|
||||
if (spread) {
|
||||
const newState = {
|
||||
spreadMode,
|
||||
isDualPage: spreadMode !== SpreadMode.None
|
||||
};
|
||||
|
||||
// Register this bridge with ViewerContext
|
||||
registerBridge('spread', {
|
||||
state: newState,
|
||||
api: {
|
||||
setSpreadMode: (mode: SpreadMode) => {
|
||||
spread.setSpreadMode(mode);
|
||||
},
|
||||
getSpreadMode: () => spread.getSpreadMode(),
|
||||
toggleSpreadMode: () => {
|
||||
// Toggle between None and Odd (most common dual-page mode)
|
||||
const newMode = spreadMode === SpreadMode.None ? SpreadMode.Odd : SpreadMode.None;
|
||||
spread.setSpreadMode(newMode);
|
||||
},
|
||||
SpreadMode: SpreadMode, // Export enum for reference
|
||||
}
|
||||
});
|
||||
if (!spread) {
|
||||
return;
|
||||
}
|
||||
}, [spread, spreadMode]);
|
||||
|
||||
const newState = {
|
||||
spreadMode,
|
||||
isDualPage: spreadMode !== SpreadMode.None,
|
||||
};
|
||||
|
||||
registerBridge('spread', {
|
||||
state: newState,
|
||||
api: {
|
||||
setSpreadMode: (mode: SpreadMode) => {
|
||||
spread.setSpreadMode(mode);
|
||||
},
|
||||
getSpreadMode: () => spread.getSpreadMode(),
|
||||
toggleSpreadMode: () => {
|
||||
const current = spread.getSpreadMode();
|
||||
const nextMode = current === SpreadMode.None ? SpreadMode.Odd : SpreadMode.None;
|
||||
spread.setSpreadMode(nextMode);
|
||||
},
|
||||
SpreadMode,
|
||||
},
|
||||
});
|
||||
|
||||
triggerImmediateSpreadUpdate(spreadMode);
|
||||
}, [spread, spreadMode, registerBridge, triggerImmediateSpreadUpdate]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1,68 +1,256 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useZoom } from '@embedpdf/plugin-zoom/react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useZoom, ZoomMode } from '@embedpdf/plugin-zoom/react';
|
||||
import { useSpread, SpreadMode } from '@embedpdf/plugin-spread/react';
|
||||
import { useViewer } from '@app/contexts/ViewerContext';
|
||||
import { useFileState } from '@app/contexts/FileContext';
|
||||
import {
|
||||
determineAutoZoom,
|
||||
DEFAULT_FALLBACK_ZOOM,
|
||||
DEFAULT_VISIBILITY_THRESHOLD,
|
||||
measureRenderedPageRect,
|
||||
useFitWidthResize,
|
||||
ZoomViewport,
|
||||
} from '@core/utils/viewerZoom';
|
||||
import { getFirstPageAspectRatioFromStub } from '@core/utils/pageMetadata';
|
||||
|
||||
/**
|
||||
* Component that runs inside EmbedPDF context and manages zoom state locally
|
||||
*/
|
||||
export function ZoomAPIBridge() {
|
||||
const { provides: zoom, state: zoomState } = useZoom();
|
||||
const { state: spreadState } = useSpread();
|
||||
const { registerBridge, triggerImmediateZoomUpdate } = useViewer();
|
||||
const hasSetInitialZoom = useRef(false);
|
||||
const { selectors } = useFileState();
|
||||
|
||||
const hasSetInitialZoom = useRef(false);
|
||||
const lastSpreadMode = useRef(spreadState?.spreadMode);
|
||||
const lastFileId = useRef<string | undefined>();
|
||||
const lastAppliedZoom = useRef<number | null>(null);
|
||||
const [autoZoomTick, setAutoZoomTick] = useState(0);
|
||||
|
||||
const scheduleAutoZoom = useCallback(() => {
|
||||
hasSetInitialZoom.current = false;
|
||||
lastAppliedZoom.current = null;
|
||||
setAutoZoomTick((tick) => tick + 1);
|
||||
}, []);
|
||||
|
||||
const requestFitWidth = useCallback(() => {
|
||||
if (zoom) {
|
||||
zoom.requestZoom(ZoomMode.FitWidth, { vx: 0.5, vy: 0 });
|
||||
}
|
||||
}, [zoom]);
|
||||
|
||||
const stubs = selectors.getStirlingFileStubs();
|
||||
const firstFileStub = stubs[0];
|
||||
const firstFileId = firstFileStub?.id;
|
||||
|
||||
// Set initial zoom once when plugin is ready
|
||||
useEffect(() => {
|
||||
if (!zoom || hasSetInitialZoom.current) {
|
||||
if (!firstFileId) {
|
||||
hasSetInitialZoom.current = false;
|
||||
lastFileId.current = undefined;
|
||||
lastAppliedZoom.current = null;
|
||||
return;
|
||||
}
|
||||
|
||||
let retryTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
const attemptInitialZoom = () => {
|
||||
try {
|
||||
zoom.requestZoom(1.4);
|
||||
hasSetInitialZoom.current = true;
|
||||
} catch (error) {
|
||||
console.log('Zoom initialization delayed, viewport not ready:', error);
|
||||
retryTimer = setTimeout(() => {
|
||||
try {
|
||||
zoom.requestZoom(1.4);
|
||||
hasSetInitialZoom.current = true;
|
||||
} catch (retryError) {
|
||||
console.log('Zoom initialization failed:', retryError);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
};
|
||||
|
||||
const timer = setTimeout(attemptInitialZoom, 50);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
if (retryTimer) {
|
||||
clearTimeout(retryTimer);
|
||||
}
|
||||
};
|
||||
}, [zoom, zoomState]);
|
||||
if (firstFileId !== lastFileId.current) {
|
||||
lastFileId.current = firstFileId;
|
||||
scheduleAutoZoom();
|
||||
}
|
||||
}, [firstFileId, scheduleAutoZoom]);
|
||||
|
||||
useEffect(() => {
|
||||
if (zoom && zoomState) {
|
||||
// Update local state
|
||||
const currentZoomLevel = zoomState.currentZoomLevel ?? 1.4;
|
||||
const newState = {
|
||||
currentZoom: currentZoomLevel,
|
||||
zoomPercent: Math.round(currentZoomLevel * 100),
|
||||
};
|
||||
const currentSpreadMode = spreadState?.spreadMode ?? SpreadMode.None;
|
||||
if (currentSpreadMode !== lastSpreadMode.current) {
|
||||
lastSpreadMode.current = currentSpreadMode;
|
||||
|
||||
// Trigger immediate update for responsive UI
|
||||
triggerImmediateZoomUpdate(newState.zoomPercent);
|
||||
|
||||
// Register this bridge with ViewerContext
|
||||
registerBridge('zoom', {
|
||||
state: newState,
|
||||
api: zoom
|
||||
});
|
||||
const hadTrackedAutoZoom = lastAppliedZoom.current !== null;
|
||||
const zoomLevel = zoomState?.zoomLevel;
|
||||
if (
|
||||
zoomLevel === ZoomMode.FitWidth ||
|
||||
zoomLevel === ZoomMode.Automatic ||
|
||||
hadTrackedAutoZoom
|
||||
) {
|
||||
requestFitWidth();
|
||||
scheduleAutoZoom();
|
||||
}
|
||||
}
|
||||
}, [zoom, zoomState]);
|
||||
}, [
|
||||
spreadState?.spreadMode,
|
||||
zoomState?.zoomLevel,
|
||||
scheduleAutoZoom,
|
||||
requestFitWidth,
|
||||
]);
|
||||
|
||||
const getViewportSnapshot = useCallback((): ZoomViewport | null => {
|
||||
if (!zoomState || typeof zoomState !== 'object') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ('viewport' in zoomState) {
|
||||
const candidate = (zoomState as { viewport?: ZoomViewport | null }).viewport;
|
||||
return candidate ?? null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [zoomState]);
|
||||
|
||||
const isManagedZoom =
|
||||
!!zoom &&
|
||||
(zoomState?.zoomLevel === ZoomMode.FitWidth ||
|
||||
zoomState?.zoomLevel === ZoomMode.Automatic ||
|
||||
lastAppliedZoom.current !== null);
|
||||
|
||||
useFitWidthResize({
|
||||
isManaged: isManagedZoom,
|
||||
requestFitWidth,
|
||||
onDebouncedResize: scheduleAutoZoom,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!zoom || !zoomState) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!firstFileId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasSetInitialZoom.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (zoomState.zoomLevel !== ZoomMode.FitWidth) {
|
||||
if (zoomState.zoomLevel === ZoomMode.Automatic) {
|
||||
requestFitWidth();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const fitWidthZoom = zoomState.currentZoomLevel;
|
||||
if (!fitWidthZoom || fitWidthZoom <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const applyTrackedZoom = (level: number | ZoomMode, effectiveZoom: number) => {
|
||||
zoom.requestZoom(level, { vx: 0.5, vy: 0 });
|
||||
lastAppliedZoom.current = effectiveZoom;
|
||||
triggerImmediateZoomUpdate(Math.round(effectiveZoom * 100));
|
||||
hasSetInitialZoom.current = true;
|
||||
};
|
||||
|
||||
let cancelled = false;
|
||||
|
||||
const applyAutoZoom = async () => {
|
||||
const spreadMode = spreadState?.spreadMode ?? SpreadMode.None;
|
||||
const pagesPerSpread = spreadMode !== SpreadMode.None ? 2 : 1;
|
||||
const metadataAspectRatio = getFirstPageAspectRatioFromStub(firstFileStub);
|
||||
|
||||
const viewport = getViewportSnapshot();
|
||||
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const metrics = viewport ?? {};
|
||||
const viewportWidth =
|
||||
metrics.clientWidth ?? metrics.width ?? window.innerWidth ?? 0;
|
||||
const viewportHeight =
|
||||
metrics.clientHeight ?? metrics.height ?? window.innerHeight ?? 0;
|
||||
|
||||
if (viewportWidth <= 0 || viewportHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pageRect = await measureRenderedPageRect({
|
||||
shouldCancel: () => cancelled,
|
||||
});
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const decision = determineAutoZoom({
|
||||
viewportWidth,
|
||||
viewportHeight,
|
||||
fitWidthZoom,
|
||||
pagesPerSpread,
|
||||
pageRect: pageRect
|
||||
? { width: pageRect.width, height: pageRect.height }
|
||||
: undefined,
|
||||
metadataAspectRatio: metadataAspectRatio ?? null,
|
||||
visibilityThreshold: DEFAULT_VISIBILITY_THRESHOLD,
|
||||
fallbackZoom: DEFAULT_FALLBACK_ZOOM,
|
||||
});
|
||||
|
||||
if (decision.type === 'fallback') {
|
||||
applyTrackedZoom(decision.zoom, decision.zoom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (decision.type === 'fitWidth') {
|
||||
applyTrackedZoom(ZoomMode.FitWidth, fitWidthZoom);
|
||||
return;
|
||||
}
|
||||
|
||||
applyTrackedZoom(decision.zoom, decision.zoom);
|
||||
};
|
||||
|
||||
applyAutoZoom();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [
|
||||
zoom,
|
||||
zoomState,
|
||||
firstFileId,
|
||||
firstFileStub,
|
||||
requestFitWidth,
|
||||
getViewportSnapshot,
|
||||
autoZoomTick,
|
||||
spreadState?.spreadMode,
|
||||
triggerImmediateZoomUpdate,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!zoom || typeof zoom.onZoomChange !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
const unsubscribe = zoom.onZoomChange((event: { newZoom?: number }) => {
|
||||
if (typeof event?.newZoom !== 'number') {
|
||||
return;
|
||||
}
|
||||
lastAppliedZoom.current = event.newZoom;
|
||||
triggerImmediateZoomUpdate(Math.round(event.newZoom * 100));
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (typeof unsubscribe === 'function') {
|
||||
unsubscribe();
|
||||
}
|
||||
};
|
||||
}, [zoom, triggerImmediateZoomUpdate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!zoom || !zoomState) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentZoomLevel =
|
||||
lastAppliedZoom.current ?? zoomState.currentZoomLevel ?? 1;
|
||||
|
||||
const newState = {
|
||||
currentZoom: currentZoomLevel,
|
||||
zoomPercent: Math.round(currentZoomLevel * 100),
|
||||
};
|
||||
|
||||
triggerImmediateZoomUpdate(newState.zoomPercent);
|
||||
|
||||
registerBridge('zoom', {
|
||||
state: newState,
|
||||
api: zoom,
|
||||
});
|
||||
}, [zoom, zoomState, registerBridge, triggerImmediateZoomUpdate]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
import React, { createContext, useContext, useState, ReactNode, useRef } from 'react';
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useState,
|
||||
ReactNode,
|
||||
useRef,
|
||||
useCallback,
|
||||
} from 'react';
|
||||
import { SpreadMode } from '@embedpdf/plugin-spread/react';
|
||||
import { useNavigation } from '@app/contexts/NavigationContext';
|
||||
|
||||
@ -109,6 +116,20 @@ interface BridgeRef<TState = unknown, TApi = unknown> {
|
||||
api: TApi;
|
||||
}
|
||||
|
||||
function useImmediateNotifier<Args extends unknown[]>() {
|
||||
const callbackRef = useRef<((...args: Args) => void) | null>(null);
|
||||
|
||||
const register = useCallback((callback: (...args: Args) => void) => {
|
||||
callbackRef.current = callback;
|
||||
}, []);
|
||||
|
||||
const trigger = useCallback((...args: Args) => {
|
||||
callbackRef.current?.(...args);
|
||||
}, []);
|
||||
|
||||
return { register, trigger };
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewerContext provides a unified interface to EmbedPDF functionality.
|
||||
*
|
||||
@ -150,10 +171,12 @@ interface ViewerContextType {
|
||||
// Immediate update callbacks
|
||||
registerImmediateZoomUpdate: (callback: (percent: number) => void) => void;
|
||||
registerImmediateScrollUpdate: (callback: (currentPage: number, totalPages: number) => void) => void;
|
||||
registerImmediateSpreadUpdate: (callback: (mode: SpreadMode, isDualPage: boolean) => void) => void;
|
||||
|
||||
// Internal - for bridges to trigger immediate updates
|
||||
triggerImmediateScrollUpdate: (currentPage: number, totalPages: number) => void;
|
||||
triggerImmediateZoomUpdate: (zoomPercent: number) => void;
|
||||
triggerImmediateSpreadUpdate: (mode: SpreadMode, isDualPage?: boolean) => void;
|
||||
|
||||
// Action handlers - call EmbedPDF APIs directly
|
||||
scrollActions: {
|
||||
@ -241,11 +264,39 @@ export const ViewerProvider: React.FC<ViewerProviderProps> = ({ children }) => {
|
||||
export: null as BridgeRef<ExportState, ExportAPIWrapper> | null,
|
||||
});
|
||||
|
||||
// Immediate zoom callback for responsive display updates
|
||||
const immediateZoomUpdateCallback = useRef<((percent: number) => void) | null>(null);
|
||||
const {
|
||||
register: registerImmediateZoomUpdate,
|
||||
trigger: triggerImmediateZoomInternal,
|
||||
} = useImmediateNotifier<[number]>();
|
||||
const {
|
||||
register: registerImmediateScrollUpdate,
|
||||
trigger: triggerImmediateScrollInternal,
|
||||
} = useImmediateNotifier<[number, number]>();
|
||||
const {
|
||||
register: registerImmediateSpreadUpdate,
|
||||
trigger: triggerImmediateSpreadInternal,
|
||||
} = useImmediateNotifier<[SpreadMode, boolean]>();
|
||||
|
||||
// Immediate scroll callback for responsive display updates
|
||||
const immediateScrollUpdateCallback = useRef<((currentPage: number, totalPages: number) => void) | null>(null);
|
||||
const triggerImmediateZoomUpdate = useCallback(
|
||||
(percent: number) => {
|
||||
triggerImmediateZoomInternal(percent);
|
||||
},
|
||||
[triggerImmediateZoomInternal]
|
||||
);
|
||||
|
||||
const triggerImmediateScrollUpdate = useCallback(
|
||||
(currentPage: number, totalPages: number) => {
|
||||
triggerImmediateScrollInternal(currentPage, totalPages);
|
||||
},
|
||||
[triggerImmediateScrollInternal]
|
||||
);
|
||||
|
||||
const triggerImmediateSpreadUpdate = useCallback(
|
||||
(mode: SpreadMode, isDualPage: boolean = mode !== SpreadMode.None) => {
|
||||
triggerImmediateSpreadInternal(mode, isDualPage);
|
||||
},
|
||||
[triggerImmediateSpreadInternal]
|
||||
);
|
||||
|
||||
const registerBridge = (type: string, ref: BridgeRef) => {
|
||||
// Type-safe assignment - we know the bridges will provide correct types
|
||||
@ -372,24 +423,18 @@ export const ViewerProvider: React.FC<ViewerProviderProps> = ({ children }) => {
|
||||
zoomIn: () => {
|
||||
const api = bridgeRefs.current.zoom?.api;
|
||||
if (api?.zoomIn) {
|
||||
// Update display immediately if callback is registered
|
||||
if (immediateZoomUpdateCallback.current) {
|
||||
const currentState = getZoomState();
|
||||
const newPercent = Math.min(Math.round(currentState.zoomPercent * 1.2), 300);
|
||||
immediateZoomUpdateCallback.current(newPercent);
|
||||
}
|
||||
const currentState = getZoomState();
|
||||
const newPercent = Math.min(Math.round(currentState.zoomPercent * 1.2), 300);
|
||||
triggerImmediateZoomUpdate(newPercent);
|
||||
api.zoomIn();
|
||||
}
|
||||
},
|
||||
zoomOut: () => {
|
||||
const api = bridgeRefs.current.zoom?.api;
|
||||
if (api?.zoomOut) {
|
||||
// Update display immediately if callback is registered
|
||||
if (immediateZoomUpdateCallback.current) {
|
||||
const currentState = getZoomState();
|
||||
const newPercent = Math.max(Math.round(currentState.zoomPercent / 1.2), 20);
|
||||
immediateZoomUpdateCallback.current(newPercent);
|
||||
}
|
||||
const currentState = getZoomState();
|
||||
const newPercent = Math.max(Math.round(currentState.zoomPercent / 1.2), 20);
|
||||
triggerImmediateZoomUpdate(newPercent);
|
||||
api.zoomOut();
|
||||
}
|
||||
},
|
||||
@ -550,26 +595,6 @@ export const ViewerProvider: React.FC<ViewerProviderProps> = ({ children }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const registerImmediateZoomUpdate = (callback: (percent: number) => void) => {
|
||||
immediateZoomUpdateCallback.current = callback;
|
||||
};
|
||||
|
||||
const registerImmediateScrollUpdate = (callback: (currentPage: number, totalPages: number) => void) => {
|
||||
immediateScrollUpdateCallback.current = callback;
|
||||
};
|
||||
|
||||
const triggerImmediateScrollUpdate = (currentPage: number, totalPages: number) => {
|
||||
if (immediateScrollUpdateCallback.current) {
|
||||
immediateScrollUpdateCallback.current(currentPage, totalPages);
|
||||
}
|
||||
};
|
||||
|
||||
const triggerImmediateZoomUpdate = (zoomPercent: number) => {
|
||||
if (immediateZoomUpdateCallback.current) {
|
||||
immediateZoomUpdateCallback.current(zoomPercent);
|
||||
}
|
||||
};
|
||||
|
||||
const value: ViewerContextType = {
|
||||
// UI state
|
||||
isThumbnailSidebarVisible,
|
||||
@ -600,8 +625,10 @@ export const ViewerProvider: React.FC<ViewerProviderProps> = ({ children }) => {
|
||||
// Immediate updates
|
||||
registerImmediateZoomUpdate,
|
||||
registerImmediateScrollUpdate,
|
||||
registerImmediateSpreadUpdate,
|
||||
triggerImmediateScrollUpdate,
|
||||
triggerImmediateZoomUpdate,
|
||||
triggerImmediateSpreadUpdate,
|
||||
|
||||
// Actions
|
||||
scrollActions,
|
||||
|
||||
@ -58,14 +58,21 @@ const addFilesMutex = new SimpleMutex();
|
||||
/**
|
||||
* Helper to create ProcessedFile metadata structure
|
||||
*/
|
||||
export function createProcessedFile(pageCount: number, thumbnail?: string, pageRotations?: number[]) {
|
||||
export function createProcessedFile(
|
||||
pageCount: number,
|
||||
thumbnail?: string,
|
||||
pageRotations?: number[],
|
||||
pageDimensions?: Array<{ width: number; height: number }>
|
||||
) {
|
||||
return {
|
||||
totalPages: pageCount,
|
||||
pages: Array.from({ length: pageCount }, (_, index) => ({
|
||||
pageNumber: index + 1,
|
||||
thumbnail: index === 0 ? thumbnail : undefined, // Only first page gets thumbnail initially
|
||||
rotation: pageRotations?.[index] ?? 0,
|
||||
splitBefore: false
|
||||
splitBefore: false,
|
||||
width: pageDimensions?.[index]?.width,
|
||||
height: pageDimensions?.[index]?.height
|
||||
})),
|
||||
thumbnailUrl: thumbnail,
|
||||
lastProcessed: Date.now()
|
||||
@ -92,7 +99,8 @@ export async function generateProcessedFileMetadata(file: File): Promise<Process
|
||||
const processedFile = createProcessedFile(
|
||||
unrotatedResult.pageCount,
|
||||
unrotatedResult.thumbnail, // Page thumbnails (unrotated)
|
||||
unrotatedResult.pageRotations
|
||||
unrotatedResult.pageRotations,
|
||||
unrotatedResult.pageDimensions
|
||||
);
|
||||
|
||||
// Use rotated thumbnail for file manager
|
||||
|
||||
@ -14,6 +14,8 @@ export interface ProcessedFilePage {
|
||||
pageNumber?: number;
|
||||
rotation?: number;
|
||||
splitBefore?: boolean;
|
||||
width?: number;
|
||||
height?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
|
||||
53
frontend/src/core/utils/pageMetadata.ts
Normal file
53
frontend/src/core/utils/pageMetadata.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import {
|
||||
ProcessedFileMetadata,
|
||||
ProcessedFilePage,
|
||||
StirlingFileStub,
|
||||
} from '@app/types/fileContext';
|
||||
|
||||
export interface PageDimensions {
|
||||
width: number | null;
|
||||
height: number | null;
|
||||
}
|
||||
|
||||
export function getPageDimensions(
|
||||
page?: ProcessedFilePage | null
|
||||
): PageDimensions {
|
||||
const width =
|
||||
typeof page?.width === 'number' && page.width > 0 ? page.width : null;
|
||||
const height =
|
||||
typeof page?.height === 'number' && page.height > 0 ? page.height : null;
|
||||
|
||||
return { width, height };
|
||||
}
|
||||
|
||||
export function getFirstPageDimensionsFromMetadata(
|
||||
metadata?: ProcessedFileMetadata | null
|
||||
): PageDimensions {
|
||||
if (!metadata?.pages?.length) {
|
||||
return { width: null, height: null };
|
||||
}
|
||||
|
||||
return getPageDimensions(metadata.pages[0]);
|
||||
}
|
||||
|
||||
export function getFirstPageDimensionsFromStub(
|
||||
file?: StirlingFileStub
|
||||
): PageDimensions {
|
||||
return getFirstPageDimensionsFromMetadata(file?.processedFile);
|
||||
}
|
||||
|
||||
export function getFirstPageAspectRatioFromMetadata(
|
||||
metadata?: ProcessedFileMetadata | null
|
||||
): number | null {
|
||||
const { width, height } = getFirstPageDimensionsFromMetadata(metadata);
|
||||
if (width && height) {
|
||||
return height / width;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getFirstPageAspectRatioFromStub(
|
||||
file?: StirlingFileStub
|
||||
): number | null {
|
||||
return getFirstPageAspectRatioFromMetadata(file?.processedFile);
|
||||
}
|
||||
@ -4,6 +4,7 @@ export interface ThumbnailWithMetadata {
|
||||
thumbnail: string; // Always returns a thumbnail (placeholder if needed)
|
||||
pageCount: number;
|
||||
pageRotations?: number[]; // Rotation for each page (0, 90, 180, 270)
|
||||
pageDimensions?: Array<{ width: number; height: number }>;
|
||||
}
|
||||
|
||||
interface ColorScheme {
|
||||
@ -402,12 +403,18 @@ export async function generateThumbnailWithMetadata(file: File, applyRotation: b
|
||||
|
||||
const pageCount = pdf.numPages;
|
||||
const page = await pdf.getPage(1);
|
||||
const pageDimensions: Array<{ width: number; height: number }> = [];
|
||||
|
||||
// If applyRotation is false, render without rotation (for CSS-based rotation)
|
||||
// If applyRotation is true, let PDF.js apply rotation (for static display)
|
||||
const viewport = applyRotation
|
||||
? page.getViewport({ scale })
|
||||
: page.getViewport({ scale, rotation: 0 });
|
||||
const baseViewport = page.getViewport({ scale: 1, rotation: 0 });
|
||||
pageDimensions[0] = {
|
||||
width: baseViewport.width,
|
||||
height: baseViewport.height
|
||||
};
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = viewport.width;
|
||||
@ -428,10 +435,17 @@ export async function generateThumbnailWithMetadata(file: File, applyRotation: b
|
||||
const p = await pdf.getPage(i);
|
||||
const rotation = p.rotate || 0;
|
||||
pageRotations.push(rotation);
|
||||
if (!pageDimensions[i - 1]) {
|
||||
const pageViewport = p.getViewport({ scale: 1, rotation: 0 });
|
||||
pageDimensions[i - 1] = {
|
||||
width: pageViewport.width,
|
||||
height: pageViewport.height
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pdfWorkerManager.destroyDocument(pdf);
|
||||
return { thumbnail, pageCount, pageRotations };
|
||||
return { thumbnail, pageCount, pageRotations, pageDimensions };
|
||||
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.name === "PasswordException") {
|
||||
|
||||
188
frontend/src/core/utils/viewerZoom.ts
Normal file
188
frontend/src/core/utils/viewerZoom.ts
Normal file
@ -0,0 +1,188 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
export const DEFAULT_VISIBILITY_THRESHOLD = 80; // Require at least 80% of the page height to be visible
|
||||
export const DEFAULT_FALLBACK_ZOOM = 1.44; // 144% fallback when no reliable metadata is present
|
||||
|
||||
export interface ZoomViewport {
|
||||
clientWidth?: number;
|
||||
clientHeight?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export type AutoZoomDecision =
|
||||
| { type: 'fallback'; zoom: number }
|
||||
| { type: 'fitWidth' }
|
||||
| { type: 'adjust'; zoom: number };
|
||||
|
||||
export interface AutoZoomParams {
|
||||
viewportWidth: number;
|
||||
viewportHeight: number;
|
||||
fitWidthZoom: number;
|
||||
pagesPerSpread: number;
|
||||
pageRect?: { width: number; height: number } | null;
|
||||
metadataAspectRatio?: number | null;
|
||||
visibilityThreshold?: number;
|
||||
fallbackZoom?: number;
|
||||
}
|
||||
|
||||
export function determineAutoZoom({
|
||||
viewportWidth,
|
||||
viewportHeight,
|
||||
fitWidthZoom,
|
||||
pagesPerSpread,
|
||||
pageRect,
|
||||
metadataAspectRatio,
|
||||
visibilityThreshold = DEFAULT_VISIBILITY_THRESHOLD,
|
||||
fallbackZoom = DEFAULT_FALLBACK_ZOOM,
|
||||
}: AutoZoomParams): AutoZoomDecision {
|
||||
const rectWidth = pageRect?.width ?? 0;
|
||||
const rectHeight = pageRect?.height ?? 0;
|
||||
|
||||
const aspectRatio: number | null =
|
||||
rectWidth > 0 ? rectHeight / rectWidth : metadataAspectRatio ?? null;
|
||||
|
||||
let renderedHeight: number | null = rectHeight > 0 ? rectHeight : null;
|
||||
|
||||
if (!renderedHeight || renderedHeight <= 0) {
|
||||
if (aspectRatio == null || aspectRatio <= 0) {
|
||||
return { type: 'fallback', zoom: Math.min(fitWidthZoom, fallbackZoom) };
|
||||
}
|
||||
|
||||
const pageWidth = viewportWidth / (fitWidthZoom * pagesPerSpread);
|
||||
const pageHeight = pageWidth * aspectRatio;
|
||||
renderedHeight = pageHeight * fitWidthZoom;
|
||||
}
|
||||
|
||||
if (!renderedHeight || renderedHeight <= 0) {
|
||||
return { type: 'fitWidth' };
|
||||
}
|
||||
|
||||
const isLandscape = aspectRatio !== null && aspectRatio < 1;
|
||||
const targetVisibility = isLandscape ? 100 : visibilityThreshold;
|
||||
|
||||
const visiblePercent = (viewportHeight / renderedHeight) * 100;
|
||||
|
||||
if (visiblePercent >= targetVisibility) {
|
||||
return { type: 'fitWidth' };
|
||||
}
|
||||
|
||||
const allowableHeightRatio = targetVisibility / 100;
|
||||
const zoomScale =
|
||||
viewportHeight / (allowableHeightRatio * renderedHeight);
|
||||
const targetZoom = Math.min(fitWidthZoom, fitWidthZoom * zoomScale);
|
||||
|
||||
if (Math.abs(targetZoom - fitWidthZoom) < 0.001) {
|
||||
return { type: 'fitWidth' };
|
||||
}
|
||||
|
||||
return { type: 'adjust', zoom: targetZoom };
|
||||
}
|
||||
|
||||
export interface MeasurePageRectOptions {
|
||||
selector?: string;
|
||||
maxAttempts?: number;
|
||||
shouldCancel?: () => boolean;
|
||||
}
|
||||
|
||||
export async function measureRenderedPageRect({
|
||||
selector = '[data-page-index="0"]',
|
||||
maxAttempts = 12,
|
||||
shouldCancel,
|
||||
}: MeasurePageRectOptions = {}): Promise<DOMRect | null> {
|
||||
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
let rafId: number | null = null;
|
||||
|
||||
const waitForNextFrame = () =>
|
||||
new Promise<void>((resolve) => {
|
||||
rafId = window.requestAnimationFrame(() => {
|
||||
rafId = null;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
||||
if (shouldCancel?.()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const element = document.querySelector(selector) as HTMLElement | null;
|
||||
|
||||
if (element) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
if (rect.width > 0 && rect.height > 0) {
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
|
||||
await waitForNextFrame();
|
||||
}
|
||||
} finally {
|
||||
if (rafId !== null) {
|
||||
window.cancelAnimationFrame(rafId);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export interface FitWidthResizeOptions {
|
||||
isManaged: boolean;
|
||||
requestFitWidth: () => void;
|
||||
onDebouncedResize: () => void;
|
||||
debounceMs?: number;
|
||||
}
|
||||
|
||||
export function useFitWidthResize({
|
||||
isManaged,
|
||||
requestFitWidth,
|
||||
onDebouncedResize,
|
||||
debounceMs = 150,
|
||||
}: FitWidthResizeOptions): void {
|
||||
const managedRef = useRef(isManaged);
|
||||
const requestFitWidthRef = useRef(requestFitWidth);
|
||||
const onDebouncedResizeRef = useRef(onDebouncedResize);
|
||||
|
||||
useEffect(() => {
|
||||
managedRef.current = isManaged;
|
||||
}, [isManaged]);
|
||||
|
||||
useEffect(() => {
|
||||
requestFitWidthRef.current = requestFitWidth;
|
||||
}, [requestFitWidth]);
|
||||
|
||||
useEffect(() => {
|
||||
onDebouncedResizeRef.current = onDebouncedResize;
|
||||
}, [onDebouncedResize]);
|
||||
|
||||
useEffect(() => {
|
||||
let timeoutId: number | undefined;
|
||||
|
||||
const handleResize = () => {
|
||||
if (!managedRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof timeoutId === 'number') {
|
||||
window.clearTimeout(timeoutId);
|
||||
}
|
||||
|
||||
timeoutId = window.setTimeout(() => {
|
||||
requestFitWidthRef.current?.();
|
||||
onDebouncedResizeRef.current?.();
|
||||
}, debounceMs);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => {
|
||||
if (typeof timeoutId === 'number') {
|
||||
window.clearTimeout(timeoutId);
|
||||
}
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, [debounceMs]);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user