diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a8365a8..2af8e23 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,6 +12,7 @@ "@tabler/icons-react": "^3.35.0", "axios": "^1.13.2", "pdfjs-dist": "^5.4.394", + "prop-types": "^15.8.1", "react": "^19.2.0", "react-dom": "^19.2.0", "react-redux": "^9.2.0", @@ -22,18 +23,19 @@ "@babel/preset-env": "^7.28.5", "@babel/preset-react": "^7.28.5", "@babel/preset-typescript": "^7.28.5", + "@eslint/js": "^9.39.1", "@svgr/webpack": "^8.1.0", "@typescript-eslint/eslint-plugin": "^8.46.4", "@typescript-eslint/parser": "^8.18.1", "babel-loader": "^10.0.0", "copy-webpack-plugin": "^13.0.1", "css-loader": "^7.1.2", - "dotenv": "^17.2.3", "eslint": "^9.39.1", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", "globals": "^16.5.0", "html-webpack-plugin": "^5.6.4", + "knip": "^5.71.0", "style-loader": "^4.0.0", "typescript": "^5.7.3", "webpack": "^5.102.1", @@ -1868,6 +1870,40 @@ "node": ">=14.17.0" } }, + "node_modules/@emnapi/core": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", + "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", @@ -2495,6 +2531,19 @@ "node": ">= 10" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz", + "integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2533,6 +2582,289 @@ "node": ">= 8" } }, + "node_modules/@oxc-resolver/binding-android-arm-eabi": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.14.2.tgz", + "integrity": "sha512-bTrdE4Z1JcGwPxBOaGbxRbpOHL8/xPVJTTq3/bAZO2euWX0X7uZ+XxsbC+5jUDMhLenqdFokgE1akHEU4xsh6A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oxc-resolver/binding-android-arm64": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.14.2.tgz", + "integrity": "sha512-bL7/f6YGKUvt/wzpX7ZrHCf1QerotbSG+IIb278AklXuwr6yQdfQHt7KQ8hAWqSYpB2TAbPbAa9HE4wzVyxL9Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oxc-resolver/binding-darwin-arm64": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.14.2.tgz", + "integrity": "sha512-0zhMhqHz/kC6/UzMC4D9mVBz3/M9UTorbaULfHjAW5b8SUC08H01lZ5fR3OzfDbJI0ByLfiQZmbovuR/pJ8Wzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxc-resolver/binding-darwin-x64": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.14.2.tgz", + "integrity": "sha512-kRJBTCQnrGy1mjO+658yMrlGYWEKi6j4JvKt92PRCoeDX0vW4jvzgoJXzZXNxZL1pCY6jIdwsn9u53v4jwpR6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oxc-resolver/binding-freebsd-x64": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.14.2.tgz", + "integrity": "sha512-lpKiya7qPq5EAV5E16SJbxfhNYRCBZATGngn9mZxR2fMLDVbHISDIP2Br8eWA8M1FBJFsOGgBzxDo+42ySSNZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.14.2.tgz", + "integrity": "sha512-zRIf49IGs4cE9rwpVM3NxlHWquZpwQLebtc9dY9S+4+B+PSLIP95BrzdRfkspwzWC5DKZsOWpvGQjxQiLoUwGA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.14.2.tgz", + "integrity": "sha512-sF1fBrcfwoRkv1pR3Kp6D5MuBeHRPxYuzk9rhaun/50vq5nAMOaomkEm4hBbTSubfU86CoBIEbLUQ+1f7NvUVA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.14.2.tgz", + "integrity": "sha512-O8iTBqz6oxf1k93Rn6WMGGQYo2jV1K81hq4N/Nke3dHE25EIEg2RKQqMz1dFrvVb2RkvD7QaUTEevbx0Lq+4wQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-arm64-musl": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.14.2.tgz", + "integrity": "sha512-HOfzpS6eUxvdch9UlXCMx2kNJWMNBjUpVJhseqAKDB1dlrfCHgexeLyBX977GLXkq2BtNXKsY3KCryy1QhRSRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.14.2.tgz", + "integrity": "sha512-0uLG6F2zljUseQAUmlpx/9IdKpiLsSirpmrr8/aGVfiEurIJzC/1lo2HQskkM7e0VVOkXg37AjHUDLE23Fi8SA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.14.2.tgz", + "integrity": "sha512-Pdh0BH/E0YIK7Qg95IsAfQyU9rAoDoFh50R19zCTNfjSnwsoDMGHjmUc82udSfPo2YMnuxA+/+aglxmLQVSu2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.14.2.tgz", + "integrity": "sha512-3DLQhJ2r53rCH5cudYFqD7nh+Z6ABvld3GjbiqHhT43GMIPw3JcHekC2QunLRNjRr1G544fo1HtjTJz9rCBpyg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.14.2.tgz", + "integrity": "sha512-G5BnAOQ5f+RUG1cvlJ4BvV+P7iKLYBv67snqgcfwD5b2N4UwJj32bt4H5JfolocWy4x3qUjEDWTIjHdE+2uZ9w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-x64-gnu": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.14.2.tgz", + "integrity": "sha512-VirQAX2PqKrhWtQGsSDEKlPhbgh3ggjT1sWuxLk4iLFwtyA2tLEPXJNAsG0kfAS2+VSA8OyNq16wRpQlMPZ4yA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-linux-x64-musl": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.14.2.tgz", + "integrity": "sha512-q4ORcwMkpzu4EhZyka/s2TuH2QklEHAr/mIQBXzu5BACeBJZIFkICp8qrq4XVnkEZ+XhSFTvBECqfMTT/4LSkA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oxc-resolver/binding-openharmony-arm64": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.14.2.tgz", + "integrity": "sha512-ZsMIpDCxSFpUM/TwOovX5vZUkV0IukPFnrKTGaeJRuTKXMcJxMiQGCYTwd6y684Y3j55QZqIMkVM9NdCGUX6Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.14.2.tgz", + "integrity": "sha512-Lvq5ZZNvSjT3Jq/buPFMtp55eNyGlEWsq30tN+yLOfODSo6T6yAJNs6+wXtqu9PiMj4xpVtgXypHtbQ1f+t7kw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.14.2.tgz", + "integrity": "sha512-7w7WHSLSSmkkYHH52QF7TrO0Z8eaIjRUrre5M56hSWRAZupCRzADZxBVMpDnHobZ8MAa2kvvDEfDbERuOK/avQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxc-resolver/binding-win32-ia32-msvc": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-11.14.2.tgz", + "integrity": "sha512-hIrdlWa6tzqyfuWrxUetURBWHttBS+NMbBrGhCupc54NCXFy2ArB+0JOOaLYiI2ShKL5a3uqB7EWxmjzOuDdPQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oxc-resolver/binding-win32-x64-msvc": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.14.2.tgz", + "integrity": "sha512-dP9aV6AZRRpg5mlg0eMuTROtttpQwj3AiegNJ/NNmMSjs+0+aLNcgkWRPhskK3vjTsthH4/+kKLpnQhSxdJkNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", @@ -2853,6 +3185,17 @@ "node": ">=10.13.0" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", @@ -5237,19 +5580,6 @@ "tslib": "^2.0.3" } }, - "node_modules/dotenv": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", - "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -6138,6 +6468,16 @@ "node": ">=0.8.0" } }, + "node_modules/fd-package-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz", + "integrity": "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "walk-up-path": "^4.0.0" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -6280,6 +6620,22 @@ "node": ">= 6" } }, + "node_modules/formatly": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz", + "integrity": "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fd-package-json": "^2.0.0" + }, + "bin": { + "formatly": "bin/index.mjs" + }, + "engines": { + "node": ">=18.3.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -7544,17 +7900,26 @@ "node": ">= 10.13.0" } }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -7654,6 +8019,74 @@ "node": ">=0.10.0" } }, + "node_modules/knip": { + "version": "5.71.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-5.71.0.tgz", + "integrity": "sha512-hwgdqEJ+7DNJ5jE8BCPu7b57TY7vUwP6MzWYgCgPpg6iPCee/jKPShDNIlFER2koti4oz5xF88VJbKCb4Wl71g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/webpro" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/knip" + } + ], + "license": "ISC", + "dependencies": { + "@nodelib/fs.walk": "^1.2.3", + "fast-glob": "^3.3.3", + "formatly": "^0.3.0", + "jiti": "^2.6.0", + "js-yaml": "^4.1.1", + "minimist": "^1.2.8", + "oxc-resolver": "^11.13.2", + "picocolors": "^1.1.1", + "picomatch": "^4.0.1", + "smol-toml": "^1.5.2", + "strip-json-comments": "5.0.3", + "zod": "^4.1.11" + }, + "bin": { + "knip": "bin/knip.js", + "knip-bun": "bin/knip-bun.js" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "@types/node": ">=18", + "typescript": ">=5.0.4 <7" + } + }, + "node_modules/knip/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/knip/node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/launch-editor": { "version": "2.11.1", "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.11.1.tgz", @@ -7738,7 +8171,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -7917,6 +8349,16 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -8036,7 +8478,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -8225,6 +8666,38 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/oxc-resolver": { + "version": "11.14.2", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.14.2.tgz", + "integrity": "sha512-M5fERQKcrCngMZNnk1gRaBbYcqpqXLgMcoqAo7Wpty+KH0I18i03oiy2peUsGJwFaKAEbmo+CtAyhXh08RZ1RA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-resolver/binding-android-arm-eabi": "11.14.2", + "@oxc-resolver/binding-android-arm64": "11.14.2", + "@oxc-resolver/binding-darwin-arm64": "11.14.2", + "@oxc-resolver/binding-darwin-x64": "11.14.2", + "@oxc-resolver/binding-freebsd-x64": "11.14.2", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.14.2", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.14.2", + "@oxc-resolver/binding-linux-arm64-gnu": "11.14.2", + "@oxc-resolver/binding-linux-arm64-musl": "11.14.2", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.14.2", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.14.2", + "@oxc-resolver/binding-linux-riscv64-musl": "11.14.2", + "@oxc-resolver/binding-linux-s390x-gnu": "11.14.2", + "@oxc-resolver/binding-linux-x64-gnu": "11.14.2", + "@oxc-resolver/binding-linux-x64-musl": "11.14.2", + "@oxc-resolver/binding-openharmony-arm64": "11.14.2", + "@oxc-resolver/binding-wasm32-wasi": "11.14.2", + "@oxc-resolver/binding-win32-arm64-msvc": "11.14.2", + "@oxc-resolver/binding-win32-ia32-msvc": "11.14.2", + "@oxc-resolver/binding-win32-x64-msvc": "11.14.2" + } + }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -8590,7 +9063,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -8736,7 +9208,6 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/react-redux": { @@ -9613,6 +10084,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/smol-toml": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.5.2.tgz", + "integrity": "sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, "node_modules/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -10572,6 +11056,16 @@ "node": ">= 0.8" } }, + "node_modules/walk-up-path": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz", + "integrity": "sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/watchpack": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 629b87c..3d9e490 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,7 +7,7 @@ "dev": "webpack serve --mode development --open", "build": "webpack --mode production", "lint": "eslint src --ext .js,.jsx,.ts,.tsx", - "check": "tsc --noEmit && npm run lint", + "check": "tsc --noEmit && npx knip && npm run lint", "test:engine": "node --test tests/workspaceEngine.test.js" }, "dependencies": { @@ -15,6 +15,7 @@ "@tabler/icons-react": "^3.35.0", "axios": "^1.13.2", "pdfjs-dist": "^5.4.394", + "prop-types": "^15.8.1", "react": "^19.2.0", "react-dom": "^19.2.0", "react-redux": "^9.2.0", @@ -25,18 +26,19 @@ "@babel/preset-env": "^7.28.5", "@babel/preset-react": "^7.28.5", "@babel/preset-typescript": "^7.28.5", + "@eslint/js": "^9.39.1", "@svgr/webpack": "^8.1.0", "@typescript-eslint/eslint-plugin": "^8.46.4", "@typescript-eslint/parser": "^8.18.1", "babel-loader": "^10.0.0", "copy-webpack-plugin": "^13.0.1", "css-loader": "^7.1.2", - "dotenv": "^17.2.3", "eslint": "^9.39.1", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", "globals": "^16.5.0", "html-webpack-plugin": "^5.6.4", + "knip": "^5.71.0", "style-loader": "^4.0.0", "typescript": "^5.7.3", "webpack": "^5.102.1", diff --git a/frontend/src/app/ApiContext.tsx b/frontend/src/app/ApiContext.tsx index d7abd48..0619330 100644 --- a/frontend/src/app/ApiContext.tsx +++ b/frontend/src/app/ApiContext.tsx @@ -41,5 +41,3 @@ export const useApi = (): ApiContextValue => { } return ctx; }; - -export const getHttpClient = (): HttpClient => httpClient; diff --git a/frontend/src/app/PanelManagerContext.tsx b/frontend/src/app/PanelManagerContext.tsx index 7627752..fd1074e 100644 --- a/frontend/src/app/PanelManagerContext.tsx +++ b/frontend/src/app/PanelManagerContext.tsx @@ -421,5 +421,3 @@ export const usePanelResizeBindings = ( isPanelResizing: resizingPanel === panel, }; }; - -export default PanelManagerContext; diff --git a/frontend/src/app/WorkspaceSelectionContext.tsx b/frontend/src/app/WorkspaceSelectionContext.tsx index 47fc5ad..3f9ded5 100644 --- a/frontend/src/app/WorkspaceSelectionContext.tsx +++ b/frontend/src/app/WorkspaceSelectionContext.tsx @@ -1,5 +1,5 @@ import React, { createContext, useContext } from 'react'; -import type useWorkspaceSelection from './useWorkspaceSelection'; +import type { useWorkspaceSelection } from './useWorkspaceSelection'; export type WorkspaceSelectionValue = ReturnType; @@ -23,5 +23,3 @@ export const useWorkspaceSelectionContext = () => { } return context; }; - -export default WorkspaceSelectionContext; diff --git a/frontend/src/app/useDetailPanel.ts b/frontend/src/app/useDetailPanel.ts index 671e5d0..af7c594 100644 --- a/frontend/src/app/useDetailPanel.ts +++ b/frontend/src/app/useDetailPanel.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -export interface DetailDocument { +interface DetailDocument { id?: string; [key: string]: unknown; } @@ -102,5 +102,3 @@ export const useDetailPanel = ({ setDetailPanelOpen, }; }; - -export default useDetailPanel; diff --git a/frontend/src/app/useDocumentSelection.ts b/frontend/src/app/useDocumentSelection.ts index 446364c..a96d92c 100644 --- a/frontend/src/app/useDocumentSelection.ts +++ b/frontend/src/app/useDocumentSelection.ts @@ -285,5 +285,3 @@ export const useDocumentSelection = ({ configureSelectionEnvironment, }; }; - -export default useDocumentSelection; diff --git a/frontend/src/app/useManagementModals.tsx b/frontend/src/app/useManagementModals.tsx index 322763a..4b42179 100644 --- a/frontend/src/app/useManagementModals.tsx +++ b/frontend/src/app/useManagementModals.tsx @@ -231,5 +231,3 @@ export const useManagementModals = ({ activeModal, }; }; - -export default useManagementModals; diff --git a/frontend/src/app/useWorkspaceSelection.ts b/frontend/src/app/useWorkspaceSelection.ts index 11fdf02..0a31e07 100644 --- a/frontend/src/app/useWorkspaceSelection.ts +++ b/frontend/src/app/useWorkspaceSelection.ts @@ -115,5 +115,3 @@ export const useWorkspaceSelection = ({ inspectFolder, }; }; - -export default useWorkspaceSelection; diff --git a/frontend/src/app/useWorkspaceSurface.tsx b/frontend/src/app/useWorkspaceSurface.tsx index 0c6b2be..0e28d4b 100644 --- a/frontend/src/app/useWorkspaceSurface.tsx +++ b/frontend/src/app/useWorkspaceSurface.tsx @@ -7,17 +7,17 @@ import { usePanelManager } from './PanelManagerContext'; import { FolderManagerProvider } from '../folders/FolderManagerContext'; import type { Identifier } from '../types/identifiers'; -export type EnsureAssetUrl = ( +type EnsureAssetUrl = ( docId: Identifier, asset: unknown, options?: Record, ) => Promise | void; -export type EnsurePreviewData = (docId: Identifier, options?: Record) => Promise; -export type GetDocumentAsset = (document: unknown, assetType: string) => unknown; -export type NotifyApiError = (error: unknown, fallbackMessage?: string) => void; +type EnsurePreviewData = (docId: Identifier, options?: Record) => Promise; +type GetDocumentAsset = (document: unknown, assetType: string) => unknown; +type NotifyApiError = (error: unknown, fallbackMessage?: string) => void; -export type DocumentsTableProps = ComponentProps | null; -export type DetailPanelProps = (ComponentProps & { +type DocumentsTableProps = ComponentProps | null; +type DetailPanelProps = (ComponentProps & { onClose?: () => void; onOpenPreview?: (args: { documentIds: Array }) => void; folderNodes?: Map; @@ -27,7 +27,7 @@ export type DetailPanelProps = (ComponentProps & { ) => Promise; }) | null; -export type WorkspaceSurface = { content: ReactNode; detail?: ReactNode | null; detailMode?: 'overlay' | 'inline' | null } | null; +type WorkspaceSurface = { content: ReactNode; detail?: ReactNode | null; detailMode?: 'overlay' | 'inline' | null } | null; export interface UseWorkspaceSurfaceArgs { sidebarHidden?: boolean; @@ -44,7 +44,7 @@ export interface UseWorkspaceSurfaceArgs { closeDocumentPreview?: () => void; } -export interface UseWorkspaceSurfaceResult { +interface UseWorkspaceSurfaceResult { surface: WorkspaceSurface; } diff --git a/frontend/src/appShellContext.ts b/frontend/src/appShellContext.ts index d4b30cc..73e3afe 100644 --- a/frontend/src/appShellContext.ts +++ b/frontend/src/appShellContext.ts @@ -1,6 +1,6 @@ import React from 'react'; -export type AppShellContextValue = Record; +type AppShellContextValue = Record; export const AppShellContext = React.createContext(null); diff --git a/frontend/src/asset_manager.ts b/frontend/src/asset_manager.ts index 5742bb3..72f5183 100644 --- a/frontend/src/asset_manager.ts +++ b/frontend/src/asset_manager.ts @@ -4,9 +4,9 @@ import type { DocumentVersion, Document } from './types/documents'; type Nullable = T | null; -export type { Asset, DocumentVersion as DocumentVersionLike, Document }; +export type { Asset }; -export const resolveAssetExpiresAt = (asset?: { download?: { expires_at: number } | null } | null): number | null => +const resolveAssetExpiresAt = (asset?: { download?: { expires_at: number } | null } | null): number | null => asset?.download?.expires_at ?? null; export const resolveAssetUrl = (asset?: { download?: { url: string } | null } | null): string | null => @@ -20,7 +20,7 @@ export type EnsureAssetUrl = ( export type GetAsset = (document: Document, assetType: string) => Nullable; -export const getAssetFromGroup = ( +const getAssetFromGroup = ( assets?: Asset[] | Record | null, assetType: string = '', ): Nullable => { diff --git a/frontend/src/constants/desktop.ts b/frontend/src/constants/desktop.ts index dfe35b5..3acd06b 100644 --- a/frontend/src/constants/desktop.ts +++ b/frontend/src/constants/desktop.ts @@ -1,46 +1,3 @@ export const DB_NAME = 'papercrate_desk'; export const DB_VERSION = 1; export const LAYOUT_STORE = 'layouts'; - -export const CLICK_ACTIONS = { - selectSingle: 'selectSingle', - openDetail: 'openDetail', - addCard: 'addCard', - addStack: 'addStack', - none: 'none', -} as const; - -export const DRAG_ACTIONS = { - dragSelectSingle: 'dragSelectSingle', - dragSelection: 'dragSelection', - dragSelectStack: 'dragSelectStack', - none: 'none', -} as const; - -export const STACK_HIT_EPSILON = 4; -export const POINTER_DRAG_THRESHOLD_SQUARED = 16; -export const LONG_PRESS_DURATION_MS = 450; - -export const DRAG_HYSTERESIS_PX = 4; -export const DRAG_HYSTERESIS_SQUARED = DRAG_HYSTERESIS_PX * DRAG_HYSTERESIS_PX; -export const EDGE_COLLISION_THRESHOLD = 0.5; - -export const DEBUG_DRAG = false; -export const DEBUG_FOCUS = false; - -export const DESK_CANVAS_PADDING = 24; -export const DESK_ROTATION_RANGE = 7; -export const DESK_DEFAULT_CANVAS_WIDTH = 1024; -export const DESK_DEFAULT_CANVAS_HEIGHT = 680; -export const DESK_CARD_MIN = 240; -export const DESK_CARD_MAX = 340; -export const CARD_PAGE_WEIGHT_GRAMS = 5; -export const CARD_BASE_WEIGHT_GRAMS = 30; -export const DEFAULT_Z_START = 10; -export const MIN_TIMESTEP = 1 / 120; -export const MAX_TIMESTEP = 1 / 20; -export const MAX_DYNAMIC_ROTATION = 7; -export const MAX_ANGULAR_VELOCITY = 210; -export const ANGULAR_DAMPING = 10; -export const TORQUE_TO_ACCELERATION = 0.007; -export const SETTLE_ANGULAR_VELOCITY = 0.45; diff --git a/frontend/src/constants/settings.ts b/frontend/src/constants/settings.ts index c483948..68d6145 100644 --- a/frontend/src/constants/settings.ts +++ b/frontend/src/constants/settings.ts @@ -3,19 +3,19 @@ import ApiTokensSection from '../settings/sections/ApiTokensSection'; import CapabilitySetsSection from '../settings/sections/CapabilitySetsSection'; import PasskeysSection from '../settings/sections/PasskeysSection'; -export const PASSKEYS_SECTION: SettingsSectionConfig = { +const PASSKEYS_SECTION: SettingsSectionConfig = { id: 'passkeys', label: 'Passkeys', component: PasskeysSection, }; -export const API_TOKENS_SECTION: SettingsSectionConfig = { +const API_TOKENS_SECTION: SettingsSectionConfig = { id: 'apiTokens', label: 'API tokens', component: ApiTokensSection, }; -export const CAPABILITY_SETS_SECTION: SettingsSectionConfig = { +const CAPABILITY_SETS_SECTION: SettingsSectionConfig = { id: 'capabilitySets', label: 'Capability sets', component: CapabilitySetsSection, diff --git a/frontend/src/constants/sidebar.ts b/frontend/src/constants/sidebar.ts index ed24baf..21d2364 100644 --- a/frontend/src/constants/sidebar.ts +++ b/frontend/src/constants/sidebar.ts @@ -2,7 +2,6 @@ export const SIDEBAR_COLLAPSE_STORAGE_KEY = 'papercrate_sidebar_collapsed'; export const THEME_STORAGE_KEY = 'papercrate_theme_settings'; export const DEFAULT_NEUTRAL_HUE = 29; export const DEFAULT_NEUTRAL_CHROMA = 0.44; -export const DEFAULT_NEUTRAL_CONTRAST = 0.56; export const DEFAULT_THEME_MODE = 'system'; export const THEME_MODES = ['system', 'light', 'dark']; export const THEME_MODE_LABELS = { diff --git a/frontend/src/constants/workspace.ts b/frontend/src/constants/workspace.ts index 33bec91..86f339d 100644 --- a/frontend/src/constants/workspace.ts +++ b/frontend/src/constants/workspace.ts @@ -8,5 +8,3 @@ export const VIEW_MODE_STORAGE_KEY = 'papercrate_view_mode'; export const SORT_FIELD_STORAGE_KEY = 'papercrate_sort_field'; export const SORT_DIRECTION_STORAGE_KEY = 'papercrate_sort_direction'; export const INCLUDE_DESCENDANTS_STORAGE_KEY = 'papercrate_include_descendants'; - -export const ROOT_FOLDER_LABEL = DEFAULT_FOLDER_NAME; diff --git a/frontend/src/contexts/DocumentOpenContext.tsx b/frontend/src/contexts/DocumentOpenContext.tsx index 3f396e6..e08021a 100644 --- a/frontend/src/contexts/DocumentOpenContext.tsx +++ b/frontend/src/contexts/DocumentOpenContext.tsx @@ -2,9 +2,9 @@ import React, { createContext, useContext, useCallback } from 'react'; import type { Document } from '../types/documents'; import type { Identifier } from '../types/identifiers'; -export type DocumentOpenTarget = 'preview' | 'sidepanel' | 'viewer'; +type DocumentOpenTarget = 'preview' | 'sidepanel' | 'viewer'; -export interface DocumentOpenContextValue { +interface DocumentOpenContextValue { openDocument: (doc: Document, target?: DocumentOpenTarget) => void; } diff --git a/frontend/src/contexts/StatusToastContext.tsx b/frontend/src/contexts/StatusToastContext.tsx index 541b0fc..fccad20 100644 --- a/frontend/src/contexts/StatusToastContext.tsx +++ b/frontend/src/contexts/StatusToastContext.tsx @@ -2,7 +2,7 @@ import React, { createContext, useContext, useState, useCallback, useEffect, use export type ToastVariant = 'info' | 'success' | 'error'; -export interface ToastMessage { +interface ToastMessage { id: string; message: string; variant: ToastVariant; diff --git a/frontend/src/correspondents/CorrespondentsPanel.tsx b/frontend/src/correspondents/CorrespondentsPanel.tsx index b8e8fa1..8863cfa 100644 --- a/frontend/src/correspondents/CorrespondentsPanel.tsx +++ b/frontend/src/correspondents/CorrespondentsPanel.tsx @@ -6,7 +6,7 @@ interface CorrespondentUsage { [key: string]: unknown; } -export interface CorrespondentEntry { +interface CorrespondentEntry { id?: string; name?: string; usage?: CorrespondentUsage; diff --git a/frontend/src/desktop/DesktopWorkspace.tsx b/frontend/src/desktop/DesktopWorkspace.tsx index ebf0389..b33665d 100644 --- a/frontend/src/desktop/DesktopWorkspace.tsx +++ b/frontend/src/desktop/DesktopWorkspace.tsx @@ -8,7 +8,7 @@ import React, { import { LayoutStore, LayoutCard } from './LayoutSystem'; import DesktopDocumentCard from './DesktopDocumentCard'; import usePreviewMetadata from './hooks/usePreviewMetadata'; -import useDeskTagInteractions from './tags/useDeskTagInteractions'; +import { useDeskTagInteractions } from './tags/useDeskTagInteractions'; import '../styles/workspace/workspace-layout.css'; import '../styles/workspace/workspace-items.css'; import '../styles/workspace/workspace-cards.css'; @@ -32,7 +32,7 @@ const computeFallbackCardSize = (_doc: Document, defaultSize: number = 200): Doc return { width: size, height: size, source: 'fallback' }; }; -export interface DesktopWorkspaceProps { +interface DesktopWorkspaceProps { entries: DocumentsListEntry[]; ensureAssetUrl?: (...args: any[]) => Promise; getDocumentAsset?: (...args: any[]) => unknown; diff --git a/frontend/src/desktop/LayoutSystem.ts b/frontend/src/desktop/LayoutSystem.ts index f8ba85c..0e14fa9 100644 --- a/frontend/src/desktop/LayoutSystem.ts +++ b/frontend/src/desktop/LayoutSystem.ts @@ -3,7 +3,7 @@ import { constrainDimensions, getInitialPosition, CONTAINER_PADDING } from './ut import { fetchLayoutRecords, upsertLayoutRecords } from './db'; import { CardPhysics } from './CardPhysics'; -export interface LayoutCardState { +interface LayoutCardState { id: string; x: number; y: number; diff --git a/frontend/src/desktop/db.ts b/frontend/src/desktop/db.ts index 2396025..d84cc9a 100644 --- a/frontend/src/desktop/db.ts +++ b/frontend/src/desktop/db.ts @@ -47,26 +47,6 @@ const requestToPromise = (request: IDBRequest, defaultValue: T): Promise, iteratee: (cursor: IDBCursorWithValue) => void) => - new Promise((resolve, reject) => { - request.onsuccess = (event) => { - const cursor = (event.target as IDBRequest).result; - if (!cursor) { - resolve(); - return; - } - try { - iteratee(cursor); - cursor.continue(); - } catch (error) { - reject(error); - } - }; - request.onerror = () => { - reject(request.error || new Error('IndexedDB cursor failed')); - }; - }); - const transactionComplete = (transaction: IDBTransaction) => new Promise((resolve, reject) => { transaction.oncomplete = () => { @@ -182,36 +162,3 @@ export const upsertLayoutRecords = async ({ console.warn('[desk] Failed to upsert layout records', error); } }; - -export const deleteTenantLayouts = async (tenantId?: TenantId) => { - if (!tenantId) { - return; - } - try { - await withStore('readwrite', (store) => { - const index = store.index('tenantIdx'); - const request = index.openCursor(tenantId); - return iterateCursor(request, (cursor) => { - cursor.delete(); - }); - }); - } catch (error) { - console.warn('[desk] Failed to clean tenant layouts', error); - } -}; - -export const closeDeskDatabase = (): void => { - if (!currentDbPromise.value) { - return; - } - currentDbPromise.value = currentDbPromise.value.then((db) => { - if (db) { - try { - db.close(); - } catch (error) { - console.warn('[desk] Failed to close IndexedDB', error); - } - } - return null; - }); -}; diff --git a/frontend/src/desktop/tags/useDeskTagInteractions.ts b/frontend/src/desktop/tags/useDeskTagInteractions.ts index 9e58fca..27ac140 100644 --- a/frontend/src/desktop/tags/useDeskTagInteractions.ts +++ b/frontend/src/desktop/tags/useDeskTagInteractions.ts @@ -239,5 +239,3 @@ export const useDeskTagInteractions = ({ handleCanvasDrop, }; }; - -export default useDeskTagInteractions; diff --git a/frontend/src/documents/CorrespondentLinks.tsx b/frontend/src/documents/CorrespondentLinks.tsx index e4612a1..28368d7 100644 --- a/frontend/src/documents/CorrespondentLinks.tsx +++ b/frontend/src/documents/CorrespondentLinks.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { NBSP } from '../constants/ui'; -export interface CorrespondentLinkEntry { +interface CorrespondentLinkEntry { id?: string | null; name?: string | null; key?: string; diff --git a/frontend/src/documents/DocumentSummarySection.tsx b/frontend/src/documents/DocumentSummarySection.tsx index dbcbe3e..5be4b25 100644 --- a/frontend/src/documents/DocumentSummarySection.tsx +++ b/frontend/src/documents/DocumentSummarySection.tsx @@ -147,7 +147,7 @@ const normalizeQuickAddOption = (option?: QuickAddOption | string | null): Quick }; }; -export const TagSection: React.FC = ({ +const TagSection: React.FC = ({ tags = [], onRemove, onAdd, @@ -292,7 +292,7 @@ export const TagSection: React.FC = ({ ); }; -export const CorrespondentSection: React.FC = ({ +const CorrespondentSection: React.FC = ({ entries = [], onRemove, onAdd, diff --git a/frontend/src/documents/DocumentThumbnailImage.tsx b/frontend/src/documents/DocumentThumbnailImage.tsx index b8adfcc..73649c4 100644 --- a/frontend/src/documents/DocumentThumbnailImage.tsx +++ b/frontend/src/documents/DocumentThumbnailImage.tsx @@ -187,4 +187,3 @@ const DocumentThumbnailImage = ({ }; export default DocumentThumbnailImage; -export { DEFAULT_THUMBNAIL_SIZE }; diff --git a/frontend/src/documents/DocumentsManager.ts b/frontend/src/documents/DocumentsManager.ts index 98b5924..3430962 100644 --- a/frontend/src/documents/DocumentsManager.ts +++ b/frontend/src/documents/DocumentsManager.ts @@ -1,7 +1,7 @@ import { shallowEqual } from 'react-redux'; import type { DocumentId } from '../types/identifiers'; -export type ManagedDocument = { id?: DocumentId | null } & Record; +type ManagedDocument = { id?: DocumentId | null } & Record; type FetchDocument = (id: DocumentId) => Promise; diff --git a/frontend/src/documents/SelectionAssignmentMenu.tsx b/frontend/src/documents/SelectionAssignmentMenu.tsx index bee9893..59ec9be 100644 --- a/frontend/src/documents/SelectionAssignmentMenu.tsx +++ b/frontend/src/documents/SelectionAssignmentMenu.tsx @@ -2,7 +2,7 @@ import React, { CSSProperties, useCallback, useEffect, useMemo, useRef, useState import useFloatingMenu from '../ui/useFloatingMenu'; import { CheckIcon, CircleDashedCheckIcon, PlusIcon } from '../ui/icons'; -export type AssignmentState = 'all' | 'partial' | 'none'; +type AssignmentState = 'all' | 'partial' | 'none'; export interface SelectionAssignmentMenuItem { id?: string; @@ -24,7 +24,7 @@ export interface NormalizedSelectionAssignmentItem { payload: unknown; } -export interface SelectionAssignmentMenuProps { +interface SelectionAssignmentMenuProps { label: React.ReactNode; items?: SelectionAssignmentMenuItem[]; placeholder?: string; diff --git a/frontend/src/documents/SelectionFloatingActions.tsx b/frontend/src/documents/SelectionFloatingActions.tsx index cb8d68d..d42d0c3 100644 --- a/frontend/src/documents/SelectionFloatingActions.tsx +++ b/frontend/src/documents/SelectionFloatingActions.tsx @@ -53,7 +53,7 @@ interface BulkCorrespondentRemoveArgs { documentIds: DocumentId[]; } -export interface SelectionFloatingActionsProps { +interface SelectionFloatingActionsProps { selectionCount?: number; selectedDocumentIds?: SelectedIdList; selectedFolderIds?: SelectedIdList; @@ -487,7 +487,7 @@ const SelectionFloatingActions: React.FC = ({ ); }; -export type SelectionFloatingPanelProps = Omit; +type SelectionFloatingPanelProps = Omit; export const SelectionFloatingPanel: React.FC = ({ onClearSelection, ...rest }) => { const { selectedDocumentIds, selectedFolderIds, clearSelection } = useWorkspaceSelectionContext(); @@ -512,6 +512,3 @@ export const SelectionFloatingPanel: React.FC = ({ ); }; - - -export default SelectionFloatingActions; diff --git a/frontend/src/documents/SelectionFolderMenu.tsx b/frontend/src/documents/SelectionFolderMenu.tsx index 958f5ec..d2acf6e 100644 --- a/frontend/src/documents/SelectionFolderMenu.tsx +++ b/frontend/src/documents/SelectionFolderMenu.tsx @@ -8,7 +8,7 @@ import { import type { FolderTreeNode } from '../lib/apiTypes'; import type { DocumentId } from '../types/identifiers'; -export interface SelectionFolderMenuProps { +interface SelectionFolderMenuProps { label: React.ReactNode; folderTree?: FolderTreeNode[]; onSelectFolder?: (folderId: DocumentId | null) => Promise | void; diff --git a/frontend/src/documents/components/EntryShell.tsx b/frontend/src/documents/components/EntryShell.tsx index 027c47b..d5f4cf5 100644 --- a/frontend/src/documents/components/EntryShell.tsx +++ b/frontend/src/documents/components/EntryShell.tsx @@ -1,6 +1,6 @@ import React, { type DragEvent } from 'react'; -export interface EntryShellHandlers { +interface EntryShellHandlers { onClick: (event: React.MouseEvent) => void; onDoubleClick: (event: React.MouseEvent) => void; onDragStart: (event: DragEvent) => void; diff --git a/frontend/src/documents/components/IconSizeSlider.tsx b/frontend/src/documents/components/IconSizeSlider.tsx deleted file mode 100644 index ec8af31..0000000 --- a/frontend/src/documents/components/IconSizeSlider.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; - -interface IconSizeSliderProps { - scale: number; - onChange: (scale: number) => void; - min?: number; - max?: number; -} - -const IconSizeSlider: React.FC = ({ - scale, - onChange, - min = 0.5, - max = 2, -}) => { - return ( -
- onChange(parseFloat(e.target.value))} - /> -
- ); -}; - -export default IconSizeSlider; diff --git a/frontend/src/documents/context/DocumentsFilterContext.tsx b/frontend/src/documents/context/DocumentsFilterContext.tsx index 706865e..2094360 100644 --- a/frontend/src/documents/context/DocumentsFilterContext.tsx +++ b/frontend/src/documents/context/DocumentsFilterContext.tsx @@ -35,5 +35,3 @@ export const useDocumentsFilter = (): DocumentsFilterValue => { } return context; }; - -export default DocumentsFilterContext; diff --git a/frontend/src/documents/correspondents.ts b/frontend/src/documents/correspondents.ts index b70a662..6d81ca4 100644 --- a/frontend/src/documents/correspondents.ts +++ b/frontend/src/documents/correspondents.ts @@ -1,12 +1,6 @@ -export interface CorrespondentReference { - id?: string | null; - name?: string | null; - key?: string; -} - import type { Document } from '../types/documents'; -export interface ResolvedCorrespondent { +interface ResolvedCorrespondent { id?: string | null; name: string; key: string; @@ -44,5 +38,3 @@ export const resolveCorrespondents = (doc?: Document | null): ResolvedCorrespond return results; }; - -export default resolveCorrespondents; diff --git a/frontend/src/documents/documentActions.ts b/frontend/src/documents/documentActions.ts index a36b6ee..863cb6f 100644 --- a/frontend/src/documents/documentActions.ts +++ b/frontend/src/documents/documentActions.ts @@ -6,8 +6,6 @@ import type { } from '../utils/ocr'; import type { Document } from '../types/documents'; -export type { Document }; - const asyncFalse = async () => false; const resolveDocumentDownloadHref = (document?: Document | null): string | null => { @@ -82,8 +80,3 @@ export const createDocumentActionState = ({ openOcr, }; }; - -export const documentActionsTestExports = { - resolveDocumentDownloadHref, - hasDocumentTextContentAsset, -}; diff --git a/frontend/src/documents/documentSummary.ts b/frontend/src/documents/documentSummary.ts index f9f3b8a..648c3ca 100644 --- a/frontend/src/documents/documentSummary.ts +++ b/frontend/src/documents/documentSummary.ts @@ -8,7 +8,7 @@ interface DescribeSummaryOptions { formatDateTime?: typeof defaultFormatDateTime; } -export type DocumentSummaryRowType = 'text' | 'editable-title' | 'editable-issued' | 'tags' | 'correspondents' | 'folder'; +type DocumentSummaryRowType = 'text' | 'editable-title' | 'editable-issued' | 'tags' | 'correspondents' | 'folder'; export interface DocumentSummaryRow { key: string; @@ -17,7 +17,7 @@ export interface DocumentSummaryRow { kind?: DocumentSummaryRowType; } -export type DocumentSummary = DocumentSummaryRow[]; +type DocumentSummary = DocumentSummaryRow[]; const coercePageCount = (metadata?: { page_count?: number | string | null } | null): number | null => { const raw = metadata?.page_count; diff --git a/frontend/src/documents/hooks/useDocumentsPanelProps.ts b/frontend/src/documents/hooks/useDocumentsPanelProps.ts index a05ee60..6694555 100644 --- a/frontend/src/documents/hooks/useDocumentsPanelProps.ts +++ b/frontend/src/documents/hooks/useDocumentsPanelProps.ts @@ -2,20 +2,20 @@ import { useMemo } from 'react'; import type { WorkspaceSelectionValue } from '../../app/WorkspaceSelectionContext'; import type { Identifier } from '../../types/identifiers'; -export interface Breadcrumb { +interface Breadcrumb { id?: Identifier; name?: string; label?: string; title?: string; } -export interface FolderClickHandlers { +interface FolderClickHandlers { onDrop?: (...args: unknown[]) => void; onDragOver?: (...args: unknown[]) => void; onDragLeave?: (...args: unknown[]) => void; } -export interface UseDocumentsPanelPropsArgs { +interface UseDocumentsPanelPropsArgs { currentFolderName?: string | null; breadcrumbs?: Breadcrumb[]; refreshCurrentFolder?: () => void | Promise; @@ -65,8 +65,6 @@ export interface UseDocumentsPanelPropsArgs { selectionValue: WorkspaceSelectionValue; } -export type DocumentsPanelProps = ReturnType; - const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => { const { currentFolderName, diff --git a/frontend/src/documents/panel/DocumentsToolbar.tsx b/frontend/src/documents/panel/DocumentsToolbar.tsx index bced972..45c18a6 100644 --- a/frontend/src/documents/panel/DocumentsToolbar.tsx +++ b/frontend/src/documents/panel/DocumentsToolbar.tsx @@ -51,43 +51,43 @@ export const createDocumentsTableHeaderActions = ({ const includeDescendantsToggle = isFilterActive && onToggleIncludeDescendants ? ( - - ) + + ) : null; const sortControls = onSortFieldChange ? ( -
- - {onSortDirectionToggle ? ( - - ) : null} -
- ) +
+ + {onSortDirectionToggle ? ( + + ) : null} +
+ ) : null; return ( @@ -152,5 +152,3 @@ export const createDocumentsTableHeaderActions = ({ ); }; - -export default createDocumentsTableHeaderActions; diff --git a/frontend/src/documents/tagTransfer.ts b/frontend/src/documents/tagTransfer.ts index b679466..a8e82fc 100644 --- a/frontend/src/documents/tagTransfer.ts +++ b/frontend/src/documents/tagTransfer.ts @@ -21,7 +21,7 @@ const serializePayload = (payload: TagPayload): string | null => { } }; -export const createTagTransferPayload = ( +const createTagTransferPayload = ( tag?: TagLike | null, sourceDocId: DocumentId | null = null, ): TagPayload | null => { @@ -65,7 +65,7 @@ export const writeTagTransferData = ( } }; -export const readTagTransferData = (dataTransfer?: DataTransfer | null): string | null => { +const readTagTransferData = (dataTransfer?: DataTransfer | null): string | null => { if (!dataTransfer) { return null; } @@ -133,4 +133,3 @@ export const isTagTransferEvent = (event?: DragEventLike | null): boolean => { return TAG_MIME_TYPES.some((type) => typeList.includes(type)); }; -export { TAG_MIME_TYPES }; diff --git a/frontend/src/documents/useEntryPointer.ts b/frontend/src/documents/useEntryPointer.ts index 7e9451e..df44bca 100644 --- a/frontend/src/documents/useEntryPointer.ts +++ b/frontend/src/documents/useEntryPointer.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react'; import { createDocumentEntryKey, createFolderEntryKey } from '../app/entryKey'; -export type PointerEventLike = MouseEvent | PointerEvent; +type PointerEventLike = MouseEvent | PointerEvent; export const isPointerModifierEvent = (event?: PointerEventLike | null): boolean => Boolean(event && (event.shiftKey || event.metaKey || event.ctrlKey || event.altKey)); @@ -17,9 +17,9 @@ export const isPrimaryPointerEvent = (event?: PointerEventLike | null): boolean return type === 'click' || type === 'pointerdown' || type === 'pointerup'; }; -export type EntryType = 'document' | 'folder'; +type EntryType = 'document' | 'folder'; -export interface WorkspaceEntry { +interface WorkspaceEntry { id: string; key?: string; type: EntryType; @@ -31,7 +31,7 @@ interface UseEntryPointerOptions { onDocumentActivate?: (id: string, metadata?: EntryPointerMetadata) => void; } -export interface EntryPointerMetadata { +interface EntryPointerMetadata { modifierClick: boolean; primaryClick: boolean; rowKey: string; @@ -72,5 +72,3 @@ export const useEntryPointer = ({ }, [onSelectEntry, onDocumentActivate], ); - -export default useEntryPointer; diff --git a/frontend/src/folders/FolderManagerContext.tsx b/frontend/src/folders/FolderManagerContext.tsx index e258b7c..fe383ff 100644 --- a/frontend/src/folders/FolderManagerContext.tsx +++ b/frontend/src/folders/FolderManagerContext.tsx @@ -3,7 +3,7 @@ import { DEFAULT_FOLDER_NAME } from '../app/workspaceUtils'; type FolderId = string | null; -export interface FolderManager { +interface FolderManager { getNameSync: (folderId: FolderId) => string | null; resolveName: (folderId: FolderId) => Promise; } @@ -55,5 +55,3 @@ export const FolderManagerProvider: React.FC = ({ }; export const useFolderManager = (): FolderManager => useContext(FolderManagerContext); - -export default FolderManagerContext; diff --git a/frontend/src/hooks/documents/useDocumentsWorkspace.ts b/frontend/src/hooks/documents/useDocumentsWorkspace.ts index 8c1ce1e..4f6f657 100644 --- a/frontend/src/hooks/documents/useDocumentsWorkspace.ts +++ b/frontend/src/hooks/documents/useDocumentsWorkspace.ts @@ -20,7 +20,7 @@ import { useManagementModals } from '../../app/useManagementModals'; import { useAppDispatch, useAppState } from '../../app/appState'; import { fetchAsset } from '../../lib/apiClient'; import { useApi } from '../../app/ApiContext'; -import useWorkspaceSelection from '../../app/useWorkspaceSelection'; +import { useWorkspaceSelection } from '../../app/useWorkspaceSelection'; import { useEntryPointer as useEntryPointerCore } from '../../documents/useEntryPointer'; import { isTagTransferEvent } from '../../documents/tagTransfer'; import useDocumentsSelection from '../../documents/hooks/useDocumentsSelection'; diff --git a/frontend/src/lib/apiClient.ts b/frontend/src/lib/apiClient.ts index 672e718..85d66e5 100644 --- a/frontend/src/lib/apiClient.ts +++ b/frontend/src/lib/apiClient.ts @@ -342,15 +342,4 @@ api.interceptors.response.use( }, ); -export type { - DownloadLink, - DocumentResponse, - AssetResponse, - FolderTreeNode, - CapabilitySetResponse, - CapabilityResponse, - ApiTokenRecord, - PasskeySummary, - Identifier, - TenantSnippet, -} from './apiTypes'; +export type { ApiTokenRecord } from './apiTypes'; diff --git a/frontend/src/lib/apiTypes.ts b/frontend/src/lib/apiTypes.ts index 1abb117..0c6d621 100644 --- a/frontend/src/lib/apiTypes.ts +++ b/frontend/src/lib/apiTypes.ts @@ -14,7 +14,7 @@ export interface TagResponse { color?: string | null; } -export interface CorrespondentResponse { +interface CorrespondentResponse { id: string; name: string; metadata: Record; @@ -29,7 +29,7 @@ export interface AssetResponse { [key: string]: unknown; } -export interface DocumentVersionResponse { +interface DocumentVersionResponse { id: string; version_number: number; size_bytes: number; @@ -57,7 +57,7 @@ export interface DocumentResponse { current_version?: DocumentVersionResponse | null; } -export interface FolderInfo { +interface FolderInfo { id: string; name: string; parent_id?: string | null; diff --git a/frontend/src/preview/DocumentViewerPanel.tsx b/frontend/src/preview/DocumentViewerPanel.tsx index 7d659b5..010e61d 100644 --- a/frontend/src/preview/DocumentViewerPanel.tsx +++ b/frontend/src/preview/DocumentViewerPanel.tsx @@ -23,7 +23,7 @@ import { resolveDocumentAssetUrl } from '../asset_manager'; import PanelHeader from '../ui/PanelHeader'; import BreadcrumbTrail from '../ui/BreadcrumbTrail'; import DocumentViewerLayout from './DocumentViewerLayout'; -import useViewerLayoutMode from './useViewerLayoutMode'; +import { useViewerLayoutMode } from './useViewerLayoutMode'; import { usePanelResizeBindings } from '../app/PanelManagerContext'; import type { DocumentId, FolderId } from '../types/identifiers'; import type { Document } from '../types/documents'; @@ -46,7 +46,7 @@ interface DocumentViewerPanelProps extends DocumentSummarySectionProps { sidebarMode?: SidebarMode; } -export const createDocumentViewerHeaderActions = ({ +const createDocumentViewerHeaderActions = ({ document, actionState, onZoom, diff --git a/frontend/src/preview/MediaViewer.tsx b/frontend/src/preview/MediaViewer.tsx index e6f288f..305f525 100644 --- a/frontend/src/preview/MediaViewer.tsx +++ b/frontend/src/preview/MediaViewer.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { DownloadIcon } from '../ui/icons'; import { AUDIO_EXTENSIONS, VIDEO_EXTENSIONS } from '../constants/preview'; -export interface MediaViewerProps { +interface MediaViewerProps { src: string; mimeType?: string; filename?: string; diff --git a/frontend/src/preview/useViewerLayoutMode.ts b/frontend/src/preview/useViewerLayoutMode.ts index c16ee60..b98c74c 100644 --- a/frontend/src/preview/useViewerLayoutMode.ts +++ b/frontend/src/preview/useViewerLayoutMode.ts @@ -5,8 +5,6 @@ import { PORTRAIT_RATIO_STYLE_ID, } from '../constants/preview'; -export { DOCUMENT_VIEWER_PORTRAIT_HEIGHT_RATIO }; - const ensurePortraitRatioStyle = () => { const cssValue = String(DOCUMENT_VIEWER_PORTRAIT_HEIGHT_RATIO); const cssText = `:root { --document-viewer-portrait-height-ratio: ${cssValue}; }`; @@ -78,5 +76,3 @@ export const useViewerLayoutMode = ( return isStacked; }; - -export default useViewerLayoutMode; diff --git a/frontend/src/routes/DocumentViewerRoute.tsx b/frontend/src/routes/DocumentViewerRoute.tsx deleted file mode 100644 index 4e6540e..0000000 --- a/frontend/src/routes/DocumentViewerRoute.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React, { useEffect } from 'react'; -import { Navigate, useNavigate, useParams } from 'react-router-dom'; - -import { useAppShell } from '../appShellContext'; -import type { Identifier } from '../types/identifiers'; - -interface DocumentViewerRouteContext { - previewWorkspaceDocument?: { id?: Identifier } | null; - ensurePreviewData?: (documentId: Identifier, options?: Record) => Promise; - notifyApiError?: (error: unknown, message?: string) => void; -} - -const DocumentViewerRoute: React.FC = () => { - const { - previewWorkspaceDocument, - ensurePreviewData, - notifyApiError, - } = useAppShell() as DocumentViewerRouteContext; - const { documentId } = useParams(); - const navigate = useNavigate(); - - useEffect(() => { - if (!documentId) { - navigate('/documents', { replace: true }); - return; - } - - let cancelled = false; - - const hydrate = async () => { - try { - await ensurePreviewData?.(documentId); - } catch (error) { - if (cancelled) { - return; - } - notifyApiError?.(error, 'Failed to open document preview.'); - navigate('/documents', { replace: true }); - } - }; - - hydrate(); - - return () => { - cancelled = true; - }; - }, [documentId, ensurePreviewData, notifyApiError, navigate]); - - if (!documentId) { - return ; - } - - const previewId = previewWorkspaceDocument?.id; - if (!previewWorkspaceDocument || String(previewId) !== String(documentId)) { - return
Loading preview…
; - } - - return ; -}; - -export default DocumentViewerRoute; diff --git a/frontend/src/settings/sections/index.ts b/frontend/src/settings/sections/index.ts index 6fe29ce..70f1e17 100644 --- a/frontend/src/settings/sections/index.ts +++ b/frontend/src/settings/sections/index.ts @@ -1,12 +1,5 @@ -import PasskeysSection from './PasskeysSection'; -import ApiTokensSection from './ApiTokensSection'; -import CapabilitySetsSection from './CapabilitySetsSection'; import { - API_TOKENS_SECTION, - CAPABILITY_SETS_SECTION, DEFAULT_SETTINGS_SECTIONS, - PASSKEYS_SECTION, } from '../../constants/settings'; -export { PasskeysSection, ApiTokensSection, CapabilitySetsSection }; -export { PASSKEYS_SECTION, API_TOKENS_SECTION, CAPABILITY_SETS_SECTION, DEFAULT_SETTINGS_SECTIONS }; +export { DEFAULT_SETTINGS_SECTIONS }; diff --git a/frontend/src/settings/usePasskeys.ts b/frontend/src/settings/usePasskeys.ts index 058178d..527eb8c 100644 --- a/frontend/src/settings/usePasskeys.ts +++ b/frontend/src/settings/usePasskeys.ts @@ -59,13 +59,13 @@ interface PasskeyRegisterPayload { nickname?: string; } -export type RegisterPasskeyFailureReason = 'unsupported' | 'busy' | 'cancelled' | 'error'; +type RegisterPasskeyFailureReason = 'unsupported' | 'busy' | 'cancelled' | 'error'; export type RegisterPasskeyResult = | { ok: true } | { ok: false; reason: RegisterPasskeyFailureReason; message?: string }; -export type RevokePasskeyFailureReason = 'missing-id' | 'error'; -export type RevokePasskeyResult = +type RevokePasskeyFailureReason = 'missing-id' | 'error'; +type RevokePasskeyResult = | { ok: true } | { ok: false; reason: RevokePasskeyFailureReason; message?: string }; diff --git a/frontend/src/sidebar/SidebarContext.tsx b/frontend/src/sidebar/SidebarContext.tsx index f15953b..504502b 100644 --- a/frontend/src/sidebar/SidebarContext.tsx +++ b/frontend/src/sidebar/SidebarContext.tsx @@ -295,12 +295,3 @@ export const useSidebarContext = () => { } return context; }; - -export const useSidebarControls = () => { - const { setCollapsed } = useSidebarContext(); - const openSidebar = useCallback(() => setCollapsed(false), [setCollapsed]); - const closeSidebar = useCallback(() => setCollapsed(true), [setCollapsed]); - return { openSidebar, closeSidebar }; -}; - -export default SidebarContext; diff --git a/frontend/src/sidebar/components/SidebarFolderNode.tsx b/frontend/src/sidebar/components/SidebarFolderNode.tsx index c3eb9b4..125fcbf 100644 --- a/frontend/src/sidebar/components/SidebarFolderNode.tsx +++ b/frontend/src/sidebar/components/SidebarFolderNode.tsx @@ -20,7 +20,7 @@ export interface FolderTreeNode { expanded?: boolean; } -export interface FolderNodeProps { +interface FolderNodeProps { node: FolderTreeNode; depth: number; isSelected: boolean; diff --git a/frontend/src/types/documents.ts b/frontend/src/types/documents.ts index a49f0bd..0905ce3 100644 --- a/frontend/src/types/documents.ts +++ b/frontend/src/types/documents.ts @@ -57,14 +57,14 @@ export interface Folder { name?: string; } -export type FolderEntry = { +type FolderEntry = { type: 'folder'; id: Identifier | 'root'; key: string; folder: Folder; }; -export type DocumentEntry = { +type DocumentEntry = { type: 'document'; id: Identifier; key: string; @@ -77,4 +77,3 @@ export type DocumentsListEntry = FolderEntry | DocumentEntry; import type { MouseEvent } from 'react'; export type FolderEventHandler = (folder: Folder, event: MouseEvent) => void; -export type DocumentEventHandler = (document: Document, event: MouseEvent) => void; diff --git a/frontend/src/ui/PanelHeader.tsx b/frontend/src/ui/PanelHeader.tsx index 25647cc..ebda6a8 100644 --- a/frontend/src/ui/PanelHeader.tsx +++ b/frontend/src/ui/PanelHeader.tsx @@ -1,5 +1,5 @@ import React, { ElementType, ReactNode } from 'react'; -import composeClassName from './classNames'; +import { composeClassName } from './classNames'; type TitleProps = Record; diff --git a/frontend/src/ui/QuickAddMenu.tsx b/frontend/src/ui/QuickAddMenu.tsx index e993557..a3707fe 100644 --- a/frontend/src/ui/QuickAddMenu.tsx +++ b/frontend/src/ui/QuickAddMenu.tsx @@ -49,7 +49,7 @@ interface FloatingMenuState { type CSSVarStyle = CSSProperties & Record; -export interface QuickAddMenuProps { +interface QuickAddMenuProps { onSelectOption?: (value: QuickAddOption, normalized: NormalizedOption) => Promise | void; onCreate?: (value: string) => Promise | void; options?: QuickAddOption[]; diff --git a/frontend/src/ui/classNames.ts b/frontend/src/ui/classNames.ts index b5119fc..a95a0cd 100644 --- a/frontend/src/ui/classNames.ts +++ b/frontend/src/ui/classNames.ts @@ -1,4 +1,2 @@ export const composeClassName = (base: string, extra?: string | null): string => extra ? `${base} ${extra}` : base; - -export default composeClassName; diff --git a/frontend/src/ui/icons.tsx b/frontend/src/ui/icons.tsx index 6654732..7dda564 100644 --- a/frontend/src/ui/icons.tsx +++ b/frontend/src/ui/icons.tsx @@ -12,15 +12,11 @@ import { IconLayoutList, IconLayoutGrid, IconArrowLeft, - IconArrowRight, - IconArrowUp, IconAnalyze, IconUpload, IconWindowMaximize, - IconTextScan2, IconFolderPlus, IconFolder, - IconFolderUp, IconFolders, IconFoldersOff, IconRefresh, @@ -37,10 +33,8 @@ import { IconDeviceLaptop, IconLayoutSidebarLeftCollapse, IconLayoutSidebarLeftExpand, - IconLayoutSidebarRightCollapse, IconLayoutBottombarCollapse, IconLayoutBottombarExpand, - IconClearAll, IconInfoCircle, IconCircleDashedCheck, IconFile, @@ -56,7 +50,7 @@ import { import FolderSvg from '../assets/folder.svg'; const logoWebp = new URL('../assets/logo.webp', import.meta.url).toString(); const logoSmallWebp = new URL('../assets/logo_small.webp', import.meta.url).toString(); -import composeClassName from './classNames'; +import { composeClassName } from './classNames'; type TablerIconComponent = (props: TablerIconProps) => JSX.Element; @@ -223,16 +217,6 @@ export const ArrowLeftIcon: TablerIconComponent = ({ className, size = '1em', st {...rest} /> ); - -export const ArrowRightIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( - -); - export const SidebarCollapseIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( ); - -export const DetailPanelCollapseIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( - -); - export const BottombarCollapseIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( ( - -); - export const FolderPlusIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( ); - -export const ArrowUpIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( - -); - export const MinusVerticalIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( ); - -export const FolderUpIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( - -); - export const FolderMoveIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( ); - -export default { - ChevronIcon, - TrashIcon, - EditIcon, - FolderIcon, - TagIcon, - CorrespondentIcon, - DownloadIcon, - ViewListIcon, - ViewGridIcon, - SidebarCollapseIcon, - SidebarExpandIcon, - DetailPanelCollapseIcon, - AnalyzeIcon, - LoaderIcon, - WindowMaximizeIcon, - FolderPlusIcon, - RefreshIcon, - ArrowUpIcon, - MinusVerticalIcon, - LogoutIcon, - ChevronDownIcon, - FolderUpIcon, - FolderMoveIcon, -}; - -export const TextScanIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( - -); diff --git a/frontend/src/ui/usePointerTap.ts b/frontend/src/ui/usePointerTap.ts deleted file mode 100644 index 211ac15..0000000 --- a/frontend/src/ui/usePointerTap.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { useCallback, useEffect, useRef } from 'react'; -import type { PointerEvent as ReactPointerEvent } from 'react'; - -export interface PointerTapContext { - clientX: number; - clientY: number; - pointerType: string; - event: PointerEventWithPersist; - data?: T; -} - -type PointerEventWithPersist = (PointerEvent | ReactPointerEvent) & { persist?: () => void }; - -type PointerTapFilter = (event?: PointerEventWithPersist | null) => boolean; - -export interface UsePointerTapOptions { - onSingle?: (context: PointerTapContext) => void; - onDouble?: (context: PointerTapContext) => void; - delay?: number; - filter?: PointerTapFilter; -} - -const defaultFilter: PointerTapFilter = (event) => { - if (!event) { - return false; - } - const { type, button, pointerType, isPrimary } = event; - const isPointerUp = type === 'pointerup'; - const buttonValid = - button == null || button === 0 || (isPointerUp && (button === -1 || button === 0)); - if (!buttonValid) { - return false; - } - if (pointerType === 'touch' && isPrimary === false) { - return false; - } - return true; -}; - -const usePointerTap = ({ - onSingle, - onDouble, - delay = 240, - filter = defaultFilter, -}: UsePointerTapOptions = {}) => { - const timerRef = useRef | null>(null); - - useEffect( - () => () => { - if (timerRef.current) { - clearTimeout(timerRef.current); - timerRef.current = null; - } - }, - [], - ); - - return useCallback( - (event?: PointerEventWithPersist | null, metadata?: T) => { - if (!filter(event)) { - return; - } - - if (!event) { - return; - } - - event.persist?.(); - - const context: PointerTapContext = { - clientX: event.clientX ?? 0, - clientY: event.clientY ?? 0, - pointerType: event.pointerType ?? 'mouse', - event, - data: metadata, - }; - - if (timerRef.current) { - clearTimeout(timerRef.current); - timerRef.current = null; - onDouble?.(context); - return; - } - - timerRef.current = setTimeout(() => { - timerRef.current = null; - onSingle?.(context); - }, delay); - }, - [delay, filter, onDouble, onSingle], - ); -}; - -export default usePointerTap; diff --git a/frontend/src/utils/colors.ts b/frontend/src/utils/colors.ts index de6eb18..f5cc949 100644 --- a/frontend/src/utils/colors.ts +++ b/frontend/src/utils/colors.ts @@ -93,7 +93,7 @@ const srgbChannelToLinear = (value: number) => { return ((normalized + 0.055) / 1.055) ** 2.4; }; -export const hexToRgb = (input?: string): (RgbColor & { hex: string }) | null => { +const hexToRgb = (input?: string): (RgbColor & { hex: string }) | null => { if (!input) return null; const match = HEX_COLOR_PATTERN.exec(input.trim()); if (!match) return null; @@ -163,7 +163,7 @@ const buildTagStyle = (backgroundHex: string) => { }; }; -export const relativeLuminance = ({ r, g, b }: RgbColor): number => { +const relativeLuminance = ({ r, g, b }: RgbColor): number => { const toLinear = (channel) => { const normalized = channel / 255; return normalized <= 0.03928 @@ -198,7 +198,7 @@ const contrastForPair = (backgroundHex: string, textHex: string): number => { return contrastRatio(relativeLuminance(background), relativeLuminance(text)); }; -export const getReadableTextColor = ( +const getReadableTextColor = ( hex: string, { light = '#1f1f1f', dark = '#ffffff', fallback = '#1f1f1f' }: { light?: string; dark?: string; fallback?: string } = {}, ): string => { diff --git a/frontend/src/utils/date.ts b/frontend/src/utils/date.ts index ae09654..62ae8c3 100644 --- a/frontend/src/utils/date.ts +++ b/frontend/src/utils/date.ts @@ -51,13 +51,3 @@ export const toIssuedTimestamp = (dateString: string | null, fallback: string | candidate.setUTCFullYear(year, month - 1, day); return Number.isNaN(candidate.getTime()) ? null : candidate.toISOString(); }; - -export const parseDateValue = (value: string | Date | null): Date | null => ensureDate(value); - -export default { - formatDate, - formatDateTime, - toDateInputValue, - toIssuedTimestamp, - parseDateValue, -}; diff --git a/frontend/src/utils/math.ts b/frontend/src/utils/math.ts index 5432b90..da25cc9 100644 --- a/frontend/src/utils/math.ts +++ b/frontend/src/utils/math.ts @@ -7,19 +7,3 @@ export const clamp = (value: number, min: number, max: number): number => { } return value; }; - -export const formatTransform = ( - x: number, - y: number, - rotation = 0, - scale = 1, -): string => `translate3d(${x}px, ${y}px, 0) rotate(${rotation}deg) scale(${scale})`; - -export const toNumber = (v: unknown, fallback = 0): number => - Number.isFinite(Number(v)) ? Number(v) : fallback; - -export default { - clamp, - formatTransform, - toNumber, -}; diff --git a/frontend/src/utils/ocr.ts b/frontend/src/utils/ocr.ts index 89aaa64..fc24fa1 100644 --- a/frontend/src/utils/ocr.ts +++ b/frontend/src/utils/ocr.ts @@ -4,12 +4,9 @@ import type { } from '../asset_manager'; import type { Document, - DocumentVersion, } from '../types/documents'; import type { Asset } from '../types/assets'; -export type { Document, DocumentVersion, Asset }; - export type EnsurePreviewData = (id: string) => Promise; export type EnsureAssetUrl = ( id: string, @@ -32,7 +29,7 @@ const pickAsset = (doc?: Document | null, getDocumentAsset?: GetDocumentAsset): return getDocumentAsset(doc, 'text-content') || null; }; -export async function resolveTextContentUrl({ +async function resolveTextContentUrl({ document, ensurePreviewData, getDocumentAsset, diff --git a/frontend/src/utils/webauthn.ts b/frontend/src/utils/webauthn.ts index f2aba51..2576ed9 100644 --- a/frontend/src/utils/webauthn.ts +++ b/frontend/src/utils/webauthn.ts @@ -43,7 +43,7 @@ const decodeBase64 = (value: string): string => window.atob(value); const encodeBase64 = (binary: string): string => window.btoa(binary); -export const base64urlToUint8Array = (value?: string | null): Uint8Array => { +const base64urlToUint8Array = (value?: string | null): Uint8Array => { const base64 = base64urlToBase64(value || ''); const binary = decodeBase64(base64); const bytes = new Uint8Array(binary.length); @@ -77,7 +77,7 @@ const toUint8Array = (input?: ArrayBuffer | ArrayBufferView | ArrayLike return Uint8Array.from(input as ArrayLike) as Uint8Array; }; -export const arrayBufferToBase64url = ( +const arrayBufferToBase64url = ( buffer?: ArrayBuffer | ArrayBufferView | ArrayLike | null, ): string => { const bytes = toUint8Array(buffer); diff --git a/frontend/tests/ts-register.js b/frontend/tests/ts-register.js deleted file mode 100644 index 81ae86f..0000000 --- a/frontend/tests/ts-register.js +++ /dev/null @@ -1,22 +0,0 @@ -const { readFileSync } = require('node:fs'); -const ts = require('typescript'); - -const compilerOptions = { - module: ts.ModuleKind.CommonJS, - target: ts.ScriptTarget.ES2022, - jsx: ts.JsxEmit.ReactJSX, - esModuleInterop: true, -}; - -const compileTs = (module, filename) => { - const source = readFileSync(filename, 'utf8'); - const { outputText } = ts.transpileModule(source, { - compilerOptions, - fileName: filename, - reportDiagnostics: false, - }); - module._compile(outputText, filename); -}; - -require.extensions['.ts'] = compileTs; -require.extensions['.tsx'] = compileTs; diff --git a/frontend/tests/workspaceEngine.test.js b/frontend/tests/workspaceEngine.test.js deleted file mode 100644 index 4732e75..0000000 --- a/frontend/tests/workspaceEngine.test.js +++ /dev/null @@ -1,72 +0,0 @@ -const test = require('node:test'); -const assert = require('node:assert/strict'); -require('./ts-register'); - -const { - WorkspaceEngine, - DESK_CANVAS_PADDING, -} = require('../src/desktop/workspaceEngine.ts'); - -const makeEngine = () => { - const engine = new WorkspaceEngine(); - engine.setEnsureDocumentSize(() => ({ width: 200, height: 200 })); - engine.setCanvasSize({ width: 1200, height: 800 }); - return engine; -}; - -test('syncLayoutSnapshot clones from layout map', () => { - const engine = makeEngine(); - engine.setItems([{ id: 'doc-1' }]); - engine.ensureLayoutForItems(); - engine.syncLayoutSnapshot(); - - const firstSnapshot = engine.getSnapshot(); - assert(firstSnapshot.layout instanceof Map); - assert(firstSnapshot.layout.get('doc-1')); - - engine.layout.set('doc-1', { - centerX: DESK_CANVAS_PADDING + 150, - centerY: DESK_CANVAS_PADDING + 150, - rotation: 0, - width: 200, - height: 200, - }); - engine.syncLayoutSnapshot(); - - const secondSnapshot = engine.getSnapshot(); - assert.notStrictEqual(secondSnapshot.layout, engine.layout); - assert.equal(secondSnapshot.layout.get('doc-1').centerX, engine.layout.get('doc-1').centerX); -}); - -test('recalcVisibleDocIds respects viewport bounds', () => { - const engine = makeEngine(); - engine.items = [{ id: 'visible' }, { id: 'hidden' }]; - engine.setDocumentLookup(new Map([ - ['visible', { id: 'visible' }], - ['hidden', { id: 'hidden' }], - ])); - - engine.layout = new Map([ - ['visible', { - centerX: DESK_CANVAS_PADDING + 150, - centerY: DESK_CANVAS_PADDING + 150, - rotation: 0, - width: 200, - height: 200, - }], - ['hidden', { - centerX: -500, - centerY: -500, - rotation: 0, - width: 200, - height: 200, - }], - ]); - engine.layoutSnapshot = new Map(engine.layout); - - engine.recalcVisibleDocIds(); - - const snapshot = engine.getSnapshot(); - assert(snapshot.visibleDocIds.has('visible')); - assert(!snapshot.visibleDocIds.has('hidden')); -});