mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-31 10:37:11 -04:00 
			
		
		
		
	chore: svelte 5 🎉 (#13738)
chore: svelte 5 Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									fed882a28a
								
							
						
					
					
						commit
						eadcbd52fb
					
				
							
								
								
									
										250
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										250
									
								
								web/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -23,9 +23,9 @@ | ||||
|         "justified-layout": "^4.1.0", | ||||
|         "lodash-es": "^4.17.21", | ||||
|         "luxon": "^3.4.4", | ||||
|         "socket.io-client": "^4.7.4", | ||||
|         "socket.io-client": "~4.7.5", | ||||
|         "svelte-gestures": "^5.0.4", | ||||
|         "svelte-i18n": "^4.0.0", | ||||
|         "svelte-i18n": "^4.0.1", | ||||
|         "svelte-local-storage-store": "^0.6.4", | ||||
|         "svelte-maplibre": "^0.9.13", | ||||
|         "thumbhash": "^0.1.1" | ||||
| @ -35,12 +35,12 @@ | ||||
|         "@eslint/js": "^9.8.0", | ||||
|         "@faker-js/faker": "^9.0.0", | ||||
|         "@socket.io/component-emitter": "^3.1.0", | ||||
|         "@sveltejs/adapter-static": "^3.0.1", | ||||
|         "@sveltejs/adapter-static": "^3.0.5", | ||||
|         "@sveltejs/enhanced-img": "^0.3.0", | ||||
|         "@sveltejs/kit": "^2.5.18", | ||||
|         "@sveltejs/vite-plugin-svelte": "^3.1.2", | ||||
|         "@sveltejs/kit": "^2.7.2", | ||||
|         "@sveltejs/vite-plugin-svelte": "^4.0.0", | ||||
|         "@testing-library/jest-dom": "^6.4.2", | ||||
|         "@testing-library/svelte": "^5.2.0", | ||||
|         "@testing-library/svelte": "^5.2.4", | ||||
|         "@testing-library/user-event": "^14.5.2", | ||||
|         "@types/dom-to-image": "^2.6.7", | ||||
|         "@types/justified-layout": "^4.1.4", | ||||
| @ -63,7 +63,7 @@ | ||||
|         "prettier-plugin-sort-json": "^4.0.0", | ||||
|         "prettier-plugin-svelte": "^3.2.6", | ||||
|         "rollup-plugin-visualizer": "^5.12.0", | ||||
|         "svelte": "^4.2.19", | ||||
|         "svelte": "^5.1.5", | ||||
|         "svelte-check": "^4.0.0", | ||||
|         "tailwindcss": "^3.4.1", | ||||
|         "tslib": "^2.6.2", | ||||
| @ -80,7 +80,7 @@ | ||||
|         "@oazapfts/runtime": "^1.0.2" | ||||
|       }, | ||||
|       "devDependencies": { | ||||
|         "@types/node": "^22.8.0", | ||||
|         "@types/node": "^22.8.1", | ||||
|         "typescript": "^5.3.3" | ||||
|       } | ||||
|     }, | ||||
| @ -1994,43 +1994,42 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@sveltejs/vite-plugin-svelte": { | ||||
|       "version": "3.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz", | ||||
|       "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==", | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-4.0.0.tgz", | ||||
|       "integrity": "sha512-kpVJwF+gNiMEsoHaw+FJL76IYiwBikkxYU83+BpqQLdVMff19KeRKLd2wisS8niNBMJ2omv5gG+iGDDwd8jzag==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", | ||||
|         "debug": "^4.3.4", | ||||
|         "@sveltejs/vite-plugin-svelte-inspector": "^3.0.0-next.0||^3.0.0", | ||||
|         "debug": "^4.3.7", | ||||
|         "deepmerge": "^4.3.1", | ||||
|         "kleur": "^4.1.5", | ||||
|         "magic-string": "^0.30.10", | ||||
|         "svelte-hmr": "^0.16.0", | ||||
|         "vitefu": "^0.2.5" | ||||
|         "magic-string": "^0.30.12", | ||||
|         "vitefu": "^1.0.3" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^18.0.0 || >=20" | ||||
|         "node": "^18.0.0 || ^20.0.0 || >=22" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "svelte": "^4.0.0 || ^5.0.0-next.0", | ||||
|         "svelte": "^5.0.0-next.96 || ^5.0.0", | ||||
|         "vite": "^5.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@sveltejs/vite-plugin-svelte-inspector": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", | ||||
|       "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", | ||||
|       "version": "3.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-3.0.1.tgz", | ||||
|       "integrity": "sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "debug": "^4.3.4" | ||||
|         "debug": "^4.3.7" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^18.0.0 || >=20" | ||||
|         "node": "^18.0.0 || ^20.0.0 || >=22" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "@sveltejs/vite-plugin-svelte": "^3.0.0", | ||||
|         "svelte": "^4.0.0 || ^5.0.0-next.0", | ||||
|         "@sveltejs/vite-plugin-svelte": "^4.0.0-next.0||^4.0.0", | ||||
|         "svelte": "^5.0.0-next.96 || ^5.0.0", | ||||
|         "vite": "^5.0.0" | ||||
|       } | ||||
|     }, | ||||
| @ -2230,7 +2229,6 @@ | ||||
|       "resolved": "https://registry.npmjs.org/@testing-library/svelte/-/svelte-5.2.4.tgz", | ||||
|       "integrity": "sha512-EFdy73+lULQgMJ1WolAymrxWWrPv9DWyDuDFKKlUip2PA/EXuHptzfYOKWljccFWDKhhGOu3dqNmoc2f/h/Ecg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@testing-library/dom": "^10.0.0" | ||||
|       }, | ||||
| @ -2797,6 +2795,15 @@ | ||||
|         "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/acorn-typescript": { | ||||
|       "version": "1.4.13", | ||||
|       "resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz", | ||||
|       "integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==", | ||||
|       "license": "MIT", | ||||
|       "peerDependencies": { | ||||
|         "acorn": ">=8.9.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/agent-base": { | ||||
|       "version": "7.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", | ||||
| @ -2883,6 +2890,7 @@ | ||||
|       "version": "5.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", | ||||
|       "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "dequal": "^2.0.3" | ||||
|       } | ||||
| @ -2960,11 +2968,12 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/axobject-query": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", | ||||
|       "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", | ||||
|       "dependencies": { | ||||
|         "dequal": "^2.0.3" | ||||
|       "version": "4.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", | ||||
|       "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", | ||||
|       "license": "Apache-2.0", | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/balanced-match": { | ||||
| @ -3271,18 +3280,6 @@ | ||||
|         "node": ">=6" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/code-red": { | ||||
|       "version": "1.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", | ||||
|       "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", | ||||
|       "dependencies": { | ||||
|         "@jridgewell/sourcemap-codec": "^1.4.15", | ||||
|         "@types/estree": "^1.0.1", | ||||
|         "acorn": "^8.10.0", | ||||
|         "estree-walker": "^3.0.3", | ||||
|         "periscopic": "^3.1.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/color": { | ||||
|       "version": "4.2.3", | ||||
|       "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", | ||||
| @ -3404,18 +3401,6 @@ | ||||
|         "node": ">= 8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/css-tree": { | ||||
|       "version": "2.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", | ||||
|       "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", | ||||
|       "dependencies": { | ||||
|         "mdn-data": "2.0.30", | ||||
|         "source-map-js": "^1.0.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/css.escape": { | ||||
|       "version": "1.5.1", | ||||
|       "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", | ||||
| @ -3505,11 +3490,12 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/debug": { | ||||
|       "version": "4.3.6", | ||||
|       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", | ||||
|       "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", | ||||
|       "version": "4.3.7", | ||||
|       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", | ||||
|       "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "ms": "2.1.2" | ||||
|         "ms": "^2.1.3" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=6.0" | ||||
| @ -3656,22 +3642,23 @@ | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/engine.io-client": { | ||||
|       "version": "6.6.1", | ||||
|       "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.1.tgz", | ||||
|       "integrity": "sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==", | ||||
|       "version": "6.5.4", | ||||
|       "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", | ||||
|       "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@socket.io/component-emitter": "~3.1.0", | ||||
|         "debug": "~4.3.1", | ||||
|         "engine.io-parser": "~5.2.1", | ||||
|         "ws": "~8.17.1", | ||||
|         "xmlhttprequest-ssl": "~2.1.1" | ||||
|         "xmlhttprequest-ssl": "~2.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/engine.io-parser": { | ||||
|       "version": "5.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", | ||||
|       "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", | ||||
|       "version": "5.2.3", | ||||
|       "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", | ||||
|       "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", | ||||
|       "license": "MIT", | ||||
|       "engines": { | ||||
|         "node": ">=10.0.0" | ||||
|       } | ||||
| @ -4151,8 +4138,7 @@ | ||||
|     "node_modules/esm-env": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.0.0.tgz", | ||||
|       "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==", | ||||
|       "dev": true | ||||
|       "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==" | ||||
|     }, | ||||
|     "node_modules/esniff": { | ||||
|       "version": "2.0.1", | ||||
| @ -4197,6 +4183,16 @@ | ||||
|         "node": ">=0.10" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/esrap": { | ||||
|       "version": "1.2.2", | ||||
|       "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.2.2.tgz", | ||||
|       "integrity": "sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@jridgewell/sourcemap-codec": "^1.4.15", | ||||
|         "@types/estree": "^1.0.1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/esrecurse": { | ||||
|       "version": "4.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", | ||||
| @ -4222,6 +4218,7 @@ | ||||
|       "version": "3.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", | ||||
|       "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "@types/estree": "^1.0.0" | ||||
|       } | ||||
| @ -4962,6 +4959,7 @@ | ||||
|       "version": "3.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", | ||||
|       "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@types/estree": "*" | ||||
|       } | ||||
| @ -5329,9 +5327,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/magic-string": { | ||||
|       "version": "0.30.11", | ||||
|       "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", | ||||
|       "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", | ||||
|       "version": "0.30.12", | ||||
|       "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", | ||||
|       "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@jridgewell/sourcemap-codec": "^1.5.0" | ||||
| @ -5475,11 +5473,6 @@ | ||||
|         "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/mdn-data": { | ||||
|       "version": "2.0.30", | ||||
|       "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", | ||||
|       "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" | ||||
|     }, | ||||
|     "node_modules/memoizee": { | ||||
|       "version": "0.4.17", | ||||
|       "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", | ||||
| @ -5601,9 +5594,10 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/ms": { | ||||
|       "version": "2.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", | ||||
|       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" | ||||
|       "version": "2.1.3", | ||||
|       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", | ||||
|       "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/murmurhash-js": { | ||||
|       "version": "1.0.0", | ||||
| @ -5943,16 +5937,6 @@ | ||||
|         "pbf": "bin/pbf" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/periscopic": { | ||||
|       "version": "3.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", | ||||
|       "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", | ||||
|       "dependencies": { | ||||
|         "@types/estree": "^1.0.0", | ||||
|         "estree-walker": "^3.0.0", | ||||
|         "is-reference": "^3.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/picocolors": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", | ||||
| @ -6860,14 +6844,14 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/socket.io-client": { | ||||
|       "version": "4.8.1", | ||||
|       "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", | ||||
|       "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", | ||||
|       "version": "4.7.5", | ||||
|       "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", | ||||
|       "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@socket.io/component-emitter": "~3.1.0", | ||||
|         "debug": "~4.3.2", | ||||
|         "engine.io-client": "~6.6.1", | ||||
|         "engine.io-client": "~6.5.2", | ||||
|         "socket.io-parser": "~4.2.4" | ||||
|       }, | ||||
|       "engines": { | ||||
| @ -6930,6 +6914,7 @@ | ||||
|       "version": "1.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", | ||||
|       "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", | ||||
|       "dev": true, | ||||
|       "license": "BSD-3-Clause", | ||||
|       "engines": { | ||||
|         "node": ">=0.10.0" | ||||
| @ -7176,28 +7161,27 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/svelte": { | ||||
|       "version": "4.2.19", | ||||
|       "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz", | ||||
|       "integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==", | ||||
|       "version": "5.1.5", | ||||
|       "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.1.5.tgz", | ||||
|       "integrity": "sha512-AyYondx6wS0g8mmBMfwJVnOYYBswjBv6L4bc99awfbET2KozWvVwxe8NSN7fhx7Pgr7pOfOXIv7K8+Impc0OoQ==", | ||||
|       "license": "MIT", | ||||
|       "dependencies": { | ||||
|         "@ampproject/remapping": "^2.2.1", | ||||
|         "@jridgewell/sourcemap-codec": "^1.4.15", | ||||
|         "@jridgewell/trace-mapping": "^0.3.18", | ||||
|         "@types/estree": "^1.0.1", | ||||
|         "acorn": "^8.9.0", | ||||
|         "aria-query": "^5.3.0", | ||||
|         "axobject-query": "^4.0.0", | ||||
|         "code-red": "^1.0.3", | ||||
|         "css-tree": "^2.3.1", | ||||
|         "estree-walker": "^3.0.3", | ||||
|         "is-reference": "^3.0.1", | ||||
|         "@ampproject/remapping": "^2.3.0", | ||||
|         "@jridgewell/sourcemap-codec": "^1.5.0", | ||||
|         "@types/estree": "^1.0.5", | ||||
|         "acorn": "^8.12.1", | ||||
|         "acorn-typescript": "^1.4.13", | ||||
|         "aria-query": "^5.3.1", | ||||
|         "axobject-query": "^4.1.0", | ||||
|         "esm-env": "^1.0.0", | ||||
|         "esrap": "^1.2.2", | ||||
|         "is-reference": "^3.0.2", | ||||
|         "locate-character": "^3.0.0", | ||||
|         "magic-string": "^0.30.4", | ||||
|         "periscopic": "^3.1.0" | ||||
|         "magic-string": "^0.30.11", | ||||
|         "zimmerframe": "^1.1.2" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=16" | ||||
|         "node": ">=18" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/svelte-check": { | ||||
| @ -7318,18 +7302,6 @@ | ||||
|       "integrity": "sha512-kElJnoZrQtlkXE0O/RcKioz9NP0Sxx05j31ohyosNkydo6NOEsZB85mhoaCxOQNjxN+QPumYWfmIUsznYFjihA==", | ||||
|       "license": "MIT" | ||||
|     }, | ||||
|     "node_modules/svelte-hmr": { | ||||
|       "version": "0.16.0", | ||||
|       "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", | ||||
|       "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": "^12.20 || ^14.13.1 || >= 16" | ||||
|       }, | ||||
|       "peerDependencies": { | ||||
|         "svelte": "^3.19.0 || ^4.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/svelte-i18n": { | ||||
|       "version": "4.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/svelte-i18n/-/svelte-i18n-4.0.1.tgz", | ||||
| @ -7821,6 +7793,15 @@ | ||||
|         "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0-next.1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/svelte/node_modules/aria-query": { | ||||
|       "version": "5.3.2", | ||||
|       "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", | ||||
|       "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", | ||||
|       "license": "Apache-2.0", | ||||
|       "engines": { | ||||
|         "node": ">= 0.4" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/symbol-tree": { | ||||
|       "version": "3.2.4", | ||||
|       "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", | ||||
| @ -8420,12 +8401,17 @@ | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/vitefu": { | ||||
|       "version": "0.2.5", | ||||
|       "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", | ||||
|       "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", | ||||
|       "version": "1.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.0.3.tgz", | ||||
|       "integrity": "sha512-iKKfOMBHob2WxEJbqbJjHAkmYgvFDPhuqrO82om83S8RLk+17FtyMBfcyeH8GqD0ihShtkMW/zzJgiA51hCNCQ==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "workspaces": [ | ||||
|         "tests/deps/*", | ||||
|         "tests/projects/*" | ||||
|       ], | ||||
|       "peerDependencies": { | ||||
|         "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" | ||||
|         "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0-beta.0" | ||||
|       }, | ||||
|       "peerDependenciesMeta": { | ||||
|         "vite": { | ||||
| @ -8757,9 +8743,9 @@ | ||||
|       "peer": true | ||||
|     }, | ||||
|     "node_modules/xmlhttprequest-ssl": { | ||||
|       "version": "2.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz", | ||||
|       "integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==", | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", | ||||
|       "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", | ||||
|       "engines": { | ||||
|         "node": ">=0.4.0" | ||||
|       } | ||||
| @ -8820,6 +8806,12 @@ | ||||
|       "funding": { | ||||
|         "url": "https://github.com/sponsors/sindresorhus" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/zimmerframe": { | ||||
|       "version": "1.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz", | ||||
|       "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", | ||||
|       "license": "MIT" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|     "build:stats": "BUILD_STATS=true vite build", | ||||
|     "package": "svelte-kit package", | ||||
|     "preview": "vite preview", | ||||
|     "check:svelte": "svelte-check --no-tsconfig --fail-on-warnings", | ||||
|     "check:svelte": "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'reactive_declaration_non_reactive_property:ignore'", | ||||
|     "check:typescript": "tsc --noEmit", | ||||
|     "check:watch": "npm run check:svelte -- --watch", | ||||
|     "check:code": "npm run format && npm run lint && npm run check:svelte && npm run check:typescript", | ||||
| @ -27,12 +27,12 @@ | ||||
|     "@eslint/js": "^9.8.0", | ||||
|     "@faker-js/faker": "^9.0.0", | ||||
|     "@socket.io/component-emitter": "^3.1.0", | ||||
|     "@sveltejs/adapter-static": "^3.0.1", | ||||
|     "@sveltejs/adapter-static": "^3.0.5", | ||||
|     "@sveltejs/enhanced-img": "^0.3.0", | ||||
|     "@sveltejs/kit": "^2.5.18", | ||||
|     "@sveltejs/vite-plugin-svelte": "^3.1.2", | ||||
|     "@sveltejs/kit": "^2.7.2", | ||||
|     "@sveltejs/vite-plugin-svelte": "^4.0.0", | ||||
|     "@testing-library/jest-dom": "^6.4.2", | ||||
|     "@testing-library/svelte": "^5.2.0", | ||||
|     "@testing-library/svelte": "^5.2.4", | ||||
|     "@testing-library/user-event": "^14.5.2", | ||||
|     "@types/dom-to-image": "^2.6.7", | ||||
|     "@types/justified-layout": "^4.1.4", | ||||
| @ -55,7 +55,7 @@ | ||||
|     "prettier-plugin-sort-json": "^4.0.0", | ||||
|     "prettier-plugin-svelte": "^3.2.6", | ||||
|     "rollup-plugin-visualizer": "^5.12.0", | ||||
|     "svelte": "^4.2.19", | ||||
|     "svelte": "^5.1.5", | ||||
|     "svelte-check": "^4.0.0", | ||||
|     "tailwindcss": "^3.4.1", | ||||
|     "tslib": "^2.6.2", | ||||
| @ -79,9 +79,9 @@ | ||||
|     "justified-layout": "^4.1.0", | ||||
|     "lodash-es": "^4.17.21", | ||||
|     "luxon": "^3.4.4", | ||||
|     "socket.io-client": "^4.7.4", | ||||
|     "socket.io-client": "~4.7.5", | ||||
|     "svelte-gestures": "^5.0.4", | ||||
|     "svelte-i18n": "^4.0.0", | ||||
|     "svelte-i18n": "^4.0.1", | ||||
|     "svelte-local-storage-store": "^0.6.4", | ||||
|     "svelte-maplibre": "^0.9.13", | ||||
|     "thumbhash": "^0.1.1" | ||||
|  | ||||
							
								
								
									
										17
									
								
								web/src/lib/__mocks__/animate.mock.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								web/src/lib/__mocks__/animate.mock.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| import { tick } from 'svelte'; | ||||
| import { vi } from 'vitest'; | ||||
| 
 | ||||
| export const getAnimateMock = () => | ||||
|   vi.fn().mockImplementation(() => { | ||||
|     let onfinish: (() => void) | null = null; | ||||
|     void tick().then(() => onfinish?.()); | ||||
| 
 | ||||
|     return { | ||||
|       set onfinish(fn: () => void) { | ||||
|         onfinish = fn; | ||||
|       }, | ||||
|       cancel() { | ||||
|         onfinish = null; | ||||
|       }, | ||||
|     }; | ||||
|   }); | ||||
| @ -14,14 +14,14 @@ | ||||
|     mdiPlay, | ||||
|     mdiSelectionSearch, | ||||
|   } from '@mdi/js'; | ||||
|   import { type ComponentType } from 'svelte'; | ||||
|   import { type Component } from 'svelte'; | ||||
|   import { t } from 'svelte-i18n'; | ||||
|   import JobTileButton from './job-tile-button.svelte'; | ||||
|   import JobTileStatus from './job-tile-status.svelte'; | ||||
| 
 | ||||
|   export let title: string; | ||||
|   export let subtitle: string | undefined; | ||||
|   export let description: ComponentType | undefined; | ||||
|   export let description: Component | undefined; | ||||
|   export let jobCounts: JobCountsDto; | ||||
|   export let queueStatus: QueueStatusDto; | ||||
|   export let icon: string; | ||||
|  | ||||
| @ -19,7 +19,7 @@ | ||||
|     mdiTagFaces, | ||||
|     mdiVideo, | ||||
|   } from '@mdi/js'; | ||||
|   import type { ComponentType } from 'svelte'; | ||||
|   import type { Component } from 'svelte'; | ||||
|   import JobTile from './job-tile.svelte'; | ||||
|   import StorageMigrationDescription from './storage-migration-description.svelte'; | ||||
|   import { dialogController } from '$lib/components/shared-components/dialog/dialog'; | ||||
| @ -30,7 +30,7 @@ | ||||
|   interface JobDetails { | ||||
|     title: string; | ||||
|     subtitle?: string; | ||||
|     description?: ComponentType; | ||||
|     description?: Component; | ||||
|     allText?: string; | ||||
|     refreshText?: string; | ||||
|     missingText: string; | ||||
| @ -56,6 +56,7 @@ | ||||
|     await handleCommand(jobId, dto); | ||||
|   }; | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: jobDetails = <Partial<Record<JobName, JobDetails>>>{ | ||||
|     [JobName.ThumbnailGeneration]: { | ||||
|       icon: mdiFileJpgBox, | ||||
|  | ||||
| @ -87,6 +87,7 @@ | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: { | ||||
|     if (selectedGroupOption.id === AlbumGroupBy.None) { | ||||
|       groupIcon = mdiFolderRemoveOutline; | ||||
| @ -96,8 +97,10 @@ | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: sortIcon = $albumViewSettings.sortOrder === SortOrder.Desc ? mdiArrowDownThin : mdiArrowUpThin; | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: albumFilterNames = ((): Record<AlbumFilter, string> => { | ||||
|     return { | ||||
|       [AlbumFilter.All]: $t('all'), | ||||
| @ -106,6 +109,7 @@ | ||||
|     }; | ||||
|   })(); | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: albumSortByNames = ((): Record<AlbumSortBy, string> => { | ||||
|     return { | ||||
|       [AlbumSortBy.Title]: $t('sort_title'), | ||||
| @ -117,6 +121,7 @@ | ||||
|     }; | ||||
|   })(); | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: albumGroupByNames = ((): Record<AlbumGroupBy, string> => { | ||||
|     return { | ||||
|       [AlbumGroupBy.None]: $t('group_no'), | ||||
|  | ||||
| @ -135,6 +135,7 @@ | ||||
|   let isOpen = false; | ||||
| 
 | ||||
|   // Step 1: Filter between Owned and Shared albums, or both. | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: { | ||||
|     switch (userSettings.filter) { | ||||
|       case AlbumFilter.Owned: { | ||||
|  | ||||
| @ -13,7 +13,7 @@ | ||||
|       $albumViewSettings.sortOrder = option.defaultOrder; | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: albumSortByNames = ((): Record<AlbumSortBy, string> => { | ||||
|     return { | ||||
|       [AlbumSortBy.Title]: $t('sort_title'), | ||||
|  | ||||
| @ -293,7 +293,7 @@ | ||||
|               class="h-[18px] {disabled | ||||
|                 ? 'cursor-not-allowed' | ||||
|                 : ''} w-full max-h-56 pr-2 items-center overflow-y-auto leading-4 outline-none resize-none bg-gray-200" | ||||
|             /> | ||||
|             ></textarea> | ||||
|           </div> | ||||
|           {#if isSendingMessage} | ||||
|             <div class="flex items-end place-items-center pb-2 ml-0"> | ||||
|  | ||||
| @ -15,13 +15,31 @@ describe('AssetViewerNavBar component', () => { | ||||
|     showShareButton: false, | ||||
|     onZoomImage: () => {}, | ||||
|     onCopyImage: () => {}, | ||||
|     onAction: () => {}, | ||||
|     onRunJob: () => {}, | ||||
|     onPlaySlideshow: () => {}, | ||||
|     onShowDetail: () => {}, | ||||
|     onClose: () => {}, | ||||
|   }; | ||||
| 
 | ||||
|   beforeAll(() => { | ||||
|     Element.prototype.animate = vi.fn().mockImplementation(() => ({ | ||||
|       cancel: () => {}, | ||||
|     })); | ||||
|     vi.stubGlobal( | ||||
|       'ResizeObserver', | ||||
|       vi.fn(() => ({ observe: vi.fn(), unobserve: vi.fn(), disconnect: vi.fn() })), | ||||
|     ); | ||||
|   }); | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|     vi.resetAllMocks(); | ||||
|     resetSavedUser(); | ||||
|   }); | ||||
| 
 | ||||
|   afterAll(() => { | ||||
|     vi.restoreAllMocks(); | ||||
|   }); | ||||
| 
 | ||||
|   it('shows back button', () => { | ||||
|     const asset = assetFactory.build({ isTrashed: false }); | ||||
|     const { getByTitle } = render(AssetViewerNavBar, { asset, ...additionalProps }); | ||||
|  | ||||
| @ -61,6 +61,7 @@ | ||||
| 
 | ||||
|   const sharedLink = getSharedLink(); | ||||
|   $: isOwner = $user && asset.ownerId === $user?.id; | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: showDownloadButton = sharedLink ? sharedLink.allowDownload : !asset.isOffline; | ||||
|   // $: showEditorButton = | ||||
|   //   isOwner && | ||||
|  | ||||
| @ -598,7 +598,7 @@ | ||||
| 
 | ||||
|             {#if stackedAsset.id == asset.id} | ||||
|               <div class="w-full flex place-items-center place-content-center"> | ||||
|                 <div class="w-2 h-2 bg-white rounded-full flex mt-[2px]" /> | ||||
|                 <div class="w-2 h-2 bg-white rounded-full flex mt-[2px]"></div> | ||||
|               </div> | ||||
|             {/if} | ||||
|           </div> | ||||
|  | ||||
| @ -154,7 +154,7 @@ | ||||
|         <div class="border border-t-0 border-red-400 bg-red-100 px-4 py-3 text-red-700"> | ||||
|           <p> | ||||
|             {#if $user?.isAdmin} | ||||
|               <p>{$t('admin.asset_offline_description')}</p> | ||||
|               {$t('admin.asset_offline_description')} | ||||
|             {:else} | ||||
|               {$t('asset_offline_description')} | ||||
|             {/if} | ||||
|  | ||||
| @ -32,7 +32,7 @@ | ||||
|             </div> | ||||
|             <div class="flex place-items-center gap-2"> | ||||
|               <div class="h-[7px] w-full rounded-full bg-gray-200 dark:bg-gray-700"> | ||||
|                 <div class="h-[7px] rounded-full bg-immich-primary" style={`width: ${download.percentage}%`} /> | ||||
|                 <div class="h-[7px] rounded-full bg-immich-primary" style={`width: ${download.percentage}%`}></div> | ||||
|               </div> | ||||
|               <p class="min-w-[4em] whitespace-nowrap text-right"> | ||||
|                 <span class="text-immich-primary"> | ||||
|  | ||||
| @ -23,6 +23,7 @@ | ||||
|   export let onClose: () => void; | ||||
| 
 | ||||
|   let selectedType: string = editTypes[0].name; | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: selectedTypeObj = editTypes.find((t) => t.name === selectedType) || editTypes[0]; | ||||
| 
 | ||||
|   setTimeout(() => { | ||||
|  | ||||
| @ -55,4 +55,4 @@ | ||||
|   }); | ||||
| </script> | ||||
| 
 | ||||
| <div class="h-full w-full mb-0" bind:this={container} /> | ||||
| <div class="h-full w-full mb-0" bind:this={container}></div> | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { getAnimateMock } from '$lib/__mocks__/animate.mock'; | ||||
| import PhotoViewer from '$lib/components/asset-viewer/photo-viewer.svelte'; | ||||
| import * as utils from '$lib/utils'; | ||||
| import { AssetMediaSize } from '@immich/sdk'; | ||||
| @ -24,6 +25,10 @@ describe('PhotoViewer component', () => { | ||||
|     getAssetThumbnailUrlSpy = vi.spyOn(utils, 'getAssetThumbnailUrl'); | ||||
|   }); | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     Element.prototype.animate = getAnimateMock(); | ||||
|   }); | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|     vi.resetAllMocks(); | ||||
|   }); | ||||
|  | ||||
| @ -193,7 +193,7 @@ | ||||
|         <div | ||||
|           class="absolute border-solid border-white border-[3px] rounded-lg" | ||||
|           style="top: {boundingbox.top}px; left: {boundingbox.left}px; height: {boundingbox.height}px; width: {boundingbox.width}px;" | ||||
|         /> | ||||
|         ></div> | ||||
|       {/each} | ||||
|     </div> | ||||
|   {/if} | ||||
|  | ||||
| @ -3,9 +3,9 @@ import { render } from '@testing-library/svelte'; | ||||
| 
 | ||||
| describe('ImageThumbnail component', () => { | ||||
|   beforeAll(() => { | ||||
|     Object.defineProperty(HTMLImageElement.prototype, 'complete', { | ||||
|       value: true, | ||||
|     }); | ||||
|     Element.prototype.animate = vi.fn().mockImplementation(() => ({ | ||||
|       cancel: () => {}, | ||||
|     })); | ||||
|   }); | ||||
| 
 | ||||
|   it('shows thumbhash while image is loading', () => { | ||||
|  | ||||
| @ -96,5 +96,5 @@ | ||||
|     class:rounded-full={circle} | ||||
|     draggable="false" | ||||
|     out:fade={{ duration: THUMBHASH_FADE_DURATION }} | ||||
|   /> | ||||
|   ></canvas> | ||||
| {/if} | ||||
|  | ||||
| @ -218,6 +218,7 @@ | ||||
|           href={currentUrlReplaceAssetId(asset.id)} | ||||
|           on:click={(evt) => evt.preventDefault()} | ||||
|           tabindex={0} | ||||
|           aria-label="Thumbnail URL" | ||||
|         > | ||||
|         </a> | ||||
|       {/if} | ||||
| @ -255,12 +256,12 @@ | ||||
|         <div | ||||
|           class="absolute z-10 h-full w-full bg-gradient-to-b from-black/25 via-[transparent_25%] opacity-0 transition-opacity group-hover:opacity-100" | ||||
|           class:rounded-xl={selected} | ||||
|         /> | ||||
|         ></div> | ||||
| 
 | ||||
|         <!-- Outline on focus --> | ||||
|         <div | ||||
|           class="absolute size-full group-focus-visible:outline outline-4 -outline-offset-4 outline-immich-primary" | ||||
|         /> | ||||
|         ></div> | ||||
| 
 | ||||
|         <!-- Favorite asset star --> | ||||
|         {#if !isSharedLink() && asset.isFavorite} | ||||
| @ -339,7 +340,7 @@ | ||||
|           class="absolute top-0 h-full w-full bg-immich-primary opacity-40" | ||||
|           in:fade={{ duration: 100 }} | ||||
|           out:fade={{ duration: 100 }} | ||||
|         /> | ||||
|         ></div> | ||||
|       {/if} | ||||
|     </div> | ||||
|   {/if} | ||||
|  | ||||
| @ -113,7 +113,10 @@ | ||||
|     }} | ||||
|     on:timeupdate={({ currentTarget }) => { | ||||
|       const remaining = currentTarget.duration - currentTarget.currentTime; | ||||
|       remainingSeconds = Math.min(Math.ceil(remaining), durationInSeconds); | ||||
|       remainingSeconds = Math.min( | ||||
|         Math.ceil(Number.isNaN(remaining) ? Number.POSITIVE_INFINITY : remaining), | ||||
|         durationInSeconds, | ||||
|       ); | ||||
|     }} | ||||
|   /> | ||||
|   ></video> | ||||
| {/if} | ||||
|  | ||||
| @ -102,7 +102,7 @@ | ||||
|               {renderedOption.title} | ||||
|             </p> | ||||
|           {:else} | ||||
|             <div /> | ||||
|             <div></div> | ||||
|             <p class="justify-self-start"> | ||||
|               {renderedOption.title} | ||||
|             </p> | ||||
|  | ||||
| @ -28,11 +28,11 @@ | ||||
|   {#if disabled} | ||||
|     <span | ||||
|       class="slider slider-disabled cursor-not-allowed border border-transparent before:border before:border-transparent" | ||||
|     /> | ||||
|     ></span> | ||||
|   {:else} | ||||
|     <span | ||||
|       class="slider slider-enabled cursor-pointer border-2 border-transparent before:border-2 before:border-transparent peer-focus-visible:outline before:peer-focus-visible:outline peer-focus-visible:dark:outline-gray-200 before:peer-focus-visible:dark:outline-gray-200 peer-focus-visible:outline-gray-600 before:peer-focus-visible:outline-gray-600 peer-focus-visible:dark:border-black before:peer-focus-visible:dark:border-black peer-focus-visible:border-white before:peer-focus-visible:border-white" | ||||
|     /> | ||||
|     ></span> | ||||
|   {/if} | ||||
| </label> | ||||
| 
 | ||||
|  | ||||
| @ -36,14 +36,14 @@ | ||||
|     class:hover:opacity-100={selectable} | ||||
|     class:rounded-full={circle} | ||||
|     class:rounded-lg={!circle} | ||||
|   /> | ||||
|   ></div> | ||||
| 
 | ||||
|   {#if selected} | ||||
|     <div | ||||
|       class="absolute left-0 top-0 h-full w-full bg-blue-500/80" | ||||
|       class:rounded-full={circle} | ||||
|       class:rounded-lg={!circle} | ||||
|     /> | ||||
|     ></div> | ||||
|   {/if} | ||||
| 
 | ||||
|   {#if person.name} | ||||
|  | ||||
| @ -44,6 +44,8 @@ | ||||
|     return personIsHidden; | ||||
|   }; | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   // svelte-ignore reactive_declaration_module_script_dependency | ||||
|   $: toggleButtonOptions = ((): Record<ToggleVisibility, { icon: string; label: string }> => { | ||||
|     return { | ||||
|       [ToggleVisibility.HIDE_ALL]: { icon: mdiEyeOff, label: $t('hide_all_people') }, | ||||
|  | ||||
| @ -102,7 +102,7 @@ | ||||
|       {:else} | ||||
|         {$t('merge_people')} | ||||
|       {/if} | ||||
|       <div /> | ||||
|       <div></div> | ||||
|     </svelte:fragment> | ||||
|     <svelte:fragment slot="trailing"> | ||||
|       <Button size={'sm'} disabled={!hasSelection} on:click={handleMerge}> | ||||
|  | ||||
| @ -119,7 +119,7 @@ | ||||
|   <ControlAppBar {onClose}> | ||||
|     <svelte:fragment slot="leading"> | ||||
|       <slot name="header" /> | ||||
|       <div /> | ||||
|       <div></div> | ||||
|     </svelte:fragment> | ||||
|     <svelte:fragment slot="trailing"> | ||||
|       <div class="flex gap-4"> | ||||
|  | ||||
| @ -18,7 +18,7 @@ | ||||
| 
 | ||||
|   <div class="my-4 flex flex-col gap-2"> | ||||
|     <!-- <label class="immich-form-label" for="secret">{ $t("api_key") }</label> --> | ||||
|     <textarea class="immich-form-input" id="secret" name="secret" readonly={true} value={secret} /> | ||||
|     <textarea class="immich-form-input" id="secret" name="secret" readonly={true} value={secret}></textarea> | ||||
|   </div> | ||||
| 
 | ||||
|   <svelte:fragment slot="sticky-bottom"> | ||||
|  | ||||
| @ -52,7 +52,7 @@ | ||||
| 
 | ||||
|         <div class="m-4 flex flex-col gap-2"> | ||||
|           <label class="immich-form-label" for="description">{$t('description')}</label> | ||||
|           <textarea class="immich-form-input" id="description" bind:value={description} /> | ||||
|           <textarea class="immich-form-input" id="description" bind:value={description}></textarea> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
| @ -5,6 +5,8 @@ import { render, screen } from '@testing-library/svelte'; | ||||
| import { init, locale, register, waitLocale, type Translations } from 'svelte-i18n'; | ||||
| import { describe } from 'vitest'; | ||||
| 
 | ||||
| const getSanitizedHTML = (container: HTMLElement) => container.innerHTML.replaceAll('<!---->', ''); | ||||
| 
 | ||||
| describe('FormatMessage component', () => { | ||||
|   beforeAll(async () => { | ||||
|     register('en', () => | ||||
| @ -57,7 +59,7 @@ describe('FormatMessage component', () => { | ||||
|       key: 'html' as Translations, | ||||
|       values: { name: 'test' }, | ||||
|     }); | ||||
|     expect(container.innerHTML).toBe('Hello <strong>test</strong>'); | ||||
|     expect(getSanitizedHTML(container)).toBe('Hello <strong>test</strong>'); | ||||
|   }); | ||||
| 
 | ||||
|   it('renders a message with html and plural', () => { | ||||
| @ -65,7 +67,7 @@ describe('FormatMessage component', () => { | ||||
|       key: 'plural' as Translations, | ||||
|       values: { count: 1 }, | ||||
|     }); | ||||
|     expect(container.innerHTML).toBe('You have <strong>1 item</strong>'); | ||||
|     expect(getSanitizedHTML(container)).toBe('You have <strong>1 item</strong>'); | ||||
|   }); | ||||
| 
 | ||||
|   it('protects agains XSS injection', () => { | ||||
| @ -85,7 +87,7 @@ describe('FormatMessage component', () => { | ||||
|       key: 'plural_with_html' as Translations, | ||||
|       values: { count: 10 }, | ||||
|     }); | ||||
|     expect(container.innerHTML).toBe('You have <strong>10</strong> items'); | ||||
|     expect(getSanitizedHTML(container)).toBe('You have <strong>10</strong> items'); | ||||
|   }); | ||||
| 
 | ||||
|   it('supports html tags inside select', () => { | ||||
| @ -93,7 +95,7 @@ describe('FormatMessage component', () => { | ||||
|       key: 'select_with_html' as Translations, | ||||
|       values: { status: true }, | ||||
|     }); | ||||
|     expect(container.innerHTML).toBe('Item is <strong>disabled</strong>'); | ||||
|     expect(getSanitizedHTML(container)).toBe('Item is <strong>disabled</strong>'); | ||||
|   }); | ||||
| 
 | ||||
|   it('supports html tags inside selectordinal', () => { | ||||
| @ -101,6 +103,6 @@ describe('FormatMessage component', () => { | ||||
|       key: 'ordinal_with_html' as Translations, | ||||
|       values: { count: 4 }, | ||||
|     }); | ||||
|     expect(container.innerHTML).toBe('<strong>4th</strong> item'); | ||||
|     expect(getSanitizedHTML(container)).toBe('<strong>4th</strong> item'); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| @ -267,14 +267,15 @@ | ||||
| 
 | ||||
|         {#each current.memory.assets as asset, index} | ||||
|           <a class="relative w-full py-2" href={asHref(asset)}> | ||||
|             <span class="absolute left-0 h-[2px] w-full bg-gray-500" /> | ||||
|             <span class="absolute left-0 h-[2px] w-full bg-gray-500"></span> | ||||
|             {#await resetPromise} | ||||
|               <span class="absolute left-0 h-[2px] bg-white" style:width={`${index < current.assetIndex ? 100 : 0}%`} /> | ||||
|               <span class="absolute left-0 h-[2px] bg-white" style:width={`${index < current.assetIndex ? 100 : 0}%`} | ||||
|               ></span> | ||||
|             {:then} | ||||
|               <span | ||||
|                 class="absolute left-0 h-[2px] bg-white" | ||||
|                 style:width={`${index < current.assetIndex ? 100 : index > current.assetIndex ? 0 : $progress * 100}%`} | ||||
|               /> | ||||
|               ></span> | ||||
|             {/await} | ||||
|           </a> | ||||
|         {/each} | ||||
|  | ||||
| @ -18,6 +18,7 @@ | ||||
| 
 | ||||
|   const { clearSelect, getOwnedAssets } = getAssetControlContext(); | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: isAllVideos = [...getOwnedAssets()].every((asset) => asset.type === AssetTypeEnum.Video); | ||||
| 
 | ||||
|   const handleRunJob = async (name: AssetJobName) => { | ||||
|  | ||||
| @ -766,7 +766,7 @@ | ||||
| {#if showShortcuts} | ||||
|   <ShowShortcuts onClose={() => (showShortcuts = !showShortcuts)} /> | ||||
| {/if} | ||||
| {#if assetStore.buckets.length > 0} | ||||
| {#if $assetStore.buckets.length > 0} | ||||
|   <Scrubber | ||||
|     invisible={showSkeleton} | ||||
|     {assetStore} | ||||
|  | ||||
| @ -86,7 +86,7 @@ | ||||
|             </p> | ||||
|             <div | ||||
|               class="absolute left-0 top-0 z-0 h-full w-full rounded-xl bg-gradient-to-t from-black/40 via-transparent to-transparent transition-all hover:bg-black/20" | ||||
|             /> | ||||
|             ></div> | ||||
|           </a> | ||||
|         {/if} | ||||
|       {/each} | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import NumberRangeInput from '$lib/components/shared-components/number-range-input.svelte'; | ||||
| import { act, render, type RenderResult } from '@testing-library/svelte'; | ||||
| import { render, type RenderResult } from '@testing-library/svelte'; | ||||
| import userEvent from '@testing-library/user-event'; | ||||
| 
 | ||||
| describe('NumberRangeInput component', () => { | ||||
| @ -8,13 +8,18 @@ describe('NumberRangeInput component', () => { | ||||
|   let input: HTMLInputElement; | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     sut = render(NumberRangeInput, { id: '', min: -90, max: 90, onInput: () => {} }); | ||||
|     sut = render(NumberRangeInput, { | ||||
|       id: '', | ||||
|       min: -90, | ||||
|       max: 90, | ||||
|       onInput: () => {}, | ||||
|     }); | ||||
|     input = sut.getByRole('spinbutton') as HTMLInputElement; | ||||
|   }); | ||||
| 
 | ||||
|   it('updates value', async () => { | ||||
|     expect(input.value).toBe(''); | ||||
|     await act(() => sut.component.$set({ value: 10 })); | ||||
|     await sut.rerender({ value: 10 }); | ||||
|     expect(input.value).toBe('10'); | ||||
|   }); | ||||
| 
 | ||||
|  | ||||
| @ -51,12 +51,12 @@ | ||||
|     {#if loading} | ||||
|       {#each { length: 3 } as _} | ||||
|         <div class="flex animate-pulse gap-4 px-6 py-2"> | ||||
|           <div class="h-12 w-12 rounded-xl bg-slate-200" /> | ||||
|           <div class="h-12 w-12 rounded-xl bg-slate-200"></div> | ||||
|           <div class="flex flex-col items-start justify-center gap-2"> | ||||
|             <span class="h-4 w-36 animate-pulse bg-slate-200" /> | ||||
|             <span class="h-4 w-36 animate-pulse bg-slate-200"></span> | ||||
|             <div class="flex animate-pulse gap-1"> | ||||
|               <span class="h-3 w-8 bg-slate-200" /> | ||||
|               <span class="h-3 w-20 bg-slate-200" /> | ||||
|               <span class="h-3 w-8 bg-slate-200"></span> | ||||
|               <span class="h-3 w-20 bg-slate-200"></span> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
| @ -52,6 +52,7 @@ | ||||
|     })), | ||||
|   ]; | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: shareType = albumId ? SharedLinkType.Album : SharedLinkType.Individual; | ||||
|   $: { | ||||
|     if (!showMetadata) { | ||||
|  | ||||
| @ -57,6 +57,7 @@ | ||||
|   let map: maplibregl.Map; | ||||
|   let marker: maplibregl.Marker | null = null; | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: style = (async () => { | ||||
|     const config = await getServerConfig(); | ||||
|     const theme = $mapSettings.allowDarkMode ? $colorTheme.value : Theme.LIGHT; | ||||
|  | ||||
| @ -27,6 +27,6 @@ | ||||
| 
 | ||||
| {#if showing} | ||||
|   <div class="absolute left-0 top-0 z-[999999999] h-[3px] w-screen bg-white"> | ||||
|     <span class="absolute h-[3px] bg-immich-primary" style:width={`${$progress}%`} /> | ||||
|     <span class="absolute h-[3px] bg-immich-primary" style:width={`${$progress}%`}></span> | ||||
|   </div> | ||||
| {/if} | ||||
|  | ||||
| @ -79,6 +79,8 @@ describe('NotificationCard component', () => { | ||||
|     }); | ||||
| 
 | ||||
|     expect(sut.getByTestId('title')).toHaveTextContent('info'); | ||||
|     expect(sut.getByTestId('message').innerHTML).toEqual('Notification <b>message</b> with <a href="link">link</a>'); | ||||
|     expect(sut.getByTestId('message').innerHTML.replaceAll('<!---->', '')).toEqual( | ||||
|       'Notification <b>message</b> with <a href="link">link</a>', | ||||
|     ); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| import { getAnimateMock } from '$lib/__mocks__/animate.mock'; | ||||
| import '@testing-library/jest-dom'; | ||||
| import { render, waitFor, type RenderResult } from '@testing-library/svelte'; | ||||
| import { get } from 'svelte/store'; | ||||
| @ -10,10 +11,7 @@ function _getNotificationListElement(sut: RenderResult<NotificationList>): HTMLA | ||||
| 
 | ||||
| describe('NotificationList component', () => { | ||||
|   beforeAll(() => { | ||||
|     // https://testing-library.com/docs/svelte-testing-library/faq#why-arent-transition-events-running
 | ||||
|     vi.stubGlobal('requestAnimationFrame', (fn: FrameRequestCallback) => { | ||||
|       setTimeout(() => fn(Date.now()), 16); | ||||
|     }); | ||||
|     Element.prototype.animate = getAnimateMock(); | ||||
|   }); | ||||
| 
 | ||||
|   afterAll(() => { | ||||
| @ -21,7 +19,7 @@ describe('NotificationList component', () => { | ||||
|   }); | ||||
| 
 | ||||
|   it('shows a notification when added and closes it automatically after the delay timeout', async () => { | ||||
|     const sut: RenderResult<NotificationList> = render(NotificationList); | ||||
|     const sut: RenderResult<NotificationList> = render(NotificationList, { intro: false }); | ||||
|     const status = await sut.findAllByRole('status'); | ||||
| 
 | ||||
|     expect(status).toHaveLength(1); | ||||
|  | ||||
| @ -15,6 +15,7 @@ | ||||
| 
 | ||||
|   export let notification: Notification | ComponentNotification; | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: icon = notification.type === NotificationType.Error ? mdiCloseCircleOutline : mdiInformationOutline; | ||||
|   $: hoverStyle = notification.action.type === 'discard' ? 'hover:cursor-pointer' : ''; | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import type { ComponentProps, ComponentType, SvelteComponent } from 'svelte'; | ||||
| import type { Component as ComponentType } from 'svelte'; | ||||
| import { writable } from 'svelte/store'; | ||||
| 
 | ||||
| export enum NotificationType { | ||||
| @ -28,27 +28,26 @@ type NoopAction = { type: 'noop' }; | ||||
| 
 | ||||
| export type NotificationAction = DiscardAction | NoopAction; | ||||
| 
 | ||||
| type Component<T extends ComponentType> = { | ||||
|   type: T; | ||||
|   props: ComponentProps<InstanceType<T>>; | ||||
| type Props = Record<string, unknown>; | ||||
| type Component<T extends Props> = { | ||||
|   type: ComponentType<T>; | ||||
|   props: T; | ||||
| }; | ||||
| 
 | ||||
| type BaseNotificationOptions<T, R extends keyof T> = Partial<Omit<T, 'id'>> & Pick<T, R>; | ||||
| 
 | ||||
| export type NotificationOptions = BaseNotificationOptions<Notification, 'message'>; | ||||
| export type ComponentNotificationOptions<T extends ComponentType> = BaseNotificationOptions< | ||||
| export type ComponentNotificationOptions<T extends Props> = BaseNotificationOptions< | ||||
|   ComponentNotification<T>, | ||||
|   'component' | ||||
| >; | ||||
| 
 | ||||
| export type ComponentNotification<T extends ComponentType = ComponentType<SvelteComponent>> = Omit< | ||||
|   Notification, | ||||
|   'message' | ||||
| > & { | ||||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||
| export type ComponentNotification<T extends Props = any> = Omit<Notification, 'message'> & { | ||||
|   component: Component<T>; | ||||
| }; | ||||
| 
 | ||||
| export const isComponentNotification = <T extends ComponentType>( | ||||
| export const isComponentNotification = <T extends Props>( | ||||
|   notification: Notification | ComponentNotification<T>, | ||||
| ): notification is ComponentNotification<T> => { | ||||
|   return 'component' in notification; | ||||
| @ -58,7 +57,7 @@ function createNotificationList() { | ||||
|   const notificationList = writable<(Notification | ComponentNotification)[]>([]); | ||||
|   let count = 1; | ||||
| 
 | ||||
|   const show = <T>(options: T extends ComponentType ? ComponentNotificationOptions<T> : NotificationOptions) => { | ||||
|   const show = <T>(options: T extends Props ? ComponentNotificationOptions<T> : NotificationOptions) => { | ||||
|     notificationList.update((currentList) => { | ||||
|       currentList.push({ | ||||
|         id: count++, | ||||
|  | ||||
| @ -6,7 +6,7 @@ | ||||
| 
 | ||||
|   interface $$Props extends HTMLInputAttributes { | ||||
|     password: string; | ||||
|     autocomplete: string; | ||||
|     autocomplete: AutoFill; | ||||
|     required?: boolean; | ||||
|     onInput?: (value: string) => void; | ||||
|   } | ||||
|  | ||||
| @ -86,5 +86,5 @@ | ||||
| </script> | ||||
| 
 | ||||
| {#if !hidden} | ||||
|   <span class="absolute left-0 h-[3px] bg-immich-primary shadow-2xl" style:width={`${$progress * 100}%`} /> | ||||
|   <span class="absolute left-0 h-[3px] bg-immich-primary shadow-2xl" style:width={`${$progress * 100}%`}></span> | ||||
| {/if} | ||||
|  | ||||
| @ -50,7 +50,7 @@ | ||||
|         <p> | ||||
|           {$t('purchase_panel_info_2')} | ||||
|         </p> | ||||
|         <div /> | ||||
|         <div></div> | ||||
|       </div> | ||||
|     {/if} | ||||
| 
 | ||||
|  | ||||
| @ -257,7 +257,7 @@ | ||||
|   {/if} | ||||
|   <div id="lead-in" class="relative" style:height={relativeTopOffset + 'px'} data-label={segments.at(0)?.dateFormatted}> | ||||
|     {#if relativeTopOffset > 6} | ||||
|       <div class="absolute right-[0.75rem] h-[4px] w-[4px] rounded-full bg-gray-300" /> | ||||
|       <div class="absolute right-[0.75rem] h-[4px] w-[4px] rounded-full bg-gray-300"></div> | ||||
|     {/if} | ||||
|   </div> | ||||
|   <!-- Time Segment --> | ||||
| @ -282,7 +282,7 @@ | ||||
|         <div | ||||
|           aria-label={segment.dateFormatted + ' ' + segment.count} | ||||
|           class="absolute right-[0.75rem] bottom-0 h-[4px] w-[4px] rounded-full bg-gray-300" | ||||
|         /> | ||||
|         ></div> | ||||
|       {/if} | ||||
|     </div> | ||||
|   {/each} | ||||
|  | ||||
| @ -28,7 +28,7 @@ | ||||
|   export let disabled = false; | ||||
|   export let isEdited = false; | ||||
|   export let autofocus = false; | ||||
|   export let passwordAutocomplete: string = 'current-password'; | ||||
|   export let passwordAutocomplete: AutoFill = 'current-password'; | ||||
| 
 | ||||
|   let input: HTMLInputElement; | ||||
| 
 | ||||
|  | ||||
| @ -50,5 +50,5 @@ | ||||
|     {value} | ||||
|     on:input={handleInput} | ||||
|     {disabled} | ||||
|   /> | ||||
|   ></textarea> | ||||
| </div> | ||||
|  | ||||
| @ -38,12 +38,12 @@ | ||||
| > | ||||
|   {#if $connected} | ||||
|     <div class="flex gap-2 place-items-center place-content-center"> | ||||
|       <div class="w-[7px] h-[7px] bg-green-500 rounded-full" /> | ||||
|       <div class="w-[7px] h-[7px] bg-green-500 rounded-full"></div> | ||||
|       <p class="dark:text-immich-gray">{$t('server_online')}</p> | ||||
|     </div> | ||||
|   {:else} | ||||
|     <div class="flex gap-2 place-items-center place-content-center"> | ||||
|       <div class="w-[7px] h-[7px] bg-red-500 rounded-full" /> | ||||
|       <div class="w-[7px] h-[7px] bg-red-500 rounded-full"></div> | ||||
|       <p class="text-red-500">{$t('server_offline')}</p> | ||||
|     </div> | ||||
|   {/if} | ||||
|  | ||||
| @ -63,7 +63,7 @@ | ||||
|       </p> | ||||
| 
 | ||||
|       <div class="mt-4 h-[7px] w-full rounded-full bg-gray-200 dark:bg-gray-700"> | ||||
|         <div class="h-[7px] rounded-full {usageClasses}" style="width: {usedPercentage}%" /> | ||||
|         <div class="h-[7px] rounded-full {usageClasses}" style="width: {usedPercentage}%"></div> | ||||
|       </div> | ||||
|     {:else} | ||||
|       <div class="mt-2"> | ||||
|  | ||||
| @ -5,8 +5,11 @@ | ||||
|   import { colorTheme, handleToggleTheme } from '$lib/stores/preferences.store'; | ||||
|   import { t } from 'svelte-i18n'; | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: icon = $colorTheme.value === Theme.LIGHT ? moonPath : sunPath; | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: viewBox = $colorTheme.value === Theme.LIGHT ? moonViewBox : sunViewBox; | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: isDark = $colorTheme.value === Theme.DARK; | ||||
| 
 | ||||
|   export let padding: Padding = '3'; | ||||
|  | ||||
| @ -92,7 +92,7 @@ | ||||
| 
 | ||||
|   {#if uploadAsset.state === UploadState.STARTED} | ||||
|     <div class="text-black relative mt-[5px] h-[15px] w-full rounded-md bg-gray-300 dark:bg-immich-dark-gray"> | ||||
|       <div class="h-[15px] rounded-md bg-immich-primary transition-all" style={`width: ${uploadAsset.progress}%`} /> | ||||
|       <div class="h-[15px] rounded-md bg-immich-primary transition-all" style={`width: ${uploadAsset.progress}%`}></div> | ||||
|       <p class="absolute top-0 h-full w-full text-center text-[10px]"> | ||||
|         {#if uploadAsset.message} | ||||
|           {uploadAsset.message} | ||||
|  | ||||
| @ -33,6 +33,7 @@ | ||||
|   }); | ||||
|   $: selectedDate = `${formattedDate} ${timePortion}`; | ||||
|   $: editedLocale = findLocale($locale).code; | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: selectedOption = { | ||||
|     value: findLocale(editedLocale).code || fallbackLocale.code, | ||||
|     label: findLocale(editedLocale).name || fallbackLocale.name, | ||||
|  | ||||
| @ -16,7 +16,7 @@ export const handleToggleTheme = () => { | ||||
| }; | ||||
| 
 | ||||
| const initTheme = (): ThemeSetting => { | ||||
|   if (browser && !window.matchMedia('(prefers-color-scheme: dark)').matches) { | ||||
|   if (browser && window.matchMedia && !window.matchMedia('(prefers-color-scheme: dark)').matches) { | ||||
|     return { value: Theme.LIGHT, system: false }; | ||||
|   } | ||||
|   return { value: Theme.DARK, system: false }; | ||||
|  | ||||
| @ -142,10 +142,12 @@ | ||||
|   $: showActivityStatus = | ||||
|     album.albumUsers.length > 0 && !$showAssetViewer && (album.isActivityEnabled || $numberOfComments > 0); | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: isEditor = | ||||
|     album.albumUsers.find(({ user: { id } }) => id === $user.id)?.role === AlbumUserRole.Editor || | ||||
|     album.ownerId === $user.id; | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: albumHasViewers = album.albumUsers.some(({ role }) => role === AlbumUserRole.Viewer); | ||||
| 
 | ||||
|   afterNavigate(({ from }) => { | ||||
|  | ||||
| @ -40,6 +40,7 @@ | ||||
|   let isAssetStackSelected: boolean; | ||||
|   let isLinkActionAvailable: boolean; | ||||
| 
 | ||||
|   // svelte-ignore reactive_declaration_non_reactive_property | ||||
|   $: { | ||||
|     const selection = [...$selectedAssets]; | ||||
|     isAllOwned = selection.every((asset) => asset.ownerId === $user.id); | ||||
|  | ||||
| @ -242,7 +242,7 @@ | ||||
|               <th class="text-center text-sm font-medium">{$t('owner')}</th> | ||||
|               <th class="text-center text-sm font-medium">{$t('assets')}</th> | ||||
|               <th class="text-center text-sm font-medium">{$t('size')}</th> | ||||
|               <th class="text-center text-sm font-medium" /> | ||||
|               <th class="text-center text-sm font-medium"></th> | ||||
|             </tr> | ||||
|           </thead> | ||||
|           <tbody class="block overflow-y-auto rounded-md border dark:border-immich-dark-gray"> | ||||
| @ -309,16 +309,19 @@ | ||||
|                 </td> | ||||
|               </tr> | ||||
|               {#if renameLibrary === index} | ||||
|                 <!-- svelte-ignore node_invalid_placement_ssr --> | ||||
|                 <div transition:slide={{ duration: 250 }}> | ||||
|                   <LibraryRenameForm {library} onSubmit={handleUpdate} onCancel={() => (renameLibrary = null)} /> | ||||
|                 </div> | ||||
|               {/if} | ||||
|               {#if editImportPaths === index} | ||||
|                 <!-- svelte-ignore node_invalid_placement_ssr --> | ||||
|                 <div transition:slide={{ duration: 250 }}> | ||||
|                   <LibraryImportPathsForm {library} onSubmit={handleUpdate} onCancel={() => (editImportPaths = null)} /> | ||||
|                 </div> | ||||
|               {/if} | ||||
|               {#if editScanSettings === index} | ||||
|                 <!-- svelte-ignore node_invalid_placement_ssr --> | ||||
|                 <div transition:slide={{ duration: 250 }} class="mb-4 ml-4 mr-4"> | ||||
|                   <LibraryScanSettingsForm | ||||
|                     {library} | ||||
|  | ||||
| @ -9,7 +9,8 @@ process.env.PUBLIC_IMMICH_PAY_HOST = process.env.PUBLIC_IMMICH_PAY_HOST || 'http | ||||
| 
 | ||||
| /** @type {import('@sveltejs/kit').Config} */ | ||||
| const config = { | ||||
|   preprocess: vitePreprocess(), | ||||
|   // TODO: migrate all enums to .ts files and remove `{script: true}` once
 | ||||
|   preprocess: vitePreprocess({ script: true }), | ||||
|   kit: { | ||||
|     adapter: adapter({ | ||||
|       fallback: 'index.html', | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| import { enhancedImages } from '@sveltejs/enhanced-img'; | ||||
| import { sveltekit } from '@sveltejs/kit/vite'; | ||||
| import { svelteTesting } from '@testing-library/svelte/vite'; | ||||
| import path from 'node:path'; | ||||
| import { visualizer } from 'rollup-plugin-visualizer'; | ||||
| import { defineConfig } from 'vite'; | ||||
| @ -37,6 +38,7 @@ export default defineConfig({ | ||||
|         }) | ||||
|       : undefined, | ||||
|     enhancedImages(), | ||||
|     svelteTesting(), | ||||
|   ], | ||||
|   optimizeDeps: { | ||||
|     entries: ['src/**/*.{svelte,ts,html}'], | ||||
| @ -49,6 +51,5 @@ export default defineConfig({ | ||||
|     sequence: { | ||||
|       hooks: 'list', | ||||
|     }, | ||||
|     alias: [{ find: /^svelte$/, replacement: 'svelte/internal' }], | ||||
|   }, | ||||
| }); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user