diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 51e500cb7..e74347837 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -10,24 +10,24 @@
"license": "SEE LICENSE IN https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/refs/heads/main/proprietary/LICENSE",
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.7.7",
- "@embedpdf/core": "^1.3.1",
- "@embedpdf/engines": "^1.3.1",
- "@embedpdf/plugin-annotation": "^1.3.1",
- "@embedpdf/plugin-export": "^1.3.1",
- "@embedpdf/plugin-history": "^1.3.1",
- "@embedpdf/plugin-interaction-manager": "^1.3.1",
- "@embedpdf/plugin-loader": "^1.3.1",
- "@embedpdf/plugin-pan": "^1.3.1",
- "@embedpdf/plugin-render": "^1.3.1",
- "@embedpdf/plugin-rotate": "^1.3.1",
- "@embedpdf/plugin-scroll": "^1.3.1",
- "@embedpdf/plugin-search": "^1.3.1",
- "@embedpdf/plugin-selection": "^1.3.1",
- "@embedpdf/plugin-spread": "^1.3.1",
- "@embedpdf/plugin-thumbnail": "^1.3.1",
- "@embedpdf/plugin-tiling": "^1.3.1",
- "@embedpdf/plugin-viewport": "^1.3.1",
- "@embedpdf/plugin-zoom": "^1.3.1",
+ "@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",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@iconify/react": "^6.0.2",
@@ -497,13 +497,13 @@
}
},
"node_modules/@embedpdf/core": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/core/-/core-1.3.1.tgz",
- "integrity": "sha512-2Az6trhiMMBIv+GFvV8H8UOS1gwQn7NK0KaJMcdsZbUHYLO0P95aVd6Pi/GRzEH4XyF51TDIoTOAUtf07TQ5dQ==",
+ "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==",
"license": "MIT",
"dependencies": {
- "@embedpdf/engines": "1.3.1",
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/engines": "1.3.14",
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
"preact": "^10.26.4",
@@ -513,13 +513,13 @@
}
},
"node_modules/@embedpdf/engines": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/engines/-/engines-1.3.1.tgz",
- "integrity": "sha512-G3pI+18la7spviUMuA5s9/hV95jlfkA2+CNxqlHBO5ocw3641E3d36Lv+mx+6yU7k0B5vEOQPZDGRMg7KFziBQ==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/engines/-/engines-1.3.14.tgz",
+ "integrity": "sha512-+/FPW2gAzj2lQYvsMH/Oj9+MEXgkyEuyYDC+HFkltTuXvmiP2S/3BD0YslZDX9K4BzcmMxnWB+BiQpNJokbDVg==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1",
- "@embedpdf/pdfium": "1.3.1"
+ "@embedpdf/models": "1.3.14",
+ "@embedpdf/pdfium": "1.3.14"
},
"peerDependencies": {
"preact": "^10.26.4",
@@ -529,31 +529,31 @@
}
},
"node_modules/@embedpdf/models": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/models/-/models-1.3.1.tgz",
- "integrity": "sha512-OzmO1rQAuOP/Y3aYXmW21dPNAx49olhr9ZO2hDdI0fbNBHTVGxnaKqOISxVmUz7TmhTwVBljERACnaA8Ib4b4Q==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/models/-/models-1.3.14.tgz",
+ "integrity": "sha512-BujY4bmr8b2DQdoZkOge03SzoRVoWxzfIQATLSPPtp4WiFh1U4BPp6cADlGuCwGkp6zBcH/aM4h8PwwA75d/eg==",
"license": "MIT"
},
"node_modules/@embedpdf/pdfium": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/pdfium/-/pdfium-1.3.1.tgz",
- "integrity": "sha512-qYGSS5ntz6DSY9Cxw/aigvHqGB+AKJLEcymNTZOL0GdlBzZpL++dOIYNEYHO2Tm/lOQVpE7I0e+Xh2TvD8O1zQ==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/pdfium/-/pdfium-1.3.14.tgz",
+ "integrity": "sha512-TQMZabXzHmzvvfPwopubFcYgQuYV7POvMgjICYu3Pgfn3sgr+UdIUh3aNXR/COcl3q8sXPMFQ2GDuyOHR9QQnA==",
"license": "MIT"
},
"node_modules/@embedpdf/plugin-annotation": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-annotation/-/plugin-annotation-1.3.1.tgz",
- "integrity": "sha512-mmePRYYBB8v8NIZ95XVfFkpyQ2QiKIGdWyvrPeJXSbL3/K6d6ix+o/jHBVvBWyTsQzdIlzs+FW8+iT0M1zkEow==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-annotation/-/plugin-annotation-1.3.14.tgz",
+ "integrity": "sha512-JJYqEWwUKCdBZsXCDq/CW96p3pVLn8N+XZ4W3OyL7djI2fvYC9x6ys9m82vwlSathAVOxk1D7xXiY8AzJQVF0Q==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1",
- "@embedpdf/utils": "1.3.1"
+ "@embedpdf/models": "1.3.14",
+ "@embedpdf/utils": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
- "@embedpdf/plugin-history": "1.3.1",
- "@embedpdf/plugin-interaction-manager": "1.3.1",
- "@embedpdf/plugin-selection": "1.3.1",
+ "@embedpdf/core": "1.3.14",
+ "@embedpdf/plugin-history": "1.3.14",
+ "@embedpdf/plugin-interaction-manager": "1.3.14",
+ "@embedpdf/plugin-selection": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -561,15 +561,15 @@
}
},
"node_modules/@embedpdf/plugin-export": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-export/-/plugin-export-1.3.1.tgz",
- "integrity": "sha512-reb03vNPFP5GuIAFExMcuYBVYu/deVO2v8EoCwRZ/lzzYMORIkJjpNWDQPo9VfyGBh1x4/o3CHvxisU1Y1tDLg==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-export/-/plugin-export-1.3.14.tgz",
+ "integrity": "sha512-fMGp2YxvI4uTRIViUKxfnJts2Jw/vktEM45XUNGNSjT/kAW6znVNgdceYjpK++xU8CGs2grAQ1i5UvMd3aRNDA==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
+ "@embedpdf/core": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -577,15 +577,15 @@
}
},
"node_modules/@embedpdf/plugin-history": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-history/-/plugin-history-1.3.1.tgz",
- "integrity": "sha512-HrPkWQmAk08mbHiOcIN4htVq5KJMqI9zSjAqaYQEhV/TugeHfWVpK+xMst/PzuFb14HWgk5gWXjtV5E4SDlw9w==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-history/-/plugin-history-1.3.14.tgz",
+ "integrity": "sha512-77hnNLp0W0FHw8lT7SeqzCgp8bOClfeOAPZdcInu/jPDhVASUGYbtE/0fkLhiaqPH7kyMirNCLif4sF6n4b5vg==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
+ "@embedpdf/core": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -593,15 +593,15 @@
}
},
"node_modules/@embedpdf/plugin-interaction-manager": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-interaction-manager/-/plugin-interaction-manager-1.3.1.tgz",
- "integrity": "sha512-8h3y5a9tQ1fZlc4mP1/+XKyuHWwcQEm9AujKxy+6f6omtCBzpnKrH95bURgYOzQEBGY7d5C3HvG6JOlh0o1x3A==",
+ "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==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
+ "@embedpdf/core": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -609,15 +609,15 @@
}
},
"node_modules/@embedpdf/plugin-loader": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-loader/-/plugin-loader-1.3.1.tgz",
- "integrity": "sha512-NjNmA7TOs3E/zwb9I+YohzyGkxq8y5NUGu0MKgh2g41lZoFvyqTAjFPar+RjEiLX8iiJiwNZswyJsNrytmS3Xg==",
+ "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==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
+ "@embedpdf/core": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -625,17 +625,17 @@
}
},
"node_modules/@embedpdf/plugin-pan": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-pan/-/plugin-pan-1.3.1.tgz",
- "integrity": "sha512-lF1gkz/a77G3+Rr8MOefkGnPJ1i5xWnClXm2ZzYAl7PbOScp59/PaP7qeU7eMPC4FHQM81ZhCgVYGXogbaB8ww==",
+ "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==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
- "@embedpdf/plugin-interaction-manager": "1.3.1",
- "@embedpdf/plugin-viewport": "1.3.1",
+ "@embedpdf/core": "1.3.14",
+ "@embedpdf/plugin-interaction-manager": "1.3.14",
+ "@embedpdf/plugin-viewport": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -643,15 +643,15 @@
}
},
"node_modules/@embedpdf/plugin-render": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-render/-/plugin-render-1.3.1.tgz",
- "integrity": "sha512-c9oH097e1CVUpYF9RgZRfV/7XCJ0pf+svdT1wyM2MbWby06ti20oCwT9wf7BLY0hPQ7+eO3wunr1I1/y3MnVrw==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-render/-/plugin-render-1.3.14.tgz",
+ "integrity": "sha512-IPj7GCQXJBsY++JaU+z7y+FwX5NaDBj4YYV6hsHNtSGf42Y1AdlwJzDYetivG2bA84xmk7KgD1X2Y3eIFBhjwA==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
+ "@embedpdf/core": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -659,15 +659,15 @@
}
},
"node_modules/@embedpdf/plugin-rotate": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-rotate/-/plugin-rotate-1.3.1.tgz",
- "integrity": "sha512-mRAlIW7IZAnCyDuYqN13yDc6yoNIYLUB4uYTUAR7vTIt021C8H5jDHk9TmLwcH0tQ8/R3yHuDm/XPAe0zfs81g==",
+ "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==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
+ "@embedpdf/core": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -675,16 +675,16 @@
}
},
"node_modules/@embedpdf/plugin-scroll": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-scroll/-/plugin-scroll-1.3.1.tgz",
- "integrity": "sha512-mDvK3DyBZC8/8pOEdJsWtSjCmV2ZuZJJ6xfspJpsaDVywo1Vq6M55BtKThkhqED6mqbFWTN9rP9cbWG8KDBWVA==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-scroll/-/plugin-scroll-1.3.14.tgz",
+ "integrity": "sha512-fQbt7OlRMLQJMuZj/Bzh0qpRxMw1ld5Qe/OTw8N54b/plljnFA52joE7cITl3H03huWWyHS3NKOScbw7f34dog==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
- "@embedpdf/plugin-viewport": "1.3.1",
+ "@embedpdf/core": "1.3.14",
+ "@embedpdf/plugin-viewport": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -692,16 +692,16 @@
}
},
"node_modules/@embedpdf/plugin-search": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-search/-/plugin-search-1.3.1.tgz",
- "integrity": "sha512-SLwYPQg1NJWytq2sd4MnWFmRVGgzwbohBedB2kH0ALsvdnoRYqgjR5HqAsKgoRJO/pphQhHlk3L1gLW62r6hqQ==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-search/-/plugin-search-1.3.14.tgz",
+ "integrity": "sha512-tlZEgR2tG+GSNnh2u1SjCxhUHfTDgcr38sE/xRK1bRLDGPZWlr6Ln7qP7JSWqeYBGni75sGrj0iZqcZbPWyJag==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
- "@embedpdf/plugin-loader": "1.3.1",
+ "@embedpdf/core": "1.3.14",
+ "@embedpdf/plugin-loader": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -709,17 +709,17 @@
}
},
"node_modules/@embedpdf/plugin-selection": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-selection/-/plugin-selection-1.3.1.tgz",
- "integrity": "sha512-yef2XB/zR7zjyeUB3Ul0SbTcXqu5isR0GtINkFwL7bJMok6HpYNDnMXSuo55BaxI0dOCnnCSZfoRkAgosnZ1uQ==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-selection/-/plugin-selection-1.3.14.tgz",
+ "integrity": "sha512-EXENuaAsse3rT6cjA1nYzyrNvoy62ojJl28wblCng6zcs3HSlGPemIQZAvaYKPUxoY608M+6nKlcMQ5neRnk/A==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
- "@embedpdf/plugin-interaction-manager": "1.3.1",
- "@embedpdf/plugin-viewport": "1.3.1",
+ "@embedpdf/core": "1.3.14",
+ "@embedpdf/plugin-interaction-manager": "1.3.14",
+ "@embedpdf/plugin-viewport": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -727,16 +727,16 @@
}
},
"node_modules/@embedpdf/plugin-spread": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-spread/-/plugin-spread-1.3.1.tgz",
- "integrity": "sha512-RJ/kgJsFRdtWlPMXTW1feUSb6WHIvxtNRLgqzX8dlFIoyc4oZex2Vw+URo/VZuWSe/NvCIihQ20rkNAQJMnNMQ==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-spread/-/plugin-spread-1.3.14.tgz",
+ "integrity": "sha512-DVlk6tDgUoDRkp2S4Jc3LrRTuf4DPMlph9vywJw5z6Qpbh0vgcMnObg896/S0Eu5FgACNAj0WGcXpLrcrn5b9Q==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
- "@embedpdf/plugin-loader": "1.3.1",
+ "@embedpdf/core": "1.3.14",
+ "@embedpdf/plugin-loader": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -744,34 +744,35 @@
}
},
"node_modules/@embedpdf/plugin-thumbnail": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-thumbnail/-/plugin-thumbnail-1.3.1.tgz",
- "integrity": "sha512-xv96ESa7JgD5z+TzcOK18/u0gq3d9v7QPv2wpr0ZhcnwLwf4sH0eUJZIsv7z7DMOpBNz7o7jJbrtxDUdCEHGhg==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-thumbnail/-/plugin-thumbnail-1.3.14.tgz",
+ "integrity": "sha512-cnwb5dG8Jph8XSArys1WFCQ6kK2R5FKoO0B5mDrHFv9Fcm2pKszlmZC/NDoskX4pgNUgSnwhI1X3cP37ebF9Ng==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
- "@embedpdf/plugin-render": "1.3.1",
+ "@embedpdf/core": "1.3.14",
+ "@embedpdf/plugin-render": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
- "react-dom": ">=16.8.0"
+ "react-dom": ">=16.8.0",
+ "vue": ">=3.2.0"
}
},
"node_modules/@embedpdf/plugin-tiling": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-tiling/-/plugin-tiling-1.3.1.tgz",
- "integrity": "sha512-Q8RF80fb6y9GDAKwvgsu0BsWJlQuhNCtSKWwp3YcZJtIBFm94DVcg0zTgvDmE9/WNOmn4Z1Edt86usmYauHolw==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-tiling/-/plugin-tiling-1.3.14.tgz",
+ "integrity": "sha512-SaCTo2LdZwGeE6jCqkwJxvwt8YKbsI3QGxa9S7Ez+5OcBchlhHeTfLQswcErDQ3WH2p8WHtGuucAcOLrVVOm0A==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
- "@embedpdf/plugin-render": "1.3.1",
- "@embedpdf/plugin-scroll": "1.3.1",
- "@embedpdf/plugin-viewport": "1.3.1",
+ "@embedpdf/core": "1.3.14",
+ "@embedpdf/plugin-render": "1.3.14",
+ "@embedpdf/plugin-scroll": "1.3.14",
+ "@embedpdf/plugin-viewport": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -779,15 +780,15 @@
}
},
"node_modules/@embedpdf/plugin-viewport": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-viewport/-/plugin-viewport-1.3.1.tgz",
- "integrity": "sha512-gzosrWL18ZhN175Kxocf/p7uqYBhNHvEuV1CpJQmN7ys48aew6Qq8z7MjAsCnJBANXk/8syNdo3qWwBriyjQNg==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-viewport/-/plugin-viewport-1.3.14.tgz",
+ "integrity": "sha512-mfJ7EbbU68eKk6oFvQ4ozGJNpxUxWbjQ5Gm3uuB+Gj5/tWgBocBOX36k/9LgivEEeX7g2S0tOgyErljApmH8Vg==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1"
+ "@embedpdf/models": "1.3.14"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
+ "@embedpdf/core": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -795,19 +796,19 @@
}
},
"node_modules/@embedpdf/plugin-zoom": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/plugin-zoom/-/plugin-zoom-1.3.1.tgz",
- "integrity": "sha512-3GXpgv6XmZiQnjaPbsxblTqUn84ALFiyONh2gwrEU9apB6STT3TQiY0QRindwrUXdQLpCSjRSB9PpDBCtTww7w==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/plugin-zoom/-/plugin-zoom-1.3.14.tgz",
+ "integrity": "sha512-/N5tyMk+8OzhObrS3O9yPkcmX8EPiuTo+WaT2QCVSmIUqKnOO4AnKpHJ6Vl0uVhcuXHCMwLucZKyhJ7tRqavwg==",
"license": "MIT",
"dependencies": {
- "@embedpdf/models": "1.3.1",
+ "@embedpdf/models": "1.3.14",
"hammerjs": "^2.0.8"
},
"peerDependencies": {
- "@embedpdf/core": "1.3.1",
- "@embedpdf/plugin-interaction-manager": "1.3.1",
- "@embedpdf/plugin-scroll": "1.3.1",
- "@embedpdf/plugin-viewport": "1.3.1",
+ "@embedpdf/core": "1.3.14",
+ "@embedpdf/plugin-interaction-manager": "1.3.14",
+ "@embedpdf/plugin-scroll": "1.3.14",
+ "@embedpdf/plugin-viewport": "1.3.14",
"preact": "^10.26.4",
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
@@ -815,9 +816,9 @@
}
},
"node_modules/@embedpdf/utils": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/@embedpdf/utils/-/utils-1.3.1.tgz",
- "integrity": "sha512-6trYysnggwCCTB2q7cX6tkOTbZJNtt2YYZohPCmh0yaDpkfNSgwDwD0jCLtEU2UZLQoH4+2GvNo+4xe+KAGlIQ==",
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@embedpdf/utils/-/utils-1.3.14.tgz",
+ "integrity": "sha512-gxEJD12nageCMqAjdbicNfDQolXU3nvnV0EX96OdZITRNj0Q1tisutVYoaxcCiJu3vvIEOzipjsAnQOubbFCEA==",
"license": "MIT",
"peerDependencies": {
"preact": "^10.26.4",
diff --git a/frontend/package.json b/frontend/package.json
index 4eb01f202..f11184471 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -6,24 +6,24 @@
"proxy": "http://localhost:8080",
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.7.7",
- "@embedpdf/core": "^1.3.1",
- "@embedpdf/engines": "^1.3.1",
- "@embedpdf/plugin-annotation": "^1.3.1",
- "@embedpdf/plugin-export": "^1.3.1",
- "@embedpdf/plugin-history": "^1.3.1",
- "@embedpdf/plugin-interaction-manager": "^1.3.1",
- "@embedpdf/plugin-loader": "^1.3.1",
- "@embedpdf/plugin-pan": "^1.3.1",
- "@embedpdf/plugin-render": "^1.3.1",
- "@embedpdf/plugin-rotate": "^1.3.1",
- "@embedpdf/plugin-scroll": "^1.3.1",
- "@embedpdf/plugin-search": "^1.3.1",
- "@embedpdf/plugin-selection": "^1.3.1",
- "@embedpdf/plugin-spread": "^1.3.1",
- "@embedpdf/plugin-thumbnail": "^1.3.1",
- "@embedpdf/plugin-tiling": "^1.3.1",
- "@embedpdf/plugin-viewport": "^1.3.1",
- "@embedpdf/plugin-zoom": "^1.3.1",
+ "@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",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@iconify/react": "^6.0.2",
diff --git a/frontend/src/components/fileEditor/FileEditorThumbnail.tsx b/frontend/src/components/fileEditor/FileEditorThumbnail.tsx
index 8159e30a9..bf3ddd885 100644
--- a/frontend/src/components/fileEditor/FileEditorThumbnail.tsx
+++ b/frontend/src/components/fileEditor/FileEditorThumbnail.tsx
@@ -45,6 +45,7 @@ const FileEditorThumbnail = ({
selectedFiles,
onToggleFile,
onCloseFile,
+ onViewFile,
_onSetStatus,
onReorderFiles,
onDownloadFile,
@@ -205,6 +206,11 @@ const FileEditorThumbnail = ({
onToggleFile(file.id);
};
+ const handleCardDoubleClick = () => {
+ if (!isSupported) return;
+ onViewFile(file.id);
+ };
+
// ---- Style helpers ----
const getHeaderClassName = () => {
if (hasError) return styles.headerError;
@@ -226,6 +232,7 @@ const FileEditorThumbnail = ({
role="listitem"
aria-selected={isSelected}
onClick={handleCardClick}
+ onDoubleClick={handleCardDoubleClick}
>
{/* Header bar */}
= ({
{currentFile && thumbnail ? (
= ({
{/* File info */}
-
+
{currentFile ? currentFile.name : 'No file selected'}
diff --git a/frontend/src/components/layout/Workbench.tsx b/frontend/src/components/layout/Workbench.tsx
index f0f9a542e..62fc48667 100644
--- a/frontend/src/components/layout/Workbench.tsx
+++ b/frontend/src/components/layout/Workbench.tsx
@@ -1,9 +1,11 @@
+import React from 'react';
import { Box } from '@mantine/core';
import { useRainbowThemeContext } from '../shared/RainbowThemeProvider';
import { useToolWorkflow } from '../../contexts/ToolWorkflowContext';
import { useFileHandler } from '../../hooks/useFileHandler';
import { useFileState } from '../../contexts/FileContext';
import { useNavigationState, useNavigationActions } from '../../contexts/NavigationContext';
+import { useViewer } from '../../contexts/ViewerContext';
import './Workbench.css';
import TopControls from '../shared/TopControls';
@@ -20,11 +22,11 @@ export default function Workbench() {
const { isRainbowMode } = useRainbowThemeContext();
// Use context-based hooks to eliminate all prop drilling
- const { state } = useFileState();
+ const { selectors } = useFileState();
const { workbench: currentView } = useNavigationState();
const { actions: navActions } = useNavigationActions();
const setCurrentView = navActions.setWorkbench;
- const activeFiles = state.files.ids;
+ const activeFiles = selectors.getFiles();
const {
previewFile,
pageEditorFunctions,
@@ -44,6 +46,9 @@ export default function Workbench() {
const selectedTool = selectedToolId ? toolRegistry[selectedToolId] : null;
const { addFiles } = useFileHandler();
+ // Get active file index from ViewerContext
+ const { activeFileIndex, setActiveFileIndex } = useViewer();
+
const handlePreviewClose = () => {
setPreviewFile(null);
const previousMode = sessionStorage.getItem('previousMode');
@@ -95,6 +100,8 @@ export default function Workbench() {
setSidebarsVisible={setSidebarsVisible}
previewFile={previewFile}
onClose={handlePreviewClose}
+ activeFileIndex={activeFileIndex}
+ setActiveFileIndex={setActiveFileIndex}
/>
);
@@ -150,6 +157,12 @@ export default function Workbench() {
{
+ const stub = selectors.getStirlingFileStub(f.fileId);
+ return { fileId: f.fileId, name: f.name, versionNumber: stub?.versionNumber };
+ })}
+ currentFileIndex={activeFileIndex}
+ onFileSelect={setActiveFileIndex}
/>
)}
@@ -161,7 +174,7 @@ export default function Workbench() {
className="flex-1 min-h-0 relative z-10 workbench-scrollable "
style={{
transition: 'opacity 0.15s ease-in-out',
- paddingTop: activeFiles.length > 0 ? '3.5rem' : '0',
+ paddingTop: currentView === 'viewer' ? '0' : (activeFiles.length > 0 ? '3.5rem' : '0'),
}}
>
{renderMainContent()}
diff --git a/frontend/src/components/shared/FileDropdownMenu.tsx b/frontend/src/components/shared/FileDropdownMenu.tsx
new file mode 100644
index 000000000..b03995fc7
--- /dev/null
+++ b/frontend/src/components/shared/FileDropdownMenu.tsx
@@ -0,0 +1,78 @@
+import React from 'react';
+import { Menu, Loader, Group, Text } from '@mantine/core';
+import VisibilityIcon from '@mui/icons-material/Visibility';
+import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
+import FitText from './FitText';
+
+interface FileDropdownMenuProps {
+ displayName: string;
+ activeFiles: Array<{ fileId: string; name: string; versionNumber?: number }>;
+ currentFileIndex: number;
+ onFileSelect?: (index: number) => void;
+ switchingTo?: string | null;
+ viewOptionStyle: React.CSSProperties;
+ pillRef?: React.RefObject;
+}
+
+export const FileDropdownMenu: React.FC = ({
+ displayName,
+ activeFiles,
+ currentFileIndex,
+ onFileSelect,
+ switchingTo,
+ viewOptionStyle,
+}) => {
+ return (
+
+ );
+};
diff --git a/frontend/src/components/shared/TopControls.tsx b/frontend/src/components/shared/TopControls.tsx
index 8edae6e72..73ae39311 100644
--- a/frontend/src/components/shared/TopControls.tsx
+++ b/frontend/src/components/shared/TopControls.tsx
@@ -6,9 +6,10 @@ import VisibilityIcon from "@mui/icons-material/Visibility";
import EditNoteIcon from "@mui/icons-material/EditNote";
import FolderIcon from "@mui/icons-material/Folder";
import { WorkbenchType, isValidWorkbench } from '../../types/workbench';
+import { FileDropdownMenu } from './FileDropdownMenu';
-const viewOptionStyle = {
+const viewOptionStyle: React.CSSProperties = {
display: 'inline-flex',
flexDirection: 'row',
alignItems: 'center',
@@ -19,16 +20,38 @@ const viewOptionStyle = {
// Build view options showing text always
-const createViewOptions = (currentView: WorkbenchType, switchingTo: WorkbenchType | null) => {
+const createViewOptions = (
+ currentView: WorkbenchType,
+ switchingTo: WorkbenchType | null,
+ activeFiles: Array<{ fileId: string; name: string; versionNumber?: number }>,
+ currentFileIndex: number,
+ onFileSelect?: (index: number) => void
+) => {
+ const currentFile = activeFiles[currentFileIndex];
+ const isInViewer = currentView === 'viewer';
+ const fileName = currentFile?.name || '';
+ const displayName = isInViewer && fileName ? fileName : 'Viewer';
+ const hasMultipleFiles = activeFiles.length > 1;
+ const showDropdown = isInViewer && hasMultipleFiles;
+
const viewerOption = {
- label: (
-
+ label: showDropdown ? (
+
+ ) : (
+
{switchingTo === "viewer" ? (
) : (
)}
- Viewer
+ {displayName}
),
value: "viewer",
@@ -36,7 +59,7 @@ const createViewOptions = (currentView: WorkbenchType, switchingTo: WorkbenchTyp
const pageEditorOption = {
label: (
-
+
{currentView === "pageEditor" ? (
<>
{switchingTo === "pageEditor" ?
:
}
@@ -55,7 +78,7 @@ const createViewOptions = (currentView: WorkbenchType, switchingTo: WorkbenchTyp
const fileEditorOption = {
label: (
-
+
{currentView === "fileEditor" ? (
<>
{switchingTo === "fileEditor" ?
:
}
@@ -83,12 +106,18 @@ const createViewOptions = (currentView: WorkbenchType, switchingTo: WorkbenchTyp
interface TopControlsProps {
currentView: WorkbenchType;
setCurrentView: (view: WorkbenchType) => void;
+ activeFiles?: Array<{ fileId: string; name: string; versionNumber?: number }>;
+ currentFileIndex?: number;
+ onFileSelect?: (index: number) => void;
}
const TopControls = ({
currentView,
setCurrentView,
- }: TopControlsProps) => {
+ activeFiles = [],
+ currentFileIndex = 0,
+ onFileSelect,
+}: TopControlsProps) => {
const { isRainbowMode } = useRainbowThemeContext();
const [switchingTo, setSwitchingTo] = useState
(null);
@@ -118,7 +147,7 @@ const TopControls = ({
void;
onClose?: () => void;
previewFile?: File | null;
+ activeFileIndex?: number;
+ setActiveFileIndex?: (index: number) => void;
}
const EmbedPdfViewerContent = ({
@@ -27,9 +28,9 @@ const EmbedPdfViewerContent = ({
setSidebarsVisible: _setSidebarsVisible,
onClose,
previewFile,
+ activeFileIndex: externalActiveFileIndex,
+ setActiveFileIndex: externalSetActiveFileIndex,
}: EmbedPdfViewerProps) => {
- const theme = useMantineTheme();
- const { colorScheme: _colorScheme } = useMantineColorScheme();
const viewerRef = React.useRef(null);
const [isViewerHovered, setIsViewerHovered] = React.useState(false);
@@ -52,10 +53,11 @@ const EmbedPdfViewerContent = ({
const { signatureApiRef, historyApiRef } = useSignature();
// Get current file from FileContext
- const { selectors } = useFileState();
+ const { selectors, state } = useFileState();
const { actions } = useFileActions();
const activeFiles = selectors.getFiles();
const activeFileIds = activeFiles.map(f => f.fileId);
+ const selectedFileIds = state.ui.selectedFileIds;
// Navigation guard for unsaved changes
const { setHasUnsavedChanges, registerUnsavedChangesChecker, unregisterUnsavedChangesChecker } = useNavigationGuard();
@@ -67,15 +69,40 @@ const EmbedPdfViewerContent = ({
// Enable annotations when: in sign mode, OR annotation mode is active, OR we want to show existing annotations
const shouldEnableAnnotations = isSignatureMode || isAnnotationMode || isAnnotationsVisible;
+ // Track which file tab is active
+ const [internalActiveFileIndex, setInternalActiveFileIndex] = useState(0);
+ const activeFileIndex = externalActiveFileIndex ?? internalActiveFileIndex;
+ const setActiveFileIndex = externalSetActiveFileIndex ?? setInternalActiveFileIndex;
+ const hasInitializedFromSelection = useRef(false);
+
+ // When viewer opens with a selected file, switch to that file
+ useEffect(() => {
+ if (!hasInitializedFromSelection.current && selectedFileIds.length > 0 && activeFiles.length > 0) {
+ const selectedFileId = selectedFileIds[0];
+ const index = activeFiles.findIndex(f => f.fileId === selectedFileId);
+ if (index !== -1 && index !== activeFileIndex) {
+ setActiveFileIndex(index);
+ }
+ hasInitializedFromSelection.current = true;
+ }
+ }, [selectedFileIds, activeFiles, activeFileIndex]);
+
+ // Reset active tab if it's out of bounds
+ useEffect(() => {
+ if (activeFileIndex >= activeFiles.length && activeFiles.length > 0) {
+ setActiveFileIndex(0);
+ }
+ }, [activeFiles.length, activeFileIndex]);
+
// Determine which file to display
const currentFile = React.useMemo(() => {
if (previewFile) {
return previewFile;
} else if (activeFiles.length > 0) {
- return activeFiles[0]; // Use first file for simplicity
+ return activeFiles[activeFileIndex] || activeFiles[0];
}
return null;
- }, [previewFile, activeFiles]);
+ }, [previewFile, activeFiles, activeFileIndex]);
// Get file with URL for rendering
const fileWithUrl = useFileWithUrl(currentFile);
@@ -244,15 +271,6 @@ const EmbedPdfViewerContent = ({
) : (
<>
- {/* Tabs for multiple files */}
- {activeFiles.length > 1 && !previewFile && (
-
-
- Multiple files loaded - showing first file for now
-
-
- )}
-
{/* EmbedPDF Viewer */}
{/* Navigation Warning Modal */}
diff --git a/frontend/src/components/viewer/LocalEmbedPDF.tsx b/frontend/src/components/viewer/LocalEmbedPDF.tsx
index a7fe26d6e..cc3550dd8 100644
--- a/frontend/src/components/viewer/LocalEmbedPDF.tsx
+++ b/frontend/src/components/viewer/LocalEmbedPDF.tsx
@@ -18,7 +18,6 @@ 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 { Rotation } from '@embedpdf/models';
// Import annotation plugins
import { HistoryPluginPackage } from '@embedpdf/plugin-history/react';
@@ -67,6 +66,10 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
const plugins = useMemo(() => {
if (!pdfUrl) return [];
+ // Calculate 3.5rem in pixels dynamically based on root font size
+ const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
+ const viewportGap = rootFontSize * 3.5;
+
return [
createPluginRegistration(LoaderPluginPackage, {
loadingOptions: {
@@ -78,7 +81,7 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
},
}),
createPluginRegistration(ViewportPluginPackage, {
- viewportGap: 10,
+ viewportGap,
}),
createPluginRegistration(ScrollPluginPackage, {
strategy: ScrollStrategy.Vertical,
@@ -134,9 +137,7 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
createPluginRegistration(ThumbnailPluginPackage),
// Register rotate plugin
- createPluginRegistration(RotatePluginPackage, {
- defaultRotation: Rotation.Degree0, // Start with no rotation
- }),
+ createPluginRegistration(RotatePluginPackage),
// Register export plugin for downloading PDFs
createPluginRegistration(ExportPluginPackage, {
@@ -288,48 +289,50 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
}}
>
(
-
-
- e.preventDefault()}
- onDrop={(e) => e.preventDefault()}
- onDragOver={(e) => e.preventDefault()}
- >
- {/* High-resolution tile layer */}
-
+ renderPage={({ document, width, height, pageIndex, scale, rotation }) => {
+ return (
+
+
+ e.preventDefault()}
+ onDrop={(e) => e.preventDefault()}
+ onDragOver={(e) => e.preventDefault()}
+ >
+ {/* High-resolution tile layer */}
+
- {/* Search highlight layer */}
-
+ {/* Search highlight layer */}
+
- {/* Selection layer for text interaction */}
-
- {/* Annotation layer for signatures (only when enabled) */}
- {enableAnnotations && (
-
- )}
-
-
-
- )}
+ {/* Selection layer for text interaction */}
+
+ {/* Annotation layer for signatures (only when enabled) */}
+ {enableAnnotations && (
+
+ )}
+
+
+
+ );
+ }}
/>
diff --git a/frontend/src/components/viewer/LocalEmbedPDFWithAnnotations.tsx b/frontend/src/components/viewer/LocalEmbedPDFWithAnnotations.tsx
index af60d8fa8..25efcb7dd 100644
--- a/frontend/src/components/viewer/LocalEmbedPDFWithAnnotations.tsx
+++ b/frontend/src/components/viewer/LocalEmbedPDFWithAnnotations.tsx
@@ -64,6 +64,10 @@ export function LocalEmbedPDFWithAnnotations({
const plugins = useMemo(() => {
if (!pdfUrl) return [];
+ // Calculate 3.5rem in pixels dynamically based on root font size
+ const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
+ const viewportGap = rootFontSize * 3.5;
+
return [
createPluginRegistration(LoaderPluginPackage, {
loadingOptions: {
@@ -75,7 +79,7 @@ export function LocalEmbedPDFWithAnnotations({
},
}),
createPluginRegistration(ViewportPluginPackage, {
- viewportGap: 10,
+ viewportGap,
}),
createPluginRegistration(ScrollPluginPackage, {
strategy: ScrollStrategy.Vertical,
diff --git a/frontend/src/components/viewer/ThumbnailSidebar.tsx b/frontend/src/components/viewer/ThumbnailSidebar.tsx
index 6c3422d36..e6c515941 100644
--- a/frontend/src/components/viewer/ThumbnailSidebar.tsx
+++ b/frontend/src/components/viewer/ThumbnailSidebar.tsx
@@ -5,15 +5,27 @@ import { useViewer } from '../../contexts/ViewerContext';
interface ThumbnailSidebarProps {
visible: boolean;
onToggle: () => void;
+ activeFileIndex?: number;
}
-export function ThumbnailSidebar({ visible, onToggle: _onToggle }: ThumbnailSidebarProps) {
+export function ThumbnailSidebar({ visible, onToggle: _onToggle, activeFileIndex }: ThumbnailSidebarProps) {
const { getScrollState, scrollActions, getThumbnailAPI } = useViewer();
const [thumbnails, setThumbnails] = useState<{ [key: number]: string }>({});
const scrollState = getScrollState();
const thumbnailAPI = getThumbnailAPI();
+ // Clear thumbnails when active file changes
+ useEffect(() => {
+ // Revoke old blob URLs to prevent memory leaks
+ Object.values(thumbnails).forEach((thumbUrl) => {
+ if (typeof thumbUrl === 'string' && thumbUrl.startsWith('blob:')) {
+ URL.revokeObjectURL(thumbUrl);
+ }
+ });
+ setThumbnails({});
+ }, [activeFileIndex]);
+
// Clear thumbnails when sidebar closes and revoke blob URLs to prevent memory leaks
useEffect(() => {
if (!visible) {
diff --git a/frontend/src/components/viewer/Viewer.tsx b/frontend/src/components/viewer/Viewer.tsx
index ea2403fd0..dab9ff826 100644
--- a/frontend/src/components/viewer/Viewer.tsx
+++ b/frontend/src/components/viewer/Viewer.tsx
@@ -5,6 +5,8 @@ export interface ViewerProps {
setSidebarsVisible: (v: boolean) => void;
onClose?: () => void;
previewFile?: File | null;
+ activeFileIndex?: number;
+ setActiveFileIndex?: (index: number) => void;
}
const Viewer = (props: ViewerProps) => {
diff --git a/frontend/src/contexts/ViewerContext.tsx b/frontend/src/contexts/ViewerContext.tsx
index 895776f96..e3aa94487 100644
--- a/frontend/src/contexts/ViewerContext.tsx
+++ b/frontend/src/contexts/ViewerContext.tsx
@@ -132,6 +132,10 @@ interface ViewerContextType {
setAnnotationMode: (enabled: boolean) => void;
toggleAnnotationMode: () => void;
+ // Active file index for multi-file viewing
+ activeFileIndex: number;
+ setActiveFileIndex: (index: number) => void;
+
// State getters - read current state from bridges
getScrollState: () => ScrollState;
getZoomState: () => ZoomState;
@@ -219,6 +223,7 @@ export const ViewerProvider: React.FC = ({ children }) => {
const [isThumbnailSidebarVisible, setIsThumbnailSidebarVisible] = useState(false);
const [isAnnotationsVisible, setIsAnnotationsVisible] = useState(true);
const [isAnnotationMode, setIsAnnotationModeState] = useState(false);
+ const [activeFileIndex, setActiveFileIndex] = useState(0);
// Get current navigation state to check if we're in sign mode
useNavigation();
@@ -577,6 +582,10 @@ export const ViewerProvider: React.FC = ({ children }) => {
setAnnotationMode,
toggleAnnotationMode,
+ // Active file index
+ activeFileIndex,
+ setActiveFileIndex,
+
// State getters
getScrollState,
getZoomState,
diff --git a/frontend/src/index.css b/frontend/src/index.css
index b4bb41b3e..c5a604900 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -52,3 +52,12 @@ code {
color: var(--mantine-color-blue-8);
text-decoration: underline;
}
+
+/* Viewer file tabs */
+.viewer-file-tab {
+ justify-content: flex-start;
+}
+
+.viewer-file-tab[data-active] {
+ background-color: rgba(147, 197, 253, 0.5);
+}
diff --git a/frontend/src/tools/Sign.tsx b/frontend/src/tools/Sign.tsx
index f76621895..64a304042 100644
--- a/frontend/src/tools/Sign.tsx
+++ b/frontend/src/tools/Sign.tsx
@@ -17,7 +17,7 @@ const Sign = (props: BaseToolProps) => {
const { setWorkbench } = useNavigation();
const { setSignatureConfig, activateDrawMode, activateSignaturePlacementMode, deactivateDrawMode, updateDrawSettings, undo, redo, signatureApiRef, getImageData, setSignaturesApplied } = useSignature();
const { consumeFiles, selectors } = useFileContext();
- const { exportActions, getScrollState } = useViewer();
+ const { exportActions, getScrollState, activeFileIndex, setActiveFileIndex } = useViewer();
const { setHasUnsavedChanges, unregisterUnsavedChangesChecker } = useNavigation();
// Track which signature mode was active for reactivation after save
@@ -75,19 +75,11 @@ const Sign = (props: BaseToolProps) => {
unregisterUnsavedChangesChecker();
setHasUnsavedChanges(false);
- // Get the original file
- let originalFile = null;
- if (base.selectedFiles.length > 0) {
- originalFile = base.selectedFiles[0];
- } else {
- const allFileIds = selectors.getAllFileIds();
- if (allFileIds.length > 0) {
- const stirlingFile = selectors.getFile(allFileIds[0]);
- if (stirlingFile) {
- originalFile = stirlingFile;
- }
- }
- }
+ // Get the original file from FileContext using activeFileIndex
+ // The viewer displays files from FileContext, not from base.selectedFiles
+ const allFiles = selectors.getFiles();
+ const fileIndex = activeFileIndex < allFiles.length ? activeFileIndex : 0;
+ const originalFile = allFiles[fileIndex];
if (!originalFile) {
console.error('No file available to replace');
@@ -101,7 +93,8 @@ const Sign = (props: BaseToolProps) => {
exportActions,
selectors,
originalFile,
- getScrollState
+ getScrollState,
+ activeFileIndex
});
if (flattenResult) {
@@ -112,6 +105,10 @@ const Sign = (props: BaseToolProps) => {
[flattenResult.outputStub]
);
+ // According to FileReducer.processFileSwap, new files are inserted at the beginning
+ // So the new file will be at index 0
+ setActiveFileIndex(0);
+
// Mark signatures as applied
setSignaturesApplied(true);
@@ -125,7 +122,7 @@ const Sign = (props: BaseToolProps) => {
} catch (error) {
console.error('Error saving signed document:', error);
}
- }, [exportActions, base.selectedFiles, selectors, consumeFiles, signatureApiRef, getImageData, setWorkbench, activateDrawMode, setSignaturesApplied, getScrollState, handleDeactivateSignature, setHasUnsavedChanges, unregisterUnsavedChangesChecker]);
+ }, [exportActions, base.selectedFiles, selectors, consumeFiles, signatureApiRef, getImageData, setWorkbench, activateDrawMode, setSignaturesApplied, getScrollState, handleDeactivateSignature, setHasUnsavedChanges, unregisterUnsavedChangesChecker, activeFileIndex, setActiveFileIndex]);
const getSteps = () => {
const steps = [];
diff --git a/frontend/src/utils/signatureFlattening.ts b/frontend/src/utils/signatureFlattening.ts
index 1ff343155..ccb739253 100644
--- a/frontend/src/utils/signatureFlattening.ts
+++ b/frontend/src/utils/signatureFlattening.ts
@@ -19,6 +19,7 @@ interface SignatureFlatteningOptions {
selectors: MinimalFileContextSelectors;
originalFile?: StirlingFile;
getScrollState: () => { currentPage: number; totalPages: number };
+ activeFileIndex?: number;
}
export interface SignatureFlatteningResult {
@@ -28,7 +29,7 @@ export interface SignatureFlatteningResult {
}
export async function flattenSignatures(options: SignatureFlatteningOptions): Promise {
- const { signatureApiRef, getImageData, exportActions, selectors, originalFile, getScrollState } = options;
+ const { signatureApiRef, getImageData, exportActions, selectors, originalFile, getScrollState, activeFileIndex } = options;
try {
// Step 1: Extract all annotations from EmbedPDF before export
@@ -104,10 +105,12 @@ export async function flattenSignatures(options: SignatureFlatteningOptions): Pr
if (!currentFile) {
const allFileIds = selectors.getAllFileIds();
if (allFileIds.length > 0) {
- const fileStub = selectors.getStirlingFileStub(allFileIds[0]);
- const fileObject = selectors.getFile(allFileIds[0]);
+ // Use activeFileIndex if provided, otherwise default to 0
+ const fileIndex = activeFileIndex !== undefined && activeFileIndex < allFileIds.length ? activeFileIndex : 0;
+ const fileStub = selectors.getStirlingFileStub(allFileIds[fileIndex]);
+ const fileObject = selectors.getFile(allFileIds[fileIndex]);
if (fileStub && fileObject) {
- currentFile = createStirlingFile(fileObject, allFileIds[0] as FileId);
+ currentFile = createStirlingFile(fileObject, allFileIds[fileIndex] as FileId);
}
}
}