diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c884272c4..71e50a074 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,24 +10,25 @@ "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-redaction": "^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 +503,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 +569,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,49 +602,71 @@ } }, "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-redaction": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@embedpdf/plugin-redaction/-/plugin-redaction-1.4.1.tgz", + "integrity": "sha512-lyVIDxAwU8mUbhxh8J1dhWZ90086YSZ1+RB50hfCAyaxftRF3XpHPhmLZhr6lr3z1lDx+w9VIcoCU6IvhcweTw==", + "license": "MIT", + "dependencies": { + "@embedpdf/models": "1.4.1", + "@embedpdf/utils": "1.4.1" + }, + "peerDependencies": { + "@embedpdf/core": "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", @@ -648,182 +674,192 @@ } }, "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 +3081,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 +3848,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": { @@ -4605,13 +4650,13 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", - "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz", + "integrity": "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@vue/shared": "3.5.21", + "@babel/parser": "^7.28.4", + "@vue/shared": "3.5.22", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" @@ -4636,28 +4681,28 @@ "license": "MIT" }, "node_modules/@vue/compiler-dom": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", - "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.22.tgz", + "integrity": "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.21", - "@vue/shared": "3.5.21" + "@vue/compiler-core": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", - "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.22.tgz", + "integrity": "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@vue/compiler-core": "3.5.21", - "@vue/compiler-dom": "3.5.21", - "@vue/compiler-ssr": "3.5.21", - "@vue/shared": "3.5.21", + "@babel/parser": "^7.28.4", + "@vue/compiler-core": "3.5.22", + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22", "estree-walker": "^2.0.2", - "magic-string": "^0.30.18", + "magic-string": "^0.30.19", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } @@ -4669,67 +4714,67 @@ "license": "MIT" }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", - "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.22.tgz", + "integrity": "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.21", - "@vue/shared": "3.5.21" + "@vue/compiler-dom": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/reactivity": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz", - "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.22.tgz", + "integrity": "sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==", "license": "MIT", "peer": true, "dependencies": { - "@vue/shared": "3.5.21" + "@vue/shared": "3.5.22" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz", - "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.22.tgz", + "integrity": "sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==", "license": "MIT", "peer": true, "dependencies": { - "@vue/reactivity": "3.5.21", - "@vue/shared": "3.5.21" + "@vue/reactivity": "3.5.22", + "@vue/shared": "3.5.22" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz", - "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.22.tgz", + "integrity": "sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==", "license": "MIT", "peer": true, "dependencies": { - "@vue/reactivity": "3.5.21", - "@vue/runtime-core": "3.5.21", - "@vue/shared": "3.5.21", + "@vue/reactivity": "3.5.22", + "@vue/runtime-core": "3.5.22", + "@vue/shared": "3.5.22", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz", - "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.22.tgz", + "integrity": "sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==", "license": "MIT", "peer": true, "dependencies": { - "@vue/compiler-ssr": "3.5.21", - "@vue/shared": "3.5.21" + "@vue/compiler-ssr": "3.5.22", + "@vue/shared": "3.5.22" }, "peerDependencies": { - "vue": "3.5.21" + "vue": "3.5.22" } }, "node_modules/@vue/shared": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", - "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.22.tgz", + "integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==", "license": "MIT" }, "node_modules/abbrev": { @@ -4743,7 +4788,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 +5191,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 +7230,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 +7295,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 +8818,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 +9704,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 +13047,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", @@ -14026,17 +14150,17 @@ } }, "node_modules/vue": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz", - "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==", + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.22.tgz", + "integrity": "sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==", "license": "MIT", "peer": true, "dependencies": { - "@vue/compiler-dom": "3.5.21", - "@vue/compiler-sfc": "3.5.21", - "@vue/runtime-dom": "3.5.21", - "@vue/server-renderer": "3.5.21", - "@vue/shared": "3.5.21" + "@vue/compiler-dom": "3.5.22", + "@vue/compiler-sfc": "3.5.22", + "@vue/runtime-dom": "3.5.22", + "@vue/server-renderer": "3.5.22", + "@vue/shared": "3.5.22" }, "peerDependencies": { "typescript": "*" @@ -14447,6 +14571,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", diff --git a/frontend/package.json b/frontend/package.json index 892e48569..d33cb65a3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,24 +6,25 @@ "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-redaction": "^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", diff --git a/frontend/src/core/components/shared/rightRail/RightRail.css b/frontend/src/core/components/shared/rightRail/RightRail.css index 6ea40538d..b987dc34b 100644 --- a/frontend/src/core/components/shared/rightRail/RightRail.css +++ b/frontend/src/core/components/shared/rightRail/RightRail.css @@ -68,6 +68,11 @@ color: var(--right-rail-icon); } +.right-rail-icon--active { + background-color: var(--accent-interactive) !important; + color: white !important; +} + .right-rail-icon[aria-disabled="true"], .right-rail-icon[disabled] { color: var(--right-rail-icon-disabled) !important; diff --git a/frontend/src/core/components/tools/redact/ManualRedactionWorkbenchView.tsx b/frontend/src/core/components/tools/redact/ManualRedactionWorkbenchView.tsx new file mode 100644 index 000000000..18ad8694b --- /dev/null +++ b/frontend/src/core/components/tools/redact/ManualRedactionWorkbenchView.tsx @@ -0,0 +1,671 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { createPluginRegistration } from '@embedpdf/core'; +import { EmbedPDF } from '@embedpdf/core/react'; +import { usePdfiumEngine } from '@embedpdf/engines/react'; +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 { 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'; +import { SpreadPluginPackage, SpreadMode } from '@embedpdf/plugin-spread/react'; +import { SearchPluginPackage } from '@embedpdf/plugin-search/react'; +import { ThumbnailPluginPackage } from '@embedpdf/plugin-thumbnail/react'; +import { RotatePluginPackage, Rotate } from '@embedpdf/plugin-rotate/react'; +import { ExportPluginPackage } from '@embedpdf/plugin-export/react'; +import { HistoryPluginPackage } from '@embedpdf/plugin-history/react'; +import { RedactionPluginPackage, RedactionLayer, useRedaction } from '@embedpdf/plugin-redaction/react'; +import type { SelectionMenuProps } from '@embedpdf/plugin-redaction/react'; +import { Stack, Group, Text, Button, Alert, Loader } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; +import CropFreeRoundedIcon from '@mui/icons-material/CropFreeRounded'; +import TextFieldsRoundedIcon from '@mui/icons-material/TextFieldsRounded'; +import UndoRoundedIcon from '@mui/icons-material/UndoRounded'; +import RedoRoundedIcon from '@mui/icons-material/RedoRounded'; +import ToolLoadingFallback from '@app/components/tools/ToolLoadingFallback'; +import { alert } from '@app/components/toast'; +import { useRightRailButtons, type RightRailButtonWithAction } from '@app/hooks/useRightRailButtons'; +import { useNavigationActions } from '@app/contexts/NavigationContext'; +import type { ManualRedactionWorkbenchData } from '@app/types/redact'; + +interface ManualRedactionWorkbenchViewProps { + data: ManualRedactionWorkbenchData | null; +} + +const toPdfBlob = async (value: any): Promise => { + if (!value) return null; + if (value instanceof Blob) return value; + if (value instanceof ArrayBuffer) return new Blob([value], { type: 'application/pdf' }); + if (value instanceof Uint8Array) { + const copy = new Uint8Array(value.byteLength); + copy.set(value); + return new Blob([copy.buffer], { type: 'application/pdf' }); + } + if (value.data instanceof ArrayBuffer) return new Blob([value.data], { type: 'application/pdf' }); + if (value.blob instanceof Blob) return value.blob; + if (typeof value.toBlob === 'function') { + return value.toBlob(); + } + if (typeof value.toPromise === 'function') { + const result = await value.toPromise(); + if (result instanceof ArrayBuffer) return new Blob([result], { type: 'application/pdf' }); + } + if (typeof value.arrayBuffer === 'function') { + const buffer = await value.arrayBuffer(); + return new Blob([buffer], { type: 'application/pdf' }); + } + return null; +}; + +const buildRedactedFileName = (name: string | undefined | null) => { + if (!name || name.trim() === '') { + return 'redacted.pdf'; + } + + const lower = name.toLowerCase(); + if (lower.includes('redacted')) { + return name; + } + + const dotIndex = name.lastIndexOf('.'); + if (dotIndex === -1) { + return `${name}_redacted.pdf`; + } + + const base = name.slice(0, dotIndex); + const ext = name.slice(dotIndex); + return `${base}_redacted${ext}`; +}; + +const ManualRedactionWorkbenchView = ({ data }: ManualRedactionWorkbenchViewProps) => { + const { t } = useTranslation(); + const { actions: navigationActions } = useNavigationActions(); + const redactionApiRef = useRef | null>(null); + const exportApiRef = useRef | null>(null); + const selectionApiRef = useRef | null>(null); + const historyApiRef = useRef | null>(null); + const [isReady, setIsReady] = useState(false); + const [isApplying, setIsApplying] = useState(false); + const [canUndo, setCanUndo] = useState(false); + const [canRedo, setCanRedo] = useState(false); + const [activeType, setActiveType] = useState(null); + const [pdfUrl, setPdfUrl] = useState(null); + const [objectUrl, setObjectUrl] = useState(null); + const selectedFile = data?.file ?? null; + const redactionPluginPackage = RedactionPluginPackage; + const RedactionLayerComponent = RedactionLayer; + const hasRedactionSupport = Boolean(redactionPluginPackage && RedactionLayerComponent); + const exitWorkbench = useCallback(() => { + if (data?.onExit) { + data.onExit(); + } else { + navigationActions.setWorkbench('fileEditor'); + } + }, [data, navigationActions]); + + useEffect(() => { + if (selectedFile) { + const url = URL.createObjectURL(selectedFile); + setPdfUrl(url); + setObjectUrl(url); + return () => { + URL.revokeObjectURL(url); + setObjectUrl(null); + }; + } + + setPdfUrl(null); + return () => {}; + }, [selectedFile]); + + const { engine, isLoading, error } = usePdfiumEngine(); + + const plugins = useMemo(() => { + if (!pdfUrl) return []; + + const rootFontSize = typeof window !== 'undefined' + ? parseFloat(getComputedStyle(document.documentElement).fontSize) + : 16; + const viewportGap = rootFontSize * 3.5; + + const baseRegistrations: any[] = [ + createPluginRegistration(LoaderPluginPackage, { + loadingOptions: { + type: 'url', + pdfFile: { + id: 'stirling-pdf-manual-redaction', + url: pdfUrl, + }, + }, + }), + createPluginRegistration(ViewportPluginPackage, { viewportGap }), + createPluginRegistration(ScrollPluginPackage, { + strategy: ScrollStrategy.Vertical, + initialPage: 0, + }), + createPluginRegistration(RenderPluginPackage), + createPluginRegistration(InteractionManagerPluginPackage), + createPluginRegistration(SelectionPluginPackage), + createPluginRegistration(HistoryPluginPackage), + // Intentionally omit Pan plugin here so drag gestures are captured by redaction/selection layers + createPluginRegistration(ZoomPluginPackage, { + defaultZoomLevel: 1.2, + minZoom: 0.25, + maxZoom: 4, + }), + createPluginRegistration(TilingPluginPackage, { + tileSize: 768, + overlapPx: 5, + extraRings: 1, + }), + createPluginRegistration(SpreadPluginPackage, { + defaultSpreadMode: SpreadMode.None, + }), + createPluginRegistration(SearchPluginPackage), + createPluginRegistration(ThumbnailPluginPackage), + createPluginRegistration(RotatePluginPackage), + createPluginRegistration(ExportPluginPackage, { + defaultFileName: buildRedactedFileName(data?.fileName), + }), + ]; + if (hasRedactionSupport) { + baseRegistrations.splice(6, 0, createPluginRegistration(redactionPluginPackage, { autoPreview: true })); + } + return baseRegistrations; + }, [pdfUrl, data?.fileName, hasRedactionSupport, redactionPluginPackage]); + + const assignPluginApi = useCallback((plugin: any, ref: React.MutableRefObject | null>, onReady?: () => void) => { + if (!plugin || typeof plugin.provides !== 'function') return; + + try { + const provided = plugin.provides(); + if (provided && typeof provided.then === 'function') { + provided + .then((resolved: any) => { + ref.current = resolved ?? null; + onReady?.(); + }) + .catch((err: any) => { + console.warn('[manual-redaction] Failed to resolve plugin capability', err); + }); + } else { + ref.current = provided ?? null; + onReady?.(); + } + } catch (err) { + console.warn('[manual-redaction] Plugin capability unavailable', err); + } + }, [hasRedactionSupport]); + + const handleInitialized = useCallback(async (registry: any) => { + const redactionPlugin = hasRedactionSupport ? registry.getPlugin?.('redaction') : null; + const exportPlugin = registry.getPlugin?.('export'); + const historyPlugin = registry.getPlugin?.('history'); + + if (hasRedactionSupport) { + assignPluginApi(redactionPlugin, redactionApiRef, () => { + setIsReady(true); + // default to area redaction mode + enableAreaRedaction(); + // no pan plugin: drags go to redaction/selection layers + // subscribe to state changes to drive undo/redo availability + try { + const api = redactionApiRef.current; + api?.onStateChange?.((state: any) => { + // heuristics: if there are any pending or previous operations, enable undo + const pending = Number(state?.pendingCount ?? 0); + setCanUndo(pending > 0 || Boolean(state?.canUndo)); + setCanRedo(Boolean(state?.canRedo)); + setActiveType(state?.activeType ?? null); + }); + } catch {} + }); + } else { + setIsReady(false); + } + assignPluginApi(exportPlugin, exportApiRef); + assignPluginApi(historyPlugin, historyApiRef); + const selectionPlugin = registry.getPlugin?.('selection'); + assignPluginApi(selectionPlugin, selectionApiRef); + }, [assignPluginApi, hasRedactionSupport]); + + const invokeRedactionMethod = useCallback((names: string[], args: any[] = []) => { + if (!hasRedactionSupport) return false; + const api = redactionApiRef.current; + if (!api) return false; + for (const name of names) { + const candidate = (api as Record)[name]; + if (typeof candidate === 'function') { + try { + const result = candidate.apply(api, args); + if (result && typeof result.then === 'function') { + // Fire and forget for interactive methods + result.catch((err: any) => console.warn(`[manual-redaction] ${name} failed`, err)); + } + return true; + } catch (err) { + console.warn(`[manual-redaction] ${name} threw`, err); + } + } + } + return false; + }, []); + + const invokeRedactionMethodAsync = useCallback(async (names: string[], args: any[] = []) => { + if (!hasRedactionSupport) return false; + const api = redactionApiRef.current; + if (!api) return false; + for (const name of names) { + const candidate = (api as Record)[name]; + if (typeof candidate === 'function') { + try { + const result = candidate.apply(api, args); + if (result && typeof result.then === 'function') { + await result; + } + return true; + } catch (err) { + console.warn(`[manual-redaction] ${name} failed`, err); + } + } + } + return false; + }, []); + + const enableAreaRedaction = useCallback(() => { + if (!hasRedactionSupport) return; + const api = redactionApiRef.current; + // Ensure selection plugin is not intercepting as text selection + try { selectionApiRef.current?.setMode?.('none'); } catch {} + // Prefer official capability + if (api?.toggleMarqueeRedact) { + try { api.toggleMarqueeRedact(); setActiveType('marqueeRedact'); return; } catch {} + } + // Fall back to common method names + const areaNames = ['area', 'box', 'rectangle', 'shape']; + for (const mode of areaNames) { + if (invokeRedactionMethod(['activateAreaRedaction', 'startAreaRedaction', 'enableAreaRedaction', 'activateMode'], [mode])) return; + if (invokeRedactionMethod(['setRedactionMode', 'setMode'], [mode])) return; + if (invokeRedactionMethod(['setRedactionMode', 'setMode'], [{ mode }])) return; + if (invokeRedactionMethod(['setMode'], [{ type: mode }])) return; + if (invokeRedactionMethod(['setMode'], [mode.toUpperCase?.() ?? mode])) return; + } + console.warn('[manual-redaction] No compatible area redaction activation method found'); + }, [hasRedactionSupport, invokeRedactionMethod]); + + const enableTextRedaction = useCallback(() => { + if (!hasRedactionSupport) return; + const api = redactionApiRef.current; + // Ensure selection plugin is in text mode when redacting text + try { selectionApiRef.current?.setMode?.('text'); } catch {} + if (api?.toggleRedactSelection) { + try { api.toggleRedactSelection(); setActiveType('redactSelection'); return; } catch {} + } + const textModes = ['text', 'search', 'pattern']; + for (const mode of textModes) { + if (invokeRedactionMethod(['activateTextRedaction', 'startTextRedaction', 'enableTextRedaction', 'activateMode'], [mode])) return; + if (invokeRedactionMethod(['setRedactionMode', 'setMode'], [mode])) return; + if (invokeRedactionMethod(['setRedactionMode', 'setMode'], [{ mode }])) return; + if (invokeRedactionMethod(['setMode'], [{ type: mode }])) return; + if (invokeRedactionMethod(['setMode'], [mode.toUpperCase?.() ?? mode])) return; + } + console.warn('[manual-redaction] No compatible text redaction activation method found'); + }, [hasRedactionSupport, invokeRedactionMethod]); + + const handleUndo = useCallback(() => { + if (!hasRedactionSupport) return; + // Prefer redaction-aware undo + if (invokeRedactionMethod(['undo', 'stepBack', 'undoLast'])) { + return; + } + // Fallback: remove the most recent pending mark if available + try { + const state = (redactionApiRef.current?.getState?.() as any) || {}; + const pendingMap = state.pending || {}; + const pages = Object.keys(pendingMap).map(n => parseInt(n, 10)).sort((a,b) => b-a); + for (const page of pages) { + const items = pendingMap[page]; + const last = Array.isArray(items) ? items[items.length - 1] : null; + if (last) { + redactionApiRef.current?.removePending?.(page, last.id); + return; + } + } + } catch {} + const historyApi = historyApiRef.current; + if (historyApi && typeof historyApi.undo === 'function') { + historyApi.undo(); + return; + } + console.warn('[manual-redaction] Undo not available'); + }, [hasRedactionSupport, invokeRedactionMethod]); + + const handleRedo = useCallback(() => { + if (!hasRedactionSupport) return; + if (invokeRedactionMethod(['redo', 'stepForward', 'redoLast'])) { + return; + } + const historyApi = historyApiRef.current; + if (historyApi && typeof historyApi.redo === 'function') { + historyApi.redo(); + return; + } + console.warn('[manual-redaction] Redo not available'); + }, [hasRedactionSupport, invokeRedactionMethod]); + + const exportRedactedBlob = useCallback(async (): Promise => { + if (!hasRedactionSupport) { + throw new Error('Manual redaction plugin is not available.'); + } + const redactionApi = redactionApiRef.current; + const exportApi = exportApiRef.current; + + const tryCall = async (api: Record | null, method: string, args: any[] = []): Promise => { + if (!api) return null; + const candidate = api[method]; + if (typeof candidate !== 'function') return null; + try { + const result = candidate.apply(api, args); + if (result && typeof result.then === 'function') { + return await result; + } + return result; + } catch (err) { + console.warn(`[manual-redaction] ${method} failed`, err); + return null; + } + }; + + const attempts: Array<[Record | null, string, any[]]> = [ + [redactionApi, 'exportRedactedDocument', [{ type: 'blob' }]], + [redactionApi, 'exportRedactedDocument', []], + [redactionApi, 'getRedactedDocument', []], + [redactionApi, 'getBlob', []], + [redactionApi, 'download', [{ type: 'blob' }]], + [exportApi, 'exportDocument', [{ type: 'blob' }]], + [exportApi, 'exportDocument', []], + [exportApi, 'download', [{ type: 'blob' }]], + ]; + + for (const [api, method, args] of attempts) { + const result = await tryCall(api, method, args); + const blob = await toPdfBlob(result); + if (blob) return blob; + } + + // Fallback: some export APIs return handles with toPromise() + if (exportApi && typeof exportApi.saveAsCopy === 'function') { + const handle = exportApi.saveAsCopy(); + const blob = await toPdfBlob(handle); + if (blob) return blob; + } + + return null; + }, [hasRedactionSupport]); + + const handleApplyAndSave = useCallback(async () => { + if (!selectedFile) { + alert({ + alertType: 'error', + title: t('redact.manual.noFileSelected', 'No PDF selected'), + body: t('redact.manual.noFileSelectedBody', 'Select a PDF before opening the redaction editor.'), + }); + return; + } + + setIsApplying(true); + try { + const applied = await invokeRedactionMethodAsync([ + 'applyRedactions', + 'applyPendingRedactions', + 'apply', + 'commit', + 'finalizeRedactions', + 'performRedactions', + ]); + + if (!applied) { + console.warn('[manual-redaction] No compatible apply method found'); + } + + const blob = await exportRedactedBlob(); + if (!blob) { + throw new Error('Unable to export redacted PDF'); + } + + const outputName = buildRedactedFileName(selectedFile.name); + const exportedFile = new File([blob], outputName, { type: 'application/pdf' }); + + if (data?.onExport) { + await data.onExport(exportedFile); + } + + alert({ + alertType: 'success', + title: t('redact.manual.exportSuccess', 'Redacted copy saved'), + body: t('redact.manual.exportSuccessBody', 'A redacted PDF has been added to your files.'), + }); + + exitWorkbench(); + } catch (err) { + const message = err instanceof Error ? err.message : t('redact.manual.exportUnknownError', 'Failed to export redacted PDF.'); + alert({ + alertType: 'error', + title: t('redact.manual.exportFailed', 'Export failed'), + body: message, + }); + } finally { + setIsApplying(false); + } + }, [data, exitWorkbench, exportRedactedBlob, invokeRedactionMethodAsync, navigationActions, selectedFile, t]); + + useEffect(() => { + if (!isReady || !hasRedactionSupport) return; + return () => { + setIsReady(false); + redactionApiRef.current = null; + exportApiRef.current = null; + historyApiRef.current = null; + }; + }, [hasRedactionSupport, isReady, objectUrl]); + + const rightRailButtons = useMemo(() => ([ + { + id: 'manual-redaction-area', + icon: , + tooltip: t('redact.manual.buttons.area', 'Mark area for redaction'), + ariaLabel: t('redact.manual.buttons.area', 'Mark area for redaction'), + section: 'top', + order: 0, + disabled: !isReady || !hasRedactionSupport, + className: activeType === 'marqueeRedact' ? 'right-rail-icon--active' : undefined, + onClick: enableAreaRedaction, + }, + { + id: 'manual-redaction-text', + icon: , + tooltip: t('redact.manual.buttons.text', 'Mark text for redaction'), + ariaLabel: t('redact.manual.buttons.text', 'Mark text for redaction'), + section: 'top', + order: 1, + disabled: !isReady || !hasRedactionSupport, + className: activeType === 'redactSelection' ? 'right-rail-icon--active' : undefined, + onClick: enableTextRedaction, + }, + { + id: 'manual-redaction-undo', + icon: , + tooltip: t('redact.manual.buttons.undo', 'Undo last change'), + ariaLabel: t('redact.manual.buttons.undo', 'Undo last change'), + section: 'top', + order: 2, + disabled: !isReady || !hasRedactionSupport || !canUndo, + onClick: handleUndo, + }, + { + id: 'manual-redaction-redo', + icon: , + tooltip: t('redact.manual.buttons.redo', 'Redo change'), + ariaLabel: t('redact.manual.buttons.redo', 'Redo change'), + section: 'top', + order: 3, + disabled: !isReady || !hasRedactionSupport || !canRedo, + onClick: handleRedo, + }, + ]), [enableAreaRedaction, enableTextRedaction, handleUndo, handleRedo, hasRedactionSupport, isReady, t, canUndo, canRedo]); + + useRightRailButtons(rightRailButtons); + + if (!selectedFile) { + return ( + + + {t('redact.manual.selectFilePrompt', 'Select a single PDF from the sidebar to start manual redaction.')} + + + ); + } + + if (isLoading || !engine || !pdfUrl) { + return ; + } + + if (error) { + return ( + + + {error.message} + + + ); + } + + return ( + + + + {t('redact.manual.editorHeading', 'Manual redaction')} + + {t('redact.manual.editorSubheading', 'Draw rectangles or search for text to mark redactions, then apply the changes.')} + + + {t('redact.manual.currentFile', 'Current file: {{name}}', { name: selectedFile.name })} + + + + + + + + + +
+ + + + ( + + +
+ + + {hasRedactionSupport && RedactionLayerComponent && ( + } + /> + )} +
+
+
+ )} + /> +
+
+
+
+
+ ); +}; + +export default ManualRedactionWorkbenchView; + +// Inline redaction menu displayed beneath selection/rectangle +function InlineRedactionMenu({ item, selected, menuWrapperProps }: SelectionMenuProps) { + const { provides } = useRedaction(); + if (!selected) return null; + return ( +
+ + + + +
+ ); +} diff --git a/frontend/src/core/components/tools/redact/RedactModeSelector.tsx b/frontend/src/core/components/tools/redact/RedactModeSelector.tsx index 47f15da66..3d781558b 100644 --- a/frontend/src/core/components/tools/redact/RedactModeSelector.tsx +++ b/frontend/src/core/components/tools/redact/RedactModeSelector.tsx @@ -24,7 +24,6 @@ export default function RedactModeSelector({ mode, onModeChange, disabled }: Red { value: 'manual' as const, label: t('redact.modeSelector.manual', 'Manual'), - disabled: true, // Keep manual mode disabled until implemented }, ]} disabled={disabled} diff --git a/frontend/src/core/components/tooltips/useRedactTips.ts b/frontend/src/core/components/tooltips/useRedactTips.ts index 7689a6a2a..e08da770d 100644 --- a/frontend/src/core/components/tooltips/useRedactTips.ts +++ b/frontend/src/core/components/tooltips/useRedactTips.ts @@ -15,7 +15,7 @@ export const useRedactModeTips = (): TooltipContent => { }, { title: t("redact.tooltip.mode.manual.title", "Manual Redaction"), - description: t("redact.tooltip.mode.manual.text", "Click and drag to manually select specific areas to redact. Gives you precise control over what gets redacted. (Coming soon)") + description: t("redact.tooltip.mode.manual.text", "Click and drag to manually select specific areas or search for text to redact. Gives you precise control over what gets removed.") } ] }; diff --git a/frontend/src/core/hooks/tools/redact/useRedactOperation.ts b/frontend/src/core/hooks/tools/redact/useRedactOperation.ts index bf2a05121..cbc689986 100644 --- a/frontend/src/core/hooks/tools/redact/useRedactOperation.ts +++ b/frontend/src/core/hooks/tools/redact/useRedactOperation.ts @@ -17,8 +17,7 @@ export const buildRedactFormData = (parameters: RedactParameters, file: File): F formData.append("customPadding", parameters.customPadding.toString()); formData.append("convertPDFToImage", parameters.convertPDFToImage.toString()); } else { - // Manual mode parameters would go here when implemented - throw new Error('Manual redaction not yet implemented'); + throw new Error('Manual redaction is handled directly in the editor'); } return formData; @@ -30,12 +29,11 @@ export const redactOperationConfig = { buildFormData: buildRedactFormData, operationType: 'redact', endpoint: (parameters: RedactParameters) => { - if (parameters.mode === 'automatic') { - return '/api/v1/security/auto-redact'; - } else { - // Manual redaction endpoint would go here when implemented - throw new Error('Manual redaction not yet implemented'); - } + if (parameters.mode === 'automatic') { + return '/api/v1/security/auto-redact'; + } else { + throw new Error('Manual redaction is handled directly in the editor'); + } }, defaultParameters, } as const; diff --git a/frontend/src/core/hooks/tools/redact/useRedactParameters.ts b/frontend/src/core/hooks/tools/redact/useRedactParameters.ts index f29f56f96..5abcc4afb 100644 --- a/frontend/src/core/hooks/tools/redact/useRedactParameters.ts +++ b/frontend/src/core/hooks/tools/redact/useRedactParameters.ts @@ -34,15 +34,13 @@ export const useRedactParameters = (): RedactParametersHook => { if (params.mode === 'automatic') { return '/api/v1/security/auto-redact'; } - // Manual redaction endpoint would go here when implemented - throw new Error('Manual redaction not yet implemented'); + return ''; }, validateFn: (params) => { if (params.mode === 'automatic') { return params.wordsToRedact.length > 0 && params.wordsToRedact.some(word => word.trim().length > 0); } - // Manual mode validation would go here when implemented - return false; + return true; } }); }; diff --git a/frontend/src/core/tools/Redact.tsx b/frontend/src/core/tools/Redact.tsx index 27604da0b..a93a093cf 100644 --- a/frontend/src/core/tools/Redact.tsx +++ b/frontend/src/core/tools/Redact.tsx @@ -1,6 +1,9 @@ import { useTranslation } from "react-i18next"; -import { useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { Alert, Button, Stack, Text } from "@mantine/core"; +import VisibilityOffRoundedIcon from "@mui/icons-material/VisibilityOffRounded"; import { createToolFlow } from "@app/components/tools/shared/createToolFlow"; +import type { MiddleStepConfig } from "@app/components/tools/shared/createToolFlow"; import RedactModeSelector from "@app/components/tools/redact/RedactModeSelector"; import { useRedactParameters } from "@app/hooks/tools/redact/useRedactParameters"; import { useRedactOperation } from "@app/hooks/tools/redact/useRedactOperation"; @@ -9,9 +12,28 @@ import { BaseToolProps, ToolComponent } from "@app/types/tool"; import { useRedactModeTips, useRedactWordsTips, useRedactAdvancedTips } from "@app/components/tooltips/useRedactTips"; import RedactAdvancedSettings from "@app/components/tools/redact/RedactAdvancedSettings"; import WordsToRedactInput from "@app/components/tools/redact/WordsToRedactInput"; +import { useToolWorkflow } from "@app/contexts/ToolWorkflowContext"; +import { useNavigationActions, useNavigationState } from "@app/contexts/NavigationContext"; +import ManualRedactionWorkbenchView from "@app/components/tools/redact/ManualRedactionWorkbenchView"; +import type { ManualRedactionWorkbenchData } from "@app/types/redact"; +import { useFileContext } from "@app/contexts/file/fileHooks"; +import type { StirlingFile } from "@app/types/fileContext"; + +const MANUAL_VIEW_ID = "manualRedactionWorkbench"; +const MANUAL_WORKBENCH_ID = "custom:manualRedactionWorkbench" as const; const Redact = (props: BaseToolProps) => { const { t } = useTranslation(); + const { + registerCustomWorkbenchView, + unregisterCustomWorkbenchView, + setCustomWorkbenchViewData, + clearCustomWorkbenchViewData, + } = useToolWorkflow(); + const { actions: navigationActions } = useNavigationActions(); + const navigationState = useNavigationState(); + const { actions: fileActions } = useFileContext(); + const manualWorkbenchIcon = useMemo(() => , []); // State for managing step collapse status const [methodCollapsed, setMethodCollapsed] = useState(false); @@ -25,14 +47,92 @@ const Redact = (props: BaseToolProps) => { props ); + const createManualWorkbenchData = useCallback((file: StirlingFile): ManualRedactionWorkbenchData => ({ + fileId: file.fileId, + file, + fileName: file.name, + onExport: async (exportedFile: File) => { + await fileActions.addFiles([exportedFile], { selectFiles: true }); + }, + onExit: () => { + clearCustomWorkbenchViewData(MANUAL_VIEW_ID); + navigationActions.setWorkbench('fileEditor'); + }, + }), [clearCustomWorkbenchViewData, fileActions, navigationActions]); + + const handleOpenManualEditor = useCallback(() => { + if (base.selectedFiles.length !== 1) { + return; + } + const [selected] = base.selectedFiles as [StirlingFile]; + const workbenchData = createManualWorkbenchData(selected); + setCustomWorkbenchViewData(MANUAL_VIEW_ID, workbenchData); + navigationActions.setWorkbench(MANUAL_WORKBENCH_ID); + }, [base.selectedFiles, createManualWorkbenchData, navigationActions, setCustomWorkbenchViewData]); + + useEffect(() => { + registerCustomWorkbenchView({ + id: MANUAL_VIEW_ID, + workbenchId: MANUAL_WORKBENCH_ID, + label: t('redact.manual.workbenchLabel', 'Manual redaction'), + icon: manualWorkbenchIcon, + component: ManualRedactionWorkbenchView, + }); + + return () => { + clearCustomWorkbenchViewData(MANUAL_VIEW_ID); + unregisterCustomWorkbenchView(MANUAL_VIEW_ID); + }; + }, [ + clearCustomWorkbenchViewData, + manualWorkbenchIcon, + registerCustomWorkbenchView, + t, + unregisterCustomWorkbenchView, + ]); + + useEffect(() => { + if (base.params.parameters.mode !== 'manual') { + clearCustomWorkbenchViewData(MANUAL_VIEW_ID); + if (navigationState.workbench === MANUAL_WORKBENCH_ID) { + navigationActions.setWorkbench('fileEditor'); + } + } + }, [ + base.params.parameters.mode, + clearCustomWorkbenchViewData, + navigationActions, + navigationState.workbench, + ]); + + useEffect(() => { + if ( + navigationState.workbench !== MANUAL_WORKBENCH_ID || + base.params.parameters.mode !== 'manual' || + base.selectedFiles.length !== 1 + ) { + return; + } + const [selected] = base.selectedFiles as [StirlingFile]; + setCustomWorkbenchViewData(MANUAL_VIEW_ID, createManualWorkbenchData(selected)); + }, [ + base.params.parameters.mode, + base.selectedFiles, + createManualWorkbenchData, + navigationState.workbench, + setCustomWorkbenchViewData, + ]); + // Tooltips for each step const modeTips = useRedactModeTips(); const wordsTips = useRedactWordsTips(); const advancedTips = useRedactAdvancedTips(); + const isManualMode = base.params.parameters.mode === 'manual'; + const isExecuteDisabled = () => { - if (base.params.parameters.mode === 'manual') { - return true; // Manual mode not implemented yet + if (isManualMode) { + return true; } return !base.params.validateParameters() || !base.hasFiles || !base.endpointEnabled; }; @@ -44,7 +144,7 @@ const Redact = (props: BaseToolProps) => { // Build conditional steps based on redaction mode const buildSteps = () => { - const steps = [ + const steps: MiddleStepConfig[] = [ // Method selection step (always present) { title: t("redact.modeSelector.title", "Redaction Method"), @@ -62,7 +162,7 @@ const Redact = (props: BaseToolProps) => { ]; // Add mode-specific steps - if (base.params.parameters.mode === 'automatic') { + if (!isManualMode && base.params.parameters.mode === 'automatic') { steps.push( { title: t("redact.auto.settings.title", "Redaction Settings"), @@ -87,8 +187,43 @@ const Redact = (props: BaseToolProps) => { />, }, ); - } else if (base.params.parameters.mode === 'manual') { - // Manual mode steps would go here when implemented + } else if (isManualMode) { + const manualHasFile = base.selectedFiles.length > 0; + const manualHasSingleFile = base.selectedFiles.length === 1; + const manualTooManyFiles = base.selectedFiles.length > 1; + const selectedName = manualHasSingleFile ? base.selectedFiles[0].name : null; + + steps.push({ + title: t("redact.manual.stepTitle", "Manual redaction editor"), + isCollapsed: false, + content: ( + + + {manualHasSingleFile + ? t("redact.manual.stepDescriptionSelected", "Launch the editor to mark redactions on {{file}}.", { file: selectedName }) + : t("redact.manual.stepDescription", "Open the embedded redaction editor to draw boxes or search for sensitive text.")} + + {manualTooManyFiles && ( + + {t("redact.manual.multipleNotSupported", "Manual redaction works on one PDF at a time. Deselect extra files to continue.")} + + )} + {!manualHasFile && ( + + {t("redact.manual.noFileSelectedInfo", "Select a PDF from the file sidebar to begin manual redaction.")} + + )} + + + ), + }); } return steps; @@ -98,9 +233,10 @@ const Redact = (props: BaseToolProps) => { files: { selectedFiles: base.selectedFiles, isCollapsed: base.hasResults, + minFiles: isManualMode ? 1 : undefined, }, steps: buildSteps(), - executeButton: { + executeButton: isManualMode ? undefined : { text: t("redact.submit", "Redact"), isVisible: !base.hasResults, loadingText: t("loading"), @@ -108,7 +244,7 @@ const Redact = (props: BaseToolProps) => { disabled: isExecuteDisabled(), }, review: { - isVisible: base.hasResults, + isVisible: !isManualMode && base.hasResults, operation: base.operation, title: t("redact.title", "Redaction Results"), onFileClick: base.handleThumbnailClick, diff --git a/frontend/src/core/types/redact.ts b/frontend/src/core/types/redact.ts new file mode 100644 index 000000000..e34f77737 --- /dev/null +++ b/frontend/src/core/types/redact.ts @@ -0,0 +1,11 @@ +import type { FileId } from '@app/types/file'; +import type { StirlingFile } from '@app/types/fileContext'; + +export interface ManualRedactionWorkbenchData { + fileId: FileId; + file: StirlingFile | null; + fileName: string; + onExport?: (file: File) => Promise; + onExit?: () => void; + contextId?: string; +} diff --git a/frontend/src/global.d.ts b/frontend/src/global.d.ts index 96d969c97..7f7e0a7d2 100644 --- a/frontend/src/global.d.ts +++ b/frontend/src/global.d.ts @@ -12,4 +12,10 @@ declare module 'assets/material-symbols-icons.json' { export default value; } +declare module '@embedpdf/plugin-redaction/react' { + export const RedactionPluginPackage: any; + export const RedactionLayer: any; + export function useRedactionCapability(): { provides?: () => any } | undefined; +} + export {};