mirror of
https://github.com/immich-app/immich.git
synced 2025-12-25 01:11:43 +03:00
progress
This commit is contained in:
164
pnpm-lock.yaml
generated
164
pnpm-lock.yaml
generated
@@ -718,7 +718,7 @@ importers:
|
||||
version: link:../open-api/typescript-sdk
|
||||
'@immich/ui':
|
||||
specifier: ^0.50.1
|
||||
version: 0.50.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)
|
||||
version: 0.50.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)
|
||||
'@mapbox/mapbox-gl-rtl-text':
|
||||
specifier: 0.2.3
|
||||
version: 0.2.3(mapbox-gl@1.13.3)
|
||||
@@ -751,7 +751,7 @@ importers:
|
||||
version: 0.41.4
|
||||
'@zoom-image/svelte':
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.8(svelte@5.43.3)
|
||||
version: 0.3.8(svelte@https://pkg.pr.new/svelte@17362)
|
||||
async-mutex:
|
||||
specifier: ^0.5.0
|
||||
version: 0.5.0
|
||||
@@ -805,13 +805,13 @@ importers:
|
||||
version: 5.2.2
|
||||
svelte-i18n:
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.1(svelte@5.43.3)
|
||||
version: 4.0.1(svelte@https://pkg.pr.new/svelte@17362)
|
||||
svelte-maplibre:
|
||||
specifier: ^1.2.5
|
||||
version: 1.2.5(svelte@5.43.3)
|
||||
version: 1.2.5(svelte@https://pkg.pr.new/svelte@17362)
|
||||
svelte-persisted-store:
|
||||
specifier: ^0.12.0
|
||||
version: 0.12.0(svelte@5.43.3)
|
||||
version: 0.12.0(svelte@https://pkg.pr.new/svelte@17362)
|
||||
tabbable:
|
||||
specifier: ^6.2.0
|
||||
version: 6.3.0
|
||||
@@ -836,16 +836,16 @@ importers:
|
||||
version: 3.1.2
|
||||
'@sveltejs/adapter-static':
|
||||
specifier: ^3.0.8
|
||||
version: 3.0.10(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))
|
||||
version: 3.0.10(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))
|
||||
'@sveltejs/enhanced-img':
|
||||
specifier: ^0.9.0
|
||||
version: 0.9.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(rollup@4.53.4)(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
version: 0.9.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(rollup@4.53.4)(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
'@sveltejs/kit':
|
||||
specifier: ^2.27.1
|
||||
version: 2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
version: 2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
'@sveltejs/vite-plugin-svelte':
|
||||
specifier: 6.2.1
|
||||
version: 6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
version: 6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.1.7
|
||||
version: 4.1.18(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
@@ -854,7 +854,7 @@ importers:
|
||||
version: 6.9.1
|
||||
'@testing-library/svelte':
|
||||
specifier: ^5.2.8
|
||||
version: 5.2.9(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@20.0.3(canvas@2.11.2))(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
version: 5.2.9(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@20.0.3(canvas@2.11.2))(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
'@testing-library/user-event':
|
||||
specifier: ^14.5.2
|
||||
version: 14.6.1(@testing-library/dom@10.4.1)
|
||||
@@ -893,7 +893,7 @@ importers:
|
||||
version: 6.0.2(eslint@9.39.2(jiti@2.6.1))
|
||||
eslint-plugin-svelte:
|
||||
specifier: ^3.12.4
|
||||
version: 3.13.1(eslint@9.39.2(jiti@2.6.1))(svelte@5.43.3)
|
||||
version: 3.13.1(eslint@9.39.2(jiti@2.6.1))(svelte@https://pkg.pr.new/svelte@17362)
|
||||
eslint-plugin-unicorn:
|
||||
specifier: ^62.0.0
|
||||
version: 62.0.0(eslint@9.39.2(jiti@2.6.1))
|
||||
@@ -914,19 +914,19 @@ importers:
|
||||
version: 4.1.1(prettier@3.7.4)
|
||||
prettier-plugin-svelte:
|
||||
specifier: ^3.3.3
|
||||
version: 3.4.1(prettier@3.7.4)(svelte@5.43.3)
|
||||
version: 3.4.1(prettier@3.7.4)(svelte@https://pkg.pr.new/svelte@17362)
|
||||
rollup-plugin-visualizer:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.5(rollup@4.53.4)
|
||||
svelte:
|
||||
specifier: 5.43.3
|
||||
version: 5.43.3
|
||||
specifier: https://pkg.pr.new/svelte@17362
|
||||
version: https://pkg.pr.new/svelte@17362
|
||||
svelte-check:
|
||||
specifier: ^4.1.5
|
||||
version: 4.3.4(picomatch@4.0.3)(svelte@5.43.3)(typescript@5.9.3)
|
||||
version: 4.3.4(picomatch@4.0.3)(svelte@https://pkg.pr.new/svelte@17362)(typescript@5.9.3)
|
||||
svelte-eslint-parser:
|
||||
specifier: ^1.3.3
|
||||
version: 1.4.1(svelte@5.43.3)
|
||||
version: 1.4.1(svelte@https://pkg.pr.new/svelte@17362)
|
||||
tailwindcss:
|
||||
specifier: ^4.1.7
|
||||
version: 4.1.18
|
||||
@@ -3009,11 +3009,13 @@ packages:
|
||||
|
||||
'@immich/svelte-markdown-preprocess@0.1.0':
|
||||
resolution: {integrity: sha512-jgSOJEGLPKEXQCNRI4r4YUayeM2b0ZYLdzgKGl891jZBhOQIetlY7rU44kPpV1AA3/8wGDwNFKduIQZZ/qJYzg==}
|
||||
version: 0.1.0
|
||||
peerDependencies:
|
||||
svelte: ^5.0.0
|
||||
|
||||
'@immich/ui@0.50.1':
|
||||
resolution: {integrity: sha512-fNlQGh75ZFa/UZAgJaYk9/ItHOXHNNzN4CunjCmE7WocVVkUZbUxopN9Ku3F5GULSqD/zJ5gNO6PQAZ1ZoSaaQ==}
|
||||
version: 0.50.1
|
||||
peerDependencies:
|
||||
svelte: ^5.0.0
|
||||
|
||||
@@ -4326,11 +4328,13 @@ packages:
|
||||
|
||||
'@sveltejs/adapter-static@3.0.10':
|
||||
resolution: {integrity: sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==}
|
||||
version: 3.0.10
|
||||
peerDependencies:
|
||||
'@sveltejs/kit': ^2.0.0
|
||||
|
||||
'@sveltejs/enhanced-img@0.9.2':
|
||||
resolution: {integrity: sha512-hAYZ8YFgYtqrQ0dXyq6rdmHBFyG+eIQnNjdIoVhqeZQEBIREXoBThkx+7FtDa6ZV35lTRaT9dgFKF4W+4LbuaQ==}
|
||||
version: 0.9.2
|
||||
peerDependencies:
|
||||
'@sveltejs/vite-plugin-svelte': ^6.0.0
|
||||
svelte: ^5.0.0
|
||||
@@ -4338,6 +4342,7 @@ packages:
|
||||
|
||||
'@sveltejs/kit@2.49.2':
|
||||
resolution: {integrity: sha512-Vp3zX/qlwerQmHMP6x0Ry1oY7eKKRcOWGc2P59srOp4zcqyn+etJyQpELgOi4+ZSUgteX8Y387NuwruLgGXLUQ==}
|
||||
version: 2.49.2
|
||||
engines: {node: '>=18.13'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -4351,6 +4356,7 @@ packages:
|
||||
|
||||
'@sveltejs/vite-plugin-svelte-inspector@5.0.1':
|
||||
resolution: {integrity: sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==}
|
||||
version: 5.0.1
|
||||
engines: {node: ^20.19 || ^22.12 || >=24}
|
||||
peerDependencies:
|
||||
'@sveltejs/vite-plugin-svelte': ^6.0.0-next.0
|
||||
@@ -4359,6 +4365,7 @@ packages:
|
||||
|
||||
'@sveltejs/vite-plugin-svelte@6.2.1':
|
||||
resolution: {integrity: sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==}
|
||||
version: 6.2.1
|
||||
engines: {node: ^20.19 || ^22.12 || >=24}
|
||||
peerDependencies:
|
||||
svelte: ^5.0.0
|
||||
@@ -4624,6 +4631,7 @@ packages:
|
||||
|
||||
'@testing-library/svelte@5.2.9':
|
||||
resolution: {integrity: sha512-p0Lg/vL1iEsEasXKSipvW9nBCtItQGhYvxL8OZ4w7/IDdC+LGoSJw4mMS5bndVFON/gWryitEhMr29AlO4FvBg==}
|
||||
version: 5.2.9
|
||||
engines: {node: '>= 10'}
|
||||
peerDependencies:
|
||||
svelte: ^3 || ^4 || ^5 || ^5.0.0-next.0
|
||||
@@ -5187,6 +5195,7 @@ packages:
|
||||
|
||||
'@zoom-image/svelte@0.3.8':
|
||||
resolution: {integrity: sha512-rkXS+JS4qkBccmRK9+I5j+Pe4rp78GWK/7y0EduBJNtt38q+AwmKhhQs8oTMKTU6lOzLgxjXy1TI802mtvcAmw==}
|
||||
version: 0.3.8
|
||||
peerDependencies:
|
||||
svelte: ^3.0.0 || ^4.0.0 || ^5.0.0
|
||||
|
||||
@@ -5565,6 +5574,7 @@ packages:
|
||||
|
||||
bits-ui@2.14.4:
|
||||
resolution: {integrity: sha512-W6kenhnbd/YVvur+DKkaVJ6GldE53eLewur5AhUCqslYQ0vjZr8eWlOfwZnMiPB+PF5HMVqf61vXBvmyrAmPWg==}
|
||||
version: 2.14.4
|
||||
engines: {node: '>=20'}
|
||||
peerDependencies:
|
||||
'@internationalized/date': ^3.8.1
|
||||
@@ -6725,6 +6735,7 @@ packages:
|
||||
|
||||
eslint-plugin-svelte@3.13.1:
|
||||
resolution: {integrity: sha512-Ng+kV/qGS8P/isbNYVE3sJORtubB+yLEcYICMkUWNaDTb0SwZni/JhAYXh/Dz/q2eThUwWY0VMPZ//KYD1n3eQ==}
|
||||
version: 3.13.1
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.1 || ^9.0.0
|
||||
@@ -9758,6 +9769,7 @@ packages:
|
||||
|
||||
prettier-plugin-svelte@3.4.1:
|
||||
resolution: {integrity: sha512-xL49LCloMoZRvSwa6IEdN2GV6cq2IqpYGstYtMT+5wmml1/dClEoI0MZR78MiVPpu6BdQFfN0/y73yO6+br5Pg==}
|
||||
version: 3.4.1
|
||||
peerDependencies:
|
||||
prettier: ^3.0.0
|
||||
svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0
|
||||
@@ -10230,6 +10242,7 @@ packages:
|
||||
|
||||
runed@0.35.1:
|
||||
resolution: {integrity: sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q==}
|
||||
version: 0.35.1
|
||||
peerDependencies:
|
||||
'@sveltejs/kit': ^2.21.0
|
||||
svelte: ^5.7.0
|
||||
@@ -10700,6 +10713,7 @@ packages:
|
||||
|
||||
svelte-check@4.3.4:
|
||||
resolution: {integrity: sha512-DVWvxhBrDsd+0hHWKfjP99lsSXASeOhHJYyuKOFYJcP7ThfSCKgjVarE8XfuMWpS5JV3AlDf+iK1YGGo2TACdw==}
|
||||
version: 4.3.4
|
||||
engines: {node: '>= 18.0.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -10708,6 +10722,7 @@ packages:
|
||||
|
||||
svelte-eslint-parser@1.4.1:
|
||||
resolution: {integrity: sha512-1eqkfQ93goAhjAXxZiu1SaKI9+0/sxp4JIWQwUpsz7ybehRE5L8dNuz7Iry7K22R47p5/+s9EM+38nHV2OlgXA==}
|
||||
version: 1.4.1
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0, pnpm: 10.24.0}
|
||||
peerDependencies:
|
||||
svelte: ^3.37.0 || ^4.0.0 || ^5.0.0
|
||||
@@ -10723,6 +10738,7 @@ packages:
|
||||
|
||||
svelte-i18n@4.0.1:
|
||||
resolution: {integrity: sha512-jaykGlGT5PUaaq04JWbJREvivlCnALtT+m87Kbm0fxyYHynkQaxQMnIKHLm2WeIuBRoljzwgyvz0Z6/CMwfdmQ==}
|
||||
version: 4.0.1
|
||||
engines: {node: '>= 16'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -10730,6 +10746,7 @@ packages:
|
||||
|
||||
svelte-maplibre@1.2.5:
|
||||
resolution: {integrity: sha512-Uklcbi6inW9GA0MuSusbXmFr/MQPmXrjuP8hS1+yFX3ySvCQ477tsM3I7Jo/fUDK3XAxFSIHW6hZfucnM3kXwQ==}
|
||||
version: 1.2.5
|
||||
peerDependencies:
|
||||
'@deck.gl/core': ^9
|
||||
'@deck.gl/layers': ^9
|
||||
@@ -10745,23 +10762,27 @@ packages:
|
||||
|
||||
svelte-parse-markup@0.1.5:
|
||||
resolution: {integrity: sha512-T6mqZrySltPCDwfKXWQ6zehipVLk4GWfH1zCMGgRtLlOIFPuw58ZxVYxVvotMJgJaurKi1i14viB2GIRKXeJTQ==}
|
||||
version: 0.1.5
|
||||
peerDependencies:
|
||||
svelte: ^3.0.0 || ^4.0.0 || ^5.0.0-next.1
|
||||
|
||||
svelte-persisted-store@0.12.0:
|
||||
resolution: {integrity: sha512-BdBQr2SGSJ+rDWH8/aEV5GthBJDapVP0GP3fuUCA7TjYG5ctcB+O9Mj9ZC0+Jo1oJMfZUd1y9H68NFRR5MyIJA==}
|
||||
version: 0.12.0
|
||||
engines: {node: '>=0.14'}
|
||||
peerDependencies:
|
||||
svelte: ^3.48.0 || ^4 || ^5
|
||||
|
||||
svelte-toolbelt@0.10.6:
|
||||
resolution: {integrity: sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ==}
|
||||
version: 0.10.6
|
||||
engines: {node: '>=18', pnpm: '>=8.7.0'}
|
||||
peerDependencies:
|
||||
svelte: ^5.30.2
|
||||
|
||||
svelte@5.43.3:
|
||||
resolution: {integrity: sha512-kjkAjCk41mJfvJZG56XcJNOdJSke94JxtcX8zFzzz2vrt47E0LnoBzU6azIZ1aBxJgUep8qegAkguSf1GjxLXQ==}
|
||||
svelte@https://pkg.pr.new/svelte@17362:
|
||||
resolution: {tarball: https://pkg.pr.new/svelte@17362}
|
||||
version: 5.46.0
|
||||
engines: {node: '>=18'}
|
||||
|
||||
svg-parser@2.0.4:
|
||||
@@ -14649,19 +14670,19 @@ snapshots:
|
||||
|
||||
'@immich/justified-layout-wasm@0.4.3': {}
|
||||
|
||||
'@immich/svelte-markdown-preprocess@0.1.0(svelte@5.43.3)':
|
||||
'@immich/svelte-markdown-preprocess@0.1.0(svelte@https://pkg.pr.new/svelte@17362)':
|
||||
dependencies:
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
|
||||
'@immich/ui@0.50.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)':
|
||||
'@immich/ui@0.50.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)':
|
||||
dependencies:
|
||||
'@immich/svelte-markdown-preprocess': 0.1.0(svelte@5.43.3)
|
||||
'@immich/svelte-markdown-preprocess': 0.1.0(svelte@https://pkg.pr.new/svelte@17362)
|
||||
'@internationalized/date': 3.10.0
|
||||
'@mdi/js': 7.4.47
|
||||
bits-ui: 2.14.4(@internationalized/date@3.10.0)(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)
|
||||
bits-ui: 2.14.4(@internationalized/date@3.10.0)(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)
|
||||
luxon: 3.7.2
|
||||
simple-icons: 15.22.0
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
svelte-highlight: 7.9.0
|
||||
tailwind-merge: 3.4.0
|
||||
tailwind-variants: 3.2.2(tailwind-merge@3.4.0)(tailwindcss@4.1.18)
|
||||
@@ -16165,17 +16186,17 @@ snapshots:
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
|
||||
'@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))':
|
||||
'@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))':
|
||||
dependencies:
|
||||
'@sveltejs/kit': 2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
'@sveltejs/kit': 2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
|
||||
'@sveltejs/enhanced-img@0.9.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(rollup@4.53.4)(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))':
|
||||
'@sveltejs/enhanced-img@0.9.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(rollup@4.53.4)(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
magic-string: 0.30.21
|
||||
sharp: 0.34.5
|
||||
svelte: 5.43.3
|
||||
svelte-parse-markup: 0.1.5(svelte@5.43.3)
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
svelte-parse-markup: 0.1.5(svelte@https://pkg.pr.new/svelte@17362)
|
||||
vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)
|
||||
vite-imagetools: 9.0.2(rollup@4.53.4)
|
||||
zimmerframe: 1.1.4
|
||||
@@ -16183,11 +16204,11 @@ snapshots:
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
'@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))':
|
||||
'@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@standard-schema/spec': 1.1.0
|
||||
'@sveltejs/acorn-typescript': 1.0.8(acorn@8.15.0)
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
'@types/cookie': 0.6.0
|
||||
acorn: 8.15.0
|
||||
cookie: 0.6.0
|
||||
@@ -16199,27 +16220,27 @@ snapshots:
|
||||
sade: 1.8.1
|
||||
set-cookie-parser: 2.7.2
|
||||
sirv: 3.0.2
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)
|
||||
optionalDependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
|
||||
'@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))':
|
||||
'@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
'@sveltejs/vite-plugin-svelte': 6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
debug: 4.4.3
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))':
|
||||
'@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
'@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
debug: 4.4.3
|
||||
deepmerge: 4.3.1
|
||||
magic-string: 0.30.21
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)
|
||||
vitefu: 1.1.1(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
transitivePeerDependencies:
|
||||
@@ -16467,10 +16488,10 @@ snapshots:
|
||||
picocolors: 1.1.1
|
||||
redent: 3.0.0
|
||||
|
||||
'@testing-library/svelte@5.2.9(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@20.0.3(canvas@2.11.2))(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))':
|
||||
'@testing-library/svelte@5.2.9(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@20.0.3(canvas@2.11.2))(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))':
|
||||
dependencies:
|
||||
'@testing-library/dom': 10.4.1
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
optionalDependencies:
|
||||
vite: 7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)
|
||||
vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(happy-dom@20.0.11)(jiti@2.6.1)(jsdom@20.0.3(canvas@2.11.2))(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)
|
||||
@@ -17201,10 +17222,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@namnode/store': 0.1.0
|
||||
|
||||
'@zoom-image/svelte@0.3.8(svelte@5.43.3)':
|
||||
'@zoom-image/svelte@0.3.8(svelte@https://pkg.pr.new/svelte@17362)':
|
||||
dependencies:
|
||||
'@zoom-image/core': 0.41.4
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
|
||||
abab@2.0.6:
|
||||
optional: true
|
||||
@@ -17569,15 +17590,15 @@ snapshots:
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
|
||||
bits-ui@2.14.4(@internationalized/date@3.10.0)(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3):
|
||||
bits-ui@2.14.4(@internationalized/date@3.10.0)(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362):
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.7.3
|
||||
'@floating-ui/dom': 1.7.4
|
||||
'@internationalized/date': 3.10.0
|
||||
esm-env: 1.2.2
|
||||
runed: 0.35.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)
|
||||
svelte: 5.43.3
|
||||
svelte-toolbelt: 0.10.6(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)
|
||||
runed: 0.35.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
svelte-toolbelt: 0.10.6(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)
|
||||
tabbable: 6.3.0
|
||||
transitivePeerDependencies:
|
||||
- '@sveltejs/kit'
|
||||
@@ -18870,7 +18891,7 @@ snapshots:
|
||||
'@types/eslint': 9.6.1
|
||||
eslint-config-prettier: 10.1.8(eslint@9.39.2(jiti@2.6.1))
|
||||
|
||||
eslint-plugin-svelte@3.13.1(eslint@9.39.2(jiti@2.6.1))(svelte@5.43.3):
|
||||
eslint-plugin-svelte@3.13.1(eslint@9.39.2(jiti@2.6.1))(svelte@https://pkg.pr.new/svelte@17362):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@2.6.1))
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
@@ -18882,9 +18903,9 @@ snapshots:
|
||||
postcss-load-config: 3.1.4(postcss@8.5.6)
|
||||
postcss-safe-parser: 7.0.1(postcss@8.5.6)
|
||||
semver: 7.7.3
|
||||
svelte-eslint-parser: 1.4.1(svelte@5.43.3)
|
||||
svelte-eslint-parser: 1.4.1(svelte@https://pkg.pr.new/svelte@17362)
|
||||
optionalDependencies:
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
transitivePeerDependencies:
|
||||
- ts-node
|
||||
|
||||
@@ -22557,10 +22578,10 @@ snapshots:
|
||||
dependencies:
|
||||
prettier: 3.7.4
|
||||
|
||||
prettier-plugin-svelte@3.4.1(prettier@3.7.4)(svelte@5.43.3):
|
||||
prettier-plugin-svelte@3.4.1(prettier@3.7.4)(svelte@https://pkg.pr.new/svelte@17362):
|
||||
dependencies:
|
||||
prettier: 3.7.4
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
|
||||
prettier@3.7.4: {}
|
||||
|
||||
@@ -23157,14 +23178,14 @@ snapshots:
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
|
||||
runed@0.35.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3):
|
||||
runed@0.35.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362):
|
||||
dependencies:
|
||||
dequal: 2.0.3
|
||||
esm-env: 1.2.2
|
||||
lz-string: 1.5.0
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
optionalDependencies:
|
||||
'@sveltejs/kit': 2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
'@sveltejs/kit': 2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||
|
||||
rw@1.3.3: {}
|
||||
|
||||
@@ -23779,19 +23800,19 @@ snapshots:
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
svelte-check@4.3.4(picomatch@4.0.3)(svelte@5.43.3)(typescript@5.9.3):
|
||||
svelte-check@4.3.4(picomatch@4.0.3)(svelte@https://pkg.pr.new/svelte@17362)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
chokidar: 4.0.3
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
picocolors: 1.1.1
|
||||
sade: 1.8.1
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- picomatch
|
||||
|
||||
svelte-eslint-parser@1.4.1(svelte@5.43.3):
|
||||
svelte-eslint-parser@1.4.1(svelte@https://pkg.pr.new/svelte@17362):
|
||||
dependencies:
|
||||
eslint-scope: 8.4.0
|
||||
eslint-visitor-keys: 4.2.1
|
||||
@@ -23800,7 +23821,7 @@ snapshots:
|
||||
postcss-scss: 4.0.9(postcss@8.5.6)
|
||||
postcss-selector-parser: 7.1.1
|
||||
optionalDependencies:
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
|
||||
svelte-gestures@5.2.2: {}
|
||||
|
||||
@@ -23808,7 +23829,7 @@ snapshots:
|
||||
dependencies:
|
||||
highlight.js: 11.11.1
|
||||
|
||||
svelte-i18n@4.0.1(svelte@5.43.3):
|
||||
svelte-i18n@4.0.1(svelte@https://pkg.pr.new/svelte@17362):
|
||||
dependencies:
|
||||
cli-color: 2.0.4
|
||||
deepmerge: 4.3.1
|
||||
@@ -23816,36 +23837,36 @@ snapshots:
|
||||
estree-walker: 2.0.2
|
||||
intl-messageformat: 10.7.18
|
||||
sade: 1.8.1
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
tiny-glob: 0.2.9
|
||||
|
||||
svelte-maplibre@1.2.5(svelte@5.43.3):
|
||||
svelte-maplibre@1.2.5(svelte@https://pkg.pr.new/svelte@17362):
|
||||
dependencies:
|
||||
d3-geo: 3.1.1
|
||||
dequal: 2.0.3
|
||||
just-compare: 2.3.0
|
||||
maplibre-gl: 5.14.0
|
||||
pmtiles: 3.2.1
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
|
||||
svelte-parse-markup@0.1.5(svelte@5.43.3):
|
||||
svelte-parse-markup@0.1.5(svelte@https://pkg.pr.new/svelte@17362):
|
||||
dependencies:
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
|
||||
svelte-persisted-store@0.12.0(svelte@5.43.3):
|
||||
svelte-persisted-store@0.12.0(svelte@https://pkg.pr.new/svelte@17362):
|
||||
dependencies:
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
|
||||
svelte-toolbelt@0.10.6(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3):
|
||||
svelte-toolbelt@0.10.6(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362):
|
||||
dependencies:
|
||||
clsx: 2.1.1
|
||||
runed: 0.35.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@5.43.3)
|
||||
runed: 0.35.1(@sveltejs/kit@2.49.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)(vite@7.3.0(@types/node@24.10.4)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)))(svelte@https://pkg.pr.new/svelte@17362)
|
||||
style-to-object: 1.0.14
|
||||
svelte: 5.43.3
|
||||
svelte: https://pkg.pr.new/svelte@17362
|
||||
transitivePeerDependencies:
|
||||
- '@sveltejs/kit'
|
||||
|
||||
svelte@5.43.3:
|
||||
svelte@https://pkg.pr.new/svelte@17362:
|
||||
dependencies:
|
||||
'@jridgewell/remapping': 2.3.5
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
@@ -23855,6 +23876,7 @@ snapshots:
|
||||
aria-query: 5.3.2
|
||||
axobject-query: 4.1.0
|
||||
clsx: 2.1.1
|
||||
devalue: 5.6.1
|
||||
esm-env: 1.2.2
|
||||
esrap: 2.2.1
|
||||
is-reference: 3.0.3
|
||||
|
||||
@@ -148,6 +148,7 @@ export class MediaRepository {
|
||||
quality: options.quality,
|
||||
// this is default in libvips (except the threshold is 90), but we need to set it manually in sharp
|
||||
chromaSubsampling: options.quality >= 80 ? '4:4:4' : '4:2:0',
|
||||
progressive: true,
|
||||
})
|
||||
.toFile(output);
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
"prettier-plugin-sort-json": "^4.1.1",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"rollup-plugin-visualizer": "^6.0.0",
|
||||
"svelte": "5.43.3",
|
||||
"svelte": "https://pkg.pr.new/svelte@17362",
|
||||
"svelte-check": "^4.1.5",
|
||||
"svelte-eslint-parser": "^1.3.3",
|
||||
"tailwindcss": "^4.1.7",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { preloadManager } from '$lib/managers/PreloadManager.svelte';
|
||||
|
||||
export interface SwipeFeedbackOptions {
|
||||
/** Whether the swipe feedback is disabled */
|
||||
disabled?: boolean;
|
||||
@@ -17,8 +19,6 @@ export interface SwipeFeedbackOptions {
|
||||
swipeThreshold?: number;
|
||||
/** Current asset URL - when this changes, preview containers are reset */
|
||||
currentAssetUrl?: string | null;
|
||||
/** The img or video element to transform. If not provided, will query for img/video inside the node */
|
||||
imageElement?: HTMLImageElement | HTMLVideoElement | null;
|
||||
}
|
||||
|
||||
interface SwipeAnimations {
|
||||
@@ -31,6 +31,8 @@ interface SwipeAnimations {
|
||||
* Allows the user to drag an element left or right (horizontal only),
|
||||
* and resets the position when the drag ends.
|
||||
* Optionally shows preview images on the left/right during swipe.
|
||||
*
|
||||
* Requires exactly one element with [data-swipe-subject] attribute within the node.
|
||||
*/
|
||||
export const swipeFeedback = (node: HTMLElement, options?: SwipeFeedbackOptions) => {
|
||||
// Animation configuration
|
||||
@@ -38,9 +40,18 @@ export const swipeFeedback = (node: HTMLElement, options?: SwipeFeedbackOptions)
|
||||
// Enable/disable scaling effect during animation
|
||||
const ENABLE_SCALE_ANIMATION = false;
|
||||
|
||||
// Find the image element to apply custom transforms
|
||||
let imgElement: HTMLImageElement | HTMLVideoElement | null =
|
||||
options?.imageElement ?? node.querySelector('img') ?? node.querySelector('video');
|
||||
// Find the element to apply custom transforms - must have [data-swipe-subject] attribute
|
||||
const swipeSubjects = node.querySelectorAll<HTMLElement>('[data-swipe-subject]');
|
||||
|
||||
if (swipeSubjects.length === 0) {
|
||||
throw new Error('swipeFeedback action requires exactly one element with [data-swipe-subject] attribute, found 0');
|
||||
}
|
||||
|
||||
if (swipeSubjects.length > 1) {
|
||||
throw new Error(`swipeFeedback action requires exactly one element with [data-swipe-subject] attribute, found ${swipeSubjects.length}`);
|
||||
}
|
||||
|
||||
const imgElement: HTMLElement = swipeSubjects[0];
|
||||
|
||||
let isDragging = false;
|
||||
let startX = 0;
|
||||
@@ -437,12 +448,9 @@ export const swipeFeedback = (node: HTMLElement, options?: SwipeFeedbackOptions)
|
||||
|
||||
// Get current time before modifying animation
|
||||
const currentTime = Number(activeAnimations.currentImageAnimation.currentTime) || 0;
|
||||
console.log(`Committing transition from ${currentTime}ms / ${ANIMATION_DURATION_MS}ms`);
|
||||
|
||||
// If animation is already at or near the end, skip to finish immediately
|
||||
if (currentTime >= ANIMATION_DURATION_MS - 5) {
|
||||
console.log('Animation already complete, finishing immediately');
|
||||
|
||||
// Keep the preview visible by hiding the main image but showing the preview
|
||||
imgElement.style.opacity = '0';
|
||||
|
||||
@@ -495,7 +503,6 @@ export const swipeFeedback = (node: HTMLElement, options?: SwipeFeedbackOptions)
|
||||
};
|
||||
|
||||
const pointerUp = (event: PointerEvent) => {
|
||||
console.log('up', event);
|
||||
if (!isDragging || !event.isPrimary || (event.pointerType === 'mouse' && event.button !== 0)) {
|
||||
return;
|
||||
}
|
||||
@@ -551,23 +558,34 @@ export const swipeFeedback = (node: HTMLElement, options?: SwipeFeedbackOptions)
|
||||
|
||||
return {
|
||||
update(newOptions?: SwipeFeedbackOptions) {
|
||||
// Update imgElement if provided
|
||||
if (newOptions?.imageElement !== undefined) {
|
||||
imgElement = newOptions.imageElement;
|
||||
}
|
||||
|
||||
// Check if asset URL changed - if so, reset everything
|
||||
if (newOptions?.currentAssetUrl && newOptions.currentAssetUrl !== lastAssetUrl) {
|
||||
resetPreviewContainers();
|
||||
lastAssetUrl = newOptions.currentAssetUrl;
|
||||
}
|
||||
|
||||
const lastLeftPreviewUrl = options?.leftPreviewUrl;
|
||||
const lastRightPreviewUrl = options?.rightPreviewUrl;
|
||||
if (
|
||||
lastLeftPreviewUrl &&
|
||||
lastLeftPreviewUrl != newOptions?.leftPreviewUrl &&
|
||||
lastLeftPreviewUrl !== newOptions?.currentAssetUrl
|
||||
) {
|
||||
preloadManager.cancelUrl(lastLeftPreviewUrl);
|
||||
}
|
||||
if (
|
||||
lastRightPreviewUrl &&
|
||||
lastRightPreviewUrl != newOptions?.rightPreviewUrl &&
|
||||
lastRightPreviewUrl !== newOptions?.currentAssetUrl
|
||||
) {
|
||||
preloadManager.cancelUrl(lastRightPreviewUrl);
|
||||
}
|
||||
options = newOptions;
|
||||
|
||||
// Update or create left preview
|
||||
if (options?.leftPreviewUrl) {
|
||||
if (leftPreviewImg) {
|
||||
// Update existing
|
||||
|
||||
leftPreviewImg.src = options.leftPreviewUrl;
|
||||
} else if (!leftPreviewContainer) {
|
||||
// Create if doesn't exist
|
||||
@@ -582,6 +600,7 @@ export const swipeFeedback = (node: HTMLElement, options?: SwipeFeedbackOptions)
|
||||
if (options?.rightPreviewUrl) {
|
||||
if (rightPreviewImg) {
|
||||
// Update existing
|
||||
|
||||
rightPreviewImg.src = options.rightPreviewUrl;
|
||||
} else if (!rightPreviewContainer) {
|
||||
// Create if doesn't exist
|
||||
|
||||
@@ -7,13 +7,23 @@ import { thumbHashToRGBA } from 'thumbhash';
|
||||
* @param param1 object containing the base64 encoded hash (base64Thumbhash: yourString)
|
||||
*/
|
||||
export function thumbhash(canvas: HTMLCanvasElement, { base64ThumbHash }: { base64ThumbHash: string }) {
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (ctx) {
|
||||
const { w, h, rgba } = thumbHashToRGBA(decodeBase64(base64ThumbHash));
|
||||
const pixels = ctx.createImageData(w, h);
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
pixels.data.set(rgba);
|
||||
ctx.putImageData(pixels, 0, 0);
|
||||
}
|
||||
const render = (hash: string) => {
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (ctx) {
|
||||
const { w, h, rgba } = thumbHashToRGBA(decodeBase64(hash));
|
||||
const pixels = ctx.createImageData(w, h);
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
pixels.data.set(rgba);
|
||||
ctx.putImageData(pixels, 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
render(base64ThumbHash);
|
||||
|
||||
return {
|
||||
update({ base64ThumbHash: newHash }: { base64ThumbHash: string }) {
|
||||
render(newHash);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,48 +3,63 @@ import { useZoomImageWheel } from '@zoom-image/svelte';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export const zoomImageAction = (node: HTMLElement, options?: { disabled?: boolean }) => {
|
||||
const { createZoomImage, zoomImageState, setZoomImageState } = useZoomImageWheel();
|
||||
|
||||
createZoomImage(node, {
|
||||
maxZoom: 10,
|
||||
});
|
||||
|
||||
const state = get(photoZoomState);
|
||||
if (state) {
|
||||
setZoomImageState(state);
|
||||
}
|
||||
|
||||
node.style.overflow = 'visible';
|
||||
|
||||
// Store original event handlers so we can prevent them when disabled
|
||||
const wheelHandler = (event: WheelEvent) => {
|
||||
let unsubscribes: (() => void)[] = [];
|
||||
const createZoomAction = (newOptions?: { disabled?: boolean }) => {
|
||||
options = newOptions;
|
||||
if (options?.disabled) {
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
};
|
||||
|
||||
const disabledPointerDownHandler = (event: PointerEvent) => {
|
||||
if (options?.disabled) {
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
};
|
||||
|
||||
// Add handlers at capture phase with higher priority for disabled state
|
||||
node.addEventListener('wheel', wheelHandler, { capture: true });
|
||||
node.addEventListener('pointerdown', disabledPointerDownHandler, { capture: true });
|
||||
|
||||
const unsubscribes = [photoZoomState.subscribe(setZoomImageState), zoomImageState.subscribe(photoZoomState.set)];
|
||||
|
||||
return {
|
||||
update(newOptions?: { disabled?: boolean }) {
|
||||
options = newOptions;
|
||||
},
|
||||
destroy() {
|
||||
node.removeEventListener('wheel', wheelHandler, { capture: true });
|
||||
node.removeEventListener('pointerdown', disabledPointerDownHandler, { capture: true });
|
||||
for (const unsubscribe of unsubscribes) {
|
||||
unsubscribe();
|
||||
}
|
||||
unsubscribes = [];
|
||||
} else {
|
||||
const { createZoomImage, zoomImageState, setZoomImageState } = useZoomImageWheel();
|
||||
|
||||
createZoomImage(node, {
|
||||
maxZoom: 10,
|
||||
});
|
||||
|
||||
const state = get(photoZoomState);
|
||||
if (state) {
|
||||
setZoomImageState(state);
|
||||
}
|
||||
|
||||
node.style.overflow = 'visible';
|
||||
|
||||
// Store original event handlers so we can prevent them when disabled
|
||||
const wheelHandler = (event: WheelEvent) => {
|
||||
if (options?.disabled) {
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
};
|
||||
|
||||
const disabledPointerDownHandler = (event: PointerEvent) => {
|
||||
if (options?.disabled) {
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
};
|
||||
|
||||
// Add handlers at capture phase with higher priority for disabled state
|
||||
node.addEventListener('wheel', wheelHandler, { capture: true });
|
||||
node.addEventListener('pointerdown', disabledPointerDownHandler, { capture: true });
|
||||
|
||||
unsubscribes = [
|
||||
photoZoomState.subscribe(setZoomImageState),
|
||||
zoomImageState.subscribe(photoZoomState.set),
|
||||
() => node.removeEventListener('wheel', wheelHandler, { capture: true }),
|
||||
() => node.removeEventListener('pointerdown', disabledPointerDownHandler, { capture: true }),
|
||||
];
|
||||
}
|
||||
};
|
||||
createZoomAction();
|
||||
return {
|
||||
update(newOptions?: { disabled?: boolean }) {
|
||||
createZoomAction(newOptions);
|
||||
},
|
||||
destroy() {
|
||||
for (const unsubscribe of unsubscribes) {
|
||||
unsubscribe();
|
||||
}
|
||||
unsubscribes = [];
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
import {
|
||||
AssetJobName,
|
||||
AssetTypeEnum,
|
||||
getAllAlbums,
|
||||
getAssetInfo,
|
||||
getStack,
|
||||
runAssetJobs,
|
||||
@@ -97,12 +96,10 @@
|
||||
stopProgress: stopSlideshowProgress,
|
||||
slideshowNavigation,
|
||||
slideshowState,
|
||||
slideshowTransition,
|
||||
} = slideshowStore;
|
||||
const stackThumbnailSize = 60;
|
||||
const stackSelectedThumbnailSize = 65;
|
||||
|
||||
let appearsInAlbums: AlbumResponseDto[] = $state([]);
|
||||
let shouldPlayMotionPhoto = $state(false);
|
||||
let sharedLink = getSharedLink();
|
||||
let enableDetailPanel = asset.hasMetadata;
|
||||
@@ -116,11 +113,17 @@
|
||||
let selectedEditType: string = $state('');
|
||||
let stack: StackResponseDto | null = $state(null);
|
||||
|
||||
let slideShowPlaying = $derived($slideshowState === SlideshowState.PlaySlideshow);
|
||||
let slideShowAscending = $derived($slideshowNavigation === SlideshowNavigation.AscendingOrder);
|
||||
let slideShowShuffle = $derived($slideshowNavigation === SlideshowNavigation.Shuffle);
|
||||
|
||||
let zoomToggle = $state(() => void 0);
|
||||
let playOriginalVideo = $state($alwaysLoadOriginalVideo);
|
||||
|
||||
let nextSizeHint = $state<{ width: number; height: number } | null>(null);
|
||||
|
||||
let refreshAlbumsSignal = $state(0);
|
||||
|
||||
const setPlayOriginalVideo = (value: boolean) => {
|
||||
playOriginalVideo = value;
|
||||
};
|
||||
@@ -165,26 +168,23 @@
|
||||
let equirectangularTransitionName = $state<string | null>();
|
||||
let detailPanelTransitionName = $state<string | null>(null);
|
||||
|
||||
if (viewTransitionManager.activeViewTransition) {
|
||||
transitionName = 'hero';
|
||||
console.log('setting name initial');
|
||||
equirectangularTransitionName = 'hero';
|
||||
}
|
||||
let addInfoTransition;
|
||||
let finished;
|
||||
onMount(async () => {
|
||||
onMount(() => {
|
||||
addInfoTransition = () => {
|
||||
detailPanelTransitionName = 'info';
|
||||
transitionName = 'hero';
|
||||
equirectangularTransitionName = 'hero';
|
||||
console.log('transitioned');
|
||||
};
|
||||
eventManager.on('TransitionToAssetViewer', addInfoTransition);
|
||||
eventManager.on('TransitionToTimeline', addInfoTransition);
|
||||
finished = () => {
|
||||
detailPanelTransitionName = null;
|
||||
transitionName = null;
|
||||
console.log('setting null');
|
||||
};
|
||||
eventManager.on('Finished', finished);
|
||||
// eventManager.emit('AssetViewerLoaded');
|
||||
|
||||
unsubscribes.push(
|
||||
websocketEvents.on('on_upload_success', (asset) => onAssetUpdate({ event: 'upload', asset })),
|
||||
websocketEvents.on('on_asset_update', (asset) => onAssetUpdate({ event: 'update', asset })),
|
||||
@@ -206,10 +206,6 @@
|
||||
slideshowHistory.queue(toTimelineAsset(asset));
|
||||
}
|
||||
});
|
||||
|
||||
if (!sharedLink) {
|
||||
await handleGetAllAlbums();
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
@@ -231,18 +227,6 @@
|
||||
eventManager.off('Finished', finished!);
|
||||
});
|
||||
|
||||
const handleGetAllAlbums = async () => {
|
||||
if (authManager.isSharedLink) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
appearsInAlbums = await getAllAlbums({ assetId: asset.id });
|
||||
} catch (error) {
|
||||
console.error('Error getting album that asset belong to', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpenActivity = () => {
|
||||
if ($isShowDetail) {
|
||||
$isShowDetail = false;
|
||||
@@ -266,33 +250,43 @@
|
||||
});
|
||||
};
|
||||
|
||||
const startTransition = async (targetTransition: string | null, targetAsset?: AssetResponseDto) => {
|
||||
const startTransition = async (
|
||||
types: string[],
|
||||
targetTransition: string | null,
|
||||
targetAsset: AssetResponseDto | null,
|
||||
navigateFn: () => Promise<boolean>,
|
||||
) => {
|
||||
transitionName = viewTransitionManager.getTransitionName('old', targetTransition);
|
||||
console.log('transitionName', transitionName);
|
||||
|
||||
equirectangularTransitionName = viewTransitionManager.getTransitionName('old', targetTransition);
|
||||
detailPanelTransitionName = 'detail-panel';
|
||||
await tick();
|
||||
debugger;
|
||||
viewTransitionManager.startTransition(
|
||||
new Promise<void>((resolve) => {
|
||||
eventManager.once('StartViewTransition', () => {
|
||||
transitionName = viewTransitionManager.getTransitionName('new', targetTransition);
|
||||
console.log(transitionName);
|
||||
if (targetAsset && isEquirectangular(asset) && !isEquirectangular(targetAsset)) {
|
||||
equirectangularTransitionName = null;
|
||||
}
|
||||
});
|
||||
eventManager.once('AssetViewerFree', () => tick().then(resolve()));
|
||||
}),
|
||||
);
|
||||
|
||||
const navigationResult = new Promise<boolean>((navigationResolve) => {
|
||||
viewTransitionManager.startTransition(
|
||||
new Promise<void>((resolve) => {
|
||||
eventManager.once('StartViewTransition', async () => {
|
||||
transitionName = viewTransitionManager.getTransitionName('new', targetTransition);
|
||||
if (targetAsset && isEquirectangular(asset) && !isEquirectangular(targetAsset)) {
|
||||
equirectangularTransitionName = null;
|
||||
}
|
||||
await tick();
|
||||
navigationResolve(await navigateFn());
|
||||
});
|
||||
eventManager.once('AssetViewerFree', () => tick().then(resolve));
|
||||
}),
|
||||
types,
|
||||
);
|
||||
});
|
||||
return navigationResult;
|
||||
};
|
||||
|
||||
const tracker = new InvocationTracker();
|
||||
|
||||
const navigateAsset = (order?: 'previous' | 'next', skipTransition: boolean = false) => {
|
||||
if (!order) {
|
||||
if ($slideshowState === SlideshowState.PlaySlideshow) {
|
||||
order = $slideshowNavigation === SlideshowNavigation.AscendingOrder ? 'previous' : 'next';
|
||||
if (slideShowPlaying) {
|
||||
order = slideShowAscending ? 'previous' : 'next';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@@ -302,49 +296,53 @@
|
||||
return;
|
||||
}
|
||||
|
||||
void tracker.invoke(async () => {
|
||||
let skipped = false;
|
||||
if (viewTransitionManager.skipTransitions()) {
|
||||
await tick();
|
||||
skipped = true;
|
||||
console.log('was skipped');
|
||||
}
|
||||
let skipped = false;
|
||||
if (viewTransitionManager.skipTransitions()) {
|
||||
skipped = true;
|
||||
}
|
||||
|
||||
void tracker.invoke(async () => {
|
||||
let hasNext = false;
|
||||
if ($slideshowState === SlideshowState.PlaySlideshow && $slideshowNavigation === SlideshowNavigation.Shuffle) {
|
||||
console.log('$slideshowState', $slideshowState, skipTransition);
|
||||
if (!skipTransition) {
|
||||
await startTransition('slideshow', undefined);
|
||||
}
|
||||
hasNext = order === 'previous' ? slideshowHistory.previous() : slideshowHistory.next();
|
||||
if (!hasNext) {
|
||||
const asset = await onRandom?.();
|
||||
if (asset) {
|
||||
slideshowHistory.queue(asset);
|
||||
hasNext = true;
|
||||
if (slideShowPlaying && slideShowShuffle) {
|
||||
const navigate = async () => {
|
||||
let next = order === 'previous' ? slideshowHistory.previous() : slideshowHistory.next();
|
||||
if (!next) {
|
||||
const asset = await onRandom?.();
|
||||
if (asset) {
|
||||
slideshowHistory.queue(asset);
|
||||
next = true;
|
||||
}
|
||||
}
|
||||
return next;
|
||||
};
|
||||
// eslint-disable-next-line unicorn/prefer-ternary
|
||||
if (viewTransitionManager.isSupported() && !skipped && !skipTransition) {
|
||||
hasNext = await startTransition(['slideshow'], null, null, navigate);
|
||||
} else {
|
||||
hasNext = await navigate();
|
||||
}
|
||||
} else if (onNavigateToAsset) {
|
||||
// only transition if the target is already preloaded, and is in a secure context
|
||||
const targetAsset = order === 'previous' ? previousAsset : nextAsset;
|
||||
const preloaded = await preloadManager.isPreloaded(targetAsset);
|
||||
|
||||
if (!skipTransition && !!targetAsset && globalThis.isSecureContext && preloaded) {
|
||||
const targetTransition = $slideshowState === SlideshowState.PlaySlideshow ? null : order;
|
||||
console.log('sta', $slideshowState);
|
||||
await startTransition(targetTransition, targetAsset);
|
||||
const navigate = async () =>
|
||||
order === 'previous' ? await onNavigateToAsset(previousAsset) : await onNavigateToAsset(nextAsset);
|
||||
if (viewTransitionManager.isSupported() && !skipped && !skipTransition && !!targetAsset) {
|
||||
const targetTransition = slideShowPlaying ? null : order;
|
||||
hasNext = await startTransition(
|
||||
slideShowPlaying ? ['slideshow'] : ['viewer-nav'],
|
||||
targetTransition,
|
||||
targetAsset,
|
||||
navigate,
|
||||
);
|
||||
} else {
|
||||
console.log('not');
|
||||
hasNext = await navigate();
|
||||
}
|
||||
resetZoomState();
|
||||
console.log('about to');
|
||||
hasNext = order === 'previous' ? await onNavigateToAsset(previousAsset) : await onNavigateToAsset(nextAsset);
|
||||
console.log('done to');
|
||||
} else {
|
||||
hasNext = false;
|
||||
}
|
||||
|
||||
if ($slideshowState === SlideshowState.PlaySlideshow) {
|
||||
if (slideShowPlaying) {
|
||||
if (hasNext) {
|
||||
$restartSlideshowProgress = true;
|
||||
} else {
|
||||
@@ -411,7 +409,7 @@
|
||||
const handleAction = async (action: Action) => {
|
||||
switch (action.type) {
|
||||
case AssetAction.ADD_TO_ALBUM: {
|
||||
await handleGetAllAlbums();
|
||||
refreshAlbumsSignal++;
|
||||
break;
|
||||
}
|
||||
case AssetAction.REMOVE_ASSET_FROM_STACK: {
|
||||
@@ -454,14 +452,6 @@
|
||||
await goto(`${AppRoute.PHOTOS}/${newAssetId}`);
|
||||
};
|
||||
|
||||
const handleAboutToNavigate = (target: { direction: 'left' | 'right'; nextWidth: number; nextHeight: number }) => {
|
||||
nextSizeHint = {
|
||||
width: target.nextWidth,
|
||||
height: target.nextHeight,
|
||||
};
|
||||
console.log('setting', nextSizeHint);
|
||||
};
|
||||
|
||||
let isFullScreen = $derived(fullscreenElement !== null);
|
||||
|
||||
$effect(() => {
|
||||
@@ -479,7 +469,6 @@
|
||||
$effect(() => {
|
||||
const refresh = async () => {
|
||||
await refreshStack();
|
||||
await handleGetAllAlbums();
|
||||
ocrManager.clear();
|
||||
if (!sharedLink) {
|
||||
if (previewStackedAsset) {
|
||||
@@ -504,6 +493,7 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
asset.id;
|
||||
if (viewerKind !== 'PhotoViewer' && viewerKind !== 'ImagePanaramaViewer' && viewerKind !== 'VideoViewer') {
|
||||
console.log('EMMITTTTT');
|
||||
eventManager.emit('AssetViewerFree');
|
||||
}
|
||||
});
|
||||
@@ -592,7 +582,10 @@
|
||||
{/if}
|
||||
|
||||
{#if $slideshowState === SlideshowState.None && showNavigation && !isShowEditor && previousAsset}
|
||||
<div class="my-auto column-span-1 col-start-1 row-span-full row-start-1 justify-self-start">
|
||||
<div
|
||||
class="my-auto column-span-1 col-start-1 row-span-full row-start-1 justify-self-start"
|
||||
style:view-transition-name="exclude-leftbutton"
|
||||
>
|
||||
<PreviousAssetAction onPreviousAsset={() => navigateAsset('previous')} />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -608,7 +601,6 @@
|
||||
{nextAsset}
|
||||
{previousAsset}
|
||||
{nextSizeHint}
|
||||
onAboutToNavigate={handleAboutToNavigate}
|
||||
onPreviousAsset={() => navigateAsset('previous', true)}
|
||||
onNextAsset={() => navigateAsset('next', true)}
|
||||
{sharedLink}
|
||||
@@ -616,6 +608,7 @@
|
||||
{:else if viewerKind === 'StackVideoViewer'}
|
||||
<VideoViewer
|
||||
{transitionName}
|
||||
{asset}
|
||||
assetId={previewStackedAsset!.id}
|
||||
{nextAsset}
|
||||
{previousAsset}
|
||||
@@ -633,6 +626,7 @@
|
||||
{:else if viewerKind === 'LiveVideoViewer'}
|
||||
<VideoViewer
|
||||
{transitionName}
|
||||
{asset}
|
||||
assetId={asset.livePhotoVideoId!}
|
||||
{nextAsset}
|
||||
{previousAsset}
|
||||
@@ -659,15 +653,15 @@
|
||||
{nextAsset}
|
||||
{previousAsset}
|
||||
{nextSizeHint}
|
||||
onAboutToNavigate={handleAboutToNavigate}
|
||||
onPreviousAsset={() => navigateAsset('previous', true)}
|
||||
onNextAsset={() => navigateAsset('next', true)}
|
||||
{sharedLink}
|
||||
onFree={() => eventManager.emit('AssetViewerFree')}
|
||||
onReady={() => eventManager.emit('AssetViewerFree')}
|
||||
/>
|
||||
{:else if viewerKind === 'VideoViewer'}
|
||||
<VideoViewer
|
||||
{transitionName}
|
||||
{asset}
|
||||
assetId={asset.id}
|
||||
{nextAsset}
|
||||
{previousAsset}
|
||||
@@ -706,7 +700,10 @@
|
||||
</div>
|
||||
|
||||
{#if $slideshowState === SlideshowState.None && showNavigation && !isShowEditor && nextAsset}
|
||||
<div class="my-auto col-span-1 col-start-4 row-span-full row-start-1 justify-self-end">
|
||||
<div
|
||||
class="my-auto col-span-1 col-start-4 row-span-full row-start-1 justify-self-end"
|
||||
style:view-transition-name="exclude-rightbutton"
|
||||
>
|
||||
<NextAssetAction onNextAsset={() => navigateAsset('next')} />
|
||||
</div>
|
||||
{/if}
|
||||
@@ -719,7 +716,7 @@
|
||||
class="row-start-1 row-span-4 w-[360px] overflow-y-auto transition-all dark:border-l dark:border-s-immich-dark-gray bg-light"
|
||||
translate="yes"
|
||||
>
|
||||
<DetailPanel {asset} currentAlbum={album} albums={appearsInAlbums} onClose={() => ($isShowDetail = false)} />
|
||||
<DetailPanel {asset} {refreshAlbumsSignal} currentAlbum={album} onClose={() => ($isShowDetail = false)} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -13,13 +13,19 @@
|
||||
import { boundingBoxesArray } from '$lib/stores/people.store';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { preferences, user } from '$lib/stores/user.store';
|
||||
import { getAssetThumbnailUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetThumbnailUrl, getPeopleThumbnailUrl, handlePromiseError } from '$lib/utils';
|
||||
import { delay, getDimensions } from '$lib/utils/asset-utils';
|
||||
import { getByteUnitString } from '$lib/utils/byte-units';
|
||||
import { getMetadataSearchQuery } from '$lib/utils/metadata-search';
|
||||
import { fromISODateTime, fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { getParentPath } from '$lib/utils/tree-utils';
|
||||
import { AssetMediaSize, getAssetInfo, type AlbumResponseDto, type AssetResponseDto } from '@immich/sdk';
|
||||
import {
|
||||
AssetMediaSize,
|
||||
getAllAlbums,
|
||||
getAssetInfo,
|
||||
type AlbumResponseDto,
|
||||
type AssetResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { Icon, IconButton, LoadingSpinner, modalManager } from '@immich/ui';
|
||||
import {
|
||||
mdiCalendar,
|
||||
@@ -43,12 +49,12 @@
|
||||
|
||||
interface Props {
|
||||
asset: AssetResponseDto;
|
||||
albums?: AlbumResponseDto[];
|
||||
currentAlbum?: AlbumResponseDto | null;
|
||||
refreshAlbumsSignal?: number;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
let { asset, albums = [], currentAlbum = null, onClose }: Props = $props();
|
||||
let { asset, refreshAlbumsSignal = 0, currentAlbum = null, onClose }: Props = $props();
|
||||
|
||||
let showAssetPath = $state(false);
|
||||
let showEditFaces = $state(false);
|
||||
@@ -74,6 +80,17 @@
|
||||
);
|
||||
let previousId: string | undefined = $state();
|
||||
|
||||
let albums = $state<AlbumResponseDto[]>([]);
|
||||
|
||||
$effect(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
refreshAlbumsSignal;
|
||||
if (authManager.isSharedLink) {
|
||||
return;
|
||||
}
|
||||
handlePromiseError(getAllAlbums({ assetId: asset.id }).then((response) => (albums = response)));
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
if (!previousId) {
|
||||
previousId = asset.id;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
interface Props {
|
||||
htmlElement: HTMLImageElement | HTMLVideoElement;
|
||||
htmlElement: HTMLImageElement | HTMLVideoElement | undefined | null;
|
||||
containerWidth: number;
|
||||
containerHeight: number;
|
||||
assetId: string;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { shortcuts } from '$lib/actions/shortcut';
|
||||
import { swipeFeedback } from '$lib/actions/swipe-feedback';
|
||||
import { thumbhash } from '$lib/actions/thumbhash';
|
||||
import { zoomImageAction } from '$lib/actions/zoom-image';
|
||||
import FaceEditor from '$lib/components/asset-viewer/face-editor/face-editor.svelte';
|
||||
import OcrBoundingBox from '$lib/components/asset-viewer/ocr-bounding-box.svelte';
|
||||
@@ -11,20 +12,30 @@
|
||||
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
||||
import { ocrManager } from '$lib/stores/ocr.svelte';
|
||||
import { boundingBoxesArray } from '$lib/stores/people.store';
|
||||
import { SlideshowLook, SlideshowState, slideshowLookCssMapping, slideshowStore } from '$lib/stores/slideshow.store';
|
||||
import { SlideshowLook, slideshowLookCssMapping, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
||||
import { photoZoomState, resetZoomState } from '$lib/stores/zoom-image.store';
|
||||
import { getAssetUrl, targetImageSize as getTargetImageSize, handlePromiseError } from '$lib/utils';
|
||||
import { canCopyImageToClipboard, copyImageToClipboard } from '$lib/utils/asset-utils';
|
||||
import {
|
||||
getAssetThumbnailUrl,
|
||||
getAssetUrl,
|
||||
targetImageSize as getTargetImageSize,
|
||||
handlePromiseError,
|
||||
} from '$lib/utils';
|
||||
import { canCopyImageToClipboard, copyImageToClipboard, getDimensions } from '$lib/utils/asset-utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { scaleToFit } from '$lib/utils/layout-utils';
|
||||
import { getOcrBoundingBoxes } from '$lib/utils/ocr-utils';
|
||||
import { getBoundingBox } from '$lib/utils/people-utils';
|
||||
import { cancelImageUrl } from '$lib/utils/sw-messaging';
|
||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { TUNABLES } from '$lib/utils/tunables';
|
||||
import { AssetMediaSize, type AssetResponseDto, type SharedLinkResponseDto } from '@immich/sdk';
|
||||
import { LoadingSpinner, toastManager } from '@immich/ui';
|
||||
import { onDestroy, onMount, untrack } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
let {
|
||||
IMAGE_THUMBNAIL: { THUMBHASH_FADE_DURATION },
|
||||
} = TUNABLES;
|
||||
|
||||
interface Props {
|
||||
transitionName?: string | null | undefined;
|
||||
@@ -34,19 +45,10 @@
|
||||
element?: HTMLDivElement;
|
||||
sharedLink?: SharedLinkResponseDto;
|
||||
nextSizeHint?: { width: number; height: number } | null;
|
||||
onAboutToNavigate?: ({
|
||||
direction,
|
||||
nextWidth,
|
||||
nextHeight,
|
||||
}: {
|
||||
direction: 'left' | 'right';
|
||||
nextWidth: number;
|
||||
nextHeight: number;
|
||||
}) => void;
|
||||
|
||||
onPreviousAsset?: (() => void) | null;
|
||||
onNextAsset?: (() => void) | null;
|
||||
onLoad?: (() => void) | null;
|
||||
onError?: (() => void) | null;
|
||||
onReady?: (() => void) | null;
|
||||
onBusy?: (() => void) | null;
|
||||
onFree?: (() => void) | null;
|
||||
copyImage?: () => Promise<void>;
|
||||
@@ -61,11 +63,10 @@
|
||||
element = $bindable(),
|
||||
sharedLink,
|
||||
nextSizeHint,
|
||||
onAboutToNavigate,
|
||||
|
||||
onPreviousAsset = null,
|
||||
onNextAsset = null,
|
||||
onLoad,
|
||||
onError,
|
||||
onReady,
|
||||
onBusy,
|
||||
onFree,
|
||||
copyImage = $bindable(),
|
||||
@@ -79,16 +80,32 @@
|
||||
let imageError: boolean = $state(false);
|
||||
|
||||
let loader = $state<HTMLImageElement>();
|
||||
$effect(() => {
|
||||
if (loader) {
|
||||
const _loader = loader;
|
||||
const _src = loader.src;
|
||||
const _imageLoaderUrl = imageLoaderUrl;
|
||||
_loader.onload = () => {
|
||||
if (_loader.src === _src && imageLoaderUrl === _imageLoaderUrl) {
|
||||
onload();
|
||||
}
|
||||
};
|
||||
_loader.onerror = () => {
|
||||
if (_loader.src === _src && imageLoaderUrl === _imageLoaderUrl) {
|
||||
onerror();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
resetZoomState();
|
||||
|
||||
onDestroy(() => {
|
||||
$boundingBoxesArray = [];
|
||||
});
|
||||
$inspect(transitionName).with(console.log.bind(null, 'transit'));
|
||||
|
||||
const box = $derived.by(() => {
|
||||
const { width, height } = scaleToFit(naturalWidth, naturalHeight, containerWidth, containerHeight);
|
||||
const { width, height } = scaledDimensions;
|
||||
return {
|
||||
width: width + 'px',
|
||||
height: height + 'px',
|
||||
@@ -97,6 +114,63 @@
|
||||
};
|
||||
});
|
||||
|
||||
const blurredSlideshow = $derived(
|
||||
$slideshowState !== SlideshowState.None && $slideshowLook === SlideshowLook.BlurredBackground && asset.thumbhash,
|
||||
);
|
||||
const transitionLetterboxLeft = $derived(transitionName === 'hero' || blurredSlideshow ? null : 'letterbox-left');
|
||||
const transitionLetterboxRight = $derived(transitionName === 'hero' || blurredSlideshow ? null : 'letterbox-right');
|
||||
const transitionLetterboxTop = $derived(transitionName === 'hero' || blurredSlideshow ? null : 'letterbox-top');
|
||||
const transitionLetterboxBottom = $derived(transitionName === 'hero' || blurredSlideshow ? null : 'letterbox-bottom');
|
||||
|
||||
// Letterbox regions (the empty space around the main box)
|
||||
const letterboxLeft = $derived.by(() => {
|
||||
const { width } = scaledDimensions;
|
||||
const leftOffset = (containerWidth - width) / 2;
|
||||
return {
|
||||
width: leftOffset + 'px',
|
||||
height: containerHeight + 'px',
|
||||
left: '0px',
|
||||
top: '0px',
|
||||
};
|
||||
});
|
||||
|
||||
const letterboxRight = $derived.by(() => {
|
||||
const { width } = scaledDimensions;
|
||||
const leftOffset = (containerWidth - width) / 2;
|
||||
const rightOffset = leftOffset;
|
||||
return {
|
||||
width: rightOffset + 'px',
|
||||
height: containerHeight + 'px',
|
||||
left: containerWidth - rightOffset + 'px',
|
||||
top: '0px',
|
||||
};
|
||||
});
|
||||
|
||||
const letterboxTop = $derived.by(() => {
|
||||
const { width, height } = scaledDimensions;
|
||||
const topOffset = (containerHeight - height) / 2;
|
||||
const leftOffset = (containerWidth - width) / 2;
|
||||
return {
|
||||
width: width + 'px',
|
||||
height: topOffset + 'px',
|
||||
left: leftOffset + 'px',
|
||||
top: '0px',
|
||||
};
|
||||
});
|
||||
|
||||
const letterboxBottom = $derived.by(() => {
|
||||
const { width, height } = scaledDimensions;
|
||||
const topOffset = (containerHeight - height) / 2;
|
||||
const bottomOffset = topOffset;
|
||||
const leftOffset = (containerWidth - width) / 2;
|
||||
return {
|
||||
width: width + 'px',
|
||||
height: bottomOffset + 'px',
|
||||
left: leftOffset + 'px',
|
||||
top: containerHeight - bottomOffset + 'px',
|
||||
};
|
||||
});
|
||||
|
||||
let ocrBoxes = $derived(
|
||||
ocrManager.showOverlay && $photoViewerImgElement
|
||||
? getOcrBoundingBoxes(ocrManager.data, $photoZoomState, $photoViewerImgElement)
|
||||
@@ -105,29 +179,6 @@
|
||||
|
||||
let isOcrActive = $derived(ocrManager.showOverlay);
|
||||
|
||||
const handlePreCommit = (direction: 'left' | 'right', nextWidth: number, nextHeight: number) => {
|
||||
// Scale the preview dimensions to fit within the viewport (like object-fit: contain)
|
||||
// This prevents flashing when small images are scaled up by scaleToFit
|
||||
|
||||
let width = nextWidth;
|
||||
let height = nextHeight;
|
||||
if (direction === 'right' && nextAsset?.exifInfo?.exifImageWidth && nextAsset?.exifInfo?.exifImageHeight) {
|
||||
width = nextAsset.exifInfo.exifImageWidth;
|
||||
height = nextAsset.exifInfo.exifImageHeight;
|
||||
} else if (
|
||||
direction === 'left' &&
|
||||
previousAsset?.exifInfo?.exifImageWidth &&
|
||||
previousAsset?.exifInfo?.exifImageHeight
|
||||
) {
|
||||
width = previousAsset.exifInfo.exifImageWidth;
|
||||
height = previousAsset.exifInfo.exifImageHeight;
|
||||
}
|
||||
const box = scaleToFit(width, height, containerWidth, containerHeight);
|
||||
console.log('nextSize', nextWidth, nextHeight, box);
|
||||
// onAboutToNavigate?.({ direction, nextWidth: scaledWidth, nextHeight: scaledHeight });
|
||||
onAboutToNavigate?.({ direction, nextWidth: box.width, nextHeight: box.height });
|
||||
};
|
||||
|
||||
const handleSwipeCommit = (direction: 'left' | 'right') => {
|
||||
if (direction === 'left' && onNextAsset) {
|
||||
// Swiped left, go to next asset
|
||||
@@ -196,30 +247,45 @@
|
||||
}
|
||||
};
|
||||
|
||||
let lastFreedUrl: string | undefined | null;
|
||||
const notifyFree = () => {
|
||||
if (lastFreedUrl !== imageLoaderUrl) {
|
||||
onFree?.();
|
||||
lastFreedUrl = imageLoaderUrl;
|
||||
}
|
||||
};
|
||||
|
||||
const notifyReady = () => {
|
||||
onReady?.();
|
||||
};
|
||||
|
||||
const onload = () => {
|
||||
onLoad?.();
|
||||
onFree?.();
|
||||
imageLoaded = true;
|
||||
naturalWidth = loader?.naturalWidth ?? 1;
|
||||
naturalHeight = loader?.naturalHeight ?? 1;
|
||||
notifyFree();
|
||||
dimensions = {
|
||||
width: loader?.naturalWidth ?? 1,
|
||||
height: loader?.naturalHeight ?? 1,
|
||||
};
|
||||
originalImageLoaded = targetImageSize === AssetMediaSize.Fullsize || targetImageSize === 'original';
|
||||
};
|
||||
|
||||
const onerror = () => {
|
||||
onError?.();
|
||||
onFree?.();
|
||||
naturalWidth = loader?.naturalWidth ?? 1;
|
||||
naturalHeight = loader?.naturalHeight ?? 1;
|
||||
imageError = imageLoaded = true;
|
||||
notifyFree();
|
||||
dimensions = {
|
||||
width: loader?.naturalWidth ?? 1,
|
||||
height: loader?.naturalHeight ?? 1,
|
||||
};
|
||||
imageError = true;
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
notifyReady();
|
||||
return () => {
|
||||
if (!imageLoaded && !imageError) {
|
||||
onFree?.();
|
||||
notifyFree();
|
||||
}
|
||||
if (imageLoaderUrl) {
|
||||
preloadManager.cancelPreloadUrl(imageLoaderUrl);
|
||||
preloadManager.cancelUrl(imageLoaderUrl);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -229,38 +295,56 @@
|
||||
);
|
||||
const previousAssetUrl = $derived(getAssetUrl({ asset: previousAsset, sharedLink }));
|
||||
const nextAssetUrl = $derived(getAssetUrl({ asset: nextAsset, sharedLink }));
|
||||
const thumbnailUrl = $derived(
|
||||
getAssetThumbnailUrl({
|
||||
id: asset.id,
|
||||
size: AssetMediaSize.Thumbnail,
|
||||
cacheKey: asset.thumbhash,
|
||||
}),
|
||||
);
|
||||
let thumbnailPreloaded = $state(false);
|
||||
$effect(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
asset;
|
||||
untrack(() => {
|
||||
void preloadManager.isUrlPreloaded(thumbnailUrl).then((preloaded) => (thumbnailPreloaded = preloaded));
|
||||
});
|
||||
});
|
||||
|
||||
let containerWidth = $state(0);
|
||||
let containerHeight = $state(0);
|
||||
let naturalWidth = $derived(nextSizeHint?.width ?? 1);
|
||||
let naturalHeight = $derived(nextSizeHint?.height ?? 1);
|
||||
const exifDimensions = $derived(
|
||||
asset.exifInfo?.exifImageHeight && asset.exifInfo.exifImageHeight
|
||||
? (getDimensions(asset.exifInfo) as { width: number; height: number })
|
||||
: null,
|
||||
);
|
||||
let dimensions = $derived(nextSizeHint ?? exifDimensions ?? { width: 1, height: 1 });
|
||||
const scaledDimensions = $derived(scaleToFit(dimensions, containerWidth, containerHeight));
|
||||
|
||||
let lastUrl: string | undefined | null;
|
||||
let lastPreviousUrl: string | undefined | null;
|
||||
let lastNextUrl: string | undefined | null;
|
||||
|
||||
$effect(() => {
|
||||
if (!lastUrl) {
|
||||
untrack(() => onBusy?.());
|
||||
}
|
||||
if (lastUrl && lastUrl !== imageLoaderUrl) {
|
||||
if (lastUrl !== imageLoaderUrl && imageLoaderUrl) {
|
||||
untrack(() => {
|
||||
const isPreviewedImage = imageLoaderUrl === lastPreviousUrl || imageLoaderUrl === lastNextUrl;
|
||||
|
||||
if (!isPreviewedImage) {
|
||||
// It is a previewed image - prevent flicker - skip spinner but still let loader go through lifecycle
|
||||
imageLoaded = false;
|
||||
}
|
||||
|
||||
imageLoaded = false;
|
||||
originalImageLoaded = false;
|
||||
imageError = false;
|
||||
cancelImageUrl(lastUrl);
|
||||
onBusy?.();
|
||||
|
||||
notifyReady();
|
||||
});
|
||||
}
|
||||
|
||||
lastUrl = imageLoaderUrl;
|
||||
lastPreviousUrl = previousAssetUrl;
|
||||
lastNextUrl = nextAssetUrl;
|
||||
});
|
||||
$effect(() => {
|
||||
$photoViewerImgElement = loader;
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:document
|
||||
@@ -272,12 +356,8 @@
|
||||
{ shortcut: { key: 'z' }, onShortcut: zoomToggle, preventDefault: false },
|
||||
]}
|
||||
/>
|
||||
{#if imageError}
|
||||
<div class="h-full w-full">
|
||||
<BrokenAsset class="text-xl h-full w-full" />
|
||||
</div>
|
||||
{/if}
|
||||
<img bind:this={loader} style="display:none" src={imageLoaderUrl} alt="" aria-hidden="true" {onload} {onerror} />
|
||||
|
||||
<!-- <img bind:this={loader} style="display:none" src={imageLoaderUrl} alt="" aria-hidden="true" {onload} {onerror} /> -->
|
||||
<div
|
||||
bind:this={element}
|
||||
class="absolute h-full w-full select-none"
|
||||
@@ -285,64 +365,118 @@
|
||||
bind:clientHeight={containerHeight}
|
||||
use:swipeFeedback={{
|
||||
disabled: isOcrActive || $photoZoomState.currentZoom > 1,
|
||||
onPreCommit: handlePreCommit,
|
||||
onSwipeCommit: handleSwipeCommit,
|
||||
leftPreviewUrl: previousAssetUrl,
|
||||
rightPreviewUrl: nextAssetUrl,
|
||||
currentAssetUrl: imageLoaderUrl,
|
||||
imageElement: $photoViewerImgElement,
|
||||
}}
|
||||
>
|
||||
{#if !imageLoaded}
|
||||
<div id="spinner" class="flex h-full items-center justify-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
{:else if !imageError}
|
||||
{#if $slideshowState !== SlideshowState.None && $slideshowLook === SlideshowLook.BlurredBackground}
|
||||
<img
|
||||
src={imageLoaderUrl}
|
||||
alt=""
|
||||
class="-z-1 absolute top-0 start-0 object-cover h-full w-full blur-lg"
|
||||
draggable="false"
|
||||
/>
|
||||
{#if blurredSlideshow}
|
||||
<canvas
|
||||
id="test"
|
||||
use:thumbhash={{ base64ThumbHash: asset.thumbhash! }}
|
||||
class="-z-1 absolute top-0 left-0 start-0 h-dvh w-dvw"
|
||||
></canvas>
|
||||
{/if}
|
||||
<div
|
||||
class="absolute"
|
||||
style:view-transition-name={transitionLetterboxLeft}
|
||||
style:left={letterboxLeft.left}
|
||||
style:top={letterboxLeft.top}
|
||||
style:width={letterboxLeft.width}
|
||||
style:height={letterboxLeft.height}
|
||||
></div>
|
||||
<div
|
||||
class="absolute"
|
||||
style:view-transition-name={transitionLetterboxRight}
|
||||
style:left={letterboxRight.left}
|
||||
style:top={letterboxRight.top}
|
||||
style:width={letterboxRight.width}
|
||||
style:height={letterboxRight.height}
|
||||
></div>
|
||||
<div
|
||||
class="absolute"
|
||||
style:view-transition-name={transitionLetterboxTop}
|
||||
style:left={letterboxTop.left}
|
||||
style:top={letterboxTop.top}
|
||||
style:width={letterboxTop.width}
|
||||
style:height={letterboxTop.height}
|
||||
></div>
|
||||
<div
|
||||
class="absolute"
|
||||
style:view-transition-name={transitionLetterboxBottom}
|
||||
style:left={letterboxBottom.left}
|
||||
style:top={letterboxBottom.top}
|
||||
style:width={letterboxBottom.width}
|
||||
style:height={letterboxBottom.height}
|
||||
></div>
|
||||
<div
|
||||
style:view-transition-name={transitionName}
|
||||
data-transition-name={transitionName}
|
||||
class="absolute"
|
||||
style:left={box.left}
|
||||
style:top={box.top}
|
||||
style:width={box.width}
|
||||
style:height={box.height}
|
||||
data-swipe-subject
|
||||
>
|
||||
{#if asset.thumbhash}
|
||||
<canvas data-blur use:thumbhash={{ base64ThumbHash: asset.thumbhash }} class="h-full w-full absolute -z-2"
|
||||
></canvas>
|
||||
{#if thumbnailPreloaded}
|
||||
<img src={thumbnailUrl} alt={$getAltText(toTimelineAsset(asset))} class="h-full w-full absolute -z-1" />
|
||||
{/if}
|
||||
{/if}
|
||||
<div
|
||||
use:zoomImageAction={{ disabled: isOcrActive }}
|
||||
style:width={box.width}
|
||||
style:height={box.height}
|
||||
style:left={box.left}
|
||||
style:top={box.top}
|
||||
style:overflow="visible"
|
||||
class="absolute"
|
||||
>
|
||||
<img
|
||||
style:view-transition-name={transitionName}
|
||||
bind:this={$photoViewerImgElement}
|
||||
src={imageLoaderUrl}
|
||||
alt={$getAltText(toTimelineAsset(asset))}
|
||||
class="w-full h-full {$slideshowState === SlideshowState.None
|
||||
? 'object-contain'
|
||||
: slideshowLookCssMapping[$slideshowLook]}"
|
||||
draggable="false"
|
||||
/>
|
||||
{#if !imageLoaded && !asset.thumbhash && !imageError}
|
||||
<div id="spinner" class="absolute flex h-full items-center justify-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
{/if}
|
||||
{#if imageError}
|
||||
<div class="h-full w-full">
|
||||
<BrokenAsset class="text-xl h-full w-full" />
|
||||
</div>
|
||||
{/if}
|
||||
{#key imageLoaderUrl}
|
||||
<div
|
||||
use:zoomImageAction={{ disabled: isOcrActive }}
|
||||
style:width={box.width}
|
||||
style:height={box.height}
|
||||
style:overflow="visible"
|
||||
class="absolute"
|
||||
>
|
||||
<img
|
||||
decoding="async"
|
||||
bind:this={loader}
|
||||
src={imageLoaderUrl}
|
||||
alt={$getAltText(toTimelineAsset(asset))}
|
||||
class={[
|
||||
'w-full',
|
||||
'h-full',
|
||||
$slideshowState === SlideshowState.None ? 'object-contain' : slideshowLookCssMapping[$slideshowLook],
|
||||
imageError && 'hidden',
|
||||
]}
|
||||
draggable="false"
|
||||
/>
|
||||
|
||||
<!-- eslint-disable-next-line svelte/require-each-key -->
|
||||
{#each getBoundingBox($boundingBoxesArray, $photoZoomState, $photoViewerImgElement) as boundingbox}
|
||||
<div
|
||||
class="absolute border-solid border-white border-3 rounded-lg"
|
||||
style="top: {boundingbox.top}px; left: {boundingbox.left}px; height: {boundingbox.height}px; width: {boundingbox.width}px;"
|
||||
></div>
|
||||
{/each}
|
||||
<!-- eslint-disable-next-line svelte/require-each-key -->
|
||||
{#each getBoundingBox($boundingBoxesArray, $photoZoomState, $photoViewerImgElement) as boundingbox}
|
||||
<div
|
||||
class="absolute border-solid border-white border-3 rounded-lg"
|
||||
style="top: {boundingbox.top}px; left: {boundingbox.left}px; height: {boundingbox.height}px; width: {boundingbox.width}px;"
|
||||
></div>
|
||||
{/each}
|
||||
|
||||
{#each ocrBoxes as ocrBox (ocrBox.id)}
|
||||
<OcrBoundingBox {ocrBox} />
|
||||
{/each}
|
||||
</div>
|
||||
{#each ocrBoxes as ocrBox (ocrBox.id)}
|
||||
<OcrBoundingBox {ocrBox} />
|
||||
{/each}
|
||||
</div>
|
||||
{/key}
|
||||
|
||||
{#if isFaceEditMode.value}
|
||||
<FaceEditor htmlElement={$photoViewerImgElement} {containerWidth} {containerHeight} assetId={asset.id} />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -355,4 +489,8 @@
|
||||
visibility: hidden;
|
||||
animation: 0s linear 0.4s forwards delayedVisibility;
|
||||
}
|
||||
[data-blur] {
|
||||
visibility: hidden;
|
||||
animation: 0s linear 0.1s forwards delayedVisibility;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
videoViewerVolume,
|
||||
} from '$lib/stores/preferences.store';
|
||||
import { getAssetOriginalUrl, getAssetPlaybackUrl, getAssetThumbnailUrl, getAssetUrl } from '$lib/utils';
|
||||
import { getDimensions } from '$lib/utils/asset-utils';
|
||||
import { scaleToFit } from '$lib/utils/layout-utils';
|
||||
import { AssetMediaSize, type AssetResponseDto, type SharedLinkResponseDto } from '@immich/sdk';
|
||||
import { LoadingSpinner } from '@immich/ui';
|
||||
@@ -21,6 +22,7 @@
|
||||
|
||||
interface Props {
|
||||
transitionName?: string | null;
|
||||
asset: AssetResponseDto;
|
||||
assetId: string;
|
||||
previousAsset?: AssetResponseDto;
|
||||
nextAsset?: AssetResponseDto;
|
||||
@@ -29,15 +31,6 @@
|
||||
loopVideo: boolean;
|
||||
cacheKey: string | null;
|
||||
playOriginalVideo: boolean;
|
||||
onAboutToNavigate?: ({
|
||||
direction,
|
||||
nextWidth,
|
||||
nextHeight,
|
||||
}: {
|
||||
direction: 'left' | 'right';
|
||||
nextWidth: number;
|
||||
nextHeight: number;
|
||||
}) => void;
|
||||
onPreviousAsset?: () => void;
|
||||
onNextAsset?: () => void;
|
||||
onVideoEnded?: () => void;
|
||||
@@ -47,6 +40,7 @@
|
||||
|
||||
let {
|
||||
transitionName,
|
||||
asset,
|
||||
assetId,
|
||||
previousAsset,
|
||||
nextAsset,
|
||||
@@ -55,7 +49,6 @@
|
||||
loopVideo,
|
||||
cacheKey,
|
||||
playOriginalVideo,
|
||||
onAboutToNavigate,
|
||||
onPreviousAsset = () => {},
|
||||
onNextAsset = () => {},
|
||||
onVideoEnded = () => {},
|
||||
@@ -73,10 +66,14 @@
|
||||
|
||||
let containerWidth = $state(document.documentElement.clientWidth);
|
||||
let containerHeight = $state(document.documentElement.clientHeight);
|
||||
let videoHeight = $derived(nextSizeHint?.height ?? 1);
|
||||
let videoWidth = $derived(nextSizeHint?.width ?? 1);
|
||||
$inspect(videoWidth).with(console.log.bind(null, 'vwidth'));
|
||||
console.log('next', nextSizeHint);
|
||||
const exifDimensions = $derived(
|
||||
asset.exifInfo?.exifImageHeight && asset.exifInfo.exifImageHeight
|
||||
? (getDimensions(asset.exifInfo) as { width: number; height: number })
|
||||
: null,
|
||||
);
|
||||
let dimensions = $derived(nextSizeHint ?? exifDimensions ?? { width: 1, height: 1 });
|
||||
const scaledDimensions = $derived(scaleToFit(dimensions, containerWidth, containerHeight));
|
||||
|
||||
onMount(() => {
|
||||
// Show video after mount to ensure fading in.
|
||||
showVideo = true;
|
||||
@@ -96,9 +93,10 @@
|
||||
});
|
||||
|
||||
const handleLoadedMetadata = () => {
|
||||
console.log('loaded', videoPlayer?.videoWidth);
|
||||
videoWidth = videoPlayer?.videoWidth ?? 1;
|
||||
videoHeight = videoPlayer?.videoHeight ?? 1;
|
||||
dimensions = {
|
||||
width: videoPlayer?.videoWidth ?? 1,
|
||||
height: videoPlayer?.videoHeight ?? 1,
|
||||
};
|
||||
eventManager.emit('AssetViewerFree');
|
||||
};
|
||||
|
||||
@@ -141,17 +139,6 @@
|
||||
}
|
||||
};
|
||||
|
||||
const handlePreCommit = (direction: 'left' | 'right', nextWidth: number, nextHeight: number) => {
|
||||
const { width: scaledWidth, height: scaledHeight } = scaleToFit(
|
||||
nextWidth,
|
||||
nextHeight,
|
||||
containerWidth,
|
||||
containerHeight,
|
||||
);
|
||||
|
||||
onAboutToNavigate?.({ direction, nextWidth: scaledWidth, nextHeight: scaledHeight });
|
||||
};
|
||||
|
||||
$effect(() => {
|
||||
if (isFaceEditMode.value) {
|
||||
videoPlayer?.pause();
|
||||
@@ -159,7 +146,7 @@
|
||||
});
|
||||
|
||||
const calculateSize = () => {
|
||||
const { width, height } = scaleToFit(videoWidth, videoHeight, containerWidth, containerHeight);
|
||||
const { width, height } = scaledDimensions;
|
||||
|
||||
const size = {
|
||||
width: width + 'px',
|
||||
@@ -188,16 +175,14 @@
|
||||
bind:clientWidth={containerWidth}
|
||||
bind:clientHeight={containerHeight}
|
||||
use:swipeFeedback={{
|
||||
onPreCommit: handlePreCommit,
|
||||
onSwipeCommit: handleSwipeCommit,
|
||||
leftPreviewUrl: previousAssetUrl,
|
||||
rightPreviewUrl: nextAssetUrl,
|
||||
currentAssetUrl: assetFileUrl,
|
||||
imageElement: videoPlayer,
|
||||
}}
|
||||
>
|
||||
{#if castManager.isCasting}
|
||||
<div class="place-content-center h-full place-items-center">
|
||||
<div class="place-content-center h-full place-items-center" data-swipe-subject>
|
||||
<VideoRemoteViewer
|
||||
poster={getAssetThumbnailUrl({ id: assetId, size: AssetMediaSize.Preview, cacheKey })}
|
||||
{onVideoStarted}
|
||||
@@ -206,7 +191,7 @@
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div>
|
||||
<div class="relative">
|
||||
<video
|
||||
style:view-transition-name={transitionName}
|
||||
style:height={box.height}
|
||||
@@ -231,15 +216,14 @@
|
||||
bind:volume={$videoViewerVolume}
|
||||
poster={getAssetThumbnailUrl({ id: assetId, size: AssetMediaSize.Preview, cacheKey })}
|
||||
src={assetFileUrl}
|
||||
data-swipe-subject
|
||||
>
|
||||
</video>
|
||||
|
||||
{#if isLoading}
|
||||
<div class="absolute flex place-content-center place-items-center">
|
||||
<div class="absolute inset-0 flex place-content-center place-items-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if isFaceEditMode.value}
|
||||
<FaceEditor htmlElement={videoPlayer} {containerWidth} {containerHeight} {assetId} />
|
||||
{/if}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
interface Props {
|
||||
transitionName?: string | null;
|
||||
asset: AssetResponseDto;
|
||||
assetId: string;
|
||||
previousAsset?: AssetResponseDto;
|
||||
nextAsset?: AssetResponseDto;
|
||||
@@ -15,15 +16,7 @@
|
||||
cacheKey: string | null;
|
||||
loopVideo: boolean;
|
||||
playOriginalVideo: boolean;
|
||||
onAboutToNavigate?: ({
|
||||
direction,
|
||||
nextWidth,
|
||||
nextHeight,
|
||||
}: {
|
||||
direction: 'left' | 'right';
|
||||
nextWidth: number;
|
||||
nextHeight: number;
|
||||
}) => void;
|
||||
|
||||
onClose?: () => void;
|
||||
onPreviousAsset?: () => void;
|
||||
onNextAsset?: () => void;
|
||||
@@ -33,6 +26,7 @@
|
||||
|
||||
let {
|
||||
transitionName,
|
||||
asset,
|
||||
assetId,
|
||||
previousAsset,
|
||||
nextAsset,
|
||||
@@ -42,7 +36,6 @@
|
||||
cacheKey,
|
||||
loopVideo,
|
||||
playOriginalVideo,
|
||||
onAboutToNavigate,
|
||||
onPreviousAsset,
|
||||
onClose,
|
||||
onNextAsset,
|
||||
@@ -58,13 +51,13 @@
|
||||
{transitionName}
|
||||
{loopVideo}
|
||||
{cacheKey}
|
||||
{asset}
|
||||
{assetId}
|
||||
{nextAsset}
|
||||
{sharedLink}
|
||||
{nextSizeHint}
|
||||
{previousAsset}
|
||||
{playOriginalVideo}
|
||||
{onAboutToNavigate}
|
||||
{onPreviousAsset}
|
||||
{onNextAsset}
|
||||
{onVideoEnded}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { preloadManager } from '$lib/managers/PreloadManager.svelte';
|
||||
import { Icon } from '@immich/ui';
|
||||
import { mdiEyeOffOutline } from '@mdi/js';
|
||||
import { untrack } from 'svelte';
|
||||
import type { ActionReturn } from 'svelte/action';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
|
||||
@@ -54,13 +55,20 @@
|
||||
onComplete?.(true);
|
||||
};
|
||||
|
||||
$effect(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
url;
|
||||
untrack(() => {
|
||||
preloadManager.loading(url);
|
||||
});
|
||||
});
|
||||
function mount(elem: HTMLImageElement): ActionReturn {
|
||||
if (elem.complete) {
|
||||
loaded = true;
|
||||
onComplete?.(false);
|
||||
}
|
||||
return {
|
||||
destroy: () => preloadManager.cancelPreloadUrl(url),
|
||||
destroy: () => preloadManager.cancelUrl(url),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import { uploadAssetsStore } from '$lib/stores/upload';
|
||||
import type { CommonPosition } from '$lib/utils/layout-utils';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { flip } from 'svelte/animate';
|
||||
|
||||
let { isUploading } = uploadAssetsStore;
|
||||
|
||||
@@ -50,13 +49,14 @@
|
||||
<div
|
||||
data-asset-id={asset.id}
|
||||
class="absolute"
|
||||
data-transition-name={transitionName}
|
||||
style:view-transition-name={transitionName}
|
||||
style:top={position.top + 'px'}
|
||||
style:left={position.left + 'px'}
|
||||
style:width={position.width + 'px'}
|
||||
style:height={position.height + 'px'}
|
||||
animate:flip={{ duration: transitionDuration }}
|
||||
>
|
||||
<!-- animate:flip={{ duration: transitionDuration }} -->
|
||||
{@render thumbnail({ asset, position })}
|
||||
{@render customThumbnailLayout?.(asset)}
|
||||
</div>
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
if (!asset) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewTransitionManager.startTransition(
|
||||
new Promise<void>((resolve) => {
|
||||
eventManager.once('TimelineLoaded', async ({ id }) => {
|
||||
@@ -73,15 +72,18 @@
|
||||
resolve();
|
||||
});
|
||||
}),
|
||||
[],
|
||||
() => {
|
||||
animationTargetAssetId = null;
|
||||
},
|
||||
);
|
||||
};
|
||||
eventManager.on('TransitionToTimeline', transitionToTimelineCallback);
|
||||
onDestroy(() => {
|
||||
eventManager.off('TransitionToTimeline', transitionToTimelineCallback);
|
||||
});
|
||||
if (viewTransitionManager.isSupported()) {
|
||||
eventManager.on('TransitionToTimeline', transitionToTimelineCallback);
|
||||
onDestroy(() => {
|
||||
eventManager.off('TransitionToTimeline', transitionToTimelineCallback);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each filterIntersecting(monthGroup.dayGroups) as dayGroup, groupIndex (dayGroup.day)}
|
||||
@@ -147,7 +149,7 @@
|
||||
|
||||
:global(::view-transition) {
|
||||
background: black;
|
||||
animation-duration: 500ms;
|
||||
animation-duration: 250ms;
|
||||
}
|
||||
|
||||
:global(::view-transition-old(*)),
|
||||
@@ -166,12 +168,27 @@
|
||||
}
|
||||
|
||||
:global(::view-transition-old(root)) {
|
||||
animation: 500ms 0s fadeOut forwards;
|
||||
animation: 250ms 0s fadeOut forwards;
|
||||
}
|
||||
:global(::view-transition-new(root)) {
|
||||
animation: 500ms 0s fadeIn forwards;
|
||||
animation: 250ms 0s fadeIn forwards;
|
||||
}
|
||||
:global(html:active-view-transition-type(slideshow)) {
|
||||
:global(&::view-transition-old(root)) {
|
||||
animation: 1s 0s fadeOut forwards;
|
||||
}
|
||||
:global(&::view-transition-new(root)) {
|
||||
animation: 1s 0s fadeIn forwards;
|
||||
}
|
||||
}
|
||||
:global(html:active-view-transition-type(viewer-nav)) {
|
||||
:global(&::view-transition-old(root)) {
|
||||
animation: 350ms 0s fadeOut forwards;
|
||||
}
|
||||
:global(&::view-transition-new(root)) {
|
||||
animation: 350ms 0s fadeIn forwards;
|
||||
}
|
||||
}
|
||||
|
||||
:global(::view-transition-old(info)) {
|
||||
animation: 250ms 0s flyOutRight forwards;
|
||||
}
|
||||
@@ -179,21 +196,56 @@
|
||||
animation: 250ms 0s flyInRight forwards;
|
||||
}
|
||||
|
||||
:global(::view-transition-group(detail-panel)) {
|
||||
z-index: 1;
|
||||
}
|
||||
:global(::view-transition-old(detail-panel)),
|
||||
:global(::view-transition-new(detail-panel)) {
|
||||
z-index: 3;
|
||||
animation: none;
|
||||
}
|
||||
:global(::view-transition-group(letterbox-left)),
|
||||
:global(::view-transition-group(letterbox-right)),
|
||||
:global(::view-transition-group(letterbox-top)),
|
||||
:global(::view-transition-group(letterbox-bottom)) {
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
:global(::view-transition-old(letterbox-left)),
|
||||
:global(::view-transition-old(letterbox-right)),
|
||||
:global(::view-transition-old(letterbox-top)),
|
||||
:global(::view-transition-old(letterbox-bottom)) {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
:global(::view-transition-new(letterbox-left)),
|
||||
:global(::view-transition-new(letterbox-right)) {
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
:global(::view-transition-new(letterbox-left)),
|
||||
:global(::view-transition-new(letterbox-right)),
|
||||
:global(::view-transition-new(letterbox-top)),
|
||||
:global(::view-transition-new(letterbox-bottom)) {
|
||||
background-color: black;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
:global(::view-transition-group(exclude-leftbutton)),
|
||||
:global(::view-transition-group(exclude-rightbutton)),
|
||||
:global(::view-transition-group(exclude)) {
|
||||
animation: none;
|
||||
z-index: 2;
|
||||
z-index: 5;
|
||||
}
|
||||
:global(::view-transition-old(exclude-leftbutton)),
|
||||
:global(::view-transition-old(exclude-rightbutton)),
|
||||
:global(::view-transition-old(exclude)) {
|
||||
visibility: hidden;
|
||||
}
|
||||
:global(::view-transition-new(exclude-leftbutton)),
|
||||
:global(::view-transition-new(exclude-rightbutton)),
|
||||
:global(::view-transition-new(exclude)) {
|
||||
animation: none;
|
||||
z-index: 2;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
:global(::view-transition-old(hero)) {
|
||||
@@ -206,124 +258,100 @@
|
||||
}
|
||||
:global(::view-transition-old(next)),
|
||||
:global(::view-transition-old(next-old)) {
|
||||
animation: 250ms flyOutLeft forwards;
|
||||
/* transform-origin: center; */
|
||||
height: 100%;
|
||||
/* display: flex; */
|
||||
object-fit: contain;
|
||||
/* margin: auto; */
|
||||
/* transform: translateY(50%); */
|
||||
/* width: auto;
|
||||
left: 50dvw;
|
||||
top: 50dvh;
|
||||
transform: translate3d(-50%, -50%, 0); */
|
||||
/* object-fit: contain;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
z-index: 10; */
|
||||
animation: 250ms cubic-bezier(0.25, 0.46, 0.45, 0.94) flyOutLeft forwards;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(::view-transition-new(next)),
|
||||
:global(::view-transition-new(next-new)) {
|
||||
animation: 250ms fadeIn forwards;
|
||||
height: 100%;
|
||||
/* display: flex; */
|
||||
object-fit: contain;
|
||||
/* transform-origin: center;
|
||||
width: auto;
|
||||
left: 50dvw;
|
||||
top: 50dvh;
|
||||
transform: translate3d(-50%, -50%, 0); */
|
||||
/* height: 100%;
|
||||
object-fit: contain;
|
||||
height: 100vh;
|
||||
width: 100vw; */
|
||||
animation: 250ms cubic-bezier(0.25, 0.46, 0.45, 0.94) flyInRight forwards;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(::view-transition-old(previous)) {
|
||||
animation: 1s flyOutRight forwards;
|
||||
animation: 250ms cubic-bezier(0.25, 0.46, 0.45, 0.94) flyOutRight forwards;
|
||||
}
|
||||
:global(::view-transition-old(previous-old)) {
|
||||
animation: 250ms flyOutRight forwards;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
/* transform-origin: center; */
|
||||
/* height: 100%;
|
||||
object-fit: contain;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
z-index: 10; */
|
||||
animation: 250ms cubic-bezier(0.25, 0.46, 0.45, 0.94) flyOutRight forwards;
|
||||
overflow: hidden;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
:global(::view-transition-new(previous)) {
|
||||
animation: 1s flyOutRight forwards;
|
||||
animation: 250ms cubic-bezier(0.25, 0.46, 0.45, 0.94) flyInLeft forwards;
|
||||
}
|
||||
|
||||
:global(::view-transition-new(previous-new)) {
|
||||
animation: 250ms fadeIn forwards;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
/* transform-origin: center; */
|
||||
/* height: 100%;
|
||||
object-fit: contain;
|
||||
height: 100vh;
|
||||
width: 100vw; */
|
||||
animation: 250ms cubic-bezier(0.25, 0.46, 0.45, 0.94) flyInLeft forwards;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes -global-flyInLeft {
|
||||
from {
|
||||
/* transform: translateX(-50dvw); */
|
||||
object-position: -25dvw;
|
||||
opacity: 0;
|
||||
/* object-position: -25dvw; */
|
||||
transform: translateX(-15%);
|
||||
opacity: 0.1;
|
||||
filter: blur(4px);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.4;
|
||||
filter: blur(2px);
|
||||
}
|
||||
to {
|
||||
/* transform: translateX(0); */
|
||||
object-position: 0px 0px;
|
||||
opacity: 1;
|
||||
filter: blur(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes -global-flyOutLeft {
|
||||
from {
|
||||
/* transform: translateX(0); */
|
||||
object-position: 0px;
|
||||
opacity: 1;
|
||||
filter: blur(0);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.4;
|
||||
filter: blur(2px);
|
||||
}
|
||||
to {
|
||||
/* transform: translateX(-50dvw); */
|
||||
object-position: -25dvw;
|
||||
opacity: 0;
|
||||
/* object-position: -25dvw; */
|
||||
transform: translateX(-15%);
|
||||
opacity: 0.1;
|
||||
filter: blur(4px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes -global-flyInRight {
|
||||
from {
|
||||
/* transform: translateX(50dvw); */
|
||||
object-position: 25dvw;
|
||||
opacity: 0;
|
||||
/* object-position: 25dvw; */
|
||||
transform: translateX(15%);
|
||||
opacity: 0.1;
|
||||
filter: blur(4px);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.4;
|
||||
filter: blur(2px);
|
||||
}
|
||||
to {
|
||||
/* transform: translateX(0); */
|
||||
object-position: 0px;
|
||||
opacity: 1;
|
||||
filter: blur(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fly out to right */
|
||||
@keyframes -global-flyOutRight {
|
||||
from {
|
||||
/* transform: translateX(0); */
|
||||
opacity: 1;
|
||||
filter: blur(0);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.4;
|
||||
filter: blur(2px);
|
||||
}
|
||||
|
||||
to {
|
||||
/* transform: translateX(50dvw); */
|
||||
object-position: 50dvw 0px;
|
||||
opacity: 0;
|
||||
/* object-position: 50dvw 0px; */
|
||||
transform: translateX(15%);
|
||||
opacity: 0.1;
|
||||
filter: blur(4px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -229,9 +229,7 @@
|
||||
// if the asset is not found, scroll to the top
|
||||
timelineManager.scrollTo(0);
|
||||
}
|
||||
if (!isAssetViewerRoute(page)) {
|
||||
invisible = false;
|
||||
}
|
||||
invisible = isAssetViewerRoute(page) ? true : false;
|
||||
};
|
||||
|
||||
// note: only modified once in afterNavigate()
|
||||
@@ -243,7 +241,6 @@
|
||||
// and a new route is being navigated to. It will never be called on direct
|
||||
// navigations by the browser.
|
||||
beforeNavigate(({ from, to }) => {
|
||||
console.log('BEFORE NAV');
|
||||
timelineManager.suspendTransitions = true;
|
||||
const isNavigatingToAssetViewer = isAssetViewerRoute(to);
|
||||
const isNavigatingFromAssetViewer = isAssetViewerRoute(from);
|
||||
@@ -257,7 +254,6 @@
|
||||
// after successful navigation.
|
||||
afterNavigate(({ complete }) => {
|
||||
void complete.finally(async () => {
|
||||
console.log('AFTER nav');
|
||||
const isAssetViewerPage = isAssetViewerRoute(page);
|
||||
|
||||
// Set initial load state only once - if initialLoadWasAssetViewer is null, then
|
||||
@@ -722,32 +718,39 @@
|
||||
{asset}
|
||||
{albumUsers}
|
||||
{groupIndex}
|
||||
onClick={(asset) => {
|
||||
onClick={async (asset) => {
|
||||
const callClickHandler = () => {
|
||||
if (typeof onThumbnailClick === 'function') {
|
||||
onThumbnailClick(asset, timelineManager, dayGroup, _onClick);
|
||||
} else {
|
||||
_onClick(timelineManager, dayGroup.getAssets(), dayGroup.groupTitle, asset);
|
||||
}
|
||||
};
|
||||
|
||||
if (!viewTransitionManager.isSupported()) {
|
||||
callClickHandler();
|
||||
return;
|
||||
}
|
||||
|
||||
// tag target on the 'old' snapshot
|
||||
toAssetViewerTransitionId = asset.id;
|
||||
await tick();
|
||||
|
||||
eventManager.once('StartViewTransition', () => {
|
||||
// remove target on the 'old' view,
|
||||
// asset-viewer will tag new target element for 'new' snapshot
|
||||
toAssetViewerTransitionId = null;
|
||||
|
||||
callClickHandler();
|
||||
});
|
||||
|
||||
viewTransitionManager.startTransition(
|
||||
new Promise((resolve) =>
|
||||
new Promise<void>((resolve) => {
|
||||
eventManager.once('AssetViewerFree', async () => {
|
||||
toAssetViewerTransitionId = null;
|
||||
await tick();
|
||||
eventManager.emit('TransitionToAssetViewer');
|
||||
resolve();
|
||||
}),
|
||||
),
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
if (typeof onThumbnailClick === 'function') {
|
||||
onThumbnailClick(asset, timelineManager, dayGroup, _onClick);
|
||||
} else {
|
||||
_onClick(timelineManager, dayGroup.getAssets(), dayGroup.groupTitle, asset);
|
||||
}
|
||||
}}
|
||||
onSelect={() => {
|
||||
if (isSelectionMode || assetInteraction.selectionActive) {
|
||||
|
||||
@@ -41,18 +41,26 @@
|
||||
person = null,
|
||||
}: Props = $props();
|
||||
|
||||
const getNextAsset = async (currentAsset: AssetResponseDto) => {
|
||||
const getNextAsset = async (currentAsset: AssetResponseDto, preload: boolean = true) => {
|
||||
const earlierTimelineAsset = await timelineManager.getEarlierAsset(currentAsset);
|
||||
if (earlierTimelineAsset) {
|
||||
const asset = assetCacheManager.getAsset({ ...authManager.params, id: earlierTimelineAsset.id });
|
||||
const asset = await assetCacheManager.getAsset({ ...authManager.params, id: earlierTimelineAsset.id });
|
||||
if (preload) {
|
||||
// also pre-cache an extra one, to pre-cache these assetInfos for the next nav after this one is complete
|
||||
void getNextAsset(asset, false);
|
||||
}
|
||||
return asset;
|
||||
}
|
||||
};
|
||||
|
||||
const getPreviousAsset = async (currentAsset: AssetResponseDto) => {
|
||||
const getPreviousAsset = async (currentAsset: AssetResponseDto, preload: boolean = true) => {
|
||||
const laterTimelineAsset = await timelineManager.getLaterAsset(currentAsset);
|
||||
if (laterTimelineAsset) {
|
||||
const asset = assetCacheManager.getAsset({ ...authManager.params, id: laterTimelineAsset.id });
|
||||
const asset = await assetCacheManager.getAsset({ ...authManager.params, id: laterTimelineAsset.id });
|
||||
if (preload) {
|
||||
// also pre-cache an extra one, to pre-cache these assetInfos for the next nav after this one is complete
|
||||
void getPreviousAsset(asset, false);
|
||||
}
|
||||
return asset;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,44 +1,60 @@
|
||||
import { getAssetUrl } from '$lib/utils';
|
||||
import { cancelImageUrl, isImageUrlCached, preloadImageUrl } from '$lib/utils/sw-messaging';
|
||||
import { cancelImageUrl, isImageUrlCached, isServiceWorkerEnabled, preloadImageUrl } from '$lib/utils/sw-messaging';
|
||||
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
|
||||
|
||||
class PreloadManager {
|
||||
#cachedImages = new Set<string>();
|
||||
loading(url: string) {
|
||||
if (!isServiceWorkerEnabled()) {
|
||||
this.#cachedImages.add(url);
|
||||
}
|
||||
}
|
||||
|
||||
preload(asset: AssetResponseDto | undefined) {
|
||||
if (!asset) {
|
||||
return;
|
||||
}
|
||||
if (globalThis.isSecureContext) {
|
||||
if (isServiceWorkerEnabled()) {
|
||||
preloadImageUrl(getAssetUrl({ asset }));
|
||||
return;
|
||||
}
|
||||
if (asset.type === AssetTypeEnum.Image) {
|
||||
const img = new Image();
|
||||
img.src = getAssetUrl({ asset });
|
||||
const src = getAssetUrl({ asset });
|
||||
if (src) {
|
||||
const img = new Image();
|
||||
img.src = src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isPreloaded(asset: AssetResponseDto | undefined) {
|
||||
if (!asset) {
|
||||
return false;
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
if (globalThis.isSecureContext) {
|
||||
const img = getAssetUrl({ asset });
|
||||
const url = getAssetUrl({ asset });
|
||||
return this.isUrlPreloaded(url);
|
||||
}
|
||||
|
||||
return isImageUrlCached(img);
|
||||
isUrlPreloaded(url: string | undefined | null) {
|
||||
if (!url) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return false;
|
||||
if (isServiceWorkerEnabled()) {
|
||||
return isImageUrlCached(url);
|
||||
}
|
||||
return Promise.resolve(this.#cachedImages.has(url));
|
||||
}
|
||||
|
||||
cancel(asset: AssetResponseDto | undefined) {
|
||||
if (!globalThis.isSecureContext || !asset) {
|
||||
if (!isServiceWorkerEnabled() || !asset) {
|
||||
return;
|
||||
}
|
||||
const url = getAssetUrl({ asset });
|
||||
cancelImageUrl(url);
|
||||
}
|
||||
|
||||
cancelPreloadUrl(url: string) {
|
||||
if (!globalThis.isSecureContext) {
|
||||
cancelUrl(url: string) {
|
||||
if (!isServiceWorkerEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function traceTransitionEvents(msg: string, error?: unknown) {
|
||||
// console.log(msg, error);
|
||||
}
|
||||
class ViewTransitionManager {
|
||||
#activeViewTransition = $state<ViewTransition | null>(null);
|
||||
#finishedCallbacks: (() => void)[] = [];
|
||||
@@ -25,36 +28,57 @@ class ViewTransitionManager {
|
||||
return this.#activeViewTransition;
|
||||
}
|
||||
|
||||
isSupported() {
|
||||
return 'startViewTransition' in document;
|
||||
}
|
||||
|
||||
skipTransitions() {
|
||||
const skippedTransitions = !!this.#activeViewTransition;
|
||||
if (skippedTransitions) {
|
||||
console.log('skipped!');
|
||||
}
|
||||
this.#activeViewTransition?.skipTransition();
|
||||
this.#notifyFinished();
|
||||
return skippedTransitions;
|
||||
}
|
||||
|
||||
startTransition(domUpdateComplete: Promise<void>, finishedCallback?: () => void) {
|
||||
startTransition(domUpdateComplete: Promise<unknown>, types?: string[], finishedCallback?: () => unknown) {
|
||||
if (!this.isSupported()) {
|
||||
throw new Error('View transition API not available');
|
||||
}
|
||||
if (this.#activeViewTransition) {
|
||||
console.error('Can not start transition - one already active');
|
||||
traceTransitionEvents('Can not start transition - one already active');
|
||||
return;
|
||||
}
|
||||
|
||||
// good time to add view-transition-name styles (if needed)
|
||||
traceTransitionEvents('emit BeforeStartViewTransition');
|
||||
eventManager.emit('BeforeStartViewTransition');
|
||||
|
||||
// next call will create the 'old' view snapshot
|
||||
// eslint-disable-next-line tscompat/tscompat
|
||||
const transition = document.startViewTransition(async () => {
|
||||
try {
|
||||
let transition: ViewTransition;
|
||||
try {
|
||||
// eslint-disable-next-line tscompat/tscompat
|
||||
transition = document.startViewTransition({
|
||||
update: async () => {
|
||||
// Good time to remove any view-transition-name styles created during
|
||||
// BeforeStartViewTransition, then trigger the actual view transition.
|
||||
traceTransitionEvents('emit StartViewTransition');
|
||||
eventManager.emit('StartViewTransition');
|
||||
|
||||
await domUpdateComplete;
|
||||
traceTransitionEvents('awaited domUpdateComplete');
|
||||
},
|
||||
types,
|
||||
});
|
||||
} catch {
|
||||
// eslint-disable-next-line tscompat/tscompat
|
||||
transition = document.startViewTransition(async () => {
|
||||
// Good time to remove any view-transition-name styles created during
|
||||
// BeforeStartViewTransition, then trigger the actual view transition.
|
||||
traceTransitionEvents('emit StartViewTransition');
|
||||
eventManager.emit('StartViewTransition');
|
||||
await domUpdateComplete;
|
||||
} catch (error: unknown) {
|
||||
console.log('exception', error);
|
||||
}
|
||||
});
|
||||
traceTransitionEvents('awaited domUpdateComplete');
|
||||
});
|
||||
}
|
||||
this.#activeViewTransition = transition;
|
||||
this.#finishedCallbacks.push(() => {
|
||||
this.#activeViewTransition = null;
|
||||
@@ -67,10 +91,10 @@ class ViewTransitionManager {
|
||||
// eslint-disable-next-line tscompat/tscompat
|
||||
transition.updateCallbackDone
|
||||
.then(() => {
|
||||
console.log('update done');
|
||||
traceTransitionEvents('emit UpdateCallbackDone');
|
||||
eventManager.emit('UpdateCallbackDone');
|
||||
})
|
||||
.catch((error: unknown) => console.log('exception in update', error));
|
||||
.catch((error: unknown) => traceTransitionEvents('error in UpdateCallbackDone', error));
|
||||
// Both old/new snapshots are taken - pseudo elements are created, transition is
|
||||
// about to start
|
||||
// eslint-disable-next-line tscompat/tscompat
|
||||
@@ -78,21 +102,21 @@ class ViewTransitionManager {
|
||||
.then(() => eventManager.emit('Ready'))
|
||||
.catch((error: unknown) => {
|
||||
this.#notifyFinished();
|
||||
console.log('exception in ready', error);
|
||||
traceTransitionEvents('error in Ready', error);
|
||||
});
|
||||
// Transition is complete
|
||||
// eslint-disable-next-line tscompat/tscompat
|
||||
transition.finished
|
||||
.then(() => {
|
||||
traceTransitionEvents('emit Finished');
|
||||
eventManager.emit('Finished');
|
||||
console.log('finished');
|
||||
})
|
||||
.catch((error: unknown) => console.log('exception in finished', error));
|
||||
.catch((error: unknown) => traceTransitionEvents('error in Finished', error));
|
||||
// eslint-disable-next-line tscompat/tscompat
|
||||
void transition.finished.then(() => this.#notifyFinished());
|
||||
}
|
||||
|
||||
#notifyFinished() {
|
||||
console.log('finishedCallbacks len', this.#finishedCallbacks.length);
|
||||
for (const callback of this.#finishedCallbacks) {
|
||||
callback();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const photoViewerImgElement = writable<HTMLImageElement | null>(null);
|
||||
export const photoViewerImgElement = writable<HTMLImageElement | null | undefined>(null);
|
||||
export const isSelectingAllAssets = writable(false);
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import type { ZoomImageWheelState } from '@zoom-image/core';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const photoZoomState = writable<ZoomImageWheelState>();
|
||||
export const photoZoomState = writable<ZoomImageWheelState>({
|
||||
currentRotation: 0,
|
||||
currentZoom: 1,
|
||||
enable: true,
|
||||
currentPositionX: 0,
|
||||
currentPositionY: 0,
|
||||
});
|
||||
|
||||
export const resetZoomState = () => {
|
||||
photoZoomState.set({
|
||||
|
||||
@@ -132,14 +132,14 @@ export type CommonPosition = {
|
||||
};
|
||||
|
||||
// Scales dimensions to fit within a container (like object-fit: contain)
|
||||
export const scaleToFit = (width: number, height: number, containerW: number, containerH: number) => {
|
||||
const scaleX = containerW / width;
|
||||
const scaleY = containerH / height;
|
||||
export const scaleToFit = (dimensions: { width: number; height: number }, containerW: number, containerH: number) => {
|
||||
const scaleX = containerW / dimensions.width;
|
||||
const scaleY = containerH / dimensions.height;
|
||||
|
||||
const scale = Math.min(scaleX, scaleY);
|
||||
|
||||
return {
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
width: dimensions.width * scale,
|
||||
height: dimensions.height * scale,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -24,11 +24,11 @@ export interface boundingBox {
|
||||
export const getBoundingBox = (
|
||||
faces: Faces[],
|
||||
zoom: ZoomImageWheelState,
|
||||
photoViewer: HTMLImageElement | null,
|
||||
photoViewer: HTMLImageElement | null | undefined,
|
||||
): boundingBox[] => {
|
||||
const boxes: boundingBox[] = [];
|
||||
|
||||
if (photoViewer === null) {
|
||||
if (!photoViewer) {
|
||||
return boxes;
|
||||
}
|
||||
const clientHeight = photoViewer.clientHeight;
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
const broadcast = new BroadcastChannel('immich');
|
||||
|
||||
let isLoadedReplyListeners: ((url: string, isUrlCached: boolean) => void)[] = [];
|
||||
let serviceWorkerEnabled = false;
|
||||
let replyListeners: ((url: string, isUrlCached: boolean) => void)[] = [];
|
||||
broadcast.addEventListener('message', (event) => {
|
||||
if (event.data.type == 'isImageUrlCachedReply') {
|
||||
for (const listener of isLoadedReplyListeners) {
|
||||
if (event.data.type === 'isImageUrlCachedReply') {
|
||||
for (const listener of replyListeners) {
|
||||
listener(event.data.url, event.data.isImageUrlCached);
|
||||
}
|
||||
} else if (event.data.type === 'isServiceWorkerEnabledReply') {
|
||||
serviceWorkerEnabled = true;
|
||||
}
|
||||
});
|
||||
broadcast.postMessage({ type: 'isServiceWorkerEnabled' });
|
||||
|
||||
export function isServiceWorkerEnabled() {
|
||||
return serviceWorkerEnabled;
|
||||
}
|
||||
|
||||
export function cancelImageUrl(url: string | undefined | null) {
|
||||
if (!url) {
|
||||
@@ -24,22 +32,21 @@ export function preloadImageUrl(url: string | undefined | null) {
|
||||
}
|
||||
|
||||
export function isImageUrlCached(url: string) {
|
||||
if (!globalThis.isSecureContext) {
|
||||
if (!globalThis.isSecureContext || !serviceWorkerEnabled) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
const listener = (urlReply: string, isUrlCached: boolean) => {
|
||||
if (urlReply === url) {
|
||||
cleanup(isUrlCached);
|
||||
}
|
||||
};
|
||||
const cleanup = (isUrlCached: boolean) => {
|
||||
isLoadedReplyListeners = isLoadedReplyListeners.filter((element) => element !== listener);
|
||||
replyListeners = replyListeners.filter((element) => element !== listener);
|
||||
resolve(isUrlCached);
|
||||
};
|
||||
isLoadedReplyListeners.push(listener);
|
||||
replyListeners.push(listener);
|
||||
broadcast.postMessage({ type: 'isImageUrlCached', url });
|
||||
|
||||
setTimeout(() => cleanup(false), 5000);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,6 +26,11 @@ export const installBroadcastChannelListener = () => {
|
||||
void handleIsUrlCached(url);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'isServiceWorkerEnabled': {
|
||||
replyIsServiceWorkerEnabled();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -33,3 +38,7 @@ export const installBroadcastChannelListener = () => {
|
||||
export const replyIsImageUrlCached = (url: string, isImageUrlCached: boolean) => {
|
||||
broadcast.postMessage({ type: 'isImageUrlCachedReply', url, isImageUrlCached });
|
||||
};
|
||||
|
||||
export const replyIsServiceWorkerEnabled = () => {
|
||||
broadcast.postMessage({ type: 'isServiceWorkerEnabledReply', enabled: true });
|
||||
};
|
||||
|
||||
@@ -37,3 +37,4 @@ sw.addEventListener('install', handleInstall, { passive: true });
|
||||
sw.addEventListener('activate', handleActivate, { passive: true });
|
||||
sw.addEventListener('fetch', handleFetch, { passive: true });
|
||||
installBroadcastChannelListener();
|
||||
|
||||
|
||||
@@ -77,6 +77,5 @@ export const handleIsUrlCached = async (url: URL) => {
|
||||
const cacheKey = getCacheKey(url);
|
||||
|
||||
const isImageUrlCached = !!(await get(cacheKey));
|
||||
console.log('cacheKey', cacheKey, isImageUrlCached);
|
||||
replyIsImageUrlCached(url.pathname + url.search + url.hash, isImageUrlCached);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user