diff --git a/package-lock.json b/package-lock.json index 7c164e5..7bbba00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -121,7 +121,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1791,7 +1790,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -1815,7 +1813,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -3622,7 +3619,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.1.tgz", "integrity": "sha512-hj9YIJimBCipHVfHKRMnvmHg+wfhKc0o4mTtXh9pKBjC8TLJzz0nzGmLi5UJsYAUgSvXFHgb0V2oY10DUFtImw==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -4152,7 +4148,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4197,7 +4192,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4367,9 +4361,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", - "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "version": "2.11.12", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.12.tgz", + "integrity": "sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -4468,7 +4462,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -4530,9 +4523,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001774", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz", - "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==", + "version": "1.0.30001807", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001807.tgz", + "integrity": "sha512-daRXJ9EB/rdRgu7kV+TTl1YUKtlsMWblPl2sLnpg9DZae16QCegol6A1SmCE31Lm9mXC1sRWGt/krouH+/dl7Q==", "funding": [ { "type": "opencollective", @@ -5222,7 +5215,6 @@ "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==", "dev": true, "license": "MIT", - "peer": true, "workspaces": [ "packages/*" ], @@ -5969,7 +5961,6 @@ "integrity": "sha512-UVIHeVhxmxedbWPCfgS55Jg2rDfwf2BCKeylcPSqazLz5w3Kri7Q4xdBJubsr/+VUzFLh0VjIvh13RaDA2/Xug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "webidl-conversions": "^7.0.0", "whatwg-mimetype": "^3.0.0" @@ -6877,7 +6868,6 @@ "resolved": "https://registry.npmjs.org/ol/-/ol-10.9.0.tgz", "integrity": "sha512-svbbgVQUmEHaKpLQ8kRySojs59Brvgl2zYIrqG9eQNXGfsbi55rQasZIDpwpQzDL6OlzrUb0H4hQaiX9wDoGmA==", "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@types/rbush": "4.0.0", "earcut": "^3.0.0", @@ -7212,7 +7202,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -7354,7 +7343,6 @@ "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.20.9.tgz", "integrity": "sha512-GLBGqXaTcdWnppre3o1sMmy4DcMGSGq/ng+9k2MTNddarRK6SveINqlqYzi3xEXuy06ljY1TTrC6H9C4f360IQ==", "license": "MIT", - "peer": true, "dependencies": { "mgrs": "1.0.0", "wkt-parser": "^1.5.5" @@ -7746,7 +7734,6 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", "license": "MIT", - "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -8122,8 +8109,7 @@ "version": "0.180.0", "resolved": "https://registry.npmjs.org/three/-/three-0.180.0.tgz", "integrity": "sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tiny-lr": { "version": "1.1.1", @@ -8203,7 +8189,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8450,7 +8435,6 @@ "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -8589,7 +8573,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8688,7 +8671,6 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.29.tgz", "integrity": "sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA==", "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.29", "@vue/compiler-sfc": "3.5.29", @@ -8711,6 +8693,7 @@ "integrity": "sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.4.0", "eslint-scope": "^8.2.0 || ^9.0.0", @@ -8735,6 +8718,7 @@ "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", @@ -8754,6 +8738,7 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=4.0" } @@ -8764,6 +8749,7 @@ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -8899,7 +8885,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.3.tgz", "integrity": "sha512-LLBBA4oLmT7sZdHiYE/PeVuifOxYyE2uL/V+9VQP7YSYdJU7bSf7H8bZRRxW8kEPMkmVjnrXmoR3oejIdX0xbg==", "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -8948,7 +8933,6 @@ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "license": "MIT", - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", @@ -9275,7 +9259,6 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "peer": true, "requires": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -10321,15 +10304,13 @@ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", "dev": true, - "peer": true, "requires": {} }, "@csstools/css-tokenizer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "dev": true, - "peer": true + "dev": true }, "@discoveryjs/json-ext": { "version": "0.5.7", @@ -11317,7 +11298,6 @@ "version": "25.3.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.1.tgz", "integrity": "sha512-hj9YIJimBCipHVfHKRMnvmHg+wfhKc0o4mTtXh9pKBjC8TLJzz0nzGmLi5UJsYAUgSvXFHgb0V2oY10DUFtImw==", - "peer": true, "requires": { "undici-types": "~7.18.0" } @@ -11707,8 +11687,7 @@ "acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "peer": true + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==" }, "acorn-import-phases": { "version": "1.0.4", @@ -11733,7 +11712,6 @@ "version": "8.18.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "peer": true, "requires": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -11844,9 +11822,9 @@ "dev": true }, "baseline-browser-mapping": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", - "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==" + "version": "2.11.12", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.12.tgz", + "integrity": "sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==" }, "big.js": { "version": "5.2.2", @@ -11898,7 +11876,6 @@ "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "peer": true, "requires": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -11945,9 +11922,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001774", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz", - "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==" + "version": "1.0.30001807", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001807.tgz", + "integrity": "sha512-daRXJ9EB/rdRgu7kV+TTl1YUKtlsMWblPl2sLnpg9DZae16QCegol6A1SmCE31Lm9mXC1sRWGt/krouH+/dl7Q==" }, "chai": { "version": "5.3.3", @@ -12397,7 +12374,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz", "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==", "dev": true, - "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -12877,7 +12853,6 @@ "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.6.3.tgz", "integrity": "sha512-UVIHeVhxmxedbWPCfgS55Jg2rDfwf2BCKeylcPSqazLz5w3Kri7Q4xdBJubsr/+VUzFLh0VjIvh13RaDA2/Xug==", "dev": true, - "peer": true, "requires": { "webidl-conversions": "^7.0.0", "whatwg-mimetype": "^3.0.0" @@ -13499,7 +13474,6 @@ "version": "10.9.0", "resolved": "https://registry.npmjs.org/ol/-/ol-10.9.0.tgz", "integrity": "sha512-svbbgVQUmEHaKpLQ8kRySojs59Brvgl2zYIrqG9eQNXGfsbi55rQasZIDpwpQzDL6OlzrUb0H4hQaiX9wDoGmA==", - "peer": true, "requires": { "@types/rbush": "4.0.0", "earcut": "^3.0.0", @@ -13718,7 +13692,6 @@ "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "peer": true, "requires": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -13809,7 +13782,6 @@ "version": "2.20.9", "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.20.9.tgz", "integrity": "sha512-GLBGqXaTcdWnppre3o1sMmy4DcMGSGq/ng+9k2MTNddarRK6SveINqlqYzi3xEXuy06ljY1TTrC6H9C4f360IQ==", - "peer": true, "requires": { "mgrs": "1.0.0", "wkt-parser": "^1.5.5" @@ -14071,7 +14043,6 @@ "version": "1.97.3", "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", - "peer": true, "requires": { "@parcel/watcher": "^2.4.1", "chokidar": "^4.0.0", @@ -14292,8 +14263,7 @@ "three": { "version": "0.180.0", "resolved": "https://registry.npmjs.org/three/-/three-0.180.0.tgz", - "integrity": "sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==", - "peer": true + "integrity": "sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==" }, "tiny-lr": { "version": "1.1.1", @@ -14353,8 +14323,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "peer": true + "dev": true } } }, @@ -14510,7 +14479,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", "dev": true, - "peer": true, "requires": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -14539,8 +14507,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "peer": true + "dev": true } } }, @@ -14608,7 +14575,6 @@ "version": "3.5.29", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.29.tgz", "integrity": "sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA==", - "peer": true, "requires": { "@vue/compiler-dom": "3.5.29", "@vue/compiler-sfc": "3.5.29", @@ -14622,6 +14588,7 @@ "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.4.1.tgz", "integrity": "sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==", "dev": true, + "peer": true, "requires": { "debug": "^4.4.0", "eslint-scope": "^8.2.0 || ^9.0.0", @@ -14636,6 +14603,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, + "peer": true, "requires": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", @@ -14647,13 +14615,15 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "dev": true, + "peer": true }, "semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true + "dev": true, + "peer": true } } }, @@ -14749,7 +14719,6 @@ "version": "5.105.3", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.3.tgz", "integrity": "sha512-LLBBA4oLmT7sZdHiYE/PeVuifOxYyE2uL/V+9VQP7YSYdJU7bSf7H8bZRRxW8kEPMkmVjnrXmoR3oejIdX0xbg==", - "peer": true, "requires": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -14782,7 +14751,6 @@ "version": "5.1.4", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", - "peer": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", diff --git a/webapp/js/composables/useDialogManager.js b/webapp/js/composables/useDialogManager.js index 0012a0e..a4ffe75 100644 --- a/webapp/js/composables/useDialogManager.js +++ b/webapp/js/composables/useDialogManager.js @@ -226,39 +226,37 @@ export default { this.selectedUsingFileBrowserList = true; }, - async handleRenameClose(id, newPath, entry, measurementsInfo) { + async handleRenameClose(id, newPath, entry, sidecarsInfo) { if (id == "rename") { if (newPath == null || newPath.length == 0) return; - // Handle measurements rename if requested - if (measurementsInfo && measurementsInfo.renameMeasurements) { + // Handle sidecar files rename if requested + if (sidecarsInfo && sidecarsInfo.sidecars && sidecarsInfo.sidecars.length > 0) { this.isBusy = true; try { - // First rename the point cloud + // First rename the main file await this.renameSelectedFile(newPath); - // Then rename the measurements file - console.log('Renaming measurements file...'); - await this.dataset.moveObj( - measurementsInfo.oldMeasurementsPath, - measurementsInfo.newMeasurementsPath - ); + // Then rename each sidecar file, reusing renameFile() so the + // file browser list and treeview (via emitter) are refreshed too + for (const sc of sidecarsInfo.sidecars) { + const sidecarItem = this.fileBrowserFiles.find(item => item.entry.path === sc.oldPath); + if (sidecarItem) { + await this.renameFile(sidecarItem, sc.newPath, { action: 'rename', silent: true }); + } else { + await this.dataset.moveObj(sc.oldPath, sc.newPath); + } + } - console.log('Both files renamed successfully'); - this.$toast.add({ severity: 'success', summary: 'Renamed', detail: 'Point cloud and measurements file renamed successfully', life: 3000 }); + const count = sidecarsInfo.sidecars.length; + this.$toast.add({ severity: 'success', summary: 'Renamed', detail: `File and ${count} sidecar${count > 1 ? 's' : ''} renamed successfully`, life: 3000 }); } catch (e) { - // If measurements rename fails, show warning but not critical error - if (e.message && e.message.includes('measurements')) { - this.$toast.add({ severity: 'warn', summary: 'Partial Rename', detail: 'Point cloud renamed, but measurements file could not be renamed', life: 5000 }); - console.warn('Measurements rename failed:', e); - } else { - this.showError(e, "Rename"); - } + this.showError(e, "Rename"); } finally { this.isBusy = false; } } else { - // Normal rename without measurements + // Normal rename without sidecars await this.renameSelectedFile(newPath); } } else if (id == "renameddb") { diff --git a/webapp/js/composables/useFileOperations.js b/webapp/js/composables/useFileOperations.js index a87620e..c9f52fe 100644 --- a/webapp/js/composables/useFileOperations.js +++ b/webapp/js/composables/useFileOperations.js @@ -109,6 +109,8 @@ export default { * result toast and whether the build pipeline is re-triggered. * DroneDB indexes builds by file hash, so a pure move never * changes buildability and must NOT trigger build callbacks. + * @param {boolean} [options.silent=false] - skip the result toast (used + * when the caller shows its own aggregate toast, e.g. sidecar renames). */ async renameFile(file, newPath, options = {}) { const action = options.action === 'move' ? 'move' : 'rename'; @@ -141,14 +143,20 @@ export default { this.sortFiles(); - // Only re-trigger build pipeline on rename (extension may have changed, - // making the file buildable). On move the hash is unchanged, so any - // existing build artifacts still apply and no notification is needed. - if (action === 'rename' && BuildManager.isBuildableType(newItem.entry.type)) { + // Only notify the build pipeline when the rename's extension change made a + // previously non-buildable file buildable. Hash-based build artifacts already + // apply to files that were buildable before the rename, so no new build is + // ever queued in that case - showing the "buildable files detected" toast then + // would be misleading since nothing is actually being built. + const wasBuildable = BuildManager.isBuildableType(file.entry.type); + const isBuildable = BuildManager.isBuildableType(newItem.entry.type); + if (action === 'rename' && !wasBuildable && isBuildable) { BuildManager.onFilesAdded(this.dataset, [newItem.entry]); } - if (action === 'move') { + if (options.silent) { + // Caller shows its own (aggregate) toast + } else if (action === 'move') { this.$toast.add({ severity: 'success', summary: 'Moved', detail: `File moved successfully`, life: 3000 }); } else { this.$toast.add({ severity: 'success', summary: 'Renamed', detail: `File renamed successfully`, life: 3000 }); diff --git a/webapp/js/composables/useTaskFormatting.js b/webapp/js/composables/useTaskFormatting.js index af6d8b1..f239776 100644 --- a/webapp/js/composables/useTaskFormatting.js +++ b/webapp/js/composables/useTaskFormatting.js @@ -14,6 +14,7 @@ * Pure formatting (severity/icon/date/duration) stays client-side. */ import reg from '@/libs/api/sharedRegistry'; +import { formatTimeAgo } from '@/libs/utils'; export default { data() { @@ -109,15 +110,7 @@ export default { getRelativeTime(dateString) { if (!dateString) return ''; - const diffMs = Date.now() - new Date(dateString).getTime(); - const minutes = Math.floor(diffMs / 60000); - const hours = Math.floor(diffMs / 3600000); - const days = Math.floor(diffMs / 86400000); - if (minutes < 1) return 'Just now'; - if (minutes < 60) return `${minutes} minutes ago`; - if (hours < 24) return `${hours} hours ago`; - if (days < 30) return `${days} days ago`; - return 'Over a month ago'; + return formatTimeAgo(new Date(dateString).getTime()); }, // Elapsed time for a task still in flight. diff --git a/webapp/js/features/admin/Tasks.vue b/webapp/js/features/admin/Tasks.vue index 1cd97b0..69ebeea 100644 --- a/webapp/js/features/admin/Tasks.vue +++ b/webapp/js/features/admin/Tasks.vue @@ -27,7 +27,7 @@ style="flex: 1; min-height: 0;" empty-message="No tasks match the current filters." @page="onPage" @view-log="openLog" @download-result="downloadResult" - @cancel="cancelTask" @retry="retryTask" /> + @cancel="cancelTask" @retry="retryTask" @delete="deleteTask" /> @@ -39,6 +39,16 @@ confirmText="Cancel Task" cancelText="Don't Cancel" confirmButtonClass="danger" @onClose="handleCancelDialogClose"> + + + + @@ -84,6 +94,9 @@ export default { cancelDialogOpen: false, cancellingTask: null, + deleteDialogOpen: false, + deletingTask: null, + _refreshTimer: null }; }, @@ -221,6 +234,28 @@ export default { } }, + deleteTask(task) { + this.deletingTask = task; + this.deleteDialogOpen = true; + }, + + async handleDeleteDialogClose(buttonId) { + this.deleteDialogOpen = false; + if (buttonId !== 'confirm' || !this.deletingTask) { + this.deletingTask = null; + return; + } + const task = this.deletingTask; + this.deletingTask = null; + try { + await reg.adminDeleteTask(task.taskId); + await this.loadTasks(); + this._toast('success', 'Task deleted', 'The task has been removed from history.'); + } catch (e) { + this._toast('error', 'Delete failed', e.message); + } + }, + async downloadResult(task) { // Navigate to the authenticated result URL (cookie auth); the server // sends Content-Disposition: attachment so the browser downloads it. diff --git a/webapp/js/features/dataset/TaskHistory.vue b/webapp/js/features/dataset/TaskHistory.vue index 7184ea5..3c9aaab 100644 --- a/webapp/js/features/dataset/TaskHistory.vue +++ b/webapp/js/features/dataset/TaskHistory.vue @@ -28,7 +28,7 @@ :downloading-task-id="downloadingTaskId" empty-message="No processing tasks have been run for this dataset yet." @view-log="openLog" @download-result="downloadResult" - @cancel="cancelTask" @retry="retryTask" + @cancel="cancelTask" @retry="retryTask" @delete="deleteTask" @page-reset="onPageReset" /> @@ -200,6 +200,16 @@ @onClose="handleCancelDialogClose"> + + + +
- + @@ -388,6 +388,9 @@ export default { // Load view mode preference from localStorage const savedViewMode = localStorage.getItem('fileViewMode') || 'grid'; + // Load expanded grid preference (table view: thumbnails + relative time vs icons + date only) + const savedExpandedGrid = localStorage.getItem('tableExpandedGrid') !== 'false'; + return { startTab: mainTabs[0].key, error: "", @@ -401,6 +404,7 @@ export default { .Dataset(this.$route.params.ds), currentPath: null, viewMode: savedViewMode, // 'grid' or 'table' + expandedGrid: savedExpandedGrid, // Table view: thumbnails + relative time vs icons + date only selectedDetailFile: null, // For DetailPanel in table view rootDatasetEntry: null, // Root dataset entry with permissions @@ -705,6 +709,12 @@ export default { this.switchViewMode(this.viewMode === 'grid' ? 'table' : 'grid'); }, + toggleExpandedGrid() { + this.expandedGrid = !this.expandedGrid; + localStorage.setItem('tableExpandedGrid', this.expandedGrid); + this.updateExplorerTools(); + }, + handleRefresh() { if (this.$refs.fileBrowser) { this.$refs.fileBrowser.refreshAndNavigate(this.currentPath); @@ -1389,6 +1399,19 @@ export default { } }); + // Expanded grid toggle (thumbnails + relative time) only applies to table view + if (this.viewMode === 'table') { + this.explorerTools.push({ + id: 'expanded-grid', + title: "Expanded Table View", + icon: "fa-solid fa-table-cells-large", + selected: this.expandedGrid, + onClick: () => { + this.toggleExpandedGrid(); + } + }); + } + // Show only the button to switch to the opposite view if (this.viewMode === 'grid') { this.explorerTools.push({ diff --git a/webapp/js/features/dataset/dialogs/RenameDialog.vue b/webapp/js/features/dataset/dialogs/RenameDialog.vue index ce37573..457702a 100644 --- a/webapp/js/features/dataset/dialogs/RenameDialog.vue +++ b/webapp/js/features/dataset/dialogs/RenameDialog.vue @@ -4,22 +4,21 @@ - -
+ +
-
-
- - A measurements file was found for this point cloud -
+
    +
  • {{ sc.label }}
  • +
@@ -35,7 +34,7 @@ import Button from 'primevue/button'; import InputText from 'primevue/inputtext'; import Checkbox from 'primevue/checkbox'; import ddb from 'ddb'; -import { MeasurementStorage } from '@/libs/map/measurementStorage'; +import { discoverSidecars } from '@/libs/sidecarUtils'; export default { components: { @@ -44,16 +43,16 @@ export default { props: { file: { type: Object, required: true }, - busy: { type: Boolean, default: false } + busy: { type: Boolean, default: false }, + allEntries: { type: Array, default: () => [] } }, emits: ['onClose'], data: function () { return { renameText: null, - hasMeasurementsFile: false, - renameMeasurements: true, // Checked by default - checkingMeasurements: false + sidecars: [], + renameSidecars: true // Checked by default }; }, mounted: function () { @@ -64,8 +63,7 @@ export default { // completed and PrimeVue's own focus handling can no longer steal it. this.focusInput(); - // Check if measurements file exists - this.checkForMeasurementsFile(); + this.sidecars = discoverSidecars(this.allEntries, this.file.entry.path); }, methods: { focusInput() { @@ -81,37 +79,6 @@ export default { } }); }, - async checkForMeasurementsFile() { - // Check only for point clouds - if (this.file.entry.type !== ddb.entry.type.POINTCLOUD) { - this.hasMeasurementsFile = false; - return; - } - - this.checkingMeasurements = true; - - try { - // Use dataset from parent to create MeasurementStorage - const dataset = this.$parent.dataset || this.$root.dataset; - if (!dataset) { - console.warn('Dataset not available for measurements check'); - this.hasMeasurementsFile = false; - return; - } - - const storage = new MeasurementStorage(dataset, this.file.entry); - this.hasMeasurementsFile = await storage.exists(); - - if (this.hasMeasurementsFile) { - console.log('Measurements file found:', storage.measurementPath); - } - } catch (e) { - console.error('Error checking for measurements file:', e); - this.hasMeasurementsFile = false; - } finally { - this.checkingMeasurements = false; - } - }, close: function (buttonId) { if (this.busy && buttonId !== 'rename' && buttonId !== 'renameddb') return; this.$emit('onClose', buttonId); @@ -146,39 +113,21 @@ export default { ? basePath + '/' + this.renameText : this.renameText; - // If it's a point cloud with measurements, handle rename for that too - if (this.file.entry.type === ddb.entry.type.POINTCLOUD && - this.hasMeasurementsFile && - this.renameMeasurements) { - - try { - // Calculate the new path for the measurements file - const dataset = this.$parent.dataset || this.$root.dataset; - if (dataset) { - const oldStorage = new MeasurementStorage(dataset, this.file.entry); - const oldMeasurementsPath = oldStorage.measurementPath; - - // Calculate new measurements path based on new name - const newPathWithoutExt = newPath.substring(0, newPath.lastIndexOf('.')); - const newMeasurementsPath = `${newPathWithoutExt}_measurements.geojson`; - - console.log(`Will rename measurements: ${oldMeasurementsPath} -> ${newMeasurementsPath}`); - - // Emit event with both paths - this.$emit('onClose', "rename", newPath, this.file.entry, { - renameMeasurements: true, - oldMeasurementsPath: oldMeasurementsPath, - newMeasurementsPath: newMeasurementsPath - }); - return; - } - } catch (e) { - console.error('Error preparing measurements rename:', e); - // Continue anyway with normal rename - } + // If sidecar files were found and the checkbox is checked, rename them too + if (this.renameSidecars && this.sidecars.length > 0) { + const oldBase = this.file.entry.path.replace(/\.[^./\\]+$/, ''); + const newBase = newPath.replace(/\.[^./\\]+$/, ''); + + const sidecarsInfo = this.sidecars.map(sc => ({ + oldPath: sc.path, + newPath: newBase + sc.path.substring(oldBase.length) + })); + + this.$emit('onClose', "rename", newPath, this.file.entry, { sidecars: sidecarsInfo }); + return; } - // Normal emit for files without measurements or with unchecked checkbox + // Normal emit for files without sidecars or with unchecked checkbox this.$emit('onClose', "rename", newPath, this.file.entry); } } @@ -191,7 +140,7 @@ export default { width: 100%; } -.measurements-rename-option { +.sidecar-rename-option { margin-top: var(--ddb-spacing-lg); padding: var(--ddb-spacing-md); background: rgba(var(--ddb-primary-rgb), 0.1); @@ -199,21 +148,20 @@ export default { border-left: 0.25rem solid var(--ddb-primary); } -.measurements-rename-option label { +.sidecar-rename-option label { color: var(--ddb-text); font-weight: 500; } -.measurements-rename-option .hint { +.sidecar-list { + margin: 0; + padding-left: 1.25rem; font-size: 0.75rem; color: var(--ddb-text-muted); - margin-top: 0.25rem; - display: flex; - align-items: center; - gap: 0.25rem; + list-style: disc; } -.measurements-rename-option .hint i { - font-size: var(--ddb-font-size-base); +.sidecar-list li { + margin-bottom: 0.15rem; } diff --git a/webapp/js/features/dataset/explorer/TableView.vue b/webapp/js/features/dataset/explorer/TableView.vue index a2f22c4..ccb4229 100644 --- a/webapp/js/features/dataset/explorer/TableView.vue +++ b/webapp/js/features/dataset/explorer/TableView.vue @@ -30,8 +30,12 @@ @row-contextmenu="onRowContextMenu"> @@ -76,7 +80,10 @@