From 8c95bfe81b5d81c3c71708a93877b637b8c6a290 Mon Sep 17 00:00:00 2001 From: Gslmao Date: Fri, 10 Apr 2026 21:50:42 +0530 Subject: [PATCH 1/9] feat: swapped the pdf viewer with embedPDF --- package.json | 10 +- pnpm-lock.yaml | 497 +++++++++++++++++++++++++++++++- src/app/paper/[id]/page.tsx | 11 +- src/components/ReportButton.tsx | 2 +- src/components/ShareButton.tsx | 2 +- src/components/newPdfViewer.tsx | 255 ++++++++++++++++ src/components/pdfViewer.tsx | 21 +- 7 files changed, 772 insertions(+), 26 deletions(-) create mode 100644 src/components/newPdfViewer.tsx diff --git a/package.json b/package.json index ed875c45..342b6f11 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,14 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "@embedpdf/core": "^2.14.0", + "@embedpdf/engines": "^2.14.0", + "@embedpdf/plugin-document-manager": "^2.14.0", + "@embedpdf/plugin-export": "^2.14.0", + "@embedpdf/plugin-render": "^2.14.0", + "@embedpdf/plugin-scroll": "^2.14.0", + "@embedpdf/plugin-viewport": "^2.14.0", + "@embedpdf/plugin-zoom": "^2.14.0", "@google-cloud/storage": "^7.17.1", "@google/genai": "^0.7.0", "@radix-ui/react-accordion": "^1.2.4", @@ -56,7 +64,7 @@ "prettier": "^3.5.3", "prettier-plugin-tailwindcss": "^0.6.11", "raw-loader": "^4.0.2", - "react": "^18.3.1", + "react": "link:@embedpdf/plugin-viewport/react", "react-beautiful-dnd": "^13.1.1", "react-dom": "^18.3.1", "react-dropzone": "^14.3.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6a7f3cdf..5f809cd9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,30 @@ importers: '@dnd-kit/utilities': specifier: ^3.2.2 version: 3.2.2(react@18.3.1) + '@embedpdf/core': + specifier: ^2.14.0 + version: 2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/engines': + specifier: ^2.14.0 + version: 2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/plugin-document-manager': + specifier: ^2.14.0 + version: 2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/plugin-export': + specifier: ^2.14.0 + version: 2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/plugin-render': + specifier: ^2.14.0 + version: 2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/plugin-scroll': + specifier: ^2.14.0 + version: 2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(@embedpdf/plugin-viewport@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/plugin-viewport': + specifier: ^2.14.0 + version: 2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/plugin-zoom': + specifier: ^2.14.0 + version: 2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(@embedpdf/plugin-scroll@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(@embedpdf/plugin-viewport@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(@embedpdf/plugin-viewport@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) '@google-cloud/storage': specifier: ^7.17.1 version: 7.17.1 @@ -147,7 +171,7 @@ importers: specifier: ^4.0.2 version: 4.0.2(webpack@5.102.0) react: - specifier: ^18.3.1 + specifier: link:@embedpdf/plugin-viewport/react version: 18.3.1 react-beautiful-dnd: specifier: ^13.1.1 @@ -214,10 +238,27 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.28.4': resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + '@devmehq/email-validator-js@2.10.2': resolution: {integrity: sha512-buwijUWnL+Vbs4uOd4nBtmNnrxBpCODT4c9Rqz242u8SgReCtCHU25RO8ZvCn68fXqDFtY4tgk9j5XAu2dk5BQ==} engines: {node: '>= 12.0'} @@ -244,6 +285,114 @@ packages: peerDependencies: react: '>=16.8.0' + '@embedpdf/core@2.14.0': + resolution: {integrity: sha512-Fd26Xs9EIXdgYn3fJU4H1nGugdKXzYG0hG/HGCeze8pSb5Fh1GsNOOKg6lFsglhulwNhei7aaoJCxdYOjt+vZg==} + peerDependencies: + preact: ^10.26.4 + react: '>=16.8.0' + react-dom: '>=16.8.0' + svelte: '>=5 <6' + vue: '>=3.2.0' + + '@embedpdf/engines@2.14.0': + resolution: {integrity: sha512-fYK4eRaVydk9zt1cfoXERya68FCmUKFr/eAjXilLjb4pKPt5HBJZlsZsJ/eGGbchDmvJIGo02PBtH5KgAxhA5A==} + peerDependencies: + preact: ^10.26.4 + react: '>=16.8.0' + react-dom: '>=16.8.0' + svelte: '>=5 <6' + vue: '>=3.2.0' + + '@embedpdf/fonts-arabic@1.0.0': + resolution: {integrity: sha512-SnGvQb+LwPZQO2WjjvlmXrJZolJUfLYbLZQSaYUw1vrQyMyJKT4LewvJGG+hZ+Yz2fz7OMIQ+4Gc98mGODZtOg==} + + '@embedpdf/fonts-hebrew@1.0.0': + resolution: {integrity: sha512-5HVAKGL7VqPeTxxADDrSqAFBxfmAXdP8fIqrPwJIKkqdK2643bOer8CqnnpO3/nPoFhkzxhttWMB9BGiqSW62w==} + + '@embedpdf/fonts-jp@1.0.0': + resolution: {integrity: sha512-BY2tv/mcICUUKf+M/bizf3RU65PMqKClJ/e5o9mgMibxyML0OQvEDwYMRPODQkKgJKXCO3ScHmVvcmXp6kt+fA==} + + '@embedpdf/fonts-kr@1.0.0': + resolution: {integrity: sha512-bh88HXSvOBS581kgmihWY7Ijp9hBsvlmXogFG5LSNx9UBAobRcakZiFMGieRBc06hUSkpo7WhjaFM/z/SfQ8dQ==} + + '@embedpdf/fonts-latin@1.0.0': + resolution: {integrity: sha512-LLYysdr8O6sRNzhmW3PbF3AeA8xnqvOi4XLFfIfNlW5uEZ+qsJdcfd78Q78sFJMhlaOAYFMziMMsnOzmx463rA==} + + '@embedpdf/fonts-sc@1.0.0': + resolution: {integrity: sha512-ETXl7XCwaQLSSvMO3EUDwMNqtL64kX2LlFxarTRi/NsIGGOIxUurGfKtrkmtnKHrWy1jAJSt6oxK2uJhvdvQIw==} + + '@embedpdf/fonts-tc@1.0.0': + resolution: {integrity: sha512-rGZJbVD6DYS5BbXdpEMnWkpVF0Knar+bsiyb2o3+YRx7O8eyFubEBQUSUInirQk69HA6fc3GhYCg7TyC/oD76Q==} + + '@embedpdf/models@2.14.0': + resolution: {integrity: sha512-MAszh66Phe76oSabj8YMYaNuIKqS8UYBnL841MaTTRYxzfEuLTPrZwzqCDjIb7qUOAW75kWI9x/AdOukDQbHYw==} + + '@embedpdf/pdfium@2.14.0': + resolution: {integrity: sha512-yOg1VcIZPXASrzNmS4ZRjo78SUxf9ohey2zTSP7qOR+opMS0OenI/iXtbFGizpZFtk0gfK1Y1HQOxvDpuIiOEw==} + + '@embedpdf/plugin-document-manager@2.14.0': + resolution: {integrity: sha512-p/qwPeygrWi6H5YmWxiRQ/PBWMswi/nm22DDQa7HOabQNAI5glHrz5bv1Z7P5dwkupYiz5qCI5ObAbY/WWtEQQ==} + peerDependencies: + '@embedpdf/core': 2.14.0 + preact: ^10.26.4 + react: '>=16.8.0' + react-dom: '>=16.8.0' + svelte: '>=5 <6' + vue: '>=3.2.0' + + '@embedpdf/plugin-export@2.14.0': + resolution: {integrity: sha512-QrML9qr5lCBjOOUkBJ88B8sW5olF7szibdo20XMjLSwULW+yGmMJ0M3GgPqT0MlJWu0gZQiMjzx5N8fGPB98aw==} + peerDependencies: + '@embedpdf/core': 2.14.0 + preact: ^10.26.4 + react: '>=16.8.0' + react-dom: '>=16.8.0' + svelte: '>=5 <6' + vue: '>=3.2.0' + + '@embedpdf/plugin-render@2.14.0': + resolution: {integrity: sha512-VcypqFBBypixGiq0dXcxyUO3B05Gae3PafzRrP52uJ0R/PY2IfLQ/eQ6gmRIQpXX1MZ9Q7X1Gm1Ql1iWC8ZAWw==} + peerDependencies: + '@embedpdf/core': 2.14.0 + preact: ^10.26.4 + react: '>=16.8.0' + react-dom: '>=16.8.0' + svelte: '>=5 <6' + vue: '>=3.2.0' + + '@embedpdf/plugin-scroll@2.14.0': + resolution: {integrity: sha512-eQhz+lTMEr/0dPAoavP/HXfUppy1S50YZpt0kn5pHfjPZb1HuWMDUkmq8DCL0BcGnt3wT2m2Z30RK1LEUkyfvA==} + peerDependencies: + '@embedpdf/core': 2.14.0 + '@embedpdf/plugin-viewport': 2.14.0 + preact: ^10.26.4 + react: '>=16.8.0' + react-dom: '>=16.8.0' + svelte: '>=5 <6' + vue: '>=3.2.0' + + '@embedpdf/plugin-viewport@2.14.0': + resolution: {integrity: sha512-O7GIm90uMopqvJsBwt4y3Hwry/vVXiegAMubSnlQxbrBhDufDP0CkIvSK7f9wkialBLP+VfrVgZ+ycDFjpWJFw==} + peerDependencies: + '@embedpdf/core': 2.14.0 + preact: ^10.26.4 + react: '>=16.8.0' + react-dom: '>=16.8.0' + svelte: '>=5 <6' + vue: '>=3.2.0' + + '@embedpdf/plugin-zoom@2.14.0': + resolution: {integrity: sha512-BkGim8aUvApZECvmVB4uzkmaD76KenRnJuKMaHVZznGm7BvOHWuX/1sg9zzhF4dvyZRmH0tzIMovUgdn89f9AQ==} + peerDependencies: + '@embedpdf/core': 2.14.0 + '@embedpdf/plugin-scroll': 2.14.0 + '@embedpdf/plugin-viewport': 2.14.0 + preact: ^10.26.4 + react: '>=16.8.0' + react-dom: '>=16.8.0' + svelte: '>=5 <6' + vue: '>=3.2.0' + '@emnapi/core@1.5.0': resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} @@ -326,6 +475,9 @@ packages: '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -991,6 +1143,11 @@ packages: '@rushstack/eslint-patch@1.12.0': resolution: {integrity: sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==} + '@sveltejs/acorn-typescript@1.0.9': + resolution: {integrity: sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==} + peerDependencies: + acorn: ^8.9.0 + '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} @@ -1074,6 +1231,9 @@ packages: '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -1255,6 +1415,35 @@ packages: '@upstash/redis@1.35.7': resolution: {integrity: sha512-bdCdKhke+kYUjcLLuGWSeQw7OLuWIx3eyKksyToLBAlGIMX9qiII0ptp8E0y7VFE1yuBxBd/3kSzJ8774Q4g+A==} + '@vue/compiler-core@3.5.32': + resolution: {integrity: sha512-4x74Tbtqnda8s/NSD6e1Dr5p1c8HdMU5RWSjMSUzb8RTcUQqevDCxVAitcLBKT+ie3o0Dl9crc/S/opJM7qBGQ==} + + '@vue/compiler-dom@3.5.32': + resolution: {integrity: sha512-ybHAu70NtiEI1fvAUz3oXZqkUYEe5J98GjMDpTGl5iHb0T15wQYLR4wE3h9xfuTNA+Cm2f4czfe8B4s+CCH57Q==} + + '@vue/compiler-sfc@3.5.32': + resolution: {integrity: sha512-8UYUYo71cP/0YHMO814TRZlPuUUw3oifHuMR7Wp9SNoRSrxRQnhMLNlCeaODNn6kNTJsjFoQ/kqIj4qGvya4Xg==} + + '@vue/compiler-ssr@3.5.32': + resolution: {integrity: sha512-Gp4gTs22T3DgRotZ8aA/6m2jMR+GMztvBXUBEUOYOcST+giyGWJ4WvFd7QLHBkzTxkfOt8IELKNdpzITLbA2rw==} + + '@vue/reactivity@3.5.32': + resolution: {integrity: sha512-/ORasxSGvZ6MN5gc+uE364SxFdJ0+WqVG0CENXaGW58TOCdrAW76WWaplDtECeS1qphvtBZtR+3/o1g1zL4xPQ==} + + '@vue/runtime-core@3.5.32': + resolution: {integrity: sha512-pDrXCejn4UpFDFmMd27AcJEbHaLemaE5o4pbb7sLk79SRIhc6/t34BQA7SGNgYtbMnvbF/HHOftYBgFJtUoJUQ==} + + '@vue/runtime-dom@3.5.32': + resolution: {integrity: sha512-1CDVv7tv/IV13V8Nip1k/aaObVbWqRlVCVezTwx3K07p7Vxossp5JU1dcPNhJk3w347gonIUT9jQOGutyJrSVQ==} + + '@vue/server-renderer@3.5.32': + resolution: {integrity: sha512-IOjm2+JQwRFS7W28HNuJeXQle9KdZbODFY7hFGVtnnghF51ta20EWAZJHX+zLGtsHhaU6uC9BGPV52KVpYryMQ==} + peerDependencies: + vue: 3.5.32 + + '@vue/shared@3.5.32': + resolution: {integrity: sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==} + '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -1391,6 +1580,10 @@ packages: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} + aria-query@5.3.1: + resolution: {integrity: sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==} + engines: {node: '>= 0.4'} + aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -1628,6 +1821,9 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} @@ -1706,6 +1902,9 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + devalue@5.7.1: + resolution: {integrity: sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==} + didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -1774,6 +1973,10 @@ packages: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + es-abstract@1.24.0: resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} engines: {node: '>= 0.4'} @@ -1913,6 +2116,9 @@ packages: deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1921,6 +2127,9 @@ packages: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} + esrap@2.2.4: + resolution: {integrity: sha512-suICpxAmZ9A8bzJjEl/+rLJiDKC0X4gYWUxT6URAWBLvlXmtbZd5ySMu/N2ZGEtMCAmflUDPSehrP9BQcsGcSg==} + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -1933,6 +2142,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -2359,6 +2571,9 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -2513,6 +2728,9 @@ packages: resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} engines: {node: '>=8.9.0'} + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -2539,6 +2757,9 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + make-cancellable-promise@1.3.2: resolution: {integrity: sha512-GCXh3bq/WuMbS+Ky4JBPW1hYTOU+znU+Q5m9Pu+pI8EoUqIHk9+tviOKC6/qhHh8C4/As3tzJ69IF32kdz85ww==} @@ -2922,6 +3143,13 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.9: + resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==} + engines: {node: ^10 || ^12 || >=14} + + preact@10.29.1: + resolution: {integrity: sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==} + prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} @@ -3441,6 +3669,10 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svelte@5.55.2: + resolution: {integrity: sha512-z41M/hi0ZPTzrwVKLvB/R1/Oo08gL1uIib8HZ+FncqxxtY9MLb01emg2fqk+WLZ/lNrrtNDFh7BZLDxAHvMgLw==} + engines: {node: '>=18'} + tailwind-merge@2.6.0: resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} @@ -3636,6 +3868,14 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true + vue@3.5.32: + resolution: {integrity: sha512-vM4z4Q9tTafVfMAK7IVzmxg34rSzTFMyIe0UUEijUCkn9+23lj0WRfA83dg7eQZIUlgOSGrkViIaCfqSAUXsMw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + warning@4.0.3: resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} @@ -3745,6 +3985,9 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -3752,8 +3995,21 @@ snapshots: '@alloc/quick-lru@5.2.0': {} + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + '@babel/runtime@7.28.4': {} + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@devmehq/email-validator-js@2.10.2': dependencies: psl: 1.15.0 @@ -3785,6 +4041,114 @@ snapshots: react: 18.3.1 tslib: 2.8.1 + '@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3))': + dependencies: + '@embedpdf/engines': 2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/models': 2.14.0 + preact: 10.29.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + svelte: 5.55.2 + vue: 3.5.32(typescript@5.9.3) + + '@embedpdf/engines@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3))': + dependencies: + '@embedpdf/fonts-arabic': 1.0.0 + '@embedpdf/fonts-hebrew': 1.0.0 + '@embedpdf/fonts-jp': 1.0.0 + '@embedpdf/fonts-kr': 1.0.0 + '@embedpdf/fonts-latin': 1.0.0 + '@embedpdf/fonts-sc': 1.0.0 + '@embedpdf/fonts-tc': 1.0.0 + '@embedpdf/models': 2.14.0 + '@embedpdf/pdfium': 2.14.0 + preact: 10.29.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + svelte: 5.55.2 + vue: 3.5.32(typescript@5.9.3) + + '@embedpdf/fonts-arabic@1.0.0': {} + + '@embedpdf/fonts-hebrew@1.0.0': {} + + '@embedpdf/fonts-jp@1.0.0': {} + + '@embedpdf/fonts-kr@1.0.0': {} + + '@embedpdf/fonts-latin@1.0.0': {} + + '@embedpdf/fonts-sc@1.0.0': {} + + '@embedpdf/fonts-tc@1.0.0': {} + + '@embedpdf/models@2.14.0': {} + + '@embedpdf/pdfium@2.14.0': {} + + '@embedpdf/plugin-document-manager@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3))': + dependencies: + '@embedpdf/core': 2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/models': 2.14.0 + preact: 10.29.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + svelte: 5.55.2 + vue: 3.5.32(typescript@5.9.3) + + '@embedpdf/plugin-export@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3))': + dependencies: + '@embedpdf/core': 2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/models': 2.14.0 + preact: 10.29.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + svelte: 5.55.2 + vue: 3.5.32(typescript@5.9.3) + + '@embedpdf/plugin-render@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3))': + dependencies: + '@embedpdf/core': 2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/models': 2.14.0 + preact: 10.29.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + svelte: 5.55.2 + vue: 3.5.32(typescript@5.9.3) + + '@embedpdf/plugin-scroll@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(@embedpdf/plugin-viewport@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3))': + dependencies: + '@embedpdf/core': 2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/models': 2.14.0 + '@embedpdf/plugin-viewport': 2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + preact: 10.29.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + svelte: 5.55.2 + vue: 3.5.32(typescript@5.9.3) + + '@embedpdf/plugin-viewport@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3))': + dependencies: + '@embedpdf/core': 2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/models': 2.14.0 + preact: 10.29.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + svelte: 5.55.2 + vue: 3.5.32(typescript@5.9.3) + + ? '@embedpdf/plugin-zoom@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(@embedpdf/plugin-scroll@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(@embedpdf/plugin-viewport@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(@embedpdf/plugin-viewport@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3))' + : dependencies: + '@embedpdf/core': 2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/models': 2.14.0 + '@embedpdf/plugin-scroll': 2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(@embedpdf/plugin-viewport@2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + '@embedpdf/plugin-viewport': 2.14.0(@embedpdf/core@2.14.0(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)))(preact@10.29.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svelte@5.55.2)(vue@3.5.32(typescript@5.9.3)) + preact: 10.29.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + svelte: 5.55.2 + vue: 3.5.32(typescript@5.9.3) + '@emnapi/core@1.5.0': dependencies: '@emnapi/wasi-threads': 1.1.0 @@ -3907,6 +4271,11 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/source-map@0.3.11': @@ -4541,6 +4910,10 @@ snapshots: '@rushstack/eslint-patch@1.12.0': {} + '@sveltejs/acorn-typescript@1.0.9(acorn@8.15.0)': + dependencies: + acorn: 8.15.0 + '@swc/counter@0.1.3': {} '@swc/helpers@0.5.5': @@ -4630,6 +5003,8 @@ snapshots: '@types/tough-cookie@4.0.5': {} + '@types/trusted-types@2.0.7': {} + '@types/webidl-conversions@7.0.3': {} '@types/whatwg-url@11.0.5': @@ -4809,6 +5184,60 @@ snapshots: dependencies: uncrypto: 0.1.3 + '@vue/compiler-core@3.5.32': + dependencies: + '@babel/parser': 7.29.2 + '@vue/shared': 3.5.32 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.32': + dependencies: + '@vue/compiler-core': 3.5.32 + '@vue/shared': 3.5.32 + + '@vue/compiler-sfc@3.5.32': + dependencies: + '@babel/parser': 7.29.2 + '@vue/compiler-core': 3.5.32 + '@vue/compiler-dom': 3.5.32 + '@vue/compiler-ssr': 3.5.32 + '@vue/shared': 3.5.32 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.9 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.32': + dependencies: + '@vue/compiler-dom': 3.5.32 + '@vue/shared': 3.5.32 + + '@vue/reactivity@3.5.32': + dependencies: + '@vue/shared': 3.5.32 + + '@vue/runtime-core@3.5.32': + dependencies: + '@vue/reactivity': 3.5.32 + '@vue/shared': 3.5.32 + + '@vue/runtime-dom@3.5.32': + dependencies: + '@vue/reactivity': 3.5.32 + '@vue/runtime-core': 3.5.32 + '@vue/shared': 3.5.32 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.32(vue@3.5.32(typescript@5.9.3))': + dependencies: + '@vue/compiler-ssr': 3.5.32 + '@vue/shared': 3.5.32 + vue: 3.5.32(typescript@5.9.3) + + '@vue/shared@3.5.32': {} + '@webassemblyjs/ast@1.14.1': dependencies: '@webassemblyjs/helper-numbers': 1.13.2 @@ -4963,6 +5392,8 @@ snapshots: dependencies: tslib: 2.8.1 + aria-query@5.3.1: {} + aria-query@5.3.2: {} array-buffer-byte-length@1.0.2: @@ -5227,6 +5658,8 @@ snapshots: csstype@3.1.3: {} + csstype@3.2.3: {} + damerau-levenshtein@1.0.8: {} data-uri-to-buffer@4.0.1: {} @@ -5289,6 +5722,8 @@ snapshots: detect-node-es@1.1.0: {} + devalue@5.7.1: {} + didyoumean@1.2.2: {} dijkstrajs@1.0.3: {} @@ -5355,6 +5790,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 + entities@7.0.1: {} + es-abstract@1.24.0: dependencies: array-buffer-byte-length: 1.0.2 @@ -5647,6 +6084,8 @@ snapshots: transitivePeerDependencies: - supports-color + esm-env@1.2.2: {} + espree@9.6.1: dependencies: acorn: 8.15.0 @@ -5657,6 +6096,11 @@ snapshots: dependencies: estraverse: 5.3.0 + esrap@2.2.4: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@typescript-eslint/types': 8.45.0 + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -5665,6 +6109,8 @@ snapshots: estraverse@5.3.0: {} + estree-walker@2.0.2: {} + esutils@2.0.3: {} event-target-shim@5.0.1: {} @@ -6144,6 +6590,10 @@ snapshots: is-path-inside@3.0.3: {} + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.8 + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -6306,6 +6756,8 @@ snapshots: emojis-list: 3.0.0 json5: 2.2.3 + locate-character@3.0.0: {} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -6328,6 +6780,10 @@ snapshots: dependencies: react: 18.3.1 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + make-cancellable-promise@1.3.2: {} make-event-props@1.6.2: {} @@ -6662,6 +7118,14 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.9: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact@10.29.1: {} + prebuild-install@7.1.3: dependencies: detect-libc: 2.1.1 @@ -7202,6 +7666,25 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svelte@5.55.2: + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.9(acorn@8.15.0) + '@types/estree': 1.0.8 + '@types/trusted-types': 2.0.7 + acorn: 8.15.0 + aria-query: 5.3.1 + axobject-query: 4.1.0 + clsx: 2.1.1 + devalue: 5.7.1 + esm-env: 1.2.2 + esrap: 2.2.4 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.21 + zimmerframe: 1.1.4 + tailwind-merge@2.6.0: {} tailwindcss-animate@1.0.7(tailwindcss@3.4.18): @@ -7445,6 +7928,16 @@ snapshots: uuid@9.0.1: {} + vue@3.5.32(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.32 + '@vue/compiler-sfc': 3.5.32 + '@vue/runtime-dom': 3.5.32 + '@vue/server-renderer': 3.5.32(vue@3.5.32(typescript@5.9.3)) + '@vue/shared': 3.5.32 + optionalDependencies: + typescript: 5.9.3 + warning@4.0.3: dependencies: loose-envify: 1.4.0 @@ -7598,4 +8091,6 @@ snapshots: yocto-queue@0.1.0: {} + zimmerframe@1.1.4: {} + zod@3.25.76: {} diff --git a/src/app/paper/[id]/page.tsx b/src/app/paper/[id]/page.tsx index 80c8d02f..552b52a9 100644 --- a/src/app/paper/[id]/page.tsx +++ b/src/app/paper/[id]/page.tsx @@ -8,6 +8,7 @@ import axios, { type AxiosResponse } from "axios"; import { type Metadata } from "next"; import { redirect } from "next/navigation"; import { PaperProvider } from "@/context/PaperContext"; +import PDFViewer from "@/components/newPdfViewer"; export async function generateMetadata({ params, @@ -174,10 +175,16 @@ const PaperPage = async ({ params }: { params: { id: string } }) => { year: paper.year, }} > - + + {/* + > */} + diff --git a/src/components/ReportButton.tsx b/src/components/ReportButton.tsx index 80cd7f80..98405348 100644 --- a/src/components/ReportButton.tsx +++ b/src/components/ReportButton.tsx @@ -13,7 +13,7 @@ export default function ReportButton(){ <> diff --git a/src/components/ShareButton.tsx b/src/components/ShareButton.tsx index 475d5bb1..f83958c2 100644 --- a/src/components/ShareButton.tsx +++ b/src/components/ShareButton.tsx @@ -30,7 +30,7 @@ export default function ShareButton() { return ( - diff --git a/src/components/newPdfViewer.tsx b/src/components/newPdfViewer.tsx new file mode 100644 index 00000000..1cdfe74f --- /dev/null +++ b/src/components/newPdfViewer.tsx @@ -0,0 +1,255 @@ +"use client" +import { createPluginRegistration } from '@embedpdf/core'; +import { EmbedPDF } from '@embedpdf/core/react'; +import { usePdfiumEngine } from '@embedpdf/engines/react'; + +import { Download, ZoomIn, ZoomOut, Maximize2, Minimize2 } from "lucide-react"; +import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { Button } from "./ui/button"; +import { downloadFile } from "../lib/utils/download"; + +import ShareButton from "./ShareButton"; +import ReportButton from "./ReportButton"; + +import { Viewport, ViewportPluginPackage } from '@embedpdf/plugin-viewport/react'; +import { useScroll, Scroller, ScrollPluginPackage } from '@embedpdf/plugin-scroll/react'; +import { DocumentContent, DocumentManagerPluginPackage } from '@embedpdf/plugin-document-manager/react'; +import { useZoom, ZoomPluginPackage, ZoomMode } from '@embedpdf/plugin-zoom/react'; +import { RenderLayer, RenderPluginPackage } from '@embedpdf/plugin-render/react'; +import { ExportPluginPackage } from '@embedpdf/plugin-export/react'; + +interface ControlProps { + documentId: string; + toggleFullscreen: () => void; + isFullscreen: boolean; + onDownload: () => Promise; +} + +interface PdfViewerProps { + url: string; + name: string; +} + +// memo — only re-renders when documentId / isFullscreen / onDownload / toggleFullscreen refs change. +// All function props must be stable (useCallback) in the parent for this to be effective. +const Controls = memo(function Controls({ + documentId, + toggleFullscreen, + isFullscreen, + onDownload, +}: ControlProps) { + const { provides: zoomProv, state: zoomState } = useZoom(documentId); + const { provides: scrollProv, state: scrollState } = useScroll(documentId); + const [pageNo, setPageNo] = useState("1"); + + useEffect(() => { + if (!scrollProv) return; + // Subscribe to page changes and keep the input in sync. + const unsub = scrollProv.onPageChange(() => + setPageNo(String(scrollProv.getCurrentPage())) + ); + return () => unsub(); + }, [scrollProv]); + + // pageNo and totalPages are reactive values read inside the handler, + // so they must be listed as deps to avoid a stale closure. + const pageChange = useCallback( + (e: React.KeyboardEvent) => { + if (e.key !== "Enter") return; + const page = parseInt(pageNo, 10); + if (!isNaN(page) && page >= 1 && page <= (scrollState?.totalPages ?? 1)) { + scrollProv?.scrollToPage({ pageNumber: page, behavior: "smooth" }); + } + }, + [pageNo, scrollState?.totalPages, scrollProv] + ); + + if (!zoomProv || !scrollProv) return null; + + const zoomIn = () => zoomProv.zoomIn(); + const zoomOut = () => zoomProv.zoomOut(); + const { zoomLevel } = zoomState; + const { totalPages } = scrollState; + + return ( +
+ + + + + + + + + + {typeof zoomLevel === "number" + ? `${Math.round(zoomLevel * 100)}%` + : zoomLevel} + + + + +
+ setPageNo(e.target.value)} + onKeyDown={pageChange} + onFocus={() => setPageNo("")} + className="h-9 w-14 rounded border bg-[#e7e9ff] p-1 text-center text-sm [appearance:textfield] dark:bg-[#1f1f2a] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" + /> + of {totalPages ?? 1} +
+ + +
+ ); +}); + +export default function PDFViewer({ url, name }: PdfViewerProps) { + const { engine, isLoading } = usePdfiumEngine(); + const [isFullscreen, setIsFullscreen] = useState(false); + const viewerRef = useRef(null); + + // Stable reference — only changes if url/name change (which should be never + // during a viewer session). Avoids re-creating the fn on every PDFViewer render. + const handleDownload = useCallback(async () => { + window.dataLayer?.push({ + event: "pdf_download_start", + paper_title: name, + paper_url: url, + }); + await downloadFile(url, `${name}.pdf`); + }, [url, name]); + + // toggleFullscreen reads only from a ref and DOM APIs — no React state + // is captured, so an empty dep array is correct and the reference never + // needs to change, keeping memo(Controls) happy. + const toggleFullscreen = useCallback(() => { + if (!document.fullscreenElement) { + void viewerRef.current?.requestFullscreen(); + } else { + void document.exitFullscreen(); + } + }, []); + + useEffect(() => { + const handleFullscreenChange = () => + setIsFullscreen(!!document.fullscreenElement); + document.addEventListener("fullscreenchange", handleFullscreenChange); + return () => document.removeEventListener("fullscreenchange", handleFullscreenChange); + }, []); + + // useMemo prevents the plugin array from being recreated on every render. + // url is the only real dep — if the url changes, a fresh plugin set is correct. + const plugins = useMemo(() => [ + createPluginRegistration(DocumentManagerPluginPackage, { + initialDocuments: [{ url }], + }), + createPluginRegistration(ViewportPluginPackage), + createPluginRegistration(ScrollPluginPackage), + createPluginRegistration(RenderPluginPackage), + createPluginRegistration(ZoomPluginPackage, { + defaultZoomLevel: ZoomMode.FitPage, + }), + createPluginRegistration(ExportPluginPackage, { + defaultFileName: `${name}.pdf`, + }), + ], [url, name]); + + if (isLoading || !engine) { + return
Loading PDF Engine...
; + } + + return ( +
+ + {({ activeDocumentId }) => + activeDocumentId && ( + <> + + {({ isLoaded }) => + isLoaded && ( + + ( +
+ +
+ )} + /> +
+ ) + } +
+ + + + ) + } +
+
+ ); +} \ No newline at end of file diff --git a/src/components/pdfViewer.tsx b/src/components/pdfViewer.tsx index 5758e553..0dda0cc3 100644 --- a/src/components/pdfViewer.tsx +++ b/src/components/pdfViewer.tsx @@ -72,22 +72,6 @@ export default function PdfViewer({ url, name }: PdfViewerProps) { } }, [handleScroll]); - const goToPreviousPage = () => { - setPageNumber((prev) => { - const newPage = Math.max(1, prev - 1); - scrollToPage(newPage); - return newPage; - }); - }; - - const goToNextPage = () => { - setPageNumber((prev) => { - const newPage = Math.min(numPages ?? 1, prev + 1); - scrollToPage(newPage); - return newPage; - }); - }; - const handlePageChange = (e: React.KeyboardEvent) => { if (e.key === "Enter") { const target = e.target as HTMLInputElement; @@ -107,7 +91,7 @@ export default function PdfViewer({ url, name }: PdfViewerProps) { }; const zoomIn = () => { - setScale((prev) => Math.min(prev + 0.1, 3)); + setScale((prev) => Math.min(prev*1.1, 3)); }; const zoomOut = () => { @@ -219,12 +203,9 @@ export default function PdfViewer({ url, name }: PdfViewerProps) { > - {(scale * 100).toFixed(0)}% - - diff --git a/src/components/newPdfViewer.tsx b/src/components/newPdfViewer.tsx index 1cdfe74f..4d7b6a9e 100644 --- a/src/components/newPdfViewer.tsx +++ b/src/components/newPdfViewer.tsx @@ -1,16 +1,7 @@ "use client" import { createPluginRegistration } from '@embedpdf/core'; -import { EmbedPDF } from '@embedpdf/core/react'; -import { usePdfiumEngine } from '@embedpdf/engines/react'; - -import { Download, ZoomIn, ZoomOut, Maximize2, Minimize2 } from "lucide-react"; -import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { Button } from "./ui/button"; -import { downloadFile } from "../lib/utils/download"; - -import ShareButton from "./ShareButton"; -import ReportButton from "./ReportButton"; - +import { EmbedPDF } from '@embedpdf/core/react'; +import { usePdfiumEngine } from '@embedpdf/engines/react'; import { Viewport, ViewportPluginPackage } from '@embedpdf/plugin-viewport/react'; import { useScroll, Scroller, ScrollPluginPackage } from '@embedpdf/plugin-scroll/react'; import { DocumentContent, DocumentManagerPluginPackage } from '@embedpdf/plugin-document-manager/react'; @@ -18,11 +9,21 @@ import { useZoom, ZoomPluginPackage, ZoomMode } from '@embedpdf/plugin- import { RenderLayer, RenderPluginPackage } from '@embedpdf/plugin-render/react'; import { ExportPluginPackage } from '@embedpdf/plugin-export/react'; +import { Download, ZoomIn, ZoomOut, Maximize2, Minimize2 } from "lucide-react"; +import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { downloadFile } from "../lib/utils/download"; +import { Button } from "./ui/button"; +import ShareButton from "./ShareButton"; +import ReportButton from "./ReportButton"; + interface ControlProps { documentId: string; toggleFullscreen: () => void; isFullscreen: boolean; onDownload: () => Promise; + forceMobile?: boolean; + isMobile: boolean; + isSmall: boolean; } interface PdfViewerProps { @@ -30,29 +31,34 @@ interface PdfViewerProps { name: string; } -// memo — only re-renders when documentId / isFullscreen / onDownload / toggleFullscreen refs change. -// All function props must be stable (useCallback) in the parent for this to be effective. -const Controls = memo(function Controls({ - documentId, - toggleFullscreen, - isFullscreen, - onDownload, -}: ControlProps) { +function useBreakpoint() { + const [width, setWidth] = useState(() => window.innerWidth); + + useEffect(() => { + setWidth(window.innerWidth); + const handler = () => setWidth(window.innerWidth); + window.addEventListener("resize", handler); + return () => window.removeEventListener("resize", handler); + }, []); + + return {isMobile: width < 768, isSmall: width < 640}; +} + +const Controls = memo(function Controls({documentId, toggleFullscreen, isFullscreen, onDownload, + forceMobile, isMobile, isSmall}: ControlProps) { + const { provides: zoomProv, state: zoomState } = useZoom(documentId); const { provides: scrollProv, state: scrollState } = useScroll(documentId); const [pageNo, setPageNo] = useState("1"); useEffect(() => { if (!scrollProv) return; - // Subscribe to page changes and keep the input in sync. const unsub = scrollProv.onPageChange(() => setPageNo(String(scrollProv.getCurrentPage())) ); return () => unsub(); }, [scrollProv]); - // pageNo and totalPages are reactive values read inside the handler, - // so they must be listed as deps to avoid a stale closure. const pageChange = useCallback( (e: React.KeyboardEvent) => { if (e.key !== "Enter") return; @@ -70,44 +76,39 @@ const Controls = memo(function Controls({ const zoomOut = () => zoomProv.zoomOut(); const { zoomLevel } = zoomState; const { totalPages } = scrollState; + + const fullScreenStyle = { + bottom: 20, + left: "50%", + transform: "translateX(-50%)", + } - return ( -
+ const pageInput = ( +
+ setPageNo(e.target.value)} + onKeyDown={pageChange} + onFocus={() => setPageNo("")} + className="h-9 w-14 rounded border bg-[#e7e9ff] p-1 text-center text-sm [appearance:textfield] dark:bg-[#1f1f2a] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" + /> + of {totalPages ?? 1} +
+ ) + + const toolSet = ( + <> @@ -117,49 +118,137 @@ const Controls = memo(function Controls({ - - {typeof zoomLevel === "number" - ? `${Math.round(zoomLevel * 100)}%` - : zoomLevel} + + {typeof zoomLevel === "number" && `${Math.round(zoomLevel * 100)}%`} -
- setPageNo(e.target.value)} - onKeyDown={pageChange} - onFocus={() => setPageNo("")} - className="h-9 w-14 rounded border bg-[#e7e9ff] p-1 text-center text-sm [appearance:textfield] dark:bg-[#1f1f2a] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" - /> - of {totalPages ?? 1} -
+ {isSmall && } + + + ) + + if (!forceMobile) { + return ( + <> + {!isSmall ? +
+ {toolSet} + {pageInput} + {!isSmall && } +
: +
+ {pageInput} +
+ {toolSet} +
+ +
} + + + ); + } - + return( +
+ {isSmall ? ( + <> + {pageInput} +
+ {toolSet} +
+ + ) : ( + <> + {toolSet} + {pageInput} + + + )}
- ); + ) }); export default function PDFViewer({ url, name }: PdfViewerProps) { const { engine, isLoading } = usePdfiumEngine(); + const {isMobile, isSmall} = useBreakpoint(); const [isFullscreen, setIsFullscreen] = useState(false); const viewerRef = useRef(null); - // Stable reference — only changes if url/name change (which should be never - // during a viewer session). Avoids re-creating the fn on every PDFViewer render. const handleDownload = useCallback(async () => { window.dataLayer?.push({ event: "pdf_download_start", @@ -169,9 +258,6 @@ export default function PDFViewer({ url, name }: PdfViewerProps) { await downloadFile(url, `${name}.pdf`); }, [url, name]); - // toggleFullscreen reads only from a ref and DOM APIs — no React state - // is captured, so an empty dep array is correct and the reference never - // needs to change, keeping memo(Controls) happy. const toggleFullscreen = useCallback(() => { if (!document.fullscreenElement) { void viewerRef.current?.requestFullscreen(); @@ -187,8 +273,6 @@ export default function PDFViewer({ url, name }: PdfViewerProps) { return () => document.removeEventListener("fullscreenchange", handleFullscreenChange); }, []); - // useMemo prevents the plugin array from being recreated on every render. - // url is the only real dep — if the url changes, a fresh plugin set is correct. const plugins = useMemo(() => [ createPluginRegistration(DocumentManagerPluginPackage, { initialDocuments: [{ url }], @@ -211,12 +295,22 @@ export default function PDFViewer({ url, name }: PdfViewerProps) { return (
{({ activeDocumentId }) => activeDocumentId && ( <> + {(isMobile && !isFullscreen) && + } {({ isLoaded }) => isLoaded && ( @@ -239,13 +333,19 @@ export default function PDFViewer({ url, name }: PdfViewerProps) { ) } - - + + {(!isMobile || isFullscreen) && ( + + )} + ) } diff --git a/src/components/pdfViewer.tsx b/src/components/pdfViewer.tsx index 0dda0cc3..3b6fc990 100644 --- a/src/components/pdfViewer.tsx +++ b/src/components/pdfViewer.tsx @@ -19,6 +19,8 @@ interface PdfViewerProps { name: string; } + + export default function PdfViewer({ url, name }: PdfViewerProps) { const [numPages, setNumPages] = useState(); const [pageNumber, setPageNumber] = useState(1); From 8f1f9e5b48e957f8ba9cbed7f50c02116924fade Mon Sep 17 00:00:00 2001 From: Gslmao Date: Sun, 12 Apr 2026 12:27:14 +0530 Subject: [PATCH 4/9] fix: report button made smaller --- src/components/ReportButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ReportButton.tsx b/src/components/ReportButton.tsx index 98405348..80cd7f80 100644 --- a/src/components/ReportButton.tsx +++ b/src/components/ReportButton.tsx @@ -13,7 +13,7 @@ export default function ReportButton(){ <> From 7b23009959c21a97e5c836368018a6c57cf276f1 Mon Sep 17 00:00:00 2001 From: Gslmao Date: Sun, 12 Apr 2026 12:30:33 +0530 Subject: [PATCH 5/9] fix: remove old pdfviewer --- src/components/pdfViewer.tsx | 381 ----------------------------------- 1 file changed, 381 deletions(-) delete mode 100644 src/components/pdfViewer.tsx diff --git a/src/components/pdfViewer.tsx b/src/components/pdfViewer.tsx deleted file mode 100644 index 3b6fc990..00000000 --- a/src/components/pdfViewer.tsx +++ /dev/null @@ -1,381 +0,0 @@ -"use client"; - -import "react-pdf/dist/Page/AnnotationLayer.css"; -import "react-pdf/dist/Page/TextLayer.css"; -import ReportButton from "./ReportButton"; -import { useState, useRef, useCallback, useEffect } from "react"; -import { Document, Page, pdfjs } from "react-pdf"; -import { Download, ZoomIn, ZoomOut, Maximize2, Minimize2 } from "lucide-react"; -import { Button } from "./ui/button"; -import { downloadFile } from "../lib/utils/download"; -import ShareButton from "./ShareButton"; -import Loader from "./ui/loader"; - -pdfjs.GlobalWorkerOptions.workerSrc = - "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.8.69/pdf.worker.min.mjs"; - -interface PdfViewerProps { - url: string; - name: string; -} - - - -export default function PdfViewer({ url, name }: PdfViewerProps) { - const [numPages, setNumPages] = useState(); - const [pageNumber, setPageNumber] = useState(1); - const [scale, setScale] = useState(1); - const [isFullscreen, setIsFullscreen] = useState(false); - const [inputValue, setInputValue] = useState(pageNumber.toString()); - const pageRefs = useRef<(HTMLDivElement | null)[]>([]); - const containerRef = useRef(null); - - function onDocumentLoadSuccess({ numPages }: { numPages: number }): void { - setNumPages(numPages); - setPageNumber(1); - pageRefs.current = Array(numPages).fill(null) as (HTMLDivElement | null)[]; - } - - const scrollToPage = useCallback((page: number) => { - if (pageRefs.current[page - 1] && containerRef.current) { - const pageElement = pageRefs.current[page - 1]; - const container = containerRef.current; - if (pageElement) { - const offset = pageElement.offsetTop - container.offsetTop; - container.scrollTo({ top: offset, behavior: "smooth" }); - setPageNumber(page); - } - } - }, []); - - const handleScroll = useCallback(() => { - if (!containerRef.current || !pageRefs.current) return; - const container = containerRef.current; - const scrollTop = container.scrollTop + container.offsetTop; - - for (let i = 0; i < pageRefs.current.length; i++) { - const pageEl = pageRefs.current[i]; - if (pageEl) { - const pageTop = pageEl.offsetTop; - const pageBottom = pageTop + pageEl.offsetHeight; - if (scrollTop >= pageTop && scrollTop < pageBottom) { - setPageNumber(i + 1); - break; - } - } - } - }, []); - - useEffect(() => { - const container = containerRef.current; - if (container) { - container.addEventListener("scroll", handleScroll); - return () => container.removeEventListener("scroll", handleScroll); - } - }, [handleScroll]); - - const handlePageChange = (e: React.KeyboardEvent) => { - if (e.key === "Enter") { - const target = e.target as HTMLInputElement; - if (/^\d+$/.test(inputValue)) { - const value = parseInt(inputValue, 10); - if (value >= 1 && value <= (numPages ?? 1)) { - setPageNumber(value); - scrollToPage(value); - } else { - setInputValue(pageNumber.toString()); - } - } else { - setInputValue(pageNumber.toString()); - } - target.blur(); - } - }; - - const zoomIn = () => { - setScale((prev) => Math.min(prev*1.1, 3)); - }; - - const zoomOut = () => { - setScale((prev) => Math.max(prev - 0.1, 0.25)); - }; - - const downloadPDF = async () => { - if (window.dataLayer) { - window.dataLayer.push({ - event: "pdf_download_start", - paper_title: name, - paper_url: url, - }); - } - const fileName = `${name}.pdf`; - await downloadFile(url, fileName); - }; - - const toggleFullscreen = () => { - if (!containerRef.current) return; - - if (!document.fullscreenElement) { - containerRef.current - .requestFullscreen() - .then(() => { - setIsFullscreen(true); - }) - .catch((err) => { - console.error("Error entering fullscreen:", err); - }); - } else { - document - .exitFullscreen() - .then(() => { - setIsFullscreen(false); - }) - .catch((err) => { - console.error("Error exiting fullscreen:", err); - }); - } - }; - - useEffect(() => { - setInputValue(pageNumber.toString()); - }, [pageNumber]); - - useEffect(() => { - const handleFullscreenChange = () => { - setIsFullscreen(!!document.fullscreenElement); - }; - - document.addEventListener("fullscreenchange", handleFullscreenChange); - return () => { - document.removeEventListener("fullscreenchange", handleFullscreenChange); - }; - }, []); - - useEffect(() => { - const calculateInitialScale = () => { - if (containerRef.current) { - const containerWidth = containerRef.current.offsetWidth; - const initialScale = containerWidth / 800; - setScale(initialScale > 1 ? 1 : initialScale); - } - }; - calculateInitialScale(); - }, []); - - useEffect(() => { - const container = containerRef.current; - if (!container) return; - - const handleWheel = (e: WheelEvent) => { - if (e.ctrlKey) { - e.preventDefault(); - const step = 0.02; - setScale((prev) => - Math.max(0.25, Math.min(3, prev + (e.deltaY < 0 ? step : -step))), - ); - } - }; - - container.addEventListener("wheel", handleWheel, { passive: false }); - return () => container.removeEventListener("wheel", handleWheel); - }, []); - - return ( -
- {!isFullscreen && ( -
-
- setInputValue(e.target.value)} - onKeyDown={(e) => handlePageChange(e)} - onFocus={() => setInputValue("")} - className="h-9 w-14 rounded border bg-[#e7e9ff] p-1 text-center text-sm [appearance:textfield] dark:bg-[#1f1f2a] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" - /> - - of {numPages ?? 1} -
- -
- - - {(scale * 100).toFixed(0)}% - - - - - - - - -
-
- )} - -
- Failed to load PDF file.
- } - loading={ -
- -
- } - noData={ -
No PDF file specified.
- } - > - {numPages && - Array.from({ length: numPages }, (_, index) => ( -
{ - pageRefs.current[index] = el; - }} - > -
- -
-
- ))} - - - {isFullscreen && ( -
-
- setInputValue(e.target.value)} - onKeyDown={(e) => handlePageChange(e)} - onFocus={() => setInputValue("")} - className="h-10 w-16 rounded border p-1 text-center [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" - /> - of {numPages ?? 1} -
- -
- - {(scale * 100).toFixed(0)}% - - - - -
-
- )} -
- - {!isFullscreen && ( -
-
- - - - - -
-
- - - {(scale * 100).toFixed(0)}% - - -
-
-
- setInputValue(e.target.value)} - onKeyDown={(e) => handlePageChange(e)} - onFocus={() => setInputValue("")} - className="h-10 w-16 rounded border p-1 text-center [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" - /> - of {numPages ?? 1} -
-
- -
- )} -
- ); -} From 269fa44c46a8701aca14555ad61533cfac246f62 Mon Sep 17 00:00:00 2001 From: Gslmao Date: Sun, 12 Apr 2026 22:43:05 +0530 Subject: [PATCH 6/9] fix: zoom level indicator UI --- src/components/newPdfViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/newPdfViewer.tsx b/src/components/newPdfViewer.tsx index 4d7b6a9e..a26f3915 100644 --- a/src/components/newPdfViewer.tsx +++ b/src/components/newPdfViewer.tsx @@ -123,7 +123,7 @@ const Controls = memo(function Controls({documentId, toggleFullscreen, isFullscr - + {typeof zoomLevel === "number" && `${Math.round(zoomLevel * 100)}%`} From a63e36d129743a8285a6ad28bf96023d063cfe9d Mon Sep 17 00:00:00 2001 From: Gslmao Date: Sun, 12 Apr 2026 22:44:09 +0530 Subject: [PATCH 7/9] feat: add mouse wheel scroll to the viewer --- src/components/newPdfViewer.tsx | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/components/newPdfViewer.tsx b/src/components/newPdfViewer.tsx index a26f3915..a3489d4b 100644 --- a/src/components/newPdfViewer.tsx +++ b/src/components/newPdfViewer.tsx @@ -31,6 +31,11 @@ interface PdfViewerProps { name: string; } +interface WheelZoomProps { + documentId: string; + viewerRef: React.RefObject; +} + function useBreakpoint() { const [width, setWidth] = useState(() => window.innerWidth); @@ -243,6 +248,30 @@ const Controls = memo(function Controls({documentId, toggleFullscreen, isFullscr ) }); +function WheelZoom({documentId, viewerRef}: WheelZoomProps){ + const { provides: zoomProv } = useZoom(documentId); + + const handleWheel = useCallback((e: WheelEvent) => { + if (e.ctrlKey && zoomProv) { + e.preventDefault(); + if (e.deltaY < 0) { + zoomProv.zoomIn(); + } else { + zoomProv.zoomOut(); + } + } + }, [zoomProv]); + + useEffect(() => { + const viewer = viewerRef.current; + if (!viewer) return; + viewer.addEventListener("wheel", handleWheel, { passive: false }); + return () => viewer.removeEventListener("wheel", handleWheel); + }, [handleWheel]); + + return null; +} + export default function PDFViewer({ url, name }: PdfViewerProps) { const { engine, isLoading } = usePdfiumEngine(); const {isMobile, isSmall} = useBreakpoint(); @@ -301,6 +330,7 @@ export default function PDFViewer({ url, name }: PdfViewerProps) { {({ activeDocumentId }) => activeDocumentId && ( <> + {(isMobile && !isFullscreen) && Date: Sun, 12 Apr 2026 22:53:43 +0530 Subject: [PATCH 8/9] fix: make useBreakpoint ssr-safe --- src/components/newPdfViewer.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/newPdfViewer.tsx b/src/components/newPdfViewer.tsx index a3489d4b..d2a52bcb 100644 --- a/src/components/newPdfViewer.tsx +++ b/src/components/newPdfViewer.tsx @@ -37,7 +37,7 @@ interface WheelZoomProps { } function useBreakpoint() { - const [width, setWidth] = useState(() => window.innerWidth); + const [width, setWidth] = useState(null); useEffect(() => { setWidth(window.innerWidth); @@ -45,8 +45,8 @@ function useBreakpoint() { window.addEventListener("resize", handler); return () => window.removeEventListener("resize", handler); }, []); - - return {isMobile: width < 768, isSmall: width < 640}; + + return {isMobile: width !== null && width < 768, isSmall: width !== null && width < 640}; } const Controls = memo(function Controls({documentId, toggleFullscreen, isFullscreen, onDownload, From fc18ae39b9601eb489ce08087969dba530f0849b Mon Sep 17 00:00:00 2001 From: Gslmao Date: Mon, 13 Apr 2026 13:47:51 +0530 Subject: [PATCH 9/9] fix: fixed trackpad zoom sensitivity and page jitter during wheel zoom --- src/components/newPdfViewer.tsx | 40 ++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/components/newPdfViewer.tsx b/src/components/newPdfViewer.tsx index d2a52bcb..c7c3f73e 100644 --- a/src/components/newPdfViewer.tsx +++ b/src/components/newPdfViewer.tsx @@ -248,25 +248,43 @@ const Controls = memo(function Controls({documentId, toggleFullscreen, isFullscr ) }); -function WheelZoom({documentId, viewerRef}: WheelZoomProps){ +function WheelZoom({ documentId, viewerRef }: WheelZoomProps) { const { provides: zoomProv } = useZoom(documentId); + const accumulatedDelta = useRef(0); + const rafId = useRef(null); - const handleWheel = useCallback((e: WheelEvent) => { - if (e.ctrlKey && zoomProv) { + const handleWheel = useCallback( + (e: WheelEvent) => { + if (!e.ctrlKey || !zoomProv) return; e.preventDefault(); - if (e.deltaY < 0) { - zoomProv.zoomIn(); - } else { - zoomProv.zoomOut(); - } - } - }, [zoomProv]); + + // Trackpad pinch sends fractional deltaY (often < 1), + // mouse wheel sends large discrete steps (typically 100–120). + const isTrackpad = Math.abs(e.deltaY) < 50; + const scaleFactor = isTrackpad ? 0.007 : 0.08; + + accumulatedDelta.current += -e.deltaY * scaleFactor; + + // Flush on the next animation frame to batch rapid events + if (rafId.current !== null) cancelAnimationFrame(rafId.current); + rafId.current = requestAnimationFrame(() => { + const clamped = Math.max(-0.15, Math.min(accumulatedDelta.current, 0.15)); + zoomProv.requestZoomBy(clamped); + accumulatedDelta.current = 0; + rafId.current = null; + }); + }, + [zoomProv] + ); useEffect(() => { const viewer = viewerRef.current; if (!viewer) return; viewer.addEventListener("wheel", handleWheel, { passive: false }); - return () => viewer.removeEventListener("wheel", handleWheel); + return () => { + viewer.removeEventListener("wheel", handleWheel); + if (rafId.current !== null) cancelAnimationFrame(rafId.current); + }; }, [handleWheel]); return null;