feat: Refactor utility and component exports, update dependencies, and add Knip for unused code detection.

This commit is contained in:
2025-12-04 11:58:04 +01:00
parent c3375b714d
commit 2ea74816fb
65 changed files with 624 additions and 707 deletions
+516 -22
View File
@@ -12,6 +12,7 @@
"@tabler/icons-react": "^3.35.0", "@tabler/icons-react": "^3.35.0",
"axios": "^1.13.2", "axios": "^1.13.2",
"pdfjs-dist": "^5.4.394", "pdfjs-dist": "^5.4.394",
"prop-types": "^15.8.1",
"react": "^19.2.0", "react": "^19.2.0",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
@@ -22,18 +23,19 @@
"@babel/preset-env": "^7.28.5", "@babel/preset-env": "^7.28.5",
"@babel/preset-react": "^7.28.5", "@babel/preset-react": "^7.28.5",
"@babel/preset-typescript": "^7.28.5", "@babel/preset-typescript": "^7.28.5",
"@eslint/js": "^9.39.1",
"@svgr/webpack": "^8.1.0", "@svgr/webpack": "^8.1.0",
"@typescript-eslint/eslint-plugin": "^8.46.4", "@typescript-eslint/eslint-plugin": "^8.46.4",
"@typescript-eslint/parser": "^8.18.1", "@typescript-eslint/parser": "^8.18.1",
"babel-loader": "^10.0.0", "babel-loader": "^10.0.0",
"copy-webpack-plugin": "^13.0.1", "copy-webpack-plugin": "^13.0.1",
"css-loader": "^7.1.2", "css-loader": "^7.1.2",
"dotenv": "^17.2.3",
"eslint": "^9.39.1", "eslint": "^9.39.1",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"globals": "^16.5.0", "globals": "^16.5.0",
"html-webpack-plugin": "^5.6.4", "html-webpack-plugin": "^5.6.4",
"knip": "^5.71.0",
"style-loader": "^4.0.0", "style-loader": "^4.0.0",
"typescript": "^5.7.3", "typescript": "^5.7.3",
"webpack": "^5.102.1", "webpack": "^5.102.1",
@@ -1868,6 +1870,40 @@
"node": ">=14.17.0" "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": { "node_modules/@eslint-community/eslint-utils": {
"version": "4.9.0", "version": "4.9.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
@@ -2495,6 +2531,19 @@
"node": ">= 10" "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": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -2533,6 +2582,289 @@
"node": ">= 8" "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": { "node_modules/@svgr/babel-plugin-add-jsx-attribute": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", "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": ">=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": { "node_modules/@types/body-parser": {
"version": "1.19.6", "version": "1.19.6",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
@@ -5237,19 +5580,6 @@
"tslib": "^2.0.3" "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": { "node_modules/dunder-proto": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -6138,6 +6468,16 @@
"node": ">=0.8.0" "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": { "node_modules/file-entry-cache": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -6280,6 +6620,22 @@
"node": ">= 6" "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": { "node_modules/forwarded": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -7544,17 +7900,26 @@
"node": ">= 10.13.0" "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": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/js-yaml": { "node_modules/js-yaml": {
"version": "4.1.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -7654,6 +8019,74 @@
"node": ">=0.10.0" "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": { "node_modules/launch-editor": {
"version": "2.11.1", "version": "2.11.1",
"resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.11.1.tgz", "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.11.1.tgz",
@@ -7738,7 +8171,6 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0" "js-tokens": "^3.0.0 || ^4.0.0"
@@ -7917,6 +8349,16 @@
"node": "*" "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": { "node_modules/ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -8036,7 +8478,6 @@
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@@ -8225,6 +8666,38 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/p-limit": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
@@ -8590,7 +9063,6 @@
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
@@ -8736,7 +9208,6 @@
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/react-redux": { "node_modules/react-redux": {
@@ -9613,6 +10084,19 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/snake-case": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
@@ -10572,6 +11056,16 @@
"node": ">= 0.8" "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": { "node_modules/watchpack": {
"version": "2.4.4", "version": "2.4.4",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
+4 -2
View File
@@ -7,7 +7,7 @@
"dev": "webpack serve --mode development --open", "dev": "webpack serve --mode development --open",
"build": "webpack --mode production", "build": "webpack --mode production",
"lint": "eslint src --ext .js,.jsx,.ts,.tsx", "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" "test:engine": "node --test tests/workspaceEngine.test.js"
}, },
"dependencies": { "dependencies": {
@@ -15,6 +15,7 @@
"@tabler/icons-react": "^3.35.0", "@tabler/icons-react": "^3.35.0",
"axios": "^1.13.2", "axios": "^1.13.2",
"pdfjs-dist": "^5.4.394", "pdfjs-dist": "^5.4.394",
"prop-types": "^15.8.1",
"react": "^19.2.0", "react": "^19.2.0",
"react-dom": "^19.2.0", "react-dom": "^19.2.0",
"react-redux": "^9.2.0", "react-redux": "^9.2.0",
@@ -25,18 +26,19 @@
"@babel/preset-env": "^7.28.5", "@babel/preset-env": "^7.28.5",
"@babel/preset-react": "^7.28.5", "@babel/preset-react": "^7.28.5",
"@babel/preset-typescript": "^7.28.5", "@babel/preset-typescript": "^7.28.5",
"@eslint/js": "^9.39.1",
"@svgr/webpack": "^8.1.0", "@svgr/webpack": "^8.1.0",
"@typescript-eslint/eslint-plugin": "^8.46.4", "@typescript-eslint/eslint-plugin": "^8.46.4",
"@typescript-eslint/parser": "^8.18.1", "@typescript-eslint/parser": "^8.18.1",
"babel-loader": "^10.0.0", "babel-loader": "^10.0.0",
"copy-webpack-plugin": "^13.0.1", "copy-webpack-plugin": "^13.0.1",
"css-loader": "^7.1.2", "css-loader": "^7.1.2",
"dotenv": "^17.2.3",
"eslint": "^9.39.1", "eslint": "^9.39.1",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"globals": "^16.5.0", "globals": "^16.5.0",
"html-webpack-plugin": "^5.6.4", "html-webpack-plugin": "^5.6.4",
"knip": "^5.71.0",
"style-loader": "^4.0.0", "style-loader": "^4.0.0",
"typescript": "^5.7.3", "typescript": "^5.7.3",
"webpack": "^5.102.1", "webpack": "^5.102.1",
-2
View File
@@ -41,5 +41,3 @@ export const useApi = (): ApiContextValue => {
} }
return ctx; return ctx;
}; };
export const getHttpClient = (): HttpClient => httpClient;
-2
View File
@@ -421,5 +421,3 @@ export const usePanelResizeBindings = (
isPanelResizing: resizingPanel === panel, isPanelResizing: resizingPanel === panel,
}; };
}; };
export default PanelManagerContext;
@@ -1,5 +1,5 @@
import React, { createContext, useContext } from 'react'; import React, { createContext, useContext } from 'react';
import type useWorkspaceSelection from './useWorkspaceSelection'; import type { useWorkspaceSelection } from './useWorkspaceSelection';
export type WorkspaceSelectionValue = ReturnType<typeof useWorkspaceSelection>; export type WorkspaceSelectionValue = ReturnType<typeof useWorkspaceSelection>;
@@ -23,5 +23,3 @@ export const useWorkspaceSelectionContext = () => {
} }
return context; return context;
}; };
export default WorkspaceSelectionContext;
+1 -3
View File
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
export interface DetailDocument { interface DetailDocument {
id?: string; id?: string;
[key: string]: unknown; [key: string]: unknown;
} }
@@ -102,5 +102,3 @@ export const useDetailPanel = ({
setDetailPanelOpen, setDetailPanelOpen,
}; };
}; };
export default useDetailPanel;
-2
View File
@@ -285,5 +285,3 @@ export const useDocumentSelection = ({
configureSelectionEnvironment, configureSelectionEnvironment,
}; };
}; };
export default useDocumentSelection;
-2
View File
@@ -231,5 +231,3 @@ export const useManagementModals = ({
activeModal, activeModal,
}; };
}; };
export default useManagementModals;
@@ -115,5 +115,3 @@ export const useWorkspaceSelection = ({
inspectFolder, inspectFolder,
}; };
}; };
export default useWorkspaceSelection;
+8 -8
View File
@@ -7,17 +7,17 @@ import { usePanelManager } from './PanelManagerContext';
import { FolderManagerProvider } from '../folders/FolderManagerContext'; import { FolderManagerProvider } from '../folders/FolderManagerContext';
import type { Identifier } from '../types/identifiers'; import type { Identifier } from '../types/identifiers';
export type EnsureAssetUrl = ( type EnsureAssetUrl = (
docId: Identifier, docId: Identifier,
asset: unknown, asset: unknown,
options?: Record<string, unknown>, options?: Record<string, unknown>,
) => Promise<unknown> | void; ) => Promise<unknown> | void;
export type EnsurePreviewData = (docId: Identifier, options?: Record<string, unknown>) => Promise<unknown>; type EnsurePreviewData = (docId: Identifier, options?: Record<string, unknown>) => Promise<unknown>;
export type GetDocumentAsset = (document: unknown, assetType: string) => unknown; type GetDocumentAsset = (document: unknown, assetType: string) => unknown;
export type NotifyApiError = (error: unknown, fallbackMessage?: string) => void; type NotifyApiError = (error: unknown, fallbackMessage?: string) => void;
export type DocumentsTableProps = ComponentProps<typeof DocumentsPanel> | null; type DocumentsTableProps = ComponentProps<typeof DocumentsPanel> | null;
export type DetailPanelProps = (ComponentProps<typeof DocumentViewerPanel> & { type DetailPanelProps = (ComponentProps<typeof DocumentViewerPanel> & {
onClose?: () => void; onClose?: () => void;
onOpenPreview?: (args: { documentIds: Array<string> }) => void; onOpenPreview?: (args: { documentIds: Array<string> }) => void;
folderNodes?: Map<Identifier | 'root', unknown>; folderNodes?: Map<Identifier | 'root', unknown>;
@@ -27,7 +27,7 @@ export type DetailPanelProps = (ComponentProps<typeof DocumentViewerPanel> & {
) => Promise<void>; ) => Promise<void>;
}) | null; }) | 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 { export interface UseWorkspaceSurfaceArgs {
sidebarHidden?: boolean; sidebarHidden?: boolean;
@@ -44,7 +44,7 @@ export interface UseWorkspaceSurfaceArgs {
closeDocumentPreview?: () => void; closeDocumentPreview?: () => void;
} }
export interface UseWorkspaceSurfaceResult { interface UseWorkspaceSurfaceResult {
surface: WorkspaceSurface; surface: WorkspaceSurface;
} }
+1 -1
View File
@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
export type AppShellContextValue = Record<string, unknown>; type AppShellContextValue = Record<string, unknown>;
export const AppShellContext = React.createContext<AppShellContextValue | null>(null); export const AppShellContext = React.createContext<AppShellContextValue | null>(null);
+3 -3
View File
@@ -4,9 +4,9 @@ import type { DocumentVersion, Document } from './types/documents';
type Nullable<T> = T | null; type Nullable<T> = 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; asset?.download?.expires_at ?? null;
export const resolveAssetUrl = (asset?: { download?: { url: string } | null } | null): string | 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<Asset>; export type GetAsset = (document: Document, assetType: string) => Nullable<Asset>;
export const getAssetFromGroup = ( const getAssetFromGroup = (
assets?: Asset[] | Record<string, Asset> | null, assets?: Asset[] | Record<string, Asset> | null,
assetType: string = '', assetType: string = '',
): Nullable<Asset> => { ): Nullable<Asset> => {
-43
View File
@@ -1,46 +1,3 @@
export const DB_NAME = 'papercrate_desk'; export const DB_NAME = 'papercrate_desk';
export const DB_VERSION = 1; export const DB_VERSION = 1;
export const LAYOUT_STORE = 'layouts'; 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;
+3 -3
View File
@@ -3,19 +3,19 @@ import ApiTokensSection from '../settings/sections/ApiTokensSection';
import CapabilitySetsSection from '../settings/sections/CapabilitySetsSection'; import CapabilitySetsSection from '../settings/sections/CapabilitySetsSection';
import PasskeysSection from '../settings/sections/PasskeysSection'; import PasskeysSection from '../settings/sections/PasskeysSection';
export const PASSKEYS_SECTION: SettingsSectionConfig = { const PASSKEYS_SECTION: SettingsSectionConfig = {
id: 'passkeys', id: 'passkeys',
label: 'Passkeys', label: 'Passkeys',
component: PasskeysSection, component: PasskeysSection,
}; };
export const API_TOKENS_SECTION: SettingsSectionConfig = { const API_TOKENS_SECTION: SettingsSectionConfig = {
id: 'apiTokens', id: 'apiTokens',
label: 'API tokens', label: 'API tokens',
component: ApiTokensSection, component: ApiTokensSection,
}; };
export const CAPABILITY_SETS_SECTION: SettingsSectionConfig = { const CAPABILITY_SETS_SECTION: SettingsSectionConfig = {
id: 'capabilitySets', id: 'capabilitySets',
label: 'Capability sets', label: 'Capability sets',
component: CapabilitySetsSection, component: CapabilitySetsSection,
-1
View File
@@ -2,7 +2,6 @@ export const SIDEBAR_COLLAPSE_STORAGE_KEY = 'papercrate_sidebar_collapsed';
export const THEME_STORAGE_KEY = 'papercrate_theme_settings'; export const THEME_STORAGE_KEY = 'papercrate_theme_settings';
export const DEFAULT_NEUTRAL_HUE = 29; export const DEFAULT_NEUTRAL_HUE = 29;
export const DEFAULT_NEUTRAL_CHROMA = 0.44; export const DEFAULT_NEUTRAL_CHROMA = 0.44;
export const DEFAULT_NEUTRAL_CONTRAST = 0.56;
export const DEFAULT_THEME_MODE = 'system'; export const DEFAULT_THEME_MODE = 'system';
export const THEME_MODES = ['system', 'light', 'dark']; export const THEME_MODES = ['system', 'light', 'dark'];
export const THEME_MODE_LABELS = { export const THEME_MODE_LABELS = {
-2
View File
@@ -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_FIELD_STORAGE_KEY = 'papercrate_sort_field';
export const SORT_DIRECTION_STORAGE_KEY = 'papercrate_sort_direction'; export const SORT_DIRECTION_STORAGE_KEY = 'papercrate_sort_direction';
export const INCLUDE_DESCENDANTS_STORAGE_KEY = 'papercrate_include_descendants'; export const INCLUDE_DESCENDANTS_STORAGE_KEY = 'papercrate_include_descendants';
export const ROOT_FOLDER_LABEL = DEFAULT_FOLDER_NAME;
@@ -2,9 +2,9 @@ import React, { createContext, useContext, useCallback } from 'react';
import type { Document } from '../types/documents'; import type { Document } from '../types/documents';
import type { Identifier } from '../types/identifiers'; 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; openDocument: (doc: Document, target?: DocumentOpenTarget) => void;
} }
+1 -1
View File
@@ -2,7 +2,7 @@ import React, { createContext, useContext, useState, useCallback, useEffect, use
export type ToastVariant = 'info' | 'success' | 'error'; export type ToastVariant = 'info' | 'success' | 'error';
export interface ToastMessage { interface ToastMessage {
id: string; id: string;
message: string; message: string;
variant: ToastVariant; variant: ToastVariant;
@@ -6,7 +6,7 @@ interface CorrespondentUsage {
[key: string]: unknown; [key: string]: unknown;
} }
export interface CorrespondentEntry { interface CorrespondentEntry {
id?: string; id?: string;
name?: string; name?: string;
usage?: CorrespondentUsage; usage?: CorrespondentUsage;
+2 -2
View File
@@ -8,7 +8,7 @@ import React, {
import { LayoutStore, LayoutCard } from './LayoutSystem'; import { LayoutStore, LayoutCard } from './LayoutSystem';
import DesktopDocumentCard from './DesktopDocumentCard'; import DesktopDocumentCard from './DesktopDocumentCard';
import usePreviewMetadata from './hooks/usePreviewMetadata'; 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-layout.css';
import '../styles/workspace/workspace-items.css'; import '../styles/workspace/workspace-items.css';
import '../styles/workspace/workspace-cards.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' }; return { width: size, height: size, source: 'fallback' };
}; };
export interface DesktopWorkspaceProps { interface DesktopWorkspaceProps {
entries: DocumentsListEntry[]; entries: DocumentsListEntry[];
ensureAssetUrl?: (...args: any[]) => Promise<unknown>; ensureAssetUrl?: (...args: any[]) => Promise<unknown>;
getDocumentAsset?: (...args: any[]) => unknown; getDocumentAsset?: (...args: any[]) => unknown;
+1 -1
View File
@@ -3,7 +3,7 @@ import { constrainDimensions, getInitialPosition, CONTAINER_PADDING } from './ut
import { fetchLayoutRecords, upsertLayoutRecords } from './db'; import { fetchLayoutRecords, upsertLayoutRecords } from './db';
import { CardPhysics } from './CardPhysics'; import { CardPhysics } from './CardPhysics';
export interface LayoutCardState { interface LayoutCardState {
id: string; id: string;
x: number; x: number;
y: number; y: number;
-53
View File
@@ -47,26 +47,6 @@ const requestToPromise = <T>(request: IDBRequest<T>, defaultValue: T): Promise<T
}; };
}); });
const iterateCursor = (request: IDBRequest<IDBCursorWithValue | null>, iteratee: (cursor: IDBCursorWithValue) => void) =>
new Promise<void>((resolve, reject) => {
request.onsuccess = (event) => {
const cursor = (event.target as IDBRequest<IDBCursorWithValue>).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) => const transactionComplete = (transaction: IDBTransaction) =>
new Promise<void>((resolve, reject) => { new Promise<void>((resolve, reject) => {
transaction.oncomplete = () => { transaction.oncomplete = () => {
@@ -182,36 +162,3 @@ export const upsertLayoutRecords = async ({
console.warn('[desk] Failed to upsert layout records', error); 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;
});
};
@@ -239,5 +239,3 @@ export const useDeskTagInteractions = ({
handleCanvasDrop, handleCanvasDrop,
}; };
}; };
export default useDeskTagInteractions;
@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { NBSP } from '../constants/ui'; import { NBSP } from '../constants/ui';
export interface CorrespondentLinkEntry { interface CorrespondentLinkEntry {
id?: string | null; id?: string | null;
name?: string | null; name?: string | null;
key?: string; key?: string;
@@ -147,7 +147,7 @@ const normalizeQuickAddOption = (option?: QuickAddOption | string | null): Quick
}; };
}; };
export const TagSection: React.FC<TagSectionProps> = ({ const TagSection: React.FC<TagSectionProps> = ({
tags = [], tags = [],
onRemove, onRemove,
onAdd, onAdd,
@@ -292,7 +292,7 @@ export const TagSection: React.FC<TagSectionProps> = ({
); );
}; };
export const CorrespondentSection: React.FC<CorrespondentSectionProps> = ({ const CorrespondentSection: React.FC<CorrespondentSectionProps> = ({
entries = [], entries = [],
onRemove, onRemove,
onAdd, onAdd,
@@ -187,4 +187,3 @@ const DocumentThumbnailImage = ({
}; };
export default DocumentThumbnailImage; export default DocumentThumbnailImage;
export { DEFAULT_THUMBNAIL_SIZE };
+1 -1
View File
@@ -1,7 +1,7 @@
import { shallowEqual } from 'react-redux'; import { shallowEqual } from 'react-redux';
import type { DocumentId } from '../types/identifiers'; import type { DocumentId } from '../types/identifiers';
export type ManagedDocument = { id?: DocumentId | null } & Record<string, unknown>; type ManagedDocument = { id?: DocumentId | null } & Record<string, unknown>;
type FetchDocument = (id: DocumentId) => Promise<unknown>; type FetchDocument = (id: DocumentId) => Promise<unknown>;
@@ -2,7 +2,7 @@ import React, { CSSProperties, useCallback, useEffect, useMemo, useRef, useState
import useFloatingMenu from '../ui/useFloatingMenu'; import useFloatingMenu from '../ui/useFloatingMenu';
import { CheckIcon, CircleDashedCheckIcon, PlusIcon } from '../ui/icons'; import { CheckIcon, CircleDashedCheckIcon, PlusIcon } from '../ui/icons';
export type AssignmentState = 'all' | 'partial' | 'none'; type AssignmentState = 'all' | 'partial' | 'none';
export interface SelectionAssignmentMenuItem { export interface SelectionAssignmentMenuItem {
id?: string; id?: string;
@@ -24,7 +24,7 @@ export interface NormalizedSelectionAssignmentItem {
payload: unknown; payload: unknown;
} }
export interface SelectionAssignmentMenuProps { interface SelectionAssignmentMenuProps {
label: React.ReactNode; label: React.ReactNode;
items?: SelectionAssignmentMenuItem[]; items?: SelectionAssignmentMenuItem[];
placeholder?: string; placeholder?: string;
@@ -53,7 +53,7 @@ interface BulkCorrespondentRemoveArgs {
documentIds: DocumentId[]; documentIds: DocumentId[];
} }
export interface SelectionFloatingActionsProps { interface SelectionFloatingActionsProps {
selectionCount?: number; selectionCount?: number;
selectedDocumentIds?: SelectedIdList; selectedDocumentIds?: SelectedIdList;
selectedFolderIds?: SelectedIdList; selectedFolderIds?: SelectedIdList;
@@ -487,7 +487,7 @@ const SelectionFloatingActions: React.FC<SelectionFloatingActionsProps> = ({
); );
}; };
export type SelectionFloatingPanelProps = Omit<SelectionFloatingActionsProps, 'selectedDocumentIds' | 'selectedFolderIds' | 'selectionCount'>; type SelectionFloatingPanelProps = Omit<SelectionFloatingActionsProps, 'selectedDocumentIds' | 'selectedFolderIds' | 'selectionCount'>;
export const SelectionFloatingPanel: React.FC<SelectionFloatingPanelProps> = ({ onClearSelection, ...rest }) => { export const SelectionFloatingPanel: React.FC<SelectionFloatingPanelProps> = ({ onClearSelection, ...rest }) => {
const { selectedDocumentIds, selectedFolderIds, clearSelection } = useWorkspaceSelectionContext(); const { selectedDocumentIds, selectedFolderIds, clearSelection } = useWorkspaceSelectionContext();
@@ -512,6 +512,3 @@ export const SelectionFloatingPanel: React.FC<SelectionFloatingPanelProps> = ({
</div> </div>
); );
}; };
export default SelectionFloatingActions;
@@ -8,7 +8,7 @@ import {
import type { FolderTreeNode } from '../lib/apiTypes'; import type { FolderTreeNode } from '../lib/apiTypes';
import type { DocumentId } from '../types/identifiers'; import type { DocumentId } from '../types/identifiers';
export interface SelectionFolderMenuProps { interface SelectionFolderMenuProps {
label: React.ReactNode; label: React.ReactNode;
folderTree?: FolderTreeNode[]; folderTree?: FolderTreeNode[];
onSelectFolder?: (folderId: DocumentId | null) => Promise<void> | void; onSelectFolder?: (folderId: DocumentId | null) => Promise<void> | void;
@@ -1,6 +1,6 @@
import React, { type DragEvent } from 'react'; import React, { type DragEvent } from 'react';
export interface EntryShellHandlers { interface EntryShellHandlers {
onClick: (event: React.MouseEvent) => void; onClick: (event: React.MouseEvent) => void;
onDoubleClick: (event: React.MouseEvent) => void; onDoubleClick: (event: React.MouseEvent) => void;
onDragStart: (event: DragEvent<HTMLElement>) => void; onDragStart: (event: DragEvent<HTMLElement>) => void;
@@ -1,31 +0,0 @@
import React from 'react';
interface IconSizeSliderProps {
scale: number;
onChange: (scale: number) => void;
min?: number;
max?: number;
}
const IconSizeSlider: React.FC<IconSizeSliderProps> = ({
scale,
onChange,
min = 0.5,
max = 2,
}) => {
return (
<div className="icon-size-slider">
<input
type="range"
className="icon-size-slider__input"
min={min}
max={max}
step="any"
value={scale}
onChange={(e) => onChange(parseFloat(e.target.value))}
/>
</div>
);
};
export default IconSizeSlider;
@@ -35,5 +35,3 @@ export const useDocumentsFilter = (): DocumentsFilterValue => {
} }
return context; return context;
}; };
export default DocumentsFilterContext;
+1 -9
View File
@@ -1,12 +1,6 @@
export interface CorrespondentReference {
id?: string | null;
name?: string | null;
key?: string;
}
import type { Document } from '../types/documents'; import type { Document } from '../types/documents';
export interface ResolvedCorrespondent { interface ResolvedCorrespondent {
id?: string | null; id?: string | null;
name: string; name: string;
key: string; key: string;
@@ -44,5 +38,3 @@ export const resolveCorrespondents = (doc?: Document | null): ResolvedCorrespond
return results; return results;
}; };
export default resolveCorrespondents;
@@ -6,8 +6,6 @@ import type {
} from '../utils/ocr'; } from '../utils/ocr';
import type { Document } from '../types/documents'; import type { Document } from '../types/documents';
export type { Document };
const asyncFalse = async () => false; const asyncFalse = async () => false;
const resolveDocumentDownloadHref = (document?: Document | null): string | null => { const resolveDocumentDownloadHref = (document?: Document | null): string | null => {
@@ -82,8 +80,3 @@ export const createDocumentActionState = ({
openOcr, openOcr,
}; };
}; };
export const documentActionsTestExports = {
resolveDocumentDownloadHref,
hasDocumentTextContentAsset,
};
+2 -2
View File
@@ -8,7 +8,7 @@ interface DescribeSummaryOptions {
formatDateTime?: typeof defaultFormatDateTime; 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 { export interface DocumentSummaryRow {
key: string; key: string;
@@ -17,7 +17,7 @@ export interface DocumentSummaryRow {
kind?: DocumentSummaryRowType; kind?: DocumentSummaryRowType;
} }
export type DocumentSummary = DocumentSummaryRow[]; type DocumentSummary = DocumentSummaryRow[];
const coercePageCount = (metadata?: { page_count?: number | string | null } | null): number | null => { const coercePageCount = (metadata?: { page_count?: number | string | null } | null): number | null => {
const raw = metadata?.page_count; const raw = metadata?.page_count;
@@ -2,20 +2,20 @@ import { useMemo } from 'react';
import type { WorkspaceSelectionValue } from '../../app/WorkspaceSelectionContext'; import type { WorkspaceSelectionValue } from '../../app/WorkspaceSelectionContext';
import type { Identifier } from '../../types/identifiers'; import type { Identifier } from '../../types/identifiers';
export interface Breadcrumb { interface Breadcrumb {
id?: Identifier; id?: Identifier;
name?: string; name?: string;
label?: string; label?: string;
title?: string; title?: string;
} }
export interface FolderClickHandlers { interface FolderClickHandlers {
onDrop?: (...args: unknown[]) => void; onDrop?: (...args: unknown[]) => void;
onDragOver?: (...args: unknown[]) => void; onDragOver?: (...args: unknown[]) => void;
onDragLeave?: (...args: unknown[]) => void; onDragLeave?: (...args: unknown[]) => void;
} }
export interface UseDocumentsPanelPropsArgs { interface UseDocumentsPanelPropsArgs {
currentFolderName?: string | null; currentFolderName?: string | null;
breadcrumbs?: Breadcrumb[]; breadcrumbs?: Breadcrumb[];
refreshCurrentFolder?: () => void | Promise<void>; refreshCurrentFolder?: () => void | Promise<void>;
@@ -65,8 +65,6 @@ export interface UseDocumentsPanelPropsArgs {
selectionValue: WorkspaceSelectionValue; selectionValue: WorkspaceSelectionValue;
} }
export type DocumentsPanelProps = ReturnType<typeof useDocumentsPanelProps>;
const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => { const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => {
const { const {
currentFolderName, currentFolderName,
@@ -51,43 +51,43 @@ export const createDocumentsTableHeaderActions = ({
const includeDescendantsToggle = isFilterActive && onToggleIncludeDescendants const includeDescendantsToggle = isFilterActive && onToggleIncludeDescendants
? ( ? (
<button <button
type="button" type="button"
className="icon-button documents-toolbar__toggle" className="icon-button documents-toolbar__toggle"
onClick={onToggleIncludeDescendants} onClick={onToggleIncludeDescendants}
aria-pressed={!includeDescendants} aria-pressed={!includeDescendants}
aria-label={includeDescendants ? 'Include subfolders' : 'Limit to current folder'} aria-label={includeDescendants ? 'Include subfolders' : 'Limit to current folder'}
title={includeDescendants title={includeDescendants
? 'Including subfolders. Click to limit the search to the current folder.' ? 'Including subfolders. Click to limit the search to the current folder.'
: 'Limiting to the current folder. Click to include subfolders again.'} : 'Limiting to the current folder. Click to include subfolders again.'}
> >
{includeDescendants ? <FoldersIcon /> : <FoldersOffIcon />} {includeDescendants ? <FoldersIcon /> : <FoldersOffIcon />}
</button> </button>
) )
: null; : null;
const sortControls = onSortFieldChange const sortControls = onSortFieldChange
? ( ? (
<div className="documents-actions__sort-group"> <div className="documents-actions__sort-group">
<SortFieldQuickMenu sortField={sortField} onChange={onSortFieldChange} /> <SortFieldQuickMenu sortField={sortField} onChange={onSortFieldChange} />
{onSortDirectionToggle ? ( {onSortDirectionToggle ? (
<button <button
type="button" type="button"
className="icon-button documents-toolbar__toggle documents-sort__direction" className="icon-button documents-toolbar__toggle documents-sort__direction"
onClick={onSortDirectionToggle} onClick={onSortDirectionToggle}
aria-pressed={sortDirectionIsDesc} aria-pressed={sortDirectionIsDesc}
aria-label={sortDirectionIsDesc ? 'Sort descending' : 'Sort ascending'} aria-label={sortDirectionIsDesc ? 'Sort descending' : 'Sort ascending'}
title={sortDirectionTitle} title={sortDirectionTitle}
> >
{sortDirectionIsDesc ? ( {sortDirectionIsDesc ? (
<SortDescendingLettersIcon size={18} /> <SortDescendingLettersIcon size={18} />
) : ( ) : (
<SortAscendingLettersIcon size={18} /> <SortAscendingLettersIcon size={18} />
)} )}
</button> </button>
) : null} ) : null}
</div> </div>
) )
: null; : null;
return ( return (
@@ -152,5 +152,3 @@ export const createDocumentsTableHeaderActions = ({
</> </>
); );
}; };
export default createDocumentsTableHeaderActions;
+2 -3
View File
@@ -21,7 +21,7 @@ const serializePayload = (payload: TagPayload): string | null => {
} }
}; };
export const createTagTransferPayload = ( const createTagTransferPayload = (
tag?: TagLike | null, tag?: TagLike | null,
sourceDocId: DocumentId | null = null, sourceDocId: DocumentId | null = null,
): TagPayload | 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) { if (!dataTransfer) {
return null; return null;
} }
@@ -133,4 +133,3 @@ export const isTagTransferEvent = (event?: DragEventLike | null): boolean => {
return TAG_MIME_TYPES.some((type) => typeList.includes(type)); return TAG_MIME_TYPES.some((type) => typeList.includes(type));
}; };
export { TAG_MIME_TYPES };
+4 -6
View File
@@ -1,7 +1,7 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { createDocumentEntryKey, createFolderEntryKey } from '../app/entryKey'; import { createDocumentEntryKey, createFolderEntryKey } from '../app/entryKey';
export type PointerEventLike = MouseEvent | PointerEvent; type PointerEventLike = MouseEvent | PointerEvent;
export const isPointerModifierEvent = (event?: PointerEventLike | null): boolean => export const isPointerModifierEvent = (event?: PointerEventLike | null): boolean =>
Boolean(event && (event.shiftKey || event.metaKey || event.ctrlKey || event.altKey)); 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'; return type === 'click' || type === 'pointerdown' || type === 'pointerup';
}; };
export type EntryType = 'document' | 'folder'; type EntryType = 'document' | 'folder';
export interface WorkspaceEntry { interface WorkspaceEntry {
id: string; id: string;
key?: string; key?: string;
type: EntryType; type: EntryType;
@@ -31,7 +31,7 @@ interface UseEntryPointerOptions {
onDocumentActivate?: (id: string, metadata?: EntryPointerMetadata) => void; onDocumentActivate?: (id: string, metadata?: EntryPointerMetadata) => void;
} }
export interface EntryPointerMetadata { interface EntryPointerMetadata {
modifierClick: boolean; modifierClick: boolean;
primaryClick: boolean; primaryClick: boolean;
rowKey: string; rowKey: string;
@@ -72,5 +72,3 @@ export const useEntryPointer = ({
}, },
[onSelectEntry, onDocumentActivate], [onSelectEntry, onDocumentActivate],
); );
export default useEntryPointer;
@@ -3,7 +3,7 @@ import { DEFAULT_FOLDER_NAME } from '../app/workspaceUtils';
type FolderId = string | null; type FolderId = string | null;
export interface FolderManager { interface FolderManager {
getNameSync: (folderId: FolderId) => string | null; getNameSync: (folderId: FolderId) => string | null;
resolveName: (folderId: FolderId) => Promise<string>; resolveName: (folderId: FolderId) => Promise<string>;
} }
@@ -55,5 +55,3 @@ export const FolderManagerProvider: React.FC<FolderManagerProviderProps> = ({
}; };
export const useFolderManager = (): FolderManager => useContext(FolderManagerContext); export const useFolderManager = (): FolderManager => useContext(FolderManagerContext);
export default FolderManagerContext;
@@ -20,7 +20,7 @@ import { useManagementModals } from '../../app/useManagementModals';
import { useAppDispatch, useAppState } from '../../app/appState'; import { useAppDispatch, useAppState } from '../../app/appState';
import { fetchAsset } from '../../lib/apiClient'; import { fetchAsset } from '../../lib/apiClient';
import { useApi } from '../../app/ApiContext'; import { useApi } from '../../app/ApiContext';
import useWorkspaceSelection from '../../app/useWorkspaceSelection'; import { useWorkspaceSelection } from '../../app/useWorkspaceSelection';
import { useEntryPointer as useEntryPointerCore } from '../../documents/useEntryPointer'; import { useEntryPointer as useEntryPointerCore } from '../../documents/useEntryPointer';
import { isTagTransferEvent } from '../../documents/tagTransfer'; import { isTagTransferEvent } from '../../documents/tagTransfer';
import useDocumentsSelection from '../../documents/hooks/useDocumentsSelection'; import useDocumentsSelection from '../../documents/hooks/useDocumentsSelection';
+1 -12
View File
@@ -342,15 +342,4 @@ api.interceptors.response.use(
}, },
); );
export type { export type { ApiTokenRecord } from './apiTypes';
DownloadLink,
DocumentResponse,
AssetResponse,
FolderTreeNode,
CapabilitySetResponse,
CapabilityResponse,
ApiTokenRecord,
PasskeySummary,
Identifier,
TenantSnippet,
} from './apiTypes';
+3 -3
View File
@@ -14,7 +14,7 @@ export interface TagResponse {
color?: string | null; color?: string | null;
} }
export interface CorrespondentResponse { interface CorrespondentResponse {
id: string; id: string;
name: string; name: string;
metadata: Record<string, unknown>; metadata: Record<string, unknown>;
@@ -29,7 +29,7 @@ export interface AssetResponse {
[key: string]: unknown; [key: string]: unknown;
} }
export interface DocumentVersionResponse { interface DocumentVersionResponse {
id: string; id: string;
version_number: number; version_number: number;
size_bytes: number; size_bytes: number;
@@ -57,7 +57,7 @@ export interface DocumentResponse {
current_version?: DocumentVersionResponse | null; current_version?: DocumentVersionResponse | null;
} }
export interface FolderInfo { interface FolderInfo {
id: string; id: string;
name: string; name: string;
parent_id?: string | null; parent_id?: string | null;
+2 -2
View File
@@ -23,7 +23,7 @@ import { resolveDocumentAssetUrl } from '../asset_manager';
import PanelHeader from '../ui/PanelHeader'; import PanelHeader from '../ui/PanelHeader';
import BreadcrumbTrail from '../ui/BreadcrumbTrail'; import BreadcrumbTrail from '../ui/BreadcrumbTrail';
import DocumentViewerLayout from './DocumentViewerLayout'; import DocumentViewerLayout from './DocumentViewerLayout';
import useViewerLayoutMode from './useViewerLayoutMode'; import { useViewerLayoutMode } from './useViewerLayoutMode';
import { usePanelResizeBindings } from '../app/PanelManagerContext'; import { usePanelResizeBindings } from '../app/PanelManagerContext';
import type { DocumentId, FolderId } from '../types/identifiers'; import type { DocumentId, FolderId } from '../types/identifiers';
import type { Document } from '../types/documents'; import type { Document } from '../types/documents';
@@ -46,7 +46,7 @@ interface DocumentViewerPanelProps extends DocumentSummarySectionProps {
sidebarMode?: SidebarMode; sidebarMode?: SidebarMode;
} }
export const createDocumentViewerHeaderActions = ({ const createDocumentViewerHeaderActions = ({
document, document,
actionState, actionState,
onZoom, onZoom,
+1 -1
View File
@@ -2,7 +2,7 @@ import React from 'react';
import { DownloadIcon } from '../ui/icons'; import { DownloadIcon } from '../ui/icons';
import { AUDIO_EXTENSIONS, VIDEO_EXTENSIONS } from '../constants/preview'; import { AUDIO_EXTENSIONS, VIDEO_EXTENSIONS } from '../constants/preview';
export interface MediaViewerProps { interface MediaViewerProps {
src: string; src: string;
mimeType?: string; mimeType?: string;
filename?: string; filename?: string;
@@ -5,8 +5,6 @@ import {
PORTRAIT_RATIO_STYLE_ID, PORTRAIT_RATIO_STYLE_ID,
} from '../constants/preview'; } from '../constants/preview';
export { DOCUMENT_VIEWER_PORTRAIT_HEIGHT_RATIO };
const ensurePortraitRatioStyle = () => { const ensurePortraitRatioStyle = () => {
const cssValue = String(DOCUMENT_VIEWER_PORTRAIT_HEIGHT_RATIO); const cssValue = String(DOCUMENT_VIEWER_PORTRAIT_HEIGHT_RATIO);
const cssText = `:root { --document-viewer-portrait-height-ratio: ${cssValue}; }`; const cssText = `:root { --document-viewer-portrait-height-ratio: ${cssValue}; }`;
@@ -78,5 +76,3 @@ export const useViewerLayoutMode = (
return isStacked; return isStacked;
}; };
export default useViewerLayoutMode;
@@ -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<string, unknown>) => Promise<unknown>;
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 <Navigate to="/documents" replace />;
}
const previewId = previewWorkspaceDocument?.id;
if (!previewWorkspaceDocument || String(previewId) !== String(documentId)) {
return <div className="document-viewer__message">Loading preview</div>;
}
return <Navigate to="/documents" replace />;
};
export default DocumentViewerRoute;
+1 -8
View File
@@ -1,12 +1,5 @@
import PasskeysSection from './PasskeysSection';
import ApiTokensSection from './ApiTokensSection';
import CapabilitySetsSection from './CapabilitySetsSection';
import { import {
API_TOKENS_SECTION,
CAPABILITY_SETS_SECTION,
DEFAULT_SETTINGS_SECTIONS, DEFAULT_SETTINGS_SECTIONS,
PASSKEYS_SECTION,
} from '../../constants/settings'; } from '../../constants/settings';
export { PasskeysSection, ApiTokensSection, CapabilitySetsSection }; export { DEFAULT_SETTINGS_SECTIONS };
export { PASSKEYS_SECTION, API_TOKENS_SECTION, CAPABILITY_SETS_SECTION, DEFAULT_SETTINGS_SECTIONS };
+3 -3
View File
@@ -59,13 +59,13 @@ interface PasskeyRegisterPayload {
nickname?: string; nickname?: string;
} }
export type RegisterPasskeyFailureReason = 'unsupported' | 'busy' | 'cancelled' | 'error'; type RegisterPasskeyFailureReason = 'unsupported' | 'busy' | 'cancelled' | 'error';
export type RegisterPasskeyResult = export type RegisterPasskeyResult =
| { ok: true } | { ok: true }
| { ok: false; reason: RegisterPasskeyFailureReason; message?: string }; | { ok: false; reason: RegisterPasskeyFailureReason; message?: string };
export type RevokePasskeyFailureReason = 'missing-id' | 'error'; type RevokePasskeyFailureReason = 'missing-id' | 'error';
export type RevokePasskeyResult = type RevokePasskeyResult =
| { ok: true } | { ok: true }
| { ok: false; reason: RevokePasskeyFailureReason; message?: string }; | { ok: false; reason: RevokePasskeyFailureReason; message?: string };
-9
View File
@@ -295,12 +295,3 @@ export const useSidebarContext = () => {
} }
return context; 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;
@@ -20,7 +20,7 @@ export interface FolderTreeNode {
expanded?: boolean; expanded?: boolean;
} }
export interface FolderNodeProps { interface FolderNodeProps {
node: FolderTreeNode; node: FolderTreeNode;
depth: number; depth: number;
isSelected: boolean; isSelected: boolean;
+2 -3
View File
@@ -57,14 +57,14 @@ export interface Folder {
name?: string; name?: string;
} }
export type FolderEntry = { type FolderEntry = {
type: 'folder'; type: 'folder';
id: Identifier | 'root'; id: Identifier | 'root';
key: string; key: string;
folder: Folder; folder: Folder;
}; };
export type DocumentEntry = { type DocumentEntry = {
type: 'document'; type: 'document';
id: Identifier; id: Identifier;
key: string; key: string;
@@ -77,4 +77,3 @@ export type DocumentsListEntry = FolderEntry | DocumentEntry;
import type { MouseEvent } from 'react'; import type { MouseEvent } from 'react';
export type FolderEventHandler = (folder: Folder, event: MouseEvent<HTMLElement>) => void; export type FolderEventHandler = (folder: Folder, event: MouseEvent<HTMLElement>) => void;
export type DocumentEventHandler = (document: Document, event: MouseEvent<HTMLElement>) => void;
+1 -1
View File
@@ -1,5 +1,5 @@
import React, { ElementType, ReactNode } from 'react'; import React, { ElementType, ReactNode } from 'react';
import composeClassName from './classNames'; import { composeClassName } from './classNames';
type TitleProps = Record<string, unknown>; type TitleProps = Record<string, unknown>;
+1 -1
View File
@@ -49,7 +49,7 @@ interface FloatingMenuState {
type CSSVarStyle = CSSProperties & Record<string, string>; type CSSVarStyle = CSSProperties & Record<string, string>;
export interface QuickAddMenuProps { interface QuickAddMenuProps {
onSelectOption?: (value: QuickAddOption, normalized: NormalizedOption) => Promise<void> | void; onSelectOption?: (value: QuickAddOption, normalized: NormalizedOption) => Promise<void> | void;
onCreate?: (value: string) => Promise<void> | void; onCreate?: (value: string) => Promise<void> | void;
options?: QuickAddOption[]; options?: QuickAddOption[];
-2
View File
@@ -1,4 +1,2 @@
export const composeClassName = (base: string, extra?: string | null): string => export const composeClassName = (base: string, extra?: string | null): string =>
extra ? `${base} ${extra}` : base; extra ? `${base} ${extra}` : base;
export default composeClassName;
+1 -92
View File
@@ -12,15 +12,11 @@ import {
IconLayoutList, IconLayoutList,
IconLayoutGrid, IconLayoutGrid,
IconArrowLeft, IconArrowLeft,
IconArrowRight,
IconArrowUp,
IconAnalyze, IconAnalyze,
IconUpload, IconUpload,
IconWindowMaximize, IconWindowMaximize,
IconTextScan2,
IconFolderPlus, IconFolderPlus,
IconFolder, IconFolder,
IconFolderUp,
IconFolders, IconFolders,
IconFoldersOff, IconFoldersOff,
IconRefresh, IconRefresh,
@@ -37,10 +33,8 @@ import {
IconDeviceLaptop, IconDeviceLaptop,
IconLayoutSidebarLeftCollapse, IconLayoutSidebarLeftCollapse,
IconLayoutSidebarLeftExpand, IconLayoutSidebarLeftExpand,
IconLayoutSidebarRightCollapse,
IconLayoutBottombarCollapse, IconLayoutBottombarCollapse,
IconLayoutBottombarExpand, IconLayoutBottombarExpand,
IconClearAll,
IconInfoCircle, IconInfoCircle,
IconCircleDashedCheck, IconCircleDashedCheck,
IconFile, IconFile,
@@ -56,7 +50,7 @@ import {
import FolderSvg from '../assets/folder.svg'; import FolderSvg from '../assets/folder.svg';
const logoWebp = new URL('../assets/logo.webp', import.meta.url).toString(); const logoWebp = new URL('../assets/logo.webp', import.meta.url).toString();
const logoSmallWebp = new URL('../assets/logo_small.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; type TablerIconComponent = (props: TablerIconProps) => JSX.Element;
@@ -223,16 +217,6 @@ export const ArrowLeftIcon: TablerIconComponent = ({ className, size = '1em', st
{...rest} {...rest}
/> />
); );
export const ArrowRightIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconArrowRight
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const SidebarCollapseIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( export const SidebarCollapseIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconLayoutSidebarLeftCollapse <IconLayoutSidebarLeftCollapse
className={composeClassName('icon', className)} className={composeClassName('icon', className)}
@@ -268,16 +252,6 @@ export const FileInfoIcon: TablerIconComponent = ({ className, size = '1em', str
{...rest} {...rest}
/> />
); );
export const DetailPanelCollapseIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconLayoutSidebarRightCollapse
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const BottombarCollapseIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( export const BottombarCollapseIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconLayoutBottombarCollapse <IconLayoutBottombarCollapse
className={composeClassName('icon', className)} className={composeClassName('icon', className)}
@@ -295,16 +269,6 @@ export const BottombarExpandIcon: TablerIconComponent = ({ className, size = '1e
{...rest} {...rest}
/> />
); );
export const ClearAllIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconClearAll
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const FolderPlusIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( export const FolderPlusIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconFolderPlus <IconFolderPlus
className={composeClassName('icon', className)} className={composeClassName('icon', className)}
@@ -349,16 +313,6 @@ export const RestoreIcon: TablerIconComponent = ({ className, size = '1em', stro
{...rest} {...rest}
/> />
); );
export const ArrowUpIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconArrowUp
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const MinusVerticalIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( export const MinusVerticalIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconMinusVertical <IconMinusVertical
className={composeClassName('icon', className)} className={composeClassName('icon', className)}
@@ -567,16 +521,6 @@ export const ChevronDownIcon: TablerIconComponent = ({ className, size = '1em',
{...rest} {...rest}
/> />
); );
export const FolderUpIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<IconFolderUp
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
export const FolderMoveIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => ( export const FolderMoveIcon: TablerIconComponent = ({ className, size = '1em', stroke = 1.6, ...rest }) => (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -601,38 +545,3 @@ export const FolderMoveIcon: TablerIconComponent = ({ className, size = '1em', s
</g> </g>
</svg> </svg>
); );
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 }) => (
<IconTextScan2
className={composeClassName('icon', className)}
size={size}
stroke={stroke}
{...rest}
/>
);
-94
View File
@@ -1,94 +0,0 @@
import { useCallback, useEffect, useRef } from 'react';
import type { PointerEvent as ReactPointerEvent } from 'react';
export interface PointerTapContext<T = unknown> {
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<T = unknown> {
onSingle?: (context: PointerTapContext<T>) => void;
onDouble?: (context: PointerTapContext<T>) => 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 = <T,>({
onSingle,
onDouble,
delay = 240,
filter = defaultFilter,
}: UsePointerTapOptions<T> = {}) => {
const timerRef = useRef<ReturnType<typeof setTimeout> | 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<T> = {
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;
+3 -3
View File
@@ -93,7 +93,7 @@ const srgbChannelToLinear = (value: number) => {
return ((normalized + 0.055) / 1.055) ** 2.4; 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; if (!input) return null;
const match = HEX_COLOR_PATTERN.exec(input.trim()); const match = HEX_COLOR_PATTERN.exec(input.trim());
if (!match) return null; 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 toLinear = (channel) => {
const normalized = channel / 255; const normalized = channel / 255;
return normalized <= 0.03928 return normalized <= 0.03928
@@ -198,7 +198,7 @@ const contrastForPair = (backgroundHex: string, textHex: string): number => {
return contrastRatio(relativeLuminance(background), relativeLuminance(text)); return contrastRatio(relativeLuminance(background), relativeLuminance(text));
}; };
export const getReadableTextColor = ( const getReadableTextColor = (
hex: string, hex: string,
{ light = '#1f1f1f', dark = '#ffffff', fallback = '#1f1f1f' }: { light?: string; dark?: string; fallback?: string } = {}, { light = '#1f1f1f', dark = '#ffffff', fallback = '#1f1f1f' }: { light?: string; dark?: string; fallback?: string } = {},
): string => { ): string => {
-10
View File
@@ -51,13 +51,3 @@ export const toIssuedTimestamp = (dateString: string | null, fallback: string |
candidate.setUTCFullYear(year, month - 1, day); candidate.setUTCFullYear(year, month - 1, day);
return Number.isNaN(candidate.getTime()) ? null : candidate.toISOString(); 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,
};
-16
View File
@@ -7,19 +7,3 @@ export const clamp = (value: number, min: number, max: number): number => {
} }
return value; 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,
};
+1 -4
View File
@@ -4,12 +4,9 @@ import type {
} from '../asset_manager'; } from '../asset_manager';
import type { import type {
Document, Document,
DocumentVersion,
} from '../types/documents'; } from '../types/documents';
import type { Asset } from '../types/assets'; import type { Asset } from '../types/assets';
export type { Document, DocumentVersion, Asset };
export type EnsurePreviewData = (id: string) => Promise<Document | null>; export type EnsurePreviewData = (id: string) => Promise<Document | null>;
export type EnsureAssetUrl = ( export type EnsureAssetUrl = (
id: string, id: string,
@@ -32,7 +29,7 @@ const pickAsset = (doc?: Document | null, getDocumentAsset?: GetDocumentAsset):
return getDocumentAsset(doc, 'text-content') || null; return getDocumentAsset(doc, 'text-content') || null;
}; };
export async function resolveTextContentUrl({ async function resolveTextContentUrl({
document, document,
ensurePreviewData, ensurePreviewData,
getDocumentAsset, getDocumentAsset,
+2 -2
View File
@@ -43,7 +43,7 @@ const decodeBase64 = (value: string): string => window.atob(value);
const encodeBase64 = (binary: string): string => window.btoa(binary); 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 base64 = base64urlToBase64(value || '');
const binary = decodeBase64(base64); const binary = decodeBase64(base64);
const bytes = new Uint8Array(binary.length); const bytes = new Uint8Array(binary.length);
@@ -77,7 +77,7 @@ const toUint8Array = (input?: ArrayBuffer | ArrayBufferView | ArrayLike<number>
return Uint8Array.from(input as ArrayLike<number>) as Uint8Array; return Uint8Array.from(input as ArrayLike<number>) as Uint8Array;
}; };
export const arrayBufferToBase64url = ( const arrayBufferToBase64url = (
buffer?: ArrayBuffer | ArrayBufferView | ArrayLike<number> | null, buffer?: ArrayBuffer | ArrayBufferView | ArrayLike<number> | null,
): string => { ): string => {
const bytes = toUint8Array(buffer); const bytes = toUint8Array(buffer);
-22
View File
@@ -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;
-72
View File
@@ -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'));
});