From 18537264ec62966330dca3882d7806609efd00af Mon Sep 17 00:00:00 2001 From: Tom Lebreux Date: Wed, 20 Nov 2024 17:25:55 -0500 Subject: [PATCH 1/8] Rebase on top of v1.121.0 (part 1) --- web/package-lock.json | 353 +++++++++--------- web/package.json | 8 +- .../asset-viewer/image-panorama-viewer.svelte | 32 ++ .../asset-viewer/video-panorama-viewer.svelte | 29 ++ web/src/lib/components/elements/badge.svelte | 15 +- .../components/elements/buttons/button.svelte | 103 ++--- .../buttons/circle-icon-button.svelte | 114 +++--- .../elements/buttons/link-button.svelte | 29 +- .../elements/buttons/skip-link.svelte | 22 +- .../lib/components/elements/checkbox.svelte | 30 +- .../lib/components/elements/date-input.svelte | 30 +- .../lib/components/elements/dropdown.svelte | 44 ++- .../lib/components/elements/group-tab.svelte | 14 +- web/src/lib/components/elements/icon.svelte | 51 ++- .../components/elements/radio-button.svelte | 14 +- .../lib/components/elements/search-bar.svelte | 33 +- web/src/lib/components/elements/slider.svelte | 34 +- web/src/lib/components/error.svelte | 8 +- .../i18n/__test__/format-tag-b.svelte | 18 +- .../i18n/format-bold-message.svelte | 18 +- .../lib/components/i18n/format-message.svelte | 19 +- .../album-selection-modal.svelte | 38 +- .../context-menu/button-context-menu.svelte | 87 +++-- .../context-menu/context-menu.svelte | 49 ++- .../context-menu/menu-option.svelte | 35 +- .../right-click-context-menu.svelte | 55 +-- .../coordinates-input.svelte | 11 +- .../settings/setting-accordion-state.svelte | 34 +- .../settings/setting-accordion.svelte | 50 ++- .../settings/setting-buttons-row.svelte | 18 +- .../settings/setting-checkboxes.svelte | 28 +- .../settings/setting-combobox.svelte | 31 +- .../settings/setting-dropdown.svelte | 27 +- .../settings/setting-input-field.spec.ts | 4 +- .../settings/setting-input-field.svelte | 77 ++-- .../settings/setting-select.svelte | 34 +- .../settings/setting-switch.svelte | 32 +- .../settings/setting-textarea.svelte | 36 +- .../shared-components/show-shortcuts.svelte | 42 ++- .../shared-components/theme-button.svelte | 17 +- .../shared-components/tree/breadcrumbs.svelte | 16 +- .../tree/tree-item-thumbnails.svelte | 12 +- .../shared-components/tree/tree-items.svelte | 16 +- .../shared-components/tree/tree.svelte | 42 ++- .../shared-components/user-avatar.svelte | 59 +-- .../lib/components/slideshow-settings.svelte | 22 +- web/src/lib/constants.ts | 47 +++ web/src/routes/+page.svelte | 11 +- web/svelte.config.js | 3 +- 49 files changed, 1213 insertions(+), 738 deletions(-) create mode 100644 web/src/lib/components/asset-viewer/image-panorama-viewer.svelte create mode 100644 web/src/lib/components/asset-viewer/video-panorama-viewer.svelte diff --git a/web/package-lock.json b/web/package-lock.json index a24de1c..eaf6d35 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "immich-web", - "version": "1.119.1", + "version": "1.121.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "immich-web", - "version": "1.119.1", + "version": "1.121.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@formatjs/icu-messageformat-parser": "^2.7.8", @@ -36,7 +36,7 @@ "@faker-js/faker": "^9.0.0", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.5", - "@sveltejs/enhanced-img": "^0.3.0", + "@sveltejs/enhanced-img": "^0.3.9", "@sveltejs/kit": "^2.7.2", "@sveltejs/vite-plugin-svelte": "^4.0.0", "@testing-library/jest-dom": "^6.4.2", @@ -53,7 +53,7 @@ "dotenv": "^16.4.5", "eslint": "^9.0.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.43.0", + "eslint-plugin-svelte": "^2.45.1", "eslint-plugin-unicorn": "^55.0.0", "factory.ts": "^1.4.1", "globals": "^15.9.0", @@ -68,19 +68,19 @@ "tailwindcss": "^3.4.1", "tslib": "^2.6.2", "typescript": "^5.5.0", - "vite": "^5.1.4", + "vite": "^5.4.4", "vitest": "^2.0.5" } }, "../open-api/typescript-sdk": { "name": "@immich/sdk", - "version": "1.119.1", + "version": "1.121.0", "license": "GNU Affero General Public License version 3", "dependencies": { "@oazapfts/runtime": "^1.0.2" }, "devDependencies": { - "@types/node": "^22.8.1", + "@types/node": "^22.9.0", "typescript": "^5.3.3" } }, @@ -138,19 +138,21 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -170,12 +172,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", - "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.25.2" + "@babel/types": "^7.26.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -197,14 +200,14 @@ } }, "node_modules/@babel/types": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", - "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -769,9 +772,9 @@ } }, "node_modules/@faker-js/faker": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.0.3.tgz", - "integrity": "sha512-lWrrK4QNlFSU+13PL9jMbMKLJYXDFu3tQfayBsMXX7KL/GiQeqfB1CzHkqD5UHBUtPAuPo6XwGbMFNdVMZObRA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.1.0.tgz", + "integrity": "sha512-GJvX9iM9PBtKScJVlXQ0tWpihK3i0pha/XAhzQa1hPK/ILLa1Wq3I63Ij7lRtqTwmdTxRCyrUhLC5Sly9SLbug==", "dev": true, "funding": [ { @@ -1635,30 +1638,30 @@ } }, "node_modules/@photo-sphere-viewer/core": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.11.0.tgz", - "integrity": "sha512-SjEVBBMNj5v3aSLglpTd88q+cyDw0Lke7mK3kN3p+KsQK8X0OG7GsbtI7sGj81zSCbHmV3c0DgUaXMv+xdGFaw==", + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/core/-/core-5.11.1.tgz", + "integrity": "sha512-bxWnoQGYjXfmHGee4OSkoYLZmdgqvJWMn7wmpK0V0Vf46Fqu+TJ4Yt8+dY2PgpM89HoKzNr15Dzt6jqOfjkFxQ==", "license": "MIT", "dependencies": { "three": "^0.169.0" } }, "node_modules/@photo-sphere-viewer/equirectangular-video-adapter": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.11.0.tgz", - "integrity": "sha512-xs5+vT5jYjBtEsguuJe6CVJNGxtwopb3+Zs4z/VJg0oNm2mTZF2TQu2RkSlRJZTE6a/IfZ7Zv1fJcN3Yiv7AVg==", + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/equirectangular-video-adapter/-/equirectangular-video-adapter-5.11.1.tgz", + "integrity": "sha512-fkWuVeArtZSWd0z282/J82YSc+oernQaE/cpo0soVaStaNbS1V35iSnPlaBKw40qX6tucJWYw15QwM8xgPC2IQ==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.11.0" + "@photo-sphere-viewer/core": "5.11.1" } }, "node_modules/@photo-sphere-viewer/video-plugin": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.11.0.tgz", - "integrity": "sha512-6ApAKvwDRgVZOk4N/3SJ14IFpQ6V0QDDtknXxLI5JqU1yAvBcyb7goa5XDbyTWXYUaPKH06Fz+8UruWRzCsBXw==", + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/@photo-sphere-viewer/video-plugin/-/video-plugin-5.11.1.tgz", + "integrity": "sha512-02spWwv9bjyI6inNdZsczX/qdMICVV9B8lWX/J4iNBaiUCHqPKmk8CeZbRyC/Uh3OHSusSJHyW0FDEOf6qjjww==", "license": "MIT", "peerDependencies": { - "@photo-sphere-viewer/core": "5.11.0" + "@photo-sphere-viewer/core": "5.11.1" } }, "node_modules/@pkgjs/parseargs": { @@ -2124,9 +2127,9 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.2.tgz", - "integrity": "sha512-P6GJD4yqc9jZLbe98j/EkyQDTPgqftohZF5FBkHY5BUERZmcf4HeO2k0XaefEg329ux2p21i1A1DmyQ1kKw2Jw==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", + "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", "dev": true, "license": "MIT", "dependencies": { @@ -2383,17 +2386,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz", - "integrity": "sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz", + "integrity": "sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.11.0", - "@typescript-eslint/type-utils": "8.11.0", - "@typescript-eslint/utils": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0", + "@typescript-eslint/scope-manager": "8.12.2", + "@typescript-eslint/type-utils": "8.12.2", + "@typescript-eslint/utils": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -2417,16 +2420,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.11.0.tgz", - "integrity": "sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.12.2.tgz", + "integrity": "sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.11.0", - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/typescript-estree": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0", + "@typescript-eslint/scope-manager": "8.12.2", + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/typescript-estree": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2", "debug": "^4.3.4" }, "engines": { @@ -2446,14 +2449,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz", - "integrity": "sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz", + "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0" + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2464,14 +2467,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz", - "integrity": "sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz", + "integrity": "sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.11.0", - "@typescript-eslint/utils": "8.11.0", + "@typescript-eslint/typescript-estree": "8.12.2", + "@typescript-eslint/utils": "8.12.2", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -2489,9 +2492,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.11.0.tgz", - "integrity": "sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz", + "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==", "dev": true, "license": "MIT", "engines": { @@ -2503,14 +2506,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz", - "integrity": "sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz", + "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/visitor-keys": "8.11.0", + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2558,16 +2561,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.11.0.tgz", - "integrity": "sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.12.2.tgz", + "integrity": "sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.11.0", - "@typescript-eslint/types": "8.11.0", - "@typescript-eslint/typescript-estree": "8.11.0" + "@typescript-eslint/scope-manager": "8.12.2", + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/typescript-estree": "8.12.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2581,13 +2584,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz", - "integrity": "sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==", + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz", + "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.11.0", + "@typescript-eslint/types": "8.12.2", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -2599,21 +2602,21 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.3.tgz", - "integrity": "sha512-2OJ3c7UPoFSmBZwqD2VEkUw6A/tzPF0LmW0ZZhhB8PFxuc+9IBG/FaSM+RLEenc7ljzFvGN+G0nGQoZnh7sy2A==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.4.tgz", + "integrity": "sha512-FPKQuJfR6VTfcNMcGpqInmtJuVXFSCd9HQltYncfR01AzXhLucMEtQ5SinPdZxsT5x/5BK7I5qFJ5/ApGCmyTQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.6", + "debug": "^4.3.7", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", "istanbul-reports": "^3.1.7", - "magic-string": "^0.30.11", - "magicast": "^0.3.4", + "magic-string": "^0.30.12", + "magicast": "^0.3.5", "std-env": "^3.7.0", "test-exclude": "^7.0.1", "tinyrainbow": "^1.2.0" @@ -2622,8 +2625,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "2.1.3", - "vitest": "2.1.3" + "@vitest/browser": "2.1.4", + "vitest": "2.1.4" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -2632,15 +2635,15 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.3.tgz", - "integrity": "sha512-SNBoPubeCJhZ48agjXruCI57DvxcsivVDdWz+SSsmjTT4QN/DfHk3zB/xKsJqMs26bLZ/pNRLnCf0j679i0uWQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.4.tgz", + "integrity": "sha512-DOETT0Oh1avie/D/o2sgMHGrzYUFFo3zqESB2Hn70z6QB1HrS2IQ9z5DfyTqU8sg4Bpu13zZe9V4+UTNQlUeQA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.3", - "@vitest/utils": "2.1.3", - "chai": "^5.1.1", + "@vitest/spy": "2.1.4", + "@vitest/utils": "2.1.4", + "chai": "^5.1.2", "tinyrainbow": "^1.2.0" }, "funding": { @@ -2648,22 +2651,21 @@ } }, "node_modules/@vitest/mocker": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.3.tgz", - "integrity": "sha512-eSpdY/eJDuOvuTA3ASzCjdithHa+GIF1L4PqtEELl6Qa3XafdMLBpBlZCIUCX2J+Q6sNmjmxtosAG62fK4BlqQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.4.tgz", + "integrity": "sha512-Ky/O1Lc0QBbutJdW0rqLeFNbuLEyS+mIPiNdlVlp2/yhJ0SbyYqObS5IHdhferJud8MbbwMnexg4jordE5cCoQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.3", + "@vitest/spy": "2.1.4", "estree-walker": "^3.0.3", - "magic-string": "^0.30.11" + "magic-string": "^0.30.12" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/spy": "2.1.3", - "msw": "^2.3.5", + "msw": "^2.4.9", "vite": "^5.0.0" }, "peerDependenciesMeta": { @@ -2676,9 +2678,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.3.tgz", - "integrity": "sha512-XH1XdtoLZCpqV59KRbPrIhFCOO0hErxrQCMcvnQete3Vibb9UeIOX02uFPfVn3Z9ZXsq78etlfyhnkmIZSzIwQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.4.tgz", + "integrity": "sha512-L95zIAkEuTDbUX1IsjRl+vyBSLh3PwLLgKpghl37aCK9Jvw0iP+wKwIFhfjdUtA2myLgjrG6VU6JCFLv8q/3Ww==", "dev": true, "license": "MIT", "dependencies": { @@ -2689,13 +2691,13 @@ } }, "node_modules/@vitest/runner": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.3.tgz", - "integrity": "sha512-JGzpWqmFJ4fq5ZKHtVO3Xuy1iF2rHGV4d/pdzgkYHm1+gOzNZtqjvyiaDGJytRyMU54qkxpNzCx+PErzJ1/JqQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.4.tgz", + "integrity": "sha512-sKRautINI9XICAMl2bjxQM8VfCMTB0EbsBc/EDFA57V6UQevEKY/TOPOF5nzcvCALltiLfXWbq4MaAwWx/YxIA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.3", + "@vitest/utils": "2.1.4", "pathe": "^1.1.2" }, "funding": { @@ -2703,14 +2705,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.3.tgz", - "integrity": "sha512-qWC2mWc7VAXmjAkEKxrScWHWFyCQx/cmiZtuGqMi+WwqQJ2iURsVY4ZfAK6dVo6K2smKRU6l3BPwqEBvhnpQGg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.4.tgz", + "integrity": "sha512-3Kab14fn/5QZRog5BPj6Rs8dc4B+mim27XaKWFWHWA87R56AKjHTGcBFKpvZKDzC4u5Wd0w/qKsUIio3KzWW4Q==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.3", - "magic-string": "^0.30.11", + "@vitest/pretty-format": "2.1.4", + "magic-string": "^0.30.12", "pathe": "^1.1.2" }, "funding": { @@ -2718,27 +2720,27 @@ } }, "node_modules/@vitest/spy": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.3.tgz", - "integrity": "sha512-Nb2UzbcUswzeSP7JksMDaqsI43Sj5+Kry6ry6jQJT4b5gAK+NS9NED6mDb8FlMRCX8m5guaHCDZmqYMMWRy5nQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.4.tgz", + "integrity": "sha512-4JOxa+UAizJgpZfaCPKK2smq9d8mmjZVPMt2kOsg/R8QkoRzydHH1qHxIYNvr1zlEaFj4SXiaaJWxq/LPLKaLg==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^3.0.0" + "tinyspy": "^3.0.2" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.3.tgz", - "integrity": "sha512-xpiVfDSg1RrYT0tX6czgerkpcKFmFOF/gCr30+Mve5V2kewCy4Prn1/NDMSRwaSmT7PRaOF83wu+bEtsY1wrvA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.4.tgz", + "integrity": "sha512-MXDnZn0Awl2S86PSNIim5PWXgIAx8CIkzu35mBdSApUip6RFOGXBCf3YFyeEu8n1IHk4bWD46DeYFu9mQlFIRg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.3", - "loupe": "^3.1.1", + "@vitest/pretty-format": "2.1.4", + "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" }, "funding": { @@ -3131,9 +3133,9 @@ "license": "CC-BY-4.0" }, "node_modules/chai": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", - "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", + "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", "dev": true, "license": "MIT", "dependencies": { @@ -3645,7 +3647,6 @@ "version": "6.5.4", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", - "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", @@ -3658,7 +3659,6 @@ "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "license": "MIT", "engines": { "node": ">=10.0.0" } @@ -4241,6 +4241,16 @@ "es5-ext": "~0.10.14" } }, + "node_modules/expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/ext": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", @@ -5336,13 +5346,14 @@ } }, "node_modules/magicast": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.4.tgz", - "integrity": "sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/parser": "^7.24.4", - "@babel/types": "^7.24.0", + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", "source-map-js": "^1.2.0" } }, @@ -6847,7 +6858,6 @@ "version": "4.7.5", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", - "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", @@ -7161,9 +7171,9 @@ } }, "node_modules/svelte": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.1.5.tgz", - "integrity": "sha512-AyYondx6wS0g8mmBMfwJVnOYYBswjBv6L4bc99awfbET2KozWvVwxe8NSN7fhx7Pgr7pOfOXIv7K8+Impc0OoQ==", + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.1.8.tgz", + "integrity": "sha512-MCZVSNNqlgwKUSEZEsq2nILhzI70qv1jJy9fG9nf3I8CyJhJ2vxtPybDuP5HdB7Q9Az0WliFmqUeLEQdnY1j+g==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", @@ -8032,17 +8042,18 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz", - "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz", + "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==", "dev": true, "license": "MIT" }, "node_modules/tinypool": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.0.tgz", - "integrity": "sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", + "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", "dev": true, + "license": "MIT", "engines": { "node": "^18.0.0 || >=20.0.0" } @@ -8071,15 +8082,6 @@ "node": ">=14.0.0" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -8379,14 +8381,14 @@ } }, "node_modules/vite-node": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.3.tgz", - "integrity": "sha512-I1JadzO+xYX887S39Do+paRePCKoiDrWRRjp9kkG5he0t7RXNvPAJPCQSJqbGN4uCrFFeS3Kj3sLqY8NMYBEdA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.4.tgz", + "integrity": "sha512-kqa9v+oi4HwkG6g8ufRnb5AeplcRw8jUF6/7/Qz1qRQOXHImG8YnLbB+LLszENwFnoBl9xIf9nVdCFzNd7GQEg==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.6", + "debug": "^4.3.7", "pathe": "^1.1.2", "vite": "^5.0.0" }, @@ -8420,30 +8422,31 @@ } }, "node_modules/vitest": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.3.tgz", - "integrity": "sha512-Zrxbg/WiIvUP2uEzelDNTXmEMJXuzJ1kCpbDvaKByFA9MNeO95V+7r/3ti0qzJzrxdyuUw5VduN7k+D3VmVOSA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.4.tgz", + "integrity": "sha512-eDjxbVAJw1UJJCHr5xr/xM86Zx+YxIEXGAR+bmnEID7z9qWfoxpHw0zdobz+TQAFOLT+nEXz3+gx6nUJ7RgmlQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "2.1.3", - "@vitest/mocker": "2.1.3", - "@vitest/pretty-format": "^2.1.3", - "@vitest/runner": "2.1.3", - "@vitest/snapshot": "2.1.3", - "@vitest/spy": "2.1.3", - "@vitest/utils": "2.1.3", - "chai": "^5.1.1", - "debug": "^4.3.6", - "magic-string": "^0.30.11", + "@vitest/expect": "2.1.4", + "@vitest/mocker": "2.1.4", + "@vitest/pretty-format": "^2.1.4", + "@vitest/runner": "2.1.4", + "@vitest/snapshot": "2.1.4", + "@vitest/spy": "2.1.4", + "@vitest/utils": "2.1.4", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", "pathe": "^1.1.2", "std-env": "^3.7.0", "tinybench": "^2.9.0", - "tinyexec": "^0.3.0", - "tinypool": "^1.0.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "2.1.3", + "vite-node": "2.1.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -8458,8 +8461,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.3", - "@vitest/ui": "2.1.3", + "@vitest/browser": "2.1.4", + "@vitest/ui": "2.1.4", "happy-dom": "*", "jsdom": "*" }, diff --git a/web/package.json b/web/package.json index b67f1cf..113bbe5 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "immich-web", - "version": "1.119.1", + "version": "1.121.0", "license": "GNU Affero General Public License version 3", "scripts": { "dev": "vite dev --host 0.0.0.0 --port 3000", @@ -28,7 +28,7 @@ "@faker-js/faker": "^9.0.0", "@socket.io/component-emitter": "^3.1.0", "@sveltejs/adapter-static": "^3.0.5", - "@sveltejs/enhanced-img": "^0.3.0", + "@sveltejs/enhanced-img": "^0.3.9", "@sveltejs/kit": "^2.7.2", "@sveltejs/vite-plugin-svelte": "^4.0.0", "@testing-library/jest-dom": "^6.4.2", @@ -45,7 +45,7 @@ "dotenv": "^16.4.5", "eslint": "^9.0.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.43.0", + "eslint-plugin-svelte": "^2.45.1", "eslint-plugin-unicorn": "^55.0.0", "factory.ts": "^1.4.1", "globals": "^15.9.0", @@ -60,7 +60,7 @@ "tailwindcss": "^3.4.1", "tslib": "^2.6.2", "typescript": "^5.5.0", - "vite": "^5.1.4", + "vite": "^5.4.4", "vitest": "^2.0.5" }, "type": "module", diff --git a/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte b/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte new file mode 100644 index 0000000..6da8cc3 --- /dev/null +++ b/web/src/lib/components/asset-viewer/image-panorama-viewer.svelte @@ -0,0 +1,32 @@ + + +
+ {#await Promise.all([loadAssetData(asset.id), import('./photo-sphere-viewer-adapter.svelte')])} + + {:then [data, { default: PhotoSphereViewer }]} + + {:catch} + {$t('errors.failed_to_load_asset')} + {/await} +
diff --git a/web/src/lib/components/asset-viewer/video-panorama-viewer.svelte b/web/src/lib/components/asset-viewer/video-panorama-viewer.svelte new file mode 100644 index 0000000..73315d6 --- /dev/null +++ b/web/src/lib/components/asset-viewer/video-panorama-viewer.svelte @@ -0,0 +1,29 @@ + + +
+ {#await modules} + + {:then [PhotoSphereViewer, adapter, videoPlugin]} + + {:catch} + {$t('errors.failed_to_load_asset')} + {/await} +
diff --git a/web/src/lib/components/elements/badge.svelte b/web/src/lib/components/elements/badge.svelte index da305e4..0db6e3f 100644 --- a/web/src/lib/components/elements/badge.svelte +++ b/web/src/lib/components/elements/badge.svelte @@ -1,11 +1,18 @@ - - type BaseProps = { - class?: string; + - - - + - + {@render children?.()} diff --git a/web/src/lib/components/elements/buttons/circle-icon-button.svelte b/web/src/lib/components/elements/buttons/circle-icon-button.svelte index 8af3f75..4b98415 100644 --- a/web/src/lib/components/elements/buttons/circle-icon-button.svelte +++ b/web/src/lib/components/elements/buttons/circle-icon-button.svelte @@ -1,64 +1,64 @@ - - + diff --git a/web/src/lib/components/elements/buttons/link-button.svelte b/web/src/lib/components/elements/buttons/link-button.svelte index b8e81f4..a39e260 100644 --- a/web/src/lib/components/elements/buttons/link-button.svelte +++ b/web/src/lib/components/elements/buttons/link-button.svelte @@ -1,22 +1,25 @@ - - diff --git a/web/src/lib/components/elements/buttons/skip-link.svelte b/web/src/lib/components/elements/buttons/skip-link.svelte index d1ad667..858d296 100644 --- a/web/src/lib/components/elements/buttons/skip-link.svelte +++ b/web/src/lib/components/elements/buttons/skip-link.svelte @@ -2,13 +2,17 @@ import { t } from 'svelte-i18n'; import Button from './button.svelte'; - /** - * Target for the skip link to move focus to. - */ - export let target: string = 'main'; - export let text: string = $t('skip_to_content'); + interface Props { + /** + * Target for the skip link to move focus to. + */ + target?: string; + text?: string; + } - let isFocused = false; + let { target = 'main', text = $t('skip_to_content') }: Props = $props(); + + let isFocused = $state(false); const moveFocus = () => { const targetEl = document.querySelector(target); @@ -20,9 +24,9 @@ diff --git a/web/src/lib/components/elements/checkbox.svelte b/web/src/lib/components/elements/checkbox.svelte index 3407262..4595c06 100644 --- a/web/src/lib/components/elements/checkbox.svelte +++ b/web/src/lib/components/elements/checkbox.svelte @@ -1,11 +1,25 @@
@@ -17,7 +31,7 @@ {disabled} class="size-5 flex-shrink-0 focus-visible:ring" bind:checked - on:change + {onchange} />
diff --git a/web/src/lib/components/elements/date-input.svelte b/web/src/lib/components/elements/date-input.svelte index f42fff4..687e944 100644 --- a/web/src/lib/components/elements/date-input.svelte +++ b/web/src/lib/components/elements/date-input.svelte @@ -1,29 +1,35 @@ (updatedValue = e.currentTarget.value)} - on:blur={() => (value = updatedValue)} - on:keydown={(e) => { + oninput={(e) => (updatedValue = e.currentTarget.value)} + onblur={() => (value = updatedValue)} + onkeydown={(e) => { if (e.key === 'Enter') { value = updatedValue; } diff --git a/web/src/lib/components/elements/dropdown.svelte b/web/src/lib/components/elements/dropdown.svelte index 80689ef..b146f34 100644 --- a/web/src/lib/components/elements/dropdown.svelte +++ b/web/src/lib/components/elements/dropdown.svelte @@ -1,4 +1,4 @@ -
- (showMenu = true)} fullwidth {title}> + (showMenu = true)} fullwidth {title}>
{#if renderedSelectedOption?.icon} @@ -92,7 +104,7 @@ type="button" class="grid grid-cols-[36px,1fr] place-items-center p-2 disabled:opacity-40 {buttonStyle}" disabled={renderedOption.disabled} - on:click={() => !renderedOption.disabled && handleSelectOption(option)} + onclick={() => !renderedOption.disabled && handleSelectOption(option)} > {#if isEqual(selectedOption, option)}
diff --git a/web/src/lib/components/elements/group-tab.svelte b/web/src/lib/components/elements/group-tab.svelte index f5e2f79..021d5ca 100644 --- a/web/src/lib/components/elements/group-tab.svelte +++ b/web/src/lib/components/elements/group-tab.svelte @@ -1,10 +1,14 @@ @@ -22,7 +26,7 @@ class="peer sr-only" value={filter} checked={filter === selected} - on:change={() => onSelect(filter)} + onchange={() => onSelect(filter)} />
diff --git a/web/src/lib/components/i18n/__test__/format-tag-b.svelte b/web/src/lib/components/i18n/__test__/format-tag-b.svelte index 122358c..6e8b241 100644 --- a/web/src/lib/components/i18n/__test__/format-tag-b.svelte +++ b/web/src/lib/components/i18n/__test__/format-tag-b.svelte @@ -3,12 +3,18 @@ import FormatMessage from '../format-message.svelte'; import type { ComponentProps } from 'svelte'; - export let key: Translations; - export let values: ComponentProps['values']; + interface Props { + key: Translations; + values: ComponentProps['values']; + } + + let { key, values }: Props = $props(); - - {#if tag === 'b'} - {message} - {/if} + + {#snippet children({ tag, message })} + {#if tag === 'b'} + {message} + {/if} + {/snippet} diff --git a/web/src/lib/components/i18n/format-bold-message.svelte b/web/src/lib/components/i18n/format-bold-message.svelte index 052b220..0381ec5 100644 --- a/web/src/lib/components/i18n/format-bold-message.svelte +++ b/web/src/lib/components/i18n/format-bold-message.svelte @@ -3,12 +3,18 @@ import type { InterpolationValues } from '$lib/components/i18n/format-message.svelte'; import type { Translations } from 'svelte-i18n'; - export let key: Translations; - export let values: InterpolationValues = {}; + interface Props { + key: Translations; + values?: InterpolationValues; + } + + let { key, values = {} }: Props = $props(); - - {#if tag === 'b'} - {message} - {/if} + + {#snippet children({ message, tag })} + {#if tag === 'b'} + {message} + {/if} + {/snippet} diff --git a/web/src/lib/components/i18n/format-message.svelte b/web/src/lib/components/i18n/format-message.svelte index 48c5947..b8909e3 100644 --- a/web/src/lib/components/i18n/format-message.svelte +++ b/web/src/lib/components/i18n/format-message.svelte @@ -1,4 +1,4 @@ - @@ -18,8 +18,13 @@ tag?: string; }; - export let key: Translations; - export let values: InterpolationValues = {}; + interface Props { + key: Translations; + values?: InterpolationValues; + children?: import('svelte').Snippet<[{ tag?: string; message?: string }]>; + } + + let { key, values = {}, children }: Props = $props(); const getLocale = (locale?: string | null) => { if (locale == null) { @@ -96,9 +101,9 @@ } }; - $: message = ($json(key) as string) || key; - $: locale = getLocale($i18nLocale); - $: parts = getParts(message, locale); + let message = $derived(($json(key) as string) || key); + let locale = $derived(getLocale($i18nLocale)); + let parts = $derived(getParts(message, locale)); {#each parts as { tag, message }} {#if tag} - {message} + {#if children}{@render children({ tag, message })}{:else}{message}{/if} {:else} {message} {/if} diff --git a/web/src/lib/components/shared-components/album-selection-modal.svelte b/web/src/lib/components/shared-components/album-selection-modal.svelte index 65f39cc..3400864 100644 --- a/web/src/lib/components/shared-components/album-selection-modal.svelte +++ b/web/src/lib/components/shared-components/album-selection-modal.svelte @@ -11,17 +11,19 @@ import { sortAlbums } from '$lib/utils/album-utils'; import { albumViewSettings } from '$lib/stores/preferences.store'; - export let onNewAlbum: (search: string) => void; - export let onAlbumClick: (album: AlbumResponseDto) => void; + let albums: AlbumResponseDto[] = $state([]); + let recentAlbums: AlbumResponseDto[] = $state([]); + let loading = $state(true); + let search = $state(''); - let albums: AlbumResponseDto[] = []; - let recentAlbums: AlbumResponseDto[] = []; - let filteredAlbums: AlbumResponseDto[] = []; - let loading = true; - let search = ''; + interface Props { + onNewAlbum: (search: string) => void; + onAlbumClick: (album: AlbumResponseDto) => void; + shared: boolean; + onClose: () => void; + } - export let shared: boolean; - export let onClose: () => void; + let { onNewAlbum, onAlbumClick, shared, onClose }: Props = $props(); onMount(async () => { albums = await getAllAlbums({ shared: shared || undefined }); @@ -29,13 +31,15 @@ loading = false; }); - $: filteredAlbums = sortAlbums( - search.length > 0 && albums.length > 0 - ? albums.filter((album) => { - return normalizeSearchString(album.albumName).includes(normalizeSearchString(search)); - }) - : albums, - { sortBy: $albumViewSettings.sortBy, orderBy: $albumViewSettings.sortOrder }, + let filteredAlbums = $derived( + sortAlbums( + search.length > 0 && albums.length > 0 + ? albums.filter((album) => { + return normalizeSearchString(album.albumName).includes(normalizeSearchString(search)); + }) + : albums, + { sortBy: $albumViewSettings.sortBy, orderBy: $albumViewSettings.sortOrder }, + ), ); const getTitle = () => { @@ -71,7 +75,7 @@
- - + +
diff --git a/web/src/lib/components/shared-components/settings/setting-checkboxes.svelte b/web/src/lib/components/shared-components/settings/setting-checkboxes.svelte index 3def0ce..09f0ea4 100644 --- a/web/src/lib/components/shared-components/settings/setting-checkboxes.svelte +++ b/web/src/lib/components/shared-components/settings/setting-checkboxes.svelte @@ -4,13 +4,25 @@ import { fly } from 'svelte/transition'; import { t } from 'svelte-i18n'; - export let value: string[]; - export let options: { value: string; text: string }[]; - export let label = ''; - export let desc = ''; - export let name = ''; - export let isEdited = false; - export let disabled = false; + interface Props { + value: string[]; + options: { value: string; text: string }[]; + label?: string; + desc?: string; + name?: string; + isEdited?: boolean; + disabled?: boolean; + } + + let { + value = $bindable(), + options, + label = '', + desc = '', + name = '', + isEdited = false, + disabled = false, + }: Props = $props(); function handleCheckboxChange(option: string) { value = value.includes(option) ? value.filter((item) => item !== option) : [...value, option]; @@ -46,7 +58,7 @@ checked={value.includes(option.value)} {disabled} labelClass="text-gray-500 dark:text-gray-300" - on:change={() => handleCheckboxChange(option.value)} + onchange={() => handleCheckboxChange(option.value)} /> {/each} diff --git a/web/src/lib/components/shared-components/settings/setting-combobox.svelte b/web/src/lib/components/shared-components/settings/setting-combobox.svelte index 722af04..5314ad7 100644 --- a/web/src/lib/components/shared-components/settings/setting-combobox.svelte +++ b/web/src/lib/components/shared-components/settings/setting-combobox.svelte @@ -3,14 +3,29 @@ import { fly } from 'svelte/transition'; import Combobox, { type ComboBoxOption } from '$lib/components/shared-components/combobox.svelte'; import { t } from 'svelte-i18n'; + import type { Snippet } from 'svelte'; - export let title: string; - export let comboboxPlaceholder: string; - export let subtitle = ''; - export let isEdited = false; - export let options: ComboBoxOption[]; - export let selectedOption: ComboBoxOption; - export let onSelect: (combobox: ComboBoxOption | undefined) => void; + interface Props { + title: string; + comboboxPlaceholder: string; + subtitle?: string; + isEdited?: boolean; + options: ComboBoxOption[]; + selectedOption: ComboBoxOption; + onSelect: (combobox: ComboBoxOption | undefined) => void; + children?: Snippet; + } + + let { + title, + comboboxPlaceholder, + subtitle = '', + isEdited = false, + options, + selectedOption, + onSelect, + children, + }: Props = $props();
@@ -33,6 +48,6 @@
- + {@render children?.()}
diff --git a/web/src/lib/components/shared-components/settings/setting-dropdown.svelte b/web/src/lib/components/shared-components/settings/setting-dropdown.svelte index 20324fe..57e78e6 100644 --- a/web/src/lib/components/shared-components/settings/setting-dropdown.svelte +++ b/web/src/lib/components/shared-components/settings/setting-dropdown.svelte @@ -3,14 +3,27 @@ import { fly } from 'svelte/transition'; import Dropdown, { type RenderedOption } from '$lib/components/elements/dropdown.svelte'; import { t } from 'svelte-i18n'; + import type { Snippet } from 'svelte'; - export let title: string; - export let subtitle = ''; - export let options: RenderedOption[]; - export let selectedOption: RenderedOption; - export let isEdited = false; + interface Props { + title: string; + subtitle?: string; + options: RenderedOption[]; + selectedOption: RenderedOption; + isEdited?: boolean; + onToggle: (option: RenderedOption) => void; + children?: Snippet; + } - export let onToggle: (option: RenderedOption) => void; + let { + title, + subtitle = '', + options, + selectedOption = $bindable(), + isEdited = false, + onToggle, + children, + }: Props = $props();
@@ -30,7 +43,7 @@

{subtitle}

- + {@render children?.()}
{ it('validates number input on blur', async () => { diff --git a/web/src/lib/components/shared-components/settings/setting-input-field.svelte b/web/src/lib/components/shared-components/settings/setting-input-field.svelte index 410adc6..1463cc4 100644 --- a/web/src/lib/components/shared-components/settings/setting-input-field.svelte +++ b/web/src/lib/components/shared-components/settings/setting-input-field.svelte @@ -1,36 +1,47 @@ - -
@@ -37,7 +51,7 @@ {#if subtitle}

{subtitle}

{/if} - + {@render children?.()}
diff --git a/web/src/lib/components/shared-components/settings/setting-textarea.svelte b/web/src/lib/components/shared-components/settings/setting-textarea.svelte index 5c7b138..9f9f885 100644 --- a/web/src/lib/components/shared-components/settings/setting-textarea.svelte +++ b/web/src/lib/components/shared-components/settings/setting-textarea.svelte @@ -2,13 +2,27 @@ import { quintOut } from 'svelte/easing'; import { fly } from 'svelte/transition'; import { t } from 'svelte-i18n'; + import type { Snippet } from 'svelte'; - export let value: string; - export let label = ''; - export let desc = ''; - export let required = false; - export let disabled = false; - export let isEdited = false; + interface Props { + value: string; + label?: string; + description?: string; + required?: boolean; + disabled?: boolean; + isEdited?: boolean; + descriptionSnippet?: Snippet; + } + + let { + value = $bindable(), + label = '', + description = '', + required = false, + disabled = false, + isEdited = false, + descriptionSnippet, + }: Props = $props(); const handleInput = (e: Event) => { value = (e.target as HTMLInputElement).value; @@ -32,23 +46,23 @@ {/if}
- {#if desc} + {#if description}

- {desc} + {description}

{:else} - + {@render descriptionSnippet?.()} {/if} diff --git a/web/src/lib/components/shared-components/show-shortcuts.svelte b/web/src/lib/components/shared-components/show-shortcuts.svelte index 2bd1b89..a3cfd83 100644 --- a/web/src/lib/components/shared-components/show-shortcuts.svelte +++ b/web/src/lib/components/shared-components/show-shortcuts.svelte @@ -15,25 +15,31 @@ info?: string; } - export let onClose: () => void; + interface Props { + onClose: () => void; + shortcuts?: Shortcuts; + } - export let shortcuts: Shortcuts = { - general: [ - { key: ['←', '→'], action: $t('previous_or_next_photo') }, - { key: ['Esc'], action: $t('back_close_deselect') }, - { key: ['Ctrl', 'k'], action: $t('search_your_photos') }, - { key: ['Ctrl', '⇧', 'k'], action: $t('open_the_search_filters') }, - ], - actions: [ - { key: ['f'], action: $t('favorite_or_unfavorite_photo') }, - { key: ['i'], action: $t('show_or_hide_info') }, - { key: ['s'], action: $t('stack_selected_photos') }, - { key: ['⇧', 'a'], action: $t('archive_or_unarchive_photo') }, - { key: ['⇧', 'd'], action: $t('download') }, - { key: ['Space'], action: $t('play_or_pause_video') }, - { key: ['Del'], action: $t('trash_delete_asset'), info: $t('shift_to_permanent_delete') }, - ], - }; + let { + onClose, + shortcuts = { + general: [ + { key: ['←', '→'], action: $t('previous_or_next_photo') }, + { key: ['Esc'], action: $t('back_close_deselect') }, + { key: ['Ctrl', 'k'], action: $t('search_your_photos') }, + { key: ['Ctrl', '⇧', 'k'], action: $t('open_the_search_filters') }, + ], + actions: [ + { key: ['f'], action: $t('favorite_or_unfavorite_photo') }, + { key: ['i'], action: $t('show_or_hide_info') }, + { key: ['s'], action: $t('stack_selected_photos') }, + { key: ['⇧', 'a'], action: $t('archive_or_unarchive_photo') }, + { key: ['⇧', 'd'], action: $t('download') }, + { key: ['Space'], action: $t('play_or_pause_video') }, + { key: ['Del'], action: $t('trash_delete_asset'), info: $t('shift_to_permanent_delete') }, + ], + }, + }: Props = $props(); diff --git a/web/src/lib/components/shared-components/theme-button.svelte b/web/src/lib/components/shared-components/theme-button.svelte index f5ba877..4466682 100644 --- a/web/src/lib/components/shared-components/theme-button.svelte +++ b/web/src/lib/components/shared-components/theme-button.svelte @@ -5,14 +5,15 @@ import { colorTheme, handleToggleTheme } from '$lib/stores/preferences.store'; import { t } from 'svelte-i18n'; - // svelte-ignore reactive_declaration_non_reactive_property - $: icon = $colorTheme.value === Theme.LIGHT ? moonPath : sunPath; - // svelte-ignore reactive_declaration_non_reactive_property - $: viewBox = $colorTheme.value === Theme.LIGHT ? moonViewBox : sunViewBox; - // svelte-ignore reactive_declaration_non_reactive_property - $: isDark = $colorTheme.value === Theme.DARK; + let icon = $derived($colorTheme.value === Theme.LIGHT ? moonPath : sunPath); + let viewBox = $derived($colorTheme.value === Theme.LIGHT ? moonViewBox : sunViewBox); + let isDark = $derived($colorTheme.value === Theme.DARK); - export let padding: Padding = '3'; + interface Props { + padding?: Padding; + } + + let { padding = '3' }: Props = $props(); {#if !$colorTheme.system} @@ -22,7 +23,7 @@ {viewBox} role="switch" aria-checked={isDark ? 'true' : 'false'} - on:click={handleToggleTheme} + onclick={handleToggleTheme} {padding} /> {/if} diff --git a/web/src/lib/components/shared-components/tree/breadcrumbs.svelte b/web/src/lib/components/shared-components/tree/breadcrumbs.svelte index a3c49a1..1d84133 100644 --- a/web/src/lib/components/shared-components/tree/breadcrumbs.svelte +++ b/web/src/lib/components/shared-components/tree/breadcrumbs.svelte @@ -4,12 +4,16 @@ import { mdiArrowUpLeft, mdiChevronRight } from '@mdi/js'; import { t } from 'svelte-i18n'; - export let pathSegments: string[] = []; - export let getLink: (path: string) => string; - export let title: string; - export let icon: string; + interface Props { + pathSegments?: string[]; + getLink: (path: string) => string; + title: string; + icon: string; + } - $: isRoot = pathSegments.length === 0; + let { pathSegments = [], getLink, title, icon }: Props = $props(); + + let isRoot = $derived(pathSegments.length === 0);